storacha / w3up

⁂ w3up protocol implementation
https://github.com/storacha-network/specs
Other
60 stars 22 forks source link

Getting "Capability can not be (self) issued by <did>" when trying to upload via delegation #1515

Open adamazad opened 4 months ago

adamazad commented 4 months ago

Hi, I'm getting the following error when trying to pin a blob via delegated agent to use my space:

image
travis commented 4 months ago

Hi there - sorry to hear you're running into trouble!

Could you give us a bit more information about what you're doing? Are you using the CLI or the web console or a custom JS integration? Happy to help track this down, but can't really do anything with the information you've provided so far.

adamazad commented 4 months ago

Hi @travis, my setup is explained in #1514. I'm delegating my frontend to upload a file directly to my web3.storage space.

curl 'https://up.web3.storage/' \
  -H 'accept: application/vnd.ipld.car' \
  -H 'accept-language: en-US,en;q=0.9,ar;q=0.8' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/vnd.ipld.car' \
  -H 'origin: http://localhost:3000' \
  -H 'pragma: no-cache' \
  -H 'priority: u=1, i' \
  -H 'referer: http://localhost:3000/' \
  -H 'sec-ch-ua: "Google Chrome";v="125", "Chromium";v="125", "Not.A/Brand";v="24"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36' \
  --data-raw $':¢eroots\u0081Ø*X%\u0000\u0001q\u0012 M\u0007¸òì£ÓÈx\u0095\u0002²8ÿÝòMy\u008a\u0090¡¯%µhÃ\u0019+ð\u0098³\u0091gversion\u0001\u008b\u0006\u0001q\u0012 áw¶6\u009d(ÇZÍ\u009bV×fä\u000cZ]\u008d¿)Ê8Î\u001f{®\u008d\u0095\u0021\u001cK\u0080§asY\u0001\u0006\u0085¤À\u0006\u0080\u0002?˾kí\u0005¹øô¾;tÇ\u0019½2\u0089\u0088ôwª¿ÿg\u00083b*52-ea\u0021µt\u009dÐܨn²å4ncX\u0099\u001d \u0009TÅò}\u0008ª\u0005ÚÇEb\u0011\u0094\u0013gì¥~Ú-DE¨³G*Ó\u0085v©v$ú\u0089\u008a=#©\u009eª\u0088©=Îl\u0009ÿÇËU]\u0091;r<j\u001cþ\u0002C¥ØG£nàªÀ\u0092\u0003±5Ð\u0006=½\u0012@%¤­U\u0010Å\u0089Ó\u00916@Ì5Ï¥/\u0008N\u0016æá\nò:Påù\u0005\u008d>M\u0008Î{"â\u000bö\u009déRݾNî|]\u0092óÍ\u0098o«uØæJ~\u001bäzl" ¼\u001f®ðMt\u001e\u0011ac%\u000cÊÚ\u0002RAÈ­Qµ%3\u008b\u0010\u0094á´\u0013\u0002ËÈ\u0011\u0092)¤¨ø\u0087é\u0084\u001dkþä\u000e\u008c;×ôm\u0011¤°8ô#Û\u0011\u0002M+\u008dave0.9.1catt\u0081£bnb¡dblob¢dsize\u001a\u0000\u0095w}fdigestX"\u0012 ü\u009c5?¨æ\u001c\u0099$0p\u0002]X¹Q;:¨m\u0081ã|Y^¦D[\u0004\u009cÖ\u008bccannspace/blob/adddwithx8did:key:z6MkhcFUdVfVTnVPrACYvC29jzQizC6pW6KvmRScxCC6hbr8caudR\u009d\u001aweb:web3.storagecexp\u001af\u007f0McissY\u0001\u0010\u0085$0\u0082\u0001\n\u0002\u0082\u0001\u0001\u0000ß*pêkßL\u0082E\u0098à\u0081,~P\u0080ø\u0011å¡\u009dw&¨à\u0008\u0008¼,äÙd\u000fuÁ\u0004[#¥=\u0093/éÓTÌÛoÊsêS\u00019\u008b\u009dÜýD\u0094\u0005\u0081l´Ä\u008aoÍÎ\u0095\u0000)ùÈ\u000b\u0092\u009b¿æÃtÝ­\u0093\u00927ëºa\u001f\u0019J\u001eZgïO]AÕìÞ\u0098B\u008cZi¬¦£b¾{F¢WéB³&\u001fZL\u0091ö\u00817³Pý?i\u0002\u0080ì\u0091¦wË\u0010\u0080\u0008nì\u008båÅ¡w\u007f5®ä\u000b9\u00099³¸P\u008f-\u0021¾J]~^ÞrkWrKD¢4\u0007p\u008fpÍÒ\u0019Ð\u001e\u00117\u0086£¡ã9* u*õù þ\u0009j1\u0010\u0003PÌ\u0085a3TPê»óÝe¢2B\u0007dy\u0081\u0016Çÿ\u0016µ\u0003\u0091\u001eF\u0011³\u0084ß^\u009eû÷`\u009dôk¢;\u0001(\u0001\u001bn\u008cº\u0085\u0002\u0003\u0001\u0000\u0001cprf\u0080m\u0001q\u0012 M\u0007¸òì£ÓÈx\u0095\u0002²8ÿÝòMy\u008a\u0090¡¯%µhÃ\u0019+ð\u0098³\u0091¡tucanto/message@7.0.0¡gexecute\u0081Ø*X%\u0000\u0001q\u0012 áw¶6\u009d(ÇZÍ\u009bV×fä\u000cZ]\u008d¿)Ê8Î\u001f{®\u008d\u0095\u0021\u001cK\u0080'
travis commented 4 months ago

Thanks - do you have a repository anywhere with this code? #1514 has some basic coding errors so I suspect something similar is happening here, but without the code it's hard to tell.

adamazad commented 4 months ago

I fixed the issues in #1514, and the fixed code yields the error above :)

Next.js backend

File: src/app/api/w3up/delegation/route.ts

import * as DID from '@ipld/dag-ucan/did';
import { getWeb3StorageClient } from './getWeb3StorageClient';

export async function GET() {
  const web3StorageClient = await getWeb3StorageClient();
  const did = web3StorageClient.did();

  // Create a delegation for a specific DID
  const audience = DID.parse(did);
  const abilities = ['upload/add', 'blob/add', 'index/add', 'filecoin/offer'];
  const expiration = Math.floor(Date.now() / 1000) + 60 * 60 * 24; // 24 hours from now
  const delegation = await web3StorageClient.createDelegation(audience, abilities, {
    expiration,
  });

  // Serialize the delegation and send it to the client
  const archive = await delegation.archive();
  const u4 = archive.ok;

  return new Response(u4, {
    status: 200,
  });
}

File: src/app/api/w3up/delegation/getWeb3StorageClient.ts

import { CarReader } from '@ipld/car';
import { Block } from '@ipld/car/api';
import { importDAG } from '@ucanto/core/delegation';
import * as Signer from '@ucanto/principal/ed25519'; // Agents on Node should use Ed25519 keys
import * as Client from '@web3-storage/w3up-client';
import { StoreMemory } from '@web3-storage/w3up-client/stores/memory';

let _client: Client.Client | null = null;

/**
 * Get a web3.storage client
 * @returns {Promise<Client.Client>} A web3.storage client
 */
export async function getWeb3StorageClient() {
  if (_client !== null) {
    return _client;
  }

  const web3StorageKey = process.env.WEB3_STORAGE_KEY;
  const web3StorageProof = process.env.WEB3_STORAGE_PROOF;

  if (!web3StorageKey || !web3StorageProof) {
    throw new Error('WEB3_STORAGE_KEY or WEB3_STORAGE_PROOF is not defined');
  }

  // from "bring your own Agent" example in `Creating a client object" section`
  // used command line to generate KEY and PROOF (stored in env variables)
  // KEY: `npx ucan-key ed --json` in command line, which returns private key and DID for Agent (the private key is stored in KEY)
  // PROOF: w3cli used to run `w3 delegation create <did_from_ucan-key_command_above> --can 'store/add' --can 'upload/add' | base64`, which returns the delegation from Space to the Agent we're using (stored in PROOF)
  const principal = Signer.parse(web3StorageKey);
  const store = new StoreMemory();
  _client = await Client.create({ principal, store });

  // now give Agent the delegation from the Space
  const proof = await parseProof(web3StorageProof);
  const space = await _client.addSpace(proof);
  await _client.setCurrentSpace(space.did());

  return _client;
}

/** @param {string} data Base64 encoded CAR file */
async function parseProof(data: string) {
  const blocks: Block[] = [];
  const reader = await CarReader.fromBytes(Buffer.from(data, 'base64'));
  for await (const block of reader.blocks()) {
    blocks.push(block);
  }
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return importDAG(blocks as any);
}

Frontend

File: src/api/hooks/useWeb3StorageClient.ts

import * as Delegation from '@ucanto/core/delegation';
import * as Client from '@web3-storage/w3up-client';

import { useEffect, useState } from 'react';

export async function getWeb3StorageClient(): Promise<{
  error: Error | null;
  client: Client.Client | null;
}> {
  try {
    // Fetch the delegation from the backend
    const response = await fetch(`/api/w3up/delegation`);
    const data = await response.arrayBuffer();

    const delegationUint8Array = new Uint8Array(data);
    const client = await Client.create();
    // Deserialize the delegation
    const delegation = await Delegation.extract(delegationUint8Array);

    if (!delegation.ok) {
      throw delegation.error as Error;
    }

    // Add proof that this agent has been delegated capabilities on the space
    const space = await client.addSpace(delegation.ok);
    client.setCurrentSpace(space.did());

    return {
      error: null,
      client,
    };
  } catch (error) {
    return {
      error: error as Error,
      client: null,
    };
  }
}

export function useWeb3StorageClient() {
  const [ready, setReady] = useState(false);
  const [client, setClient] = useState<Client.Client | null>(null);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    if (ready) {
      return;
    }

    getWeb3StorageClient().then(({ error, client }) => {
      if (error) {
        console.error(error);
        setReady(false);
        setClient(null);
        setError(error);
        return;
      }

      setClient(client);
      setReady(true);
      setError(null);
    });
  }, []);

  return { ready, client, error };
}

This setup works and I can create a frontend delegated W3up client, but when I try it pin files I get the error in the first message :)

travis commented 4 months ago

ok thank you! may not be able to get back to this until early next week, but I've checked in with a colleague and we'll get back to you asap

adamazad commented 4 months ago

@travis, any update regarding this issue? Thanks!

adamazad commented 3 months ago

@travis pinging this again :)

MathPSantos commented 3 months ago

@adamazad I am having problems with the CarReader.fromBytes function, which package version are you guys using?

This is the following error:

Error: CBOR decode error: too many terminals, data makes no sense
    at Module.decode (webpack-internal:///(rsc)/./node_modules/cborg/lib/decode.js:214:11)

And I generated the proof from the following command w3 delegation create <did_from_ucan-key_command_above> --can 'store/add' --can 'upload/add' --base64

alanshaw commented 3 months ago

@adamazad the error is saying you don't have space/blob/add capability. Your code looks like it is delegating blob/add and index/add and should be delegating space/blob/add and space/index/add.

NingXinHui commented 3 months ago

What are these two parameters? const web3StorageKey = process.env.WEB3_STORAGE_KEY; const web3StorageProof = process.env.WEB3_STORAGE_PROOF; I can't find