bitcoin-ruby 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|