From 191df2d12aeaaf4f805457e97b70bab4d0f05a34 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Wed, 11 Dec 2024 16:35:52 -0600 Subject: [PATCH] fix: correctly return an error when unable to properly parse a transaction and its logs --- pkg/indexer/indexer.go | 9 ++-- pkg/indexer/transactionLogs.go | 77 +++++++++++++++++++++++++--------- pkg/sidecar/blockIndexer.go | 4 -- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/pkg/indexer/indexer.go b/pkg/indexer/indexer.go index fcf7345a..783a0501 100644 --- a/pkg/indexer/indexer.go +++ b/pkg/indexer/indexer.go @@ -12,6 +12,7 @@ import ( "github.com/Layr-Labs/sidecar/pkg/storage" "gorm.io/gorm" "slices" + "strings" "github.com/Layr-Labs/sidecar/internal/config" "go.uber.org/zap" @@ -37,6 +38,8 @@ const ( IndexError_FailedToCombineAbis IndexErrorType = 3 IndexError_FailedToFindContract IndexErrorType = 4 IndexError_FailedToParseAbi IndexErrorType = 5 + IndexError_EmptyAbi IndexErrorType = 6 + IndexError_FailedToDecodeLog IndexErrorType = 7 ) type IndexError struct { @@ -44,7 +47,7 @@ type IndexError struct { Err error BlockNumber uint64 TransactionHash string - LogIndex int + LogIndex uint64 Metadata map[string]interface{} Message string } @@ -71,7 +74,7 @@ func (e *IndexError) WithTransactionHash(txHash string) *IndexError { return e } -func (e *IndexError) WithLogIndex(logIndex int) *IndexError { +func (e *IndexError) WithLogIndex(logIndex uint64) *IndexError { e.LogIndex = logIndex return e } @@ -234,7 +237,7 @@ func (idx *Indexer) IsInterestingAddress(addr string) bool { if addr == "" { return false } - return slices.Contains(idx.Config.GetInterestingAddressForConfigEnv(), addr) + return slices.Contains(idx.Config.GetInterestingAddressForConfigEnv(), strings.ToLower(addr)) } func (idx *Indexer) IsInterestingTransaction(txn *ethereum.EthereumTransaction, receipt *ethereum.EthereumTransactionReceipt) bool { diff --git a/pkg/indexer/transactionLogs.go b/pkg/indexer/transactionLogs.go index 321ce667..dbcf3302 100644 --- a/pkg/indexer/transactionLogs.go +++ b/pkg/indexer/transactionLogs.go @@ -57,6 +57,8 @@ func (idx *Indexer) ParseTransactionLogs( } contractAddress := receipt.GetTargetAddress() + // when the contractAddress is empty, we can skip the transaction since it wont have any logs associated with it. + // this is pretty typical of a transaction that simply sends ETH from one address to another without interacting with a contract if contractAddress.Value() == "" { idx.Logger.Sugar().Debugw("No contract address found in receipt, skipping", zap.String("hash", transaction.Hash.Value())) return nil, nil @@ -77,23 +79,26 @@ func (idx *Indexer) ParseTransactionLogs( WithMetadata("contractAddress", contractAddress.Value()) } + // if the contract is interesting but not found, throw an error to stop processing if contract == nil { - idx.Logger.Sugar().Debugw("No contract found for address", zap.String("hash", transaction.Hash.Value())) - return nil, nil + idx.Logger.Sugar().Errorw("No contract found for address", zap.String("hash", transaction.Hash.Value())) + return nil, NewIndexError(IndexError_FailedToFindContract, err). + WithMessage("No contract found for address"). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()) } contractAbi := contract.CombineAbis() - if err != nil { - idx.Logger.Sugar().Errorw("Failed to combine ABIs") - return nil, NewIndexError(IndexError_FailedToCombineAbis, err). - WithMessage("Failed to combine ABIs"). - WithBlockNumber(transaction.BlockNumber.Value()). - WithTransactionHash(transaction.Hash.Value()) - } + // If the ABI is empty, return an error if contractAbi == "" { - idx.Logger.Sugar().Debugw("No ABI found for contract", zap.String("hash", transaction.Hash.Value())) - return parsedTransaction, nil + idx.Logger.Sugar().Errorw("No ABI found for contract", zap.String("hash", transaction.Hash.Value())) + return nil, NewIndexError(IndexError_EmptyAbi, err). + WithMessage("No ABI found for contract"). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()) } a, err = idx.getAbi(contractAbi) if err != nil { @@ -112,18 +117,33 @@ func (idx *Indexer) ParseTransactionLogs( decodedSig, err := hex.DecodeString(txInput[2:10]) if err != nil { idx.Logger.Sugar().Errorw("Failed to decode signature") + return nil, NewIndexError(IndexError_FailedToParseTransaction, err). + WithMessage("Failed to decode signature"). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()) } if len(decodedSig) > 0 { method, err = a.MethodById(decodedSig) if err != nil { - idx.Logger.Sugar().Debugw(fmt.Sprintf("Failed to find method by ID '%s'", common.BytesToHash(decodedSig).String())) - parsedTransaction.MethodName = "unknown" + msg := fmt.Sprintf("Failed to find method by ID '%s'", common.BytesToHash(decodedSig).String()) + idx.Logger.Sugar().Debugw(msg) + return nil, NewIndexError(IndexError_FailedToParseTransaction, err). + WithMessage(msg). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()) } else { parsedTransaction.MethodName = method.RawName decodedData, err := hex.DecodeString(txInput[10:]) if err != nil { - idx.Logger.Sugar().Errorw("Failed to decode input data") + idx.Logger.Sugar().Errorw("Failed to decode transaction input data") + return nil, NewIndexError(IndexError_FailedToParseTransaction, err). + WithMessage("Failed to decode transaction input data"). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()) } else { callMap := map[string]interface{}{} if err := method.Inputs.UnpackIntoMap(callMap, decodedData); err != nil { @@ -132,10 +152,20 @@ func (idx *Indexer) ParseTransactionLogs( zap.String("transactionHash", transaction.Hash.Value()), zap.Uint64("blockNumber", transaction.BlockNumber.Value()), ) + return nil, NewIndexError(IndexError_FailedToParseTransaction, err). + WithMessage("Failed to unpack data"). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()) } callMapBytes, err := json.Marshal(callMap) if err != nil { idx.Logger.Sugar().Errorw("Failed to marshal callMap data", zap.String("hash", transaction.Hash.Value())) + return nil, NewIndexError(IndexError_FailedToParseTransaction, err). + WithMessage("Failed to marshal callMap data"). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()) } parsedTransaction.DecodedData = string(callMapBytes) } @@ -159,7 +189,14 @@ func (idx *Indexer) ParseTransactionLogs( } decodedLog, err := idx.DecodeLogWithAbi(a, receipt, lg) if err != nil { - idx.Logger.Sugar().Debugw(fmt.Sprintf("Error decoding log - index: '%d' - '%s'", i, transaction.Hash.Value()), zap.Error(err)) + msg := fmt.Sprintf("Error decoding log - index: '%d' - '%s'", i, transaction.Hash.Value()) + idx.Logger.Sugar().Debugw(msg, zap.Error(err)) + return nil, NewIndexError(IndexError_FailedToDecodeLog, err). + WithMessage(msg). + WithBlockNumber(transaction.BlockNumber.Value()). + WithTransactionHash(transaction.Hash.Value()). + WithMetadata("contractAddress", contractAddress.Value()). + WithLogIndex(lg.LogIndex.Value()) } else { idx.Logger.Sugar().Debugw(fmt.Sprintf("Decoded log - index: '%d' - '%s'", i, transaction.Hash.Value()), zap.Any("decodedLog", decodedLog)) } @@ -246,9 +283,10 @@ func (idx *Indexer) DecodeLog(a *abi.ABI, lg *ethereum.EthereumEventLog) (*parse } if a == nil { - idx.Logger.Sugar().Debugw(fmt.Sprintf("No ABI provided, using topic hash as event name '%s'", logAddress.String())) - decodedLog.EventName = topicHash.String() - return decodedLog, nil + idx.Logger.Sugar().Errorw("No ABI provided for decoding log", + zap.String("address", logAddress.String()), + ) + return nil, errors.New("no ABI provided for decoding log") } event, err := a.EventByID(topicHash) @@ -290,13 +328,14 @@ func (idx *Indexer) DecodeLog(a *abi.ABI, lg *ethereum.EthereumEventLog) (*parse outputDataMap := make(map[string]interface{}) err = a.UnpackIntoMap(outputDataMap, event.Name, byteData) if err != nil { - idx.Logger.Sugar().Errorw("Failed to unpack data: ", + idx.Logger.Sugar().Errorw("Failed to unpack data", zap.Error(err), zap.String("hash", lg.TransactionHash.Value()), zap.String("address", lg.Address.Value()), zap.String("eventName", event.Name), zap.String("transactionHash", lg.TransactionHash.Value()), ) + return nil, errors.New("failed to unpack data") } decodedLog.OutputData = outputDataMap diff --git a/pkg/sidecar/blockIndexer.go b/pkg/sidecar/blockIndexer.go index ef843f16..189b58f7 100644 --- a/pkg/sidecar/blockIndexer.go +++ b/pkg/sidecar/blockIndexer.go @@ -111,10 +111,6 @@ func (s *Sidecar) IndexFromCurrentToTip(ctx context.Context) error { s.Logger.Sugar().Infow("No blocks indexed, starting from genesis block", zap.Uint64("genesisBlock", s.Config.GenesisBlockNumber)) lastIndexedBlock = int64(s.Config.GenesisBlockNumber) } else { - s.Logger.Sugar().Infow("Comparing latest block and latest state root", - zap.Int64("lastIndexedBlock", lastIndexedBlock), - zap.Uint64("latestStateRootBlock", latestStateRoot.EthBlockNumber), - ) // if the latest state root is behind the latest block, delete the corrupted state and set the // latest block to the latest state root + 1 if latestStateRoot != nil && latestStateRoot.EthBlockNumber < uint64(lastIndexedBlock) {