package state import ( "bytes" "io/ioutil" "sync" "time" . "github.com/tendermint/go-common" dbm "github.com/tendermint/go-db" "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 { mtx sync.Mutex db dbm.DB GenesisDoc *types.GenesisDoc ChainID string LastBlockHeight int LastBlockHash []byte LastBlockParts types.PartSetHeader LastBlockTime time.Time Validators *types.ValidatorSet LastValidators *types.ValidatorSet LastAppHash []byte 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) wire.ReadBinaryPtr(&s, r, 0, n, err) 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) Copy() *State { return &State{ db: s.db, GenesisDoc: s.GenesisDoc, ChainID: s.ChainID, LastBlockHeight: s.LastBlockHeight, LastBlockHash: s.LastBlockHash, LastBlockParts: s.LastBlockParts, LastBlockTime: s.LastBlockTime, Validators: s.Validators.Copy(), LastValidators: s.LastValidators.Copy(), LastAppHash: s.LastAppHash, evc: nil, } } func (s *State) Save() { s.mtx.Lock() defer s.mtx.Unlock() buf, n, err := new(bytes.Buffer), new(int), new(error) wire.WriteBinary(s, buf, n, err) if *err != nil { PanicCrisis(*err) } s.db.Set(stateKey, buf.Bytes()) } // Implements events.Eventable. Typically uses events.EventCache func (s *State) SetFireable(evc events.Fireable) { s.mtx.Lock() defer s.mtx.Unlock() s.evc = evc } //----------------------------------------------------------------------------- // Genesis func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) *State { genDocJSON, err := ioutil.ReadFile(genDocFile) if err != nil { Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) } genDoc := types.GenesisDocFromJSON(genDocJSON) return 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() } // 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, GenesisDoc: genDoc, ChainID: genDoc.ChainID, LastBlockHeight: 0, LastBlockHash: nil, LastBlockParts: types.PartSetHeader{}, LastBlockTime: genDoc.GenesisTime, Validators: types.NewValidatorSet(validators), LastValidators: types.NewValidatorSet(nil), LastAppHash: genDoc.AppHash, } }