mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 14:52:17 +00:00
176 lines
4.9 KiB
Go
176 lines
4.9 KiB
Go
package state
|
|
|
|
import (
|
|
"bytes"
|
|
"io/ioutil"
|
|
"time"
|
|
|
|
. "github.com/tendermint/go-common"
|
|
dbm "github.com/tendermint/go-db"
|
|
"github.com/tendermint/go-merkle"
|
|
"github.com/tendermint/go-wire"
|
|
"github.com/tendermint/tendermint/events"
|
|
"github.com/tendermint/tendermint/types"
|
|
)
|
|
|
|
var (
|
|
stateKey = []byte("stateKey")
|
|
)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// NOTE: not goroutine-safe.
|
|
type State struct {
|
|
DB dbm.DB
|
|
ChainID string
|
|
LastBlockHeight int
|
|
LastBlockHash []byte
|
|
LastBlockParts types.PartSetHeader
|
|
LastBlockTime time.Time
|
|
Validators *types.ValidatorSet
|
|
LastValidators *types.ValidatorSet
|
|
|
|
evc events.Fireable // typically an events.EventCache
|
|
}
|
|
|
|
func LoadState(db dbm.DB) *State {
|
|
s := &State{DB: db}
|
|
buf := db.Get(stateKey)
|
|
if len(buf) == 0 {
|
|
return nil
|
|
} else {
|
|
r, n, err := bytes.NewReader(buf), new(int), new(error)
|
|
s.ChainID = wire.ReadString(r, 0, n, err)
|
|
s.LastBlockHeight = wire.ReadVarint(r, n, err)
|
|
s.LastBlockHash = wire.ReadByteSlice(r, 0, n, err)
|
|
s.LastBlockParts = wire.ReadBinary(types.PartSetHeader{}, r, 0, n, err).(types.PartSetHeader)
|
|
s.LastBlockTime = wire.ReadTime(r, n, err)
|
|
s.Validators = wire.ReadBinary(&types.ValidatorSet{}, r, 0, n, err).(*types.ValidatorSet)
|
|
s.LastValidators = wire.ReadBinary(&types.ValidatorSet{}, r, 0, n, err).(*types.ValidatorSet)
|
|
if *err != nil {
|
|
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
|
Exit(Fmt("Data has been corrupted or its spec has changed: %v\n", *err))
|
|
}
|
|
// TODO: ensure that buf is completely read.
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (s *State) Save() {
|
|
buf, n, err := new(bytes.Buffer), new(int), new(error)
|
|
wire.WriteString(s.ChainID, buf, n, err)
|
|
wire.WriteVarint(s.LastBlockHeight, buf, n, err)
|
|
wire.WriteByteSlice(s.LastBlockHash, buf, n, err)
|
|
wire.WriteBinary(s.LastBlockParts, buf, n, err)
|
|
wire.WriteTime(s.LastBlockTime, buf, n, err)
|
|
wire.WriteBinary(s.Validators, buf, n, err)
|
|
wire.WriteBinary(s.LastValidators, buf, n, err)
|
|
if *err != nil {
|
|
PanicCrisis(*err)
|
|
}
|
|
s.DB.Set(stateKey, buf.Bytes())
|
|
}
|
|
|
|
// CONTRACT:
|
|
// Copy() is a cheap way to take a snapshot,
|
|
// as if State were copied by value.
|
|
func (s *State) Copy() *State {
|
|
return &State{
|
|
DB: s.DB,
|
|
ChainID: s.ChainID,
|
|
LastBlockHeight: s.LastBlockHeight,
|
|
LastBlockHash: s.LastBlockHash,
|
|
LastBlockParts: s.LastBlockParts,
|
|
LastBlockTime: s.LastBlockTime,
|
|
Validators: s.Validators.Copy(), // TODO remove need for Copy() here.
|
|
LastValidators: s.LastValidators.Copy(), // That is, make updates to the validator set
|
|
evc: nil,
|
|
}
|
|
}
|
|
|
|
// Returns a hash that represents the state data, excluding Last*
|
|
func (s *State) Hash() []byte {
|
|
return merkle.SimpleHashFromMap(map[string]interface{}{
|
|
"Validators": s.Validators,
|
|
})
|
|
}
|
|
|
|
// Mutates the block in place and updates it with new state hash.
|
|
func (s *State) ComputeBlockStateHash(block *types.Block) error {
|
|
sCopy := s.Copy()
|
|
// sCopy has no event cache in it, so this won't fire events
|
|
err := execBlock(sCopy, block, types.PartSetHeader{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Set block.StateHash
|
|
block.StateHash = sCopy.Hash()
|
|
return nil
|
|
}
|
|
|
|
func (s *State) SetDB(db dbm.DB) {
|
|
s.DB = db
|
|
}
|
|
|
|
// Implements events.Eventable. Typically uses events.EventCache
|
|
func (s *State) SetFireable(evc events.Fireable) {
|
|
s.evc = evc
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Genesis
|
|
|
|
func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*types.GenesisDoc, *State) {
|
|
jsonBlob, err := ioutil.ReadFile(genDocFile)
|
|
if err != nil {
|
|
Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
|
|
}
|
|
genDoc := types.GenesisDocFromJSON(jsonBlob)
|
|
return genDoc, MakeGenesisState(db, genDoc)
|
|
}
|
|
|
|
func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) *State {
|
|
if len(genDoc.Validators) == 0 {
|
|
Exit(Fmt("The genesis file has no validators"))
|
|
}
|
|
|
|
if genDoc.GenesisTime.IsZero() {
|
|
genDoc.GenesisTime = time.Now()
|
|
}
|
|
|
|
// XXX Speak to application, ensure genesis state.
|
|
|
|
// 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,
|
|
VotingPower: val.Amount,
|
|
}
|
|
}
|
|
|
|
return &State{
|
|
DB: db,
|
|
ChainID: genDoc.ChainID,
|
|
LastBlockHeight: 0,
|
|
LastBlockHash: nil,
|
|
LastBlockParts: types.PartSetHeader{},
|
|
LastBlockTime: genDoc.GenesisTime,
|
|
Validators: types.NewValidatorSet(validators),
|
|
LastValidators: types.NewValidatorSet(nil),
|
|
}
|
|
}
|
|
|
|
func RandGenesisState(numValidators int, randPower bool, minPower int64) (*State, []*types.PrivValidator) {
|
|
db := dbm.NewMemDB()
|
|
genDoc, privValidators := types.RandGenesisDoc(numValidators, randPower, minPower)
|
|
s0 := MakeGenesisState(db, genDoc)
|
|
s0.Save()
|
|
return s0, privValidators
|
|
}
|