pinetwork 0.1.3 → 0.1.5

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: 8a84d8ec60d970ce532eaa91719ad9489df92a4d705b0fd0e9e729ec787f2b87
4
+ data.tar.gz: 18f1f89e4bc966b552556d8bd6c4a1569e15b6fb29612a55f41cb8aa47284ede
5
5
  SHA512:
6
- metadata.gz: 68b9a5f54bab85dd97c1e358a8d7ae428e7c04b5029ebdcfe66bfb1057fc53c59126236528aaada6c8017a25050fc3bd251287eb4d132f8ebf8ece6cad467506
7
- data.tar.gz: 469f3afd74061b371b27118172bc3f10955fd24e89f68c00fe26e2677e9ca3b09a69949e70babfd9d10679784c62619b44420cb50b40dc13fdf26cf1298abc05
6
+ metadata.gz: 5e3149e0c3bffeaf65dd395b57c51fdb87ff3667e915aa0fe9ab0cf8e3f7ec0f9d6770d0c62cf1ca855d28ed73974d83c84e38917f4c77b85e7c0ff62f9aae46
7
+ data.tar.gz: 6e561e7f72fafde97d68245f01ad4296b2b9c182e497978d0899ba4e827879e5e8884b74862bbac6acdae9ba3c06f0c31d27247f71a40416b60bce55ba59f338
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
 
@@ -148,7 +164,7 @@ class PiNetwork
148
164
  host = (network.starts_with? "Pi Network") ? @mainnet_host : @testnet_host
149
165
  horizon = "https://#{host}"
150
166
 
151
- client = Stellar::Client.new(host: host, horizon: horizon)
167
+ client = Stellar::Horizon::Client.new(host: host, horizon: horizon)
152
168
  Stellar::default_network = network
153
169
 
154
170
  @client = client
@@ -169,10 +185,7 @@ class PiNetwork
169
185
  recipient = Stellar::KeyPair.from_address(transaction_data[:recipient])
170
186
  memo = Stellar::Memo.new(:memo_text, transaction_data[:identifier])
171
187
 
172
- payment_operation = Stellar::Operation.payment({
173
- destination: recipient,
174
- amount: amount.to_payment
175
- })
188
+ payment_operation = Stellar::Operation.payment(destination: recipient, amount: amount.to_payment)
176
189
 
177
190
  my_public_key = self.account.address
178
191
  sequence_number = self.client.account_info(my_public_key).sequence.to_i
@@ -188,8 +201,13 @@ class PiNetwork
188
201
 
189
202
  def submit_transaction(transaction)
190
203
  envelope = transaction.to_envelope(self.account.keypair)
191
- response = self.client.submit_transaction(tx_envelope: envelope)
192
- txid = response._response.body["id"]
204
+ begin
205
+ response = self.client.submit_transaction(tx_envelope: envelope)
206
+ txid = response._response.body["id"]
207
+ rescue => error
208
+ result_codes = error.response&.dig(:body, "extras", "result_codes")
209
+ raise Errors::TxSubmissionError.new(result_codes&.dig("transaction"), result_codes&.dig("operations"))
210
+ end
193
211
  end
194
212
 
195
213
  def validate_payment_data!(data, options = {})
@@ -202,7 +220,11 @@ class PiNetwork
202
220
  end
203
221
 
204
222
  def validate_private_seed_format!(seed)
205
- raise StandardError.new("Private Seed should start with \"S\"") unless seed.upcase.starts_with?("S")
223
+ raise StandardError.new("Private Seed should start with \"S\"") unless seed.upcase.start_with?("S")
206
224
  raise StandardError.new("Private Seed should be 56 characters") unless seed.length == 56
207
225
  end
226
+
227
+ def extract_error_message(response_body, default_message)
228
+ JSON.parse(response_body).dig("error_message") rescue default_message
229
+ end
208
230
  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.5
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-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: stellar-sdk
@@ -16,42 +16,44 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.29.0
19
+ version: 0.31.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.29.0
26
+ version: 0.31.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: faraday
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 1.6.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 1.6.0
41
41
  description: Pi Network backend library for Ruby-based webservers.
42
42
  email: support@minepi.com
43
43
  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: []