peatio-jruby 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yml +29 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +148 -0
  6. data/.simplecov +17 -0
  7. data/.tool-versions +1 -0
  8. data/.travis.yml +18 -0
  9. data/Gemfile +8 -0
  10. data/Gemfile.lock +198 -0
  11. data/README.md +47 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/peatio +12 -0
  15. data/bin/setup +8 -0
  16. data/lib/peatio.rb +52 -0
  17. data/lib/peatio/adapter_registry.rb +25 -0
  18. data/lib/peatio/auth/error.rb +18 -0
  19. data/lib/peatio/auth/jwt_authenticator.rb +127 -0
  20. data/lib/peatio/block.rb +29 -0
  21. data/lib/peatio/blockchain/abstract.rb +161 -0
  22. data/lib/peatio/blockchain/error.rb +37 -0
  23. data/lib/peatio/blockchain/registry.rb +16 -0
  24. data/lib/peatio/command/base.rb +11 -0
  25. data/lib/peatio/command/db.rb +20 -0
  26. data/lib/peatio/command/inject.rb +13 -0
  27. data/lib/peatio/command/root.rb +14 -0
  28. data/lib/peatio/command/security.rb +29 -0
  29. data/lib/peatio/command/service.rb +40 -0
  30. data/lib/peatio/error.rb +18 -0
  31. data/lib/peatio/executor.rb +64 -0
  32. data/lib/peatio/injectors/peatio_events.rb +240 -0
  33. data/lib/peatio/logger.rb +39 -0
  34. data/lib/peatio/metrics/server.rb +15 -0
  35. data/lib/peatio/mq/client.rb +51 -0
  36. data/lib/peatio/ranger/connection.rb +117 -0
  37. data/lib/peatio/ranger/events.rb +11 -0
  38. data/lib/peatio/ranger/router.rb +234 -0
  39. data/lib/peatio/ranger/web_socket.rb +68 -0
  40. data/lib/peatio/security/key_generator.rb +26 -0
  41. data/lib/peatio/sql/client.rb +19 -0
  42. data/lib/peatio/sql/schema.rb +72 -0
  43. data/lib/peatio/transaction.rb +122 -0
  44. data/lib/peatio/upstream/base.rb +116 -0
  45. data/lib/peatio/upstream/registry.rb +14 -0
  46. data/lib/peatio/version.rb +3 -0
  47. data/lib/peatio/wallet/abstract.rb +189 -0
  48. data/lib/peatio/wallet/error.rb +37 -0
  49. data/lib/peatio/wallet/registry.rb +16 -0
  50. data/peatio.gemspec +59 -0
  51. metadata +480 -0
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio::Ranger
4
+ def self.run(jwt_public_key, exchange_name, opts={})
5
+ host = opts[:ranger_host] || ENV["RANGER_HOST"] || "0.0.0.0"
6
+ port = opts[:ranger_port] || ENV["RANGER_PORT"] || "8081"
7
+
8
+ authenticator = Peatio::Auth::JWTAuthenticator.new(jwt_public_key)
9
+
10
+ logger = Peatio::Logger.logger
11
+ logger.info "Starting the server on port #{port}"
12
+
13
+ client = Peatio::MQ::Client.new
14
+ router = Peatio::Ranger::Router.new(opts[:registry])
15
+ client.subscribe(exchange_name, &router.method(:on_message))
16
+
17
+ if opts[:display_stats]
18
+ EM.add_periodic_timer(opts[:stats_period]) do
19
+ Peatio::Logger.logger.info { router.stats }
20
+ Peatio::Logger.logger.debug { router.debug }
21
+ end
22
+ end
23
+
24
+ EM::WebSocket.start(host: host, port: port, secure: false) do |socket|
25
+ connection = Peatio::Ranger::Connection.new(router, socket, logger)
26
+ socket.onopen do |hs|
27
+ connection.handshake(authenticator, hs)
28
+ router.on_connection_open(connection)
29
+ end
30
+
31
+ socket.onmessage do |msg|
32
+ connection.handle(msg)
33
+ end
34
+
35
+ socket.onping do |value|
36
+ logger.debug { "Received ping: #{value}" }
37
+ end
38
+
39
+ socket.onclose do
40
+ logger.debug { "Websocket connection closed" }
41
+ router.on_connection_close(connection)
42
+ end
43
+
44
+ socket.onerror do |e|
45
+ logger.error { "WebSocket Error: #{e.message}\n" + e.backtrace.join("\n") }
46
+ end
47
+ end
48
+ end
49
+
50
+ def self.run!(jwt_public_key, exchange_name, opts={})
51
+ metrics_host = opts[:metrics_host] || ENV["METRICS_HOST"] || "0.0.0.0"
52
+ metrics_port = opts[:metrics_port] || ENV["METRICS_PORT"] || "8082"
53
+
54
+ EM.run do
55
+ run(jwt_public_key, exchange_name, opts)
56
+
57
+ if opts[:registry]
58
+ thin = Rack::Handler.get("thin")
59
+ thin.run(Peatio::Metrics::Server.app(opts[:registry]), Host: metrics_host, Port: metrics_port)
60
+ end
61
+
62
+ trap("INT") do
63
+ puts "\nSIGINT received, stopping ranger..."
64
+ EM.stop
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,26 @@
1
+ require 'fileutils'
2
+
3
+ module Peatio::Security
4
+ class KeyGenerator
5
+
6
+ attr_reader :public, :private
7
+
8
+ def initialize
9
+ OpenSSL::PKey::RSA.generate(2048).tap do |pkey|
10
+ @public = pkey.public_key.to_pem
11
+ @private = pkey.to_pem
12
+ end
13
+ end
14
+
15
+ def save(folder)
16
+ FileUtils.mkdir_p(folder) unless File.exists?(folder)
17
+
18
+ write(File.join(folder, 'rsa-key'), @private)
19
+ write(File.join(folder, 'rsa-key.pub'), @public)
20
+ end
21
+
22
+ def write(filename, text)
23
+ File.open(filename, 'w') { |file| file.write(text) }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module Peatio::Sql
2
+ class Client
3
+ attr_accessor :client, :config
4
+
5
+ def initialize
6
+ @config = {
7
+ host: ENV["DATABASE_HOST"] || "localhost",
8
+ username: ENV["DATABASE_USER"] || "root",
9
+ password: ENV["DATABASE_PASS"] || "",
10
+ port: ENV["DATABASE_PORT"] || "3306",
11
+ database: ENV["DATABASE_NAME"] || "peatio_development",
12
+ }
13
+ end
14
+
15
+ def connect
16
+ @client = Mysql2::Client.new(config)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,72 @@
1
+ module Peatio::Sql
2
+ class Schema
3
+ attr_accessor :client
4
+
5
+ def initialize(sql_client)
6
+ @client = sql_client
7
+ end
8
+
9
+ def create_database(name)
10
+ client.query("CREATE DATABASE IF NOT EXISTS `#{ name }`;")
11
+ end
12
+
13
+ def create_tables(options = {})
14
+ statements = []
15
+ statements << "DROP TABLE IF EXISTS `operations`;" if options[:drop_if_exists]
16
+ statements << <<-EOF
17
+ CREATE TABLE IF NOT EXISTS `operations` (
18
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
19
+ code TINYINT UNSIGNED NOT NULL,
20
+ account_id INT UNSIGNED NOT NULL,
21
+ reference INT UNSIGNED NOT NULL,
22
+ debit DECIMAL(32, 16) NOT NULL,
23
+ credit DECIMAL(32, 16) NOT NULL,
24
+ created_at DATETIME NOT NULL,
25
+ updated_at DATETIME NOT NULL,
26
+ PRIMARY KEY (id),
27
+ INDEX `balance_key` (account_id, debit, credit)
28
+ ) ENGINE = InnoDB;
29
+ EOF
30
+
31
+ statements << "DROP TABLE IF EXISTS `orders`;" if options[:drop_if_exists]
32
+ statements << <<EOF
33
+ CREATE TABLE IF NOT EXISTS`orders` (
34
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
35
+ `uid` INT(11) UNSIGNED NOT NULL,
36
+ `bid` VARCHAR(5) NOT NULL,
37
+ `ask` VARCHAR(5) NOT NULL,
38
+ `market` VARCHAR(10) NOT NULL,
39
+ `price` DECIMAL(32,16) DEFAULT NULL,
40
+ `volume` DECIMAL(32,16) NOT NULL,
41
+ `fee` DECIMAL(32,16) NOT NULL DEFAULT '0.0000000000000000',
42
+ `type` TINYINT UNSIGNED NOT NULL,
43
+ `state` TINYINT UNSIGNED NOT NULL,
44
+ `created_at` DATETIME NOT NULL,
45
+ `updated_at` DATETIME NOT NULL,
46
+ PRIMARY KEY (`id`)
47
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
48
+ EOF
49
+
50
+ statements << "DROP TABLE IF EXISTS `trades`;" if options[:drop_if_exists]
51
+ statements << <<EOF
52
+ CREATE TABLE IF NOT EXISTS `trades` (
53
+ `id` int(11) NOT NULL AUTO_INCREMENT,
54
+ `market` varchar(10) NOT NULL,
55
+ `volume` decimal(32,16) NOT NULL,
56
+ `price` decimal(32,16) NOT NULL,
57
+ `ask_id` int(11) NOT NULL,
58
+ `bid_id` int(11) NOT NULL,
59
+ `ask_uid` int(11) NOT NULL,
60
+ `bid_uid` int(11) NOT NULL,
61
+ `created_at` datetime NOT NULL,
62
+ `updated_at` datetime NOT NULL,
63
+ PRIMARY KEY (`id`)
64
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
65
+ EOF
66
+ statements.each do |statement|
67
+ puts statement
68
+ client.query(statement)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,122 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/string/inquiry'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_model'
5
+
6
+ module Peatio #:nodoc:
7
+
8
+ # This class represents blockchain transaction.
9
+ #
10
+ # Using the instant of this class the peatio application will send/recieve
11
+ # income/outcome transactions from a peatio pluggable blockchain.
12
+ #
13
+ # @example
14
+ #
15
+ # Peatio::Transaction.new(
16
+ # {
17
+ # hash: '0x5d0ef9697a2f3ea561c9fbefb48e380a4cf3d26ad2be253177c472fdd0e8b486',
18
+ # txout: 1,
19
+ # to_address: '0x9af4f143cd5ecfba0fcdd863c5ef52d5ccb4f3e5',
20
+ # amount: 0.01,
21
+ # block_number: 7732274,
22
+ # currency_id: 'eth',
23
+ # status: 'success'
24
+ # }
25
+ # )
26
+ #
27
+ # @author
28
+ # Maksym Naichuk <naichuk.maks@gmail.com> (https://github.com/mnaichuk)
29
+ class Transaction
30
+ include ActiveModel::Model
31
+
32
+ # List of statuses supported by peatio.
33
+ #
34
+ # @note Statuses list:
35
+ #
36
+ # pending - the transaction is unconfirmed in the blockchain or
37
+ # wasn't created yet.
38
+ #
39
+ # success - the transaction is a successfull,
40
+ # the transaction amount has been successfully transferred
41
+ #
42
+ # failed - the transaction is failed in the blockchain.
43
+
44
+ STATUSES = %w[success pending failed].freeze
45
+
46
+ DEFAULT_STATUS = 'pending'.freeze
47
+
48
+ # @!attribute [rw] hash
49
+ # return [String] transaction hash
50
+ attr_accessor :hash
51
+
52
+ # @!attribute [rw] txout
53
+ # return [Integer] transaction number in send-to-many request
54
+ attr_accessor :txout
55
+
56
+ # @!attribute [rw] from_address
57
+ # return [Array<String>] transaction source addresses
58
+ attr_accessor :from_addresses
59
+
60
+ # @!attribute [rw] to_address
61
+ # return [String] transaction recepient address
62
+ attr_accessor :to_address
63
+
64
+ # @!attribute [rw] amount
65
+ # return [Decimal] amount of the transaction
66
+ attr_accessor :amount
67
+
68
+ # @!attribute [rw] block_number
69
+ # return [Integer] transaction block number
70
+ attr_accessor :block_number
71
+
72
+ # @!attribute [rw] currency_id
73
+ # return [String] transaction currency id
74
+ attr_accessor :currency_id
75
+
76
+ # @!attribute [rw] options
77
+ # return [JSON] transaction options
78
+ attr_accessor :options
79
+
80
+ validates :to_address,
81
+ :amount,
82
+ :currency_id,
83
+ :status,
84
+ presence: true
85
+
86
+ validates :hash,
87
+ :block_number,
88
+ presence: { if: -> (t){ t.status.failed? || t.status.success? } }
89
+
90
+ validates :txout,
91
+ presence: { if: -> (t){ t.status.success? } }
92
+
93
+ validates :block_number,
94
+ numericality: { greater_than_or_equal_to: 0, only_integer: true }
95
+
96
+ validates :amount,
97
+ numericality: { greater_than_or_equal_to: 0 }
98
+
99
+ validates :status, inclusion: { in: STATUSES }
100
+
101
+ def initialize(attributes={})
102
+ super
103
+ @status = @status.present? ? @status.to_s : DEFAULT_STATUS
104
+ end
105
+
106
+ # Status for specific transaction.
107
+ #
108
+ # @!method status
109
+ #
110
+ # @example
111
+ #
112
+ # status.failed? # true if transaction status 'failed'
113
+ # status.success? # true if transaction status 'success'
114
+ def status
115
+ @status&.inquiry
116
+ end
117
+
118
+ def status=(s)
119
+ @status = s.to_s
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Upstream
5
+ class Base
6
+ DEFAULT_DELAY = 1
7
+ WEBSOCKET_CONNECTION_RETRY_DELAY = 2
8
+
9
+ attr_accessor :logger
10
+
11
+ def initialize(config)
12
+ @host = config["rest"]
13
+ @adapter = config[:faraday_adapter] || :em_synchrony
14
+ @config = config
15
+ @ws_status = false
16
+ @market = config['source']
17
+ @target = config['target']
18
+ @public_trades_cb = []
19
+ @logger = Peatio::Logger.logger
20
+ @peatio_mq = config['amqp']
21
+ mount
22
+ end
23
+
24
+ def mount
25
+ @public_trades_cb << method(:on_trade)
26
+ end
27
+
28
+ def ws_connect
29
+ logger.info { "Websocket connecting to #{@ws_url}" }
30
+ raise "websocket url missing for account #{id}" unless @ws_url
31
+
32
+ @ws = Faye::WebSocket::Client.new(@ws_url)
33
+
34
+ @ws.on(:open) do |_e|
35
+ subscribe_trades(@target, @ws)
36
+ subscribe_orderbook(@target, @ws)
37
+ logger.info { "Websocket connected" }
38
+ end
39
+
40
+ @ws.on(:message) do |msg|
41
+ ws_read_message(msg)
42
+ end
43
+
44
+ @ws.on(:close) do |e|
45
+ @ws = nil
46
+ @ws_status = false
47
+ logger.error "Websocket disconnected: #{e.code} Reason: #{e.reason}"
48
+ Fiber.new do
49
+ EM::Synchrony.sleep(WEBSOCKET_CONNECTION_RETRY_DELAY)
50
+ ws_connect
51
+ end.resume
52
+ end
53
+ end
54
+
55
+ def ws_connect_public
56
+ ws_connect
57
+ end
58
+
59
+ def subscribe_trades(_market, _ws)
60
+ method_not_implemented
61
+ end
62
+
63
+ def subscribe_orderbook(_market, _ws)
64
+ method_not_implemented
65
+ end
66
+
67
+ def ws_read_public_message(msg)
68
+ logger.info { "received public message: #{msg}" }
69
+ end
70
+
71
+ def ws_read_message(msg)
72
+ logger.debug {"received websocket message: #{msg.data}" }
73
+
74
+ object = JSON.parse(msg.data)
75
+ ws_read_public_message(object)
76
+ end
77
+
78
+ def on_trade(trade)
79
+ logger.info { "Publishing trade event: #{trade.inspect}" }
80
+ @peatio_mq.enqueue_event("public", @market, "trades", {trades: [trade]})
81
+ @peatio_mq.publish :trade, trade_json(trade), {
82
+ headers: {
83
+ type: :upstream,
84
+ market: @market,
85
+ }
86
+ }
87
+ end
88
+
89
+ def trade_json(trade)
90
+ trade.deep_symbolize_keys!
91
+ {
92
+ id: trade[:tid],
93
+ price: trade[:price],
94
+ amount: trade[:amount],
95
+ market_id: @market,
96
+ created_at: Time.at(trade[:date]).utc.iso8601,
97
+ taker_type: trade[:taker_type]
98
+ }
99
+ end
100
+
101
+ def notify_public_trade(trade)
102
+ @public_trades_cb.each {|cb| cb&.call(trade) }
103
+ end
104
+
105
+ def to_s
106
+ "Exchange::#{self.class} config: #{@opts}"
107
+ end
108
+
109
+ def build_error(response)
110
+ JSON.parse(response.body)
111
+ rescue StandardError => e
112
+ "Code: #{response.env.status} Message: #{response.env.reason_phrase}"
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Upstream
5
+ class << self
6
+ def registry
7
+ @registry ||= Registry.new
8
+ end
9
+
10
+ class Registry < Peatio::AdapterRegistry
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Peatio
2
+ VERSION = "2.6.2"
3
+ end