From e048aae942f53497baa13b3547edfdd52194e37b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 30 Jan 2024 10:29:54 -0800 Subject: [PATCH] fix: eth: re-execute tipsets on missing events (#11588) This will re-execute tipsets to forcibly re-compute and store events when they're missing. This is effectively lazy backfilling of events. NOTE: This _won't_ backfill the index itself, it'll just give us the events. fixes #11335 --- chain/stmgr/execute.go | 16 ++++++++++++++-- node/impl/full/eth.go | 10 +--------- node/impl/full/eth_utils.go | 24 +++++++++++++++++++++--- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index bed8578338b..e716ed198df 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -13,6 +13,16 @@ import ( ) func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) { + return sm.tipSetState(ctx, ts, false) +} + +// Recompute the tipset state without trying to lookup a pre-computed result in the chainstore. +// Useful if we know that our local chain-state isn't complete (e.g., we've discarded the events). +func (sm *StateManager) RecomputeTipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) { + return sm.tipSetState(ctx, ts, true) +} + +func (sm *StateManager) tipSetState(ctx context.Context, ts *types.TipSet, recompute bool) (st cid.Cid, rec cid.Cid, err error) { ctx, span := trace.StartSpan(ctx, "tipSetState") defer span.End() if span.IsRecordingEvents() { @@ -65,8 +75,10 @@ func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st c // First, try to find the tipset in the current chain. If found, we can avoid re-executing // it. - if st, rec, found := tryLookupTipsetState(ctx, sm.cs, ts); found { - return st, rec, nil + if !recompute { + if st, rec, found := tryLookupTipsetState(ctx, sm.cs, ts); found { + return st, rec, nil + } } st, rec, err = sm.tsExec.ExecuteTipSet(ctx, sm, ts, sm.tsExecMonitor, false) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 5c3fcac960d..e7aeafa9085 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -424,15 +424,7 @@ func (a *EthModule) EthGetTransactionReceiptLimited(ctx context.Context, txHash return nil, xerrors.Errorf("failed to convert %s into an Eth Txn: %w", txHash, err) } - var events []types.Event - if rct := msgLookup.Receipt; rct.EventsRoot != nil { - events, err = a.ChainAPI.ChainGetEvents(ctx, *rct.EventsRoot) - if err != nil { - return nil, xerrors.Errorf("failed get events for %s", txHash) - } - } - - receipt, err := newEthTxReceipt(ctx, tx, msgLookup, events, a.Chain, a.StateAPI) + receipt, err := newEthTxReceipt(ctx, tx, msgLookup, a.ChainAPI, a.StateAPI) if err != nil { return nil, xerrors.Errorf("failed to convert %s into an Eth Receipt: %w", txHash, err) } diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index dbb1ce96839..3e9df852a93 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -659,7 +659,7 @@ func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, tx return tx, nil } -func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLookup, events []types.Event, cs *store.ChainStore, sa StateAPI) (api.EthTxReceipt, error) { +func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLookup, ca ChainAPI, sa StateAPI) (api.EthTxReceipt, error) { var ( transactionIndex ethtypes.EthUint64 blockHash ethtypes.EthHash @@ -700,7 +700,7 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook receipt.CumulativeGasUsed = ethtypes.EmptyEthInt // TODO: avoid loading the tipset twice (once here, once when we convert the message to a txn) - ts, err := cs.GetTipSetFromKey(ctx, lookup.TipSet) + ts, err := ca.Chain.GetTipSetFromKey(ctx, lookup.TipSet) if err != nil { return api.EthTxReceipt{}, xerrors.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", lookup.TipSet, err) } @@ -711,7 +711,7 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook } // The tx is located in the parent tipset - parentTs, err := cs.LoadTipSet(ctx, ts.Parents()) + parentTs, err := ca.Chain.LoadTipSet(ctx, ts.Parents()) if err != nil { return api.EthTxReceipt{}, xerrors.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", ts.Parents(), err) } @@ -736,6 +736,24 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook receipt.ContractAddress = &addr } + var events []types.Event + if rct := lookup.Receipt; rct.EventsRoot != nil { + events, err = ca.ChainGetEvents(ctx, *rct.EventsRoot) + if err != nil { + // Fore-recompute, we must have enabled the Event APIs after computing this + // tipset. + if _, _, err := sa.StateManager.RecomputeTipSetState(ctx, ts); err != nil { + + return api.EthTxReceipt{}, xerrors.Errorf("failed get events: %w", err) + } + // Try again + events, err = ca.ChainGetEvents(ctx, *rct.EventsRoot) + if err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed get events: %w", err) + } + } + } + if len(events) > 0 { receipt.Logs = make([]ethtypes.EthLog, 0, len(events)) for i, evt := range events {