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.

447 lines
17 KiB

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