diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 4543f22ac..f5fbffee1 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -236,6 +236,7 @@ FOR_LOOP: } else { bcR.pool.PopRequest() // TODO: use ApplyBlock instead of Exec/Commit/SetAppHash/Save + // TODO: should we be firing events? need to fire NewBlock events manually ... err := bcR.state.ExecBlock(bcR.evsw, bcR.proxyAppConn, first, firstPartsHeader) if err != nil { // TODO This is bad, are we zombie? diff --git a/blockchain/store.go b/blockchain/store.go index 565e131a7..3b4226429 100644 --- a/blockchain/store.go +++ b/blockchain/store.go @@ -163,6 +163,7 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes) // Save seen commit (seen +2/3 precommits for block) + // NOTE: we can delete this at a later height seenCommitBytes := wire.BinaryBytes(seenCommit) bs.db.Set(calcSeenCommitKey(height), seenCommitBytes) diff --git a/consensus/state.go b/consensus/state.go index 78a9b4f52..185ad658e 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1263,6 +1263,8 @@ func (cs *ConsensusState) finalizeCommit(height int) { // Save to blockStore. if cs.blockStore.Height() < block.Height { + // NOTE: the seenCommit is local justification to commit this block, + // but may differ from the LastCommit included in the next block precommits := cs.Votes.Precommits(cs.CommitRound) seenCommit := precommits.MakeCommit() cs.blockStore.SaveBlock(block, blockParts, seenCommit) diff --git a/state/execution.go b/state/execution.go index 2b8461bed..bc02174a4 100644 --- a/state/execution.go +++ b/state/execution.go @@ -8,6 +8,7 @@ import ( . "github.com/tendermint/go-common" cfg "github.com/tendermint/go-config" + "github.com/tendermint/go-crypto" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" tmsp "github.com/tendermint/tmsp/types" @@ -21,28 +22,32 @@ import ( func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnConsensus, block *types.Block, blockPartsHeader types.PartSetHeader) error { // Validate the block. - err := s.validateBlock(block) - if err != nil { + if err := s.validateBlock(block); err != nil { return ErrInvalidBlock(err) } - // Update the validator set - valSet := s.Validators.Copy() - // Update valSet with signatures from block. + // compute bitarray of validators that signed signed := commitBitArrayFromBlock(block) - _ = signed // TODO + _ = signed // TODO send on begin block - // TODO: Update the validator set (e.g. block.Data.ValidatorUpdates?) + // copy the valset + valSet := s.Validators.Copy() nextValSet := valSet.Copy() // Execute the block txs - err = s.execBlockOnProxyApp(eventCache, proxyAppConn, block) + changedValidators, err := execBlockOnProxyApp(eventCache, proxyAppConn, block) if err != nil { // There was some error in proxyApp // TODO Report error and wait for proxyApp to be available. return ErrProxyAppConn(err) } + // update the validator set + if err := updateValidators(nextValSet, changedValidators); err != nil { + log.Warn("Error changing validator set", "error", err) + // TODO: err or carry on? + } + // All good! // Update validator accums and set state variables nextValSet.IncrementAccum(1) @@ -56,8 +61,9 @@ func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnC } // Executes block's transactions on proxyAppConn. +// Returns a list of updates to the validator set // TODO: Generate a bitmap or otherwise store tx validity in state. -func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnConsensus, block *types.Block) error { +func execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnConsensus, block *types.Block) ([]*tmsp.Validator, error) { var validTxs, invalidTxs = 0, 0 @@ -96,7 +102,7 @@ func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn prox err := proxyAppConn.BeginBlockSync(block.Hash(), types.TM2PB.Header(block.Header)) if err != nil { log.Warn("Error in proxyAppConn.BeginBlock", "error", err) - return err + return nil, err } fail.Fail() // XXX @@ -106,7 +112,7 @@ func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn prox fail.FailRand(len(block.Txs)) // XXX proxyAppConn.AppendTxAsync(tx) if err := proxyAppConn.Error(); err != nil { - return err + return nil, err } } @@ -116,15 +122,53 @@ func (s *State) execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn prox changedValidators, err := proxyAppConn.EndBlockSync(uint64(block.Height)) if err != nil { log.Warn("Error in proxyAppConn.EndBlock", "error", err) - return err + return nil, err } fail.Fail() // XXX - // TODO: Do something with changedValidators - log.Debug("TODO: Do something with changedValidators", "changedValidators", changedValidators) - log.Info(Fmt("ExecBlock got %v valid txs and %v invalid txs", validTxs, invalidTxs)) + return changedValidators, nil +} + +func updateValidators(validators *types.ValidatorSet, changedValidators []*tmsp.Validator) error { + // TODO: prevent change of 1/3+ at once + + for _, v := range changedValidators { + pubkey, err := crypto.PubKeyFromBytes(v.PubKey) // NOTE: expects go-wire encoded pubkey + if err != nil { + return err + } + + address := pubkey.Address() + power := int64(v.Power) + // mind the overflow from uint64 + if power < 0 { + return errors.New(Fmt("Power (%d) overflows int64", v.Power)) + } + + _, val := validators.GetByAddress(address) + if val == nil { + // add val + added := validators.Add(types.NewValidator(pubkey, power)) + if !added { + return errors.New(Fmt("Failed to add new validator %X with voting power %d", address, power)) + } + } else if v.Power == 0 { + // remove val + _, removed := validators.Remove(address) + if !removed { + return errors.New(Fmt("Failed to remove validator %X)")) + } + } else { + // update val + val.VotingPower = power + updated := validators.Update(val) + if !updated { + return errors.New(Fmt("Failed to update validator %X with voting power %d", address, power)) + } + } + } return nil } diff --git a/state/state.go b/state/state.go index 4a54dfe6d..969df9ff8 100644 --- a/state/state.go +++ b/state/state.go @@ -34,7 +34,7 @@ type State struct { LastBlockID types.BlockID LastBlockTime time.Time Validators *types.ValidatorSet - LastValidators *types.ValidatorSet + LastValidators *types.ValidatorSet // block.LastCommit validated against this // AppHash is updated after Commit; // it's stale after ExecBlock and before Commit diff --git a/types/block.go b/types/block.go index 80a59f8a3..5b145a19b 100644 --- a/types/block.go +++ b/types/block.go @@ -154,10 +154,10 @@ type Header struct { Time time.Time `json:"time"` NumTxs int `json:"num_txs"` LastBlockID BlockID `json:"last_block_id"` - LastCommitHash []byte `json:"last_commit_hash"` - DataHash []byte `json:"data_hash"` - ValidatorsHash []byte `json:"validators_hash"` - AppHash []byte `json:"app_hash"` // state merkle root of txs from the previous block + LastCommitHash []byte `json:"last_commit_hash"` // commit from validators from the last block + ValidatorsHash []byte `json:"validators_hash"` // validators for the current block + DataHash []byte `json:"data_hash"` // transactions + AppHash []byte `json:"app_hash"` // state after txs from the previous block } // NOTE: hash is nil if required fields are missing. diff --git a/types/validator.go b/types/validator.go index 7adb64dbb..ae297112b 100644 --- a/types/validator.go +++ b/types/validator.go @@ -20,6 +20,15 @@ type Validator struct { Accum int64 `json:"accum"` } +func NewValidator(pubKey crypto.PubKey, votingPower int64) *Validator { + return &Validator{ + Address: pubKey.Address(), + PubKey: pubKey, + VotingPower: votingPower, + Accum: 0, + } +} + // Creates a new copy of the validator so we can mutate accum. // Panics if the validator is nil. func (v *Validator) Copy() *Validator {