bitcoin-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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