はじめに
前回はHyperledger Fabricの2つの特徴を紹介しました。1つはPermissionedであること、もう1つはExecute-Order-Validateアーキテクチャであることでした。今回はHyperledger Fabricを動かしながら、これらの詳細を解説していきます。
Hyperledger Fabric Samples
Hyperledger Fabricでは、Hyperledger Fabricの操作や機能の確認、アプリケーション構築を学ぶためにサンプル(fabric-samples)が用意されているので、これを利用します。サンプルを動かすためにはdockerとdocker-compose、golangが必要です。
なお、サンプルを動かすために必要なバイナリやdockerコンテナ、サンプルのソースコードを取得するために、簡単なスクリプトが用意されています(https://hyperledger-fabric.readthedocs.io/en/master/install.html)。
fabric-samplesのtest-networkディレクトリに移動してnetwork.shスクリプを実行します。このスクリプトは証明書を生成し、docker-composeで3つのコンテナを起動します。
01 | $ cd fabric-samples/test-network/ |
03 | README.md addOrg3 configtx docker network.sh organizations scripts system-genesis-block |
05 | Starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb' with crypto from 'cryptogen' |
08 | DOCKER_IMAGE_VERSION=2.2.0 |
09 | /home/ubuntu/fabric-samples/test-network/../bin/cryptogen |
11 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
12 | 2ee6d87fb94d hyperledger/fabric-orderer:latest "orderer" 2 seconds ago Up Less than a second 0.0.0.0:7050->7050/tcp orderer.example.com |
13 | d17e7e35e5e8 hyperledger/fabric-peer:latest "peer node start" 2 seconds ago Up 1 second 0.0.0.0:7051->7051/tcp peer0.org1.example.com |
14 | 544d6a99f3cf hyperledger/fabric-peer:latest "peer node start" 2 seconds ago Up Less than a second 7051/tcp, 0.0.0.0:9051->9051/tcp peer0.org2.example.com |
コンテナを起動すると、下図のような構成になっています。
このfabric networkにはOrderer、Org1、Org2の3つの組織があり、ordererコンテナと2つのpeerコンテナの計3つのコンテナが動いています。また、それぞれの組織にはCA(認証局)やtls、adminの証明書、org1とorg2にはuser1の証明書が生成されています。
各組織の証明書はorganizationsディレクトリに生成されます。例えば、ordererの証明書はorganizations/ordererOrganizations/example.comに入っています。同様にorg1とorg2の証明書もorganizations/peerOrganizations/org1.example.comやorganizations/peerOrganizations/org2.example.comに入っています。
Permissionedを実現するGenesis block
ブロックチェーンの最初のブロックのことを「genesis block」と呼びます。最初のブロックなので中は空であったり、bitcoinではTimes誌の見出しが書かれていたりします。
Hyperledger FabricはPermissionedを実現するためgenesis blockに参加組織のリストや証明書、channelに関する設定情報を入れています。
Channelでは、fabric networkの中にいくつか小さいグループを作り、そのグループ間でデータを共有することができます。例えば、3つの組織でfabric networkを作るとしたとき、下図のようにchannelを作ることができます。Channel Aには組織1と組織2、Channel Bには組織2と組織3のようにプライベート空間を作り、その中で情報共有することが可能です。
Fabric networkにはorderer system channelという特別なchannelが存在します。これはchannel(application channelとも呼ぶ)の作成を許可された組織のリストを含みます。
それでは、System channelの中身がどのようになっているのか確認してみましょう。Genesis blockはsystem-genesis-blockディレクトリに生成されています。
1 | $ ls system-genesis-block/ |
Genesis blockの中身を確認してみます。
01 | $ strings system-genesis-block/genesis.block |
02 | system-channel*@c401aa2f9da7e46d564f6cba344873d303f129ea244f83882beb8d63b19afdc1 |
06 | -----BEGIN CERTIFICATE----- |
07 | MIICPTCCAeOgAwIBAgIQDYCeY66vNwmM+gSw/PcAITAKBggqhkjOPQQDAjBpMQsw |
08 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy |
09 | YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1w |
10 | bGUuY29tMB4XDTIwMDcxMzA5MjAwMFoXDTMwMDcxMTA5MjAwMFowaTELMAkGA1UE |
11 | BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz |
12 | Y28xFDASBgNVBAoTC2V4YW1wbGUuY29tMRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv |
13 | bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCMkk/PHfD5eV2urk2OxiSFz1JDt |
14 | 0lM9wDommSrErcCQkFSmCidbXY2IdLvuDGkHLbVeVC4fPEUvpXhRorcPtNWjbTBr |
15 | MA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw |
16 | DwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgWR5x7Dsmmi2/Ynz2q0M4wykJ+Sgh |
17 | EDj23VitESDz160wCgYIKoZIzj0EAwIDSAAwRQIgR1cPAvybKwtZBI7qOsnQtEiU |
18 | qiMb7bP2DMgaz8IthOkCIQCXYmyuGbLJAew4KW2/3s9R4rq4C7/VIerNVcgMovg8 |
20 | -----END CERTIFICATE----- |
23 | orderer.example.com:7050 |
6~19行目にはorderer組織のCAの証明書、23行目にはordererのエンドポイント、27行目にはconsensus typeなどの設定があります。CAの証明書自体はorganizations/ordererOrganizations/example.com/ca/ca.example.com-cert.pemにあります。中身を確認するとgenesis blockにある証明書と同じことが分かります。
01 | $ cat organizations/ordererOrganizations/example.com/ca/ca.example.com-cert.pem |
02 | -----BEGIN CERTIFICATE----- |
03 | MIICPTCCAeOgAwIBAgIQDYCeY66vNwmM+gSw/PcAITAKBggqhkjOPQQDAjBpMQsw |
04 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy |
05 | YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1w |
06 | bGUuY29tMB4XDTIwMDcxMzA5MjAwMFoXDTMwMDcxMTA5MjAwMFowaTELMAkGA1UE |
07 | BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz |
08 | Y28xFDASBgNVBAoTC2V4YW1wbGUuY29tMRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv |
09 | bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCMkk/PHfD5eV2urk2OxiSFz1JDt |
10 | 0lM9wDommSrErcCQkFSmCidbXY2IdLvuDGkHLbVeVC4fPEUvpXhRorcPtNWjbTBr |
11 | MA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw |
12 | DwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgWR5x7Dsmmi2/Ynz2q0M4wykJ+Sgh |
13 | EDj23VitESDz160wCgYIKoZIzj0EAwIDSAAwRQIgR1cPAvybKwtZBI7qOsnQtEiU |
14 | qiMb7bP2DMgaz8IthOkCIQCXYmyuGbLJAew4KW2/3s9R4rq4C7/VIerNVcgMovg8 |
同様にorg1, org2のCAの証明書も入っていることが確認できると思います。また、それ以外にも様々な証明書が入っています。これらの証明書は細かな制御を行うためのものです。
Hyperledger Fabricにはfabric networkを細かく制御するポリシーを設定できます。ポリシーの設定はgenesis blockの生成に必要な設定ファイルのconfigtx/configtx.yamlにあります。このファイルの中盤にポリシーの設定が記載されています。
01 | $ cat configtx/configtx.yaml |
05 | # Policies defines the set of policies at this level of the config tree |
06 | # For organization policies, their canonical path is usually |
07 | # /Channel/<Application|Orderer>/<OrgName>/<PolicyName> |
11 | Rule: "OR('Org1MSP.admin', 'Org1MSP.peer', 'Org1MSP.client')" |
14 | Rule: "OR('Org1MSP.admin', 'Org1MSP.client')" |
17 | Rule: "OR('Org1MSP.admin')" |
20 | Rule: "OR('Org1MSP.peer')" |
例えば、上記の例ではブロックのデータを読むにはOrg1のadminやpeer, clientのOU属性を持つ証明書が必要です。管理者権限が必要な操作はOrg1のadminのOU属性を持つ証明書(下記12行目のOU)、エンドースメント(後述)はOrg1のpeerのOU属性を持つも証明書が必要など、細かく設定ができます。
01 | $ openssl x509 -noout -text -in organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem |
06 | 8c:2e:1f:39:fd:2b:c2:1f:a0:49:16:39:52:00:a9:8b |
07 | Signature Algorithm: ecdsa-with-SHA256 |
08 | Issuer: C = US, ST = California, L = San Francisco, O = org1.example.com, CN = ca.org1.example.com |
10 | Not Before: Jul 21 01:30:00 2020 GMT |
11 | Not After : Jul 19 01:30:00 2030 GMT |
12 | Subject: C = US, ST = California, L = San Francisco, OU = admin, CN = Admin@org1.example.com |
13 | Subject Public Key Info: |
14 | Public Key Algorithm: id-ecPublicKey |
17 | 04:9d:cd:74:38:63:b2:c7:6b:77:21:f3:b1:7c:99: |
18 | a9:34:73:87:bb:dc:5d:d1:f0:e5:08:90:f4:c5:c1: |
このポリシーを実現するため、genesis blockにはCA以外のadminやpeer, clientの証明書が含まれています。このように、genesis blockは設定や含まれている証明書のリストをもとに許可された組織がchannelを作成できるようにすることで、Hyperledger Fabricの特徴であるPermissionedを実現しています。
Hyperledger Fabric CA
先ほど「CAの証明書がgenesis blockに入っている」と説明しましたが、network.shの最小構成ではCAを起動していません。./network.shの起動ログを見ると次のようになっています。
02 | Starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb' with crypto from 'cryptogen' |
05 | DOCKER_IMAGE_VERSION=2.2.0 |
06 | /home/ubuntu/fabric-samples/test-network/../bin/cryptogen |
08 | ########################################################## |
09 | ##### Generate certificates using cryptogen tool ######### |
10 | ########################################################## |
12 | ########################################################## |
13 | ############ Create Org1 Identities ###################### |
14 | ########################################################## |
15 | + cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output=organizations |
19 | ########################################################## |
20 | ############ Create Org2 Identities ###################### |
21 | ########################################################## |
22 | + cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output=organizations |
26 | ########################################################## |
27 | ############ Create Orderer Org Identities ############### |
28 | ########################################################## |
29 | + cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output=organizations |
15行目でcryptgenという証明書を生成するツールを用いて証明書を生成しています。デモやPoCレベルではCAの運用はそこまで必要ありませんが、本来は各組織にCAを構築し、組織間で証明書をやりとりしてgenesis blockを生成します。CAがいなければ、新たなユーザーーの追加や証明書の失効を行うことはできません。
./network.shではCAを起動した場合もサポートしています。実際に確認してみましょう。まずは残った環境を削除します。
次に引数として-caを加え、CAを利用した環境を作成します。
02 | Starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb' with crypto from 'Certificate Authorities' |
05 | DOCKER_IMAGE_VERSION=2.2.0 |
07 | CA_DOCKER_IMAGE_VERSION=1.4.7 |
08 | ########################################################## |
09 | ##### Generate certificates using Fabric CA's ############ |
10 | ########################################################## |
11 | Creating network "net_test" with the default driver |
12 | Creating ca_org1 ... done |
13 | Creating ca_org2 ... done |
14 | Creating ca_orderer ... done |
15 | ########################################################## |
16 | ############ Create Org1 Identities ###################### |
17 | ########################################################## |
21 | + fabric-ca-client register --caname ca-org1 --id.name peer0 --id.secret peer0pw --id.type peer --tls.certfiles /home/ubuntu/fabric-samples/test-network/organizations/fabric-ca/org1/tls-cert.pem |
22 | 2020/07/13 14:17:07 [INFO] Configuration file location: /home/ubuntu/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/fabric-ca-client-config.yaml |
23 | 2020/07/13 14:17:07 [INFO] TLS Enabled |
24 | 2020/07/13 14:17:07 [INFO] TLS Enabled |
30 | + fabric-ca-client register --caname ca-org1 --id.name user1 --id.secret user1pw --id.type client --tls.certfiles /home/ubuntu/fabric-samples/test-network/organizations/fabric-ca/org1/tls-cert.pem |
31 | 2020/07/13 14:17:07 [INFO] Configuration file location: /home/ubuntu/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/fabric-ca-client-config.yaml |
32 | 2020/07/13 14:17:07 [INFO] TLS Enabled |
33 | 2020/07/13 14:17:07 [INFO] TLS Enabled |
37 | ######### Generating Orderer Genesis block ############## |
38 | + configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block |
最初の方で各組織のCAコンテンを3つ(ca_org1、ca_org2、ca_orderer)起動しています。その後“Register peer0”や“Register user”が表示され、fabric-ca-clientコマンドが実行されています。fabric-ca-clientはCAを操作するツールで、CAにユーザーや証明書の登録を行います。これらの操作をpeerやuser、admin、ordererなどで行い、証明書を用いてgenesis blockを生成するツールのconfigtxgenを実行し、genesis blockを生成します。
Execute-Order-Validateアーキテクチャ
一般的なブロックチェーンは、トランザクションを発行する場合、最初にコンセンサスプロトコルを用いてトランザクションの順序を決めます。その後、その順序に従ってスマートコントラクトを実行し台帳を更新します。この方式はシーケンシャルにトランザクションを実行するためスループットが制限されます。
Hyperledger Fabricでは、この制限を回避するため、以下のように動作するExecute-Order-Validateアーキテクチャを採用しています。
- Peerでchaincodeを実行してエンドースメント をもらうExecuteフェーズ
- Ordererにトランザクションを発行して順序を決めるOrdererフェーズ
- Peerでトランザクションがポリシーを満たすかどうかを検証するValidateフェーズ
これらがどのような処理を行っているのか、実際にトランザクションを発行して確認したいと思います。
エンドースメントポリシー
Hyperledger Fabricでトランザクションを有効にするには、各組織が持つ分散台帳の値を用いてchaincodeを実行し、分散環境でも実行結果に対して同一であることを保証する必要があります。
Hyperledger Fabricでは複数の組織でchaincodeを実行し、その実行結果に裏書き(署名)をします。そして、必要な数の署名が集まれば「その時点で」実行結果が正しいことが保証されます。その必要な数を定義したのがエンドースメントポリシーです。
例えば、3つの組織A、B、Cがあるとして「過半数のエンドースメントが必要」と定義すれば、組織AとB、組織AとC、組織BとCのエンドースメントの3つのパターンのいずれかを満たせば、そのトランザクションは有効とみなされます。エンドースメントが1つの組織だけでは、そのトランザクションは有効とみなされません。
Fabric-samplesを動かして、エンドースメントポリシーの動きを理解していただきたいと思います。すでにordererやpeerが動いているので、channelを作成するところから始めます。
1 | $ ./network.sh createChannel |
2 | Creating channel 'mychannel'. |
3 | If network is not up, starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb |
5 | ### Generating channel create transaction 'mychannel.tx' ### |
7 | ========= Channel successfully joined =========== |
次に、fabcarという自動車の所有者を管理するchiancodeをデプロイします。Chaincodeに関しては次回で詳しく説明したいと思います。
01 | $ ./network.sh deployCC |
02 | deploying chaincode on channel 'mychannel' |
04 | Vendoring Go dependencies |
06 | ===================== Querying on peer0.org1 on channel 'mychannel'... ===================== |
07 | Attempting to Query peer0.org1, Retry after 3 seconds. |
08 | ++ peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}' |
12 | [{"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"}}] |
13 | ===================== Query successful on peer0.org1 on channel 'mychannel' ===================== |
このサンプルでは、chiancodeをデプロイすると初期化関数を呼ぶ処理まで実行されるため、10人の自動車の所有者が登録され表示されます。次に、今curlコマンドを用いて冒頭で用意したpeerコマンドからfabric networkを操作するために環境変数を設定します。
1 | $ export PATH=${PWD}/../bin:$PATH |
2 | $ export FABRIC_CFG_PATH=$PWD/../config/ |
3 | $ export CORE_PEER_TLS_ENABLED=true |
4 | $ export CORE_PEER_LOCALMSPID="Org1MSP" |
5 | $ export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt |
6 | $ export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp |
7 | $ export CORE_PEER_ADDRESS=localhost:7051 |
Peerコマンドを実行してchaincodeを呼び出してみます。まずはorg1のpeer0がどのchannelに所属しているか調べます。
2 | 2020-07-14 12:53:57.043 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized |
3 | Channels peers has joined: |
このように、org1のpeer0はmychannelに属していることが分かります。次に、このchannelが持つブロックの数を調べてみます。
1 | $ peer channel getinfo -c mychannel |
2 | 2020-07-14 12:53:58.577 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized |
3 | Blockchain info: {"height":7,"currentBlockHash":"fuSrTtSPB8RuvMQNpMR85VaRWvDSxDFfZ/jpN2T68kQ=","previousBlockHash":"hCXf7YiFGcRzAaE4eReIF0inDjiVSB/z+gcj3iRvpiY="} |
ブロックの高さは7つあります。genesis block、異なる組織がお互いに認識できるようにするための情報が入っているブロック2つ(org1とorg2の分)、chaincodeの定義に対する承認ブロック2つ(org1とorg2の分)、chaincodeを定義したブロック、そして最初のinvoke(chaincodeで定義された関数を呼び出すコマンド)を実行した結果のブロックの計7つです。
次に、CAR9のレコードの車の所有者を“Shotaro”から“Dave”に変更するトランザクションを投げてみます。
1 | $ 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":"changeCarOwner","Args":["CAR9","Dave"]}' |
2 | 2020-07-14 13:49:49.414 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 |
変更されたquery(chaincodeで定義された値を取得する関数を呼び出すコマンド)を実行して確認します。
1 | $ peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}' |
2 | [{"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":"Dave"}}] |
CAR9のレコードのownerがshotaroからDaveに変更されていることが分かります。そしてブロックが1つ積まれています。
1 | $ peer channel getinfo -c mychannel |
2 | 2020-07-14 13:50:16.910 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized |
3 | Blockchain info: {"height":8,"currentBlockHash":"Gyo8UEDLosIyz0byrtz1Y5HyFVXaqpJlhw5h8bQnwRc=","previousBlockHash":"bQHdt4mlbEAaCxUU0j0VzmaWE7/p+1WrkKmbWQ3Vm4g="} |
先ほど実行したpeer chaincode invokeコマンドにはpeerAddressの引数が2つあります。このコマンドは、引数に指定されたorg1のpeer(localhost:7051)とorg2のpeer(localhost:9051)にトランザクションを投げてエンドースメントを取得します。その後、ordererにトランザクションを投げています。これは、エンドースメントポリシーに何も指定しないとデフォルトポリシーである「過半数以上」となるため、2組織の過半数である2つのエンドースメントポリシーが必要になります。
試しにpeerAddressの引数を1つ(localhost:7051)だけにして、名前を“Shotaro”に戻してみます。
1 | $ 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 -c '{"function":"changeCarOwner","Args":["CAR9","Shotaro"]}' |
2 | 2020-07-14 13:56:11.789 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 |
結果は“status:200”が返ってきているので、org1のpeerに対するトランザクションは成功し、1つのエンドースメントを得てからordererにトランザクションを投げています。しかし、値を確認するとCAR9のレコードのownerは変わっていません。
1 | $ peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}' |
2 | [{"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":"Dave"}}] |
Peerのログを調べてみます。
1 | $ docker logs peer0.org1.example.com |
3 | 2020-07-14 13:56:13.796 UTC [gossip.privdata] StoreBlock -> INFO 080 [mychannel] Received block [8] from buffer |
4 | 2020-07-14 13:56:13.797 UTC [vscc] Validate -> ERRO 081 VSCC error: stateBasedValidator.Validate failed, err validation of endorsement policy for chaincode fabcar in tx 8:0 failed: implicit policy evaluation failed - 1 sub-policies were satisfied, but this policy requires 2 of the 'Endorsement' sub-policies to be satisfied |
5 | 2020-07-14 13:56:13.797 UTC [committer.txvalidator] validateTx -> ERRO 082 Dispatch for transaction txId = 210bc6e65ab2327f4e3fb43f7f1caede6a18b4a3e6cf161bfe15778a4dc06fc1 returned error: validation of endorsement policy for chaincode fabcar in tx 8:0 failed: implicit policy evaluation failed - 1 sub-policies were satisfied, but this policy requires 2 of the 'Endorsement' sub-policies to be satisfied |
6 | 2020-07-14 13:56:13.797 UTC [committer.txvalidator] Validate -> INFO 083 [mychannel] Validated block [8] in 0ms |
7 | 2020-07-14 13:56:13.797 UTC [validation] preprocessProtoBlock -> WARN 084 Channel [mychannel]: Block [8] Transaction index [0] TxId [210bc6e65ab2327f4e3fb43f7f1caede6a18b4a3e6cf161bfe15778a4dc06fc1] marked as invalid by committer. Reason code [ENDORSEMENT_POLICY_FAILURE] |
4行目のErrorを見ると、ポリシーは2つのエンドースメントを要求しているのに「1つのエンドースメントしかない」と書かれています。このようにエンドースメントポリシーが足りないとエラーとなります。
しかし、ブロックの高さを調べてみると1つ増えています。
1 | $ peer channel getinfo -c mychannel |
2 | 2020-07-14 13:59:49.405 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized |
3 | Blockchain info: {"height":9,"currentBlockHash":"uyw+NsBJq+327SGQDxbvJJrgt6cqO8bow6WxsuTAGHo=","previousBlockHash":"Gyo8UEDLosIyz0byrtz1Y5HyFVXaqpJlhw5h8bQnwRc="} |
これは、Execute-Order-Validateアーキテクチャの最後のvalidationフェーズにおいて、エンドースメントポリシーを満たしてないトランザクションであるため、ブロックチェーンにブロックは積まれるが、トランザクションの変更は値に反映されていないことを示しています(次回でchiancodeとともにデータの取り扱いに関して解説します)。
おわりに
今回はHyperledger Fabricの特徴であるPermissionedとExecute-Orderer-Validateアーキテクチャを解説しました。トランザクションフローは長く複雑な動作になっていますが、Execute-Orderer-Validateアーキテクチャを理解すれば、なぜこのようなフローになっているのか分かるようになると思います。
次回は、実際にchaincodeとデータの取り扱いについて、サンプルを動かしながら解説したいと思います。