Skip to content

Commit 481bdce

Browse files
MariusVanDerWijdencp-wjhan
authored andcommitted
eth/catalyst: update implementation to spec (ethereum#24802)
* eth/catalyst: return invalid payload attributes error * eth/catalyst: implement LVH as specified, add tests * eth/catalyst: return current block hash not header hash * eth/catalyst: fix test * eth/catalyst: bring error codes in line with spec
1 parent 5e81224 commit 481bdce

File tree

4 files changed

+283
-85
lines changed

4 files changed

+283
-85
lines changed

core/beacon/errors.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ var (
4343

4444
INVALIDBLOCKHASH = "INVALID_BLOCK_HASH"
4545

46-
GenericServerError = rpc.CustomError{Code: -32000, ValidationError: "Server error"}
47-
UnknownPayload = rpc.CustomError{Code: -32001, ValidationError: "Unknown payload"}
48-
InvalidTB = rpc.CustomError{Code: -32002, ValidationError: "Invalid terminal block"}
46+
GenericServerError = rpc.CustomError{Code: -32000, ValidationError: "Server error"}
47+
UnknownPayload = rpc.CustomError{Code: -38001, ValidationError: "Unknown payload"}
48+
InvalidForkChoiceState = rpc.CustomError{Code: -38002, ValidationError: "Invalid forkchoice state"}
49+
InvalidPayloadAttributes = rpc.CustomError{Code: -38003, ValidationError: "Invalid payload attributes"}
4950

5051
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
5152
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}

eth/catalyst/api.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/ethereum/go-ethereum/common/hexutil"
3030
"github.com/ethereum/go-ethereum/core/beacon"
3131
"github.com/ethereum/go-ethereum/core/rawdb"
32+
"github.com/ethereum/go-ethereum/core/types"
3233
"github.com/ethereum/go-ethereum/eth"
3334
"github.com/ethereum/go-ethereum/log"
3435
"github.com/ethereum/go-ethereum/node"
@@ -172,10 +173,10 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
172173
finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash)
173174
if finalBlock == nil {
174175
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
175-
return beacon.STATUS_INVALID, errors.New("final block not available")
176+
return beacon.STATUS_INVALID, &beacon.InvalidForkChoiceState
176177
} else if rawdb.ReadCanonicalHash(api.eth.ChainDb(), finalBlock.NumberU64()) != update.FinalizedBlockHash {
177178
log.Warn("Final block not in canonical chain", "number", block.NumberU64(), "hash", update.HeadBlockHash)
178-
return beacon.STATUS_INVALID, errors.New("final block not canonical")
179+
return beacon.STATUS_INVALID, &beacon.InvalidForkChoiceState
179180
}
180181
// Set the finalized block
181182
api.eth.BlockChain().SetFinalized(finalBlock)
@@ -185,11 +186,11 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
185186
safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash)
186187
if safeBlock == nil {
187188
log.Warn("Safe block not available in database")
188-
return beacon.STATUS_INVALID, errors.New("safe head not available")
189+
return beacon.STATUS_INVALID, &beacon.InvalidForkChoiceState
189190
}
190191
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), safeBlock.NumberU64()) != update.SafeBlockHash {
191192
log.Warn("Safe block not in canonical chain")
192-
return beacon.STATUS_INVALID, errors.New("safe head not canonical")
193+
return beacon.STATUS_INVALID, &beacon.InvalidForkChoiceState
193194
}
194195
}
195196

@@ -207,13 +208,15 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
207208
// Create an empty block first which can be used as a fallback
208209
empty, err := api.eth.Miner().GetSealingBlockSync(update.HeadBlockHash, payloadAttributes.Timestamp, payloadAttributes.SuggestedFeeRecipient, payloadAttributes.Random, true)
209210
if err != nil {
210-
return valid(nil), err
211+
log.Error("Failed to create empty sealing payload", "err", err)
212+
return valid(nil), &beacon.InvalidPayloadAttributes
211213
}
212214
// Send a request to generate a full block in the background.
213215
// The result can be obtained via the returned channel.
214216
resCh, err := api.eth.Miner().GetSealingBlockAsync(update.HeadBlockHash, payloadAttributes.Timestamp, payloadAttributes.SuggestedFeeRecipient, payloadAttributes.Random, false)
215217
if err != nil {
216-
return valid(nil), err
218+
log.Error("Failed to create async sealing payload", "err", err)
219+
return valid(nil), &beacon.InvalidPayloadAttributes
217220
}
218221
id := computePayloadId(update.HeadBlockHash, payloadAttributes)
219222
api.localBlocks.put(id, &payload{empty: empty, result: resCh})
@@ -310,7 +313,7 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
310313
}
311314
if block.Time() <= parent.Time() {
312315
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
313-
return api.invalid(errors.New("invalid timestamp")), nil
316+
return api.invalid(errors.New("invalid timestamp"), parent), nil
314317
}
315318
if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
316319
api.remoteBlocks.put(block.Hash(), block.Header())
@@ -320,7 +323,7 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
320323
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number)
321324
if err := api.eth.BlockChain().InsertBlockWithoutSetHead(block); err != nil {
322325
log.Warn("NewPayloadV1: inserting block failed", "error", err)
323-
return api.invalid(err), nil
326+
return api.invalid(err, parent), nil
324327
}
325328
// We've accepted a valid payload from the beacon client. Mark the local
326329
// chain transitions to notify other subsystems (e.g. downloader) of the
@@ -346,9 +349,13 @@ func computePayloadId(headBlockHash common.Hash, params *beacon.PayloadAttribute
346349
return out
347350
}
348351

349-
// invalid returns a response "INVALID" with the latest valid hash set to the current head.
350-
func (api *ConsensusAPI) invalid(err error) beacon.PayloadStatusV1 {
351-
currentHash := api.eth.BlockChain().CurrentHeader().Hash()
352+
// invalid returns a response "INVALID" with the latest valid hash supplied by latest or to the current head
353+
// if no latestValid block was provided.
354+
func (api *ConsensusAPI) invalid(err error, latestValid *types.Block) beacon.PayloadStatusV1 {
355+
currentHash := api.eth.BlockChain().CurrentBlock().Hash()
356+
if latestValid != nil {
357+
currentHash = latestValid.Hash()
358+
}
352359
errorMsg := err.Error()
353360
return beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: &currentHash, ValidationError: &errorMsg}
354361
}

0 commit comments

Comments
 (0)