Skip to content

dio executable to make -w config #96

@wjmelements

Description

@wjmelements

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.

  1. eth_blockNumber to get block number
  2. debug_traceCall to get structLogs
  3. Go through structLogs and assemble an access list
  4. Query code, nonce, balance, and storage from the access list
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions