mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-15 19:56:11 +00:00
evidence: cap evidence to an absolute number (#4780)
The number of evidence that can be committed in a single block is capped by a new evidence parameter called MaxNum
This commit is contained in:
@@ -29,6 +29,8 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
|||||||
- Blockchain Protocol
|
- Blockchain Protocol
|
||||||
|
|
||||||
- [types] [\#4792](https://github.com/tendermint/tendermint/pull/4792) Sort validators by voting power to enable faster commit verification (@melekes)
|
- [types] [\#4792](https://github.com/tendermint/tendermint/pull/4792) Sort validators by voting power to enable faster commit verification (@melekes)
|
||||||
|
- [evidence] [\#4780](https://github.com/tendermint/tendermint/pull/4780) Cap evidence to an absolute number (@cmwaters)
|
||||||
|
Add `max_num` to consensus evidence parameters (default: 50 items).
|
||||||
|
|
||||||
### FEATURES:
|
### FEATURES:
|
||||||
|
|
||||||
|
|||||||
@@ -2644,6 +2644,7 @@ type EvidenceParams struct {
|
|||||||
// Note: must be greater than 0
|
// Note: must be greater than 0
|
||||||
MaxAgeNumBlocks int64 `protobuf:"varint,1,opt,name=max_age_num_blocks,json=maxAgeNumBlocks,proto3" json:"max_age_num_blocks,omitempty"`
|
MaxAgeNumBlocks int64 `protobuf:"varint,1,opt,name=max_age_num_blocks,json=maxAgeNumBlocks,proto3" json:"max_age_num_blocks,omitempty"`
|
||||||
MaxAgeDuration time.Duration `protobuf:"bytes,2,opt,name=max_age_duration,json=maxAgeDuration,proto3,stdduration" json:"max_age_duration"`
|
MaxAgeDuration time.Duration `protobuf:"bytes,2,opt,name=max_age_duration,json=maxAgeDuration,proto3,stdduration" json:"max_age_duration"`
|
||||||
|
MaxNumEvidence uint32 `protobuf:"varint,1,opt,name=max_num_evidence,json=maxNumEvidence,proto3" json:"max_num_evidence,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ message EvidenceParams {
|
|||||||
int64 max_age_num_blocks = 1;
|
int64 max_age_num_blocks = 1;
|
||||||
google.protobuf.Duration max_age_duration = 2
|
google.protobuf.Duration max_age_duration = 2
|
||||||
[(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
|
[(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
|
||||||
|
uint32 max_num = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatorParams contains limits on validators.
|
// ValidatorParams contains limits on validators.
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ func newMockEvidencePool(val []byte) *mockEvidencePool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: maxBytes is ignored
|
// NOTE: maxBytes is ignored
|
||||||
func (m *mockEvidencePool) PendingEvidence(maxBytes int64) []types.Evidence {
|
func (m *mockEvidencePool) PendingEvidence(maxBytes uint32) []types.Evidence {
|
||||||
if m.height > 0 {
|
if m.height > 0 {
|
||||||
return m.ev
|
return m.ev
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ type emptyEvidencePool struct{}
|
|||||||
|
|
||||||
var _ sm.EvidencePool = emptyEvidencePool{}
|
var _ sm.EvidencePool = emptyEvidencePool{}
|
||||||
|
|
||||||
func (emptyEvidencePool) PendingEvidence(int64) []types.Evidence { return nil }
|
func (emptyEvidencePool) PendingEvidence(uint32) []types.Evidence { return nil }
|
||||||
func (emptyEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
func (emptyEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
||||||
func (emptyEvidencePool) Update(*types.Block, sm.State) {}
|
func (emptyEvidencePool) Update(*types.Block, sm.State) {}
|
||||||
func (emptyEvidencePool) IsCommitted(types.Evidence) bool { return false }
|
func (emptyEvidencePool) IsCommitted(types.Evidence) bool { return false }
|
||||||
func (emptyEvidencePool) IsPending(types.Evidence) bool { return false }
|
func (emptyEvidencePool) IsPending(types.Evidence) bool { return false }
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// mockProxyApp uses ABCIResponses to give the right results.
|
// mockProxyApp uses ABCIResponses to give the right results.
|
||||||
|
|||||||
+10
-3
@@ -82,8 +82,16 @@ func NewPool(stateDB, evidenceDB dbm.DB, blockStore *store.BlockStore) (*Pool, e
|
|||||||
|
|
||||||
// PendingEvidence is used primarily as part of block proposal and returns up to maxNum of uncommitted evidence.
|
// PendingEvidence is used primarily as part of block proposal and returns up to maxNum of uncommitted evidence.
|
||||||
// If maxNum is -1, all evidence is returned. Pending evidence is prioritised based on time.
|
// If maxNum is -1, all evidence is returned. Pending evidence is prioritised based on time.
|
||||||
func (evpool *Pool) PendingEvidence(maxNum int64) []types.Evidence {
|
func (evpool *Pool) PendingEvidence(maxNum uint32) []types.Evidence {
|
||||||
evidence, err := evpool.listEvidence(baseKeyPending, maxNum)
|
evidence, err := evpool.listEvidence(baseKeyPending, int64(maxNum))
|
||||||
|
if err != nil {
|
||||||
|
evpool.logger.Error("Unable to retrieve pending evidence", "err", err)
|
||||||
|
}
|
||||||
|
return evidence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (evpool *Pool) AllPendingEvidence() []types.Evidence {
|
||||||
|
evidence, err := evpool.listEvidence(baseKeyPending, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evpool.logger.Error("Unable to retrieve pending evidence", "err", err)
|
evpool.logger.Error("Unable to retrieve pending evidence", "err", err)
|
||||||
}
|
}
|
||||||
@@ -300,7 +308,6 @@ func (evpool *Pool) removePendingEvidence(evidence types.Evidence) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// listEvidence lists up to maxNum pieces of evidence for the given prefix key.
|
// listEvidence lists up to maxNum pieces of evidence for the given prefix key.
|
||||||
// It is wrapped by PriorityEvidence and PendingEvidence for convenience.
|
|
||||||
// If maxNum is -1, there's no cap on the size of returned evidence.
|
// 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) {
|
func (evpool *Pool) listEvidence(prefixKey byte, maxNum int64) ([]types.Evidence, error) {
|
||||||
var count int64
|
var count int64
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func TestProposingAndCommittingEvidence(t *testing.T) {
|
|||||||
assert.False(t, pool.IsCommitted(evidence))
|
assert.False(t, pool.IsCommitted(evidence))
|
||||||
|
|
||||||
// test evidence is proposed
|
// test evidence is proposed
|
||||||
proposedEvidence := pool.PendingEvidence(-1)
|
proposedEvidence := pool.AllPendingEvidence()
|
||||||
assert.Equal(t, proposedEvidence[0], evidence)
|
assert.Equal(t, proposedEvidence[0], evidence)
|
||||||
|
|
||||||
// evidence seen and committed:
|
// evidence seen and committed:
|
||||||
|
|||||||
@@ -92,11 +92,11 @@ func _waitForEvidence(
|
|||||||
reactors []*Reactor,
|
reactors []*Reactor,
|
||||||
) {
|
) {
|
||||||
evpool := reactors[reactorIdx].evpool
|
evpool := reactors[reactorIdx].evpool
|
||||||
for len(evpool.PendingEvidence(-1)) != len(evs) {
|
for len(evpool.AllPendingEvidence()) != len(evs) {
|
||||||
time.Sleep(time.Millisecond * 100)
|
time.Sleep(time.Millisecond * 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
reapedEv := evpool.PendingEvidence(-1)
|
reapedEv := evpool.AllPendingEvidence()
|
||||||
// put the reaped evidence in a map so we can quickly check we got everything
|
// put the reaped evidence in a map so we can quickly check we got everything
|
||||||
evMap := make(map[string]types.Evidence)
|
evMap := make(map[string]types.Evidence)
|
||||||
for _, e := range reapedEv {
|
for _, e := range reapedEv {
|
||||||
|
|||||||
+6
-4
@@ -219,6 +219,8 @@ func testFreeAddr(t *testing.T) string {
|
|||||||
// create a proposal block using real and full
|
// create a proposal block using real and full
|
||||||
// mempool and evidence pool and validate it.
|
// mempool and evidence pool and validate it.
|
||||||
func TestCreateProposalBlock(t *testing.T) {
|
func TestCreateProposalBlock(t *testing.T) {
|
||||||
|
const minEvSize = 12
|
||||||
|
|
||||||
config := cfg.ResetTestRoot("node_create_proposal")
|
config := cfg.ResetTestRoot("node_create_proposal")
|
||||||
defer os.RemoveAll(config.RootDir)
|
defer os.RemoveAll(config.RootDir)
|
||||||
cc := proxy.NewLocalClientCreator(kvstore.NewApplication())
|
cc := proxy.NewLocalClientCreator(kvstore.NewApplication())
|
||||||
@@ -232,7 +234,9 @@ func TestCreateProposalBlock(t *testing.T) {
|
|||||||
var height int64 = 1
|
var height int64 = 1
|
||||||
state, stateDB := state(1, height)
|
state, stateDB := state(1, height)
|
||||||
maxBytes := 16384
|
maxBytes := 16384
|
||||||
|
maxEvidence := 10
|
||||||
state.ConsensusParams.Block.MaxBytes = int64(maxBytes)
|
state.ConsensusParams.Block.MaxBytes = int64(maxBytes)
|
||||||
|
state.ConsensusParams.Evidence.MaxNum = uint32(maxEvidence)
|
||||||
proposerAddr, _ := state.Validators.GetByIndex(0)
|
proposerAddr, _ := state.Validators.GetByIndex(0)
|
||||||
|
|
||||||
// Make Mempool
|
// Make Mempool
|
||||||
@@ -258,10 +262,8 @@ func TestCreateProposalBlock(t *testing.T) {
|
|||||||
|
|
||||||
// fill the evidence pool with more evidence
|
// fill the evidence pool with more evidence
|
||||||
// than can fit in a block
|
// than can fit in a block
|
||||||
minEvSize := 12
|
for i := 0; i < maxEvidence+1; i++ {
|
||||||
numEv := (maxBytes / types.MaxEvidenceBytesDenominator) / minEvSize
|
ev := types.NewMockRandomEvidence(height, time.Now(), proposerAddr, tmrand.Bytes(minEvSize))
|
||||||
for i := 0; i < numEv; i++ {
|
|
||||||
ev := types.NewMockRandomEvidence(1, time.Now(), proposerAddr, tmrand.Bytes(minEvSize))
|
|
||||||
err := evidencePool.AddEvidence(ev)
|
err := evidencePool.AddEvidence(ev)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ type EvidenceParams struct {
|
|||||||
// Note: must be greater than 0
|
// Note: must be greater than 0
|
||||||
MaxAgeNumBlocks int64 `protobuf:"varint,1,opt,name=max_age_num_blocks,json=maxAgeNumBlocks,proto3" json:"max_age_num_blocks,omitempty"`
|
MaxAgeNumBlocks int64 `protobuf:"varint,1,opt,name=max_age_num_blocks,json=maxAgeNumBlocks,proto3" json:"max_age_num_blocks,omitempty"`
|
||||||
MaxAgeDuration time.Duration `protobuf:"bytes,2,opt,name=max_age_duration,json=maxAgeDuration,proto3,stdduration" json:"max_age_duration"`
|
MaxAgeDuration time.Duration `protobuf:"bytes,2,opt,name=max_age_duration,json=maxAgeDuration,proto3,stdduration" json:"max_age_duration"`
|
||||||
|
MaxNumEvidence uint32 `protobuf:"varint,1,opt,name=max_num_evidence,json=maxNumEvidence,proto3" json:"max_num_evidence,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@@ -189,6 +190,13 @@ func (m *EvidenceParams) GetMaxAgeDuration() time.Duration {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *EvidenceParams) GetMaxNumEvidence() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.MaxNumEvidence
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// ValidatorParams restrict the public key types validators can use.
|
// ValidatorParams restrict the public key types validators can use.
|
||||||
// NOTE: uses ABCI pubkey naming, not Amino names.
|
// NOTE: uses ABCI pubkey naming, not Amino names.
|
||||||
type ValidatorParams struct {
|
type ValidatorParams struct {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ message EvidenceParams {
|
|||||||
int64 max_age_num_blocks = 1;
|
int64 max_age_num_blocks = 1;
|
||||||
google.protobuf.Duration max_age_duration = 2
|
google.protobuf.Duration max_age_duration = 2
|
||||||
[(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
|
[(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
|
||||||
|
uint32 max_num = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatorParams restrict the public key types validators can use.
|
// ValidatorParams restrict the public key types validators can use.
|
||||||
|
|||||||
+1
-3
@@ -98,9 +98,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
|
|||||||
maxBytes := state.ConsensusParams.Block.MaxBytes
|
maxBytes := state.ConsensusParams.Block.MaxBytes
|
||||||
maxGas := state.ConsensusParams.Block.MaxGas
|
maxGas := state.ConsensusParams.Block.MaxGas
|
||||||
|
|
||||||
// Fetch a limited amount of valid evidence
|
evidence := blockExec.evpool.PendingEvidence(state.ConsensusParams.Evidence.MaxNum)
|
||||||
maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBytes)
|
|
||||||
evidence := blockExec.evpool.PendingEvidence(maxNumEvidence)
|
|
||||||
|
|
||||||
// Fetch a limited amount of valid txs
|
// Fetch a limited amount of valid txs
|
||||||
maxDataBytes := types.MaxDataBytes(maxBytes, state.Validators.Size(), len(evidence))
|
maxDataBytes := types.MaxDataBytes(maxBytes, state.Validators.Size(), len(evidence))
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ func (_m *EvidencePool) IsPending(_a0 types.Evidence) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PendingEvidence provides a mock function with given fields: _a0
|
// PendingEvidence provides a mock function with given fields: _a0
|
||||||
func (_m *EvidencePool) PendingEvidence(_a0 int64) []types.Evidence {
|
func (_m *EvidencePool) PendingEvidence(_a0 uint32) []types.Evidence {
|
||||||
ret := _m.Called(_a0)
|
ret := _m.Called(_a0)
|
||||||
|
|
||||||
var r0 []types.Evidence
|
var r0 []types.Evidence
|
||||||
if rf, ok := ret.Get(0).(func(int64) []types.Evidence); ok {
|
if rf, ok := ret.Get(0).(func(uint32) []types.Evidence); ok {
|
||||||
r0 = rf(_a0)
|
r0 = rf(_a0)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
|
|||||||
+7
-6
@@ -40,17 +40,18 @@ type BlockStore interface {
|
|||||||
// EvidencePool defines the EvidencePool interface used by the ConsensusState.
|
// EvidencePool defines the EvidencePool interface used by the ConsensusState.
|
||||||
// Get/Set/Commit
|
// Get/Set/Commit
|
||||||
type EvidencePool interface {
|
type EvidencePool interface {
|
||||||
PendingEvidence(int64) []types.Evidence
|
PendingEvidence(uint32) []types.Evidence
|
||||||
AddEvidence(types.Evidence) error
|
AddEvidence(types.Evidence) error
|
||||||
Update(*types.Block, State)
|
Update(*types.Block, State)
|
||||||
IsCommitted(types.Evidence) bool
|
IsCommitted(types.Evidence) bool
|
||||||
IsPending(types.Evidence) bool
|
IsPending(types.Evidence) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MockEvidencePool is an empty implementation of EvidencePool, useful for testing.
|
||||||
type MockEvidencePool struct{}
|
type MockEvidencePool struct{}
|
||||||
|
|
||||||
func (me MockEvidencePool) PendingEvidence(int64) []types.Evidence { return nil }
|
func (me MockEvidencePool) PendingEvidence(uint32) []types.Evidence { return nil }
|
||||||
func (me MockEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
func (me MockEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
||||||
func (me MockEvidencePool) Update(*types.Block, State) {}
|
func (me MockEvidencePool) Update(*types.Block, State) {}
|
||||||
func (me MockEvidencePool) IsCommitted(types.Evidence) bool { return false }
|
func (me MockEvidencePool) IsCommitted(types.Evidence) bool { return false }
|
||||||
func (me MockEvidencePool) IsPending(types.Evidence) bool { return false }
|
func (me MockEvidencePool) IsPending(types.Evidence) bool { return false }
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ func TxPreCheck(state State) mempl.PreCheckFunc {
|
|||||||
maxDataBytes := types.MaxDataBytesUnknownEvidence(
|
maxDataBytes := types.MaxDataBytesUnknownEvidence(
|
||||||
state.ConsensusParams.Block.MaxBytes,
|
state.ConsensusParams.Block.MaxBytes,
|
||||||
state.Validators.Size(),
|
state.Validators.Size(),
|
||||||
|
state.ConsensusParams.Evidence.MaxNum,
|
||||||
)
|
)
|
||||||
return mempl.PreCheckAminoMaxBytes(maxDataBytes)
|
return mempl.PreCheckAminoMaxBytes(maxDataBytes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
func TestTxFilter(t *testing.T) {
|
func TestTxFilter(t *testing.T) {
|
||||||
genDoc := randomGenesisDoc()
|
genDoc := randomGenesisDoc()
|
||||||
genDoc.ConsensusParams.Block.MaxBytes = 3000
|
genDoc.ConsensusParams.Block.MaxBytes = 3000
|
||||||
|
genDoc.ConsensusParams.Evidence.MaxNum = 1
|
||||||
|
|
||||||
// Max size of Txs is much smaller than size of block,
|
// Max size of Txs is much smaller than size of block,
|
||||||
// since we need to account for commits and evidence.
|
// since we need to account for commits and evidence.
|
||||||
@@ -24,10 +25,7 @@ func TestTxFilter(t *testing.T) {
|
|||||||
tx types.Tx
|
tx types.Tx
|
||||||
isErr bool
|
isErr bool
|
||||||
}{
|
}{
|
||||||
{types.Tx(tmrand.Bytes(250)), false},
|
{types.Tx(tmrand.Bytes(1680)), false},
|
||||||
{types.Tx(tmrand.Bytes(1811)), false},
|
|
||||||
{types.Tx(tmrand.Bytes(1831)), false},
|
|
||||||
{types.Tx(tmrand.Bytes(1838)), true},
|
|
||||||
{types.Tx(tmrand.Bytes(1839)), true},
|
{types.Tx(tmrand.Bytes(1839)), true},
|
||||||
{types.Tx(tmrand.Bytes(3000)), true},
|
{types.Tx(tmrand.Bytes(3000)), true},
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -120,11 +120,10 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Limit the amount of evidence
|
// Limit the amount of evidence
|
||||||
maxNumEvidence, _ := types.MaxEvidencePerBlock(state.ConsensusParams.Block.MaxBytes)
|
numEvidence := len(block.Evidence.Evidence)
|
||||||
numEvidence := int64(len(block.Evidence.Evidence))
|
// MaxNumEvidence is capped at uint16, so conversion is always safe.
|
||||||
if numEvidence > maxNumEvidence {
|
if maxEvidence := int(state.ConsensusParams.Evidence.MaxNum); numEvidence > maxEvidence {
|
||||||
return types.NewErrEvidenceOverflow(maxNumEvidence, numEvidence)
|
return types.NewErrEvidenceOverflow(maxEvidence, numEvidence)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate all evidence.
|
// Validate all evidence.
|
||||||
|
|||||||
@@ -216,16 +216,15 @@ func TestValidateBlockEvidence(t *testing.T) {
|
|||||||
for height := int64(1); height < validationTestsStopHeight; height++ {
|
for height := int64(1); height < validationTestsStopHeight; height++ {
|
||||||
proposerAddr := state.Validators.GetProposer().Address
|
proposerAddr := state.Validators.GetProposer().Address
|
||||||
goodEvidence := types.NewMockEvidence(height, time.Now(), proposerAddr)
|
goodEvidence := types.NewMockEvidence(height, time.Now(), proposerAddr)
|
||||||
|
maxNumEvidence := state.ConsensusParams.Evidence.MaxNum
|
||||||
if height > 1 {
|
if height > 1 {
|
||||||
/*
|
/*
|
||||||
A block with too much evidence fails
|
A block with too much evidence fails
|
||||||
*/
|
*/
|
||||||
maxBlockSize := state.ConsensusParams.Block.MaxBytes
|
|
||||||
maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize)
|
|
||||||
require.True(t, maxNumEvidence > 2)
|
require.True(t, maxNumEvidence > 2)
|
||||||
evidence := make([]types.Evidence, 0)
|
evidence := make([]types.Evidence, 0)
|
||||||
// one more than the maximum allowed evidence
|
// one more than the maximum allowed evidence
|
||||||
for i := int64(0); i <= maxNumEvidence; i++ {
|
for i := uint32(0); i <= maxNumEvidence; i++ {
|
||||||
evidence = append(evidence, goodEvidence)
|
evidence = append(evidence, goodEvidence)
|
||||||
}
|
}
|
||||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr)
|
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr)
|
||||||
@@ -237,12 +236,10 @@ func TestValidateBlockEvidence(t *testing.T) {
|
|||||||
/*
|
/*
|
||||||
A good block with several pieces of good evidence passes
|
A good block with several pieces of good evidence passes
|
||||||
*/
|
*/
|
||||||
maxBlockSize := state.ConsensusParams.Block.MaxBytes
|
|
||||||
maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize)
|
|
||||||
require.True(t, maxNumEvidence > 2)
|
require.True(t, maxNumEvidence > 2)
|
||||||
evidence := make([]types.Evidence, 0)
|
evidence := make([]types.Evidence, 0)
|
||||||
// precisely the amount of allowed evidence
|
// precisely the amount of allowed evidence
|
||||||
for i := int64(0); i < maxNumEvidence; i++ {
|
for i := uint32(0); i < maxNumEvidence; i++ {
|
||||||
evidence = append(evidence, goodEvidence)
|
evidence = append(evidence, goodEvidence)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ const (
|
|||||||
},
|
},
|
||||||
"evidence": {
|
"evidence": {
|
||||||
"max_age_num_blocks": "100000",
|
"max_age_num_blocks": "100000",
|
||||||
"max_age_duration": "172800000000000"
|
"max_age_duration": "172800000000000",
|
||||||
|
"max_num_evidence": 50
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"pub_key_types": [
|
"pub_key_types": [
|
||||||
|
|||||||
+2
-2
@@ -253,8 +253,8 @@ func MaxDataBytes(maxBytes int64, valsCount, evidenceCount int) int64 {
|
|||||||
// of evidence.
|
// of evidence.
|
||||||
//
|
//
|
||||||
// XXX: Panics on negative result.
|
// XXX: Panics on negative result.
|
||||||
func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int) int64 {
|
func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int, maxNumEvidence uint32) int64 {
|
||||||
_, maxEvidenceBytes := MaxEvidencePerBlock(maxBytes)
|
maxEvidenceBytes := int64(maxNumEvidence) * MaxEvidenceBytes
|
||||||
maxDataBytes := maxBytes -
|
maxDataBytes := maxBytes -
|
||||||
MaxAminoOverheadForBlock -
|
MaxAminoOverheadForBlock -
|
||||||
MaxHeaderBytes -
|
MaxHeaderBytes -
|
||||||
|
|||||||
+13
-11
@@ -399,28 +399,30 @@ func TestBlockMaxDataBytes(t *testing.T) {
|
|||||||
|
|
||||||
func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) {
|
func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
maxBytes int64
|
maxBytes int64
|
||||||
valsCount int
|
maxEvidence uint32
|
||||||
panics bool
|
valsCount int
|
||||||
result int64
|
panics bool
|
||||||
|
result int64
|
||||||
}{
|
}{
|
||||||
0: {-10, 1, true, 0},
|
0: {-10, 0, 1, true, 0},
|
||||||
1: {10, 1, true, 0},
|
1: {10, 0, 1, true, 0},
|
||||||
2: {961, 1, true, 0},
|
2: {865, 0, 1, true, 0},
|
||||||
3: {962, 1, false, 0},
|
3: {866, 0, 1, false, 0},
|
||||||
4: {963, 1, false, 1},
|
4: {1310, 1, 1, false, 0},
|
||||||
|
5: {1311, 1, 1, false, 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
tc := tc
|
tc := tc
|
||||||
if tc.panics {
|
if tc.panics {
|
||||||
assert.Panics(t, func() {
|
assert.Panics(t, func() {
|
||||||
MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount)
|
MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount, tc.maxEvidence)
|
||||||
}, "#%v", i)
|
}, "#%v", i)
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
tc.result,
|
tc.result,
|
||||||
MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount),
|
MaxDataBytesUnknownEvidence(tc.maxBytes, tc.valsCount, tc.maxEvidence),
|
||||||
"#%v", i)
|
"#%v", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-18
@@ -48,12 +48,12 @@ func (err *ErrEvidenceInvalid) Error() string {
|
|||||||
|
|
||||||
// ErrEvidenceOverflow is for when there is too much evidence in a block.
|
// ErrEvidenceOverflow is for when there is too much evidence in a block.
|
||||||
type ErrEvidenceOverflow struct {
|
type ErrEvidenceOverflow struct {
|
||||||
MaxNum int64
|
MaxNum int
|
||||||
GotNum int64
|
GotNum int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewErrEvidenceOverflow returns a new ErrEvidenceOverflow where got > max.
|
// NewErrEvidenceOverflow returns a new ErrEvidenceOverflow where got > max.
|
||||||
func NewErrEvidenceOverflow(max, got int64) *ErrEvidenceOverflow {
|
func NewErrEvidenceOverflow(max, got int) *ErrEvidenceOverflow {
|
||||||
return &ErrEvidenceOverflow{max, got}
|
return &ErrEvidenceOverflow{max, got}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,21 +97,6 @@ func RegisterMockEvidences(cdc *amino.Codec) {
|
|||||||
cdc.RegisterConcrete(MockRandomEvidence{}, "tendermint/MockRandomEvidence", nil)
|
cdc.RegisterConcrete(MockRandomEvidence{}, "tendermint/MockRandomEvidence", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
MaxEvidenceBytesDenominator = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
// MaxEvidencePerBlock returns the maximum number of evidences
|
|
||||||
// allowed in the block and their maximum total size (limitted to 1/10th
|
|
||||||
// of the maximum block size).
|
|
||||||
// TODO: change to a constant, or to a fraction of the validator set size.
|
|
||||||
// See https://github.com/tendermint/tendermint/issues/2590
|
|
||||||
func MaxEvidencePerBlock(blockMaxBytes int64) (int64, int64) {
|
|
||||||
maxBytes := blockMaxBytes / MaxEvidenceBytesDenominator
|
|
||||||
maxNum := maxBytes / MaxEvidenceBytes
|
|
||||||
return maxNum, maxBytes
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------
|
//-------------------------------------------
|
||||||
|
|
||||||
// DuplicateVoteEvidence contains evidence a validator signed two conflicting
|
// DuplicateVoteEvidence contains evidence a validator signed two conflicting
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ const (
|
|||||||
|
|
||||||
// MaxBlockPartsCount is the maximum number of block parts.
|
// MaxBlockPartsCount is the maximum number of block parts.
|
||||||
MaxBlockPartsCount = (MaxBlockSizeBytes / BlockPartSizeBytes) + 1
|
MaxBlockPartsCount = (MaxBlockSizeBytes / BlockPartSizeBytes) + 1
|
||||||
|
|
||||||
|
// Restrict the upper bound of the amount of evidence (uses uint16 for safe conversion)
|
||||||
|
MaxEvidencePerBlock = 65535
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConsensusParams contains consensus critical parameters that determine the
|
// ConsensusParams contains consensus critical parameters that determine the
|
||||||
@@ -67,6 +70,12 @@ type EvidenceParams struct {
|
|||||||
// mechanism for handling [Nothing-At-Stake
|
// 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).
|
// 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 `json:"max_age_duration"`
|
MaxAgeDuration time.Duration `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 `json:"max_num"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatorParams restrict the public key types validators can use.
|
// ValidatorParams restrict the public key types validators can use.
|
||||||
@@ -98,6 +107,7 @@ func DefaultEvidenceParams() EvidenceParams {
|
|||||||
return EvidenceParams{
|
return EvidenceParams{
|
||||||
MaxAgeNumBlocks: 100000, // 27.8 hrs at 1block/s
|
MaxAgeNumBlocks: 100000, // 27.8 hrs at 1block/s
|
||||||
MaxAgeDuration: 48 * time.Hour,
|
MaxAgeDuration: 48 * time.Hour,
|
||||||
|
MaxNum: 50,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +158,16 @@ func (params *ConsensusParams) Validate() error {
|
|||||||
params.Evidence.MaxAgeDuration)
|
params.Evidence.MaxAgeDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.Evidence.MaxNum > MaxEvidencePerBlock {
|
||||||
|
return errors.Errorf("evidenceParams.MaxNumEvidence is greater than upper bound, %d > %d",
|
||||||
|
params.Evidence.MaxNum, MaxEvidencePerBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
if int64(params.Evidence.MaxNum)*MaxEvidenceBytes > params.Block.MaxBytes {
|
||||||
|
return errors.Errorf("total possible evidence size is bigger than block.MaxBytes, %d > %d",
|
||||||
|
int64(params.Evidence.MaxNum)*MaxEvidenceBytes, params.Block.MaxBytes)
|
||||||
|
}
|
||||||
|
|
||||||
if len(params.Validator.PubKeyTypes) == 0 {
|
if len(params.Validator.PubKeyTypes) == 0 {
|
||||||
return errors.New("len(Validator.PubKeyTypes) must be greater than 0")
|
return errors.New("len(Validator.PubKeyTypes) must be greater than 0")
|
||||||
}
|
}
|
||||||
@@ -204,6 +224,7 @@ func (params ConsensusParams) Update(params2 *abci.ConsensusParams) ConsensusPar
|
|||||||
if params2.Evidence != nil {
|
if params2.Evidence != nil {
|
||||||
res.Evidence.MaxAgeNumBlocks = params2.Evidence.MaxAgeNumBlocks
|
res.Evidence.MaxAgeNumBlocks = params2.Evidence.MaxAgeNumBlocks
|
||||||
res.Evidence.MaxAgeDuration = params2.Evidence.MaxAgeDuration
|
res.Evidence.MaxAgeDuration = params2.Evidence.MaxAgeDuration
|
||||||
|
res.Evidence.MaxNum = params2.Evidence.MaxNumEvidence
|
||||||
}
|
}
|
||||||
if params2.Validator != nil {
|
if params2.Validator != nil {
|
||||||
// Copy params2.Validator.PubkeyTypes, and set result's value to the copy.
|
// Copy params2.Validator.PubkeyTypes, and set result's value to the copy.
|
||||||
|
|||||||
+30
-25
@@ -22,22 +22,24 @@ func TestConsensusParamsValidation(t *testing.T) {
|
|||||||
valid bool
|
valid bool
|
||||||
}{
|
}{
|
||||||
// test block params
|
// test block params
|
||||||
0: {makeParams(1, 0, 10, 1, valEd25519), true},
|
0: {makeParams(1, 0, 10, 1, 0, valEd25519), true},
|
||||||
1: {makeParams(0, 0, 10, 1, valEd25519), false},
|
1: {makeParams(0, 0, 10, 1, 0, valEd25519), false},
|
||||||
2: {makeParams(47*1024*1024, 0, 10, 1, valEd25519), true},
|
2: {makeParams(47*1024*1024, 0, 10, 1, 0, valEd25519), true},
|
||||||
3: {makeParams(10, 0, 10, 1, valEd25519), true},
|
3: {makeParams(10, 0, 10, 1, 0, valEd25519), true},
|
||||||
4: {makeParams(100*1024*1024, 0, 10, 1, valEd25519), true},
|
4: {makeParams(100*1024*1024, 0, 10, 1, 0, valEd25519), true},
|
||||||
5: {makeParams(101*1024*1024, 0, 10, 1, valEd25519), false},
|
5: {makeParams(101*1024*1024, 0, 10, 1, 0, valEd25519), false},
|
||||||
6: {makeParams(1024*1024*1024, 0, 10, 1, valEd25519), false},
|
6: {makeParams(1024*1024*1024, 0, 10, 1, 0, valEd25519), false},
|
||||||
7: {makeParams(1024*1024*1024, 0, 10, -1, valEd25519), false},
|
7: {makeParams(1024*1024*1024, 0, 10, -1, 0, valEd25519), false},
|
||||||
8: {makeParams(1, 0, -10, 1, valEd25519), false},
|
8: {makeParams(1, 0, -10, 1, 0, valEd25519), false},
|
||||||
// test evidence params
|
// test evidence params
|
||||||
9: {makeParams(1, 0, 10, 0, valEd25519), false},
|
9: {makeParams(1, 0, 10, 0, 0, valEd25519), false},
|
||||||
10: {makeParams(1, 0, 10, -1, valEd25519), false},
|
10: {makeParams(1, 0, 10, 1, 1, valEd25519), false},
|
||||||
|
11: {makeParams(1000, 0, 10, 1, 1, valEd25519), true},
|
||||||
|
12: {makeParams(1, 0, 10, -1, 0, valEd25519), false},
|
||||||
// test no pubkey type provided
|
// test no pubkey type provided
|
||||||
11: {makeParams(1, 0, 10, 1, []string{}), false},
|
13: {makeParams(1, 0, 10, 1, 0, []string{}), false},
|
||||||
// test invalid pubkey type provided
|
// test invalid pubkey type provided
|
||||||
12: {makeParams(1, 0, 10, 1, []string{"potatoes make good pubkeys"}), false},
|
14: {makeParams(1, 0, 10, 1, 0, []string{"potatoes make good pubkeys"}), false},
|
||||||
}
|
}
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
@@ -52,6 +54,7 @@ func makeParams(
|
|||||||
blockBytes, blockGas int64,
|
blockBytes, blockGas int64,
|
||||||
blockTimeIotaMs int64,
|
blockTimeIotaMs int64,
|
||||||
evidenceAge int64,
|
evidenceAge int64,
|
||||||
|
maxEvidence uint32,
|
||||||
pubkeyTypes []string,
|
pubkeyTypes []string,
|
||||||
) ConsensusParams {
|
) ConsensusParams {
|
||||||
return ConsensusParams{
|
return ConsensusParams{
|
||||||
@@ -63,6 +66,7 @@ func makeParams(
|
|||||||
Evidence: EvidenceParams{
|
Evidence: EvidenceParams{
|
||||||
MaxAgeNumBlocks: evidenceAge,
|
MaxAgeNumBlocks: evidenceAge,
|
||||||
MaxAgeDuration: time.Duration(evidenceAge),
|
MaxAgeDuration: time.Duration(evidenceAge),
|
||||||
|
MaxNum: maxEvidence,
|
||||||
},
|
},
|
||||||
Validator: ValidatorParams{
|
Validator: ValidatorParams{
|
||||||
PubKeyTypes: pubkeyTypes,
|
PubKeyTypes: pubkeyTypes,
|
||||||
@@ -72,14 +76,14 @@ func makeParams(
|
|||||||
|
|
||||||
func TestConsensusParamsHash(t *testing.T) {
|
func TestConsensusParamsHash(t *testing.T) {
|
||||||
params := []ConsensusParams{
|
params := []ConsensusParams{
|
||||||
makeParams(4, 2, 10, 3, valEd25519),
|
makeParams(4, 2, 10, 3, 1, valEd25519),
|
||||||
makeParams(1, 4, 10, 3, valEd25519),
|
makeParams(1, 4, 10, 3, 1, valEd25519),
|
||||||
makeParams(1, 2, 10, 4, valEd25519),
|
makeParams(1, 2, 10, 4, 1, valEd25519),
|
||||||
makeParams(2, 5, 10, 7, valEd25519),
|
makeParams(2, 5, 10, 7, 1, valEd25519),
|
||||||
makeParams(1, 7, 10, 6, valEd25519),
|
makeParams(1, 7, 10, 6, 1, valEd25519),
|
||||||
makeParams(9, 5, 10, 4, valEd25519),
|
makeParams(9, 5, 10, 4, 1, valEd25519),
|
||||||
makeParams(7, 8, 10, 9, valEd25519),
|
makeParams(7, 8, 10, 9, 1, valEd25519),
|
||||||
makeParams(4, 6, 10, 5, valEd25519),
|
makeParams(4, 6, 10, 5, 1, valEd25519),
|
||||||
}
|
}
|
||||||
|
|
||||||
hashes := make([][]byte, len(params))
|
hashes := make([][]byte, len(params))
|
||||||
@@ -105,13 +109,13 @@ func TestConsensusParamsUpdate(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
// empty updates
|
// empty updates
|
||||||
{
|
{
|
||||||
makeParams(1, 2, 10, 3, valEd25519),
|
makeParams(1, 2, 10, 3, 0, valEd25519),
|
||||||
&abci.ConsensusParams{},
|
&abci.ConsensusParams{},
|
||||||
makeParams(1, 2, 10, 3, valEd25519),
|
makeParams(1, 2, 10, 3, 0, valEd25519),
|
||||||
},
|
},
|
||||||
// fine updates
|
// fine updates
|
||||||
{
|
{
|
||||||
makeParams(1, 2, 10, 3, valEd25519),
|
makeParams(1, 2, 10, 3, 0, valEd25519),
|
||||||
&abci.ConsensusParams{
|
&abci.ConsensusParams{
|
||||||
Block: &abci.BlockParams{
|
Block: &abci.BlockParams{
|
||||||
MaxBytes: 100,
|
MaxBytes: 100,
|
||||||
@@ -120,12 +124,13 @@ func TestConsensusParamsUpdate(t *testing.T) {
|
|||||||
Evidence: &abci.EvidenceParams{
|
Evidence: &abci.EvidenceParams{
|
||||||
MaxAgeNumBlocks: 300,
|
MaxAgeNumBlocks: 300,
|
||||||
MaxAgeDuration: time.Duration(300),
|
MaxAgeDuration: time.Duration(300),
|
||||||
|
MaxNumEvidence: 50,
|
||||||
},
|
},
|
||||||
Validator: &abci.ValidatorParams{
|
Validator: &abci.ValidatorParams{
|
||||||
PubKeyTypes: valSecp256k1,
|
PubKeyTypes: valSecp256k1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
makeParams(100, 200, 10, 300, valSecp256k1),
|
makeParams(100, 200, 10, 300, 50, valSecp256k1),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams {
|
|||||||
Evidence: &abci.EvidenceParams{
|
Evidence: &abci.EvidenceParams{
|
||||||
MaxAgeNumBlocks: params.Evidence.MaxAgeNumBlocks,
|
MaxAgeNumBlocks: params.Evidence.MaxAgeNumBlocks,
|
||||||
MaxAgeDuration: params.Evidence.MaxAgeDuration,
|
MaxAgeDuration: params.Evidence.MaxAgeDuration,
|
||||||
|
MaxNumEvidence: params.Evidence.MaxNum,
|
||||||
},
|
},
|
||||||
Validator: &abci.ValidatorParams{
|
Validator: &abci.ValidatorParams{
|
||||||
PubKeyTypes: params.Validator.PubKeyTypes,
|
PubKeyTypes: params.Validator.PubKeyTypes,
|
||||||
|
|||||||
Reference in New Issue
Block a user