import json from mythril.analysis.symbolic import SymExecWrapper from mythril.analysis.callgraph import generate_graph from mythril.ether.ethcontract import ETHContract from mythril.ether.soliditycontract import SolidityContract from mythril.laser.ethereum.state import GlobalState, MachineState, Account from mythril.laser.ethereum import svm from tests import * class LaserEncoder(json.JSONEncoder): def default(self, o): if getattr(o, "__module__", None) == "z3.z3": return str(o) return str(o) def _all_info(laser): accounts = {} for address, _account in laser.world_state.accounts.items(): account = _account.as_dict account["code"] = account["code"].instruction_list account["balance"] = str(account["balance"]) accounts[address] = account nodes = {} for uid, node in laser.nodes.items(): states = [] for state in node.states: if isinstance(state, MachineState): states.append(state.as_dict) elif isinstance(state, GlobalState): environment = state.environment.as_dict environment["active_account"] = environment["active_account"].address states.append( { "accounts": state.accounts.keys(), "environment": environment, "mstate": state.mstate.as_dict, } ) nodes[uid] = { "uid": node.uid, "contract_name": node.contract_name, "start_addr": node.start_addr, "states": states, "constraints": node.constraints, "function_name": node.function_name, "flags": str(node.flags), } edges = [edge.as_dict for edge in laser.edges] return { "accounts": accounts, "nodes": nodes, "edges": edges, "total_states": laser.total_states, "max_depth": laser.max_depth, } class SVMTestCase(BaseTestCase): def setUp(self): super(SVMTestCase, self).setUp() svm.gbl_next_uid = 0 def test_laser_result(self): for input_file in TESTDATA_INPUTS_CONTRACTS.iterdir(): if input_file.name in ["weak_random.sol", "environments.sol"]: continue output_expected = TESTDATA_OUTPUTS_EXPECTED_LASER_RESULT / ( input_file.name + ".json" ) output_current = TESTDATA_OUTPUTS_CURRENT_LASER_RESULT / ( input_file.name + ".json" ) disassembly = SolidityContract(str(input_file)).disassembly account = Account("0x0000000000000000000000000000000000000000", disassembly) accounts = {account.address: account} laser = svm.LaserEVM(accounts, max_depth=22) laser.sym_exec(account.address) laser_info = _all_info(laser) output_current.write_text( json.dumps(laser_info, cls=LaserEncoder, indent=4) ) if not (output_expected.read_text() == output_expected.read_text()): self.found_changed_files(input_file, output_expected, output_current) self.assert_and_show_changed_files() def runTest(self): code = "0x60606040525b603c5b60006010603e565b9050593681016040523660008237602060003683856040603f5a0204f41560545760206000f35bfe5b50565b005b73c3b2ae46792547a96b9f84405e36d0e07edcd05c5b905600a165627a7a7230582062a884f947232ada573f95940cce9c8bfb7e4e14e21df5af4e884941afb55e590029" contract = ETHContract(code) sym = SymExecWrapper(contract, "0xd0a6E6C543bC68Db5db3A191B171A77407Ff7ccf") html = generate_graph(sym) self.assertTrue("0 PUSH1 0x60\\n2 PUSH1 0x40\\n4 MSTORE\\n5 JUMPDEST" in html)