mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-31 12:11:58 +00:00
save block b4 apply; track stale apphash
This commit is contained in:
@@ -1255,33 +1255,34 @@ func (cs *ConsensusState) finalizeCommit(height int) {
|
|||||||
"height", block.Height, "hash", block.Hash(), "root", block.AppHash)
|
"height", block.Height, "hash", block.Hash(), "root", block.AppHash)
|
||||||
log.Info(Fmt("%v", block))
|
log.Info(Fmt("%v", block))
|
||||||
|
|
||||||
// Fire off event for new block.
|
|
||||||
// TODO: Handle app failure. See #177
|
|
||||||
types.FireEventNewBlock(cs.evsw, types.EventDataNewBlock{block})
|
|
||||||
types.FireEventNewBlockHeader(cs.evsw, types.EventDataNewBlockHeader{block.Header})
|
|
||||||
|
|
||||||
// Create a copy of the state for staging
|
|
||||||
stateCopy := cs.state.Copy()
|
|
||||||
|
|
||||||
// event cache for txs
|
|
||||||
eventCache := types.NewEventCache(cs.evsw)
|
|
||||||
|
|
||||||
// Execute and commit the block
|
|
||||||
// NOTE: All calls to the proxyAppConn should come here
|
|
||||||
stateCopy.ApplyBlock(eventCache, cs.proxyAppConn, block, blockParts.Header(), cs.mempool)
|
|
||||||
|
|
||||||
// txs committed, bad ones removed from mepool; fire events
|
|
||||||
// NOTE: the block.AppHash wont reflect these txs until the next block
|
|
||||||
eventCache.Flush()
|
|
||||||
|
|
||||||
// Save to blockStore.
|
// Save to blockStore.
|
||||||
if cs.blockStore.Height() < block.Height {
|
if cs.blockStore.Height() < block.Height {
|
||||||
precommits := cs.Votes.Precommits(cs.CommitRound)
|
precommits := cs.Votes.Precommits(cs.CommitRound)
|
||||||
seenCommit := precommits.MakeCommit()
|
seenCommit := precommits.MakeCommit()
|
||||||
log.Notice("save block", "height", block.Height)
|
log.Notice("save block", "height", block.Height)
|
||||||
cs.blockStore.SaveBlock(block, blockParts, seenCommit)
|
cs.blockStore.SaveBlock(block, blockParts, seenCommit)
|
||||||
|
} else {
|
||||||
|
log.Warn("Why are we finalizeCommitting a block height we already have?", "height", block.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a copy of the state for staging
|
||||||
|
// and an event cache for txs
|
||||||
|
stateCopy := cs.state.Copy()
|
||||||
|
|
||||||
|
// event cache for txs
|
||||||
|
eventCache := types.NewEventCache(cs.evsw)
|
||||||
|
|
||||||
|
// Execute and commit the block, and update the mempool.
|
||||||
|
// All calls to the proxyAppConn should come here.
|
||||||
|
// NOTE: the block.AppHash wont reflect these txs until the next block
|
||||||
|
stateCopy.ApplyBlock(eventCache, cs.proxyAppConn, block, blockParts.Header(), cs.mempool)
|
||||||
|
|
||||||
|
// Fire off event for new block.
|
||||||
|
// TODO: Handle app failure. See #177
|
||||||
|
types.FireEventNewBlock(cs.evsw, types.EventDataNewBlock{block})
|
||||||
|
types.FireEventNewBlockHeader(cs.evsw, types.EventDataNewBlockHeader{block.Header})
|
||||||
|
eventCache.Flush()
|
||||||
|
|
||||||
// Save the state.
|
// Save the state.
|
||||||
log.Notice("save state", "height", stateCopy.LastBlockHeight, "hash", stateCopy.AppHash)
|
log.Notice("save state", "height", stateCopy.LastBlockHeight, "hash", stateCopy.AppHash)
|
||||||
stateCopy.Save()
|
stateCopy.Save()
|
||||||
|
@@ -282,8 +282,7 @@ func (mem *Mempool) collectTxs(maxTxs int) []types.Tx {
|
|||||||
// NOTE: this should be called *after* block is committed by consensus.
|
// NOTE: this should be called *after* block is committed by consensus.
|
||||||
// NOTE: unsafe; Lock/Unlock must be managed by caller
|
// NOTE: unsafe; Lock/Unlock must be managed by caller
|
||||||
func (mem *Mempool) Update(height int, txs []types.Tx) {
|
func (mem *Mempool) Update(height int, txs []types.Tx) {
|
||||||
// mem.proxyMtx.Lock()
|
// TODO: check err ?
|
||||||
// defer mem.proxyMtx.Unlock()
|
|
||||||
mem.proxyAppConn.FlushSync() // To flush async resCb calls e.g. from CheckTx
|
mem.proxyAppConn.FlushSync() // To flush async resCb calls e.g. from CheckTx
|
||||||
|
|
||||||
// First, create a lookup map of txns in new txs.
|
// First, create a lookup map of txns in new txs.
|
||||||
|
@@ -2,7 +2,6 @@ package state
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/tendermint/proxy"
|
"github.com/tendermint/tendermint/proxy"
|
||||||
@@ -10,10 +9,13 @@ import (
|
|||||||
tmsp "github.com/tendermint/tmsp/types"
|
tmsp "github.com/tendermint/tmsp/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validate block
|
//--------------------------------------------------
|
||||||
func (s *State) ValidateBlock(block *types.Block) error {
|
// Execute the block
|
||||||
return s.validateBlock(block)
|
|
||||||
}
|
type (
|
||||||
|
ErrInvalidBlock error
|
||||||
|
ErrProxyAppConn error
|
||||||
|
)
|
||||||
|
|
||||||
// Execute the block to mutate State.
|
// Execute the block to mutate State.
|
||||||
// Validates block and then executes Data.Txs in the block.
|
// Validates block and then executes Data.Txs in the block.
|
||||||
@@ -22,7 +24,7 @@ func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnC
|
|||||||
// Validate the block.
|
// Validate the block.
|
||||||
err := s.validateBlock(block)
|
err := s.validateBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return ErrInvalidBlock(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the validator set
|
// Update the validator set
|
||||||
@@ -37,7 +39,7 @@ func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnC
|
|||||||
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 err
|
return ErrProxyAppConn(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// All good!
|
// All good!
|
||||||
@@ -45,6 +47,10 @@ func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnC
|
|||||||
nextValSet.IncrementAccum(1)
|
nextValSet.IncrementAccum(1)
|
||||||
s.SetBlockAndValidators(block.Header, blockPartsHeader, valSet, nextValSet)
|
s.SetBlockAndValidators(block.Header, blockPartsHeader, valSet, nextValSet)
|
||||||
|
|
||||||
|
// save state with updated height/blockhash/validators
|
||||||
|
// but stale apphash, in case we fail between Commit and Save
|
||||||
|
s.Save()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,33 +119,6 @@ func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn prox
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) validateBlock(block *types.Block) error {
|
|
||||||
// Basic block validation.
|
|
||||||
err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockID, s.LastBlockTime, s.AppHash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate block LastCommit.
|
|
||||||
if block.Height == 1 {
|
|
||||||
if len(block.LastCommit.Precommits) != 0 {
|
|
||||||
return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(block.LastCommit.Precommits) != s.LastValidators.Size() {
|
|
||||||
return fmt.Errorf("Invalid block commit size. Expected %v, got %v",
|
|
||||||
s.LastValidators.Size(), len(block.LastCommit.Precommits))
|
|
||||||
}
|
|
||||||
err := s.LastValidators.VerifyCommit(
|
|
||||||
s.ChainID, s.LastBlockID, block.Height-1, block.LastCommit)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the LastCommitHeight of the validators in valSet, in place.
|
// Updates the LastCommitHeight of the validators in valSet, in place.
|
||||||
// Assumes that lastValSet matches the valset of block.LastCommit
|
// Assumes that lastValSet matches the valset of block.LastCommit
|
||||||
// CONTRACT: lastValSet is not mutated.
|
// CONTRACT: lastValSet is not mutated.
|
||||||
@@ -168,18 +147,62 @@ func updateValidatorsWithBlock(lastValSet *types.ValidatorSet, valSet *types.Val
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------
|
||||||
|
// Validate block
|
||||||
|
|
||||||
type InvalidTxError struct {
|
func (s *State) ValidateBlock(block *types.Block) error {
|
||||||
Tx types.Tx
|
return s.validateBlock(block)
|
||||||
Code tmsp.CodeType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txErr InvalidTxError) Error() string {
|
func (s *State) validateBlock(block *types.Block) error {
|
||||||
return Fmt("Invalid tx: [%v] code: [%v]", txErr.Tx, txErr.Code)
|
// Basic block validation.
|
||||||
|
err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockID, s.LastBlockTime, s.AppHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate block LastCommit.
|
||||||
|
if block.Height == 1 {
|
||||||
|
if len(block.LastCommit.Precommits) != 0 {
|
||||||
|
return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(block.LastCommit.Precommits) != s.LastValidators.Size() {
|
||||||
|
return errors.New(Fmt("Invalid block commit size. Expected %v, got %v",
|
||||||
|
s.LastValidators.Size(), len(block.LastCommit.Precommits)))
|
||||||
|
}
|
||||||
|
err := s.LastValidators.VerifyCommit(
|
||||||
|
s.ChainID, s.LastBlockID, block.Height-1, block.LastCommit)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
// ApplyBlock executes the block, then commits and updates the mempool atomically
|
||||||
|
|
||||||
|
// Execute and commit block against app, save block and state
|
||||||
|
func (s *State) ApplyBlock(eventCache events.Fireable, proxyAppConn proxy.AppConnConsensus,
|
||||||
|
block *types.Block, partsHeader types.PartSetHeader, mempool Mempool) error {
|
||||||
|
|
||||||
|
// Run the block on the State:
|
||||||
|
// + update validator sets
|
||||||
|
// + run txs on the proxyAppConn
|
||||||
|
err := s.ExecBlock(eventCache, proxyAppConn, block, partsHeader)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(Fmt("Exec failed for application: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// lock mempool, commit state, update mempoool
|
||||||
|
err = s.CommitStateUpdateMempool(proxyAppConn, block, mempool)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(Fmt("Commit failed for application: %v", err))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// mempool must be locked during commit and update
|
// mempool must be locked during commit and update
|
||||||
// because state is typically reset on Commit and old txs must be replayed
|
// because state is typically reset on Commit and old txs must be replayed
|
||||||
@@ -188,9 +211,6 @@ func (s *State) CommitStateUpdateMempool(proxyAppConn proxy.AppConnConsensus, bl
|
|||||||
mempool.Lock()
|
mempool.Lock()
|
||||||
defer mempool.Unlock()
|
defer mempool.Unlock()
|
||||||
|
|
||||||
// flush out any CheckTx that have already started
|
|
||||||
// cs.proxyAppConn.FlushSync() // ?! XXX
|
|
||||||
|
|
||||||
// Commit block, get hash back
|
// Commit block, get hash back
|
||||||
res := proxyAppConn.CommitSync()
|
res := proxyAppConn.CommitSync()
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
@@ -210,25 +230,40 @@ func (s *State) CommitStateUpdateMempool(proxyAppConn proxy.AppConnConsensus, bl
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute and commit block against app, save block and state
|
// Updates to the mempool need to be synchronized with committing a block
|
||||||
func (s *State) ApplyBlock(eventCache events.Fireable, proxyAppConn proxy.AppConnConsensus,
|
// so apps can reset their transient state on Commit
|
||||||
block *types.Block, partsHeader types.PartSetHeader, mempool Mempool) {
|
type Mempool interface {
|
||||||
|
Lock()
|
||||||
|
Unlock()
|
||||||
|
Update(height int, txs []types.Tx)
|
||||||
|
}
|
||||||
|
|
||||||
// Run the block on the State:
|
type mockMempool struct {
|
||||||
// + update validator sets
|
}
|
||||||
// + run txs on the proxyAppConn
|
|
||||||
err := s.ExecBlock(eventCache, proxyAppConn, block, partsHeader)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: handle this gracefully.
|
|
||||||
PanicQ(Fmt("Exec failed for application: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// lock mempool, commit state, update mempoool
|
func (m mockMempool) Lock() {}
|
||||||
err = s.CommitStateUpdateMempool(proxyAppConn, block, mempool)
|
func (m mockMempool) Unlock() {}
|
||||||
if err != nil {
|
func (m mockMempool) Update(height int, txs []types.Tx) {}
|
||||||
// TODO: handle this gracefully.
|
|
||||||
PanicQ(Fmt("Commit failed for application: %v", err))
|
//----------------------------------------------------------------
|
||||||
}
|
// Replay blocks to sync app to latest state of core
|
||||||
|
|
||||||
|
type ErrAppBlockHeightTooHigh struct {
|
||||||
|
coreHeight int
|
||||||
|
appHeight int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrAppBlockHeightTooHigh) Error() string {
|
||||||
|
return Fmt("App block height (%d) is higher than core (%d)", e.appHeight, e.coreHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrStateMismatch struct {
|
||||||
|
got *State
|
||||||
|
expected *State
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrStateMismatch) Error() string {
|
||||||
|
return Fmt("State after replay does not match saved state. Got ----\n%v\nExpected ----\n%v\n", e.got, e.expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -241,34 +276,45 @@ func (s *State) ReplayBlocks(appHash []byte, header *types.Header, partsHeader t
|
|||||||
// it should save all eg. valset changes before calling Commit.
|
// 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
|
// then, if tm state is behind app state, the only thing missing can be app hash
|
||||||
|
|
||||||
// fresh state to work on
|
// get a fresh state and reset to the apps latest
|
||||||
stateCopy := s.Copy()
|
stateCopy := s.Copy()
|
||||||
|
|
||||||
// reset to this height (do nothing if its 0)
|
|
||||||
var blockHeight int
|
|
||||||
if header != nil {
|
if header != nil {
|
||||||
blockHeight = header.Height
|
|
||||||
// TODO: put validators in iavl tree so we can set the state with an older validator set
|
// TODO: put validators in iavl tree so we can set the state with an older validator set
|
||||||
lastVals, nextVals := stateCopy.GetValidators()
|
lastVals, nextVals := stateCopy.GetValidators()
|
||||||
stateCopy.SetBlockAndValidators(header, partsHeader, lastVals, nextVals)
|
stateCopy.SetBlockAndValidators(header, partsHeader, lastVals, nextVals)
|
||||||
|
stateCopy.Stale = false
|
||||||
stateCopy.AppHash = appHash
|
stateCopy.AppHash = appHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the transactions
|
appBlockHeight := stateCopy.LastBlockHeight
|
||||||
var eventCache events.Fireable // nil
|
coreBlockHeight := blockStore.Height()
|
||||||
|
if coreBlockHeight < appBlockHeight {
|
||||||
|
return ErrAppBlockHeightTooHigh{coreBlockHeight, appBlockHeight}
|
||||||
|
|
||||||
// replay all blocks starting with blockHeight+1
|
} else if coreBlockHeight == appBlockHeight {
|
||||||
for i := blockHeight + 1; i <= blockStore.Height(); i++ {
|
// if we crashed between Commit and SaveState,
|
||||||
blockMeta := blockStore.LoadBlockMeta(i)
|
// the state's app hash is stale
|
||||||
block := blockStore.LoadBlock(i)
|
if s.Stale {
|
||||||
panicOnNilBlock(i, blockStore.Height(), block, blockMeta) // XXX
|
s.Stale = false
|
||||||
|
s.AppHash = appHash
|
||||||
|
}
|
||||||
|
|
||||||
stateCopy.ApplyBlock(eventCache, appConnConsensus, block, blockMeta.PartsHeader, mockMempool{})
|
} else {
|
||||||
|
// the app is behind.
|
||||||
|
// replay all blocks starting with appBlockHeight+1
|
||||||
|
for i := appBlockHeight + 1; i <= coreBlockHeight; i++ {
|
||||||
|
blockMeta := blockStore.LoadBlockMeta(i)
|
||||||
|
block := blockStore.LoadBlock(i)
|
||||||
|
panicOnNilBlock(i, coreBlockHeight, block, blockMeta) // XXX
|
||||||
|
|
||||||
|
var eventCache events.Fireable // nil
|
||||||
|
stateCopy.ApplyBlock(eventCache, appConnConsensus, block, blockMeta.PartsHeader, mockMempool{})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The computed state and the previously set state should be identical
|
// The computed state and the previously set state should be identical
|
||||||
if !s.Equals(stateCopy) {
|
if !s.Equals(stateCopy) {
|
||||||
return fmt.Errorf("State after replay does not match saved state. Got ----\n%v\nExpected ----\n%v\n", stateCopy, s)
|
return ErrStateMismatch{stateCopy, s}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -284,20 +330,3 @@ BlockMeta: %v
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------
|
|
||||||
// Updates to the mempool need to be synchronized with committing a block
|
|
||||||
// so apps can reset their transient state on Commit
|
|
||||||
|
|
||||||
type Mempool interface {
|
|
||||||
Lock()
|
|
||||||
Unlock()
|
|
||||||
Update(height int, txs []types.Tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockMempool struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m mockMempool) Lock() {}
|
|
||||||
func (m mockMempool) Unlock() {}
|
|
||||||
func (m mockMempool) Update(height int, txs []types.Tx) {}
|
|
||||||
|
@@ -21,16 +21,25 @@ var (
|
|||||||
|
|
||||||
// NOTE: not goroutine-safe.
|
// NOTE: not goroutine-safe.
|
||||||
type State struct {
|
type State struct {
|
||||||
mtx sync.Mutex
|
// mtx for writing to db
|
||||||
db dbm.DB
|
mtx sync.Mutex
|
||||||
GenesisDoc *types.GenesisDoc
|
db dbm.DB
|
||||||
ChainID string
|
|
||||||
|
// should not change
|
||||||
|
GenesisDoc *types.GenesisDoc
|
||||||
|
ChainID string
|
||||||
|
|
||||||
|
// updated at end of ExecBlock
|
||||||
LastBlockHeight int // Genesis state has this set to 0. So, Block(H=0) does not exist.
|
LastBlockHeight int // Genesis state has this set to 0. So, Block(H=0) does not exist.
|
||||||
LastBlockID types.BlockID
|
LastBlockID types.BlockID
|
||||||
LastBlockTime time.Time
|
LastBlockTime time.Time
|
||||||
Validators *types.ValidatorSet
|
Validators *types.ValidatorSet
|
||||||
LastValidators *types.ValidatorSet
|
LastValidators *types.ValidatorSet
|
||||||
AppHash []byte
|
|
||||||
|
// AppHash is updated after Commit;
|
||||||
|
// it's stale after ExecBlock and before Commit
|
||||||
|
Stale bool
|
||||||
|
AppHash []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadState(db dbm.DB) *State {
|
func LoadState(db dbm.DB) *State {
|
||||||
@@ -60,6 +69,7 @@ func (s *State) Copy() *State {
|
|||||||
LastBlockTime: s.LastBlockTime,
|
LastBlockTime: s.LastBlockTime,
|
||||||
Validators: s.Validators.Copy(),
|
Validators: s.Validators.Copy(),
|
||||||
LastValidators: s.LastValidators.Copy(),
|
LastValidators: s.LastValidators.Copy(),
|
||||||
|
Stale: s.Stale, // but really state shouldnt be copied while its stale
|
||||||
AppHash: s.AppHash,
|
AppHash: s.AppHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,12 +94,15 @@ func (s *State) Bytes() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mutate state variables to match block and validators
|
// Mutate state variables to match block and validators
|
||||||
|
// Since we don't have the AppHash yet, it becomes stale
|
||||||
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, prevValSet, nextValSet *types.ValidatorSet) {
|
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, prevValSet, nextValSet *types.ValidatorSet) {
|
||||||
s.LastBlockHeight = header.Height
|
s.LastBlockHeight = header.Height
|
||||||
s.LastBlockID = types.BlockID{block.Hash(), blockPartsHeader}
|
s.LastBlockID = types.BlockID{block.Hash(), blockPartsHeader}
|
||||||
s.LastBlockTime = header.Time
|
s.LastBlockTime = header.Time
|
||||||
s.Validators = nextValSet
|
s.Validators = nextValSet
|
||||||
s.LastValidators = prevValSet
|
s.LastValidators = prevValSet
|
||||||
|
|
||||||
|
s.Stale = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) {
|
func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) {
|
||||||
|
Reference in New Issue
Block a user