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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +2 -2
- data/COPYING +1 -1
- data/Gemfile +5 -11
- data/README.rdoc +11 -5
- data/Rakefile +5 -0
- data/bin/bitcoin_node +11 -29
- data/bin/bitcoin_node_cli +81 -0
- data/bin/bitcoin_wallet +9 -6
- data/doc/NODE.rdoc +79 -26
- data/examples/bbe_verify_tx.rb +1 -1
- data/examples/index_nhash.rb +24 -0
- data/examples/reindex_p2sh_addrs.rb +44 -0
- data/lib/bitcoin.rb +135 -20
- data/lib/bitcoin/builder.rb +233 -63
- data/lib/bitcoin/key.rb +89 -16
- data/lib/bitcoin/litecoin.rb +13 -11
- data/lib/bitcoin/namecoin.rb +5 -4
- data/lib/bitcoin/network/command_client.rb +23 -13
- data/lib/bitcoin/network/command_handler.rb +336 -131
- data/lib/bitcoin/network/connection_handler.rb +14 -13
- data/lib/bitcoin/network/node.rb +61 -20
- data/lib/bitcoin/protocol.rb +5 -1
- data/lib/bitcoin/protocol/block.rb +15 -3
- data/lib/bitcoin/protocol/parser.rb +3 -3
- data/lib/bitcoin/protocol/tx.rb +82 -20
- data/lib/bitcoin/protocol/txin.rb +7 -0
- data/lib/bitcoin/protocol/txout.rb +12 -9
- data/lib/bitcoin/script.rb +329 -75
- data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
- data/lib/bitcoin/storage/models.rb +6 -11
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
- data/lib/bitcoin/storage/storage.rb +161 -97
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
- data/lib/bitcoin/validation.rb +87 -56
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +38 -0
- data/spec/bitcoin/builder_spec.rb +177 -0
- data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
- data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
- data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
- data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
- data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
- data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
- data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
- data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
- data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
- data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
- data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
- data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
- data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
- data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
- data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
- data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
- data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
- data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
- data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
- data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
- data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
- data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
- data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
- data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
- data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
- data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
- data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
- data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
- data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
- data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
- data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
- data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
- data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
- data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
- data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
- data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
- data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
- data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
- data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
- data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
- data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
- data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
- data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
- data/spec/bitcoin/key_spec.rb +79 -8
- data/spec/bitcoin/namecoin_spec.rb +1 -1
- data/spec/bitcoin/node/command_api_spec.rb +373 -86
- data/spec/bitcoin/performance/storage_spec.rb +41 -0
- data/spec/bitcoin/protocol/addr_spec.rb +7 -5
- data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
- data/spec/bitcoin/protocol/block_spec.rb +6 -0
- data/spec/bitcoin/protocol/tx_spec.rb +184 -1
- data/spec/bitcoin/protocol/txin_spec.rb +27 -0
- data/spec/bitcoin/protocol/txout_spec.rb +27 -0
- data/spec/bitcoin/script/opcodes_spec.rb +74 -3
- data/spec/bitcoin/script/script_spec.rb +271 -0
- data/spec/bitcoin/spec_helper.rb +34 -6
- data/spec/bitcoin/storage/models_spec.rb +104 -0
- data/spec/bitcoin/storage/reorg_spec.rb +42 -11
- data/spec/bitcoin/storage/storage_spec.rb +58 -15
- data/spec/bitcoin/storage/validation_spec.rb +44 -14
- data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
- data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
- data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
- 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
|
-
|
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,
|
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",
|
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"
|
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
|
-
|
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")
|
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
|
92
|
-
info["connections"].should ==
|
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
|
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",
|
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 == "
|
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
|
125
|
-
res.should == { "queued" =>
|
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 ==
|
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",
|
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",
|
140
|
-
|
141
|
-
|
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",
|
147
|
-
|
148
|
-
|
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
|
-
|
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[
|
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",
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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",
|
232
|
+
test_command("store_block", hex: @block.to_payload.hth)
|
196
233
|
sleep 0.1
|
197
|
-
res = test_command("assemble_tx",
|
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",
|
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",
|
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",
|
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",
|
276
|
+
test_command("store_block", hex: @block.to_payload.hth)
|
239
277
|
sleep 0.1
|
240
|
-
res = test_command("relay_tx",
|
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
|
254
|
-
@
|
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
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
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
|
-
|
264
|
-
|
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
|
269
|
-
should_receive
|
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
|
278
|
-
|
398
|
+
@request = send "monitor", channel: "block"
|
399
|
+
|
400
|
+
should_receive(@request, id: 0)
|
279
401
|
store_block @block
|
280
|
-
|
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
|
-
|
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
|
-
|
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
|
484
|
+
r1 = send "monitor", channel: "tx"
|
485
|
+
should_receive r1, id: 0
|
309
486
|
tx = @block.tx[0]
|
310
|
-
send
|
311
|
-
should_receive
|
312
|
-
|
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
|
508
|
+
r = send "monitor", channel: "tx", conf: 1
|
509
|
+
should_receive r, id: 0
|
317
510
|
store_block @block
|
318
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
346
|
-
should_receive
|
347
|
-
|
348
|
-
|
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
|
592
|
+
r = send "monitor", channel: "output", conf: 1
|
593
|
+
should_receive r, id: 0
|
353
594
|
store_block @block
|
354
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|