Amazon Managed BlockchainのブロックチェーンネットワークはVPC内に構築されるため、VPC外からブロックチェーンネットワークへアクセスするにはクライアントとなるアプリなりサービスを開発して経由する必要があります。
AWS LambdaでVPC内に関数を配置するとアクセス可能になるはずなので試してみました。

Amazon VPC 内のリソースにアクセスできるように Lambda 関数を構成する – AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/vpc.html

前提

Dockerを利用して開発環境を構築します。

AWS Lambdaへのデプロイにはserverlessを利用します。

Serverless – The Serverless Application Framework powered by AWS Lambda, API Gateway, and more
https://serverless.com/

AWS Lambdaを利用するのでAWSアカウントや権限も必要となります。

> docker --version
Docker version 18.09.2, build 6247962

> docker-compose --version
docker-compose version 1.23.2, build 1110ad01

> sls --version
1.43.0

Amazon Managed Blockchainでブロックチェーンネットワークが構築済み

下記の2記事の手順でブロックチェーンネットワークが構築済みでfabcarのサンプルが動作する環境がある前提です。

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた – Qiita
https://cloudpack.media/46963

Amazon Managed Blockchainで作成したブロックチェーンネットワークにHyperledger Fabric SDK for Node.jsでアクセスしてみる – Qiita
https://cloudpack.media/47382

開発環境を構築する

Hyperledger FabricのSDKをAWS Lambda上で利用するにはLinuxでnpm installする必要があったのでDockerを利用して開発環境を構築します。

Dockerコンテナの立ち上げ

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> touch Dockerfile
> touch docker-compose.yml

AWS Lambdaで利用できるNode.jsのバージョンは8.1010.xとなります。
Hyperledger Fabric SDK for Node.jsは8.x系で動作するのでDockerにも8.xをインストールします。

AWS Lambda ランタイム – AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html

Dockerfile

FROM amazonlinux

RUN yum update && \
    curl -sL https://rpm.nodesource.com/setup_8.x | bash - && \
    yum install -y gcc-c++ make nodejs && \
    npm i -g serverless

docker-compose.yml

version: '3'
services:
  app:
    build: .
    volumes:
      - ./:/src
    working_dir: /src
    tty: true
> docker-compose build

> docker-compose run app bash

Node.jsのプロジェクト作成

コンテナが立ち上がったらserverlessでNode.jsのテンプレートでプロジェクトを作成します。

コンテナ内

$ sls create \
  --template aws-nodejs \
  --path fablic-app

$ cd fablic-app
$ npm init

Hyperledger Fabric SDK for Node.jsが利用できるようにpackage.jsonを編集してnpm installを実行します。

package.json

{
    "name": "fabcar",
    "version": "1.0.0",
    "description": "Hyperledger Fabric Car Sample Application",
    "main": "fabcar.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "dependencies": {
        "fabric-ca-client": "~1.2.0",
        "fabric-client": "~1.2.0",
        "fs-extra": "^8.0.1",
        "grpc": "^1.6.0"
    },
    "author": "",
    "license": "Apache-2.0",
    "keywords": [
    ]
}

コンテナ内

$ npm install

証明書の用意

Hyperledger Fabric SDK for Node.jsでブロックチェーンネットワークへアクセスするのに各種証明書が必要となるためプロジェクトに含めます。こちらはDockerコンテナ外で行います。

コンテナ外

> cd fablic-app
> aws s3 cp s3://us-east-1.managedblockchain-preview/etc/managedblockchain-tls-chain.pem ./managedblockchain-tls-chain.pem

# EC2インスタンスからhfc-key-storeフォルダを取得
> scp -r -i [EC2インスタンス用のpemファイルパス] ec2-user@xxx.xxx.xxx.xxx:/home/ec2-user/fabric-samples/fabcar/hfc-key-store ./hfc-key-store

実装

今回はブロックチェーンネットワークのステートDBから情報を取得する実装を行います。
下記記事でも利用しているquery.jsをAWS Lambdaで実行できるように編集しました。

Amazon Managed Blockchainで作成したブロックチェーンネットワークにHyperledger Fabric SDK for Node.jsでアクセスしてみる – Qiita
https://cloudpack.media/47382

handler.jsquery.jsのメソッドを呼び出し結果を返す実装にしました。

> cd fabric-app
> tree -F -L 1 .
.
├── handler.js
├── hfc-key-store/
├── managedblockchain-tls-chain.pem
├── node_modules/
├── package-lock.json
├── package.json
├── query.js
└── serverless.yml

handler.js

var query = require('./query');

module.exports.hello = async (event) => {
  var result = await query.run();
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: JSON.parse(result),
      input: event,
    }, null, 2),
  };
};

実装のポイントは下記となります。

  • 証明書フォルダhfc-key-store/tmp にコピーして利用
  • module.exports.run = async () => {}handler.jsから呼び出し可能にする
  • async/await で同期的に実行する

query.js

'use strict';
/*
* Copyright IBM Corp All Rights Reserved
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
 * Chaincode query
 */

var Fabric_Client = require('fabric-client');
var path = require('path');
var util = require('util');
var os = require('os');
var fs = require('fs-extra');

//
var fabric_client = new Fabric_Client();

// setup the fabric network
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpcs://nd-xxxxxxxxxxxxxxxxxxxxxxxxxx.m-xxxxxxxxxxxxxxxxxxxxxxxxxx.n-xxxxxxxxxxxxxxxxxxxxxxxxxx.managedblockchain.us-east-1.amazonaws.com:30003',
    { pem: fs.readFileSync('./managedblockchain-tls-chain.pem').toString(), 'ssl-target-name-override': null});
channel.addPeer(peer);

//
var member_user = null;
var store_base_path = path.join(__dirname, 'hfc-key-store');
var store_path = path.join('/tmp', 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;

// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
module.exports.run = async () => {
    // 証明書ファイルを/tmp ディレクトリにコピーして利用する
    fs.copySync(store_base_path, store_path);
    console.log('Store copied!');

    return await Fabric_Client.newDefaultKeyValueStore({ path: store_path
    }).then((state_store) => {
        // assign the store to the fabric client
        fabric_client.setStateStore(state_store);
        var crypto_suite = Fabric_Client.newCryptoSuite();
        // use the same location for the state store (where the users' certificate are kept)
        // and the crypto store (where the users' keys are kept)
        var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
        crypto_suite.setCryptoKeyStore(crypto_store);
        fabric_client.setCryptoSuite(crypto_suite);

        // get the enrolled user from persistence, this user will sign all requests
        return fabric_client.getUserContext('user1', true);
    }).then((user_from_store) => {
        if (user_from_store && user_from_store.isEnrolled()) {
            console.log('Successfully loaded user1 from persistence');
            member_user = user_from_store;
        } else {
            throw new Error('Failed to get user1.... run registerUser.js');
        }

        // queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
        // queryAllCars chaincode function - requires no arguments , ex: args: [''],
        const request = {
            //targets : --- letting this default to the peers assigned to the channel
            chaincodeId: 'fabcar',
            fcn: 'queryAllCars',
            args: ['']
        };

        // send the query proposal to the peer
        return channel.queryByChaincode(request);
    }).then((query_responses) => {
        console.log("Query has completed, checking results");
        // query_responses could have more than one  results if there multiple peers were used as targets
        if (query_responses && query_responses.length == 1) {
            if (query_responses[0] instanceof Error) {
                console.error("error from query = ", query_responses[0]);
            } else {
                console.log("Response is ", query_responses[0].toString());
                return query_responses[0].toString();
            }
        } else {
            console.log("No payloads were returned from query");
        }
    }).catch((err) => {
        console.error('Failed to query successfully :: ' + err);
    });
};

serverlessの設定

AWS LambdaでVPC内配置されるようにserverless.yml を編集します。
vpciamRoleStatements の定義については下記が参考になりました。
セキュリティグループとサブネットはAmazon Managed Blockchainで構築したブロックチェーンネットワークと同じものを指定します。

ServerlessでLambdaをVPC内にデプロイする – Qiita
https://qiita.com/70_10/items/ae22a7a9bca62c273495

serverless.yml

service: fabric-app

provider:
  name: aws
  runtime: nodejs8.10

  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ec2:CreateNetworkInterface"
        - "ec2:DescribeNetworkInterfaces"
        - "ec2:DeleteNetworkInterface"
      Resource:
        - "*"

  vpc:
    securityGroupIds:
      - sg-xxxxxxxxxxxxxxxxx
    subnetIds:
      - subnet-xxxxxxxx
      - subnet-yyyyyyyy

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get

AWS Lambdaにデプロイする

> sls deploy

Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service fabric-app.zip file to S3 (40.12 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............
Serverless: Stack update finished...
Service Information
service: fabric-app
stage: dev
region: us-east-1
stack: fabric-app-dev
resources: 10
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
  hello: fabric-app-dev-hello
layers:
  None
Serverless: Removing old service artifacts from S3...

デプロイができたらエンドポイントにアクセスしてみます。

> curl https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello

{
  "message": [
    {
      "Key": "CAR0",
      "Record": {
        "make": "Toyota",
        "model": "Prius",
        "colour": "blue",
        "owner": "Tomoko"
      }
    },
    {
      "Key": "CAR1",
      "Record": {
        "make": "Ford",
        "model": "Mustang",
        "colour": "red",
        "owner": "Brad"
      }
    },
    (略)
  ],
  "input": {
    "resource": "/hello",
    "path": "/hello",
    "httpMethod": "GET",
    (略)
  }
}

はい。
無事にAWS Lambda関数からHyperledger Fabric SDK for Node.jsを利用してブロックチェーンネットワークにアクセスすることができました。

VPC内にLamnbda関数を配置する必要があるため、ENI(仮想ネットワークインターフェース)の利用に伴う制限や起動速度に課題が発生するかもしれませんので、実際に利用する際には負荷検証などしっかりと行う必要がありそうです。

AWS LambdaをVPC内に配置する際の注意点 | そるでぶろぐ
https://devlog.arksystems.co.jp/2018/04/04/4807/

参考

Amazon VPC 内のリソースにアクセスできるように Lambda 関数を構成する – AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/vpc.html

Serverless – The Serverless Application Framework powered by AWS Lambda, API Gateway, and more
https://serverless.com/

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた – Qiita
https://cloudpack.media/46963

Amazon Managed Blockchainで作成したブロックチェーンネットワークにHyperledger Fabric SDK for Node.jsでアクセスしてみる – Qiita
https://cloudpack.media/47382

AWS Lambda ランタイム – AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html

ServerlessでLambdaをVPC内にデプロイする – Qiita
https://qiita.com/70_10/items/ae22a7a9bca62c273495

AWS LambdaをVPC内に配置する際の注意点 | そるでぶろぐ
https://devlog.arksystems.co.jp/2018/04/04/4807/

元記事はこちら

AWS LambdaとHyperledger Fabric SDK for Node.jsを利用してAmazon Managed Blockchainのブロックチェーンネットワークにアクセスする