Skip to content

Commit

Permalink
Merge branch 'main' into fix/use-multihashdigest-type1
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Shaw authored Jun 7, 2024
2 parents 08c1695 + 237b0c6 commit 37ede86
Show file tree
Hide file tree
Showing 19 changed files with 332 additions and 148 deletions.
7 changes: 0 additions & 7 deletions .github/workflows/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,3 @@ jobs:
- run: pnpm -r --filter @web3-storage/upload-client publish --tag next --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
deploy-docs:
permissions:
contents: read
pages: write
id-token: write
if: github.event.inputs.package == 'docs'
uses: './.github/workflows/reusable-deploy-docs.yml'
17 changes: 0 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,3 @@ jobs:
- run: pnpm -r publish --access=public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
docs:
permissions:
contents: read
pages: write
id-token: write
needs: release
if: |
contains(fromJson(needs.release.outputs.paths_released), 'packages/access-client') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/blob-index') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/capabilities') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/did-mailto') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/upload-client') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/upload-api') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/filecoin-client') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/filecoin-api') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/w3up-client')
uses: './.github/workflows/reusable-deploy-docs.yml'
35 changes: 0 additions & 35 deletions .github/workflows/reusable-deploy-docs.yml

This file was deleted.

2 changes: 1 addition & 1 deletion packages/capabilities/src/index/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const add = capability({
with: SpaceDID,
nb: Schema.struct({
/** Content Archive (CAR) containing the `Index`. */
index: Schema.link({ code: CAR.code }),
index: Schema.link({ code: CAR.code, version: 1 }),
}),
derives: (claimed, delegated) =>
and(equalWith(claimed, delegated)) ||
Expand Down
3 changes: 1 addition & 2 deletions packages/upload-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,10 @@
"@web3-storage/access": "workspace:^",
"@web3-storage/blob-index": "workspace:^",
"@web3-storage/capabilities": "workspace:^",
"@web3-storage/content-claims": "^5.0.0",
"@web3-storage/content-claims": "^5.1.0",
"@web3-storage/did-mailto": "workspace:^",
"@web3-storage/filecoin-api": "workspace:^",
"multiformats": "^12.1.2",
"p-retry": "^5.1.2",
"uint8arrays": "^5.0.3"
},
"devDependencies": {
Expand Down
35 changes: 32 additions & 3 deletions packages/upload-api/src/index/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Server from '@ucanto/server'
import { ok, error } from '@ucanto/server'
import * as Index from '@web3-storage/capabilities/index'
import { ShardedDAGIndex } from '@web3-storage/blob-index'
import { Assert } from '@web3-storage/content-claims/capability'
import { concat } from 'uint8arrays'
import * as API from '../types.js'

Expand Down Expand Up @@ -61,13 +62,21 @@ const add = async ({ capability }, context) => {
shardDigests.map((s) => assertAllocated(context, space, s, 'ShardNotFound'))
)
for (const res of shardAllocRes) {
if (!res.ok) return res
if (res.error) return res
}

// TODO: randomly validate slices in the index correspond to slices in the blob

// publish the index data to IPNI
return context.ipniService.publish(idxRes.ok)
const publishRes = await Promise.all([
// publish the index data to IPNI
context.ipniService.publish(idxRes.ok),
// publish a content claim for the index
publishIndexClaim(context, { content: idxRes.ok.content, index: idxLink }),
])
for (const res of publishRes) {
if (res.error) return res
}
return ok({})
}

/**
Expand All @@ -87,3 +96,23 @@ const assertAllocated = async (context, space, digest, errorName) => {
)
return ok({})
}

/**
* @param {API.ClaimsClientContext} ctx
* @param {{ content: API.UnknownLink, index: API.CARLink }} params
*/
const publishIndexClaim = async (ctx, { content, index }) => {
const { invocationConfig, connection } = ctx.claimsService
const { issuer, audience, with: resource, proofs } = invocationConfig
const res = await Assert.index
.invoke({
issuer,
audience,
with: resource,
nb: { content, index },
expiration: Infinity,
proofs,
})
.execute(connection)
return res.out
}
25 changes: 20 additions & 5 deletions packages/upload-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,21 @@ 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 { Claim } from '@web3-storage/content-claims/client/api'
export type {
IndexServiceContext,
IPNIService,
BlobRetriever,
BlobNotFound,
ShardedDAGIndex,
} from './types/index.js'
export type {
ClaimsInvocationConfig,
ClaimsClientConfig,
ClaimsClientContext,
Service as ClaimsService,
} from './types/content-claims.js'

export interface Service extends StorefrontService, W3sService {
store: {
Expand Down Expand Up @@ -392,7 +400,6 @@ export type UploadServiceContext = ConsumerServiceContext &
ConcludeServiceContext & {
signer: EdSigner.Signer
uploadTable: UploadTable
dudewhereBucket: DudewhereBucket
}

export interface AccessClaimContext {
Expand Down Expand Up @@ -591,6 +598,18 @@ export interface UcantoServerTestContext
ipniService: IPNIService & {
query(digest: MultihashDigest): Promise<Result<Unit, RecordNotFound>>
}

carStoreBucket: CarStoreBucket & Deactivator
blobsStorage: BlobsStorage & Deactivator
claimsService: ClaimsClientConfig & ClaimReader & Deactivator
}

export interface ClaimReader {
read(digest: MultihashDigest): Promise<Result<Claim[], Failure>>
}

export interface Deactivator {
deactivate: () => Promise<void>
}

export interface StoreTestContext {}
Expand Down Expand Up @@ -628,10 +647,6 @@ export interface CarStoreBucketService {
use(options?: CarStoreBucketOptions): Promise<CarStoreBucket>
}

export interface DudewhereBucket {
put: (dataCid: string, carCid: string) => Promise<void>
}

/**
* Indicates the requested record was not present in the table.
*/
Expand Down
31 changes: 31 additions & 0 deletions packages/upload-api/src/types/content-claims.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
ConnectionView,
DID,
Principal,
Proof,
Signer,
} from '@ucanto/interface'
import { Service } from '@web3-storage/content-claims/server/service/api'

export type { ConnectionView, DID, Principal, Proof, Signer }
export type { Service }

export interface ClaimsInvocationConfig {
/** Signing authority issuing the UCAN invocation(s). */
issuer: Signer
/** The principal delegated to in the current UCAN. */
audience: Principal
/** The resource the invocation applies to. */
with: DID
/** Proof(s) the issuer has the capability to perform the action. */
proofs?: Proof[]
}

export interface ClaimsClientConfig {
invocationConfig: ClaimsInvocationConfig
connection: ConnectionView<Service>
}

export interface ClaimsClientContext {
claimsService: ClaimsClientConfig
}
5 changes: 3 additions & 2 deletions packages/upload-api/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { MultihashDigest } from 'multiformats'
import { Failure, Result, Unit } from '@ucanto/interface'
import { ShardedDAGIndex } from '@web3-storage/blob-index/types'
import { AllocationsStorage } from './blob.js'
import { ClaimsClientContext } from './content-claims.js'

export type { ShardedDAGIndex }
export type { ShardedDAGIndex, ClaimsClientContext }

/**
* Service that allows publishing a set of multihashes to IPNI for a
Expand All @@ -26,7 +27,7 @@ export interface BlobRetriever {
): Promise<Result<ReadableStream<Uint8Array>, BlobNotFound>>
}

export interface IndexServiceContext {
export interface IndexServiceContext extends ClaimsClientContext {
allocationsStorage: AllocationsStorage
blobRetriever: BlobRetriever
ipniService: IPNIService
Expand Down
44 changes: 8 additions & 36 deletions packages/upload-api/src/upload/add.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import pRetry from 'p-retry'
import * as Server from '@ucanto/server'
import * as Upload from '@web3-storage/capabilities/upload'
import * as API from '../types.js'
Expand All @@ -10,7 +9,7 @@ import { allocate } from '../space-allocate.js'
*/
export function uploadAddProvider(context) {
return Server.provide(Upload.add, async ({ capability, invocation }) => {
const { uploadTable, dudewhereBucket } = context
const { uploadTable } = context
const { root, shards } = capability.nb
const space = /** @type {import('@ucanto/interface').DIDKey} */ (
Server.DID.parse(capability.with).did()
Expand All @@ -28,39 +27,12 @@ export function uploadAddProvider(context) {
return allocated
}

const [res] = await Promise.all([
// Store in Database
uploadTable.upsert({
space,
root,
shards,
issuer,
invocation: invocation.cid,
}),
writeDataCidToCarCidsMapping(dudewhereBucket, root, shards),
])

return res
})
}

/**
* Writes to a "bucket DB" the mapping from a data CID to the car CIDs it is composed of.
* Retries up to 3 times, in case of failures.
*
* @param {import("../types.js").DudewhereBucket} dudewhereStore
* @param {Server.API.Link<unknown, number, number, 0 | 1>} root
* @param {Server.API.Link<unknown, 514, number, 1>[] | undefined} shards
*/
async function writeDataCidToCarCidsMapping(dudewhereStore, root, shards) {
const dataCid = root.toString()
const carCids =
shards?.map((/** @type {{ toString: () => any; }} */ s) => s.toString()) ||
[]

return Promise.all(
carCids.map(async (/** @type {string} */ carCid) => {
await pRetry(() => dudewhereStore.put(dataCid, carCid), { retries: 3 })
return uploadTable.upsert({
space,
root,
shards,
issuer,
invocation: invocation.cid,
})
)
})
}
Loading

0 comments on commit 37ede86

Please sign in to comment.