🔑

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

2022/10/16に公開約6,700字

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

マルチシグの利用

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

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

署名用アカウント A

Address: rKjU1h8eKNv53ZnuGhVPX8T4GGBgoUmjj4
Secret:  sEdVkxWPmae9QDV8yd2aSbyPPQTAwJ6

署名用アカウント B

Address: rUasRGvHhr5AzaGVTdJaUtGU1r8vxQAuRU
Secret:  sEdSyyNeCQV9TCA2MLgwUYB3jbFxUAU

署名用アカウント C

Address: rn9PWWoUmv3wzdR546eKXE45ceWT6ZXzft
Secret:  sEdVJuTiqx88njPjuxbsQxjeAoGcP1k

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

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

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

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

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

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

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

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

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

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

今回は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

ログインするとコメントできます