diff --git a/block/manager.go b/block/manager.go index 3f3ab671ff3..c0451799710 100644 --- a/block/manager.go +++ b/block/manager.go @@ -3,6 +3,8 @@ package block import ( "context" "fmt" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" "sync/atomic" "time" @@ -75,6 +77,17 @@ func NewManager( } exec := state.NewBlockExecutor(proposerAddress, conf.NamespaceID, mempool, proxyApp, logger) + if s.LastBlockHeight+1 == genesis.InitialHeight { + res, err := exec.InitChain(genesis) + if err != nil { + return nil, err + } + + updateState(&s, res) + if err := store.UpdateState(s); err != nil { + return nil, err + } + } agg := &Manager{ proposerKey: proposerKey, @@ -284,3 +297,36 @@ func (m *Manager) broadcastBlock(ctx context.Context, block *types.Block) error return nil } + +func updateState(s *state.State, res *abci.ResponseInitChain) { + // If the app did not return an app hash, we keep the one set from the genesis doc in + // the state. We don't set appHash since we don't want the genesis doc app hash + // recorded in the genesis block. We should probably just remove GenesisDoc.AppHash. + if len(res.AppHash) > 0 { + copy(s.AppHash[:], res.AppHash) + } + + if res.ConsensusParams != nil { + params := res.ConsensusParams + if params.Block != nil { + s.ConsensusParams.Block.MaxBytes = params.Block.MaxBytes + s.ConsensusParams.Block.MaxGas = params.Block.MaxGas + } + if params.Evidence != nil { + s.ConsensusParams.Evidence.MaxAgeNumBlocks = params.Evidence.MaxAgeNumBlocks + s.ConsensusParams.Evidence.MaxAgeDuration = params.Evidence.MaxAgeDuration + s.ConsensusParams.Evidence.MaxBytes = params.Evidence.MaxBytes + } + if params.Validator != nil { + // Copy params.Validator.PubkeyTypes, and set result's value to the copy. + // This avoids having to initialize the slice to 0 values, and then write to it again. + s.ConsensusParams.Validator.PubKeyTypes = append([]string{}, params.Validator.PubKeyTypes...) + } + if params.Version != nil { + s.ConsensusParams.Version.AppVersion = params.Version.AppVersion + } + s.Version.Consensus.App = s.ConsensusParams.Version.AppVersion + } + // We update the last results hash with the empty hash, to conform with RFC-6962. + copy(s.LastResultsHash[:], merkle.HashFromByteSlices(nil)) +} diff --git a/node/integration_test.go b/node/integration_test.go index 0bc47e87807..d486bcc5f42 100644 --- a/node/integration_test.go +++ b/node/integration_test.go @@ -33,6 +33,7 @@ func TestAggregatorMode(t *testing.T) { require := require.New(t) 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("DeliverTx", mock.Anything).Return(abci.ResponseDeliverTx{}) @@ -191,6 +192,7 @@ func createNode(n int, aggregator bool, dalc da.DataAvailabilityLayerClient, key p2pConfig.Seeds = strings.TrimSuffix(p2pConfig.Seeds, ",") 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("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}) diff --git a/node/node_test.go b/node/node_test.go index fee42dd0b14..591bd4b092f 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -28,6 +28,7 @@ func TestStartup(t *testing.T) { require := require.New(t) app := &mocks.Application{} + app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{}) key, _, _ := crypto.GenerateEd25519Key(rand.Reader) node, err := NewNode(context.Background(), config.NodeConfig{DALayer: "mock"}, key, proxy.NewLocalClientCreator(app), &types.GenesisDoc{ChainID: "test"}, log.TestingLogger()) require.NoError(err) @@ -49,6 +50,7 @@ func TestMempoolDirectly(t *testing.T) { require := require.New(t) app := &mocks.Application{} + app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{}) app.On("CheckTx", mock.Anything).Return(abci.ResponseCheckTx{}) key, _, _ := crypto.GenerateEd25519Key(rand.Reader) anotherKey, _, _ := crypto.GenerateEd25519Key(rand.Reader) diff --git a/rpcclient/local_test.go b/rpcclient/local_test.go index 335cea744bf..c9877f4d2f3 100644 --- a/rpcclient/local_test.go +++ b/rpcclient/local_test.go @@ -10,13 +10,13 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/peer" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" - "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/peer" "github.com/celestiaorg/optimint/config" "github.com/celestiaorg/optimint/mocks" @@ -188,6 +188,7 @@ func getRPC(t *testing.T) (*mocks.Application, *Local) { t.Helper() require := require.New(t) app := &mocks.Application{} + app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{}) key, _, _ := crypto.GenerateEd25519Key(rand.Reader) node, err := node.NewNode(context.Background(), config.NodeConfig{DALayer: "mock"}, key, proxy.NewLocalClientCreator(app), &types.GenesisDoc{ChainID: "test"}, log.TestingLogger()) require.NoError(err) @@ -204,7 +205,7 @@ func TestMempool2Nodes(t *testing.T) { require := require.New(t) app := &mocks.Application{} - // app.On("CheckTx", mock.Anything).Return(abci.ResponseCheckTx{}) + app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{}) app.On("CheckTx", abci.RequestCheckTx{Tx: []byte("bad")}).Return(abci.ResponseCheckTx{Code: 1}) app.On("CheckTx", abci.RequestCheckTx{Tx: []byte("good")}).Return(abci.ResponseCheckTx{Code: 0}) key1, _, _ := crypto.GenerateEd25519Key(rand.Reader) diff --git a/state/executor.go b/state/executor.go index 9525d6f8d21..f74fd94af69 100644 --- a/state/executor.go +++ b/state/executor.go @@ -8,6 +8,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" tmstate "github.com/tendermint/tendermint/proto/tendermint/state" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/proxy" tmtypes "github.com/tendermint/tendermint/types" @@ -39,6 +40,34 @@ func NewBlockExecutor(proposerAddress []byte, namespaceID [8]byte, mempool mempo } } +func (e *BlockExecutor) InitChain(genesis *tmtypes.GenesisDoc) (*abci.ResponseInitChain, error) { + params := genesis.ConsensusParams + return e.proxyApp.InitChainSync(abci.RequestInitChain{ + Time: genesis.GenesisTime, + ChainId: genesis.ChainID, + ConsensusParams: &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxBytes: params.Block.MaxBytes, + MaxGas: params.Block.MaxGas, + }, + Evidence: &tmproto.EvidenceParams{ + MaxAgeNumBlocks: params.Evidence.MaxAgeNumBlocks, + MaxAgeDuration: params.Evidence.MaxAgeDuration, + MaxBytes: params.Evidence.MaxBytes, + }, + Validator: &tmproto.ValidatorParams{ + PubKeyTypes: params.Validator.PubKeyTypes, + }, + Version: &tmproto.VersionParams{ + AppVersion: params.Version.AppVersion, + }, + }, + Validators: nil, + AppStateBytes: genesis.AppState, + InitialHeight: genesis.InitialHeight, + }) +} + // CreateBlock reaps transactions from mempool and builds a block. func (e *BlockExecutor) CreateBlock(height uint64, commit *types.Commit, state State) *types.Block { maxBytes := state.ConsensusParams.Block.MaxBytes diff --git a/state/executor_test.go b/state/executor_test.go index 299ff788b33..45d7f70d289 100644 --- a/state/executor_test.go +++ b/state/executor_test.go @@ -124,4 +124,4 @@ func TestApplyBlock(t *testing.T) { require.NoError(err) require.NotNil(newState) assert.Equal(int64(2), newState.LastBlockHeight) -} +} \ No newline at end of file diff --git a/store/store.go b/store/store.go index d54887b65ee..dd9fa3066a0 100644 --- a/store/store.go +++ b/store/store.go @@ -149,6 +149,9 @@ func (s *DefaultStore) LoadState() (state.State, error) { } err = json.Unmarshal(blob, &state) + s.mtx.Lock() + s.height = uint64(state.LastBlockHeight) + s.mtx.Unlock() return state, err } diff --git a/store/store_test.go b/store/store_test.go index 4c0b1276c04..a85c55d7897 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -1,17 +1,17 @@ package store import ( + "github.com/celestiaorg/optimint/state" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "math/rand" "os" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/celestiaorg/optimint/types" ) -func TestBlockstoreHeight(t *testing.T) { +func TestStoreHeight(t *testing.T) { t.Parallel() cases := []struct { name string @@ -51,7 +51,7 @@ func TestBlockstoreHeight(t *testing.T) { } } -func TestBlockstoreLoad(t *testing.T) { +func TestStoreLoad(t *testing.T) { t.Parallel() cases := []struct { name string @@ -111,6 +111,28 @@ func TestBlockstoreLoad(t *testing.T) { } } +func TestRestart(t *testing.T) { + t.Parallel() + + assert := assert.New(t) + + kv := NewDefaultInMemoryKVStore() + s1 := New(kv) + expectedHeight := uint64(10) + //block := getRandomBlock(expectedHeight, 10) + //err := s1.SaveBlock(block, &types.Commit{Height: block.Header.Height, HeaderHash: block.Header.Hash()}) + err := s1.UpdateState(state.State{ + LastBlockHeight: int64(expectedHeight), + }) + assert.NoError(err) + + s2 := New(kv) + _, err = s2.LoadState() + assert.NoError(err) + + assert.Equal(expectedHeight, s2.Height()) +} + func getRandomBlock(height uint64, nTxs int) *types.Block { block := &types.Block{ Header: types.Header{