bitcoin-ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. metadata +295 -0
@@ -0,0 +1,246 @@
1
+ require_relative '../spec_helper.rb'
2
+ require 'bitcoin/script'
3
+
4
+ include Bitcoin
5
+
6
+ describe 'Bitcoin::Script' do
7
+ SCRIPT = [
8
+ "410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac",
9
+ "47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901",
10
+ "76a91417977bca1b6287a5e6559c57ef4b6525e9d7ded688ac",
11
+ "524104573b6e9f3a714440048a7b87d606bcbf9e45b8586e70a67a3665ea720c095658471a523e5d923f3f3e015626e7c900bd08560ddffeb17d33c5b52c96edb875954104039c2f4e413a26901e67ad4adbb6a4759af87bc16c7120459ecc9482fed3dd4a4502947f7b4c7782dcadc2bed513ed14d5e770452b97ae246ac2030f13b80a5141048b0f9d04e495c3c754f8c3c109196d713d0778882ef098f785570ee6043f8c192d8f84df43ebafbcc168f5d95a074dc4010b62c003e560abc163c312966b74b653ae", # multisig 2 of 3
12
+ "5141040ee607b584b36e995f2e96dec35457dbb40845d0ce0782c84002134e816a6b8cbc65e9eed047ae05e10760e4113f690fd49ad73b86b04a1d7813d843f8690ace4104220a78f5f6741bb0739675c2cc200643516b02cfdfda5cba21edeaa62c0f954936b30dfd956e3e99af0a8e7665cff6ac5b429c54c418184c81fbcd4bde4088f552ae", # multisig 1 of 2
13
+ ].map{|s|[s].pack("H*")}
14
+ PUBKEYS = [
15
+ "04fb0123fe2c399981bc77d522e2ae3268d2ab15e9a84ae49338a4b1db3886a1ea04cdab955d81e9fa1fcb0c062cb9a5af1ad5dd5064f4afcca322402b07030ec2",
16
+ "0423b8161514560bc8638054b6637ab78f400b24e5694ec8061db635d1f28a17902b14dbf4f80780da659ab24f11ded3095c780452a4004c30ab58dffac33d839a",
17
+ "04f43e76afac66bf3927638b6c4f7e324513ce56d2d658ac9d24c420d09993a4464eea6141a68a4748c092ad0e8f4ac29c4a2f661ef4d22b21f20110f42fcd6f6d",
18
+ ]
19
+ describe "serialization" do
20
+ it '#to_string' do
21
+ Script.new(SCRIPT[0]).to_string.should ==
22
+ "0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 OP_CHECKSIG"
23
+
24
+ Script.new(SCRIPT[1]).to_string.should ==
25
+ "304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"
26
+
27
+ Script.new([123].pack("C")).to_string.should == "(opcode 123)"
28
+ Script.new([176].pack("C")).to_string.should == "OP_EVAL"
29
+
30
+ Script.from_string("1 OP_DROP 2").to_string.should == "1 OP_DROP 2"
31
+ end
32
+
33
+ it 'Script#binary_from_string' do
34
+ str = Script.new(SCRIPT[0]).to_string
35
+ Script.binary_from_string(str).unpack("H*")[0].should == SCRIPT[0].unpack("H*")[0]
36
+ Script.new(Script.binary_from_string(str)).to_string.should == str
37
+
38
+ str = Script.new(SCRIPT[1]).to_string
39
+ Script.binary_from_string(str).unpack("H*")[0].should == SCRIPT[1].unpack("H*")[0]
40
+ Script.new(Script.binary_from_string(str)).to_string.should == str
41
+ # TODO make tests for OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4 cases
42
+
43
+ string = "2 OP_TOALTSTACK 0 OP_TOALTSTACK OP_TUCK OP_CHECKSIG OP_SWAP OP_HASH160 3cd1def404e12a85ead2b4d3f5f9f817fb0d46ef OP_EQUAL OP_BOOLAND OP_FROMALTSTACK OP_ADD"
44
+ Script.from_string(string).to_string.should == string
45
+
46
+ Script.from_string("0 OP_DROP 2 3 4").to_string.should == "0 OP_DROP 2 3 4"
47
+
48
+ Script.from_string("OP_EVAL").to_string.should == "OP_EVAL"
49
+ Script.from_string("OP_NOP1").to_string.should == "OP_EVAL" # test opcodes_alias table
50
+ Script.from_string("OP_NOP").to_string.should == "OP_NOP"
51
+ Script.from_string("1").to_string.should == "1"
52
+
53
+ Script.from_string("0 ffff OP_CODESEPARATOR 1 ffff 1 OP_CHECKMULTISIG").to_string.should == "0 ffff OP_CODESEPARATOR 1 ffff 1 OP_CHECKMULTISIG"
54
+
55
+ [1,2,4].all?{|n| script = "OP_PUSHDATA#{n} 01 ff"
56
+ Bitcoin::Script.binary_from_string(script) == Bitcoin::Script.binary_from_string( Bitcoin::Script.from_string(script).to_string )
57
+ }.should == true
58
+
59
+ proc{ Script.from_string("OP_NOP OP_UNKOWN") }.should.raise(Script::ScriptOpcodeError).message.should == "OP_UNKOWN not defined!"
60
+ end
61
+ end
62
+
63
+ describe "get keys/addresses" do
64
+ it '#get_pubkey' do
65
+ Script.new(SCRIPT[0]).get_pubkey.should ==
66
+ "0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"
67
+ end
68
+
69
+ it '#get_pubkey_address' do
70
+ Script.new(SCRIPT[0]).get_pubkey_address.should ==
71
+ "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S"
72
+ end
73
+
74
+ it "#get_hash160" do
75
+ Script.new(SCRIPT[2]).get_hash160.should ==
76
+ "17977bca1b6287a5e6559c57ef4b6525e9d7ded6"
77
+ Script.from_string("OP_DUP OP_HASH160 0 OP_EQUALVERIFY OP_CHECKSIG")
78
+ .get_hash160.should == nil
79
+ end
80
+
81
+ it "#get_hash160_address" do
82
+ Script.new(SCRIPT[2]).get_hash160_address.should ==
83
+ "139k1g5rtTsL4aGZbcASH3Fv3fUh9yBEdW"
84
+ end
85
+
86
+ it "#get_multisig_pubkeys" do
87
+ Script.new(SCRIPT[3]).get_multisig_pubkeys.should == [
88
+ "04573b6e9f3a714440048a7b87d606bcbf9e45b8586e70a67a3665ea720c095658471a523e5d923f3f3e015626e7c900bd08560ddffeb17d33c5b52c96edb87595",
89
+ "04039c2f4e413a26901e67ad4adbb6a4759af87bc16c7120459ecc9482fed3dd4a4502947f7b4c7782dcadc2bed513ed14d5e770452b97ae246ac2030f13b80a51",
90
+ "048b0f9d04e495c3c754f8c3c109196d713d0778882ef098f785570ee6043f8c192d8f84df43ebafbcc168f5d95a074dc4010b62c003e560abc163c312966b74b6"].map{|pk| [pk].pack("H*")}
91
+ Script.from_string("3 #{PUBKEYS[0..2].join(' ')} 3 OP_CHECKMULTISIG")
92
+ .get_multisig_pubkeys.should == [
93
+ "04fb0123fe2c399981bc77d522e2ae3268d2ab15e9a84ae49338a4b1db3886a1ea04cdab955d81e9fa1fcb0c062cb9a5af1ad5dd5064f4afcca322402b07030ec2",
94
+ "0423b8161514560bc8638054b6637ab78f400b24e5694ec8061db635d1f28a17902b14dbf4f80780da659ab24f11ded3095c780452a4004c30ab58dffac33d839a",
95
+ "04f43e76afac66bf3927638b6c4f7e324513ce56d2d658ac9d24c420d09993a4464eea6141a68a4748c092ad0e8f4ac29c4a2f661ef4d22b21f20110f42fcd6f6d"].map{|k|[k].pack("H*")}
96
+ end
97
+
98
+ it "#get_multisig_addresses" do
99
+ Script.new(SCRIPT[3]).get_multisig_addresses.should == [
100
+ "1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj", "19Fm2gY7qDTXriNTEhFY2wjxbHna3Gvenk",
101
+ "1B6k6g1d2L975i7beAbiBRxfBWhxomPxvy"]
102
+ Script.new(SCRIPT[4]).get_multisig_addresses.should == [
103
+ "1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5", "1EE7JGimkV7QqyHwXDJvk3b1yEN4ZUWeqx"]
104
+ end
105
+
106
+ it "#get_address" do
107
+ Script.new(SCRIPT[0]).get_address.should ==
108
+ "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S"
109
+ Script.new(SCRIPT[1]).get_address.should == nil
110
+ Script.new(SCRIPT[2]).get_address.should ==
111
+ "139k1g5rtTsL4aGZbcASH3Fv3fUh9yBEdW"
112
+ Script.new(SCRIPT[3]).get_address.should ==
113
+ "1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj"
114
+ Script.new(SCRIPT[4]).get_address.should ==
115
+ "1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5"
116
+ end
117
+
118
+ it "#get_addresses" do
119
+ Script.new(SCRIPT[0]).get_addresses.
120
+ should == ["12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S"]
121
+ Script.new(SCRIPT[3]).get_addresses
122
+ .should == ["1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj",
123
+ "19Fm2gY7qDTXriNTEhFY2wjxbHna3Gvenk", "1B6k6g1d2L975i7beAbiBRxfBWhxomPxvy"]
124
+ end
125
+ end
126
+
127
+ describe "determine type" do
128
+
129
+ it '#is_standard?' do
130
+ Script.new(SCRIPT[0]).is_standard?.should == true
131
+ Script.new(SCRIPT[1]).is_standard?.should == false
132
+ Script.new(SCRIPT[2]).is_standard?.should == true
133
+ Script.new(SCRIPT[3]).is_standard?.should == true
134
+ Script.new(SCRIPT[4]).is_standard?.should == true
135
+ end
136
+
137
+ it '#is_pubkey?' do
138
+ Script.new(SCRIPT[0]).is_pubkey?.should == true
139
+ Script.new(SCRIPT[1]).is_pubkey?.should == false
140
+ Script.new(SCRIPT[2]).is_pubkey?.should == false
141
+ Script.new(SCRIPT[3]).is_pubkey?.should == false
142
+ Script.new(SCRIPT[4]).is_send_to_ip?.should == false
143
+ end
144
+
145
+ it "#is_hash160?" do
146
+ Script.new(SCRIPT[0]).is_hash160?.should == false
147
+ Script.new(SCRIPT[1]).is_pubkey?.should == false
148
+ Script.new(SCRIPT[2]).is_hash160?.should == true
149
+ Script.from_string("OP_DUP OP_HASH160 0 OP_EQUALVERIFY OP_CHECKSIG")
150
+ .is_hash160?.should == false
151
+ end
152
+
153
+ it "#is_multisig?" do
154
+ Script.new(SCRIPT[3]).is_multisig?.should == true
155
+ Script.new(SCRIPT[4]).is_multisig?.should == true
156
+ Script.new(SCRIPT[0]).is_multisig?.should == false
157
+ Script.new("OP_DUP OP_DROP 2 #{PUBKEYS[0..2].join(' ')} 3 OP_CHECKMULTISIG")
158
+ .is_multisig?.should == false
159
+ Script.new("OP_DROP OP_CHECKMULTISIG").is_multisig?.should == false
160
+ end
161
+
162
+ it "#type" do
163
+ Script.new(SCRIPT[0]).type.should == :pubkey
164
+ Script.new(SCRIPT[1]).type.should == :unknown
165
+ Script.new(SCRIPT[2]).type.should == :hash160
166
+ Script.new(SCRIPT[3]).type.should == :multisig
167
+ Script.new(SCRIPT[4]).type.should == :multisig
168
+ end
169
+
170
+ end
171
+
172
+ describe "generate scripts" do
173
+
174
+ it "should generate pubkey script" do
175
+ Script.to_pubkey_script(PUBKEYS[0]).should ==
176
+ Script.from_string("#{PUBKEYS[0]} OP_CHECKSIG").raw
177
+ Script.to_pubkey_script(PUBKEYS[1]).should ==
178
+ Script.from_string("#{PUBKEYS[1]} OP_CHECKSIG").raw
179
+ end
180
+
181
+ it "should generate hash160 script" do
182
+ Script.to_address_script('16Tc7znw2mfpWcqS84vBFfJ7PyoeHaXSz9')
183
+ .should == ["76a9143be0c2daaabbf3d53e47352c19d1e8f047e2f94188ac"].pack("H*")
184
+ hash160 = Bitcoin.hash160_from_address('16Tc7znw2mfpWcqS84vBFfJ7PyoeHaXSz9')
185
+ Script.to_hash160_script(hash160)
186
+ .should == Script.from_string("OP_DUP OP_HASH160 #{hash160} OP_EQUALVERIFY OP_CHECKSIG").raw
187
+ Script.to_address_script('mr1jU3Adw2pkvxTLvQA4MKpXB9Dynj9cXF')
188
+ .should == nil
189
+ end
190
+
191
+ it "should generate multisig script" do
192
+ Script.to_multisig_script(2, *PUBKEYS[0..2]).should ==
193
+ Script.from_string("2 #{PUBKEYS[0..2].join(' ')} 3 OP_CHECKMULTISIG").raw
194
+ Script.to_multisig_script(1, *PUBKEYS[0..1]).should ==
195
+ Script.from_string("1 #{PUBKEYS[0..1].join(' ')} 2 OP_CHECKMULTISIG").raw
196
+ end
197
+
198
+ it "should generate p2sh script" do
199
+ address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
200
+ hash160 = Bitcoin.hash160_from_address address
201
+ Script.to_p2sh_script(hash160).should ==
202
+ Script.from_string("OP_HASH160 #{hash160} OP_EQUAL").raw
203
+ end
204
+
205
+ it "should determine type for address script" do
206
+ address = '16Tc7znw2mfpWcqS84vBFfJ7PyoeHaXSz9'
207
+ hash160 = Bitcoin.hash160_from_address address
208
+ Script.to_address_script(address).should ==
209
+ Script.from_string("OP_DUP OP_HASH160 #{hash160} OP_EQUALVERIFY OP_CHECKSIG").raw
210
+
211
+ address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
212
+ hash160 = Bitcoin.hash160_from_address address
213
+ Script.to_p2sh_script(hash160).should ==
214
+ Script.from_string("OP_HASH160 #{hash160} OP_EQUAL").raw
215
+ end
216
+
217
+ end
218
+
219
+ describe "generate script sigs" do
220
+
221
+ it "should generate pubkey script sig" do
222
+ sig = ["3045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec"].pack("H*")
223
+ pub = ["04bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41a5f6e093277b774b5893347e38ffafce2b9e82226e6e0b378cf79b8c2eed983c"].pack("H*")
224
+ Script.to_pubkey_script_sig(sig, pub)
225
+ .should == ["483045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec014104bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41a5f6e093277b774b5893347e38ffafce2b9e82226e6e0b378cf79b8c2eed983c"].pack("H*")
226
+ end
227
+
228
+ end
229
+
230
+ it '#run' do
231
+ script = SCRIPT[1] + SCRIPT[0]
232
+ Script.new(script).run.should == true
233
+
234
+ Script.from_string("1 OP_DUP OP_DROP 1 OP_EQUAL")
235
+ .run.should == true
236
+ Script.from_string("1 OP_DUP OP_DROP 1 OP_EQUAL")
237
+ .run.should == true
238
+ Script.from_string("foo OP_DUP OP_DROP foo OP_EQUAL")
239
+ .run.should == true
240
+ Script.from_string("bar foo OP_DUP OP_DROP bar OP_EQUAL")
241
+ .run.should == false
242
+
243
+ Script.from_string("1 OP_DROP 2").run.should == true
244
+ end
245
+
246
+ end
@@ -0,0 +1,36 @@
1
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '/../../lib'))
2
+
3
+ begin
4
+ require 'simplecov'
5
+ SimpleCov.start do
6
+ add_group("Bitcoin") do |file|
7
+ ["bitcoin.rb", "opcodes.rb", "script.rb", "key.rb"].include?(file.filename.split("/").last)
8
+ end
9
+ add_group "Protocol", "lib/bitcoin/protocol"
10
+ add_group "Storage", "lib/bitcoin/storage"
11
+ add_group "Wallet", "lib/bitcoin/wallet"
12
+ add_group("Utilities") do |file|
13
+ ["logger.rb", "openssl.rb"].include?(file.filename.split("/").last)
14
+ end
15
+ end
16
+ rescue LoadError
17
+ end
18
+
19
+ require 'bitcoin'
20
+
21
+ def fixtures_file(relative_path)
22
+ basedir = File.join(File.dirname(__FILE__), 'fixtures')
23
+ Bitcoin::Protocol.read_binary_file( File.join(basedir, relative_path) )
24
+ end
25
+
26
+ Bitcoin::network = :bitcoin
27
+
28
+ begin
29
+ require 'bacon'
30
+ rescue LoadError
31
+ puts "Cannot load 'bacon' - install with `gem install bacon`"
32
+ puts "Note: to run all the tests, you will also need: ffi, sequel, sqlite3"
33
+ exit 1
34
+ end
35
+ Bacon.summary_on_exit
36
+ require 'minitest/mock'
@@ -0,0 +1,229 @@
1
+ require_relative 'spec_helper'
2
+
3
+ [
4
+ # { :name => :dummy },
5
+ { :name => :sequel, :db => 'sqlite:/' }, # in memory
6
+ # { :name => :sequel, :db => 'sqlite:///tmp/bitcoin_test.db' },
7
+ # { :name => :sequel, :db => 'postgres://localhost/bitcoin_test' },
8
+ ].each do |configuration|
9
+ describe "Bitcoin::Storage::Backends::#{configuration[:name].capitalize}Store" do
10
+
11
+ before do
12
+ Bitcoin::network = :testnet
13
+ Bitcoin::Storage.log.level = 3
14
+ @store = Bitcoin::Storage.send(configuration[:name], configuration)
15
+ @store.reset
16
+
17
+ @store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin')))
18
+ @store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_1.bin')))
19
+ @store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_2.bin')))
20
+ @store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_3.bin')))
21
+
22
+ @store.store_tx(Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-01.bin')))
23
+ @store.store_tx(Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-02.bin')))
24
+
25
+
26
+ @blk = Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_4.bin'))
27
+ @tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-03.bin'))
28
+ end
29
+
30
+ it "should get depth" do
31
+ @store.get_depth.should == 3
32
+ end
33
+
34
+ it "should report depth as -1 if store is empty" do
35
+ @store.reset
36
+ @store.get_depth.should == -1
37
+ end
38
+
39
+ it "should get head" do
40
+ @store.get_head
41
+ .should == @store.get_block("0000000098932356a236718829dd9e3eb0f9143317ab921333b1a203de336de4")
42
+ end
43
+
44
+ it "should get locator" do
45
+ @store.get_locator.should == [
46
+ "0000000098932356a236718829dd9e3eb0f9143317ab921333b1a203de336de4",
47
+ "000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f",
48
+ "000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604",
49
+ "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"]
50
+ end
51
+
52
+ it "should not store if there is no prev block" do
53
+ @store.reset
54
+ @store.store_block(@blk).should == [0, 2]
55
+ @store.get_depth.should == -1
56
+ end
57
+
58
+ it "should check whether block is already stored" do
59
+ @store.has_block(@blk.hash).should == false
60
+ @store.store_block(@blk)
61
+ @store.has_block(@blk.hash).should == true
62
+ end
63
+
64
+ it "should get block by depth" do
65
+ @store.get_block_by_depth(0).to_hash.should ==
66
+ Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin')).to_hash
67
+ @store.get_block_by_depth(1).to_hash.should ==
68
+ Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_1.bin')).to_hash
69
+ @store.get_block_by_depth(2).to_hash.should ==
70
+ Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_2.bin')).to_hash
71
+ end
72
+
73
+ it "should get block by hash" do
74
+ @store.get_block(
75
+ "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008").to_hash
76
+ .should == Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin')).to_hash
77
+ @store.get_block(
78
+ "000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604").to_hash
79
+ .should == Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_1.bin')).to_hash
80
+ @store.get_block(
81
+ "000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f").to_hash
82
+ .should == Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_2.bin')).to_hash
83
+ end
84
+
85
+ it "should not get block" do
86
+ @store.get_block("nonexistant").should == nil
87
+ end
88
+
89
+ it "should get block depth" do
90
+ @store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
91
+ .depth.should == 0
92
+ @store.get_block("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")
93
+ .depth.should == 1
94
+ @store.get_block("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f")
95
+ .depth.should == 2
96
+ end
97
+
98
+ it "should get prev block" do
99
+ @store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
100
+ .get_prev_block.should == nil
101
+ @store.get_block("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")
102
+ .get_prev_block.should ==
103
+ @store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
104
+ end
105
+
106
+ it "should get next block" do
107
+ @store.get_block("0000000098932356a236718829dd9e3eb0f9143317ab921333b1a203de336de4")
108
+ .get_next_block.should == nil
109
+ @store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
110
+ .get_next_block.should ==
111
+ @store.get_block("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")
112
+ end
113
+
114
+ it "should get block for tx" do
115
+ @store.store_block(@blk)
116
+ @store.get_block_by_tx(@blk.tx[0].hash).should == @blk
117
+ end
118
+
119
+ it "should store tx" do
120
+ @store.store_tx(@tx).should != false
121
+ end
122
+
123
+ it "should not store tx if already stored and return existing id" do
124
+ id = @store.store_tx(@tx)
125
+ @store.store_tx(@tx).should == id
126
+ end
127
+
128
+ it "should check if tx is already stored" do
129
+ @store.has_tx(@tx.hash).should == false
130
+ @store.store_tx(@tx)
131
+ @store.has_tx(@tx.hash).should == true
132
+ end
133
+
134
+ it "should store hash160 for txout" do
135
+ @store.store_tx(@tx)
136
+ @store.get_tx(@tx.hash).out[0].hash160
137
+ .should == "3129d7051d509424d23d533fa2d5258977e822e3"
138
+ end
139
+
140
+ it "should get tx" do
141
+ @store.store_tx(@tx)
142
+ @store.get_tx(@tx.hash).should == @tx
143
+ end
144
+
145
+ it "should not get tx" do
146
+ @store.get_tx("nonexistant").should == nil
147
+ end
148
+
149
+
150
+ it "should get txouts for pk script" do
151
+ @store.store_block(@blk)
152
+ script = @blk.tx[0].out[0].pk_script
153
+ @store.get_txouts_for_pk_script(script)
154
+ .should == [@blk.tx[0].out[0]]
155
+ end
156
+
157
+ it "should get block for tx" do
158
+ @store.store_block(@blk)
159
+ tx = @blk.tx[0]
160
+ @store.get_tx(tx.hash).get_block.should == @blk
161
+ end
162
+
163
+ it "should get tx for txin" do
164
+ @store.store_tx(@tx)
165
+ @store.get_tx(@tx.hash).in[0].get_tx.should == @tx
166
+ end
167
+
168
+ it "should get prev out for txin" do
169
+ tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin'))
170
+ outpoint_tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin'))
171
+ @store.store_tx(outpoint_tx)
172
+ @store.store_tx(tx)
173
+
174
+ @store.get_tx(tx.hash).in[0].get_prev_out.should == outpoint_tx.out[0]
175
+ end
176
+
177
+ it "should get tx for txout" do
178
+ @store.store_tx(@tx)
179
+ @store.get_tx(@tx.hash).out[0].get_tx.should == @tx
180
+ end
181
+
182
+ it "should get next in for txin" do
183
+ tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin'))
184
+ outpoint_tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin'))
185
+ @store.store_tx(outpoint_tx)
186
+ @store.store_tx(tx)
187
+
188
+ @store.get_tx(outpoint_tx.hash).out[0].get_next_in.should == tx.in[0]
189
+ end
190
+
191
+ it "should get txouts for hash160" do
192
+ @store.store_tx(@tx)
193
+ @store.get_txouts_for_hash160("3129d7051d509424d23d533fa2d5258977e822e3", true)
194
+ .should == [@tx.out[0]]
195
+ end
196
+
197
+ it "should get txouts for address" do
198
+ @store.store_tx(@tx)
199
+ @store.get_txouts_for_address("mjzuXYR2fncbPzn9nR5Ee5gBgYk9UQx36x", true)
200
+ .should == [@tx.out[0]]
201
+ end
202
+
203
+ it "should get balance for address" do
204
+ @store.store_tx(@tx)
205
+ @store.get_balance("62e907b15cbf27d5425399ebf6f0fb50ebb88f18").should == 5000000000
206
+ @store.get_balance("4580f1b3632948202655fd555fdaaf9b9ef5ac0d").should == 0
207
+ end
208
+
209
+ it "should store multisig tx and index hash160's" do
210
+ (true.should==true) && next if @store.class == Bitcoin::Storage::Backends::DummyStore
211
+ *keys = Bitcoin::Key.generate, Bitcoin::Key.generate
212
+ pk_script = Bitcoin::Script.to_multisig_script(1, keys[0].pub, keys[1].pub)
213
+ txout = Bitcoin::Protocol::TxOut.new(1000, pk_script)
214
+ @store.store_txout(0, txout, 0)
215
+ keys.each do |key|
216
+ hash160 = Bitcoin.hash160(key.pub)
217
+ txouts = @store.get_txouts_for_hash160(hash160, true)
218
+ txouts.size.should == 1
219
+ txouts[0].pk_script.should == txout.pk_script
220
+ end
221
+ end
222
+
223
+ it "should index output script type" do
224
+ @store.store_tx(@tx)
225
+ @store.get_tx(@tx.hash).out.first.type.should == :hash160
226
+ end
227
+
228
+ end
229
+ end