Create Affinidi Wallet Demo#

What wallet actually is ?#

Name wallet create a lot of confusion. In real life you keep your kredit cards, money, identity cards in a wallet. Any crypto wallet have one and only one function that really matter - it is manage and give access to your private and public keys.

Wallet is more

  • key manager - manage access to a keys

  • signer - sign VCs , transactions, JWTs with your private key

Majority of crypto Wallets show a state of your account and etc. Affinity Wallet is more focused on VCs

Follow a video#

Create a wallet on edge#

const { createWallet } = require('@affinidi/wallet-node-sdk')
import { EventComponent } from '@affinidi/wallet-core-sdk'
const walletFactory = createWallet(EventComponent.AffinidiCore)
const accessApiKey = '<your key>' 
    
const options = {
    env: 'prod',
    accessApiKey: accessApiKey
}

All you need on edge is a password

const password = 'P@55w0rd!!'

Now we could generate you a wallet

const sdk = await walletFactory.createWallet(options, password)
const  seed = sdk.encryptedSeed 
const  did = sdk.did  
  
did 
did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q;elem:initial-state=eyJwcm90ZWN0ZWQiOiJleUp2Y0dWeVlYUnBiMjRpT2lKamNtVmhkR1VpTENKcmFXUWlPaUlqY0hKcGJXRnllU0lzSW1Gc1p5STZJa1ZUTWpVMlN5SjkiLCJwYXlsb2FkIjoiZXlKQVkyOXVkR1Y0ZENJNkltaDBkSEJ6T2k4dmR6TnBaQzV2Y21jdmMyVmpkWEpwZEhrdmRqSWlMQ0p3ZFdKc2FXTkxaWGtpT2x0N0ltbGtJam9pSTNCeWFXMWhjbmtpTENKMWMyRm5aU0k2SW5OcFoyNXBibWNpTENKMGVYQmxJam9pVTJWamNESTFObXN4Vm1WeWFXWnBZMkYwYVc5dVMyVjVNakF4T0NJc0luQjFZbXhwWTB0bGVVaGxlQ0k2SWpBeU1tTmhOak5tWm1aaVpEaGlObVJrTjJVMU5HWmhPRGhpTnpaa05USTBOVGN3TUdGak9ERTJOVGRtWkRVNVlUQXpZamN6WlRRek1qVmlZVEZsTVRsaVlTSjlMSHNpYVdRaU9pSWpjbVZqYjNabGNua2lMQ0oxYzJGblpTSTZJbkpsWTI5MlpYSjVJaXdpZEhsd1pTSTZJbE5sWTNBeU5UWnJNVlpsY21sbWFXTmhkR2x2Ymt0bGVUSXdNVGdpTENKd2RXSnNhV05MWlhsSVpYZ2lPaUl3TXprd1pEWTNZbVppWm1NNE1HUXdNR1ZrWXpjd09EQmhOR001TVdZeFl6ZzBOREl3T0daaFltUXdNMlV4TlRoaE5Ua3hNR1kxWkRFMk1ERmxOamxsWWpVaWZWMHNJbUYxZEdobGJuUnBZMkYwYVc5dUlqcGJJaU53Y21sdFlYSjVJbDBzSW1GemMyVnlkR2x2YmsxbGRHaHZaQ0k2V3lJamNISnBiV0Z5ZVNKZGZRIiwic2lnbmF0dXJlIjoiTkJqblFlQVRuTXVJQUFrZTlEcFhDeXAzcmJzcnBsQWFRQ1A0ZEl2b3NfNVJZRnJTc2VDdnBSY3k1XzFOMUpObzB2WEpDdWVVVmZvaHJtNHROenM2cmcifQ

Wallet Internals#

Whats actually happens Our Wallet is clasical hierarchical deterministic wallet based on BIP-32 standard. It is mean that based on seed (32 bit random entropy) we generate all private , public keys and derived addresses etc.

All you need is seed. We never expose a seed or private keys ouside of wallet.

We use a user pasword to encrypt a seed. As a result user get encrypted seed and password that allow to create an instance of wallet any time All you need is securely store this pair and do not share it on public internet

Hide code cell source
const plantumlEncoder = require('plantuml-encoder')
const tslab = require("tslab");
const diagram = plantumlEncoder.encode(`
@startuml
digraph G {
    graph [rankdir = LR, splines=ortho];

    node[shape=record];
    User[label="User", height=4];
    User2[label="User", height=4];
    subgraph SDK {
      graph [rankdir = LR, splines=ortho];
      node[shape=record];
      label = "SDK";
      random[label="random", height=1];
      encrypt[label="encrypt", height=2];
      seedGenerator[label="seed Generator", height=1];
      bip32[label="bip32", height=2];
      didDocGenerator[label="DidDoc Generator", height=1];
      registry[label="Registry", height=1];
    }
    random -> seedGenerator [label="32 bit"]
    User -> seedGenerator [label="meta data"]
    seedGenerator -> encrypt [label="seed"]
    User -> encrypt [label="password"]
    encrypt -> User2  [label="encrypted seed"]
    User -> bip32 [label="meta data"]
    seedGenerator -> bip32 [label="seed"]
    bip32 -> didDocGenerator  [label="public keys"]
    bip32 -> didDocGenerator  [label="private keys"]
    bip32 -> registry [label="private keys"]
    didDocGenerator -> registry [label="document"]
    registry -> User2 [label="did"]
}
@enduml`)
const urldiagram = 'http://www.plantuml.com/plantuml/svg/' + diagram
tslab.display.html(`<img src="${urldiagram}"/>`)
    

Unlock wallet with seed and password#

Now you could unlock your wallet with seed and password

const sdk = await walletFactory.openWalletByEncryptedSeed(options, seed , password)
sdk.did === did
true

DID magic . lets resolve it#

Dids follow a URI schema and could be parsed Every did have a

  • schema . always equal to did

  • method

  • identifier Some did methods like element could have a initial parameters

const didParser = require('did-uri')
const parsedDid = didParser.parse(did)
parsedDid
{
  reference: 'did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q;elem:initial-state=eyJwcm90ZWN0ZWQiOiJleUp2Y0dWeVlYUnBiMjRpT2lKamNtVmhkR1VpTENKcmFXUWlPaUlqY0hKcGJXRnllU0lzSW1Gc1p5STZJa1ZUTWpVMlN5SjkiLCJwYXlsb2FkIjoiZXlKQVkyOXVkR1Y0ZENJNkltaDBkSEJ6T2k4dmR6TnBaQzV2Y21jdmMyVmpkWEpwZEhrdmRqSWlMQ0p3ZFdKc2FXTkxaWGtpT2x0N0ltbGtJam9pSTNCeWFXMWhjbmtpTENKMWMyRm5aU0k2SW5OcFoyNXBibWNpTENKMGVYQmxJam9pVTJWamNESTFObXN4Vm1WeWFXWnBZMkYwYVc5dVMyVjVNakF4T0NJc0luQjFZbXhwWTB0bGVVaGxlQ0k2SWpBeU1tTmhOak5tWm1aaVpEaGlObVJrTjJVMU5HWmhPRGhpTnpaa05USTBOVGN3TUdGak9ERTJOVGRtWkRVNVlUQXpZamN6WlRRek1qVmlZVEZsTVRsaVlTSjlMSHNpYVdRaU9pSWpjbVZqYjNabGNua2lMQ0oxYzJGblpTSTZJbkpsWTI5MlpYSjVJaXdpZEhsd1pTSTZJbE5sWTNBeU5UWnJNVlpsY21sbWFXTmhkR2x2Ymt0bGVUSXdNVGdpTENKd2RXSnNhV05MWlhsSVpYZ2lPaUl3TXprd1pEWTNZbVppWm1NNE1HUXdNR1ZrWXpjd09EQmhOR001TVdZeFl6ZzBOREl3T0daaFltUXdNMlV4TlRoaE5Ua3hNR1kxWkRFMk1ERmxOamxsWWpVaWZWMHNJbUYxZEdobGJuUnBZMkYwYVc5dUlqcGJJaU53Y21sdFlYSjVJbDBzSW1GemMyVnlkR2x2YmsxbGRHaHZaQ0k2V3lJamNISnBiV0Z5ZVNKZGZRIiwic2lnbmF0dXJlIjoiTkJqblFlQVRuTXVJQUFrZTlEcFhDeXAzcmJzcnBsQWFRQ1A0ZEl2b3NfNVJZRnJTc2VDdnBSY3k1XzFOMUpObzB2WEpDdWVVVmZvaHJtNHROenM2cmcifQ',
  did: 'did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q',
  method: 'elem',
  identifier: 'EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q',
  path: '',
  fragment: '',
  query: '',
  param: ';elem:initial-state=eyJwcm90ZWN0ZWQiOiJleUp2Y0dWeVlYUnBiMjRpT2lKamNtVmhkR1VpTENKcmFXUWlPaUlqY0hKcGJXRnllU0lzSW1Gc1p5STZJa1ZUTWpVMlN5SjkiLCJwYXlsb2FkIjoiZXlKQVkyOXVkR1Y0ZENJNkltaDBkSEJ6T2k4dmR6TnBaQzV2Y21jdmMyVmpkWEpwZEhrdmRqSWlMQ0p3ZFdKc2FXTkxaWGtpT2x0N0ltbGtJam9pSTNCeWFXMWhjbmtpTENKMWMyRm5aU0k2SW5OcFoyNXBibWNpTENKMGVYQmxJam9pVTJWamNESTFObXN4Vm1WeWFXWnBZMkYwYVc5dVMyVjVNakF4T0NJc0luQjFZbXhwWTB0bGVVaGxlQ0k2SWpBeU1tTmhOak5tWm1aaVpEaGlObVJrTjJVMU5HWmhPRGhpTnpaa05USTBOVGN3TUdGak9ERTJOVGRtWkRVNVlUQXpZamN6WlRRek1qVmlZVEZsTVRsaVlTSjlMSHNpYVdRaU9pSWpjbVZqYjNabGNua2lMQ0oxYzJGblpTSTZJbkpsWTI5MlpYSjVJaXdpZEhsd1pTSTZJbE5sWTNBeU5UWnJNVlpsY21sbWFXTmhkR2x2Ymt0bGVUSXdNVGdpTENKd2RXSnNhV05MWlhsSVpYZ2lPaUl3TXprd1pEWTNZbVppWm1NNE1HUXdNR1ZrWXpjd09EQmhOR001TVdZeFl6ZzBOREl3T0daaFltUXdNMlV4TlRoaE5Ua3hNR1kxWkRFMk1ERmxOamxsWWpVaWZWMHNJbUYxZEdobGJuUnBZMkYwYVc5dUlqcGJJaU53Y21sdFlYSjVJbDBzSW1GemMyVnlkR2x2YmsxbGRHaHZaQ0k2V3lJamNISnBiV0Z5ZVNKZGZRIiwic2lnbmF0dXJlIjoiTkJqblFlQVRuTXVJQUFrZTlEcFhDeXAzcmJzcnBsQWFRQ1A0ZEl2b3NfNVJZRnJTc2VDdnBSY3k1XzFOMUpObzB2WEpDdWVVVmZvaHJtNHROenM2cmcifQ'
}

Key operation for every did is a resolution of a diddocument Our affinidi-registry service do this job quite well

const didDoc = await sdk.resolveDid(sdk.did)
JSON.stringify(didDoc, null , 2)
{
  "@context": "https://w3id.org/security/v2",
  "publicKey": [
    {
      "id": "did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q#primary",
      "usage": "signing",
      "type": "Secp256k1VerificationKey2018",
      "publicKeyHex": "022ca63fffbd8b6dd7e54fa88b76d5245700ac81657fd59a03b73e4325ba1e19ba"
    },
    {
      "id": "did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q#recovery",
      "usage": "recovery",
      "type": "Secp256k1VerificationKey2018",
      "publicKeyHex": "0390d67bfbfc80d00edc7080a4c91f1c844208fabd03e158a5910f5d1601e69eb5"
    }
  ],
  "authentication": [
    "did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q#primary"
  ],
  "assertionMethod": [
    "did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q#primary"
  ],
  "id": "did:elem:EiBa0KyUWgvMdkt_ywullSPac2kyOkRP5JRtHSeICQ1t6Q"
}
unknown msg_type: comm_open
unknown msg_type: comm_msg
unknown msg_type: comm_open
unknown msg_type: comm_msg
unknown msg_type: comm_open
unknown msg_type: comm_msg
unknown msg_type: comm_open
unknown msg_type: comm_msg

As you can see we have two public keys as a part of document

  • signing used for sign operations

  • recovery could be used for key rotation and did document operations