Browse Source

state: move methods to funcs

pull/1015/head
Ethan Buchman 7 years ago
parent
commit
f55135578c
9 changed files with 299 additions and 295 deletions
  1. +1
    -1
      consensus/replay.go
  2. +7
    -5
      evidence/pool.go
  3. +2
    -1
      rpc/core/blocks.go
  4. +2
    -1
      rpc/core/consensus.go
  5. +199
    -0
      state/db.go
  6. +55
    -22
      state/execution.go
  7. +10
    -238
      state/state.go
  8. +23
    -18
      state/state_test.go
  9. +0
    -9
      types/services.go

+ 1
- 1
consensus/replay.go View File

@ -301,7 +301,7 @@ func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int64, proxyApp
} else if appBlockHeight == storeBlockHeight { } else if appBlockHeight == storeBlockHeight {
// We ran Commit, but didn't save the state, so replayBlock with mock app // We ran Commit, but didn't save the state, so replayBlock with mock app
abciResponses, err := h.state.LoadABCIResponses(storeBlockHeight)
abciResponses, err := sm.LoadABCIResponses(h.state.DB(), storeBlockHeight)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 7
- 5
evidence/pool.go View File

@ -9,22 +9,24 @@ import (
// EvidencePool maintains a pool of valid evidence // EvidencePool maintains a pool of valid evidence
// in an EvidenceStore. // in an EvidenceStore.
type EvidencePool struct { type EvidencePool struct {
params types.EvidenceParams
logger log.Logger logger log.Logger
state types.State // TODO: update this on commit!
evidenceStore *EvidenceStore evidenceStore *EvidenceStore
chainID string
lastBlockHeight int64
params types.EvidenceParams
// never close // never close
evidenceChan chan types.Evidence evidenceChan chan types.Evidence
} }
func NewEvidencePool(params types.EvidenceParams, evidenceStore *EvidenceStore, state types.State) *EvidencePool {
func NewEvidencePool(params types.EvidenceParams, evidenceStore *EvidenceStore, state *types.State) *EvidencePool {
evpool := &EvidencePool{ evpool := &EvidencePool{
params: params, params: params,
logger: log.NewNopLogger(), logger: log.NewNopLogger(),
evidenceStore: evidenceStore, evidenceStore: evidenceStore,
state: state,
state: *state,
evidenceChan: make(chan types.Evidence), evidenceChan: make(chan types.Evidence),
} }
return evpool return evpool
@ -56,7 +58,7 @@ func (evpool *EvidencePool) AddEvidence(evidence types.Evidence) (err error) {
// TODO: check if we already have evidence for this // TODO: check if we already have evidence for this
// validator at this height so we dont get spammed // validator at this height so we dont get spammed
priority, err := evpool.state.VerifyEvidence(evidence)
priority, err := sm.VerifyEvidence(evpool.state, evidence)
if err != nil { if err != nil {
// TODO: if err is just that we cant find it cuz we pruned, ignore. // TODO: if err is just that we cant find it cuz we pruned, ignore.
// TODO: if its actually bad evidence, punish peer // TODO: if its actually bad evidence, punish peer


+ 2
- 1
rpc/core/blocks.go View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
@ -337,7 +338,7 @@ func BlockResults(heightPtr *int64) (*ctypes.ResultBlockResults, error) {
// load the results // load the results
state := consensusState.GetState() state := consensusState.GetState()
results, err := state.LoadABCIResponses(height)
results, err := sm.LoadABCIResponses(state.DB(), height)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 2
- 1
rpc/core/consensus.go View File

@ -4,6 +4,7 @@ import (
cm "github.com/tendermint/tendermint/consensus" cm "github.com/tendermint/tendermint/consensus"
cstypes "github.com/tendermint/tendermint/consensus/types" cstypes "github.com/tendermint/tendermint/consensus/types"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -50,7 +51,7 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
} }
state := consensusState.GetState() state := consensusState.GetState()
validators, err := state.LoadValidators(height)
validators, err := sm.LoadValidators(state.DB(), height)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 199
- 0
state/db.go View File

@ -0,0 +1,199 @@
package state
import (
"bytes"
"fmt"
abci "github.com/tendermint/abci/types"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
)
//------------------------------------------------------------------------
// ABCIResponses retains the responses
// of the various ABCI calls during block processing.
// It is persisted to disk for each height before calling Commit.
type ABCIResponses struct {
DeliverTx []*abci.ResponseDeliverTx
EndBlock *abci.ResponseEndBlock
}
// NewABCIResponses returns a new ABCIResponses
func NewABCIResponses(block *types.Block) *ABCIResponses {
return &ABCIResponses{
DeliverTx: make([]*abci.ResponseDeliverTx, block.NumTxs),
}
}
// Bytes serializes the ABCIResponse using go-wire
func (a *ABCIResponses) Bytes() []byte {
return wire.BinaryBytes(*a)
}
func (a *ABCIResponses) ResultsHash() []byte {
results := types.NewResults(a.DeliverTx)
return results.Hash()
}
// LoadABCIResponses loads the ABCIResponses for the given height from the database.
// This is useful for recovering from crashes where we called app.Commit and before we called
// s.Save(). It can also be used to produce Merkle proofs of the result of txs.
func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) {
buf := db.Get(calcABCIResponsesKey(height))
if len(buf) == 0 {
return nil, ErrNoABCIResponsesForHeight{height}
}
abciResponses := new(ABCIResponses)
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(abciResponses, r, 0, n, err)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
cmn.Exit(cmn.Fmt(`LoadABCIResponses: Data has been corrupted or its spec has
changed: %v\n`, *err))
}
// TODO: ensure that buf is completely read.
return abciResponses, nil
}
// SaveABCIResponses persists the ABCIResponses to the database.
// This is useful in case we crash after app.Commit and before s.Save().
// Responses are indexed by height so they can also be loaded later to produce Merkle proofs.
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
}
//-----------------------------------------------------------------------------
// ValidatorsInfo represents the latest validator set, or the last height it changed
type ValidatorsInfo struct {
ValidatorSet *types.ValidatorSet
LastHeightChanged int64
}
// Bytes serializes the ValidatorsInfo using go-wire
func (valInfo *ValidatorsInfo) Bytes() []byte {
return wire.BinaryBytes(*valInfo)
}
// LoadValidators loads the ValidatorSet for a given height.
// Returns ErrNoValSetForHeight if the validator set can't be found for this height.
func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
valInfo := loadValidatorsInfo(db, height)
if valInfo == nil {
return nil, ErrNoValSetForHeight{height}
}
if valInfo.ValidatorSet == nil {
valInfo = loadValidatorsInfo(db, valInfo.LastHeightChanged)
if valInfo == nil {
cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at height %d as
last changed from height %d`, valInfo.LastHeightChanged, height))
}
}
return valInfo.ValidatorSet, nil
}
func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
buf := db.Get(calcValidatorsKey(height))
if len(buf) == 0 {
return nil
}
v := new(ValidatorsInfo)
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(v, r, 0, n, err)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
cmn.Exit(cmn.Fmt(`LoadValidators: Data has been corrupted or its spec has changed:
%v\n`, *err))
}
// TODO: ensure that buf is completely read.
return v
}
// saveValidatorsInfo persists the validator set for the next block to disk.
// It should be called from s.Save(), right before the state itself is persisted.
// If the validator set did not change after processing the latest block,
// only the last height for which the validators changed is persisted.
func saveValidatorsInfo(db dbm.DB, nextHeight, changeHeight int64, valSet *types.ValidatorSet) {
valInfo := &ValidatorsInfo{
LastHeightChanged: changeHeight,
}
if changeHeight == nextHeight {
valInfo.ValidatorSet = valSet
}
db.SetSync(calcValidatorsKey(nextHeight), valInfo.Bytes())
}
//-----------------------------------------------------------------------------
// ConsensusParamsInfo represents the latest consensus params, or the last height it changed
type ConsensusParamsInfo struct {
ConsensusParams types.ConsensusParams
LastHeightChanged int64
}
// Bytes serializes the ConsensusParamsInfo using go-wire
func (params ConsensusParamsInfo) Bytes() []byte {
return wire.BinaryBytes(params)
}
// LoadConsensusParams loads the ConsensusParams for a given height.
func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) {
empty := types.ConsensusParams{}
paramsInfo := loadConsensusParamsInfo(db, height)
if paramsInfo == nil {
return empty, ErrNoConsensusParamsForHeight{height}
}
if paramsInfo.ConsensusParams == empty {
paramsInfo = loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
if paramsInfo == nil {
cmn.PanicSanity(fmt.Sprintf(`Couldn't find consensus params at height %d as
last changed from height %d`, paramsInfo.LastHeightChanged, height))
}
}
return paramsInfo.ConsensusParams, nil
}
func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo {
buf := db.Get(calcConsensusParamsKey(height))
if len(buf) == 0 {
return nil
}
paramsInfo := new(ConsensusParamsInfo)
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(paramsInfo, r, 0, n, err)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
cmn.Exit(cmn.Fmt(`LoadConsensusParams: Data has been corrupted or its spec has changed:
%v\n`, *err))
}
// TODO: ensure that buf is completely read.
return paramsInfo
}
// saveConsensusParamsInfo persists the consensus params for the next block to disk.
// It should be called from s.Save(), right before the state itself is persisted.
// If the consensus params did not change after processing the latest block,
// only the last height for which they changed is persisted.
func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) {
paramsInfo := &ConsensusParamsInfo{
LastHeightChanged: changeHeight,
}
if changeHeight == nextHeight {
paramsInfo.ConsensusParams = params
}
db.SetSync(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
}

+ 55
- 22
state/execution.go View File

@ -209,28 +209,9 @@ func changeInVotingPowerMoreOrEqualToOneThird(currentSet *types.ValidatorSet, up
return false, nil return false, nil
} }
// return a bit array of validators that signed the last commit
// NOTE: assumes commits have already been authenticated
/* function is currently unused
func commitBitArrayFromBlock(block *types.Block) *cmn.BitArray {
signed := cmn.NewBitArray(len(block.LastCommit.Precommits))
for i, precommit := range block.LastCommit.Precommits {
if precommit != nil {
signed.SetIndex(i, true) // val_.LastCommitHeight = block.Height - 1
}
}
return signed
}
*/
//----------------------------------------------------- //-----------------------------------------------------
// Validate block // Validate block
// ValidateBlock validates the block against the state.
func (s *State) ValidateBlock(block *types.Block) error {
return s.validateBlock(block)
}
// MakeBlock builds a block with the given txs and commit from the current state. // MakeBlock builds a block with the given txs and commit from the current state.
func (s *State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*types.Block, *types.PartSet) { func (s *State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*types.Block, *types.PartSet) {
// build base block // build base block
@ -248,7 +229,12 @@ func (s *State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*
return block, block.MakePartSet(s.ConsensusParams.BlockGossip.BlockPartSizeBytes) return block, block.MakePartSet(s.ConsensusParams.BlockGossip.BlockPartSizeBytes)
} }
func (s *State) validateBlock(b *types.Block) error {
// ValidateBlock validates the block against the state.
func (s State) ValidateBlock(block *types.Block) error {
return s.validateBlock(block)
}
func (s State) validateBlock(b *types.Block) error {
// validate internal consistency // validate internal consistency
if err := b.ValidateBasic(); err != nil { if err := b.ValidateBasic(); err != nil {
return err return err
@ -310,7 +296,7 @@ func (s *State) validateBlock(b *types.Block) error {
} }
for _, ev := range b.Evidence.Evidence { for _, ev := range b.Evidence.Evidence {
if _, err := s.VerifyEvidence(ev); err != nil {
if _, err := VerifyEvidence(s, ev); err != nil {
return types.NewEvidenceInvalidErr(ev, err) return types.NewEvidenceInvalidErr(ev, err)
} }
} }
@ -318,10 +304,57 @@ func (s *State) validateBlock(b *types.Block) error {
return nil return nil
} }
// VerifyEvidence verifies the evidence fully by checking it is internally
// consistent and corresponds to an existing or previous validator.
// It returns the priority of this evidence, or an error.
// NOTE: return error may be ErrNoValSetForHeight, in which case the validator set
// for the evidence height could not be loaded.
func VerifyEvidence(s State, evidence types.Evidence) (priority int64, err error) {
height := s.LastBlockHeight
evidenceAge := height - evidence.Height()
maxAge := s.ConsensusParams.EvidenceParams.MaxAge
if evidenceAge > maxAge {
return priority, fmt.Errorf("Evidence from height %d is too old. Min height is %d",
evidence.Height(), height-maxAge)
}
if err := evidence.Verify(s.ChainID); err != nil {
return priority, err
}
// The address must have been an active validator at the height
ev := evidence
height, addr, idx := ev.Height(), ev.Address(), ev.Index()
valset, err := LoadValidators(s.db, height)
if err != nil {
// XXX/TODO: what do we do if we can't load the valset?
// eg. if we have pruned the state or height is too high?
return priority, err
}
valIdx, val := valset.GetByAddress(addr)
if val == nil {
return priority, fmt.Errorf("Address %X was not a validator at height %d", addr, height)
} else if idx != valIdx {
return priority, fmt.Errorf("Address %X was validator %d at height %d, not %d", addr, valIdx, height, idx)
}
priority = val.VotingPower
return priority, nil
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// ApplyBlock validates & executes the block, updates state w/ ABCI responses, // ApplyBlock validates & executes the block, updates state w/ ABCI responses,
// then commits and updates the mempool atomically, then saves state. // then commits and updates the mempool atomically, then saves state.
// BlockExecutor provides the context and accessories for properly executing a block.
type BlockExecutor struct {
txEventPublisher types.TxEventPublisher
proxyApp proxy.AppConnConsensus
mempool types.Mempool
evpool types.EvidencePool
}
// ApplyBlock validates the block against the state, executes it against the app, // ApplyBlock validates the block against the state, executes it against the app,
// commits it, and saves the block and state. It's the only function that needs to be called // commits it, and saves the block and state. It's the only function that needs to be called
// from outside this package to process and commit an entire block. // from outside this package to process and commit an entire block.
@ -337,7 +370,7 @@ func (s *State) ApplyBlock(txEventPublisher types.TxEventPublisher, proxyAppConn
fail.Fail() // XXX fail.Fail() // XXX
// save the results before we commit // save the results before we commit
s.SaveABCIResponses(block.Height, abciResponses)
SaveABCIResponses(s.db, block.Height, abciResponses)
fail.Fail() // XXX fail.Fail() // XXX


+ 10
- 238
state/state.go View File

@ -4,11 +4,8 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"sync"
"time" "time"
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -44,9 +41,7 @@ func calcABCIResponsesKey(height int64) []byte {
// but the fields should only be changed by calling state.SetBlockAndValidators. // but the fields should only be changed by calling state.SetBlockAndValidators.
// NOTE: not goroutine-safe. // NOTE: not goroutine-safe.
type State struct { type State struct {
// mtx for writing to db
mtx sync.Mutex
db dbm.DB
db dbm.DB
// Immutable // Immutable
ChainID string ChainID string
@ -82,6 +77,10 @@ type State struct {
logger log.Logger logger log.Logger
} }
func (s *State) DB() dbm.DB {
return s.db
}
// GetState loads the most recent state from the database, // GetState loads the most recent state from the database,
// or creates a new one from the given genesisFile and persists the result // or creates a new one from the given genesisFile and persists the result
// to the database. // to the database.
@ -157,150 +156,13 @@ func (s *State) Copy() *State {
// Save persists the State to the database. // Save persists the State to the database.
func (s *State) Save() { func (s *State) Save() {
s.mtx.Lock()
defer s.mtx.Unlock()
s.saveValidatorsInfo()
s.saveConsensusParamsInfo()
s.db.SetSync(stateKey, s.Bytes())
}
// SaveABCIResponses persists the ABCIResponses to the database.
// This is useful in case we crash after app.Commit and before s.Save().
// Responses are indexed by height so they can also be loaded later to produce Merkle proofs.
func (s *State) SaveABCIResponses(height int64, abciResponses *ABCIResponses) {
s.db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
}
// LoadABCIResponses loads the ABCIResponses for the given height from the database.
// This is useful for recovering from crashes where we called app.Commit and before we called
// s.Save(). It can also be used to produce Merkle proofs of the result of txs.
func (s *State) LoadABCIResponses(height int64) (*ABCIResponses, error) {
buf := s.db.Get(calcABCIResponsesKey(height))
if len(buf) == 0 {
return nil, ErrNoABCIResponsesForHeight{height}
}
abciResponses := new(ABCIResponses)
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(abciResponses, r, 0, n, err)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
cmn.Exit(cmn.Fmt(`LoadABCIResponses: Data has been corrupted or its spec has
changed: %v\n`, *err))
}
// TODO: ensure that buf is completely read.
return abciResponses, nil
}
// LoadValidators loads the ValidatorSet for a given height.
// Returns ErrNoValSetForHeight if the validator set can't be found for this height.
func (s *State) LoadValidators(height int64) (*types.ValidatorSet, error) {
valInfo := s.loadValidatorsInfo(height)
if valInfo == nil {
return nil, ErrNoValSetForHeight{height}
}
if valInfo.ValidatorSet == nil {
valInfo = s.loadValidatorsInfo(valInfo.LastHeightChanged)
if valInfo == nil {
cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at height %d as
last changed from height %d`, valInfo.LastHeightChanged, height))
}
}
return valInfo.ValidatorSet, nil
}
func (s *State) loadValidatorsInfo(height int64) *ValidatorsInfo {
buf := s.db.Get(calcValidatorsKey(height))
if len(buf) == 0 {
return nil
}
v := new(ValidatorsInfo)
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(v, r, 0, n, err)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
cmn.Exit(cmn.Fmt(`LoadValidators: Data has been corrupted or its spec has changed:
%v\n`, *err))
}
// TODO: ensure that buf is completely read.
return v
}
// saveValidatorsInfo persists the validator set for the next block to disk.
// It should be called from s.Save(), right before the state itself is persisted.
// If the validator set did not change after processing the latest block,
// only the last height for which the validators changed is persisted.
func (s *State) saveValidatorsInfo() {
changeHeight := s.LastHeightValidatorsChanged
nextHeight := s.LastBlockHeight + 1 nextHeight := s.LastBlockHeight + 1
valInfo := &ValidatorsInfo{
LastHeightChanged: changeHeight,
}
if changeHeight == nextHeight {
valInfo.ValidatorSet = s.Validators
}
s.db.SetSync(calcValidatorsKey(nextHeight), valInfo.Bytes())
}
// LoadConsensusParams loads the ConsensusParams for a given height.
func (s *State) LoadConsensusParams(height int64) (types.ConsensusParams, error) {
empty := types.ConsensusParams{}
paramsInfo := s.loadConsensusParamsInfo(height)
if paramsInfo == nil {
return empty, ErrNoConsensusParamsForHeight{height}
}
if paramsInfo.ConsensusParams == empty {
paramsInfo = s.loadConsensusParamsInfo(paramsInfo.LastHeightChanged)
if paramsInfo == nil {
cmn.PanicSanity(fmt.Sprintf(`Couldn't find consensus params at height %d as
last changed from height %d`, paramsInfo.LastHeightChanged, height))
}
}
return paramsInfo.ConsensusParams, nil
}
func (s *State) loadConsensusParamsInfo(height int64) *ConsensusParamsInfo {
buf := s.db.Get(calcConsensusParamsKey(height))
if len(buf) == 0 {
return nil
}
paramsInfo := new(ConsensusParamsInfo)
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(paramsInfo, r, 0, n, err)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
cmn.Exit(cmn.Fmt(`LoadConsensusParams: Data has been corrupted or its spec has changed:
%v\n`, *err))
}
// TODO: ensure that buf is completely read.
return paramsInfo
}
// saveConsensusParamsInfo persists the consensus params for the next block to disk.
// It should be called from s.Save(), right before the state itself is persisted.
// If the consensus params did not change after processing the latest block,
// only the last height for which they changed is persisted.
func (s *State) saveConsensusParamsInfo() {
changeHeight := s.LastHeightConsensusParamsChanged
nextHeight := s.LastBlockHeight + 1
paramsInfo := &ConsensusParamsInfo{
LastHeightChanged: changeHeight,
}
if changeHeight == nextHeight {
paramsInfo.ConsensusParams = s.ConsensusParams
}
s.db.SetSync(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
// persist everything to db
db := s.db
saveValidatorsInfo(db, nextHeight, s.LastHeightValidatorsChanged, s.Validators)
saveConsensusParamsInfo(db, nextHeight, s.LastHeightConsensusParamsChanged, s.ConsensusParams)
db.SetSync(stateKey, s.Bytes())
} }
// Equals returns true if the States are identical. // Equals returns true if the States are identical.
@ -383,96 +245,6 @@ func (s *State) GetValidators() (last *types.ValidatorSet, current *types.Valida
return s.LastValidators, s.Validators return s.LastValidators, s.Validators
} }
// VerifyEvidence verifies the evidence fully by checking it is internally
// consistent and corresponds to an existing or previous validator.
// It returns the priority of this evidence, or an error.
// NOTE: return error may be ErrNoValSetForHeight, in which case the validator set
// for the evidence height could not be loaded.
func (s *State) VerifyEvidence(evidence types.Evidence) (priority int64, err error) {
evidenceAge := s.LastBlockHeight - evidence.Height()
maxAge := s.ConsensusParams.EvidenceParams.MaxAge
if evidenceAge > maxAge {
return priority, fmt.Errorf("Evidence from height %d is too old. Min height is %d",
evidence.Height(), s.LastBlockHeight-maxAge)
}
if err := evidence.Verify(s.ChainID); err != nil {
return priority, err
}
// The address must have been an active validator at the height
ev := evidence
height, addr, idx := ev.Height(), ev.Address(), ev.Index()
valset, err := s.LoadValidators(height)
if err != nil {
// XXX/TODO: what do we do if we can't load the valset?
// eg. if we have pruned the state or height is too high?
return priority, err
}
valIdx, val := valset.GetByAddress(addr)
if val == nil {
return priority, fmt.Errorf("Address %X was not a validator at height %d", addr, height)
} else if idx != valIdx {
return priority, fmt.Errorf("Address %X was validator %d at height %d, not %d", addr, valIdx, height, idx)
}
priority = val.VotingPower
return priority, nil
}
//------------------------------------------------------------------------
// ABCIResponses retains the responses
// of the various ABCI calls during block processing.
// It is persisted to disk for each height before calling Commit.
type ABCIResponses struct {
DeliverTx []*abci.ResponseDeliverTx
EndBlock *abci.ResponseEndBlock
}
// NewABCIResponses returns a new ABCIResponses
func NewABCIResponses(block *types.Block) *ABCIResponses {
return &ABCIResponses{
DeliverTx: make([]*abci.ResponseDeliverTx, block.NumTxs),
}
}
// Bytes serializes the ABCIResponse using go-wire
func (a *ABCIResponses) Bytes() []byte {
return wire.BinaryBytes(*a)
}
func (a *ABCIResponses) ResultsHash() []byte {
results := types.NewResults(a.DeliverTx)
return results.Hash()
}
//-----------------------------------------------------------------------------
// ValidatorsInfo represents the latest validator set, or the last height it changed
type ValidatorsInfo struct {
ValidatorSet *types.ValidatorSet
LastHeightChanged int64
}
// Bytes serializes the ValidatorsInfo using go-wire
func (valInfo *ValidatorsInfo) Bytes() []byte {
return wire.BinaryBytes(*valInfo)
}
//-----------------------------------------------------------------------------
// ConsensusParamsInfo represents the latest consensus params, or the last height it changed
type ConsensusParamsInfo struct {
ConsensusParams types.ConsensusParams
LastHeightChanged int64
}
// Bytes serializes the ConsensusParamsInfo using go-wire
func (params ConsensusParamsInfo) Bytes() []byte {
return wire.BinaryBytes(params)
}
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// Genesis // Genesis


+ 23
- 18
state/state_test.go View File

@ -88,8 +88,8 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
}, },
}} }}
state.SaveABCIResponses(block.Height, abciResponses)
loadedAbciResponses, err := state.LoadABCIResponses(block.Height)
SaveABCIResponses(state.db, block.Height, abciResponses)
loadedAbciResponses, err := LoadABCIResponses(state.db, block.Height)
assert.Nil(err) assert.Nil(err)
assert.Equal(abciResponses, loadedAbciResponses, assert.Equal(abciResponses, loadedAbciResponses,
cmn.Fmt(`ABCIResponses don't match: Got %v, Expected %v`, loadedAbciResponses, cmn.Fmt(`ABCIResponses don't match: Got %v, Expected %v`, loadedAbciResponses,
@ -142,7 +142,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
// query all before, should return error // query all before, should return error
for i := range cases { for i := range cases {
h := int64(i + 1) h := int64(i + 1)
res, err := state.LoadABCIResponses(h)
res, err := LoadABCIResponses(state.db, h)
assert.Error(err, "%d: %#v", i, res) assert.Error(err, "%d: %#v", i, res)
} }
@ -153,13 +153,13 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
DeliverTx: tc.added, DeliverTx: tc.added,
EndBlock: &abci.ResponseEndBlock{}, EndBlock: &abci.ResponseEndBlock{},
} }
state.SaveABCIResponses(h, responses)
SaveABCIResponses(state.db, h, responses)
} }
// query all before, should return expected value // query all before, should return expected value
for i, tc := range cases { for i, tc := range cases {
h := int64(i + 1) h := int64(i + 1)
res, err := state.LoadABCIResponses(h)
res, err := LoadABCIResponses(state.db, h)
assert.NoError(err, "%d", i) assert.NoError(err, "%d", i)
assert.Equal(tc.expected.Hash(), res.ResultsHash(), "%d", i) assert.Equal(tc.expected.Hash(), res.ResultsHash(), "%d", i)
} }
@ -173,30 +173,32 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
// can't load anything for height 0 // can't load anything for height 0
v, err := state.LoadValidators(0)
v, err := LoadValidators(state.db, 0)
assert.IsType(ErrNoValSetForHeight{}, err, "expected err at height 0") assert.IsType(ErrNoValSetForHeight{}, err, "expected err at height 0")
// should be able to load for height 1 // should be able to load for height 1
v, err = state.LoadValidators(1)
v, err = LoadValidators(state.db, 1)
assert.Nil(err, "expected no err at height 1") assert.Nil(err, "expected no err at height 1")
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match") assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match")
// increment height, save; should be able to load for next height // increment height, save; should be able to load for next height
state.LastBlockHeight++ state.LastBlockHeight++
state.saveValidatorsInfo()
v, err = state.LoadValidators(state.LastBlockHeight + 1)
nextHeight := state.LastBlockHeight + 1
saveValidatorsInfo(state.db, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
v, err = LoadValidators(state.db, nextHeight)
assert.Nil(err, "expected no err") assert.Nil(err, "expected no err")
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match") assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match")
// increment height, save; should be able to load for next height // increment height, save; should be able to load for next height
state.LastBlockHeight += 10 state.LastBlockHeight += 10
state.saveValidatorsInfo()
v, err = state.LoadValidators(state.LastBlockHeight + 1)
nextHeight = state.LastBlockHeight + 1
saveValidatorsInfo(state.db, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
v, err = LoadValidators(state.db, nextHeight)
assert.Nil(err, "expected no err") assert.Nil(err, "expected no err")
assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match") assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match")
// should be able to load for next next height // should be able to load for next next height
_, err = state.LoadValidators(state.LastBlockHeight + 2)
_, err = LoadValidators(state.db, state.LastBlockHeight+2)
assert.IsType(ErrNoValSetForHeight{}, err, "expected err at unknown height") assert.IsType(ErrNoValSetForHeight{}, err, "expected err at unknown height")
} }
@ -225,7 +227,8 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
header, parts, responses := makeHeaderPartsResponsesValPowerChange(state, i, power) header, parts, responses := makeHeaderPartsResponsesValPowerChange(state, i, power)
err := state.SetBlockAndValidators(header, parts, responses) err := state.SetBlockAndValidators(header, parts, responses)
assert.Nil(t, err) assert.Nil(t, err)
state.saveValidatorsInfo()
nextHeight := state.LastBlockHeight + 1
saveValidatorsInfo(state.db, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
} }
// on each change height, increment the power by one. // on each change height, increment the power by one.
@ -243,7 +246,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
} }
for i, power := range testCases { for i, power := range testCases {
v, err := state.LoadValidators(int64(i + 1))
v, err := LoadValidators(state.db, int64(i+1))
assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", i)) assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", i))
assert.Equal(t, v.Size(), 1, "validator set size is greater than 1: %d", v.Size()) assert.Equal(t, v.Size(), 1, "validator set size is greater than 1: %d", v.Size())
_, val := v.GetByIndex(0) _, val := v.GetByIndex(0)
@ -268,9 +271,10 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
header, parts, responses := makeHeaderPartsResponsesValPubKeyChange(state, height, pubkey) header, parts, responses := makeHeaderPartsResponsesValPubKeyChange(state, height, pubkey)
err := state.SetBlockAndValidators(header, parts, responses) err := state.SetBlockAndValidators(header, parts, responses)
require.Nil(t, err) require.Nil(t, err)
state.saveValidatorsInfo()
nextHeight := state.LastBlockHeight + 1
saveValidatorsInfo(state.db, nextHeight, state.LastHeightValidatorsChanged, state.Validators)
v, err := state.LoadValidators(height + 1)
v, err := LoadValidators(state.db, height+1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, valSetSize, v.Size()) assert.Equal(t, valSetSize, v.Size())
@ -323,7 +327,8 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
header, parts, responses := makeHeaderPartsResponsesParams(state, i, cp) header, parts, responses := makeHeaderPartsResponsesParams(state, i, cp)
err := state.SetBlockAndValidators(header, parts, responses) err := state.SetBlockAndValidators(header, parts, responses)
require.Nil(t, err) require.Nil(t, err)
state.saveConsensusParamsInfo()
nextHeight := state.LastBlockHeight + 1
saveConsensusParamsInfo(state.db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
} }
// make all the test cases by using the same params until after the change // make all the test cases by using the same params until after the change
@ -341,7 +346,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
p, err := state.LoadConsensusParams(testCase.height)
p, err := LoadConsensusParams(state.db, testCase.height)
assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", testCase.height)) assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", testCase.height))
assert.Equal(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at assert.Equal(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at
height %d`, testCase.height)) height %d`, testCase.height))


+ 0
- 9
types/services.go View File

@ -70,15 +70,6 @@ type BlockStore interface {
SaveBlock(block *Block, blockParts *PartSet, seenCommit *Commit) SaveBlock(block *Block, blockParts *PartSet, seenCommit *Commit)
} }
//------------------------------------------------------
// state
// State defines the stateful interface used to verify evidence.
// UNSTABLE
type State interface {
VerifyEvidence(Evidence) (priority int64, err error)
}
//------------------------------------------------------ //------------------------------------------------------
// evidence pool // evidence pool


Loading…
Cancel
Save