tsunagi-functionsでSymbolブロックチェーンのトランザクションを自由自在に作ろう！（PHP編）

この記事は、以下に紹介したtsunagi-functionsのPHP実装版です。

https://qiita.com/nem_takanobu/items/4b5d67fe9eb80ae35433

トランザクション構築の内部処理やノードへの通知方法については以下の記事もご参考ください。

https://qiita.com/nem_takanobu/items/d69f86487fce527dbc29

Symbolブロックチェーンをよく知らないという方はこちらで速習することができます。

https://github.com/xembook/quick_learning_symbol

# リポジトリ

##### tsunagi-functions for PHP
https://github.com/xembook/tsunagi-sdk/tree/main/php

関数群の提供のため、sdkという名称は今後functionsに変更予定です。


# テスト

テストスクリプトを以下に置いています。実行することができればそのロジックをそのまま実装に利用することができます。

#### test_0_1.php

https://github.com/xembook/tsunagi-sdk/blob/main/php/test_0_1.php

```json:composer.json
{
    "require": {
        "christian-riesen/base32": "^1.6"
    }
}
```

```sh:テスト実行
vendor/bin/phpunit test_0_1.php
```

# 実装例

まずは簡単な実装例を紹介します。

### 転送トランザクション

```php
$private_key = "94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c75f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb";

$this->network = [
	"version" => 1,
	"network" => "TESTNET",
	"generationHash" => "7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836",
	"epochAdjustment" => 1637848847,
	"catjasonBase" => "https://xembook.github.io/tsunagi-sdk/catjson/",

];
$now = $this->network["epochAdjustment"] * 1000;
$this->deadline_time = ((intval($now/1000)  + 7200) - 1637848847) * 1000;
//$deadline_time = ((time()  + 7200) - 1637848847) * 1000;

$tx1 = [
	"type" => "TRANSFER",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"recipient_address" => generate_address_id("TCO7HLVDQUX6V7C737BCM3VYJ3MKP6REE2EKROA"),
	"mosaics" => [
		["mosaic_id" =>  0x2A09B7F9097934C2, "amount" => 1],
		["mosaic_id" =>  0x3A8416DB2D53B6C8, "amount" => 100],
	],
	"message" => "Hello Tsunagi(Catjson) SDK!",
];

$catjson = load_catjson($tx1,$this->network);
$layout = load_layout($tx1,$catjson,false); //isEmbedded false
$prepared_tx = prepare_transaction($tx1,$layout,$this->network); //TX事前準備
$parsed_tx = parse_transaction($prepared_tx,$layout,$catjson,$this->network);

$built_tx    = build_transaction($parsed_tx); //TX構築
$signature = sign_transaction($built_tx,$private_key,$this->network);
$built_tx = update_transaction($built_tx,"signature","value",$signature);
$tx_hash = hash_transaction($tx1["signer_public_key"],$signature,$built_tx,$this->network);
$payload = hexlify_transaction($built_tx);

```

### 通知

```php
$node = "https://sym-test-01.opening-line.jp:3001";
$params = array(
    "payload" => $payload
);
$params = json_encode($params); // json化
$header = array(
    "Content-Type: application/json",
);

$context = array(
    "http" => array(
        "method"  => 'PUT',
        "header"  => implode("\r\n", $header),
        "content" => $params
    )
);

$json_response = file_get_contents($node . "/transactions" ,false,stream_context_create($context));
echo $json_response . PHP_EOL;
echo $http_response_header . PHP_EOL;
```

### 連署

連署を必要とするトランザクションの場合は、$tx1 に加えて以下のような定義と署名の追加が必要になります。

```php
$cosignature1 = [
	"version" => 0,
	"signer_public_key" =>"6199bae3b241df60418e258d046c22c8c1a5de2f4f325753554e7fd9c650afec",
	"signature"=>"",
];

$cosignature2 = [
	"version" => 0,
	"signer_public_key" => "886adfbd4213576d63ea7e7a4bece61c6933c27cd2ff36f85155c8febfb6eb4e",
	"signature" => "",
];

$agg_tx = [
	"type" => 'AGGREGATE_COMPLETE',
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 1000000,
	"deadline" => $this->deadline_time,
	"transactions" => [$tx1,$tx2,$tx3],
	"cosignatures" => [$cosignature1,$cosignature2]

];

$catjson = load_catjson($agg_tx,$this->network);
$layout = load_layout($agg_tx,$catjson,false); //isEmbedded false
$prepared_tx = prepare_transaction($agg_tx,$layout,$this->network); //TX事前準備
$parsed_tx = parse_transaction($prepared_tx,$layout,$catjson,$this->network);
$built_tx    = build_transaction($parsed_tx); //TX構築
$private_key = "94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c75f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb";
$signature = sign_transaction($built_tx,$private_key,$this->network);
$built_tx = update_transaction($built_tx,"signature","value",$signature);

$tx_hash = hash_transaction($agg_tx["signer_public_key"],$signature,$built_tx,$this->network);

//連署
$bob_private_key = "fa6373f4f497773c5cc55c103e348b139461d61fd4b45387e69d08a68000e06b6199BAE3B241DF60418E258D046C22C8C1A5DE2F4F325753554E7FD9C650AFEC";
$carol_private_key = "1e090b2a266877a9f88a510af2eb0945a63dc69dbce674ccd83272717d4175cf886ADFBD4213576D63EA7E7A4BECE61C6933C27CD2FF36F85155C8FEBFB6EB4E";

$prepared_tx["cosignatures"][0]["signature"] = cosign_transaction($tx_hash,$bob_private_key);
$prepared_tx["cosignatures"][1]["signature"] = cosign_transaction($tx_hash,$carol_private_key);

$filter_layout = array_filter($layout,function($fl){
	return $fl["name"] === "cosignatures";
});
$cosignatures_layout = array_values($filter_layout);

$parsed_cosignatures = parse_transaction($prepared_tx,$cosignatures_layout,$catjson,$this->network); //連署TXの構築
$built_tx = update_transaction($built_tx,"cosignatures","layout",$parsed_cosignatures[0]["layout"]);

$payload = hexlify_transaction($built_tx);
```

# トランザクションタイプ別記述例

その他のトランザクションの記述方法について説明します。

### アグリゲートボンデッドトランザクション

##### ハッシュロックトランザクション
```php
$tx1 = [
	"type" => "HASH_LOCK",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"mosaic" => [["mosaic_id" =>  0x3A8416DB2D53B6C8, "amount" => 10000000]],
	"duration" =>  480,
	"hash" => "a3ed27ee26592f6c501349a7de3427fc729e8d625ed214a6331c11b981f59f78"
];
```

##### ボンデッド
```php
$agg_tx = [
	"type" => 'AGGREGATE_BONDED',
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 1000000,
	"deadline" => $this->deadline_time,
	"transactions" => [$tx1,$tx2,$tx3],
];
```


##### モザイク生成
```php
$tx1 = [
	"type" => "MOSAIC_DEFINITION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"duration" => 0,
	"id" => generate_mosaic_id(generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),$nonce),
	"nonce" => $nonce,
	"flags" => 'TRANSFERABLE RESTRICTABLE',
	"divisibility" => 2,
];

$tx2 = [
	"type" => "MOSAIC_SUPPLY_CHANGE",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"duration" => 0,
	"mosaic_id" => generate_mosaic_id(generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),$nonce),
	"delta" => 1000 * 100,
	"action" => 'INCREASE',
];
```
#### ネームスペース生成
###### ルートネームスペース
```php
$tx1 = [
	"type" => "NAMESPACE_REGISTRATION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"duration" => 86400,
	"registration_type" => "ROOT",
	"name" => "xembook",
	"id" => generate_namespace_id("xembook"),
];
```

###### サブネームスペース
```php
$tx1 = [
	"type" => "NAMESPACE_REGISTRATION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"parent_id" => generate_namespace_id("xembook"),
	"registration_type" => "CHILD",
	"name" => "tomato",
	"id" => generate_namespace_id("tomato",generate_namespace_id("xembook")),
];
```
###### アドレスへのリンク
```php
$tx1 = [
	"type" => "ADDRESS_ALIAS",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"namespace_id" => generate_namespace_id("xembook"),
	"address" => generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
	"alias_action" => "LINK"
];
```
##### モザイクへのリンク
```php
$tx1 = [
	"type" => "MOSAIC_ALIAS",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"namespace_id" => generate_namespace_id("tomato",generate_namespace_id("xembook")),
	"mosaic_id" => 0x4DAFFBE5505DE676,
	"alias_action" => "LINK"
];
```

### メタデータ

##### モザイクへのメタデータ

```php
$tx1 = [
	"type" => "MOSAIC_METADATA",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"target_address" => generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
	"target_mosaic_id" => 0x4DAFFBE5505DE676,
	"scoped_metadata_key" => generate_key("key_mosaic"),
	"value_size_delta" => 27,
	"value" => "Hello Tsunagi(Catjson) SDK!",
];

$agg_tx = [
	"type" => 'AGGREGATE_COMPLETE',
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 1000000,
	"deadline" => $this->deadline_time,
	"transactions" => [$tx1],
];
```

##### ネームスペースへのメタデータ

```php
$tx1 = [
	"type" => "NAMESPACE_METADATA",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"target_address" => generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
	"target_namespace_id" => generate_namespace_id("xembook"),
	"scoped_metadata_key" => generate_key("key_namespace"),
	"value_size_delta" => 27,
	"value" => "Hello Tsunagi(Catjson) SDK!",
];

$agg_tx = [
	"type" => 'AGGREGATE_COMPLETE',
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 1000000,
	"deadline" => $this->deadline_time,
	"transactions" => [$tx1],
];
```

### マルチシグ

```php
$tx1 = [
	"type" => "MULTISIG_ACCOUNT_MODIFICATION",
	"signer_public_key" => "66adb706bc9a93e6e803b2b76a1341a8acd98690ef204b402643ae3d4701ee77",
	"min_removal_delta" => 1,
	"min_approval_delta" => 1,
	"address_additions" => [
		generate_address_id("TCO7HLVDQUX6V7C737BCM3VYJ3MKP6REE2EKROA"),
		generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
	],
	"address_deletions" => [],
];

$cosignature1 = [
	"version" => 0,
	"signer_public_key" =>"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"signature"=>"",
];

$cosignature2 = [
	"version" => 0,
	"signer_public_key" => "6199bae3b241df60418e258d046c22c8c1a5de2f4f325753554e7fd9c650afec",
	"signature" => "",
];


$agg_tx = [
	"type" => 'AGGREGATE_COMPLETE',
	"signer_public_key" => "66adb706bc9a93e6e803b2b76a1341a8acd98690ef204b402643ae3d4701ee77",
	"fee" => 1000000,
	"deadline" => $this->deadline_time,
	"transactions" => [$tx1],
	"cosignatures" => [$cosignature1,$cosignature2]

];

```

### 制限

##### アカウントアドレス制限

```php
$tx1 = [
	"type" => "ACCOUNT_ADDRESS_RESTRICTION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"restriction_flags" => "ADDRESS BLOCK OUTGOING",
	"restriction_additions" => [
		generate_address_id("TCO7HLVDQUX6V7C737BCM3VYJ3MKP6REE2EKROA"),
		generate_address_id("TDZBCWHAVA62R4JFZJJUXQWXLIRTUK5KZHFR5AQ")
	],
	"restriction_deletions" => [],
];
```

##### アカウントモザイク制限
```php
$tx1 = [
	"type" => "ACCOUNT_MOSAIC_RESTRICTION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"restriction_flags" => "MOSAIC_ID BLOCK",
	"restriction_additions" => [0x4DAFFBE5505DE676,0x2A09B7F9097934C2],
	"restriction_deletions" => [],
];
```

##### アカウント操作制限

```php
$tx1 = [
	"type" => "ACCOUNT_OPERATION_RESTRICTION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"restriction_flags" => "TRANSACTION_TYPE BLOCK OUTGOING",
	"restriction_additions" => ["TRANSFER","AGGREGATE_COMPLETE"],
	"restriction_deletions" => [],
];
```

### グローバルモザイク制限

##### モザイクグローバル制限
```php
$tx1 = [
	"type" => "MOSAIC_GLOBAL_RESTRICTION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"mosaic_id" => 0x4DAFFBE5505DE676,
	"reference_mosaic_id" => 0,
	"restriction_key" => generate_key("key_account"),
	"previous_restriction_value" => 0,
	"new_restriction_value" => 0x1,
	"previous_restriction_type" => "NONE",
	"new_restriction_type" => "EQ"
];
```

##### モザイクアドレス制限
```php
$tx1 = [
	"type" => "MOSAIC_ADDRESS_RESTRICTION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"mosaic_id" => 0x4DAFFBE5505DE676,
	"restriction_key" => generate_key("key_account"),
	"previous_restriction_value" => 0xFFFFFFFFFFFFFFFF,
	"new_restriction_value" => 0x1,
	"target_address" => generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
];
```

### リボーカブルトランザクション

```php
$tx1 = [
	"type" => "MOSAIC_SUPPLY_REVOCATION",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"source_address" => generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
	"mosaic" => [
		["mosaic_id" =>  0x0552BC5EF5BD589D, "amount" => 100],
	],
];
```

### シークレット

##### シークレットロック
```php
$tx1 = [
	"type" => "SECRET_LOCK",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"recipient_address" => generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
	"secret" => "f260bfb53478f163ee61ee3e5fb7cfcaf7f0b663bc9dd4c537b958d4ce00e240",
	"mosaic" => [
		["mosaic_id" =>  0x3A8416DB2D53B6C8, "amount" => 10000000],
	],
	"duration" => 480,
	"hash_algorithm" => "SHA3_256",
];

```
##### シークレットプルーフ
```php
$tx1 = [
	"type" => "SECRET_PROOF",
	"signer_public_key" => "5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb",
	"fee" => 25000,
	"deadline" => $this->deadline_time,
	"recipient_address" => generate_address_id("TBUXMJAYYW3EH3XHBZXSBVGVKXKZS4EH26TINKI"),
	"secret" => "f260bfb53478f163ee61ee3e5fb7cfcaf7f0b663bc9dd4c537b958d4ce00e240",
	"hash_algorithm" => "SHA3_256",
	"proof" => "7944496ac0f572173c2549baf9ac18f893aab6d0",
];


```