🔑

XRP Ledgerでマルチシグを使用する

2022/10/16に公開

XRP Ledger とは

BitcoinやEthereumなどと同じ分散型のパブリックブロックチェーンです。PoWやPoSとは異なる独自のコンセンサスアルゴリズムが用いられています。
EVMによるスマートコントラクトは搭載しておらず、ネイティブでの機能(広義のスマートコントラクト)拡張を行うことで進化し続けています。執筆時点ではNFT機能の有効化についてコミュニティによる投票中であり、AMMや独自の(狭義の)スマートコントラクトについての開発が進められています。
ネイティブトークンとしてXRPが存在し、その最小単位は1 drop(1 XRP = 1,000,000 drop)となっています。

ExpandedSignerList 修正案の有効化

2022/10/14にマルチシグの機能を拡張するExpandedSignerList 修正案がXRP Ledgerメインネットで有効化されました。
この修正案はXRPL Labsの開発者であるRichardAHにより提案されました。彼はXRP Ledgerへスマートコントラクト機能をもたらすHooksのメイン開発者でもあります。

この修正により次の2つが変更されます。

  1. マルチシグに設定可能な署名者数が8から32へと拡張されました。
  2. マルチシグによるトランザクション作成時に各署名者の情報欄に32バイトの情報を格納可能になります。

これらによりHooksやサイドチェーンなどのセキュリティが大幅に向上します。

https://xrpscan.com/amendment/B2A4DB846F0891BF2C76AB2F2ACC8F5B4EC64437135C6E56F3F859DE5FFD5856

マルチシグの利用

1. 署名者のリストを用意する

今回は3つの署名用のアカウントを用意し、マルチシグの定足数を2とします。
つまり署名用のアカウント3つのうち、2つのアカウントの署名により有効なトランザクションを送信可能とします。

  • 署名用アカウントA
Address: rKjU1h8eKNv53ZnuGhVPX8T4GGBgoUmjj4
Secret:  sEdVkxWPmae9QDV8yd2aSbyPPQTAwJ6
  • 署名用アカウントB
Address: rUasRGvHhr5AzaGVTdJaUtGU1r8vxQAuRU
Secret:  sEdSyyNeCQV9TCA2MLgwUYB3jbFxUAU
  • 署名用アカウントC
Address: rn9PWWoUmv3wzdR546eKXE45ceWT6ZXzft
Secret:  sEdVJuTiqx88njPjuxbsQxjeAoGcP1k

2. アカウントへマルチシグを設定する

  • マルチシグを設定するアカウント
Address: rJFxHTb6RzbW5wTUwBDQ92A27qXtnYVrRw
Secret:  sEd7Pi2eypYE5owiH7vD38FX7ALKZSd

SignerListSet トランザクションを利用し、署名用アカウントを署名者として設定します。

import { Client, Wallet } from "xrpl";
const wallet = Wallet.fromSeed("sEd7Pi2eypYE5owiH7vD38FX7ALKZSd")
const client = new Client("wss://s.altnet.rippletest.net:51233");
await client.connect();

await client.submitAndWait(
  {
    TransactionType: "SignerListSet",
    Account: wallet.classicAddress,
    SignerEntries: [
      {
        SignerEntry: {
          // 署名用アカウント A
          Account: 'rKjU1h8eKNv53ZnuGhVPX8T4GGBgoUmjj4',
          SignerWeight: 1,
        },
      },
      {
        SignerEntry: {
          // 署名用アカウント B
          Account: 'rUasRGvHhr5AzaGVTdJaUtGU1r8vxQAuRU,
          SignerWeight: 1,
        },
      },
      {
        SignerEntry: {
          // 署名用アカウント C
          Account: 'rn9PWWoUmv3wzdR546eKXE45ceWT6ZXzft,
          SignerWeight: 1,
        },
      },
    ],
    // トランザクションが有効となるSignerWeight数:2
    SignerQuorum: 2,
  },
  { wallet }
);

https://xrpl.org/ja/signerlistset.html

3. アカウントのマスターキーを無効化する

AccountSet トランザクションを使用しマスターキーを無効化します。
マスターキーを無効化することにより、マルチシグでの署名でのみトランザクションを送信可能とします。

await client.submitAndWait(
  {
    TransactionType: "AccountSet",
    Account: wallet.classicAddress,
    SetFlag: 4,
  },
  { wallet }
);

Flag 4がマスターキーの無効化(asfDisableMaster)を表しています。

https://xrpl.org/ja/accountset.html

4. トランザクションへの署名

署名対象のトランザクションを用意

今回は rQQQrUdN1cLdNmxH4dHfKgmX5P4kf3ZrM アカウントへ100dropを送信する Payment トランザクションを例として解説していきます。
Sequence については後述します。

{
  "TransactionType": "Payment",
  "Account": "rJFxHTb6RzbW5wTUwBDQ92A27qXtnYVrRw",
  "Destination": "rQQQrUdN1cLdNmxH4dHfKgmX5P4kf3ZrM",
  "Amount": "100",
  "Fee": "100"
  // "Sequence": 0,
}

シーケンス番号の取得

XRP Ledgerにてトランザクションを送信する場合、トラザクションにはそのアカウントが送信したトランザクションの順序を一意的に決定するためのシーケンス番号(Sequence)を付与しなければなりません。
この番号は Client.submit メソッドを利用する場合は自動入力も可能ですが、マルチシグでは事前にトランザクション情報を決定する必要があるため、手動での設定が必要となります。

また、同様に手数料を表す Fee 項目に関しても、マルチシグの場合は手動での設定が必要となります。

const account = await client.request({
  account: wallet.classicAddress,
  command: "account_info",
});

const sequence = account.result.account_data.Sequence;

取得したシーケンス番号を使用し、トランザクションのJSONデータを用意します。
手数料はここでは100dropを設定します。

const txJson = JSON.stringify({
  TransactionType: "Payment",
  Destination: "rQQQrUdN1cLdNmxH4dHfKgmX5P4kf3ZrM",
  Amount: "100",
  Account: wallet.classicAddress,
  Sequence: sequence,
  Fee: "100",
});

署名用アカウント A での署名

署名用アカウントAにてトランザクション情報に署名します。

import sign from "xrpl-sign-keypairs";

const signerWalletA = Wallet.fromSeed("sEdVkxWPmae9QDV8yd2aSbyPPQTAwJ6");
const signedTxByA = sign(txJson, deriveKeypair(signerWalletA.seed), {
  signAs: signerWalletA.classicAddress,
});
// 署名用アカウントAの署名データが付与されたトランザクションのJSON情報を取得
const txJsonByA = JSON.stringify(signedTxByA.txJson);

txJsonByAは次のような内容になっています。
Signer の箇所に署名アカウントAの署名情報が格納されています。

txJsonByA
{
  "TransactionType": "Payment",
  "Account": "rJFxHTb6RzbW5wTUwBDQ92A27qXtnYVrRw",
  "Destination": "rQQQrUdN1cLdNmxH4dHfKgmX5P4kf3ZrM",
  "Amount": "100",
  "Sequence": 32045670,
  "Fee": "100",
  "SigningPubKey": "",
  "Signers": [
    {
      "Signer": {
        "Account": "rKjU1h8eKNv53ZnuGhVPX8T4GGBgoUmjj4",
        "SigningPubKey": "03236E980A25EB65E54A0ECD1EDD4634436112446345F35576513F097B4798DDAE",
        "TxnSignature": "3045022100F58CD2CD842CC105AD3AFA7A751E2B69E5FD65DE837A5EC39183ED4E543F8FC202207026D2A53082E5B026C5C8E3F0D7B233A62B706CFE4B58C38C5F12EE5F4239CA"
      }
    }
  ]
}

署名用アカウント B での署名

署名用アカウントBにてアカウントAが作成したトランザクション情報に署名します。

const signerWalletB = Wallet.fromSeed("sEdSyyNeCQV9TCA2MLgwUYB3jbFxUAU");
const signedTxByB = sign(txJsonByA, deriveKeypair(signerWalletB.seed), {
  signAs: signerWalletB.classicAddress,
});
// 署名用アカウントBの署名データが付与されたトランザクションのJSON情報を取得
const signedTxJson = JSON.stringify(signedTxByB.txJson);
signedTxJson
{
  "TransactionType": "Payment",
  "Account": "rJFxHTb6RzbW5wTUwBDQ92A27qXtnYVrRw",
  "Destination": "rQQQrUdN1cLdNmxH4dHfKgmX5P4kf3ZrM",
  "Amount": "100",
  "Sequence": 32045670,
  "Fee": "100",
  "SigningPubKey": "",
  "Signers": [
    {
      "Signer": {
        "Account": "rKjU1h8eKNv53ZnuGhVPX8T4GGBgoUmjj4",
        "SigningPubKey": "03236E980A25EB65E54A0ECD1EDD4634436112446345F35576513F097B4798DDAE",
        "TxnSignature": "3045022100F58CD2CD842CC105AD3AFA7A751E2B69E5FD65DE837A5EC39183ED4E543F8FC202207026D2A53082E5B026C5C8E3F0D7B233A62B706CFE4B58C38C5F12EE5F4239CA"
      }
    },
    {
      "Signer": {
        "Account": "rUasRGvHhr5AzaGVTdJaUtGU1r8vxQAuRU",
        "SigningPubKey": "02D00F62CE4348742C8C63DA9EBAE5EB11CCE1BB891E3B66FAA7C547F744D77E3C",
        "TxnSignature": "30440220363D1EBEC631070F3371176F65D7D71184E47C6E70FEBCAD03E49E2CCC611FC70220662B0AED26951B19668568C709A6F69E0FE2CD2C555CDBA73925BCA1201B0FD1"
      }
    }
  ]
}

トランザクションを送信する

submit_multisigned コマンドを使用し、署名済みのトランザクションを送信します。

await client.request({
  command: "submit_multisigned",
  tx_json: signedTxJson,
});

https://xrpl.org/ja/submit_multisigned.html

まとめ

以上の流れでマルチシグを使用したトランザクションの送信が行えます。

マルチシグを利用すると高いセキュリティでアカウントを管理できるようになります。
dAppsとしてのHooksやサイドチェーンでは必須の機能となるでしょう。

Discussion