トランザクション送信を代行する（symbol testnet 0.10.0.7）

# はじめに

トランザクションを送信するには、トランザクション手数料が必要です。

そのため、アカウントには残高が必要となってしまいます。

なんかのサービスを利用するのに、取引所に登録してKYCして、暗号通貨買って、自分のアカウント作って送金するのって、めちゃ手間です。

![symbol-tx-relayer-Page-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/0efa1aa1-778f-d9d3-472c-3d4e5de93aee.png)

それが当たり前の世界になってくれればいいんですけど、いまはそうじゃないみたいです。

ほかには、サービス提供者が秘密鍵を管理してユーザーにアカウントを提供する方法があります。

でも今の日本では、秘密鍵をサービス提供者が管理することは難しいです。

ユーザーの資産を管理するカストディ業務の規制などがあるからです。

なので、ユーザー自身が秘密鍵を管理しつつ、トランザクションを送信できるようにする方法が必要です。

これはいろんな方法が既にあって、[Dapper](https://www.meetdapper.com/)とかそうです。

カタパルトでもやり方はあるだろうから、まずはシンプルなやつを作ってみる。

# 方法

まずは、APIサーバーを作ります。

このAPIは、トランザクションを受け取って、アグリゲートトランザクションでラップします。

インナートランザクションの署名者は、API呼び出し者の公開鍵を設定します。

APIサーバーで保持している秘密鍵でアグリゲートトランザクションに署名して、API呼び出し者の連署をつけてブロックチェーンに送信します。

![symbol-tx-relayer-Page-2 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/843724fe-d5a1-1c96-163f-400f659a36b4.png)

こうすれば、トランザクション手数料はAPIサーバーのアカウントが支払うことになります。

アグリゲートボンデッドトランザクションにすると、担保の10XYMが必要になります。スパムに攻撃される可能性があるので、アグリゲートコンプリートトランザクションにします。そのかわり、APIサーバーとの通信が2回発生することになります。

# symbol-tx-relayer

https://github.com/planethouki/symbol-tx-relayer

だいたいこんな感じで

![Untitled Diagram-Page-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/05b2b64c-e78c-6fe8-0c33-c8223d9c2e3f.png)

試しにサンプルクライアントも作ってある

https://planethouki.github.io/symbol-tx-relayer/

メタデータトランザクションを代理で送信してもらう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/bd750f2e-bd58-e6f5-78f0-5be6914ae9be.png)

## 送ってみる

試しにこのようなデータでメタデータトランザクションを送る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/2ae68e5a-53e5-190e-1e56-5906638566a8.png)

このアカウントは、残高が0です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/614171f8-6689-5976-6f54-208407badc00.png)


送信されたトランザクションはこんな感じになります。アグリゲートコンプリートトランザクションでラップしてあるので、結構長くなります。

```json
{
  "meta": {
    "height": "142012",
    "hash": "E668161B15986B86C16179F2FC2C478ADBB83E0FD2D4099C1CC7AC666FB3C6F3",
    "merkleComponentHash": "08963611105C59E5ACD03788FB8136C3692E36C23B2B56C42196E255C9EEC68D",
    "index": 0
  },
  "transaction": {
    "size": 456,
    "signature": "38764720469B962BF344E6A57628A42E1615D03EC22020BD91CE2D9A1C7E71CEF0416CB939093E846A0C0F791E2BA9BDC7BF05057191940DA4DB46126EAC2502",
    "signerPublicKey": "C65B49BA7673BFEC3EFD04DE7EF412A6346F4BA745AAC09649E8CAFE1AC38580",
    "version": 1,
    "network": 152,
    "type": 16705,
    "maxFee": "50000",
    "deadline": "40960617412",
    "transactionsHash": "EAB5B9AA8D7AAF83060E6612520836C45C7B24A8D811E8963569CDFB0827FE0F",
    "cosignatures": [
      {
        "version": "0",
        "signerPublicKey": "0050FD4FB81BD8B75C9BED665820B316B164D515FAA83A730C95FB3412248CA9",
        "signature": "AD9205C5033A3086678D1BA98952B2DB57D33895865BA06AD9D105FC2BD7AEC501507DB59B483E64D7B498B06C10338CB929CC1AB1FD756F6EBD101649501E05"
      }
    ],
    "transactions": [
      {
        "meta": {
          "height": "142012",
          "aggregateHash": "E668161B15986B86C16179F2FC2C478ADBB83E0FD2D4099C1CC7AC666FB3C6F3",
          "aggregateId": "60398AFF525C57631166DDBC",
          "index": 0
        },
        "transaction": {
          "signerPublicKey": "0050FD4FB81BD8B75C9BED665820B316B164D515FAA83A730C95FB3412248CA9",
          "version": 1,
          "network": 152,
          "type": 16708,
          "targetAddress": "98F6B36735496378B48B5AE4572F994A050A17B1073D3E51",
          "scopedMetadataKey": "ABEE0312D41F80D5",
          "valueSizeDelta": 3,
          "valueSize": 3,
          "value": "333532"
        },
        "id": "60398AFF525C57631166DDBD"
      },
      {
        "meta": {
          "height": "142012",
          "aggregateHash": "E668161B15986B86C16179F2FC2C478ADBB83E0FD2D4099C1CC7AC666FB3C6F3",
          "aggregateId": "60398AFF525C57631166DDBC",
          "index": 1
        },
        "transaction": {
          "signerPublicKey": "C65B49BA7673BFEC3EFD04DE7EF412A6346F4BA745AAC09649E8CAFE1AC38580",
          "version": 1,
          "network": 152,
          "type": 16724,
          "recipientAddress": "9844BAF97731660BC5B5B5C0DA20AB080C5D059DB741C86E",
          "mosaics": [
            {
              "id": "2CF403E85507F39E",
              "amount": "0"
            }
          ]
        },
        "id": "60398AFF525C57631166DDBE"
      }
    ]
  },
  "id": "60398AFF525C57631166DDBC"
}
```

内部トランザクションが2つあるのは、ひとつはメタデータトランザクション、もうひとつはダミーです。というのも、代理アカウントがトランザクション署名するために必要なのです。全く関係のないアカウントは署名してはならないので、ダミーのトランスファートランザクションをくっつけています。

## メタデータ取得

メタデータは一覧形式で表示できるようにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/136f2c62-880b-7cc7-bf0b-ed6c5c2e1956.png)

さっき送信したやつを赤枠で囲っています。変更にも対応しています。

# おわりに

トランザクション送信を代行するAPIを作って、メタデータトランザクションを代行してみました。

モザイクの送信代行も同じように可能です。

ただ、アグリゲートコンプリートトランザクションでラップする関係上、アグリゲートトランザクションは代理送信できません。

副作用としてですが、仕組み上、ユーザーが設定したいメタデータの事前検証が可能になります。なので、ユーザーの行動履歴やゲームステータスなんかに応用することができそうです。

メタデータは、キーと設定者のアドレスで一意なので、`SourceAddress`のあたりをうまく制御するといけそうな気がします。

クライアント側は、サーバーが送ってくる署名済み未連署トランザクションの検証が必須になります。サーバーが悪意のある内部トランザクションを含める可能性があります。内部トランザクションの検証と、トランザクションハッシュの検証が最低限必要でしょう。