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 } }
// Mutates the block in place and updates it with new state hash. func (s *State) ComputeBlockStateHash(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 }
// 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 }