btcruby 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -0
  3. data/.travis.yml +7 -0
  4. data/FAQ.md +7 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +18 -0
  7. data/HOWTO.md +17 -0
  8. data/LICENSE +19 -0
  9. data/README.md +59 -0
  10. data/Rakefile +6 -0
  11. data/TODO.txt +40 -0
  12. data/bin/console +19 -0
  13. data/btcruby.gemspec +20 -0
  14. data/documentation/address.md +73 -0
  15. data/documentation/base58.md +52 -0
  16. data/documentation/block.md +127 -0
  17. data/documentation/block_header.md +120 -0
  18. data/documentation/constants.md +88 -0
  19. data/documentation/data.md +54 -0
  20. data/documentation/diagnostics.md +90 -0
  21. data/documentation/extensions.md +76 -0
  22. data/documentation/hash_functions.md +58 -0
  23. data/documentation/hash_id.md +22 -0
  24. data/documentation/index.md +230 -0
  25. data/documentation/key.md +177 -0
  26. data/documentation/keychain.md +180 -0
  27. data/documentation/network.md +75 -0
  28. data/documentation/opcode.md +220 -0
  29. data/documentation/openssl.md +7 -0
  30. data/documentation/p2pkh.md +71 -0
  31. data/documentation/p2sh.md +64 -0
  32. data/documentation/proof_of_work.md +84 -0
  33. data/documentation/script.md +280 -0
  34. data/documentation/signature.md +71 -0
  35. data/documentation/transaction.md +213 -0
  36. data/documentation/transaction_builder.md +188 -0
  37. data/documentation/transaction_input.md +133 -0
  38. data/documentation/transaction_output.md +130 -0
  39. data/documentation/wif.md +72 -0
  40. data/documentation/wire_format.md +70 -0
  41. data/lib/btcruby/address.rb +296 -0
  42. data/lib/btcruby/base58.rb +108 -0
  43. data/lib/btcruby/big_number.rb +47 -0
  44. data/lib/btcruby/block.rb +170 -0
  45. data/lib/btcruby/block_header.rb +231 -0
  46. data/lib/btcruby/constants.rb +59 -0
  47. data/lib/btcruby/currency_formatter.rb +64 -0
  48. data/lib/btcruby/data.rb +98 -0
  49. data/lib/btcruby/diagnostics.rb +92 -0
  50. data/lib/btcruby/errors.rb +8 -0
  51. data/lib/btcruby/extensions.rb +65 -0
  52. data/lib/btcruby/hash_functions.rb +54 -0
  53. data/lib/btcruby/hash_id.rb +18 -0
  54. data/lib/btcruby/key.rb +517 -0
  55. data/lib/btcruby/keychain.rb +464 -0
  56. data/lib/btcruby/network.rb +73 -0
  57. data/lib/btcruby/opcode.rb +197 -0
  58. data/lib/btcruby/open_assets/asset.rb +35 -0
  59. data/lib/btcruby/open_assets/asset_address.rb +49 -0
  60. data/lib/btcruby/open_assets/asset_definition.rb +75 -0
  61. data/lib/btcruby/open_assets/asset_id.rb +24 -0
  62. data/lib/btcruby/open_assets/asset_marker.rb +94 -0
  63. data/lib/btcruby/open_assets/asset_processor.rb +377 -0
  64. data/lib/btcruby/open_assets/asset_transaction.rb +184 -0
  65. data/lib/btcruby/open_assets/asset_transaction_builder/errors.rb +15 -0
  66. data/lib/btcruby/open_assets/asset_transaction_builder/provider.rb +32 -0
  67. data/lib/btcruby/open_assets/asset_transaction_builder/result.rb +47 -0
  68. data/lib/btcruby/open_assets/asset_transaction_builder.rb +418 -0
  69. data/lib/btcruby/open_assets/asset_transaction_input.rb +64 -0
  70. data/lib/btcruby/open_assets/asset_transaction_output.rb +140 -0
  71. data/lib/btcruby/open_assets.rb +26 -0
  72. data/lib/btcruby/openssl.rb +536 -0
  73. data/lib/btcruby/proof_of_work.rb +110 -0
  74. data/lib/btcruby/safety.rb +26 -0
  75. data/lib/btcruby/script.rb +733 -0
  76. data/lib/btcruby/signature_hashtype.rb +37 -0
  77. data/lib/btcruby/transaction.rb +511 -0
  78. data/lib/btcruby/transaction_builder/errors.rb +15 -0
  79. data/lib/btcruby/transaction_builder/provider.rb +54 -0
  80. data/lib/btcruby/transaction_builder/result.rb +73 -0
  81. data/lib/btcruby/transaction_builder/signer.rb +28 -0
  82. data/lib/btcruby/transaction_builder.rb +520 -0
  83. data/lib/btcruby/transaction_input.rb +298 -0
  84. data/lib/btcruby/transaction_outpoint.rb +30 -0
  85. data/lib/btcruby/transaction_output.rb +315 -0
  86. data/lib/btcruby/version.rb +3 -0
  87. data/lib/btcruby/wif.rb +118 -0
  88. data/lib/btcruby/wire_format.rb +362 -0
  89. data/lib/btcruby.rb +44 -2
  90. data/sample_code/creating_a_p2sh_multisig_address.rb +21 -0
  91. data/sample_code/creating_a_transaction_manually.rb +44 -0
  92. data/sample_code/generating_an_address.rb +20 -0
  93. data/sample_code/using_transaction_builder.rb +49 -0
  94. data/spec/address_spec.rb +206 -0
  95. data/spec/all.rb +6 -0
  96. data/spec/base58_spec.rb +83 -0
  97. data/spec/block_header_spec.rb +18 -0
  98. data/spec/block_spec.rb +18 -0
  99. data/spec/currency_formatter_spec.rb +46 -0
  100. data/spec/data_spec.rb +50 -0
  101. data/spec/diagnostics_spec.rb +41 -0
  102. data/spec/key_spec.rb +205 -0
  103. data/spec/keychain_spec.rb +261 -0
  104. data/spec/network_spec.rb +48 -0
  105. data/spec/open_assets/asset_address_spec.rb +33 -0
  106. data/spec/open_assets/asset_id_spec.rb +15 -0
  107. data/spec/open_assets/asset_marker_spec.rb +47 -0
  108. data/spec/open_assets/asset_processor_spec.rb +567 -0
  109. data/spec/open_assets/asset_transaction_builder_spec.rb +273 -0
  110. data/spec/open_assets/asset_transaction_spec.rb +70 -0
  111. data/spec/proof_of_work_spec.rb +53 -0
  112. data/spec/script_spec.rb +66 -0
  113. data/spec/spec_helper.rb +8 -0
  114. data/spec/transaction_builder_spec.rb +338 -0
  115. data/spec/transaction_spec.rb +162 -0
  116. data/spec/wire_format_spec.rb +283 -0
  117. metadata +141 -7
@@ -0,0 +1,206 @@
1
+ require_relative 'spec_helper'
2
+ describe BTC::Address do
3
+
4
+ it "should decode/encode mainnet pay-to-pubkey-hash address" do
5
+ address = BTC::Address.parse("19FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ")
6
+ address.is_a?(BTC::PublicKeyAddress).must_equal(true)
7
+
8
+ address.mainnet?.must_equal true
9
+ address.testnet?.must_equal false
10
+ address.p2sh?.must_equal false
11
+ address.p2pkh?.must_equal true
12
+
13
+ address.data.must_equal "5a73e920b7836c74f9e740a5bb885e8580557038".from_hex
14
+
15
+ address = BTC::PublicKeyAddress.new(hash: "5a73e920b7836c74f9e740a5bb885e8580557038".from_hex)
16
+
17
+ address.script.to_s.must_equal "OP_DUP OP_HASH160 5a73e920b7836c74f9e740a5bb885e8580557038 OP_EQUALVERIFY OP_CHECKSIG"
18
+
19
+ address.to_s.must_equal("19FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ")
20
+ address.data.must_equal(BTC::Data.data_from_hex("5a73e920b7836c74f9e740a5bb885e8580557038"))
21
+ end
22
+
23
+ it "should decode/encode testnet pay-to-pubkey-hash address" do
24
+
25
+ address = BTC::Address.parse("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn")
26
+ address.is_a?(BTC::PublicKeyAddress).must_equal true
27
+
28
+ address.mainnet?.must_equal false
29
+ address.testnet?.must_equal true
30
+ address.p2sh?.must_equal false
31
+ address.p2pkh?.must_equal true
32
+
33
+ address.data.must_equal "243f1394f44554f4ce3fd68649c19adc483ce924".from_hex
34
+
35
+ address2 = BTC::PublicKeyAddress.new(hash: "243f1394f44554f4ce3fd68649c19adc483ce924".from_hex, network: Network.testnet)
36
+
37
+ address2.to_s.must_equal("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn")
38
+ address2.data.must_equal("243f1394f44554f4ce3fd68649c19adc483ce924".from_hex)
39
+ end
40
+
41
+ it "should detect invalid pay-to-pubkey-hash address" do
42
+ lambda { BTC::Address.parse(nil) }.must_raise ArgumentError
43
+ lambda { BTC::Address.parse("") }.must_raise FormatError
44
+ lambda { BTC::Address.parse("18FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ") }.must_raise FormatError
45
+ lambda { BTC::Address.parse("19FGfswVqxNubJbh1NW8A4t51T9x9RDvwq") }.must_raise FormatError
46
+ lambda { BTC::Address.parse("19FGfswVqxNubJbh1NW8A4t51T9x9RD") }.must_raise FormatError
47
+ lambda { BTC::Address.parse("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRf") }.must_raise FormatError
48
+ lambda { BTC::Address.parse("m") }.must_raise FormatError
49
+ lambda { BTC::Address.parse("mipc") }.must_raise FormatError
50
+ end
51
+
52
+ it "should decode/encode mainnet pay-to-script-hash address" do
53
+
54
+ address = BTC::Address.parse("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX")
55
+ address.is_a?(BTC::ScriptHashAddress).must_equal(true)
56
+
57
+ address.mainnet?.must_equal true
58
+ address.testnet?.must_equal false
59
+ address.p2sh?.must_equal true
60
+ address.p2pkh?.must_equal false
61
+
62
+ address.data.must_equal "8f55563b9a19f321c211e9b9f38cdf686ea07845".from_hex
63
+ address.hash.must_equal "8f55563b9a19f321c211e9b9f38cdf686ea07845".from_hex
64
+
65
+ address = BTC::ScriptHashAddress.new(hash: "8f55563b9a19f321c211e9b9f38cdf686ea07845".from_hex)
66
+
67
+ address.script.to_s.must_equal "OP_HASH160 8f55563b9a19f321c211e9b9f38cdf686ea07845 OP_EQUAL"
68
+
69
+ address.to_s.must_equal("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX")
70
+ address.data.must_equal("8f55563b9a19f321c211e9b9f38cdf686ea07845".from_hex)
71
+ end
72
+
73
+ it "should decode/encode testnet pay-to-script-hash address" do
74
+
75
+ address = BTC::Address.parse("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX")
76
+ address.is_a?(BTC::ScriptHashAddress).must_equal(true)
77
+
78
+ address.mainnet?.must_equal true
79
+ address.testnet?.must_equal false
80
+ address.p2sh?.must_equal true
81
+ address.p2pkh?.must_equal false
82
+
83
+ address.data.must_equal "8f55563b9a19f321c211e9b9f38cdf686ea07845".from_hex
84
+
85
+ address = BTC::ScriptHashAddress.new(hash: "8f55563b9a19f321c211e9b9f38cdf686ea07845".from_hex)
86
+
87
+ address.to_s.must_equal("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX")
88
+ address.data.must_equal("8f55563b9a19f321c211e9b9f38cdf686ea07845".from_hex)
89
+ end
90
+
91
+ it "should detect invalid pay-to-script-hash address" do
92
+ lambda { BTC::Address.parse("3ektnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX") }.must_raise FormatError
93
+ lambda { BTC::Address.parse("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzqX") }.must_raise FormatError
94
+ lambda { BTC::Address.parse("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSg") }.must_raise FormatError
95
+ lambda { BTC::Address.parse("2mzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc") }.must_raise FormatError
96
+ lambda { BTC::Address.parse("2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1vc") }.must_raise FormatError
97
+ lambda { BTC::Address.parse("2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJ") }.must_raise FormatError
98
+ end
99
+
100
+
101
+ it "should decode/encode private key string with uncompressed pubkey" do
102
+
103
+ address = BTC::Address.parse("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS")
104
+ address.is_a?(BTC::WIF).must_equal(true)
105
+
106
+ address.public_key_compressed?.must_equal false
107
+ address.mainnet?.must_equal true
108
+ address.testnet?.must_equal false
109
+ address.p2sh?.must_equal false
110
+ address.p2pkh?.must_equal false
111
+
112
+ address.data.must_equal "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex
113
+
114
+ address = BTC::WIF.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex)
115
+
116
+ address.to_s.must_equal("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS")
117
+ address.data.must_equal("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex)
118
+
119
+ end
120
+
121
+ it "should decode/encode private key string with compressed pubkey" do
122
+
123
+ address = BTC::Address.parse("L3p8oAcQTtuokSCRHQ7i4MhjWc9zornvpJLfmg62sYpLRJF9woSu")
124
+ address.is_a?(BTC::WIF).must_equal(true)
125
+
126
+ address.public_key_compressed?.must_equal true
127
+ address.mainnet?.must_equal true
128
+ address.testnet?.must_equal false
129
+ address.p2sh?.must_equal false
130
+ address.p2pkh?.must_equal false
131
+
132
+ address.data.must_equal "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex
133
+
134
+ address = BTC::WIF.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: true)
135
+
136
+ address.to_s.must_equal("L3p8oAcQTtuokSCRHQ7i4MhjWc9zornvpJLfmg62sYpLRJF9woSu")
137
+ address.data.must_equal("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex)
138
+
139
+ end
140
+
141
+ it "should detect invalid private key address" do
142
+ lambda { BTC::Address.parse("5kJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS") }.must_raise FormatError
143
+ lambda { BTC::Address.parse("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hs") }.must_raise FormatError
144
+ lambda { BTC::Address.parse("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZ") }.must_raise FormatError
145
+ lambda { BTC::Address.parse("L3P8oAcQTtuokSCRHQ7i4MhjWc9zornvpJLfmg62sYpLRJF9woSu") }.must_raise FormatError
146
+ lambda { BTC::Address.parse("L3p8oAcQTtuokSCRHQ7i4MhjWc9zornvpJLfmg62sYpLRJF9woSU") }.must_raise FormatError
147
+ lambda { BTC::Address.parse("L3p8oAcQTtuokSCRHQ7i4MhjWc9zornvpJLfmg62sYpLRJ") }.must_raise FormatError
148
+ end
149
+
150
+ it "should convert strings and addresses into normalized address" do
151
+ BTC::Address.parse(BTC::Address.parse("19FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ")).class.must_equal BTC::PublicKeyAddress
152
+ BTC::Address.parse("19FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ").class.must_equal BTC::PublicKeyAddress
153
+ ->{ BTC::Address.parse(nil) }.must_raise ArgumentError
154
+ end
155
+
156
+ it "should convert any address to a public_address" do
157
+ a1 = BTC::Address.parse("1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8")
158
+ a1.public_address.must_equal a1
159
+
160
+ a2 = BTC::Address.parse("L3p8oAcQTtuokSCRHQ7i4MhjWc9zornvpJLfmg62sYpLRJF9woSu")
161
+ a2.public_address.must_equal a1
162
+
163
+ a3 = BTC::Address.parse("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX")
164
+ a3.public_address.must_equal a3
165
+ end
166
+
167
+ it "should support equality" do
168
+ address1 = BTC::Address.parse("19FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ")
169
+ address2 = BTC::PublicKeyAddress.new(hash: "5a73e920b7836c74f9e740a5bb885e8580557038".from_hex)
170
+ address3 = BTC::PublicKeyAddress.new(hash: "5a73e920b7836c74f9e740a5bb885e8580557038".from_hex, network: BTC::Network.testnet)
171
+ address4 = BTC::WIF.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: true)
172
+ address41 = BTC::WIF.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: true)
173
+ address5 = BTC::WIF.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: false)
174
+ address51 = BTC::WIF.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: false)
175
+
176
+ (address1 == address2).must_equal true
177
+ (address2 == address1).must_equal true
178
+ (address1 == address3).must_equal false
179
+ (address2 == address3).must_equal false
180
+
181
+ (address1 == address4).must_equal false
182
+ (address2 == address4).must_equal false
183
+ (address3 == address4).must_equal false
184
+ (address5 == address4).must_equal false
185
+ (address4 == address5).must_equal false
186
+
187
+ (address4 == address41).must_equal true
188
+ (address5 == address51).must_equal true
189
+ end
190
+
191
+ it "should detect if wrong subclass is used when parsing an address" do
192
+ btc_addr_string = "19FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ"
193
+ asset_addr_string = "akB4NBW9UuCmHuepksob6yfZs6naHtRCPNy"
194
+ btc_addr = Address.parse(btc_addr_string)
195
+ asset_addr = Address.parse(asset_addr_string)
196
+
197
+ ->{ BTC::AssetAddress.parse(btc_addr_string) }.must_raise ArgumentError
198
+ ->{ BTC::AssetAddress.parse(btc_addr) }.must_raise ArgumentError
199
+
200
+ ->{ BTC::BitcoinPaymentAddress.parse(asset_addr_string) }.must_raise ArgumentError
201
+ ->{ BTC::BitcoinPaymentAddress.parse(asset_addr) }.must_raise ArgumentError
202
+ end
203
+
204
+ end
205
+
206
+
data/spec/all.rb ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ test_folder = File.expand_path(File.dirname(__FILE__))
4
+ (Dir["#{test_folder}/**/*_test.rb"].to_a + Dir["#{test_folder}/**/*_spec.rb"].to_a).sort.each do |spec|
5
+ require spec
6
+ end
@@ -0,0 +1,83 @@
1
+ require_relative 'spec_helper'
2
+ describe BTC::Base58 do
3
+
4
+ def check_invalid_base58(base58_string, expected_class = BTC::FormatError)
5
+ lambda { Base58.data_from_base58(base58_string) }.must_raise expected_class
6
+ end
7
+
8
+ def check_invalid_base58check(base58check_string, expected_class = BTC::FormatError)
9
+ lambda { Base58.data_from_base58check(base58check_string) }.must_raise expected_class
10
+ end
11
+
12
+ def check_valid_base58(hex_string, base58_string)
13
+ # Convert to Base58
14
+ Base58.base58_from_data(BTC::Data.data_from_hex(hex_string)).must_equal(base58_string)
15
+
16
+ # Convert from Base58
17
+ BTC::Data.hex_from_data(Base58.data_from_base58(base58_string)).must_equal(hex_string)
18
+ end
19
+
20
+ def check_valid_base58check(hex, string)
21
+ # Convert to Base58Check
22
+ Base58.base58check_from_data(BTC::Data.data_from_hex(hex)).must_equal(string)
23
+
24
+ # Convert from Base58Check
25
+ BTC::Data.hex_from_data(Base58.data_from_base58check(string)).must_equal(hex)
26
+ end
27
+
28
+ describe "Base58" do
29
+ it "should handle valid input" do
30
+
31
+ lambda { Base58.data_from_base58(nil) }.must_raise ArgumentError
32
+ lambda { Base58.base58_from_data(nil) }.must_raise ArgumentError
33
+ check_valid_base58("", "")
34
+ check_valid_base58("13", "L")
35
+ check_valid_base58("2e", "o")
36
+ check_valid_base58("61", "2g")
37
+ check_valid_base58("626262", "a3gV")
38
+ check_valid_base58("636363", "aPEr")
39
+ check_valid_base58("73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2")
40
+ check_valid_base58("00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L")
41
+ check_valid_base58("516b6fcd0f", "ABnLTmg")
42
+ check_valid_base58("bf4f89001e670274dd", "3SEo3LWLoPntC")
43
+ check_valid_base58("572e4794", "3EFU7m")
44
+ check_valid_base58("ecac89cad93923c02321", "EJDM8drfXA6uyA")
45
+ check_valid_base58("10c8511e", "Rt5zm")
46
+ check_valid_base58("00000000000000000000", "1111111111")
47
+ end
48
+
49
+ it "should handle invalid input" do
50
+ check_invalid_base58(nil, ArgumentError);
51
+ check_invalid_base58(" ");
52
+ check_invalid_base58("lLoO");
53
+ check_invalid_base58("l");
54
+ check_invalid_base58("O");
55
+ check_invalid_base58("öまи");
56
+ end
57
+ end
58
+
59
+ describe "Base58Check" do
60
+
61
+ it "should handle valid input" do
62
+ check_valid_base58check("", "3QJmnh")
63
+ check_valid_base58check("007ab89f9fae3f8043dcee5f7b5467a0f0a6e2f7e1", "1CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG")
64
+ end
65
+
66
+ it "should handle invalid input" do
67
+ check_invalid_base58check(nil, ArgumentError);
68
+ check_invalid_base58check(" ");
69
+ check_invalid_base58check("lLoO");
70
+ check_invalid_base58check("l");
71
+ check_invalid_base58check("O");
72
+ check_invalid_base58check("öまи");
73
+ end
74
+ it "should detect incorrect checksum" do
75
+ check_invalid_base58check("L");
76
+ check_invalid_base58check("o");
77
+ check_invalid_base58check("0CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG");
78
+ check_invalid_base58check("2CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG");
79
+ check_invalid_base58check("11BtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG");
80
+ check_invalid_base58check("1CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAbG");
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'spec_helper'
2
+ describe BTC::BlockHeader do
3
+
4
+ describe "Genesis Mainnet" do
5
+ it "should have a correct hash" do
6
+ BlockHeader.genesis_mainnet.block_id.must_equal "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
7
+ BlockHeader.genesis_mainnet.height.must_equal 0
8
+ end
9
+ end
10
+
11
+ describe "Genesis Testnet" do
12
+ it "should have a correct hash" do
13
+ BlockHeader.genesis_testnet.block_id.must_equal "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
14
+ BlockHeader.genesis_testnet.height.must_equal 0
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'spec_helper'
2
+ describe BTC::Block do
3
+
4
+ describe "Genesis Mainnet" do
5
+ it "should have a correct hash" do
6
+ Block.genesis_mainnet.block_id.must_equal "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
7
+ Block.genesis_mainnet.height.must_equal 0
8
+ end
9
+ end
10
+
11
+ describe "Genesis Testnet" do
12
+ it "should have a correct hash" do
13
+ Block.genesis_testnet.block_id.must_equal "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
14
+ Block.genesis_testnet.height.must_equal 0
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,46 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe BTC::CurrencyFormatter do
4
+
5
+ it "should support normal bitcoin formatting" do
6
+ fm = BTC::CurrencyFormatter.btc_formatter
7
+
8
+ fm.string_from_number(1*COIN).must_equal("1.0")
9
+ fm.string_from_number(42*COIN).must_equal("42.0")
10
+ fm.string_from_number(42000*COIN).must_equal("42000.0")
11
+ fm.string_from_number(42000*COIN + 123).must_equal("42000.00000123")
12
+ fm.string_from_number(42000*COIN + 123000).must_equal("42000.00123")
13
+ fm.string_from_number(42000*COIN + 123456).must_equal("42000.00123456")
14
+ fm.string_from_number(42000*COIN + COIN/2).must_equal("42000.5")
15
+
16
+ fm.number_from_string("1").must_equal 1*COIN
17
+ fm.number_from_string("1.").must_equal 1*COIN
18
+ fm.number_from_string("1.0").must_equal 1*COIN
19
+ fm.number_from_string("42").must_equal 42*COIN
20
+ fm.number_from_string("42.123").must_equal 42*COIN + 12300000
21
+ fm.number_from_string("42.12345678").must_equal 42*COIN + 12345678
22
+ fm.number_from_string("42.10000000").must_equal 42*COIN + 10000000
23
+ fm.number_from_string("42.10000").must_equal 42*COIN + 10000000
24
+ end
25
+
26
+ it "should support long bitcoin formatting" do
27
+ fm = BTC::CurrencyFormatter.btc_long_formatter
28
+
29
+ fm.string_from_number(1*COIN).must_equal("1.00000000")
30
+ fm.string_from_number(42*COIN).must_equal("42.00000000")
31
+ fm.string_from_number(42000*COIN).must_equal("42000.00000000")
32
+ fm.string_from_number(42000*COIN + 123).must_equal("42000.00000123")
33
+ fm.string_from_number(42000*COIN + 123000).must_equal("42000.00123000")
34
+ fm.string_from_number(42000*COIN + 123456).must_equal("42000.00123456")
35
+ fm.string_from_number(42000*COIN + COIN/2).must_equal("42000.50000000")
36
+
37
+ fm.number_from_string("1").must_equal 1*COIN
38
+ fm.number_from_string("1.").must_equal 1*COIN
39
+ fm.number_from_string("1.0").must_equal 1*COIN
40
+ fm.number_from_string("42").must_equal 42*COIN
41
+ fm.number_from_string("42.123").must_equal 42*COIN + 12300000
42
+ fm.number_from_string("42.12345678").must_equal 42*COIN + 12345678
43
+ fm.number_from_string("42.10000000").must_equal 42*COIN + 10000000
44
+ fm.number_from_string("42.10000").must_equal 42*COIN + 10000000
45
+ end
46
+ end
data/spec/data_spec.rb ADDED
@@ -0,0 +1,50 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe BTC::Data do
4
+
5
+ it "should decode valid hex" do
6
+ lambda { BTC::Data.data_from_hex(nil) }.must_raise ArgumentError
7
+ BTC::Data.data_from_hex("fe").bytes.must_equal "\xfe".bytes
8
+ BTC::Data.data_from_hex("deadBEEF").bytes.must_equal "\xde\xad\xbe\xef".bytes
9
+ BTC::Data.data_from_hex(" \r\n\tdeadBEEF \t \r \n").bytes.must_equal "\xde\xad\xbe\xef".bytes
10
+ BTC::Data.data_from_hex("").bytes.must_equal "".bytes
11
+ BTC::Data.data_from_hex(" \t ").bytes.must_equal "".bytes
12
+ end
13
+
14
+ it "should not decode invalid hex" do
15
+ lambda { BTC::Data.data_from_hex("f") }.must_raise FormatError
16
+ lambda { BTC::Data.data_from_hex("dxadBEEF") }.must_raise FormatError
17
+ lambda { BTC::Data.data_from_hex("-") }.must_raise FormatError
18
+ end
19
+
20
+ it "should encode valid hex" do
21
+ lambda { BTC::Data.hex_from_data(nil) }.must_raise ArgumentError
22
+ BTC::Data.hex_from_data("\xfe").bytes.must_equal "fe".bytes
23
+ BTC::Data.hex_from_data("\xde\xad\xbe\xef").bytes.must_equal "deadbeef".bytes
24
+ BTC::Data.hex_from_data("").bytes.must_equal "".bytes
25
+ end
26
+
27
+ it "should encode bytes" do
28
+ BTC::Data.bytes_from_data("Hello, world").must_equal "Hello, world".bytes
29
+ BTC::Data.bytes_from_data("Hello, world").must_equal [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100]
30
+ BTC::Data.data_from_bytes([72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100]).must_equal "Hello, world"
31
+ end
32
+
33
+ it "should access ranges of bytes" do
34
+ BTC::Data.bytes_from_data("Hello, world", offset: 1).must_equal "ello, world".bytes
35
+ BTC::Data.bytes_from_data("Hello, world", offset: 0, limit: 3).must_equal "Hel".bytes
36
+
37
+ # Range takes precedence over offset/limit.
38
+ BTC::Data.bytes_from_data("Hello, world", offset: 0, limit: 3, range: 1..2).must_equal "el".bytes
39
+
40
+ BTC::Data.bytes_from_data("Hello, world", range: 1..3).must_equal "ell".bytes
41
+ BTC::Data.bytes_from_data("Hello, world", range: 1...3).must_equal "el".bytes
42
+
43
+ # Outside bounds
44
+ BTC::Data.bytes_from_data("Hello, world", offset: 110, limit: 3).must_equal []
45
+ BTC::Data.bytes_from_data("Hello, world", offset: 0, limit: 0).must_equal []
46
+ BTC::Data.bytes_from_data("Hello, world", range: 100..101).must_equal []
47
+ BTC::Data.bytes_from_data("Hello, world", range: 0...0).must_equal []
48
+ end
49
+
50
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe BTC::Diagnostics do
4
+
5
+ it "should record messages" do
6
+
7
+ BTC::Diagnostics.current.wont_be_nil
8
+
9
+ # Due to other tests this may not be nil, so we should not check for it.
10
+ # We also should not clear the state in order to test our recording code against whatever state was there before.
11
+ # BTC::Diagnostics.current.last_message.must_equal nil
12
+
13
+ BTC::Diagnostics.current.add_message("msg1")
14
+ BTC::Diagnostics.current.last_message.must_equal "msg1"
15
+ BTC::Diagnostics.current.add_message("msg2")
16
+ BTC::Diagnostics.current.last_message.must_equal "msg2"
17
+
18
+ BTC::Diagnostics.current.record do
19
+
20
+ BTC::Diagnostics.current.record do
21
+ BTC::Diagnostics.current.add_message("a")
22
+ BTC::Diagnostics.current.add_message("b")
23
+ BTC::Diagnostics.current.last_message.must_equal "b"
24
+ end.map(&:to_s).must_equal ["a", "b"]
25
+
26
+ BTC::Diagnostics.current.last_message.must_equal "b"
27
+
28
+ BTC::Diagnostics.current.add_message("c")
29
+
30
+ BTC::Diagnostics.current.record do
31
+ BTC::Diagnostics.current.add_message("d")
32
+ BTC::Diagnostics.current.add_message("e")
33
+ end.map(&:to_s).must_equal ["d", "e"]
34
+
35
+ end.map(&:to_s).must_equal ["a", "b", "c", "d", "e"]
36
+
37
+ BTC::Diagnostics.current.last_message.must_equal "e"
38
+
39
+ end
40
+
41
+ end