all: fix invalid signer list on checkpoint block for trace api #1185 (#1186)

This commit is contained in:
Daniel Liu 2025-07-12 06:56:30 +08:00 committed by GitHub
parent ae006030c6
commit c46302cf65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 52 additions and 45 deletions

View file

@ -190,12 +190,12 @@ func (x *XDPoS) Author(header *types.Header) (common.Address, error) {
}
// VerifyHeader checks whether a header conforms to the consensus rules.
func (x *XDPoS) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
func (x *XDPoS) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool, verifyCheckpoint bool) error {
switch x.config.BlockConsensusVersion(header.Number, header.Extra, ExtraFieldCheck) {
case params.ConsensusEngineVersion2:
return x.EngineV2.VerifyHeader(chain, header, fullVerify)
default: // Default "v1"
return x.EngineV1.VerifyHeader(chain, header, fullVerify)
return x.EngineV1.VerifyHeader(chain, header, fullVerify, verifyCheckpoint)
}
}

View file

@ -112,8 +112,8 @@ func (x *XDPoS_v1) Author(header *types.Header) (common.Address, error) {
}
// VerifyHeader checks whether a header conforms to the consensus rules.
func (x *XDPoS_v1) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
return x.verifyHeaderWithCache(chain, header, nil, fullVerify)
func (x *XDPoS_v1) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool, verifyCheckpoint bool) error {
return x.verifyHeaderWithCache(chain, header, nil, fullVerify, verifyCheckpoint)
}
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
@ -122,7 +122,7 @@ func (x *XDPoS_v1) VerifyHeader(chain consensus.ChainReader, header *types.Heade
func (x *XDPoS_v1) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool, abort <-chan struct{}, results chan<- error) {
go func() {
for i, header := range headers {
err := x.verifyHeaderWithCache(chain, header, headers[:i], fullVerifies[i])
err := x.verifyHeaderWithCache(chain, header, headers[:i], fullVerifies[i], true)
select {
case <-abort:
@ -133,12 +133,12 @@ func (x *XDPoS_v1) VerifyHeaders(chain consensus.ChainReader, headers []*types.H
}()
}
func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool, verifyCheckpoint bool) error {
_, check := x.verifiedHeaders.Get(header.Hash())
if check {
return nil
}
err := x.verifyHeader(chain, header, parents, fullVerify)
err := x.verifyHeader(chain, header, parents, fullVerify, verifyCheckpoint)
if err == nil {
x.verifiedHeaders.Add(header.Hash(), struct{}{})
}
@ -149,7 +149,7 @@ func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *ty
// caller may optionally pass in a batch of parents (ascending order) to avoid
// looking those up from the database. This is useful for concurrently verifying
// a batch of new headers.
func (x *XDPoS_v1) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
func (x *XDPoS_v1) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool, verifyCheckpoint bool) error {
// If we're running a engine faking, accept any block as valid
if x.config.SkipV1Validation {
return nil
@ -207,14 +207,14 @@ func (x *XDPoS_v1) verifyHeader(chain consensus.ChainReader, header *types.Heade
return utils.ErrInvalidUncleHash
}
// All basic checks passed, verify cascading fields
return x.verifyCascadingFields(chain, header, parents, fullVerify)
return x.verifyCascadingFields(chain, header, parents, fullVerify, verifyCheckpoint)
}
// verifyCascadingFields verifies all the header fields that are not standalone,
// rather depend on a batch of previous headers. The caller may optionally pass
// in a batch of parents (ascending order) to avoid looking those up from the
// database. This is useful for concurrently verifying a batch of new headers.
func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool, verifyCheckpoint bool) error {
// The genesis block is the always valid dead-end
number := header.Number.Uint64()
if number == 0 {
@ -253,7 +253,7 @@ func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *ty
}
signers := snap.GetSigners()
err = x.checkSignersOnCheckpoint(chain, header, signers)
err = x.checkSignersOnCheckpoint(chain, header, signers, verifyCheckpoint)
if err == nil {
return x.verifySeal(chain, header, parents, fullVerify)
}
@ -262,7 +262,7 @@ func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *ty
if err != nil {
return err
}
err = x.checkSignersOnCheckpoint(chain, header, signers)
err = x.checkSignersOnCheckpoint(chain, header, signers, verifyCheckpoint)
if err == nil {
return x.verifySeal(chain, header, parents, fullVerify)
}
@ -270,7 +270,7 @@ func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *ty
return err
}
func (x *XDPoS_v1) checkSignersOnCheckpoint(chain consensus.ChainReader, header *types.Header, signers []common.Address) error {
func (x *XDPoS_v1) checkSignersOnCheckpoint(chain consensus.ChainReader, header *types.Header, signers []common.Address, verifyCheckpoint bool) error {
number := header.Number.Uint64()
// ignore signerCheck at checkpoint block.
if common.IsIgnoreSignerCheckBlock(number) {
@ -301,6 +301,12 @@ func (x *XDPoS_v1) checkSignersOnCheckpoint(chain consensus.ChainReader, header
signers = removePenaltiesFromBlock(chain, signers, number-uint64(i)*x.config.Epoch)
}
}
// fix issue #1185
if !verifyCheckpoint {
return nil
}
extraSuffix := len(header.Extra) - utils.ExtraSeal
masternodesFromCheckpointHeader := common.ExtractAddressFromBytes(header.Extra[utils.ExtraVanity:extraSuffix])
validSigners := utils.CompareSignersLists(masternodesFromCheckpointHeader, signers)
@ -490,7 +496,7 @@ func (x *XDPoS_v1) snapshot(chain consensus.ChainReader, number uint64, hash com
// If we're at block zero, make a snapshot
if number == 0 {
genesis := chain.GetHeaderByNumber(0)
if err := x.VerifyHeader(chain, genesis, true); err != nil {
if err := x.VerifyHeader(chain, genesis, true, true); err != nil {
return nil, err
}
signers := make([]common.Address, (len(genesis.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength)

View file

@ -59,7 +59,7 @@ type Engine interface {
// VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
VerifyHeader(chain ChainReader, header *types.Header, fullVerify bool) error
VerifyHeader(chain ChainReader, header *types.Header, fullVerify bool, verifyCheckpoint bool) error
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and

View file

@ -68,7 +68,7 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) {
// VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool, verifyCheckpoint bool) error {
// If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake {
return nil

View file

@ -33,77 +33,77 @@ func TestShouldVerifyBlock(t *testing.T) {
// Happy path
happyPathHeader := blockchain.GetBlockByNumber(901).Header()
err = adaptor.VerifyHeader(blockchain, happyPathHeader, true)
err = adaptor.VerifyHeader(blockchain, happyPathHeader, true, true)
assert.Nil(t, err)
// Unhappy path
// Verify non-epoch switch block
err = adaptor.VerifyHeader(blockchain, blockchain.GetBlockByNumber(902).Header(), true)
err = adaptor.VerifyHeader(blockchain, blockchain.GetBlockByNumber(902).Header(), true, true)
assert.Nil(t, err)
nonEpochSwitchWithValidators := blockchain.GetBlockByNumber(902).Header()
nonEpochSwitchWithValidators.Validators = acc1Addr.Bytes()
err = adaptor.VerifyHeader(blockchain, nonEpochSwitchWithValidators, true)
err = adaptor.VerifyHeader(blockchain, nonEpochSwitchWithValidators, true, true)
assert.Equal(t, utils.ErrInvalidFieldInNonEpochSwitch, err)
noValidatorBlock := blockchain.GetBlockByNumber(902).Header()
noValidatorBlock.Validator = []byte{}
err = adaptor.VerifyHeader(blockchain, noValidatorBlock, true)
err = adaptor.VerifyHeader(blockchain, noValidatorBlock, true, true)
assert.Equal(t, consensus.ErrNoValidatorSignatureV2, err)
blockFromFuture := blockchain.GetBlockByNumber(902).Header()
blockFromFuture.Time = big.NewInt(time.Now().Unix() + 10000)
err = adaptor.VerifyHeader(blockchain, blockFromFuture, true)
err = adaptor.VerifyHeader(blockchain, blockFromFuture, true, true)
assert.Equal(t, consensus.ErrFutureBlock, err)
invalidQcBlock := blockchain.GetBlockByNumber(902).Header()
invalidQcBlock.Extra = []byte{2}
err = adaptor.VerifyHeader(blockchain, invalidQcBlock, true)
err = adaptor.VerifyHeader(blockchain, invalidQcBlock, true, true)
assert.Equal(t, utils.ErrInvalidV2Extra, err)
// Epoch switch
invalidAuthNonceBlock := blockchain.GetBlockByNumber(901).Header()
invalidAuthNonceBlock.Nonce = types.BlockNonce{123}
err = adaptor.VerifyHeader(blockchain, invalidAuthNonceBlock, true)
err = adaptor.VerifyHeader(blockchain, invalidAuthNonceBlock, true, true)
assert.Equal(t, utils.ErrInvalidVote, err)
emptyValidatorsBlock := blockchain.GetBlockByNumber(901).Header()
emptyValidatorsBlock.Validators = []byte{}
err = adaptor.VerifyHeader(blockchain, emptyValidatorsBlock, true)
err = adaptor.VerifyHeader(blockchain, emptyValidatorsBlock, true, true)
assert.Equal(t, utils.ErrEmptyEpochSwitchValidators, err)
invalidValidatorsSignerBlock := blockchain.GetBlockByNumber(901).Header()
invalidValidatorsSignerBlock.Validators = []byte{123}
err = adaptor.VerifyHeader(blockchain, invalidValidatorsSignerBlock, true)
err = adaptor.VerifyHeader(blockchain, invalidValidatorsSignerBlock, true, true)
assert.Equal(t, utils.ErrInvalidCheckpointSigners, err)
// non-epoch switch
invalidValidatorsExistBlock := blockchain.GetBlockByNumber(902).Header()
invalidValidatorsExistBlock.Validators = []byte{123}
err = adaptor.VerifyHeader(blockchain, invalidValidatorsExistBlock, true)
err = adaptor.VerifyHeader(blockchain, invalidValidatorsExistBlock, true, true)
assert.Equal(t, utils.ErrInvalidFieldInNonEpochSwitch, err)
invalidPenaltiesExistBlock := blockchain.GetBlockByNumber(902).Header()
invalidPenaltiesExistBlock.Penalties = common.Hex2BytesFixed("123131231", 20)
err = adaptor.VerifyHeader(blockchain, invalidPenaltiesExistBlock, true)
err = adaptor.VerifyHeader(blockchain, invalidPenaltiesExistBlock, true, true)
assert.Equal(t, utils.ErrInvalidFieldInNonEpochSwitch, err)
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb123"
parentNotExistBlock := blockchain.GetBlockByNumber(901).Header()
parentNotExistBlock.ParentHash = common.HexToHash(merkleRoot)
err = adaptor.VerifyHeader(blockchain, parentNotExistBlock, true)
err = adaptor.VerifyHeader(blockchain, parentNotExistBlock, true, true)
assert.Equal(t, consensus.ErrUnknownAncestor, err)
block901 := blockchain.GetBlockByNumber(901).Header()
tooFastMinedBlock := blockchain.GetBlockByNumber(902).Header()
tooFastMinedBlock.Time = big.NewInt(block901.Time.Int64() - 10)
err = adaptor.VerifyHeader(blockchain, tooFastMinedBlock, true)
err = adaptor.VerifyHeader(blockchain, tooFastMinedBlock, true, true)
assert.Equal(t, utils.ErrInvalidTimestamp, err)
invalidDifficultyBlock := blockchain.GetBlockByNumber(902).Header()
invalidDifficultyBlock.Difficulty = big.NewInt(2)
err = adaptor.VerifyHeader(blockchain, invalidDifficultyBlock, true)
err = adaptor.VerifyHeader(blockchain, invalidDifficultyBlock, true, true)
assert.Equal(t, utils.ErrInvalidDifficulty, err)
// Create an invalid QC round
@ -144,7 +144,7 @@ func TestShouldVerifyBlock(t *testing.T) {
invalidRoundBlock := blockchain.GetBlockByNumber(902).Header()
invalidRoundBlock.Extra = extraInBytes
err = adaptor.VerifyHeader(blockchain, invalidRoundBlock, true)
err = adaptor.VerifyHeader(blockchain, invalidRoundBlock, true, true)
assert.Equal(t, utils.ErrRoundInvalid, err)
// Not valid validator
@ -152,19 +152,19 @@ func TestShouldVerifyBlock(t *testing.T) {
notQualifiedSigner, notQualifiedSignFn, err := getSignerAndSignFn(voterKey)
assert.Nil(t, err)
sealHeader(blockchain, coinbaseValidatorMismatchBlock, notQualifiedSigner, notQualifiedSignFn)
err = adaptor.VerifyHeader(blockchain, coinbaseValidatorMismatchBlock, true)
err = adaptor.VerifyHeader(blockchain, coinbaseValidatorMismatchBlock, true, true)
assert.Equal(t, utils.ErrCoinbaseAndValidatorMismatch, err)
// Make the validators not legit by adding something to the validator
validatorsNotLegit := blockchain.GetBlockByNumber(901).Header()
validatorsNotLegit.Validators = append(validatorsNotLegit.Validators, acc1Addr[:]...)
err = adaptor.VerifyHeader(blockchain, validatorsNotLegit, true)
err = adaptor.VerifyHeader(blockchain, validatorsNotLegit, true, true)
assert.Equal(t, utils.ErrValidatorsNotLegit, err)
// Make the penalties not legit by adding something to the penalty
penaltiesNotLegit := blockchain.GetBlockByNumber(901).Header()
penaltiesNotLegit.Penalties = append(penaltiesNotLegit.Penalties, acc1Addr[:]...)
err = adaptor.VerifyHeader(blockchain, penaltiesNotLegit, true)
err = adaptor.VerifyHeader(blockchain, penaltiesNotLegit, true, true)
assert.Equal(t, utils.ErrPenaltiesNotLegit, err)
}
@ -215,7 +215,7 @@ func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) {
// after 910 require 4 signs, but we only give 3 signs
block912 := blockchain.GetBlockByNumber(912).Header()
block912.Extra = extraInBytes
err = adaptor.VerifyHeader(blockchain, block912, true)
err = adaptor.VerifyHeader(blockchain, block912, true, true)
assert.Equal(t, utils.ErrInvalidQCSignatures, err)
@ -254,7 +254,7 @@ func TestConfigSwitchOnDifferentCertThreshold(t *testing.T) {
// QC contains 910, so it requires 3 signatures, not use block number to determine which config to use
block911 := blockchain.GetBlockByNumber(911).Header()
block911.Extra = extraInBytes
err = adaptor.VerifyHeader(blockchain, block911, true)
err = adaptor.VerifyHeader(blockchain, block911, true, true)
// error ErrValidatorNotWithinMasternodes means verifyQC is passed and move to next verification process
assert.Equal(t, utils.ErrValidatorNotWithinMasternodes, err)
@ -298,7 +298,7 @@ func TestConfigSwitchOnDifferentMasternodeCount(t *testing.T) {
adaptor.EngineV2.SetNewRoundFaker(blockchain, 899, false)
err = adaptor.VerifyHeader(blockchain, header1800, true)
err = adaptor.VerifyHeader(blockchain, header1800, true, true)
// error ErrValidatorNotWithinMasternodes means verifyQC is passed and move to next verification process
assert.Equal(t, utils.ErrValidatorNotWithinMasternodes, err)
@ -352,7 +352,7 @@ func TestConfigSwitchOnDifferentMindPeriod(t *testing.T) {
block911 := blockchain.GetBlockByNumber(911).Header()
block911.Extra = extraInBytes
block911.Time = big.NewInt(blockchain.GetBlockByNumber(910).Time().Int64() + 2) //2 is previous config, should get the right config from round
err = adaptor.VerifyHeader(blockchain, block911, true)
err = adaptor.VerifyHeader(blockchain, block911, true, true)
assert.Equal(t, utils.ErrInvalidTimestamp, err)
}
@ -403,7 +403,7 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
headerWithDuplicatedSignatures := currentBlock.Header()
headerWithDuplicatedSignatures.Extra = extraInBytes
// Happy path
err = adaptor.VerifyHeader(blockchain, headerWithDuplicatedSignatures, true)
err = adaptor.VerifyHeader(blockchain, headerWithDuplicatedSignatures, true, true)
assert.Equal(t, utils.ErrInvalidQCSignatures, err)
}

View file

@ -1988,7 +1988,7 @@ func (bc *BlockChain) PrepareBlock(block *types.Block) (err error) {
log.Debug("Stop prepare a block because inserting", "number", block.NumberU64(), "hash", block.Hash(), "validator", block.Header().Validator)
return nil
}
err = bc.engine.VerifyHeader(bc, block.Header(), false)
err = bc.engine.VerifyHeader(bc, block.Header(), false, true)
if err != nil {
return err
}

View file

@ -140,7 +140,7 @@ func printChain(bc *BlockChain) {
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
for _, block := range chain {
// Try and process the block
err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true)
err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true, true)
if err == nil {
err = blockchain.validator.ValidateBody(block)
}
@ -178,7 +178,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
for _, header := range chain {
// Try and validate the header
if err := blockchain.engine.VerifyHeader(blockchain, header, false); err != nil {
if err := blockchain.engine.VerifyHeader(blockchain, header, false, true); err != nil {
return err
}
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)

View file

@ -432,10 +432,11 @@ func (api *DebugAPI) TraceBlockFromFile(ctx context.Context, file string, config
// executes all the transactions contained within. The return value will be one item
// per transaction, dependent on the requestd tracer.
func (api *DebugAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
// Create the parent state database
if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil {
// skip verify signer list on checkpoint block to fix #1185
if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true, false); err != nil {
return nil, err
}
// Create the parent state database
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, fmt.Errorf("parent %x not found", block.ParentHash())

View file

@ -211,7 +211,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
manager.downloader = downloader.New(chaindb, manager.eventMux, blockchain, nil, manager.removePeer, handleProposedBlock)
validator := func(header *types.Header) error {
return engine.VerifyHeader(blockchain, header, true)
return engine.VerifyHeader(blockchain, header, true, true)
}
heighter := func() uint64 {