def test_transaction(filename, testname, testdata): testdata = fixture_to_bytes(testdata) try: rlpdata = decode_hex(testdata["rlp"][2:]) o = {} tx = rlp.decode(rlpdata, transactions.Transaction) blknum = int(testdata["blocknumber"]) if blknum >= config.default_config["HOMESTEAD_FORK_BLKNUM"]: tx.check_low_s() o["sender"] = tx.sender o["transaction"] = { "data": b'0x' * (len(tx.data) > 0) + encode_hex(tx.data), "gasLimit": str_to_bytes(str(tx.startgas)), "gasPrice": str_to_bytes(str(tx.gasprice)), "nonce": str_to_bytes(str(tx.nonce)), "r": b'0x' + encode_hex(utils.zpad(utils.int_to_big_endian(tx.r), 32)), "s": b'0x' + encode_hex(utils.zpad(utils.int_to_big_endian(tx.s), 32)), "v": str_to_bytes(str(tx.v)), "value": str_to_bytes(str(tx.value)), "to": encode_hex(tx.to), } except Exception as e: tx = None sys.stderr.write(str(e)) if 'transaction' not in testdata: # expected to fail assert tx is None else: assert set(o['transaction'].keys()) == set(testdata.get("transaction", dict()).keys()) o.get("transaction", None) == testdata.get("transaction", None) assert encode_hex(o.get("sender", '')) == testdata.get("sender", '')
def coerce_addr_to_hex(x): if is_numeric(x): return encode_hex(zpad(big_endian_int.serialize(x), 20)) elif len(x) == 40 or len(x) == 0: return x else: return encode_hex(zpad(x, 20)[-20:])
def make_keystore_json(priv, pw, kdf="pbkdf2", cipher="aes-128-ctr"): # Get the hash function and default parameters if kdf not in kdfs: raise Exception("Hash algo %s not supported" % kdf) kdfeval = kdfs[kdf]["calc"] kdfparams = kdfs[kdf]["mkparams"]() # Compute derived key derivedkey = kdfeval(pw, kdfparams) # Get the cipher and default parameters if cipher not in ciphers: raise Exception("Encryption algo %s not supported" % cipher) encrypt = ciphers[cipher]["encrypt"] cipherparams = ciphers[cipher]["mkparams"]() # Produce the encryption key and encrypt enckey = derivedkey[:16] c = encrypt(priv, enckey, cipherparams) # Compute the MAC mac = sha3(derivedkey[16:32] + c) # Make a UUID u = encode_hex(os.urandom(16)) uuid = b'-'.join((u[:8], u[8:12], u[12:16], u[16:20], u[20:])) # Return the keystore json return { "crypto": { "cipher": cipher, "ciphertext": encode_hex(c), "cipherparams": cipherparams, "kdf": kdf, "kdfparams": kdfparams, "mac": encode_hex(mac), "version": 1 }, "id": uuid, "version": 3 }
def run_test(filename, testname, testdata): testdata = fixture_to_bytes(testdata) try: rlpdata = decode_hex(testdata["rlp"][2:]) print rlpdata.encode("hex") o = {} tx = rlp.decode(rlpdata, transactions.Transaction) o["sender"] = tx.sender o["transaction"] = { "data": b"0x" * (len(tx.data) > 0) + encode_hex(tx.data), "gasLimit": str_to_bytes(str(tx.startgas)), "gasPrice": str_to_bytes(str(tx.gasprice)), "nonce": str_to_bytes(str(tx.nonce)), "r": b"0x" + encode_hex(utils.zpad(utils.int_to_big_endian(tx.r), 32)), "s": b"0x" + encode_hex(utils.zpad(utils.int_to_big_endian(tx.s), 32)), "v": str_to_bytes(str(tx.v)), "value": str_to_bytes(str(tx.value)), "to": encode_hex(tx.to), } except: tx = None if "transaction" not in testdata: # expected to fail assert tx is None else: assert set(o["transaction"].keys()) == set(testdata.get("transaction", dict()).keys()) o.get("transaction", None) == testdata.get("transaction", None) assert encode_hex(o.get("sender", "")) == testdata.get("sender", "")
def msg_wrapper(msg): hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append(dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=encode_hex(msg.to), data=b'0x' + hexdata)) return 1, msg.gas, b''
def decode_single(typ, data): base, sub, _ = typ if base == 'address': return encode_hex(data[12:]) elif base == 'hash': return data[32 - int(sub):] elif base == 'string' or base == 'bytes': if len(sub): return data[:int(sub)] else: l = big_endian_to_int(data[0:32]) return data[32:][:l] elif base == 'uint': return big_endian_to_int(data) elif base == 'int': o = big_endian_to_int(data) return (o - 2 ** int(sub)) if o >= 2 ** (int(sub) - 1) else o elif base == 'ureal': high, low = [int(x) for x in sub.split('x')] return big_endian_to_int(data) * 1.0 // 2 ** low elif base == 'real': high, low = [int(x) for x in sub.split('x')] o = big_endian_to_int(data) i = (o - 2 ** (high + low)) if o >= 2 ** (high + low - 1) else o return (i * 1.0 // 2 ** low) elif base == 'bool': return bool(int(encode_hex(data), 16))
def to_dict(self): return { "bloom": encode_hex(bloom.b64(bloom.bloom_from_list(self.bloomables()))), "address": encode_hex(self.address), "data": b"0x" + encode_hex(self.data), "topics": [encode_hex(utils.int32.serialize(t)) for t in self.topics], }
def decode_single(typ, data): try: base, sub, arrlist = typ except ValueError: base, sub, arrlist = process_type(typ) if is_hex_encoded_value(data): data = decode_hex(strip_0x_prefix(data)) if base == 'address': return encode_hex(data[12:]) elif base == 'hash': return data[32 - int(sub):] elif base == 'string' or base == 'bytes': if len(sub): return data[:int(sub)] else: l = big_endian_to_int(data[0:32]) return data[32:][:l] elif base == 'uint': return big_endian_to_int(data) elif base == 'int': o = big_endian_to_int(data) return (o - 2**int(sub)) if o >= 2**(int(sub) - 1) else o elif base == 'ureal': high, low = [int(x) for x in sub.split('x')] return big_endian_to_int(data) * 1.0 / 2**low elif base == 'real': high, low = [int(x) for x in sub.split('x')] o = big_endian_to_int(data) i = (o - 2**(high + low)) if o >= 2**(high + low - 1) else o return (i * 1.0 / 2**low) elif base == 'bool': return bool(int(encode_hex(data), 16))
def on_receive_blockhashes(self, proto, blockhashes): if blockhashes: log.debug("on_receive_blockhashes", count=len(blockhashes), remote_id=proto, first=encode_hex(blockhashes[0]), last=encode_hex(blockhashes[-1])) else: log.debug("recv 0 remote block hashes, signifying genesis block") self.synchronizer.receive_blockhashes(proto, blockhashes)
def gen_test(code, val, data): while 1: s = t.state(1) c = s.contract(code) pre = s.block.to_dict()['state'] env = { "currentCoinbase": s.block.coinbase, "currentDifficulty": str(s.block.difficulty), "currentGasLimit": str(s.block.gas_limit), "currentNumber": str(s.block.number), "currentTimestamp": str(s.block.timestamp), "previousHash": encode_hex(s.block.prevhash) } apply_message_calls = [] orig_apply_msg = pb.apply_msg def apply_msg_wrapper(_block, _tx, msg, code): apply_message_calls.append(dict(gasLimit=msg.gas, value=msg.value, desgination=msg.to, data=encode_hex(msg.data))) result, gas_rem, data = orig_apply_msg(_block, _tx, msg, code) return result, gas_rem, data pb.apply_msg = apply_msg_wrapper d = serpent.encode_datalist(map(int, data)) tx = pyethereum.transactions.Transaction(1, 10**12, 10000, c, val, data)\ .sign(t.keys[0]) msg = pb.Message(t.accounts[0], c, val, 10000, d) exek = { "address": msg.to, "caller": msg.sender, "code": '0x' + encode_hex(s.block.get_code(c)), "data": '0x' + encode_hex(d), "gas": str(10000), "gasPrice": str(10**12), "origin": tx.sender, "value": str(val) } success, gas, o = pb.apply_msg(s.block, tx, msg, s.block.get_code(c)) post = s.block.to_dict()['state'] callcreates = apply_message_calls[1:] if success: break return { "callcreates": callcreates, "env": env, "pre": pre, "post": post, "exec": exek, "gas": str(gas), "out": '0x'+encode_hex(''.join(map(ascii_chr, o))) }
def eth_getBlockByNumber(block_number, full_tx): block_number = format_block_number(block_number) #if block_number >= len( block = evm.blocks[block_number] return { "number": "0x" + int_to_hex(block.number), "hash": "0x" + encode_hex(block.hash), "parentHash": "0x" + encode_hex(block.prevhash), "nonce": "0x" + encode_hex(block.nonce), "sha3Uncles": "0x" + encode_hex(block.uncles_hash), # TODO logsBloom / padding "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "transactionsRoot": "0x" + encode_hex(block.tx_list_root), "stateRoot": "0x" + encode_hex(block.state_root), "miner": "0x" + encode_hex(block.coinbase), "difficulty": "0x" + int_to_hex(block.difficulty), # https://github.com/ethereum/pyethereum/issues/266 # "totalDifficulty": "0x" + int_to_hex(block.chain_difficulty()), "size": "0x" + int_to_hex(len(rlp.encode(block))), "extraData": "0x" + encode_hex(block.extra_data), "gasLimit": "0x" + int_to_hex(block.gas_limit), "gasUsed": "0x" + int_to_hex(block.gas_used), "timestamp": "0x" + int_to_hex(block.timestamp), "transactions": jsonTxs(block.get_transactions()) if full_tx else [encode_hex(tx) for tx in block.get_transaction_hashes()], "uncles": block.uncles }
def test_allowances(self): # a # aa ab # aaa aab aba # aaaa aaba aabb abaa genesis_hash = decode_hex("fca5e1a248b8fee34db137da5e38b41f95d11feb5a8fa192a150d8d5d8de1c59") null_hash = decode_hex("0000000000000000000000000000000000000000000000000000000000000000") # print encode_hex(null_hash) k0_addr = encode_hex(keys.privtoaddr(t.k0)) k1_addr = encode_hex(keys.privtoaddr(t.k1)) k2_addr = encode_hex(keys.privtoaddr(t.k2)) contract_addr = encode_hex(keys.privtoaddr(t.k9)) self.assertEqual(k1_addr, '7d577a597b2742b498cb5cf0c26cdcd726d39e6e') self.assertEqual(self.rc.balanceOf(keys.privtoaddr(t.k0), genesis_hash), 2100000000000000) self.rc.transfer(k1_addr, 1000000, genesis_hash, sender=t.k0, startgas=200000) # self.s.block.timestamp = self.s.block.timestamp + 100 # self.s = t.state() window_index = 4 # index of genesis hash in struct self.assertEqual(self.rc.balanceOf(keys.privtoaddr(t.k0), genesis_hash), 2100000000000000-1000000) self.assertEqual(self.rc.balanceOf(k1_addr, genesis_hash), 1000000) with self.assertRaises(TransactionFailed): self.rc.transferFrom(k0_addr, k1_addr, 400000, genesis_hash, sender=t.k2, startgas=200000) self.rc.approve(k2_addr, 500000, genesis_hash, sender=t.k0, startgas=200000) with self.assertRaises(TransactionFailed): self.rc.transferFrom(k0_addr, k1_addr, 600000, genesis_hash, sender=t.k2, startgas=200000) self.mine() start_bal = self.rc.balanceOf(k0_addr, genesis_hash) self.rc.transferFrom(k0_addr, k1_addr, 400000, genesis_hash, sender=t.k2, startgas=200000) #self.assertEqual(self.rc.balanceOf(k1_addr, genesis_hash), 1000000-500000) self.mine() self.assertEqual(self.rc.balanceOf(k1_addr, genesis_hash), 400000+1000000) self.assertEqual(self.rc.balanceOf(k0_addr, genesis_hash), start_bal - 400000) with self.assertRaises(TransactionFailed): self.rc.transferFrom(k0_addr, 400000, genesis_hash, sender=t.k2, startgas=200000)
def jsonTxs(txs): return [{ "from": "0x" + encode_hex(tx._sender), "gas": tx.startgas, "gasPrice": tx.gasprice, "hash": "0x" + encode_hex(tx.hash), "input": "0x" + encode_hex(tx.data), "nonce": tx.nonce, "to": "0x" + encode_hex(tx.to) if tx.to else None, "transactionIndex": i, "value": tx.value } for i, tx in enumerate(txs)]
def dump_genesis_block_tests_data(db): import json g = genesis(db) data = dict( genesis_state_root=encode_hex(g.state_root), genesis_hash=g.hex_hash(), genesis_rlp_hex=encode_hex(g.serialize()), initial_alloc=dict() ) for addr, balance in GENESIS_INITIAL_ALLOC.items(): data['initial_alloc'][addr] = to_string(balance) print(json.dumps(data, indent=1))
def synchronize_unknown_block(self, peer, block_hash, force=False): "Case: block with unknown parent. Fetches unknown ancestors and this block" log.debug('sync unknown', peer=peer, block=encode_hex(block_hash)) if block_hash == self.chain_manager.genesis.hash or block_hash in self.chain_manager: log.debug('known_hash, skipping', peer=peer, hash=encode_hex(block_hash)) return if peer and (not peer in self.synchronization_tasks) or force: log.debug('new sync task', peer=peer) self.synchronization_tasks[peer] = SynchronizationTask( self.chain_manager, peer, block_hash) else: log.debug('existing synctask', peer=peer)
def send(transaction): if "from" in transaction: addr = decode_hex(strip_0x(transaction['from'])) sender = keys[accounts.index(addr)] else: sender = keys[0] if "value" in transaction: value = int(strip_0x(transaction['value']), 16) else: value = 0 if "to" in transaction: to = strip_0x(transaction['to']) else: to = None if "data" in transaction: data = decode_hex(strip_0x(transaction['data'])) else: data = "" if "gas" in transaction: gas = int(strip_0x(transaction['gas']), 16) else: gas = None # print("value: " + encode_hex(value)) # print("to: " + to) # print("from: " + encode_hex(accounts[keys.index(sender)])) # print("data: " + encode_hex(data)) # print("gas: " + str(gas)) if isContract(transaction): estimated_cost = len(encode_hex(data)) / 2 * 200 print("Adding contract...") print("Estimated gas cost: " + str(estimated_cost)) if gas != None and estimated_cost > gas: print("* ") print("* WARNING: Estimated cost higher than sent gas: " + str(estimated_cost) + " > " + str(gas)) print("* ") r = encode_hex(evm.evm(data, sender, value, gas)) else: r = encode_hex(evm.send(sender, to, value, data, gas)) r = "0x" + r return r
def encode_payments(payments): args = [] value_sum = 0L for idx, v in payments: addr = tester.accounts[idx] value_sum += v v = long(v) assert v < 2**96 vv = zpad(int_to_big_endian(v), 12) mix = vv + addr assert len(mix) == 32 print encode_hex(mix), "v: ", v, "addr", encode_hex(addr) args.append(mix) return args, value_sum
def _apply_msg(ext, msg, code): trace_msg = log_msg.is_active("trace") if trace_msg: log_msg.debug( "MSG APPLY", sender=encode_hex(msg.sender), to=encode_hex(msg.to), gas=msg.gas, value=msg.value, data=encode_hex(msg.data.extract_all()), ) if log_state.is_active("trace"): log_state.trace( "MSG PRE STATE SENDER", account=encode_hex(msg.sender), bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender), ) log_state.trace( "MSG PRE STATE RECIPIENT", account=encode_hex(msg.to), bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to), ) # log_state.trace('CODE', code=code) # Transfer value, instaquit if not enough snapshot = ext._block.snapshot() if msg.transfers_value: if not ext._block.transfer_value(msg.sender, msg.to, msg.value): log_msg.debug("MSG TRANSFER FAILED", have=ext.get_balance(msg.to), want=msg.value) return 1, msg.gas, [] # Main loop if msg.code_address in specials.specials: res, gas, dat = specials.specials[msg.code_address](ext, msg) else: res, gas, dat = vm.vm_execute(ext, msg, code) # gas = int(gas) # assert utils.is_numeric(gas) if trace_msg: log_msg.debug("MSG APPLIED", gas_remained=gas, sender=encode_hex(msg.sender), to=encode_hex(msg.to), data=dat) if log_state.is_active("trace"): log_state.trace( "MSG POST STATE SENDER", account=encode_hex(msg.sender), bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender), ) log_state.trace( "MSG POST STATE RECIPIENT", account=encode_hex(msg.to), bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to), ) if res == 0: log_msg.debug("REVERTING") ext._block.revert(snapshot) return res, gas, dat
def to_dict(self): """Serialize the header to a readable dictionary.""" d = {} for field in ('prevhash', 'uncles_hash', 'extra_data', 'nonce', 'mixhash'): d[field] = b'0x' + encode_hex(getattr(self, field)) for field in ('state_root', 'tx_list_root', 'receipts_root', 'coinbase'): d[field] = encode_hex(getattr(self, field)) for field in ('number', 'difficulty', 'gas_limit', 'gas_used', 'timestamp'): d[field] = to_string(getattr(self, field)) d['bloom'] = encode_hex(int256.serialize(self.bloom)) assert len(d) == len(BlockHeader.fields) return d
def test_encode(name, in_out): msg_format = 'Test {} failed (encoded {} to {} instead of {})' data = in_out['in'] result = utils.encode_hex(encode(data)).upper() expected = in_out['out'].upper() if result != expected: pytest.fail(msg_format.format(name, data, result, expected))
def apply_msg_wrapper(_block, _tx, msg, code): apply_message_calls.append(dict(gasLimit=msg.gas, value=msg.value, desgination=msg.to, data=encode_hex(msg.data))) result, gas_rem, out = orig_apply_msg(_block, _tx, msg, code) return result, gas_rem, out
def eth_getCode(address, block_number="latest"): address = strip_0x(address) block_number = format_block_number(block_number) block = evm.blocks[block_number] return "0x" + encode_hex(block.get_code(address))
def on_receive_getblockhashes(self, proto, child_block_hash, count): log.debug('----------------------------------') log.debug("handle_get_blockhashes", count=count, block_hash=encode_hex(child_block_hash)) max_hashes = min(count, self.wire_protocol.max_getblockhashes_count) found = [] if child_block_hash not in self.chain: log.debug("unknown block") proto.send_blockhashes(*[]) return last = child_block_hash while len(found) < max_hashes: try: last = rlp.decode_lazy(self.chain.db.get(last))[0][0] # [head][prevhash] except KeyError: # this can happen if we started a chain download, which did not complete # should not happen if the hash is part of the canonical chain log.warn('KeyError in getblockhashes', hash=last) break if last: found.append(last) else: break log.debug("sending: found block_hashes", count=len(found)) proto.send_blockhashes(*found)
def run_test(name): logger.debug('testing %s' % name) pairs = load_tests()[name] def _dec(x): if is_string(x) and x.startswith(b'0x'): return decode_hex(x[2:]) return x pairs['in'] = [(_dec(k), _dec(v)) for k, v in pairs['in']] deletes = [(k, v) for k, v in pairs['in'] if v is None] N_PERMUTATIONS = 1000 for i, permut in enumerate(itertools.permutations(pairs['in'])): if i > N_PERMUTATIONS: break t = trie.Trie(db.EphemDB()) for k, v in permut: #logger.debug('updating with (%s, %s)' %(k, v)) if v is not None: t.update(k, v) else: t.delete(k) # make sure we have deletes at the end for k, v in deletes: t.delete(k) assert pairs['root'] == b'0x' + encode_hex(t.root_hash), (i, list(permut) + deletes)
def big_endian_to_int(value): if len(value) == 1: return ord(value) elif len(value) <= 8: return struct.unpack('>Q', value.rjust(8, b'\x00'))[0] else: return int(encode_hex(value), 16)
def test_ecrecover(): s = tester.state() c = s.abi_contract(ecrecover_code) priv = encode_hex(utils.sha3('some big long brainwallet password')) pub = bitcoin.privtopub(priv) msghash = encode_hex(utils.sha3('the quick brown fox jumps over the lazy dog')) V, R, S = bitcoin.ecdsa_raw_sign(msghash, priv) 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(decode_hex(msghash)), V, R, S) assert result == addr
def test_library_from_code(): with open(path.join(CONTRACTS_DIR, 'seven_library.sol')) as handler: library_code = handler.read() with open(path.join(CONTRACTS_DIR, 'seven_contract_without_import.sol')) as handler: contract_code = handler.read() state = tester.state() state.env.config['HOMESTEAD_FORK_BLKNUM'] = 0 # enable CALLCODE opcode library = state.abi_contract( library_code, path=None, language='solidity', ) libraries = { 'SevenLibrary': encode_hex(library.address), } contract = state.abi_contract( contract_code, path=None, libraries=libraries, language='solidity', ) # pylint: disable=no-member assert library.seven() == 7 assert contract.test() == 7
def get_call_data(self, args): """ TODO: this needs tests. """ prefix = self.encoded_abi_signature suffix = self.abi_args_signature(args) return encode_hex(prefix + suffix)
def encode_data(data, length=None): """Encode unformatted binary `data`. If `length` is given, the result will be padded like this: ``quantity_encoder(255, 3) == '0x0000ff'``. """ return add_0x(encode_hex(zpad(data, length or 0)))
def to_dict(self): # TODO: previous version used printers d = {} for name, _ in self.__class__.fields: d[name] = getattr(self, name) d['sender'] = self.sender d['hash'] = encode_hex(self.hash) return d
def bytes2Hex(string) -> str: return '0x' + encode_hex(string)
def dump_state(trie): res = '' for k, v in list(trie.to_dict().items()): res += '%r:%r\n' % (encode_hex(k), encode_hex(v)) return res
args.data = decode_hex(args.data[2:]) amount = Decimal(args.amount) * 10**18 tx = Transaction(nonce=int(args.nonce), gasprice=int(args.gasprice), startgas=int(args.startgas), to=decode_hex(args.to[2:]), value=int(amount), data=args.data) encodedTx = encode(tx, UnsignedTransaction) donglePath = parse_bip32_path(args.path) apdu = "e0040000".decode('hex') + chr(len(donglePath) + 1 + len(encodedTx)) + chr( len(donglePath) / 4) + donglePath + encodedTx dongle = getDongle(True) result = dongle.exchange(bytes(apdu)) v = result[0] r = int(str(result[1:1 + 32]).encode('hex'), 16) s = int(str(result[1 + 32:1 + 32 + 32]).encode('hex'), 16) tx = Transaction(tx.nonce, tx.gasprice, tx.startgas, tx.to, tx.value, tx.data, v, r, s) print "Signed transaction " + encode_hex(encode(tx))
def create_contract(ext, msg): log_msg.debug("CONTRACT CREATION") if msg.is_cross_shard: return 0, msg.gas, b"" 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 = mk_contract_address(msg.sender, msg.to_full_shard_id, 0) # msg.to = sha3(msg.sender + code)[12:] else: nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1) msg.to = mk_contract_address(msg.sender, msg.to_full_shard_id, 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 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') # Initialize stack, memory, program counter, etc compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory # Compute jumpdest_mask, pushcache = preprocess_code(code) codelen = len(code) # For tracing purposes op = None steps = 0 _prevop = None # for trace only while compustate.pc < codelen: opcode = safe_ord(code[compustate.pc]) # Invalid operation if opcode not in opcodes.opcodes: return vm_exception('INVALID OP', opcode=opcode) op, in_args, out_args, fee = opcodes.opcodes[opcode] # out of gas error if fee > compustate.gas: return vm_exception('OUT OF GAS') # empty stack error if in_args > len(compustate.stack): return vm_exception('INSUFFICIENT STACK', op=op, needed=to_string(in_args), available=to_string(len(compustate.stack))) # overfull stack error if len(compustate.stack) - in_args + out_args > 1024: return vm_exception('STACK SIZE LIMIT EXCEEDED', op=op, pre_height=to_string(len(compustate.stack))) # Apply operation compustate.gas -= fee compustate.pc += 1 # Tracing 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 + fee) 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'] = pushcache[compustate.pc - 1] 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: stk.append(pushcache[compustate.pc - 1]) compustate.pc += opcode - 0x5f # Move 1 byte forward for 0x60, up to 32 bytes for 0x7f # Arithmetic 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_spurious_dragon_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) # Comparisons 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) # SHA3 and environment info 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(codelen) 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 < codelen: mem[mstart + i] = 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] = safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 # Block info 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) # VM state manipulations 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 ext.add_refund( refund) # adds neg gascost as a refund if below zero ext.set_storage_data(msg.to, s0, s1) elif op == 'JUMP': compustate.pc = stk.pop() if compustate.pc >= codelen or not ( (1 << compustate.pc) & jumpdest_mask): return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 if compustate.pc >= codelen or not ( (1 << compustate.pc) & jumpdest_mask): 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 # DUPn (eg. DUP1: a b c -> a b c c, DUP3: a b c -> a b c a) elif op[:3] == 'DUP': stk.append( stk[0x7f - opcode] ) # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for 0x8f # SWAPn (eg. SWAP1: a b c d -> a b d c, SWAP3: a b c d -> d b c a) elif op[:4] == 'SWAP': temp = stk[ 0x8e - opcode] # 0x8e - opcode is a negative number, -2 for 0x90 ... -17 for 0x9f stk[0x8e - opcode] = stk[-1] stk[-1] = temp # Logs (aka "events") 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))) # Create a new contract 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) # Calls 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 and op == 'CALL': 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 various factors extra_gas = 0 # Creating a new account if op == 'CALL' and not ext.account_exists(to) and ( value > 0 or not ext.post_spurious_dragon_hardfork()): extra_gas += opcodes.GCALLNEWACCOUNT # Value transfer if value > 0: extra_gas += opcodes.GCALLVALUETRANSFER # Cost increased from 40 to 700 in Tangerine Whistle if ext.post_anti_dos_hardfork(): extra_gas += 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 ext.post_metropolis_hardfork() and op == 'STATICCALL': call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=True) elif op in ('DELEGATECALL', 'STATICCALL'): return vm_exception('OPCODE %s INACTIVE' % op) elif op == 'CALLCODE': call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=msg.static) 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) # Return opcode 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]) # Revert opcode (Metropolis) 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]) # SUICIDE opcode (also called SELFDESTRUCT) 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_spurious_dragon_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 peaceful_exit('SUICIDED', compustate.gas, []) return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])
def __init__(self, genesis=None, env=None, new_head_cb=None, reset_genesis=False, localtime=None, max_history=1000, **kwargs): self.env = env or Env() self.patricia = PatriciaState() self.patricia.from_db() # TODO: test # Initialize the state if 'head_hash' in self.db: # new head tag self.state = self.mk_poststate_of_blockhash(self.db.get('head_hash')) self.state.executing_on_head = True databaseLog.info('Initializing chain from saved head, #%d (%s)',self.state.prev_headers[0].number, encode_hex(self.state.prev_headers[0].hash)) elif genesis is None: raise Exception("Need genesis decl!") elif isinstance(genesis, State): assert env is None self.state = genesis self.env = self.state.env databaseLog.info('Initializing chain from provided state') elif isinstance(genesis, dict): databaseLog.info('Initializing chain from new state based on alloc') diction = {} self.state = state_from_genesis_declaration( genesis, self.env, executing_on_head=True, pytricia=diction) for key in diction: self.patricia.set_value(str(key), str(diction[key])) self.patricia.to_db() reset_genesis = True assert self.env.db == self.state.db initialize(self.state) self.new_head_cb = new_head_cb if self.state.block_number == 0: assert self.state.block_number == self.state.prev_headers[0].number else: assert self.state.block_number == self.state.prev_headers[0].number if reset_genesis: if isinstance(self.state.prev_headers[0], FakeHeader): header = self.state.prev_headers[0].to_block_header() else: header = self.state.prev_headers[0] self.genesis = Block(header) self.state.prev_headers[0] = header initialize_genesis_keys(self.state, self.genesis) else: self.genesis = self.get_block_by_number(0) self.head_hash = self.state.prev_headers[0].hash self.time_queue = [] self.parent_queue = {} self.localtime = time.time() if localtime is None else localtime self.max_history = max_history
def event_topic(self): return b'0x' + encode_hex(utils.sha3(self.signature))
def _apply_msg(ext, msg, code): trace_msg = log_msg.is_active("trace") if trace_msg: log_msg.debug( "MSG APPLY", sender=encode_hex(msg.sender), to=encode_hex(msg.to), gas=msg.gas, value=msg.value, codelen=len(code), data=encode_hex(msg.data.extract_all()) if msg.data.size < 2500 else ("data<%d>" % msg.data.size), pre_storage=ext.log_storage(msg.to), static=msg.static, depth=msg.depth, ) # transfer value, quit if not enough snapshot = ext.snapshot() if msg.transfers_value: if msg.is_cross_shard: if not ext.deduct_value(msg.sender, msg.value): return 1, msg.gas, [] ext.add_cross_shard_transaction_deposit( quarkchain.core.CrossShardTransactionDeposit( tx_hash=msg.tx_hash, from_address=quarkchain.core.Address( msg.sender, msg.from_full_shard_id), to_address=quarkchain.core.Address(msg.to, msg.to_full_shard_id), value=msg.value, gas_price=ext.tx_gasprice, )) elif not ext.transfer_value(msg.sender, msg.to, msg.value): log_msg.debug("MSG TRANSFER FAILED", have=ext.get_balance(msg.to), want=msg.value) return 1, msg.gas, [] if msg.is_cross_shard: # Cross shard contract call is not supported return 1, msg.gas, [] # Main loop if msg.code_address in ext.specials: res, gas, dat = ext.specials[msg.code_address](ext, msg) else: res, gas, dat = vm.vm_execute(ext, msg, code) if trace_msg: log_msg.debug( "MSG APPLIED", gas_remained=gas, sender=encode_hex(msg.sender), to=encode_hex(msg.to), data=dat if len(dat) < 2500 else ("data<%d>" % len(dat)), post_storage=ext.log_storage(msg.to), ) if res == 0: log_msg.debug("REVERTING") ext.revert(snapshot) return res, gas, dat
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 codelen = len(processed_code) op = None steps = 0 _prevop = None # for trace only while 1: # stack size limit error if compustate.pc >= codelen: return peaceful_exit('CODE OUT OF RANGE', compustate.gas, []) op, in_args, out_args, fee, opcode, pushval = \ processed_code[compustate.pc] # out of gas error if fee > compustate.gas: return vm_exception('OUT OF GAS') # empty stack error if in_args > len(compustate.stack): return vm_exception('INSUFFICIENT STACK', op=op, needed=to_string(in_args), available=to_string(len(compustate.stack))) if len(compustate.stack) - in_args + out_args > 1024: return vm_exception('STACK SIZE LIMIT EXCEEDED', op=op, pre_height=to_string(len(compustate.stack))) # Apply operation compustate.gas -= fee compustate.pc += 1 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'] = \ b''.join([encode_hex(ascii_chr(x)) for x in compustate.memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(''.join([ascii_chr(x) for x in compustate.memory]))) if _prevop in ('SSTORE', 'SLOAD') or steps == 0: trace_data['storage'] = ext.log_storage(msg.to) trace_data['gas'] = to_string(compustate.gas + fee) 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['op'] = op trace_data['steps'] = steps if op[:4] == 'PUSH': trace_data['pushvalue'] = pushval log_vm_op.trace('vm', **trace_data) steps += 1 _prevop = op # Invalid operation if op == 'INVALID': return vm_exception('INVALID OP', opcode=opcode) # Valid operations if 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 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 = b''.join(map(ascii_chr, 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': # EIP150: Increase the gas cost of BALANCE to 400 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(processed_code)) elif op == 'CODECOPY': start, s1, size = stk.pop(), stk.pop(), stk.pop() 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 s1 + i < len(processed_code): mem[start + i] = processed_code[s1 + i][4] else: mem[start + i] = 0 elif op == 'GASPRICE': stk.append(ext.tx_gasprice) elif op == 'EXTCODESIZE': # EIP150: Increase the gas cost of EXTCODESIZE to 700 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': # EIP150: Increase the base gas cost of EXTCODECOPY to 700 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': 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') data = b''.join(map(ascii_chr, mem[s0:s0 + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') v = s1 for i in range(31, -1, -1): mem[s0 + i] = v % 256 v //= 256 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': # EIP150: Increase the gas cost of SLOAD to 200 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 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 ext.add_refund( refund) # adds neg gascost as a refund if below zero ext.set_storage_data(msg.to, s0, s1) elif op == 'JUMP': compustate.pc = stk.pop() opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' if opnew != 'JUMPDEST': return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' 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[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc += pushnum stk.append(pushval) elif op[:3] == 'DUP': depth = int(op[3:]) stk.append(stk[-depth]) elif op[:4] == 'SWAP': depth = int(op[4:]) temp = stk[-depth - 1] stk[-depth - 1] = 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 not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, 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 ext.get_balance(msg.to) >= value and msg.depth < 1024: cd = CallData(mem, mstart, msz) ingas = compustate.gas # EIP150(1b) CREATE only provides all but one 64th of the # parent gas to the child call if ext.post_anti_dos_hardfork: ingas = max_call_gas(ingas) 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)) compustate.gas -= (ingas - gas) else: stk.append(0) compustate.gas -= ingas else: stk.append(0) elif op == 'CALL': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() 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.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \ (value > 0) * opcodes.GCALLVALUETRANSFER + \ ext.post_anti_dos_hardfork * opcodes.CALL_SUPPLEMENTAL_GAS # ^ EIP150 Increase the gas cost of CALL to 700 if ext.post_anti_dos_hardfork: # EIP150(1b) if a call asks for more gas than all but one 64th of # the maximum allowed amount, call with all but one 64th of the # maximum allowed amount of gas if compustate.gas < extra_gas: return vm_exception('OUT OF GAS', needed=extra_gas) gas = min(gas, max_call_gas(compustate.gas - extra_gas)) else: if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == 'CALLCODE' or op == 'DELEGATECALL': if op == '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 if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER + \ ext.post_anti_dos_hardfork * opcodes.CALL_SUPPLEMENTAL_GAS # ^ EIP150 Increase the gas cost of CALLCODE, DELEGATECALL to 700 if ext.post_anti_dos_hardfork: # EIP150(1b) if a call asks for more gas than all but one 64th of # the maximum allowed amount, call with all but one 64th of the # maximum allowed amount of gas if compustate.gas < extra_gas: return vm_exception('OUT OF GAS', needed=extra_gas) gas = min(gas, max_call_gas(compustate.gas - extra_gas)) else: if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] cd = CallData(mem, meminstart, meminsz) if 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) elif op == 'DELEGATECALL': return vm_exception('OPCODE INACTIVE') else: call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) 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 == 'SUICIDE': to = utils.encode_int(stk.pop()) to = ((b'\x00' * (32 - len(to))) + to)[12:] if ext.post_anti_dos_hardfork: # EIP150 Increase the gas cost of SUICIDE to 5000 extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \ (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT # ^ EIP150(1c) If SUICIDE hits a newly created account, it # triggers an additional gas cost of 25000 (similar to CALLs) if not eat_gas(compustate, extra_gas): return vm_exception("OUT OF GAS") xfer = ext.get_balance(msg.to) ext.set_balance(to, ext.get_balance(to) + xfer) ext.set_balance(msg.to, 0) ext.add_suicide(msg.to) # print('suiciding %s %s %d' % (msg.to, to, xfer)) return 1, compustate.gas, []
def __repr__(self): return '<Log(address=%r, topics=%r, data=%r)>' % \ (encode_hex(self.address), self.topics, self.data)
def test_abi_signature_single_arg(): func = Function("register", [{'type': 'bytes32', 'name': 'name'}]) assert encode_hex(func.encoded_abi_signature) == b"e1fa8e84"
def valueconv(k, v): if k in ['r', 's']: return '0x' + encode_hex(utils.int_to_big_endian(v)) return v
def run_state_test(params, mode): pre = params['pre'] exek = params['transaction'] env = params['env'] assert set(env.keys()) == set([ 'currentGasLimit', 'currentTimestamp', 'previousHash', 'currentCoinbase', 'currentDifficulty', 'currentNumber' ]) assert len(env['currentCoinbase']) == 40 # setup env header = blocks.BlockHeader( prevhash=decode_hex(env['previousHash']), number=parse_int_or_hex(env['currentNumber']), coinbase=decode_hex(env['currentCoinbase']), difficulty=parse_int_or_hex(env['currentDifficulty']), timestamp=parse_int_or_hex(env['currentTimestamp']), # work around https://github.com/ethereum/pyethereum/issues/390 [1]: gas_limit=min(db_env.config['MAX_GAS_LIMIT'], parse_int_or_hex(env['currentGasLimit']))) blk = blocks.Block(header, env=db_env) # work around https://github.com/ethereum/pyethereum/issues/390 [2]: blk.gas_limit = parse_int_or_hex(env['currentGasLimit']) # setup state for address, h in list(pre.items()): assert len(address) == 40 address = decode_hex(address) assert set(h.keys()) == set(['code', 'nonce', 'balance', 'storage']) blk.set_nonce(address, parse_int_or_hex(h['nonce'])) blk.set_balance(address, parse_int_or_hex(h['balance'])) blk.set_code(address, decode_hex(h['code'][2:])) for k, v in h['storage'].items(): blk.set_storage_data(address, utils.big_endian_to_int(decode_hex(k[2:])), utils.big_endian_to_int(decode_hex(v[2:]))) for address, h in list(pre.items()): address = decode_hex(address) assert blk.get_nonce(address) == parse_int_or_hex(h['nonce']) assert blk.get_balance(address) == parse_int_or_hex(h['balance']) assert blk.get_code(address) == decode_hex(h['code'][2:]) for k, v in h['storage'].items(): assert blk.get_storage_data( address, utils.big_endian_to_int(decode_hex( k[2:]))) == utils.big_endian_to_int(decode_hex(v[2:])) # execute transactions orig_apply_msg = pb.apply_msg def apply_msg_wrapper(ext, msg): def blkhash(n): if n >= blk.number or n < blk.number - 256: return b'' else: return utils.sha3(to_string(n)) ext.block_hash = blkhash return orig_apply_msg(ext, msg) pb.apply_msg = apply_msg_wrapper try: tx = transactions.Transaction( nonce=parse_int_or_hex(exek['nonce'] or b"0"), gasprice=parse_int_or_hex(exek['gasPrice'] or b"0"), startgas=parse_int_or_hex(exek['gasLimit'] or b"0"), to=normalize_address(exek['to'], allow_blank=True), value=parse_int_or_hex(exek['value'] or b"0"), data=decode_hex(remove_0x_head(exek['data']))) except InvalidTransaction: tx = None success, output = False, b'' time_pre = time.time() time_post = time_pre else: if 'secretKey' in exek: tx.sign(exek['secretKey']) elif all(key in exek for key in ['v', 'r', 's']): tx.v = decode_hex(remove_0x_head(exek['v'])) tx.r = decode_hex(remove_0x_head(exek['r'])) tx.s = decode_hex(remove_0x_head(exek['s'])) else: assert False time_pre = time.time() try: print('trying') success, output = pb.apply_transaction(blk, tx) blk.commit_state() print('success', blk.get_receipts()[-1].gas_used) except InvalidTransaction: success, output = False, b'' blk.commit_state() pass time_post = time.time() if tx.to == b'': output = blk.get_code(output) pb.apply_msg = orig_apply_msg params2 = copy.deepcopy(params) if success: params2['logs'] = [log.to_dict() for log in blk.get_receipt(0).logs] params2['out'] = b'0x' + encode_hex(output) params2['post'] = copy.deepcopy(blk.to_dict(True)['state']) params2['postStateRoot'] = encode_hex(blk.state.root_hash) if mode == FILL: return params2 elif mode == VERIFY: params1 = copy.deepcopy(params) shouldbe, reallyis = params1.get('post', None), params2.get('post', None) compare_post_states(shouldbe, reallyis) for k in [ 'pre', 'exec', 'env', 'callcreates', 'out', 'gas', 'logs', 'postStateRoot' ]: _shouldbe = params1.get(k, None) _reallyis = params2.get(k, None) if str_to_bytes(k) == b'out' and _shouldbe[:1] in ('#', b'#'): _reallyis = str_to_bytes('#%s' % ((len(_reallyis) - 2) // 2)) if _shouldbe != _reallyis: print(( 'Mismatch {key}: shouldbe {shouldbe_key} != reallyis {reallyis_key}.\n' 'post: {shouldbe_post} != {reallyis_post}').format( shouldbe_key=_shouldbe, reallyis_key=_reallyis, shouldbe_post=shouldbe, reallyis_post=reallyis, key=k)) raise Exception("Mismatch: " + k + ':\n shouldbe %r\n reallyis %r' % (_shouldbe, _reallyis)) elif mode == TIME: return time_post - time_pre
def int_to_32bytearray(i): o = [0] * 32 for x in range(32): o[31 - x] = i & 0xff i >>= 8 return o # sha3_count = [0] def sha3(seed): return sha3_256(to_string(seed)) assert encode_hex( sha3(b'')) == 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' def privtoaddr(k): k = normalize_key(k) x, y = privtopub(k) return sha3(encode_int32(x) + encode_int32(y))[12:] def checksum_encode(addr): # Takes a 20-byte binary address as input addr = normalize_address(addr) o = '' v = big_endian_to_int(sha3(encode_hex(addr))) for i, c in enumerate(encode_hex(addr)): if c in '0123456789': o += c
def run_block_test(params, config_overrides={}): b = blocks.genesis(env, start_alloc=params["pre"]) gbh = params["genesisBlockHeader"] b.bloom = utils.scanners['int256b'](gbh["bloom"]) b.timestamp = utils.scanners['int'](gbh["timestamp"]) b.nonce = utils.scanners['bin'](gbh["nonce"]) b.extra_data = utils.scanners['bin'](gbh["extraData"]) b.gas_limit = utils.scanners['int'](gbh["gasLimit"]) b.gas_used = utils.scanners['int'](gbh["gasUsed"]) b.coinbase = utils.scanners['addr'](decode_hex(gbh["coinbase"])) b.difficulty = utils.parse_int_or_hex(gbh["difficulty"]) b.prevhash = utils.scanners['bin'](gbh["parentHash"]) b.mixhash = utils.scanners['bin'](gbh["mixHash"]) assert b.receipts.root_hash == \ utils.scanners['bin'](gbh["receiptTrie"]) assert b.transactions.root_hash == \ utils.scanners['bin'](gbh["transactionsTrie"]) assert utils.sha3rlp(b.uncles) == \ utils.scanners['bin'](gbh["uncleHash"]) h = encode_hex(b.state.root_hash) if h != str_to_bytes(gbh["stateRoot"]): raise Exception("state root mismatch") if b.hash != utils.scanners['bin'](gbh["hash"]): raise Exception("header hash mismatch") # assert b.header.check_pow() blockmap = {b.hash: b} env.db.put(b.hash, rlp.encode(b)) old_config = copy.deepcopy(env.config) for k, v in config_overrides.items(): env.config[k] = v b2 = None for blk in params["blocks"]: if 'blockHeader' not in blk: try: rlpdata = decode_hex(blk["rlp"][2:]) blkparent = rlp.decode( rlp.encode(rlp.decode(rlpdata)[0]), blocks.BlockHeader).prevhash b2 = rlp.decode(rlpdata, blocks.Block, parent=blockmap[blkparent], env=env) success = b2.validate_uncles() except (ValueError, TypeError, AttributeError, VerificationFailed, DecodingError, DeserializationError, InvalidTransaction, KeyError): success = False assert not success else: rlpdata = decode_hex(blk["rlp"][2:]) blkparent = rlp.decode(rlp.encode(rlp.decode(rlpdata)[0]), blocks.BlockHeader).prevhash b2 = rlp.decode(rlpdata, blocks.Block, parent=blockmap[blkparent], env=env) assert b2.validate_uncles() blockmap[b2.hash] = b2 env.db.put(b2.hash, rlp.encode(b2)) if b2: print('Block %d with state root %s' % (b2.number, encode_hex(b2.state.root_hash))) # blkdict = b.to_dict(False, True, False, True) # assert blk["blockHeader"] == \ # translate_keys(blkdict["header"], translator_list, lambda y, x: x, []) # assert blk["transactions"] == \ # [translate_keys(t, translator_list, valueconv, ['hash']) # for t in blkdict["transactions"]] # assert blk["uncleHeader"] == \ # [translate_keys(u, translator_list, lambda x: x, []) # for u in blkdict["uncles"]] env.config = old_config
import rlp from rlp.utils import decode_hex, encode_hex, str_to_bytes import ethereum.testutils as testutils from ethereum.testutils import fixture_to_bytes import sys import json import os from ethereum.slogging import get_logger, configure_logging logger = get_logger() # customize VM log output to your needs # hint: use 'py.test' with the '-s' option to dump logs to the console # configure_logging(':trace') encode_hex('') def run_test(filename, testname, testdata): testdata = fixture_to_bytes(testdata) try: rlpdata = decode_hex(testdata["rlp"][2:]) o = {} tx = rlp.decode(rlpdata, transactions.Transaction) o["sender"] = tx.sender o["transaction"] = { "data": b'0x' * (len(tx.data) > 0) + encode_hex(tx.data), "gasLimit": str_to_bytes(str(tx.startgas)), "gasPrice": str_to_bytes(str(tx.gasprice)), "nonce": str_to_bytes(str(tx.nonce)),
import rlp from rlp.utils import decode_hex, encode_hex, str_to_bytes import ethereum.testutils as testutils from ethereum.testutils import fixture_to_bytes import ethereum.config as config import sys import json from ethereum.slogging import get_logger logger = get_logger() # customize VM log output to your needs # hint: use 'py.test' with the '-s' option to dump logs to the console # configure_logging(':trace') encode_hex('') def test_transaction(filename, testname, testdata): testdata = fixture_to_bytes(testdata) try: rlpdata = decode_hex(testdata["rlp"][2:]) o = {} tx = rlp.decode(rlpdata, transactions.Transaction) blknum = int(testdata["blocknumber"]) if blknum >= config.default_config["HOMESTEAD_FORK_BLKNUM"]: tx.check_low_s() o["sender"] = tx.sender o["transaction"] = { "data": b'0x' * (len(tx.data) > 0) + encode_hex(tx.data),
def encode(data): from rlp.utils import encode_hex return encode_hex(json.dumps(data))
def to_hex(value): return '0x' + encode_hex(value)
"addr": decode_addr, "int": decode_int, "int256b": decode_int256, } # Encoding to RLP serialization encoders = { "bin": encode_bin, "int": encode_int, "trie_root": encode_root, "int256b": encode_int256, } # Encoding to printable format printers = { "bin": lambda v: b'0x' + encode_hex(v), "addr": lambda v: v, "int": lambda v: to_string(v), "trie_root": lambda v: encode_hex(v), "int256b": lambda x: encode_hex(zpad(encode_int256(x), 256)) } # Decoding from printable format scanners = { "bin": scan_bin, "addr": lambda x: x[2:] if x[:2] == b'0x' else x, "int": scan_int, "trie_root": lambda x: scan_bin, "int256b": lambda x: big_endian_to_int(decode_hex(x)) }
def test_genesis_state_root(genesis_fixture): genesis = blocks.genesis(new_env()) assert encode_hex(genesis.state_root) == utils.to_string( genesis_fixture['genesis_state_root'])
def decode_addr(v): '''decodes an address from serialization''' if len(v) not in [0, 20]: raise Exception("Serialized addresses must be empty or 20 bytes long!") return encode_hex(v)
def run_vm_test(params, mode, profiler=None): pre = params['pre'] exek = params['exec'] env = params['env'] if 'previousHash' not in env: env['previousHash'] = encode_hex(db_env.config['GENESIS_PREVHASH']) assert set(env.keys()) == set([ 'currentGasLimit', 'currentTimestamp', 'previousHash', 'currentCoinbase', 'currentDifficulty', 'currentNumber' ]) # setup env header = blocks.BlockHeader( prevhash=decode_hex(env['previousHash']), number=parse_int_or_hex(env['currentNumber']), coinbase=decode_hex(env['currentCoinbase']), difficulty=parse_int_or_hex(env['currentDifficulty']), gas_limit=parse_int_or_hex(env['currentGasLimit']), timestamp=parse_int_or_hex(env['currentTimestamp'])) blk = blocks.Block(header, env=db_env) # setup state for address, h in list(pre.items()): address = eliminate0x(address) assert len(address) == 40 address = decode_hex(address) assert set(h.keys()) == set(['code', 'nonce', 'balance', 'storage']) blk.set_nonce(address, parse_int_or_hex(h['nonce'])) blk.set_balance(address, parse_int_or_hex(h['balance'])) blk.set_code(address, decode_hex(h['code'][2:])) for k, v in h['storage'].items(): blk.set_storage_data(address, utils.big_endian_to_int(decode_hex(k[2:])), utils.big_endian_to_int(decode_hex(v[2:]))) # execute transactions sender = decode_hex(exek['caller']) # a party that originates a call recvaddr = decode_hex(exek['address']) nonce = blk._get_acct_item(sender, 'nonce') gasprice = parse_int_or_hex(exek['gasPrice']) startgas = parse_int_or_hex(exek['gas']) value = parse_int_or_hex(exek['value']) data = decode_hex(exek['data'][2:]) # bypass gas check in tx initialization by temporarily increasing startgas num_zero_bytes = str_to_bytes(data).count(ascii_chr(0)) num_non_zero_bytes = len(data) - num_zero_bytes intrinsic_gas = (opcodes.GTXCOST + opcodes.GTXDATAZERO * num_zero_bytes + opcodes.GTXDATANONZERO * num_non_zero_bytes) startgas += intrinsic_gas tx = transactions.Transaction(nonce=nonce, gasprice=gasprice, startgas=startgas, to=recvaddr, value=value, data=data) tx.startgas -= intrinsic_gas tx.sender = sender # capture apply_message calls apply_message_calls = [] orig_apply_msg = pb.apply_msg ext = pb.VMExt(blk, tx) def msg_wrapper(msg): hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append( dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=encode_hex(msg.to), data=b'0x' + hexdata)) return 1, msg.gas, b'' def create_wrapper(msg): sender = decode_hex(msg.sender) if \ len(msg.sender) == 40 else msg.sender nonce = utils.encode_int(ext._block.get_nonce(msg.sender)) addr = utils.sha3(rlp.encode([sender, nonce]))[12:] hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append( dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=b'', data=b'0x' + hexdata)) return 1, msg.gas, addr ext.msg = msg_wrapper ext.create = create_wrapper def blkhash(n): if n >= ext.block_number or n < ext.block_number - 256: return b'' else: return utils.sha3(to_string(n)) ext.block_hash = blkhash msg = vm.Message(tx.sender, tx.to, tx.value, tx.startgas, vm.CallData([safe_ord(x) for x in tx.data])) code = decode_hex(exek['code'][2:]) time_pre = time.time() if profiler: profiler.enable() success, gas_remained, output = vm.vm_execute(ext, msg, code) if profiler: profiler.disable() pb.apply_msg = orig_apply_msg blk.commit_state() for s in blk.suicides: blk.del_account(s) time_post = time.time() """ generally expected that the test implementer will read env, exec and pre then check their results against gas, logs, out, post and callcreates. If an exception is expected, then latter sections are absent in the test. Since the reverting of the state is not part of the VM tests. """ params2 = copy.deepcopy(params) if success: params2['callcreates'] = apply_message_calls params2['out'] = b'0x' + encode_hex(b''.join(map(ascii_chr, output))) params2['gas'] = to_string(gas_remained) params2['logs'] = [log.to_dict() for log in blk.logs] params2['post'] = blk.to_dict(with_state=True)['state'] if mode == FILL: return params2 elif mode == VERIFY: if not success: assert 'post' not in params, 'failed, but expected to succeed' params1 = copy.deepcopy(params) shouldbe, reallyis = params1.get('post', None), params2.get('post', None) compare_post_states(shouldbe, reallyis) def normalize_value(k, p): if k in p: if k == 'gas': return parse_int_or_hex(p[k]) elif k == 'callcreates': return list(map(callcreate_standard_form, p[k])) else: return utils.to_string(k) return None for k in ['pre', 'exec', 'env', 'callcreates', 'out', 'gas', 'logs']: shouldbe = normalize_value(k, params1) reallyis = normalize_value(k, params2) if shouldbe != reallyis: raise Exception("Mismatch: " + k + ':\n shouldbe %r\n reallyis %r' % (shouldbe, reallyis)) elif mode == TIME: return time_post - time_pre
def int_to_hex(x): o = encode_hex(encode_int(x)) return '0x' + (o[1:] if (len(o) > 0 and o[0] == b'0') else o)
def add_block(self, block): now = self.localtime # Are we receiving the block too early? if block.header.timestamp > now: i = 0 while i < len(self.time_queue ) and block.timestamp > self.time_queue[i].timestamp: i += 1 self.time_queue.insert(i, block) log.info( 'Block received too early (%d vs %d). Delaying for %d seconds' % (now, block.header.timestamp, block.header.timestamp - now)) return False # Is the block being added to the head? if block.header.prevhash == self.head_hash: log.info('Adding to head', head=encode_hex(block.header.prevhash[:4])) self.state.deletes = [] self.state.changed = {} try: apply_block(self.state, block) except (AssertionError, KeyError, ValueError, InvalidTransaction, VerificationFailed) as e: log.info('Block %d (%s) with parent %s invalid, reason: %s' % (block.number, encode_hex(block.header.hash[:4]), encode_hex(block.header.prevhash[:4]), str(e))) return False self.db.put(b'block:%d' % block.header.number, block.header.hash) # side effect: put 'score:' cache in db block_score = self.get_score(block) self.head_hash = block.header.hash for i, tx in enumerate(block.transactions): self.db.put(b'txindex:' + tx.hash, rlp.encode([block.number, i])) assert self.get_blockhash_by_number( block.header.number) == block.header.hash deletes = self.state.deletes changed = self.state.changed # Or is the block being added to a chain that is not currently the # head? elif block.header.prevhash in self.env.db: log.info( 'Receiving block %d (%s) not on head (%s), adding to secondary post state %s' % (block.number, encode_hex( block.header.hash[:4]), encode_hex(self.head_hash[:4]), encode_hex(block.header.prevhash[:4]))) temp_state = self.mk_poststate_of_blockhash(block.header.prevhash) try: apply_block(temp_state, block) except (AssertionError, KeyError, ValueError, InvalidTransaction, VerificationFailed) as e: log.info('Block %s with parent %s invalid, reason: %s' % (encode_hex(block.header.hash[:4]), encode_hex(block.header.prevhash[:4]), str(e))) return False deletes = temp_state.deletes block_score = self.get_score(block) changed = temp_state.changed # If the block should be the new head, replace the head if block_score > self.get_score(self.head): b = block new_chain = {} # Find common ancestor while b.header.number >= int(self.db.get(b'GENESIS_NUMBER')): new_chain[b.header.number] = b key = b'block:%d' % b.header.number orig_at_height = self.db.get( key) if key in self.db else None if orig_at_height == b.header.hash: break if b.prevhash not in self.db or self.db.get( b.prevhash) == b'GENESIS': break b = self.get_parent(b) replace_from = b.header.number # Replace block index and tx indices, and edit the state cache # Get a list of all accounts that have been edited along the old and # new chains changed_accts = {} # Read: for i in range(common ancestor block number...new block # number) for i in itertools.count(replace_from): log.info('Rewriting height %d' % i) key = b'block:%d' % i # Delete data for old blocks orig_at_height = self.db.get( key) if key in self.db else None if orig_at_height: orig_block_at_height = self.get_block(orig_at_height) log.info('%s no longer in main chain' % encode_hex(orig_block_at_height.header.hash)) # Delete from block index self.db.delete(key) # Delete from txindex for tx in orig_block_at_height.transactions: if b'txindex:' + tx.hash in self.db: self.db.delete(b'txindex:' + tx.hash) # Add to changed list acct_list = self.db.get(b'changed:' + orig_block_at_height.hash) for j in range(0, len(acct_list), 20): changed_accts[acct_list[j:j + 20]] = True # Add data for new blocks if i in new_chain: new_block_at_height = new_chain[i] log.info('%s now in main chain' % encode_hex(new_block_at_height.header.hash)) # Add to block index self.db.put(key, new_block_at_height.header.hash) # Add to txindex for j, tx in enumerate( new_block_at_height.transactions): self.db.put( b'txindex:' + tx.hash, rlp.encode([new_block_at_height.number, j])) # Add to changed list if i < b.number: acct_list = self.db.get(b'changed:' + new_block_at_height.hash) for j in range(0, len(acct_list), 20): changed_accts[acct_list[j:j + 20]] = True if i not in new_chain and not orig_at_height: break # Add changed list from new head to changed list for c in changed.keys(): changed_accts[c] = True # Update the on-disk state cache for addr in changed_accts.keys(): data = temp_state.trie.get(addr) if data: self.state.db.put(b'address:' + addr, data) else: try: self.state.db.delete(b'address:' + addr) except KeyError: pass self.head_hash = block.header.hash self.state = temp_state self.state.executing_on_head = True # Block has no parent yet else: if block.header.prevhash not in self.parent_queue: self.parent_queue[block.header.prevhash] = [] self.parent_queue[block.header.prevhash].append(block) log.info( 'Got block %d (%s) with prevhash %s, parent not found. Delaying for now' % (block.number, encode_hex( block.hash[:4]), encode_hex(block.prevhash[:4]))) return False self.add_child(block) self.db.put(b'head_hash', self.head_hash) self.db.put(block.hash, rlp.encode(block)) self.db.put( b'changed:' + block.hash, b''.join([ k.encode() if not is_string(k) else k for k in list(changed.keys()) ])) print('Saved %d address change logs' % len(changed.keys())) self.db.put(b'deletes:' + block.hash, b''.join(deletes)) log.debug('Saved %d trie node deletes for block %d (%s)' % (len(deletes), block.number, utils.encode_hex(block.hash))) # Delete old junk data old_block_hash = self.get_blockhash_by_number(block.number - self.max_history) if old_block_hash: try: deletes = self.db.get(b'deletes:' + old_block_hash) log.debug('Deleting up to %d trie nodes' % (len(deletes) // 32)) rdb = RefcountDB(self.db) for i in range(0, len(deletes), 32): rdb.delete(deletes[i:i + 32]) self.db.delete(b'deletes:' + old_block_hash) self.db.delete(b'changed:' + old_block_hash) except KeyError as e: print(e) pass self.db.commit() assert (b'deletes:' + block.hash) in self.db log.info('Added block %d (%s) with %d txs and %d gas' % (block.header.number, encode_hex(block.header.hash)[:8], len(block.transactions), block.header.gas_used)) # Call optional callback if self.new_head_cb and block.header.number != 0: self.new_head_cb(block) # Are there blocks that we received that were waiting for this block? # If so, process them. if block.header.hash in self.parent_queue: for _blk in self.parent_queue[block.header.hash]: self.add_block(_blk) del self.parent_queue[block.header.hash] return True
def __init__(self, genesis=None, env=None, new_head_cb=None, reset_genesis=False, localtime=None, max_history=1000, **kwargs): self.env = env or Env() # Initialize the state if b'head_hash' in self.db: # new head tag self.state = self.mk_poststate_of_blockhash( self.db.get('head_hash')) self.state.executing_on_head = True print('Initializing chain from saved head, #%d (%s)' % (self.state.prev_headers[0].number, encode_hex(self.state.prev_headers[0].hash))) elif genesis is None: raise Exception("Need genesis decl!") elif isinstance(genesis, State): assert env is None self.state = genesis self.env = self.state.env print('Initializing chain from provided state') reset_genesis = True elif "extraData" in genesis: self.state = state_from_genesis_declaration(genesis, self.env, executing_on_head=True) reset_genesis = True print('Initializing chain from provided genesis declaration') elif "prev_headers" in genesis: self.state = State.from_snapshot(genesis, self.env, executing_on_head=True) reset_genesis = True print('Initializing chain from provided state snapshot, %d (%s)' % (self.state.block_number, encode_hex(self.state.prev_headers[0].hash[:8]))) elif isinstance(genesis, dict): print('Initializing chain from new state based on alloc') self.state = mk_basic_state( genesis, { "number": kwargs.get('number', 0), "gas_limit": kwargs.get('gas_limit', self.env.config['BLOCK_GAS_LIMIT']), "gas_used": kwargs.get('gas_used', 0), "timestamp": kwargs.get('timestamp', 1467446877), "difficulty": kwargs.get('difficulty', 2**25), "hash": kwargs.get('prevhash', '00' * 32), "uncles_hash": kwargs.get('uncles_hash', '0x' + encode_hex(BLANK_UNCLES_HASH)) }, self.env) reset_genesis = True assert self.env.db == self.state.db initialize(self.state) self.new_head_cb = new_head_cb if self.state.block_number == 0: assert self.state.block_number == self.state.prev_headers[0].number else: assert self.state.block_number - 1 == self.state.prev_headers[ 0].number if reset_genesis: if isinstance(self.state.prev_headers[0], FakeHeader): header = self.state.prev_headers[0].to_block_header() else: header = self.state.prev_headers[0] self.genesis = Block(header) self.state.prev_headers[0] = header initialize_genesis_keys(self.state, self.genesis) else: self.genesis = self.get_block_by_number(0) self.head_hash = self.state.prev_headers[0].hash self.time_queue = [] self.parent_queue = {} self.localtime = time.time() if localtime is None else localtime self.max_history = max_history
def test_abi_signature_no_args(): func = Function("doit", []) assert encode_hex(func.encoded_abi_signature) == b"4d536fe3"
def encode_node(nd): if is_string(nd): return encode_hex(nd) else: return encode_hex(rlp_encode(nd))
def eth_coinbase(): print 'eth_coinbase' return '0x' + encode_hex(evm.block.coinbase)
def __str__(self): if not isinstance(self.data, (str, unicode)): return repr(self.data) else: return encode_hex(self.data)