decentralized-identity / veramo

A JavaScript Framework for Verifiable Data
https://veramo.io
Apache License 2.0
431 stars 131 forks source link

Error when "veramo did add-key" #862

Closed weippig closed 2 years ago

weippig commented 2 years ago

When I use the command veramo did add-key,encountering this error :

Error: insufficient funds for intrinsic transaction cost [ See: https://links.ethers.org/v5-errors-INSUFFICIENT_FUNDS ] (error={"reason":"processing response error","code":"SERVER_ERROR","body":"{\"jsonrpc\":\"2.0\",\"id\":52,\"error\":{\"code\":-32000,\"message\":\"insufficient funds for gas * price + value\"}}","error":{"code":-32000},"requestBody":"{\"method\":\"eth_sendRawTransaction\",\"params\":[\"0x02f9017104808459682f00846f9541b4830f424194dca7ef03e98e0dc2b855be647c39abe984fcf21b80b901047ad4b0a40000000000000000000000009c5280252ff17fad291a9d1596847668968bfb886469642f7075622f536563703235366b312f766572694b65792f68657800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000001da9c010000000000000000000000000000000000000000000000000000000000000041044fd0b9de1a550c5923e7059f0cc78b6be2640efb6780b1c0e0a25bc73d75653f6a911a0ad721ea8b9ac3d3298fdf3beed192b8bad1ed3a003a47fdc47180787100000000000000000000000000000000000000000000000000000000000000c001a016809b76c7a0257293442463c5d0b9c4a5ba6c33ba05d9e4445b7545f5d82f21a002c99dba95e3abe5753c0925b28902c3b1cd6bb90a20c664f0f52e1cdc15ec61\"],\"id\":52,\"jsonrpc\":\"2.0\"}","requestMethod":"POST","url":"https://rinkeby.infura.io/v3/d3321433cd864acba8dcc581a2801225"}, method="sendTransaction", transaction="0x02f9017104808459682f00846f9541b4830f424194dca7ef03e98e0dc2b855be647c39abe984fcf21b80b901047ad4b0a40000000000000000000000009c5280252ff17fad291a9d1596847668968bfb886469642f7075622f536563703235366b312f766572694b65792f68657800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000001da9c010000000000000000000000000000000000000000000000000000000000000041044fd0b9de1a550c5923e7059f0cc78b6be2640efb6780b1c0e0a25bc73d75653f6a911a0ad721ea8b9ac3d3298fdf3beed192b8bad1ed3a003a47fdc47180787100000000000000000000000000000000000000000000000000000000000000c001a016809b76c7a0257293442463c5d0b9c4a5ba6c33ba05d9e4445b7545f5d82f21a002c99dba95e3abe5753c0925b28902c3b1cd6bb90a20c664f0f52e1cdc15ec61", code=INSUFFICIENT_FUNDS, version=providers/5.6.4)
    at Logger.makeError (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/logger/lib/index.js:233:21)
    at Logger.throwError (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/logger/lib/index.js:242:20)
    at checkError (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:123:16)
    at JsonRpcProvider.<anonymous> (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:688:47)
    at step (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:48:23)
    at Object.throw (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:29:53)
    at rejected (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:21:65)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  reason: 'insufficient funds for intrinsic transaction cost',
  code: 'INSUFFICIENT_FUNDS',
  error: Error: processing response error (body="{\"jsonrpc\":\"2.0\",\"id\":52,\"error\":{\"code\":-32000,\"message\":\"insufficient funds for gas * price + value\"}}", error={"code":-32000}, requestBody="{\"method\":\"eth_sendRawTransaction\",\"params\":[\"0x02f9017104808459682f00846f9541b4830f424194dca7ef03e98e0dc2b855be647c39abe984fcf21b80b901047ad4b0a40000000000000000000000009c5280252ff17fad291a9d1596847668968bfb886469642f7075622f536563703235366b312f766572694b65792f68657800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000001da9c010000000000000000000000000000000000000000000000000000000000000041044fd0b9de1a550c5923e7059f0cc78b6be2640efb6780b1c0e0a25bc73d75653f6a911a0ad721ea8b9ac3d3298fdf3beed192b8bad1ed3a003a47fdc47180787100000000000000000000000000000000000000000000000000000000000000c001a016809b76c7a0257293442463c5d0b9c4a5ba6c33ba05d9e4445b7545f5d82f21a002c99dba95e3abe5753c0925b28902c3b1cd6bb90a20c664f0f52e1cdc15ec61\"],\"id\":52,\"jsonrpc\":\"2.0\"}", requestMethod="POST", url="https://rinkeby.infura.io/v3/d3321433cd864acba8dcc581a2801225", code=SERVER_ERROR, version=web/5.6.0)
      at Logger.makeError (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/logger/lib/index.js:233:21)
      at Logger.throwError (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/logger/lib/index.js:242:20)
      at /usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:305:32
      at step (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:33:23)
      at Object.next (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:14:53)
      at fulfilled (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:5:58)
      at processTicksAndRejections (node:internal/process/task_queues:96:5) {
    reason: 'processing response error',
    code: 'SERVER_ERROR',
    body: '{"jsonrpc":"2.0","id":52,"error":{"code":-32000,"message":"insufficient funds for gas * price + value"}}',
    error: Error: insufficient funds for gas * price + value
        at getResult (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:170:21)
        at processJsonFunc (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:348:22)
        at /usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:280:46
        at step (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:33:23)
        at Object.next (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:14:53)
        at fulfilled (/usr/local/lib/node_modules/@veramo/cli/node_modules/@ethersproject/web/lib/index.js:5:58)
        at processTicksAndRejections (node:internal/process/task_queues:96:5) {
      code: -32000,
      data: undefined
    },
    requestBody: '{"method":"eth_sendRawTransaction","params":["0x02f9017104808459682f00846f9541b4830f424194dca7ef03e98e0dc2b855be647c39abe984fcf21b80b901047ad4b0a40000000000000000000000009c5280252ff17fad291a9d1596847668968bfb886469642f7075622f536563703235366b312f766572694b65792f68657800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000001da9c010000000000000000000000000000000000000000000000000000000000000041044fd0b9de1a550c5923e7059f0cc78b6be2640efb6780b1c0e0a25bc73d75653f6a911a0ad721ea8b9ac3d3298fdf3beed192b8bad1ed3a003a47fdc47180787100000000000000000000000000000000000000000000000000000000000000c001a016809b76c7a0257293442463c5d0b9c4a5ba6c33ba05d9e4445b7545f5d82f21a002c99dba95e3abe5753c0925b28902c3b1cd6bb90a20c664f0f52e1cdc15ec61"],"id":52,"jsonrpc":"2.0"}',
    requestMethod: 'POST',
    url: 'https://rinkeby.infura.io/v3/d3321433cd864acba8dcc581a2801225'
  },
  method: 'sendTransaction',
  transaction: {
    type: 2,
    chainId: 4,
    nonce: 0,
    maxPriorityFeePerGas: BigNumber { _hex: '0x59682f00', _isBigNumber: true },
    maxFeePerGas: BigNumber { _hex: '0x6f9541b4', _isBigNumber: true },
    gasPrice: null,
    gasLimit: BigNumber { _hex: '0x0f4241', _isBigNumber: true },
    to: '0xdCa7EF03e98e0DC2B855bE647C39ABe984fcF21B',
    value: BigNumber { _hex: '0x00', _isBigNumber: true },
    data: '0x7ad4b0a40000000000000000000000009c5280252ff17fad291a9d1596847668968bfb886469642f7075622f536563703235366b312f766572694b65792f68657800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000001da9c010000000000000000000000000000000000000000000000000000000000000041044fd0b9de1a550c5923e7059f0cc78b6be2640efb6780b1c0e0a25bc73d75653f6a911a0ad721ea8b9ac3d3298fdf3beed192b8bad1ed3a003a47fdc47180787100000000000000000000000000000000000000000000000000000000000000',
    accessList: [],
    hash: '0xf26aa1211ac1ac21843b5a2b7bbf163f78c9a557a97b821164f117edd78de81d',
    v: 1,
    r: '0x16809b76c7a0257293442463c5d0b9c4a5ba6c33ba05d9e4445b7545f5d82f21',
    s: '0x02c99dba95e3abe5753c0925b28902c3b1cd6bb90a20c664f0f52e1cdc15ec61',
    from: '0x9C5280252ff17FAd291a9D1596847668968bfB88',
    confirmations: 0
  },
  transactionHash: '0xf26aa1211ac1ac21843b5a2b7bbf163f78c9a557a97b821164f117edd78de81d'
}

Where to setup my metamask account private key? I can't find it. Thanks!

mirceanis commented 2 years ago

@weippig it's not recommended to use your metamask private key for this, but you likely need to send some rinkeby ETH to the DID controller.

I assume you are trying to add a key to a did:ethr document, which requires a transaction to be sent and that transaction needs gas.

This means you have to fund the controller address of that DID.

If the error message doesn't indicate the address, then you can discover it by running veramo did resolve <your DID here> and looking at the part of the resolult that says blockchainAccountId

I hope this helps

weippig commented 2 years ago
截圖 2022-04-29 下午1 42 11
  1. So I need to transfer some ETH from my metamask account to 0x26A079E213624Fe0be3C325faE575982B96c5f00 ?
  2. If I delete this program, How do i get my account(0x26A079E213624Fe0be3C325faE575982B96c5f00) back? Is this the same thing as KMS?
  3. Is the place where the yellow box is the public key of this account? Why we need to use computePublicKey function to convert publicKeyHex to publicKey? Can I use private key to find my account back?(like matamask do)

Sorry, I have too many question:( Thank you!

mirceanis commented 2 years ago
  1. Yes, that is the controller address 0x26A079E213624Fe0be3C325faE575982B96c5f00 and it is on the Rinkeby network. Be careful to choose the Rinkeby network in metamask, and not mainnet!
  2. The data related to your local Veramo installation is stored in a database file agent.sqlite next to the configuration file agent.yml. You will need to keep those files if you delete this installation. I don't understand the question about the KMS.
  3. The yellow box is indeed the public key of the account, written in HEX encoding. The controller address can also be computed from that public key, but in most cases it is easier to do did resolve. You can also compute everything from the private key, but it is hard to extract the private key from the database.

It's OK to ask questions, no need to apologize :) I hope my answers helped

weippig commented 2 years ago

Your answer helped me a lot! But I still have some questions:

  1. Does every DID have their own special blockchainAccountId ? Is there a function that can help me get the private key for this blockchainAccountId? Is blockchainAccountId created by my DID public key ?

  2. Can I create a key-pair by myself and use this key-pair to create a DID? Because I want to create the mnemonic seed first and then create the key-pair. But looking at the source code below, it seems that while creating the DID, the program will also help me create the key-pair first.

    截圖 2022-05-01 下午12 03 47
  3. Is the KMS secret-key related to the DID key pair? Or is he just like the database administrator?

    截圖 2022-05-01 下午12 01 04

Thank you!!!

mirceanis commented 2 years ago

I'm glad to hear that this is helping, let me try to answer some more:

  1. In the case of did:ethr there should always be a controller address with a blockchainAccountId. The controller address can be changed along the way, but the resolution process will reveal the current one. There can also be multiple entries in the verificationMethod array that have a blockchainAccountId but only one will have the id property ending in #controller.

  2. Yes, definitely. You would create the mnemonic, derive the private key, and then call agent.didManagerImport. I'm not sure if the import process will derive everything automatically just from the private key, and I can't check at this time, but please look through the code base and especially through the test suite to see how it is done in other circumstances.

  3. The KMS secret key is used to encrypt all private keys stored by Veramo in the local database. It is not used to derive DIDs, only to encrypt some data locally. If you delete your installation of veramo and then want to recover the data, you will need that KMS key along with the database file.

I hope this helps. Have fun!

weippig commented 2 years ago

Hello! I did some implementation: code

import { agent } from './veramo/setup'
import { Wallet } from '@ethersproject/wallet'
import { computePublicKey } from '@ethersproject/signing-key'

async function main() {
    const wallet = Wallet.createRandom()
    const mnemonic = wallet.mnemonic.phrase
    const publicKeyHex = wallet.publicKey.substring(2)
    const privateKeyHex = wallet.privateKey.substring(2)
    const address = wallet.address
    const compressedPublicKey = computePublicKey(`0x${publicKeyHex}`, true)
    const identifier = `did:ethr:rinkeby:${compressedPublicKey}`;

    console.log('mnemonic:', mnemonic)
    console.log('publicKeyHex:', publicKeyHex)
    console.log('privateKeyHex:', privateKeyHex)
    console.log(`send Rinkeby ETH to ${address} to be able to update ${identifier}`);
    try {
        const identity = await agent.didManagerImport({
          did: identifier,
          provider: 'did:ethr:rinkeby',
          controllerKeyId: publicKeyHex,
          keys: [
            {
              kid: publicKeyHex,
              kms: 'local',
              type: 'Secp256k1',
              publicKeyHex: publicKeyHex,
              privateKeyHex: privateKeyHex,
            },
          ],
          services: [],
        });
        console.log(`New identity created`)
        console.log(identity)
      } catch (err) {
        console.log(err);
    }
}

main().catch(console.log)

And I use these code to create first DID : result

截圖 2022-05-02 下午9 38 07

I can import privateKeyHex to metamask wallet, and its address is the same as above(0x54e7363...), so far so good.

Then I create second DID, but change controllerKeyId to first DID 's publicKeyHex like :

const identity = await agent.didManagerImport({
          did: identifier,
          provider: 'did:ethr:rinkeby',
          controllerKeyId: '04c35d5374f8541dc09a739766f10349d1699490dfff4caeeb2d1e922fcc7c185a80dc91518b355a1707f5115c18855986eab5e25d0860c1d290519516bd476395',
          keys: [
            {
              kid: publicKeyHex,
              kms: 'local',
              type: 'Secp256k1',
              publicKeyHex: publicKeyHex,
              privateKeyHex: privateKeyHex,
            },
          ],
          services: [],
        });

result

截圖 2022-05-02 下午10 08 49

Then I use resolveDid function to resolve second DID:

截圖 2022-05-02 下午10 07 22
  1. I thought its controller would be the first DID and then the blockchainAccountId would be the first DID's account(0x54e.....), but it wasn't. I want all DIDs to have the same blockchainAccountId and share the ETH in it to pay for addKey & addService gas fees.
  2. Do I need to call didManagerAddKey function every time I create a DID? Because I found out that DID which include compressedPublicKey seems to be able to deduce publickey?Is it possible to issue and verify VC without running didManagerAddKey function?

I seem to have a lot of misconceptions. I learned a lot from your answer, thank you very much :)

mirceanis commented 2 years ago

@weippig nice work digging into this. Indeed there are some misconceptions that I should try to clarify. I will have to start with some basics.

did:ethr is based on an implementation of the ERC1056, which uses the concept of owner. The owner is the ethereum address that is allowed to make changes to the DID document by setting or revoking attributes (which means adding or removing verificationMethod and service endpoints from the DID document) or by setting a new owner address.

In the most basic case, the owner address is an ethereum address generated from a private key. In more advanced cases the owner can be another smart contract. Veramo only supports the simple case so far and it uses the controllerKeyId property to remember which of the private keys managed by the @veramo/key-manager corresponds to the owner address of a did:ethr. It needs to do this because every time you call didManagerAddKey, or other methods to modify a did:ethr DID document, it will internally sign an ethereum transaction using that private key.

When you call didManagerImport Veramo simply records the parameters you specify for that DID in its internal database, but does not change the owner property on the smart contract automatically. If the controllerKeyId is set to some other key that doesn't correspond to the owner, then the future calls to didManagerAddKey or others will fail, because the transactions won't be accepted by the contract.

Also, since the owner doesn't change automatically on the smart contract, it means that when the DID is resolved by interrogating the contract, there is no change to the #controller verificationMethod in the resulting DID document.

There is a proposal ( #583) to add a plugin to Veramo that will allow you to change the owner and implicitly the controllerKeyId, but there is not enough bandwidth now to implement that at the moment. This would be one option to use only one address to pay the gas fees.

There is another option, but it also requires some implementation. The ERC1056 contract allows a third party to pay the gas fees by using the setAttributeSigned() method and other doSomethingSigned() methods. This means that the owner signs the modification to the DID document and sends this signed string to a third party which incorporates this signed string in a transation and implicitly also paying for the gas. However, this is not implemented in Veramo.


Now, to answer your second question, which is perhaps some good news for you.

You do not have to call didManagerAddKey for every new did:ethr you create. You can use it directly to sign VCs and these VCs will also be perfectly verifiable. Take a look at the __tests__ folder where there are plenty of examples of this in our integration test suite. Here is one example.

did:ethr implicitly has one or two verification methods listed out of the box, depending on whether you use the did:ethr:<ethereumAddress> format or the did:ethr:<compressedPublicKey> format. They can both sign JWT VCs using the ES256K-R algorithm, and the compressedPublicKey format can also sign ES256K. (actually with veramo both formats can sign using both algorithms, but other libraries might only accept one algorithm for each format).

Wow, that is a lot of text, I hope it gives you a better idea about what is going on.

weippig commented 2 years ago

Thank you for the detailed explanation!I only read the W3C official documentation before and didn't know Veramo use ERC1056, I think that's why I have so many misconceptions.

In conclusion:

  1. We can't change DID owner and use only one address to pay the gas fees in Veramo now.
  2. If we change DID's controllerKeyId, we shoud change owner on the smart contract(use setAttributeSigned() method) in the same time. Otherwise our transaction will fail.
  3. When using function resolveDid(or make some transaction), Resolver will check the transaction records of DID's account on Rinkeby chain to determine whether the controller has been changed.

Still have some problem:

  1. How do we get our DID-document and publicKey if it is offline and nothing is written to the smart contract? Is the public key converted from the compressedPublicKey in DID?
  2. When will didManagerAddKey be used? Does this mean that a DID can have many public keys (sounds unreasonable)?

Thank you!

mirceanis commented 2 years ago

I'm glad we're making some progress. Let me try to answer the rest.

  1. You can't change the owner using the Veramo API yet. It can be done externally but you would then have to import the correct keys in Veramo and point the controllerKeyId to it.
  2. Yes, if you change the controllerKeyId, you also have to change the owner in the contract, but the method in the contract to change the owner is changeOwner() or its equivalent changeOwnerSigned()
  3. correct. The resolver will check the blockchain to see if the DID has any updates, including changes of owner address, additional public key entries, or revocations, additional service endpoints.
  4. did:ethr doesn't work offline since the resolver needs to verify if the DID has any updates. The DID document for a did:ethr is initially constructed from the public key or address part of the DID URL string. This is what allows did:ethr to be created for free, without a transaction. But then, the document can be mutated using transactions on an ethereum blockchain. This is the reason why resolving the DID will always have to verify the entries on the blockchain; to get the latest form of the DID document.
  5. didManagerAddKey will be used whenever a DID controller wants to delegate signing capabilities to a third party, or if they use different keys on different devices, or if they want to add different types of keys for different signing algorithms or for encryption. It is perfectly reasonable to have multiple keys listed in a DID document.

I hope these answers make things clearer for you. If you think you got all your answers please close this issue.

weippig commented 2 years ago

Thank you for your answer, it helped me a lot! 👍

weippig commented 2 years ago

Already got the answer, close the issue, thanks again.