From 011987867a0053d133209b6e67e81674b72fd6de Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 7 Mar 2022 02:54:57 +0100 Subject: [PATCH] pkg/ethereum: check receipt status code commit-id:0b2539be --- node/pkg/ethereum/by_transaction.go | 12 ++++++++++++ node/pkg/ethereum/watcher.go | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/node/pkg/ethereum/by_transaction.go b/node/pkg/ethereum/by_transaction.go index 82ea55557..753a16430 100644 --- a/node/pkg/ethereum/by_transaction.go +++ b/node/pkg/ethereum/by_transaction.go @@ -38,6 +38,18 @@ func MessageEventsForTransaction( return 0, nil, fmt.Errorf("failed to get transaction receipt: %w", err) } + // Bail early when the transaction receipt status is anything other than + // 1 (success). In theory, this check isn't strictly necessary - a failed + // transaction cannot emit logs and will trigger neither subscription + // messages nor have log messages in its receipt. + // + // However, relying on that invariant is brittle - we connect to a lot of + // EVM-compatible chains which might accidentally break this API contract + // and return logs for failed transactions. Check explicitly instead. + if receipt.Status != 1 { + return 0, nil, fmt.Errorf("non-success transaction status: %d", receipt.Status) + } + // Get block block, err := c.BlockByHash(ctx, receipt.BlockHash) if err != nil { diff --git a/node/pkg/ethereum/watcher.go b/node/pkg/ethereum/watcher.go index 21d6c4355..9e0b5a787 100644 --- a/node/pkg/ethereum/watcher.go +++ b/node/pkg/ethereum/watcher.go @@ -415,6 +415,24 @@ func (e *Watcher) Run(ctx context.Context) error { tx, err := c.TransactionReceipt(timeout, pLock.message.TxHash) cancel() + // This should never happen - if we got this far, it means that logs were emitted, + // which is only possible if the transaction succeeded. We check it anyway just + // in case the EVM implementation is buggy. + if tx.Status != 1 { + logger.Error("transaction receipt with non-success status", + zap.Stringer("tx", pLock.message.TxHash), + zap.Stringer("blockhash", key.BlockHash), + zap.Stringer("emitter_address", key.EmitterAddress), + zap.Uint64("sequence", key.Sequence), + zap.Stringer("current_block", ev.Number), + zap.Stringer("current_blockhash", currentHash), + zap.String("eth_network", e.networkName), + zap.Error(err)) + delete(e.pending, key) + ethMessagesOrphaned.WithLabelValues(e.networkName, "tx_failed").Inc() + continue + } + // If the node returns an error after waiting expectedConfirmation blocks, // it means the chain reorged and the transaction was orphaned. The // TransactionReceipt call is using the same websocket connection than the