pinetwork 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e55714fc1d6fbd13ff97e4d78e32d918a7926bc29e2e10cba93c8c9797ff9d5
4
- data.tar.gz: f5dc2d60e6f0b835d677504e6ea70d0b78e21e48f3ad09a692287e5feacd7d71
3
+ metadata.gz: 9cab0d9673b76e3b49f2ccb041f917b9574ca85dedb1c5d4529fd19ada191b2b
4
+ data.tar.gz: ee2bf09fd869205f4377f7ad72c79897282b419d3551dc72f88facbd8d33ca0c
5
5
  SHA512:
6
- metadata.gz: 68b9a5f54bab85dd97c1e358a8d7ae428e7c04b5029ebdcfe66bfb1057fc53c59126236528aaada6c8017a25050fc3bd251287eb4d132f8ebf8ece6cad467506
7
- data.tar.gz: 469f3afd74061b371b27118172bc3f10955fd24e89f68c00fe26e2677e9ca3b09a69949e70babfd9d10679784c62619b44420cb50b40dc13fdf26cf1298abc05
6
+ metadata.gz: 9f3279cb6518e666ef5ee59de68af1f98d147f1e159c2af1d9c775915f0d22624a640b2f36654c48f8f9a729e4ace10db4f9bedff721769eb220f82a67ca420a
7
+ data.tar.gz: f6d002eae9fd88cfb6e6d86958558c6f01dbb69b365cd7799ebdbe601d9d6242fb30ded18353f51f7d7f1bd43992e68d763f7687d6453f094f9ccbfe384d58e5
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList["test/*.rb"]
6
+ end
7
+
8
+ desc "Run tests"
9
+ task default: :test
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
- def initialize(api_key, wallet_private_key, options = {})
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] || "https://api.minepi.com"
19
- @mainnet_host = options[:mainnet_host] || "api.mainnet.minepi.com"
20
- @testnet_host = options[:testnet_host] || "api.testnet.minepi.com"
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 = Faraday.post(
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
- @open_payments[identifier] = parsed_response
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
- payment = @open_payments[payment_id]
69
+ @open_payments_mutex.synchronize do
70
+ payment = @open_payments[payment_id]
62
71
 
63
- if payment.nil? || payment["identifier"] != payment_id
64
- payment = get_payment(payment_id)
65
- txid = payment["transaction"]&.dig("txid")
66
- raise Errors::TxidAlreadyLinkedError.new("This payment already has a linked txid", payment_id, txid) if txid.present?
67
- end
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
- set_horizon_client(payment["network"])
70
- @from_address = payment["from_address"]
78
+ set_horizon_client(payment["network"])
79
+ @from_address = payment["from_address"]
71
80
 
72
- transaction_data = {
73
- amount: payment["amount"],
74
- identifier: payment["identifier"],
75
- recipient: payment["to_address"]
76
- }
81
+ transaction_data = {
82
+ amount: payment["amount"],
83
+ identifier: payment["identifier"],
84
+ recipient: payment["to_address"]
85
+ }
77
86
 
78
- transaction = build_a2u_transaction(transaction_data)
79
- txid = submit_transaction(transaction)
87
+ transaction = build_a2u_transaction(transaction_data)
88
+ txid = submit_transaction(transaction)
80
89
 
81
- @open_payments.delete(payment_id)
90
+ @open_payments.delete(payment_id)
82
91
 
83
- return txid
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
- @open_payments.delete(payment_id)
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
- @open_payments.delete(payment_id)
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 = JSON.parse(response.body).dig("error_message") rescue unknown_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
- response = self.client.submit_transaction(tx_envelope: envelope)
192
- txid = response._response.body["id"]
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.starts_with?("S")
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.3
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: 2023-10-12 00:00:00.000000000 Z
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.0.3.1
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: []