txcatcher 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,148 @@
1
+ module TxCatcher
2
+
3
+ VERSION = File.read(File.expand_path('../../', File.dirname(__FILE__)) + '/VERSION')
4
+
5
+ class << self
6
+ attr_accessor :db_connection, :rpc_node, :current_block_height, :config_dir
7
+ end
8
+
9
+ module Initializer
10
+
11
+ GEM_ROOT = File.expand_path('../..', File.dirname(__FILE__))
12
+ MIGRATIONS_ROOT = GEM_ROOT + '/db/migrations/'
13
+
14
+ module ConfigFile
15
+
16
+ class << self
17
+
18
+ # Determine config dir or set default. Useful when we want to
19
+ # have different settings for production or staging or development environments.
20
+ def set!(path=nil)
21
+ @@config_file = path and return if path
22
+ @@config_file = ENV['HOME'] + '/.txcatcher/config.yml'
23
+ ARGV.each_with_index do |a,i|
24
+ if a =~ /\A--config-file=.+/
25
+ @@config_file = File.expand_path(a.sub('--config-file=', ''))
26
+ ARGV.delete_at(i)
27
+ break
28
+ elsif a =~ /\A-c\Z/
29
+ @@config_file = File.expand_path(ARGV[1])
30
+ ARGV.delete_at(i) and ARGV.delete_at(i+1)
31
+ break
32
+ end
33
+ end
34
+ TxCatcher::Config.config_dir = File.dirname(@@config_file)
35
+ puts "Using config file: #{@@config_file}"
36
+ end
37
+
38
+ def path
39
+ @@config_file
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ def prepare
47
+ ConfigFile.set!
48
+ create_config_files
49
+ read_config_file
50
+ connect_to_db
51
+ connect_to_rpc_node
52
+ run_migrations if migrations_pending?
53
+ set_server_port
54
+ end
55
+
56
+ def add_route(path, &block)
57
+ @routes[path] = block
58
+ end
59
+
60
+ def create_config_files
61
+ FileUtils.mkdir_p(ConfigFile.path) unless File.exist?(ConfigFile.path)
62
+
63
+ unless File.exist?(ConfigFile.path)
64
+ puts "\e[1;33mWARNING!\e[0m \e[33mNo file #{ConfigFile.path} was found. Created a sample one for you.\e[0m"
65
+ puts "You should edit it and try starting the server again.\n"
66
+
67
+ FileUtils.cp(GEM_ROOT + '/templates/config.yml', ConfigFile.path)
68
+ config_contents = File.read(ConfigFile.path)
69
+ config_contents.sub!("$home", File.expand_path('~'))
70
+ File.open(ConfigFile.path, "w") { |f| f.write(config_contents) }
71
+ puts "Shutting down now.\n\n"
72
+ exit
73
+ end
74
+
75
+ end
76
+
77
+ def read_config_file
78
+ YAML.load_file(ConfigFile.path).each do |k,v|
79
+ TxCatcher::Config.send(k + '=', v)
80
+ end
81
+ end
82
+
83
+ def set_server_port
84
+ # Setting default port to 9498
85
+ unless ARGV.include?("-p")
86
+ ARGV.push "-p"
87
+ if Config.server_port
88
+ ARGV.push Config.server_port.to_s
89
+ else
90
+ ARGV.push "9498"
91
+ end
92
+ end
93
+ end
94
+
95
+ def connect_to_db
96
+
97
+ # symbolize keys for convenience
98
+ db_config = TxCatcher::Config.db.keys_to_sym
99
+
100
+ db_name = if db_config[:adapter] == 'sqlite'
101
+ if db_config[:db_path].start_with?("/")
102
+ db_config[:db_path]
103
+ else
104
+ "#{File.dirname(ConfigFile.path)}/#{db_config[:db_path]}"
105
+ end
106
+ else
107
+ db_config[:db_name]
108
+ end
109
+
110
+ TxCatcher.db_connection = Sequel.connect(
111
+ "#{db_config[:adapter]}://" +
112
+ "#{db_config[:user]}#{(":" if db_config[:user])}" +
113
+ "#{db_config[:password]}#{("@" if db_config[:user] || db_config[:password])}" +
114
+ "#{db_config[:host]}#{(":" if db_config[:port])}" +
115
+ "#{db_config[:port]}#{("/" if db_config[:host] || db_config[:port])}" +
116
+ "#{db_name}"
117
+ )
118
+ end
119
+
120
+ # Connects to all bitcoin daemons listed in config
121
+ def connect_to_rpc_node
122
+ n = TxCatcher::Config.rpcnode
123
+ print "Checking #{n["name"]} RPC connection... "
124
+ begin
125
+ TxCatcher.rpc_node = BitcoinRPC.new("http://#{n["user"]}:#{n["password"]}@#{n["host"]}:#{n["port"]}")
126
+ TxCatcher.current_block_height = TxCatcher.rpc_node.getblockcount
127
+ print "balance: #{TxCatcher.rpc_node.getbalance}\n"
128
+ print "current block height: #{TxCatcher.current_block_height}\n"
129
+ rescue Errno::ECONNREFUSED
130
+ print "ERROR, cannot connect using connection data #{n["name"]}\n"
131
+ exit
132
+ end
133
+ end
134
+
135
+ def run_migrations
136
+ Sequel.extension :migration
137
+ print "\nPending migrations for the database detected. Migrating..."
138
+ Sequel::Migrator.run(TxCatcher.db_connection, MIGRATIONS_ROOT)
139
+ print "done\n\n"
140
+ end
141
+
142
+ def migrations_pending?
143
+ !Sequel::Migrator.is_current?(TxCatcher.db_connection, MIGRATIONS_ROOT)
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -0,0 +1,13 @@
1
+ module TxCatcher
2
+
3
+ class Address < Sequel::Model
4
+ one_to_many :deposits
5
+
6
+ def received_in_btc
7
+ Satoshi.new(self.received, from_unit: :satoshi).to_btc
8
+ end
9
+
10
+ end
11
+
12
+
13
+ end
@@ -0,0 +1,23 @@
1
+ module TxCatcher
2
+
3
+ class Deposit < Sequel::Model
4
+ many_to_one :transaction
5
+ many_to_one :address
6
+
7
+ attr_accessor :address_string
8
+
9
+ def before_save
10
+ if @address_string
11
+ self.address = Address.find_or_create(address: @address_string)
12
+ end
13
+
14
+ self.address.update(received: self.address.received + self.amount)
15
+ end
16
+
17
+ def amount_in_btc
18
+ Satoshi.new(self.amount, from_unit: :satoshi).to_btc
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,55 @@
1
+ module TxCatcher
2
+
3
+ class Transaction < Sequel::Model
4
+ one_to_many :deposits
5
+
6
+ def before_validation
7
+ return if !self.new? || !self.deposits.empty?
8
+ parse_transaction
9
+ assign_transaction_attrs
10
+ @tx_hash["vout"].uniq { |out| out["n"] }.each do |out|
11
+ amount = Satoshi.new(out["value"], from_unit: :btc).to_i if out["value"]
12
+ address = out["scriptPubKey"]["addresses"]&.first
13
+ # Do not create a new deposit unless it actually makes sense to create one
14
+ if address && amount && amount > 0
15
+ self.deposits << Deposit.new(amount: amount, address_string: address)
16
+ end
17
+ end
18
+ end
19
+
20
+ def after_save
21
+ self.deposits.each do |d|
22
+ d.transaction = self
23
+ d.save
24
+ end
25
+ end
26
+
27
+ def tx_hash
28
+ @tx_hash || parse_transaction
29
+ end
30
+
31
+ def confirmations
32
+ if self.block_height
33
+ TxCatcher.current_block_height - self.block_height + 1
34
+ else
35
+ 0
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def parse_transaction
42
+ @tx_hash = TxCatcher.rpc_node.decoderawtransaction(self.hex)
43
+ end
44
+
45
+ def assign_transaction_attrs
46
+ self.txid = @tx_hash["txid"]
47
+ end
48
+
49
+ def validate
50
+ errors.add(:base, "No outputs for this transactions") if self.deposits.empty?
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,74 @@
1
+ module TxCatcher
2
+
3
+ class Server < Goliath::API
4
+
5
+ use Goliath::Rack::Params
6
+
7
+ def response(env)
8
+ uri = env["REQUEST_URI"]
9
+ return route_for(uri)
10
+ end
11
+
12
+ def route_for(path)
13
+ puts "[REQUEST: #{path}]"
14
+ if path =~ /\A\/addr\/[0-9a-zA-Z]+\/utxo/
15
+ utxo(path)
16
+ elsif path.start_with? "/addr/"
17
+ address(path)
18
+ elsif path.start_with? "/tx/send"
19
+ broadcast_tx(params["rawtx"])
20
+ else
21
+ [404, {}, { error: "404, not found" }.to_json]
22
+ end
23
+ end
24
+
25
+ def address(path)
26
+ path = path.split("/").delete_if { |i| i.empty? }
27
+ addr = path.last
28
+
29
+ model = Address.where(address: addr).eager(deposits: :transactions).first
30
+ if model
31
+ transactions_ids = model.deposits.map { |d| d.transaction.txid }
32
+ deposits = model.deposits.map do |d|
33
+ t = d.transaction
34
+ {
35
+ txid: t.txid,
36
+ amount: d.amount_in_btc,
37
+ satoshis: d.amount,
38
+ confirmations: 0
39
+ }
40
+ end
41
+ [200, {}, { address: model.address, received: model.received, deposits: deposits }.to_json]
42
+ else
43
+ [200, {}, { address: addr, received: 0, deposits: [] }.to_json]
44
+ end
45
+ end
46
+
47
+ def utxo(path)
48
+ path = path.split("/").delete_if { |i| i.empty? }
49
+ path.pop
50
+ addr = path.last
51
+
52
+ model = Address.where(address: addr).eager(deposits: :transactions).first
53
+ transactions = model.deposits.map { |d| d.transaction }
54
+ utxos = transactions.map do |t|
55
+ outs = t.tx_hash["vout"].select { |out| out["scriptPubKey"]["addresses"] == [addr] }
56
+ outs.map! do |out|
57
+ out["confirmations"] = t.confirmations || 0
58
+ out["txid"] = t.txid
59
+ out
60
+ end
61
+ outs
62
+ end.flatten
63
+ [200, {}, utxos.to_json]
64
+ end
65
+
66
+ def broadcast_tx(txhex)
67
+ TxCatcher.rpc_node.sendrawtransaction(txhex)
68
+ tx = TxCatcher.rpc_node.decoderawtransaction(txhex)
69
+ [200, {}, tx.to_json]
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,24 @@
1
+ class Hash
2
+
3
+ # Replace String keys in the current hash with symbol keys
4
+ def keys_to_sym!
5
+ new_hash = keys_to_sym
6
+ self.clear
7
+ new_hash.each do |k,v|
8
+ self[k] = v
9
+ end
10
+ end
11
+
12
+ def keys_to_sym
13
+ symbolized_hash = {}
14
+ self.each do |k,v|
15
+ if k =~ /\A[a-zA-Z0-9!?_]+\Z/
16
+ symbolized_hash[k.to_sym] = v
17
+ else
18
+ symbolized_hash[k] = v
19
+ end
20
+ end
21
+ symbolized_hash
22
+ end
23
+
24
+ end
data/lib/txcatcher.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'json'
4
+ require 'thread'
5
+ require 'satoshi-unit'
6
+ require 'sequel'
7
+ Sequel.extension :migration
8
+
9
+ require_relative 'txcatcher/utils/hash_string_to_sym_keys'
10
+ require_relative 'txcatcher/bitcoin_rpc'
11
+ require_relative 'txcatcher/config'
12
+ require_relative 'txcatcher/initializer'
13
+ require_relative 'txcatcher/catcher'
14
+ require_relative 'txcatcher/cleaner'
15
+
16
+ include TxCatcher::Initializer
17
+ prepare
18
+
19
+ require_relative 'txcatcher/models/transaction'
20
+ require_relative 'txcatcher/models/address'
21
+ require_relative 'txcatcher/models/deposit'
@@ -0,0 +1,96 @@
1
+ require 'rubygems'
2
+ require 'ffi-rzmq'
3
+
4
+ require_relative 'spec_helper'
5
+ require_relative '../lib/txcatcher/models/address'
6
+ require_relative '../lib/txcatcher/models/transaction'
7
+ require_relative '../lib/txcatcher/catcher'
8
+
9
+ RSpec.describe TxCatcher::Catcher do
10
+
11
+ before(:all) do
12
+ @tx_sock = ZMQ::Context.create(1).socket(ZMQ::PUB)
13
+ @block_sock = ZMQ::Context.create(2).socket(ZMQ::PUB)
14
+ @tx_sock.bind("ipc:///tmp/bitcoind_test.rawtx")
15
+ @block_sock.bind("ipc:///tmp/bitcoind_test.hashblock")
16
+ @hextx = File.read(File.dirname(__FILE__) + "/fixtures/transaction.txt").strip
17
+ @rawtx = unhexlify(File.read(File.dirname(__FILE__) + "/fixtures/transaction.txt"))
18
+ @catcher = TxCatcher::Catcher.new(name: "bitcoind_test")
19
+ sleep 1
20
+ end
21
+
22
+ after(:all) do
23
+ @tx_sock.unbind('ipc:///tmp/bitcoind_test.rawtx')
24
+ @block_sock.unbind('ipc:///tmp/bitcoind_test.hashblock')
25
+ end
26
+
27
+ it "creates a new transaction in the DB" do
28
+ @tx_sock.send_string('rawtx', ZMQ::SNDMORE)
29
+ @tx_sock.send_string(@rawtx)
30
+
31
+ i = 0
32
+ until (@catcher.sockets.values&.first && @catcher.sockets.values.first[:last_message]) || i > 10
33
+ sleep 1 and i += 1
34
+ end
35
+
36
+ i = 0
37
+ until tx = TxCatcher::Transaction.last
38
+ sleep 1 and i += 1
39
+ end
40
+ expect(tx.hex).to eq(@hextx)
41
+
42
+ end
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")
49
+
50
+ i = 0
51
+ until transaction.reload.confirmations == 1
52
+ sleep 1 and i += 1
53
+ end
54
+ expect(transaction.block_height).to eq(TxCatcher.current_block_height)
55
+
56
+ end
57
+
58
+ describe "error logging" do
59
+
60
+ before(:each) do
61
+ @error_log = File.dirname(__FILE__) + "/config/error.log"
62
+ TxCatcher::Config.config_dir = File.dirname(@error_log)
63
+ end
64
+
65
+ after(:each) do
66
+ File.delete(@error_log) if File.exists?(@error_log)
67
+ end
68
+
69
+ it "ignores validation errors" do
70
+ tx = eval File.read(File.dirname(__FILE__) + "/fixtures/transaction_decoded_no_outputs.txt")
71
+ allow(TxCatcher.rpc_node).to receive(:decoderawtransaction).and_return(tx)
72
+ @tx_sock.send_string('rawtx', ZMQ::SNDMORE)
73
+ @tx_sock.send_string(@rawtx)
74
+
75
+ i = 0
76
+ until (@catcher.sockets.values&.first && @catcher.sockets.values.first[:last_message]) || i > 10
77
+ sleep 1 and i += 1
78
+ end
79
+ expect(File.exists?(@error_log)).to be_falsey
80
+ end
81
+
82
+ it "logs all other errors" do
83
+ allow(TxCatcher.rpc_node).to receive(:decoderawtransaction).and_raise(StandardError)
84
+ @tx_sock.send_string('rawtx', ZMQ::SNDMORE)
85
+ @tx_sock.send_string(@rawtx)
86
+
87
+ i = 0
88
+ until File.exists?(@error_log)
89
+ sleep 1 and i += 1
90
+ end
91
+ expect(File.read(@error_log)).not_to be_empty
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'ffi-rzmq'
3
+
4
+ require_relative 'spec_helper'
5
+ require_relative '../lib/txcatcher/models/address'
6
+ require_relative '../lib/txcatcher/models/transaction'
7
+ require_relative '../lib/txcatcher/cleaner'
8
+
9
+ RSpec.describe TxCatcher::Cleaner do
10
+
11
+ before(:each) do
12
+ allow(TxCatcher.rpc_node).to receive(:decoderawtransaction).and_return({ "vout" => []})
13
+ end
14
+
15
+ it "doesn't clean anything if transaction count is below threshold" do
16
+ create_transactions(9)
17
+ clean_transactions
18
+ expect(TxCatcher::Transaction.count).to eq(9)
19
+ expect(TxCatcher::Deposit.count).to eq(9)
20
+ expect(TxCatcher::Address.count).to eq(9)
21
+ end
22
+
23
+ it "cleans excessive transactions from the DB" do
24
+ create_transactions(15)
25
+ clean_transactions
26
+ expect(TxCatcher::Transaction.count).to eq(9)
27
+ expect(TxCatcher::Deposit.count).to eq(9)
28
+ expect(TxCatcher::Address.count).to eq(9)
29
+ end
30
+
31
+ it "doesn't delete the address upon cleanup if it has another deposit associated with it" do
32
+ create_transactions(15)
33
+
34
+ d = TxCatcher::Deposit.create(address_string: "addr1", amount: 0)
35
+ tx = TxCatcher::Transaction.last
36
+ tx.deposits << d
37
+ tx.save
38
+
39
+ clean_transactions
40
+ expect(TxCatcher::Transaction.count).to eq(9)
41
+ expect(TxCatcher::Deposit.count).to eq(10)
42
+ expect(TxCatcher::Address.count).to eq(10)
43
+ end
44
+
45
+
46
+ def create_transactions(n)
47
+ (1..n).to_a.each do |i|
48
+ d = TxCatcher::Deposit.new(address_string: "addr#{i}", amount: 0)
49
+ tx = TxCatcher::Transaction.new
50
+ tx.deposits << d
51
+ tx.save
52
+ end
53
+ end
54
+
55
+ def clean_transactions
56
+ TxCatcher::Cleaner.start(run_once: true)
57
+ sleep 1 until TxCatcher::Cleaner.stopped?
58
+ end
59
+
60
+ end
@@ -0,0 +1,22 @@
1
+ db:
2
+ adapter: sqlite
3
+ db_path: txcatcher_test.db
4
+
5
+ # No need to set these options for sqlite,
6
+ # but other DBs may require them.
7
+ #
8
+ #user: username
9
+ #password: password
10
+ #host: hostname
11
+ #port: 1234
12
+
13
+ rpcnode:
14
+ name: bitcoind
15
+ user: roman
16
+ password: 12990duxcibciwebf
17
+ host: 127.0.0.1
18
+ port: 8332
19
+
20
+ # zeromq: bitcoind
21
+ max_db_transactions_stored: 10
22
+ db_clean_period_seconds: 300
Binary file
@@ -0,0 +1 @@
1
+ 0100000001b0130468da00458400a40db0509a2d41fb6b3cf8cc54bd494a5ae4439979e9af01000000fdfe000048304502210085ba337244b670a3241af0a47cc05cdbf633dfda94c26fe8c9cfa77ab4e14bb002203d046ccc53d715d0632420c964eca9d8d6bca922fc95dd09344016e53793e59e01483045022100b111fe6e28d07aff12494976e4fd168f0265f21cc3c5abf5e83f4b872124e1930220669cd375691ef92f905efaaece66f672902294d283529c2b9ffcd488c861e766014c6952210204568228ddc9bd466bd346a9789eeec116ccdd1fda7823c264740b1261b5c4c221025df4d301519e9fb509361c025c5286d23ecf89be4060418ce2cec1e4a58c48d421031dc9a14a2daf2f20581e885c466fa6f6ace0e00a155fce9df476aadc390b2d3553aeffffffff02e046d11b0000000017a91403d7a39c85363805fb19e972c1e5c5956a75b016871e8e704f0000000017a9143921d410a0360f72d07b607173923b1e8ebab7578700000000
@@ -0,0 +1 @@
1
+ {"txid"=>"faf8368fec418a971e8fa94943e951490759498e0b2d3c4d5b9b12212c4f2e5a", "hash"=>"faf8368fec418a971e8fa94943e951490759498e0b2d3c4d5b9b12212c4f2e5a", "size"=>371, "vsize"=>371, "version"=>1, "locktime"=>0, "vin"=>[{"txid"=>"afe9799943e45a4a49bd54ccf83c6bfb412d9a50b00da400844500da680413b0", "vout"=>1, "scriptSig"=>{"asm"=>"0 304502210085ba337244b670a3241af0a47cc05cdbf633dfda94c26fe8c9cfa77ab4e14bb002203d046ccc53d715d0632420c964eca9d8d6bca922fc95dd09344016e53793e59e[ALL] 3045022100b111fe6e28d07aff12494976e4fd168f0265f21cc3c5abf5e83f4b872124e1930220669cd375691ef92f905efaaece66f672902294d283529c2b9ffcd488c861e766[ALL] 52210204568228ddc9bd466bd346a9789eeec116ccdd1fda7823c264740b1261b5c4c221025df4d301519e9fb509361c025c5286d23ecf89be4060418ce2cec1e4a58c48d421031dc9a14a2daf2f20581e885c466fa6f6ace0e00a155fce9df476aadc390b2d3553ae", "hex"=>"0048304502210085ba337244b670a3241af0a47cc05cdbf633dfda94c26fe8c9cfa77ab4e14bb002203d046ccc53d715d0632420c964eca9d8d6bca922fc95dd09344016e53793e59e01483045022100b111fe6e28d07aff12494976e4fd168f0265f21cc3c5abf5e83f4b872124e1930220669cd375691ef92f905efaaece66f672902294d283529c2b9ffcd488c861e766014c6952210204568228ddc9bd466bd346a9789eeec116ccdd1fda7823c264740b1261b5c4c221025df4d301519e9fb509361c025c5286d23ecf89be4060418ce2cec1e4a58c48d421031dc9a14a2daf2f20581e885c466fa6f6ace0e00a155fce9df476aadc390b2d3553ae"}, "sequence"=>4294967295}], "vout"=>[]}
@@ -0,0 +1,5 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../../lib/txcatcher/models/address'
3
+
4
+ RSpec.describe TxCatcher::Address do
5
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe TxCatcher::Transaction do
4
+
5
+ before(:each) do
6
+ @hextx = File.read(File.dirname(__FILE__) + "/../fixtures/transaction.txt").strip
7
+ @transaction = TxCatcher::Transaction.create(hex: @hextx)
8
+ end
9
+
10
+ it "parses rawtx using bitcoind" do
11
+ expect(@transaction.txid).to eq("faf8368fec418a971e8fa94943e951490759498e0b2d3c4d5b9b12212c4f2e5a")
12
+ end
13
+
14
+ it "creates addresses and deposits" do
15
+ expect(TxCatcher::Address.where(address: "323LGzCm43NgbtoYJhT6oKSwmKFTQ7AHzH").first.received).to eq(466700000)
16
+ expect(TxCatcher::Address.where(address: "36u6xv2TvZqPPYdogzfLZpMAXrYdW4Vwjp").first.received).to eq(1332776478)
17
+ end
18
+
19
+ it "doesn't create duplicate deposits if valid? called manually before save" do
20
+ transaction = TxCatcher::Transaction.new(hex: @hextx)
21
+ transaction.valid?
22
+ transaction.save
23
+ expect(transaction.deposits.size).to eq(2)
24
+ end
25
+
26
+ end
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'json'
4
+ require 'satoshi-unit'
5
+ require 'sequel'
6
+ Sequel.extension :migration
7
+
8
+ db_file = File.dirname(__FILE__) + "/config/txcatcher_test.db"
9
+ File.delete(db_file) if File.exists?(db_file)
10
+
11
+ require_relative '../lib/txcatcher/utils/hash_string_to_sym_keys'
12
+ require_relative '../lib/txcatcher/config'
13
+ require_relative '../lib/txcatcher/initializer'
14
+ require_relative '../lib/txcatcher/bitcoin_rpc'
15
+
16
+ include TxCatcher::Initializer
17
+ ConfigFile.set!(File.dirname(__FILE__) + "/config/config.yml")
18
+
19
+ read_config_file
20
+ connect_to_db
21
+ run_migrations if migrations_pending?
22
+ connect_to_rpc_node
23
+
24
+ require_relative '../lib/txcatcher/models/transaction'
25
+ require_relative '../lib/txcatcher/models/address'
26
+ require_relative '../lib/txcatcher/models/deposit'
27
+
28
+ def unhexlify(msg)
29
+ msg.scan(/../).collect { |c| c.to_i(16).chr }.join
30
+ end
31
+
32
+ RSpec.configure do |config|
33
+
34
+ config.before(:all) do
35
+ end
36
+
37
+ config.after(:all) do
38
+ end
39
+
40
+ config.before(:each) do
41
+ TxCatcher::Transaction.select_all.delete
42
+ TxCatcher::Deposit.select_all.delete
43
+ TxCatcher::Address.select_all.delete
44
+ end
45
+
46
+ end
@@ -0,0 +1,23 @@
1
+ db:
2
+ adapter: sqlite
3
+ db_path: txcatcher_test.db
4
+
5
+ # No need to set these options for sqlite,
6
+ # but other DBs may require them.
7
+ #
8
+ #user: username
9
+ #password: password
10
+ #host: hostname
11
+ #port: 1234
12
+
13
+ rpcnode:
14
+ name: bitcoind
15
+ user: roman
16
+ password: 12990duxcibciwebf
17
+ host: 127.0.0.1
18
+ port: 8332
19
+
20
+ zeromq: bitcoind
21
+ max_db_transactions_stored: 100000
22
+ db_clean_period_seconds: 300
23
+ server_port: 9498