txcatcher 0.1.0

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.
@@ -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