tendermint/state/state.go

378 lines
11 KiB
Go
Raw Normal View History

package state
import (
"bytes"
2017-09-05 18:57:36 -07:00
"fmt"
"io/ioutil"
2015-12-01 20:12:01 -08:00
"sync"
"time"
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
2017-04-21 15:12:54 -07:00
dbm "github.com/tendermint/tmlibs/db"
2017-05-02 00:53:32 -07:00
"github.com/tendermint/tmlibs/log"
2017-04-08 19:04:06 -07:00
2017-05-02 00:53:32 -07:00
wire "github.com/tendermint/go-wire"
2015-11-01 11:34:08 -08:00
"github.com/tendermint/tendermint/types"
)
var (
stateKey = []byte("stateKey")
abciResponsesKey = []byte("abciResponsesKey")
)
func calcValidatorsKey(height int64) []byte {
2017-08-21 13:31:54 -07:00
return []byte(cmn.Fmt("validatorsKey:%v", height))
}
2014-10-06 21:28:49 -07:00
//-----------------------------------------------------------------------------
2017-08-21 13:12:07 -07:00
// State represents the latest committed state of the Tendermint consensus,
// including the last committed block and validator set.
// Newly committed blocks are validated and executed against the State.
2014-10-07 01:05:54 -07:00
// NOTE: not goroutine-safe.
type State struct {
// mtx for writing to db
mtx sync.Mutex
db dbm.DB
ChainID string
// Consensus parameters used for validating blocks
Params types.ConsensusParams
2017-09-12 11:37:32 -07:00
// These fields are updated by SetBlockAndValidators.
// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
// LastValidators is used to validate block.LastCommit.
LastBlockHeight int64
2016-08-16 14:59:19 -07:00
LastBlockID types.BlockID
2015-11-01 11:34:08 -08:00
LastBlockTime time.Time
Validators *types.ValidatorSet
2017-09-12 11:37:32 -07:00
LastValidators *types.ValidatorSet
2017-09-05 18:57:36 -07:00
// 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 int64
2017-08-21 13:31:54 -07:00
// AppHash is updated after Commit
AppHash []byte
2017-05-02 00:53:32 -07:00
logger log.Logger
}
// GetState loads the most recent state from the database,
// 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) {
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
}
2017-08-21 13:12:07 -07:00
// LoadState loads the State from the database.
func LoadState(db dbm.DB) *State {
return loadState(db, stateKey)
}
func loadState(db dbm.DB, key []byte) *State {
buf := db.Get(key)
if len(buf) == 0 {
2014-10-03 17:59:54 -07:00
return nil
}
s := &State{db: db}
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
}
2017-08-21 13:12:07 -07:00
// SetLogger sets the logger on the State.
2017-05-02 00:53:32 -07:00
func (s *State) SetLogger(l log.Logger) {
s.logger = l
}
2017-08-21 13:12:07 -07:00
// Copy makes a copy of the State for mutating.
func (s *State) Copy() *State {
return &State{
2017-09-04 15:27:04 -07:00
db: s.db,
LastBlockHeight: s.LastBlockHeight,
LastBlockID: s.LastBlockID,
LastBlockTime: s.LastBlockTime,
Validators: s.Validators.Copy(),
LastValidators: s.LastValidators.Copy(),
AppHash: s.AppHash,
LastHeightValidatorsChanged: s.LastHeightValidatorsChanged,
logger: s.logger,
ChainID: s.ChainID,
Params: s.Params,
}
}
2017-08-21 13:12:07 -07:00
// Save persists the State to the database.
2015-12-01 20:12:01 -08:00
func (s *State) Save() {
s.mtx.Lock()
defer s.mtx.Unlock()
2017-09-04 15:27:04 -07:00
s.saveValidatorsInfo()
2016-12-06 02:52:07 -08:00
s.db.SetSync(stateKey, s.Bytes())
}
2017-08-21 13:12:07 -07:00
// SaveABCIResponses persists the ABCIResponses to the database.
// This is useful in case we crash after app.Commit and before s.Save().
func (s *State) SaveABCIResponses(abciResponses *ABCIResponses) {
s.db.SetSync(abciResponsesKey, abciResponses.Bytes())
}
2017-08-21 13:12:07 -07:00
// 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 {
buf := s.db.Get(abciResponsesKey)
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
}
2017-08-21 13:31:54 -07:00
// LoadValidators loads the ValidatorSet for a given height.
func (s *State) LoadValidators(height int64) (*types.ValidatorSet, error) {
valInfo := s.loadValidators(height)
if valInfo == nil {
2017-08-21 13:31:54 -07:00
return nil, ErrNoValSetForHeight{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))
2017-08-21 13:31:54 -07:00
}
}
return valInfo.ValidatorSet, nil
2017-08-21 13:31:54 -07:00
}
func (s *State) loadValidators(height int64) *ValidatorsInfo {
2017-08-21 13:31:54 -07:00
buf := s.db.Get(calcValidatorsKey(height))
if len(buf) == 0 {
return nil
}
2017-09-04 15:27:04 -07:00
v := new(ValidatorsInfo)
2017-08-21 13:31:54 -07:00
r, n, err := bytes.NewReader(buf), new(int), new(error)
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))
2017-08-21 13:31:54 -07:00
}
// TODO: ensure that buf is completely read.
2017-08-21 13:31:54 -07:00
return v
}
2017-09-04 15:27:04 -07:00
// saveValidatorsInfo persists the validator set for the next block to disk.
2017-09-05 18:57:36 -07:00
// It should be called from s.Save(), right before the state itself is persisted.
2017-09-04 15:27:04 -07:00
// If the validator set did not change after processing the latest block,
// only the last height for which the validators changed is persisted.
func (s *State) saveValidatorsInfo() {
changeHeight := s.LastHeightValidatorsChanged
nextHeight := s.LastBlockHeight + 1
valInfo := &ValidatorsInfo{
2017-09-04 15:27:04 -07:00
LastHeightChanged: changeHeight,
}
if changeHeight == nextHeight {
valInfo.ValidatorSet = s.Validators
2017-09-04 15:27:04 -07:00
}
s.db.SetSync(calcValidatorsKey(nextHeight), valInfo.Bytes())
2017-09-04 15:27:04 -07:00
}
2017-08-21 13:12:07 -07:00
// Equals returns true if the States are identical.
2016-08-23 18:44:07 -07:00
func (s *State) Equals(s2 *State) bool {
return bytes.Equal(s.Bytes(), s2.Bytes())
}
2017-08-21 13:12:07 -07:00
// Bytes serializes the State using go-wire.
2016-08-23 18:44:07 -07:00
func (s *State) Bytes() []byte {
2017-08-21 13:12:07 -07:00
return wire.BinaryBytes(s)
2016-08-23 18:44:07 -07:00
}
2017-10-17 00:59:05 -07:00
// SetBlockAndValidators mutates State variables
// to update block and validators after running EndBlock.
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader,
abciResponses *ABCIResponses) {
2017-04-18 07:44:13 -07:00
// copy the valset so we can apply changes from EndBlock
// and update s.LastValidators and s.Validators
prevValSet := s.Validators.Copy()
nextValSet := prevValSet.Copy()
2017-08-21 13:31:54 -07:00
// update the validator set with the latest abciResponses
if len(abciResponses.EndBlock.Diffs) > 0 {
err := updateValidators(nextValSet, abciResponses.EndBlock.Diffs)
if err != nil {
s.logger.Error("Error changing validator set", "err", err)
// TODO: err or carry on?
}
// change results from this height but only applies to the next height
2017-09-04 15:27:04 -07:00
s.LastHeightValidatorsChanged = header.Height + 1
}
// Update validator accums and set state variables
nextValSet.IncrementAccum(1)
s.setBlockAndValidators(header.Height,
types.BlockID{header.Hash(), blockPartsHeader},
header.Time,
prevValSet, nextValSet)
2017-08-21 13:12:07 -07:00
}
func (s *State) setBlockAndValidators(height int64, blockID types.BlockID, blockTime time.Time,
prevValSet, nextValSet *types.ValidatorSet) {
s.LastBlockHeight = height
s.LastBlockID = blockID
s.LastBlockTime = blockTime
2016-08-23 18:44:07 -07:00
s.Validators = nextValSet
s.LastValidators = prevValSet
}
2017-08-21 13:12:07 -07:00
// GetValidators returns the last and current validator sets.
func (s *State) GetValidators() (last *types.ValidatorSet, current *types.ValidatorSet) {
2016-08-23 18:44:07 -07:00
return s.LastValidators, s.Validators
2015-06-19 08:36:20 -07:00
}
//------------------------------------------------------------------------
2017-08-21 13:12:07 -07:00
// ABCIResponses retains the responses of the various ABCI calls during block processing.
// It is persisted to disk before calling Commit.
type ABCIResponses struct {
Height int64
DeliverTx []*abci.ResponseDeliverTx
EndBlock *abci.ResponseEndBlock
2017-04-14 22:33:30 -07:00
txs types.Txs // reference for indexing results by hash
}
2017-08-21 13:12:07 -07:00
// NewABCIResponses returns a new ABCIResponses
func NewABCIResponses(block *types.Block) *ABCIResponses {
return &ABCIResponses{
Height: block.Height,
DeliverTx: make([]*abci.ResponseDeliverTx, block.NumTxs),
txs: block.Data.Txs,
}
}
2017-08-21 13:12:07 -07:00
// Bytes serializes the ABCIResponse using go-wire
func (a *ABCIResponses) Bytes() []byte {
2017-08-21 13:12:07 -07:00
return wire.BinaryBytes(*a)
}
2017-08-21 13:31:54 -07:00
//-----------------------------------------------------------------------------
// ValidatorsInfo represents the latest validator set, or the last height it changed
2017-09-04 15:27:04 -07:00
type ValidatorsInfo struct {
2017-08-21 13:31:54 -07:00
ValidatorSet *types.ValidatorSet
LastHeightChanged int64
2017-08-21 13:31:54 -07:00
}
2017-09-04 15:27:04 -07:00
// Bytes serializes the ValidatorsInfo using go-wire
func (valInfo *ValidatorsInfo) Bytes() []byte {
return wire.BinaryBytes(*valInfo)
2017-08-21 13:31:54 -07:00
}
//------------------------------------------------------------------------
// Genesis
2014-10-07 23:11:04 -07:00
// MakeGenesisStateFromFile reads and unmarshals state from the given
// file.
//
// Used during replay and in tests.
func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*State, error) {
genDoc, err := MakeGenesisDocFromFile(genDocFile)
if err != nil {
return nil, err
}
return MakeGenesisState(db, genDoc)
}
// MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file.
func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) {
2015-12-01 20:12:01 -08:00
genDocJSON, err := ioutil.ReadFile(genDocFile)
if err != nil {
return nil, fmt.Errorf("Couldn't read GenesisDoc file: %v", err)
}
2017-01-29 13:50:04 -08:00
genDoc, err := types.GenesisDocFromJSON(genDocJSON)
if err != nil {
return nil, fmt.Errorf("Error reading GenesisDoc: %v", err)
2017-01-29 13:50:04 -08:00
}
return genDoc, nil
}
// MakeGenesisState creates state from types.GenesisDoc.
func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) {
2017-09-11 13:48:41 -07:00
err := genDoc.ValidateAndComplete()
if err != nil {
return nil, fmt.Errorf("Error in genesis file: %v", err)
}
2015-11-01 11:34:08 -08:00
// Make validators slice
validators := make([]*types.Validator, len(genDoc.Validators))
for i, val := range genDoc.Validators {
pubKey := val.PubKey
address := pubKey.Address()
// Make validator
validators[i] = &types.Validator{
Address: address,
PubKey: pubKey,
2017-09-21 11:37:34 -07:00
VotingPower: val.Power,
}
}
return &State{
db: db,
ChainID: genDoc.ChainID,
Params: *genDoc.ConsensusParams,
2017-08-21 13:31:54 -07:00
LastBlockHeight: 0,
LastBlockID: types.BlockID{},
LastBlockTime: genDoc.GenesisTime,
Validators: types.NewValidatorSet(validators),
LastValidators: types.NewValidatorSet(nil),
AppHash: genDoc.AppHash,
2017-09-04 15:27:04 -07:00
LastHeightValidatorsChanged: 1,
}, nil
}