mirror of
https://github.com/tendermint/tendermint.git
synced 2026-04-25 10:10:30 +00:00
Sync PrepareProposal with Spec. Main part (#9158)
* ----start---- * [PARTIAL cherry-pick] ABCI Vote Extension 2 (#6885) * Cherry-picked #6567: state/types: refactor makeBlock, makeBlocks and makeTxs (#6567) * [Cherrypicked] types: remove panic from block methods (#7501) * [cherrypicked] abci++: synchronize PrepareProposal with the newest version of the spec (#8094) This change implements the logic for the PrepareProposal ABCI++ method call. The main logic for creating and issuing the PrepareProposal request lives in execution.go and is tested in a set of new tests in execution_test.go. This change also updates the mempool mock to use a mockery generated version and removes much of the plumbing for the no longer used ABCIResponses. * make proto-gen * Backported EvidenceList's method ToABCI from #7961 * make build * Fix mockery for Mempool * mockery * Backported abci Application mocks from #7961 * mockery2 * Fixed new PrepareProposal test cases in state/execution_test.go * Fixed returned errors in consensus/state.go * lint * Addressed @cmwaters' comment Co-authored-by: mconcat <monoidconcat@gmail.com> Co-authored-by: JayT106 <JayT106@users.noreply.github.com> Co-authored-by: Sam Kleinman <garen@tychoish.com> Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>
This commit is contained in:
@@ -173,6 +173,5 @@ func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.Respo
|
||||
|
||||
func (app *Application) PrepareProposal(
|
||||
req types.RequestPrepareProposal) types.ResponsePrepareProposal {
|
||||
return types.ResponsePrepareProposal{
|
||||
BlockData: req.BlockData}
|
||||
return types.ResponsePrepareProposal{}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,10 @@ func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) t
|
||||
return app.execValidatorTx(req.Tx)
|
||||
}
|
||||
|
||||
if isPrepareTx(req.Tx) {
|
||||
return app.execPrepareTx(req.Tx)
|
||||
}
|
||||
|
||||
// otherwise, update the key-value store
|
||||
return app.app.DeliverTx(req)
|
||||
}
|
||||
@@ -172,11 +176,7 @@ func (app *PersistentKVStoreApplication) ApplySnapshotChunk(
|
||||
|
||||
func (app *PersistentKVStoreApplication) PrepareProposal(
|
||||
req types.RequestPrepareProposal) types.ResponsePrepareProposal {
|
||||
if len(req.BlockData) > 1 && false { // this breaks TC: TestReactorValidatorSetChanges
|
||||
req.BlockData[1] = []byte("modified tx")
|
||||
}
|
||||
|
||||
return types.ResponsePrepareProposal{BlockData: req.BlockData}
|
||||
return types.ResponsePrepareProposal{TxRecords: app.substPrepareTx(req.Txs)}
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
@@ -293,3 +293,46 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate
|
||||
|
||||
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
|
||||
const PreparePrefix = "prepare"
|
||||
|
||||
func isPrepareTx(tx []byte) bool {
|
||||
return bytes.HasPrefix(tx, []byte(PreparePrefix))
|
||||
}
|
||||
|
||||
// execPrepareTx is noop. tx data is considered as placeholder
|
||||
// and is substitute at the PrepareProposal.
|
||||
func (app *PersistentKVStoreApplication) execPrepareTx(tx []byte) types.ResponseDeliverTx {
|
||||
// noop
|
||||
return types.ResponseDeliverTx{}
|
||||
}
|
||||
|
||||
// substPrepareTx substitutes all the transactions prefixed with 'prepare' in the
|
||||
// proposal for transactions with the prefix strips.
|
||||
// It marks all of the original transactions as 'REMOVED' so that
|
||||
// Tendermint will remove them from its mempool.
|
||||
func (app *PersistentKVStoreApplication) substPrepareTx(blockData [][]byte) []*types.TxRecord {
|
||||
trs := make([]*types.TxRecord, len(blockData))
|
||||
var removed []*types.TxRecord
|
||||
for i, tx := range blockData {
|
||||
if isPrepareTx(tx) {
|
||||
removed = append(removed, &types.TxRecord{
|
||||
Tx: tx,
|
||||
Action: types.TxRecord_REMOVED,
|
||||
})
|
||||
trs[i] = &types.TxRecord{
|
||||
Tx: bytes.TrimPrefix(tx, []byte(PreparePrefix)),
|
||||
Action: types.TxRecord_ADDED,
|
||||
}
|
||||
continue
|
||||
}
|
||||
trs[i] = &types.TxRecord{
|
||||
Tx: tx,
|
||||
Action: types.TxRecord_UNMODIFIED,
|
||||
}
|
||||
}
|
||||
|
||||
return append(trs, removed...)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
context "golang.org/x/net/context"
|
||||
)
|
||||
|
||||
//go:generate mockery --case underscore --name Application
|
||||
// Application is an interface that enables any finite, deterministic state machine
|
||||
// to be driven by a blockchain-based replication engine via the ABCI.
|
||||
// All methods take a RequestXxx argument and return a ResponseXxx argument,
|
||||
@@ -97,9 +98,7 @@ func (BaseApplication) ApplySnapshotChunk(req RequestApplySnapshotChunk) Respons
|
||||
}
|
||||
|
||||
func (BaseApplication) PrepareProposal(req RequestPrepareProposal) ResponsePrepareProposal {
|
||||
return ResponsePrepareProposal{
|
||||
BlockData: req.BlockData,
|
||||
}
|
||||
return ResponsePrepareProposal{}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
|
||||
224
abci/types/mocks/application.go
Normal file
224
abci/types/mocks/application.go
Normal file
@@ -0,0 +1,224 @@
|
||||
// Code generated by mockery v2.14.0. 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// SetOption provides a mock function with given fields: _a0
|
||||
func (_m *Application) SetOption(_a0 types.RequestSetOption) types.ResponseSetOption {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 types.ResponseSetOption
|
||||
if rf, ok := ret.Get(0).(func(types.RequestSetOption) types.ResponseSetOption); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(types.ResponseSetOption)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewApplication interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewApplication creates a new instance of Application. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewApplication(t mockConstructorTestingTNewApplication) *Application {
|
||||
mock := &Application{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
176
abci/types/mocks/base.go
Normal file
176
abci/types/mocks/base.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
types "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// BaseMock provides a wrapper around the generated Application mock and a BaseApplication.
|
||||
// BaseMock first tries to use the mock's implementation of the method.
|
||||
// If no functionality was provided for the mock by the user, BaseMock dispatches
|
||||
// to the BaseApplication and uses its functionality.
|
||||
// BaseMock allows users to provide mocked functionality for only the methods that matter
|
||||
// for their test while avoiding a panic if the code calls Application methods that are
|
||||
// not relevant to the test.
|
||||
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.Application.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.Application.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.Application.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.Application.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.Application.PrepareProposal(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.Application.Commit()
|
||||
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.Application.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.Application.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.Application.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.Application.ApplySnapshotChunk(input)
|
||||
return ret
|
||||
}
|
||||
|
||||
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.Application.BeginBlock(input)
|
||||
return ret
|
||||
}
|
||||
|
||||
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.Application.DeliverTx(input)
|
||||
return ret
|
||||
}
|
||||
|
||||
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.Application.EndBlock(input)
|
||||
return ret
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -360,14 +360,20 @@ FOR_LOOP:
|
||||
didProcessCh <- struct{}{}
|
||||
}
|
||||
|
||||
firstParts := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
firstParts, err := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
bcR.Logger.Error("failed to make ",
|
||||
"height", first.Height,
|
||||
"err", err.Error())
|
||||
break FOR_LOOP
|
||||
}
|
||||
firstPartSetHeader := firstParts.Header()
|
||||
firstID := types.BlockID{Hash: first.Hash(), PartSetHeader: firstPartSetHeader}
|
||||
// Finally, verify the first block using the second's commit
|
||||
// NOTE: we can probably make this more efficient, but note that calling
|
||||
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
|
||||
// currently necessary.
|
||||
err := state.Validators.VerifyCommitLight(
|
||||
err = state.Validators.VerifyCommitLight(
|
||||
chainID, firstID, first.Height, second.LastCommit)
|
||||
|
||||
if err == nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
@@ -15,10 +16,11 @@ import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/mempool/mock"
|
||||
mpmocks "github.com/tendermint/tendermint/mempool/mocks"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
@@ -52,6 +54,7 @@ type BlockchainReactorPair struct {
|
||||
}
|
||||
|
||||
func newBlockchainReactor(
|
||||
t *testing.T,
|
||||
logger log.Logger,
|
||||
genDoc *types.GenesisDoc,
|
||||
privVals []types.PrivValidator,
|
||||
@@ -78,6 +81,18 @@ func newBlockchainReactor(
|
||||
panic(fmt.Errorf("error constructing state from genesis file: %w", err))
|
||||
}
|
||||
|
||||
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).Return(nil)
|
||||
|
||||
// Make the BlockchainReactor itself.
|
||||
// NOTE we have to create and commit the blocks first because
|
||||
// pool.height is determined from the store.
|
||||
@@ -85,7 +100,7 @@ func newBlockchainReactor(
|
||||
db := dbm.NewMemDB()
|
||||
stateStore = sm.NewStore(db)
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
|
||||
mock.Mempool{}, sm.EmptyEvidencePool{})
|
||||
mp, sm.EmptyEvidencePool{})
|
||||
if err = stateStore.Save(state); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -112,9 +127,10 @@ func newBlockchainReactor(
|
||||
lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
|
||||
}
|
||||
|
||||
thisBlock := makeBlock(blockHeight, state, lastCommit)
|
||||
thisBlock := sf.MakeBlock(state, blockHeight, lastCommit)
|
||||
|
||||
thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
|
||||
|
||||
state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock)
|
||||
@@ -140,8 +156,8 @@ func TestNoBlockResponse(t *testing.T) {
|
||||
|
||||
reactorPairs := make([]BlockchainReactorPair, 2)
|
||||
|
||||
reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
|
||||
reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[0] = newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, maxBlockHeight)
|
||||
reactorPairs[1] = newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, 0)
|
||||
|
||||
p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch {
|
||||
s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor)
|
||||
@@ -202,7 +218,7 @@ func TestBadBlockStopsPeer(t *testing.T) {
|
||||
|
||||
// Other chain needs a different validator set
|
||||
otherGenDoc, otherPrivVals := randGenesisDoc(1, false, 30)
|
||||
otherChain := newBlockchainReactor(log.TestingLogger(), otherGenDoc, otherPrivVals, maxBlockHeight)
|
||||
otherChain := newBlockchainReactor(t, log.TestingLogger(), otherGenDoc, otherPrivVals, maxBlockHeight)
|
||||
|
||||
defer func() {
|
||||
err := otherChain.reactor.Stop()
|
||||
@@ -213,10 +229,10 @@ func TestBadBlockStopsPeer(t *testing.T) {
|
||||
|
||||
reactorPairs := make([]BlockchainReactorPair, 4)
|
||||
|
||||
reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
|
||||
reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[0] = newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, maxBlockHeight)
|
||||
reactorPairs[1] = newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[2] = newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[3] = newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, 0)
|
||||
|
||||
switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch {
|
||||
s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor)
|
||||
@@ -254,7 +270,7 @@ func TestBadBlockStopsPeer(t *testing.T) {
|
||||
// race, but can't be easily avoided.
|
||||
reactorPairs[3].reactor.store = otherChain.reactor.store
|
||||
|
||||
lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
lastReactorPair := newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs = append(reactorPairs, lastReactorPair)
|
||||
|
||||
switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch {
|
||||
@@ -278,21 +294,6 @@ func TestBadBlockStopsPeer(t *testing.T) {
|
||||
assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1)
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// utility funcs
|
||||
|
||||
func makeTxs(height int64) (txs []types.Tx) {
|
||||
for i := 0; i < 10; i++ {
|
||||
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
return block
|
||||
}
|
||||
|
||||
type testApp struct {
|
||||
abci.BaseApplication
|
||||
}
|
||||
|
||||
@@ -465,7 +465,13 @@ func (bcR *BlockchainReactor) processBlock() error {
|
||||
|
||||
chainID := bcR.initialState.ChainID
|
||||
|
||||
firstParts := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
firstParts, err := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
bcR.Logger.Error("failed to make ",
|
||||
"height", first.Height,
|
||||
"err", err.Error())
|
||||
return err
|
||||
}
|
||||
firstPartSetHeader := firstParts.Header()
|
||||
firstID := types.BlockID{Hash: first.Hash(), PartSetHeader: firstPartSetHeader}
|
||||
// Finally, verify the first block using the second's commit
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
@@ -16,11 +17,12 @@ import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/mempool/mock"
|
||||
mpmocks "github.com/tendermint/tendermint/mempool/mocks"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
@@ -110,6 +112,18 @@ func newBlockchainReactor(
|
||||
panic(fmt.Errorf("error constructing state from genesis file: %w", err))
|
||||
}
|
||||
|
||||
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).Return(nil)
|
||||
|
||||
// Make the BlockchainReactor itself.
|
||||
// NOTE we have to create and commit the blocks first because
|
||||
// pool.height is determined from the store.
|
||||
@@ -117,7 +131,7 @@ func newBlockchainReactor(
|
||||
db := dbm.NewMemDB()
|
||||
stateStore = sm.NewStore(db)
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
|
||||
mock.Mempool{}, sm.EmptyEvidencePool{})
|
||||
mp, sm.EmptyEvidencePool{})
|
||||
if err = stateStore.Save(state); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -133,9 +147,10 @@ func newBlockchainReactor(
|
||||
lastCommit = types.NewCommit(vote.Height, vote.Round, lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
|
||||
}
|
||||
|
||||
thisBlock := makeBlock(blockHeight, state, lastCommit)
|
||||
thisBlock := sf.MakeBlock(state, blockHeight, lastCommit)
|
||||
|
||||
thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
|
||||
|
||||
state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock)
|
||||
@@ -345,21 +360,6 @@ outerFor:
|
||||
assert.True(t, lastReactorPair.bcR.Switch.Peers().Size() < len(reactorPairs)-1)
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// utility funcs
|
||||
|
||||
func makeTxs(height int64) (txs []types.Tx) {
|
||||
for i := 0; i < 10; i++ {
|
||||
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
return block
|
||||
}
|
||||
|
||||
type testApp struct {
|
||||
abci.BaseApplication
|
||||
}
|
||||
|
||||
@@ -158,11 +158,12 @@ func (state *pcState) handle(event Event) (Event, error) {
|
||||
return noOp, nil
|
||||
}
|
||||
|
||||
var (
|
||||
first, second = firstItem.block, secondItem.block
|
||||
firstParts = first.MakePartSet(types.BlockPartSizeBytes)
|
||||
firstID = types.BlockID{Hash: first.Hash(), PartSetHeader: firstParts.Header()}
|
||||
)
|
||||
first, second := firstItem.block, secondItem.block
|
||||
firstParts, err := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to make part set, height %d: %v ", first.Height, err.Error()))
|
||||
}
|
||||
firstID := types.BlockID{Hash: first.Hash(), PartSetHeader: firstParts.Header()}
|
||||
|
||||
// verify if +second+ last commit "confirms" +first+ block
|
||||
err = state.context.verifyCommit(tmState.ChainID, firstID, first.Height, second.LastCommit)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
@@ -19,12 +20,13 @@ import (
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
"github.com/tendermint/tendermint/mempool/mock"
|
||||
mpmocks "github.com/tendermint/tendermint/mempool/mocks"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/p2p/conn"
|
||||
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
@@ -142,8 +144,8 @@ type testReactorParams struct {
|
||||
mockA bool
|
||||
}
|
||||
|
||||
func newTestReactor(p testReactorParams) *BlockchainReactor {
|
||||
store, state, _ := newReactorStore(p.genDoc, p.privVals, p.startHeight)
|
||||
func newTestReactor(t *testing.T, p testReactorParams) *BlockchainReactor {
|
||||
store, state, _ := newReactorStore(t, p.genDoc, p.privVals, p.startHeight)
|
||||
reporter := behaviour.NewMockReporter()
|
||||
|
||||
var appl blockApplier
|
||||
@@ -151,6 +153,18 @@ func newTestReactor(p testReactorParams) *BlockchainReactor {
|
||||
if p.mockA {
|
||||
appl = &mockBlockApplier{}
|
||||
} else {
|
||||
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).Return(nil)
|
||||
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc)
|
||||
@@ -160,7 +174,7 @@ func newTestReactor(p testReactorParams) *BlockchainReactor {
|
||||
}
|
||||
db := dbm.NewMemDB()
|
||||
stateStore := sm.NewStore(db)
|
||||
appl = sm.NewBlockExecutor(stateStore, p.logger, proxyApp.Consensus(), mock.Mempool{}, sm.EmptyEvidencePool{})
|
||||
appl = sm.NewBlockExecutor(stateStore, p.logger, proxyApp.Consensus(), mp, sm.EmptyEvidencePool{})
|
||||
if err = stateStore.Save(state); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -391,7 +405,7 @@ func TestReactorHelperMode(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reactor := newTestReactor(params)
|
||||
reactor := newTestReactor(t, params)
|
||||
mockSwitch := &mockSwitchIo{switchedToConsensus: false}
|
||||
reactor.io = mockSwitch
|
||||
err := reactor.Start()
|
||||
@@ -434,7 +448,7 @@ func TestReactorSetSwitchNil(t *testing.T) {
|
||||
defer os.RemoveAll(config.RootDir)
|
||||
genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
|
||||
|
||||
reactor := newTestReactor(testReactorParams{
|
||||
reactor := newTestReactor(t, testReactorParams{
|
||||
logger: log.TestingLogger(),
|
||||
genDoc: genDoc,
|
||||
privVals: privVals,
|
||||
@@ -445,21 +459,6 @@ func TestReactorSetSwitchNil(t *testing.T) {
|
||||
assert.Nil(t, reactor.io)
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// utility funcs
|
||||
|
||||
func makeTxs(height int64) (txs []types.Tx) {
|
||||
for i := 0; i < 10; i++ {
|
||||
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
return block
|
||||
}
|
||||
|
||||
type testApp struct {
|
||||
abci.BaseApplication
|
||||
}
|
||||
@@ -488,6 +487,7 @@ func randGenesisDoc(chainID string, numValidators int, randPower bool, minPower
|
||||
// Why are we importing the entire blockExecutor dependency graph here
|
||||
// when we have the facilities to
|
||||
func newReactorStore(
|
||||
t *testing.T,
|
||||
genDoc *types.GenesisDoc,
|
||||
privVals []types.PrivValidator,
|
||||
maxBlockHeight int64) (*store.BlockStore, sm.State, *sm.BlockExecutor) {
|
||||
@@ -510,10 +510,22 @@ func newReactorStore(
|
||||
panic(fmt.Errorf("error constructing state from genesis file: %w", err))
|
||||
}
|
||||
|
||||
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).Return(nil)
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
stateStore = sm.NewStore(db)
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
|
||||
mock.Mempool{}, sm.EmptyEvidencePool{})
|
||||
mp, sm.EmptyEvidencePool{})
|
||||
if err = stateStore.Save(state); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -539,9 +551,11 @@ func newReactorStore(
|
||||
lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
|
||||
}
|
||||
|
||||
thisBlock := makeBlock(blockHeight, state, lastCommit)
|
||||
thisBlock := sf.MakeBlock(state, blockHeight, lastCommit)
|
||||
|
||||
thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
|
||||
|
||||
state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock)
|
||||
|
||||
@@ -187,7 +187,6 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
|
||||
}
|
||||
|
||||
var commit *types.Commit
|
||||
var votes []*types.Vote
|
||||
switch {
|
||||
case lazyProposer.Height == lazyProposer.state.InitialHeight:
|
||||
// We're creating a proposal for the first block.
|
||||
@@ -196,7 +195,6 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
|
||||
case lazyProposer.LastCommit.HasTwoThirdsMajority():
|
||||
// Make the commit from LastCommit
|
||||
commit = lazyProposer.LastCommit.MakeCommit()
|
||||
votes = lazyProposer.LastCommit.GetVotes()
|
||||
default: // This shouldn't happen.
|
||||
lazyProposer.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block")
|
||||
return
|
||||
@@ -213,9 +211,11 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
|
||||
}
|
||||
proposerAddr := lazyProposer.privValidatorPubKey.Address()
|
||||
|
||||
block, blockParts := lazyProposer.blockExec.CreateProposalBlock(
|
||||
lazyProposer.Height, lazyProposer.state, commit, proposerAddr, votes,
|
||||
)
|
||||
block, err := lazyProposer.blockExec.CreateProposalBlock(
|
||||
lazyProposer.Height, lazyProposer.state, commit, proposerAddr, nil)
|
||||
require.NoError(t, err)
|
||||
blockParts, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Flush the WAL. Otherwise, we may not recompute the same proposal to sign,
|
||||
// and the privValidator will refuse to sign anything.
|
||||
@@ -463,7 +463,10 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int32, cs *St
|
||||
// Avoid sending on internalMsgQueue and running consensus state.
|
||||
|
||||
// Create a new proposal block from state/txs from the mempool.
|
||||
block1, blockParts1 := cs.createProposalBlock()
|
||||
block1, err := cs.createProposalBlock()
|
||||
require.NoError(t, err)
|
||||
blockParts1, err := block1.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
polRound, propBlockID := cs.ValidRound, types.BlockID{Hash: block1.Hash(), PartSetHeader: blockParts1.Header()}
|
||||
proposal1 := types.NewProposal(height, round, polRound, propBlockID)
|
||||
p1 := proposal1.ToProto()
|
||||
@@ -477,7 +480,10 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int32, cs *St
|
||||
deliverTxsRange(cs, 0, 1)
|
||||
|
||||
// Create a new proposal block from state/txs from the mempool.
|
||||
block2, blockParts2 := cs.createProposalBlock()
|
||||
block2, err := cs.createProposalBlock()
|
||||
require.NoError(t, err)
|
||||
blockParts2, err := block2.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
polRound, propBlockID = cs.ValidRound, types.BlockID{Hash: block2.Hash(), PartSetHeader: blockParts2.Header()}
|
||||
proposal2 := types.NewProposal(height, round, polRound, propBlockID)
|
||||
p2 := proposal2.ToProto()
|
||||
|
||||
@@ -201,13 +201,17 @@ func startTestRound(cs *State, height int64, round int32) {
|
||||
|
||||
// Create proposal block from cs1 but sign it with vs.
|
||||
func decideProposal(
|
||||
t *testing.T,
|
||||
cs1 *State,
|
||||
vs *validatorStub,
|
||||
height int64,
|
||||
round int32,
|
||||
) (proposal *types.Proposal, block *types.Block) {
|
||||
cs1.mtx.Lock()
|
||||
block, blockParts := cs1.createProposalBlock()
|
||||
block, err := cs1.createProposalBlock()
|
||||
require.NoError(t, err)
|
||||
blockParts, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
validRound := cs1.ValidRound
|
||||
chainID := cs1.state.ChainID
|
||||
cs1.mtx.Unlock()
|
||||
|
||||
@@ -259,5 +259,5 @@ func (app *CounterApplication) Commit() abci.ResponseCommit {
|
||||
|
||||
func (app *CounterApplication) PrepareProposal(
|
||||
req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
|
||||
return abci.ResponsePrepareProposal{BlockData: req.BlockData}
|
||||
return abci.ResponsePrepareProposal{}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -362,9 +363,11 @@ func TestSimulateValidatorsChange(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower)
|
||||
err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx1, nil, mempl.TxInfo{})
|
||||
assert.Nil(t, err)
|
||||
propBlock, _ := css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
assert.NoError(t, err)
|
||||
propBlock, err := css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
require.NoError(t, err)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
|
||||
proposal := types.NewProposal(vss[1].Height, round, -1, blockID)
|
||||
@@ -392,9 +395,11 @@ func TestSimulateValidatorsChange(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25)
|
||||
err = assertMempool(css[0].txNotifier).CheckTx(updateValidatorTx1, nil, mempl.TxInfo{})
|
||||
assert.Nil(t, err)
|
||||
propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
propBlockParts = propBlock.MakePartSet(partSize)
|
||||
assert.NoError(t, err)
|
||||
propBlock, err = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
require.NoError(t, err)
|
||||
propBlockParts, err = propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
|
||||
proposal = types.NewProposal(vss[2].Height, round, -1, blockID)
|
||||
@@ -429,9 +434,11 @@ func TestSimulateValidatorsChange(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower)
|
||||
err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx3, nil, mempl.TxInfo{})
|
||||
assert.Nil(t, err)
|
||||
propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
propBlockParts = propBlock.MakePartSet(partSize)
|
||||
assert.NoError(t, err)
|
||||
propBlock, err = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
require.NoError(t, err)
|
||||
propBlockParts, err = propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
newVss := make([]*validatorStub, nVals+1)
|
||||
copy(newVss, vss[:nVals+1])
|
||||
@@ -504,9 +511,11 @@ func TestSimulateValidatorsChange(t *testing.T) {
|
||||
incrementHeight(vss...)
|
||||
removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0)
|
||||
err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx3, nil, mempl.TxInfo{})
|
||||
assert.Nil(t, err)
|
||||
propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
propBlockParts = propBlock.MakePartSet(partSize)
|
||||
assert.NoError(t, err)
|
||||
propBlock, err = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
require.NoError(t, err)
|
||||
propBlockParts, err = propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
newVss = make([]*validatorStub, nVals+3)
|
||||
copy(newVss, vss[:nVals+3])
|
||||
@@ -665,7 +674,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
|
||||
config = sim.Config
|
||||
chain = append([]*types.Block{}, sim.Chain...) // copy chain
|
||||
commits = sim.Commits
|
||||
store = newMockBlockStore(config, genesisState.ConsensusParams)
|
||||
store = newMockBlockStore(t, config, genesisState.ConsensusParams)
|
||||
} else { // test single node
|
||||
testConfig := ResetConfig(fmt.Sprintf("%s_%v_s", t.Name(), mode))
|
||||
defer os.RemoveAll(testConfig.RootDir)
|
||||
@@ -690,7 +699,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
|
||||
require.NoError(t, err)
|
||||
pubKey, err := privVal.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
stateDB, genesisState, store = stateAndStore(config, pubKey, kvstore.ProtocolVersion)
|
||||
stateDB, genesisState, store = stateAndStore(t, config, pubKey, kvstore.ProtocolVersion)
|
||||
|
||||
}
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
@@ -699,7 +708,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
|
||||
|
||||
state := genesisState.Copy()
|
||||
// run the chain through state.ApplyBlock to build up the tendermint state
|
||||
state = buildTMStateFromChain(config, stateStore, state, chain, nBlocks, mode)
|
||||
state = buildTMStateFromChain(t, config, stateStore, state, chain, nBlocks, mode)
|
||||
latestAppHash := state.AppHash
|
||||
|
||||
// make a new client creator
|
||||
@@ -715,7 +724,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
|
||||
stateStore := sm.NewStore(stateDB1)
|
||||
err := stateStore.Save(genesisState)
|
||||
require.NoError(t, err)
|
||||
buildAppStateFromChain(proxyApp, stateStore, genesisState, chain, nBlocks, mode)
|
||||
buildAppStateFromChain(t, proxyApp, stateStore, genesisState, chain, nBlocks, mode)
|
||||
}
|
||||
|
||||
// Prune block store if requested
|
||||
@@ -775,19 +784,19 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
|
||||
}
|
||||
}
|
||||
|
||||
func applyBlock(stateStore sm.Store, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State {
|
||||
func applyBlock(t *testing.T, stateStore sm.Store, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State {
|
||||
testPartSize := types.BlockPartSizeBytes
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
|
||||
|
||||
blkID := types.BlockID{Hash: blk.Hash(), PartSetHeader: blk.MakePartSet(testPartSize).Header()}
|
||||
bps, err := blk.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blkID := types.BlockID{Hash: blk.Hash(), PartSetHeader: bps.Header()}
|
||||
newState, _, err := blockExec.ApplyBlock(st, blkID, blk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
return newState
|
||||
}
|
||||
|
||||
func buildAppStateFromChain(proxyApp proxy.AppConns, stateStore sm.Store,
|
||||
func buildAppStateFromChain(t *testing.T, proxyApp proxy.AppConns, stateStore sm.Store,
|
||||
state sm.State, chain []*types.Block, nBlocks int, mode uint) {
|
||||
// start a new app without handshake, play nBlocks blocks
|
||||
if err := proxyApp.Start(); err != nil {
|
||||
@@ -809,18 +818,18 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateStore sm.Store,
|
||||
case 0:
|
||||
for i := 0; i < nBlocks; i++ {
|
||||
block := chain[i]
|
||||
state = applyBlock(stateStore, state, block, proxyApp)
|
||||
state = applyBlock(t, stateStore, state, block, proxyApp)
|
||||
}
|
||||
case 1, 2, 3:
|
||||
for i := 0; i < nBlocks-1; i++ {
|
||||
block := chain[i]
|
||||
state = applyBlock(stateStore, state, block, proxyApp)
|
||||
state = applyBlock(t, stateStore, state, block, proxyApp)
|
||||
}
|
||||
|
||||
if mode == 2 || mode == 3 {
|
||||
// update the kvstore height and apphash
|
||||
// as if we ran commit but not
|
||||
state = applyBlock(stateStore, state, chain[nBlocks-1], proxyApp)
|
||||
state = applyBlock(t, stateStore, state, chain[nBlocks-1], proxyApp)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown mode %v", mode))
|
||||
@@ -829,6 +838,7 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateStore sm.Store,
|
||||
}
|
||||
|
||||
func buildTMStateFromChain(
|
||||
t *testing.T,
|
||||
config *cfg.Config,
|
||||
stateStore sm.Store,
|
||||
state sm.State,
|
||||
@@ -859,19 +869,19 @@ func buildTMStateFromChain(
|
||||
case 0:
|
||||
// sync right up
|
||||
for _, block := range chain {
|
||||
state = applyBlock(stateStore, state, block, proxyApp)
|
||||
state = applyBlock(t, stateStore, state, block, proxyApp)
|
||||
}
|
||||
|
||||
case 1, 2, 3:
|
||||
// sync up to the penultimate as if we stored the block.
|
||||
// whether we commit or not depends on the appHash
|
||||
for _, block := range chain[:len(chain)-1] {
|
||||
state = applyBlock(stateStore, state, block, proxyApp)
|
||||
state = applyBlock(t, stateStore, state, block, proxyApp)
|
||||
}
|
||||
|
||||
// apply the final block to a state copy so we can
|
||||
// get the right next appHash but keep the state back
|
||||
applyBlock(stateStore, state, chain[len(chain)-1], proxyApp)
|
||||
applyBlock(t, stateStore, state, chain[len(chain)-1], proxyApp)
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown mode %v", mode))
|
||||
}
|
||||
@@ -890,12 +900,14 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) {
|
||||
const appVersion = 0x0
|
||||
pubKey, err := privVal.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
stateDB, state, store := stateAndStore(config, pubKey, appVersion)
|
||||
stateDB, state, store := stateAndStore(t, config, pubKey, appVersion)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
|
||||
state.LastValidators = state.Validators.Copy()
|
||||
// mode = 0 for committing all the blocks
|
||||
blocks := makeBlocks(3, &state, privVal)
|
||||
blocks, err := sf.MakeBlocks(3, &state, privVal)
|
||||
require.NoError(t, err)
|
||||
|
||||
store.chain = blocks
|
||||
|
||||
// 2. Tendermint must panic if app returns wrong hash for the first block
|
||||
@@ -947,52 +959,6 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeBlocks(n int, state *sm.State, privVal types.PrivValidator) []*types.Block {
|
||||
blocks := make([]*types.Block, 0)
|
||||
|
||||
var (
|
||||
prevBlock *types.Block
|
||||
prevBlockMeta *types.BlockMeta
|
||||
)
|
||||
|
||||
appHeight := byte(0x01)
|
||||
for i := 0; i < n; i++ {
|
||||
height := int64(i + 1)
|
||||
|
||||
block, parts := makeBlock(*state, prevBlock, prevBlockMeta, privVal, height)
|
||||
blocks = append(blocks, block)
|
||||
|
||||
prevBlock = block
|
||||
prevBlockMeta = types.NewBlockMeta(block, parts)
|
||||
|
||||
// update state
|
||||
state.AppHash = []byte{appHeight}
|
||||
appHeight++
|
||||
state.LastBlockHeight = height
|
||||
}
|
||||
|
||||
return blocks
|
||||
}
|
||||
|
||||
func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.BlockMeta,
|
||||
privVal types.PrivValidator, height int64) (*types.Block, *types.PartSet) {
|
||||
|
||||
lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
|
||||
if height > 1 {
|
||||
vote, _ := types.MakeVote(
|
||||
lastBlock.Header.Height,
|
||||
lastBlockMeta.BlockID,
|
||||
state.Validators,
|
||||
privVal,
|
||||
lastBlock.Header.ChainID,
|
||||
time.Now())
|
||||
lastCommit = types.NewCommit(vote.Height, vote.Round,
|
||||
lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
|
||||
}
|
||||
|
||||
return state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
}
|
||||
|
||||
type badApp struct {
|
||||
abci.BaseApplication
|
||||
numBlocks byte
|
||||
@@ -1144,17 +1110,19 @@ func readPieceFromWAL(msg *TimedWALMessage) interface{} {
|
||||
|
||||
// fresh state and mock store
|
||||
func stateAndStore(
|
||||
t *testing.T,
|
||||
config *cfg.Config,
|
||||
pubKey crypto.PubKey,
|
||||
appVersion uint64) (dbm.DB, sm.State, *mockBlockStore) {
|
||||
appVersion uint64,
|
||||
) (dbm.DB, sm.State, *mockBlockStore) {
|
||||
stateDB := dbm.NewMemDB()
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile())
|
||||
state, err := sm.MakeGenesisStateFromFile(config.GenesisFile())
|
||||
require.NoError(t, err)
|
||||
state.Version.Consensus.App = appVersion
|
||||
store := newMockBlockStore(config, state.ConsensusParams)
|
||||
if err := stateStore.Save(state); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store := newMockBlockStore(t, config, state.ConsensusParams)
|
||||
require.NoError(t, stateStore.Save(state))
|
||||
|
||||
return stateDB, state, store
|
||||
}
|
||||
|
||||
@@ -1167,11 +1135,16 @@ type mockBlockStore struct {
|
||||
chain []*types.Block
|
||||
commits []*types.Commit
|
||||
base int64
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
// TODO: NewBlockStore(db.NewMemDB) ...
|
||||
func newMockBlockStore(config *cfg.Config, params tmproto.ConsensusParams) *mockBlockStore {
|
||||
return &mockBlockStore{config, params, nil, nil, 0}
|
||||
func newMockBlockStore(t *testing.T, config *cfg.Config, params tmproto.ConsensusParams) *mockBlockStore {
|
||||
return &mockBlockStore{
|
||||
config: config,
|
||||
params: params,
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (bs *mockBlockStore) Height() int64 { return int64(len(bs.chain)) }
|
||||
@@ -1184,8 +1157,10 @@ func (bs *mockBlockStore) LoadBlockByHash(hash []byte) *types.Block {
|
||||
}
|
||||
func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
||||
block := bs.chain[height-1]
|
||||
bps, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(bs.t, err)
|
||||
return &types.BlockMeta{
|
||||
BlockID: types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(types.BlockPartSizeBytes).Header()},
|
||||
BlockID: types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()},
|
||||
Header: block.Header,
|
||||
}
|
||||
}
|
||||
@@ -1224,7 +1199,7 @@ func TestHandshakeUpdatesValidators(t *testing.T) {
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||
pubKey, err := privVal.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
stateDB, state, store := stateAndStore(config, pubKey, 0x0)
|
||||
stateDB, state, store := stateAndStore(t, config, pubKey, 0x0)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
|
||||
oldValAddr := state.Validators.Validators[0].Address
|
||||
|
||||
@@ -1131,8 +1131,17 @@ func (cs *State) defaultDecideProposal(height int64, round int32) {
|
||||
block, blockParts = cs.ValidBlock, cs.ValidBlockParts
|
||||
} else {
|
||||
// Create a new proposal block from state/txs from the mempool.
|
||||
block, blockParts = cs.createProposalBlock()
|
||||
if block == nil {
|
||||
var err error
|
||||
block, err = cs.createProposalBlock()
|
||||
if err != nil {
|
||||
cs.Logger.Error("unable to create proposal block", "error", err)
|
||||
return
|
||||
} else if block == nil {
|
||||
panic("Method createProposalBlock should not provide a nil block without errors")
|
||||
}
|
||||
blockParts, err = block.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
cs.Logger.Error("unable to create proposal block part set", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1187,13 +1196,12 @@ func (cs *State) isProposalComplete() bool {
|
||||
//
|
||||
// NOTE: keep it side-effect free for clarity.
|
||||
// CONTRACT: cs.privValidator is not nil.
|
||||
func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.PartSet) {
|
||||
func (cs *State) createProposalBlock() (*types.Block, error) {
|
||||
if cs.privValidator == nil {
|
||||
panic("entered createProposalBlock with privValidator being nil")
|
||||
return nil, errors.New("entered createProposalBlock with privValidator being nil")
|
||||
}
|
||||
|
||||
var commit *types.Commit
|
||||
var votes []*types.Vote
|
||||
switch {
|
||||
case cs.Height == cs.state.InitialHeight:
|
||||
// We're creating a proposal for the first block.
|
||||
@@ -1203,23 +1211,20 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa
|
||||
case cs.LastCommit.HasTwoThirdsMajority():
|
||||
// Make the commit from LastCommit
|
||||
commit = cs.LastCommit.MakeCommit()
|
||||
votes = cs.LastCommit.GetVotes()
|
||||
|
||||
default: // This shouldn't happen.
|
||||
cs.Logger.Error("propose step; cannot propose anything without commit for the previous block")
|
||||
return
|
||||
return nil, errors.New("propose step; cannot propose anything without commit for the previous block")
|
||||
}
|
||||
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// If this node is a validator & proposer in the current round, it will
|
||||
// miss the opportunity to create a block.
|
||||
cs.Logger.Error("propose step; empty priv validator public key", "err", errPubKeyIsNotSet)
|
||||
return
|
||||
return nil, fmt.Errorf("propose step; empty priv validator public key, error: %w", errPubKeyIsNotSet)
|
||||
}
|
||||
|
||||
proposerAddr := cs.privValidatorPubKey.Address()
|
||||
|
||||
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, votes)
|
||||
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, cs.LastCommit.GetVotes())
|
||||
}
|
||||
|
||||
// Enter: `timeoutPropose` after entering Propose.
|
||||
|
||||
@@ -191,7 +191,8 @@ func TestStateBadProposal(t *testing.T) {
|
||||
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
|
||||
|
||||
propBlock, _ := cs1.createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
propBlock, err := cs1.createProposalBlock() // changeProposer(t, cs1, vs2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// make the second validator the proposer by incrementing round
|
||||
round++
|
||||
@@ -204,7 +205,8 @@ func TestStateBadProposal(t *testing.T) {
|
||||
}
|
||||
stateHash[0] = (stateHash[0] + 1) % 255
|
||||
propBlock.AppHash = stateHash
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
proposal := types.NewProposal(vs2.Height, round, -1, blockID)
|
||||
p := proposal.ToProto()
|
||||
@@ -230,13 +232,19 @@ func TestStateBadProposal(t *testing.T) {
|
||||
validatePrevote(t, cs1, round, vss[0], nil)
|
||||
|
||||
// add bad prevote from vs2 and wait for it
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
|
||||
bps, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlock.Hash(), bps.Header(), vs2)
|
||||
ensurePrevote(voteCh, height, round)
|
||||
|
||||
// wait for precommit
|
||||
ensurePrecommit(voteCh, height, round)
|
||||
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
|
||||
signAddVotes(cs1, tmproto.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
|
||||
|
||||
bps2, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
signAddVotes(cs1, tmproto.PrecommitType, propBlock.Hash(), bps2.Header(), vs2)
|
||||
}
|
||||
|
||||
func TestStateOversizedBlock(t *testing.T) {
|
||||
@@ -250,7 +258,8 @@ func TestStateOversizedBlock(t *testing.T) {
|
||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||
voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
|
||||
|
||||
propBlock, _ := cs1.createProposalBlock()
|
||||
propBlock, err := cs1.createProposalBlock()
|
||||
require.NoError(t, err)
|
||||
propBlock.Data.Txs = []types.Tx{tmrand.Bytes(2001)}
|
||||
propBlock.Header.DataHash = propBlock.Data.Hash()
|
||||
|
||||
@@ -258,7 +267,8 @@ func TestStateOversizedBlock(t *testing.T) {
|
||||
round++
|
||||
incrementRound(vss[1:]...)
|
||||
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
proposal := types.NewProposal(height, round, -1, blockID)
|
||||
p := proposal.ToProto()
|
||||
@@ -290,11 +300,18 @@ func TestStateOversizedBlock(t *testing.T) {
|
||||
// precommit on it
|
||||
ensurePrevote(voteCh, height, round)
|
||||
validatePrevote(t, cs1, round, vss[0], nil)
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
|
||||
|
||||
bps, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlock.Hash(), bps.Header(), vs2)
|
||||
ensurePrevote(voteCh, height, round)
|
||||
ensurePrecommit(voteCh, height, round)
|
||||
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
|
||||
signAddVotes(cs1, tmproto.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
|
||||
|
||||
bps2, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
signAddVotes(cs1, tmproto.PrecommitType, propBlock.Hash(), bps2.Header(), vs2)
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -466,9 +483,7 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
|
||||
rs := cs1.GetRoundState()
|
||||
|
||||
if rs.ProposalBlock != nil {
|
||||
panic("Expected proposal block to be nil")
|
||||
}
|
||||
require.Nil(t, rs.ProposalBlock, "Expected proposal block to be nil")
|
||||
|
||||
// wait to finish prevote
|
||||
ensurePrevote(voteCh, height, round)
|
||||
@@ -476,7 +491,10 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
|
||||
|
||||
// add a conflicting prevote from the other validator
|
||||
signAddVotes(cs1, tmproto.PrevoteType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
|
||||
bps, err := rs.LockedBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
signAddVotes(cs1, tmproto.PrevoteType, hash, bps.Header(), vs2)
|
||||
ensurePrevote(voteCh, height, round)
|
||||
|
||||
// now we're going to enter prevote again, but with invalid args
|
||||
@@ -489,7 +507,9 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash)
|
||||
|
||||
// add conflicting precommit from vs2
|
||||
signAddVotes(cs1, tmproto.PrecommitType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
|
||||
bps2, err := rs.LockedBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
signAddVotes(cs1, tmproto.PrecommitType, hash, bps2.Header(), vs2)
|
||||
ensurePrecommit(voteCh, height, round)
|
||||
|
||||
// (note we're entering precommit for a second time this round, but with invalid args
|
||||
@@ -519,7 +539,9 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
ensurePrevote(voteCh, height, round) // prevote
|
||||
validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
|
||||
|
||||
signAddVotes(cs1, tmproto.PrevoteType, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
|
||||
bps0, err := rs.ProposalBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
signAddVotes(cs1, tmproto.PrevoteType, hash, bps0.Header(), vs2)
|
||||
ensurePrevote(voteCh, height, round)
|
||||
|
||||
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds())
|
||||
@@ -527,11 +549,13 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
|
||||
validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
|
||||
|
||||
bps1, err := rs.ProposalBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
signAddVotes(
|
||||
cs1,
|
||||
tmproto.PrecommitType,
|
||||
hash,
|
||||
rs.ProposalBlock.MakePartSet(partSize).Header(),
|
||||
bps1.Header(),
|
||||
vs2) // NOTE: conflicting precommits at same height
|
||||
ensurePrecommit(voteCh, height, round)
|
||||
|
||||
@@ -539,7 +563,7 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
|
||||
cs2, _ := randState(2) // needed so generated block is different than locked block
|
||||
// before we time out into new round, set next proposal block
|
||||
prop, propBlock := decideProposal(cs2, vs2, vs2.Height, vs2.Round+1)
|
||||
prop, propBlock := decideProposal(t, cs2, vs2, vs2.Height, vs2.Round+1)
|
||||
if prop == nil || propBlock == nil {
|
||||
t.Fatal("Failed to create proposal block with vs2")
|
||||
}
|
||||
@@ -555,7 +579,9 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
|
||||
// now we're on a new round and not the proposer
|
||||
// so set the proposal block
|
||||
if err := cs1.SetProposalAndBlock(prop, propBlock, propBlock.MakePartSet(partSize), ""); err != nil {
|
||||
bps3, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
if err := cs1.SetProposalAndBlock(prop, propBlock, bps3, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -565,18 +591,23 @@ func TestStateLockNoPOL(t *testing.T) {
|
||||
validatePrevote(t, cs1, 3, vss[0], cs1.LockedBlock.Hash())
|
||||
|
||||
// prevote for proposed block
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
|
||||
bps4, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlock.Hash(), bps4.Header(), vs2)
|
||||
ensurePrevote(voteCh, height, round)
|
||||
|
||||
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds())
|
||||
ensurePrecommit(voteCh, height, round)
|
||||
validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
|
||||
|
||||
bps5, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
signAddVotes(
|
||||
cs1,
|
||||
tmproto.PrecommitType,
|
||||
propBlock.Hash(),
|
||||
propBlock.MakePartSet(partSize).Header(),
|
||||
bps5.Header(),
|
||||
vs2) // NOTE: conflicting precommits at same height
|
||||
ensurePrecommit(voteCh, height, round)
|
||||
}
|
||||
@@ -631,11 +662,13 @@ func TestStateLockPOLRelock(t *testing.T) {
|
||||
|
||||
// before we timeout to the new round set the new proposal
|
||||
cs2 := newState(cs1.state, vs2, counter.NewApplication(true))
|
||||
prop, propBlock := decideProposal(cs2, vs2, vs2.Height, vs2.Round+1)
|
||||
prop, propBlock := decideProposal(t, cs2, vs2, vs2.Height, vs2.Round+1)
|
||||
if prop == nil || propBlock == nil {
|
||||
t.Fatal("Failed to create proposal block with vs2")
|
||||
}
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
propBlockHash := propBlock.Hash()
|
||||
require.NotEqual(t, propBlockHash, theBlockHash)
|
||||
|
||||
@@ -728,8 +761,9 @@ func TestStateLockPOLUnlock(t *testing.T) {
|
||||
signAddVotes(cs1, tmproto.PrecommitType, theBlockHash, theBlockParts, vs3)
|
||||
|
||||
// before we time out into new round, set next proposal block
|
||||
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
prop, propBlock := decideProposal(t, cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
// timeout to new round
|
||||
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
|
||||
@@ -816,11 +850,13 @@ func TestStateLockPOLUnlockOnUnknownBlock(t *testing.T) {
|
||||
|
||||
// before we timeout to the new round set the new proposal
|
||||
cs2 := newState(cs1.state, vs2, counter.NewApplication(true))
|
||||
prop, propBlock := decideProposal(cs2, vs2, vs2.Height, vs2.Round+1)
|
||||
prop, propBlock := decideProposal(t, cs2, vs2, vs2.Height, vs2.Round+1)
|
||||
if prop == nil || propBlock == nil {
|
||||
t.Fatal("Failed to create proposal block with vs2")
|
||||
}
|
||||
secondBlockParts := propBlock.MakePartSet(partSize)
|
||||
secondBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
secondBlockHash := propBlock.Hash()
|
||||
require.NotEqual(t, secondBlockHash, firstBlockHash)
|
||||
|
||||
@@ -860,11 +896,12 @@ func TestStateLockPOLUnlockOnUnknownBlock(t *testing.T) {
|
||||
|
||||
// before we timeout to the new round set the new proposal
|
||||
cs3 := newState(cs1.state, vs3, counter.NewApplication(true))
|
||||
prop, propBlock = decideProposal(cs3, vs3, vs3.Height, vs3.Round+1)
|
||||
prop, propBlock = decideProposal(t, cs3, vs3, vs3.Height, vs3.Round+1)
|
||||
if prop == nil || propBlock == nil {
|
||||
t.Fatal("Failed to create proposal block with vs2")
|
||||
}
|
||||
thirdPropBlockParts := propBlock.MakePartSet(partSize)
|
||||
thirdPropBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
thirdPropBlockHash := propBlock.Hash()
|
||||
require.NotEqual(t, secondBlockHash, thirdPropBlockHash)
|
||||
|
||||
@@ -928,7 +965,10 @@ func TestStateLockPOLSafety1(t *testing.T) {
|
||||
validatePrevote(t, cs1, round, vss[0], propBlock.Hash())
|
||||
|
||||
// the others sign a polka but we don't see it
|
||||
prevotes := signVotes(tmproto.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
|
||||
bps, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
prevotes := signVotes(tmproto.PrevoteType, propBlock.Hash(), bps.Header(), vs2, vs3, vs4)
|
||||
|
||||
t.Logf("old prop hash %v", fmt.Sprintf("%X", propBlock.Hash()))
|
||||
|
||||
@@ -941,9 +981,10 @@ func TestStateLockPOLSafety1(t *testing.T) {
|
||||
|
||||
t.Log("### ONTO ROUND 1")
|
||||
|
||||
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
prop, propBlock := decideProposal(t, cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
propBlockHash := propBlock.Hash()
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
@@ -1037,18 +1078,20 @@ func TestStateLockPOLSafety2(t *testing.T) {
|
||||
|
||||
// the block for R0: gets polkad but we miss it
|
||||
// (even though we signed it, shhh)
|
||||
_, propBlock0 := decideProposal(cs1, vss[0], height, round)
|
||||
_, propBlock0 := decideProposal(t, cs1, vss[0], height, round)
|
||||
propBlockHash0 := propBlock0.Hash()
|
||||
propBlockParts0 := propBlock0.MakePartSet(partSize)
|
||||
propBlockParts0, err := propBlock0.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
propBlockID0 := types.BlockID{Hash: propBlockHash0, PartSetHeader: propBlockParts0.Header()}
|
||||
|
||||
// the others sign a polka but we don't see it
|
||||
prevotes := signVotes(tmproto.PrevoteType, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4)
|
||||
|
||||
// the block for round 1
|
||||
prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
prop1, propBlock1 := decideProposal(t, cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
propBlockHash1 := propBlock1.Hash()
|
||||
propBlockParts1 := propBlock1.MakePartSet(partSize)
|
||||
propBlockParts1, err := propBlock1.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
@@ -1146,7 +1189,9 @@ func TestProposeValidBlock(t *testing.T) {
|
||||
validatePrevote(t, cs1, round, vss[0], propBlockHash)
|
||||
|
||||
// the others sign a polka
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlockHash, propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
|
||||
bps, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlockHash, bps.Header(), vs2, vs3, vs4)
|
||||
|
||||
ensurePrecommit(voteCh, height, round)
|
||||
// we should have precommitted
|
||||
@@ -1230,7 +1275,8 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) {
|
||||
rs := cs1.GetRoundState()
|
||||
propBlock := rs.ProposalBlock
|
||||
propBlockHash := propBlock.Hash()
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
ensurePrevote(voteCh, height, round)
|
||||
validatePrevote(t, cs1, round, vss[0], propBlockHash)
|
||||
@@ -1296,9 +1342,10 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) {
|
||||
ensurePrevote(voteCh, height, round)
|
||||
validatePrevote(t, cs1, round, vss[0], nil)
|
||||
|
||||
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
prop, propBlock := decideProposal(t, cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
propBlockHash := propBlock.Hash()
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
// vs2, vs3 and vs4 send prevote for propBlock
|
||||
signAddVotes(cs1, tmproto.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
|
||||
@@ -1456,9 +1503,10 @@ func TestEmitNewValidBlockEventOnCommitWithoutBlock(t *testing.T) {
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
||||
|
||||
_, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round)
|
||||
_, propBlock := decideProposal(t, cs1, vs2, vs2.Height, vs2.Round)
|
||||
propBlockHash := propBlock.Hash()
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
// start round in which PO is not proposer
|
||||
startTestRound(cs1, height, round)
|
||||
@@ -1489,9 +1537,10 @@ func TestCommitFromPreviousRound(t *testing.T) {
|
||||
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
||||
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
|
||||
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round)
|
||||
prop, propBlock := decideProposal(t, cs1, vs2, vs2.Height, vs2.Round)
|
||||
propBlockHash := propBlock.Hash()
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
// start round in which PO is not proposer
|
||||
startTestRound(cs1, height, round)
|
||||
@@ -1634,8 +1683,9 @@ func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) {
|
||||
|
||||
ensureNewBlockHeader(newBlockHeader, height, theBlockHash)
|
||||
|
||||
prop, propBlock := decideProposal(cs1, vs2, height+1, 0)
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
prop, propBlock := decideProposal(t, cs1, vs2, height+1, 0)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1756,7 +1806,8 @@ func TestStateHalt1(t *testing.T) {
|
||||
ensureNewProposal(proposalCh, height, round)
|
||||
rs := cs1.GetRoundState()
|
||||
propBlock := rs.ProposalBlock
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
propBlockParts, err := propBlock.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
ensurePrevote(voteCh, height, round)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
smmocks "github.com/tendermint/tendermint/state/mocks"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
@@ -147,7 +148,7 @@ func TestAddExpiredEvidence(t *testing.T) {
|
||||
func TestReportConflictingVotes(t *testing.T) {
|
||||
var height int64 = 10
|
||||
|
||||
pool, pv := defaultTestPool(height)
|
||||
pool, pv := defaultTestPool(t, height)
|
||||
val := types.NewValidator(pv.PrivKey.PubKey(), 10)
|
||||
ev := types.NewMockDuplicateVoteEvidenceWithValidator(height+1, defaultEvidenceTime, pv, evidenceChainID)
|
||||
|
||||
@@ -181,7 +182,7 @@ func TestReportConflictingVotes(t *testing.T) {
|
||||
|
||||
func TestEvidencePoolUpdate(t *testing.T) {
|
||||
height := int64(21)
|
||||
pool, val := defaultTestPool(height)
|
||||
pool, val := defaultTestPool(t, height)
|
||||
state := pool.State()
|
||||
|
||||
// create new block (no need to save it to blockStore)
|
||||
@@ -214,7 +215,7 @@ func TestEvidencePoolUpdate(t *testing.T) {
|
||||
|
||||
func TestVerifyPendingEvidencePasses(t *testing.T) {
|
||||
var height int64 = 1
|
||||
pool, val := defaultTestPool(height)
|
||||
pool, val := defaultTestPool(t, height)
|
||||
ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime.Add(1*time.Minute),
|
||||
val, evidenceChainID)
|
||||
err := pool.AddEvidence(ev)
|
||||
@@ -226,7 +227,7 @@ func TestVerifyPendingEvidencePasses(t *testing.T) {
|
||||
|
||||
func TestVerifyDuplicatedEvidenceFails(t *testing.T) {
|
||||
var height int64 = 1
|
||||
pool, val := defaultTestPool(height)
|
||||
pool, val := defaultTestPool(t, height)
|
||||
ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime.Add(1*time.Minute),
|
||||
val, evidenceChainID)
|
||||
err := pool.CheckEvidence(types.EvidenceList{ev, ev})
|
||||
@@ -306,7 +307,8 @@ func TestRecoverPendingEvidence(t *testing.T) {
|
||||
stateStore := initializeValidatorState(val, height)
|
||||
state, err := stateStore.Load()
|
||||
require.NoError(t, err)
|
||||
blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress)
|
||||
blockStore, err := initializeBlockStore(dbm.NewMemDB(), state, valAddress)
|
||||
require.NoError(t, err)
|
||||
// create previous pool and populate it
|
||||
pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore)
|
||||
require.NoError(t, err)
|
||||
@@ -398,23 +400,26 @@ func initializeValidatorState(privVal types.PrivValidator, height int64) sm.Stor
|
||||
|
||||
// initializeBlockStore creates a block storage and populates it w/ a dummy
|
||||
// block at +height+.
|
||||
func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore {
|
||||
func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) (*store.BlockStore, error) {
|
||||
blockStore := store.NewBlockStore(db)
|
||||
|
||||
for i := int64(1); i <= state.LastBlockHeight; i++ {
|
||||
lastCommit := makeCommit(i-1, valAddr)
|
||||
block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil,
|
||||
state.Validators.GetProposer().Address)
|
||||
block := sf.MakeBlock(state, i, lastCommit)
|
||||
|
||||
block.Header.Time = defaultEvidenceTime.Add(time.Duration(i) * time.Minute)
|
||||
block.Header.Version = tmversion.Consensus{Block: version.BlockProtocol, App: 1}
|
||||
const parts = 1
|
||||
partSet := block.MakePartSet(parts)
|
||||
partSet, err := block.MakePartSet(parts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seenCommit := makeCommit(i, valAddr)
|
||||
blockStore.SaveBlock(block, partSet, seenCommit)
|
||||
}
|
||||
|
||||
return blockStore
|
||||
return blockStore, nil
|
||||
}
|
||||
|
||||
func makeCommit(height int64, valAddr []byte) *types.Commit {
|
||||
@@ -427,13 +432,15 @@ func makeCommit(height int64, valAddr []byte) *types.Commit {
|
||||
return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
|
||||
}
|
||||
|
||||
func defaultTestPool(height int64) (*evidence.Pool, types.MockPV) {
|
||||
func defaultTestPool(t *testing.T, height int64) (*evidence.Pool, types.MockPV) {
|
||||
t.Helper()
|
||||
val := types.NewMockPV()
|
||||
valAddress := val.PrivKey.PubKey().Address()
|
||||
evidenceDB := dbm.NewMemDB()
|
||||
stateStore := initializeValidatorState(val, height)
|
||||
state, _ := stateStore.Load()
|
||||
blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress)
|
||||
blockStore, err := initializeBlockStore(dbm.NewMemDB(), state, valAddress)
|
||||
require.NoError(t, err)
|
||||
pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore)
|
||||
if err != nil {
|
||||
panic("test evidence pool could not be created")
|
||||
|
||||
@@ -23,6 +23,8 @@ const (
|
||||
MaxActiveIDs = math.MaxUint16
|
||||
)
|
||||
|
||||
//go:generate mockery --case underscore --name Mempool
|
||||
|
||||
// Mempool defines the mempool interface.
|
||||
//
|
||||
// Updates to the mempool need to be synchronized with committing a block so
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/clist"
|
||||
"github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Mempool is an empty implementation of a Mempool, useful for testing.
|
||||
type Mempool struct{}
|
||||
|
||||
var _ mempool.Mempool = Mempool{}
|
||||
|
||||
func (Mempool) Lock() {}
|
||||
func (Mempool) Unlock() {}
|
||||
func (Mempool) Size() int { return 0 }
|
||||
func (Mempool) CheckTx(_ types.Tx, _ func(*abci.Response), _ mempool.TxInfo) error {
|
||||
return nil
|
||||
}
|
||||
func (Mempool) RemoveTxByKey(txKey types.TxKey) error { return nil }
|
||||
func (Mempool) ReapMaxBytesMaxGas(_, _ int64) types.Txs { return types.Txs{} }
|
||||
func (Mempool) ReapMaxTxs(n int) types.Txs { return types.Txs{} }
|
||||
func (Mempool) Update(
|
||||
_ int64,
|
||||
_ types.Txs,
|
||||
_ []*abci.ResponseDeliverTx,
|
||||
_ mempool.PreCheckFunc,
|
||||
_ mempool.PostCheckFunc,
|
||||
) error {
|
||||
return nil
|
||||
}
|
||||
func (Mempool) Flush() {}
|
||||
func (Mempool) FlushAppConn() error { return nil }
|
||||
func (Mempool) TxsAvailable() <-chan struct{} { return make(chan struct{}) }
|
||||
func (Mempool) EnableTxsAvailable() {}
|
||||
func (Mempool) SizeBytes() int64 { return 0 }
|
||||
|
||||
func (Mempool) TxsFront() *clist.CElement { return nil }
|
||||
func (Mempool) TxsWaitChan() <-chan struct{} { return nil }
|
||||
|
||||
func (Mempool) InitWAL() error { return nil }
|
||||
func (Mempool) CloseWAL() {}
|
||||
184
mempool/mocks/mempool.go
Normal file
184
mempool/mocks/mempool.go
Normal file
@@ -0,0 +1,184 @@
|
||||
// Code generated by mockery v2.14.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
abcitypes "github.com/tendermint/tendermint/abci/types"
|
||||
mempool "github.com/tendermint/tendermint/mempool"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Mempool is an autogenerated mock type for the Mempool type
|
||||
type Mempool struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CheckTx provides a mock function with given fields: tx, callback, txInfo
|
||||
func (_m *Mempool) CheckTx(tx types.Tx, callback func(*abcitypes.Response), txInfo mempool.TxInfo) error {
|
||||
ret := _m.Called(tx, callback, txInfo)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Tx, func(*abcitypes.Response), mempool.TxInfo) error); ok {
|
||||
r0 = rf(tx, callback, txInfo)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// EnableTxsAvailable provides a mock function with given fields:
|
||||
func (_m *Mempool) EnableTxsAvailable() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// Flush provides a mock function with given fields:
|
||||
func (_m *Mempool) Flush() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// FlushAppConn provides a mock function with given fields:
|
||||
func (_m *Mempool) FlushAppConn() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Lock provides a mock function with given fields:
|
||||
func (_m *Mempool) Lock() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// ReapMaxBytesMaxGas provides a mock function with given fields: maxBytes, maxGas
|
||||
func (_m *Mempool) ReapMaxBytesMaxGas(maxBytes int64, maxGas int64) types.Txs {
|
||||
ret := _m.Called(maxBytes, maxGas)
|
||||
|
||||
var r0 types.Txs
|
||||
if rf, ok := ret.Get(0).(func(int64, int64) types.Txs); ok {
|
||||
r0 = rf(maxBytes, maxGas)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(types.Txs)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// ReapMaxTxs provides a mock function with given fields: max
|
||||
func (_m *Mempool) ReapMaxTxs(max int) types.Txs {
|
||||
ret := _m.Called(max)
|
||||
|
||||
var r0 types.Txs
|
||||
if rf, ok := ret.Get(0).(func(int) types.Txs); ok {
|
||||
r0 = rf(max)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(types.Txs)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RemoveTxByKey provides a mock function with given fields: txKey
|
||||
func (_m *Mempool) RemoveTxByKey(txKey types.TxKey) error {
|
||||
ret := _m.Called(txKey)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.TxKey) error); ok {
|
||||
r0 = rf(txKey)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Size provides a mock function with given fields:
|
||||
func (_m *Mempool) Size() int {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func() int); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SizeBytes provides a mock function with given fields:
|
||||
func (_m *Mempool) SizeBytes() int64 {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func() int64); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// TxsAvailable provides a mock function with given fields:
|
||||
func (_m *Mempool) TxsAvailable() <-chan struct{} {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 <-chan struct{}
|
||||
if rf, ok := ret.Get(0).(func() <-chan struct{}); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(<-chan struct{})
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Unlock provides a mock function with given fields:
|
||||
func (_m *Mempool) Unlock() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: blockHeight, blockTxs, deliverTxResponses, newPreFn, newPostFn
|
||||
func (_m *Mempool) Update(blockHeight int64, blockTxs types.Txs, deliverTxResponses []*abcitypes.ResponseDeliverTx, newPreFn mempool.PreCheckFunc, newPostFn mempool.PostCheckFunc) error {
|
||||
ret := _m.Called(blockHeight, blockTxs, deliverTxResponses, newPreFn, newPostFn)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(int64, types.Txs, []*abcitypes.ResponseDeliverTx, mempool.PreCheckFunc, mempool.PostCheckFunc) error); ok {
|
||||
r0 = rf(blockHeight, blockTxs, deliverTxResponses, newPreFn, newPostFn)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewMempool interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewMempool creates a new instance of Mempool. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewMempool(t mockConstructorTestingTNewMempool) *Mempool {
|
||||
mock := &Mempool{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -305,15 +305,17 @@ func TestCreateProposalBlock(t *testing.T) {
|
||||
)
|
||||
|
||||
commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
|
||||
block, _ := blockExec.CreateProposalBlock(
|
||||
block, err := blockExec.CreateProposalBlock(
|
||||
height,
|
||||
state, commit,
|
||||
proposerAddr,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// check that the part set does not exceed the maximum block size
|
||||
partSet := block.MakePartSet(partSize)
|
||||
partSet, err := block.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
assert.Less(t, partSet.ByteSize(), int64(maxBytes))
|
||||
|
||||
partSetFromHeader := types.NewPartSetFromHeader(partSet.Header())
|
||||
@@ -384,19 +386,21 @@ func TestMaxProposalBlockSize(t *testing.T) {
|
||||
)
|
||||
|
||||
commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
|
||||
block, _ := blockExec.CreateProposalBlock(
|
||||
block, err := blockExec.CreateProposalBlock(
|
||||
height,
|
||||
state, commit,
|
||||
proposerAddr,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
pb, err := block.ToProto()
|
||||
require.NoError(t, err)
|
||||
assert.Less(t, int64(pb.Size()), maxBytes)
|
||||
|
||||
// check that the part set does not exceed the maximum block size
|
||||
partSet := block.MakePartSet(partSize)
|
||||
partSet, err := block.MakePartSet(partSize)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, partSet.ByteSize(), int64(pb.Size()))
|
||||
}
|
||||
|
||||
|
||||
@@ -126,15 +126,15 @@ message RequestApplySnapshotChunk {
|
||||
}
|
||||
|
||||
message RequestPrepareProposal {
|
||||
// block_data is an array of transactions that will be included in a block,
|
||||
bytes hash = 1;
|
||||
tendermint.types.Header header = 2 [(gogoproto.nullable) = false];
|
||||
// txs is an array of transactions that will be included in a block,
|
||||
// sent to the app for possible modifications.
|
||||
// applications can not exceed the size of the data passed to it.
|
||||
repeated bytes block_data = 1;
|
||||
// If an application decides to populate block_data with extra information, they can not exceed this value.
|
||||
int64 block_data_size = 2;
|
||||
// votes includes all votes from the previous block. This contains vote extension data that can be used in proposal
|
||||
// preparation. The votes here will then form the last commit that gets sent in the proposed block.
|
||||
repeated types.Vote votes = 3;
|
||||
repeated bytes txs = 3;
|
||||
ExtendedCommitInfo local_last_commit = 4 [(gogoproto.nullable) = false];
|
||||
repeated Evidence byzantine_validators = 5 [(gogoproto.nullable) = false];
|
||||
// the modified transactions cannot exceed this size.
|
||||
int64 max_tx_bytes = 6;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
@@ -298,7 +298,8 @@ message ResponseApplySnapshotChunk {
|
||||
}
|
||||
|
||||
message ResponsePrepareProposal {
|
||||
repeated bytes block_data = 1;
|
||||
bool modified_tx = 1;
|
||||
repeated TxRecord tx_records = 2;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
@@ -326,6 +327,11 @@ message LastCommitInfo {
|
||||
repeated VoteInfo votes = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
message ExtendedCommitInfo {
|
||||
int32 round = 1;
|
||||
repeated ExtendedVoteInfo votes = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// Event allows application developers to attach additional information to
|
||||
// ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and ResponseDeliverTx.
|
||||
// Later, transactions may be queried using these events.
|
||||
@@ -354,6 +360,19 @@ message TxResult {
|
||||
ResponseDeliverTx result = 4 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
message TxRecord {
|
||||
TxAction action = 1;
|
||||
bytes tx = 2;
|
||||
|
||||
// TxAction contains App-provided information on what to do with a transaction that is part of a raw proposal
|
||||
enum TxAction {
|
||||
UNKNOWN = 0; // Unknown action
|
||||
UNMODIFIED = 1; // The Application did not modify this transaction.
|
||||
ADDED = 2; // The Application added this transaction.
|
||||
REMOVED = 3; // The Application wants this transaction removed from the proposal and the mempool.
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Blockchain Types
|
||||
|
||||
@@ -374,6 +393,14 @@ message ValidatorUpdate {
|
||||
message VoteInfo {
|
||||
Validator validator = 1 [(gogoproto.nullable) = false];
|
||||
bool signed_last_block = 2;
|
||||
reserved 3; // Placeholder for tendermint_signed_extension in v0.37
|
||||
reserved 4; // Placeholder for app_signed_extension in v0.37
|
||||
}
|
||||
|
||||
message ExtendedVoteInfo {
|
||||
Validator validator = 1 [(gogoproto.nullable) = false];
|
||||
bool signed_last_block = 2;
|
||||
bytes vote_extension = 3; // Reserved for future use
|
||||
}
|
||||
|
||||
enum EvidenceType {
|
||||
|
||||
@@ -4,7 +4,6 @@ package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
|
||||
types "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
@@ -4,7 +4,6 @@ package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
|
||||
types "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
@@ -70,7 +70,7 @@ func WaitForOneEvent(c EventsClient, evtTyp string, timeout time.Duration) (type
|
||||
// make sure to unregister after the test is over
|
||||
defer func() {
|
||||
if deferErr := c.UnsubscribeAll(ctx, subscriber); deferErr != nil {
|
||||
panic(err)
|
||||
panic(deferErr)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -32,19 +32,16 @@ func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error
|
||||
return nil, fmt.Errorf("tx (%X) not found", hash)
|
||||
}
|
||||
|
||||
height := r.Height
|
||||
index := r.Index
|
||||
|
||||
var proof types.TxProof
|
||||
if prove {
|
||||
block := env.BlockStore.LoadBlock(height)
|
||||
proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines
|
||||
block := env.BlockStore.LoadBlock(r.Height)
|
||||
proof = block.Data.Txs.Proof(int(r.Index))
|
||||
}
|
||||
|
||||
return &ctypes.ResultTx{
|
||||
Hash: hash,
|
||||
Height: height,
|
||||
Index: index,
|
||||
Height: r.Height,
|
||||
Index: r.Index,
|
||||
TxResult: r.Result,
|
||||
Tx: r.Tx,
|
||||
Proof: proof,
|
||||
@@ -118,7 +115,7 @@ func TxSearch(
|
||||
var proof types.TxProof
|
||||
if prove {
|
||||
block := env.BlockStore.LoadBlock(r.Height)
|
||||
proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines
|
||||
proof = block.Data.Txs.Proof(int(r.Index))
|
||||
}
|
||||
|
||||
apiResults = append(apiResults, &ctypes.ResultTx{
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
|
||||
"github.com/tendermint/tendermint/libs/fail"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/mempool"
|
||||
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
@@ -34,7 +34,7 @@ type BlockExecutor struct {
|
||||
|
||||
// manage the mempool lock during commit
|
||||
// and update both with block results after commit.
|
||||
mempool mempl.Mempool
|
||||
mempool mempool.Mempool
|
||||
evpool EvidencePool
|
||||
|
||||
logger log.Logger
|
||||
@@ -56,7 +56,7 @@ func NewBlockExecutor(
|
||||
stateStore Store,
|
||||
logger log.Logger,
|
||||
proxyApp proxy.AppConnConsensus,
|
||||
mempool mempl.Mempool,
|
||||
mempool mempool.Mempool,
|
||||
evpool EvidencePool,
|
||||
options ...BlockExecutorOption,
|
||||
) *BlockExecutor {
|
||||
@@ -95,10 +95,11 @@ func (blockExec *BlockExecutor) SetEventBus(eventBus types.BlockEventPublisher)
|
||||
// Contract: application will not return more bytes than are sent over the wire.
|
||||
func (blockExec *BlockExecutor) CreateProposalBlock(
|
||||
height int64,
|
||||
state State, commit *types.Commit,
|
||||
state State,
|
||||
commit *types.Commit,
|
||||
proposerAddr []byte,
|
||||
votes []*types.Vote,
|
||||
) (*types.Block, *types.PartSet) {
|
||||
) (*types.Block, error) {
|
||||
|
||||
maxBytes := state.ConsensusParams.Block.MaxBytes
|
||||
maxGas := state.ConsensusParams.Block.MaxGas
|
||||
@@ -109,12 +110,17 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
|
||||
maxDataBytes := types.MaxDataBytes(maxBytes, evSize, state.Validators.Size())
|
||||
|
||||
txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas)
|
||||
block := state.MakeBlock(height, txs, commit, evidence, proposerAddr)
|
||||
|
||||
preparedProposal, err := blockExec.proxyApp.PrepareProposalSync(
|
||||
localLastCommit := getBeginBlockValidatorInfo(block, blockExec.store, state.InitialHeight)
|
||||
rpp, err := blockExec.proxyApp.PrepareProposalSync(
|
||||
abci.RequestPrepareProposal{
|
||||
BlockData: txs.ToSliceOfBytes(),
|
||||
BlockDataSize: maxDataBytes,
|
||||
Votes: types.VotesToProto(votes),
|
||||
Hash: block.Hash(),
|
||||
Header: *block.Header.ToProto(),
|
||||
Txs: block.Txs.ToSliceOfBytes(),
|
||||
LocalLastCommit: extendedCommitInfo(localLastCommit, votes),
|
||||
ByzantineValidators: block.Evidence.Evidence.ToABCI(),
|
||||
MaxTxBytes: maxDataBytes,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -128,19 +134,28 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
|
||||
// purpose for now.
|
||||
panic(err)
|
||||
}
|
||||
newTxs := preparedProposal.GetBlockData()
|
||||
var txSize int
|
||||
for _, tx := range newTxs {
|
||||
txSize += len(tx)
|
||||
|
||||
if maxDataBytes < int64(txSize) {
|
||||
panic("block data exceeds max amount of allowed bytes")
|
||||
}
|
||||
if !rpp.ModifiedTx {
|
||||
return block, nil
|
||||
}
|
||||
txrSet := types.NewTxRecordSet(rpp.TxRecords)
|
||||
|
||||
if err := txrSet.Validate(maxDataBytes, block.Txs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modifiedTxs := types.ToTxs(preparedProposal.GetBlockData())
|
||||
|
||||
return state.MakeBlock(height, modifiedTxs, commit, evidence, proposerAddr)
|
||||
for _, rtx := range txrSet.RemovedTxs() {
|
||||
if err := blockExec.mempool.RemoveTxByKey(rtx.Key()); err != nil {
|
||||
blockExec.logger.Debug("error removing transaction from the mempool", "error", err, "tx hash", rtx.Hash())
|
||||
}
|
||||
}
|
||||
for _, atx := range txrSet.AddedTxs() {
|
||||
if err := blockExec.mempool.CheckTx(atx, nil, mempool.TxInfo{}); err != nil {
|
||||
blockExec.logger.Error("error adding tx to the mempool", "error", err, "tx hash", atx.Hash())
|
||||
}
|
||||
}
|
||||
itxs := txrSet.IncludedTxs()
|
||||
return state.MakeBlock(height, itxs, commit, evidence, proposerAddr), nil
|
||||
}
|
||||
|
||||
// ValidateBlock validates the given block against the given state.
|
||||
@@ -407,6 +422,24 @@ func getBeginBlockValidatorInfo(block *types.Block, store Store,
|
||||
}
|
||||
}
|
||||
|
||||
func extendedCommitInfo(c abci.LastCommitInfo, votes []*types.Vote) abci.ExtendedCommitInfo {
|
||||
vs := make([]abci.ExtendedVoteInfo, len(c.Votes))
|
||||
for i := range vs {
|
||||
vs[i] = abci.ExtendedVoteInfo{
|
||||
Validator: c.Votes[i].Validator,
|
||||
SignedLastBlock: c.Votes[i].SignedLastBlock,
|
||||
/*
|
||||
TODO: Include vote extensions information when implementing vote extensions.
|
||||
VoteExtension: []byte{},
|
||||
*/
|
||||
}
|
||||
}
|
||||
return abci.ExtendedCommitInfo{
|
||||
Round: c.Round,
|
||||
Votes: vs,
|
||||
}
|
||||
}
|
||||
|
||||
func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate,
|
||||
params tmproto.ValidatorParams) error {
|
||||
for _, valUpdate := range abciUpdates {
|
||||
|
||||
@@ -10,17 +10,20 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
abcimocks "github.com/tendermint/tendermint/abci/types/mocks"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
mmock "github.com/tendermint/tendermint/mempool/mock"
|
||||
mpmocks "github.com/tendermint/tendermint/mempool/mocks"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/mocks"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/test/factory"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
@@ -29,7 +32,6 @@ import (
|
||||
var (
|
||||
chainID = "execution_chain"
|
||||
testPartSize uint32 = 65536
|
||||
nTxsPerBlock = 10
|
||||
)
|
||||
|
||||
func TestApplyBlock(t *testing.T) {
|
||||
@@ -42,12 +44,24 @@ func TestApplyBlock(t *testing.T) {
|
||||
|
||||
state, stateDB, _ := makeState(1, 1)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
|
||||
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).Return(nil)
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
|
||||
mmock.Mempool{}, sm.EmptyEvidencePool{})
|
||||
mp, sm.EmptyEvidencePool{})
|
||||
|
||||
block := makeBlock(state, 1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(state, 1, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block)
|
||||
require.Nil(t, err)
|
||||
@@ -100,7 +114,7 @@ func TestBeginBlockValidators(t *testing.T) {
|
||||
lastCommit := types.NewCommit(1, 0, prevBlockID, tc.lastCommitSigs)
|
||||
|
||||
// block for height 2
|
||||
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
block := sf.MakeBlock(state, 2, lastCommit)
|
||||
|
||||
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
|
||||
require.Nil(t, err, tc.desc)
|
||||
@@ -197,14 +211,28 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
|
||||
evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return(ev, int64(100))
|
||||
evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return()
|
||||
evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).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).Return(nil)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
|
||||
mmock.Mempool{}, evpool)
|
||||
mp, evpool)
|
||||
|
||||
block := makeBlock(state, 1)
|
||||
block := sf.MakeBlock(state, 1, new(types.Commit))
|
||||
block.Evidence = types.EvidenceData{Evidence: ev}
|
||||
block.Header.EvidenceHash = block.Evidence.Hash()
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block)
|
||||
require.Nil(t, err)
|
||||
@@ -355,12 +383,24 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
|
||||
|
||||
state, stateDB, _ := makeState(1, 1)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
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).Return(nil)
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mmock.Mempool{},
|
||||
mp,
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
|
||||
@@ -378,8 +418,10 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := makeBlock(state, 1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(state, 1, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
pubkey := ed25519.GenPrivKey().PubKey()
|
||||
pk, err := cryptoenc.PubKeyToProto(pubkey)
|
||||
@@ -430,12 +472,14 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mmock.Mempool{},
|
||||
new(mpmocks.Mempool),
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
|
||||
block := makeBlock(state, 1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(state, 1, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
vp, err := cryptoenc.PubKeyToProto(state.Validators.Validators[0].PubKey)
|
||||
require.NoError(t, err)
|
||||
@@ -449,6 +493,201 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
|
||||
assert.NotEmpty(t, state.NextValidators.Validators)
|
||||
}
|
||||
|
||||
func TestEmptyPrepareProposal(t *testing.T) {
|
||||
const height = 2
|
||||
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc)
|
||||
err := proxyApp.Start()
|
||||
require.NoError(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
|
||||
|
||||
state, stateDB, privVals := makeState(1, height)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
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).Return(nil)
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mp,
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
|
||||
require.NoError(t, err)
|
||||
_, err = blockExec.CreateProposalBlock(height, state, commit, pa, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestPrepareProposalRemoveTxs tests that any transactions marked as REMOVED
|
||||
// are not included in the block produced by CreateProposalBlock. The test also
|
||||
// ensures that any transactions removed are also removed from the mempool.
|
||||
func TestPrepareProposalRemoveTxs(t *testing.T) {
|
||||
const height = 2
|
||||
|
||||
state, stateDB, privVals := makeState(1, height)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
|
||||
|
||||
txs := factory.MakeTenTxs(height)
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
|
||||
|
||||
trs := txsToTxRecords(types.Txs(txs))
|
||||
trs[0].Action = abci.TxRecord_REMOVED
|
||||
trs[1].Action = abci.TxRecord_REMOVED
|
||||
mp.On("RemoveTxByKey", mock.Anything).Return(nil).Twice()
|
||||
|
||||
app := abcimocks.NewBaseMock()
|
||||
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
|
||||
ModifiedTx: true,
|
||||
TxRecords: trs,
|
||||
}, nil)
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc)
|
||||
err := proxyApp.Start()
|
||||
require.NoError(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mp,
|
||||
evpool,
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
|
||||
require.NoError(t, err)
|
||||
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, block.Data.Txs.ToSliceOfBytes(), len(trs)-2)
|
||||
|
||||
require.Equal(t, -1, block.Data.Txs.Index(types.Tx(trs[0].Tx)))
|
||||
require.Equal(t, -1, block.Data.Txs.Index(types.Tx(trs[1].Tx)))
|
||||
|
||||
mp.AssertCalled(t, "RemoveTxByKey", types.Tx(trs[0].Tx).Key())
|
||||
mp.AssertCalled(t, "RemoveTxByKey", types.Tx(trs[1].Tx).Key())
|
||||
mp.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// TestPrepareProposalAddedTxsIncluded tests that any transactions marked as ADDED
|
||||
// in the prepare proposal response are included in the block. The test also
|
||||
// ensures that any transactions added are also checked into the mempool.
|
||||
func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
|
||||
const height = 2
|
||||
|
||||
state, stateDB, privVals := makeState(1, height)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
|
||||
|
||||
txs := factory.MakeTenTxs(height)
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs[2:]))
|
||||
mp.On("CheckTx", mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice()
|
||||
|
||||
trs := txsToTxRecords(types.Txs(txs))
|
||||
trs[0].Action = abci.TxRecord_ADDED
|
||||
trs[1].Action = abci.TxRecord_ADDED
|
||||
|
||||
app := abcimocks.NewBaseMock()
|
||||
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
|
||||
ModifiedTx: true,
|
||||
TxRecords: trs,
|
||||
}, nil)
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc)
|
||||
err := proxyApp.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mp,
|
||||
evpool,
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
|
||||
require.NoError(t, err)
|
||||
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, txs[0], block.Data.Txs[0])
|
||||
require.Equal(t, txs[1], block.Data.Txs[1])
|
||||
|
||||
mp.AssertExpectations(t)
|
||||
mp.AssertCalled(t, "CheckTx", types.Tx(trs[0].Tx), mock.Anything, mock.Anything)
|
||||
mp.AssertCalled(t, "CheckTx", types.Tx(trs[1].Tx), mock.Anything, mock.Anything)
|
||||
}
|
||||
|
||||
// TestPrepareProposalReorderTxs tests that CreateBlock produces a block with transactions
|
||||
// in the order matching the order they are returned from PrepareProposal.
|
||||
func TestPrepareProposalReorderTxs(t *testing.T) {
|
||||
const height = 2
|
||||
|
||||
state, stateDB, privVals := makeState(1, height)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
|
||||
|
||||
txs := factory.MakeTenTxs(height)
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
|
||||
|
||||
trs := txsToTxRecords(types.Txs(txs))
|
||||
trs = trs[2:]
|
||||
trs = append(trs[len(trs)/2:], trs[:len(trs)/2]...)
|
||||
|
||||
app := abcimocks.NewBaseMock()
|
||||
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
|
||||
ModifiedTx: true,
|
||||
TxRecords: trs,
|
||||
}, nil)
|
||||
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc)
|
||||
err := proxyApp.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mp,
|
||||
evpool,
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
|
||||
require.NoError(t, err)
|
||||
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
|
||||
require.NoError(t, err)
|
||||
for i, tx := range block.Data.Txs {
|
||||
require.Equal(t, types.Tx(trs[i].Tx), tx)
|
||||
}
|
||||
|
||||
mp.AssertExpectations(t)
|
||||
|
||||
}
|
||||
|
||||
func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
|
||||
var (
|
||||
h = make([]byte, tmhash.Size)
|
||||
@@ -464,3 +703,14 @@ func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.Bloc
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func txsToTxRecords(txs []types.Tx) []*abci.TxRecord {
|
||||
trs := make([]*abci.TxRecord, len(txs))
|
||||
for i, tx := range txs {
|
||||
trs[i] = &abci.TxRecord{
|
||||
Action: abci.TxRecord_UNMODIFIED,
|
||||
Tx: tx,
|
||||
}
|
||||
}
|
||||
return trs
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package state_test
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
@@ -10,11 +11,12 @@ import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
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"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/test/factory"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
@@ -54,13 +56,18 @@ func makeAndCommitGoodBlock(
|
||||
|
||||
func makeAndApplyGoodBlock(state sm.State, height int64, lastCommit *types.Commit, proposerAddr []byte,
|
||||
blockExec *sm.BlockExecutor, evidence []types.Evidence) (sm.State, types.BlockID, error) {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr)
|
||||
block := state.MakeBlock(height, factory.MakeTenTxs(height), lastCommit, evidence, proposerAddr)
|
||||
partSet, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
return state, types.BlockID{}, err
|
||||
}
|
||||
|
||||
if err := blockExec.ValidateBlock(state, block); err != nil {
|
||||
return state, types.BlockID{}, err
|
||||
}
|
||||
blockID := types.BlockID{Hash: block.Hash(),
|
||||
PartSetHeader: types.PartSetHeader{Total: 3, Hash: tmrand.Bytes(32)}}
|
||||
state, _, err := blockExec.ApplyBlock(state, blockID, block)
|
||||
PartSetHeader: partSet.Header()}
|
||||
state, _, err = blockExec.ApplyBlock(state, blockID, block)
|
||||
if err != nil {
|
||||
return state, types.BlockID{}, err
|
||||
}
|
||||
@@ -85,14 +92,6 @@ func makeValidCommit(
|
||||
return types.NewCommit(height, 0, blockID, sigs), nil
|
||||
}
|
||||
|
||||
// make some bogus txs
|
||||
func makeTxs(height int64) (txs []types.Tx) {
|
||||
for i := 0; i < nTxsPerBlock; i++ {
|
||||
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) {
|
||||
vals := make([]types.GenesisValidator, nVals)
|
||||
privVals := make(map[string]types.PrivValidator, nVals)
|
||||
@@ -131,17 +130,6 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida
|
||||
return s, stateDB, privVals
|
||||
}
|
||||
|
||||
func makeBlock(state sm.State, height int64) *types.Block {
|
||||
block, _ := state.MakeBlock(
|
||||
height,
|
||||
makeTxs(state.LastBlockHeight),
|
||||
new(types.Commit),
|
||||
nil,
|
||||
state.Validators.GetProposer().Address,
|
||||
)
|
||||
return block
|
||||
}
|
||||
|
||||
func genValSet(size int) *types.ValidatorSet {
|
||||
vals := make([]*types.Validator, size)
|
||||
for i := 0; i < size; i++ {
|
||||
@@ -151,11 +139,12 @@ func genValSet(size int) *types.ValidatorSet {
|
||||
}
|
||||
|
||||
func makeHeaderPartsResponsesValPubKeyChange(
|
||||
t *testing.T,
|
||||
state sm.State,
|
||||
pubkey crypto.PubKey,
|
||||
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1)
|
||||
block := sf.MakeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
@@ -175,11 +164,13 @@ func makeHeaderPartsResponsesValPubKeyChange(
|
||||
}
|
||||
|
||||
func makeHeaderPartsResponsesValPowerChange(
|
||||
t *testing.T,
|
||||
state sm.State,
|
||||
power int64,
|
||||
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1)
|
||||
block := sf.MakeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
|
||||
@@ -199,11 +190,12 @@ func makeHeaderPartsResponsesValPowerChange(
|
||||
}
|
||||
|
||||
func makeHeaderPartsResponsesParams(
|
||||
t *testing.T,
|
||||
state sm.State,
|
||||
params tmproto.ConsensusParams,
|
||||
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1)
|
||||
block := sf.MakeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)},
|
||||
|
||||
@@ -4,7 +4,6 @@ package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
state "github.com/tendermint/tendermint/state"
|
||||
|
||||
types "github.com/tendermint/tendermint/types"
|
||||
|
||||
@@ -4,7 +4,6 @@ package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
state "github.com/tendermint/tendermint/state"
|
||||
|
||||
tendermintstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
|
||||
@@ -238,7 +238,7 @@ func (state State) MakeBlock(
|
||||
commit *types.Commit,
|
||||
evidence []types.Evidence,
|
||||
proposerAddress []byte,
|
||||
) (*types.Block, *types.PartSet) {
|
||||
) *types.Block {
|
||||
|
||||
// Build base block with block data.
|
||||
block := types.MakeBlock(height, txs, commit, evidence)
|
||||
@@ -260,7 +260,7 @@ func (state State) MakeBlock(
|
||||
proposerAddress,
|
||||
)
|
||||
|
||||
return block, block.MakePartSet(types.BlockPartSizeBytes)
|
||||
return block
|
||||
}
|
||||
|
||||
// MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -101,7 +102,7 @@ func TestABCIResponsesSaveLoad1(t *testing.T) {
|
||||
state.LastBlockHeight++
|
||||
|
||||
// Build mock responses.
|
||||
block := makeBlock(state, 2)
|
||||
block := sf.MakeBlock(state, 2, new(types.Commit))
|
||||
|
||||
abciResponses := new(tmstate.ABCIResponses)
|
||||
dtxs := make([]*abci.ResponseDeliverTx, 2)
|
||||
@@ -269,12 +270,12 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
changeIndex++
|
||||
power++
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, power)
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(t, state, power)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
err := stateStore.Save(state)
|
||||
err = stateStore.Save(state)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -443,8 +444,10 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) {
|
||||
// NewValidatorSet calls IncrementProposerPriority but uses on a copy of val1
|
||||
assert.EqualValues(t, 0, val1.ProposerPriority)
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
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},
|
||||
@@ -557,8 +560,10 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
|
||||
// we only have one validator:
|
||||
assert.Equal(t, val1PubKey.Address(), state.Validators.Proposer.Address)
|
||||
|
||||
block := makeBlock(state, state.LastBlockHeight+1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(state, state.LastBlockHeight+1, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
// no updates:
|
||||
abciResponses := &tmstate.ABCIResponses{
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
@@ -744,8 +749,10 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := makeBlock(oldState, oldState.LastBlockHeight+1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
@@ -773,8 +780,12 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal}},
|
||||
}
|
||||
block := makeBlock(oldState, oldState.LastBlockHeight+1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -788,8 +799,12 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := makeBlock(lastState, lastState.LastBlockHeight+1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(lastState, lastState.LastBlockHeight+1, new(types.Commit))
|
||||
|
||||
bps, err = block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
updatedStateInner, err := sm.UpdateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
@@ -821,8 +836,11 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{addedVal}},
|
||||
}
|
||||
block := makeBlock(oldState, oldState.LastBlockHeight+1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
state, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -836,8 +854,14 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
BeginBlock: &abci.ResponseBeginBlock{},
|
||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal}},
|
||||
}
|
||||
block = makeBlock(oldState, oldState.LastBlockHeight+1)
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
|
||||
block = sf.MakeBlock(oldState, oldState.LastBlockHeight+1, new(types.Commit))
|
||||
require.NoError(t, err)
|
||||
|
||||
bps, err = block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
updatedState, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
@@ -857,8 +881,12 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
}
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
block = makeBlock(curState, curState.LastBlockHeight+1)
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block = sf.MakeBlock(curState, curState.LastBlockHeight+1, new(types.Commit))
|
||||
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
curState, err = sm.UpdateState(curState, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
if !bytes.Equal(curState.Validators.Proposer.Address, curState.NextValidators.Proposer.Address) {
|
||||
@@ -882,8 +910,12 @@ func TestLargeGenesisValidator(t *testing.T) {
|
||||
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := makeBlock(updatedState, updatedState.LastBlockHeight+1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
block := sf.MakeBlock(updatedState, updatedState.LastBlockHeight+1, new(types.Commit))
|
||||
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
|
||||
updatedState, err = sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates)
|
||||
require.NoError(t, err)
|
||||
@@ -938,7 +970,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
|
||||
pubkey := ed25519.GenPrivKey().PubKey()
|
||||
|
||||
// Swap the first validator with a new one (validator set size stays the same).
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, pubkey)
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(t, state, pubkey)
|
||||
|
||||
// Save state etc.
|
||||
var validatorUpdates []*types.Validator
|
||||
@@ -977,7 +1009,7 @@ func TestStateMakeBlock(t *testing.T) {
|
||||
|
||||
proposerAddress := state.Validators.GetProposer().Address
|
||||
stateVersion := state.Version.Consensus
|
||||
block := makeBlock(state, 2)
|
||||
block := sf.MakeBlock(state, 2, new(types.Commit))
|
||||
|
||||
// test we set some fields
|
||||
assert.Equal(t, stateVersion, block.Version)
|
||||
@@ -1019,13 +1051,13 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
changeIndex++
|
||||
cp = params[changeIndex]
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesParams(state, cp)
|
||||
header, blockID, responses := makeHeaderPartsResponsesParams(t, state, cp)
|
||||
validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates)
|
||||
require.NoError(t, err)
|
||||
state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates)
|
||||
|
||||
require.Nil(t, err)
|
||||
err := stateStore.Save(state)
|
||||
require.NoError(t, err)
|
||||
err = stateStore.Save(state)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
||||
75
state/test/factory/block.go
Normal file
75
state/test/factory/block.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package factory
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/test/factory"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func MakeBlocks(n int, state *sm.State, privVal types.PrivValidator) ([]*types.Block, error) {
|
||||
blocks := make([]*types.Block, n)
|
||||
|
||||
var (
|
||||
prevBlock *types.Block
|
||||
prevBlockMeta *types.BlockMeta
|
||||
)
|
||||
|
||||
appHeight := byte(0x01)
|
||||
for i := 0; i < n; i++ {
|
||||
height := int64(i + 1)
|
||||
|
||||
block, parts, err := makeBlockAndPartSet(*state, prevBlock, prevBlockMeta, privVal, height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocks[i] = block
|
||||
|
||||
prevBlock = block
|
||||
prevBlockMeta = types.NewBlockMeta(block, parts)
|
||||
|
||||
// update state
|
||||
state.AppHash = []byte{appHeight}
|
||||
appHeight++
|
||||
state.LastBlockHeight = height
|
||||
}
|
||||
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func MakeBlock(state sm.State, height int64, c *types.Commit) *types.Block {
|
||||
return state.MakeBlock(
|
||||
height,
|
||||
factory.MakeTenTxs(state.LastBlockHeight),
|
||||
c,
|
||||
nil,
|
||||
state.Validators.GetProposer().Address,
|
||||
)
|
||||
}
|
||||
|
||||
func makeBlockAndPartSet(
|
||||
state sm.State,
|
||||
lastBlock *types.Block,
|
||||
lastBlockMeta *types.BlockMeta,
|
||||
privVal types.PrivValidator,
|
||||
height int64,
|
||||
) (*types.Block, *types.PartSet, error) {
|
||||
lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
|
||||
if height > 1 {
|
||||
vote, _ := types.MakeVote(
|
||||
lastBlock.Header.Height,
|
||||
lastBlockMeta.BlockID,
|
||||
state.Validators,
|
||||
privVal,
|
||||
lastBlock.Header.ChainID,
|
||||
time.Now())
|
||||
lastCommit = types.NewCommit(vote.Height, vote.Round,
|
||||
lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
|
||||
}
|
||||
|
||||
block := state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
partSet, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
return block, partSet, err
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
query "github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
|
||||
txindex "github.com/tendermint/tendermint/state/txindex"
|
||||
|
||||
@@ -12,10 +12,12 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
memmock "github.com/tendermint/tendermint/mempool/mock"
|
||||
mpmocks "github.com/tendermint/tendermint/mempool/mocks"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/mocks"
|
||||
sf "github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/test/factory"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
@@ -29,11 +31,23 @@ func TestValidateBlockHeader(t *testing.T) {
|
||||
|
||||
state, stateDB, privVals := makeState(3, 1)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
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).Return(nil)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
memmock.Mempool{},
|
||||
mp,
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
@@ -73,12 +87,11 @@ func TestValidateBlockHeader(t *testing.T) {
|
||||
|
||||
// Build up state for multiple heights
|
||||
for height := int64(1); height < validationTestsStopHeight; height++ {
|
||||
proposerAddr := state.Validators.GetProposer().Address
|
||||
/*
|
||||
Invalid blocks don't pass
|
||||
*/
|
||||
for _, tc := range testCases {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr)
|
||||
block := sf.MakeBlock(state, height, lastCommit)
|
||||
tc.malleateBlock(block)
|
||||
err := blockExec.ValidateBlock(state, block)
|
||||
require.Error(t, err, tc.name)
|
||||
@@ -88,7 +101,8 @@ func TestValidateBlockHeader(t *testing.T) {
|
||||
A good block passes
|
||||
*/
|
||||
var err error
|
||||
state, _, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, nil)
|
||||
state, _, lastCommit, err = makeAndCommitGoodBlock(
|
||||
state, height, lastCommit, state.Validators.GetProposer().Address, blockExec, privVals, nil)
|
||||
require.NoError(t, err, "height %d", height)
|
||||
}
|
||||
}
|
||||
@@ -100,11 +114,23 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
|
||||
state, stateDB, privVals := makeState(1, 1)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
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).Return(nil)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
memmock.Mempool{},
|
||||
mp,
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
@@ -133,7 +159,7 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
state.LastBlockID,
|
||||
[]types.CommitSig{wrongHeightVote.CommitSig()},
|
||||
)
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), wrongHeightCommit, nil, proposerAddr)
|
||||
block := sf.MakeBlock(state, height, wrongHeightCommit)
|
||||
err = blockExec.ValidateBlock(state, block)
|
||||
_, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight)
|
||||
require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err)
|
||||
@@ -141,7 +167,7 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
/*
|
||||
#2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size()
|
||||
*/
|
||||
block, _ = state.MakeBlock(height, makeTxs(height), wrongSigsCommit, nil, proposerAddr)
|
||||
block = sf.MakeBlock(state, height, wrongSigsCommit)
|
||||
err = blockExec.ValidateBlock(state, block)
|
||||
_, isErrInvalidCommitSignatures := err.(types.ErrInvalidCommitSignatures)
|
||||
require.True(t, isErrInvalidCommitSignatures,
|
||||
@@ -222,12 +248,23 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return(
|
||||
[]abci.Evidence{})
|
||||
|
||||
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).Return(nil)
|
||||
state.ConsensusParams.Evidence.MaxBytes = 1000
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
memmock.Mempool{},
|
||||
mp,
|
||||
evpool,
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
@@ -248,7 +285,8 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
evidence = append(evidence, newEv)
|
||||
currentBytes += int64(len(newEv.Bytes()))
|
||||
}
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr)
|
||||
block := state.MakeBlock(height, factory.MakeTenTxs(height), lastCommit, evidence, proposerAddr)
|
||||
|
||||
err := blockExec.ValidateBlock(state, block)
|
||||
if assert.Error(t, err) {
|
||||
_, ok := err.(*types.ErrEvidenceOverflow)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
state "github.com/tendermint/tendermint/state"
|
||||
|
||||
types "github.com/tendermint/tendermint/types"
|
||||
|
||||
@@ -3,6 +3,7 @@ package store
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
tmstore "github.com/tendermint/tendermint/proto/tendermint/store"
|
||||
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/test/factory"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
@@ -42,18 +44,6 @@ func makeTestCommit(height int64, timestamp time.Time) *types.Commit {
|
||||
types.BlockID{Hash: []byte(""), PartSetHeader: types.PartSetHeader{Hash: []byte(""), Total: 2}}, commitSigs)
|
||||
}
|
||||
|
||||
func makeTxs(height int64) (txs []types.Tx) {
|
||||
for i := 0; i < 10; i++ {
|
||||
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
return block
|
||||
}
|
||||
|
||||
func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFunc) {
|
||||
config := cfg.ResetTestRoot("blockchain_reactor_test")
|
||||
// blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB())
|
||||
@@ -144,9 +134,14 @@ var (
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var cleanup cleanupFunc
|
||||
var err error
|
||||
state, _, cleanup = makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer)))
|
||||
block = makeBlock(1, state, new(types.Commit))
|
||||
partSet = block.MakePartSet(2)
|
||||
block = factory.MakeBlock(state, 1, new(types.Commit))
|
||||
|
||||
partSet, err = block.MakePartSet(2)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
part1 = partSet.GetPart(0)
|
||||
part2 = partSet.GetPart(1)
|
||||
seenCommit1 = makeTestCommit(10, tmtime.Now())
|
||||
@@ -172,8 +167,9 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
// save a block
|
||||
block := makeBlock(bs.Height()+1, state, new(types.Commit))
|
||||
validPartSet := block.MakePartSet(2)
|
||||
block := factory.MakeBlock(state, bs.Height()+1, new(types.Commit))
|
||||
validPartSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(10, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
require.EqualValues(t, 1, bs.Base(), "expecting the new height to be changed")
|
||||
@@ -181,7 +177,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
|
||||
incompletePartSet := types.NewPartSetFromHeader(types.PartSetHeader{Total: 2})
|
||||
uncontiguousPartSet := types.NewPartSetFromHeader(types.PartSetHeader{Total: 0})
|
||||
_, err := uncontiguousPartSet.AddPart(part2)
|
||||
_, err = uncontiguousPartSet.AddPart(part2)
|
||||
require.Error(t, err)
|
||||
|
||||
header1 := types.Header{
|
||||
@@ -375,8 +371,9 @@ func TestLoadBaseMeta(t *testing.T) {
|
||||
bs := NewBlockStore(dbm.NewMemDB())
|
||||
|
||||
for h := int64(1); h <= 10; h++ {
|
||||
block := makeBlock(h, state, new(types.Commit))
|
||||
partSet := block.MakePartSet(2)
|
||||
block := factory.MakeBlock(state, h, new(types.Commit))
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(h, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
}
|
||||
@@ -443,8 +440,9 @@ func TestPruneBlocks(t *testing.T) {
|
||||
|
||||
// make more than 1000 blocks, to test batch deletions
|
||||
for h := int64(1); h <= 1500; h++ {
|
||||
block := makeBlock(h, state, new(types.Commit))
|
||||
partSet := block.MakePartSet(2)
|
||||
block := factory.MakeBlock(state, h, new(types.Commit))
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(h, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
}
|
||||
@@ -552,9 +550,10 @@ func TestBlockFetchAtHeight(t *testing.T) {
|
||||
state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer)))
|
||||
defer cleanup()
|
||||
require.Equal(t, bs.Height(), int64(0), "initially the height should be zero")
|
||||
block := makeBlock(bs.Height()+1, state, new(types.Commit))
|
||||
block := factory.MakeBlock(state, bs.Height()+1, new(types.Commit))
|
||||
|
||||
partSet := block.MakePartSet(2)
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(10, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
|
||||
|
||||
@@ -259,7 +259,8 @@ func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) a
|
||||
|
||||
func (app *Application) PrepareProposal(
|
||||
req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
|
||||
return abci.ResponsePrepareProposal{BlockData: req.BlockData}
|
||||
// None of the transactions are modified by this application.
|
||||
return abci.ResponsePrepareProposal{ModifiedTx: false}
|
||||
}
|
||||
|
||||
func (app *Application) Rollback() error {
|
||||
|
||||
16
test/factory/tx.go
Normal file
16
test/factory/tx.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package factory
|
||||
|
||||
import "github.com/tendermint/tendermint/types"
|
||||
|
||||
// MakeTxs is a helper function to generate mock transactions by given the block height
|
||||
// and the transaction numbers.
|
||||
func MakeTxs(height int64, num int) (txs []types.Tx) {
|
||||
for i := 0; i < num; i++ {
|
||||
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func MakeTenTxs(height int64) (txs []types.Tx) {
|
||||
return MakeTxs(height, 10)
|
||||
}
|
||||
@@ -1202,8 +1202,17 @@ func (cs *State) defaultDecideProposal(height int64, round int32) {
|
||||
block, blockParts = cs.ValidBlock, cs.ValidBlockParts
|
||||
} else {
|
||||
// Create a new proposal block from state/txs from the mempool.
|
||||
block, blockParts = cs.createProposalBlock()
|
||||
if block == nil {
|
||||
var err error
|
||||
block, err = cs.createProposalBlock()
|
||||
if err != nil {
|
||||
cs.Logger.Error("unable to create proposal block", "error", err)
|
||||
return
|
||||
} else if block == nil {
|
||||
return
|
||||
}
|
||||
blockParts, err = block.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
cs.Logger.Error("unable to create proposal block part set", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1259,13 +1268,12 @@ func (cs *State) isProposalComplete() bool {
|
||||
//
|
||||
// NOTE: keep it side-effect free for clarity.
|
||||
// CONTRACT: cs.privValidator is not nil.
|
||||
func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.PartSet) {
|
||||
func (cs *State) createProposalBlock() (*types.Block, error) {
|
||||
if cs.privValidator == nil {
|
||||
panic("entered createProposalBlock with privValidator being nil")
|
||||
return nil, errors.New("entered createProposalBlock with privValidator being nil")
|
||||
}
|
||||
|
||||
var commit *types.Commit
|
||||
var votes []*types.Vote
|
||||
switch {
|
||||
case cs.Height == cs.state.InitialHeight:
|
||||
// We're creating a proposal for the first block.
|
||||
@@ -1274,21 +1282,20 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa
|
||||
case cs.LastCommit.HasTwoThirdsMajority():
|
||||
// Make the commit from LastCommit
|
||||
commit = cs.LastCommit.MakeCommit()
|
||||
votes = cs.LastCommit.GetVotes()
|
||||
default: // This shouldn't happen.
|
||||
cs.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block")
|
||||
return
|
||||
cs.Logger.Error("propose step; cannot propose anything without commit for the previous block")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// If this node is a validator & proposer in the current round, it will
|
||||
// miss the opportunity to create a block.
|
||||
cs.Logger.Error(fmt.Sprintf("enterPropose: %v", errPubKeyIsNotSet))
|
||||
return
|
||||
cs.Logger.Error("propose step; empty priv validator public key", "err", errPubKeyIsNotSet)
|
||||
return nil, nil
|
||||
}
|
||||
proposerAddr := cs.privValidatorPubKey.Address()
|
||||
|
||||
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, votes)
|
||||
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, cs.LastCommit.GetVotes())
|
||||
}
|
||||
|
||||
// Enter: any +2/3 prevotes at next round.
|
||||
|
||||
@@ -137,22 +137,22 @@ func (b *Block) Hash() tmbytes.HexBytes {
|
||||
// MakePartSet returns a PartSet containing parts of a serialized block.
|
||||
// This is the form in which the block is gossipped to peers.
|
||||
// CONTRACT: partSize is greater than zero.
|
||||
func (b *Block) MakePartSet(partSize uint32) *PartSet {
|
||||
func (b *Block) MakePartSet(partSize uint32) (*PartSet, error) {
|
||||
if b == nil {
|
||||
return nil
|
||||
return nil, errors.New("nil block")
|
||||
}
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
|
||||
pbb, err := b.ToProto()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
bz, err := proto.Marshal(pbb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
return NewPartSetFromData(bz, partSize)
|
||||
return NewPartSetFromData(bz, partSize), nil
|
||||
}
|
||||
|
||||
// HashesTo is a convenience function that checks if a block hashes to the given argument.
|
||||
|
||||
@@ -110,15 +110,20 @@ func TestBlockHash(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBlockMakePartSet(t *testing.T) {
|
||||
assert.Nil(t, (*Block)(nil).MakePartSet(2))
|
||||
bps, err := (*Block)(nil).MakePartSet(2)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bps)
|
||||
|
||||
partSet := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).MakePartSet(1024)
|
||||
partSet, err := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).MakePartSet(1024)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, partSet)
|
||||
assert.EqualValues(t, 1, partSet.Total())
|
||||
}
|
||||
|
||||
func TestBlockMakePartSetWithEvidence(t *testing.T) {
|
||||
assert.Nil(t, (*Block)(nil).MakePartSet(2))
|
||||
bps, err := (*Block)(nil).MakePartSet(2)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bps)
|
||||
|
||||
lastID := makeBlockIDRandom()
|
||||
h := int64(3)
|
||||
@@ -130,7 +135,9 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) {
|
||||
ev := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain")
|
||||
evList := []Evidence{ev}
|
||||
|
||||
partSet := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(512)
|
||||
partSet, err := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(512)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, partSet)
|
||||
assert.EqualValues(t, 4, partSet.Total())
|
||||
}
|
||||
|
||||
@@ -459,6 +459,16 @@ func (evl EvidenceList) Has(evidence Evidence) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ToABCI converts the evidence list to a slice of the ABCI protobuf messages
|
||||
// for use when communicating the evidence to an application.
|
||||
func (evl EvidenceList) ToABCI() []abci.Evidence {
|
||||
var el []abci.Evidence
|
||||
for _, e := range evl {
|
||||
el = append(el, e.ABCI()...)
|
||||
}
|
||||
return el
|
||||
}
|
||||
|
||||
//------------------------------------------ PROTO --------------------------------------
|
||||
|
||||
// EvidenceToProto is a generalized function for encoding evidence that conforms to the
|
||||
|
||||
@@ -245,9 +245,8 @@ func makeVote(
|
||||
|
||||
vpb := v.ToProto()
|
||||
err = val.SignVote(chainID, vpb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
v.Signature = vpb.Signature
|
||||
return v
|
||||
}
|
||||
|
||||
223
types/tx.go
223
types/tx.go
@@ -5,7 +5,9 @@ import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
@@ -45,13 +47,8 @@ type Txs []Tx
|
||||
// Hash returns the Merkle root hash of the transaction hashes.
|
||||
// i.e. the leaves of the tree are the hashes of the txs.
|
||||
func (txs Txs) Hash() []byte {
|
||||
// These allocations will be removed once Txs is switched to [][]byte,
|
||||
// ref #2603. This is because golang does not allow type casting slices without unsafe
|
||||
txBzs := make([][]byte, len(txs))
|
||||
for i := 0; i < len(txs); i++ {
|
||||
txBzs[i] = txs[i].Hash()
|
||||
}
|
||||
return merkle.HashFromByteSlices(txBzs)
|
||||
hl := txs.hashList()
|
||||
return merkle.HashFromByteSlices(hl)
|
||||
}
|
||||
|
||||
// Index returns the index of this transaction in the list, or -1 if not found
|
||||
@@ -74,16 +71,9 @@ func (txs Txs) IndexByHash(hash []byte) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Proof returns a simple merkle proof for this node.
|
||||
// Panics if i < 0 or i >= len(txs)
|
||||
// TODO: optimize this!
|
||||
func (txs Txs) Proof(i int) TxProof {
|
||||
l := len(txs)
|
||||
bzs := make([][]byte, l)
|
||||
for i := 0; i < l; i++ {
|
||||
bzs[i] = txs[i].Hash()
|
||||
}
|
||||
root, proofs := merkle.ProofsFromByteSlices(bzs)
|
||||
hl := txs.hashList()
|
||||
root, proofs := merkle.ProofsFromByteSlices(hl)
|
||||
|
||||
return TxProof{
|
||||
RootHash: root,
|
||||
@@ -92,11 +82,23 @@ func (txs Txs) Proof(i int) TxProof {
|
||||
}
|
||||
}
|
||||
|
||||
func (txs Txs) hashList() [][]byte {
|
||||
hl := make([][]byte, len(txs))
|
||||
for i := 0; i < len(txs); i++ {
|
||||
hl[i] = txs[i].Hash()
|
||||
}
|
||||
return hl
|
||||
}
|
||||
|
||||
// Txs is a slice of transactions. Sorting a Txs value orders the transactions
|
||||
// lexicographically.
|
||||
func (txs Txs) Len() int { return len(txs) }
|
||||
func (txs Txs) Swap(i, j int) { txs[i], txs[j] = txs[j], txs[i] }
|
||||
func (txs Txs) Less(i, j int) bool {
|
||||
return bytes.Compare(txs[i], txs[j]) == -1
|
||||
}
|
||||
|
||||
// ToSliceOfBytes converts a Txs to slice of byte slices.
|
||||
//
|
||||
// NOTE: This method should become obsolete once Txs is switched to [][]byte.
|
||||
// ref: #2603
|
||||
// TODO This function is to disappear when TxRecord is introduced
|
||||
func (txs Txs) ToSliceOfBytes() [][]byte {
|
||||
txBzs := make([][]byte, len(txs))
|
||||
for i := 0; i < len(txs); i++ {
|
||||
@@ -105,13 +107,182 @@ func (txs Txs) ToSliceOfBytes() [][]byte {
|
||||
return txBzs
|
||||
}
|
||||
|
||||
// ToTxs converts a raw slice of byte slices into a Txs type.
|
||||
func ToTxs(txs [][]byte) Txs {
|
||||
txBzs := make(Txs, len(txs))
|
||||
for i := 0; i < len(txs); i++ {
|
||||
txBzs[i] = txs[i]
|
||||
// TxRecordSet contains indexes into an underlying set of transactions.
|
||||
// These indexes are useful for validating and working with a list of TxRecords
|
||||
// from the PrepareProposal response.
|
||||
//
|
||||
// Only one copy of the original data is referenced by all of the indexes but a
|
||||
// transaction may appear in multiple indexes.
|
||||
type TxRecordSet struct {
|
||||
// all holds the complete list of all transactions from the original list of
|
||||
// TxRecords.
|
||||
all Txs
|
||||
|
||||
// included is an index of the transactions that will be included in the block
|
||||
// and is constructed from the list of both added and unmodified transactions.
|
||||
// included maintains the original order that the transactions were present
|
||||
// in the list of TxRecords.
|
||||
included Txs
|
||||
|
||||
// added, unmodified, removed, and unknown are indexes for each of the actions
|
||||
// that may be supplied with a transaction.
|
||||
//
|
||||
// Because each transaction only has one action, it can be referenced by
|
||||
// at most 3 indexes in this data structure: the action-specific index, the
|
||||
// included index, and the all index.
|
||||
added Txs
|
||||
unmodified Txs
|
||||
removed Txs
|
||||
unknown Txs
|
||||
}
|
||||
|
||||
// NewTxRecordSet constructs a new set from the given transaction records.
|
||||
// The contents of the input transactions are shared by the set, and must not
|
||||
// be modified during the lifetime of the set.
|
||||
func NewTxRecordSet(trs []*abci.TxRecord) TxRecordSet {
|
||||
txrSet := TxRecordSet{
|
||||
all: make([]Tx, len(trs)),
|
||||
}
|
||||
return txBzs
|
||||
for i, tr := range trs {
|
||||
|
||||
txrSet.all[i] = Tx(tr.Tx)
|
||||
|
||||
// The following set of assignments do not allocate new []byte, they create
|
||||
// pointers to the already allocated slice.
|
||||
switch tr.GetAction() {
|
||||
case abci.TxRecord_UNKNOWN:
|
||||
txrSet.unknown = append(txrSet.unknown, txrSet.all[i])
|
||||
case abci.TxRecord_UNMODIFIED:
|
||||
txrSet.unmodified = append(txrSet.unmodified, txrSet.all[i])
|
||||
txrSet.included = append(txrSet.included, txrSet.all[i])
|
||||
case abci.TxRecord_ADDED:
|
||||
txrSet.added = append(txrSet.added, txrSet.all[i])
|
||||
txrSet.included = append(txrSet.included, txrSet.all[i])
|
||||
case abci.TxRecord_REMOVED:
|
||||
txrSet.removed = append(txrSet.removed, txrSet.all[i])
|
||||
}
|
||||
}
|
||||
return txrSet
|
||||
}
|
||||
|
||||
// IncludedTxs returns the transactions marked for inclusion in a block. This
|
||||
// list maintains the order that the transactions were included in the list of
|
||||
// TxRecords that were used to construct the TxRecordSet.
|
||||
func (t TxRecordSet) IncludedTxs() []Tx {
|
||||
return t.included
|
||||
}
|
||||
|
||||
// AddedTxs returns the transactions added by the application.
|
||||
func (t TxRecordSet) AddedTxs() []Tx {
|
||||
return t.added
|
||||
}
|
||||
|
||||
// RemovedTxs returns the transactions marked for removal by the application.
|
||||
func (t TxRecordSet) RemovedTxs() []Tx {
|
||||
return t.removed
|
||||
}
|
||||
|
||||
// Validate checks that the record set was correctly constructed from the original
|
||||
// list of transactions.
|
||||
func (t TxRecordSet) Validate(maxSizeBytes int64, otxs Txs) error {
|
||||
if len(t.unknown) > 0 {
|
||||
return fmt.Errorf("%d transactions marked unknown (first unknown hash: %x)", len(t.unknown), t.unknown[0].Hash())
|
||||
}
|
||||
|
||||
// The following validation logic performs a set of sorts on the data in the TxRecordSet indexes.
|
||||
// It sorts the original transaction list, otxs, once.
|
||||
// It sorts the new transaction list twice: once when sorting 'all', the total list,
|
||||
// and once by sorting the set of the added, removed, and unmodified transactions indexes,
|
||||
// which, when combined, comprise the complete list of modified transactions.
|
||||
//
|
||||
// Each of the added, removed, and unmodified indices is then iterated and once
|
||||
// and each value index is checked against the sorted original list for containment.
|
||||
// Asymptotically, this yields a total runtime of O(N*log(N) + 2*M*log(M) + M*log(N)).
|
||||
// in the input size of the original list, N, and the input size of the new list, M, respectively.
|
||||
// Performance gains are likely possible, but this was preferred for readability and maintainability.
|
||||
|
||||
// Sort a copy of the complete transaction slice so we can check for
|
||||
// duplication. The copy is so we do not change the original ordering.
|
||||
// Only the slices are copied, the transaction contents are shared.
|
||||
allCopy := sortedCopy(t.all)
|
||||
|
||||
var size int64
|
||||
for i, cur := range allCopy {
|
||||
size += int64(len(cur))
|
||||
if size > maxSizeBytes {
|
||||
return fmt.Errorf("transaction data size %d exceeds maximum %d", size, maxSizeBytes)
|
||||
}
|
||||
|
||||
// allCopy is sorted, so any duplicated data will be adjacent.
|
||||
if i+1 < len(allCopy) && bytes.Equal(cur, allCopy[i+1]) {
|
||||
return fmt.Errorf("found duplicate transaction with hash: %x", cur.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
// create copies of each of the action-specific indexes so that order of the original
|
||||
// indexes can be preserved.
|
||||
addedCopy := sortedCopy(t.added)
|
||||
removedCopy := sortedCopy(t.removed)
|
||||
unmodifiedCopy := sortedCopy(t.unmodified)
|
||||
|
||||
// make a defensive copy of otxs so that the order of
|
||||
// the caller's data is not altered.
|
||||
otxsCopy := sortedCopy(otxs)
|
||||
|
||||
if ix, ok := containsAll(otxsCopy, unmodifiedCopy); !ok {
|
||||
return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", unmodifiedCopy[ix].Hash())
|
||||
}
|
||||
|
||||
if ix, ok := containsAll(otxsCopy, removedCopy); !ok {
|
||||
return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", removedCopy[ix].Hash())
|
||||
}
|
||||
if ix, ok := containsAny(otxsCopy, addedCopy); ok {
|
||||
return fmt.Errorf("existing transaction incorrectly marked as added, transaction hash: %x", addedCopy[ix].Hash())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortedCopy(txs Txs) Txs {
|
||||
cp := make(Txs, len(txs))
|
||||
copy(cp, txs)
|
||||
sort.Sort(cp)
|
||||
return cp
|
||||
}
|
||||
|
||||
// containsAny checks that list a contains one of the transactions in list
|
||||
// b. If a match is found, the index in b of the matching transaction is returned.
|
||||
// Both lists must be sorted.
|
||||
func containsAny(a, b []Tx) (int, bool) {
|
||||
for i, cur := range b {
|
||||
if _, ok := contains(a, cur); ok {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
// containsAll checks that super contains all of the transactions in the sub
|
||||
// list. If not all values in sub are present in super, the index in sub of the
|
||||
// first Tx absent from super is returned.
|
||||
func containsAll(super, sub Txs) (int, bool) {
|
||||
for i, cur := range sub {
|
||||
if _, ok := contains(super, cur); !ok {
|
||||
return i, false
|
||||
}
|
||||
}
|
||||
return -1, true
|
||||
}
|
||||
|
||||
// contains checks that the sorted list, set contains elem. If set does contain elem, then the
|
||||
// index in set of elem is returned.
|
||||
func contains(set []Tx, elem Tx) (int, bool) {
|
||||
n := sort.Search(len(set), func(i int) bool {
|
||||
return bytes.Compare(elem, set[i]) <= 0
|
||||
})
|
||||
if n == len(set) || !bytes.Equal(elem, set[n]) {
|
||||
return -1, false
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
// TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree.
|
||||
|
||||
165
types/tx_test.go
165
types/tx_test.go
@@ -2,11 +2,13 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
ctest "github.com/tendermint/tendermint/libs/test"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
@@ -20,11 +22,6 @@ func makeTxs(cnt, size int) Txs {
|
||||
return txs
|
||||
}
|
||||
|
||||
func randInt(low, high int) int {
|
||||
off := tmrand.Int() % (high - low)
|
||||
return low + off
|
||||
}
|
||||
|
||||
func TestTxIndex(t *testing.T) {
|
||||
for i := 0; i < 20; i++ {
|
||||
txs := makeTxs(15, 60)
|
||||
@@ -51,6 +48,160 @@ func TestTxIndexByHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateTxRecordSet(t *testing.T) {
|
||||
t.Run("should error on total transaction size exceeding max data size", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{6, 7, 8, 9, 10}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(9, []Tx{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("should error on duplicate transactions with the same action", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{100}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{200}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(100, []Tx{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("should error on duplicate transactions with mixed actions", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{100}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_REMOVED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{200}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(100, []Tx{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("should error on new transactions marked UNMODIFIED", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_UNMODIFIED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(100, []Tx{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("should error on new transactions marked REMOVED", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_REMOVED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(100, []Tx{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("should error on existing transaction marked as ADDED", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{5, 4, 3, 2, 1}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{6}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(100, []Tx{{0}, {1, 2, 3, 4, 5}})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("should error if any transaction marked as UNKNOWN", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_UNKNOWN,
|
||||
Tx: Tx([]byte{1, 2, 3, 4, 5}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(100, []Tx{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("TxRecordSet preserves order", func(t *testing.T) {
|
||||
trs := []*abci.TxRecord{
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{100}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{99}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{55}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{12}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{66}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{9}),
|
||||
},
|
||||
{
|
||||
Action: abci.TxRecord_ADDED,
|
||||
Tx: Tx([]byte{17}),
|
||||
},
|
||||
}
|
||||
txrSet := NewTxRecordSet(trs)
|
||||
err := txrSet.Validate(100, []Tx{})
|
||||
require.NoError(t, err)
|
||||
for i, tx := range txrSet.IncludedTxs() {
|
||||
require.Equal(t, Tx(trs[i].Tx), tx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidTxProof(t *testing.T) {
|
||||
cases := []struct {
|
||||
txs Txs
|
||||
@@ -149,3 +300,7 @@ func assertBadProof(t *testing.T, root []byte, bad []byte, good TxProof) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func randInt(low, high int) int {
|
||||
return rand.Intn(high-low) + low
|
||||
}
|
||||
|
||||
@@ -227,6 +227,9 @@ func (voteSet *VoteSet) getVote(valIndex int32, blockKey string) (vote *Vote, ok
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) GetVotes() []*Vote {
|
||||
if voteSet == nil {
|
||||
return nil
|
||||
}
|
||||
return voteSet.votes
|
||||
}
|
||||
|
||||
@@ -631,6 +634,7 @@ func (voteSet *VoteSet) MakeCommit() *Commit {
|
||||
if commitSig.ForBlock() && !v.BlockID.Equals(*voteSet.maj23) {
|
||||
commitSig = NewCommitSigAbsent()
|
||||
}
|
||||
|
||||
commitSigs[i] = commitSig
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user