Hyperledger Fabric chaincode lifecycle について

# はじめに
Hyperledger Fabric の[公式ドキュメント](https://hyperledger-fabric.readthedocs.io/ja/latest/chaincode_lifecycle.html)を参考に、chaincode lifecycle について調べた内容をまとめました。

### 目次
1. 概要
1. v2.0の変更点
1. chaincode definition
1. デプロイメント
1. 検証

# 概要
**chaincode lifecycle** とは、チャネル上でチェーンコードを運用する際、チェーンコードのインストールやアップグレード等をどのように合意し実施するかといったプロセスのことです。

v1.4では、１組織がエンドースメントポリシーなどのパラメータを設定できてしまう・チャネル上のチェーンコードをアップグレードできてしまうなどの運用上の問題点がありました。v2.0では複数組織の合意が必要となり、より安全な運用が可能となっています。

またv2.0ではチャネル内で異なったチェーンコードを使用できるようになったのも大きな変化です。これにより、トランザクションに対して組織独自の検証を実施することが可能となりました。

以降のセクションでは、上記をより詳細に解説します。

#### 設定方法
チャネルの [capability](https://hyperledger-fabric.readthedocs.io/ja/latest/capabilities_concept.html) をV2_0に設定することで、 Fabricのv2.0からの新しいチェーンコードライフサイクルを使用することができます。
V1_4に設定すれば、v2.0以降の Fabricで v1.4.x以前のライフサイクルを使用可能です。
V2_0の capability が有効になっているチャネルでは、v1.4.x以前の古いライフサイクルを使用して、 チェーンコードをインストール、インスタンス化、または更新することはできません。


# v2.0の変更点
v2.0での変更で、より安全・柔軟なチェーンコードの運用が可能になりました。これらの変更について、特に重要と思われる点を説明します。詳細は、[公式ドキュメント](https://hyperledger-fabric.readthedocs.io/ja/latest/chaincode_lifecycle.html)を参照ください。

##### 1. より安全な運用

- **デプロイするチェーンコードのパラメータに対して、複数組織の合意が必要に**
v1.x では一つの組織が他の全てのチャネルのメンバーにも適用されるチェーンコードのパラメータ(例えば、エンドースメントポリシーやChaincodeの初期化パラメータ)を設定することができたが、v2.xでは複数の承認が必要な設定が可能

- **アップグレードに対して、複数組織の承認が必要に**
v1.xでは、アップグレードのトランザクションは一つの組織から発行することができたが、2.xでは複数の承認が必要な設定が可能

##### 2. より柔軟な運用

- **同じパッケージで複数のチェーンコードを起動できる**
v1.x では、チェーンコードのインストール時に指定する名前とバージョンによって、チェーンコードが定義されていた。 v2.x では、同じ一つのチェーンコードパッケージで、異なる名前で複数回デプロイすることが可能。（同じチャネルあるいは異なるチャネルで）


- **チャネル内で異なったパッケージを使用できる**
必須とされている数の組織が、チェーンコードのトランザクションと同一の結果をエンドースしていれば、そのトランザクションは検証の後台帳にコミットされる。
各組織は独自の検証などのために、チェーンコードを拡張できる。


# chaincode definition
chaincode definitionとは、組織間で合意するチェーンコードのパラメータのことです。
チャネルのメンバーからchaincode definitionの承認が十分な数得られると、chaincode　 definitionをチャネルにコミットできます。以下がパラメータの例です。

- **名前 (Name):** チェーンコードを呼び出すときにアプリケーションが使用する名前
- **バージョン (Version):** 特定のチェーンコードパッケージに関連付けられたバージョン番号または値
- **エンドースメントポリシー (Endorsement Policy):** どの組織がトランザクション出力を実行および検証する必要があるかを示したもの。デフォルトでは、チャネル内の過半数の組織のエンドースが必要
- **ESCC/VSCCプラグイン (ESCC/VSCC Plugins):** このチェーンコードで使用されるカスタムエンドースメントまたはバリデーション (検証) プラグインの名前
- **初期化 (Initialization):** チェーンコードの呼び出しの前にInitを呼び出す必要があるかどうかを指定する 

例：

```
chaincode 'fabcar' on channel 'mychannel' Name: 'fabcar’, Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc
```

chaincode definition は以下の図のように、各組織上に定義された後、チャネルにコミットされます。

パラメータが共通していれば、各メンバーでチェーンコードが異なっていても同じチェーンコードとして使用できます。ただし、入力に対して全く同じ結果を返さなければ、トランザクションは失敗します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972236/3d26d173-60cd-d68c-f0ad-500a1f487550.png)


# デプロイメント
続いて、チェーンコードのデプロイ方法について概要を説明します。CLIを利用した手順の詳細は[公式ドキュメントのチュートリアル](https://hyperledger-fabric.readthedocs.io/ja/latest/deploy_chaincode.html)を参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972236/8d46eaea-fde1-ebe3-584c-979ac46f4701.png)

上図の通り、下記の手順でチェーンコードをデプロイします。④については過半数の組織が③までを完了後、代表の１組織が実施できます。図では表現していませんが、組織2も1と同様に①〜③を実施したものとします。
##### 手順
① 必要であれば、チェーンコードのパッケージ化を行う(すでにパッケージ化されたものを使用してもよい)
② Peerへパッケージをインストールする
③ 各組織が同じchaincode definition (同じパラメータ）を承認する
④ 各組織が承認したchaincode definition をチャネルにコミットする
⑤ 各Peerは、チャネルにコミットされたchaincode definition に従ってchaincode を使用する


以下、補足です。

- ①でPackage IDは異なっていてもよい
- デフォルトの設定では、チャネルの過半数が同じchaincode definitionを承認していないと、channelへのコミットは失敗する
- 新しく追加された組織は、chaincode をインストールし、すでにあるchaincode definition を承認すれば、チェーンコードの使用を開始できる。(再コミットの必要はない)
- 新しく組織が追加された際、chaincode definitionのエンドースメントポリシーが過半数の承認で
あれば、その数は自動的に更新される。(組織が2→3であれば、EPは2/2の→2/3へ)
　(v1.4では、手動更新の必要があった)
- あるチェーンコードをインストールする必要がない組織については、そのチェーンコードをインストールせずにchaincode definitionを承認することができる。(過半数超えのため)

# 検証
組織1,2にGoとJavaScriptで記述された[チェーンコード(公式サンプルのfabcar)](https://github.com/hyperledger/fabric-samples/tree/master/chaincode/fabcar) をデプロイし、トランザクションが実施できることを確認します。手順の詳細は[公式ドキュメントのチュートリアル](https://hyperledger-fabric.readthedocs.io/ja/latest/deploy_chaincode.html)を参照してください。

#### 1. 準備

##### チェーンコード編集
サンプルのチェーンコードはGoとJavaScriptでデータの構造が異なっており、このまま使用するとトランザクションが失敗してしまうため、少し修正を行います。[fabcar.js](https://github.com/hyperledger/fabric-samples/blob/master/chaincode/fabcar/javascript/lib/fabcar.js)のinitLedger内、carsの各要素のcolorをcolourに変更し、以下のように順番を変えます。createChannelについても同様に変更し、docTypeについては削除します。

```
fabric-samples/chaincode/fabcar/javascript/lib/fabcar.js 

async initLedger(ctx) {
        console.info('============= START : Initialize Ledger ===========');
        const cars = [
            {
                make: 'Toyota',
                model: 'Prius',
                colour: 'blue',
                owner: 'Tomoko',
            },
            {
                make: 'Ford',
                model: 'Mustang',
                colour: 'red',
                owner: 'Brad',
            },
            {
                make: 'Hyundai',
                model: 'Tucson',
                colour: 'green',
                owner: 'Jin Soo',
            },
            {
                make: 'Volkswagen',
                model: 'Passat',
                colour: 'yellow',
                owner: 'Max',
            },
            {
                make: 'Tesla',
                model: 'S',
                colour: 'black',
                owner: 'Adriana',
            },
            {
                make: 'Peugeot',
                model: '205',
                colour: 'purple',
                owner: 'Michel',
            },
            {
                make: 'Chery',
                model: 'S22L',
                colour: 'white',
                owner: 'Aarav',
            },
            {
                make: 'Fiat',
                model: 'Punto',
                colour: 'violet',
                owner: 'Pari',
            },
            {
                make: 'Tata',
                model: 'Nano',
                colour: 'indigo',
                owner: 'Valeria',
            },
            {
                make: 'Holden',
                model: 'Barina',
                colour: 'brown',
                owner: 'Shotaro',
            },
        ];

async createCar(ctx, carNumber, make, model, color, owner) {
        console.info('============= START : Create Car ===========');

        const car = {
            make,
            model,
            colour,
            owner,
        };
```

##### チャネル作成
テストネットワークを起動し、2つの組織が属するチャネル(mychannel)を作成します。

```
# cd fabric-samples/test-network
# ./network.sh up createChannel
...
========= Channel successfully joined =========== 
```
#### 2. パッケージ化
GoバージョンのFabcarチェーンコードをパッケージ化します。
ここでは、ファイル名をgo_fabcar.tar.gzとしてパッケージ化しています。

```
# cd ~/fabric-samples/chaincode/fabcar/go
# GO111MODULE=on go mod vendor

# cd ../../../test-network
# export PATH=${PWD}/../bin:$PATH
# export FABRIC_CFG_PATH=$PWD/../config/
# peer lifecycle chaincode package go_fabcar.tar.gz --path ../chaincode/fabcar/go/ --lang golang --label fabcar_1
```

JavaScriptも同様にjs_fabcar.tar.gzとしてパッケージ化します。

```
# cd ~/fabric-samples/chaincode/fabcar/javascript
# npm install

# cd ../../../test-network
# peer lifecycle chaincode package js_fabcar.tar.gz --path ../chaincode/fabcar/javascript/ --lang node --label fabcar_1
```

#### 3. インストール
続いて、組織1にgo_fabcar.tar.gz、組織2にjs_fabcar.tar.gzをインストールします。
**組織1**
最後の行では、次のchaincode definition 承認のため、パッケージIDを保存しています。インストール時の出力結果は例とは異なっているはずなので、パッケージIDは出力結果に合わせて保存してください。

```
# export CORE_PEER_TLS_ENABLED=true
# export CORE_PEER_LOCALMSPID="Org1MSP"
# export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
# export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
# export CORE_PEER_ADDRESS=localhost:7051
# peer lifecycle chaincode install go_fabcar.tar.gz
2020-02-12 11:40:02.923 EST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nIfabcar_1:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3\022\010fabcar_1" >
2020-02-12 11:40:02.925 EST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: fabcar_1:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3
# export CC_PACKAGE_ID1=fabcar_1:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3
```

**組織2**
組織1の時と同様、パッケージIDは出力結果に合わせてください。

```
# export CORE_PEER_LOCALMSPID="Org2MSP"
# export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
# export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
# export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
# export CORE_PEER_ADDRESS=localhost:9051
# peer lifecycle chaincode install js_fabcar.tar.gz
2020-02-12 11:40:02.923 EST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nIfabcar_1:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3\022\010fabcar_1" >
2020-02-12 11:40:02.925 EST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: fabcar_1:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3
# export CC_PACKAGE_ID2=fabcar_1:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3

```

#### 4. chaincode definition の承認
各組織でchaincode definition を承認します。現在組織2を操作しているので、組織2から承認します。続いて、組織1で承認を行います。

```
# peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 1.0 --package-id $CC_PACKAGE_ID2 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

# export CORE_PEER_TLS_ENABLED=true
# export CORE_PEER_LOCALMSPID="Org1MSP"
# export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
# export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
# export CORE_PEER_ADDRESS=localhost:7051
# peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 1.0 --package-id $CC_PACKAGE_ID1 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
```

#### 5. チャネルへのコミット
チャネルへchaincode definition をコミットします。

```
# peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 1.0 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
```

#### 6. 実行
チェーンコードを実行します。initLedgerを使用し初期化を行った後、createCarで新しい車を作成します。車のセットの読み込みには、queryAllCarsを使用します。

```
# peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n fabcar --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"initLedger","Args":[]}'

# peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}'
[{"Key":"CAR0","Record":{"make":"Toyota","model":"Prius","colour":"blue","owner":"Tomoko"}},
{"Key":"CAR1","Record":{"make":"Ford","model":"Mustang","colour":"red","owner":"Brad"}},
{"Key":"CAR2","Record":{"make":"Hyundai","model":"Tucson","colour":"green","owner":"Jin Soo"}},
{"Key":"CAR3","Record":{"make":"Volkswagen","model":"Passat","colour":"yellow","owner":"Max"}},
{"Key":"CAR4","Record":{"make":"Tesla","model":"S","colour":"black","owner":"Adriana"}},
{"Key":"CAR5","Record":{"make":"Peugeot","model":"205","colour":"purple","owner":"Michel"}},
{"Key":"CAR6","Record":{"make":"Chery","model":"S22L","colour":"white","owner":"Aarav"}},
{"Key":"CAR7","Record":{"make":"Fiat","model":"Punto","colour":"violet","owner":"Pari"}},
{"Key":"CAR8","Record":{"make":"Tata","model":"Nano","colour":"indigo","owner":"Valeria"}},
{"Key":"CAR9","Record":{"make":"Holden","model":"Barina","colour":"brown","owner":"Shotaro"}}]

# peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n fabcar --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"createCar","Args":["CAR11","Honda","Accord","Black","Tom"]}'

# peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}'
[{"Key":"CAR0","Record":{"make":"Toyota","model":"Prius","colour":"blue","owner":"Tomoko"}},
{"Key":"CAR1","Record":{"make":"Ford","model":"Mustang","colour":"red","owner":"Brad"}},
{"Key":"CAR11","Record":{"color":"Black","docType":"car","make":"Honda","model":"Accord","owner":"Tom"}},
{"Key":"CAR2","Record":{"make":"Hyundai","model":"Tucson","colour":"green","owner":"Jin Soo"}},
{"Key":"CAR3","Record":{"make":"Volkswagen","model":"Passat","colour":"yellow","owner":"Max"}},
{"Key":"CAR4","Record":{"make":"Tesla","model":"S","colour":"black","owner":"Adriana"}},
{"Key":"CAR5","Record":{"make":"Peugeot","model":"205","colour":"purple","owner":"Michel"}},
{"Key":"CAR6","Record":{"make":"Chery","model":"S22L","colour":"white","owner":"Aarav"}},
{"Key":"CAR7","Record":{"make":"Fiat","model":"Punto","colour":"violet","owner":"Pari"}},
{"Key":"CAR8","Record":{"make":"Tata","model":"Nano","colour":"indigo","owner":"Valeria"}},
{"Key":"CAR9","Record":{"make":"Holden","model":"Barina","colour":"brown","owner":"Shotaro"}}]
```

これで、組織でパッケージが異なっていても同じchaincode definiton のチェーンコードを使用できることが確認できました。