https://github.com/hyperledger/composer-sample-networks の例をあげながらとりあえず手っ取り早くComposer扱うときのTIPSをまとめます
Hyperledger Composer をたじたじと使ってみた(環境構築編)
Hyperledger Composer をたじたじと使ってみた(作成編)
に書いた事は前提になってます。
定義体の作成
ソースは composer-sample-networks/packages/perishable-network/models/perishable.cto
より引用
はじめに
このファイルでデータベース、内部構造体、APIの形が決まります。
必要なデータ(とAPI)の構造をガシガシ定義しましょう。
そのうち意味がわかる細々したもの
-
o
って何?- データの実態があるフィールドを指します。
-
-->
って何?- 「他のデータ」を参照するためのフィールドを示します。そのフィールドの中にはデータの識別子が入る必要があり、存在しないものは入れる事ができません。
プリミティブ型
最初に使える型は以下のものがあります。
- String: a UTF8 encoded String.
- Double: a double precision 64 bit numeric value.
- Integer: a 32 bit signed whole number.
- Long: a 64 bit signed whole number.
- DateTime: an ISO-8601 compatible time instance, with optional time zone and UTZ offset.
- Boolean: a Boolean value, either true or false.
DateTimeはUnixtimeではないので注意してください。
構造体の定義
ふつーの構造体です。この定義自体に意味はなく、ほかの定義から使用するために定義します。
/**
* A concept for a simple street address
*/
concept Address {
o String city optional
o String country
o String street optional
o String zip optional
}
participant
実体データその1。内部でassetとどう区別されているのかよくわかりません(!)が、「ユーザ」とかなんとか、モノを操作する主体に対する定義です。
-
abstract
、extends
-
abstract
で定義したものをそのまま他の構造体でextends
で使用できます。イメージどおり(?)のものだとおもいます。
-
-
identified by ...
- これで指定したフィールドで要素をユニークに識別します。主キーみたいなもんです。
/**
* An abstract participant type in this business network
*/
abstract participant Business identified by email {
o String email
o Address address
o Double accountBalance
}
/**
* A Grower is a type of participant in the network
*/
participant Grower extends Business {
}
Asset
実体データその2。
「商品」とかなんとか、モノを示すのに使用する定義みたいです。
/**
* A shipment being tracked as an asset on the ledger
*/
asset Shipment identified by shipmentId {
o String shipmentId
o ProductType type
o ShipmentStatus status
o Long unitCount
o TemperatureReading[] temperatureReadings optional
--> Contract contract
}
他のデータ実体を参照するためのフィールドは -->
を頭につけます。 o
がついてるのはそれそのものが実体みたいです。多分。
Transaction
ブロックチェーンに命令を出すためのAPI定義をします。ここで定義した型がそのまま REST API とかに必要なフィールドになります。すでにあるデータを示す必要のあるフィールドは -->
で定義します。
/**
* An abstract transaction that is related to a Shipment
*/
abstract transaction ShipmentTransaction {
--> Shipment shipment
}
/**
* A notification that a shipment has been received by the
* importer and that funds should be transferred from the importer
* to the grower to pay for the shipment.
*/
transaction ShipmentReceived extends ShipmentTransaction {
}
enum
名前の通りです、選択的な値を定義するのに使います。
/**
* The type of perishable product being shipped
*/
enum ProductType {
o BANANAS
o APPLES
o PEARS
o PEACHES
o COFFEE
}
もっと詳しい事が知りたかったら Hyperledger Composer Modeling Language を参照ください。
ロジックの作成
定義
/**
* A shipment has been received by an importer
* @param {org.acme.shipping.perishable.ShipmentReceived} shipmentReceived - the ShipmentReceived transaction
* @transaction
*/
async function payOut(shipmentReceived) {
....
await shipmentRegistry.update(shipment);
}
@param
のあとにかかれている型のトランザクションを受け取ったときに直下の関数がコールされます。コメントなのにね。
要素のアクセスはそんなに考える事なく (定義体の定義の通りに) shipmentReceived.shipment.shipmentId
とかで要素にアクセスできます。
ちなみに最後は promis を返す必要があります。(コードでは return が省略されていますが、その場合JSでは行の最後に書かれた文の結果が戻り値になります。)
データの追加更新操作で終わる事が多いでしょうからあまり気にしなくてもいいかも。
操作
基本的なサイクルは以下の通り
async function setupDemo(setupDemo) {
// ファクトリの取得(データの新規作成とかに使う
const factory = getFactory();
// 名前空間はよくつかうから定義しておく
const NS = 'org.acme.shipping.perishable';
// ...
// 新規のデータ作成はファクトリから生成する
const shipper = factory.newResource(NS, 'Shipper', 'shipper@email.com');
// 構造体もファクトリから取得する必要がある。
const shipperAddress = factory.newConcept(NS, 'Address');
// あとは割と普通に内容の操作ができる。
shipperAddress.country = 'Panama';
shipper.address = shipperAddress;
shipper.accountBalance = 0;
// ...
const contract = factory.newResource(NS, 'Contract', 'CON_001');
// データから他のデータを参照する必要があるときは Relationship とやらを作成する。
contract.grower = factory.newRelationship(NS, 'Grower', 'farmer@email.com');
// ...
// 実際にストアにデータを追加するための口(レジストリ)を取得する。
// 非同期メソッドなので await をつけないと大変な事になる。
const shipperRegistry = await getParticipantRegistry(NS + '.Shipper');
// レジストリから追加を行う。これが promis を返す。
// もちろん更新や削除のメソッドもある。
await shipperRegistry.addAll([shipper]);
// ちなみにすでにあるデータを取得するときもレジストリ経由で行う。
// ...
}