You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

283 lines
8.2 KiB

package types
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
tmmath "github.com/tendermint/tendermint/libs/math"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)
// Check VerifyCommit, VerifyCommitLight and VerifyCommitLightTrusting basic
// verification.
func TestValidatorSet_VerifyCommit_All(t *testing.T) {
var (
round = int32(0)
height = int64(100)
blockID = makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
chainID = "Lalande21185"
trustLevel = tmmath.Fraction{Numerator: 2, Denominator: 3}
)
testCases := []struct {
description string
// vote chainID
chainID string
// vote blockID
blockID BlockID
valSize int
// height of the commit
height int64
// votes
blockVotes int
nilVotes int
absentVotes int
expErr bool
}{
{"good (batch verification)", chainID, blockID, 3, height, 3, 0, 0, false},
{"good (single verification)", chainID, blockID, 1, height, 1, 0, 0, false},
{"wrong signature (#0)", "EpsilonEridani", blockID, 2, height, 2, 0, 0, true},
{"wrong block ID", chainID, makeBlockIDRandom(), 2, height, 2, 0, 0, true},
{"wrong height", chainID, blockID, 1, height - 1, 1, 0, 0, true},
{"wrong set size: 4 vs 3", chainID, blockID, 4, height, 3, 0, 0, true},
{"wrong set size: 1 vs 2", chainID, blockID, 1, height, 2, 0, 0, true},
{"insufficient voting power: got 30, needed more than 66", chainID, blockID, 10, height, 3, 2, 5, true},
{"insufficient voting power: got 0, needed more than 6", chainID, blockID, 1, height, 0, 0, 1, true},
{"insufficient voting power: got 60, needed more than 60", chainID, blockID, 9, height, 6, 3, 0, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, valSet, vals := randVoteSet(ctx, t, tc.height, round, tmproto.PrecommitType, tc.valSize, 10)
totalVotes := tc.blockVotes + tc.absentVotes + tc.nilVotes
sigs := make([]CommitSig, totalVotes)
vi := 0
// add absent sigs first
for i := 0; i < tc.absentVotes; i++ {
sigs[vi] = NewCommitSigAbsent()
vi++
}
for i := 0; i < tc.blockVotes+tc.nilVotes; i++ {
pubKey, err := vals[vi%len(vals)].GetPubKey(ctx)
require.NoError(t, err)
vote := &Vote{
ValidatorAddress: pubKey.Address(),
ValidatorIndex: int32(vi),
Height: tc.height,
Round: round,
Type: tmproto.PrecommitType,
BlockID: tc.blockID,
Timestamp: time.Now(),
}
if i >= tc.blockVotes {
vote.BlockID = BlockID{}
}
v := vote.ToProto()
require.NoError(t, vals[vi%len(vals)].SignVote(ctx, tc.chainID, v))
vote.Signature = v.Signature
sigs[vi] = vote.CommitSig()
vi++
}
commit := NewCommit(tc.height, round, tc.blockID, sigs)
err := valSet.VerifyCommit(chainID, blockID, height, commit)
if tc.expErr {
if assert.Error(t, err, "VerifyCommit") {
assert.Contains(t, err.Error(), tc.description, "VerifyCommit")
}
} else {
assert.NoError(t, err, "VerifyCommit")
}
err = valSet.VerifyCommitLight(chainID, blockID, height, commit)
if tc.expErr {
if assert.Error(t, err, "VerifyCommitLight") {
assert.Contains(t, err.Error(), tc.description, "VerifyCommitLight")
}
} else {
assert.NoError(t, err, "VerifyCommitLight")
}
// only a subsection of the tests apply to VerifyCommitLightTrusting
if totalVotes != tc.valSize || !tc.blockID.Equals(blockID) || tc.height != height {
tc.expErr = false
}
err = valSet.VerifyCommitLightTrusting(chainID, commit, trustLevel)
if tc.expErr {
if assert.Error(t, err, "VerifyCommitLightTrusting") {
assert.Contains(t, err.Error(), tc.description, "VerifyCommitLightTrusting")
}
} else {
assert.NoError(t, err, "VerifyCommitLightTrusting")
}
})
}
}
func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) {
var (
chainID = "test_chain_id"
h = int64(3)
blockID = makeBlockIDRandom()
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(t, err)
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
// malleate 4th signature
vote := voteSet.GetByIndex(3)
v := vote.ToProto()
err = vals[3].SignVote(ctx, "CentaurusA", v)
require.NoError(t, err)
vote.Signature = v.Signature
commit.Signatures[3] = vote.CommitSig()
err = valSet.VerifyCommit(chainID, blockID, h, commit)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "wrong signature (#3)")
}
}
func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSigned(t *testing.T) {
var (
chainID = "test_chain_id"
h = int64(3)
blockID = makeBlockIDRandom()
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(t, err)
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
// malleate 4th signature (3 signatures are enough for 2/3+)
vote := voteSet.GetByIndex(3)
v := vote.ToProto()
err = vals[3].SignVote(ctx, "CentaurusA", v)
require.NoError(t, err)
vote.Signature = v.Signature
commit.Signatures[3] = vote.CommitSig()
err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
assert.NoError(t, err)
}
func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) {
var (
chainID = "test_chain_id"
h = int64(3)
blockID = makeBlockIDRandom()
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(t, err)
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
// malleate 3rd signature (2 signatures are enough for 1/3+ trust level)
vote := voteSet.GetByIndex(2)
v := vote.ToProto()
err = vals[2].SignVote(ctx, "CentaurusA", v)
require.NoError(t, err)
vote.Signature = v.Signature
commit.Signatures[2] = vote.CommitSig()
err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3})
assert.NoError(t, err)
}
func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var (
blockID = makeBlockIDRandom()
voteSet, originalValset, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 6, 1)
commit, err = makeCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
newValSet, _ = randValidatorPrivValSet(ctx, t, 2, 1)
)
require.NoError(t, err)
testCases := []struct {
valSet *ValidatorSet
err bool
}{
// good
0: {
valSet: originalValset,
err: false,
},
// bad - no overlap between validator sets
1: {
valSet: newValSet,
err: true,
},
// good - first two are different but the rest of the same -> >1/3
2: {
valSet: NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)),
err: false,
},
}
for _, tc := range testCases {
err = tc.valSet.VerifyCommitLightTrusting("test_chain_id", commit,
tmmath.Fraction{Numerator: 1, Denominator: 3})
if tc.err {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
}
}
func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var (
blockID = makeBlockIDRandom()
voteSet, valSet, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower)
commit, err = makeCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
)
require.NoError(t, err)
err = valSet.VerifyCommitLightTrusting("test_chain_id", commit,
tmmath.Fraction{Numerator: 25, Denominator: 55})
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "int64 overflow")
}
}