アグリゲートトランザクションでパブリックチェーンの参加者を増やす(手数料代払いの話)

# はじめに

パブリックなブロックチェーン(以下、パブリックチェーン)を利用するに当たり、その障壁となる課題の一つに**手数料**があります。
パブリックチェーンで送金などの処理を行う際は、原則として**その処理を行うアカウント自身**が手数料を負担する必要があります。

この手数料は円やドルといった法定通貨ではなく、パブリックチェーン上の基軸通貨(例:NENであればXEM、イーサリアムであればETHなど)で支払う必要があります。
これらの通貨は、誰かから譲ってもらうなり、交換所・取引所を利用するなどなんらかの手段で調達する必要があります。

## なぜ手数料があるのか

この手数料は**ネットワークの秩序維持**のために必要となります。
仮に手数料がかからなかった場合、悪意を持った人が闇雲にトランザクションを送り続け、ネットワークを容易にパンクさせ混乱に陥れることができてしまいます。
誰でも参加することができるパブリックチェーンにおいては、手数料を導入することによりスパムトランザクションを抑制し、ネットワークの秩序が保たれるようにしている側面があります。

また、手数料は**ネットワークの参加者への報酬**としても使われており、NEMにおいては、生成されたブロックに含まれているトランザクション手数料がブロックを作成する権利を得た人(ハーベスター)に支払われます。

このように、パブリックチェーンを維持する上で、手数料は必要不可欠なものではありますが、一方でこれによりパブリックチェーンを利用するハードルが高くなっている側面もあります。

# その手数料はそもそも自分が負担すべきか

例えば、以下の図の様にゲーム内通貨とアイテムがブロックチェーン上で作られたトークンによって取引されるとします。
<img width="700" alt="スクリーンショット 2019-12-16 15.12.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9315/33810342-6b59-84b5-5d43-7bb729f78ae1.png">
この際、この取引を行うAliceとBobはそれぞれゲーム内通貨やアイテムのトークンの他にトランザクション手数料を支払うためにネットワークの基軸通貨(XEMやETHなど)を用意し、負担する必要があります。

しかしながら、この取引はゲーム内のプラットフォーム内で行われてるとすると、それぞれのユーザがネットワークの基軸通貨を用意しないといけないのは、ユーザにとっては負担が大きく、プラットフォームの活性化を阻害する要因にもなります。
AliceとBobはあくまでもゲーム内の通貨とアイテムを交換したいだけです。

# 手数料代払い

<img width="700" alt="スクリーンショット 2019-12-16 15.48.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9315/9a6ad4e1-e1ef-a970-1680-bd0a3d867158.png">

仮に、ゲームプラットフォームを運営してるゲーム会社がこのトランザクション手数料を負担することができれば、AliceとBobはネットワークの基軸通貨を用意しなくともゲーム内の通貨とアイテムを交換することができます。
これにより、AliceとBobは**ブロックチェーンを使っている事を意識することなく**、しかしその裏で**信頼の高い取引**が行われています。

このように、手数料代払いができると全ての利用者が**ネットワークの基軸通貨を保有していなくても、ブロックチェーンの恩恵を受ける事ができる**ようになります。

# NEM2(Catapult)における手数料代払い

NEMの次期コアエンジンCatapultには、アグリゲートトランザクションという機能が追加されました。
近々パブリックチェーンでリリースされるNEM2(Catapult)においては、このアグリゲートトランザクションを利用することで手数料の代払いを実現させることができます。

## アグリゲートトランザクションとは

<img width="700" alt="スクリーンショット 2019-12-16 15.12.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9315/93508840-48a6-ef5b-4756-0e53b7fad963.png">

アグリゲートトランザクションは複数のトランザクションをひとまとめにして処理する機能です。
先ほどのゲーム内通貨とアイテムの取引の例では、現行のNEMでは、BobがAliceにゲーム内通貨を支払う処理と、AliceがBobにアイテムを渡す処理を別々に行う必要がありました。別々に処理を行った場合、BoBが代金を支払ったのに、Aliceからアイテムが渡ってこず、お金だけが持ち逃げされる可能性があります。

アグリゲートトランザクションを用いると、AliceとBobの署名が揃って始めて、ゲーム内通貨とアイテムの交換が実行されます。これによりお金やアイテムの持ち逃げを回避でき**安全な**取引を行うことが可能となります。

## アグリゲートトランザクションを使った手数料代払い

結論から先に書いてしますと、アグリゲートトランザクションの場合、その手数料は、**トランザクションの作成者が負担する**ことになります。

<img width="700" alt="スクリーンショット 2019-12-16 15.48.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9315/d892a8eb-f782-e3a1-0d3e-c29f34abe8e1.png">

上記の例においては、ゲーム会社がトランザクションの作成者となり、手数料を負担することによって、AliceとBobは、このトランザクションを承認さえすれば、手数料を負担することなく安全に取引をすることができます。

## 手数料代払いを行うためのコード

以下は、Fushicho3対応のnem2-sdk(0.16.0)で上記の取引を行うためのコードです。

```typescript
import * as dotenv from 'dotenv';
import { TransactionService, Listener, Account, PublicAccount, Mosaic, UInt64,
 TransferTransaction, Deadline, EmptyMessage, AggregateTransaction,
 HashLockTransaction, NetworkCurrencyMosaic, TransactionHttp,
 NamespaceId } from 'nem2-sdk';
import { filter, mergeMap } from 'rxjs/operators';

dotenv.config();

const transactionService = new TransactionService(process.env.API_ENDPOINT);
const listener = new Listener(process.env.API_ENDPOINT);
const networkType = Number(process.env.NETWORK_TYPE);

const gameCompanyAccount = Account.createFromPrivateKey('GAME_COMPANY_PRIVATE_KEY', networkType);
const alicePubAccount = PublicAccount.createFromPublicKey('ALICE_PUBLIC_KEY', networkType);
const bobPubAccount  = PublicAccount.createFromPublicKey('BOB_PUBLIC_KEY', networkType);

const gameCurrency = new Mosaic(new NamespaceId('game_company.currency'), UInt64.fromUint(100));
const gameItem = new Mosaic(new NamespaceId('game_company.item'), UInt64.fromUint(1));

const tx1 = TransferTransaction.create(
  Deadline.create(),
  bobPubAccount.address,
  [gameItem],
  EmptyMessage,
  networkType,
);

const tx2 = TransferTransaction.create(
  Deadline.create(),
  alicePubAccount.address,
  [gameCurrency],
  EmptyMessage,
  networkType
);

const dummyTx = TransferTransaction.create(
  Deadline.create(),
  gameCompanyAccount.address,
  [],
  EmptyMessage,
  networkType
);

const aggregateTx = AggregateTransaction.createBonded(
  Deadline.create(),
  [
    tx1.toAggregate(alicePubAccount),
    tx2.toAggregate(bobPubAccount),
    dummyTx.toAggregate(gameCompanyAccount.publicAccount),
  ],
  networkType,
  [],
  UInt64.fromUint(200000)
);

const signedTx = gameCompanyAccount.sign(aggregateTx, process.env.GENERATION_HASH);

console.log(`txHash: ${signedTx.hash}`);

const hashLockTx = HashLockTransaction.create(
  Deadline.create(),
  NetworkCurrencyMosaic.createRelative(10),
  UInt64.fromUint(480),
  signedTx,
  networkType,
  UInt64.fromUint(18400)
);

const hashLockTxSigned = gameCompanyAccount.sign(hashLockTx, process.env.GENERATION_HASH);

listener.open().then(() => {
  transactionService.announceHashLockAggregateBonded(hashLockTxSigned, signedTx, listener)
  .subscribe((x) => {
    console.log(x);
    listener.close()
  }, (err) => {
    console.error(err);
    listener.close();
  });
});
```

### ポイント

- 手数料代払いといいながら、実際にはゲーム会社のアカウント`gameCompanyAccount`でaliceとbobのゲーム内通貨とアイテムの取引のトランザクションを作っているだけです。
- とはいいながら、内部トランザクションに`gameCompanyAccount`が作成するトランザクションが1つでも含まれていないと、最終的にトランザクションが無効になってしまうため、`gameCompanyAccount`自身に空のトランザクションを送るダミーのトランザクションの入れています。
- トランザクション手数料はあらかじめ十分な量を積んでおかないと、トランザクションが無効になってしまいます。

# まとめ

パブリックチェーンを利用する際のボトルネックとして、ユーザがそのチェーンの基軸通貨で手数料を払わないといけないという課題がありますが、NEMの次期コアエンジンCatapultのアグリゲートトランザクションを利用することによって、トランザクション手数料の代払いを行うことができるようになります。
手数料代払いは、Catapult上で作成されるどんなモザイク(トークン)にも適用することが可能で、比較的容易に手数料代払いの仕組みを実現することが可能です。

これにより、パブリックチェーンを利用する人が**必ずしも手数料を負担する必要がなくなり**、今までより多くの人がブロックチェーンの恩恵を受けやすくなると思います。