Browse Source

state: define interface for state store (#5348)

## Description

Make an interface for the state store. 

Closes: #5213
pull/5358/head
Marko 4 years ago
committed by GitHub
parent
commit
56911ee352
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 494 additions and 326 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +7
    -3
      blockchain/v0/reactor_test.go
  3. +7
    -3
      blockchain/v1/reactor_test.go
  4. +12
    -6
      blockchain/v2/reactor_test.go
  5. +8
    -5
      consensus/byzantine_test.go
  6. +10
    -4
      consensus/common_test.go
  7. +7
    -2
      consensus/mempool_test.go
  8. +7
    -3
      consensus/reactor_test.go
  9. +9
    -9
      consensus/replay.go
  10. +3
    -2
      consensus/replay_file.go
  11. +39
    -26
      consensus/replay_test.go
  12. +1
    -1
      consensus/state.go
  13. +6
    -2
      consensus/wal_generator.go
  14. +7
    -5
      evidence/pool.go
  15. +12
    -7
      evidence/pool_test.go
  16. +4
    -3
      evidence/reactor_test.go
  17. +0
    -28
      evidence/services.go
  18. +1
    -1
      evidence/verify.go
  19. +15
    -6
      evidence/verify_test.go
  20. +19
    -13
      node/node.go
  21. +12
    -5
      node/node_test.go
  22. +1
    -2
      rpc/core/blocks.go
  23. +3
    -2
      rpc/core/blocks_test.go
  24. +2
    -3
      rpc/core/consensus.go
  25. +1
    -3
      rpc/core/env.go
  26. +1
    -2
      rpc/core/status.go
  27. +20
    -18
      state/execution.go
  28. +10
    -5
      state/execution_test.go
  29. +4
    -10
      state/export_test.go
  30. +7
    -2
      state/helpers_test.go
  31. +50
    -30
      state/state_test.go
  32. +166
    -89
      state/store.go
  33. +27
    -16
      state/store_test.go
  34. +2
    -1
      state/tx_filter_test.go
  35. +1
    -3
      state/validation.go
  36. +8
    -4
      state/validation_test.go
  37. +4
    -2
      store/store_test.go

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -22,6 +22,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
- [evidence] \#5319 Remove Amnesia & potentialAmnesia evidence types and removed POLC. (@marbar3778) - [evidence] \#5319 Remove Amnesia & potentialAmnesia evidence types and removed POLC. (@marbar3778)
- [params] \#5319 Remove `ProofofTrialPeriod` from evidence params (@marbar3778) - [params] \#5319 Remove `ProofofTrialPeriod` from evidence params (@marbar3778)
- [crypto/secp256k1] \#5280 `secp256k1` has been removed from the Tendermint repo. (@marbar3778) - [crypto/secp256k1] \#5280 `secp256k1` has been removed from the Tendermint repo. (@marbar3778)
- [state] \#5348 Define an Interface for the state store. (@marbar3778)
- Blockchain Protocol - Blockchain Protocol


+ 7
- 3
blockchain/v0/reactor_test.go View File

@ -70,9 +70,10 @@ func newBlockchainReactor(
blockDB := dbm.NewMemDB() blockDB := dbm.NewMemDB()
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB)
blockStore := store.NewBlockStore(blockDB) blockStore := store.NewBlockStore(blockDB)
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
if err != nil { if err != nil {
panic(fmt.Errorf("error constructing state from genesis file: %w", err)) panic(fmt.Errorf("error constructing state from genesis file: %w", err))
} }
@ -82,9 +83,12 @@ func newBlockchainReactor(
// pool.height is determined from the store. // pool.height is determined from the store.
fastSync := true fastSync := true
db := dbm.NewMemDB() db := dbm.NewMemDB()
blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(),
stateStore = sm.NewStore(db)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, sm.MockEvidencePool{}) mock.Mempool{}, sm.MockEvidencePool{})
sm.SaveState(db, state)
if err = stateStore.Save(state); err != nil {
panic(err)
}
// let's add some blocks in // let's add some blocks in
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {


+ 7
- 3
blockchain/v1/reactor_test.go View File

@ -102,9 +102,10 @@ func newBlockchainReactor(
blockDB := dbm.NewMemDB() blockDB := dbm.NewMemDB()
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB)
blockStore := store.NewBlockStore(blockDB) blockStore := store.NewBlockStore(blockDB)
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
if err != nil { if err != nil {
panic(fmt.Errorf("error constructing state from genesis file: %w", err)) panic(fmt.Errorf("error constructing state from genesis file: %w", err))
} }
@ -114,9 +115,12 @@ func newBlockchainReactor(
// pool.height is determined from the store. // pool.height is determined from the store.
fastSync := true fastSync := true
db := dbm.NewMemDB() db := dbm.NewMemDB()
blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(),
stateStore = sm.NewStore(db)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, sm.MockEvidencePool{}) mock.Mempool{}, sm.MockEvidencePool{})
sm.SaveState(db, state)
if err = stateStore.Save(state); err != nil {
panic(err)
}
// let's add some blocks in // let's add some blocks in
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {


+ 12
- 6
blockchain/v2/reactor_test.go View File

@ -156,8 +156,11 @@ func newTestReactor(p testReactorParams) *BlockchainReactor {
panic(fmt.Errorf("error start app: %w", err)) panic(fmt.Errorf("error start app: %w", err))
} }
db := dbm.NewMemDB() db := dbm.NewMemDB()
appl = sm.NewBlockExecutor(db, p.logger, proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
sm.SaveState(db, state)
stateStore := sm.NewStore(db)
appl = sm.NewBlockExecutor(stateStore, p.logger, proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
if err = stateStore.Save(state); err != nil {
panic(err)
}
} }
r := newReactor(state, store, reporter, appl, true) r := newReactor(state, store, reporter, appl, true)
@ -498,16 +501,19 @@ func newReactorStore(
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
blockStore := store.NewBlockStore(dbm.NewMemDB()) blockStore := store.NewBlockStore(dbm.NewMemDB())
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
if err != nil { if err != nil {
panic(fmt.Errorf("error constructing state from genesis file: %w", err)) panic(fmt.Errorf("error constructing state from genesis file: %w", err))
} }
db := dbm.NewMemDB() db := dbm.NewMemDB()
blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(),
stateStore = sm.NewStore(db)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, sm.MockEvidencePool{}) mock.Mempool{}, sm.MockEvidencePool{})
sm.SaveState(db, state)
if err = stateStore.Save(state); err != nil {
panic(err)
}
// add blocks in // add blocks in
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {


+ 8
- 5
consensus/byzantine_test.go View File

@ -45,7 +45,8 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
for i := 0; i < nValidators; i++ { for i := 0; i < nValidators; i++ {
logger := consensusLogger().With("test", "byzantine", "validator", i) logger := consensusLogger().With("test", "byzantine", "validator", i)
stateDB := dbm.NewMemDB() // each state needs its own db stateDB := dbm.NewMemDB() // each state needs its own db
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
defer os.RemoveAll(thisConfig.RootDir) defer os.RemoveAll(thisConfig.RootDir)
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
@ -70,12 +71,12 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
// Make a full instance of the evidence pool // Make a full instance of the evidence pool
evidenceDB := dbm.NewMemDB() evidenceDB := dbm.NewMemDB()
evpool, err := evidence.NewPool(evidenceDB, evidence.NewEvidenceStateStore(stateDB), blockStore)
evpool, err := evidence.NewPool(evidenceDB, stateStore, blockStore)
require.NoError(t, err) require.NoError(t, err)
evpool.SetLogger(logger.With("module", "evidence")) evpool.SetLogger(logger.With("module", "evidence"))
// Make State // Make State
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool) cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool)
cs.SetLogger(cs.Logger) cs.SetLogger(cs.Logger)
// set private validator // set private validator
@ -111,7 +112,8 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
blocksSubs = append(blocksSubs, blocksSub) blocksSubs = append(blocksSubs, blocksSub)
if css[i].state.LastBlockHeight == 0 { //simulate handle initChain in handshake if css[i].state.LastBlockHeight == 0 { //simulate handle initChain in handshake
sm.SaveState(css[i].blockExec.DB(), css[i].state)
err = css[i].blockExec.Store().Save(css[i].state)
require.NoError(t, err)
} }
} }
// make connected switches and start all reactors // make connected switches and start all reactors
@ -249,7 +251,8 @@ func TestByzantineConflictingProposalsWithPartition(t *testing.T) {
} }
reactors[i] = conRI reactors[i] = conRI
sm.SaveState(css[i].blockExec.DB(), css[i].state) //for save height 1's validators info
err = css[i].blockExec.Store().Save(css[i].state) //for save height 1's validators info
require.NoError(t, err)
} }
defer func() { defer func() {


+ 10
- 4
consensus/common_test.go View File

@ -390,8 +390,12 @@ func newStateWithConfigAndBlockStore(
// Make State // Make State
stateDB := blockDB stateDB := blockDB
sm.SaveState(stateDB, state) //for save height 1's validators info
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
stateStore := sm.NewStore(stateDB)
if err := stateStore.Save(state); err != nil { //for save height 1's validators info
panic(err)
}
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool) cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool)
cs.SetLogger(log.TestingLogger().With("module", "consensus")) cs.SetLogger(log.TestingLogger().With("module", "consensus"))
cs.SetPrivValidator(pv) cs.SetPrivValidator(pv)
@ -676,7 +680,8 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
configRootDirs := make([]string, 0, nValidators) configRootDirs := make([]string, 0, nValidators)
for i := 0; i < nValidators; i++ { for i := 0; i < nValidators; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db stateDB := dbm.NewMemDB() // each state needs its own db
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
configRootDirs = append(configRootDirs, thisConfig.RootDir) configRootDirs = append(configRootDirs, thisConfig.RootDir)
for _, opt := range configOpts { for _, opt := range configOpts {
@ -713,7 +718,8 @@ func randConsensusNetWithPeers(
configRootDirs := make([]string, 0, nPeers) configRootDirs := make([]string, 0, nPeers)
for i := 0; i < nPeers; i++ { for i := 0; i < nPeers; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db stateDB := dbm.NewMemDB() // each state needs its own db
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
configRootDirs = append(configRootDirs, thisConfig.RootDir) configRootDirs = append(configRootDirs, thisConfig.RootDir)
ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal


+ 7
- 2
consensus/mempool_test.go View File

@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db" dbm "github.com/tendermint/tm-db"
@ -112,8 +113,10 @@ func deliverTxsRange(cs *State, start, end int) {
func TestMempoolTxConcurrentWithCommit(t *testing.T) { func TestMempoolTxConcurrentWithCommit(t *testing.T) {
state, privVals := randGenesisState(1, false, 10) state, privVals := randGenesisState(1, false, 10)
blockDB := dbm.NewMemDB() blockDB := dbm.NewMemDB()
stateStore := sm.NewStore(blockDB)
cs := newStateWithConfigAndBlockStore(config, state, privVals[0], NewCounterApplication(), blockDB) cs := newStateWithConfigAndBlockStore(config, state, privVals[0], NewCounterApplication(), blockDB)
sm.SaveState(blockDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
newBlockHeaderCh := subscribe(cs.eventBus, types.EventQueryNewBlockHeader) newBlockHeaderCh := subscribe(cs.eventBus, types.EventQueryNewBlockHeader)
const numTxs int64 = 3000 const numTxs int64 = 3000
@ -135,8 +138,10 @@ func TestMempoolRmBadTx(t *testing.T) {
state, privVals := randGenesisState(1, false, 10) state, privVals := randGenesisState(1, false, 10)
app := NewCounterApplication() app := NewCounterApplication()
blockDB := dbm.NewMemDB() blockDB := dbm.NewMemDB()
stateStore := sm.NewStore(blockDB)
cs := newStateWithConfigAndBlockStore(config, state, privVals[0], app, blockDB) cs := newStateWithConfigAndBlockStore(config, state, privVals[0], app, blockDB)
sm.SaveState(blockDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
// increment the counter by 1 // increment the counter by 1
txBytes := make([]byte, 8) txBytes := make([]byte, 8)


+ 7
- 3
consensus/reactor_test.go View File

@ -64,7 +64,10 @@ func startConsensusNet(t *testing.T, css []*State, n int) (
blocksSubs = append(blocksSubs, blocksSub) blocksSubs = append(blocksSubs, blocksSub)
if css[i].state.LastBlockHeight == 0 { //simulate handle initChain in handshake if css[i].state.LastBlockHeight == 0 { //simulate handle initChain in handshake
sm.SaveState(css[i].blockExec.DB(), css[i].state)
if err := css[i].blockExec.Store().Save(css[i].state); err != nil {
t.Error(err)
}
} }
} }
// make connected switches and start all reactors // make connected switches and start all reactors
@ -131,7 +134,8 @@ func TestReactorWithEvidence(t *testing.T) {
logger := consensusLogger() logger := consensusLogger()
for i := 0; i < nValidators; i++ { for i := 0; i < nValidators; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db stateDB := dbm.NewMemDB() // each state needs its own db
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
defer os.RemoveAll(thisConfig.RootDir) defer os.RemoveAll(thisConfig.RootDir)
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
@ -164,7 +168,7 @@ func TestReactorWithEvidence(t *testing.T) {
evpool := newMockEvidencePool(privVals[vIdx]) evpool := newMockEvidencePool(privVals[vIdx])
// Make State // Make State
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool) cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool)
cs.SetLogger(log.TestingLogger().With("module", "consensus")) cs.SetLogger(log.TestingLogger().With("module", "consensus"))
cs.SetPrivValidator(pv) cs.SetPrivValidator(pv)


+ 9
- 9
consensus/replay.go View File

@ -8,8 +8,6 @@ import (
"reflect" "reflect"
"time" "time"
dbm "github.com/tendermint/tm-db"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
@ -200,7 +198,7 @@ func makeHeightSearchFunc(height int64) auto.SearchFunc {
//--------------------------------------------------- //---------------------------------------------------
type Handshaker struct { type Handshaker struct {
stateDB dbm.DB
stateStore sm.Store
initialState sm.State initialState sm.State
store sm.BlockStore store sm.BlockStore
eventBus types.BlockEventPublisher eventBus types.BlockEventPublisher
@ -210,11 +208,11 @@ type Handshaker struct {
nBlocks int // number of blocks applied to the state nBlocks int // number of blocks applied to the state
} }
func NewHandshaker(stateDB dbm.DB, state sm.State,
func NewHandshaker(stateStore sm.Store, state sm.State,
store sm.BlockStore, genDoc *types.GenesisDoc) *Handshaker { store sm.BlockStore, genDoc *types.GenesisDoc) *Handshaker {
return &Handshaker{ return &Handshaker{
stateDB: stateDB,
stateStore: stateStore,
initialState: state, initialState: state,
store: store, store: store,
eventBus: types.NopEventBus{}, eventBus: types.NopEventBus{},
@ -351,7 +349,9 @@ func (h *Handshaker) ReplayBlocks(
} }
// We update the last results hash with the empty hash, to conform with RFC-6962. // We update the last results hash with the empty hash, to conform with RFC-6962.
state.LastResultsHash = merkle.HashFromByteSlices(nil) state.LastResultsHash = merkle.HashFromByteSlices(nil)
sm.SaveState(h.stateDB, state)
if err := h.stateStore.Save(state); err != nil {
return nil, err
}
} }
} }
@ -418,7 +418,7 @@ func (h *Handshaker) ReplayBlocks(
case appBlockHeight == storeBlockHeight: case 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 := sm.LoadABCIResponses(h.stateDB, storeBlockHeight)
abciResponses, err := h.stateStore.LoadABCIResponses(storeBlockHeight)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -468,7 +468,7 @@ func (h *Handshaker) replayBlocks(
assertAppHashEqualsOneFromBlock(appHash, block) assertAppHashEqualsOneFromBlock(appHash, block)
} }
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateDB, h.genDoc.InitialHeight)
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateStore, h.genDoc.InitialHeight)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -496,7 +496,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap
// Use stubs for both mempool and evidence pool since no transactions nor // Use stubs for both mempool and evidence pool since no transactions nor
// evidence are needed here - block already exists. // evidence are needed here - block already exists.
blockExec := sm.NewBlockExecutor(h.stateDB, h.logger, proxyApp, emptyMempool{}, emptyEvidencePool{})
blockExec := sm.NewBlockExecutor(h.stateStore, h.logger, proxyApp, emptyMempool{}, emptyEvidencePool{})
blockExec.SetEventBus(h.eventBus) blockExec.SetEventBus(h.eventBus)
var err error var err error


+ 3
- 2
consensus/replay_file.go View File

@ -297,6 +297,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
if err != nil { if err != nil {
tmos.Exit(err.Error()) tmos.Exit(err.Error())
} }
stateStore := sm.NewStore(stateDB)
gdoc, err := sm.MakeGenesisDocFromFile(config.GenesisFile()) gdoc, err := sm.MakeGenesisDocFromFile(config.GenesisFile())
if err != nil { if err != nil {
tmos.Exit(err.Error()) tmos.Exit(err.Error())
@ -319,7 +320,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
tmos.Exit(fmt.Sprintf("Failed to start event bus: %v", err)) tmos.Exit(fmt.Sprintf("Failed to start event bus: %v", err))
} }
handshaker := NewHandshaker(stateDB, state, blockStore, gdoc)
handshaker := NewHandshaker(stateStore, state, blockStore, gdoc)
handshaker.SetEventBus(eventBus) handshaker.SetEventBus(eventBus)
err = handshaker.Handshake(proxyApp) err = handshaker.Handshake(proxyApp)
if err != nil { if err != nil {
@ -327,7 +328,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
} }
mempool, evpool := emptyMempool{}, emptyEvidencePool{} mempool, evpool := emptyMempool{}, emptyEvidencePool{}
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
consensusState := NewState(csConfig, state.Copy(), blockExec, consensusState := NewState(csConfig, state.Copy(), blockExec,
blockStore, mempool, evpool) blockStore, mempool, evpool)


+ 39
- 26
consensus/replay_test.go View File

@ -65,9 +65,9 @@ func TestMain(m *testing.M) {
// wal writer when we need to, instead of with every message. // wal writer when we need to, instead of with every message.
func startNewStateAndWaitForBlock(t *testing.T, consensusReplayConfig *cfg.Config, func startNewStateAndWaitForBlock(t *testing.T, consensusReplayConfig *cfg.Config,
lastBlockHeight int64, blockDB dbm.DB, stateDB dbm.DB) {
lastBlockHeight int64, blockDB dbm.DB, stateStore sm.Store) {
logger := log.TestingLogger() logger := log.TestingLogger()
state, _ := sm.LoadStateFromDBOrGenesisFile(stateDB, consensusReplayConfig.GenesisFile())
state, _ := stateStore.LoadFromDBOrGenesisFile(consensusReplayConfig.GenesisFile())
privValidator := loadPrivValidator(consensusReplayConfig) privValidator := loadPrivValidator(consensusReplayConfig)
cs := newStateWithConfigAndBlockStore( cs := newStateWithConfigAndBlockStore(
consensusReplayConfig, consensusReplayConfig,
@ -159,6 +159,7 @@ LOOP:
logger := log.NewNopLogger() logger := log.NewNopLogger()
blockDB := dbm.NewMemDB() blockDB := dbm.NewMemDB()
stateDB := blockDB stateDB := blockDB
stateStore := sm.NewStore(stateDB)
state, err := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile()) state, err := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile())
require.NoError(t, err) require.NoError(t, err)
privValidator := loadPrivValidator(consensusReplayConfig) privValidator := loadPrivValidator(consensusReplayConfig)
@ -199,7 +200,7 @@ LOOP:
t.Logf("WAL panicked: %v", err) t.Logf("WAL panicked: %v", err)
// make sure we can make blocks after a crash // make sure we can make blocks after a crash
startNewStateAndWaitForBlock(t, consensusReplayConfig, cs.Height, blockDB, stateDB)
startNewStateAndWaitForBlock(t, consensusReplayConfig, cs.Height, blockDB, stateStore)
// stop consensus state and transactions sender (initFn) // stop consensus state and transactions sender (initFn)
cs.Stop() //nolint:errcheck // Logging this error causes failure cs.Stop() //nolint:errcheck // Logging this error causes failure
@ -669,6 +670,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
testConfig := ResetConfig(fmt.Sprintf("%s_%v_m", t.Name(), mode)) testConfig := ResetConfig(fmt.Sprintf("%s_%v_m", t.Name(), mode))
defer os.RemoveAll(testConfig.RootDir) defer os.RemoveAll(testConfig.RootDir)
stateDB = dbm.NewMemDB() stateDB = dbm.NewMemDB()
genisisState = sim.GenesisState genisisState = sim.GenesisState
config = sim.Config config = sim.Config
chain = append([]*types.Block{}, sim.Chain...) // copy chain chain = append([]*types.Block{}, sim.Chain...) // copy chain
@ -701,12 +703,13 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
stateDB, genisisState, store = stateAndStore(config, pubKey, kvstore.ProtocolVersion) stateDB, genisisState, store = stateAndStore(config, pubKey, kvstore.ProtocolVersion)
} }
stateStore := sm.NewStore(stateDB)
store.chain = chain store.chain = chain
store.commits = commits store.commits = commits
state := genisisState.Copy() state := genisisState.Copy()
// run the chain through state.ApplyBlock to build up the tendermint state // run the chain through state.ApplyBlock to build up the tendermint state
state = buildTMStateFromChain(config, stateDB, state, chain, nBlocks, mode)
state = buildTMStateFromChain(config, stateStore, state, chain, nBlocks, mode)
latestAppHash := state.AppHash latestAppHash := state.AppHash
// make a new client creator // make a new client creator
@ -719,8 +722,10 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
// use a throwaway tendermint state // use a throwaway tendermint state
proxyApp := proxy.NewAppConns(clientCreator2) proxyApp := proxy.NewAppConns(clientCreator2)
stateDB1 := dbm.NewMemDB() stateDB1 := dbm.NewMemDB()
sm.SaveState(stateDB1, genisisState)
buildAppStateFromChain(proxyApp, stateDB1, genisisState, chain, nBlocks, mode)
stateStore := sm.NewStore(stateDB1)
err := stateStore.Save(genisisState)
require.NoError(t, err)
buildAppStateFromChain(proxyApp, stateStore, genisisState, chain, nBlocks, mode)
} }
// Prune block store if requested // Prune block store if requested
@ -734,7 +739,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
// now start the app using the handshake - it should sync // now start the app using the handshake - it should sync
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
handshaker := NewHandshaker(stateDB, state, store, genDoc)
handshaker := NewHandshaker(stateStore, state, store, genDoc)
proxyApp := proxy.NewAppConns(clientCreator2) proxyApp := proxy.NewAppConns(clientCreator2)
if err := proxyApp.Start(); err != nil { if err := proxyApp.Start(); err != nil {
t.Fatalf("Error starting proxy app connections: %v", err) t.Fatalf("Error starting proxy app connections: %v", err)
@ -780,9 +785,9 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
} }
} }
func applyBlock(stateDB dbm.DB, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State {
func applyBlock(stateStore sm.Store, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State {
testPartSize := types.BlockPartSizeBytes testPartSize := types.BlockPartSizeBytes
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
blkID := types.BlockID{Hash: blk.Hash(), PartSetHeader: blk.MakePartSet(testPartSize).Header()} blkID := types.BlockID{Hash: blk.Hash(), PartSetHeader: blk.MakePartSet(testPartSize).Header()}
newState, _, err := blockExec.ApplyBlock(st, blkID, blk) newState, _, err := blockExec.ApplyBlock(st, blkID, blk)
@ -792,7 +797,7 @@ func applyBlock(stateDB dbm.DB, st sm.State, blk *types.Block, proxyApp proxy.Ap
return newState return newState
} }
func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
func buildAppStateFromChain(proxyApp proxy.AppConns, stateStore sm.Store,
state sm.State, chain []*types.Block, nBlocks int, mode uint) { state sm.State, chain []*types.Block, nBlocks int, mode uint) {
// start a new app without handshake, play nBlocks blocks // start a new app without handshake, play nBlocks blocks
if err := proxyApp.Start(); err != nil { if err := proxyApp.Start(); err != nil {
@ -807,24 +812,25 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
}); err != nil { }); err != nil {
panic(err) panic(err)
} }
sm.SaveState(stateDB, state) //save height 1's validatorsInfo
if err := stateStore.Save(state); err != nil { //save height 1's validatorsInfo
panic(err)
}
switch mode { switch mode {
case 0: case 0:
for i := 0; i < nBlocks; i++ { for i := 0; i < nBlocks; i++ {
block := chain[i] block := chain[i]
state = applyBlock(stateDB, state, block, proxyApp)
state = applyBlock(stateStore, state, block, proxyApp)
} }
case 1, 2, 3: case 1, 2, 3:
for i := 0; i < nBlocks-1; i++ { for i := 0; i < nBlocks-1; i++ {
block := chain[i] block := chain[i]
state = applyBlock(stateDB, state, block, proxyApp)
state = applyBlock(stateStore, state, block, proxyApp)
} }
if mode == 2 || mode == 3 { if mode == 2 || mode == 3 {
// update the kvstore height and apphash // update the kvstore height and apphash
// as if we ran commit but not // as if we ran commit but not
state = applyBlock(stateDB, state, chain[nBlocks-1], proxyApp)
state = applyBlock(stateStore, state, chain[nBlocks-1], proxyApp)
} }
default: default:
panic(fmt.Sprintf("unknown mode %v", mode)) panic(fmt.Sprintf("unknown mode %v", mode))
@ -834,7 +840,7 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
func buildTMStateFromChain( func buildTMStateFromChain(
config *cfg.Config, config *cfg.Config,
stateDB dbm.DB,
stateStore sm.Store,
state sm.State, state sm.State,
chain []*types.Block, chain []*types.Block,
nBlocks int, nBlocks int,
@ -856,25 +862,26 @@ func buildTMStateFromChain(
}); err != nil { }); err != nil {
panic(err) panic(err)
} }
sm.SaveState(stateDB, state) //save height 1's validatorsInfo
if err := stateStore.Save(state); err != nil { //save height 1's validatorsInfo
panic(err)
}
switch mode { switch mode {
case 0: case 0:
// sync right up // sync right up
for _, block := range chain { for _, block := range chain {
state = applyBlock(stateDB, state, block, proxyApp)
state = applyBlock(stateStore, state, block, proxyApp)
} }
case 1, 2, 3: case 1, 2, 3:
// sync up to the penultimate as if we stored the block. // sync up to the penultimate as if we stored the block.
// whether we commit or not depends on the appHash // whether we commit or not depends on the appHash
for _, block := range chain[:len(chain)-1] { for _, block := range chain[:len(chain)-1] {
state = applyBlock(stateDB, state, block, proxyApp)
state = applyBlock(stateStore, state, block, proxyApp)
} }
// apply the final block to a state copy so we can // apply the final block to a state copy so we can
// get the right next appHash but keep the state back // get the right next appHash but keep the state back
applyBlock(stateDB, state, chain[len(chain)-1], proxyApp)
applyBlock(stateStore, state, chain[len(chain)-1], proxyApp)
default: default:
panic(fmt.Sprintf("unknown mode %v", mode)) panic(fmt.Sprintf("unknown mode %v", mode))
} }
@ -894,6 +901,7 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) {
pubKey, err := privVal.GetPubKey() pubKey, err := privVal.GetPubKey()
require.NoError(t, err) require.NoError(t, err)
stateDB, state, store := stateAndStore(config, pubKey, appVersion) stateDB, state, store := stateAndStore(config, pubKey, appVersion)
stateStore := sm.NewStore(stateDB)
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
state.LastValidators = state.Validators.Copy() state.LastValidators = state.Validators.Copy()
// mode = 0 for committing all the blocks // mode = 0 for committing all the blocks
@ -917,7 +925,7 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) {
}) })
assert.Panics(t, func() { assert.Panics(t, func() {
h := NewHandshaker(stateDB, state, store, genDoc)
h := NewHandshaker(stateStore, state, store, genDoc)
if err = h.Handshake(proxyApp); err != nil { if err = h.Handshake(proxyApp); err != nil {
t.Log(err) t.Log(err)
} }
@ -941,7 +949,7 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) {
}) })
assert.Panics(t, func() { assert.Panics(t, func() {
h := NewHandshaker(stateDB, state, store, genDoc)
h := NewHandshaker(stateStore, state, store, genDoc)
if err = h.Handshake(proxyApp); err != nil { if err = h.Handshake(proxyApp); err != nil {
t.Log(err) t.Log(err)
} }
@ -1150,10 +1158,13 @@ func stateAndStore(
pubKey crypto.PubKey, pubKey crypto.PubKey,
appVersion uint64) (dbm.DB, sm.State, *mockBlockStore) { appVersion uint64) (dbm.DB, sm.State, *mockBlockStore) {
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB)
state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile()) state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile())
state.Version.Consensus.App = appVersion state.Version.Consensus.App = appVersion
store := newMockBlockStore(config, state.ConsensusParams) store := newMockBlockStore(config, state.ConsensusParams)
sm.SaveState(stateDB, state)
if err := stateStore.Save(state); err != nil {
panic(err)
}
return stateDB, state, store return stateDB, state, store
} }
@ -1223,12 +1234,13 @@ func TestHandshakeUpdatesValidators(t *testing.T) {
pubKey, err := privVal.GetPubKey() pubKey, err := privVal.GetPubKey()
require.NoError(t, err) require.NoError(t, err)
stateDB, state, store := stateAndStore(config, pubKey, 0x0) stateDB, state, store := stateAndStore(config, pubKey, 0x0)
stateStore := sm.NewStore(stateDB)
oldValAddr := state.Validators.Validators[0].Address oldValAddr := state.Validators.Validators[0].Address
// now start the app using the handshake - it should sync // now start the app using the handshake - it should sync
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
handshaker := NewHandshaker(stateDB, state, store, genDoc)
handshaker := NewHandshaker(stateStore, state, store, genDoc)
proxyApp := proxy.NewAppConns(clientCreator) proxyApp := proxy.NewAppConns(clientCreator)
if err := proxyApp.Start(); err != nil { if err := proxyApp.Start(); err != nil {
t.Fatalf("Error starting proxy app connections: %v", err) t.Fatalf("Error starting proxy app connections: %v", err)
@ -1242,7 +1254,8 @@ func TestHandshakeUpdatesValidators(t *testing.T) {
t.Fatalf("Error on abci handshake: %v", err) t.Fatalf("Error on abci handshake: %v", err)
} }
// reload the state, check the validator set was updated // reload the state, check the validator set was updated
state = sm.LoadState(stateDB)
state, err = stateStore.Load()
require.NoError(t, err)
newValAddr := state.Validators.Validators[0].Address newValAddr := state.Validators.Validators[0].Address
expectValAddr := val.Address expectValAddr := val.Address


+ 1
- 1
consensus/state.go View File

@ -1617,7 +1617,7 @@ func (cs *State) pruneBlocks(retainHeight int64) (uint64, error) {
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to prune block store: %w", err) return 0, fmt.Errorf("failed to prune block store: %w", err)
} }
err = sm.PruneStates(cs.blockExec.DB(), base, retainHeight)
err = cs.blockExec.Store().PruneStates(base, retainHeight)
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to prune state database: %w", err) return 0, fmt.Errorf("failed to prune state database: %w", err)
} }


+ 6
- 2
consensus/wal_generator.go View File

@ -48,12 +48,16 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) {
} }
blockStoreDB := db.NewMemDB() blockStoreDB := db.NewMemDB()
stateDB := blockStoreDB stateDB := blockStoreDB
stateStore := sm.NewStore(stateDB)
state, err := sm.MakeGenesisState(genDoc) state, err := sm.MakeGenesisState(genDoc)
if err != nil { if err != nil {
return fmt.Errorf("failed to make genesis state: %w", err) return fmt.Errorf("failed to make genesis state: %w", err)
} }
state.Version.Consensus.App = kvstore.ProtocolVersion state.Version.Consensus.App = kvstore.ProtocolVersion
sm.SaveState(stateDB, state)
if err = stateStore.Save(state); err != nil {
t.Error(err)
}
blockStore := store.NewBlockStore(blockStoreDB) blockStore := store.NewBlockStore(blockStoreDB)
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app)) proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app))
@ -79,7 +83,7 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) {
}) })
mempool := emptyMempool{} mempool := emptyMempool{}
evpool := emptyEvidencePool{} evpool := emptyEvidencePool{}
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
consensusState := NewState(config.Consensus, state.Copy(), blockExec, blockStore, mempool, evpool) consensusState := NewState(config.Consensus, state.Copy(), blockExec, blockStore, mempool, evpool)
consensusState.SetLogger(logger) consensusState.SetLogger(logger)
consensusState.SetEventBus(eventBus) consensusState.SetEventBus(eventBus)


+ 7
- 5
evidence/pool.go View File

@ -30,7 +30,7 @@ type Pool struct {
evidenceList *clist.CList // concurrent linked-list of evidence evidenceList *clist.CList // concurrent linked-list of evidence
// needed to load validators to verify evidence // needed to load validators to verify evidence
stateDB StateStore
stateDB sm.Store
// needed to load headers to verify evidence // needed to load headers to verify evidence
blockStore BlockStore blockStore BlockStore
@ -41,10 +41,12 @@ type Pool struct {
// NewPool creates an evidence pool. If using an existing evidence store, // NewPool creates an evidence pool. If using an existing evidence store,
// it will add all pending evidence to the concurrent list. // it will add all pending evidence to the concurrent list.
func NewPool(evidenceDB dbm.DB, stateDB StateStore, blockStore BlockStore) (*Pool, error) {
var (
state = stateDB.LoadState()
)
func NewPool(evidenceDB dbm.DB, stateDB sm.Store, blockStore BlockStore) (*Pool, error) {
state, err := stateDB.Load()
if err != nil {
return nil, fmt.Errorf("cannot load state: %w", err)
}
pool := &Pool{ pool := &Pool{
stateDB: stateDB, stateDB: stateDB,


+ 12
- 7
evidence/pool_test.go View File

@ -199,15 +199,16 @@ func TestRecoverPendingEvidence(t *testing.T) {
stateStore = initializeValidatorState(val, height) stateStore = initializeValidatorState(val, height)
evidenceDB = dbm.NewMemDB() evidenceDB = dbm.NewMemDB()
blockStoreDB = dbm.NewMemDB() blockStoreDB = dbm.NewMemDB()
state = stateStore.LoadState()
blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
expiredEvidenceTime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC) expiredEvidenceTime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height,
defaultEvidenceTime, val, evidenceChainID) defaultEvidenceTime, val, evidenceChainID)
expiredEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(int64(1), expiredEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(int64(1),
expiredEvidenceTime, val, evidenceChainID) expiredEvidenceTime, val, evidenceChainID)
) )
state, err := stateStore.Load()
require.NoError(t, err)
blockStore := initializeBlockStore(blockStoreDB, state, valAddr)
// load good evidence // load good evidence
goodKey := keyPending(goodEvidence) goodKey := keyPending(goodEvidence)
evi, err := types.EvidenceToProto(goodEvidence) evi, err := types.EvidenceToProto(goodEvidence)
@ -232,8 +233,9 @@ func TestRecoverPendingEvidence(t *testing.T) {
assert.False(t, pool.Has(expiredEvidence)) assert.False(t, pool.Has(expiredEvidence))
} }
func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) StateStore {
func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) sm.Store {
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB)
state := sm.State{ state := sm.State{
ChainID: evidenceChainID, ChainID: evidenceChainID,
InitialHeight: 1, InitialHeight: 1,
@ -259,13 +261,15 @@ func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) S
// save all states up to height // save all states up to height
for i := int64(0); i <= height; i++ { for i := int64(0); i <= height; i++ {
state.LastBlockHeight = i state.LastBlockHeight = i
sm.SaveState(stateDB, state)
if err := stateStore.Save(state); err != nil {
panic(err)
}
} }
return &stateStore{db: stateDB}
return stateStore
} }
func initializeValidatorState(privVal types.PrivValidator, height int64) StateStore {
func initializeValidatorState(privVal types.PrivValidator, height int64) sm.Store {
pubKey, _ := privVal.GetPubKey() pubKey, _ := privVal.GetPubKey()
validator := &types.Validator{Address: pubKey.Address(), VotingPower: 0, PubKey: pubKey} validator := &types.Validator{Address: pubKey.Address(), VotingPower: 0, PubKey: pubKey}
@ -314,7 +318,8 @@ func defaultTestPool(height int64) (*Pool, types.MockPV) {
valAddress := val.PrivKey.PubKey().Address() valAddress := val.PrivKey.PubKey().Address()
evidenceDB := dbm.NewMemDB() evidenceDB := dbm.NewMemDB()
stateStore := initializeValidatorState(val, height) stateStore := initializeValidatorState(val, height)
blockStore := initializeBlockStore(dbm.NewMemDB(), stateStore.LoadState(), valAddress)
state, _ := stateStore.Load()
blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress)
pool, err := NewPool(evidenceDB, stateStore, blockStore) pool, err := NewPool(evidenceDB, stateStore, blockStore)
if err != nil { if err != nil {
panic("test evidence pool could not be created") panic("test evidence pool could not be created")


+ 4
- 3
evidence/reactor_test.go View File

@ -22,6 +22,7 @@ import (
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
ep "github.com/tendermint/tendermint/proto/tendermint/evidence" ep "github.com/tendermint/tendermint/proto/tendermint/evidence"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -39,7 +40,7 @@ func evidenceLogger() log.Logger {
} }
// connect N evidence reactors through N switches // connect N evidence reactors through N switches
func makeAndConnectReactors(config *cfg.Config, stateStores []StateStore) []*Reactor {
func makeAndConnectReactors(config *cfg.Config, stateStores []sm.Store) []*Reactor {
N := len(stateStores) N := len(stateStores)
reactors := make([]*Reactor, N) reactors := make([]*Reactor, N)
@ -143,7 +144,7 @@ func TestReactorBroadcastEvidence(t *testing.T) {
N := 7 N := 7
// create statedb for everyone // create statedb for everyone
stateDBs := make([]StateStore, N)
stateDBs := make([]sm.Store, N)
val := types.NewMockPV() val := types.NewMockPV()
// we need validators saved for heights at least as high as we have evidence for // we need validators saved for heights at least as high as we have evidence for
height := int64(numEvidence) + 10 height := int64(numEvidence) + 10
@ -188,7 +189,7 @@ func TestReactorSelectiveBroadcast(t *testing.T) {
stateDB2 := initializeValidatorState(val, height2) stateDB2 := initializeValidatorState(val, height2)
// make reactors from statedb // make reactors from statedb
reactors := makeAndConnectReactors(config, []StateStore{stateDB1, stateDB2})
reactors := makeAndConnectReactors(config, []sm.Store{stateDB1, stateDB2})
// set the peer height on each reactor // set the peer height on each reactor
for _, r := range reactors { for _, r := range reactors {


+ 0
- 28
evidence/services.go View File

@ -1,9 +1,6 @@
package evidence package evidence
import ( import (
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -12,28 +9,3 @@ import (
type BlockStore interface { type BlockStore interface {
LoadBlockMeta(height int64) *types.BlockMeta LoadBlockMeta(height int64) *types.BlockMeta
} }
type StateStore interface {
LoadValidators(height int64) (*types.ValidatorSet, error)
LoadState() state.State
}
type stateStore struct {
db dbm.DB
}
var _ StateStore = &stateStore{}
// This is a temporary measure until stateDB becomes a store
// TODO: deprecate once state has a store
func NewEvidenceStateStore(db dbm.DB) StateStore {
return &stateStore{db}
}
func (s *stateStore) LoadValidators(height int64) (*types.ValidatorSet, error) {
return state.LoadValidators(s.db, height)
}
func (s *stateStore) LoadState() state.State {
return state.LoadState(s.db)
}

+ 1
- 1
evidence/verify.go View File

@ -12,7 +12,7 @@ import (
// - it is from a key who was a validator at the given height // - it is from a key who was a validator at the given height
// - it is internally consistent // - it is internally consistent
// - it was properly signed by the alleged equivocator // - it was properly signed by the alleged equivocator
func VerifyEvidence(evidence types.Evidence, state sm.State, stateDB StateStore, blockStore BlockStore) error {
func VerifyEvidence(evidence types.Evidence, state sm.State, stateDB sm.Store, blockStore BlockStore) error {
var ( var (
height = state.LastBlockHeight height = state.LastBlockHeight
evidenceParams = state.ConsensusParams.Evidence evidenceParams = state.ConsensusParams.Evidence


+ 15
- 6
evidence/verify_test.go View File

@ -16,13 +16,16 @@ func TestVerifyEvidenceWrongAddress(t *testing.T) {
var height int64 = 4 var height int64 = 4
val := types.NewMockPV() val := types.NewMockPV()
stateStore := initializeValidatorState(val, height) stateStore := initializeValidatorState(val, height)
state := stateStore.LoadState()
state, err := stateStore.Load()
if err != nil {
t.Error(err)
}
blockStore := &mocks.BlockStore{} blockStore := &mocks.BlockStore{}
blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
&types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}}, &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}},
) )
evidence := types.NewMockDuplicateVoteEvidence(1, defaultEvidenceTime, evidenceChainID) evidence := types.NewMockDuplicateVoteEvidence(1, defaultEvidenceTime, evidenceChainID)
err := VerifyEvidence(evidence, state, stateStore, blockStore)
err = VerifyEvidence(evidence, state, stateStore, blockStore)
errMsg := fmt.Sprintf("address %X was not a validator at height 1", evidence.Address()) errMsg := fmt.Sprintf("address %X was not a validator at height 1", evidence.Address())
if assert.Error(t, err) { if assert.Error(t, err) {
assert.Equal(t, err.Error(), errMsg) assert.Equal(t, err.Error(), errMsg)
@ -33,7 +36,10 @@ func TestVerifyEvidenceExpiredEvidence(t *testing.T) {
var height int64 = 4 var height int64 = 4
val := types.NewMockPV() val := types.NewMockPV()
stateStore := initializeValidatorState(val, height) stateStore := initializeValidatorState(val, height)
state := stateStore.LoadState()
state, err := stateStore.Load()
if err != nil {
t.Error(err)
}
state.ConsensusParams.Evidence.MaxAgeNumBlocks = 1 state.ConsensusParams.Evidence.MaxAgeNumBlocks = 1
expiredEvidenceTime := time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC) expiredEvidenceTime := time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
blockStore := &mocks.BlockStore{} blockStore := &mocks.BlockStore{}
@ -42,7 +48,7 @@ func TestVerifyEvidenceExpiredEvidence(t *testing.T) {
) )
expiredEv := types.NewMockDuplicateVoteEvidenceWithValidator(1, expiredEvidenceTime, val, evidenceChainID) expiredEv := types.NewMockDuplicateVoteEvidenceWithValidator(1, expiredEvidenceTime, val, evidenceChainID)
err := VerifyEvidence(expiredEv, state, stateStore, blockStore)
err = VerifyEvidence(expiredEv, state, stateStore, blockStore)
errMsg := "evidence from height 1 (created at: 2018-01-01 00:00:00 +0000 UTC) is too old" errMsg := "evidence from height 1 (created at: 2018-01-01 00:00:00 +0000 UTC) is too old"
if assert.Error(t, err) { if assert.Error(t, err) {
assert.Equal(t, err.Error()[:len(errMsg)], errMsg) assert.Equal(t, err.Error()[:len(errMsg)], errMsg)
@ -53,7 +59,10 @@ func TestVerifyEvidenceInvalidTime(t *testing.T) {
height := int64(4) height := int64(4)
val := types.NewMockPV() val := types.NewMockPV()
stateStore := initializeValidatorState(val, height) stateStore := initializeValidatorState(val, height)
state := stateStore.LoadState()
state, err := stateStore.Load()
if err != nil {
t.Error(err)
}
blockStore := &mocks.BlockStore{} blockStore := &mocks.BlockStore{}
blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
&types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}}, &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}},
@ -61,7 +70,7 @@ func TestVerifyEvidenceInvalidTime(t *testing.T) {
differentTime := time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC) differentTime := time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC)
ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, differentTime, val, evidenceChainID) ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, differentTime, val, evidenceChainID)
err := VerifyEvidence(ev, state, stateStore, blockStore)
err = VerifyEvidence(ev, state, stateStore, blockStore)
errMsg := "evidence time (2019-02-01 00:00:00 +0000 UTC) is different to the time" + errMsg := "evidence time (2019-02-01 00:00:00 +0000 UTC) is different to the time" +
" of the header we have for the same height (2019-01-01 00:00:00 +0000 UTC)" " of the header we have for the same height (2019-01-01 00:00:00 +0000 UTC)"
if assert.Error(t, err) { if assert.Error(t, err) {


+ 19
- 13
node/node.go View File

@ -185,7 +185,7 @@ type Node struct {
// services // services
eventBus *types.EventBus // pub/sub for services eventBus *types.EventBus // pub/sub for services
stateDB dbm.DB
stateStore sm.Store
blockStore *store.BlockStore // store the blockchain to disk blockStore *store.BlockStore // store the blockchain to disk
bcReactor p2p.Reactor // for fast-syncing bcReactor p2p.Reactor // for fast-syncing
mempoolReactor *mempl.Reactor // for gossipping transactions mempoolReactor *mempl.Reactor // for gossipping transactions
@ -263,7 +263,7 @@ func createAndStartIndexerService(config *cfg.Config, dbProvider DBProvider,
} }
func doHandshake( func doHandshake(
stateDB dbm.DB,
stateStore sm.Store,
state sm.State, state sm.State,
blockStore sm.BlockStore, blockStore sm.BlockStore,
genDoc *types.GenesisDoc, genDoc *types.GenesisDoc,
@ -271,7 +271,7 @@ func doHandshake(
proxyApp proxy.AppConns, proxyApp proxy.AppConns,
consensusLogger log.Logger) error { consensusLogger log.Logger) error {
handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc)
handshaker := cs.NewHandshaker(stateStore, state, blockStore, genDoc)
handshaker.SetLogger(consensusLogger) handshaker.SetLogger(consensusLogger)
handshaker.SetEventBus(eventBus) handshaker.SetEventBus(eventBus)
if err := handshaker.Handshake(proxyApp); err != nil { if err := handshaker.Handshake(proxyApp); err != nil {
@ -342,7 +342,7 @@ func createEvidenceReactor(config *cfg.Config, dbProvider DBProvider,
return nil, nil, err return nil, nil, err
} }
evidenceLogger := logger.With("module", "evidence") evidenceLogger := logger.With("module", "evidence")
evidencePool, err := evidence.NewPool(evidenceDB, evidence.NewEvidenceStateStore(stateDB), blockStore)
evidencePool, err := evidence.NewPool(evidenceDB, sm.NewStore(stateDB), blockStore)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -559,7 +559,7 @@ func createPEXReactorAndAddToSwitch(addrBook pex.AddrBook, config *cfg.Config,
// startStateSync starts an asynchronous state sync process, then switches to fast sync mode. // startStateSync starts an asynchronous state sync process, then switches to fast sync mode.
func startStateSync(ssR *statesync.Reactor, bcR fastSyncReactor, conR *cs.Reactor, func startStateSync(ssR *statesync.Reactor, bcR fastSyncReactor, conR *cs.Reactor,
stateProvider statesync.StateProvider, config *cfg.StateSyncConfig, fastSync bool, stateProvider statesync.StateProvider, config *cfg.StateSyncConfig, fastSync bool,
stateDB dbm.DB, blockStore *store.BlockStore, state sm.State) error {
stateStore sm.Store, blockStore *store.BlockStore, state sm.State) error {
ssR.Logger.Info("Starting state sync") ssR.Logger.Info("Starting state sync")
if stateProvider == nil { if stateProvider == nil {
@ -582,7 +582,7 @@ func startStateSync(ssR *statesync.Reactor, bcR fastSyncReactor, conR *cs.Reacto
ssR.Logger.Error("State sync failed", "err", err) ssR.Logger.Error("State sync failed", "err", err)
return return
} }
err = sm.BootstrapState(stateDB, state)
err = stateStore.Bootstrap(state)
if err != nil { if err != nil {
ssR.Logger.Error("Failed to bootstrap node with new state", "err", err) ssR.Logger.Error("Failed to bootstrap node with new state", "err", err)
return return
@ -625,6 +625,8 @@ func NewNode(config *cfg.Config,
return nil, err return nil, err
} }
stateStore := sm.NewStore(stateDB)
state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider) state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider)
if err != nil { if err != nil {
return nil, err return nil, err
@ -677,14 +679,17 @@ func NewNode(config *cfg.Config,
// and replays any blocks as necessary to sync tendermint with the app. // and replays any blocks as necessary to sync tendermint with the app.
consensusLogger := logger.With("module", "consensus") consensusLogger := logger.With("module", "consensus")
if !stateSync { if !stateSync {
if err := doHandshake(stateDB, state, blockStore, genDoc, eventBus, proxyApp, consensusLogger); err != nil {
if err := doHandshake(stateStore, state, blockStore, genDoc, eventBus, proxyApp, consensusLogger); err != nil {
return nil, err return nil, err
} }
// Reload the state. It will have the Version.Consensus.App set by the // Reload the state. It will have the Version.Consensus.App set by the
// Handshake, and may have other modifications as well (ie. depending on // Handshake, and may have other modifications as well (ie. depending on
// what happened during block replay). // what happened during block replay).
state = sm.LoadState(stateDB)
state, err = stateStore.Load()
if err != nil {
return nil, fmt.Errorf("cannot load state: %w", err)
}
} }
// Determine whether we should do fast sync. This must happen after the handshake, since the // Determine whether we should do fast sync. This must happen after the handshake, since the
@ -706,7 +711,7 @@ func NewNode(config *cfg.Config,
// make block executor for consensus and blockchain reactors to execute blocks // make block executor for consensus and blockchain reactors to execute blocks
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
logger.With("module", "state"), logger.With("module", "state"),
proxyApp.Consensus(), proxyApp.Consensus(),
mempool, mempool,
@ -805,7 +810,7 @@ func NewNode(config *cfg.Config,
nodeInfo: nodeInfo, nodeInfo: nodeInfo,
nodeKey: nodeKey, nodeKey: nodeKey,
stateDB: stateDB,
stateStore: stateStore,
blockStore: blockStore, blockStore: blockStore,
bcReactor: bcReactor, bcReactor: bcReactor,
mempoolReactor: mempoolReactor, mempoolReactor: mempoolReactor,
@ -895,7 +900,7 @@ func (n *Node) OnStart() error {
return fmt.Errorf("this blockchain reactor does not support switching from state sync") return fmt.Errorf("this blockchain reactor does not support switching from state sync")
} }
err := startStateSync(n.stateSyncReactor, bcR, n.consensusReactor, n.stateSyncProvider, err := startStateSync(n.stateSyncReactor, bcR, n.consensusReactor, n.stateSyncProvider,
n.config.StateSync, n.config.FastSyncMode, n.stateDB, n.blockStore, n.stateSyncGenesis)
n.config.StateSync, n.config.FastSyncMode, n.stateStore, n.blockStore, n.stateSyncGenesis)
if err != nil { if err != nil {
return fmt.Errorf("failed to start state sync: %w", err) return fmt.Errorf("failed to start state sync: %w", err)
} }
@ -966,7 +971,7 @@ func (n *Node) ConfigureRPC() error {
ProxyAppQuery: n.proxyApp.Query(), ProxyAppQuery: n.proxyApp.Query(),
ProxyAppMempool: n.proxyApp.Mempool(), ProxyAppMempool: n.proxyApp.Mempool(),
StateDB: n.stateDB,
StateStore: n.stateStore,
BlockStore: n.blockStore, BlockStore: n.blockStore,
EvidencePool: n.evidencePool, EvidencePool: n.evidencePool,
ConsensusState: n.consensusState, ConsensusState: n.consensusState,
@ -1298,7 +1303,8 @@ func LoadStateFromDBOrGenesisDocProvider(
return sm.State{}, nil, err return sm.State{}, nil, err
} }
} }
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
if err != nil { if err != nil {
return sm.State{}, nil, err return sm.State{}, nil, err
} }


+ 12
- 5
node/node_test.go View File

@ -123,7 +123,8 @@ func TestNodeSetAppVersion(t *testing.T) {
var appVersion uint64 = kvstore.ProtocolVersion var appVersion uint64 = kvstore.ProtocolVersion
// check version is set in state // check version is set in state
state := sm.LoadState(n.stateDB)
state, err := n.stateStore.Load()
require.NoError(t, err)
assert.Equal(t, state.Version.Consensus.App, appVersion) assert.Equal(t, state.Version.Consensus.App, appVersion)
// check version is set in node info // check version is set in node info
@ -231,6 +232,7 @@ func TestCreateProposalBlock(t *testing.T) {
var height int64 = 1 var height int64 = 1
state, stateDB, privVals := state(1, height) state, stateDB, privVals := state(1, height)
stateStore := sm.NewStore(stateDB)
maxBytes := 16384 maxBytes := 16384
maxEvidence := 10 maxEvidence := 10
state.ConsensusParams.Block.MaxBytes = int64(maxBytes) state.ConsensusParams.Block.MaxBytes = int64(maxBytes)
@ -252,7 +254,7 @@ func TestCreateProposalBlock(t *testing.T) {
// Make EvidencePool // Make EvidencePool
evidenceDB := dbm.NewMemDB() evidenceDB := dbm.NewMemDB()
blockStore := store.NewBlockStore(dbm.NewMemDB()) blockStore := store.NewBlockStore(dbm.NewMemDB())
evidencePool, err := evidence.NewPool(evidenceDB, evidence.NewEvidenceStateStore(stateDB), blockStore)
evidencePool, err := evidence.NewPool(evidenceDB, stateStore, blockStore)
require.NoError(t, err) require.NoError(t, err)
evidencePool.SetLogger(logger) evidencePool.SetLogger(logger)
@ -274,7 +276,7 @@ func TestCreateProposalBlock(t *testing.T) {
} }
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
logger, logger,
proxyApp.Consensus(), proxyApp.Consensus(),
mempool, mempool,
@ -346,12 +348,17 @@ func state(nVals int, height int64) (sm.State, dbm.DB, []types.PrivValidator) {
// save validators to db for 2 heights // save validators to db for 2 heights
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
sm.SaveState(stateDB, s)
stateStore := sm.NewStore(stateDB)
if err := stateStore.Save(s); err != nil {
panic(err)
}
for i := 1; i < int(height); i++ { for i := 1; i < int(height); i++ {
s.LastBlockHeight++ s.LastBlockHeight++
s.LastValidators = s.Validators.Copy() s.LastValidators = s.Validators.Copy()
sm.SaveState(stateDB, s)
if err := stateStore.Save(s); err != nil {
panic(err)
}
} }
return s, stateDB, privVals return s, stateDB, privVals
} }

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

@ -6,7 +6,6 @@ import (
tmmath "github.com/tendermint/tendermint/libs/math" tmmath "github.com/tendermint/tendermint/libs/math"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -141,7 +140,7 @@ func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockR
return nil, err return nil, err
} }
results, err := sm.LoadABCIResponses(env.StateDB, height)
results, err := env.StateStore.LoadABCIResponses(height)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 3
- 2
rpc/core/blocks_test.go View File

@ -81,8 +81,9 @@ func TestBlockResults(t *testing.T) {
} }
env = &Environment{} env = &Environment{}
env.StateDB = dbm.NewMemDB()
sm.SaveABCIResponses(env.StateDB, 100, results)
env.StateStore = sm.NewStore(dbm.NewMemDB())
err := env.StateStore.SaveABCIResponses(100, results)
require.NoError(t, err)
env.BlockStore = mockBlockStore{height: 100} env.BlockStore = mockBlockStore{height: 100}
testCases := []struct { testCases := []struct {


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

@ -5,7 +5,6 @@ import (
tmmath "github.com/tendermint/tendermint/libs/math" tmmath "github.com/tendermint/tendermint/libs/math"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -23,7 +22,7 @@ func Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *in
return nil, err return nil, err
} }
validators, err := sm.LoadValidators(env.StateDB, height)
validators, err := env.StateStore.LoadValidators(height)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -99,7 +98,7 @@ func ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCon
return nil, err return nil, err
} }
consensusParams, err := sm.LoadConsensusParams(env.StateDB, height)
consensusParams, err := env.StateStore.LoadConsensusParams(height)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 1
- 3
rpc/core/env.go View File

@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"time" "time"
dbm "github.com/tendermint/tm-db"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/consensus"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
@ -73,7 +71,7 @@ type Environment struct {
ProxyAppMempool proxy.AppConnMempool ProxyAppMempool proxy.AppConnMempool
// interfaces defined in types and above // interfaces defined in types and above
StateDB dbm.DB
StateStore sm.Store
BlockStore sm.BlockStore BlockStore sm.BlockStore
EvidencePool sm.EvidencePool EvidencePool sm.EvidencePool
ConsensusState Consensus ConsensusState Consensus


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

@ -7,7 +7,6 @@ import (
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -77,7 +76,7 @@ func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
} }
func validatorAtHeight(h int64) *types.Validator { func validatorAtHeight(h int64) *types.Validator {
vals, err := sm.LoadValidators(env.StateDB, h)
vals, err := env.StateStore.LoadValidators(h)
if err != nil { if err != nil {
return nil return nil
} }


+ 20
- 18
state/execution.go View File

@ -5,8 +5,6 @@ import (
"fmt" "fmt"
"time" "time"
dbm "github.com/tendermint/tm-db"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
cryptoenc "github.com/tendermint/tendermint/crypto/encoding" cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/fail"
@ -26,7 +24,7 @@ import (
// BlockExecutor provides the context and accessories for properly executing a block. // BlockExecutor provides the context and accessories for properly executing a block.
type BlockExecutor struct { type BlockExecutor struct {
// save state, validators, consensus params, abci responses here // save state, validators, consensus params, abci responses here
db dbm.DB
store Store
// execute the app against this // execute the app against this
proxyApp proxy.AppConnConsensus proxyApp proxy.AppConnConsensus
@ -55,7 +53,7 @@ func BlockExecutorWithMetrics(metrics *Metrics) BlockExecutorOption {
// NewBlockExecutor returns a new BlockExecutor with a NopEventBus. // NewBlockExecutor returns a new BlockExecutor with a NopEventBus.
// Call SetEventBus to provide one. // Call SetEventBus to provide one.
func NewBlockExecutor( func NewBlockExecutor(
db dbm.DB,
stateStore Store,
logger log.Logger, logger log.Logger,
proxyApp proxy.AppConnConsensus, proxyApp proxy.AppConnConsensus,
mempool mempl.Mempool, mempool mempl.Mempool,
@ -63,7 +61,7 @@ func NewBlockExecutor(
options ...BlockExecutorOption, options ...BlockExecutorOption,
) *BlockExecutor { ) *BlockExecutor {
res := &BlockExecutor{ res := &BlockExecutor{
db: db,
store: stateStore,
proxyApp: proxyApp, proxyApp: proxyApp,
eventBus: types.NopEventBus{}, eventBus: types.NopEventBus{},
mempool: mempool, mempool: mempool,
@ -79,8 +77,8 @@ func NewBlockExecutor(
return res return res
} }
func (blockExec *BlockExecutor) DB() dbm.DB {
return blockExec.db
func (blockExec *BlockExecutor) Store() Store {
return blockExec.store
} }
// SetEventBus - sets the event bus for publishing block related events. // SetEventBus - sets the event bus for publishing block related events.
@ -116,7 +114,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
// Validation does not mutate state, but does require historical information from the stateDB, // Validation does not mutate state, but does require historical information from the stateDB,
// ie. to verify evidence from a validator at an old height. // ie. to verify evidence from a validator at an old height.
func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error { func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error {
return validateBlock(blockExec.evpool, blockExec.db, state, block)
return validateBlock(blockExec.evpool, state, block)
} }
// ApplyBlock validates the block against the state, executes it against the app, // ApplyBlock validates the block against the state, executes it against the app,
@ -135,7 +133,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
startTime := time.Now().UnixNano() startTime := time.Now().UnixNano()
abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block,
blockExec.db, state.InitialHeight)
blockExec.store, state.InitialHeight)
endTime := time.Now().UnixNano() endTime := time.Now().UnixNano()
blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000) blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000)
if err != nil { if err != nil {
@ -145,7 +143,9 @@ func (blockExec *BlockExecutor) ApplyBlock(
fail.Fail() // XXX fail.Fail() // XXX
// Save the results before we commit. // Save the results before we commit.
SaveABCIResponses(blockExec.db, block.Height, abciResponses)
if err := blockExec.store.SaveABCIResponses(block.Height, abciResponses); err != nil {
return state, 0, err
}
fail.Fail() // XXX fail.Fail() // XXX
@ -182,7 +182,9 @@ func (blockExec *BlockExecutor) ApplyBlock(
// Update the app hash and save the state. // Update the app hash and save the state.
state.AppHash = appHash state.AppHash = appHash
SaveState(blockExec.db, state)
if err := blockExec.store.Save(state); err != nil {
return state, 0, err
}
fail.Fail() // XXX fail.Fail() // XXX
@ -254,7 +256,7 @@ func execBlockOnProxyApp(
logger log.Logger, logger log.Logger,
proxyAppConn proxy.AppConnConsensus, proxyAppConn proxy.AppConnConsensus,
block *types.Block, block *types.Block,
stateDB dbm.DB,
store Store,
initialHeight int64, initialHeight int64,
) (*tmstate.ABCIResponses, error) { ) (*tmstate.ABCIResponses, error) {
var validTxs, invalidTxs = 0, 0 var validTxs, invalidTxs = 0, 0
@ -283,7 +285,7 @@ func execBlockOnProxyApp(
} }
proxyAppConn.SetResponseCallback(proxyCb) proxyAppConn.SetResponseCallback(proxyCb)
commitInfo, byzVals := getBeginBlockValidatorInfo(block, stateDB, initialHeight)
commitInfo, byzVals := getBeginBlockValidatorInfo(block, store, initialHeight)
// Begin block // Begin block
var err error var err error
@ -322,14 +324,14 @@ func execBlockOnProxyApp(
return abciResponses, nil return abciResponses, nil
} }
func getBeginBlockValidatorInfo(block *types.Block, stateDB dbm.DB,
func getBeginBlockValidatorInfo(block *types.Block, store Store,
initialHeight int64) (abci.LastCommitInfo, []abci.Evidence) { initialHeight int64) (abci.LastCommitInfo, []abci.Evidence) {
voteInfos := make([]abci.VoteInfo, block.LastCommit.Size()) voteInfos := make([]abci.VoteInfo, block.LastCommit.Size())
// Initial block -> LastCommitInfo.Votes are empty. // Initial block -> LastCommitInfo.Votes are empty.
// Remember that the first LastCommit is intentionally empty, so it makes // Remember that the first LastCommit is intentionally empty, so it makes
// sense for LastCommitInfo.Votes to also be empty. // sense for LastCommitInfo.Votes to also be empty.
if block.Height > initialHeight { if block.Height > initialHeight {
lastValSet, err := LoadValidators(stateDB, block.Height-1)
lastValSet, err := store.LoadValidators(block.Height - 1)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -359,7 +361,7 @@ func getBeginBlockValidatorInfo(block *types.Block, stateDB dbm.DB,
// We need the validator set. We already did this in validateBlock. // We need the validator set. We already did this in validateBlock.
// TODO: Should we instead cache the valset in the evidence itself and add // TODO: Should we instead cache the valset in the evidence itself and add
// `SetValidatorSet()` and `ToABCI` methods ? // `SetValidatorSet()` and `ToABCI` methods ?
valset, err := LoadValidators(stateDB, ev.Height())
valset, err := store.LoadValidators(ev.Height())
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -528,10 +530,10 @@ func ExecCommitBlock(
appConnConsensus proxy.AppConnConsensus, appConnConsensus proxy.AppConnConsensus,
block *types.Block, block *types.Block,
logger log.Logger, logger log.Logger,
stateDB dbm.DB,
store Store,
initialHeight int64, initialHeight int64,
) ([]byte, error) { ) ([]byte, error) {
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, stateDB, initialHeight)
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight)
if err != nil { if err != nil {
logger.Error("Error executing block on proxy app", "height", block.Height, "err", err) logger.Error("Error executing block on proxy app", "height", block.Height, "err", err)
return nil, err return nil, err


+ 10
- 5
state/execution_test.go View File

@ -35,8 +35,9 @@ func TestApplyBlock(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, _ := makeState(1, 1) state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(),
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, sm.MockEvidencePool{}) mock.Mempool{}, sm.MockEvidencePool{})
block := makeBlock(state, 1) block := makeBlock(state, 1)
@ -60,6 +61,7 @@ func TestBeginBlockValidators(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // no need to check error again defer proxyApp.Stop() //nolint:errcheck // no need to check error again
state, stateDB, _ := makeState(2, 2) state, stateDB, _ := makeState(2, 2)
stateStore := sm.NewStore(stateDB)
prevHash := state.LastBlockID.Hash prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{} prevParts := types.PartSetHeader{}
@ -94,7 +96,7 @@ func TestBeginBlockValidators(t *testing.T) {
// block for height 2 // block for height 2
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB, 1)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
require.Nil(t, err, tc.desc) require.Nil(t, err, tc.desc)
// -> app receives a list of validators with a bool indicating if they signed // -> app receives a list of validators with a bool indicating if they signed
@ -122,6 +124,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(2, 12) state, stateDB, privVals := makeState(2, 12)
stateStore := sm.NewStore(stateDB)
prevHash := state.LastBlockID.Hash prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{} prevParts := types.PartSetHeader{}
@ -163,7 +166,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
block.Time = now block.Time = now
block.Evidence.Evidence = tc.evidence block.Evidence.Evidence = tc.evidence
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB, 1)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
require.Nil(t, err, tc.desc) require.Nil(t, err, tc.desc)
// -> app must receive an index of the byzantine validator // -> app must receive an index of the byzantine validator
@ -311,9 +314,10 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, _ := makeState(1, 1) state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(), log.TestingLogger(),
proxyApp.Consensus(), proxyApp.Consensus(),
mock.Mempool{}, mock.Mempool{},
@ -381,8 +385,9 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, _ := makeState(1, 1) state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(), log.TestingLogger(),
proxyApp.Consensus(), proxyApp.Consensus(),
mock.Mempool{}, mock.Mempool{},


+ 4
- 10
state/export_test.go View File

@ -1,12 +1,11 @@
package state package state
import ( import (
dbm "github.com/tendermint/tm-db"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state" tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
) )
// //
@ -40,14 +39,9 @@ func ValidateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, params tmproto
return validateValidatorUpdates(abciUpdates, params) return validateValidatorUpdates(abciUpdates, params)
} }
// SaveConsensusParamsInfo is an alias for the private saveConsensusParamsInfo
// method in store.go, exported exclusively and explicitly for testing.
func SaveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params tmproto.ConsensusParams) {
saveConsensusParamsInfo(db, nextHeight, changeHeight, params)
}
// SaveValidatorsInfo is an alias for the private saveValidatorsInfo method in // SaveValidatorsInfo is an alias for the private saveValidatorsInfo method in
// store.go, exported exclusively and explicitly for testing. // store.go, exported exclusively and explicitly for testing.
func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
saveValidatorsInfo(db, height, lastHeightChanged, valSet)
func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) error {
stateStore := dbStore{db}
return stateStore.saveValidatorsInfo(height, lastHeightChanged, valSet)
} }

+ 7
- 2
state/helpers_test.go View File

@ -115,12 +115,17 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida
}) })
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
sm.SaveState(stateDB, s)
stateStore := sm.NewStore(stateDB)
if err := stateStore.Save(s); err != nil {
panic(err)
}
for i := 1; i < height; i++ { for i := 1; i < height; i++ {
s.LastBlockHeight++ s.LastBlockHeight++
s.LastValidators = s.Validators.Copy() s.LastValidators = s.Validators.Copy()
sm.SaveState(stateDB, s)
if err := stateStore.Save(s); err != nil {
panic(err)
}
} }
return s, stateDB, privVals return s, stateDB, privVals


+ 50
- 30
state/state_test.go View File

@ -29,10 +29,12 @@ func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, sm.State) {
config := cfg.ResetTestRoot("state_") config := cfg.ResetTestRoot("state_")
dbType := dbm.BackendType(config.DBBackend) dbType := dbm.BackendType(config.DBBackend)
stateDB, err := dbm.NewDB("state", dbType, config.DBDir()) stateDB, err := dbm.NewDB("state", dbType, config.DBDir())
stateStore := sm.NewStore(stateDB)
require.NoError(t, err) require.NoError(t, err)
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
assert.NoError(t, err, "expected no error on LoadStateFromDBOrGenesisFile") assert.NoError(t, err, "expected no error on LoadStateFromDBOrGenesisFile")
sm.SaveState(stateDB, state)
err = stateStore.Save(state)
require.NoError(t, err)
tearDown := func(t *testing.T) { os.RemoveAll(config.RootDir) } tearDown := func(t *testing.T) { os.RemoveAll(config.RootDir) }
@ -74,13 +76,16 @@ func TestMakeGenesisStateNilValidators(t *testing.T) {
func TestStateSaveLoad(t *testing.T) { func TestStateSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t) defer tearDown(t)
stateStore := sm.NewStore(stateDB)
assert := assert.New(t) assert := assert.New(t)
state.LastBlockHeight++ state.LastBlockHeight++
state.LastValidators = state.Validators state.LastValidators = state.Validators
sm.SaveState(stateDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
loadedState := sm.LoadState(stateDB)
loadedState, err := stateStore.Load()
require.NoError(t, err)
assert.True(state.Equals(loadedState), assert.True(state.Equals(loadedState),
fmt.Sprintf("expected state and its copy to be identical.\ngot: %v\nexpected: %v\n", fmt.Sprintf("expected state and its copy to be identical.\ngot: %v\nexpected: %v\n",
loadedState, state)) loadedState, state))
@ -90,6 +95,7 @@ func TestStateSaveLoad(t *testing.T) {
func TestABCIResponsesSaveLoad1(t *testing.T) { func TestABCIResponsesSaveLoad1(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t) defer tearDown(t)
stateStore := sm.NewStore(stateDB)
assert := assert.New(t) assert := assert.New(t)
state.LastBlockHeight++ state.LastBlockHeight++
@ -107,8 +113,9 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10), types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10),
}} }}
sm.SaveABCIResponses(stateDB, block.Height, abciResponses)
loadedABCIResponses, err := sm.LoadABCIResponses(stateDB, block.Height)
err := stateStore.SaveABCIResponses(block.Height, abciResponses)
require.NoError(t, err)
loadedABCIResponses, err := stateStore.LoadABCIResponses(block.Height)
assert.Nil(err) assert.Nil(err)
assert.Equal(abciResponses, loadedABCIResponses, assert.Equal(abciResponses, loadedABCIResponses,
fmt.Sprintf("ABCIResponses don't match:\ngot: %v\nexpected: %v\n", fmt.Sprintf("ABCIResponses don't match:\ngot: %v\nexpected: %v\n",
@ -121,6 +128,8 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
defer tearDown(t) defer tearDown(t)
assert := assert.New(t) assert := assert.New(t)
stateStore := sm.NewStore(stateDB)
cases := [...]struct { cases := [...]struct {
// Height is implied to equal index+2, // Height is implied to equal index+2,
// as block 1 is created from genesis. // as block 1 is created from genesis.
@ -169,7 +178,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
// Query all before, this should return error. // Query all before, this should return error.
for i := range cases { for i := range cases {
h := int64(i + 1) h := int64(i + 1)
res, err := sm.LoadABCIResponses(stateDB, h)
res, err := stateStore.LoadABCIResponses(h)
assert.Error(err, "%d: %#v", i, res) assert.Error(err, "%d: %#v", i, res)
} }
@ -181,13 +190,14 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
DeliverTxs: tc.added, DeliverTxs: tc.added,
EndBlock: &abci.ResponseEndBlock{}, EndBlock: &abci.ResponseEndBlock{},
} }
sm.SaveABCIResponses(stateDB, h, responses)
err := stateStore.SaveABCIResponses(h, responses)
require.NoError(t, err)
} }
// 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 := sm.LoadABCIResponses(stateDB, h)
res, err := stateStore.LoadABCIResponses(h)
if assert.NoError(err, "%d", i) { if assert.NoError(err, "%d", i) {
t.Log(res) t.Log(res)
responses := &tmstate.ABCIResponses{ responses := &tmstate.ABCIResponses{
@ -206,27 +216,30 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
defer tearDown(t) defer tearDown(t)
assert := assert.New(t) assert := assert.New(t)
statestore := sm.NewStore(stateDB)
// Can't load anything for height 0. // Can't load anything for height 0.
_, err := sm.LoadValidators(stateDB, 0)
_, err := statestore.LoadValidators(0)
assert.IsType(sm.ErrNoValSetForHeight{}, err, "expected err at height 0") assert.IsType(sm.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 := sm.LoadValidators(stateDB, 1)
v, err := statestore.LoadValidators(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")
// Should be able to load for height 2. // Should be able to load for height 2.
v, err = sm.LoadValidators(stateDB, 2)
v, err = statestore.LoadValidators(2)
assert.Nil(err, "expected no err at height 2") assert.Nil(err, "expected no err at height 2")
assert.Equal(v.Hash(), state.NextValidators.Hash(), "expected validator hashes to match") assert.Equal(v.Hash(), state.NextValidators.Hash(), "expected validator hashes to match")
// Increment height, save; should be able to load for next & next next height. // Increment height, save; should be able to load for next & next next height.
state.LastBlockHeight++ state.LastBlockHeight++
nextHeight := state.LastBlockHeight + 1 nextHeight := state.LastBlockHeight + 1
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
vp0, err := sm.LoadValidators(stateDB, nextHeight+0)
err = statestore.Save(state)
require.NoError(t, err)
vp0, err := statestore.LoadValidators(nextHeight + 0)
assert.Nil(err, "expected no err") assert.Nil(err, "expected no err")
vp1, err := sm.LoadValidators(stateDB, nextHeight+1)
vp1, err := statestore.LoadValidators(nextHeight + 1)
assert.Nil(err, "expected no err") assert.Nil(err, "expected no err")
assert.Equal(vp0.Hash(), state.Validators.Hash(), "expected validator hashes to match") assert.Equal(vp0.Hash(), state.Validators.Hash(), "expected validator hashes to match")
assert.Equal(vp1.Hash(), state.NextValidators.Hash(), "expected next validator hashes to match") assert.Equal(vp1.Hash(), state.NextValidators.Hash(), "expected next validator hashes to match")
@ -236,6 +249,7 @@ func TestValidatorSimpleSaveLoad(t *testing.T) {
func TestOneValidatorChangesSaveLoad(t *testing.T) { func TestOneValidatorChangesSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t) defer tearDown(t)
stateStore := sm.NewStore(stateDB)
// Change vals at these heights. // Change vals at these heights.
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20} changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
@ -260,8 +274,8 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.NoError(t, err) require.NoError(t, err)
nextHeight := state.LastBlockHeight + 1
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
err := stateStore.Save(state)
require.NoError(t, err)
} }
// On each height change, increment the power by one. // On each height change, increment the power by one.
@ -279,7 +293,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
} }
for i, power := range testCases { for i, power := range testCases {
v, err := sm.LoadValidators(stateDB, int64(i+1+1)) // +1 because vset changes delayed by 1 block.
v, err := stateStore.LoadValidators(int64(i + 1 + 1)) // +1 because vset changes delayed by 1 block.
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)
@ -886,18 +900,20 @@ func TestLargeGenesisValidator(t *testing.T) {
func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) {
const valSetSize = 2 const valSetSize = 2
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t)
t.Cleanup(func() { tearDown(t) })
stateStore := sm.NewStore(stateDB)
state.Validators = genValSet(valSetSize) state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
sm.SaveState(stateDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
nextHeight := state.LastBlockHeight + 1 nextHeight := state.LastBlockHeight + 1
v0, err := sm.LoadValidators(stateDB, nextHeight)
v0, err := stateStore.LoadValidators(nextHeight)
assert.Nil(t, err) assert.Nil(t, err)
acc0 := v0.Validators[0].ProposerPriority acc0 := v0.Validators[0].ProposerPriority
v1, err := sm.LoadValidators(stateDB, nextHeight+1)
v1, err := stateStore.LoadValidators(nextHeight + 1)
assert.Nil(t, err) assert.Nil(t, err)
acc1 := v1.Validators[0].ProposerPriority acc1 := v1.Validators[0].ProposerPriority
@ -910,10 +926,12 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
const valSetSize = 7 const valSetSize = 7
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t) defer tearDown(t)
stateStore := sm.NewStore(stateDB)
require.Equal(t, int64(0), state.LastBlockHeight) require.Equal(t, int64(0), state.LastBlockHeight)
state.Validators = genValSet(valSetSize) state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
sm.SaveState(stateDB, state)
err := stateStore.Save(state)
require.NoError(t, err)
_, valOld := state.Validators.GetByIndex(0) _, valOld := state.Validators.GetByIndex(0)
var pubkeyOld = valOld.PubKey var pubkeyOld = valOld.PubKey
@ -923,17 +941,17 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, pubkey) header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, pubkey)
// Save state etc. // Save state etc.
var err error
var validatorUpdates []*types.Validator var validatorUpdates []*types.Validator
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates) validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
require.NoError(t, err) require.NoError(t, err)
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.Nil(t, err) require.Nil(t, err)
nextHeight := state.LastBlockHeight + 1 nextHeight := state.LastBlockHeight + 1
sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
err = stateStore.Save(state)
require.NoError(t, err)
// Load nextheight, it should be the oldpubkey. // Load nextheight, it should be the oldpubkey.
v0, err := sm.LoadValidators(stateDB, nextHeight)
v0, err := stateStore.LoadValidators(nextHeight)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, valSetSize, v0.Size()) assert.Equal(t, valSetSize, v0.Size())
index, val := v0.GetByAddress(pubkeyOld.Address()) index, val := v0.GetByAddress(pubkeyOld.Address())
@ -943,7 +961,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
} }
// Load nextheight+1, it should be the new pubkey. // Load nextheight+1, it should be the new pubkey.
v1, err := sm.LoadValidators(stateDB, nextHeight+1)
v1, err := stateStore.LoadValidators(nextHeight + 1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, valSetSize, v1.Size()) assert.Equal(t, valSetSize, v1.Size())
index, val = v1.GetByAddress(pubkey.Address()) index, val = v1.GetByAddress(pubkey.Address())
@ -972,6 +990,8 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
tearDown, stateDB, state := setupTestCase(t) tearDown, stateDB, state := setupTestCase(t)
defer tearDown(t) defer tearDown(t)
stateStore := sm.NewStore(stateDB)
// Change vals at these heights. // Change vals at these heights.
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20} changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
N := len(changeHeights) N := len(changeHeights)
@ -1004,8 +1024,8 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
require.Nil(t, err) require.Nil(t, err)
nextHeight := state.LastBlockHeight + 1
sm.SaveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
err := stateStore.Save(state)
require.NoError(t, err)
} }
// 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.
@ -1023,7 +1043,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
p, err := sm.LoadConsensusParams(stateDB, testCase.height)
p, err := stateStore.LoadConsensusParams(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.EqualValues(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at assert.EqualValues(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at
height %d`, testCase.height)) height %d`, testCase.height))


+ 166
- 89
state/store.go View File

@ -1,6 +1,7 @@
package state package state
import ( import (
"errors"
"fmt" "fmt"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
@ -36,10 +37,52 @@ func calcABCIResponsesKey(height int64) []byte {
return []byte(fmt.Sprintf("abciResponsesKey:%v", height)) return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
} }
//----------------------
type Store interface {
// LoadFromDBOrGenesisFile loads the most recent state.
// If the chain is new it will use the genesis file from the provided genesis file path as the current state.
LoadFromDBOrGenesisFile(string) (State, error)
// LoadFromDBOrGenesisDoc loads the most recent state.
// If the chain is new it will use the genesis doc as the current state.
LoadFromDBOrGenesisDoc(*types.GenesisDoc) (State, error)
// Load loads the current state of the blockchain
Load() (State, error)
// LoadValidators loads the validator set at a given height
LoadValidators(int64) (*types.ValidatorSet, error)
// LoadABCIResponses loads the abciResponse for a given height
LoadABCIResponses(int64) (*tmstate.ABCIResponses, error)
// LoadConsensusParams loads the consensus params for a given height
LoadConsensusParams(int64) (tmproto.ConsensusParams, error)
// Save overwrites the previous state with the updated one
Save(State) error
// SaveABCIResponses saves ABCIResponses for a given height
SaveABCIResponses(int64, *tmstate.ABCIResponses) error
// Bootstrap is used for bootstrapping state when not starting from a initial height.
Bootstrap(State) error
// PruneStates takes the height from which to start prning and which height stop at
PruneStates(int64, int64) error
}
//dbStore wraps a db (github.com/tendermint/tm-db)
type dbStore struct {
db dbm.DB
}
var _ Store = (*dbStore)(nil)
// NewStore creates the dbStore of the state pkg.
func NewStore(db dbm.DB) Store {
return dbStore{db}
}
// LoadStateFromDBOrGenesisFile loads the most recent state from the database, // LoadStateFromDBOrGenesisFile loads the most recent state from the database,
// or creates a new one from the given genesisFilePath. // or creates a new one from the given genesisFilePath.
func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State, error) {
state := LoadState(stateDB)
func (store dbStore) LoadFromDBOrGenesisFile(genesisFilePath string) (State, error) {
state, err := store.Load()
if err != nil {
return State{}, err
}
if state.IsEmpty() { if state.IsEmpty() {
var err error var err error
state, err = MakeGenesisStateFromFile(genesisFilePath) state, err = MakeGenesisStateFromFile(genesisFilePath)
@ -53,8 +96,11 @@ func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State
// LoadStateFromDBOrGenesisDoc loads the most recent state from the database, // LoadStateFromDBOrGenesisDoc loads the most recent state from the database,
// or creates a new one from the given genesisDoc. // or creates a new one from the given genesisDoc.
func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) {
state := LoadState(stateDB)
func (store dbStore) LoadFromDBOrGenesisDoc(genesisDoc *types.GenesisDoc) (State, error) {
state, err := store.Load()
if err != nil {
return State{}, err
}
if state.IsEmpty() { if state.IsEmpty() {
var err error var err error
@ -68,17 +114,17 @@ func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (
} }
// LoadState loads the State from the database. // LoadState loads the State from the database.
func LoadState(db dbm.DB) State {
return loadState(db, stateKey)
func (store dbStore) Load() (State, error) {
return store.loadState(stateKey)
} }
func loadState(db dbm.DB, key []byte) (state State) {
buf, err := db.Get(key)
func (store dbStore) loadState(key []byte) (state State, err error) {
buf, err := store.db.Get(key)
if err != nil { if err != nil {
panic(err)
return state, err
} }
if len(buf) == 0 { if len(buf) == 0 {
return state
return state, nil
} }
sp := new(tmstate.State) sp := new(tmstate.State)
@ -92,51 +138,72 @@ func loadState(db dbm.DB, key []byte) (state State) {
sm, err := StateFromProto(sp) sm, err := StateFromProto(sp)
if err != nil { if err != nil {
panic(err)
return state, err
} }
return *sm
return *sm, nil
} }
// SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
// Save persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
// This flushes the writes (e.g. calls SetSync). // This flushes the writes (e.g. calls SetSync).
func SaveState(db dbm.DB, state State) {
saveState(db, state, stateKey)
func (store dbStore) Save(state State) error {
return store.save(state, stateKey)
} }
func saveState(db dbm.DB, state State, key []byte) {
func (store dbStore) save(state State, key []byte) error {
nextHeight := state.LastBlockHeight + 1 nextHeight := state.LastBlockHeight + 1
// If first block, save validators for the block. // If first block, save validators for the block.
if nextHeight == 1 { if nextHeight == 1 {
nextHeight = state.InitialHeight nextHeight = state.InitialHeight
// This extra logic due to Tendermint validator set changes being delayed 1 block. // This extra logic due to Tendermint validator set changes being delayed 1 block.
// It may get overwritten due to InitChain validator updates. // It may get overwritten due to InitChain validator updates.
saveValidatorsInfo(db, nextHeight, nextHeight, state.Validators)
if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators); err != nil {
return err
}
} }
// Save next validators. // Save next validators.
saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators); err != nil {
return err
}
// Save next consensus params. // Save next consensus params.
saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
err := db.SetSync(key, state.Bytes())
if err := store.saveConsensusParamsInfo(nextHeight,
state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil {
return err
}
err := store.db.SetSync(key, state.Bytes())
if err != nil { if err != nil {
panic(err)
return err
} }
return nil
} }
// BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height. // BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height.
func BootstrapState(db dbm.DB, state State) error {
func (store dbStore) Bootstrap(state State) error {
height := state.LastBlockHeight + 1 height := state.LastBlockHeight + 1
if height == 1 { if height == 1 {
height = state.InitialHeight height = state.InitialHeight
} }
if height > 1 && !state.LastValidators.IsNilOrEmpty() { if height > 1 && !state.LastValidators.IsNilOrEmpty() {
saveValidatorsInfo(db, height-1, height-1, state.LastValidators)
if err := store.saveValidatorsInfo(height-1, height-1, state.LastValidators); err != nil {
return err
}
}
if err := store.saveValidatorsInfo(height, height, state.Validators); err != nil {
return err
}
if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators); err != nil {
return err
}
if err := store.saveConsensusParamsInfo(height, height, state.ConsensusParams); err != nil {
return err
} }
saveValidatorsInfo(db, height, height, state.Validators)
saveValidatorsInfo(db, height+1, height+1, state.NextValidators)
saveConsensusParamsInfo(db, height, height, state.ConsensusParams)
return db.SetSync(stateKey, state.Bytes())
return store.db.SetSync(stateKey, state.Bytes())
} }
// PruneStates deletes states between the given heights (including from, excluding to). It is not // PruneStates deletes states between the given heights (including from, excluding to). It is not
@ -147,20 +214,20 @@ func BootstrapState(db dbm.DB, state State) error {
// encoding not preserving ordering: https://github.com/tendermint/tendermint/issues/4567 // encoding not preserving ordering: https://github.com/tendermint/tendermint/issues/4567
// This will cause some old states to be left behind when doing incremental partial prunes, // This will cause some old states to be left behind when doing incremental partial prunes,
// specifically older checkpoints and LastHeightChanged targets. // specifically older checkpoints and LastHeightChanged targets.
func PruneStates(db dbm.DB, from int64, to int64) error {
func (store dbStore) PruneStates(from int64, to int64) error {
if from <= 0 || to <= 0 { if from <= 0 || to <= 0 {
return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to) return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to)
} }
if from >= to { if from >= to {
return fmt.Errorf("from height %v must be lower than to height %v", from, to) return fmt.Errorf("from height %v must be lower than to height %v", from, to)
} }
valInfo := loadValidatorsInfo(db, to)
if valInfo == nil {
return fmt.Errorf("validators at height %v not found", to)
valInfo, err := loadValidatorsInfo(store.db, to)
if err != nil {
return fmt.Errorf("validators at height %v not found: %w", to, err)
} }
paramsInfo := loadConsensusParamsInfo(db, to)
if paramsInfo == nil {
return fmt.Errorf("consensus params at height %v not found", to)
paramsInfo, err := store.loadConsensusParamsInfo(to)
if err != nil {
return fmt.Errorf("consensus params at height %v not found: %w", to, err)
} }
keepVals := make(map[int64]bool) keepVals := make(map[int64]bool)
@ -173,10 +240,9 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
keepParams[paramsInfo.LastHeightChanged] = true keepParams[paramsInfo.LastHeightChanged] = true
} }
batch := db.NewBatch()
batch := store.db.NewBatch()
defer batch.Close() defer batch.Close()
pruned := uint64(0) pruned := uint64(0)
var err error
// We have to delete in reverse order, to avoid deleting previous heights that have validator // We have to delete in reverse order, to avoid deleting previous heights that have validator
// sets and consensus params that we may need to retrieve. // sets and consensus params that we may need to retrieve.
@ -185,10 +251,9 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
// params, otherwise they will panic if they're retrieved directly (instead of // params, otherwise they will panic if they're retrieved directly (instead of
// indirectly via a LastHeightChanged pointer). // indirectly via a LastHeightChanged pointer).
if keepVals[h] { if keepVals[h] {
v := loadValidatorsInfo(db, h)
if v.ValidatorSet == nil {
vip, err := LoadValidators(db, h)
v, err := loadValidatorsInfo(store.db, h)
if err != nil || v.ValidatorSet == nil {
vip, err := store.LoadValidators(h)
if err != nil { if err != nil {
return err return err
} }
@ -218,17 +283,23 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
} }
if keepParams[h] { if keepParams[h] {
p := loadConsensusParamsInfo(db, h)
p, err := store.loadConsensusParamsInfo(h)
if err != nil {
return err
}
if p.ConsensusParams.Equal(&tmproto.ConsensusParams{}) { if p.ConsensusParams.Equal(&tmproto.ConsensusParams{}) {
p.ConsensusParams, err = LoadConsensusParams(db, h)
p.ConsensusParams, err = store.LoadConsensusParams(h)
if err != nil { if err != nil {
return err return err
} }
p.LastHeightChanged = h p.LastHeightChanged = h
bz, err := p.Marshal() bz, err := p.Marshal()
if err != nil { if err != nil {
return err return err
} }
err = batch.Set(calcConsensusParamsKey(h), bz) err = batch.Set(calcConsensusParamsKey(h), bz)
if err != nil { if err != nil {
return err return err
@ -254,7 +325,7 @@ func PruneStates(db dbm.DB, from int64, to int64) error {
return err return err
} }
batch.Close() batch.Close()
batch = db.NewBatch()
batch = store.db.NewBatch()
defer batch.Close() defer batch.Close()
} }
} }
@ -283,8 +354,8 @@ func ABCIResponsesResultsHash(ar *tmstate.ABCIResponses) []byte {
// This is useful for recovering from crashes where we called app.Commit and // 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 // before we called s.Save(). It can also be used to produce Merkle proofs of
// the result of txs. // the result of txs.
func LoadABCIResponses(db dbm.DB, height int64) (*tmstate.ABCIResponses, error) {
buf, err := db.Get(calcABCIResponsesKey(height))
func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, error) {
buf, err := store.db.Get(calcABCIResponsesKey(height))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -311,7 +382,7 @@ func LoadABCIResponses(db dbm.DB, height int64) (*tmstate.ABCIResponses, error)
// Merkle proofs. // Merkle proofs.
// //
// Exposed for testing. // Exposed for testing.
func SaveABCIResponses(db dbm.DB, height int64, abciResponses *tmstate.ABCIResponses) {
func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error {
var dtxs []*abci.ResponseDeliverTx var dtxs []*abci.ResponseDeliverTx
//strip nil values, //strip nil values,
for _, tx := range abciResponses.DeliverTxs { for _, tx := range abciResponses.DeliverTxs {
@ -323,33 +394,36 @@ func SaveABCIResponses(db dbm.DB, height int64, abciResponses *tmstate.ABCIRespo
bz, err := abciResponses.Marshal() bz, err := abciResponses.Marshal()
if err != nil { if err != nil {
panic(err)
return err
} }
err = db.SetSync(calcABCIResponsesKey(height), bz)
err = store.db.SetSync(calcABCIResponsesKey(height), bz)
if err != nil { if err != nil {
panic(err)
return err
} }
return nil
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// LoadValidators loads the ValidatorSet for a given height. // LoadValidators loads the ValidatorSet for a given height.
// Returns ErrNoValSetForHeight if the validator set can't be found for this 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 {
func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) {
valInfo, err := loadValidatorsInfo(store.db, height)
if err != nil {
return nil, ErrNoValSetForHeight{height} return nil, ErrNoValSetForHeight{height}
} }
if valInfo.ValidatorSet == nil { if valInfo.ValidatorSet == nil {
lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged) lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
valInfo2 := loadValidatorsInfo(db, lastStoredHeight)
if valInfo2 == nil || valInfo2.ValidatorSet == nil {
panic(
fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)",
valInfo2, err := loadValidatorsInfo(store.db, lastStoredHeight)
if err != nil || valInfo2.ValidatorSet == nil {
return nil,
fmt.Errorf("couldn't find validators at height %d (height %d was originally requested): %w",
lastStoredHeight, lastStoredHeight,
height, height,
),
)
err,
)
} }
vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet) vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet)
@ -381,14 +455,14 @@ func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
} }
// CONTRACT: Returned ValidatorsInfo can be mutated. // CONTRACT: Returned ValidatorsInfo can be mutated.
func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo {
func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error) {
buf, err := db.Get(calcValidatorsKey(height)) buf, err := db.Get(calcValidatorsKey(height))
if err != nil { if err != nil {
panic(err)
return nil, err
} }
if len(buf) == 0 { if len(buf) == 0 {
return nil
return nil, errors.New("value retrieved from db is empty")
} }
v := new(tmstate.ValidatorsInfo) v := new(tmstate.ValidatorsInfo)
@ -400,7 +474,7 @@ func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo {
} }
// TODO: ensure that buf is completely read. // TODO: ensure that buf is completely read.
return v
return v, nil
} }
// saveValidatorsInfo persists the validator set. // saveValidatorsInfo persists the validator set.
@ -408,9 +482,9 @@ func loadValidatorsInfo(db dbm.DB, height int64) *tmstate.ValidatorsInfo {
// `height` is the effective height for which the validator is responsible for // `height` is the effective height for which the validator is responsible for
// signing. It should be called from s.Save(), right before the state itself is // signing. It should be called from s.Save(), right before the state itself is
// persisted. // persisted.
func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet *types.ValidatorSet) error {
if lastHeightChanged > height { if lastHeightChanged > height {
panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
return errors.New("lastHeightChanged cannot be greater than ValidatorsInfo height")
} }
valInfo := &tmstate.ValidatorsInfo{ valInfo := &tmstate.ValidatorsInfo{
LastHeightChanged: lastHeightChanged, LastHeightChanged: lastHeightChanged,
@ -420,20 +494,22 @@ func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *type
if height == lastHeightChanged || height%valSetCheckpointInterval == 0 { if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
pv, err := valSet.ToProto() pv, err := valSet.ToProto()
if err != nil { if err != nil {
panic(err)
return err
} }
valInfo.ValidatorSet = pv valInfo.ValidatorSet = pv
} }
bz, err := valInfo.Marshal() bz, err := valInfo.Marshal()
if err != nil { if err != nil {
panic(err)
return err
} }
err = db.Set(calcValidatorsKey(height), bz)
err = store.db.Set(calcValidatorsKey(height), bz)
if err != nil { if err != nil {
panic(err)
return err
} }
return nil
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -441,23 +517,22 @@ func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *type
// ConsensusParamsInfo represents the latest consensus params, or the last height it changed // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
// LoadConsensusParams loads the ConsensusParams for a given height. // LoadConsensusParams loads the ConsensusParams for a given height.
func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, error) {
func (store dbStore) LoadConsensusParams(height int64) (tmproto.ConsensusParams, error) {
empty := tmproto.ConsensusParams{} empty := tmproto.ConsensusParams{}
paramsInfo := loadConsensusParamsInfo(db, height)
if paramsInfo == nil {
return empty, ErrNoConsensusParamsForHeight{height}
paramsInfo, err := store.loadConsensusParamsInfo(height)
if err != nil {
return empty, fmt.Errorf("could not find consensus params for height #%d: %w", height, err)
} }
if paramsInfo.ConsensusParams.Equal(&empty) { if paramsInfo.ConsensusParams.Equal(&empty) {
paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
if paramsInfo2 == nil {
panic(
fmt.Sprintf(
"Couldn't find consensus params at height %d as last changed from height %d",
paramsInfo.LastHeightChanged,
height,
),
paramsInfo2, err := store.loadConsensusParamsInfo(paramsInfo.LastHeightChanged)
if err != nil {
return empty, fmt.Errorf(
"couldn't find consensus params at height %d as last changed from height %d: %w",
paramsInfo.LastHeightChanged,
height,
err,
) )
} }
@ -467,13 +542,13 @@ func LoadConsensusParams(db dbm.DB, height int64) (tmproto.ConsensusParams, erro
return paramsInfo.ConsensusParams, nil return paramsInfo.ConsensusParams, nil
} }
func loadConsensusParamsInfo(db dbm.DB, height int64) *tmstate.ConsensusParamsInfo {
buf, err := db.Get(calcConsensusParamsKey(height))
func (store dbStore) loadConsensusParamsInfo(height int64) (*tmstate.ConsensusParamsInfo, error) {
buf, err := store.db.Get(calcConsensusParamsKey(height))
if err != nil { if err != nil {
panic(err)
return nil, err
} }
if len(buf) == 0 { if len(buf) == 0 {
return nil
return nil, errors.New("value retrieved from db is empty")
} }
paramsInfo := new(tmstate.ConsensusParamsInfo) paramsInfo := new(tmstate.ConsensusParamsInfo)
@ -484,14 +559,14 @@ func loadConsensusParamsInfo(db dbm.DB, height int64) *tmstate.ConsensusParamsIn
} }
// TODO: ensure that buf is completely read. // TODO: ensure that buf is completely read.
return paramsInfo
return paramsInfo, nil
} }
// saveConsensusParamsInfo persists the consensus params for the next block to disk. // 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. // 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, // If the consensus params did not change after processing the latest block,
// only the last height for which they changed is persisted. // only the last height for which they changed is persisted.
func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params tmproto.ConsensusParams) {
func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, params tmproto.ConsensusParams) error {
paramsInfo := &tmstate.ConsensusParamsInfo{ paramsInfo := &tmstate.ConsensusParamsInfo{
LastHeightChanged: changeHeight, LastHeightChanged: changeHeight,
} }
@ -501,11 +576,13 @@ func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params t
} }
bz, err := paramsInfo.Marshal() bz, err := paramsInfo.Marshal()
if err != nil { if err != nil {
panic(err)
return err
} }
err = db.Set(calcConsensusParamsKey(nextHeight), bz)
err = store.db.Set(calcConsensusParamsKey(nextHeight), bz)
if err != nil { if err != nil {
panic(err)
return err
} }
return nil
} }

+ 27
- 16
state/store_test.go View File

@ -23,21 +23,25 @@ import (
func TestStoreLoadValidators(t *testing.T) { func TestStoreLoadValidators(t *testing.T) {
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB)
val, _ := types.RandValidator(true, 10) val, _ := types.RandValidator(true, 10)
vals := types.NewValidatorSet([]*types.Validator{val}) vals := types.NewValidatorSet([]*types.Validator{val})
// 1) LoadValidators loads validators using a height where they were last changed // 1) LoadValidators loads validators using a height where they were last changed
sm.SaveValidatorsInfo(stateDB, 1, 1, vals)
sm.SaveValidatorsInfo(stateDB, 2, 1, vals)
loadedVals, err := sm.LoadValidators(stateDB, 2)
err := sm.SaveValidatorsInfo(stateDB, 1, 1, vals)
require.NoError(t, err)
err = sm.SaveValidatorsInfo(stateDB, 2, 1, vals)
require.NoError(t, err)
loadedVals, err := stateStore.LoadValidators(2)
require.NoError(t, err) require.NoError(t, err)
assert.NotZero(t, loadedVals.Size()) assert.NotZero(t, loadedVals.Size())
// 2) LoadValidators loads validators using a checkpoint height // 2) LoadValidators loads validators using a checkpoint height
sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals)
err = sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals)
require.NoError(t, err)
loadedVals, err = sm.LoadValidators(stateDB, sm.ValSetCheckpointInterval)
loadedVals, err = stateStore.LoadValidators(sm.ValSetCheckpointInterval)
require.NoError(t, err) require.NoError(t, err)
assert.NotZero(t, loadedVals.Size()) assert.NotZero(t, loadedVals.Size())
} }
@ -50,22 +54,27 @@ func BenchmarkLoadValidators(b *testing.B) {
dbType := dbm.BackendType(config.DBBackend) dbType := dbm.BackendType(config.DBBackend)
stateDB, err := dbm.NewDB("state", dbType, config.DBDir()) stateDB, err := dbm.NewDB("state", dbType, config.DBDir())
require.NoError(b, err) require.NoError(b, err)
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
stateStore := sm.NewStore(stateDB)
state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
state.Validators = genValSet(valSetSize) state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1)
sm.SaveState(stateDB, state)
err = stateStore.Save(state)
require.NoError(b, err)
for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ... for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ...
i := i i := i
sm.SaveValidatorsInfo(stateDB, int64(i), state.LastHeightValidatorsChanged, state.NextValidators)
if err := sm.SaveValidatorsInfo(stateDB,
int64(i), state.LastHeightValidatorsChanged, state.NextValidators); err != nil {
b.Fatal(err)
}
b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) { b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
_, err := sm.LoadValidators(stateDB, int64(i))
_, err := stateStore.LoadValidators(int64(i))
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@ -98,6 +107,7 @@ func TestPruneStates(t *testing.T) {
tc := tc tc := tc
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
db := dbm.NewMemDB() db := dbm.NewMemDB()
stateStore := sm.NewStore(db)
pk := ed25519.GenPrivKey().PubKey() pk := ed25519.GenPrivKey().PubKey()
// Generate a bunch of state data. Validators change for heights ending with 3, and // Generate a bunch of state data. Validators change for heights ending with 3, and
@ -134,19 +144,21 @@ func TestPruneStates(t *testing.T) {
state.LastValidators = state.Validators state.LastValidators = state.Validators
} }
sm.SaveState(db, state)
err := stateStore.Save(state)
require.NoError(t, err)
sm.SaveABCIResponses(db, h, &tmstate.ABCIResponses{
err = stateStore.SaveABCIResponses(h, &tmstate.ABCIResponses{
DeliverTxs: []*abci.ResponseDeliverTx{ DeliverTxs: []*abci.ResponseDeliverTx{
{Data: []byte{1}}, {Data: []byte{1}},
{Data: []byte{2}}, {Data: []byte{2}},
{Data: []byte{3}}, {Data: []byte{3}},
}, },
}) })
require.NoError(t, err)
} }
// Test assertions // Test assertions
err := sm.PruneStates(db, tc.pruneFrom, tc.pruneTo)
err := stateStore.PruneStates(tc.pruneFrom, tc.pruneTo)
if tc.expectErr { if tc.expectErr {
require.Error(t, err) require.Error(t, err)
return return
@ -158,7 +170,7 @@ func TestPruneStates(t *testing.T) {
expectABCI := sliceToMap(tc.expectABCI) expectABCI := sliceToMap(tc.expectABCI)
for h := int64(1); h <= tc.makeHeights; h++ { for h := int64(1); h <= tc.makeHeights; h++ {
vals, err := sm.LoadValidators(db, h)
vals, err := stateStore.LoadValidators(h)
if expectVals[h] { if expectVals[h] {
require.NoError(t, err, "validators height %v", h) require.NoError(t, err, "validators height %v", h)
require.NotNil(t, vals) require.NotNil(t, vals)
@ -167,16 +179,15 @@ func TestPruneStates(t *testing.T) {
require.Equal(t, sm.ErrNoValSetForHeight{Height: h}, err) require.Equal(t, sm.ErrNoValSetForHeight{Height: h}, err)
} }
params, err := sm.LoadConsensusParams(db, h)
params, err := stateStore.LoadConsensusParams(h)
if expectParams[h] { if expectParams[h] {
require.NoError(t, err, "params height %v", h) require.NoError(t, err, "params height %v", h)
require.False(t, params.Equal(&tmproto.ConsensusParams{})) require.False(t, params.Equal(&tmproto.ConsensusParams{}))
} else { } else {
require.Error(t, err, "params height %v", h) require.Error(t, err, "params height %v", h)
require.Equal(t, sm.ErrNoConsensusParamsForHeight{Height: h}, err)
} }
abci, err := sm.LoadABCIResponses(db, h)
abci, err := stateStore.LoadABCIResponses(h)
if expectABCI[h] { if expectABCI[h] {
require.NoError(t, err, "abci height %v", h) require.NoError(t, err, "abci height %v", h)
require.NotNil(t, abci) require.NotNil(t, abci)


+ 2
- 1
state/tx_filter_test.go View File

@ -33,7 +33,8 @@ func TestTxFilter(t *testing.T) {
for i, tc := range testCases { for i, tc := range testCases {
stateDB, err := dbm.NewDB("state", "memdb", os.TempDir()) stateDB, err := dbm.NewDB("state", "memdb", os.TempDir())
require.NoError(t, err) require.NoError(t, err)
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
stateStore := sm.NewStore(stateDB)
state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
require.NoError(t, err) require.NoError(t, err)
f := sm.TxPreCheck(state) // current max size of a tx 1850 f := sm.TxPreCheck(state) // current max size of a tx 1850


+ 1
- 3
state/validation.go View File

@ -5,8 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -14,7 +12,7 @@ import (
//----------------------------------------------------- //-----------------------------------------------------
// Validate block // Validate block
func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block *types.Block) error {
func validateBlock(evidencePool EvidencePool, state State, block *types.Block) error {
// Validate internal consistency. // Validate internal consistency.
if err := block.ValidateBasic(); err != nil { if err := block.ValidateBasic(); err != nil {
return err return err


+ 8
- 4
state/validation_test.go View File

@ -29,8 +29,9 @@ func TestValidateBlockHeader(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(3, 1) state, stateDB, privVals := makeState(3, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(), log.TestingLogger(),
proxyApp.Consensus(), proxyApp.Consensus(),
memmock.Mempool{}, memmock.Mempool{},
@ -99,8 +100,9 @@ func TestValidateBlockCommit(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(1, 1) state, stateDB, privVals := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(), log.TestingLogger(),
proxyApp.Consensus(), proxyApp.Consensus(),
memmock.Mempool{}, memmock.Mempool{},
@ -212,6 +214,7 @@ func TestValidateBlockEvidence(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(4, 1) state, stateDB, privVals := makeState(4, 1)
stateStore := sm.NewStore(stateDB)
defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
evpool := &mocks.EvidencePool{} evpool := &mocks.EvidencePool{}
@ -220,7 +223,7 @@ func TestValidateBlockEvidence(t *testing.T) {
state.ConsensusParams.Evidence.MaxNum = 3 state.ConsensusParams.Evidence.MaxNum = 3
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB,
stateStore,
log.TestingLogger(), log.TestingLogger(),
proxyApp.Consensus(), proxyApp.Consensus(),
memmock.Mempool{}, memmock.Mempool{},
@ -280,6 +283,7 @@ func TestValidateBlockEvidence(t *testing.T) {
func TestValidateDuplicateEvidenceShouldFail(t *testing.T) { func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
var height int64 = 1 var height int64 = 1
state, stateDB, privVals := makeState(2, int(height)) state, stateDB, privVals := makeState(2, int(height))
stateStore := sm.NewStore(stateDB)
_, val := state.Validators.GetByIndex(0) _, val := state.Validators.GetByIndex(0)
_, val2 := state.Validators.GetByIndex(1) _, val2 := state.Validators.GetByIndex(1)
ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultTestTime, ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultTestTime,
@ -288,7 +292,7 @@ func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
privVals[val2.Address.String()], chainID) privVals[val2.Address.String()], chainID)
blockExec := sm.NewBlockExecutor( blockExec := sm.NewBlockExecutor(
stateDB, log.TestingLogger(),
stateStore, log.TestingLogger(),
nil, nil,
nil, nil,
sm.MockEvidencePool{}) sm.MockEvidencePool{})


+ 4
- 2
store/store_test.go View File

@ -60,7 +60,8 @@ func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFu
// stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB()) // stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB())
blockDB := dbm.NewMemDB() blockDB := dbm.NewMemDB()
stateDB := dbm.NewMemDB() stateDB := dbm.NewMemDB()
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
stateStore := sm.NewStore(stateDB)
state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
if err != nil { if err != nil {
panic(fmt.Errorf("error constructing state from genesis file: %w", err)) panic(fmt.Errorf("error constructing state from genesis file: %w", err))
} }
@ -401,7 +402,8 @@ func TestLoadBlockPart(t *testing.T) {
func TestPruneBlocks(t *testing.T) { func TestPruneBlocks(t *testing.T) {
config := cfg.ResetTestRoot("blockchain_reactor_test") config := cfg.ResetTestRoot("blockchain_reactor_test")
defer os.RemoveAll(config.RootDir) defer os.RemoveAll(config.RootDir)
state, err := sm.LoadStateFromDBOrGenesisFile(dbm.NewMemDB(), config.GenesisFile())
stateStore := sm.NewStore(dbm.NewMemDB())
state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
require.NoError(t, err) require.NoError(t, err)
db := dbm.NewMemDB() db := dbm.NewMemDB()
bs := NewBlockStore(db) bs := NewBlockStore(db)


Loading…
Cancel
Save