bitcoin-ruby 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +2 -2
  4. data/COPYING +1 -1
  5. data/Gemfile +5 -11
  6. data/README.rdoc +11 -5
  7. data/Rakefile +5 -0
  8. data/bin/bitcoin_node +11 -29
  9. data/bin/bitcoin_node_cli +81 -0
  10. data/bin/bitcoin_wallet +9 -6
  11. data/doc/NODE.rdoc +79 -26
  12. data/examples/bbe_verify_tx.rb +1 -1
  13. data/examples/index_nhash.rb +24 -0
  14. data/examples/reindex_p2sh_addrs.rb +44 -0
  15. data/lib/bitcoin.rb +135 -20
  16. data/lib/bitcoin/builder.rb +233 -63
  17. data/lib/bitcoin/key.rb +89 -16
  18. data/lib/bitcoin/litecoin.rb +13 -11
  19. data/lib/bitcoin/namecoin.rb +5 -4
  20. data/lib/bitcoin/network/command_client.rb +23 -13
  21. data/lib/bitcoin/network/command_handler.rb +336 -131
  22. data/lib/bitcoin/network/connection_handler.rb +14 -13
  23. data/lib/bitcoin/network/node.rb +61 -20
  24. data/lib/bitcoin/protocol.rb +5 -1
  25. data/lib/bitcoin/protocol/block.rb +15 -3
  26. data/lib/bitcoin/protocol/parser.rb +3 -3
  27. data/lib/bitcoin/protocol/tx.rb +82 -20
  28. data/lib/bitcoin/protocol/txin.rb +7 -0
  29. data/lib/bitcoin/protocol/txout.rb +12 -9
  30. data/lib/bitcoin/script.rb +329 -75
  31. data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
  32. data/lib/bitcoin/storage/models.rb +6 -11
  33. data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
  34. data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
  35. data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
  36. data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
  37. data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
  38. data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
  39. data/lib/bitcoin/storage/storage.rb +161 -97
  40. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
  41. data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
  42. data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
  43. data/lib/bitcoin/validation.rb +87 -56
  44. data/lib/bitcoin/version.rb +1 -1
  45. data/spec/bitcoin/bitcoin_spec.rb +38 -0
  46. data/spec/bitcoin/builder_spec.rb +177 -0
  47. data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
  48. data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
  49. data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
  50. data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
  51. data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
  52. data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
  53. data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
  54. data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
  55. data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
  56. data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
  57. data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
  58. data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
  59. data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
  60. data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
  61. data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
  62. data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
  63. data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
  64. data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
  65. data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
  66. data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
  67. data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
  68. data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
  69. data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
  70. data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
  71. data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
  72. data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
  73. data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
  74. data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
  75. data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
  76. data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
  77. data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
  78. data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
  79. data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
  80. data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
  81. data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
  82. data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
  83. data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
  84. data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
  85. data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
  86. data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
  87. data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
  88. data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
  89. data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
  90. data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
  91. data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
  92. data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
  93. data/spec/bitcoin/key_spec.rb +79 -8
  94. data/spec/bitcoin/namecoin_spec.rb +1 -1
  95. data/spec/bitcoin/node/command_api_spec.rb +373 -86
  96. data/spec/bitcoin/performance/storage_spec.rb +41 -0
  97. data/spec/bitcoin/protocol/addr_spec.rb +7 -5
  98. data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
  99. data/spec/bitcoin/protocol/block_spec.rb +6 -0
  100. data/spec/bitcoin/protocol/tx_spec.rb +184 -1
  101. data/spec/bitcoin/protocol/txin_spec.rb +27 -0
  102. data/spec/bitcoin/protocol/txout_spec.rb +27 -0
  103. data/spec/bitcoin/script/opcodes_spec.rb +74 -3
  104. data/spec/bitcoin/script/script_spec.rb +271 -0
  105. data/spec/bitcoin/spec_helper.rb +34 -6
  106. data/spec/bitcoin/storage/models_spec.rb +104 -0
  107. data/spec/bitcoin/storage/reorg_spec.rb +42 -11
  108. data/spec/bitcoin/storage/storage_spec.rb +58 -15
  109. data/spec/bitcoin/storage/validation_spec.rb +44 -14
  110. data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
  111. data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
  112. data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
  113. metadata +117 -11
@@ -3,14 +3,33 @@ require_relative '../spec_helper.rb'
3
3
  include Bitcoin
4
4
  include Builder
5
5
 
6
+ class Array
7
+ def stringify_keys
8
+ map do |e|
9
+ (e.is_a?(Array) || e.is_a?(Hash)) ? e.stringify_keys : e
10
+ end
11
+ end
12
+ end
13
+
14
+ class Hash
15
+ def stringify_keys
16
+ Hash[map do |k, v|
17
+ v = v.stringify_keys if v.is_a?(Hash) || v.is_a?(Array)
18
+ [k.to_s, v]
19
+ end]
20
+ end
21
+ end
22
+
6
23
  describe 'Node Command API' do
7
24
 
8
- def test_command command, params = [], response = nil, &block
25
+ TSLB_TIMEOUT = 3
26
+
27
+ def test_command command, params = nil, response = nil, &block
9
28
  $responses = {}
10
29
  EM.run do
11
30
  @client = Bitcoin::Network::CommandClient.connect(*@config[:command]) do
12
31
  on_connected do
13
- request(command, *params)
32
+ request(command, params)
14
33
  end
15
34
  on_response do |cmd, data|
16
35
  $responses[cmd] = data
@@ -26,7 +45,7 @@ describe 'Node Command API' do
26
45
  if block
27
46
  block.call(result)
28
47
  else
29
- result.should == response
48
+ raise "ERROR: #{result} != #{response}" unless result.should == response
30
49
  end
31
50
  end
32
51
 
@@ -49,7 +68,7 @@ describe 'Node Command API' do
49
68
 
50
69
  @node = Bitcoin::Network::Node.new(@config)
51
70
  @pid = fork do
52
- $stdout = StringIO.new
71
+ # $stdout = StringIO.new
53
72
  SimpleCov.running = false if defined?(SimpleCov)
54
73
  @node.run
55
74
  end
@@ -60,8 +79,10 @@ describe 'Node Command API' do
60
79
  @key = Bitcoin::Key.generate
61
80
  @block = create_block @genesis.hash, false, [], @key
62
81
 
63
- test_command "store_block", [@genesis.to_payload.hth]
82
+ test_command "store_block", hex: @genesis.to_payload.hth
64
83
  sleep 0.1
84
+
85
+ @id = 0
65
86
  end
66
87
 
67
88
  after do
@@ -69,27 +90,24 @@ describe 'Node Command API' do
69
90
  end
70
91
 
71
92
  it "should return error for unknown command" do
72
- test_command("foo").should == {"error" => "unknown command: foo. send 'help' for help."}
93
+ test_command("foo", nil, {"error" => "unknown command: foo. send 'help' for help."})
73
94
  end
74
95
 
75
- it "should return error for wrong parameters" do
76
- test_command("info", "foo").should == {"error" => "wrong number of arguments (1 for 0)"}
77
- end
96
+ # it "should return error for wrong parameters" do
97
+ # test_command("info", "foo", {"error" => "wrong number of arguments (1 for 0)"})
98
+ # end
78
99
 
79
100
  it "should query tslb" do
80
- test_command("tslb") do |res|
81
- res.keys.include?("tslb").should == true
82
- res["tslb"].should >= 0
83
- res["tslb"].should <= 1
84
- end
101
+ test_command("tslb") {|r| (0..TSLB_TIMEOUT).include?(r['tslb']).should == true }
85
102
  end
86
103
 
87
104
  it "should query info" do
88
105
  info = test_command "info"
89
106
  info.is_a?(Hash).should == true
90
- info["blocks"].should == "0 (?)"
91
- info["addrs"].should == "0 (0)"
92
- info["connections"].should == "0 established (0 out, 0 in), 0 connecting"
107
+ info["blocks"].should == { "depth" => 0, "peers" => "?", "sync" => false }
108
+ info["addrs"].should == { "alive" => 0, "total" => 0 }
109
+ info["connections"].should == {
110
+ "established" => 0, "outgoing" => 0, "incoming" => 0, "connecting" => 0 }
93
111
  info["queue"].should == 0
94
112
  info["inv_queue"].should == 0
95
113
  info["inv_cache"].should == 0
@@ -97,7 +115,7 @@ describe 'Node Command API' do
97
115
  info["storage"].should == "sequel::sqlite:/"
98
116
  info["version"].should == 70001
99
117
  info["external_ip"].should == "127.0.0.1"
100
- info["uptime"].should =~ /00:00:00:0[0|1]/
118
+ info["uptime"].between?(0, 1).should == true
101
119
  end
102
120
 
103
121
  it "should query config" do
@@ -111,45 +129,60 @@ describe 'Node Command API' do
111
129
 
112
130
  # TODO
113
131
  it "should connect" do
114
- test_command("connect", ["127.0.0.1:1234"])["state"].should == "Connecting..."
132
+ test_command("connect", {host: "127.0.0.1", port: 1234})["state"].should == "connecting"
115
133
  end
116
134
 
117
135
  # TODO
118
136
  it "should disconnect" do
119
- test_command("disconnect", ["127.0.0.1:1234"])["state"].should == "Disconnected"
137
+ test_command("disconnect", ["127.0.0.1:1234"])["state"].should == "disconnected"
120
138
  end
121
139
 
122
140
  it "should store block" do
123
- test_command("info")["blocks"].should == "0 (?)"
124
- res = test_command "store_block", [ @block.to_payload.hth ]
125
- res.should == { "queued" => [ "block", @block.hash ] }
141
+ test_command("info")["blocks"].should == {"depth" => 0, "peers" => "?", "sync" => false}
142
+ res = test_command("store_block", { hex: @block.to_payload.hth })
143
+ res.should == { "queued" => @block.hash }
126
144
  sleep 0.1
127
- test_command("info")["blocks"].should == "1 (?) sync"
145
+ test_command("info")["blocks"]["depth"].should == 1
146
+ test_command("info")["blocks"]["sync"].should == true
128
147
  end
129
148
 
149
+ # TODO
150
+ # it "should store tx" do
151
+ # @tx = @block.tx[1]
152
+ # res = test_command("store_tx", { hex: @tx.to_payload.htb })
153
+ # res.should == { "queued" => @tx.hash }
154
+ # end
155
+
130
156
  describe :create_tx do
131
157
 
132
158
  before do
133
159
  @key2 = Key.generate
134
- test_command("store_block", [@block.to_payload.hth])
160
+ test_command("store_block", hex: @block.to_payload.hth)
135
161
  sleep 0.1
136
162
  end
137
163
 
138
164
  it "should create transaction from given private keys" do
139
- res = test_command("create_tx", [[@key.to_base58], [[@key2.addr, 10e8], [@key.addr, 40e8]]])
140
- tx = P::Tx.new(res[0].htb)
141
- tx.is_a?(P::Tx).should == true
165
+ res = test_command("create_tx", {
166
+ keys: [ @key.to_base58 ],
167
+ recipients: [[@key2.addr, 10e8], [@key.addr, 40e8]]
168
+ })
169
+ tx = P::Tx.new(res["hex"].htb)
170
+ tx.hash.should == res["hash"]
142
171
  tx.verify_input_signature(0, @block.tx[0]).should == true
143
172
  end
144
173
 
145
- it "should create transaction from given addresses" do
146
- res = test_command("create_tx", [[@key.addr], [[@key2.addr, 10e8], [@key.addr, 40e8]]])
147
- tx = P::Tx.new(res[0].htb)
148
- tx.is_a?(P::Tx).should == true
174
+ it "should create unsigned transaction from given addresses" do
175
+ res = test_command("create_tx", {
176
+ keys: [ @key.addr ],
177
+ recipients: [[@key2.addr, 10e8], [@key.addr, 40e8]]
178
+ })
179
+ tx = P::Tx.new(res["hex"].htb)
180
+ tx.hash.should == res["hash"]
149
181
  tx.in[0].script_sig.should == ""
150
- -> { tx.verify_input_signature(0, @block.tx[0]) }.should.raise(TypeError)
182
+ #-> { tx.verify_input_signature(0, @block.tx[0]) }.should.raise(TypeError)
183
+ tx.verify_input_signature(0, @block.tx[0]).should == false
151
184
 
152
- res[1].each.with_index do |sig_data, idx|
185
+ res["missing_sigs"].each.with_index do |sig_data, idx|
153
186
  sig_hash, sig_addr = *sig_data
154
187
  sig_addr.should == @key.addr
155
188
  sig = @key.sign(sig_hash.htb)
@@ -162,12 +195,16 @@ describe 'Node Command API' do
162
195
  end
163
196
 
164
197
  it "should create transaction from given pubkeys" do
165
- res = test_command("create_tx", [[@key.pub], [[@key2.addr, 10e8], [@key.addr, 40e8]]])
166
- tx = P::Tx.new(res[0].htb)
167
- tx.is_a?(P::Tx).should == true
168
- -> { tx.verify_input_signature(0, @block.tx[0]) }.should.raise(TypeError)
169
-
170
- res[1].each.with_index do |sig_data, idx|
198
+ res = test_command("create_tx", {
199
+ keys: [@key.pub],
200
+ recipients: [[@key2.addr, 10e8], [@key.addr, 40e8]]
201
+ })
202
+ tx = P::Tx.new(res["hex"].htb)
203
+ tx.hash.should == res["hash"]
204
+ #-> { tx.verify_input_signature(0, @block.tx[0]) }.should.raise(TypeError)
205
+ tx.verify_input_signature(0, @block.tx[0]).should == false
206
+
207
+ res["missing_sigs"].each.with_index do |sig_data, idx|
171
208
  sig_hash, sig_addr = *sig_data
172
209
  sig_addr.should == @key.addr
173
210
  sig = @key.sign(sig_hash.htb)
@@ -192,10 +229,11 @@ describe 'Node Command API' do
192
229
  t.output {|o| o.value 50e8; o.script {|s| s.recipient @key.addr } }
193
230
  end
194
231
  sig = @key.sign(tx.in[0].sig_hash)
195
- test_command("store_block", [@block.to_payload.hth])
232
+ test_command("store_block", hex: @block.to_payload.hth)
196
233
  sleep 0.1
197
- res = test_command("assemble_tx", [tx.to_payload.hth, [[sig.hth, @key.pub]]])
198
- tx = Bitcoin::P::Tx.new(res.htb)
234
+ res = test_command("assemble_tx", {tx: tx.to_payload.hth, sig_pubs: [[sig.hth, @key.pub]]})
235
+ tx = Bitcoin::P::Tx.new(res["hex"].htb)
236
+ tx.hash.should == res["hash"]
199
237
  tx.verify_input_signature(0, @block.tx[0]).should == true
200
238
  end
201
239
 
@@ -204,7 +242,7 @@ describe 'Node Command API' do
204
242
  describe :relay_tx do
205
243
 
206
244
  it "should handle decoding error" do
207
- res = test_command("relay_tx", ["foobar"])
245
+ res = test_command("relay_tx", hex: "foobar")
208
246
  res["error"].should == "Error decoding transaction."
209
247
  end
210
248
 
@@ -214,7 +252,7 @@ describe 'Node Command API' do
214
252
  create_tx(t, @block.tx[0], 0, [[22e14, @key]]) }], @key)
215
253
  tx = block.tx[1]
216
254
 
217
- error = test_command("relay_tx", [tx.to_payload.hth])
255
+ error = test_command("relay_tx", hex: tx.to_payload.hth)
218
256
  error["error"].should == "Transaction syntax invalid."
219
257
  error["details"].should == ["output_values", [22e14, 21e14]]
220
258
  end
@@ -225,7 +263,7 @@ describe 'Node Command API' do
225
263
  create_tx(t, @block.tx[0], 0, [[25e8, @key]]) }], @key)
226
264
  tx = block.tx[1]
227
265
 
228
- error = test_command("relay_tx", [tx.to_payload.hth])
266
+ error = test_command("relay_tx", hex: tx.to_payload.hth)
229
267
  error["error"].should == "Transaction context invalid."
230
268
  error["details"].should == ["prev_out", [[@block.tx[0].hash, 0]]]
231
269
  end
@@ -235,9 +273,9 @@ describe 'Node Command API' do
235
273
  create_tx(t, @block.tx[0], 0, [[25e8, @key]]) }], @key)
236
274
  tx = block.tx[1]
237
275
 
238
- test_command("store_block", [@block.to_payload.hth])
276
+ test_command("store_block", hex: @block.to_payload.hth)
239
277
  sleep 0.1
240
- res = test_command("relay_tx", [tx.to_payload.hth, 1, 0])
278
+ res = test_command("relay_tx", hex: tx.to_payload.hth, send: 1, wait: 0)
241
279
  res["success"].should == true
242
280
  res["hash"].should == tx.hash
243
281
  res["propagation"].should == { "sent" => 1, "received" => 0, "percent" => 0.0 }
@@ -250,23 +288,106 @@ describe 'Node Command API' do
250
288
  before do
251
289
  @client = TCPSocket.new(*@config[:command])
252
290
 
253
- def send data
254
- @client.write(data.to_json + "\x00")
291
+ def send method, params = nil, client = @client
292
+ request = { id: @id += 1, method: method, params: params }
293
+ client.write(request.to_json + "\x00")
294
+ request.stringify_keys
255
295
  end
256
296
 
257
- def should_receive expected
258
- buf = ""
259
- while b = @client.read(1)
260
- break if b == "\x00"
261
- buf << b
297
+ def should_receive request, expected, client = @client
298
+ expected = expected.stringify_keys if expected.is_a?(Hash)
299
+ begin
300
+ Timeout.timeout(100) do
301
+ buf = ""
302
+ while b = client.read(1)
303
+ break if b == "\x00"
304
+ buf << b
305
+ end
306
+ resp = JSON.load(buf)
307
+ expected = request.merge(result: expected).stringify_keys
308
+ expected.delete("params")
309
+ raise "ERROR: #{resp} != #{expected}" unless resp.should == expected
310
+ end
311
+ rescue Timeout::Error
312
+ print " [TIMEOUT]"
313
+ :timeout.should == nil
262
314
  end
263
- resp = JSON.load(buf)
264
- resp.should == expected
315
+ end
316
+
317
+ def should_receive_block request, block, depth, client = @client
318
+ expected = { hash: block.hash, hex: block.to_payload.hth, depth: depth }
319
+ should_receive(request, expected, client)
320
+ end
321
+
322
+ def should_receive_tx request, tx, conf, client = @client
323
+ expected = { hash: tx.hash, nhash: tx.nhash, hex: tx.to_payload.hth, conf: conf }
324
+ should_receive(request, expected, client)
325
+ end
326
+
327
+ def should_receive_output request, tx, idx, conf, client = @client
328
+ expected = { hash: tx.hash, nhash: tx.nhash, idx: idx,
329
+ address: tx.out[idx].parsed_script.get_address, value: tx.out[idx].value, conf: conf }
330
+ should_receive(request, expected, client)
265
331
  end
266
332
 
267
333
  def store_block block
268
- send ["store_block", [ block.to_payload.hth ]]
269
- should_receive ["store_block", {"queued" => [ "block", block.hash ]}]
334
+ request = send("store_block", hex: block.to_payload.hth)
335
+ should_receive(request, {"queued" => block.hash })
336
+ end
337
+
338
+ end
339
+
340
+ describe :channels do
341
+
342
+ it "should combine multiple channels" do
343
+ should_receive r1 = send("monitor", channel: "block"), id: 0
344
+ should_receive r2 = send("monitor", channel: "tx", conf: 1), id: 1
345
+ store_block @block
346
+ should_receive_block(r1, @block, 1)
347
+ should_receive_tx(r2, @block.tx[0], 1)
348
+ end
349
+
350
+ it "should handle multiple clients" do
351
+ @client2 = TCPSocket.new(*@config[:command])
352
+ should_receive r1_1 = send("monitor", channel: "tx", conf: 1), id: 0
353
+ r1_2 = send("monitor", { channel: "block" }, @client2)
354
+ should_receive r1_2, { id: 0 }, @client2
355
+
356
+ store_block @block
357
+
358
+ should_receive_block(r1_2, @block, 1, @client2)
359
+ should_receive_tx(r1_1, @block.tx[0], 1)
360
+
361
+ block = create_block @block.hash, false
362
+ store_block block
363
+
364
+ should_receive_block(r1_2, block, 2, @client2)
365
+ should_receive_tx(r1_1, block.tx[0], 1)
366
+
367
+ r2_2 = send "monitor", { channel: "tx", conf: 1 }, @client2
368
+ should_receive r2_2, { id: 1 }, @client2
369
+ should_receive r2_1 = send("monitor", channel: "block"), id: 1
370
+
371
+ block = create_block block.hash, false
372
+ store_block block
373
+
374
+ should_receive_block(r1_2, block, 3, @client2)
375
+ should_receive_tx(r2_2, block.tx[0], 1, @client2)
376
+
377
+ should_receive_tx(r1_1, block.tx[0], 1)
378
+
379
+ # if something was wrong, we would now receive the last tx again
380
+
381
+ should_receive_block(r2_1, block, 3)
382
+
383
+ block = create_block block.hash, false
384
+ store_block block
385
+
386
+ should_receive_tx(r1_1, block.tx[0], 1)
387
+
388
+ should_receive_block(r2_1, block, 4)
389
+ should_receive_block(r1_2, block, 4, @client2)
390
+ should_receive_tx(r2_2, block.tx[0], 1, @client2)
270
391
  end
271
392
 
272
393
  end
@@ -274,18 +395,26 @@ describe 'Node Command API' do
274
395
  describe :block do
275
396
 
276
397
  before do
277
- send ["monitor", ["block"]]
278
- should_receive ["monitor", ["block", [ @genesis.to_hash, 0 ]]]
398
+ @request = send "monitor", channel: "block"
399
+
400
+ should_receive(@request, id: 0)
279
401
  store_block @block
280
- should_receive ["monitor", ["block", [ @block.to_hash, 1 ]]]
402
+ should_receive_block(@request, @block, 1)
281
403
  end
282
404
 
283
405
  it "should monitor block" do
284
406
  @block = create_block @block.hash, false
285
407
  store_block @block
286
- should_receive ["monitor", ["block", [ @block.to_hash, 2 ]]]
408
+ should_receive_block(@request, @block, 2)
287
409
  end
288
410
 
411
+ it "should unmonitor block" do
412
+ @request = send "unmonitor", id: 0
413
+ should_receive @request, id: 0
414
+ store_block create_block(@block.hash, false)
415
+
416
+ test_command("tslb") {|r| (0..TSLB_TIMEOUT).include?(r['tslb']).should == true }
417
+ end
289
418
 
290
419
  it "should not monitor side or orphan blocks" do
291
420
  @side = create_block @genesis.hash, false
@@ -297,37 +426,138 @@ describe 'Node Command API' do
297
426
  # should not send side or orphan block only the next main block
298
427
  @block = create_block @block.hash, false
299
428
  store_block @block
300
- should_receive ["monitor", ["block", [ @block.to_hash, 2 ]]]
429
+
430
+ should_receive_block(@request, @block, 2)
431
+ end
432
+
433
+ it "should received missed blocks when last block hash is given" do
434
+ @client = TCPSocket.new(*@config[:command])
435
+ blocks = [@block]
436
+ 3.times do
437
+ blocks << create_block(blocks.last.hash, false)
438
+ store_block blocks.last
439
+ end
440
+ sleep 0.1
441
+
442
+ r = send "monitor", channel: "block", last: blocks[1].hash
443
+
444
+ should_receive_block(r, blocks[1], 2)
445
+ should_receive_block(r, blocks[2], 3)
446
+ should_receive_block(r, blocks[3], 4)
447
+ end
448
+
449
+ end
450
+
451
+ describe :reorg do
452
+
453
+ before do
454
+ @request = send "monitor", channel: "reorg"
455
+ should_receive @request, id: 0
456
+ store_block @block
457
+ end
458
+
459
+ it "should monitor reorg" do
460
+ @block1 = create_block @genesis.hash, false
461
+ store_block @block1
462
+ @block2 = create_block @block1.hash, false
463
+ store_block @block2
464
+ should_receive @request, { new_main: [ @block1.hash ], new_side: [ @block.hash ] }
465
+ end
466
+
467
+ it "should unmonitor reorg" do
468
+ r = send "unmonitor", id: 0
469
+ should_receive r, id: 0
470
+ @block1 = create_block @genesis.hash, false
471
+ store_block @block1
472
+ @block2 = create_block @block1.hash, false
473
+ store_block @block2
474
+
475
+ test_command("tslb") {|r| (0..TSLB_TIMEOUT).include?(r['tslb']).should == true }
301
476
  end
302
477
 
303
478
  end
304
479
 
305
480
  describe :tx do
306
481
 
482
+
307
483
  it "should monitor unconfirmed tx" do
308
- send ["monitor", ["tx"]]
484
+ r1 = send "monitor", channel: "tx"
485
+ should_receive r1, id: 0
309
486
  tx = @block.tx[0]
310
- send ["store_tx", [ tx.to_payload.hth ] ]
311
- should_receive ["store_tx", { "queued" => [ "tx", tx.hash ]}]
312
- should_receive ["monitor", ["tx", [ tx.to_hash, 0 ]]]
487
+ r2 = send "store_tx", hex: tx.to_payload.hth
488
+ should_receive r2, { "queued" => tx.hash }
489
+
490
+ should_receive_tx(r1, tx, 0)
491
+ end
492
+
493
+ it "should unmonitor tx" do
494
+ r1 = send "monitor", channel: "tx"
495
+ should_receive r1, id: 0
496
+
497
+ r2 = send "unmonitor", id: 0
498
+ should_receive r2, id: 0
499
+
500
+ tx = @block.tx[0]
501
+ r3 = send "store_tx", hex: tx.to_payload.hth
502
+ should_receive r3, { "queued" => tx.hash }
503
+
504
+ test_command("tslb") {|r| (0..TSLB_TIMEOUT).include?(r['tslb']).should == true }
313
505
  end
314
506
 
315
507
  it "should monitor confirmed tx" do
316
- send ["monitor", ["tx_1"]]
508
+ r = send "monitor", channel: "tx", conf: 1
509
+ should_receive r, id: 0
317
510
  store_block @block
318
- should_receive ["monitor", ["tx_1", [ @block.tx[0].to_hash, 1 ]]]
511
+
512
+ should_receive_tx(r, @block.tx[0], 1)
319
513
  end
320
514
 
321
515
  it "should monitor tx for given confirmation level" do
322
- send ["monitor", ["tx_3"]]
516
+ r = send "monitor", channel: "tx", conf: 3
517
+ should_receive r, id: 0
518
+
323
519
  @tx = @block.tx[0]
324
520
  store_block @block
325
521
  @block = create_block @block.hash, false
326
522
  store_block @block
327
- should_receive ["monitor", ["tx_3", [ @genesis.tx[0].to_hash, 3 ]]]
523
+
524
+ should_receive_tx(r, @genesis.tx[0], 3)
525
+
328
526
  @block = create_block @block.hash, false
329
527
  store_block @block
330
- should_receive ["monitor", ["tx_3", [ @tx.to_hash, 3 ]]]
528
+
529
+ should_receive_tx(r, @tx, 3)
530
+ end
531
+
532
+ it "should receive missed txs when last txhash is given" do
533
+ @client = TCPSocket.new(*@config[:command])
534
+ blocks = [@block]; store_block @block
535
+ 3.times do
536
+ blocks << create_block(blocks.last.hash, false)
537
+ store_block blocks.last
538
+ end
539
+ sleep 0.1
540
+
541
+ r = send "monitor", channel: "tx", conf: 1, last: blocks[0].tx[0].hash
542
+
543
+ should_receive_tx(r, blocks[1].tx[0], 3)
544
+ should_receive_tx(r, blocks[2].tx[0], 2)
545
+ should_receive_tx(r, blocks[3].tx[0], 1)
546
+
547
+ should_receive r, id: 0
548
+ end
549
+
550
+
551
+ it "should filter txs for given addresses" do
552
+ @key2 = Bitcoin::Key.generate
553
+ block = create_block(@block.hash, false, [->(t) {
554
+ create_tx(t, @block.tx[0], 0, [[50e8, @key2]]) }], @key)
555
+ @addr = @block.tx[0].out[0].parsed_script.get_address
556
+ r = send "monitor", channel: "tx", conf: 1, addresses: [ @key2.addr ]
557
+ should_receive r, id: 0
558
+ store_block @block
559
+ store_block block
560
+ should_receive_tx(r, block.tx[1], 1)
331
561
  end
332
562
 
333
563
  end
@@ -336,37 +566,94 @@ describe 'Node Command API' do
336
566
 
337
567
  before do
338
568
  @tx = @block.tx[0]; @out = @tx.out[0]
339
- @addr = Bitcoin::Script.new(@out.pk_script).get_address
340
569
  end
341
570
 
342
571
  it "should monitor unconfirmed outputs" do
343
- send ["monitor", ["output"]]
572
+ r1 = send "monitor", channel: "output"
573
+ should_receive r1, id: 0
574
+ tx = @block.tx[0]
575
+ r2 = send "store_tx", hex: tx.to_payload.hth
576
+ should_receive r2, { "queued" => tx.hash }
577
+ should_receive_output(r1, tx, 0, 0)
578
+ end
579
+
580
+ it "should unmonitor outputs" do
581
+ should_receive send("monitor", channel: "output"), id: 0
582
+ should_receive send("unmonitor", id: 0), id: 0
583
+
344
584
  tx = @block.tx[0]
345
- send ["store_tx", [ tx.to_payload.hth ]]
346
- should_receive ["store_tx", { "queued" => [ "tx", tx.hash ]}]
347
- addr = Bitcoin::Script.new(tx.out[0].pk_script).get_address
348
- should_receive ["monitor", ["output", [ tx.hash, addr, tx.out[0].value, 0]]]
585
+ r2 = send "store_tx", hex: tx.to_payload.hth
586
+ should_receive r2, { "queued" => tx.hash }
587
+
588
+ test_command("tslb") {|r| (0..TSLB_TIMEOUT).include?(r['tslb']).should == true }
349
589
  end
350
590
 
351
591
  it "should monitor confirmed output" do
352
- send ["monitor", ["output_1"]]
592
+ r = send "monitor", channel: "output", conf: 1
593
+ should_receive r, id: 0
353
594
  store_block @block
354
- should_receive ["monitor", ["output_1", [ @tx.hash, @addr, @out.value, 1 ]]]
595
+ should_receive_output(r, @tx, 0, 1)
355
596
  end
356
597
 
357
598
  it "should monitor output for given confirmation level" do
358
- send ["monitor", ["output_3"]]
599
+ r = send "monitor", channel: "output", conf: 3
600
+ should_receive r, id: 0
359
601
  store_block @block
360
602
  @block = create_block @block.hash, false
361
603
  store_block @block
362
604
  tx = @genesis.tx[0]; out = tx.out[0]
363
- addr = Bitcoin::Script.new(out.pk_script).get_address
364
- should_receive ["monitor", ["output_3", [ tx.hash, addr, out.value, 3 ]]]
605
+ should_receive_output(r, tx, 0, 3)
365
606
 
366
607
  @block = create_block @block.hash, false
367
608
  store_block @block
368
- should_receive ["monitor", ["output_3", [ @tx.hash, @addr, @out.value, 3 ]]]
369
-
609
+ should_receive_output(r, @tx, 0, 3)
610
+ end
611
+
612
+ it "should receive missed outputs when last txhash:idx is given" do
613
+ @key = Bitcoin::Key.generate
614
+ @client = TCPSocket.new(*@config[:command])
615
+ blocks = [@block]; store_block @block
616
+ 3.times do
617
+ blocks << create_block(blocks.last.hash, false, [], @key)
618
+ store_block blocks.last
619
+ end
620
+ sleep 0.1
621
+
622
+ r = send "monitor", channel: "output", conf: 1, last: "#{blocks[0].tx[0].hash}:0"
623
+
624
+ should_receive_output(r, blocks[1].tx[0], 0, 3)
625
+ should_receive_output(r, blocks[2].tx[0], 0, 2)
626
+ should_receive_output(r, blocks[3].tx[0], 0, 1)
627
+
628
+ should_receive r, id: 0
629
+ end
630
+
631
+ it "should filter outputs for given addresses" do
632
+ @key2 = Bitcoin::Key.generate
633
+ block = create_block(@block.hash, false, [->(t) {
634
+ create_tx(t, @block.tx[0], 0, [[50e8, @key2]]) }], @key)
635
+
636
+ r = send "monitor", channel: "output", conf: 1, addresses: [ @key2.addr ]
637
+ should_receive r, id: 0
638
+ store_block @block
639
+ store_block block
640
+ should_receive_output(r, block.tx[1], 0, 1)
641
+ end
642
+
643
+ it "should add filter address to output monitor params" do
644
+ @key2 = Bitcoin::Key.generate
645
+ block = create_block(@block.hash, false, [->(t) {
646
+ create_tx(t, @block.tx[0], 0, [[50e8, @key2]]) }], @key)
647
+
648
+ r1 = send "monitor", channel: "output", conf: 1, addresses: [ ]
649
+ should_receive r1, id: 0
650
+
651
+ r2 = send "filter_monitor_output", id: 0, address: @key2.addr
652
+ should_receive r2, id: 0
653
+
654
+ store_block @block
655
+ store_block block
656
+ should_receive_output(r1, block.tx[1], 0, 1)
370
657
  end
371
658
 
372
659
  end