コンソーシアム型Ethereum: Kaleidoを利用したatomicなDvP（トークンスワップ）

【始めに】
元々、アドカレ用ではないのですが、空いてるのでリンク。
KaleidoのようなBaaSモデルが出てくることで、ブロックチェーンに馴染みのない（極論するとエンジニアでなくても）ブロックチェーンを意識せず実装出来て行くのは、今後のブロックチェーンベースのシステムを拡大していく上で、とても重要なことだと思っています。

---

【本文】
---

コンソーシアム型EthereumのBaaSモデルを提供するKaleidoには興味深い機能がいくつかあるが、そのうちの一つに"Token Swap"機能がある。
仕組みを簡単に説明すると、図1に示すようにHTLC(Hashed Timelock Contract)を利用することで、2者間で交換したいトークン（ERC20/ERC721)をコントラクト上でアトミックなDelivery v.s Payment（アセット同士だと、DvDと言っても良いか。）として実現するものである。

**[図1]**
![infographic_tokenswaps.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167054/f823eeaa-390b-b1b2-14c8-bb06f06a828d.png)
（出典： https://api.kaleido.io/tokenswap.html )


Kaleidoの最大の利点は、こうしたアトミックスワップ機能をREST API経由で簡単に実装できる点にある。

今回、簡易的にKaleidoが提供するToken Swap機能を実装したので、紹介する。

【簡単なフロー】
---

イメージとしては、以下の流れとなる。


（1)
`user1が交換したいトークンをuser2宛にdeposit。`
↓
（2）
`hash-lockとsecret及びtrade-id（user2引出し用）が生成される。`
↓
（3）
`user2は交換したいトークンをuser1宛にdeposit。
この時、hash-lockを引数に添える`
↓
（4）
`trade-id（user1引出し用）が生成される。
（この段階でDvP自体は完了しており、後続処理で各userが引き出すだけ。）`
↓
（5）
`user1はuser2がdepositしたトークンを引き出す為、secret及びtrade-id（user1引出し用）を引数に添える`
↓
（6）
`user2はuser1がdepositしたトークンを引き出す為、secret及びtrade-id（user2引出し用）を引数に添える。`

以上の流れとなる。
本格的に実装する際には、hash-lock, secret, trade-idをどうやって上手く2者間のみで共有するか、少し実装方法について検討は必要。
（WhisperのようなEthereumのネットワークを利用した通信相手を限定できるメッセージングを利用すれば良いのかも。）

（備考）
なお、hash-lock, secret, trade-idはワンタイムものなので、一度使ってしまえば、再利用不可となるのと、時限性がある（デフォルト設定は1時間だが、自分で設定可能）ので、時間内にDvPが成立しなければ、勝手にフェールして、depositしたトークンはそれぞれの所有者に戻される。

【今回の環境構成】
---

AWS環境上で構築

+ ユーザ認証：AWS Amplify
+ フロントエンド： Vue.js
+ バックエンド： Nodejs + Express
+ off-chain DB： DynamoDB
+ ブロックチェーン： Kaleido ( Ethereum: geth + PoA）　*ノード：2台+ 1台（モニター）

【デモ：トークンスワップ】
---


![output_tokenswap_kaleido.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167054/a11a4e69-1cec-4938-54e4-1d7a7e7b2704.gif)




【実際のコード】
---

一部、割愛しているが、フロントから呼び出され、KakeidoのREST APIへ連携し実行するバックエンド処理は以下のコード事例としている。

**Token Swap**
（トークンをHTLCにデポジット）

```javascript:backend_tokenSwap.js
    let rawData = {
      'token-address': contractAddress,
      'from': fromAddress,
      'to': toAddress,
      'amount': sendAmount,
      'token-id': tokenId,
      'hash-lock': hashlock
    };
    
    let rawDataStr = JSON.stringify(rawData);
    var options = { 
      host: api_host_tkswap,
      path: '/api/v1' +  '/trades',
      method: 'POST',
      headers:
       { accept: 'application/json',
        'Authorization': 'Basic ' + new Buffer.from(from_userid + ':' + from_password).toString('base64'),
         'Content-Type': 'application/json',
         'Content-Length': rawDataStr.length,
       },
      body: rawDataStr,
      json: true };

    let inner_req = https.request(options, (inner_res) => {
      let body ='';
      inner_res.setEncoding('utf8');
      inner_res.on('data', function (chunk) {
        console.log('BODY: ' + chunk);
      });
    });
    inner_req.on('error', (e) => {
      console.log('problem with request: ' + e.message);
    });
    inner_req.write(rawDataStr);
    inner_req.end();
  }
```

*ご参考：REST API: "Initiate or respond to a token swap trade"*
https://api.kaleido.io/tokenswap.html#tag/Token-Trades/paths/~1trades/post

**Claim**
（depositしたトークンの引き出し）

```javascript:backend_tsClaim.js
    let rawData = {
      'secret': secret,
      'from': fromAddress
    };
    
    let rawDataStr = JSON.stringify(rawData);
    
    var options = { 
      host: api_host_tkswap,
      path: '/api/v1' +  '/trades' + '/' + tradeId + '/withdraw',
      method: 'POST',
      headers:
       { accept: 'application/json',
        'Authorization': 'Basic ' + new Buffer.from(from_userid + ':' + from_password).toString('base64'),
         'Content-Type': 'application/json',
         'Content-Length': rawDataStr.length,
       },
      body: rawDataStr,
      json: true };
  
    let inner_req = https.request(options, (inner_res) => {
      let body ='';
      inner_res.setEncoding('utf8');
      inner_res.on('data', function (chunk) {
        console.log('BODY: ' + chunk);
        };
      });
    });
    inner_req.on('error', (e) => {
      console.log('problem with request: ' + e.message);
    });
    inner_req.write(rawDataStr);
    inner_req.end();
  }
```
*ご参考： REST API: "Withdraw / claim the token(s) from a trade"*
https://api.kaleido.io/tokenswap.html#tag/Token-Trades/paths/~1trades~1{trade_id}~1withdraw/post

【最後に】
---

Kaleidoの強みは簡単にブロックチェーン環境を構築出来、基本的な機能であればREST APIを利用して比較的簡単に（少なくともブロックチェーンをそれほど意識しなくても良い。）システム実装できる点にあると思う。
今後、こうしたBaaSモデルはエンタープライズ向けのみならず個人や中小企業向けにも広がっていくのではないかと思う。
また、Kaleidoはパブリック型Ethereumとのブリッジ機能（図2参照）も将来的に実装していくことになり、パブリックネットワークとkaleidoに構築した各ブロックチェーンネットワークとのインターオペラビリティも意識している点も、注目して良いと思う。

**[図2]**
![73235255_2513563715540882_3078712169286598656_n.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167054/39e9ca93-9c92-1551-713f-489ba7cc6b18.jpeg)
（出典： https://github.com/ConsenSys/ERC1400 )

>
＜コメント＞
Consensys社によるプログラマブル証券のERC規格（ERC1400）の実装案。発行や取引はkaleido上で行い、長期間保有する際のカストディとして、パブリック型Ethereumのメインネットを利用するブリッジ型の仕組み。
こうしたブリッジ型の仕組みであれば、トークン発行や取引（移転）時のgasコストや速度を大幅に改善することができ、取引しない間（ロックアップ期間等も含め。）は、パブリックネットワーク上で安全に保管しておく、と言うことが可能になる。
>