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

  1. package types
  2. import (
  3. "context"
  4. "testing"
  5. "time"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. tmmath "github.com/tendermint/tendermint/libs/math"
  9. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  10. )
  11. // Check VerifyCommit, VerifyCommitLight and VerifyCommitLightTrusting basic
  12. // verification.
  13. func TestValidatorSet_VerifyCommit_All(t *testing.T) {
  14. var (
  15. round = int32(0)
  16. height = int64(100)
  17. blockID = makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
  18. chainID = "Lalande21185"
  19. trustLevel = tmmath.Fraction{Numerator: 2, Denominator: 3}
  20. )
  21. testCases := []struct {
  22. description string
  23. // vote chainID
  24. chainID string
  25. // vote blockID
  26. blockID BlockID
  27. valSize int
  28. // height of the commit
  29. height int64
  30. // votes
  31. blockVotes int
  32. nilVotes int
  33. absentVotes int
  34. expErr bool
  35. }{
  36. {"good (batch verification)", chainID, blockID, 3, height, 3, 0, 0, false},
  37. {"good (single verification)", chainID, blockID, 1, height, 1, 0, 0, false},
  38. {"wrong signature (#0)", "EpsilonEridani", blockID, 2, height, 2, 0, 0, true},
  39. {"wrong block ID", chainID, makeBlockIDRandom(), 2, height, 2, 0, 0, true},
  40. {"wrong height", chainID, blockID, 1, height - 1, 1, 0, 0, true},
  41. {"wrong set size: 4 vs 3", chainID, blockID, 4, height, 3, 0, 0, true},
  42. {"wrong set size: 1 vs 2", chainID, blockID, 1, height, 2, 0, 0, true},
  43. {"insufficient voting power: got 30, needed more than 66", chainID, blockID, 10, height, 3, 2, 5, true},
  44. {"insufficient voting power: got 0, needed more than 6", chainID, blockID, 1, height, 0, 0, 1, true},
  45. {"insufficient voting power: got 60, needed more than 60", chainID, blockID, 9, height, 6, 3, 0, true},
  46. }
  47. for _, tc := range testCases {
  48. tc := tc
  49. t.Run(tc.description, func(t *testing.T) {
  50. ctx, cancel := context.WithCancel(context.Background())
  51. defer cancel()
  52. _, valSet, vals := randVoteSet(ctx, t, tc.height, round, tmproto.PrecommitType, tc.valSize, 10)
  53. totalVotes := tc.blockVotes + tc.absentVotes + tc.nilVotes
  54. sigs := make([]CommitSig, totalVotes)
  55. vi := 0
  56. // add absent sigs first
  57. for i := 0; i < tc.absentVotes; i++ {
  58. sigs[vi] = NewCommitSigAbsent()
  59. vi++
  60. }
  61. for i := 0; i < tc.blockVotes+tc.nilVotes; i++ {
  62. pubKey, err := vals[vi%len(vals)].GetPubKey(ctx)
  63. require.NoError(t, err)
  64. vote := &Vote{
  65. ValidatorAddress: pubKey.Address(),
  66. ValidatorIndex: int32(vi),
  67. Height: tc.height,
  68. Round: round,
  69. Type: tmproto.PrecommitType,
  70. BlockID: tc.blockID,
  71. Timestamp: time.Now(),
  72. }
  73. if i >= tc.blockVotes {
  74. vote.BlockID = BlockID{}
  75. }
  76. v := vote.ToProto()
  77. require.NoError(t, vals[vi%len(vals)].SignVote(ctx, tc.chainID, v))
  78. vote.Signature = v.Signature
  79. sigs[vi] = vote.CommitSig()
  80. vi++
  81. }
  82. commit := NewCommit(tc.height, round, tc.blockID, sigs)
  83. err := valSet.VerifyCommit(chainID, blockID, height, commit)
  84. if tc.expErr {
  85. if assert.Error(t, err, "VerifyCommit") {
  86. assert.Contains(t, err.Error(), tc.description, "VerifyCommit")
  87. }
  88. } else {
  89. assert.NoError(t, err, "VerifyCommit")
  90. }
  91. err = valSet.VerifyCommitLight(chainID, blockID, height, commit)
  92. if tc.expErr {
  93. if assert.Error(t, err, "VerifyCommitLight") {
  94. assert.Contains(t, err.Error(), tc.description, "VerifyCommitLight")
  95. }
  96. } else {
  97. assert.NoError(t, err, "VerifyCommitLight")
  98. }
  99. // only a subsection of the tests apply to VerifyCommitLightTrusting
  100. if totalVotes != tc.valSize || !tc.blockID.Equals(blockID) || tc.height != height {
  101. tc.expErr = false
  102. }
  103. err = valSet.VerifyCommitLightTrusting(chainID, commit, trustLevel)
  104. if tc.expErr {
  105. if assert.Error(t, err, "VerifyCommitLightTrusting") {
  106. assert.Contains(t, err.Error(), tc.description, "VerifyCommitLightTrusting")
  107. }
  108. } else {
  109. assert.NoError(t, err, "VerifyCommitLightTrusting")
  110. }
  111. })
  112. }
  113. }
  114. func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) {
  115. var (
  116. chainID = "test_chain_id"
  117. h = int64(3)
  118. blockID = makeBlockIDRandom()
  119. )
  120. ctx, cancel := context.WithCancel(context.Background())
  121. defer cancel()
  122. voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
  123. commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
  124. require.NoError(t, err)
  125. require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
  126. // malleate 4th signature
  127. vote := voteSet.GetByIndex(3)
  128. v := vote.ToProto()
  129. err = vals[3].SignVote(ctx, "CentaurusA", v)
  130. require.NoError(t, err)
  131. vote.Signature = v.Signature
  132. commit.Signatures[3] = vote.CommitSig()
  133. err = valSet.VerifyCommit(chainID, blockID, h, commit)
  134. if assert.Error(t, err) {
  135. assert.Contains(t, err.Error(), "wrong signature (#3)")
  136. }
  137. }
  138. func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSigned(t *testing.T) {
  139. var (
  140. chainID = "test_chain_id"
  141. h = int64(3)
  142. blockID = makeBlockIDRandom()
  143. )
  144. ctx, cancel := context.WithCancel(context.Background())
  145. defer cancel()
  146. voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
  147. commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
  148. require.NoError(t, err)
  149. require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
  150. // malleate 4th signature (3 signatures are enough for 2/3+)
  151. vote := voteSet.GetByIndex(3)
  152. v := vote.ToProto()
  153. err = vals[3].SignVote(ctx, "CentaurusA", v)
  154. require.NoError(t, err)
  155. vote.Signature = v.Signature
  156. commit.Signatures[3] = vote.CommitSig()
  157. err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
  158. assert.NoError(t, err)
  159. }
  160. func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) {
  161. var (
  162. chainID = "test_chain_id"
  163. h = int64(3)
  164. blockID = makeBlockIDRandom()
  165. )
  166. ctx, cancel := context.WithCancel(context.Background())
  167. defer cancel()
  168. voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
  169. commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
  170. require.NoError(t, err)
  171. require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
  172. // malleate 3rd signature (2 signatures are enough for 1/3+ trust level)
  173. vote := voteSet.GetByIndex(2)
  174. v := vote.ToProto()
  175. err = vals[2].SignVote(ctx, "CentaurusA", v)
  176. require.NoError(t, err)
  177. vote.Signature = v.Signature
  178. commit.Signatures[2] = vote.CommitSig()
  179. err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3})
  180. assert.NoError(t, err)
  181. }
  182. func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) {
  183. ctx, cancel := context.WithCancel(context.Background())
  184. defer cancel()
  185. var (
  186. blockID = makeBlockIDRandom()
  187. voteSet, originalValset, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 6, 1)
  188. commit, err = makeCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
  189. newValSet, _ = randValidatorPrivValSet(ctx, t, 2, 1)
  190. )
  191. require.NoError(t, err)
  192. testCases := []struct {
  193. valSet *ValidatorSet
  194. err bool
  195. }{
  196. // good
  197. 0: {
  198. valSet: originalValset,
  199. err: false,
  200. },
  201. // bad - no overlap between validator sets
  202. 1: {
  203. valSet: newValSet,
  204. err: true,
  205. },
  206. // good - first two are different but the rest of the same -> >1/3
  207. 2: {
  208. valSet: NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)),
  209. err: false,
  210. },
  211. }
  212. for _, tc := range testCases {
  213. err = tc.valSet.VerifyCommitLightTrusting("test_chain_id", commit,
  214. tmmath.Fraction{Numerator: 1, Denominator: 3})
  215. if tc.err {
  216. assert.Error(t, err)
  217. } else {
  218. assert.NoError(t, err)
  219. }
  220. }
  221. }
  222. func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) {
  223. ctx, cancel := context.WithCancel(context.Background())
  224. defer cancel()
  225. var (
  226. blockID = makeBlockIDRandom()
  227. voteSet, valSet, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower)
  228. commit, err = makeCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
  229. )
  230. require.NoError(t, err)
  231. err = valSet.VerifyCommitLightTrusting("test_chain_id", commit,
  232. tmmath.Fraction{Numerator: 25, Denominator: 55})
  233. if assert.Error(t, err) {
  234. assert.Contains(t, err.Error(), "int64 overflow")
  235. }
  236. }