package clique import ( "bytes" "fmt" "time" libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/common/length" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/consensus/misc" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/params" ) // verifyHeader checks whether a header conforms to the consensus rules.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 (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { if header.Number == nil { return errUnknownBlock } number := header.Number.Uint64() now := time.Now() nowUnix := now.Unix() // Don't waste time checking blocks from the future if header.Time > uint64(nowUnix) { return consensus.ErrFutureBlock } // Checkpoint blocks need to enforce zero beneficiary checkpoint := (number % c.config.Epoch) == 0 if checkpoint && header.Coinbase != (libcommon.Address{}) { return errInvalidCheckpointBeneficiary } // Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints if !bytes.Equal(header.Nonce[:], NonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) { return errInvalidVote } if checkpoint && !bytes.Equal(header.Nonce[:], nonceDropVote) { return errInvalidCheckpointVote } // Check that the extra-data contains both the vanity and signature if len(header.Extra) < ExtraVanity { return errMissingVanity } if len(header.Extra) < ExtraVanity+ExtraSeal { return errMissingSignature } // Ensure that the extra-data contains a signer list on checkpoint, but none otherwise signersBytes := len(header.Extra) - ExtraVanity - ExtraSeal if !checkpoint && signersBytes != 0 { return errExtraSigners } if checkpoint && signersBytes%length.Addr != 0 { return errInvalidCheckpointSigners } // Ensure that the mix digest is zero as we don't have fork protection currently if header.MixDigest != (libcommon.Hash{}) { return errInvalidMixDigest } // Ensure that the block doesn't contain any uncles which are meaningless in PoA if header.UncleHash != uncleHash { return errInvalidUncleHash } // Ensure that the block's difficulty is meaningful (may not be correct at this point) if number > 0 { if header.Difficulty == nil || (header.Difficulty.Cmp(DiffInTurn) != 0 && header.Difficulty.Cmp(diffNoTurn) != 0) { return errInvalidDifficulty } } if header.WithdrawalsHash != nil { return consensus.ErrUnexpectedWithdrawals } // All basic checks passed, verify cascading fields return c.verifyCascadingFields(chain, header, parents) } // 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 (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { // The genesis block is the always valid dead-end number := header.Number.Uint64() if number == 0 { return nil } // Retrieve the snapshot needed to verify this header and cache it var parent *types.Header if len(parents) > 0 { parent = parents[len(parents)-1] } else { parent = chain.GetHeader(header.ParentHash, number-1) } if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { return consensus.ErrUnknownAncestor } if parent.Time+c.config.Period > header.Time { return errInvalidTimestamp } if !chain.Config().IsLondon(header.Number.Uint64()) { // Verify BaseFee not present before EIP-1559 fork. if header.BaseFee != nil { return fmt.Errorf("invalid baseFee before fork: have %d, want ", header.BaseFee) } if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil { return err } } else if err := misc.VerifyEip1559Header(chain.Config(), parent, header, false /*skipGasLimit*/); err != nil { // Verify the header's EIP-1559 attributes. return err } if err := misc.VerifyAbsenceOfCancunHeaderFields(header); err != nil { return err } // Retrieve the snapshot needed to verify this header and cache it snap, err := c.Snapshot(chain, number-1, header.ParentHash, parents) if err != nil { return err } // If the block is a checkpoint block, verify the signer list if number%c.config.Epoch == 0 { signers := make([]byte, len(snap.Signers)*length.Addr) for i, signer := range snap.GetSigners() { copy(signers[i*length.Addr:], signer[:]) } extraSuffix := len(header.Extra) - ExtraSeal if !bytes.Equal(header.Extra[ExtraVanity:extraSuffix], signers) { return errMismatchingCheckpointSigners } } // All basic checks passed, verify the seal and return return c.verifySeal(chain, header, snap) } func (c *Clique) Snapshot(chain consensus.ChainHeaderReader, number uint64, hash libcommon.Hash, parents []*types.Header) (*Snapshot, error) { // Search for a snapshot in memory or on disk for checkpoints var ( headers []*types.Header snap *Snapshot ) for snap == nil { // If an in-memory snapshot was found, use that if s, ok := c.recents.Get(hash); ok { snap = s break } // If an on-disk checkpoint snapshot can be found, use that if number%c.snapshotConfig.CheckpointInterval == 0 { if s, err := loadSnapshot(c.config, c.DB, number, hash); err == nil { c.logger.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash) snap = s break } } // If we're at the genesis, snapshot the initial state. Alternatively if we're // at a checkpoint block without a parent (light client CHT), or we have piled // up more headers than allowed to be reorged (chain reinit from a freezer), // consider the checkpoint trusted and snapshot it. if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) { checkpoint := chain.GetHeaderByNumber(number) if checkpoint != nil { hash := checkpoint.Hash() signers := make([]libcommon.Address, (len(checkpoint.Extra)-ExtraVanity-ExtraSeal)/length.Addr) for i := 0; i < len(signers); i++ { copy(signers[i][:], checkpoint.Extra[ExtraVanity+i*length.Addr:]) } snap = newSnapshot(c.config, number, hash, signers) if err := snap.store(c.DB); err != nil { return nil, err } c.logger.Info("[Clique] Stored checkpoint snapshot to disk", "number", number, "hash", hash) break } } // No snapshot for this header, gather the header and move backward var header *types.Header if len(parents) > 0 { // If we have explicit parents, pick from there (enforced) header = parents[len(parents)-1] if header.Hash() != hash || header.Number.Uint64() != number { return nil, consensus.ErrUnknownAncestor } parents = parents[:len(parents)-1] } else { // No explicit parents (or no more left), reach out to the database header = chain.GetHeader(hash, number) if header == nil { return nil, consensus.ErrUnknownAncestor } } headers = append(headers, header) number, hash = number-1, header.ParentHash } // Previous snapshot found, apply any pending headers on top of it for i := 0; i < len(headers)/2; i++ { headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] } snap, err := snap.apply(c.signatures, c.logger, headers...) if err != nil { return nil, err } c.recents.Add(snap.Hash, snap) // If we've generated a new checkpoint snapshot, save to disk if snap.Number%c.snapshotConfig.CheckpointInterval == 0 && len(headers) > 0 { if err = snap.store(c.DB); err != nil { return nil, err } c.logger.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) } return snap, err } // verifySeal checks whether the signature contained in the header satisfies the // consensus protocol requirements. The method accepts an optional list of parent // headers that aren't yet part of the local blockchain to generate the snapshots // from. func (c *Clique) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, snap *Snapshot) error { // Verifying the genesis block is not supported number := header.Number.Uint64() if number == 0 { return errUnknownBlock } // Resolve the authorization key and check against signers signer, err := ecrecover(header, c.signatures) if err != nil { return err } if _, ok := snap.Signers[signer]; !ok { return ErrUnauthorizedSigner } for seen, recent := range snap.Recents { if recent == signer { // Signer is among RecentsRLP, only fail if the current block doesn't shift it out if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit { return ErrRecentlySigned } } } // Ensure that the difficulty corresponds to the turn-ness of the signer if !c.FakeDiff { inturn := snap.inturn(header.Number.Uint64(), signer) if inturn && header.Difficulty.Cmp(DiffInTurn) != 0 { return errWrongDifficulty } if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 { return errWrongDifficulty } } return nil }