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.

444 lines
17 KiB

  1. package evidence_test
  2. import (
  3. "context"
  4. "testing"
  5. "time"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. dbm "github.com/tendermint/tm-db"
  9. "github.com/tendermint/tendermint/crypto"
  10. "github.com/tendermint/tendermint/crypto/tmhash"
  11. "github.com/tendermint/tendermint/evidence"
  12. "github.com/tendermint/tendermint/evidence/mocks"
  13. "github.com/tendermint/tendermint/libs/log"
  14. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  15. sm "github.com/tendermint/tendermint/state"
  16. smmocks "github.com/tendermint/tendermint/state/mocks"
  17. "github.com/tendermint/tendermint/types"
  18. "github.com/tendermint/tendermint/version"
  19. )
  20. func TestVerifyLightClientAttack_Lunatic(t *testing.T) {
  21. commonVals, commonPrivVals := types.RandValidatorSet(2, 10)
  22. newVal, newPrivVal := types.RandValidator(false, 9)
  23. conflictingVals, err := types.ValidatorSetFromExistingValidators(append(commonVals.Validators, newVal))
  24. require.NoError(t, err)
  25. conflictingPrivVals := append(commonPrivVals, newPrivVal)
  26. commonHeader := makeHeaderRandom(4)
  27. commonHeader.Time = defaultEvidenceTime
  28. trustedHeader := makeHeaderRandom(10)
  29. conflictingHeader := makeHeaderRandom(10)
  30. conflictingHeader.Time = defaultEvidenceTime.Add(1 * time.Hour)
  31. conflictingHeader.ValidatorsHash = conflictingVals.Hash()
  32. // we are simulating a lunatic light client attack
  33. blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash"))
  34. voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
  35. commit, err := types.MakeCommit(blockID, 10, 1, voteSet, conflictingPrivVals, defaultEvidenceTime)
  36. require.NoError(t, err)
  37. ev := &types.LightClientAttackEvidence{
  38. ConflictingBlock: &types.LightBlock{
  39. SignedHeader: &types.SignedHeader{
  40. Header: conflictingHeader,
  41. Commit: commit,
  42. },
  43. ValidatorSet: conflictingVals,
  44. },
  45. CommonHeight: 4,
  46. TotalVotingPower: 20,
  47. ByzantineValidators: commonVals.Validators,
  48. Timestamp: defaultEvidenceTime,
  49. }
  50. commonSignedHeader := &types.SignedHeader{
  51. Header: commonHeader,
  52. Commit: &types.Commit{},
  53. }
  54. trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash"))
  55. vals, privVals := types.RandValidatorSet(3, 8)
  56. trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), vals)
  57. trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, privVals, defaultEvidenceTime)
  58. require.NoError(t, err)
  59. trustedSignedHeader := &types.SignedHeader{
  60. Header: trustedHeader,
  61. Commit: trustedCommit,
  62. }
  63. // good pass -> no error
  64. err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, trustedSignedHeader, commonVals,
  65. defaultEvidenceTime.Add(2*time.Hour), 3*time.Hour)
  66. assert.NoError(t, err)
  67. // trusted and conflicting hashes are the same -> an error should be returned
  68. err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, ev.ConflictingBlock.SignedHeader, commonVals,
  69. defaultEvidenceTime.Add(2*time.Hour), 3*time.Hour)
  70. assert.Error(t, err)
  71. // evidence with different total validator power should fail
  72. ev.TotalVotingPower = 1
  73. err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, trustedSignedHeader, commonVals,
  74. defaultEvidenceTime.Add(2*time.Hour), 3*time.Hour)
  75. assert.Error(t, err)
  76. ev.TotalVotingPower = 20
  77. state := sm.State{
  78. LastBlockTime: defaultEvidenceTime.Add(2 * time.Hour),
  79. LastBlockHeight: 11,
  80. ConsensusParams: *types.DefaultConsensusParams(),
  81. }
  82. stateStore := &smmocks.Store{}
  83. stateStore.On("LoadValidators", int64(4)).Return(commonVals, nil)
  84. stateStore.On("Load").Return(state, nil)
  85. blockStore := &mocks.BlockStore{}
  86. blockStore.On("LoadBlockMeta", int64(4)).Return(&types.BlockMeta{Header: *commonHeader})
  87. blockStore.On("LoadBlockMeta", int64(10)).Return(&types.BlockMeta{Header: *trustedHeader})
  88. blockStore.On("LoadBlockCommit", int64(4)).Return(commit)
  89. blockStore.On("LoadBlockCommit", int64(10)).Return(trustedCommit)
  90. pool, err := evidence.NewPool(log.TestingLogger(), dbm.NewMemDB(), stateStore, blockStore)
  91. require.NoError(t, err)
  92. evList := types.EvidenceList{ev}
  93. err = pool.CheckEvidence(evList)
  94. assert.NoError(t, err)
  95. pendingEvs, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
  96. assert.Equal(t, 1, len(pendingEvs))
  97. // if we submit evidence only against a single byzantine validator when we see there are more validators then this
  98. // should return an error
  99. ev.ByzantineValidators = []*types.Validator{commonVals.Validators[0]}
  100. err = pool.CheckEvidence(evList)
  101. assert.Error(t, err)
  102. ev.ByzantineValidators = commonVals.Validators // restore evidence
  103. // If evidence is submitted with an altered timestamp it should return an error
  104. ev.Timestamp = defaultEvidenceTime.Add(1 * time.Minute)
  105. err = pool.CheckEvidence(evList)
  106. assert.Error(t, err)
  107. }
  108. func TestVerifyLightClientAttack_Equivocation(t *testing.T) {
  109. conflictingVals, conflictingPrivVals := types.RandValidatorSet(5, 10)
  110. trustedHeader := makeHeaderRandom(10)
  111. conflictingHeader := makeHeaderRandom(10)
  112. conflictingHeader.ValidatorsHash = conflictingVals.Hash()
  113. trustedHeader.ValidatorsHash = conflictingHeader.ValidatorsHash
  114. trustedHeader.NextValidatorsHash = conflictingHeader.NextValidatorsHash
  115. trustedHeader.ConsensusHash = conflictingHeader.ConsensusHash
  116. trustedHeader.AppHash = conflictingHeader.AppHash
  117. trustedHeader.LastResultsHash = conflictingHeader.LastResultsHash
  118. // we are simulating a duplicate vote attack where all the validators in the conflictingVals set
  119. // except the last validator vote twice
  120. blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash"))
  121. voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
  122. commit, err := types.MakeCommit(blockID, 10, 1, voteSet, conflictingPrivVals[:4], defaultEvidenceTime)
  123. require.NoError(t, err)
  124. ev := &types.LightClientAttackEvidence{
  125. ConflictingBlock: &types.LightBlock{
  126. SignedHeader: &types.SignedHeader{
  127. Header: conflictingHeader,
  128. Commit: commit,
  129. },
  130. ValidatorSet: conflictingVals,
  131. },
  132. CommonHeight: 10,
  133. ByzantineValidators: conflictingVals.Validators[:4],
  134. TotalVotingPower: 50,
  135. Timestamp: defaultEvidenceTime,
  136. }
  137. trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash"))
  138. trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
  139. trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime)
  140. require.NoError(t, err)
  141. trustedSignedHeader := &types.SignedHeader{
  142. Header: trustedHeader,
  143. Commit: trustedCommit,
  144. }
  145. // good pass -> no error
  146. err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, conflictingVals,
  147. defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour)
  148. assert.NoError(t, err)
  149. // trusted and conflicting hashes are the same -> an error should be returned
  150. err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, ev.ConflictingBlock.SignedHeader, conflictingVals,
  151. defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour)
  152. assert.Error(t, err)
  153. // conflicting header has different next validators hash which should have been correctly derived from
  154. // the previous round
  155. ev.ConflictingBlock.Header.NextValidatorsHash = crypto.CRandBytes(tmhash.Size)
  156. err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, nil,
  157. defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour)
  158. assert.Error(t, err)
  159. // revert next validators hash
  160. ev.ConflictingBlock.Header.NextValidatorsHash = trustedHeader.NextValidatorsHash
  161. state := sm.State{
  162. LastBlockTime: defaultEvidenceTime.Add(1 * time.Minute),
  163. LastBlockHeight: 11,
  164. ConsensusParams: *types.DefaultConsensusParams(),
  165. }
  166. stateStore := &smmocks.Store{}
  167. stateStore.On("LoadValidators", int64(10)).Return(conflictingVals, nil)
  168. stateStore.On("Load").Return(state, nil)
  169. blockStore := &mocks.BlockStore{}
  170. blockStore.On("LoadBlockMeta", int64(10)).Return(&types.BlockMeta{Header: *trustedHeader})
  171. blockStore.On("LoadBlockCommit", int64(10)).Return(trustedCommit)
  172. pool, err := evidence.NewPool(log.TestingLogger(), dbm.NewMemDB(), stateStore, blockStore)
  173. require.NoError(t, err)
  174. evList := types.EvidenceList{ev}
  175. err = pool.CheckEvidence(evList)
  176. assert.NoError(t, err)
  177. pendingEvs, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
  178. assert.Equal(t, 1, len(pendingEvs))
  179. }
  180. func TestVerifyLightClientAttack_Amnesia(t *testing.T) {
  181. conflictingVals, conflictingPrivVals := types.RandValidatorSet(5, 10)
  182. conflictingHeader := makeHeaderRandom(10)
  183. conflictingHeader.ValidatorsHash = conflictingVals.Hash()
  184. trustedHeader := makeHeaderRandom(10)
  185. trustedHeader.ValidatorsHash = conflictingHeader.ValidatorsHash
  186. trustedHeader.NextValidatorsHash = conflictingHeader.NextValidatorsHash
  187. trustedHeader.AppHash = conflictingHeader.AppHash
  188. trustedHeader.ConsensusHash = conflictingHeader.ConsensusHash
  189. trustedHeader.LastResultsHash = conflictingHeader.LastResultsHash
  190. // we are simulating an amnesia attack where all the validators in the conflictingVals set
  191. // except the last validator vote twice. However this time the commits are of different rounds.
  192. blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash"))
  193. voteSet := types.NewVoteSet(evidenceChainID, 10, 0, tmproto.SignedMsgType(2), conflictingVals)
  194. commit, err := types.MakeCommit(blockID, 10, 0, voteSet, conflictingPrivVals, defaultEvidenceTime)
  195. require.NoError(t, err)
  196. ev := &types.LightClientAttackEvidence{
  197. ConflictingBlock: &types.LightBlock{
  198. SignedHeader: &types.SignedHeader{
  199. Header: conflictingHeader,
  200. Commit: commit,
  201. },
  202. ValidatorSet: conflictingVals,
  203. },
  204. CommonHeight: 10,
  205. ByzantineValidators: nil, // with amnesia evidence no validators are submitted as abci evidence
  206. TotalVotingPower: 50,
  207. Timestamp: defaultEvidenceTime,
  208. }
  209. trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash"))
  210. trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
  211. trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime)
  212. require.NoError(t, err)
  213. trustedSignedHeader := &types.SignedHeader{
  214. Header: trustedHeader,
  215. Commit: trustedCommit,
  216. }
  217. // good pass -> no error
  218. err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, conflictingVals,
  219. defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour)
  220. assert.NoError(t, err)
  221. // trusted and conflicting hashes are the same -> an error should be returned
  222. err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, ev.ConflictingBlock.SignedHeader, conflictingVals,
  223. defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour)
  224. assert.Error(t, err)
  225. state := sm.State{
  226. LastBlockTime: defaultEvidenceTime.Add(1 * time.Minute),
  227. LastBlockHeight: 11,
  228. ConsensusParams: *types.DefaultConsensusParams(),
  229. }
  230. stateStore := &smmocks.Store{}
  231. stateStore.On("LoadValidators", int64(10)).Return(conflictingVals, nil)
  232. stateStore.On("Load").Return(state, nil)
  233. blockStore := &mocks.BlockStore{}
  234. blockStore.On("LoadBlockMeta", int64(10)).Return(&types.BlockMeta{Header: *trustedHeader})
  235. blockStore.On("LoadBlockCommit", int64(10)).Return(trustedCommit)
  236. pool, err := evidence.NewPool(log.TestingLogger(), dbm.NewMemDB(), stateStore, blockStore)
  237. require.NoError(t, err)
  238. evList := types.EvidenceList{ev}
  239. err = pool.CheckEvidence(evList)
  240. assert.NoError(t, err)
  241. pendingEvs, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
  242. assert.Equal(t, 1, len(pendingEvs))
  243. }
  244. type voteData struct {
  245. vote1 *types.Vote
  246. vote2 *types.Vote
  247. valid bool
  248. }
  249. func TestVerifyDuplicateVoteEvidence(t *testing.T) {
  250. val := types.NewMockPV()
  251. val2 := types.NewMockPV()
  252. valSet := types.NewValidatorSet([]*types.Validator{val.ExtractIntoValidator(1)})
  253. blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
  254. blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash"))
  255. blockID3 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash"))
  256. blockID4 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash2"))
  257. const chainID = "mychain"
  258. vote1 := makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultEvidenceTime)
  259. v1 := vote1.ToProto()
  260. err := val.SignVote(context.Background(), chainID, v1)
  261. require.NoError(t, err)
  262. badVote := makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultEvidenceTime)
  263. bv := badVote.ToProto()
  264. err = val2.SignVote(context.Background(), chainID, bv)
  265. require.NoError(t, err)
  266. vote1.Signature = v1.Signature
  267. badVote.Signature = bv.Signature
  268. cases := []voteData{
  269. {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultEvidenceTime), true}, // different block ids
  270. {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID3, defaultEvidenceTime), true},
  271. {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID4, defaultEvidenceTime), true},
  272. {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultEvidenceTime), false}, // wrong block id
  273. {vote1, makeVote(t, val, "mychain2", 0, 10, 2, 1, blockID2, defaultEvidenceTime), false}, // wrong chain id
  274. {vote1, makeVote(t, val, chainID, 0, 11, 2, 1, blockID2, defaultEvidenceTime), false}, // wrong height
  275. {vote1, makeVote(t, val, chainID, 0, 10, 3, 1, blockID2, defaultEvidenceTime), false}, // wrong round
  276. {vote1, makeVote(t, val, chainID, 0, 10, 2, 2, blockID2, defaultEvidenceTime), false}, // wrong step
  277. {vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID2, defaultEvidenceTime), false}, // wrong validator
  278. // a different vote time doesn't matter
  279. {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)), true},
  280. {vote1, badVote, false}, // signed by wrong key
  281. }
  282. require.NoError(t, err)
  283. for _, c := range cases {
  284. ev := &types.DuplicateVoteEvidence{
  285. VoteA: c.vote1,
  286. VoteB: c.vote2,
  287. ValidatorPower: 1,
  288. TotalVotingPower: 1,
  289. Timestamp: defaultEvidenceTime,
  290. }
  291. if c.valid {
  292. assert.Nil(t, evidence.VerifyDuplicateVote(ev, chainID, valSet), "evidence should be valid")
  293. } else {
  294. assert.NotNil(t, evidence.VerifyDuplicateVote(ev, chainID, valSet), "evidence should be invalid")
  295. }
  296. }
  297. // create good evidence and correct validator power
  298. goodEv := types.NewMockDuplicateVoteEvidenceWithValidator(10, defaultEvidenceTime, val, chainID)
  299. goodEv.ValidatorPower = 1
  300. goodEv.TotalVotingPower = 1
  301. badEv := types.NewMockDuplicateVoteEvidenceWithValidator(10, defaultEvidenceTime, val, chainID)
  302. badTimeEv := types.NewMockDuplicateVoteEvidenceWithValidator(10, defaultEvidenceTime.Add(1*time.Minute), val, chainID)
  303. badTimeEv.ValidatorPower = 1
  304. badTimeEv.TotalVotingPower = 1
  305. state := sm.State{
  306. ChainID: chainID,
  307. LastBlockTime: defaultEvidenceTime.Add(1 * time.Minute),
  308. LastBlockHeight: 11,
  309. ConsensusParams: *types.DefaultConsensusParams(),
  310. }
  311. stateStore := &smmocks.Store{}
  312. stateStore.On("LoadValidators", int64(10)).Return(valSet, nil)
  313. stateStore.On("Load").Return(state, nil)
  314. blockStore := &mocks.BlockStore{}
  315. blockStore.On("LoadBlockMeta", int64(10)).Return(&types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}})
  316. pool, err := evidence.NewPool(log.TestingLogger(), dbm.NewMemDB(), stateStore, blockStore)
  317. require.NoError(t, err)
  318. evList := types.EvidenceList{goodEv}
  319. err = pool.CheckEvidence(evList)
  320. assert.NoError(t, err)
  321. // evidence with a different validator power should fail
  322. evList = types.EvidenceList{badEv}
  323. err = pool.CheckEvidence(evList)
  324. assert.Error(t, err)
  325. // evidence with a different timestamp should fail
  326. evList = types.EvidenceList{badTimeEv}
  327. err = pool.CheckEvidence(evList)
  328. assert.Error(t, err)
  329. }
  330. func makeVote(
  331. t *testing.T, val types.PrivValidator, chainID string, valIndex int32, height int64,
  332. round int32, step int, blockID types.BlockID, time time.Time) *types.Vote {
  333. pubKey, err := val.GetPubKey(context.Background())
  334. require.NoError(t, err)
  335. v := &types.Vote{
  336. ValidatorAddress: pubKey.Address(),
  337. ValidatorIndex: valIndex,
  338. Height: height,
  339. Round: round,
  340. Type: tmproto.SignedMsgType(step),
  341. BlockID: blockID,
  342. Timestamp: time,
  343. }
  344. vpb := v.ToProto()
  345. err = val.SignVote(context.Background(), chainID, vpb)
  346. if err != nil {
  347. panic(err)
  348. }
  349. v.Signature = vpb.Signature
  350. return v
  351. }
  352. func makeHeaderRandom(height int64) *types.Header {
  353. return &types.Header{
  354. Version: version.Consensus{Block: version.BlockProtocol, App: 1},
  355. ChainID: evidenceChainID,
  356. Height: height,
  357. Time: defaultEvidenceTime,
  358. LastBlockID: makeBlockID([]byte("headerhash"), 1000, []byte("partshash")),
  359. LastCommitHash: crypto.CRandBytes(tmhash.Size),
  360. DataHash: crypto.CRandBytes(tmhash.Size),
  361. ValidatorsHash: crypto.CRandBytes(tmhash.Size),
  362. NextValidatorsHash: crypto.CRandBytes(tmhash.Size),
  363. ConsensusHash: crypto.CRandBytes(tmhash.Size),
  364. AppHash: crypto.CRandBytes(tmhash.Size),
  365. LastResultsHash: crypto.CRandBytes(tmhash.Size),
  366. EvidenceHash: crypto.CRandBytes(tmhash.Size),
  367. ProposerAddress: crypto.CRandBytes(crypto.AddressSize),
  368. }
  369. }
  370. func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
  371. var (
  372. h = make([]byte, tmhash.Size)
  373. psH = make([]byte, tmhash.Size)
  374. )
  375. copy(h, hash)
  376. copy(psH, partSetHash)
  377. return types.BlockID{
  378. Hash: h,
  379. PartSetHeader: types.PartSetHeader{
  380. Total: partSetSize,
  381. Hash: psH,
  382. },
  383. }
  384. }