しゅーと (@shutingrz)
しゅーと (@shutingrz)
Security researcher
Apr 29, 2019 5 min read

自作仮想通貨入門(8) - アドレスのプレフィックス変更

※この章では src/chainparams.cpp を変更します。

仮想通貨の花形的存在、ウォレットアドレスについての説明とソースコードの変更をしていきます。

プレフィックスの仕組みについて

Litecoinではアドレスの1文字目に必ず"L"が入ります。このようにアドレスの1文字目に何らかのプレフィックスがつく仕様は、bitcoinの実装に由来するものです。
bitcoinでは各種管理情報(ウォレットの秘密鍵や公開鍵など)の視認性をあげるためにデータをハッシュ化したのちBase58エンコードを行います。
また、Base58エンコード前に、タイプミスによる事故の発生を防ぐ目的でチェックサムをつけています。
これらの一連の処理を base58Check と呼びますが、base58Check にはヘッダを付与することでアドレスの種類を仕分けることが可能になっています。
技術的詳細については下記URLを参照してください。

https://en.bitcoin.it/wiki/List_of_address_prefixes

初期のbitcoinはビットコインアドレス(P2PKHアドレス)を生成するとき、 アドレスが必ず"1"から始まっていました。
なお、今回の仮想通貨の基となるLitecoinはP2PKHアドレスが必ず"L"で始まります。
これは下記コードで指定することで実現しています。

base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,48);

上記のURLを確認すると、48を指定すれば1文字目が必ず"L"で始まることがわかります。

アドレスプレフィックスを決める(PUBKEY_ADDRESS)

今回作成する仮想通貨名称はRaccoinで、接頭辞がRなのでアドレスもRから始まるように変更します。
上記URLを参考にすると60か61を指定すれば必ず R から始まるようなので、60 を指定します。

P2SH 用のアドレスプレフィックスを決める(SCRIPT_ADDRESS)

Litecoin 0.16 は P2SH にも対応しています。
P2SH はより汎用的なデータを定義できる方式で、メジャーな例ではマルチシグに利用されたりしています。
P2SH 用のプレフィックスは BIP-13 で 5 にする(3から始まる)よう決められていますから、何も変えないようにしましょう。

P2SH ベースの Segwit 用アドレスプレフィックスを決める (SCRIPT_ADDRESS2)

Litecoin 0.16 は Segwit にも対応しています。(というかそのために0.16を使いました)
ただ Segwit は新しい機能であるため、Segwit アドレスの形式に対応していないウォレットがあります。
そのため、P2SH に対応した Segwit 用のアドレスプレフィックスを上記のURLを参考にして設定します。
今回は S から始めることにしたので、 63 を指定します。

ネイティブ Segwit 用のアドレスプレフィックスを決める(bech32)

こちらは完全に Segwit に対応したウォレット同士で利用するためのアドレスプレフィックスです。
Litecoin の Segwit 用のアドレスプレフィックスは3文字で、“ltc"です。
この値は割と好きに決めてよさそうですが、今後作成するウォレットのバリデータにあまり手を加えたくないため、同じく3文字で設定することにします。
今回は Raccoin から3文字とって、“xri” にします。
・・・が、実はこの変更は「単純な文字列置換」章の「小文字略称の書き換え」で既に行っているのでこの項目での置き換えは不要です。

Testnet/Regtest 用のアドレスプレフィックス

今まで決めてきたのは Mainnet 用のアドレスプレフィックスであり、テスト用のネットワークであるTestnetとRegtestのアドレスプレフィックスも決めないといけません。
ただこれらは本番環境では使わないのでカジュアルに決めていきましょう。

Testnet

  • アドレスプレフィックス(PUBKEY_ADDRESS)
    一般的に、Mainnetのプレフィックスを小文字に変えるようです。
    今回は"r"から始めたいので、122 を指定します。

  • P2SH 用のアドレスプレフィックス(SCRIPT_ADDRESS)
    こちらもBIP13で196を指定(2から始まる)されているので、変更しません。

  • P2SH ベースの Segwit 用アドレスプレフィックス (SCRIPT_ADDRESS2)
    今回は"s"から始めるようにするので、 125 にします。

  • ネイティブ Segwit 用のアドレスプレフィックス(bech32) 一般的に、Mainnetのアドレスプレフィックスの最初に"t"を付与する形にするようです。
    なので今回は"txri"にします。
    ・・・が、実はこの変更は「単純な文字列置換」章の「小文字略称の書き換え」で既に行っているのでこの項目での置き換えは不要です。

Regtest

PUBKEY_ADDRESS、SCIPRT_ADDRESS、SCRIPT_ADDRESS2 ともにTestnetと同様でよいです。

  • ネイティブ Segwit 用のアドレスプレフィックス(bech32)
    一般的に、Mainnetのアドレスプレフィックスの最初に"r"を付与する形にするようです。
    なので今回は"rxri"にします。
    ・・・が、実はこの変更は「単純な文字列置換」章の「小文字略称の書き換え」で既に行っているのでこの項目での置き換えは不要です。

ソースコードの変更作業

CMainParams()

base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,60);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
base58Prefixes[SCRIPT_ADDRESS2] = std::vector<unsigned char>(1,63);  
base58Prefixes[SECRET_KEY] =     std::vector<unsigned char>(1,188);

bech32_hrp = "xri";
  • アドレスは「R」 から始まるようにしました。
  • マルチシグアドレスは「S」から始まるようにしました。
  • SECRET_KEY は PUBKEY_ADDRESS + 128 にするようなので、今回は 188 にしました。

CTestNetParams()

base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,122);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
base58Prefixes[SCRIPT_ADDRESS2] = std::vector<unsigned char>(1,125);  
base58Prefixes[SECRET_KEY] =     std::vector<unsigned char>(1,254);

bech32_hrp = "txri";
  • アドレスは「r」 から始まるようにしました。
  • マルチシグアドレスは「s」から始まるようにしました。
  • SECRET_KEY は PUBKEY_ADDRESS + 128 にするようですが、それだと255を超えてしまうので254にしました。

CRegTestParams()

base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,122);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
base58Prefixes[SCRIPT_ADDRESS2] = std::vector<unsigned char>(1,125);  
base58Prefixes[SECRET_KEY] =     std::vector<unsigned char>(1,254);

bech32_hrp = "rxri";
  • アドレスは「r」 から始まるようにしました。
  • マルチシグアドレスは「s」から始まるようにしました。
  • SECRET_KEY は PUBKEY_ADDRESS + 128 にするようですが、それだと255を超えてしまうので254にしました。

本章ではアドレスのプレフィックスの説明と変更をしました。
次の章でやっとchainparams.cppの変更は終わりで、もうすぐで完成です。