Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/bitswap/src/want-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
const cidVersion = values[0]
const multicodec = values[1]
const hashAlg = values[2]
// const hashLen = values[3] // We haven't need to use this so far
const hashLen = values[3]

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

Expand All @@ -381,7 +381,9 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
continue
}

let hash: any = hasher.digest(block.data)
let hash: any = hasher.digest(block.data, {
truncate: hashLen
})

if (hash.then != null) {
hash = await hash
Expand Down
61 changes: 60 additions & 1 deletion packages/bitswap/test/bitswap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { peerIdFromPrivateKey } from '@libp2p/peer-id'
import { expect } from 'aegir/chai'
import { MemoryBlockstore } from 'blockstore-core'
import { CID } from 'multiformats/cid'
import { sha256 } from 'multiformats/hashes/sha2'
import * as raw from 'multiformats/codecs/raw'
import { sha256, sha512 } from 'multiformats/hashes/sha2'
import pDefer from 'p-defer'
import Sinon from 'sinon'
import { stubInterface } from 'sinon-ts'
import { Bitswap } from '../src/bitswap.js'
import { cidToPrefix } from '../src/utils/cid-prefix.js'
import type { MultihashHasherLoader } from '../src/index.ts'
import type { BitswapMessageEventDetail } from '../src/network.js'
import type { Routing } from '@helia/interface/routing'
import type { Libp2p, PeerId } from '@libp2p/interface'
Expand All @@ -29,11 +31,13 @@ describe('bitswap', () => {
let bitswap: Bitswap
let cid: CID
let block: Uint8Array
let hashLoader: StubbedInstance<MultihashHasherLoader>

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

components = {
peerId: peerIdFromPrivateKey(await generateKeyPair('Ed25519')),
Expand All @@ -47,6 +51,8 @@ describe('bitswap', () => {
bitswap = new Bitswap({
...components,
logger: defaultLogger()
}, {
hashLoader
})

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

it('should want a block with a truncated hash', async () => {
hashLoader.getHasher.withArgs(sha512.code).resolves(sha512)

const mh = await sha512.digest(block, {
truncate: 32
})
cid = CID.createV1(raw.code, mh)

const remotePeer = peerIdFromPrivateKey(await generateKeyPair('Ed25519'))
const findProvsSpy = bitswap.network.findAndConnect = Sinon.stub()
findProvsSpy.resolves()

// add peer
bitswap.wantList.peers.set(remotePeer, new Set())

// wait for message send to peer
const sentMessages = pDefer()

bitswap.network.sendMessage = async (peerId) => {
if (remotePeer.equals(peerId)) {
sentMessages.resolve()
}
}

const p = bitswap.want(cid)

// wait for message send to peer
await sentMessages.promise

// provider sends message
bitswap.network.safeDispatchEvent<BitswapMessageEventDetail>('bitswap:message', {
detail: {
peer: remotePeer,
message: {
blocks: [{
prefix: cidToPrefix(cid),
data: block
}],
blockPresences: [],
pendingBytes: 0
}
}
})

const b = await p

// should have added cid to wantlist and searched for providers
expect(findProvsSpy.called).to.be.true()

// should have cancelled the notification request
expect(b).to.equalBytes(block)
})

it('should abort wanting a block that is not available on the network', async () => {
const p = bitswap.want(cid, {
signal: AbortSignal.timeout(100)
Expand Down
5 changes: 4 additions & 1 deletion packages/utils/src/utils/networked-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,10 @@ export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher):
return async (block: Uint8Array): Promise<void> => {
// verify block
let hash: MultihashDigest<number>
const res = hasher.digest(block)
const res = hasher.digest(block, {
// support truncated hashes where they are truncated
truncate: cid.multihash.digest.byteLength
})

if (isPromise(res)) {
hash = await res
Expand Down
27 changes: 26 additions & 1 deletion packages/utils/test/utils/networked-storage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import toBuffer from 'it-to-buffer'
import { CID } from 'multiformats/cid'
import * as raw from 'multiformats/codecs/raw'
import { identity } from 'multiformats/hashes/identity'
import { sha256, sha512 } from 'multiformats/hashes/sha2'
import Sinon from 'sinon'
import { stubInterface } from 'sinon-ts'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import { getHasher } from '../../src/utils/get-hasher.js'
import { NetworkedStorage } from '../../src/utils/networked-storage.js'
import { getCidBlockVerifierFunction, NetworkedStorage } from '../../src/utils/networked-storage.js'
import { createBlock } from '../fixtures/create-block.js'
import type { NetworkedStorageComponents } from '../../src/utils/networked-storage.js'
import type { BlockBroker } from '@helia/interface/blocks'
Expand Down Expand Up @@ -234,4 +235,28 @@ describe('networked-storage', () => {
expect(block).to.equalBytes(blocks[0].block)
expect(slowBroker.retrieve.getCall(0)).to.have.nested.property('args[1].signal.aborted', true)
})

describe('block verifier', () => {
it('should verify a block', async () => {
const block = Uint8Array.from([0, 1, 2, 3, 4])
const digest = await sha256.digest(block)
const cid = CID.createV1(raw.code, digest)
const fn = getCidBlockVerifierFunction(cid, sha256)

// no promise rejection is a success
await fn(block)
})

it('should verify a block with a truncated hash', async () => {
const block = Uint8Array.from([0, 1, 2, 3, 4])
const digest = await sha512.digest(block, {
truncate: 32
})
const cid = CID.createV1(raw.code, digest)
const fn = getCidBlockVerifierFunction(cid, sha512)

// no promise rejection is a success
await fn(block)
})
})
})