Symbol SDKを利用したトランザクションの発行プログラム

<p>この記事では、Symbol SDKを使ってトランザクションの発行するプログラムについて解説します。トランザクションを発行することで、XYMやモザイクを送金することができます。
<p>ここでは、Node.jsをインストールし、またWebページ制作に使うHTMLファイルとブラウザに利用できるようにカスタマイズしたSymbol SDKが同じフォルダにあるものとして説明します。</p>

<p>なので、Symbol SDKを用意していない方は、以下の記事を参照してください。</p>
<p><strong><a href="https://qiita.com/YutopianF/items/f2196b844487b0e9ef7a">ブラウザ環境でのSymbolの実行環境の構築について</a></strong></p>
<p>&nbsp;</p>
<h2 style="background-color: #44004e; padding: 8px;"><strong><span style="font-size: 16pt; color: #ffffff;">トランザクションの発行プログラムについて</span></strong></h2>
<p>以下のプログラムについて説明します。</p>
<pre class="language-markup"><code>&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"&gt;&lt;/script&gt;
    &lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" /&gt;
    &lt;script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"&gt;&lt;/script&gt;
    &lt;script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"&gt;&lt;/script&gt;
    &lt;script src="symbol-sdk-0.21.0.js"&gt;&lt;/script&gt;
    &lt;title&gt;Symbolを送金してみる&lt;/title&gt;
  &lt;/head&gt;
  &lt;body style="margin:30px;"&gt;
    &lt;h1&gt;Symbolを触ってみる&lt;/h1&gt;
    
    &lt;h2&gt;送金してみる&lt;/h2&gt;
    &lt;div style="margin-top:10px;"&gt;
      Receiver Address:&lt;input type="text" id="receiver_address" class="uk-input"&gt;
    &lt;/div&gt;
    &lt;div style="margin-top:10px;"&gt;
      Sender Private Key:&lt;input type="text" id="pk" class="uk-input"&gt;
    &lt;/div&gt;
    &lt;div id="issue_tx" class="uk-button uk-button-primary" style="margin-top:10px;"&gt;1xym送金&lt;/div&gt;

    &lt;script&gt;
      const symbol=require("/node_modules/symbol-sdk");
      let privateKey;
      const nodeAddress='http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000';
      const networkGenerationHash="6C1B92391CCB41C96478471C2634C111D9E989DECD66130C0430B5B8D20117CD";
      const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
      const accountHttp=repositoryFactory.createAccountRepository();

      $('#issue_tx').click(function(){
        privateKey=$('#pk').val();
        let tx = symbol.TransferTransaction.create(
          symbol.Deadline.create(),
          symbol.Address.createFromRawAddress($('#receiver_address').val()), 
          [new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],
          symbol.PlainMessage.create('Send Symbol'),
          symbol.NetworkType.TEST_NET,
          symbol.UInt64.fromUint(100000)
        );
        const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);
        let signedTx = account.sign(tx,networkGenerationHash);
        console.log(signedTx);
        new symbol.TransactionHttp(nodeAddress).announce(signedTx)
        .subscribe(function(x){
          alert("Txが発行されました！");
          console.log(x);
        },function(x){
          alert("エラーが発生しました！");
          console.error(err)
        });  
      });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>&nbsp;</p>
<p>このコードの完成版は、以下のようになります。</p>
<p><img src="https://s3-ap-northeast-1.amazonaws.com/nem-social/blog/50000/51000/51400/51422/1604479277スクリーンショット 2020-11-04 17.40.33.png" alt="" width="352" height="304" /></p>
<p>このことについて、説明します。</p>

<h2><span style="background-color: #993366; color: #ffffff;"><strong><span style="font-size: 18pt; background-color: #44004e;">head要素について</span></strong></span></h2>
<p><em><strong>&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"&gt; </strong></em><br /><em><strong>&lt;/script&gt;</strong></em></p>
<p>この要素で、jQueryを読み込みます。</p>

<p>以下の3行でCSSのフレームワークであるUIKitを読み込んでいます。</p>
<p><em><strong>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" /&gt;</strong></em><br /><em><strong>&lt;script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"&gt;&lt;/script&gt;</strong></em><br /><em><strong>&lt;script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"&gt;&lt;/script&gt;</strong></em></p>

<p>&nbsp;Symbol SDKをHTMLファイルと同じフォルダ内に置いている上で、このようにして書きます。</p>
<p><em><strong> &lt;script src="symbol-sdk-0.21.0.min.js"&gt;&lt;/script&gt;</strong></em></p>
<p>このように書くことで、Symbol SDKをHTMLファイルに読むこむことができるようになります。</p>
<p>また、以下のようにして書くと、Symbol SDKをネットワークからダウンロードして読む込みこともできます。</p>
<p><em><strong>&lt;script src=&ldquo;https://xembook.github.io/nem2-browserify/symbol-sdk-0.21.0.js&rdquo;&gt;&lt;/script&gt;</strong></em></p>

<h2><span style="background-color: #993366; color: #ffffff;"><strong><span style="font-size: 18pt; background-color: #44004e;">body要素について</span></strong></span></h2>
<p>ここで、このプログラムでは2つの入力枠を用意しています。</p>
<p>&nbsp;</p>
<p><img src="https://s3-ap-northeast-1.amazonaws.com/nem-social/blog/50000/51000/51400/51422/1604479277スクリーンショット 2020-11-04 17.40.33.png" alt="" width="352" height="304" /></p>
<p>以下のコードがRecever Addressの入力枠となるコードです。</p>
<p><strong>&nbsp;<em>&lt;div style="margin-top:10px;"&gt;</em></strong><br /><em><strong>Receiver Address:&lt;input type="text" id="receiver_address" class="uk-input"&gt;</strong></em><br /><em><strong>&lt;/div&gt;</strong></em></p>

<p>次に、以下のコードでSender Private Keyの入力枠となるコードです。</p>
<p><em><strong>&lt;div style="margin-top:10px;"&gt;</strong></em><br /><em><strong>Sender Private Key:&lt;input type="text" id="pk" class="uk-input"&gt;</strong></em><br /><em><strong>&lt;/div&gt;</strong></em></p>

<p>そして、以下のコードがボタンとなるものになります。このボタンをクリックすると、実行となります。</p>
<p><em><strong>&lt;div id="issue_tx" class="uk-button uk-button-primary" style="margin-top:10px;"&gt;1xym送金&lt;/div&gt;</strong></em></p>

<h2><span style="background-color: #993366; color: #ffffff;"><strong><span style="font-size: 18pt; background-color: #44004e;">script要素について</span></strong></span></h2>
<p>以下の1行で、Symbol SDKに入っている情報を変数symbolに代入します。</p>
<p><em><strong>const symbol=require("/node_modules/symbol-sdk");</strong></em></p>
<p>つまり、Symbol SDKを呼び出す宣言になります。</p>

<p>また、プライベートキーを入れておく事を確保するために、変数をここで用意しておきます。</p>
<p><em><strong>let privateKey</strong></em></p>

<p>以下のコードでどこのノードアドレスに情報を問い合わせるのかを定数nodeAddressにて設定します。</p>
<p><em><strong>const nodeAddress='http://api-01.eu-central-1.096x.symboldev.network:3000';</strong></em></p>
<p>ノードアドレスを調べたい場合は、赤い四角で囲んだところをクリックしてノードのアドレスを確認できます。</p>
<p><img src="https://s3-ap-northeast-1.amazonaws.com/nem-social/blog/50000/51000/51400/51422/16047202921604583179スクリーンショット 2020-11-05 22.31.39.png" alt="" width="585" height="838" /></p>
<p>&nbsp;</p>
<p>ここから、ノードを選んでください。</p>
<p><img src="https://s3-ap-northeast-1.amazonaws.com/nem-social/blog/50000/51000/51400/51421/1604411726スクリーンショット 2020-11-03 11.21.45.png" alt="" width="729" height="441" /></p>

<p>ノードを選んだら、それに対応するネットワークジェネレーションハッシュが必要になります。</p>
<p><em><strong> const networkGenerationHash="1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B";</strong></em></p>
<p>このジェネレーションハッシュは以下のページにて見つけ方が書かれています。</p>
<p><a href="https://docs.symbolplatform.com/ja/getting-started/first-application.html">https://docs.symbolplatform.com/ja/getting-started/first-application.html</a></p>

<p><img src="https://s3-ap-northeast-1.amazonaws.com/nem-social/blog/50000/51000/51400/51421/1604411698スクリーンショット 2020-11-03 11.31.38.png" alt="" width="1217" height="266" /></p>

<p>つまり、ノードアドレスが、</p>
<p><em><strong>http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000</strong></em></p>
<p>の場合は</p>
<p><em><strong>http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/node/info</strong></em></p>
<p>ということになります。このURLで開いてみると・・・</p>
<p><img src="https://s3-ap-northeast-1.amazonaws.com/nem-social/blog/50000/51000/51400/51421/1604411679スクリーンショット 2020-11-03 11.47.07.png" alt="" width="1217" height="208" /></p>
<p>networkGenerationHashSeedに、ネットワークジェネレーションハッシュが書かれています。これをコードに書きます。</p>
<p>ノードのURLとネットワークジェネレーションハッシュは一対一に結びついているので、ノードURLを決めたら、必ずネットワークジェネレーションハッシュを見つけてください。</p>
<p>次に、このコードを見ます。</p>
<p><em><strong> const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);</strong></em></p>
<p>これは、Symbolの中身の仕組みを使うのに、nodeAddressを引数とした<strong>r</strong>epositoryFactoryを定数としておいたものです。(何をしているのかはわかりません)</p>

<p>次に、このコードを見ます。</p>
<p><em><strong>const accountHttp=repositoryFactory.createAccountRepository();</strong></em></p>
<p>定数 accountHttpは、先ほど書いたrepositoryFactoryをcreateAccountRepositoryで読んで来いというものです。</p>

<p>ここまで読んだ以下のコード</p>
<pre class="language-markup"><code>      const symbol=require("/node_modules/symbol-sdk");
      let address;
      const nodeAddress='http://api-01.eu-central-1.096x.symboldev.network:3000';;
      const networkGenerationHash="1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B";
      const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
      const accountHttp=repositoryFactory.createAccountRepository();</code></pre>
<p>は、基本的にコピペでOKで、変えるべきなのは、ノードアドレスと、そのジェネレーションハッシュということになります。</p>

<p>以下のコードについて、IDをissue_txで定義したボタン(「1XYM送金」と書かれたボタン)をクリックすると、</p>
<p><em><strong> $('#issue_tx').click(function(){</strong></em></p>
<p>の文以降の処理が発生します。</p>
<pre class="language-markup"><code> privateKey=$('#pk').val();
        let tx = symbol.TransferTransaction.create(
          symbol.Deadline.create(),
          symbol.Address.createFromRawAddress($('#receiver_address').val()), 
          [new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],
          symbol.PlainMessage.create('Send Symbol'),
          symbol.NetworkType.TEST_NET,
          symbol.UInt64.fromUint(100000)
        );
        const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);
        let signedTx = account.sign(tx,networkGenerationHash);
        console.log(signedTx);
        new symbol.TransactionHttp(nodeAddress).announce(signedTx)
        .subscribe(function(x){
          alert("Txが発行されました！");
          console.log(x);
        },function(x){
          alert("エラーが発生しました！");
          console.error(err)
        }); </code></pre>
<p>このコードについて見ていきます。</p>

<p>以下のコードで、入力枠に入力したプライベートキーと.val()を変数PrivateKeyに代入したものになります。</p>
<p><em><strong>privateKey=$('#pk').val();</strong></em></p>
<p>以下のコードで、トランザクションを発行するコードになり、基本的にコピペでOKです。</p>
<pre class="language-markup"><code>let tx = symbol.TransferTransaction.create(
          symbol.Deadline.create(),
          symbol.Address.createFromRawAddress($('#receiver_address').val()), 
          [new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],
          symbol.PlainMessage.create('Send Symbol'),
          symbol.NetworkType.TEST_NET,
          symbol.UInt64.fromUint(100000)
        );</code></pre>

<p>以下のコードは、トランザクションの有効期間になります。</p>
<p><em><strong>symbol.Deadline.create(),</strong></em></p>
<p>この書き方で、デフォルトで設定された期間になります。</p>
<p><br />以下のコードで入力したアドレスを取得しています。</p>
<p><em><strong> symbol.Address.createFromRawAddress($('#receiver_address').val()),</strong></em></p>

<p>次に、以下のコードで、送るモザイクの種類とその量を決めます。</p>
<p><em><strong>[new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],</strong></em></p>
<p>5E62990DCAC5BE8A：モザイクのID</p>
<p>fromUint(100)：送る量。単位は&mu;XYM。この時は100&mu;XYMを送る量としています。</p>

<p>以下のコードで送るメッセージを設定します。</p>
<p><em><strong>symbol.PlainMessage.create('Send Symbol'),</strong></em></p>
<p>このコードでは、メッセージを「Send Symbol」としています。</p>
<p>&nbsp;</p>
<p>以下のコードで、テストネット上で送るように指示したものになります。</p>
<p><em><strong>symbol.NetworkType.TEST_NET,</strong></em></p>
<p>&nbsp;</p>
<p>以下のコードで、送金手数料の最大値を書いています。</p>
<p><em><strong>symbol.UInt64.fromUint(100000)</strong></em></p>

<p>次に、以下のコードを見ていきます。</p>
<pre class="language-markup"><code>const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);
        let signedTx = account.sign(tx,networkGenerationHash);
        console.log(signedTx);
        new symbol.TransactionHttp(nodeAddress).announce(signedTx)
        .subscribe(function(x){
          alert("Txが発行されました！");
          console.log(x);
        },function(x){
          alert("エラーが発生しました！");
          console.error(err)
        }); </code></pre>
<p>このコードで、どのアカウントでサインするのかを定義します。</p>
<p><em><strong>const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);</strong></em></p>
<p>ここでは、プライベートキーからアカウントを作ります。ここでは、引数privateKyeに入っているプライベートキーと使うSymbolのネットワークタイプ(ここではテストネット)を</p>
<p>引数privateKeyを変える事で、アカウントが変わります。さっき作ったトランザクションを、このアカウントでサインします。</p>

<p>次に、signTxという変数に対して</p>
<p><em><strong>let signedTx = account.sign(tx,networkGenerationHash);</strong></em></p>
<p>と書く事で、トランザクションデータとネットワークジェネレーションハッシュを使うことで、サインしたトランザクションができます。</p>

<p>ここで、sighedTxの中身を見てみます。</p>
<p><strong><em>console.log(signedTx);</em></strong></p>
<p>すると、Google Chromeのディベロッパーツールのconsoleを見ると、以下のようになっています。</p>
<p><img src="https://s3-ap-northeast-1.amazonaws.com/nem-social/blog/50000/51000/51400/51422/1604566611スクリーンショット 2020-11-05 17.55.49.png" alt="" width="698" height="143" /></p>
<p>payloadが、サインされたトランザクションを表します。これを作れる仕組みを考えれば、他のプログラミング言語にも移植することができます。(これがややこしいみたいです)<br />このpayroadは表示されていると、トランザクションを発生させたことに成功したことになります。</p>

<p>そして、次のコードで、ネットワークにXYMやモザイクを送金します。</p>
<p><em><strong> new symbol.TransactionHttp(nodeAddress).announce(signedTx)</strong></em><br /><em><strong>.subscribe(function(x){</strong></em><br /><em><strong>alert("Txが発行されました！");</strong></em><br /><em><strong>console.log(x);</strong></em><br /><em><strong>},function(x){</strong></em><br /><em><strong>alert("エラーが発生しました！");</strong></em><br /><em><strong>console.error(err)</strong></em><br /><em><strong>}); </strong></em></p>
<p>ここで、TransactionHttp(nodeAddress)で使うノードを決め、announce(signedTx)でサインしたトランザクションをネットワークにてアナウンスし、</p>
<p><strong>.subscribe(function(x){</strong><br /><strong>alert("Txが発行されました！");</strong><br /><strong>console.log(x);</strong><br /><strong>},function(x){</strong><br /><strong>alert("エラーが発生しました！");</strong><br /><strong>console.error(err)</strong><br /><strong>});</strong></p>
<p>でアナウンスした結果が画面に表示されるようになります。</p>

※この記事は、nemlogで企画されたイベントであるしゅうさんの勉強会「しゅうさんといっしょ!」の内容の一部をまとめたものです。