├── 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 |
--------------------------------------------------------------------------------