Open34

ion-tools を使って ION の DID の作成・アンカリング・更新をやってみる

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

キーペアの生成・保存

DID を作成するのに使用するキーペアを生成してファイルに保存する

touch generate.mjs
generate.mjs
import { DID, generateKeyPair } from "@decentralized-identity/ion-tools";

main().catch((err) => console.error(err));

async function main() {
  const keyPair = await generateKeyPair();
  console.log(JSON.stringify(keyPair, null, 2));
}
node generate.mjs > key.json

@decentralized-identity/ion-tools は ESM なので package.json に "type": "module" を追記するかファイルの拡張子を .mjs にする

実行結果の例は下記の通り

key.json
{
  "publicJwk": {
    "kty": "EC",
    "crv": "secp256k1",
    "x": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "y": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
  },
  "privateJwk": {
    "kty": "EC",
    "crv": "secp256k1",
    "x": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "y": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
    "d": "ddddddddddddddddddddddddddddddddddddddddddd"
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

DID URI の取得

touch uri.mjs
uri.mjs
import { DID } from "@decentralized-identity/ion-tools";
import fsPromises from "fs/promises";

main().catch((err) => console.error(err));

async function main() {
  const keyPair = await fsPromises.readFile("key.json", "utf8");
  const { publicJwk } = JSON.parse(keyPair);
  const did = new DID({
    content: {
      publicKeys: [
        {
          id: "key-1",
          type: "EcdsaSecp256k1VerificationKey2019",
          publicKeyJwk: publicJwk,
          purposes: ["authentication"],
        },
      ],
      services: [
        {
          id: "domain-1",
          type: "LinkedDomains",
          serviceEndpoint: "https://foo.example.com",
        },
      ],
    },
  });

  const uri = {
    long: await did.getURI(),
    short: await did.getURI('short'),
  }

  console.log(JSON.stringify(uri, null, 2));
}
node uri.mjs > uri.json
uri.json
{
  "long": "did:ion:EiBqKd-OC-GCTZDOFuC7RORjJsxhCN06IzWnVzM7f2c-gw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJrZXktMSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJ1QWVKUTF6UTNNM0tUZXhFeTVyaEtJTlBtZjIzbzhVdThxV0sycUZ5MXlVIiwieSI6IjNJTEVWQnNSaGx0X2MwSXhQaVhrUE5rM2VRb0VmYm8yYWM2a0pxVFVYemsifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOlt7ImlkIjoiZG9tYWluLTEiLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2Zvby5leGFtcGxlLmNvbSIsInR5cGUiOiJMaW5rZWREb21haW5zIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlDNXU1S1RpYzljN0QySnJibDlSYjZzOC1zdnpLekxyUVNsVVVCc3U4U25BQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQXV4akgweVJBemJwRU9idTdkRnlyeWpIMXN3djI0OEJuYXZFYlJvUGhpTHciLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUJKMnBvZXR5bVcxaURzbndybXVBbDFMM1pFSVZWbzJ2T1VOY0xtc1NJRkZRIn19",
  "short": "did:ion:EiBqKd-OC-GCTZDOFuC7RORjJsxhCN06IzWnVzM7f2c-gw"
}

DID の short の方を Universal Resolver を使って解決してみる

当然だがまだアンカーしていないので解決できない

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

DID URI の解決

touch resolove.mjs
resolve.mjs
import { DID, resolve } from "@decentralized-identity/ion-tools";
import fsPromises from "fs/promises";

main().catch((err) => console.error(err));

async function main() {
  const didUri = await fsPromises.readFile("uri.json", "utf8");
  const { long } = JSON.parse(didUri);
  const { didDocument } = await resolve(long);

  console.log(JSON.stringify(didDocument, null, 2));
}
node resolove.mjs > resolve.json

実行結果は下記の通り

resolve.json
{
  "id": "did:ion:EiBqKd-OC-GCTZDOFuC7RORjJsxhCN06IzWnVzM7f2c-gw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJrZXktMSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJ1QWVKUTF6UTNNM0tUZXhFeTVyaEtJTlBtZjIzbzhVdThxV0sycUZ5MXlVIiwieSI6IjNJTEVWQnNSaGx0X2MwSXhQaVhrUE5rM2VRb0VmYm8yYWM2a0pxVFVYemsifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOlt7ImlkIjoiZG9tYWluLTEiLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2Zvby5leGFtcGxlLmNvbSIsInR5cGUiOiJMaW5rZWREb21haW5zIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlDNXU1S1RpYzljN0QySnJibDlSYjZzOC1zdnpLekxyUVNsVVVCc3U4U25BQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQXV4akgweVJBemJwRU9idTdkRnlyeWpIMXN3djI0OEJuYXZFYlJvUGhpTHciLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUJKMnBvZXR5bVcxaURzbndybXVBbDFMM1pFSVZWbzJ2T1VOY0xtc1NJRkZRIn19",
  "@context": [
    "https://www.w3.org/ns/did/v1",
    {
      "@base": "did:ion:EiBqKd-OC-GCTZDOFuC7RORjJsxhCN06IzWnVzM7f2c-gw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJrZXktMSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJ1QWVKUTF6UTNNM0tUZXhFeTVyaEtJTlBtZjIzbzhVdThxV0sycUZ5MXlVIiwieSI6IjNJTEVWQnNSaGx0X2MwSXhQaVhrUE5rM2VRb0VmYm8yYWM2a0pxVFVYemsifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOlt7ImlkIjoiZG9tYWluLTEiLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2Zvby5leGFtcGxlLmNvbSIsInR5cGUiOiJMaW5rZWREb21haW5zIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlDNXU1S1RpYzljN0QySnJibDlSYjZzOC1zdnpLekxyUVNsVVVCc3U4U25BQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQXV4akgweVJBemJwRU9idTdkRnlyeWpIMXN3djI0OEJuYXZFYlJvUGhpTHciLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUJKMnBvZXR5bVcxaURzbndybXVBbDFMM1pFSVZWbzJ2T1VOY0xtc1NJRkZRIn19"
    }
  ],
  "service": [
    {
      "id": "#domain-1",
      "type": "LinkedDomains",
      "serviceEndpoint": "https://foo.example.com"
    }
  ],
  "verificationMethod": [
    {
      "id": "#key-1",
      "controller": "did:ion:EiBqKd-OC-GCTZDOFuC7RORjJsxhCN06IzWnVzM7f2c-gw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJrZXktMSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJ1QWVKUTF6UTNNM0tUZXhFeTVyaEtJTlBtZjIzbzhVdThxV0sycUZ5MXlVIiwieSI6IjNJTEVWQnNSaGx0X2MwSXhQaVhrUE5rM2VRb0VmYm8yYWM2a0pxVFVYemsifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOlt7ImlkIjoiZG9tYWluLTEiLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2Zvby5leGFtcGxlLmNvbSIsInR5cGUiOiJMaW5rZWREb21haW5zIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlDNXU1S1RpYzljN0QySnJibDlSYjZzOC1zdnpLekxyUVNsVVVCc3U4U25BQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQXV4akgweVJBemJwRU9idTdkRnlyeWpIMXN3djI0OEJuYXZFYlJvUGhpTHciLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUJKMnBvZXR5bVcxaURzbndybXVBbDFMM1pFSVZWbzJ2T1VOY0xtc1NJRkZRIn19",
      "type": "EcdsaSecp256k1VerificationKey2019",
      "publicKeyJwk": {
        "crv": "secp256k1",
        "kty": "EC",
        "x": "uAeJQ1zQ3M3KTexEy5rhKINPmf23o8Uu8qWK2qFy1yU",
        "y": "3ILEVBsRhlt_c0IxPiXkPNk3eQoEfbo2ac6kJqTUXzk"
      }
    }
  ],
  "authentication": [
    "#key-1"
  ]
}

少し長くて読みにくいが uri.mjs で指定した内容や key.json の内容が含まれていることがわかる

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

DID のアンカー

touch anchor.mjs
anchor.mjs
import { DID, anchor } from "@decentralized-identity/ion-tools";
import fsPromises from "fs/promises";

main().catch((err) => console.error(err));

async function main() {
  const keyPair = await fsPromises.readFile("key.json", "utf8");
  const { publicJwk } = JSON.parse(keyPair);
  const did = new DID({
    content: {
      publicKeys: [
        {
          id: "key-1",
          type: "EcdsaSecp256k1VerificationKey2019",
          publicKeyJwk: publicJwk,
          purposes: ["authentication"],
        },
      ],
      services: [
        {
          id: "domain-1",
          type: "LinkedDomains",
          serviceEndpoint: "https://foo.example.com",
        },
      ],
    },
  });

  const request = await did.generateRequest();
  const response = await anchor(request);

  await fsPromises.writeFile("anchor.json", response);
}
node anchor.mjs > anchor.log
anchor.log
Getting challenge from: https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge
{
  challengeNonce: 'cdf9929ee09d6fea563b306569656f7c58e46f916a21653224a18b6eead1e440',
  validDurationInMinutes: 10,
  largestAllowedHash: '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
}
Solving for body:
{"type":"create","suffixData":{"deltaHash":"EiDebLxWX1gg-m5jFL0S9P-os1cAsFKI6gRqROBEiyN7eQ","recoveryCommitment":"EiDTolNjRuDJ7twBUP2XX3HLzR2EQ3BJkiYVV_0F9kdf3Q"},"delta":{"updateCommitment":"EiAOo5mLZea1ViZMCMa-EHz8jHAEgDsUaS8YOT0l9vhGdA","patches":[{"action":"replace","document":{"publicKeys":[{"id":"key-1","type":"EcdsaSecp256k1VerificationKey2019","publicKeyJwk":{"kty":"EC","crv":"secp256k1","x":"uAeJQ1zQ3M3KTexEy5rhKINPmf23o8Uu8qWK2qFy1yU","y":"3ILEVBsRhlt_c0IxPiXkPNk3eQoEfbo2ac6kJqTUXzk"},"purposes":["authentication"]}],"services":[{"id":"domain-1","type":"LinkedDomains","serviceEndpoint":"https://foo.example.com"}]}}]}}
8819869c934f6f192509141d64a8c7473b01c34d19ddb8bb7aec11b50f164c88
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
116ba82114ace542b4c15b953248bed0a35e62e8fc381e0b07b0de8714c91757
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
65cfb58f6bb748911deb862d0c06b800b409bc2210b2ef6acfc91a5978217a78
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
c801b8c1d42726b50a0414802a453b3eae32f01e2670ea2beed83d0e26fc9bc0
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
02a746e16e65f6601ca8f9f17dce705a14802b7b8c104827215d16bd0237a601
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3
Successful registration
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "uAeJQ1zQ3M3KTexEy5rhKINPmf23o8Uu8qWK2qFy1yU",
          "y": "3ILEVBsRhlt_c0IxPiXkPNk3eQoEfbo2ac6kJqTUXzk"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": false,
      "recoveryCommitment": "EiDTolNjRuDJ7twBUP2XX3HLzR2EQ3BJkiYVV_0F9kdf3Q",
      "updateCommitment": "EiAOo5mLZea1ViZMCMa-EHz8jHAEgDsUaS8YOT0l9vhGdA"
    },
    "canonicalId": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
  }
}
anchor.json
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "uAeJQ1zQ3M3KTexEy5rhKINPmf23o8Uu8qWK2qFy1yU",
          "y": "3ILEVBsRhlt_c0IxPiXkPNk3eQoEfbo2ac6kJqTUXzk"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": false,
      "recoveryCommitment": "EiDTolNjRuDJ7twBUP2XX3HLzR2EQ3BJkiYVV_0F9kdf3Q",
      "updateCommitment": "EiAOo5mLZea1ViZMCMa-EHz8jHAEgDsUaS8YOT0l9vhGdA"
    },
    "canonicalId": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
  }
}

勝手に標準出力にデータを出すのはご勘弁いただきたい、何かを設定すれば OFF にできるのだろうか

今のところ Universal Resolver では表示されない、時間が経てば表示されるようになる?

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

3つの鍵が必要というけれど

Sidetreeでは、以下の3つの鍵ペアを生成する。

署名鍵:DID自体に関連付けられるメインの鍵で、DIDのユースケースにおける署名や認証に用いる鍵。
Update Key:DIDの更新とUpdate Key自体の更新に使用する鍵。
Recovery Key:DIDの更新、失効とUpdate Key、Recovery Key自体の更新に使用する鍵。

ion-tools のソースコードを読むと確かにそんな感じのことが書かれている

ion-tools/src/did.js
      case 'create':
      default:
        return IonRequest.createCreateRequest({
          recoveryKey: op.recovery.publicJwk,
          updateKey: op.update.publicJwk,
          document: op.content
        });
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Universal Resolver では失敗する

20分くらい経ったけどいまだに Universal Resolver では表示されない

もしかしてキャッシュされてしまった?

再び resolve のコードを書いて試してみる

touch resolve-again.mjs
resolve-again.mjs
import { DID, resolve } from "@decentralized-identity/ion-tools";
import fsPromises from "fs/promises";

main().catch((err) => console.error(err));

async function main() {
  const anchor = await fsPromises.readFile("anchor.json", "utf8");
  const { didDocument } = JSON.parse(anchor);
  const result = await resolve(didDocument.id);

  console.log(JSON.stringify(result, null, 2));
}
node resolve-again.mjs
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "uAeJQ1zQ3M3KTexEy5rhKINPmf23o8Uu8qWK2qFy1yU",
          "y": "3ILEVBsRhlt_c0IxPiXkPNk3eQoEfbo2ac6kJqTUXzk"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": true,
      "recoveryCommitment": "EiDTolNjRuDJ7twBUP2XX3HLzR2EQ3BJkiYVV_0F9kdf3Q",
      "updateCommitment": "EiAOo5mLZea1ViZMCMa-EHz8jHAEgDsUaS8YOT0l9vhGdA"
    },
    "canonicalId": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
  }
}

解決できた!メタデータの publishedtrue になっている

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

結局更新までやってみる

#domain-1 を DID ドキュメントの services から削除してみる

準備コマンド
npm install --save-dev @decentralized-identity/ion-sdk base64url
touch update.mjs
update.mjs
import { IonRequest, LocalSigner } from "@decentralized-identity/ion-sdk";
import { anchor } from "@decentralized-identity/ion-tools";
import fsPromises from "fs/promises";
import base64url from "base64url";

main().catch((err) => console.error(err));

async function main() {
  const key = JSON.parse(await fsPromises.readFile("key.json", "utf8"));
  const { didDocument } = JSON.parse(
    await fsPromises.readFile("anchor.json", "utf8")
  );

  const didSuffix = didDocument.id.split(":").pop();
  const request = await IonRequest.createUpdateRequest({
    didSuffix,
    updatePublicKey: key.publicJwk,
    nextUpdatePublicKey: key.publicJwk,
    signer: LocalSigner.create(key.privateJwk),
    idsOfServicesToRemove: [base64url("#domain-1")],
  });

  const response = await anchor(request);
  await fsPromises.writeFile("update.json", response);
}
実行コマンド
node update.mjs > update.log
update.log
Getting challenge from: https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge
{
  challengeNonce: '5d3a09c51c13531d26f0f465dbd161042604150f8c5a0776421d847a0e154701',
  validDurationInMinutes: 10,
  largestAllowedHash: '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
}
Solving for body:
{
  "type": "update",
  "didSuffix": "EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
  "revealValue": "EiAnH57oPijxFGXYjSq65mo6XFaZ8vp-4w77qThYL4e48Q",
  "delta": {
    "patches": [{ "action": "remove-services", "ids": ["I2RvbWFpbi0x"] }],
    "updateCommitment": "EiB0KQbqbgW-LPTXzLy9x2nxnmR4RmPR6VLa6h3UmePO0A"
  },
  "signedData": "eyJhbGciOiJFUzI1NksifQ.eyJ1cGRhdGVLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJzZWNwMjU2azEiLCJ4IjoidUFlSlExelEzTTNLVGV4RXk1cmhLSU5QbWYyM284VXU4cVdLMnFGeTF5VSIsInkiOiIzSUxFVkJzUmhsdF9jMEl4UGlYa1BOazNlUW9FZmJvMmFjNmtKcVRVWHprIn0sImRlbHRhSGFzaCI6IkVpQTJSSWo4bnRFWmE1cFZwMS1tTTVKVnNuVnJTTlFMOVZmTFZoMU1GWUVVSEEifQ.pCfVTnL6iFpddW0tCgd0gcf9ZtOGaqeXa-rO9hXfFhJWcVfVdlSKu2xeZQRoPk_vcRJKMK7gmGWmR6dIICe0mA"
}
0add7a4882ddbe34df544af4bb6efff35caeadeb809d3d98bd7be1d7af738657
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3
Successful registration

JSON 部分は見やすいように整形している

update.json

update.json には特に何も書き込まれていなかった

終わったら resolve-again.mjs を実行する

確認コマンド
node resolve-again.mjs
確認結果
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "uAeJQ1zQ3M3KTexEy5rhKINPmf23o8Uu8qWK2qFy1yU",
          "y": "3ILEVBsRhlt_c0IxPiXkPNk3eQoEfbo2ac6kJqTUXzk"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": true,
      "recoveryCommitment": "EiDTolNjRuDJ7twBUP2XX3HLzR2EQ3BJkiYVV_0F9kdf3Q",
      "updateCommitment": "EiAOo5mLZea1ViZMCMa-EHz8jHAEgDsUaS8YOT0l9vhGdA"
    },
    "canonicalId": "did:ion:EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg"
  }
}

まだ反映されていない

もう1回実行してみる
node update.mjs
update.log
Getting challenge from: https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge
{
  challengeNonce: 'e500ecc1efd7cd93d32df4a8d266d6bb1de598c60cfe1c166b15fb8904840eda',
  validDurationInMinutes: 10,
  largestAllowedHash: '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
}
Solving for body:
{
  "type": "update",
  "didSuffix": "EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg",
  "revealValue": "EiAnH57oPijxFGXYjSq65mo6XFaZ8vp-4w77qThYL4e48Q",
  "delta": {
    "patches": [{ "action": "remove-services", "ids": ["I2RvbWFpbi0x"] }],
    "updateCommitment": "EiB0KQbqbgW-LPTXzLy9x2nxnmR4RmPR6VLa6h3UmePO0A"
  },
  "signedData": "eyJhbGciOiJFUzI1NksifQ.eyJ1cGRhdGVLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJzZWNwMjU2azEiLCJ4IjoidUFlSlExelEzTTNLVGV4RXk1cmhLSU5QbWYyM284VXU4cVdLMnFGeTF5VSIsInkiOiIzSUxFVkJzUmhsdF9jMEl4UGlYa1BOazNlUW9FZmJvMmFjNmtKcVRVWHprIn0sImRlbHRhSGFzaCI6IkVpQTJSSWo4bnRFWmE1cFZwMS1tTTVKVnNuVnJTTlFMOVZmTFZoMU1GWUVVSEEifQ.pCfVTnL6iFpddW0tCgd0gcf9ZtOGaqeXa-rO9hXfFhJWcVfVdlSKu2xeZQRoPk_vcRJKMK7gmGWmR6dIICe0mA"
}
7742cdad305d7c4cbb5519518d18ba294c27e10142755da169339128ef7e64e7
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
5d3a3379116d2950a310979f9294d0d7b7e1469795e3b722d832ef4a132725a0
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
03d133c2525cbb7859f5a950c1f4ea60cad94c781b21c163c26486fbbc06e4b7
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3
Bed request: {
  "requestId": "8586df540b1700f1f9f8331b0d791e9f",
  "date": "Tue, 03 Jan 2023 06:49:03 GMT",
  "mscv": "4NcOa0vybiJuvnHC.1",
  "error": {
    "code": "ion_driver.operation_failed.bad_request",
    "message": "An operation request already exists in queue for DID 'EiDXaUl6iZaGdmjDuxRpm7uA8_Rnbm-eZDZCvqWilWR6zg', only one is allowed at a time."
  }
}
Retrying with new challenge and difficulty
TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined
    at Object.writeFile (node:internal/fs/promises:785:5)
    at main (file:///Users/susukida/workspace/js/ion-did/update.mjs:24:20)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  code: 'ERR_INVALID_ARG_TYPE'
}

同じリクエストがあるのでダメと言われる

しばらく待ってみよう

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

待てど暮らせど

serviceEndpoint が削除されない

キーが間違っているのだろうか

base64url("#domain-1") の部分を "domain-1" に変えてみたり色々やったけどダメだった

serviceEndpoint の追加ならできるかと思って下記も試してみた

servicesToAdd: [
  {
    id: "domain-2",
    type: "LinkedDomains",
    serviceEndpoint: "https://bar.example.com",
  },
],

しかし今のところ反映されている様子はない

秘密鍵とかが間違っている?

いずれにしても結果が反映されるまでに時間がかかるのは辛い、長いと半日から1日くらいかかる

結局 Sidetree の仕様書を読んでいる

https://identity.foundation/sidetree/spec/

これはこれで面白い

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

気持ちを新たに

ワークスペース準備コマンド
mkdir hello-ion
cd hello-ion
npm init -y
npm install --save-dev @decentralized-identity/ion-sdk @types/node ts-node
touch generate-keypair.ts

ion-sdk だと別のものがインストールされるので注意

generate-keypair.ts
import { IonKey, IonRequest } from "@decentralized-identity/ion-sdk";

async function main() {
  const authentication = await generateKeyPair();
  const update = await generateKeyPair();
  const recovery = await generateKeyPair();

  console.log(JSON.stringify({ authentication, update, recovery }, null, 2));
}

async function generateKeyPair() {
  const [publicJwk, privateJwk] = await IonKey.generateEs256kOperationKeyPair();
  return { publicJwk, privateJwk };
}

main().catch((err) => {
  console.error(err);
});
キーペア生成コマンド
npx ts-node generate-keypair.ts > generate-keypair.json
generate-keypair.json
{
  "authentication": {
    "publicJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
      "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
    },
    "privateJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
      "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I",
      "d": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
  },
  "update": {
    "publicJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "xGKAW04o_2qnkWZ-h9x3r1JO3Owx5fAmG4oNS1bGxio",
      "y": "s3iGatN3WPmP8GRKr_okFurlcr2wlXN8V5x4lLDFMp4"
    },
    "privateJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "xGKAW04o_2qnkWZ-h9x3r1JO3Owx5fAmG4oNS1bGxio",
      "y": "s3iGatN3WPmP8GRKr_okFurlcr2wlXN8V5x4lLDFMp4",
      "d": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
  },
  "recovery": {
    "publicJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "kWW-Rr9o1TPGy25fWDM_VlVaBAxDYPw3H5ZiI-Td2sk",
      "y": "WyCpnOuJdwSI2bqMSJc6xEA6DXPBuQrPg-ZEFKDfneg"
    },
    "privateJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "kWW-Rr9o1TPGy25fWDM_VlVaBAxDYPw3H5ZiI-Td2sk",
      "y": "WyCpnOuJdwSI2bqMSJc6xEA6DXPBuQrPg-ZEFKDfneg",
      "d": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

CREATE リクエストの作成

準備コマンド
touch create-request.ts
create-request.ts
import { IonRequest, IonPublicKeyPurpose } from "@decentralized-identity/ion-sdk";

async function main() {
  const keyPair = require("./generate-keypair.json");
  const createRequest = await IonRequest.createCreateRequest({
    recoveryKey: keyPair.recovery.publicJwk,
    updateKey: keyPair.update.publicJwk,
    document: {
      publicKeys: [
        {
          id: "key-1",
          type: "EcdsaSecp256k1VerificationKey2019",
          publicKeyJwk: keyPair.authentication.publicJwk,
          purposes: [IonPublicKeyPurpose.Authentication],
        },
      ],
      services: [
        {
          id: "domain-1",
          type: "LinkedDomains",
          serviceEndpoint: "https://foo.example.com",
        },
      ],
    },
  });

  console.log(JSON.stringify(createRequest, null, 2));
}

main().catch((err) => {
  console.error(err);
});
リクエスト作成コマンド
npx ts-node create-request.ts > create-request.json
create-request.json
{
  "type": "create",
  "suffixData": {
    "deltaHash": "EiCmJtugyAoO9gE5uLskVe6oQOIFbJCxZI3isdRCF_XiKQ",
    "recoveryCommitment": "EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw"
  },
  "delta": {
    "updateCommitment": "EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ",
    "patches": [
      {
        "action": "replace",
        "document": {
          "publicKeys": [
            {
              "id": "key-1",
              "type": "EcdsaSecp256k1VerificationKey2019",
              "publicKeyJwk": {
                "kty": "EC",
                "crv": "secp256k1",
                "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
                "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
              },
              "purposes": [
                "authentication"
              ]
            }
          ],
          "services": [
            {
              "id": "domain-1",
              "type": "LinkedDomains",
              "serviceEndpoint": "https://foo.example.com"
            }
          ]
        }
      }
    ]
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

リクエストの送信

準備コマンド
touch send-ion-request.mjs

TypeScript にしたいけど @decentralized-identity/ion-tools が未対応なので仕方がない

ESM なので拡張子は mjs にする

send-ion-request
import { anchor } from '@decentralized-identity/ion-tools';
import { readFile } from 'fs/promises';

async function main() {
  const anchorRequestText = await readFile(process.env.REQUEST_FILE, 'utf-8');
  const anchorRequest = JSON.parse(anchorRequestText);

  console.log(await anchor(anchorRequest));
}

main().catch((err) => {
  console.error(err);
});
リクエスト送信コマンド
REQUEST_FILE=create-request.json node send-ion-request.mjs
実行結果
Getting challenge from: https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge
{
  challengeNonce: 'd37a51e114ede2b8c1e24a1951cedc77aa17e1a65e74bc1ed29d03d29ead199c',
  validDurationInMinutes: 10,
  largestAllowedHash: '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
}
Solving for body:
{"type":"create","suffixData":{"deltaHash":"EiCmJtugyAoO9gE5uLskVe6oQOIFbJCxZI3isdRCF_XiKQ","recoveryCommitment":"EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw"},"delta":{"updateCommitment":"EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ","patches":[{"action":"replace","document":{"publicKeys":[{"id":"key-1","type":"EcdsaSecp256k1VerificationKey2019","publicKeyJwk":{"kty":"EC","crv":"secp256k1","x":"PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0","y":"O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"},"purposes":["authentication"]}],"services":[{"id":"domain-1","type":"LinkedDomains","serviceEndpoint":"https://foo.example.com"}]}}]}}
02308b21bc29cb53fce0a8f039b34f4654671af3e61da76b5b2aedc25fb2be5e
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3
Successful registration
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
          "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": false,
      "recoveryCommitment": "EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw",
      "updateCommitment": "EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"
    },
    "canonicalId": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
  }
}
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
          "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": false,
      "recoveryCommitment": "EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw",
      "updateCommitment": "EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"
    },
    "canonicalId": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Resolve してみる

準備コマンド
touch resolve-did.mjs
resolve-did.mjs
import { resolve } from '@decentralized-identity/ion-tools';

async function main() {
  const didUri = 'did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA';

  console.log(await resolve(didUri));
}

main().catch((err) => {
  console.error(err);
});
実行コマンド
node resolve-did.mjs
実行結果
Error: Not Found
    at resolve (file:///Users/susukida/workspace/web3/hello-ion/node_modules/@decentralized-identity/ion-tools/src/utils.js:175:11)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async main (file:///Users/susukida/workspace/web3/hello-ion/resolve-did.mjs:6:15)

今のところ何も表示されない、反映されるまでにタイムラグがある?

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

更新してみる

準備コマンド
touch update-request.ts
update-request.ts
import { IonRequest, LocalSigner } from "@decentralized-identity/ion-sdk";

async function main() {
  const keyPair = require("./generate-keypair.json");
  const updateRequest = await IonRequest.createUpdateRequest({
    didSuffix: 'EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA',
    updatePublicKey: keyPair.update.publicJwk,
    nextUpdatePublicKey: keyPair.update.publicJwk,
    signer: LocalSigner.create(keyPair.update.privateJwk),
    servicesToAdd: [
      {
        id: 'domain-2',
        type: "LinkedDomains",
        serviceEndpoint: "https://foo.example.com",
      },
    ],
  });

  console.log(JSON.stringify(updateRequest, null, 2));
}

main().catch((err) => {
  console.error(err);
});
実行コマンド
npx ts-node update-request.ts > update-request.json
update-request.json
{
  "type": "update",
  "didSuffix": "EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
  "revealValue": "EiCcIejMmrETer4nexifLZ8cDiApoUUWdZ8W3vIxvvwv_A",
  "delta": {
    "patches": [
      {
        "action": "add-services",
        "services": [
          {
            "id": "domain-2",
            "type": "LinkedDomains",
            "serviceEndpoint": "https://foo.example.com"
          }
        ]
      }
    ],
    "updateCommitment": "EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"
  },
  "signedData": "eyJhbGciOiJFUzI1NksifQ.eyJ1cGRhdGVLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJzZWNwMjU2azEiLCJ4IjoieEdLQVcwNG9fMnFua1daLWg5eDNyMUpPM093eDVmQW1HNG9OUzFiR3hpbyIsInkiOiJzM2lHYXROM1dQbVA4R1JLcl9va0Z1cmxjcjJ3bFhOOFY1eDRsTERGTXA0In0sImRlbHRhSGFzaCI6IkVpQldfYUtwMlZjNHpxUTkyeFhpVkZ0R2I5c081Nnh4ZklOeGVOTWFQOVBrUkEifQ._BvsEyB4Lcua0ZBJKq8wdZUfbuMJGUME6Io3vs9GbtE9dQM6A1zvloz-xMzqFy_KX5KT9ES1x8iQhjFYiBignQ"
}

ついでに送信してみる

REQUEST_FILE=update-request.json node send-ion-request.mjs
実行結果
Getting challenge from: https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge
{
  challengeNonce: 'f6590ebbde68aeace88f3c7863b6287139a3cf8d526d758d94d6d53078ae7863',
  validDurationInMinutes: 10,
  largestAllowedHash: '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
}
Solving for body:
{"type":"update","didSuffix":"EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA","revealValue":"EiCcIejMmrETer4nexifLZ8cDiApoUUWdZ8W3vIxvvwv_A","delta":{"patches":[{"action":"add-services","services":[{"id":"domain-2","type":"LinkedDomains","serviceEndpoint":"https://foo.example.com"}]}],"updateCommitment":"EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"},"signedData":"eyJhbGciOiJFUzI1NksifQ.eyJ1cGRhdGVLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJzZWNwMjU2azEiLCJ4IjoieEdLQVcwNG9fMnFua1daLWg5eDNyMUpPM093eDVmQW1HNG9OUzFiR3hpbyIsInkiOiJzM2lHYXROM1dQbVA4R1JLcl9va0Z1cmxjcjJ3bFhOOFY1eDRsTERGTXA0In0sImRlbHRhSGFzaCI6IkVpQldfYUtwMlZjNHpxUTkyeFhpVkZ0R2I5c081Nnh4ZklOeGVOTWFQOVBrUkEifQ._BvsEyB4Lcua0ZBJKq8wdZUfbuMJGUME6Io3vs9GbtE9dQM6A1zvloz-xMzqFy_KX5KT9ES1x8iQhjFYiBignQ"}
65c5d287cc7ed2ae3226b2e39158ae6ec01555d3ecb9d4923bbe8873ff794508
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3a0aa69df5626500f81b05fcf7e764bfd1a8b4f0662d0b0ebe2a121eb214b83f
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
64428c8939a380e79b57edfd3ddf3f6fef7ac2b593cece3092a2b08854d17f34
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
500ec27f67e05ccaac22bac8a834fcd446db4b2021fbaed6f53b8fd953e0acc2
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
bde09ee16ebc12f5bef0973302e8e507b79cc70b1c7fbb021b319f501dcc3129
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
6ef199fa42e7758bfd8082724ca7b4bbdd380a2157d6dc64d940fd81f1e39483
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
71fdfe1b178bf694e4db33ae2fe5cdbc5923ba33651a567a77107a18534a6ad6
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
320fe47a8774dc7e759c3b0b93edb6498d9a61c4751a4ea6f53453bf99480f9c
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
969e31ec9b636e34fcac3372f6eabdeeb80b531c2566291850b9259da1c76ed8
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
e96fe7aeb15d03738bdc35ba282a7b4d90aa90cb2e6b5e60c3d871c7a02f8eea
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
6937814b2b453cc434fea6ef801b4002a07127c26fd8d7f62f1094c18e61f2cd
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fa2e0e39dd0a617ae25e6e2f1e84be35d445f0ffbd09f397b9c6e01a96a7fae1
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
480c0d1f38cf2a905c636aa7e23c91cdebd19d98b551f6fb2649fbdbbf4bbf4b
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
0f6acdcb87bda3f55681943e69256e794633f4ec83c88803a9609b909ab49bb3
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3
Bed request: {"requestId":"47f37821d4e046bb8ed23c7fc20aac37","date":"Wed, 18 Jan 2023 05:38:46 GMT","mscv":"8OuyWA1vfmyMJZdT.1","error":{"code":"ion_driver.operation_failed.bad_request","message":"An operation request already exists in queue for DID 'EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA', only one is allowed at a time."}}
Retrying with new challenge and difficulty
undefined

CREATEリクエストが完了しないことにはダメそうだ、気長に待とう

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

やっと出てきた

~/workspace/web3/hello-ion$ node resolve-did.mjs
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
          "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": true,
      "recoveryCommitment": "EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw",
      "updateCommitment": "EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"
    },
    "canonicalId": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
  }
}

反映されるまでに2時間くらいかかったようだ

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

更新リクエストも行けた

$ REQUEST_FILE=update-request.json node send-ion-request.mjs
Getting challenge from: https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge
{
  challengeNonce: 'ccb4e08834c06e991c4e9230c9a42458866c4bc03b952706232929bbafe07271',
  validDurationInMinutes: 10,
  largestAllowedHash: '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
}
Solving for body:
{"type":"update","didSuffix":"EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA","revealValue":"EiCcIejMmrETer4nexifLZ8cDiApoUUWdZ8W3vIxvvwv_A","delta":{"patches":[{"action":"add-services","services":[{"id":"domain-2","type":"LinkedDomains","serviceEndpoint":"https://foo.example.com"}]}],"updateCommitment":"EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"},"signedData":"eyJhbGciOiJFUzI1NksifQ.eyJ1cGRhdGVLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJzZWNwMjU2azEiLCJ4IjoieEdLQVcwNG9fMnFua1daLWg5eDNyMUpPM093eDVmQW1HNG9OUzFiR3hpbyIsInkiOiJzM2lHYXROM1dQbVA4R1JLcl9va0Z1cmxjcjJ3bFhOOFY1eDRsTERGTXA0In0sImRlbHRhSGFzaCI6IkVpQldfYUtwMlZjNHpxUTkyeFhpVkZ0R2I5c081Nnh4ZklOeGVOTWFQOVBrUkEifQ._BvsEyB4Lcua0ZBJKq8wdZUfbuMJGUME6Io3vs9GbtE9dQM6A1zvloz-xMzqFy_KX5KT9ES1x8iQhjFYiBignQ"}
3aaced1c8cb1a126daa090f8f7663b874673279892b621aa7fe9ed6cc50789b0
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
e86fb9fd7bf4263533eb7db2aec2db517120170df865b42b612af79d24ac331d
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
b1f158ea912962e166b5950d6088136bc41f45711af3c86cff19e4e1b6e9f752
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
b33eb45ddfab96f49155e3e6c39458db53b38e9d4993cae2c024cbe8eaf4f664
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
463ac74bdf95a59bd878a06eba35519990e812f40ade6d26c8b456f749f213a8
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
70aa884c9d117772f42510c94a469ccb7fde636a8f5b4b0d7e1807360126b349
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ef81aceaee1605ae74ba32b7f77f839a13b22569bed3b118e4f095bdc525685d
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
f127d8f3b64e3c0a741ce1fdae9ebde15b407e1237aa929face6330273f2f9f7
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
24a9e038dbfcd8e1e504acb31b70f4c654ca2aa1e320771517af415b558f4fcd
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
eac906d6975b0b33286da232fb63840ff287b10d65c5835f85efe14b0bb976da
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
22f0325bbdb2b7517187315a0e262479e1c786f32eca81c5bd8dba34104ce562
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
a9af2da51db74086d5c0ccb1f50531c810b9ca45871d636c0080bf97e25dea66
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
e3d6e195997ec076b115db794567235b8d32a1a7e9612b10bcf7c7edf99b8744
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
4435377e9f57277b6594e1876f762b3cb2bc631ae3be05ffbb16b0ec6dd13d5c
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
c69ee150ec4583d00694c87fb8c8869e3cc0d5eeb9f68bcd56aaf88a6938b661
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
5e82cbdb30d8a8db58e216d96f97063a84c2db888cc84b101057fdded84af830
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
d7751676885fb8d1825fc8eeca0bf314e896872a64008e77d8a7adb5947fad2a
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
140d95b332f0cd403afa1a2c4c8201818390a346f095840030dc28ce761a8460
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
8ac16580049bbd1b02b47a2405ad03f44bf2939a18014ec2add42451e51599ea
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
b1fc5925cb14e3214f08f94d92d45a8f0da4dd30fdc6eaadb1ebf345d802eafa
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
46a5f922a50b94ed090e1441e5074c93bdcad7aa11f1b5cb4c24cde85ad962a5
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
7fbffcdc014870a596b085d4f068f0e88953173d4eeddd3d5dc786e88d639319
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
4d47d5d3ea014363814836183adc1661623fd01898ed24a1b87f372e5ccd17e2
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1c03709e95eae0988bf4d8f489f0d5ff393141b70d2b6280dc766b81776ef0b9
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
8dd9db06908ef3ce0ff9058cfe62286e45b744948d31b52a1765961cc58abfff
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
2afc5e48ccea043d70e6e7024cda390f08fa2eebdf86d52e86cfe355e0c2f6b8
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
673e4d74d19a7e592b6ccaca83716f8b7fbe6b10364831562c6ce87cc62ebd6f
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
89cf7b071a3428f747bed6721c9f8acb06f5db69e08cba4f942b7e0a27b58938
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
172e76f8c4c4620048b529449283fd58a1e81521f7d886ec6abc4acde1171e22
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
74b652e93318b3bb1e1878d1d0d0cb641fe4b19a58b3e18e908e11467c6bfe99
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
4a6028d57c9e91f32b1ee33df8a61114619633dde826293fe42e4c15fb0ad9d8
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
c016e8b42afea8b2644ae970da3d7440fe309e5921380d4b1b636f0dde721f19
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
f32cae5a502617786292231573b5f377c556e60c6de585c41862f2d8692c59fc
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
18450cffe8521a594fc0407d1821b196556c7f62fc686b39209be9f9ef467c90
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
c6fdfadfdb22b7f2ed57fd4de06714a0c42b5c53385ebaf6dafccf1496db634f
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
00c68d170dfefe5df95f9dcdbf8e6198f0eceaef757ae3582b93c1d2b74915b4
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3
Successful registration
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

相変わらず更新できない

確認コマンド
node resolve-did.mjs
確認結果
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
          "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": true,
      "recoveryCommitment": "EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw",
      "updateCommitment": "EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"
    },
    "canonicalId": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

ION DID URL

create-request.ts に long form URL を生成するコードを追加してみた

create-request.ts
  console.log(await IonDid.createLongFormDid({
    document: createRequest.delta.patches[0].document,
    recoveryKey: keyPair.recovery.publicJwk,
    updateKey: keyPair.update.publicJwk,
  }));
実行結果
did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJrZXktMSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJQeHVDMFhtNUg0ZmNSNE9BRlR5bkJlUWZHamEwdTlGanNGQUpSaTRza2wwIiwieSI6Ik84UkYtQlBfSHlfUmxjTmFaZFI1Tk9KUUdQUkgtQ1Y0M0xsZm13TVM5OEkifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOlt7ImlkIjoiZG9tYWluLTEiLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2Zvby5leGFtcGxlLmNvbSIsInR5cGUiOiJMaW5rZWREb21haW5zIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlCWlotU1VBZ241TW9vQnNENXpNZ0xQQ2EzVnF5WS1iemJIV2V0NjJWNElhUSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQ21KdHVneUFvTzlnRTV1THNrVmU2b1FPSUZiSkN4WkkzaXNkUkNGX1hpS1EiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUJWN29wdFNpZVp3RE9MbC03cHdKVC1uN25YZEdGX0VRSlpHQk1OUGpMSGt3In19

did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA の部分は変わらないことがわかる

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

更新用の鍵を生成

generate-keypair-for-update.ts
import { IonKey } from "@decentralized-identity/ion-sdk";

async function main() {
  const update = await generateKeyPair();

  console.log(JSON.stringify({ update }, null, 2));
}

async function generateKeyPair() {
  const [publicJwk, privateJwk] = await IonKey.generateEs256kOperationKeyPair();
  return { publicJwk, privateJwk };
}

main().catch((err) => {
  console.error(err);
});
実行コマンド
npx ts-node generate-keypair-for-update.ts > generate-keypair-for-update.json
generate-keypair-for-update.json
{
  "update": {
    "publicJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "CgHHnj1LrMOax9k3O4it0uXmL0BYzc7w0mFfYteHPPw",
      "y": "tZRtO2-eaU7FIhfHwV6cG3r3nNdQ43D1rde2VsCwWRY"
    },
    "privateJwk": {
      "kty": "EC",
      "crv": "secp256k1",
      "x": "CgHHnj1LrMOax9k3O4it0uXmL0BYzc7w0mFfYteHPPw",
      "y": "tZRtO2-eaU7FIhfHwV6cG3r3nNdQ43D1rde2VsCwWRY",
      "d": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    }
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

更新リクエスト生成も少し変更

update-request.ts
import { IonRequest, LocalSigner } from "@decentralized-identity/ion-sdk";

async function main() {
  const keyPair = require("./generate-keypair.json");
  const keyPairNext = require("./generate-keypair-for-update.json");
  const updateRequest = await IonRequest.createUpdateRequest({
    didSuffix: 'EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA',
    updatePublicKey: keyPair.update.publicJwk,
    nextUpdatePublicKey: keyPairNext.update.publicJwk,
    signer: LocalSigner.create(keyPair.update.privateJwk),
    servicesToAdd: [
      {
        id: 'domain-2',
        type: "LinkedDomains",
        serviceEndpoint: "https://foo.example.com",
      },
    ],
  });

  console.log(JSON.stringify(updateRequest, null, 2));
}

main().catch((err) => {
  console.error(err);
});
実行コマンド
npx ts-node update-request.ts > update-request.json
update-request.json
{
  "type": "update",
  "didSuffix": "EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
  "revealValue": "EiCcIejMmrETer4nexifLZ8cDiApoUUWdZ8W3vIxvvwv_A",
  "delta": {
    "patches": [
      {
        "action": "add-services",
        "services": [
          {
            "id": "domain-2",
            "type": "LinkedDomains",
            "serviceEndpoint": "https://foo.example.com"
          }
        ]
      }
    ],
    "updateCommitment": "EiB7QKEEygN_pQj78P5hORx6cqJpFG04zs8r4xh9TCJlzQ"
  },
  "signedData": "eyJhbGciOiJFUzI1NksifQ.eyJ1cGRhdGVLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJzZWNwMjU2azEiLCJ4IjoieEdLQVcwNG9fMnFua1daLWg5eDNyMUpPM093eDVmQW1HNG9OUzFiR3hpbyIsInkiOiJzM2lHYXROM1dQbVA4R1JLcl9va0Z1cmxjcjJ3bFhOOFY1eDRsTERGTXA0In0sImRlbHRhSGFzaCI6IkVpQUQ4dXdqSTd4cnhaY0VoQ3BybnBwT1hoSXBxcGRKNlpiY0FOeHhRNDRVSncifQ.PwQzZVG_rAqjbC5iUOzjqRVkcpx1nKnXcrIZXd_Kxj54548fLODjp1X2Wf18zqyKZqyWNiposBkvY0kVdyhiWQ"
}

わかりにくいけど updateCommitment が変更されている

Sidetree の仕様書によるとコミットメントとは JWK 形式の公開鍵を正規化(フィールド準備を辞書順にするなど)してから2回 SHA-256 ハッシュを計算するようだ

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今度こそうまく行きますように

実行コマンド
REQUEST_FILE=update-request.json node send-ion-request.mjs
出力結果
Getting challenge from: https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge
{
  challengeNonce: '5aa5df3eff71801a7e4b9eda79f2afa557249737c8af20aa0d6961972610d182',
  validDurationInMinutes: 10,
  largestAllowedHash: '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
}
Solving for body:
{"type":"update","didSuffix":"EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA","revealValue":"EiCcIejMmrETer4nexifLZ8cDiApoUUWdZ8W3vIxvvwv_A","delta":{"patches":[{"action":"add-services","services":[{"id":"domain-2","type":"LinkedDomains","serviceEndpoint":"https://foo.example.com"}]}],"updateCommitment":"EiB7QKEEygN_pQj78P5hORx6cqJpFG04zs8r4xh9TCJlzQ"},"signedData":"eyJhbGciOiJFUzI1NksifQ.eyJ1cGRhdGVLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJzZWNwMjU2azEiLCJ4IjoieEdLQVcwNG9fMnFua1daLWg5eDNyMUpPM093eDVmQW1HNG9OUzFiR3hpbyIsInkiOiJzM2lHYXROM1dQbVA4R1JLcl9va0Z1cmxjcjJ3bFhOOFY1eDRsTERGTXA0In0sImRlbHRhSGFzaCI6IkVpQUQ4dXdqSTd4cnhaY0VoQ3BybnBwT1hoSXBxcGRKNlpiY0FOeHhRNDRVSncifQ.PwQzZVG_rAqjbC5iUOzjqRVkcpx1nKnXcrIZXd_Kxj54548fLODjp1X2Wf18zqyKZqyWNiposBkvY0kVdyhiWQ"}
3b174de812800172f5865bd1b087bc4e594a6174685f9e984f837b92e1b8151b
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
24806c820969f698ff7f9bb5dd85ceea92144ac2694c846f2a8c4c90d836925d
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ade91d1a87c34c4fc70fa0af633c09ec6b4bb8380f0ca531da1358df31f72f83
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
9024365103d83b13abdfadbf8d31333a4fb66dd32cc34944bdbf6bf0d940f961
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ab2b864151a7d24c4cc5c31c93f0f115e02bfb88a9e350311f96471c3206d0bb
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
a136a368b40f0f0a13b60c2c3e48df10038ffcae538384d0d2216ff16c5f2386
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
d4d50a2b76be6c361edb50fa06dc321cc62d5c0c141272354b57583adc905a7c
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffe077286c55c5d59629130df24a873fd73f611ac9a1123a5068320cc15e9f9f
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
b7f59714f57e6b9bcb28a9e95250165a75385c60f47ffd1b778cb3addc36e24d
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
76d2956bf98d7666dcf348d7507aef326de05d9c7dbde6a3ab2fee6f89f4e3a2
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
cd0e9289861eace0ad61cdacfeb6cecc10e12129d32b68efafe3d0690917af60
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ad2103472158c9deb125847f4f094b9d4a49c5ce3c43bf9cffb034f1a90358a3
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
5e14c02e15b1797117e3d0ccc13edf11b8ada3d0353e4e93f3f669762cc68498
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
5333e4c17db8e976afe091472da0635bc847d796ca82b521589c5cfd636499ca
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
af176dfadfbede16488ab027b403032b1b75aaa721ae4d759501cad2de57b21e
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
63ca9723079e5af9dc34b5c3da54a320ad1b45d074fd9dee7cb236586a1c2b78
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
75844f282d7b9d9dba69f8bb0318dc8d99b30911f96db2807b0b1317f4645ca4
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
18b56e5159fa71657ba873626cb4e89323c46466aa81962afc34d9c7c57e8ba6
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
b7a6e1a1bdb3c7ab556f7ebe32a7ab028d7a0655a969a2d806073b85b58ac3dd
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1fc2a12684a63642dbec8e76a1bf691ab3461778ccfe5287871b4ac930c963e8
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
c509358158f8a705c198fd12ec6851b94262af5a7cf669851fea9a5322179fde
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
a0474612d2bfc28f4f51a9c3046eec65b0fede6a13ebb4323c896e3c92ee1b85
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
117d5b6cf5e19e355f0d6057b305d603fb207874a84a5e9435a38269291eefca
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
eed53939da16255a9219403e84dd2e649335a8a986b6d14e7aff93dd842863e5
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1e9bc775487bb6ce886084478289467c3a6da4a3caeea8dfb2b239509df1933a
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
cb020fd2ff2006a3bc0801bdfe199bdd3065a696a8d4f381b36d5b644f8d8e43
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
6eb7e32fd955b06b03d55ec12f5c5b44f1dcda62aae46031cc72d09491955bfd
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
de119c6146e6996cb11d4c5126de71023f39c128c180ffd30b9a04307eb386cb
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
874955f404cddfb7bb3b6ef18adf0db6ac48c0fdccc0180fff0075b6f7f70cca
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
7d0d9df51827285c1c47e283e6cae9edf30a0029442d9387e2a9eb4cc537d9be
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
844049a7f7a3d382c00a02ade928af13d0d4ef12806783e413fc71d3c0f8e2c2
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
a74a39422f6c16e51d358abd75a7177067d17d84dde20e053cf1404b2a4d475f
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
a26a28f87f84e06dd182e25846179069e30ab237ff60d9e9ce2257fdab988f03
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
0935914e59d65931bdceaf4e730f194b8786719c2d635dd1645f2a1f929f7193
0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
3
Successful registration
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

そんなにすぐには反映されないか

コマンド
node resolve-did.mjs
実行結果
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
          "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": true,
      "recoveryCommitment": "EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw",
      "updateCommitment": "EiBZZ-SUAgn5MooBsD5zMgLPCa3VqyY-bzbHWet62V4IaQ"
    },
    "canonicalId": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
  }
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

更新できた!!

コマンド
node resolve-did.mjs
実行結果
{
  "@context": "https://w3id.org/did-resolution/v1",
  "didDocument": {
    "id": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
    "@context": [
      "https://www.w3.org/ns/did/v1",
      {
        "@base": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
      }
    ],
    "service": [
      {
        "id": "#domain-1",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      },
      {
        "id": "#domain-2",
        "type": "LinkedDomains",
        "serviceEndpoint": "https://foo.example.com"
      }
    ],
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "PxuC0Xm5H4fcR4OAFTynBeQfGja0u9FjsFAJRi4skl0",
          "y": "O8RF-BP_Hy_RlcNaZdR5NOJQGPRH-CV43LlfmwMS98I"
        }
      }
    ],
    "authentication": [
      "#key-1"
    ]
  },
  "didDocumentMetadata": {
    "method": {
      "published": true,
      "recoveryCommitment": "EiBV7optSieZwDOLl-7pwJT-n7nXdGF_EQJZGBMNPjLHkw",
      "updateCommitment": "EiB7QKEEygN_pQj78P5hORx6cqJpFG04zs8r4xh9TCJlzQ"
    },
    "canonicalId": "did:ion:EiDTaXiorXIqIBa4DQQxS9MZXpTqHE-4vvtTnE_NcPkPWA"
  }
}

ちゃんと service#domain-2 が増えている

結局更新する度に鍵を変える必要があるというのが正解だった