tendermint/state/state.go

238 lines
6.4 KiB
Go
Raw Normal View History

package state
import (
"bytes"
"errors"
"time"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
2014-10-04 19:16:49 -07:00
. "github.com/tendermint/tendermint/db"
"github.com/tendermint/tendermint/merkle"
)
var (
2014-10-07 23:11:04 -07:00
ErrStateInvalidAccountId = errors.New("Error State invalid account id")
ErrStateInvalidSignature = errors.New("Error State invalid signature")
2014-10-07 00:43:34 -07:00
ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number")
2014-10-07 23:11:04 -07:00
ErrStateInvalidAccountState = errors.New("Error State invalid account state")
2014-10-07 00:43:34 -07:00
ErrStateInvalidValidationStateHash = errors.New("Error State invalid ValidationStateHash")
ErrStateInvalidAccountStateHash = errors.New("Error State invalid AccountStateHash")
2014-10-07 23:11:04 -07:00
ErrStateInsufficientFunds = errors.New("Error State insufficient funds")
2014-10-11 21:27:58 -07:00
stateKey = []byte("stateKey")
minBondAmount = uint64(1) // TODO adjust
defaultAccountDetailsCacheCapacity = 1000 // TODO adjust
)
2014-10-06 21:28:49 -07:00
//-----------------------------------------------------------------------------
2014-10-07 01:05:54 -07:00
// NOTE: not goroutine-safe.
type State struct {
2014-10-07 23:11:04 -07:00
DB DB
Height uint32 // Last known block height
BlockHash []byte // Last known block hash
CommitTime time.Time
2014-10-11 21:27:58 -07:00
AccountDetails merkle.Tree
2014-10-07 23:11:04 -07:00
Validators *ValidatorSet
}
2014-10-07 23:11:04 -07:00
func GenesisState(db DB, genesisTime time.Time, accDets []*AccountDetail) *State {
2014-10-04 19:16:49 -07:00
2014-10-06 21:28:49 -07:00
// TODO: Use "uint64Codec" instead of BasicCodec
2014-10-11 21:27:58 -07:00
accountDetails := merkle.NewIAVLTree(BasicCodec, AccountDetailCodec, defaultAccountDetailsCacheCapacity, db)
validators := []*Validator{}
2014-10-04 19:16:49 -07:00
2014-10-07 23:11:04 -07:00
for _, accDet := range accDets {
accountDetails.Set(accDet.Id, accDet)
2014-10-11 21:27:58 -07:00
if accDet.Status == AccountDetailStatusBonded {
validators = append(validators, &Validator{
Account: accDet.Account,
BondHeight: 0,
VotingPower: accDet.Balance,
Accum: 0,
})
2014-10-04 19:16:49 -07:00
}
}
2014-10-11 21:27:58 -07:00
if len(validators) == 0 {
panic("Must have some validators")
}
2014-10-04 19:16:49 -07:00
validatorSet := NewValidatorSet(validators)
return &State{
2014-10-07 23:11:04 -07:00
DB: db,
Height: 0,
BlockHash: nil,
CommitTime: genesisTime,
AccountDetails: accountDetails,
Validators: validatorSet,
2014-10-04 19:16:49 -07:00
}
2014-10-03 17:59:54 -07:00
}
2014-10-04 19:16:49 -07:00
func LoadState(db DB) *State {
2014-10-07 01:05:54 -07:00
s := &State{DB: db}
buf := db.Get(stateKey)
if len(buf) == 0 {
2014-10-03 17:59:54 -07:00
return nil
} else {
reader := bytes.NewReader(buf)
var n int64
var err error
2014-10-07 01:05:54 -07:00
s.Height = ReadUInt32(reader, &n, &err)
s.CommitTime = ReadTime(reader, &n, &err)
s.BlockHash = ReadByteSlice(reader, &n, &err)
2014-10-07 23:11:04 -07:00
accountDetailsHash := ReadByteSlice(reader, &n, &err)
2014-10-11 21:27:58 -07:00
s.AccountDetails = merkle.NewIAVLTree(BasicCodec, AccountDetailCodec, defaultAccountDetailsCacheCapacity, db)
s.AccountDetails.Load(accountDetailsHash)
s.Validators = ReadValidatorSet(reader, &n, &err)
if err != nil {
panic(err)
}
2014-10-11 21:27:58 -07:00
// TODO: ensure that buf is completely read.
}
return s
}
// Save this state into the db.
2014-09-11 10:55:32 -07:00
// For convenience, the commitTime (required by ConsensusAgent)
// is saved here.
func (s *State) Save(commitTime time.Time) {
2014-10-07 01:05:54 -07:00
s.CommitTime = commitTime
2014-10-11 21:27:58 -07:00
s.AccountDetails.Save()
var buf bytes.Buffer
var n int64
var err error
2014-10-07 01:05:54 -07:00
WriteUInt32(&buf, s.Height, &n, &err)
WriteTime(&buf, commitTime, &n, &err)
2014-10-07 01:05:54 -07:00
WriteByteSlice(&buf, s.BlockHash, &n, &err)
2014-10-11 21:27:58 -07:00
WriteByteSlice(&buf, s.AccountDetails.Hash(), &n, &err)
WriteBinary(&buf, s.Validators, &n, &err)
if err != nil {
panic(err)
}
2014-10-07 01:05:54 -07:00
s.DB.Set(stateKey, buf.Bytes())
}
func (s *State) Copy() *State {
return &State{
2014-10-07 23:11:04 -07:00
DB: s.DB,
Height: s.Height,
CommitTime: s.CommitTime,
BlockHash: s.BlockHash,
AccountDetails: s.AccountDetails.Copy(),
Validators: s.Validators.Copy(),
}
}
2014-09-11 22:44:59 -07:00
// If the tx is invalid, an error will be returned.
2014-10-06 21:28:49 -07:00
// Unlike AppendBlock(), state will not be altered.
func (s *State) ExecTx(tx Tx) error {
2014-10-07 23:11:04 -07:00
accDet := s.GetAccountDetail(tx.GetSignature().SignerId)
if accDet == nil {
return ErrStateInvalidAccountId
}
// Check signature
if !accDet.Verify(tx) {
return ErrStateInvalidSignature
}
// Check sequence
if tx.GetSequence() <= accDet.Sequence {
return ErrStateInvalidSequenceNumber
}
// Exec tx
switch tx.(type) {
case *SendTx:
stx := tx.(*SendTx)
toAccDet := s.GetAccountDetail(stx.To)
// Accounts must be nominal
if accDet.Status != AccountDetailStatusNominal {
return ErrStateInvalidAccountState
}
if toAccDet.Status != AccountDetailStatusNominal {
return ErrStateInvalidAccountState
}
// Check account balance
if accDet.Balance < stx.Fee+stx.Amount {
return ErrStateInsufficientFunds
}
// Check existence of destination account
if toAccDet == nil {
return ErrStateInvalidAccountId
}
2014-10-07 23:11:04 -07:00
// Good!
accDet.Balance -= (stx.Fee + stx.Amount)
toAccDet.Balance += (stx.Amount)
s.SetAccountDetail(accDet)
s.SetAccountDetail(toAccDet)
//case *NameTx
case *BondTx:
btx := tx.(*BondTx)
// Account must be nominal
if accDet.Status != AccountDetailStatusNominal {
return ErrStateInvalidAccountState
}
// Check account balance
if accDet.Balance < minBondAmount {
return ErrStateInsufficientFunds
}
// TODO: max number of validators?
// Good!
accDet.Balance -= btx.Fee // remaining balance are bonded coins.
accDet.Status = AccountDetailStatusBonded
s.SetAccountDetail(accDet)
// XXX add validator
case *UnbondTx:
case *TimeoutTx:
case *DupeoutTx:
}
2014-10-06 21:28:49 -07:00
panic("Implement ExecTx()")
return nil
}
2014-09-11 22:44:59 -07:00
// NOTE: If an error occurs during block execution, state will be left
2014-10-07 19:37:20 -07:00
// at an invalid state. Copy the state before calling AppendBlock!
2014-10-06 21:28:49 -07:00
func (s *State) AppendBlock(b *Block) error {
2014-09-11 22:44:59 -07:00
// Basic block validation.
2014-10-07 01:05:54 -07:00
err := b.ValidateBasic(s.Height, s.BlockHash)
2014-09-11 22:44:59 -07:00
if err != nil {
return err
}
// Commit each tx
for _, tx := range b.Data.Txs {
2014-10-07 01:05:54 -07:00
err := s.ExecTx(tx)
2014-09-11 22:44:59 -07:00
if err != nil {
return err
}
}
2014-10-07 00:43:34 -07:00
// Increment validator AccumPowers
2014-10-07 01:05:54 -07:00
s.Validators.IncrementAccum()
2014-10-07 00:43:34 -07:00
// State hashes should match
2014-10-07 01:05:54 -07:00
if !bytes.Equal(s.Validators.Hash(), b.ValidationStateHash) {
2014-10-07 00:43:34 -07:00
return ErrStateInvalidValidationStateHash
}
2014-10-11 21:27:58 -07:00
if !bytes.Equal(s.AccountDetails.Hash(), b.AccountStateHash) {
2014-10-07 00:43:34 -07:00
return ErrStateInvalidAccountStateHash
}
2014-10-07 01:05:54 -07:00
s.Height = b.Height
s.BlockHash = b.Hash()
return nil
}
2014-10-07 23:11:04 -07:00
func (s *State) GetAccountDetail(accountId uint64) *AccountDetail {
2014-10-11 21:27:58 -07:00
_, accDet := s.AccountDetails.Get(accountId)
2014-10-07 23:11:04 -07:00
if accDet == nil {
2014-10-06 21:28:49 -07:00
return nil
}
2014-10-07 23:11:04 -07:00
return accDet.(*AccountDetail)
}
// Returns false if new, true if updated.
func (s *State) SetAccountDetail(accDet *AccountDetail) (updated bool) {
return s.AccountDetails.Set(accDet.Id, accDet)
}