eth/catalyst: log errors returned on the API locally (trace level)

having a local log of errors returned on the API could be
useful during debug. Here we add trace level logging, which
can easily be turned on specific to this module.

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
Csaba Kiraly 2025-09-03 13:16:15 +02:00
parent e6884ccccf
commit f1b393cdb4
No known key found for this signature in database
GPG key ID: 0FE274EE8C95166E

View file

@ -275,7 +275,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
} }
log.Info("Forkchoice requested sync to new head", context...) log.Info("Forkchoice requested sync to new head", context...)
if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), header, finalized); err != nil { if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), header, finalized); err != nil {
return engine.STATUS_SYNCING, err return engine.STATUS_SYNCING, logErr(err)
} }
return engine.STATUS_SYNCING, nil return engine.STATUS_SYNCING, nil
} }
@ -284,7 +284,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
if block.Difficulty().BitLen() > 0 && block.NumberU64() > 0 { if block.Difficulty().BitLen() > 0 && block.NumberU64() > 0 {
ph := api.eth.BlockChain().GetHeader(block.ParentHash(), block.NumberU64()-1) ph := api.eth.BlockChain().GetHeader(block.ParentHash(), block.NumberU64()-1)
if ph == nil { if ph == nil {
return engine.STATUS_INVALID, errors.New("parent unavailable for difficulty check") return engine.STATUS_INVALID, logErr(errors.New("parent unavailable for difficulty check"))
} }
if ph.Difficulty.Sign() == 0 && block.Difficulty().Sign() > 0 { if ph.Difficulty.Sign() == 0 && block.Difficulty().Sign() > 0 {
log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0))) log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
@ -303,7 +303,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash { if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
// Block is not canonical, set head. // Block is not canonical, set head.
if latestValid, err := api.eth.BlockChain().SetCanonical(block); err != nil { if latestValid, err := api.eth.BlockChain().SetCanonical(block); err != nil {
return engine.ForkChoiceResponse{PayloadStatus: engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: &latestValid}}, err return engine.ForkChoiceResponse{PayloadStatus: engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: &latestValid}}, logErr(err)
} }
} else if api.eth.BlockChain().CurrentBlock().Hash() == update.HeadBlockHash { } else if api.eth.BlockChain().CurrentBlock().Hash() == update.HeadBlockHash {
// If the specified head matches with our local head, do nothing and keep // If the specified head matches with our local head, do nothing and keep
@ -324,10 +324,10 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash) finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash)
if finalBlock == nil { if finalBlock == nil {
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash) log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not available in database")) return engine.STATUS_INVALID, invalidForkChoiceStateErr("final block not available in database")
} else if rawdb.ReadCanonicalHash(api.eth.ChainDb(), finalBlock.NumberU64()) != update.FinalizedBlockHash { } else if rawdb.ReadCanonicalHash(api.eth.ChainDb(), finalBlock.NumberU64()) != update.FinalizedBlockHash {
log.Warn("Final block not in canonical chain", "number", finalBlock.NumberU64(), "hash", update.FinalizedBlockHash) log.Warn("Final block not in canonical chain", "number", finalBlock.NumberU64(), "hash", update.FinalizedBlockHash)
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not in canonical chain")) return engine.STATUS_INVALID, invalidForkChoiceStateErr("final block not in canonical chain")
} }
// Set the finalized block // Set the finalized block
api.eth.BlockChain().SetFinalized(finalBlock.Header()) api.eth.BlockChain().SetFinalized(finalBlock.Header())
@ -337,11 +337,11 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash) safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash)
if safeBlock == nil { if safeBlock == nil {
log.Warn("Safe block not available in database") log.Warn("Safe block not available in database")
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("safe block not available in database")) return engine.STATUS_INVALID, invalidForkChoiceStateErr("safe block not available in database")
} }
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), safeBlock.NumberU64()) != update.SafeBlockHash { if rawdb.ReadCanonicalHash(api.eth.ChainDb(), safeBlock.NumberU64()) != update.SafeBlockHash {
log.Warn("Safe block not in canonical chain") log.Warn("Safe block not in canonical chain")
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("safe block not in canonical chain")) return engine.STATUS_INVALID, invalidForkChoiceStateErr("safe block not in canonical chain")
} }
// Set the safe block // Set the safe block
api.eth.BlockChain().SetSafe(safeBlock.Header()) api.eth.BlockChain().SetSafe(safeBlock.Header())
@ -368,7 +368,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
payload, err := api.eth.Miner().BuildPayload(args, payloadWitness) payload, err := api.eth.Miner().BuildPayload(args, payloadWitness)
if err != nil { if err != nil {
log.Error("Failed to build payload", "err", err) log.Error("Failed to build payload", "err", err)
return valid(nil), engine.InvalidPayloadAttributes.With(err) return valid(nil), logErr(engine.InvalidPayloadAttributes.With(err))
} }
api.localBlocks.put(id, payload) api.localBlocks.put(id, payload)
return valid(&id), nil return valid(&id), nil
@ -381,7 +381,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.TransitionConfigurationV1) (*engine.TransitionConfigurationV1, error) { func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.TransitionConfigurationV1) (*engine.TransitionConfigurationV1, error) {
log.Trace("Engine API request received", "method", "ExchangeTransitionConfiguration", "ttd", config.TerminalTotalDifficulty) log.Trace("Engine API request received", "method", "ExchangeTransitionConfiguration", "ttd", config.TerminalTotalDifficulty)
if config.TerminalTotalDifficulty == nil { if config.TerminalTotalDifficulty == nil {
return nil, errors.New("invalid terminal total difficulty") return nil, logErr(errors.New("invalid terminal total difficulty"))
} }
// Stash away the last update to warn the user if the beacon client goes offline // Stash away the last update to warn the user if the beacon client goes offline
api.lastTransitionUpdate.Store(time.Now().Unix()) api.lastTransitionUpdate.Store(time.Now().Unix())
@ -389,7 +389,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
ttd := api.config().TerminalTotalDifficulty ttd := api.config().TerminalTotalDifficulty
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 { if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty) log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty) return nil, logErr(fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty))
} }
if config.TerminalBlockHash != (common.Hash{}) { if config.TerminalBlockHash != (common.Hash{}) {
if hash := api.eth.BlockChain().GetCanonicalHash(uint64(config.TerminalBlockNumber)); hash == config.TerminalBlockHash { if hash := api.eth.BlockChain().GetCanonicalHash(uint64(config.TerminalBlockNumber)); hash == config.TerminalBlockHash {
@ -399,7 +399,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
TerminalBlockNumber: config.TerminalBlockNumber, TerminalBlockNumber: config.TerminalBlockNumber,
}, nil }, nil
} }
return nil, errors.New("invalid terminal block hash") return nil, logErr(errors.New("invalid terminal block hash"))
} }
return &engine.TransitionConfigurationV1{TerminalTotalDifficulty: (*hexutil.Big)(ttd)}, nil return &engine.TransitionConfigurationV1{TerminalTotalDifficulty: (*hexutil.Big)(ttd)}, nil
} }
@ -407,11 +407,11 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
// GetPayloadV1 returns a cached payload by id. // GetPayloadV1 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) { func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) {
if !payloadID.Is(engine.PayloadV1) { if !payloadID.Is(engine.PayloadV1) {
return nil, engine.UnsupportedFork return nil, logErr(engine.UnsupportedFork)
} }
data, err := api.getPayload(payloadID, false) data, err := api.getPayload(payloadID, false)
if err != nil { if err != nil {
return nil, err return nil, logErr(err)
} }
return data.ExecutionPayload, nil return data.ExecutionPayload, nil
} }
@ -419,7 +419,7 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu
// GetPayloadV2 returns a cached payload by id. // GetPayloadV2 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
if !payloadID.Is(engine.PayloadV1, engine.PayloadV2) { if !payloadID.Is(engine.PayloadV1, engine.PayloadV2) {
return nil, engine.UnsupportedFork return nil, logErr(engine.UnsupportedFork)
} }
return api.getPayload(payloadID, false) return api.getPayload(payloadID, false)
} }
@ -427,7 +427,7 @@ func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.Execu
// GetPayloadV3 returns a cached payload by id. // GetPayloadV3 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
if !payloadID.Is(engine.PayloadV3) { if !payloadID.Is(engine.PayloadV3) {
return nil, engine.UnsupportedFork return nil, logErr(engine.UnsupportedFork)
} }
return api.getPayload(payloadID, false) return api.getPayload(payloadID, false)
} }
@ -435,7 +435,7 @@ func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.Execu
// GetPayloadV4 returns a cached payload by id. // GetPayloadV4 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
if !payloadID.Is(engine.PayloadV3) { if !payloadID.Is(engine.PayloadV3) {
return nil, engine.UnsupportedFork return nil, logErr(engine.UnsupportedFork)
} }
return api.getPayload(payloadID, false) return api.getPayload(payloadID, false)
} }
@ -443,7 +443,7 @@ func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.Execu
// GetPayloadV5 returns a cached payload by id. // GetPayloadV5 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
if !payloadID.Is(engine.PayloadV3) { if !payloadID.Is(engine.PayloadV3) {
return nil, engine.UnsupportedFork return nil, logErr(engine.UnsupportedFork)
} }
return api.getPayload(payloadID, false) return api.getPayload(payloadID, false)
} }
@ -452,7 +452,7 @@ func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*eng
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID) log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
data := api.localBlocks.get(payloadID, full) data := api.localBlocks.get(payloadID, full)
if data == nil { if data == nil {
return nil, engine.UnknownPayload return nil, logErr(engine.UnknownPayload)
} }
return data, nil return data, nil
} }
@ -480,11 +480,11 @@ func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*eng
// unable to serve blob pool data. // unable to serve blob pool data.
func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProofV1, error) { func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProofV1, error) {
if len(hashes) > 128 { if len(hashes) > 128 {
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) return nil, logErr(engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))))
} }
blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion0) blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion0)
if err != nil { if err != nil {
return nil, engine.InvalidParams.With(err) return nil, logErr(engine.InvalidParams.With(err))
} }
res := make([]*engine.BlobAndProofV1, len(hashes)) res := make([]*engine.BlobAndProofV1, len(hashes))
for i := 0; i < len(blobs); i++ { for i := 0; i < len(blobs); i++ {
@ -530,7 +530,7 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo
// blob pool data. // blob pool data.
func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProofV2, error) { func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProofV2, error) {
if len(hashes) > 128 { if len(hashes) > 128 {
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) return nil, logErr(engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))))
} }
available := api.eth.BlobTxPool().AvailableBlobs(hashes) available := api.eth.BlobTxPool().AvailableBlobs(hashes)
getBlobsRequestedCounter.Inc(int64(len(hashes))) getBlobsRequestedCounter.Inc(int64(len(hashes)))
@ -545,7 +545,7 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo
blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1) blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1)
if err != nil { if err != nil {
return nil, engine.InvalidParams.With(err) return nil, logErr(engine.InvalidParams.With(err))
} }
res := make([]*engine.BlobAndProofV2, len(hashes)) res := make([]*engine.BlobAndProofV2, len(hashes))
for i := 0; i < len(blobs); i++ { for i := 0; i < len(blobs); i++ {
@ -638,7 +638,7 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas
} }
requests := convertRequests(executionRequests) requests := convertRequests(executionRequests)
if err := validateRequests(requests); err != nil { if err := validateRequests(requests); err != nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) return engine.PayloadStatusV1{Status: engine.INVALID}, logErr(engine.InvalidParams.With(err))
} }
return api.newPayload(params, versionedHashes, beaconRoot, requests, false) return api.newPayload(params, versionedHashes, beaconRoot, requests, false)
} }
@ -1005,10 +1005,10 @@ func (api *ConsensusAPI) GetPayloadBodiesByRangeV2(start, count hexutil.Uint64)
func (api *ConsensusAPI) getBodiesByRange(start, count hexutil.Uint64) ([]*engine.ExecutionPayloadBody, error) { func (api *ConsensusAPI) getBodiesByRange(start, count hexutil.Uint64) ([]*engine.ExecutionPayloadBody, error) {
if start == 0 || count == 0 { if start == 0 || count == 0 {
return nil, engine.InvalidParams.With(fmt.Errorf("invalid start or count, start: %v count: %v", start, count)) return nil, logErr(engine.InvalidParams.With(fmt.Errorf("invalid start or count, start: %v count: %v", start, count)))
} }
if count > 1024 { if count > 1024 {
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested count too large: %v", count)) return nil, logErr(engine.TooLargeRequest.With(fmt.Errorf("requested count too large: %v", count)))
} }
// limit count up until current // limit count up until current
current := api.eth.BlockChain().CurrentBlock().Number.Uint64() current := api.eth.BlockChain().CurrentBlock().Number.Uint64()
@ -1073,20 +1073,34 @@ func validateRequests(requests [][]byte) error {
return nil return nil
} }
// logErr is a helper to log errors passed on the API surface on our side as well
func logErr(err error) error {
if err != nil {
log.Trace("Engine API error", "err", err)
}
return err
}
// paramsErr is a helper function for creating an InvalidPayloadAttributes // paramsErr is a helper function for creating an InvalidPayloadAttributes
// Engine API error. // Engine API error.
func paramsErr(msg string) error { func paramsErr(msg string) error {
return engine.InvalidParams.With(errors.New(msg)) return logErr(engine.InvalidParams.With(errors.New(msg)))
} }
// attributesErr is a helper function for creating an InvalidPayloadAttributes // attributesErr is a helper function for creating an InvalidPayloadAttributes
// Engine API error. // Engine API error.
func attributesErr(msg string) error { func attributesErr(msg string) error {
return engine.InvalidPayloadAttributes.With(errors.New(msg)) return logErr(engine.InvalidPayloadAttributes.With(errors.New(msg)))
} }
// unsupportedForkErr is a helper function for creating an UnsupportedFork // unsupportedForkErr is a helper function for creating an UnsupportedFork
// Engine API error. // Engine API error.
func unsupportedForkErr(msg string) error { func unsupportedForkErr(msg string) error {
return engine.UnsupportedFork.With(errors.New(msg)) return logErr(engine.UnsupportedFork.With(errors.New(msg)))
}
// invalidForkChoiceStateErr is a helper function for creating an InvalidForkChoiceState
// Engine API error.
func invalidForkChoiceStateErr(msg string) error {
return logErr(engine.InvalidForkChoiceState.With(errors.New(msg)))
} }