Skip to content
Snippets Groups Projects
state.go 14.3 KiB
Newer Older
Casey Kuhlman's avatar
Casey Kuhlman committed
package state

import (
	"bytes"
	"io"
Ethan Buchman's avatar
Ethan Buchman committed
	"io/ioutil"
Casey Kuhlman's avatar
Casey Kuhlman committed
	"time"

	acm "github.com/eris-ltd/eris-db/account"
	ptypes "github.com/eris-ltd/eris-db/permission/types"
	. "github.com/eris-ltd/eris-db/state/types"
	txs "github.com/eris-ltd/eris-db/txs"

Ethan Buchman's avatar
Ethan Buchman committed
	. "github.com/tendermint/go-common"
	dbm "github.com/tendermint/go-db"
	"github.com/tendermint/go-events"
	"github.com/tendermint/go-merkle"
	"github.com/tendermint/go-wire"
Ethan Buchman's avatar
Ethan Buchman committed
	"github.com/tendermint/tendermint/types"
Casey Kuhlman's avatar
Casey Kuhlman committed
)

var (
	stateKey                     = []byte("stateKey")
	minBondAmount                = int64(1)           // TODO adjust
	defaultAccountsCacheCapacity = 1000               // TODO adjust
	unbondingPeriodBlocks        = int(60 * 24 * 365) // TODO probably better to make it time based.
	validatorTimeoutBlocks       = int(10)            // TODO adjust
	maxLoadStateElementSize      = 0                  // no max
Casey Kuhlman's avatar
Casey Kuhlman committed
)

//-----------------------------------------------------------------------------

// NOTE: not goroutine-safe.
type State struct {
Ethan Buchman's avatar
Ethan Buchman committed
	DB              dbm.DB
	ChainID         string
	LastBlockHeight int
	LastBlockHash   []byte
	LastBlockParts  types.PartSetHeader
	LastBlockTime   time.Time
	//	BondedValidators     *types.ValidatorSet
	//	LastBondedValidators *types.ValidatorSet
	//	UnbondingValidators  *types.ValidatorSet
	accounts       merkle.Tree // Shouldn't be accessed directly.
	validatorInfos merkle.Tree // Shouldn't be accessed directly.
	nameReg        merkle.Tree // Shouldn't be accessed directly.
Casey Kuhlman's avatar
Casey Kuhlman committed

	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, maxLoadStateElementSize, n, err)
androlo's avatar
androlo committed
		s.LastBlockHeight = wire.ReadVarint(r, n, err)
		s.LastBlockHash = wire.ReadByteSlice(r, maxLoadStateElementSize, n, err)
		s.LastBlockParts = wire.ReadBinary(types.PartSetHeader{}, r, maxLoadStateElementSize, n, err).(types.PartSetHeader)
androlo's avatar
androlo committed
		s.LastBlockTime = wire.ReadTime(r, n, err)
Ethan Buchman's avatar
Ethan Buchman committed
		// s.BondedValidators = wire.ReadBinary(&types.ValidatorSet{}, r, maxLoadStateElementSize, n, err).(*types.ValidatorSet)
		// s.LastBondedValidators = wire.ReadBinary(&types.ValidatorSet{}, r, maxLoadStateElementSize, n, err).(*types.ValidatorSet)
		// s.UnbondingValidators = wire.ReadBinary(&types.ValidatorSet{}, r, maxLoadStateElementSize, n, err).(*types.ValidatorSet)
		accountsHash := wire.ReadByteSlice(r, maxLoadStateElementSize, n, err)
Ethan Buchman's avatar
Ethan Buchman committed
		s.accounts = merkle.NewIAVLTree(defaultAccountsCacheCapacity, db)
Casey Kuhlman's avatar
Casey Kuhlman committed
		s.accounts.Load(accountsHash)
		//validatorInfosHash := wire.ReadByteSlice(r, maxLoadStateElementSize, n, err)
		//s.validatorInfos = merkle.NewIAVLTree(wire.BasicCodec, types.ValidatorInfoCodec, 0, db)
		//s.validatorInfos.Load(validatorInfosHash)
		nameRegHash := wire.ReadByteSlice(r, maxLoadStateElementSize, n, err)
Ethan Buchman's avatar
Ethan Buchman committed
		s.nameReg = merkle.NewIAVLTree(0, db)
Casey Kuhlman's avatar
Casey Kuhlman committed
		s.nameReg.Load(nameRegHash)
		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))
Casey Kuhlman's avatar
Casey Kuhlman committed
		}
		// TODO: ensure that buf is completely read.
	}
	return s
}

func (s *State) Save() {
	s.accounts.Save()
	//s.validatorInfos.Save()
Casey Kuhlman's avatar
Casey Kuhlman committed
	s.nameReg.Save()
	buf, n, err := new(bytes.Buffer), new(int), new(error)
androlo's avatar
androlo committed
	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)
Ethan Buchman's avatar
Ethan Buchman committed
	// wire.WriteBinary(s.BondedValidators, buf, n, err)
	// wire.WriteBinary(s.LastBondedValidators, buf, n, err)
	// wire.WriteBinary(s.UnbondingValidators, buf, n, err)
androlo's avatar
androlo committed
	wire.WriteByteSlice(s.accounts.Hash(), buf, n, err)
	//wire.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
androlo's avatar
androlo committed
	wire.WriteByteSlice(s.nameReg.Hash(), buf, n, err)
Casey Kuhlman's avatar
Casey Kuhlman committed
	if *err != nil {
androlo's avatar
androlo committed
		PanicCrisis(*err)
Casey Kuhlman's avatar
Casey Kuhlman committed
	}
	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{
Ethan Buchman's avatar
Ethan Buchman committed
		DB:              s.DB,
		ChainID:         s.ChainID,
		LastBlockHeight: s.LastBlockHeight,
		LastBlockHash:   s.LastBlockHash,
		LastBlockParts:  s.LastBlockParts,
		LastBlockTime:   s.LastBlockTime,
		// BondedValidators:     s.BondedValidators.Copy(),     // TODO remove need for Copy() here.
		// LastBondedValidators: s.LastBondedValidators.Copy(), // That is, make updates to the validator set
		// UnbondingValidators: s.UnbondingValidators.Copy(), // copy the valSet lazily.
		accounts: s.accounts.Copy(),
		//validatorInfos:       s.validatorInfos.Copy(),
		nameReg: s.nameReg.Copy(),
		evc:     nil,
Casey Kuhlman's avatar
Casey Kuhlman committed
	}
}

// Returns a hash that represents the state data, excluding Last*
func (s *State) Hash() []byte {
Ethan Buchman's avatar
Ethan Buchman committed
	return merkle.SimpleHashFromMap(map[string]interface{}{
Ethan Buchman's avatar
Ethan Buchman committed
		//"BondedValidators":    s.BondedValidators,
		//"UnbondingValidators": s.UnbondingValidators,
		"Accounts": s.accounts,
		//"ValidatorInfos":      s.validatorInfos,
		"NameRegistry": s.nameReg,
/* //XXX Done by tendermint core
Casey Kuhlman's avatar
Casey Kuhlman committed
// Mutates the block in place and updates it with new state hash.
func (s *State) ComputeBlockStateHash(block *types.Block) error {
Casey Kuhlman's avatar
Casey Kuhlman committed
	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
}
androlo's avatar
androlo committed
//-------------------------------------
// State.params

func (s *State) GetGasLimit() int64 {
	return 1000000 // TODO
}

// State.params
Casey Kuhlman's avatar
Casey Kuhlman committed
//-------------------------------------
// State.accounts

// Returns nil if account does not exist with given address.
Casey Kuhlman's avatar
Casey Kuhlman committed
// Implements Statelike
androlo's avatar
androlo committed
func (s *State) GetAccount(address []byte) *acm.Account {
Ethan Buchman's avatar
Ethan Buchman committed
	_, accBytes, _ := s.accounts.Get(address)
	if accBytes == nil {
Casey Kuhlman's avatar
Casey Kuhlman committed
		return nil
	}
Ethan Buchman's avatar
Ethan Buchman committed
	return acm.DecodeAccount(accBytes)
Casey Kuhlman's avatar
Casey Kuhlman committed
}

// The account is copied before setting, so mutating it
// afterwards has no side effects.
// Implements Statelike
androlo's avatar
androlo committed
func (s *State) UpdateAccount(account *acm.Account) bool {
Ethan Buchman's avatar
Ethan Buchman committed
	return s.accounts.Set(account.Address, acm.EncodeAccount(account))
Casey Kuhlman's avatar
Casey Kuhlman committed
}

// Implements Statelike
func (s *State) RemoveAccount(address []byte) bool {
	_, removed := s.accounts.Remove(address)
	return removed
}

// The returned Account is a copy, so mutating it
// has no side effects.
func (s *State) GetAccounts() merkle.Tree {
	return s.accounts.Copy()
}

// Set the accounts tree
func (s *State) SetAccounts(accounts merkle.Tree) {
	s.accounts = accounts
}

Casey Kuhlman's avatar
Casey Kuhlman committed
// State.accounts
//-------------------------------------
// State.validators

// XXX: now handled by tendermint core

/*

Casey Kuhlman's avatar
Casey Kuhlman committed
// The returned ValidatorInfo is a copy, so mutating it
// has no side effects.
Ethan Buchman's avatar
Ethan Buchman committed
func (s *State) GetValidatorInfo(address []byte) *types.ValidatorInfo {
Casey Kuhlman's avatar
Casey Kuhlman committed
	_, valInfo := s.validatorInfos.Get(address)
	if valInfo == nil {
		return nil
	}
Ethan Buchman's avatar
Ethan Buchman committed
	return valInfo.(*types.ValidatorInfo).Copy()
Casey Kuhlman's avatar
Casey Kuhlman committed
}

// Returns false if new, true if updated.
// The valInfo is copied before setting, so mutating it
// afterwards has no side effects.
Ethan Buchman's avatar
Ethan Buchman committed
func (s *State) SetValidatorInfo(valInfo *types.ValidatorInfo) (updated bool) {
Casey Kuhlman's avatar
Casey Kuhlman committed
	return s.validatorInfos.Set(valInfo.Address, valInfo.Copy())
}

func (s *State) GetValidatorInfos() merkle.Tree {
	return s.validatorInfos.Copy()
}

Ethan Buchman's avatar
Ethan Buchman committed
func (s *State) unbondValidator(val *types.Validator) {
Casey Kuhlman's avatar
Casey Kuhlman committed
	// Move validator to UnbondingValidators
	val, removed := s.BondedValidators.Remove(val.Address)
	if !removed {
androlo's avatar
androlo committed
		PanicCrisis("Couldn't remove validator for unbonding")
Casey Kuhlman's avatar
Casey Kuhlman committed
	}
	val.UnbondHeight = s.LastBlockHeight + 1
	added := s.UnbondingValidators.Add(val)
	if !added {
androlo's avatar
androlo committed
		PanicCrisis("Couldn't add validator for unbonding")
Ethan Buchman's avatar
Ethan Buchman committed
func (s *State) rebondValidator(val *types.Validator) {
Casey Kuhlman's avatar
Casey Kuhlman committed
	// Move validator to BondingValidators
	val, removed := s.UnbondingValidators.Remove(val.Address)
	if !removed {
androlo's avatar
androlo committed
		PanicCrisis("Couldn't remove validator for rebonding")
Casey Kuhlman's avatar
Casey Kuhlman committed
	}
	val.BondHeight = s.LastBlockHeight + 1
	added := s.BondedValidators.Add(val)
	if !added {
androlo's avatar
androlo committed
		PanicCrisis("Couldn't add validator for rebonding")
Ethan Buchman's avatar
Ethan Buchman committed
func (s *State) releaseValidator(val *types.Validator) {
Casey Kuhlman's avatar
Casey Kuhlman committed
	// Update validatorInfo
	valInfo := s.GetValidatorInfo(val.Address)
	if valInfo == nil {
androlo's avatar
androlo committed
		PanicSanity("Couldn't find validatorInfo for release")
Casey Kuhlman's avatar
Casey Kuhlman committed
	}
	valInfo.ReleasedHeight = s.LastBlockHeight + 1
	s.SetValidatorInfo(valInfo)

	// Send coins back to UnbondTo outputs
	accounts, err := getOrMakeOutputs(s, nil, valInfo.UnbondTo)
Casey Kuhlman's avatar
Casey Kuhlman committed
	if err != nil {
androlo's avatar
androlo committed
		PanicSanity("Couldn't get or make unbondTo accounts")
Casey Kuhlman's avatar
Casey Kuhlman committed
	}
	adjustByOutputs(accounts, valInfo.UnbondTo)
	for _, acc := range accounts {
		s.UpdateAccount(acc)
	}

	// Remove validator from UnbondingValidators
	_, removed := s.UnbondingValidators.Remove(val.Address)
	if !removed {
androlo's avatar
androlo committed
		PanicCrisis("Couldn't remove validator for release")
Ethan Buchman's avatar
Ethan Buchman committed
func (s *State) destroyValidator(val *types.Validator) {
Casey Kuhlman's avatar
Casey Kuhlman committed
	// Update validatorInfo
	valInfo := s.GetValidatorInfo(val.Address)
	if valInfo == nil {
androlo's avatar
androlo committed
		PanicSanity("Couldn't find validatorInfo for release")
Casey Kuhlman's avatar
Casey Kuhlman committed
	}
	valInfo.DestroyedHeight = s.LastBlockHeight + 1
	valInfo.DestroyedAmount = val.VotingPower
	s.SetValidatorInfo(valInfo)

	// Remove validator
	_, removed := s.BondedValidators.Remove(val.Address)
	if !removed {
		_, removed := s.UnbondingValidators.Remove(val.Address)
		if !removed {
androlo's avatar
androlo committed
			PanicCrisis("Couldn't remove validator for destruction")
// Set the validator infos tree
func (s *State) SetValidatorInfos(validatorInfos merkle.Tree) {
	s.validatorInfos = validatorInfos
}

Casey Kuhlman's avatar
Casey Kuhlman committed
// State.validators
//-------------------------------------
// State.storage

func (s *State) LoadStorage(hash []byte) (storage merkle.Tree) {
Ethan Buchman's avatar
Ethan Buchman committed
	storage = merkle.NewIAVLTree(1024, s.DB)
Casey Kuhlman's avatar
Casey Kuhlman committed
	storage.Load(hash)
	return storage
}

// State.storage
//-------------------------------------
// State.nameReg

func (s *State) GetNameRegEntry(name string) *txs.NameRegEntry {
Ethan Buchman's avatar
Ethan Buchman committed
	_, valueBytes, _ := s.nameReg.Get([]byte(name))
	if valueBytes == nil {
Casey Kuhlman's avatar
Casey Kuhlman committed
		return nil
	}
Ethan Buchman's avatar
Ethan Buchman committed
	return DecodeNameRegEntry(valueBytes)
Ethan Buchman's avatar
Ethan Buchman committed
func DecodeNameRegEntry(entryBytes []byte) *txs.NameRegEntry {
	var n int
	var err error
	value := NameRegCodec.Decode(bytes.NewBuffer(entryBytes), &n, &err)
	return value.(*txs.NameRegEntry)
}

func (s *State) UpdateNameRegEntry(entry *txs.NameRegEntry) bool {
Ethan Buchman's avatar
Ethan Buchman committed
	w := new(bytes.Buffer)
	var n int
	var err error
	NameRegCodec.Encode(entry, w, &n, &err)
	return s.nameReg.Set([]byte(entry.Name), w.Bytes())
Casey Kuhlman's avatar
Casey Kuhlman committed
}

func (s *State) RemoveNameRegEntry(name string) bool {
Ethan Buchman's avatar
Ethan Buchman committed
	_, removed := s.nameReg.Remove([]byte(name))
Casey Kuhlman's avatar
Casey Kuhlman committed
	return removed
}

func (s *State) GetNames() merkle.Tree {
	return s.nameReg.Copy()
}

// Set the name reg tree
func (s *State) SetNameReg(nameReg merkle.Tree) {
	s.nameReg = nameReg
}

func NameRegEncoder(o interface{}, w io.Writer, n *int, err *error) {
	wire.WriteBinary(o.(*txs.NameRegEntry), w, n, err)
func NameRegDecoder(r io.Reader, n *int, err *error) interface{} {
	return wire.ReadBinary(&txs.NameRegEntry{}, r, txs.MaxDataLength, n, err)
androlo's avatar
androlo committed
var NameRegCodec = wire.Codec{
Casey Kuhlman's avatar
Casey Kuhlman committed
	Encode: NameRegEncoder,
	Decode: NameRegDecoder,
}

// State.nameReg
//-------------------------------------

// Implements events.Eventable. Typically uses events.EventCache
func (s *State) SetFireable(evc events.Fireable) {
	s.evc = evc
}

//-----------------------------------------------------------------------------
Ethan Buchman's avatar
Ethan Buchman committed
// Genesis
Ethan Buchman's avatar
Ethan Buchman committed
func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*GenesisDoc, *State) {
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
	}
	genDoc := GenesisDocFromJSON(jsonBlob)
	return genDoc, MakeGenesisState(db, genDoc)
Ethan Buchman's avatar
Ethan Buchman committed
func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
	if len(genDoc.Validators) == 0 {
		Exit(Fmt("The genesis file has no validators"))
	}

	if genDoc.GenesisTime.IsZero() {
		genDoc.GenesisTime = time.Now()
	}

	// Make accounts state tree
Ethan Buchman's avatar
Ethan Buchman committed
	accounts := merkle.NewIAVLTree(defaultAccountsCacheCapacity, db)
Ethan Buchman's avatar
Ethan Buchman committed
	for _, genAcc := range genDoc.Accounts {
		perm := ptypes.ZeroAccountPermissions
		if genAcc.Permissions != nil {
			perm = *genAcc.Permissions
		}
		acc := &acm.Account{
			Address:     genAcc.Address,
			PubKey:      nil,
			Sequence:    0,
			Balance:     genAcc.Amount,
			Permissions: perm,
		}
Ethan Buchman's avatar
Ethan Buchman committed
		accounts.Set(acc.Address, acm.EncodeAccount(acc))
Ethan Buchman's avatar
Ethan Buchman committed
	}

	// global permissions are saved as the 0 address
	// so they are included in the accounts tree
	globalPerms := ptypes.DefaultAccountPermissions
	if genDoc.Params != nil && genDoc.Params.GlobalPermissions != nil {
		globalPerms = *genDoc.Params.GlobalPermissions
		// XXX: make sure the set bits are all true
		// Without it the HasPermission() functions will fail
		globalPerms.Base.SetBit = ptypes.AllPermFlags
	}

	permsAcc := &acm.Account{
		Address:     ptypes.GlobalPermissionsAddress,
		PubKey:      nil,
		Sequence:    0,
		Balance:     1337,
		Permissions: globalPerms,
	}
Ethan Buchman's avatar
Ethan Buchman committed
	accounts.Set(permsAcc.Address, acm.EncodeAccount(permsAcc))
Ethan Buchman's avatar
Ethan Buchman committed

	// Make validatorInfos state tree && validators slice
	/*
		validatorInfos := merkle.NewIAVLTree(wire.BasicCodec, types.ValidatorInfoCodec, 0, db)
		validators := make([]*types.Validator, len(genDoc.Validators))
		for i, val := range genDoc.Validators {
			pubKey := val.PubKey
			address := pubKey.Address()

			// Make ValidatorInfo
			valInfo := &types.ValidatorInfo{
				Address:         address,
				PubKey:          pubKey,
				UnbondTo:        make([]*types.TxOutput, len(val.UnbondTo)),
				FirstBondHeight: 0,
				FirstBondAmount: val.Amount,
			for i, unbondTo := range val.UnbondTo {
				valInfo.UnbondTo[i] = &types.TxOutput{
					Address: unbondTo.Address,
					Amount:  unbondTo.Amount,
				}
			}
			validatorInfos.Set(address, valInfo)
			// Make validator
			validators[i] = &types.Validator{
				Address:     address,
				PubKey:      pubKey,
				VotingPower: val.Amount,
			}
Ethan Buchman's avatar
Ethan Buchman committed

	// Make namereg tree
Ethan Buchman's avatar
Ethan Buchman committed
	nameReg := merkle.NewIAVLTree(0, db)
Ethan Buchman's avatar
Ethan Buchman committed
	// TODO: add names, contracts to genesis.json

	// IAVLTrees must be persisted before copy operations.
	accounts.Save()
	//validatorInfos.Save()
Ethan Buchman's avatar
Ethan Buchman committed
	nameReg.Save()

	return &State{
		DB:              db,
		ChainID:         genDoc.ChainID,
		LastBlockHeight: 0,
		LastBlockHash:   nil,
		LastBlockParts:  types.PartSetHeader{},
		LastBlockTime:   genDoc.GenesisTime,
		//BondedValidators:     types.NewValidatorSet(validators),
		//LastBondedValidators: types.NewValidatorSet(nil),
		//UnbondingValidators:  types.NewValidatorSet(nil),
		accounts: accounts,
		//validatorInfos:       validatorInfos,
		nameReg: nameReg,