def ecrecover(msg, signature, address=None): """ Returns None on failure, returns the recovered address on success. If address is provided: Returns True if the recovered address matches it, otherwise False. """ rawhash = sha3(msg) if len(signature) >= 65: v = safe_ord(signature[64]) + 27 r = big_endian_to_int(signature[0:32]) s = big_endian_to_int(signature[32:64]) else: if address: return False else: return None pk = PublicKey(flags=ALL_FLAGS) pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32), v - 27), raw=True) pub = pk.serialize(compressed=False) recaddr = data_encoder(sha3(pub[1:])[-20:]) if address: if not address.startswith("0x"): recaddr = recaddr[2:] return recaddr == address return recaddr
def sign(self, payload): rawhash = keccak256(payload) v, r, s = ecsign(rawhash, self.key) signature = \ zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + \ zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32) + \ bytearray_to_bytestr([v]) return signature
def personal_sign(private_key, message): if isinstance(private_key, str): private_key = data_decoder(private_key) rawhash = sha3("\x19Ethereum Signed Message:\n{}{}".format(len(message), message)) v, r, s = ecsign(rawhash, private_key) signature = zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + \ zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32) + \ bytearray_to_bytestr([v]) return data_encoder(signature)
def sign_payload(private_key, payload): if isinstance(private_key, str): private_key = data_decoder(private_key) rawhash = sha3(payload) v, r, s = ecsign(rawhash, private_key) signature = zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + \ zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32) + \ bytearray_to_bytestr([v]) return data_encoder(signature)
def parseCode(code): code = code[2:] if code[:2] == '0x' else code try: codes = [c for c in decode_hex(code)] except ValueError as e: print(code) raise Exception("Did you forget to link any libraries?") from e instructions = collections.OrderedDict() pc = 0 length = None while pc < len(codes): try: opcode = opcodes[codes[pc]] except KeyError: opcode = ['INVALID', 0, 0, 0] if opcode[0][:4] == 'PUSH': opcode = copy(opcode) length = codes[pc] - 0x5f pushData = codes[pc + 1:pc + length + 1] pushData = "0x" + encode_hex(bytearray_to_bytestr(pushData)) if type(pushData) is not str: pushData = pushData.decode() opcode.append(pushData) instructions[pc] = opcode if length is not None: pc += length length = None pc += 1 return instructions
def sender(self): if not self._sender: # Determine sender if self.v: if self.r >= N or self.s >= N or self.v < 27 or self.v > 28 \ or self.r == 0 or self.s == 0: raise InvalidTransaction("Invalid signature values!") log.debug('recovering sender') rlpdata = rlp.encode(self, UnsignedTransaction) rawhash = utils.sha3(rlpdata) pk = PublicKey(flags=ALL_FLAGS) try: pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.r)), 32) + zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.s)), 32), self.v - 27 ), raw=True ) pub = pk.serialize(compressed=False) except Exception: raise InvalidTransaction("Invalid signature values (x^3+7 is non-residue)") if pub[1:] == b"\x00" * 32: raise InvalidTransaction("Invalid signature (zero privkey cannot sign)") pub = encode_pubkey(pub, 'bin') self._sender = utils.sha3(pub[1:])[-20:] assert self.sender == self._sender else: self._sender = 0 return self._sender
def sender(self): if not self._sender: # Determine sender if self.v: if self.r >= N or self.s >= N or self.v < 27 or self.v > 28 \ or self.r == 0 or self.s == 0: raise InvalidTransaction("Invalid signature values!") log.debug('recovering sender') rlpdata = rlp.encode(self, UnsignedTransaction) rawhash = utils.sha3(rlpdata) pk = PublicKey(flags=ALL_FLAGS) try: pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.r)), 32) + zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.s)), 32), self.v - 27 ), raw=True ) pub = pk.serialize(compressed=False) except Exception: raise InvalidTransaction("Invalid signature values (x^3+7 is non-residue)") if pub[1:] == b"\x00" * 32: raise InvalidTransaction("Invalid signature (zero privkey cannot sign)") pub = encode_pubkey(pub, 'bin') self._sender = utils.sha3(pub[1:])[-20:] assert self.sender == self._sender else: self._sender = 0 return self._sender
def parseCode(code): code = remove_0x_head(code) codes = [c for c in decode_hex(code)] instructions = collections.OrderedDict() pc = 0 length = None while pc < len(codes): try: opcode = opcodes[codes[pc]] except KeyError: opcode = ['INVALID', 0, 0, 0] if opcode[0][:4] == 'PUSH': opcode = copy(opcode) length = codes[pc] - 0x5f pushData = codes[pc + 1:pc + length + 1] pushData = "0x" + encode_hex( bytearray_to_bytestr(pushData)).decode() opcode.append(pushData) instructions[pc] = opcode if length is not None: pc += length length = None pc += 1 return instructions
def test_ecrecover(): s = tester.state() c = s.abi_contract(ecrecover_code) priv = utils.sha3('some big long brainwallet password') pub = bitcoin.privtopub(priv) msghash = utils.sha3('the quick brown fox jumps over the lazy dog') pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msghash, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) V = utils.safe_ord(signature[64]) + 27 R = big_endian_to_int(signature[0:32]) S = big_endian_to_int(signature[32:64]) assert bitcoin.ecdsa_raw_verify(msghash, (V, R, S), pub) addr = utils.big_endian_to_int(utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) assert utils.big_endian_to_int(utils.privtoaddr(priv)) == addr result = c.test_ecrecover(utils.big_endian_to_int(msghash), V, R, S) assert result == addr
def sign(data: bytes, private_key_seed_ascii: str): priv = private_key_seed_ascii pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(data, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) return signature, eth_privtoaddr(priv)
def test_ecrecover(): s = tester.state() c = s.abi_contract(ecrecover_code) priv = utils.sha3('some big long brainwallet password') pub = bitcoin.privtopub(priv) msghash = utils.sha3('the quick brown fox jumps over the lazy dog') pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) V = utils.safe_ord(signature[64]) + 27 R = big_endian_to_int(signature[0:32]) S = big_endian_to_int(signature[32:64]) assert bitcoin.ecdsa_raw_verify(msghash, (V, R, S), pub) addr = utils.big_endian_to_int( utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) assert utils.big_endian_to_int(utils.privtoaddr(priv)) == addr result = c.test_ecrecover(utils.big_endian_to_int(msghash), V, R, S) assert result == addr
def sha3_(self, global_state): global keccak_function_manager state = global_state.mstate environment = global_state.environment op0, op1 = state.stack.pop(), state.stack.pop() try: index, length = util.get_concrete_int(op0), util.get_concrete_int(op1) # FIXME: broad exception catch except: # Can't access symbolic memory offsets if is_expr(op0): op0 = simplify(op0) state.stack.append(BitVec("KECCAC_mem[" + str(op0) + "]", 256)) return [global_state] try: state.mem_extend(index, length) data = b''.join([util.get_concrete_int(i).to_bytes(1, byteorder='big') for i in state.memory[index: index + length]]) except AttributeError: argument = str(state.memory[index]).replace(" ", "_") result = BitVec("KECCAC[{}]".format(argument), 256) keccak_function_manager.add_keccak(result, state.memory[index]) state.stack.append(result) return [global_state] keccak = utils.sha3(utils.bytearray_to_bytestr(data)) logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccak))) state.stack.append(BitVecVal(util.concrete_int_from_bytes(keccak, 0), 256)) return [global_state]
def apply_message(state, msg=None, **kwargs): if msg is None: msg = vm.Message(**kwargs) else: assert not kwargs ext = VMExt(state, transactions.Transaction(0, 0, 21000, b'', 0, b'')) result, gas_remained, data = apply_msg(ext, msg) return bytearray_to_bytestr(data) if result else None
def sign(data: str, private_key_seed_ascii: str): data = eth_message_hex(data) print("--eth_message_hex hash", data) priv = private_key_seed_ascii pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(data, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) return signature, eth_privtoaddr(priv)
def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256): """Sign data using Ethereum private key. :param private_key_seed_ascii: Private key seed as ASCII string """ msghash = hash_function(data) priv = utils.sha3(private_key_seed_ascii) pub = bitcoin.privtopub(priv) # Based on ethereum/tesrt_contracts.py test_ecrecover pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) # Enforce non-tightly-packed arguments for signing # (0x00 left pad) # https://github.com/ethereum/web3.py/issues/466 v = utils.safe_ord(signature[64]) + 27 r_bytes = signature[0:32] r_bytes = pad_left(r_bytes, 32, b"\0") r = big_endian_to_int(r_bytes) s_bytes = signature[32:64] s_bytes = pad_left(s_bytes, 32, b"\0") s = big_endian_to_int(s_bytes) # Make sure we use bytes data and zero padding stays # good across different systems r_hex = binascii.hexlify(r_bytes).decode("ascii") s_hex = binascii.hexlify(s_bytes).decode("ascii") # Convert to Etheruem address format addr = utils.big_endian_to_int( utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) # Return various bits about signing so it's easier to debug return { "signature": signature, "v": v, "r": r, "s": s, "r_bytes": r_bytes, "s_bytes": s_bytes, "r_hex": "0x" + r_hex, "s_hex": "0x" + s_hex, "address_bitcoin": addr, "address_ethereum": get_ethereum_address_from_private_key(priv), "public_key": pub, "hash": msghash, "payload": binascii.hexlify(bytes([v] + list(r_bytes) + list(s_bytes, ))) }
def sign_eth(rawhash, priv): pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r = utils.big_endian_to_int(signature[0:32]) s = utils.big_endian_to_int(signature[32:64]) return (v, r, s)
def sign_eth(rawhash, priv): pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r = utils.big_endian_to_int(signature[0:32]) s = utils.big_endian_to_int(signature[32:64]) return (v, r, s)
def run(self, sender=None, to=None, code=None, gas=None): sender = normalize_address(sender) if sender else normalize_address(zpad('sender', 20)) to = normalize_address(to) if to else normalize_address(zpad('receiver', 20)) code = scan_bin(code) if code else '' gas = scan_int(gas) if gas else 10000000000000 msg = vm.Message(sender, to, gas=gas) ext = VMExt(self.state, Transaction(0, 0, 21000, b'', 0, b'')) result, gas_remained, data = _apply_msg(ext, msg, code) return bytearray_to_bytestr(data) if result else None
def sign_payload(private_key, payload): if isinstance(private_key, str): private_key = data_decoder(private_key) rawhash = sha3(payload) pk = PrivateKey(private_key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True)) signature = signature[0] + bytearray_to_bytestr([signature[1]]) return data_encoder(signature)
def personal_sign(private_key, message): if isinstance(private_key, str): private_key = data_decoder(private_key) rawhash = sha3("\x19Ethereum Signed Message:\n{}{}".format( len(message), message)) pk = PrivateKey(private_key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True)) signature = signature[0] + bytearray_to_bytestr([signature[1] + 27]) return data_encoder(signature)
def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256): """Sign data using Ethereum private key. :param private_key_seed_ascii: Private key seed as ASCII string """ msghash = hash_function(data) priv = utils.sha3(private_key_seed_ascii) pub = bitcoin.privtopub(priv) # Based on ethereum/tesrt_contracts.py test_ecrecover pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r_bytes = signature[0:32] r = big_endian_to_int(r_bytes) s_bytes = signature[32:64] s = big_endian_to_int(s_bytes) #: XXX Ethereum wants its address in different format addr = utils.big_endian_to_int( utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) return { "signature": signature, "v": v, "r": r, "s": s, "r_bytes": r_bytes, "s_bytes": s_bytes, "address_bitcoin": addr, "address_ethereum": eth_privtoaddr(priv), "public_key": pub, "hash": msghash, "payload": bytes([v] + list(r_bytes) + list(s_bytes, )) }
def eth_sign_with_keyfile(message: bytes, raw: bool, keyfile: str, password: str): assert(isinstance(message, bytes)) assert(isinstance(raw, bool)) assert(isinstance(keyfile, str)) assert(isinstance(password, str)) if not raw: message = hexstring_to_bytes(Eth._recoveryMessageHash(data=message)) key = eth_keyfile.decode_keyfile_json(eth_keyfile.load_keyfile(keyfile), bytes(password, 'utf-8')) pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(message, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) signature_hex = signature.hex()[0:128] + int_to_bytes(ord(bytes.fromhex(signature.hex()[128:130]))+27).hex() return '0x' + signature_hex
def may_write_to(self, z3_index, z3_value, storage, constraint_list, consider_length): if consider_length: may_write_to, added_constraints = ConcretSlot.may_write_to(self, z3_index, z3_value, storage, constraint_list, consider_length) if may_write_to: return may_write_to, added_constraints slot_hash = utils.sha3(utils.bytearray_to_bytestr(util.concrete_int_to_bytes(self.slot_counter))) slot_hash = str(BitVecVal(util.concrete_int_from_bytes(slot_hash, 0), 256)) z3_index_str = str(z3_index).replace("\n", "") if z3_index_str.startswith(slot_hash): if z3_index_str.endswith(slot_hash) or z3_index_str[len(slot_hash):].startswith(" +"): return True, [] if z3_index_str in keccak_map: unresolved_index = keccak_map[str(z3_index).replace("\n" , "")] unresolved_index = str(unresolved_index).replace("\n", "") if unresolved_index.startswith(str(self.slot_counter)): if unresolved_index.endswith(str(self.slot_counter)) or unresolved_index[len(str(self.slot_counter)):].startswith(" +"): return True, [] return False, []
def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256): """Sign data using Ethereum private key. :param private_key_seed_ascii: Private key seed as ASCII string """ msghash = hash_function(data) priv = utils.sha3(private_key_seed_ascii) pub = bitcoin.privtopub(priv) # Based on ethereum/tesrt_contracts.py test_ecrecover pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r_bytes = signature[0:32] r = big_endian_to_int(r_bytes) s_bytes = signature[32:64] s = big_endian_to_int(s_bytes) #: XXX Ethereum wants its address in different format addr = utils.big_endian_to_int(utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) return { "signature": signature, "v": v, "r": r, "s": s, "r_bytes": r_bytes, "s_bytes": s_bytes, "address_bitcoin": addr, "address_ethereum": eth_privtoaddr(priv), "public_key": pub, "hash": msghash, "payload": bytes([v] + list(r_bytes)+ list(s_bytes,)) }
def eth_sign(message: bytes, web3: Web3): assert(isinstance(message, bytes)) assert(isinstance(web3, Web3)) # as `EthereumTesterProvider` does not support `eth_sign`, we implement it ourselves if str(web3.providers[0]) == 'EthereumTesterProvider': key = k0 msg = hexstring_to_bytes(Eth._recoveryMessageHash(data=message)) pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msg, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) signature_hex = signature.hex()[0:128] + int_to_bytes(ord(bytes.fromhex(signature.hex()[128:130]))+27).hex() return '0x' + signature_hex return web3.manager.request_blocking( "eth_sign", [web3.eth.defaultAccount, encode_hex(message)], )
def sha3_(self, global_state): state = global_state.mstate environment = global_state.environment op0, op1 = state.stack.pop(), state.stack.pop() try: index, length = util.get_concrete_int(op0), util.get_concrete_int( op1) # FIXME: broad exception catch except: # Can't access symbolic memory offsets if is_expr(op0): op0 = simplify(op0) state.stack.append(BitVec("KECCAC_mem[" + str(op0) + "]", 256)) return [global_state] try: data = b'' for i in range(index, index + length): data += util.get_concrete_int(state.memory[i]).to_bytes( 1, byteorder='big') i += 1 # FIXME: broad exception catch except: svar = str(state.memory[index]) svar = svar.replace(" ", "_") state.stack.append(BitVec("keccac_" + svar, 256)) return [global_state] keccac = utils.sha3(utils.bytearray_to_bytestr(data)) logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccac))) state.stack.append( BitVecVal(util.concrete_int_from_bytes(keccac, 0), 256)) return [global_state]
def sign(self, key): """Sign this transaction with a private key. A potentially already existing signature would be overridden. """ if key in (0, '', b'\x00' * 32, '0' * 64): raise InvalidTransaction("Zero privkey cannot sign") rawhash = utils.sha3(rlp.encode(self, UnsignedTransaction)) if len(key) == 64: # we need a binary key key = encode_privkey(key, 'bin') pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) self.v = utils.safe_ord(signature[64]) + 27 self.r = big_endian_to_int(signature[0:32]) self.s = big_endian_to_int(signature[32:64]) self.sender = utils.privtoaddr(key) return self
def sign(self, key): """Sign this transaction with a private key. A potentially already existing signature would be overridden. """ if key in (0, '', b'\x00' * 32, '0' * 64): raise InvalidTransaction("Zero privkey cannot sign") rawhash = utils.sha3(rlp.encode(self, UnsignedTransaction)) if len(key) == 64: # we need a binary key key = encode_privkey(key, 'bin') pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) self.v = utils.safe_ord(signature[64]) + 27 self.r = big_endian_to_int(signature[0:32]) self.s = big_endian_to_int(signature[32:64]) self.sender = utils.privtoaddr(key) return self
def _sym_exec(self, context, state, depth=0, constraints=[]): disassembly = context.module['disassembly'] depth = depth start_addr = disassembly.instruction_list[state.pc]['address'] if start_addr == 0: self.execution_state['current_func'] = "fallback" self.execution_state['current_func_addr'] = start_addr node = Node(context.module['name'], start_addr, constraints) logging.debug("DEBUG Node(type=" + str(type(node)) + "): " + str(node)) logging.debug("- Entering block " + str(node.uid) + ", index = " + str(state.pc) + ", address = " + str(start_addr) + ", depth = " + str(depth)) if start_addr in disassembly.addr_to_func: # Enter a new function function_name = disassembly.addr_to_func[start_addr] self.execution_state['current_func'] = function_name logging.info("- Entering function " + context.module['name'] + ":" + function_name) node.instruction_list.append({ 'opcode': function_name, 'address': disassembly.instruction_list[state.pc]['address'] }) state.pc += 1 node.function_name = self.execution_state['current_func'] halt = False instr = disassembly.instruction_list[state.pc] while not halt: try: instr = disassembly.instruction_list[state.pc] except IndexError: logging.debug("Invalid PC") return node # Save instruction and state node.instruction_list.append(instr) node.states[instr['address']] = state state = copy.deepcopy(state) self.total_states += 1 state.pc += 1 op = instr['opcode'] logging.debug("[" + context.module['name'] + "] " + helper.get_trace_line(instr, state)) # slows down execution significantly. # stack ops if op.startswith("PUSH"): value = BitVecVal(int(instr['argument'][2:], 16), 256) state.stack.append(value) elif op.startswith('DUP'): dpth = int(op[3:]) try: state.stack.append(state.stack[-dpth]) except: halt = True continue elif op.startswith('SWAP'): dpth = int(op[4:]) try: temp = state.stack[-dpth - 1] except IndexError: # Stack underflow halt = True continue state.stack[-dpth - 1] = state.stack[-1] state.stack[-1] = temp elif op == 'POP': try: state.stack.pop() except IndexError: # Stack underflow halt = True continue # Bitwise ops elif op == 'AND': try: state.stack.append(state.stack.pop() & state.stack.pop()) except IndexError: # Stack underflow halt = True continue elif op == 'OR': try: op1, op2 = state.stack.pop(), state.stack.pop() except IndexError: # Stack underflow halt = True continue if (type(op1) == BoolRef): op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256)) if (type(op2) == BoolRef): op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256)) state.stack.append(op1 | op2) elif op == 'XOR': state.stack.append(state.stack.pop() ^ state.stack.pop()) elif op == 'NOT': state.stack.append(TT256M1 - state.stack.pop()) elif op == 'BYTE': s0, s1 = state.stack.pop(), state.stack.pop() state.stack.append(BitVecVal(0, 256)) # Arithmetics elif op == 'ADD': state.stack.append( (helper.pop_bitvec(state) + helper.pop_bitvec(state))) elif op == 'SUB': state.stack.append( (helper.pop_bitvec(state) - helper.pop_bitvec(state))) elif op == 'MUL': state.stack.append( helper.pop_bitvec(state) * helper.pop_bitvec(state)) elif op == 'DIV': s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state) state.stack.append(UDiv(s0, s1)) elif op == 'MOD': s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state) state.stack.append(0 if s1 == 0 else s0 % s1) elif op == 'SDIV': s0, s1 = helper.to_signed( helper.pop_bitvec(state)), helper.to_signed( helper.pop_bitvec(state)) state.stack.append(0 if s1 == 0 else ( abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == 'SMOD': s0, s1 = helper.to_signed( helper.pop_bitvec(state)), helper.to_signed( helper.pop_bitvec(state)) state.stack.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == 'ADDMOD': s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec( state), helper.pop_bitvec(state) state.stack.append((s0 + s1) % s2 if s2 else 0) elif op == 'MULMOD': s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec( state), helper.pop_bitvec(state) state.stack.append((s0 * s1) % s2 if s2 else 0) elif op == 'EXP': # we only implement 2 ** x base, exponent = state.stack.pop(), state.stack.pop() if (type(base) != BitVecNumRef): state.stack.append( BitVec(str(base) + "_EXP_" + str(exponent), 256)) elif (base.as_long() == 2): if exponent == 0: state.stack.append(BitVecVal(1, 256)) else: state.stack.append(base << (exponent - 1)) else: state.stack.append(base) elif op == 'SIGNEXTEND': s0, s1 = state.stack.pop(), state.stack.pop() try: s0 = get_concrete_int(s0) s1 = get_concrete_int(s1) except: halt = True continue if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): state.stack.append(s1 | (TT256 - (1 << testbit))) else: state.stack.append(s1 & ((1 << testbit) - 1)) else: state.stack.append(s1) # Comparisons elif op == 'LT': exp = ULT(helper.pop_bitvec(state), helper.pop_bitvec(state)) state.stack.append(exp) elif op == 'GT': exp = UGT(helper.pop_bitvec(state), helper.pop_bitvec(state)) state.stack.append(exp) elif op == 'SLT': exp = helper.pop_bitvec(state) < helper.pop_bitvec(state) state.stack.append(exp) elif op == 'SGT': exp = helper.pop_bitvec(state) > helper.pop_bitvec(state) state.stack.append(exp) elif op == 'EQ': op1 = state.stack.pop() op2 = state.stack.pop() if (type(op1) == BoolRef): op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256)) if (type(op2) == BoolRef): op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256)) exp = op1 == op2 state.stack.append(exp) elif op == 'ISZERO': val = state.stack.pop() if (type(val) == BoolRef): exp = val == False else: exp = val == 0 state.stack.append(exp) # Call data elif op == 'CALLVALUE': state.stack.append(context.callvalue) elif op == 'CALLDATALOAD': # unpack 32 bytes from calldata into a word and put it on the stack op0 = state.stack.pop() try: offset = helper.get_concrete_int(simplify(op0)) except AttributeError: logging.debug("CALLDATALOAD: Unsupported symbolic index") state.stack.append( BitVec( "calldata_" + str(context.module['name']) + "_" + str(op0), 256)) continue try: b = context.calldata[offset] except IndexError: logging.debug( "Calldata not set, using symbolic variable instead") state.stack.append( BitVec( "calldata_" + str(context.module['name']) + "_" + str(op0), 256)) continue if type(b) == int: # 32 byte concrete value val = b'' try: for i in range(offset, offset + 32): val += context.calldata[i].to_bytes( 1, byteorder='big') state.stack.append( BitVecVal(int.from_bytes(val, byteorder='big'), 256)) except: state.stack.append(b) else: # symbolic variable state.stack.append(b) elif op == 'CALLDATASIZE': if context.calldata_type == CalldataType.SYMBOLIC: state.stack.append( BitVec("calldatasize_" + context.module['name'], 256)) else: state.stack.append(BitVecVal(len(context.calldata), 256)) elif op == 'CALLDATACOPY': op0, op1, op2 = state.stack.pop(), state.stack.pop( ), state.stack.pop() try: mstart = helper.get_concrete_int(op0) except: logging.debug( "Unsupported symbolic memory offset in CALLDATACOPY") continue try: dstart = helper.get_concrete_int(op1) except: logging.debug( "Unsupported symbolic calldata offset in CALLDATACOPY") state.mem_extend(mstart, 1) state.memory[mstart] = BitVec( "calldata_" + str(context.module['name']) + "_cpy", 256) continue try: size = helper.get_concrete_int(op2) except: logging.debug("Unsupported symbolic size in CALLDATACOPY") state.mem_extend(mstart, 1) state.memory[mstart] = BitVec( "calldata_" + str(context.module['name']) + "_" + str(dstart), 256) continue if size > 0: try: state.mem_extend(mstart, size) except: logging.debug("Memory allocation error: mstart = " + str(mstart) + ", size = " + str(size)) state.mem_extend(mstart, 1) state.memory[mstart] = BitVec( "calldata_" + str(context.module['name']) + "_" + str(dstart), 256) continue try: i_data = context.calldata[dstart] for i in range(mstart, mstart + size): state.memory[i] = context.calldata[i_data] i_data += 1 except: logging.debug("Exception copying calldata to memory") state.memory[mstart] = BitVec( "calldata_" + str(context.module['name']) + "_" + str(dstart), 256) # continue # Control flow elif op == 'STOP': if self.last_call_address is not None: self.pending_returns[self.last_call_address].append( node.uid) halt = True continue # Environment elif op == 'ADDRESS': state.stack.append(context.address) elif op == 'BALANCE': addr = state.stack.pop() state.stack.append(BitVec("balance_at_" + str(addr), 256)) elif op == 'ORIGIN': state.stack.append(context.origin) elif op == 'CALLER': state.stack.append(context.caller) elif op == 'CODESIZE': state.stack.append(len(disassembly.instruction_list)) if op == 'SHA3': op0, op1 = state.stack.pop(), state.stack.pop() try: index, length = helper.get_concrete_int( op0), helper.get_concrete_int(op1) except: # Can't access symbolic memory offsets state.stack.append( BitVec("KECCAC_mem_" + str(op0) + ")", 256)) continue try: data = b'' for i in range(index, index + length): data += helper.get_concrete_int( state.memory[i]).to_bytes(1, byteorder='big') i += 1 except: svar = str(state.memory[index]) svar = svar.replace(" ", "_") state.stack.append(BitVec("keccac_" + svar, 256)) continue logging.debug("SHA3 Data: " + str(data)) keccac = utils.sha3(utils.bytearray_to_bytestr(data)) logging.debug("SHA3 Hash: " + str(binascii.hexlify(keccac))) state.stack.append( BitVecVal(helper.concrete_int_from_bytes(keccac, 0), 256)) elif op == 'GASPRICE': state.stack.append(BitVecVal(1, 256)) elif op == 'CODECOPY': # Not implemented start, s1, size = state.stack.pop(), state.stack.pop( ), state.stack.pop() elif op == 'EXTCODESIZE': addr = state.stack.pop() state.stack.append(BitVec("extcodesize", 256)) elif op == 'EXTCODECOPY': # Not implemented addr = state.stack.pop() start, s2, size = state.stack.pop(), state.stack.pop( ), state.stack.pop() elif op == 'BLOCKHASH': blocknumber = state.stack.pop() state.stack.append( BitVec("blockhash_block_" + str(blocknumber), 256)) elif op == 'COINBASE': state.stack.append(BitVec("coinbase", 256)) elif op == 'TIMESTAMP': state.stack.append(BitVec("timestamp", 256)) elif op == 'NUMBER': state.stack.append(BitVec("block_number", 256)) elif op == 'DIFFICULTY': state.stack.append(BitVec("block_difficulty", 256)) elif op == 'GASLIMIT': state.stack.append(BitVec("block_gaslimit", 256)) elif op == 'MLOAD': op0 = state.stack.pop() logging.debug("MLOAD[" + str(op0) + "]") try: offset = helper.get_concrete_int(op0) except AttributeError: logging.debug("Can't MLOAD from symbolic index") data = BitVec("mem_" + str(op0), 256) continue try: data = helper.concrete_int_from_bytes(state.memory, offset) except IndexError: # Memory slot not allocated data = BitVec("mem_" + str(offset), 256) except TypeError: # Symbolic memory data = state.memory[offset] logging.debug("Load from memory[" + str(offset) + "]: " + str(data)) state.stack.append(data) elif op == 'MSTORE': op0, value = state.stack.pop(), state.stack.pop() try: mstart = helper.get_concrete_int(op0) except AttributeError: logging.debug("MSTORE to symbolic index. Not supported") continue try: state.mem_extend(mstart, 32) except Exception: logging.debug("Error extending memory, mstart = " + str(mstart) + ", size = 32") logging.debug("MSTORE to mem[" + str(mstart) + "]: " + str(value)) try: # Attempt to concretize value _bytes = helper.concrete_int_to_bytes(value) i = 0 for b in _bytes: state.memory[mstart + i] = _bytes[i] i += 1 except: try: state.memory[mstart] = value except: logging.debug("Invalid memory access") continue # logging.debug("MEM: " + str(state.memory)) elif op == 'MSTORE8': # Is this ever used? op0, value = state.stack.pop(), state.stack.pop() try: offset = helper.get_concrete_int(op0) except AttributeError: logging.debug("MSTORE to symbolic index. Not supported") continue state.mem_extend(offset, 1) state.memory[offset] = value % 256 elif op == 'SLOAD': index = state.stack.pop() logging.debug("Storage access at index " + str(index)) if type(index) == BitVecRef: # SLOAD from hash offset # k = sha3.keccak_512() # k.update(bytes(str(index), 'utf-8')) # index = k.hexdigest()[:8] index = str(index) try: data = state.storage[index] except KeyError: data = BitVec("storage_" + str(index), 256) state.storage[index] = data state.stack.append(data) elif op == 'SSTORE': index, value = state.stack.pop(), state.stack.pop() logging.debug("Write to storage[" + str(index) + "] at node " + str(start_addr)) if type(index) == BitVecRef: index = str(index) try: state.storage[index] = value except KeyError: logging.debug("Error writing to storage: Invalid index") continue elif op == 'JUMP': try: jump_addr = helper.get_concrete_int(state.stack.pop()) except AttributeError: logging.debug("Invalid jump argument (symbolic address)") halt = True continue except IndexError: # Stack Underflow halt = True continue if (depth < self.max_depth): i = helper.get_instruction_index( disassembly.instruction_list, jump_addr) if i is None: logging.debug("JUMP to invalid address") halt = True continue opcode = disassembly.instruction_list[i]['opcode'] if opcode == "JUMPDEST": if (self.can_jump(jump_addr)): new_state = copy.deepcopy(state) new_state.pc = i new_node = self._sym_exec(context, new_state, depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node self.edges.append( Edge(node.uid, new_node.uid, JumpType.UNCONDITIONAL)) halt = True continue else: logging.debug("JUMP target limit reached") halt = True continue else: logging.debug( "Skipping JUMP to invalid destination (not JUMPDEST): " + str(jump_addr)) halt = True continue else: logging.debug("Max depth reached, skipping JUMP") halt = True continue elif op == 'JUMPI': op0, condition = state.stack.pop(), state.stack.pop() try: jump_addr = helper.get_concrete_int(op0) except: logging.debug("Skipping JUMPI to invalid destination.") if (depth < self.max_depth): i = helper.get_instruction_index( disassembly.instruction_list, jump_addr) if not i: logging.debug("Invalid jump destination: " + str(jump_addr)) else: instr = disassembly.instruction_list[i] # Add new node for condition == True if instr['opcode'] != "JUMPDEST": logging.debug("Invalid jump destination: " + str(jump_addr)) else: if (type(condition) == bool): logging.debug("BOOL CONDITION TYPE") # continue elif (type(condition) == BoolRef): if (self.can_jump(jump_addr)): new_state = copy.deepcopy(state) new_state.pc = i new_constraints = copy.deepcopy( constraints) new_constraints.append(condition) new_node = self._sym_exec( context, new_state, depth=depth + 1, constraints=new_constraints) self.nodes[new_node.uid] = new_node self.edges.append( Edge(node.uid, new_node.uid, JumpType.CONDITIONAL, condition)) else: logging.debug( "JUMP target limit reached (JUMPI)") else: logging.debug("Invalid condition: " + str(condition) + "(type " + str(type(condition)) + ")") halt = True continue new_state = copy.deepcopy(state) if (type(condition) == BoolRef): negated = Not(condition) else: negated = condition == 0 new_constraints = copy.deepcopy(constraints) new_constraints.append(negated) new_node = self._sym_exec(context, new_state, depth=depth, constraints=new_constraints) self.nodes[new_node.uid] = new_node self.edges.append( Edge(node.uid, new_node.uid, JumpType.CONDITIONAL, negated)) halt = True continue else: logging.debug("Max depth reached, skipping JUMPI") elif op == 'PC': state.stack.append(state.pc - 1) elif op == 'MSIZE': state.stack.append(BitVec("msize", 256)) elif op == 'GAS': state.stack.append(10000000) elif op.startswith('LOG'): dpth = int(op[3:]) state.stack.pop(), state.stack.pop() [state.stack.pop() for x in range(dpth)] # Not supported elif op == 'CREATE': state.stack.pop(), state.stack.pop(), state.stack.pop() # Not supported state.stack.append(0) elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'): if op in ('CALL', 'CALLCODE'): gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop() else: gas, to, meminstart, meminsz, memoutstart, memoutsz = \ state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop() try: callee_address = hex(helper.get_concrete_int(to)) except AttributeError: # Not a concrete call address. Call target may be an address in storage. m = re.search(r'storage_(\d+)', str(simplify(to))) logging.debug("CALL to: " + str(simplify(to))) if (m and self.dynamic_loader is not None): idx = int(m.group(1)) logging.info( "Dynamic contract address at storage index " + str(idx)) # attempt to read the contract address from instance storage callee_address = self.dynamic_loader.read_storage( context.module['address'], idx) # testrpc simply returns the address, geth response is more elaborate. if not re.match(r"^0x[0-9a-f]{40}$", callee_address): callee_address = "0x" + callee_address[26:] else: logging.info("Unable to resolve address from storage.") ret = BitVec( "retval_" + str(disassembly.instruction_list[ state.pc]['address']), 256) state.stack.append(ret) continue if (int(callee_address, 16) < 5): logging.info("Native contract called: " + callee_address) # Todo: Implement native contracts ret = BitVec( "retval_" + str(disassembly.instruction_list[state.pc]['address']), 256) state.stack.append(ret) continue if not re.match(r"^0x[0-9a-f]{40}", callee_address): logging.debug("Invalid address: " + str(callee_address)) ret = BitVec( "retval_" + str(disassembly.instruction_list[state.pc]['address']), 256) state.stack.append(ret) continue try: module = self.modules[callee_address] except KeyError: # We have a valid call address, but contract is not in the modules list logging.info("Module with address " + callee_address + " not loaded.") if self.dynamic_loader is not None: logging.info("Attempting to load dependency") module = self.dynamic_loader.dynld( context.module['address'], callee_address) if module is None: logging.info( "No code returned, not a contract account?") ret = BitVec( "retval_" + str(disassembly.instruction_list[ state.pc]['address']), 256) state.stack.append(ret) continue # New contract loaded successfully, add it to the modules list self.modules[callee_address] = module self.addr_visited[callee_address] = [] logging.info("Dependency loaded: " + module['address']) else: logging.info( "Dynamic loader unavailable. Skipping call") ret = BitVec( "retval_" + str(disassembly.instruction_list[ state.pc]['address']), 256) state.stack.append(ret) continue logging.info("Executing " + op + " to: " + callee_address) try: callee_module = self.modules[callee_address] except KeyError: logging.info("Contract " + str(callee_address) + " not loaded.") logging.info((str(self.modules))) ret = BitVec( "retval_" + str(disassembly.instruction_list[state.pc]['address']), 256) state.stack.append(ret) continue # Attempt to write concrete calldata try: calldata = state.memory[helper.get_concrete_int( meminstart):helper.get_concrete_int(meminstart + meminsz)] calldata_type = CalldataType.CONCRETE logging.debug("calldata: " + str(calldata)) except AttributeError: logging.info("Unsupported symbolic calldata offset") calldata_type = CalldataType.SYMBOLIC calldata = [] self.last_call_address = disassembly.instruction_list[ state.pc]['address'] self.pending_returns[self.last_call_address] = [] callee_context = Context(callee_module, calldata=calldata, caller=context.address, origin=context.origin, calldata_type=calldata_type) if (op == 'CALL'): new_node = self._sym_exec(callee_context, State(), depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node elif (op == 'CALLCODE'): temp_module = context.module temp_callvalue = context.callvalue temp_caller = context.caller temp_calldata = context.calldata context.module = callee_module context.callvalue = value context.caller = context.address context.calldata = calldata new_node = self._sym_exec(context, State(), depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node context.module = temp_module context.callvalue = temp_callvalue context.caller = temp_caller context.calldata = temp_calldata elif (op == 'DELEGATECALL'): temp_module = context.module temp_calldata = context.calldata context.module = callee_module context.calldata = calldata new_node = self._sym_exec(context, State(), depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node context.module = temp_module context.calldata = temp_calldata self.edges.append(Edge(node.uid, new_node.uid, JumpType.CALL)) ret = BitVec( "retval_" + str(disassembly.instruction_list[state.pc]['address']), 256) state.stack.append(ret) new_state = copy.deepcopy(state) new_node = self._sym_exec(context, new_state, depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node for ret_uid in self.pending_returns[self.last_call_address]: self.edges.append( Edge(ret_uid, new_node.uid, JumpType.RETURN)) state.stack.append(BitVec("retval", 256)) continue elif op == 'RETURN': offset, length = state.stack.pop(), state.stack.pop() try: self.last_returned = state.memory[helper.get_concrete_int( offset):helper.get_concrete_int(offset + length)] except AttributeError: logging.debug( "Return with symbolic length or offset. Not supported") if self.last_call_address is not None: self.pending_returns[self.last_call_address].append( node.uid) halt = True continue elif op == 'SUICIDE': halt = True continue elif op == 'REVERT': if self.last_call_address is not None: self.pending_returns[self.last_call_address].append( node.uid) halt = True continue elif op == 'INVALID': halt = True continue logging.debug("Returning from node " + str(node.uid)) return node
def apply_transaction(state, tx): state.logs = [] state.suicides = [] state.refunds = 0 validate_transaction(state, tx) intrinsic_gas = tx.intrinsic_gas_used if state.is_HOMESTEAD(): assert tx.s * 2 < transactions.secpk1n if not tx.to or tx.to == CREATE_CONTRACT_ADDRESS: intrinsic_gas += opcodes.CREATE[3] if tx.startgas < intrinsic_gas: raise InsufficientStartGas( rp(tx, 'startgas', tx.startgas, intrinsic_gas)) log_tx.debug('TX NEW', txdict=tx.to_dict()) # start transacting ################# if tx.sender != null_address: state.increment_nonce(tx.sender) # buy startgas assert state.get_balance(tx.sender) >= tx.startgas * tx.gasprice state.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message( tx.sender, tx.to, tx.value, tx.startgas - intrinsic_gas, message_data, code_address=tx.to) # MESSAGE ext = VMExt(state, tx) if tx.to != b'': result, gas_remained, data = apply_msg(ext, message) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=data) gas_used = tx.startgas - gas_remained # Transaction failed if not result: log_tx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) state.delta_balance(tx.sender, tx.gasprice * gas_remained) state.delta_balance(state.block_coinbase, tx.gasprice * gas_used) output = b'' success = 0 # Transaction success else: log_tx.debug('TX SUCCESS', data=data) state.refunds += len(set(state.suicides)) * opcodes.GSUICIDEREFUND if state.refunds > 0: log_tx.debug( 'Refunding', gas_refunded=min( state.refunds, gas_used // 2)) gas_remained += min(state.refunds, gas_used // 2) gas_used -= min(state.refunds, gas_used // 2) state.refunds = 0 # sell remaining gas state.delta_balance(tx.sender, tx.gasprice * gas_remained) state.delta_balance(state.block_coinbase, tx.gasprice * gas_used) if tx.to: output = bytearray_to_bytestr(data) else: output = data success = 1 state.gas_used += gas_used # Clear suicides suicides = state.suicides state.suicides = [] for s in suicides: state.set_balance(s, 0) state.del_account(s) # Pre-Metropolis: commit state after every tx if not state.is_METROPOLIS() and not SKIP_MEDSTATES: state.commit() # Construct a receipt r = mk_receipt(state, success, state.logs) _logs = list(state.logs) state.logs = [] state.add_receipt(r) state.set_param('bloom', state.bloom | r.bloom) state.set_param('txindex', state.txindex + 1) return success, output
def vm_execute(ext, msg, code): # precompute trace flag # if we trace vm, we're in slow mode anyway trace_vm = log_vm_op.is_active('trace') compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory if code in code_cache: processed_code = code_cache[code] else: processed_code = preprocess_code(code) code_cache[code] = processed_code # print(processed_code.keys(), code) codelen = len(code) s = time.time() steps = 0 _prevop = None # for trace only while compustate.pc in processed_code: ops, minstack, maxstack, totgas, nextpos = processed_code[ compustate.pc] if len(compustate.stack) < minstack: return vm_exception('INSUFFICIENT STACK') if len(compustate.stack) > maxstack: return vm_exception('STACK SIZE LIMIT EXCEEDED') if totgas > compustate.gas: return vm_exception('OUT OF GAS %d %d' % (totgas, compustate.gas)) jumped = False compustate.gas -= totgas compustate.pc = nextpos # Invalid operation; can only come at the end of a chunk if ops[-1][0] == 'INVALID': return vm_exception('INVALID OP', opcode=ops[-1][1]) for op, opcode, pushval in ops: if trace_vm: """ This diverges from normal logging, as we use the logging namespace only to decide which features get logged in 'eth.vm.op' i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack' """ trace_data = {} trace_data['stack'] = list( map(to_string, list(compustate.stack))) if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL', 'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY', 'EXTCODECOPY'): if len(compustate.memory) < 1024: trace_data['memory'] = \ ''.join([encode_hex(ascii_chr(x)) for x in compustate.memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(b''.join([ascii_chr(x) for x in compustate.memory]))) if _prevop in ('SSTORE', ) or steps == 0: trace_data['storage'] = ext.log_storage(msg.to) trace_data['gas'] = to_string(compustate.gas + totgas) trace_data['inst'] = opcode trace_data['pc'] = to_string(compustate.pc - 1) if steps == 0: trace_data['depth'] = msg.depth trace_data['address'] = msg.to trace_data['steps'] = steps trace_data['depth'] = msg.depth if op[:4] == 'PUSH': trace_data['pushvalue'] = pushval log_vm_op.trace('vm', op=op, **trace_data) steps += 1 _prevop = op # Valid operations # Pushes first because they are very frequent if 0x60 <= opcode <= 0x7f: # compustate.pc += opcode - 0x5f # Move 1 byte forward for # 0x60, up to 32 bytes for 0x7f stk.append(pushval) elif opcode < 0x10: if op == 'STOP': return peaceful_exit('STOP', compustate.gas, []) elif op == 'ADD': stk.append((stk.pop() + stk.pop()) & TT256M1) elif op == 'SUB': stk.append((stk.pop() - stk.pop()) & TT256M1) elif op == 'MUL': stk.append((stk.pop() * stk.pop()) & TT256M1) elif op == 'DIV': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 // s1) elif op == 'MOD': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) elif op == 'SDIV': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == 'SMOD': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == 'ADDMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) elif op == 'MULMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) elif op == 'EXP': base, exponent = stk.pop(), stk.pop() # fee for exponent is dependent on its bytes # calc n bytes to represent exponent nbytes = len(utils.encode_int(exponent)) expfee = nbytes * opcodes.GEXPONENTBYTE if ext.post_clearing_hardfork(): expfee += opcodes.EXP_SUPPLEMENTAL_GAS * nbytes if compustate.gas < expfee: compustate.gas = 0 return vm_exception('OOG EXPONENT') compustate.gas -= expfee stk.append(pow(base, exponent, TT256)) elif op == 'SIGNEXTEND': s0, s1 = stk.pop(), stk.pop() if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): stk.append(s1 | (TT256 - (1 << testbit))) else: stk.append(s1 & ((1 << testbit) - 1)) else: stk.append(s1) elif opcode < 0x20: if op == 'LT': stk.append(1 if stk.pop() < stk.pop() else 0) elif op == 'GT': stk.append(1 if stk.pop() > stk.pop() else 0) elif op == 'SLT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == 'SGT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(1 if s0 > s1 else 0) elif op == 'EQ': stk.append(1 if stk.pop() == stk.pop() else 0) elif op == 'ISZERO': stk.append(0 if stk.pop() else 1) elif op == 'AND': stk.append(stk.pop() & stk.pop()) elif op == 'OR': stk.append(stk.pop() | stk.pop()) elif op == 'XOR': stk.append(stk.pop() ^ stk.pop()) elif op == 'NOT': stk.append(TT256M1 - stk.pop()) elif op == 'BYTE': s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 // 256**(31 - s0)) % 256) elif opcode < 0x40: if op == 'SHA3': s0, s1 = stk.pop(), stk.pop() compustate.gas -= opcodes.GSHA3WORD * \ (utils.ceil32(s1) // 32) if compustate.gas < 0: return vm_exception('OOG PAYING FOR SHA3') if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') data = bytearray_to_bytestr(mem[s0:s0 + s1]) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.BALANCE_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(ext.get_balance(addr)) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(ext.tx_origin)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': stk.append(msg.data.extract32(stk.pop())) elif op == 'CALLDATASIZE': stk.append(msg.data.size) elif op == 'CALLDATACOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') msg.data.extract_copy(mem, mstart, dstart, size) elif op == 'CODESIZE': stk.append(len(code)) elif op == 'CODECOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if dstart + i < len(code): mem[mstart + i] = utils.safe_ord(code[dstart + i]) else: mem[mstart + i] = 0 elif op == 'RETURNDATACOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') if dstart + size > len(compustate.last_returned): return vm_exception('RETURNDATACOPY out of range') mem[mstart:mstart + size] = compustate.last_returned elif op == 'RETURNDATASIZE': stk.append(len(compustate.last_returned)) elif op == 'GASPRICE': stk.append(ext.tx_gasprice) elif op == 'EXTCODESIZE': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(len(ext.get_code(addr) or b'')) elif op == 'EXTCODECOPY': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) start, s2, size = stk.pop(), stk.pop(), stk.pop() extcode = ext.get_code(addr) or b'' assert utils.is_string(extcode) if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s2 + i < len(extcode): mem[start + i] = utils.safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 elif opcode < 0x50: if op == 'BLOCKHASH': if ext.post_metropolis_hardfork() and False: bh_addr = ext.blockhash_store stk.append(ext.get_storage_data(bh_addr, stk.pop())) else: stk.append( utils.big_endian_to_int(ext.block_hash(stk.pop()))) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(ext.block_coinbase)) elif op == 'TIMESTAMP': stk.append(ext.block_timestamp) elif op == 'NUMBER': stk.append(ext.block_number) elif op == 'DIFFICULTY': stk.append(ext.block_difficulty) elif op == 'GASLIMIT': stk.append(ext.block_gas_limit) elif opcode < 0x60: if op == 'POP': stk.pop() elif op == 'MLOAD': s0 = stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') stk.append(utils.bytes_to_int(mem[s0:s0 + 32])) elif op == 'MSTORE': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') mem[s0:s0 + 32] = utils.encode_int32(s1) elif op == 'MSTORE8': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 1): return vm_exception('OOG EXTENDING MEMORY') mem[s0] = s1 % 256 elif op == 'SLOAD': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.SLOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") stk.append(ext.get_storage_data(msg.to, stk.pop())) elif op == 'SSTORE': s0, s1 = stk.pop(), stk.pop() if msg.static: return vm_exception( 'Cannot SSTORE inside a static context') if ext.get_storage_data(msg.to, s0): gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL refund = 0 if s1 else opcodes.GSTORAGEREFUND else: gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD refund = 0 if compustate.gas < gascost: return vm_exception('OUT OF GAS') compustate.gas -= gascost # adds neg gascost as a refund if below zero ext.add_refund(refund) ext.set_storage_data(msg.to, s0, s1) elif op == 'JUMP': compustate.pc = stk.pop() opnew = code[ compustate.pc] if compustate.pc < codelen else 0 jumped = True if opnew != JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = code[ compustate.pc] if compustate.pc < codelen else 0 jumped = True if opnew != JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == 'PC': stk.append(compustate.pc - 1) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(compustate.gas) # AFTER subtracting cost 1 elif op[:3] == 'DUP': # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for # 0x8f stk.append(stk[0x7f - opcode]) elif op[:4] == 'SWAP': # 0x8e - opcode is a negative number, -2 for 0x90 ... -17 for # 0x9f temp = stk[0x8e - opcode] stk[0x8e - opcode] = stk[-1] stk[-1] = temp elif op[:3] == 'LOG': """ 0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4) b. Logs are kept track of during tx execution exactly the same way as suicides (except as an ordered list, not a set). Each log is in the form [address, [topic1, ... ], data] where: * address is what the ADDRESS opcode would output * data is mem[MEMSTART: MEMSTART + MEMSZ] * topics are as provided by the opcode c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN]. """ depth = int(op[3:]) mstart, msz = stk.pop(), stk.pop() topics = [stk.pop() for x in range(depth)] compustate.gas -= msz * opcodes.GLOGBYTE if msg.static: return vm_exception('Cannot LOG inside a static context') if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = bytearray_to_bytestr(mem[mstart:mstart + msz]) ext.log(msg.to, topics, data) log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data))) # print('LOG', msg.to, topics, list(map(ord, data))) elif op == 'CREATE': value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') if msg.static: return vm_exception( 'Cannot CREATE inside a static context') if ext.get_balance(msg.to) >= value and msg.depth < MAX_DEPTH: cd = CallData(mem, mstart, msz) ingas = compustate.gas if ext.post_anti_dos_hardfork(): ingas = all_but_1n(ingas, opcodes.CALL_CHILD_LIMIT_DENOM) create_msg = Message(msg.to, b'', value, ingas, cd, msg.depth + 1) o, gas, addr = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(addr)) else: stk.append(0) compustate.gas = compustate.gas - ingas + gas else: stk.append(0) elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'): # Pull arguments from the stack if op in ('CALL', 'CALLCODE'): gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() else: gas, to, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() value = 0 # Static context prohibition if msg.static and value > 0: return vm_exception( 'Cannot make a non-zero-value call inside a static context' ) # Expand memory if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') to = utils.int_to_addr(to) # Extra gas costs based on hard fork-dependent factors extra_gas = (not ext.account_exists(to)) * (op == 'CALL') * (value > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT + \ (value > 0) * opcodes.GCALLVALUETRANSFER + \ ext.post_anti_dos_hardfork() * opcodes.CALL_SUPPLEMENTAL_GAS # Compute child gas limit if ext.post_anti_dos_hardfork(): if compustate.gas < extra_gas: return vm_exception('OUT OF GAS', needed=extra_gas) gas = min( gas, all_but_1n(compustate.gas - extra_gas, opcodes.CALL_CHILD_LIMIT_DENOM)) else: if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) submsg_gas = gas + opcodes.GSTIPEND * (value > 0) # Verify that there is sufficient balance and depth if ext.get_balance(msg.to) < value or msg.depth >= MAX_DEPTH: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) else: # Subtract gas from parent compustate.gas -= (gas + extra_gas) assert compustate.gas >= 0 cd = CallData(mem, meminstart, meminsz) # Generate the message if op == 'CALL': call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=msg.static) elif ext.post_homestead_hardfork( ) and op == 'DELEGATECALL': call_msg = Message(msg.sender, msg.to, msg.value, submsg_gas, cd, msg.depth + 1, code_address=to, transfers_value=False, static=msg.static) elif op == 'DELEGATECALL': return vm_exception('OPCODE INACTIVE') elif op == 'CALLCODE': call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=msg.static) elif op == 'STATICCALL': call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=True) else: raise Exception("Lolwut") # Get result result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) # Set output memory for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] compustate.gas += gas compustate.last_returned = bytearray(data) elif op == 'RETURN': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1]) elif op == 'REVERT': if not ext.post_metropolis_hardfork(): return vm_exception('Opcode not yet enabled') s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return revert(compustate.gas, mem[s0:s0 + s1]) elif op == 'SUICIDE': if msg.static: return vm_exception( 'Cannot SUICIDE inside a static context') to = utils.encode_int(stk.pop()) to = ((b'\x00' * (32 - len(to))) + to)[12:] xfer = ext.get_balance(msg.to) if ext.post_anti_dos_hardfork(): extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \ (not ext.account_exists( to)) * (xfer > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT if not eat_gas(compustate, extra_gas): return vm_exception("OUT OF GAS") ext.set_balance(to, ext.get_balance(to) + xfer) ext.set_balance(msg.to, 0) ext.add_suicide(msg.to) log_msg.debug('SUICIDING', addr=utils.checksum_encode(msg.to), to=utils.checksum_encode(to), xferring=xfer) return 1, compustate.gas, [] # assert utils.is_numeric(compustate.gas) # this is slow! # for a in stk: # assert is_numeric(a), (op, stk) # assert a >= 0 and a < 2**256, (a, op, stk) # if not jumped: # assert compustate.pc == nextpos # compustate.pc = nextpos if compustate.pc >= codelen: return peaceful_exit('CODE OUT OF RANGE', compustate.gas, []) return vm_exception('INVALID JUMP')
def create_contract(ext, msg): log_msg.debug('CONTRACT CREATION') code = msg.data.extract_all() if ext.tx_origin != msg.sender: ext.increment_nonce(msg.sender) if ext.post_constantinople_hardfork() and msg.sender == null_address: msg.to = utils.mk_contract_address(msg.sender, 0) # msg.to = sha3(msg.sender + code)[12:] else: nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1) msg.to = utils.mk_contract_address(msg.sender, nonce) if ext.post_metropolis_hardfork() and ( ext.get_nonce(msg.to) or len(ext.get_code(msg.to))): log_msg.debug('CREATING CONTRACT ON TOP OF EXISTING CONTRACT') return 0, 0, b'' b = ext.get_balance(msg.to) if b > 0: ext.set_balance(msg.to, b) ext.set_nonce(msg.to, 0) ext.set_code(msg.to, b'') # ext.reset_storage(msg.to) msg.is_create = True # assert not ext.get_code(msg.to) msg.data = vm.CallData([], 0, 0) snapshot = ext.snapshot() ext.set_nonce(msg.to, 1 if ext.post_spurious_dragon_hardfork() else 0) res, gas, dat = _apply_msg(ext, msg, code) log_msg.debug( 'CONTRACT CREATION FINISHED', res=res, gas=gas, dat=dat if len(dat) < 2500 else ( "data<%d>" % len(dat))) if res: if not len(dat): # ext.set_code(msg.to, b'') return 1, gas, msg.to gcost = len(dat) * opcodes.GCONTRACTBYTE if gas >= gcost and ( len(dat) <= 24576 or not ext.post_spurious_dragon_hardfork()): gas -= gcost else: dat = [] log_msg.debug( 'CONTRACT CREATION FAILED', have=gas, want=gcost, block_number=ext.block_number) if ext.post_homestead_hardfork(): ext.revert(snapshot) return 0, 0, b'' ext.set_code(msg.to, bytearray_to_bytestr(dat)) log_msg.debug('SETTING CODE', addr=encode_hex(msg.to), lendat=len(dat)) return 1, gas, msg.to else: ext.revert(snapshot) return 0, gas, dat
def createOrder(orderHash, key=tester.k0): key = normalize_key(key) v, r, s = ecsign(sha3("\x19Ethereum Signed Message:\n32" + orderHash), key) return v, zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32), zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32)
def createOrder(orderHash, key=tester.k0): key = normalize_key(key) v, r, s = ecsign(sha3("\x19Ethereum Signed Message:\n32" + orderHash), key) return v, zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32), zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32)
def sign(data: bytes, private_key_seed_ascii: str): priv = private_key_seed_ascii pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(data, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) return signature, eth_privtoaddr(priv)
def _sym_exec(self, gblState, depth=0, constraints=[]): environment = gblState.environment disassembly = environment.code state = gblState.mstate depth = depth start_addr = disassembly.instruction_list[state.pc]['address'] if start_addr == 0: self.current_func = "fallback" self.current_func_addr = start_addr node = Node(environment.active_account.contract_name, start_addr, constraints) logging.debug("- Entering node " + str(node.uid) + ", index = " + str(state.pc) + ", address = " + str(start_addr) + ", depth = " + str(depth)) if start_addr in disassembly.addr_to_func: # Enter a new function function_name = disassembly.addr_to_func[start_addr] self.current_func = function_name node.flags |= NodeFlags.FUNC_ENTRY logging.info("- Entering function " + environment.active_account.contract_name + ":" + function_name) node.function_name = self.current_func halt = False while not halt: try: instr = disassembly.instruction_list[state.pc] except IndexError: logging.debug("Invalid PC") return node # Save state before modifying anything node.states.append(gblState) gblState = self.copy_global_state(gblState) state = gblState.mstate self.total_states += 1 # Point program counter to next instruction state.pc += 1 op = instr['opcode'] # logging.debug("[" + environment.active_account.contract_name + "] " + helper.get_trace_line(instr, state)) # slows down execution significantly. # Stack ops if op.startswith("PUSH"): value = BitVecVal(int(instr['argument'][2:], 16), 256) state.stack.append(value) elif op.startswith('DUP'): dpth = int(op[3:]) try: state.stack.append(state.stack[-dpth]) except: halt = True elif op.startswith('SWAP'): dpth = int(op[4:]) try: temp = state.stack[-dpth - 1] state.stack[-dpth - 1] = state.stack[-1] state.stack[-1] = temp except IndexError: # Stack underflow halt = True elif op == 'POP': try: state.stack.pop() except IndexError: # Stack underflow halt = True # Bitwise ops elif op == 'AND': try: op1, op2 = state.stack.pop(), state.stack.pop() if (type(op1) == BoolRef): op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256)) if (type(op2) == BoolRef): op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256)) state.stack.append(op1 & op2) except IndexError: # Stack underflow halt = True elif op == 'OR': try: op1, op2 = state.stack.pop(), state.stack.pop() if (type(op1) == BoolRef): op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256)) if (type(op2) == BoolRef): op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256)) state.stack.append(op1 | op2) except IndexError: # Stack underflow halt = True elif op == 'XOR': state.stack.append(state.stack.pop() ^ state.stack.pop()) elif op == 'NOT': state.stack.append(TT256M1 - state.stack.pop()) elif op == 'BYTE': s0, s1 = state.stack.pop(), state.stack.pop() state.stack.append(BitVecVal(0, 256)) # Arithmetics elif op == 'ADD': state.stack.append((helper.pop_bitvec(state) + helper.pop_bitvec(state))) elif op == 'SUB': state.stack.append((helper.pop_bitvec(state) - helper.pop_bitvec(state))) elif op == 'MUL': state.stack.append(helper.pop_bitvec(state) * helper.pop_bitvec(state)) elif op == 'DIV': state.stack.append(UDiv(helper.pop_bitvec(state), helper.pop_bitvec(state))) elif op == 'MOD': s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state) state.stack.append(0 if s1 == 0 else URem(s0, s1)) elif op == 'SDIV': s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state) state.stack.append(s0 / s1) elif op == 'SMOD': s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state) state.stack.append(0 if s1 == 0 else s0 % s1) elif op == 'ADDMOD': s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec(state), helper.pop_bitvec(state) state.stack.append((s0 + s1) % s2 if s2 else 0) elif op == 'MULMOD': s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec(state), helper.pop_bitvec(state) state.stack.append((s0 * s1) % s2 if s2 else 0) elif op == 'EXP': # we only implement 2 ** x base, exponent = helper.pop_bitvec(state), helper.pop_bitvec(state) if (type(base) != BitVecNumRef) or (type(exponent) != BitVecNumRef): state.stack.append(BitVec(str(base) + "_EXP_" + str(exponent), 256)) elif (base.as_long() == 2): if exponent.as_long() == 0: state.stack.append(BitVecVal(1, 256)) else: state.stack.append(base << (exponent - 1)) else: state.stack.append(base) elif op == 'SIGNEXTEND': s0, s1 = state.stack.pop(), state.stack.pop() try: s0 = helper.get_concrete_int(s0) s1 = helper.get_concrete_int(s1) if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): state.stack.append(s1 | (TT256 - (1 << testbit))) else: state.stack.append(s1 & ((1 << testbit) - 1)) else: state.stack.append(s1) except: halt = True continue # Comparisons elif op == 'LT': exp = ULT(helper.pop_bitvec(state), helper.pop_bitvec(state)) state.stack.append(exp) elif op == 'GT': exp = UGT(helper.pop_bitvec(state), helper.pop_bitvec(state)) state.stack.append(exp) elif op == 'SLT': exp = helper.pop_bitvec(state) < helper.pop_bitvec(state) state.stack.append(exp) elif op == 'SGT': exp = helper.pop_bitvec(state) > helper.pop_bitvec(state) state.stack.append(exp) elif op == 'EQ': op1 = state.stack.pop() op2 = state.stack.pop() if(type(op1) == BoolRef): op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256)) if(type(op2) == BoolRef): op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256)) exp = op1 == op2 state.stack.append(exp) elif op == 'ISZERO': val = state.stack.pop() if (type(val) == BoolRef): exp = val == False else: exp = val == 0 state.stack.append(exp) # Call data elif op == 'CALLVALUE': state.stack.append(environment.callvalue) elif op == 'CALLDATALOAD': # unpack 32 bytes from calldata into a word and put it on the stack op0 = state.stack.pop() try: offset = helper.get_concrete_int(simplify(op0)) b = environment.calldata[offset] except AttributeError: logging.debug("CALLDATALOAD: Unsupported symbolic index") state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256)) continue except IndexError: logging.debug("Calldata not set, using symbolic variable instead") state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256)) continue if type(b) == int: val = b'' try: for i in range(offset, offset + 32): val += environment.calldata[i].to_bytes(1, byteorder='big') logging.debug("Final value: " + str(int.from_bytes(val, byteorder='big'))) state.stack.append(BitVecVal(int.from_bytes(val, byteorder='big'), 256)) except: state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256)) else: # symbolic variable state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256)) elif op == 'CALLDATASIZE': if environment.calldata_type == CalldataType.SYMBOLIC: state.stack.append(BitVec("calldatasize_" + environment.active_account.contract_name, 256)) else: state.stack.append(BitVecVal(len(environment.calldata), 256)) elif op == 'CALLDATACOPY': op0, op1, op2 = state.stack.pop(), state.stack.pop(), state.stack.pop() try: mstart = helper.get_concrete_int(op0) except: logging.debug("Unsupported symbolic memory offset in CALLDATACOPY") continue try: dstart = helper.get_concrete_int(op1) except: logging.debug("Unsupported symbolic calldata offset in CALLDATACOPY") state.mem_extend(mstart, 1) state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_cpy", 256) continue try: size = helper.get_concrete_int(op2) except: logging.debug("Unsupported symbolic size in CALLDATACOPY") state.mem_extend(mstart, 1) state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(dstart), 256) continue if size > 0: try: state.mem_extend(mstart, size) except: logging.debug("Memory allocation error: mstart = " + str(mstart) + ", size = " + str(size)) state.mem_extend(mstart, 1) state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(dstart), 256) continue try: i_data = environment.calldata[dstart] for i in range(mstart, mstart + size): state.memory[i] = environment.calldata[i_data] i_data += 1 except: logging.debug("Exception copying calldata to memory") state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(dstart), 256) elif op == 'STOP': if len(self.call_stack): self.pending_returns[self.call_stack[-1]].append(node.uid) halt = True continue # Environment elif op == 'ADDRESS': state.stack.append(environment.sender) elif op == 'BALANCE': addr = state.stack.pop() state.stack.append(BitVec("balance_at_" + str(addr), 256)) elif op == 'ORIGIN': state.stack.append(environment.origin) elif op == 'CALLER': state.stack.append(environment.sender) elif op == 'CODESIZE': state.stack.append(len(disassembly.instruction_list)) if op == 'SHA3': op0, op1 = state.stack.pop(), state.stack.pop() try: index, length = helper.get_concrete_int(op0), helper.get_concrete_int(op1) except: # Can't access symbolic memory offsets state.stack.append(BitVec("KECCAC_mem_" + str(op0) + ")", 256)) continue try: data = b'' for i in range(index, index + length): data += helper.get_concrete_int(state.memory[i]).to_bytes(1, byteorder='big') i += 1 except: svar = str(state.memory[index]) svar = svar.replace(" ", "_") state.stack.append(BitVec("keccac_" + svar, 256)) continue keccac = utils.sha3(utils.bytearray_to_bytestr(data)) logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccac))) state.stack.append(BitVecVal(helper.concrete_int_from_bytes(keccac, 0), 256)) elif op == 'GASPRICE': state.stack.append(BitVec("gasprice", 256)) elif op == 'CODECOPY': # Not implemented start, s1, size = state.stack.pop(), state.stack.pop(), state.stack.pop() elif op == 'EXTCODESIZE': addr = state.stack.pop() state.stack.append(BitVec("extcodesize", 256)) elif op == 'EXTCODECOPY': # Not implemented addr = state.stack.pop() start, s2, size = state.stack.pop(), state.stack.pop(), state.stack.pop() elif op == 'BLOCKHASH': blocknumber = state.stack.pop() state.stack.append(BitVec("blockhash_block_" + str(blocknumber), 256)) elif op == 'COINBASE': state.stack.append(BitVec("coinbase", 256)) elif op == 'TIMESTAMP': state.stack.append(BitVec("timestamp", 256)) elif op == 'NUMBER': state.stack.append(BitVec("block_number", 256)) elif op == 'DIFFICULTY': state.stack.append(BitVec("block_difficulty", 256)) elif op == 'GASLIMIT': state.stack.append(BitVec("block_gaslimit", 256)) elif op == 'MLOAD': op0 = state.stack.pop() logging.debug("MLOAD[" + str(op0) + "]") try: offset = helper.get_concrete_int(op0) except AttributeError: logging.debug("Can't MLOAD from symbolic index") data = BitVec("mem_" + str(op0), 256) continue try: data = helper.concrete_int_from_bytes(state.memory, offset) except IndexError: # Memory slot not allocated data = BitVec("mem_" + str(offset), 256) except TypeError: # Symbolic memory data = state.memory[offset] logging.debug("Load from memory[" + str(offset) + "]: " + str(data)) state.stack.append(data) elif op == 'MSTORE': op0, value = state.stack.pop(), state.stack.pop() try: mstart = helper.get_concrete_int(op0) except AttributeError: logging.debug("MSTORE to symbolic index. Not supported") continue try: state.mem_extend(mstart, 32) except Exception: logging.debug("Error extending memory, mstart = " + str(mstart) + ", size = 32") logging.debug("MSTORE to mem[" + str(mstart) + "]: " + str(value)) try: # Attempt to concretize value _bytes = helper.concrete_int_to_bytes(value) i = 0 for b in _bytes: state.memory[mstart + i] = _bytes[i] i += 1 except: try: state.memory[mstart] = value except: logging.debug("Invalid memory access") continue elif op == 'MSTORE8': op0, value = state.stack.pop(), state.stack.pop() try: offset = helper.get_concrete_int(op0) except AttributeError: logging.debug("MSTORE to symbolic index. Not supported") continue state.mem_extend(offset, 1) state.memory[offset] = value % 256 elif op == 'SLOAD': index = state.stack.pop() logging.debug("Storage access at index " + str(index)) try: index = helper.get_concrete_int(index) except AttributeError: index = str(index) try: data = gblState.accounts[gblState.environment.sender].storage[index] except KeyError: data = BitVec("storage_" + str(index), 256) gblState.environment.active_account.storage[index] = data state.stack.append(data) elif op == 'SSTORE': index, value = state.stack.pop(), state.stack.pop() logging.debug("Write to storage[" + str(index) + "] at node " + str(start_addr)) try: index = helper.get_concrete_int(index) except AttributeError: index = str(index) try: # Create a fresh copy of the account object before modifying storage for k in gblState.accounts: if gblState.accounts[k] == gblState.environment.active_account: gblState.accounts[k] = copy.deepcopy(gblState.accounts[k]) gblState.environment.active_account = gblState.accounts[k] break gblState.environment.active_account.storage[index] = value except KeyError: logging.debug("Error writing to storage: Invalid index") continue elif op == 'JUMP': try: jump_addr = helper.get_concrete_int(state.stack.pop()) except AttributeError: logging.debug("Invalid jump argument (symbolic address)") halt = True continue except IndexError: # Stack Underflow halt = True continue if (depth < self.max_depth): i = helper.get_instruction_index(disassembly.instruction_list, jump_addr) if i is None: logging.debug("JUMP to invalid address") halt = True continue opcode = disassembly.instruction_list[i]['opcode'] if opcode == "JUMPDEST": new_gblState = self.copy_global_state(gblState) new_gblState.mstate.pc = i new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node self.edges.append(Edge(node.uid, new_node.uid, JumpType.UNCONDITIONAL)) halt = True continue else: logging.debug("Skipping JUMP to invalid destination (not JUMPDEST): " + str(jump_addr)) halt = True # continue else: logging.debug("Max depth reached, skipping JUMP") halt = True # continue elif op == 'JUMPI': op0, condition = state.stack.pop(), state.stack.pop() try: jump_addr = helper.get_concrete_int(op0) except: logging.debug("Skipping JUMPI to invalid destination.") if (depth < self.max_depth): i = helper.get_instruction_index(disassembly.instruction_list, jump_addr) if not i: logging.debug("Invalid jump destination: " + str(jump_addr)) else: instr = disassembly.instruction_list[i] if instr['opcode'] != "JUMPDEST": logging.debug("Invalid jump destination: " + str(jump_addr)) halt = True continue elif (type(condition) == BoolRef): if not is_false(simplify(condition)): # Create new node for condition == True new_gblState = self.copy_global_state(gblState) new_gblState.mstate.pc = i new_constraints = copy.deepcopy(constraints) new_constraints.append(condition) new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=new_constraints) self.nodes[new_node.uid] = new_node self.edges.append(Edge(node.uid, new_node.uid, JumpType.CONDITIONAL, condition)) else: logging.debug("Pruned unreachable states.") else: logging.debug("Invalid condition: " + str(condition) + "(type " + str(type(condition)) + ")") halt = True continue new_gblState = self.copy_global_state(gblState) if (type(condition) == BoolRef): negated = Not(condition) else: negated = condition == 0 if not is_false(simplify(negated)): new_constraints = copy.deepcopy(constraints) new_constraints.append(negated) new_node = self._sym_exec(new_gblState, depth=depth, constraints=new_constraints) self.nodes[new_node.uid] = new_node self.edges.append(Edge(node.uid, new_node.uid, JumpType.CONDITIONAL, negated)) halt = True # continue else: logging.debug("Max depth reached, skipping JUMPI") elif op == 'PC': state.stack.append(state.pc - 1) elif op == 'MSIZE': state.stack.append(BitVec("msize", 256)) elif op == 'GAS': state.stack.append(BitVec("gas", 256)) elif op.startswith('LOG'): dpth = int(op[3:]) state.stack.pop(), state.stack.pop() [state.stack.pop() for x in range(dpth)] # Not supported elif op == 'CREATE': state.stack.pop(), state.stack.pop(), state.stack.pop() # Not supported state.stack.append(0) elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'): if op in ('CALL', 'CALLCODE'): gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop() else: gas, to, meminstart, meminsz, memoutstart, memoutsz = \ state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop() try: callee_address = hex(helper.get_concrete_int(to)) except AttributeError: # Not a concrete call address. Call target may be an address in storage. m = re.search(r'storage_(\d+)', str(simplify(to))) logging.debug("CALL to: " + str(simplify(to))) if (m and self.dynamic_loader is not None): idx = int(m.group(1)) logging.info("Dynamic contract address at storage index " + str(idx)) # attempt to read the contract address from instance storage try: callee_address = self.dynamic_loader.read_storage(environment.active_account.address, idx) except: logging.debug("Error accessing contract storage.") ret = BitVec("retval_" + str(instr['address']), 256) state.stack.append(ret) continue # testrpc simply returns the address, geth response is more elaborate. if not re.match(r"^0x[0-9a-f]{40}$", callee_address): callee_address = "0x" + callee_address[26:] else: ret = BitVec("retval_" + str(instr['address']), 256) state.stack.append(ret) continue if not re.match(r"^0x[0-9a-f]{40}", callee_address): logging.debug("Invalid address: " + str(callee_address)) ret = BitVec("retval_" + str(instr['address']), 256) state.stack.append(ret) continue if (int(callee_address, 16) < 5): logging.info("Native contract called: " + callee_address) # Todo: Implement native contracts ret = BitVec("retval_" + str(instr['address']), 256) state.stack.append(ret) continue try: callee_account = self.accounts[callee_address] except KeyError: # We have a valid call address, but contract is not in the modules list logging.info("Module with address " + callee_address + " not loaded.") if self.dynamic_loader is not None: logging.info("Attempting to load dependency") try: code = self.dynamic_loader.dynld(environment.active_account.address, callee_address) except Exception as e: logging.info("Unable to execute dynamic loader.") if code is None: logging.info("No code returned, not a contract account?") ret = BitVec("retval_" + str(instr['address']), 256) state.stack.append(ret) continue # New contract bytecode loaded successfully, create a new contract account self.accounts[callee_address] = Account(callee_address, code, callee_address) logging.info("Dependency loaded: " + callee_address) else: logging.info("Dynamic loader unavailable. Skipping call") ret = BitVec("retval_" + str(instr['address']), 256) state.stack.append(ret) continue logging.info("Executing " + op + " to: " + callee_address) try: callee_account = self.accounts[callee_address] except KeyError: logging.info("Contract " + str(callee_address) + " not loaded.") logging.info((str(self.accounts))) ret = BitVec("retval_" + str(instr['address']), 256) state.stack.append(ret) continue try: # TODO: This only allows for either fully concrete or fully symbolic calldata. # Improve management of memory and callata to support a mix between both types. calldata = state.memory[helper.get_concrete_int(meminstart):helper.get_concrete_int(meminstart + meminsz)] if (len(calldata) < 32): calldata += [0] * (32 - len(calldata)) calldata_type = CalldataType.CONCRETE logging.debug("Calldata: " + str(calldata)) except AttributeError: logging.info("Unsupported symbolic calldata offset") calldata_type = CalldataType.SYMBOLIC calldata = [] self.call_stack.append(instr['address']) self.pending_returns[instr['address']] = [] if (op == 'CALL'): callee_environment = Environment(callee_account, BitVecVal(int(environment.active_account.address, 16), 256), calldata, environment.gasprice, value, environment.origin, calldata_type=calldata_type) new_gblState = GlobalState(gblState.accounts, callee_environment, MachineState(gas)) new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node elif (op == 'CALLCODE'): temp_callvalue = environment.callvalue temp_caller = environment.caller temp_calldata = environment.calldata environment.callvalue = value environment.caller = environment.address environment.calldata = calldata new_gblState = GlobalState(gblState.accounts, environment, MachineState(gas)) new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node environment.callvalue = temp_callvalue environment.caller = temp_caller environment.calldata = temp_calldata elif (op == 'DELEGATECALL'): temp_code = environment.code temp_calldata = environment.calldata environment.code = callee_account.code environment.calldata = calldata new_gblState = GlobalState(gblState.accounts, environment, MachineState(gas)) new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints) self.nodes[new_node.uid] = new_node environment.code = temp_code environment.calldata = temp_calldata self.edges.append(Edge(node.uid, new_node.uid, JumpType.CALL)) ''' There may be multiple possible returns from the callee contract. Currently, we don't create separate nodes on the CFG for each of them. Instead, a single "return node" is created and a separate edge is added for each return path. The return value is always symbolic. ''' ret = BitVec("retval_" + str(disassembly.instruction_list[state.pc]['address']), 256) state.stack.append(ret) return_address = self.call_stack.pop() new_gblState = self.copy_global_state(gblState) new_node = self._sym_exec(gblState, depth=depth + 1, constraints=constraints) new_node.flags |= NodeFlags.CALL_RETURN self.nodes[new_node.uid] = new_node for ret_uid in self.pending_returns[return_address]: self.edges.append(Edge(ret_uid, new_node.uid, JumpType.RETURN)) state.stack.append(BitVec("retval", 256)) halt = True elif op == 'RETURN': offset, length = state.stack.pop(), state.stack.pop() try: self.last_returned = state.memory[helper.get_concrete_int(offset):helper.get_concrete_int(offset + length)] except AttributeError: logging.debug("Return with symbolic length or offset. Not supported") if len(self.call_stack): self.pending_returns[self.call_stack[-1]].append(node.uid) halt = True elif op == 'SUICIDE': halt = True elif op == 'REVERT': if len(self.call_stack): self.pending_returns[self.call_stack[-1]].append(node.uid) halt = True elif op == 'ASSERT_FAIL' or op == 'INVALID': if len(self.call_stack): self.pending_returns[self.call_stack[-1]].append(node.uid) halt = True logging.debug("Returning from node " + str(node.uid)) return node
def send_msg_transfer_value(mainchain_state, shard_state, shard_id, tx): urs_addr = get_urs_contract(shard_id)['addr'] log_rctx.debug("Begin: urs.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) receipt_id = tx.r # we should deduct the startgas of this message in advance, because # the message may be possibly a contract, not only a normal value transfer. value = tx.value - tx.gasprice * tx.startgas log_rctx.debug( "value={}, tx.value={}, tx.gasprice={}, tx.startgas={}".format( value, tx.value, tx.gasprice, tx.startgas)) if value <= 0: return False, None # start transactioning if not send_msg_add_used_receipt(shard_state, shard_id, receipt_id): return False, None receipt_sender_hex = call_valmgr(mainchain_state, 'get_receipts__sender', [receipt_id]) receipt_data = call_valmgr(mainchain_state, 'get_receipts__data', [receipt_id]) msg_data = (b'00' * 12) + utils.parse_as_bin(receipt_sender_hex) + receipt_data msg = vm.Message(urs_addr, tx.to, value, tx.startgas - tx.intrinsic_gas_used, msg_data) env_tx = Transaction(0, tx.gasprice, tx.startgas, b'', 0, b'') env_tx._sender = urs_addr ext = VMExt(shard_state, env_tx) log_rctx.debug( "before apply_msg: urs_addr.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) # even if `transfer_value` in `apply_msg` fails, no error occurs. # it seems no raise in apply_msg result, gas_remained, data = apply_msg(ext, msg) log_rctx.debug( "after apply_msg: urs_addr.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) assert gas_remained >= 0 gas_used = tx.startgas - gas_remained # Transaction failed if not result: log_rctx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) shard_state.gas_used += tx.startgas shard_state.delta_balance(tx.to, tx.gasprice * gas_remained) shard_state.delta_balance(shard_state.block_coinbase, tx.gasprice * gas_used) output = b'' success = 0 # Transaction success else: log_rctx.debug('TX SUCCESS', data=data) shard_state.refunds += len(set( shard_state.suicides)) * opcodes.GSUICIDEREFUND if shard_state.refunds > 0: log_rctx.debug('Refunding', gas_refunded=min(shard_state.refunds, gas_used // 2)) gas_remained += min(shard_state.refunds, gas_used // 2) gas_used -= min(shard_state.refunds, gas_used // 2) shard_state.refunds = 0 # sell remaining gas shard_state.delta_balance(tx.to, tx.gasprice * gas_remained) log_rctx.debug("gas_remained={}, gasprice={}".format( gas_remained, tx.gasprice)) log_rctx.debug("End: urs.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) shard_state.delta_balance(shard_state.block_coinbase, tx.gasprice * gas_used) shard_state.gas_used += gas_used if tx.to: output = utils.bytearray_to_bytestr(data) else: output = data success = 1 # Clear suicides suicides = shard_state.suicides shard_state.suicides = [] for s in suicides: shard_state.set_balance(s, 0) shard_state.del_account(s) # Pre-Metropolis: commit state after every tx if not shard_state.is_METROPOLIS() and not SKIP_MEDSTATES: shard_state.commit() # Construct a receipt r = mk_receipt(shard_state, success, shard_state.logs) _logs = list(shard_state.logs) shard_state.logs = [] shard_state.add_receipt(r) shard_state.set_param('bloom', shard_state.bloom | r.bloom) shard_state.set_param('txindex', shard_state.txindex + 1) return success, output