Category Archives: ブロックチェーン

ビットコイン ブロックチェーン プログラミング

Ledger Nanoの受取アドレスを生成するプログラムを作った

Published by:

朝からこんな怖い話がニュースになっていました・・・!

レッジャー社のハードウェアウォレットに脆弱性 「中間者攻撃」で資金流出リスク

 レッジャー社がツイッターで参照する匿名レポートによると、マルウェアに感染したパソコンを使ってレッジャー中にビットコイン受取アドレスを生成しようとする際、「中間者攻撃」とよばれるセキュリティ侵害が起こる可能性がある。マルウェアがアドレス生成のためのコードを改編し全ての入金がハッカーに送られてしまうという。

普通のディスプレイ付きLedger Nanoであればこのサイトで紹介されている方法でアドレスが正しいかどうかを判定できますが、私が持っている古いディスプレイなしのタイプでは確認できません。

というわけで確認用のツールを作ってみました。

Ledger wallet address checker
http://hello.lumiere-couleur.com/app/browser_ledger/

browser_ledger

サーバー生成ではありませんので、ネットワークを切断した状態でも使えます。
というか、そのように使った方がいいかと思います。

TypeScript+Browserify+jQueryで作りましたが、意外に簡単なんですね。
ちょっとしたツールだったら、これからこの方法で行こうかな。

Github リポジトリはこちら

ビットコイン ブロックチェーン プログラミング 発表、ニュース

ビットコインのウォレットと、支払い用のプログラムを作ろう

Published by:

ビットコインのウォレットは数多くありますが、ハードウェアウォレットは高いしなあ・・・という方は、とりあえずウォレットアプリを利用するのがおすすめです。
ウォレットアプリだと、スマホが壊れたときや新しいスマホに買い替えたときに大事なビットコインがなくなっちゃうんじゃない?なんて心配になりますが、大丈夫!
すべてのビットコインはブロックチェーン上に保管されていて、そのブロックチェーンは世界中に遍在しています。

ハードウェアウォレットだろうとペーパーウォレットだろうと、もちろんウォレットアプリも同じですが、すべてその中にビットコインはありません!
これらウォレットはブロックチェーンに保管されているビットコインへの鍵のようなもので、仮にウォレットがぶっ壊れてもビットコインが失われたわけではなく、合鍵を使ってアクセスすればいいのです。

合鍵となるのがリカバリーフレーズですが、最初にウォレットを作るときに必ずメモを残すようにしましょう。
また、このリカバリーフレーズさえあればだれでもそのウォレットへアクセスできますので、十分気をつけて保管して下さい。

私が使っているCopayというウォレットアプリでは、12の日本語の単語(ひらがな)でリカバリーフレーズが作れます。
仮に私がCopayでウォレットを1つ作り、そのリカバリーフレーズを人にあげれば、それはそのまま相手のウォレットにもなっちゃいます。
この仕組み、本当に面白いですよね・・・

最近個人的に作っているプログラムでビットコインの支払いを受け付ける必要があったのですが、HDウォレット形式に対応することで毎回支払いのビットコインアドレスを変えることが出来ます。
こうすることでちゃんと支払われたかどうかをプログラムでチェックできるのでおすすめです。
1つのアドレスに全部送ってもらうようにすると、どれが誰のものか分からなくなってしまうんですよね。

下はTypeScript版のコードですが、これで新しい受取用のアドレスを生成することが出来ます。
Wallet.tsの「const mnemonic = ‘リカバリーフレーズ’;」のところに、Copayで生成したウォレットのリカバリーフレーズを入れます。

import bitcoin = require('bitcoinjs-lib');
import bip39 = require('bip39');

const mnemonicToM = (mnemonic, password, network) => {
    const seed = bip39.mnemonicToSeed(mnemonic, password || "")
    const m = bitcoin.HDNode.fromSeedBuffer(seed, bitcoin.networks[network || "bitcoin"])
    return m
}
const mnemonic = 'リカバリーフレーズ';

/**
 *  ビットコインウォレット
 */
export class Wallet {
  /**
   *  支払い用アドレス生成
   */
  public static createPaymentAddress(tx_index: number) {
    const m = mnemonicToM(mnemonic, '', 'bitcoin');  // 秘密鍵
    // console.log(m.derivePath("m/44'/0'/0'").toBase58()); // xpriv...
    // console.log(m.derivePath("m/44'/0'/0'").neutered().toBase58());  // xpub...
    // console.log(m.derivePath("m/44'/0'/0'/0/0").getAddress()); // 1DQ...

    let paymentAddress = m.derivePath("m/44'/0'/0'/0/" + tx_index).getAddress();
    return paymentAddress;
  }
}

次に、受取チェックのプログラム。

import blockexplorer = require('blockchain.info/blockexplorer');
import rp = require('request-promise-native');

import { BitcoinAddress } from "./BitcoinAddress";
import { BitcoinTransaction } from "./BitcoinTransaction";

/**
 *  ビットコイントランザクション
 */
export class Transaction {
  /**
   *  最新のブロック総数を取得
   */
  public static getBlockCount(): Promise<number> {
    let url = 'https://blockchain.info/ja/q/getblockcount';
    return rp(url)
      .then((body: string) => {
        let count = parseInt(body.trim());
        if (isNaN(count)) {
          return -1;
        }
        return count;
      });
  }

  /**
   *  指定のアドレスの情報を取得
   */
  public static getAddress(address): Promise<BitcoinAddress> {
    // 最新のブロック総数を取得
    return Transaction.getBlockCount()
      .then((count: number) => {
        // アドレス情報を取得
        return blockexplorer.getAddress(address)
          .then((result) => {
            let bitcoinAddress = new BitcoinAddress();
            bitcoinAddress.address = address;
            bitcoinAddress.total_received = result.total_received / 100000000;
            bitcoinAddress.last_confirmation = 0;
            bitcoinAddress.txs = [];

            // トランザクションの承認数を確認
            if (result.txs && result.txs.length > 0) {
              for (let i = 0; i < result.txs.length; i++) {
                let tx = result.txs[i];
                let bitcoinTransaction = new BitcoinTransaction();
                bitcoinTransaction.block_height = tx.block_height ? tx.block_height : count;
                bitcoinTransaction.confirmation = Math.max(0, count - tx.block_height);
                bitcoinTransaction.confirmation = !isNaN(bitcoinTransaction.confirmation) ? bitcoinTransaction.confirmation : 0;
                if (bitcoinAddress.last_confirmation == 0) {
                  bitcoinAddress.last_confirmation = bitcoinTransaction.confirmation;
                } else {
                  bitcoinAddress.last_confirmation = Math.min(bitcoinAddress.last_confirmation, bitcoinTransaction.confirmation);
                }
                bitcoinTransaction.btc_amount = 0;
                for (let j = 0; j < tx.out.length; j++) {
                  let out = tx.out[j];
                  if (out.addr == address) {
                    bitcoinTransaction.btc_amount += out.value / 100000000;
                  }
                }
                bitcoinAddress.txs.push(bitcoinTransaction);
              }
            }

            return bitcoinAddress;
          });
      });
  }
}
import { BitcoinTransaction } from "./BitcoinTransaction";

/**
 *  ビットコインアドレス
 */
 export class BitcoinAddress {
   public address: string;
   public total_received: number;
   public last_confirmation: number;
   public txs: BitcoinTransaction[];
 }
/**
 *  ビットコイントランザクション
 */
 export class BitcoinTransaction {
   public block_height: number;
   public confirmation: number;
   public btc_amount: number;
 }

実行するとこんな感じです。

import { BitcoinAddress } from "bitcoin/BitcoinAddress";
import { Transaction } from "bitcoin/Transaction";
import { Wallet } from "bitcoin/Wallet";

// tx_indexは0スタートのインクリメント値
let paymentAddress = Wallet.createPaymentAddress(0);
console.log('paymentAddress', paymentAddress);

Transaction.getAddress(paymentAddress)
  .then((bitcoinAddress: BitcoinAddress) => {
    console.log('bitcoinAddress', bitcoinAddress);
  });

$ npm run wallet_test 

> myprogram@1.0.0 wallet_test /home/xxxxxxxxx/myprogram
> env NODE_PATH=./build node ./build/wallet_test.js

paymentAddress 1rUTG3jWJ3rkEiKFLyvXbMaQcLnqxr49c
bitcoinAddress BitcoinAddress {
  address: '1rUTG3jWJ3rkEiKFLyvXbMaQcLnqxr49c',
  total_received: 0.0104252,
  last_confirmation: 1508,
  txs: 
   [ BitcoinTransaction {
       block_height: 492652,
       confirmation: 1508,
       btc_amount: 0.0104252 } ] }

生成された支払い法のアドレス(上の実行例では1rUTG3jWJ3rkEiKFLyvXbMaQcLnqxr49c)が、Copayで表示される受取用アドレスと一致してればOK。
または、Copayの設定画面で確認できる、ウォレットの拡張公開鍵と、Wallet.tsでコメントアウトしている「// console.log(m.derivePath(“m/44’/0’/0′”).neutered().toBase58()); // xpub…」のコメントを外して実行してみて、その出力結果が一致しているかどうかも確認した方がいいでしょう。

ソースのダウンロードはこちら

ビットコイン ブロックチェーン

Ledger NanoでSegwitに切り替えたら残高なくなってて焦った

Published by:

ビットコインを受け取ろうと、ハードウェアウォレットのLedger Nano(過去の記事「ビットコインを普通のお財布に入れてみた」)をPCに挿したところ、Segwit対応のファームウェアにアップデートできるとのことで、アップデートしました。
いったん全部消してリカバリーするのでドキドキするのですが、8月のビットコインキャッシュ誕生の時にも一度やっていたので同じようにアップデートしました。

アップデート後にLedger Nanoを挿し直して、Segwit切り替えの確認画面が出たのでSegwitを選んだところ、残高が 0 BTCになってる!?
確認画面をよく見たらアドレスが変わるので、旧ウォレットから新ウォレットへ送金しないといけないそう。
設定画面から旧ウォレットに戻して新ウォレットのアドレスへ全額送信して、ちゃんと移動できました。
あ~、びっくりした。

Segwitへの切替画面。 後からいつでもスイッチ可能です。

Segwitへの切替画面。
後からいつでもスイッチ可能です。

SETTINGSの右下のBLOCKCHAINSをクリック

SETTINGSの右下のBLOCKCHAINSをクリック

BITCOINを選択して、次の画面(最初の写真と同じ)でLEGACYをクリックすると旧ウォレットが開きます。

BITCOINを選択して、次の画面(最初の写真と同じ)でLEGACYをクリックすると旧ウォレットが開きます。

旧ウォレットから新ウォレットへ全額移動。焦らせるぜ・・・

旧ウォレットから新ウォレットへ全額移動。焦らせるぜ・・・

Ledger Nanoは品切れで、より高機能なLedger Nano Sも売ってるけど、高い・・・。
見た目もLedger Nanoの方が好きなんだけどな。

ビットコイン ブロックチェーン

インターネットはcopy、ブロックチェーンはmove

Published by:

ブロックチェーンやビットコインについての記事を度々見かけるようになりましたが、技術的な説明が全面に出てきてしまうので、結局何?という感じの人が多いのではないでしょうか。

一昨日の朝日新聞にもブロックチェーンの記事があり、最近読んだ中では一番分かりやすかったのですが、横からのぞき込んできた小学三年生の息子に「ブロックチェーンって何?」とシンプルに質問されて、思わずたじろいでしまいました。

その時は「インターネットは調べ物をしたりするときに使うけど、ブロックチェーンはいろんなものを交換するのに使うんだよ」と答えましたが、頭のなかでは何かうまく言えてないなあと息子の問いが残ったままになっていました。

あれから考え続けてやっと一つ分かりやすい言い回しを思いついたのが、タイトルの「インターネットはcopy、ブロックチェーンはmove」です。

インターネットブラウザで「ソースを見る」をしたことがある人は、最初びっくりしたのではないでしょうか。
どこかのWebサイトのページを表示して、「ソースを見る(Ctrl-U)」でHTMLソースを表示して、ブラウザによっては編集も出来て、しかも編集した内容がブラウザにも反映されてしまうと、もしかしてハッキングしちゃった??なんてドキドキしたりして。

実際はブラウザで表示しているのは、Webサイトのサーバーに置いてある実体のコピーなので、サーバーの元ファイルを書き換えることは出来ません。
私も昔、そこでやっとインターネットの仕組みについて理解しました(なーんだコピーを見てるんだー)。

ブロックチェーンは問答無用でmove(移動)します。
地上波放送の「ダビング10(9回まではDVDにコピーできるけど、10回目は元ファイルが消えてしまう仕組み)」の最後のムーブだけみたいな感じです。

オンライン銀行取引なんかでmove(振込)出来るよ?と思われる方もいるでしょうが、あれは銀行内部の取引をインターネットを介して依頼しているだけで、仕組み自体は疑似moveといったところです。

インターネットは商取引やデータのやり取りに長けていますが、実はmoveが超苦手で、どんどんコピーばかりを作ってしまいます。
なので、著作物の違法コピーや違法ダウンロードは、法律で取り締まるしか対応策がありません。

ブロックチェーンはインターネットに新しく取り付けられたmove機能と言ってもいいかもしれません。
コピーされては困るもの、お金や著作物、株式・債権や契約書、プライバシーに関わるものなど、これまでインターネットでのやり取りをしようと思ったら信頼できる第三者を経由しなくてはいけませんでしたが、これからは直接当人同士がオンラインでやり取りできるようになるというものです。

ちなみに、開発者にとってはブロックチェーンは勉強しがいのあるテーマです。
ブラウザプログラムのソースは超巨大で、Chromiumの場合ソースコードをリポジトリからダウンロードすると数TBにもなりますが、ビットコイン・コアのソースはzipでダウンロードすると8MBしかなく、一つ一つのソースを簡単に見ることが出来ます。
いつか自分もブロックチェーン・プログラムを書いてみたい!