diff --git a/node/core/config.go b/node/core/config.go index dbd6e19ca..8ce2f58c7 100644 --- a/node/core/config.go +++ b/node/core/config.go @@ -23,6 +23,8 @@ import ( ) var ( + MainnetBlsKeyCheckForkHeight uint64 = 18409547 + // L1 Mainnet Contract Addresses MainnetRollupContractAddress = common.HexToAddress("0x759894ced0e6af42c26668076ffa84d02e3cef60") ) @@ -33,6 +35,7 @@ type Config struct { SequencerAddress common.Address `json:"sequencer_address"` L2StakingAddress common.Address `json:"l2staking_address"` MaxL1MessageNumPerBlock uint64 `json:"max_l1_message_num_per_block"` + BlsKeyCheckForkHeight uint64 `json:"bls_key_check_fork_height"` DevSequencer bool `json:"dev_sequencer"` Logger tmlog.Logger `json:"logger"` } @@ -146,5 +149,10 @@ func (c *Config) SetCliContext(ctx *cli.Context) error { c.DevSequencer = ctx.GlobalBool(flags.DevSequencer.Name) } + if ctx.GlobalIsSet(flags.MainnetFlag.Name) { + c.BlsKeyCheckForkHeight = MainnetBlsKeyCheckForkHeight + logger.Info("mainnet historical validator compatibility enabled", "BlsKeyCheckForkHeight", c.BlsKeyCheckForkHeight) + } + return nil } diff --git a/node/core/executor.go b/node/core/executor.go index fa8bf0a88..086703e1b 100644 --- a/node/core/executor.go +++ b/node/core/executor.go @@ -45,6 +45,8 @@ type Executor struct { isSequencer bool devSequencer bool + blsKeyCheckForkHeight uint64 + logger tmlog.Logger metrics *Metrics } @@ -90,17 +92,18 @@ func NewExecutor(newSyncFunc NewSyncerFunc, config *Config, tmPubKey crypto.PubK tmPubKeyBytes = tmPubKey.Bytes() } executor := &Executor{ - l2Client: l2Client, - bc: &Version1Converter{}, - sequencerCaller: sequencer, - l2StakingCaller: l2Staking, - tmPubKey: tmPubKeyBytes, - nextL1MsgIndex: index, - maxL1MsgNumPerBlock: config.MaxL1MessageNumPerBlock, - newSyncerFunc: newSyncFunc, - devSequencer: config.DevSequencer, - logger: logger, - metrics: PrometheusMetrics("morphnode"), + l2Client: l2Client, + bc: &Version1Converter{}, + sequencerCaller: sequencer, + l2StakingCaller: l2Staking, + tmPubKey: tmPubKeyBytes, + nextL1MsgIndex: index, + maxL1MsgNumPerBlock: config.MaxL1MessageNumPerBlock, + newSyncerFunc: newSyncFunc, + devSequencer: config.DevSequencer, + blsKeyCheckForkHeight: config.BlsKeyCheckForkHeight, + logger: logger, + metrics: PrometheusMetrics("morphnode"), } if config.DevSequencer { @@ -356,6 +359,10 @@ func (e *Executor) getValidatorsAtHeight(height int64) ([][]byte, error) { } newValidators := make([][]byte, 0, len(addrs)) for i := range stakesInfo { + if !e.shouldKeepSequencerAtHeight(uint64(height), stakesInfo[i].BlsKey) { + e.logger.Error("getValidatorsAtHeight: skip sequencer with invalid bls key", "height", height, "addr", stakesInfo[i].Addr) + continue + } newValidators = append(newValidators, stakesInfo[i].TmKey[:]) } return newValidators, nil diff --git a/node/core/sequencers.go b/node/core/sequencers.go index e56b522b3..4c43a8cf6 100644 --- a/node/core/sequencers.go +++ b/node/core/sequencers.go @@ -5,6 +5,7 @@ import ( "slices" "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/crypto/bls12381" "github.com/tendermint/tendermint/crypto/ed25519" ) @@ -15,7 +16,7 @@ func (e *Executor) sequencerSetUpdates(height uint64) ([][]byte, error) { if err != nil { return nil, err } - if e.currentSeqHash != nil && bytes.Equal(e.currentSeqHash[:], seqHash[:]) { + if e.shouldReuseSequencerCache(height, seqHash) { return e.nextValidators, nil } @@ -49,6 +50,10 @@ func (e *Executor) sequencerSetUpdates(height uint64) ([][]byte, error) { seqTmKeySet := make(map[[tmKeySize]byte]struct{}, len(stakesInfo)) nextValidators := make([][]byte, 0, len(sequencerSet2)) for i := range stakesInfo { + if !e.shouldKeepSequencerAtHeight(height, stakesInfo[i].BlsKey) { + e.logger.Error("sequencerSetUpdates: skip sequencer with invalid bls key", "height", height, "addr", stakesInfo[i].Addr) + continue + } // sequencerSet2 is the latest updated sequencer set which is considered as the next validator set for tendermint if slices.Contains(sequencerSet2, stakesInfo[i].Addr) { nextValidators = append(nextValidators, stakesInfo[i].TmKey[:]) @@ -63,6 +68,26 @@ func (e *Executor) sequencerSetUpdates(height uint64) ([][]byte, error) { return nextValidators, nil } +func (e *Executor) shouldReuseSequencerCache(height uint64, seqHash [32]byte) bool { + if e.currentSeqHash == nil || !bytes.Equal(e.currentSeqHash[:], seqHash[:]) { + return false + } + + if e.blsKeyCheckForkHeight > 0 && + (height == e.blsKeyCheckForkHeight || height == e.blsKeyCheckForkHeight+1) { + return false + } + return true +} + +func (e *Executor) shouldKeepSequencerAtHeight(height uint64, blsKey []byte) bool { + if isValidBlsKey(blsKey) { + return true + } + + return e.blsKeyCheckForkHeight > 0 && height <= e.blsKeyCheckForkHeight +} + func (e *Executor) updateSequencerSet(height uint64) ([][]byte, error) { validatorUpdates, err := e.sequencerSetUpdates(height) if err != nil { @@ -94,3 +119,8 @@ func (e *Executor) updateSequencerSet(height uint64) ([][]byte, error) { e.isSequencer = isSequencer return validatorUpdates, nil } + +func isValidBlsKey(in []byte) bool { + _, err := bls12381.NewG2().DecodePoint(in) + return err == nil +}