|
|
@ -8,11 +8,13 @@ import ( |
|
|
|
"time" |
|
|
|
|
|
|
|
abci "github.com/tendermint/abci/types" |
|
|
|
|
|
|
|
cmn "github.com/tendermint/tmlibs/common" |
|
|
|
dbm "github.com/tendermint/tmlibs/db" |
|
|
|
"github.com/tendermint/tmlibs/log" |
|
|
|
|
|
|
|
wire "github.com/tendermint/go-wire" |
|
|
|
|
|
|
|
"github.com/tendermint/tendermint/state/txindex" |
|
|
|
"github.com/tendermint/tendermint/state/txindex/null" |
|
|
|
"github.com/tendermint/tendermint/types" |
|
|
@ -50,18 +52,18 @@ type State struct { |
|
|
|
LastBlockTime time.Time |
|
|
|
Validators *types.ValidatorSet |
|
|
|
LastValidators *types.ValidatorSet |
|
|
|
|
|
|
|
// AppHash is updated after Commit
|
|
|
|
AppHash []byte |
|
|
|
|
|
|
|
TxIndexer txindex.TxIndexer `json:"-"` // Transaction indexer
|
|
|
|
|
|
|
|
// When a block returns a validator set change via EndBlock,
|
|
|
|
// the change only applies to the next block.
|
|
|
|
// So, if s.LastBlockHeight causes a valset change,
|
|
|
|
// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1
|
|
|
|
LastHeightValidatorsChanged int |
|
|
|
|
|
|
|
// AppHash is updated after Commit
|
|
|
|
AppHash []byte |
|
|
|
|
|
|
|
// TxIndexer indexes transactions
|
|
|
|
TxIndexer txindex.TxIndexer `json:"-"` |
|
|
|
|
|
|
|
logger log.Logger |
|
|
|
} |
|
|
|
|
|
|
@ -88,19 +90,21 @@ func LoadState(db dbm.DB) *State { |
|
|
|
} |
|
|
|
|
|
|
|
func loadState(db dbm.DB, key []byte) *State { |
|
|
|
s := &State{db: db, TxIndexer: &null.TxIndex{}} |
|
|
|
buf := db.Get(key) |
|
|
|
if len(buf) == 0 { |
|
|
|
return nil |
|
|
|
} else { |
|
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error) |
|
|
|
wire.ReadBinaryPtr(&s, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt("LoadState: Data has been corrupted or its spec has changed: %v\n", *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
} |
|
|
|
|
|
|
|
s := &State{db: db, TxIndexer: &null.TxIndex{}} |
|
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error) |
|
|
|
wire.ReadBinaryPtr(&s, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt(`LoadState: Data has been corrupted or its spec has changed: |
|
|
|
%v\n`, *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
|
|
|
|
return s |
|
|
|
} |
|
|
|
|
|
|
@ -110,6 +114,8 @@ func (s *State) SetLogger(l log.Logger) { |
|
|
|
} |
|
|
|
|
|
|
|
// Copy makes a copy of the State for mutating.
|
|
|
|
// NOTE: Does not create a copy of TxIndexer. It creates a new pointer that points to the same
|
|
|
|
// underlying TxIndexer.
|
|
|
|
func (s *State) Copy() *State { |
|
|
|
return &State{ |
|
|
|
db: s.db, |
|
|
@ -119,7 +125,7 @@ func (s *State) Copy() *State { |
|
|
|
Validators: s.Validators.Copy(), |
|
|
|
LastValidators: s.LastValidators.Copy(), |
|
|
|
AppHash: s.AppHash, |
|
|
|
TxIndexer: s.TxIndexer, // pointer here, not value
|
|
|
|
TxIndexer: s.TxIndexer, |
|
|
|
LastHeightValidatorsChanged: s.LastHeightValidatorsChanged, |
|
|
|
logger: s.logger, |
|
|
|
ChainID: s.ChainID, |
|
|
@ -131,6 +137,7 @@ func (s *State) Copy() *State { |
|
|
|
func (s *State) Save() { |
|
|
|
s.mtx.Lock() |
|
|
|
defer s.mtx.Unlock() |
|
|
|
|
|
|
|
s.saveValidatorsInfo() |
|
|
|
s.db.SetSync(stateKey, s.Bytes()) |
|
|
|
} |
|
|
@ -142,38 +149,43 @@ func (s *State) SaveABCIResponses(abciResponses *ABCIResponses) { |
|
|
|
} |
|
|
|
|
|
|
|
// LoadABCIResponses loads the ABCIResponses from the database.
|
|
|
|
// This is useful for recovering from crashes where we called app.Commit and before we called
|
|
|
|
// s.Save()
|
|
|
|
func (s *State) LoadABCIResponses() *ABCIResponses { |
|
|
|
abciResponses := new(ABCIResponses) |
|
|
|
|
|
|
|
buf := s.db.Get(abciResponsesKey) |
|
|
|
if len(buf) != 0 { |
|
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error) |
|
|
|
wire.ReadBinaryPtr(abciResponses, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt("LoadABCIResponses: Data has been corrupted or its spec has changed: %v\n", *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
if len(buf) == 0 { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
abciResponses := new(ABCIResponses) |
|
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error) |
|
|
|
wire.ReadBinaryPtr(abciResponses, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt(`LoadABCIResponses: Data has been corrupted or its spec has |
|
|
|
changed: %v\n`, *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
|
|
|
|
return abciResponses |
|
|
|
} |
|
|
|
|
|
|
|
// LoadValidators loads the ValidatorSet for a given height.
|
|
|
|
func (s *State) LoadValidators(height int) (*types.ValidatorSet, error) { |
|
|
|
v := s.loadValidators(height) |
|
|
|
if v == nil { |
|
|
|
valInfo := s.loadValidators(height) |
|
|
|
if valInfo == nil { |
|
|
|
return nil, ErrNoValSetForHeight{height} |
|
|
|
} |
|
|
|
|
|
|
|
if v.ValidatorSet == nil { |
|
|
|
v = s.loadValidators(v.LastHeightChanged) |
|
|
|
if v == nil { |
|
|
|
cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at |
|
|
|
height %d as last changed from height %d`, v.LastHeightChanged, height)) |
|
|
|
if valInfo.ValidatorSet == nil { |
|
|
|
valInfo = s.loadValidators(valInfo.LastHeightChanged) |
|
|
|
if valInfo == nil { |
|
|
|
cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at height %d as |
|
|
|
last changed from height %d`, valInfo.LastHeightChanged, height)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return v.ValidatorSet, nil |
|
|
|
return valInfo.ValidatorSet, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (s *State) loadValidators(height int) *ValidatorsInfo { |
|
|
@ -187,9 +199,11 @@ func (s *State) loadValidators(height int) *ValidatorsInfo { |
|
|
|
wire.ReadBinaryPtr(v, r, 0, n, err) |
|
|
|
if *err != nil { |
|
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
|
|
cmn.Exit(cmn.Fmt("LoadValidators: Data has been corrupted or its spec has changed: %v\n", *err)) |
|
|
|
cmn.Exit(cmn.Fmt(`LoadValidators: Data has been corrupted or its spec has changed: |
|
|
|
%v\n`, *err)) |
|
|
|
} |
|
|
|
// TODO: ensure that buf is completely read.
|
|
|
|
|
|
|
|
return v |
|
|
|
} |
|
|
|
|
|
|
@ -200,13 +214,13 @@ func (s *State) loadValidators(height int) *ValidatorsInfo { |
|
|
|
func (s *State) saveValidatorsInfo() { |
|
|
|
changeHeight := s.LastHeightValidatorsChanged |
|
|
|
nextHeight := s.LastBlockHeight + 1 |
|
|
|
vi := &ValidatorsInfo{ |
|
|
|
valInfo := &ValidatorsInfo{ |
|
|
|
LastHeightChanged: changeHeight, |
|
|
|
} |
|
|
|
if changeHeight == nextHeight { |
|
|
|
vi.ValidatorSet = s.Validators |
|
|
|
valInfo.ValidatorSet = s.Validators |
|
|
|
} |
|
|
|
s.db.SetSync(calcValidatorsKey(nextHeight), vi.Bytes()) |
|
|
|
s.db.SetSync(calcValidatorsKey(nextHeight), valInfo.Bytes()) |
|
|
|
} |
|
|
|
|
|
|
|
// Equals returns true if the States are identical.
|
|
|
@ -219,8 +233,10 @@ func (s *State) Bytes() []byte { |
|
|
|
return wire.BinaryBytes(s) |
|
|
|
} |
|
|
|
|
|
|
|
// SetBlockAndValidators mutates State variables to update block and validators after running EndBlock.
|
|
|
|
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, abciResponses *ABCIResponses) { |
|
|
|
// SetBlockAndValidators mutates State variables
|
|
|
|
// to update block and validators after running EndBlock.
|
|
|
|
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, |
|
|
|
abciResponses *ABCIResponses) { |
|
|
|
|
|
|
|
// copy the valset so we can apply changes from EndBlock
|
|
|
|
// and update s.LastValidators and s.Validators
|
|
|
@ -248,8 +264,7 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
func (s *State) setBlockAndValidators( |
|
|
|
height int, blockID types.BlockID, blockTime time.Time, |
|
|
|
func (s *State) setBlockAndValidators(height int, blockID types.BlockID, blockTime time.Time, |
|
|
|
prevValSet, nextValSet *types.ValidatorSet) { |
|
|
|
|
|
|
|
s.LastBlockHeight = height |
|
|
@ -260,7 +275,7 @@ func (s *State) setBlockAndValidators( |
|
|
|
} |
|
|
|
|
|
|
|
// GetValidators returns the last and current validator sets.
|
|
|
|
func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) { |
|
|
|
func (s *State) GetValidators() (last *types.ValidatorSet, current *types.ValidatorSet) { |
|
|
|
return s.LastValidators, s.Validators |
|
|
|
} |
|
|
|
|
|
|
@ -293,15 +308,15 @@ func (a *ABCIResponses) Bytes() []byte { |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// ValidatorsInfo represents the latest validator set, or the last time it changed
|
|
|
|
// ValidatorsInfo represents the latest validator set, or the last height it changed
|
|
|
|
type ValidatorsInfo struct { |
|
|
|
ValidatorSet *types.ValidatorSet |
|
|
|
LastHeightChanged int |
|
|
|
} |
|
|
|
|
|
|
|
// Bytes serializes the ValidatorsInfo using go-wire
|
|
|
|
func (vi *ValidatorsInfo) Bytes() []byte { |
|
|
|
return wire.BinaryBytes(*vi) |
|
|
|
func (valInfo *ValidatorsInfo) Bytes() []byte { |
|
|
|
return wire.BinaryBytes(*valInfo) |
|
|
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
@ -353,6 +368,7 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// we do not need indexer during replay and in tests
|
|
|
|
return &State{ |
|
|
|
db: db, |
|
|
|
|
|
|
@ -365,7 +381,7 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) { |
|
|
|
Validators: types.NewValidatorSet(validators), |
|
|
|
LastValidators: types.NewValidatorSet(nil), |
|
|
|
AppHash: genDoc.AppHash, |
|
|
|
TxIndexer: &null.TxIndex{}, // we do not need indexer during replay and in tests
|
|
|
|
TxIndexer: &null.TxIndex{}, |
|
|
|
LastHeightValidatorsChanged: 1, |
|
|
|
}, nil |
|
|
|
} |