txcatcher 0.2.9 → 0.2.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/txcatcher/catcher.rb +6 -5
- data/lib/txcatcher/models/transaction.rb +5 -2
- data/spec/catcher_spec.rb +96 -56
- data/spec/models/transaction_spec.rb +6 -2
- data/txcatcher.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb9065445b439bf8fbb85b5d4d04af848908d7b7425c10d3035ab4dd1b58d63b
|
4
|
+
data.tar.gz: 7a3b3b10e2c1a94e7189354fae0ff0366a4fc17274edbac21d96ff321b59157d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e9d926e86745773f08798185220e2ad3d8551994f8e552fd61f60f223cb96ae9bff2838235893210c8f26161d4d42e6f2d911c66cf850db44db52a1604b968a
|
7
|
+
data.tar.gz: 655b824f663d94ef2346007bc6735a32065d14a1347ed56e85dcca8e9ed52611bb99add794c561a43865d89b4c5d719ceee094d4f332344825b61bbaa4fde25a
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.10
|
data/lib/txcatcher/catcher.rb
CHANGED
@@ -105,12 +105,13 @@ module TxCatcher
|
|
105
105
|
undetected_transactions_ids = block_transactions_ids - existing_transactions_ids
|
106
106
|
undetected_transactions_ids.each { |txid| Transaction.create_from_rpc(txid) }
|
107
107
|
Transaction.where(txid: existing_transactions_ids).update(block_height: height)
|
108
|
+
# Update RBF transactions and deposits if a transaction a with lower fee
|
109
|
+
# (no associated deposit) got accidentally confirmed.
|
110
|
+
TxCatcher::Transaction.where(block_height: height).exclude(rbf_next_transaction_id: nil).each do |t|
|
111
|
+
t.force_deposit_association_on_rbf!
|
112
|
+
end
|
108
113
|
})
|
109
|
-
|
110
|
-
# accidentally confirmed.
|
111
|
-
TxCatcher::Transaction.where(block_height: height).exclude(rbf_next_transaction_id: nil).each do |t|
|
112
|
-
t.force_deposit_association_on_rbf!
|
113
|
-
end
|
114
|
+
|
114
115
|
end
|
115
116
|
|
116
117
|
end # class Catcher
|
@@ -180,7 +180,7 @@ module TxCatcher
|
|
180
180
|
end
|
181
181
|
|
182
182
|
# Sometimes, even though an RBF transaction with higher fee was broadcasted,
|
183
|
-
# miners accept
|
183
|
+
# miners accept the lower-fee transaction instead. However, in txcatcher database, the
|
184
184
|
# deposits are already associated with the latest transaction. In this case,
|
185
185
|
# we need to find the deposits in the DB set their transaction_id field to current transaction id.
|
186
186
|
def force_deposit_association_on_rbf!
|
@@ -189,7 +189,10 @@ module TxCatcher
|
|
189
189
|
tx = tx.rbf_next_transaction
|
190
190
|
end
|
191
191
|
tx.deposits.each do |d|
|
192
|
-
d.
|
192
|
+
d.rbf_transaction_ids.delete(self.id)
|
193
|
+
d.rbf_transaction_ids.push(d.transaction_id)
|
194
|
+
d.transaction_id = self.id
|
195
|
+
d.save
|
193
196
|
end
|
194
197
|
end
|
195
198
|
|
data/spec/catcher_spec.rb
CHANGED
@@ -8,78 +8,118 @@ require_relative '../lib/txcatcher/catcher'
|
|
8
8
|
|
9
9
|
RSpec.describe TxCatcher::Catcher do
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
@block_sock = ZMQ::Context.create.socket(ZMQ::PUB)
|
11
|
+
# None of the unit tests here should require a running bitcoind
|
12
|
+
describe "(unit tests that require bitcoind to test)" do
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
before(:all) do
|
15
|
+
@tx_sock = ZMQ::Context.create.socket(ZMQ::PUB)
|
16
|
+
@block_sock = ZMQ::Context.create.socket(ZMQ::PUB)
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
@catcher = TxCatcher::Catcher.new(name: "bitcoind_test")
|
21
|
-
sleep 1
|
22
|
-
end
|
18
|
+
@tx_sock.bind("ipc:///tmp/bitcoind_test.rawtx")
|
19
|
+
@block_sock.bind("ipc:///tmp/bitcoind_test.hashblock")
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
@hextx = File.read(File.dirname(__FILE__) + "/fixtures/transaction.txt").strip
|
22
|
+
@rawtx = unhexlify(File.read(File.dirname(__FILE__) + "/fixtures/transaction.txt"))
|
23
|
+
@catcher = TxCatcher::Catcher.new(name: "bitcoind_test")
|
24
|
+
sleep 1
|
25
|
+
end
|
29
26
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
after(:all) do
|
28
|
+
@catcher.close_all_connections
|
29
|
+
@tx_sock.unbind("ipc:///tmp/bitcoind_test.rawtx")
|
30
|
+
@block_sock.unbind('ipc:///tmp/bitcoind_test.hashblock')
|
31
|
+
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
until (tx = TxCatcher::Transaction.last) || i > 3
|
39
|
-
sleep 1
|
40
|
-
i+=1
|
33
|
+
after(:each) do
|
34
|
+
@catcher.sockets["rawtx"][:last_message] = nil
|
35
|
+
@catcher.sockets["hashblock"][:last_message] = nil
|
41
36
|
end
|
42
|
-
expect(tx.hex).to eq(@hextx)
|
43
|
-
end
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
|
38
|
+
it "creates a new transaction in the DB" do
|
39
|
+
@tx_sock.send_strings(['rawtx', @rawtx])
|
40
|
+
i = 0
|
41
|
+
until (tx = TxCatcher::Transaction.last) || i > 3
|
42
|
+
sleep 1
|
43
|
+
i+=1
|
44
|
+
end
|
45
|
+
expect(tx.hex).to eq(@hextx)
|
46
|
+
end
|
48
47
|
|
49
|
-
|
48
|
+
it "updates transactions block height upon receiving a new block " do
|
49
|
+
transaction = TxCatcher::Transaction.create(hex: @hextx)
|
50
|
+
expect(TxCatcher.rpc_node).to receive(:getblock).at_least(:once).and_return({ "height" => TxCatcher.current_block_height + 1, "tx" => [transaction.txid]})
|
50
51
|
|
51
|
-
|
52
|
-
begin
|
53
|
-
sleep 1 and i += 1
|
54
|
-
end until @catcher.sockets["hashblock"][:last_message] || i > 3
|
52
|
+
@block_sock.send_strings(["hashblock", 'hello'])
|
55
53
|
|
56
|
-
|
57
|
-
|
54
|
+
i = 0
|
55
|
+
begin
|
56
|
+
sleep 1 and i += 1
|
57
|
+
end until @catcher.sockets["hashblock"][:last_message] || i > 3
|
58
|
+
|
59
|
+
expect(transaction.reload.block_height).to eq(TxCatcher.current_block_height)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "ignores validation errors" do
|
63
|
+
tx = eval File.read(File.dirname(__FILE__) + "/fixtures/transaction_decoded_no_outputs.txt")
|
64
|
+
expect(TxCatcher.rpc_node).to receive(:decoderawtransaction).at_least(:once).and_return(tx)
|
65
|
+
@tx_sock.send_strings(["rawtx", @rawtx])
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
67
|
+
i = 0
|
68
|
+
begin
|
69
|
+
sleep 1 and i += 1
|
70
|
+
end until @catcher.sockets["rawtx"][:last_message] || i > 3
|
63
71
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
72
|
+
expect(File.exists?(ERRFILE)).to be_falsey
|
73
|
+
end
|
74
|
+
|
75
|
+
it "logs all other errors" do
|
76
|
+
sleep 1
|
77
|
+
expect(TxCatcher.rpc_node).to receive(:decoderawtransaction).at_least(:once).and_raise(StandardError)
|
78
|
+
@tx_sock.send_strings(["rawtx", @rawtx])
|
79
|
+
|
80
|
+
i = 0
|
81
|
+
begin
|
82
|
+
sleep 1 and i += 1
|
83
|
+
end until @catcher.sockets["rawtx"][:last_message] || i > 3
|
84
|
+
expect(File.read(ERRFILE)).not_to be_empty
|
85
|
+
end
|
68
86
|
|
69
|
-
expect(File.exists?(ERRFILE)).to be_falsey
|
70
87
|
end
|
71
88
|
|
72
|
-
|
89
|
+
|
90
|
+
it "updates deposits association with an RBF tx if a transaction with lower fee gets confirmed" do
|
91
|
+
|
92
|
+
class TxCatcher::Transaction
|
93
|
+
def assign_tx_hash(h)
|
94
|
+
@tx_hash = h
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class CatcherStub < TxCatcher::Catcher
|
99
|
+
def listen_to_zeromq_channels(channel);end
|
100
|
+
end
|
101
|
+
|
102
|
+
hextx = File.read(File.dirname(__FILE__) + "/fixtures/transaction.txt").strip
|
103
|
+
tx1 = TxCatcher::Transaction.create(hex: hextx)
|
104
|
+
deposits = tx1.deposits
|
105
|
+
rbf_tx_hash = tx1.tx_hash
|
106
|
+
rbf_tx_hash["txid"] = "rbftxid1"
|
107
|
+
rbf_tx_hash["locktime"] = "1"
|
108
|
+
tx2 = TxCatcher::Transaction.new(hex: hextx)
|
109
|
+
tx2.assign_tx_hash(rbf_tx_hash)
|
110
|
+
tx2.save
|
111
|
+
|
112
|
+
catcher = CatcherStub.new(name: "bitcoind_test")
|
113
|
+
allow(TxCatcher.rpc_node).to receive(:getblock).and_return({ "tx" => [tx1.txid], "height" => 123 })
|
114
|
+
catcher.send(:handle_hashblock, "new block")
|
73
115
|
sleep 1
|
74
|
-
expect(TxCatcher.rpc_node).to receive(:decoderawtransaction).at_least(:once).and_raise(StandardError)
|
75
|
-
@tx_sock.send_strings(["rawtx", @rawtx])
|
76
|
-
|
77
|
-
i = 0
|
78
|
-
begin
|
79
|
-
sleep 1 and i += 1
|
80
|
-
end until @catcher.sockets["rawtx"][:last_message] || i > 3
|
81
|
-
expect(File.read(ERRFILE)).not_to be_empty
|
82
|
-
end
|
83
116
|
|
117
|
+
deposits.each do |d|
|
118
|
+
expect(d.reload.transaction_id).to eq(tx1.id)
|
119
|
+
end
|
120
|
+
|
121
|
+
expect(tx1.reload.deposits.map(&:id)).to eq(deposits.map(&:id))
|
122
|
+
expect(tx2.reload.deposits.map(&:id)).to eq([])
|
123
|
+
end
|
84
124
|
|
85
125
|
end
|
@@ -82,11 +82,15 @@ RSpec.describe TxCatcher::Transaction do
|
|
82
82
|
rbf_tx.assign_tx_hash(rbf_tx_hash)
|
83
83
|
rbf_tx.save
|
84
84
|
|
85
|
+
expect(rbf_tx.reload.deposits.size).to eq(2)
|
86
|
+
expect(@transaction.reload.deposits.size).to eq(0)
|
87
|
+
|
85
88
|
@transaction.reload.force_deposit_association_on_rbf!
|
86
89
|
expect(rbf_tx.reload.deposits).to be_empty
|
87
90
|
expect(@transaction.reload.deposits.map(&:id)).to eq(deposit_ids)
|
91
|
+
|
88
92
|
@transaction.deposits.each do |d|
|
89
|
-
expect(d.rbf_transaction_ids).to eq([
|
93
|
+
expect(d.rbf_transaction_ids).to eq([2])
|
90
94
|
end
|
91
95
|
|
92
96
|
end
|
@@ -96,7 +100,7 @@ RSpec.describe TxCatcher::Transaction do
|
|
96
100
|
it "updates block height by searching if tx is included in one of the previous blocks" do
|
97
101
|
expect(TxCatcher.rpc_node).to receive(:getblockhash).exactly(10).times.and_return("blockhash123")
|
98
102
|
expect(TxCatcher.rpc_node).to receive(:getblock).exactly(10).times.and_return({ "height" => "123", "tx" => [@transaction.txid], "hash" => "blockhash123"})
|
99
|
-
@transaction.
|
103
|
+
@transaction.update_block_height!
|
100
104
|
expect(@transaction.block_height).to eq(123)
|
101
105
|
end
|
102
106
|
|
data/txcatcher.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: txcatcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Snitko
|
@@ -206,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
206
|
- !ruby/object:Gem::Version
|
207
207
|
version: '0'
|
208
208
|
requirements: []
|
209
|
-
rubygems_version: 3.0.
|
209
|
+
rubygems_version: 3.0.8
|
210
210
|
signing_key:
|
211
211
|
specification_version: 4
|
212
212
|
summary: An lightweight version of Bitpay's Insight, allows to check Bitcoin/Litecoin
|