btcruby 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +18 -0
- data/.travis.yml +7 -0
- data/FAQ.md +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +18 -0
- data/HOWTO.md +17 -0
- data/LICENSE +19 -0
- data/README.md +59 -0
- data/Rakefile +6 -0
- data/TODO.txt +40 -0
- data/bin/console +19 -0
- data/btcruby.gemspec +20 -0
- data/documentation/address.md +73 -0
- data/documentation/base58.md +52 -0
- data/documentation/block.md +127 -0
- data/documentation/block_header.md +120 -0
- data/documentation/constants.md +88 -0
- data/documentation/data.md +54 -0
- data/documentation/diagnostics.md +90 -0
- data/documentation/extensions.md +76 -0
- data/documentation/hash_functions.md +58 -0
- data/documentation/hash_id.md +22 -0
- data/documentation/index.md +230 -0
- data/documentation/key.md +177 -0
- data/documentation/keychain.md +180 -0
- data/documentation/network.md +75 -0
- data/documentation/opcode.md +220 -0
- data/documentation/openssl.md +7 -0
- data/documentation/p2pkh.md +71 -0
- data/documentation/p2sh.md +64 -0
- data/documentation/proof_of_work.md +84 -0
- data/documentation/script.md +280 -0
- data/documentation/signature.md +71 -0
- data/documentation/transaction.md +213 -0
- data/documentation/transaction_builder.md +188 -0
- data/documentation/transaction_input.md +133 -0
- data/documentation/transaction_output.md +130 -0
- data/documentation/wif.md +72 -0
- data/documentation/wire_format.md +70 -0
- data/lib/btcruby/address.rb +296 -0
- data/lib/btcruby/base58.rb +108 -0
- data/lib/btcruby/big_number.rb +47 -0
- data/lib/btcruby/block.rb +170 -0
- data/lib/btcruby/block_header.rb +231 -0
- data/lib/btcruby/constants.rb +59 -0
- data/lib/btcruby/currency_formatter.rb +64 -0
- data/lib/btcruby/data.rb +98 -0
- data/lib/btcruby/diagnostics.rb +92 -0
- data/lib/btcruby/errors.rb +8 -0
- data/lib/btcruby/extensions.rb +65 -0
- data/lib/btcruby/hash_functions.rb +54 -0
- data/lib/btcruby/hash_id.rb +18 -0
- data/lib/btcruby/key.rb +517 -0
- data/lib/btcruby/keychain.rb +464 -0
- data/lib/btcruby/network.rb +73 -0
- data/lib/btcruby/opcode.rb +197 -0
- data/lib/btcruby/open_assets/asset.rb +35 -0
- data/lib/btcruby/open_assets/asset_address.rb +49 -0
- data/lib/btcruby/open_assets/asset_definition.rb +75 -0
- data/lib/btcruby/open_assets/asset_id.rb +24 -0
- data/lib/btcruby/open_assets/asset_marker.rb +94 -0
- data/lib/btcruby/open_assets/asset_processor.rb +377 -0
- data/lib/btcruby/open_assets/asset_transaction.rb +184 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/errors.rb +15 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/provider.rb +32 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/result.rb +47 -0
- data/lib/btcruby/open_assets/asset_transaction_builder.rb +418 -0
- data/lib/btcruby/open_assets/asset_transaction_input.rb +64 -0
- data/lib/btcruby/open_assets/asset_transaction_output.rb +140 -0
- data/lib/btcruby/open_assets.rb +26 -0
- data/lib/btcruby/openssl.rb +536 -0
- data/lib/btcruby/proof_of_work.rb +110 -0
- data/lib/btcruby/safety.rb +26 -0
- data/lib/btcruby/script.rb +733 -0
- data/lib/btcruby/signature_hashtype.rb +37 -0
- data/lib/btcruby/transaction.rb +511 -0
- data/lib/btcruby/transaction_builder/errors.rb +15 -0
- data/lib/btcruby/transaction_builder/provider.rb +54 -0
- data/lib/btcruby/transaction_builder/result.rb +73 -0
- data/lib/btcruby/transaction_builder/signer.rb +28 -0
- data/lib/btcruby/transaction_builder.rb +520 -0
- data/lib/btcruby/transaction_input.rb +298 -0
- data/lib/btcruby/transaction_outpoint.rb +30 -0
- data/lib/btcruby/transaction_output.rb +315 -0
- data/lib/btcruby/version.rb +3 -0
- data/lib/btcruby/wif.rb +118 -0
- data/lib/btcruby/wire_format.rb +362 -0
- data/lib/btcruby.rb +44 -2
- data/sample_code/creating_a_p2sh_multisig_address.rb +21 -0
- data/sample_code/creating_a_transaction_manually.rb +44 -0
- data/sample_code/generating_an_address.rb +20 -0
- data/sample_code/using_transaction_builder.rb +49 -0
- data/spec/address_spec.rb +206 -0
- data/spec/all.rb +6 -0
- data/spec/base58_spec.rb +83 -0
- data/spec/block_header_spec.rb +18 -0
- data/spec/block_spec.rb +18 -0
- data/spec/currency_formatter_spec.rb +46 -0
- data/spec/data_spec.rb +50 -0
- data/spec/diagnostics_spec.rb +41 -0
- data/spec/key_spec.rb +205 -0
- data/spec/keychain_spec.rb +261 -0
- data/spec/network_spec.rb +48 -0
- data/spec/open_assets/asset_address_spec.rb +33 -0
- data/spec/open_assets/asset_id_spec.rb +15 -0
- data/spec/open_assets/asset_marker_spec.rb +47 -0
- data/spec/open_assets/asset_processor_spec.rb +567 -0
- data/spec/open_assets/asset_transaction_builder_spec.rb +273 -0
- data/spec/open_assets/asset_transaction_spec.rb +70 -0
- data/spec/proof_of_work_spec.rb +53 -0
- data/spec/script_spec.rb +66 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/transaction_builder_spec.rb +338 -0
- data/spec/transaction_spec.rb +162 -0
- data/spec/wire_format_spec.rb +283 -0
- metadata +141 -7
data/spec/key_spec.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe BTC::Key do
|
4
|
+
|
5
|
+
def verify_rfc6979_nonce(keyhex, msg, khex)
|
6
|
+
keybin = BTC::Data.data_from_hex(keyhex)
|
7
|
+
hash = BTC.sha256(msg)
|
8
|
+
k = BTC::OpenSSL.rfc6979_ecdsa_nonce(hash, keybin)
|
9
|
+
k.to_hex.must_equal khex
|
10
|
+
end
|
11
|
+
|
12
|
+
def verify_rfc6979_signature(keyhex, msg, sighex)
|
13
|
+
key = BTC::Key.new(private_key: keyhex.from_hex)
|
14
|
+
hash = BTC.sha256(msg)
|
15
|
+
sig = key.ecdsa_signature(hash)
|
16
|
+
sig.to_hex.must_equal sighex
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should use deterministic ECDSA nonce according to RFC6979" do
|
20
|
+
verify_rfc6979_nonce("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50",
|
21
|
+
"sample",
|
22
|
+
"2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3")
|
23
|
+
verify_rfc6979_nonce("0000000000000000000000000000000000000000000000000000000000000001",
|
24
|
+
"Satoshi Nakamoto",
|
25
|
+
"8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15")
|
26
|
+
verify_rfc6979_nonce("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
27
|
+
"Satoshi Nakamoto",
|
28
|
+
"33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90")
|
29
|
+
verify_rfc6979_nonce("f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
|
30
|
+
"Alan Turing",
|
31
|
+
"525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1")
|
32
|
+
verify_rfc6979_nonce("0000000000000000000000000000000000000000000000000000000000000001",
|
33
|
+
"All those moments will be lost in time, like tears in rain. Time to die...",
|
34
|
+
"38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3")
|
35
|
+
verify_rfc6979_nonce("e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
|
36
|
+
"There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
|
37
|
+
"1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979" do
|
41
|
+
verify_rfc6979_signature("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50",
|
42
|
+
"sample",
|
43
|
+
"3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124")
|
44
|
+
verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001",
|
45
|
+
"Satoshi Nakamoto",
|
46
|
+
"3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5")
|
47
|
+
verify_rfc6979_signature("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
48
|
+
"Satoshi Nakamoto",
|
49
|
+
"3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5")
|
50
|
+
verify_rfc6979_signature("f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
|
51
|
+
"Alan Turing",
|
52
|
+
"304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea")
|
53
|
+
verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001",
|
54
|
+
"All those moments will be lost in time, like tears in rain. Time to die...",
|
55
|
+
"30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21")
|
56
|
+
verify_rfc6979_signature("e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
|
57
|
+
"There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
|
58
|
+
"3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should perform Diffie-Hellman multiplication" do
|
62
|
+
alice = BTC::Key.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: true)
|
63
|
+
bob = BTC::Key.new(private_key: "2db963f0fe106f483d9afa73bd4e39a8ac4bbcb1fbec99d65bf59d85c8cb62ee".from_hex, public_key_compressed: true)
|
64
|
+
dh_pubkey1 = alice.diffie_hellman(bob)
|
65
|
+
dh_pubkey1.compressed_public_key.to_hex.must_equal "03735932754bc16e10febe40ee0280906d29459d477442f1838dcf27de3b5d9699"
|
66
|
+
|
67
|
+
dh_pubkey2 = bob.diffie_hellman(alice)
|
68
|
+
dh_pubkey2.compressed_public_key.to_hex.must_equal "03735932754bc16e10febe40ee0280906d29459d477442f1838dcf27de3b5d9699"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should support compressed public keys" do
|
72
|
+
k = BTC::Key.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: true)
|
73
|
+
k.private_key.to_hex.must_equal "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a"
|
74
|
+
k.public_key.to_hex.must_equal "0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71"
|
75
|
+
k.address.to_s.must_equal "1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8"
|
76
|
+
k.public_key_compressed.must_equal true
|
77
|
+
|
78
|
+
k.compressed_public_key.to_hex.must_equal "0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71"
|
79
|
+
k.uncompressed_public_key.to_hex.must_equal "0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455"
|
80
|
+
|
81
|
+
(k.compressed_key == k).must_equal true
|
82
|
+
(k.uncompressed_key == k).must_equal false
|
83
|
+
|
84
|
+
k.compressed_key.address.to_s.must_equal "1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8"
|
85
|
+
k.uncompressed_key.address.to_s.must_equal "1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should support uncompressed public keys" do
|
89
|
+
k = BTC::Key.new(private_key: BTC::Data.data_from_hex("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a"), public_key_compressed: false)
|
90
|
+
k.private_key.to_hex.must_equal "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a"
|
91
|
+
k.public_key.to_hex.must_equal "0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455"
|
92
|
+
k.address.to_s.must_equal "1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T"
|
93
|
+
k.public_key_compressed.must_equal false
|
94
|
+
|
95
|
+
k.compressed_public_key.to_hex.must_equal "0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71"
|
96
|
+
k.uncompressed_public_key.to_hex.must_equal "0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455"
|
97
|
+
|
98
|
+
(k.compressed_key == k).must_equal false
|
99
|
+
(k.uncompressed_key == k).must_equal true
|
100
|
+
|
101
|
+
k.compressed_key.address.to_s.must_equal "1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8"
|
102
|
+
k.uncompressed_key.address.to_s.must_equal "1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T"
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should sign and verify data using ECDSA signatures" do
|
106
|
+
300.times do |n|
|
107
|
+
message = "Test message #{n}"
|
108
|
+
private_key = "Key #{n}".sha256
|
109
|
+
|
110
|
+
key = BTC::Key.new(private_key: private_key)
|
111
|
+
|
112
|
+
signature = key.ecdsa_signature(message.sha256)
|
113
|
+
|
114
|
+
signature.bytesize.must_be :>=, 60
|
115
|
+
signature.bytesize.must_be :<=, 72
|
116
|
+
|
117
|
+
key2 = BTC::Key.new(public_key: key.public_key)
|
118
|
+
|
119
|
+
result = key2.verify_ecdsa_signature(signature, message.sha256)
|
120
|
+
result.must_equal true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should test canonicality of the signature" do
|
125
|
+
|
126
|
+
# Stress-test canonicality checks
|
127
|
+
key = BTC::Key.new(private_key: BTC.sha256("some key"))
|
128
|
+
2560.times do |i|
|
129
|
+
hash = BTC.sha256("tx#{i}")
|
130
|
+
sig = key.ecdsa_signature(hash) + "\x01".b
|
131
|
+
canonical = Key.validate_script_signature(sig)
|
132
|
+
if !canonical
|
133
|
+
puts Diagnostics.current.last_message
|
134
|
+
end
|
135
|
+
canonical.must_equal true
|
136
|
+
end
|
137
|
+
|
138
|
+
sig = "3045022100e81a33ac22d0ef25d359a5353977f0f953608b2733141239ec02363237ab6781022045c71237e95b56079e9fa88591060e4c1a4bb02c0cad1ebeb092749d4aa9754701".from_hex
|
139
|
+
canonical = Key.validate_script_signature(sig)
|
140
|
+
if !canonical
|
141
|
+
puts Diagnostics.current.last_message
|
142
|
+
end
|
143
|
+
canonical.must_equal true
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should test normalization of non-canonical signatures" do
|
147
|
+
|
148
|
+
# # Generate non-canonical signatures
|
149
|
+
# key = BTC::Key.new(private_key: BTC.sha256("some other key"))
|
150
|
+
# 2560.times do |i|
|
151
|
+
# hash = BTC.sha256("tx#{i}")
|
152
|
+
# sig = key.ecdsa_signature(hash, normalized: false) + "\x01".b
|
153
|
+
# if !Key.validate_script_signature(sig)
|
154
|
+
# puts sig[0, sig.size - 1].to_hex
|
155
|
+
# end
|
156
|
+
# end
|
157
|
+
%w[
|
158
|
+
30460221009bf6f0f7480d0562f5f56bc53bc90d564d7ce15ecbd116716c583da7311098b8022100880fd3e1c788c144aeede40072e814bead8b470a5a7f978a3804363b4920dc37
|
159
|
+
304502206700757f4609501016944a7b3144fee587848ad108c79dd4eb7664ac374d646c022100b762e1010f4300fb3b15cde575c80ad405314472812051e34823a5051bfc13fe
|
160
|
+
3046022100e71c70039b4f3c3894a3e5d20d1e19f95fc7b216a3ad2f540bd817470c07530e022100ebacde4827011195bd789edb6e3bca4dd0d5b59cae6892ee88465ebb205674b3
|
161
|
+
3045022000ef0203c4df709ab28f701a6c340ecce0c30d60b717096c7a360d4a801371fe022100cd83e86d6d5f0304d8ef428ed928a5af3fcfec652fb334bbfb9e2750c8ebb51a
|
162
|
+
3046022100cda613be5da7afc13aa6e04399557603b10317ca09e53a8d24c4ea0d892a0a9702210081a3012d810d6228c532fc8225dc32babda8f7b7f749c656b84af14e41a50ef2
|
163
|
+
3045022063ec43b0a6515abe17636058aac61b11b2e11becbb49439b711cb868788fe52e022100ed350ebafa430efc8e55285aa94ee8365345238596412ac085f9fc0898bb0003
|
164
|
+
3046022100d4890791c4b9f9fc7a9e853da404df26ab2302c5046527faaf9ae9fd8c99213a022100daec31bda7aa8ce0d9459e14d9fe86feda806ff90b6dd9701a6d94fad04a2860
|
165
|
+
304502203ee680db8f247e005e84bb0db8e0a39bf47d32282c730b0f1b003c1f32891730022100d69b240494d0562de75b3916acc023711cc37c30d70760ab1fc35e8931738410
|
166
|
+
304502206f08b37b53fd27096161015c53e20e05e01af59a32333e2bb79fb629479bd3800221009b125d988cd210c2438e6a206f5b9ddd7b6eae67accb0b914fde6b2a80c39b21
|
167
|
+
3046022100bf94dadbff4a67716955fcc4e22c3c4c88852ef31c5a0c1145ad3d433c97705d022100f2c339e6a1cc6ce75fda29b9d47dc2e7f6fb1df4a2f5e8502a75faf63f2cc87b
|
168
|
+
3046022100cf268bfa6c9b0a10747979654e386fb64d18bd2ae69e549a0545dc2fe154a6cb022100f651c4389233482f8b99f39f2e2d1fd29c08795f0dad50de47860bca29a4300d
|
169
|
+
304502203a969e1650b758a6597c01beb050ae737838c24376fcf0cf8fb66b2bb1b291a7022100b59ba04b19a00becb4e41e964cec1c29fd5d080be53d844b020d613a71ae7f65
|
170
|
+
304602210085234fe98f3bb43e665b2d138de16a265751b83851b1fc50478359c731b9a185022100db2a37784987554344071e60df361d847e1ee2c18844321a7a1edb9a0b53cd92
|
171
|
+
3046022100b07a37a66c5db8c718147d16b74fbba048e632911be0db380821c399e4823702022100ff67919de148346fdb4d25676d4c4e42a5002002c9c7e20176783ceb46e08a02
|
172
|
+
3046022100a962f13335383fa0a2ad2c38568fe87301dcc7b8e78cafc05dc3e805c0ecb02b022100c15a38847cc259b0f616cb477ecde0ea0fe8767f0aac1e630e314fcde4c65487
|
173
|
+
3046022100a8ba33f415f456c7f8daac01824a17612927c4c2b35876caf26b63e33fa17b99022100b4e89ea2021afd554f5f048cc5b9208bba55e727133ca184b51de121654efb33
|
174
|
+
3045022079b58a6574851d07ab8779a6a6597c326f9181852f88156546ce2331f95bc5b2022100dea5a3b8f454a781e444854416b2a42bacc02d1d21ad385022f50b439c98c076
|
175
|
+
304502201ff1e2b23342bc5d2960822247084fb86e4be9a37bb0a6a9aad512b6627c2e0a0221008624ceebb150e1de1103b51e1ac533f43e8e7322a37c925baae0dcc90e272ea2
|
176
|
+
304502205ee578855612719d0f4a7bcaf149d5f283a12176e99a27042d9d8f4a9074f2dd022100f4d7b7b923de4d63e646bb6890ef3634bc317c6dd04e55f2ffa686e32574ccb7
|
177
|
+
3046022100c2e4f75fd6c294c03dd684608212ec65240bd3319abe20663c7a24676ad8ef600221008743b32fd6051c2eaf326f2e824d3a8ca0ebdc1aa25175ee6b61c1f694b9cfbd
|
178
|
+
30450220450d1e68977e2ad2776925725e444eb9a58743459a4fd15188552cfc171f8798022100edac0b9210f02f0d8d4201729c8d62c7c302cb7c1a6f9f2f7741c7f99a3518a2
|
179
|
+
3045022018cddb05aa87011d7fe3ba56d6657bd813eeab9c43e2422af2f44cdd3bd8bc3a022100c53eb96ba81e12e59da73846223310ea6cb325a42ab2a081a101d99c5e3bfeec
|
180
|
+
304502200be9a279cca265b4e273c165d877474ab854b14ce2b17dff958dc932eae3d9e8022100e45c9087e78ff0120ae2f911299f64c995c61d6750a5913eaac36f096b560685
|
181
|
+
3045022062d08bff9580238c8cd62d9d64e9c1d518932886651f46a445bff773993500f00221008029263cc9ec64589bbbe140995096c257a1fbacf5f9f5ee69a69b96c626cc7a
|
182
|
+
].each do |hex_sig|
|
183
|
+
sig = hex_sig.from_hex
|
184
|
+
canonical = Key.validate_script_signature(sig + "\x01".b)
|
185
|
+
canonical.must_equal false
|
186
|
+
sig2 = Key.normalized_signature(sig)
|
187
|
+
sig2.wont_equal nil
|
188
|
+
sig2.wont_equal sig
|
189
|
+
canonical = Key.validate_script_signature(sig2 + "\x01".b)
|
190
|
+
if !canonical
|
191
|
+
puts Diagnostics.current.last_message
|
192
|
+
end
|
193
|
+
canonical.must_equal true
|
194
|
+
|
195
|
+
# Non-canonical signature must be normalized
|
196
|
+
sig3 = Key.validate_and_normalize_script_signature(sig + "\x01".b)
|
197
|
+
sig3.must_equal sig2 + "\x01".b
|
198
|
+
|
199
|
+
# Canonical signature should stay the same
|
200
|
+
sig4 = Key.validate_and_normalize_script_signature(sig2 + "\x01".b)
|
201
|
+
sig4.must_equal sig2 + "\x01".b
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe BTC::Keychain do
|
4
|
+
|
5
|
+
it "should support tpub/tprv prefixes for testnet" do
|
6
|
+
seed = "000102030405060708090a0b0c0d0e0f".from_hex
|
7
|
+
master = Keychain.new(seed: seed)
|
8
|
+
master.network = Network.testnet
|
9
|
+
master.xpub.must_equal "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp"
|
10
|
+
master.xprv.must_equal "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m"
|
11
|
+
master.network = Network.mainnet
|
12
|
+
master.xpub.must_equal "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
|
13
|
+
master.xprv.must_equal "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should support path API" do
|
17
|
+
seed = "000102030405060708090a0b0c0d0e0f".from_hex
|
18
|
+
master = Keychain.new(seed: seed)
|
19
|
+
master.derived_keychain("").xpub.must_equal "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
|
20
|
+
master.derived_keychain("m").xpub.must_equal "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
|
21
|
+
master.derived_keychain("0'").xpub.must_equal "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
|
22
|
+
master.derived_keychain("m/0'").xpub.must_equal "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
|
23
|
+
master.derived_keychain("0'").xpub.must_equal "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
|
24
|
+
master.derived_keychain("m/0'/1").xpub.must_equal "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
|
25
|
+
master.derived_keychain("0'/1").xpub.must_equal "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
|
26
|
+
master.derived_keychain("m/0'/1/2'").xprv.must_equal "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"
|
27
|
+
master.derived_keychain("0'/1/2'").xprv.must_equal "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"
|
28
|
+
master.derived_keychain("m/0'/1/2'/2/1000000000").xpub.must_equal "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
|
29
|
+
master.derived_keychain("0'/1/2'/2/1000000000").xprv.must_equal "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should support test vector 1" do
|
33
|
+
# Master (hex): 000102030405060708090a0b0c0d0e0f
|
34
|
+
# * [Chain m]
|
35
|
+
# * ext pub: xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8
|
36
|
+
# * ext prv: xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi
|
37
|
+
# * [Chain m/0']
|
38
|
+
# * ext pub: xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw
|
39
|
+
# * ext prv: xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7
|
40
|
+
# * [Chain m/0'/1]
|
41
|
+
# * ext pub: xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ
|
42
|
+
# * ext prv: xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs
|
43
|
+
# * [Chain m/0'/1/2']
|
44
|
+
# * ext pub: xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5
|
45
|
+
# * ext prv: xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM
|
46
|
+
# * [Chain m/0'/1/2'/2]
|
47
|
+
# * ext pub: xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV
|
48
|
+
# * ext prv: xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334
|
49
|
+
# * [Chain m/0'/1/2'/2/1000000000]
|
50
|
+
# * ext pub: xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy
|
51
|
+
# * ext prv: xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76
|
52
|
+
|
53
|
+
seed = "000102030405060708090a0b0c0d0e0f".from_hex
|
54
|
+
master = Keychain.new(seed: seed)
|
55
|
+
|
56
|
+
master.key.address.to_s.must_equal "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"
|
57
|
+
master.key.address.to_s.must_equal master.public_keychain.key.address.to_s
|
58
|
+
|
59
|
+
master.parent_fingerprint.must_equal 0
|
60
|
+
master.identifier.to_hex.must_equal "3442193e1bb70916e914552172cd4e2dbc9df811"
|
61
|
+
master.fingerprint.must_equal 876747070
|
62
|
+
master.depth.must_equal 0
|
63
|
+
master.index.must_equal 0
|
64
|
+
master.hardened?.must_equal false
|
65
|
+
master.mainnet?.must_equal true
|
66
|
+
master.testnet?.must_equal false
|
67
|
+
master.private?.must_equal true
|
68
|
+
master.hardened?.must_equal false
|
69
|
+
|
70
|
+
master.xpub.must_equal "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
|
71
|
+
master.xprv.must_equal "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
|
72
|
+
|
73
|
+
master2 = Keychain.new(xprv: master.extended_private_key)
|
74
|
+
master2.private?.must_equal true
|
75
|
+
(master2 == master).must_equal true
|
76
|
+
master2.xpub.must_equal master.xpub
|
77
|
+
master2.xprv.must_equal master.xprv
|
78
|
+
|
79
|
+
m0prv = master.derived_keychain(0, hardened:true)
|
80
|
+
|
81
|
+
m0prv.parent_fingerprint.wont_equal 0
|
82
|
+
m0prv.depth.must_equal 1
|
83
|
+
m0prv.index.must_equal 0
|
84
|
+
m0prv.private?.must_equal true
|
85
|
+
m0prv.hardened?.must_equal true
|
86
|
+
|
87
|
+
m0prv.extended_public_key.must_equal "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
|
88
|
+
m0prv.extended_private_key.must_equal "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"
|
89
|
+
|
90
|
+
m0prv1pub = m0prv.derived_keychain(1)
|
91
|
+
|
92
|
+
m0prv1pub.parent_fingerprint.wont_equal 0
|
93
|
+
m0prv1pub.depth.must_equal 2
|
94
|
+
m0prv1pub.index.must_equal 1
|
95
|
+
m0prv1pub.private?.must_equal true
|
96
|
+
m0prv1pub.hardened?.must_equal false
|
97
|
+
|
98
|
+
m0prv1pub.extended_public_key.must_equal "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
|
99
|
+
m0prv1pub.extended_private_key.must_equal "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"
|
100
|
+
|
101
|
+
m0prv1pub2prv = m0prv1pub.derived_keychain(2, hardened:true)
|
102
|
+
m0prv1pub2prv.extended_public_key.must_equal "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"
|
103
|
+
m0prv1pub2prv.extended_private_key.must_equal "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"
|
104
|
+
|
105
|
+
m0prv1pub2prv2pub = m0prv1pub2prv.derived_keychain(2)
|
106
|
+
m0prv1pub2prv2pub.extended_public_key.must_equal "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"
|
107
|
+
m0prv1pub2prv2pub.extended_private_key.must_equal "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"
|
108
|
+
|
109
|
+
m0prv1pub2prv2pub1Gpub = m0prv1pub2prv2pub.derived_keychain(1000000000)
|
110
|
+
m0prv1pub2prv2pub1Gpub.extended_public_key.must_equal "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
|
111
|
+
m0prv1pub2prv2pub1Gpub.extended_private_key.must_equal "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"
|
112
|
+
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should support test vector 2" do
|
117
|
+
# Master (hex): fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542
|
118
|
+
# * [Chain m]
|
119
|
+
# * ext pub: xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB
|
120
|
+
# * ext prv: xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U
|
121
|
+
# * [Chain m/0]
|
122
|
+
# * ext pub: xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH
|
123
|
+
# * ext prv: xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt
|
124
|
+
# * [Chain m/0/2147483647']
|
125
|
+
# * ext pub: xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a
|
126
|
+
# * ext prv: xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9
|
127
|
+
# * [Chain m/0/2147483647'/1]
|
128
|
+
# * ext pub: xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon
|
129
|
+
# * ext prv: xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef
|
130
|
+
# * [Chain m/0/2147483647'/1/2147483646']
|
131
|
+
# * ext pub: xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL
|
132
|
+
# * ext prv: xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc
|
133
|
+
# * [Chain m/0/2147483647'/1/2147483646'/2]
|
134
|
+
# * ext pub: xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt
|
135
|
+
# * ext prv: xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j
|
136
|
+
|
137
|
+
seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex
|
138
|
+
master = Keychain.new(seed: seed)
|
139
|
+
|
140
|
+
master.parent_fingerprint.must_equal 0
|
141
|
+
master.identifier.to_hex.must_equal "bd16bee53961a47d6ad888e29545434a89bdfe95"
|
142
|
+
master.fingerprint.must_equal 3172384485
|
143
|
+
master.depth.must_equal 0
|
144
|
+
master.index.must_equal 0
|
145
|
+
master.hardened?.must_equal false
|
146
|
+
master.mainnet?.must_equal true
|
147
|
+
master.testnet?.must_equal false
|
148
|
+
master.private?.must_equal true
|
149
|
+
master.hardened?.must_equal false
|
150
|
+
|
151
|
+
master.extended_public_key.must_equal "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"
|
152
|
+
master.extended_private_key.must_equal "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"
|
153
|
+
|
154
|
+
m0pub = master.derived_keychain(0)
|
155
|
+
m0pub.extended_public_key.must_equal "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"
|
156
|
+
m0pub.extended_private_key.must_equal "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"
|
157
|
+
|
158
|
+
m0pubFFprv = m0pub.derived_keychain(2147483647, hardened:true)
|
159
|
+
m0pubFFprv.extended_public_key.must_equal "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"
|
160
|
+
m0pubFFprv.extended_private_key.must_equal "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"
|
161
|
+
|
162
|
+
m0pubFFprv1 = m0pubFFprv.derived_keychain(1)
|
163
|
+
m0pubFFprv1.extended_public_key.must_equal "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"
|
164
|
+
m0pubFFprv1.extended_private_key.must_equal "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"
|
165
|
+
|
166
|
+
m0pubFFprv1pubFEprv = m0pubFFprv1.derived_keychain(2147483646, hardened:true)
|
167
|
+
m0pubFFprv1pubFEprv.extended_public_key.must_equal "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"
|
168
|
+
m0pubFFprv1pubFEprv.extended_private_key.must_equal "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"
|
169
|
+
|
170
|
+
m0pubFFprv1pubFEprv2 = m0pubFFprv1pubFEprv.derived_keychain(2)
|
171
|
+
m0pubFFprv1pubFEprv2.extended_public_key.must_equal "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"
|
172
|
+
m0pubFFprv1pubFEprv2.extended_private_key.must_equal "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should behave correctly on privkeys below 32-byte size" do
|
176
|
+
keychain = Keychain.new(seed: "stress test")
|
177
|
+
#puts keychain.extended_private_key
|
178
|
+
# Uncomment this to figure out the indexes for the shorter keys
|
179
|
+
if false
|
180
|
+
indexes = []
|
181
|
+
10000.times do |i|
|
182
|
+
key = keychain.derived_key(i, hardened: true)
|
183
|
+
key.private_key.bytesize.must_equal 32
|
184
|
+
key.public_key.bytesize.must_equal 33
|
185
|
+
if key.private_key.bytes[0] == 0
|
186
|
+
indexes << i
|
187
|
+
puts "i = #{i} " + key.private_key.to_hex + " #{key.address.to_s}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
puts "Short private key indexes: #{indexes.inspect}"
|
191
|
+
end
|
192
|
+
|
193
|
+
# These indexes are brute-forced in the block above.
|
194
|
+
[70, 227, 455, 524, 530, 583,
|
195
|
+
1150, 1193, 1351, 1987,
|
196
|
+
2209, 2320, 2703, 2800, 2984,
|
197
|
+
3029, 3203, 3275, 3472, 3526, 3896, 3900,
|
198
|
+
4070, 4236, 4670, 4831, 4929,
|
199
|
+
5233, 5254, 5301, 5609, 5980,
|
200
|
+
6202, 6283, 6313, 6430, 6951,
|
201
|
+
7056, 7060, 7211, 7274, 7311, 7614, 7897,
|
202
|
+
8313, 8328, 8329, 8840, 8950, 8996,
|
203
|
+
9323, 9354].each do |i|
|
204
|
+
key = keychain.derived_key(i, hardened: true)
|
205
|
+
key.private_key.bytesize.must_equal 32
|
206
|
+
key.public_key.bytesize.must_equal 33
|
207
|
+
key.private_key.bytes[0].must_equal 0
|
208
|
+
key2 = keychain.derived_key(i, hardened: false)
|
209
|
+
key2.private_key.bytesize.must_equal 32
|
210
|
+
key2.public_key.bytesize.must_equal 33
|
211
|
+
end
|
212
|
+
|
213
|
+
# same as BIP32.org and CoreBitcoin
|
214
|
+
keychain.derived_key(70, hardened: true).address.to_s.must_equal '1FZQfsXwAoUcn9WVwbfRb4jMMkPJEozLWH'
|
215
|
+
keychain.derived_key(70, hardened: true).private_key.bytes[0].must_equal 0x00
|
216
|
+
keychain.derived_key(227, hardened: true).address.to_s.must_equal '1LRbeWJC3sLGRk7ob82djVYTNhsH2UdR4f'
|
217
|
+
keychain.derived_key(227, hardened: true).private_key.bytes[0].must_equal 0x00
|
218
|
+
keychain.derived_key(455, hardened: true).address.to_s.must_equal '1HSr4B5Hr3hc7vAzNHbp7SV7rsFzUhQSeF'
|
219
|
+
keychain.derived_key(455, hardened: true).private_key.bytes[0].must_equal 0x00
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should verify a certain regression test" do
|
223
|
+
extprv = "xprv9s21ZrQH143K3ZhiFsU612wiYCnd5miCTnWRMRJCmbTUxnn3F2WXuTXcoEyWpsit8ZqS5ddNvsoaEQuwzNwH8nmVDS24NwHbiu5oCrj85Kz"
|
224
|
+
keychain = Keychain.new(xprv: extprv)
|
225
|
+
key0 = keychain.derived_keychain(0, hardened: false).key
|
226
|
+
# puts "UNCOMPR: #{key0.uncompressed_key.address.to_s}"
|
227
|
+
# puts " COMPR: #{key0.compressed_key.address.to_s}"
|
228
|
+
# puts "DEFAULT: #{key0.address.to_s}"
|
229
|
+
# Test reports: 1MLjpNJZ3KZUdd5J9ZVnhxjFioC8DnhSr4
|
230
|
+
# BIP32.org reports: 15aALBTZkDrW8iZBKXrUHQo9dPJtGPEHSy
|
231
|
+
key0.address.to_s.must_equal "15aALBTZkDrW8iZBKXrUHQo9dPJtGPEHSy"
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should support conversion to public keychain" do
|
235
|
+
seed = "000102030405060708090a0b0c0d0e0f".from_hex
|
236
|
+
master = Keychain.new(seed: seed)
|
237
|
+
|
238
|
+
m0prv = master.derived_keychain(0, hardened:true)
|
239
|
+
m0prv_pub = m0prv.public_keychain
|
240
|
+
|
241
|
+
m0prv.private?.must_equal true
|
242
|
+
m0prv.public?.must_equal false
|
243
|
+
m0prv.hardened?.must_equal true
|
244
|
+
|
245
|
+
m0prv_pub.extended_public_key.must_equal m0prv.extended_public_key
|
246
|
+
m0prv_pub.extended_private_key.must_equal nil
|
247
|
+
m0prv_pub.private?.must_equal false
|
248
|
+
m0prv_pub.public?.must_equal true
|
249
|
+
m0prv_pub.hardened?.must_equal true
|
250
|
+
|
251
|
+
m0prv_pub2 = Keychain.new(extended_key: m0prv.extended_public_key)
|
252
|
+
m0prv_pub2.must_equal m0prv_pub
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should support public-only derivation" do
|
256
|
+
keychain = Keychain.new(xpub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB")
|
257
|
+
m0pub = keychain.derived_keychain(0)
|
258
|
+
m0pub.extended_public_key.must_equal "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"
|
259
|
+
m0pub.extended_private_key.must_equal nil
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe BTC::Network do
|
4
|
+
|
5
|
+
it "should have mainnet shared instance" do
|
6
|
+
mainnet = BTC::Network.mainnet
|
7
|
+
mainnet.name.must_equal "mainnet"
|
8
|
+
mainnet.mainnet?.must_equal true
|
9
|
+
mainnet.mainnet.must_equal true
|
10
|
+
mainnet.testnet?.must_equal false
|
11
|
+
mainnet.testnet.must_equal false
|
12
|
+
mainnet.genesis_block.block_id.must_equal "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
13
|
+
mainnet.genesis_block_header.block_id.must_equal "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
14
|
+
mainnet.max_target.must_equal 0x00000000ffff0000000000000000000000000000000000000000000000000000
|
15
|
+
BTC::Network.mainnet.object_id.must_equal BTC::Network.mainnet.object_id
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have testnet shared instance" do
|
19
|
+
testnet = BTC::Network.testnet
|
20
|
+
testnet.name.must_equal "testnet3"
|
21
|
+
testnet.mainnet?.must_equal false
|
22
|
+
testnet.mainnet.must_equal false
|
23
|
+
testnet.testnet?.must_equal true
|
24
|
+
testnet.testnet.must_equal true
|
25
|
+
testnet.genesis_block.block_id.must_equal "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
26
|
+
testnet.genesis_block_header.block_id.must_equal "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
27
|
+
testnet.max_target.must_equal 0x00000007fff80000000000000000000000000000000000000000000000000000
|
28
|
+
BTC::Network.testnet.object_id.must_equal BTC::Network.testnet.object_id
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should allow copying" do
|
32
|
+
network = BTC::Network.testnet.dup
|
33
|
+
network.object_id.wont_equal BTC::Network.testnet.object_id
|
34
|
+
|
35
|
+
network.name.must_equal "testnet3"
|
36
|
+
network.mainnet?.must_equal false
|
37
|
+
network.testnet?.must_equal true
|
38
|
+
|
39
|
+
network.name = "xnetwork"
|
40
|
+
network.mainnet = true
|
41
|
+
network.mainnet?.must_equal true
|
42
|
+
network.testnet?.must_equal false
|
43
|
+
|
44
|
+
BTC::Network.testnet.mainnet?.must_equal false
|
45
|
+
BTC::Network.testnet.testnet?.must_equal true
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe BTC::AssetAddress do
|
4
|
+
it "should encode bitcoin address to a correct asset address" do
|
5
|
+
btc_address = Address.parse("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")
|
6
|
+
asset_address = AssetAddress.new(bitcoin_address: btc_address)
|
7
|
+
asset_address.bitcoin_address.must_equal btc_address
|
8
|
+
asset_address.to_s.must_equal "akB4NBW9UuCmHuepksob6yfZs6naHtRCPNy"
|
9
|
+
asset_address.mainnet?.must_equal true
|
10
|
+
end
|
11
|
+
it "should decode an asset address" do
|
12
|
+
asset_address = Address.parse("akB4NBW9UuCmHuepksob6yfZs6naHtRCPNy")
|
13
|
+
asset_address.bitcoin_address.to_s.must_equal "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM"
|
14
|
+
asset_address.mainnet?.must_equal true
|
15
|
+
end
|
16
|
+
it "should allow instantiating Asset Address with an Asset Address" do
|
17
|
+
asset_address = AssetAddress.new(bitcoin_address: "akB4NBW9UuCmHuepksob6yfZs6naHtRCPNy")
|
18
|
+
asset_address.to_s.must_equal "akB4NBW9UuCmHuepksob6yfZs6naHtRCPNy"
|
19
|
+
asset_address.bitcoin_address.to_s.must_equal "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM"
|
20
|
+
asset_address.mainnet?.must_equal true
|
21
|
+
end
|
22
|
+
it "should support P2SH address for assets" do
|
23
|
+
asset_address = AssetAddress.new(bitcoin_address: "3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX")
|
24
|
+
asset_address.to_s.must_equal "anQin2TDYaubr6M5MQM8kNXMitHc2hsmfGc"
|
25
|
+
asset_address.bitcoin_address.to_s.must_equal "3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"
|
26
|
+
asset_address.mainnet?.must_equal true
|
27
|
+
|
28
|
+
asset_address = Address.parse("anQin2TDYaubr6M5MQM8kNXMitHc2hsmfGc")
|
29
|
+
asset_address.class.must_equal(AssetAddress)
|
30
|
+
asset_address.bitcoin_address.to_s.must_equal "3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"
|
31
|
+
asset_address.mainnet?.must_equal true
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe BTC::AssetID do
|
4
|
+
it "should encode script to a correct address" do
|
5
|
+
key = Key.new(private_key: "18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725".from_hex, public_key_compressed: false)
|
6
|
+
asset_id = AssetID.new(script: key.address(network: Network.mainnet).script, network: Network.mainnet)
|
7
|
+
asset_id.to_s.must_equal "ALn3aK1fSuG27N96UGYB1kUYUpGKRhBuBC"
|
8
|
+
asset_id.is_a?(BTC::AssetID).must_equal true
|
9
|
+
end
|
10
|
+
it "should decode an asset address" do
|
11
|
+
asset_id = Address.parse("ALn3aK1fSuG27N96UGYB1kUYUpGKRhBuBC")
|
12
|
+
asset_id.is_a?(BTC::AssetID).must_equal true
|
13
|
+
asset_id.hash.must_equal "36e0ea8e93eaa0285d641305f4c81e563aa570a2".from_hex
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe BTC::AssetMarker do
|
4
|
+
it "should decode marker output" do
|
5
|
+
# Data in the marker output Description
|
6
|
+
# ----------------------------- -------------------------------------------------------------------
|
7
|
+
# 0x6a The OP_RETURN opcode.
|
8
|
+
# 0x11 The PUSHDATA opcode for a 17 bytes payload.
|
9
|
+
# 0x4f 0x41 The Open Assets Protocol tag.
|
10
|
+
# 0x01 0x00 Version 1 of the protocol.
|
11
|
+
# 0x03 There are 3 items in the asset quantity list.
|
12
|
+
# 0xac 0x02 0x00 0xe5 0x8e 0x26 The asset quantity list:
|
13
|
+
# - '0xac 0x02' means output 0 has an asset quantity of 300.
|
14
|
+
# - Output 1 is skipped and has an asset quantity of 0
|
15
|
+
# because it is the marker output.
|
16
|
+
# - '0x00' means output 2 has an asset quantity of 0.
|
17
|
+
# - '0xe5 0x8e 0x26' means output 3 has an asset quantity of 624,485.
|
18
|
+
# - Outputs after output 3 (if any) have an asset quantity of 0.
|
19
|
+
# 0x05 The metadata is 5 bytes long.
|
20
|
+
# 0x48 0x65 0x6c 0x6c 0x6f Some arbitrary metadata.
|
21
|
+
output = TransactionOutput.new(value: 0, script: Script.new << OP_RETURN << "4f41010003ac0200e58e260548656c6c6f".from_hex)
|
22
|
+
marker = AssetMarker.new(output: output)
|
23
|
+
marker.quantities.must_equal [300, 0, 624_485]
|
24
|
+
marker.metadata.must_equal "Hello"
|
25
|
+
|
26
|
+
marker = AssetMarker.new(script: output.script)
|
27
|
+
marker.quantities.must_equal [300, 0, 624_485]
|
28
|
+
marker.metadata.must_equal "Hello"
|
29
|
+
|
30
|
+
marker = AssetMarker.new(data: output.script.op_return_data)
|
31
|
+
marker.quantities.must_equal [300, 0, 624_485]
|
32
|
+
marker.metadata.must_equal "Hello"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should encode marker output" do
|
36
|
+
marker = AssetMarker.new
|
37
|
+
marker.data.must_equal "4f4101000000".from_hex
|
38
|
+
marker.metadata = "Hello"
|
39
|
+
marker.data.must_equal "4f410100000548656c6c6f".from_hex
|
40
|
+
marker.quantities = [1]
|
41
|
+
marker.data.must_equal "4f41010001010548656c6c6f".from_hex
|
42
|
+
marker.quantities = [1, 2]
|
43
|
+
marker.data.must_equal "4f4101000201020548656c6c6f".from_hex
|
44
|
+
marker.quantities = [300, 0, 624_485]
|
45
|
+
marker.data.must_equal "4f41010003ac0200e58e260548656c6c6f".from_hex
|
46
|
+
end
|
47
|
+
end
|