func (cs *ConsensusState) stageBlock(block *types.Block, blockParts *types.PartSet) error { if block == nil { PanicSanity("Cannot stage nil block") } // Already staged? blockHash := block.Hash() if cs.stagedBlock != nil && len(blockHash) != 0 && bytes.Equal(cs.stagedBlock.Hash(), blockHash) { return nil } // Create a copy of the state for staging stateCopy := cs.state.Copy() // reset the event cache and pass it into the state cs.evc = events.NewEventCache(cs.evsw) stateCopy.SetFireable(cs.evc) // Commit block onto the copied state. // NOTE: Basic validation is done in state.AppendBlock(). err := sm.ExecBlock(stateCopy, block, blockParts.Header()) if err != nil { return err } else { cs.stagedBlock = block cs.stagedState = stateCopy return nil } }
// Execute the block to mutate State. // Validates block and then executes Data.Txs in the block. func (s *State) ExecBlock(evsw *events.EventSwitch, proxyAppConn proxy.AppConn, block *types.Block, blockPartsHeader types.PartSetHeader) error { // Validate the block. err := s.validateBlock(block) if err != nil { return err } // Update the validator set valSet := s.Validators.Copy() // Update valSet with signatures from block. updateValidatorsWithBlock(s.LastValidators, valSet, block) // TODO: Update the validator set (e.g. block.Data.ValidatorUpdates?) nextValSet := valSet.Copy() // Execute the block txs err = s.execBlockOnProxyApp(evsw, proxyAppConn, block) if err != nil { // There was some error in proxyApp // TODO Report error and wait for proxyApp to be available. return err } // All good! nextValSet.IncrementAccum(1) s.LastBlockHeight = block.Height s.LastBlockHash = block.Hash() s.LastBlockParts = blockPartsHeader s.LastBlockTime = block.Time s.Validators = nextValSet s.LastValidators = valSet return nil }
func (s *State) validateBlock(block *types.Block) error { // Basic block validation. err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime, s.AppHash) if err != nil { return err } // Validate block LastValidation. if block.Height == 1 { if len(block.LastValidation.Precommits) != 0 { return errors.New("Block at height 1 (first block) should have no LastValidation precommits") } } else { if len(block.LastValidation.Precommits) != s.LastValidators.Size() { return fmt.Errorf("Invalid block validation size. Expected %v, got %v", s.LastValidators.Size(), len(block.LastValidation.Precommits)) } err := s.LastValidators.VerifyValidation( s.ChainID, s.LastBlockHash, s.LastBlockParts, block.Height-1, block.LastValidation) if err != nil { return err } } return nil }
func (cs *ConsensusState) stageBlock(block *types.Block, blockParts *types.PartSet) error { if block == nil { PanicSanity("Cannot stage nil block") } // Already staged? blockHash := block.Hash() if cs.stagedBlock != nil && len(blockHash) != 0 && bytes.Equal(cs.stagedBlock.Hash(), blockHash) { return nil } // Create a new event cache to cache all events. cs.evc = events.NewEventCache(cs.evsw) // Create a copy of the state for staging stateCopy := cs.state.Copy() stateCopy.SetEventCache(cs.evc) // Run the block on the State: // + update validator sets // + first rolls back proxyAppCtx // + run txs on the proxyAppCtx or rollback err := stateCopy.ExecBlock(cs.proxyAppCtx, block, blockParts.Header()) if err != nil { return err } // Everything looks good! cs.stagedBlock = block cs.stagedState = stateCopy return nil }
// Mutates the block in place and updates it with new state hash. func (s *State) SetBlockStateHash(block *types.Block) error { 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 }
// Execute the block to mutate State. // Also, execute txs on the proxyAppCtx and validate apphash // Rolls back before executing transactions. // Rolls back if invalid, but never commits. func (s *State) ExecBlock(proxyAppCtx proxy.AppContext, block *types.Block, blockPartsHeader types.PartSetHeader) error { // Validate the block. err := s.validateBlock(block) if err != nil { return err } // Update the validator set valSet := s.Validators.Copy() // Update valSet with signatures from block. updateValidatorsWithBlock(s.LastValidators, valSet, block) // TODO: Update the validator set (e.g. block.Data.ValidatorUpdates?) nextValSet := valSet.Copy() // First, rollback. proxyAppCtx.RollbackSync() // Execute, or rollback. (Does not commit) err = s.execBlockOnProxyApp(proxyAppCtx, block) if err != nil { proxyAppCtx.RollbackSync() return err } // All good! nextValSet.IncrementAccum(1) s.Validators = nextValSet s.LastValidators = valSet s.LastAppHash = block.AppHash s.LastBlockHeight = block.Height s.LastBlockHash = block.Hash() s.LastBlockParts = blockPartsHeader s.LastBlockTime = block.Time return nil }
// executes transactions of a block, does not check block.StateHash // NOTE: If an error occurs during block execution, state will be left // at an invalid state. Copy the state before calling execBlock! func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader) error { // Basic block validation. err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime) if err != nil { return err } // Validate block LastValidation. if block.Height == 1 { if len(block.LastValidation.Precommits) != 0 { return errors.New("Block at height 1 (first block) should have no LastValidation precommits") } } else { if len(block.LastValidation.Precommits) != s.LastBondedValidators.Size() { return errors.New(Fmt("Invalid block validation size. Expected %v, got %v", s.LastBondedValidators.Size(), len(block.LastValidation.Precommits))) } err := s.LastBondedValidators.VerifyValidation( s.ChainID, s.LastBlockHash, s.LastBlockParts, block.Height-1, block.LastValidation) if err != nil { return err } } // Update Validator.LastCommitHeight as necessary. for i, precommit := range block.LastValidation.Precommits { if precommit == nil { continue } _, val := s.LastBondedValidators.GetByIndex(i) if val == nil { PanicCrisis(Fmt("Failed to fetch validator at index %v", i)) } if _, val_ := s.BondedValidators.GetByAddress(val.Address); val_ != nil { val_.LastCommitHeight = block.Height - 1 updated := s.BondedValidators.Update(val_) if !updated { PanicCrisis("Failed to update bonded validator LastCommitHeight") } } else if _, val_ := s.UnbondingValidators.GetByAddress(val.Address); val_ != nil { val_.LastCommitHeight = block.Height - 1 updated := s.UnbondingValidators.Update(val_) if !updated { PanicCrisis("Failed to update unbonding validator LastCommitHeight") } } else { PanicCrisis("Could not find validator") } } // Remember LastBondedValidators s.LastBondedValidators = s.BondedValidators.Copy() // Create BlockCache to cache changes to state. blockCache := NewBlockCache(s) // Execute each tx for _, tx := range block.Data.Txs { err := ExecTx(blockCache, tx, true, s.evc) if err != nil { return InvalidTxError{tx, err} } } // Now sync the BlockCache to the backend. blockCache.Sync() // If any unbonding periods are over, // reward account with bonded coins. toRelease := []*types.Validator{} s.UnbondingValidators.Iterate(func(index int, val *types.Validator) bool { if val.UnbondHeight+unbondingPeriodBlocks < block.Height { toRelease = append(toRelease, val) } return false }) for _, val := range toRelease { s.releaseValidator(val) } // If any validators haven't signed in a while, // unbond them, they have timed out. toTimeout := []*types.Validator{} s.BondedValidators.Iterate(func(index int, val *types.Validator) bool { lastActivityHeight := MaxInt(val.BondHeight, val.LastCommitHeight) if lastActivityHeight+validatorTimeoutBlocks < block.Height { log.Notice("Validator timeout", "validator", val, "height", block.Height) toTimeout = append(toTimeout, val) } return false }) for _, val := range toTimeout { s.unbondValidator(val) } // Increment validator AccumPowers s.BondedValidators.IncrementAccum(1) s.LastBlockHeight = block.Height s.LastBlockHash = block.Hash() s.LastBlockParts = blockPartsHeader s.LastBlockTime = block.Time return nil }
// executes transactions of a block, does not check block.StateHash // NOTE: If an error occurs during block execution, state will be left // at an invalid state. Copy the state before calling execBlock! func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeader) error { // Basic block validation. err := block.ValidateBasic(s.LastBlockHeight, s.LastBlockHash, s.LastBlockParts, s.LastBlockTime) if err != nil { return err } // Validate block Validation. 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(block.Validation.Commits)) != s.LastBondedValidators.Size() { return errors.New(Fmt("Invalid block validation size. Expected %v, got %v", s.LastBondedValidators.Size(), len(block.Validation.Commits))) } var sumVotingPower uint64 s.LastBondedValidators.Iterate(func(index uint, val *Validator) bool { commit := block.Validation.Commits[index] if commit.IsZero() { return false } else { vote := &types.Vote{ Height: block.Height - 1, Round: commit.Round, Type: types.VoteTypeCommit, BlockHash: block.LastBlockHash, BlockParts: block.LastBlockParts, } if val.PubKey.VerifyBytes(account.SignBytes(vote), commit.Signature) { sumVotingPower += val.VotingPower return false } else { log.Warn(Fmt("Invalid validation signature.\nval: %v\nvote: %v", val, vote)) err = errors.New("Invalid validation signature") return true } } }) if err != nil { return err } if sumVotingPower <= s.LastBondedValidators.TotalVotingPower()*2/3 { return errors.New("Insufficient validation voting power") } } // Update Validator.LastCommitHeight as necessary. for i, commit := range block.Validation.Commits { if commit.IsZero() { continue } _, val := s.LastBondedValidators.GetByIndex(uint(i)) if val == nil { panic(Fmt("Failed to fetch validator at index %v", i)) } if _, val_ := s.BondedValidators.GetByAddress(val.Address); val_ != nil { val_.LastCommitHeight = block.Height - 1 updated := s.BondedValidators.Update(val_) if !updated { panic("Failed to update bonded validator LastCommitHeight") } } else if _, val_ := s.UnbondingValidators.GetByAddress(val.Address); val_ != nil { val_.LastCommitHeight = block.Height - 1 updated := s.UnbondingValidators.Update(val_) if !updated { panic("Failed to update unbonding validator LastCommitHeight") } } else { panic("Could not find validator") } } // Remember LastBondedValidators s.LastBondedValidators = s.BondedValidators.Copy() // Create BlockCache to cache changes to state. blockCache := NewBlockCache(s) // Commit each tx for _, tx := range block.Data.Txs { err := ExecTx(blockCache, tx, true, s.evc) if err != nil { return InvalidTxError{tx, err} } } // Now sync the BlockCache to the backend. blockCache.Sync() // If any unbonding periods are over, // reward account with bonded coins. toRelease := []*Validator{} s.UnbondingValidators.Iterate(func(index uint, val *Validator) bool { if val.UnbondHeight+unbondingPeriodBlocks < block.Height { toRelease = append(toRelease, val) } return false }) for _, val := range toRelease { s.releaseValidator(val) } // If any validators haven't signed in a while, // unbond them, they have timed out. toTimeout := []*Validator{} s.BondedValidators.Iterate(func(index uint, val *Validator) bool { lastActivityHeight := MaxUint(val.BondHeight, val.LastCommitHeight) if lastActivityHeight+validatorTimeoutBlocks < block.Height { log.Info("Validator timeout", "validator", val, "height", block.Height) toTimeout = append(toTimeout, val) } return false }) for _, val := range toTimeout { s.unbondValidator(val) } // Increment validator AccumPowers s.BondedValidators.IncrementAccum(1) s.LastBlockHeight = block.Height s.LastBlockHash = block.Hash() s.LastBlockParts = blockPartsHeader s.LastBlockTime = block.Time return nil }