|
import json |
|
import sys |
|
|
|
import requests |
|
head = {"Content-type": "application/json"} |
|
|
|
# debug_traceTransaction script to get all touched storage locations for a tx |
|
tracer_script = "{ data: [], step: function(log) { if (log.op.toString() == \"SSTORE\" || log.op.toString() == \"SLOAD\") { var location = log.stack.peek(0); this.data.push( {op: log.op.toString(), location: location.toString(16), contract: toHex(log.contract.getAddress())} ); } }, fault: function() { return \"error\"; }, result: function() { return this.data; } }" |
|
|
|
# curl --header "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"debug_traceTransactionWitness","params":["0x6c919fd479697e59df670501c8cce85e1bcbe7a20af193baba8acb7ab6a59e30", {"tracer": "{ data: [], step: function(log) { if (log.op.toString() == \"SSTORE\" || log.op.toString() == \"SLOAD\") { var location = log.stack.peek(0); this.data.push( {op: log.op.toString(), location: location.toString(16), contract: toHex(log.contract.getAddress())} ); } }, fault: function() { return \"error\"; }, result: function() { return this.data; } }"}],"id":5}' http://localhost:8545 |
|
|
|
|
|
def sizeOfTxWitness(tx_witness): |
|
# tx_witness is same as proofs_for_tx |
|
# dict with keys being contract addresses |
|
# '0x6dea55ba04a37fddd05e1fd979c30aa0e634e837': |
|
# {'address': '0x6dea55ba04a37fddd05e1fd979c30aa0e634e837', |
|
# 'storageHash': '0xebb084cd7fdfaa7bd2ca7b08b45b778a3101f9323f221cb4f608d8578f4d50b8', |
|
# 'storageProof': [{'key': '0x0', 'value': '0x85d4780b73119b644ae5ecd22b376', 'proof': ['0xf90211a0f7d4f786dcbba72c73cb57d65d1860cfc313d1ff65281abbba514b7434a8de7fa0e856f68c65373465c9efecc80de72b199df949dc13eb0ced87911fcb20d13294a07544af8f2ce60f55ffaf52a1af8dfb8c467934fa7e1f392dc7de3e103c39c8fda0d7b415d1343c1a93f66d86910a2fc5c2ebe177b864cb0893c0d0cb17b6775029a005902822971ea6d056b23e3cbb3509ec32244348e1721769ea37f8bc6a26a2e4a0d9df80bbb8294a30b9c3c4a63eda7d05f94d345cf69cc351c850a8f71dd5573da02f0374f70e3f65444bc90cb2b81154ec859a46891bbbc79d3696ad1034332eb1a0ad14a54e3fa59e5a983680535469307f7ec87a43e5d5de9d332d4f4009e6acc2a0e87129a4297fabc68fcd87f2bf24d30236039512b783b9c468105eab145a709ea0a05207641b4bfc2c0d6c6701ee4c9a8d378064a1497e78f070080a91b1861bb1a0822341970924e5095f01f553aaff5f09b6a2d0565296da9a3e1f44f21d5762f8a0c84ada85c9e62d7d53f8556dc2e4fc6f1571dae71907881e068baeb38238182aa0fb8f3e45e1cee000f28768738a443df032745c2476ebd385a6e51156fabddee3a0bc34001e0002e3d1eab535942b7cab1b1de0731750bb9422595eb1041e4e27aca04ec7413e5d29fd5b8a3c30b78f3382700110e63fbadb02536930620bbf0aa76da06b61088e80564eae6b5d67e4d1f3d61be7f07136d11c707c95b78ab7fb4a979580','0xf90211a0212cfe1b66fa481bb6f8142f5ce806cff1d5f603466b8616e444b68c4d056914a085fc940e1a2817ccad137c69fb35cd4715d40468a9f0bdf234cb990e67438213a05d994412d0a580d74df4febeab5f742125935f917a16f5d77779829c7f5faf5ba090e4f9544517cdd050d4d945dc22f69020b56dcc0fc987735249151053af8c01a0513af332ee99fcb0ffd04303cbcfcf4e236559367690eff282f70b2756a81a3da06a722b651ae055d7579eb45aaad30f0334eaa612645c835581cd42d83c6dbc8aa044ed3f87498f5f2d390729da19f7824ab9b2da0356c43777cc10e00a2246bb66a04f868b02b3a8dbe5a614fb71110d3263c49162499e521e8d8e44e9c184b7ea3ca0c44a7d9593254d07d2dd9b4ce1452eec3e54808a136653d8fb78dbc7c5240422a07feb1c2336a635aacb206ae1eb68a59b48e1486a80b80bbdcef24503da3f6dc3a00ba071ff9618e76747c72b1f4c41cc3b41c27c515070e750e45a3a2b78f408f2a0eb8d3bcdd8ea9ce5b8d0dd4467bab515315134a786cc3db47674ceeb6da0d0b9a0beb7e2e6b70fd2e10298a6575ce50c9c1927b1fc172521b67300a86cf6617834a0fb8861283ece5631a81cbeb714eaa9959ffd998d988424b46de77d4320e8a5c2a0ade86900c11bb1528ca916c8304e5bceb9c3444868adbb271c36b293fd9e9c2aa0d76392d372aeeee121e0b71543caf3fc36f8bf8724b6957233212e46048274ec80','0xf901b1a0c519ccfae243eff48d7a109595bc925151b25fdf1c2249fcb1e39128dd049b60a0951ccb97037f542eb784210f77bb1a8a692061cb1ea0dc21d6842bd084bcd50ea0fcf58348406877282b566614eee773654496b2cbb0ef2ad50288d097c0ac4be8a03bba320400b31a03364d627f727dc5a15a032627f0d412305f7c4b06fde36f31a0f73ea4f844a220aa441cb5d4ed7c5e8460b062f4e7ca32d006378a7fa12e52b680a0cac32435937324f911c4f21346eac2be3de1703380fb7be8cf6789eeba4e2474a03149936adf08c23e63ff2c9a8c17472ca36fb39cc1b9452b86a5e4156a53ba44a05c6bd4d6f424bd2958ce010593992d4322a4ad586597a3e34fe33794981ec07780a0ef5ea7a182d7aa2f6c0de8d8036dbb652f296559a710dbeb34fe42751cb7cb9ca0706216ad427ff6f32c27c62153bc7ae304ebae77ef47d9ecdb0e2681b44c97f2a0794be2b6e01bdc1d1ec59654f974f7f8f9a995c61a57fba4813e522ca986dfe3a03c594d3f2602ea28ffdab90e24672f594e45e487020639a4dac8463a7bc65eeea05bd92b1652b0fe39578a05cab1efb2de4e766e306577beb5a973cd9308d8778f8080','0xf851808080808080808080808080a0f21dcbeb48f5a49dc50288fc2d332c085a874400847283320d16d607448b92b6a04db38b36c06c3c4d297d685c7fd15420c42252460287ae3a853ba5ff85575cb0808080', '0xf19f20ecd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563908f085d4780b73119b644ae5ecd22b376']}, |
|
# ... more {'key', 'value', 'proof'} |
|
|
|
print("tx touches {} contracts.".format(len(tx_witness.keys()))) |
|
|
|
total_size = 0 |
|
for contract in tx_witness: |
|
if contract != tx_witness[contract]['address']: |
|
sys.exit("ERROR! contract address is messed up.") |
|
|
|
total_size += 20 # size of contract address |
|
|
|
storage_proofs = tx_witness[contract]['storageProof'] |
|
print("{} storage keys touched in contract {}".format(len(storage_proofs), contract)) |
|
|
|
for key_proof in storage_proofs: |
|
print("doing key {}...".format(key_proof['key'])) |
|
total_size += 32 # size of storage key |
|
# ignore size of value for now |
|
for merkle_node in key_proof['proof']: |
|
hex_length = len(merkle_node) - 2 |
|
byte_size = hex_length / 2 |
|
total_size += byte_size |
|
|
|
# do next touched contract |
|
|
|
# did all touched contracts in tx |
|
#print("witness size: {}".format(total_size)) |
|
return total_size |
|
|
|
|
|
def getProofsForTouches(touches_in_tx): |
|
# curl --header "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getProof","params":["0xea38eaa3c86c8f9b751533ba2e562deb9acded40",["0x52419b386ce9c40b298ef864f6f9232f796eabab80a3f81a2a24d8e1018caa14","0xc4cba991f74e7ac769beca22e8f2aeca08a2ba638096cc15d357d1ca5f87a8e7"],"0x6b23ea"],"id":1}' http://localhost:8545 |
|
|
|
proofs_for_tx = {} |
|
touches_by_contract = touches_in_tx |
|
for contract in touches_by_contract: |
|
keys_touched = touches_by_contract[contract] |
|
keys_touched_hex = ["0x{}".format(k['location']) for k in keys_touched] |
|
payload = { |
|
'method': 'eth_getProof', |
|
'params': [contract, keys_touched_hex, hex(7021700)], |
|
'id': 1 |
|
} |
|
|
|
response = requests.post("http://localhost:8545", data=json.dumps(payload), headers=head) |
|
#print("proofs for contract {}".format(contract)) |
|
#print(response.text) |
|
r = response.json()['result'] |
|
storageProofs = {} |
|
storageProofs['address'] = r['address'] |
|
storageProofs['storageHash'] = r['storageHash'] |
|
storageProofs['storageProof'] = r['storageProof'] |
|
print('storageProofs: {}'.format(storageProofs)) |
|
proofs_for_tx[contract] = storageProofs |
|
|
|
return proofs_for_tx |
|
|
|
|
|
def traceBlockAndGetWitnessSize(txs_to_trace): |
|
proofs_by_tx = {} |
|
|
|
for tx in txs_to_trace: |
|
print("getting trace for tx {}".format(tx)) |
|
payload = { |
|
'method': 'debug_traceTransaction', |
|
'params': [tx, {'tracer': tracer_script}], |
|
'id': 1 |
|
} |
|
response = requests.post("http://localhost:8545", data=json.dumps(payload), headers=head) |
|
#print(response.text) |
|
try: |
|
trace_touches = response.json()['result'] |
|
except: |
|
print("got error parsing debug_traceTransaction response!", sys.exc_info()[0]) |
|
print(response.text) |
|
sys.exit("got error parsing debug_traceTransaction response. stopping.") |
|
|
|
# sort traces into dict, indexed by contract address |
|
touches_by_contract = {} |
|
for touch in trace_touches: |
|
address = touch['contract'] |
|
if address not in touches_by_contract: |
|
touches_by_contract[address] = [] |
|
same_loc = list(filter(lambda t: t['location'] == touch['location'], touches_by_contract[address])) |
|
if not same_loc: |
|
touches_by_contract[address].append({'op': touch['op'], 'location': touch['location']}) |
|
else: |
|
# same_loc should always be length 1 |
|
if len(same_loc) is not 1: |
|
print("ERROR WITH SAME_LOC!") |
|
break |
|
if same_loc[0]['op'] is 'SSTORE' and touch['op'] is SLOAD: |
|
same_loc[0]['op'] = 'SLOAD' # sload overrides sstore |
|
|
|
print("touches_by_contract: {}".format(touches_by_contract)) |
|
# now get the storage proofs for all touched contracts and storage locations |
|
tx_proofs = getProofsForTouches(touches_by_contract) |
|
print("got proofs for tx: {}".format(tx)) |
|
proofs_by_tx[tx] = tx_proofs |
|
|
|
# done getting proofs for tx's |
|
print("got proofs for all txs: {}".format(proofs_by_tx)) |
|
block_witness_size = 0 |
|
|
|
for tx in proofs_by_tx: |
|
witness_size = sizeOfTxWitness(proofs_by_tx[tx]) |
|
print("tx {} has witness size {}".format(tx, witness_size)) |
|
block_witness_size += witness_size |
|
|
|
print("total size of block witness: {}".format(block_witness_size)) |
|
return block_witness_size |
|
|
|
|
|
|
|
# curl --header "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getProof","params":["0x5c2c629feefcc07b338e97e39c73d2db33a85548",["0x1fef3744f4fa28ec9914695a58fd34ee7414218a94c6aa27eb874af0218ef0a8","0x8f3901471e122af74b0ac91231e0147b7f7f02dd0c8e90e40afde2c74be23f47"],"0x6b2188"],"id":1}' http://localhost:8545 |
|
|
|
# start at block 7021700 |
|
# do getBlock, with full transaction objects |
|
|
|
# for each transaction, do eth_getCode at the block num, on the `to` address. |
|
# if `to` address has code, then do debug_traceTransaction to get list of storage locations |
|
# sort list of storage locations by contract address |
|
# for each accessed contract, do eth_getProof on all the touched storage locations |
|
# bundle the results of eth_getProof storage proofs |
|
|
|
|
|
def getWitnessSizeForBlock(block_num): |
|
payload = { |
|
'method': 'eth_getBlockByNumber', |
|
'params': [hex(block_num), True], |
|
'id': 1 |
|
} |
|
response = requests.post("http://localhost:8545", data=json.dumps(payload), headers=head) |
|
#print(response.text) |
|
|
|
r = response.json() |
|
block = r['result'] |
|
|
|
print("gasUsed: {}".format(block['gasUsed'])) |
|
print("tx count: {}".format(len(block['transactions']))) |
|
|
|
txs_to_trace = [] |
|
|
|
for idx, tx in enumerate(block['transactions']): |
|
if tx['to'] is None: |
|
print("deploy tx in block!: {}".format(tx)) |
|
txs_to_trace.append(tx['hash']) |
|
continue |
|
payload = { |
|
'method': 'eth_getCode', |
|
'params': [tx['to'], hex(block_num)], |
|
'id': 1 |
|
} |
|
print("getting code: {}".format(payload)) |
|
response = requests.post("http://localhost:8545", data=json.dumps(payload), headers=head) |
|
if 'result' not in response.json(): |
|
print("bad result!: {}".format(response.json())) |
|
print("tx: ".format(tx)) |
|
break |
|
code = response.json()['result'] |
|
print("tx {} has code: {}".format(idx, code)) |
|
if len(code) > 2: |
|
txs_to_trace.append(tx['hash']) |
|
|
|
print("txs_to_trace: {}".format(txs_to_trace)) |
|
print("number of tx's to trace: {}".format(len(txs_to_trace))) |
|
|
|
block_witness_size = traceBlockAndGetWitnessSize(txs_to_trace) |
|
print("got block_witness_size: {}".format(block_witness_size)) |
|
return block_witness_size |
|
|
|
|
|
|
|
START_BLOCK = 7021700 |
|
|
|
block_sizes = {} |
|
|
|
for i in range(START_BLOCK, START_BLOCK+10): |
|
print("doing block {}...".format(i)) |
|
size = getWitnessSizeForBlock(i) |
|
print("got size for block #{}: {}".format(i, size)) |
|
block_sizes[i] = size |
|
|
|
print("got some block witness sizes:") |
|
for i in block_sizes: |
|
print(" block #{}: {} bytes".format(i, block_sizes[i])) |