txcatcher 0.1.5 → 0.1.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 +4 -4
- data/Gemfile.lock +23 -23
- data/README.md +2 -2
- data/VERSION +1 -1
- data/db/migrations/004_add_timestamps_to_transactions.rb +7 -0
- data/db/migrations/005_add_protected_flag_to_transactions.rb +9 -0
- data/lib/txcatcher/catcher.rb +1 -1
- data/lib/txcatcher/cleaner.rb +8 -2
- data/lib/txcatcher/models/transaction.rb +33 -0
- data/lib/txcatcher/server.rb +7 -1
- data/spec/catcher_spec.rb +14 -9
- data/spec/cleaner_spec.rb +13 -3
- data/spec/config/config.yml.sample +1 -0
- data/spec/models/transaction_spec.rb +7 -0
- data/spec/spec_helper.rb +2 -0
- data/txcatcher.gemspec +2 -2
- metadata +4 -4
- data/spec/config/txcatcher_test.db +0 -0
- data/spec/models/address_spec.rb +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57275484854984cda01f4f29c6660d19db19ff71
|
|
4
|
+
data.tar.gz: 6bb6d1e3de974824ffcfd8e918545b112b4bd0d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e81da207158daf3c4b8cf61636deecd85088c2edb7bd191b52007f47639fe00cbc89c923019fc92566a69ac675a56d792e90247338c33b5a787208ddf46b9b2c
|
|
7
|
+
data.tar.gz: dbf857e7c92d76e34e4adfbe3c792115084159d6c2be8b90af2bec9d69b0b417ac1ccc520a208185e36f3de69c8ddfcc91f7e44965c5636ced454742487b28b9
|
data/Gemfile.lock
CHANGED
|
@@ -30,7 +30,7 @@ GEM
|
|
|
30
30
|
hashie (>= 3.4)
|
|
31
31
|
mime-types (>= 1.16, < 3.0)
|
|
32
32
|
oauth2 (~> 1.0)
|
|
33
|
-
goliath (1.0.
|
|
33
|
+
goliath (1.0.6)
|
|
34
34
|
async-rack
|
|
35
35
|
einhorn
|
|
36
36
|
em-synchrony (>= 1.0.0)
|
|
@@ -42,8 +42,8 @@ GEM
|
|
|
42
42
|
rack (>= 1.2.2)
|
|
43
43
|
rack-contrib
|
|
44
44
|
rack-respond_to
|
|
45
|
-
hashie (3.5.
|
|
46
|
-
highline (1.7.
|
|
45
|
+
hashie (3.5.7)
|
|
46
|
+
highline (1.7.10)
|
|
47
47
|
http_parser.rb (0.6.0)
|
|
48
48
|
jeweler (2.3.7)
|
|
49
49
|
builder
|
|
@@ -59,12 +59,12 @@ GEM
|
|
|
59
59
|
jwt (1.5.6)
|
|
60
60
|
log4r (1.1.10)
|
|
61
61
|
mime-types (2.99.3)
|
|
62
|
-
mini_portile2 (2.
|
|
63
|
-
multi_json (1.
|
|
62
|
+
mini_portile2 (2.3.0)
|
|
63
|
+
multi_json (1.13.1)
|
|
64
64
|
multi_xml (0.6.0)
|
|
65
65
|
multipart-post (2.0.0)
|
|
66
|
-
nokogiri (1.8.
|
|
67
|
-
mini_portile2 (~> 2.
|
|
66
|
+
nokogiri (1.8.1)
|
|
67
|
+
mini_portile2 (~> 2.3.0)
|
|
68
68
|
oauth2 (1.4.0)
|
|
69
69
|
faraday (>= 0.8, < 0.13)
|
|
70
70
|
jwt (~> 1.0)
|
|
@@ -74,28 +74,28 @@ GEM
|
|
|
74
74
|
psych (2.2.4)
|
|
75
75
|
rack (1.6.8)
|
|
76
76
|
rack-accept-media-types (0.9)
|
|
77
|
-
rack-contrib (1.
|
|
77
|
+
rack-contrib (1.8.0)
|
|
78
78
|
rack (~> 1.4)
|
|
79
79
|
rack-respond_to (0.9.8)
|
|
80
80
|
rack-accept-media-types (>= 0.6)
|
|
81
|
-
rake (12.
|
|
82
|
-
rdoc (
|
|
83
|
-
rspec (3.
|
|
84
|
-
rspec-core (~> 3.
|
|
85
|
-
rspec-expectations (~> 3.
|
|
86
|
-
rspec-mocks (~> 3.
|
|
87
|
-
rspec-core (3.
|
|
88
|
-
rspec-support (~> 3.
|
|
89
|
-
rspec-expectations (3.
|
|
81
|
+
rake (12.3.0)
|
|
82
|
+
rdoc (6.0.1)
|
|
83
|
+
rspec (3.7.0)
|
|
84
|
+
rspec-core (~> 3.7.0)
|
|
85
|
+
rspec-expectations (~> 3.7.0)
|
|
86
|
+
rspec-mocks (~> 3.7.0)
|
|
87
|
+
rspec-core (3.7.1)
|
|
88
|
+
rspec-support (~> 3.7.0)
|
|
89
|
+
rspec-expectations (3.7.0)
|
|
90
90
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
91
|
-
rspec-support (~> 3.
|
|
92
|
-
rspec-mocks (3.
|
|
91
|
+
rspec-support (~> 3.7.0)
|
|
92
|
+
rspec-mocks (3.7.0)
|
|
93
93
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
94
|
-
rspec-support (~> 3.
|
|
95
|
-
rspec-support (3.
|
|
94
|
+
rspec-support (~> 3.7.0)
|
|
95
|
+
rspec-support (3.7.0)
|
|
96
96
|
satoshi-unit (0.2.2)
|
|
97
97
|
semver2 (3.4.2)
|
|
98
|
-
sequel (4.
|
|
98
|
+
sequel (5.4.0)
|
|
99
99
|
sqlite3 (1.3.13)
|
|
100
100
|
thread_safe (0.3.6)
|
|
101
101
|
|
|
@@ -113,4 +113,4 @@ DEPENDENCIES
|
|
|
113
113
|
sqlite3
|
|
114
114
|
|
|
115
115
|
BUNDLED WITH
|
|
116
|
-
1.
|
|
116
|
+
1.16.0
|
data/README.md
CHANGED
|
@@ -71,7 +71,7 @@ Installation
|
|
|
71
71
|
|
|
72
72
|
Install txcatcher: `gem install txcatcher`
|
|
73
73
|
|
|
74
|
-
Configuring &
|
|
74
|
+
Configuring & Running
|
|
75
75
|
--------------------
|
|
76
76
|
You're going to need a simple config file. By default, txcatcher looks in `~/.txcatcher` dir,
|
|
77
77
|
so create the dir and the file there: `mkdir ~/.txcatcher && touch ~/.txcatcher/config.yml`
|
|
@@ -79,7 +79,7 @@ Example config file can be found in [templates/config.yml](templates/config.yml)
|
|
|
79
79
|
to copy the contents and edit it.
|
|
80
80
|
|
|
81
81
|
Once your bitcoin/litecoind finished syncing, you can start txcatcher with a simple command: `txcatcher`.
|
|
82
|
-
You can
|
|
82
|
+
You can start multiple instances of txcatcher, when, for example, you want instances of it
|
|
83
83
|
tracking both litecoin and bitcoin transactions:
|
|
84
84
|
|
|
85
85
|
txcatcher -c ~/.txcatcher/litecoin_config.yml
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.6
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
change do
|
|
3
|
+
alter_table :transactions do
|
|
4
|
+
# This flag means someone has checked it once through the API and we might not want to remove
|
|
5
|
+
# it from the DB (upon scheduled cleanup) because it's of a potential interest.
|
|
6
|
+
add_column :protected, FalseClass, default: false
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
data/lib/txcatcher/catcher.rb
CHANGED
|
@@ -93,7 +93,7 @@ module TxCatcher
|
|
|
93
93
|
block_hash = TxCatcher.rpc_node.getblock(block_hex)
|
|
94
94
|
transactions = block_hash["tx"]
|
|
95
95
|
height = TxCatcher.current_block_height = block_hash["height"].to_i
|
|
96
|
-
$stdout.puts "Block #{height} mined, transactions received:\n #{transactions.join(" \n")}"
|
|
96
|
+
$stdout.puts "*** Block #{height} mined, transactions received:\n #{transactions.join(" \n")}"
|
|
97
97
|
@queue["hashblock"] << ( Proc.new {
|
|
98
98
|
Transaction.where(txid: transactions).update(block_height: height)
|
|
99
99
|
})
|
data/lib/txcatcher/cleaner.rb
CHANGED
|
@@ -17,7 +17,11 @@ module TxCatcher
|
|
|
17
17
|
Thread.new do
|
|
18
18
|
loop do
|
|
19
19
|
@@cleaning_counter = { transactions: 0, deposits: 0, addresses: 0 }
|
|
20
|
-
db_tx_count =
|
|
20
|
+
db_tx_count = if Config.protected_transactions
|
|
21
|
+
TxCatcher::Transaction.where(Sequel.~(protected: true)).count
|
|
22
|
+
else
|
|
23
|
+
TxCatcher::Transaction.count
|
|
24
|
+
end
|
|
21
25
|
$stdout.print "Cleaning transactions in DB..."
|
|
22
26
|
$stdout.print "#{db_tx_count} now, needs to be below #{Config.max_db_transactions_stored}\n"
|
|
23
27
|
if db_tx_count > Config.max_db_transactions_stored
|
|
@@ -42,7 +46,9 @@ module TxCatcher
|
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
def clean_transactions(n)
|
|
45
|
-
transactions = Transaction.order("created_at ASC")
|
|
49
|
+
transactions = Transaction.order("created_at ASC")
|
|
50
|
+
transactions.where(Sequel.~(protected: true)) if Config.protected_transactions
|
|
51
|
+
transactions.limit(n).each do |t|
|
|
46
52
|
clean_deposits(t)
|
|
47
53
|
t.delete
|
|
48
54
|
@@cleaning_counter[:transactions] += 1
|
|
@@ -19,6 +19,10 @@ module TxCatcher
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def before_create
|
|
23
|
+
self.created_at = Time.now
|
|
24
|
+
end
|
|
25
|
+
|
|
22
26
|
def after_create
|
|
23
27
|
self.deposits.each do |d|
|
|
24
28
|
d.transaction = self
|
|
@@ -38,6 +42,35 @@ module TxCatcher
|
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
|
|
45
|
+
# Queries rpc node to check whether the transaction has been included in any of the blocks,
|
|
46
|
+
# but only if current block_height is nil.
|
|
47
|
+
def update_block_height!(limit=100)
|
|
48
|
+
|
|
49
|
+
# This calculates the approximate number of blocks to check.
|
|
50
|
+
# So, for example, if transaction is less than 10 minutes old,
|
|
51
|
+
# there's probably no reason to try and check more than 2-3 blocks back.
|
|
52
|
+
# However, to make absolute sure, we always bump up this number for 10 blocks.
|
|
53
|
+
# Over larger periods of time, the avg block per minute value should even out, so
|
|
54
|
+
# it's probably going to be fine either way.
|
|
55
|
+
created_minutes_ago = ((self.created_at - Time.now).to_i/60)
|
|
56
|
+
blocks_to_check = (created_minutes_ago / 10).abs + 10
|
|
57
|
+
return self.block_height if self.block_height
|
|
58
|
+
blocks_to_check = limit if blocks_to_check > limit
|
|
59
|
+
|
|
60
|
+
i = 0
|
|
61
|
+
while i <= blocks_to_check
|
|
62
|
+
block_hash = TxCatcher.rpc_node.getblockhash(TxCatcher.current_block_height - i)
|
|
63
|
+
block = TxCatcher.rpc_node.getblock(block_hash)
|
|
64
|
+
i += 1
|
|
65
|
+
$stdout.puts "--- checking block #{TxCatcher.current_block_height - i} for tx #{self.txid}"
|
|
66
|
+
if block["tx"] && block["tx"].include?(self.txid)
|
|
67
|
+
$stdout.puts "tx #{self.txid} block height updated to #{block["height"]}"
|
|
68
|
+
self.update(block_height: block["height"])
|
|
69
|
+
return block["height"].to_i
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
41
74
|
private
|
|
42
75
|
|
|
43
76
|
def parse_transaction
|
data/lib/txcatcher/server.rb
CHANGED
|
@@ -32,7 +32,9 @@ module TxCatcher
|
|
|
32
32
|
if address
|
|
33
33
|
transactions_ids = address.deposits.map { |d| d.transaction.txid }
|
|
34
34
|
deposits = address.deposits.map do |d|
|
|
35
|
+
t.update(protected: true) unless t.protected
|
|
35
36
|
t = d.transaction
|
|
37
|
+
t.update_block_height!
|
|
36
38
|
{
|
|
37
39
|
txid: t.txid,
|
|
38
40
|
amount: d.amount_in_btc,
|
|
@@ -52,11 +54,15 @@ module TxCatcher
|
|
|
52
54
|
path.pop
|
|
53
55
|
addr = path.last
|
|
54
56
|
|
|
55
|
-
model
|
|
57
|
+
model = Address.where(address: addr).eager(deposits: :transactions).first
|
|
58
|
+
return [200, {}, "{}"] unless model
|
|
59
|
+
|
|
56
60
|
transactions = model.deposits.map { |d| d.transaction }
|
|
57
61
|
utxos = transactions.map do |t|
|
|
58
62
|
outs = t.tx_hash["vout"].select { |out| out["scriptPubKey"]["addresses"] == [addr] }
|
|
59
63
|
outs.map! do |out|
|
|
64
|
+
t.update(protected: true) unless t.protected
|
|
65
|
+
t.update_block_height!
|
|
60
66
|
out["confirmations"] = t.confirmations || 0
|
|
61
67
|
out["txid"] = t.txid
|
|
62
68
|
out
|
data/spec/catcher_spec.rb
CHANGED
|
@@ -41,17 +41,22 @@ RSpec.describe TxCatcher::Catcher do
|
|
|
41
41
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
describe "upon receiving a new block" do
|
|
45
|
+
|
|
46
|
+
it "updates transactions block height" do
|
|
47
|
+
transaction = TxCatcher::Transaction.create(hex: @hextx)
|
|
48
|
+
allow(TxCatcher.rpc_node).to receive(:getblock).and_return({ "height" => TxCatcher.current_block_height + 1, "tx" => [transaction.txid]})
|
|
49
|
+
@block_sock.send_string('hashblock', ZMQ::SNDMORE)
|
|
50
|
+
@block_sock.send_string("hello")
|
|
51
|
+
#transaction.update_block_height!
|
|
52
|
+
|
|
53
|
+
i = 0
|
|
54
|
+
until transaction.reload.confirmations == 1
|
|
55
|
+
sleep 1 and i += 1
|
|
56
|
+
end
|
|
57
|
+
expect(transaction.block_height).to eq(TxCatcher.current_block_height)
|
|
49
58
|
|
|
50
|
-
i = 0
|
|
51
|
-
until transaction.reload.confirmations == 1
|
|
52
|
-
sleep 1 and i += 1
|
|
53
59
|
end
|
|
54
|
-
expect(transaction.block_height).to eq(TxCatcher.current_block_height)
|
|
55
60
|
|
|
56
61
|
end
|
|
57
62
|
|
data/spec/cleaner_spec.rb
CHANGED
|
@@ -42,13 +42,23 @@ RSpec.describe TxCatcher::Cleaner do
|
|
|
42
42
|
expect(TxCatcher::Address.count).to eq(10)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
it "protects checked transactions" do
|
|
46
|
+
protected_txs = create_transactions(3, { protected: true })
|
|
47
|
+
regular_txs = create_transactions(15)
|
|
48
|
+
clean_transactions
|
|
49
|
+
expect(TxCatcher::Transaction.count).to eq(12)
|
|
50
|
+
expect(TxCatcher::Deposit.count).to eq(12)
|
|
51
|
+
expect(TxCatcher::Address.count).to eq(12)
|
|
52
|
+
end
|
|
53
|
+
|
|
45
54
|
|
|
46
|
-
def create_transactions(n)
|
|
47
|
-
(1..n).to_a.
|
|
55
|
+
def create_transactions(n, attrs={})
|
|
56
|
+
(1..n).to_a.map do |i|
|
|
48
57
|
d = TxCatcher::Deposit.new(address_string: "addr#{i}", amount: 0)
|
|
49
|
-
tx = TxCatcher::Transaction.new
|
|
58
|
+
tx = TxCatcher::Transaction.new(attrs)
|
|
50
59
|
tx.deposits << d
|
|
51
60
|
tx.save
|
|
61
|
+
tx
|
|
52
62
|
end
|
|
53
63
|
end
|
|
54
64
|
|
|
@@ -26,4 +26,11 @@ RSpec.describe TxCatcher::Transaction do
|
|
|
26
26
|
expect(TxCatcher::Transaction.where(txid: transaction.txid).count).to eq(1)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
it "updates block height by making manual requests to RPC and searching if tx is included in one of the previous blocks" do
|
|
30
|
+
expect(TxCatcher.rpc_node).to receive(:getblockhash).and_return("blockhash123")
|
|
31
|
+
expect(TxCatcher.rpc_node).to receive(:getblock).and_return({ "height" => "123", "tx" => [@transaction.txid], "hash" => "blockhash123"})
|
|
32
|
+
@transaction.update_block_height!
|
|
33
|
+
expect(@transaction.block_height).to eq(123)
|
|
34
|
+
end
|
|
35
|
+
|
|
29
36
|
end
|
data/spec/spec_helper.rb
CHANGED
data/txcatcher.gemspec
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
|
5
|
-
# stub: txcatcher 0.1.
|
|
5
|
+
# stub: txcatcher 0.1.6 ruby lib
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |s|
|
|
8
8
|
s.name = "txcatcher"
|
|
9
|
-
s.version = "0.1.
|
|
9
|
+
s.version = "0.1.6"
|
|
10
10
|
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
12
12
|
s.require_paths = ["lib"]
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: txcatcher
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Snitko
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-01-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: goliath
|
|
@@ -120,6 +120,8 @@ files:
|
|
|
120
120
|
- db/migrations/001_create_transactions.rb
|
|
121
121
|
- db/migrations/002_create_addresses.rb
|
|
122
122
|
- db/migrations/003_create_deposits.rb
|
|
123
|
+
- db/migrations/004_add_timestamps_to_transactions.rb
|
|
124
|
+
- db/migrations/005_add_protected_flag_to_transactions.rb
|
|
123
125
|
- db/schema.rb
|
|
124
126
|
- lib/tasks/db.rake
|
|
125
127
|
- lib/txcatcher.rb
|
|
@@ -136,10 +138,8 @@ files:
|
|
|
136
138
|
- spec/catcher_spec.rb
|
|
137
139
|
- spec/cleaner_spec.rb
|
|
138
140
|
- spec/config/config.yml.sample
|
|
139
|
-
- spec/config/txcatcher_test.db
|
|
140
141
|
- spec/fixtures/transaction.txt
|
|
141
142
|
- spec/fixtures/transaction_decoded_no_outputs.txt
|
|
142
|
-
- spec/models/address_spec.rb
|
|
143
143
|
- spec/models/transaction_spec.rb
|
|
144
144
|
- spec/spec_helper.rb
|
|
145
145
|
- templates/config.yml
|
|
Binary file
|