Skip to content

Commit

Permalink
feat!: implement receipts
Browse files Browse the repository at this point in the history
  • Loading branch information
Gozala committed Mar 28, 2023
1 parent 86332db commit d590015
Show file tree
Hide file tree
Showing 27 changed files with 1,410 additions and 373 deletions.
4 changes: 2 additions & 2 deletions packages/client/src/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { sha256 } from 'multiformats/hashes/sha2'
* @param {API.ConnectionOptions<T>} options
* @returns {API.ConnectionView<T>}
*/
export const connect = (options) => new Connection(options)
export const connect = options => new Connection(options)

/**
* @template {Record<string, any>} T
Expand Down Expand Up @@ -42,7 +42,7 @@ class Connection {
* @template {API.Tuple<API.ServiceInvocation<C, T>>} I
* @param {API.Connection<T>} connection
* @param {I} invocations
* @returns {Promise<API.InferServiceInvocations<I, T>>}
* @returns {Promise<API.InferWorkflowReceipts<I, T>>}
*/
export const execute = async (invocations, connection) => {
const request = await connection.encoder.encode(invocations, connection)
Expand Down
6 changes: 3 additions & 3 deletions packages/client/test/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,16 @@ export interface AccessProvider {
* Associates a DID with another DID in the system. If there is no account
* associated with a `to` DID will produce an error.
*/
link(member: DID, group: DID, proof: Link): Result<null, UnknownDIDError>
link(member: DID, group: DID, proof: Link): Result<{}, UnknownDIDError>

unlink(member: DID, group: DID, proof: Link): Result<null, UnknownDIDError>
unlink(member: DID, group: DID, proof: Link): Result<{}, UnknownDIDError>

/**
* Associates new child DID with an accound of the parent DID. If there is no
* account associated with a parent it creates account with `parent` did first
* and then associates child DID with it.
*/
register(member: DID, group: DID, proof: Link): Result<null, UnknownDIDError>
register(member: DID, group: DID, proof: Link): Result<{}, UnknownDIDError>

/**
* Resolves account DID associated with a given DID. Returns either account
Expand Down
5 changes: 1 addition & 4 deletions packages/client/test/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class AccessService {
* with: API.DID
* }} Identify
* @param {API.Invocation<Identify>} ucan
* @returns {Promise<API.Result<null, API.UnknownDIDError>>}
* @returns {Promise<API.Result<{}, API.UnknownDIDError>>}
*/
async identify(ucan) {
const [capability] = ucan.capabilities
Expand All @@ -133,9 +133,6 @@ class AccessService {
/** @type {any} */ (ucan).link
)
}
// } else {
// return access
// }
}
}

Expand Down
12 changes: 12 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@
"./delegation": {
"types": "./dist/src/delegation.d.ts",
"import": "./src/delegation.js"
},
"./receipt": {
"types": "./dist/src/receipt.d.ts",
"import": "./src/receipt.js"
},
"./cbor": {
"types": "./dist/src/cbor.d.ts",
"import": "./src/cbor.js"
},
"./dag": {
"types": "./dist/src/dag.d.ts",
"import": "./src/dag.js"
}
},
"c8": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as API from '@ucanto/interface'
import * as CBOR from '@ipld/dag-cbor'
export { code, decode } from '@ipld/dag-cbor'
export { code, name, decode } from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import { createLink, isLink } from '@ucanto/core'
import { create as createLink, isLink } from 'multiformats/link'

/**
* @param {unknown} data
Expand Down
102 changes: 102 additions & 0 deletions packages/core/src/dag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as API from '@ucanto/interface'
import { create as createLink } from './link.js'
import { sha256 } from 'multiformats/hashes/sha2'
import * as MF from 'multiformats/interface'
import * as CBOR from './cbor.js'

/**
* @param {unknown} value
* @returns {IterableIterator<API.Block>}
*/
export const iterate = function* (value) {
if (
value &&
typeof value === 'object' &&
'iterateIPLDBlocks' in value &&
typeof value.iterateIPLDBlocks === 'function'
) {
yield* value.iterateIPLDBlocks()
}
}

/**
* @template T
* @typedef {Map<API.ToString<API.Link>, API.Block<T>>} BlockStore
*/

/**
* @template [T=unknown]
* @returns {BlockStore<T>}
*/
export const createStore = () => new Map()

/**
* @template T
* @template {T} U
* @param {U} source
* @param {BlockStore<T>} store
* @param {object} options
* @param {MF.BlockEncoder<number, U>} [options.codec]
* @param {MF.MultihashHasher} [options.hasher]
* @returns {Promise<API.Block<U> & { data: U }>}
*/
export const encodeInto = async (
source,
store,
{ codec = CBOR, hasher = sha256 } = {}
) => {
const bytes = codec.encode(source)
const digest = await hasher.digest(bytes)
/** @type {API.Link<U, typeof codec.code, typeof hasher.code>} */
const link = createLink(codec.code, digest)
store.set(/** @type {API.ToString<typeof link>} */ (link.toString()), {
bytes,
cid: link,
})

return { bytes, cid: link, data: source }
}

/**
* @template T
* @template {T} U
* @param {API.Block<U>} block
* @param {BlockStore<T>} store
* @returns {API.Block<U>}
*/
export const addInto = ({ cid, bytes }, store) => {
store.set(/** @type {API.ToString<typeof cid>} */ (cid.toString()), {
bytes,
cid,
})

return { bytes, cid }
}

/**
* @template T
* @template {T} U
* @param {Iterable<API.Block<U>>} source
* @param {BlockStore<T>} store
*/
export const addEveryInto = (source, store) => {
for (const block of source) {
addInto(block, store)
}
}

/**
* @template T
* @param {API.Link<T>} link
* @param {BlockStore<T>} store
* @returns {API.Block<T> & { data: T }}
*/
export const decodeFrom = (link, store) => {
const block = store.get(`${link}`)
/* c8 ignore next 3 */
if (!block) {
throw new Error(`Block for the ${link} is not found`)
}
const data = /** @type {T} */ (CBOR.decode(block.bytes))
return { cid: link, bytes: block.bytes, data }
}
11 changes: 11 additions & 0 deletions packages/core/src/delegation.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ export class Delegation {
get cid() {
return this.root.cid
}
link() {
return this.root.cid
}
get asCID() {
return this.cid
}
Expand All @@ -192,6 +195,10 @@ export class Delegation {
return exportDAG(this.root, this.blocks)
}

iterateIPLDBlocks() {
return exportDAG(this.root, this.blocks)
}

/**
* @type {API.Proof[]}
*/
Expand Down Expand Up @@ -262,6 +269,10 @@ export class Delegation {
return this
}

buildIPLDView() {
return this
}

/**
* @returns {API.DelegationJSON<this>}
*/
Expand Down
40 changes: 38 additions & 2 deletions packages/core/src/invocation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as API from '@ucanto/interface'
import { delegate } from './delegation.js'
import { delegate, Delegation } from './delegation.js'
import * as DAG from './dag.js'

/**
* @template {API.Capability} Capability
Expand All @@ -8,6 +9,30 @@ import { delegate } from './delegation.js'
*/
export const invoke = options => new IssuedInvocation(options)

/**
* @template {API.Capability} C
* @param {object} dag
* @param {API.UCANLink<[C]>} dag.root
* @param {Map<string, API.Block>} dag.blocks
* @returns {API.Invocation<C>}
*/
export const view = ({ root, blocks }) => {
const { bytes, cid } = DAG.decodeFrom(root, blocks)
return new Invocation({ bytes, cid }, blocks)
}

/**
* @template {API.Invocation} Invocation
* @param {object} dag
* @param {ReturnType<Invocation['link']>} dag.root
* @param {Map<string, API.Block>} dag.blocks
* @returns {Invocation|ReturnType<Invocation['link']>}
*/
export const embed = ({ root, blocks }) =>
blocks.has(root.toString())
? /** @type {Invocation} */ (view({ root, blocks }))
: root

/**
* @template {API.Capability} Capability
* @implements {API.IssuedInvocationView<Capability>}
Expand Down Expand Up @@ -52,10 +77,14 @@ class IssuedInvocation {
return delegate(this)
}

buildIPLDView() {
return delegate(this)
}

/**
* @template {API.InvocationService<Capability>} Service
* @param {API.ConnectionView<Service>} connection
* @returns {Promise<API.InferServiceInvocationReturn<Capability, Service>>}
* @returns {Promise<API.InferServiceInvocationReceipt<Capability, Service>>}
*/
async execute(connection) {
/** @type {API.ServiceInvocation<Capability, Service>} */
Expand All @@ -67,3 +96,10 @@ class IssuedInvocation {
return result
}
}

/**
* @template {API.Capability} Capability
* @implements {API.Invocation<Capability>}
* @extends {Delegation<[Capability]>}
*/
export class Invocation extends Delegation {}
5 changes: 5 additions & 0 deletions packages/core/src/lib.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export * as API from '@ucanto/interface'
export * as Delegation from './delegation.js'
export * as Invocation from './invocation.js'
export * as Receipt from './receipt.js'
import * as DAG from './dag.js'
import * as CBOR from './cbor.js'
export { delegate, isDelegation } from './delegation.js'
export { invoke } from './invocation.js'
export {
Expand Down
Loading

0 comments on commit d590015

Please sign in to comment.