diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 91f0adede..9cc01fbac 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -175,7 +175,7 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) // maxMsgSize returns the maximum allowable size of a // message on the blockchain reactor. func (bcR *BlockchainReactor) maxMsgSize() int { - return bcR.state.Params().BlockSizeParams.MaxBytes + 2 + return bcR.state.Params.BlockSizeParams.MaxBytes + 2 } // Handle messages from the poolReactor telling the reactor what to do. @@ -187,6 +187,8 @@ func (bcR *BlockchainReactor) poolRoutine() { statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second) switchToConsensusTicker := time.NewTicker(switchToConsensusIntervalSeconds * time.Second) + chainID := bcR.state.ChainID + FOR_LOOP: for { select { @@ -236,14 +238,14 @@ FOR_LOOP: // We need both to sync the first block. break SYNC_LOOP } - firstParts := first.MakePartSet(bcR.state.Params().BlockPartSizeBytes) + firstParts := first.MakePartSet(bcR.state.Params.BlockPartSizeBytes) firstPartsHeader := firstParts.Header() // Finally, verify the first block using the second's commit // NOTE: we can probably make this more efficient, but note that calling // first.Hash() doesn't verify the tx contents, so MakePartSet() is // currently necessary. err := bcR.state.Validators.VerifyCommit( - bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit) + chainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit) if err != nil { bcR.Logger.Error("Error in validation", "err", err) bcR.pool.RedoRequest(first.Height) diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 492ea7a80..633cae169 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -42,7 +42,7 @@ func newBlockchainReactor(logger log.Logger, maxBlockHeight int) *BlockchainReac for blockHeight := 1; blockHeight <= maxBlockHeight; blockHeight++ { firstBlock := makeBlock(blockHeight, state) secondBlock := makeBlock(blockHeight+1, state) - firstParts := firstBlock.MakePartSet(state.Params().BlockGossipParams.BlockPartSizeBytes) + firstParts := firstBlock.MakePartSet(state.Params.BlockGossipParams.BlockPartSizeBytes) blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit) } @@ -113,7 +113,7 @@ func makeBlock(blockNumber int, state *sm.State) *types.Block { valHash := state.Validators.Hash() prevBlockID := types.BlockID{prevHash, prevParts} block, _ := types.MakeBlock(blockNumber, "test_chain", makeTxs(blockNumber), - new(types.Commit), prevBlockID, valHash, state.AppHash, state.Params().BlockGossipParams.BlockPartSizeBytes) + new(types.Commit), prevBlockID, valHash, state.AppHash, state.Params.BlockGossipParams.BlockPartSizeBytes) return block } diff --git a/blockchain/store.go b/blockchain/store.go index a96aa0fb7..79edfeaf5 100644 --- a/blockchain/store.go +++ b/blockchain/store.go @@ -7,10 +7,10 @@ import ( "io" "sync" + wire "github.com/tendermint/go-wire" + "github.com/tendermint/tendermint/types" . "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" - "github.com/tendermint/go-wire" - "github.com/tendermint/tendermint/types" ) /* diff --git a/consensus/replay_test.go b/consensus/replay_test.go index c478a0958..98295ec44 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -382,7 +382,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { } func applyBlock(st *sm.State, blk *types.Block, proxyApp proxy.AppConns) { - testPartSize := st.Params().BlockPartSizeBytes + testPartSize := st.Params.BlockPartSizeBytes err := st.ApplyBlock(nil, proxyApp.Consensus(), blk, blk.MakePartSet(testPartSize).Header(), mempool) if err != nil { panic(err) @@ -562,7 +562,7 @@ func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (*sm.State, *mockBl state, _ := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) state.SetLogger(log.TestingLogger().With("module", "state")) - store := NewMockBlockStore(config, state.Params()) + store := NewMockBlockStore(config, state.Params) return state, store } diff --git a/consensus/state.go b/consensus/state.go index f0fbad811..94ecd2a2c 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -390,7 +390,7 @@ func (cs *ConsensusState) reconstructLastCommit(state *sm.State) { return } seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) - lastPrecommits := types.NewVoteSet(cs.state.ChainID, state.LastBlockHeight, seenCommit.Round(), types.VoteTypePrecommit, state.LastValidators) + lastPrecommits := types.NewVoteSet(state.ChainID, state.LastBlockHeight, seenCommit.Round(), types.VoteTypePrecommit, state.LastValidators) for _, precommit := range seenCommit.Precommits { if precommit == nil { continue @@ -707,6 +707,7 @@ func (cs *ConsensusState) proposalHeartbeat(height, round int) { // not a validator valIndex = -1 } + chainID := cs.state.ChainID for { rs := cs.GetRoundState() // if we've already moved on, no need to send more heartbeats @@ -720,7 +721,7 @@ func (cs *ConsensusState) proposalHeartbeat(height, round int) { ValidatorAddress: addr, ValidatorIndex: valIndex, } - cs.privValidator.SignHeartbeat(cs.state.ChainID, heartbeat) + cs.privValidator.SignHeartbeat(chainID, heartbeat) heartbeatEvent := types.EventDataProposalHeartbeat{heartbeat} types.FireEventProposalHeartbeat(cs.evsw, heartbeatEvent) counter += 1 @@ -797,8 +798,7 @@ func (cs *ConsensusState) defaultDecideProposal(height, round int) { // Make proposal polRound, polBlockID := cs.Votes.POLInfo() proposal := types.NewProposal(height, round, blockParts.Header(), polRound, polBlockID) - err := cs.privValidator.SignProposal(cs.state.ChainID, proposal) - if err == nil { + if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal); err == nil { // Set fields /* fields set by setProposal and addBlockPart cs.Proposal = proposal @@ -857,10 +857,9 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts // Mempool validated transactions txs := cs.mempool.Reap(cs.config.MaxBlockSizeTxs) - return types.MakeBlock(cs.Height, cs.state.ChainID, txs, commit, cs.state.LastBlockID, cs.state.Validators.Hash(), - cs.state.AppHash, cs.state.Params().BlockPartSizeBytes) + cs.state.AppHash, cs.state.Params.BlockPartSizeBytes) } // Enter: `timeoutPropose` after entering Propose. @@ -1295,7 +1294,7 @@ func (cs *ConsensusState) addProposalBlockPart(height int, part *types.Part, ver var n int var err error cs.ProposalBlock = wire.ReadBinary(&types.Block{}, cs.ProposalBlockParts.GetReader(), - cs.state.Params().BlockSizeParams.MaxBytes, &n, &err).(*types.Block) + cs.state.Params.BlockSizeParams.MaxBytes, &n, &err).(*types.Block) // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() { diff --git a/consensus/state_test.go b/consensus/state_test.go index 69b6d53ce..060e37d4e 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -181,7 +181,7 @@ func TestBadProposal(t *testing.T) { height, round := cs1.Height, cs1.Round vs2 := vss[1] - partSize := cs1.state.Params().BlockPartSizeBytes + partSize := cs1.state.Params.BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1) @@ -328,7 +328,7 @@ func TestLockNoPOL(t *testing.T) { vs2 := vss[1] height := cs1.Height - partSize := cs1.state.Params().BlockPartSizeBytes + partSize := cs1.state.Params.BlockPartSizeBytes timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) @@ -494,7 +494,7 @@ func TestLockPOLRelock(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.Params().BlockPartSizeBytes + partSize := cs1.state.Params.BlockPartSizeBytes timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) @@ -607,7 +607,7 @@ func TestLockPOLUnlock(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.Params().BlockPartSizeBytes + partSize := cs1.state.Params.BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) @@ -702,7 +702,7 @@ func TestLockPOLSafety1(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.Params().BlockPartSizeBytes + partSize := cs1.state.Params.BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) @@ -823,7 +823,7 @@ func TestLockPOLSafety2(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.Params().BlockPartSizeBytes + partSize := cs1.state.Params.BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) @@ -998,7 +998,7 @@ func TestHalt1(t *testing.T) { cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] - partSize := cs1.state.Params().BlockPartSizeBytes + partSize := cs1.state.Params.BlockPartSizeBytes proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) diff --git a/node/node.go b/node/node.go index 824a0926c..9dfe617db 100644 --- a/node/node.go +++ b/node/node.go @@ -2,6 +2,7 @@ package node import ( "bytes" + "encoding/json" "errors" "fmt" "net" @@ -132,19 +133,27 @@ func NewNode(config *cfg.Config, if err != nil { return nil, err } - state := sm.LoadState(stateDB) - if state == nil { - genDoc, err := genesisDocProvider() + + // Get genesis doc + genDoc, err := loadGenesisDoc(stateDB) + if err != nil { + genDoc, err = genesisDocProvider() if err != nil { return nil, err } + // save genesis doc to prevent a certain class of user errors (e.g. when it + // was changed, accidentally or not). Also good for audit trail. + saveGenesisDoc(stateDB, genDoc) + } + + state := sm.LoadState(stateDB) + if state == nil { state, err = sm.MakeGenesisState(stateDB, genDoc) if err != nil { return nil, err } state.Save() } - state.SetLogger(stateLogger) // Create the proxyApp, which manages connections (consensus, mempool, query) @@ -286,7 +295,7 @@ func NewNode(config *cfg.Config, node := &Node{ config: config, - genesisDoc: state.GenesisDoc, + genesisDoc: genDoc, privValidator: privValidator, privKey: privKey, @@ -485,11 +494,10 @@ func (n *Node) makeNodeInfo() *p2p.NodeInfo { if _, ok := n.txIndexer.(*null.TxIndex); ok { txIndexerStatus = "off" } - nodeInfo := &p2p.NodeInfo{ PubKey: n.privKey.PubKey().Unwrap().(crypto.PubKeyEd25519), Moniker: n.config.Moniker, - Network: n.consensusState.GetState().ChainID, + Network: n.genesisDoc.ChainID, Version: version.Version, Other: []string{ cmn.Fmt("wire_version=%v", wire.Version), @@ -536,3 +544,31 @@ func (n *Node) DialSeeds(seeds []string) error { } //------------------------------------------------------------------------------ + +var ( + genesisDocKey = []byte("genesisDoc") +) + +// panics if failed to unmarshal bytes +func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) { + bytes := db.Get(genesisDocKey) + if len(bytes) == 0 { + return nil, errors.New("Genesis doc not found") + } else { + var genDoc *types.GenesisDoc + err := json.Unmarshal(bytes, &genDoc) + if err != nil { + cmn.PanicCrisis(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes)) + } + return genDoc, nil + } +} + +// panics if failed to marshal the given genesis document +func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) { + bytes, err := json.Marshal(genDoc) + if err != nil { + cmn.PanicCrisis(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err)) + } + db.SetSync(genesisDocKey, bytes) +} diff --git a/state/state.go b/state/state.go index 53ec8cc03..d3646cf18 100644 --- a/state/state.go +++ b/state/state.go @@ -38,9 +38,9 @@ type State struct { mtx sync.Mutex db dbm.DB - // should not change - GenesisDoc *types.GenesisDoc - ChainID string + ChainID string + // Consensus parameters used for validating blocks + Params types.ConsensusParams // These fields are updated by SetBlockAndValidators. // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) @@ -69,15 +69,16 @@ type State struct { // or creates a new one from the given genesisFile and persists the result // to the database. func GetState(stateDB dbm.DB, genesisFile string) (*State, error) { - var err error state := LoadState(stateDB) if state == nil { + var err error state, err = MakeGenesisStateFromFile(stateDB, genesisFile) if err != nil { return nil, err } state.Save() } + return state, nil } @@ -100,7 +101,6 @@ func loadState(db dbm.DB, key []byte) *State { } // TODO: ensure that buf is completely read. } - return s } @@ -113,8 +113,6 @@ func (s *State) SetLogger(l log.Logger) { func (s *State) Copy() *State { return &State{ db: s.db, - GenesisDoc: s.GenesisDoc, - ChainID: s.ChainID, LastBlockHeight: s.LastBlockHeight, LastBlockID: s.LastBlockID, LastBlockTime: s.LastBlockTime, @@ -123,7 +121,9 @@ func (s *State) Copy() *State { AppHash: s.AppHash, TxIndexer: s.TxIndexer, // pointer here, not value LastHeightValidatorsChanged: s.LastHeightValidatorsChanged, - logger: s.logger, + logger: s.logger, + ChainID: s.ChainID, + Params: s.Params, } } @@ -168,7 +168,7 @@ func (s *State) LoadValidators(height int) (*types.ValidatorSet, error) { if v.ValidatorSet == nil { v = s.loadValidators(v.LastHeightChanged) if v == nil { - cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at + cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at height %d as last changed from height %d`, v.LastHeightChanged, height)) } } @@ -264,14 +264,6 @@ func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) { return s.LastValidators, s.Validators } -// Params returns the consensus parameters used for -// validating blocks -func (s *State) Params() types.ConsensusParams { - // TODO: this should move into the State proper - // when we allow the app to change it - return *s.GenesisDoc.ConsensusParams -} - //------------------------------------------------------------------------ // ABCIResponses retains the responses of the various ABCI calls during block processing. @@ -341,8 +333,6 @@ func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { } // MakeGenesisState creates state from types.GenesisDoc. -// -// Used in tests. func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) { err := genDoc.ValidateAndComplete() if err != nil { @@ -364,9 +354,11 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) { } return &State{ - db: db, - GenesisDoc: genDoc, - ChainID: genDoc.ChainID, + db: db, + + ChainID: genDoc.ChainID, + Params: *genDoc.ConsensusParams, + LastBlockHeight: 0, LastBlockID: types.BlockID{}, LastBlockTime: genDoc.GenesisTime, diff --git a/state/state_test.go b/state/state_test.go index 2ab2e9349..8ac2eada0 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -12,6 +12,7 @@ import ( abci "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" + cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" @@ -127,7 +128,8 @@ func TestValidatorChangesSaveLoad(t *testing.T) { // each valset is just one validator. // create list of them pubkeys := make([]crypto.PubKey, N+1) - pubkeys[0] = state.GenesisDoc.Validators[0].PubKey + _, val := state.Validators.GetByIndex(0) + pubkeys[0] = val.PubKey for i := 1; i < N+1; i++ { pubkeys[i] = crypto.GenPrivKeyEd25519().PubKey() }