diff --git a/consensus/wal.go b/consensus/wal.go index d6bc7fa7b..166c95d9e 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -20,6 +20,7 @@ import ( const ( // amino overhead + time.Time + max consensus msg size + // TODO: Can we clarify better where 24 comes from precisely? maxMsgSizeBytes = maxMsgSize + 24 // how often the WAL should be sync'd during period sync'ing diff --git a/crypto/merkle/simple_proof.go b/crypto/merkle/simple_proof.go index 93b6f9fea..79a4729eb 100644 --- a/crypto/merkle/simple_proof.go +++ b/crypto/merkle/simple_proof.go @@ -9,7 +9,10 @@ import ( ) const ( - maxAunts = 100 + // MaxAunts is the maximum number of aunts that can be included in a SimpleProof. + // This corresponds to a tree of size 2^100, which should be sufficient for all conceivable purposes. + // This maximum helps prevent Denial-of-Service attacks by limitting the size of the proofs. + MaxAunts = 100 ) // SimpleProof represents a simple Merkle proof. @@ -114,8 +117,8 @@ func (sp *SimpleProof) StringIndented(indent string) string { } // ValidateBasic performs basic validation. -// NOTE: - it expects LeafHash and Aunts of tmhash.Size size -// - it expects no more than 100 aunts +// NOTE: it expects the LeafHash and the elements of Aunts to be of size tmhash.Size, +// and it expects at most MaxAunts elements in Aunts. func (sp *SimpleProof) ValidateBasic() error { if sp.Total < 0 { return errors.New("negative Total") @@ -126,8 +129,8 @@ func (sp *SimpleProof) ValidateBasic() error { if len(sp.LeafHash) != tmhash.Size { return errors.Errorf("expected LeafHash size to be %d, got %d", tmhash.Size, len(sp.LeafHash)) } - if len(sp.Aunts) > maxAunts { - return errors.Errorf("expected no more than %d aunts, got %d", maxAunts, len(sp.Aunts)) + if len(sp.Aunts) > MaxAunts { + return errors.Errorf("expected no more than %d aunts, got %d", MaxAunts, len(sp.Aunts)) } for i, auntHash := range sp.Aunts { if len(auntHash) != tmhash.Size { diff --git a/crypto/merkle/simple_proof_test.go b/crypto/merkle/simple_proof_test.go index 1a517905b..68e6912fb 100644 --- a/crypto/merkle/simple_proof_test.go +++ b/crypto/merkle/simple_proof_test.go @@ -15,9 +15,12 @@ func TestSimpleProofValidateBasic(t *testing.T) { {"Good", func(sp *SimpleProof) {}, ""}, {"Negative Total", func(sp *SimpleProof) { sp.Total = -1 }, "negative Total"}, {"Negative Index", func(sp *SimpleProof) { sp.Index = -1 }, "negative Index"}, - {"Invalid LeafHash", func(sp *SimpleProof) { sp.LeafHash = make([]byte, 10) }, "expected LeafHash size to be 32, got 10"}, - {"Too many Aunts", func(sp *SimpleProof) { sp.Aunts = make([][]byte, maxAunts+1) }, "expected no more than 100 aunts, got 101"}, - {"Invalid Aunt", func(sp *SimpleProof) { sp.Aunts[0] = make([]byte, 10) }, "expected Aunts#0 size to be 32, got 10"}, + {"Invalid LeafHash", func(sp *SimpleProof) { sp.LeafHash = make([]byte, 10) }, + "expected LeafHash size to be 32, got 10"}, + {"Too many Aunts", func(sp *SimpleProof) { sp.Aunts = make([][]byte, MaxAunts+1) }, + "expected no more than 100 aunts, got 101"}, + {"Invalid Aunt", func(sp *SimpleProof) { sp.Aunts[0] = make([]byte, 10) }, + "expected Aunts#0 size to be 32, got 10"}, } for _, tc := range testCases { diff --git a/docs/spec/blockchain/blockchain.md b/docs/spec/blockchain/blockchain.md index cd31c5dc1..1683da006 100644 --- a/docs/spec/blockchain/blockchain.md +++ b/docs/spec/blockchain/blockchain.md @@ -434,6 +434,8 @@ All votes must have a valid signature from the corresponding validator. The sum total of the voting power of the validators that voted must be greater than 2/3 of the total voting power of the complete validator set. +The number of votes in a commit is limited to 10000 (see `types.MaxVotesCount`). + ### Vote A vote is a signed message broadcast in the consensus for a particular block at a particular height and round. diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index a171020b0..12586807e 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -155,7 +155,9 @@ See details of SimpleProof, below. ### MakeParts Encode an object using Amino and slice it into parts. -Tendermint uses a part size of 65536 bytes. +Tendermint uses a part size of 65536 bytes, and allows a maximum of 1601 parts +(see `types.MaxBlockPartsCount`). This corresponds to the hard-coded block size +limit of 100MB. ```go func MakeParts(block Block) []Part @@ -288,7 +290,9 @@ func computeHashFromAunts(index, total int, leafHash []byte, innerHashes [][]byt } ``` -The number of aunts is limited to 100 (`maxAunts`) to protect the node against DOS attacks. +The number of aunts is limited to 100 (`MaxAunts`) to protect the node against DOS attacks. +This limits the tree size to 2^100 leaves, which should be sufficient for any +conceivable purpose. ### IAVL+ Tree diff --git a/docs/spec/blockchain/state.md b/docs/spec/blockchain/state.md index 15fc37761..0430819fa 100644 --- a/docs/spec/blockchain/state.md +++ b/docs/spec/blockchain/state.md @@ -27,6 +27,9 @@ type State struct { } ``` +Note there is a hard-coded limit of 10000 validators. This is inherited from the +limit on the number of votes in a commit. + ### Result ```go diff --git a/types/params.go b/types/params.go index 61c1d2da6..9d2b1b9b4 100644 --- a/types/params.go +++ b/types/params.go @@ -15,7 +15,7 @@ const ( // BlockPartSizeBytes is the size of one block part. BlockPartSizeBytes = 65536 // 64kB - // MaxBlockPartsCount is the maximum count of block parts. + // MaxBlockPartsCount is the maximum number of block parts. MaxBlockPartsCount = (MaxBlockSizeBytes / BlockPartSizeBytes) + 1 ) diff --git a/types/validator_set.go b/types/validator_set.go index 343c7723f..eda3a0b22 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -52,6 +52,8 @@ type ValidatorSet struct { // the new ValidatorSet will have an empty list of Validators. // The addresses of validators in `valz` must be unique otherwise the // function panics. +// Note the validator set size has an implied limit equal to that of the MaxVotesCount - +// commits by a validator set larger than this will fail validation. func NewValidatorSet(valz []*Validator) *ValidatorSet { vals := &ValidatorSet{} err := vals.updateWithChangeSet(valz, false) diff --git a/types/vote_set.go b/types/vote_set.go index 028065f7c..9e42b8cde 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -12,8 +12,9 @@ import ( ) const ( - // MaxVotesCount is the maximum votes count. Used in ValidateBasic funcs for - // protection against DOS attacks. + // MaxVotesCount is the maximum number of votes in a set. Used in ValidateBasic funcs for + // protection against DOS attacks. Note this implies a corresponding equal limit to + // the number of validators. MaxVotesCount = 10000 )