まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:ibm

IBM Watson から提供されている Visual Recognition(画像認識) API の1つで、ずっとベータ版として提供されていた Similarity Search (類似画像検索)API が正式公開となる前にサービス終了となりました:
Visual Recognition API – Similarity Search Update


非常に簡単に使うことができて、機能も便利で、ベータ版と知りつつ自分もいくつかのプロダクトの中で使ったり、そのサービスをブログやイベント等で紹介したりもしていました。詳しい経緯はわかりませんが、サービスが終了になったことは非常に残念でした。

実は以前にも同じようなことがありました。いわゆる「トレードオフ分析」を行う機能を API として提供していた Watson Tradeoff Analytics API は 2017/05/11 を以ってサービスの追加ができなくなりました(作成済みのサービスは 2018/04 まで使えます)。この API はちょうど自分がこの分析手法を勉強し始めた頃にサービス終了がアナウンスされ、とても残念でした。

この API に関してはちょうどトレードオフ分析を勉強しはじめた当初だったこともあり、自分でもトレードオフアルゴリズムを理解し始めた頃でもありました。で、こんな互換 API を作って公開したのでした:
Yet Another Watson Tradeoff Analytics API


さて話を元に戻して今回の Similarity Search API のサービス終了です。これも非常に残念なことでした。トレードオフ分析の時と異なるのは自分が画像検索アルゴリズムについて勉強していたわけではなく、互換 API を作るにはちとハードルが高かったということでした。

が、調べてみると画像検索にはいくつかの考え方やアルゴリズム、それらごとの向き/不向きがあって、比較的簡単に実現する方法もないわけではない、ということがわかりました。その1つがカラーヒストグラムを使った方法でした:
類似画像検索とカラーヒストグラム


オリジナルの Similarity Search API がこのアルゴリズムを使っていたかどうかは分からない(おそらく使っていない)のですが、とりあえずこのアルゴリズムによって「画像を学習する」&「学習した画像の中から類似した画像を見つける」という最低限必要な機能は実装できそうだ、と思えるようになりました。というわけで今回も互換 API の開発に挑戦してみたのでした。そしてとりあえず出来上がったものをオープンソース化したのがこちらです:
YASS(Yet Another Similarity Search)

2017092400



実装の対象としたのは Watson API Explorer の Visual Recognition API のページを参考に "Collections" カテゴリと "collection images" カテゴリにある全 12 API 、そしてオリジナルにはなかった「学習画像のバイナリを取得する API 」を合わせた 13 API です。オリジナル API のパラメータについては元のものとほぼ互換性を付けたつもりです。また新しい API のパラメータは URL 部分以外にないので特に説明は不要と思っています。なお「ほぼ互換性」という表現を使いましたが、互換性のない箇所は以下のとおりです:
  • オリジナル API の実行時に指定が必要だった API Key を廃止しました。実行時に API Key の指定は不要です。代わりに(必要であれば)Basic 認証をかけることができるようにしました。
  • オリジナル API の実行時に指定が必要だった version パラメータを廃止しました。実行時に version の指定は不要です。正確には version = '2016-05-20' の仕様に従ったものを実装しています。
  • オリジナル API の制限事項であった「1コレクションに対して 1,000,000 画像まで」という制約はありません。ただし画像バイナリごと Cloudant に格納されるため、Cloudant の容量にはお気をつけください。
  • 学習させた画像のバイナリ(画像そのもの)を取得するための API : GET /v3/collections/{collection_id}/images/{image_id}/binary を新たに追加しました。collection_id と image_id を指定して実行すると Content-Type と合わせて画像バイナリが API サーバーから返されます。
  • API 実行後の HTTP レスポンスには status という属性を含めるようにしました(このため、実行結果のフォーマットには互換性がありません)。status = true の場合は成功、false の場合はなんらかのエラーが発生したことを意味しています。

【前提環境】
この互換 API は Node.js で記述されています。従って API サーバーには Node.js のランタイム(と npm)がインストールされている必要があります。また画像や検索に必要な情報は Cloudant データベースに格納されます。2017/Sep/24 現在の仕様では IBM Bluemix 上の Cloudant を使う前提で記述されているため、IBM Bluemix のアカウントを取得して、Cloudant サービスを1つインスタンス化し、その username と password を用意してください。


【インストール方法】
(1) 上記サイトから git clone するか、zip download &展開して、ソースコードを取得します。
(2) ソースコードの settings.js を編集します(青字はコメント。最低限編集が必要になるのは上2つ):
exports.cloudant_username = '(Cloudant Username)';  // Cloudant の username(必須)
exports.cloudant_password = '(Cloudant Password)';  // Cloudant の password(必須)
exports.cloudant_db_prefix = 'yass_';               // Cloudant に作成するデータベース名の prefix(必須だが変更しなくてもよい)
exports.basic_username = 'username';                // Basic 認証をかける場合の username(''にした場合は Basic 認証なし)
exports.basic_password = 'password';                // Basic 認証をかける場合の password(''にした場合は Basic 認証なし)
exports.api_base_path = '/v3';                      // API の基本パス(''でもよいし、変更してもしなくても良い)
(3) npm install を実行
例) $ npm install

(4) Node.js で app.js を実行
例) $ node app.js

(5) curl やアプリケーションから API を利用
例) $ curl -XGET 'http://localhost:6001/v3/collections'


【システム構成】
Similarity Search API の "Collection" に相当する部分を「Cloudant のデータベース」に、画像データに相当する部分を「Cloudant のドキュメント」にみなして格納するようにしました。ドキュメントには attachment として画像ファイルそのものを格納すると同時に、類似検索ができるよう正規化されたカラーヒストグラム情報や、画像データの metadata を合わせて格納するようにしています:
2017092401


類似検索 API(POST /v3/collections/{collection_id}/find_similar)を実行した時の挙動はまず Collection から全画像を取り出し(つまり Collection に該当する Cloudant データベースから全ドキュメントを取り出し)、カラーヒストグラム情報を1つずつ取り出しては色の類似性を調べて、その上位何件かを返すというアルゴリズムにしています。必ずしも効率のよい検索方法ではないと理解していますが、カラーヒストグラムの比較を効率よく行う方法が思いついていないので、現状はこの方法にしています:
2017092402



【所感】
カラーヒストグラムアルゴリズムを採用したこの互換 API の検索精度はオリジナルのものとは同じではない可能性が高いと思っています(色が重要、どちらかというとイラストよりも写真の類似性に向いている、など)。あまり期待通りでなかったとしてもオープンソース化されているので該当部分のアルゴリズムを変更する、という方法もあると思っています(ちなみに MIT ライセンスで公開してます)。

あと、IBM Watson の互換 API を作ったのは今回が2回目ですが、このように「はじめから仕様が決まっている(仕様が変わらないことも決まっている) API を作る」のは楽です。ある程度、仕様として完成されているが故の安心感もありますし、何度も使っていた API なので動作確認も楽でした。実際、今回の互換 API は休暇を利用して(学習部分も含めて)ほぼ3日で作り上げました(「3日で作れる程度のアルゴリズム実装」と言えなくもないと思います)。


 

最近メインの業務が変わって、ずっと勉強モードだったこともあってブログの更新が滞ってました。やっと心に余裕がでてきたので、しばらくシリーズで(比較的簡単な)エントリを続けてみようと思います。

というわけで、今回紹介するのは「Node-RED で REST API を作る」というものです。Node-RED をある程度使っている人ならば「何それ簡単じゃん」と思う程度のことですが、今後引き続いて紹介しようと思ってる内容の導入部分として読んでいただければと。

まず最近話題(になってると感じる)の Node-RED はビジュアルフローエディタです:
2017090801


「ノード」と呼ばれる組み合わせ可能なブロックを順番に配置/接続して、データのフローを定義し、実行します。一通りの作業をブラウザ上の GUI で行うことができる手軽さとわかりやすさが素晴らしいと思っています。Node-RED はラズベリーパイの標準 OS である Raspbian OS にも標準搭載されていますが、IBM Bluemix 環境を使ってすぐにクラウド上の環境を用意することもできます(以下は Bluemix 環境前提で紹介します):
2017090802


Node-RED は基本的に GUI でデータのフローを定義するものであって、ここで作ったものの動きをそのまま視覚化できるわけではありません。ただノードの中にはデータを受け取って HTML ベースのダッシュボードを作るようなものがあったり、HTTP のリクエスト/レスポンスのノードも用意されているので、データを HTML で出力する(そしてそれをブラウザで表示する)といった応用もすぐにできます。今回はこの HTTP リクエスト/レスポンスノードを使って REST API を作ってみます。

Node-RED 画面左のキャンバス部分から、ノードパレットから HTTP リクエストfunctionHTTP レスポンスノードを1つずつドラッグ&ドロップでキャンバスに配置します。この時点でこのような画面になります:
2017090803


これら3つのノードの左右の端をドラッグ&ドロップして以下のように接続します。Node-RED ではデータは左から右に向かって流れていきます。つまりこの場合だと「HTTP リクエスト → function → HTTP レスポンス」という順にデータが送られていくようなフローができました:
2017090804


配置した3つのノードそれぞれの属性を(ダブルクリックして)変更してみましょう。まずは HTTP リクエストノードです。ここは「どのような HTTP リクエストに対して反応するか」を属性で指定します。今回は GET /getDate という日付時刻を返す REST API を作ってみましょう。メソッドは GET 、そして URL には /getDate を指定します。これでこのサーバー上の /getDate に対して GET リクエストが送られてきた場合にこのノードから始まる一連のフローの実行が開始するようになります。編集が終わったら右上の「完了」ボタンをクリックします:
2017090805



なお GET リクエストの場合、URL パラメータで指定された値は msg.req.query 内に格納されます。例えば /getDate?a=x&b=y というリクエストを処理した場合には msg.req.query.a = x, msg.req.query.b = y という値が格納された状態で次のノードが実行されます(といったような情報が画面右の「情報」タブに表示されます。キャンバスで選択中のノードに関する説明はここから参照できます):
2017090801


その次のノードである function ノードは JavaScript を記述することができます。ある意味でビジュアルフローっぽくないというか、一般的なプログラミングに近い処理を行うノードです。今回はこのノードを使って API として返す値(今回の例であれば日付時刻)を生成します。以下のような内容に書き換えます(緑文字部分は処理に関係のないコメントなので不要です):
var dt = new Date();  //. 現在時刻
if( msg.req.query.timestamp ){
  //. timestamp パラメータが指定されていた場合は、そのタイムスタンプの時刻に設定する
  dt.setTime( msg.req.query.timestamp );
}
msg.payload = dt;   //. ペイロードに時刻文字列を設定
return msg;

2017090806




今回作成する API は時刻の文字列を返すようにしました。特にパラメータ指定なしで実行した場合は現在時刻を、timestamp というパラメータが指定された場合はその値をタイムスタンプ値(1970年1月1日午前0時からの経過ミリ秒)と見なして、その日時の時刻を返すようにしています。


そして最後に HTTP レスポンスノードの属性を設定します。普通に動かすだけであれば何も設定しなくても(このままでも)動きます。が、今回は後でこの API を外部の JavaScript からも呼び出せるような設定を加えます(外部からの呼び出しを許可するため、HTTP レスポンスヘッダにクロスオリジンを許可する項目を加えます。詳しくはこちらを参照)。HTTP レスポンスノードのヘッダに Access-Control-Allow-Origin という項目を追加し、その値を * に指定します:
2017090807


これでこの HTTP リクエストは、外部のサーバーから AJAX などで呼び出しても利用できるようになりました。


こうして Node-RED 上に作成したフローを画面右上のボタンから「デプロイ」します:
2017090808


デプロイが成功することを確認します:
2017090802


デプロイが成功したら、ウェブブラウザで http(s)://(Node-RED の動いているサーバー)/getDate にアクセスしてみます。画面に現在時刻が表示されれば成功です(実際には Node-RED が稼働している環境の言語設定などに依存するので日本時間で表示されたり、外国の時間で表示されたりしますが、同じユニバーサル時刻が表示されるはずです。下図では GMT 時間で表示されています):
2017090803


今度は timestamp パラメータを指定して、 http(s)://(Node-RED の動いているサーバー)/getDate?timestamp=1 にアクセスしてみます。これは 1970/01/01 00:00:00 から1ミリ秒経過したタイミングを指定しているので、このような表示になるはずです:
2017090804


と、まずは単純な GET アクセスだけの API を1つだけ作ってみました。Node-RED を使うと最小で3つのノードを組み合わせるだけで簡単に作れることが分かりました。同様にして POST メソッドに対応させたりすることもできますので、興味ある方はチャレンジしてみてください。


なお、Node-RED に関しては最近たて続けに日本語書籍が発表されています。Node-RED そのものに関する情報はこれらの書籍も参考にしてください(↓アマゾンへのリンクです):




 

IBM Bluemix からも提供されている IBM の DBaaS サービスである dashDB に Node.js からアクセスする方法を紹介します。実際には dashDB だけでなく、DB2 のサービスやオンプレミスデータベースへも同様に応用できますが、今回は Bluemix 上の DB2/dashDB 関連サービスを例に紹介します:
2017063002


dashDB は行指向/列指向型のテーブルをどちらも作成することができるリレーショナル・データベースのサービスですが、そのデータベースシステムとしての実体は IBM DB2 です。というわけで、このライブラリを使ってアクセスします:
https://www.npmjs.com/package/ibm_db

2017063001


まず以下のコマンドを実行して ibm_db をインストールします(このコマンドだけで DB2 ODBC Driver ごとインストールされます):
$ npm install ibm_db


そして以下のようなコードを用意して dashDB にアクセスします:

(settings.js)
exports.db_host = 'dashdb-entry-yp-XXXXXXXX.services.dal.bluemix.net';
exports.db_port = 50000;
exports.db_name = 'BLUDB';
exports.db_username = 'dashNNNN';
exports.db_password = 'PASSWORD';

(sample.js)
var ibm_db = require( 'ibm_db' );
var settings = require( './settings' );

var db_str = "DATABASE=" + settings.db_name
  + ";HOSTNAME=" + settings.db_host
  + ";UID=" + settings.db_username
  + ";PWD=" + settings.db_password
  + ";PORT=" + settings.db_port
  + ";PROTOCOL=TCPIP";
var sql = "select OBJECTID, NAME from SAMPLES.GEO_CUSTOMER limit 10";

ibm_db.open( db_str, function( err, conn ){
  if( err ) return console.log( err );

  conn.query( sql, function( err, data ){
    if( err ) console.log( err );
    else console.log( data );

    conn.close( function(){
      console.log( 'done.' );
    });
  });
});

settings.js の中身はユーザー名やパスワードといった dashDB に接続するためのサービス資格情報です。IBM Bluemix の画面から取得できる値を使って、実際の値で書き換えて使ってください:

2017063003


アプリケーションの実体は sample.js です。今回の例ではシンプルに接続して、サンプルデータとして GEO_CUSTOMER テーブルから OBJECTID と NAME の値を 10 件だけ取得する、という SQL (青字部分)を実行しました。また settings.js で定義した情報を取り出して接続文字列(赤字部分)を生成しています。

node コマンドで sample.js を実行して、以下のような結果が表示されれば成功です:
$ node sample.js
[ { OBJECTID: 1322, NAME: 'Kami Labarbera' },
  { OBJECTID: 1323, NAME: 'Johnathon Tunney' },
     :
  { OBJECTID: 1587, NAME: 'Althea Alcazar' } ]
done.







 

IBM Bluemix(Cloud Foundry) のプラットフォームが現在持っている制約の1つが「IPアドレスによるアクセス制限」に関するものです。残念ながら現時点ではベースとなっている Cloud Foundry にこの機能がなく、IBM Bluemix でも実装されていません。

というわけで、現状この機能を実現するにはプラットフォーム側ではなくアプリケーション側で用意する必要があります。Node.js アプリケーションでこれを実現する方法の1つとして、Express-IpFilter があります:
https://www.npmjs.com/package/express-ipfilter

2017060601



名前の通りの機能です。Node.js の Express フレームワークの中で IP アドレス制限(許可/拒絶)を簡単に実現することができます。

Express-IpFIlter をインストールするには npm で以下を実行します:
$ npm install express-ipfilter
(実際には express のインストールも必要です)

例えば、以下のような Node.js + Express のシンプルなアプリケーションを例に IP アドレス制御をかける例を紹介します。まずアプリケーション(app.js)は以下のような内容のものを使います:
//. app.js

var express = require( 'express' );
var app = express();

app.use( express.static( __dirname + '/public/' ) );

app.listen( 3000 );

アプリケーションの中でスタティックディレクトリを /public/ に指定しています。そこで /public/index.html というファイルを用意し、中身を以下のようなものにします:
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>はろーわーるど</h1>
</body>
</html>

これを普通に Node.js で実行します:
$ node app

3000 番ポートを listen するように指定しているので、このポートを指定して同一マシンからウェブブラウザでアクセスすると、スタティックディレクトリに用意した index.html を見ることができます:
2017060602


ではこのアプリにアクセス制御をかけてみます。まずは '127.0.0.1' からのアクセスを拒絶するようなフィルターをかけてみます。app.js の内容を以下のように変更します(赤字部分を追加):
//. app.js

var express = require( 'express' );
var ipfilter = require( 'express-ipfilter' ).IpFilter;
var app = express();

var ips = [ '127.0.0.1' ];
app.use( ipfilter( ips ) );

app.use( express.static( __dirname + '/public/' ) );

app.listen( 3000 );

この例では ips という配列変数を用意し、その中に対象とする IP アドレスを文字列配列の形式で代入します。そして ipfilter を有効にしています。

この状態で app.js を起動し、先程と同じように同一マシンからウェブブラウザでアクセスすると以下のようになります:
2017060603


IP アドレス制御が有効になり、アクセスは拒否されました。

では今後は逆に '127.0.0.1' からのアクセスのみ許可するようなフィルタをかけてみます。app.js の内容を以下のように変更します(青字部分を追加):

//. app.js

var express = require( 'express' );
var ipfilter = require( 'express-ipfilter' ).IpFilter;
var app = express();

var ips = [ '127.0.0.1' ];
app.use( ipfilter( ips, { mode: 'allow' } ) );

app.use( express.static( __dirname + '/public/' ) );

app.listen( 3000 );

この状態で再度アプリケーションを起動し、同様にウェブブラウザでアクセスすると、今度は元のように表示されます(つまり IP Filter はデフォルトだと拒絶、'allow' モードを指定すると許可のフィルタをそれぞれ有効にします):
2017060604


これでアプリケーションレベルでの IP アドレス制限が実現できます。

話題(?)のブロックチェーンを小型コンピュータであるラズベリーパイ(以下、「ラズパイ」)から操作する、ということに挑戦してみました。


まずは操作の対象となるブロックチェーン環境を用意します。今回は IBM のクラウドプラットフォームである IBM Bluemix から提供されているマネージドハイパーレジャー環境である IBM Blockchain サービスを使うことにします(2017/Jun/04 現在では HyperLedger Fabric v0.6 と v1.0 がベータ提供されています)。IBM Bluemix アカウントが必要になりますが、30日間無料試用も用意されているので、必要に応じてアカウントを取得してください。

IBM Bluemix にログイン後、米国南部データセンターになっていることを確認し、「サービスの作成」をクリックします:
2017060404


そして「アプリケーション・サービス」カテゴリ内にある "Blockchain" を探してクリック:
2017060405


利用する Blockchain の種類をプランから選択します。今回は無料の "Starter Developer plan(beta)" を選択します。ちなみにこのプランの場合、Hyperledger Fabric は v0.6 を使うことになり、4つのブロックチェーンピアが作成されます。最後に「作成」ボタンをクリック:
2017060406


しばらく待つと IBM Blockchain サービスが起動します。ここでダッシュボードに移動することもできますが、この後で利用する情報を先に取得しておきます。左ペインの「サービス資格情報」を選択:
2017060407


サービス資格情報から「資格情報の表示」をクリックして、表示される JSON テキストを全てコピーしておきます(この後、ラズパイ内で使います):
2017060408


改めて「管理」タブを選択し、「ダッシュボードを起動」ボタンをクリックして、ダッシュボード画面に切り替えておきましょう:
2017060409


IBM Blockchain のダッシュボード画面が表示されます。4つのピアが動いていますが、この時点ではまだチェーンコードは作られていないことが確認できます。これでブロックチェーン側の準備は完了です:
2017060401


では続けてラズパイ側の準備に取り掛かります。以下はラズパイ上での作業です(僕の検証では3Bを使いました)。ラズパイにはネットワークのセットアップが済んだ Raspbian Jessie を用意し、そこに Hyperledger Fabric Node SDK ごとサンプルコードを導入してデモアプリケーションを動かします。というわけでまずは Node.js と npm を用意します。まずはこのドキュメント内にあるように、Node.js v6.2.x ~ v6.10.x のバージョンの環境を用意します(以下は v6.10.3 の例です。v7.x は未対応):
$ sudo apt-get install -y nodejs npm
$ sudo npm cache clean
$ sudo npm install n -g
$ sudo n v6.10.3
$ node -v
v6.10.3  <- v6.10.3 の導入が完了

続けて npm を最新版(以下の例では v5.0.2)に更新します:
$ sudo npm update -g npm
$ sudo npm outdated -g
$ npm -v
v5.0.2  <- v5.0.2 の導入が完了

git でデモアプリをダウンロードし、必要なライブラリをインストールします:
$ git clone https://github.com/IBM-Blockchain/SDK-Demo.git
$ cd SDK-Demo
$ npm install

ダウンロードしたデモアプリの中(直下のディレクトリ)にある ServiceCredentials.json ファイルの中身を、上記でコピーしたサービス資格情報の JSON テキストの中身で全て書き換えます:
{
  "peers": [
    {
      "discovery_host": ******

      :
      :
"cert_path": "/certs/peer/cert.pem" }

これでラズパイ側の準備も完了しました。後は以下のコマンドを実行して、デモアプリを実行します:
$ node helloblockchain.js

以下のようにコマンドが次々を流れていきます。実際にはこの中でチェーンコードの初期化や、初期データ入力、データ変更などが行われています。最後に "Successfully ****" というメッセージがいくつか表示されていればコマンドは成功です:

2017060403


この最後のメッセージについて補足します。この helloblockchain.js ではチェーンコード初期化などの作業を行った後に「"a" という入れ物に 100 、"b" という入れ物に 200 のデータを保存」します(ここまでが初期化)。そして「"a" から "b" へ 10 移動」するようなトランザクションが実行されます(なのでここまでの処理が完了すると "a" に 90 、"b" に 210 入っている状態になるはずです)。最後に「"a" の値を取り出す」処理が実行されます。その辺りの様子が以下のメッセージから分かります("a" が 90 になっています):
  :
  :
Chaincode ID : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Successfully deployed chaincode: request={"fcn":"init","args":["a","100","b","200"],"chaincodePath":"chaincode","certificatePath":"/certs/peer/cert.pem"}, response={"uuid":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}

Successfully submitted chaincode invoke transaction: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"invoke","args":["a","b","10"]}, response={"uuid":"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"}

Successfully completed chaincode invoke transaction: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"invoke","args":["a","b","10"]}, response={"result":"Tx zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz complete"}
Custom event received, payload: "Event Counter is 1"


Successfully queried  chaincode function: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"query","args":["a"]}, value=90


※↑数値の移動は Submit して Complete しているのが別行で表示されているので、全部で4行表示されています。


なお初期化部分は最初の1回しか実行されないため、この helloblockchain.js を何度か実行すると、"a" の値が 10 ずつ減っていく様子がわかります:
$ node helloblockchain.js

  :
  :
Successfully queried  chaincode function: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"query","args":["a"]}, value=80

$ node helloblockchain.js

  :
  :
Successfully queried  chaincode function: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"query","args":["a"]}, value=70


また、これらの処理を実行すると、ダッシュボード上からもチェーンコードが生成されて、実行されている様子が確認できます(初期状態だと画面更新が3分ごとなので、画面に反映されるまで少し時間がかかるかもしれません):
2017060402


まだ環境の一部がベータ版だったりする状態ではありますが、今やラズパイでもブロックチェーンが動くようになったんですねー。

このページのトップヘ