pinetwork 0.1.3 → 0.1.4
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/Rakefile +9 -0
- data/lib/errors.rb +13 -0
- data/lib/pinetwork.rb +54 -29
- data/test/a2u_concurrency_test.rb +35 -0
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9cab0d9673b76e3b49f2ccb041f917b9574ca85dedb1c5d4529fd19ada191b2b
|
4
|
+
data.tar.gz: ee2bf09fd869205f4377f7ad72c79897282b419d3551dc72f88facbd8d33ca0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f3279cb6518e666ef5ee59de68af1f98d147f1e159c2af1d9c775915f0d22624a640b2f36654c48f8f9a729e4ace10db4f9bedff721769eb220f82a67ca420a
|
7
|
+
data.tar.gz: f6d002eae9fd88cfb6e6d86958558c6f01dbb69b365cd7799ebdbe601d9d6242fb30ded18353f51f7d7f1bd43992e68d763f7687d6453f094f9ccbfe384d58e5
|
data/Rakefile
ADDED
data/lib/errors.rb
CHANGED
@@ -3,6 +3,7 @@ class ::PiNetwork
|
|
3
3
|
class APIRequestError < StandardError
|
4
4
|
attr_reader :response_body
|
5
5
|
attr_reader :response_status
|
6
|
+
|
6
7
|
def initialize(message, response_status, response_body)
|
7
8
|
super(message)
|
8
9
|
@response_status = response_status
|
@@ -12,6 +13,7 @@ class ::PiNetwork
|
|
12
13
|
|
13
14
|
class PaymentNotFoundError < StandardError
|
14
15
|
attr_reader :payment_id
|
16
|
+
|
15
17
|
def initialize(message, payment_id)
|
16
18
|
super(message)
|
17
19
|
@payment_id = payment_id
|
@@ -28,5 +30,16 @@ class ::PiNetwork
|
|
28
30
|
@txid = txid
|
29
31
|
end
|
30
32
|
end
|
33
|
+
|
34
|
+
class TxSubmissionError < StandardError
|
35
|
+
attr_reader :tx_error_code
|
36
|
+
attr_reader :op_error_codes
|
37
|
+
|
38
|
+
def initialize(tx_error_code, op_error_codes)
|
39
|
+
super(message)
|
40
|
+
@tx_error_code = tx_error_code
|
41
|
+
@op_error_codes = op_error_codes
|
42
|
+
end
|
43
|
+
end
|
31
44
|
end
|
32
45
|
end
|
data/lib/pinetwork.rb
CHANGED
@@ -11,15 +11,21 @@ class PiNetwork
|
|
11
11
|
attr_reader :base_url
|
12
12
|
attr_reader :from_address
|
13
13
|
|
14
|
-
|
14
|
+
BASE_URL = "https://api.minepi.com".freeze
|
15
|
+
MAINNET_HOST = "api.mainnet.minepi.com".freeze
|
16
|
+
TESTNET_HOST = "api.testnet.minepi.com".freeze
|
17
|
+
|
18
|
+
def initialize(api_key:, wallet_private_key:, faraday: Faraday.new, options: {})
|
15
19
|
validate_private_seed_format!(wallet_private_key)
|
16
20
|
@api_key = api_key
|
17
21
|
@account = load_account(wallet_private_key)
|
18
|
-
@base_url = options[:base_url] ||
|
19
|
-
@mainnet_host = options[:mainnet_host] ||
|
20
|
-
@testnet_host = options[:testnet_host] ||
|
22
|
+
@base_url = options[:base_url] || BASE_URL
|
23
|
+
@mainnet_host = options[:mainnet_host] || MAINNET_HOST
|
24
|
+
@testnet_host = options[:testnet_host] || TESTNET_HOST
|
25
|
+
@faraday = faraday
|
21
26
|
|
22
27
|
@open_payments = {}
|
28
|
+
@open_payments_mutex = Mutex.new
|
23
29
|
end
|
24
30
|
|
25
31
|
def get_payment(payment_id)
|
@@ -43,7 +49,7 @@ class PiNetwork
|
|
43
49
|
payment: payment_data,
|
44
50
|
}
|
45
51
|
|
46
|
-
response =
|
52
|
+
response = @faraday.post(
|
47
53
|
base_url + "/v2/payments",
|
48
54
|
request_body.to_json,
|
49
55
|
http_headers,
|
@@ -52,35 +58,39 @@ class PiNetwork
|
|
52
58
|
parsed_response = handle_http_response(response, "An unknown error occurred while creating a payment")
|
53
59
|
|
54
60
|
identifier = parsed_response["identifier"]
|
55
|
-
@
|
61
|
+
@open_payments_mutex.synchronize do
|
62
|
+
@open_payments[identifier] = parsed_response
|
63
|
+
end
|
56
64
|
|
57
65
|
return identifier
|
58
66
|
end
|
59
67
|
|
60
68
|
def submit_payment(payment_id)
|
61
|
-
|
69
|
+
@open_payments_mutex.synchronize do
|
70
|
+
payment = @open_payments[payment_id]
|
62
71
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
72
|
+
if payment.nil? || payment["identifier"] != payment_id
|
73
|
+
payment = get_payment(payment_id)
|
74
|
+
txid = payment["transaction"]&.dig("txid")
|
75
|
+
raise Errors::TxidAlreadyLinkedError.new("This payment already has a linked txid", payment_id, txid) if txid.present?
|
76
|
+
end
|
68
77
|
|
69
|
-
|
70
|
-
|
78
|
+
set_horizon_client(payment["network"])
|
79
|
+
@from_address = payment["from_address"]
|
71
80
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
81
|
+
transaction_data = {
|
82
|
+
amount: payment["amount"],
|
83
|
+
identifier: payment["identifier"],
|
84
|
+
recipient: payment["to_address"]
|
85
|
+
}
|
77
86
|
|
78
|
-
|
79
|
-
|
87
|
+
transaction = build_a2u_transaction(transaction_data)
|
88
|
+
txid = submit_transaction(transaction)
|
80
89
|
|
81
|
-
|
90
|
+
@open_payments.delete(payment_id)
|
82
91
|
|
83
|
-
|
92
|
+
return txid
|
93
|
+
end
|
84
94
|
end
|
85
95
|
|
86
96
|
def complete_payment(payment_id, txid)
|
@@ -92,7 +102,10 @@ class PiNetwork
|
|
92
102
|
http_headers
|
93
103
|
)
|
94
104
|
|
95
|
-
@
|
105
|
+
@open_payments_mutex.synchronize do
|
106
|
+
@open_payments.delete(payment_id)
|
107
|
+
end
|
108
|
+
|
96
109
|
handle_http_response(response, "An unknown error occurred while completing the payment")
|
97
110
|
end
|
98
111
|
|
@@ -103,7 +116,10 @@ class PiNetwork
|
|
103
116
|
http_headers,
|
104
117
|
)
|
105
118
|
|
106
|
-
@
|
119
|
+
@open_payments_mutex.synchronize do
|
120
|
+
@open_payments.delete(payment_id)
|
121
|
+
end
|
122
|
+
|
107
123
|
handle_http_response(response, "An unknown error occurred while cancelling the payment")
|
108
124
|
end
|
109
125
|
|
@@ -131,7 +147,7 @@ class PiNetwork
|
|
131
147
|
|
132
148
|
def handle_http_response(response, unknown_error_message = "An unknown error occurred while making an API request")
|
133
149
|
unless response.status == 200
|
134
|
-
error_message =
|
150
|
+
error_message = extract_error_message(response.body, unknown_error_message)
|
135
151
|
raise Errors::APIRequestError.new(error_message, response.status, response.body)
|
136
152
|
end
|
137
153
|
|
@@ -188,8 +204,13 @@ class PiNetwork
|
|
188
204
|
|
189
205
|
def submit_transaction(transaction)
|
190
206
|
envelope = transaction.to_envelope(self.account.keypair)
|
191
|
-
|
192
|
-
|
207
|
+
begin
|
208
|
+
response = self.client.submit_transaction(tx_envelope: envelope)
|
209
|
+
txid = response._response.body["id"]
|
210
|
+
rescue => error
|
211
|
+
result_codes = error.response&.dig(:body, "extras", "result_codes")
|
212
|
+
raise Errors::TxSubmissionError.new(result_codes&.dig("transaction"), result_codes&.dig("operations"))
|
213
|
+
end
|
193
214
|
end
|
194
215
|
|
195
216
|
def validate_payment_data!(data, options = {})
|
@@ -202,7 +223,11 @@ class PiNetwork
|
|
202
223
|
end
|
203
224
|
|
204
225
|
def validate_private_seed_format!(seed)
|
205
|
-
raise StandardError.new("Private Seed should start with \"S\"") unless seed.upcase.
|
226
|
+
raise StandardError.new("Private Seed should start with \"S\"") unless seed.upcase.start_with?("S")
|
206
227
|
raise StandardError.new("Private Seed should be 56 characters") unless seed.length == 56
|
207
228
|
end
|
229
|
+
|
230
|
+
def extract_error_message(response_body, default_message)
|
231
|
+
JSON.parse(response_body).dig("error_message") rescue default_message
|
232
|
+
end
|
208
233
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/pinetwork'
|
3
|
+
|
4
|
+
class A2UConcurrencyTest < Minitest::Test
|
5
|
+
def test_concurrent_create_payment
|
6
|
+
total_threads = 10000
|
7
|
+
api_key = "api-key"
|
8
|
+
wallet_private_key = "SC2L62EYF7LYF43L4OOSKUKDESRAFJZW3UW6RFZ57UY25VAMHTL2BFER"
|
9
|
+
|
10
|
+
threads = []
|
11
|
+
|
12
|
+
faraday_stub = Minitest::Mock.new
|
13
|
+
pi = PiNetwork.new(api_key: api_key, wallet_private_key: wallet_private_key, faraday: faraday_stub)
|
14
|
+
|
15
|
+
total_threads.times do
|
16
|
+
threads << Thread.new do
|
17
|
+
faraday_response = Faraday::Response.new(
|
18
|
+
status: 200,
|
19
|
+
body: {identifier: SecureRandom.alphanumeric(12)}.to_json,
|
20
|
+
response_headers: {}
|
21
|
+
)
|
22
|
+
faraday_stub.expect(:post, faraday_response) do |url|
|
23
|
+
url == "https://api.minepi.com/v2/payments"
|
24
|
+
end
|
25
|
+
|
26
|
+
payment_data = { amount: 1, memo: "test", metadata: {"info": "test"}, uid: "test-uid" }
|
27
|
+
payment_id = pi.create_payment(payment_data)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
threads.each(&:join)
|
31
|
+
|
32
|
+
open_payments_after = pi.instance_variable_get(:@open_payments)
|
33
|
+
assert_equal(total_threads, open_payments_after.values.uniq.count, "open_payments got corrupted!")
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pinetwork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pi Core Team
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: stellar-sdk
|
@@ -44,14 +44,16 @@ executables: []
|
|
44
44
|
extensions: []
|
45
45
|
extra_rdoc_files: []
|
46
46
|
files:
|
47
|
+
- Rakefile
|
47
48
|
- lib/errors.rb
|
48
49
|
- lib/pinetwork.rb
|
50
|
+
- test/a2u_concurrency_test.rb
|
49
51
|
homepage: https://github.com/pi-apps/pi-ruby
|
50
52
|
licenses:
|
51
53
|
- PiOS
|
52
54
|
metadata:
|
53
55
|
documentation_uri: https://github.com/pi-apps/pi-ruby
|
54
|
-
post_install_message:
|
56
|
+
post_install_message:
|
55
57
|
rdoc_options: []
|
56
58
|
require_paths:
|
57
59
|
- lib
|
@@ -66,8 +68,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
68
|
- !ruby/object:Gem::Version
|
67
69
|
version: '0'
|
68
70
|
requirements: []
|
69
|
-
rubygems_version: 3.
|
70
|
-
signing_key:
|
71
|
+
rubygems_version: 3.4.10
|
72
|
+
signing_key:
|
71
73
|
specification_version: 4
|
72
74
|
summary: Pi Network Ruby
|
73
75
|
test_files: []
|