txcatcher 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|