module TxCatcher VERSION = File.read(File.expand_path('../../', File.dirname(__FILE__)) + '/VERSION') class << self attr_accessor :db_connection, :rpc_node, :current_block_height, :config_dir end module Initializer GEM_ROOT = File.expand_path('../..', File.dirname(__FILE__)) MIGRATIONS_ROOT = GEM_ROOT + '/db/migrations/' module ConfigFile class << self # Determine config dir or set default. Useful when we want to # have different settings for production or staging or development environments. def set!(path=nil) @@config_file = path and return if path @@config_file = ENV['HOME'] + '/.txcatcher/config.yml' ARGV.each_with_index do |a,i| if a =~ /\A--config-file=.+/ @@config_file = File.expand_path(a.sub('--config-file=', '')) ARGV.delete_at(i) break elsif a =~ /\A-c\Z/ @@config_file = File.expand_path(ARGV[1]) ARGV.delete_at(i) and ARGV.delete_at(i+1) break end end TxCatcher::Config.config_dir = File.dirname(@@config_file) puts "Using config file: #{@@config_file}" end def path @@config_file end end end def prepare ConfigFile.set! create_config_files read_config_file connect_to_db connect_to_rpc_node run_migrations if migrations_pending? set_server_port end def add_route(path, &block) @routes[path] = block end def create_config_files FileUtils.mkdir_p(ConfigFile.path) unless File.exist?(ConfigFile.path) unless File.exist?(ConfigFile.path) puts "\e[1;33mWARNING!\e[0m \e[33mNo file #{ConfigFile.path} was found. Created a sample one for you.\e[0m" puts "You should edit it and try starting the server again.\n" FileUtils.cp(GEM_ROOT + '/templates/config.yml', ConfigFile.path) config_contents = File.read(ConfigFile.path) config_contents.sub!("$home", File.expand_path('~')) File.open(ConfigFile.path, "w") { |f| f.write(config_contents) } puts "Shutting down now.\n\n" exit end end def read_config_file YAML.load_file(ConfigFile.path).each do |k,v| TxCatcher::Config.send(k + '=', v) end end def set_server_port # Setting default port to 9498 unless ARGV.include?("-p") ARGV.push "-p" if Config.server_port ARGV.push Config.server_port.to_s else ARGV.push "9498" end end end def connect_to_db # symbolize keys for convenience db_config = TxCatcher::Config.db.keys_to_sym db_name = if db_config[:adapter] == 'sqlite' if db_config[:db_path].start_with?("/") db_config[:db_path] else "#{File.dirname(ConfigFile.path)}/#{db_config[:db_path]}" end else db_config[:db_name] end TxCatcher.db_connection = Sequel.connect( "#{db_config[:adapter]}://" + "#{db_config[:user]}#{(":" if db_config[:user])}" + "#{db_config[:password]}#{("@" if db_config[:user] || db_config[:password])}" + "#{db_config[:host]}#{(":" if db_config[:port])}" + "#{db_config[:port]}#{("/" if db_config[:host] || db_config[:port])}" + "#{db_name}" ) end # Connects to all bitcoin daemons listed in config def connect_to_rpc_node n = TxCatcher::Config.rpcnode print "Checking #{n["name"]} RPC connection... " TxCatcher.rpc_node = BitcoinRPC.new("http://#{n["user"]}:#{n["password"]}@#{n["host"]}:#{n["port"]}") i = 0 # try to connect to RPC 100 times before exiting with error until TxCatcher.current_block_height begin TxCatcher.current_block_height = TxCatcher.rpc_node.getblockcount rescue Errno::ECONNREFUSED, BitcoinRPC::JSONRPCError => e if i > 100 print "ERROR, cannot connect using connection data #{n["name"]}\n" exit else print "Bitcoin RPC connection error: #{e.to_s}; will try again in 1 sec...\n" i += 1 sleep 1 end end end print "current block height: #{TxCatcher.current_block_height}\n" end def run_migrations Sequel.extension :migration print "\nPending migrations for the database detected. Migrating..." Sequel::Migrator.run(TxCatcher.db_connection, MIGRATIONS_ROOT) print "done\n\n" end def migrations_pending? !Sequel::Migrator.is_current?(TxCatcher.db_connection, MIGRATIONS_ROOT) end end end