├── Chapter01.md ├── Chapter02-test.md ├── Chapter03.md ├── Chapter04.md ├── Chapter05.md ├── Chapter06.md ├── Chapter07.md ├── Chapter09.md ├── Chapter10.md ├── Chapter11.md └── README.md /Chapter01.md: -------------------------------------------------------------------------------- 1 | # ツールの導入 2 | 3 | ## Bitcoin Core 4 | 5 | ### Mac OS X の場合 6 | 7 | #### ソースコードのクローン 8 | 9 | ``` 10 | $ git clone https://github.com/bitcoin/bitcoin.git 11 | ``` 12 | 13 | ``` 14 | $ cd bitcoin 15 | $ git tag 16 | ``` 17 | 18 | ``` 19 | $ git checkout -b 0.16.0 refs/tags/v0.16.0 20 | ``` 21 | 22 | #### 依存パッケージのダウンロード 23 | 24 | ``` 25 | brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config homebrew/versions/protobuf260 --c++11 qt5 libevent 26 | ``` 27 | 28 | #### コンパイル 29 | 30 | ``` 31 | $ ./autogen.sh 32 | $ ./configure 33 | $ make -j4 34 | $ make install 35 | ``` 36 | 37 | ``` 38 | $ which bitcoind 39 | /usr/local/bin/bitcoind 40 | 41 | $ which bitcoin-cli 42 | /usr/local/bin/bitcoin-cli 43 | ``` 44 | 45 | ### Ubuntu の場合 46 | 47 | #### パッケージファイルからインストール 48 | 49 | ``` 50 | $ sudo apt-add-repository ppa:bitcoin/bitcoin 51 | $ sudo apt-get update 52 | $ sudo apt-get install bitcoind bitcoin-qt 53 | ``` 54 | 55 | #### ソースコードからコンパイル 56 | 57 | ソースコードのクローンとコンパイルの手順については Mac OS X と同様 58 | 59 | ##### 依存パッケージのダウンロード 60 | 61 | ``` 62 | $ sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils 63 | ``` 64 | 65 | ``` 66 | $ sudo apt-get install libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev 67 | または 68 | $ sudo apt-get install libboost-all-dev 69 | ``` 70 | 71 | ``` 72 | $ sudo add-apt-repository ppa:bitcoin/bitcoin 73 | $ sudo apt-get update 74 | $ sudo apt-get install libdb4.8-dev libdb4.8++-dev 75 | ``` 76 | 77 | ``` 78 | $ sudo apt-get install libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libprotobuf-dev protobuf-compiler 79 | ``` 80 | 81 | ``` 82 | $ sudo apt-get install libqrencode-dev 83 | ``` 84 | 85 | ``` 86 | $ sudo apt-get install libminiupnpc-dev 87 | ``` 88 | 89 | ### 初期設定 90 | 91 | #### Bitcoin Core でビットコイン・ネットワークに接続する 92 | 93 | ##### bitcoind の起動と停止 94 | 95 | ``` 96 | $ bitcoin-qt & 97 | ``` 98 | 99 | デーモンのみ 100 | 101 | ``` 102 | $ bitcoind -daemon 103 | ``` 104 | 105 | regtestモード 106 | 107 | ``` 108 | $ bitcoind -regtest -daemon 109 | ``` 110 | 111 | 停止 112 | 113 | ``` 114 | $ bitcoin-cli stop 115 | ``` 116 | 117 | #### bitcoin-cli コマンドを使ってbitcoind と対話する 118 | 119 | ``` 120 | $ bitcoin-cli getblockchaininfo 121 | ``` 122 | 123 | ##### RESTインターフェースを使ってbitcoind と対話する 124 | 125 | ``` 126 | $ curl localhost:18332/rest/chaininfo.json 127 | ``` 128 | 129 | ##### ビットコイン・ネットワークのノードとの接続 130 | 131 | ``` 132 | $ bitcoin-cli getconnectioncount 133 | ``` 134 | 135 | ``` 136 | $ bitcoin-cli getpeerinfo 137 | ``` 138 | 139 | ##### regtestモードでのブロック生成 140 | 141 | ``` 142 | $ bitcoin-cli -regtest generate 3 143 | ``` 144 | 145 | ## bitcoin-ruby 146 | 147 | ### インストール 148 | 149 | ``` 150 | $ sudo gem install bitcoin-ruby 151 | ``` 152 | 153 | Rubygemsがインストールされていない場合は、先にRubygemsをインストール 154 | ``` 155 | $ sudo apt install ruby 156 | ``` 157 | 158 | 159 | ## openassets-ruby 160 | 161 | ### インストール 162 | 163 | ``` 164 | $ sudo gem install openassets-ruby 165 | ``` 166 | 167 | ruby.hが見つからない場合 168 | 169 | ``` 170 | $ sudo apt-get install ruby-dev 171 | ``` 172 | 173 | sqlite3が見つからない場合 174 | 175 | ``` 176 | $ sudo apt-get install libsqlite3-dev 177 | ``` 178 | -------------------------------------------------------------------------------- /Chapter02-test.md: -------------------------------------------------------------------------------- 1 | # 2章のテスト 2 | 3 | ### 問1 4 | ビットコインネットワークの特徴を、構造、信用、機能、頑強性の観点で説明してください 5 | 6 | ### 問2 7 | トランザクションのインプットとアウトプットは、それぞれどのような情報が格納されていますか? 8 | 9 | ### 問3 10 | トランザクションのブロードキャストに対して、不正な中継ノードによる攻撃にはどのようなものがあるか論じてください 11 | 12 | ### 問4 13 | トランザクションのインプットとアウトプットの差額は、誰のものになりますか?またそれを支払う理由は何ですか? 14 | -------------------------------------------------------------------------------- /Chapter03.md: -------------------------------------------------------------------------------- 1 | # ビットコインの暗号技術 2 | 3 | ## 利用する暗号ライブラリの準備 4 | 5 | ``` 6 | $ gem install ecdsa 7 | $ gem install bitcoin 8 | ``` 9 | 10 | ### openssl 11 | 12 | openssl のrubygems ライブラリはインストール不要です。 13 | 14 | 以下、irbのプロンプト >> は省力します。 15 | 16 | ```ruby 17 | $ irb 18 | 19 | # 以下 irbのプロンプト >> から入力 20 | 21 | require 'openssl' 22 | ecdsa = OpenSSL::PKey::EC.new('secp256k1') 23 | key = ecdsa.generate_key # 新しい暗号鍵を生成 24 | key.private_key.to_i # 秘密鍵を整数として取り出す 25 | ``` 26 | 27 | [class OpenSSL::PKey::ECのドキュメント](https://docs.ruby-lang.org/ja/latest/class/OpenSSL=3a=3aPKey=3a=3aEC.html) 28 | 29 | ### securerandom による安全な乱数 30 | 31 | ```ruby 32 | require 'securerandom' 33 | SecureRandom.hex(32) # 32バイト(=256ビット)の疑似乱数を生成する 34 | ``` 35 | 36 | [securerandomのドキュメント](https://docs.ruby-lang.org/ja/latest/class/SecureRandom.html) 37 | 38 | ### ruby_ecdsa 39 | 40 | ```ruby 41 | require 'ecdsa' 42 | group = ECDSA::Group::Secp256k1 43 | ``` 44 | 45 | [ruby_ecdsa のドキュメント](http://www.rubydoc.info/gems/ecdsa/ECDSA/Point) 46 | 47 | ### bitcoin-ruby 48 | 49 | ```ruby 50 | require 'bitcoin' 51 | Bitcoin.network = :testnet3 52 | key = Bitcoin::generate_key # 暗号鍵の生成 53 | ``` 54 | 55 | ## ビットコインに利用されている暗号技術の基本 56 | 57 | ### 暗号学的ハッシュ関数 58 | 59 | ```ruby 60 | require 'digest' 61 | message = "暗号学的ハッシュ関数とは、直感的には任意の長さの文字列を入力すると一定のサイズのランダムな数値を返すような関数です。" 62 | Digest::SHA256.digest(message) # sha256のハッシュ値を求める 63 | Digest::RMD160.digest(message) # ripemd160のハッシュ値を求める 64 | 65 | require 'securerandom' 66 | nonce = SecureRandom.hex(32) # コミットメントのためのnonce 67 | commitment = Digest::SHA256.digest(nonce + message) # sha256のハッシュ値を求める 68 | ``` 69 | 70 | ## ECDSA 71 | 72 | ### opensslに実装されている楕円曲線 73 | 74 | ```ruby 75 | require 'openssl' 76 | curves = OpenSSL::PKey::EC.builtin_curves 77 | ``` 78 | 79 | ### 楕円曲線 80 | 81 | 以下の式を満たす数のこと 82 | 83 | 84 | 85 | 86 | ### ruby_ecdsaで SECP256k1のパラメータを確認する 87 | 88 | ```ruby 89 | require 'ecdsa' 90 | group = ECDSA::Group::Secp256k1 # secp256k1のパラメータセットのオブジェクト生成 91 | prime=group.field.prime # 剰余系の素数 92 | => 115792089237316195423570985008687907853269984665640564039457584007908834671663 93 | group.generator.x # 基準点Gのx座標 94 | => 55066263022277343669578718895168534326250603453777594175500187360389116729240 95 | group.generator.y # 基準点Gのy座標 96 | => 32670510020758816978083085130507043184471273380659243275938904335757337482424 97 | group.param_a # 係数a 98 | => 0 99 | group.param_b # 係数b 100 | => 7 101 | group.order # Gの位数 102 | => 115792089237316195423570985008687907852837564279074904382605163141518161494337 103 | ``` 104 | ### ruby_ecdsaによる楕円曲線の基本演算 105 | 106 | 107 | ```ruby 108 | require 'ecdsa' 109 | group = ECDSA::Group::Secp256k1 110 | G=group.generator # 基点 111 | G.multiply_by_scalar(3) # 基点のスカラー倍 3G 112 | G*3 # 基点のスカラー倍 3G(簡略型) 113 | n=group.order # 基点の位数 114 | G*n # 基点を位数倍する 115 | (G*n).infinity? # 無限遠点=0 116 | => true 117 | (G*(n+1)).infinity? 118 | => false 119 | (G*(n+1))==G # (n+1)G = G 120 | => true 121 | p=G*4 122 | q=G*7 123 | p.add_to_point(q) # 点の和 124 | r=p+q # 点の和(簡略型) 125 | s=p.negate # 点の符号の反転 s=(-p) 126 | (p+s).infinity? # p+(-p) = 0 127 | => true 128 | p.x # 点pのx座標 129 | => 103388573995635080359749164254216598308788835304023601477803095234286494993683 130 | s.x # 点sのx座標 131 | => 103388573995635080359749164254216598308788835304023601477803095234286494993683 132 | p.y # 点pのy座標 133 | => 37057141145242123013015316630864329550140216928701153669873286428255828810018 134 | s.y # 点sのy座標 135 | => 78734948092074072410555668377823578303129767736939410369584297579653005861645 136 | prime=group.field.prime # スカラーの剰余系の素数 137 | (prime - p.y) == s.y # prime - 点pのy座標 = 点sのy座標 138 | => true 139 | ``` 140 | 141 | 142 | ### ECDSAの暗号鍵の生成 143 | 144 | 145 | ```ruby 146 | require 'ecdsa' 147 | require 'securerandom' 148 | 149 | group = ECDSA::Group::Secp256k1 150 | n = group.order # Gの位数 151 | privKey = 1 + SecureRandom.random_number(n-1) # 秘密鍵の生成 152 | pubKey = group.generator.multiply_by_scalar(privKey) # 公開鍵の生成 153 | G = group.generator # 基準点 154 | pubkey_simple = G*privKey # 公開鍵の生成(簡略版) 155 | pubkey.eql?(pubkey_simple) # 点と点が等しい 156 | => true 157 | pubKey.x # 公開鍵のx座標の値 158 | pubKey.y # 公開鍵のy座標の値 159 | ``` 160 | 161 | bitcoin-rubyの場合 162 | 163 | ```ruby 164 | require 'bitcoin' 165 | key = Bitcoin::generate_key # 秘密鍵と公開鍵の配列を出力する 166 | privKey=key[0] # 秘密鍵 167 | pubKey=key[1] # 公開鍵 168 | ``` 169 | 170 | ruby-opensslの場合 171 | 172 | ```ruby 173 | require 'openssl' 174 | ec = OpenSSL::PKey::EC.new('secp256k1') # secp256k1楕円曲線暗号オブジェクトの生成 175 | key=ec.generate_key # 秘密鍵と公開鍵を生成 176 | privKey = key.private_key.to_i # 秘密鍵を整数で表示 177 | pubKey = key.public_key.to_bn.to_i # 公開鍵を整数で表示 178 | ``` 179 | 180 | ### ECDSAによる電子署名 181 | 182 | ```ruby 183 | require 'digest' 184 | require 'securerandom' 185 | require 'ecdsa' 186 | 187 | group = ECDSA::Group::Secp256k1 188 | n = group.order # Gの位数 189 | privKey = 1 + SecureRandom.random_number(n-1) # 秘密鍵の生成 190 | pubKey = group.generator.multiply_by_scalar(privKey) # 公開鍵の生成 191 | 192 | message = "この文字列が電子署名の対象となるメッセージです" 193 | digest = Digest::SHA256.hexdigest(message) # メッセージのダイジェスト 194 | 195 | k = 1 + SecureRandom.random_number(n-1) # ランダムな整数kを選択 196 | sign = ECDSA.sign(group,privKey,digest,k) # 電子署名の作成 197 | 198 | sign.r # 電子署名の rの確認 199 | sign.s # 電子署名の sの確認 200 | 201 | ``` 202 | 203 | #### 電子署名のバイナリエンコーディング 204 | 205 | ```ruby 206 | der_sign = ECDSA::Format::SignatureDerString.encode(sign) 207 | ``` 208 | 209 | #### 電子署名の検証 210 | 211 | ```ruby 212 | digest = OpenSSL::Digest::SHA256.hexdigest(message) # メッセージのダイジェスト 213 | sign = ECDSA::Format::SignatureDerString.decode(der_sign) # DER形式の電子署名をデコード 214 | 215 | ECDSA.valid_signature?(pubKey, digest, sign) # 電子署名の検証 216 | ``` 217 | 218 | ruby-opensslの場合 219 | 220 | ```ruby 221 | require 'openssl' 222 | ec = OpenSSL::PKey::EC.new('secp256k1') 223 | 224 | key=ec.generate_key # 暗号鍵ペアの生成 225 | message = "この文字列が電子署名の対象となるメッセージです" 226 | digest = OpenSSL::Digest::SHA256.hexdigest(message) # メッセージのダイジェスト 227 | sign = key.dsa_sign_asn1(digest) # 電子署名の作成 228 | 229 | key.dsa_verify_asn1(digest,sign) # 電子署名の検証 230 | 231 | ``` 232 | 233 | ### ビットコインアドレス 234 | 235 | #### base58エンコーディング 236 | 237 | ```ruby 238 | # base58エンコーディングを行うRuby プログラム 239 | 240 | A = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 241 | remain = ->n{(n<58 ? A[n] : [remain[n/58],A[n%58]])} 242 | base58encode = ->h{remain[h.hex].flatten.join} 243 | 244 | # 実行例 245 | base58encode["6f98c2e2dba3c5befb3892f0b09010abd920ae1a75bcc96fe3"] 246 | => "muSgU96EZbyokvEwhZPQvmRJPEB7B97CUA" 247 | ``` 248 | 249 | #### base58デコーディング 250 | 251 | ```ruby 252 | # base58デコーディングを行うRuby プログラム 253 | 254 | A = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 255 | base58decode = ->b{b.reverse.split('').map.with_index{|c,i| A.index(c)*58**i}.reduce(:+).to_s(16)} 256 | 257 | # 実行例 258 | base58decode["muSgU96EZbyokvEwhZPQvmRJPEB7B97CUA"] 259 | => "6f98c2e2dba3c5befb3892f0b09010abd920ae1a75bcc96fe3" 260 | ``` 261 | 262 | #### ペイロード 263 | 264 | ```ruby 265 | require 'bitcoin' 266 | key = Bitcoin::generate_key # 暗号鍵ペアの生成 267 | pubKey = [key[1]].pack("H*") # 16進数文字列表現を1文字ずつ4ビットずつのビット列にする 268 | 269 | include Digest # Digestクラスの名前空間を登録 270 | payload = RMD160.hexdigest(SHA256.digest(pubKey)) # ペイロードを作成 271 | 272 | payload6f = ['6f'+ payload].pack("H*") # testnetのチェックサム用ペイロード 273 | 274 | checksum = SHA256.hexdigest(SHA256.digest(payload6f) )[0...8] # testnetのチェックサム 275 | 276 | address = Bitcoin::encode_base58('6f'+ payload +checksum) # testnetのアドレス 277 | 278 | payload00 = ['00'+ payload].pack("H*") # mainnetのチェックサム用ペイロード 279 | 280 | checksum = SHA256.hexdigest(SHA256.digest(payload00) )[0...8] # mainnetのチェックサム 281 | 282 | address = Bitcoin::encode_base58('00'+ payload + checksum) # mainnetのアドレス 283 | 284 | ``` 285 | 286 | bitcoin-rubyの場合 287 | 288 | ```ruby 289 | Bitcoin::network = :testnet3 # testnet の場合 290 | address = Bitcoin::pubkey_to_address(key[1]) # testnetアドレスの生成 291 | 292 | Bitcoin::network = :bitcoin # mainnet の場合 293 | 294 | address = Bitcoin::pubkey_to_address(key[1]) # mainnetアドレスの生成 295 | ``` 296 | 297 | #### Base58Checkエンコーディング 298 | 299 | ```ruby 300 | require 'bitcoin' 301 | base58data = "muSgU96EZbyokvEwhZPQvmRJPEB7B97CUA" # Base58Check形式のデータ 302 | 303 | Bitcoin.base58_checksum?(base58data) # Base58Check形式の検査 304 | 305 | ``` 306 | 307 | ## 暗号鍵のフォーマット 308 | 309 | ### 公開鍵のフォーマット 310 | 311 | #### 非圧縮形式の公開鍵 312 | 313 | ```ruby 314 | x = "8b7a293ef55f76be5bbcd72de1d767e568f218f7713c000e3abc15662e3af6cc" 315 | y = "bf01df16760f317b796ecb799701fcbc776a781eaa6806d88b31e1e187d45a5f" 316 | pubKey = "04" + x + y 317 | 318 | pubKey # 公開鍵 319 | ``` 320 | 321 | #### 圧縮形式の公開鍵 322 | 323 | ```ruby 324 | x = "8b7a293ef55f76be5bbcd72de1d767e568f218f7713c000e3abc15662e3af6cc" 325 | y = "bf01df16760f317b796ecb799701fcbc776a781eaa6806d88b31e1e187d45a5f" # yは奇数 326 | compressed_pubKey = "03" + x # y が奇数の場合、プレフィックス "03"をつける 327 | => "038b7a293ef55f76be5bbcd72de1d767e568f218f7713c000e3abc15662e3af6cc" 328 | 329 | 330 | x = "b724a33a5ff35d56a66ab8cf3a314252d8680c35d2e6cc385747d03c8860919c" 331 | y = "6967c2222dc07b674e6207c81972fd162d12f76c7a4ac603cf057fa34d6b09e2" # yは偶数 332 | compressed_pubKey = "02" + x # y が偶数の場合、プレフィックス "02"をつける 333 | => "02b724a33a5ff35d56a66ab8cf3a314252d8680c35d2e6cc385747d03c8860919c" 334 | 335 | 336 | # 非圧縮形式から圧縮形式に変換するRubyプログラム 337 | to_compressed_pubKey = -> pk{(pk.to_i(16).odd? ? "03"+pk[2..65] : "02"+pk[2..65])} 338 | 339 | # 実行例 340 | pubKey1 = "048b7a293ef55f76be5bbcd72de1d767e568f218f7713c000e3abc15662e3af6ccbf01df16760f317b796ecb799701fcbc776a781eaa6806d88b31e1e187d45a5f" # yが奇数 341 | pubKey2 = "04b724a33a5ff35d56a66ab8cf3a314252d8680c35d2e6cc385747d03c8860919c6967c2222dc07b674e6207c81972fd162d12f76c7a4ac603cf057fa34d6b09e2" # yが偶数 342 | to_compressed_pubKey[pubKey1] 343 | => "038b7a293ef55f76be5bbcd72de1d767e568f218f7713c000e3abc15662e3af6cc" 344 | to_compressed_pubKey[pubKey2] 345 | => "02b724a33a5ff35d56a66ab8cf3a314252d8680c35d2e6cc385747d03c8860919c" 346 | ``` 347 | 348 | 349 | -------------------------------------------------------------------------------- /Chapter04.md: -------------------------------------------------------------------------------- 1 | # ビットコインワレット 2 | 3 | bitcoin core デーモンの起動 4 | 5 | ``` 6 | $ bitcoind & 7 | ``` 8 | 9 | bitcoin-cli コマンド 10 | 11 | ``` 12 | $ bitcoin-cli getblockcount 13 | 14 | ``` 15 | 16 | ## ワレットの初期化 17 | 18 | ### 新しいアドレスの生成 19 | 20 | ``` 21 | $ bitcoin-cli getnewaddress '' legacy 22 | <ビットコインアドレス1> 23 | ``` 24 | 25 | ### アドレスにアカウントを設定する 26 | 27 | ``` 28 | $ bitcoin-cli setaccount <ビットコインアドレス> <アカウント> 29 | ``` 30 | 31 | アカウントごとの所持金残高を知る。 32 | 33 | ``` 34 | $ bitcoin-cli listaccounts 35 | 36 | ``` 37 | 38 | ### ワレットの秘密鍵の暗号化 39 | 40 | ``` 41 | $ bitcoin-cli encryptwallet <パスワード> 42 | ``` 43 | 44 | ワレットの秘密鍵の復号化 45 | 46 | ``` 47 | $ bitcoin-cli walletpassphrase <パスワード> <保持時間(秒)> 48 | ``` 49 | 50 | ワレットの再暗号化 51 | 52 | ``` 53 | $ bitcoin-cli walletlock 54 | ``` 55 | 56 | ### testnetでのビットコインの入手 57 | 58 | 59 | * [UO1 testnet Faucet :](http://bitcoinfaucet.uo1.net/) 60 | * [TP's testnet Faucet :](http://tpfaucet.appspot.com/) 61 | * [flyingkiwi's testnet Faucet :](https://testnet.manu.backend.hamburg/faucet) 62 | * [nkuttler's Bitcoin testnet Faucet :](https://kuttler.eu/en/bitcoin/btc/faucet/) 63 | 64 | 65 | ## ワレット残高を確認する 66 | 67 | ``` 68 | $ bitcoin-cli getbalance 69 | ``` 70 | 71 | ### 自分宛のUTXOを確認する 72 | 73 | ``` 74 | $ bitcoin-cli listunspent 75 | ``` 76 | 77 | ### bitcoin core のJSON RPC APIの利用 78 | 79 | 80 | ```ruby 81 | require 'bitcoin' 82 | require 'net/http' 83 | require 'json' 84 | Bitcoin.network = :testnet3 85 | RPCUSER = <ユーザ名> # JSON RPC のためのユーザ名 86 | RPCPASSWORD = <パスワード> # JSON RPC のためのパスワード 87 | HOST="localhost" # JSON RPC のhost 88 | PORT=18332 # ポート番号 89 | 90 | #bitcoindへのHTTPアクセスするメソッド 91 | def bitcoinRPC(method,param) 92 | http = Net::HTTP.new(HOST, PORT) 93 | request = Net::HTTP::Post.new('/') 94 | request.basic_auth(RPCUSER,RPCPASSWORD) 95 | request.content_type = 'application/json' 96 | request.body = {method: method, params: param, id: 'jsonrpc'}.to_json 97 | JSON.parse(http.request(request).body)["result"] 98 | end 99 | 100 | ``` 101 | 102 | ```ruby 103 | bitcoinRPC('getbalance',[]) # ワレットの残高を得る 104 | bitcoinRPC('listunspent',[]) # UTXOのリストを得る 105 | ``` 106 | 107 | ## ビットコインを送金する 108 | 109 | 取引手数料の既定値の設定 110 | 111 | ``` 112 | $ bitcoin-cli settxfee 0.00001 113 | ``` 114 | 115 | 暗号化された秘密鍵を復号化するパスワードのセット 116 | 117 | ``` 118 | $ bitcoin-cli walletpassphrase <パスワード> <保持時間(秒)> 119 | ``` 120 | 121 | ビットコインの送金 122 | 123 | ``` 124 | $ bitcoin-cli sendfrom <送金元アカウント> <送金先ビットコインアドレス> <送金金額> 125 | <トランザクションID> 126 | ``` 127 | 128 | 秘密鍵の復号化に使用するパスワードをメモリから削除してワレットをロック状態に戻す 129 | 130 | ``` 131 | $ bitcoin-cli walletlock 132 | ``` 133 | 134 | ```ruby 135 | bitcoinRPC('walletpassphrase',[<パスワード>, <保持時間(秒)>]) 136 | bitcoinRPC('sendfrom',[<アカウント>, <送金先ビットコインアドレス>, <送金金額(BTC)>]) 137 | bitcoinRPC('walletlock',[]) 138 | ``` 139 | 140 | ### トランザクション内容の確認 141 | 142 | `bitcoin-cli gettransaction <トランザクションID>` 143 | 144 | 実行例 145 | 146 | ``` 147 | $ bitcoin-cli gettransaction 45d3d4b716418fcf3a983df572d9faa9e799a7e36439cd415211e66554ecf381 148 | ``` 149 | 150 | `bitcoinRPC('gettransaction',['<トランザクションID>'])` 151 | 152 | 実行例 153 | 154 | ```ruby 155 | tx = bitcoinRPC('gettransaction',['45d3d4b716418fcf3a983df572d9faa9e799a7e36439cd415211e66554ecf381']) 156 | 157 | ``` 158 | 159 | ```ruby 160 | tx.keys # Hashとしてのトランザクションのキーの一覧 161 | => ["amount", "fee", "confirmations", "blockhash", "blockindex", "blocktime", "txid", "walletconflicts", "time", "timereceived", "bip125-replaceable", "details", "hex"] 162 | 163 | tx["details"] # トランザクションの詳細の例 164 | => [{"account"=>"MickeyMouse", "address"=>"msksQAKTiCFYC8MH7CqoKpH1FfvBGuAbeU", "category"=>"send", "amount"=>-0.1, "label"=>"SatoshiNakamoto", "vout"=>0, "fee"=>-2.31e-05, "abandoned"=>false}, {"account"=>"SatoshiNakamoto", "address"=>"msksQAKTiCFYC8MH7CqoKpH1FfvBGuAbeU", "category"=>"receive", "amount"=>0.1, "label"=>"SatoshiNakamoto", "vout"=>0}] 165 | 166 | ``` 167 | 168 | ### トランザクションの一覧 169 | 170 | ``` 171 | $ bitcoin-cli listtransactions 172 | ``` 173 | 174 | Ruby版の実行例 175 | 176 | ```ruby 177 | tx_list = bitcoinRPC('listtransactions',[]) 178 | => [{"account"=>"SatoshiNakamoto", "address"=>"msksQAKTiCFYC8MH7CqoKpH1FfvBGuAbeU", "category"=>"receive", "amount"=>0.1, "label"=>"SatoshiNakamoto", "vout"=>0, "confirmations"=>59, "blockhash"=>"0000000001ea44d8f4d84f43d75772d74ef170776f9f75ef221d44b24904707c", 179 | ... 180 | no", "abandoned"=>false}] 181 | 182 | ``` 183 | 184 | ### ビットコインによる支払い請求の作成 185 | 186 | ``` 187 | ビットコインURN = "bitcoin:" ビットコインアドレス [ "?" bitcoinparams ] 188 | ビットコインアドレス = *base58 189 | bitcoinparams = amountparam [ "&" bitcoinparams ] 190 | bitcoinparam = [ 送金金額 / ラベル / メッセージ / 追加パラメータ / reqparam ] 191 | 送金金額 = "amount=" *数値 [ "." *数値 ] 192 | ラベル = "label=" *qchar 193 | メッセージ = "message=" *qchar 194 | 追加パラメータ = qchar *qchar [ "=" *qchar ] 195 | reqparam = "req-" qchar *qchar [ "=" *qchar ] 196 | ``` 197 | 198 | 199 | ```ruby 200 | require 'uri' 201 | label = URI.encode("サトシ書店") 202 | => "%E3%82%B5%E3%83%88%E3%82%B7%E6%9B%B8%E5%BA%97" 203 | message = URI.encode("書籍代") 204 | => "%E6%9B%B8%E7%B1%8D%E4%BB%A3" 205 | ``` 206 | 207 | ``` 208 | bitcoin:1kQiJ5E27JKPXzSpUVaWeXgnGXLRmfiVx?amount=0.02&label=%E3%82%B5%E3%83%88%E3%82%B7%E6%9B%B8%E5%BA%97&message=%E6%9B%B8%E7%B1%8D%E4%BB%A3 209 | ``` 210 | 211 | ``` 212 | # rqrcode_pngライブラリのインストール 213 | $ gem install rqrcode_png 214 | ``` 215 | 216 | ```ruby 217 | require 'rqrcode' 218 | require 'rqrcode_png' 219 | 220 | uri='bitcoin:1kQiJ5E27JKPXzSpUVaWeXgnGXLRmfiVx?amount=0.02&label=%E3%82%B5%E3%83%88%E3%82%B7%E6%9B%B8%E5%BA%97&message=%E6%9B%B8%E7%B1%8D%E4%BB%A3' 221 | 222 | qr = RQRCode::QRCode.new(uri) # QRコードの生成 223 | png = qr.to_img # pngデータへの変換 224 | png.resize(300, 300).save("bill.png") # サイズを300*300 に拡大して保存 225 | ``` 226 | 227 | 228 | ## ビットコインワレットによる暗号鍵管理 229 | 230 | 231 | ### ルートシードの生成 232 | 233 | ```ruby 234 | require 'securerandom' 235 | # ルートシードの生成 236 | root_seed=SecureRandom.hex(32) 237 | ``` 238 | 239 | ### マスター鍵の生成 240 | 241 | ```ruby 242 | # マスターキーの生成 243 | master_key = Bitcoin::ExtKey.generate_master(root_seed.htb) 244 | # マスター秘密鍵 245 | m = master_key.priv 246 | # マスター公開鍵 247 | M = master_key.pub 248 | # マスターチェインコード 249 | master_key.chain_code 250 | #派生階層 251 | master_key.depth 252 | => 0 253 | ``` 254 | ### 上位階層の鍵導出 255 | 256 | #### 第2階層 m/44' の強化導出 257 | 258 | ```ruby 259 | # 44' の鍵を強化導出 260 | key44 = master_key.derive(2**31+44) 261 | # 鍵の派生階層 262 | key44.depth 263 | => 1 264 | ``` 265 | 266 | #### 第3階層 m/44'/0' の強化導出 267 | 268 | ```ruby 269 | # インデックス0'強化導出するので、2**31+0 270 | key440=key44.derive(2**31+0) 271 | # 鍵の派生階層 272 | key440.depth 273 | => 2 274 | ``` 275 | 276 | #### 第4階層のアカウントの強化導出 277 | 278 | ```ruby 279 | # インデックス0'強化導出するので、2**31+0 280 | key4400=key440.derive(2**31+0) 281 | # 鍵の派生階層 282 | key4400.depth 283 | => 3 284 | # インデックス0'強化導出するので、2**31+1 285 | key4401=key440.derive(2**31+1) 286 | # 鍵の派生階層 287 | key4401.depth 288 | => 3 289 | 290 | # 拡張秘密鍵のシリアライズ 291 | key4400.to_base58 292 | => "xprv9z6uo3ZoHrWf6u5SvbZU4LGAmUHwrCT2mhXR46Xrr8naiimGUPM3g4dh78EMhhXMDfb3fA1Nav6SeJjtoaGouo1ktTHx1BYwidoAJx9eGgz" 293 | 294 | # 拡張公開鍵のシリアライズ 295 | key4401.ext_pubkey.to_base58 296 | => "xpub6D6GCZ6h8E4xNAHKKBQdeJARQgXoyjtoYF3VfsDGoAERTbWBGBunTVnrLXRaWhR7kuVvh9qb1F4zp58P6Gqv5w9DkammPZCU93VWoKWY1ER" 297 | 298 | # 拡張秘密鍵のインポート 299 | ext_privkey = Bitcoin::ExtKey.from_base58("xprv9z6uo3ZoHrWf6u5SvbZU4LGAmUHwrCT2mhXR46Xrr8naiimGUPM3g4dh78EMhhXMDfb3fA1Nav6SeJjtoaGouo1ktTHx1BYwidoAJx9eGgz") 300 | 301 | # 拡張公開鍵のインポート 302 | ext_pubkey = Bitcoin::ExtPubkey.from_base58("xpub6D6GCZ6h8E4xNAHKKBQdeJARQgXoyjtoYF3VfsDGoAERTbWBGBunTVnrLXRaWhR7kuVvh9qb1F4zp58P6Gqv5w9DkammPZCU93VWoKWY1ER") 303 | ``` 304 | 305 | #### 第5階層、第6階層の導出 306 | 307 | ```ruby 308 | # このアカウントのエクスターナルチェーン 309 | key44000 = key4400.derive(0) 310 | # 鍵の派生階層 311 | key44000.depth 312 | => 4 313 | 314 | # このアカウントのエクスターナルチェーンの外部公開用公開鍵 315 | key440000 = key44000.derive(0) 316 | # 鍵の派生階層 317 | key440000.depth 318 | => 5 319 | # このアカウントのエクスターナルチェーンの外部公開アドレス 320 | key440000.addr 321 | => "163fo3ebG6yo7bCTV7zJiyfWLbfZtaA9Ku" 322 | ``` 323 | 324 | -------------------------------------------------------------------------------- /Chapter05.md: -------------------------------------------------------------------------------- 1 | # ブロックチェーン 2 | 3 | ### ブロックの識別子 4 | 5 | ```json 6 | $ bitcoin-cli getchaintips 7 | [ 8 | { 9 | "height": 467961, 10 | "hash": "0000000000000000005fb29471da62846b06fcc982cdd21ac8ae7fb235b691ac", 11 | "branchlen": 53, 12 | "status": "headers-only" 13 | }, 14 | { 15 | "height": 467908, 16 | "hash": "000000000000000001808e86f1c9af5ab8d4903e4ddc6424f1bff197b7520d0f", 17 | "branchlen": 0, 18 | "status": "active" 19 | } 20 | ] 21 | ``` 22 | 23 | 24 | ``` 25 | $ bitcoin-cli getblockhash 467908 26 | 000000000000000001808e86f1c9af5ab8d4903e4ddc6424f1bff197b7520d0f 27 | ``` 28 | 29 | ### ブロックの本体を得る 30 | 31 | ```json 32 | $ bitcoin-cli getblock 000000000004fc60b9bd5906556ea30dde31bdd1ffb3e8e40bcfc9896004faa9 33 | { 34 | "hash": "000000000004fc60b9bd5906556ea30dde31bdd1ffb3e8e40bcfc9896004faa9", 35 | "confirmations": 114533, 36 | "strippedsize": 18585, 37 | "size": 18585, 38 | "weight": 74340, 39 | "height": 1087530, 40 | "version": 536870912, 41 | "versionHex": "20000000", 42 | "merkleroot": "b3fcd1d3666ee3a62618aae79b93eae4e14638cc98e8e2c9f10edbceecb4494b", 43 | "tx": [ 44 | "9556f2d49f797252e16ad03fe649ba0e3c70a2745e2e3f8eecd13244d94fb207", 45 | "803b91f42e7bfd4bc2097cebc3854a37844b5f47abb3be307d4f877cb059dfd0", 46 | "3b14d39ca1e8c2a70f0a423e833bd454eaee31ea4f5609c57a4d605244946ca3", 47 | "bad7d593c408eae7d5b50c40a1353ca1b2db80fd4bf9b73ae712a2bf9b1d44d3", 48 | "d451b694a856265faaee7d3d48118c4e57c68c362f5fed161cacb9358bfa65f8", 49 | "9de7ee1db2f268819666dd32d9373bc637370bf0ce85a63dafe39e627b0b6cdf", 50 | "a5b002db81f1c09eab197cb2cb1d1cecf193ac6677fa78f26dadf54e09a7042f", 51 | "c3bafa0c5cea0a641ca278db832eef30fec16f69cfe3c2dce958f242409c014a", 52 | "bedae8f51726e7ed59bf8bae7a4f257edaeb64d794199834180eca85c8ca026c", 53 | "de2c06f4cb60950d5b72ec37a31e6d6d47852c53abd05ebe6cf090de2db814ec", 54 | "7865c689b63531eaf4eca9aa6f8501a05de8435207d829d3c99da84430a1df18", 55 | "6c556d773ada9b6c1cf8b00d7e60fdd30df3882340e277f2d9e553f03bc108e5", 56 | "cb5ce4fa100c5483082672009a919237172068cc2d0c7e8b585f7eeb5d836183", 57 | "3f2a45c80d8cf24fea9fb7a6a6bad7f848676690bf7982ff98c1884adf532382", 58 | "6ef2e72d36f7927d93ed2f880df4469ef9ed745e60b3ddfcb2e991ff04e20c73", 59 | "5235324c70538022b8d9f7f3a51647cad83ba404c46830ac95b22a425c08ecf8", 60 | "90bafb624a0dace70c99063b27f75efc2b867d0a4148f63f2dd7c44e50dbf664", 61 | "8884dbd545e8998594d1f143b298ea1234e9c895897dd6ab119c9866ceeda1f4", 62 | "7e40c1fc794028cacaffe9245991a31ebb2328a051f68a1108f0e6a7d1b7e797", 63 | "40820e1039b84c3901956c3b6060bc18430b10fd4c2f414fc5d8e31a35c81e4a", 64 | "569772729262d50a2f14836560f46b141618d7e87a89b8434800e687fadb371d", 65 | "1fd7c930c23d53661d92b0b86754ade22ad34a499965a5570d1f48a607766c47", 66 | "5c398761b779790ef3d49354a3b04ebf9645a765b1e496f87f51d02389d27c99", 67 | "c4c3928a402147b27da4bba8a860681e8d322530c2ce182e7c2d87af055a3765", 68 | "a0b78858f9303532495cb00579d87284a24b8cd1864e1650ecee0eaa64559a11", 69 | "f9c4e5a83dd92c6a70c7a7abbc281b05c90488170827ba66eeebc08c9fe44cb6", 70 | "75131cd6be0f350c5d312a40cc687e20473d6bfa0bf098fa82fd29d2a296a547", 71 | "3e2b5a8e4a59dbe4cc7f04ecc47dc2346733c205c1f9d448b84b94c925f592ad", 72 | "9b3aa88a4ae994f51cbff9ca07e0c4e854719ec697e54266a72693e2da86a260", 73 | "346cca552be9b292e8dc01a4322d8596c943f94b60d49320ff97349ab3328c06", 74 | "6f5ea126e5a633d6ce0c719f067151030f185c6432cf9e9b5f778a410c4c7211", 75 | "3efd6b1c64e9887df87b4f130b38630c96d341e857f5646e7ab81fd3a4eb6703", 76 | "1fb4925268e86e1c20894f920d0cb23c2685dff1e7792c80d8290e3f36d4008f", 77 | "5d10bfb32188b75bedd47a23690cd1ffdacf900051a6c24995303106ae5f3c87", 78 | "34c8300ea04b06cc07737f1f3a67aa7854fcb275b725683f2feb69c56241e55f", 79 | "8295be39b6c38e734e92d58c85100d238f956370da2ebf0d9b91fb7e3da1b0a6", 80 | "b893468e8482ebae38f2b4c26ec7c08420b66619a33852be4dd9eecf1971367c", 81 | "d84ad309b6d4b1252c5c13e1bb890e0baa25405871222e28b888ca0e069e51bf", 82 | "1aebcb5f98f586619afbcec21012565a9ae4e6770c0bab95e3c1d407fb98977f", 83 | "6ea261976c63ccfaa57f2e74df5358ae0888758fdc67ff1d3c0f9da4bc84f32c", 84 | "9636511008f069630cd731920deabfc20ff07f7c0e557f1f5b3a7fceda3569a9", 85 | "46bab973bf381dc7c4cd70fd669c11e36d93ed42a4c22399849d476a93067a34", 86 | "09bef05054c8f01c3b48561780022a3b2c0bd59b7776e9eb2e33ced7a9992cfa", 87 | "54e2280cec37e4827e0d178d9bc09a7e28550e0cfbd8b8e67c11776ffac8f2a3", 88 | "3205375e00315850da938e2aaa6144d098441aa9b67b7ae3457994cfa167eb56", 89 | "1982cd168706f105f17bda981cf386b0482fbeac26b9a0068fb365aed1b895c1", 90 | "98050e96588d68e770e6008a0af0944781cc4c1eadca5d6637aba60c8a1136f8", 91 | "ba4e7e22c85b0bc6391cf1bfc65a066b656797e197bef37feb47848d2c50c029", 92 | "4a3b38c56b52c2de9b538839eba51bafdaa338c9b6b8674345a685d7b3c63100", 93 | "8abf83cc6d3fd14e7ac55f0959e224b6a5fe4a776ff9f4f7d0233604f690b600", 94 | "b745fb1ea8bdfe6f36593e54f6dda311d2f991de99619ba6c7a0efc101d51d1a", 95 | "a1c8cc9684a9f7f212cca19bbf0da2fcabaf21622ff63bd0a0828c498dc935bd", 96 | "a50f2c3fb6a197772a3f7d46be019fcf025a07e573d424e725f62fdd66fdebcf" 97 | ], 98 | "time": 1486101281, 99 | "mediantime": 1486097753, 100 | "nonce": 4109938712, 101 | "bits": "1d00ffff", 102 | "difficulty": 1, 103 | "chainwork": "00000000000000000000000000000000000000000000001e9b25b784d24108ce", 104 | "previousblockhash": "00000000000005d6953af533ab31befe6006e27156ce7257337abfac24037d6b", 105 | "nextblockhash": "00000000000003396748a21cffdad3733d4f71faa3bb022f242f71d401d177f0" 106 | } 107 | ``` 108 | 109 | ### ブロックヘッダの構造 110 | 111 | 112 | ```json 113 | $ bitcoin-cli getblockheader 000000000004fc60b9bd5906556ea30dde31bdd1ffb3e8e40bcfc9896004faa9 114 | { 115 | "hash": "000000000004fc60b9bd5906556ea30dde31bdd1ffb3e8e40bcfc9896004faa9", 116 | "confirmations": 114531, 117 | "height": 1087530, 118 | "version": 536870912, 119 | "versionHex": "20000000", 120 | "merkleroot": "b3fcd1d3666ee3a62618aae79b93eae4e14638cc98e8e2c9f10edbceecb4494b", 121 | "time": 1486101281, 122 | "mediantime": 1486097753, 123 | "nonce": 4109938712, 124 | "bits": "1d00ffff", 125 | "difficulty": 1, 126 | "chainwork": "00000000000000000000000000000000000000000000001e9b25b784d24108ce", 127 | "previousblockhash": "00000000000005d6953af533ab31befe6006e27156ce7257337abfac24037d6b", 128 | "nextblockhash": "00000000000003396748a21cffdad3733d4f71faa3bb022f242f71d401d177f0" 129 | } 130 | 131 | $ bitcoin-cli getblockheader 000000000004fc60b9bd5906556ea30dde31bdd1ffb3e8e40bcfc9896004faa9 false 132 | 000000206b7d0324acbf7a335772ce5671e20660febe31ab33f53a95d6050000000000004b49b4eccedb0ef1c9e2e898cc3846e1e4ea939be7aa1826a6e36e66d3d1fcb3211b9458ffff001d18b0f8f4 133 | 134 | ``` 135 | 136 | #### ブロックハッシュの求め方 137 | 138 | ```ruby 139 | require 'digest' 140 | 141 | # Internal Byte Order によるSHA256のダブルハッシュ 142 | def dh(data) 143 | Digest::SHA256.digest(Digest::SHA256.digest([data].pack("H*"))).unpack("h*")[0].reverse 144 | end 145 | ``` 146 | 147 | 148 | ```ruby 149 | block_header="000000206b7d0324acbf7a335772ce5671e20660febe31ab33f53a95d6050000000000004b49b4eccedb0ef1c9e2e898cc3846e1e4ea939be7aa1826a6e36e66d3d1fcb3211b9458ffff001d18b0f8f4" 150 | 151 | dh(block_header) 152 | => "000000000004fc60b9bd5906556ea30dde31bdd1ffb3e8e40bcfc9896004faa9" 153 | ``` 154 | 155 | ### マークル木 156 | 157 | ```ruby 158 | require 'digest' 159 | 160 | # ビットコインでは、バイト単位で反転し、さらに逆順にするSHA-256のダブルハッシュを利用しています 161 | def dhash(data) 162 | Digest::SHA256.digest(Digest::SHA256.digest([data].pack("H*").reverse )).reverse.unpack("H*")[0] 163 | end 164 | 165 | # マークルルートの作成(入力は、ハッシュ値のリスト) 166 | def merkle_root(nodes) 167 | if nodes.size==1 then nodes[0] # ノード数が一つになったらマークルルート 168 | else 169 | merkle_root(nodes.each_slice(2).map{|x| 170 | if x.size==2 then dhash(x[1]+x[0]) # ノードを2つずつ連接してハッシュ値をとる 171 | else dhash(x[0]+x[0]) # 一つ残ったらコピーして2つにしてハッシュ値をとる 172 | end}) 173 | end 174 | end 175 | 176 | TXID_LIST = [ 177 | "9556f2d49f797252e16ad03fe649ba0e3c70a2745e2e3f8eecd13244d94fb207", 178 | "803b91f42e7bfd4bc2097cebc3854a37844b5f47abb3be307d4f877cb059dfd0", 179 | "3b14d39ca1e8c2a70f0a423e833bd454eaee31ea4f5609c57a4d605244946ca3", 180 | "bad7d593c408eae7d5b50c40a1353ca1b2db80fd4bf9b73ae712a2bf9b1d44d3", 181 | "d451b694a856265faaee7d3d48118c4e57c68c362f5fed161cacb9358bfa65f8", 182 | "9de7ee1db2f268819666dd32d9373bc637370bf0ce85a63dafe39e627b0b6cdf", 183 | "a5b002db81f1c09eab197cb2cb1d1cecf193ac6677fa78f26dadf54e09a7042f", 184 | "c3bafa0c5cea0a641ca278db832eef30fec16f69cfe3c2dce958f242409c014a", 185 | "bedae8f51726e7ed59bf8bae7a4f257edaeb64d794199834180eca85c8ca026c", 186 | "de2c06f4cb60950d5b72ec37a31e6d6d47852c53abd05ebe6cf090de2db814ec", 187 | "7865c689b63531eaf4eca9aa6f8501a05de8435207d829d3c99da84430a1df18", 188 | "6c556d773ada9b6c1cf8b00d7e60fdd30df3882340e277f2d9e553f03bc108e5", 189 | "cb5ce4fa100c5483082672009a919237172068cc2d0c7e8b585f7eeb5d836183", 190 | "3f2a45c80d8cf24fea9fb7a6a6bad7f848676690bf7982ff98c1884adf532382", 191 | "6ef2e72d36f7927d93ed2f880df4469ef9ed745e60b3ddfcb2e991ff04e20c73", 192 | "5235324c70538022b8d9f7f3a51647cad83ba404c46830ac95b22a425c08ecf8", 193 | "90bafb624a0dace70c99063b27f75efc2b867d0a4148f63f2dd7c44e50dbf664", 194 | "8884dbd545e8998594d1f143b298ea1234e9c895897dd6ab119c9866ceeda1f4", 195 | "7e40c1fc794028cacaffe9245991a31ebb2328a051f68a1108f0e6a7d1b7e797", 196 | "40820e1039b84c3901956c3b6060bc18430b10fd4c2f414fc5d8e31a35c81e4a", 197 | "569772729262d50a2f14836560f46b141618d7e87a89b8434800e687fadb371d", 198 | "1fd7c930c23d53661d92b0b86754ade22ad34a499965a5570d1f48a607766c47", 199 | "5c398761b779790ef3d49354a3b04ebf9645a765b1e496f87f51d02389d27c99", 200 | "c4c3928a402147b27da4bba8a860681e8d322530c2ce182e7c2d87af055a3765", 201 | "a0b78858f9303532495cb00579d87284a24b8cd1864e1650ecee0eaa64559a11", 202 | "f9c4e5a83dd92c6a70c7a7abbc281b05c90488170827ba66eeebc08c9fe44cb6", 203 | "75131cd6be0f350c5d312a40cc687e20473d6bfa0bf098fa82fd29d2a296a547", 204 | "3e2b5a8e4a59dbe4cc7f04ecc47dc2346733c205c1f9d448b84b94c925f592ad", 205 | "9b3aa88a4ae994f51cbff9ca07e0c4e854719ec697e54266a72693e2da86a260", 206 | "346cca552be9b292e8dc01a4322d8596c943f94b60d49320ff97349ab3328c06", 207 | "6f5ea126e5a633d6ce0c719f067151030f185c6432cf9e9b5f778a410c4c7211", 208 | "3efd6b1c64e9887df87b4f130b38630c96d341e857f5646e7ab81fd3a4eb6703", 209 | "1fb4925268e86e1c20894f920d0cb23c2685dff1e7792c80d8290e3f36d4008f", 210 | "5d10bfb32188b75bedd47a23690cd1ffdacf900051a6c24995303106ae5f3c87", 211 | "34c8300ea04b06cc07737f1f3a67aa7854fcb275b725683f2feb69c56241e55f", 212 | "8295be39b6c38e734e92d58c85100d238f956370da2ebf0d9b91fb7e3da1b0a6", 213 | "b893468e8482ebae38f2b4c26ec7c08420b66619a33852be4dd9eecf1971367c", 214 | "d84ad309b6d4b1252c5c13e1bb890e0baa25405871222e28b888ca0e069e51bf", 215 | "1aebcb5f98f586619afbcec21012565a9ae4e6770c0bab95e3c1d407fb98977f", 216 | "6ea261976c63ccfaa57f2e74df5358ae0888758fdc67ff1d3c0f9da4bc84f32c", 217 | "9636511008f069630cd731920deabfc20ff07f7c0e557f1f5b3a7fceda3569a9", 218 | "46bab973bf381dc7c4cd70fd669c11e36d93ed42a4c22399849d476a93067a34", 219 | "09bef05054c8f01c3b48561780022a3b2c0bd59b7776e9eb2e33ced7a9992cfa", 220 | "54e2280cec37e4827e0d178d9bc09a7e28550e0cfbd8b8e67c11776ffac8f2a3", 221 | "3205375e00315850da938e2aaa6144d098441aa9b67b7ae3457994cfa167eb56", 222 | "1982cd168706f105f17bda981cf386b0482fbeac26b9a0068fb365aed1b895c1", 223 | "98050e96588d68e770e6008a0af0944781cc4c1eadca5d6637aba60c8a1136f8", 224 | "ba4e7e22c85b0bc6391cf1bfc65a066b656797e197bef37feb47848d2c50c029", 225 | "4a3b38c56b52c2de9b538839eba51bafdaa338c9b6b8674345a685d7b3c63100", 226 | "8abf83cc6d3fd14e7ac55f0959e224b6a5fe4a776ff9f4f7d0233604f690b600", 227 | "b745fb1ea8bdfe6f36593e54f6dda311d2f991de99619ba6c7a0efc101d51d1a", 228 | "a1c8cc9684a9f7f212cca19bbf0da2fcabaf21622ff63bd0a0828c498dc935bd", 229 | "a50f2c3fb6a197772a3f7d46be019fcf025a07e573d424e725f62fdd66fdebcf"] 230 | 231 | mRoot = merkle_root(TXID_LIST) 232 | => "b3fcd1d3666ee3a62618aae79b93eae4e14638cc98e8e2c9f10edbceecb4494b" 233 | ``` 234 | 235 | この計算結果は、上記のブロックの内容のマークルルートに一致します。 236 | 237 | ``` 238 | "merkleroot": "b3fcd1d3666ee3a62618aae79b93eae4e14638cc98e8e2c9f10edbceecb4494b", 239 | ``` 240 | 241 | ## マイニング 242 | 243 | ### マイニング処理 244 | 245 | ```ruby 246 | # 80バイトのブロックヘッダ 247 | blockheader="000000203876761537d4fb9b27a15382a21cd5d36c538d37ce632b000000000000000000c2d022b34fddf98bb3cafd834a4bd7a0264f14d30bf3d9852580fc29794677746d91255954d80118605d1964" 248 | 249 | # ブロックヘッダのハッシュ値を得る 250 | dhash(blockheader) 251 | => "000000000000000001808e86f1c9af5ab8d4903e4ddc6424f1bff197b7520d0f" 252 | 253 | # ブロックヘッダーのnonce フィールドの値のInternal Byte Orderを整数にする 254 | noce=[blockheader[-8..-1]].pack('H*').unpack('h*')[0].reverse.to_i(16) 255 | => 1679383904 256 | ``` 257 | 258 | #### コインベーストランザクションを得る 259 | 260 | ```json 261 | $ bitcoin-cli getrawtransaction 169f6278f5dccc673a7c9b7f2432f22dc6cf14b2a5f23c9c209b3ebd6f9c0b7b 262 | 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1c03c42307046b9125592f426978696e2f0832b7658b20804200000000ffffffff01cfa2eb5a000000001976a914cef3550ff9e637ddd120717d43fc21f8a563caf888ac00000000 263 | ``` 264 | 265 | 266 | ```json 267 | $ bitcoin-cli decoderawtransaction 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1c03c42307046b9125592f426978696e2f0832b7658b20804200000000ffffffff01cfa2eb5a000000001976a914cef3550ff9e637ddd120717d43fc21f8a563caf888ac00000000 268 | { 269 | "txid": "169f6278f5dccc673a7c9b7f2432f22dc6cf14b2a5f23c9c209b3ebd6f9c0b7b", 270 | "hash": "169f6278f5dccc673a7c9b7f2432f22dc6cf14b2a5f23c9c209b3ebd6f9c0b7b", 271 | "size": 113, 272 | "vsize": 113, 273 | "version": 1, 274 | "locktime": 0, 275 | "vin": [ 276 | { 277 | "coinbase": "03c42307046b9125592f426978696e2f0832b7658b20804200000000", 278 | "sequence": 4294967295 279 | } 280 | ], 281 | "vout": [ 282 | { 283 | "value": 15.25392079, 284 | "n": 0, 285 | "scriptPubKey": { 286 | "asm": "OP_DUP OP_HASH160 cef3550ff9e637ddd120717d43fc21f8a563caf8 OP_EQUALVERIFY OP_CHECKSIG", 287 | "hex": "76a914cef3550ff9e637ddd120717d43fc21f8a563caf888ac", 288 | "reqSigs": 1, 289 | "type": "pubkeyhash", 290 | "addresses": [ 291 | "1KsFhYKLs8qb1GHqrPxHoywNQpet2CtP9t" 292 | ] 293 | } 294 | } 295 | ] 296 | } 297 | ``` 298 | 299 | 300 | ```ruby 301 | coinbase="03c42307046b9125592f426978696e2f0832b7658b20804200000000" 302 | 303 | coinbase[2..8] 304 | => "c423070" 305 | ["c42307"].pack('h*').unpack('H*')[0].reverse.to_i(16) 306 | => 467908 307 | ``` 308 | 309 | ``` 310 | coinbase[9..10] 311 | => "04" 312 | 313 | coinbase[10..17] 314 | => "6b912559" 315 | ``` 316 | 317 | ### マイニングの難易度 318 | 319 | 320 | #### difficultyの表現 321 | 322 | 323 | ```ruby 324 | blockheader="000000203876761537d4fb9b27a15382a21cd5d36c538d37ce632b000000000000000000c2d022b34fddf98bb3cafd834a4bd7a0264f14d30bf3d9852580fc29794677746d91255954d80118605d1964" 325 | 326 | difficulty_target=[blockheader[-16..-9]].pack('H*').unpack('h*')[0].reverse 327 | => "1801d854" 328 | ``` 329 | 330 | $ terget = 係数部 * 2**(8*(指数部 - 3)) $ 331 | 332 | 333 | ```ruby 334 | def terget(difficulty_target) 335 | exponent=difficulty_target[0..1].hex 336 | coefficient=difficulty_target[2..7].hex 337 | (coefficient*2**(8*(exponent-3))) 338 | end 339 | 340 | terget(difficulty_target) 341 | => 45240046586752885057924289339576851866807485277820420096 342 | ``` 343 | 344 | 345 | ```ruby 346 | require 'digest' 347 | # SHA-256 ダブルハッシュ 348 | def dhash(data) 349 | Digest::SHA256.digest(Digest::SHA256.digest([data].pack("H*"))).unpack("h*")[0].reverse 350 | end 351 | 352 | # ブロックヘッダのダブルハッシュと難易度ターゲットの比較 353 | dhash(blockheader).hex < terget(difficulty_target) 354 | => true 355 | ``` 356 | 357 | #### getblocktemplate API 358 | 359 | ```json 360 | $ bitcoin-cli getblocktemplate '{"jsonrpc": "1.0", "id":"curltest", "method": "getblocktemplate", "params": [] }' 361 | 362 | { 363 | "capabilities": [ 364 | "proposal" 365 | ], 366 | "version": 536870914, 367 | "rules": [ 368 | "csv" 369 | ], 370 | "vbavailable": { 371 | "segwit": 1 372 | }, 373 | "vbrequired": 0, 374 | "previousblockhash": "000000000000000001495cb64f4264baf2b8e6dd189c73c7b01ee083e16ea694", 375 | "transactions": [ 376 | { 377 | "data": "010000000106c8e8bd303fec27356c0a04a4e91f71c6307dd2fdd80ad72524055664618b67010000006b4830450221008fb9cd0c2e7dfd762453f58a84a19facb44a6b8bee83bdd047ed1b83e8e8421502205c3aa141f900e2d634bbe6ec22def3873bbb554dbc1cddbd4d4db6a616aa907b01210377ac3fe26ce1325f804bae131ce1652a14df96c3227735d5aadddefc663a41f7ffffffff01c26ed71b0000000017a91481f5c7f34ff0b8c19eb33cb3a69feb1b3694a2968700000000", 378 | 379 | ... 380 | 381 | ], 382 | "noncerange": "00000000ffffffff", 383 | "sigoplimit": 20000, 384 | "sizelimit": 1000000, 385 | "curtime": 1496235485, 386 | "bits": "1801d854", 387 | "height": 469048 388 | } 389 | ``` 390 | 391 | 392 | ## ブロックチェーンからの情報収集 393 | 394 | ### mainnet における仮想通貨の流通経路の追跡 395 | 396 | bitcoin.conf 397 | 398 | ``` 399 | mainnet=1 400 | server=1 401 | rest=1 402 | txindex=1 403 | rpcuser=<ユーザ名> 404 | rpcpassword=<パスワード> 405 | rpcport=8332 406 | ``` 407 | 408 | 追跡プログラム 409 | 410 | ```ruby 411 | require 'bitcoin' 412 | require 'net/http' 413 | require 'json' 414 | RPCUSER = <ユーザ名>  # JSON RPC のためのユーザ名 415 | RPCPASSWORD = <パスワード> # JSON RPC のためのパスワード 416 | HOST="localhost" # JSON RPC のhost 417 | PORT=8332 # ポート番号 418 | 419 | #bitcoindへのHTTPアクセスするメソッド 420 | def bitcoinRPC(method,param) 421 | http = Net::HTTP.new(HOST, PORT) 422 | request = Net::HTTP::Post.new('/') 423 | request.basic_auth(RPCUSER,RPCPASSWORD) 424 | request.content_type = 'application/json' 425 | request.body = {method: method, params: param, id: 'jsonrpc'}.to_json 426 | JSON.parse(http.request(request).body)["result"] 427 | end 428 | ``` 429 | 430 | ```ruby 431 | require 'bitcoin' 432 | 433 | # 最初のトランザクションID 434 | txid="ab.....................ec8c50" 435 | # そのトランザクションの16進データを得る 436 | rtx = bitcoinRPC('getrawtransaction',[txid]) 437 | # そのトランザクションの構造を得る(ハッシュ) 438 | tx=bitcoinRPC('decoderawtransaction',[rtx]) 439 | tx.keys 440 | => ["txid", "hash", "size", "vsize", "version", "locktime", "vin", "vout"] 441 | # このトランザクションのインプットのサイズを見る 442 | tx["vin"].size 443 | => 1 444 | tx["vin"][0].keys 445 | => ["txid", "vout", "scriptSig", "sequence"] 446 | # このインプットに送金したトランザクションIDとアウトプットの順序を求める 447 | ptxid=tx["vin"][0]["txid"] 448 | pout=tx["vin"][0]["vout"] 449 | # インプットに送金したトランザクションの16進形式 450 | prtx = bitcoinRPC('getrawtransaction',[ptxid]) 451 | # インプットに送金したトランザクションの解析結果 452 | ptx=bitcoinRPC('decoderawtransaction',[prtx]) 453 | # インプットに送金したトランザクションから見た送金先アドレス 454 | ptx["vout"][pout]["scriptPubKey"]["addresses"][0] 455 | # 最初のトランザクションの送信元のビットコインアドレス 456 | => "1L.....................Q7LVr" 457 | ``` 458 | 459 | ```ruby 460 | def sender_address(txid) 461 | rtx=bitcoinRPC('getrawtransaction',[txid]) 462 | tx=bitcoinRPC('decoderawtransaction',[rtx]) 463 | tx["vin"].map{|x| 464 | prtx=bitcoinRPC('getrawtransaction',[x["txid"]]) 465 | pout=x["vout"] 466 | ptx=bitcoinRPC('decoderawtransaction',[prtx]) 467 | ptx["vout"][pout]["scriptPubKey"]["addresses"][0]}.uniq 468 | end 469 | 470 | sender_address("f5.....................085").uniq 471 | => ["3A.....................4AG", "3J.....................ZAN"] 472 | 473 | ``` 474 | 475 | ```ruby 476 | def prev_txids(txid) 477 | rtx=bitcoinRPC('getrawtransaction',[txid]) 478 | tx=bitcoinRPC('decoderawtransaction',[rtx]) 479 | tx["vin"].map{|x|x["txid"]} 480 | end 481 | 482 | # 直前のトランザクションIDの一覧 483 | prev_txids("25.....................9b1a") 484 | => ["7c.....................f3a6", "94.....................8353", "ad.....................0c99", "71.....................839c", "93.....................07b3"] 485 | 486 | # コインベーストランザクションの場合 487 | prev_txids("24.....................07d8") 488 | => [nil] 489 | ``` 490 | 491 | ```ruby 492 | def trace_tx(txid,depth) 493 | if txid==nil or depth==0 then 494 | else [prev_txids(txid).map{|x|trace_tx(x,depth-1)},txid] 495 | end 496 | end 497 | ``` 498 | 499 | ### 仮想通貨の流通速度の測定 500 | 501 | ```ruby 502 | blkid=bitcoinRPC('getblockhash',[469060]) 503 | blk=bitcoinRPC('getblock',[blkid]) 504 | blk["tx"].size 505 | => 1734 506 | rtx=bitcoinRPC('getrawtransaction',[blk["tx"][1]]) 507 | tx=bitcoinRPC('decoderawtransaction',[rtx]) 508 | tx["vout"][0]["value"] 509 | => 0.075 510 | ``` 511 | 512 | ```ruby 513 | def circulation(height) 514 | blkid=bitcoinRPC('getblockhash',[height]) 515 | blk=bitcoinRPC('getblock',[blkid]) 516 | blk["tx"].map{|txid| 517 | rtx=bitcoinRPC('getrawtransaction',[txid]) 518 | tx=bitcoinRPC('decoderawtransaction',[rtx]) 519 | tx["vout"].map{|x|x["value"]}.reduce(:+)}.reduce(:+) 520 | end 521 | 522 | circulation(469060) 523 | => 10913.489625569999 524 | ``` 525 | 526 | 527 | -------------------------------------------------------------------------------- /Chapter06.md: -------------------------------------------------------------------------------- 1 | # トランザクション 2 | 3 | ## 標準的なトランザクションの構成方法 4 | 5 | ### トランザクションの具体例 6 | 7 | ``` 8 | $ bitcoin-cli decoderawtransaction 0100000001f11b0fdc283bba47b57a0eff0846848c5578b732909ddd5d2edd2d8537eabac0000000006a4730440220246ca676c6878cd55609751625904b1d14189909048f1957d5277d2ecd8fd726022034dad9a880e47c1f475df535b3ba1fa4867f8037ff9981bd5361c19436fa8e54012102321bc438a8f9d1aa69f1c730d9c84e0e85c2bddd0df4924ae851f082b205d646ffffffff01b01df505000000001976a914be7966685d48ebbc07c7104b16528455c3b157a888ac00000000 9 | ``` 10 | 11 | ### 可変長整数 12 | ``` 13 | $ irb 14 | >> require 'bitcoin' 15 | >> Bitcoin::Protocol.pack_var_int(252) 16 | >> Bitcoin::Protocol.pack_var_int(253) 17 | >> Bitcoin::Protocol.pack_var_int(65535) 18 | >> Bitcoin::Protocol.pack_var_int(65536) 19 | ``` 20 | 21 | 22 | ## スクリプト言語 23 | 24 | ### スクリプトの作成 25 | 26 | ``` 27 | $ irb 28 | >> require 'bitcoin' 29 | >> Bitcoin::Script.from_string("2 4 OP_ADD 6 OP_EQUAL") #文字列 30 | >> Bitcoin::Script.new("RT\x93V\x87") #16進数 31 | >> Bitcoin::Script.new(["5254935687"].pack("H*")) #バイナリ 32 | ``` 33 | 34 | ### スクリプトの参照 35 | 36 | ``` 37 | $ irb 38 | >> require 'bitcoin' 39 | >> script = Bitcoin::Script.from_string("2 4 OP_ADD 6 OP_EQUAL") 40 | >> script.to_string #文字列 41 | >> script.to_payload #バイナリ 42 | >> script.to_binary #バイナリ 43 | ``` 44 | 45 | ### スクリプトの検証 46 | 47 | ``` 48 | $ irb 49 | >> require 'bitcoin' 50 | >> Bitcoin::Script.from_string("2 4 OP_ADD 6 OP_EQUAL").run 51 | >> Bitcoin::Script.from_string("2 3 OP_ADD 6 OP_EQUAL").run 52 | ``` 53 | 54 | ``` 55 | $ irb 56 | >> require 'bitcoin' 57 | >> script = Bitcoin::Script.from_string("2 4 OP_ADD 6 OP_EQUAL") 58 | >> script.run 59 | >> script.debug 60 | >> script.debug[0] 61 | >> script.debug[1] 62 | >> script.debug[2] 63 | >> script.debug[3] 64 | >> script.debug[4] 65 | >> script.debug[5] 66 | >> script.debug[6] 67 | >> script.debug[7] 68 | >> script.debug[8] 69 | >> script.debug[9] 70 | >> script.debug[10] 71 | >> script.debug[11] 72 | ``` 73 | 74 | 75 | ## トランザクションの作成 76 | 77 | ### Bitcoin CoreのAPIを使用する場合 78 | 79 | #### インプットにセットするUTXOの準備 80 | 81 | ``` 82 | $ bitcoind -regtest -daemon 83 | $ bitcoin-cli generate 101 84 | ``` 85 | 86 | ``` 87 | $ bitcoin-cli listunspent 88 | ``` 89 | 90 | ``` 91 | $ bitcoin-cli getrawtransaction 785149aedc5637c446d40674895f16f98c487be98972c5550a71bd929c4cbd2d 1 92 | ``` 93 | 94 | #### トランザクション全体の作成、インプットの作成、アウトプットの作成 95 | 96 | ``` 97 | $ bitcoin-cli getnewaddress 98 | $ bitcoin-cli createrawtransaction "[{\"txid\":\"785149aedc5637c446d40674895f16f98c487be98972c5550a71bd929c4cbd2d\",\"vout\":0}]" "{\"mndTEiHWjkbQqeTrSNKB4YnvVY5XH1DZvv\":0.999}" 99 | ``` 100 | 101 | ``` 102 | $ bitcoin-cli decoderawtransaction 01000000012dbd4c9c92bd710a55c57289e97b488cf9165f897406d446c43756dcae4951780000000000ffffffff01605af405000000001976a9144e038940f498b0880d259e7e8b2283f645087e8488ac00000000 103 | ``` 104 | 105 | 106 | ### bitcoin-rubyを使用する場合 107 | 108 | #### インプットにセットするUTXOの準備 109 | 110 | ``` 111 | $ irb 112 | >> require 'bitcoin' 113 | >> Bitcoin.network = :testnet3 #testnetの場合 114 | >> Bitcoin.network = :regtest #regtestの場合 115 | ``` 116 | 117 | #### トランザクション全体の作成 118 | 119 | ``` 120 | >> tx = Bitcoin::Protocol::Tx.new 121 | ``` 122 | 123 | #### インプットの作成 124 | 125 | ``` 126 | >> prev_tx_hash = "785149aedc5637c446d40674895f16f98c487be98972c5550a71bd929c4cbd2d" 127 | >> prev_output_index = 0 128 | >> tx_in = Bitcoin::Protocol::TxIn.from_hex_hash(prev_tx_hash, prev_output_index) 129 | >> tx.add_in(tx_in) 130 | ``` 131 | 132 | #### アウトプットの作成 133 | 134 | ``` 135 | >> address = "mndTEiHWjkbQqeTrSNKB4YnvVY5XH1DZvv" 136 | >> value = 99900000 137 | >> tx_out = Bitcoin::Protocol::TxOut.value_to_address(value, address) 138 | >> tx.add_out(tx_out) 139 | ``` 140 | 141 | ScriptPubkeyを指定する場合 142 | ``` 143 | >> value = 99900000 144 | >> script_pubkey = "v\xA9\x14N\x03\x89@\xF4\x98\xB0\x88\r%\x9E~\x8B\"\x83\xF6E\b~\x84\x88\xAC" 145 | >> Bitcoin::Protocol::TxOut.new(value, pk_script) 146 | ``` 147 | 148 | #### トランザクションの確認 149 | 150 | ``` 151 | >> tx.to_payload.bth 152 | ``` 153 | 154 | ## トランザクションへの電子署名 155 | 156 | 署名前のトランザクション 157 | ``` 158 | $ bitcoin-cli decoderawtransaction 01000000012dbd4c9c92bd710a55c57289e97b488cf9165f897406d446c43756dcae4951780000000000ffffffff01605af405000000001976a9144e038940f498b0880d259e7e8b2283f645087e8488ac00000000 159 | ``` 160 | 161 | ### BitcoinCoreのAPIを使用する場合 162 | 163 | ``` 164 | $ bitcoin-cli signrawtransaction 01000000012dbd4c9c92bd710a55c57289e97b488cf9165f897406d446c43756dcae4951780000000000ffffffff01605af405000000001976a9144e038940f498b0880d259e7e8b2283f645087e8488ac00000000 165 | ``` 166 | 167 | ### bitcoin-rubyを使用する場合 168 | 169 | #### ALLで署名 170 | 171 | ``` 172 | $ bitcoin-cli dumpprivkey mxt67XLBNcnsFpWtFZCDq9e5poL9ecEwSX 173 | 174 | $ irb 175 | >> require 'bitcoin' 176 | >> Bitcoin.network = :testnet3 177 | >> tx = Bitcoin::Protocol::Tx.new("01000000012dbd4c9c92bd710a55c57289e97b488cf9165f897406d446c43756dcae4951780000000000ffffffff01605af405000000001976a9144e038940f498b0880d259e7e8b2283f645087e8488ac00000000".htb) #署名前のトランザクション 178 | >> prev_tx = Bitcoin::Protocol::Tx.new("0100000001f11b0fdc283bba47b57a0eff0846848c5578b732909ddd5d2edd2d8537eabac0000000006a4730440220246ca676c6878cd55609751625904b1d14189909048f1957d5277d2ecd8fd726022034dad9a880e47c1f475df535b3ba1fa4867f8037ff9981bd5361c19436fa8e54012102321bc438a8f9d1aa69f1c730d9c84e0e85c2bddd0df4924ae851f082b205d646ffffffff01b01df505000000001976a914be7966685d48ebbc07c7104b16528455c3b157a888ac00000000".htb) #使用するUTXOが含まれるトランザクション 179 | >> secret_key = "cTPqVpfbEPwPp8QG4cj4mMPWqA5UKpVVWWMvyK8WgsKjg5K5cyYu" 180 | >> key = Bitcoin::Key.from_base58(secret_key) 181 | >> sig_hash = tx.signature_hash_for_input(0, prev_tx, Bitcoin::Script::SIGHASH_TYPE[:all]) #ALLの範囲でsignature hashを作成 182 | >> signature = key.sign(sig_hash) #署名の実行 183 | >> script_sig = Bitcoin::Script.to_signature_pubkey_script(signature, key.pub.htb, Bitcoin::Script::SIGHASH_TYPE[:all]) #署名スクリプトの作成 184 | >> tx.in[0].script_sig = script_sig #署名スクリプトをインプットに追加 185 | ``` 186 | 187 | 署名の検証 188 | 189 | ``` 190 | >> verify_tx = Bitcoin::Protocol::Tx.new(tx.to_payload) #トランザクションを複製 191 | >> verify_tx.verify_input_signature(0, prev_tx) #0番目のアウトプットについて署名を検証 192 | ``` 193 | 194 | ブロードキャスト 195 | 196 | ``` 197 | >> tx.to_payload.bth #トランザクションの16進数を確認 198 | 199 | $ bitcoin-cli sendrawtransaction 01000000012dbd4c9c92bd710a55c57289e97b488cf9165f897406d446c43756dcae495178000000006a473044022100f1f59a90054907dabcbf0f88cef63a54bfce89dcf715e2c972310c897faf09fd021f17fd17da54992684c2eaa9820286f56e0552e35df15423340b210a0ae21aa3012102321bc438a8f9d1aa69f1c730d9c84e0e85c2bddd0df4924ae851f082b205d646ffffffff01605af405000000001976a9144e038940f498b0880d259e7e8b2283f645087e8488ac00000000 200 | 201 | $ bitcoin-cli getrawtransaction 4a9181bc83868084165b0bbafca7124e6c233f9ff26c587204bfda557fec2848 1 #ブロードキャストしたトランザクションの内容を確認 202 | ``` 203 | 204 | ##### Secp256k1での署名 205 | 206 | ``` 207 | >> ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.so' # Ubuntuの場合 208 | >> ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.dylib' # Mac OS X の場合 209 | 210 | >> signature = Bitcoin::Secp256k1.sign(sig_hash, key.priv.htb) + [Bitcoin::Script::SIGHASH_TYPE[:all]].pack("C") 211 | >> signature.bth 212 | ``` 213 | 214 | 署名の検証 215 | 216 | ``` 217 | >> Bitcoin::Secp256k1.verify(sig_hash, signature, key.pub.htb) 218 | ``` 219 | 220 | 鍵の生成 221 | 222 | ``` 223 | >> key = Bitcoin::Secp256k1.generate_key 224 | >> key.addr #ビットコインアドレス 225 | >> key.pub #公開鍵 226 | >> key.priv #秘密鍵 227 | ``` 228 | 229 | 230 | #### NONEで署名 231 | 232 | ``` 233 | $ irb 234 | >> require 'bitcoin' 235 | >> Bitcoin.network = :testnet3 236 | >> tx = Bitcoin::Protocol::Tx.new("0100000001eaa0a91dd395453285a4a0430d9701fbce8b19f794ef53f603e5abdc66c5514f0000000000ffffffff01b0feea0b000000001976a9140e71550e6e5124cc0347a6622c5d8971a60a43d888ac00000000".htb) #署名前のトランザクション 237 | >> secret_key = "cS6oDaYpDSNwHZhG8H5xeuKVhrnwge17BQUjsrzkhzNaMPzzcrhZ" 238 | >> key = Bitcoin::Key.from_base58(secret_key) 239 | 240 | >> sig_hash = tx.signature_hash_for_input(0, prev_tx, Bitcoin::Script::SIGHASH_TYPE[:none]) #NONEの範囲でsignature hashを作成 241 | >> signature = key.sign(sig_hash) #署名を実行 242 | >> script_sig = Bitcoin::Script.to_signature_pubkey_script(signature, key.pub.htb, Bitcoin::Script::SIGHASH_TYPE[:none]) #署名スクリプトを作成 243 | >> tx.in[0].script_sig = script_sig #署名スクリプトをインプットに追加 244 | >> tx.to_payload.bth 245 | ``` 246 | 247 | 新しいアウトプットで置き換え 248 | ``` 249 | >> steal_tx = Bitcoin::Protocol::Tx.new( "0100000001eaa0a91dd395453285a4a0430d9701fbce8b19f794ef53f603e5abdc66c5514f000000006a473044022074e36984a6788b705e7f504210b23587542d80c2bf481115f8425e4bfd2f365302205410d3dcd848cc28607538acb61447c0761759c1e102bd238802b6d5762bed840221032db652232e705c3d38f08ef484524e9da7899de36955a2f52685960a51c12303ffffffff01b0feea0b000000001976a9140e71550e6e5124cc0347a6622c5d8971a60a43d888ac00000000".htb) #トランザクションを複製 250 | >> steal_address = "n4LfUUnpNnMpLT6PiE8yPbFztiZF7CxDpK" #新しいアドレス 251 | >> value = 199950000 252 | >> steal_script_pubkey = Bitcoin::Script.new(Bitcoin::Script.to_address_script(steal_address)).to_payload 253 | >> steal_tx_out = Bitcoin::Protocol::TxOut.new(value, steal_script_pubkey) #新しいアドレス宛のアウトプットを作成 254 | >> steal_tx.out[0] = steal_tx_out #新しいアウトプットを設定 255 | >> verify_steal_tx = Bitcoin::Protocol::Tx.new(steal_tx.to_payload) 256 | >> verify_steal_tx.verify_input_signature(0, prev_tx) #アウトプットを入れ替えたトランザクションで署名の検証を実施 257 | ``` 258 | 259 | アウトプットを入れ替えたトランザクションをブロードキャスト 260 | ``` 261 | >> steal_tx.to_payload.bth 262 | 263 | $ bitcoin-cli sendrawtransaction 0100000001eaa0a91dd395453285a4a0430d9701fbce8b19f794ef53f603e5abdc66c5514f000000006a473044022074e36984a6788b705e7f504210b23587542d80c2bf481115f8425e4bfd2f365302205410d3dcd848cc28607538acb61447c0761759c1e102bd238802b6d5762bed840221032db652232e705c3d38f08ef484524e9da7899de36955a2f52685960a51c12303ffffffff01b0feea0b000000001976a914fa58b14cc42b4409989a7f2a230dfb15bdbd9b5a88ac00000000 264 | 265 | $ bitcoin-cli getrawtransaction 39b4f322d1bfdcd7b8789c6921619e074be521c2f2b5a1dc51abe481191d9bd4 1 266 | ``` 267 | 268 | ## P2PKH、マルチシグ、P2SH 269 | 270 | ## P2PKH (Pay to Public Key Hash) 271 | 272 | ### ScriptPubkeyの実装例 273 | 274 | ``` 275 | $ irb 276 | >> require 'bitcoin' 277 | >> Bitcoin.network = :testnet3 278 | >> address = "n1PEN5QVu3HcZ2Tft2MSgGqLGhws7JB9EJ" 279 | >> script_pubkey = Bitcoin::Script.to_address_script(address) 280 | >> Bitcoin::Script.new(script_pubkey).to_string #ScriptPubkeyの中身を確認 281 | ``` 282 | 283 | ### ScriptSigの実装例 284 | 285 | ``` 286 | >> sig_hash = tx.signature_hash_for_input(0, prev_tx, Bitcoin::Script::SIGHASH_TYPE[:all]) 287 | >> signature = key.sign(sig_hash) 288 | >> script_sig = Bitcoin::Script.to_signature_pubkey_script(signature, key.pub.htb, Bitcoin::Script::SIGHASH_TYPE[:all]) 289 | >> Bitcoin::Script.new(script_sig).to_string #ScriptSigの中身を確認 290 | ``` 291 | 292 | ## マルチシグ 293 | 294 | ### ScriptPubkeyの実装例 295 | ``` 296 | $ irb 297 | >> require 'bitcoin' 298 | >> public_key_a = "0357cf4ce57e92a0c15a989b351c31e69db9c75dcc5932e9a6d5fc0fc64947422b" 299 | >> public_key_b = "036671fad937127aaf45226bcb9e6e94e289eda721d1d9e94992feb78af78e83e5" 300 | >> public_key_c = "028f1ff70b9b502862133e3d7f0597492776007287c092f3ee1fff13051dbb3857" 301 | >> script_pubkey = Bitcoin::Script.to_multisig_script(2, public_key_a, public_key_b, public_key_c) 302 | >> Bitcoin::Script.new(script_pubkey).to_string #ScriptPubkeyの中身を確認 303 | ``` 304 | 305 | ### ScriptSigの実装例 306 | 307 | ``` 308 | >> sig_hash = tx.signature_hash_for_input(0, prev_tx, Bitcoin::Script::SIGHASH_TYPE[:all]) 309 | >> signature_a = key_a.sign(sig_hash) 310 | >> signature_b = key_b.sign(sig_hash) 311 | >> script_sig_b = Bitcoin::Script.to_multisig_script_sig(signature_b) #Bが署名 312 | >> script_sig_ab = Bitcoin::Script.add_sig_to_multisig_script_sig(signature_a, script_sig_b) #Aの署名を追加 313 | >> Bitcoin::Script.new(script_sig).to_string #ScriptSigの中身を確認 314 | ``` 315 | 316 | ## P2SH (Pay to Script Hash) 317 | 318 | ### ScriptPubkeyの実装例 319 | 320 | ``` 321 | $ irb 322 | >> require 'bitcoin' 323 | >> public_key_a = "0357cf4ce57e92a0c15a989b351c31e69db9c75dcc5932e9a6d5fc0fc64947422b" 324 | >> public_key_b = "036671fad937127aaf45226bcb9e6e94e289eda721d1d9e94992feb78af78e83e5" 325 | >> public_key_c = "028f1ff70b9b502862133e3d7f0597492776007287c092f3ee1fff13051dbb3857" 326 | >> script_pubkey, redeem_script = Bitcoin::Script.to_p2sh_multisig_script(2, public_key_a, public_key_b, public_key_c) 327 | >> Bitcoin::Script.new(script_pubkey).to_string #ScriptPubkeyの中身を確認 328 | ``` 329 | 330 | ### ScriptSigの実装例 331 | 332 | ``` 333 | >> redeem_script = "R!\x03W\xCFL\xE5~\x92\xA0\xC1Z\x98\x9B5\x1C1\xE6\x9D\xB9\xC7]\xCCY2\xE9\xA6\xD5\xFC\x0F\xC6IGB+!\x03fq\xFA\xD97\x12z\xAFE\"k\xCB\x9En\x94\xE2\x89\xED\xA7!\xD1\xD9\xE9I\x92\xFE\xB7\x8A\xF7\x8E\x83\xE5!\x02\x8F\x1F\xF7\v\x9BP(b\x13>=\x7F\x05\x97I'v\x00r\x87\xC0\x92\xF3\xEE\x1F\xFF\x13\x05\x1D\xBB8WS\xAE" 334 | >> sig_hash = tx.signature_hash_for_input(0, redeem_script) 335 | >> signature_a = key_a.sign(sig_hash) 336 | >> signature_b = key_b.sign(sig_hash) 337 | >> script_sig = Bitcoin::Script.to_p2sh_multisig_script_sig(redeem_script, signature_a, signature_b) 338 | >> Bitcoin::Script.new(script_sig).to_string #ScriptSigの中身を確認 339 | ``` 340 | 341 | ### 署名の順番をソートする実装例 342 | 343 | ``` 344 | >> sig_hash = tx.signature_hash_for_input(0, redeem_script) 345 | >> signature_a = key_a.sign(sig_hash) 346 | >> signature_b = key_b.sign(sig_hash) 347 | >> script_sig = Bitcoin::Script.to_p2sh_multisig_script_sig(redeem_script, signature_b, signature_a) 348 | >> Bitcoin::Script.new(script_sig).to_string # OP_0 の形 349 | >> new_script_sig = Bitcoin::Script.sort_p2sh_multisig_signatures(script_sig, sig_hash) #署名の順番をソート 350 | >> Bitcoin::Script.new(new_script_sig).to_string # OP_0 の形 351 | ``` 352 | 353 | ## ロックタイム(OP_CLTV、OP_CSV) 354 | 355 | ### OP_CLTV(OP_CHECKLOCKTIMEVERIFY) 356 | 357 | #### ScriptPubkeyの実装例 358 | 359 | ``` 360 | $ bitcoin-cli getblockcount 361 | >> public_key = "0357cf4ce57e92a0c15a989b351c31e69db9c75dcc5932e9a6d5fc0fc64947422b" 362 | >> expiry_time = 1089535 363 | >> et = expiry_time.to_bn.to_s(2).reverse.unpack("H*")[0] 364 | >> script_pubkey = Bitcoin::Script.from_string("#{et} OP_NOP2 OP_DROP #{public_key} OP_CHECKSIG").to_payload 365 | >> Bitcoin::Script.new(script_pubkey).to_string #ScriptPubkeyの中身を確認 366 | ``` 367 | 368 | #### ScriptSigの実装例 369 | 370 | ``` 371 | >> tx.lock_time = expiry_time 372 | >> tx.in[0].sequence = [0].pack("V") #sequenceに値を設定 373 | >> sig_hash = tx.signature_hash_for_input(0, prev_tx, Bitcoin::Script::SIGHASH_TYPE[:all]) 374 | >> signature = key_a.sign(sig_hash) 375 | >> script_sig = Bitcoin::Script.pack_pushdata(signature + [hash_type].pack("C")) 376 | ``` 377 | 378 | ### OP_CSV(OP_CHECKSEQUENCEVERIFY) 379 | 380 | #### ScriptPubkeyの実装例 381 | 382 | ``` 383 | >> public_key = "0357cf4ce57e92a0c15a989b351c31e69db9c75dcc5932e9a6d5fc0fc64947422b" 384 | >> expiry_time = 6 385 | >> script_pubkey = Bitcoin::Script.from_string("#{expiry_time} OP_NOP3 OP_DROP #{public_key} OP_CHECKSIG").to_payload 386 | >> Bitcoin::Script.new(script_pubkey).to_string #ScriptPubkeyの中身を確認 387 | ``` 388 | 389 | ブロック数が16を超える場合 390 | ``` 391 | >> expiry_time = 100 392 | >> et = expiry_time.to_bn.to_s(2).reverse.unpack("H*")[0] 393 | >> script_pubkey = Bitcoin::Script.from_string("#{et} OP_NOP3 OP_DROP #{public_key} OP_CHECKSIG").to_payload 394 | >> Bitcoin::Script.new(script_pubkey).to_string #ScriptPubkeyの中身を確認 395 | ``` 396 | 397 | #### ScriptSigの実装例 398 | 399 | ``` 400 | >> tx.ver = 2 401 | >> tx.in[0].sequence = [expiry_time].pack("V") 402 | >> sig_hash = tx.signature_hash_for_input(0, prev_tx, Bitcoin::Script::SIGHASH_TYPE[:all]) 403 | >> signature = key_a.sign(sig_hash) 404 | >> script_sig = Bitcoin::Script.pack_pushdata(signature + [hash_type].pack("C")) 405 | >> Bitcoin::Script.new(script_sig).to_string #ScriptSigの中身を確認 406 | ``` 407 | -------------------------------------------------------------------------------- /Chapter07.md: -------------------------------------------------------------------------------- 1 | # ノードとビットコインネットワーク 2 | 3 | ## ハンドシェイク 4 | 5 | ```ruby 6 | require 'bitcoin' 7 | require 'eventmachine' 8 | 9 | Bitcoin.network = :testnet3 10 | 11 | class Connection < EM::Connection 12 | 13 | attr_reader :host, :port, :parser 14 | 15 | def initialize(host, port) 16 | @host = host 17 | @port = port 18 | # メッセージのパーサー 19 | @parser = Bitcoin::Protocol::Parser.new( self ) 20 | end 21 | 22 | # コネクション確立直後によばれる初期化処理 23 | def post_init 24 | puts "connected." 25 | on_handshake_begin 26 | end 27 | 28 | # リモートノードからデータを受信した際のハンドラ 29 | def receive_data(data) 30 | parser.parse(data) 31 | end 32 | 33 | # ハンドシェイクの開始 34 | def on_handshake_begin 35 | puts "handshake begin." 36 | # リモートノードにバージョンメッセージを送信 37 | params = {block: 0, from: "127.0.0.1:18333", nonce: Bitcoin::Protocol::Uniq, to: "#{host}:#{port}", 38 | user_agent: "/bitcoin-ruby:#{Bitcoin::VERSION}/", version: 70015, time: Time.now.tv_sec} 39 | send_data(Bitcoin::Protocol.version_pkt(params)) 40 | end 41 | 42 | # リモートノードからversionメッセージを受信 43 | def on_version(version) 44 | puts "receive version. Version:#{version.version}, UserAgent: #{version.user_agent}" 45 | # リモードノードにverackメッセージを送信 46 | send_data( Bitcoin::Protocol.verack_pkt ) 47 | end 48 | 49 | # リモートノードからverackメッセージを受信 50 | def on_verack 51 | puts "receive verack. handshake complete." 52 | end 53 | 54 | end 55 | 56 | EM.run do 57 | # ローカルのBitcoin Coreのtestnetに接続 58 | EM.connect('localhost', 18333, Connection, 'localhost', 18333) 59 | end 60 | ``` 61 | 62 | ## SPVを効率的に実現するブルームフィルタ 63 | 64 | ### ブルームフィルタ 65 | 66 | #### ハッシュ関数の実装 67 | 68 | 69 | ```ruby 70 | require 'murmurhash3' 71 | 72 | class BloomFilter 73 | 74 | SEED_BASE = 0xFBA4C795 75 | 76 | attr_reader :bits 77 | attr_reader :hash_funcs 78 | attr_reader :tweak 79 | 80 | def initialize(n, k, tweak) 81 | @bits = Array.new(n, 0) # ビット列 82 | @tweak = tweak # 乱数 83 | @hash_funcs = k # ハッシュ関数の数 84 | end 85 | 86 | # フィルタに要素を追加 87 | def add(data) 88 | calc_index(data).each do |i| 89 | bits[i] = 1 90 | end 91 | end 92 | 93 | # フィルタに要素が登録されているか確認 94 | def contains?(data) 95 | calc_index(data).map{|i|bits[i]}.find{|i| i == 0}.nil? 96 | end 97 | 98 | private 99 | 100 | def calc_index(data) 101 | list = [] 102 | hash_funcs.times do |i| 103 | seed = (i * SEED_BASE + tweak) & 0xffffffff 104 | hash = MurmurHash3::V32.str_hash(data, seed) 105 | list << hash % bits.length 106 | end 107 | list 108 | end 109 | end 110 | ``` 111 | 112 | ```ruby 113 | filter = BloomFilter.new(10, 2, 123456) # 123456はtweak 114 | 115 | # フィルタに要素を追加 116 | filter.add('bitcoin') 117 | filter.add('monacoin') 118 | 119 | # フィルタを検索 120 | filter.contains?('bitcoin') 121 | => true 122 | 123 | filter.contains?('litecoin') 124 | => false 125 | 126 | filter.contains?('bitcoin4') 127 | => true 128 | ``` 129 | -------------------------------------------------------------------------------- /Chapter09.md: -------------------------------------------------------------------------------- 1 | # Open Assets Protocol 2 | 3 | ## Marker Output 4 | 5 | ### Marker Outputの例 6 | 7 | ```ruby 8 | >> marker = OpenAssets::Protocol::MarkerOutput.deserialize_payload("4f41010003ac0200e58e260412345678") 9 | 10 | >> marker.asset_quantities 11 | => [300, 0, 624485] 12 | 13 | >> marker.metadata 14 | => "\x124Vx" 15 | ``` 16 | 17 | ## Asset IDの計算 18 | 19 | ```ruby 20 | require 'bitcoin' 21 | 22 | # 1.秘密鍵を生成 23 | key = Bitcoin::Key.generate 24 | private_key = key.priv 25 | => "e1b9722c716b568f2bb25d7fc9a4943b76369711fa33cc7a0b309307f8a4bfdd" 26 | 27 | # 2.対応する公開鍵を生成 28 | public_key = key.pub 29 | => "03ecf9321ba066d9b7e224803aae0df9104fed0553ab64acb5937c1b079cfb9da7" 30 | 31 | # 3.P2PKHスクリプトを生成 32 | script = Bitcoin::Script.to_hash160_script(key.hash160) 33 | 34 | # 4.スクリプトのハッシュを生成 35 | hash = Bitcoin.hash160(script.bth) 36 | => "f8cc4f23cdedeca2c429aef5b3345e666c411ece" 37 | 38 | # 5. Asset IDとしてエンコード 39 | hash = 23.to_s(16) + hash 40 | asset_id = Bitcoin.encode_base58(hash + Bitcoin.checksum(hash)) 41 | => "AeTPyiiyYKEFXWeu4WjDqjb4raPck2XWeJ" 42 | ``` 43 | 44 | ## openassets-rubyを使ったアセットの発行・送付 45 | 46 | ### 設定 47 | 48 | ```ruby 49 | require 'openassets' 50 | 51 | Bitcoin.network = :testnet3 52 | 53 | api = OpenAssets::Api.new({ 54 | network: 'testnet', 55 | rpc: { 56 | user: 'xxx', 57 | password: 'xxx', 58 | schema: 'http', 59 | port: 18332, 60 | host: 'localhost'} 61 | }) 62 | ``` 63 | 64 | ```ruby 65 | utxo_list = api.list_unspent 66 | 67 | puts JSON.pretty_generate(utxo_list) 68 | => [ 69 | { 70 | "txid": "a98b000f28c9e8251925a1225832edf1c2992e103445b6b2cec3f5c0a81469e5", 71 | "vout": 2, 72 | "confirmations": 154856, 73 | "address": "mvZM34fU6wdDqF3gKd2tYA67vjWvwBHbDU", 74 | "oa_address": "bX6XEHEUoaRWuhVD3NF95C1zhGLh6qqqk5d", 75 | "script": "76a914a4fdb3ce954a3cbb49fdfc9df1712bf72749c9e788ac", 76 | "script_type": "pubkeyhash", 77 | "amount": "0.00039400", 78 | "asset_id": null, 79 | "asset_quantity": "0", 80 | "asset_amount": "0", 81 | "account": "sample", 82 | "asset_definition_url": "", 83 | "proof_of_authenticity": false, 84 | "output_type": "transfer", 85 | "solvable": true, 86 | "spendable": true 87 | }, 88 | ... 89 | ] 90 | ``` 91 | 92 | ### アセットの発行 93 | 94 | ```ruby 95 | # ビットコインアドレス 96 | btc_address = 'mxpc1LnTMDFxwy1Va6Xpd4p1nDwRUopeic' 97 | 98 | # ビットコインアドレスからOpen Assets Address 99 | oa_address = OpenAssets.address_to_oa_address(btc_address) 100 | 101 | # メタデータ 102 | metadata = 'u=https://goo.gl/uapCsJ' 103 | 104 | # アセットの発行 105 | tx = api.issue_asset(oa_address, 100, metadata) 106 | => 07e108133edcc3a5407de8e8f117a995625abcba2c8bb42ad0831c3665dbe29b # TXID 107 | ``` 108 | 109 | ```ruby 110 | # ビットコインアドレスから公開鍵のHash160を計算 111 | hash160 = Bitcoin.hash160_from_address(btc_address) 112 | 113 | # 公開鍵のHash160からAsset IDを計算 114 | asset_id = OpenAssets.pubkey_hash_to_asset_id(hash160) 115 | => "oQDVsbKEAkkBz1nN5BTGUwMxu9Y5CmhFEU" 116 | 117 | # 公開鍵が分かっていれば以下のメソッドで計算することも可能です 118 | asset_id = OpenAssets.generate_asset_id(公開鍵) 119 | ``` 120 | 121 | ### アセットの送付 122 | 123 | ```ruby 124 | asset_id = 'oQDVsbKEAkkBz1nN5BTGUwMxu9Y5CmhFEU' 125 | 126 | # 送付先のアドレス 127 | to_oa_address = OpenAssets.address_to_oa_address('mvAQLozzMcAKPGyFwck4opDCQb86pmz3TZ') 128 | 129 | # アセットを30送付 130 | tx = api.send_asset(oa_address, asset_id, 30, to_oa_address) 131 | => 12712bdda9cbdd40268c0b95429a731ec3047692f45fe0599b4bd91739c0bd91 #TXID 132 | ``` 133 | 134 | ```ruby 135 | # 送付元アドレス 136 | from = api.address_to_oa_address('mrxpeizRrF8ymNx5FrvcGGZVecZjtUFVP3') 137 | 138 | # 送付先アドレス 139 | to = api.address_to_oa_address('n4MEsSUN8GktDFZzU3V55mP3jWGMN7e4wE') 140 | 141 | # 送付アセットと量 142 | params = [] 143 | params << OpenAssets::SendAssetParam.new('oGu4VXx2TU97d9LmPP8PMCkHckkcPqC5RY', 50, to) 144 | params << OpenAssets::SendAssetParam.new('oUygwarZqNGrjDvcZUpZdvEc7es6dcs1vs', 4, to) 145 | 146 | tx = api.send_assets(from, params) 147 | ``` 148 | 149 | ### ビットコインの送付 150 | 151 | ```ruby 152 | # 送付元のビットコインアドレス 153 | from = 'mt1hZLajqyc63NkWy7qvgiuum5nuTBdVZ6' 154 | 155 | # 送付先のビットコインアドレス 156 | to = 'mgfx7ZKuvutFozSc1aH1U1iDEgqM8bRDX2' 157 | 158 | # ビットコインの送金 159 | api.send_bitcoin(from, 2150000, to) 160 | => 11929789a686dbf6895b0d52bfe64ad1768b95c8458412cf9adadb9431403ca7 # TXID 161 | ``` 162 | 163 | ### アセットの焼却 164 | 165 | ```ruby 166 | oa_address = OpenAssets.address_to_oa_address('mxpc1LnTMDFxwy1Va6Xpd4p1nDwRUopeic') 167 | 168 | asset_id = 'oQDVsbKEAkkBz1nN5BTGUwMxu9Y5CmhFEU' 169 | 170 | # アセットの焼却 171 | tx = api.burn_asset(oa_address, asset_id) 172 | => 4f67034b074edf5fa4880e5a30997d450ba5d1d59d0fb5b8dba6cf793f78e923 # TXID 173 | ``` 174 | 175 | ### ユーティリティ 176 | 177 | ```ruby 178 | # OP_RETURNのOpen Assets Payloadをパース 179 | m = OpenAssets::Protocol::MarkerOutput.deserialize_payload('4f410100016417753d68747470733a2f2f676f6f2e676c2f75617043734a') 180 | 181 | m.asset_quantities 182 | => [100] # Asset Quantity 183 | 184 | m.metadata 185 | => "u=https://goo.gl/uapCsJ" # メタデータ 186 | 187 | # Open Assets Payloadの生成 188 | quantities = [500, 100, 15] 189 | metadata = 'u=https://goo.gl/uapCsJ' 190 | 191 | m = OpenAssets::Protocol::MarkerOutput.new(quantities, metadata) 192 | payload = m.to_payload 193 | => "4f41010003f403640f17753d68747470733a2f2f676f6f2e676c2f75617043734a" 194 | 195 | # Marker Outputスクリプトの生成 196 | script = m.build_script 197 | script.to_payload.bth 198 | => "6a214f41010003f403640f17753d68747470733a2f2f676f6f2e676c2f75617043734a" 199 | ``` 200 | 201 | ```ruby 202 | # Bitcoin Coreへアクセスするプロバイダの取得(OpenAssets::Provider::BitcoinCoreProvider) 203 | provider = api.provider 204 | 205 | # Bitcoin Coreの各種APIのよび出し 206 | 207 | # getinfoの実行 208 | provider.getinfo 209 | => { 210 | "version": 140100, 211 | "protocolversion": 70015, 212 | "walletversion": 60000, 213 | "balance": 0.0905132, 214 | "blocks": 1125666, 215 | "timeoffset": 0, 216 | "connections": 8, 217 | "proxy": "", 218 | "difficulty": 1, 219 | "testnet": true, 220 | "keypoololdest": 1458624560, 221 | "keypoolsize": 100, 222 | "paytxfee": 0.0, 223 | "relayfee": 1.0e-05, 224 | "errors": "Warning: unknown new rules activated (versionbit 28)" 225 | } 226 | 227 | # getblockhashの実行 228 | provider.getblockhash(1125666) 229 | => "000000000002945741d2fd9690fb9ebdecb2e11b3ab836666bb04c31a10a5713" 230 | 231 | # getblockの実行 232 | provider.getblock("000000000002945741d2fd9690fb9ebdecb2e11b3ab836666bb04c31a10a5713") 233 | => { 234 | "hash": "000000000002945741d2fd9690fb9ebdecb2e11b3ab836666bb04c31a10a5713", 235 | "confirmations": 1, 236 | "strippedsize": 36114, 237 | "size": 36114, 238 | "weight": 144456, 239 | "height": 1125666, 240 | "version": 536870912, 241 | "versionHex": "20000000", 242 | "merkleroot": "707ab240cc863361b7ce2429b30a21d4c771306947a28adf5e91e3fb1709c5ab", 243 | "tx": [ 244 | "3e9831a06117c34f65a7575fb8bf2064ccce5ecd6081c596511ec5b5f84008aa", 245 | "d51d2a3d7cf4b54ad655d07bcc03f04a5bfa02117e90680d12fd3b8ca934cc2c", 246 | "508147ee0ec806fb1c322eafcab245bff871851b216e0565054cf88dbc2e9bf0", 247 | ... 248 | "190cd8167ed5e65e1b743e8ec4b8031ccb89d904d7584af34f295528d9eebfc6" 249 | ], 250 | "time": 1495762246, 251 | "mediantime": 1495756158, 252 | "nonce": 2559794018, 253 | "bits": "1d00ffff", 254 | "difficulty": 1, 255 | "chainwork": "00000000000000000000000000000000000000000000002457b3f28a59bccf62", 256 | "previousblockhash": "000000000000d6245eb09589e0efdabf5e76405220aeffdd12cc39d616b72282" 257 | } 258 | 259 | # この他にもbitcoin-cliがサポートしているコマンドとその引数をそのまま実行できます。 260 | ``` 261 | 262 | -------------------------------------------------------------------------------- /Chapter10.md: -------------------------------------------------------------------------------- 1 | # Segregated Witness 2 | 3 | ## Segwitのアドレスフォーマット 4 | 5 | ### Bech32フォーマット 6 | 7 | #### チェックサム 8 | 9 | ```ruby 10 | def create_checksum(hrp, data) 11 | values = expand_hrp(hrp) + data 12 | polymod = polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 13 | (0..5).map{|i|(polymod >> 5 * (5 - i)) & 31} 14 | end 15 | 16 | def expand_hrp(hrp) 17 | hrp.each_char.map{|c|c.ord >> 5} + [0] + hrp.each_char.map{|c|c.ord & 31} 18 | end 19 | 20 | def polymod(values) 21 | generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] 22 | chk = 1 23 | values.each do |v| 24 | top = chk >> 25 25 | chk = (chk & 0x1ffffff) << 5 ^ v 26 | (0..4).each{|i|chk ^= ((top >> i) & 1) == 0 ? 0 : generator[i]} 27 | end 28 | chk 29 | end 30 | ``` 31 | 32 | ```ruby 33 | > create_checksum('A', [1, 2, 3,4]) 34 | => [14, 5, 25, 5, 22, 26] 35 | ``` 36 | 37 | また、human-readable部(hrp)とデータ部(data)が与えられた際のチェックサムの有効性は以下のメソッドで検証できます。 38 | 39 | ```ruby 40 | def verify_checksum(hrp, data) 41 | polymod(expand_hrp(hrp) + data) == 1 42 | end 43 | ``` 44 | 45 | ```ruby 46 | > verify_checksum('A', [1, 2, 3,4, 14, 5, 25, 5, 22, 26]) 47 | => true 48 | ``` 49 | 50 | ### Segwitアドレス 51 | 52 | ```ruby 53 | require 'bech32' 54 | require 'digest' 55 | 56 | pubkey = '0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798' 57 | 58 | # 公開鍵をHASH160 59 | hash160 = Digest::RMD160.hexdigest Digest::SHA256.digest([pubkey].pack("H*")) 60 | 61 | # P2WPKHのscriptPubkeyを生成 62 | script_pubkey = [["00", "14", hash160].join ].pack("H*").unpack("H*").first 63 | => "0014751e76e8199196d454941c45d1b3a323f1433bd6" 64 | 65 | segwit_addr = Bech32::SegwitAddr.new 66 | segwit_addr.hrp = 'bc' 67 | segwit_addr.script_pubkey = script_pubkey 68 | 69 | segwit_addr.addr 70 | => "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" 71 | 72 | # Testnetの場合のアドレス 73 | segwit_addr.hrp = 'tb' 74 | segwit_addr.addr 75 | => "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" 76 | ``` 77 | 78 | ```ruby 79 | segwit_addr = Bech32::SegwitAddr.new('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') 80 | 81 | segwit_addr.to_script_pubkey 82 | => "0014751e76e8199196d454941c45d1b3a323f1433bd6" 83 | 84 | segwit_addr.hrp 85 | => "bc" 86 | ``` 87 | 88 | ## Segwitトランザクションの新しい署名方式 89 | 90 | ### P2WPKHの署名の作成 91 | 92 | ```ruby 93 | require 'bitcoin' 94 | Bitcoin.network = :testnet3 95 | 96 | pubkey = '02effb2edfcf826d43027feae226143bdac058ad2e87b7cec26f97af2d357ddefa' 97 | script_pubkey = Bitcoin::Script.to_witness_hash160_script(Bitcoin.hash160(pubkey)).bth 98 | => '00148911455a265235b2d356a1324af000d4dae03262' 99 | ``` 100 | 101 | ```ruby 102 | require 'bitcoin' 103 | Bitcoin.network = :testnet3 104 | 105 | # 公開鍵に対応する秘密鍵 106 | key = Bitcoin::Key.from_base58('cT1HdGwS1vFwZsXqVzAKx1G8JYuwmM2sXtohU43uwXdNs6F7Xr2J') 107 | 108 | # 上記P2WPKHのScriptPubkey宛の送金アウトプット(0番目のアウトプット)を含むトランザクション 109 | prev_tx_payload = '0100000001f55cb86d8d04d4759fb8b05a198cf4d48d790e6c64d00e072aed98281d0ebff1010000006b483045022100fe718c5f0bb58d86225e1d9370f858b8c864f00112c6a33f9910fa7ea8e9c34b02203cef1a73a0a8e210c6a264446bbb90c86b7b6f6e6f411aa505f0a835ff147204012102effb2edfcf826d43027feae226143bdac058ad2e87b7cec26f97af2d357ddefaffffffff02806d0d00000000001600148911455a265235b2d356a1324af000d4dae0326250a1c917000000001976a9142c159d64daa0de5ae6abac61a9416c8a54e834bd88ac00000000'.htb 110 | prev_tx = Bitcoin::Protocol::Tx.new(prev_tx_payload) 111 | 112 | # P2WPKHのScriptPubkeyを使用するトランザクションを作成 113 | tx = Bitcoin::Protocol::Tx.new 114 | tx.add_in(Bitcoin::Protocol::TxIn.from_hex_hash(prev_tx.hash, 0)) 115 | tx.add_out(Bitcoin::Protocol::TxOut.value_to_address(870000, 'mt1hZLajqyc63NkWy7qvgiuum5nuTBdVZ6')) 116 | 117 | # Segwit用の新しいフォーマットで署名対象のダイジェストを生成(ロックされているコインの量も必要になります。) 118 | sig_hash = tx.signature_hash_for_witness_input(0, prev_tx.out[0].script, prev_tx.out[0].value) 119 | 120 | # 秘密鍵で署名 121 | sig = key.sign(sig_hash) + [Bitcoin::Script::SIGHASH_TYPE[:all]].pack("C") 122 | 123 | # 生成した署名値と公開鍵をwitness領域にセット(script_sigにはセットしない) 124 | tx.in[0].script_witness.stack << sig 125 | tx.in[0].script_witness.stack << key.pub.htb 126 | 127 | # 生成した署名が有効か検証(ロックされているコインの量も必要になります。) 128 | tx.verify_witness_input_signature(0, prev_tx.out[0].script, prev_tx.out[0].value) 129 | => true 130 | 131 | # Segwitのシリアライゼーションフォーマットのペイロードを生成 132 | tx.to_witness_payload.bth 133 | => "010000000001018015516590902931d31f650f7e0e79a931e01bcb2f73d4ca49195aed2854b5fd0000000000ffffffff0170460d00000000001976a9148911455a265235b2d356a1324af000d4dae0326288ac0247304402207999b9ad187981fdda5606d0b4aa36fbbf5afe2ca234b5819420756cc103274802203eabb26cc5d0f5510380735283934935b70beb1a8836bbaaee9244c2fda0c830012102effb2edfcf826d43027feae226143bdac058ad2e87b7cec26f97af2d357ddefa00000000" 134 | ``` 135 | 136 | ### P2WSHの署名の作成 137 | 138 | ```ruby 139 | require 'bitcoin' 140 | Bitcoin.network = :testnet3 141 | 142 | # 2-of-2のマルチシグのredeem script 143 | redeem_script = Bitcoin::Script.from_string('2 0303f4c425fe2bb1a15bf44010014c74d014bf1268dd43aadab0cd79b042bfaf2f 03d9cd29752768733c4d497b63b3f72c3e58f9927c6fc5912468e4536f98d7821d 2 OP_CHECKMULTISIG') 144 | 145 | script_pubkey = Bitcoin::Script.to_witness_p2sh_script(Bitcoin.sha256(redeem_script.to_payload.bth)).bth 146 | => '0020b271acf6e944c13352405bf1c101d5531a208d1c3e4bef15b61f0717bd95e2bd' 147 | ``` 148 | 149 | ```ruby 150 | require 'bitcoin' 151 | Bitcoin.network = :testnet3 152 | 153 | # マルチシグの各公開鍵に対応する秘密鍵 154 | key_a = Bitcoin::Key.from_base58('cVXseE36PXUwCu7xT4kbhjibVajy2RW4VmoT3b2eq2dNrqy19mcn') 155 | key_b = Bitcoin::Key.from_base58('cMz1VdDkfTMXEJq9s7njw2BRXgF7o1CqaWnBtNgmkqSkjGW7huW5') 156 | 157 | # P2WSHのScriptPubkey宛の送金アウトプット(0番目のアウトプット)を含むトランザクション 158 | prev_tx = Bitcoin::Protocol::Tx.new('0100000001b090eb4d4233616d8f00ff830bdc460b4559f7577b8e99362b0a96f67ef713a00000000000ffffffff0150c3000000000000220020b271acf6e944c13352405bf1c101d5531a208d1c3e4bef15b61f0717bd95e2bd00000000'.htb) 159 | 160 | # P2WSHのScriptPubkeyを使用するトランザクションを作成 161 | tx = Bitcoin::Protocol::Tx.new 162 | tx.add_in(Bitcoin::Protocol::TxIn.from_hex_hash(prev_tx.hash, 0)) 163 | tx.add_out(Bitcoin::Protocol::TxOut.value_to_address(40000, 'mt1hZLajqyc63NkWy7qvgiuum5nuTBdVZ6')) 164 | 165 | # Segwit用の新しいフォーマットで署名対象のダイジェストを生成(ロックされているコインの量も必要になります。) 166 | sig_hash = tx.signature_hash_for_witness_input(0, prev_tx.out[0].pk_script, prev_tx.out[0].value, redeem_script.to_payload) 167 | 168 | # マルチシグに使われている各鍵で署名を生成 169 | sig1 = key_a.sign(sig_hash) + [Bitcoin::Script::SIGHASH_TYPE[:all]].pack("C") 170 | sig2 = key_b.sign(sig_hash) + [Bitcoin::Script::SIGHASH_TYPE[:all]].pack("C") 171 | 172 | # 生成した署名値とredeem scriptをwitness領域にセット(script_sigにはセットしない) 173 | tx.in[0].script_witness.stack << '' 174 | tx.in[0].script_witness.stack << sig1 175 | tx.in[0].script_witness.stack << sig2 176 | tx.in[0].script_witness.stack << redeem_script.to_payload 177 | 178 | # 生成した署名が有効か検証(ロックされているコインの量も必要になります。) 179 | tx.verify_witness_input_signature(0, prev_tx, prev_tx.out[0].value) 180 | => true 181 | 182 | # Segwitのシリアライゼーションフォーマットのペイロードを生成 183 | tx.to_witness_payload.bth 184 | => '010000000001010cd3b20630a2b31884adf205ac80e617ba5ff8ffff872b89b772a826b8033ff60000000000ffffffff01409c0000000000001976a9148911455a265235b2d356a1324af000d4dae0326288ac040047304402202b1caf17a0a889f2dfb6e0d103ac5c88283852b72930f9d05672a073334677bb02206bc5ca5e51c3313456339d513d2d923784f2ddcf59af158a88773d1411d49d970147304402206eb8d4309cb803a69d5913b25e8df526ab1d4dd4c238d4b5dbf950bd0bfbb217022041880c51d85fe7e0448bbb26d8ce837c390b965c4ceda92f3c128b89c8857c68014752210303f4c425fe2bb1a15bf44010014c74d014bf1268dd43aadab0cd79b042bfaf2f2103d9cd29752768733c4d497b63b3f72c3e58f9927c6fc5912468e4536f98d7821d52ae00000000' 185 | ``` 186 | 187 | -------------------------------------------------------------------------------- /Chapter11.md: -------------------------------------------------------------------------------- 1 | # スケーラビリティへの対応 2 | 3 | ## マイクロペイメントチャネル 4 | 5 | ### 単方向マイクロペイメントチャネルのプログラム例 6 | 7 | ``` 8 | $ irb 9 | >> require 'bitcoin' 10 | >> require 'openassets' 11 | >> Bitcoin.network = :testnet3 12 | 13 | >> api = OpenAssets::Api.new({ 14 | network: 'testnet', 15 | provider: 'bitcoind', 16 | cache: 'testnet.db', 17 | dust_limit: 600, 18 | default_fees: 10000, 19 | min_confirmation: 1, 20 | max_confirmation: 9999999, 21 | rpc: { 22 | user: 'xxx', 23 | password: 'xxx', 24 | schema: 'http', 25 | port: 18332, 26 | host: 'localhost', 27 | timeout: 60, 28 | open_timeout: 60 } 29 | }) 30 | 31 | #1 オープニングトランザクションの作成 32 | >> tx_fee = 50000 33 | >> deposit_amount = 1000000000 34 | 35 | >> p2sh_script, redeem_script = Bitcoin::Script.to_p2sh_multisig_script(2, alice_key.pub, bob_key.pub) 36 | >> multisig_addr = Bitcoin::Script.new(p2sh_script).get_p2sh_address 37 | >> opening_tx = api.send_bitcoin(alice_key.addr, deposit_amount, multisig_addr, tx_fee, 'signed') #ブロードキャストは払い戻し用トランザクションの作成後に行う 38 | 39 | #2 アリスが払い戻しトランザクションを作成 40 | >> refund_tx = Bitcoin::Protocol::Tx.new 41 | 42 | >> opening_tx_vout = 0 43 | >> block_height = api.provider.getinfo['blocks'].to_i #現在のブロック高を取得 44 | >> locktime = block_height + 100 45 | 46 | >> refund_tx_in = Bitcoin::Protocol::TxIn.from_hex_hash(opening_tx.hash, opening_tx_vout) 47 | >> refund_tx.add_in(refund_tx_in) 48 | 49 | >> refund_tx_out = Bitcoin::Protocol::TxOut.value_to_address(deposit_amount, alice_key.addr) 50 | >> refund_tx.add_out(refund_tx_out) 51 | 52 | >> refund_tx.in[0].sequence = [0].pack("V") 53 | >> refund_tx.lock_time = locktime 54 | 55 | #3 ボブが払い戻し用トランザクションに署名 56 | >> sig_hash = refund_tx.signature_hash_for_input(0, redeem_script) 57 | >> script_sig = Bitcoin::Script.to_p2sh_multisig_script_sig(redeem_script) 58 | 59 | >> script_sig_1 = Bitcoin::Script.add_sig_to_multisig_script_sig(bob_key.sign(sig_hash), script_sig) 60 | >> refund_tx.in[0].script_sig = script_sig_1 61 | 62 | #4 アリスがボブから払い戻し用トランザクションを受け取り、ボブの署名が正しくされているか検証 63 | >> refund_tx_copy = refund_tx 64 | 65 | >> script_sig_2 = Bitcoin::Script.add_sig_to_multisig_script_sig(alice_key.sign(sig_hash), script_sig_1) #アリスの署名を追加 66 | >> script_sig_3 = Bitcoin::Script.sort_p2sh_multisig_signatures(script_sig_2, sig_hash) #署名の順番を並び替え 67 | >> refund_tx_copy.in[0].script_sig = script_sig_3 68 | 69 | >> if refund_tx_copy.verify_input_signature(0, opening_tx) 70 | refund_tx = refund_tx_copy #署名の検証が正しい場合は、署名を確定する 71 | end 72 | 73 | #5 オープニングトランザクションのブロードキャスト 74 | >> api.provider.send_transaction(opening_tx.to_payload.bth) 75 | 76 | #6 コミットメントトランザクションの作成 77 | >> commitment_tx = Bitcoin::Protocol::Tx.new 78 | 79 | >> amount_to_bob = 300000000 80 | >> amount_to_alice = deposit_amount - amount_to_bob - tx_fee 81 | 82 | >> commitment_tx_in = Bitcoin::Protocol::TxIn.from_hex_hash(opening_tx.hash, opening_tx_vout) 83 | >> commitment_tx.add_in(refund_tx_in) 84 | 85 | >> commitment_tx_out_1 = Bitcoin::Protocol::TxOut.value_to_address(amount_to_bob, bob_key.addr) #ボブへの支払い 86 | >> commitment_tx_out_2 = Bitcoin::Protocol::TxOut.value_to_address(amount_to_alice, alice_key.addr) #アリスへのお釣り 87 | >> commitment_tx.add_out(commitment_tx_out_1) 88 | >> commitment_tx.add_out(commitment_tx_out_2) 89 | 90 | #7 アリスがコミットメントトランザクションに署名 91 | >> commitment_sig_hash = commitment_tx.signature_hash_for_input(0, redeem_script) 92 | >> commitment_script_sig = Bitcoin::Script.to_p2sh_multisig_script_sig(redeem_script) 93 | 94 | >> script_sig_a = Bitcoin::Script.add_sig_to_multisig_script_sig(alice_key.sign(commitment_sig_hash), commitment_script_sig) 95 | >> commitment_tx.in[0].script_sig = script_sig_a 96 | 97 | #8 ボブがアリスからコミットメントトランザクションを受け取り、アリスの署名が正しくされているか検証 98 | >> commitment_tx_copy = commitment_tx 99 | 100 | >> script_sig_b = Bitcoin::Script.add_sig_to_multisig_script_sig(bob_key.sign(commitment_sig_hash), script_sig_a) #ボブの署名を追加 101 | >> script_sig_c = Bitcoin::Script.sort_p2sh_multisig_signatures(script_sig_b, commitment_sig_hash) #署名の順番を並び替え 102 | >> commitment_tx_copy.in[0].script_sig = script_sig_c 103 | >> commitment_tx_copy.verify_input_signature(0, opening_tx) 104 | 105 | >> if commitment_tx_copy.verify_input_signature(0, opening_tx) 106 | commitment_tx = commitment_tx_copy #署名の検証が正しい場合は、署名を確定する 107 | end 108 | 109 | #9 コミットメントトランザクションのブロードキャスト 110 | >> api.provider.send_transaction(commitment_tx.to_payload.bth) 111 | ``` 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 書籍「ブロックチェーン・プログラミング 仮想通貨入門」 2 | 書籍[「ブロックチェーン・プログラミング 仮想通貨入門」](https://www.amazon.co.jp/dp/4061538314/)のサンプルやフィードバック用のリポジトリです 3 | 4 | 書籍の記述内容に関する質問などがあればIssueへ登録してください。必要に応じてWikiのQ&Aページで回答します。 5 | 6 | ## サンプルコード 7 | 8 | 書籍に掲載されているサンプルコートです。 9 | 10 | * [Chapter01](https://github.com/blockchain-programming/book/blob/master/Chapter01.md) 11 | * [Chapter03](https://github.com/blockchain-programming/book/blob/master/Chapter03.md) 12 | * [Chapter04](https://github.com/blockchain-programming/book/blob/master/Chapter04.md) 13 | * [Chapter05](https://github.com/blockchain-programming/book/blob/master/Chapter05.md) 14 | * [Chapter06](https://github.com/blockchain-programming/book/blob/master/Chapter06.md) 15 | * [Chapter07](https://github.com/blockchain-programming/book/blob/master/Chapter07.md) 16 | * [Chapter09](https://github.com/blockchain-programming/book/blob/master/Chapter09.md) 17 | * [Chapter10](https://github.com/blockchain-programming/book/blob/master/Chapter10.md) 18 | * [Chapter11](https://github.com/blockchain-programming/book/blob/master/Chapter11.md) 19 | 20 | ## [Q&A](https://github.com/blockchain-programming/book/wiki/Q&A) 21 | 22 | --------------------------------------------------------------------------------