Skip to content

Commit

Permalink
fix: publish location claim to content claims service (#1571)
Browse files Browse the repository at this point in the history
Big oversight here, although I think the original intention was to have
the client publish these so perhaps not.

Anyways, since no location claims are being published the gateway cannot
fetch an index and serve bytes quickly. It has to get a location claim
for every single block using the old dynamodb. Being able to use the
index will result in a speed increase for serving content via the
gateway.

Depends on storacha/content-claims#74 for
passing tests.

---------

Co-authored-by: Petra Jaros <peeja@peeja.com>
  • Loading branch information
alanshaw and Peeja authored Nov 28, 2024
1 parent 3eb0f8e commit fb08e0e
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 14 deletions.
2 changes: 1 addition & 1 deletion packages/upload-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
"@web3-storage/access": "workspace:^",
"@web3-storage/blob-index": "workspace:^",
"@web3-storage/capabilities": "workspace:^",
"@web3-storage/content-claims": "^5.1.0",
"@web3-storage/content-claims": "^5.1.3",
"@web3-storage/did-mailto": "workspace:^",
"@web3-storage/filecoin-api": "workspace:^",
"multiformats": "^12.1.2",
Expand Down
30 changes: 30 additions & 0 deletions packages/upload-api/src/blob/accept.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ export function blobAcceptProvider(context) {
expiration: Infinity,
})

// Publish this claim to the content claims service
const pubClaim = await publishLocationClaim(context, { space, digest, size: blob.size, location: createUrl.ok })
if (pubClaim.error) {
return pubClaim
}

// Create result object
/** @type {API.OkBuilder<API.BlobAcceptSuccess, API.BlobAcceptFailure>} */
const result = Server.ok({
Expand Down Expand Up @@ -137,3 +143,27 @@ export const poll = async (context, receipt) => {

return { ok: {} }
}

/**
* @param {API.ClaimsClientContext} ctx
* @param {{ space: API.SpaceDID, digest: API.MultihashDigest, size: number, location: API.URI }} params
*/
const publishLocationClaim = async (ctx, { digest, size, location }) => {
const { invocationConfig, connection } = ctx.claimsService
const { issuer, audience, with: resource, proofs } = invocationConfig
const res = await Assert.location
.invoke({
issuer,
audience,
with: resource,
nb: {
content: { digest: digest.bytes },
location: [location],
range: { offset: 0, length: size }
},
expiration: Infinity,
proofs,
})
.execute(connection)
return res.out
}
6 changes: 3 additions & 3 deletions packages/upload-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ import { StorageGetError } from './types/storage.js'
import { AllocationsStorage, BlobsStorage, BlobAddInput } from './types/blob.js'
export type { AllocationsStorage, BlobsStorage, BlobAddInput }
import { IPNIService, IndexServiceContext } from './types/index.js'
import { ClaimsClientConfig } from './types/content-claims.js'
import { ClaimsClientConfig, ClaimsClientContext } from './types/content-claims.js'
import { Claim } from '@web3-storage/content-claims/client/api'
export type {
IndexServiceContext,
Expand Down Expand Up @@ -366,7 +366,7 @@ export interface W3sService {
}
}

export type BlobServiceContext = SpaceServiceContext & {
export type BlobServiceContext = SpaceServiceContext & ClaimsClientContext & {
/**
* Service signer
*/
Expand All @@ -378,7 +378,7 @@ export type BlobServiceContext = SpaceServiceContext & {
getServiceConnection: () => ConnectionView<Service>
}

export type W3ServiceContext = SpaceServiceContext & {
export type W3ServiceContext = SpaceServiceContext & ClaimsClientContext & {
/**
* Service signer
*/
Expand Down
62 changes: 62 additions & 0 deletions packages/upload-api/test/handlers/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { alice, registerSpace } from '../util.js'
import { BlobSizeOutsideOfSupportedRangeName } from '../../src/blob/lib.js'
import { createConcludeInvocation } from '../../src/ucan/conclude.js'
import { parseBlobAddReceiptNext } from '../helpers/blob.js'
import * as Result from '../helpers/result.js'

/**
* @type {API.Tests}
Expand Down Expand Up @@ -429,6 +430,67 @@ export const test = {
'accept was not successful'
)
},
'blob/accept publishes location claim to claims service': async (
assert,
context
) => {
const { proof, spaceDid } = await registerSpace(alice, context)

const data = new Uint8Array([11, 22, 34, 44, 55])
const digest = await sha256.digest(data)
const size = data.byteLength

const service = createServer(context)
const connection = connect({ id: context.id, channel: service })

const blobAddInvocation = BlobCapabilities.add.invoke({
issuer: alice,
audience: context.id,
with: spaceDid,
nb: { blob: { digest: digest.bytes, size } },
proofs: [proof],
})
const receipt = await blobAddInvocation.execute(connection)
assert.ok(receipt.out.ok)

const nextTasks = parseBlobAddReceiptNext(receipt)
const { address } = Result.unwrap(nextTasks.allocate.receipt.out)
assert.ok(address)

if (address) {
const httpPut = await fetch(address.url, {
method: 'PUT',
mode: 'cors',
body: data,
headers: address.headers,
})
assert.equal(httpPut.status, 200, `PUT ${address.url} failed (${httpPut.status}): ${await httpPut.text()}`)
}

const keys =
/** @type {API.SignerArchive<API.DID, typeof ed25519.signatureCode>} */
(nextTasks.put.task.facts[0]['keys'])
const blobProvider = ed25519.from(keys)
const httpPutReceipt = await Receipt.issue({
issuer: blobProvider,
ran: nextTasks.put.task.link(),
result: { ok: {} },
})
const httpPutConcludeInvocation = createConcludeInvocation(
alice,
context.id,
httpPutReceipt
)
const ucanConclude = await httpPutConcludeInvocation.execute(connection)
assert.ok(ucanConclude.out.ok)

// ensure a location claim exists for the content root
const claims = Result.unwrap(await context.claimsService.read(digest))
assert.ok(
claims.some(c => c.type === 'assert/location'),
'did not find location claim'
)
},
'blob/add fails when a blob with size bigger than maximum size is added':
async (assert, context) => {
const { proof, spaceDid } = await registerSpace(alice, context)
Expand Down
8 changes: 4 additions & 4 deletions packages/w3up-client/test/capability/space.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const SpaceClient = Test.withContext({
assert.equal(egressRecord.bytes, car.size, 'bytes should be the same')
assert.equal(
new Date(egressRecord.servedAt).getTime(),
Math.floor(new Date(egressData.servedAt).getTime() / 1000) * 1000,
Math.floor(new Date(egressData.servedAt).getTime() / 1000),
'servedAt should be the same'
)
assert.ok(egressRecord.cause.toString(), 'cause should be a link')
Expand Down Expand Up @@ -252,7 +252,7 @@ export const SpaceClient = Test.withContext({
assert.equal(egressRecord.bytes, car.size, 'bytes should be the same')
assert.equal(
new Date(egressRecord.servedAt).getTime(),
Math.floor(new Date(egressData.servedAt).getTime() / 1000) * 1000,
Math.floor(new Date(egressData.servedAt).getTime() / 1000),
'servedAt should be the same'
)
assert.ok(egressRecord.cause.toString(), 'cause should be a link')
Expand Down Expand Up @@ -364,7 +364,7 @@ export const SpaceClient = Test.withContext({
assert.equal(egressRecord.bytes, car.size, 'bytes should be the same')
assert.equal(
new Date(egressRecord.servedAt).getTime(),
Math.floor(new Date(egressData.servedAt).getTime() / 1000) * 1000,
Math.floor(new Date(egressData.servedAt).getTime() / 1000),
'servedAt should be the same'
)
assert.ok(egressRecord.cause.toString(), 'cause should be a link')
Expand Down Expand Up @@ -476,7 +476,7 @@ export const SpaceClient = Test.withContext({
assert.equal(egressRecord.bytes, car.size, 'bytes should be the same')
assert.equal(
new Date(egressRecord.servedAt).getTime(),
Math.floor(new Date(egressData.servedAt).getTime() / 1000) * 1000,
Math.floor(new Date(egressData.servedAt).getTime() / 1000),
'servedAt should be the same'
)
assert.ok(egressRecord.cause.toString(), 'cause should be a link')
Expand Down
12 changes: 6 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fb08e0e

Please sign in to comment.