I want to be able to generate test cases from a debug_traceCall.
This requires network and ethjsonrpc support, not currently available in this repo.
I'd rather avoid pulling in any more dependencies if possible.
eth_blockNumber to get block number
debug_traceCall to get structLogs
- Go through structLogs and assemble an access list
- Query code, nonce, balance, and storage from the access list
- Dump access list contents into file along with a test entry for the tracecall.
Ideally it can also extend an existing config, failing on conflicting state.
Some python I have been using for this:
reply = json.loads(
await post_with_retry(
json.dumps(debug_traceCall(call, block_number=block_number)[0]),
provider_url=provider_url,
)
)
if "error" in reply:
print(reply)
return
result = reply["result"]
access_list = {}
accounts = []
if "to" in call:
account = call["to"]
access_list[account] = []
accounts.append(account)
if "from" in call:
account = call["from"]
access_list[account] = []
def on_sload(log):
access_list[accounts[-1]].append(log["stack"][-1])
def on_call(log):
account = bytes32_to_address(
uint256_to_bytes32(
int(log["stack"][-2], 16) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)
)
accounts.append(account)
if account not in access_list:
access_list[account] = []
def on_delegatecall(log):
account = bytes32_to_address(
uint256_to_bytes32(
int(log["stack"][-2], 16) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)
)
if account not in access_list:
access_list[account] = []
accounts.append(accounts[-1])
def on_extcode(log):
account = bytes32_to_address(
uint256_to_bytes32(
int(log["stack"][-2], 16) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)
)
if account not in access_list:
access_list[account] = []
def on_stop(log):
accounts.pop()
actions = {
"SLOAD": on_sload,
"CALL": on_call,
"STATICCALL": on_call,
"DELEGATECALL": on_delegatecall,
"EXTCODECOPY": on_extcode,
"EXTCODESIZE": on_extcode,
"STOP": on_stop,
"RETURN": on_stop,
"REVERT": on_stop,
}
for log in result["structLogs"]:
action = actions.get(log["op"])
if action is None:
continue
action(log)
requests = []
for address, storage_keys in access_list.items():
requests += eth_getBalance(address, block_number=block_number)
requests += nonce_of(address, block_number=block_number)
requests += eth_getCode(address, block_number=block_number)
for key in storage_keys:
requests += eth_getStorageAt(address, key, block_number=block_number)
state = json.loads(
await post_with_retry(json.dumps(requests), provider_url=provider_url)
)
i = 0
output = []
for address, storage_keys in access_list.items():
if "result" not in state[i]:
assert False, (requests[i], state[i])
balance = state[i]["result"]
i += 1
nonce = state[i]["result"]
i += 1
code = state[i]["result"]
i += 1
storage = {}
for key in storage_keys:
storage[key] = state[i]["result"]
i += 1
account = {
"address": address,
"nonce": nonce,
"balance": balance,
"code": code,
}
if storage:
account["storage"] = storage
output.append(account)
if "data" in call:
data = call["data"]
del call["data"]
call["input"] = data
call["debug"] = "0x20"
if block_number != "latest":
call["blockNumber"] = hex(block_number)
else:
call["blockNumber"] = context.block_number_hex
output.append({"tests": [call]})
print("Writing config to", outfile)
with open(outfile, "w") as f:
process = Popen(
["jq", "--indent", "4"], stdin=PIPE, stdout=f, stderr=f, text=True
)
process.communicate(input=json.dumps(output))
It might also need to support block.number and block.timestamp but that's less important than everything else for now.
I want to be able to generate test cases from a
debug_traceCall.This requires network and ethjsonrpc support, not currently available in this repo.
I'd rather avoid pulling in any more dependencies if possible.
eth_blockNumberto get block numberdebug_traceCallto getstructLogsIdeally it can also extend an existing config, failing on conflicting state.
Some python I have been using for this:
It might also need to support
block.numberandblock.timestampbut that's less important than everything else for now.