Skip to content

Commit

Permalink
chore: use w3up client final version (#190)
Browse files Browse the repository at this point in the history
Needs to wire a server to serve a receipt agent message so that we can
finish the upload blob flow, given this uses a server also for storing
data
  • Loading branch information
vasco-santos authored Jun 4, 2024
1 parent 4961f4a commit 82f3d76
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 21 deletions.
8 changes: 5 additions & 3 deletions can.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ export async function blobAdd(blobPath) {
}

spinner.start('Storing')
const digest = await client.capability.blob.add(blob)
const cid = Link.create(raw.code, digest)
spinner.stopAndPersist({ symbol: '⁂', text: `Stored ${base58btc.encode(digest.bytes)} (${cid})` })
const { multihash } = await client.capability.blob.add(blob, {
receiptsEndpoint: client._receiptsEndpoint.toString()
})
const cid = Link.create(raw.code, multihash)
spinner.stopAndPersist({ symbol: '⁂', text: `Stored ${base58btc.encode(multihash.bytes)} (${cid})` })
}

/**
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export async function upload(firstPath, opts) {
concurrentRequests:
opts?.['concurrent-requests'] &&
parseInt(String(opts?.['concurrent-requests'])),
receiptsEndpoint: client._receiptsEndpoint.toString()
})
spinner.stopAndPersist({
symbol: '⁂',
Expand Down
8 changes: 7 additions & 1 deletion lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ export function getClient() {
process.env.W3UP_SERVICE_DID || process.env.W3_UPLOAD_SERVICE_DID
const uploadServiceURL =
process.env.W3UP_SERVICE_URL || process.env.W3_UPLOAD_SERVICE_URL
const receiptsEndpointString = (process.env.W3UP_RECEIPTS_ENDPOINT || process.env.W3_UPLOAD_RECEIPTS_URL)
let receiptsEndpoint
if (receiptsEndpointString) {
receiptsEndpoint = new URL(receiptsEndpointString)
}

let serviceConf
if (
accessServiceDID &&
Expand Down Expand Up @@ -134,7 +140,7 @@ export function getClient() {
}

/** @type {import('@web3-storage/w3up-client/types').ClientFactoryOptions} */
const createConfig = { store, serviceConf }
const createConfig = { store, serviceConf, receiptsEndpoint }

const principal = process.env.W3_PRINCIPAL
if (principal) {
Expand Down
30 changes: 15 additions & 15 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"@web3-storage/access": "^20.0.0",
"@web3-storage/data-segment": "^5.0.0",
"@web3-storage/did-mailto": "^2.1.0",
"@web3-storage/w3up-client": "^14.0.0-rc.2",
"@web3-storage/w3up-client": "^14.0.0",
"ansi-escapes": "^6.2.0",
"chalk": "^5.3.0",
"files-from-path": "^1.0.4",
Expand Down
7 changes: 7 additions & 0 deletions test/helpers/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { createEnv } from './env.js'
import { Signer } from '@ucanto/principal/ed25519'
import { createServer as createHTTPServer } from './http-server.js'
import { createReceiptsServer } from './receipt-http-server.js'
import http from 'node:http'
import { StoreConf } from '@web3-storage/w3up-client/stores/conf'
import * as FS from 'node:fs/promises'
Expand Down Expand Up @@ -49,6 +50,7 @@ export const provisionSpace = async (context, { space, account, provider }) => {
* @typedef {import('@web3-storage/w3up-client/types').StoreAddSuccess} StoreAddSuccess
* @typedef {UcantoServerTestContext & {
* server: import('./http-server').TestingServer['server']
* receiptsServer: import('./receipt-http-server.js').TestingServer['server']
* router: import('./http-server').Router
* env: { alice: Record<string, string>, bob: Record<string, string> }
* serverURL: URL
Expand All @@ -61,22 +63,26 @@ export const setup = async () => {
const { server, serverURL, router } = await createHTTPServer({
'/': context.connection.channel.request.bind(context.connection.channel),
})
const { server: receiptsServer, serverURL: receiptsServerUrl } = await createReceiptsServer()

return Object.assign(context, {
server,
serverURL,
receiptsServer,
router,
serverRouter: router,
env: {
alice: createEnv({
storeName: `w3cli-test-alice-${context.service.did()}`,
servicePrincipal: context.service,
serviceURL: serverURL,
receiptsEndpoint: new URL('receipt', receiptsServerUrl),
}),
bob: createEnv({
storeName: `w3cli-test-bob-${context.service.did()}`,
servicePrincipal: context.service,
serviceURL: serverURL,
receiptsEndpoint: new URL('receipt', receiptsServerUrl),
}),
},
})
Expand All @@ -88,6 +94,7 @@ export const setup = async () => {
export const teardown = async (context) => {
await cleanupContext(context)
context.server.close()
context.receiptsServer.close()

const stores = [
context.env.alice.W3_STORE_NAME,
Expand Down
4 changes: 3 additions & 1 deletion test/helpers/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
* @param {import('@ucanto/interface').Principal} [options.servicePrincipal]
* @param {URL} [options.serviceURL]
* @param {string} [options.storeName]
* @param {URL} [options.receiptsEndpoint]
*/
export function createEnv(options = {}) {
const { servicePrincipal, serviceURL, storeName } = options
const { servicePrincipal, serviceURL, storeName, receiptsEndpoint } = options
const env = { W3_STORE_NAME: storeName ?? 'w3cli-test' }
if (servicePrincipal && serviceURL) {
Object.assign(env, {
W3UP_SERVICE_DID: servicePrincipal.did(),
W3UP_SERVICE_URL: serviceURL.toString(),
W3UP_RECEIPTS_ENDPOINT: receiptsEndpoint?.toString()
})
}
return env
Expand Down
61 changes: 61 additions & 0 deletions test/helpers/random.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { CarWriter } from '@ipld/car'
import * as CAR from '@ucanto/transport/car'
import { CID } from 'multiformats/cid'
import * as raw from 'multiformats/codecs/raw'
import { sha256 } from 'multiformats/hashes/sha2'

/** @param {number} size */
export async function randomBytes(size) {
const bytes = new Uint8Array(size)
while (size) {
const chunk = new Uint8Array(Math.min(size, 65_536))
if (!globalThis.crypto) {
try {
const { webcrypto } = await import('node:crypto')
webcrypto.getRandomValues(chunk)
} catch (err) {
throw new Error(
'unknown environment - no global crypto and not Node.js',
{ cause: err }
)
}
} else {
crypto.getRandomValues(chunk)
}
size -= chunk.length
bytes.set(chunk, size)
}
return bytes
}

/** @param {number} size */
export async function randomCAR(size) {
const bytes = await randomBytes(size)
return toCAR(bytes)
}

/** @param {Uint8Array} bytes */
export async function toBlock(bytes) {
const hash = await sha256.digest(bytes)
const cid = CID.createV1(raw.code, hash)
return { cid, bytes }
}

/**
* @param {Uint8Array} bytes
*/
export async function toCAR(bytes) {
const block = await toBlock(bytes)
const { writer, out } = CarWriter.create(block.cid)
writer.put(block)
writer.close()

const chunks = []
for await (const chunk of out) {
chunks.push(chunk)
}
const blob = new Blob(chunks)
const cid = await CAR.codec.link(new Uint8Array(await blob.arrayBuffer()))

return Object.assign(blob, { cid, roots: [block.cid] })
}
79 changes: 79 additions & 0 deletions test/helpers/receipt-http-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import http from 'http'
import { once } from 'events'

import { parseLink } from '@ucanto/server'
import * as Signer from '@ucanto/principal/ed25519'
import { Receipt, Message } from '@ucanto/core'
import * as CAR from '@ucanto/transport/car'
import { Assert } from '@web3-storage/content-claims/capability'
import { randomCAR } from './random.js'

/**
* @typedef {{
* server: http.Server
* serverURL: URL
* }} TestingServer
*/

/**
* @returns {Promise<TestingServer>}
*/
export async function createReceiptsServer() {
/**
* @param {http.IncomingMessage} request
* @param {http.ServerResponse} response
*/
const listener = async (request, response) => {
const taskCid = request.url?.split('/')[1] ?? ''
const body = await generateReceipt(taskCid)
response.writeHead(200)
response.end(body)
return undefined
}

const server = http.createServer(listener).listen()

await once(server, 'listening')

return {
server,
// @ts-expect-error
serverURL: new URL(`http://127.0.0.1:${server.address().port}`),
}
}

/**
* @param {string} taskCid
*/
const generateReceipt = async (taskCid) => {
const issuer = await Signer.generate()
const content = (await randomCAR(128)).cid
const locationClaim = await Assert.location.delegate({
issuer,
audience: issuer,
with: issuer.toDIDKey(),
nb: {
content,
location: ['http://localhost'],
},
expiration: Infinity,
})

const receipt = await Receipt.issue({
issuer,
fx: {
fork: [locationClaim],
},
ran: parseLink(taskCid),
result: {
ok: {
site: locationClaim.link(),
},
},
})

const message = await Message.build({
receipts: [receipt],
})
return CAR.request.encode(message).body
}

0 comments on commit 82f3d76

Please sign in to comment.