|
@ -3,39 +3,52 @@ package state |
|
|
import ( |
|
|
import ( |
|
|
"bytes" |
|
|
"bytes" |
|
|
"errors" |
|
|
"errors" |
|
|
|
|
|
"fmt" |
|
|
"time" |
|
|
"time" |
|
|
|
|
|
|
|
|
. "github.com/tendermint/tendermint/binary" |
|
|
. "github.com/tendermint/tendermint/binary" |
|
|
. "github.com/tendermint/tendermint/blocks" |
|
|
. "github.com/tendermint/tendermint/blocks" |
|
|
|
|
|
. "github.com/tendermint/tendermint/common" |
|
|
. "github.com/tendermint/tendermint/db" |
|
|
. "github.com/tendermint/tendermint/db" |
|
|
"github.com/tendermint/tendermint/merkle" |
|
|
"github.com/tendermint/tendermint/merkle" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
var ( |
|
|
var ( |
|
|
ErrStateInvalidAccountId = errors.New("Error State invalid account id") |
|
|
|
|
|
ErrStateInvalidSignature = errors.New("Error State invalid signature") |
|
|
|
|
|
ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number") |
|
|
|
|
|
ErrStateInvalidAccountState = errors.New("Error State invalid account state") |
|
|
|
|
|
ErrStateInvalidValidationStateHash = errors.New("Error State invalid ValidationStateHash") |
|
|
|
|
|
ErrStateInvalidAccountStateHash = errors.New("Error State invalid AccountStateHash") |
|
|
|
|
|
ErrStateInsufficientFunds = errors.New("Error State insufficient funds") |
|
|
|
|
|
|
|
|
ErrStateInvalidAccountId = errors.New("Error State invalid account id") |
|
|
|
|
|
ErrStateInvalidSignature = errors.New("Error State invalid signature") |
|
|
|
|
|
ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number") |
|
|
|
|
|
ErrStateInvalidAccountState = errors.New("Error State invalid account state") |
|
|
|
|
|
ErrStateInsufficientFunds = errors.New("Error State insufficient funds") |
|
|
|
|
|
|
|
|
stateKey = []byte("stateKey") |
|
|
stateKey = []byte("stateKey") |
|
|
minBondAmount = uint64(1) // TODO adjust
|
|
|
|
|
|
defaultAccountDetailsCacheCapacity = 1000 // TODO adjust
|
|
|
|
|
|
|
|
|
minBondAmount = uint64(1) // TODO adjust
|
|
|
|
|
|
defaultAccountDetailsCacheCapacity = 1000 // TODO adjust
|
|
|
|
|
|
unbondingPeriodBlocks = uint32(60 * 24 * 365) // TODO probably better to make it time based.
|
|
|
|
|
|
validatorTimeoutBlocks = uint32(10) // TODO adjust
|
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
type InvalidTxError struct { |
|
|
|
|
|
Tx Tx |
|
|
|
|
|
Reason error |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (txErr InvalidTxError) Error() string { |
|
|
|
|
|
return fmt.Sprintf("Invalid tx: [%v] reason: [%v]", txErr.Tx, txErr.Reason) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
// NOTE: not goroutine-safe.
|
|
|
// NOTE: not goroutine-safe.
|
|
|
type State struct { |
|
|
type State struct { |
|
|
DB DB |
|
|
|
|
|
Height uint32 // Last known block height
|
|
|
|
|
|
BlockHash []byte // Last known block hash
|
|
|
|
|
|
CommitTime time.Time |
|
|
|
|
|
AccountDetails merkle.Tree |
|
|
|
|
|
BondedValidators *ValidatorSet |
|
|
|
|
|
UnbondedValidators *ValidatorSet |
|
|
|
|
|
|
|
|
DB DB |
|
|
|
|
|
Height uint32 // Last known block height
|
|
|
|
|
|
BlockHash []byte // Last known block hash
|
|
|
|
|
|
CommitTime time.Time |
|
|
|
|
|
AccountDetails merkle.Tree |
|
|
|
|
|
BondedValidators *ValidatorSet |
|
|
|
|
|
UnbondingValidators *ValidatorSet |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func GenesisState(db DB, genesisTime time.Time, accDets []*AccountDetail) *State { |
|
|
func GenesisState(db DB, genesisTime time.Time, accDets []*AccountDetail) *State { |
|
@ -46,7 +59,7 @@ func GenesisState(db DB, genesisTime time.Time, accDets []*AccountDetail) *State |
|
|
|
|
|
|
|
|
for _, accDet := range accDets { |
|
|
for _, accDet := range accDets { |
|
|
accountDetails.Set(accDet.Id, accDet) |
|
|
accountDetails.Set(accDet.Id, accDet) |
|
|
if accDet.Status == AccountDetailStatusBonded { |
|
|
|
|
|
|
|
|
if accDet.Status == AccountStatusBonded { |
|
|
validators = append(validators, &Validator{ |
|
|
validators = append(validators, &Validator{ |
|
|
Account: accDet.Account, |
|
|
Account: accDet.Account, |
|
|
BondHeight: 0, |
|
|
BondHeight: 0, |
|
@ -61,13 +74,13 @@ func GenesisState(db DB, genesisTime time.Time, accDets []*AccountDetail) *State |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return &State{ |
|
|
return &State{ |
|
|
DB: db, |
|
|
|
|
|
Height: 0, |
|
|
|
|
|
BlockHash: nil, |
|
|
|
|
|
CommitTime: genesisTime, |
|
|
|
|
|
AccountDetails: accountDetails, |
|
|
|
|
|
BondedValidators: NewValidatorSet(validators), |
|
|
|
|
|
UnbondedValidators: NewValidatorSet(nil), |
|
|
|
|
|
|
|
|
DB: db, |
|
|
|
|
|
Height: 0, |
|
|
|
|
|
BlockHash: nil, |
|
|
|
|
|
CommitTime: genesisTime, |
|
|
|
|
|
AccountDetails: accountDetails, |
|
|
|
|
|
BondedValidators: NewValidatorSet(validators), |
|
|
|
|
|
UnbondingValidators: NewValidatorSet(nil), |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -87,7 +100,7 @@ func LoadState(db DB) *State { |
|
|
s.AccountDetails = merkle.NewIAVLTree(BasicCodec, AccountDetailCodec, defaultAccountDetailsCacheCapacity, db) |
|
|
s.AccountDetails = merkle.NewIAVLTree(BasicCodec, AccountDetailCodec, defaultAccountDetailsCacheCapacity, db) |
|
|
s.AccountDetails.Load(accountDetailsHash) |
|
|
s.AccountDetails.Load(accountDetailsHash) |
|
|
s.BondedValidators = ReadValidatorSet(reader, &n, &err) |
|
|
s.BondedValidators = ReadValidatorSet(reader, &n, &err) |
|
|
s.UnbondedValidators = ReadValidatorSet(reader, &n, &err) |
|
|
|
|
|
|
|
|
s.UnbondingValidators = ReadValidatorSet(reader, &n, &err) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
panic(err) |
|
|
panic(err) |
|
|
} |
|
|
} |
|
@ -110,7 +123,7 @@ func (s *State) Save(commitTime time.Time) { |
|
|
WriteByteSlice(&buf, s.BlockHash, &n, &err) |
|
|
WriteByteSlice(&buf, s.BlockHash, &n, &err) |
|
|
WriteByteSlice(&buf, s.AccountDetails.Hash(), &n, &err) |
|
|
WriteByteSlice(&buf, s.AccountDetails.Hash(), &n, &err) |
|
|
WriteBinary(&buf, s.BondedValidators, &n, &err) |
|
|
WriteBinary(&buf, s.BondedValidators, &n, &err) |
|
|
WriteBinary(&buf, s.UnbondedValidators, &n, &err) |
|
|
|
|
|
|
|
|
WriteBinary(&buf, s.UnbondingValidators, &n, &err) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
panic(err) |
|
|
panic(err) |
|
|
} |
|
|
} |
|
@ -119,13 +132,13 @@ func (s *State) Save(commitTime time.Time) { |
|
|
|
|
|
|
|
|
func (s *State) Copy() *State { |
|
|
func (s *State) Copy() *State { |
|
|
return &State{ |
|
|
return &State{ |
|
|
DB: s.DB, |
|
|
|
|
|
Height: s.Height, |
|
|
|
|
|
CommitTime: s.CommitTime, |
|
|
|
|
|
BlockHash: s.BlockHash, |
|
|
|
|
|
AccountDetails: s.AccountDetails.Copy(), |
|
|
|
|
|
BondedValidators: s.BondedValidators.Copy(), |
|
|
|
|
|
UnbondedValidators: s.UnbondedValidators.Copy(), |
|
|
|
|
|
|
|
|
DB: s.DB, |
|
|
|
|
|
Height: s.Height, |
|
|
|
|
|
CommitTime: s.CommitTime, |
|
|
|
|
|
BlockHash: s.BlockHash, |
|
|
|
|
|
AccountDetails: s.AccountDetails.Copy(), |
|
|
|
|
|
BondedValidators: s.BondedValidators.Copy(), |
|
|
|
|
|
UnbondingValidators: s.UnbondingValidators.Copy(), |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -144,20 +157,26 @@ func (s *State) ExecTx(tx Tx) error { |
|
|
if tx.GetSequence() <= accDet.Sequence { |
|
|
if tx.GetSequence() <= accDet.Sequence { |
|
|
return ErrStateInvalidSequenceNumber |
|
|
return ErrStateInvalidSequenceNumber |
|
|
} |
|
|
} |
|
|
|
|
|
// Subtract fee from balance.
|
|
|
|
|
|
if accDet.Balance < tx.GetFee() { |
|
|
|
|
|
return ErrStateInsufficientFunds |
|
|
|
|
|
} else { |
|
|
|
|
|
accDet.Balance -= tx.GetFee() |
|
|
|
|
|
} |
|
|
// Exec tx
|
|
|
// Exec tx
|
|
|
switch tx.(type) { |
|
|
switch tx.(type) { |
|
|
case *SendTx: |
|
|
case *SendTx: |
|
|
stx := tx.(*SendTx) |
|
|
stx := tx.(*SendTx) |
|
|
toAccDet := s.GetAccountDetail(stx.To) |
|
|
toAccDet := s.GetAccountDetail(stx.To) |
|
|
// Accounts must be nominal
|
|
|
// Accounts must be nominal
|
|
|
if accDet.Status != AccountDetailStatusNominal { |
|
|
|
|
|
|
|
|
if accDet.Status != AccountStatusNominal { |
|
|
return ErrStateInvalidAccountState |
|
|
return ErrStateInvalidAccountState |
|
|
} |
|
|
} |
|
|
if toAccDet.Status != AccountDetailStatusNominal { |
|
|
|
|
|
|
|
|
if toAccDet.Status != AccountStatusNominal { |
|
|
return ErrStateInvalidAccountState |
|
|
return ErrStateInvalidAccountState |
|
|
} |
|
|
} |
|
|
// Check account balance
|
|
|
// Check account balance
|
|
|
if accDet.Balance < stx.Fee+stx.Amount { |
|
|
|
|
|
|
|
|
if accDet.Balance < stx.Amount { |
|
|
return ErrStateInsufficientFunds |
|
|
return ErrStateInsufficientFunds |
|
|
} |
|
|
} |
|
|
// Check existence of destination account
|
|
|
// Check existence of destination account
|
|
@ -165,27 +184,26 @@ func (s *State) ExecTx(tx Tx) error { |
|
|
return ErrStateInvalidAccountId |
|
|
return ErrStateInvalidAccountId |
|
|
} |
|
|
} |
|
|
// Good!
|
|
|
// Good!
|
|
|
accDet.Balance -= (stx.Fee + stx.Amount) |
|
|
|
|
|
toAccDet.Balance += (stx.Amount) |
|
|
|
|
|
|
|
|
accDet.Balance -= stx.Amount |
|
|
|
|
|
toAccDet.Balance += stx.Amount |
|
|
s.SetAccountDetail(accDet) |
|
|
s.SetAccountDetail(accDet) |
|
|
s.SetAccountDetail(toAccDet) |
|
|
s.SetAccountDetail(toAccDet) |
|
|
|
|
|
return nil |
|
|
//case *NameTx
|
|
|
//case *NameTx
|
|
|
case *BondTx: |
|
|
case *BondTx: |
|
|
btx := tx.(*BondTx) |
|
|
|
|
|
|
|
|
//btx := tx.(*BondTx)
|
|
|
// Account must be nominal
|
|
|
// Account must be nominal
|
|
|
if accDet.Status != AccountDetailStatusNominal { |
|
|
|
|
|
|
|
|
if accDet.Status != AccountStatusNominal { |
|
|
return ErrStateInvalidAccountState |
|
|
return ErrStateInvalidAccountState |
|
|
} |
|
|
} |
|
|
// Check account balance
|
|
|
// Check account balance
|
|
|
if accDet.Balance < minBondAmount { |
|
|
if accDet.Balance < minBondAmount { |
|
|
return ErrStateInsufficientFunds |
|
|
return ErrStateInsufficientFunds |
|
|
} |
|
|
} |
|
|
// TODO: max number of validators?
|
|
|
|
|
|
// Good!
|
|
|
// Good!
|
|
|
accDet.Balance -= btx.Fee // remaining balance are bonded coins.
|
|
|
|
|
|
accDet.Status = AccountDetailStatusBonded |
|
|
|
|
|
|
|
|
accDet.Status = AccountStatusBonded |
|
|
s.SetAccountDetail(accDet) |
|
|
s.SetAccountDetail(accDet) |
|
|
added := s.BondednValidators.Add(&Validator{ |
|
|
|
|
|
|
|
|
added := s.BondedValidators.Add(&Validator{ |
|
|
Account: accDet.Account, |
|
|
Account: accDet.Account, |
|
|
BondHeight: s.Height, |
|
|
BondHeight: s.Height, |
|
|
VotingPower: accDet.Balance, |
|
|
VotingPower: accDet.Balance, |
|
@ -194,29 +212,106 @@ func (s *State) ExecTx(tx Tx) error { |
|
|
if !added { |
|
|
if !added { |
|
|
panic("Failed to add validator") |
|
|
panic("Failed to add validator") |
|
|
} |
|
|
} |
|
|
|
|
|
return nil |
|
|
case *UnbondTx: |
|
|
case *UnbondTx: |
|
|
utx := tx.(*UnbondTx) |
|
|
|
|
|
|
|
|
//utx := tx.(*UnbondTx)
|
|
|
// Account must be bonded.
|
|
|
// Account must be bonded.
|
|
|
if accDet.Status != AccountDetailStatusBonded { |
|
|
|
|
|
|
|
|
if accDet.Status != AccountStatusBonded { |
|
|
return ErrStateInvalidAccountState |
|
|
return ErrStateInvalidAccountState |
|
|
} |
|
|
} |
|
|
// Good!
|
|
|
// Good!
|
|
|
accDet.Status = AccountDetailStatusUnbonding |
|
|
|
|
|
|
|
|
s.unbondValidator(accDet.Id, accDet) |
|
|
s.SetAccountDetail(accDet) |
|
|
s.SetAccountDetail(accDet) |
|
|
val, removed := s.BondedValidators.Remove(accDet.Id) |
|
|
|
|
|
if !removed { |
|
|
|
|
|
panic("Failed to remove validator") |
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
case *DupeoutTx: |
|
|
|
|
|
{ |
|
|
|
|
|
// NOTE: accDet is the one who created this transaction.
|
|
|
|
|
|
// Subtract any fees, save, and forget.
|
|
|
|
|
|
s.SetAccountDetail(accDet) |
|
|
|
|
|
accDet = nil |
|
|
} |
|
|
} |
|
|
val.UnbondHeight = s.Height |
|
|
|
|
|
added := s.UnbondedValidators.Add(val) |
|
|
|
|
|
if !added { |
|
|
|
|
|
panic("Failed to add validator") |
|
|
|
|
|
|
|
|
dtx := tx.(*DupeoutTx) |
|
|
|
|
|
// Verify the signatures
|
|
|
|
|
|
if dtx.VoteA.SignerId != dtx.VoteB.SignerId { |
|
|
|
|
|
return ErrStateInvalidSignature |
|
|
} |
|
|
} |
|
|
case *DupeoutTx: |
|
|
|
|
|
// XXX
|
|
|
|
|
|
|
|
|
accused := s.GetAccountDetail(dtx.VoteA.SignerId) |
|
|
|
|
|
if !accused.Verify(&dtx.VoteA) || !accused.Verify(&dtx.VoteB) { |
|
|
|
|
|
return ErrStateInvalidSignature |
|
|
|
|
|
} |
|
|
|
|
|
// Verify equivocation
|
|
|
|
|
|
if dtx.VoteA.Height != dtx.VoteB.Height { |
|
|
|
|
|
return errors.New("DupeoutTx height must be the same.") |
|
|
|
|
|
} |
|
|
|
|
|
if dtx.VoteA.Type == VoteTypeCommit && dtx.VoteA.Round < dtx.VoteB.Round { |
|
|
|
|
|
// Check special case.
|
|
|
|
|
|
// Validators should not sign another vote after committing.
|
|
|
|
|
|
} else { |
|
|
|
|
|
if dtx.VoteA.Round != dtx.VoteB.Round { |
|
|
|
|
|
return errors.New("DupeoutTx rounds don't match") |
|
|
|
|
|
} |
|
|
|
|
|
if dtx.VoteA.Type != dtx.VoteB.Type { |
|
|
|
|
|
return errors.New("DupeoutTx types don't match") |
|
|
|
|
|
} |
|
|
|
|
|
if bytes.Equal(dtx.VoteA.BlockHash, dtx.VoteB.BlockHash) { |
|
|
|
|
|
return errors.New("DupeoutTx blockhash shouldn't match") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// Good! (Bad validator!)
|
|
|
|
|
|
if accused.Status == AccountStatusBonded { |
|
|
|
|
|
_, removed := s.BondedValidators.Remove(accused.Id) |
|
|
|
|
|
if !removed { |
|
|
|
|
|
panic("Failed to remove accused validator") |
|
|
|
|
|
} |
|
|
|
|
|
} else if accused.Status == AccountStatusUnbonding { |
|
|
|
|
|
_, removed := s.UnbondingValidators.Remove(accused.Id) |
|
|
|
|
|
if !removed { |
|
|
|
|
|
panic("Failed to remove accused validator") |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
panic("Couldn't find accused validator") |
|
|
|
|
|
} |
|
|
|
|
|
accused.Status = AccountStatusDupedOut |
|
|
|
|
|
updated := s.SetAccountDetail(accused) |
|
|
|
|
|
if !updated { |
|
|
|
|
|
panic("Failed to update accused validator account") |
|
|
|
|
|
} |
|
|
|
|
|
return nil |
|
|
|
|
|
default: |
|
|
|
|
|
panic("Unknown Tx type") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// accDet optional
|
|
|
|
|
|
func (s *State) unbondValidator(accountId uint64, accDet *AccountDetail) { |
|
|
|
|
|
if accDet == nil { |
|
|
|
|
|
accDet = s.GetAccountDetail(accountId) |
|
|
|
|
|
} |
|
|
|
|
|
accDet.Status = AccountStatusUnbonding |
|
|
|
|
|
s.SetAccountDetail(accDet) |
|
|
|
|
|
val, removed := s.BondedValidators.Remove(accDet.Id) |
|
|
|
|
|
if !removed { |
|
|
|
|
|
panic("Failed to remove validator") |
|
|
|
|
|
} |
|
|
|
|
|
val.UnbondHeight = s.Height |
|
|
|
|
|
added := s.UnbondingValidators.Add(val) |
|
|
|
|
|
if !added { |
|
|
|
|
|
panic("Failed to add validator") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *State) releaseValidator(accountId uint64) { |
|
|
|
|
|
accDet := s.GetAccountDetail(accountId) |
|
|
|
|
|
if accDet.Status != AccountStatusUnbonding { |
|
|
|
|
|
panic("Cannot release validator") |
|
|
|
|
|
} |
|
|
|
|
|
accDet.Status = AccountStatusNominal |
|
|
|
|
|
// TODO: move balance to designated address, UnbondTo.
|
|
|
|
|
|
s.SetAccountDetail(accDet) |
|
|
|
|
|
_, removed := s.UnbondingValidators.Remove(accountId) |
|
|
|
|
|
if !removed { |
|
|
|
|
|
panic("Couldn't release validator") |
|
|
} |
|
|
} |
|
|
panic("Implement ExecTx()") |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// NOTE: If an error occurs during block execution, state will be left
|
|
|
// NOTE: If an error occurs during block execution, state will be left
|
|
@ -232,25 +327,61 @@ func (s *State) AppendBlock(b *Block) error { |
|
|
for _, tx := range b.Data.Txs { |
|
|
for _, tx := range b.Data.Txs { |
|
|
err := s.ExecTx(tx) |
|
|
err := s.ExecTx(tx) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return err |
|
|
|
|
|
|
|
|
return InvalidTxError{tx, err} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Update LastCommitHeight as necessary.
|
|
|
|
|
|
for _, sig := range b.Validation.Signatures { |
|
|
|
|
|
_, val := s.BondedValidators.GetById(sig.SignerId) |
|
|
|
|
|
if val == nil { |
|
|
|
|
|
return ErrStateInvalidSignature |
|
|
|
|
|
} |
|
|
|
|
|
val.LastCommitHeight = b.Height |
|
|
|
|
|
updated := s.BondedValidators.Update(val) |
|
|
|
|
|
if !updated { |
|
|
|
|
|
panic("Failed to update validator LastCommitHeight") |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// If any unbonding periods are over,
|
|
|
// If any unbonding periods are over,
|
|
|
// reward account with bonded coins.
|
|
|
// reward account with bonded coins.
|
|
|
|
|
|
toRelease := []*Validator{} |
|
|
|
|
|
s.UnbondingValidators.Iterate(func(val *Validator) bool { |
|
|
|
|
|
if val.UnbondHeight+unbondingPeriodBlocks < b.Height { |
|
|
|
|
|
toRelease = append(toRelease, val) |
|
|
|
|
|
} |
|
|
|
|
|
return false |
|
|
|
|
|
}) |
|
|
|
|
|
for _, val := range toRelease { |
|
|
|
|
|
s.releaseValidator(val.Id) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// If any validators haven't signed in a while,
|
|
|
// If any validators haven't signed in a while,
|
|
|
// unbond them, they have timed out.
|
|
|
// unbond them, they have timed out.
|
|
|
|
|
|
toTimeout := []*Validator{} |
|
|
|
|
|
s.BondedValidators.Iterate(func(val *Validator) bool { |
|
|
|
|
|
if val.LastCommitHeight+validatorTimeoutBlocks < b.Height { |
|
|
|
|
|
toTimeout = append(toTimeout, val) |
|
|
|
|
|
} |
|
|
|
|
|
return false |
|
|
|
|
|
}) |
|
|
|
|
|
for _, val := range toTimeout { |
|
|
|
|
|
s.unbondValidator(val.Id, nil) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Increment validator AccumPowers
|
|
|
// Increment validator AccumPowers
|
|
|
s.BondedValidators.IncrementAccum() |
|
|
s.BondedValidators.IncrementAccum() |
|
|
|
|
|
|
|
|
// State hashes should match
|
|
|
// State hashes should match
|
|
|
|
|
|
// XXX include UnbondingValidators.Hash().
|
|
|
if !bytes.Equal(s.BondedValidators.Hash(), b.ValidationStateHash) { |
|
|
if !bytes.Equal(s.BondedValidators.Hash(), b.ValidationStateHash) { |
|
|
return ErrStateInvalidValidationStateHash |
|
|
|
|
|
|
|
|
return Errorf("Invalid ValidationStateHash. Got %X, block says %X", |
|
|
|
|
|
s.BondedValidators.Hash(), b.ValidationStateHash) |
|
|
} |
|
|
} |
|
|
if !bytes.Equal(s.AccountDetails.Hash(), b.AccountStateHash) { |
|
|
if !bytes.Equal(s.AccountDetails.Hash(), b.AccountStateHash) { |
|
|
return ErrStateInvalidAccountStateHash |
|
|
|
|
|
|
|
|
return Errorf("Invalid AccountStateHash. Got %X, block says %X", |
|
|
|
|
|
s.AccountDetails.Hash(), b.AccountStateHash) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
s.Height = b.Height |
|
|
s.Height = b.Height |
|
|