Skip to content

Commit 81e92f0

Browse files
vuong177coderabbitai[bot]MSevey
authored
feat: enable valset update (#1658)
<!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> ## Overview Re-enable `ABCI update valset`. Closes #1673 <!-- Please provide an explanation of the PR, including the appropriate context, background, goal, and rationale. If there is an issue with this information, please provide a tl;dr and link the issue. Ex: Closes #<issue number> --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved handling of validator updates and state management to ensure consistent and accurate validator information. - **New Features** - Enhanced validator and proposer logic for better performance and reliability. - **Tests** - Updated node creation tests to include new parameters for better simulation of real-world scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Matthew Sevey <[email protected]>
1 parent 8ab5e71 commit 81e92f0

File tree

17 files changed

+632
-129
lines changed

17 files changed

+632
-129
lines changed

block/manager.go

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ const blockInChLength = 10000
6161
var initialBackoff = 100 * time.Millisecond
6262

6363
var (
64-
// ErrNoValidatorsInGenesis is used when no validators/proposers are found in genesis state
65-
ErrNoValidatorsInGenesis = errors.New("no validators found in genesis")
64+
// ErrNoValidatorsInState is used when no validators/proposers are found in state
65+
ErrNoValidatorsInState = errors.New("no validators found in state")
6666

6767
// ErrNotProposer is used when the manager is not a proposer
6868
ErrNotProposer = errors.New("not a proposer")
@@ -108,10 +108,6 @@ type Manager struct {
108108

109109
logger log.Logger
110110

111-
// Rollkit doesn't have "validators", but
112-
// we store the sequencer in this struct for compatibility.
113-
validatorSet *cmtypes.ValidatorSet
114-
115111
// For usage by Lazy Aggregator mode
116112
buildingBlock bool
117113
txsAvailable <-chan struct{}
@@ -172,10 +168,6 @@ func NewManager(
172168
//set block height in store
173169
store.SetHeight(context.Background(), s.LastBlockHeight)
174170

175-
// genesis should have exactly one "validator", the centralized sequencer.
176-
// this should have been validated in the above call to getInitialState.
177-
valSet := types.GetValidatorSetFromGenesis(genesis)
178-
179171
if s.DAHeight < conf.DAStartHeight {
180172
s.DAHeight = conf.DAStartHeight
181173
}
@@ -205,31 +197,33 @@ func NewManager(
205197
return nil, err
206198
}
207199

208-
isProposer, err := isProposer(genesis, proposerKey)
209-
if err != nil {
210-
return nil, err
211-
}
212-
213200
maxBlobSize, err := dalc.DA.MaxBlobSize(context.Background())
214201
if err != nil {
215202
return nil, err
216203
}
217204
// allow buffer for the block header and protocol encoding
218205
maxBlobSize -= blockProtocolOverhead
219206

220-
exec := state.NewBlockExecutor(proposerAddress, genesis.ChainID, mempool, proxyApp, eventBus, maxBlobSize, logger, execMetrics, valSet.Hash())
207+
exec := state.NewBlockExecutor(proposerAddress, genesis.ChainID, mempool, proxyApp, eventBus, maxBlobSize, logger, execMetrics)
221208
if s.LastBlockHeight+1 == uint64(genesis.InitialHeight) {
222209
res, err := exec.InitChain(genesis)
223210
if err != nil {
224211
return nil, err
225212
}
213+
if err := updateState(&s, res); err != nil {
214+
return nil, err
215+
}
226216

227-
updateState(&s, res)
228217
if err := store.UpdateState(context.Background(), s); err != nil {
229218
return nil, err
230219
}
231220
}
232221

222+
isProposer, err := isProposer(proposerKey, s)
223+
if err != nil {
224+
return nil, err
225+
}
226+
233227
var txsAvailableCh <-chan struct{}
234228
if mempool != nil {
235229
txsAvailableCh = mempool.TxsAvailable()
@@ -261,7 +255,6 @@ func NewManager(
261255
blockCache: NewBlockCache(),
262256
retrieveCh: make(chan struct{}, 1),
263257
logger: logger,
264-
validatorSet: &valSet,
265258
txsAvailable: txsAvailableCh,
266259
buildingBlock: false,
267260
pendingBlocks: pendingBlocks,
@@ -285,15 +278,17 @@ func (m *Manager) SetDALC(dalc *da.DAClient) {
285278
}
286279

287280
// isProposer returns whether or not the manager is a proposer
288-
func isProposer(genesis *cmtypes.GenesisDoc, signerPrivKey crypto.PrivKey) (bool, error) {
289-
if len(genesis.Validators) == 0 {
290-
return false, ErrNoValidatorsInGenesis
281+
func isProposer(signerPrivKey crypto.PrivKey, s types.State) (bool, error) {
282+
if len(s.Validators.Validators) == 0 {
283+
return false, ErrNoValidatorsInState
291284
}
285+
292286
signerPubBytes, err := signerPrivKey.GetPublic().Raw()
293287
if err != nil {
294288
return false, err
295289
}
296-
return bytes.Equal(genesis.Validators[0].PubKey.Bytes(), signerPubBytes), nil
290+
291+
return bytes.Equal(s.Validators.Validators[0].PubKey.Bytes(), signerPubBytes), nil
297292
}
298293

299294
// SetLastState is used to set lastState used by Manager.
@@ -786,7 +781,6 @@ func (m *Manager) publishBlock(ctx context.Context) error {
786781
)
787782
height := m.store.Height()
788783
newHeight := height + 1
789-
790784
// this is a special case, when first block is produced - there is no previous commit
791785
if newHeight == uint64(m.genesis.InitialHeight) {
792786
lastCommit = &types.Commit{}
@@ -823,9 +817,6 @@ func (m *Manager) publishBlock(ctx context.Context) error {
823817
}
824818
m.logger.Debug("block info", "num_tx", len(block.Data.Txs))
825819

826-
block.SignedHeader.Validators = m.validatorSet
827-
block.SignedHeader.ValidatorHash = m.validatorSet.Hash()
828-
829820
/*
830821
here we set the SignedHeader.DataHash, and SignedHeader.Commit as a hack
831822
to make the block pass ValidateBasic() when it gets called by applyBlock on line 681
@@ -836,6 +827,9 @@ func (m *Manager) publishBlock(ctx context.Context) error {
836827
return err
837828
}
838829

830+
block.SignedHeader.Validators = m.getLastStateValidators()
831+
block.SignedHeader.ValidatorHash = block.SignedHeader.Validators.Hash()
832+
839833
commit, err = m.getCommit(block.SignedHeader.Header)
840834
if err != nil {
841835
return err
@@ -996,7 +990,6 @@ func (m *Manager) recordMetrics(block *types.Block) {
996990
m.metrics.BlockSizeBytes.Set(float64(block.Size()))
997991
m.metrics.CommittedHeight.Set(float64(block.Height()))
998992
}
999-
1000993
func (m *Manager) submitBlocksToDA(ctx context.Context) error {
1001994
submittedAllBlocks := false
1002995
var backoff time.Duration
@@ -1106,6 +1099,12 @@ func (m *Manager) exponentialBackoff(backoff time.Duration) time.Duration {
11061099
return backoff
11071100
}
11081101

1102+
func (m *Manager) getLastStateValidators() *cmtypes.ValidatorSet {
1103+
m.lastStateMtx.RLock()
1104+
defer m.lastStateMtx.RUnlock()
1105+
return m.lastState.Validators
1106+
}
1107+
11091108
// Updates the state stored in manager's store along the manager's lastState
11101109
func (m *Manager) updateState(ctx context.Context, s types.State) error {
11111110
m.lastStateMtx.Lock()
@@ -1137,7 +1136,7 @@ func (m *Manager) applyBlock(ctx context.Context, block *types.Block) (types.Sta
11371136
return m.executor.ApplyBlock(ctx, m.lastState, block)
11381137
}
11391138

1140-
func updateState(s *types.State, res *abci.ResponseInitChain) {
1139+
func updateState(s *types.State, res *abci.ResponseInitChain) error {
11411140
// If the app did not return an app hash, we keep the one set from the genesis doc in
11421141
// the state. We don't set appHash since we don't want the genesis doc app hash
11431142
// recorded in the genesis block. We should probably just remove GenesisDoc.AppHash.
@@ -1169,4 +1168,24 @@ func updateState(s *types.State, res *abci.ResponseInitChain) {
11691168
// We update the last results hash with the empty hash, to conform with RFC-6962.
11701169
s.LastResultsHash = merkle.HashFromByteSlices(nil)
11711170

1171+
vals, err := cmtypes.PB2TM.ValidatorUpdates(res.Validators)
1172+
if err != nil {
1173+
return err
1174+
}
1175+
1176+
// apply initchain valset change
1177+
nValSet := s.Validators.Copy()
1178+
err = nValSet.UpdateWithChangeSet(vals)
1179+
if err != nil {
1180+
return err
1181+
}
1182+
if len(nValSet.Validators) != 1 {
1183+
return fmt.Errorf("expected exactly one validator")
1184+
}
1185+
1186+
s.Validators = cmtypes.NewValidatorSet(nValSet.Validators)
1187+
s.NextValidators = cmtypes.NewValidatorSet(nValSet.Validators).CopyIncrementProposerPriority(1)
1188+
s.LastValidators = cmtypes.NewValidatorSet(nValSet.Validators)
1189+
1190+
return nil
11721191
}

block/manager_test.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ func TestInitialStateClean(t *testing.T) {
7272
func TestInitialStateStored(t *testing.T) {
7373
require := require.New(t)
7474
genesisDoc, _ := types.GetGenesisWithPrivkey()
75+
valset := types.GetRandomValidatorSet()
7576
genesis := &cmtypes.GenesisDoc{
7677
ChainID: "myChain",
7778
InitialHeight: 1,
@@ -82,7 +83,11 @@ func TestInitialStateStored(t *testing.T) {
8283
ChainID: "myChain",
8384
InitialHeight: 1,
8485
LastBlockHeight: 100,
86+
Validators: valset,
87+
NextValidators: valset,
88+
LastValidators: valset,
8589
}
90+
8691
ctx, cancel := context.WithCancel(context.Background())
8792
defer cancel()
8893
es, _ := store.NewDefaultInMemoryKVStore()
@@ -97,6 +102,7 @@ func TestInitialStateStored(t *testing.T) {
97102

98103
func TestInitialStateUnexpectedHigherGenesis(t *testing.T) {
99104
require := require.New(t)
105+
valset := types.GetRandomValidatorSet()
100106
genesisDoc, _ := types.GetGenesisWithPrivkey()
101107
genesis := &cmtypes.GenesisDoc{
102108
ChainID: "myChain",
@@ -108,6 +114,9 @@ func TestInitialStateUnexpectedHigherGenesis(t *testing.T) {
108114
ChainID: "myChain",
109115
InitialHeight: 1,
110116
LastBlockHeight: 0,
117+
Validators: valset,
118+
NextValidators: valset,
119+
LastValidators: valset,
111120
}
112121
ctx, cancel := context.WithCancel(context.Background())
113122
defer cancel()
@@ -405,7 +414,7 @@ func Test_isProposer(t *testing.T) {
405414
require := require.New(t)
406415

407416
type args struct {
408-
genesis *cmtypes.GenesisDoc
417+
state types.State
409418
signerPrivKey crypto.PrivKey
410419
}
411420
tests := []struct {
@@ -418,10 +427,12 @@ func Test_isProposer(t *testing.T) {
418427
name: "Signing key matches genesis proposer public key",
419428
args: func() args {
420429
genesisData, privKey := types.GetGenesisWithPrivkey()
430+
s, err := types.NewFromGenesisDoc(genesisData)
431+
require.NoError(err)
421432
signingKey, err := types.PrivKeyToSigningKey(privKey)
422433
require.NoError(err)
423434
return args{
424-
genesisData,
435+
s,
425436
signingKey,
426437
}
427438
}(),
@@ -432,11 +443,14 @@ func Test_isProposer(t *testing.T) {
432443
name: "Signing key does not match genesis proposer public key",
433444
args: func() args {
434445
genesisData, _ := types.GetGenesisWithPrivkey()
446+
s, err := types.NewFromGenesisDoc(genesisData)
447+
require.NoError(err)
448+
435449
randomPrivKey := ed25519.GenPrivKey()
436450
signingKey, err := types.PrivKeyToSigningKey(randomPrivKey)
437451
require.NoError(err)
438452
return args{
439-
genesisData,
453+
s,
440454
signingKey,
441455
}
442456
}(),
@@ -448,20 +462,23 @@ func Test_isProposer(t *testing.T) {
448462
args: func() args {
449463
genesisData, privKey := types.GetGenesisWithPrivkey()
450464
genesisData.Validators = nil
465+
s, err := types.NewFromGenesisDoc(genesisData)
466+
require.NoError(err)
467+
451468
signingKey, err := types.PrivKeyToSigningKey(privKey)
452469
require.NoError(err)
453470
return args{
454-
genesisData,
471+
s,
455472
signingKey,
456473
}
457474
}(),
458475
isProposer: false,
459-
err: ErrNoValidatorsInGenesis,
476+
err: ErrNoValidatorsInState,
460477
},
461478
}
462479
for _, tt := range tests {
463480
t.Run(tt.name, func(t *testing.T) {
464-
isProposer, err := isProposer(tt.args.genesis, tt.args.signerPrivKey)
481+
isProposer, err := isProposer(tt.args.signerPrivKey, tt.args.state)
465482
if !errors.Is(err, tt.err) {
466483
t.Errorf("isProposer() error = %v, expected err %v", err, tt.err)
467484
return

node/full_client_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,13 @@ func TestStatus(t *testing.T) {
899899
require.NotNil(node)
900900
ctx := context.Background()
901901

902-
err = node.Store.UpdateState(ctx, types.State{})
902+
validators := make([]*cmtypes.Validator, len(genesisDoc.Validators))
903+
for i, val := range genesisDoc.Validators {
904+
validators[i] = cmtypes.NewValidator(val.PubKey, val.Power)
905+
}
906+
validatorSet := cmtypes.NewValidatorSet(validators)
907+
908+
err = node.Store.UpdateState(ctx, types.State{LastValidators: validatorSet, NextValidators: validatorSet, Validators: validatorSet})
903909
assert.NoError(err)
904910

905911
rpc := NewFullClient(node)

0 commit comments

Comments
 (0)