Browse Source

fixes for handshake replay through consensus

pull/408/head
Ethan Buchman 8 years ago
parent
commit
6403b2f468
4 changed files with 67 additions and 62 deletions
  1. +35
    -28
      consensus/handshake_test.go_
  2. +9
    -24
      consensus/state.go
  3. +7
    -3
      node/node.go
  4. +16
    -7
      state/execution.go

state/execution_test.go → consensus/handshake_test.go_ View File


+ 9
- 24
consensus/state.go View File

@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
"io"
"reflect"
"sync"
"time"
@ -14,7 +13,6 @@ import (
. "github.com/tendermint/go-common"
cfg "github.com/tendermint/go-config"
"github.com/tendermint/go-wire"
bc "github.com/tendermint/tendermint/blockchain"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
@ -225,7 +223,7 @@ type ConsensusState struct {
config cfg.Config
proxyAppConn proxy.AppConnConsensus
blockStore *bc.BlockStore
blockStore sm.BlockStore
mempool sm.Mempool
privValidator PrivValidator // for signing votes
@ -256,18 +254,20 @@ type ConsensusState struct {
func ReplayLastBlock(config cfg.Config, state *sm.State, proxyApp proxy.AppConnConsensus, blockStore sm.BlockStore) {
mempool := sm.MockMempool{}
cs := NewConsensusState(config, state, proxyApp, blockStore.(*bc.BlockStore), mempool)
cs := NewConsensusState(config, state, proxyApp, blockStore, mempool)
evsw := types.NewEventSwitch()
evsw.Start()
cs.SetEventSwitch(evsw)
newBlockCh := subscribeToEvent(evsw, "consensus-replay", types.EventStringNewBlock(), 0)
newBlockCh := subscribeToEvent(evsw, "consensus-replay", types.EventStringNewBlock(), 1)
// run through the WAL, commit new block, stop
cs.Start()
<-newBlockCh
cs.Stop()
}
func NewConsensusState(config cfg.Config, state *sm.State, proxyAppConn proxy.AppConnConsensus, blockStore *bc.BlockStore, mempool sm.Mempool) *ConsensusState {
func NewConsensusState(config cfg.Config, state *sm.State, proxyAppConn proxy.AppConnConsensus, blockStore sm.BlockStore, mempool sm.Mempool) *ConsensusState {
cs := &ConsensusState{
config: config,
proxyAppConn: proxyAppConn,
@ -366,23 +366,6 @@ func (cs *ConsensusState) OnStart() error {
return err
}
// If the latest block was applied in the abci handshake,
// we may not have written the current height to the wal,
// so check here and write it if not found.
// TODO: remove this and run the handhsake/replay
// through the consensus state with a mock app
gr, found, err := cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(cs.Height))
if (err == io.EOF || !found) && cs.Step == RoundStepNewHeight {
log.Warn("Height not found in wal. Writing new height", "height", cs.Height)
rs := cs.RoundStateEvent()
cs.wal.Save(rs)
} else if err != nil {
return err
}
if gr != nil {
gr.Close()
}
// we need the timeoutRoutine for replay so
// we don't block on the tick chan.
// NOTE: we will get a build up of garbage go routines
@ -581,7 +564,6 @@ func (cs *ConsensusState) updateToState(state *sm.State) {
// Reset fields based on state.
validators := state.Validators
height := state.LastBlockHeight + 1 // Next desired block height
lastPrecommits := (*types.VoteSet)(nil)
if cs.CommitRound > -1 && cs.Votes != nil {
if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() {
@ -590,6 +572,9 @@ func (cs *ConsensusState) updateToState(state *sm.State) {
lastPrecommits = cs.Votes.Precommits(cs.CommitRound)
}
// Next desired block height
height := state.LastBlockHeight + 1
// RoundState fields
cs.updateHeight(height)
cs.updateRoundStep(0, RoundStepNewHeight)


+ 7
- 3
node/node.go View File

@ -63,15 +63,19 @@ func NewNode(config cfg.Config, privValidator *types.PrivValidator, clientCreato
stateDB := dbm.NewDB("state", config.GetString("db_backend"), config.GetString("db_dir"))
state := sm.GetState(config, stateDB)
// add the chainid and number of validators to the global config
config.Set("chain_id", state.ChainID)
config.Set("num_vals", state.Validators.Size())
// Create the proxyApp, which manages connections (consensus, mempool, query)
// and sync tendermint and the app by replaying any necessary blocks
proxyApp := proxy.NewAppConns(config, clientCreator, sm.NewHandshaker(config, state, blockStore, consensus.ReplayLastBlock))
if _, err := proxyApp.Start(); err != nil {
cmn.Exit(cmn.Fmt("Error starting proxy app connections: %v", err))
}
// add the chainid and number of validators to the global config
config.Set("chain_id", state.ChainID)
config.Set("num_vals", state.Validators.Size())
// reload the state (it may have been updated by the handshake)
state = sm.LoadState(stateDB)
// Generate node PrivKey
privKey := crypto.GenPrivKeyEd25519()


+ 16
- 7
state/execution.go View File

@ -298,8 +298,15 @@ func (m MockMempool) Update(height int, txs types.Txs) {}
// TODO: Should we move blockchain/store.go to its own package?
type BlockStore interface {
Height() int
LoadBlock(height int) *types.Block
LoadBlockMeta(height int) *types.BlockMeta
LoadBlock(height int) *types.Block
LoadBlockPart(height int, index int) *types.Part
SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit)
LoadBlockCommit(height int) *types.Commit
LoadSeenCommit(height int) *types.Commit
}
type blockReplayFunc func(cfg.Config, *State, proxy.AppConnConsensus, BlockStore)
@ -317,6 +324,10 @@ func NewHandshaker(config cfg.Config, state *State, store BlockStore, f blockRep
return &Handshaker{config, state, store, f, 0}
}
func (h *Handshaker) NBlocks() int {
return h.nBlocks
}
// TODO: retry the handshake/replay if it fails ?
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
// handshake is done via info request on the query conn
@ -338,9 +349,6 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
return errors.New(Fmt("Error on replay: %v", err))
}
// Save the state
h.state.Save()
// TODO: (on restart) replay mempool
return nil
@ -359,16 +367,17 @@ func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, proxyApp p
// if the app is ahead, there's nothing we can do
return ErrAppBlockHeightTooHigh{storeBlockHeight, appBlockHeight}
} else if storeBlockHeight == appBlockHeight {
// We already ran Commit, so run through consensus with mock app
} else if storeBlockHeight == appBlockHeight && storeBlockHeight == stateBlockHeight+1 {
// We already ran Commit, but didn't save the state, so run through consensus with mock app
mockApp := newMockProxyApp(appHash)
log.Info("Replay last block using mock app")
h.replayLastBlock(h.config, h.state, mockApp, h.store)
} else if storeBlockHeight == appBlockHeight+1 {
// We crashed after saving the block
// but before Commit (both the state and app are behind),
// so run through consensus with the real app
log.Info("Replay last block using real app")
h.replayLastBlock(h.config, h.state, proxyApp.Consensus(), h.store)
} else {


Loading…
Cancel
Save