From 586437364c22e657e0914b707b9e3aec82cc3093 Mon Sep 17 00:00:00 2001 From: Niven Date: Mon, 29 Jan 2024 19:10:33 +0800 Subject: [PATCH] RPC: Fix state override support in estimate gas (#2806) * Add state override support in estimate gas RPC --------- Co-authored-by: Jouzo <15011228+Jouzo@users.noreply.github.com> --- lib/ain-grpc/src/rpc/eth.rs | 16 +++- test/functional/feature_evm_gas.py | 144 ++++++++++++++++++++++++----- 2 files changed, 134 insertions(+), 26 deletions(-) diff --git a/lib/ain-grpc/src/rpc/eth.rs b/lib/ain-grpc/src/rpc/eth.rs index e4b8eb195a..c795093500 100644 --- a/lib/ain-grpc/src/rpc/eth.rs +++ b/lib/ain-grpc/src/rpc/eth.rs @@ -811,11 +811,17 @@ impl MetachainRPCServer for MetachainRPCModule { // Recap the highest gas allowance with account's balance if call.from.is_some() { - let balance = self - .handler - .core - .get_balance(caller, block.header.state_root) - .map_err(to_custom_err)?; + let balance = if let Some(balance) = overlay + .as_ref() + .and_then(|o| o.get_account(&caller).map(|acc| acc.balance)) + { + balance + } else { + self.handler + .core + .get_balance(caller, block.header.state_root) + .map_err(to_custom_err)? + }; let mut available = balance; if let Some(value) = call.value { if balance < value { diff --git a/test/functional/feature_evm_gas.py b/test/functional/feature_evm_gas.py index a78da75834..30a200a809 100644 --- a/test/functional/feature_evm_gas.py +++ b/test/functional/feature_evm_gas.py @@ -104,83 +104,112 @@ def setup(self): self.nodes[0].generate(1) self.start_height = self.nodes[0].getblockcount() + def generate_test_txs(self, address): + dict_txs = dict() # Eth call for balance transfer with gas specified - self.valid_balance_transfer_tx_with_gas = { - "from": self.ethAddress, + valid_balance_transfer_tx_with_gas = { + "from": address, "to": self.toAddress, "value": "0xDE0B6B3A7640000", # 1 DFI "gas": "0x7a120", "gasPrice": "0x37E11D600", # 15_000_000_000 } + dict_txs["valid_balance_transfer_tx_with_gas"] = ( + valid_balance_transfer_tx_with_gas + ) # Eth call for balance transfer with exact gas specified - self.valid_balance_transfer_tx_with_exact_gas = { - "from": self.ethAddress, + valid_balance_transfer_tx_with_exact_gas = { + "from": address, "to": self.toAddress, "value": "0xDE0B6B3A7640000", # 1 DFI "gas": "0x5208", "gasPrice": "0x37E11D600", # 15_000_000_000 } + dict_txs["valid_balance_transfer_tx_with_exact_gas"] = ( + valid_balance_transfer_tx_with_exact_gas + ) # Valid eth call for balance transfer without gas specified - self.valid_balance_transfer_tx_without_gas = { - "from": self.ethAddress, + valid_balance_transfer_tx_without_gas = { + "from": address, "to": self.toAddress, "value": "0xDE0B6B3A7640000", # 1 DFI "gasPrice": "0x37E11D600", # 15_000_000_000 } + dict_txs["valid_balance_transfer_tx_without_gas"] = ( + valid_balance_transfer_tx_without_gas + ) # Invalid eth call for balance transfer with both gasPrice and maxFeePerGas specified - self.invalid_balance_transfer_tx_specified_gas_1 = { - "from": self.ethAddress, + invalid_balance_transfer_tx_specified_gas_1 = { + "from": address, "to": self.toAddress, "value": "0xDE0B6B3A7640000", # 1 DFI "gasPrice": "0x37E11D600", # 15_000_000_000 "maxFeePerGas": "0x37E11D600", # 15_000_000_000 } + dict_txs["invalid_balance_transfer_tx_specified_gas_1"] = ( + invalid_balance_transfer_tx_specified_gas_1 + ) # Invalid eth call for balance transfer with both gasPrice and priorityFeePerGas specified - self.invalid_balance_transfer_tx_specified_gas_2 = { - "from": self.ethAddress, + invalid_balance_transfer_tx_specified_gas_2 = { + "from": address, "to": self.toAddress, "value": "0xDE0B6B3A7640000", # 1 DFI "gasPrice": "0x37E11D600", # 15_000_000_000 "maxPriorityFeePerGas": "0x37E11D600", # 15_000_000_000 } + dict_txs["invalid_balance_transfer_tx_specified_gas_2"] = ( + invalid_balance_transfer_tx_specified_gas_2 + ) # Invalid eth call for balance transfer with both data and input fields specified - self.invalid_balance_transfer_tx_specified_data_and_input = { - "from": self.ethAddress, + invalid_balance_transfer_tx_specified_data_and_input = { + "from": address, "to": self.toAddress, "value": "0xDE0B6B3A7640000", # 1 DFI "gasPrice": "0x37E11D600", # 15_000_000_000 "data": "0xffffffffffffffff", "input": "0xffffffffffffffff", } + dict_txs["invalid_balance_transfer_tx_specified_data_and_input"] = ( + invalid_balance_transfer_tx_specified_data_and_input + ) # Invalid eth call from insufficient balance for balance transfer - self.invalid_balance_transfer_tx_insufficient_funds = { - "from": self.ethAddress, + invalid_balance_transfer_tx_insufficient_funds = { + "from": address, "to": self.toAddress, "value": "0x152D02C7E14AF6800000", # 100_000 DFI "gasPrice": "0x37E11D600", # 15_000_000_000 } + dict_txs["invalid_balance_transfer_tx_insufficient_funds"] = ( + invalid_balance_transfer_tx_insufficient_funds + ) + return dict_txs def test_estimate_gas_balance_transfer(self): self.rollback_to(self.start_height) + dict_txs = self.generate_test_txs(self.ethAddress) # Test valid estimateGas call for transfer tx with gas specified - gas = self.nodes[0].eth_estimateGas(self.valid_balance_transfer_tx_with_gas) + gas = self.nodes[0].eth_estimateGas( + dict_txs["valid_balance_transfer_tx_with_gas"] + ) assert_equal(gas, "0x5208") # Test valid estimateGas call for transfer tx with exact gas specified gas = self.nodes[0].eth_estimateGas( - self.valid_balance_transfer_tx_with_exact_gas + dict_txs["valid_balance_transfer_tx_with_exact_gas"], ) assert_equal(gas, "0x5208") # Test valid estimateGas call for transfer tx without gas specified - gas = self.nodes[0].eth_estimateGas(self.valid_balance_transfer_tx_without_gas) + gas = self.nodes[0].eth_estimateGas( + dict_txs["valid_balance_transfer_tx_without_gas"] + ) assert_equal(gas, "0x5208") # Test invalid estimateGas call, both gasPrice and maxFeePerGas specified @@ -188,7 +217,7 @@ def test_estimate_gas_balance_transfer(self): -32001, "both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified", self.nodes[0].eth_estimateGas, - self.invalid_balance_transfer_tx_specified_gas_1, + dict_txs["invalid_balance_transfer_tx_specified_gas_1"], ) # Test invalid estimateGas call, both gasPrice and maxPriorityFeePerGas specified @@ -196,7 +225,7 @@ def test_estimate_gas_balance_transfer(self): -32001, "Custom error: both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified", self.nodes[0].eth_estimateGas, - self.invalid_balance_transfer_tx_specified_gas_2, + dict_txs["invalid_balance_transfer_tx_specified_gas_2"], ) # Test invalid estimateGas call, both data and input field specified @@ -204,7 +233,7 @@ def test_estimate_gas_balance_transfer(self): -32001, "Custom error: data and input fields are mutually exclusive", self.nodes[0].eth_estimateGas, - self.invalid_balance_transfer_tx_specified_data_and_input, + dict_txs["invalid_balance_transfer_tx_specified_data_and_input"], ) # Test invalid estimateGas call, insufficient funds @@ -212,7 +241,7 @@ def test_estimate_gas_balance_transfer(self): -32001, "Custom error: insufficient funds for transfer", self.nodes[0].eth_estimateGas, - self.invalid_balance_transfer_tx_insufficient_funds, + dict_txs["invalid_balance_transfer_tx_insufficient_funds"], ) def test_estimate_gas_contract(self): @@ -355,6 +384,77 @@ def test_estimate_gas_revert(self): contract.functions.value_check(0).estimate_gas, ) + def test_estimate_gas_state_override(self): + self.rollback_to(self.start_height) + + emptyAddress = self.nodes[0].getnewaddress("", "erc55") + self.nodes[0].generate(1) + balance = self.nodes[0].eth_getBalance(emptyAddress, "latest") + assert_equal(int(balance[2:], 16), 0) + + dict_txs = self.generate_test_txs(emptyAddress) + state_override = {emptyAddress: {"balance": "0x8AC7230489E80000"}} # 10 DFI + + # Test valid estimateGas call for transfer tx with gas specified and state override + gas = self.nodes[0].eth_estimateGas( + dict_txs["valid_balance_transfer_tx_with_gas"], "latest", state_override + ) + assert_equal(gas, "0x5208") + + # Test valid estimateGas call for transfer tx with exact gas specified and state override + gas = self.nodes[0].eth_estimateGas( + dict_txs["valid_balance_transfer_tx_with_exact_gas"], + "latest", + state_override, + ) + assert_equal(gas, "0x5208") + + # Test valid estimateGas call for transfer tx without gas specified and state override + gas = self.nodes[0].eth_estimateGas( + dict_txs["valid_balance_transfer_tx_without_gas"], "latest", state_override + ) + assert_equal(gas, "0x5208") + + # Test invalid estimateGas call, both gasPrice and maxFeePerGas specified and state override + assert_raises_rpc_error( + -32001, + "both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified", + self.nodes[0].eth_estimateGas, + dict_txs["invalid_balance_transfer_tx_specified_gas_1"], + "latest", + state_override, + ) + + # Test invalid estimateGas call, both gasPrice and maxPriorityFeePerGas specified and state override + assert_raises_rpc_error( + -32001, + "Custom error: both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified", + self.nodes[0].eth_estimateGas, + dict_txs["invalid_balance_transfer_tx_specified_gas_2"], + "latest", + state_override, + ) + + # Test invalid estimateGas call, both data and input field specified and state override + assert_raises_rpc_error( + -32001, + "Custom error: data and input fields are mutually exclusive", + self.nodes[0].eth_estimateGas, + dict_txs["invalid_balance_transfer_tx_specified_data_and_input"], + "latest", + state_override, + ) + + # Test invalid estimateGas call, insufficient funds and state override + assert_raises_rpc_error( + -32001, + "Custom error: insufficient funds for transfer", + self.nodes[0].eth_estimateGas, + dict_txs["invalid_balance_transfer_tx_insufficient_funds"], + "latest", + state_override, + ) + def run_test(self): self.setup() @@ -366,6 +466,8 @@ def run_test(self): self.test_estimate_gas_revert() + self.test_estimate_gas_state_override() + if __name__ == "__main__": EVMGasTest().main()