【Blockchain】Hyperledger Fabric 2.0αの新機能「FabToken」を試してみた

こんにちは、(株)日立製作所 研究開発グループ サービスコンピューティング研究部の中島です。
今回は、コンソーシアム型のブロックチェーン(BC)基盤の一つであるHyperledger Fabricの最新トライアル版 (2.0α) において、試験的にリリースされた新機能である、FabToken を試用してみましたので、その内容についてご紹介します。

# FabToken とは 
ブロックチェーンプラットフォームである Hyperledger Fabric では、ブロックチェーンの仕組みにより、管理されるデータに耐改ざん性が提供され、セキュリティリスクが抑制されています。

一方で、二重支払いなど、データを価値のある資産 (Token) として扱うための機能を直接的には提供しておらず、 Hyperleder Fabric を利用するアプリ側での考慮・実装が必要でした。

FabToken は、HyperLedger 上で扱われるデータを、価値のある資産 (Token) として表現し、Fabric SDK の API 及び Token CLI により、Token の発行、譲渡、引換をおこなうための UTXO 型のToken管理システムです。FabToken の提供により、資産をデータとして管理する際のリスクと難しさの低減を目指しており、延いては、Hyperledger Fabric を Token エコノミーシステムへと繋げるための、試験的な機能とも言えるかと思います(※)。

(※) Fabric2.0α のみに試験導入されており、正式リリースの予定は決まっておりません。

# FabToken における Token

前述の通り、Token とはデータを単なる情報ではなく、データを価値のある資産として表現するものです。

FabToken では、Tokenの状態とTokenの所有権を一意に示すことができます。また、Tokenの所有者のみがTokenを譲渡でき、二重使用できないことを保証しています。
この特徴により、Tokenとして、金融商品などの有形資産、ロイヤリティポイントなどの無形資産いずれも表現可能ですし、別の切り口では、Fungible な資産 (統合や分割可能なモノ(500円、等))も、NonFungible な資産(この世に一つしかないモノ(Aさんの家、など))も表現可能です。

Hyperledger Fabric 以外のブロックチェーン基盤の中には、FabTokenと同様の仕組みが備わっているものもあります。有名なところでは、金融系に強みを持つブロックチェーン基盤 Corda では、State という概念で、Hyperledger Fabric でいう Token の概念が表現されています。ご存知の方は、その辺りをイメージしていただければ、理解がしやすいかと思います。

# FabToken のライフサイクル
## 1. Token の発行トランザクション

Token 発行者(Issuer)が、Token 所有者(になるメンバ)に対して、Token を割り当てるトランザクションです。このために、IssuingPolicy を事前に定義し、Token 発行権限を与えることで、任意のメンバを Issuer に設定しておくことが必要です。Token を発行できるのは、この Issuer のみです。

Issuer は、発行トランザクション(下記3つの属性を指定する)を使用して、台帳にToken を追加できます。
  - Owner: Token 所有者。Token を譲渡・引換できるチャネルメンバ。
  -  Type:  Token が示す資産の種類。USD、EUR、BFYNcoins など。
  ※IssuingPolicy でどのようなTypeのToken を発行できるかを指定可能(α版ではANYのみサポートで設定できません)。
  - Quantity: Token が示す資産の数量。

## 2. Token の譲渡トランザクション
Token の所有者が、Token で表される資産を、新しい所有者に譲渡するトランザクションで、
FabToken をブロックチェーン上で管理するための主要なトランザクションになります。

以下の値を指定してToken を別のメンバに譲渡します。
  - Token ID: 譲渡したいToken の ID
  - Quantity: 譲渡されるToken により示される資産の金額
  - Recipient: アセットを転送したいメンバの MSP識別子 (*1)

(*1)Hyperledger Fabric の MSP (メンバシップサービス) の仕組みを使用して、Token の所有者のIDを認証・公開鍵と秘密鍵を管理しています。

FabToken は UTXO 型のToken管理システムと言いましたが、UTXO 型のモデルでは、ブロックチェーン上のトランザクションは、入力と出力の繰り返しで行われます。FabTokenでも、一般的な UTXO型のモデルを踏襲しており、トランザクションの入力として、他のトランザクションの出力として作成された"未消費" の Token を指定し、トランザクションの実行により、Token が消費され、トランザクションの結果として、新しい"未消費"の Token が生成されます。

Token の譲渡のトランザクションによる Token の"消費"により、Token は StateDB から削除されます。これにより、前の所有者がその Token を使用したりアクセスしたりできなくなります。そして、資産を譲渡された所有者が、譲渡のトランザクションにり生成された新しいToken の所有者になります。

Fungible な資産の一部を譲渡する場合、自分に残る資産に相当する新しい Token も、自動的に生成されます。一部を譲渡するケースでは、二人の所有者が同じ Token ID を持つことになるため、Index 番号を付けて区別します。Original の所有者の方に Index 番号が付きます。
ここでは、頭の片隅においておいてください。後ほど実際に Token CLI コマンドを叩いてみてみます。

## 3.  Token の引換トランザクション：
Token の所有者が、Token を削除するトランザクションになります。

以下の値を指定して、"資産"を削除し、誰からも使用できない状態にします(空の所有者に譲渡されます)。
  - TokenID: 引換したいToken の ID
  - Quantity: 引換したいToken によってあらわされる資産の数量

サプライチェーン内の資産が最終目的地に到達した場合、金融資産がその期間に達した場合など、資産をメンバが管理する必要がなくなり、Token を引き換えます。Fungible な資産の資産の一部を引き換える場合、新しい TokenID に付け替えられたうえで、数量が差し引かれます。

## 4. Token の参照トランザクション
Token の所有者が、未消費の Token の情報を取得するトランザクションです。

以下の値を指定して実行します。
  - TokenID: 所有しているTokenの ID
  - Type: 所有しているToken が表す資産のタイプ
  - Quantity: 所有している資産の16進数で表された数量

前述の1. ～3. のトランザクション実行の結果を確認するなど、Token の状況を確認するための利用します。

# FabToken トランザクションの実行
長い説明が終わり、ここからようやく、具体的に Token CLI を使って FabToken トランザクションを実行してみます。
ここで試すユースケースは、Token を生成し、組織間 (Org1とOrg2の間) でToken を譲渡し、Token を削除する、という Token のライフサイクルを一通り辿るものです。

FabToken を実行における前提環境は以下になります。
- Hyperledger Fabric (v2.0.0-alpha)
- Fabric-Samples  (v2.0.0-alphaに対応したVersion)

Fabric 及び Fabric-Samples の構築については、Qiita にも多くの記事がありますし、公式ドキュメントもわかりやすく詳しいので、そちらを参考にして、セットアップしてみてください。

## 事前準備 

まずは、FabToken を扱うためのブロックチェーンネットワークを生成します。

Fabric-Samples に含まれる byfn.sh スクリプト (Building Your First Network スクリプト) を用いて、2つの組織 Org1 と Org2 を備えたネットワークを生成します。各組織のノードはそれぞれ 2 個ずつで、mychannel というチャネルを作成の上、そのチャネルに各ノードが参加します。

チャネルはブロックチェーンネットワークを論理分割した単位で、台帳もチャネルごとに管理されます。FabToken も Channel 毎の台帳に保存され、チャネルに所属する任意のメンバが所有することになります。
FabToken を利用するには、まず以下の2種類の設定ファイルを用意する必要があります。

-  configfile: 各組織ごとに、組織が信頼する prover peer、トランザクションを送信する ordering service に関する情報を定義するためのファイルです。以下が、Org1の configfile です。

```
{
  "ChannelID":"",
  "MSPInfo":{
    "MSPConfigPath":"",
    "MSPID":"Org1MSP",
    "MSPType":"bccsp"
  },
  "Orderer":{
    "Address":"orderer.example.com:7050",
    "ConnectionTimeout":0,
    "TLSEnabled":true,
    "TLSRootCertFile":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.ex
ample.com/msp/tlscacerts/tlsca.example.com-cert.pem",
    "ServerNameOverride":""
  },
  "CommitterPeer":{
    "Address":"peer0.org1.example.com:7051",
    "ConnectionTimeout":0,
    "TLSEnabled":true,
    "TLSRootCertFile":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.
example.com/tls/ca.crt",
    "ServerNameOverride":""
  },
  "ProverPeer":{
    "Address":"peer0.org1.example.com:7051",
    "ConnectionTimeout":0,
    "TLSEnabled":true,
    "TLSRootCertFile":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.
example.com/tls/ca.crt",
    "ServerNameOverride":""
  }
```

Org1 は自身を prover peer として使用するため、ProverPeer セクションでエンドポイント情報を記述しています。ここで示したものと同様のファイルを組織ごとに用意する必要があります。以下が Org2 の configfile です。 

```
{
  "ChannelID":"",
  "MSPInfo":{
    "MSPConfigPath":"",
    "MSPID":"Org2MSP",
    "MSPType":"bccsp"
  },
  "Orderer":{
    "Address":"orderer.example.com:7050",
    "ConnectionTimeout":0,
    "TLSEnabled":true,
    "TLSRootCertFile":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.ex
ample.com/msp/tlscacerts/tlsca.example.com-cert.pem",
    "ServerNameOverride":""
  },
  "CommitterPeer":{
    "Address":"peer0.org2.example.com:9051",
    "ConnectionTimeout":0,
    "TLSEnabled":true,
    "TLSRootCertFile":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.
example.com/tls/ca.crt",
    "ServerNameOverride":""
  },
  "ProverPeer":{
    "Address":"peer0.org2.example.com:9051",
    "ConnectionTimeout":0,
    "TLSEnabled":true,
    "TLSRootCertFile":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.
example.com/tls/ca.crt",
    "ServerNameOverride":""
  }
```

- share.json: Token の譲渡先と、譲渡する Token の量を記述するためのファイルです。Token の譲渡のコマンドで引数として指定されます。

```
[
    {
    "recipient":"Org2MSP:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp",
    "quantity":"50"
    }
]
```

必要なファイルの準備が整いましたら、以下のコマンドによりネットワークを生成します。

```
byfn.sh up
```

このコマンドにより、Token の発行と転送に使用するノード、ordering service、チャネル等を作成します。byfn.sh の中で、Query のテスト実行などもおこなわれますが、実行しなくても問題ありません。
以下に実行結果を示します。

```
$ ./byfn.sh up
Starting for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] Y
proceeding ...
LOCAL_VERSION=2.0.0
DOCKER_IMAGE_VERSION=2.0.0-alpha
=================== WARNING ===================
  Local fabric binaries and docker images are  
  out of  sync. This may cause problems.       
===============================================
Creating network "net_byfn" with the default driver
Creating volume "net_orderer.example.com" with default driver
Creating volume "net_peer0.org1.example.com" with default driver
Creating volume "net_peer1.org1.example.com" with default driver
Creating volume "net_peer0.org2.example.com" with default driver
Creating volume "net_peer1.org2.example.com" with default driver
Creating orderer.example.com    ... done
Creating peer0.org1.example.com ... done
Creating peer1.org2.example.com ... done
Creating peer1.org1.example.com ... done
Creating peer0.org2.example.com ... done
Creating cli                    ... done
CONTAINER ID        IMAGE                               COMMAND                  CREATED             STATUS                  PORTS                                NAMES
2b1b4f841829        hyperledger/fabric-tools:latest     "/bin/bash"              1 second ago        Up Less than a second                                        cli
7441a72a9d2d        hyperledger/fabric-peer:latest      "peer node start"        3 seconds ago       Up 1 second             7051/tcp, 0.0.0.0:9051->9051/tcp     peer0.org2.example.com
4124a0e59d0b        hyperledger/fabric-peer:latest      "peer node start"        3 seconds ago       Up Less than a second   7051/tcp, 0.0.0.0:8051->8051/tcp     peer1.org1.example.com
3726f23eead2        hyperledger/fabric-orderer:latest   "orderer"                3 seconds ago       Up 1 second             0.0.0.0:7050->7050/tcp               orderer.example.com
64d983e43b01        hyperledger/fabric-peer:latest      "peer node start"        3 seconds ago       Up 2 seconds            7051/tcp, 0.0.0.0:10051->10051/tcp   peer1.org2.example.com
95780fcc2ce1        hyperledger/fabric-peer:latest      "peer node start"        3 seconds ago       Up 2 seconds            0.0.0.0:7051->7051/tcp               peer0.org1.example.com
1f185e99482a        couchdb:2.3                         "tini -- /docker-ent…"   6 days ago          Exited (0) 6 days ago                                        couchdb
a5b97bf6fc81        hyperledger/fabric-ca               "sh -c 'fabric-ca-se…"   6 days ago          Exited (2) 6 days ago                                        ca.example.com

 ____    _____      _      ____    _____ 
/ ___|  |_   _|    / \    |  _ \  |_   _|
\___ \    | |     / _ \   | |_) |   | |  
 ___) |   | |    / ___ \  |  _ <    | |  
|____/    |_|   /_/   \_\ |_| \_\   |_|  

Build your first network (BYFN) end-to-end test

Channel name : mychannel
Creating channel...
+ peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
+ res=0
+ set +x
2019-12-04 09:00:44.830 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-12-04 09:00:44.888 UTC [cli.common] readBlock -> INFO 002 Received block: 0
===================== Channel 'mychannel' created ===================== 

Having all peers join the channel...
+ peer channel join -b mychannel.block
+ res=0
+ set +x
2019-12-04 09:00:44.971 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-12-04 09:00:45.028 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer0.org1 joined channel 'mychannel' ===================== 

+ peer channel join -b mychannel.block
+ res=0
+ set +x
2019-12-04 09:00:48.102 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-12-04 09:00:48.154 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer1.org1 joined channel 'mychannel' ===================== 

+ peer channel join -b mychannel.block
+ res=0
+ set +x
2019-12-04 09:00:51.243 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-12-04 09:00:51.322 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer0.org2 joined channel 'mychannel' ===================== 

+ peer channel join -b mychannel.block
+ res=0
+ set +x
2019-12-04 09:00:54.438 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-12-04 09:00:54.510 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer1.org2 joined channel 'mychannel' ===================== 

Updating anchor peers for org1...
+ peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
+ res=0
+ set +x
2019-12-04 09:00:57.588 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-12-04 09:00:57.617 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
===================== Anchor peers updated for org 'Org1MSP' on channel 'mychannel' ===================== 

Updating anchor peers for org2...
+ peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org2MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
+ res=0
+ set +x
2019-12-04 09:01:00.695 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-12-04 09:01:00.710 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
===================== Anchor peers updated for org 'Org2MSP' on channel 'mychannel' ===================== 

+ peer lifecycle chaincode package mycc.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/abstore/go/ --lang golang --label mycc_1
+ res=0
+ set +x
===================== Chaincode is packaged on peer0.org1 ===================== 

Installing chaincode on peer0.org1...
+ peer lifecycle chaincode install mycc.tar.gz
+ res=0
+ set +x
2019-12-04 09:01:04.396 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGmycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca\022\006mycc_1" > 
2019-12-04 09:01:04.396 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: mycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca
===================== Chaincode is installed on peer0.org1 ===================== 

Install chaincode on peer0.org2...
+ peer lifecycle chaincode install mycc.tar.gz
+ res=0
+ set +x
2019-12-04 09:01:04.470 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGmycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca\022\006mycc_1" > 
2019-12-04 09:01:04.470 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: mycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca
===================== Chaincode is installed on peer0.org2 ===================== 

+ peer lifecycle chaincode queryinstalled
+ res=0
+ set +x
Installed chaincodes on peer:
Package ID: mycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca, Label: mycc_1
PackageID is mycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca
===================== Query installed successful on peer0.org1 on channel ===================== 

+ peer lifecycle chaincode approveformyorg --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name mycc --version 1 --init-required --package-id mycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca --sequence 1 --waitForEvent
+ set +x
2019-12-04 09:01:04.646 UTC [cli.lifecycle.chaincode] setOrdererClient -> INFO 001 Retrieved channel (mychannel) orderer endpoint: orderer.example.com:7050
2019-12-04 09:01:06.697 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [a313ee7859bc00460834e5f31abde3f7f9414472f7cfcb58366781fb6b2f2e5f] committed with status (VALID) at 
===================== Chaincode definition approved on peer0.org1 on channel 'mychannel' ===================== 

===================== Querying approval status on peer0.org1 on channel 'mychannel'... ===================== 
Attempting to Query approval status on peer0.org1 ...3 secs
+ peer lifecycle chaincode queryapprovalstatus --channelID mychannel --name mycc --version 1 --sequence 1 --init-required
+ res=0
+ set +x

{
	"Approved": {
		"Org1MSP": true,
		"Org2MSP": false
	}
}
===================== Query approval status successful on peer0.org1 on channel 'mychannel' ===================== 
===================== Querying approval status on peer0.org2 on channel 'mychannel'... ===================== 
Attempting to Query approval status on peer0.org2 ...3 secs
+ peer lifecycle chaincode queryapprovalstatus --channelID mychannel --name mycc --version 1 --sequence 1 --init-required
+ res=0
+ set +x

{
	"Approved": {
		"Org1MSP": true,
		"Org2MSP": false
	}
}
===================== Query approval status successful on peer0.org2 on channel 'mychannel' ===================== 
+ peer lifecycle chaincode approveformyorg --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name mycc --version 1 --init-required --package-id mycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca --sequence 1 --waitForEvent
+ set +x
2019-12-04 09:01:13.022 UTC [cli.lifecycle.chaincode] setOrdererClient -> INFO 001 Retrieved channel (mychannel) orderer endpoint: orderer.example.com:7050
2019-12-04 09:01:15.058 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [a424b503030ef1e71f63d9a511c2aaa7009721422134c9c0b2c9d0cab20d6071] committed with status (VALID) at 
===================== Chaincode definition approved on peer0.org2 on channel 'mychannel' ===================== 

===================== Querying approval status on peer0.org1 on channel 'mychannel'... ===================== 
Attempting to Query approval status on peer0.org1 ...3 secs
+ peer lifecycle chaincode queryapprovalstatus --channelID mychannel --name mycc --version 1 --sequence 1 --init-required
+ res=0
+ set +x

{
	"Approved": {
		"Org1MSP": true,
		"Org2MSP": true
	}
}
===================== Query approval status successful on peer0.org1 on channel 'mychannel' ===================== 
===================== Querying approval status on peer0.org2 on channel 'mychannel'... ===================== 
Attempting to Query approval status on peer0.org2 ...3 secs
+ peer lifecycle chaincode queryapprovalstatus --channelID mychannel --name mycc --version 1 --sequence 1 --init-required
+ res=0
+ set +x

{
	"Approved": {
		"Org1MSP": true,
		"Org2MSP": true
	}
}
===================== Query approval status successful on peer0.org2 on channel 'mychannel' ===================== 
+ peer lifecycle chaincode commit -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1 --sequence 1 --init-required
+ res=0
+ set +x
2019-12-04 09:01:23.420 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [fe34047104076a7d0e02b044448d03feee9d3c51d4f00461946f5e61dea2bf33] committed with status (VALID) at peer0.org2.example.com:9051
2019-12-04 09:01:23.420 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [fe34047104076a7d0e02b044448d03feee9d3c51d4f00461946f5e61dea2bf33] committed with status (VALID) at peer0.org1.example.com:7051
===================== Chaincode definition committed on channel 'mychannel' ===================== 

===================== Querying chaincode definition on peer0.org1 on channel 'mychannel'... ===================== 
Attempting to Query committed status on peer0.org1 ...3 secs
+ peer lifecycle chaincode querycommitted --channelID mychannel --name mycc
+ res=0
+ set +x

Committed chaincode definition for chaincode 'mycc' on channel 'mychannel':
Version: 1, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc
===================== Query chaincode definition successful on peer0.org1 on channel 'mychannel' ===================== 
===================== Querying chaincode definition on peer0.org2 on channel 'mychannel'... ===================== 
Attempting to Query committed status on peer0.org2 ...3 secs
+ peer lifecycle chaincode querycommitted --channelID mychannel --name mycc
+ res=0
+ set +x

Committed chaincode definition for chaincode 'mycc' on channel 'mychannel':
Version: 1, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc
===================== Query chaincode definition successful on peer0.org2 on channel 'mychannel' ===================== 
+ peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --isInit -c '{"Args":["Init","a","100","b","100"]}'
+ res=0
+ set +x
2019-12-04 09:01:49.579 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 
===================== Invoke transaction successful on peer0.org1 peer0.org2 on channel 'mychannel' ===================== 

Querying chaincode on peer0.org1...
===================== Querying on peer0.org1 on channel 'mychannel'... ===================== 
Attempting to Query peer0.org1 ...3 secs
+ peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
+ res=0
+ set +x

100
===================== Query successful on peer0.org1 on channel 'mychannel' ===================== 
Sending invoke transaction on peer0.org1 peer0.org2...
+ peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'
+ res=0
+ set +x
2019-12-04 09:01:52.796 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 
===================== Invoke transaction successful on peer0.org1 peer0.org2 on channel 'mychannel' ===================== 

Querying chaincode on peer0.org1...
===================== Querying on peer0.org1 on channel 'mychannel'... ===================== 
Attempting to Query peer0.org1 ...3 secs
+ peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
+ res=0
+ set +x

90
===================== Query successful on peer0.org1 on channel 'mychannel' ===================== 
Installing chaincode on peer1.org2...
+ peer lifecycle chaincode install mycc.tar.gz
+ res=0
+ set +x
2019-12-04 09:01:55.982 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGmycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca\022\006mycc_1" > 
2019-12-04 09:01:55.983 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: mycc_1:1a5cee241429a822f8b7282a9d196217e54efcc49e122d8675dfca2e20ef82ca
===================== Chaincode is installed on peer1.org2 ===================== 

Querying chaincode on peer1.org2...
===================== Querying on peer1.org2 on channel 'mychannel'... ===================== 
Attempting to Query peer1.org2 ...3 secs
+ peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
+ res=0
+ set +x

90
===================== Query successful on peer1.org2 on channel 'mychannel' ===================== 

========= All GOOD, BYFN execution completed =========== 


 _____   _   _   ____   
| ____| | \ | | |  _ \  
|  _|   |  \| | | | | | 
| |___  | |\  | | |_| | 
|_____| |_| \_| |____/  
```
コマンドが正常に完了すると、上記のような結果が表示され、ネットワーク作成成功です。

## Token の発行

まず、Token の発行に先立って、次のコマンドを使用して CLIコンテナにログインします。

```
docker exec -it cli bash
```

コンテナにログインした後、以下のコマンドを使用して、Org1 管理者として999 BYFNcoins相当のToken を発行します。 このコマンドはconfigorg1.jsonを使用してorg1の証明者ピアのエンドポイントを検索し、それを使用してトランザクションを処理します。 Org1管理者がトランザクションを送信しますが、Org1のUser1がToken 所有者になることに注意してください。

```
token issue --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg1.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp --channel mychannel --type BYFNcoins --quantity 999 --recipient Org1MSP:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp
```

Token の発行に成功すると、以下のような結果が表示されます。

```
Orderer Status [SUCCESS]
Committed [true]
```

処理としては成功したようですが、実際に Token が発行されたことをToken 表示コマンドで確認してみます。

```
# token list --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg1.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp --channel mychannel
{"tx_id":"923fb0320fa09a514c9bd440ea3f55a29322df31fa8643eef77d399b2574c784"}
[BYFNcoins,999]
```

999 BYFNcoins を示す Token が発行されているようです。Org1 が資産をデータ化形成しました。

## Token の譲渡

Token が生成されたので、Token の所有者である Org1 の User1は、発行した 999 BYFNcoins を別のユーザに譲渡することが可能となります。

ここでは例として、50BYFNcoins を Org2 の User2 に譲渡して、残りは手元に残すことにします。
譲渡する量 (50BYFNcoins) については、引数に指定した shares.json 内で設定しています。

```
# token transfer --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg1.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp --channel mychannel --tokenIDs '[{"tx_id":"923fb0320fa09a514c9bd440ea3f55a29322df31fa8643eef77d399b2574c784"}]' --shares /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/shares.json
```

Token の譲渡に成功すると、以下のような結果が表示されます。

```
Orderer Status [SUCCESS]
Committed [true]
```

処理は成功したようです。実際に Token が譲渡されたことを確認するため、Token 表示コマンドで Org1、Org2 それぞれの持つ Token 情報を確認してみます。


まずは org1 の結果です。

```org1
# token list --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg1.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp --channel mychannel
{"tx_id":"b6e9cc747083c9b87ee4c7397fc5ec40f33ce8903898c6056a890a1a70bc4a64","index":1}
[BYFNcoins,949]
```

次に org2 の結果です。

```
# token list --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg2.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp --channel mychannel
{"tx_id":"b6e9cc747083c9b87ee4c7397fc5ec40f33ce8903898c6056a890a1a70bc4a64"}
[BYFNcoins,50]
```

50BYFNcoins の譲渡に成功したようです。
譲渡しなかった残りの BYFNcoins はOrg1のUser1の資産として残ったままであることがわかります。

結果をよく見ると分かるのですが、これらの TX_ID はもともと Org1 の User1 が保有していた資産に振られた TX_ID とは別の新しい ID が割り振られています。
また、残ったままの資産と譲渡された資産には同じ TX_ID が付いていますが、元々の所有者である Org1 の User1 の  Token には、TX_IDに加えて index 番号("index":1)が付いており、これにより、各 Token を識別可能になっています。

続いて、保有している以上の資産を譲渡できるか試してみます。まず shares.json の "quantity" の値を 10000 にします。

```
[
    {
    "recipient":"Org2MSP:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp",
    "quantity":"10000"
    }
]
```

そして、以下の通り、Org1 の User1 の資産から Org2 の User2 に、10000 BYFNcoins の譲渡を試みます。

```
# token transfer --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg1.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp --channel mychannel --tokenIDs '[{"tx_id":"b6e9cc747083c9b87ee4c7397fc5ec40f33ce8903898c6056a890a1a70bc4a64", "index":1}]' --shares /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/shares.json
transfer: failed invoking transfer [mychannel][/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp][][[{"tx_id":"b6e9cc747083c9b87ee4c7397fc5ec40f33ce8903898c6056a890a1a70bc4a64", "index":1}]][/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/shares.json]: error from prover: total quantity [949] from TokenIds is less than total quantity [10000] for transfer
```

資産が足りないので譲渡できません、という旨のエラーがでていることがわかります。悪いことはできません。

## Token の引換え(削除)

最後に BYFNcoins の資産の引換えおこなってみます。他の操作同様、Token の所有者のみがTokenを引換可能です。

以下のコマンドで、Org2 も持つ 25BYFNcoinsを引換えてみます。

```
# token redeem --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg2.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp --channel mychannel --tokenIDs '[{"tx_id":"b6e9cc747083c9b87ee4c7397fc5ec40f33ce8903898c6056a890a1a70bc4a64"}]' --quantity 25
```

Token の引換えに成功すると、以下のような結果が表示されます。

```
Orderer Status [SUCCESS]
Committed [true]
```

確かに Token が引換えされたことをToken 表示コマンドで確認してみます。

```
# token list --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg2.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp --channel mychannel
{"tx_id":"054c3143dfbbaaad7fb41f5a5564df0247dc4c04646bb8d1a851db3636425faa","index":1}
[BYFNcoins,25]
```

25BYFNcoins のみが残っていることがわかります。
ここでも、トランザクションの出力として作成された新しい Token には、新しい tx_id が割り振られています。

次に、別のユーザーに属するToken を引き換えられるか試してみます。
以下のコマンドを使用して、Org2としてOrg1に属する50 BYFNcoins相当のToken を引き換えようとしてみます。

```
# token redeem --config /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/configorg2.json --mspPath /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp --channel mychannel --tokenIDs '[{"tx_id":"b6e9cc747083c9b87ee4c7397fc5ec40f33ce8903898c6056a890a1a70bc4a64", "index":1}]' --quantity 25
```

結果は以下の通り、Token の所有者以外は Token に対するトランザクションの実行は確かにできないようです。資産を取られなくて良かった。
```
error from prover: input TokenId (b6e9cc747083c9b87ee4c7397fc5ec40f33ce8903898c6056a890a1a70bc4a64, 1) does not exist or not owned by the user
```

# FabToken のトランザクション(処理)フロー
次に、FabToken のトランザクションにおけるシステム内部での処理の流れを簡単に紹介しておきます。

Hyperledger Fabric をご存知の方は、Hyperledger Fabric (v 1.0以降)では、大きく分けて Endorsement と Commit の二段階のステップがあることをご存知かと思います。

Hyperledger Fabric の通常のトランザクションと、FabToken におけるトランザクションの最も大きな違いは、FabToken のトランザクションは、Endorsementフローがバイパスされるという点にあります！
これは、Token は、所有者のみが譲渡、引換できるので、他者の Endorsement は不要という考え方に基づいているとのことで、Hyperledger Fabric におけるベースとなるアーキとは異なる方式を選んでいます。

Token CLI や Fabric SDK に利用される FabToken Client は、"prover peer" という信頼できる peer を利用して、Token トランザクションに対して以下の検証を実施します。

- Token 発行のトランザクション:
   - prover peer は、要求された操作が、生成しようとしている Token に関連するポリシ ( IssuingPolicy) を満たすことを検証します。

- Token 譲渡のトランザクション、Token 引換えのトランザクション: 
   - prover peer は、入力Token が未消費で、トランザクション実行要求者が所有者である Token であるかどうかを検証します。
   - prover peer は、入出力Token がすべて同じ type であることをチェックし(別の type のToken を同時に入力できません)、出力Token が入力Token と同じ type で、同じ quantity であることをチェックします。


FabToken Client は、"prove peer" による Tokenトランザクションの検証を通過した後、ordering サービスにトランザクションを送信し、ordering サービスがトランザクションを committing peer に送ります。committing peer は、ordering サービスから送られてきたトランザクションが、UTXO モデルに準拠していることと、Token に関連づく資産が二重に消費されていないことを確認します。
以上が、FabToken のトランザクションの基本的な流れになります。

# まとめ
長々と説明してきましたが、FabToken の機能はまだまだ改良の余地があるということで、Hyperledger Fabric 2.0 の正式版リリースからはドロップする可能性が高いようです (2019/12時点でmaster branch から削除されているようです)。

将来バージョンでどのように姿を変えて出てくるかはわかりませんが、Hyperledger Fabric 2.0α における FabToken で採用された UTXO 型のモデルや  prover peer などの考え方が重要になることは確かで、何かしらの形で踏襲されるのではないかと思います。

今後もこの動きに注目していきたいと思います。

# 参考
公式ドキュメントにおける紹介
<https://hyperledger-fabric.readthedocs.io/en/latest/whatsnew.html>

How to perform token operation
<https://fabric-sdk-node.github.io/master/tutorial-fabtoken.html>

ブロックチェーンの特徴やユースケースについて
<https://www.hitachi.co.jp/products/it/blockchain/index.html>