From e90e03ae31606e64d407547fddc8fc5551da649a Mon Sep 17 00:00:00 2001 From: Giulio rebuffo Date: Sun, 3 Jul 2022 11:58:57 +0200 Subject: [PATCH] Proper Pos block checker when INVALID/ACCEPTED status is sent (#4604) * added proper PoS block checker * proper invalid lvh * p * fixed smol thingy * fix more * fixed engine API * fixed engine API * better nil hash * added 0x0 checks * full support --- cmd/rpcdaemon/commands/engine_api.go | 3 ++ core/rawdb/accessors_chain.go | 14 ++++++++ eth/stagedsync/stage_headers.go | 38 +++++++++++++++++---- ethdb/privateapi/ethbackend.go | 4 +-- turbo/stages/headerdownload/header_algos.go | 12 ++++--- turbo/stages/stageloop.go | 9 +++++ 6 files changed, 67 insertions(+), 13 deletions(-) diff --git a/cmd/rpcdaemon/commands/engine_api.go b/cmd/rpcdaemon/commands/engine_api.go index 1d7984fa91a..2fd1621a72a 100644 --- a/cmd/rpcdaemon/commands/engine_api.go +++ b/cmd/rpcdaemon/commands/engine_api.go @@ -76,6 +76,9 @@ func convertPayloadStatus(x *remote.EnginePayloadStatus) map[string]interface{} json := map[string]interface{}{ "status": x.Status.String(), } + if x.Status == remote.EngineStatus_INVALID || x.Status == remote.EngineStatus_ACCEPTED { + json["latestValidHash"] = common.Hash{} + } if x.LatestValidHash != nil { json["latestValidHash"] = common.Hash(gointerfaces.ConvertH256ToHash(x.LatestValidHash)) } diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 5393535b61f..79d7da31804 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -1603,3 +1603,17 @@ func Transitioned(db kv.Getter, blockNum uint64, terminalTotalDifficulty *big.In return headerTd.Cmp(terminalTotalDifficulty) >= 0, nil } + +// IsPosBlock returns true if the block is a PoS block, aka. all blocks with null difficulty. +func IsPosBlock(db kv.Getter, blockHash common.Hash, blockNum uint64) (trans bool, err error) { + if blockNum == 0 { + return false, nil + } + + header := ReadHeader(db, blockHash, blockNum) + if header == nil { + return false, nil + } + + return header.Difficulty.Cmp(common.Big0) == 0, nil +} diff --git a/eth/stagedsync/stage_headers.go b/eth/stagedsync/stage_headers.go index fd0c70e8bd5..fe2e3d628f2 100644 --- a/eth/stagedsync/stage_headers.go +++ b/eth/stagedsync/stage_headers.go @@ -500,6 +500,11 @@ func handleNewPayload( }, nil } + isAncestorPos, err := rawdb.IsPosBlock(tx, header.ParentHash, headerNumber-1) + if err != nil { + return nil, err + } + parent, err := cfg.blockReader.HeaderByHash(ctx, tx, header.ParentHash) if err != nil { return nil, err @@ -514,9 +519,14 @@ func handleNewPayload( } if header.Number.Uint64() != parent.Number.Uint64()+1 { + latestValidHash := common.Hash{} + if isAncestorPos { + latestValidHash = header.ParentHash + } + cfg.hd.ReportBadHeaderPoS(headerHash, latestValidHash) return &privateapi.PayloadStatus{ Status: remote.EngineStatus_INVALID, - LatestValidHash: header.ParentHash, + LatestValidHash: latestValidHash, ValidationError: errors.New("invalid block number"), }, nil } @@ -525,10 +535,14 @@ func handleNewPayload( for _, tx := range payloadMessage.Body.Transactions { if types.TypedTransactionMarshalledAsRlpString(tx) { - cfg.hd.ReportBadHeaderPoS(headerHash, header.ParentHash) + latestValidHash := common.Hash{} + if isAncestorPos { + latestValidHash = header.ParentHash + } + cfg.hd.ReportBadHeaderPoS(headerHash, latestValidHash) return &privateapi.PayloadStatus{ Status: remote.EngineStatus_INVALID, - LatestValidHash: header.ParentHash, + LatestValidHash: latestValidHash, ValidationError: errors.New("typed txn marshalled as RLP string"), }, nil } @@ -537,7 +551,11 @@ func handleNewPayload( transactions, err := types.DecodeTransactions(payloadMessage.Body.Transactions) if err != nil { log.Warn("Error during Beacon transaction decoding", "err", err.Error()) - cfg.hd.ReportBadHeaderPoS(headerHash, header.ParentHash) + latestValidHash := common.Hash{} + if isAncestorPos { + latestValidHash = header.ParentHash + } + cfg.hd.ReportBadHeaderPoS(headerHash, latestValidHash) return &privateapi.PayloadStatus{ Status: remote.EngineStatus_INVALID, LatestValidHash: header.ParentHash, @@ -574,7 +592,15 @@ func verifyAndSaveNewPoSHeader( if verificationErr := cfg.hd.VerifyHeader(header); verificationErr != nil { log.Warn("Verification failed for header", "hash", headerHash, "height", headerNumber, "err", verificationErr) - cfg.hd.ReportBadHeaderPoS(headerHash, header.ParentHash) + isAncestorPos, err := rawdb.IsPosBlock(tx, header.ParentHash, headerNumber-1) + if err != nil { + return nil, false, err + } + latestValidHash := common.Hash{} + if isAncestorPos { + latestValidHash = header.ParentHash + } + cfg.hd.ReportBadHeaderPoS(headerHash, latestValidHash) return &privateapi.PayloadStatus{ Status: remote.EngineStatus_INVALID, LatestValidHash: header.ParentHash, @@ -616,7 +642,7 @@ func verifyAndSaveNewPoSHeader( } if cfg.memoryOverlay && (cfg.hd.GetNextForkHash() == (common.Hash{}) || header.ParentHash == cfg.hd.GetNextForkHash()) { - status, latestValidHash, validationError, criticalError := cfg.hd.ValidatePayload(tx, header, body, cfg.chainConfig.TerminalTotalDifficulty, true, cfg.execPayload) + status, latestValidHash, validationError, criticalError := cfg.hd.ValidatePayload(tx, header, body, true, cfg.execPayload) if criticalError != nil { return &privateapi.PayloadStatus{CriticalError: criticalError}, false, criticalError } diff --git a/ethdb/privateapi/ethbackend.go b/ethdb/privateapi/ethbackend.go index 079f3be97bc..5e43af9da9a 100644 --- a/ethdb/privateapi/ethbackend.go +++ b/ethdb/privateapi/ethbackend.go @@ -246,9 +246,7 @@ func (s *EthBackendServer) Block(ctx context.Context, req *remote.BlockRequest) func convertPayloadStatus(payloadStatus *PayloadStatus) *remote.EnginePayloadStatus { reply := remote.EnginePayloadStatus{Status: payloadStatus.Status} - if payloadStatus.LatestValidHash != (common.Hash{}) { - reply.LatestValidHash = gointerfaces.ConvertHashToH256(payloadStatus.LatestValidHash) - } + reply.LatestValidHash = gointerfaces.ConvertHashToH256(payloadStatus.LatestValidHash) if payloadStatus.ValidationError != nil { reply.ValidationError = payloadStatus.ValidationError.Error() } diff --git a/turbo/stages/headerdownload/header_algos.go b/turbo/stages/headerdownload/header_algos.go index c0ed7df24c5..9c36365bc43 100644 --- a/turbo/stages/headerdownload/header_algos.go +++ b/turbo/stages/headerdownload/header_algos.go @@ -1117,10 +1117,13 @@ func (hd *HeaderDownload) ValidatePayload(tx kv.RwTx, header *types.Header, body return } - isAncestorPosBlock, criticalError := rawdb.Transitioned(tx, header.Number.Uint64()-1, terminalTotalDifficulty) + // If the previous block is the transition block, then the latest valid hash is the 0x0 hash, in case we return INVALID + isAncestorPosBlock, criticalError := rawdb.IsPosBlock(tx, header.ParentHash, header.Number.Uint64()-1) if criticalError != nil { return } + _, isAncestorSideFork := hd.sideForksBlock[header.ParentHash] + if store { // If it is a continuation of the canonical chain we can stack it up. if hd.nextForkState == nil { @@ -1133,7 +1136,7 @@ func (hd *HeaderDownload) ValidatePayload(tx kv.RwTx, header *types.Header, body validationError = execPayload(hd.nextForkState, header, body, 0, nil, nil) if validationError != nil { status = remote.EngineStatus_INVALID - if isAncestorPosBlock { + if isAncestorPosBlock || isAncestorSideFork { latestValidHash = header.ParentHash } return @@ -1183,13 +1186,14 @@ func (hd *HeaderDownload) ValidatePayload(tx kv.RwTx, header *types.Header, body batch := memdb.NewMemoryBatch(tx) defer batch.Close() validationError = execPayload(batch, header, body, unwindPoint, headersChain, bodiesChain) - latestValidHash = header.Hash() if validationError != nil { - if isAncestorPosBlock { + if isAncestorPosBlock || isAncestorSideFork { latestValidHash = header.ParentHash } status = remote.EngineStatus_INVALID + return } + latestValidHash = header.Hash() // After the we finished executing, we clean up old forks hd.cleanupOutdateSideForks(*currentHeight, maxDepth) return diff --git a/turbo/stages/stageloop.go b/turbo/stages/stageloop.go index a93342e2dd2..b3416c1eded 100644 --- a/turbo/stages/stageloop.go +++ b/turbo/stages/stageloop.go @@ -188,7 +188,16 @@ func StageLoopStep( return headBlockHash, err } headBlockHash = rawdb.ReadHeadBlockHash(rotx) + headBlockNumber := rawdb.ReadCurrentBlockNumber(rotx) + isAncestorPosBlock, err := rawdb.IsPosBlock(rotx, headBlockHash, *headBlockNumber) + if err != nil { + return headBlockHash, err + } + + if !isAncestorPosBlock { + headBlockHash = common.Hash{} + } if canRunCycleInOneTransaction && snapshotMigratorFinal != nil { err = snapshotMigratorFinal(rotx) if err != nil {