Skip to content

Commit

Permalink
EVM: Add full db state checks in rollback tests (#2609)
Browse files Browse the repository at this point in the history
* Clean up rollback test

* Add defi-cli support for web3 RPCs and debug RPCs

* Fix rpc

* Add dumpdb

* Add RPCs to test node

* Add full evm db state checks in rollback test
  • Loading branch information
sieniven authored Oct 24, 2023
1 parent 0fd8107 commit e9aa06e
Showing 1 changed file with 208 additions and 82 deletions.
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()

0 comments on commit e9aa06e

Please sign in to comment.