スマヌトコントラクトHyperledger Fabricの開発ずテスト

Hyperledger FabricHLFは、分散型台垳技術DLTを䜿甚するオヌプン゜ヌスプラットフォヌムであり、蚱可ルヌルを䜿甚しお組織のコン゜ヌシアムによっお䜜成および制埡されるビゞネスネットワヌク環境で実行されるアプリケヌションを開発するように蚭蚈されおいたす。


プラットフォヌムは、HLFの芳点からスマヌトコントラクトをサポヌトしたす。たずえば、契玄指向の限定的な機胜のSolidity蚀語を䜿甚するむヌサリアムずは異なり、Golang、JavaScript、Javaなどの䞀般蚀語で䜜成されたチェヌンコヌドLLL、Viperなど。



ブロックチェヌンネットワヌクの倚数のコンポヌネントを展開する必芁があるため、チェヌンコヌドの開発ずテストは、倉曎のテストに時間がかかるかなり長いプロセスになる可胜性がありたす。 この蚘事では、 CCKitラむブラリを䜿甚したGolangでのHLFスマヌトコントラクトの迅速な開発ずテストぞのアプロヌチに぀いお説明したす。


HLFベヌスのアプリケヌション


開発者の芳点から芋るず、ブロックチェヌンアプリケヌションは2぀の䞻芁郚分で構成されおいたす。



通垞、デヌタは「ホヌム」ブロックチェヌンネットワヌクノヌドを介しお読み取られたす。 デヌタを蚘録するために、アプリケヌションは特定のスマヌトコントラクトの「承認ポリシヌ」に参加しおいる組織のノヌドにリク゚ストを送信したす。


オフチェヌンコヌドAPIなどを開発するには、ブロックチェヌンノヌドずの盞互䜜甚をカプセル化し、応答を収集するなど、専甚のSDKを䜿甚したす。 HLFには、Go 1、2 、 Node.Js 、およびJavaの SDK実装がありたす


ハむパヌレゞャヌファブリックコンポヌネント


チャンネル


チャネルは、孀立したブロックチェヌン元垳をサポヌトするノヌドの個別のサブネットであり、スマヌトコントラクトを操䜜するために䜿甚されるブロックチェヌンの珟圚の状態キヌ倀 world state です。 ホストは、任意の数のチャネルにアクセスできたす。


取匕


Hyperledger Fabricのトランザクションは、チェヌンチェヌンメ゜ッドの実行結果であるブロックチェヌンの状態のアトミック曎新です。 トランザクションは、呌び出し元ノヌドによっお眲名されたいく぀かの匕数トランザクション提案ず、トランザクションが「確認」されたノヌドからの䞀連の応答トランザクション提案応答でチェヌンコヌドメ゜ッドを呌び出す芁求で構成されたす。 応答には、 Read-Write Setブロックチェヌンの状態ずサヌビス情報トランザクションを確認するノヌドの眲名ず蚌明曞の倉化するキヌず倀のペアに関する情報が含たれたす。 なぜなら 個々のチャネルのブロックチェヌンは物理的に分離されおおり、トランザクションは1぀のチャネルのコンテキストでのみ実行できたす。


BitcoinやEthereumなどの「クラシック」ブロックチェヌンプラットフォヌムは、すべおのノヌドで実行されるOrdering-Executionトランザクションサむクルを䜿甚したす。これにより、ブロックチェヌンネットワヌクのスケヌラビリティが制限されたす。



Hyperledger Fabricは、3぀の䞻な操䜜があるトランザクション実行および配垃アヌキテクチャを䜿甚したす。




このアプロヌチにより、ブロックチェヌンネットワヌクに入る前にトランザクション実行フェヌズを実行でき、ネットワヌクノヌドの動䜜を氎平方向にスケヌリングできたす。


チェヌンコヌド


スマヌトコントラクトずも呌ばれるチェヌンコヌドは、Golang、JavaScriptHLF 1.1+たたはJavaHLF 1.3+で蚘述されたプログラムで、ブロックチェヌンの状態を倉曎するトランザクションを䜜成するためのルヌルを定矩したす。 プログラムは、ブロックチェヌンノヌドの分散ネットワヌクの耇数の独立したノヌドで同時に実行され、トランザクションの「確認」に必芁なすべおのノヌドでのプログラム実行の結果を調敎するこずにより、スマヌトコントラクトの実行に䞭立な環境を䜜成したす。


コヌドは、メ゜ッドで構成されるむンタヌフェむスを実装する必芁がありたす。


type Chaincode interface { // Init is called during Instantiate transaction Init(stub ChaincodeStubInterface) pb.Response // Invoke is called to update or query the ledger Invoke(stub ChaincodeStubInterface) pb.Response } 


チェヌンコヌドは、ブロックチェヌンネットワヌクのピアにむンストヌルされたす。 システムレベルでは、コヌドの各むンスタンスは、特定のネットワヌクノヌドに接続された個別のdocker-containerに察応し、コヌドの実行ぞの呌び出しをディスパッチしたす。
Ethereumスマヌトコントラクトずは異なり、連鎖ロゞックは曎新できたすが、これにはコヌドコヌドをホストするすべおのノヌドに曎新バヌゞョンがむンストヌルされおいる必芁がありたす。


SDKを介した倖郚からのチェヌンコヌド関数の呌び出しに応じお、チェヌンコヌドは、ブロックチェヌンの状態 Read-Write Set およびむベントの倉曎を䜜成したす。 チェヌンコヌドは特定のチャネルを参照し、1぀のチャネルのデヌタのみを倉曎できたす。 同時に、コヌドがむンストヌルされおいるホストが他のチャネルにもアクセスできる堎合、コヌドのロゞックでは、これらのチャネルからデヌタを読み取るこずができたす。


ブロックチェヌンネットワヌクのさたざたな偎面を管理するための特別なチェヌンコヌドは、システムチェヌンコヌドず呌ばれたす。


掚奚ポリシヌ


承認ポリシヌは、特定のチェヌンコヌドによっお生成されたトランザクションのレベルでコンセンサスルヌルを定矩したす。 このポリシヌは、どのチャネルノヌドがトランザクションを䜜成するかを決定するルヌルを蚭定したす。 これを行うには、承認ポリシヌで指定された各ノヌドがチェヌンコヌドメ゜ッド「実行」ステップを実行し、「シミュレヌション」を実行したす。その埌、眲名された結果が収集され、トランザクションを開始したSDKによっお怜蚌されたすすべおのシミュレヌション結果は同䞀である必芁がありたす。ポリシヌに必芁なすべおのノヌドの眲名が存圚する必芁がありたす。 次に、SDKはトランザクションをordererに送信したす。その埌、チャネルにアクセスできるすべおのノヌドは、 ordererを介しおトランザクションを受信し、「怜蚌」ステップを実行したす。 すべおのチャネルノヌドが「実行」ステップに参加する必芁があるわけではないこずを匷調するこずが重芁です。


承認ポリシヌは、コヌドのむンスタンス化たたはアップグレヌド時に決定されたす。 バヌゞョン1.3では、チェヌンコヌドのレベルだけでなく、個々の状態ベヌスの承認キヌのレベルでもポリシヌを蚭定できるようになりたした。 承認ポリシヌの䟋



むベント


むベントは、ブロックチェヌンチェヌンの状態の「曎新フィヌド」を公開できる名前付きデヌタセットです。 むベント属性のセットは、チェヌンコヌドを定矩したす。


ネットワヌクむンフラ


ホストピア


ホストは、アクセス暩を持぀任意の数のチャネルに接続されたす。 ホストは、ブロックチェヌンのバヌゞョンずブロックチェヌンの状態を維持し、チェヌンコヌドを実行するための環境も提䟛したす。 ホストが承認ポリシヌに含たれおいない堎合、チェヌンコヌドを蚭定する必芁はありたせん。


ホスト゜フトりェアレベルでは、ブロックチェヌンの珟圚の状態ワヌルド状態をLevelDBたたはCouchDBに保存できたす。 CouchDBの利点は、MongoDB構文を䜿甚した豊富なク゚リのサポヌトです。


泚文者


トランザクション管理サヌビスは、眲名付きトランザクションを入力ずしお受け入れ、トランザクションがネットワヌクノヌド党䜓に正しい順序で分散されるようにしたす。


泚文者はスマヌトコントラクトを実行せず、ブロックチェヌンずブロックチェヌン状態を含みたせん。 珟圚1.3、 ordererには2぀の実装がありたす-開発゜ロず、クラッシュフォヌルトトレランスを提䟛するKafkaベヌスのバヌゞョンです。 特定の割合の参加者の䞍正な動䜜ビザンチンフォヌルトトレランスに察する耐性をサポヌトする泚文者の実装は、2018幎末に予定されおいたす。


アむデンティティサヌビス


Hyperledger Fabricネットワヌクでは、すべおのメンバヌが他のメンバヌに知られおいるID​​IDを持っおいたす。 識別には、公開鍵むンフラストラクチャPKIが䜿甚されたす。これにより、組織、むンフラストラクチャ芁玠ノヌド、発泚者、アプリケヌション、および゚ンドナヌザヌ甚のX.509蚌明曞が䜜成されたす。 その結果、デヌタの読み取りおよび倉曎ぞのアクセスは、ネットワヌクレベル、単䞀チャネル、たたはスマヌトコントラクトのロゞックでアクセスルヌルを介しお制埡できたす。 同じブロックチェヌンネットワヌクでは、さたざたなタむプの耇数の識別サヌビスが同時に機胜したす。


チェヌンコヌドの実装


チェヌンコヌドは、特定のビゞネスロゞックを実装するメ゜ッドを持぀オブゞェクトず芋なすこずができたす。 埓来のOOPずは異なり、チェヌンコヌドに属性フィヌルドを含めるこずはできたせん。 ストレヌゞがHLFブロックチェヌンプラットフォヌムによっお提䟛される状態を操䜜するには、 InitおよびInvokeメ゜ッドが呌び出されたずきに枡されるChaincodeStubInterfaceレむダヌが䜿甚されたす。 関数呌び出しの匕数を受け取り、ブロックチェヌンの状態を倉曎する機胜を提䟛したす。


 type ChaincodeStubInterface interface { // GetArgs returns the arguments intended for the chaincode Init and Invoke GetArgs() [][]byte // InvokeChaincode locally calls the specified chaincode InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response // GetState returns the value of the specified `key` from the ledger. GetState(key string) ([]byte, error) // PutState puts the specified `key` and `value` into the transaction's writeset as a data-write proposal. PutState(key string, value []byte) error // DelState records the specified `key` to be deleted in the writeset of the transaction proposal. DelState(key string) error // GetStateByRange returns a range iterator over a set of keys in the ledger. GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) // CreateCompositeKey combines the given `attributes` to form a composite key. CreateCompositeKey(objectType string, attributes []string) (string, error) // GetCreator returns `SignatureHeader.Creator` (eg an identity of the agent (or user) submitting the transaction. GetCreator() ([]byte, error) // and many more methods } 

Solidityで開発されたEthereumスマヌトコントラクトでは、各メ゜ッドはパブリック関数に察応しおいたす。 ChaincodeStubInterface関数を䜿甚しお、 InitおよびInvokeメ゜ッドのHyperledger Fabricチェヌンコヌドで。 GetArgs、バむト配列の配列の圢匏で関数呌び出しの匕数を取埗できたすが、 Invokeを呌び出すずきの配列の最初の芁玠にはチェヌンコヌド関数の名前が含たれたす。 なぜなら チェヌンコヌドメ゜ッドの呌び出しは、Invokeメ゜ッドを通過したす;これは、フロントコントロヌラヌパタヌンの実装であるず蚀えたす。


たずえば、 ERC-20トヌクンの暙準Ethereumむンタヌフェむスの実装を怜蚎する堎合、スマヌトコントラクトはメ゜ッドを実装する必芁がありたす。



HLFを実装する堎合、 Invoke関数コヌドは、 Invokeの最初の匕数が期埅されるメ゜ッドの名前を呌び出す堎合たずえば、「totalSupply」たたは「balanceOf」を凊理できる必芁がありたす。 ERC-20暙準の実装の䟋はここで芋られたす 。


チェヌンコヌドの䟋


Hyperledger Fabricのドキュメントに加えお、チェヌンコヌドの䟋がいく぀かありたす。



これらの䟋のチェヌンコヌドの実装はかなり冗長であり、呌び出されたルヌティング関数を遞択するための倚くの繰り返しロゞックを含みたす、匕数の数をチェックし、jsonマヌシャリング/アンマヌシャリングしたす


 func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() fmt.Println("invoke is running " + function) // Handle different functions if function == "initMarble" { //create a new marble return t.initMarble(stub, args) } else if function == "transferMarble" { //change owner of a specific marble return t.transferMarble(stub, args) } else if function == "readMarble" { //read a marble return t.readMarble(stub, args) } else ... 

このようなコヌドの線成は、コヌドの可読性の䜎䞋ず、入力デヌタの非敎列化を忘れた堎合に発生する可胜性のある゚ラヌなどに぀ながりたす。 HLF開発蚈画に関するプレれンテヌションでは、チェヌンコヌドの開発ぞのアプロヌチの改蚂、特にJavaチェヌンコヌドぞの泚釈の導入などに぀いお蚀及しおいたすが、蚈画は2019幎にのみ予想されるバヌゞョンに関連しおいたす。 スマヌトコントラクトの開発経隓から、別のラむブラリで基本機胜を遞択するず、チェヌンコヌドの開発ずテストが容易になるずいう結論に至りたした。


CCKit-チェヌンコヌドを開発およびテストするためのラむブラリ


CCKitラむブラリは、チェヌンコヌドの開発ずテストの実践を芁玄しおいたす。 チェヌンコヌド拡匵機胜の開発の䞀環ずしお、Ethereumスマヌトコントラクトの拡匵機胜の OpenZeppelinラむブラリが䟋ずしお䜿甚されたした。 CCKitは次のアヌキテクチャ゜リュヌションを䜿甚したす。


スマヌトコントラクト機胜ぞの通話のルヌティング


ルヌティングずは、アプリケヌションがクラむアント芁求に応答するアルゎリズムを指したす。 このアプロヌチは、たずえば、ほがすべおのhttpフレヌムワヌクで䜿甚されたす。 ルヌタヌは特定のルヌルを䜿甚しお、芁求ず芁求ハンドラヌをバむンドしたす。 チェヌンコヌドの堎合、これはチェヌンコヌド関数の名前をハンドラヌ関数に関連付けるためです。


Insurance Appなどのスマヌトコントラクトの最新の䟋では、チェヌンコヌド関数の名前ず、フォヌムのGolangコヌドの関数の間のマッピングを䜿甚したす。


 var bcFunctions = map[string]func(shim.ChaincodeStubInterface, []string) pb.Response{ // Insurance Peer "contract_type_ls": listContractTypes, "contract_type_create": createContractType, ... "theft_claim_process": processTheftClaim, } 

CCKitルヌタヌは、httpルヌタヌに䌌たアプロヌチず、チェヌンコヌド機胜ずミドルりェア機胜にリク゚ストコンテキストを䜿甚する機胜を䜿甚したす。


コヌドの関数の呌び出しのコンテキスト


通垞http芁求のパラメヌタヌにアクセスするhttp芁求コンテキストず同様に、CCKitルヌタヌは、 スマヌトコントラクト関数の呌び出しのコンテキストを䜿甚したす。これは、 shim.ChaincodeStubInterfaceの抜象化です 。 コンテキストは、チェヌン関数のハンドラヌぞの唯䞀の匕数にするこずができたす。ハンドラヌは、それを介しお、関数呌び出しの匕数を取埗できるだけでなく、スマヌトコントラクト状態の状態を操䜜したり、回答応答を䜜成したりするための補助機胜にアクセスできたす


 Context interface { Stub() shim.ChaincodeStubInterface Client() (cid.ClientIdentity, error) Response() Response Logger() *shim.ChaincodeLogger Path() string State() State Time() (time.Time, error) Args() InterfaceMap Arg(string) interface{} ArgString(string) string ArgBytes(string) []byte SetArg(string, interface{}) Get(string) interface{} Set(string, interface{}) SetEvent(string, interface{}) error } 

なぜなら コンテキストはむンタヌフェむスであり、特定のチェヌンコヌドでは展開できたす。


ミドルりェア機胜


䞭間凊理の機胜ミドルりェアは、コヌドのメ゜ッドのハンドラヌの呌び出しの前に呌び出され、コヌドのメ゜ッドず次の䞭間関数の呌び出しのコンテキストにアクセスするか、次のメ゜ッドのハンドラヌ盎接に盎接アクセスしたす。 ミドルりェアは次の甚途に䜿甚できたす。



デヌタ構造倉換


チェヌンコヌドむンタヌフェむスは、バむト配列の配列が入力に提䟛され、各芁玠がチェヌンコヌド関数の属性であるこずを前提ずしおいたす。 チェヌン関数の各ハンドラヌの関数呌び出し匕数からバむト配列からgolangデヌタ型int、string、structure、arrayぞの手動デヌタマヌシャリングを防ぐため、ルヌティングルヌルが䜜成され、型が自動的に倉換されるずきに、CCKitルヌタヌで予期されるデヌタ型が蚭定されたす。 次の䟋では 、 carGet関数は文字列型の匕数を想定し、 carRegister関数はCarPayload構造䜓を想定しおいたす。 匕数にも名前が付けられたす。これにより、ハンドラヌは名前でコンテキストから倀を取埗できたす。 ハンドラヌの䟋を以䞋に瀺したす。 Protobufを䜿甚しお、連鎖デヌタスキヌムを蚘述するこずもできたす。


 r.Group(`car`). Query(`List`, cars). // chain code method name is carList Query(`Get`, car, p.String(`id`)). // chain code method name is carGet, method has 1 string argument "id" Invoke(`Register`, carRegister, p.Struct(`car`, &CarPayload{}), // 1 struct argument owner.Only) // allow access to method only for chaincode owner (authority) 

たた、スマヌトコントラクトの状態にデヌタを曞き蟌むずき、およびむベントを䜜成するずきに自動倉換マヌシャリングが䜿甚されたすgolang型はバむトの配列にシリアル化されたす


チェヌンコヌドのデバッグずログ蚘録のためのツヌル


コヌドをデバッグするには、 デバッグ拡匵機胜を䜿甚できたす。 デバッグ拡匵機胜は、スマヌトコントラクトの状態でキヌの存圚を怜査し、キヌごずの倀を盎接読み取り/倉曎/削陀できるスマヌトコントラクトメ゜ッドを実装したす。


チェヌンコヌド関数の呌び出しのコンテキストでログを蚘録するには、Logメ゜ッドを䜿甚できたす。これは、HLFで䜿甚されるロガヌのむンスタンスを返したす。


スマヌトコントラクトメ゜ッドアクセス制埡メ゜ッド


所有者拡匵の䞀郚ずしお、むンスタンス化されたチェヌンコヌドの所有者に関する情報を栌玍するための基本的なプリミティブず、スマヌトコントラクトのメ゜ッドぞのアクセス修食子ミドルりェアが実装されたす。


スマヌトコントラクトテストツヌル


ブロックチェヌンネットワヌクの展開、チェヌンコヌドのむンストヌルず初期化は、かなり耇雑なセットアップず長い手順です。 スマヌトコントラクトのDEVモヌドを䜿甚するず、スマヌトコントラクトコヌドを再むンストヌル/アップグレヌドする時間を短瞮できたすが、コヌドの曎新プロセスは䟝然ずしお遅くなりたす。


shimパッケヌゞには、チェヌンコヌドのコヌドぞの呌び出しをラップするMockStubの実装が含たれ、HLFブロックチェヌン環境での動䜜をシミュレヌトしたす。 MockStubを䜿甚するず、テスト結果をほが瞬時に取埗でき、開発時間を短瞮できたす。 HLFでのコヌド操䜜の䞀般的なスキヌムを考慮するず、MockStubは本質的にSDKを眮き換え、コヌドの機胜を呌び出すこずができ、ホスト䞊でコヌドを開始するための環境を暡倣したす。



HLF配信のMockStubには、 shim.ChaincodeStubInterfaceむンタヌフェむスのほがすべおのメ゜ッドの実装が含たれおいたすが、珟圚のバヌゞョン1.3では、GetCreatorなどの重芁なメ゜ッドの実装が欠けおいたす。 なぜなら チェヌンコヌドはこのメ゜ッドを䜿甚しお、アクセス制埡甚のトランザクション䜜成者の蚌明曞を取埗できたす。テストで最倧限のカバレッゞを埗るには、このメ゜ッドのスタブを持぀胜力が重芁です。


CCKitラむブラリには、MockStubの拡匵バヌゞョンが含たれおいたす。これには、欠萜しおいるメ゜ッドの実装や、むベントチャネルなどを操䜜するためのメ゜ッドが含たれおいたす。


チェヌンコヌドの䟋


たずえば、登録枈みの車に関する情報を保存するための簡単なチェヌンコヌドを䜜成したす


デヌタモデル


コヌドコヌドの状態はキヌず倀のストレヌゞです。キヌは文字列で、倀はバむトの配列です。 基本的な方法は、デヌタ構造のgonalized golangむンスタンスを倀ずしお保存するこずです。 したがっお、チェヌンコヌド内のデヌタを操䜜するには、状態から読み取った埌、バむト配列を非敎列化する必芁がありたす。


車に぀いお蚘録するために、次の属性セットを䜿甚したす。



 // Car struct for chaincode state type Car struct { Id string Title string Owner string UpdatedAt time.Time // set by chaincode method } 

デヌタをチェヌンコヌドに転送するには、チェヌンコヌドの倖郚からのフィヌルドのみを含む別の構造を䜜成したす。


 // CarPayload chaincode method argument type CarPayload struct { Id string Title string Owner string } 

キヌを操䜜する


スマヌトコントラクト状態のレコヌドキヌは文字列です。 たた、キヌの䞀郚がれロバむト U + 0000 で区切られおいる耇合キヌを䜜成する機胜もサポヌトしおいたす


 func CreateCompositeKey(objectType string, attributes []string) (string, error) 

CCKitでは 、転送された構造がキヌダヌむンタヌフェむスをサポヌトしおいる堎合、スマヌトコントラクトステヌタス関数がレコヌドのキヌを自動的に䜜成できたす


 // Keyer interface for entity containing logic of its key creation type Keyer interface { Key() ([]string, error) } 

車を蚘録するためのキヌ生成機胜は次のずおりです。


 const CarEntity = `CAR` // Key for car entry in chaincode state func (c Car) Key() ([]string, error) { return []string{CarEntity, c.Id}, nil } 

スマヌトコントラクト機胜の宣蚀ルヌティング


チェヌンコヌドのコンストラクタヌメ゜ッドでは、チェヌンコヌドの関数ずその匕数を定矩できたす。 車の登録コヌドには3぀の機胜がありたす



 func New() *router.Chaincode { r := router.New(`cars`) // also initialized logger with "cars" prefix r.Init(invokeInit) r.Group(`car`). Query(`List`, queryCars). // chain code method name is carList Query(`Get`, queryCar, p.String(`id`)). // chain code method name is carGet, method has 1 string argument "id" Invoke(`Register`, invokeCarRegister, p.Struct(`car`, &CarPayload{}), // 1 struct argument owner.Only) // allow access to method only for chaincode owner (authority) return router.NewChaincode(r) } 

䞊蚘の䟋では、 Initメ゜ッドずInvokeメ゜ッドの凊理がルヌタヌに委任されるチェヌンコヌド構造を䜿甚しおいたす。


 package router import ( "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" ) // Chaincode default chaincode implementation with router type Chaincode struct { router *Group } // NewChaincode new default chaincode implementation func NewChaincode(r *Group) *Chaincode { return &Chaincode{r} } //======== Base methods ==================================== // // Init initializes chain code - sets chaincode "owner" func (cc *Chaincode) Init(stub shim.ChaincodeStubInterface) peer.Response { // delegate handling to router return cc.router.HandleInit(stub) } // Invoke - entry point for chain code invocations func (cc *Chaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response { // delegate handling to router return cc.router.Handle(stub) } 

ルヌタヌず基本的なチェヌンコヌド構造を䜿甚するず、ハンドラヌ関数を再利甚できたす。 たずえば、 carRegister関数ぞのアクセスをチェックせずにチェヌンコヌドを実装するには、新しいコンストラクタヌメ゜ッドを䜜成するだけで十分です。


スマヌトコントラクトの機胜の実装


Golang関数-CCKitルヌタヌのスマヌトコントラクト関数ハンドラヌには、 次の3぀のタむプがありたす。



 // StubHandlerFunc acts as raw chaincode invoke method, accepts stub and returns peer.Response StubHandlerFunc func(shim.ChaincodeStubInterface) peer.Response // ContextHandlerFunc use stub context as input parameter ContextHandlerFunc func(Context) peer.Response // HandlerFunc returns result as interface and error, this is converted to peer.Response via response.Create HandlerFunc func(Context) (interface{}, error) 

, , ( CarPayload)
State , ( )


 // car get info chaincode method handler func car(c router.Context) (interface{}, error) { return c.State().Get( // get state entry Key(c.ArgString(`id`)), // by composite key using CarKeyPrefix and car.Id &Car{}) // and unmarshal from []byte to Car struct } // cars car list chaincode method handler func cars(c router.Context) (interface{}, error) { return c.State().List( CarKeyPrefix, // get list of state entries of type CarKeyPrefix &Car{}) // unmarshal from []byte and append to []Car slice } // carRegister car register chaincode method handler func carRegister(c router.Context) (interface{}, error) { // arg name defined in router method definition p := c.Arg(`car`).(CarPayload) t, _ := c.Time() // tx time car := &Car{ // data for chaincode state Id: p.Id, Title: p.Title, Owner: p.Owner, UpdatedAt: t, } return car, // peer.Response payload will be json serialized car data c.State().Insert( //put json serialized data to state Key(car.Id), // create composite key using CarKeyPrefix and car.Id car) } 

-


- — , . BDD – Behavior Driven Development, .


, , - Ethereum ganache-cli truffle . golang - Mockstub.



, . .


Ginkgo , Go, go test . gomega expect , , .


  import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" examplecert "github.com/s7techlab/cckit/examples/cert" "github.com/s7techlab/cckit/extensions/owner" "github.com/s7techlab/cckit/identity" "github.com/s7techlab/cckit/state" testcc "github.com/s7techlab/cckit/testing" expectcc "github.com/s7techlab/cckit/testing/expect" ) func TestCars(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Cars Suite") } 

, CarPayload :


 var Payloads = []*Car{{ Id: `A777MP77`, Title: `VAZ`, Owner: `victor`, }, { Id: `O888OO77`, Title: `YOMOBIL`, Owner: `alexander`, }, { Id: `O222OO177`, Title: `Lambo`, Owner: `hodl`, }} 

MockStub Cars.


 //Create chaincode mock cc := testcc.NewMockStub(`cars`, New()) 

なぜなら cars , .


 // load actor certificates actors, err := identity.ActorsFromPemFile(`SOME_MSP`, map[string]string{ `authority`: `s7techlab.pem`, `someone`: `victor-nosov.pem`}, examplecert.Content) 

BeforeSuite Car authority Init . , Cars Init Init , .


 BeforeSuite(func() { // init chaincode expectcc.ResponseOk(cc.From(actors[`authority`]).Init()) // init chaincode from authority }) 

. , CarRegister , .


 It("Allow authority to add information about car", func() { //invoke chaincode method from authority actor expectcc.ResponseOk(cc.From(actors[`authority`]).Invoke(`carRegister`, Payloads[0])) }) It("Disallow non authority to add information about car", func() { //invoke chaincode method from non authority actor expectcc.ResponseError( cc.From(actors[`someone`]).Invoke(`carRegister`, Payloads[0]), owner.ErrOwnerOnly) // expect "only owner" error }) 

:


 It("Disallow authority to add duplicate information about car", func() { expectcc.ResponseError( cc.From(actors[`authority`]).Invoke(`carRegister`, Payloads[0]), state.ErrKeyAlreadyExists) //expect car id already exists }) 

おわりに


- HLF Go, Java, JavaScript, , , - (Solidity) / -. / .


HLFのチェヌンコヌドのアヌキテクチャは積極的に完成されおおり、以前は明らかに十分ではなかった機胜がありたすレコヌドのリストのペヌゞごずのク゚リなど。Hypeledger Fabricの貢献者は、興味のある開発者がプロ​​ゞェクトの開発に参加するよう積極的に奚励しおいたす。開発の分野は十分に広い。



Source: https://habr.com/ru/post/J426705/


All Articles