From 067d2c0b4799294fb78d65f9b98ccf6be4f842e1 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 15 Sep 2021 04:39:44 +0100 Subject: [PATCH] Add BASEFEE opcode (#1513) * Add BASEFEE opcode * Add test --- mythril/laser/ethereum/instruction_data.py | 1 + mythril/laser/ethereum/instructions.py | 10 +++++ mythril/laser/ethereum/state/environment.py | 2 + .../transaction/transaction_models.py | 32 ++++++++++----- mythril/support/opcodes.py | 1 + .../instructions/berlin_fork_opcodes_test.py | 2 +- tests/instructions/codecopy_test.py | 2 +- tests/instructions/create2_test.py | 2 +- tests/instructions/create_test.py | 2 +- tests/instructions/extcodecopy_test.py | 4 +- tests/instructions/extcodehash_test.py | 2 +- tests/instructions/sar_test.py | 2 +- tests/instructions/shl_test.py | 2 +- tests/instructions/shr_test.py | 2 +- tests/instructions/static_call_test.py | 2 +- tests/instructions/test_basefee.py | 39 +++++++++++++++++++ 16 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 tests/instructions/test_basefee.py diff --git a/mythril/laser/ethereum/instruction_data.py b/mythril/laser/ethereum/instruction_data.py index 8b4f6890..0fb77602 100644 --- a/mythril/laser/ethereum/instruction_data.py +++ b/mythril/laser/ethereum/instruction_data.py @@ -175,6 +175,7 @@ OPCODES = { "CALL": {GAS: (700, 700 + 9000 + 25000), STACK: (7, 1)}, "CALLCODE": {GAS: (700, 700 + 9000 + 25000), STACK: (7, 1)}, "RETURN": {GAS: (0, 0), STACK: (2, 0)}, + "BASEFEE": {GAS: (2, 2), STACK: Z_OPERATOR_TUPLE}, "DELEGATECALL": {GAS: (700, 700 + 9000 + 25000), STACK: (6, 1)}, "STATICCALL": {GAS: (700, 700 + 9000 + 25000), STACK: (6, 1)}, "REVERT": {GAS: (0, 0), STACK: (2, 0)}, diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 676fa9e0..0d4fbc24 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -1041,6 +1041,16 @@ class Instruction: global_state.mstate.stack.append(global_state.environment.gasprice) return [global_state] + @StateTransition() + def basefee_(self, global_state: GlobalState) -> List[GlobalState]: + """ + + :param global_state: + :return: + """ + global_state.mstate.stack.append(global_state.environment.basefee) + return [global_state] + @StateTransition() def codecopy_(self, global_state: GlobalState) -> List[GlobalState]: """ diff --git a/mythril/laser/ethereum/state/environment.py b/mythril/laser/ethereum/state/environment.py index f3dbfcce..9a5837ad 100644 --- a/mythril/laser/ethereum/state/environment.py +++ b/mythril/laser/ethereum/state/environment.py @@ -21,6 +21,7 @@ class Environment: gasprice: ExprRef, callvalue: ExprRef, origin: ExprRef, + basefee: ExprRef, code=None, static=False, ) -> None: @@ -55,6 +56,7 @@ class Environment: self.origin = origin self.callvalue = callvalue self.static = static + self.basefee = basefee def __str__(self) -> str: """ diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index 0b93d1a1..3b1d9d53 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -69,6 +69,7 @@ class BaseTransaction: call_value=None, init_call_data=True, static=False, + base_fee=None, ) -> None: assert isinstance(world_state, WorldState) self.world_state = world_state @@ -77,14 +78,21 @@ class BaseTransaction: self.gas_price = ( gas_price if gas_price is not None - else symbol_factory.BitVecSym("gasprice{}".format(identifier), 256) + else symbol_factory.BitVecSym(f"gasprice{identifier}", 256) ) + + self.base_fee = ( + base_fee + if base_fee is not None + else symbol_factory.BitVecSym(f"basefee{identifier}", 256) + ) + self.gas_limit = gas_limit self.origin = ( origin if origin is not None - else symbol_factory.BitVecSym("origin{}".format(identifier), 256) + else symbol_factory.BitVecSym(f"origin{identifier}", 256) ) self.code = code @@ -102,7 +110,7 @@ class BaseTransaction: self.call_value = ( call_value if call_value is not None - else symbol_factory.BitVecSym("callvalue{}".format(identifier), 256) + else symbol_factory.BitVecSym(f"callvalue{identifier}", 256) ) self.static = static self.return_data = None # type: str @@ -161,6 +169,7 @@ class MessageCallTransaction(BaseTransaction): self.gas_price, self.call_value, self.origin, + self.base_fee, code=self.code or self.callee_account.code, static=self.static, ) @@ -196,6 +205,7 @@ class ContractCreationTransaction(BaseTransaction): call_value=None, contract_name=None, contract_address=None, + base_fee=None, ) -> None: self.prev_world_state = deepcopy(world_state) contract_address = ( @@ -219,18 +229,20 @@ class ContractCreationTransaction(BaseTransaction): code=code, call_value=call_value, init_call_data=True, + base_fee=base_fee, ) def initial_global_state(self) -> GlobalState: """Initialize the execution environment.""" environment = Environment( - self.callee_account, - self.caller, - self.call_data, - self.gas_price, - self.call_value, - self.origin, - self.code, + active_account=self.callee_account, + sender=self.caller, + calldata=self.call_data, + gasprice=self.gas_price, + callvalue=self.call_value, + origin=self.origin, + basefee=self.base_fee, + code=self.code, ) return super().initial_global_state_from_environment( environment, active_function="constructor" diff --git a/mythril/support/opcodes.py b/mythril/support/opcodes.py index 3ba79cc7..8cd86073 100644 --- a/mythril/support/opcodes.py +++ b/mythril/support/opcodes.py @@ -53,6 +53,7 @@ opcodes = { 0x45: ("GASLIMIT", 0, 1, 2), 0x46: ("CHAINID", 0, 1, 2), 0x47: ("SELFBALANCE", 0, 1, 5), + 0x48: ("BASEFEE", 0, 1, 2), 0x50: ("POP", 1, 0, 2), 0x51: ("MLOAD", 1, 1, 3), 0x52: ("MSTORE", 2, 0, 3), diff --git a/tests/instructions/berlin_fork_opcodes_test.py b/tests/instructions/berlin_fork_opcodes_test.py index b647d100..7c6ba133 100644 --- a/tests/instructions/berlin_fork_opcodes_test.py +++ b/tests/instructions/berlin_fork_opcodes_test.py @@ -17,7 +17,7 @@ def get_state(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("0x60045e005c5d") - environment = Environment(account, None, None, None, None, None) + environment = Environment(account, None, None, None, None, None, None) state = GlobalState(world_state, environment, None, MachineState(gas_limit=8000000)) state.transaction_stack.append( (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) diff --git a/tests/instructions/codecopy_test.py b/tests/instructions/codecopy_test.py index 8843199f..b7ed02aa 100644 --- a/tests/instructions/codecopy_test.py +++ b/tests/instructions/codecopy_test.py @@ -13,7 +13,7 @@ def test_codecopy_concrete(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606040") - environment = Environment(account, None, None, None, None, None) + environment = Environment(account, None, None, None, None, None, None) og_state = GlobalState( world_state, environment, None, MachineState(gas_limit=8000000) ) diff --git a/tests/instructions/create2_test.py b/tests/instructions/create2_test.py index ffe72917..1cba44f4 100644 --- a/tests/instructions/create2_test.py +++ b/tests/instructions/create2_test.py @@ -34,7 +34,7 @@ def test_create2(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606040") - environment = Environment(account, None, None, None, None, None) + environment = Environment(account, None, None, None, None, None, None) og_state = GlobalState( world_state, environment, None, MachineState(gas_limit=8000000) ) diff --git a/tests/instructions/create_test.py b/tests/instructions/create_test.py index 8abab624..03f23634 100644 --- a/tests/instructions/create_test.py +++ b/tests/instructions/create_test.py @@ -21,7 +21,7 @@ def test_create(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606060") - environment = Environment(account, None, None, None, None, None) + environment = Environment(account, None, None, None, None, None, None) og_state = GlobalState( world_state, environment, None, MachineState(gas_limit=8000000) ) diff --git a/tests/instructions/extcodecopy_test.py b/tests/instructions/extcodecopy_test.py index ca006fc2..8dc3e4b2 100644 --- a/tests/instructions/extcodecopy_test.py +++ b/tests/instructions/extcodecopy_test.py @@ -16,7 +16,7 @@ def test_extcodecopy(): ext_account = new_world_state.create_account(balance=1000, address=121) ext_account.code = Disassembly("6040404040") - new_environment = Environment(new_account, None, None, None, None, None) + new_environment = Environment(new_account, None, None, None, None, None, None) state = GlobalState( new_world_state, new_environment, None, MachineState(gas_limit=8000000) ) @@ -38,7 +38,7 @@ def test_extcodecopy_fail(): new_world_state = WorldState() new_account = new_world_state.create_account(balance=10, address=101) new_account.code = Disassembly("60616240") - new_environment = Environment(new_account, None, None, None, None, None) + new_environment = Environment(new_account, None, None, None, None, None, None) state = GlobalState( new_world_state, new_environment, None, MachineState(gas_limit=8000000) ) diff --git a/tests/instructions/extcodehash_test.py b/tests/instructions/extcodehash_test.py index 14f2ad65..00dbce72 100644 --- a/tests/instructions/extcodehash_test.py +++ b/tests/instructions/extcodehash_test.py @@ -16,7 +16,7 @@ world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606040") world_state.create_account(balance=10, address=1000) -environment = Environment(account, None, None, None, None, None) +environment = Environment(account, None, None, None, None, None, None) og_state = GlobalState(world_state, environment, None, MachineState(gas_limit=8000000)) og_state.transaction_stack.append( (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) diff --git a/tests/instructions/sar_test.py b/tests/instructions/sar_test.py index 3a23da13..7b8c773f 100644 --- a/tests/instructions/sar_test.py +++ b/tests/instructions/sar_test.py @@ -16,7 +16,7 @@ def get_state(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606040") - environment = Environment(account, None, None, None, None, None) + environment = Environment(account, None, None, None, None, None, None) state = GlobalState(world_state, environment, None, MachineState(gas_limit=8000000)) state.transaction_stack.append( (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) diff --git a/tests/instructions/shl_test.py b/tests/instructions/shl_test.py index fb1680a5..5233ca6c 100644 --- a/tests/instructions/shl_test.py +++ b/tests/instructions/shl_test.py @@ -15,7 +15,7 @@ def get_state(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606040") - environment = Environment(account, None, None, None, None, None) + environment = Environment(account, None, None, None, None, None, None) state = GlobalState(world_state, environment, None, MachineState(gas_limit=8000000)) state.transaction_stack.append( (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) diff --git a/tests/instructions/shr_test.py b/tests/instructions/shr_test.py index f0f66787..e6840d08 100644 --- a/tests/instructions/shr_test.py +++ b/tests/instructions/shr_test.py @@ -15,7 +15,7 @@ def get_state(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606040") - environment = Environment(account, None, None, None, None, None) + environment = Environment(account, None, None, None, None, None, None) state = GlobalState(world_state, environment, None, MachineState(gas_limit=8000000)) state.transaction_stack.append( (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) diff --git a/tests/instructions/static_call_test.py b/tests/instructions/static_call_test.py index 0d27eb21..d0611ff0 100644 --- a/tests/instructions/static_call_test.py +++ b/tests/instructions/static_call_test.py @@ -19,7 +19,7 @@ from mythril.laser.ethereum.evm_exceptions import WriteProtection def get_global_state(): active_account = Account("0x0", code=Disassembly("60606040")) environment = Environment( - active_account, None, SymbolicCalldata("2"), None, None, None + active_account, None, SymbolicCalldata("2"), None, None, None, None ) world_state = WorldState() world_state.put_account(active_account) diff --git a/tests/instructions/test_basefee.py b/tests/instructions/test_basefee.py new file mode 100644 index 00000000..0a29d3ef --- /dev/null +++ b/tests/instructions/test_basefee.py @@ -0,0 +1,39 @@ +from mythril.disassembler.disassembly import Disassembly +from mythril.laser.ethereum.state.environment import Environment +from mythril.laser.ethereum.state.machine_state import MachineState +from mythril.laser.ethereum.state.global_state import GlobalState +from mythril.laser.ethereum.state.world_state import WorldState +from mythril.laser.ethereum.instructions import Instruction +from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction +from mythril.laser.smt import symbol_factory + + +def test_basefee(): + # Arrange + world_state = WorldState() + account = world_state.create_account(balance=10, address=101) + account.code = Disassembly("60606040") + environment = Environment( + account, + None, + None, + None, + None, + None, + basefee=symbol_factory.BitVecSym("gasfee", 256), + ) + og_state = GlobalState( + world_state, environment, None, MachineState(gas_limit=8000000) + ) + og_state.transaction_stack.append( + (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) + ) + + og_state.mstate.stack = [] + instruction = Instruction("basefee", dynamic_loader=None) + + # Act + new_state = instruction.evaluate(og_state)[0] + + # Assert + assert new_state.mstate.stack == [symbol_factory.BitVecSym("gasfee", 256)]