chain/ethereum: Log reproducible call details on call failures - #6698
Open
tachyontec wants to merge 2 commits into
Open
chain/ethereum: Log reproducible call details on call failures #6698tachyontec wants to merge 2 commits into
tachyontec wants to merge 2 commits into
Conversation
Call handler decoding errors and eth_call reverts named the function but nothing that identifies the call itself, so there was no way to replay a failure. The address is already at hand in both places; data_source.rs even builds a logging_extras block containing it, just after the decode errors have returned. Add the contract address, block, transaction hash, calldata and raw output to these messages, 0x-prefixed so they can be pasted into an RPC request. Empty return data now prints as 0x instead of nothing, which used to look like a formatting bug. Closes graphprotocol#3404
The call decoding errors printed the resolved function with {:?}, which
expands to the full alloy Function struct and dominates the message.
Use Function::signature_with_outputs() instead, rendering the same
information as `withdraw()(uint256)`.
This changes the text of a deterministic subgraph error. That text does
not participate in Proof of Indexing: the PoI event for a deterministic
error carries only a redacted-event count and has no message field, so
this is not consensus-breaking. It does change the derived id of rows
in the local subgraph_error table, which is a per-node dedup key only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3404.
Problem
When a call handler fails to decode its trigger, the error tells you the function
ABI and nothing else:
There is no contract address and no calldata, so you cannot retry the call to see
what happened.
raw output:has nothing after it, which looks like a brokenformat string. What it actually means is that the call returned no data.
The address is already available where this error is raised.
data_source.rsbuilds a
logging_extrasblock with the contract address and transaction hash inthe same
EthereumTrigger::Callarm, but it builds it after both decode errorshave returned, so those errors never get it.
The
eth_callpath has the same problem for a different reason.decode()inethereum_adapter.rsthrew away the request:reqis thecall::Request, and it holds the address and the encoded calldata.Both failure branches then logged just a reason:
There is already a log line with the right fields (
fn,address,data,block_hash,block_number), but it is on the success path and it istrace!,so it is not there when a call fails in production.
Fix
chain/ethereum/src/data_source.rs: build one context string in theEthereumTrigger::Callarm with the contract address,from, block number andhash, transaction hash and calldata, and use it in both decode errors. That way
the field list is not written twice and the calldata is not printed twice. Hex is
0x-prefixed so you can paste it into an RPC request. Empty output now prints as0x. A missingtransaction_hashprints asnoneinstead ofNone.chain/ethereum/src/ethereum_adapter.rs: keepreqand log its address andcalldata on both revert branches, along with
fn,output,block_hashandblock_number. These are slog key/value fields and use the same names as theexisting
eth_calltrace.log_call_errorgets the same fields. I also0x-prefixed the existing trace so both lines look the same.The second commit swaps the
{:?}dump of the function forFunction::signature_with_outputs(). It is a separate commit so you can drop itif you think it does not belong here.
Result
Before:
After:
It is shorter than before and you can rebuild the call from it.
Verification
The second commit changes the text of an error that can become a deterministic
subgraph failure, so I checked whether that text is used in Proof of Indexing. It
is not.
ProofOfIndexingEvent::DeterministicErroronly holdsredacted_events: u64and has no message field, and bothStableHashimpls hashonly that number (
graph/src/components/subgraph/proof_of_indexing/event.rs). Theonly place it is written is
core/src/subgraph/trigger_processor.rs, which passesa count from
deterministic_errors.len()and never the error itself. So this doesnot break consensus.
It does change the id of rows in the local
subgraph_errortable, becauseinsert_subgraph_errorhashes the message into the primary key. That id is localto one node and the insert uses
on_conflict_do_nothing.This only touches logging. No changes to control flow, error variants,
determinism classification (
DeterministicvsPossibleReorg), or whether a callcounts as a revert.
Checks I ran:
cargo fmt --all -- --check,cargo clippy -p graph-chain-ethereum -- -D warnings(no warnings),cargo test -p graph-chain-ethereum(59 passed), andcargo check --workspace --all-targets.One question
"Contract call reverted"isinfo!, and reverts are common rather than rare.Subgraphs that probe
symbol()ordecimals()on odd tokens, or usetry_callsthat are meant to revert, will hit it a lot. This adds about 150 to 250 bytes per
line. I can drop
block_hash, since you can get the block fromblock_number, ormove the calldata to
debug!if that is too much.