diff --git a/consensus/replay.go b/consensus/replay.go index 158fa3ce0..47f3edcd9 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -6,13 +6,9 @@ import ( "hash/crc32" "io" "reflect" - - //"strconv" - //"strings" "time" abci "github.com/tendermint/tendermint/abci/types" - //auto "github.com/tendermint/tendermint/libs/autofile" dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/libs/log" diff --git a/consensus/replay_stubs.go b/consensus/replay_stubs.go index 36f3b03fe..4df75c65c 100644 --- a/consensus/replay_stubs.go +++ b/consensus/replay_stubs.go @@ -4,6 +4,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/clist" mempl "github.com/tendermint/tendermint/mempool" + tmstate "github.com/tendermint/tendermint/proto/state" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -64,7 +65,7 @@ func (emptyEvidencePool) Header(int64) *types.Header { return nil } // Useful because we don't want to call Commit() twice for the same block on // the real app. -func newMockProxyApp(appHash []byte, abciResponses *sm.ABCIResponses) proxy.AppConnConsensus { +func newMockProxyApp(appHash []byte, abciResponses *tmstate.ABCIResponses) proxy.AppConnConsensus { clientCreator := proxy.NewLocalClientCreator(&mockProxyApp{ appHash: appHash, abciResponses: abciResponses, @@ -82,7 +83,7 @@ type mockProxyApp struct { appHash []byte txCount int - abciResponses *sm.ABCIResponses + abciResponses *tmstate.ABCIResponses } func (mock *mockProxyApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { diff --git a/consensus/replay_test.go b/consensus/replay_test.go index a1c73b048..cdfe98715 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -28,6 +28,7 @@ import ( tmrand "github.com/tendermint/tendermint/libs/rand" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/privval" + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -176,6 +177,7 @@ LOOP: csWal, err := cs.OpenWAL(walFile) require.NoError(t, err) crashingWal.next = csWal + // reset the message counter crashingWal.msgIndex = 1 cs.wal = crashingWal @@ -577,13 +579,13 @@ func TestMockProxyApp(t *testing.T) { txIndex := 0 assert.NotPanics(t, func() { - abciResWithEmptyDeliverTx := new(sm.ABCIResponses) + abciResWithEmptyDeliverTx := new(tmstate.ABCIResponses) abciResWithEmptyDeliverTx.DeliverTxs = make([]*abci.ResponseDeliverTx, 0) abciResWithEmptyDeliverTx.DeliverTxs = append(abciResWithEmptyDeliverTx.DeliverTxs, &abci.ResponseDeliverTx{}) // called when saveABCIResponses: bytes := cdc.MustMarshalBinaryBare(abciResWithEmptyDeliverTx) - loadedAbciRes := new(sm.ABCIResponses) + loadedAbciRes := new(tmstate.ABCIResponses) // this also happens sm.LoadABCIResponses err := cdc.UnmarshalBinaryBare(bytes, loadedAbciRes) @@ -591,7 +593,7 @@ func TestMockProxyApp(t *testing.T) { mock := newMockProxyApp([]byte("mock_hash"), loadedAbciRes) - abciRes := new(sm.ABCIResponses) + abciRes := new(tmstate.ABCIResponses) abciRes.DeliverTxs = make([]*abci.ResponseDeliverTx, len(loadedAbciRes.DeliverTxs)) // Execute transactions and get hash. proxyCb := func(req *abci.Request, res *abci.Response) { diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 4867fbddd..f1a2af83e 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -11,6 +11,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/types" sm "github.com/tendermint/tendermint/state" @@ -283,19 +284,21 @@ func TestRecoverPendingEvidence(t *testing.T) { func initializeValidatorState(valAddr []byte, height int64) dbm.DB { stateDB := dbm.NewMemDB() + pk := ed25519.GenPrivKey().PubKey() // create validator set and state + validator := &types.Validator{Address: valAddr, VotingPower: 100, PubKey: pk} valSet := &types.ValidatorSet{ - Validators: []*types.Validator{ - {Address: valAddr, VotingPower: 0}, - }, + Validators: []*types.Validator{validator}, + Proposer: validator, } + state := sm.State{ LastBlockHeight: height, LastBlockTime: tmtime.Now(), - LastValidators: valSet, Validators: valSet, NextValidators: valSet.CopyIncrementProposerPriority(1), + LastValidators: valSet, LastHeightValidatorsChanged: 1, ConsensusParams: tmproto.ConsensusParams{ Block: tmproto.BlockParams{ diff --git a/rpc/core/blocks_test.go b/rpc/core/blocks_test.go index 6dc9c9b72..2a5d783d0 100644 --- a/rpc/core/blocks_test.go +++ b/rpc/core/blocks_test.go @@ -10,6 +10,7 @@ import ( dbm "github.com/tendermint/tm-db" abci "github.com/tendermint/tendermint/abci/types" + tmstate "github.com/tendermint/tendermint/proto/state" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" sm "github.com/tendermint/tendermint/state" @@ -69,7 +70,7 @@ func TestBlockchainInfo(t *testing.T) { } func TestBlockResults(t *testing.T) { - results := &sm.ABCIResponses{ + results := &tmstate.ABCIResponses{ DeliverTxs: []*abci.ResponseDeliverTx{ {Code: 0, Data: []byte{0x01}, Log: "ok"}, {Code: 0, Data: []byte{0x02}, Log: "ok"}, diff --git a/state/execution.go b/state/execution.go index c98dd4fbf..350194cdd 100644 --- a/state/execution.go +++ b/state/execution.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" @@ -252,11 +253,13 @@ func execBlockOnProxyApp( proxyAppConn proxy.AppConnConsensus, block *types.Block, stateDB dbm.DB, -) (*ABCIResponses, error) { +) (*tmstate.ABCIResponses, error) { var validTxs, invalidTxs = 0, 0 txIndex := 0 - abciResponses := NewABCIResponses(block) + abciResponses := new(tmstate.ABCIResponses) + dtxs := make([]*abci.ResponseDeliverTx, len(block.Txs)) + abciResponses.DeliverTxs = dtxs // Execute transactions and get hash. proxyCb := func(req *abci.Request, res *abci.Response) { @@ -391,7 +394,7 @@ func updateState( state State, blockID types.BlockID, header *types.Header, - abciResponses *ABCIResponses, + abciResponses *tmstate.ABCIResponses, validatorUpdates []*types.Validator, ) (State, error) { @@ -444,7 +447,7 @@ func updateState( LastHeightValidatorsChanged: lastHeightValsChanged, ConsensusParams: nextParams, LastHeightConsensusParamsChanged: lastHeightParamsChanged, - LastResultsHash: abciResponses.ResultsHash(), + LastResultsHash: ABCIResponsesResultsHash(*abciResponses), AppHash: nil, }, nil } @@ -456,7 +459,7 @@ func fireEvents( logger log.Logger, eventBus types.BlockEventPublisher, block *types.Block, - abciResponses *ABCIResponses, + abciResponses *tmstate.ABCIResponses, validatorUpdates []*types.Validator, ) { eventBus.PublishEventNewBlock(types.EventDataNewBlock{ diff --git a/state/export_test.go b/state/export_test.go index 51d2c487e..028d36419 100644 --- a/state/export_test.go +++ b/state/export_test.go @@ -4,6 +4,7 @@ import ( dbm "github.com/tendermint/tm-db" abci "github.com/tendermint/tendermint/abci/types" + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" "github.com/tendermint/tendermint/types" ) @@ -27,7 +28,7 @@ func UpdateState( state State, blockID types.BlockID, header *types.Header, - abciResponses *ABCIResponses, + abciResponses *tmstate.ABCIResponses, validatorUpdates []*types.Validator, ) (State, error) { return updateState(state, blockID, header, abciResponses, validatorUpdates) diff --git a/state/helpers_test.go b/state/helpers_test.go index 944d4ae44..bc7ce603e 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" tmrand "github.com/tendermint/tendermint/libs/rand" + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -121,6 +122,7 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida s.LastValidators = s.Validators.Copy() sm.SaveState(stateDB, s) } + return s, stateDB, privVals } @@ -166,13 +168,12 @@ func makeConsensusParams( func makeHeaderPartsResponsesValPubKeyChange( state sm.State, pubkey crypto.PubKey, -) (types.Header, types.BlockID, *sm.ABCIResponses) { +) (types.Header, types.BlockID, *tmstate.ABCIResponses) { block := makeBlock(state, state.LastBlockHeight+1) - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } - // If the pubkey is new, remove the old and add the new. _, val := state.NextValidators.GetByIndex(0) if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) { @@ -190,10 +191,10 @@ func makeHeaderPartsResponsesValPubKeyChange( func makeHeaderPartsResponsesValPowerChange( state sm.State, power int64, -) (types.Header, types.BlockID, *sm.ABCIResponses) { +) (types.Header, types.BlockID, *tmstate.ABCIResponses) { block := makeBlock(state, state.LastBlockHeight+1) - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } @@ -213,10 +214,10 @@ func makeHeaderPartsResponsesValPowerChange( func makeHeaderPartsResponsesParams( state sm.State, params tmproto.ConsensusParams, -) (types.Header, types.BlockID, *sm.ABCIResponses) { +) (types.Header, types.BlockID, *tmstate.ABCIResponses) { block := makeBlock(state, state.LastBlockHeight+1) - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)}, } return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses diff --git a/state/state.go b/state/state.go index 2f2ab9325..00b807ee8 100644 --- a/state/state.go +++ b/state/state.go @@ -2,10 +2,14 @@ package state import ( "bytes" + "errors" "fmt" "io/ioutil" "time" + "github.com/gogo/protobuf/proto" + + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" tmversion "github.com/tendermint/tendermint/proto/version" "github.com/tendermint/tendermint/types" @@ -20,20 +24,11 @@ var ( //----------------------------------------------------------------------------- -// Version is for versioning the State. -// It holds the Block and App version needed for making blocks, -// and the software version to support upgrades to the format of -// the State as stored on disk. -type Version struct { - Consensus tmversion.Consensus - Software string -} - // InitStateVersion sets the Consensus.Block and Software versions, // but leaves the Consensus.App version blank. // The Consensus.App version will be set during the Handshake, once // we hear from the app what protocol version it is running. -var InitStateVersion = Version{ +var InitStateVersion = tmstate.Version{ Consensus: tmversion.Consensus{ Block: version.BlockProtocol, App: 0, @@ -51,7 +46,7 @@ var InitStateVersion = Version{ // Instead, use state.Copy() or state.NextState(...). // NOTE: not goroutine-safe. type State struct { - Version Version + Version tmstate.Version // immutable ChainID string @@ -86,6 +81,7 @@ type State struct { // Copy makes a copy of the State for mutating. func (state State) Copy() State { + return State{ Version: state.Version, ChainID: state.ChainID, @@ -114,9 +110,18 @@ func (state State) Equals(state2 State) bool { return bytes.Equal(sbz, s2bz) } -// Bytes serializes the State using go-amino. +// Bytes serializes the State using protobuf. +// It panics if either casting to protobuf or serialization fails. func (state State) Bytes() []byte { - return cdc.MustMarshalBinaryBare(state) + sm, err := state.ToProto() + if err != nil { + panic(err) + } + bz, err := proto.Marshal(sm) + if err != nil { + panic(err) + } + return bz } // IsEmpty returns true if the State is equal to the empty State. @@ -124,6 +129,99 @@ func (state State) IsEmpty() bool { return state.Validators == nil // XXX can't compare to Empty } +//ToProto takes the local state type and returns the equivalent proto type +func (state *State) ToProto() (*tmstate.State, error) { + if state == nil { + return nil, errors.New("state is nil") + } + + sm := new(tmstate.State) + + sm.Version = state.Version + sm.ChainID = state.ChainID + sm.LastBlockHeight = state.LastBlockHeight + + sm.LastBlockID = state.LastBlockID.ToProto() + sm.LastBlockTime = state.LastBlockTime + vals, err := state.Validators.ToProto() + if err != nil { + return nil, err + } + sm.Validators = vals + + nVals, err := state.NextValidators.ToProto() + if err != nil { + return nil, err + } + sm.NextValidators = nVals + + if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil + lVals, err := state.LastValidators.ToProto() + if err != nil { + return nil, err + } + sm.LastValidators = lVals + } + + sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged + sm.ConsensusParams = state.ConsensusParams + sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged + sm.LastResultsHash = state.LastResultsHash + sm.AppHash = state.AppHash + + return sm, nil +} + +// StateFromProto takes a state proto message & returns the local state type +func StateFromProto(pb *tmstate.State) (*State, error) { //nolint:golint + if pb == nil { + return nil, errors.New("nil State") + } + + state := new(State) + + state.Version = pb.Version + state.ChainID = pb.ChainID + + bi, err := types.BlockIDFromProto(&pb.LastBlockID) + if err != nil { + return nil, err + } + state.LastBlockID = *bi + state.LastBlockHeight = pb.LastBlockHeight + state.LastBlockTime = pb.LastBlockTime + + vals, err := types.ValidatorSetFromProto(pb.Validators) + if err != nil { + return nil, err + } + state.Validators = vals + + nVals, err := types.ValidatorSetFromProto(pb.NextValidators) + if err != nil { + return nil, err + } + state.NextValidators = nVals + + if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil + lVals, err := types.ValidatorSetFromProto(pb.LastValidators) + if err != nil { + return nil, err + } + state.LastValidators = lVals + } else { + state.LastValidators = types.NewValidatorSet(nil) + } + + state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged + state.ConsensusParams = pb.ConsensusParams + state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged + state.LastResultsHash = pb.LastResultsHash + state.AppHash = pb.AppHash + + return state, nil +} + //------------------------------------------------------------------------ // Create a block from the latest state diff --git a/state/state_test.go b/state/state_test.go index 30c7fc708..83ba3bf95 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -18,6 +18,7 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto/ed25519" tmrand "github.com/tendermint/tendermint/libs/rand" + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -49,6 +50,7 @@ func TestStateCopy(t *testing.T) { stateCopy, state)) stateCopy.LastBlockHeight++ + stateCopy.LastValidators = state.Validators assert.False(state.Equals(stateCopy), fmt.Sprintf(`expected states to be different. got same %v`, state)) } @@ -73,6 +75,7 @@ func TestStateSaveLoad(t *testing.T) { assert := assert.New(t) state.LastBlockHeight++ + state.LastValidators = state.Validators sm.SaveState(stateDB, state) loadedState := sm.LoadState(stateDB) @@ -91,7 +94,11 @@ func TestABCIResponsesSaveLoad1(t *testing.T) { // Build mock responses. block := makeBlock(state, 2) - abciResponses := sm.NewABCIResponses(block) + + abciResponses := new(tmstate.ABCIResponses) + dtxs := make([]*abci.ResponseDeliverTx, 2) + abciResponses.DeliverTxs = dtxs + abciResponses.DeliverTxs[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Events: nil} abciResponses.DeliverTxs[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Events: nil} abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{ @@ -148,6 +155,10 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { nil, nil, }, + 4: { + []*abci.ResponseDeliverTx{nil}, + nil, + }, } // Query all before, this should return error. @@ -160,7 +171,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { // Add all cases. for i, tc := range cases { h := int64(i + 1) // last block height, one below what we save - responses := &sm.ABCIResponses{ + responses := &tmstate.ABCIResponses{ DeliverTxs: tc.added, EndBlock: &abci.ResponseEndBlock{}, } @@ -172,7 +183,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { h := int64(i + 1) res, err := sm.LoadABCIResponses(stateDB, h) assert.NoError(err, "%d", i) - assert.Equal(tc.expected.Hash(), res.ResultsHash(), "%d", i) + assert.Equal(tc.expected.Hash(), sm.ABCIResponsesResultsHash(*res), "%d", i) } } @@ -407,7 +418,7 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { block := makeBlock(state, state.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -518,7 +529,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { block := makeBlock(state, state.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} // no updates: - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -618,7 +629,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { // no changes in voting power and both validators have same voting power // -> proposers should alternate: oldState := updatedState3 - abciResponses = &sm.ABCIResponses{ + abciResponses = &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -633,7 +644,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { for i := 0; i < 1000; i++ { // no validator updates: - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -690,7 +701,7 @@ func TestLargeGenesisValidator(t *testing.T) { oldState := state for i := 0; i < 10; i++ { // no updates: - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -719,7 +730,7 @@ func TestLargeGenesisValidator(t *testing.T) { firstAddedVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(firstAddedValPubKey), Power: firstAddedValVotingPower} validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{firstAddedVal}) assert.NoError(t, err) - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal}}, } block := makeBlock(oldState, oldState.LastBlockHeight+1) @@ -730,7 +741,7 @@ func TestLargeGenesisValidator(t *testing.T) { lastState := updatedState for i := 0; i < 200; i++ { // no updates: - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -764,7 +775,7 @@ func TestLargeGenesisValidator(t *testing.T) { validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{addedVal}) assert.NoError(t, err) - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{addedVal}}, } block := makeBlock(oldState, oldState.LastBlockHeight+1) @@ -776,7 +787,7 @@ func TestLargeGenesisValidator(t *testing.T) { // remove genesis validator: removeGenesisVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(genesisPubKey), Power: 0} - abciResponses = &sm.ABCIResponses{ + abciResponses = &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal}}, } block = makeBlock(oldState, oldState.LastBlockHeight+1) @@ -794,7 +805,7 @@ func TestLargeGenesisValidator(t *testing.T) { count := 0 isProposerUnchanged := true for isProposerUnchanged { - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -817,7 +828,7 @@ func TestLargeGenesisValidator(t *testing.T) { proposers := make([]*types.Validator, numVals) for i := 0; i < 100; i++ { // no updates: - abciResponses := &sm.ABCIResponses{ + abciResponses := &tmstate.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -980,7 +991,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) { for _, testCase := range testCases { p, err := sm.LoadConsensusParams(stateDB, testCase.height) assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", testCase.height)) - assert.Equal(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at + assert.EqualValues(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at height %d`, testCase.height)) } } @@ -1019,3 +1030,36 @@ func TestApplyUpdates(t *testing.T) { assert.Equal(t, tc.expected, res, "case %d", i) } } + +func TestStateProto(t *testing.T) { + tearDown, _, state := setupTestCase(t) + defer tearDown(t) + + tc := []struct { + testName string + state *sm.State + expPass bool + }{ + {"empty state", &sm.State{}, false}, + {"nil failure state", nil, false}, + {"success state", &state, true}, + } + + for _, tt := range tc { + tt := tt + pbs, err := tt.state.ToProto() + if !tt.expPass { + assert.Error(t, err) + } else { + assert.NoError(t, err, tt.testName) + } + + smt, err := sm.StateFromProto(pbs) + if tt.expPass { + require.NoError(t, err, tt.testName) + require.Equal(t, tt.state, smt, tt.testName) + } else { + require.Error(t, err, tt.testName) + } + } +} diff --git a/state/store.go b/state/store.go index 7f1e67e6d..c51393a7a 100644 --- a/state/store.go +++ b/state/store.go @@ -3,11 +3,13 @@ package state import ( "fmt" + "github.com/gogo/protobuf/proto" dbm "github.com/tendermint/tm-db" abci "github.com/tendermint/tendermint/abci/types" tmmath "github.com/tendermint/tendermint/libs/math" tmos "github.com/tendermint/tendermint/libs/os" + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" "github.com/tendermint/tendermint/types" ) @@ -56,6 +58,7 @@ func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State // to the database. func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) { state := LoadState(stateDB) + if state.IsEmpty() { var err error state, err = MakeGenesisState(genesisDoc) @@ -82,15 +85,21 @@ func loadState(db dbm.DB, key []byte) (state State) { return state } - err = cdc.UnmarshalBinaryBare(buf, &state) + sp := new(tmstate.State) + + err = proto.Unmarshal(buf, sp) if err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED tmos.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed: - %v\n`, err)) + %v\n`, err)) } - // TODO: ensure that buf is completely read. - return state + sm, err := StateFromProto(sp) + if err != nil { + panic(err) + } + + return *sm } // SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database. @@ -110,6 +119,7 @@ func saveState(db dbm.DB, state State, key []byte) { } // Save next validators. saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) + // Save next consensus params. saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams) err := db.SetSync(key, state.Bytes()) @@ -130,15 +140,6 @@ func BootstrapState(db dbm.DB, state State) error { //------------------------------------------------------------------------ -// ABCIResponses retains the responses -// of the various ABCI calls during block processing. -// It is persisted to disk for each height before calling Commit. -type ABCIResponses struct { - DeliverTxs []*abci.ResponseDeliverTx `json:"deliver_txs"` - EndBlock *abci.ResponseEndBlock `json:"end_block"` - BeginBlock *abci.ResponseBeginBlock `json:"begin_block"` -} - // PruneStates deletes states between the given heights (including from, excluding to). It is not // guaranteed to delete all states, since the last checkpointed state and states being pointed to by // e.g. `LastHeightChanged` must remain. The state at to must also exist. @@ -187,12 +188,25 @@ func PruneStates(db dbm.DB, from int64, to int64) error { if keepVals[h] { v := loadValidatorsInfo(db, h) if v.ValidatorSet == nil { - v.ValidatorSet, err = LoadValidators(db, h) + + vip, err := LoadValidators(db, h) if err != nil { return err } + + pvi, err := vip.ToProto() + if err != nil { + return err + } + + v.ValidatorSet = pvi v.LastHeightChanged = h - batch.Set(calcValidatorsKey(h), v.Bytes()) + + bz, err := v.Marshal() + if err != nil { + return err + } + batch.Set(calcValidatorsKey(h), bz) } } else { batch.Delete(calcValidatorsKey(h)) @@ -206,7 +220,11 @@ func PruneStates(db dbm.DB, from int64, to int64) error { return err } p.LastHeightChanged = h - batch.Set(calcConsensusParamsKey(h), p.Bytes()) + bz, err := p.Marshal() + if err != nil { + return err + } + batch.Set(calcConsensusParamsKey(h), bz) } } else { batch.Delete(calcConsensusParamsKey(h)) @@ -235,42 +253,27 @@ func PruneStates(db dbm.DB, from int64, to int64) error { return nil } -// NewABCIResponses returns a new ABCIResponses -func NewABCIResponses(block *types.Block) *ABCIResponses { - resDeliverTxs := make([]*abci.ResponseDeliverTx, len(block.Data.Txs)) - if len(block.Data.Txs) == 0 { - // This makes Amino encoding/decoding consistent. - resDeliverTxs = nil - } - return &ABCIResponses{ - DeliverTxs: resDeliverTxs, - } -} - -// Bytes serializes the ABCIResponse using go-amino. -func (arz *ABCIResponses) Bytes() []byte { - return cdc.MustMarshalBinaryBare(arz) -} - -func (arz *ABCIResponses) ResultsHash() []byte { - results := types.NewResults(arz.DeliverTxs) +// ABCIResponsesResultsHash returns the merkle hash of the deliverTxs within ABCIResponses +func ABCIResponsesResultsHash(ar tmstate.ABCIResponses) []byte { + results := types.NewResults(ar.DeliverTxs) return results.Hash() } // LoadABCIResponses loads the ABCIResponses for the given height from the database. // This is useful for recovering from crashes where we called app.Commit and before we called // s.Save(). It can also be used to produce Merkle proofs of the result of txs. -func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) { +func LoadABCIResponses(db dbm.DB, height int64) (*tmstate.ABCIResponses, error) { buf, err := db.Get(calcABCIResponsesKey(height)) if err != nil { return nil, err } if len(buf) == 0 { + return nil, ErrNoABCIResponsesForHeight{height} } - abciResponses := new(ABCIResponses) - err = cdc.UnmarshalBinaryBare(buf, abciResponses) + abciResponses := new(tmstate.ABCIResponses) + err = abciResponses.Unmarshal(buf) if err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has @@ -287,22 +290,24 @@ func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) { // Merkle proofs. // // Exposed for testing. -func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) { - db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes()) -} - -//----------------------------------------------------------------------------- +func SaveABCIResponses(db dbm.DB, height int64, abciResponses *tmstate.ABCIResponses) { + var dtxs []*abci.ResponseDeliverTx + //strip nil values, + for _, tx := range abciResponses.DeliverTxs { + if tx != nil { + dtxs = append(dtxs, tx) + } + } + abciResponses.DeliverTxs = dtxs -// ValidatorsInfo represents the latest validator set, or the last height it changed -type ValidatorsInfo struct { - ValidatorSet *types.ValidatorSet - LastHeightChanged int64 + bz, err := abciResponses.Marshal() + if err != nil { + panic(err) + } + db.SetSync(calcABCIResponsesKey(height), bz) } -// Bytes serializes the ValidatorsInfo using go-amino. -func (valInfo *ValidatorsInfo) Bytes() []byte { - return cdc.MustMarshalBinaryBare(valInfo) -} +//----------------------------------------------------------------------------- // LoadValidators loads the ValidatorSet for a given height. // Returns ErrNoValSetForHeight if the validator set can't be found for this height. @@ -322,11 +327,28 @@ func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) { ), ) } - valInfo2.ValidatorSet.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate + + vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet) + if err != nil { + return nil, err + } + + vs.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate + vi2, err := vs.ToProto() + if err != nil { + return nil, err + } + + valInfo2.ValidatorSet = vi2 valInfo = valInfo2 } - return valInfo.ValidatorSet, nil + vip, err := types.ValidatorSetFromProto(valInfo.ValidatorSet) + if err != nil { + return nil, err + } + + return vip, nil } func lastStoredHeightFor(height, lastHeightChanged int64) int64 { @@ -335,17 +357,18 @@ func lastStoredHeightFor(height, lastHeightChanged int64) int64 { } // CONTRACT: Returned ValidatorsInfo can be mutated. -func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { +func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo { buf, err := db.Get(calcValidatorsKey(height)) if err != nil { panic(err) } + if len(buf) == 0 { return nil } - v := new(ValidatorsInfo) - err = cdc.UnmarshalBinaryBare(buf, v) + v := new(tmstate.ValidatorsInfo) + err = v.Unmarshal(buf) if err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed: @@ -365,29 +388,30 @@ func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *type if lastHeightChanged > height { panic("LastHeightChanged cannot be greater than ValidatorsInfo height") } - valInfo := &ValidatorsInfo{ + valInfo := &tmstate.ValidatorsInfo{ LastHeightChanged: lastHeightChanged, } // Only persist validator set if it was updated or checkpoint height (see // valSetCheckpointInterval) is reached. if height == lastHeightChanged || height%valSetCheckpointInterval == 0 { - valInfo.ValidatorSet = valSet + pv, err := valSet.ToProto() + if err != nil { + panic(err) + } + valInfo.ValidatorSet = pv } - db.Set(calcValidatorsKey(height), valInfo.Bytes()) + + bz, err := valInfo.Marshal() + if err != nil { + panic(err) + } + + db.Set(calcValidatorsKey(height), bz) } //----------------------------------------------------------------------------- // ConsensusParamsInfo represents the latest consensus params, or the last height it changed -type ConsensusParamsInfo struct { - ConsensusParams tmproto.ConsensusParams - LastHeightChanged int64 -} - -// Bytes serializes the ConsensusParamsInfo using go-amino. -func (params ConsensusParamsInfo) Bytes() []byte { - return cdc.MustMarshalBinaryBare(params) -} // LoadConsensusParams loads the ConsensusParams for a given height. func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, error) { @@ -409,13 +433,14 @@ func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, erro ), ) } + paramsInfo = paramsInfo2 } return paramsInfo.ConsensusParams, nil } -func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { +func loadConsensusParamsInfo(db dbm.DB, height int64) *tmstate.ConsensusParamsInfo { buf, err := db.Get(calcConsensusParamsKey(height)) if err != nil { panic(err) @@ -424,9 +449,8 @@ func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { return nil } - paramsInfo := new(ConsensusParamsInfo) - err = cdc.UnmarshalBinaryBare(buf, paramsInfo) - if err != nil { + paramsInfo := new(tmstate.ConsensusParamsInfo) + if err = paramsInfo.Unmarshal(buf); err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed: %v\n`, err)) @@ -441,11 +465,17 @@ func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { // If the consensus params did not change after processing the latest block, // only the last height for which they changed is persisted. func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params tmproto.ConsensusParams) { - paramsInfo := &ConsensusParamsInfo{ + paramsInfo := &tmstate.ConsensusParamsInfo{ LastHeightChanged: changeHeight, } + if changeHeight == nextHeight { paramsInfo.ConsensusParams = params } - db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes()) + bz, err := paramsInfo.Marshal() + if err != nil { + panic(err) + } + + db.Set(calcConsensusParamsKey(nextHeight), bz) } diff --git a/state/store_test.go b/state/store_test.go index 9084621d8..04b952373 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -10,7 +10,10 @@ import ( dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto/ed25519" + tmstate "github.com/tendermint/tendermint/proto/state" tmproto "github.com/tendermint/tendermint/proto/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -48,6 +51,7 @@ func BenchmarkLoadValidators(b *testing.B) { if err != nil { b.Fatal(err) } + state.Validators = genValSet(valSetSize) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) sm.SaveState(stateDB, state) @@ -91,10 +95,11 @@ func TestPruneStates(t *testing.T) { tc := tc t.Run(name, func(t *testing.T) { db := dbm.NewMemDB() + pk := ed25519.GenPrivKey().PubKey() // Generate a bunch of state data. Validators change for heights ending with 3, and // parameters when ending with 5. - validator := &types.Validator{Address: []byte{1, 2, 3}, VotingPower: 100} + validator := &types.Validator{Address: []byte{1, 2, 3}, VotingPower: 100, PubKey: pk} validatorSet := &types.ValidatorSet{ Validators: []*types.Validator{validator}, Proposer: validator, @@ -110,7 +115,7 @@ func TestPruneStates(t *testing.T) { paramsChanged = h } - sm.SaveState(db, sm.State{ + state := sm.State{ LastBlockHeight: h - 1, Validators: validatorSet, NextValidators: validatorSet, @@ -119,17 +124,21 @@ func TestPruneStates(t *testing.T) { }, LastHeightValidatorsChanged: valsChanged, LastHeightConsensusParamsChanged: paramsChanged, - }) - sm.SaveABCIResponses(db, h, sm.NewABCIResponses(&types.Block{ - Header: types.Header{Height: h}, - Data: types.Data{ - Txs: types.Txs{ - []byte{1}, - []byte{2}, - []byte{3}, - }, + } + + if state.LastBlockHeight >= 1 { + state.LastValidators = state.Validators + } + + sm.SaveState(db, state) + + sm.SaveABCIResponses(db, h, &tmstate.ABCIResponses{ + DeliverTxs: []*abci.ResponseDeliverTx{ + {Data: []byte{1}}, + {Data: []byte{2}}, + {Data: []byte{3}}, }, - })) + }) } // Test assertions diff --git a/state/validation.go b/state/validation.go index 8d8db3240..018b8d295 100644 --- a/state/validation.go +++ b/state/validation.go @@ -40,7 +40,6 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block block.Height, ) } - // Validate prev block info. if !block.LastBlockID.Equals(state.LastBlockID) { return fmt.Errorf("wrong Block.Header.LastBlockID. Expected %v, got %v", @@ -103,7 +102,6 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block state.LastBlockTime, ) } - medianTime := MedianTime(block.LastCommit, state.LastValidators) if !block.Time.Equal(medianTime) { return fmt.Errorf("invalid block time. Expected %v, got %v", @@ -199,7 +197,6 @@ func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence, commit state.LastBlockTime.Add(evidenceParams.MaxAgeDuration), ) } - if ev, ok := evidence.(*types.LunaticValidatorEvidence); ok { if err := ev.VerifyHeader(committedHeader); err != nil { return err diff --git a/statesync/stateprovider.go b/statesync/stateprovider.go index a6e411465..268098e96 100644 --- a/statesync/stateprovider.go +++ b/statesync/stateprovider.go @@ -14,6 +14,7 @@ import ( lighthttp "github.com/tendermint/tendermint/light/provider/http" lightrpc "github.com/tendermint/tendermint/light/rpc" lightdb "github.com/tendermint/tendermint/light/store/db" + tmstate "github.com/tendermint/tendermint/proto/state" rpchttp "github.com/tendermint/tendermint/rpc/client/http" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -36,14 +37,14 @@ type StateProvider interface { type lightClientStateProvider struct { sync.Mutex // light.Client is not concurrency-safe lc *light.Client - version sm.Version + version tmstate.Version providers map[lightprovider.Provider]string } // NewLightClientStateProvider creates a new StateProvider using a light client and RPC clients. func NewLightClientStateProvider( chainID string, - version sm.Version, + version tmstate.Version, servers []string, trustOptions light.TrustOptions, logger log.Logger, diff --git a/statesync/syncer_test.go b/statesync/syncer_test.go index 0f8e6b40e..0b51a5e07 100644 --- a/statesync/syncer_test.go +++ b/statesync/syncer_test.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" p2pmocks "github.com/tendermint/tendermint/p2p/mocks" + tmstate "github.com/tendermint/tendermint/proto/state" ssproto "github.com/tendermint/tendermint/proto/statesync" tmversion "github.com/tendermint/tendermint/proto/version" "github.com/tendermint/tendermint/proxy" @@ -44,7 +45,7 @@ func simplePeer(id string) *p2pmocks.Peer { func TestSyncer_SyncAny(t *testing.T) { state := sm.State{ ChainID: "chain", - Version: sm.Version{ + Version: tmstate.Version{ Consensus: tmversion.Consensus{ Block: version.BlockProtocol, App: 0,