def test_istanbul_fork(self): insn = EVMAsm.disassemble_one(b'\x31', fork='istanbul') self.assertTrue(insn.mnemonic == 'BALANCE') self.assertTrue(insn.fee == 700) self.assertTrue(insn.pops == 1) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b'\x3f', fork='istanbul') self.assertTrue(insn.mnemonic == 'EXTCODEHASH') self.assertTrue(insn.fee == 700) self.assertTrue(insn.pops == 1) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b'\x46', fork='istanbul') self.assertTrue(insn.mnemonic == 'CHAINID') self.assertTrue(insn.fee == 2) self.assertTrue(insn.pops == 0) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b'\x47', fork='istanbul') self.assertTrue(insn.mnemonic == 'SELFBALANCE') self.assertTrue(insn.fee == 5) self.assertTrue(insn.pops == 0) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b'\x54', fork='istanbul') self.assertTrue(insn.mnemonic == 'SLOAD') self.assertTrue(insn.fee == 800) self.assertTrue(insn.pops == 1) self.assertTrue(insn.pushes == 1)
def test_istanbul_fork(self): insn = EVMAsm.disassemble_one(b"\x31", fork="istanbul") self.assertTrue(insn.mnemonic == "BALANCE") self.assertTrue(insn.fee == 700) self.assertTrue(insn.pops == 1) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b"\x3f", fork="istanbul") self.assertTrue(insn.mnemonic == "EXTCODEHASH") self.assertTrue(insn.fee == 700) self.assertTrue(insn.pops == 1) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b"\x46", fork="istanbul") self.assertTrue(insn.mnemonic == "CHAINID") self.assertTrue(insn.fee == 2) self.assertTrue(insn.pops == 0) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b"\x47", fork="istanbul") self.assertTrue(insn.mnemonic == "SELFBALANCE") self.assertTrue(insn.fee == 5) self.assertTrue(insn.pops == 0) self.assertTrue(insn.pushes == 1) insn = EVMAsm.disassemble_one(b"\x54", fork="istanbul") self.assertTrue(insn.mnemonic == "SLOAD") self.assertTrue(insn.fee == 800) self.assertTrue(insn.pops == 1) self.assertTrue(insn.pushes == 1)
def test_pre_byzantium(self): insn = EVMAsm.disassemble_one(b'\x57', fork='frontier') self.assertTrue(insn.mnemonic == 'JUMPI') self.assertTrue(insn.is_branch) insn = EVMAsm.disassemble_one(b'\xfa', fork='frontier') self.assertTrue(insn.mnemonic == 'INVALID') # STATICCALL added in byzantium insn = EVMAsm.disassemble_one(b'\xfd', fork='frontier') self.assertTrue(insn.mnemonic == 'INVALID') # REVERT added in byzantium
def test_pre_byzantium(self): insn = EVMAsm.disassemble_one(b"\x57", fork="frontier") self.assertTrue(insn.mnemonic == "JUMPI") self.assertTrue(insn.is_branch) insn = EVMAsm.disassemble_one(b"\xfa", fork="frontier") self.assertTrue( insn.mnemonic == "INVALID") # STATICCALL added in byzantium insn = EVMAsm.disassemble_one(b"\xfd", fork="frontier") self.assertTrue( insn.mnemonic == "INVALID") # REVERT added in byzantium
def test_constantinople_fork(self): insn = EVMAsm.disassemble_one(b'\x1b', fork='constantinople') self.assertTrue(insn.mnemonic == 'SHL') self.assertTrue(insn.is_arithmetic) insn = EVMAsm.disassemble_one(b'\x1c', fork='constantinople') self.assertTrue(insn.mnemonic == 'SHR') self.assertTrue(insn.is_arithmetic) insn = EVMAsm.disassemble_one(b'\x1d', fork='constantinople') self.assertTrue(insn.mnemonic == 'SAR') self.assertTrue(insn.is_arithmetic) insn = EVMAsm.disassemble_one(b'\x3f', fork='constantinople') self.assertTrue(insn.mnemonic == 'EXTCODEHASH') insn = EVMAsm.disassemble_one(b'\xf5', fork='constantinople') self.assertTrue(insn.mnemonic == 'CREATE2')
def test_constantinople_fork(self): insn = EVMAsm.disassemble_one(b"\x1b", fork="constantinople") self.assertTrue(insn.mnemonic == "SHL") self.assertTrue(insn.is_arithmetic) insn = EVMAsm.disassemble_one(b"\x1c", fork="constantinople") self.assertTrue(insn.mnemonic == "SHR") self.assertTrue(insn.is_arithmetic) insn = EVMAsm.disassemble_one(b"\x1d", fork="constantinople") self.assertTrue(insn.mnemonic == "SAR") self.assertTrue(insn.is_arithmetic) insn = EVMAsm.disassemble_one(b"\x3f", fork="constantinople") self.assertTrue(insn.mnemonic == "EXTCODEHASH") insn = EVMAsm.disassemble_one(b"\xf5", fork="constantinople") self.assertTrue(insn.mnemonic == "CREATE2")
def get_instruction_text(self, data, addr): instruction = disassemble_one(data, addr) tokens = [] tokens.append( InstructionTextToken( InstructionTextTokenType.TextToken, "{:7} ".format( instruction.name ) ) ) if instruction.name.startswith('PUSH'): tokens.append( InstructionTextToken( InstructionTextTokenType.IntegerToken, '#{:0{i.operand_size}x}'.format( instruction.operand, i=instruction ), instruction.operand ) ) return tokens, instruction.size
def test_ADD_1(self): instruction = EVMAsm.disassemble_one(b'\x60\x10') self.assertEqual( EVMAsm.Instruction(0x60, 'PUSH', 1, 0, 1, 0, 'Place 1 byte item on stack.', 16, 0), instruction) instruction = EVMAsm.assemble_one('PUSH1 0x10') EVMAsm.Instruction(0x60, 'PUSH', 1, 0, 1, 0, 'Place 1 byte item on stack.', 16, 0) instructions1 = EVMAsm.disassemble_all(b'\x30\x31') instructions2 = EVMAsm.assemble_all('ADDRESS\nBALANCE') self.assertTrue( all(a == b for a, b in zip(instructions1, instructions2))) # High level simple assembler/disassembler bytecode = EVMAsm.assemble_hex("""PUSH1 0x80 BLOCKHASH MSTORE PUSH1 0x2 PUSH2 0x100 """) self.assertEqual(bytecode, '0x608040526002610100') asmcode = EVMAsm.disassemble_hex('0x608040526002610100') self.assertEqual( asmcode, '''PUSH1 0x80\nBLOCKHASH\nMSTORE\nPUSH1 0x2\nPUSH2 0x100''')
def test_codehash_contract(self): evm_code = make_evm_ext_code( disassemble_one(bytes.fromhex("3f")), "0x895521964D724c8362A36608AAf09A3D7d0A0445", ) hex_code = assemble_hex(evm_code) code_hash = int.from_bytes(eth_utils.crypto.keccak(hexstr=hex_code), byteorder="big") contract_a = make_contract(evm_code, "uint256") contracts = create_many_contracts(contract_a) vm = create_evm_vm(contracts, True, False) output_handler = create_output_handler(contracts) vm.env.messages = messagestack.addMessage( value.Tuple([]), value.Tuple( make_msg_val( value.Tuple([0, 2345, contract_a.testMethod(0, 0)]) # type # sender )), ) run_until_block(vm, self) self.assertEqual(len(vm.logs), 1) val = vm.logs[0] parsed_out = output_handler(val) self.assertIsInstance(parsed_out, EVMReturn) self.assertEqual(parsed_out.output_values[0], code_hash)
def get_instruction_low_level_il(self, data, addr, il): instruction = disassemble_one(data, addr) ill = insn_il.get(instruction.name, None) if ill is None: for i in range(instruction.pops): il.append( il.set_reg(ADDR_SIZE, LLIL_TEMP(i), il.pop(ADDR_SIZE)) ) for i in range(instruction.pushes): il.append(il.push(ADDR_SIZE, il.unimplemented())) il.append(il.nop()) return instruction.size ils = ill(il, addr, instruction.operand) if isinstance(ils, list): for i in ils: il.append(il) else: il.append(ils) return instruction.size
def test_ADD_1(self): instruction = EVMAsm.disassemble_one(b"\x60\x10") self.assertEqual( EVMAsm.Instruction(0x60, "PUSH", 1, 0, 1, 3, "Place 1 byte item on stack.", 16, 0), instruction) instruction = EVMAsm.assemble_one("PUSH1 0x10") self.assertEqual( instruction, EVMAsm.Instruction(0x60, "PUSH", 1, 0, 1, 3, "Place 1 byte item on stack.", 16, 0)) instructions1 = EVMAsm.disassemble_all(b"\x30\x31") instructions2 = EVMAsm.assemble_all("ADDRESS\nBALANCE") self.assertTrue( all(a == b for a, b in zip(instructions1, instructions2))) # High level simple assembler/disassembler bytecode = EVMAsm.assemble_hex("""PUSH1 0x80 BLOCKHASH MSTORE PUSH1 0x2 PUSH2 0x100 """) self.assertEqual(bytecode, "0x608040526002610100") asmcode = EVMAsm.disassemble_hex("0x608040526002610100") self.assertEqual( asmcode, """PUSH1 0x80\nBLOCKHASH\nMSTORE\nPUSH1 0x2\nPUSH2 0x100""")
def test_consistency_assembler_disassembler(self): """ Tests whether every opcode that can be disassembled, can also be assembled again. """ inst_table = EVMAsm.instruction_tables[EVMAsm.DEFAULT_FORK] for opcode in inst_table.keys(): b = int_to_bytes(opcode) + b"\x00" * 32 inst_dis = EVMAsm.disassemble_one(b) a = str(inst_dis) inst_as = EVMAsm.assemble_one(a) self.assertEqual(inst_dis, inst_as)
def test_codehash_fail(self): evm_code = make_evm_ext_code(disassemble_one(bytes.fromhex('3f')), "0x9999") contract_a = make_contract(evm_code, "uint256") vm = create_many_contract_vm(contract_a) vm.env.send_message( [make_msg_val(contract_a.testMethod(4)), 2345, 0, 0]) vm.env.deliver_pending() run_until_block(vm, self) self.assertEqual(len(vm.logs), 1) val = vm.logs[0] parsed_out = vm.output_handler(val) self.assertIsInstance(parsed_out, EVMInvalid)
def get_instruction_info(self, data, addr): instruction = disassemble_one(data, addr) result = InstructionInfo() result.length = instruction.size if instruction.name == "JUMP": result.add_branch(BranchType.UnresolvedBranch) elif instruction.name == "JUMPI": result.add_branch(BranchType.UnresolvedBranch) result.add_branch(BranchType.FalseBranch, addr + 1) elif instruction.name in ('RETURN', 'REVERT', 'SUICIDE', 'INVALID', 'STOP', 'SELFDESTRUCT'): result.add_branch(BranchType.FunctionReturn) return result
def disassemble_one(bytecode: Iterable, pc: int = 0, fork=pyevmasm.DEFAULT_FORK) -> EVMInstruction: ''' Decode a single instruction from a bytecode :param bytecode: the bytecode stream :param pc: program counter of the instruction in the bytecode(optional) :type bytecode: iterator/sequence/str :return: an Instruction object Example use:: >>> print EVMAsm.assemble_one('PUSH1 0x10') ''' instruction = pyevmasm.disassemble_one(bytecode, pc, fork) return EVMAsm.convert_instruction_to_evminstruction(instruction)
def test_codehash_succeed(self): evm_code = make_evm_ext_code( disassemble_one(bytes.fromhex('3f')), "0x895521964D724c8362A36608AAf09A3D7d0A0445") hex_code = assemble_hex(evm_code) code_hash = int.from_bytes(eth_utils.crypto.keccak(hexstr=hex_code), byteorder="big") contract_a = make_contract(evm_code, "uint256") vm = create_many_contract_vm(contract_a) vm.env.send_message( [make_msg_val(contract_a.testMethod(4)), 2345, 0, 0]) vm.env.deliver_pending() run_until_block(vm, self) self.assertEqual(len(vm.logs), 1) val = vm.logs[0] parsed_out = vm.output_handler(val) self.assertIsInstance(parsed_out, EVMCall) self.assertEqual(parsed_out.output_values[0], code_hash)
def test_byzantium_fork(self): insn = EVMAsm.disassemble_one(b'\x57', fork='byzantium') self.assertTrue(insn.mnemonic == 'JUMPI') self.assertTrue(insn.is_branch) insn = EVMAsm.disassemble_one(b'\x1b', fork='byzantium') self.assertTrue(insn.mnemonic == 'INVALID') # SHL added in constantinople insn = EVMAsm.disassemble_one(b'\x1c', fork='byzantium') self.assertTrue(insn.mnemonic == 'INVALID') # SHR added in constantinople insn = EVMAsm.disassemble_one(b'\x1d', fork='byzantium') self.assertTrue(insn.mnemonic == 'INVALID') # SAR added in constantinople insn = EVMAsm.disassemble_one(b'\x3f', fork='byzantium') self.assertTrue(insn.mnemonic == 'INVALID') # EXTCODEHASH added in constantinople insn = EVMAsm.disassemble_one(b'\xf5', fork='byzantium') self.assertTrue(insn.mnemonic == 'INVALID') # CREATE2 added in constantinople
def test_codehash_empty(self): evm_code = make_evm_ext_code(disassemble_one(bytes.fromhex("3f")), "0x9999") contract_a = make_contract(evm_code, "uint256") contracts = create_many_contracts(contract_a) vm = create_evm_vm(contracts, True, False) output_handler = create_output_handler(contracts) vm.env.messages = messagestack.addMessage( value.Tuple([]), value.Tuple( make_msg_val( value.Tuple([0, 2345, contract_a.testMethod(0, 0)]) # type # sender )), ) run_until_block(vm, self) self.assertEqual(len(vm.logs), 1) val = vm.logs[0] parsed_out = output_handler(val) self.assertIsInstance(parsed_out, EVMReturn) self.assertEqual(parsed_out.output_values[0], 0)
def test_byzantium_fork(self): insn = EVMAsm.disassemble_one(b"\x57", fork="byzantium") self.assertTrue(insn.mnemonic == "JUMPI") self.assertTrue(insn.is_branch) insn = EVMAsm.disassemble_one(b"\x1b", fork="byzantium") self.assertTrue( insn.mnemonic == "INVALID") # SHL added in constantinople insn = EVMAsm.disassemble_one(b"\x1c", fork="byzantium") self.assertTrue( insn.mnemonic == "INVALID") # SHR added in constantinople insn = EVMAsm.disassemble_one(b"\x1d", fork="byzantium") self.assertTrue( insn.mnemonic == "INVALID") # SAR added in constantinople insn = EVMAsm.disassemble_one(b"\x3f", fork="byzantium") self.assertTrue( insn.mnemonic == "INVALID") # EXTCODEHASH added in constantinople insn = EVMAsm.disassemble_one(b"\xf5", fork="byzantium") self.assertTrue( insn.mnemonic == "INVALID") # CREATE2 added in constantinople
def test_JUMPI(self): insn = EVMAsm.disassemble_one(b"\x57") self.assertTrue(insn.mnemonic == "JUMPI") self.assertTrue(insn.is_branch)
def test_STOP(self): insn = EVMAsm.disassemble_one(b"\x00") self.assertTrue(insn.mnemonic == "STOP")
print("\toperand_size:", instruction.operand_size) print("\toperand:", instruction.operand) print("\tsemantics:", instruction.semantics) print("\tpops:", instruction.pops) print("\tpushes:", instruction.pushes) print(f"\tbytes: 0x" + hexlify(instruction.bytes).decode()) print("\twrites to stack:", instruction.writes_to_stack) print("\treads from stack:", instruction.reads_from_stack) print("\twrites to memory:", instruction.writes_to_memory) print("\treads from memory:", instruction.reads_from_memory) print("\twrites to storage:", instruction.writes_to_storage) print("\treads from storage:", instruction.reads_from_storage) print("\tis terminator", instruction.is_terminator) instruction = ea.disassemble_one("\x60\x10") printi(instruction) instruction = ea.assemble_one("PUSH1 0x10") printi(instruction) for instruction in ea.disassemble_all("\x30\x31"): printi(instruction) for instruction in ea.assemble_all("ADDRESS\nBALANCE"): printi(instruction) # High level simple assembler/disassembler print( ea.assemble_hex("""PUSH1 0x60 BLOCKHASH
def test_STOP(self): insn = EVMAsm.disassemble_one(b'\x00') self.assertTrue(insn.mnemonic == 'STOP')
def test_JUMPI(self): insn = EVMAsm.disassemble_one(b'\x57') self.assertTrue(insn.mnemonic == 'JUMPI') self.assertTrue(insn.is_branch)
print('\toperand_size:', instruction.operand_size) print('\toperand:', instruction.operand) print('\tsemantics:', instruction.semantics) print('\tpops:', instruction.pops) print('\tpushes:', instruction.pushes) print(f'\tbytes: 0x'+hexlify(instruction.bytes).decode()) print('\twrites to stack:', instruction.writes_to_stack) print('\treads from stack:', instruction.reads_from_stack) print('\twrites to memory:', instruction.writes_to_memory) print('\treads from memory:', instruction.reads_from_memory) print('\twrites to storage:', instruction.writes_to_storage) print('\treads from storage:', instruction.reads_from_storage) print('\tis terminator', instruction.is_terminator) instruction = ea.disassemble_one('\x60\x10') printi(instruction) instruction = ea.assemble_one('PUSH1 0x10') printi(instruction) for instruction in ea.disassemble_all('\x30\x31'): printi(instruction) for instruction in ea.assemble_all('ADDRESS\nBALANCE'): printi(instruction) #High level simple assembler/disassembler print(ea.assemble_hex( """PUSH1 0x60