Skip to content

Commit 8deede4

Browse files
authored
fix(manager): move update height (#1782)
## Overview Update height only after block responses are saved. Partially fixes #477 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced testing capabilities for the block publishing process, including validation for successful executions and error handling. - **Bug Fixes** - Improved state management logic during block publication to enhance consistency and error handling. - **Tests** - Added new test cases to ensure the correct behavior of the block publishing method under various scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 5f1c0ad commit 8deede4

File tree

2 files changed

+128
-2
lines changed

2 files changed

+128
-2
lines changed

block/manager.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,6 @@ func (m *Manager) publishBlock(ctx context.Context) error {
917917
}
918918

919919
blockHeight := block.Height()
920-
// Update the store height before submitting to the DA layer and committing to the DB
921-
m.store.SetHeight(ctx, blockHeight)
922920

923921
blockHash := block.Hash().String()
924922
m.blockCache.setSeen(blockHash)
@@ -943,6 +941,9 @@ func (m *Manager) publishBlock(ctx context.Context) error {
943941
return err
944942
}
945943

944+
// Update the store height before submitting to the DA layer but after committing to the DB
945+
m.store.SetHeight(ctx, blockHeight)
946+
946947
newState.DAHeight = atomic.LoadUint64(&m.daHeight)
947948
// After this call m.lastState is the NEW state returned from ApplyBlock
948949
// updateState also commits the DB tx

block/manager_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@ package block
33
import (
44
"bytes"
55
"context"
6+
"crypto/rand"
67
"errors"
78
"fmt"
89
"os"
910
"strconv"
11+
"sync"
1012
"testing"
1113
"time"
1214

15+
abci "github.com/cometbft/cometbft/abci/types"
16+
cfg "github.com/cometbft/cometbft/config"
1317
cmcrypto "github.com/cometbft/cometbft/crypto"
1418
"github.com/cometbft/cometbft/crypto/ed25519"
1519
"github.com/cometbft/cometbft/crypto/secp256k1"
20+
"github.com/cometbft/cometbft/libs/log"
21+
cmproto "github.com/cometbft/cometbft/proto/tendermint/types"
22+
"github.com/cometbft/cometbft/proxy"
1623
cmtypes "github.com/cometbft/cometbft/types"
1724
ds "github.com/ipfs/go-datastore"
1825
"github.com/libp2p/go-libp2p/core/crypto"
1926
"github.com/stretchr/testify/assert"
27+
"github.com/stretchr/testify/mock"
2028
"github.com/stretchr/testify/require"
2129

2230
goDA "github.com/rollkit/go-da"
@@ -25,6 +33,8 @@ import (
2533
"github.com/rollkit/rollkit/config"
2634
"github.com/rollkit/rollkit/da"
2735
mockda "github.com/rollkit/rollkit/da/mock"
36+
"github.com/rollkit/rollkit/mempool"
37+
"github.com/rollkit/rollkit/state"
2838
"github.com/rollkit/rollkit/store"
2939
test "github.com/rollkit/rollkit/test/log"
3040
"github.com/rollkit/rollkit/test/mocks"
@@ -549,6 +559,121 @@ func Test_publishBlock_ManagerNotProposer(t *testing.T) {
549559
require.ErrorIs(err, ErrNotProposer)
550560
}
551561

562+
func TestManager_publishBlock(t *testing.T) {
563+
mockStore := new(mocks.Store)
564+
mockLogger := new(test.MockLogger)
565+
assert := assert.New(t)
566+
require := require.New(t)
567+
568+
logger := log.TestingLogger()
569+
570+
var mockAppHash []byte
571+
_, err := rand.Read(mockAppHash[:])
572+
require.NoError(err)
573+
574+
app := &mocks.Application{}
575+
app.On("CheckTx", mock.Anything, mock.Anything).Return(&abci.ResponseCheckTx{}, nil)
576+
app.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil)
577+
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(func(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
578+
return &abci.ResponsePrepareProposal{
579+
Txs: req.Txs,
580+
}, nil
581+
})
582+
app.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
583+
app.On("FinalizeBlock", mock.Anything, mock.Anything).Return(
584+
func(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
585+
txResults := make([]*abci.ExecTxResult, len(req.Txs))
586+
for idx := range req.Txs {
587+
txResults[idx] = &abci.ExecTxResult{
588+
Code: abci.CodeTypeOK,
589+
}
590+
}
591+
592+
return &abci.ResponseFinalizeBlock{
593+
TxResults: txResults,
594+
AppHash: mockAppHash,
595+
}, nil
596+
},
597+
)
598+
599+
client, err := proxy.NewLocalClientCreator(app).NewABCIClient()
600+
require.NoError(err)
601+
require.NotNil(client)
602+
603+
vKey := ed25519.GenPrivKey()
604+
validators := []*cmtypes.Validator{
605+
{
606+
Address: vKey.PubKey().Address(),
607+
PubKey: vKey.PubKey(),
608+
VotingPower: int64(100),
609+
ProposerPriority: int64(1),
610+
},
611+
}
612+
613+
lastState := types.State{}
614+
lastState.ConsensusParams.Block = &cmproto.BlockParams{}
615+
lastState.ConsensusParams.Block.MaxBytes = 100
616+
lastState.ConsensusParams.Block.MaxGas = 100000
617+
lastState.ConsensusParams.Abci = &cmproto.ABCIParams{VoteExtensionsEnableHeight: 0}
618+
lastState.Validators = cmtypes.NewValidatorSet(validators)
619+
lastState.NextValidators = cmtypes.NewValidatorSet(validators)
620+
lastState.LastValidators = cmtypes.NewValidatorSet(validators)
621+
622+
mpool := mempool.NewCListMempool(cfg.DefaultMempoolConfig(), proxy.NewAppConnMempool(client, proxy.NopMetrics()), 0)
623+
executor := state.NewBlockExecutor(vKey.PubKey().Address(), "test", mpool, proxy.NewAppConnConsensus(client, proxy.NopMetrics()), nil, 100, logger, state.NopMetrics())
624+
625+
signingKey, err := types.PrivKeyToSigningKey(vKey)
626+
require.NoError(err)
627+
m := &Manager{
628+
lastState: lastState,
629+
lastStateMtx: new(sync.RWMutex),
630+
blockCache: NewBlockCache(),
631+
executor: executor,
632+
store: mockStore,
633+
logger: mockLogger,
634+
genesis: &cmtypes.GenesisDoc{
635+
ChainID: "myChain",
636+
InitialHeight: 1,
637+
AppHash: []byte("app hash"),
638+
},
639+
conf: config.BlockManagerConfig{
640+
BlockTime: time.Second,
641+
LazyAggregator: false,
642+
},
643+
isProposer: true,
644+
proposerKey: signingKey,
645+
metrics: NopMetrics(),
646+
}
647+
648+
t.Run("height should not be updated if saving block responses fails", func(t *testing.T) {
649+
mockStore.On("Height").Return(uint64(0))
650+
signature := types.Signature([]byte{1, 1, 1})
651+
block, err := executor.CreateBlock(0, &signature, abci.ExtendedCommitInfo{}, []byte{}, lastState)
652+
require.NoError(err)
653+
require.NotNil(block)
654+
assert.Equal(uint64(0), block.Height())
655+
dataHash, err := block.Data.Hash()
656+
assert.NoError(err)
657+
block.SignedHeader.DataHash = dataHash
658+
659+
// Update the signature on the block to current from last
660+
voteBytes := block.SignedHeader.Header.MakeCometBFTVote()
661+
signature, _ = vKey.Sign(voteBytes)
662+
block.SignedHeader.Signature = signature
663+
block.SignedHeader.Validators = lastState.Validators
664+
665+
mockStore.On("GetBlock", mock.Anything, uint64(1)).Return(block, nil).Once()
666+
mockStore.On("SaveBlock", mock.Anything, block, mock.Anything).Return(nil).Once()
667+
mockStore.On("SaveBlockResponses", mock.Anything, uint64(0), mock.Anything).Return(errors.New("failed to save block responses")).Once()
668+
669+
ctx := context.Background()
670+
err = m.publishBlock(ctx)
671+
assert.ErrorContains(err, "failed to save block responses")
672+
673+
mockStore.AssertExpectations(t)
674+
})
675+
}
676+
552677
func TestManager_getRemainingSleep(t *testing.T) {
553678
tests := []struct {
554679
name string

0 commit comments

Comments
 (0)