diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 512863c41..2966b2ec4 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -15,6 +15,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - P2P Protocol - Go API + - [evidence] [\#5499](https://github.com/tendermint/tendermint/pull/5449) `MaxNum` evidence consensus parameter has been changed to `MaxBytes` (@cmwaters) - Blockchain Protocol diff --git a/config/toml.go b/config/toml.go index 260eda7cd..0dce3e6dc 100644 --- a/config/toml.go +++ b/config/toml.go @@ -515,7 +515,7 @@ var testGenesisFmt = `{ "evidence": { "max_age_num_blocks": "100000", "max_age_duration": "172800000000000", - "max_num": 50 + "max_bytes": "1048576" }, "validator": { "pub_key_types": [ diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index eb8591848..ce08daab4 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -167,11 +167,11 @@ func TestReactorWithEvidence(t *testing.T) { // mock the evidence pool // everyone includes evidence of another double signing vIdx := (i + 1) % nValidators + ev := types.NewMockDuplicateVoteEvidenceWithValidator(1, defaultTestTime, privVals[vIdx], config.ChainID()) evpool := &statemocks.EvidencePool{} evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil) - evpool.On("PendingEvidence", mock.AnythingOfType("uint32")).Return([]types.Evidence{ - types.NewMockDuplicateVoteEvidenceWithValidator(1, defaultTestTime, privVals[vIdx], config.ChainID()), - }) + evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return([]types.Evidence{ + ev}, int64(len(ev.Bytes()))) evpool.On("Update", mock.AnythingOfType("state.State")).Return() evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return( []abci.Evidence{}) diff --git a/evidence/doc.go b/evidence/doc.go index 5d823952d..d521debd3 100644 --- a/evidence/doc.go +++ b/evidence/doc.go @@ -32,7 +32,7 @@ All evidence is proto encoded to disk. Proposing When a new block is being proposed (in state/execution.go#CreateProposalBlock), -`PendingEvidence(maxNum)` is called to send up to the maxNum number of uncommitted evidence, from the evidence store, +`PendingEvidence(maxBytes)` is called to send up to the maxBytes of uncommitted evidence, from the evidence store, prioritized in order of age. All evidence is checked for expiration. When a node receives evidence in a block it will use the evidence module as a cache first to see if it has diff --git a/evidence/mocks/state_store.go b/evidence/mocks/state_store.go deleted file mode 100644 index cdda2ce9a..000000000 --- a/evidence/mocks/state_store.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated by mockery v2.1.0. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - state "github.com/tendermint/tendermint/state" - - types "github.com/tendermint/tendermint/types" -) - -// StateStore is an autogenerated mock type for the StateStore type -type StateStore struct { - mock.Mock -} - -// LoadState provides a mock function with given fields: -func (_m *StateStore) LoadState() state.State { - ret := _m.Called() - - var r0 state.State - if rf, ok := ret.Get(0).(func() state.State); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(state.State) - } - - return r0 -} - -// LoadValidators provides a mock function with given fields: height -func (_m *StateStore) LoadValidators(height int64) (*types.ValidatorSet, error) { - ret := _m.Called(height) - - var r0 *types.ValidatorSet - if rf, ok := ret.Get(0).(func(int64) *types.ValidatorSet); ok { - r0 = rf(height) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.ValidatorSet) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64) error); ok { - r1 = rf(height) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/evidence/pool.go b/evidence/pool.go index 9db7048c7..87ae46e3d 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -64,19 +64,16 @@ func NewPool(evidenceDB dbm.DB, stateDB sm.Store, blockStore BlockStore) (*Pool, logger: log.NewNopLogger(), evidenceStore: evidenceDB, evidenceList: clist.New(), - evidenceSize: 0, - pruningHeight: state.LastBlockHeight, - pruningTime: state.LastBlockTime, } // if pending evidence already in db, in event of prior failure, then check for expiration, // update the size and load it back to the evidenceList - pool.removeExpiredPendingEvidence() - evList, err := pool.listEvidence(baseKeyPending, -1) + pool.pruningHeight, pool.pruningTime = pool.removeExpiredPendingEvidence() + evList, _, err := pool.listEvidence(baseKeyPending, -1) if err != nil { return nil, err } - atomic.AddUint32(&pool.evidenceSize, uint32(len(evList))) + atomic.StoreUint32(&pool.evidenceSize, uint32(len(evList))) for _, ev := range evList { pool.evidenceList.PushBack(ev) } @@ -85,12 +82,15 @@ func NewPool(evidenceDB dbm.DB, stateDB sm.Store, blockStore BlockStore) (*Pool, } // PendingEvidence is used primarily as part of block proposal and returns up to maxNum of uncommitted evidence. -func (evpool *Pool) PendingEvidence(maxNum uint32) []types.Evidence { - evidence, err := evpool.listEvidence(baseKeyPending, int64(maxNum)) +func (evpool *Pool) PendingEvidence(maxBytes int64) ([]types.Evidence, int64) { + if atomic.LoadUint32(&evpool.evidenceSize) == 0 { + return []types.Evidence{}, 0 + } + evidence, size, err := evpool.listEvidence(baseKeyPending, maxBytes) if err != nil { evpool.logger.Error("Unable to retrieve pending evidence", "err", err) } - return evidence + return evidence, size } // Update pulls the latest state to be used for expiration and evidence params and then prunes all expired evidence @@ -330,6 +330,7 @@ type info struct { Time time.Time Validators []*types.Validator TotalVotingPower int64 + ByteSize int64 } // ToProto encodes into protobuf @@ -381,6 +382,7 @@ func infoFromProto(proto *evproto.Info) (info, error) { Time: proto.Time, Validators: vals, TotalVotingPower: proto.TotalVotingPower, + ByteSize: int64(proto.Evidence.Size()), }, nil } @@ -489,31 +491,32 @@ func (evpool *Pool) removePendingEvidence(evidence types.Evidence) { } } -// listEvidence lists up to maxNum pieces of evidence for the given prefix key. -// If maxNum is -1, there's no cap on the size of returned evidence. -func (evpool *Pool) listEvidence(prefixKey byte, maxNum int64) ([]types.Evidence, error) { - var count int64 +// listEvidence retrieves lists evidence from oldest to newest within maxBytes. +// If maxBytes is -1, there's no cap on the size of returned evidence. +func (evpool *Pool) listEvidence(prefixKey byte, maxBytes int64) ([]types.Evidence, int64, error) { + var totalSize int64 var evidence []types.Evidence iter, err := dbm.IteratePrefix(evpool.evidenceStore, []byte{prefixKey}) if err != nil { - return nil, fmt.Errorf("database error: %v", err) + return nil, totalSize, fmt.Errorf("database error: %v", err) } defer iter.Close() for ; iter.Valid(); iter.Next() { - if count == maxNum { - return evidence, nil - } - count++ - evInfo, err := bytesToInfo(iter.Value()) if err != nil { - return nil, err + return nil, totalSize, err + } + + totalSize += evInfo.ByteSize + + if maxBytes != -1 && totalSize > maxBytes { + return evidence, totalSize - evInfo.ByteSize, nil } evidence = append(evidence, evInfo.Evidence) } - return evidence, nil + return evidence, totalSize, nil } func (evpool *Pool) removeExpiredPendingEvidence() (int64, time.Time) { @@ -534,7 +537,8 @@ func (evpool *Pool) removeExpiredPendingEvidence() (int64, time.Time) { if len(blockEvidenceMap) != 0 { evpool.removeEvidenceFromList(blockEvidenceMap) } - // return the time with which this evidence will have expired so we know when to prune next + + // return the height and time with which this evidence will have expired so we know when to prune next return evInfo.Evidence.Height() + evpool.State().ConsensusParams.Evidence.MaxAgeNumBlocks + 1, evInfo.Time.Add(evpool.State().ConsensusParams.Evidence.MaxAgeDuration).Add(time.Second) } diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 53e9e9ba2..40949ef89 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -32,7 +32,10 @@ func TestMain(m *testing.M) { const evidenceChainID = "test_chain" -var defaultEvidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) +var ( + defaultEvidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) + defaultEvidenceMaxBytes int64 = 1000 +) func TestEvidencePoolBasic(t *testing.T) { var ( @@ -52,10 +55,12 @@ func TestEvidencePoolBasic(t *testing.T) { pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) require.NoError(t, err) + pool.SetLogger(log.TestingLogger()) // evidence not seen yet: - evs := pool.PendingEvidence(10) + evs, size := pool.PendingEvidence(defaultEvidenceMaxBytes) assert.Equal(t, 0, len(evs)) + assert.Zero(t, size) ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, privVals[0], evidenceChainID) @@ -78,12 +83,14 @@ func TestEvidencePoolBasic(t *testing.T) { next := pool.EvidenceFront() assert.Equal(t, ev, next.Value.(types.Evidence)) - evs = pool.PendingEvidence(10) + evs, size = pool.PendingEvidence(defaultEvidenceMaxBytes) assert.Equal(t, 1, len(evs)) + assert.Equal(t, int64(357), size) // check that the size of the single evidence in bytes is correct // shouldn't be able to add evidence twice assert.Error(t, pool.AddEvidence(ev)) - assert.Equal(t, 1, len(pool.PendingEvidence(10))) + evs, _ = pool.PendingEvidence(defaultEvidenceMaxBytes) + assert.Equal(t, 1, len(evs)) } @@ -183,12 +190,15 @@ func TestEvidencePoolUpdate(t *testing.T) { }, } assert.Equal(t, expectedByzVals, byzVals) - assert.Equal(t, 1, len(pool.PendingEvidence(10))) + evList, _ := pool.PendingEvidence(defaultEvidenceMaxBytes) + assert.Equal(t, 1, len(evList)) pool.Update(state) // a) Update marks evidence as committed so pending evidence should be empty - assert.Empty(t, pool.PendingEvidence(10)) + evList, evSize := pool.PendingEvidence(defaultEvidenceMaxBytes) + assert.Empty(t, evList) + assert.Zero(t, evSize) // b) If we try to check this evidence again it should fail because it has already been committed err = pool.CheckEvidence(types.EvidenceList{ev}) @@ -293,7 +303,6 @@ func TestCheckEvidenceWithLightClientAttack(t *testing.T) { func TestRecoverPendingEvidence(t *testing.T) { height := int64(10) - expiredEvidenceTime := time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC) val := types.NewMockPV() valAddress := val.PrivKey.PubKey().Address() evidenceDB := dbm.NewMemDB() @@ -301,21 +310,24 @@ func TestRecoverPendingEvidence(t *testing.T) { state, err := stateStore.Load() require.NoError(t, err) blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress) + // create previous pool and populate it pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) require.NoError(t, err) pool.SetLogger(log.TestingLogger()) goodEvidence := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, val, evidenceChainID) expiredEvidence := types.NewMockDuplicateVoteEvidenceWithValidator(int64(1), - expiredEvidenceTime, val, evidenceChainID) + defaultEvidenceTime, val, evidenceChainID) err = pool.AddEvidence(goodEvidence) require.NoError(t, err) err = pool.AddEvidence(expiredEvidence) require.NoError(t, err) + + // now recover from the previous pool at a different time newStateStore := &smmocks.Store{} newStateStore.On("Load").Return(sm.State{ - LastBlockTime: defaultEvidenceTime.Add(49 * time.Hour), - LastBlockHeight: height + 12, + LastBlockTime: defaultEvidenceTime.Add(25 * time.Minute), + LastBlockHeight: height + 15, ConsensusParams: tmproto.ConsensusParams{ Block: tmproto.BlockParams{ MaxBytes: 22020096, @@ -323,14 +335,15 @@ func TestRecoverPendingEvidence(t *testing.T) { }, Evidence: tmproto.EvidenceParams{ MaxAgeNumBlocks: 20, - MaxAgeDuration: 1 * time.Hour, - MaxNum: 50, + MaxAgeDuration: 20 * time.Minute, + MaxBytes: 1000, }, }, }, nil) newPool, err := evidence.NewPool(evidenceDB, newStateStore, blockStore) assert.NoError(t, err) - assert.Equal(t, 1, len(newPool.PendingEvidence(10))) + evList, _ := newPool.PendingEvidence(defaultEvidenceMaxBytes) + assert.Equal(t, 1, len(evList)) next := newPool.EvidenceFront() assert.Equal(t, goodEvidence, next.Value.(types.Evidence)) @@ -356,7 +369,7 @@ func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) s Evidence: tmproto.EvidenceParams{ MaxAgeNumBlocks: 20, MaxAgeDuration: 20 * time.Minute, - MaxNum: 50, + MaxBytes: 1000, }, }, } diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index d21ab52b5..8b05ab0fd 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -106,14 +106,17 @@ func _waitForEvidence( pools []*evidence.Pool, ) { evpool := pools[poolIdx] - for len(evpool.PendingEvidence(uint32(len(evs)))) != len(evs) { + var evList []types.Evidence + currentPoolSize := 0 + for currentPoolSize != len(evs) { + evList, _ = evpool.PendingEvidence(int64(len(evs) * 500)) // each evidence should not be more than 500 bytes + currentPoolSize = len(evList) time.Sleep(time.Millisecond * 100) } - reapedEv := evpool.PendingEvidence(uint32(len(evs))) // put the reaped evidence in a map so we can quickly check we got everything evMap := make(map[string]types.Evidence) - for _, e := range reapedEv { + for _, e := range evList { evMap[string(e.Hash())] = e } for i, expectedEv := range evs { diff --git a/evidence/verify_test.go b/evidence/verify_test.go index 016a867dc..e344cd496 100644 --- a/evidence/verify_test.go +++ b/evidence/verify_test.go @@ -102,7 +102,7 @@ func TestVerifyLightClientAttack_Lunatic(t *testing.T) { err = pool.CheckEvidence(evList) assert.NoError(t, err) - pendingEvs := pool.PendingEvidence(2) + pendingEvs, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes) assert.Equal(t, 1, len(pendingEvs)) pubKey, err := newPrivVal.GetPubKey() @@ -206,7 +206,7 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { err = pool.CheckEvidence(evList) assert.NoError(t, err) - pendingEvs := pool.PendingEvidence(2) + pendingEvs, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes) assert.Equal(t, 1, len(pendingEvs)) pubKey, err := conflictingPrivVals[0].GetPubKey() @@ -303,7 +303,7 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) { err = pool.CheckEvidence(evList) assert.NoError(t, err) - pendingEvs := pool.PendingEvidence(2) + pendingEvs, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes) assert.Equal(t, 1, len(pendingEvs)) pubKey, err := conflictingPrivVals[0].GetPubKey() diff --git a/node/node_test.go b/node/node_test.go index 101699cce..65a972fc7 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -234,9 +234,9 @@ func TestCreateProposalBlock(t *testing.T) { state, stateDB, privVals := state(1, height) stateStore := sm.NewStore(stateDB) maxBytes := 16384 - maxEvidence := 10 + maxEvidenceBytes := int64(maxBytes / 2) state.ConsensusParams.Block.MaxBytes = int64(maxBytes) - state.ConsensusParams.Evidence.MaxNum = uint32(maxEvidence) + state.ConsensusParams.Evidence.MaxBytes = maxEvidenceBytes proposerAddr, _ := state.Validators.GetByIndex(0) // Make Mempool @@ -260,8 +260,10 @@ func TestCreateProposalBlock(t *testing.T) { // fill the evidence pool with more evidence // than can fit in a block - for i := 0; i <= maxEvidence; i++ { + var currentBytes int64 = 0 + for currentBytes <= maxEvidenceBytes { ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), privVals[0], "test-chain") + currentBytes += int64(len(ev.Bytes())) err := evidencePool.AddEvidenceFromConsensus(ev, time.Now(), state.Validators) require.NoError(t, err) } diff --git a/proto/tendermint/types/params.pb.go b/proto/tendermint/types/params.pb.go index 0b0773250..5c9eff877 100644 --- a/proto/tendermint/types/params.pb.go +++ b/proto/tendermint/types/params.pb.go @@ -179,11 +179,10 @@ type EvidenceParams struct { // mechanism for handling [Nothing-At-Stake // attacks](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ#what-is-the-nothing-at-stake-problem-and-how-can-it-be-fixed). MaxAgeDuration time.Duration `protobuf:"bytes,2,opt,name=max_age_duration,json=maxAgeDuration,proto3,stdduration" json:"max_age_duration"` - // This sets the maximum number of evidence that can be committed in a single block. - // and should fall comfortably under the max block bytes when we consider the size of - // each evidence (See MaxEvidenceBytes). The maximum number is MaxEvidencePerBlock. - // Default is 50 - MaxNum uint32 `protobuf:"varint,3,opt,name=max_num,json=maxNum,proto3" json:"max_num,omitempty"` + // This sets the maximum size of total evidence in bytes that can be committed in a single block. + // and should fall comfortably under the max block bytes. + // Default is 1048576 or 1MB + MaxBytes int64 `protobuf:"varint,3,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` } func (m *EvidenceParams) Reset() { *m = EvidenceParams{} } @@ -233,9 +232,9 @@ func (m *EvidenceParams) GetMaxAgeDuration() time.Duration { return 0 } -func (m *EvidenceParams) GetMaxNum() uint32 { +func (m *EvidenceParams) GetMaxBytes() int64 { if m != nil { - return m.MaxNum + return m.MaxBytes } return 0 } @@ -398,42 +397,41 @@ func init() { func init() { proto.RegisterFile("tendermint/types/params.proto", fileDescriptor_e12598271a686f57) } var fileDescriptor_e12598271a686f57 = []byte{ - // 545 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0x4f, 0x6f, 0xd3, 0x30, - 0x1c, 0xad, 0xd7, 0xb2, 0xb5, 0xbf, 0xae, 0xeb, 0x64, 0x21, 0x51, 0x86, 0x96, 0x94, 0x1c, 0xd0, - 0x24, 0xa4, 0x44, 0x82, 0x03, 0x62, 0x97, 0x89, 0xc0, 0x34, 0x10, 0xea, 0x84, 0x22, 0xe0, 0xb0, - 0x4b, 0xe4, 0xb4, 0x26, 0x8b, 0x56, 0xc7, 0x51, 0x6c, 0x57, 0xed, 0xb7, 0xe0, 0xb8, 0xe3, 0x2e, - 0x48, 0x7c, 0x04, 0x3e, 0xc2, 0x8e, 0x3b, 0x72, 0x02, 0xd4, 0x5e, 0xf8, 0x18, 0x28, 0x4e, 0x4d, - 0xff, 0x6c, 0xb7, 0xe4, 0xf7, 0x7b, 0xef, 0xd9, 0xef, 0x3d, 0x19, 0xf6, 0x25, 0x4d, 0x07, 0x34, - 0x67, 0x49, 0x2a, 0x3d, 0x39, 0xc9, 0xa8, 0xf0, 0x32, 0x92, 0x13, 0x26, 0xdc, 0x2c, 0xe7, 0x92, - 0xe3, 0xdd, 0xc5, 0xda, 0xd5, 0xeb, 0xbd, 0xfb, 0x31, 0x8f, 0xb9, 0x5e, 0x7a, 0xc5, 0x57, 0x89, - 0xdb, 0xb3, 0x62, 0xce, 0xe3, 0x21, 0xf5, 0xf4, 0x5f, 0xa4, 0xbe, 0x78, 0x03, 0x95, 0x13, 0x99, - 0xf0, 0xb4, 0xdc, 0x3b, 0x97, 0x1b, 0xd0, 0x7e, 0xcd, 0x53, 0x41, 0x53, 0xa1, 0xc4, 0x07, 0x7d, - 0x02, 0x7e, 0x09, 0xf7, 0xa2, 0x21, 0xef, 0x5f, 0x74, 0x50, 0x17, 0x1d, 0x34, 0x9f, 0xed, 0xbb, - 0xeb, 0x67, 0xb9, 0x7e, 0xb1, 0x2e, 0xd1, 0x7e, 0xed, 0xfa, 0x97, 0x5d, 0x09, 0x4a, 0x06, 0xf6, - 0xa1, 0x4e, 0x47, 0xc9, 0x80, 0xa6, 0x7d, 0xda, 0xd9, 0xd0, 0xec, 0xee, 0x6d, 0xf6, 0xf1, 0x1c, - 0xb1, 0x22, 0xf0, 0x9f, 0x87, 0x8f, 0xa1, 0x31, 0x22, 0xc3, 0x64, 0x40, 0x24, 0xcf, 0x3b, 0x55, - 0x2d, 0xf2, 0xf8, 0xb6, 0xc8, 0x67, 0x03, 0x59, 0x51, 0x59, 0x30, 0xf1, 0x11, 0x6c, 0x8d, 0x68, - 0x2e, 0x12, 0x9e, 0x76, 0x6a, 0x5a, 0xc4, 0xbe, 0x43, 0xa4, 0x04, 0xac, 0x48, 0x18, 0x96, 0x43, - 0xa1, 0xb9, 0xe4, 0x13, 0x3f, 0x82, 0x06, 0x23, 0xe3, 0x30, 0x9a, 0x48, 0x2a, 0x74, 0x32, 0xd5, - 0xa0, 0xce, 0xc8, 0xd8, 0x2f, 0xfe, 0xf1, 0x03, 0xd8, 0x2a, 0x96, 0x31, 0x11, 0xda, 0x76, 0x35, - 0xd8, 0x64, 0x64, 0x7c, 0x42, 0x04, 0xee, 0xc2, 0xb6, 0x4c, 0x18, 0x0d, 0x13, 0x2e, 0x49, 0xc8, - 0x84, 0xf6, 0x53, 0x0d, 0xa0, 0x98, 0xbd, 0xe3, 0x92, 0xf4, 0x84, 0xf3, 0x0d, 0xc1, 0xce, 0x6a, - 0x22, 0xf8, 0x29, 0xe0, 0x42, 0x8d, 0xc4, 0x34, 0x4c, 0x15, 0x0b, 0x75, 0xb4, 0xe6, 0xcc, 0x36, - 0x23, 0xe3, 0x57, 0x31, 0x3d, 0x55, 0x4c, 0x5f, 0x4e, 0xe0, 0x1e, 0xec, 0x1a, 0xb0, 0xe9, 0x76, - 0x1e, 0xfd, 0x43, 0xb7, 0x2c, 0xdf, 0x35, 0xe5, 0xbb, 0x6f, 0xe6, 0x00, 0xbf, 0x5e, 0x58, 0xbd, - 0xfc, 0x6d, 0xa3, 0x60, 0xa7, 0xd4, 0x33, 0x1b, 0xe3, 0x24, 0x55, 0x4c, 0xdf, 0xb5, 0xa5, 0x9d, - 0x9c, 0x2a, 0xe6, 0x1c, 0x41, 0x7b, 0x2d, 0x73, 0xec, 0x40, 0x2b, 0x53, 0x51, 0x78, 0x41, 0x27, - 0xa1, 0xce, 0xb3, 0x83, 0xba, 0xd5, 0x83, 0x46, 0xd0, 0xcc, 0x54, 0xf4, 0x9e, 0x4e, 0x3e, 0x16, - 0xa3, 0xc3, 0xfa, 0x8f, 0x2b, 0x1b, 0xfd, 0xbd, 0xb2, 0x91, 0x73, 0x08, 0xad, 0x95, 0xbc, 0xb1, - 0x0d, 0x4d, 0x92, 0x65, 0xa1, 0x69, 0xa9, 0xf0, 0x57, 0x0b, 0x80, 0x64, 0xd9, 0x1c, 0xb6, 0xc4, - 0x3d, 0x83, 0xed, 0xb7, 0x44, 0x9c, 0xd3, 0xc1, 0x9c, 0xfa, 0x04, 0xda, 0x3a, 0x95, 0x70, 0xbd, - 0x92, 0x96, 0x1e, 0xf7, 0x4c, 0x2f, 0x0e, 0xb4, 0x16, 0xb8, 0x45, 0x3b, 0x4d, 0x83, 0x3a, 0x21, - 0xc2, 0xff, 0xf4, 0x7d, 0x6a, 0xa1, 0xeb, 0xa9, 0x85, 0x6e, 0xa6, 0x16, 0xfa, 0x33, 0xb5, 0xd0, - 0xd7, 0x99, 0x55, 0xb9, 0x99, 0x59, 0x95, 0x9f, 0x33, 0xab, 0x72, 0xf6, 0x22, 0x4e, 0xe4, 0xb9, - 0x8a, 0xdc, 0x3e, 0x67, 0xde, 0xf2, 0x93, 0x5c, 0x7c, 0x96, 0x6f, 0x6e, 0xfd, 0xb9, 0x46, 0x9b, - 0x7a, 0xfe, 0xfc, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x53, 0x0a, 0x6c, 0x0d, 0xc9, 0x03, 0x00, - 0x00, + // 537 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0x31, 0x6f, 0xd3, 0x40, + 0x18, 0xcd, 0xd5, 0xa5, 0x4d, 0xbe, 0x34, 0x4d, 0x75, 0x42, 0x22, 0x14, 0xd5, 0x0e, 0x1e, 0x50, + 0x25, 0x24, 0x5b, 0x82, 0x01, 0xd1, 0xa5, 0xc2, 0x50, 0x15, 0x84, 0x82, 0x90, 0x05, 0x0c, 0x5d, + 0xac, 0x73, 0x72, 0xb8, 0x56, 0x73, 0x3e, 0xcb, 0x77, 0x8e, 0x92, 0x7f, 0xc1, 0xd8, 0xb1, 0x23, + 0xfc, 0x03, 0x7e, 0x42, 0xc7, 0x8e, 0x4c, 0x80, 0x92, 0x85, 0x9f, 0x81, 0x7c, 0xce, 0xe1, 0x38, + 0x65, 0xf3, 0x7d, 0xdf, 0x7b, 0xef, 0xfc, 0xde, 0xd3, 0xc1, 0x81, 0xa4, 0xc9, 0x88, 0x66, 0x2c, + 0x4e, 0xa4, 0x2b, 0x67, 0x29, 0x15, 0x6e, 0x4a, 0x32, 0xc2, 0x84, 0x93, 0x66, 0x5c, 0x72, 0xbc, + 0x57, 0xad, 0x1d, 0xb5, 0xde, 0xbf, 0x1b, 0xf1, 0x88, 0xab, 0xa5, 0x5b, 0x7c, 0x95, 0xb8, 0x7d, + 0x33, 0xe2, 0x3c, 0x1a, 0x53, 0x57, 0x9d, 0xc2, 0xfc, 0xb3, 0x3b, 0xca, 0x33, 0x22, 0x63, 0x9e, + 0x94, 0x7b, 0xfb, 0x72, 0x03, 0xba, 0x2f, 0x79, 0x22, 0x68, 0x22, 0x72, 0xf1, 0x5e, 0xdd, 0x80, + 0x9f, 0xc3, 0x9d, 0x70, 0xcc, 0x87, 0x17, 0x3d, 0xd4, 0x47, 0x87, 0xed, 0x27, 0x07, 0xce, 0xfa, + 0x5d, 0x8e, 0x57, 0xac, 0x4b, 0xb4, 0xb7, 0x79, 0xfd, 0xd3, 0x6a, 0xf8, 0x25, 0x03, 0x7b, 0xd0, + 0xa4, 0x93, 0x78, 0x44, 0x93, 0x21, 0xed, 0x6d, 0x28, 0x76, 0xff, 0x36, 0xfb, 0x64, 0x89, 0xa8, + 0x09, 0xfc, 0xe3, 0xe1, 0x13, 0x68, 0x4d, 0xc8, 0x38, 0x1e, 0x11, 0xc9, 0xb3, 0x9e, 0xa1, 0x44, + 0x1e, 0xde, 0x16, 0xf9, 0xa4, 0x21, 0x35, 0x95, 0x8a, 0x89, 0x8f, 0x61, 0x7b, 0x42, 0x33, 0x11, + 0xf3, 0xa4, 0xb7, 0xa9, 0x44, 0xac, 0xff, 0x88, 0x94, 0x80, 0x9a, 0x84, 0x66, 0xd9, 0x14, 0xda, + 0x2b, 0x3e, 0xf1, 0x03, 0x68, 0x31, 0x32, 0x0d, 0xc2, 0x99, 0xa4, 0x42, 0x25, 0x63, 0xf8, 0x4d, + 0x46, 0xa6, 0x5e, 0x71, 0xc6, 0xf7, 0x60, 0xbb, 0x58, 0x46, 0x44, 0x28, 0xdb, 0x86, 0xbf, 0xc5, + 0xc8, 0xf4, 0x94, 0x08, 0xdc, 0x87, 0x1d, 0x19, 0x33, 0x1a, 0xc4, 0x5c, 0x92, 0x80, 0x09, 0xe5, + 0xc7, 0xf0, 0xa1, 0x98, 0xbd, 0xe1, 0x92, 0x0c, 0x84, 0xfd, 0x0d, 0xc1, 0x6e, 0x3d, 0x11, 0xfc, + 0x18, 0x70, 0xa1, 0x46, 0x22, 0x1a, 0x24, 0x39, 0x0b, 0x54, 0xb4, 0xfa, 0xce, 0x2e, 0x23, 0xd3, + 0x17, 0x11, 0x7d, 0x97, 0x33, 0xf5, 0x73, 0x02, 0x0f, 0x60, 0x4f, 0x83, 0x75, 0xb7, 0xcb, 0xe8, + 0xef, 0x3b, 0x65, 0xf9, 0x8e, 0x2e, 0xdf, 0x79, 0xb5, 0x04, 0x78, 0xcd, 0xc2, 0xea, 0xe5, 0x2f, + 0x0b, 0xf9, 0xbb, 0xa5, 0x9e, 0xde, 0xd4, 0x6d, 0x1a, 0x75, 0x9b, 0xf6, 0x31, 0x74, 0xd7, 0x72, + 0xc7, 0x36, 0x74, 0xd2, 0x3c, 0x0c, 0x2e, 0xe8, 0x2c, 0x50, 0x99, 0xf6, 0x50, 0xdf, 0x38, 0x6c, + 0xf9, 0xed, 0x34, 0x0f, 0xdf, 0xd2, 0xd9, 0x87, 0x62, 0x74, 0xd4, 0xfc, 0x7e, 0x65, 0xa1, 0x3f, + 0x57, 0x16, 0xb2, 0x8f, 0xa0, 0x53, 0xcb, 0x1c, 0x5b, 0xd0, 0x26, 0x69, 0x1a, 0xe8, 0xa6, 0x0a, + 0x8f, 0x9b, 0x3e, 0x90, 0x34, 0x5d, 0xc2, 0x56, 0xb8, 0x67, 0xb0, 0xf3, 0x9a, 0x88, 0x73, 0x3a, + 0x5a, 0x52, 0x1f, 0x41, 0x57, 0x25, 0x13, 0xac, 0xd7, 0xd2, 0x51, 0xe3, 0x81, 0xee, 0xc6, 0x86, + 0x4e, 0x85, 0xab, 0x1a, 0x6a, 0x6b, 0xd4, 0x29, 0x11, 0xde, 0xc7, 0xaf, 0x73, 0x13, 0x5d, 0xcf, + 0x4d, 0x74, 0x33, 0x37, 0xd1, 0xef, 0xb9, 0x89, 0xbe, 0x2c, 0xcc, 0xc6, 0xcd, 0xc2, 0x6c, 0xfc, + 0x58, 0x98, 0x8d, 0xb3, 0x67, 0x51, 0x2c, 0xcf, 0xf3, 0xd0, 0x19, 0x72, 0xe6, 0xae, 0x3e, 0xcb, + 0xea, 0xb3, 0x7c, 0x77, 0xeb, 0x4f, 0x36, 0xdc, 0x52, 0xf3, 0xa7, 0x7f, 0x03, 0x00, 0x00, 0xff, + 0xff, 0xfe, 0xe0, 0x3d, 0x9c, 0xcd, 0x03, 0x00, 0x00, } func (this *ConsensusParams) Equal(that interface{}) bool { @@ -524,7 +522,7 @@ func (this *EvidenceParams) Equal(that interface{}) bool { if this.MaxAgeDuration != that1.MaxAgeDuration { return false } - if this.MaxNum != that1.MaxNum { + if this.MaxBytes != that1.MaxBytes { return false } return true @@ -730,8 +728,8 @@ func (m *EvidenceParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.MaxNum != 0 { - i = encodeVarintParams(dAtA, i, uint64(m.MaxNum)) + if m.MaxBytes != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MaxBytes)) i-- dAtA[i] = 0x18 } @@ -993,8 +991,8 @@ func (m *EvidenceParams) Size() (n int) { } l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.MaxAgeDuration) n += 1 + l + sovParams(uint64(l)) - if m.MaxNum != 0 { - n += 1 + sovParams(uint64(m.MaxNum)) + if m.MaxBytes != 0 { + n += 1 + sovParams(uint64(m.MaxBytes)) } return n } @@ -1425,9 +1423,9 @@ func (m *EvidenceParams) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MaxNum", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MaxBytes", wireType) } - m.MaxNum = 0 + m.MaxBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowParams @@ -1437,7 +1435,7 @@ func (m *EvidenceParams) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.MaxNum |= uint32(b&0x7F) << shift + m.MaxBytes |= int64(b&0x7F) << shift if b < 0x80 { break } diff --git a/proto/tendermint/types/params.proto b/proto/tendermint/types/params.proto index 897c07c17..0de7d846f 100644 --- a/proto/tendermint/types/params.proto +++ b/proto/tendermint/types/params.proto @@ -48,11 +48,10 @@ message EvidenceParams { google.protobuf.Duration max_age_duration = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; - // This sets the maximum number of evidence that can be committed in a single block. - // and should fall comfortably under the max block bytes when we consider the size of - // each evidence (See MaxEvidenceBytes). The maximum number is MaxEvidencePerBlock. - // Default is 50 - uint32 max_num = 3; + // This sets the maximum size of total evidence in bytes that can be committed in a single block. + // and should fall comfortably under the max block bytes. + // Default is 1048576 or 1MB + int64 max_bytes = 3; } // ValidatorParams restrict the public key types validators can use. diff --git a/state/execution.go b/state/execution.go index 41ea8a9b0..2cb5857a1 100644 --- a/state/execution.go +++ b/state/execution.go @@ -100,10 +100,10 @@ func (blockExec *BlockExecutor) CreateProposalBlock( maxBytes := state.ConsensusParams.Block.MaxBytes maxGas := state.ConsensusParams.Block.MaxGas - evidence := blockExec.evpool.PendingEvidence(state.ConsensusParams.Evidence.MaxNum) + evidence, evSize := blockExec.evpool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes) // Fetch a limited amount of valid txs - maxDataBytes := types.MaxDataBytes(maxBytes, state.Validators.Size(), len(evidence)) + maxDataBytes := types.MaxDataBytes(maxBytes, evSize, state.Validators.Size()) txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas) return state.MakeBlock(height, txs, commit, evidence, proposerAddr) diff --git a/state/mocks/evidence_pool.go b/state/mocks/evidence_pool.go index 0915c9966..bfd82e596 100644 --- a/state/mocks/evidence_pool.go +++ b/state/mocks/evidence_pool.go @@ -61,11 +61,11 @@ func (_m *EvidencePool) CheckEvidence(_a0 types.EvidenceList) error { } // PendingEvidence provides a mock function with given fields: _a0 -func (_m *EvidencePool) PendingEvidence(_a0 uint32) []types.Evidence { +func (_m *EvidencePool) PendingEvidence(_a0 int64) ([]types.Evidence, int64) { ret := _m.Called(_a0) var r0 []types.Evidence - if rf, ok := ret.Get(0).(func(uint32) []types.Evidence); ok { + if rf, ok := ret.Get(0).(func(int64) []types.Evidence); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { @@ -73,7 +73,14 @@ func (_m *EvidencePool) PendingEvidence(_a0 uint32) []types.Evidence { } } - return r0 + var r1 int64 + if rf, ok := ret.Get(1).(func(int64) int64); ok { + r1 = rf(_a0) + } else { + r1 = ret.Get(1).(int64) + } + + return r0, r1 } // Update provides a mock function with given fields: _a0 diff --git a/state/services.go b/state/services.go index bf00b3fe0..e2f12b237 100644 --- a/state/services.go +++ b/state/services.go @@ -43,7 +43,7 @@ type BlockStore interface { // EvidencePool defines the EvidencePool interface used by State. type EvidencePool interface { - PendingEvidence(uint32) []types.Evidence + PendingEvidence(maxBytes int64) (ev []types.Evidence, size int64) AddEvidence(types.Evidence) error Update(State) CheckEvidence(types.EvidenceList) error @@ -54,7 +54,9 @@ type EvidencePool interface { // to the consensus evidence pool interface type EmptyEvidencePool struct{} -func (EmptyEvidencePool) PendingEvidence(uint32) []types.Evidence { return nil } +func (EmptyEvidencePool) PendingEvidence(maxBytes int64) (ev []types.Evidence, size int64) { + return nil, 0 +} func (EmptyEvidencePool) AddEvidence(types.Evidence) error { return nil } func (EmptyEvidencePool) Update(State) {} func (EmptyEvidencePool) CheckEvidence(evList types.EvidenceList) error { return nil } diff --git a/state/tx_filter.go b/state/tx_filter.go index acc4aad8f..52d055966 100644 --- a/state/tx_filter.go +++ b/state/tx_filter.go @@ -8,10 +8,9 @@ import ( // TxPreCheck returns a function to filter transactions before processing. // The function limits the size of a transaction to the block's maximum data size. func TxPreCheck(state State) mempl.PreCheckFunc { - maxDataBytes := types.MaxDataBytesUnknownEvidence( + maxDataBytes := types.MaxDataBytesNoEvidence( state.ConsensusParams.Block.MaxBytes, state.Validators.Size(), - state.ConsensusParams.Evidence.MaxNum, ) return mempl.PreCheckMaxBytes(maxDataBytes) } diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index fa5392337..f5923b84b 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -17,7 +17,7 @@ import ( func TestTxFilter(t *testing.T) { genDoc := randomGenesisDoc() genDoc.ConsensusParams.Block.MaxBytes = 3000 - genDoc.ConsensusParams.Evidence.MaxNum = 1 + genDoc.ConsensusParams.Evidence.MaxBytes = 1500 // Max size of Txs is much smaller than size of block, // since we need to account for commits and evidence. @@ -25,8 +25,8 @@ func TestTxFilter(t *testing.T) { tx types.Tx isErr bool }{ - {types.Tx(tmrand.Bytes(1680)), false}, - {types.Tx(tmrand.Bytes(1853)), true}, + {types.Tx(tmrand.Bytes(2154)), false}, + {types.Tx(tmrand.Bytes(2155)), true}, {types.Tx(tmrand.Bytes(3000)), true}, } diff --git a/state/validation.go b/state/validation.go index 4bcf11721..779edf273 100644 --- a/state/validation.go +++ b/state/validation.go @@ -142,8 +142,8 @@ func validateBlock(evidencePool EvidencePool, state State, block *types.Block) e block.Height, state.InitialHeight) } - // Check evidence doesn't exceed the limit. MaxNumEvidence is capped at uint16, so conversion is always safe. - if max, got := int(state.ConsensusParams.Evidence.MaxNum), len(block.Evidence.Evidence); got > max { + // Check evidence doesn't exceed the limit amount of bytes. + if max, got := state.ConsensusParams.Evidence.MaxBytes, block.Evidence.ByteSize(); got > max { return types.NewErrEvidenceOverflow(max, got) } diff --git a/state/validation_test.go b/state/validation_test.go index a4b0667c8..8fa5f89b3 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -222,7 +222,7 @@ func TestValidateBlockEvidence(t *testing.T) { evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return( []abci.Evidence{}) - state.ConsensusParams.Evidence.MaxNum = 3 + state.ConsensusParams.Evidence.MaxBytes = 1000 blockExec := sm.NewBlockExecutor( stateStore, log.TestingLogger(), @@ -234,17 +234,19 @@ func TestValidateBlockEvidence(t *testing.T) { for height := int64(1); height < validationTestsStopHeight; height++ { proposerAddr := state.Validators.GetProposer().Address - maxNumEvidence := state.ConsensusParams.Evidence.MaxNum + maxBytesEvidence := state.ConsensusParams.Evidence.MaxBytes if height > 1 { /* A block with too much evidence fails */ - require.True(t, maxNumEvidence > 2) evidence := make([]types.Evidence, 0) - // one more than the maximum allowed evidence - for i := uint32(0); i <= maxNumEvidence; i++ { - evidence = append(evidence, types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), - privVals[proposerAddr.String()], chainID)) + var currentBytes int64 = 0 + // more bytes than the maximum allowed for evidence + for currentBytes <= maxBytesEvidence { + newEv := types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), + privVals[proposerAddr.String()], chainID) + evidence = append(evidence, newEv) + currentBytes += int64(len(newEv.Bytes())) } block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr) err := blockExec.ValidateBlock(state, block) @@ -257,14 +259,17 @@ func TestValidateBlockEvidence(t *testing.T) { /* A good block with several pieces of good evidence passes */ - require.True(t, maxNumEvidence > 2) evidence := make([]types.Evidence, 0) + var currentBytes int64 = 0 // precisely the amount of allowed evidence - for i := int32(0); uint32(i) < maxNumEvidence; i++ { - // make different evidence for each validator - _, val := state.Validators.GetByIndex(i) - evidence = append(evidence, types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, - privVals[val.Address.String()], chainID)) + for { + newEv := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, + privVals[proposerAddr.String()], chainID) + currentBytes += int64(len(newEv.Bytes())) + if currentBytes >= maxBytesEvidence { + break + } + evidence = append(evidence, newEv) } var err error diff --git a/types/block.go b/types/block.go index 749770768..f4942a243 100644 --- a/types/block.go +++ b/types/block.go @@ -273,12 +273,12 @@ func BlockFromProto(bp *tmproto.Block) (*Block, error) { // MaxDataBytes returns the maximum size of block's data. // // XXX: Panics on negative result. -func MaxDataBytes(maxBytes int64, valsCount, evidenceCount int) int64 { +func MaxDataBytes(maxBytes, evidenceBytes int64, valsCount int) int64 { maxDataBytes := maxBytes - MaxOverheadForBlock - MaxHeaderBytes - int64(valsCount)*MaxVoteBytes - - int64(evidenceCount)*MaxEvidenceBytes + evidenceBytes if maxDataBytes < 0 { panic(fmt.Sprintf( @@ -292,18 +292,16 @@ func MaxDataBytes(maxBytes int64, valsCount, evidenceCount int) int64 { } -// MaxDataBytesUnknownEvidence returns the maximum size of block's data when +// MaxDataBytesNoEvidence returns the maximum size of block's data when // evidence count is unknown. MaxEvidencePerBlock will be used for the size // of evidence. // // XXX: Panics on negative result. -func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int, maxNumEvidence uint32) int64 { - maxEvidenceBytes := int64(maxNumEvidence) * MaxEvidenceBytes +func MaxDataBytesNoEvidence(maxBytes int64, valsCount int) int64 { maxDataBytes := maxBytes - MaxOverheadForBlock - MaxHeaderBytes - - int64(valsCount)*MaxVoteBytes - - maxEvidenceBytes + int64(valsCount)*MaxVoteBytes if maxDataBytes < 0 { panic(fmt.Sprintf( @@ -1073,8 +1071,9 @@ func DataFromProto(dp *tmproto.Data) (Data, error) { type EvidenceData struct { Evidence EvidenceList `json:"evidence"` - // Volatile - hash tmbytes.HexBytes + // Volatile. Used as cache + hash tmbytes.HexBytes + byteSize int64 } // Hash returns the hash of the data. @@ -1085,6 +1084,20 @@ func (data *EvidenceData) Hash() tmbytes.HexBytes { return data.hash } +// ByteSize returns the total byte size of all the evidence +func (data *EvidenceData) ByteSize() int64 { + if data.byteSize == 0 && len(data.Evidence) != 0 { + for _, ev := range data.Evidence { + pb, err := EvidenceToProto(ev) + if err != nil { + panic(err) + } + data.byteSize += int64(pb.Size()) + } + } + return data.byteSize +} + // StringIndented returns a string representation of the evidence. func (data *EvidenceData) StringIndented(indent string) string { if data == nil { @@ -1142,11 +1155,10 @@ func (data *EvidenceData) FromProto(eviData *tmproto.EvidenceData) error { return err } eviBzs[i] = evi + data.byteSize += int64(eviData.Evidence[i].Size()) } data.Evidence = eviBzs - data.hash = eviData.GetHash() - return nil } diff --git a/types/block_test.go b/types/block_test.go index eb7d29237..8e2d65f96 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -401,7 +401,7 @@ func TestBlockMaxDataBytes(t *testing.T) { testCases := []struct { maxBytes int64 valsCount int - evidenceCount int + evidenceBytes int64 panics bool result int64 }{ @@ -416,43 +416,41 @@ func TestBlockMaxDataBytes(t *testing.T) { tc := tc if tc.panics { assert.Panics(t, func() { - MaxDataBytes(tc.maxBytes, tc.valsCount, tc.evidenceCount) + MaxDataBytes(tc.maxBytes, tc.evidenceBytes, tc.valsCount) }, "#%v", i) } else { assert.Equal(t, tc.result, - MaxDataBytes(tc.maxBytes, tc.valsCount, tc.evidenceCount), + MaxDataBytes(tc.maxBytes, tc.evidenceBytes, tc.valsCount), "#%v", i) } } } -func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) { +func TestBlockMaxDataBytesNoEvidence(t *testing.T) { testCases := []struct { - maxBytes int64 - maxEvidence uint32 - valsCount int - panics bool - result int64 + maxBytes int64 + valsCount int + panics bool + result int64 }{ - 0: {-10, 0, 1, true, 0}, - 1: {10, 0, 1, true, 0}, - 2: {845, 0, 1, true, 0}, - 3: {846, 0, 1, false, 0}, - 4: {1290, 1, 1, false, 0}, - 5: {1291, 1, 1, false, 1}, + 0: {-10, 1, true, 0}, + 1: {10, 1, true, 0}, + 2: {845, 1, true, 0}, + 3: {846, 1, false, 0}, + 4: {847, 1, false, 1}, } for i, tc := range testCases { tc := tc if tc.panics { assert.Panics(t, func() { - MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount, tc.maxEvidence) + MaxDataBytesNoEvidence(tc.maxBytes, tc.valsCount) }, "#%v", i) } else { assert.Equal(t, tc.result, - MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount, tc.maxEvidence), + MaxDataBytesNoEvidence(tc.maxBytes, tc.valsCount), "#%v", i) } } @@ -620,7 +618,7 @@ func TestBlockProtoBuf(t *testing.T) { require.NoError(t, err, tc.msg) require.EqualValues(t, tc.b1.Header, block.Header, tc.msg) require.EqualValues(t, tc.b1.Data, block.Data, tc.msg) - require.EqualValues(t, tc.b1.Evidence, block.Evidence, tc.msg) + require.EqualValues(t, tc.b1.Evidence.Evidence, block.Evidence.Evidence, tc.msg) require.EqualValues(t, *tc.b1.LastCommit, *block.LastCommit, tc.msg) } else { require.Error(t, err, tc.msg) @@ -653,6 +651,7 @@ func TestDataProtoBuf(t *testing.T) { } } +// TestEvidenceDataProtoBuf ensures parity in converting to and from proto. func TestEvidenceDataProtoBuf(t *testing.T) { val := NewMockPV() blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) @@ -662,7 +661,7 @@ func TestEvidenceDataProtoBuf(t *testing.T) { v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, time.Now()) ev := NewDuplicateVoteEvidence(v2, v) data := &EvidenceData{Evidence: EvidenceList{ev}} - _ = data.Hash() + _ = data.ByteSize() testCases := []struct { msg string data1 *EvidenceData diff --git a/types/evidence.go b/types/evidence.go index f6a8c560f..5b450f9e1 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -25,11 +25,6 @@ type Evidence interface { String() string // string format of the evidence } -const ( - // MaxEvidenceBytes is a maximum size of any evidence (including amino overhead). - MaxEvidenceBytes int64 = 444 -) - //-------------------------------------------------------------------------------------- // DuplicateVoteEvidence contains evidence a validator signed two conflicting @@ -365,20 +360,20 @@ func (err *ErrInvalidEvidence) Error() string { return fmt.Sprintf("Invalid evidence: %v. Evidence: %v", err.Reason, err.Evidence) } -// ErrEvidenceOverflow is for when there is too much evidence in a block. +// ErrEvidenceOverflow is for when there the amount of evidence exceeds the max bytes. type ErrEvidenceOverflow struct { - MaxNum int - GotNum int + Max int64 + Got int64 } // NewErrEvidenceOverflow returns a new ErrEvidenceOverflow where got > max. -func NewErrEvidenceOverflow(max, got int) *ErrEvidenceOverflow { +func NewErrEvidenceOverflow(max, got int64) *ErrEvidenceOverflow { return &ErrEvidenceOverflow{max, got} } // Error returns a string representation of the error. func (err *ErrEvidenceOverflow) Error() string { - return fmt.Sprintf("Too much evidence: Max %d, got %d", err.MaxNum, err.GotNum) + return fmt.Sprintf("Too much evidence: Max %d, got %d", err.Max, err.Got) } //-------------------------------------------- MOCKING -------------------------------------- diff --git a/types/evidence_test.go b/types/evidence_test.go index 1afbf6a99..4982a7d90 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -27,37 +27,6 @@ func TestEvidenceList(t *testing.T) { assert.False(t, evl.Has(&DuplicateVoteEvidence{})) } -func TestMaxEvidenceBytes(t *testing.T) { - val := NewMockPV() - blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) - blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) - maxTime := time.Date(9999, 0, 0, 0, 0, 0, 0, time.UTC) - const chainID = "mychain" - ev := &DuplicateVoteEvidence{ - VoteA: makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, math.MaxInt64, blockID, maxTime), - VoteB: makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, math.MaxInt64, blockID2, maxTime), - } - - //TODO: Add other types of evidence to test and set MaxEvidenceBytes accordingly - - testCases := []struct { - testName string - evidence Evidence - }{ - {"DuplicateVote", ev}, - } - - for _, tt := range testCases { - pb, err := EvidenceToProto(tt.evidence) - require.NoError(t, err, tt.testName) - bz, err := pb.Marshal() - require.NoError(t, err, tt.testName) - - assert.LessOrEqual(t, int64(len(bz)), MaxEvidenceBytes, tt.testName) - } - -} - func randomDuplicateVoteEvidence(t *testing.T) *DuplicateVoteEvidence { val := NewMockPV() blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) diff --git a/types/params.go b/types/params.go index 1466faa60..16c85aa55 100644 --- a/types/params.go +++ b/types/params.go @@ -19,9 +19,6 @@ const ( // MaxBlockPartsCount is the maximum number of block parts. MaxBlockPartsCount = (MaxBlockSizeBytes / BlockPartSizeBytes) + 1 - - // Restrict the upper bound of the amount of evidence (uses uint16 for safe conversion) - MaxEvidencePerBlock = 65535 ) // DefaultConsensusParams returns a default ConsensusParams. @@ -48,7 +45,7 @@ func DefaultEvidenceParams() tmproto.EvidenceParams { return tmproto.EvidenceParams{ MaxAgeNumBlocks: 100000, // 27.8 hrs at 1block/s MaxAgeDuration: 48 * time.Hour, - MaxNum: 50, + MaxBytes: 1048576, // 1MB } } @@ -98,23 +95,23 @@ func ValidateConsensusParams(params tmproto.ConsensusParams) error { } if params.Evidence.MaxAgeNumBlocks <= 0 { - return fmt.Errorf("evidenceParams.MaxAgeNumBlocks must be greater than 0. Got %d", + return fmt.Errorf("evidence.MaxAgeNumBlocks must be greater than 0. Got %d", params.Evidence.MaxAgeNumBlocks) } if params.Evidence.MaxAgeDuration <= 0 { - return fmt.Errorf("evidenceParams.MaxAgeDuration must be grater than 0 if provided, Got %v", + return fmt.Errorf("evidence.MaxAgeDuration must be grater than 0 if provided, Got %v", params.Evidence.MaxAgeDuration) } - if params.Evidence.MaxNum > MaxEvidencePerBlock { - return fmt.Errorf("evidenceParams.MaxNumEvidence is greater than upper bound, %d > %d", - params.Evidence.MaxNum, MaxEvidencePerBlock) + if params.Evidence.MaxBytes > params.Block.MaxBytes { + return fmt.Errorf("evidence.MaxBytesEvidence is greater than upper bound, %d > %d", + params.Evidence.MaxBytes, params.Block.MaxBytes) } - if int64(params.Evidence.MaxNum)*MaxEvidenceBytes > params.Block.MaxBytes { - return fmt.Errorf("total possible evidence size is bigger than block.MaxBytes, %d > %d", - int64(params.Evidence.MaxNum)*MaxEvidenceBytes, params.Block.MaxBytes) + if params.Evidence.MaxBytes < 0 { + return fmt.Errorf("evidence.MaxBytes must be non negative. Got: %d", + params.Evidence.MaxBytes) } if len(params.Validator.PubKeyTypes) == 0 { @@ -174,7 +171,7 @@ func UpdateConsensusParams(params tmproto.ConsensusParams, params2 *abci.Consens if params2.Evidence != nil { res.Evidence.MaxAgeNumBlocks = params2.Evidence.MaxAgeNumBlocks res.Evidence.MaxAgeDuration = params2.Evidence.MaxAgeDuration - res.Evidence.MaxNum = params2.Evidence.MaxNum + res.Evidence.MaxBytes = params2.Evidence.MaxBytes } if params2.Validator != nil { // Copy params2.Validator.PubkeyTypes, and set result's value to the copy. diff --git a/types/params_test.go b/types/params_test.go index e266c2389..e6fbc4cad 100644 --- a/types/params_test.go +++ b/types/params_test.go @@ -33,7 +33,7 @@ func TestConsensusParamsValidation(t *testing.T) { 8: {makeParams(1, 0, -10, 2, 0, valEd25519), false}, // test evidence params 9: {makeParams(1, 0, 10, 0, 0, valEd25519), false}, - 10: {makeParams(1, 0, 10, 2, 1, valEd25519), false}, + 10: {makeParams(1, 0, 10, 2, 2, valEd25519), false}, 11: {makeParams(1000, 0, 10, 2, 1, valEd25519), true}, 12: {makeParams(1, 0, 10, -1, 0, valEd25519), false}, // test no pubkey type provided @@ -54,7 +54,7 @@ func makeParams( blockBytes, blockGas int64, blockTimeIotaMs int64, evidenceAge int64, - maxEvidence uint32, + maxEvidenceBytes int64, pubkeyTypes []string, ) tmproto.ConsensusParams { return tmproto.ConsensusParams{ @@ -66,7 +66,7 @@ func makeParams( Evidence: tmproto.EvidenceParams{ MaxAgeNumBlocks: evidenceAge, MaxAgeDuration: time.Duration(evidenceAge), - MaxNum: maxEvidence, + MaxBytes: maxEvidenceBytes, }, Validator: tmproto.ValidatorParams{ PubKeyTypes: pubkeyTypes, @@ -124,7 +124,7 @@ func TestConsensusParamsUpdate(t *testing.T) { Evidence: &tmproto.EvidenceParams{ MaxAgeNumBlocks: 300, MaxAgeDuration: time.Duration(300), - MaxNum: 50, + MaxBytes: 50, }, Validator: &tmproto.ValidatorParams{ PubKeyTypes: valEd25519, diff --git a/types/part_set.go b/types/part_set.go index b16fc583c..5e76b57fa 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -121,7 +121,7 @@ func (psh PartSetHeader) ValidateBasic() error { return nil } -// ToProto converts BloPartSetHeaderckID to protobuf +// ToProto converts PartSetHeader to protobuf func (psh *PartSetHeader) ToProto() tmproto.PartSetHeader { if psh == nil { return tmproto.PartSetHeader{}