Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVM: Add full db state checks in rollback tests #2609

Merged
merged 9 commits into from
Oct 24, 2023
290 changes: 208 additions & 82 deletions test/functional/feature_evm_rollback.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,46 +74,63 @@ def setup(self):
}
}
)
self.nodes[0].generate(2)

self.creationAddress = "0xe61a3a6eb316d773c773f4ce757a542f673023c6"
self.nodes[0].importprivkey(
"957ac3be2a08afe1fafb55bd3e1d479c4ae6d7bf1c9b2a0dcc5caad6929e6617"
self.nodes[0].generate(1)
self.nodes[0].transferdomain(
[
{
"src": {"address": self.address, "amount": "100@DFI", "domain": 2},
"dst": {
"address": self.ethAddress,
"amount": "100@DFI",
"domain": 3,
},
}
]
)
self.nodes[0].generate(1)

self.startBlockNum = self.nodes[0].eth_blockNumber()
self.startBlock = self.nodes[0].eth_getBlockByNumber(self.startBlockNum)
self.start_height = self.nodes[0].getblockcount()

def test_rollback_block(self):
self.nodes[0].generate(1)
initialBlockHash = self.nodes[0].getbestblockhash()
blockNumberPreInvalidation = self.nodes[0].eth_blockNumber()
blockPreInvalidation = self.nodes[0].eth_getBlockByNumber(
blockNumberPreInvalidation
)
assert_equal(blockNumberPreInvalidation, "0x3")
assert_equal(blockPreInvalidation["number"], blockNumberPreInvalidation)
self.rollback_to(self.start_height)
assert_equal(self.nodes[0].eth_blockNumber(), self.startBlockNum)
assert_equal(self.nodes[0].eth_getBlockByNumber("latest"), self.startBlock)

self.nodes[0].invalidateblock(initialBlockHash)
self.nodes[0].generate(1)
nextblockHash = self.nodes[0].getbestblockhash()
nextBlockNum = self.nodes[0].eth_blockNumber()
nextBlock = self.nodes[0].eth_getBlockByNumber(nextBlockNum)
assert_equal(nextBlock["number"], nextBlockNum)

self.nodes[0].invalidateblock(nextblockHash)
assert_raises_rpc_error(
-32001,
"Custom error: header not found",
self.nodes[0].eth_getBlockByNumber,
blockNumberPreInvalidation,
nextBlockNum,
)
blockByHash = self.nodes[0].eth_getBlockByHash(blockPreInvalidation["hash"])
blockByHash = self.nodes[0].eth_getBlockByHash(nextBlock["hash"])
assert_equal(blockByHash, None)
block = self.nodes[0].eth_getBlockByNumber("latest")
assert_equal(block["number"], "0x2")

self.nodes[0].reconsiderblock(initialBlockHash)
blockNumber = self.nodes[0].eth_blockNumber()
block = self.nodes[0].eth_getBlockByNumber(blockNumber)
assert_equal(blockNumber, blockNumberPreInvalidation)
assert_equal(block, blockPreInvalidation)
currBlockNum = self.nodes[0].eth_blockNumber()
currBlock = self.nodes[0].eth_getBlockByNumber(currBlockNum)
assert_equal(currBlockNum, self.startBlockNum)
assert_equal(currBlock, self.startBlock)

self.nodes[0].reconsiderblock(nextblockHash)
reconsiderBlockNum = self.nodes[0].eth_blockNumber()
reconsiderBlock = self.nodes[0].eth_getBlockByNumber(reconsiderBlockNum)
assert_equal(reconsiderBlockNum, nextBlockNum)
assert_equal(reconsiderBlock, nextBlock)

def test_rollback_transactions(self):
initialBlockHash = self.nodes[0].getbestblockhash()
self.rollback_to(self.start_height)
assert_equal(self.nodes[0].eth_blockNumber(), self.startBlockNum)
assert_equal(self.nodes[0].eth_getBlockByNumber("latest"), self.startBlock)

hash = self.nodes[0].eth_sendTransaction(
txHash = self.nodes[0].eth_sendTransaction(
{
"from": self.ethAddress,
"to": self.toAddress,
Expand All @@ -123,105 +140,214 @@ def test_rollback_transactions(self):
}
)
self.nodes[0].generate(1)
blockHash = self.nodes[0].getblockhash(self.nodes[0].getblockcount())
currBlockNum = self.nodes[0].getblockcount()
currblockHash = self.nodes[0].getblockhash(currBlockNum)

# Check accounting of EVM fees
tx = {
txInfo = {
"from": self.ethAddress,
"to": self.toAddress,
"value": "0xa",
"gas": "0x7a120", # 500_000
"gasPrice": "0x2540BE400", # 10_000_000_000,
}
fees = self.nodes[0].debug_feeEstimate(tx)
self.burnt_fee = hex_to_decimal(fees["burnt_fee"])
self.priority_fee = hex_to_decimal(fees["priority_fee"])
fees = self.nodes[0].debug_feeEstimate(txInfo)
self.burntFee = hex_to_decimal(fees["burnt_fee"])
self.priorityFee = hex_to_decimal(fees["priority_fee"])
attributes = self.nodes[0].getgov("ATTRIBUTES")["ATTRIBUTES"]
assert_equal(attributes["v0/live/economy/evm/block/fee_burnt"], self.burnt_fee)
assert_equal(attributes["v0/live/economy/evm/block/fee_burnt"], self.burntFee)
assert_equal(
attributes["v0/live/economy/evm/block/fee_burnt_min"], self.burnt_fee
attributes["v0/live/economy/evm/block/fee_burnt_min"], self.burntFee
)
assert_equal(
attributes["v0/live/economy/evm/block/fee_burnt_min_hash"], blockHash
attributes["v0/live/economy/evm/block/fee_burnt_min_hash"], currblockHash
)
assert_equal(
attributes["v0/live/economy/evm/block/fee_burnt_max"], self.burnt_fee
attributes["v0/live/economy/evm/block/fee_burnt_max"], self.burntFee
)
assert_equal(
attributes["v0/live/economy/evm/block/fee_burnt_max_hash"], blockHash
attributes["v0/live/economy/evm/block/fee_burnt_max_hash"], currblockHash
)
assert_equal(
attributes["v0/live/economy/evm/block/fee_priority"], self.priority_fee
attributes["v0/live/economy/evm/block/fee_priority"], self.priorityFee
)
assert_equal(
attributes["v0/live/economy/evm/block/fee_priority_max"], self.priority_fee
attributes["v0/live/economy/evm/block/fee_priority_max"], self.priorityFee
)

blockNumberPreInvalidation = self.nodes[0].eth_blockNumber()
blockPreInvalidation = self.nodes[0].eth_getBlockByNumber(
blockNumberPreInvalidation
)
assert_equal(blockNumberPreInvalidation, "0x4")
assert_equal(blockPreInvalidation["number"], blockNumberPreInvalidation)
evmBlockNum = self.nodes[0].eth_blockNumber()
evmBlock = self.nodes[0].eth_getBlockByNumber(evmBlockNum)
assert_equal(evmBlock["number"], evmBlockNum)

txPreInvalidation = self.nodes[0].eth_getTransactionByHash(hash)
receiptPreInvalidation = self.nodes[0].eth_getTransactionReceipt(hash)
assert_equal(blockPreInvalidation["transactions"][0], txPreInvalidation["hash"])
tx = self.nodes[0].eth_getTransactionByHash(txHash)
txReceipt = self.nodes[0].eth_getTransactionReceipt(txHash)
assert_equal(evmBlock["transactions"][0], tx["hash"])
assert_equal(
blockPreInvalidation["transactions"][0],
receiptPreInvalidation["transactionHash"],
evmBlock["transactions"][0],
txReceipt["transactionHash"],
)

self.nodes[0].invalidateblock(initialBlockHash)
# Check that chain tip is back to the starting block
self.nodes[0].invalidateblock(currblockHash)
currEvmBlockNum = self.nodes[0].eth_blockNumber()
currEvmBlock = self.nodes[0].eth_getBlockByNumber(currEvmBlockNum)
assert_equal(currEvmBlockNum, self.startBlockNum)
assert_equal(currEvmBlock, self.startBlock)

tx = self.nodes[0].eth_getTransactionByHash(hash)
receipt = self.nodes[0].eth_getTransactionReceipt(hash)
# Check that txs are no longer valid
tx = self.nodes[0].eth_getTransactionByHash(txHash)
receipt = self.nodes[0].eth_getTransactionReceipt(txHash)
assert_equal(tx, None)
assert_equal(receipt, None)

self.nodes[0].reconsiderblock(initialBlockHash)
tx = self.nodes[0].eth_getTransactionByHash(hash)
receipt = self.nodes[0].eth_getTransactionReceipt(hash)
assert_equal(blockPreInvalidation["transactions"][0], tx["hash"])
self.nodes[0].reconsiderblock(currblockHash)
reconsiderBlockNum = self.nodes[0].eth_blockNumber()
reconsiderBlock = self.nodes[0].eth_getBlockByNumber(reconsiderBlockNum)
tx = self.nodes[0].eth_getTransactionByHash(txHash)
receipt = self.nodes[0].eth_getTransactionReceipt(txHash)
assert_equal(reconsiderBlockNum, evmBlockNum)
assert_equal(reconsiderBlock, evmBlock)
assert_equal(reconsiderBlock["transactions"][0], tx["hash"])
assert_equal(
blockPreInvalidation["transactions"][0], receipt["transactionHash"]
reconsiderBlock["transactions"][0],
receipt["transactionHash"],
)

def run_test(self):
self.setup()
def test_state_rollback(self):
self.rollback_to(self.start_height)
assert_equal(self.nodes[0].eth_blockNumber(), self.startBlockNum)
assert_equal(self.nodes[0].eth_getBlockByNumber("latest"), self.startBlock)
startBlockHash = self.nodes[0].getbestblockhash()
startdbdump = self.nodes[0].debug_dumpdb("all")
# assert_equal((startdbdump != None), True)

self.nodes[0].transferdomain(
[
{
"src": {"address": self.address, "amount": "100@DFI", "domain": 2},
"dst": {
"address": self.creationAddress,
"amount": "100@DFI",
"domain": 3,
},
}
]
)
evmAddresses = []
numEvmAddresses = 10
for i in range(numEvmAddresses):
evmAddresses.append(self.nodes[0].getnewaddress("", "erc55"))

# Transferdomain txs
tdHashes = []
for i in range(numEvmAddresses):
hash = self.nodes[0].transferdomain(
[
{
"src": {
"address": self.address,
"amount": "10@DFI",
"domain": 2,
},
"dst": {
"address": evmAddresses[i],
"amount": "10@DFI",
"domain": 3,
},
}
]
)
tdHashes.append(hash)
self.nodes[0].generate(1)

self.nodes[0].transferdomain(
[
{
"src": {"address": self.address, "amount": "100@DFI", "domain": 2},
"dst": {
"address": self.ethAddress,
"amount": "100@DFI",
"domain": 3,
},
}
]
)
# first block (transferdomain txs)
firstBlockNum = self.nodes[0].eth_blockNumber()
firstBlock = self.nodes[0].eth_getBlockByNumber(firstBlockNum)
firstBlockHash = self.nodes[0].getbestblockhash()
block_info = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 4)
tdtx_id = 0
for tx_info in block_info["tx"][1:]:
if tx_info["vm"]["txtype"] == "TransferDomain":
# Check that all transferdomain txs are minted in the first block
assert_equal(tx_info["txid"], tdHashes[tdtx_id])
tdtx_id += 1
assert_equal(tdtx_id, numEvmAddresses)
firstdbdump = self.nodes[0].debug_dumpdb("all")
# assert_equal((firstdbdump != None), True)

# second block (transfer txs)
hashes = []
counts = [0] * numEvmAddresses
for i in range(numEvmAddresses):
for j in range(numEvmAddresses):
if i == j:
continue

hash = self.nodes[0].eth_sendTransaction(
{
"nonce": hex(counts[i]),
"from": evmAddresses[i],
"to": evmAddresses[j],
"value": "0xDE0B6B3A7640000", # 1 DFI
"gas": "0x5209",
"gasPrice": "0x5D21DBA00", # 25_000_000_000
}
)
counts[i] += 1
hashes.append(hash)

self.nodes[0].generate(1)

# Second block (evm txs)
secondBlockNum = self.nodes[0].eth_blockNumber()
secondBlock = self.nodes[0].eth_getBlockByNumber(secondBlockNum)
secondBlockHash = self.nodes[0].getbestblockhash()
block_info = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 4)
for idx, tx_info in enumerate(block_info["tx"][1:]):
assert_equal(tx_info["vm"]["vmtype"], "evm")
assert_equal(tx_info["vm"]["txtype"], "Evm")
assert_equal(tx_info["vm"]["msg"]["hash"], hashes[idx][2:])
assert_equal(len(block_info["tx"][1:]), numEvmAddresses * (numEvmAddresses - 1))
seconddbdump = self.nodes[0].debug_dumpdb("all")
# assert_equal((seconddbdump != None), True)

# Invalidate second block
self.nodes[0].invalidateblock(secondBlockHash)
blockByHash = self.nodes[0].eth_getBlockByHash(secondBlock["hash"])
assert_equal(blockByHash, None)
currBlockNum = self.nodes[0].eth_blockNumber()
currBlock = self.nodes[0].eth_getBlockByNumber(currBlockNum)
currBlockHash = self.nodes[0].getbestblockhash()
assert_equal(currBlockNum, firstBlockNum)
assert_equal(currBlock, firstBlock)
assert_equal(currBlockHash, firstBlockHash)
currdbdump = self.nodes[0].debug_dumpdb("all")
# assert_equal((currdbdump != None), True)
assert_equal(currdbdump, firstdbdump)

# Invalidate first block
self.nodes[0].invalidateblock(firstBlockHash)
blockByHash = self.nodes[0].eth_getBlockByHash(firstBlock["hash"])
assert_equal(blockByHash, None)
currBlockNum = self.nodes[0].eth_blockNumber()
currBlock = self.nodes[0].eth_getBlockByNumber(currBlockNum)
currBlockHash = self.nodes[0].getbestblockhash()
assert_equal(currBlockNum, self.startBlockNum)
assert_equal(currBlock, self.startBlock)
assert_equal(currBlockHash, startBlockHash)
currdbdump = self.nodes[0].debug_dumpdb("all")
# assert_equal((currdbdump != None), True)
assert_equal(currdbdump, startdbdump)

# Reconsider blocks
self.nodes[0].reconsiderblock(firstBlockHash)
reconsiderBlockNum = self.nodes[0].eth_blockNumber()
reconsiderBlock = self.nodes[0].eth_getBlockByNumber(reconsiderBlockNum)
reconsiderBlockHash = self.nodes[0].getbestblockhash()
assert_equal(reconsiderBlockNum, secondBlockNum)
assert_equal(reconsiderBlock, secondBlock)
assert_equal(reconsiderBlockHash, secondBlockHash)
currdbdump = self.nodes[0].debug_dumpdb("all")
# assert_equal((currdbdump != None), True)
assert_equal(currdbdump, seconddbdump)

def run_test(self):
self.setup()

self.test_rollback_block()

self.test_rollback_transactions()

self.test_state_rollback()


if __name__ == "__main__":
EVMRolllbackTest().main()
Loading