From 18a787b6f25371136e20faa287f4bc547f0d1b67 Mon Sep 17 00:00:00 2001 From: "crashtestzombie0@gmail.com" Date: Sat, 5 Dec 2015 20:54:11 -0500 Subject: [PATCH] ran rubocop linter --- Gemfile | 4 +- Rakefile | 16 +- bin/straight-server-benchmark | 37 +- db/migrations/001_create_orders.rb | 14 +- db/migrations/002_create_gateways.rb | 16 +- db/migrations/003_add_payment_id_to_orders.rb | 4 +- .../004_add_description_to_orders.rb | 2 - ...dd_orders_expiration_period_to_gateways.rb | 2 - ...ck_order_status_in_db_first_to_gateways.rb | 2 - .../007_add_active_switcher_to_gateways.rb | 2 - .../008_add_order_counters_to_gateways.rb | 2 - .../009_add_hashed_id_to_gateways.rb | 8 +- .../010_add_address_reusability_orders.rb | 12 +- .../011_add_callback_data_to_orders.rb | 2 - db/migrations/012_add_address_provider.rb | 4 +- .../013_add_address_derivation_scheme.rb | 2 - ...4_pubkey_null_address_provider_not_null.rb | 2 +- .../015_add_amount_paid_to_orders.rb | 2 - db/migrations/016_add_new_params_to_orders.rb | 2 - .../017_add_test_mode_to_gateways.rb | 2 - .../018_add_test_keychain_id_to_gateways.rb | 2 - .../019_add_test_pubkey_to_gateways.rb | 2 - db/migrations/020_add_test_mode_to_orders.rb | 2 - db/schema.rb | 94 ++--- lib/straight-server.rb | 2 - lib/straight-server/config.rb | 5 +- lib/straight-server/gateway.rb | 362 +++++++++--------- lib/straight-server/initializer.rb | 41 +- lib/straight-server/logger.rb | 6 +- lib/straight-server/order.rb | 50 ++- lib/straight-server/orders_controller.rb | 131 +++---- lib/straight-server/random_string.rb | 1 - lib/straight-server/server.rb | 11 +- lib/straight-server/signature_validator.rb | 8 +- lib/straight-server/thread.rb | 3 +- lib/straight-server/throttler.rb | 1 - .../utils/hash_string_to_sym_keys.rb | 10 +- lib/tasks/db.rake | 8 +- spec/factories.rb | 4 +- spec/fixtures/test_addon.rb | 2 +- spec/lib/gateway_spec.rb | 223 +++++------ spec/lib/initializer_spec.rb | 39 +- spec/lib/order_spec.rb | 63 ++- spec/lib/orders_controller_spec.rb | 174 ++++----- spec/lib/signature_validator_spec.rb | 35 +- spec/lib/thread_spec.rb | 5 +- spec/lib/throttle_spec.rb | 5 +- spec/lib/utils/hash_string_to_sym_keys.rb | 10 +- spec/spec_helper.rb | 19 +- spec/support/custom_matchers.rb | 22 +- straight-server.gemspec | 265 +++++++------ 51 files changed, 806 insertions(+), 936 deletions(-) diff --git a/Gemfile b/Gemfile index 6ca5dad..bc38832 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ source 'https://rubygems.org' do - gem 'straight', '1.0.0' #, path: '../straight-engine' + gem 'straight', '1.0.0' # , path: '../straight-engine' gem 'satoshi-unit' gem 'goliath' gem 'faye-websocket' @@ -13,7 +13,7 @@ end unless ENV['STRAIGHT_SERVER_IGNORE_ADDONS_GEMFILE'] # use this flag when building straight-server.gemspec addons_gemfile = File.join(ENV['STRAIGHT_SERVER_CONFIG_DIR'] || File.join(ENV['HOME'], '.straight'), 'AddonsGemfile') - eval_gemfile addons_gemfile if File.exists?(addons_gemfile) + eval_gemfile addons_gemfile if File.exist?(addons_gemfile) end group :development do diff --git a/Rakefile b/Rakefile index 66c9ef1..a19e1ee 100644 --- a/Rakefile +++ b/Rakefile @@ -8,7 +8,7 @@ begin Bundler.setup(:default, :development, :test) rescue Bundler::BundlerError => e $stderr.puts e.message - $stderr.puts "Run `bundle install` to install missing gems" + $stderr.puts 'Run `bundle install` to install missing gems' exit e.status_code end @@ -17,13 +17,13 @@ Dir.glob('lib/tasks/*.rake').each { |r| load r } require 'jeweler' Jeweler::Tasks.new do |gem| # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options - gem.name = "straight-server" - gem.homepage = "http://github.com/snitko/straight-server" - gem.license = "MIT" - gem.summary = %Q{A Bitcoin payment gateway server: a state server for the stateless Straight library} - gem.description = %Q{Accepts orders via http, returns payment info via http or streams updates via websockets, stores orders in a DB} - gem.email = "roman.snitko@gmail.com" - gem.authors = ["Roman Snitko"] + gem.name = 'straight-server' + gem.homepage = 'http://github.com/snitko/straight-server' + gem.license = 'MIT' + gem.summary = %(A Bitcoin payment gateway server: a state server for the stateless Straight library) + gem.description = %(Accepts orders via http, returns payment info via http or streams updates via websockets, stores orders in a DB) + gem.email = 'roman.snitko@gmail.com' + gem.authors = ['Roman Snitko'] # dependencies defined in Gemfile end Jeweler::RubygemsDotOrgTasks.new diff --git a/bin/straight-server-benchmark b/bin/straight-server-benchmark index 89f3394..1a933e1 100755 --- a/bin/straight-server-benchmark +++ b/bin/straight-server-benchmark @@ -8,31 +8,31 @@ require 'fileutils' BENCHMARK_DB_FILE = File.expand_path('benchmark/straight.db') def delete_benchmark_db - FileUtils.rm_f(BENCHMARK_DB_FILE) if File.exists?(BENCHMARK_DB_FILE) + FileUtils.rm_f(BENCHMARK_DB_FILE) if File.exist?(BENCHMARK_DB_FILE) end def delete_log_files - logs_to_delete = Dir[File.expand_path('benchmark') + "/*.log*"] + - Dir[File.expand_path('bin') + "/*.log*"] + logs_to_delete = Dir[File.expand_path('benchmark') + '/*.log*'] + + Dir[File.expand_path('bin') + '/*.log*'] logs_to_delete.each { |f| FileUtils.rm_f(f) } unless logs_to_delete.empty? end if ARGV.empty? - print "Enter quantity of requests: " + print 'Enter quantity of requests: ' request_qty = gets.strip.to_i - - print "Enter concurrency of the requests: " + + print 'Enter concurrency of the requests: ' request_concurrency = gets.strip.to_i - - print "Enter PORT: " + + print 'Enter PORT: ' port = gets.strip.to_s port = port.empty? ? nil : port - - print "Enter HOST: " + + print 'Enter HOST: ' host = gets.strip.to_s host = host.empty? ? nil : host else - request_qty = ARGV[0] + request_qty = ARGV[0] request_concurrency = ARGV[1] host_and_port = ARGV[2] end @@ -45,24 +45,23 @@ delete_log_files system "#{File.expand_path('bin/straight-server')} --config-dir=./benchmark -p " + (port ? "#{port}" : '9697') + " --daemonize --pid #{File.expand_path('benchmark/straight.pid')}" # Create order: POST /gateways/1/orders -puts "\n\n########## Create order: POST /gateways/1/orders #####################################" + - "###############################################################################" +puts "\n\n########## Create order: POST /gateways/1/orders #####################################" \ + '###############################################################################' puts system "ab -n #{request_qty} -c #{request_concurrency} -T application/x-www-form-urlencoded " + host_and_port + '/gateways/1/orders?amount=1' # See order info: GET /gateways/1/orders/1 -puts "\n\n########## See order info: GET /gateways/1/orders/1 #####################################" + - "##############################################################################" +puts "\n\n########## See order info: GET /gateways/1/orders/1 #####################################" \ + '##############################################################################' puts system "ab -n #{request_qty} -c #{request_concurrency} " + host_and_port + '/gateways/1/orders/1' # Subscribe to order status changes via a websocket: GET /gateways/1/orders/1/websocket -puts "\n\n########## Subscribe to order status changes via a websocket: GET /gateways/1/orders/1/websocket ##################" + - "##############################################################################" +puts "\n\n########## Subscribe to order status changes via a websocket: GET /gateways/1/orders/1/websocket ##################" \ + '##############################################################################' puts system "ab -n #{request_qty} -c #{request_concurrency} " + host_and_port + '/gateways/1/orders/1/websocket' - # killing straight server process pid = File.read(File.expand_path('benchmark/straight.pid')) system "kill #{pid}" delete_benchmark_db -delete_log_files \ No newline at end of file +delete_log_files diff --git a/db/migrations/001_create_orders.rb b/db/migrations/001_create_orders.rb index 655f67a..90f22cd 100644 --- a/db/migrations/001_create_orders.rb +++ b/db/migrations/001_create_orders.rb @@ -1,16 +1,15 @@ Sequel.migration do - up do create_table(:orders) do primary_key :id - String :address, null: false - String :tid + String :address, null: false + String :tid Integer :status, null: false, default: 0 Integer :keychain_id, null: false - Bignum :amount, null: false - Integer :gateway_id, null: false - String :data - String :callback_response, text: true + Bignum :amount, null: false + Integer :gateway_id, null: false + String :data + String :callback_response, text: true DateTime :created_at, null: false DateTime :updated_at end @@ -22,5 +21,4 @@ down do drop_table(:orders) end - end diff --git a/db/migrations/002_create_gateways.rb b/db/migrations/002_create_gateways.rb index d685183..99529f3 100644 --- a/db/migrations/002_create_gateways.rb +++ b/db/migrations/002_create_gateways.rb @@ -1,18 +1,17 @@ Sequel.migration do - up do create_table(:gateways) do primary_key :id Integer :confirmations_required, null: false, default: 0 Integer :last_keychain_id, null: false, default: 0 - String :pubkey, null: false - String :order_class, null: false - String :secret, null: false - String :name, null: false - String :default_currency, default: 'BTC' - String :callback_url + String :pubkey, null: false + String :order_class, null: false + String :secret, null: false + String :name, null: false + String :default_currency, default: 'BTC' + String :callback_url Boolean :check_signature, null: false, default: false - String :exchange_rate_adapter_names + String :exchange_rate_adapter_names DateTime :created_at, null: false DateTime :updated_at end @@ -24,5 +23,4 @@ down do drop_table(:gateways) end - end diff --git a/db/migrations/003_add_payment_id_to_orders.rb b/db/migrations/003_add_payment_id_to_orders.rb index d4b3a6c..7d8b04d 100644 --- a/db/migrations/003_add_payment_id_to_orders.rb +++ b/db/migrations/003_add_payment_id_to_orders.rb @@ -1,13 +1,11 @@ Sequel.migration do - up do add_column :orders, :payment_id, String add_index :orders, :payment_id, unique: true end down do - drop_index :orders, :payment_id + drop_index :orders, :payment_id drop_column :orders, :payment_id end - end diff --git a/db/migrations/004_add_description_to_orders.rb b/db/migrations/004_add_description_to_orders.rb index 9017e89..894d041 100644 --- a/db/migrations/004_add_description_to_orders.rb +++ b/db/migrations/004_add_description_to_orders.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :orders, :description, String end @@ -7,5 +6,4 @@ down do drop_column :orders, :description end - end diff --git a/db/migrations/005_add_orders_expiration_period_to_gateways.rb b/db/migrations/005_add_orders_expiration_period_to_gateways.rb index 9bfc19d..28ecd27 100644 --- a/db/migrations/005_add_orders_expiration_period_to_gateways.rb +++ b/db/migrations/005_add_orders_expiration_period_to_gateways.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :orders_expiration_period, Integer end @@ -7,5 +6,4 @@ down do drop_column :gateways, :orders_expiration_period end - end diff --git a/db/migrations/006_add_check_order_status_in_db_first_to_gateways.rb b/db/migrations/006_add_check_order_status_in_db_first_to_gateways.rb index c2d98f0..3e5329d 100644 --- a/db/migrations/006_add_check_order_status_in_db_first_to_gateways.rb +++ b/db/migrations/006_add_check_order_status_in_db_first_to_gateways.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :check_order_status_in_db_first, TrueClass end @@ -7,5 +6,4 @@ down do drop_column :gateways, :check_order_status_in_db_first end - end diff --git a/db/migrations/007_add_active_switcher_to_gateways.rb b/db/migrations/007_add_active_switcher_to_gateways.rb index 155f81c..5c83462 100644 --- a/db/migrations/007_add_active_switcher_to_gateways.rb +++ b/db/migrations/007_add_active_switcher_to_gateways.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :active, TrueClass, default: true end @@ -7,5 +6,4 @@ down do drop_column :gateways, :active end - end diff --git a/db/migrations/008_add_order_counters_to_gateways.rb b/db/migrations/008_add_order_counters_to_gateways.rb index 24749e3..3676ec4 100644 --- a/db/migrations/008_add_order_counters_to_gateways.rb +++ b/db/migrations/008_add_order_counters_to_gateways.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :order_counters, String end @@ -7,5 +6,4 @@ down do drop_column :gateways, :order_counters end - end diff --git a/db/migrations/009_add_hashed_id_to_gateways.rb b/db/migrations/009_add_hashed_id_to_gateways.rb index 1dce32d..82588ee 100644 --- a/db/migrations/009_add_hashed_id_to_gateways.rb +++ b/db/migrations/009_add_hashed_id_to_gateways.rb @@ -1,18 +1,16 @@ Sequel.migration do - up do add_column :gateways, :hashed_id, String - add_index :gateways, :hashed_id + add_index :gateways, :hashed_id if defined?(StraightServer) StraightServer.db_connection[:gateways].each do |g| - g.update(hashed_id: OpenSSL::HMAC.digest('sha256', StraightServer::Config.server_secret, g[:id].to_s).unpack("H*").first) + g.update(hashed_id: OpenSSL::HMAC.digest('sha256', StraightServer::Config.server_secret, g[:id].to_s).unpack('H*').first) end end end down do - drop_index :gateways, :hashed_id + drop_index :gateways, :hashed_id drop_column :gateways, :hashed_id end - end diff --git a/db/migrations/010_add_address_reusability_orders.rb b/db/migrations/010_add_address_reusability_orders.rb index 9c02a7b..e212e21 100644 --- a/db/migrations/010_add_address_reusability_orders.rb +++ b/db/migrations/010_add_address_reusability_orders.rb @@ -1,19 +1,17 @@ Sequel.migration do - up do - drop_index :orders, [:keychain_id, :gateway_id] - drop_index :orders, :address + drop_index :orders, [:keychain_id, :gateway_id] + drop_index :orders, :address add_index :orders, [:keychain_id, :gateway_id] add_index :orders, :address add_column :orders, :reused, Integer, default: 0 end down do - drop_index :orders, [:keychain_id, :gateway_id] - drop_index :orders, :address - drop_column :orders, :reused + drop_index :orders, [:keychain_id, :gateway_id] + drop_index :orders, :address + drop_column :orders, :reused add_index :orders, [:keychain_id, :gateway_id], unique: true add_index :orders, :address, unique: true end - end diff --git a/db/migrations/011_add_callback_data_to_orders.rb b/db/migrations/011_add_callback_data_to_orders.rb index 0b5c4aa..2a27626 100644 --- a/db/migrations/011_add_callback_data_to_orders.rb +++ b/db/migrations/011_add_callback_data_to_orders.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :orders, :callback_data, String end @@ -7,5 +6,4 @@ down do drop_column :orders, :callback_data end - end diff --git a/db/migrations/012_add_address_provider.rb b/db/migrations/012_add_address_provider.rb index 2d41383..69c6e77 100644 --- a/db/migrations/012_add_address_provider.rb +++ b/db/migrations/012_add_address_provider.rb @@ -1,11 +1,9 @@ Sequel.migration do - up do - add_column :gateways, :address_provider, String, default: "Bip32" + add_column :gateways, :address_provider, String, default: 'Bip32' end down do drop_column :gateways, :address_provider end - end diff --git a/db/migrations/013_add_address_derivation_scheme.rb b/db/migrations/013_add_address_derivation_scheme.rb index b646d74..5e3e4e6 100644 --- a/db/migrations/013_add_address_derivation_scheme.rb +++ b/db/migrations/013_add_address_derivation_scheme.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :address_derivation_scheme, String end @@ -7,5 +6,4 @@ down do drop_column :gateways, :address_derivation_scheme end - end diff --git a/db/migrations/014_pubkey_null_address_provider_not_null.rb b/db/migrations/014_pubkey_null_address_provider_not_null.rb index b7d85a3..6d5a53c 100644 --- a/db/migrations/014_pubkey_null_address_provider_not_null.rb +++ b/db/migrations/014_pubkey_null_address_provider_not_null.rb @@ -2,7 +2,7 @@ change do alter_table :gateways do set_column_allow_null :pubkey - set_column_not_null :address_provider + set_column_not_null :address_provider end end end diff --git a/db/migrations/015_add_amount_paid_to_orders.rb b/db/migrations/015_add_amount_paid_to_orders.rb index ae50234..d4e6890 100644 --- a/db/migrations/015_add_amount_paid_to_orders.rb +++ b/db/migrations/015_add_amount_paid_to_orders.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :orders, :amount_paid, Bignum end @@ -7,5 +6,4 @@ down do drop_column :orders, :amount_paid end - end diff --git a/db/migrations/016_add_new_params_to_orders.rb b/db/migrations/016_add_new_params_to_orders.rb index 7a6f9fb..611b78f 100644 --- a/db/migrations/016_add_new_params_to_orders.rb +++ b/db/migrations/016_add_new_params_to_orders.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :orders, :callback_url, String add_column :orders, :title, String @@ -9,5 +8,4 @@ drop_column :orders, :callback_url drop_column :orders, :title end - end diff --git a/db/migrations/017_add_test_mode_to_gateways.rb b/db/migrations/017_add_test_mode_to_gateways.rb index c4b1a95..85a47b9 100644 --- a/db/migrations/017_add_test_mode_to_gateways.rb +++ b/db/migrations/017_add_test_mode_to_gateways.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :test_mode, TrueClass, default: false end @@ -7,5 +6,4 @@ down do drop_column :gateways, :test_mode end - end diff --git a/db/migrations/018_add_test_keychain_id_to_gateways.rb b/db/migrations/018_add_test_keychain_id_to_gateways.rb index 08df9fd..dd57842 100644 --- a/db/migrations/018_add_test_keychain_id_to_gateways.rb +++ b/db/migrations/018_add_test_keychain_id_to_gateways.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :test_last_keychain_id, Integer, default: 0, null: false end @@ -7,5 +6,4 @@ down do drop_column :gateways, :test_last_keychain_id end - end diff --git a/db/migrations/019_add_test_pubkey_to_gateways.rb b/db/migrations/019_add_test_pubkey_to_gateways.rb index 93e5fc6..7da4b60 100644 --- a/db/migrations/019_add_test_pubkey_to_gateways.rb +++ b/db/migrations/019_add_test_pubkey_to_gateways.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :gateways, :test_pubkey, String end @@ -7,5 +6,4 @@ down do drop_column :gateways, :test_pubkey end - end diff --git a/db/migrations/020_add_test_mode_to_orders.rb b/db/migrations/020_add_test_mode_to_orders.rb index bb48c65..964eac0 100644 --- a/db/migrations/020_add_test_mode_to_orders.rb +++ b/db/migrations/020_add_test_mode_to_orders.rb @@ -1,5 +1,4 @@ Sequel.migration do - up do add_column :orders, :test_mode, TrueClass, default: false end @@ -7,5 +6,4 @@ down do drop_column :orders, :test_mode end - end diff --git a/db/schema.rb b/db/schema.rb index 9886324..afd8879 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,65 +1,65 @@ Sequel.migration do change do - create_table(:gateways, :ignore_index_errors=>true) do + create_table(:gateways, ignore_index_errors: true) do primary_key :id - Integer :confirmations_required, :default=>0, :null=>false - Integer :last_keychain_id, :default=>0, :null=>false - String :pubkey, :size=>255 - String :order_class, :size=>255, :null=>false - String :secret, :size=>255, :null=>false - String :name, :size=>255, :null=>false - String :default_currency, :default=>"BTC", :size=>255 - String :callback_url, :size=>255 - TrueClass :check_signature, :default=>false, :null=>false - String :exchange_rate_adapter_names, :size=>255 - DateTime :created_at, :null=>false + Integer :confirmations_required, default: 0, null: false + Integer :last_keychain_id, default: 0, null: false + String :pubkey, size: 255 + String :order_class, size: 255, null: false + String :secret, size: 255, null: false + String :name, size: 255, null: false + String :default_currency, default: 'BTC', size: 255 + String :callback_url, size: 255 + TrueClass :check_signature, default: false, null: false + String :exchange_rate_adapter_names, size: 255 + DateTime :created_at, null: false DateTime :updated_at Integer :orders_expiration_period TrueClass :check_order_status_in_db_first - TrueClass :active, :default=>true - String :order_counters, :size=>255 - String :hashed_id, :size=>255 - String :address_provider, :default=>"Bip32", :size=>255, :null=>false - String :address_derivation_scheme, :size=>255 - TrueClass :test_mode, :default=>false - Integer :test_last_keychain_id, :default=>0, :null=>false - String :test_pubkey, :size=>255 - + TrueClass :active, default: true + String :order_counters, size: 255 + String :hashed_id, size: 255 + String :address_provider, default: 'Bip32', size: 255, null: false + String :address_derivation_scheme, size: 255 + TrueClass :test_mode, default: false + Integer :test_last_keychain_id, default: 0, null: false + String :test_pubkey, size: 255 + index [:hashed_id] - index [:id], :unique=>true - index [:name], :unique=>true - index [:pubkey], :unique=>true + index [:id], unique: true + index [:name], unique: true + index [:pubkey], unique: true end - - create_table(:orders, :ignore_index_errors=>true) do + + create_table(:orders, ignore_index_errors: true) do primary_key :id - String :address, :size=>255, :null=>false - String :tid, :size=>255 - Integer :status, :default=>0, :null=>false - Integer :keychain_id, :null=>false - Bignum :amount, :null=>false - Integer :gateway_id, :null=>false - String :data, :size=>255 - String :callback_response, :text=>true - DateTime :created_at, :null=>false + String :address, size: 255, null: false + String :tid, size: 255 + Integer :status, default: 0, null: false + Integer :keychain_id, null: false + Bignum :amount, null: false + Integer :gateway_id, null: false + String :data, size: 255 + String :callback_response, text: true + DateTime :created_at, null: false DateTime :updated_at - String :payment_id, :size=>255 - String :description, :size=>255 - Integer :reused, :default=>0 - String :callback_data, :size=>255 + String :payment_id, size: 255 + String :description, size: 255 + Integer :reused, default: 0 + String :callback_data, size: 255 String :amount_paid - String :callback_url, :size=>255 - String :title, :size=>255 - TrueClass :test_mode, :default=>false - + String :callback_url, size: 255 + String :title, size: 255 + TrueClass :test_mode, default: false + index [:address] - index [:id], :unique=>true + index [:id], unique: true index [:keychain_id, :gateway_id] - index [:payment_id], :unique=>true + index [:payment_id], unique: true end - + create_table(:schema_info) do - Integer :version, :default=>0, :null=>false + Integer :version, default: 0, null: false end end end diff --git a/lib/straight-server.rb b/lib/straight-server.rb index 1324744..1162fea 100644 --- a/lib/straight-server.rb +++ b/lib/straight-server.rb @@ -11,7 +11,6 @@ Sequel.extension :migration module StraightServer - VERSION = File.read(File.expand_path('../', File.dirname(__FILE__)) + '/VERSION') StraightServerError = Class.new(StandardError) @@ -19,7 +18,6 @@ module StraightServer class << self attr_accessor :db_connection, :redis_connection, :logger end - end require_relative 'straight-server/utils/hash_string_to_sym_keys' diff --git a/lib/straight-server/config.rb b/lib/straight-server/config.rb index f6b32a6..e9574b1 100644 --- a/lib/straight-server/config.rb +++ b/lib/straight-server/config.rb @@ -1,7 +1,6 @@ require 'ostruct' module StraightServer - # :db # :gateways_source # :gateways @@ -20,9 +19,9 @@ module StraightServer class << (Config = OpenStruct.new) def [](key_chain) key_chain = key_chain.to_s.split('.') - config = self.public_send(key_chain.shift) + config = public_send(key_chain.shift) key_chain.each do |key| - if config.kind_of?(Hash) + if config.is_a?(Hash) config = config[key] || config[key.to_sym] else return diff --git a/lib/straight-server/gateway.rb b/lib/straight-server/gateway.rb index 1b663ee..8e9d5e6 100644 --- a/lib/straight-server/gateway.rb +++ b/lib/straight-server/gateway.rb @@ -1,56 +1,53 @@ require 'cgi' module StraightServer - # This module contains common features of Gateway, later to be included # in one of the classes below. module GatewayModule - @@websockets = {} - class CallbackUrlBadResponse < StraightServerError; end - class WebsocketExists < StraightServerError; end + class CallbackUrlBadResponse < StraightServerError; end + class WebsocketExists < StraightServerError; end class WebsocketForCompletedOrder < StraightServerError; end - class GatewayInactive < StraightServerError; end - class NoBlockchainAdapters < StraightServerError + class GatewayInactive < StraightServerError; end + class NoBlockchainAdapters < StraightServerError def message - "No blockchain adapters were found! StraightServer cannot query the blockchain.\n" + - "Check your ~/.straight/config.yml file and make sure valid blockchain adapters\n" + - "are present." + "No blockchain adapters were found! StraightServer cannot query the blockchain.\n" \ + "Check your ~/.straight/config.yml file and make sure valid blockchain adapters\n" \ + 'are present.' end end - class NoWebsocketsForNewGateway < StraightServerError + class NoWebsocketsForNewGateway < StraightServerError def message "You're trying to get access to websockets on a Gateway that hasn't been saved yet" end end - class OrderCountersDisabled < StraightServerError + class OrderCountersDisabled < StraightServerError def message - "Please enable order counting in config file! You can do is using the following option:\n\n" + - " count_orders: true\n\n" + - "and don't forget to provide Redis connection info by adding this to the config file as well:\n\n" + - " redis:\n" + - " host: localhost\n" + - " port: 6379\n" + + "Please enable order counting in config file! You can do is using the following option:\n\n" \ + " count_orders: true\n\n" \ + "and don't forget to provide Redis connection info by adding this to the config file as well:\n\n" \ + " redis:\n" \ + " host: localhost\n" \ + " port: 6379\n" \ " db: null\n" end end class NoPubkey < StraightServerError def message - "No public key were found! Gateway can't work without it.\n" + - "Please provide it in config file or DB." + "No public key were found! Gateway can't work without it.\n" \ + 'Please provide it in config file or DB.' end end class NoTestPubkey < StraightServerError def message - "No test public key were found! Gateway can't work in test mode without it.\n" + - "Please provide it in config file or DB." + "No test public key were found! Gateway can't work in test mode without it.\n" \ + 'Please provide it in config file or DB.' end end CALLBACK_URL_ATTEMPT_TIMEFRAME = 3600 # seconds - ############# Initializers methods ######################################################## # We have separate methods, because with GatewayOnDB they are called from #after_initialize # but in GatewayOnConfig they are called from #initialize intself. @@ -58,8 +55,8 @@ def message # def initialize_exchange_rate_adapters @exchange_rate_adapters ||= [] - if self.exchange_rate_adapter_names.kind_of?(Array) && self.exchange_rate_adapter_names - self.exchange_rate_adapter_names.each do |adapter| + if exchange_rate_adapter_names.is_a?(Array) && exchange_rate_adapter_names + exchange_rate_adapter_names.each do |adapter| begin @exchange_rate_adapters << Straight::ExchangeRate.const_get("#{adapter}Adapter").instance rescue NameError => e @@ -72,18 +69,25 @@ def initialize_exchange_rate_adapters def initialize_blockchain_adapters @blockchain_adapters = [] StraightServer::Config.blockchain_adapters.each do |a| - adapter = Straight::Blockchain.const_get("#{a}Adapter") next unless adapter begin - main_url = StraightServer::Config.__send__("#{a.downcase}_url") rescue next - test_url = StraightServer::Config.__send__("#{a.downcase}_test_url") rescue nil + main_url = begin + StraightServer::Config.__send__("#{a.downcase}_url") + rescue + next + end + test_url = begin + StraightServer::Config.__send__("#{a.downcase}_test_url") + rescue + nil + end @blockchain_adapters << adapter.mainnet_adapter(main_url: main_url, test_url: test_url) rescue ArgumentError @blockchain_adapters << adapter.mainnet_adapter end end - raise NoBlockchainAdapters if @blockchain_adapters.empty? + fail NoBlockchainAdapters if @blockchain_adapters.empty? end def initialize_callbacks @@ -120,9 +124,8 @@ def fetch_transactions_for(address) # Creates a new order and saves into the DB. Checks if the MD5 hash # is correct first. - def create_order(attrs={}) - - raise GatewayInactive unless self.active + def create_order(attrs = {}) + fail GatewayInactive unless active StraightServer.logger.info "Creating new order with attrs: #{attrs}" @@ -133,40 +136,40 @@ def create_order(attrs={}) attrs[:keychain_id] = reused_order.keychain_id end - - attrs[:keychain_id] = nil if attrs[:keychain_id] == '' - order = new_order( - amount: (attrs[:amount] && attrs[:amount].to_f), - keychain_id: attrs[:keychain_id] || get_next_last_keychain_id, - currency: attrs[:currency], - btc_denomination: attrs[:btc_denomination] - ) - order.id = attrs[:id].to_i if attrs[:id] - order.data = attrs[:data] if attrs[:data] - order.callback_data = attrs[:callback_data] if attrs[:callback_data] - order.title = attrs[:title] if attrs[:title] - order.callback_url = attrs[:callback_url] if attrs[:callback_url] - order.gateway = self - order.test_mode = test_mode - order.description = attrs[:description] - order.reused = reused_order.reused + 1 if reused_order - order.save + attrs[:keychain_id] = nil if attrs[:keychain_id] == '' - self.update_last_keychain_id(attrs[:keychain_id]) unless order.reused > 0 - self.save + order = new_order( + amount: (attrs[:amount] && attrs[:amount].to_f), + keychain_id: attrs[:keychain_id] || get_next_last_keychain_id, + currency: attrs[:currency], + btc_denomination: attrs[:btc_denomination] + ) + order.id = attrs[:id].to_i if attrs[:id] + order.data = attrs[:data] if attrs[:data] + order.callback_data = attrs[:callback_data] if attrs[:callback_data] + order.title = attrs[:title] if attrs[:title] + order.callback_url = attrs[:callback_url] if attrs[:callback_url] + order.gateway = self + order.test_mode = test_mode + order.description = attrs[:description] + order.reused = reused_order.reused + 1 if reused_order + order.save + + update_last_keychain_id(attrs[:keychain_id]) unless order.reused > 0 + save StraightServer.logger.info "Order #{order.id} created: #{order.to_h}" order end def get_next_last_keychain_id - return self.test_last_keychain_id + 1 if self.test_mode - self.last_keychain_id + 1 + return test_last_keychain_id + 1 if test_mode + last_keychain_id + 1 end # TODO: make it preaty - def update_last_keychain_id(new_value=nil) - if self.test_mode + def update_last_keychain_id(new_value = nil) + if test_mode new_value ? self.test_last_keychain_id = new_value : self.test_last_keychain_id += 1 else new_value ? self.last_keychain_id = new_value : self.last_keychain_id += 1 @@ -174,10 +177,10 @@ def update_last_keychain_id(new_value=nil) end def add_websocket_for_order(ws, order) - raise WebsocketExists unless websockets[order.id].nil? - raise WebsocketForCompletedOrder unless order.status < 2 + fail WebsocketExists unless websockets[order.id].nil? + fail WebsocketForCompletedOrder unless order.status < 2 StraightServer.logger.info "Opening ws connection for #{order.id}" - ws.on(:close) do |event| + ws.on(:close) do |_event| websockets.delete(order.id) StraightServer.logger.info "Closing ws connection for #{order.id}" end @@ -186,8 +189,8 @@ def add_websocket_for_order(ws, order) end def websockets - raise NoWebsocketsForNewGateway unless self.id - @@websockets[self.id] + fail NoWebsocketsForNewGateway unless id + @@websockets[id] end def send_order_to_websocket_client(order) @@ -200,7 +203,7 @@ def send_order_to_websocket_client(order) def sign_with_secret(content, level: 1) result = content.to_s level.times do - result = OpenSSL::HMAC.digest('sha256', secret, result).unpack("H*").first + result = OpenSSL::HMAC.digest('sha256', secret, result).unpack('H*').first end result end @@ -223,17 +226,17 @@ def order_counters(reload: false) underpaid: get_order_counter(:underpaid), overpaid: get_order_counter(:overpaid), expired: get_order_counter(:expired), - canceled: get_order_counter(:canceled), + canceled: get_order_counter(:canceled) } end def get_order_counter(counter_name) - raise OrderCountersDisabled unless StraightServer::Config.count_orders + fail OrderCountersDisabled unless StraightServer::Config.count_orders StraightServer.redis_connection.get("#{StraightServer::Config.redis[:prefix]}:gateway_#{id}:#{counter_name}_orders_counter").to_i || 0 end - def increment_order_counter!(counter_name, by=1) - raise OrderCountersDisabled unless StraightServer::Config.count_orders + def increment_order_counter!(counter_name, by = 1) + fail OrderCountersDisabled unless StraightServer::Config.count_orders StraightServer.redis_connection.incrby("#{StraightServer::Config.redis[:prefix]}:gateway_#{id}:#{counter_name}_orders_counter", by) end @@ -250,7 +253,7 @@ def increment_order_counter!(counter_name, by=1) def find_reusable_order expired_orders = find_expired_orders_row if expired_orders.size >= Config.reuse_address_orders_threshold && - fetch_transactions_for(expired_orders.last.address).empty? + fetch_transactions_for(expired_orders.last.address).empty? return expired_orders.last end nil @@ -258,104 +261,99 @@ def find_reusable_order private - # Tries to send a callback HTTP request to the resource specified - # in the #callback_url. Use #callback_url given to Order if it exist, otherwise default. - # If it fails for any reason, it keeps trying for an hour (3600 seconds) - # making 10 http requests, each delayed by twice the time the previous one was delayed. - # This method is supposed to be running in a separate thread. - def send_callback_http_request(order, delay: 5) - url = order.callback_url || self.callback_url - return if url.to_s.empty? - - # Composing the request uri here - callback_data = order.callback_data ? "&callback_data=#{CGI.escape(order.callback_data)}" : '' - uri = URI.parse("#{url}#{url.include?('?') ? '&' : '?'}#{order.to_http_params}#{callback_data}") - - StraightServer.logger.info "Attempting callback for order #{order.id}: #{uri.to_s}" - - begin - request = Net::HTTP::Get.new(uri.request_uri) - request.add_field 'X-Signature', SignatureValidator.signature(method: 'GET', request_uri: uri.request_uri, secret: secret, nonce: nil, body: nil) - response = Net::HTTP.new(uri.host, uri.port).start do |http| - http.request request - end - order.callback_response = { code: response.code, body: response.body } - order.save - raise CallbackUrlBadResponse unless response.code.to_i == 200 - rescue => ex - if delay < CALLBACK_URL_ATTEMPT_TIMEFRAME - sleep(delay) - send_callback_http_request(order, delay: delay*2) - else - StraightServer.logger.warn "Callback request for order #{order.id} failed with #{ex.inspect}, see order's #callback_response field for details" - end + # Tries to send a callback HTTP request to the resource specified + # in the #callback_url. Use #callback_url given to Order if it exist, otherwise default. + # If it fails for any reason, it keeps trying for an hour (3600 seconds) + # making 10 http requests, each delayed by twice the time the previous one was delayed. + # This method is supposed to be running in a separate thread. + def send_callback_http_request(order, delay: 5) + url = order.callback_url || callback_url + return if url.to_s.empty? + + # Composing the request uri here + callback_data = order.callback_data ? "&callback_data=#{CGI.escape(order.callback_data)}" : '' + uri = URI.parse("#{url}#{url.include?('?') ? '&' : '?'}#{order.to_http_params}#{callback_data}") + + StraightServer.logger.info "Attempting callback for order #{order.id}: #{uri}" + + begin + request = Net::HTTP::Get.new(uri.request_uri) + request.add_field 'X-Signature', SignatureValidator.signature(method: 'GET', request_uri: uri.request_uri, secret: secret, nonce: nil, body: nil) + response = Net::HTTP.new(uri.host, uri.port).start do |http| + http.request request + end + order.callback_response = { code: response.code, body: response.body } + order.save + fail CallbackUrlBadResponse unless response.code.to_i == 200 + rescue => ex + if delay < CALLBACK_URL_ATTEMPT_TIMEFRAME + sleep(delay) + send_callback_http_request(order, delay: delay * 2) + else + StraightServer.logger.warn "Callback request for order #{order.id} failed with #{ex.inspect}, see order's #callback_response field for details" end - - StraightServer.logger.info "Callback request for order #{order.id} performed successfully" end + StraightServer.logger.info "Callback request for order #{order.id} performed successfully" + end - # Wallets that support BIP32 do a limited address lookup. If you have 20 empty addresses in a row - # (actually not 20, but Config.reuse_address_orders_threshold, 20 is the default value) it won't - # look past it and if an order is generated with the 21st address and Bitcoins are paid there, - # the wallet may not detect it. Thus we need to always check for the number of expired orders - # in a row and reuse an address. - # - # This method takes care of the first part of that equation: finds the row of expired orders. - # It works like this: - # - # 1. Finds 20 last orders - # 2. Checks if they form a row of expired orders, that is if there is no non-expired non-new orders - # in the array: - # - # if YES (all orders in the row are indeed expired) - # a) Try the next 20 until we find that one non-expired, non-new order - # b) Put all orders in an array, then slice it so only the oldest 20 are there - # c) return 20 oldest expired orders - # - # if NO (some orders are paid) - # Return the row of expired orders - which is not enough to trigger a reuse - # (the triger is in the #find_reusable_order method, which calls this one). - def find_expired_orders_row - - orders = [] - row = nil - offset = 0 - - while row.nil? || row.size > 0 - row = Order.where(gateway_id: self.id).order(Sequel.desc(:keychain_id), Sequel.desc(:reused)).limit(Config.reuse_address_orders_threshold).offset(offset).to_a - - row.reject! do |o| - reject = false - row.each do |o2| - reject = true if o.keychain_id == o2.keychain_id && o.reused < o2.reused - end - reject + # Wallets that support BIP32 do a limited address lookup. If you have 20 empty addresses in a row + # (actually not 20, but Config.reuse_address_orders_threshold, 20 is the default value) it won't + # look past it and if an order is generated with the 21st address and Bitcoins are paid there, + # the wallet may not detect it. Thus we need to always check for the number of expired orders + # in a row and reuse an address. + # + # This method takes care of the first part of that equation: finds the row of expired orders. + # It works like this: + # + # 1. Finds 20 last orders + # 2. Checks if they form a row of expired orders, that is if there is no non-expired non-new orders + # in the array: + # + # if YES (all orders in the row are indeed expired) + # a) Try the next 20 until we find that one non-expired, non-new order + # b) Put all orders in an array, then slice it so only the oldest 20 are there + # c) return 20 oldest expired orders + # + # if NO (some orders are paid) + # Return the row of expired orders - which is not enough to trigger a reuse + # (the triger is in the #find_reusable_order method, which calls this one). + def find_expired_orders_row + orders = [] + row = nil + offset = 0 + + while row.nil? || row.size > 0 + row = Order.where(gateway_id: id).order(Sequel.desc(:keychain_id), Sequel.desc(:reused)).limit(Config.reuse_address_orders_threshold).offset(offset).to_a + + row.reject! do |o| + reject = false + row.each do |o2| + reject = true if o.keychain_id == o2.keychain_id && o.reused < o2.reused end + reject + end - row.sort! { |o1, o2| o2.id <=> o1.id } + row.sort! { |o1, o2| o2.id <=> o1.id } - row.each do |o| - if o.status == Order::STATUSES[:expired] - orders.unshift(o) - elsif o.status == Order::STATUSES[:new] - next - else - return orders[0...Config.reuse_address_orders_threshold] - end + row.each do |o| + if o.status == Order::STATUSES[:expired] + orders.unshift(o) + elsif o.status == Order::STATUSES[:new] + next + else + return orders[0...Config.reuse_address_orders_threshold] end - offset += Config.reuse_address_orders_threshold end - - orders - + offset += Config.reuse_address_orders_threshold end + orders + end end # Uses database to load and save attributes class GatewayOnDB < Sequel::Model(:gateways) - include Straight::GatewayModule include GatewayModule plugin :timestamps, create: :created_at, update: :updated_at @@ -363,7 +361,7 @@ class GatewayOnDB < Sequel::Model(:gateways) plugin :after_initialize def self.find_by_hashed_id(s) - self.where(hashed_id: s).first + where(hashed_id: s).first end # This virtual attribute is important because it's difficult to detect whether secret was actually @@ -386,13 +384,13 @@ def before_update end def after_create - @@websockets[self.id] = {} - update(hashed_id: OpenSSL::HMAC.digest('sha256', Config.server_secret, self.id.to_s).unpack("H*").first) + @@websockets[id] = {} + update(hashed_id: OpenSSL::HMAC.digest('sha256', Config.server_secret, id.to_s).unpack('H*').first) end def after_initialize @status_check_schedule = Straight::GatewayModule::DEFAULT_STATUS_CHECK_SCHEDULE - @@websockets[self.id] ||= {} if self.id + @@websockets[id] ||= {} if id initialize_callbacks initialize_exchange_rate_adapters initialize_blockchain_adapters @@ -402,8 +400,8 @@ def after_initialize def validate super - errors.add(:pubkey, "Please provide public key") if pubkey_missing? - errors.add(:test_pubkey, "Please provide test public key if you activate test mode") if test_pubkey_missing? + errors.add(:pubkey, 'Please provide public key') if pubkey_missing? + errors.add(:test_pubkey, 'Please provide test public key if you activate test mode') if test_pubkey_missing? end # We cannot allow to store gateway secret in a DB plaintext, this would be completetly unsecure. @@ -422,14 +420,14 @@ def self.find_by_id(id) end def encrypt_secret - cipher = OpenSSL::Cipher::AES.new(128, :CBC) + cipher = OpenSSL::Cipher::AES.new(128, :CBC) cipher.encrypt - cipher.key = OpenSSL::HMAC.digest('sha256', 'nonce', Config.server_secret).unpack("H*").first[0,16] + cipher.key = OpenSSL::HMAC.digest('sha256', 'nonce', Config.server_secret).unpack('H*').first[0, 16] - cipher.iv = iv = OpenSSL::HMAC.digest('sha256', 'nonce', "#{self.class.max(:id)}#{Config.server_secret}").unpack("H*").first[0,16] - raise "cipher.iv cannot be nil" unless iv + cipher.iv = iv = OpenSSL::HMAC.digest('sha256', 'nonce', "#{self.class.max(:id)}#{Config.server_secret}").unpack('H*').first[0, 16] + fail 'cipher.iv cannot be nil' unless iv - encrypted = cipher.update(self[:secret]) << cipher.final() + encrypted = cipher.update(self[:secret]) << cipher.final base64_encrypted = Base64.strict_encode64(encrypted).encode('utf-8') result = "#{iv}:#{base64_encrypted}" @@ -438,7 +436,7 @@ def encrypt_secret if decrypt_secret(result) == self[:secret] self.secret = result else - raise "Decrypted and original secrets don't match! Cannot proceed with writing the encrypted gateway secret." + fail "Decrypted and original secrets don't match! Cannot proceed with writing the encrypted gateway secret." end end @@ -462,21 +460,19 @@ def enable_test_mode! private - def decrypt_secret(encrypted_field=self[:secret]) - decipher = OpenSSL::Cipher::AES.new(128, :CBC) - iv, encrypted = encrypted_field.split(':') - decipher.decrypt - decipher.key = OpenSSL::HMAC.digest('sha256', 'nonce', Config.server_secret).unpack("H*").first[0,16] - decipher.iv = iv - decipher.update(Base64.decode64(encrypted)) + decipher.final - end - + def decrypt_secret(encrypted_field = self[:secret]) + decipher = OpenSSL::Cipher::AES.new(128, :CBC) + iv, encrypted = encrypted_field.split(':') + decipher.decrypt + decipher.key = OpenSSL::HMAC.digest('sha256', 'nonce', Config.server_secret).unpack('H*').first[0, 16] + decipher.iv = iv + decipher.update(Base64.decode64(encrypted)) + decipher.final + end end # Uses a config file to load attributes and a special _last_keychain_id file # to store last_keychain_id class GatewayOnConfig - include Straight::GatewayModule include GatewayModule @@ -513,7 +509,7 @@ class GatewayOnConfig attr_accessor :active def self.find_by_hashed_id(s) - self.find_by_id(s) + find_by_id(s) end def initialize @@ -525,8 +521,8 @@ def initialize end def validate_config - raise NoPubkey if pubkey_missing? - raise NoTestPubkey if test_pubkey_missing? + fail NoPubkey if pubkey_missing? + fail NoTestPubkey if test_pubkey_missing? end # Because this is a config based gateway, we only save last_keychain_id @@ -540,7 +536,7 @@ def save # we save it to the file. def load_last_keychain_id! @last_keychain_id_file ||= build_keychain_path - if File.exists?(@last_keychain_id_file) + if File.exist?(@last_keychain_id_file) self.last_keychain_id = File.read(@last_keychain_id_file).to_i else self.last_keychain_id = 0 @@ -550,7 +546,7 @@ def load_last_keychain_id! def save_last_keychain_id! @last_keychain_id_file ||= build_keychain_path - File.open(@last_keychain_id_file, 'w') {|f| f.write(last_keychain_id) } + File.open(@last_keychain_id_file, 'w') { |f| f.write(last_keychain_id) } end def build_keychain_path @@ -569,7 +565,7 @@ def address_provider # This method is a replacement for the Sequel's model one used in DB version of the gateway # and it finds gateways using the index of @@gateways Array. def self.find_by_id(id) - @@gateways[id.to_i-1] + @@gateways[id.to_i - 1] end # This will later be used in the #find_by_id. Because we don't use a DB, @@ -581,7 +577,7 @@ def self.find_by_id(id) i = 0 StraightServer::Config.gateways.each do |name, attrs| i += 1 - gateway = self.new + gateway = new gateway.pubkey = attrs['pubkey'] gateway.test_pubkey = attrs['test_pubkey'] gateway.confirmations_required = attrs['confirmations_required'].to_i @@ -592,7 +588,7 @@ def self.find_by_id(id) gateway.default_currency = attrs['default_currency'] gateway.orders_expiration_period = attrs['orders_expiration_period'] gateway.active = attrs['active'] - gateway.address_provider = attrs['address_provider'] || "Bip32" + gateway.address_provider = attrs['address_provider'] || 'Bip32' gateway.address_derivation_scheme = attrs['address_derivation_scheme'] gateway.test_mode = attrs['test_mode'] || false gateway.name = name @@ -604,7 +600,6 @@ def self.find_by_id(id) @@websockets[i] = {} @@gateways << gateway end if StraightServer::Config.gateways - end # It may not be a perfect way to implement such a thing, but it gives enough flexibility to people @@ -612,9 +607,8 @@ def self.find_by_id(id) # in a config file instead of a DB. That way they don't need special tools to access the DB and create # a gateway, but can simply edit the config file. Gateway = if StraightServer::Config.gateways_source == 'config' - GatewayOnConfig - else - GatewayOnDB + GatewayOnConfig + else + GatewayOnDB end - end diff --git a/lib/straight-server/initializer.rb b/lib/straight-server/initializer.rb index 31cc1ea..fc808da 100644 --- a/lib/straight-server/initializer.rb +++ b/lib/straight-server/initializer.rb @@ -1,17 +1,13 @@ module StraightServer - module Initializer - GEM_ROOT = File.expand_path('../..', File.dirname(__FILE__)) MIGRATIONS_ROOT = GEM_ROOT + '/db/migrations/' module ConfigDir - 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) + def set!(path = nil) @@config_dir = path and return if path @@config_dir = ENV['HOME'] + '/.straight' ARGV.each do |a| @@ -29,9 +25,7 @@ def set!(path=nil) def path @@config_dir end - end - end def prepare @@ -40,7 +34,7 @@ def prepare read_config_file create_logger connect_to_db - run_migrations if migrations_pending? + run_migrations if migrations_pending? setup_redis_connection initialize_routes end @@ -54,14 +48,14 @@ def create_config_files unless File.exist?(ConfigDir.path + '/addons.yml') puts "\e[1;33mNOTICE!\e[0m \e[33mNo file #{ConfigDir.path}/addons.yml was found. Created an empty sample for you.\e[0m" - puts "No need to restart until you actually list your addons there. Now will continue loading StraightServer." + puts 'No need to restart until you actually list your addons there. Now will continue loading StraightServer.' FileUtils.cp(GEM_ROOT + '/templates/addons.yml', ConfigDir.path) end unless File.exist?(ConfigDir.path + '/server_secret') puts "\e[1;33mNOTICE!\e[0m \e[33mNo file #{ConfigDir.path}/server_secret was found. Created one for you.\e[0m" - puts "No need to restart so far. Now will continue loading StraightServer." - File.open(ConfigDir.path + '/server_secret', "w") do |f| + puts 'No need to restart so far. Now will continue loading StraightServer.' + File.open(ConfigDir.path + '/server_secret', 'w') do |f| f.puts String.random(16) end end @@ -74,33 +68,31 @@ def create_config_files puts "Shutting down now.\n\n" exit end - end def read_config_file - YAML.load_file(ConfigDir.path + '/config.yml').each do |k,v| + YAML.load_file(ConfigDir.path + '/config.yml').each do |k, v| StraightServer::Config.send(k + '=', v) end StraightServer::Config.server_secret = File.read(ConfigDir.path + '/server_secret').chomp end def connect_to_db - # symbolize keys for convenience db_config = StraightServer::Config.db.keys_to_sym db_name = if db_config[:adapter] == 'sqlite' - ConfigDir.path + "/" + db_config[:name] - else - db_config[:name] + ConfigDir.path + '/' + db_config[:name] + else + db_config[:name] end StraightServer.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_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 @@ -168,7 +160,6 @@ def load_addons # but some orders statuses are not resolved. def resume_tracking_active_orders! StraightServer::Order.where('status < 2').each do |order| - # Order is expired, but status is < 2! Suspcicious, probably # an unclean shutdown of the server. Let's check and update the status manually once. if order.time_left_before_expiration < 1 @@ -195,7 +186,7 @@ def resume_tracking_active_orders! # Loads redis gem and sets up key prefixes for order counters # for the current straight environment. def setup_redis_connection - raise "Redis not configured" unless Config.redis + fail 'Redis not configured' unless Config.redis Config.redis = Config.redis.keys_to_sym Config.redis[:prefix] ||= "StraightServer:#{Config.environment}" StraightServer.redis_connection = Redis.new( @@ -205,7 +196,5 @@ def setup_redis_connection password: Config.redis[:password] ) end - end - end diff --git a/lib/straight-server/logger.rb b/lib/straight-server/logger.rb index adfae16..581e3cc 100644 --- a/lib/straight-server/logger.rb +++ b/lib/straight-server/logger.rb @@ -1,9 +1,7 @@ module StraightServer class Logger < Logmaster - # inserts a number of blank lines - def blank_lines(n=1) - + def blank_lines(n = 1) n.times { puts "\n" } File.open(StraightServer::Initializer::ConfigDir.path + '/' + Config.logmaster['file'], 'a') do |f| @@ -11,8 +9,6 @@ def blank_lines(n=1) f.puts "\n" end end if Config.logmaster['file'] - end - end end diff --git a/lib/straight-server/order.rb b/lib/straight-server/order.rb index 1701c85..c1cef8c 100644 --- a/lib/straight-server/order.rb +++ b/lib/straight-server/order.rb @@ -1,7 +1,5 @@ module StraightServer - class Order < Sequel::Model - include Straight::OrderModule plugin :validation_helpers plugin :timestamps, create: :created_at, update: :updated_at @@ -49,12 +47,12 @@ def gateway=(g) # set StraightServer::Config.check_order_status_in_db_first to true def status(as_sym: false, reload: false) if reload && StraightServer::Config.check_order_status_in_db_first - @old_status = self.status - self.refresh + @old_status = status + refresh unless self[:status] == @old_status @status = self[:status] @status_changed = true - self.gateway.order_status_changed(self) + gateway.order_status_changed(self) end end self[:status] = @status @@ -76,12 +74,12 @@ def save end def to_h - super.merge({ id: id, - payment_id: payment_id, - amount_in_btc: amount_in_btc(as: :string), - amount_paid_in_btc: amount_in_btc(field: amount_paid,as: :string), - keychain_id: keychain_id, - last_keychain_id: self.gateway.last_keychain_id }) + super.merge(id: id, + payment_id: payment_id, + amount_in_btc: amount_in_btc(as: :string), + amount_paid_in_btc: amount_in_btc(field: amount_paid, as: :string), + keychain_id: keychain_id, + last_keychain_id: gateway.last_keychain_id) end def to_json @@ -90,12 +88,12 @@ def to_json def validate super # calling Sequel::Model validator - errors.add(:amount, "is not numeric") if !amount.kind_of?(Numeric) - errors.add(:amount, "should be more than 0") if amount && amount <= 0 - errors.add(:amount_paid, "is not numeric") if !amount.kind_of?(Numeric) - errors.add(:gateway_id, "is invalid") if !gateway_id.kind_of?(Numeric) || gateway_id <= 0 - errors.add(:description, "should be shorter than 256 characters") if description.kind_of?(String) && description.length > 255 - errors.add(:gateway, "is inactive, cannot create order for inactive gateway") if !gateway.active && self.new? + errors.add(:amount, 'is not numeric') unless amount.is_a?(Numeric) + errors.add(:amount, 'should be more than 0') if amount && amount <= 0 + errors.add(:amount_paid, 'is not numeric') unless amount.is_a?(Numeric) + errors.add(:gateway_id, 'is invalid') if !gateway_id.is_a?(Numeric) || gateway_id <= 0 + errors.add(:description, 'should be shorter than 256 characters') if description.is_a?(String) && description.length > 255 + errors.add(:gateway, 'is inactive, cannot create order for inactive gateway') if !gateway.active && self.new? validates_unique :id validates_presence [:address, :keychain_id, :gateway_id, :amount] end @@ -105,12 +103,12 @@ def to_http_params end def before_create - self.payment_id = gateway.sign_with_secret("#{keychain_id}#{amount}#{created_at}#{(Order.max(:id) || 0)+1}") + self.payment_id = gateway.sign_with_secret("#{keychain_id}#{amount}#{created_at}#{(Order.max(:id) || 0) + 1}") # Save info about current exchange rate at the time of purchase unless gateway.default_currency == 'BTC' - self.data = {} unless self.data - self.data[:exchange_rate] = { price: gateway.current_exchange_rate, currency: gateway.default_currency } + self.data = {} unless data + data[:exchange_rate] = { price: gateway.current_exchange_rate, currency: gateway.default_currency } end super @@ -121,7 +119,7 @@ def before_create # but the initial :new increment needs this code because the Gateway#order_status_changed # isn't called in this case. def after_create - self.gateway.increment_order_counter!(:new) if StraightServer::Config.count_orders + gateway.increment_order_counter!(:new) if StraightServer::Config.count_orders end # Reloads the method in Straight engine. We need to take @@ -133,23 +131,21 @@ def start_periodic_status_check StraightServer.logger.info "Starting periodic status checks of order #{id} (expires in #{t} seconds)" check_status_on_schedule(duration: t) end - self.save if self.status_changed? + save if self.status_changed? end def check_status_on_schedule(period: 10, iteration_index: 0, duration: 600, time_passed: 0) if StraightServer::Thread.interrupted?(thread: ::Thread.current) - StraightServer.logger.info "Checking status of order #{self.id} interrupted" + StraightServer.logger.info "Checking status of order #{id} interrupted" return end - StraightServer.logger.info "Checking status of order #{self.id}" + StraightServer.logger.info "Checking status of order #{id}" super end def time_left_before_expiration time_passed_after_creation = (Time.now - created_at).to_i - gateway.orders_expiration_period+(StraightServer::Config.expiration_overtime || 0) - time_passed_after_creation + gateway.orders_expiration_period + (StraightServer::Config.expiration_overtime || 0) - time_passed_after_creation end - end - end diff --git a/lib/straight-server/orders_controller.rb b/lib/straight-server/orders_controller.rb index 495a274..a60c90f 100644 --- a/lib/straight-server/orders_controller.rb +++ b/lib/straight-server/orders_controller.rb @@ -2,7 +2,6 @@ require_relative 'signature_validator' module StraightServer - class OrdersController include Goliath::Constants @@ -17,10 +16,9 @@ def initialize(env) end def create - unless @gateway - StraightServer.logger.warn "Gateway not found" - return [404, {}, "Gateway not found" ] + StraightServer.logger.warn 'Gateway not found' + return [404, {}, 'Gateway not found'] end if @gateway.check_signature @@ -29,7 +27,7 @@ def create ip = @env['HTTP_X_FORWARDED_FOR'].to_s ip = @env['REMOTE_ADDR'] if ip.empty? if StraightServer::Throttler.new(@gateway.id).deny?(ip) - StraightServer.logger.warn message = "Too many requests, please try again later" + StraightServer.logger.warn message = 'Too many requests, please try again later' return [429, {}, message] end end @@ -39,7 +37,7 @@ def create # This is to inform users of previous version of a deprecated param # It will have to be removed at some point. if @params['order_id'] - return [409, {}, "Error: order_id is no longer a valid param. Use keychain_id instead and consult the documentation." ] + return [409, {}, 'Error: order_id is no longer a valid param. Use keychain_id instead and consult the documentation.'] end order_data = { @@ -63,24 +61,23 @@ def create [200, {}, add_callback_data_warning(order).to_json] rescue Sequel::ValidationFailed => e StraightServer.logger.warn( - "VALIDATION ERRORS in order, cannot create it:\n" + - "#{e.message.split(",").each_with_index.map { |e,i| "#{i+1}. #{e.lstrip}"}.join("\n") }\n" + + "VALIDATION ERRORS in order, cannot create it:\n" \ + "#{e.message.split(',').each_with_index.map { |e, i| "#{i + 1}. #{e.lstrip}" }.join("\n")}\n" \ "Order data: #{order_data.inspect}\n" ) - [409, {}, "Invalid order: #{e.message}" ] + [409, {}, "Invalid order: #{e.message}"] rescue Straight::Gateway::OrderAmountInvalid => e - [409, {}, "Invalid order: #{e.message}" ] + [409, {}, "Invalid order: #{e.message}"] rescue StraightServer::GatewayModule::GatewayInactive - StraightServer.logger.warn message = "The gateway is inactive, you cannot create order with it" - [503, {}, message ] + StraightServer.logger.warn message = 'The gateway is inactive, you cannot create order with it' + [503, {}, message] end end def show - unless @gateway - StraightServer.logger.warn "Gateway not found" - return [404, {}, "Gateway not found" ] + StraightServer.logger.warn 'Gateway not found' + return [404, {}, 'Gateway not found'] end if @gateway.check_signature @@ -97,24 +94,23 @@ def show end def websocket - order = find_order if order begin @gateway.add_websocket_for_order ws = Faye::WebSocket.new(@env), order ws.rack_response rescue Gateway::WebsocketExists - [403, {}, "Someone is already listening to that order"] + [403, {}, 'Someone is already listening to that order'] rescue Gateway::WebsocketForCompletedOrder - [403, {}, "You cannot listen to this order because it is completed (status > 1)"] + [403, {}, 'You cannot listen to this order because it is completed (status > 1)'] end end end def cancel unless @gateway - StraightServer.logger.warn "Gateway not found" - return [404, {}, "Gateway not found"] + StraightServer.logger.warn 'Gateway not found' + return [404, {}, 'Gateway not found'] end if @gateway.check_signature @@ -128,73 +124,70 @@ def cancel order.cancel [200, {}, ''] else - [409, {}, "Order is not cancelable"] + [409, {}, 'Order is not cancelable'] end end end def last_keychain_id unless @gateway - StraightServer.logger.warn "Gateway not foun" - return [404, {}, "Gateway not found"] + StraightServer.logger.warn 'Gateway not foun' + return [404, {}, 'Gateway not found'] end - [200, {}, {gateway_id: @gateway.id, last_keychain_id: @gateway.last_keychain_id}.to_json] + [200, {}, { gateway_id: @gateway.id, last_keychain_id: @gateway.last_keychain_id }.to_json] end private - # Refactoring proposed: https://github.com/AlexanderPavlenko/straight-server/commit/49ea6e3732a9564c04d8dfecaee6d0ebaa462042 - def dispatch - - StraightServer.logger.blank_lines - StraightServer.logger.info "#{@method} #{@env['REQUEST_PATH']}\n#{@params}" - - @gateway = StraightServer::Gateway.find_by_hashed_id(@request_path[1]) - - @response = begin - if @request_path[3] # if an order id is supplied - @params['id'] = @request_path[3] - @params['id'] = @params['id'].to_i if @params['id'] =~ /\A\d+\Z/ - if @request_path[4] == 'websocket' - websocket - elsif @request_path[4] == 'cancel'&& @method == 'POST' - cancel - elsif @request_path[4].nil? && @method == 'GET' - show - end - elsif @request_path[2] == 'last_keychain_id' - last_keychain_id - elsif @request_path[3].nil?# && @method == 'POST' - create + # Refactoring proposed: https://github.com/AlexanderPavlenko/straight-server/commit/49ea6e3732a9564c04d8dfecaee6d0ebaa462042 + def dispatch + StraightServer.logger.blank_lines + StraightServer.logger.info "#{@method} #{@env['REQUEST_PATH']}\n#{@params}" + + @gateway = StraightServer::Gateway.find_by_hashed_id(@request_path[1]) + + @response = begin + if @request_path[3] # if an order id is supplied + @params['id'] = @request_path[3] + @params['id'] = @params['id'].to_i if @params['id'] =~ /\A\d+\Z/ + if @request_path[4] == 'websocket' + websocket + elsif @request_path[4] == 'cancel' && @method == 'POST' + cancel + elsif @request_path[4].nil? && @method == 'GET' + show end - rescue StraightServer::SignatureValidator::InvalidNonce - StraightServer.logger.warn message = "X-Nonce is invalid: #{@env["#{HTTP_PREFIX}X_NONCE"].inspect}" - [409, {}, message] - rescue StraightServer::SignatureValidator::InvalidSignature - StraightServer.logger.warn message = "X-Signature is invalid: #{@env["#{HTTP_PREFIX}X_SIGNATURE"].inspect}" - [409, {}, message] + elsif @request_path[2] == 'last_keychain_id' + last_keychain_id + elsif @request_path[3].nil? # && @method == 'POST' + create end - - @response = [404, {}, "#{@method} /#{@request_path.join('/')} Not found"] if @response.nil? + rescue StraightServer::SignatureValidator::InvalidNonce + StraightServer.logger.warn message = "X-Nonce is invalid: #{@env["#{HTTP_PREFIX}X_NONCE"].inspect}" + [409, {}, message] + rescue StraightServer::SignatureValidator::InvalidSignature + StraightServer.logger.warn message = "X-Signature is invalid: #{@env["#{HTTP_PREFIX}X_SIGNATURE"].inspect}" + [409, {}, message] end - def find_order - if @params['id'] =~ /[^\d]+/ - Order[:payment_id => @params['id']] - else - Order[@params['id']] - end - end + @response = [404, {}, "#{@method} /#{@request_path.join('/')} Not found"] if @response.nil? + end - def add_callback_data_warning(order) - o = order.to_h - if @params['data'].kind_of?(String) && @params['callback_data'].nil? - o[:WARNING] = "Maybe you meant to use callback_data? The API has changed now. Consult the documentation." - end - o + def find_order + if @params['id'] =~ /[^\d]+/ + Order[payment_id: @params['id']] + else + Order[@params['id']] end + end + def add_callback_data_warning(order) + o = order.to_h + if @params['data'].is_a?(String) && @params['callback_data'].nil? + o[:WARNING] = 'Maybe you meant to use callback_data? The API has changed now. Consult the documentation.' + end + o + end end - end diff --git a/lib/straight-server/random_string.rb b/lib/straight-server/random_string.rb index 6ec2ef4..2c26765 100644 --- a/lib/straight-server/random_string.rb +++ b/lib/straight-server/random_string.rb @@ -1,7 +1,6 @@ require 'securerandom' class String - def self.random(len) BTC::Base58.base58_from_data(SecureRandom.random_bytes(len))[0, len] end diff --git a/lib/straight-server/server.rb b/lib/straight-server/server.rb index c8928cc..df2de39 100644 --- a/lib/straight-server/server.rb +++ b/lib/straight-server/server.rb @@ -1,6 +1,5 @@ module StraightServer class Server < Goliath::API - use Goliath::Rack::Params include StraightServer::Initializer Faye::WebSocket.load_adapter('goliath') @@ -20,8 +19,8 @@ def options_parser(opts, options) # Goliath server. If don't do that, there will be an exception saying "unrecognized argument". # In reality, we make use of --config-dir value in the in StraightServer::Initializer and stored # it in StraightServer::Initializer.config_dir property. - opts.on('-c', '--config-dir STRING', "Directory where config files and addons are placed") do |val| - options[:config_dir] = File.expand_path(val || ENV['HOME'] + '/.straight' ) + opts.on('-c', '--config-dir STRING', 'Directory where config files and addons are placed') do |val| + options[:config_dir] = File.expand_path(val || ENV['HOME'] + '/.straight') end end @@ -34,7 +33,6 @@ def response(env) # just checks that the path starts with /gateways/:id/orders StraightServer.logger.watch_exceptions do - # If the process is daemonized, we get Sequel::DatabaseDisconnectError with Postgres. # The explanation is here: https://github.com/thuehlinger/daemons/issues/31 # Until I figure out where to call connect_to_db so that it connects to the DB @@ -45,12 +43,10 @@ def response(env) connect_to_db return process_request(env) end - end # Assume things went wrong, if they didn't go right [500, {}, "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} Server Error"] - end # This is a separate method now because of the need to rescue Sequel::DatabaseDisconnectError @@ -71,8 +67,7 @@ def process_request(env) return action.call(env) if env['REQUEST_PATH'] =~ path end # no block was called, means no route matched. Let's render 404 - return [404, {}, "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} Not found"] + [404, {}, "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} Not found"] end - end end diff --git a/lib/straight-server/signature_validator.rb b/lib/straight-server/signature_validator.rb index dd08962..66938d3 100644 --- a/lib/straight-server/signature_validator.rb +++ b/lib/straight-server/signature_validator.rb @@ -17,8 +17,8 @@ def initialize(gateway, env) end def validate! - raise InvalidNonce unless valid_nonce? - raise InvalidSignature unless valid_signature? + fail InvalidNonce unless valid_nonce? + fail InvalidSignature unless valid_signature? true end @@ -52,10 +52,10 @@ def last_nonce_key def signature self.class.signature( nonce: env["#{HTTP_PREFIX}X_NONCE"], - body: env[RACK_INPUT].kind_of?(StringIO) ? env[RACK_INPUT].string : env[RACK_INPUT].to_s, + body: env[RACK_INPUT].is_a?(StringIO) ? env[RACK_INPUT].string : env[RACK_INPUT].to_s, method: env[REQUEST_METHOD], request_uri: env[REQUEST_URI], - secret: gateway.secret, + secret: gateway.secret ) end diff --git a/lib/straight-server/thread.rb b/lib/straight-server/thread.rb index 85dba2f..d7ae18b 100644 --- a/lib/straight-server/thread.rb +++ b/lib/straight-server/thread.rb @@ -1,13 +1,12 @@ module StraightServer class Thread - def self.new(label: nil, &block) thread = ::Thread.new(&block) thread[:label] = label thread end - INTERRUPTION_FLAG = lambda { |label| "#{Config[:'redis.prefix']}:interrupt_thread:#{label}" } + INTERRUPTION_FLAG = ->(label) { "#{Config[:'redis.prefix']}:interrupt_thread:#{label}" } def self.interrupt(label:) redis = StraightServer.redis_connection diff --git a/lib/straight-server/throttler.rb b/lib/straight-server/throttler.rb index c584983..e608626 100644 --- a/lib/straight-server/throttler.rb +++ b/lib/straight-server/throttler.rb @@ -1,6 +1,5 @@ module StraightServer class Throttler - def initialize(gateway_id) @id = "gateway_#{gateway_id}" @redis = StraightServer.redis_connection diff --git a/lib/straight-server/utils/hash_string_to_sym_keys.rb b/lib/straight-server/utils/hash_string_to_sym_keys.rb index dd54535..12c6f2c 100644 --- a/lib/straight-server/utils/hash_string_to_sym_keys.rb +++ b/lib/straight-server/utils/hash_string_to_sym_keys.rb @@ -1,17 +1,16 @@ class Hash - # Replace String keys in the current hash with symbol keys def keys_to_sym! new_hash = keys_to_sym - self.clear - new_hash.each do |k,v| + clear + new_hash.each do |k, v| self[k] = v end end def keys_to_sym - symbolized_hash = {} - self.each do |k,v| + symbolized_hash = {} + each do |k, v| if k =~ /\A[a-zA-Z0-9!?_]+\Z/ symbolized_hash[k.to_sym] = v else @@ -20,5 +19,4 @@ def keys_to_sym end symbolized_hash end - end diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 682fd2a..5837ef1 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -9,8 +9,8 @@ namespace :db do connect_to_db end - desc "Migrates the database" - task :migrate, [:step] => :environment do |t, args| + desc 'Migrates the database' + task :migrate, [:step] => :environment do |_t, args| target = args[:step] && (step = args[:step].to_i) > 0 ? current_migration_version + step : nil @@ -18,8 +18,8 @@ namespace :db do dump_schema end - desc "Rollbacks database migrations" - task :rollback, [:step] => :environment do |t, args| + desc 'Rollbacks database migrations' + task :rollback, [:step] => :environment do |_t, args| target = args[:step] && (step = args[:step].to_i) > 0 ? current_migration_version - step : current_migration_version - 1 diff --git a/spec/factories.rb b/spec/factories.rb index 6db1826..40ec5f4 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,12 +1,10 @@ FactoryGirl.define do - factory :order, class: StraightServer::Order do sequence(:id) { |i| i } sequence(:keychain_id) { |i| i } sequence(:address) { |i| "address_#{i}" } amount 10 gateway_id 1 - to_create { |order| order.save } + to_create(&:save) end - end diff --git a/spec/fixtures/test_addon.rb b/spec/fixtures/test_addon.rb index cb891ca..974db1a 100644 --- a/spec/fixtures/test_addon.rb +++ b/spec/fixtures/test_addon.rb @@ -5,4 +5,4 @@ def test_addon_method end end end -end \ No newline at end of file +end diff --git a/spec/lib/gateway_spec.rb b/spec/lib/gateway_spec.rb index f09fac9..8ced76d 100644 --- a/spec/lib/gateway_spec.rb +++ b/spec/lib/gateway_spec.rb @@ -1,10 +1,9 @@ require 'spec_helper' RSpec.describe StraightServer::Gateway do - before(:each) do @gateway = StraightServer::GatewayOnConfig.find_by_id(1) - @order_mock = double("order mock") + @order_mock = double('order mock') allow(@order_mock).to receive(:old_status) allow(@order_mock).to receive(:description=) allow(@order_mock).to receive(:set_amount_paid) @@ -14,26 +13,26 @@ @new_order_args = { amount: 1, keychain_id: 1, currency: nil, btc_denomination: nil } end - it "sets order amount in satoshis calculated from another currency" do + it 'sets order amount in satoshis calculated from another currency' do @gateway = StraightServer::GatewayOnConfig.find_by_id(2) allow(@gateway.exchange_rate_adapters.first).to receive(:rate_for).and_return(450.5412) - expect(@gateway.create_order(amount: 2252.706, currency: 'USD').amount).to eq(500000000) + expect(@gateway.create_order(amount: 2252.706, currency: 'USD').amount).to eq(500_000_000) end it "doesn't allow to create a new order if the gateway is inactive" do @gateway.active = false - expect( -> { @gateway.create_order }).to raise_exception(StraightServer::GatewayModule::GatewayInactive) + expect(-> { @gateway.create_order }).to raise_exception(StraightServer::GatewayModule::GatewayInactive) @gateway.active = true end - it "loads blockchain adapters according to the config file" do + it 'loads blockchain adapters according to the config file' do gateway = StraightServer::GatewayOnConfig.find_by_id(2) expect(gateway.blockchain_adapters.map(&:class)).to eq([Straight::Blockchain::InsightAdapter, Straight::Blockchain::BlockchainInfoAdapter, Straight::Blockchain::MyceliumAdapter]) end - it "loads Insight adapter with given host url" do + it 'loads Insight adapter with given host url' do gateway = StraightServer::GatewayOnConfig.find_by_id(2) expect(gateway.blockchain_adapters.first.class).to eq(Straight::Blockchain::InsightAdapter) end @@ -48,15 +47,14 @@ end it "only warns about an invalid Bitcoin address, but doesn't fail", one: true do - expect(StraightServer.logger).to receive(:warn) + expect(StraightServer.logger).to receive(:warn) allow_any_instance_of(Straight::Blockchain::MyceliumAdapter).to receive(:api_request).and_return(nil) allow_any_instance_of(Straight::Blockchain::InsightAdapter).to receive(:api_request).and_return(nil) # mainnet Bitcoin address, while we're on testnet expect(@gateway.fetch_transactions_for('12X3JTpcGPS1GXmuJn9gT3gspP6YFsFT6W')).to eq([]) end - context "reusing addresses" do - + context 'reusing addresses' do # Config.reuse_address_orders_threshold for the test env is 5 before(:each) do @@ -69,27 +67,27 @@ @expired_orders_2 = create_list(:order, 2, status: StraightServer::Order::STATUSES[:expired], gateway_id: @gateway.id) end - it "finds all expired orders that follow in a row" do + it 'finds all expired orders that follow in a row' do expect(@gateway.send(:find_expired_orders_row).size).to eq(5) - expect(@gateway.send(:find_expired_orders_row).map(&:id)).to include(*@expired_orders_1.map(&:id)) + expect(@gateway.send(:find_expired_orders_row).map(&:id)).to include(*@expired_orders_1.map(&:id)) expect(@gateway.send(:find_expired_orders_row).map(&:id)).not_to include(*@expired_orders_2.map(&:id)) end - it "picks an expired order which address is going to be reused" do + it 'picks an expired order which address is going to be reused' do expect(@gateway.find_reusable_order).to eq(@expired_orders_1.last) end - it "picks an expired order which address is going to be reused only when this address received no transactions" do + it 'picks an expired order which address is going to be reused only when this address received no transactions' do allow(@gateway).to receive(:fetch_transactions_for).with(@expired_orders_1.last.address).and_return(['transaction']) expect(@gateway.find_reusable_order).to eq(nil) end - it "creates a new order with a reused address" do + it 'creates a new order with a reused address' do reused_order = @expired_orders_1.last order = @gateway.create_order(amount: 2252.706, currency: 'BTC') expect(order.keychain_id).to eq(reused_order.keychain_id) - expect(order.address).to eq(@gateway.address_provider.new_address(keychain_id: reused_order.keychain_id)) - expect(order.reused).to eq(1) + expect(order.address).to eq(@gateway.address_provider.new_address(keychain_id: reused_order.keychain_id)) + expect(order.reused).to eq(1) end it "doesn't increment last_keychain_id if order is reused" do @@ -100,10 +98,10 @@ order.status = StraightServer::Order::STATUSES[:paid] order.save order_2 = @gateway.create_order(amount: 2252.706, currency: 'BTC') - expect(@gateway.last_keychain_id).to eq(last_keychain_id+1) + expect(@gateway.last_keychain_id).to eq(last_keychain_id + 1) end - it "after the reused order was paid, gives next order a new keychain_id" do + it 'after the reused order was paid, gives next order a new keychain_id' do order = @gateway.create_order(amount: 2252.706, currency: 'BTC') order.status = StraightServer::Order::STATUSES[:expired] order.save @@ -114,11 +112,9 @@ order.save expect(@gateway.send(:find_expired_orders_row).map(&:id)).to be_empty end - end - context "callback url" do - + context 'callback url' do before(:each) do @gateway = StraightServer::GatewayOnConfig.find_by_id(2) @order = build(:order, gateway: @gateway, address: 'address_1', keychain_id: 1, id: 1) @@ -126,91 +122,88 @@ allow(@order).to receive(:tid).and_return('tid1') end - it "sends a request to the callback_url and saves response" do - stub_request(:get, 'http://localhost:3001/payment-callback?address=address_1&amount=10&amount_in_btc=0.0000001&amount_paid_in_btc=0.&keychain_id=1&last_keychain_id=0&order_id=1&status=1&tid=tid1'). - with(headers: {'X-Signature' => 'waGfkiy5VdiAjOfzMXp4ShRF5FaOy7BfIKWzryKhNKVWdCToqTC2vt9aE/FNqc0mjVqAh3uQBfyRdC1YYPFr6g=='}). - to_return(status: 200, body: 'okay') + it 'sends a request to the callback_url and saves response' do + stub_request(:get, 'http://localhost:3001/payment-callback?address=address_1&amount=10&amount_in_btc=0.0000001&amount_paid_in_btc=0.&keychain_id=1&last_keychain_id=0&order_id=1&status=1&tid=tid1') + .with(headers: { 'X-Signature' => 'waGfkiy5VdiAjOfzMXp4ShRF5FaOy7BfIKWzryKhNKVWdCToqTC2vt9aE/FNqc0mjVqAh3uQBfyRdC1YYPFr6g==' }) + .to_return(status: 200, body: 'okay') expect(@gateway).to receive(:sleep).exactly(0).times @gateway.order_status_changed(@order) expect(@order.callback_response).to eq(code: '200', body: 'okay') end it "keeps sending request according to the callback schedule if there's an error" do - stub_request(:get, 'http://localhost:3001/payment-callback?address=address_1&amount=10&amount_in_btc=0.0000001&amount_paid_in_btc=0.&keychain_id=1&last_keychain_id=0&order_id=1&status=1&tid=tid1'). - with(headers: {'X-Signature' => 'waGfkiy5VdiAjOfzMXp4ShRF5FaOy7BfIKWzryKhNKVWdCToqTC2vt9aE/FNqc0mjVqAh3uQBfyRdC1YYPFr6g=='}). - to_return(status: 404, body: '') + stub_request(:get, 'http://localhost:3001/payment-callback?address=address_1&amount=10&amount_in_btc=0.0000001&amount_paid_in_btc=0.&keychain_id=1&last_keychain_id=0&order_id=1&status=1&tid=tid1') + .with(headers: { 'X-Signature' => 'waGfkiy5VdiAjOfzMXp4ShRF5FaOy7BfIKWzryKhNKVWdCToqTC2vt9aE/FNqc0mjVqAh3uQBfyRdC1YYPFr6g==' }) + .to_return(status: 404, body: '') expect(@gateway).to receive(:sleep).exactly(10).times @gateway.order_status_changed(@order) end - it "receives random data in :data params and sends it back in a callback request" do - uri = "/payment-callback?order_id=1&amount=10&amount_in_btc=0.0000001&amount_paid_in_btc=0.&status=1&address=address_1&tid=tid1&keychain_id=1&last_keychain_id=1&callback_data=so%3Fme+ran%26dom+data" + it 'receives random data in :data params and sends it back in a callback request' do + uri = '/payment-callback?order_id=1&amount=10&amount_in_btc=0.0000001&amount_paid_in_btc=0.&status=1&address=address_1&tid=tid1&keychain_id=1&last_keychain_id=1&callback_data=so%3Fme+ran%26dom+data' signature = 'S2P8A16+RPaegTzJnb0Eg91csb1SExjdnvadABmQvfoIry4POBp6WbA6UOSqXojzRevyC8Ya/5QrQTnNxIb4og==' expect(StraightServer::SignatureValidator.signature(nonce: nil, body: nil, method: 'GET', request_uri: uri, secret: @gateway.secret)).to eq signature - stub_request(:get, "http://localhost:3001#{uri}").with(headers: {'X-Signature' => signature}).to_return(status: 200, body: '') + stub_request(:get, "http://localhost:3001#{uri}").with(headers: { 'X-Signature' => signature }).to_return(status: 200, body: '') expect(@gateway).to receive(:new_order).with(@new_order_args).once.and_return(@order) @gateway.create_order(amount: 1, callback_data: 'so?me ran&dom data') @gateway.order_status_changed(@order) end - it "uses callback_url from order when making callback" do + it 'uses callback_url from order when making callback' do uri = '/?with=params&order_id=1&amount=10&amount_in_btc=0.0000001&amount_paid_in_btc=0.&status=1&address=address_1&tid=tid1&keychain_id=1&last_keychain_id=0' signature = 'jCCEthsR0XsdDtMfEFBn1+G+XxoVTH1noO4yrdGUBJwH3ysF9QDuPGwWDlgrSqrEyxbJB1KoBIBbcRxaAE2iZA==' expect(StraightServer::SignatureValidator.signature(nonce: nil, body: nil, method: 'GET', request_uri: uri, secret: @gateway.secret)).to eq signature - stub_request(:get, "http://new_url#{uri}").with(headers: {'X-Signature' => signature}).to_return(status: 200, body: '') + stub_request(:get, "http://new_url#{uri}").with(headers: { 'X-Signature' => signature }).to_return(status: 200, body: '') @order.callback_url = 'http://new_url?with=params' @gateway.order_status_changed(@order) end end - describe "order counters" do - - it "uses 0 for non-existent order counters and increments them" do - expect(@gateway.order_counters(reload: true)).to include({ new: 0, unconfirmed: 0, paid: 0, underpaid: 0, overpaid: 0, expired: 0 }) + describe 'order counters' do + it 'uses 0 for non-existent order counters and increments them' do + expect(@gateway.order_counters(reload: true)).to include(new: 0, unconfirmed: 0, paid: 0, underpaid: 0, overpaid: 0, expired: 0) @gateway.increment_order_counter!(:new) expect(@gateway.order_counters(reload: true)[:new]).to eq(1) end - it "raises exception when trying to access counters but the feature is disabled" do + it 'raises exception when trying to access counters but the feature is disabled' do allow(StraightServer::Config).to receive(:count_orders).and_return(false) - expect( -> { @gateway.order_counters(reload: true) }).to raise_exception(StraightServer::Gateway::OrderCountersDisabled) - expect( -> { @gateway.increment_order_counter!(:new) }).to raise_exception(StraightServer::Gateway::OrderCountersDisabled) + expect(-> { @gateway.order_counters(reload: true) }).to raise_exception(StraightServer::Gateway::OrderCountersDisabled) + expect(-> { @gateway.increment_order_counter!(:new) }).to raise_exception(StraightServer::Gateway::OrderCountersDisabled) end it "updates gateway's order counters when an associated order status changes" do - allow_any_instance_of(StraightServer::Order).to receive(:transaction).and_return({ tid: 'xxx' }) + allow_any_instance_of(StraightServer::Order).to receive(:transaction).and_return(tid: 'xxx') allow(@gateway).to receive(:send_callback_http_request) allow(@gateway).to receive(:send_order_to_websocket_client) - expect(@gateway.order_counters(reload: true)).to eq({ new: 0, unconfirmed: 0, paid: 0, underpaid: 0, overpaid: 0, expired: 0, canceled: 0 }) + expect(@gateway.order_counters(reload: true)).to eq(new: 0, unconfirmed: 0, paid: 0, underpaid: 0, overpaid: 0, expired: 0, canceled: 0) order = create(:order, gateway_id: @gateway.id) - expect(@gateway.order_counters(reload: true)).to eq({ new: 1, unconfirmed: 0, paid: 0, underpaid: 0, overpaid: 0, expired: 0, canceled: 0 }) + expect(@gateway.order_counters(reload: true)).to eq(new: 1, unconfirmed: 0, paid: 0, underpaid: 0, overpaid: 0, expired: 0, canceled: 0) order.status = 2 - expect(@gateway.order_counters(reload: true)).to eq({ new: 0, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0 }) + expect(@gateway.order_counters(reload: true)).to eq(new: 0, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0) - expect(@gateway.order_counters(reload: true)).to eq({ new: 0, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0 }) + expect(@gateway.order_counters(reload: true)).to eq(new: 0, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0) order = create(:order, gateway_id: @gateway.id) - expect(@gateway.order_counters(reload: true)).to eq({ new: 1, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0 }) + expect(@gateway.order_counters(reload: true)).to eq(new: 1, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0) order.status = 1 - expect(@gateway.order_counters(reload: true)).to eq({ new: 0, unconfirmed: 1, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0 }) + expect(@gateway.order_counters(reload: true)).to eq(new: 0, unconfirmed: 1, paid: 1, underpaid: 0, overpaid: 0, expired: 0, canceled: 0) order.status = 5 - expect(@gateway.order_counters(reload: true)).to eq({ new: 0, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 1, canceled: 0 }) + expect(@gateway.order_counters(reload: true)).to eq(new: 0, unconfirmed: 0, paid: 1, underpaid: 0, overpaid: 0, expired: 1, canceled: 0) end it "doesn't increment orders on status update unless the option is turned on (but no exception raised)" do allow(StraightServer::Config).to receive(:count_orders).and_return(false) - allow_any_instance_of(StraightServer::Order).to receive(:transaction).and_return({ tid: 'xxx' }) + allow_any_instance_of(StraightServer::Order).to receive(:transaction).and_return(tid: 'xxx') allow(@gateway).to receive(:send_callback_http_request) allow(@gateway).to receive(:send_order_to_websocket_client) order = create(:order, gateway_id: @gateway.id) expect(StraightServer.redis_connection.get("#{StraightServer::Config.redis[:prefix]}:gateway_#{@gateway.id}:new_orders_counter")).to be_nil end - end - describe "config based gateway" do - - it "loads all the gateways from the config file and assigns correct attributes" do + describe 'config based gateway' do + it 'loads all the gateways from the config file and assigns correct attributes' do gateway1 = StraightServer::GatewayOnConfig.find_by_id(1) gateway2 = StraightServer::GatewayOnConfig.find_by_id(2) expect(gateway1).to be_kind_of(StraightServer::GatewayOnConfig) @@ -218,16 +211,16 @@ expect(gateway1.pubkey).to eq('xpub6Arp6y5VVQzq3LWTHz7gGsGKAdM697RwpWgauxmyCybncqoAYim6P63AasNKSy3VUAYXFj7tN2FZ9CM9W7yTfmerdtAPU4amuSNjEKyDeo6') expect(gateway1.confirmations_required).to eq(0) - expect(gateway1.order_class).to eq("StraightServer::Order") - expect(gateway1.name).to eq("default") + expect(gateway1.order_class).to eq('StraightServer::Order') + expect(gateway1.name).to eq('default') expect(gateway2.pubkey).to eq('xpub6AH1Ymkkrwk3TaMrVrXBCpcGajKc9a1dAJBTKr1i4GwYLgLk7WDvPtN1o1cAqS5DZ9CYzn3gZtT7BHEP4Qpsz24UELTncPY1Zsscsm3ajmX') expect(gateway2.confirmations_required).to eq(0) - expect(gateway2.order_class).to eq("StraightServer::Order") - expect(gateway2.name).to eq("second_gateway") + expect(gateway2.order_class).to eq('StraightServer::Order') + expect(gateway2.name).to eq('second_gateway') end - it "saves and retrieves last_keychain_id from the file in the .straight dir" do + it 'saves and retrieves last_keychain_id from the file in the .straight dir' do @gateway = StraightServer::GatewayOnConfig.find_by_id(2) @gateway.check_signature = false @gateway.save @@ -236,50 +229,50 @@ @gateway.save expect(File.read("#{ENV['HOME']}/.straight/second_gateway_last_keychain_id").to_i).to eq(1) - expect(@gateway).to receive(:new_order).with(@new_order_args.merge({ keychain_id: 2})).once.and_return(@order_mock) + expect(@gateway).to receive(:new_order).with(@new_order_args.merge(keychain_id: 2)).once.and_return(@order_mock) @gateway.create_order(amount: 1) expect(File.read("#{ENV['HOME']}/.straight/second_gateway_last_keychain_id").to_i).to eq(2) end - it "searches for Gateway using regular ids when find_by_hashed_id method is called" do + it 'searches for Gateway using regular ids when find_by_hashed_id method is called' do expect(StraightServer::GatewayOnConfig.find_by_hashed_id(1)).not_to be_nil end - it "set test mode `on` based on config" do + it 'set test mode `on` based on config' do expect(@gateway.test_mode).to be true end - it "set test mode `off`" do + it 'set test mode `off`' do @gateway = StraightServer::GatewayOnConfig.find_by_id(2) expect(@gateway.test_mode).to be false end - it "using testnet when test mode is enabled" do + it 'using testnet when test mode is enabled' do @gateway = StraightServer::GatewayOnConfig.find_by_id(1) expect(@gateway.blockchain_adapters).to_not be nil expect(@gateway.blockchain_adapters.map(&:class)).to eq(@gateway.test_blockchain_adapters.map(&:class)) end - it "using testnet adapter that url given in config" do + it 'using testnet adapter that url given in config' do @gateway = StraightServer::GatewayOnConfig.find_by_id(1) expect(@gateway.blockchain_adapters).to_not be nil expect(@gateway.test_blockchain_adapters.first.class).to eq(Straight::Blockchain::InsightAdapter) end - it "fallback to another adapter if on previous one not ready for testnet" do + it 'fallback to another adapter if on previous one not ready for testnet' do @gateway = StraightServer::GatewayOnConfig.find_by_id(1) - Straight::Blockchain::InsightAdapter.class_eval("def self.test_url=(val) @@test_url=val end") + Straight::Blockchain::InsightAdapter.class_eval('def self.test_url=(val) @@test_url=val end') Straight::Blockchain::InsightAdapter.test_url = nil expect(@gateway.test_blockchain_adapters.first.class).to eq(Straight::Blockchain::MyceliumAdapter) end - it "disable test mode manually" do + it 'disable test mode manually' do @gateway = StraightServer::GatewayOnConfig.find_by_id(1) @gateway.test_mode = false expect(@gateway.blockchain_adapters).to_not eq([Straight::Blockchain::MyceliumAdapter.testnet_adapter]) end - it "save test_last_keychain_id file with approciate data" do + it 'save test_last_keychain_id file with approciate data' do gateway = StraightServer::GatewayOnConfig.find_by_id(1) gateway.check_signature = false gateway.save @@ -288,14 +281,12 @@ gateway.save expect(File.read("#{ENV['HOME']}/.straight/default_test_last_keychain_id").to_i).to eq(1) end - end - describe "db based gateway" do - + describe 'db based gateway' do before(:each) do # clean the database - DB.run("DELETE FROM gateways") + DB.run('DELETE FROM gateways') @gateway = StraightServer::GatewayOnDB.new( confirmations_required: 0, @@ -304,65 +295,64 @@ secret: 'secret', name: 'default', check_signature: true, - exchange_rate_adapter_names: ['Bitpay', 'Coinbase', 'Bitstamp'] + exchange_rate_adapter_names: %w(Bitpay Coinbase Bitstamp) ) end - it "saves and retrieves last_keychain_id from the db" do + it 'saves and retrieves last_keychain_id from the db' do @gateway.check_signature = false @gateway.save - expect(DB[:gateways][:name => 'default'][:last_keychain_id]).to eq(0) + expect(DB[:gateways][name: 'default'][:last_keychain_id]).to eq(0) @gateway.update_last_keychain_id @gateway.save @gateway.refresh - expect(DB[:gateways][:name => 'default'][:last_keychain_id]).to eq(1) + expect(DB[:gateways][name: 'default'][:last_keychain_id]).to eq(1) - expect(@gateway).to receive(:new_order).with(@new_order_args.merge({ keychain_id: 2})).once.and_return(@order_mock) + expect(@gateway).to receive(:new_order).with(@new_order_args.merge(keychain_id: 2)).once.and_return(@order_mock) @gateway.create_order(amount: 1) - expect(DB[:gateways][:name => 'default'][:last_keychain_id]).to eq(2) + expect(DB[:gateways][name: 'default'][:last_keychain_id]).to eq(2) end - it "encryptes and decrypts the gateway secret" do + it 'encryptes and decrypts the gateway secret' do expect(@gateway.save) - expect(@gateway[:secret]).to eq("96c1c24edff5c1c2:6THJEZqg+2qlDhtWE2Tytg==") - expect(@gateway.secret).to eq("secret") + expect(@gateway[:secret]).to eq('96c1c24edff5c1c2:6THJEZqg+2qlDhtWE2Tytg==') + expect(@gateway.secret).to eq('secret') end - it "re-encrypts the new gateway secrect if it was changed" do + it 're-encrypts the new gateway secrect if it was changed' do @gateway.save @gateway.update(secret: 'new secret', update_secret: true) - expect(@gateway.secret).to eq("new secret") + expect(@gateway.secret).to eq('new secret') end - it "finds orders using #find_by_id method which is essentially an alias for Gateway[]" do + it 'finds orders using #find_by_id method which is essentially an alias for Gateway[]' do @gateway.save expect(StraightServer::GatewayOnDB.find_by_id(@gateway.id)).to eq(@gateway) end - it "assigns hashed_id to gateway and then finds gateway using that value" do + it 'assigns hashed_id to gateway and then finds gateway using that value' do @gateway.save hashed_id = hmac_sha256(@gateway.id, 'global server secret') expect(@gateway.hashed_id).to eq(hashed_id) expect(StraightServer::GatewayOnDB.find_by_hashed_id(hashed_id)).to eq(@gateway) end - context "test mode" do - + context 'test mode' do before(:each) do - @gateway.test_pubkey = "txpub" + @gateway.test_pubkey = 'txpub' end - - it "not activate after created" do + + it 'not activate after created' do @gateway.save expect(@gateway.test_mode).to be false end - it "not using testnet adapter by default" do + it 'not using testnet adapter by default' do @gateway.save expect(@gateway.blockchain_adapters.map(&:class)).to_not eq(@gateway.test_blockchain_adapters.map(&:class)) end - it "activated if mode is specified explicity" do + it 'activated if mode is specified explicity' do @gateway.test_mode = true @gateway.save @gateway.refresh @@ -370,7 +360,7 @@ expect(@gateway.blockchain_adapters.map(&:class)).to eq(@gateway.test_blockchain_adapters.map(&:class)) end - it "enabled and not saved" do + it 'enabled and not saved' do @gateway.save @gateway.enable_test_mode! expect(@gateway.test_mode).to be true @@ -378,7 +368,7 @@ expect(@gateway.test_mode).to be false end - it "enabled and saved" do + it 'enabled and saved' do @gateway.save @gateway.enable_test_mode! expect(@gateway.test_mode).to be true @@ -386,7 +376,7 @@ expect(@gateway.test_mode).to be false end - it "enabled and saved" do + it 'enabled and saved' do @gateway[:test_mode] = false expect(@gateway.test_mode).to eq false @gateway.enable_test_mode! @@ -394,16 +384,16 @@ @gateway.refresh expect(@gateway.test_mode).to be true end - - it "field updates in mass assigment" do + + it 'field updates in mass assigment' do @gateway.save - fields = {test_mode: true} + fields = { test_mode: true } @gateway.update(fields) @gateway.refresh expect(@gateway.test_mode).to be true end - it "update test_last_keychain_id" do + it 'update test_last_keychain_id' do @gateway.check_signature = false @gateway.enable_test_mode! @gateway.save @@ -411,10 +401,10 @@ expect(@gateway).to receive(:new_order).with(@new_order_args).once.and_return(@order_mock) @gateway.create_order(amount: 1) - expect(DB[:gateways][:name => 'default'][:test_last_keychain_id]).to eq(1) + expect(DB[:gateways][name: 'default'][:test_last_keychain_id]).to eq(1) end - - it "validate that test public key is provided when saving with test mode flag" do + + it 'validate that test public key is provided when saving with test mode flag' do @gateway = StraightServer::GatewayOnDB.new( confirmations_required: 0, pubkey: 'xpub-000', @@ -427,7 +417,7 @@ it "doesn't require pubkey in test mode" do @gateway = StraightServer::GatewayOnDB.new( test_pubkey: 'xpub-000', - test_mode: true, + test_mode: true ) expect(@gateway.valid?).to be true @gateway.test_mode = false @@ -436,25 +426,24 @@ end end - describe "handling websockets" do - + describe 'handling websockets' do before(:each) do - StraightServer::GatewayModule.class_variable_set(:@@websockets, { @gateway.id => {} }) - @ws = double("websocket mock") + StraightServer::GatewayModule.class_variable_set(:@@websockets, @gateway.id => {}) + @ws = double('websocket mock') allow(@ws).to receive(:on).with(:close) allow(@order_mock).to receive(:id).and_return(1) allow(@order_mock).to receive(:status).and_return(0) end - it "adds a new websocket for the order" do + it 'adds a new websocket for the order' do @gateway.add_websocket_for_order(@ws, @order_mock) - expect(@gateway.websockets).to eq({1 => @ws}) + expect(@gateway.websockets).to eq(1 => @ws) end - it "sends a message to the websocket when status of the order is changed and closes the connection" do + it 'sends a message to the websocket when status of the order is changed and closes the connection' do allow(@gateway).to receive(:send_callback_http_request) # ignoring the callback which sends an callback_url request - expect(@order_mock).to receive(:to_json).and_return("order json info") - expect(@ws).to receive(:send).with("order json info") + expect(@order_mock).to receive(:to_json).and_return('order json info') + expect(@ws).to receive(:send).with('order json info') expect(@ws).to receive(:close) @gateway.add_websocket_for_order(@ws, @order_mock) @gateway.order_status_changed(@order_mock) @@ -462,19 +451,17 @@ it "doesn't allow to listen to orders with statuses other than 0 or 1" do allow(@order_mock).to receive(:status).and_return(2) - expect( -> { @gateway.add_websocket_for_order(@ws, @order_mock) }).to raise_exception(StraightServer::Gateway::WebsocketForCompletedOrder) + expect(-> { @gateway.add_websocket_for_order(@ws, @order_mock) }).to raise_exception(StraightServer::Gateway::WebsocketForCompletedOrder) end it "doesn't allow to create a second websocket for the same order" do allow(@order_mock).to receive(:status).and_return(0) @gateway.add_websocket_for_order(@ws, @order_mock) - expect( -> { @gateway.add_websocket_for_order(@ws, @order_mock) }).to raise_exception(StraightServer::Gateway::WebsocketExists) + expect(-> { @gateway.add_websocket_for_order(@ws, @order_mock) }).to raise_exception(StraightServer::Gateway::WebsocketExists) end - end def hmac_sha256(key, secret) - OpenSSL::HMAC.digest('sha256', secret, key.to_s).unpack("H*").first + OpenSSL::HMAC.digest('sha256', secret, key.to_s).unpack('H*').first end - end diff --git a/spec/lib/initializer_spec.rb b/spec/lib/initializer_spec.rb index 1ca8561..38717fb 100644 --- a/spec/lib/initializer_spec.rb +++ b/spec/lib/initializer_spec.rb @@ -2,7 +2,6 @@ require_relative '../../lib/straight-server' RSpec.describe StraightServer::Initializer do - class StraightServer::TestInitializerClass include StraightServer::Initializer include StraightServer::Initializer::ConfigDir @@ -10,24 +9,25 @@ class StraightServer::TestInitializerClass before(:each) do # redefining Kernel #puts and #print, to get rid of outputs/noise while running specs - module Kernel - alias :original_puts :puts - alias :original_print :print - def puts(s); end - def print(s); end + module Kernel + alias_method :original_puts, :puts + alias_method :original_print, :print + def puts(_s); end + + def print(_s); end end remove_tmp_dir @templates_dir = File.expand_path('../../templates', File.dirname(__FILE__)) - ENV['HOME'] = File.expand_path('../tmp', File.dirname(__FILE__)) + ENV['HOME'] = File.expand_path('../tmp', File.dirname(__FILE__)) @initializer = StraightServer::TestInitializerClass.new StraightServer::Initializer::ConfigDir.set! end after(:each) do # reverting redefinition of Kernel #puts and #print made in before block - module Kernel - alias :puts :original_puts - alias :print :original_print + module Kernel + alias_method :puts, :original_puts + alias_method :print, :original_print end remove_tmp_dir end @@ -38,11 +38,11 @@ module Kernel begin @initializer.create_config_files rescue Exception => e - expect(e.status).to eq 0 + expect(e.status).to eq 0 end end - it "creates config files" do + it 'creates config files' do create_config_files expect(File.exist?(StraightServer::Initializer::ConfigDir.path)).to eq true created_config_files = Dir.glob(File.join(File.expand_path('../tmp', File.dirname(__FILE__)), '**', '*'), File::FNM_DOTMATCH).select { |f| File.file? f } @@ -54,25 +54,25 @@ module Kernel end end - it "connects to the database" do - StraightServer::Config.db = { + it 'connects to the database' do + StraightServer::Config.db = { adapter: 'sqlite', - name: 'straight.db', + name: 'straight.db' } create_config_files @initializer.connect_to_db expect(StraightServer.db_connection.test_connection).to be true end - it "creates logger" do + it 'creates logger' do StraightServer::Config.logmaster = { 'log_level' => 'WARN', 'file' => 'straight.log' } create_config_files expect(@initializer.create_logger).to be_kind_of(StraightServer::Logger) end - it "runs migrations" + it 'runs migrations' - it "loads addons" do + it 'loads addons' do create_config_files FileUtils.ln_sf(File.expand_path(File.join(ENV['HOME'], '../fixtures/addons.yml')), File.expand_path(File.join(ENV['HOME'], '../tmp/.straight/addons.yml'))) FileUtils.ln_sf(File.expand_path(File.join(ENV['HOME'], '../fixtures')), File.expand_path(File.join(ENV['HOME'], '../tmp/.straight/addons'))) @@ -92,7 +92,6 @@ def disable_logger_noise def remove_tmp_dir if Dir.exist?(File.expand_path('../tmp/', File.dirname(__FILE__))) FileUtils.rm_r(File.expand_path('../tmp/', File.dirname(__FILE__))) - end + end end - end diff --git a/spec/lib/order_spec.rb b/spec/lib/order_spec.rb index d15d823..8522c51 100644 --- a/spec/lib/order_spec.rb +++ b/spec/lib/order_spec.rb @@ -2,11 +2,10 @@ require 'spec_helper' RSpec.describe StraightServer::Order do - before(:each) do # clean the database - DB.run("DELETE FROM orders") - @gateway = double("Straight Gateway mock") + DB.run('DELETE FROM orders') + @gateway = double('Straight Gateway mock') allow(@gateway).to receive(:id).and_return(1) allow(@gateway).to receive(:active).and_return(true) allow(@gateway).to receive(:order_status_changed) @@ -18,7 +17,7 @@ @order = create(:order, gateway_id: @gateway.id) allow(@gateway).to receive(:fetch_transactions_for).with(anything).and_return([]) allow(@gateway).to receive(:order_status_changed).with(anything) - allow(@gateway).to receive(:sign_with_secret).with(anything).and_return("1", "2", "3") + allow(@gateway).to receive(:sign_with_secret).with(anything).and_return('1', '2', '3') allow(StraightServer::Gateway).to receive(:find_by_id).and_return(@gateway) websockets = {} @@ -28,16 +27,16 @@ StraightServer::GatewayModule.class_variable_set(:@@websockets, websockets) end - it "prepares data as http params" do - allow(@order).to receive(:tid).and_return("tid1") + it 'prepares data as http params' do + allow(@order).to receive(:tid).and_return('tid1') expect(@order.to_http_params).to eq("order_id=#{@order.id}&amount=10&amount_in_btc=#{@order.amount_in_btc(as: :string)}&amount_paid_in_btc=#{@order.amount_in_btc(field: @order.amount_paid, as: :string)}&status=#{@order.status}&address=#{@order.address}&tid=tid1&keychain_id=#{@order.keychain_id}&last_keychain_id=#{@order.gateway.last_keychain_id}") end - it "generates a payment_id" do + it 'generates a payment_id' do expect(@order.payment_id).not_to be_nil end - it "starts a periodic status check but subtracts the time passed from order creation from the duration of the check" do + it 'starts a periodic status check but subtracts the time passed from order creation from the duration of the check' do expect(@order).to receive(:check_status_on_schedule).with(duration: 900) @order.start_periodic_status_check @@ -46,7 +45,7 @@ @order.start_periodic_status_check end - it "checks DB for a status update first if the respective option for the gateway is turned on" do + it 'checks DB for a status update first if the respective option for the gateway is turned on' do # allow(@order).to receive(:transaction).and_raise("Shouldn't ever be happening!") StraightServer::Config.check_order_status_in_db_first = true StraightServer::Order.where(id: @order.id).update(status: 2) @@ -56,7 +55,7 @@ expect(@order.status(reload: true)).to eq(2) end - it "updates order status when the time in which it expires passes (periodic status checks finish)" do + it 'updates order status when the time in which it expires passes (periodic status checks finish)' do allow(@order).to receive(:status=) do expect(@order).to receive(:status_changed?).and_return(true) expect(@order).to receive(:save) @@ -67,15 +66,15 @@ it "doesn't allow to create an order for inactive gateway" do allow(@gateway).to receive(:active).and_return(false) - expect( -> { create(:order, gateway_id: @gateway.id) }).to raise_exception(Sequel::ValidationFailed, "gateway is inactive, cannot create order for inactive gateway") + expect(-> { create(:order, gateway_id: @gateway.id) }).to raise_exception(Sequel::ValidationFailed, 'gateway is inactive, cannot create order for inactive gateway') end - it "adds exchange rate at the moment of purchase to the data hash" do + it 'adds exchange rate at the moment of purchase to the data hash' do order = create(:order, gateway_id: @gateway.id) - expect(order.data[:exchange_rate]).to eq({ price: 111, currency: 'USD' }) + expect(order.data[:exchange_rate]).to eq(price: 111, currency: 'USD') end - it "returns last_keychain_id for the gateway along with other order data" do + it 'returns last_keychain_id for the gateway along with other order data' do order = create(:order, gateway_id: @gateway.id) expect(order.to_h).to include(keychain_id: order.keychain_id, last_keychain_id: @gateway.last_keychain_id) end @@ -89,59 +88,53 @@ end end - describe "DB interaction" do - - it "saves a new order into the database" do - expect(DB[:orders][:keychain_id => @order.id]).not_to be_nil + describe 'DB interaction' do + it 'saves a new order into the database' do + expect(DB[:orders][keychain_id: @order.id]).not_to be_nil end - it "updates an existing order" do + it 'updates an existing order' do allow(@order).to receive(:gateway).and_return(@gateway) - expect(DB[:orders][:keychain_id => @order.id][:status]).to eq(0) + expect(DB[:orders][keychain_id: @order.id][:status]).to eq(0) @order.status = 1 @order.save - expect(DB[:orders][:keychain_id => @order.id][:status]).to eq(1) + expect(DB[:orders][keychain_id: @order.id][:status]).to eq(1) end - it "finds first order in the database by id" do + it 'finds first order in the database by id' do expect(StraightServer::Order.find(id: @order.id)).to equal_order(@order) end - it "finds first order in the database by keychain_id" do + it 'finds first order in the database by keychain_id' do expect(StraightServer::Order.find(keychain_id: @order.keychain_id)).to equal_order(@order) end - it "finds orders in the database by any conditions" do + it 'finds orders in the database by any conditions' do order1 = create(:order, gateway_id: @gateway.id) order2 = create(:order, gateway_id: @gateway.id) expect(StraightServer::Order.where(keychain_id: order1.keychain_id).first).to equal_order(order1) expect(StraightServer::Order.where(keychain_id: order2.keychain_id).first).to equal_order(order2) - expect(StraightServer::Order.where(keychain_id: order2.keychain_id+1).first).to be_nil - + expect(StraightServer::Order.where(keychain_id: order2.keychain_id + 1).first).to be_nil end - describe "with validations" do - + describe 'with validations' do it "doesn't save order if the order with the same id exists" do order = create(:order, gateway_id: @gateway.id) - expect( -> { create(:order, id: order.id, gateway_id: @gateway.id) }).to raise_error() + expect(-> { create(:order, id: order.id, gateway_id: @gateway.id) }).to raise_error end it "doesn't save order if the amount is invalid" do - expect( -> { create(:order, amount: 0) }).to raise_error() + expect(-> { create(:order, amount: 0) }).to raise_error end it "doesn't save order if gateway_id is invalid" do - expect( -> { create(:order, gateway_id: 0) }).to raise_error() + expect(-> { create(:order, gateway_id: 0) }).to raise_error end it "doesn't save order if description is too long" do - expect( -> { create(:order, description: ("text" * 100)) }).to raise_error() + expect(-> { create(:order, description: ('text' * 100)) }).to raise_error end - end - end - end diff --git a/spec/lib/orders_controller_spec.rb b/spec/lib/orders_controller_spec.rb index 2abb4b2..d91606f 100644 --- a/spec/lib/orders_controller_spec.rb +++ b/spec/lib/orders_controller_spec.rb @@ -1,188 +1,183 @@ require 'spec_helper' RSpec.describe StraightServer::OrdersController do - before(:each) do - DB.run("DELETE FROM orders") + DB.run('DELETE FROM orders') @gateway = gateway = StraightServer::Gateway.find_by_id(2) - allow(gateway).to receive_message_chain("address_provider.takes_fees?").and_return(false) - allow(gateway).to receive_message_chain("address_provider.new_address").and_return("address#{gateway.last_keychain_id+1}") + allow(gateway).to receive_message_chain('address_provider.takes_fees?').and_return(false) + allow(gateway).to receive_message_chain('address_provider.new_address').and_return("address#{gateway.last_keychain_id + 1}") allow(gateway).to receive(:fetch_transactions_for).with(anything).and_return([]) allow(gateway).to receive(:send_callback_http_request) end - describe "create action" do - - it "creates an order and renders its attrs in json" do + describe 'create action' do + it 'creates an order and renders its attrs in json' do allow(StraightServer::Thread).to receive(:new) # ignore periodic status checks, we're not testing it here - send_request "POST", '/gateways/2/orders', amount: 10 - expect(response).to render_json_with(status: 0, amount: 10, address: "address1", tid: nil, id: :anything, keychain_id: @gateway.last_keychain_id, last_keychain_id: @gateway.last_keychain_id) + send_request 'POST', '/gateways/2/orders', amount: 10 + expect(response).to render_json_with(status: 0, amount: 10, address: 'address1', tid: nil, id: :anything, keychain_id: @gateway.last_keychain_id, last_keychain_id: @gateway.last_keychain_id) end - it "renders 409 error when an order cannot be created due to invalid amount" do - send_request "POST", '/gateways/2/orders', amount: 0 + it 'renders 409 error when an order cannot be created due to invalid amount' do + send_request 'POST', '/gateways/2/orders', amount: 0 expect(response[0]).to eq(409) - expect(response[2]).to eq("Invalid order: amount cannot be nil and should be more than 0") + expect(response[2]).to eq('Invalid order: amount cannot be nil and should be more than 0') end - it "renders 409 error when an order cannot be created due to other validation errors" do - send_request "POST", '/gateways/2/orders', amount: 1, description: 'A'*256 + it 'renders 409 error when an order cannot be created due to other validation errors' do + send_request 'POST', '/gateways/2/orders', amount: 1, description: 'A' * 256 expect(response[0]).to eq(409) - expect(response[2]).to eq("Invalid order: description should be shorter than 256 characters") + expect(response[2]).to eq('Invalid order: description should be shorter than 256 characters') end - it "starts tracking the order status in a separate thread" do - order_mock = double("order mock") + it 'starts tracking the order status in a separate thread' do + order_mock = double('order mock') expect(order_mock).to receive(:start_periodic_status_check) expect(order_mock).to receive(:payment_id).and_return('blabla') - allow(order_mock).to receive(:to_h).and_return({}) - expect(@gateway).to receive(:create_order).and_return(order_mock) - send_request "POST", '/gateways/2/orders', amount: 10 + allow(order_mock).to receive(:to_h).and_return({}) + expect(@gateway).to receive(:create_order).and_return(order_mock) + send_request 'POST', '/gateways/2/orders', amount: 10 end - it "passes data and callback_data param to Order which then saves it serialized" do + it 'passes data and callback_data param to Order which then saves it serialized' do allow(StraightServer::Thread).to receive(:new) # ignore periodic status checks, we're not testing it here - send_request "POST", '/gateways/2/orders', amount: 10, data: { hello: 'world' }, callback_data: 'some random data' + send_request 'POST', '/gateways/2/orders', amount: 10, data: { hello: 'world' }, callback_data: 'some random data' expect(StraightServer::Order.last.data.hello).to eq('world') expect(StraightServer::Order.last.callback_data).to eq('some random data') end - it "renders 503 page when the gateway is inactive" do + it 'renders 503 page when the gateway is inactive' do @gateway.active = false - send_request "POST", '/gateways/2/orders', amount: 1 + send_request 'POST', '/gateways/2/orders', amount: 1 expect(response[0]).to eq(503) - expect(response[2]).to eq("The gateway is inactive, you cannot create order with it") + expect(response[2]).to eq('The gateway is inactive, you cannot create order with it') @gateway.active = true end - it "finds gateway using hashed_id" do + it 'finds gateway using hashed_id' do allow(StraightServer::Thread).to receive(:new) - send_request "POST", "/gateways/#{@gateway.id}/orders", amount: 10 + send_request 'POST', "/gateways/#{@gateway.id}/orders", amount: 10 end - it "warns about a deprecated order_id param" do - send_request "POST", "/gateways/#{@gateway.id}/orders", amount: 10, order_id: 1 - expect(response[2]).to eq("Error: order_id is no longer a valid param. Use keychain_id instead and consult the documentation.") + it 'warns about a deprecated order_id param' do + send_request 'POST', "/gateways/#{@gateway.id}/orders", amount: 10, order_id: 1 + expect(response[2]).to eq('Error: order_id is no longer a valid param. Use keychain_id instead and consult the documentation.') end it 'limits creation of orders without signature' do new_config = StraightServer::Config.clone - new_config.throttle = {requests_limit: 1, period: 1} + new_config.throttle = { requests_limit: 1, period: 1 } stub_const 'StraightServer::Config', new_config allow(StraightServer::Thread).to receive(:new) - send_request "POST", '/gateways/2/orders', amount: 10 - expect(response).to render_json_with(status: 0, amount: 10, address: "address1", tid: nil, id: :anything, keychain_id: @gateway.last_keychain_id, last_keychain_id: @gateway.last_keychain_id) - send_request "POST", '/gateways/2/orders', amount: 10 - expect(response).to eq [429, {}, "Too many requests, please try again later"] + send_request 'POST', '/gateways/2/orders', amount: 10 + expect(response).to render_json_with(status: 0, amount: 10, address: 'address1', tid: nil, id: :anything, keychain_id: @gateway.last_keychain_id, last_keychain_id: @gateway.last_keychain_id) + send_request 'POST', '/gateways/2/orders', amount: 10 + expect(response).to eq [429, {}, 'Too many requests, please try again later'] @gateway1 = StraightServer::Gateway.find_by_id(1) @gateway1.check_signature = true 5.times do |i| i += 1 - send_signed_request @gateway1, "POST", '/gateways/1/orders', amount: 10, keychain_id: i + send_signed_request @gateway1, 'POST', '/gateways/1/orders', amount: 10, keychain_id: i expect(response[0]).to eq 200 expect(response).to render_json_with(status: 0, amount: 10, tid: nil, id: :anything, keychain_id: i, last_keychain_id: i) end end - it "warns you about the use of callback_data instead of data" do + it 'warns you about the use of callback_data instead of data' do allow(StraightServer::Thread).to receive(:new) - send_request "POST", '/gateways/2/orders', amount: 10, data: "I meant this to be callback_data" - expect(response).to render_json_with(WARNING: "Maybe you meant to use callback_data? The API has changed now. Consult the documentation.") + send_request 'POST', '/gateways/2/orders', amount: 10, data: 'I meant this to be callback_data' + expect(response).to render_json_with(WARNING: 'Maybe you meant to use callback_data? The API has changed now. Consult the documentation.') end end - describe "show action" do - + describe 'show action' do before(:each) do @order_mock = double('order mock') allow(@order_mock).to receive(:status).and_return(2) - allow(@order_mock).to receive(:to_json).and_return("order json mock") + allow(@order_mock).to receive(:to_json).and_return('order json mock') end - it "renders json info about an order if it is found" do + it 'renders json info about an order if it is found' do allow(@order_mock).to receive(:status_changed?).and_return(false) expect(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock) - send_request "GET", '/gateways/2/orders/1' - expect(response).to eq([200, {}, "order json mock"]) + send_request 'GET', '/gateways/2/orders/1' + expect(response).to eq([200, {}, 'order json mock']) end - it "saves an order if status is updated" do + it 'saves an order if status is updated' do allow(@order_mock).to receive(:status_changed?).and_return(true) expect(@order_mock).to receive(:save) expect(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock) - send_request "GET", '/gateways/2/orders/1' - expect(response).to eq([200, {}, "order json mock"]) + send_request 'GET', '/gateways/2/orders/1' + expect(response).to eq([200, {}, 'order json mock']) end - it "renders 404 if order is not found" do + it 'renders 404 if order is not found' do expect(StraightServer::Order).to receive(:[]).with(1).and_return(nil) - send_request "GET", '/gateways/2/orders/1' - expect(response).to eq([404, {}, "GET /gateways/2/orders/1 Not found"]) + send_request 'GET', '/gateways/2/orders/1' + expect(response).to eq([404, {}, 'GET /gateways/2/orders/1 Not found']) end - it "finds order by payment_id" do + it 'finds order by payment_id' do allow(@order_mock).to receive(:status_changed?).and_return(false) - expect(StraightServer::Order).to receive(:[]).with(:payment_id => 'payment_id').and_return(@order_mock) - send_request "GET", '/gateways/2/orders/payment_id' - expect(response).to eq([200, {}, "order json mock"]) + expect(StraightServer::Order).to receive(:[]).with(payment_id: 'payment_id').and_return(@order_mock) + send_request 'GET', '/gateways/2/orders/payment_id' + expect(response).to eq([200, {}, 'order json mock']) end - end - describe "websocket action" do - + describe 'websocket action' do before(:each) do - StraightServer::GatewayModule.class_variable_set(:@@websockets, { @gateway.id => {} }) - @ws_mock = double("websocket mock") - @order_mock = double("order mock") - allow(@ws_mock).to receive(:rack_response).and_return("ws rack response") + StraightServer::GatewayModule.class_variable_set(:@@websockets, @gateway.id => {}) + @ws_mock = double('websocket mock') + @order_mock = double('order mock') + allow(@ws_mock).to receive(:rack_response).and_return('ws rack response') [:id, :gateway=, :save, :to_h, :id=].each { |m| allow(@order_mock).to receive(m) } allow(@ws_mock).to receive(:on) allow(Faye::WebSocket).to receive(:new).and_return(@ws_mock) end - it "returns a websocket connection" do + it 'returns a websocket connection' do allow(@order_mock).to receive(:status).and_return(0) allow(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock) - send_request "GET", '/gateways/2/orders/1/websocket' - expect(response).to eq("ws rack response") + send_request 'GET', '/gateways/2/orders/1/websocket' + expect(response).to eq('ws rack response') end - it "returns 403 when socket already exists" do + it 'returns 403 when socket already exists' do allow(@order_mock).to receive(:status).and_return(0) allow(StraightServer::Order).to receive(:[]).with(1).twice.and_return(@order_mock) - send_request "GET", '/gateways/2/orders/1/websocket' - send_request "GET", '/gateways/2/orders/1/websocket' - expect(response).to eq([403, {}, "Someone is already listening to that order"]) + send_request 'GET', '/gateways/2/orders/1/websocket' + send_request 'GET', '/gateways/2/orders/1/websocket' + expect(response).to eq([403, {}, 'Someone is already listening to that order']) end - it "returns 403 when order has is completed (status > 1 )" do + it 'returns 403 when order has is completed (status > 1 )' do allow(@order_mock).to receive(:status).and_return(2) allow(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock) - send_request "GET", '/gateways/2/orders/1/websocket' - expect(response).to eq([403, {}, "You cannot listen to this order because it is completed (status > 1)"]) + send_request 'GET', '/gateways/2/orders/1/websocket' + expect(response).to eq([403, {}, 'You cannot listen to this order because it is completed (status > 1)']) end - it "finds order by payment_id" do + it 'finds order by payment_id' do allow(@order_mock).to receive(:status).and_return(0) - expect(StraightServer::Order).to receive(:[]).with(:payment_id => 'payment_id').and_return(@order_mock) - send_request "GET", '/gateways/2/orders/payment_id/websocket' - expect(response).to eq("ws rack response") + expect(StraightServer::Order).to receive(:[]).with(payment_id: 'payment_id').and_return(@order_mock) + send_request 'GET', '/gateways/2/orders/payment_id/websocket' + expect(response).to eq('ws rack response') end end - describe "cancel action" do - it "cancels new order" do + describe 'cancel action' do + it 'cancels new order' do allow(StraightServer::Thread).to receive(:new) - send_request "POST", '/gateways/2/orders', amount: 1 + send_request 'POST', '/gateways/2/orders', amount: 1 payment_id = JSON.parse(response[2])['payment_id'] - send_request "POST", "/gateways/2/orders/#{payment_id}/cancel" + send_request 'POST', "/gateways/2/orders/#{payment_id}/cancel" expect(response[0]).to eq 200 end - it "requires signature to cancel signed order" do + it 'requires signature to cancel signed order' do allow(StraightServer::Thread).to receive(:new) @gateway1 = StraightServer::Gateway.find_by_id(1) @gateway1.check_signature = true @@ -192,21 +187,21 @@ allow(@order_mock).to receive(:cancelable?).and_return(true) expect(@order_mock).to receive(:cancel) allow(StraightServer::Order).to receive(:[]).and_return(@order_mock) - send_request "POST", "/gateways/1/orders/payment_id/cancel" + send_request 'POST', '/gateways/1/orders/payment_id/cancel' expect(response).to eq [409, {}, 'X-Nonce is invalid: nil'] - send_signed_request @gateway1, "POST", "/gateways/1/orders/payment_id/cancel" + send_signed_request @gateway1, 'POST', '/gateways/1/orders/payment_id/cancel' expect(response[0]).to eq 200 end - it "do not cancel orders with status != new" do + it 'do not cancel orders with status != new' do @order_mock = double('order mock') allow(@order_mock).to receive(:status).with(reload: true) allow(@order_mock).to receive(:status_changed?).and_return(true) expect(@order_mock).to receive(:save) allow(@order_mock).to receive(:cancelable?).and_return(false) allow(StraightServer::Order).to receive(:[]).and_return(@order_mock) - send_request "POST", "/gateways/2/orders/payment_id/cancel" - expect(response).to eq [409, {}, "Order is not cancelable"] + send_request 'POST', '/gateways/2/orders/payment_id/cancel' + expect(response).to eq [409, {}, 'Order is not cancelable'] end end @@ -215,16 +210,16 @@ @gateway = StraightServer::Gateway.find_by_id(1) @gateway.last_keychain_id = lk_id @gateway.save - send_request "GET", '/gateway/1/last_keychain_id' + send_request 'GET', '/gateway/1/last_keychain_id' expect(response).to render_json_with(gateway_id: @gateway.id, last_keychain_id: lk_id) end - def send_request(method, path, params={}) + def send_request(method, path, params = {}) env = Hashie::Mash.new('REQUEST_METHOD' => method, 'REQUEST_PATH' => path, 'params' => params) @controller = StraightServer::OrdersController.new(env) end - def send_signed_request(gateway, method, path, params={}) + def send_signed_request(gateway, method, path, params = {}) env = Hashie::Mash.new('REQUEST_METHOD' => method, 'REQUEST_PATH' => path, 'params' => params, 'HTTP_X_NONCE' => (Time.now.to_f * 1e6).to_i) env['HTTP_X_SIGNATURE'] = StraightServer::SignatureValidator.new(gateway, env).signature @controller = StraightServer::OrdersController.new(env) @@ -233,5 +228,4 @@ def send_signed_request(gateway, method, path, params={}) def response @controller.response end - end diff --git a/spec/lib/signature_validator_spec.rb b/spec/lib/signature_validator_spec.rb index ef9f50f..58e8766 100644 --- a/spec/lib/signature_validator_spec.rb +++ b/spec/lib/signature_validator_spec.rb @@ -1,22 +1,19 @@ require 'spec_helper' RSpec.describe StraightServer::SignatureValidator do - it 'calculates signature' do expect(described_class.signature(nonce: '123', body: '', method: 'GET', request_uri: '/somewhere', secret: 'gateway_secret')).to eq 'ZSWEKzuWy6QWCc05I+t4QYQhUtkeogkW7rCwieQvy/56Y+bVwxGGKB3yNQg1XL2LmtuNNwv2SXUxjlFEP7+0+A==' expect(described_class.signature(nonce: '123', body: '', method: 'GET', request_uri: '/somewhere', secret: 'gateway-secret')).to eq 'nYLq7IXlgw5FAsXGc0+JoXmfHBEwl7zwVQhsix+FraIIFsPeGYnQ/22wkjPAwwyu0GoYEbM6gmN+sxEzciNkFg==' - expect(described_class.signature(nonce: '12345', body: 'text' * 10000, method: 'POST', request_uri: '/somewhere', secret: 'gateway_secret')).to eq 'F0GsyqPkxDgmqdTomIGVIRQ/ik2GiZtXy1GVNx0j+UDUL8VS496HsbcOlyUocKUM0fU96KkjhrpUh0LC29AXyQ==' + expect(described_class.signature(nonce: '12345', body: 'text' * 10_000, method: 'POST', request_uri: '/somewhere', secret: 'gateway_secret')).to eq 'F0GsyqPkxDgmqdTomIGVIRQ/ik2GiZtXy1GVNx0j+UDUL8VS496HsbcOlyUocKUM0fU96KkjhrpUh0LC29AXyQ==' end it 'validates signature' do @validator = described_class.new( Struct.new(:secret).new('abc'), - { - 'HTTP_X_NONCE' => '1', - 'rack.input' => 'request body', - 'REQUEST_METHOD' => 'POST', - 'REQUEST_URI' => '/gateway/123/orders', - } + 'HTTP_X_NONCE' => '1', + 'rack.input' => 'request body', + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/gateway/123/orders' ) expect(@validator.env['HTTP_X_SIGNATURE'] = @validator.signature).to eq '1EtQNASecMF85tyag+pSSdF2yxLfy3xCddM2ZGA86M8OTxleEixBnbOeMEBp37Ke5+7jWQm+Gpx95y6MZiW6wQ==' expect(@validator.valid_signature?).to eq true @@ -25,7 +22,7 @@ it 'validates nonce' do @validator = described_class.new( Struct.new(:id).new(1), - {'HTTP_X_NONCE' => '100500'} + 'HTTP_X_NONCE' => '100500' ) expect(@validator.valid_nonce?).to eq true expect(@validator.valid_nonce?).to eq false @@ -38,30 +35,28 @@ it 'validates nonce in a thread-safe way' do # TODO: test on real concurrency (JRuby?) - @validator = described_class.new( + @validator = described_class.new( Struct.new(:id).new(2), - {'HTTP_X_NONCE' => '100500'} + 'HTTP_X_NONCE' => '100500' ) thread_number = 100 @threads = thread_number.times.map do |i| Thread.new do - sleep (thread_number - i) / 10000.0 + sleep (thread_number - i) / 10_000.0 Thread.current[:result] = @validator.valid_nonce? end end @threads.each(&:join) - expect(@threads.select { |thread| thread[:result] }.size).to eq 1 + expect(@threads.count { |thread| thread[:result] }).to eq 1 end it 'raises exceptions if invalid' do - @validator = described_class.new( + @validator = described_class.new( Struct.new(:id, :secret).new(3, 'abc'), - { - 'HTTP_X_NONCE' => '1', - 'rack.input' => 'request body', - 'REQUEST_METHOD' => 'POST', - 'REQUEST_PATH' => '/gateway/123/orders', - } + 'HTTP_X_NONCE' => '1', + 'rack.input' => 'request body', + 'REQUEST_METHOD' => 'POST', + 'REQUEST_PATH' => '/gateway/123/orders' ) @validator.env['HTTP_X_SIGNATURE'] = @validator.signature expect(@validator.validate!).to eq true diff --git a/spec/lib/thread_spec.rb b/spec/lib/thread_spec.rb index e077b95..d89edaf 100644 --- a/spec/lib/thread_spec.rb +++ b/spec/lib/thread_spec.rb @@ -1,14 +1,13 @@ require 'spec_helper' RSpec.describe StraightServer::Thread do - it 'labels threads' do - thread = described_class.new(label: 'payment_id'){} + thread = described_class.new(label: 'payment_id') {} expect(thread[:label]).to eq 'payment_id' end it 'sets and clears interruption flag' do - thread = described_class.new(label: 'payment_id'){} + thread = described_class.new(label: 'payment_id') {} described_class.interrupt(label: 'payment_id') expect(described_class.interrupted?(thread: thread)).to eq true expect(described_class.interrupted?(thread: thread)).to eq false diff --git a/spec/lib/throttle_spec.rb b/spec/lib/throttle_spec.rb index 7d7d564..29b2ef5 100644 --- a/spec/lib/throttle_spec.rb +++ b/spec/lib/throttle_spec.rb @@ -2,10 +2,9 @@ require 'timecop' RSpec.describe StraightServer::Throttler do - it 'throttles requests' do new_config = StraightServer::Config.clone - new_config.throttle = {requests_limit: 1, period: 1} + new_config.throttle = { requests_limit: 1, period: 1 } stub_const 'StraightServer::Config', new_config throttler1 = described_class.new(1) throttler2 = described_class.new(2) @@ -30,7 +29,7 @@ it 'bans by ip' do new_config = StraightServer::Config.clone - new_config.throttle = {requests_limit: 3, period: 1, ip_ban_duration: 30} + new_config.throttle = { requests_limit: 3, period: 1, ip_ban_duration: 30 } stub_const 'StraightServer::Config', new_config throttler1 = described_class.new(1) throttler2 = described_class.new(2) diff --git a/spec/lib/utils/hash_string_to_sym_keys.rb b/spec/lib/utils/hash_string_to_sym_keys.rb index eacfb33..081c2c8 100644 --- a/spec/lib/utils/hash_string_to_sym_keys.rb +++ b/spec/lib/utils/hash_string_to_sym_keys.rb @@ -1,18 +1,16 @@ -require_relative "../../../lib/straight-server/utils/hash_string_to_sym_keys" +require_relative '../../../lib/straight-server/utils/hash_string_to_sym_keys' describe Hash do - - it "converts string keys to symbols in a hash" do + it 'converts string keys to symbols in a hash' do hash = { 'hello' => 'world', 'hello?' => 'world!' } hash.keys_to_sym! - expect(hash).to include(hello: 'world', :hello? => 'world!') + expect(hash).to include(hello: 'world', hello?: 'world!') expect(hash).not_to include('hello' => 'world', 'hello?' => 'world!') end - + it "doesn't convert string keys that have spaces or other unintended chars in them" do hash = { 'hello' => 'world', 'hello hi' => 'world planet' } hash.keys_to_sym! expect(hash).to include(hello: 'world', 'hello hi' => 'world planet') end - end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ab032fb..c1df22d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,8 +27,8 @@ require_relative 'support/custom_matchers' -require "factory_girl" -require_relative "factories" +require 'factory_girl' +require_relative 'factories' require 'webmock/rspec' @@ -39,22 +39,21 @@ class StraightServer::Thread def self.new(label: nil, &block) block.call - {label: label} + { label: label } end end RSpec.configure do |config| - config.include FactoryGirl::Syntax::Methods config.before(:suite) do - StraightServer.db_connection = DB #use a memory DB + StraightServer.db_connection = DB # use a memory DB end - config.before(:each) do |spec| + config.before(:each) do |_spec| # puts spec.description DB[:orders].delete - logger_mock = double("logger mock") + logger_mock = double('logger mock') [:debug, :info, :warn, :fatal, :unknown, :blank_lines].each do |e| allow(logger_mock).to receive(e) end @@ -71,13 +70,11 @@ def self.new(label: nil, &block) Redis.current.keys("#{StraightServer::Config.redis[:prefix]}*").each do |k| Redis.current.del k end - end config.after(:all) do - ["default_last_keychain_id", "second_gateway_last_keychain_id", "default_order_counters.yml"].each do |f| - FileUtils.rm "#{ENV['HOME']}/.straight/#{f}" if File.exists?("#{ENV['HOME']}/.straight/#{f}") + ['default_last_keychain_id', 'second_gateway_last_keychain_id', 'default_order_counters.yml'].each do |f| + FileUtils.rm "#{ENV['HOME']}/.straight/#{f}" if File.exist?("#{ENV['HOME']}/.straight/#{f}") end end - end diff --git a/spec/support/custom_matchers.rb b/spec/support/custom_matchers.rb index f3256f4..046954a 100644 --- a/spec/support/custom_matchers.rb +++ b/spec/support/custom_matchers.rb @@ -1,32 +1,29 @@ RSpec::Matchers.define :equal_order do |expected| match do |actual| true - actual.address == expected.address && - actual.status == expected.status && - actual.keychain_id == expected.keychain_id && - actual.amount == expected.amount && - actual.gateway_id == expected.gateway_id && - actual.id == expected.id + actual.address == expected.address && + actual.status == expected.status && + actual.keychain_id == expected.keychain_id && + actual.amount == expected.amount && actual.gateway_id == expected.gateway_id && actual.id == expected.id end diffable end RSpec::Matchers.define :render_json_with do |hash| - match do |r| json_response = JSON.parse(r[2]) check_one_dimensional_hash(hash, json_response) end def check_one_dimensional_hash(hash, json_response) - hash.each do |k,v| + hash.each do |k, v| if v == :anything expect(json_response[k.to_s]).to_not be_nil - elsif v == nil + elsif v.nil? expect(json_response[k.to_s]).to be_nil - elsif v.kind_of?(Hash) - expect(json_response[k.to_s].kind_of?(Hash)).to be_truthy + elsif v.is_a?(Hash) + expect(json_response[k.to_s].is_a?(Hash)).to be_truthy check_one_dimensional_hash(v, json_response[k.to_s]) else expect(json_response[k.to_s]).to eq(v) @@ -37,8 +34,7 @@ def check_one_dimensional_hash(hash, json_response) failure_message do |actual| "expected that it had:\n\n\t\t#{hash},\n\nbut instead it had:\n\n\t\t#{JSON.parse(actual[2])}" end - failure_message_when_negated do |actual| + failure_message_when_negated do |_actual| "expected that it wouldn't render #{hash.inspect} but it did!" end - end diff --git a/straight-server.gemspec b/straight-server.gemspec index 5f3c3e7..061993f 100644 --- a/straight-server.gemspec +++ b/straight-server.gemspec @@ -5,149 +5,148 @@ # stub: straight-server 1.0.0 ruby lib Gem::Specification.new do |s| - s.name = "straight-server" - s.version = "1.0.0" + s.name = 'straight-server' + s.version = '1.0.0' - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.require_paths = ["lib"] - s.authors = ["Roman Snitko"] - s.date = "2015-07-10" - s.description = "Accepts orders via http, returns payment info via http or streams updates via websockets, stores orders in a DB" - s.email = "roman.snitko@gmail.com" - s.executables = ["straight-console", "straight-server", "straight-server-benchmark"] + s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version= + s.require_paths = ['lib'] + s.authors = ['Roman Snitko'] + s.date = '2015-07-10' + s.description = 'Accepts orders via http, returns payment info via http or streams updates via websockets, stores orders in a DB' + s.email = 'roman.snitko@gmail.com' + s.executables = ['straight-console', 'straight-server', 'straight-server-benchmark'] s.extra_rdoc_files = [ - "LICENSE.txt", - "README.md" + 'LICENSE.txt', + 'README.md' ] s.files = [ - ".document", - ".rspec", - ".travis.yml", - "Gemfile", - "Gemfile.lock", - "Gemfile.travis", - "LICENSE.txt", - "README.md", - "Rakefile", - "VERSION", - "benchmark/addons.yml", - "benchmark/config.yml", - "benchmark/default_last_keychain_id", - "benchmark/server_secret", - "bin/straight-console", - "bin/straight-server", - "bin/straight-server-benchmark", - "db/migrations/001_create_orders.rb", - "db/migrations/002_create_gateways.rb", - "db/migrations/003_add_payment_id_to_orders.rb", - "db/migrations/004_add_description_to_orders.rb", - "db/migrations/005_add_orders_expiration_period_to_gateways.rb", - "db/migrations/006_add_check_order_status_in_db_first_to_gateways.rb", - "db/migrations/007_add_active_switcher_to_gateways.rb", - "db/migrations/008_add_order_counters_to_gateways.rb", - "db/migrations/009_add_hashed_id_to_gateways.rb", - "db/migrations/010_add_address_reusability_orders.rb", - "db/migrations/011_add_callback_data_to_orders.rb", - "db/migrations/012_add_address_provider.rb", - "db/migrations/013_add_address_derivation_scheme.rb", - "db/migrations/014_pubkey_null_address_provider_not_null.rb", - "db/migrations/015_add_amount_paid_to_orders.rb", - "db/migrations/016_add_new_params_to_orders.rb", - "db/migrations/017_add_test_mode_to_gateways.rb", - "db/migrations/018_add_test_keychain_id_to_gateways.rb", - "db/migrations/019_add_test_pubkey_to_gateways.rb", - "db/migrations/020_add_test_mode_to_orders.rb", - "db/schema.rb", - "examples/client/client.dart", - "examples/client/client.html", - "examples/client/client.js", - "lib/straight-server.rb", - "lib/straight-server/config.rb", - "lib/straight-server/gateway.rb", - "lib/straight-server/initializer.rb", - "lib/straight-server/logger.rb", - "lib/straight-server/order.rb", - "lib/straight-server/orders_controller.rb", - "lib/straight-server/random_string.rb", - "lib/straight-server/server.rb", - "lib/straight-server/signature_validator.rb", - "lib/straight-server/thread.rb", - "lib/straight-server/throttler.rb", - "lib/straight-server/utils/hash_string_to_sym_keys.rb", - "lib/tasks/db.rake", - "spec/.straight/config.yml", - "spec/.straight/server_secret", - "spec/factories.rb", - "spec/fixtures/addons.yml", - "spec/fixtures/test_addon.rb", - "spec/lib/gateway_spec.rb", - "spec/lib/initializer_spec.rb", - "spec/lib/order_spec.rb", - "spec/lib/orders_controller_spec.rb", - "spec/lib/signature_validator_spec.rb", - "spec/lib/thread_spec.rb", - "spec/lib/throttle_spec.rb", - "spec/lib/utils/hash_string_to_sym_keys.rb", - "spec/spec_helper.rb", - "spec/support/custom_matchers.rb", - "straight-server.gemspec", - "templates/addons.yml", - "templates/config.yml" + '.document', + '.rspec', + '.travis.yml', + 'Gemfile', + 'Gemfile.lock', + 'Gemfile.travis', + 'LICENSE.txt', + 'README.md', + 'Rakefile', + 'VERSION', + 'benchmark/addons.yml', + 'benchmark/config.yml', + 'benchmark/default_last_keychain_id', + 'benchmark/server_secret', + 'bin/straight-console', + 'bin/straight-server', + 'bin/straight-server-benchmark', + 'db/migrations/001_create_orders.rb', + 'db/migrations/002_create_gateways.rb', + 'db/migrations/003_add_payment_id_to_orders.rb', + 'db/migrations/004_add_description_to_orders.rb', + 'db/migrations/005_add_orders_expiration_period_to_gateways.rb', + 'db/migrations/006_add_check_order_status_in_db_first_to_gateways.rb', + 'db/migrations/007_add_active_switcher_to_gateways.rb', + 'db/migrations/008_add_order_counters_to_gateways.rb', + 'db/migrations/009_add_hashed_id_to_gateways.rb', + 'db/migrations/010_add_address_reusability_orders.rb', + 'db/migrations/011_add_callback_data_to_orders.rb', + 'db/migrations/012_add_address_provider.rb', + 'db/migrations/013_add_address_derivation_scheme.rb', + 'db/migrations/014_pubkey_null_address_provider_not_null.rb', + 'db/migrations/015_add_amount_paid_to_orders.rb', + 'db/migrations/016_add_new_params_to_orders.rb', + 'db/migrations/017_add_test_mode_to_gateways.rb', + 'db/migrations/018_add_test_keychain_id_to_gateways.rb', + 'db/migrations/019_add_test_pubkey_to_gateways.rb', + 'db/migrations/020_add_test_mode_to_orders.rb', + 'db/schema.rb', + 'examples/client/client.dart', + 'examples/client/client.html', + 'examples/client/client.js', + 'lib/straight-server.rb', + 'lib/straight-server/config.rb', + 'lib/straight-server/gateway.rb', + 'lib/straight-server/initializer.rb', + 'lib/straight-server/logger.rb', + 'lib/straight-server/order.rb', + 'lib/straight-server/orders_controller.rb', + 'lib/straight-server/random_string.rb', + 'lib/straight-server/server.rb', + 'lib/straight-server/signature_validator.rb', + 'lib/straight-server/thread.rb', + 'lib/straight-server/throttler.rb', + 'lib/straight-server/utils/hash_string_to_sym_keys.rb', + 'lib/tasks/db.rake', + 'spec/.straight/config.yml', + 'spec/.straight/server_secret', + 'spec/factories.rb', + 'spec/fixtures/addons.yml', + 'spec/fixtures/test_addon.rb', + 'spec/lib/gateway_spec.rb', + 'spec/lib/initializer_spec.rb', + 'spec/lib/order_spec.rb', + 'spec/lib/orders_controller_spec.rb', + 'spec/lib/signature_validator_spec.rb', + 'spec/lib/thread_spec.rb', + 'spec/lib/throttle_spec.rb', + 'spec/lib/utils/hash_string_to_sym_keys.rb', + 'spec/spec_helper.rb', + 'spec/support/custom_matchers.rb', + 'straight-server.gemspec', + 'templates/addons.yml', + 'templates/config.yml' ] - s.homepage = "http://github.com/snitko/straight-server" - s.licenses = ["MIT"] - s.rubygems_version = "2.4.3" - s.summary = "A Bitcoin payment gateway server: a state server for the stateless Straight library" + s.homepage = 'http://github.com/snitko/straight-server' + s.licenses = ['MIT'] + s.rubygems_version = '2.4.3' + s.summary = 'A Bitcoin payment gateway server: a state server for the stateless Straight library' - if s.respond_to? :specification_version then + if s.respond_to? :specification_version s.specification_version = 4 - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["= 1.0.0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, ["= 0.1.5"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 1.0"]) - s.add_development_dependency(%q, ["~> 2.0.1"]) - s.add_development_dependency(%q, ["= 0.11.3"]) + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') + s.add_runtime_dependency('straight', ['= 1.0.0']) + s.add_runtime_dependency('satoshi-unit', ['>= 0']) + s.add_runtime_dependency('goliath', ['>= 0']) + s.add_runtime_dependency('faye-websocket', ['>= 0']) + s.add_runtime_dependency('sequel', ['>= 0']) + s.add_runtime_dependency('logmaster', ['= 0.1.5']) + s.add_runtime_dependency('ruby-hmac', ['>= 0']) + s.add_runtime_dependency('httparty', ['>= 0']) + s.add_runtime_dependency('redis', ['>= 0']) + s.add_runtime_dependency('btcruby', ['>= 0']) + s.add_development_dependency('byebug', ['>= 0']) + s.add_development_dependency('bundler', ['~> 1.0']) + s.add_development_dependency('jeweler', ['~> 2.0.1']) + s.add_development_dependency('github_api', ['= 0.11.3']) else - s.add_dependency(%q, ["= 1.0.0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["= 0.1.5"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 2.0.1"]) - s.add_dependency(%q, ["= 0.11.3"]) + s.add_dependency('straight', ['= 1.0.0']) + s.add_dependency('satoshi-unit', ['>= 0']) + s.add_dependency('goliath', ['>= 0']) + s.add_dependency('faye-websocket', ['>= 0']) + s.add_dependency('sequel', ['>= 0']) + s.add_dependency('logmaster', ['= 0.1.5']) + s.add_dependency('ruby-hmac', ['>= 0']) + s.add_dependency('httparty', ['>= 0']) + s.add_dependency('redis', ['>= 0']) + s.add_dependency('btcruby', ['>= 0']) + s.add_dependency('byebug', ['>= 0']) + s.add_dependency('bundler', ['~> 1.0']) + s.add_dependency('jeweler', ['~> 2.0.1']) + s.add_dependency('github_api', ['= 0.11.3']) end else - s.add_dependency(%q, ["= 1.0.0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["= 0.1.5"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 2.0.1"]) - s.add_dependency(%q, ["= 0.11.3"]) + s.add_dependency('straight', ['= 1.0.0']) + s.add_dependency('satoshi-unit', ['>= 0']) + s.add_dependency('goliath', ['>= 0']) + s.add_dependency('faye-websocket', ['>= 0']) + s.add_dependency('sequel', ['>= 0']) + s.add_dependency('logmaster', ['= 0.1.5']) + s.add_dependency('ruby-hmac', ['>= 0']) + s.add_dependency('httparty', ['>= 0']) + s.add_dependency('redis', ['>= 0']) + s.add_dependency('btcruby', ['>= 0']) + s.add_dependency('byebug', ['>= 0']) + s.add_dependency('bundler', ['~> 1.0']) + s.add_dependency('jeweler', ['~> 2.0.1']) + s.add_dependency('github_api', ['= 0.11.3']) end end -