メインコンテンツへスキップ

ローカル Kubernetes に Hyperledger Fabric ネットワークを迅速にデプロイする

Paper
アークブロック

著者 論文

ArcBlock は、より優れたブロックチェーン開発プラットフォームの構築に取り組んでおり、開発者により効率的な開発ツールを提供したいと考えています。そのため、さまざまなブロックチェーン テクノロジーを研究し、まず開発者が緊急に必要としているものを理解する必要があります。私たちはこれまでに BTC と ETH に関する多くの洞察を共有してきましたが、どうすれば Hyperledger を手放すことができるでしょうか?さらに、ArcBlock は権威ある Hyperledger Foundation のメンバーであり、Hyperledger の知識を共有することが必須です。

ハイパーレジャーとは何ですか?

Hyperledger (or the Hyperledger project) is an umbrella project of open source blockchains and related tools, started in December 2015 by the Linux Foundation, to support the collaborative development of blockchain-based distributed ledgers.

ウィキペディアからの説明です。これはブロックチェーンの包括的なプロジェクトであるため、Burrow、イーサリアム仮想マシン、分散台帳上の独立した ID をサポートする Indy、そして今日の主役である Fabric など、多くの優れたプロジェクトをその傘下に抱えています。

hyperledger 项目

Fabric は分散台帳ソリューションを提供するプラットフォームです。 Hyperledger Fabric はモジュラー アーキテクチャによってサポートされており、優れた機密性、スケーラビリティ、柔軟性、拡張性を提供します。 Hyperledger Fabric は、さまざまなモジュール コンポーネントの直接プラグイン アクティベーションをサポートするように設計されており、経済エコシステムのさまざまな複雑なシナリオに適応できます。 Fabric が提供するスケーラブルで拡張可能なアーキテクチャ共有は、完全なレビュー メカニズムを必要とし、さまざまなニーズがあるエンタープライズ レベルのブロックチェーン構築に適しています。

さっそく始めましょう

Open Fabric を開始します。おっと...ファブリックのソース コードを取得して、さまざまなツールをコンパイルする必要もあります。 HelloWorld! を実行したいだけです。

スケーラビリティと柔軟性を得るには、実際に多額の費用を支払う必要があります。スケーラビリティと柔軟性といえば、これがKubernetsの特徴ではないでしょうか。ありがとう神様!いいえ、オープンソースに感謝します!

https://github.com/IBM/blockchain-network-on-kubernetes

kuberntes でのファブリックのデプロイメントをサポートする素晴らしいプロジェクトがあるので、始めてみましょう。

こんにちは、Kubernetes

brew cask install docker

まず Docker クライアントをインストールし、設定を開き、kubernetes タブに移動して kubernetes を有効にするにチェックを入れてから、しばらく作業を続けます~ enable k8s

kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml

ダッシュボードをインストールします。もちろん、インストールする必要はありません。

# token を取得し、どれか 1 行をコピーすればよい
kubectl -n kube-system describe secret `kubectl -n kube-system get secret|grep admin-token|cut -d " " -f1`|grep "token:"|tr -s " "|cut -d " " -f2
# プロキシを起動する
kubectl proxy

http://localhost/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/discovery?namespace=default を開いて貼り付けると、ダッシュボードが表示されます ダッシュボードから、デプロイしたコンテナーとコンテナーのログを視覚的に確認できます。もちろんターミナルからも閲覧可能です。

ブロックチェーンネットワークを導入する

git clone git@github.com:IBM/blockchain-network-on-kubernetes.git
chmod +x setup_blockchainNetwork.sh
chmod +x deleteNetwork.sh
./setup_blockchainNetwork.sh

さて、完了しました:)、あとは待つだけです。

TIPS: このプロセスでは Docker ハブからイメージをプルする必要があるため、いくつかのよく知られたネットワーク機能が必要になる場合があります。

すべてが完了したら:

$ kubectl get pods
NAME                                    READY     STATUS      RESTARTS   AGE
blockchain-ca-77459f9b84-zd8qt          1/1       Running     0          2m
blockchain-orderer-5c88f8cf95-25862     1/1       Running     0          2m
blockchain-org1peer1-7d95cbfd64-wcvz6   1/1       Running     0          2m
blockchain-org2peer1-d85dfcfc7-4kpmz    1/1       Running     0          2m
blockchain-org3peer1-6cffb6cbc7-stnj5   1/1       Running     0          2m
blockchain-org4peer1-84486f557c-pmvq8   1/1       Running     0          2m
chaincodeinstall-skd26                  0/4       Completed   0          1m
chaincodeinstantiate-jm85m              0/1       Completed   0          1m
copyartifacts-kmcp9                     0/1       Completed   0          3m
createchannel-p6ggq                     0/2       Completed   0          2m
joinchannel-lhqnx                       0/4       Completed   0          2m
utils-vr8fr                             0/2       Completed   0          2m

おめでとうございます。ファブリック ネットワークの展開が完了しました。とても簡単なことではありませんか?

ぜひこのネットワークを試してみてください

上記の出力から、このブロックチェーン ネットワークが CA ノード、発注者ノード、4 つの組織ノード org1peer ~ org4peer をデプロイし、いくつかの完了したトランザクション操作を実行していることがわかります。

CA サービス の主な機能は、ID の登録、ログインおよびトランザクション証明書の管理ですが、ここでは詳しく説明しません。

ORDERER は、注文サービスを提供し、クライアントとピアノードに共有通信チャネルを提供し、トランザクションを含むブロードキャストのサービスを提供します。簡単に言えば、このサービスは台帳の正しい記録と各ピアのローカル台帳の一貫性を保証します。

ピア ノード は、注文されたステータスの更新を注文サービスからブロックの形式で受信し、ステータスと台帳を維持します。ここに台帳が保存されます。このコンテナを見てみましょう。

kubectl exec -it blockchain-org1peer1-7d95cbfd64-wcvz6 bash #进入org1节点的bash
root@blockchain-org1peer1-7d95cbfd64-wcvz6:/# peer chaincode query -C channel1 -n cc -c '{"Args":["query","a"]}'

あなたは見るでしょう get a value 今はこのプロセスを無視しましょう。最後の行が見えます。得られた結果は 100 です。同じ方法を使用して b の値をクエリすると、200 が得られます。 次に、b を使用して 51 元を a に送金しようとします。

peer chaincode invoke -o blockchain-orderer:31010 -C channel1 -n cc -c '{"Args":["invoke","b","a","51"]}'

get a value

また、最初に検証プロセスを無視すると、invoke success などの単語が表示されます。 次に、他のノードにログインして、a と b の値を確認しますか?

何が起こったのでしょうか?

実際、上記の部分で、Kubernetes のローカル デプロイメントと Kubernetes への Fabirc ネットワークのインストールが完了しました。知らないうちにネットワークにチェーンコード (スマート コントラクトとして理解できます) をインストールしてインスタンス化していることもあります。これ以上先に進みたくない場合は、ブラウザを閉じても構いません。とても簡単なことではありませんか? このネットワークに独自のチェーンコードをデプロイしたい場合は、詳しく調べる必要があります。 まず setup_blockchainNetwork.sh スクリプトを開いて、その内容を確認してください。 読んでみると、次の手順以外に何もないことがわかりました。

  1. 永続ボリュームの作成
  2. アーティファクトのコピー ジョブ (構成ファイルを永続ボリュームにコピー)
  3. アーティファクト生成ジョブ(設定ファイルを使用して必要なファイルを生成)
  4. ブロックチェーンネットワーク用のサービスの作成
  5. Kubernetes Deployments (デプロイメントサービス) を使用してピア、CA、Orderer を作成する
  6. チャネルトランザクションアーティファクトとチャネルの作成
  7. チャネル上のすべてのピアに参加します (すべてのノードをチャネルに参加させます)
  8. 各ピアにチェーンコードをインストールします (各ノードにチェーンコードをインストールします)
  9. チャネル上でチェーンコードをインスタンス化します (チャネル上でチェーンコードをインスタンス化します)

実際、すべての手順に注意を払う必要はありません。独自のチェーンコードをデプロイしたいだけです。 1 と 2 は、Kubernetes 上にデプロイしているため、ディスク領域が必要で、そこにファイルを配置するため、無視できます (クラウド上の Kubernets サービスにデプロイしたい場合は、各クラウド サービス プラットフォームに応じて調整する必要がある場合があります)。 3、4、5、6、7 はすべて、4 つの組織ノードを含むブロックチェーン環境全体を確立しています。 8 からはチェーンコードに重点を置きます。 これら 2 つのステップが正確に何をするのか見てみましょう。 configFiles のchaincode_install.xml を開くと、いくつかの違いはあるものの類似した 4 つのタスクがあることがわかります。重要なポイントは次のとおりです。

command: ["sh", "-c", "echo $GOPATH; cp -r /shared/artifacts/chaincode $GOPATH/src/; ls $GOPATH/src; peer chaincode install -n ${CHAINCODE_NAME} -v ${CHAINCODE_VERSION} -p chaincode_example02/"]

実際、手順は 2 つだけです。アーティファクト下のチェーンコードを gopath にコピーし、インストール コマンドを実行します。それでは、チェーンコードの下にあるものを見てみましょう。

万圣节快乐

実際には、go ファイルは 1 つだけです。はい、これは先ほど誤ってインストールしたチェーンコードです。

同様に、chaincode_instantiate.yaml ファイルをもう一度見てみましょう。重要なポイントは依然として共通です。

peer chaincode instantiate -o blockchain-orderer:31010 -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -v ${CHAINCODE_VERSION} -c '{\"Args\":[\"init\",\"a\",\"100\",\"b\",\"200\"]}'

a と b の値がどこから来たのかがついにわかりました。

チェーンコードとは

チェーンコードは Go 言語で書かれたプログラム (Java などの他のプログラミング言語をサポート) で、事前定義されたインターフェイスを実装できます。チェーンコードは保護された Docker コンテナ内で実行され、承認ノードの操作から分離されます。チェーンコードは、アプリケーションによって送信されたトランザクション台帳の状態によって初期化および管理できます。 通常、チェーンコードはネットワーク参加者が合意したビジネスロジックを処理するため、「スマートコントラクト」と呼ばれることもあります。チェーンコードによって生成された(台帳)状態はほかのチェーンコードから分離されているため、直接アクセスすることはできません。ただし、同じネットワーク上にあり、必要な権限を取得していれば、chaincode は別の chaincode を呼び出して台帳にアクセスできます。 先ほど go ファイルを開いて、次の機能に注目してください。

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response;
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response;
func main();

init 関数は、チェーンコードがインスタンス化およびアップグレードされるときに呼び出されます。 呼び出し関数は、各ノードからのクエリとレジャーの変更リクエストを処理できます。 主な機能はエントリー機能であり、その主な機能はチェーンコードを開始することです。

これらの関数の内容を見てみましょう。当然のことながら、これら 2 つの関数はパラメータの読み取りと形式の変換です。 いくつかの文が私たちの注意を引きました。 3 コメントを見るとわかりやすいですね。レジャーに書き込み、マウスをそのタイプに移動すると、その他のメソッドが表示されます。 当面は getState と putState を使用するだけで十分です。

チェーンコードの作成を開始する

このセクションでは、golang の基本的な知識が少し必要になる場合があります。それは問題ではありません。できるだけ簡潔にするために、元のチェーンコードを直接変更しますが、その前に、まずネットワークを削除します (ネットワークを削除せずにチェーンコードを直接更新することもできますが、新しい構成ファイルを作成する必要があります。デモを簡略化するために、最初にネットワークを削除します)。

./deleteNetwork.sh

次に、ギャンブル ゲームを書いてみましょう。ルールは非常に簡単です。

  1. 賞金プールとして初期値を設定します
  2. ユーザーは賞金プールの金額を推測します。失敗した推測の数が賞金プールに追加されます。
  3. 賭け金と賞金総額の差額により、賞金総額の 1/4 ~ 3/8 の範囲内の報酬がユーザーのフィードバックに与えられます。
  4. 正しい金額を推測するとゲームが終了し、すべての報酬を獲得できます

話は安い、私のコードを見せてください!

コードをコピーして、元のチェーンコードを上書きします。

package main

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

type GameChainCode struct {
}

func (cc *GameChainCode) Init(stub shim.ChaincodeStubInterface) pb.Response {

	var sum string // Sum of asset holdings across accounts. Initially 0
	var sumVal int // Sum of holdings
	var err error
	_, args := stub.GetFunctionAndParameters()
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}
	// Initialize the chaincode
	sum = args[0]
	sumVal, err = strconv.Atoi(sum)
	if err != nil {
		return shim.Error("Expecting integer value for sum")
	}
	fmt.Printf("sumVal = %d\n", sumVal)
	if sumVal < 100 {
		return shim.Error("init value must bigger than 100")
	}
	// Write the state to the ledger
	err = stub.PutState("total", []byte(strconv.Itoa(sumVal)))
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

func (cc *GameChainCode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()
	if function == "query" {
		return cc.query(stub, args)
	} else if function == "lottery" {
		return cc.lottery(stub, args)
	} else if function == "create_user" {
		return cc.create_user(stub, args)
	}
	return shim.Error("Invalid invoke function name. Expecting query uid,lottery uid token,create_user uid")
}

func (cc *GameChainCode) create_user(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 1 {
		return shim.Error("params error")
	}
	uId := args[0]
	balance, err := cc.getBalance(stub, uId)
	if balance < 0 || err != nil {
		stub.PutState(uId, []byte("1000"))
		return shim.Success([]byte("1000"))
	} else {
		return shim.Error("uid has been registed")
	}
}

func (cc *GameChainCode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) == 0 {
		t, err := cc.getTotal(stub)
		if err != nil {
			return shim.Error("get total error!")
		}
		if t < 100 {
			return shim.Success([]byte("false"))
		} else {
			return shim.Success([]byte("true"))
		}
	} else {
		t, err := cc.getBalance(stub, args[0])
		if err != nil {
			return shim.Error("get balance error!")
		} else {
			return shim.Success([]byte(strconv.Itoa(t)))
		}
	}

}

func (cc *GameChainCode) lottery(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 2 {
		return shim.Error("params error")
	}
	t, err := cc.getTotal(stub)
	if err != nil {
		return shim.Error("get total error!")
	}
	uId := args[0]
	uWager, err := strconv.Atoi(args[1])
	if err != nil {
		return shim.Error("wager error!")
	}
	err = cc.plus(stub, uId, -uWager)
	if err != nil {
		return shim.Error("balance not enough")
	}

	if (uWager - t) == 0 {
		//hit
		err = cc.plus(stub, uId, uWager+t)
		return shim.Success([]byte("you win! game over!"))
	} else if ((t - uWager) > t>>2) && ((t - uWager) < t>>2+t>>3) {
		err = cc.plus(stub, uId, uWager>>1)
		return shim.Success([]byte("congratulation!!!!"))
	} else {
		err = cc.plus(stub, "total", uWager)
		return shim.Success([]byte("Good Luck Next Time!"))
	}
	return shim.Error("bet error")

}

//plus or minus value of key
func (cc *GameChainCode) plus(stub shim.ChaincodeStubInterface, k string, v int) error {
	Avalbytes, err := stub.GetState(k)
	if err != nil {
		return err
	}
	TotalV, err := strconv.Atoi(string(Avalbytes))
	if err != nil {
		return err
	}
	TotalV += v
	if TotalV < 0 {
		return errors.New("balance not enough")
	}
	stub.PutState(k, []byte(strconv.Itoa(TotalV)))
	return nil
}

func (cc *GameChainCode) getBalance(stub shim.ChaincodeStubInterface, k string) (int, error) {
	Avalbytes, err := stub.GetState(k)
	if err != nil {
		return -1, err
	}
	TotalV, err := strconv.Atoi(string(Avalbytes))
	if err != nil {
		return -1, err
	}
	return TotalV, nil
}

func (cc *GameChainCode) getTotal(stub shim.ChaincodeStubInterface) (int, error) {
	return cc.getBalance(stub, "total")
}

func main() {
	err := shim.Start(new(GameChainCode))
	if err != nil {
		fmt.Printf("Error starting game chaincode: %s", err)
	}
}

次に、chaincode_instantiate ファイル内のチェーンコードのインスタンス化のパラメーターを変更します。

peer chaincode instantiate -o blockchain-orderer:31010 -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -v ${CHAINCODE_VERSION} -c '{\"Args\":[\"init\",\"1024\"]}'

最初の賞金総額を 1024 に設定しました:) 最後に、chaincode_instantiate とchaincode_install の CHAINCODE_VERSION 値を 1.1 に変更します。

OK、もう一度始めてください。/setup_blockchainNetwork.sh

まずユーザー用紙を作成すると、システムが 1000 を彼に割り当てます。

kubectl exec -it $(kubectl get pods | grep org1peer | awk '{ print $1 }')  -- peer chaincode invoke -C channel1 -n cc -c '{"Args":["create_user","paper"]}'
# 残高を確認し、1000 になっているか見てみる
kubectl exec -it $(kubectl get pods | grep org1peer | awk '{ print $1 }')  -- peer chaincode invoke -C channel1 -n cc -c '{"Args":["query","paper"]}'
# 50 を投資して試してみる
kubectl exec -it $(kubectl get pods | grep org1peer | awk '{ print $1 }')  -- peer chaincode invoke -C channel1 -n cc -c '{"Args":["lottery","paper","50"]}'
# 結果を取得 result: status:200 payload:"Good Luck Next Time!"

# もう一度、今度は 800 で大きく賭ける
kubectl exec -it $(kubectl get pods | grep org1peer | awk '{ print $1 }')  -- peer chaincode invoke -C channel1 -n cc -c '{"Args":["lottery","paper","800"]}'
#result: status:200 payload:"congratulation!!!!"
# やった、当たりだ

同じピアノード上にさらに複数のユーザーを作成して、ゲームをシミュレートできます。 これは典型的なマルチパーティのゼロサム ゲームです。従来のCITICベースのネットワークに導入した場合、主催者の不正行為を疑い、監督機関の数を増やすことは避けられない。監督官庁にさらなる疑いがあれば、別の関係者を紹介する必要があるだろう。

このデモのブロックチェーン ネットワークでは、台帳を保存するための 4 つの組織ピアが存在し、どの当事者も不正行為を行うことはできません。さらに、ゼロサム ゲームとして、誰の利益にもならないため、すべてのノードが不正行為を行う必要はありません。最後に、ゲームのラウンドが終了したら、台帳内のすべての記録をチェックして、不正行為をしている人がいないかどうかを確認できます。そのため、ユーザーは非常に安心してゲームに参加することができます。これは、低コストの信頼というブロックチェーン技術の非常に大きな利点を示しています。もちろん、上記のコードは非常に単純なデモにすぎず、そのロジックと記述は十分に完璧ではありません。興味のある学生は自分で改善できます。 このデモのコードは、https://github.com/ArcBlock/blockchain-network-on-kubernetes にあります。

上記の内容を完了すると、ファブリック ネットワークが正常にデプロイされ、独自のチェーンコードがデプロイされました。ただし、検証プロセス、選別プロセス、承認戦略なども含まれており、一度に明確に説明することはできません。今後のブログで一つ一つ解説していきます。皆さんも引き続き ArcBlock エンジニア ブログをフォローしてください。

ArcBlock では、ブロックチェーン開発者にさまざまなブロックチェーン テクノロジーを学習するためのさまざまな便利なツールを提供し、また、誰もが一緒に進歩できるように、学んだすべてを共有します。ブロックチェーンが好き、または私たちの共有文化が好きなら、ぜひ 这里 に参加してください。