txcatcher 0.2.9 → 0.2.10
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 +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
|