Skip to content

Commit bd62b89

Browse files
committed
fix: allow truncated hashes
Use the incoming hash length in bitswap and the passed digest length in block brokers to test the validtity of incoming blocks.
1 parent 1ef86e4 commit bd62b89

File tree

4 files changed

+94
-5
lines changed

4 files changed

+94
-5
lines changed

packages/bitswap/src/want-list.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
372372
const cidVersion = values[0]
373373
const multicodec = values[1]
374374
const hashAlg = values[2]
375-
// const hashLen = values[3] // We haven't need to use this so far
375+
const hashLen = values[3]
376376

377377
const hasher = hashAlg === sha256.code ? sha256 : await this.hashLoader?.getHasher(hashAlg)
378378

@@ -381,7 +381,9 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
381381
continue
382382
}
383383

384-
let hash: any = hasher.digest(block.data)
384+
let hash: any = hasher.digest(block.data, {
385+
truncate: hashLen
386+
})
385387

386388
if (hash.then != null) {
387389
hash = await hash

packages/bitswap/test/bitswap.spec.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import { peerIdFromPrivateKey } from '@libp2p/peer-id'
55
import { expect } from 'aegir/chai'
66
import { MemoryBlockstore } from 'blockstore-core'
77
import { CID } from 'multiformats/cid'
8-
import { sha256 } from 'multiformats/hashes/sha2'
8+
import * as raw from 'multiformats/codecs/raw'
9+
import { sha256, sha512 } from 'multiformats/hashes/sha2'
910
import pDefer from 'p-defer'
1011
import Sinon from 'sinon'
1112
import { stubInterface } from 'sinon-ts'
1213
import { Bitswap } from '../src/bitswap.js'
1314
import { cidToPrefix } from '../src/utils/cid-prefix.js'
15+
import type { MultihashHasherLoader } from '../src/index.ts'
1416
import type { BitswapMessageEventDetail } from '../src/network.js'
1517
import type { Routing } from '@helia/interface/routing'
1618
import type { Libp2p, PeerId } from '@libp2p/interface'
@@ -29,11 +31,13 @@ describe('bitswap', () => {
2931
let bitswap: Bitswap
3032
let cid: CID
3133
let block: Uint8Array
34+
let hashLoader: StubbedInstance<MultihashHasherLoader>
3235

3336
beforeEach(async () => {
3437
block = Uint8Array.from([0, 1, 2, 3, 4])
3538
const mh = await sha256.digest(block)
3639
cid = CID.createV0(mh).toV1()
40+
hashLoader = stubInterface<MultihashHasherLoader>()
3741

3842
components = {
3943
peerId: peerIdFromPrivateKey(await generateKeyPair('Ed25519')),
@@ -47,6 +51,8 @@ describe('bitswap', () => {
4751
bitswap = new Bitswap({
4852
...components,
4953
logger: defaultLogger()
54+
}, {
55+
hashLoader
5056
})
5157

5258
components.libp2p.getConnections.returns([])
@@ -107,6 +113,59 @@ describe('bitswap', () => {
107113
expect(b).to.equalBytes(block)
108114
})
109115

116+
it('should want a block with a truncated hash', async () => {
117+
hashLoader.getHasher.withArgs(sha512.code).resolves(sha512)
118+
119+
const mh = await sha512.digest(block, {
120+
truncate: 32
121+
})
122+
cid = CID.createV1(raw.code, mh)
123+
124+
const remotePeer = peerIdFromPrivateKey(await generateKeyPair('Ed25519'))
125+
const findProvsSpy = bitswap.network.findAndConnect = Sinon.stub()
126+
findProvsSpy.resolves()
127+
128+
// add peer
129+
bitswap.wantList.peers.set(remotePeer, new Set())
130+
131+
// wait for message send to peer
132+
const sentMessages = pDefer()
133+
134+
bitswap.network.sendMessage = async (peerId) => {
135+
if (remotePeer.equals(peerId)) {
136+
sentMessages.resolve()
137+
}
138+
}
139+
140+
const p = bitswap.want(cid)
141+
142+
// wait for message send to peer
143+
await sentMessages.promise
144+
145+
// provider sends message
146+
bitswap.network.safeDispatchEvent<BitswapMessageEventDetail>('bitswap:message', {
147+
detail: {
148+
peer: remotePeer,
149+
message: {
150+
blocks: [{
151+
prefix: cidToPrefix(cid),
152+
data: block
153+
}],
154+
blockPresences: [],
155+
pendingBytes: 0
156+
}
157+
}
158+
})
159+
160+
const b = await p
161+
162+
// should have added cid to wantlist and searched for providers
163+
expect(findProvsSpy.called).to.be.true()
164+
165+
// should have cancelled the notification request
166+
expect(b).to.equalBytes(block)
167+
})
168+
110169
it('should abort wanting a block that is not available on the network', async () => {
111170
const p = bitswap.want(cid, {
112171
signal: AbortSignal.timeout(100)

packages/utils/src/utils/networked-storage.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,10 @@ export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher):
413413
return async (block: Uint8Array): Promise<void> => {
414414
// verify block
415415
let hash: MultihashDigest<number>
416-
const res = hasher.digest(block)
416+
const res = hasher.digest(block, {
417+
// support truncated hashes where they are truncated
418+
truncate: cid.multihash.digest.byteLength
419+
})
417420

418421
if (isPromise(res)) {
419422
hash = await res

packages/utils/test/utils/networked-storage.spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import toBuffer from 'it-to-buffer'
1111
import { CID } from 'multiformats/cid'
1212
import * as raw from 'multiformats/codecs/raw'
1313
import { identity } from 'multiformats/hashes/identity'
14+
import { sha256, sha512 } from 'multiformats/hashes/sha2'
1415
import Sinon from 'sinon'
1516
import { stubInterface } from 'sinon-ts'
1617
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
1718
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
1819
import { getHasher } from '../../src/utils/get-hasher.js'
19-
import { NetworkedStorage } from '../../src/utils/networked-storage.js'
20+
import { getCidBlockVerifierFunction, NetworkedStorage } from '../../src/utils/networked-storage.js'
2021
import { createBlock } from '../fixtures/create-block.js'
2122
import type { NetworkedStorageComponents } from '../../src/utils/networked-storage.js'
2223
import type { BlockBroker } from '@helia/interface/blocks'
@@ -234,4 +235,28 @@ describe('networked-storage', () => {
234235
expect(block).to.equalBytes(blocks[0].block)
235236
expect(slowBroker.retrieve.getCall(0)).to.have.nested.property('args[1].signal.aborted', true)
236237
})
238+
239+
describe('block verifier', () => {
240+
it('should verify a block', async () => {
241+
const block = Uint8Array.from([0, 1, 2, 3, 4])
242+
const digest = await sha256.digest(block)
243+
const cid = CID.createV1(raw.code, digest)
244+
const fn = getCidBlockVerifierFunction(cid, sha256)
245+
246+
// no promise rejection is a success
247+
await fn(block)
248+
})
249+
250+
it('should verify a block with a truncated hash', async () => {
251+
const block = Uint8Array.from([0, 1, 2, 3, 4])
252+
const digest = await sha512.digest(block, {
253+
truncate: 32
254+
})
255+
const cid = CID.createV1(raw.code, digest)
256+
const fn = getCidBlockVerifierFunction(cid, sha512)
257+
258+
// no promise rejection is a success
259+
await fn(block)
260+
})
261+
})
237262
})

0 commit comments

Comments
 (0)