Skip to content

Commit

Permalink
Support based rollups + centralized sequencer to based rollup integra…
Browse files Browse the repository at this point in the history
…tion test (cosmos#827)

Closes: cosmos#848, cosmos#849
  • Loading branch information
Manav-Aggarwal authored Apr 14, 2023
1 parent 23cc81a commit ea0dec6
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 41 deletions.
8 changes: 5 additions & 3 deletions block/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,10 @@ func (m *Manager) publishBlock(ctx context.Context) error {
return err
}

blockHeight := uint64(block.SignedHeader.Header.Height())

// Only update the stored height after successfully submitting to DA layer and committing to the DB
m.store.SetHeight(uint64(block.SignedHeader.Header.Height()))
m.store.SetHeight(blockHeight)

// Commit the new state and block which writes to disk on the proxy app
_, _, err = m.executor.Commit(ctx, newState, block, responses)
Expand All @@ -574,13 +576,13 @@ func (m *Manager) publishBlock(ctx context.Context) error {
}

// SaveBlockResponses commits the DB tx
err = m.store.SaveBlockResponses(uint64(block.SignedHeader.Header.Height()), responses)
err = m.store.SaveBlockResponses(blockHeight, responses)
if err != nil {
return err
}

// SaveValidators commits the DB tx
err = m.store.SaveValidators(uint64(block.SignedHeader.Header.Height()), m.lastState.Validators)
err = m.store.SaveValidators(blockHeight, m.lastState.Validators)
if err != nil {
return err
}
Expand Down
141 changes: 105 additions & 36 deletions node/full_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,21 +650,16 @@ func TestBlockchainInfo(t *testing.T) {
}
}

func TestValidatorSetHandling(t *testing.T) {
assert := assert.New(t)
require := require.New(t)

waitCh := make(chan interface{})

vKeys := make([]tmcrypto.PrivKey, 2)
apps := make([]*mocks.Application, 2)
nodes := make([]*FullNode, 2)
func createGenesisValidators(numNodes int, appCreator func(vKeyToRemove tmcrypto.PrivKey) *mocks.Application, require *require.Assertions) *FullClient {
vKeys := make([]tmcrypto.PrivKey, numNodes)
apps := make([]*mocks.Application, numNodes)
nodes := make([]*FullNode, numNodes)

genesisValidators := make([]tmtypes.GenesisValidator, len(vKeys))
for i := 0; i < len(vKeys); i++ {
vKeys[i] = ed25519.GenPrivKey()
genesisValidators[i] = tmtypes.GenesisValidator{Address: vKeys[i].PubKey().Address(), PubKey: vKeys[i].PubKey(), Power: int64(i + 100), Name: fmt.Sprintf("gen #%d", i)}
apps[i] = createApp(vKeys[0], waitCh, require)
apps[i] = appCreator(vKeys[0])
}

dalc := &mockda.DataAvailabilityLayerClient{}
Expand Down Expand Up @@ -712,6 +707,39 @@ func TestValidatorSetHandling(t *testing.T) {
err := nodes[i].Start()
require.NoError(err)
}
return rpc
}

// Tests moving from two validators to one validator and then back to two validators
func TestValidatorSetHandling(t *testing.T) {
assert := assert.New(t)
require := require.New(t)

waitCh := make(chan interface{})

numNodes := 2
createApp := func(vKeyToRemove tmcrypto.PrivKey) *mocks.Application {
app := &mocks.Application{}
app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{})
app.On("CheckTx", mock.Anything).Return(abci.ResponseCheckTx{})
app.On("BeginBlock", mock.Anything).Return(abci.ResponseBeginBlock{})
app.On("Commit", mock.Anything).Return(abci.ResponseCommit{})
app.On("GetAppHash", mock.Anything).Return(abci.ResponseGetAppHash{})
app.On("GenerateFraudProof", mock.Anything).Return(abci.ResponseGenerateFraudProof{})

pbValKey, err := encoding.PubKeyToProto(vKeyToRemove.PubKey())
require.NoError(err)

app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Times(2)
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{{PubKey: pbValKey, Power: 0}}}).Once()
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Once()
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{{PubKey: pbValKey, Power: 100}}}).Once()
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Run(func(args mock.Arguments) {
waitCh <- nil
})
return app
}
rpc := createGenesisValidators(numNodes, createApp, require)

<-waitCh
<-waitCh
Expand All @@ -721,8 +749,8 @@ func TestValidatorSetHandling(t *testing.T) {
vals, err := rpc.Validators(context.Background(), &h, nil, nil)
assert.NoError(err)
assert.NotNil(vals)
assert.EqualValues(len(genesisValidators), vals.Total)
assert.Len(vals.Validators, len(genesisValidators))
assert.EqualValues(numNodes, vals.Total)
assert.Len(vals.Validators, numNodes)
assert.EqualValues(vals.BlockHeight, h)
}

Expand All @@ -731,8 +759,8 @@ func TestValidatorSetHandling(t *testing.T) {
vals, err := rpc.Validators(context.Background(), &h, nil, nil)
assert.NoError(err)
assert.NotNil(vals)
assert.EqualValues(len(genesisValidators)-1, vals.Total)
assert.Len(vals.Validators, len(genesisValidators)-1)
assert.EqualValues(numNodes-1, vals.Total)
assert.Len(vals.Validators, numNodes-1)
assert.EqualValues(vals.BlockHeight, h)
}

Expand All @@ -743,40 +771,81 @@ func TestValidatorSetHandling(t *testing.T) {
vals, err := rpc.Validators(context.Background(), &h, nil, nil)
assert.NoError(err)
assert.NotNil(vals)
assert.EqualValues(len(genesisValidators), vals.Total)
assert.Len(vals.Validators, len(genesisValidators))
assert.EqualValues(numNodes, vals.Total)
assert.Len(vals.Validators, numNodes)
assert.EqualValues(vals.BlockHeight, h)
}

// check for "latest block"
vals, err := rpc.Validators(context.Background(), nil, nil, nil)
assert.NoError(err)
assert.NotNil(vals)
assert.EqualValues(len(genesisValidators), vals.Total)
assert.Len(vals.Validators, len(genesisValidators))
assert.EqualValues(numNodes, vals.Total)
assert.Len(vals.Validators, numNodes)
assert.GreaterOrEqual(vals.BlockHeight, int64(9))
}

func createApp(keyToRemove tmcrypto.PrivKey, waitCh chan interface{}, require *require.Assertions) *mocks.Application {
app := &mocks.Application{}
app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{})
app.On("CheckTx", mock.Anything).Return(abci.ResponseCheckTx{})
app.On("BeginBlock", mock.Anything).Return(abci.ResponseBeginBlock{})
app.On("Commit", mock.Anything).Return(abci.ResponseCommit{})
app.On("GetAppHash", mock.Anything).Return(abci.ResponseGetAppHash{})
app.On("GenerateFraudProof", mock.Anything).Return(abci.ResponseGenerateFraudProof{})
// Tests moving from a centralized validator to empty validator set
func TestValidatorSetHandlingBased(t *testing.T) {
assert := assert.New(t)
require := require.New(t)

pbValKey, err := encoding.PubKeyToProto(keyToRemove.PubKey())
require.NoError(err)
waitCh := make(chan interface{})

app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Times(2)
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{{PubKey: pbValKey, Power: 0}}}).Once()
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Once()
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{{PubKey: pbValKey, Power: 100}}}).Once()
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Run(func(args mock.Arguments) {
waitCh <- nil
})
return app
numNodes := 1
createApp := func(vKeyToRemove tmcrypto.PrivKey) *mocks.Application {
app := &mocks.Application{}
app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{})
app.On("CheckTx", mock.Anything).Return(abci.ResponseCheckTx{})
app.On("BeginBlock", mock.Anything).Return(abci.ResponseBeginBlock{})
app.On("Commit", mock.Anything).Return(abci.ResponseCommit{})
app.On("GetAppHash", mock.Anything).Return(abci.ResponseGetAppHash{})
app.On("GenerateFraudProof", mock.Anything).Return(abci.ResponseGenerateFraudProof{})

pbValKey, err := encoding.PubKeyToProto(vKeyToRemove.PubKey())
require.NoError(err)

app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Times(2)
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{{PubKey: pbValKey, Power: 0}}}).Once()
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}).Run(func(args mock.Arguments) {
waitCh <- nil
})
return app
}

rpc := createGenesisValidators(numNodes, createApp, require)

<-waitCh

// test first blocks
for h := int64(1); h <= 3; h++ {
vals, err := rpc.Validators(context.Background(), &h, nil, nil)
assert.NoError(err)
assert.NotNil(vals)
assert.EqualValues(numNodes, vals.Total)
assert.Len(vals.Validators, numNodes)
assert.EqualValues(vals.BlockHeight, h)
}

// 3rd EndBlock removes the first validator and makes the rollup based
for h := int64(4); h <= 9; h++ {
<-waitCh
vals, err := rpc.Validators(context.Background(), &h, nil, nil)
assert.NoError(err)
assert.NotNil(vals)
assert.EqualValues(numNodes-1, vals.Total)
assert.Len(vals.Validators, numNodes-1)
assert.EqualValues(vals.BlockHeight, h)
}

// check for "latest block"
<-waitCh
vals, err := rpc.Validators(context.Background(), nil, nil, nil)
assert.NoError(err)
assert.NotNil(vals)
assert.EqualValues(numNodes-1, vals.Total)
assert.Len(vals.Validators, numNodes-1)
assert.GreaterOrEqual(vals.BlockHeight, int64(9))
}

// copy-pasted from store/store_test.go
Expand Down
13 changes: 11 additions & 2 deletions state/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
)

var ErrFraudProofGenerated = errors.New("failed to ApplyBlock: halting node due to fraud")
var ErrEmptyValSetGenerated = errors.New("applying the validator changes would result in empty set")

// BlockExecutor creates and applies blocks and maintains state.
type BlockExecutor struct {
Expand Down Expand Up @@ -210,14 +211,22 @@ func (e *BlockExecutor) updateState(state types.State, block *types.Block, abciR
if len(validatorUpdates) > 0 {
err := nValSet.UpdateWithChangeSet(validatorUpdates)
if err != nil {
return state, nil
if err.Error() != ErrEmptyValSetGenerated.Error() {
return state, err
}
nValSet = &tmtypes.ValidatorSet{
Validators: make([]*tmtypes.Validator, 0),
Proposer: nil,
}
}
// Change results from this height but only applies to the next next height.
lastHeightValSetChanged = block.SignedHeader.Header.Height() + 1 + 1
}

if len(nValSet.Validators) > 0 {
nValSet.IncrementProposerPriority(1)
}
// TODO(tzdybal): right now, it's for backward compatibility, may need to change this
nValSet.IncrementProposerPriority(1)
}

s := types.State{
Expand Down
6 changes: 6 additions & 0 deletions store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ func (s *DefaultStore) LoadValidators(height uint64) (*tmtypes.ValidatorSet, err
if err != nil {
return nil, fmt.Errorf("failed to unmarshal to protobuf: %w", err)
}
if len(pbValSet.Validators) == 0 {
return &tmtypes.ValidatorSet{
Validators: make([]*tmtypes.Validator, 0),
Proposer: nil,
}, nil
}

return tmtypes.ValidatorSetFromProto(&pbValSet)
}
Expand Down
5 changes: 5 additions & 0 deletions types/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func (h *SignedHeader) ValidateBasic() error {
return err
}

// Handle Based Rollup case
if len(h.Validators.Validators) == 0 {
return nil
}

err = h.Validators.ValidateBasic()
if err != nil {
return err
Expand Down

0 comments on commit ea0dec6

Please sign in to comment.