From 23f782ddafdb51dec9d387acec37e5ec133acbc0 Mon Sep 17 00:00:00 2001 From: wolflo Date: Tue, 1 Sep 2020 14:55:20 +0400 Subject: [PATCH] Update acct existence check in EVM.SELFDESTRUCT_gas --- manticore/platforms/evm.py | 2 +- tests/ethereum/test_general.py | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/manticore/platforms/evm.py b/manticore/platforms/evm.py index 921338c6c..eb6087ddf 100644 --- a/manticore/platforms/evm.py +++ b/manticore/platforms/evm.py @@ -2277,7 +2277,7 @@ def SELFDESTRUCT_gas(self, recipient): CreateBySelfdestructGas = 25000 SelfdestructRefundGas = 24000 fee = 0 - if recipient not in self.world and self.world.get_balance(self.address) != 0: + if not self.world.account_exists(recipient) and self.world.get_balance(self.address) != 0: fee += CreateBySelfdestructGas if self.address not in self.world._deleted_accounts: diff --git a/tests/ethereum/test_general.py b/tests/ethereum/test_general.py index 22e34f459..f7f154aeb 100644 --- a/tests/ethereum/test_general.py +++ b/tests/ethereum/test_general.py @@ -1913,6 +1913,55 @@ def test_call_gas(self): GCALLSTATIC + GCALLVALUE + GCALLNEW - GCALLSTIPEND ) + def test_selfdestruct_gas(self): + GSDSTATIC = 26003 # 21000 + 3 (push op) + 5000 static cost for selfdestruct + GNEWACCOUNT = 25000 + RSELFDESTRUCT = 24000 + + with disposable_mevm() as m: + # empty call target + empty = m.create_account(address=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + # nonempty call target + nonempty = m.create_account(address=0x1111111111111111111111111111111111111111, nonce=1) + + asm_sd_empty = """ PUSH20 0xffffffffffffffffffffffffffffffffffffffff + SELFDESTRUCT + """ + asm_sd_nonempty = """ PUSH20 0x1111111111111111111111111111111111111111 + SELFDESTRUCT + """ + + caller = m.create_account( + address=0x222222222222222222222222222222222222222, balance=1000000000000000000 + ) + + # selfdestruct to empty acct with no value + sd_empty = m.create_account(code=EVMAsm.assemble(asm_sd_empty)) + m.transaction(caller=caller, address=sd_empty, data=b"", value=0, gas=50000000) + self.assertEqual(m.count_ready_states(), 1) + state = next(m.ready_states) + txs = state.platform.transactions + # no value, so only static cost charged and refund is gas_used / 2 + self.assertEqual(txs[-1].used_gas, round(GSDSTATIC - (GSDSTATIC / 2))) + + # selfdestruct to existing acct with value > 0 + sd_nonempty = m.create_account(code=EVMAsm.assemble(asm_sd_nonempty)) + m.transaction(caller=caller, address=sd_nonempty, data=b"", value=1, gas=50000000) + self.assertEqual(m.count_ready_states(), 1) + state = next(m.ready_states) + txs = state.platform.transactions + # recipient exists, so only static cost charged and refund is gas_used / 2 + self.assertEqual(txs[-1].used_gas, round(GSDSTATIC - (GSDSTATIC / 2))) + + # selfdestruct to empty acct with value > 0, forcing addition to state trie + sd_empty = m.create_account(code=EVMAsm.assemble(asm_sd_empty)) + m.transaction(caller=caller, address=sd_empty, data=b"", value=1, gas=50000000) + self.assertEqual(m.count_ready_states(), 1) + state = next(m.ready_states) + txs = state.platform.transactions + # new account gas charged and full refund returned + self.assertEqual(txs[-1].used_gas, GSDSTATIC + GNEWACCOUNT - RSELFDESTRUCT) + class EthPluginTests(unittest.TestCase): def test_FilterFunctions_fallback_function_matching(self):