🫖

Unityでブロックチェーンウォレットを作るには

2023/03/23に公開

この記事は株式会社Gincoのテックブログとして書いています。

この記事では、ブロックチェーンでゲームを開発したい人に向けて、Unity上でブロックチェーンウォレットを作る方法を紹介します。

Unityは広く知られたゲームエンジンであり、その豊富な機能や使いやすさなどの特徴から、VRサービスやゲーム開発の現場で広く使われています。

そこで、ブロックチェーンを利用するためのHDウォレット(階層決定性ウォレット)を実装することで、ゲーム内でEtherの送金やアセット(武器などのアイテムなど)をブロックチェーンに記録できるようになります。

具体的には、.NET向けライブラリである「NBitcoin(ビットコイン用)」と「Nethereum(イーサリアム用)」を利用して、アプリ内にHDウォレットを作成する方法を紹介します。

実装の流れ

ここからは、大まかに以下のステップに沿って、HDウォレットの実装の流れを紹介します。

  • 開発環境の構築(.NETライブラリ環境/Unity環境)
  • Unityプロジェクトの新規作成
  • 各種.NETライブラリのダウンロード
  • C#スクリプトの作成(ライブラリを用いたニーモニックの生成)
  • HDウォレットの作成 (ニーモニックからの鍵の導出)

1. 開発環境の準備

今回利用する開発環境は以下になります。

  • .NET Core SDK
  • Mono Stable版
  • Unity Hub / Unity Editor
  • VS CodeのC#拡張機能

まずは、Visual Studio Codeなどの統合開発環境を使用して、スクリプトを開発するために.NET Core SDKをインストールします。この記事で使用しているバージョンは.NET 7.0です。
https://dotnet.microsoft.com/ja-jp/download

Unityが利用しているランタイムは、Monoランタイムなので、Mono Stable版をインストールします。この記事で使用しているバージョンは6.12.0(Stable Channel)をです。
https://www.mono-project.com/download/stable/

続いてUnity EditorをインストールするにはUnity Hubを使用します。

まずは以下のサイトにアクセスします。
https://unity.com/ja/download#how-get-started

サイトを少し下にスクロールして「Unity Hubをダウンロード」からダウンロードしてインストールします。

この記事で使用しているバージョンは以下になります。

  • Unity Hub 3.4.1
  • Unity Editor 2021.3.15f1

Unityの外部エディタとしてVS Codeを使用しています。
VS Code MarketplaceからC#拡張機能をインストールします。
https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp

2. Unityプロジェクトの準備

Unity Hubの「New project」から新しいプロジェクトを作成します。

Visual Studio Codeを外部エディタに設定します。
メニューからUnity > Settings
左側の階層からExternal Toolsを選択
External Script Editorに「Visual Studio Code」を選択します。

3. ライブラリの準備

今回使用するライブラリは以下の2つになります。

  • Nethereum
  • NBitcoin

まずは下準備として、Assetsフォルダの下にPluginsフォルダを作成します。
Assets > Create > Folder

フォルダ名にPluginsと入力して変更します。

それではNethereumを組み込むために、Nethereumのgithubリポジトリから「net472UnityCommonAOT.zip」をダウンロードします。
https://github.com/Nethereum/Nethereum/releases/tag/4.12.0

Pluginsフォルダ内に「net472UnityCommonAOT.zip」を解凍して中身のdllファイルをすべてコピーします。

次にNethereumに同梱されているNBitcoinが少し古いため、NBitcoinを新しいものに置き換えます。
NBitcoinのNugetパッケージをダウンロードして解凍します。
下のリンクをクリックするとダウンロードがはじまります。
https://www.nuget.org/api/v2/package/NBitcoin/7.0.24

「nbitcoin.7.0.24.nupkg」の拡張子をnupkgからzipに置き換えて解凍します。
解凍したフォルダの「nbitcoin.7.0.24/lib/netstandard2.1」の階層からNBitcoin.dllをPluginsフォルダに上書きコピーします。

4. C#スクリプトの作成

それではUnity上でプログラムを書いていきましょう。
まずはC#スクリプトの作成をします。
メニューからAssets > Create > C# Script

Assetsフォルダの下に「NewBehaviourScript.cs」が作成されます。
そのファイルをダブルクリックするとVisutal Studio Codeで開くことができます。

NBitcoinを使用してニーモニックを生成するスクリプトを書いていきます。ニーモニックとはランダムな単語列のことでウォレットの秘密鍵を生成するために使用されます。生成する方法についてはBIP39で定められています。

using NBitcoin;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    // Start()は実行すると一度だけ呼び出される
    void Start()
    {
        // 英語で24文字のニーモニックを生成する
        NBitcoin.Mnemonic mnemonic = new NBitcoin.Mnemonic(Wordlist.English);

        // コンソール画面に出力
        Debug.Log("mnemonic: " + mnemonic);
    }
}

空のゲームオブジェクトを作成します。
左上の+ボタン > Create Empty

Game Objectが作成されています。

スクリプトをGame Object上までドラッグでスクリプトを設置します。

Consoleを表示します。
Window > General > Console

Unityの真ん中上部の再生ボタンで実行します。

Consoleにmnemonicの内容が表示されます。

5. HDウォレットの作成

それでは、4で作成したニーモニックを使って、次はウォレットの実装に進みましょう。
ここではウォレットの一種で階層的決定性ウォレットと同義のHDウォレットを作成していきます。

HDウォレット(Hierarchical Deterministic Wallet)とは、暗号通貨を安全に保管するためのウォレットの一種で、親子関係にある多数のアドレスを生成することができるウォレットです。BIP32というビットコインに関するルールで定められています。

HDウォレットは1つのマスターシードを保管するだけで、複数のアドレスを使用できます。これにより、ユーザーは複数の暗号通貨アドレスを簡単に管理することができます。

下の図はHDウォレットの鍵階層のイメージになります。

1つのマスターシードからHDウォレットを作成し、アドレスを取得するためのサンプルプログラムを見ていきます。

ニーモニックからマスターシードを生成します。

byte[] seed = mnemonic.DeriveSeed();

マスターシードからマスターノードを作成します。

ExtKey node = ExtKey.CreateFromSeed(seed);

次にHDウォレットの階層部分の解説をしていきます。
BIP32の標準パスはBIP44に定められています。

m / purpose' / coin_type' / account' / change / address_index

各階層ごとの意味を分解してみると下記の様になります。

  • m: マスターノード
  • purpose: 44の定数を使用する (BIP44への準拠をあらわしています)
  • coin_type: コインタイプ (BTC, ETHなど。登録済みのコインタイプについてはこちらを参照できます => SLIP-0044)
  • account: アカウントノード
  • change: 定数0が非お釣り用のアドレス、定数1がお釣り用のアドレスとして使われます
  • address_index: アドレス階層でインデックス値が振られます
  • アポストロフィー(')は、BIP32の強化鍵を示します
    • 強化鍵にすると子の秘密鍵から親の秘密鍵を求められないようにできます

しかし、このパスはBitcoinなどのUTXO向けに最初に作られたものであり、
Ethereumでは次のパスを使うのが一般的です。

m/44'/60'/0'/0/n
  • purpose: 44'
  • coin_type: 60'
  • account: 0'
  • change: 0
  • address_index: n番目のアドレス

m/44'/60'/0'の階層を作成していきます。Derive(44, true)は定数44の強化鍵をマスターノードから導出しています。Derive(60, true)はコインタイプが60でEthereumの強化鍵を導出し、Derive(0, true)はアカウントのインデックス値が0の強化鍵のアカウントノードを導出しています。

ExtKey accountNode = node.Derive(44, true).Derive(60, true).Derive(0, true);

m/44'/60'/0'に続く/0/0で、Derive(0, false)で受け取りアドレスは定数0の通常の鍵を導出しています。Derive(0, false)はアドレスのインデックス値に0を使用しているので、0番目のアドレスの通常の鍵を導出しています。最後のパスのインデックス値をインクリメントしていくことでひとつのマスターノードから多数のアドレスを作ることができます。

ExtKey key = accountNode.Derive(0, false).Derive(0, false);

導出した鍵から秘密鍵のバイト配列を16進数の文字列にエンコードしたものを作成しています。

string privKeyHex = Encoders.Hex.EncodeData(key.PrivateKey.ToBytes());	

最後に秘密鍵からEthereumアドレスを生成しています。

EthECKey ethKey = new EthECKey(privKeyHex);
string address = ethKey.GetPublicAddress();
Debug.Log("address: " + address);

全体のサンプルプログラムはこうなります。

using NBitcoin;
using NBitcoin.DataEncoders;
using Nethereum.Signer;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    void Start()
    {
        NBitcoin.Mnemonic mnemonic = new NBitcoin.Mnemonic(Wordlist.English);
        Debug.Log("mnemonic: " + mnemonic);

                // ニーモニックからマスターシードを生成します
        byte[] seed = mnemonic.DeriveSeed();
	
	// マスターシードからマスターノードを作成します
        ExtKey node = ExtKey.CreateFromSeed(seed);

        // 今回は下記のような階層からアカウントノードを導出します
        // m / purpose' / coin_type' / account'
        // m / 44' / 60' / 0'
        ExtKey accountNode = node.Derive(44, true).Derive(60, true).Derive(0, true);
	
	// 次にアカウントノードからindexが0の位置の通常鍵を導出します
	// このaddress_indexを0, 1, 2, ...と増やしていくことにより複数のアドレスを生成できます
	// ... / change / address_index
        // ... / 0 / 0
        ExtKey key = accountNode.Derive(0, false).Derive(0, false);

                // 導出した鍵から秘密鍵のバイト配列を16進数の文字列にエンコードしたものを作成します
        string privKeyHex = Encoders.Hex.EncodeData(key.PrivateKey.ToBytes());
	
	// 最後に秘密鍵からEthereumアドレスを生成しています
        EthECKey ethKey = new EthECKey(privKeyHex);
        string address = ethKey.GetPublicAddress();
        Debug.Log("address: " + address);
    }
}

サンプルプログラムはこちらからでも参照できます。
https://github.com/kazpot/techblog-sample

まとめ

本記事では、Unityアプリ上でのHDウォレットの作成について説明しました。ライブラリを利用することで、Unity上で簡単にウォレットを作成できることを示しました。ただし、アプリ開発においては、ニーモニックをどのように安全に保管するかという課題が残ります。この課題を解決するために、GincoではUnityのゲームアプリ内で手早く安全にウォレットを組み込めるUnity SDKを開発しています。

https://www.ginco.co.jp/service/web3cloud

最後に、Gincoでは、ブロックチェーンの未来を一緒に開発する仲間を募集しています!Gincoでブロックチェーンについて学びたい方、ウォレットについて詳しく知りたい方は、以下のリンクからご応募ください。お待ちしています。
株式会社Ginco の求人一覧

参考文献

https://docs.nethereum.com/en/latest/
https://ethereum.org/ja/developers/docs/programming-languages/dot-net/
https://techmedia-think.hatenablog.com/entry/2017/08/31/125000

Discussion