こんにちは、ディベロップメントサービス1課の山本です。
今回の記事では、AMB(Amazon Managed Blockchain) の利用方法について解説します。
特に、プライベートネットワークの構築について、AWS が提供しているサンプルを用いて詳しく説明します。
このサンプルは 9 つのパートに分かれていますので、1 つのパートを 1 つのブログ記事として扱い、それぞれ詳しく解説していきます。
皆様の理解を深めるために、図や詳細な説明を多く取り入れる予定です。
ブロックチェーンやAMBの基本的な内容が知りたい方は、過去のブログを参考ください。
- この記事の対象者は?
- 進め方
- Hyperledger Fabric ブロックチェーン ネットワークの構築
- 構成図
- 前提条件
- ステップ 1:Hyperledger Fabric ブロックチェーン ネットワークを作成する
- ステップ 2:ネットワークが利用可能であることを確認する
- ステップ 3:ファブリック クライアント ノードを作成する
- ステップ 4:ファブリック クライアントノードを準備し、ID を登録する
- ステップ 5:configtx チャネル構成を更新する
- ステップ 6:ファブリックチャネルを作成する
- ステップ 7:ピアノードをチャネルに参加させる
- ステップ 8:ピアノードにチェーンコードをインストールする
- ステップ 9:チェーンコードをインスタンス化する
- ステップ 10:チェーンコードをクエリする
- ステップ 11:トランザクションを呼び出す
- ステップ 12:チェーンコードを再度クエリし、値の変化を確認する
- さいごに
この記事の対象者は?
この記事は以下の方々に向けて書かれています:
- Amazon Managed Blockchain を使用してプライベートネットワークを構築することを検討している開発者の方々
- AWS の公式ドキュメントを読んだものの、具体的なイメージが湧かなかった方々
進め方
AMB(Amazon Managed Blockchain)のプライベートネットワークについての理解を深めるため、AWS が提供しているサンプルを用いて具体的な実装を進めていきます。
この過程で、各ステップの説明を挟みつつ、実際の操作を行っていきます。
サンプルの詳細は以下のリンクからご覧いただけます:
https://github.com/aws-samples/non-profit-blockchain/blob/master/README.md
サンプルの概要
このサンプルでは、架空の非営利団体(NGO 団体)向けに、寄付金の出納をブロックチェーンで管理するシステムを構築します。
このシステムを通じて、ブロックチェーンの透明性と信頼性を活用し、寄付金の管理をより効率的かつ安全に行う方法を学びます。
サンプルのパート
- Amazon マネージド ブロックチェーンを使用して Hyperledger Fabric ブロックチェーン ネットワークを構築することでワークショップを開始します。
- 非営利チェーンコードをデプロイします。
- RESTful API サーバーを実行します。
- アプリケーションを実行します。
- 新しいメンバーをネットワークに追加します。
- Amazon API Gateway と AWS Lambda を使用したブロックチェーンへの読み取りと書き込み。
- ブロックチェーン イベントを使用して、NGO の寄付をユーザーに通知します。
- Hyperledger Explorer を展開します。
- ブロックチェーンユーザーと Amazon Cognito の統合。
本ブログでは
1. Amazon マネージド ブロックチェーンを使用して Hyperledger Fabric ブロックチェーン ネットワークを構築することでワークショップを開始します。
を解説します。
Hyperledger Fabric ブロックチェーン ネットワークの構築
このセクションでは、こちらの README.mdを参考に、Hyperledger Fabric ブロックチェーン ネットワークの構築を行います。
本ブログの目標は以下の通りです:
- 構成図通りのインフラを正確に作成する
- チェーンコード(スマートコントラクト)を利用してデータの取得や変更を実施する
- 各ステップの内容を、README.md よりもわかりやすく説明する
構成図
このパートでは、以下の図に示すネットワーク構成を構築します。
前提条件
本作業は、AWS Cloud9 を使用してデプロイを行い、Fabric クライアントノードへのアクセスを実施します。
以下の条件で Cloud9 のインスタンスを構築します。
- リージョン: us-east-1
- 環境の名前: fabric-c9
- インスタンスタイプ: t2.medium
インスタンスの構築には約 30 秒から 60 秒程度かかります。
構築が完了したら、ターミナルを開き、AWS のサンプルリポジトリをホームディレクトリにクローンします。
cd ~
git clone https://github.com/aws-samples/non-profit-blockchain.git
次に、AWS CLI を最新版に更新します。
sudo pip install awscli --upgrade
これで事前準備は完了です。
ステップ 1:Hyperledger Fabric ブロックチェーン ネットワークを作成する
提供された CloudFormation テンプレートを使用してファブリック ネットワークを作成します。
CloudFormation テンプレートはngo-fabric/amb.yaml
にあります。
作成するリソースは、ブロックチェーンに参加するメンバー(デフォルト:ngo-member)と そのメンバーが所有するピアノードの二つとなります。
下記コマンドでテンプレートをデプロイします。 デプロイ完了までには、1 時間程度かかるのでご注意ください。
export REGION=us-east-1 export STACKNAME=non-profit-amb cd ~/non-profit-blockchain/ngo-fabric ./amb.sh
環境変数を上書きすることにより、テンプレート内のパラメータを変更することができるので、必要に応じて実施ください。
ただし、サンプルで作成済みの AMI を利用するため、リージョンはus-east-1
を推奨します。
ステップ 2:ネットワークが利用可能であることを確認する
デプロイが完了するまでコーヒーでも飲んで待ちましょう。
私のおすすめは、スタバのペットボトルコーヒーです。
CloudFormation のスタックステータスが CREATE_COMPLETE になったら完了です。
ステップ 3:ファブリック クライアント ノードを作成する
ファブリック CLI をホストするファブリッククライアントノードを EC2 で作成します。
ファブリック CLI とはブロックチェーンを構成するファブリックネットワークの設定を行う CLI となります。
このノードは、独自の VPC に作成されて、VPC エンドポイント経由でステップ 1 で作成したファブリックネットワークにアクセスします。
ngo-fabric/fabric-client-node.yaml
にあるテンプレートを利用して、以下のリソースを作成します。
※ 作成用の AMI は us-east-1 でのみ利用可能です。
別リージョンで構築されている方は、そのリージョンにコピー後、AMI ID を置き換えてください。
作成するリソースは、クライアントノードとなる EC2 とネットワーク周りのリソースとなります。
下記コマンドでテンプレートをデプロイします。
export REGION=us-east-1 cd ~/non-profit-blockchain/ngo-fabric ./vpc-client-node.sh
下記エラーが出た場合も問題ないので、無視してください。 これは、スクリプトがキーペアを作成時に、既存のキーペアを上書きしない処理になっているため発生します。
An error occurred (InvalidKeyPair.NotFound)
ステップ 4:ファブリック クライアントノードを準備し、ID を登録する
ファブリック CLI を利用する際、下記の情報をクライアントノードに設定する必要があります。
- 接続先のファブリックネットワーク
- 接続先のピアノード
- 利用する TLS 証明書
このステップではクライアントノードに cloud9 から SSH で接続し、環境変数の更新を実施します。
クライアントノードへの SSH 接続
SSH 用のキー(.pem ファイル)は スタック生成時に code9 のホームディレクトリに格納されてます。
administrator:~ $ ls
environment ngo-keypair.pem node_modules non-profit-blockchain package.json package-lock.json
DNS はステップ 3 で作成したスタックの出力に記載しています。
※対象リソースは全て削除済みです。
下記コマンドでクライアントノードに SSH でアクセスします。
cd ~ ssh ec2-user@<dns of EC2 instance> -i ~/<Fabric network name>-keypair.pem
Are you sure you want to continue connecting (yes/no)
が表示されたら、yes
を入力します。
クライアントノードの環境変数更新
まず、サンプルのリポジトリをクライアントノードでクローンします。
cd ~
git clone https://github.com/aws-samples/non-profit-blockchain.git
次に環境変数を上書きします。
※SSH セッションを今後一旦終了する場合は、再度ファイルをソースする必要があります。
export REGION=us-east-1 cd ~/non-profit-blockchain/ngo-fabric cp templates/exports-template.sh fabric-exports.sh source fabric-exports.sh source ~/peer-exports.sh
ここで変更した環境変数は以下の通りです。
※この環境は既に削除済みです。
Name | Value | 説明 |
---|---|---|
ADMINPWD | **** | メンバー管理者パスワード |
ADMINUSER | admin | メンバー管理者名 |
CAFILE | /opt/home/managedblockchain-tls-chain.pem | CA ファイル置き場 |
CASERVICEENDPOINT | ca.m-n5mspx3yfja35hkrjs5ofvbwca.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com:30002 | CA サービスエンドポイント |
CHAINCODEDIR | github.com/chaincode_example02/go | チェインコードのパス |
CHAINCODENAME | mycc | チェインコード名 |
CHAINCODEVERSION | v0 | チェインコードバージョン |
CHANNEL | mychannel | チャネル名 |
MEMBERID | m-N5MSPX3YFJA35HKRJS5OFVBWCA | メンバー ID |
MEMBERNAME | member-ngo | メンバー名 |
MSP_PATH | /opt/home/admin-msp | MSP(Membership Service Provider)のパス |
MSP | m-N5MSPX3YFJA35HKRJS5OFVBWCA | MSP の ID |
NETWORKID | n-WTU2LODE4JENXN6TS2JRHJTUJA | ネットワークの ID |
NETWORKNAME | ngo | ネットワーク名 |
NETWORKVERSION | 1.4 | Hyperledger Fabric のバージョン |
ORDERER | orderer.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com:30001 | Ordering Service のエンドポイント |
ORDERINGSERVICEENDPOINT | orderer.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com:30001 | Ordering Service のエンドポイント |
ORDERINGSERVICEENDPOINTNOPORT | orderer.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com | Ordering Service のエンドポイント(ポート番号無し) |
PEER | nd-iy5ft3o7fjcl7oe4dpftpdzuzy.m-n5mspx3yfja35hkrjs5ofvbwca.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com:30003 | ピアエンドポイント |
PEEREVENTENDPOINT | nd-iy5ft3o7fjcl7oe4dpftpdzuzy.m-n5mspx3yfja35hkrjs5ofvbwca.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com:30004 | ピアイベントエンドポイント |
PEERNODEID | nd-IY5FT3O7FJCL7OE4DPFTPDZUZY | ピアノード ID |
PEERSERVICEENDPOINT | nd-iy5ft3o7fjcl7oe4dpftpdzuzy.m-n5mspx3yfja35hkrjs5ofvbwca.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com:30003 | ピアエンドポイント |
PEERSERVICEENDPOINTNOPORT | nd-iy5ft3o7fjcl7oe4dpftpdzuzy.m-n5mspx3yfja35hkrjs5ofvbwca.n-wtu2lode4jenxn6ts2jrhjtuja.managedblockchain.us-east-1.amazonaws.com | ピアエンドポイント(ポート番号無し) |
STACKNAME | non-profit-amb | CFN スタック名 |
VPCENDPOINTSERVICENAME | com.amazonaws.us-east-1.managedblockchain.n-wtu2lode4jenxn6ts2jrhjtuja | VPC エンドポイント |
こちらの情報を元に、AMB へと接続します。
各種証明書の取得・生成
次に、最新バージョンの AMB のサーバー証明書を取得します。
aws s3 cp s3://us-east-1.managedblockchain/etc/managedblockchain-tls-chain.pem /home/ec2-user/managedblockchain-tls-chain.pem
取得したサーバー証明書を利用して、管理者 ID をファブリック CA(認証局)に登録します。
この ID はファブリックネットワークを管理し、チャネルの生成やチェーンコードのインスタンス化ができます。
export PATH=$PATH:/home/ec2-user/go/src/github.com/hyperledger/fabric-ca/bin cd ~ fabric-ca-client enroll -u https://$ADMINUSER:$ADMINPWD@$CASERVICEENDPOINT --tls.certfiles /home/ec2-user/managedblockchain-tls-chain.pem -M /home/ec2-user/admin-msp
これにより、/home/ec2-user/admin-msp
の配下に各種証明書や公開鍵が生成されます。
最後に生成した証明書をコピーします。
mkdir -p /home/ec2-user/admin-msp/admincerts cp ~/admin-msp/signcerts/* ~/admin-msp/admincerts/ cd ~/non-profit-blockchain/ngo-fabric
ステップ 5:configtx チャネル構成を更新する
ネットワーク内で、通信する際にチャネルと呼ばれるグループが必要となります。
このステップでは、チャネルを生成時に必要となる設定ファイルを作成します。
configtx.yaml
->{チャネル名}.pb
の順番で作成します。
まず、サンプルのconfigtx.yaml
のメンバー ID を実際のものに置換します。
cp ~/non-profit-blockchain/ngo-fabric/configtx.yaml ~ sed -i "s|__MEMBERID__|$MEMBERID|g" ~/configtx.yaml
このconfigtx.yaml
の内容を、ファブリック CLI を利用して{チャネル名}.pb
に変換します。
次ステップでチャネルを作成した際、この内容がブロックの最初の内容になります。
docker exec cli configtxgen -outputCreateChannelTx /opt/home/$CHANNEL.pb -profile OneOrgChannel -channelID $CHANNEL --configPath /opt/home/
{チャネル名}.pb
が生成されたことを確認します。
ls -lt ~/$CHANNEL.pb
実行結果
-rw-r--r-- 1 root root 327 Nov 24 07:05 /home/ec2-user/mychannel.pb
ステップ 6:ファブリックチャネルを作成する
早速、ファブリックチャネルを生成しましょう。
ただし、このステップではピアノードの参加はまだとなります。
次のコマンドを実行します。
docker exec -e "CORE_PEER_TLS_ENABLED=true" -e "CORE_PEER_TLS_ROOTCERT_FILE=/opt/home/managedblockchain-tls-chain.pem" \ -e "CORE_PEER_ADDRESS=$PEER" -e "CORE_PEER_LOCALMSPID=$MSP" -e "CORE_PEER_MSPCONFIGPATH=$MSP_PATH" \ cli peer channel create -c $CHANNEL -f /opt/home/$CHANNEL.pb -o $ORDERER --cafile $CAFILE --tls --timeout 900s
/opt/home/fabric-samples/chaincode/hyperledger/fabric/peer
内に
mychannel.block
というファイルが生成されます。
ファイル生成の確認。
ls -lt /home/ec2-user/fabric-samples/chaincode/hyperledger/fabric/peer
実行結果
-rw-r--r-- 1 root root 13185 Nov 24 07:15 mychannel.block
このディレクトリは、ピアノードからマウントされているので、ブロックファイルの確認はこのディレクトリ内で行えます。
ステップ 7:ピアノードをチャネルに参加させる
ピアノードをファブリックチャネルに参加させます。
次のコマンドを実行します。
docker exec -e "CORE_PEER_TLS_ENABLED=true" -e "CORE_PEER_TLS_ROOTCERT_FILE=/opt/home/managedblockchain-tls-chain.pem" \ -e "CORE_PEER_ADDRESS=$PEER" -e "CORE_PEER_LOCALMSPID=$MSP" -e "CORE_PEER_MSPCONFIGPATH=$MSP_PATH" \ cli peer channel join -b $CHANNEL.block -o $ORDERER --cafile $CAFILE --tls
実行結果
2023-11-24 07:21:29.100 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2023-11-24 07:21:29.772 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
ステップ 8:ピアノードにチェーンコードをインストールする
ピアノードにチェーンコードをインストールします。
チェーンコードとは、Hyperledger Fabric において、スマートコントラクトを実現させるためのコードになってます。
docker exec -e "CORE_PEER_TLS_ENABLED=true" -e "CORE_PEER_TLS_ROOTCERT_FILE=/opt/home/managedblockchain-tls-chain.pem" \ -e "CORE_PEER_ADDRESS=$PEER" -e "CORE_PEER_LOCALMSPID=$MSP" -e "CORE_PEER_MSPCONFIGPATH=$MSP_PATH" \ cli peer chaincode install -n $CHAINCODENAME -v $CHAINCODEVERSION -p $CHAINCODEDIR
チェーンコードの内容は、docker イメージ内の$CHAINCODEVERSION(github.com/chaincode_example02/go)になります。
下記の git に格納されているコードと同一です。
ステップ 9:チェーンコードをインスタンス化する
ファブリックチャネルでチェーンコードをインスタンス化します。
インスタンス化とは、インストールしたチェーンコードのコンパイル、ビルドを行い利用可能な状態にすることです。 初期値が必要な場合は、同時に引数で設定します。
docker exec -e "CORE_PEER_TLS_ENABLED=true" -e "CORE_PEER_TLS_ROOTCERT_FILE=/opt/home/managedblockchain-tls-chain.pem" \ -e "CORE_PEER_ADDRESS=$PEER" -e "CORE_PEER_LOCALMSPID=$MSP" -e "CORE_PEER_MSPCONFIGPATH=$MSP_PATH" \ cli peer chaincode instantiate -o $ORDERER -C $CHANNEL -n $CHAINCODENAME -v $CHAINCODEVERSION \ -c '{"Args":["init","a","100","b","200"]}' --cafile $CAFILE --tls
このステートメントには 30 秒ほどかかる場合があります。 特定の成功応答は表示されません。
先ほど紹介した、git のコードを見ると init の処理は下記のようになってます。 a、b という名前のデータに足して、値をそれぞれ 100, 200 セットして初期化する処理です。
func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) { var A, B string // Entities var Aval, Bval int // Asset holdings var err error if len(args) != 4 { return nil, errors.New("Incorrect number of arguments. Expecting 4") } // Initialize the chaincode A = args[0] Aval, err = strconv.Atoi(args[1]) if err != nil { return nil, errors.New("Expecting integer value for asset holding") } B = args[2] Bval, err = strconv.Atoi(args[3]) if err != nil { return nil, errors.New("Expecting integer value for asset holding") } fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) // Write the state to the ledger err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil { return nil, err } err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil { return nil, err } return nil, nil }
ステップ 10:チェーンコードをクエリする
ファブリックピアのチェーンコードをクエリします。
現在の台帳データを取得することを指します。
今回は、a の現在値を取得しましょう。
docker exec -e "CORE_PEER_TLS_ENABLED=true" -e "CORE_PEER_TLS_ROOTCERT_FILE=/opt/home/managedblockchain-tls-chain.pem" \ -e "CORE_PEER_ADDRESS=$PEER" -e "CORE_PEER_LOCALMSPID=$MSP" -e "CORE_PEER_MSPCONFIGPATH=$MSP_PATH" \ cli peer chaincode query -C $CHANNEL -n $CHAINCODENAME -c '{"Args":["query","a"]}'
結果
100
init の処理は下記のようになってます。 stub.GetState を利用して、台帳から a の値を取得して、返却してます。
func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) { if function != "query" { return nil, errors.New("Invalid query function name. Expecting \"query\"") } var A string // Entities var err error if len(args) != 1 { return nil, errors.New("Incorrect number of arguments. Expecting name of the person to query") } A = args[0] // Get the state from the ledger Avalbytes, err := stub.GetState(A) if err != nil { jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}" return nil, errors.New(jsonResp) } if Avalbytes == nil { jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}" return nil, errors.New(jsonResp) } jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}" fmt.Printf("Query Response:%s\n", jsonResp) return Avalbytes, nil }
ステップ 11:トランザクションを呼び出す
チェーンコードを利用してトランザクションを呼び出します。
現在の台帳データを変更することを指します。
今回は、a -> b に 10 だけ値を移譲させます。
docker exec -e "CORE_PEER_TLS_ENABLED=true" -e "CORE_PEER_TLS_ROOTCERT_FILE=/opt/home/managedblockchain-tls-chain.pem" \ -e "CORE_PEER_ADDRESS=$PEER" -e "CORE_PEER_LOCALMSPID=$MSP" -e "CORE_PEER_MSPCONFIGPATH=$MSP_PATH" \ cli peer chaincode invoke -o $ORDERER -C $CHANNEL -n $CHAINCODENAME \ -c '{"Args":["invoke","a","b","10"]}' --cafile $CAFILE --tls
結果
2023-11-24 07:29:34.177 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200
invoke の処理は下記のようになってます。 この引数で、a の値を-10, b の値を+10 する処理となります。
func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) { if function == "delete" { // Deletes an entity from its state return t.delete(stub, args) } var A, B string // Entities var Aval, Bval int // Asset holdings var X int // Transaction value var err error if len(args) != 3 { return nil, errors.New("Incorrect number of arguments. Expecting 3") } A = args[0] B = args[1] // Get the state from the ledger // TODO: will be nice to have a GetAllState call to ledger Avalbytes, err := stub.GetState(A) if err != nil { return nil, errors.New("Failed to get state") } if Avalbytes == nil { return nil, errors.New("Entity not found") } Aval, _ = strconv.Atoi(string(Avalbytes)) Bvalbytes, err := stub.GetState(B) if err != nil { return nil, errors.New("Failed to get state") } if Bvalbytes == nil { return nil, errors.New("Entity not found") } Bval, _ = strconv.Atoi(string(Bvalbytes)) // Perform the execution X, err = strconv.Atoi(args[2]) Aval = Aval - X Bval = Bval + X fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) // Write the state back to the ledger err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil { return nil, err } err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil { return nil, err } return nil, nil }
ステップ 12:チェーンコードを再度クエリし、値の変化を確認する
ファブリックピアのチェーンコードを再度クエリします。
ステップ 11 で、a, b 間で 10 の移動があったので、その変化が見えるはずです。
docker exec -e "CORE_PEER_TLS_ENABLED=true" -e "CORE_PEER_TLS_ROOTCERT_FILE=/opt/home/managedblockchain-tls-chain.pem" \ -e "CORE_PEER_ADDRESS=$PEER" -e "CORE_PEER_LOCALMSPID=$MSP" -e "CORE_PEER_MSPCONFIGPATH=$MSP_PATH" \ cli peer chaincode query -C $CHANNEL -n $CHAINCODENAME -c '{"Args":["query","a"]}'
90
a の値は、無事 10 減ってました。
さいごに
この記事では、AMBを利用した、Hyperledger Fabric ブロックチェーン ネットワークの構築について解説しました。 コマンドを叩くだけのサンプルが、少しでも理解しやすくなっていたのならば幸いです。
今後も、以降のサンプルパートについて、さらに詳しく解説していきますので、ぜひご期待ください。
本ブログがどなかたのお役に立てれば幸いです。