straight-server 0.2.3 → 1.0.0
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.
- 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] = {}
|