honeymaker 0.9.0 → 0.9.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.
- checksums.yaml +4 -4
- data/lib/honeymaker/clients/kraken.rb +18 -1
- data/lib/honeymaker/version.rb +1 -1
- data/test/honeymaker/clients/kraken_client_test.rb +58 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac4aba6f18266cfffed7dfe3939a60198cb6bdb7361818db8f25f435e19d92f9
|
|
4
|
+
data.tar.gz: e0e398092c55d7483ac386a94846fd5eff4abf115feb9dffd59a85c59462da37
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aabbbc8fdd2c884c85770fc21a5862fe05c014860775f44e362e59c7db7c841cf92e74494af5028abdf10f4af70d88dc5fe13cc4fdc1838e359dc44f6f5c6dd5
|
|
7
|
+
data.tar.gz: a6cb39b92b07b6a5d04bb9b5d3adc14f11f7ea045b628c3b7dc09dd7501ddc0b9268bd3b477d8f2aac5072cfd379afe527738899c706b3d60ace82edd91073ad
|
|
@@ -237,8 +237,25 @@ module Honeymaker
|
|
|
237
237
|
end
|
|
238
238
|
end
|
|
239
239
|
|
|
240
|
+
# Kraken rejects any request whose nonce is <= the last nonce it saw for the
|
|
241
|
+
# same API key. We protect against in-process collisions (microsecond bursts,
|
|
242
|
+
# backward clock corrections) by tracking the last issued nonce per API key
|
|
243
|
+
# under a mutex. Cross-process reuse of the same API key (other workers,
|
|
244
|
+
# other containers, other tools) is not protected here — affected users
|
|
245
|
+
# should increase the Nonce Window on their Kraken API key.
|
|
246
|
+
@@nonce_mutex = Mutex.new
|
|
247
|
+
@@last_nonces = {}
|
|
248
|
+
|
|
249
|
+
def self.reset_nonce_state!
|
|
250
|
+
@@nonce_mutex.synchronize { @@last_nonces.clear }
|
|
251
|
+
end
|
|
252
|
+
|
|
240
253
|
def nonce
|
|
241
|
-
(
|
|
254
|
+
key = @api_key ? Digest::SHA256.hexdigest(@api_key) : :__no_api_key__
|
|
255
|
+
@@nonce_mutex.synchronize do
|
|
256
|
+
candidate = (Time.now.utc.to_f * 1_000_000).to_i
|
|
257
|
+
@@last_nonces[key] = [candidate, (@@last_nonces[key] || 0) + 1].max
|
|
258
|
+
end
|
|
242
259
|
end
|
|
243
260
|
|
|
244
261
|
def private_headers(path, body)
|
data/lib/honeymaker/version.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "test_helper"
|
|
|
4
4
|
|
|
5
5
|
class Honeymaker::Clients::KrakenTest < Minitest::Test
|
|
6
6
|
def setup
|
|
7
|
+
Honeymaker::Clients::Kraken.reset_nonce_state! if Honeymaker::Clients::Kraken.respond_to?(:reset_nonce_state!)
|
|
7
8
|
@client = Honeymaker::Clients::Kraken.new(
|
|
8
9
|
api_key: "test_key",
|
|
9
10
|
api_secret: Base64.strict_encode64("test_secret_key_1234567890123456")
|
|
@@ -74,8 +75,65 @@ class Honeymaker::Clients::KrakenTest < Minitest::Test
|
|
|
74
75
|
refute headers.key?(:"API-Key")
|
|
75
76
|
end
|
|
76
77
|
|
|
78
|
+
def test_reset_nonce_state_is_available_for_test_isolation
|
|
79
|
+
assert_respond_to Honeymaker::Clients::Kraken, :reset_nonce_state!
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_nonce_is_strictly_increasing_in_a_tight_loop
|
|
83
|
+
nonces = Array.new(10_000) { @client.send(:nonce) }
|
|
84
|
+
|
|
85
|
+
assert_strictly_increasing nonces
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_nonce_is_strictly_increasing_when_clock_is_frozen
|
|
89
|
+
frozen_time = Time.utc(2026, 1, 1, 12, 0, 0)
|
|
90
|
+
Time.stubs(:now).returns(frozen_time)
|
|
91
|
+
|
|
92
|
+
nonces = Array.new(3) { @client.send(:nonce) }
|
|
93
|
+
|
|
94
|
+
assert_strictly_increasing nonces
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_nonce_is_strictly_increasing_across_clients_with_the_same_api_key
|
|
98
|
+
frozen_time = Time.utc(2026, 1, 1, 12, 0, 0)
|
|
99
|
+
Time.stubs(:now).returns(frozen_time)
|
|
100
|
+
other_client = Honeymaker::Clients::Kraken.new(
|
|
101
|
+
api_key: "test_key",
|
|
102
|
+
api_secret: Base64.strict_encode64("test_secret_key_1234567890123456")
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
nonces = [
|
|
106
|
+
@client.send(:nonce),
|
|
107
|
+
other_client.send(:nonce),
|
|
108
|
+
@client.send(:nonce),
|
|
109
|
+
other_client.send(:nonce)
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
assert_strictly_increasing nonces
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def test_nonce_sequences_are_independent_for_different_api_keys
|
|
116
|
+
frozen_time = Time.utc(2026, 1, 1, 12, 0, 0)
|
|
117
|
+
Time.stubs(:now).returns(frozen_time)
|
|
118
|
+
first_key_client = Honeymaker::Clients::Kraken.new(api_key: "first_key", api_secret: @client.api_secret)
|
|
119
|
+
second_key_client = Honeymaker::Clients::Kraken.new(api_key: "second_key", api_secret: @client.api_secret)
|
|
120
|
+
|
|
121
|
+
first_key_first_nonce = first_key_client.send(:nonce)
|
|
122
|
+
first_key_second_nonce = first_key_client.send(:nonce)
|
|
123
|
+
second_key_first_nonce = second_key_client.send(:nonce)
|
|
124
|
+
|
|
125
|
+
assert_operator first_key_second_nonce, :>, first_key_first_nonce
|
|
126
|
+
assert_equal first_key_first_nonce, second_key_first_nonce
|
|
127
|
+
end
|
|
128
|
+
|
|
77
129
|
private
|
|
78
130
|
|
|
131
|
+
def assert_strictly_increasing(values)
|
|
132
|
+
values.each_cons(2) do |previous, current|
|
|
133
|
+
assert_operator current, :>, previous
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
79
137
|
def stub_connection(method, body)
|
|
80
138
|
response = stub(body: body)
|
|
81
139
|
connection = stub
|