読者です 読者をやめる 読者になる 読者になる

Counterwallet 12-word passphrase作成〜ビットコインアドレスの残高表示までの流れ

f:id:yzono:20150107230707j:plain

はじめに

Counterwalletを初めて使うときはまず12-wordのパスフレーズを作成して、そのパスフレーズを使ってログインします。

パスフレーズを元に作成したビットコインアドレスと、wallet_idと呼ばれる設定情報を保存するためのIDだけがクライアントとサーバーでやりとりされ、パスフレーズ秘密鍵はサーバーに送信されません。

今回はそのあたりについて書きます。

目次

  • 12-word passphrase作成
  • 12-word passphraseからwallet_idを生成
  • 12-word passphraseからビットコインアドレスを生成
  • サーバーとの通信

12-word passphrase作成

logon.jsのgeneratePassphraseメソッドからMnemonicメソッドが呼ばれます。Mnemonicメソッドは、mnemonic.jsで定義されています。

self.generatePassphrase = function() {
  var m = new Mnemonic(128); //128 bits of entropy (12 word passphrase)

  var words = m.toWords();
  self.generatedPassphrase(words.join(' '));

  //select the generated passphrase text
  selectText('generated');
}

mnemonic.jsの使い方は、folk元のmnemonic.jsが参考になります。

また、mnemonic.js作者の@ggozadによるこのスライドに、どうしてmnemonicパスフレーズを使用すべきか書いてあります。以下は抜粋です。

  1. 設定するパスワードのルールが明確。(一般の方法は、サイトによって大文字や数字を含めないといけないなど独自のルールが存在して分かりにくい)
  2. 覚えやすい1626通りの単語からフレーズが生成される。(記憶間違いを防げます)
  3. 3単語で32-bit integer、12単語の組み合わせで128-bit integerのentropyが実現可能。

12-word passphraseからwallet_idを生成

logon.jsのopenWalletメソッドでwallet_idを生成しています。

// generate the wallet ID from a double SHA256 hash of the passphrase and the network (if testnet)
var hashBase = CryptoJS.SHA256(self.sanitizedEnteredPassphrase() + (USE_TESTNET ? '_testnet' : '')); // sanitizedEnteredPassphrase()は、小文字にしてtrimしています
var hash = CryptoJS.SHA256(hashBase).toString(CryptoJS.enc.Base64);

WALLET.identifier(hash);
$.jqlog.log("My wallet ID: " + WALLET.identifier());

wallet_idはCounterwalletの設定(色の設定など)や、多重ログインの有無チェックのために利用され、MongoDB内に保存されます。Bitcoinの送信やCounterpartyのAssetに対する変更などクリティカルなところでは使用されません。

12-word passphraseからビットコインアドレスを生成

wallet_id作成後、規約画面が表示され、それに同意すると12-word passphraseからビットコインアドレスを生成します。

logon.jsのopenWalletPt2メソッド内でCWHierarchicalKeyが初期化されます。

self.openWalletPt2 = function(mustSavePreferencesToServer) {
    WALLET.BITCOIN_WALLET = new CWHierarchicalKey(self.enteredPassphrase());
    WALLET.isOldWallet(WALLET.BITCOIN_WALLET.useOldHierarchicalKey);
    setTimeout(function() { self.genAddress(mustSavePreferencesToServer, PREFERENCES['num_addresses_used']) }, 1);
}

CWHierarchicalKeyはutil.bitcore.jsに定義されてます。(以下メソッドはだいぶ省略してます)

CWHierarchicalKey.prototype.init = function(passphrase) {
  this.passphrase = passphrase;
  var seed = this.wordsToSeed(words);   
  this.HierarchicalKey = this.useOldHierarchicalKey ? this.oldHierarchicalKeyFromSeed(seed) : bitcore.HierarchicalKey.seed(seed, NETWORK.name);
}

logon.jsのgenAddressメソッドビットコインアドレスが生成されます。

self.genAddress = function(mustSavePreferencesToServer, addressCount) {
    
  var moreAddresses = [];

  for (var i = 0; i < addressCount; i++) {

    var address = WALLET.addAddress('normal');
    var addressHash = hashToB64(address);
    var len = WALLET.addresses().length;
    moreAddresses.push(address);
...

サーバーとの通信

クライアントとサーバー間で以下APIがやりとりされます。APIは全てcounterblockd APIです。詳細は別途書きます。

1. wallet_id取得前

  • is_ready

2. wallet_id取得後

  • is_wallet_online {'wallet_id': WALLET.identifier()}
  • get_preferences {'wallet_id': WALLET.identifier(),'network': USE_TESTNET ? 'testnet' : 'mainnet','for_login': true}

3. ビットコインアドレス取得後

  • get_num_users_online
  • get_chain_address_info {addresses: ["19wcJfJR2uSw5mf6yRdHhdifnJTZAJJdRz"], with_uxtos: true, with_last_txn_hashes: 5}
  • store_preferences {'wallet_id': WALLET.identifier(), 'preferences': PREFERENCES, 'network': USE_TESTNET ? 'testnet' : 'mainnet', 'referer': ORIG_REFERER }
  • get_normalized_balances {addresses: ["1AG4JA3oDNunXmTYH4PmG6eAbRZRfSQd2y"]}

まとめ

次回はビットコインの送信処理の流れを見てみます。

補足

Mnemonic Code Converter @bip39ってジョナサンかな

bitpay/bitcore