Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions blocks.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
192 changes: 112 additions & 80 deletions evm_transition.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
113 changes: 61 additions & 52 deletions genesis_state.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,72 @@
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

web3 = Web3()

# 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"),
}
}