速習Symbolブロックチェーン体験記「とろ速Symbol」です。

「1. はじめに」「2.環境構築」についてはほぼ目を通すだけのため、スキップしました。

本ページでは「アカウント」をやっていきます。

感想編はこちら。

なお、今回はこちらのテストネットノードをお借りしました。

@ftakao2007

hoge.harvesting-sweet-potatoes.club(Symbol テストネット)

(敬称略)

もちろんメインネットのノードも運用されております。

間違っていたり、問題があったら教えてください。

本稿では「3. アカウント」の実施内容を記載します。

新規作成 – 秘密鍵と公開鍵の導出 – アドレスの導出

アカウントの新規作成を行います。

秘密鍵と公開鍵のキーペアを生成し、そこからアドレスの導出を行います。

また、アドレスの導出にはテストネット or メインネットの属性が必要なようです。

アカウント新規作成

まったく当てにならない僕の自作コードはこちら。

    // 使用するホスト情報を取得する。
    var availableHost = await _getNetworkHost();

    try {
      // 新アカウントを生成する。
      var newAccount = await _generateNewAccount(availableHost);

      return newAccount;

    } catch (e) {
      debugPrint(e.toString());
      debugPrint(StackTrace.current.toString());

      throw CreateNewAccountFailedException();
    }

※全体像を載せられるようなコードになっていないため、一部分だけです。

色々ありましたが、実行結果としてはこんな感じです。

※一応秘密鍵は消しました。

秘密鍵の導出はさておいて(自分で用意していない部分でもあるので)、

導出された秘密鍵を使ってSymbolSDKから、アドレスを取得しようと思います。

そうすることで秘密鍵 → 公開鍵 → 生アドレス → アドレスの導出が同一であるということの検証とします。

※なお、raw addressと記載してますが、これをraw addressと呼んでいいかはわかりません。が、とりあえずこれで進めます。

なので比較のため、SymbolSDKでも同じことを実行します。

SymbolSDKでの実行結果。

アドレスが同一の”TCY7YADZLU6G34GK7VPGI3EUK2PMS2SDS25QMIA”となっているため、秘密鍵からアドレスの生成までは同一である、ということとしました。

アカウント情報の確認

既に(もう少し先まで)実行した関係上、データがきれいではないのですが。

実行テストを行う上で、alice、bobの秘密鍵は固定で使用していくこととします。(期間が空いた際に毎度やり直すと大変なため)

aliceのアカウント。

基本的にaliceのアカウントは上記で行います。

既にトランザクションを実行している関係上、変な値ですがxymは保持されています。

explorerで表示した残高。

こちらを取得します。

当てにならないコードはこちら。

_startProcessing();

                _accountWorker.getAccountAssetsFromAddress(Address("TAEKZ65QU3T4HK3JEEI2SOAHYNPYBUZ7BPC4YAA"))
                  .then((AccountMosaicProfile accountMosaicProfile) {
                    print("取得アカウント情報。");
                    print("address: ${accountMosaicProfile.account.address}");
                    print("public key: ${accountMosaicProfile.account.publicKey}");
                    
                    print("所持モザイク情報。");

                    var account = accountMosaicProfile.account;
                    var mosaicMap = accountMosaicProfile.mosaicInfos;

                    for (var mosaic in account.mosaics){

                      var mosaicDTO = mosaicMap[mosaic.id];
                      var mosaicAmount = "";
                      if (mosaicDTO != null && mosaicDTO.divisibility > 0){
                        var amountStr = mosaic.amount.toString();
                        var intVal = amountStr.substring(0, amountStr.length - mosaicDTO.divisibility);
                        var decVal = amountStr.substring(amountStr.length - mosaicDTO.divisibility);
                        mosaicAmount = "$intVal.$decVal";
                      }

                      print("id: ${mosaic.id} amount: $mosaicAmount");
                    }
                  })
                  .whenComplete(_finishProcessing);

実行結果はこちら。

※実行結果。

所持モザイク情報が取得できていることが確認できますね。

現場で使えるヒント

暗号化と署名

AliceとBobのみがわかるように、内容を暗号化できるみたいです。

なのでやっていきます。

当てにならないコードはこちら。


  // aliceのアカウントを作成する。
  var alice = await _accountWorker
.createNewAccountFromPrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178*****");

  // bobのアカウントを作成する。
  var bob = await _accountWorker
.createNewAccountFromPrivateKey("4A570201B9E491F0B1F4BDFE4C1AEED7D75582AAAA7771374FC88DC58C*****");

  var baseMessage = "super hogehoge";
  var message = PlainMessage.create(baseMessage);

  print("メッセージ[$message]を暗号化/復号化。");

  // メッセージの暗号化。
  var encryptedMessage = await alice.encryptMessage(message, bob.publicAccount);

  print("encrypted: ${encryptedMessage.payload}");

  // メッセージの復号化。
  // 念のため、payloadから暗号化メッセージクラスを別途インスタンス化し、復号化を行う。

  var newEncryptedMessage = await EncryptedMessage.create(encryptedMessage.payload, alice.publicAccount);
  var decryptedMessage = await bob.decryptMessage(newEncryptedMessage);

  print("decrypted: ${decryptedMessage.value}");

  print("署名と検証");

  // 復号化した内容にaliceが署名する。
  var aliceSignature = await alice.sign(decryptedMessage.toByte());

  print("aliceSignature: ${aliceSignature.value}");

  print("署名を行ったのがaliceなのかを検証する。");

  var isVerifiedAlice = await alice.publicAccount.verify(decryptedMessage, aliceSignature);
  var isVerifiedBob = await bob.publicAccount.verify(decryptedMessage, aliceSignature);

  print("alice: ${isVerifiedAlice ? "yes" : "no" }");
  print("bob: ${isVerifiedBob ? "yes" : "no"}");

“super hogehoge”をaliceがbobの公開アカウントに対して暗号化。

bobがaliceの公開アカウント情報に対して復号します。

公開アカウントってなに? っていうと「公開鍵/生アドレス/アドレス」のセットです。

※使用するのは公開鍵なんですけど。

復号した内容への署名を行い、それを検証することで、署名したのがaliceである証明とします。

実行結果はこちら。

こんな感じです。

encryptedが暗号化後のメッセージ、decryptedが復号したメッセージ。

aliceSignatureがaliceの署名です。

検証結果から、aliceが署名したことをうかがえます。

ここでの作りは僕がコーディングしただけのため、実際にSDKと同じなの? ということを確認する必要があります。

が、面倒でもあるため、SDKにて復号と署名の検証のみ行います。

ということで、復号は以下。

bobがaliceの公開情報を使用して復号した結果です。

同様に署名についても証明します。

aliceが署名していることを証明できました。

また自作のコードで暗号化 -> SDKにて復号(署名)も可能であることを確認しました。

アカウントの保管

アカウント項における最後。アカウントの保管についてです。

具体的には秘密鍵は大事な情報のため、ぱっと見ではわからないように暗号化して保管しようぜ! って話です。

使用しているaliceの秘密鍵379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178E*****を使用することとします。

ということでまずはSDKを使用して実行します。

こんな感じとなりました。

※QRコード表示はカットとします。

もちろん復号も可能です。

さて、これと同じようなものを実装しました。

※これ、厳密にはSDKに含まれているわけではない気がしますが。

そんなわけで当てにならないコードです。


  var alicePrivateKey = PrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178E*****");

  print("秘密鍵の暗号化");

  // パスフレーズ付きで暗号化。
  // invest_in_kishidaはPW。
  var signerQR = qr.QRCodeGenerator.createExportAccount(alicePrivateKey, 
    NetworkTypeEnum.testnet, 
    symbolTestnetGenerationHash, "invest_in_kishida");

  var schema = signerQR.getSchema();

  var outputObject = await schema.toObject(signerQR);
  var printOutputObject = jsonEncode(outputObject);

  print("暗号結果。");
  print(printOutputObject);

  {
    print("復号する。");
    var decryptedQR = await qr.QRCodeGenerator.fromJson(printOutputObject, password: "invest_in_kishida");

    decryptedQR as qr.AccountQR;

    print("復号結果。");
    print(jsonEncode(decryptedQR.privateKey));
  }

  {
    print("速習Symbolより。Edgeを使用して作成した暗号化メッセージpayloadをdecryptする。");
    print("symbol-qr-libraryと等価であることを検証。");

    var fromBrowser = '''
      {"v":3,"type":2,"network_id":152,"chain_id":"49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4","data":{"ciphertext":"aed4e1b553e02e5a38946db98ebf795bMOiGKwS3VtiQZnIvzBRQ21IQgyAM662tKUMJ3MiI3k/+YYrGC/JVbrvtkN1WfjmLOaBXYIg3dJw6FqdZ0mh80YiOl******....","salt":"d8b4f954c878fb6888cbff896ac1409eb861706d2fd933f4ede030c515148396"}}
    ''';

    var decryptedQR = await qr.QRCodeGenerator.fromJson(fromBrowser, password: "invest_in_kishida");

    decryptedQR as qr.AccountQR;

    print("復号結果。");
    print(jsonEncode(decryptedQR.privateKey));

    // 表示するQRコードの取得。
    _dispAccountQR = await decryptedQR.toQrImage(null);
  }

実行結果はこちら。

こんな感じとなりました。

秘密鍵の暗号化 -> 復号が可能であり、SDKで暗号化した秘密鍵の復号も可能であることから、おそらく、秘密鍵の暗号化、復号が等価である。かもしれない。というところになりました。

一応、QRを表示するのも用意したんですよ。

お見せできないんですけども。

そんなわけで第3章、アカウントについては以上です。

後半の感想編に続きます。

投稿者 和泉