この記事は、以下に紹介したtsunagi-functionsのDart実装版です。
トランザクション構築の内部処理やノードへの通知方法については以下の記事もご参考ください。
Symbolブロックチェーンをよく知らないという方はこちらで速習することができます。
リポジトリ
tsunagi-functions for Dart
関数群の提供のため、sdkという名称は今後functionsに変更予定です。
テスト
テストスクリプトを以下に置いています。実行することができればそのロジックをそのまま実装に利用することができます。
test_0_1.dart
pubspec.yaml
name: hello
environment:
sdk: '>=2.10.0 <3.0.0'
dependencies:
convert: ^3.0.2
ed25519_edwards: ^0.3.1
base32: 2.1.3
sha3: ^0.2.0
http: ^0.13.4
hash: ^1.0.4
dev_dependencies:
test:
テスト実行
dart test_0_1.dart
実装例
まずは簡単な実装例を紹介します。
転送トランザクション
var privateKey = "94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c7";
var network = {
"version":1,
"network":'TESTNET',
"generationHash":'7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836',
"epochAdjustment":1637848847,
"catjasonBase":"https://xembook.github.io/tsunagi-sdk/catjson/",
};
// var now = (new DateTime.now().millisecondsSinceEpoch / 1000).floor();
int now = network["epochAdjustment"];
var deadline = ((now + 7200) - network["epochAdjustment"]) * 1000;
var tx1 = {
"type" : "TRANSFER",
"signer_public_key" : "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee" : 25000,
"deadline" : deadline,
"recipient_address" : generateAddressId("TCO7HLVDQUX6V7C737BCM3VYJ3MKP6REE2EKROA"),
"mosaics" : [
{"mosaic_id" : 0x2A09B7F9097934C2, "amount" : 1},
{"mosaic_id" : 0x3A8416DB2D53B6C8, "amount" : 100},
],
"message" : "Hello Tsunagi(Catjson) SDK!"
};
var catjson = await loadCatjson(tx,network);
var layout = await loadLayout(tx,catjson,false);
var preparedTx = await prepareTransaction(tx,layout,network); //TX事前準備
var parsedTx = await parseTransaction(preparedTx,layout,catjson,network);
var builtTx = buildTransaction(parsedTx);
var signature = signTransaction(builtTx,"94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c7",network);
builtTx = updateTransaction(builtTx,"signature","value",signature);
var txHash = hashTransaction(tx["signer_public_key"],signature,builtTx,network);
var payload = hexlifyTransaction(builtTx,0);
通知
var payload = {
"payload":payloadString
};
var response = http.put(
Uri.parse("https://sym-test-02.opening-line.jp:3001/transactions"),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(payload),
)
.then((http.Response response) {
print("Response status: ${response.statusCode}");
print("Response body: ${response.contentLength}");
print(response.headers);
print(response.request);
});
連署
連署を必要とするトランザクションの場合は、tx1 に加えて以下のような定義と署名の追加が必要になります。
var cosignature1 = {
"version":0,
"signer_public_key":"6199bae3b241df60418e258d046c22c8c1a5de2f4f325753554e7fd9c650afec",
"signature":"",
};
var cosignature2 = {
"version":0,
"signer_public_key":"886adfbd4213576d63ea7e7a4bece61c6933c27cd2ff36f85155c8febfb6eb4e",
"signature":"",
};
var aggTx = {
"type":'AGGREGATE_COMPLETE',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":1000000,
"deadline":deadline,
"transactions":[tx1,tx2,tx3],
"cosignatures":[cosignature1,cosignature2]
};
var catjson = await loadCatjson(aggTx,network);
var layout = loadLayout(aggTx,catjson,false); //isEmbedded false
var preparedTx = await prepareTransaction(aggTx,layout,network); //TX事前準備
var parsedTx = await parseTransaction(preparedTx,layout,catjson,network); //TX解析
var builtTx = buildTransaction(parsedTx); //TX構築
var signature = signTransaction(builtTx,privateKey,network);
builtTx = updateTransaction(builtTx,"signature","value",signature);
//トランザクションハッシュ作成
var txHash = hashTransaction(aggTx["signer_public_key"],signature,builtTx,network);
//連署
preparedTx["cosignatures"][0]["signature"] = cosignTransaction(txHash,bobPrivateKey);
preparedTx["cosignatures"][1]["signature"] = cosignTransaction(txHash,carolPrivateKey);
var cosignaturesLayout = layout.firstWhere((lf)=>lf["name"] == "cosignatures");
var parsedCosignatures = await parseTransaction(preparedTx,[cosignaturesLayout],catjson,network); //構築
builtTx = updateTransaction(builtTx,"cosignatures","layout",parsedCosignatures[0]["layout"]);
var payload = hexlifyTransaction(builtTx,0);
トランザクションタイプ別記述例
その他のトランザクションの記述方法について説明します。
アグリゲートボンデッドトランザクション
ハッシュロックトランザクション
var tx1 = {
"type":'HASH_LOCK',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"mosaic":[{"mosaic_id": 0x3A8416DB2D53B6C8, "amount": 10000000}],
"duration": 480,
"hash":"a3ed27ee26592f6c501349a7de3427fc729e8d625ed214a6331c11b981f59f78"
};
ボンデッド
var aggTx = {
"type":'AGGREGATE_BONDED',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":1000000,
"deadline":deadline,
"transactions":[tx1,tx2,tx3],
};
モザイク生成
var nonce = 1700836761;
var tx1 = {
"type":'MOSAIC_DEFINITION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"duration": 0,
"id":generateMosaicId(generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),nonce), //0x4DAFFBE5505DE676,
"nonce": 1700836761,
"flags": 'TRANSFERABLE RESTRICTABLE',
"divisibility": 2
};
var tx2 = {
"type": 'MOSAIC_SUPPLY_CHANGE',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"mosaic_id":generateMosaicId(generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),nonce), //0x4DAFFBE5505DE676,
"delta": 1000 * 100, // assuming divisibility = 2
"action": 'INCREASE'
};
ネームスペース生成
ルートネームスペース
var tx1 = {
"type":'NAMESPACE_REGISTRATION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"duration": 86400,
"registration_type": "ROOT",
"name":"xembook",
"id":generateNamespaceId("xembook",0) //0xA43415EB268C7385, //BigInt((new sym.NamespaceId("xembook")).id.toString())
};
サブネームスペース
var tx1 = {
"type":'NAMESPACE_REGISTRATION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"parent_id": generateNamespaceId("xembook",0), //0xA43415EB268C7385, //BigInt((new sym.NamespaceId("xembook")).id.toString())
"registration_type": "CHILD",
"name":"tomato",
"id":generateNamespaceId("tomato",generateNamespaceId("xembook",0)) //0xFA547FD28C836431, //BigInt((new sym.NamespaceId("xembook.tomato")).id.toString())
};
アドレスへのリンク
var tx1 = {
"type":'ADDRESS_ALIAS',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"namespace_id":generateNamespaceId("xembook",0), //0xA43415EB268C7385, //BigInt((new sym.NamespaceId("xembook")).id.toString()),
"address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
"alias_action":"LINK"
};
モザイクへのリンク
var tx1 = {
"type":'MOSAIC_ALIAS',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"namespace_id":generateNamespaceId("tomato",generateNamespaceId("xembook",0)), //0xFA547FD28C836431, //BigInt((new sym.NamespaceId("xembook.tomato")).id.toString())
"mosaic_id":0x4DAFFBE5505DE676,
"alias_action":"LINK"
};
メタデータ
アカウントへのメタデータ
var tx1 = {
"type":'ACCOUNT_METADATA',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"target_address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
"scoped_metadata_key":generateKey("key_account"), //0x9772B71B058127D7, //"key_account"
"value_size_delta":27,
"value":"Hello Tsunagi(Catjson) SDK!",
};
var aggTx = {
"type":'AGGREGATE_COMPLETE',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":1000000,
"deadline":deadline,
"transactions":[tx1],
};
モザイクへのメタデータ
var tx1 = {
"type":'MOSAIC_METADATA',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":1000000,
"deadline":deadline,
"target_address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
"target_mosaic_id":0x4DAFFBE5505DE676,
"scoped_metadata_key":generateKey("key_mosaic"), //0xCF217E116AA422E2, //"key_mosaic"
"value_size_delta":27,
"value":"Hello Tsunagi(Catjson) SDK!",
};
var aggTx = {
"type":'AGGREGATE_COMPLETE',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":1000000,
"deadline":deadline,
"transactions":[tx1],
};
ネームスペースへのメタデータ
var tx1 = {
"type":'NAMESPACE_METADATA',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":1000000,
"deadline":deadline,
"target_address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
"target_namespace_id":generateNamespaceId("xembook",0), //xembook
"scoped_metadata_key":generateKey("key_namespace"), //0x8B6A8A370873D0D9, //"key_namespace"
"value_size_delta":27,
"value":"Hello Tsunagi(Catjson) SDK!",
};
var aggTx = {
"type":'AGGREGATE_COMPLETE',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":1000000,
"deadline":deadline,
"transactions":[tx1],
};
マルチシグ
var tx1 = {
"type":'MULTISIG_ACCOUNT_MODIFICATION',
"signer_public_key":"66adb706bc9a93e6e803b2b76a1341a8acd98690ef204b402643ae3d4701ee77",
"min_removal_delta":1,
"min_approval_delta":1,
"address_additions":[
generateAddressId("TCO7HLVDQUX6V7C737BCM3VYJ3MKP6REE2EKROA"),
generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI")
],
"address_deletions":[]
};
var cosignature1 = {
"version":0,
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"signature":"",
};
var cosignature2 = {
"version":0,
"signer_public_key":"6199bae3b241df60418e258d046c22c8c1a5de2f4f325753554e7fd9c650afec",
"signature":"",
};
var aggTx = {
"type":'AGGREGATE_COMPLETE',
"signer_public_key":"66adb706bc9a93e6e803b2b76a1341a8acd98690ef204b402643ae3d4701ee77",
"fee":1000000,
"deadline":deadline,
"transactions":[tx1],
"cosignatures":[cosignature1,cosignature2]
};
制限
アカウントアドレス制限
var tx1 = {
"type":'ACCOUNT_ADDRESS_RESTRICTION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"restriction_flags":"ADDRESS BLOCK OUTGOING",
"restriction_additions":[generateAddressId("TCO7HLVDQUX6V7C737BCM3VYJ3MKP6REE2EKROA"),generateAddressId("TDZBCWHAVA62R4JFZJJUXQWXLIRTUK5KZHFR5AQ")],
"restriction_deletions":[]
};
アカウントモザイク制限
var tx1 = {
"type":'ACCOUNT_MOSAIC_RESTRICTION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"restriction_flags":"MOSAIC_ID BLOCK",
"restriction_additions":[0x4DAFFBE5505DE676,0x2A09B7F9097934C2],
"restriction_deletions":[]
};
アカウント操作制限
var tx1 = {
"type":'ACCOUNT_OPERATION_RESTRICTION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"restriction_flags":"TRANSACTION_TYPE BLOCK OUTGOING",
"restriction_additions":["TRANSFER","AGGREGATE_COMPLETE"],
"restriction_deletions":[]
};
グローバルモザイク制限
モザイクグローバル制限
var tx1 = {
"type":'MOSAIC_GLOBAL_RESTRICTION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"mosaic_id":0x4DAFFBE5505DE676,
"reference_mosaic_id":0,
"restriction_key":0x9772B71B058127D7,
"previous_restriction_value":0,
"new_restriction_value":0x1,
"previous_restriction_type":"NONE",
"new_restriction_type":"EQ"
};
モザイクアドレス制限
var tx1 = {
"type":'MOSAIC_ADDRESS_RESTRICTION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"mosaic_id":0x4DAFFBE5505DE676,
"restriction_key":0x9772B71B058127D7,
"previous_restriction_value":0xFFFFFFFFFFFFFFFF,
"new_restriction_value":0x1,
"target_address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI")
};
リボーカブルトランザクション
var tx1 = {
"type":'MOSAIC_SUPPLY_REVOCATION',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"source_address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
"mosaic":[{"mosaic_id": 0x0552BC5EF5BD589D, "amount": 100}]
};
シークレット
シークレットロック
var tx1 = {
"type":'SECRET_LOCK',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"recipient_address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
"secret":"f260bfb53478f163ee61ee3e5fb7cfcaf7f0b663bc9dd4c537b958d4ce00e240",
"mosaic":[{"mosaic_id": 0x3A8416DB2D53B6C8, "amount": 10000000}],
"duration": 480,
"hash_algorithm":"SHA3_256"
};
シークレットプルーフ
var tx1 = {
"type":'SECRET_PROOF',
"signer_public_key":"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
"fee":25000,
"deadline":deadline,
"recipient_address":generateAddressId("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
"secret":"f260bfb53478f163ee61ee3e5fb7cfcaf7f0b663bc9dd4c537b958d4ce00e240",
"hash_algorithm":"SHA3_256",
"proof":"7944496ac0f572173c2549baf9ac18f893aab6d0"
};