straight-server 0.2.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Gemfile +21 -16
- data/Gemfile.lock +44 -30
- data/Gemfile.travis +15 -16
- data/README.md +66 -47
- data/VERSION +1 -1
- data/db/migrations/011_add_callback_data_to_orders.rb +1 -1
- data/db/migrations/012_add_address_provider.rb +11 -0
- data/db/migrations/013_add_address_derivation_scheme.rb +11 -0
- data/db/migrations/014_pubkey_null_address_provider_not_null.rb +8 -0
- data/db/migrations/015_add_amount_paid_to_orders.rb +11 -0
- data/db/migrations/016_add_new_params_to_orders.rb +13 -0
- data/db/migrations/017_add_test_mode_to_gateways.rb +11 -0
- data/db/migrations/018_add_test_keychain_id_to_gateways.rb +11 -0
- data/db/migrations/019_add_test_pubkey_to_gateways.rb +11 -0
- data/db/migrations/020_add_test_mode_to_orders.rb +11 -0
- data/db/schema.rb +11 -1
- data/lib/straight-server.rb +11 -9
- data/lib/straight-server/config.rb +28 -18
- data/lib/straight-server/gateway.rb +167 -87
- data/lib/straight-server/initializer.rb +13 -7
- data/lib/straight-server/order.rb +39 -17
- data/lib/straight-server/orders_controller.rb +71 -21
- data/lib/straight-server/random_string.rb +3 -13
- data/lib/straight-server/server.rb +3 -4
- data/lib/straight-server/signature_validator.rb +69 -0
- data/lib/straight-server/thread.rb +19 -4
- data/lib/straight-server/throttler.rb +7 -13
- data/lib/tasks/db.rake +1 -1
- data/spec/.straight/config.yml +8 -3
- data/spec/.straight/default_test_last_keychain_id +1 -0
- data/spec/factories.rb +2 -1
- data/spec/lib/gateway_spec.rb +222 -94
- data/spec/lib/initializer_spec.rb +1 -1
- data/spec/lib/order_spec.rb +26 -7
- data/spec/lib/orders_controller_spec.rb +65 -6
- data/spec/lib/signature_validator_spec.rb +72 -0
- data/spec/lib/thread_spec.rb +16 -0
- data/spec/lib/throttle_spec.rb +2 -2
- data/spec/spec_helper.rb +17 -22
- data/straight-server.gemspec +31 -12
- data/templates/config.yml +19 -10
- metadata +52 -11
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/db/schema.rb
CHANGED
@@ -4,7 +4,7 @@ Sequel.migration do
|
|
4
4
|
primary_key :id
|
5
5
|
Integer :confirmations_required, :default=>0, :null=>false
|
6
6
|
Integer :last_keychain_id, :default=>0, :null=>false
|
7
|
-
String :pubkey, :size=>255
|
7
|
+
String :pubkey, :size=>255
|
8
8
|
String :order_class, :size=>255, :null=>false
|
9
9
|
String :secret, :size=>255, :null=>false
|
10
10
|
String :name, :size=>255, :null=>false
|
@@ -19,6 +19,11 @@ Sequel.migration do
|
|
19
19
|
TrueClass :active, :default=>true
|
20
20
|
String :order_counters, :size=>255
|
21
21
|
String :hashed_id, :size=>255
|
22
|
+
String :address_provider, :default=>"Bip32", :size=>255, :null=>false
|
23
|
+
String :address_derivation_scheme, :size=>255
|
24
|
+
TrueClass :test_mode, :default=>false
|
25
|
+
Integer :test_last_keychain_id, :default=>0, :null=>false
|
26
|
+
String :test_pubkey, :size=>255
|
22
27
|
|
23
28
|
index [:hashed_id]
|
24
29
|
index [:id], :unique=>true
|
@@ -41,6 +46,11 @@ Sequel.migration do
|
|
41
46
|
String :payment_id, :size=>255
|
42
47
|
String :description, :size=>255
|
43
48
|
Integer :reused, :default=>0
|
49
|
+
String :callback_data, :size=>255
|
50
|
+
String :amount_paid
|
51
|
+
String :callback_url, :size=>255
|
52
|
+
String :title, :size=>255
|
53
|
+
TrueClass :test_mode, :default=>false
|
44
54
|
|
45
55
|
index [:address]
|
46
56
|
index [:id], :unique=>true
|
data/lib/straight-server.rb
CHANGED
@@ -6,23 +6,25 @@ require 'logmaster'
|
|
6
6
|
require 'openssl'
|
7
7
|
require 'base64'
|
8
8
|
require 'net/http'
|
9
|
+
require 'redis'
|
9
10
|
require 'faye/websocket'
|
10
11
|
Sequel.extension :migration
|
11
12
|
|
12
|
-
|
13
|
-
require_relative 'straight-server/utils/hash_string_to_sym_keys'
|
14
|
-
require_relative 'straight-server/random_string'
|
15
|
-
require_relative 'straight-server/config'
|
16
|
-
require_relative 'straight-server/initializer'
|
17
|
-
require_relative 'straight-server/thread'
|
18
|
-
require_relative 'straight-server/orders_controller'
|
19
|
-
|
20
13
|
module StraightServer
|
21
14
|
|
22
15
|
VERSION = File.read(File.expand_path('../', File.dirname(__FILE__)) + '/VERSION')
|
23
16
|
|
17
|
+
StraightServerError = Class.new(StandardError)
|
18
|
+
|
24
19
|
class << self
|
25
|
-
attr_accessor :db_connection, :logger
|
20
|
+
attr_accessor :db_connection, :redis_connection, :logger
|
26
21
|
end
|
27
22
|
|
28
23
|
end
|
24
|
+
|
25
|
+
require_relative 'straight-server/utils/hash_string_to_sym_keys'
|
26
|
+
require_relative 'straight-server/random_string'
|
27
|
+
require_relative 'straight-server/config'
|
28
|
+
require_relative 'straight-server/initializer'
|
29
|
+
require_relative 'straight-server/thread'
|
30
|
+
require_relative 'straight-server/orders_controller'
|
@@ -1,24 +1,34 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
1
3
|
module StraightServer
|
2
4
|
|
3
|
-
|
5
|
+
# :db
|
6
|
+
# :gateways_source
|
7
|
+
# :gateways
|
8
|
+
# :logmaster
|
9
|
+
# :server_secret
|
10
|
+
# :count_orders
|
11
|
+
# :environment
|
12
|
+
# :redis
|
13
|
+
# :check_order_status_in_db_first
|
14
|
+
# :port
|
15
|
+
# :blockchain_adapters
|
16
|
+
# :expiration_overtime
|
17
|
+
# :reuse_address_orders_threshold
|
18
|
+
# :throttle
|
4
19
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
:expiration_overtime,
|
18
|
-
:reuse_address_orders_threshold,
|
19
|
-
:throttle
|
20
|
+
class << (Config = OpenStruct.new)
|
21
|
+
def [](key_chain)
|
22
|
+
key_chain = key_chain.to_s.split('.')
|
23
|
+
config = self.public_send(key_chain.shift)
|
24
|
+
key_chain.each do |key|
|
25
|
+
if config.kind_of?(Hash)
|
26
|
+
config = config[key] || config[key.to_sym]
|
27
|
+
else
|
28
|
+
return
|
29
|
+
end
|
30
|
+
end
|
31
|
+
config
|
20
32
|
end
|
21
|
-
|
22
33
|
end
|
23
|
-
|
24
34
|
end
|
@@ -1,36 +1,30 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
1
3
|
module StraightServer
|
2
4
|
|
3
5
|
# This module contains common features of Gateway, later to be included
|
4
6
|
# in one of the classes below.
|
5
7
|
module GatewayModule
|
6
8
|
|
7
|
-
# Temporary fix for straight server benchmarking
|
8
|
-
@@redis = StraightServer::Config.redis[:connection] if StraightServer::Config.redis
|
9
9
|
@@websockets = {}
|
10
|
-
|
11
|
-
def fetch_transactions_for(address)
|
12
|
-
try_adapters(@blockchain_adapters, type: 'blockchain') { |b| b.fetch_transactions_for(address) }
|
13
|
-
end
|
14
10
|
|
15
|
-
class
|
16
|
-
class
|
17
|
-
class
|
18
|
-
class
|
19
|
-
class
|
20
|
-
class GatewayInactive < Exception; end
|
21
|
-
class NoBlockchainAdapters < Exception
|
11
|
+
class CallbackUrlBadResponse < StraightServerError; end
|
12
|
+
class WebsocketExists < StraightServerError; end
|
13
|
+
class WebsocketForCompletedOrder < StraightServerError; end
|
14
|
+
class GatewayInactive < StraightServerError; end
|
15
|
+
class NoBlockchainAdapters < StraightServerError
|
22
16
|
def message
|
23
17
|
"No blockchain adapters were found! StraightServer cannot query the blockchain.\n" +
|
24
18
|
"Check your ~/.straight/config.yml file and make sure valid blockchain adapters\n" +
|
25
19
|
"are present."
|
26
20
|
end
|
27
21
|
end
|
28
|
-
class NoWebsocketsForNewGateway <
|
22
|
+
class NoWebsocketsForNewGateway < StraightServerError
|
29
23
|
def message
|
30
24
|
"You're trying to get access to websockets on a Gateway that hasn't been saved yet"
|
31
25
|
end
|
32
26
|
end
|
33
|
-
class OrderCountersDisabled <
|
27
|
+
class OrderCountersDisabled < StraightServerError
|
34
28
|
def message
|
35
29
|
"Please enable order counting in config file! You can do is using the following option:\n\n" +
|
36
30
|
" count_orders: true\n\n" +
|
@@ -41,6 +35,18 @@ module StraightServer
|
|
41
35
|
" db: null\n"
|
42
36
|
end
|
43
37
|
end
|
38
|
+
class NoPubkey < StraightServerError
|
39
|
+
def message
|
40
|
+
"No public key were found! Gateway can't work without it.\n" +
|
41
|
+
"Please provide it in config file or DB."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
class NoTestPubkey < StraightServerError
|
45
|
+
def message
|
46
|
+
"No test public key were found! Gateway can't work in test mode without it.\n" +
|
47
|
+
"Please provide it in config file or DB."
|
48
|
+
end
|
49
|
+
end
|
44
50
|
|
45
51
|
CALLBACK_URL_ATTEMPT_TIMEFRAME = 3600 # seconds
|
46
52
|
|
@@ -56,7 +62,7 @@ module StraightServer
|
|
56
62
|
self.exchange_rate_adapter_names.each do |adapter|
|
57
63
|
begin
|
58
64
|
@exchange_rate_adapters << Straight::ExchangeRate.const_get("#{adapter}Adapter").instance
|
59
|
-
rescue NameError => e
|
65
|
+
rescue NameError => e
|
60
66
|
puts "WARNING: No exchange rate adapter with the name #{adapter} was found!"
|
61
67
|
end
|
62
68
|
end
|
@@ -67,18 +73,15 @@ module StraightServer
|
|
67
73
|
@blockchain_adapters = []
|
68
74
|
StraightServer::Config.blockchain_adapters.each do |a|
|
69
75
|
|
70
|
-
adapter =
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
76
|
+
adapter = Straight::Blockchain.const_get("#{a}Adapter")
|
77
|
+
next unless adapter
|
78
|
+
begin
|
79
|
+
main_url = StraightServer::Config.__send__("#{a.downcase}_url") rescue next
|
80
|
+
test_url = StraightServer::Config.__send__("#{a.downcase}_test_url") rescue nil
|
81
|
+
@blockchain_adapters << adapter.mainnet_adapter(main_url: main_url, test_url: test_url)
|
82
|
+
rescue ArgumentError
|
83
|
+
@blockchain_adapters << adapter.mainnet_adapter
|
79
84
|
end
|
80
|
-
|
81
|
-
@blockchain_adapters << adapter.mainnet_adapter if adapter
|
82
85
|
end
|
83
86
|
raise NoBlockchainAdapters if @blockchain_adapters.empty?
|
84
87
|
end
|
@@ -89,9 +92,11 @@ module StraightServer
|
|
89
92
|
@order_callbacks = [
|
90
93
|
lambda do |order|
|
91
94
|
StraightServer::Thread.new do
|
92
|
-
send_callback_http_request order
|
93
95
|
send_order_to_websocket_client order
|
94
96
|
end
|
97
|
+
StraightServer::Thread.new do
|
98
|
+
send_callback_http_request order
|
99
|
+
end
|
95
100
|
end
|
96
101
|
]
|
97
102
|
end
|
@@ -99,10 +104,20 @@ module StraightServer
|
|
99
104
|
def initialize_status_check_schedule
|
100
105
|
@status_check_schedule = Straight::GatewayModule::DEFAULT_STATUS_CHECK_SCHEDULE
|
101
106
|
end
|
107
|
+
|
108
|
+
def initialize_network
|
109
|
+
BTC::Network.default = test_mode ? BTC::Network.testnet : BTC::Network.mainnet
|
110
|
+
end
|
102
111
|
#
|
103
112
|
############# END OF Initializers methods ##################################################
|
104
|
-
|
105
|
-
|
113
|
+
|
114
|
+
def fetch_transactions_for(address)
|
115
|
+
super
|
116
|
+
rescue Straight::Blockchain::Adapter::BitcoinAddressInvalid => e
|
117
|
+
StraightServer.logger.warn "Address seems to be invalid, ignoring it. #{e.message}"
|
118
|
+
return []
|
119
|
+
end
|
120
|
+
|
106
121
|
# Creates a new order and saves into the DB. Checks if the MD5 hash
|
107
122
|
# is correct first.
|
108
123
|
def create_order(attrs={})
|
@@ -110,43 +125,53 @@ module StraightServer
|
|
110
125
|
raise GatewayInactive unless self.active
|
111
126
|
|
112
127
|
StraightServer.logger.info "Creating new order with attrs: #{attrs}"
|
113
|
-
signature = attrs.delete(:signature)
|
114
|
-
if !check_signature || sign_with_secret(attrs[:keychain_id]) == signature
|
115
|
-
raise InvalidOrderId if check_signature && (attrs[:keychain_id].nil? || attrs[:keychain_id].to_i <= 0)
|
116
|
-
|
117
|
-
# If we decide to reuse the order, we simply need to supply the
|
118
|
-
# keychain_id that was used in the order we're reusing.
|
119
|
-
# The address will be generated correctly.
|
120
|
-
if reused_order = find_reusable_order
|
121
|
-
attrs[:keychain_id] = reused_order.keychain_id
|
122
|
-
end
|
123
128
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
order.id = attrs[:id].to_i if attrs[:id]
|
131
|
-
order.data = attrs[:data] if attrs[:data]
|
132
|
-
order.callback_data = attrs[:callback_data] if attrs[:callback_data]
|
133
|
-
order.gateway = self
|
134
|
-
order.reused = reused_order.reused + 1 if reused_order
|
135
|
-
order.save
|
136
|
-
|
137
|
-
self.update_last_keychain_id(attrs[:keychain_id]) unless order.reused > 0
|
138
|
-
self.save
|
139
|
-
StraightServer.logger.info "Order #{order.id} created: #{order.to_h}"
|
140
|
-
order
|
141
|
-
else
|
142
|
-
StraightServer.logger.warn "Invalid signature, cannot create an order for gateway (#{id})"
|
143
|
-
raise InvalidSignature
|
129
|
+
# If we decide to reuse the order, we simply need to supply the
|
130
|
+
# keychain_id that was used in the order we're reusing.
|
131
|
+
# The address will be generated correctly.
|
132
|
+
if reused_order = find_reusable_order
|
133
|
+
attrs[:keychain_id] = reused_order.keychain_id
|
134
|
+
|
144
135
|
end
|
136
|
+
|
137
|
+
attrs[:keychain_id] = nil if attrs[:keychain_id] == ''
|
138
|
+
|
139
|
+
|
140
|
+
order = new_order(
|
141
|
+
amount: (attrs[:amount] && attrs[:amount].to_f),
|
142
|
+
keychain_id: attrs[:keychain_id] || get_next_last_keychain_id,
|
143
|
+
currency: attrs[:currency],
|
144
|
+
btc_denomination: attrs[:btc_denomination]
|
145
|
+
)
|
146
|
+
|
147
|
+
order.id = attrs[:id].to_i if attrs[:id]
|
148
|
+
order.data = attrs[:data] if attrs[:data]
|
149
|
+
order.callback_data = attrs[:callback_data] if attrs[:callback_data]
|
150
|
+
order.title = attrs[:title] if attrs[:title]
|
151
|
+
order.callback_url = attrs[:callback_url] if attrs[:callback_url]
|
152
|
+
order.gateway = self
|
153
|
+
order.test_mode = test_mode
|
154
|
+
order.description = attrs[:description]
|
155
|
+
order.reused = reused_order.reused + 1 if reused_order
|
156
|
+
order.save
|
157
|
+
|
158
|
+
self.update_last_keychain_id(attrs[:keychain_id]) unless order.reused > 0
|
159
|
+
self.save
|
160
|
+
StraightServer.logger.info "Order #{order.id} created: #{order.to_h}"
|
161
|
+
order
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_next_last_keychain_id
|
165
|
+
self.test_mode ? self.test_last_keychain_id + 1 : self.last_keychain_id + 1
|
145
166
|
end
|
146
167
|
|
168
|
+
# TODO: make it pretty
|
147
169
|
def update_last_keychain_id(new_value=nil)
|
148
|
-
|
149
|
-
|
170
|
+
if self.test_mode
|
171
|
+
new_value ? self.test_last_keychain_id = new_value : self.test_last_keychain_id += 1
|
172
|
+
else
|
173
|
+
new_value ? self.last_keychain_id = new_value : self.last_keychain_id += 1
|
174
|
+
end
|
150
175
|
end
|
151
176
|
|
152
177
|
def add_websocket_for_order(ws, order)
|
@@ -198,18 +223,19 @@ module StraightServer
|
|
198
223
|
paid: get_order_counter(:paid),
|
199
224
|
underpaid: get_order_counter(:underpaid),
|
200
225
|
overpaid: get_order_counter(:overpaid),
|
201
|
-
expired: get_order_counter(:expired)
|
226
|
+
expired: get_order_counter(:expired),
|
227
|
+
canceled: get_order_counter(:canceled),
|
202
228
|
}
|
203
229
|
end
|
204
230
|
|
205
231
|
def get_order_counter(counter_name)
|
206
232
|
raise OrderCountersDisabled unless StraightServer::Config.count_orders
|
207
|
-
|
233
|
+
StraightServer.redis_connection.get("#{StraightServer::Config.redis[:prefix]}:gateway_#{id}:#{counter_name}_orders_counter").to_i || 0
|
208
234
|
end
|
209
235
|
|
210
236
|
def increment_order_counter!(counter_name, by=1)
|
211
237
|
raise OrderCountersDisabled unless StraightServer::Config.count_orders
|
212
|
-
|
238
|
+
StraightServer.redis_connection.incrby("#{StraightServer::Config.redis[:prefix]}:gateway_#{id}:#{counter_name}_orders_counter", by)
|
213
239
|
end
|
214
240
|
|
215
241
|
# If we have more than Config.reuse_address_orders_threshold i a row for this gateway,
|
@@ -234,33 +260,38 @@ module StraightServer
|
|
234
260
|
private
|
235
261
|
|
236
262
|
# Tries to send a callback HTTP request to the resource specified
|
237
|
-
# in the #callback_url.
|
263
|
+
# in the #callback_url. Use #callback_url given to Order if it exist, otherwise default.
|
264
|
+
# If it fails for any reason, it keeps trying for an hour (3600 seconds)
|
238
265
|
# making 10 http requests, each delayed by twice the time the previous one was delayed.
|
239
266
|
# This method is supposed to be running in a separate thread.
|
240
267
|
def send_callback_http_request(order, delay: 5)
|
241
|
-
|
242
|
-
|
243
|
-
StraightServer.logger.info "Attempting to send request to the callback url for order #{order.id} to #{callback_url}..."
|
268
|
+
url = order.callback_url || self.callback_url
|
269
|
+
return if url.to_s.empty?
|
244
270
|
|
245
271
|
# Composing the request uri here
|
246
|
-
|
247
|
-
|
248
|
-
|
272
|
+
callback_data = order.callback_data ? "&callback_data=#{CGI.escape(order.callback_data)}" : ''
|
273
|
+
uri = URI.parse("#{url}#{url.include?('?') ? '&' : '?'}#{order.to_http_params}#{callback_data}")
|
274
|
+
|
275
|
+
StraightServer.logger.info "Attempting callback for order #{order.id}: #{uri.to_s}"
|
249
276
|
|
250
277
|
begin
|
251
|
-
|
278
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
279
|
+
request.add_field 'X-Signature', SignatureValidator.signature(method: 'GET', request_uri: uri.request_uri, secret: secret, nonce: nil, body: nil)
|
280
|
+
response = Net::HTTP.new(uri.host, uri.port).start do |http|
|
281
|
+
http.request request
|
282
|
+
end
|
252
283
|
order.callback_response = { code: response.code, body: response.body }
|
253
284
|
order.save
|
254
285
|
raise CallbackUrlBadResponse unless response.code.to_i == 200
|
255
|
-
rescue
|
286
|
+
rescue => ex
|
256
287
|
if delay < CALLBACK_URL_ATTEMPT_TIMEFRAME
|
257
288
|
sleep(delay)
|
258
289
|
send_callback_http_request(order, delay: delay*2)
|
259
290
|
else
|
260
|
-
StraightServer.logger.warn "Callback request for order #{order.id} failed, see order's #callback_response field for details"
|
291
|
+
StraightServer.logger.warn "Callback request for order #{order.id} failed with #{ex.inspect}, see order's #callback_response field for details"
|
261
292
|
end
|
262
293
|
end
|
263
|
-
|
294
|
+
|
264
295
|
StraightServer.logger.info "Callback request for order #{order.id} performed successfully"
|
265
296
|
end
|
266
297
|
|
@@ -287,7 +318,7 @@ module StraightServer
|
|
287
318
|
# Return the row of expired orders - which is not enough to trigger a reuse
|
288
319
|
# (the triger is in the #find_reusable_order method, which calls this one).
|
289
320
|
def find_expired_orders_row
|
290
|
-
|
321
|
+
|
291
322
|
orders = []
|
292
323
|
row = nil
|
293
324
|
offset = 0
|
@@ -298,7 +329,7 @@ module StraightServer
|
|
298
329
|
row.reject! do |o|
|
299
330
|
reject = false
|
300
331
|
row.each do |o2|
|
301
|
-
reject = true if o.keychain_id == o2.keychain_id && o.reused < o2.reused
|
332
|
+
reject = true if o.keychain_id == o2.keychain_id && o.reused < o2.reused
|
302
333
|
end
|
303
334
|
reject
|
304
335
|
end
|
@@ -330,10 +361,8 @@ module StraightServer
|
|
330
361
|
include GatewayModule
|
331
362
|
plugin :timestamps, create: :created_at, update: :updated_at
|
332
363
|
plugin :serialization, :marshal, :exchange_rate_adapter_names
|
333
|
-
plugin :serialization, :marshal
|
334
364
|
plugin :after_initialize
|
335
365
|
|
336
|
-
|
337
366
|
def self.find_by_hashed_id(s)
|
338
367
|
self.where(hashed_id: s).first
|
339
368
|
end
|
@@ -347,6 +376,8 @@ module StraightServer
|
|
347
376
|
def before_create
|
348
377
|
super
|
349
378
|
encrypt_secret
|
379
|
+
self.test_mode ||= false
|
380
|
+
self.test_last_keychain_id ||= 0
|
350
381
|
end
|
351
382
|
|
352
383
|
def before_update
|
@@ -367,8 +398,15 @@ module StraightServer
|
|
367
398
|
initialize_exchange_rate_adapters
|
368
399
|
initialize_blockchain_adapters
|
369
400
|
initialize_status_check_schedule
|
401
|
+
initialize_network
|
370
402
|
end
|
371
|
-
|
403
|
+
|
404
|
+
def validate
|
405
|
+
super
|
406
|
+
errors.add(:pubkey, "Please provide public key") if pubkey_missing?
|
407
|
+
errors.add(:test_pubkey, "Please provide test public key if you activate test mode") if test_pubkey_missing?
|
408
|
+
end
|
409
|
+
|
372
410
|
# We cannot allow to store gateway secret in a DB plaintext, this would be completetly unsecure.
|
373
411
|
# Althougth we use symmetrical encryption here and store the encryption key in the
|
374
412
|
# server's in a special file (~/.straight/server_secret), which in turn can also be stolen,
|
@@ -393,9 +431,9 @@ module StraightServer
|
|
393
431
|
raise "cipher.iv cannot be nil" unless iv
|
394
432
|
|
395
433
|
encrypted = cipher.update(self[:secret]) << cipher.final()
|
396
|
-
base64_encrypted = Base64.strict_encode64(encrypted).encode('utf-8')
|
434
|
+
base64_encrypted = Base64.strict_encode64(encrypted).encode('utf-8')
|
397
435
|
result = "#{iv}:#{base64_encrypted}"
|
398
|
-
|
436
|
+
|
399
437
|
# Check whether we can decrypt. It should not be possible to encrypt the
|
400
438
|
# gateway secret unless we are sure we can decrypt it.
|
401
439
|
if decrypt_secret(result) == self[:secret]
|
@@ -405,6 +443,24 @@ module StraightServer
|
|
405
443
|
end
|
406
444
|
end
|
407
445
|
|
446
|
+
def address_provider_type
|
447
|
+
self[:address_provider] ? self[:address_provider].to_sym : :Bip32
|
448
|
+
end
|
449
|
+
|
450
|
+
def address_provider
|
451
|
+
Kernel.const_get("Straight::AddressProvider::#{address_provider_type}").new(self)
|
452
|
+
end
|
453
|
+
|
454
|
+
def disable_test_mode!
|
455
|
+
self[:test_mode] = false
|
456
|
+
save(columns: 'test_mode')
|
457
|
+
end
|
458
|
+
|
459
|
+
def enable_test_mode!
|
460
|
+
self[:test_mode] = true
|
461
|
+
save(columns: 'test_mode')
|
462
|
+
end
|
463
|
+
|
408
464
|
private
|
409
465
|
|
410
466
|
def decrypt_secret(encrypted_field=self[:secret])
|
@@ -433,7 +489,7 @@ module StraightServer
|
|
433
489
|
attr_accessor :secret
|
434
490
|
|
435
491
|
# This is used to generate the next address to accept payments
|
436
|
-
attr_accessor :last_keychain_id
|
492
|
+
attr_accessor :last_keychain_id, :test_last_keychain_id
|
437
493
|
|
438
494
|
# If set to false, doesn't require an unique id of the order along with
|
439
495
|
# the signed md5 hash of that id + secret to be passed into the #create_order method.
|
@@ -451,7 +507,7 @@ module StraightServer
|
|
451
507
|
|
452
508
|
attr_accessor :exchange_rate_adapter_names
|
453
509
|
attr_accessor :orders_expiration_period
|
454
|
-
|
510
|
+
|
455
511
|
# This affects whether it is possible to create a new order with the gateway.
|
456
512
|
# If it's set to false, then it won't be possible to create a new order, but
|
457
513
|
# it will keep checking on the existing ones.
|
@@ -466,6 +522,12 @@ module StraightServer
|
|
466
522
|
initialize_exchange_rate_adapters
|
467
523
|
initialize_blockchain_adapters
|
468
524
|
initialize_status_check_schedule
|
525
|
+
initialize_network
|
526
|
+
end
|
527
|
+
|
528
|
+
def validate_config
|
529
|
+
raise NoPubkey if pubkey_missing?
|
530
|
+
raise NoTestPubkey if test_pubkey_missing?
|
469
531
|
end
|
470
532
|
|
471
533
|
# Because this is a config based gateway, we only save last_keychain_id
|
@@ -478,7 +540,7 @@ module StraightServer
|
|
478
540
|
# If the file doesn't exist, we create it. Later, whenever an attribute is updated,
|
479
541
|
# we save it to the file.
|
480
542
|
def load_last_keychain_id!
|
481
|
-
@last_keychain_id_file ||=
|
543
|
+
@last_keychain_id_file ||= build_keychain_path
|
482
544
|
if File.exists?(@last_keychain_id_file)
|
483
545
|
self.last_keychain_id = File.read(@last_keychain_id_file).to_i
|
484
546
|
else
|
@@ -488,10 +550,23 @@ module StraightServer
|
|
488
550
|
end
|
489
551
|
|
490
552
|
def save_last_keychain_id!
|
491
|
-
@last_keychain_id_file ||=
|
553
|
+
@last_keychain_id_file ||= build_keychain_path
|
492
554
|
File.open(@last_keychain_id_file, 'w') {|f| f.write(last_keychain_id) }
|
493
555
|
end
|
494
556
|
|
557
|
+
def build_keychain_path
|
558
|
+
filename = self.test_mode ? "/#{name}_test_last_keychain_id" : "/#{name}_last_keychain_id"
|
559
|
+
StraightServer::Initializer::ConfigDir.path + filename
|
560
|
+
end
|
561
|
+
|
562
|
+
def address_provider_type
|
563
|
+
@address_provider ? @address_provider.to_sym : :Bip32
|
564
|
+
end
|
565
|
+
|
566
|
+
def address_provider
|
567
|
+
Kernel.const_get("Straight::AddressProvider::#{address_provider_type}").new(self)
|
568
|
+
end
|
569
|
+
|
495
570
|
# This method is a replacement for the Sequel's model one used in DB version of the gateway
|
496
571
|
# and it finds gateways using the index of @@gateways Array.
|
497
572
|
def self.find_by_id(id)
|
@@ -509,6 +584,7 @@ module StraightServer
|
|
509
584
|
i += 1
|
510
585
|
gateway = self.new
|
511
586
|
gateway.pubkey = attrs['pubkey']
|
587
|
+
gateway.test_pubkey = attrs['test_pubkey']
|
512
588
|
gateway.confirmations_required = attrs['confirmations_required'].to_i
|
513
589
|
gateway.order_class = attrs['order_class']
|
514
590
|
gateway.secret = attrs['secret']
|
@@ -517,9 +593,13 @@ module StraightServer
|
|
517
593
|
gateway.default_currency = attrs['default_currency']
|
518
594
|
gateway.orders_expiration_period = attrs['orders_expiration_period']
|
519
595
|
gateway.active = attrs['active']
|
596
|
+
gateway.address_provider = attrs['address_provider'] || "Bip32"
|
597
|
+
gateway.address_derivation_scheme = attrs['address_derivation_scheme']
|
598
|
+
gateway.test_mode = attrs['test_mode'] || false
|
520
599
|
gateway.name = name
|
521
600
|
gateway.id = i
|
522
601
|
gateway.exchange_rate_adapter_names = attrs['exchange_rate_adapters']
|
602
|
+
gateway.validate_config
|
523
603
|
gateway.initialize_exchange_rate_adapters
|
524
604
|
gateway.load_last_keychain_id!
|
525
605
|
@@websockets[i] = {}
|