diff --git a/node/node_test.go b/node/node_test.go index 67153c4b0..f25e6243d 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -241,7 +241,7 @@ func TestCreateProposalBlock(t *testing.T) { proposerAddr, _ := state.Validators.GetByIndex(0) // Make Mempool - memplMetrics := mempl.PrometheusMetrics("node_test") + memplMetrics := mempl.PrometheusMetrics("node_test_1") mempool := mempl.NewCListMempool( config.Mempool, proxyApp.Mempool(), @@ -309,6 +309,67 @@ func TestCreateProposalBlock(t *testing.T) { assert.NoError(t, err) } +func TestMaxProposalBlockSize(t *testing.T) { + config := cfg.ResetTestRoot("node_create_proposal") + defer os.RemoveAll(config.RootDir) + cc := proxy.NewLocalClientCreator(kvstore.NewApplication()) + proxyApp := proxy.NewAppConns(cc) + err := proxyApp.Start() + require.Nil(t, err) + defer proxyApp.Stop() //nolint:errcheck // ignore for tests + + logger := log.TestingLogger() + + var height int64 = 1 + state, stateDB, _ := state(1, height) + stateStore := sm.NewStore(stateDB) + var maxBytes int64 = 16384 + var partSize uint32 = 256 + state.ConsensusParams.Block.MaxBytes = maxBytes + proposerAddr, _ := state.Validators.GetByIndex(0) + + // Make Mempool + memplMetrics := mempl.PrometheusMetrics("node_test_2") + mempool := mempl.NewCListMempool( + config.Mempool, + proxyApp.Mempool(), + state.LastBlockHeight, + mempl.WithMetrics(memplMetrics), + mempl.WithPreCheck(sm.TxPreCheck(state)), + mempl.WithPostCheck(sm.TxPostCheck(state)), + ) + mempool.SetLogger(logger) + + // fill the mempool with one txs just below the maximum size + txLength := int(types.MaxDataBytesNoEvidence(maxBytes, 1)) + tx := tmrand.Bytes(txLength - 4) // to account for the varint + err = mempool.CheckTx(tx, nil, mempl.TxInfo{}) + assert.NoError(t, err) + + blockExec := sm.NewBlockExecutor( + stateStore, + logger, + proxyApp.Consensus(), + mempool, + sm.EmptyEvidencePool{}, + ) + + commit := types.NewCommit(height-1, 0, types.BlockID{}, nil) + block, _ := blockExec.CreateProposalBlock( + height, + state, commit, + proposerAddr, + ) + + 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) + assert.EqualValues(t, partSet.ByteSize(), int64(pb.Size())) +} + func TestNodeNewNodeCustomReactors(t *testing.T) { config := cfg.ResetTestRoot("node_new_node_custom_reactors_test") defer os.RemoveAll(config.RootDir) diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index aff8247c0..c1bbfd346 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -25,8 +25,8 @@ func TestTxFilter(t *testing.T) { tx types.Tx isErr bool }{ - {types.Tx(tmrand.Bytes(2151)), false}, - {types.Tx(tmrand.Bytes(2152)), true}, + {types.Tx(tmrand.Bytes(2187)), false}, + {types.Tx(tmrand.Bytes(2188)), true}, {types.Tx(tmrand.Bytes(3000)), true}, } diff --git a/types/block.go b/types/block.go index 3590db2a9..01ef9b43b 100644 --- a/types/block.go +++ b/types/block.go @@ -267,7 +267,7 @@ func MaxDataBytes(maxBytes, evidenceBytes int64, valsCount int) int64 { maxDataBytes := maxBytes - MaxOverheadForBlock - MaxHeaderBytes - - int64(valsCount)*MaxVoteBytes - + MaxCommitBytes(valsCount) - evidenceBytes if maxDataBytes < 0 { @@ -290,7 +290,7 @@ func MaxDataBytesNoEvidence(maxBytes int64, valsCount int) int64 { maxDataBytes := maxBytes - MaxOverheadForBlock - MaxHeaderBytes - - int64(valsCount)*MaxVoteBytes + MaxCommitBytes(valsCount) if maxDataBytes < 0 { panic(fmt.Sprintf( @@ -570,6 +570,14 @@ const ( BlockIDFlagNil ) +const ( + // Max size of commit without any commitSigs -> 82 for BlockID, 8 for Height, 4 for Round. + MaxCommitOverheadBytes int64 = 94 + // Commit sig size is made up of 32 bytes for the signature, 20 bytes for the address, + // 1 byte for the flag and 14 bytes for the timestamp + MaxCommitSigBytes int64 = 77 +) + // CommitSig is a part of the Vote included in a Commit. type CommitSig struct { BlockIDFlag BlockIDFlag `json:"block_id_flag"` @@ -588,9 +596,10 @@ func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time) Commi } } -// ForBlock returns true if CommitSig is for the block. -func (cs CommitSig) ForBlock() bool { - return cs.BlockIDFlag == BlockIDFlagCommit +func MaxCommitBytes(valCount int) int64 { + // From the repeated commit sig field + var protoEncodingOverhead int64 = 2 + return MaxCommitOverheadBytes + ((MaxCommitSigBytes + protoEncodingOverhead) * int64(valCount)) } // NewCommitSigAbsent returns new CommitSig with BlockIDFlagAbsent. Other @@ -601,6 +610,11 @@ func NewCommitSigAbsent() CommitSig { } } +// ForBlock returns true if CommitSig is for the block. +func (cs CommitSig) ForBlock() bool { + return cs.BlockIDFlag == BlockIDFlagCommit +} + // Absent returns true if CommitSig is absent. func (cs CommitSig) Absent() bool { return cs.BlockIDFlag == BlockIDFlagAbsent diff --git a/types/block_test.go b/types/block_test.go index b6d7fb710..0c36c812b 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -268,6 +268,62 @@ func TestCommitValidateBasic(t *testing.T) { } } +func TestMaxCommitSigBytes(t *testing.T) { + // time is varint encoded so need to pick the max. + // year int, month Month, day, hour, min, sec, nsec int, loc *Location + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + + cs := &CommitSig{ + BlockIDFlag: BlockIDFlagNil, + ValidatorAddress: crypto.AddressHash([]byte("validator_address")), + Timestamp: timestamp, + Signature: tmhash.Sum([]byte("signature")), + } + + pb := cs.ToProto() + + assert.EqualValues(t, MaxCommitSigBytes, pb.Size()) +} + +func TestMaxCommitBytes(t *testing.T) { + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + + cs := CommitSig{ + BlockIDFlag: BlockIDFlagNil, + ValidatorAddress: crypto.AddressHash([]byte("validator_address")), + Timestamp: timestamp, + Signature: tmhash.Sum([]byte("signature")), + } + + // check size with a single commit + commit := &Commit{ + Height: math.MaxInt64, + Round: math.MaxInt32, + BlockID: BlockID{ + Hash: tmhash.Sum([]byte("blockID_hash")), + PartSetHeader: PartSetHeader{ + Total: math.MaxInt32, + Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")), + }, + }, + Signatures: []CommitSig{cs}, + } + + pb := commit.ToProto() + + assert.EqualValues(t, MaxCommitBytes(1), int64(pb.Size())) + + // check the upper bound of the commit size + for i := 1; i < MaxVotesCount; i++ { + commit.Signatures = append(commit.Signatures, cs) + } + + pb = commit.ToProto() + + assert.EqualValues(t, MaxCommitBytes(MaxVotesCount), int64(pb.Size())) + +} + func TestHeaderHash(t *testing.T) { testCases := []struct { desc string @@ -418,9 +474,9 @@ func TestBlockMaxDataBytes(t *testing.T) { }{ 0: {-10, 1, 0, true, 0}, 1: {10, 1, 0, true, 0}, - 2: {844, 1, 0, true, 0}, - 3: {846, 1, 0, false, 0}, - 4: {847, 1, 0, false, 1}, + 2: {809, 1, 0, true, 0}, + 3: {810, 1, 0, false, 0}, + 4: {811, 1, 0, false, 1}, } for i, tc := range testCases { @@ -447,9 +503,9 @@ func TestBlockMaxDataBytesNoEvidence(t *testing.T) { }{ 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}, + 2: {809, 1, true, 0}, + 3: {810, 1, false, 0}, + 4: {811, 1, false, 1}, } for i, tc := range testCases { diff --git a/types/vote.go b/types/vote.go index c9ded9d71..7b841c28a 100644 --- a/types/vote.go +++ b/types/vote.go @@ -13,9 +13,7 @@ import ( ) const ( - // MaxVoteBytes is a maximum vote size (including amino overhead). - MaxVoteBytes int64 = 209 - nilVoteStr string = "nil-Vote" + nilVoteStr string = "nil-Vote" ) var ( diff --git a/types/vote_test.go b/types/vote_test.go index 64e6f20fa..51e20cd7a 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -1,7 +1,6 @@ package types import ( - "math" "testing" "time" @@ -218,38 +217,6 @@ func TestVoteVerify(t *testing.T) { } } -func TestMaxVoteBytes(t *testing.T) { - // time is varint encoded so need to pick the max. - // year int, month Month, day, hour, min, sec, nsec int, loc *Location - timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) - - vote := &Vote{ - ValidatorAddress: crypto.AddressHash([]byte("validator_address")), - ValidatorIndex: math.MaxInt32, - Height: math.MaxInt64, - Round: math.MaxInt32, - Timestamp: timestamp, - Type: tmproto.PrevoteType, - BlockID: BlockID{ - Hash: tmhash.Sum([]byte("blockID_hash")), - PartSetHeader: PartSetHeader{ - Total: math.MaxInt32, - Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")), - }, - }, - } - - v := vote.ToProto() - privVal := NewMockPV() - err := privVal.SignVote("test_chain_id", v) - require.NoError(t, err) - - bz, err := proto.Marshal(v) - require.NoError(t, err) - - assert.EqualValues(t, MaxVoteBytes, len(bz)) -} - func TestVoteString(t *testing.T) { str := examplePrecommit().String() expected := `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests