Skip to content

Commit

Permalink
feat: wip router
Browse files Browse the repository at this point in the history
  • Loading branch information
alanshaw committed Nov 1, 2024
1 parent ac49094 commit ffcd9c7
Show file tree
Hide file tree
Showing 21 changed files with 770 additions and 254 deletions.
14 changes: 11 additions & 3 deletions packages/capabilities/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
"types": "./dist/test/helpers/*.d.ts",
"import": "./test/helpers/*.js"
},
"./blob": {
"types": "./dist/src/blob.d.ts",
"import": "./src/blob.js"
},
"./filecoin": {
"types": "./dist/src/filecoin/index.d.ts",
"import": "./src/filecoin/index.js"
Expand All @@ -57,9 +61,13 @@
"types": "./dist/src/filecoin/dealer.d.ts",
"import": "./src/filecoin/dealer.js"
},
"./index": {
"types": "./dist/src/index/index.d.ts",
"import": "./src/index/index.js"
"./space/index": {
"types": "./dist/src/space/index.d.ts",
"import": "./src/space/index.js"
},
"./space/blob": {
"types": "./dist/src/space/blob.d.ts",
"import": "./src/space/blob.js"
},
"./web3.storage/blob": {
"types": "./dist/src/web3.storage/blob.d.ts",
Expand Down
202 changes: 58 additions & 144 deletions packages/capabilities/src/blob.js
Original file line number Diff line number Diff line change
@@ -1,177 +1,91 @@
/**
* Blob Capabilities.
*
* Blob is a fixed size byte array addressed by the multihash.
* Usually blobs are used to represent set of IPLD blocks at different byte ranges.
* The blob protocol allows authorized agents allocate memory space on a storage
* node and subsequently verify the content has been accepted by / delivered to
* said node.
*
* These can be imported directly with:
* ```js
* import * as Blob from '@storacha/capabilities/blob'
* import * as Index from '@storacha/capabilities/blob'
* ```
*
* @module
* @see https://github.com/storacha/specs/blob/main/w3-blob.md
*/
import { equals } from 'uint8arrays/equals'
import { capability, Schema, fail, ok } from '@ucanto/validator'
import { equalBlob, equalWith, SpaceDID } from './utils.js'

/**
* Agent capabilities for Blob protocol
*/
import { capability, Schema, Link, ok } from '@ucanto/validator'
import { content } from './space/blob.js'
import {
equalBlob,
equalWith,
SpaceDID,
and,
equal,
checkLink,
Await,
} from './utils.js'

/**
* Capability can only be delegated (but not invoked) allowing audience to
* derived any `space/blob/` prefixed capability for the (memory) space identified
* by DID in the `with` field.
* derive any `blob/` prefixed capability.
*/
export const blob = capability({
can: 'space/blob/*',
/**
* DID of the (memory) space where Blob is intended to
* be stored.
*/
with: SpaceDID,
can: 'blob/*',
/** Storage provider DID. */
with: Schema.did(),
derives: equalWith,
})

/**
* Blob description for being ingested by the service.
*/
export const content = Schema.struct({
/**
* A multihash digest of the blob payload bytes, uniquely identifying blob.
*/
digest: Schema.bytes(),
/**
* Number of bytes contained by this blob. Service will provision write target
* for this exact size. Attempt to write a larger Blob file will fail.
*/
size: Schema.integer(),
})

/**
* `space/blob/add` capability allows agent to store a Blob into a (memory) space
* identified by did:key in the `with` field. Agent should compute blob multihash
* and size and provide it under `nb.blob` field, allowing a service to provision
* a write location for the agent to PUT desired Blob into.
* The `blob/allocate` capability can be invoked to create a memory address on a
* storage node where blob content can be written via a HTTP PUT request.
*/
export const add = capability({
can: 'space/blob/add',
/**
* DID of the (memory) space where Blob is intended to
* be stored.
*/
with: SpaceDID,
export const allocate = capability({
can: 'blob/allocate',
/** Storage provider DID. */
with: Schema.did(),
nb: Schema.struct({
/**
* Blob to be added on the space.
*/
/** Blob to allocate. */
blob: content,
/** Link to the add blob task that initiated the allocation. */
cause: Link,
/** DID of the user space where the allocation takes place. */
space: SpaceDID,
}),
derives: equalBlob,
})

/**
* Capability can be used to remove the stored Blob from the (memory)
* space identified by `with` field.
*/
export const remove = capability({
can: 'space/blob/remove',
/**
* DID of the (memory) space where Blob is stored.
*/
with: SpaceDID,
nb: Schema.struct({
/**
* A multihash digest of the blob payload bytes, uniquely identifying blob.
*/
digest: Schema.bytes(),
}),
derives: (claimed, delegated) => {
if (claimed.with !== delegated.with) {
return fail(
`Expected 'with: "${delegated.with}"' instead got '${claimed.with}'`
)
} else if (
delegated.nb.digest &&
!equals(delegated.nb.digest, claimed.nb.digest)
) {
return fail(
`Link ${
claimed.nb.digest ? `${claimed.nb.digest}` : ''
} violates imposed ${delegated.nb.digest} constraint.`
)
}
return ok({})
},
})

/**
* Capability can be invoked to request a list of stored Blobs in the
* (memory) space identified by `with` field.
*/
export const list = capability({
can: 'space/blob/list',
/**
* DID of the (memory) space where Blobs to be listed are stored.
*/
with: SpaceDID,
nb: Schema.struct({
/**
* A pointer that can be moved back and forth on the list.
* It can be used to paginate a list for instance.
*/
cursor: Schema.string().optional(),
/**
* Maximum number of items per page.
*/
size: Schema.integer().optional(),
}),
derives: (claimed, delegated) => {
if (claimed.with !== delegated.with) {
return fail(
`Expected 'with: "${delegated.with}"' instead got '${claimed.with}'`
)
}
return ok({})
},
derives: (claimed, delegated) => (
and(equalWith(claimed, delegated)) ||
and(equalBlob(claimed, delegated)) ||
and(checkLink(claimed.nb.cause, delegated.nb.cause, 'cause')) ||
and(equal(claimed.nb.space, delegated.nb.space, 'space')) ||
ok({})
),
})

/**
* Capability can be used to get the stored Blob from the (memory)
* space identified by `with` field.
* The `blob/accept` capability invocation should either succeed when content is
* delivered on allocated address or fail if no content is allocation expires
* without content being delivered.
*/
export const get = capability({
can: 'space/blob/get/0/1',
/**
* DID of the (memory) space where Blob is stored.
*/
with: SpaceDID,
export const accept = capability({
can: 'blob/accept',
/** Storage provider DID. */
with: Schema.did(),
nb: Schema.struct({
/**
* A multihash digest of the blob payload bytes, uniquely identifying blob.
*/
digest: Schema.bytes(),
/** Blob to accept. */
blob: content,
/** DID of the user space where allocation took place. */
space: SpaceDID,
/** This task is blocked on `http/put` receipt available */
_put: Await,
}),
derives: (claimed, delegated) => {
if (claimed.with !== delegated.with) {
return fail(
`Expected 'with: "${delegated.with}"' instead got '${claimed.with}'`
)
} else if (
delegated.nb.digest &&
!equals(delegated.nb.digest, claimed.nb.digest)
) {
return fail(
`Link ${
claimed.nb.digest ? `${claimed.nb.digest}` : ''
} violates imposed ${delegated.nb.digest} constraint.`
)
}
return ok({})
},
derives: (claimed, delegated) => (
and(equalWith(claimed, delegated)) ||
and(equalBlob(claimed, delegated)) ||
and(equal(claimed.nb.space, delegated.nb.space, 'space')) ||
ok({})
),
})

// ⚠️ We export imports here so they are not omitted in generated typedefs
// @see https://github.com/microsoft/TypeScript/issues/51548
export { Schema }
export { Schema, Link }
2 changes: 1 addition & 1 deletion packages/capabilities/src/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @module
*/
import { capability, Schema, ok } from '@ucanto/validator'
import { content } from './blob.js'
import { content } from './space/blob.js'
import { equal, equalBody, equalWith, SpaceDID, Await, and } from './utils.js'

/**
Expand Down
20 changes: 10 additions & 10 deletions packages/capabilities/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import * as Storefront from './filecoin/storefront.js'
import * as Aggregator from './filecoin/aggregator.js'
import * as Dealer from './filecoin/dealer.js'
import * as DealTracker from './filecoin/deal-tracker.js'
import * as Index from './index/index.js'
import * as SpaceIndex from './space/index.js'
import * as UCAN from './ucan.js'
import * as Plan from './plan.js'
import * as Usage from './usage.js'
import * as Blob from './blob.js'
import * as SpaceBlob from './space/blob.js'
import * as W3sBlob from './web3.storage/blob.js'
import * as HTTP from './http.js'

Expand All @@ -38,7 +38,7 @@ export {
RateLimit,
Subscription,
Filecoin,
Index,
SpaceIndex,
Storefront,
Aggregator,
Dealer,
Expand All @@ -47,7 +47,7 @@ export {
UCAN,
Plan,
Usage,
Blob,
SpaceBlob,
W3sBlob,
HTTP,
}
Expand Down Expand Up @@ -97,14 +97,14 @@ export const abilitiesAsStrings = [
Plan.createAdminSession.can,
Usage.usage.can,
Usage.report.can,
Blob.blob.can,
Blob.add.can,
Blob.remove.can,
Blob.list.can,
SpaceBlob.blob.can,
SpaceBlob.add.can,
SpaceBlob.remove.can,
SpaceBlob.list.can,
W3sBlob.blob.can,
W3sBlob.allocate.can,
W3sBlob.accept.can,
HTTP.put.can,
Index.index.can,
Index.add.can,
SpaceIndex.index.can,
SpaceIndex.add.can,
]
Loading

0 comments on commit ffcd9c7

Please sign in to comment.