この記事はnem Advent Calendar 2018の12日目の記事です。
nemgo、AWS LambdaとAmazon API Gatewayを連携させてXEM残高を取得するAPIを作成してみました。
ただすでにnemのネットワークを構築しているノード(NIS)からは残高を取得するAPIが提供されていますので今回作成したAPIは機能的には全く意味がありません。
なので今回APIを作成した理由はGolangと、Lamdaを一度も扱ったことがないから入門するため。なだけです。
NISについての詳しくはこちら。
Contents
本記事の対象者
NEM/Golang/Lambdaをこれから学習したいけど「使ったことない」または「使ってみたい」と思っている初心者になります。
nemgoとは?
Go言語で作られたNem blockchainを操作するためのライブラリです。
AWS Lambdaとは?
AWSに関するイベントによって処理を実行する環境です。例えばAWS S3にファイルをアップした時、削除したタイミングでプログラムを実行する。APIサーバとして、クライアントから通信があったらプログラムを実行するなどです。
今回はAmazon API Gatewayと連携してAPIを作成しています。また現在対応している言語はNode.js(JavaScript)、Python、Java、C#、Goとなっています。

実装の手順
それでは早速実装の手順を見ていきましょう!
今回はAWSの操作は画面から手動で行うのではなくコマンドで操作するようにしています。
最終的に書いたコードはこちら。
また、自分で今回準備したAPIのURLは「https://szqp96efe8.execute-api.ap-northeast-1.amazonaws.com/Prod/account?address=XEMアドレス」です。
開発環境
- Mac OS Mjave v10.14.1
 - go v1.11.2
 - Docker v18.09.0
 - AWS CLI v1.16.60
 - SAM CLI v0.7.0
 
前提条件
- Goがインストールされている
 - Dockerがインストールされている
 - AWS CLIがインストールされている
 - SAM CLIがインストールされている
 
それぞれインストールの参考になりそうな記事はこちらです。
ライブラリのインストール
AWS Lambda GOをインストール
| 
					 1  | 
						go get github.com/aws/aws-lambda-go/lambda  | 
					
プロジェクトのひな形を作成
| 
					 1  | 
						$ sam init -r go1.x -n プロジェクト名  | 
					
ビルドしてAPI動作確認
ビルドする
| 
					 1 2 3  | 
						$ cd プロジェクト名 $ make build GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world  | 
					
Dockerを起動してからAPIのテスト用ローカルエンドポイントを準備する
| 
					 1  | 
						$ sam local start-api  | 
					
ブラウザから「http://127.0.0.1:3000/hello」にアクセスして画面に「Hello」の文言が表示されればOKです。 これでLambdaと連携させるための雛形ができました! 次からNEMを扱うための手順になります。
nemgoインストール
| 
					 1  | 
						$ go get github.com/myndshft/nemgo  | 
					
コード実装
ここらからやっとコードを書いていきます。main.goを以下のように変更。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83  | 
						package main import ( 	"encoding/json" 	"errors" 	"fmt" 	"io/ioutil" 	"log" 	"math" 	"net/http" 	"github.com/aws/aws-lambda-go/events" 	"github.com/aws/aws-lambda-go/lambda" 	"github.com/myndshft/nemgo" ) type Account struct { 	Address string  `json:"address"` 	Balance float32 `json:"balance"` } var ( 	// DefaultHTTPGetAddress Default Address 	DefaultHTTPGetAddress = "https://checkip.amazonaws.com" 	// ErrNoIP No IP found in response 	ErrNoIP = errors.New("No IP in HTTP response") 	// ErrNon200Response non 200 status code in response 	ErrNon200Response = errors.New("Non 200 Response found") ) func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { 	resp, err := http.Get(DefaultHTTPGetAddress) 	if err != nil { 		return events.APIGatewayProxyResponse{}, err 	} 	if resp.StatusCode != 200 { 		return events.APIGatewayProxyResponse{}, ErrNon200Response 	} 	ip, err := ioutil.ReadAll(resp.Body) 	if err != nil { 		return events.APIGatewayProxyResponse{}, err 	} 	if len(ip) == 0 { 		return events.APIGatewayProxyResponse{}, ErrNoIP 	} 	address := request.QueryStringParameters["address"] 	env := request.QueryStringParameters["env"] 	// envパラメータによって環境を変える 	host := "23.228.67.85:7890" 	network := nemgo.Testnet 	if env == "mainnet" { 		host = "209.126.98.204:7890" 		network = nemgo.Mainnet 	} 	// AccountData 	client := nemgo.New(nemgo.WithNIS(host, network)) 	// Obtain account data using address 	accountData, err := client.AccountData(nemgo.Address(address)) // アドレスからアカウント情報を取得 	if err != nil { 		log.Fatal(err) 	} 	balance := float32(accountData.Account.Balance) / float32(math.Pow(10, 6.0)) // 残高の単位を合わせる 	account := Account{Address: accountData.Account.Address, Balance: balance} 	bs, _ := json.Marshal(account) 	return events.APIGatewayProxyResponse{ 		Body:       fmt.Sprintf("%v", string(bs)), 		StatusCode: 200, 	}, nil } func main() { 	lambda.Start(handler) }  | 
					
アドレスからアカウント情報を取得してJSON形式でアドレスとXEMの残高の単位を合わせて返すだけのシンプルなものです。URLのパラメータとして「&env=mainnet」を追加すれば本番環境(Mainnet)のアドレスでも残高を取得できるようにしました。envパラメータをつけなければTestnetになります。
あとは、プロジェクト内のファイル(template.yml、Makefile)やフォルダでHelloWorldやhello-worldのような文字をAcountまたはaccountに全て置き換えます。
再度ビルドしてAPI動作確認
| 
					 1 2 3  | 
						$ make build GOOS=linux GOARCH=amd64 go build -o account/account ./account $ sam local start-api  | 
					
ブラウザから「http://127.0.0.1:3000/account?address=XEMアドレス」にアクセスして画面に残高が表示されればOK!
デプロイ
S3バケットを作成する。
| 
					 1 2  | 
						$ aws s3 mb s3://sam-deploy-[AWSアカウントID] --region ap-northeast-1 make_bucket: sam-deploy-[AWSアカウントID]   | 
					
デプロイ用にパッケージ化を行う。
| 
					 1 2 3 4 5  | 
						$ sam package --template-file template.yaml --output-template-file serverless-output.yaml --s3-bucket sam-deploy-[AWSアカウントID] Uploading to 3bd84975f6662ddd5c2c0e231e073509 4448116 / 4448116.0 (100.00%) Successfully packaged artifacts and wrote output template to file serverless-output.yaml.  xecute the following command to deploy the packaged template aws cloudformation deploy --template-file /path/to/serverless-output.yaml --stack-name <YOUR STACK NAME>  | 
					
デプロイ実行!
| 
					 1 2 3 4  | 
						$ sam deploy --template-file serverless-output.yaml --stack-name [プロジェクト名] --capabilities CAPABILITY_IAM --region ap-northeast-1 Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - [プロジェクト名]  | 
					
最後に動作確認をしていきます。
API動作確認
ブラウザでアクセスしてみましょう。
ブラウザから「https://[APIのURL].execute-api.ap-northeast-1.amazonaws.com/Prod/account?address=XEMアドレス」を入力してローカルで確認した時と同様にXEMの残高が表示されればOK!!
APIのURLが分からない場合はAPI Gatewayの管理画面から確認してください。
以上で終了です。
まとめ
SAM CLIを使うと、ローカルで簡単にLambda関数の開発ができるし、環境の構築がすぐできるのでラクでした。環境を作った後にAWSのコンソール画面からAPI GatewayやLambdaの設定を確認するとさらに学びがあるのではないかと思います。
またnemgoはnem2(Catapult)に対応していないのでアップデートにも期待したいと思います。



	        	        		
	        	        		
	        	        		
	        	        		
	        	        		
	        	        		
	        	        		
	        	        		

この記事を書いているタイミングでLambdaに関して新しいニュースが!Rubyでも処理を実行できるように。今のうちに利用方法を押さえておきたい。