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.

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