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.

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