Added LastBlockParts to state, and also validates.

This commit is contained in:
Jae Kwon
2014-10-31 21:30:52 -07:00
parent b7b923cc6b
commit da8e25343c
7 changed files with 104 additions and 83 deletions

View File

@@ -43,9 +43,10 @@ func (txErr InvalidTxError) Error() string {
// NOTE: not goroutine-safe.
type State struct {
DB db_.DB
Height uint32 // Last known block height
BlockHash []byte // Last known block hash
BlockTime time.Time // LastKnown block time
LastBlockHeight uint32
LastBlockHash []byte
LastBlockParts PartSetHeader
LastBlockTime time.Time
BondedValidators *ValidatorSet
UnbondingValidators *ValidatorSet
accountDetails merkle.Tree // Shouldn't be accessed directly.
@@ -60,9 +61,10 @@ func LoadState(db db_.DB) *State {
reader := bytes.NewReader(buf)
var n int64
var err error
s.Height = ReadUInt32(reader, &n, &err)
s.BlockHash = ReadByteSlice(reader, &n, &err)
s.BlockTime = ReadTime(reader, &n, &err)
s.LastBlockHeight = ReadUInt32(reader, &n, &err)
s.LastBlockHash = ReadByteSlice(reader, &n, &err)
s.LastBlockParts = ReadPartSetHeader(reader, &n, &err)
s.LastBlockTime = ReadTime(reader, &n, &err)
s.BondedValidators = ReadValidatorSet(reader, &n, &err)
s.UnbondingValidators = ReadValidatorSet(reader, &n, &err)
accountDetailsHash := ReadByteSlice(reader, &n, &err)
@@ -82,9 +84,10 @@ func (s *State) Save() {
var buf bytes.Buffer
var n int64
var err error
WriteUInt32(&buf, s.Height, &n, &err)
WriteByteSlice(&buf, s.BlockHash, &n, &err)
WriteTime(&buf, s.BlockTime, &n, &err)
WriteUInt32(&buf, s.LastBlockHeight, &n, &err)
WriteByteSlice(&buf, s.LastBlockHash, &n, &err)
WriteBinary(&buf, s.LastBlockParts, &n, &err)
WriteTime(&buf, s.LastBlockTime, &n, &err)
WriteBinary(&buf, s.BondedValidators, &n, &err)
WriteBinary(&buf, s.UnbondingValidators, &n, &err)
WriteByteSlice(&buf, s.accountDetails.Hash(), &n, &err)
@@ -97,9 +100,10 @@ func (s *State) Save() {
func (s *State) Copy() *State {
return &State{
DB: s.DB,
Height: s.Height,
BlockHash: s.BlockHash,
BlockTime: s.BlockTime,
LastBlockHeight: s.LastBlockHeight,
LastBlockHash: s.LastBlockHash,
LastBlockParts: s.LastBlockParts,
LastBlockTime: s.LastBlockTime,
BondedValidators: s.BondedValidators.Copy(),
UnbondingValidators: s.UnbondingValidators.Copy(),
accountDetails: s.accountDetails.Copy(),
@@ -172,7 +176,7 @@ func (s *State) ExecTx(tx Tx) error {
s.SetAccountDetail(accDet)
added := s.BondedValidators.Add(&Validator{
Account: accDet.Account,
BondHeight: s.Height,
BondHeight: s.LastBlockHeight,
VotingPower: accDet.Balance,
Accum: 0,
})
@@ -260,7 +264,7 @@ func (s *State) unbondValidator(accountId uint64, accDet *AccountDetail) {
if !removed {
panic("Failed to remove validator")
}
val.UnbondHeight = s.Height
val.UnbondHeight = s.LastBlockHeight
added := s.UnbondingValidators.Add(val)
if !added {
panic("Failed to add validator")
@@ -286,26 +290,25 @@ func (s *State) releaseValidator(accountId uint64) {
// (used for constructing a new proposal)
// NOTE: If an error occurs during block execution, state will be left
// at an invalid state. Copy the state before calling AppendBlock!
func (s *State) AppendBlock(b *Block, checkStateHash bool) error {
func (s *State) AppendBlock(block *Block, blockPartsHeader PartSetHeader, checkStateHash bool) error {
// Basic block validation.
// XXX We need to validate LastBlockParts too.
err := b.ValidateBasic(s.Height, s.BlockHash)
err := block.ValidateBasic(s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime)
if err != nil {
return err
}
// Validate block Validation.
if b.Height == 1 {
if len(b.Validation.Commits) != 0 {
if block.Height == 1 {
if len(block.Validation.Commits) != 0 {
return errors.New("Block at height 1 (first block) should have no Validation commits")
}
} else {
if uint(len(b.Validation.Commits)) != s.BondedValidators.Size() {
if uint(len(block.Validation.Commits)) != s.BondedValidators.Size() {
return errors.New("Invalid block validation size")
}
var sumVotingPower uint64
s.BondedValidators.Iterate(func(index uint, val *Validator) bool {
rsig := b.Validation.Commits[index]
rsig := block.Validation.Commits[index]
if rsig.IsZero() {
return false
} else {
@@ -314,11 +317,11 @@ func (s *State) AppendBlock(b *Block, checkStateHash bool) error {
return true
}
vote := &Vote{
Height: b.Height,
Height: block.Height,
Round: rsig.Round,
Type: VoteTypeCommit,
BlockHash: b.LastBlockHash,
BlockParts: b.LastBlockParts,
BlockHash: block.LastBlockHash,
BlockParts: block.LastBlockParts,
Signature: rsig.Signature,
}
if val.Verify(vote) {
@@ -339,7 +342,7 @@ func (s *State) AppendBlock(b *Block, checkStateHash bool) error {
}
// Commit each tx
for _, tx := range b.Data.Txs {
for _, tx := range block.Data.Txs {
err := s.ExecTx(tx)
if err != nil {
return InvalidTxError{tx, err}
@@ -347,12 +350,12 @@ func (s *State) AppendBlock(b *Block, checkStateHash bool) error {
}
// Update Validator.LastCommitHeight as necessary.
for _, rsig := range b.Validation.Commits {
for _, rsig := range block.Validation.Commits {
_, val := s.BondedValidators.GetById(rsig.SignerId)
if val == nil {
return ErrStateInvalidSignature
}
val.LastCommitHeight = b.Height
val.LastCommitHeight = block.Height
updated := s.BondedValidators.Update(val)
if !updated {
panic("Failed to update validator LastCommitHeight")
@@ -363,7 +366,7 @@ func (s *State) AppendBlock(b *Block, checkStateHash bool) error {
// reward account with bonded coins.
toRelease := []*Validator{}
s.UnbondingValidators.Iterate(func(index uint, val *Validator) bool {
if val.UnbondHeight+unbondingPeriodBlocks < b.Height {
if val.UnbondHeight+unbondingPeriodBlocks < block.Height {
toRelease = append(toRelease, val)
}
return false
@@ -376,7 +379,7 @@ func (s *State) AppendBlock(b *Block, checkStateHash bool) error {
// unbond them, they have timed out.
toTimeout := []*Validator{}
s.BondedValidators.Iterate(func(index uint, val *Validator) bool {
if val.LastCommitHeight+validatorTimeoutBlocks < b.Height {
if val.LastCommitHeight+validatorTimeoutBlocks < block.Height {
toTimeout = append(toTimeout, val)
}
return false
@@ -392,21 +395,22 @@ func (s *State) AppendBlock(b *Block, checkStateHash bool) error {
stateHash := s.Hash()
if checkStateHash {
// State hash should match
if !bytes.Equal(stateHash, b.StateHash) {
if !bytes.Equal(stateHash, block.StateHash) {
return Errorf("Invalid state hash. Got %X, block says %X",
stateHash, b.StateHash)
stateHash, block.StateHash)
}
} else {
// Set the state hash.
if b.StateHash != nil {
if block.StateHash != nil {
panic("Cannot overwrite block.StateHash")
}
b.StateHash = stateHash
block.StateHash = stateHash
}
s.Height = b.Height
s.BlockHash = b.Hash()
s.BlockTime = b.Time
s.LastBlockHeight = block.Height
s.LastBlockHash = block.Hash()
s.LastBlockParts = blockPartsHeader
s.LastBlockTime = block.Time
return nil
}
@@ -428,7 +432,7 @@ func (s *State) SetAccountDetail(accDet *AccountDetail) (updated bool) {
}
// Returns a hash that represents the state data,
// excluding Height, BlockHash.
// excluding LastBlock*
func (s *State) Hash() []byte {
hashables := []merkle.Hashable{
s.BondedValidators,