Skip to content

Commit

Permalink
fix: updates from Alan's review
Browse files Browse the repository at this point in the history
this addresses most of the feedback:

- fail to start if w3up client principal is not passed
- move function to test helpers
- make w3up service DID configurable
  • Loading branch information
travis committed Mar 27, 2024
1 parent 9462e07 commit fc15939
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 38 deletions.
4 changes: 4 additions & 0 deletions packages/api/src/bindings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ export interface ServiceConfiguration {
/** w3up connection URL (e.g. https://up.web3.storage) */
W3UP_URL?: string

/** w3up service DID (e.g. did:web:web3.storage) */
W3UP_DID?: string

/** base64 encoded multiformats ed25519 secretKey */
W3_NFTSTORAGE_PRINCIPAL?: string

Expand Down Expand Up @@ -148,6 +151,7 @@ export interface RouteContext {
r2Uploader: Uploader
ucanService: Service
auth?: Auth
W3UP_DID?: string
W3UP_URL?: string
W3_NFTSTORAGE_PRINCIPAL?: string
W3_NFTSTORAGE_PROOF?: string
Expand Down
2 changes: 2 additions & 0 deletions packages/api/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export function serviceConfigFromVariables(vars) {
COMMITHASH: vars.NFT_STORAGE_COMMITHASH || NFT_STORAGE_COMMITHASH,

W3UP_URL: vars.W3UP_URL,
W3UP_DID: vars.W3UP_DID,
W3_NFTSTORAGE_PRINCIPAL: vars.W3_NFTSTORAGE_PRINCIPAL,
W3_NFTSTORAGE_PROOF: vars.W3_NFTSTORAGE_PROOF,
W3_NFTSTORAGE_SPACE: vars.W3_NFTSTORAGE_SPACE,
Expand Down Expand Up @@ -131,6 +132,7 @@ export function loadConfigVariables() {
'S3_ENDPOINT',
'SLACK_USER_REQUEST_WEBHOOK_URL',
'W3UP_URL',
'W3UP_DID',
'W3_NFTSTORAGE_SPACE',
'W3_NFTSTORAGE_PRINCIPAL',
'W3_NFTSTORAGE_PROOF',
Expand Down
4 changes: 4 additions & 0 deletions packages/api/src/utils/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import pkg from '../../package.json'
import { Service } from 'ucan-storage/service'
import { LinkdexApi } from './linkdex.js'
import { createW3upClientFromConfig } from './w3up.js'
import { DID } from '@ucanto/core'

/**
* Obtains a route context object.
Expand Down Expand Up @@ -66,6 +67,7 @@ export async function getContext(event, params) {

const w3upConfig = {
W3UP_URL: config.W3UP_URL,
W3UP_DID: config.W3UP_DID,
W3_NFTSTORAGE_PRINCIPAL: config.W3_NFTSTORAGE_PRINCIPAL,
W3_NFTSTORAGE_PROOF: config.W3_NFTSTORAGE_PROOF,
W3_NFTSTORAGE_SPACE: config.W3_NFTSTORAGE_SPACE,
Expand All @@ -75,12 +77,14 @@ export async function getContext(event, params) {
let w3up
if (
config.W3UP_URL &&
config.W3UP_DID &&
config.W3_NFTSTORAGE_PRINCIPAL &&
config.W3_NFTSTORAGE_PROOF
) {
try {
const w3upWIP = await createW3upClientFromConfig({
url: config.W3UP_URL,
did: DID.parse(config.W3UP_DID).did(),
principal: config.W3_NFTSTORAGE_PRINCIPAL,
proof: config.W3_NFTSTORAGE_PROOF,
})
Expand Down
41 changes: 7 additions & 34 deletions packages/api/src/utils/w3up.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import { StoreMemory } from '@web3-storage/access/stores/store-memory'
import { CID } from 'multiformats/cid'
import { base64 } from 'multiformats/bases/base64'
import { identity } from 'multiformats/hashes/identity'
import { CarReader, CarWriter } from '@ipld/car'
import { CarReader } from '@ipld/car'
import { importDAG } from '@ucanto/core/delegation'
import * as ucanto from '@ucanto/core'
import * as W3upClient from '@web3-storage/w3up-client'
import { connect } from '@ucanto/client'
import { CAR, HTTP } from '@ucanto/transport'
Expand All @@ -17,7 +16,10 @@ import { CAR, HTTP } from '@ucanto/transport'
* @param {string|undefined} [env.proof]
*/
export async function getW3upClient({ principal, proof } = {}) {
const signer = principal ? ed25519.parse(principal) : await ed25519.generate()
if (!principal) {
throw new Error('could not get w3up client, no principal was passed')
}
const signer = ed25519.parse(principal)
const store = new StoreMemory()
const w3up = await W3UP.create({ principal: signer, store })
if (proof) {
Expand Down Expand Up @@ -72,47 +74,18 @@ export async function readProofFromBytes(bytes) {
}
}

/**
* @param {import('@ucanto/interface').Delegation} delegation - delegation to encode
*/
export async function encodeDelegationAsCid(delegation) {
const { writer, out } = CarWriter.create()
/** @type {Array<Uint8Array>} */
const carChunks = []
await Promise.all([
// write delegation blocks
(async () => {
for (const block of delegation.export()) {
// @ts-expect-error different Block types
await writer.put(block)
}
await writer.close()
})(),
// read out
(async () => {
for await (const chunk of out) {
carChunks.push(chunk)
}
})(),
])
// now get car chunks
const car = new Blob(carChunks)
const bytes = new Uint8Array(await car.arrayBuffer())
const cid = CID.createV1(ucanto.CAR.code, identity.digest(bytes))
return cid
}

/**
* @param {object} options
* @param {string} options.url
* @param {string} options.principal
* @param {string} options.proof
* @param {import('@ucanto/interface').DID} options.did
*/
export async function createW3upClientFromConfig(options) {
const url = new URL(options.url)
const principal = ed25519.parse(options.principal)
const connection = connect({
id: { did: () => 'did:web:web3.storage' },
id: { did: () => options.did },
codec: CAR.outbound,
channel: HTTP.open({
url,
Expand Down
10 changes: 8 additions & 2 deletions packages/api/test/nfts-upload.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,26 @@ import { createCarCid } from '../src/utils/car.js'
import { createServer } from 'node:http'
import { ed25519 } from '@ucanto/principal'
import { delegate } from '@ucanto/core'
import { encodeDelegationAsCid } from '../src/utils/w3up.js'
import { base64 } from 'multiformats/bases/base64'
import { createMockW3up, locate } from './utils/w3up-testing.js'
import {
createMockW3up,
locate,
encodeDelegationAsCid,
} from './utils/w3up-testing.js'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

const nftStorageSpace = ed25519.generate()
const nftStorageApiPrincipal = ed25519.generate()
const nftStorageAccountEmailAllowListedForW3up = '[email protected]'
const mockW3upDID = 'did:web:test.web3.storage'
let mockW3upStoreAddCount = 0
let mockW3upUploadAddCount = 0
const mockW3up = Promise.resolve(
(async function () {
const server = createServer(
await createMockW3up({
did: mockW3upDID,
async onHandleStoreAdd(invocation) {
mockW3upStoreAddCount++
},
Expand All @@ -68,6 +73,7 @@ test.before(async (t) => {
overrides: {
LINKDEX_URL: linkdexUrl,
W3UP_URL: locate((await mockW3up).server).url.toString(),
W3UP_DID: mockW3upDID,
W3_NFTSTORAGE_SPACE: (await nftStorageSpace).did(),
W3_NFTSTORAGE_PRINCIPAL: ed25519.format(await nftStorageApiPrincipal),
W3_NFTSTORAGE_PROOF: (
Expand Down
43 changes: 41 additions & 2 deletions packages/api/test/utils/w3up-testing.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,51 @@ import * as Server from '@ucanto/server'
import * as CAR from '@ucanto/transport/car'
import * as consumers from 'stream/consumers'
import { ed25519 } from '@ucanto/principal'
import { CarWriter } from '@ipld/car'
import * as ucanto from '@ucanto/core'
import { CID } from 'multiformats/cid'
import { identity } from 'multiformats/hashes/identity'

/**
* @param {import('@ucanto/interface').Delegation} delegation - delegation to encode
*/
export async function encodeDelegationAsCid(delegation) {
const { writer, out } = CarWriter.create()
/** @type {Array<Uint8Array>} */
const carChunks = []
await Promise.all([
// write delegation blocks
(async () => {
for (const block of delegation.export()) {
// @ts-expect-error different Block types
await writer.put(block)
}
await writer.close()
})(),
// read out
(async () => {
for await (const chunk of out) {
carChunks.push(chunk)
}
})(),
])
// now get car chunks
const car = new Blob(carChunks)
const bytes = new Uint8Array(await car.arrayBuffer())
const cid = CID.createV1(ucanto.CAR.code, identity.digest(bytes))
return cid
}

/**
* create a RequestListener that can be a mock up.web3.storage
* @param {object} [options] - options
* @param {string} options.did
* @param {(invocation: import('@ucanto/server').ProviderInput<import('@ucanto/client').InferInvokedCapability<typeof Store.add>>) => Promise<void>} [options.onHandleStoreAdd] - called at start of store/add handler
* @param {(invocation: import('@ucanto/server').ProviderInput<import('@ucanto/client').InferInvokedCapability<typeof Upload.add>>) => Promise<void>} [options.onHandleUploadAdd] - called at start of upload/add handler
*/
export async function createMockW3up(options = {}) {
export async function createMockW3up(
options = { did: 'did:web:test.web3.storage' }
) {
const service = {
filecoin: {
offer: Server.provide(Filecoin.offer, async (invocation) => {
Expand Down Expand Up @@ -47,7 +84,9 @@ export async function createMockW3up(options = {}) {
}),
},
}
const serverId = (await ed25519.generate()).withDID('did:web:web3.storage')
const serverId = (await ed25519.generate()).withDID(
ucanto.DID.parse(options.did).did()
)
const server = Server.create({
id: serverId,
service,
Expand Down

0 comments on commit fc15939

Please sign in to comment.