diff --git a/blocks.py b/blocks.py index 043819a..d863601 100644 --- a/blocks.py +++ b/blocks.py @@ -1,4 +1,5 @@ from genesis_state import genesis_state +from evm_transition import initial_state from config import SHARD_IDS from config import VALIDITY_CHECKS_OFF @@ -85,7 +86,7 @@ class SwitchMessage_ChangeParent(Message): def __init__(self, base, TTL, target_shard_ID, new_parent_ID): super(SwitchMessage_ChangeParent, self).__init__(base, TTL, target_shard_ID, None) self.new_parent_ID = new_parent_ID - self.hash = rand.randint(1, 1000000) + self.hash = rand.randint(1, 1000000) def __hash__(self): return self.hash @@ -94,7 +95,7 @@ def __eq__(self, message): return self.hash == message.hash class Block: - def __init__(self, ID, prevblock=None, switch_block=False, txn_log=[], sent_log={}, received_log={}, sources={}, parent_ID=None, child_IDs=None, routing_table=None, vm_state=genesis_state): + def __init__(self, ID, prevblock=None, switch_block=False, txn_log=[], sent_log={}, received_log={}, sources={}, parent_ID=None, child_IDs=None, routing_table=None, vm_state=initial_state): if sent_log == {}: for i in SHARD_IDS: diff --git a/evm_transition.py b/evm_transition.py index 53c20fc..e66ac22 100644 --- a/evm_transition.py +++ b/evm_transition.py @@ -4,118 +4,150 @@ import os import subprocess import sys +import copy + +from eth_keys import keys +from eth_utils import decode_hex, encode_hex, to_wei +from eth_typing import Address +from eth import constants +from eth.rlp.logs import Log +from eth.rlp.receipts import Receipt +from eth.db.atomic import AtomicDB +from eth.vm.forks.byzantium import ByzantiumVM +from eth.vm.forks.byzantium.transactions import ByzantiumTransaction +from eth.chains.base import MiningChain +import rlp from blocks import * from web3 import Web3 from genesis_state import * -from config import DEADBEEF +from config import DEADBEEF, SHARD_IDS from generate_transactions import format_transaction abi = json.loads('[{"constant":false,"inputs":[{"name":"_shard_ID","type":"uint256"},{"name":"_sendGas","type":"uint256"},{"name":"_sendToAddress","type":"address"},{"name":"_data","type":"bytes"}],"name":"send","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"shard_ID","type":"uint256"},{"indexed":false,"name":"sendGas","type":"uint256"},{"indexed":false,"name":"sendFromAddress","type":"address"},{"indexed":true,"name":"sendToAddress","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"data","type":"bytes"},{"indexed":true,"name":"base","type":"uint256"},{"indexed":false,"name":"TTL","type":"uint256"}],"name":"SentMessage","type":"event"}]') -evm_path = './evm-ubuntu' -if (sys.platform == 'darwin'): - evm_path = './evm-macos' - +web3 = Web3() contract = web3.eth.contract(address='0x000000000000000000000000000000000000002A', abi=abi) +klass = MiningChain.configure( __name__='TestChain', vm_configuration=( (constants.GENESIS_BLOCK_NUMBER, ByzantiumVM), ) ) +chain = klass.from_genesis(AtomicDB(), genesis_params, genesis_state) +initial_state = chain.get_vm().state +alice_key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' +alice_address = web3.eth.account.privateKeyToAccount(alice_key).address.lower() +alice_nonces = [] -def convert_state_to_pre(state): - ''' The evm output isn't quite how we want it ''' - pre = {} - for key, value in state["state"]["accounts"].items(): - # print(value) - pre[key] = value - return pre +def make_byzantium_txs(txs, alice_nonce): + byzantium_txs = [] + for tx in txs: + tx = copy.copy(tx) + print(tx.keys()) + if 'gasPrice' in tx.keys(): + tx['gas_price'] = tx['gasPrice'] + tx.pop('gasPrice') + if 'input' in tx.keys(): + tx['data'] = tx['input'] + tx.pop('input') + if 'hash' in tx.keys(): + tx.pop('hash') + tx_fields = ['nonce', 'gas_price', 'gas', 'value', 'v', 'r', 's'] + for field in tx_fields: + if field in tx.keys(): + if isinstance(tx[field], str): + tx[field] = int(tx[field], 16) + if isinstance(tx['to'], str): + tx['to'] = decode_hex(tx['to']) + if isinstance(tx['data'], str): + tx['data'] = decode_hex(tx['data']) + print(tx) + try: + byzantium_tx = ByzantiumTransaction(**tx) + if encode_hex(byzantium_tx.sender) == alice_address: + tx['nonce'] = alice_nonce + alice_nonce += 1 + tx.pop('v') + tx.pop('r') + tx.pop('s') + unsigned_tx = ByzantiumTransaction.create_unsigned_transaction(**tx) + byzantium_tx = unsigned_tx.as_signed_transaction(keys.PrivateKey(decode_hex(alice_key))) + byzantium_txs.append(byzantium_tx) + except TypeError: + print(tx) + assert False, "That tx" + pass + return byzantium_txs -# NOTES: from convo with steve -# The “vm state” is really the “pre” part of what we send to evm. -# The “env” stuff is constant # the “transactions” list is a list of transactions that come from the # mempool (originally a file full of test data?) and ones that are constructed from # `MessagePayload`s. (This is done via `web3.eth.account.signTransaction(…)`.) -# function apply(vm_state, [tx], mapping(S => received)) -> (vm_state, mapping(S => received) ) -def apply_to_state(pre_state, tx, received_log, genesis_blocks): - # print(pre_state["pre"][address]["nonce"]) - nonce = int(pre_state["pre"][pusher_address]["nonce"], 0) +# function apply(vm_state, [txs], mapping(S => received)) -> (vm_state, mapping(S => received) ) +def apply_to_state(pre_state, txs, received_log, genesis_blocks): + alice_nonce = pre_state.account_db.get_nonce(decode_hex(alice_address)) + txs = make_byzantium_txs(txs, alice_nonce) + nonce = pre_state.account_db.get_nonce(decode_hex(pusher_address)) + flattened_payloads = [message.payload for l in received_log.values() for message in l] for payload in flattened_payloads: - transaction = { + unsigned_tx = { "gas": 3000000, - "gasPrice": "0x2", - "nonce": hex(nonce), + "gas_price": int("0x2", 16), + "nonce": nonce, "to": payload.toAddress, "value": payload.value, "data": payload.data, } + unsigned_tx = ByzantiumTransaction.create_unsigned_transaction(**unsigned_tx) + tx = unsigned_tx.as_signed_transaction(keys.PrivateKey(decode_hex(pusher_key))) nonce += 1 - signed = web3.eth.account.signTransaction(transaction, pusher_key) - tx.append(format_transaction(transaction, signed)) + txs.append(tx) - # create inputst evm by combining the pre_state, env, and transactions - transition_inputs = {} - transition_inputs["pre"] = pre_state["pre"] - transition_inputs["env"] = pre_state["env"] - transition_inputs["transactions"] = tx + state_roots = [] + computations = [] + all_logs = [] + receipts = [] - # open evm - evm = subprocess.Popen([evm_path, 'apply', '/dev/stdin'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + for tx in txs: + if encode_hex(tx.sender)==alice_address: + assert tx.nonce not in alice_nonces, "Nonce {} has occured before".format(tx.nonce) + alice_nonces.append(tx.nonce) + state_root, computation = pre_state.apply_transaction(tx) + state_roots.append(state_root) + computations.append(computation) - out = evm.communicate(json.dumps(transition_inputs).encode())[0].decode('utf-8') - # print("out2", out) + logs = [ + Log(address, topics, data) + for address, topics, data + in computation.get_log_entries() + ] + all_logs.append(logs) - result = json.loads(out) - new_state = { - "env": pre_state["env"], - "pre": result["state"]["accounts"].copy(), - } - for addr, account in new_state["pre"].items(): - for key in ("nonce", "balance"): - account[key] = hex(int(account[key])) - for key in ("code", "codeHash"): - account[key] = "0x" + account[key] + receipt = Receipt( + state_root=state_root, + gas_used=50, # This is a fake filled-in gas_used value + logs=logs, + ) + receipts.append(receipt) - # look through logs for outgoing messages sent_log = {} for ID in SHARD_IDS: sent_log[ID] = [] - for receipt in result.get('receipts', []): - if receipt['logs'] is not None: - for log in receipt['logs']: - log['topics'] = [binascii.unhexlify(t[2:]) for t in log['topics']] - log['data'] = binascii.unhexlify(log['data'][2:]) - for event in contract.events.SentMessage().processReceipt(receipt): - sent_log[event.args.shard_ID].append( - # This is not a message that will be stored in the sent log, it will be - # postprocessed in make_block. Namely, the next hop shard will be computed, - # the base block will be computed and TTL will be assigned. - Message( - Block(event.args.shard_ID, sources={ID : genesis_blocks[ID] for ID in SHARD_IDS}), - 10, - event.args.shard_ID, - MessagePayload( - event.args.sendFromAddress.lower()[2:], - event.args.sendToAddress.lower()[2:], - event.args.value, - event.args.data, - ) + for receipt in receipts: + for event in contract.events.SentMessage().processReceipt(receipt): + sent_log[event.args.shard_ID].append( + # This is not a message that will be stored in the sent log, it will be + # postprocessed in make_block. Namely, the next hop shard will be computed, + # the base block will be computed and TTL will be assigned. + Message( + Block(event.args.shard_ID, sources={ID : genesis_blocks[ID] for ID in SHARD_IDS}), + 10, + event.args.shard_ID, + MessagePayload( + event.args.sendFromAddress.lower()[2:], + event.args.sendToAddress.lower()[2:], + event.args.value, + event.args.data, ) ) + ) - return new_state, sent_log - -# received_log = ReceivedLog() -# received_log.add_received_message(2, Message( -# None, # base -# 5, # TTL -# MessagePayload( -# 0, # from address -# "0x1234567890123456789012345678901234567890", # to address -# 42, # value -# "0x", # data -# ) -# )) -# new_state, sent_log = apply_to_state(vm_state, transactions, received_log) -# print(json.dumps(new_state)) -# print(sent_log) + return pre_state, sent_log diff --git a/genesis_state.py b/genesis_state.py index 3a8380d..1ae93e5 100644 --- a/genesis_state.py +++ b/genesis_state.py @@ -1,3 +1,7 @@ +from eth_keys import keys +from eth_utils import decode_hex, encode_hex, to_wei +from eth_typing import Address +from eth import constants from web3 import Web3 from config import DEADBEEF @@ -5,59 +9,64 @@ # same "pusher" address on each shard pusher_key = '0x6c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' -pusher_address = web3.eth.account.privateKeyToAccount(pusher_key).address.lower()[2:] +pusher_address = web3.eth.account.privateKeyToAccount(pusher_key).address.lower() + +genesis_params = { + 'parent_hash': constants.GENESIS_PARENT_HASH, + 'uncles_hash': constants.EMPTY_UNCLE_HASH, + 'coinbase': constants.ZERO_ADDRESS, + 'transaction_root': constants.BLANK_ROOT_HASH, + 'receipt_root': constants.BLANK_ROOT_HASH, + 'difficulty': int("0x20000", 16), + 'block_number': constants.GENESIS_BLOCK_NUMBER, + 'gas_limit': int("0x750a163df65e8a", 16), + 'timestamp': 1539724066, + 'extra_data': constants.GENESIS_EXTRA_DATA, + 'nonce': constants.GENESIS_NONCE + } # just gonna reuse this initial state on each shard. genesis_state = { - "env": { - "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "currentDifficulty": "0x20000", - "currentGasLimit": "0x750a163df65e8a", - "currentNumber": "1", - "currentTimestamp": "1000", # TODO: we may need this to change - "previousHash": "dac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4" - }, - "pre": { - pusher_address: { - "balance": "0x5ffd4878be161d74", - "code": "0x", - "nonce": "0x0", - "storage": {} - }, - DEADBEEF[2:].lower(): { - "balance": "0x1", - "code": "0x", - "nonce": "0x0", - "storage": {} - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b".lower(): { - "balance": "0x5ffd4878be161d74", - "code": "0x", - "nonce": "0x0", - "storage": {} - }, - "2c7536E3605D9C16a7a3D7b1898e529396a65c23".lower(): { - "balance": "0x5ffd4878be161d74", - "code": "0x", - "nonce": "0x0", - "storage": {} - }, - "c227e8f6eE49f35ddf4dd73F105cF743914B11Af".lower(): { - "balance": "0x5ffd4878be161d74", - "code": "0x", - "nonce": "0x0", - "storage": {} - }, - "8a8eafb1cf62bfbeb1741769dae1a9dd47996192".lower():{ - "balance": "0xfeedbead", - "nonce" : "0x00" - }, - "000000000000000000000000000000000000002a": { - "balance": "0x00", - "nonce": "0x00", - "storage": {}, - "code": "0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e09ee87014610046575b600080fd5b6100d46004803603810190808035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506100d6565b005b6000806000439250349150339050438573ffffffffffffffffffffffffffffffffffffffff16887fe9fbdfd23831dbc2bdec9e9ef0d5ac734f56996d4211992cc083e97f2770ba428933348a600054604051808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001838152602001828103825284818151815260200191508051906020019080838360005b838110156101a957808201518184015260208101905061018e565b50505050905090810190601f1680156101d65780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a4505050505050505600a165627a7a7230582086844d62bfd54b247b20657c69410cefe95f27dcb63829d23c83f0d60883191e0029", - } - } + decode_hex(pusher_address): { + "balance": int("0x5ffd4878be161d74", 16), + "code": b'', + "nonce": 0, + "storage": {} + }, + decode_hex(DEADBEEF.lower()): { + "balance": int("0x1", 16), + "code": b'', + "nonce": 0, + "storage": {} + }, + decode_hex("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"): { + "balance": int("0x5ffd4878be161d74", 16), + "code": b'', + "nonce": 0, + "storage": {} + }, + decode_hex("0x2c7536E3605D9C16a7a3D7b1898e529396a65c23"): { + "balance": int("0x5ffd4878be161d74", 16), + "code": b'', + "nonce": 0, + "storage": {} + }, + decode_hex("0xc227e8f6eE49f35ddf4dd73F105cF743914B11Af"): { + "balance": int("0x5ffd4878be161d74", 16), + "code": b'', + "nonce": 0, + "storage": {} + }, + decode_hex("0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192"):{ + "balance": int("0xfeedbead", 16), + "code": b'', + "nonce" : 0, + "storage": {} + }, + decode_hex("0x000000000000000000000000000000000000002a"): { + "balance": 0, + "nonce": 0, + "storage": {}, + "code": decode_hex("0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e09ee87014610046575b600080fd5b6100d46004803603810190808035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506100d6565b005b6000806000439250349150339050438573ffffffffffffffffffffffffffffffffffffffff16887fe9fbdfd23831dbc2bdec9e9ef0d5ac734f56996d4211992cc083e97f2770ba428933348a600054604051808681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001838152602001828103825284818151815260200191508051906020019080838360005b838110156101a957808201518184015260208101905061018e565b50505050905090810190601f1680156101d65780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a4505050505050505600a165627a7a7230582086844d62bfd54b247b20657c69410cefe95f27dcb63829d23c83f0d60883191e0029"), + } } -