[アップデート] Ethereum on Amazon Managed Blockchain が GA されました

たてるだけなら EC2 インスタンスより簡単。
2021.03.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

先日のアップデートで Ethereum on Amazon Managed Blockchain が GA になりました。

昨年 12 月にプレビューリリースされていた Ethereum on Amazon Managed Blockchain がいよいよ一般提供可能になりました!

Ethereum on Amazon Managed Blockchain

Ethereum スマートコントラクト

Ethereum は分散型アプリケーション(Dapp)などのスマートコントラクトによるアプリケーション構築が可能なオープンソースプロジェクトです。

Ethereum は暗号資産($ETH)としての認知度が高いと思いますが、ゲーム、不動産取引、身分証明(DID)などのサービスを Ethereum プラットフォーム上で提供することも可能です。

記憶にあたらしいところでは、昨年、暗号資産人気を後押しした分散型金融(DeFi)、分散型取引所(DEX)、ノン・ファンジブル・トークン(NFT)などはまさに Ethereum スマートコントラクトの代表格として皆さんもご存知のことかと思います。

フルマネージドな Ethereum ノード

通常、Ethereum フルノードを抱える場合、日々増加するストレージや、ネットワーク性能、ソフトウェア(Geth)のアップデートなどの運用が必要になりますが、Ethereum on Amazon Managed Blockchain ではフルマネージドサービスとして AWS に任せることができます。

利用可能な Ethereum ネットワーク

選択可能なネットワークは以下の 3 つ。

  • メインネットワーク: 本番用の PoW ネットワーク。トランザクションには実際の Gas が必要になります。(最近、高騰していますね。。)
  • Rinkeby テストネットワーク: テスト用の PoA ネットワーク。実際の Gas は必要ありません。
  • Ropsten テストネットワーク: テスト用の PoW ネットワーク。実際の Gas は必要ありません。

上記ネットワークはいずれもパブリックネットワークです。プライベートネットワークとしての Ethereum にはまだ対応していないようです。(ネットワーク作成のメニューから見ると Coming Soon のままでした)

料金

利用可能なインスタンスタイプと料金(東京リージョン)は下記のとおりです。m5.large/t3.large だと最小要件(2core/4GB RAM) を満たしますが、Ethereum の推奨要件(4core/+16GB RAM)だと m5.xlarge/t3.xlarge あたりになります。

インスタンスタイプ 時間あたり料金
bc.c5.2xlarge $0.685
bc.c5.4xlarge $1.37
bc.c5.xlarge $0.342
bc.m5.2xlarge $0.794
bc.m5.4xlarge $1.587
bc.m5.large $0.198
bc.m5.xlarge $0.397
bc.t3.large $0.174
bc.t3.xlarge $0.348

その他に以下の料金が発生します。

  • Storage Rate $0.12 per GB-month
  • Ethereum API リクエスト $0.00000488 per Request ($4.88 per million requests)
    • 1 リクエストは交換されたデータサイズ 32 KB または 応答時間 500 ms のいずれかでカウントされます

利用可能なリージョン

執筆時点で利用可能なリージョンは以下のとおりです。

  • 米国東部(バージニア北部)
  • アジアパシフィック(ソウル)
  • アジアパシフィック(東京)
  • アジアパシフィック(シンガポール)
  • 欧州(アイルランド)
  • 欧州(ロンドン)

利用上の考慮点

ノードタイプ

執筆時点において Ethereum on Amazon Managed Blockchain で利用可能なノードは Geth フルノードのみです。以下のとおりアーカイブノードはグレーアウトになっています。

Ethereum JSON-RPC

Ethereum クライアントがノードに対してクエリやトランザクションを送信するには、JSON-RPC API をインタフェースとして利用しますが、Ethereum on Amazon Managed Blockchain のマネージドノードでは一部の Ethereum JSON-RPC メソッドはサポートされていないようです。

ざっくり調べですが、対応していないメソッドは以下のとおりです。

  • eth_coinbase
  • eth_mining
  • eth_hashrate
  • eth_accounts
  • eth_sign
  • eth_signTransaction
  • eth_sendTransaction
  • eth_getCompilers
  • eth_compileLLL
  • eth_compileSolidity
  • eth_compileSerpent
  • eth_submitWork
  • eth_submitHashrate
  • db_putString
  • db_getString
  • db_putHex
  • db_getHex
  • shh_post
  • shh_version
  • shh_newIdentity
  • shh_hasIdentity
  • shh_newGroup
  • shh_addToGroup
  • shh_newFilter
  • shh_uninstallFilter
  • shh_getFilterChanges
  • shh_getMessages

eth_mining が対応していないのでマイニング用途には利用できなさそうですね。

また以下ドキュメントに記載のとおり eth_sendRawTransaction のみがサポートされていますので、トランザクションをノードに送信する前に署名する必要があります。

トランザクションの署名を必要とする eth_sendTransactionマネージドノードではサポートされていません。

Ethereum on Managed Blockchain only supports the eth_sendRawTransaction method, which requires that you create and sign the transaction before sending it to the node. Managed Blockchain does not have any way to sign transactions similar to an Ethereum wallet application.

eth_accounts がサポートされておらずアカウント管理は出来ないので、当然 Geth クライアント(ノード)での署名を管理することも出来ない、ということでしょう。

利用可能な JSON-RPC メソッド一覧はドキュメントを参照ください。

やってみる

それでは簡単に検証してみましょう。今回の検証は以下のとおりです。

  • 東京リージョン
    • bc.t3.large
  • 検証内容
    • ノードの作成
    • Ethereum JSON-RPC API コール

ノードの作成

Amazon Managed Blockchain 管理コンソールをから [ネットワークに参加] を開きます。参加するブロックチェーンネットワーク、インスタンスタイプ、ノードタイプなどを選択し [ノードの作成] をクリックします。

今回は Ropsten テストネットワークに bc.t3.large インスタンスのフルノードを作成します。検証ですので単一 AZ として 1 ノードのみ作成しています。

作成メニューのインフォメーションに記載のとおり、ノード作成してから利用可能になるまでは 30 分程度でした。

ノードの詳細を確認すると、WebSocket エンドポイント、HTTP エンドポイントなどが確認できます。これらは後ほど JSON-RPC API をコールする際に必要となりますので控えておきます。

ちなみに Ropsten テストネットワークに作成した直後のストレージ容量は 111 GB くらいでした。

Ethereum JSON-RPC API コール

Ethereum JSON-RPC API を呼び出すために、いくつかの方法があります。

今回は web3.js を使用して HTTP エンドポイントに API コールしてみます。

環境準備

まずノードバージョンマネージャー(nvm)と Node.js バージョン 14 以降をインストールします。EC2 や Cloud9 環境を利用の場合、以下のチュートリアルを参考にすると良いです。

$ node --version
v14.16.0

バージョン 14 より前のものが表示される場合は、nvm install 14nvm use 14 を実行することでバージョン 14 が利用可能になります。

次に、公式ドキュメントのサンプルスクリプトを実行するための前提条件となる以下のパッケージをインストールします。

$ npm install aws-sdk
$ npm install web3
$ npm install xhr2

サンプルスクリプトでは ECMAScript(ES) モジュールを使用しますので、 package.json に以下の内容を追加します。

{
  "type": "module",
  "dependencies": {
    "aws-sdk": "^2.809.0",
    "web3": "^1.3.0",
    "xhr2": "^0.2.0"
  }
}

IAM ポリシー

Ethereum on Amazon Managed Blockchain のノードに対して Ethereum JSON-RPC を API コールするためには署名バージョン 4 の署名プロセスを利用します。

今回は検証用に作成した IAM ユーザーに AWS マネージドポリシーの AmazonManagedBlockchainConsoleFullAccess をアタッチしました。ポリシーの内容は以下のとおりです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "managedblockchain:*",
                "ec2:DescribeAvailabilityZones",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcs",
                "ec2:CreateVpcEndpoint",
                "kms:ListAliases",
                "kms:DescribeKey"
            ],
            "Resource": "*"
        }
    ]
}

環境変数に AWS アクセスキー/シークレットキー、エンドポイント情報などを設定します。各種エンドポイントは作成したノード情報を参照することで確認可能です。

$ export AWS_ACCESS_KEY_ID="AKXXXXXXXXXXXXXXXXXX"
$ export AWS_SECRET_ACCESS_KEY="ZoXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+"
$ export AMB_HTTP_ENDPOINT="https://nd-XXXXXXXXXXXXXXXXXXXXXXXXXX.ethereum.managedblockchain.ap-northeast-1.amazonaws.com"
$ export AMB_WS_ENDPOINT="wss://nd-XXXXXXXXXXXXXXXXXXXXXXXXXX.wss.ethereum.managedblockchain.ap-northeast-1.amazonaws.com"

サンプルスクリプトを実行する

公式ドキュメントを参考にサンプルスクリプトを準備します。aws-http-provider.js は以下のとおりです。

aws-http-provider.js

/////////////////////////////////////////////////////
// Authored by Carl Youngblood
// Senior Blockchain Solutions Architect, AWS
// Adapted from web3 npm package v1.3.0
// licensed under GNU Lesser General Public License
// https://github.com/ethereum/web3.js
/////////////////////////////////////////////////////

import AWS from 'aws-sdk';
import HttpProvider from 'web3-providers-http';
import XHR2 from 'xhr2';

export default class AWSHttpProvider extends HttpProvider {
  send(payload, callback) {
    const self = this;
    const request = new XHR2(); // eslint-disable-line

    request.timeout = self.timeout;
    request.open('POST', self.host, true);
    request.setRequestHeader('Content-Type', 'application/json');

    request.onreadystatechange = () => {
      if (request.readyState === 4 && request.timeout !== 1) {
        let result = request.responseText; // eslint-disable-line
        let error = null; // eslint-disable-line

        try {
          result = JSON.parse(result);
        } catch (jsonError) {
          let message;
          if (!!result && !!result.error && !!result.error.message) {
            message = `[aws-ethjs-provider-http] ${result.error.message}`;
          } else  {
            message = `[aws-ethjs-provider-http] Invalid JSON RPC response from host provider ${self.host}: ` +
              `${JSON.stringify(result, null, 2)}`;
          }
          error = new Error(message);
        }

        callback(error, result);
      }
    };

    request.ontimeout = () => {
      callback(`[aws-ethjs-provider-http] CONNECTION TIMEOUT: http request timeout after ${self.timeout} ` +
        `ms. (i.e. your connect has timed out for whatever reason, check your provider).`, null);
    };

    try {
      const strPayload = JSON.stringify(payload);
      const region = process.env.AWS_DEFAULT_REGION || 'us-east-1';
      const credentials = new AWS.EnvironmentCredentials('AWS');
      const endpoint = new AWS.Endpoint(self.host);
      const req = new AWS.HttpRequest(endpoint, region);
      req.method = request._method;
      req.body = strPayload;
      req.headers['host'] = request._url.host;
      const signer = new AWS.Signers.V4(req, 'managedblockchain');
      signer.addAuthorization(credentials, new Date());
      request.setRequestHeader('Authorization', req.headers['Authorization']);
      request.setRequestHeader('X-Amz-Date', req.headers['X-Amz-Date']);
      request.send(strPayload);
    } catch (error) {
      callback(`[aws-ethjs-provider-http] CONNECTION ERROR: Couldn't connect to node '${self.host}': ` +
        `${JSON.stringify(error, null, 2)}`, null);
    }
  }
}

aws-http-provider.js と同じディレクトリに Ethereum JSON-RPC API をコールする web3-example-http.js を配置します。今回のサンプルスクリプトでは getNodeInfo メソッドを実行しています。

web3-example-http.js

import Web3 from 'web3';
import AWSHttpProvider from './aws-http-provider.js';
const endpoint = process.env.AMB_HTTP_ENDPOINT
const web3 = new Web3(new AWSHttpProvider(endpoint));
web3.eth.getNodeInfo().then(console.log);

スクリプトを実行して以下のような結果が取得できれば正常に API コールできています。

$ node web3-example-http.js 
web3-shh package will be deprecated in version 1.3.5 and will no longer be supported.
web3-bzz package will be deprecated in version 1.3.5 and will no longer be supported.
Geth/v1.9.24-stable-cc05b050/linux-amd64/go1.15.5

以下のようなエラーが出る場合、環境変数にリージョン情報を設定することで解決できます。

UnhandledPromiseRejectionWarning: Error: Invalid JSON RPC response: {"message":"Credential should be scoped to a valid region, not 'us-east-1'. "}
export AWS_DEFAULT_REGION=ap-northeast-1

検証は以上です!

さいごに

Ethereum on Amazon Managed Blockchain が GA になりましたので試しにノードを作成してみましたが EC2 を起動すよりも簡単に Geth ノードを作成して Ethereum ネットワークに参加させることができました。

これを機にスマートコントラクト開発にチャレンジしてみるのも良いかもしれません!

ブロックチェーンといえば暗号資産(仮想通貨)の投機的なイメージばかりが先行して、なかなかアプリケーションプラットフォームとして広がりを感じることは少ないと思いますが、国外に目を向ければ Ethereum などのスマートコントラクトを利用したサービスがどんどん増えています。

今後、新たな 「Decentralized xx」(分散型 xx) という仕組みが次々に出てくることが予想されますが、国内からもそのようなユースケースが出てくることを期待したいですね!

以上!大阪オフィスの丸毛(@marumo1981)でした!