txcatcher 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +116 -0
- data/LICENSE.txt +20 -0
- data/README.md +11 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/bin/txcatcher +9 -0
- data/db/migrations/001_create_transactions.rb +11 -0
- data/db/migrations/002_create_addresses.rb +9 -0
- data/db/migrations/003_create_deposits.rb +10 -0
- data/db/schema.rb +35 -0
- data/lib/tasks/db.rake +52 -0
- data/lib/txcatcher/bitcoin_rpc.rb +30 -0
- data/lib/txcatcher/catcher.rb +103 -0
- data/lib/txcatcher/cleaner.rb +65 -0
- data/lib/txcatcher/config.rb +20 -0
- data/lib/txcatcher/initializer.rb +148 -0
- data/lib/txcatcher/models/address.rb +13 -0
- data/lib/txcatcher/models/deposit.rb +23 -0
- data/lib/txcatcher/models/transaction.rb +55 -0
- data/lib/txcatcher/server.rb +74 -0
- data/lib/txcatcher/utils/hash_string_to_sym_keys.rb +24 -0
- data/lib/txcatcher.rb +21 -0
- data/spec/catcher_spec.rb +96 -0
- data/spec/cleaner_spec.rb +60 -0
- data/spec/config/config.yml +22 -0
- data/spec/config/txcatcher_test.db +0 -0
- data/spec/fixtures/transaction.txt +1 -0
- data/spec/fixtures/transaction_decoded_no_outputs.txt +1 -0
- data/spec/models/address_spec.rb +5 -0
- data/spec/models/transaction_spec.rb +26 -0
- data/spec/spec_helper.rb +46 -0
- data/templates/config.yml +23 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 19e98ad3469e868a632b24ac5b511e557be28c9c
|
4
|
+
data.tar.gz: 83b80df4f5cd3b2367adfbc26e9782184c8901f7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6e5e8b687a4feb36016a0ebe7f8e07cb0c604dc9add1d31b6ce0ca412fa871f7222f45f102cef733017276277b301a52dd9f3de56a06deaa7581ef2eb1819cb5
|
7
|
+
data.tar.gz: 8436da5751fdf20ce4ef8d45d0d58842d3f807e73ae8fe0b83412c919b41e1bccb47d2c780e07c5cc9d6ae3f083953e195ca73c8bc1dd8bbf2b370126a1f3efc
|
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.4.0)
|
5
|
+
async-rack (0.5.1)
|
6
|
+
rack (~> 1.1)
|
7
|
+
builder (3.2.3)
|
8
|
+
descendants_tracker (0.0.4)
|
9
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
10
|
+
diff-lcs (1.3)
|
11
|
+
einhorn (0.7.4)
|
12
|
+
em-synchrony (1.0.6)
|
13
|
+
eventmachine (>= 1.0.0.beta.1)
|
14
|
+
em-websocket (0.3.8)
|
15
|
+
addressable (>= 2.1.1)
|
16
|
+
eventmachine (>= 0.12.9)
|
17
|
+
eventmachine (1.2.5)
|
18
|
+
faraday (0.9.2)
|
19
|
+
multipart-post (>= 1.2, < 3)
|
20
|
+
ffi (1.9.18)
|
21
|
+
ffi-rzmq (2.0.5)
|
22
|
+
ffi-rzmq-core (>= 1.0.6)
|
23
|
+
ffi-rzmq-core (1.0.6)
|
24
|
+
ffi
|
25
|
+
git (1.3.0)
|
26
|
+
github_api (0.16.0)
|
27
|
+
addressable (~> 2.4.0)
|
28
|
+
descendants_tracker (~> 0.0.4)
|
29
|
+
faraday (~> 0.8, < 0.10)
|
30
|
+
hashie (>= 3.4)
|
31
|
+
mime-types (>= 1.16, < 3.0)
|
32
|
+
oauth2 (~> 1.0)
|
33
|
+
goliath (1.0.5)
|
34
|
+
async-rack
|
35
|
+
einhorn
|
36
|
+
em-synchrony (>= 1.0.0)
|
37
|
+
em-websocket (= 0.3.8)
|
38
|
+
eventmachine (>= 1.0.0.beta.4)
|
39
|
+
http_parser.rb (>= 0.6.0)
|
40
|
+
log4r
|
41
|
+
multi_json
|
42
|
+
rack (>= 1.2.2)
|
43
|
+
rack-contrib
|
44
|
+
rack-respond_to
|
45
|
+
hashie (3.5.6)
|
46
|
+
highline (1.7.8)
|
47
|
+
http_parser.rb (0.6.0)
|
48
|
+
jeweler (2.3.7)
|
49
|
+
builder
|
50
|
+
bundler (>= 1)
|
51
|
+
git (>= 1.2.5)
|
52
|
+
github_api (~> 0.16.0)
|
53
|
+
highline (>= 1.6.15)
|
54
|
+
nokogiri (>= 1.5.10)
|
55
|
+
psych (~> 2.2)
|
56
|
+
rake
|
57
|
+
rdoc
|
58
|
+
semver2
|
59
|
+
jwt (1.5.6)
|
60
|
+
log4r (1.1.10)
|
61
|
+
mime-types (2.99.3)
|
62
|
+
mini_portile2 (2.2.0)
|
63
|
+
multi_json (1.12.1)
|
64
|
+
multi_xml (0.6.0)
|
65
|
+
multipart-post (2.0.0)
|
66
|
+
nokogiri (1.8.0)
|
67
|
+
mini_portile2 (~> 2.2.0)
|
68
|
+
oauth2 (1.4.0)
|
69
|
+
faraday (>= 0.8, < 0.13)
|
70
|
+
jwt (~> 1.0)
|
71
|
+
multi_json (~> 1.3)
|
72
|
+
multi_xml (~> 0.5)
|
73
|
+
rack (>= 1.2, < 3)
|
74
|
+
psych (2.2.4)
|
75
|
+
rack (1.6.8)
|
76
|
+
rack-accept-media-types (0.9)
|
77
|
+
rack-contrib (1.5.0)
|
78
|
+
rack (~> 1.4)
|
79
|
+
rack-respond_to (0.9.8)
|
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)
|
90
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
91
|
+
rspec-support (~> 3.6.0)
|
92
|
+
rspec-mocks (3.6.0)
|
93
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
94
|
+
rspec-support (~> 3.6.0)
|
95
|
+
rspec-support (3.6.0)
|
96
|
+
satoshi-unit (0.2.2)
|
97
|
+
semver2 (3.4.2)
|
98
|
+
sequel (4.49.0)
|
99
|
+
sqlite3 (1.3.13)
|
100
|
+
thread_safe (0.3.6)
|
101
|
+
|
102
|
+
PLATFORMS
|
103
|
+
ruby
|
104
|
+
|
105
|
+
DEPENDENCIES
|
106
|
+
bundler
|
107
|
+
ffi-rzmq
|
108
|
+
goliath
|
109
|
+
jeweler
|
110
|
+
rspec
|
111
|
+
satoshi-unit
|
112
|
+
sequel
|
113
|
+
sqlite3
|
114
|
+
|
115
|
+
BUNDLED WITH
|
116
|
+
1.15.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2017 Roman Snitko
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
= txcatcher
|
2
|
+
|
3
|
+
Dependencies
|
4
|
+
------------
|
5
|
+
|
6
|
+
In order for bitcoin-core and litecoin-core to stream new transactions
|
7
|
+
and blocks, we need ZeroMQ. Ubuntu/Debian installation instructions are here:
|
8
|
+
|
9
|
+
http://zeromq.org/distro:debian
|
10
|
+
|
11
|
+
you can safely use a package weirdly located at download.opensuse.org.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
17
|
+
gem.name = "txcatcher"
|
18
|
+
gem.homepage = "http://github.com/snitko/txcatcher"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{An lightweight version of Bitpay's Insight, allows to check Bitcoin/Litecoin addresses}
|
21
|
+
gem.description = %Q{Ccurrently, the only job of this gem is to collect all new Bitcoin/Litecoin transactions, store them in a DB, index addresses.}
|
22
|
+
gem.email = "roman.snitko@gmail.com"
|
23
|
+
gem.authors = ["Roman Snitko"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
Dir.glob('lib/tasks/*.rake').each { |r| load r }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/txcatcher
ADDED
data/db/schema.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
change do
|
3
|
+
create_table(:addresses, :ignore_index_errors=>true) do
|
4
|
+
primary_key :id
|
5
|
+
String :address, :size=>255
|
6
|
+
Integer :received, :default=>0
|
7
|
+
|
8
|
+
index [:address]
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table(:deposits, :ignore_index_errors=>true) do
|
12
|
+
primary_key :id
|
13
|
+
Integer :amount, :null=>false
|
14
|
+
Integer :transaction_id
|
15
|
+
Integer :address_id
|
16
|
+
|
17
|
+
index [:address_id]
|
18
|
+
index [:transaction_id]
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table(:schema_info) do
|
22
|
+
Integer :version, :default=>0, :null=>false
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table(:transactions, :ignore_index_errors=>true) do
|
26
|
+
primary_key :id
|
27
|
+
String :txid, :size=>255
|
28
|
+
String :hex, :text=>true
|
29
|
+
Integer :amount
|
30
|
+
Integer :block_height
|
31
|
+
|
32
|
+
index [:txid]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/tasks/db.rake
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'yaml'
|
3
|
+
require 'json'
|
4
|
+
require 'sequel'
|
5
|
+
require_relative '../txcatcher/utils/hash_string_to_sym_keys'
|
6
|
+
require_relative '../txcatcher/config'
|
7
|
+
require_relative '../txcatcher/initializer'
|
8
|
+
|
9
|
+
Sequel.extension :migration
|
10
|
+
|
11
|
+
namespace :db do
|
12
|
+
|
13
|
+
task :environment do
|
14
|
+
include TxCatcher::Initializer
|
15
|
+
ConfigFile.set!
|
16
|
+
create_config_files
|
17
|
+
read_config_file
|
18
|
+
connect_to_db
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Migrates the database"
|
22
|
+
task :migrate, [:step] => :environment do |t, args|
|
23
|
+
target = args[:step] && (step = args[:step].to_i) > 0 ?
|
24
|
+
current_migration_version + step : nil
|
25
|
+
|
26
|
+
Sequel::Migrator.run(TxCatcher.db_connection, MIGRATIONS_ROOT, target: target)
|
27
|
+
puts "Migrated DB to version #{current_migration_version}"
|
28
|
+
dump_schema
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Rollbacks database migrations"
|
32
|
+
task :rollback, [:step] => :environment do |t, args|
|
33
|
+
target = args[:step] && (step = args[:step].to_i) > 0 ?
|
34
|
+
current_migration_version - step : current_migration_version - 1
|
35
|
+
|
36
|
+
Sequel::Migrator.run(TxCatcher.db_connection, MIGRATIONS_ROOT, target: target)
|
37
|
+
puts "Rolled back DB to version #{current_migration_version}"
|
38
|
+
dump_schema
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_migration_version
|
42
|
+
db = TxCatcher.db_connection
|
43
|
+
Sequel::Migrator.migrator_class(MIGRATIONS_ROOT).new(db, MIGRATIONS_ROOT, {}).current
|
44
|
+
end
|
45
|
+
|
46
|
+
def dump_schema
|
47
|
+
TxCatcher.db_connection.extension :schema_dumper
|
48
|
+
open('db/schema.rb', 'w') do |f|
|
49
|
+
f.puts TxCatcher.db_connection.dump_schema_migration(same_db: false)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Credits for this file go to
|
2
|
+
# Mikica Ivosevic https://github.com/mikicaivosevic/bitcoin-rpc-ruby/
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class BitcoinRPC
|
9
|
+
def initialize(service_url)
|
10
|
+
@uri = URI.parse(service_url)
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(name, *args)
|
14
|
+
post_body = { 'method' => name, 'params' => args, 'id' => 'jsonrpc' }.to_json
|
15
|
+
resp = JSON.parse( http_post_request(post_body) )
|
16
|
+
raise JSONRPCError, resp['error'].to_s + " (method: '#{name}')" if resp['error']
|
17
|
+
resp['result']
|
18
|
+
end
|
19
|
+
|
20
|
+
def http_post_request(post_body)
|
21
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
22
|
+
request = Net::HTTP::Post.new(@uri.request_uri)
|
23
|
+
request.basic_auth @uri.user, @uri.password
|
24
|
+
request.content_type = 'application/json'
|
25
|
+
request.body = post_body
|
26
|
+
http.request(request).body
|
27
|
+
end
|
28
|
+
|
29
|
+
class JSONRPCError < RuntimeError; end
|
30
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module TxCatcher
|
2
|
+
|
3
|
+
class Catcher
|
4
|
+
|
5
|
+
attr_accessor :name, :sockets
|
6
|
+
|
7
|
+
def initialize(name:, socket: "ipc:///tmp/")
|
8
|
+
@queue = {}
|
9
|
+
@sockets = {}
|
10
|
+
|
11
|
+
{'rawtx' => "#{socket}#{name}.rawtx", 'hashblock' => "#{socket}#{name}.hashblock"}.each do |channel, address|
|
12
|
+
puts "Start listening on #{name} #{channel}... (#{address})"
|
13
|
+
listen_to_zeromq_message(channel: channel, address: address)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def hexlify(s)
|
20
|
+
a = []
|
21
|
+
s.each_byte do |b|
|
22
|
+
a << sprintf('%02X', b)
|
23
|
+
end
|
24
|
+
a.join
|
25
|
+
end
|
26
|
+
|
27
|
+
def listen_to_zeromq_message(channel:, address:)
|
28
|
+
@queue[channel] = Queue.new
|
29
|
+
|
30
|
+
# This thread is responsible for actions after the messages from ZeroMQ is parsed,
|
31
|
+
# typically it's writing data to DB through the models. We start it
|
32
|
+
# before we start listening to any messages from ZeroMQ.
|
33
|
+
queue_thread = Thread.new do
|
34
|
+
loop do
|
35
|
+
puts "in #{channel} queue: #{@queue[channel].size}"
|
36
|
+
if @queue[channel].empty?
|
37
|
+
sleep 1
|
38
|
+
else
|
39
|
+
begin
|
40
|
+
@queue[channel].pop.call
|
41
|
+
rescue Sequel::ValidationFailed => e
|
42
|
+
$stdout.puts "[WARNING #{Time.now.to_s}] #{e.class} #{e.to_s}\n"
|
43
|
+
rescue StandardError => e
|
44
|
+
File.open(TxCatcher::Config.config_dir + "/error.log", "a") do |f|
|
45
|
+
f.puts "[ERROR #{Time.now.to_s}] #{e.class} #{e.to_s}\n #{e.backtrace.join("\n ")}\n\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Now we can start receiving messages from ZeroMQ.
|
53
|
+
# On every received message we call a handler method, which parses it
|
54
|
+
# appropriately (each ZeroMQ channel has its own handler method) and then
|
55
|
+
# adds additional tasks, such as writing to the DB, in the queue.
|
56
|
+
# They queue itself is handled in the thread created above.
|
57
|
+
key = "#{channel}#{address}"
|
58
|
+
handler_thread = Thread.new do
|
59
|
+
context = ZMQ::Context.new
|
60
|
+
socket = context.socket(ZMQ::SUB)
|
61
|
+
socket.setsockopt(ZMQ::SUBSCRIBE, channel)
|
62
|
+
socket.connect(address)
|
63
|
+
@sockets[key] = { object: socket }
|
64
|
+
loop do
|
65
|
+
topic = []
|
66
|
+
message = []
|
67
|
+
socket.recv_multipart(topic, message)
|
68
|
+
message
|
69
|
+
if message[1]
|
70
|
+
message_hex = hexlify(message[1].copy_out_string).downcase
|
71
|
+
@sockets[key][:last_message] = message_hex
|
72
|
+
send("handle_#{channel}", message_hex)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end # listen_to_zeromq_message
|
78
|
+
|
79
|
+
def handle_rawtx(txhex)
|
80
|
+
$stdout.print "received tx hex: #{txhex[0..50]}...\n"
|
81
|
+
|
82
|
+
@queue["rawtx"] << ( Proc.new {
|
83
|
+
tx = TxCatcher::Transaction.new(hex: txhex)
|
84
|
+
tx.save
|
85
|
+
$stdout.puts "tx #{tx.txid} saved (id: #{tx.id}), deposits (outputs):"
|
86
|
+
tx.deposits.each do |d|
|
87
|
+
$stdout.puts " id: #{d.id}, addr: #{d.address.address}, amount: #{Satoshi.new(d.amount, from_unit: :satoshi).to_btc}"
|
88
|
+
end
|
89
|
+
})
|
90
|
+
end
|
91
|
+
|
92
|
+
def handle_hashblock(block_hex)
|
93
|
+
block_hash = TxCatcher.rpc_node.getblock(block_hex)
|
94
|
+
transactions = block_hash["tx"]
|
95
|
+
height = TxCatcher.current_block_height = block_hash["height"].to_i
|
96
|
+
$stdout.puts "Block #{height} mined, transactions received:\n #{transactions.join(" \n")}"
|
97
|
+
@queue["hashblock"] << ( Proc.new {
|
98
|
+
Transaction.where(txid: transactions).update(block_height: height)
|
99
|
+
})
|
100
|
+
end
|
101
|
+
|
102
|
+
end # class Catcher
|
103
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module TxCatcher
|
4
|
+
|
5
|
+
# Cleans DB so that its size doesn't go above Config.max_db_transactions_stored
|
6
|
+
class Cleaner
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def stopped?
|
11
|
+
@@stopped
|
12
|
+
end
|
13
|
+
|
14
|
+
def start(run_once: false)
|
15
|
+
@@stopped = false
|
16
|
+
@@stop = false
|
17
|
+
Thread.new do
|
18
|
+
loop do
|
19
|
+
@@cleaning_counter = { transactions: 0, deposits: 0, addresses: 0 }
|
20
|
+
db_tx_count = TxCatcher::Transaction.last.id - TxCatcher::Transaction.first.id + 1
|
21
|
+
$stdout.print "Cleaning transactions in DB..."
|
22
|
+
$stdout.print "#{db_tx_count} now, needs to be below #{Config.max_db_transactions_stored}\n"
|
23
|
+
if db_tx_count > Config.max_db_transactions_stored
|
24
|
+
number_to_delete = (db_tx_count - Config.max_db_transactions_stored) + (Config.max_db_transactions_stored*0.1).round
|
25
|
+
clean_transactions(number_to_delete)
|
26
|
+
$stdout.puts "DB cleaned: #{@@cleaning_counter.to_s}"
|
27
|
+
else
|
28
|
+
$stdout.puts "Nothing to be cleaned from DB, size is below threshold."
|
29
|
+
end
|
30
|
+
if @stop || run_once
|
31
|
+
@@stopped = true
|
32
|
+
break
|
33
|
+
end
|
34
|
+
sleep Config.db_clean_period_seconds
|
35
|
+
end # loop
|
36
|
+
end # Thread
|
37
|
+
@@stopped
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
@@stop = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def clean_transactions(n)
|
45
|
+
transactions = Transaction.order("created_at ASC").limit(n).each do |t|
|
46
|
+
clean_deposits(t)
|
47
|
+
t.delete
|
48
|
+
@@cleaning_counter[:transactions] += 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def clean_deposits(transaction)
|
53
|
+
transaction.deposits.each do |d|
|
54
|
+
if d.address && d.address.deposits.count == 1
|
55
|
+
d.address.delete
|
56
|
+
@@cleaning_counter[:addresses] += 1
|
57
|
+
end
|
58
|
+
d.delete
|
59
|
+
@@cleaning_counter[:deposits] += 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end # class << self
|
64
|
+
end # Cleaner
|
65
|
+
end # TxCatcher
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module TxCatcher
|
4
|
+
|
5
|
+
class << (Config = OpenStruct.new)
|
6
|
+
def [](key_chain)
|
7
|
+
key_chain = key_chain.to_s.split('.')
|
8
|
+
config = self.public_send(key_chain.shift)
|
9
|
+
key_chain.each do |key|
|
10
|
+
if config.kind_of?(Hash)
|
11
|
+
config = config[key] || config[key.to_sym]
|
12
|
+
else
|
13
|
+
return
|
14
|
+
end
|
15
|
+
end
|
16
|
+
config
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|