package state import ( "bytes" "errors" . "github.com/tendermint/go-common" "github.com/tendermint/tendermint/events" "github.com/tendermint/tendermint/types" ) // 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 { err := execBlock(s, block, blockPartsHeader) if err != nil { return err } // State.Hash should match block.StateHash stateHash := s.Hash() if !bytes.Equal(stateHash, block.StateHash) { return errors.New(Fmt("Invalid state hash. Expected %X, got %X", stateHash, block.StateHash)) } 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.LastValidators.Size() { return errors.New(Fmt("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 } } // Update Validator.LastCommitHeight as necessary. for i, precommit := range block.LastValidation.Precommits { if precommit == nil { continue } _, val := s.LastValidators.GetByIndex(i) if val == nil { PanicCrisis(Fmt("Failed to fetch validator at index %v", i)) } if _, val_ := s.Validators.GetByAddress(val.Address); val_ != nil { val_.LastCommitHeight = block.Height - 1 updated := s.Validators.Update(val_) if !updated { PanicCrisis("Failed to update validator LastCommitHeight") } } else { PanicCrisis("Could not find validator") } } // Remember LastValidators s.LastValidators = s.Validators.Copy() // Execute each tx for _, tx := range block.Data.Txs { err := ExecTx(s, tx, s.evc) if err != nil { return InvalidTxError{tx, err} } } // Increment validator AccumPowers s.Validators.IncrementAccum(1) s.LastBlockHeight = block.Height s.LastBlockHash = block.Hash() s.LastBlockParts = blockPartsHeader s.LastBlockTime = block.Time return nil } // If the tx is invalid, an error will be returned. // Unlike ExecBlock(), state will not be altered. func ExecTx(s *State, tx types.Tx, evc events.Fireable) (err error) { // TODO: do something with fees //fees := int64(0) //_s := blockCache.State() // hack to access validators and block height // XXX Query ledger application return nil } //----------------------------------------------------------------------------- type InvalidTxError struct { Tx types.Tx Reason error } func (txErr InvalidTxError) Error() string { return Fmt("Invalid tx: [%v] reason: [%v]", txErr.Tx, txErr.Reason) }