Hyperledger composerで作ったものをIBM Cloud上にデプロイしてみる

はじめに

ネタばらしを先にいくと、こちらで行われているハンズオンでやってること/言ってることそのままです。

初心者なので写経の気持ちで。

Hyperledger composer

Hyperledger自身の説明については「クローズドな志向のブロックチェーンプラットフォーム」に留めるのであとは詳しい方たちに任せます。
開発言語ですが、そのまま作るとGo言語かJavaになり、細かくピアの定義などもできるみたいなのですが、Javaと聞くと敷居高そうですよね、、、、半年前ならJavaとJavaScriptの違いも分からなかったので「へー」だったのですが、初学者な今ではJavaなぞ聞くだけで尻尾を巻いて逃げ出します。
というわけで、Hyperledgerの開発環境がHyperledger composerです。
Hyperledger composer playgroundというWeb開発ツールをします。
こちら→https://composer-playground.mybluemix.net/

サンプルを作る

上記にアクセスし、新規作成します。
Hyperledger_Composer_と_Hyperledger_Composer.png
business networkと呼んでいますが、プロジェクトみたいなものですね。

新規作成としては以下の3つが与えられています

  • 完全なまっさらな状態から作る(empty-business-network)
  • 最低限のものから自分で作る(basic-sample-network)
  • サンプルから作る(samples on npmのところ)

Hyperledger_Composer.png
今回はカーオークションのためのサンプルを使ってみます。
adminを作成する必要がるので適当にやっときます。

つくるとこんな感じになります。
Hyperledger_Composer.png
Hyperledger composerは3つの要素があり、左側に並んでるのがそれです。

  • Model
  • Logic
  • ACL

それぞれいきます

Model File

Model
資産(asset)と参加者(participant)、トランザクションを定義するもの。
今回のサンプルでのデフォルトは以下です。
javaをもとにしてるらしく、メソッドなどを調べるとjavaの記事に多くたどり着きました。
namespaceとかDoubleとかenumとかabstractとか

namespace org.acme.vehicle.auction //名前空間

asset Vehicle identified by vin {
  o String vin  //固有の番号らしい
  --> Member owner  //Member(下のほうで定義)というクラスの中のownerで構成
}
// 車(Vehicle)を資産とする。
// vinで定義

enum ListingState {
  o FOR_SALE //販売中
  o RESERVE_NOT_MET //最低価格未満
  o SOLD //売り切れ
}
// 出品リストのステータス(ListingState)を列挙型で定義


asset VehicleListing identified by listingId {
  o String listingId
  o Double reservePrice  //最低落札価格
  o String description  //詳細説明
  o ListingState state  //ステータス
  o Offer[] offers optional  // offerというトランザクションの際のoptional
  --> Vehicle vehicle  //Vehicle
}
// Vehicleを資産する。
// listingIdで定義


abstract participant User identified by email {
  o String email
  o String firstName
  o String lastName
}
// Userという抽象クラスを作成


participant Member extends User {
  o Double balance
}
//Memberという参加者を定義
//Userに加えて、残高(Balance)を持つ

participant Auctioneer extends User {
}
// Auctioneerという参加者を定義

transaction Offer {
  o Double bidPrice  
  --> VehicleListing listing
  --> Member member
}
// Offerというトランザクションを定義



transaction CloseBidding {
  --> VehicleListing listing
}
// CloseBiddingというトランザクションを定義

Script File

Logic
トランザクションの中身を定義するもの
JavaScriptで書かれています。

async function closeBidding(closeBidding) {  // eslint-disable-line no-unused-vars
    const listing = closeBidding.listing;
    if (listing.state !== 'FOR_SALE') {
        throw new Error('Listing is not FOR SALE');
    }
    // by default we mark the listing as RESERVE_NOT_MET
    listing.state = 'RESERVE_NOT_MET';
    let highestOffer = null;
    let buyer = null;
    let seller = null;
    if (listing.offers && listing.offers.length > 0) {
        // sort the bids by bidPrice
        listing.offers.sort(function(a, b) {
            return (b.bidPrice - a.bidPrice);
        });
        highestOffer = listing.offers[0];
        if (highestOffer.bidPrice >= listing.reservePrice) {
            // mark the listing as SOLD
            listing.state = 'SOLD';
            buyer = highestOffer.member;
            seller = listing.vehicle.owner;
            // update the balance of the seller
            console.log('#### seller balance before: ' + seller.balance);
            seller.balance += highestOffer.bidPrice;
            console.log('#### seller balance after: ' + seller.balance);
            // update the balance of the buyer
            console.log('#### buyer balance before: ' + buyer.balance);
            buyer.balance -= highestOffer.bidPrice;
            console.log('#### buyer balance after: ' + buyer.balance);
            // transfer the vehicle to the buyer
            listing.vehicle.owner = buyer;
            // clear the offers
            listing.offers = null;
        }
    }

    if (highestOffer) {
        // save the vehicle
        const vehicleRegistry = await getAssetRegistry('org.acme.vehicle.auction.Vehicle');
        await vehicleRegistry.update(listing.vehicle);
    }

    // save the vehicle listing
    const vehicleListingRegistry = await getAssetRegistry('org.acme.vehicle.auction.VehicleListing');
    await vehicleListingRegistry.update(listing);

    if (listing.state === 'SOLD') {
        // save the buyer
        const userRegistry = await getParticipantRegistry('org.acme.vehicle.auction.Member');
        await userRegistry.updateAll([buyer, seller]);
    }
}

/**
 * Make an Offer for a VehicleListing
 * @param {org.acme.vehicle.auction.Offer} offer - the offer
 * @transaction
 */
async function makeOffer(offer) {  // eslint-disable-line no-unused-vars
    let listing = offer.listing;
    if (listing.state !== 'FOR_SALE') {
        throw new Error('Listing is not FOR SALE');
    }
    if (!listing.offers) {
        listing.offers = [];
    }
    listing.offers.push(offer);

    // save the vehicle listing
    const vehicleListingRegistry = await getAssetRegistry('org.acme.vehicle.auction.VehicleListing');
    await vehicleListingRegistry.update(listing);
}

getAssetRegistryというので台帳に書き込んでるらしいです

Access Control

ACL
各参加者の権限付

rule Auctioneer {
    description: "Allow the auctioneer full access"
    participant: "org.acme.vehicle.auction.Auctioneer"
    operation: ALL
    resource: "org.acme.vehicle.auction.*"
    action: ALLOW
}

rule Member {
    description: "Allow the member read access"
    participant: "org.acme.vehicle.auction.Member"
    operation: READ
    resource: "org.acme.vehicle.auction.*"
    action: ALLOW
}

rule VehicleOwner {
    description: "Allow the owner of a vehicle total access"
    participant(m): "org.acme.vehicle.auction.Member"
    operation: ALL
    resource(v): "org.acme.vehicle.auction.Vehicle"
    condition: (v.owner.getIdentifier() == m.getIdentifier())
    action: ALLOW
}

rule VehicleListingOwner {
    description: "Allow the owner of a vehicle total access to their vehicle listing"
    participant(m): "org.acme.vehicle.auction.Member"
    operation: ALL
    resource(v): "org.acme.vehicle.auction.VehicleListing"
    condition: (v.vehicle.owner.getIdentifier() == m.getIdentifier())
    action: ALLOW
}

rule SystemACL {
    description:  "System ACL to permit all access"
    participant: "org.hyperledger.composer.system.Participant"
    operation: ALL
    resource: "org.hyperledger.composer.system.**"
    action: ALLOW<img width="1050" alt="カタログ_-_IBM_Cloud.png" src="https://qiita-image-store.s3.amazonaws.com/0/246332/1daa745a-bdb9-0831-2157-b3b92278d576.png">

}

rule NetworkAdminUser {
    description: "Grant business network administrators full access to user resources"
    participant: "org.hyperledger.composer.system.NetworkAdmin"
    operation: ALL
    resource: "**"
    action: ALLOW
}

rule NetworkAdminSystem {
    description: "Grant business network administrators full access to system resources"
    participant: "org.hyperledger.composer.system.NetworkAdmin"
    operation: ALL
    resource: "org.hyperledger.composer.system.**"
    action: ALLOW
}

車の所有者(VehicleOwner)と出品者(VehicleListingOwner)が同じだったら両方の権限を与えるようになってます

エクスポート

Hyperledger fabricにデプロイできる形で出力。
左下のExport

IBM Cloud上でブロックチェーンプラットフォームを作成

Hyperledger FablicといえばIBMなのでIBM Cloud上で作成します。
IBMアカウントがあり、カードなどを登録していれば、Starterプランというので無料で使えます。

コードのインストール

先程出力したbnaデータをインストールすると動かせるようになります
IBM_Blockchain.png

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.