diff --git a/Gemfile.lock b/Gemfile.lock index bffd2b3..9a4f80c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,16 +1,10 @@ PATH remote: . specs: - peatio (0.6.2) + peatio-core (0.6.2) activemodel (> 5.2, <= 6.0.0) - amqp bunny - clamp - em-websocket - eventmachine jwt - mysql2 - prometheus-client thin GEM @@ -25,32 +19,17 @@ GEM tzinfo (~> 1.1) zeitwerk (~> 2.1, >= 2.1.8) amq-protocol (2.3.0) - amqp (1.8.0) - amq-protocol (>= 2.2.0) - eventmachine ast (2.4.0) bump (0.6.1) bunny (2.14.3) amq-protocol (~> 2.3, >= 2.3.0) - bunny-mock (1.7.0) - bunny (>= 1.7) byebug (11.0.1) - clamp (1.3.1) coderay (1.1.2) concurrent-ruby (1.1.5) daemons (1.3.1) diff-lcs (1.3) docile (1.3.1) - em-spec (0.2.7) - eventmachine - em-websocket (0.5.1) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) - em-websocket-client (0.1.2) - eventmachine - websocket eventmachine (1.2.7) - http_parser.rb (0.6.0) i18n (1.7.0) concurrent-ruby (~> 1.0) irb (1.0.0) @@ -59,12 +38,10 @@ GEM jwt (2.2.1) method_source (0.9.2) minitest (5.13.0) - mysql2 (0.5.2) parallel (1.12.1) parser (2.5.1.2) ast (~> 2.4.0) powerpack (0.1.2) - prometheus-client (1.0.0) pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -116,7 +93,6 @@ GEM tzinfo (1.2.5) thread_safe (~> 0.1) unicode-display_width (1.4.0) - websocket (1.2.8) zeitwerk (2.2.1) PLATFORMS @@ -125,11 +101,8 @@ PLATFORMS DEPENDENCIES bump bundler (~> 1.16) - bunny-mock - em-spec - em-websocket-client irb - peatio! + peatio-core! pry-byebug rake (~> 10.0) rspec (~> 3.0) diff --git a/bin/peatio b/bin/peatio deleted file mode 100755 index 7af50f9..0000000 --- a/bin/peatio +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env ruby -# -*- RUBY -*- - -PEATIO_CORE = ENV["PEATIO_CORE"] || File.expand_path(File.join(File.dirname(__FILE__), "..")) -$: << File.join(PEATIO_CORE, "lib") - -require "clamp" - -require "peatio" -require "peatio/command/root" - -Peatio::Root.run diff --git a/lib/peatio.rb b/lib/peatio.rb deleted file mode 100644 index 1d8a7dd..0000000 --- a/lib/peatio.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require "logger" -require "json" -require "base64" -require "mysql2" -require "bunny" -require "eventmachine" -require "em-websocket" -require "socket" -require "securerandom" -require "rack" -require "prometheus/client" -require "prometheus/client/push" -require "prometheus/client/data_stores/single_threaded" -require "prometheus/middleware/exporter" - -module Peatio - require_relative "peatio/error" - require_relative "peatio/logger" - require_relative "peatio/version" - require_relative "peatio/sql/client" - require_relative "peatio/sql/schema" - require_relative "peatio/mq/client" - require_relative "peatio/metrics/server" - require_relative "peatio/ranger/events" - require_relative "peatio/ranger/router" - require_relative "peatio/ranger/connection" - require_relative "peatio/ranger/web_socket" - require_relative "peatio/injectors/peatio_events" - require_relative "peatio/security/key_generator" - require_relative "peatio/auth/jwt_authenticator" - - require_relative "peatio/blockchain/abstract" - require_relative "peatio/blockchain/error" - require_relative "peatio/blockchain/registry" - - require_relative "peatio/wallet/abstract" - require_relative "peatio/wallet/error" - require_relative "peatio/wallet/registry" - - require_relative "peatio/transaction" - require_relative "peatio/block" -end diff --git a/lib/peatio/adapter_registry.rb b/lib/peatio/adapter_registry.rb deleted file mode 100644 index 98cd34e..0000000 --- a/lib/peatio/adapter_registry.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Peatio - class AdapterRegistry - Error = Class.new(StandardError) - DuplicatedAdapterError = Class.new(Error) - NotRegisteredAdapterError = Class.new(Error) - - def []=(name, instance) - name = name.to_sym - raise DuplicatedAdapterError, name if adapters.key?(name) - adapters[name] = instance - end - - def [](name) - adapters.fetch(name.to_sym) { raise NotRegisteredAdapterError, name } - end - - def adapters - @adapters ||= {} - end - - def adapters=(h) - @adapters = h - end - end -end diff --git a/lib/peatio/block.rb b/lib/peatio/block.rb deleted file mode 100644 index 60153f3..0000000 --- a/lib/peatio/block.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Peatio #:nodoc: - - # This class represents blockchain block which contains transactions. - # - # Using instant of this class in return for Peatio::Blockchain#fetch_block! - # @see Peatio::Blockchain#fetch_block! example implementation - # (inside peatio source https://github.com/rubykube/peatio) - # - # @author - # Maksym Naichuk (https://github.com/mnaichuk) - class Block - include Enumerable - - delegate :each, to: :@transactions - - # @!attribute [r] number - # return [String] block number - attr_reader :number - - # @!attribute [r] transactions - # return [Array] - attr_reader :transactions - - def initialize(number, transactions) - @number = number - @transactions = transactions - end - end -end diff --git a/lib/peatio/blockchain/abstract.rb b/lib/peatio/blockchain/abstract.rb deleted file mode 100644 index e0ab7e6..0000000 --- a/lib/peatio/blockchain/abstract.rb +++ /dev/null @@ -1,161 +0,0 @@ -module Peatio #:nodoc: - module Blockchain #:nodoc: - - # @abstract Represents basic blockchain interface. - # - # Subclass and override abstract methods to implement - # a peatio plugable blockchain. - # Then you need to register your blockchain implementation. - # - # @see Bitcoin::Blockchain Bitcoin as example of Abstract imlementation - # (inside peatio source https://github.com/rubykube/peatio). - # - # @example - # - # class MyBlockchain < Peatio::Abstract::Blockchain - # def fetch_block(block_number) - # # do something - # end - # ... - # end - # - # # Register MyBlockchain as peatio plugable blockchain. - # Peatio::Blockchain.registry[:my_blockchain] = MyBlockchain.new - # - # @author - # Yaroslav Savchuk (https://github.com/ysv) - class Abstract - - # Hash of features supported by blockchain. - # - # @abstract - # - # @see Abstract::SUPPORTED_FEATURES for list of features supported by peatio. - # - # @!attribute [r] features - # @return [Hash] list of features supported by blockchain. - attr_reader :features - - # List of features supported by peatio. - # - # @note Features list: - # - # case_sensitive - defines if transactions and addresses of current - # blockchain are case_sensitive. - # - # cash_addr_format - defines if blockchain supports Cash Address format - # for more info see (https://support.exodus.io/article/664-bitcoin-cash-address-format) - SUPPORTED_FEATURES = %i[case_sensitive cash_addr_format].freeze - - - # Current blockchain settings for performing API calls and building blocks. - # - # @abstract - # - # @see Abstract::SUPPORTED_SETTINGS for list of settings required by blockchain. - # - # @!attribute [r] settings - # @return [Hash] current blockchain settings. - attr_reader :settings - - # List of configurable settings. - # - # @see #configure - SUPPORTED_SETTINGS = %i[server currencies].freeze - - - # Abstract constructor. - # - # @abstract - # - # @example - # class MyBlockchain < Peatio::Abstract::Blockchain - # - # DEFAULT_FEATURES = {case_sensitive: true, cash_addr_format: false}.freeze - # - # # You could override default features by passing them to initializer. - # def initialize(my_custom_features = {}) - # @features = DEFAULT_FEATURES.merge(my_custom_features) - # end - # ... - # end - # - # # Register MyBlockchain as peatio plugable blockchain. - # custom_features = {cash_addr_format: true} - # Peatio::Blockchain.registry[:my_blockchain] = MyBlockchain.new(custom_features) - def initialize(*) - abstract_method - end - - # Merges given configuration parameters with defined during initialization - # and returns the result. - # - # @abstract - # - # @param [Hash] settings parameters to use. - # - # @option settings [String] :server Public blockchain API endpoint. - # @option settings [Array] :currencies List of currency hashes - # with :id,:base_factor,:options(deprecated) keys. - # Custom keys could be added by defining them in Currency #options. - # - # @return [Hash] merged settings. - # - # @note Be careful with your blockchain state after configure. - # Clean everything what could be related to other blockchain configuration. - # E.g. client state. - def configure(settings = {}) - abstract_method - end - - # Fetches blockchain block by calling API and builds block object - # from response payload. - # - # @abstract - # - # @param block_number [Integer] the block number. - # @return [Peatio::Block] the block object. - # @raise [Peatio::Blockchain::ClientError] if error was raised - # on blockchain API call. - def fetch_block!(block_number) - abstract_method - end - - # Fetches current blockchain height by calling API and returns it as number. - # - # @abstract - # - # @return [Integer] the current blockchain height. - # @raise [Peatio::Blockchain::ClientError] if error was raised - # on blockchain API call. - def latest_block_number - abstract_method - end - - # Fetches address balance of specific currency. - # - # @note Optional. Don't override this method if your blockchain - # doesn't provide functionality to get balance by address. - # - # @param address [String] the address for requesting balance. - # @param currency_id [String] which currency balance we need to request. - # @return [BigDecimal] the current address balance. - # @raise [Peatio::Blockchain::ClientError,Peatio::Blockchain::UnavailableAddressBalanceError] - # if error was raised on blockchain API call ClientError is raised. - # if blockchain API call was successful but we can't detect balance - # for address Error is raised. - def load_balance_of_address!(address, currency_id) - raise Peatio::Blockchain::UnavailableAddressBalanceError - end - - private - - # Method for defining other methods as abstract. - # - # @raise [MethodNotImplemented] - def abstract_method - method_not_implemented - end - end - end -end diff --git a/lib/peatio/blockchain/error.rb b/lib/peatio/blockchain/error.rb deleted file mode 100644 index 11f5a15..0000000 --- a/lib/peatio/blockchain/error.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Peatio - module Blockchain - Error = Class.new(StandardError) - - class ClientError < Error - - attr_reader :wrapped_ex - - def initialize(ex_or_string) - @wrapped_ex = nil - - if ex_or_string.respond_to?(:backtrace) - super(ex_or_string.message) - @wrapped_exception = ex_or_string - else - super(ex_or_string.to_s) - end - end - end - - class MissingSettingError < Error - def initialize(key) - super "#{key.capitalize} setting is missing" - end - end - - class UnavailableAddressBalanceError < Error - def initialize(address) - @address = address - end - - def message - "Unable to load #{@address} balance" - end - end - end -end diff --git a/lib/peatio/blockchain/registry.rb b/lib/peatio/blockchain/registry.rb deleted file mode 100644 index 8ff096b..0000000 --- a/lib/peatio/blockchain/registry.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "peatio/adapter_registry" - -module Peatio - module Blockchain - - VERSION = "1.0.0".freeze - - class << self - def registry - @registry ||= Registry.new - end - end - class Registry < Peatio::AdapterRegistry - end - end -end diff --git a/lib/peatio/command/base.rb b/lib/peatio/command/base.rb deleted file mode 100644 index be780cb..0000000 --- a/lib/peatio/command/base.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Peatio::Command - class Base < Clamp::Command - def say(str) - puts str - end - - def sql_client - @sql_client ||= Peatio::Sql::Client.new.connect - end - end -end diff --git a/lib/peatio/command/db.rb b/lib/peatio/command/db.rb deleted file mode 100644 index 217a1ee..0000000 --- a/lib/peatio/command/db.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Peatio::Command::DB - class Create < Peatio::Command::Base - def execute - client = Peatio::Sql::Client.new - database_name = client.config.delete(:database) - Peatio::Sql::Schema.new(client.connect).create_database(database_name) - end - end - - class Migrate < Peatio::Command::Base - def execute - Peatio::Sql::Schema.new(sql_client).create_tables - end - end - - class Root < Peatio::Command::Base - subcommand "create", "Create database", Peatio::Command::DB::Create - subcommand "migrate", "Create tables", Peatio::Command::DB::Migrate - end -end diff --git a/lib/peatio/command/inject.rb b/lib/peatio/command/inject.rb deleted file mode 100644 index 1adc236..0000000 --- a/lib/peatio/command/inject.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Peatio::Command - class Inject < Peatio::Command::Base - class PeatioEvents < Peatio::Command::Base - option ["-e", "--exchange"], "NAME", "exchange name to inject messages to", default: "peatio.events.ranger" - def execute - Peatio::Logger.logger.level = :debug - Peatio::Injectors::PeatioEvents.new.run!(exchange) - end - end - - subcommand "peatio_events", "Inject peatio events in mq", PeatioEvents - end -end diff --git a/lib/peatio/command/root.rb b/lib/peatio/command/root.rb deleted file mode 100644 index ed2c6f0..0000000 --- a/lib/peatio/command/root.rb +++ /dev/null @@ -1,14 +0,0 @@ -require "peatio/command/base" -require "peatio/command/service" -require "peatio/command/db" -require "peatio/command/inject" -require "peatio/command/security" - -module Peatio - class Root < Command::Base - subcommand "db", "Database related sub-commands", Peatio::Command::DB::Root - subcommand "service", "Services management related sub-commands", Peatio::Command::Service::Root - subcommand "inject", "Data injectors", Peatio::Command::Inject - subcommand "security", "Security management related sub-commands", Peatio::Command::Security - end -end diff --git a/lib/peatio/command/security.rb b/lib/peatio/command/security.rb deleted file mode 100644 index ab1c9c6..0000000 --- a/lib/peatio/command/security.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Command - class Security < Peatio::Command::Base - class KeyGenerator < Peatio::Command::Base - option "--print", :flag, "print on screen" - option "--path", "FOLDER", "save keypair into folder", default: "secrets" - - def execute - keypair = Peatio::Security::KeyGenerator.new - - if print? - puts keypair.private, keypair.public - puts "-----BASE64 ENCODED-----" - puts Base64.urlsafe_encode64(keypair.public) - else - begin - keypair.save(path) - puts "Files saved in #{File.join(path, 'rsa-key')}" - rescue IOError => e - abort("Failed saving files") - end - end - end - end - - subcommand "keygen", "Generate a public private rsa key pair", KeyGenerator - end -end diff --git a/lib/peatio/command/service.rb b/lib/peatio/command/service.rb deleted file mode 100644 index 70aee70..0000000 --- a/lib/peatio/command/service.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Command::Service - class Start < Peatio::Command::Base - class Ranger < Peatio::Command::Base - option ["-e", "--exchange"], "NAME", "exchange name to inject messages to", default: "peatio.events.ranger" - option "--[no-]stats", :flag, "display periodically connections statistics", default: true - option "--stats-period", "SECONDS", "period of displaying stats in seconds", default: 30 - def execute - raise ArgumentError, "JWT_PUBLIC_KEY was not specified." if ENV["JWT_PUBLIC_KEY"].to_s.empty? - - key_decoded = Base64.urlsafe_decode64(ENV["JWT_PUBLIC_KEY"]) - - jwt_public_key = OpenSSL::PKey.read(key_decoded) - if jwt_public_key.private? - raise ArgumentError, "JWT_PUBLIC_KEY was set to private key, however it should be public." - end - - raise "stats period missing" if stats? && !stats_period - - Prometheus::Client.config.data_store = Prometheus::Client::DataStores::SingleThreaded.new() - registry = Prometheus::Client.registry - - opts = { - display_stats: stats?, - stats_period: stats_period.to_f, - metrics_port: 8082, - registry: registry - } - ::Peatio::Ranger.run!(jwt_public_key, exchange, opts) - end - end - - subcommand "ranger", "Start ranger process", Ranger - end - - class Root < Peatio::Command::Base - subcommand "start", "Start a service", Start - end -end diff --git a/lib/peatio/core.rb b/lib/peatio/core.rb new file mode 100644 index 0000000..b9b67fb --- /dev/null +++ b/lib/peatio/core.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "json" +require "base64" +require "securerandom" + +module Peatio + module Core + require_relative "core/error" + require_relative "core/version" + require_relative "core/security/key_generator" + require_relative "core/auth/jwt_authenticator" + + require_relative "core/blockchain/abstract" + require_relative "core/blockchain/error" + require_relative "core/blockchain/registry" + + require_relative "core/wallet/abstract" + require_relative "core/wallet/error" + require_relative "core/wallet/registry" + + require_relative "core/transaction" + require_relative "core/block" + end +end diff --git a/lib/peatio/core/adapter_registry.rb b/lib/peatio/core/adapter_registry.rb new file mode 100644 index 0000000..2eca3f7 --- /dev/null +++ b/lib/peatio/core/adapter_registry.rb @@ -0,0 +1,27 @@ +module Peatio + module Core + class AdapterRegistry + Error = Class.new(StandardError) + DuplicatedAdapterError = Class.new(Error) + NotRegisteredAdapterError = Class.new(Error) + + def []=(name, instance) + name = name.to_sym + raise DuplicatedAdapterError, name if adapters.key?(name) + adapters[name] = instance + end + + def [](name) + adapters.fetch(name.to_sym) { raise NotRegisteredAdapterError, name } + end + + def adapters + @adapters ||= {} + end + + def adapters=(h) + @adapters = h + end + end + end +end diff --git a/lib/peatio/auth/error.rb b/lib/peatio/core/auth/error.rb similarity index 88% rename from lib/peatio/auth/error.rb rename to lib/peatio/core/auth/error.rb index 4dc15f1..42045b6 100644 --- a/lib/peatio/auth/error.rb +++ b/lib/peatio/core/auth/error.rb @@ -1,6 +1,6 @@ -module Peatio::Auth +module Peatio::Core::Auth # Error repesent all errors that can be returned from Auth module. - class Error < Peatio::Error + class Error < Peatio::Core::Error # @return [String, JWT::*] Reason store underlying reason for given error. # # @see https://github.com/jwt/ruby-jwt/blob/master/lib/jwt/error.rb List of JWT::* errors. diff --git a/lib/peatio/auth/jwt_authenticator.rb b/lib/peatio/core/auth/jwt_authenticator.rb similarity index 89% rename from lib/peatio/auth/jwt_authenticator.rb rename to lib/peatio/core/auth/jwt_authenticator.rb index 39bebde..c8771f6 100644 --- a/lib/peatio/auth/jwt_authenticator.rb +++ b/lib/peatio/core/auth/jwt_authenticator.rb @@ -2,7 +2,7 @@ require_relative "error" -module Peatio::Auth +module Peatio::Core::Auth # JWTAuthenticator used to authenticate user using JWT. # # It allows configuration of JWT verification through following ENV @@ -40,7 +40,7 @@ module Peatio::Auth # # token = JWT.encode(payload, rsa_private, "RS256") # - # auth = Peatio::Auth::JWTAuthenticator.new(rsa_public) + # auth = Peatio::Core::Auth::JWTAuthenticator.new(rsa_public) # auth.authenticate!("Bearer #{token}") class JWTAuthenticator # Creates new authenticator with given public key. @@ -82,21 +82,21 @@ def initialize(public_key, private_key = nil) # @param token [String] Token string. Must start from "Bearer ". # @return [Hash] Payload Hash from JWT without any changes. # - # @raise [Peatio::Auth::Error] If token is invalid or can't be verified. + # @raise [Peatio::Core::Auth::Error] If token is invalid or can't be verified. def authenticate!(token) token_type, token_value = token.to_s.split(" ") unless token_type == "Bearer" - raise(Peatio::Auth::Error, "Token type is not provided or invalid.") + raise(Peatio::Core::Auth::Error, "Token type is not provided or invalid.") end decode_and_verify_token(token_value) rescue => error case error - when Peatio::Auth::Error + when Peatio::Core::Auth::Error raise(error) else - raise(Peatio::Auth::Error, error.message) + raise(Peatio::Core::Auth::Error, error.message) end end @@ -121,7 +121,7 @@ def decode_and_verify_token(token) payload rescue JWT::DecodeError => e - raise(Peatio::Auth::Error, "Failed to decode and verify JWT: #{e.message}") + raise(Peatio::Core::Auth::Error, "Failed to decode and verify JWT: #{e.message}") end end end diff --git a/lib/peatio/core/block.rb b/lib/peatio/core/block.rb new file mode 100644 index 0000000..8fa9b7c --- /dev/null +++ b/lib/peatio/core/block.rb @@ -0,0 +1,31 @@ +module Peatio #:nodoc: + + # This class represents blockchain block which contains transactions. + # + # Using instant of this class in return for Peatio::Core::Blockchain#fetch_block! + # @see Peatio::Core::Blockchain#fetch_block! example implementation + # (inside peatio source https://github.com/rubykube/peatio) + # + # @author + # Maksym Naichuk (https://github.com/mnaichuk) + module Core + class Block + include Enumerable + + delegate :each, to: :@transactions + + # @!attribute [r] number + # return [String] block number + attr_reader :number + + # @!attribute [r] transactions + # return [Array] + attr_reader :transactions + + def initialize(number, transactions) + @number = number + @transactions = transactions + end + end + end +end diff --git a/lib/peatio/core/blockchain/abstract.rb b/lib/peatio/core/blockchain/abstract.rb new file mode 100644 index 0000000..2e8571f --- /dev/null +++ b/lib/peatio/core/blockchain/abstract.rb @@ -0,0 +1,162 @@ +module Peatio #:nodoc: + module Core + module Blockchain #:nodoc: + # @abstract Represents basic blockchain interface. + # + # Subclass and override abstract methods to implement + # a peatio plugable blockchain. + # Then you need to register your blockchain implementation. + # + # @see Bitcoin::Blockchain Bitcoin as example of Abstract imlementation + # (inside peatio source https://github.com/rubykube/peatio). + # + # @example + # + # class MyBlockchain < Peatio::Core::Abstract::Blockchain + # def fetch_block(block_number) + # # do something + # end + # ... + # end + # + # # Register MyBlockchain as peatio plugable blockchain. + # Peatio::Core::Blockchain.registry[:my_blockchain] = MyBlockchain.new + # + # @author + # Yaroslav Savchuk (https://github.com/ysv) + class Abstract + + # Hash of features supported by blockchain. + # + # @abstract + # + # @see Abstract::SUPPORTED_FEATURES for list of features supported by peatio. + # + # @!attribute [r] features + # @return [Hash] list of features supported by blockchain. + attr_reader :features + + # List of features supported by peatio. + # + # @note Features list: + # + # case_sensitive - defines if transactions and addresses of current + # blockchain are case_sensitive. + # + # cash_addr_format - defines if blockchain supports Cash Address format + # for more info see (https://support.exodus.io/article/664-bitcoin-cash-address-format) + SUPPORTED_FEATURES = %i[case_sensitive cash_addr_format].freeze + + + # Current blockchain settings for performing API calls and building blocks. + # + # @abstract + # + # @see Abstract::SUPPORTED_SETTINGS for list of settings required by blockchain. + # + # @!attribute [r] settings + # @return [Hash] current blockchain settings. + attr_reader :settings + + # List of configurable settings. + # + # @see #configure + SUPPORTED_SETTINGS = %i[server currencies].freeze + + + # Abstract constructor. + # + # @abstract + # + # @example + # class MyBlockchain < Peatio::Core::Abstract::Blockchain + # + # DEFAULT_FEATURES = {case_sensitive: true, cash_addr_format: false}.freeze + # + # # You could override default features by passing them to initializer. + # def initialize(my_custom_features = {}) + # @features = DEFAULT_FEATURES.merge(my_custom_features) + # end + # ... + # end + # + # # Register MyBlockchain as peatio plugable blockchain. + # custom_features = {cash_addr_format: true} + # Peatio::Core::Blockchain.registry[:my_blockchain] = MyBlockchain.new(custom_features) + def initialize(*) + abstract_method + end + + # Merges given configuration parameters with defined during initialization + # and returns the result. + # + # @abstract + # + # @param [Hash] settings parameters to use. + # + # @option settings [String] :server Public blockchain API endpoint. + # @option settings [Array] :currencies List of currency hashes + # with :id,:base_factor,:options(deprecated) keys. + # Custom keys could be added by defining them in Currency #options. + # + # @return [Hash] merged settings. + # + # @note Be careful with your blockchain state after configure. + # Clean everything what could be related to other blockchain configuration. + # E.g. client state. + def configure(settings = {}) + abstract_method + end + + # Fetches blockchain block by calling API and builds block object + # from response payload. + # + # @abstract + # + # @param block_number [Integer] the block number. + # @return [Peatio::Core::Block] the block object. + # @raise [Peatio::Core::Blockchain::ClientError] if error was raised + # on blockchain API call. + def fetch_block!(block_number) + abstract_method + end + + # Fetches current blockchain height by calling API and returns it as number. + # + # @abstract + # + # @return [Integer] the current blockchain height. + # @raise [Peatio::Core::Blockchain::ClientError] if error was raised + # on blockchain API call. + def latest_block_number + abstract_method + end + + # Fetches address balance of specific currency. + # + # @note Optional. Don't override this method if your blockchain + # doesn't provide functionality to get balance by address. + # + # @param address [String] the address for requesting balance. + # @param currency_id [String] which currency balance we need to request. + # @return [BigDecimal] the current address balance. + # @raise [Peatio::Core::Blockchain::ClientError,Peatio::Core::Blockchain::UnavailableAddressBalanceError] + # if error was raised on blockchain API call ClientError is raised. + # if blockchain API call was successful but we can't detect balance + # for address Error is raised. + def load_balance_of_address!(address, currency_id) + raise Peatio::Core::Blockchain::UnavailableAddressBalanceError + end + + private + + # Method for defining other methods as abstract. + # + # @raise [MethodNotImplemented] + def abstract_method + method_not_implemented + end + end + end + end +end diff --git a/lib/peatio/core/blockchain/error.rb b/lib/peatio/core/blockchain/error.rb new file mode 100644 index 0000000..f7ba3eb --- /dev/null +++ b/lib/peatio/core/blockchain/error.rb @@ -0,0 +1,39 @@ +module Peatio + module Core + module Blockchain + Error = Class.new(StandardError) + + class ClientError < Error + + attr_reader :wrapped_ex + + def initialize(ex_or_string) + @wrapped_ex = nil + + if ex_or_string.respond_to?(:backtrace) + super(ex_or_string.message) + @wrapped_exception = ex_or_string + else + super(ex_or_string.to_s) + end + end + end + + class MissingSettingError < Error + def initialize(key) + super "#{key.capitalize} setting is missing" + end + end + + class UnavailableAddressBalanceError < Error + def initialize(address) + @address = address + end + + def message + "Unable to load #{@address} balance" + end + end + end + end +end diff --git a/lib/peatio/core/blockchain/registry.rb b/lib/peatio/core/blockchain/registry.rb new file mode 100644 index 0000000..b443f1d --- /dev/null +++ b/lib/peatio/core/blockchain/registry.rb @@ -0,0 +1,17 @@ +require "peatio/core/adapter_registry" + +module Peatio + module Core + module Blockchain + VERSION = "1.0.0".freeze + + class << self + def registry + @registry ||= Registry.new + end + end + class Registry < Peatio::Core::AdapterRegistry + end + end + end +end diff --git a/lib/peatio/error.rb b/lib/peatio/core/error.rb similarity index 87% rename from lib/peatio/error.rb rename to lib/peatio/core/error.rb index fd8d16c..000407c 100644 --- a/lib/peatio/error.rb +++ b/lib/peatio/core/error.rb @@ -1,4 +1,4 @@ -class Peatio::Error < ::StandardError +class Peatio::Core::Error < ::StandardError @@default_code = 2000 attr :code, :text diff --git a/lib/peatio/executor.rb b/lib/peatio/core/executor.rb similarity index 100% rename from lib/peatio/executor.rb rename to lib/peatio/core/executor.rb diff --git a/lib/peatio/security/key_generator.rb b/lib/peatio/core/security/key_generator.rb similarity index 94% rename from lib/peatio/security/key_generator.rb rename to lib/peatio/core/security/key_generator.rb index 9f872d4..61606c1 100644 --- a/lib/peatio/security/key_generator.rb +++ b/lib/peatio/core/security/key_generator.rb @@ -1,6 +1,6 @@ require 'fileutils' -module Peatio::Security +module Peatio::Core::Security class KeyGenerator attr_reader :public, :private diff --git a/lib/peatio/core/transaction.rb b/lib/peatio/core/transaction.rb new file mode 100644 index 0000000..097af3f --- /dev/null +++ b/lib/peatio/core/transaction.rb @@ -0,0 +1,116 @@ +require 'active_support/concern' +require 'active_support/core_ext/string/inquiry' +require 'active_support/core_ext/object/blank' +require 'active_model' + +module Peatio #:nodoc: + module Core + + # This class represents blockchain transaction. + # + # Using the instant of this class the peatio application will send/recieve + # income/outcome transactions from a peatio pluggable blockchain. + # + # @example + # + # Peatio::Core::Transaction.new( + # { + # hash: '0x5d0ef9697a2f3ea561c9fbefb48e380a4cf3d26ad2be253177c472fdd0e8b486', + # txout: 1, + # to_address: '0x9af4f143cd5ecfba0fcdd863c5ef52d5ccb4f3e5', + # amount: 0.01, + # block_number: 7732274, + # currency_id: 'eth', + # status: 'success' + # } + # ) + # + # @author + # Maksym Naichuk (https://github.com/mnaichuk) + class Transaction + include ActiveModel::Model + + # List of statuses supported by peatio. + # + # @note Statuses list: + # + # pending - the transaction is unconfirmed in the blockchain or + # wasn't created yet. + # + # success - the transaction is a successfull, + # the transaction amount has been successfully transferred + # + # failed - the transaction is failed in the blockchain. + + STATUSES = %w[success pending failed].freeze + + DEFAULT_STATUS = 'pending'.freeze + + # @!attribute [rw] hash + # return [String] transaction hash + attr_accessor :hash + + # @!attribute [rw] txout + # return [Integer] transaction number in send-to-many request + attr_accessor :txout + + # @!attribute [rw] to_address + # return [String] transaction recepient address + attr_accessor :to_address + + # @!attribute [rw] amount + # return [Decimal] amount of the transaction + attr_accessor :amount + + # @!attribute [rw] block_number + # return [Integer] transaction block number + attr_accessor :block_number + + # @!attribute [rw] currency_id + # return [String] transaction currency id + attr_accessor :currency_id + + validates :to_address, + :amount, + :currency_id, + :status, + presence: true + + validates :hash, + :block_number, + presence: { if: -> (t){ t.status.failed? || t.status.success? } } + + validates :txout, + presence: { if: -> (t){ t.status.success? } } + + validates :block_number, + numericality: { greater_than_or_equal_to: 0, only_integer: true } + + validates :amount, + numericality: { greater_than_or_equal_to: 0 } + + validates :status, inclusion: { in: STATUSES } + + def initialize(attributes={}) + super + @status = @status.present? ? @status.to_s : DEFAULT_STATUS + end + + # Status for specific transaction. + # + # @!method status + # + # @example + # + # status.failed? # true if transaction status 'failed' + # status.success? # true if transaction status 'success' + def status + @status&.inquiry + end + + def status=(s) + @status = s.to_s + end + end + end +end diff --git a/lib/peatio/core/version.rb b/lib/peatio/core/version.rb new file mode 100644 index 0000000..1676e90 --- /dev/null +++ b/lib/peatio/core/version.rb @@ -0,0 +1,5 @@ +module Peatio + module Core + VERSION = "0.6.2" + end +end diff --git a/lib/peatio/core/wallet/abstract.rb b/lib/peatio/core/wallet/abstract.rb new file mode 100644 index 0000000..7a98f57 --- /dev/null +++ b/lib/peatio/core/wallet/abstract.rb @@ -0,0 +1,174 @@ +module Peatio + module Core + module Wallet + # @abstract Represents basic wallet interface. + # + # Subclass and override abstract methods to implement + # a peatio plugable wallet. + # Than you need to register your wallet implementation. + # + # @see Bitcoin::Wallet Bitcoin as example of Abstract imlementation. + # + # @example + # + # class MyWallet < Peatio::Core::Abstract::Wallet + # def create_address(options = {}) + # # do something + # end + # ... + # end + # + # # Register MyWallet as peatio plugable wallet. + # Peatio::Core::Wallet.registry[:my_wallet] = MyWallet.new + # + # @author + # Yaroslav Savchuk (https://github.com/ysv) + class Abstract + # Current wallet settings for performing API calls. + # + # @abstract + # + # @!attribute [r] settings + # @return [Hash] current wallet settings. + attr_reader :settings + + # List of configurable settings. + # + # @see #configure + SUPPORTED_SETTINGS = %i[wallet currency].freeze + + + # Abstract constructor. + # + # @abstract + # + # @example + # class MyWallet< Peatio::Core::Abstract::Wallet + # + # # You could customize your wallet by passing features. + # def initialize(my_custom_features = {}) + # @features = my_custom_features + # end + # ... + # end + # + # # Register MyWallet as peatio plugable wallet. + # custom_features = {cash_addr_format: true} + # Peatio::Core::Wallet.registry[:my_wallet] = MyWallet.new(custom_features) + def initialize(*) + abstract_method + end + + # Merges given configuration parameters with defined during initialization + # and returns the result. + # + # @abstract + # + # @param [Hash] settings configurations to use. + # @option settings [Hash] :wallet Wallet settings for performing API calls. + # With :address required key other settings could be customized + # using Wallet#settings. + # @option settings [Array] :currencies List of currency hashes + # with :id,:base_factor,:options(deprecated) keys. + # Custom keys could be added by defining them in Currency #options. + # + # @return [Hash] merged settings. + # + # @note Be careful with your wallet state after configure. + # Clean everything what could be related to other wallet configuration. + # E.g. client state. + def configure(settings = {}) + abstract_method + end + + # Performs API call for address creation and returns it. + # + # @abstract + # + # @param [Hash] options + # @options options [String] :uid User UID which requested address creation. + # + # @return [Hash] newly created blockchain address. + # + # @raise [Peatio::Core::Blockchain::ClientError] if error was raised + # on wallet API call. + # + # @example + # { address: :fake_address, + # secret: :changeme, + # details: { uid: account.member.uid } } + def create_address!(options = {}) + abstract_method + end + + # Performs API call for creating transaction and returns updated transaction. + # + # @abstract + # + # @param [Peatio::Core::Transaction] transaction transaction with defined + # to_address, amount & currency_id. + # + # @param [Hash] options + # @options options [String] :subtract_fee Defines if you need to subtract + # fee from amount defined in transaction. + # It means that you need to deduct fee from amount declared in + # transaction and send only remaining amount. + # If transaction amount is 1.0 and estimated fee + # for sending transaction is 0.01 you need to send 0.09 + # so 1.0 (0.9 + 0.1) will be subtracted from wallet balance + # + # @options options [String] custon options for wallet client. + # + # @return [Peatio::Core::Transaction] transaction with updated hash. + # + # @raise [Peatio::Core::Blockchain::ClientError] if error was raised + # on wallet API call. + def create_transaction!(transaction, options = {}) + abstract_method + end + + # Fetches address balance of specific currency. + # + # @note Optional. Don't override this method if your blockchain + # doesn't provide functionality to get balance by address. + # + # @return [BigDecimal] the current address balance. + # + # @raise [Peatio::Core::Blockchain::ClientError,Peatio::Core::Blockchain::UnavailableAddressBalanceError] + # if error was raised on wallet API call ClientError is raised. + # if wallet API call was successful but we can't detect balance + # for address Error is raised. + def load_balance! + raise Peatio::Core::Wallet::UnavailableAddressBalanceError + end + + # Performs API call(s) for preparing for deposit collection. + # E.g deposits ETH for collecting ERC20 tokens in case of Ethereum blockchain. + # + # @note Optional. Override this method only if you need additional step + # before deposit collection. + # + # @param [Peatio::Core::Transaction] deposit_transaction transaction which + # describes received deposit. + # + # @param [Array] spread_transactions result of deposit + # spread between wallets. + # + # @return [Array] transaction created for + # deposit collection preparing. + # By default return empty [Array] + def prepare_deposit_collection!(deposit_transaction, spread_transactions, deposit_currency) + # This method is mostly used for coins which needs additional fees + # to be deposited before deposit collection. + [] + end + + private + + def abstract_method + method_not_implemented + end + end + end + end +end diff --git a/lib/peatio/core/wallet/error.rb b/lib/peatio/core/wallet/error.rb new file mode 100644 index 0000000..3611887 --- /dev/null +++ b/lib/peatio/core/wallet/error.rb @@ -0,0 +1,39 @@ +module Peatio + module Core + module Wallet + Error = Class.new(StandardError) + + class ClientError < Error + + attr_reader :wrapped_ex + + def initialize(ex_or_string) + @wrapped_ex = nil + + if ex_or_string.respond_to?(:backtrace) + super(ex_or_string.message) + @wrapped_exception = ex_or_string + else + super(ex_or_string.to_s) + end + end + end + + class MissingSettingError < Error + def initialize(key) + super "#{key.capitalize} setting is missing" + end + end + + class UnavailableAddressBalanceError < Error + def initialize(address) + @address = address + end + + def message + "Unable to load #{@address} balance" + end + end + end + end +end diff --git a/lib/peatio/core/wallet/registry.rb b/lib/peatio/core/wallet/registry.rb new file mode 100644 index 0000000..76a09d5 --- /dev/null +++ b/lib/peatio/core/wallet/registry.rb @@ -0,0 +1,18 @@ +require "peatio/core/adapter_registry" + +module Peatio + module Core + module Wallet + + VERSION = "1.0.0".freeze + + class << self + def registry + @registry ||= Registry.new + end + end + class Registry < Peatio::Core::AdapterRegistry + end + end + end +end diff --git a/lib/peatio/injectors/peatio_events.rb b/lib/peatio/injectors/peatio_events.rb deleted file mode 100644 index daf94ff..0000000 --- a/lib/peatio/injectors/peatio_events.rb +++ /dev/null @@ -1,240 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Injectors - class PeatioEvents - attr_accessor :market, :market_name, :base_unit, :quote_unit, :seller_uid, :buyer_uid, :logger - - def run!(exchange_name) - require "time" - @logger = Peatio::Logger.logger - @market = "eurusd" - @market_name = "EUR/USD" - @base_unit = "eur" - @quote_unit = "usd" - @seller_uid = 21 - @buyer_uid = 42 - @messages = create_messages - @opts = { - ex_name: exchange_name - } - EventMachine.run do - inject_message() - end - end - - def inject_message() - if message = @messages.shift - type, id, event, data = message - Peatio::Ranger::Events.publish(type, id, event, data, @opts) - EM.next_tick do - inject_message() - end - else - Peatio::MQ::Client.disconnect - EventMachine.stop - end - end - - def create_messages - [ - public_tickers, - public_orderbook, - private_order, - private_trade_user1, - private_trade_user2, - public_trade, - public_orderbook_increment1, - public_orderbook_snapshot1, - public_orderbook_increment2, - public_orderbook_increment3, - ] - end - - def created_at - Time.now - 600 - end - - def updated_at - Time.now - end - - alias completed_at updated_at - alias canceled_at updated_at - - def public_orderbook - [ - "public", - market, - "update", - { - "asks": [ - ["1020.0", "0.005"], - ["1026.0", "0.03"] - ], - "bids": [ - ["1000.0", "0.25"], - ["999.0", "0.005"], - ["994.0", "0.005"], - ["1.0", "11.0"] - ] - } - ] - end - - def public_orderbook_snapshot1 - [ - "public", - market, - "ob-snap", - { - "asks": [ - ["1020.0", "0.005"], - ["1026.0", "0.03"] - ], - "bids": [ - ["1000.0", "0.25"], - ["999.0", "0.005"], - ["994.0", "0.005"], - ["1.0", "11.0"] - ] - } - ] - end - - def public_orderbook_increment1 - [ - "public", - market, - "ob-inc", - { - "asks": [ - ["1020.0", "0.015"], - ], - } - ] - end - - def public_orderbook_increment2 - [ - "public", - market, - "ob-inc", - { - "bids": [ - ["1000.0", "0"], - ], - } - ] - end - - def public_orderbook_increment3 - [ - "public", - market, - "ob-inc", - { - "bids": [ - ["999.0", "0.001"], - ], - } - ] - end - - def public_tickers - [ - "public", - "global", - "tickers", - { - market => { - "name": market_name, - "base_unit": base_unit, - "quote_unit": quote_unit, - "low": "1000.0", - "high": "10000.0", - "last": "1000.0", - "open": 1000.0, - "volume": "0.0", - "sell": "1020.0", - "buy": "1000.0", - "at": Time.now.to_i - } - } - ] - end - - def private_order - [ - "private", - "IDABC0000001", - "order", - { - "id": 22, - "at": created_at.to_i, - "market": market, - "kind": "bid", - "price": "1026.0", - "state": "wait", - "volume": "0.001", - "origin_volume": "0.001" - } - ] - end - - def private_trade_user1 - [ - "private", - "IDABC0000001", - "trade", - { - "id": 7, - "kind": "ask", - "at": created_at.to_i, - "price": "1020.0", - "volume": "0.001", - "ask_id": 15, - "bid_id": 22, - "market": market - } - ] - end - - def private_trade_user2 - [ - "private", - "IDABC0000002", - "trade", - { - "id": 7, - "kind": "bid", - "at": created_at.to_i, - "price": "1020.0", - "volume": "0.001", - "ask_id": 15, - "bid_id": 22, - "market": market - } - ] - end - - def public_trade - [ - "public", - market, - "trades", - { - "trades": [ - { - "tid": 7, - "taker_type": "buy", - "date": created_at.to_i, - "price": "1020.0", - "amount": - "0.001" - } - ] - } - ] - end - end -end diff --git a/lib/peatio/logger.rb b/lib/peatio/logger.rb deleted file mode 100644 index a90be7f..0000000 --- a/lib/peatio/logger.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -module Peatio - class Logger - class << self - def logger - @logger ||= ::Logger.new(STDERR, level: level) - end - - def level - (ENV["LOG_LEVEL"] || "info").downcase.to_sym - end - - def debug(progname=nil, &block) - logger.debug(progname, &block) - end - - def info(progname=nil, &block) - logger.info(progname, &block) - end - - def warn(progname=nil, &block) - logger.warn(progname, &block) - end - - def error(progname=nil, &block) - logger.error(progname, &block) - end - - def fatal(progname=nil, &block) - logger.fatal(progname, &block) - end - - def unknown(progname=nil, &block) - logger.unknown(progname, &block) - end - end - end -end diff --git a/lib/peatio/metrics/server.rb b/lib/peatio/metrics/server.rb deleted file mode 100644 index a4de707..0000000 --- a/lib/peatio/metrics/server.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Metrics - class Server - def self.app(registry) - Rack::Builder.new do |builder| - builder.use Rack::CommonLogger - builder.use Rack::ShowExceptions - builder.use Rack::Deflater - builder.use Prometheus::Middleware::Exporter, registry: registry - builder.run ->(_) { [404, {"Content-Type" => "text/html"}, ["Not found\n"]] } - end - end - end -end diff --git a/lib/peatio/mq/client.rb b/lib/peatio/mq/client.rb deleted file mode 100644 index 58fce08..0000000 --- a/lib/peatio/mq/client.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -module Peatio::MQ - class Client - class << self - attr_accessor :connection - - def connect! - options = { - host: ENV["RABBITMQ_HOST"] || "0.0.0.0", - port: ENV["RABBITMQ_PORT"] || "5672", - username: ENV["RABBITMQ_USER"], - password: ENV["RABBITMQ_PASSWORD"], - } - @connection = Bunny.new(options) - @connection.start - end - - def disconnect - @connection.close - end - end - - def initialize - Client.connect! unless Peatio::MQ::Client.connection - @channel = Client.connection.create_channel - @exchanges = {} - end - - def exchange(name, type="topic") - @exchanges[name] ||= @channel.exchange(name, type: type) - end - - def publish(ex_name, type, id, event, payload) - routing_key = [type, id, event].join(".") - serialized_data = JSON.dump(payload) - exchange(ex_name).publish(serialized_data, routing_key: routing_key) - Peatio::Logger.debug { "published event to #{routing_key} " } - end - - def subscribe(ex_name, &callback) - suffix = "#{Socket.gethostname.split(/-/).last}#{Random.rand(10_000)}" - queue_name = "ranger.#{suffix}" - - @channel - .queue(queue_name, durable: false, auto_delete: true) - .bind(exchange(ex_name), routing_key: "#").subscribe(&callback) - Peatio::Logger.info "Subscribed to exchange #{ex_name}" - end - end -end diff --git a/lib/peatio/ranger/connection.rb b/lib/peatio/ranger/connection.rb deleted file mode 100644 index 977b46a..0000000 --- a/lib/peatio/ranger/connection.rb +++ /dev/null @@ -1,110 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Ranger - class Connection - attr_reader :socket, :user, :authorized, :streams, :logger, :id - - def initialize(router, socket, logger) - @id = SecureRandom.hex(10) - @router = router - @socket = socket - @logger = logger - @user = nil - @authorized = false - @streams = {} - end - - def inspect - if authorized - "" - else - "" - end - end - - def send_raw(payload) - logger.debug { "sending to user #{user.inspect} payload: #{payload}" } - @socket.send(payload) - end - - def send(method, data) - payload = JSON.dump(method => data) - send_raw(payload) - end - - def authenticate(authenticator, jwt) - payload = {} - authorized = false - begin - payload = authenticator.authenticate!(jwt) - authorized = true - rescue Peatio::Auth::Error => e - logger.warn e.message - end - [authorized, payload] - end - - def subscribe(subscribed_streams) - raise "Streams must be an array of strings" unless subscribed_streams.is_a?(Array) - - subscribed_streams.each do |stream| - stream = stream.to_s - next if stream.empty? - - unless @streams[stream] - @streams[stream] = true - @router.on_subscribe(self, stream) - end - end - send(:success, message: "subscribed", streams: streams.keys) - end - - def unsubscribe(unsubscribed_streams) - raise "Streams must be an array of strings" unless unsubscribed_streams.is_a?(Array) - - unsubscribed_streams.each do |stream| - stream = stream.to_s - next if stream.empty? - - if @streams[stream] - @streams.delete(stream) - @router.on_unsubscribe(self, stream) - end - end - send(:success, message: "unsubscribed", streams: streams.keys) - end - - def handle(msg) - return if msg.to_s.empty? - - data = JSON.parse(msg) - case data["event"] - when "subscribe" - subscribe(data["streams"]) - when "unsubscribe" - unsubscribe(data["streams"]) - end - rescue JSON::ParserError => e - logger.debug { "#{e}, msg: `#{msg}`" } - end - - def handshake(authenticator, hs) - query = URI.decode_www_form(hs.query_string) - subscribe(query.map {|item| item.last if item.first == "stream" }) - logger.debug "WebSocket connection openned" - headers = hs.headers_downcased - return unless headers.key?("authorization") - - authorized, payload = authenticate(authenticator, headers["authorization"]) - - if !authorized - logger.debug "Authentication failed for UID:#{payload[:uid]}" - raise EM::WebSocket::HandshakeError, "Authorization failed" - else - @user = payload[:uid] - @authorized = true - logger.debug "User #{@user} authenticated #{@streams}" - end - end - end -end diff --git a/lib/peatio/ranger/events.rb b/lib/peatio/ranger/events.rb deleted file mode 100644 index fcc5d07..0000000 --- a/lib/peatio/ranger/events.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Ranger - class Events - def self.publish(type, id, event, payload, opts={}) - ex_name = opts[:ex_name] || "peatio.events.ranger" - @client ||= Peatio::MQ::Client.new - @client.publish(ex_name, type, id, event, payload) - end - end -end diff --git a/lib/peatio/ranger/router.rb b/lib/peatio/ranger/router.rb deleted file mode 100644 index 697d80f..0000000 --- a/lib/peatio/ranger/router.rb +++ /dev/null @@ -1,234 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Ranger - class Router - attr_reader :connections - attr_reader :connections_by_userid - attr_reader :streams_sockets - attr_reader :logger - - class ConnectionArray < Array - def delete(connection) - delete_if do |c| - c.id == connection.id - end - end - end - - def initialize(prometheus=nil) - @connections = {} - @connections_by_userid = {} - @streams_sockets = {} - @logger = Peatio::Logger.logger - @stores = {} - init_metrics(prometheus) - end - - def init_metrics(prometheus) - return unless prometheus - - @prometheus = prometheus - @metric_connections_total = @prometheus.counter( - :ranger_connections_total, - docstring: "Total number of connections to ranger from the start", - labels: [:auth] - ) - @metric_connections_current = @prometheus.gauge( - :ranger_connections_current, - docstring: "Current number of connections to ranger", - labels: [:auth] - ) - @metric_subscriptions_current = @prometheus.gauge( - :ranger_subscriptions_current, - docstring: "Current number of streams subscriptions to ranger", - labels: [:stream] - ) - end - - def snapshot?(stream) - stream.end_with?("-snap") - end - - def increment?(stream) - stream.end_with?("-inc") - end - - def storekey(stream) - stream.gsub(/-(snap|inc)$/, "") - end - - def stats - [ - "==== Metrics ====", - "ranger_connections_total{auth=\"public\"}: %d" % [@metric_connections_total.get(labels: {auth: "public"})], - "ranger_connections_total{auth=\"private\"}: %d" % [@metric_connections_total.get(labels: {auth: "private"})], - "ranger_connections_current{auth=\"public\"}: %d" % [@metric_connections_current.get(labels: {auth: "public"})], - "ranger_connections_current{auth=\"private\"}: %d" % [@metric_connections_current.get(labels: {auth: "private"})], - "ranger_subscriptions_current: %d" % [compute_streams_subscriptions()], - "ranger_streams_kinds: %d" % [compute_streams_kinds()], - ].join("\n") - end - - def debug - [ - "==== Debug ====", - "connections: %s" % [@connections.inspect], - "connections_by_userid: %s" % [@connections_by_userid], - "streams_sockets: %s" % [@streams_sockets], - ].join("\n") - end - - def compute_connections_all - @connections.size - end - - def compute_connections_private - @connections_by_userid.each_value.map(&:size).reduce(0, :+) - end - - def compute_stream_subscriptions(stream) - @streams_sockets[stream]&.size || 0 - end - - def compute_streams_subscriptions - @streams_sockets.each_value.map(&:size).reduce(0, :+) - end - - def compute_streams_kinds - @streams_sockets.size - end - - def sanity_check_metrics_connections - return unless @metric_connections_current - - connections_current_all = @metric_connections_current.values.values.reduce(0, :+) - return if connections_current_all == compute_connections_all() - - logger.warn "slip detected in metric_connections_current, recalculating" - connections_current_private = compute_connections_private() - @metric_connections_current.set(connections_current_private, labels: {auth: "private"}) - @metric_connections_current.set(compute_connections_all() - connections_current_private, labels: {auth: "public"}) - end - - def on_connection_open(connection) - @connections[connection.id] = connection - unless connection.authorized - @metric_connections_current&.increment(labels: {auth: "public"}) - @metric_connections_total&.increment(labels: {auth: "public"}) - return - end - @metric_connections_current&.increment(labels: {auth: "private"}) - @metric_connections_total&.increment(labels: {auth: "private"}) - - @connections_by_userid[connection.user] ||= ConnectionArray.new - @connections_by_userid[connection.user] << connection - end - - def on_connection_close(connection) - @connections.delete(connection.id) - connection.streams.keys.each do |stream| - on_unsubscribe(connection, stream) - end - - unless connection.authorized - @metric_connections_current&.decrement(labels: {auth: "public"}) - sanity_check_metrics_connections - return - end - @metric_connections_current&.decrement(labels: {auth: "private"}) - - @connections_by_userid[connection.user].delete(connection) - @connections_by_userid.delete(connection.user) \ - if @connections_by_userid[connection.user].empty? - sanity_check_metrics_connections - end - - def on_subscribe(connection, stream) - @streams_sockets[stream] ||= ConnectionArray.new - @streams_sockets[stream] << connection - send_snapshot_and_increments(connection, storekey(stream)) if increment?(stream) - @metric_subscriptions_current&.set(compute_stream_subscriptions(stream), labels: {stream: stream}) - end - - def send_snapshot_and_increments(connection, key) - return unless @stores[key] - return unless @stores[key][:snapshot] - - connection.send_raw(@stores[key][:snapshot]) - @stores[key][:increments]&.each {|inc| connection.send_raw(inc) } - end - - def on_unsubscribe(connection, stream) - return unless @streams_sockets[stream] - - @streams_sockets[stream].delete(connection) - @streams_sockets.delete(stream) if @streams_sockets[stream].empty? - @metric_subscriptions_current&.set(compute_stream_subscriptions(stream), labels: {stream: stream}) - end - - def send_private_message(user_id, event, payload_decoded) - Array(@connections_by_userid[user_id]).each do |connection| - connection.send(event, payload_decoded) if connection.streams.include?(event) - end - end - - def send_public_message(stream, raw_message) - Array(@streams_sockets[stream]).each do |connection| - connection.send_raw(raw_message) - end - end - - # - # routing key format: type.id.event - # * `type` can be *public* or *private* - # * `id` can be user id or market id - # * `event` is the event identifier, ex: order_completed, trade, ... - # - def on_message(delivery_info, _metadata, payload) - routing_key = delivery_info.routing_key - if routing_key.count(".") != 2 - logger.error { "invalid routing key from amqp: #{routing_key}" } - return - end - - type, id, event = routing_key.split(".") - payload_decoded = JSON.parse(payload) - - if type == "private" - send_private_message(id, event, payload_decoded) - return - end - - stream = [id, event].join(".") - message = JSON.dump(stream => payload_decoded) - - if snapshot?(event) - key = storekey(stream) - - unless @stores[key] - # Send the snapshot to subscribers of -inc stream if there were no snapshot before - send_public_message("#{key}-inc", message) - end - - @stores[key] = { - snapshot: message, - increments: [], - } - return - end - - if increment?(event) - key = storekey(stream) - - unless @stores[key] - logger.warn { "Discard increment received before snapshot for store:#{key}" } - return - end - - @stores[key][:increments] << message - end - - send_public_message(stream, message) - end - end -end diff --git a/lib/peatio/ranger/web_socket.rb b/lib/peatio/ranger/web_socket.rb deleted file mode 100644 index 131efa0..0000000 --- a/lib/peatio/ranger/web_socket.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -module Peatio::Ranger - def self.run(jwt_public_key, exchange_name, opts={}) - host = opts[:ranger_host] || ENV["RANGER_HOST"] || "0.0.0.0" - port = opts[:ranger_port] || ENV["RANGER_PORT"] || "8081" - - authenticator = Peatio::Auth::JWTAuthenticator.new(jwt_public_key) - - logger = Peatio::Logger.logger - logger.info "Starting the server on port #{port}" - - client = Peatio::MQ::Client.new - router = Peatio::Ranger::Router.new(opts[:registry]) - client.subscribe(exchange_name, &router.method(:on_message)) - - if opts[:display_stats] - EM.add_periodic_timer(opts[:stats_period]) do - Peatio::Logger.logger.info { router.stats } - Peatio::Logger.logger.debug { router.debug } - end - end - - EM::WebSocket.start(host: host, port: port, secure: false) do |socket| - connection = Peatio::Ranger::Connection.new(router, socket, logger) - socket.onopen do |hs| - connection.handshake(authenticator, hs) - router.on_connection_open(connection) - end - - socket.onmessage do |msg| - connection.handle(msg) - end - - socket.onping do |value| - logger.debug { "Received ping: #{value}" } - end - - socket.onclose do - logger.debug { "Websocket connection closed" } - router.on_connection_close(connection) - end - - socket.onerror do |e| - logger.info { "WebSocket Error: #{e.message}" } - end - end - end - - def self.run!(jwt_public_key, exchange_name, opts={}) - metrics_host = opts[:metrics_host] || ENV["METRICS_HOST"] || "0.0.0.0" - metrics_port = opts[:metrics_port] || ENV["METRICS_PORT"] || "8082" - - EM.run do - run(jwt_public_key, exchange_name, opts) - - if opts[:registry] - thin = Rack::Handler.get("thin") - thin.run(Peatio::Metrics::Server.app(opts[:registry]), Host: metrics_host, Port: metrics_port) - end - - trap("INT") do - puts "\nSIGINT received, stopping ranger..." - EM.stop - end - end - end -end diff --git a/lib/peatio/sql/client.rb b/lib/peatio/sql/client.rb deleted file mode 100644 index 3878599..0000000 --- a/lib/peatio/sql/client.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Peatio::Sql - class Client - attr_accessor :client, :config - - def initialize - @config = { - host: ENV["DATABASE_HOST"] || "localhost", - username: ENV["DATABASE_USER"] || "root", - password: ENV["DATABASE_PASS"] || "", - port: ENV["DATABASE_PORT"] || "3306", - database: ENV["DATABASE_NAME"] || "peatio_development", - } - end - - def connect - @client = Mysql2::Client.new(config) - end - end -end diff --git a/lib/peatio/sql/schema.rb b/lib/peatio/sql/schema.rb deleted file mode 100644 index 43ed239..0000000 --- a/lib/peatio/sql/schema.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Peatio::Sql - class Schema - attr_accessor :client - - def initialize(sql_client) - @client = sql_client - end - - def create_database(name) - client.query("CREATE DATABASE IF NOT EXISTS `#{ name }`;") - end - - def create_tables(options = {}) - statements = [] - statements << "DROP TABLE IF EXISTS `operations`;" if options[:drop_if_exists] - statements << <<-EOF -CREATE TABLE IF NOT EXISTS `operations` ( - id INT UNSIGNED NOT NULL AUTO_INCREMENT, - code TINYINT UNSIGNED NOT NULL, - account_id INT UNSIGNED NOT NULL, - reference INT UNSIGNED NOT NULL, - debit DECIMAL(32, 16) NOT NULL, - credit DECIMAL(32, 16) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - PRIMARY KEY (id), - INDEX `balance_key` (account_id, debit, credit) -) ENGINE = InnoDB; -EOF - - statements << "DROP TABLE IF EXISTS `orders`;" if options[:drop_if_exists] - statements << < (https://github.com/mnaichuk) - class Transaction - include ActiveModel::Model - - # List of statuses supported by peatio. - # - # @note Statuses list: - # - # pending - the transaction is unconfirmed in the blockchain or - # wasn't created yet. - # - # success - the transaction is a successfull, - # the transaction amount has been successfully transferred - # - # failed - the transaction is failed in the blockchain. - - STATUSES = %w[success pending failed].freeze - - DEFAULT_STATUS = 'pending'.freeze - - # @!attribute [rw] hash - # return [String] transaction hash - attr_accessor :hash - - # @!attribute [rw] txout - # return [Integer] transaction number in send-to-many request - attr_accessor :txout - - # @!attribute [rw] to_address - # return [String] transaction recepient address - attr_accessor :to_address - - # @!attribute [rw] amount - # return [Decimal] amount of the transaction - attr_accessor :amount - - # @!attribute [rw] block_number - # return [Integer] transaction block number - attr_accessor :block_number - - # @!attribute [rw] currency_id - # return [String] transaction currency id - attr_accessor :currency_id - - validates :to_address, - :amount, - :currency_id, - :status, - presence: true - - validates :hash, - :block_number, - presence: { if: -> (t){ t.status.failed? || t.status.success? } } - - validates :txout, - presence: { if: -> (t){ t.status.success? } } - - validates :block_number, - numericality: { greater_than_or_equal_to: 0, only_integer: true } - - validates :amount, - numericality: { greater_than_or_equal_to: 0 } - - validates :status, inclusion: { in: STATUSES } - - def initialize(attributes={}) - super - @status = @status.present? ? @status.to_s : DEFAULT_STATUS - end - - # Status for specific transaction. - # - # @!method status - # - # @example - # - # status.failed? # true if transaction status 'failed' - # status.success? # true if transaction status 'success' - def status - @status&.inquiry - end - - def status=(s) - @status = s.to_s - end - end -end diff --git a/lib/peatio/version.rb b/lib/peatio/version.rb deleted file mode 100644 index 6679f19..0000000 --- a/lib/peatio/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module Peatio - VERSION = "0.6.2" -end diff --git a/lib/peatio/wallet/abstract.rb b/lib/peatio/wallet/abstract.rb deleted file mode 100644 index 1879558..0000000 --- a/lib/peatio/wallet/abstract.rb +++ /dev/null @@ -1,172 +0,0 @@ -module Peatio - module Wallet - # @abstract Represents basic wallet interface. - # - # Subclass and override abstract methods to implement - # a peatio plugable wallet. - # Than you need to register your wallet implementation. - # - # @see Bitcoin::Wallet Bitcoin as example of Abstract imlementation. - # - # @example - # - # class MyWallet < Peatio::Abstract::Wallet - # def create_address(options = {}) - # # do something - # end - # ... - # end - # - # # Register MyWallet as peatio plugable wallet. - # Peatio::Wallet.registry[:my_wallet] = MyWallet.new - # - # @author - # Yaroslav Savchuk (https://github.com/ysv) - class Abstract - # Current wallet settings for performing API calls. - # - # @abstract - # - # @!attribute [r] settings - # @return [Hash] current wallet settings. - attr_reader :settings - - # List of configurable settings. - # - # @see #configure - SUPPORTED_SETTINGS = %i[wallet currency].freeze - - - # Abstract constructor. - # - # @abstract - # - # @example - # class MyWallet< Peatio::Abstract::Wallet - # - # # You could customize your wallet by passing features. - # def initialize(my_custom_features = {}) - # @features = my_custom_features - # end - # ... - # end - # - # # Register MyWallet as peatio plugable wallet. - # custom_features = {cash_addr_format: true} - # Peatio::Wallet.registry[:my_wallet] = MyWallet.new(custom_features) - def initialize(*) - abstract_method - end - - # Merges given configuration parameters with defined during initialization - # and returns the result. - # - # @abstract - # - # @param [Hash] settings configurations to use. - # @option settings [Hash] :wallet Wallet settings for performing API calls. - # With :address required key other settings could be customized - # using Wallet#settings. - # @option settings [Array] :currencies List of currency hashes - # with :id,:base_factor,:options(deprecated) keys. - # Custom keys could be added by defining them in Currency #options. - # - # @return [Hash] merged settings. - # - # @note Be careful with your wallet state after configure. - # Clean everything what could be related to other wallet configuration. - # E.g. client state. - def configure(settings = {}) - abstract_method - end - - # Performs API call for address creation and returns it. - # - # @abstract - # - # @param [Hash] options - # @options options [String] :uid User UID which requested address creation. - # - # @return [Hash] newly created blockchain address. - # - # @raise [Peatio::Blockchain::ClientError] if error was raised - # on wallet API call. - # - # @example - # { address: :fake_address, - # secret: :changeme, - # details: { uid: account.member.uid } } - def create_address!(options = {}) - abstract_method - end - - # Performs API call for creating transaction and returns updated transaction. - # - # @abstract - # - # @param [Peatio::Transaction] transaction transaction with defined - # to_address, amount & currency_id. - # - # @param [Hash] options - # @options options [String] :subtract_fee Defines if you need to subtract - # fee from amount defined in transaction. - # It means that you need to deduct fee from amount declared in - # transaction and send only remaining amount. - # If transaction amount is 1.0 and estimated fee - # for sending transaction is 0.01 you need to send 0.09 - # so 1.0 (0.9 + 0.1) will be subtracted from wallet balance - # - # @options options [String] custon options for wallet client. - # - # @return [Peatio::Transaction] transaction with updated hash. - # - # @raise [Peatio::Blockchain::ClientError] if error was raised - # on wallet API call. - def create_transaction!(transaction, options = {}) - abstract_method - end - - # Fetches address balance of specific currency. - # - # @note Optional. Don't override this method if your blockchain - # doesn't provide functionality to get balance by address. - # - # @return [BigDecimal] the current address balance. - # - # @raise [Peatio::Blockchain::ClientError,Peatio::Blockchain::UnavailableAddressBalanceError] - # if error was raised on wallet API call ClientError is raised. - # if wallet API call was successful but we can't detect balance - # for address Error is raised. - def load_balance! - raise Peatio::Wallet::UnavailableAddressBalanceError - end - - # Performs API call(s) for preparing for deposit collection. - # E.g deposits ETH for collecting ERC20 tokens in case of Ethereum blockchain. - # - # @note Optional. Override this method only if you need additional step - # before deposit collection. - # - # @param [Peatio::Transaction] deposit_transaction transaction which - # describes received deposit. - # - # @param [Array] spread_transactions result of deposit - # spread between wallets. - # - # @return [Array] transaction created for - # deposit collection preparing. - # By default return empty [Array] - def prepare_deposit_collection!(deposit_transaction, spread_transactions, deposit_currency) - # This method is mostly used for coins which needs additional fees - # to be deposited before deposit collection. - [] - end - - private - - def abstract_method - method_not_implemented - end - end - end -end diff --git a/lib/peatio/wallet/error.rb b/lib/peatio/wallet/error.rb deleted file mode 100644 index b72aaa8..0000000 --- a/lib/peatio/wallet/error.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Peatio - module Wallet - Error = Class.new(StandardError) - - class ClientError < Error - - attr_reader :wrapped_ex - - def initialize(ex_or_string) - @wrapped_ex = nil - - if ex_or_string.respond_to?(:backtrace) - super(ex_or_string.message) - @wrapped_exception = ex_or_string - else - super(ex_or_string.to_s) - end - end - end - - class MissingSettingError < Error - def initialize(key) - super "#{key.capitalize} setting is missing" - end - end - - class UnavailableAddressBalanceError < Error - def initialize(address) - @address = address - end - - def message - "Unable to load #{@address} balance" - end - end - end -end diff --git a/lib/peatio/wallet/registry.rb b/lib/peatio/wallet/registry.rb deleted file mode 100644 index 61346a3..0000000 --- a/lib/peatio/wallet/registry.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "peatio/adapter_registry" - -module Peatio - module Wallet - - VERSION = "1.0.0".freeze - - class << self - def registry - @registry ||= Registry.new - end - end - class Registry < Peatio::AdapterRegistry - end - end -end diff --git a/peatio.gemspec b/peatio-core.gemspec similarity index 77% rename from peatio.gemspec rename to peatio-core.gemspec index 48f304a..ddd8603 100644 --- a/peatio.gemspec +++ b/peatio-core.gemspec @@ -2,11 +2,11 @@ lib = File.expand_path("lib", __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "peatio/version" +require "peatio/core/version" Gem::Specification.new do |spec| - spec.name = "peatio" - spec.version = Peatio::VERSION + spec.name = "peatio-core" + spec.version = Peatio::Core::VERSION spec.authors = ["Louis B.", "Camille M."] spec.email = ["lbellet@heliostech.fr"] @@ -24,21 +24,12 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "activemodel", "> 5.2", "<= 6.0.0" - spec.add_dependency "amqp" spec.add_dependency "bunny" - spec.add_dependency "clamp" - spec.add_dependency "em-websocket" - spec.add_dependency "eventmachine" spec.add_dependency "jwt" - spec.add_dependency "mysql2" - spec.add_dependency "prometheus-client" spec.add_dependency "thin" spec.add_development_dependency "bump" spec.add_development_dependency "bundler", "~> 1.16" - spec.add_development_dependency "bunny-mock" - spec.add_development_dependency "em-spec" - spec.add_development_dependency "em-websocket-client" spec.add_development_dependency "irb" spec.add_development_dependency "pry-byebug" spec.add_development_dependency "rake", "~> 10.0" diff --git a/spec/helpers/socket_helper.rb b/spec/helpers/socket_helper.rb deleted file mode 100644 index df3c640..0000000 --- a/spec/helpers/socket_helper.rb +++ /dev/null @@ -1,48 +0,0 @@ -# encoding: UTF-8 -# frozen_string_literal: true - -require "em-spec/rspec" -require "em-websocket" -require "em-websocket-client" -require "websocket" - -WS_HOST = ENV.fetch("WEBSOCKET_HOST", "0.0.0.0") -WS_PORT = ENV.fetch("WEBSOCKET_PORT", "13579") - -class EM::WebSocketClient - attr_accessor :url - attr_accessor :protocol_version - attr_accessor :origin - attr_accessor :headers - - def self.connect(uri, opts={}) - headers = opts[:headers] - p_uri = URI.parse(uri) - conn = EM.connect(p_uri.host, p_uri.port || 80, self) do |c| - c.url = uri - c.protocol_version = opts[:version] - c.origin = opts[:origin] - c.headers = headers - end - end - - def connection_completed - @connect.yield if @connect - @hs = ::WebSocket::Handshake::Client.new(url: @url, - headers: @headers, - origin: @origin, - version: @protocol_version) - send_data @hs.to_s - end -end - -# Start websocket server connection -def ws_server(opts = {}) - EM::WebSocket.run({ host: WS_HOST, port: WS_PORT }.merge(opts)) { |ws| - yield ws if block_given? - } -end - -def ws_connect(query = "", headers = {}) - EM::WebSocketClient.connect("ws://#{WS_HOST}:#{WS_PORT}/#{query}", headers: headers) -end diff --git a/spec/peatio/adapter_registry_spec.rb b/spec/peatio/adapter_registry_spec.rb index df815dc..17d03a2 100644 --- a/spec/peatio/adapter_registry_spec.rb +++ b/spec/peatio/adapter_registry_spec.rb @@ -1,43 +1,43 @@ -WalletAdapter = Class.new(Peatio::Blockchain::Abstract) -BlockchainAdapter = Class.new(Peatio::Wallet::Abstract) +WalletAdapter = Class.new(Peatio::Core::Blockchain::Abstract) +BlockchainAdapter = Class.new(Peatio::Core::Wallet::Abstract) -describe Peatio::AdapterRegistry do +describe Peatio::Core::AdapterRegistry do before do - Peatio::Blockchain.registry.adapters = {} - Peatio::Wallet.registry.adapters = {} + Peatio::Core::Blockchain.registry.adapters = {} + Peatio::Core::Wallet.registry.adapters = {} end it 'registers adapter' do - Peatio::Blockchain.registry[:ethereum] = BlockchainAdapter - expect(Peatio::Blockchain.registry.adapters.count).to eq(1) + Peatio::Core::Blockchain.registry[:ethereum] = BlockchainAdapter + expect(Peatio::Core::Blockchain.registry.adapters.count).to eq(1) - Peatio::Blockchain.registry[:bitcoin] = BlockchainAdapter - expect(Peatio::Blockchain.registry.adapters.count).to eq(2) + Peatio::Core::Blockchain.registry[:bitcoin] = BlockchainAdapter + expect(Peatio::Core::Blockchain.registry.adapters.count).to eq(2) - Peatio::Wallet.registry[:ethereum] = WalletAdapter - expect(Peatio::Wallet.registry.adapters.count).to eq(1) + Peatio::Core::Wallet.registry[:ethereum] = WalletAdapter + expect(Peatio::Core::Wallet.registry.adapters.count).to eq(1) - Peatio::Wallet.registry[:bitcoin] = WalletAdapter - expect(Peatio::Wallet.registry.adapters.count).to eq(2) + Peatio::Core::Wallet.registry[:bitcoin] = WalletAdapter + expect(Peatio::Core::Wallet.registry.adapters.count).to eq(2) end it 'raises error on duplicated name' do - Peatio::Blockchain.registry[:ethereum] = BlockchainAdapter - expect { Peatio::Blockchain.registry[:ethereum] = BlockchainAdapter }.to raise_error(Peatio::AdapterRegistry::DuplicatedAdapterError) + Peatio::Core::Blockchain.registry[:ethereum] = BlockchainAdapter + expect { Peatio::Core::Blockchain.registry[:ethereum] = BlockchainAdapter }.to raise_error(Peatio::Core::AdapterRegistry::DuplicatedAdapterError) - Peatio::Wallet.registry[:ethereum] = WalletAdapter - expect { Peatio::Wallet.registry[:ethereum] = WalletAdapter }.to raise_error(Peatio::AdapterRegistry::DuplicatedAdapterError) + Peatio::Core::Wallet.registry[:ethereum] = WalletAdapter + expect { Peatio::Core::Wallet.registry[:ethereum] = WalletAdapter }.to raise_error(Peatio::Core::AdapterRegistry::DuplicatedAdapterError) end it 'returns adapter for blockchain name' do - Peatio::Blockchain.registry[:ethereum] = BlockchainAdapter - Peatio::Wallet.registry[:ethereum] = WalletAdapter + Peatio::Core::Blockchain.registry[:ethereum] = BlockchainAdapter + Peatio::Core::Wallet.registry[:ethereum] = WalletAdapter - expect(Peatio::Blockchain.registry[:ethereum]).to eq(BlockchainAdapter) - expect(Peatio::Wallet.registry[:ethereum]).to eq(WalletAdapter) + expect(Peatio::Core::Blockchain.registry[:ethereum]).to eq(BlockchainAdapter) + expect(Peatio::Core::Wallet.registry[:ethereum]).to eq(WalletAdapter) end it 'raises error for not registered adapter name' do - expect{ Peatio::Blockchain.registry[:ethereum] }.to raise_error(Peatio::AdapterRegistry::NotRegisteredAdapterError) + expect{ Peatio::Core::Blockchain.registry[:ethereum] }.to raise_error(Peatio::Core::AdapterRegistry::NotRegisteredAdapterError) end end diff --git a/spec/peatio/auth/jwt_authenticator_spec.rb b/spec/peatio/auth/jwt_authenticator_spec.rb index 91821c4..d2d4807 100644 --- a/spec/peatio/auth/jwt_authenticator_spec.rb +++ b/spec/peatio/auth/jwt_authenticator_spec.rb @@ -1,9 +1,9 @@ -describe Peatio::Auth::JWTAuthenticator do +describe Peatio::Core::Auth::JWTAuthenticator do let(:rsa_private) { OpenSSL::PKey::RSA.generate(2048) } let(:rsa_public) { rsa_private.public_key } - let(:auth) { Peatio::Auth::JWTAuthenticator.new(rsa_public, rsa_private) } - let(:invalid_auth) { Peatio::Auth::JWTAuthenticator.new(rsa_public, nil) } + let(:auth) { Peatio::Core::Auth::JWTAuthenticator.new(rsa_public, rsa_private) } + let(:invalid_auth) { Peatio::Core::Auth::JWTAuthenticator.new(rsa_public, nil) } let(:token) { auth.encode(payload) } let :payload do @@ -32,7 +32,7 @@ expect do auth.authenticate!("Bearer #{token}") - end.to raise_error(Peatio::Auth::Error) + end.to raise_error(Peatio::Core::Auth::Error) end it 'will raise exception if no private key given for encoding' do @@ -42,7 +42,7 @@ end it 'will raise exception for invalid jwt (garbage)' do - auth = Peatio::Auth::JWTAuthenticator.new(rsa_public, nil) + auth = Peatio::Core::Auth::JWTAuthenticator.new(rsa_public, nil) expect do invalid_auth.authenticate!('Bearer garbage') @@ -65,7 +65,7 @@ it 'should validate issuer' do expect { auth.authenticate!("Bearer #{token}") - }.to raise_error(Peatio::Auth::Error) + }.to raise_error(Peatio::Core::Auth::Error) end end @@ -85,7 +85,7 @@ it 'should validate audience' do expect do auth.authenticate!("Bearer #{token}") - end.to raise_error(Peatio::Auth::Error) + end.to raise_error(Peatio::Core::Auth::Error) end end @@ -94,7 +94,7 @@ it 'should require JTI' do expect do auth.authenticate!("Bearer #{token}") - end.to raise_error(Peatio::Auth::Error) + end.to raise_error(Peatio::Core::Auth::Error) end end @@ -103,7 +103,7 @@ it 'should not allow JWT' do expect do auth.authenticate!("Bearer #{token}") - end.to raise_error(Peatio::Auth::Error) + end.to raise_error(Peatio::Core::Auth::Error) end end diff --git a/spec/peatio/ranger/router_spec.rb b/spec/peatio/ranger/router_spec.rb deleted file mode 100644 index 609b1a3..0000000 --- a/spec/peatio/ranger/router_spec.rb +++ /dev/null @@ -1,208 +0,0 @@ -# frozen_string_literal: true - -describe Peatio::Ranger::Router do - let(:router) { Peatio::Ranger::Router.new(registry) } - let(:registry) { Prometheus::Client::Registry.new } - let(:anonymous1) { OpenStruct.new(authorized: false, user: nil, id: 1, streams: {}) } - let(:anonymous2) { OpenStruct.new(authorized: false, user: nil, id: 2, streams: {}) } - let(:user1) { OpenStruct.new(authorized: true, user: "user1", id: 3, streams: {}) } - let(:user2) { OpenStruct.new(authorized: true, user: "user2", id: 4, streams: {}) } - - context "snapshot? method" do - it "returns true if the stream name suffixed by -snap" do - expect(router.snapshot?("anything-snap")).to eq(true) - end - - it "returns false if the stream name is not suffixed by -snap" do - expect(router.snapshot?("anything-else")).to eq(false) - end - end - - context "increment? method" do - it "returns true if the stream name suffixed by -inc" do - expect(router.increment?("anything-inc")).to eq(true) - end - - it "returns false if the stream name is not suffixed by -snap" do - expect(router.increment?("anything-else")).to eq(false) - end - end - - context "storekey method" do - it "returns the same store key for snapshots and increment streams" do - expect(router.storekey("anything-inc")).to eq("anything") - expect(router.storekey("anything-snap")).to eq("anything") - end - end - - context "no connection" do - it "shows empty stats" do - expect(router.stats).to eq( - "==== Metrics ====\n" \ - "ranger_connections_total{auth=\"public\"}: 0\n" \ - "ranger_connections_total{auth=\"private\"}: 0\n" \ - "ranger_connections_current{auth=\"public\"}: 0\n" \ - "ranger_connections_current{auth=\"private\"}: 0\n" \ - "ranger_subscriptions_current: 0\n" \ - "ranger_streams_kinds: 0" - ) - end - end - - context "unauthorized users" do - it "registers connections and subscribed streams" do - # users connect - router.on_connection_open(anonymous1) - router.on_connection_open(anonymous2) - - expect(router.connections.size).to eq(2) - expect(router.connections).to eq( - anonymous1.id => anonymous1, - anonymous2.id => anonymous2 - ) - expect(router.connections_by_userid.size).to eq(0) - expect(router.streams_sockets.size).to eq(0) - - # users subscribe to streams - router.on_subscribe(anonymous1, "some-feed") - router.on_subscribe(anonymous2, "some-feed") - router.on_subscribe(anonymous2, "another-feed") - - expect(router.connections.size).to eq(2) - expect(router.connections_by_userid.size).to eq(0) - expect(router.streams_sockets.size).to eq(2) - expect(router.streams_sockets).to eq( - "some-feed" => [anonymous1, anonymous2], - "another-feed" => [anonymous2] - ) - - # users disconnect - anonymous1.streams = {"some-feed" => true} - anonymous2.streams = {"some-feed" => true, "another-feed" => true} - router.on_connection_close(anonymous1) - router.on_connection_close(anonymous2) - expect(router.connections).to eq({}) - expect(router.connections_by_userid).to eq({}) - expect(router.streams_sockets).to eq({}) - end - - it "unsubscribes from streams" do - # users connect - router.on_connection_open(anonymous1) - router.on_connection_open(anonymous2) - - expect(router.connections.size).to eq(2) - expect(router.connections).to eq( - anonymous1.id => anonymous1, - anonymous2.id => anonymous2 - ) - expect(router.connections_by_userid.size).to eq(0) - expect(router.streams_sockets.size).to eq(0) - - # users subscribe to streams - router.on_subscribe(anonymous1, "some-feed") - router.on_subscribe(anonymous2, "some-feed") - router.on_subscribe(anonymous2, "another-feed") - - expect(router.connections.size).to eq(2) - expect(router.connections_by_userid.size).to eq(0) - expect(router.streams_sockets.size).to eq(2) - expect(router.streams_sockets).to eq( - "some-feed" => [anonymous1, anonymous2], - "another-feed" => [anonymous2] - ) - expect(router.stats).to eq( - "==== Metrics ====\n" \ - "ranger_connections_total{auth=\"public\"}: 2\n" \ - "ranger_connections_total{auth=\"private\"}: 0\n" \ - "ranger_connections_current{auth=\"public\"}: 2\n" \ - "ranger_connections_current{auth=\"private\"}: 0\n" \ - "ranger_subscriptions_current: 3\n" \ - "ranger_streams_kinds: 2" - ) - - # users unsubscribe - router.on_unsubscribe(anonymous1, "some-feed") - router.on_unsubscribe(anonymous2, "some-feed") - router.on_unsubscribe(anonymous2, "another-feed") - expect(router.connections.size).to eq(2) - expect(router.connections_by_userid).to eq({}) - expect(router.streams_sockets).to eq({}) - end - end - - context "authorized users" do - it "registers connections, subscribed streams and connections by users id" do - # users connect - router.on_connection_open(user1) - router.on_connection_open(user2) - - expect(router.connections.size).to eq(2) - expect(router.connections).to eq( - user1.id => user1, - user2.id => user2 - ) - expect(router.connections_by_userid.size).to eq(2) - expect(router.connections_by_userid).to eq( - "user1" => [user1], - "user2" => [user2] - ) - - expect(router.streams_sockets.size).to eq(0) - - # users disconnect - router.on_connection_close(user1) - router.on_connection_close(user2) - expect(router.connections).to eq({}) - expect(router.connections_by_userid).to eq({}) - expect(router.streams_sockets).to eq({}) - end - end - - context "incremental objects store" do - let(:cnx1) { double(id: 1) } - let(:delivery_snap) { double(routing_key: "public.abc.object-snap") } - let(:delivery_inc) { double(routing_key: "public.abc.object-inc") } - let(:snapshot1) { JSON.dump([1, 2, 3]) } - let(:snapshot2) { JSON.dump([1, 2, 3, 4]) } - let(:increment1) { JSON.dump([4]) } - let(:increment2) { JSON.dump([5]) } - let(:msg_snapshot1) { JSON.dump("abc.object-snap" => [1, 2, 3]) } - let(:msg_snapshot2) { JSON.dump("abc.object-snap" => [1, 2, 3, 4]) } - let(:msg_increment1) { JSON.dump("abc.object-inc" => [4]) } - let(:msg_increment2) { JSON.dump("abc.object-inc" => [5]) } - - it "sends snapshots and increments on subscribe" do - router.on_message(delivery_snap, nil, snapshot1) - router.on_message(delivery_inc, nil, increment1) - - expect(cnx1).to receive(:send_raw).with(msg_snapshot1).ordered - expect(cnx1).to receive(:send_raw).with(msg_increment1).ordered - - router.on_subscribe(cnx1, "abc.object-inc") - end - - it "sends increments as they come after subscribed and no more snapshot" do - router.on_message(delivery_snap, nil, snapshot1) - router.on_message(delivery_inc, nil, increment1) - - expect(cnx1).to receive(:send_raw).with(msg_snapshot1).ordered - expect(cnx1).to receive(:send_raw).with(msg_increment1).ordered - router.on_subscribe(cnx1, "abc.object-inc") - - expect(cnx1).to receive(:send_raw).with(msg_increment2).ordered - router.on_message(delivery_snap, nil, snapshot2) - router.on_message(delivery_inc, nil, increment2) - end - - it "does not send any increment before a first snapshot is received by ranger" do - expect(cnx1).to receive(:send_raw).with(msg_snapshot2).ordered - expect(cnx1).to receive(:send_raw).with(msg_increment2).ordered - - router.on_message(delivery_inc, nil, increment1) - router.on_subscribe(cnx1, "abc.object-inc") - router.on_message(delivery_snap, nil, snapshot2) - router.on_message(delivery_inc, nil, increment2) - end - end -end diff --git a/spec/peatio/ranger/web_socket_spec.rb b/spec/peatio/ranger/web_socket_spec.rb deleted file mode 100644 index 5257dbb..0000000 --- a/spec/peatio/ranger/web_socket_spec.rb +++ /dev/null @@ -1,333 +0,0 @@ -require "em-spec/rspec" -require "bunny-mock" - -describe Peatio::Ranger do - before(:all) do - Peatio::MQ::Client.connection = BunnyMock.new.start - end - - let(:logger) { Peatio::Logger } - - let(:jwt_private_key) { - OpenSSL::PKey::RSA.generate 2048 - } - - let(:jwt_public_key) { - jwt_private_key.public_key - } - - let(:auth) { - Peatio::Auth::JWTAuthenticator.new(jwt_public_key, jwt_private_key) - } - let(:router) { Peatio::Ranger::Router.new } - let(:ex_name) { "peatio.events.ranger" } - - let(:logger) { - Peatio::Logger.logger - } - - let(:msg_auth_failed) { - "{\"error\":{\"message\":\"Authentication failed.\"}}" - } - - let(:msg_auth_success) { - "{\"success\":{\"message\":\"Authenticated.\"}}" - } - - let(:valid_token_payload) { - { - :iat => 1534242281, - :exp => (Time.now + 3600).to_i, - :sub => "session", - :iss => "barong", - :aud => ["peatio", "barong"], - :jti => "BEF5617B7B2762DDE61702F5", - :uid => "IDE8E2280FD1", - :email => "email@heliostech.fr", - :role => "admin", - :level => 4, - :state => "active" - } - } - - let(:valid_token) { - auth.encode(valid_token_payload) - } - - include EM::SpecHelper - - context "invalid token" do - let!(:client) { Peatio::MQ::Client.new } - - it "denies access" do - em { - EM.add_timer(1) { fail "timeout" } - - ws_server do |socket| - connection = Peatio::Ranger::Connection.new(router, socket, logger) - socket.onopen do |handshake| - connection.handshake(auth, handshake) - end - socket.onmessage do |msg| - connection.handle(msg) - end - socket.onerror do |e| - expect(e.message).to eq "Authorization failed" - logger.error "ranger: WebSocket Error: #{e.message}" - end - end - - EM.add_timer(0.1) do - token = auth.encode("") - wsc = ws_connect("", { "Authorization" => "Bearer #{token}" }) - wsc.disconnect { done } - end - } - end - - end - - context "valid token" do - let!(:client) { Peatio::MQ::Client.new } - - it "allows access" do - em { - EM.add_timer(1) { fail "timeout" } - ws_server do |socket| - connection = Peatio::Ranger::Connection.new(router, socket, logger) - - socket.onopen do |handshake| - connection.handshake(auth, handshake) - end - - socket.onerror do |e| - logger.error "ranger: WebSocket Error: #{e.message}" - end - - socket.onmessage do |msg| - connection.handle(msg) - end - end - - - EM.add_timer(0.1) do - wsc = ws_connect("", { "Authorization" => "Bearer #{valid_token}" }) - wsc.callback { - logger.info "Connected" - expect("ok").to eq "ok" - done - } - wsc.disconnect { done } - end - } - end - end - - context "valid token" do - let!(:client) { Peatio::MQ::Client.new } - - it "sends messages that belong to the user and filtered by stream" do - em { - EM.add_timer(1) { fail "timeout" } - - Peatio::Ranger.run(jwt_public_key, ex_name, ranger_port: 88888) - - EM.add_timer(0.1) do - wsc = ws_connect("/?stream=stream_1&stream=stream_2", { "Authorization" => "Bearer #{valid_token}" }) - - wsc.callback { - client.publish(ex_name, "private", valid_token_payload[:uid], "stream_1", { - key: "stream_1_user_1", - }) - client.publish(ex_name, "private", "SOMEUSER2", "stream_1", { - key: "stream_1_user_2", - }) - client.publish(ex_name, "private", valid_token_payload[:uid], "stream_2", { - key: "stream_2_user_1", - }) - client.publish(ex_name, "private", valid_token_payload[:uid], "stream_3", { - key: "stream_3_user_1", - }) - client.publish(ex_name, "private", valid_token_payload[:uid], "stream_2", { - key: "stream_2_user_1_message_2", - }) - } - - step = 0 - wsc.stream { |msg| - step += 1 - logger.debug "Received: #{msg}" - case step - when 1 - expect(msg.data).to eq '{"stream_1":{"key":"stream_1_user_1"}}' - when 2 - expect(msg.data).to eq '{"stream_2":{"key":"stream_2_user_1"}}' - when 3 - expect(msg.data).to eq '{"stream_2":{"key":"stream_2_user_1_message_2"}}' - done - end - } - wsc.disconnect { done } - end - } - end - - it "sends public messages filtered by stream" do - em { - EM.add_timer(1) { fail "timeout" } - - Peatio::Ranger.run(jwt_public_key, ex_name, ranger_port: 88888) - - EM.add_timer(0.1) do - ws_client = ws_connect("/?stream=btcusd.order") - - ws_client.callback { - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_1", - }) - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_2", - }) - client.publish(ex_name, "public", "btcusd", "trade", { - key: "btcusd_trade_2", - }) - client.publish(ex_name, "public", "ethusd", "order", { - key: "ethusd_order_1", - }) - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_3", - }) - } - - step = 0 - ws_client.stream { |msg| - step += 1 - - case step - when 1 - expect(msg.data).to eq '{"btcusd.order":{"key":"btcusd_order_1"}}' - when 2 - expect(msg.data).to eq '{"btcusd.order":{"key":"btcusd_order_2"}}' - when 3 - expect(msg.data).to eq '{"btcusd.order":{"key":"btcusd_order_3"}}' - done - end - } - ws_client.disconnect { done } - end - - } - end - - it "subscribes to streams dynamically and receive public messages filtered by stream" do - em { - EM.add_timer(1) { fail "timeout" } - - Peatio::Ranger.run(jwt_public_key, ex_name, ranger_port: 88888) - - EM.add_timer(0.1) do - ws_client = ws_connect("/") - - ws_client.callback do - ws_client.send_msg(JSON.dump({event: "subscribe", streams: ["btcusd.order"]})) - - EM.add_timer(0.1) do - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_1", - }) - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_2", - }) - client.publish(ex_name, "public", "btcusd", "trade", { - key: "btcusd_trade_2", - }) - client.publish(ex_name, "public", "ethusd", "order", { - key: "ethusd_order_1", - }) - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_3", - }) - end - end - - step = 0 - ws_client.stream do |msg| - step += 1 - - case step - when 1 - expect(JSON.load(msg.data)).to eq({"success" => {"message" => "subscribed","streams" => ["btcusd.order"]}}) - when 2 - expect(msg.data).to eq '{"btcusd.order":{"key":"btcusd_order_1"}}' - when 3 - expect(msg.data).to eq '{"btcusd.order":{"key":"btcusd_order_2"}}' - when 4 - expect(msg.data).to eq '{"btcusd.order":{"key":"btcusd_order_3"}}' - done - end - end - EM.add_timer(1) do - fail "Timeout" - end - - ws_client.disconnect { done } - end - } - end - - it "unsubscribe a stream stop receiving message for this stream" do - em { - EM.add_timer(1) { fail "timeout" } - - Peatio::Ranger.run(jwt_public_key, ex_name, ranger_port: 88888) - - EM.add_timer(0.1) do - ws_client = ws_connect("/?stream=btcusd.order") - - ws_client.callback do - EM.add_timer(0.1) do - ws_client.send_msg(JSON.dump({event: "unsubscribe", streams: ["btcusd.order"]})) - - EM.add_timer(0.1) do - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_1", - }) - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_2", - }) - client.publish(ex_name, "public", "btcusd", "trade", { - key: "btcusd_trade_2", - }) - client.publish(ex_name, "public", "ethusd", "order", { - key: "ethusd_order_1", - }) - client.publish(ex_name, "public", "btcusd", "order", { - key: "btcusd_order_3", - }) - - EM.add_timer(0.1) do - done - end - end - end - end - - step = 0 - ws_client.stream do |msg| - step += 1 - - case step - when 1 - expect(JSON.load(msg.data)).to eq({"success" => {"message" => "unsubscribed","streams" => []}}) - else - fail "Unexpected message: #{msg}" - end - end - ws_client.disconnect { done } - end - - } - end - - end -end diff --git a/spec/peatio/security/key_generator_spec.rb b/spec/peatio/security/key_generator_spec.rb index 80f479f..57e63ff 100644 --- a/spec/peatio/security/key_generator_spec.rb +++ b/spec/peatio/security/key_generator_spec.rb @@ -1,6 +1,6 @@ -describe Peatio::Security::KeyGenerator do +describe Peatio::Core::Security::KeyGenerator do context "generate key pair" do - let(:key_pair) { Peatio::Security::KeyGenerator.new} + let(:key_pair) { Peatio::Core::Security::KeyGenerator.new} it "should generate a public private rsa key pair" do expect(key_pair.private).to include "-----BEGIN RSA PRIVATE KEY-----" @@ -10,7 +10,7 @@ context "сheck file content" do before do - Peatio::Security::KeyGenerator.new.save("secrets") + Peatio::Core::Security::KeyGenerator.new.save("secrets") end let(:file_for_private_key) { 'secrets/rsa-key' } let(:file_for_public_key) { 'secrets/rsa-key.pub' } diff --git a/spec/peatio/transaction_spec.rb b/spec/peatio/transaction_spec.rb index 955efd1..fe6d24d 100644 --- a/spec/peatio/transaction_spec.rb +++ b/spec/peatio/transaction_spec.rb @@ -1,6 +1,6 @@ require 'pry-byebug' -describe Peatio::Transaction do +describe Peatio::Core::Transaction do context :validations do let(:transaction_attrs) do { hash: 'txid', @@ -18,12 +18,12 @@ end it 'initialize valid transaction' do - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_truthy + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_truthy end it 'validates presence' do required_attrs.each do |attr| - expect(Peatio::Transaction.new(transaction_attrs.except(attr)).valid?).to be_falsey + expect(Peatio::Core::Transaction.new(transaction_attrs.except(attr)).valid?).to be_falsey end end @@ -35,12 +35,12 @@ end it 'initialize valid transaction' do - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_truthy + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_truthy end it 'validates presence' do required_attrs.each do |attr| - expect(Peatio::Transaction.new(transaction_attrs.except(attr)).valid?).to be_falsey + expect(Peatio::Core::Transaction.new(transaction_attrs.except(attr)).valid?).to be_falsey end end end @@ -53,12 +53,12 @@ end it 'initialize valid transaction' do - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_truthy + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_truthy end it 'validates presence' do required_attrs.each do |attr| - expect(Peatio::Transaction.new(transaction_attrs.except(attr)).valid?).to be_falsey + expect(Peatio::Core::Transaction.new(transaction_attrs.except(attr)).valid?).to be_falsey end end end @@ -66,29 +66,29 @@ context :numericality do it 'initialize valid transaction' do - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_truthy + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_truthy end it 'validates amount to be number' do transaction_attrs[:amount] = 'abc' - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_falsey + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_falsey end it 'validates block_number to be number' do transaction_attrs[:block_number] = 'abc' - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_falsey + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_falsey end it 'validates block_number to be integer' do transaction_attrs[:block_number] = 10.10 - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_falsey + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_falsey end end context :inclusion do it 'requires status inclusion in STATUSES' do transaction_attrs[:block_number] = 'other' - expect(Peatio::Transaction.new(transaction_attrs).valid?).to be_falsey + expect(Peatio::Core::Transaction.new(transaction_attrs).valid?).to be_falsey end end end @@ -106,12 +106,12 @@ it 'sets default status to pending' do transaction_attrs[:status] = nil - expect(Peatio::Transaction.new(transaction_attrs).status).to eq 'pending' + expect(Peatio::Core::Transaction.new(transaction_attrs).status).to eq 'pending' end it 'converts status to string' do transaction_attrs[:status] = :success - expect(Peatio::Transaction.new(transaction_attrs).status).to eq 'success' + expect(Peatio::Core::Transaction.new(transaction_attrs).status).to eq 'success' end end @@ -127,7 +127,7 @@ end it 'wraps status to StringInquirer' do - transaction = Peatio::Transaction.new(transaction_attrs) + transaction = Peatio::Core::Transaction.new(transaction_attrs) expect(transaction.status).to be_a(ActiveSupport::StringInquirer) expect(transaction.status).to be_respond_to(:pending?) end diff --git a/spec/peatio_spec.rb b/spec/peatio_spec.rb index 6f68250..ab2b38e 100644 --- a/spec/peatio_spec.rb +++ b/spec/peatio_spec.rb @@ -1,5 +1,5 @@ -describe Peatio do +describe Peatio::Core do it "has a version number" do - expect(Peatio::VERSION).not_to be nil + expect(Peatio::Core::VERSION).not_to be nil end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5e1ad2b..6de6ce7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,5 @@ require "bundler/setup" -require "peatio" - -require_relative "helpers/socket_helper" +require "peatio/core" RSpec.configure do |config| # Enable flags like --only-failures and --next-failure