def test_concrete_call_symbolic_to(): # arrange address = "0x10" active_account = Account(address) active_account.code = Disassembly("00") environment = Environment(active_account, None, None, None, None, None) state = GlobalState(None, environment, None) state.mstate.memory = ["placeholder", "calldata_bling_0"] node = Node("example") node.contract_name = "the contract name" node.function_name = "the function name" to = Variable("calldata_3", VarType.SYMBOLIC) meminstart = Variable(1, VarType.CONCRETE) call = Call(node, state, None, None, to, None) # act issues = _concrete_call(call, state, address, meminstart) # assert issue = issues[0] assert issue.address == address assert issue.contract == node.contract_name assert issue.function == node.function_name assert issue.title == "Call data forwarded with delegatecall()" assert issue.type == "Informational" assert ( issue.description == "This contract forwards its call data via DELEGATECALL in its fallback function." " This means that any function in the called contract can be executed." " Note that the callee contract will have access to the storage of the " "calling contract.\n DELEGATECALL target: calldata_3" )
def test_delegate_call(sym_mock, concrete_mock, curr_instruction): # arrange # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call") # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call") sym_mock.return_value = [] concrete_mock.return_value = [] curr_instruction.return_value = {"address": "0x10"} active_account = Account("0x10") active_account.code = Disassembly("00") environment = Environment(active_account, None, None, None, None, None) state = GlobalState(None, environment, Node) state.mstate.memory = ["placeholder", "calldata_bling_0"] state.mstate.stack = [1, 2, 3] assert state.get_current_instruction() == {"address": "0x10"} node = Node("example") node.contract_name = "the contract name" node.function_name = "fallback" to = Variable("storage_1", VarType.SYMBOLIC) call = Call(node, state, None, "DELEGATECALL", to, None) statespace = MagicMock() statespace.calls = [call] # act execute(statespace) # assert assert concrete_mock.call_count == 1 assert sym_mock.call_count == 1
def test_symbolic_call_storage_to(mocker): # arrange address = "0x10" active_account = Account(address) active_account.code = Disassembly("00") environment = Environment(active_account, None, None, None, None, None) state = GlobalState(None, environment, None) state.mstate.memory = ["placeholder", "calldata_bling_0"] node = Node("example") node.contract_name = "the contract name" node.function_name = "the function name" to = Variable("storage_1", VarType.SYMBOLIC) call = Call(node, state, None, "Type: ", to, None) mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None) statespace = SymExecWrapper(1) mocker.patch.object(statespace, "find_storage_write") statespace.find_storage_write.return_value = "Function name" # act issues = _symbolic_call(call, state, address, statespace) # assert issue = issues[0] assert issue.address == address assert issue.contract == node.contract_name assert issue.function == node.function_name assert issue.title == "Type: to a user-supplied address" assert issue.type == "Informational" assert ( issue.description == "This contract delegates execution to a contract address in storage slot 1." " This storage slot can be written to by calling the function `Function name`. " "Be aware that the called contract gets unrestricted access to this contract's state." )
def test_vmtest(test_name: str, pre_condition: dict, action: dict, post_condition: dict) -> None: # Arrange if test_name == "gasprice": return accounts = {} for address, details in pre_condition.items(): account = Account(address) account.code = Disassembly(details["code"][2:]) account.balance = int(details["balance"], 16) account.nonce = int(details["nonce"], 16) accounts[address] = account laser_evm = LaserEVM(accounts) # Act laser_evm.time = datetime.now() # TODO: move this line below and check for VmExceptions when gas has been implemented if post_condition == {}: return execute_message_call( laser_evm, callee_address=action["address"], caller_address=action["caller"], origin_address=binascii.a2b_hex(action["origin"][2:]), code=action["code"][2:], gas=action["gas"], data=binascii.a2b_hex(action["data"][2:]), gas_price=int(action["gasPrice"], 16), value=int(action["value"], 16), ) # Assert assert len(laser_evm.open_states) == 1 world_state = laser_evm.open_states[0] model = get_model( next(iter(laser_evm.nodes.values())).states[0].mstate.constraints) for address, details in post_condition.items(): account = world_state[address] assert account.nonce == int(details["nonce"], 16) assert account.code.bytecode == details["code"][2:] for index, value in details["storage"].items(): expected = int(value, 16) actual = account.storage[int(index, 16)] if isinstance(actual, ExprRef): actual = model.eval(actual) actual = ( 1 if actual == True else 0 if actual == False else actual ) # Comparisons should be done with == than 'is' here as actual can be a BoolRef else: if type(actual) == bytes: actual = int(binascii.b2a_hex(actual), 16) elif type(actual) == str: actual = int(actual, 16) assert actual == expected
def test_vmtest( test_name: str, environment: dict, pre_condition: dict, action: dict, gas_used: int, post_condition: dict, ) -> None: # Arrange if test_name == "gasprice": return accounts = {} for address, details in pre_condition.items(): account = Account(address) account.code = Disassembly(details["code"][2:]) account.balance = int(details["balance"], 16) account.nonce = int(details["nonce"], 16) accounts[address] = account laser_evm = LaserEVM(accounts) # Act laser_evm.time = datetime.now() final_states = execute_message_call( laser_evm, callee_address=action["address"], caller_address=action["caller"], origin_address=binascii.a2b_hex(action["origin"][2:]), code=action["code"][2:], gas_limit=int(action["gas"], 16), data=binascii.a2b_hex(action["data"][2:]), gas_price=int(action["gasPrice"], 16), value=int(action["value"], 16), track_gas=True, ) # Assert if gas_used is not None and gas_used < int(environment["currentGasLimit"], 16): # avoid gas usage larger than block gas limit # this currently exceeds our estimations gas_min_max = [(s.mstate.min_gas_used, s.mstate.max_gas_used) for s in final_states] gas_ranges = [g[0] <= gas_used for g in gas_min_max] assert all(map(lambda g: g[0] <= g[1], gas_min_max)) assert any(gas_ranges) if any( (v in test_name for v in ["error", "oog"])) and post_condition == {}: # no more work to do if error happens or out of gas assert len(laser_evm.open_states) == 0 else: assert len(laser_evm.open_states) == 1 world_state = laser_evm.open_states[0] model = get_model( next(iter(laser_evm.nodes.values())).states[0].mstate.constraints, enforce_execution_time=False, ) for address, details in post_condition.items(): account = world_state[address] assert account.nonce == int(details["nonce"], 16) assert account.code.bytecode == details["code"][2:] for index, value in details["storage"].items(): expected = int(value, 16) actual = account.storage[int(index, 16)] if isinstance(actual, Expression): actual = actual.value actual = 1 if actual is True else 0 if actual is False else actual else: if type(actual) == bytes: actual = int(binascii.b2a_hex(actual), 16) elif type(actual) == str: actual = int(actual, 16) assert actual == expected
def test_vmtest( test_name: str, environment: dict, pre_condition: dict, action: dict, gas_used: int, post_condition: dict, ) -> None: # Arrange if test_name in ignored_test_names: return world_state = WorldState() for address, details in pre_condition.items(): account = Account(address, concrete_storage=True) account.code = Disassembly(details["code"][2:]) account.nonce = int(details["nonce"], 16) for key, value in details["storage"].items(): account.storage[int(key, 16)] = int(value, 16) world_state.put_account(account) account.set_balance(int(details["balance"], 16)) laser_evm = LaserEVM() laser_evm.open_states = [world_state] # Act laser_evm.time = datetime.now() final_states = execute_message_call( laser_evm, callee_address=symbol_factory.BitVecVal(int(action["address"], 16), 256), caller_address=symbol_factory.BitVecVal(int(action["caller"], 16), 256), origin_address=symbol_factory.BitVecVal(int(action["origin"], 16), 256), code=action["code"][2:], gas_limit=int(action["gas"], 16), data=binascii.a2b_hex(action["data"][2:]), gas_price=int(action["gasPrice"], 16), value=int(action["value"], 16), track_gas=True, ) # Assert if gas_used is not None and gas_used < int(environment["currentGasLimit"], 16): # avoid gas usage larger than block gas limit # this currently exceeds our estimations gas_min_max = [(s.mstate.min_gas_used, s.mstate.max_gas_used) for s in final_states] gas_ranges = [g[0] <= gas_used for g in gas_min_max] assert all(map(lambda g: g[0] <= g[1], gas_min_max)) assert any(gas_ranges) if post_condition == {}: # no more work to do if error happens or out of gas assert len(laser_evm.open_states) == 0 else: assert len(laser_evm.open_states) == 1 world_state = laser_evm.open_states[0] for address, details in post_condition.items(): account = world_state[symbol_factory.BitVecVal( int(address, 16), 256)] assert account.nonce == int(details["nonce"], 16) assert account.code.bytecode == details["code"][2:] for index, value in details["storage"].items(): expected = int(value, 16) actual = account.storage[int(index, 16)] if isinstance(actual, Expression): actual = actual.value actual = 1 if actual is True else 0 if actual is False else actual else: if type(actual) == bytes: actual = int(binascii.b2a_hex(actual), 16) elif type(actual) == str: actual = int(actual, 16) assert actual == expected