mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 23:14:37 +00:00
abci: implement finalize block (#9468)
Adds the `FinalizeBlock` method which replaces `BeginBlock`, `DeliverTx`, and `EndBlock` in a single call.
This commit is contained in:
+1
-1
@@ -103,4 +103,4 @@ func (e ErrNoABCIResponsesForHeight) Error() string {
|
||||
return fmt.Sprintf("could not find results for height #%d", e.Height)
|
||||
}
|
||||
|
||||
var ErrABCIResponsesNotPersisted = errors.New("node is not persisting abci responses")
|
||||
var ErrFinalizeBlockResponsesNotPersisted = errors.New("node is not persisting finalize block responses")
|
||||
|
||||
+82
-123
@@ -1,7 +1,7 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/fail"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/mempool"
|
||||
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -117,8 +116,8 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
|
||||
block := state.MakeBlock(height, txs, commit, evidence, proposerAddr)
|
||||
|
||||
localLastCommit := buildLastCommitInfo(block, blockExec.store, state.InitialHeight)
|
||||
rpp, err := blockExec.proxyApp.PrepareProposalSync(
|
||||
abci.RequestPrepareProposal{
|
||||
rpp, err := blockExec.proxyApp.PrepareProposal(context.TODO(),
|
||||
&abci.RequestPrepareProposal{
|
||||
MaxTxBytes: maxDataBytes,
|
||||
Txs: block.Txs.ToSliceOfBytes(),
|
||||
LocalLastCommit: extendedCommitInfo(localLastCommit, votes),
|
||||
@@ -153,7 +152,7 @@ func (blockExec *BlockExecutor) ProcessProposal(
|
||||
block *types.Block,
|
||||
state State,
|
||||
) (bool, error) {
|
||||
resp, err := blockExec.proxyApp.ProcessProposalSync(abci.RequestProcessProposal{
|
||||
resp, err := blockExec.proxyApp.ProcessProposal(context.TODO(), &abci.RequestProcessProposal{
|
||||
Hash: block.Header.Hash(),
|
||||
Height: block.Header.Height,
|
||||
Time: block.Header.Time,
|
||||
@@ -199,33 +198,48 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
return state, ErrInvalidBlock(err)
|
||||
}
|
||||
|
||||
commitInfo := buildLastCommitInfo(block, blockExec.store, state.InitialHeight)
|
||||
|
||||
startTime := time.Now().UnixNano()
|
||||
abciResponses, err := execBlockOnProxyApp(
|
||||
blockExec.logger, blockExec.proxyApp, block, blockExec.store, state.InitialHeight,
|
||||
)
|
||||
abciResponse, err := blockExec.proxyApp.FinalizeBlock(context.TODO(), &abci.RequestFinalizeBlock{
|
||||
Hash: block.Hash(),
|
||||
NextValidatorsHash: block.NextValidatorsHash,
|
||||
ProposerAddress: block.ProposerAddress,
|
||||
Height: block.Height,
|
||||
DecidedLastCommit: commitInfo,
|
||||
Misbehavior: block.Evidence.Evidence.ToABCI(),
|
||||
Txs: block.Txs.ToSliceOfBytes(),
|
||||
})
|
||||
endTime := time.Now().UnixNano()
|
||||
blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000)
|
||||
if err != nil {
|
||||
return state, ErrProxyAppConn(err)
|
||||
blockExec.logger.Error("error in proxyAppConn.FinalizeBlock", "err", err)
|
||||
return state, err
|
||||
}
|
||||
|
||||
// Assert that the application correctly returned tx results for each of the transactions provided in the block
|
||||
if len(block.Data.Txs) != len(abciResponse.TxResults) {
|
||||
return state, fmt.Errorf("expected tx results length to match size of transactions in block. Expected %d, got %d", len(block.Data.Txs), len(abciResponse.TxResults))
|
||||
}
|
||||
|
||||
blockExec.logger.Info("executed block", "height", block.Height, "agreed_app_data", abciResponse.AgreedAppData)
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// Save the results before we commit.
|
||||
if err := blockExec.store.SaveABCIResponses(block.Height, abciResponses); err != nil {
|
||||
if err := blockExec.store.SaveFinalizeBlockResponse(block.Height, abciResponse); err != nil {
|
||||
return state, err
|
||||
}
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// validate the validator updates and convert to tendermint types
|
||||
abciValUpdates := abciResponses.EndBlock.ValidatorUpdates
|
||||
err = validateValidatorUpdates(abciValUpdates, state.ConsensusParams.Validator)
|
||||
err = validateValidatorUpdates(abciResponse.ValidatorUpdates, state.ConsensusParams.Validator)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("error in validator updates: %v", err)
|
||||
}
|
||||
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciValUpdates)
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponse.ValidatorUpdates)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
@@ -233,18 +247,18 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
blockExec.logger.Debug("updates to validators", "updates", types.ValidatorListString(validatorUpdates))
|
||||
blockExec.metrics.ValidatorSetUpdates.Add(1)
|
||||
}
|
||||
if abciResponses.EndBlock.ConsensusParamUpdates != nil {
|
||||
if abciResponse.ConsensusParamUpdates != nil {
|
||||
blockExec.metrics.ConsensusParamUpdates.Add(1)
|
||||
}
|
||||
|
||||
// Update the state with the block and responses.
|
||||
state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
state, err = updateState(state, blockID, &block.Header, abciResponse, validatorUpdates)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("commit failed for application: %v", err)
|
||||
}
|
||||
|
||||
// Lock mempool, commit app state, update mempoool.
|
||||
appHash, retainHeight, err := blockExec.Commit(state, block, abciResponses.DeliverTxs)
|
||||
retainHeight, err := blockExec.Commit(state, block, abciResponse)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("commit failed for application: %v", err)
|
||||
}
|
||||
@@ -255,7 +269,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
fail.Fail() // XXX
|
||||
|
||||
// Update the app hash and save the state.
|
||||
state.AppHash = appHash
|
||||
state.AppHash = abciResponse.AgreedAppData
|
||||
if err := blockExec.store.Save(state); err != nil {
|
||||
return state, err
|
||||
}
|
||||
@@ -274,7 +288,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
|
||||
// Events are fired after everything else.
|
||||
// NOTE: if we crash between Commit and Save, events wont be fired during replay
|
||||
fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses, validatorUpdates)
|
||||
fireEvents(blockExec.logger, blockExec.eventBus, block, blockID, abciResponse, validatorUpdates)
|
||||
|
||||
return state, nil
|
||||
}
|
||||
@@ -288,8 +302,8 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
func (blockExec *BlockExecutor) Commit(
|
||||
state State,
|
||||
block *types.Block,
|
||||
deliverTxResponses []*abci.ResponseDeliverTx,
|
||||
) ([]byte, int64, error) {
|
||||
abciResponse *abci.ResponseFinalizeBlock,
|
||||
) (int64, error) {
|
||||
blockExec.mempool.Lock()
|
||||
defer blockExec.mempool.Unlock()
|
||||
|
||||
@@ -298,114 +312,37 @@ func (blockExec *BlockExecutor) Commit(
|
||||
err := blockExec.mempool.FlushAppConn()
|
||||
if err != nil {
|
||||
blockExec.logger.Error("client error during mempool.FlushAppConn", "err", err)
|
||||
return nil, 0, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Commit block, get hash back
|
||||
res, err := blockExec.proxyApp.CommitSync()
|
||||
res, err := blockExec.proxyApp.Commit(context.TODO())
|
||||
if err != nil {
|
||||
blockExec.logger.Error("client error during proxyAppConn.CommitSync", "err", err)
|
||||
return nil, 0, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// ResponseCommit has no error code - just data
|
||||
blockExec.logger.Info(
|
||||
"committed state",
|
||||
"height", block.Height,
|
||||
"num_txs", len(block.Txs),
|
||||
"app_hash", fmt.Sprintf("%X", res.Data),
|
||||
)
|
||||
|
||||
// Update mempool.
|
||||
err = blockExec.mempool.Update(
|
||||
block.Height,
|
||||
block.Txs,
|
||||
deliverTxResponses,
|
||||
abciResponse.TxResults,
|
||||
TxPreCheck(state),
|
||||
TxPostCheck(state),
|
||||
)
|
||||
|
||||
return res.Data, res.RetainHeight, err
|
||||
return res.RetainHeight, err
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Helper functions for executing blocks and updating state
|
||||
|
||||
// Executes block's transactions on proxyAppConn.
|
||||
// Returns a list of transaction results and updates to the validator set
|
||||
func execBlockOnProxyApp(
|
||||
logger log.Logger,
|
||||
proxyAppConn proxy.AppConnConsensus,
|
||||
block *types.Block,
|
||||
store Store,
|
||||
initialHeight int64,
|
||||
) (*tmstate.ABCIResponses, error) {
|
||||
var validTxs, invalidTxs = 0, 0
|
||||
|
||||
txIndex := 0
|
||||
abciResponses := new(tmstate.ABCIResponses)
|
||||
dtxs := make([]*abci.ResponseDeliverTx, len(block.Txs))
|
||||
abciResponses.DeliverTxs = dtxs
|
||||
|
||||
// Execute transactions and get hash.
|
||||
proxyCb := func(req *abci.Request, res *abci.Response) {
|
||||
if r, ok := res.Value.(*abci.Response_DeliverTx); ok {
|
||||
// TODO: make use of res.Log
|
||||
// TODO: make use of this info
|
||||
// Blocks may include invalid txs.
|
||||
txRes := r.DeliverTx
|
||||
if txRes.Code == abci.CodeTypeOK {
|
||||
validTxs++
|
||||
} else {
|
||||
logger.Debug("invalid tx", "code", txRes.Code, "log", txRes.Log)
|
||||
invalidTxs++
|
||||
}
|
||||
|
||||
abciResponses.DeliverTxs[txIndex] = txRes
|
||||
txIndex++
|
||||
}
|
||||
}
|
||||
proxyAppConn.SetResponseCallback(proxyCb)
|
||||
|
||||
commitInfo := buildLastCommitInfo(block, store, initialHeight)
|
||||
|
||||
// Begin block
|
||||
var err error
|
||||
pbh := block.Header.ToProto()
|
||||
if pbh == nil {
|
||||
return nil, errors.New("nil header")
|
||||
}
|
||||
|
||||
abciResponses.BeginBlock, err = proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
|
||||
Hash: block.Hash(),
|
||||
Header: *pbh,
|
||||
LastCommitInfo: commitInfo,
|
||||
ByzantineValidators: block.Evidence.Evidence.ToABCI(),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("error in proxyAppConn.BeginBlock", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// run txs of block
|
||||
for _, tx := range block.Txs {
|
||||
proxyAppConn.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx})
|
||||
if err := proxyAppConn.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// End block.
|
||||
abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(abci.RequestEndBlock{Height: block.Height})
|
||||
if err != nil {
|
||||
logger.Error("error in proxyAppConn.EndBlock", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Info("executed block", "height", block.Height, "num_valid_txs", validTxs, "num_invalid_txs", invalidTxs)
|
||||
return abciResponses, nil
|
||||
}
|
||||
|
||||
func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) abci.CommitInfo {
|
||||
if block.Height == initialHeight {
|
||||
// there is no last commit for the initial height.
|
||||
@@ -495,7 +432,7 @@ func updateState(
|
||||
state State,
|
||||
blockID types.BlockID,
|
||||
header *types.Header,
|
||||
abciResponses *tmstate.ABCIResponses,
|
||||
abciResponse *abci.ResponseFinalizeBlock,
|
||||
validatorUpdates []*types.Validator,
|
||||
) (State, error) {
|
||||
|
||||
@@ -503,7 +440,7 @@ func updateState(
|
||||
// and update s.LastValidators and s.Validators.
|
||||
nValSet := state.NextValidators.Copy()
|
||||
|
||||
// Update the validator set with the latest abciResponses.
|
||||
// Update the validator set with the latest abciResponse.
|
||||
lastHeightValsChanged := state.LastHeightValidatorsChanged
|
||||
if len(validatorUpdates) > 0 {
|
||||
err := nValSet.UpdateWithChangeSet(validatorUpdates)
|
||||
@@ -517,12 +454,12 @@ func updateState(
|
||||
// Update validator proposer priority and set state variables.
|
||||
nValSet.IncrementProposerPriority(1)
|
||||
|
||||
// Update the params with the latest abciResponses.
|
||||
// Update the params with the latest abciResponse.
|
||||
nextParams := state.ConsensusParams
|
||||
lastHeightParamsChanged := state.LastHeightConsensusParamsChanged
|
||||
if abciResponses.EndBlock.ConsensusParamUpdates != nil {
|
||||
if abciResponse.ConsensusParamUpdates != nil {
|
||||
// NOTE: must not mutate s.ConsensusParams
|
||||
nextParams = state.ConsensusParams.Update(abciResponses.EndBlock.ConsensusParamUpdates)
|
||||
nextParams = state.ConsensusParams.Update(abciResponse.ConsensusParamUpdates)
|
||||
err := nextParams.ValidateBasic()
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("error updating consensus params: %v", err)
|
||||
@@ -551,7 +488,7 @@ func updateState(
|
||||
LastHeightValidatorsChanged: lastHeightValsChanged,
|
||||
ConsensusParams: nextParams,
|
||||
LastHeightConsensusParamsChanged: lastHeightParamsChanged,
|
||||
LastResultsHash: ABCIResponsesResultsHash(abciResponses),
|
||||
LastResultsHash: TxResultsHash(abciResponse.TxResults),
|
||||
AppHash: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -563,26 +500,32 @@ func fireEvents(
|
||||
logger log.Logger,
|
||||
eventBus types.BlockEventPublisher,
|
||||
block *types.Block,
|
||||
abciResponses *tmstate.ABCIResponses,
|
||||
blockID types.BlockID,
|
||||
abciResponse *abci.ResponseFinalizeBlock,
|
||||
validatorUpdates []*types.Validator,
|
||||
) {
|
||||
if err := eventBus.PublishEventNewBlock(types.EventDataNewBlock{
|
||||
Block: block,
|
||||
ResultBeginBlock: *abciResponses.BeginBlock,
|
||||
ResultEndBlock: *abciResponses.EndBlock,
|
||||
Block: block,
|
||||
BlockID: blockID,
|
||||
ResultFinalizeBlock: *abciResponse,
|
||||
}); err != nil {
|
||||
logger.Error("failed publishing new block", "err", err)
|
||||
}
|
||||
|
||||
if err := eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{
|
||||
Header: block.Header,
|
||||
NumTxs: int64(len(block.Txs)),
|
||||
ResultBeginBlock: *abciResponses.BeginBlock,
|
||||
ResultEndBlock: *abciResponses.EndBlock,
|
||||
Header: block.Header,
|
||||
}); err != nil {
|
||||
logger.Error("failed publishing new block header", "err", err)
|
||||
}
|
||||
|
||||
if err := eventBus.PublishEventNewBlockEvents(types.EventDataNewBlockEvents{
|
||||
Height: block.Height,
|
||||
Events: abciResponse.Events,
|
||||
NumTxs: int64(len(block.Txs)),
|
||||
}); err != nil {
|
||||
logger.Error("failed publishing new block events", "err", err)
|
||||
}
|
||||
|
||||
if len(block.Evidence.Evidence) != 0 {
|
||||
for _, ev := range block.Evidence.Evidence {
|
||||
if err := eventBus.PublishEventNewEvidence(types.EventDataNewEvidence{
|
||||
@@ -599,7 +542,7 @@ func fireEvents(
|
||||
Height: block.Height,
|
||||
Index: uint32(i),
|
||||
Tx: tx,
|
||||
Result: *(abciResponses.DeliverTxs[i]),
|
||||
Result: *(abciResponse.TxResults[i]),
|
||||
}}); err != nil {
|
||||
logger.Error("failed publishing event TX", "err", err)
|
||||
}
|
||||
@@ -625,21 +568,37 @@ func ExecCommitBlock(
|
||||
store Store,
|
||||
initialHeight int64,
|
||||
) ([]byte, error) {
|
||||
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight)
|
||||
commitInfo := buildLastCommitInfo(block, store, initialHeight)
|
||||
|
||||
resp, err := appConnConsensus.FinalizeBlock(context.TODO(), &abci.RequestFinalizeBlock{
|
||||
Hash: block.Hash(),
|
||||
NextValidatorsHash: block.NextValidatorsHash,
|
||||
ProposerAddress: block.ProposerAddress,
|
||||
Height: block.Height,
|
||||
DecidedLastCommit: commitInfo,
|
||||
Misbehavior: block.Evidence.Evidence.ToABCI(),
|
||||
Txs: block.Txs.ToSliceOfBytes(),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("failed executing block on proxy app", "height", block.Height, "err", err)
|
||||
logger.Error("error in proxyAppConn.FinalizeBlock", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Assert that the application correctly returned tx results for each of the transactions provided in the block
|
||||
if len(block.Data.Txs) != len(resp.TxResults) {
|
||||
return nil, fmt.Errorf("expected tx results length to match size of transactions in block. Expected %d, got %d", len(block.Data.Txs), len(resp.TxResults))
|
||||
}
|
||||
|
||||
logger.Info("executed block", "height", block.Height, "agreed_app_data", resp.AgreedAppData)
|
||||
|
||||
// Commit block, get hash back
|
||||
res, err := appConnConsensus.CommitSync()
|
||||
_, err = appConnConsensus.Commit(context.TODO())
|
||||
if err != nil {
|
||||
logger.Error("client error during proxyAppConn.CommitSync", "err", res)
|
||||
logger.Error("client error during proxyAppConn.CommitSync", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ResponseCommit has no error or log, just data
|
||||
return res.Data, nil
|
||||
return resp.AgreedAppData, nil
|
||||
}
|
||||
|
||||
func (blockExec *BlockExecutor) pruneBlocks(retainHeight int64, state State) (uint64, error) {
|
||||
|
||||
+115
-38
@@ -77,13 +77,90 @@ func TestApplyBlock(t *testing.T) {
|
||||
assert.EqualValues(t, 1, state.Version.Consensus.App, "App version wasn't updated")
|
||||
}
|
||||
|
||||
// TestBeginBlockValidators ensures we send absent validators list.
|
||||
func TestBeginBlockValidators(t *testing.T) {
|
||||
// TestFinalizeBlockDecidedLastCommit ensures we correctly send the
|
||||
// DecidedLastCommit to the application. The test ensures that the
|
||||
// DecidedLastCommit properly reflects which validators signed the preceding
|
||||
// block.
|
||||
func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
err := proxyApp.Start()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
|
||||
|
||||
state, stateDB, privVals := makeState(7, 1)
|
||||
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
|
||||
DiscardABCIResponses: false,
|
||||
})
|
||||
absentSig := types.NewCommitSigAbsent()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
absentCommitSigs map[int]bool
|
||||
}{
|
||||
{"none absent", map[int]bool{}},
|
||||
{"one absent", map[int]bool{1: true}},
|
||||
{"multiple absent", map[int]bool{1: true, 3: true}},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
blockStore := store.NewBlockStore(dbm.NewMemDB())
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, 0)
|
||||
evpool.On("Update", mock.Anything, mock.Anything).Return()
|
||||
evpool.On("CheckEvidence", mock.Anything).Return(nil)
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("Lock").Return()
|
||||
mp.On("Unlock").Return()
|
||||
mp.On("FlushAppConn", mock.Anything).Return(nil)
|
||||
mp.On("Update",
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything).Return(nil)
|
||||
|
||||
eventBus := types.NewEventBus()
|
||||
require.NoError(t, eventBus.Start())
|
||||
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.NewNopLogger(), proxyApp.Consensus(), mp, evpool, blockStore)
|
||||
state, _, lastCommit, err := makeAndCommitGoodBlock(state, 1, new(types.Commit), state.NextValidators.Validators[0].Address, blockExec, privVals, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for idx, isAbsent := range tc.absentCommitSigs {
|
||||
if isAbsent {
|
||||
lastCommit.Signatures[idx] = absentSig
|
||||
}
|
||||
}
|
||||
|
||||
// block for height 2
|
||||
block := makeBlock(state, 2, lastCommit)
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
_, err = blockExec.ApplyBlock(state, blockID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
// -> app receives a list of validators with a bool indicating if they signed
|
||||
for i, v := range app.CommitVotes {
|
||||
_, absent := tc.absentCommitSigs[i]
|
||||
assert.Equal(t, !absent, v.SignedLastBlock)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestFinalizeBlockValidators ensures we send absent validators list.
|
||||
func TestFinalizeBlockValidators(t *testing.T) {
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
err := proxyApp.Start()
|
||||
require.NoError(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // no need to check error again
|
||||
|
||||
state, stateDB, _ := makeState(2, 2)
|
||||
@@ -142,13 +219,13 @@ func TestBeginBlockValidators(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestBeginBlockByzantineValidators ensures we send byzantine validators list.
|
||||
func TestBeginBlockByzantineValidators(t *testing.T) {
|
||||
// TestFinalizeBlockMisbehavior ensures we send misbehavior list.
|
||||
func TestFinalizeBlockMisbehavior(t *testing.T) {
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
err := proxyApp.Start()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
|
||||
|
||||
state, stateDB, privVals := makeState(1, 1)
|
||||
@@ -247,8 +324,8 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
|
||||
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
state, err = blockExec.ApplyBlock(state, blockID, block)
|
||||
require.Nil(t, err)
|
||||
_, err = blockExec.ApplyBlock(state, blockID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
// TODO check state and mempool
|
||||
assert.Equal(t, abciMb, app.Misbehavior)
|
||||
@@ -259,8 +336,8 @@ func TestProcessProposal(t *testing.T) {
|
||||
txs := test.MakeNTxs(height, 10)
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
app := abcimocks.NewBaseMock()
|
||||
app.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT})
|
||||
app := &abcimocks.Application{}
|
||||
app.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
|
||||
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
@@ -316,7 +393,7 @@ func TestProcessProposal(t *testing.T) {
|
||||
})
|
||||
block1.Txs = txs
|
||||
|
||||
expectedRpp := abci.RequestProcessProposal{
|
||||
expectedRpp := &abci.RequestProcessProposal{
|
||||
Txs: block1.Txs.ToSliceOfBytes(),
|
||||
Hash: block1.Hash(),
|
||||
Height: block1.Header.Height,
|
||||
@@ -334,7 +411,7 @@ func TestProcessProposal(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.True(t, acceptBlock)
|
||||
app.AssertExpectations(t)
|
||||
app.AssertCalled(t, "ProcessProposal", expectedRpp)
|
||||
app.AssertCalled(t, "ProcessProposal", context.TODO(), expectedRpp)
|
||||
}
|
||||
|
||||
func TestValidateValidatorUpdates(t *testing.T) {
|
||||
@@ -467,13 +544,13 @@ func TestUpdateValidators(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEndBlockValidatorUpdates ensures we update validator set and send an event.
|
||||
func TestEndBlockValidatorUpdates(t *testing.T) {
|
||||
// TestFinalizeBlockValidatorUpdates ensures we update validator set and send an event.
|
||||
func TestFinalizeBlockValidatorUpdates(t *testing.T) {
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
err := proxyApp.Start()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
|
||||
|
||||
state, stateDB, _ := makeState(1, 1)
|
||||
@@ -530,7 +607,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
|
||||
}
|
||||
|
||||
state, err = blockExec.ApplyBlock(state, blockID, block)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
// test new validator was added to NextValidators
|
||||
if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) {
|
||||
idx, _ := state.NextValidators.GetByAddress(pubkey.Address())
|
||||
@@ -555,14 +632,14 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEndBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that
|
||||
// TestFinalizeBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that
|
||||
// would result in empty set causes no panic, an error is raised and NextValidators is not updated
|
||||
func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
|
||||
func TestFinalizeBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
err := proxyApp.Start()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
|
||||
|
||||
state, stateDB, _ := makeState(1, 1)
|
||||
@@ -592,14 +669,14 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block) })
|
||||
assert.NotNil(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.NotEmpty(t, state.NextValidators.Validators)
|
||||
}
|
||||
|
||||
func TestEmptyPrepareProposal(t *testing.T) {
|
||||
const height = 2
|
||||
|
||||
app := abcimocks.NewBaseMock()
|
||||
app := &abci.BaseApplication{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
err := proxyApp.Start()
|
||||
@@ -654,12 +731,12 @@ func TestPrepareProposalTxsAllIncluded(t *testing.T) {
|
||||
|
||||
txs := test.MakeNTxs(height, 10)
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs[2:]))
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs[2:])
|
||||
|
||||
app := abcimocks.NewBaseMock()
|
||||
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
|
||||
Txs: types.Txs(txs).ToSliceOfBytes(),
|
||||
})
|
||||
app := &abcimocks.Application{}
|
||||
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
|
||||
Txs: txs.ToSliceOfBytes(),
|
||||
}, nil)
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
err := proxyApp.Start()
|
||||
@@ -703,15 +780,15 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
|
||||
|
||||
txs := test.MakeNTxs(height, 10)
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
|
||||
|
||||
txs = txs[2:]
|
||||
txs = append(txs[len(txs)/2:], txs[:len(txs)/2]...)
|
||||
|
||||
app := abcimocks.NewBaseMock()
|
||||
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
|
||||
Txs: types.Txs(txs).ToSliceOfBytes(),
|
||||
})
|
||||
app := &abcimocks.Application{}
|
||||
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
|
||||
Txs: txs.ToSliceOfBytes(),
|
||||
}, nil)
|
||||
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
@@ -761,12 +838,12 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
|
||||
maxDataBytes := types.MaxDataBytes(state.ConsensusParams.Block.MaxBytes, 0, nValidators)
|
||||
txs := test.MakeNTxs(height, maxDataBytes/bytesPerTx+2) // +2 so that tx don't fit
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
|
||||
|
||||
app := abcimocks.NewBaseMock()
|
||||
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
|
||||
Txs: types.Txs(txs).ToSliceOfBytes(),
|
||||
})
|
||||
app := &abcimocks.Application{}
|
||||
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
|
||||
Txs: txs.ToSliceOfBytes(),
|
||||
}, nil)
|
||||
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
|
||||
@@ -809,13 +886,13 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
|
||||
|
||||
txs := test.MakeNTxs(height, 10)
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
|
||||
|
||||
cm := &abciclientmocks.Client{}
|
||||
cm.On("SetLogger", mock.Anything).Return()
|
||||
cm.On("Start").Return(nil)
|
||||
cm.On("Quit").Return(nil)
|
||||
cm.On("PrepareProposalSync", mock.Anything).Return(nil, errors.New("an injected error")).Once()
|
||||
cm.On("PrepareProposal", mock.Anything, mock.Anything).Return(nil, errors.New("an injected error")).Once()
|
||||
cm.On("Stop").Return(nil)
|
||||
cc := &pmocks.ClientCreator{}
|
||||
cc.On("NewABCIClient").Return(cm, nil)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -27,10 +26,10 @@ func UpdateState(
|
||||
state State,
|
||||
blockID types.BlockID,
|
||||
header *types.Header,
|
||||
abciResponses *tmstate.ABCIResponses,
|
||||
resp *abci.ResponseFinalizeBlock,
|
||||
validatorUpdates []*types.Validator,
|
||||
) (State, error) {
|
||||
return updateState(state, blockID, header, abciResponses, validatorUpdates)
|
||||
return updateState(state, blockID, header, resp, validatorUpdates)
|
||||
}
|
||||
|
||||
// ValidateValidatorUpdates is an alias for validateValidatorUpdates exported
|
||||
|
||||
+33
-53
@@ -2,6 +2,7 @@ package state_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/internal/test"
|
||||
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
@@ -153,21 +153,16 @@ func makeHeaderPartsResponsesValPubKeyChange(
|
||||
t *testing.T,
|
||||
state sm.State,
|
||||
pubkey crypto.PubKey,
|
||||
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
|
||||
) (types.Header, types.BlockID, *abci.ResponseFinalizeBlock) {
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
// If the pubkey is new, remove the old and add the new.
|
||||
_, val := state.NextValidators.GetByIndex(0)
|
||||
if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) {
|
||||
abciResponses.EndBlock = &abci.ResponseEndBlock{
|
||||
ValidatorUpdates: []abci.ValidatorUpdate{
|
||||
types.TM2PB.NewValidatorUpdate(val.PubKey, 0),
|
||||
types.TM2PB.NewValidatorUpdate(pubkey, 10),
|
||||
},
|
||||
abciResponses.ValidatorUpdates = []abci.ValidatorUpdate{
|
||||
types.TM2PB.NewValidatorUpdate(val.PubKey, 0),
|
||||
types.TM2PB.NewValidatorUpdate(pubkey, 10),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,21 +173,16 @@ func makeHeaderPartsResponsesValPowerChange(
|
||||
t *testing.T,
|
||||
state sm.State,
|
||||
power int64,
|
||||
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
|
||||
) (types.Header, types.BlockID, *abci.ResponseFinalizeBlock) {
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
|
||||
// If the pubkey is new, remove the old and add the new.
|
||||
_, val := state.NextValidators.GetByIndex(0)
|
||||
if val.VotingPower != power {
|
||||
abciResponses.EndBlock = &abci.ResponseEndBlock{
|
||||
ValidatorUpdates: []abci.ValidatorUpdate{
|
||||
types.TM2PB.NewValidatorUpdate(val.PubKey, power),
|
||||
},
|
||||
abciResponses.ValidatorUpdates = []abci.ValidatorUpdate{
|
||||
types.TM2PB.NewValidatorUpdate(val.PubKey, power),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,12 +193,11 @@ func makeHeaderPartsResponsesParams(
|
||||
t *testing.T,
|
||||
state sm.State,
|
||||
params tmproto.ConsensusParams,
|
||||
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
|
||||
) (types.Header, types.BlockID, *abci.ResponseFinalizeBlock) {
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: ¶ms},
|
||||
abciResponses := &abci.ResponseFinalizeBlock{
|
||||
ConsensusParamUpdates: ¶ms,
|
||||
}
|
||||
return block.Header, types.BlockID{Hash: block.Hash(), PartSetHeader: types.PartSetHeader{}}, abciResponses
|
||||
}
|
||||
@@ -238,49 +227,40 @@ type testApp struct {
|
||||
CommitVotes []abci.VoteInfo
|
||||
Misbehavior []abci.Misbehavior
|
||||
ValidatorUpdates []abci.ValidatorUpdate
|
||||
AgreedAppData []byte
|
||||
}
|
||||
|
||||
var _ abci.Application = (*testApp)(nil)
|
||||
|
||||
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
|
||||
return abci.ResponseInfo{}
|
||||
}
|
||||
func (app *testApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
|
||||
app.CommitVotes = req.DecidedLastCommit.Votes
|
||||
app.Misbehavior = req.Misbehavior
|
||||
txResults := make([]*abci.ExecTxResult, len(req.Txs))
|
||||
for idx := range req.Txs {
|
||||
txResults[idx] = &abci.ExecTxResult{
|
||||
Code: abci.CodeTypeOK,
|
||||
}
|
||||
}
|
||||
|
||||
func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||
app.CommitVotes = req.LastCommitInfo.Votes
|
||||
app.Misbehavior = req.ByzantineValidators
|
||||
return abci.ResponseBeginBlock{}
|
||||
}
|
||||
|
||||
func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
return abci.ResponseEndBlock{
|
||||
return &abci.ResponseFinalizeBlock{
|
||||
ValidatorUpdates: app.ValidatorUpdates,
|
||||
ConsensusParamUpdates: &tmproto.ConsensusParams{
|
||||
Version: &tmproto.VersionParams{
|
||||
App: 1}}}
|
||||
App: 1}},
|
||||
TxResults: txResults,
|
||||
AgreedAppData: app.AgreedAppData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
|
||||
return abci.ResponseDeliverTx{Events: []abci.Event{}}
|
||||
func (app *testApp) Commit(_ context.Context, _ *abci.RequestCommit) (*abci.ResponseCommit, error) {
|
||||
return &abci.ResponseCommit{RetainHeight: 1}, nil
|
||||
}
|
||||
|
||||
func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
|
||||
return abci.ResponseCheckTx{}
|
||||
}
|
||||
|
||||
func (app *testApp) Commit() abci.ResponseCommit {
|
||||
return abci.ResponseCommit{RetainHeight: 1}
|
||||
}
|
||||
|
||||
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
|
||||
return
|
||||
}
|
||||
|
||||
func (app *testApp) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal {
|
||||
func (app *testApp) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
|
||||
for _, tx := range req.Txs {
|
||||
if len(tx) == 0 {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
|
||||
}
|
||||
}
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
|
||||
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ type BlockIndexer interface {
|
||||
// upon database query failure.
|
||||
Has(height int64) (bool, error)
|
||||
|
||||
// Index indexes BeginBlock and EndBlock events for a given block by its height.
|
||||
Index(types.EventDataNewBlockHeader) error
|
||||
// Index indexes FinalizeBlock events for a given block by its height.
|
||||
Index(types.EventDataNewBlockEvents) error
|
||||
|
||||
// Search performs a query for block heights that match a given BeginBlock
|
||||
// and Endblock event search criteria.
|
||||
// Search performs a query for block heights that match a given FinalizeBlock
|
||||
// event search criteria.
|
||||
Search(ctx context.Context, q *query.Query) ([]int64, error)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
|
||||
var _ indexer.BlockIndexer = (*BlockerIndexer)(nil)
|
||||
|
||||
// BlockerIndexer implements a block indexer, indexing BeginBlock and EndBlock
|
||||
// BlockerIndexer implements a block indexer, indexing FinalizeBlock
|
||||
// events with an underlying KV store. Block events are indexed by their height,
|
||||
// such that matching search criteria returns the respective block height(s).
|
||||
type BlockerIndexer struct {
|
||||
@@ -44,17 +44,16 @@ func (idx *BlockerIndexer) Has(height int64) (bool, error) {
|
||||
return idx.store.Has(key)
|
||||
}
|
||||
|
||||
// Index indexes BeginBlock and EndBlock events for a given block by its height.
|
||||
// Index indexes FinalizeBlock events for a given block by its height.
|
||||
// The following is indexed:
|
||||
//
|
||||
// primary key: encode(block.height | height) => encode(height)
|
||||
// BeginBlock events: encode(eventType.eventAttr|eventValue|height|begin_block) => encode(height)
|
||||
// EndBlock events: encode(eventType.eventAttr|eventValue|height|end_block) => encode(height)
|
||||
func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockHeader) error {
|
||||
// FinalizeBlock events: encode(eventType.eventAttr|eventValue|height|finalize_block) => encode(height)
|
||||
func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockEvents) error {
|
||||
batch := idx.store.NewBatch()
|
||||
defer batch.Close()
|
||||
|
||||
height := bh.Header.Height
|
||||
height := bh.Height
|
||||
|
||||
// 1. index by height
|
||||
key, err := heightKey(height)
|
||||
@@ -65,21 +64,16 @@ func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockHeader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. index BeginBlock events
|
||||
if err := idx.indexEvents(batch, bh.ResultBeginBlock.Events, "begin_block", height); err != nil {
|
||||
return fmt.Errorf("failed to index BeginBlock events: %w", err)
|
||||
}
|
||||
|
||||
// 3. index EndBlock events
|
||||
if err := idx.indexEvents(batch, bh.ResultEndBlock.Events, "end_block", height); err != nil {
|
||||
return fmt.Errorf("failed to index EndBlock events: %w", err)
|
||||
// 2. index block events
|
||||
if err := idx.indexEvents(batch, bh.Events, "finalize_block", height); err != nil {
|
||||
return fmt.Errorf("failed to index FinalizeBlock events: %w", err)
|
||||
}
|
||||
|
||||
return batch.WriteSync()
|
||||
}
|
||||
|
||||
// Search performs a query for block heights that match a given BeginBlock
|
||||
// and Endblock event search criteria. The given query can match against zero,
|
||||
// Search performs a query for block heights that match a given FinalizeBlock
|
||||
// event search criteria. The given query can match against zero,
|
||||
// one or more block heights. In the case of height queries, i.e. block.height=H,
|
||||
// if the height is indexed, that height alone will be returned. An error and
|
||||
// nil slice is returned. Otherwise, a non-nil slice and nil error is returned.
|
||||
|
||||
@@ -18,32 +18,26 @@ func TestBlockIndexer(t *testing.T) {
|
||||
store := db.NewPrefixDB(db.NewMemDB(), []byte("block_events"))
|
||||
indexer := blockidxkv.New(store)
|
||||
|
||||
require.NoError(t, indexer.Index(types.EventDataNewBlockHeader{
|
||||
Header: types.Header{Height: 1},
|
||||
ResultBeginBlock: abci.ResponseBeginBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "begin_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "proposer",
|
||||
Value: "FCAA001",
|
||||
Index: true,
|
||||
},
|
||||
require.NoError(t, indexer.Index(types.EventDataNewBlockEvents{
|
||||
Height: 1,
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "begin_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "proposer",
|
||||
Value: "FCAA001",
|
||||
Index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ResultEndBlock: abci.ResponseEndBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "end_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "100",
|
||||
Index: true,
|
||||
},
|
||||
{
|
||||
Type: "end_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "100",
|
||||
Index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -56,32 +50,26 @@ func TestBlockIndexer(t *testing.T) {
|
||||
index = true
|
||||
}
|
||||
|
||||
require.NoError(t, indexer.Index(types.EventDataNewBlockHeader{
|
||||
Header: types.Header{Height: int64(i)},
|
||||
ResultBeginBlock: abci.ResponseBeginBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "begin_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "proposer",
|
||||
Value: "FCAA001",
|
||||
Index: true,
|
||||
},
|
||||
require.NoError(t, indexer.Index(types.EventDataNewBlockEvents{
|
||||
Height: int64(i),
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "begin_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "proposer",
|
||||
Value: "FCAA001",
|
||||
Index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ResultEndBlock: abci.ResponseEndBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "end_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: fmt.Sprintf("%d", i),
|
||||
Index: index,
|
||||
},
|
||||
{
|
||||
Type: "end_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: fmt.Sprintf("%d", i),
|
||||
Index: index,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ func (idx *BlockerIndexer) Has(height int64) (bool, error) {
|
||||
return false, errors.New(`indexing is disabled (set 'tx_index = "kv"' in config)`)
|
||||
}
|
||||
|
||||
func (idx *BlockerIndexer) Index(types.EventDataNewBlockHeader) error {
|
||||
func (idx *BlockerIndexer) Index(types.EventDataNewBlockEvents) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -39,11 +39,11 @@ func (_m *BlockIndexer) Has(height int64) (bool, error) {
|
||||
}
|
||||
|
||||
// Index provides a mock function with given fields: _a0
|
||||
func (_m *BlockIndexer) Index(_a0 types.EventDataNewBlockHeader) error {
|
||||
func (_m *BlockIndexer) Index(_a0 types.EventDataNewBlockEvents) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.EventDataNewBlockHeader) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(types.EventDataNewBlockEvents) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
|
||||
@@ -24,8 +24,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
eventTypeBeginBlock = "begin_block"
|
||||
eventTypeEndBlock = "end_block"
|
||||
eventTypeFinalizeBlock = "finaliz_block"
|
||||
)
|
||||
|
||||
// TxIndexer returns a bridge from es to the Tendermint v0.34 transaction indexer.
|
||||
@@ -77,7 +76,7 @@ func (BackportBlockIndexer) Has(height int64) (bool, error) {
|
||||
|
||||
// Index indexes block begin and end events for the specified block. It is
|
||||
// part of the BlockIndexer interface.
|
||||
func (b BackportBlockIndexer) Index(block types.EventDataNewBlockHeader) error {
|
||||
func (b BackportBlockIndexer) Index(block types.EventDataNewBlockEvents) error {
|
||||
return b.psql.IndexBlockEvents(block)
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ func makeIndexedEvent(compositeKey, value string) abci.Event {
|
||||
|
||||
// IndexBlockEvents indexes the specified block header, part of the
|
||||
// indexer.EventSink interface.
|
||||
func (es *EventSink) IndexBlockEvents(h types.EventDataNewBlockHeader) error {
|
||||
func (es *EventSink) IndexBlockEvents(h types.EventDataNewBlockEvents) error {
|
||||
ts := time.Now().UTC()
|
||||
|
||||
return runInTransaction(es.store, func(dbtx *sql.Tx) error {
|
||||
@@ -150,7 +150,7 @@ INSERT INTO `+tableBlocks+` (height, chain_id, created_at)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT DO NOTHING
|
||||
RETURNING rowid;
|
||||
`, h.Header.Height, es.chainID, ts)
|
||||
`, h.Height, es.chainID, ts)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil // we already saw this block; quietly succeed
|
||||
} else if err != nil {
|
||||
@@ -159,16 +159,13 @@ INSERT INTO `+tableBlocks+` (height, chain_id, created_at)
|
||||
|
||||
// Insert the special block meta-event for height.
|
||||
if err := insertEvents(dbtx, blockID, 0, []abci.Event{
|
||||
makeIndexedEvent(types.BlockHeightKey, fmt.Sprint(h.Header.Height)),
|
||||
makeIndexedEvent(types.BlockHeightKey, fmt.Sprint(h.Height)),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("block meta-events: %w", err)
|
||||
}
|
||||
// Insert all the block events. Order is important here,
|
||||
if err := insertEvents(dbtx, blockID, 0, h.ResultBeginBlock.Events); err != nil {
|
||||
return fmt.Errorf("begin-block events: %w", err)
|
||||
}
|
||||
if err := insertEvents(dbtx, blockID, 0, h.ResultEndBlock.Events); err != nil {
|
||||
return fmt.Errorf("end-block events: %w", err)
|
||||
if err := insertEvents(dbtx, blockID, 0, h.Events); err != nil {
|
||||
return fmt.Errorf("finalizeblock events: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
@@ -140,7 +141,7 @@ func TestMain(m *testing.M) {
|
||||
func TestIndexing(t *testing.T) {
|
||||
t.Run("IndexBlockEvents", func(t *testing.T) {
|
||||
indexer := &EventSink{store: testDB(), chainID: chainID}
|
||||
require.NoError(t, indexer.IndexBlockEvents(newTestBlockHeader()))
|
||||
require.NoError(t, indexer.IndexBlockEvents(newTestBlockEvents()))
|
||||
|
||||
verifyBlock(t, 1)
|
||||
verifyBlock(t, 2)
|
||||
@@ -156,7 +157,7 @@ func TestIndexing(t *testing.T) {
|
||||
require.NoError(t, verifyTimeStamp(tableBlocks))
|
||||
|
||||
// Attempting to reindex the same events should gracefully succeed.
|
||||
require.NoError(t, indexer.IndexBlockEvents(newTestBlockHeader()))
|
||||
require.NoError(t, indexer.IndexBlockEvents(newTestBlockEvents()))
|
||||
})
|
||||
|
||||
t.Run("IndexTxEvents", func(t *testing.T) {
|
||||
@@ -212,6 +213,7 @@ func TestIndexing(t *testing.T) {
|
||||
})
|
||||
|
||||
service := txindex.NewIndexerService(indexer.TxIndexer(), indexer.BlockIndexer(), eventBus, true)
|
||||
service.SetLogger(tmlog.TestingLogger())
|
||||
err = service.Start()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -221,16 +223,16 @@ func TestIndexing(t *testing.T) {
|
||||
})
|
||||
|
||||
// publish block with txs
|
||||
err = eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{
|
||||
Header: types.Header{Height: 1},
|
||||
NumTxs: int64(2),
|
||||
err = eventBus.PublishEventNewBlockEvents(types.EventDataNewBlockEvents{
|
||||
Height: 1,
|
||||
NumTxs: 2,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
txResult1 := &abci.TxResult{
|
||||
Height: 1,
|
||||
Index: uint32(0),
|
||||
Tx: types.Tx("foo"),
|
||||
Result: abci.ResponseDeliverTx{Code: 0},
|
||||
Result: abci.ExecTxResult{Code: 0},
|
||||
}
|
||||
err = eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult1})
|
||||
require.NoError(t, err)
|
||||
@@ -238,7 +240,7 @@ func TestIndexing(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: uint32(1),
|
||||
Tx: types.Tx("bar"),
|
||||
Result: abci.ResponseDeliverTx{Code: 1},
|
||||
Result: abci.ExecTxResult{Code: 1},
|
||||
}
|
||||
err = eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult2})
|
||||
require.NoError(t, err)
|
||||
@@ -253,22 +255,16 @@ func TestStop(t *testing.T) {
|
||||
require.NoError(t, indexer.Stop())
|
||||
}
|
||||
|
||||
// newTestBlockHeader constructs a fresh copy of a block header containing
|
||||
// newTestBlock constructs a fresh copy of a new block event containing
|
||||
// known test values to exercise the indexer.
|
||||
func newTestBlockHeader() types.EventDataNewBlockHeader {
|
||||
return types.EventDataNewBlockHeader{
|
||||
Header: types.Header{Height: 1},
|
||||
ResultBeginBlock: abci.ResponseBeginBlock{
|
||||
Events: []abci.Event{
|
||||
makeIndexedEvent("begin_event.proposer", "FCAA001"),
|
||||
makeIndexedEvent("thingy.whatzit", "O.O"),
|
||||
},
|
||||
},
|
||||
ResultEndBlock: abci.ResponseEndBlock{
|
||||
Events: []abci.Event{
|
||||
makeIndexedEvent("end_event.foo", "100"),
|
||||
makeIndexedEvent("thingy.whatzit", "-.O"),
|
||||
},
|
||||
func newTestBlockEvents() types.EventDataNewBlockEvents {
|
||||
return types.EventDataNewBlockEvents{
|
||||
Height: 1,
|
||||
Events: []abci.Event{
|
||||
makeIndexedEvent("begin_event.proposer", "FCAA001"),
|
||||
makeIndexedEvent("thingy.whatzit", "O.O"),
|
||||
makeIndexedEvent("end_event.foo", "100"),
|
||||
makeIndexedEvent("thingy.whatzit", "-.O"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -307,7 +303,7 @@ func txResultWithEvents(events []abci.Event) *abci.TxResult {
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: types.Tx("HELLO WORLD"),
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Data: []byte{0},
|
||||
Code: abci.CodeTypeOK,
|
||||
Log: "",
|
||||
@@ -355,17 +351,8 @@ SELECT height FROM `+tableBlocks+` WHERE height = $1;
|
||||
if err := testDB().QueryRow(`
|
||||
SELECT type, height, chain_id FROM `+viewBlockEvents+`
|
||||
WHERE height = $1 AND type = $2 AND chain_id = $3;
|
||||
`, height, eventTypeBeginBlock, chainID).Err(); err == sql.ErrNoRows {
|
||||
t.Errorf("No %q event found for height=%d", eventTypeBeginBlock, height)
|
||||
} else if err != nil {
|
||||
t.Fatalf("Database query failed: %v", err)
|
||||
}
|
||||
|
||||
if err := testDB().QueryRow(`
|
||||
SELECT type, height, chain_id FROM `+viewBlockEvents+`
|
||||
WHERE height = $1 AND type = $2 AND chain_id = $3;
|
||||
`, height, eventTypeEndBlock, chainID).Err(); err == sql.ErrNoRows {
|
||||
t.Errorf("No %q event found for height=%d", eventTypeEndBlock, height)
|
||||
`, height, eventTypeFinalizeBlock, chainID).Err(); err == sql.ErrNoRows {
|
||||
t.Errorf("No %q event found for height=%d", eventTypeFinalizeBlock, height)
|
||||
} else if err != nil {
|
||||
t.Fatalf("Database query failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_processing_time",
|
||||
Help: "Time between BeginBlock and EndBlock in ms.",
|
||||
Help: "Time spent processig finalize block.",
|
||||
|
||||
Buckets: stdprometheus.LinearBuckets(1, 10, 10),
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ const (
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
type Metrics struct {
|
||||
// Time between BeginBlock and EndBlock in ms.
|
||||
// Time spent processing FinalizeBlock
|
||||
BlockProcessingTime metrics.Histogram `metrics_buckettype:"lin" metrics_bucketsizes:"1, 10, 10"`
|
||||
|
||||
// ConsensusParamUpdates is the total number of times the application has
|
||||
|
||||
+22
-22
@@ -4,9 +4,9 @@ package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
state "github.com/tendermint/tendermint/state"
|
||||
abcitypes "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
tendermintstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
state "github.com/tendermint/tendermint/state"
|
||||
|
||||
types "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -65,17 +65,15 @@ func (_m *Store) Load() (state.State, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadABCIResponses provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadABCIResponses(_a0 int64) (*tendermintstate.ABCIResponses, error) {
|
||||
// LoadConsensusParams provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadConsensusParams(_a0 int64) (types.ConsensusParams, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *tendermintstate.ABCIResponses
|
||||
if rf, ok := ret.Get(0).(func(int64) *tendermintstate.ABCIResponses); ok {
|
||||
var r0 types.ConsensusParams
|
||||
if rf, ok := ret.Get(0).(func(int64) types.ConsensusParams); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*tendermintstate.ABCIResponses)
|
||||
}
|
||||
r0 = ret.Get(0).(types.ConsensusParams)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
@@ -88,15 +86,17 @@ func (_m *Store) LoadABCIResponses(_a0 int64) (*tendermintstate.ABCIResponses, e
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadConsensusParams provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadConsensusParams(_a0 int64) (types.ConsensusParams, error) {
|
||||
// LoadFinalizeBlockResponse provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadFinalizeBlockResponse(_a0 int64) (*abcitypes.ResponseFinalizeBlock, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 types.ConsensusParams
|
||||
if rf, ok := ret.Get(0).(func(int64) types.ConsensusParams); ok {
|
||||
var r0 *abcitypes.ResponseFinalizeBlock
|
||||
if rf, ok := ret.Get(0).(func(int64) *abcitypes.ResponseFinalizeBlock); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(types.ConsensusParams)
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcitypes.ResponseFinalizeBlock)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
@@ -151,16 +151,16 @@ func (_m *Store) LoadFromDBOrGenesisFile(_a0 string) (state.State, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadLastABCIResponse provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadLastABCIResponse(_a0 int64) (*tendermintstate.ABCIResponses, error) {
|
||||
// LoadLastFinalizeBlockResponse provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadLastFinalizeBlockResponse(_a0 int64) (*abcitypes.ResponseFinalizeBlock, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *tendermintstate.ABCIResponses
|
||||
if rf, ok := ret.Get(0).(func(int64) *tendermintstate.ABCIResponses); ok {
|
||||
var r0 *abcitypes.ResponseFinalizeBlock
|
||||
if rf, ok := ret.Get(0).(func(int64) *abcitypes.ResponseFinalizeBlock); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*tendermintstate.ABCIResponses)
|
||||
r0 = ret.Get(0).(*abcitypes.ResponseFinalizeBlock)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,12 +225,12 @@ func (_m *Store) Save(_a0 state.State) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// SaveABCIResponses provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Store) SaveABCIResponses(_a0 int64, _a1 *tendermintstate.ABCIResponses) error {
|
||||
// SaveFinalizeBlockResponse provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Store) SaveFinalizeBlockResponse(_a0 int64, _a1 *abcitypes.ResponseFinalizeBlock) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(int64, *tendermintstate.ABCIResponses) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(int64, *abcitypes.ResponseFinalizeBlock) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ type State struct {
|
||||
LastHeightValidatorsChanged int64
|
||||
|
||||
// Consensus parameters used for validating blocks.
|
||||
// Changes returned by EndBlock and updated after Commit.
|
||||
// Changes returned by FinalizeBlock and updated after Commit.
|
||||
ConsensusParams types.ConsensusParams
|
||||
LastHeightConsensusParamsChanged int64
|
||||
|
||||
|
||||
+59
-91
@@ -18,7 +18,6 @@ import (
|
||||
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
|
||||
"github.com/tendermint/tendermint/internal/test"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -94,8 +93,8 @@ func TestStateSaveLoad(t *testing.T) {
|
||||
loadedState, state))
|
||||
}
|
||||
|
||||
// TestABCIResponsesSaveLoad tests saving and loading ABCIResponses.
|
||||
func TestABCIResponsesSaveLoad1(t *testing.T) {
|
||||
// TestFinalizeBlockResponsesSaveLoad1 tests saving and loading ABCIResponses.
|
||||
func TestFinalizeBlockResponsesSaveLoad1(t *testing.T) {
|
||||
tearDown, stateDB, state := setupTestCase(t)
|
||||
defer tearDown(t)
|
||||
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
|
||||
@@ -108,27 +107,25 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
|
||||
// Build mock responses.
|
||||
block := makeBlock(state, 2, new(types.Commit))
|
||||
|
||||
abciResponses := new(tmstate.ABCIResponses)
|
||||
dtxs := make([]*abci.ResponseDeliverTx, 2)
|
||||
abciResponses.DeliverTxs = dtxs
|
||||
abciResponses := new(abci.ResponseFinalizeBlock)
|
||||
dtxs := make([]*abci.ExecTxResult, 2)
|
||||
abciResponses.TxResults = dtxs
|
||||
|
||||
abciResponses.DeliverTxs[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Events: nil}
|
||||
abciResponses.DeliverTxs[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Events: nil}
|
||||
abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{
|
||||
abciResponses.TxResults[0] = &abci.ExecTxResult{Data: []byte("foo"), Events: nil}
|
||||
abciResponses.TxResults[1] = &abci.ExecTxResult{Data: []byte("bar"), Log: "ok", Events: nil}
|
||||
abciResponses.ValidatorUpdates = []abci.ValidatorUpdate{
|
||||
types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10),
|
||||
}}
|
||||
}
|
||||
|
||||
err := stateStore.SaveABCIResponses(block.Height, abciResponses)
|
||||
err := stateStore.SaveFinalizeBlockResponse(block.Height, abciResponses)
|
||||
require.NoError(t, err)
|
||||
loadedABCIResponses, err := stateStore.LoadABCIResponses(block.Height)
|
||||
assert.Nil(err)
|
||||
assert.Equal(abciResponses, loadedABCIResponses,
|
||||
fmt.Sprintf("ABCIResponses don't match:\ngot: %v\nexpected: %v\n",
|
||||
loadedABCIResponses, abciResponses))
|
||||
loadedABCIResponses, err := stateStore.LoadFinalizeBlockResponse(block.Height)
|
||||
assert.NoError(err)
|
||||
assert.Equal(abciResponses, loadedABCIResponses)
|
||||
}
|
||||
|
||||
// TestResultsSaveLoad tests saving and loading ABCI results.
|
||||
func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
// TestResultsSaveLoad tests saving and loading FinalizeBlock results.
|
||||
func TestFinalizeBlockResponsesSaveLoad2(t *testing.T) {
|
||||
tearDown, stateDB, _ := setupTestCase(t)
|
||||
defer tearDown(t)
|
||||
assert := assert.New(t)
|
||||
@@ -140,22 +137,22 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
cases := [...]struct {
|
||||
// Height is implied to equal index+2,
|
||||
// as block 1 is created from genesis.
|
||||
added []*abci.ResponseDeliverTx
|
||||
expected []*abci.ResponseDeliverTx
|
||||
added []*abci.ExecTxResult
|
||||
expected []*abci.ExecTxResult
|
||||
}{
|
||||
0: {
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
1: {
|
||||
[]*abci.ResponseDeliverTx{
|
||||
[]*abci.ExecTxResult{
|
||||
{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
|
||||
},
|
||||
[]*abci.ResponseDeliverTx{
|
||||
[]*abci.ExecTxResult{
|
||||
{Code: 32, Data: []byte("Hello")},
|
||||
}},
|
||||
2: {
|
||||
[]*abci.ResponseDeliverTx{
|
||||
[]*abci.ExecTxResult{
|
||||
{Code: 383},
|
||||
{
|
||||
Data: []byte("Gotcha!"),
|
||||
@@ -165,7 +162,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
[]*abci.ResponseDeliverTx{
|
||||
[]*abci.ExecTxResult{
|
||||
{Code: 383, Data: nil},
|
||||
{Code: 0, Data: []byte("Gotcha!"), Events: []abci.Event{
|
||||
{Type: "type1", Attributes: []abci.EventAttribute{{Key: "a", Value: "1"}}},
|
||||
@@ -177,7 +174,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
nil,
|
||||
},
|
||||
4: {
|
||||
[]*abci.ResponseDeliverTx{nil},
|
||||
[]*abci.ExecTxResult{nil},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
@@ -185,34 +182,32 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||
// Query all before, this should return error.
|
||||
for i := range cases {
|
||||
h := int64(i + 1)
|
||||
res, err := stateStore.LoadABCIResponses(h)
|
||||
res, err := stateStore.LoadFinalizeBlockResponse(h)
|
||||
assert.Error(err, "%d: %#v", i, res)
|
||||
}
|
||||
|
||||
// Add all cases.
|
||||
for i, tc := range cases {
|
||||
h := int64(i + 1) // last block height, one below what we save
|
||||
responses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
DeliverTxs: tc.added,
|
||||
EndBlock: &abci.ResponseEndBlock{},
|
||||
responses := &abci.ResponseFinalizeBlock{
|
||||
TxResults: tc.added,
|
||||
AgreedAppData: []byte(fmt.Sprintf("%d", h)),
|
||||
}
|
||||
err := stateStore.SaveABCIResponses(h, responses)
|
||||
err := stateStore.SaveFinalizeBlockResponse(h, responses)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Query all before, should return expected value.
|
||||
for i, tc := range cases {
|
||||
h := int64(i + 1)
|
||||
res, err := stateStore.LoadABCIResponses(h)
|
||||
res, err := stateStore.LoadFinalizeBlockResponse(h)
|
||||
if assert.NoError(err, "%d", i) {
|
||||
t.Log(res)
|
||||
responses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
DeliverTxs: tc.expected,
|
||||
EndBlock: &abci.ResponseEndBlock{},
|
||||
responses := &abci.ResponseFinalizeBlock{
|
||||
TxResults: tc.expected,
|
||||
AgreedAppData: []byte(fmt.Sprintf("%d", h)),
|
||||
}
|
||||
assert.Equal(sm.ABCIResponsesResultsHash(responses), sm.ABCIResponsesResultsHash(res), "%d", i)
|
||||
assert.Equal(sm.TxResultsHash(responses.TxResults), sm.TxResultsHash(res.TxResults), "%d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,7 +276,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
power++
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(t, state, power)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
@@ -458,11 +453,8 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) {
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
assert.NoError(t, err)
|
||||
@@ -575,11 +567,8 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
// no updates:
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
@@ -639,7 +628,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
|
||||
updatedVal2,
|
||||
)
|
||||
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedState3, err := sm.UpdateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
@@ -678,11 +667,8 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
|
||||
// no changes in voting power and both validators have same voting power
|
||||
// -> proposers should alternate:
|
||||
oldState := updatedState3
|
||||
abciResponses = &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses = &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
oldState, err = sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
@@ -694,11 +680,8 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
// no validator updates:
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
@@ -752,11 +735,8 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
oldState := state
|
||||
for i := 0; i < 10; i++ {
|
||||
// no updates:
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := makeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
@@ -786,9 +766,8 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
firstAddedVal := abci.ValidatorUpdate{PubKey: fvp, Power: firstAddedValVotingPower}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{firstAddedVal})
|
||||
assert.NoError(t, err)
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal}},
|
||||
abciResponses := &abci.ResponseFinalizeBlock{
|
||||
ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal},
|
||||
}
|
||||
block := makeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
|
||||
@@ -802,11 +781,8 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
lastState := updatedState
|
||||
for i := 0; i < 200; i++ {
|
||||
// no updates:
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := makeBlock(lastState, lastState.LastBlockHeight+1, new(types.Commit))
|
||||
@@ -842,9 +818,8 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{addedVal})
|
||||
assert.NoError(t, err)
|
||||
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{addedVal}},
|
||||
abciResponses := &abci.ResponseFinalizeBlock{
|
||||
ValidatorUpdates: []abci.ValidatorUpdate{addedVal},
|
||||
}
|
||||
block := makeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
@@ -860,9 +835,8 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
gp, err := cryptoenc.PubKeyToProto(genesisPubKey)
|
||||
require.NoError(t, err)
|
||||
removeGenesisVal := abci.ValidatorUpdate{PubKey: gp, Power: 0}
|
||||
abciResponses = &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal}},
|
||||
abciResponses = &abci.ResponseFinalizeBlock{
|
||||
ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal},
|
||||
}
|
||||
|
||||
block = makeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
@@ -872,7 +846,7 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
updatedState, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
@@ -885,11 +859,8 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
count := 0
|
||||
isProposerUnchanged := true
|
||||
for isProposerUnchanged {
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
block = makeBlock(curState, curState.LastBlockHeight+1, new(types.Commit))
|
||||
|
||||
@@ -913,11 +884,8 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
proposers := make([]*types.Validator, numVals)
|
||||
for i := 0; i < 100; i++ {
|
||||
// no updates:
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
abciResponses := &abci.ResponseFinalizeBlock{}
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := makeBlock(updatedState, updatedState.LastBlockHeight+1, new(types.Commit))
|
||||
@@ -988,7 +956,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
|
||||
|
||||
// Save state etc.
|
||||
var validatorUpdates []*types.Validator
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
|
||||
require.Nil(t, err)
|
||||
@@ -1068,7 +1036,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
cp = params[changeIndex]
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesParams(t, state, cp.ToProto())
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
|
||||
|
||||
|
||||
+72
-38
@@ -58,17 +58,17 @@ type Store interface {
|
||||
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)
|
||||
// LoadFinalizeBlockResponse loads the abciResponse for a given height
|
||||
LoadFinalizeBlockResponse(int64) (*abci.ResponseFinalizeBlock, error)
|
||||
// LoadLastABCIResponse loads the last abciResponse for a given height
|
||||
LoadLastABCIResponse(int64) (*tmstate.ABCIResponses, error)
|
||||
LoadLastFinalizeBlockResponse(int64) (*abci.ResponseFinalizeBlock, error)
|
||||
// LoadConsensusParams loads the consensus params for a given height
|
||||
LoadConsensusParams(int64) (types.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
|
||||
// SaveFinalizeBlockResponse saves ABCIResponses for a given height
|
||||
SaveFinalizeBlockResponse(int64, *abci.ResponseFinalizeBlock) 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 pruning and which height stop at
|
||||
PruneStates(int64, int64, int64) error
|
||||
@@ -85,7 +85,7 @@ type dbStore struct {
|
||||
|
||||
type StoreOptions struct {
|
||||
// DiscardABCIResponses determines whether or not the store
|
||||
// retains all ABCIResponses. If DiscardABCiResponses is enabled,
|
||||
// retains all ABCIResponses. If DiscardABCIResponses is enabled,
|
||||
// the store will maintain only the response object from the latest
|
||||
// height.
|
||||
DiscardABCIResponses bool
|
||||
@@ -367,20 +367,20 @@ func (store dbStore) PruneStates(from int64, to int64, evidenceThresholdHeight i
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// ABCIResponsesResultsHash returns the root hash of a Merkle tree of
|
||||
// ResponseDeliverTx responses (see ABCIResults.Hash)
|
||||
// TxResultsHash returns the root hash of a Merkle tree of
|
||||
// ExecTxResulst responses (see ABCIResults.Hash)
|
||||
//
|
||||
// See merkle.SimpleHashFromByteSlices
|
||||
func ABCIResponsesResultsHash(ar *tmstate.ABCIResponses) []byte {
|
||||
return types.NewResults(ar.DeliverTxs).Hash()
|
||||
func TxResultsHash(txResults []*abci.ExecTxResult) []byte {
|
||||
return types.NewResults(txResults).Hash()
|
||||
}
|
||||
|
||||
// LoadABCIResponses loads the ABCIResponses for the given height from the
|
||||
// database. If the node has DiscardABCIResponses set to true, ErrABCIResponsesNotPersisted
|
||||
// LoadFinalizeBlockResponse loads the DiscardABCIResponses for the given height from the
|
||||
// database. If the node has D set to true, ErrABCIResponsesNotPersisted
|
||||
// is persisted. If not found, ErrNoABCIResponsesForHeight is returned.
|
||||
func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, error) {
|
||||
func (store dbStore) LoadFinalizeBlockResponse(height int64) (*abci.ResponseFinalizeBlock, error) {
|
||||
if store.DiscardABCIResponses {
|
||||
return nil, ErrABCIResponsesNotPersisted
|
||||
return nil, ErrFinalizeBlockResponsesNotPersisted
|
||||
}
|
||||
|
||||
buf, err := store.db.Get(calcABCIResponsesKey(height))
|
||||
@@ -388,29 +388,39 @@ func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, er
|
||||
return nil, err
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
|
||||
return nil, ErrNoABCIResponsesForHeight{height}
|
||||
}
|
||||
|
||||
abciResponses := new(tmstate.ABCIResponses)
|
||||
err = abciResponses.Unmarshal(buf)
|
||||
resp := new(abci.ResponseFinalizeBlock)
|
||||
err = resp.Unmarshal(buf)
|
||||
if err != nil {
|
||||
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
||||
tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has
|
||||
changed: %v\n`, err))
|
||||
// The data might be of the legacy ABCI response type, so
|
||||
// we try to unmarshal that
|
||||
legacyResp := new(tmstate.LegacyABCIResponses)
|
||||
rerr := legacyResp.Unmarshal(buf)
|
||||
if rerr != nil {
|
||||
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
|
||||
tmos.Exit(fmt.Sprintf(`LoadFinalizeBlockResponse: Data has been corrupted or its spec has
|
||||
changed: %v\n`, err))
|
||||
}
|
||||
// The state store contains the old format. Migrate to
|
||||
// the new ResponseFinalizeBlock format. Note that the
|
||||
// new struct expects the AgreedAppData which we don't have.
|
||||
return responseFinalizeBlockFromLegacy(legacyResp), nil
|
||||
}
|
||||
|
||||
// TODO: ensure that buf is completely read.
|
||||
|
||||
return abciResponses, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// LoadLastABCIResponses loads the ABCIResponses from the most recent height.
|
||||
// LoadLastFinalizeBlockResponses loads the FinalizeBlockResponses from the most recent height.
|
||||
// The height parameter is used to ensure that the response corresponds to the latest height.
|
||||
// If not, an error is returned.
|
||||
//
|
||||
// This method is used for recovering in the case that we called the Commit ABCI
|
||||
// method on the application but crashed before persisting the results.
|
||||
func (store dbStore) LoadLastABCIResponse(height int64) (*tmstate.ABCIResponses, error) {
|
||||
func (store dbStore) LoadLastFinalizeBlockResponse(height int64) (*abci.ResponseFinalizeBlock, error) {
|
||||
bz, err := store.db.Get(lastABCIResponseKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -420,41 +430,52 @@ func (store dbStore) LoadLastABCIResponse(height int64) (*tmstate.ABCIResponses,
|
||||
return nil, errors.New("no last ABCI response has been persisted")
|
||||
}
|
||||
|
||||
abciResponse := new(tmstate.ABCIResponsesInfo)
|
||||
err = abciResponse.Unmarshal(bz)
|
||||
info := new(tmstate.ABCIResponsesInfo)
|
||||
err = info.Unmarshal(bz)
|
||||
if err != nil {
|
||||
tmos.Exit(fmt.Sprintf(`LoadLastABCIResponses: Data has been corrupted or its spec has
|
||||
tmos.Exit(fmt.Sprintf(`LoadLastFinalizeBlockResponse: Data has been corrupted or its spec has
|
||||
changed: %v\n`, err))
|
||||
}
|
||||
|
||||
// Here we validate the result by comparing its height to the expected height.
|
||||
if height != abciResponse.GetHeight() {
|
||||
return nil, errors.New("expected height %d but last stored abci responses was at height %d")
|
||||
if height != info.GetHeight() {
|
||||
return nil, fmt.Errorf("expected height %d but last stored abci responses was at height %d", height, info.GetHeight())
|
||||
}
|
||||
|
||||
return abciResponse.AbciResponses, nil
|
||||
// It is possible if this is called directly after an upgrade that
|
||||
// ResponseFinalizeBlock is nil. In which case we use the legacy
|
||||
// ABCI responses
|
||||
if info.ResponseFinalizeBlock == nil {
|
||||
// sanity check
|
||||
if info.LegacyAbciResponses == nil {
|
||||
panic("state store contains last abci response but it is empty")
|
||||
}
|
||||
return responseFinalizeBlockFromLegacy(info.LegacyAbciResponses), nil
|
||||
}
|
||||
|
||||
return info.ResponseFinalizeBlock, nil
|
||||
}
|
||||
|
||||
// SaveABCIResponses persists the ABCIResponses to the database.
|
||||
// SaveFinalizeBlockResponse persists the ResponseFinalizeBlock to the database.
|
||||
// This is useful in case we crash after app.Commit and before s.Save().
|
||||
// Responses are indexed by height so they can also be loaded later to produce
|
||||
// Merkle proofs.
|
||||
//
|
||||
// CONTRACT: height must be monotonically increasing every time this is called.
|
||||
func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error {
|
||||
var dtxs []*abci.ResponseDeliverTx
|
||||
func (store dbStore) SaveFinalizeBlockResponse(height int64, resp *abci.ResponseFinalizeBlock) error {
|
||||
var dtxs []*abci.ExecTxResult
|
||||
// strip nil values,
|
||||
for _, tx := range abciResponses.DeliverTxs {
|
||||
for _, tx := range resp.TxResults {
|
||||
if tx != nil {
|
||||
dtxs = append(dtxs, tx)
|
||||
}
|
||||
}
|
||||
abciResponses.DeliverTxs = dtxs
|
||||
resp.TxResults = dtxs
|
||||
|
||||
// If the flag is false then we save the ABCIResponse. This can be used for the /BlockResults
|
||||
// query or to reindex an event using the command line.
|
||||
if !store.DiscardABCIResponses {
|
||||
bz, err := abciResponses.Marshal()
|
||||
bz, err := resp.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -466,8 +487,8 @@ func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCI
|
||||
// We always save the last ABCI response for crash recovery.
|
||||
// This overwrites the previous saved ABCI Response.
|
||||
response := &tmstate.ABCIResponsesInfo{
|
||||
AbciResponses: abciResponses,
|
||||
Height: height,
|
||||
ResponseFinalizeBlock: resp,
|
||||
Height: height,
|
||||
}
|
||||
bz, err := response.Marshal()
|
||||
if err != nil {
|
||||
@@ -671,3 +692,16 @@ func min(a int64, b int64) int64 {
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// responseFinalizeBlockFromLegacy is a convenience function that takes the old abci responses and morphs
|
||||
// it to the finalize block response. Note that the agreed app data is missing
|
||||
func responseFinalizeBlockFromLegacy(legacyResp *tmstate.LegacyABCIResponses) *abci.ResponseFinalizeBlock {
|
||||
return &abci.ResponseFinalizeBlock{
|
||||
TxResults: legacyResp.DeliverTxs,
|
||||
ValidatorUpdates: legacyResp.EndBlock.ValidatorUpdates,
|
||||
ConsensusParamUpdates: legacyResp.EndBlock.ConsensusParamUpdates,
|
||||
Events: append(legacyResp.BeginBlock.Events, legacyResp.EndBlock.Events...),
|
||||
// NOTE: AgreedAppData is missing in the response but will
|
||||
// be caught and filled in consensus/replay.go
|
||||
}
|
||||
}
|
||||
|
||||
+70
-33
@@ -154,8 +154,8 @@ func TestPruneStates(t *testing.T) {
|
||||
err := stateStore.Save(state)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = stateStore.SaveABCIResponses(h, &tmstate.ABCIResponses{
|
||||
DeliverTxs: []*abci.ResponseDeliverTx{
|
||||
err = stateStore.SaveFinalizeBlockResponse(h, &abci.ResponseFinalizeBlock{
|
||||
TxResults: []*abci.ExecTxResult{
|
||||
{Data: []byte{1}},
|
||||
{Data: []byte{2}},
|
||||
{Data: []byte{3}},
|
||||
@@ -195,7 +195,7 @@ func TestPruneStates(t *testing.T) {
|
||||
require.Empty(t, params)
|
||||
}
|
||||
|
||||
abci, err := stateStore.LoadABCIResponses(h)
|
||||
abci, err := stateStore.LoadFinalizeBlockResponse(h)
|
||||
if expectABCI[h] {
|
||||
require.NoError(t, err, "abci height %v", h)
|
||||
require.NotNil(t, abci)
|
||||
@@ -208,22 +208,18 @@ func TestPruneStates(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestABCIResponsesResultsHash(t *testing.T) {
|
||||
responses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
DeliverTxs: []*abci.ResponseDeliverTx{
|
||||
{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
|
||||
},
|
||||
EndBlock: &abci.ResponseEndBlock{},
|
||||
func TestTxResultsHash(t *testing.T) {
|
||||
txResults := []*abci.ExecTxResult{
|
||||
{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
|
||||
}
|
||||
|
||||
root := sm.ABCIResponsesResultsHash(responses)
|
||||
root := sm.TxResultsHash(txResults)
|
||||
|
||||
// root should be Merkle tree root of DeliverTxs responses
|
||||
results := types.NewResults(responses.DeliverTxs)
|
||||
// root should be Merkle tree root of ExecTxResult responses
|
||||
results := types.NewResults(txResults)
|
||||
assert.Equal(t, root, results.Hash())
|
||||
|
||||
// test we can prove first DeliverTx
|
||||
// test we can prove first ExecTxResult
|
||||
proof := results.ProveResult(0)
|
||||
bz, err := results[0].Marshal()
|
||||
require.NoError(t, err)
|
||||
@@ -238,41 +234,39 @@ func sliceToMap(s []int64) map[int64]bool {
|
||||
return m
|
||||
}
|
||||
|
||||
func TestLastABCIResponses(t *testing.T) {
|
||||
func TestLastFinalizeBlockResponses(t *testing.T) {
|
||||
// create an empty state store.
|
||||
t.Run("Not persisting responses", func(t *testing.T) {
|
||||
stateDB := dbm.NewMemDB()
|
||||
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
|
||||
DiscardABCIResponses: false,
|
||||
})
|
||||
responses, err := stateStore.LoadABCIResponses(1)
|
||||
responses, err := stateStore.LoadFinalizeBlockResponse(1)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, responses)
|
||||
// stub the abciresponses.
|
||||
response1 := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
DeliverTxs: []*abci.ResponseDeliverTx{
|
||||
response1 := &abci.ResponseFinalizeBlock{
|
||||
TxResults: []*abci.ExecTxResult{
|
||||
{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
|
||||
},
|
||||
EndBlock: &abci.ResponseEndBlock{},
|
||||
}
|
||||
// create new db and state store and set discard abciresponses to false.
|
||||
stateDB = dbm.NewMemDB()
|
||||
stateStore = sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false})
|
||||
height := int64(10)
|
||||
// save the last abci response.
|
||||
err = stateStore.SaveABCIResponses(height, response1)
|
||||
err = stateStore.SaveFinalizeBlockResponse(height, response1)
|
||||
require.NoError(t, err)
|
||||
// search for the last abciresponse and check if it has saved.
|
||||
lastResponse, err := stateStore.LoadLastABCIResponse(height)
|
||||
// search for the last finalize block response and check if it has saved.
|
||||
lastResponse, err := stateStore.LoadLastFinalizeBlockResponse(height)
|
||||
require.NoError(t, err)
|
||||
// check to see if the saved response height is the same as the loaded height.
|
||||
assert.Equal(t, lastResponse, response1)
|
||||
// use an incorret height to make sure the state store errors.
|
||||
_, err = stateStore.LoadLastABCIResponse(height + 1)
|
||||
_, err = stateStore.LoadLastFinalizeBlockResponse(height + 1)
|
||||
assert.Error(t, err)
|
||||
// check if the abci response didnt save in the abciresponses.
|
||||
responses, err = stateStore.LoadABCIResponses(height)
|
||||
responses, err = stateStore.LoadFinalizeBlockResponse(height)
|
||||
require.NoError(t, err, responses)
|
||||
require.Equal(t, response1, responses)
|
||||
})
|
||||
@@ -281,28 +275,71 @@ func TestLastABCIResponses(t *testing.T) {
|
||||
stateDB := dbm.NewMemDB()
|
||||
height := int64(10)
|
||||
// stub the second abciresponse.
|
||||
response2 := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
DeliverTxs: []*abci.ResponseDeliverTx{
|
||||
response2 := &abci.ResponseFinalizeBlock{
|
||||
TxResults: []*abci.ExecTxResult{
|
||||
{Code: 44, Data: []byte("Hello again"), Log: "????"},
|
||||
},
|
||||
EndBlock: &abci.ResponseEndBlock{},
|
||||
}
|
||||
// create a new statestore with the responses on.
|
||||
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
|
||||
DiscardABCIResponses: true,
|
||||
})
|
||||
// save an additional response.
|
||||
err := stateStore.SaveABCIResponses(height+1, response2)
|
||||
err := stateStore.SaveFinalizeBlockResponse(height+1, response2)
|
||||
require.NoError(t, err)
|
||||
// check to see if the response saved by calling the last response.
|
||||
lastResponse2, err := stateStore.LoadLastABCIResponse(height + 1)
|
||||
lastResponse2, err := stateStore.LoadLastFinalizeBlockResponse(height + 1)
|
||||
require.NoError(t, err)
|
||||
// check to see if the saved response height is the same as the loaded height.
|
||||
assert.Equal(t, response2, lastResponse2)
|
||||
// should error as we are no longer saving the response.
|
||||
_, err = stateStore.LoadABCIResponses(height + 1)
|
||||
assert.Equal(t, sm.ErrABCIResponsesNotPersisted, err)
|
||||
_, err = stateStore.LoadFinalizeBlockResponse(height + 1)
|
||||
assert.Equal(t, sm.ErrFinalizeBlockResponsesNotPersisted, err)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestFinalizeBlockRecoveryUsingLegacyABCIResponses(t *testing.T) {
|
||||
var (
|
||||
height int64 = 10
|
||||
lastABCIResponseKey = []byte("lastABCIResponseKey")
|
||||
memDB = dbm.NewMemDB()
|
||||
cp = types.DefaultConsensusParams().ToProto()
|
||||
legacyResp = tmstate.ABCIResponsesInfo{
|
||||
LegacyAbciResponses: &tmstate.LegacyABCIResponses{
|
||||
BeginBlock: &tmstate.ResponseBeginBlock{
|
||||
Events: []abci.Event{{
|
||||
Type: "begin_block",
|
||||
Attributes: []abci.EventAttribute{{
|
||||
Key: "key",
|
||||
Value: "value",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
DeliverTxs: []*abci.ExecTxResult{{
|
||||
Events: []abci.Event{{
|
||||
Type: "tx",
|
||||
Attributes: []abci.EventAttribute{{
|
||||
Key: "key",
|
||||
Value: "value",
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
EndBlock: &tmstate.ResponseEndBlock{
|
||||
ConsensusParamUpdates: &cp,
|
||||
},
|
||||
},
|
||||
Height: height,
|
||||
}
|
||||
)
|
||||
bz, err := legacyResp.Marshal()
|
||||
require.NoError(t, err)
|
||||
// should keep this in parity with state/store.go
|
||||
require.NoError(t, memDB.Set(lastABCIResponseKey, bz))
|
||||
stateStore := sm.NewStore(memDB, sm.StoreOptions{DiscardABCIResponses: false})
|
||||
resp, err := stateStore.LoadLastFinalizeBlockResponse(height)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp.ConsensusParamUpdates, &cp)
|
||||
require.Equal(t, resp.Events, legacyResp.LegacyAbciResponses.BeginBlock.Events)
|
||||
require.Equal(t, resp.TxResults[0], legacyResp.LegacyAbciResponses.DeliverTxs[0])
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ func (is *IndexerService) OnStart() error {
|
||||
// Use SubscribeUnbuffered here to ensure both subscriptions does not get
|
||||
// canceled due to not pulling messages fast enough. Cause this might
|
||||
// sometimes happen when there are no other subscribers.
|
||||
blockHeadersSub, err := is.eventBus.SubscribeUnbuffered(
|
||||
blockSub, err := is.eventBus.SubscribeUnbuffered(
|
||||
context.Background(),
|
||||
subscriber,
|
||||
types.EventQueryNewBlockHeader)
|
||||
types.EventQueryNewBlockEvents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,12 +59,14 @@ func (is *IndexerService) OnStart() error {
|
||||
|
||||
go func() {
|
||||
for {
|
||||
msg := <-blockHeadersSub.Out()
|
||||
eventDataHeader := msg.Data().(types.EventDataNewBlockHeader)
|
||||
height := eventDataHeader.Header.Height
|
||||
batch := NewBatch(eventDataHeader.NumTxs)
|
||||
msg := <-blockSub.Out()
|
||||
eventNewBlockEvents := msg.Data().(types.EventDataNewBlockEvents)
|
||||
height := eventNewBlockEvents.Height
|
||||
numTxs := eventNewBlockEvents.NumTxs
|
||||
|
||||
for i := int64(0); i < eventDataHeader.NumTxs; i++ {
|
||||
batch := NewBatch(numTxs)
|
||||
|
||||
for i := int64(0); i < numTxs; i++ {
|
||||
msg2 := <-txsSub.Out()
|
||||
txResult := msg2.Data().(types.EventDataTx).TxResult
|
||||
|
||||
@@ -85,7 +87,7 @@ func (is *IndexerService) OnStart() error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := is.blockIdxr.Index(eventDataHeader); err != nil {
|
||||
if err := is.blockIdxr.Index(eventNewBlockEvents); err != nil {
|
||||
is.Logger.Error("failed to index block", "height", height, "err", err)
|
||||
if is.terminateOnError {
|
||||
if err := is.Stop(); err != nil {
|
||||
@@ -106,7 +108,7 @@ func (is *IndexerService) OnStart() error {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
is.Logger.Debug("indexed transactions", "height", height, "num_txs", eventDataHeader.NumTxs)
|
||||
is.Logger.Debug("indexed block txs", "height", height, "num_txs", numTxs)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -42,9 +42,21 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// publish block with txs
|
||||
err = eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{
|
||||
Header: types.Header{Height: 1},
|
||||
// publish block with events
|
||||
err = eventBus.PublishEventNewBlockEvents(types.EventDataNewBlockEvents{
|
||||
Height: 1,
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "begin_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: "proposer",
|
||||
Value: "FCAA001",
|
||||
Index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NumTxs: int64(2),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -52,7 +64,7 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: uint32(0),
|
||||
Tx: types.Tx("foo"),
|
||||
Result: abci.ResponseDeliverTx{Code: 0},
|
||||
Result: abci.ExecTxResult{Code: 0},
|
||||
}
|
||||
err = eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult1})
|
||||
require.NoError(t, err)
|
||||
@@ -60,7 +72,7 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: uint32(1),
|
||||
Tx: types.Tx("bar"),
|
||||
Result: abci.ResponseDeliverTx{Code: 0},
|
||||
Result: abci.ExecTxResult{Code: 0},
|
||||
}
|
||||
err = eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult2})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -47,7 +47,7 @@ func BenchmarkTxSearch(b *testing.B) {
|
||||
Height: int64(i),
|
||||
Index: 0,
|
||||
Tx: types.Tx(string(txBz)),
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Data: []byte{0},
|
||||
Code: abci.CodeTypeOK,
|
||||
Log: "",
|
||||
|
||||
+10
-10
@@ -27,7 +27,7 @@ func TestTxIndex(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: tx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Data: []byte{0},
|
||||
Code: abci.CodeTypeOK, Log: "", Events: nil,
|
||||
},
|
||||
@@ -50,7 +50,7 @@ func TestTxIndex(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: tx2,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Data: []byte{0},
|
||||
Code: abci.CodeTypeOK, Log: "", Events: nil,
|
||||
},
|
||||
@@ -273,7 +273,7 @@ func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: mockTx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Code: abci.CodeTypeOK,
|
||||
},
|
||||
},
|
||||
@@ -281,7 +281,7 @@ func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) {
|
||||
Height: 2,
|
||||
Index: 0,
|
||||
Tx: mockTx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Code: abci.CodeTypeOK + 1,
|
||||
},
|
||||
},
|
||||
@@ -293,7 +293,7 @@ func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: mockTx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Code: abci.CodeTypeOK + 1,
|
||||
},
|
||||
},
|
||||
@@ -301,7 +301,7 @@ func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) {
|
||||
Height: 2,
|
||||
Index: 0,
|
||||
Tx: mockTx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Code: abci.CodeTypeOK + 1,
|
||||
},
|
||||
},
|
||||
@@ -313,7 +313,7 @@ func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) {
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: mockTx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Code: abci.CodeTypeOK,
|
||||
},
|
||||
},
|
||||
@@ -321,7 +321,7 @@ func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) {
|
||||
Height: 2,
|
||||
Index: 0,
|
||||
Tx: mockTx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Code: abci.CodeTypeOK,
|
||||
},
|
||||
},
|
||||
@@ -415,7 +415,7 @@ func txResultWithEvents(events []abci.Event) *abci.TxResult {
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: tx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Data: []byte{0},
|
||||
Code: abci.CodeTypeOK,
|
||||
Log: "",
|
||||
@@ -441,7 +441,7 @@ func benchmarkTxIndex(txsCount int64, b *testing.B) {
|
||||
Height: 1,
|
||||
Index: txIndex,
|
||||
Tx: tx,
|
||||
Result: abci.ResponseDeliverTx{
|
||||
Result: abci.ExecTxResult{
|
||||
Data: []byte{0},
|
||||
Code: abci.CodeTypeOK,
|
||||
Log: "",
|
||||
|
||||
Reference in New Issue
Block a user