5. モザイク

速習Symbolブロックチェーン4章、モザイク編です。

URLとしてはこちら

今回はテストネットの

http://sym-test-01.opening-line.jp/

をお借りしました。

オープニングライン社さんですね。

ブロックチェーンを使用した、ファイルを受け渡すJUGGLEが話題(2023/05/27時点)です。

テストネットを用意している方は素敵な方ではあるので、一言紹介していきたいんですよね。

ただ、このまま続けていくと(僕が誰か判別できる)テストネットノードはすぐに枯渇しちゃいそうです。

うちのノード使ってもいいよ、という方がいたら教えてください。(ここで一言紹介してもしょうがないかもしれませんけど。

それではやっていきます。

実践編

5.1 モザイク作成

モザイクの作成と、作成された状態を確認していきます。

まずコード。

// aliceのアカウントを作成する。
var alice = await Account.createFromPrivateKey(
  PrivateKey(alicePrivateKey), NetworkTypeEnum.testnet);

print("[送信元] alice address: ${alice.address}");

// モザイクの設定を指定する。
// 指定したフラグは有効状態とする。
var mosaicFlag = 
  MosaicFlagsEnum.supplyMutable +     // 供給量変更可能
  MosaicFlagsEnum.restrictable +      // 制限設定可能
  MosaicFlagsEnum.revocable;          // 発行者からの回収可能
// ここでは指定していないが、transferable(第三者への譲渡)も設定できるらしい。

var nonce = MosaicNonce.createRandom();

var mosaicId = MosaicId.createNew(nonce, alice.address);

// tx1
// モザイクの設定を行う。
var mosaicDefInfo = MosaicDefinitionInfoV1.create(alice.publicAccount, mosaicId, 
    BlockDuration(0), nonce, mosaicFlag, Int8(2));

// tx2
// モザイクの数量を増加する。(おそらく、設定だけでは0? そもそも設定だけでは受け取らない?)
var mosaicSupplyInfo = MosaicSupplyChangeInfoV1.create(alice.publicAccount, mosaicId, 
    Amount(100000000), MosaicSupplyChangeActionEnum.increase);

// トランザクションをまとめる。
var aggregate = [mosaicDefInfo, mosaicSupplyInfo];

// Transaction設定を行う。
var transSetting = TransactionSetting(
  signer: alice, 
  generationHash: "49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4", 
  networkType: NetworkTypeEnum.testnet, 
  deadline: Deadline.create(1667250467),
  fee:FeeMultiplier(100) );     // 手数料は100%。          

var mosaicWorker = MosaicWorker();

await mosaicWorker.sendTransaction(transSetting, aggregate);

基本的な設定はほぼ、速習Symbolと同じだと思っています。

作成するモザイクは

  • 供給量変更可能
  • 制限設定可能
  • 発行者からの回収可能

※これらを具体的に実行できる条件はわからないのですが。

nonceはランダム、

tx1は速習通りモザイクの設定、

tx2はモザイク数量の増加としています。

divisibilityに2を設定したため小数点以下は2桁まで、

数量は100000000としたため、発行量は1,000,000.00としました。百万。

細かいソースを追っても仕方ないのですが、トランザクション発行部分は下記。


var availableHost = await _getNetworkHost();

var aggCompleteInfo = AggregateCompleteInfoV2(availableHost.networkType, transInfos);

var aggregateTransaction = await AggregateTransaction.create(transSetting, aggCompleteInfo);

print("実行ペイロード");
print(aggregateTransaction.payload);
print("トランザクションハッシュ");
print(aggregateTransaction.transactionHash.value);   

var transRouteHttp = TransactionRoutesHttp(availableHost.networkHost);

// トランザクションをノードに通知する。
var result = await transRouteHttp.announceNewTransaction(aggregateTransaction.payload.toUpperCase());

print("トランザクションのアナウンスに成功した。");
print(result.message);

print("トランザクションの承認ステータスを確認する。");
var transStateRouteHttp = TransactionStateRoutesHttp(availableHost.networkHost);

var processing = true;

while(processing){
  await Future.delayed(const Duration(seconds: 1));

  // 結果の確認を行う。
  var resultState = await transStateRouteHttp.getStatus(aggregateTransaction.transactionHash);

  // 未認証の場合のみ、終わるまで継続。
  switch (resultState.group){
    case TransactionGroupEnum.confirmed:
      print("トランザクションが承認されました。やったぜ!");
      processing = false;
      break;
    case TransactionGroupEnum.unconfirmed:
      print("トランザクションは未承認です。");
      break;
    case TransactionGroupEnum.failed:
      print("トランザクションにエラーがありました。");
      if (resultState.code != null) print(resultState.code!.value);
      return;
    default:
      print("パーシャル!!! パーシャル……?");
      return;
  }
}

print("承認後はTransactionRouteから承認トランザクションの内容を取得することが可能になっている。");

var confirmedInfo = await transRouteHttp.getConfirmedInformation(aggregateTransaction.transactionHash.value);

print("取得結果。");
print(jsonEncode(confirmedInfo.baseMap));

(前の章で実施した)アグリゲートコンプリートトランザクションで発行し、その承認結果を確認します。

それでは実行します。

こんな感じです。少なくともトランザクションの承認は行われたようです。

これを、念のためにウォレットでも確認してみます。

aliceのウォレットのアセット欄に、symbol.xym以外のトークンが表示されていますね。

見づらいかもしれませんが、先ほどの一部を抜粋すると、

mosaicIdとしてある50E29F813AA1B901ということみたいです。

これでとりあえず、モザイクの生成は行われているようです。

また、数も1,000,000となっているため100万とでています。

たぶんこれで発行数の変更も実行されているかな、と思います。

5.3 現場で使えるヒント

所有証明について書かれています。ブロックチェーンがあると所有証明も可能であるとともに、NFTの発行方法などが記載されています。

NFT

というわけで、速習Symbolに倣ってNFTを発行したいと思います。

といっても、複製や数量変更できない、唯一のトークンを発行するということでしかないようですが。

以下のようにコードを設定しました。

// aliceのアカウントを作成する。
var alice = await Account.createFromPrivateKey(
  PrivateKey(alicePrivateKey), NetworkTypeEnum.testnet);

print("[送信元] alice address: ${alice.address}");

// モザイクの設定を指定する。
// 指定したフラグは有効状態となる。
var mosaicFlag = 
  MosaicFlagsEnum.restrictable +      // 制限設定可能
  MosaicFlagsEnum.revocable;          // 発行者からの回収可能
// MosaicFlagsEnum.supplyMutable      // 供給量変更可能は指定しない。
// MosaicFlagsEnum.transferable       // 第三者への譲渡不可。

var nonce = MosaicNonce.createRandom();
// var nonce = MosaicNonce.create(Uint8List.fromList([210,53,66,166]));

var mosaicId = MosaicId.createNew(nonce, alice.address);

// tx1
// モザイクの設定を行う。
// 可分性0、有効期限無制限。
var mosaicDefInfo = MosaicDefinitionInfoV1.create(alice.publicAccount, mosaicId, 
    BlockDuration(0), nonce, mosaicFlag, Int8(0));

// tx2
// モザイクの数量を増加する。
// 数量は1。可分性が0のため、1は1。
var mosaicChangeInfo = MosaicSupplyChangeInfoV1.create(alice.publicAccount, mosaicId, 
    Amount(1), MosaicSupplyChangeActionEnum.increase);

// tx3
// NFTデータ
var transInfo = TransferInfoV1.create(alice.publicAccount, alice.publicAccount.address, [],
  PlainMessage.create("Hello Symbol!!!"));

// トランザクションをまとめる。
var aggregate = [mosaicDefInfo, mosaicChangeInfo, transInfo];

// Transaction設定を行う。
var transSetting = TransactionSetting(
  signer: alice, 
  generationHash: "49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4", 
  networkType: NetworkTypeEnum.testnet, 
  deadline: Deadline.create(1667250467),
  fee:FeeMultiplier(100) );     // 手数料は100%。          

var mosaicWorker = MosaicWorker();

// トランザクションの通知。
await mosaicWorker.sendTransaction(transSetting, aggregate);

mosaicWorkerについては省略します。

同じように? 設定したつもりのため、これで実行してみます。

実行されました。ウォレットで確認すると先ほどの通り、数量は1となっています。

そして生成と同じブロックに投入したメッセージ=NFTのデータとすることで、データ付きのNFTにすることができる、ということのようです。うーむ、なるほど。。。

回収可能なポイント

先ほどの唯一発行したトークンをbobへと送信しておきます。

以下、bobのウォレット状態です。

365~~が先ほどのNFTですね。たぶん。

さて、こいつを回収できるらしいので、aliceが回収を行います。

さくっとこんな感じ。

// aliceのアカウントを作成する。
var alice = await Account.createFromPrivateKey(
  PrivateKey(alicePrivateKey), NetworkTypeEnum.testnet);

print("[送信元] alice address: ${alice.address}");

// 回収トランザクション。対象アドレスはbob。数量は1。
var revocationTx = MosaicSupplyRevocationInfoV1.create(alice.publicAccount, 
  Address("TBZ7AEGPLAP2YCVPSS6CJR27EVOQ7M4AXULACRY"), 
  Mosaic(id: MosaicId("365F2BE795816BAE"), amount: Amount(1)));

// Transaction設定を行う。
var transSetting = TransactionSetting(
  signer: alice, 
  generationHash: "49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4", 
  networkType: NetworkTypeEnum.testnet, 
  deadline: Deadline.create(1667250467),
  fee:FeeMultiplier(100) );     // 手数料は100%。          

var mosaicWorker = MosaicWorker();

// トランザクションの通知。
await mosaicWorker.sendTransaction(transSetting, revocationTx);

トランザクションの通知周りはもう省略していきます。

特に変更がなければ、アグリゲートもベーシックも通知 -> 承認を探す、取得の流れのため。

実行します。

実行完了は特に実感がなかったのですが、bobのウォレットを見るときっちりモザイクが消えていました。

Explorerで見るとちゃんと、回収したトランザクションは掲載されていました。

なお、このトークンは消滅させるわけではなくあくまでも回収のため、

aliceの元へと戻っていました。

モザイクの実践編は以上です。

感想編

おお、モザイク作成って手ごろなんだなぁ。ってところです。名前を付けるのはまだ先なので、MosaicIdのみでの表示ですが。

モザイク=あらゆるものの動きや状態を示すことができるのだろうなぁ、と実感を持ちました。

別に本筋とは関係ないのですが、MosaicIdは先頭ビットが0、NamespaceIdは先頭ビットが0にするというメモが僕の手元にありました。どこで見たのか覚えてないのですが。

ビット演算なんてそうそう使う機会ない? もんな。。。

モザイクの使い方としては、会員証・権利のようなものや、ポイントのようなもの。

アドレス=場所と紐づけたらトレーサビリティにも使用できるでしょうし、カンバン的にも使用できるかもしれないですね。あとは在庫管理とか?

これをパブリックでやるかどうかという話はあるかと思いますが、とても奥が深そうだなと思います。

以上です。

4. トランザクション

速習Symbolブロックチェーン4章、トランザクション編です。

URLとしてはこちら

……コミュニティサイトのほうにしたほうがいいのかな?

今回から、実施編と感想編を分けないことにしました。

書くのが面倒だからね。

今回はテストネットの

http://mikun-testnet.tk/

をお借りしました。

Symbol/NEMでは知らない人がいない?

@mikunNEM さんのテストネットノードです。ありがとうございます。

もちろんメインネットのノード運営も行っている方です。

それではやっていきます。

前回のアカウント編ではRawAddressの意味について認識の違いがありました。
※いずれ直すかも。

実施編

トランザクションのライフサイクル

トランザクションを作成してから、ファイナライズまでの流れが掲載されています。

説明を図に起こした感じです。

クライアント側の処理としてはトランザクション作成から署名、アナウンスするところまで。

そこから先はノード側の仕事、という話です。

なお、ファイナライズを迎えるまではロールバックする可能性がありますよ、とのことでした。確率上、どの程度おきるかはわからないですが。

また、1手続きは1トランザクションとして扱われますが、最終的なデータとしてはブロック単位で同期されるとのことでした。ブロックには複数のトランザクションを取り込まれる(可能性がある)。。ということでいいんでしょうかね。

4.2 トランザクション作成から、4.4 確認まで。

基本的なトランザクションとして、aliceからbobへと転送トランザクションを作成し、アナウンスするということです。

トランザクションの作成には有効期限・最大手数料が必要であること。転送トランザクションにはメッセージを付与することが可能であることが掲載されています。

具体的には先ほどの図のトランザクション作成から、承認済みトランザクションとなるまでの実行方法が内容が掲載されています。

すみません、それに合わせて記載するのがちょっと難しかったため、

相変わらず伝わらないソースでも掲載しておきます。

// aliceのアカウントを作成する。(送信元)
var alice = await Account.createFromPrivateKey(
  PrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178E*****"), NetworkTypeEnum.testnet);

// bobのアカウントを作成する。
var bob = await Account.createFromPrivateKey(
  PrivateKey("4A570201B9E491F0B1F4BDFE4C1AEED7D75582AAAA7771374FC88DC58C3*****"), NetworkTypeEnum.testnet);

print("[送信元] alice address: ${alice.address}");
print("[送信先] bob address: ${bob.address}");

// 送信するモザイク情報を決定する。
List<Mosaic> mosaics = [];
mosaics.add(Mosaic(id: MosaicId("72C0212E67A08BCE"), amount: Amount("100")));

// 送信するメッセージを構築する。
var message = PlainMessage.create("Hello Symbol-chan.");
var crypt = await message.crypt(alice.privateKey, bob.publicAccount);

// 送信内容を構築した。
var transInfo = TransferInfoV1.create(alice.publicAccount, bob.address, mosaics, crypt);

var deadline = Deadline.create(1667250467);     // testnetのepochAdjustment

var transSetting = TransactionSetting(
  signer: alice, 
  generationHash: "49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4", 
  networkType: NetworkTypeEnum.testnet, 
  deadline: deadline,
  fee:FeeMultiplier(100) );     // 手数料は100%。

var transWorker = TransactionWorker();

await transWorker.transferTransaction(transSetting, transInfo);

見づらいかもしれませんが、
ざっくりいえばテストネットでaliceからbobに100だけモザイク(テストネットXYM)を送信します。実際は100といっても0.000000まで有効なため、0.0001になるのですが。

メッセージとしては”Hello Symbol-chan.”を暗号化して送信すること、手数料は100%であることを示しています。

最後のtransWorker.transferTransactionは以下のような感じです。

  /// 転送トランザクションを実行する。
  Future<void> transferTransaction(TransactionSetting transSetting, TransferInfoV1 transInfo) async {

    var basicTransaction = await BasicTransaction.create(transSetting, transInfo);

    print("実行ペイロード");
    print(basicTransaction.payload);
    print("トランザクションハッシュ");
    print(basicTransaction.transactionHash.value);

    var availableHost = await _getNetworkHost();

    var transRouteHttp = TransactionRoutesHttp(availableHost.networkHost);

    // トランザクションをノードに通知する。
    var result = await transRouteHttp.announceNewTransaction(basicTransaction.payload);

    print("トランザクションのアナウンスに成功した。");
    print(result.message);

    print("トランザクションの承認ステータスを確認する。");
    var transStateRouteHttp = TransactionStateRoutesHttp(availableHost.networkHost);

    var processing = true;

    while(processing){
      await Future.delayed(const Duration(seconds: 1));

      // 結果の確認を行う。
      var resultState = await transStateRouteHttp.getStatus(basicTransaction.transactionHash);

      // 未認証の場合のみ、終わるまで継続。
      switch (resultState.group){
        case TransactionGroupEnum.confirmed:
          print("トランザクションが承認されました。やったぜ!");
          processing = false;
          break;
        case TransactionGroupEnum.unconfirmed:
          print("トランザクションは未承認です。");
          break;
        case TransactionGroupEnum.failed:
          print("トランザクションにエラーがありました。");
          if (resultState.code != null) print(resultState.code!.value);
          return;
        default:
          print("パーシャル!!! パーシャル……?");
          return;
      }
    }

    print("承認後はTransactionRouteから承認トランザクションの内容を取得することが可能になっている。");

    var confirmedInfo = await transRouteHttp.getConfirmedInformation(basicTransaction.transactionHash.value);

    print("取得結果。");
    print(jsonEncode(confirmedInfo.baseMap));

  }

別にtransferTransactionなんて名前にする必要はなかったんですけど。

SymbolSDKの作り方を半分無視しているので、こんなよくわからない感じになっています。

上のコードでトランザクション内容を作っています。

下のコードでBasicTransaction(なにかというとAggregateTransactionでない単一のTransaction)とし、それをノードにアナウンスし、承認待ちをしています。

なお、エラーの場合は考えてません。(エラーの場合に苦労した感)

そして実行した結果は以下のようになりました。

問題なく、トランザクションがアナウンスされ、承認されています。

なお、bobのウォレットから、トランザクション内容の確認は以下(メッセージの復号済み)

メッセージとモザイク数量が指定した通りに出力されていることが確認できます。

4.5 トランザクション履歴

トランザクション履歴を一覧で取得する。

アカウント情報の作成を行い、トランザクション履歴取得メソッド(searchConfirmedTransactions)を呼び出す。

// aliceのアカウントを作成する。(送信元)
var alice = await Account.createFromPrivateKey(
  PrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178******"), NetworkTypeEnum.testnet);

print("[送信元] alice address: ${alice.address}");

var transWorker = TransactionWorker();

await transWorker.searchConfirmedTransactions(alice.address);

searchConfirmedTransactionsの内容は以下の感じで。

var availableHost = await _getNetworkHost();

var transRouteHttp = TransactionRoutesHttp(availableHost.networkHost);

// 取得する条件を設定する。
var criteria = SearchConfirmedTransactionsCriteria();

criteria.address = targetAddress;
criteria.embedded = true;
criteria.order = OrderEnum.desc;

// 承認済みトランザクションを取得する。
var result = await transRouteHttp.searchConfirmed(criteria);

print("トランザクション履歴を表示する");
for (var transInfo in result.data){
  print(jsonEncode(transInfo.baseMap));     
}

取得先はどのAPIにすればいいのかわからなかったので、とりあえずTransatcion RoutesのSearch confirmed transactionとしました。

条件としてアドレス、embeddedをtrue、orderをdescにしています。

※成功するまでにいろいろと何度も実行したため、最新トランザクションがわからなくなるのでdescとしました。

取得したresultにはページ番号なども取得できているのですが、内容のdata部分だけで出力しています。

取得結果はこんな感じになりました。複数のトランザクション履歴が取得できます。

実は、前の項目で送信した転送トランザクション(448486ブロック)後に、別のトランザクションを発行してしまいまして、、、。

前の項目で送信したのは2件目になっていました。

よって2件目を抜粋します。

{
	"meta": {
		"height": "448486",
		"hash": "96C0FBA669287EFB7395EFE9AC22D061DC8A5E2154255DD72815017868C5A750",
		"merkleComponentHash": "96C0FBA669287EFB7395EFE9AC22D061DC8A5E2154255DD72815017868C5A750",
		"index": 0,
		"timestamp": "16387287287",
		"feeMultiplier": 100
	},
	"transaction": {
		"size": 269,
		"signature": "47D6B206B5E0AFE4AA97D8168C90B30A45E8B6904264056D84866F9B596FACFC084C27DB517B95F74A8CF28C714028ED2C94CD5276EAC9832960DB050466BC01",
		"signerPublicKey": "67626D0C01D6E24F7F237072C422D81A130E3A429A6C9CFFF45B5CFCBBDC9A28",
		"version": 1,
		"network": 152,
		"type": 16724,
		"maxFee": "26900",
		"deadline": "16394482000",
		"recipientAddress": "9873F010CF581FAC0AAF94BC24C75F255D0FB380BD160147",
		"message": "013334344138353233323339463146353136334638344246463745344533304233333139333333343835393538364632333937303646393444303637433130424146393633454330333042373341454239354336313835303437314543",
		"mosaics": [
			{
				"id": "72C0212E67A08BCE",
				"amount": "100"
			}
		]
	},
	"id": "645A45FACF72001C11039B4F"
}

メッセージは暗号化されていますが、モザイクを100だけ送信しています。

transactionの内容を見ると、

networkが152(testnet)

typeが16724(transfer)

recipientAddress(転送先アドレス)、signerPublickey(署名者公開鍵)、

同様にmosaicsの中にモザイクidとamount(数量)の指定がしてあります。

どのテストネットであること、手続きが転送であること、転送先とモザイクIDの指定というのが見て取ることができます。

なお、一番上のトランザクションは、次の項目で実行したものです。

transactionのtypeが16705(aggregate complete)を示しているため、アグリゲートコンプリートトランザクションであることを示しています。

じゃあ次に行きましょう。

4.6 アグリゲートトランザクション

複数のトランザクションをまとめることができるのがアグリゲートトランザクションのようです。

速習Symbolではaliceがbob、carolに転送トランザクションを送信していましたが、まぁbobに2度送るのも同じだよね? ということで、aliceがbobに2度送る形にしました。

謎のコードとしては以下。

// aliceのアカウントを作成する。(送信元)
var alice = await Account.createFromPrivateKey(
  PrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178E*****"), NetworkTypeEnum.testnet);

// bobのアカウントを作成する。
var bob = await Account.createFromPrivateKey(
  PrivateKey("4A570201B9E491F0B1F4BDFE4C1AEED7D75582AAAA7771374FC88DC58C3*****"), NetworkTypeEnum.testnet);

print("[送信元] alice address: ${alice.address}");
print("[送信先] bob address: ${bob.address}");

// 作りが悪く、名前が被ってしまっているらしい。
List<trans.TransactionInfo> transInfos = [];

// TransferTrans 1.
{

  // 送信するモザイク情報を決定する。
  List<Mosaic> mosaics = [];
  mosaics.add(Mosaic(id: MosaicId("72C0212E67A08BCE"), amount: Amount("1")));

  // 送信するメッセージを構築する。
  var message = PlainMessage.create("tx1");

  // 送信内容を構築した。
  transInfos.add(TransferInfoV1.create(alice.publicAccount, bob.address, mosaics, message));

}

// TransferTrans 2.
{

  // 送信するモザイク情報を決定する。
  List<Mosaic> mosaics = [];
  mosaics.add(Mosaic(id: MosaicId("72C0212E67A08BCE"), amount: Amount("2")));

  // 送信するメッセージを構築する。
  var message = PlainMessage.create("tx2");

  // 送信内容を構築した。
  transInfos.add(TransferInfoV1.create(alice.publicAccount, bob.address, mosaics, message));

}

// Transaction設定。

var transSetting = TransactionSetting(
  signer: alice, 
  generationHash: "49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4", 
  networkType: NetworkTypeEnum.testnet, 
  deadline: Deadline.create(1667250467),
  fee:FeeMultiplier(100) );     // 手数料は100%。          

var transWorker = TransactionWorker();

await transWorker.aggregateTransferTransaction(transSetting, transInfos);

※V1ってつけてインスタンス化しているのがダサいので、いつか変えます。たぶん。

aliceがbobに2回転送トランザクションを送信します。

それぞれ、「メッセージに”tx1″、数量として1」「メッセージに”tx2″、数量として2」となっています。

トランザクションを送信している、aggregateTransferTransactionについては以下。


var aggregateComplete = AggregateCompleteInfoV2(NetworkTypeEnum.testnet, transInfos);

var aggregateTransaction = await AggregateTransaction.create(transSetting, aggregateComplete);

print("実行ペイロード");
print(aggregateTransaction.payload);
print("トランザクションハッシュ");
print(aggregateTransaction.transactionHash.value);

var availableHost = await _getNetworkHost();

var transRouteHttp = TransactionRoutesHttp(availableHost.networkHost);

// トランザクションをノードに通知する。
var result = await transRouteHttp.announceNewTransaction(aggregateTransaction.payload.toUpperCase());

print("トランザクションのアナウンスに成功した。");
print(result.message);

print("トランザクションの承認ステータスを確認する。");
var transStateRouteHttp = TransactionStateRoutesHttp(availableHost.networkHost);

var processing = true;

while(processing){
  await Future.delayed(const Duration(seconds: 1));

  // 結果の確認を行う。
  var resultState = await transStateRouteHttp.getStatus(aggregateTransaction.transactionHash);

  // 未認証の場合のみ、終わるまで継続。
  switch (resultState.group){
    case TransactionGroupEnum.confirmed:
      print("トランザクションが承認されました。やったぜ!");
      processing = false;
      break;
    case TransactionGroupEnum.unconfirmed:
      print("トランザクションは未承認です。");
      break;
    case TransactionGroupEnum.failed:
      print("トランザクションにエラーがありました。");
      if (resultState.code != null) print(resultState.code!.value);
      return;
    default:
      print("パーシャル!!! パーシャル……?");
      return;
  }
}

print("承認後はTransactionRouteから承認トランザクションの内容を取得することが可能になっている。");

var confirmedInfo = await transRouteHttp.getConfirmedInformation(aggregateTransaction.transactionHash.value);

print("取得結果。");
print(jsonEncode(confirmedInfo.baseMap));

実行する。

結果は以下。

{
	"meta": {
		"height": "465070",
		"hash": "DA07751937046B70B5AA487AD76A1EDFAC8B3BA7D0E5A3C00BFC5325D22BD1E5",
		"merkleComponentHash": "DA07751937046B70B5AA487AD76A1EDFAC8B3BA7D0E5A3C00BFC5325D22BD1E5",
		"index": 0,
		"timestamp": "16903106860",
		"feeMultiplier": 100
	},
	"transaction": {
		"size": 376,
		"signature": "A52BB29259A1F67F45E9035C390F42025A4187FDF299C527893C6467B757185AFB4312FC574944ECCEB78A9D68AB5F9B33B13241CD0C511DD8A26915D045250F",
		"signerPublicKey": "67626D0C01D6E24F7F237072C422D81A130E3A429A6C9CFFF45B5CFCBBDC9A28",
		"version": 2,
		"network": 152,
		"type": 16705,
		"maxFee": "37600",
		"deadline": "16910295000",
		"transactionsHash": "0E6D4810A4A464142A1CB2A13CCD426BAF529AB87AC2E57FF10637F385199B3E",
		"cosignatures": [],
		"transactions": [
			{
				"meta": {
					"height": "465070",
					"aggregateHash": "DA07751937046B70B5AA487AD76A1EDFAC8B3BA7D0E5A3C00BFC5325D22BD1E5",
					"aggregateId": "646224E6CF72001C11046A39",
					"index": 0,
					"timestamp": "16903106860",
					"feeMultiplier": 100
				},
				"transaction": {
					"signerPublicKey": "67626D0C01D6E24F7F237072C422D81A130E3A429A6C9CFFF45B5CFCBBDC9A28",
					"version": 1,
					"network": 152,
					"type": 16724,
					"recipientAddress": "9873F010CF581FAC0AAF94BC24C75F255D0FB380BD160147",
					"message": "00747831",
					"mosaics": [
						{
							"id": "72C0212E67A08BCE",
							"amount": "1"
						}
					]
				},
				"id": "646224E6CF72001C11046A3A"
			},
			{
				"meta": {
					"height": "465070",
					"aggregateHash": "DA07751937046B70B5AA487AD76A1EDFAC8B3BA7D0E5A3C00BFC5325D22BD1E5",
					"aggregateId": "646224E6CF72001C11046A39",
					"index": 1,
					"timestamp": "16903106860",
					"feeMultiplier": 100
				},
				"transaction": {
					"signerPublicKey": "67626D0C01D6E24F7F237072C422D81A130E3A429A6C9CFFF45B5CFCBBDC9A28",
					"version": 1,
					"network": 152,
					"type": 16724,
					"recipientAddress": "9873F010CF581FAC0AAF94BC24C75F255D0FB380BD160147",
					"message": "00747832",
					"mosaics": [
						{
							"id": "72C0212E67A08BCE",
							"amount": "2"
						}
					]
				},
				"id": "646224E6CF72001C11046A3B"
			}
		]
	},
	"id": "646224E6CF72001C11046A39"
}

transactionsの中に2つのトランザクションが確認できます。数量(amount)も1と2となっているのが確認できるかなと思います。

4.7 現場で使えるヒント

データのハッシュをブロックチェーンに刻んでおくことで、「その時に存在した証明」となる、ということのようです。

遡って改ざんや削除できないブロックチェーンだからこその利用法の一つなのでしょう。

ハッシュ値についてはそのまま速習symbolの値をコピーしてきました。

それを分割してアグリゲートトランザクションに載せます。

謎コードは以下。

// aliceのアカウントを作成する。(送信元)
var alice = await Account.createFromPrivateKey(
  PrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178E*****"), NetworkTypeEnum.testnet);

// bobのアカウントを作成する。
var bob = await Account.createFromPrivateKey(
  PrivateKey("4A570201B9E491F0B1F4BDFE4C1AEED7D75582AAAA7771374FC88DC58C3*****"), NetworkTypeEnum.testnet);

print("[送信元] alice address: ${alice.address}");
print("[送信先] bob address: ${bob.address}");

print("アグリゲートトランザクションは1024バイトで分割する");

var bigdata = 'C00200000000000093B0B985101C1BDD1BC2BF30D72F35E34265B3F381ECA464733E147A4F0A6B9353547E2E08189EF37E50D271BEB5F09B81CE5816BB34A153D2268520AF630A0A0E5C72B0D5946C1EFEE7E5317C5985F106B739BB0BC07E4F9A288417B3CD6D26000000000198414140770200000000002A769FB40000000076B455CFAE2CCDA9C282BF8556D3E9C9C0DE18B0CBE6660ACCF86EB54AC51B33B001000000000000DB000000000000000E5C72B0D5946C1EFEE7E5317C5985F106B739BB0BC07E4F9A288417B3CD6D26000000000198544198205C1A4CE06C45B3A896B1B2360E03633B9F36BF7F22338B000000000000000066653465353435393833444430383935303645394533424446434235313637433046394232384135344536463032413837364535303734423641303337414643414233303344383841303630353343353345354235413835323835443639434132364235343233343032364244444331443133343139464435353438323930334242453038423832304100000000006800000000000000B2D4FD84B2B63A96AA37C35FC6E0A2341CEC1FD19C8FFC8D93CCCA2B028D1E9D000000000198444198205C1A4CE06C45B3A896B1B2360E03633B9F36BF7F2233BC089179EBBE01A81400140035383435344434373631364336433635373237396800000000000000B2D4FD84B2B63A96AA37C35FC6E0A2341CEC1FD19C8FFC8D93CCCA2B028D1E9D000000000198444198205C1A4CE06C45B3A896B1B2360E03633B9F36BF7F223345ECB996EDDB9BEB1400140035383435344434373631364336433635373237390000000000000000B2D4FD84B2B63A96AA37C35FC6E0A2341CEC1FD19C8FFC8D93CCCA2B028D1E9D5A71EBA9C924EFA146897BE6C9BB3DACEFA26A07D687AC4A83C9B03087640E2D1DDAE952E9DDBC33312E2C8D021B4CC0435852C0756B1EBD983FCE221A981D02';

// 作りが悪く、名前が被ってしまっているらしい。
List<trans.TransactionInfo> transInfos = [];

// TransferTrans.
{

  // 送信するモザイク情報を決定する。
  List<Mosaic> mosaics = [];
  mosaics.add(Mosaic(id: MosaicId("72C0212E67A08BCE"), amount: Amount("1")));

for (var i = 0; i < bigdata.length / 1023; i++){

  // 1023で分割する。
  var startIdx = i * 1023;
  int? endIdx = startIdx + 1023;

  if (bigdata.length < endIdx) endIdx = null;

  // 送信するメッセージを構築する。
  var message = PlainMessage.create(bigdata.substring(startIdx, endIdx));

  // 送信内容を構築した。
  transInfos.add(TransferInfoV1.create(alice.publicAccount, bob.address, mosaics, message));
}

}

// Transaction設定。
var transSetting = TransactionSetting(
  signer: alice, 
  generationHash: "49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4", 
  networkType: NetworkTypeEnum.testnet, 
  deadline: Deadline.create(1667250467),
  fee:FeeMultiplier(100) );     // 手数料は100%。

var transWorker = TransactionWorker();

await transWorker.aggregateTransferTransaction(transSetting, transInfos);

コードはほぼ前回のを流用し、bobに複数回送ります。

aggregateTransferTransactionについては割愛。

結果は以下です。

分割して送信するだけみたいですね。

なお、ウォレットで確認した内容。

たぶん、合っていると思う。

以上です。感想編に続きます。

感想編

トランザクションのライフサイクルについて。

ロールバックの可能性については、ブロックチェーンを追いかけていればそんな話を聞いたことがあるかな、というところです。

NEM時代でも取引所から送金するのに必要な承認回数などもありました。Symbolになってからファイナライズ機構があるため、それを指標にしてもいいかもしれませんが。

※ただし、ファイナライズにはそれなりに時間がかかる。

送信したトランザクションが承認するか判定する基準は複数あって、

  • チェーン(ノード)が承認する
    トランザクションの承認が覆る可能性はあるが、一応、承認されたとすることができる。(判定が早い)
  • 承認され、対象ブロックをファイナライズが経過する
    承認されたトランザクションが覆る可能性はほぼない。

このあたりが「送信したトランザクションが正常に処理された」と認識していい指標かな? と思いました。

現状ではトランザクションがスカスカ? らしく、ロールバックされてもトランザクションは再取り込みされるようなので、若干時間/ブロックがずれるかもしれませんが、結果的にはきっと承認されるんでしょう。

トランザクションが詰まるようになってきたらファイナライズやロールバックを気にしないといけない、というところでしょうか。

アグリゲートトランザクションについて。

複数のトランザクションをまとめて実行できるというのは強力な気がします。

今回はalice -> bobを2つだったため、aliceの署名のみで済みました。
たぶんこれが、複雑になると変わっていくのだろうなぁと思います。

複数のトランザクションをひとつにまとめられることで、

  • aliceから複数のアドレスへ、複数のモザイクを一度に送信する(手数料を抑えられる?)
  • aliceからbobへ、bobからaliceへのモザイクの送信を一度で行える
  • bob、carol、アベサダオ、がそれぞれ10XYMをaliceへと送信する(誰か一人でも否認したら実行されない)
  • 現場で使えるヒントにあるように、ある程度のデータサイズであれば複数のトランザクションにまとめることで格納できる

なんてことができるのでしょうね。

複数のトランザクションを1トランザクションで実行できることで、
「複数の手続きを行う必要がある際に途中で失敗した場合、それ以降を再度実施するorそれ以前を取りやめる」
なんて選択を考える必要がなくなるのは素敵なことだなぁって。

その他。

速習Symbolは直接的には関係ないのですが、疲れました。

ベーシックトランザクションはうまくいくのに、アグリゲートトランザクションが全然うまく投げれなくてですね。SDKのペイロードと睨めっこをしていました。これ、つよつよエンジニアなんかだと簡単なのかもしれませんが。。。

元々ベーシック/アグリゲートの各トランザクションのペイロードイメージは、

目指せ北海道さんのSymbol解体新書を眺めてイメージを固めたんですよ。

その後、githubにあるSymbolSDKのcatbufferのschemaを見て「こんな……感じか……?」って手で作っていたので。

あんまりよく理解せずにやるものじゃないですね。大体ペイロードサイズと手数料計算あたりが怪しい感じでした。

一番致命的なのはアグリゲートのペイロード構築時に、[]と{}を間違えて記述している箇所でjoinするというしょうもないことをしておりまして。

どれだけやってもSDKのペイロードサイズと一致しない、、、、コードもあっているように見えるのに、、、、なぜ、、、、?

とかなってました。慣れないことをするもんじゃないね。

それでは次回はモザイク編で会いましょう。
(これ、終わるのか……?)

3. アカウント[感想編]

速習Symbolブロックチェーン体験記「とろ速Symbol」の3章、アカウントについての感想編です。

こちらでは実装ではなく、「ああ、こういうことなのかな?」といった学んだと思う部分をまとめていこうと思います。まぁ、全然わかってないんだけど。

実施編はこちら。

さて。

アカウント生成・アカウントへの送信

アカウント構成はざっくり言えば下記のイメージ。

ある意味、アカウント生成とは秘密鍵(PrivateKey)を決めること。
秘密鍵(PrivateKey)から公開鍵(PublicKey)、生アドレス(RawAddress)、アドレス(Address)の導出が可能になる。

よって、バレてはまずいのは秘密鍵。
また、秘密鍵はネットワークに晒す必要もないため、誰かに教える必要はない

上記の構成で公開アカウントで囲まれた内容がある。

これについては外部に公開可能な部分であり、SymbolノードのREST APIから直接取得できる部分でもある。

僕らが普段送信を行う対象はアドレスとなる。

また、アカウント生成時点ではネットワークが不要である。
つまり、ネットワークはアカウントが作成されたことを知らない。

つまり、秘密鍵が生成されたことをブロックチェーンは知らないため、秘密鍵から生成される公開鍵も不明であるし、アドレスも不明である。

アカウント(秘密鍵)が存在することを知らせるためには、作成したアカウントからトランザクションを発生させる必要がある。

トランザクション実行には署名が必要です。(これ、次回の話?)

署名が必要であるからには、秘密鍵が存在する。

よって、トランザクション実行により、対象のアカウントに対する公開鍵以降がチェーンに刻まれる。というお話。

なお、その場合、なぜかフォーセットから、(チェーンが認識していない)aliceアカウントに送金できるという部分がある。

おそらくこんな感じで、送信されている。

2. の秘密鍵の存在は不明~~はその通りで、よって、アドレスを間違えるとGOXの可能性がある。
(実際に導出済みのアドレスとは限らないため、本当に誰も所有していないところに飛ぶ可能性もあると思う)

そのため、アプリケーションから送金などを行う場合、通常であれば「公開鍵がブロックチェーンに登録されていること」を確認したほうが安全なのだろう。
(おそらく、あらゆる場面で)

アカウント情報の確認

先ほども書いたけれど、アカウントを作成しただけではSymbolブロックチェーンはアカウントの存在を認識していない。

また導出の順の通り、アドレスについては導出の最後になる。
そのため、アドレスから公開鍵は逆引きとなるため、不明な値(ゼロフィル)となる。

よってトランザクション実行時に公開鍵を使用するため、登録される。

うん、他には特にないですね。

現場で使えるヒント

暗号化と署名。

ここ、凄い困りました。全然暗号とかわかんないし。
AES-GCM形式っていうのはわかった。
※ざっくりいえば(aliceの秘密鍵+bobの公開鍵) = (aliceの公開鍵+bobの秘密鍵)となる鍵を作って、それで暗号化するということのようだった。

わからないのでFlutterのパッケージを組み合わせて試していたのですが、全然うまくいかなかったんですよ。これ、あれですね、わかったことではなくて愚痴ですね。

もう覚えてないのであれなんですが、ハッシュの計算? かなんかが、よく使われているのとは別らしいですね。(内容はうろ覚えですが、よく提供されているのが128bitで、こちらでは256bitだったかな?)

それについて言及しているブログを目にしたため、「あ、これパッケージじゃだめなのかも」と認識しました。
(ブログではSymbolではなく、NEMについての言及でしたけれど)

結果として中身は全然わからないのですが、tweetnacl? というソースを写経する感じに至りました。ほんまこいつ。。。。

そんなわけで、暗号化についてはこんな感じで理解しました。

これでbobはaliceのメッセージを受け取ることができるようです。

公開鍵の交換はローカルでもいいですが、トランザクションさえ発生させてしまえば公開鍵がチェーンから取得できるので、「トランザクションを発行したことがあるアカウント」であれば、秘密のメッセージを送りあうことができます。

また、他人から見た場合でも安全です。

また、「本当にaliceが送信したのか?」という疑わしい点を解消するため、署名の仕組みがあるとのことでした。

今回、速習Symbolではalice → bobへの送信したメッセージに対して、aliceが署名を行いました。

その署名内容を検証することでaliceが送ったということを証明できるようです。

なお、速習Symbolでは以下の感じです。

これ、本当はトランザクションの証明についての話でもあると思うんですが、たぶんそれは次の章で触れると思います。たぶん。

アカウントの保管

秘密鍵の暗号化をしよう! という話でした。そうですね。

以上です。お疲れさまでした。

3. アカウント[実施編]

速習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章、アカウントについては以上です。

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

はじめに。

説明

速習Symbolブロックチェーンを体験したことを記した記事です。

速習Symbolブロックチェーン通りにSymbolSDKを使用しようとしましたが、色々思うところあってFlutterでチャレンジしよう、ということにしました。

なお、更新頻度は遅めです。時間と、気力次第になります。

注意事項

念のため、注意事項を記しておきます。

  • 速習Symbolブロックチェーンの「はじめに」「環境構築」は飛ばします(目は通しました)。
  • 現在、作成したコードの全体を公開する予定はありません。
    • 速習Symbolブロックチェーン&Flutterを学びながらにより、遡ってリファクタリングや修正をする可能性が高いため(無計画)。
    • オレオレ構造で作っているので、誰かの当てにならないと思うので。
    • ブロックチェーンを使用しないところは、等価比較します(予定)
  • 項目の名前や理解は間違っている可能性があります。すみません。
  • 記事の更新をしながら、行き当たりばったりに学習しているため、更新頻度は低め。
    • 完走も保証はしないです。

僕が動かせて、その記録と理解を残せればいい」というスタンスで書いていきます。

注意事項は以上です。

この先は、速習Symbolを行う動機と、タイトル理由を残しておこうと思います。

速習Symbol、ご存じですか?

Symbolという名の暗号資産があります。

このSymbolという暗号資産、ブロックチェーンなのですが、難しい知識がなくても利用できる、らしいんですよ。
※もちろん、最低限の知識は必要かと思いますけども。

しかもこのSymbolっていうのは運営する中央の組織がありません。

つまり、

「勝手に使っても怒る人がいない」

そんなブロックチェーンなんですね。※たぶん、ですけど。

しかもその利用方法や機能などを速攻でお勉強させてくれるっていう神教材があるらしいんですよ。

それが

「速習Symbolブロックチェーン」

というものなんです。

Symbolは自由に使えるブロックチェーンとのことで、もともと「手弁当でも、なんか作れたら楽しそうでいいなー」と思っていました。

ほんで、そんな親切な教材があるならやってみようかな。なんて始めてみることにしました。

タイトル「とろ速Symbol」について

で、この体験記のタイトルは「とろ速Symbol」としました。

速習」言ってるのに「とろ速」ってなんで? って話でもあるんですけれども。

公式SDKはTypeScript(JavaScript)なんだよな。。。から色々あって、Flutterでやってみようか、となりました。

僕はね、基本的に「人は苦しんだことしか覚えないし、やったことしかできるようにならない」と思っているんですよ。

そんなわけで苦しむため? にFlutterでやることとしました。

僕自体はIT系の端くれエンジニアとして食ってきましたが、勉強してきたことも基本的にない、ヘボジニア(ヘボなエンジニア)です。

なのでTypeScript、JavaScriptを知らないし、Flutter(Dart)も知らないです。

もちろんブロックチェーンなんて勉強したこともありません。

インフラ系でもないですし、Web系でもないです。組み込みでもないですけど。一体何系なんだ……?

そんな僕なので、まぁ、トロトロ進むだろうと、とろ速としました。

大体の記事の構成について

1章につき、2記事を書く予定です。初めの意気込みとしては、ですが。

  • 実際にやってみた実施編
  • やってみた感想の感想編

の2本立てで行く予定です。なお、これはモチベーションに寄ります。

最後に

基本的に毎章の終わりで、投げXYM用のアドレスを公開します。

こちら!! 投げていただけると励みになります!

NCQSPV2RF5TTUMVE2TGTJ3XXAKI3FXW7WCBJHKI

.NETで構築したアプリケーションをcronで実行できなかった。

個人的な備忘録です。

具体的な目標として、

・Raspberry pi 3

・.NET6で構築したC#コンソールアプリケーション

・cronによる定期実行

ということをしたい、という状態です。

既に構築した状況として、cronのインストール済み、.NETのインストールも実施済みです。

$ dotnet アプリケーション.dll

を実行することで呼び出すことが可能なのも確認済みです。

上記のコマンドを呼び出すシェルスクリプト(hogehoge.sh)を用意し、cronを設定して実行したのですが、うまくいきませんでした。

対策を行った結果についてですが、

  1. crontab -eコマンドでcronの設定を行った。
    設定実行ユーザーが実行ユーザーとなるため、シェルのパーミッションをそれに合わせて変更した。
  2. cronの基本実行ディレクトリは、実行ユーザーのhomeとなる。
    hogehoge.shの中でcdコマンドにより、対象ディレクトリに移動した。
    ※アプリケーション.dllのパスの問題もあるし、アプリケーション上でカレントディレクトリにファイルを出力する予定であったため。
  3. cronを実行するパスにdotnetが存在していなかった。
    実行ユーザーでhogehoge.shを直接実行した場合、うまくいったのだけれど、アプリケーション.dllが実行されなかった。
    どうやら「dotnet」というコマンド自体が認識されていないようで、crontab -eの内容に、以下の内容を追加した。
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/home/ユーザー名/.dotnet

cronの設定や、実行ディレクトリなどについては検索すると簡単に出てきてくれたのですが、dotnetを認識していない、というのは中々わかりませんでした。

というか検索しても出なかったので、予想で設定しました。

結果として実行されているので、まあよかったかな。

Symbolの夏、ハーベストの夏。それと、dHealthの夏。

夏、楽しんでいますか???

 現在、ハーベスト手数料無料の夏イベント「Symbolの夏、ハーベストの夏」を開催中です。

 ただ、ここ最近になって「9月も暑いしなぁ」「この夏、DHPネイティブが上場したなぁ」と思うことがありました。

 よって、ハーベスト手数料無料の夏のイベント「Symbolの夏、ハーベストの夏」をパワーアップすることしました。

 その名も、、、

Symbolの夏、ハーベストの夏。それと、dHealthの夏。

 ハーベストのノード徴収額(手数料)が0である、という基本部分は変わりません。

 ということで、改めて、

Symbolの夏、ハーベストの夏。それと、dHealthの夏。」の説明を行います。

  • ハーベスト時の手数料がゼロ。ハーベスト報酬が100%取得可能です。
  • 対象は僕が運用する「Symbolノード」および、「dHealthノード」です。
  • イベント期間は2022年9月末まで
    ※2022年10月に実施予定のメンテナンスまで。

 特に難しいルールはありません。

 僕の運営する「Symbolノード」またはdHealthノード」に委任いただくだけで、参加可能です。もちろん、参加を申し出るといったことなども不要です。

 よろしければいかがでしょうか?

 先の通り、イベント期間は2022年9月末までです。

注意事項

 さて、本イベント自体の注意事項でなく、Symbol/dHealthにおけるハーベスト設定時の注意事項です。

 Symbolでは悲しいことに、ハーベストを設定した際において、アグリゲートボンデッドを利用した詐欺が横行しています。

 アグリゲートボンデッドは簡単に言えば、「取引(送金)を行っていいか?」という契約書です。
 ハーベスト設定時にそれが送付されることあるのですが、署名を行うと指定された金額(ウォレット全額含む)が盗まれることになります。

 ハーベストの設定を行う際はアグリゲートボンデッドに署名することはありません。

 自分の身を守れるのは原則自分のみのため、くれぐれもご注意をお願いします。

 以上です。

運用中のdHealthノードについて。

以下の通り、暗号資産のdHealth/DHPノードを運用しています。

名前URL委任受入数優先順
dHealthSixisNodedhealth.sixis.xyz999重要度順

My Node Descriptionでの表示はこちら。

ノードイベント実施中。2022年9月末まで。

委任特典について。

現状、委任特典については特にありません。

「Symbolネットワークに貢献したい」と考えているぐらいです。

原則還元も行っていないのですが、ハーベスト手数料無料イベントを実施することがあるかもしれません。

※実施する場合、Twitterにて宣伝を行います。

その他、何かしら活動していけたらな、と思います。ふわふわしていてすみません。

運用環境について。

ノードはVPSをレンタルし、その上に構築しています。

ノードの性能については抑え気味ですが、スペックに問題が発生した場合、性能を引き上げる用意はあります。

VPSのレンタルのため、「ハードウェアトラブル」については比較的強いかと思います。

トラブル対策について。

トラブル対策として、運用中の全ノードにおいて1時間に1度、ノードダウン対策として監視を行っています。

万が一、意図せぬ停止が起きているようであれば、僕の元にメールが飛ぶようになっています。

また、毎日1度、委任していただいているハーベスター方々のアドレスを記録しています。

そのため、すべてではないですが、現在委任していただいている方々のアドレスを追う方法は保有しているつもりです。

(基本的に、個人の特定等は行っていません)

これは「万が一の際に、連絡手段としてアドレスにメッセージを送信する」ということを考えているためです。

メンテナンスについて。

Symbolノードは基本的に不具合が(現在のところ)露見していません。

また、委任状態についても、堅牢に守られています。

仮にサーバー障害などによってノードを再構築した場合でも、ブロックの同期が行われれれば委任者は復元されるようにできています。

そういった事情から、定期メンテナンスといったことは行っていません。

ノードを停止する必要のある場合、Twitterで報告しています。

Twitterをフォローしていただくと嬉しく思います。

なお、想定している、事前予告するメンテナンス内容は以下の通りです。

No項目備考
1ノードアップデートSymbol-bootstrapのアップデートがあれば適用します。
2ソフトウェアアップデートサーバーが使用しているソフトウェアのアップデートを行います。

お問い合わせについて。

メールかTwitterでお願いします。

基本的にTwitterのほうが連絡は早く、確実かと思います。

※メールの場合、添付ファイルがある場合は基本的に開けません。

運用中のノードについて。

他にもノードを運用しています。

詳しくはこちらをご覧ください。

運用中のSymbolノードについて。

以下の通り、暗号資産のSymbol/XYMノードを運用しています。

名前URL委任受入数優先順
SixisNodenode.sixis.xyz9999重要度順

My Node Descriptionでの表示はこちら。

ノードイベント実施中。2022年9月末まで。

委任特典について。

現状、委任特典については特にありません。

「Symbolネットワークに貢献したい」と考えているぐらいです。

原則還元も行っていないのですが、ハーベスト手数料無料イベントを実施することがあるかもしれません。

※実施する場合、Twitterにて宣伝を行います。

その他、何かしら活動していけたらな、と思います。ふわふわしていてすみません。

運用環境について。

ノードはVPSをレンタルし、その上に構築しています。

ノードの性能については抑え気味ですが、スペックに問題が発生した場合、性能を引き上げる用意はあります。

VPSのレンタルのため、「ハードウェアトラブル」については比較的強いかと思います。

トラブル対策について。

トラブル対策として、運用中の全ノードにおいて1時間に1度、ノードダウン対策として監視を行っています。

万が一、意図せぬ停止が起きているようであれば、僕の元にメールが飛ぶようになっています。

また、毎日1度、委任していただいているハーベスター方々のアドレスを記録しています。

そのため、すべてではないですが、現在委任していただいている方々のアドレスを追う方法は保有しているつもりです。

(基本的に、個人の特定等は行っていません)

これは「万が一の際に、連絡手段としてアドレスにメッセージを送信する」ということを考えているためです。

メンテナンスについて。

Symbolノードは基本的に不具合が(現在のところ)露見していません。

また、委任状態についても、堅牢に守られています。

仮にサーバー障害などによってノードを再構築した場合でも、ブロックの同期が行われれれば委任者は復元されるようにできています。

そういった事情から、定期メンテナンスといったことは行っていません。

ノードを停止する必要のある場合、Twitterで報告しています。

Twitterをフォローしていただくと嬉しく思います。

なお、想定している、事前予告するメンテナンス内容は以下の通りです。

No項目備考
1ノードアップデートSymbol-bootstrapのアップデートがあれば適用します。
2ソフトウェアアップデートサーバーが使用しているソフトウェアのアップデートを行います。

お問い合わせについて。

メールかTwitterでお願いします。

基本的にTwitterのほうが連絡は早く、確実かと思います。

※メールの場合、添付ファイルがある場合は基本的に開けません。

運用中のノードについて。

他にもノードを運用しています。

詳しくはこちらをご覧ください。

« Older posts

© 2023 SIXIS

Theme by Anders Noren上へ ↑