truffle-hdwallet-providerとは
mnemonicとは1~24個のランダムな単語のことを指します。MetaMaskのウォレットではmnemonicから秘密鍵を作る仕組みがあり、MetaMaskを削除してしまってもmnemonicさえあれば全てを復元できます。この仕組みはMetaMaskだけでなく様々なウォレットで使われているので仕組みを把握しておくといいと思います。
ソースコード
var bip39 = require("bip39"); var hdkey = require('ethereumjs-wallet/hdkey'); var ProviderEngine = require("web3-provider-engine"); var FiltersSubprovider = require('web3-provider-engine/subproviders/filters.js'); var HookedSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js'); var Web3Subprovider = require("web3-provider-engine/subproviders/web3.js"); var Web3 = require("web3"); var Transaction = require('ethereumjs-tx'); function HDWalletProvider(mnemonic, provider_url, address_index=0, num_addresses=1) { this.mnemonic = mnemonic; this.hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(mnemonic)); this.wallet_hdpath = "m/44'/60'/0'/0/"; this.wallets = {}; this.addresses = []; for (let i = address_index; i < address_index + num_addresses; i++){ var wallet = this.hdwallet.derivePath(this.wallet_hdpath + i).getWallet(); var addr = '0x' + wallet.getAddress().toString('hex'); this.addresses.push(addr); this.wallets[addr] = wallet; } const tmp_accounts = this.addresses; const tmp_wallets = this.wallets; this.engine = new ProviderEngine(); this.engine.addProvider(new HookedSubprovider({ getAccounts: function(cb) { cb(null, tmp_accounts) }, getPrivateKey: function(address, cb) { if (!tmp_wallets[address]) { return cb('Account not found'); } else { cb(null, tmp_wallets[address].getPrivateKey().toString('hex')); } }, signTransaction: function(txParams, cb) { let pkey; if (tmp_wallets[txParams.from]) { pkey = tmp_wallets[txParams.from].getPrivateKey(); } else { cb('Account not found'); } var tx = new Transaction(txParams); tx.sign(pkey); var rawTx = '0x' + tx.serialize().toString('hex'); cb(null, rawTx); } })); this.engine.addProvider(new FiltersSubprovider()); this.engine.addProvider(new Web3Subprovider(new Web3.providers.HttpProvider(provider_url))); this.engine.start(); // Required by the provider engine. }; HDWalletProvider.prototype.sendAsync = function() { this.engine.sendAsync.apply(this.engine, arguments); }; HDWalletProvider.prototype.send = function() { return this.engine.send.apply(this.engine, arguments); }; // returns the address of the given address_index, first checking the cache HDWalletProvider.prototype.getAddress = function(idx) { console.log('getting addresses', this.addresses[0], idx) if (!idx) { return this.addresses[0]; } else { return this.addresses[idx]; } } // returns the addresses cache HDWalletProvider.prototype.getAddresses = function() { return this.addresses; } module.exports = HDWalletProvider;
使われているモジュール
主に下記モジュールがtruffle-hdwallet-providerで使われています。特にbip39とethereumjs-walletは重要な役割を担っているので理解しとくといいと思います。
bip39
Bitcoin Improvement Proposal 39をJavascriptで実装したものです。これは決定性キーを生成するプログラムです。
ethereumjs-wallet/hdkey
Bitcoin Improvement Proposal 32をJavascriptで実装したもので階層的決定性ウォレット(Hierarchical Deterministic Wallets:HD Wallets)を作成するプログラムです。
truffle-hdwallet-provider内ではbip39を使ってmnemonicからシードを作成し、そのシードから鍵を生成するHD Walletsインスタンスを作成しています。このインスタンスから秘密鍵やアドレスを生成できます。
web3-provider-engine
Web3 ProviderEngineは独自のweb3プロバイダを作成するためのツールです。
web3
EtherumブロックチェーンとJSON RPC経由でデータの読み書きができるライブラリです。
ソースコード解説
12行目でmnemonicからHD Walletsを作成しています。作成方法はbip39モジュールを使用してmnemonicからシードを作成し、ethereumjs-wallet/hdkeyを使ってシードからHD Walletsを作るという手順になっています。
engineはweb3-provider-engineから生成されています。engine.addProvider()の引数にweb3を使って任意の接続先のネットワークを指定できるようになっています。
HDWalletProviderにはsend、sendAsync、getAddress、getAddressesが追加されていますがこれがどのタイミングで実行されているかちゃんと追えてないです。たぶん、Truffleのmigrate時にこれらの関数が呼ばれていると思います。
まとめ
使っているモジュールは把握できましたが、まだまだソースコードの中身や語句の理解が足りず理解が浅いです。少しずつ理解できてる範囲は広がってきているので今後追記していきたいと思います。
おすすめ書籍
Ethereumを使ったDApps開発を学びたいなら今だとこの1冊が1番良いです!開発環境の構築から使うべきツール、フレームワーク、実装方法・注意点まで網羅的に解説されている書籍なのでおすすめです。出版も2018年1月ということでかなり新しい本で、DMM Bitcoinを作っているネクストカレンシー所属の方が書いているので信頼できます。
ビットコインとブロックチェーンの詳細をしっかりと学びたい方にはこちらの書籍が非常におすすめです。ウォレットの仕組み、楕円曲線暗号、P2Pプロトコル、公開鍵暗号などビットコインを支える技術について詳細に解説されています。また、サンプルコードを通して実際に手を動かして学べるので非常に濃い内容となっています。
髙妻智一
最新記事 by 髙妻智一 (全て見る)
- Polkadot(Substrate)のアドレスとトランザクションについて - 2023-03-09
- 【無料公開】「Goで始めるBitcoin」3章 Bitcoinノードとの通信 技術書典8 - 2020-03-08
- エンジニアがゼロから技術ブログを書くための方法をまとめました - 2019-05-25
ブロックチェーンに詳しい方はきっと我々が実現できないものを探っているからすごいですね。Ethascanでいろいろアドレスを見ていると左上のアドレスと書いておらず、コントラクトと書かれているものがあるのに気が付きました。コントラクトクリエイターの欄に別のアドレスが書かれている場合、そちらが親のアドレスになり、その下の階層で作られた別のアドレスという意味なのでしょうか?たぶんスマートコントラクトの基本がわかっていない者に説明するのも至難の業かとは思いますが、、、