Ethereum
web3.js

lambda上でweb3を使って送金する際にやっておくといいこと


はじめに

これは Ethereum Advent Calendar 2018 の4日目です。


自己紹介


  • 株式会社MidfreeのCTOの平田です


  • 「ETHBOARD」という、書き込むとEthereumを貰える匿名掲示板サービスを運営しています


    • このAdvent Calendarを見てる人はきっともれなく全員使ってくれるはず、、、w




この記事について

web3.jsを用いて実際に送金処理を書いて色々触った経験を元にやっておくといいことをTips的に書いておこうと思います。


環境

lambda runtime node8.10 (serverless frameworkを利用)

web3 @1.0.0-beta.35
ethereumjs-tx @1.3.7


送金部分のコード

sendEth: async (fromAddress, toAddress, privateKey, ethAmount, nonce) => {

// weiの単位に変換する
let amount = web3.utils.toWei(ethAmount, 'ether');
const parameter = { from: fromAddress, to: toAddress, value: amount };
let gasLimit, gasPrice
[gasLimit, gasPrice] = await Promise.all([web3.eth.estimateGas(parameter), web3.eth.getGasPrice()])
// 余裕を持って送金できるようにLimitに余裕を持たせる
parameter.gasLimit = web3.utils.toHex(gasLimit + 10000);
parameter.gasPrice = web3.utils.toHex(gasPrice);
// nonce値はあらかじめ計算しておく
parameter.nonce = nonce;
const transaction = new Tx(parameter);
transaction.sign(Buffer.from(privateKey.substring(2, 66), 'hex'));

return new Promise((resolve, reject) => {
web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex'))
.once('transactionHash', (hash) => {
resolve({ parameter, hash })
}).catch(err => {
reject(err)
})

})
}


秘密鍵による署名

秘密鍵は「0x」から始めて使用する。

MetaMaskなどでエクスポートする秘密鍵は「0x」が省略されているので注意が必要。

また、「0x」を省略した状態で署名すると、下記の参考URLにあるような「private key length is invalid」のエラーが発生するものの送金ができてしまうという事象が発生したので、それも注意が必要。

参考: https://ethereum.stackexchange.com/questions/55038/ethereum-transaction-rangeerror-private-key-length-is-invalid


nonce値の算出

nonce値は0から始まって必ずincrementする必要がある。

通常、 web3.eth.getTransactionCount(address) で取得可能。

nonce値の値を飛ばしてTransactionを送るとpendingのまま待機状態になり、間の数値のトランザクションが送られたタイミングで承認される。

つまり、


  • 1 送信→pending

  • 0 送信

  • 0 と 1 →comfirming

のような流れになる。

そのため、nonce値をある程度コントローラブルにするために、DBに保存しながら対応するのがいいと思います。

僕の場合は、大まかには、


  • getTransactionCount と DBのMAX値を取得

  • 大きい方を採用してトランザクション送信

  • 失敗したらDBのデータを削除

みたいな形で実装しました。


タイムアウトの設定

web3は通常、下記のようにHttpProviderなどのプロバイダーを登録して使用します。(下記はINFURA利用の場合)

const web3 = new W3(new W3.providers.HttpProvider('https://ropsten.infura.io/v3/hogehoge')

タイムアウトを設定しておかないと、ノードの同期に問題が起きている場合などに全然レスポンス返ってこないことがあるので、

設定しておいた方がエラーハンドリングが楽になります。(タイムアウト時間はサービスの特性を元に検討してください。)

const web3 = new W3(new W3.providers.HttpProvider('https://ropsten.infura.io/v3/hogehoge',{ timeout: 5000 })


lambda特有の問題

web3に限った話ではないんですが、web3をlambda上で動かす場合は、nativeモジュールが必要になります。

なので、linuxで開発してデプロイするような場合を除いては、nativeモジュールでビルドしてからデプロイする必要があります。

僕はMacで開発しているので必要でした。

下記の記事を参考に、Dockerでリビルドしてからデプロイするようにしています。

参考: https://github.com/serverless/serverless/issues/308


おわりに

この記事でweb3での問題にぶち当たる人が少なくなればいいなと思っています。

個人的には、gethの同期とかの方が問題多かったりするんですけど、ちょっと解説仕切る自信がなかったので今回は見送りましたw