From 9e113fd42ad723b572c93abe9ea8f331f1809377 Mon Sep 17 00:00:00 2001 From: egonspace Date: Wed, 11 Nov 2020 10:08:20 +0900 Subject: [PATCH 1/7] fix: calculate MaxDataBytes accurately by pv key type --- node/node_test.go | 4 +- privval/signer_client_test.go | 4 +- privval/signer_listener_endpoint_test.go | 4 +- state/execution.go | 2 +- state/state_test.go | 2 +- state/validation_test.go | 2 +- types/block.go | 62 ++++- types/block_test.go | 284 ++++++++++++++++++++- types/evidence.go | 32 ++- types/evidence_test.go | 298 +++++++++++++---------- types/priv_validator.go | 34 ++- types/priv_validator_test.go | 16 ++ types/proposal_test.go | 177 ++++++++------ types/protobuf_test.go | 41 ++-- types/validator.go | 2 +- types/validator_set_test.go | 9 +- types/vote.go | 4 +- types/vote_test.go | 221 ++++++++++------- types/voter_set.go | 12 + types/voter_set_test.go | 6 +- 20 files changed, 857 insertions(+), 359 deletions(-) create mode 100644 types/priv_validator_test.go diff --git a/node/node_test.go b/node/node_test.go index 698332cec..9aeadb097 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -146,7 +146,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { signerServer := privval.NewSignerServer( dialerEndpoint, config.ChainID(), - types.NewMockPV(), + types.NewMockPV(types.PvKeyEd25519), ) go func() { @@ -192,7 +192,7 @@ func TestNodeSetPrivValIPC(t *testing.T) { pvsc := privval.NewSignerServer( dialerEndpoint, config.ChainID(), - types.NewMockPV(), + types.NewMockPV(types.PvKeyEd25519), ) go func() { diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index b8db3e681..5cc796135 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -265,8 +265,8 @@ func brokenHandler(privVal types.PrivValidator, request SignerMessage, chainID s func TestSignerUnexpectedResponse(t *testing.T) { for _, tc := range getSignerTestCases(t, false) { // this should be executed before SignerServer starts to avoid race condition - tc.signerServer.privVal = types.NewMockPV() - tc.mockPV = types.NewMockPV() + tc.signerServer.privVal = types.NewMockPV(types.PvKeyEd25519) + tc.mockPV = types.NewMockPV(types.PvKeyEd25519) tc.signerServer.SetRequestHandler(brokenHandler) tc.signerServer.Start() diff --git a/privval/signer_listener_endpoint_test.go b/privval/signer_listener_endpoint_test.go index fbb511d24..bdacb0dc7 100644 --- a/privval/signer_listener_endpoint_test.go +++ b/privval/signer_listener_endpoint_test.go @@ -68,7 +68,7 @@ func TestSignerRemoteRetryTCPOnly(t *testing.T) { SignerDialerEndpointConnRetries(retries)(dialerEndpoint) chainID := tmrand.Str(12) - mockPV := types.NewMockPV() + mockPV := types.NewMockPV(types.PvKeyEd25519) signerServer := NewSignerServer(dialerEndpoint, chainID, mockPV) err = signerServer.Start() @@ -88,7 +88,7 @@ func TestRetryConnToRemoteSigner(t *testing.T) { var ( logger = log.TestingLogger() chainID = tmrand.Str(12) - mockPV = types.NewMockPV() + mockPV = types.NewMockPV(types.PvKeyEd25519) endpointIsOpenCh = make(chan struct{}) thisConnTimeout = testTimeoutReadWrite listenerEndpoint = newSignerListenerEndpoint(logger, tc.addr, thisConnTimeout) diff --git a/state/execution.go b/state/execution.go index 3cc04aa6b..458a264d6 100644 --- a/state/execution.go +++ b/state/execution.go @@ -108,7 +108,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock( evidence := blockExec.evpool.PendingEvidence(maxNumEvidence) // Fetch a limited amount of valid txs - maxDataBytes := types.MaxDataBytes(maxBytes, state.Voters.Size(), len(evidence)) + maxDataBytes := types.MaxDataBytes(maxBytes, commit, evidence) txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas) return state.MakeBlock(height, txs, commit, evidence, proposerAddr, round, proof) diff --git a/state/state_test.go b/state/state_test.go index ea39efb14..3a7d77565 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -407,7 +407,7 @@ func TestProposerFrequency(t *testing.T) { // make sure votePower > 0 votePower := int64(tmrand.Int()%maxPower) + 1 totalVotePower += votePower - privVal := types.NewMockPV() + privVal := types.NewMockPV(types.PvKeyEd25519) pubKey, err := privVal.GetPubKey() require.NoError(t, err) val := types.NewValidator(pubKey, votePower) diff --git a/state/validation_test.go b/state/validation_test.go index c70abc4c3..a6d5e31d7 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -105,7 +105,7 @@ func TestValidateBlockCommit(t *testing.T) { ) lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) wrongSigsCommit := types.NewCommit(1, 0, types.BlockID{}, nil) - badPrivVal := types.NewMockPV() + badPrivVal := types.NewMockPV(types.PvKeyEd25519) for height := int64(1); height < validationTestsStopHeight; height++ { proposerAddr := state.Validators.SelectProposer([]byte{}, height, 0).Address diff --git a/types/block.go b/types/block.go index 96274dd10..ea1ccb2f1 100644 --- a/types/block.go +++ b/types/block.go @@ -8,6 +8,8 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/merkle" @@ -230,12 +232,16 @@ func (b *Block) Unmarshal(bs []byte) 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 int64, commit *Commit, evidences []Evidence) int64 { + evidenceBytes := int64(0) + for _, ev := range evidences { + evidenceBytes += MaxEvidenceBytes(PvKeyTypeByPubKey(ev.PublicKey())) + } maxDataBytes := maxBytes - MaxAminoOverheadForBlock - MaxHeaderBytes - - int64(valsCount)*MaxVoteBytes - - int64(evidenceCount)*MaxEvidenceBytes + commit.MaxCommitBytes() - + evidenceBytes if maxDataBytes < 0 { panic(fmt.Sprintf( @@ -246,7 +252,6 @@ func MaxDataBytes(maxBytes int64, valsCount, evidenceCount int) int64 { } return maxDataBytes - } // MaxDataBytesUnknownEvidence returns the maximum size of block's data when @@ -259,7 +264,7 @@ func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int) int64 { maxDataBytes := maxBytes - MaxAminoOverheadForBlock - MaxHeaderBytes - - int64(valsCount)*MaxVoteBytes - + int64(valsCount)*MaxCommitBytes - maxEvidenceBytes if maxDataBytes < 0 { @@ -554,6 +559,15 @@ type CommitSig struct { Signature []byte `json:"signature"` } +const ( + MaxCommitBytes = 255 + BlockIDFlagLen = 4 + TimestampMaxLen = 18 + Bytes20AminoHeadLen = 2 + Bytes64AminoHeadLen = 2 + Bytes96AminoHeadLen = 3 +) + // NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit. func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time) CommitSig { return CommitSig{ @@ -564,6 +578,21 @@ func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time) Commi } } +func (cs CommitSig) MaxCommitSigBytes() int64 { + switch len(cs.Signature) { + case 0: + return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + case ed25519.SignatureSize: + return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + + Bytes64AminoHeadLen + int64(len(cs.Signature)) + case bls.SignatureSize: + return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + + Bytes96AminoHeadLen + int64(len(cs.Signature)) + } + return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + + Bytes96AminoHeadLen + int64(len(cs.Signature)) +} + // ForBlock returns true if CommitSig is for the block. func (cs CommitSig) ForBlock() bool { return cs.BlockIDFlag == BlockIDFlagCommit @@ -706,6 +735,29 @@ func NewCommit(height int64, round int, blockID BlockID, commitSigs []CommitSig) } } +const ( + CommitHeighMaxtLen = 11 + CommitRoundMaxLen = 6 + CommitBlockIDMaxLen = 77 + CommitAminoOverhead = 1 + CommitAggrSigOverhead = 2 +) + +func (commit *Commit) MaxCommitBytes() int64 { + sigBytes := int64(0) + for _, s := range commit.Signatures { + sigBytes += CommitAminoOverhead + s.MaxCommitSigBytes() + } + if sigBytes > 0 { + sigBytes += CommitAminoOverhead + } + bytesLen := CommitHeighMaxtLen + CommitRoundMaxLen + CommitAminoOverhead + CommitBlockIDMaxLen + sigBytes + if len(commit.AggregatedSignature) > 0 { + bytesLen += CommitAggrSigOverhead + int64(len(commit.AggregatedSignature)) + } + return bytesLen +} + // CommitToVoteSet constructs a VoteSet from the Commit and validator set. // Panics if signatures from the commit can't be added to the voteset. // Inverse of VoteSet.MakeCommit(). diff --git a/types/block_test.go b/types/block_test.go index 58d409bf7..1c4fc1aec 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -13,8 +13,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/bits" @@ -377,31 +377,289 @@ func hexBytesFromString(s string) bytes.HexBytes { return bytes.HexBytes(b) } +func TestCommitSigNumOfBytes(t *testing.T) { + pv1 := NewMockPV(PvKeyEd25519) + pv2 := NewMockPV(PvKeyComposite) + pv3 := NewMockPV(PvKeyBLS) + + pub1, _ := pv1.GetPubKey() + pub2, _ := pv2.GetPubKey() + pub3, _ := pv3.GetPubKey() + + blockID := BlockID{tmrand.Bytes(tmhash.Size), + PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} + chainID := "mychain" + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + + vote1 := &Vote{ + ValidatorAddress: pub1.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + assert.NoError(t, pv1.SignVote(chainID, vote1)) + + vote2 := &Vote{ + ValidatorAddress: pub2.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + assert.NoError(t, pv2.SignVote(chainID, vote2)) + + vote3 := &Vote{ + ValidatorAddress: pub2.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + assert.NoError(t, pv3.SignVote(chainID, vote3)) + + commitSig1 := NewCommitSigForBlock(vote1.Signature, pub1.Address(), timestamp) + commitSig2 := NewCommitSigForBlock(vote2.Signature, pub2.Address(), timestamp) + commitSig3 := NewCommitSigForBlock(vote3.Signature, pub3.Address(), timestamp) + aggregatedCommitSig := NewCommitSigForBlock(nil, pub2.Address(), timestamp) + + b1, err1 := cdc.MarshalBinaryLengthPrefixed(commitSig1) + assert.NoError(t, err1) + assert.True(t, int64(len(b1)) == commitSig1.MaxCommitSigBytes()) + + b2, err2 := cdc.MarshalBinaryLengthPrefixed(commitSig2) + assert.NoError(t, err2) + assert.True(t, int64(len(b2)) == commitSig2.MaxCommitSigBytes()) + + b3, err3 := cdc.MarshalBinaryLengthPrefixed(commitSig3) + assert.NoError(t, err3) + assert.True(t, int64(len(b3)) == commitSig3.MaxCommitSigBytes()) + + b4, err4 := cdc.MarshalBinaryLengthPrefixed(aggregatedCommitSig) + assert.NoError(t, err4) + assert.True(t, int64(len(b4)) == aggregatedCommitSig.MaxCommitSigBytes()) +} + +func TestMaxCommitBytes(t *testing.T) { + pv1 := NewMockPV(PvKeyEd25519) + pv2 := NewMockPV(PvKeyComposite) + pv3 := NewMockPV(PvKeyComposite) + + pub1, _ := pv1.GetPubKey() + pub2, _ := pv2.GetPubKey() + pub3, _ := pv3.GetPubKey() + + blockID := BlockID{tmrand.Bytes(tmhash.Size), + PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} + + chainID := "mychain" + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + + vote1 := &Vote{ + ValidatorAddress: pub1.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + assert.NoError(t, pv1.SignVote(chainID, vote1)) + + vote2 := &Vote{ + ValidatorAddress: pub2.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + assert.NoError(t, pv2.SignVote(chainID, vote2)) + + vote3 := &Vote{ + ValidatorAddress: pub2.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + // does not sign vote3 + + commitSig := make([]CommitSig, 3) + commitSig[0] = NewCommitSigForBlock(vote1.Signature, pub1.Address(), timestamp) + commitSig[1] = NewCommitSigForBlock(vote2.Signature, pub2.Address(), timestamp) + commitSig[2] = NewCommitSigForBlock(vote3.Signature, pub3.Address(), timestamp) + + commit := NewCommit(math.MaxInt64, math.MaxInt32, blockID, commitSig) + commit.AggregatedSignature = tmrand.Bytes(bls.SignatureSize) + + bz1, err1 := cdc.MarshalBinaryLengthPrefixed(blockID) + assert.NoError(t, err1) + bz2, err2 := cdc.MarshalBinaryLengthPrefixed(commit) + assert.NoError(t, err2) + assert.True(t, CommitBlockIDMaxLen == len(bz1)) + assert.True(t, commit.MaxCommitBytes() == int64(len(bz2))) +} + +func TestMaxCommitBytesMany(t *testing.T) { + commitCount := 100 + commitSig := make([]CommitSig, commitCount) + blockID := BlockID{tmrand.Bytes(tmhash.Size), + PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} + + chainID := "mychain" + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + + for i := 0; i < commitCount; i++ { + pv := NewMockPV(PvKeyEd25519) + pub, _ := pv.GetPubKey() + vote := &Vote{ + ValidatorAddress: pub.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + assert.NoError(t, pv.SignVote(chainID, vote)) + commitSig[i] = NewCommitSigForBlock(vote.Signature, pub.Address(), timestamp) + } + commit := NewCommit(math.MaxInt64, math.MaxInt32, blockID, commitSig) + bz, err := cdc.MarshalBinaryLengthPrefixed(commit) + assert.NoError(t, err) + assert.True(t, commit.MaxCommitBytes() == int64(len(bz))) +} + +func TestMaxCommitBytesAggregated(t *testing.T) { + commitCount := 100 + commitSig := make([]CommitSig, commitCount) + blockID := BlockID{tmrand.Bytes(tmhash.Size), + PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} + + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + + for i := 0; i < commitCount; i++ { + pv := NewMockPV(PvKeyComposite) + pub, _ := pv.GetPubKey() + vote := &Vote{ + ValidatorAddress: pub.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + // do not sign + commitSig[i] = NewCommitSigForBlock(vote.Signature, pub.Address(), timestamp) + } + commit := NewCommit(math.MaxInt64, math.MaxInt32, blockID, commitSig) + commit.AggregatedSignature = tmrand.Bytes(bls.SignatureSize) + + bz, err := cdc.MarshalBinaryLengthPrefixed(commit) + assert.NoError(t, err) + assert.True(t, commit.MaxCommitBytes() == int64(len(bz))) +} + +func TestMaxCommitBytesMixed(t *testing.T) { + commitCount := 100 + commitSig := make([]CommitSig, commitCount) + blockID := BlockID{tmrand.Bytes(tmhash.Size), + PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} + + chainID := "mychain" + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + + for i := 0; i < commitCount; i++ { + keyType := randomKeyType() + pv := NewMockPV(keyType) + pub, _ := pv.GetPubKey() + vote := &Vote{ + ValidatorAddress: pub.Address(), + ValidatorIndex: math.MaxInt64, + Height: math.MaxInt64, + Round: math.MaxInt64, + Timestamp: timestamp, + Type: PrecommitType, + BlockID: blockID, + } + // sign only if key type is ed25519 + if keyType == PvKeyEd25519 { + assert.NoError(t, pv.SignVote(chainID, vote)) + } + commitSig[i] = NewCommitSigForBlock(vote.Signature, pub.Address(), timestamp) + } + commit := NewCommit(math.MaxInt64, math.MaxInt32, blockID, commitSig) + commit.AggregatedSignature = tmrand.Bytes(bls.SignatureSize) + + bz, err := cdc.MarshalBinaryLengthPrefixed(commit) + assert.NoError(t, err) + assert.True(t, commit.MaxCommitBytes() == int64(len(bz))) +} + func TestBlockMaxDataBytes(t *testing.T) { + pv1 := NewMockPV(PvKeyEd25519) + pv2 := NewMockPV(PvKeyComposite) + pv3 := NewMockPV(PvKeyBLS) + + pub1, _ := pv1.GetPubKey() + pub2, _ := pv2.GetPubKey() + pub3, _ := pv3.GetPubKey() + + val := make([]*Validator, 3) + val[0] = newValidator(pub1.Address(), 100) + val[1] = newValidator(pub2.Address(), 200) + val[2] = newValidator(pub3.Address(), 300) + valSet := NewValidatorSet(val) + blockID := makeBlockIDRandom() + chainID := "mychain" + vote1, _ := MakeVote(1, blockID, valSet, pv1, chainID, tmtime.Now()) + vote2, _ := MakeVote(1, blockID, valSet, pv2, chainID, tmtime.Now()) + vote3, _ := MakeVote(1, blockID, valSet, pv3, chainID, tmtime.Now()) + vote4, _ := MakeVote(1, makeBlockIDRandom(), valSet, pv3, chainID, tmtime.Now()) + + commitSig := make([]CommitSig, 3) + commitSig[0] = NewCommitSigForBlock(vote1.Signature, pub1.Address(), tmtime.Now()) + commitSig[1] = NewCommitSigForBlock(vote2.Signature, pub2.Address(), tmtime.Now()) + commitSig[2] = NewCommitSigForBlock(vote3.Signature, pub3.Address(), tmtime.Now()) + + commit := NewCommit(1, 0, blockID, commitSig) + dupEv := NewDuplicateVoteEvidence(pub3, vote3, vote4) + testCases := []struct { - maxBytes int64 - valsCount int - evidenceCount int - panics bool - result int64 + maxBytes int64 + commit *Commit + evidence []Evidence + panics bool + result int64 }{ - 0: {-10, 1, 0, true, 0}, - 1: {10, 1, 0, true, 0}, - 2: {865, 1, 0, true, 0}, - 3: {932, 1, 0, false, 0}, - 4: {933, 1, 0, false, 1}, + 0: {-10, commit, []Evidence{dupEv}, true, 0}, + 1: {10, commit, []Evidence{dupEv}, true, 0}, + 2: {1700, commit, []Evidence{dupEv}, true, 0}, + 3: {1735, commit, []Evidence{dupEv}, false, 0}, + 4: {1736, commit, []Evidence{dupEv}, false, 1}, } for i, tc := range testCases { tc := tc if tc.panics { assert.Panics(t, func() { - MaxDataBytes(tc.maxBytes, tc.valsCount, tc.evidenceCount) + MaxDataBytes(tc.maxBytes, tc.commit, tc.evidence) }, "#%v", i) } else { assert.Equal(t, tc.result, - MaxDataBytes(tc.maxBytes, tc.valsCount, tc.evidenceCount), + MaxDataBytes(tc.maxBytes, tc.commit, tc.evidence), "#%v", i) } } diff --git a/types/evidence.go b/types/evidence.go index 3bc3dce6d..4f7aaa84e 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -8,18 +8,26 @@ import ( "github.com/pkg/errors" amino "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" tmproto "github.com/tendermint/tendermint/proto/types" ) -const ( +func MaxEvidenceBytes(keyType PvKeyType) int64 { // MaxEvidenceBytes is a maximum size of any evidence (including amino overhead). - MaxEvidenceBytes int64 = 548 -) + switch keyType { + case PvKeyEd25519: + return 483 + case PvKeyComposite: + return 608 + case PvKeyBLS: + return 563 + } + panic(fmt.Sprintf("unknown private key type: %d", keyType)) +} // ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid. type ErrEvidenceInvalid struct { @@ -59,6 +67,7 @@ func (err *ErrEvidenceOverflow) Error() string { type Evidence interface { Height() int64 // height of the equivocation Time() time.Time // time of the equivocation + PublicKey() crypto.PubKey // public key of the equivocating validator Address() []byte // address of the equivocating validator Bytes() []byte // bytes which comprise the evidence Hash() []byte // hash of the evidence @@ -202,7 +211,8 @@ const ( // See https://github.com/tendermint/tendermint/issues/2590 func MaxEvidencePerBlock(blockMaxBytes int64) (int64, int64) { maxBytes := blockMaxBytes / MaxEvidenceBytesDenominator - maxNum := maxBytes / MaxEvidenceBytes + // Calculate based on PvKeyComposite, where evidence is the largest. + maxNum := maxBytes / MaxEvidenceBytes(PvKeyComposite) return maxNum, maxBytes } @@ -260,6 +270,10 @@ func (dve *DuplicateVoteEvidence) Address() []byte { return dve.PubKey.Address() } +func (dve *DuplicateVoteEvidence) PublicKey() crypto.PubKey { + return dve.PubKey +} + // Hash returns the hash of the evidence. func (dve *DuplicateVoteEvidence) Bytes() []byte { return cdcEncode(dve) @@ -399,9 +413,11 @@ func NewMockEvidence(height int64, eTime time.Time, idx int, address []byte) Moc EvidenceAddress: address} } -func (e MockEvidence) Height() int64 { return e.EvidenceHeight } -func (e MockEvidence) Time() time.Time { return e.EvidenceTime } -func (e MockEvidence) Address() []byte { return e.EvidenceAddress } +func (e MockEvidence) Height() int64 { return e.EvidenceHeight } +func (e MockEvidence) Time() time.Time { return e.EvidenceTime } +func (e MockEvidence) Address() []byte { return e.EvidenceAddress } +func (e MockEvidence) PublicKey() crypto.PubKey { return ed25519.GenPrivKey().PubKey() } + func (e MockEvidence) Hash() []byte { return []byte(fmt.Sprintf("%d-%x-%s", e.EvidenceHeight, e.EvidenceAddress, e.EvidenceTime)) diff --git a/types/evidence_test.go b/types/evidence_test.go index 64d7178b1..3ca63be7e 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/libs/rand" ) type voteData struct { @@ -39,85 +39,117 @@ func makeVote( } func TestEvidence(t *testing.T) { - val := NewMockPV() - val2 := NewMockPV() + keyTypes := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, kt := range keyTypes { + val := NewMockPV(kt) + val2 := NewMockPV(kt) - blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) - blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) - blockID3 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash")) - blockID4 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash2")) + blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) + blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) + blockID3 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash")) + blockID4 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash2")) - const chainID = "mychain" + const chainID = "mychain" - vote1 := makeVote(t, val, chainID, 0, 10, 2, 1, blockID) - badVote := makeVote(t, val, chainID, 0, 10, 2, 1, blockID) - err := val2.SignVote(chainID, badVote) - assert.NoError(t, err) - - cases := []voteData{ - {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID2), true}, // different block ids - {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID3), true}, - {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID4), true}, - {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID), false}, // wrong block id - {vote1, makeVote(t, val, "mychain2", 0, 10, 2, 1, blockID2), false}, // wrong chain id - {vote1, makeVote(t, val, chainID, 1, 10, 2, 1, blockID2), false}, // wrong val index - {vote1, makeVote(t, val, chainID, 0, 11, 2, 1, blockID2), false}, // wrong height - {vote1, makeVote(t, val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round - {vote1, makeVote(t, val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step - {vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator - {vote1, badVote, false}, // signed by wrong key - } + vote1 := makeVote(t, val, chainID, 0, 10, 2, 1, blockID) + badVote := makeVote(t, val, chainID, 0, 10, 2, 1, blockID) + err := val2.SignVote(chainID, badVote) + assert.NoError(t, err) - pubKey, err := val.GetPubKey() - require.NoError(t, err) - for _, c := range cases { - ev := &DuplicateVoteEvidence{ - VoteA: c.vote1, - VoteB: c.vote2, + cases := []voteData{ + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID2), true}, // different block ids + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID3), true}, + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID4), true}, + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID), false}, // wrong block id + {vote1, makeVote(t, val, "mychain2", 0, 10, 2, 1, blockID2), false}, // wrong chain id + {vote1, makeVote(t, val, chainID, 1, 10, 2, 1, blockID2), false}, // wrong val index + {vote1, makeVote(t, val, chainID, 0, 11, 2, 1, blockID2), false}, // wrong height + {vote1, makeVote(t, val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round + {vote1, makeVote(t, val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step + {vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator + {vote1, badVote, false}, // signed by wrong key } - if c.valid { - assert.Nil(t, ev.Verify(chainID, pubKey), "evidence should be valid") - } else { - assert.NotNil(t, ev.Verify(chainID, pubKey), "evidence should be invalid") + + pubKey, err := val.GetPubKey() + require.NoError(t, err) + for _, c := range cases { + ev := &DuplicateVoteEvidence{ + VoteA: c.vote1, + VoteB: c.vote2, + } + if c.valid { + assert.Nil(t, ev.Verify(chainID, pubKey), "evidence should be valid") + } else { + assert.NotNil(t, ev.Verify(chainID, pubKey), "evidence should be invalid") + } } } } func TestDuplicatedVoteEvidence(t *testing.T) { - ev := randomDuplicatedVoteEvidence(t) + keyTypes := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, kt := range keyTypes { + ev := randomDuplicatedVoteEvidence(kt, t) - assert.True(t, ev.Equal(ev)) - assert.False(t, ev.Equal(&DuplicateVoteEvidence{})) + assert.True(t, ev.Equal(ev)) + assert.False(t, ev.Equal(&DuplicateVoteEvidence{})) + } } func TestEvidenceList(t *testing.T) { - ev := randomDuplicatedVoteEvidence(t) - evl := EvidenceList([]Evidence{ev}) + keyTypes := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, kt := range keyTypes { + ev := randomDuplicatedVoteEvidence(kt, t) + evl := EvidenceList([]Evidence{ev}) - assert.NotNil(t, evl.Hash()) - assert.True(t, evl.Has(ev)) - assert.False(t, evl.Has(&DuplicateVoteEvidence{})) + assert.NotNil(t, evl.Hash()) + assert.True(t, evl.Has(ev)) + assert.False(t, evl.Has(&DuplicateVoteEvidence{})) + } } func TestMaxEvidenceBytes(t *testing.T) { - val := NewMockPV() - blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) - blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) - const chainID = "mychain" - ev := &DuplicateVoteEvidence{ - PubKey: secp256k1.GenPrivKey().PubKey(), // use secp because it's pubkey is longer - VoteA: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID), - VoteB: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2), + keyTypes := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, } + for _, kt := range keyTypes { + val := NewMockPV(kt) + randomPartHash := rand.Bytes(int(rand.Uint32() % 1000)) + randomHash1 := rand.Bytes(int(rand.Uint32() % 1000)) + randomHash2 := rand.Bytes(int(rand.Uint32() % 1000)) + blockID := makeBlockID(tmhash.Sum(randomHash1), math.MaxInt64, tmhash.Sum(randomPartHash)) + blockID2 := makeBlockID(tmhash.Sum(randomHash2), math.MaxInt64, tmhash.Sum(randomPartHash)) + const chainID = "mychain" + pubKey, _ := val.GetPubKey() + ev := &DuplicateVoteEvidence{ + PubKey: pubKey, // use secp because it's pubkey is longer + VoteA: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID), + VoteB: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2), + } - bz, err := cdc.MarshalBinaryLengthPrefixed(ev) - require.NoError(t, err) + bz, err := cdc.MarshalBinaryLengthPrefixed(ev) + require.NoError(t, err) - assert.EqualValues(t, MaxEvidenceBytes, len(bz)) + assert.EqualValues(t, MaxEvidenceBytes(kt), len(bz)) + } } -func randomDuplicatedVoteEvidence(t *testing.T) *DuplicateVoteEvidence { - val := NewMockPV() +func randomDuplicatedVoteEvidence(keyType PvKeyType, t *testing.T) *DuplicateVoteEvidence { + val := NewMockPV(keyType) blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) const chainID = "mychain" @@ -128,42 +160,49 @@ func randomDuplicatedVoteEvidence(t *testing.T) *DuplicateVoteEvidence { } func TestDuplicateVoteEvidenceValidation(t *testing.T) { - val := NewMockPV() - blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) - blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) - const chainID = "mychain" - - testCases := []struct { - testName string - malleateEvidence func(*DuplicateVoteEvidence) - expectErr bool - }{ - {"Good DuplicateVoteEvidence", func(ev *DuplicateVoteEvidence) {}, false}, - {"Nil vote A", func(ev *DuplicateVoteEvidence) { ev.VoteA = nil }, true}, - {"Nil vote B", func(ev *DuplicateVoteEvidence) { ev.VoteB = nil }, true}, - {"Nil votes", func(ev *DuplicateVoteEvidence) { - ev.VoteA = nil - ev.VoteB = nil - }, true}, - {"Invalid vote type", func(ev *DuplicateVoteEvidence) { - ev.VoteA = makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0, blockID2) - }, true}, - {"Invalid vote order", func(ev *DuplicateVoteEvidence) { - swap := ev.VoteA.Copy() - ev.VoteA = ev.VoteB.Copy() - ev.VoteB = swap - }, true}, + keyTypes := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, } - for _, tc := range testCases { - tc := tc - t.Run(tc.testName, func(t *testing.T) { - pk := secp256k1.GenPrivKey().PubKey() - vote1 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID) - vote2 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID2) - ev := NewDuplicateVoteEvidence(pk, vote1, vote2) - tc.malleateEvidence(ev) - assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result") - }) + for _, kt := range keyTypes { + val := NewMockPV(kt) + blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) + blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) + const chainID = "mychain" + + testCases := []struct { + testName string + malleateEvidence func(*DuplicateVoteEvidence) + expectErr bool + }{ + {"Good DuplicateVoteEvidence", func(ev *DuplicateVoteEvidence) {}, false}, + {"Nil vote A", func(ev *DuplicateVoteEvidence) { ev.VoteA = nil }, true}, + {"Nil vote B", func(ev *DuplicateVoteEvidence) { ev.VoteB = nil }, true}, + {"Nil votes", func(ev *DuplicateVoteEvidence) { + ev.VoteA = nil + ev.VoteB = nil + }, true}, + {"Invalid vote type", func(ev *DuplicateVoteEvidence) { + ev.VoteA = makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0, blockID2) + }, true}, + {"Invalid vote order", func(ev *DuplicateVoteEvidence) { + swap := ev.VoteA.Copy() + ev.VoteA = ev.VoteB.Copy() + ev.VoteB = swap + }, true}, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + pk := secp256k1.GenPrivKey().PubKey() + vote1 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID) + vote2 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID2) + ev := NewDuplicateVoteEvidence(pk, vote1, vote2) + tc.malleateEvidence(ev) + assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } } } @@ -179,41 +218,48 @@ func TestMockBadEvidenceValidateBasic(t *testing.T) { func TestEvidenceProto(t *testing.T) { // -------- Votes -------- - val := NewMockPV() - blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) - blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) - const chainID = "mychain" - v := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, 1, 0x01, blockID) - v2 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, 2, 0x01, blockID2) - - tests := []struct { - testName string - evidence Evidence - wantErr bool - wantErr2 bool - }{ - {"&DuplicateVoteEvidence empty fail", &DuplicateVoteEvidence{}, true, true}, - {"&DuplicateVoteEvidence nil voteB", &DuplicateVoteEvidence{VoteA: v, VoteB: nil}, true, true}, - {"&DuplicateVoteEvidence nil voteA", &DuplicateVoteEvidence{VoteA: nil, VoteB: v}, true, true}, - {"&DuplicateVoteEvidence success", &DuplicateVoteEvidence{VoteA: v2, VoteB: v, - PubKey: val.PrivKey.PubKey()}, false, false}, + keyTypes := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, } - for _, tt := range tests { - tt := tt - t.Run(tt.testName, func(t *testing.T) { - pb, err := EvidenceToProto(tt.evidence) - if tt.wantErr { - assert.Error(t, err, tt.testName) - return - } - assert.NoError(t, err, tt.testName) + for _, kt := range keyTypes { + val := NewMockPV(kt) + blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) + blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) + const chainID = "mychain" + v := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, 1, 0x01, blockID) + v2 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, 2, 0x01, blockID2) - evi, err := EvidenceFromProto(pb) - if tt.wantErr2 { - assert.Error(t, err, tt.testName) - return - } - require.Equal(t, tt.evidence, evi, tt.testName) - }) + tests := []struct { + testName string + evidence Evidence + wantErr bool + wantErr2 bool + }{ + {"&DuplicateVoteEvidence empty fail", &DuplicateVoteEvidence{}, true, true}, + {"&DuplicateVoteEvidence nil voteB", &DuplicateVoteEvidence{VoteA: v, VoteB: nil}, true, true}, + {"&DuplicateVoteEvidence nil voteA", &DuplicateVoteEvidence{VoteA: nil, VoteB: v}, true, true}, + {"&DuplicateVoteEvidence success", &DuplicateVoteEvidence{VoteA: v2, VoteB: v, + PubKey: val.PrivKey.PubKey()}, false, false}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.testName, func(t *testing.T) { + pb, err := EvidenceToProto(tt.evidence) + if tt.wantErr { + assert.Error(t, err, tt.testName) + return + } + assert.NoError(t, err, tt.testName) + + evi, err := EvidenceFromProto(pb) + if tt.wantErr2 { + assert.Error(t, err, tt.testName) + return + } + require.Equal(t, tt.evidence, evi, tt.testName) + }) + } } } diff --git a/types/priv_validator.go b/types/priv_validator.go index d0427b1f1..3d04d22fa 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/tendermint/tendermint/crypto/bls" "github.com/tendermint/tendermint/crypto/composite" "github.com/tendermint/tendermint/crypto" @@ -61,8 +62,37 @@ type MockPV struct { breakVoteSigning bool } -func NewMockPV() MockPV { - return MockPV{composite.GenPrivKey(), false, false} +type PvKeyType int + +const ( + PvKeyEd25519 PvKeyType = iota + PvKeyComposite + PvKeyBLS +) + +func NewMockPV(keyType PvKeyType) MockPV { + switch keyType { + case PvKeyEd25519: + return MockPV{ed25519.GenPrivKey(), false, false} + case PvKeyComposite: + return MockPV{composite.GenPrivKey(), false, false} + case PvKeyBLS: + return MockPV{bls.GenPrivKey(), false, false} + default: + panic(fmt.Sprintf("known pv key type: %d", keyType)) + } +} + +func PvKeyTypeByPubKey(pubKey crypto.PubKey) PvKeyType { + switch len(pubKey.Bytes()) { + case 37: + return PvKeyEd25519 + case 90: + return PvKeyComposite + case 53: + return PvKeyBLS + } + panic(fmt.Sprintf("unknown address len: %d", len(pubKey.Bytes()))) } // NewMockPVWithParams allows one to create a MockPV instance, but with finer diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go new file mode 100644 index 000000000..2c1604207 --- /dev/null +++ b/types/priv_validator_test.go @@ -0,0 +1,16 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPvKeyTypeByAddress(t *testing.T) { + for i := 0; i < 1000; i++ { + keyType := randomKeyType() + pv := NewMockPV(keyType) + pubKey, _ := pv.GetPubKey() + assert.True(t, keyType == PvKeyTypeByPubKey(pubKey)) + } +} diff --git a/types/proposal_test.go b/types/proposal_test.go index b4ab14b69..dc3bba0ca 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -45,35 +45,42 @@ func TestProposalString(t *testing.T) { } func TestProposalVerifySignature(t *testing.T) { - privVal := NewMockPV() - pubKey, err := privVal.GetPubKey() - require.NoError(t, err) - - prop := NewProposal( - 4, 2, 2, - BlockID{[]byte{1, 2, 3}, PartSetHeader{777, []byte("proper")}}) - signBytes := prop.SignBytes("test_chain_id") - - // sign it - err = privVal.SignProposal("test_chain_id", prop) - require.NoError(t, err) - - // verify the same proposal - valid := pubKey.VerifyBytes(signBytes, prop.Signature) - require.True(t, valid) - - // serialize, deserialize and verify again.... - newProp := new(Proposal) - bs, err := cdc.MarshalBinaryLengthPrefixed(prop) - require.NoError(t, err) - err = cdc.UnmarshalBinaryLengthPrefixed(bs, &newProp) - require.NoError(t, err) - - // verify the transmitted proposal - newSignBytes := newProp.SignBytes("test_chain_id") - require.Equal(t, string(signBytes), string(newSignBytes)) - valid = pubKey.VerifyBytes(newSignBytes, newProp.Signature) - require.True(t, valid) + keyTypeCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, kt := range keyTypeCases { + privVal := NewMockPV(kt) + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + prop := NewProposal( + 4, 2, 2, + BlockID{[]byte{1, 2, 3}, PartSetHeader{777, []byte("proper")}}) + signBytes := prop.SignBytes("test_chain_id") + + // sign it + err = privVal.SignProposal("test_chain_id", prop) + require.NoError(t, err) + + // verify the same proposal + valid := pubKey.VerifyBytes(signBytes, prop.Signature) + require.True(t, valid) + + // serialize, deserialize and verify again.... + newProp := new(Proposal) + bs, err := cdc.MarshalBinaryLengthPrefixed(prop) + require.NoError(t, err) + err = cdc.UnmarshalBinaryLengthPrefixed(bs, &newProp) + require.NoError(t, err) + + // verify the transmitted proposal + newSignBytes := newProp.SignBytes("test_chain_id") + require.Equal(t, string(signBytes), string(newSignBytes)) + valid = pubKey.VerifyBytes(newSignBytes, newProp.Signature) + require.True(t, valid) + } } func BenchmarkProposalWriteSignBytes(b *testing.B) { @@ -83,63 +90,83 @@ func BenchmarkProposalWriteSignBytes(b *testing.B) { } func BenchmarkProposalSign(b *testing.B) { - privVal := NewMockPV() - for i := 0; i < b.N; i++ { - err := privVal.SignProposal("test_chain_id", testProposal) - if err != nil { - b.Error(err) + keyTypeCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, kt := range keyTypeCases { + privVal := NewMockPV(kt) + for i := 0; i < b.N; i++ { + err := privVal.SignProposal("test_chain_id", testProposal) + if err != nil { + b.Error(err) + } } } } func BenchmarkProposalVerifySignature(b *testing.B) { - privVal := NewMockPV() - err := privVal.SignProposal("test_chain_id", testProposal) - require.NoError(b, err) - pubKey, err := privVal.GetPubKey() - require.NoError(b, err) + keyTypeCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, kt := range keyTypeCases { + privVal := NewMockPV(kt) + err := privVal.SignProposal("test_chain_id", testProposal) + require.NoError(b, err) + pubKey, err := privVal.GetPubKey() + require.NoError(b, err) - for i := 0; i < b.N; i++ { - pubKey.VerifyBytes(testProposal.SignBytes("test_chain_id"), testProposal.Signature) + for i := 0; i < b.N; i++ { + pubKey.VerifyBytes(testProposal.SignBytes("test_chain_id"), testProposal.Signature) + } } } func TestProposalValidateBasic(t *testing.T) { - - privVal := NewMockPV() - testCases := []struct { - testName string - malleateProposal func(*Proposal) - expectErr bool - }{ - {"Good Proposal", func(p *Proposal) {}, false}, - {"Invalid Type", func(p *Proposal) { p.Type = PrecommitType }, true}, - {"Invalid Height", func(p *Proposal) { p.Height = -1 }, true}, - {"Invalid Round", func(p *Proposal) { p.Round = -1 }, true}, - {"Invalid POLRound", func(p *Proposal) { p.POLRound = -2 }, true}, - {"Invalid BlockId", func(p *Proposal) { - p.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}} - }, true}, - {"Invalid Signature", func(p *Proposal) { - p.Signature = make([]byte, 0) - }, true}, - {"Too big Signature", func(p *Proposal) { - p.Signature = make([]byte, MaxSignatureSize+1) - }, true}, + keyTypeCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, } - blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) - - for _, tc := range testCases { - tc := tc - t.Run(tc.testName, func(t *testing.T) { - prop := NewProposal( - 4, 2, 2, - blockID) - err := privVal.SignProposal("test_chain_id", prop) - require.NoError(t, err) - tc.malleateProposal(prop) - assert.Equal(t, tc.expectErr, prop.ValidateBasic() != nil, "Validate Basic had an unexpected result") - }) + for _, kt := range keyTypeCases { + privVal := NewMockPV(kt) + testCases := []struct { + testName string + malleateProposal func(*Proposal) + expectErr bool + }{ + {"Good Proposal", func(p *Proposal) {}, false}, + {"Invalid Type", func(p *Proposal) { p.Type = PrecommitType }, true}, + {"Invalid Height", func(p *Proposal) { p.Height = -1 }, true}, + {"Invalid Round", func(p *Proposal) { p.Round = -1 }, true}, + {"Invalid POLRound", func(p *Proposal) { p.POLRound = -2 }, true}, + {"Invalid BlockId", func(p *Proposal) { + p.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}} + }, true}, + {"Invalid Signature", func(p *Proposal) { + p.Signature = make([]byte, 0) + }, true}, + {"Too big Signature", func(p *Proposal) { + p.Signature = make([]byte, MaxSignatureSize+1) + }, true}, + } + blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + prop := NewProposal( + 4, 2, 2, + blockID) + err := privVal.SignProposal("test_chain_id", prop) + require.NoError(t, err) + tc.malleateProposal(prop) + assert.Equal(t, tc.expectErr, prop.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } } } diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 998b5d73d..c06499c21 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -134,24 +134,31 @@ func TestABCIHeader(t *testing.T) { } func TestABCIEvidence(t *testing.T) { - val := NewMockPV() - blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) - blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) - const chainID = "mychain" - pubKey, err := val.GetPubKey() - require.NoError(t, err) - ev := &DuplicateVoteEvidence{ - PubKey: pubKey, - VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID), - VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2), + keyTypeCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, kt := range keyTypeCases { + val := NewMockPV(kt) + blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) + blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) + const chainID = "mychain" + pubKey, err := val.GetPubKey() + require.NoError(t, err) + ev := &DuplicateVoteEvidence{ + PubKey: pubKey, + VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID), + VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2), + } + abciEv := TM2PB.Evidence( + ev, + ToVoterAll([]*Validator{NewValidator(pubKey, 10)}), + time.Now(), + ) + + assert.Equal(t, "duplicate/vote", abciEv.Type) } - abciEv := TM2PB.Evidence( - ev, - ToVoterAll([]*Validator{NewValidator(pubKey, 10)}), - time.Now(), - ) - - assert.Equal(t, "duplicate/vote", abciEv.Type) } type pubKeyEddie struct{} diff --git a/types/validator.go b/types/validator.go index aba549f08..7127bf0f4 100644 --- a/types/validator.go +++ b/types/validator.go @@ -152,7 +152,7 @@ func ValidatorFromProto(vp *tmproto.Validator) (*Validator, error) { // RandValidator returns a randomized validator, useful for testing. // UNSTABLE func RandValidator(randPower bool, minPower int64) (*Validator, PrivValidator) { - privVal := NewMockPV() + privVal := NewMockPV(randomKeyType()) stakingPower := minPower if randPower { stakingPower += int64(tmrand.Uint32()) diff --git a/types/validator_set_test.go b/types/validator_set_test.go index df8a288e1..a99f32dda 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -317,20 +317,21 @@ func randValidatorSet(numValidators int) *ValidatorSet { return NewValidatorSet(validators) } -func randValidatorWithMinMax(min, max int64) (*Validator, PrivValidator) { - privVal := NewMockPV() +func randValidatorWithMinMax(keyType PvKeyType, min, max int64) (*Validator, PrivValidator) { + privVal := NewMockPV(keyType) pubKey, _ := privVal.GetPubKey() val := NewValidator(pubKey, min+int64(tmrand.Uint64()%uint64(1+max-min))) val.ProposerPriority = min + tmrand.Int64()%max return val, privVal } -func randValidatorSetWithMinMax(numValidators int, min, max int64) (*ValidatorSet, map[string]PrivValidator) { +func randValidatorSetWithMinMax(keyType PvKeyType, numValidators int, min, max int64) (*ValidatorSet, + map[string]PrivValidator) { validators := make([]*Validator, numValidators) privMap := make(map[string]PrivValidator) var privVal PrivValidator for i := 0; i < numValidators; i++ { - validators[i], privVal = randValidatorWithMinMax(min, max) + validators[i], privVal = randValidatorWithMinMax(keyType, min, max) privMap[validators[i].Address.String()] = privVal } return NewValidatorSet(validators), privMap diff --git a/types/vote.go b/types/vote.go index 0cd9b4151..c016c12b6 100644 --- a/types/vote.go +++ b/types/vote.go @@ -12,9 +12,7 @@ import ( ) const ( - // MaxVoteBytes is a maximum vote size (including amino overhead). - MaxVoteBytes int64 = 255 - nilVoteStr string = "nil-Vote" + nilVoteStr string = "nil-Vote" ) var ( diff --git a/types/vote_test.go b/types/vote_test.go index c3bd7a16f..de424855a 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -142,33 +142,40 @@ func TestVoteProposalNotEq(t *testing.T) { } func TestVoteVerifySignature(t *testing.T) { - privVal := NewMockPV() - pubkey, err := privVal.GetPubKey() - require.NoError(t, err) - - vote := examplePrecommit() - signBytes := vote.SignBytes("test_chain_id") - - // sign it - err = privVal.SignVote("test_chain_id", vote) - require.NoError(t, err) - - // verify the same vote - valid := pubkey.VerifyBytes(vote.SignBytes("test_chain_id"), vote.Signature) - require.True(t, valid) - - // serialize, deserialize and verify again.... - precommit := new(Vote) - bs, err := cdc.MarshalBinaryLengthPrefixed(vote) - require.NoError(t, err) - err = cdc.UnmarshalBinaryLengthPrefixed(bs, &precommit) - require.NoError(t, err) - - // verify the transmitted vote - newSignBytes := precommit.SignBytes("test_chain_id") - require.Equal(t, string(signBytes), string(newSignBytes)) - valid = pubkey.VerifyBytes(newSignBytes, precommit.Signature) - require.True(t, valid) + testCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, tc := range testCases { + privVal := NewMockPV(tc) + pubkey, err := privVal.GetPubKey() + require.NoError(t, err) + + vote := examplePrecommit() + signBytes := vote.SignBytes("test_chain_id") + + // sign it + err = privVal.SignVote("test_chain_id", vote) + require.NoError(t, err) + + // verify the same vote + valid := pubkey.VerifyBytes(vote.SignBytes("test_chain_id"), vote.Signature) + require.True(t, valid) + + // serialize, deserialize and verify again.... + precommit := new(Vote) + bs, err := cdc.MarshalBinaryLengthPrefixed(vote) + require.NoError(t, err) + err = cdc.UnmarshalBinaryLengthPrefixed(bs, &precommit) + require.NoError(t, err) + + // verify the transmitted vote + newSignBytes := precommit.SignBytes("test_chain_id") + require.Equal(t, string(signBytes), string(newSignBytes)) + valid = pubkey.VerifyBytes(newSignBytes, precommit.Signature) + require.True(t, valid) + } } func TestIsVoteTypeValid(t *testing.T) { @@ -193,21 +200,28 @@ func TestIsVoteTypeValid(t *testing.T) { } func TestVoteVerify(t *testing.T) { - privVal := NewMockPV() - pubkey, err := privVal.GetPubKey() - require.NoError(t, err) + testCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, tc := range testCases { + privVal := NewMockPV(tc) + pubkey, err := privVal.GetPubKey() + require.NoError(t, err) - vote := examplePrevote() - vote.ValidatorAddress = pubkey.Address() + vote := examplePrevote() + vote.ValidatorAddress = pubkey.Address() - err = vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey()) - if assert.Error(t, err) { - assert.Equal(t, ErrVoteInvalidValidatorAddress, err) - } + err = vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey()) + if assert.Error(t, err) { + assert.Equal(t, ErrVoteInvalidValidatorAddress, err) + } - err = vote.Verify("test_chain_id", pubkey) - if assert.Error(t, err) { - assert.Equal(t, ErrVoteInvalidSignature, err) + err = vote.Verify("test_chain_id", pubkey) + if assert.Error(t, err) { + assert.Equal(t, ErrVoteInvalidSignature, err) + } } } @@ -232,14 +246,21 @@ func TestMaxVoteBytes(t *testing.T) { }, } - privVal := NewMockPV() - err := privVal.SignVote("test_chain_id", vote) - require.NoError(t, err) + testCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, + } + for _, tc := range testCases { + privVal := NewMockPV(tc) + err := privVal.SignVote("test_chain_id", vote) + require.NoError(t, err) - bz, err := cdc.MarshalBinaryLengthPrefixed(vote) - require.NoError(t, err) + bz, err := cdc.MarshalBinaryLengthPrefixed(vote) + require.NoError(t, err) - assert.EqualValues(t, MaxVoteBytes, len(bz)) + assert.True(t, MaxCommitBytes >= len(bz)) + } } func TestVoteString(t *testing.T) { @@ -257,60 +278,74 @@ func TestVoteString(t *testing.T) { } func TestVoteValidateBasic(t *testing.T) { - privVal := NewMockPV() - - testCases := []struct { - testName string - malleateVote func(*Vote) - expectErr bool - }{ - {"Good Vote", func(v *Vote) {}, false}, - {"Negative Height", func(v *Vote) { v.Height = -1 }, true}, - {"Negative Round", func(v *Vote) { v.Round = -1 }, true}, - {"Invalid BlockID", func(v *Vote) { - v.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}} - }, true}, - {"Invalid Address", func(v *Vote) { v.ValidatorAddress = make([]byte, 1) }, true}, - {"Invalid ValidatorIndex", func(v *Vote) { v.ValidatorIndex = -1 }, true}, - {"Invalid Signature", func(v *Vote) { v.Signature = nil }, true}, - {"Too big Signature", func(v *Vote) { v.Signature = make([]byte, MaxSignatureSize+1) }, true}, + keyTypeCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, } - for _, tc := range testCases { - tc := tc - t.Run(tc.testName, func(t *testing.T) { - vote := examplePrecommit() - err := privVal.SignVote("test_chain_id", vote) - require.NoError(t, err) - tc.malleateVote(vote) - assert.Equal(t, tc.expectErr, vote.ValidateBasic() != nil, "Validate Basic had an unexpected result") - }) + for _, kt := range keyTypeCases { + privVal := NewMockPV(kt) + + testCases := []struct { + testName string + malleateVote func(*Vote) + expectErr bool + }{ + {"Good Vote", func(v *Vote) {}, false}, + {"Negative Height", func(v *Vote) { v.Height = -1 }, true}, + {"Negative Round", func(v *Vote) { v.Round = -1 }, true}, + {"Invalid BlockID", func(v *Vote) { + v.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}} + }, true}, + {"Invalid Address", func(v *Vote) { v.ValidatorAddress = make([]byte, 1) }, true}, + {"Invalid ValidatorIndex", func(v *Vote) { v.ValidatorIndex = -1 }, true}, + {"Invalid Signature", func(v *Vote) { v.Signature = nil }, true}, + {"Too big Signature", func(v *Vote) { v.Signature = make([]byte, MaxSignatureSize+1) }, true}, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + vote := examplePrecommit() + err := privVal.SignVote("test_chain_id", vote) + require.NoError(t, err) + tc.malleateVote(vote) + assert.Equal(t, tc.expectErr, vote.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } } } func TestVoteProtobuf(t *testing.T) { - privVal := NewMockPV() - vote := examplePrecommit() - err := privVal.SignVote("test_chain_id", vote) - require.NoError(t, err) - - testCases := []struct { - msg string - v1 *Vote - expPass bool - }{ - {"success", vote, true}, - {"fail vote validate basic", &Vote{}, false}, - {"failure nil", nil, false}, + keyTypeCases := []PvKeyType{ + PvKeyEd25519, + PvKeyComposite, + PvKeyBLS, } - for _, tc := range testCases { - protoProposal := tc.v1.ToProto() - - v, err := VoteFromProto(protoProposal) - if tc.expPass { - require.NoError(t, err) - require.Equal(t, tc.v1, v, tc.msg) - } else { - require.Error(t, err) + for _, kt := range keyTypeCases { + privVal := NewMockPV(kt) + vote := examplePrecommit() + err := privVal.SignVote("test_chain_id", vote) + require.NoError(t, err) + + testCases := []struct { + msg string + v1 *Vote + expPass bool + }{ + {"success", vote, true}, + {"fail vote validate basic", &Vote{}, false}, + {"failure nil", nil, false}, + } + for _, tc := range testCases { + protoProposal := tc.v1.ToProto() + + v, err := VoteFromProto(protoProposal) + if tc.expPass { + require.NoError(t, err) + require.Equal(t, tc.v1, v, tc.msg) + } else { + require.Error(t, err) + } } } } diff --git a/types/voter_set.go b/types/voter_set.go index e24a4b9a1..f0e7715fe 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "math/big" + "math/rand" "sort" "strings" @@ -613,6 +614,17 @@ func MakeRoundHash(proofHash []byte, height int64, round int) []byte { return hash.Sum(nil) } +func randomKeyType() PvKeyType { + r := rand.Uint32() % 2 + switch r { + case 0: + return PvKeyEd25519 + case 1: + return PvKeyComposite + } + return PvKeyEd25519 +} + // RandValidatorSet returns a randomized validator set, useful for testing. // NOTE: PrivValidator are in order. // UNSTABLE diff --git a/types/voter_set_test.go b/types/voter_set_test.go index e09db86c5..1e1f0e15d 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -173,7 +173,7 @@ func TestSelectVoterReasonableStakingPower(t *testing.T) { } func findLargestStakingPowerGap(t *testing.T, loopCount int, minMaxRate int, maxVoters int) { - valSet, privMap := randValidatorSetWithMinMax(30, 100, 100*int64(minMaxRate)) + valSet, privMap := randValidatorSetWithMinMax(PvKeyEd25519, 30, 100, 100*int64(minMaxRate)) genDoc := &GenesisDoc{ GenesisTime: tmtime.Now(), ChainID: "tendermint-test", @@ -219,7 +219,7 @@ func TestSelectVoterMaxVarious(t *testing.T) { t.Logf("<<< min: 100, max: %d >>>", 100*minMaxRate) for validators := 16; validators <= 256; validators *= 4 { for voters := 1; voters <= validators; voters += 10 { - valSet, _ := randValidatorSetWithMinMax(validators, 100, 100*int64(minMaxRate)) + valSet, _ := randValidatorSetWithMinMax(PvKeyEd25519, validators, 100, 100*int64(minMaxRate)) voterSet := SelectVoter(valSet, []byte{byte(hash)}, &VoterParams{int32(voters), 20}) if voterSet.Size() < voters { t.Logf("Cannot elect voters up to MaxVoters: validators=%d, MaxVoters=%d, actual voters=%d", @@ -328,7 +328,7 @@ func electVotersForLoop(t *testing.T, hash []byte, valSet *ValidatorSet, privMap func TestCalVotersNum2(t *testing.T) { t.Skip("take too much time and no longer using accuracy") - valSet, privMap := randValidatorSetWithMinMax(100, 100, 10000) + valSet, privMap := randValidatorSetWithMinMax(PvKeyEd25519, 100, 100, 10000) byzantinePercent := int32(20) byzantines := makeByzantine(valSet, float64(byzantinePercent)/100) genDoc := &GenesisDoc{ From 890c1f3a5490e832e07f5bea0cbec05ce1c7f58a Mon Sep 17 00:00:00 2001 From: egonspace Date: Wed, 11 Nov 2020 17:33:36 +0900 Subject: [PATCH 2/7] fix: lint error --- types/block_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/types/block_test.go b/types/block_test.go index 1c4fc1aec..c0011d4db 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -388,7 +388,7 @@ func TestCommitSigNumOfBytes(t *testing.T) { blockID := BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} - chainID := "mychain" + chainID := "mychain1" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) vote1 := &Vote{ @@ -458,7 +458,7 @@ func TestMaxCommitBytes(t *testing.T) { blockID := BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} - chainID := "mychain" + chainID := "mychain2" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) vote1 := &Vote{ @@ -516,7 +516,7 @@ func TestMaxCommitBytesMany(t *testing.T) { blockID := BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} - chainID := "mychain" + chainID := "mychain3" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) for i := 0; i < commitCount; i++ { @@ -577,7 +577,7 @@ func TestMaxCommitBytesMixed(t *testing.T) { blockID := BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{math.MaxInt32, tmrand.Bytes(tmhash.Size)}} - chainID := "mychain" + chainID := "mychain4" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) for i := 0; i < commitCount; i++ { @@ -622,7 +622,7 @@ func TestBlockMaxDataBytes(t *testing.T) { val[2] = newValidator(pub3.Address(), 300) valSet := NewValidatorSet(val) blockID := makeBlockIDRandom() - chainID := "mychain" + chainID := "mychain5" vote1, _ := MakeVote(1, blockID, valSet, pv1, chainID, tmtime.Now()) vote2, _ := MakeVote(1, blockID, valSet, pv2, chainID, tmtime.Now()) vote3, _ := MakeVote(1, blockID, valSet, pv3, chainID, tmtime.Now()) From 48f2a75b67f2243a98a4ea84c480ca551356f961 Mon Sep 17 00:00:00 2001 From: egonspace Date: Fri, 13 Nov 2020 09:52:51 +0900 Subject: [PATCH 3/7] fix: apply review comments --- node/node_test.go | 4 +- privval/signer_client_test.go | 4 +- privval/signer_listener_endpoint_test.go | 4 +- state/state_test.go | 2 +- state/validation_test.go | 2 +- types/block.go | 14 ++-- types/block_test.go | 24 +++---- types/evidence.go | 18 ++--- types/evidence_test.go | 56 ++++------------ types/priv_validator.go | 35 +++++----- types/priv_validator_test.go | 29 ++++++++- types/proposal_test.go | 83 ++++++++++++++---------- types/protobuf_test.go | 9 +-- types/validator_set_test.go | 4 +- types/vote_test.go | 51 ++++----------- types/voter_set.go | 11 ---- types/voter_set_test.go | 6 +- 17 files changed, 162 insertions(+), 194 deletions(-) diff --git a/node/node_test.go b/node/node_test.go index 9aeadb097..9986270ff 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -146,7 +146,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { signerServer := privval.NewSignerServer( dialerEndpoint, config.ChainID(), - types.NewMockPV(types.PvKeyEd25519), + types.NewMockPV(types.PrivKeyEd25519), ) go func() { @@ -192,7 +192,7 @@ func TestNodeSetPrivValIPC(t *testing.T) { pvsc := privval.NewSignerServer( dialerEndpoint, config.ChainID(), - types.NewMockPV(types.PvKeyEd25519), + types.NewMockPV(types.PrivKeyEd25519), ) go func() { diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 5cc796135..a23235b6d 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -265,8 +265,8 @@ func brokenHandler(privVal types.PrivValidator, request SignerMessage, chainID s func TestSignerUnexpectedResponse(t *testing.T) { for _, tc := range getSignerTestCases(t, false) { // this should be executed before SignerServer starts to avoid race condition - tc.signerServer.privVal = types.NewMockPV(types.PvKeyEd25519) - tc.mockPV = types.NewMockPV(types.PvKeyEd25519) + tc.signerServer.privVal = types.NewMockPV(types.PrivKeyEd25519) + tc.mockPV = types.NewMockPV(types.PrivKeyEd25519) tc.signerServer.SetRequestHandler(brokenHandler) tc.signerServer.Start() diff --git a/privval/signer_listener_endpoint_test.go b/privval/signer_listener_endpoint_test.go index bdacb0dc7..9562b7663 100644 --- a/privval/signer_listener_endpoint_test.go +++ b/privval/signer_listener_endpoint_test.go @@ -68,7 +68,7 @@ func TestSignerRemoteRetryTCPOnly(t *testing.T) { SignerDialerEndpointConnRetries(retries)(dialerEndpoint) chainID := tmrand.Str(12) - mockPV := types.NewMockPV(types.PvKeyEd25519) + mockPV := types.NewMockPV(types.PrivKeyEd25519) signerServer := NewSignerServer(dialerEndpoint, chainID, mockPV) err = signerServer.Start() @@ -88,7 +88,7 @@ func TestRetryConnToRemoteSigner(t *testing.T) { var ( logger = log.TestingLogger() chainID = tmrand.Str(12) - mockPV = types.NewMockPV(types.PvKeyEd25519) + mockPV = types.NewMockPV(types.PrivKeyEd25519) endpointIsOpenCh = make(chan struct{}) thisConnTimeout = testTimeoutReadWrite listenerEndpoint = newSignerListenerEndpoint(logger, tc.addr, thisConnTimeout) diff --git a/state/state_test.go b/state/state_test.go index 3a7d77565..61f198e5c 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -407,7 +407,7 @@ func TestProposerFrequency(t *testing.T) { // make sure votePower > 0 votePower := int64(tmrand.Int()%maxPower) + 1 totalVotePower += votePower - privVal := types.NewMockPV(types.PvKeyEd25519) + privVal := types.NewMockPV(types.PrivKeyEd25519) pubKey, err := privVal.GetPubKey() require.NoError(t, err) val := types.NewValidator(pubKey, votePower) diff --git a/state/validation_test.go b/state/validation_test.go index a6d5e31d7..445648c9d 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -105,7 +105,7 @@ func TestValidateBlockCommit(t *testing.T) { ) lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) wrongSigsCommit := types.NewCommit(1, 0, types.BlockID{}, nil) - badPrivVal := types.NewMockPV(types.PvKeyEd25519) + badPrivVal := types.NewMockPV(types.PrivKeyEd25519) for height := int64(1); height < validationTestsStopHeight; height++ { proposerAddr := state.Validators.SelectProposer([]byte{}, height, 0).Address diff --git a/types/block.go b/types/block.go index ea1ccb2f1..554acd022 100644 --- a/types/block.go +++ b/types/block.go @@ -235,7 +235,7 @@ func (b *Block) Unmarshal(bs []byte) error { func MaxDataBytes(maxBytes int64, commit *Commit, evidences []Evidence) int64 { evidenceBytes := int64(0) for _, ev := range evidences { - evidenceBytes += MaxEvidenceBytes(PvKeyTypeByPubKey(ev.PublicKey())) + evidenceBytes += MaxEvidenceBytes(PrivKeyTypeByPubKey(ev.PublicKey())) } maxDataBytes := maxBytes - MaxAminoOverheadForBlock - @@ -579,18 +579,16 @@ func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time) Commi } func (cs CommitSig) MaxCommitSigBytes() int64 { + commitSigBytesBase := BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) switch len(cs.Signature) { case 0: - return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + return commitSigBytesBase case ed25519.SignatureSize: - return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + - Bytes64AminoHeadLen + int64(len(cs.Signature)) + return commitSigBytesBase + Bytes64AminoHeadLen + int64(len(cs.Signature)) case bls.SignatureSize: - return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + - Bytes96AminoHeadLen + int64(len(cs.Signature)) + return commitSigBytesBase + Bytes96AminoHeadLen + int64(len(cs.Signature)) } - return BlockIDFlagLen + TimestampMaxLen + Bytes20AminoHeadLen + int64(len(cs.ValidatorAddress.Bytes())) + - Bytes96AminoHeadLen + int64(len(cs.Signature)) + panic(fmt.Sprintf("unknown signature size")) } // ForBlock returns true if CommitSig is for the block. diff --git a/types/block_test.go b/types/block_test.go index c0011d4db..f6b02dfe6 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -378,9 +378,9 @@ func hexBytesFromString(s string) bytes.HexBytes { } func TestCommitSigNumOfBytes(t *testing.T) { - pv1 := NewMockPV(PvKeyEd25519) - pv2 := NewMockPV(PvKeyComposite) - pv3 := NewMockPV(PvKeyBLS) + pv1 := NewMockPV(PrivKeyEd25519) + pv2 := NewMockPV(PrivKeyComposite) + pv3 := NewMockPV(PrivKeyBLS) pub1, _ := pv1.GetPubKey() pub2, _ := pv2.GetPubKey() @@ -447,9 +447,9 @@ func TestCommitSigNumOfBytes(t *testing.T) { } func TestMaxCommitBytes(t *testing.T) { - pv1 := NewMockPV(PvKeyEd25519) - pv2 := NewMockPV(PvKeyComposite) - pv3 := NewMockPV(PvKeyComposite) + pv1 := NewMockPV(PrivKeyEd25519) + pv2 := NewMockPV(PrivKeyComposite) + pv3 := NewMockPV(PrivKeyComposite) pub1, _ := pv1.GetPubKey() pub2, _ := pv2.GetPubKey() @@ -520,7 +520,7 @@ func TestMaxCommitBytesMany(t *testing.T) { timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) for i := 0; i < commitCount; i++ { - pv := NewMockPV(PvKeyEd25519) + pv := NewMockPV(PrivKeyEd25519) pub, _ := pv.GetPubKey() vote := &Vote{ ValidatorAddress: pub.Address(), @@ -549,7 +549,7 @@ func TestMaxCommitBytesAggregated(t *testing.T) { timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) for i := 0; i < commitCount; i++ { - pv := NewMockPV(PvKeyComposite) + pv := NewMockPV(PrivKeyComposite) pub, _ := pv.GetPubKey() vote := &Vote{ ValidatorAddress: pub.Address(), @@ -594,7 +594,7 @@ func TestMaxCommitBytesMixed(t *testing.T) { BlockID: blockID, } // sign only if key type is ed25519 - if keyType == PvKeyEd25519 { + if keyType == PrivKeyEd25519 { assert.NoError(t, pv.SignVote(chainID, vote)) } commitSig[i] = NewCommitSigForBlock(vote.Signature, pub.Address(), timestamp) @@ -608,9 +608,9 @@ func TestMaxCommitBytesMixed(t *testing.T) { } func TestBlockMaxDataBytes(t *testing.T) { - pv1 := NewMockPV(PvKeyEd25519) - pv2 := NewMockPV(PvKeyComposite) - pv3 := NewMockPV(PvKeyBLS) + pv1 := NewMockPV(PrivKeyEd25519) + pv2 := NewMockPV(PrivKeyComposite) + pv3 := NewMockPV(PrivKeyBLS) pub1, _ := pv1.GetPubKey() pub2, _ := pv2.GetPubKey() diff --git a/types/evidence.go b/types/evidence.go index 4f7aaa84e..6af94b37d 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -16,14 +16,14 @@ import ( tmproto "github.com/tendermint/tendermint/proto/types" ) -func MaxEvidenceBytes(keyType PvKeyType) int64 { +func MaxEvidenceBytes(keyType PrivKeyType) int64 { // MaxEvidenceBytes is a maximum size of any evidence (including amino overhead). switch keyType { - case PvKeyEd25519: + case PrivKeyEd25519: return 483 - case PvKeyComposite: + case PrivKeyComposite: return 608 - case PvKeyBLS: + case PrivKeyBLS: return 563 } panic(fmt.Sprintf("unknown private key type: %d", keyType)) @@ -211,8 +211,8 @@ const ( // See https://github.com/tendermint/tendermint/issues/2590 func MaxEvidencePerBlock(blockMaxBytes int64) (int64, int64) { maxBytes := blockMaxBytes / MaxEvidenceBytesDenominator - // Calculate based on PvKeyComposite, where evidence is the largest. - maxNum := maxBytes / MaxEvidenceBytes(PvKeyComposite) + // Calculate based on PrivKeyComposite, where evidence is the largest. + maxNum := maxBytes / MaxEvidenceBytes(PrivKeyComposite) return maxNum, maxBytes } @@ -401,6 +401,7 @@ type MockEvidence struct { EvidenceHeight int64 EvidenceTime time.Time EvidenceAddress []byte + EvidencePubKey crypto.PubKey } var _ Evidence = &MockEvidence{} @@ -410,13 +411,14 @@ func NewMockEvidence(height int64, eTime time.Time, idx int, address []byte) Moc return MockEvidence{ EvidenceHeight: height, EvidenceTime: eTime, - EvidenceAddress: address} + EvidenceAddress: address, + EvidencePubKey: ed25519.GenPrivKey().PubKey()} } func (e MockEvidence) Height() int64 { return e.EvidenceHeight } func (e MockEvidence) Time() time.Time { return e.EvidenceTime } func (e MockEvidence) Address() []byte { return e.EvidenceAddress } -func (e MockEvidence) PublicKey() crypto.PubKey { return ed25519.GenPrivKey().PubKey() } +func (e MockEvidence) PublicKey() crypto.PubKey { return e.EvidencePubKey } func (e MockEvidence) Hash() []byte { return []byte(fmt.Sprintf("%d-%x-%s", diff --git a/types/evidence_test.go b/types/evidence_test.go index 3ca63be7e..727d1c9be 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -39,12 +39,7 @@ func makeVote( } func TestEvidence(t *testing.T) { - keyTypes := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypes { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { val := NewMockPV(kt) val2 := NewMockPV(kt) @@ -87,46 +82,31 @@ func TestEvidence(t *testing.T) { assert.NotNil(t, ev.Verify(chainID, pubKey), "evidence should be invalid") } } - } + }) } func TestDuplicatedVoteEvidence(t *testing.T) { - keyTypes := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypes { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { ev := randomDuplicatedVoteEvidence(kt, t) assert.True(t, ev.Equal(ev)) assert.False(t, ev.Equal(&DuplicateVoteEvidence{})) - } + }) } func TestEvidenceList(t *testing.T) { - keyTypes := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypes { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { ev := randomDuplicatedVoteEvidence(kt, t) evl := EvidenceList([]Evidence{ev}) assert.NotNil(t, evl.Hash()) assert.True(t, evl.Has(ev)) assert.False(t, evl.Has(&DuplicateVoteEvidence{})) - } + }) } func TestMaxEvidenceBytes(t *testing.T) { - keyTypes := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypes { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { val := NewMockPV(kt) randomPartHash := rand.Bytes(int(rand.Uint32() % 1000)) randomHash1 := rand.Bytes(int(rand.Uint32() % 1000)) @@ -145,10 +125,10 @@ func TestMaxEvidenceBytes(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, MaxEvidenceBytes(kt), len(bz)) - } + }) } -func randomDuplicatedVoteEvidence(keyType PvKeyType, t *testing.T) *DuplicateVoteEvidence { +func randomDuplicatedVoteEvidence(keyType PrivKeyType, t *testing.T) *DuplicateVoteEvidence { val := NewMockPV(keyType) blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) @@ -160,12 +140,7 @@ func randomDuplicatedVoteEvidence(keyType PvKeyType, t *testing.T) *DuplicateVot } func TestDuplicateVoteEvidenceValidation(t *testing.T) { - keyTypes := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypes { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { val := NewMockPV(kt) blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) @@ -203,7 +178,7 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) } - } + }) } func TestMockGoodEvidenceValidateBasic(t *testing.T) { @@ -218,12 +193,7 @@ func TestMockBadEvidenceValidateBasic(t *testing.T) { func TestEvidenceProto(t *testing.T) { // -------- Votes -------- - keyTypes := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypes { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { val := NewMockPV(kt) blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) @@ -261,5 +231,5 @@ func TestEvidenceProto(t *testing.T) { require.Equal(t, tt.evidence, evi, tt.testName) }) } - } + }) } diff --git a/types/priv_validator.go b/types/priv_validator.go index 3d04d22fa..74a50e1c5 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -62,37 +62,38 @@ type MockPV struct { breakVoteSigning bool } -type PvKeyType int +type PrivKeyType int const ( - PvKeyEd25519 PvKeyType = iota - PvKeyComposite - PvKeyBLS + PrivKeyEd25519 PrivKeyType = iota + PrivKeyComposite + PrivKeyBLS ) -func NewMockPV(keyType PvKeyType) MockPV { +func NewMockPV(keyType PrivKeyType) MockPV { switch keyType { - case PvKeyEd25519: + case PrivKeyEd25519: return MockPV{ed25519.GenPrivKey(), false, false} - case PvKeyComposite: + case PrivKeyComposite: return MockPV{composite.GenPrivKey(), false, false} - case PvKeyBLS: + case PrivKeyBLS: return MockPV{bls.GenPrivKey(), false, false} default: panic(fmt.Sprintf("known pv key type: %d", keyType)) } } -func PvKeyTypeByPubKey(pubKey crypto.PubKey) PvKeyType { - switch len(pubKey.Bytes()) { - case 37: - return PvKeyEd25519 - case 90: - return PvKeyComposite - case 53: - return PvKeyBLS +func PrivKeyTypeByPubKey(pubKey crypto.PubKey) PrivKeyType { + switch pubKey.(type) { + case ed25519.PubKeyEd25519: + return PrivKeyEd25519 + case composite.PubKeyComposite: + return PrivKeyComposite + case bls.PubKeyBLS12: + return PrivKeyBLS + default: + panic(fmt.Sprintf("unknown address len: %d", len(pubKey.Bytes()))) } - panic(fmt.Sprintf("unknown address len: %d", len(pubKey.Bytes()))) } // NewMockPVWithParams allows one to create a MockPV instance, but with finer diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 2c1604207..a9f013c93 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -4,13 +4,40 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/rand" ) +func forAllPrivKeyTypes(t *testing.T, exec func(t *testing.T, name string, keyType PrivKeyType)) { + keyNameAndTypes := []struct { + name string + keyType PrivKeyType + }{ + {name: "ed25512", keyType: PrivKeyEd25519}, + {name: "composite", keyType: PrivKeyComposite}, + {name: "bls", keyType: PrivKeyBLS}} + for _, knt := range keyNameAndTypes { + t.Run(knt.name, func(t *testing.T) { + exec(t, knt.name, knt.keyType) + }) + } +} + +func randomKeyType() PrivKeyType { + r := rand.Uint32() % 2 + switch r { + case 0: + return PrivKeyEd25519 + case 1: + return PrivKeyComposite + } + return PrivKeyEd25519 +} + func TestPvKeyTypeByAddress(t *testing.T) { for i := 0; i < 1000; i++ { keyType := randomKeyType() pv := NewMockPV(keyType) pubKey, _ := pv.GetPubKey() - assert.True(t, keyType == PvKeyTypeByPubKey(pubKey)) + assert.True(t, keyType == PrivKeyTypeByPubKey(pubKey)) } } diff --git a/types/proposal_test.go b/types/proposal_test.go index dc3bba0ca..e7d001b9f 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -45,10 +45,10 @@ func TestProposalString(t *testing.T) { } func TestProposalVerifySignature(t *testing.T) { - keyTypeCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, + keyTypeCases := []PrivKeyType{ + PrivKeyEd25519, + PrivKeyComposite, + PrivKeyBLS, } for _, kt := range keyTypeCases { privVal := NewMockPV(kt) @@ -89,47 +89,58 @@ func BenchmarkProposalWriteSignBytes(b *testing.B) { } } -func BenchmarkProposalSign(b *testing.B) { - keyTypeCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypeCases { - privVal := NewMockPV(kt) - for i := 0; i < b.N; i++ { - err := privVal.SignProposal("test_chain_id", testProposal) - if err != nil { - b.Error(err) - } +func BenchmarkProposalSignEd25519(b *testing.B) { + benchmarkProposalSign(b, PrivKeyEd25519) +} + +func BenchmarkProposalSignComposite(b *testing.B) { + benchmarkProposalSign(b, PrivKeyComposite) +} + +func BenchmarkProposalSignBLS(b *testing.B) { + benchmarkProposalSign(b, PrivKeyBLS) +} + +func benchmarkProposalSign(b *testing.B, keyType PrivKeyType) { + privVal := NewMockPV(keyType) + for i := 0; i < b.N; i++ { + err := privVal.SignProposal("test_chain_id", testProposal) + if err != nil { + b.Error(err) } } } -func BenchmarkProposalVerifySignature(b *testing.B) { - keyTypeCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypeCases { - privVal := NewMockPV(kt) - err := privVal.SignProposal("test_chain_id", testProposal) - require.NoError(b, err) - pubKey, err := privVal.GetPubKey() - require.NoError(b, err) +func BenchmarkProposalVerifySignatureEd25519(b *testing.B) { + benchmarkProposalVerifySignature(b, PrivKeyEd25519) +} - for i := 0; i < b.N; i++ { - pubKey.VerifyBytes(testProposal.SignBytes("test_chain_id"), testProposal.Signature) - } +func BenchmarkProposalVerifySignatureComposite(b *testing.B) { + benchmarkProposalVerifySignature(b, PrivKeyComposite) +} + +func BenchmarkProposalVerifySignatureBLS(b *testing.B) { + benchmarkProposalVerifySignature(b, PrivKeyBLS) +} + +func benchmarkProposalVerifySignature(b *testing.B, keyType PrivKeyType) { + privVal := NewMockPV(keyType) + err := privVal.SignProposal("test_chain_id", testProposal) + require.NoError(b, err) + pubKey, err := privVal.GetPubKey() + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + pubKey.VerifyBytes(testProposal.SignBytes("test_chain_id"), testProposal.Signature) } } func TestProposalValidateBasic(t *testing.T) { - keyTypeCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, + keyTypeCases := []PrivKeyType{ + PrivKeyEd25519, + PrivKeyComposite, + PrivKeyBLS, } for _, kt := range keyTypeCases { privVal := NewMockPV(kt) diff --git a/types/protobuf_test.go b/types/protobuf_test.go index c06499c21..400e5010d 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -134,12 +134,7 @@ func TestABCIHeader(t *testing.T) { } func TestABCIEvidence(t *testing.T) { - keyTypeCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypeCases { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { val := NewMockPV(kt) blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) @@ -158,7 +153,7 @@ func TestABCIEvidence(t *testing.T) { ) assert.Equal(t, "duplicate/vote", abciEv.Type) - } + }) } type pubKeyEddie struct{} diff --git a/types/validator_set_test.go b/types/validator_set_test.go index a99f32dda..4e1b1ee11 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -317,7 +317,7 @@ func randValidatorSet(numValidators int) *ValidatorSet { return NewValidatorSet(validators) } -func randValidatorWithMinMax(keyType PvKeyType, min, max int64) (*Validator, PrivValidator) { +func randValidatorWithMinMax(keyType PrivKeyType, min, max int64) (*Validator, PrivValidator) { privVal := NewMockPV(keyType) pubKey, _ := privVal.GetPubKey() val := NewValidator(pubKey, min+int64(tmrand.Uint64()%uint64(1+max-min))) @@ -325,7 +325,7 @@ func randValidatorWithMinMax(keyType PvKeyType, min, max int64) (*Validator, Pri return val, privVal } -func randValidatorSetWithMinMax(keyType PvKeyType, numValidators int, min, max int64) (*ValidatorSet, +func randValidatorSetWithMinMax(keyType PrivKeyType, numValidators int, min, max int64) (*ValidatorSet, map[string]PrivValidator) { validators := make([]*Validator, numValidators) privMap := make(map[string]PrivValidator) diff --git a/types/vote_test.go b/types/vote_test.go index de424855a..9a19eb79a 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -142,13 +142,8 @@ func TestVoteProposalNotEq(t *testing.T) { } func TestVoteVerifySignature(t *testing.T) { - testCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, tc := range testCases { - privVal := NewMockPV(tc) + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { + privVal := NewMockPV(kt) pubkey, err := privVal.GetPubKey() require.NoError(t, err) @@ -175,7 +170,7 @@ func TestVoteVerifySignature(t *testing.T) { require.Equal(t, string(signBytes), string(newSignBytes)) valid = pubkey.VerifyBytes(newSignBytes, precommit.Signature) require.True(t, valid) - } + }) } func TestIsVoteTypeValid(t *testing.T) { @@ -200,13 +195,8 @@ func TestIsVoteTypeValid(t *testing.T) { } func TestVoteVerify(t *testing.T) { - testCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, tc := range testCases { - privVal := NewMockPV(tc) + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { + privVal := NewMockPV(kt) pubkey, err := privVal.GetPubKey() require.NoError(t, err) @@ -222,7 +212,7 @@ func TestVoteVerify(t *testing.T) { if assert.Error(t, err) { assert.Equal(t, ErrVoteInvalidSignature, err) } - } + }) } func TestMaxVoteBytes(t *testing.T) { @@ -246,13 +236,8 @@ func TestMaxVoteBytes(t *testing.T) { }, } - testCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, tc := range testCases { - privVal := NewMockPV(tc) + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { + privVal := NewMockPV(kt) err := privVal.SignVote("test_chain_id", vote) require.NoError(t, err) @@ -260,7 +245,7 @@ func TestMaxVoteBytes(t *testing.T) { require.NoError(t, err) assert.True(t, MaxCommitBytes >= len(bz)) - } + }) } func TestVoteString(t *testing.T) { @@ -278,12 +263,7 @@ func TestVoteString(t *testing.T) { } func TestVoteValidateBasic(t *testing.T) { - keyTypeCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypeCases { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { privVal := NewMockPV(kt) testCases := []struct { @@ -312,16 +292,11 @@ func TestVoteValidateBasic(t *testing.T) { assert.Equal(t, tc.expectErr, vote.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) } - } + }) } func TestVoteProtobuf(t *testing.T) { - keyTypeCases := []PvKeyType{ - PvKeyEd25519, - PvKeyComposite, - PvKeyBLS, - } - for _, kt := range keyTypeCases { + forAllPrivKeyTypes(t, func(t *testing.T, name string, kt PrivKeyType) { privVal := NewMockPV(kt) vote := examplePrecommit() err := privVal.SignVote("test_chain_id", vote) @@ -347,5 +322,5 @@ func TestVoteProtobuf(t *testing.T) { require.Error(t, err) } } - } + }) } diff --git a/types/voter_set.go b/types/voter_set.go index f0e7715fe..019712caa 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -614,17 +614,6 @@ func MakeRoundHash(proofHash []byte, height int64, round int) []byte { return hash.Sum(nil) } -func randomKeyType() PvKeyType { - r := rand.Uint32() % 2 - switch r { - case 0: - return PvKeyEd25519 - case 1: - return PvKeyComposite - } - return PvKeyEd25519 -} - // RandValidatorSet returns a randomized validator set, useful for testing. // NOTE: PrivValidator are in order. // UNSTABLE diff --git a/types/voter_set_test.go b/types/voter_set_test.go index 1e1f0e15d..5b88f67d6 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -173,7 +173,7 @@ func TestSelectVoterReasonableStakingPower(t *testing.T) { } func findLargestStakingPowerGap(t *testing.T, loopCount int, minMaxRate int, maxVoters int) { - valSet, privMap := randValidatorSetWithMinMax(PvKeyEd25519, 30, 100, 100*int64(minMaxRate)) + valSet, privMap := randValidatorSetWithMinMax(PrivKeyEd25519, 30, 100, 100*int64(minMaxRate)) genDoc := &GenesisDoc{ GenesisTime: tmtime.Now(), ChainID: "tendermint-test", @@ -219,7 +219,7 @@ func TestSelectVoterMaxVarious(t *testing.T) { t.Logf("<<< min: 100, max: %d >>>", 100*minMaxRate) for validators := 16; validators <= 256; validators *= 4 { for voters := 1; voters <= validators; voters += 10 { - valSet, _ := randValidatorSetWithMinMax(PvKeyEd25519, validators, 100, 100*int64(minMaxRate)) + valSet, _ := randValidatorSetWithMinMax(PrivKeyEd25519, validators, 100, 100*int64(minMaxRate)) voterSet := SelectVoter(valSet, []byte{byte(hash)}, &VoterParams{int32(voters), 20}) if voterSet.Size() < voters { t.Logf("Cannot elect voters up to MaxVoters: validators=%d, MaxVoters=%d, actual voters=%d", @@ -328,7 +328,7 @@ func electVotersForLoop(t *testing.T, hash []byte, valSet *ValidatorSet, privMap func TestCalVotersNum2(t *testing.T) { t.Skip("take too much time and no longer using accuracy") - valSet, privMap := randValidatorSetWithMinMax(PvKeyEd25519, 100, 100, 10000) + valSet, privMap := randValidatorSetWithMinMax(PrivKeyEd25519, 100, 100, 10000) byzantinePercent := int32(20) byzantines := makeByzantine(valSet, float64(byzantinePercent)/100) genDoc := &GenesisDoc{ From 594d8f685bfc370d9dc5063e108f30da7e31419e Mon Sep 17 00:00:00 2001 From: egonspace Date: Fri, 13 Nov 2020 11:11:00 +0900 Subject: [PATCH 4/7] fix: golangci error --- types/priv_validator_test.go | 1 + types/voter_set_test.go | 64 ------------------------------------ 2 files changed, 1 insertion(+), 64 deletions(-) diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 3245fee1c..2ac937421 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -14,6 +14,7 @@ func forAllPrivKeyTypes(t *testing.T, exec func(t *testing.T, name string, keyTy {name: "ed25512", keyType: PrivKeyEd25519}, {name: "composite", keyType: PrivKeyComposite}, {name: "bls", keyType: PrivKeyBLS}} + //scopelint:ignore for _, knt := range keyNameAndTypes { t.Run(knt.name, func(t *testing.T) { exec(t, knt.name, knt.keyType) diff --git a/types/voter_set_test.go b/types/voter_set_test.go index b6a8d121d..03da550a8 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -251,70 +251,6 @@ func makeByzantine(valSet *ValidatorSet, rate float64) map[string]bool { return result } -func byzantinesPower(voters []*Validator, byzantines map[string]bool) int64 { - power := int64(0) - for _, v := range voters { - if byzantines[v.Address.String()] { - power += v.VotingPower - } - } - return power -} - -func countByzantines(voters []*Validator, byzantines map[string]bool) int { - count := 0 - for _, v := range voters { - if byzantines[v.Address.String()] { - count++ - } - } - return count -} - -func electVotersForLoop(t *testing.T, hash []byte, valSet *ValidatorSet, privMap map[string]PrivValidator, - byzantines map[string]bool, loopCount int, byzantinePercent, accuracy int32) { - byzantineFault := 0 - totalVoters := 0 - totalByzantines := 0 - for i := 0; i < loopCount; i++ { - voterSet := SelectVoter(valSet, hash, &VoterParams{1, byzantinePercent}) - byzantineThreshold := int64(float64(voterSet.TotalVotingPower())*0.33) + 1 - if byzantinesPower(voterSet.Voters, byzantines) >= byzantineThreshold { - byzantineFault++ - } - totalVoters += voterSet.Size() - totalByzantines += countByzantines(voterSet.Voters, byzantines) - proposer := valSet.SelectProposer(hash, int64(i), 0) - message := MakeRoundHash(hash, int64(i), 0) - proof, _ := privMap[proposer.Address.String()].GenerateVRFProof(message) - pubKey, _ := privMap[proposer.Address.String()].GetPubKey() - hash, _ = pubKey.VRFVerify(proof, message) - } - t.Logf("voters=%d, fault=%d, avg byzantines=%f", - totalVoters/loopCount, byzantineFault, float64(totalByzantines)/float64(loopCount)) - assert.True(t, float64(byzantineFault) < float64(loopCount)) -} - -func TestCalVotersNum2(t *testing.T) { - t.Skip("take too much time and no longer using accuracy") - valSet, privMap := randValidatorSetWithMinMax(PrivKeyEd25519, 100, 100, 10000) - byzantinePercent := int32(20) - byzantines := makeByzantine(valSet, float64(byzantinePercent)/100) - genDoc := &GenesisDoc{ - GenesisTime: tmtime.Now(), - ChainID: "tendermint-test", - Validators: toGenesisValidators(valSet.Validators), - } - hash := genDoc.Hash() - - loopCount := 1000 - electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 1) - electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 2) - electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 3) - electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 4) - electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 5) -} - func TestVoterSetProtoBuf(t *testing.T) { _, voterSet, _ := RandVoterSet(10, 100) _, voterSet2, _ := RandVoterSet(10, 100) From 8704111e884dff4d07985c2237b342413a8fe158 Mon Sep 17 00:00:00 2001 From: egonspace Date: Fri, 13 Nov 2020 13:44:40 +0900 Subject: [PATCH 5/7] fix: golangci error --- types/priv_validator_test.go | 2 +- types/voter_set_test.go | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 2ac937421..ccf388d91 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -14,8 +14,8 @@ func forAllPrivKeyTypes(t *testing.T, exec func(t *testing.T, name string, keyTy {name: "ed25512", keyType: PrivKeyEd25519}, {name: "composite", keyType: PrivKeyComposite}, {name: "bls", keyType: PrivKeyBLS}} - //scopelint:ignore for _, knt := range keyNameAndTypes { + knt := knt // pin; to avoid "Using the variable on range scope knt in function literal (scopelint)" t.Run(knt.name, func(t *testing.T) { exec(t, knt.name, knt.keyType) }) diff --git a/types/voter_set_test.go b/types/voter_set_test.go index 03da550a8..9c584a542 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -237,20 +237,6 @@ func TestSelectVoterMaxVarious(t *testing.T) { } } -func makeByzantine(valSet *ValidatorSet, rate float64) map[string]bool { - result := make(map[string]bool) - byzantinePower := int64(0) - threshold := int64(float64(valSet.TotalStakingPower()) * rate) - for _, v := range valSet.Validators { - if byzantinePower+v.StakingPower > threshold { - break - } - result[v.Address.String()] = true - byzantinePower += v.StakingPower - } - return result -} - func TestVoterSetProtoBuf(t *testing.T) { _, voterSet, _ := RandVoterSet(10, 100) _, voterSet2, _ := RandVoterSet(10, 100) From 19304e3fa30119eefb5671d2063aa042485503f3 Mon Sep 17 00:00:00 2001 From: egonspace Date: Fri, 13 Nov 2020 13:59:11 +0900 Subject: [PATCH 6/7] fix: golangci error --- types/priv_validator_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index ccf388d91..c2dbe2077 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -15,9 +15,10 @@ func forAllPrivKeyTypes(t *testing.T, exec func(t *testing.T, name string, keyTy {name: "composite", keyType: PrivKeyComposite}, {name: "bls", keyType: PrivKeyBLS}} for _, knt := range keyNameAndTypes { - knt := knt // pin; to avoid "Using the variable on range scope knt in function literal (scopelint)" - t.Run(knt.name, func(t *testing.T) { - exec(t, knt.name, knt.keyType) + name := knt.name // pin; to avoid "Using the variable on range scope knt in function literal (scopelint)" + keyType := knt.keyType + t.Run(name, func(t *testing.T) { + exec(t, name, keyType) }) } } From a7be6d3052ce460efb129c0f1663dde399934df9 Mon Sep 17 00:00:00 2001 From: egonspace Date: Wed, 2 Dec 2020 15:48:39 +0900 Subject: [PATCH 7/7] fix: rollback needless modification --- types/block_test.go | 7 +++++-- types/vote.go | 6 +++--- types/vote_set.go | 7 +------ types/vote_test.go | 3 +-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/types/block_test.go b/types/block_test.go index e43dfb965..adbb72184 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -808,12 +808,15 @@ func TestCommitToVoteSet(t *testing.T) { voteSet2 := CommitToVoteSet(chainID, commit, valSet) for i := 0; i < len(vals); i++ { - vote1 := voteSet2.GetByIndex(i) - vote2 := commit.GetVote(i) + vote1 := voteSet.GetByIndex(i) + vote2 := voteSet2.GetByIndex(i) + vote3 := commit.GetVote(i) vote1bz := cdc.MustMarshalBinaryBare(vote1) vote2bz := cdc.MustMarshalBinaryBare(vote2) + vote3bz := cdc.MustMarshalBinaryBare(vote3) assert.Equal(t, vote1bz, vote2bz) + assert.Equal(t, vote1bz, vote3bz) } } diff --git a/types/vote.go b/types/vote.go index 5682f9d6a..c016c12b6 100644 --- a/types/vote.go +++ b/types/vote.go @@ -125,7 +125,7 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { return ErrVoteInvalidValidatorAddress } - if vote.Signature != nil && !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { + if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { return ErrVoteInvalidSignature } return nil @@ -162,10 +162,10 @@ func (vote *Vote) ValidateBasic() error { if vote.ValidatorIndex < 0 { return errors.New("negative ValidatorIndex") } - if vote.Signature != nil && len(vote.Signature) == 0 { + if len(vote.Signature) == 0 { return errors.New("signature is missing") } - if vote.Signature != nil && len(vote.Signature) > MaxSignatureSize { + if len(vote.Signature) > MaxSignatureSize { return fmt.Errorf("signature is too big %d (max: %d)", len(vote.Signature), MaxSignatureSize) } return nil diff --git a/types/vote_set.go b/types/vote_set.go index 998d5a92f..5bcb885f5 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -193,9 +193,6 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { // If we already know of this vote, return false. if existing, ok := voteSet.getVote(valIndex, blockKey); ok { - if existing.Signature == nil { - return false, nil // probably aggregated - } if bytes.Equal(existing.Signature, vote.Signature) { return false, nil // duplicate } @@ -215,7 +212,6 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { if !added { panic("Expected to add non-conflicting vote") } - return added, nil } @@ -581,8 +577,7 @@ func (voteSet *VoteSet) MakeCommit() *Commit { commitSigs[i] = commitSig } - return NewCommit( - voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs) + return NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs) } //-------------------------------------------------------------------------------- diff --git a/types/vote_test.go b/types/vote_test.go index a6c24ab29..9a19eb79a 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -202,7 +202,6 @@ func TestVoteVerify(t *testing.T) { vote := examplePrevote() vote.ValidatorAddress = pubkey.Address() - vote.Signature = []byte{} err = vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey()) if assert.Error(t, err) { @@ -280,7 +279,7 @@ func TestVoteValidateBasic(t *testing.T) { }, true}, {"Invalid Address", func(v *Vote) { v.ValidatorAddress = make([]byte, 1) }, true}, {"Invalid ValidatorIndex", func(v *Vote) { v.ValidatorIndex = -1 }, true}, - {"Invalid Signature", func(v *Vote) { v.Signature = []byte{} }, true}, + {"Invalid Signature", func(v *Vote) { v.Signature = nil }, true}, {"Too big Signature", func(v *Vote) { v.Signature = make([]byte, MaxSignatureSize+1) }, true}, } for _, tc := range testCases {