Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
adamazad opened this issue Jun 27, 2024 · 10 comments

Comments

@adamazad
Copy link

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

image
@travis
Copy link
Member

travis commented Jun 28, 2024

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
Copy link
Author

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/[email protected]¡gexecute\u0081Ø*X%\u0000\u0001q\u0012 áw¶6\u009d(ÇZÍ\u009bV×fä\u000cZ]\u008d¿)Ê8Î\u001f{®\u008d\u0095\u0021\u001cK\u0080'

@travis
Copy link
Member

travis commented Jun 28, 2024

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
Copy link
Author

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
Copy link
Member

travis commented Jun 28, 2024

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
Copy link
Author

adamazad commented Jul 3, 2024

@travis, any update regarding this issue? Thanks!

@adamazad
Copy link
Author

@travis pinging this again :)

@MathPSantos
Copy link

MathPSantos commented Jul 12, 2024

@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
Copy link
Member

@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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants