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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 909ad770012c2bacccbb1e9f7438bf97e4c92d15
4
- data.tar.gz: ec40ed49c4f006e30f4ff07be84fce1d2f05523f
3
+ metadata.gz: 57275484854984cda01f4f29c6660d19db19ff71
4
+ data.tar.gz: 6bb6d1e3de974824ffcfd8e918545b112b4bd0d5
5
5
  SHA512:
6
- metadata.gz: f12b5d7ef16ba43904ec6cebc3ab77a3f1c8fad8588a67b5ad17d1f5076260dee01f21d20928a5f6ae287955ab6c5a83233a05402d8350bd79523e0700095b02
7
- data.tar.gz: 3f737f4a9d32f9ae4c8f9011a3a03856cd55cf9dd4af3e517e0ded3d0dfd656d938bcf8a59008b6a32b9cfc37379ef90e1e0f78317be7f7e8d2b8ca438a3b061
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.5)
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.6)
46
- highline (1.7.8)
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.2.0)
63
- multi_json (1.12.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.0)
67
- mini_portile2 (~> 2.2.0)
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.5.0)
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.0.0)
82
- rdoc (5.1.0)
83
- rspec (3.6.0)
84
- rspec-core (~> 3.6.0)
85
- rspec-expectations (~> 3.6.0)
86
- rspec-mocks (~> 3.6.0)
87
- rspec-core (3.6.0)
88
- rspec-support (~> 3.6.0)
89
- rspec-expectations (3.6.0)
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.6.0)
92
- rspec-mocks (3.6.0)
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.6.0)
95
- rspec-support (3.6.0)
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.49.0)
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.15.4
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 & Runing
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 srart multiple instances of txcatcher, when, for example, you want instances of it
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.5
1
+ 0.1.6
@@ -0,0 +1,7 @@
1
+ Sequel.migration do
2
+ change do
3
+ alter_table :transactions do
4
+ add_column :created_at, DateTime
5
+ end
6
+ end
7
+ end
@@ -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
@@ -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
  })
@@ -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 = TxCatcher::Transaction.last.id - TxCatcher::Transaction.first.id + 1
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").limit(n).each do |t|
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
@@ -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 = Address.where(address: addr).eager(deposits: :transactions).first
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
- it "upon receiving a new block updates transactions block height" do
45
- transaction = TxCatcher::Transaction.create(hex: @hextx)
46
- expect(TxCatcher.rpc_node).to receive(:getblock).and_return({ "height" => TxCatcher.current_block_height + 1, "tx" => [transaction.txid]})
47
- @block_sock.send_string('hashblock', ZMQ::SNDMORE)
48
- @block_sock.send_string("hello")
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.each do |i|
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
 
@@ -20,3 +20,4 @@ rpcnode:
20
20
  # zeromq: bitcoind
21
21
  max_db_transactions_stored: 10
22
22
  db_clean_period_seconds: 300
23
+ protected_transactions: true
@@ -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
@@ -31,6 +31,8 @@ end
31
31
 
32
32
  RSpec.configure do |config|
33
33
 
34
+ config.default_formatter = 'doc'
35
+
34
36
  config.before(:all) do
35
37
  end
36
38
 
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 ruby lib
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.5"
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.5
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: 2017-09-25 00:00:00.000000000 Z
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
@@ -1,5 +0,0 @@
1
- require_relative '../spec_helper'
2
- require_relative '../../lib/txcatcher/models/address'
3
-
4
- RSpec.describe TxCatcher::Address do
5
- end