|
@ -8,6 +8,7 @@ import ( |
|
|
|
|
|
|
|
|
. "github.com/tendermint/go-common" |
|
|
. "github.com/tendermint/go-common" |
|
|
cfg "github.com/tendermint/go-config" |
|
|
cfg "github.com/tendermint/go-config" |
|
|
|
|
|
"github.com/tendermint/go-crypto" |
|
|
"github.com/tendermint/tendermint/proxy" |
|
|
"github.com/tendermint/tendermint/proxy" |
|
|
"github.com/tendermint/tendermint/types" |
|
|
"github.com/tendermint/tendermint/types" |
|
|
tmsp "github.com/tendermint/tmsp/types" |
|
|
tmsp "github.com/tendermint/tmsp/types" |
|
@ -21,26 +22,33 @@ import ( |
|
|
func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnConsensus, block *types.Block, blockPartsHeader types.PartSetHeader) error { |
|
|
func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnConsensus, block *types.Block, blockPartsHeader types.PartSetHeader) error { |
|
|
|
|
|
|
|
|
// Validate the block.
|
|
|
// Validate the block.
|
|
|
err := s.validateBlock(block) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
|
if err := s.validateBlock(block); err != nil { |
|
|
return ErrInvalidBlock(err) |
|
|
return ErrInvalidBlock(err) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Update the validator set
|
|
|
|
|
|
|
|
|
// compute bitarray of validators that signed
|
|
|
|
|
|
signed := commitBitArrayFromBlock(block) |
|
|
|
|
|
_ = signed // TODO send on begin block
|
|
|
|
|
|
|
|
|
|
|
|
// copy the valset
|
|
|
valSet := s.Validators.Copy() |
|
|
valSet := s.Validators.Copy() |
|
|
// Update valSet with signatures from block.
|
|
|
|
|
|
updateValidatorsWithBlock(s.LastValidators, valSet, block) |
|
|
|
|
|
// TODO: Update the validator set (e.g. block.Data.ValidatorUpdates?)
|
|
|
|
|
|
nextValSet := valSet.Copy() |
|
|
nextValSet := valSet.Copy() |
|
|
|
|
|
|
|
|
// Execute the block txs
|
|
|
// Execute the block txs
|
|
|
err = s.execBlockOnProxyApp(eventCache, proxyAppConn, block) |
|
|
|
|
|
|
|
|
changedValidators, err := execBlockOnProxyApp(eventCache, proxyAppConn, block) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
// There was some error in proxyApp
|
|
|
// There was some error in proxyApp
|
|
|
// TODO Report error and wait for proxyApp to be available.
|
|
|
// TODO Report error and wait for proxyApp to be available.
|
|
|
return ErrProxyAppConn(err) |
|
|
return ErrProxyAppConn(err) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// update the validator set
|
|
|
|
|
|
err = updateValidators(nextValSet, changedValidators) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
log.Warn("Error changing validator set", "error", err) |
|
|
|
|
|
// TODO: err or carry on?
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// All good!
|
|
|
// All good!
|
|
|
// Update validator accums and set state variables
|
|
|
// Update validator accums and set state variables
|
|
|
nextValSet.IncrementAccum(1) |
|
|
nextValSet.IncrementAccum(1) |
|
@ -54,8 +62,9 @@ func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnC |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Executes block's transactions on proxyAppConn.
|
|
|
// Executes block's transactions on proxyAppConn.
|
|
|
|
|
|
// Returns a list of updates to the validator set
|
|
|
// TODO: Generate a bitmap or otherwise store tx validity in state.
|
|
|
// TODO: Generate a bitmap or otherwise store tx validity in state.
|
|
|
func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnConsensus, block *types.Block) error { |
|
|
|
|
|
|
|
|
func execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnConsensus, block *types.Block) ([]*tmsp.Validator, error) { |
|
|
|
|
|
|
|
|
var validTxs, invalidTxs = 0, 0 |
|
|
var validTxs, invalidTxs = 0, 0 |
|
|
|
|
|
|
|
@ -94,7 +103,7 @@ func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn prox |
|
|
err := proxyAppConn.BeginBlockSync(block.Hash(), types.TM2PB.Header(block.Header)) |
|
|
err := proxyAppConn.BeginBlockSync(block.Hash(), types.TM2PB.Header(block.Header)) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
log.Warn("Error in proxyAppConn.BeginBlock", "error", err) |
|
|
log.Warn("Error in proxyAppConn.BeginBlock", "error", err) |
|
|
return err |
|
|
|
|
|
|
|
|
return nil, err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fail.Fail() // XXX
|
|
|
fail.Fail() // XXX
|
|
@ -104,7 +113,7 @@ func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn prox |
|
|
fail.FailRand(len(block.Txs)) // XXX
|
|
|
fail.FailRand(len(block.Txs)) // XXX
|
|
|
proxyAppConn.AppendTxAsync(tx) |
|
|
proxyAppConn.AppendTxAsync(tx) |
|
|
if err := proxyAppConn.Error(); err != nil { |
|
|
if err := proxyAppConn.Error(); err != nil { |
|
|
return err |
|
|
|
|
|
|
|
|
return nil, err |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -114,44 +123,69 @@ func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn prox |
|
|
changedValidators, err := proxyAppConn.EndBlockSync(uint64(block.Height)) |
|
|
changedValidators, err := proxyAppConn.EndBlockSync(uint64(block.Height)) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
log.Warn("Error in proxyAppConn.EndBlock", "error", err) |
|
|
log.Warn("Error in proxyAppConn.EndBlock", "error", err) |
|
|
return err |
|
|
|
|
|
|
|
|
return nil, err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fail.Fail() // XXX
|
|
|
fail.Fail() // XXX
|
|
|
|
|
|
|
|
|
// TODO: Do something with changedValidators
|
|
|
|
|
|
log.Debug("TODO: Do something with changedValidators", "changedValidators", changedValidators) |
|
|
|
|
|
|
|
|
|
|
|
log.Info(Fmt("ExecBlock got %v valid txs and %v invalid txs", validTxs, invalidTxs)) |
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
|
log.Info("Executed block", "height", block.Height, "valid txs", validTxs, "invalid txs", invalidTxs) |
|
|
|
|
|
if len(changedValidators) > 0 { |
|
|
|
|
|
log.Info("Update to validator set", "updates", tmsp.ValidatorsString(changedValidators)) |
|
|
|
|
|
} |
|
|
|
|
|
return changedValidators, nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Updates the LastCommitHeight of the validators in valSet, in place.
|
|
|
|
|
|
// Assumes that lastValSet matches the valset of block.LastCommit
|
|
|
|
|
|
// CONTRACT: lastValSet is not mutated.
|
|
|
|
|
|
func updateValidatorsWithBlock(lastValSet *types.ValidatorSet, valSet *types.ValidatorSet, block *types.Block) { |
|
|
|
|
|
|
|
|
func updateValidators(validators *types.ValidatorSet, changedValidators []*tmsp.Validator) error { |
|
|
|
|
|
// TODO: prevent change of 1/3+ at once
|
|
|
|
|
|
|
|
|
for i, precommit := range block.LastCommit.Precommits { |
|
|
|
|
|
if precommit == nil { |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
for _, v := range changedValidators { |
|
|
|
|
|
pubkey, err := crypto.PubKeyFromBytes(v.PubKey) // NOTE: expects go-wire encoded pubkey
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return err |
|
|
} |
|
|
} |
|
|
_, val := lastValSet.GetByIndex(i) |
|
|
|
|
|
if val == nil { |
|
|
|
|
|
PanicCrisis(Fmt("Failed to fetch validator at index %v", i)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
address := pubkey.Address() |
|
|
|
|
|
power := int64(v.Power) |
|
|
|
|
|
// mind the overflow from uint64
|
|
|
|
|
|
if power < 0 { |
|
|
|
|
|
return errors.New(Fmt("Power (%d) overflows int64", v.Power)) |
|
|
} |
|
|
} |
|
|
if _, val_ := valSet.GetByAddress(val.Address); val_ != nil { |
|
|
|
|
|
val_.LastCommitHeight = block.Height - 1 |
|
|
|
|
|
updated := valSet.Update(val_) |
|
|
|
|
|
if !updated { |
|
|
|
|
|
PanicCrisis("Failed to update validator LastCommitHeight") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_, val := validators.GetByAddress(address) |
|
|
|
|
|
if val == nil { |
|
|
|
|
|
// add val
|
|
|
|
|
|
added := validators.Add(types.NewValidator(pubkey, power)) |
|
|
|
|
|
if !added { |
|
|
|
|
|
return errors.New(Fmt("Failed to add new validator %X with voting power %d", address, power)) |
|
|
|
|
|
} |
|
|
|
|
|
} else if v.Power == 0 { |
|
|
|
|
|
// remove val
|
|
|
|
|
|
_, removed := validators.Remove(address) |
|
|
|
|
|
if !removed { |
|
|
|
|
|
return errors.New(Fmt("Failed to remove validator %X)")) |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
// XXX This is not an error if validator was removed.
|
|
|
|
|
|
// But, we don't mutate validators yet so go ahead and panic.
|
|
|
|
|
|
PanicCrisis("Could not find validator") |
|
|
|
|
|
|
|
|
// update val
|
|
|
|
|
|
val.VotingPower = power |
|
|
|
|
|
updated := validators.Update(val) |
|
|
|
|
|
if !updated { |
|
|
|
|
|
return errors.New(Fmt("Failed to update validator %X with voting power %d", address, power)) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// return a bit array of validators that signed the last commit
|
|
|
|
|
|
// NOTE: assumes commits have already been authenticated
|
|
|
|
|
|
func commitBitArrayFromBlock(block *types.Block) *BitArray { |
|
|
|
|
|
signed := NewBitArray(len(block.LastCommit.Precommits)) |
|
|
|
|
|
for i, precommit := range block.LastCommit.Precommits { |
|
|
|
|
|
if precommit != nil { |
|
|
|
|
|
signed.SetIndex(i, true) // val_.LastCommitHeight = block.Height - 1
|
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return signed |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//-----------------------------------------------------
|
|
|
//-----------------------------------------------------
|
|
@ -259,6 +293,7 @@ func (m mockMempool) Update(height int, txs []types.Tx) {} |
|
|
type BlockStore interface { |
|
|
type BlockStore interface { |
|
|
Height() int |
|
|
Height() int |
|
|
LoadBlock(height int) *types.Block |
|
|
LoadBlock(height int) *types.Block |
|
|
|
|
|
LoadBlockMeta(height int) *types.BlockMeta |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
type Handshaker struct { |
|
|
type Handshaker struct { |
|
@ -273,8 +308,7 @@ func NewHandshaker(config cfg.Config, state *State, store BlockStore) *Handshake |
|
|
return &Handshaker{config, state, store, 0} |
|
|
return &Handshaker{config, state, store, 0} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// TODO: retry the handshake once if it fails the first time
|
|
|
|
|
|
// ... let Info take an argument determining its behaviour
|
|
|
|
|
|
|
|
|
// TODO: retry the handshake/replay if it fails ?
|
|
|
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { |
|
|
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { |
|
|
// handshake is done via info request on the query conn
|
|
|
// handshake is done via info request on the query conn
|
|
|
res, tmspInfo, blockInfo, configInfo := proxyApp.Query().InfoSync() |
|
|
res, tmspInfo, blockInfo, configInfo := proxyApp.Query().InfoSync() |
|
@ -287,10 +321,9 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
log.Notice("TMSP Handshake", "height", blockInfo.BlockHeight, "block_hash", blockInfo.BlockHash, "app_hash", blockInfo.AppHash) |
|
|
|
|
|
|
|
|
log.Notice("TMSP Handshake", "height", blockInfo.BlockHeight, "app_hash", blockInfo.AppHash) |
|
|
|
|
|
|
|
|
blockHeight := int(blockInfo.BlockHeight) // safe, should be an int32
|
|
|
|
|
|
blockHash := blockInfo.BlockHash |
|
|
|
|
|
|
|
|
blockHeight := int(blockInfo.BlockHeight) // XXX: beware overflow
|
|
|
appHash := blockInfo.AppHash |
|
|
appHash := blockInfo.AppHash |
|
|
|
|
|
|
|
|
if tmspInfo != nil { |
|
|
if tmspInfo != nil { |
|
@ -298,40 +331,13 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { |
|
|
_ = tmspInfo |
|
|
_ = tmspInfo |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// last block (nil if we starting from 0)
|
|
|
|
|
|
var header *types.Header |
|
|
|
|
|
var partsHeader types.PartSetHeader |
|
|
|
|
|
|
|
|
|
|
|
// replay all blocks after blockHeight
|
|
|
|
|
|
// if blockHeight == 0, we will replay everything
|
|
|
|
|
|
if blockHeight != 0 { |
|
|
|
|
|
block := h.store.LoadBlock(blockHeight) |
|
|
|
|
|
if block == nil { |
|
|
|
|
|
return ErrUnknownBlock{blockHeight} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// check block hash
|
|
|
|
|
|
if !bytes.Equal(block.Hash(), blockHash) { |
|
|
|
|
|
return ErrBlockHashMismatch{block.Hash(), blockHash, blockHeight} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// NOTE: app hash should be in the next block ...
|
|
|
|
|
|
// check app hash
|
|
|
|
|
|
/*if !bytes.Equal(block.Header.AppHash, appHash) { |
|
|
|
|
|
return fmt.Errorf("Handshake error. App hash at height %d does not match. Got %X, expected %X", blockHeight, appHash, block.Header.AppHash) |
|
|
|
|
|
}*/ |
|
|
|
|
|
|
|
|
|
|
|
header = block.Header |
|
|
|
|
|
partsHeader = block.MakePartSet(h.config.GetInt("block_part_size")).Header() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if configInfo != nil { |
|
|
if configInfo != nil { |
|
|
// TODO: set config info
|
|
|
// TODO: set config info
|
|
|
_ = configInfo |
|
|
_ = configInfo |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// replay blocks up to the latest in the blockstore
|
|
|
// replay blocks up to the latest in the blockstore
|
|
|
err := h.ReplayBlocks(appHash, header, partsHeader, proxyApp.Consensus()) |
|
|
|
|
|
|
|
|
err := h.ReplayBlocks(appHash, blockHeight, proxyApp.Consensus()) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return errors.New(Fmt("Error on replay: %v", err)) |
|
|
return errors.New(Fmt("Error on replay: %v", err)) |
|
|
} |
|
|
} |
|
@ -342,97 +348,72 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Replay all blocks after blockHeight and ensure the result matches the current state.
|
|
|
// Replay all blocks after blockHeight and ensure the result matches the current state.
|
|
|
func (h *Handshaker) ReplayBlocks(appHash []byte, header *types.Header, partsHeader types.PartSetHeader, |
|
|
|
|
|
appConnConsensus proxy.AppConnConsensus) error { |
|
|
|
|
|
|
|
|
|
|
|
// NOTE/TODO: tendermint may crash after the app commits
|
|
|
|
|
|
// but before it can save the new state root.
|
|
|
|
|
|
// it should save all eg. valset changes before calling Commit.
|
|
|
|
|
|
// then, if tm state is behind app state, the only thing missing can be app hash
|
|
|
|
|
|
|
|
|
|
|
|
// get a fresh state and reset to the apps latest
|
|
|
|
|
|
stateCopy := h.state.Copy() |
|
|
|
|
|
|
|
|
|
|
|
// TODO: put validators in iavl tree so we can set the state with an older validator set
|
|
|
|
|
|
lastVals, nextVals := stateCopy.GetValidators() |
|
|
|
|
|
if header == nil { |
|
|
|
|
|
stateCopy.LastBlockHeight = 0 |
|
|
|
|
|
stateCopy.LastBlockID = types.BlockID{} |
|
|
|
|
|
// stateCopy.LastBlockTime = ... doesnt matter
|
|
|
|
|
|
stateCopy.Validators = nextVals |
|
|
|
|
|
stateCopy.LastValidators = lastVals |
|
|
|
|
|
} else { |
|
|
|
|
|
stateCopy.SetBlockAndValidators(header, partsHeader, lastVals, nextVals) |
|
|
|
|
|
} |
|
|
|
|
|
stateCopy.Stale = false |
|
|
|
|
|
stateCopy.AppHash = appHash |
|
|
|
|
|
|
|
|
func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, appConnConsensus proxy.AppConnConsensus) error { |
|
|
|
|
|
|
|
|
appBlockHeight := stateCopy.LastBlockHeight |
|
|
|
|
|
coreBlockHeight := h.store.Height() |
|
|
|
|
|
if coreBlockHeight < appBlockHeight { |
|
|
|
|
|
|
|
|
storeBlockHeight := h.store.Height() |
|
|
|
|
|
if storeBlockHeight < appBlockHeight { |
|
|
// if the app is ahead, there's nothing we can do
|
|
|
// if the app is ahead, there's nothing we can do
|
|
|
return ErrAppBlockHeightTooHigh{coreBlockHeight, appBlockHeight} |
|
|
|
|
|
|
|
|
return ErrAppBlockHeightTooHigh{storeBlockHeight, appBlockHeight} |
|
|
|
|
|
|
|
|
} else if coreBlockHeight == appBlockHeight { |
|
|
|
|
|
|
|
|
} else if storeBlockHeight == appBlockHeight { |
|
|
// if we crashed between Commit and SaveState,
|
|
|
// if we crashed between Commit and SaveState,
|
|
|
// the state's app hash is stale.
|
|
|
|
|
|
|
|
|
// the state's app hash is stale
|
|
|
// otherwise we're synced
|
|
|
// otherwise we're synced
|
|
|
if h.state.Stale { |
|
|
|
|
|
h.state.Stale = false |
|
|
|
|
|
|
|
|
if h.state.AppHashIsStale { |
|
|
|
|
|
h.state.AppHashIsStale = false |
|
|
h.state.AppHash = appHash |
|
|
h.state.AppHash = appHash |
|
|
} |
|
|
} |
|
|
return checkState(h.state, stateCopy) |
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
|
} else if h.state.LastBlockHeight == appBlockHeight { |
|
|
} else if h.state.LastBlockHeight == appBlockHeight { |
|
|
// core is ahead of app but core's state height is at apps height
|
|
|
|
|
|
|
|
|
// store is ahead of app but core's state height is at apps height
|
|
|
// this happens if we crashed after saving the block,
|
|
|
// this happens if we crashed after saving the block,
|
|
|
// but before committing it. We should be 1 ahead
|
|
|
// but before committing it. We should be 1 ahead
|
|
|
if coreBlockHeight != appBlockHeight+1 { |
|
|
|
|
|
PanicSanity(Fmt("core.state.height == app.height but core.height (%d) > app.height+1 (%d)", coreBlockHeight, appBlockHeight+1)) |
|
|
|
|
|
|
|
|
if storeBlockHeight != appBlockHeight+1 { |
|
|
|
|
|
PanicSanity(Fmt("core.state.height == app.height but store.height (%d) > app.height+1 (%d)", storeBlockHeight, appBlockHeight+1)) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// check that the blocks last apphash is the states apphash
|
|
|
// check that the blocks last apphash is the states apphash
|
|
|
block := h.store.LoadBlock(coreBlockHeight) |
|
|
|
|
|
|
|
|
block := h.store.LoadBlock(storeBlockHeight) |
|
|
if !bytes.Equal(block.Header.AppHash, appHash) { |
|
|
if !bytes.Equal(block.Header.AppHash, appHash) { |
|
|
return ErrLastStateMismatch{coreBlockHeight, block.Header.AppHash, appHash} |
|
|
|
|
|
|
|
|
return ErrLastStateMismatch{storeBlockHeight, block.Header.AppHash, appHash} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// replay the block against the actual tendermint state (not the copy)
|
|
|
|
|
|
return h.loadApplyBlock(coreBlockHeight, h.state, appConnConsensus) |
|
|
|
|
|
|
|
|
blockMeta := h.store.LoadBlockMeta(storeBlockHeight) |
|
|
|
|
|
|
|
|
|
|
|
h.nBlocks += 1 |
|
|
|
|
|
var eventCache types.Fireable // nil
|
|
|
|
|
|
|
|
|
|
|
|
// replay the block against the actual tendermint state
|
|
|
|
|
|
return h.state.ApplyBlock(eventCache, appConnConsensus, block, blockMeta.PartsHeader, mockMempool{}) |
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
// either we're caught up or there's blocks to replay
|
|
|
// either we're caught up or there's blocks to replay
|
|
|
// replay all blocks starting with appBlockHeight+1
|
|
|
// replay all blocks starting with appBlockHeight+1
|
|
|
for i := appBlockHeight + 1; i <= coreBlockHeight; i++ { |
|
|
|
|
|
h.loadApplyBlock(i, stateCopy, appConnConsensus) |
|
|
|
|
|
|
|
|
var eventCache types.Fireable // nil
|
|
|
|
|
|
var appHash []byte |
|
|
|
|
|
for i := appBlockHeight + 1; i <= storeBlockHeight; i++ { |
|
|
|
|
|
h.nBlocks += 1 |
|
|
|
|
|
block := h.store.LoadBlock(i) |
|
|
|
|
|
_, err := execBlockOnProxyApp(eventCache, appConnConsensus, block) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
log.Warn("Error executing block on proxy app", "height", i, "err", err) |
|
|
|
|
|
return err |
|
|
|
|
|
} |
|
|
|
|
|
// Commit block, get hash back
|
|
|
|
|
|
res := appConnConsensus.CommitSync() |
|
|
|
|
|
if res.IsErr() { |
|
|
|
|
|
log.Warn("Error in proxyAppConn.CommitSync", "error", res) |
|
|
|
|
|
return res |
|
|
|
|
|
} |
|
|
|
|
|
if res.Log != "" { |
|
|
|
|
|
log.Info("Commit.Log: " + res.Log) |
|
|
|
|
|
} |
|
|
|
|
|
appHash = res.Data |
|
|
} |
|
|
} |
|
|
return checkState(h.state, stateCopy) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func checkState(s, stateCopy *State) error { |
|
|
|
|
|
// The computed state and the previously set state should be identical
|
|
|
|
|
|
if !s.Equals(stateCopy) { |
|
|
|
|
|
return ErrStateMismatch{stateCopy, s} |
|
|
|
|
|
} |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (h *Handshaker) loadApplyBlock(blockIndex int, state *State, appConnConsensus proxy.AppConnConsensus) error { |
|
|
|
|
|
h.nBlocks += 1 |
|
|
|
|
|
block := h.store.LoadBlock(blockIndex) |
|
|
|
|
|
panicOnNilBlock(blockIndex, h.store.Height(), block) // XXX
|
|
|
|
|
|
var eventCache types.Fireable // nil
|
|
|
|
|
|
return state.ApplyBlock(eventCache, appConnConsensus, block, block.MakePartSet(h.config.GetInt("block_part_size")).Header(), mockMempool{}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func panicOnNilBlock(height, bsHeight int, block *types.Block) { |
|
|
|
|
|
if block == nil { |
|
|
|
|
|
// Sanity?
|
|
|
|
|
|
PanicCrisis(Fmt(` |
|
|
|
|
|
block is nil for height <= blockStore.Height() (%d <= %d). |
|
|
|
|
|
Block: %v, |
|
|
|
|
|
`, height, bsHeight, block)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !bytes.Equal(h.state.AppHash, appHash) { |
|
|
|
|
|
return errors.New(Fmt("Tendermint state.AppHash does not match AppHash after replay", "expected", h.state.AppHash, "got", appHash)) |
|
|
|
|
|
} |
|
|
|
|
|
return nil |
|
|
} |
|
|
} |
|
|
} |
|
|
} |