diff --git a/abci/types/mocks/application.go b/abci/types/mocks/application.go new file mode 100644 index 000000000..4e19828ac --- /dev/null +++ b/abci/types/mocks/application.go @@ -0,0 +1,237 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + types "github.com/tendermint/tendermint/abci/types" +) + +// Application is an autogenerated mock type for the Application type +type Application struct { + mock.Mock +} + +// ApplySnapshotChunk provides a mock function with given fields: _a0 +func (_m *Application) ApplySnapshotChunk(_a0 types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk { + ret := _m.Called(_a0) + + var r0 types.ResponseApplySnapshotChunk + if rf, ok := ret.Get(0).(func(types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseApplySnapshotChunk) + } + + return r0 +} + +// BeginBlock provides a mock function with given fields: _a0 +func (_m *Application) BeginBlock(_a0 types.RequestBeginBlock) types.ResponseBeginBlock { + ret := _m.Called(_a0) + + var r0 types.ResponseBeginBlock + if rf, ok := ret.Get(0).(func(types.RequestBeginBlock) types.ResponseBeginBlock); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseBeginBlock) + } + + return r0 +} + +// CheckTx provides a mock function with given fields: _a0 +func (_m *Application) CheckTx(_a0 types.RequestCheckTx) types.ResponseCheckTx { + ret := _m.Called(_a0) + + var r0 types.ResponseCheckTx + if rf, ok := ret.Get(0).(func(types.RequestCheckTx) types.ResponseCheckTx); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseCheckTx) + } + + return r0 +} + +// Commit provides a mock function with given fields: +func (_m *Application) Commit() types.ResponseCommit { + ret := _m.Called() + + var r0 types.ResponseCommit + if rf, ok := ret.Get(0).(func() types.ResponseCommit); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(types.ResponseCommit) + } + + return r0 +} + +// DeliverTx provides a mock function with given fields: _a0 +func (_m *Application) DeliverTx(_a0 types.RequestDeliverTx) types.ResponseDeliverTx { + ret := _m.Called(_a0) + + var r0 types.ResponseDeliverTx + if rf, ok := ret.Get(0).(func(types.RequestDeliverTx) types.ResponseDeliverTx); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseDeliverTx) + } + + return r0 +} + +// EndBlock provides a mock function with given fields: _a0 +func (_m *Application) EndBlock(_a0 types.RequestEndBlock) types.ResponseEndBlock { + ret := _m.Called(_a0) + + var r0 types.ResponseEndBlock + if rf, ok := ret.Get(0).(func(types.RequestEndBlock) types.ResponseEndBlock); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseEndBlock) + } + + return r0 +} + +// ExtendVote provides a mock function with given fields: _a0 +func (_m *Application) ExtendVote(_a0 types.RequestExtendVote) types.ResponseExtendVote { + ret := _m.Called(_a0) + + var r0 types.ResponseExtendVote + if rf, ok := ret.Get(0).(func(types.RequestExtendVote) types.ResponseExtendVote); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseExtendVote) + } + + return r0 +} + +// Info provides a mock function with given fields: _a0 +func (_m *Application) Info(_a0 types.RequestInfo) types.ResponseInfo { + ret := _m.Called(_a0) + + var r0 types.ResponseInfo + if rf, ok := ret.Get(0).(func(types.RequestInfo) types.ResponseInfo); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseInfo) + } + + return r0 +} + +// InitChain provides a mock function with given fields: _a0 +func (_m *Application) InitChain(_a0 types.RequestInitChain) types.ResponseInitChain { + ret := _m.Called(_a0) + + var r0 types.ResponseInitChain + if rf, ok := ret.Get(0).(func(types.RequestInitChain) types.ResponseInitChain); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseInitChain) + } + + return r0 +} + +// ListSnapshots provides a mock function with given fields: _a0 +func (_m *Application) ListSnapshots(_a0 types.RequestListSnapshots) types.ResponseListSnapshots { + ret := _m.Called(_a0) + + var r0 types.ResponseListSnapshots + if rf, ok := ret.Get(0).(func(types.RequestListSnapshots) types.ResponseListSnapshots); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseListSnapshots) + } + + return r0 +} + +// LoadSnapshotChunk provides a mock function with given fields: _a0 +func (_m *Application) LoadSnapshotChunk(_a0 types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk { + ret := _m.Called(_a0) + + var r0 types.ResponseLoadSnapshotChunk + if rf, ok := ret.Get(0).(func(types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseLoadSnapshotChunk) + } + + return r0 +} + +// OfferSnapshot provides a mock function with given fields: _a0 +func (_m *Application) OfferSnapshot(_a0 types.RequestOfferSnapshot) types.ResponseOfferSnapshot { + ret := _m.Called(_a0) + + var r0 types.ResponseOfferSnapshot + if rf, ok := ret.Get(0).(func(types.RequestOfferSnapshot) types.ResponseOfferSnapshot); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseOfferSnapshot) + } + + return r0 +} + +// PrepareProposal provides a mock function with given fields: _a0 +func (_m *Application) PrepareProposal(_a0 types.RequestPrepareProposal) types.ResponsePrepareProposal { + ret := _m.Called(_a0) + + var r0 types.ResponsePrepareProposal + if rf, ok := ret.Get(0).(func(types.RequestPrepareProposal) types.ResponsePrepareProposal); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponsePrepareProposal) + } + + return r0 +} + +// ProcessProposal provides a mock function with given fields: _a0 +func (_m *Application) ProcessProposal(_a0 types.RequestProcessProposal) types.ResponseProcessProposal { + ret := _m.Called(_a0) + + var r0 types.ResponseProcessProposal + if rf, ok := ret.Get(0).(func(types.RequestProcessProposal) types.ResponseProcessProposal); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseProcessProposal) + } + + return r0 +} + +// Query provides a mock function with given fields: _a0 +func (_m *Application) Query(_a0 types.RequestQuery) types.ResponseQuery { + ret := _m.Called(_a0) + + var r0 types.ResponseQuery + if rf, ok := ret.Get(0).(func(types.RequestQuery) types.ResponseQuery); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseQuery) + } + + return r0 +} + +// VerifyVoteExtension provides a mock function with given fields: _a0 +func (_m *Application) VerifyVoteExtension(_a0 types.RequestVerifyVoteExtension) types.ResponseVerifyVoteExtension { + ret := _m.Called(_a0) + + var r0 types.ResponseVerifyVoteExtension + if rf, ok := ret.Get(0).(func(types.RequestVerifyVoteExtension) types.ResponseVerifyVoteExtension); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(types.ResponseVerifyVoteExtension) + } + + return r0 +} diff --git a/abci/types/mocks/base.go b/abci/types/mocks/base.go new file mode 100644 index 000000000..c54d4b356 --- /dev/null +++ b/abci/types/mocks/base.go @@ -0,0 +1,207 @@ +package mocks + +import ( + types "github.com/tendermint/tendermint/abci/types" +) + +type baseMock struct { + base *types.BaseApplication + *Application +} + +func NewBaseMock() baseMock { + return baseMock{ + base: types.NewBaseApplication(), + Application: new(Application), + } +} + +// Info/Query Connection +// Return application info +func (m baseMock) Info(input types.RequestInfo) types.ResponseInfo { + var ret types.ResponseInfo + defer func() { + if r := recover(); r != nil { + ret = m.base.Info(input) + } + }() + ret = m.base.Info(input) + return ret +} + +func (m baseMock) Query(input types.RequestQuery) types.ResponseQuery { + var ret types.ResponseQuery + defer func() { + if r := recover(); r != nil { + ret = m.base.Query(input) + } + }() + ret = m.base.Query(input) + return ret +} + +// Mempool Connection +// Validate a tx for the mempool +func (m baseMock) CheckTx(input types.RequestCheckTx) types.ResponseCheckTx { + var ret types.ResponseCheckTx + defer func() { + if r := recover(); r != nil { + ret = m.base.CheckTx(input) + } + }() + ret = m.base.CheckTx(input) + return ret +} + +// Consensus Connection +// Initialize blockchain w validators/other info from TendermintCore +func (m baseMock) InitChain(input types.RequestInitChain) types.ResponseInitChain { + var ret types.ResponseInitChain + defer func() { + if r := recover(); r != nil { + ret = m.base.InitChain(input) + } + }() + ret = m.base.InitChain(input) + return ret +} + +func (m baseMock) PrepareProposal(input types.RequestPrepareProposal) types.ResponsePrepareProposal { + var ret types.ResponsePrepareProposal + defer func() { + if r := recover(); r != nil { + ret = m.base.PrepareProposal(input) + } + }() + ret = m.base.PrepareProposal(input) + return ret +} + +func (m baseMock) ProcessProposal(input types.RequestProcessProposal) types.ResponseProcessProposal { + var r types.ResponseProcessProposal + defer func() { + if r := recover(); r != nil { + r = m.base.ProcessProposal(input) + } + }() + r = m.Application.ProcessProposal(input) + return r +} + +// Signals the beginning of a block +func (m baseMock) BeginBlock(input types.RequestBeginBlock) types.ResponseBeginBlock { + var ret types.ResponseBeginBlock + defer func() { + if r := recover(); r != nil { + ret = m.base.BeginBlock(input) + } + }() + ret = m.base.BeginBlock(input) + return ret +} + +// Deliver a tx for full processing +func (m baseMock) DeliverTx(input types.RequestDeliverTx) types.ResponseDeliverTx { + var ret types.ResponseDeliverTx + defer func() { + if r := recover(); r != nil { + ret = m.base.DeliverTx(input) + } + }() + ret = m.base.DeliverTx(input) + return ret +} + +// Signals the end of a block, returns changes to the validator set +func (m baseMock) EndBlock(input types.RequestEndBlock) types.ResponseEndBlock { + var ret types.ResponseEndBlock + defer func() { + if r := recover(); r != nil { + ret = m.base.EndBlock(input) + } + }() + ret = m.base.EndBlock(input) + return ret +} + +// Commit the state and return the application Merkle root hash +func (m baseMock) Commit() types.ResponseCommit { + var ret types.ResponseCommit + defer func() { + if r := recover(); r != nil { + ret = m.base.Commit() + } + }() + ret = m.base.Commit() + return ret +} + +// Create application specific vote extension +func (m baseMock) ExtendVote(input types.RequestExtendVote) types.ResponseExtendVote { + var ret types.ResponseExtendVote + defer func() { + if r := recover(); r != nil { + ret = m.base.ExtendVote(input) + } + }() + ret = m.base.ExtendVote(input) + return ret +} + +// Verify application's vote extension data +func (m baseMock) VerifyVoteExtension(input types.RequestVerifyVoteExtension) types.ResponseVerifyVoteExtension { + var ret types.ResponseVerifyVoteExtension + defer func() { + if r := recover(); r != nil { + ret = m.base.VerifyVoteExtension(input) + } + }() + ret = m.base.VerifyVoteExtension(input) + return ret +} + +// State Sync Connection +// List available snapshots +func (m baseMock) ListSnapshots(input types.RequestListSnapshots) types.ResponseListSnapshots { + var ret types.ResponseListSnapshots + defer func() { + if r := recover(); r != nil { + ret = m.base.ListSnapshots(input) + } + }() + ret = m.base.ListSnapshots(input) + return ret +} + +func (m baseMock) OfferSnapshot(input types.RequestOfferSnapshot) types.ResponseOfferSnapshot { + var ret types.ResponseOfferSnapshot + defer func() { + if r := recover(); r != nil { + ret = m.base.OfferSnapshot(input) + } + }() + ret = m.base.OfferSnapshot(input) + return ret +} + +func (m baseMock) LoadSnapshotChunk(input types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk { + var ret types.ResponseLoadSnapshotChunk + defer func() { + if r := recover(); r != nil { + ret = m.base.LoadSnapshotChunk(input) + } + }() + ret = m.base.LoadSnapshotChunk(input) + return ret +} + +func (m baseMock) ApplySnapshotChunk(input types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk { + var ret types.ResponseApplySnapshotChunk + defer func() { + if r := recover(); r != nil { + ret = m.base.ApplySnapshotChunk(input) + } + }() + ret = m.base.ApplySnapshotChunk(input) + return ret +} diff --git a/internal/consensus/state.go b/internal/consensus/state.go index f06022767..60f4a5611 100644 --- a/internal/consensus/state.go +++ b/internal/consensus/state.go @@ -1462,6 +1462,7 @@ func (cs *State) defaultDoPrevote(ctx context.Context, height int64, round int32 if err != nil { panic(fmt.Sprintf("ProcessProposal: %v", err)) } + fmt.Println("valid? ", isAppValid) // Vote nil if the Application rejected the block if !isAppValid { diff --git a/internal/consensus/state_test.go b/internal/consensus/state_test.go index 863a570c7..1e0e74e4e 100644 --- a/internal/consensus/state_test.go +++ b/internal/consensus/state_test.go @@ -8,15 +8,18 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/abci/example/kvstore" + abcitypes "github.com/tendermint/tendermint/abci/types" abcimocks "github.com/tendermint/tendermint/abci/types/mocks" "github.com/tendermint/tendermint/crypto/tmhash" cstypes "github.com/tendermint/tendermint/internal/consensus/types" "github.com/tendermint/tendermint/internal/eventbus" tmpubsub "github.com/tendermint/tendermint/internal/pubsub" tmquery "github.com/tendermint/tendermint/internal/pubsub/query" + tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" tmrand "github.com/tendermint/tendermint/libs/rand" tmtime "github.com/tendermint/tendermint/libs/time" @@ -1928,54 +1931,62 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) { assert.True(t, rs.ValidBlockParts.Header().Equals(blockID.PartSetHeader)) assert.True(t, rs.ValidRound == round) } + func TestProcessProposalAccept(t *testing.T) { - cfg := configSetup(t) - logger := log.NewNopLogger() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + for _, testCase := range []struct { + name string + accept bool + prevoteIsNil bool + }{ + { + name: "accepted block is prevoted", + accept: true, + prevoteIsNil: false, + }, + { + name: "rejected block is not prevoted", + accept: false, + prevoteIsNil: true, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + cfg := configSetup(t) + logger := log.NewNopLogger() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - cs1, vss := makeState(ctx, t, cfg, logger, makeStateArgs{application: abcimocks.NewBaseMock()}) - vs2, vs3, vs4 := vss[1], vss[2], vss[3] - height, round := cs1.Height, cs1.Round + m := abcimocks.NewBaseMock() + m.On("ProcessProposal", mock.Anything).Return(abcitypes.ResponseProcessProposal{Accept: testCase.accept}) + cs1, _ := makeState(ctx, t, cfg, logger, makeStateArgs{application: m}) + height, round := cs1.Height, cs1.Round - partSize := types.BlockPartSizeBytes + proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal) + newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound) + pv1, err := cs1.privValidator.GetPubKey(ctx) + require.NoError(t, err) + addr := pv1.Address() + voteCh := subscribeToVoter(ctx, t, cs1, addr) - proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal) - timeoutWaitCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryTimeoutWait) - newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound) - pv1, err := cs1.privValidator.GetPubKey(ctx) - require.NoError(t, err) - addr := pv1.Address() - voteCh := subscribeToVoter(ctx, t, cs1, addr) + // start round and wait for propose and prevote + startTestRound(ctx, cs1, cs1.Height, round) + ensureNewRound(t, newRoundCh, height, round) - // start round and wait for propose and prevote - startTestRound(ctx, cs1, cs1.Height, round) - ensureNewRound(t, newRoundCh, height, round) - - ensureNewProposal(t, proposalCh, height, round) - rs := cs1.GetRoundState() - propBlock := rs.ProposalBlock - partSet, err := propBlock.MakePartSet(partSize) - require.NoError(t, err) - blockID := types.BlockID{ - Hash: propBlock.Hash(), - PartSetHeader: partSet.Header(), + ensureNewProposal(t, proposalCh, height, round) + rs := cs1.GetRoundState() + propBlock := rs.ProposalBlock + partSet, err := propBlock.MakePartSet(types.BlockPartSizeBytes) + require.NoError(t, err) + blockID := types.BlockID{ + Hash: propBlock.Hash(), + PartSetHeader: partSet.Header(), + } + var prevoteHash tmbytes.HexBytes + if !testCase.prevoteIsNil { + prevoteHash = blockID.Hash + } + ensurePrevoteMatch(t, voteCh, height, round, prevoteHash) + }) } - - ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash) - - // the others sign a polka - signAddVotes(ctx, t, cs1, tmproto.PrevoteType, cfg.ChainID(), blockID, vs2, vs3, vs4) - - ensurePrecommit(t, voteCh, height, round) - // we should have precommitted the proposed block in this round. - - validatePrecommit(ctx, t, cs1, round, round, vss[0], blockID.Hash, blockID.Hash) - - signAddVotes(ctx, t, cs1, tmproto.PrecommitType, cfg.ChainID(), types.BlockID{}, vs2, vs3, vs4) - - ensureNewTimeout(t, timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) - } // 4 vals, 3 Nil Precommits at P0