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.

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