stellar-sdk 0.27.0 → 0.31.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4956394163d9ef780494428a310af5d3a80b2b63caba45be39e42e55b7557298
4
- data.tar.gz: 482c2efc7b68b1348551a08f9e8ab4dc17e723ae86b9dad76fd1bc94e42f72d2
3
+ metadata.gz: e6374a1f2407fd63aeaf4ec40ab27409bd0b187aed21ac9d34eceb19b6f0eb48
4
+ data.tar.gz: 1198273f4bea602f9d9062b1ecf143bdbd7414172680ceaaf92c89edb940bd4f
5
5
  SHA512:
6
- metadata.gz: 34dfeb9b87ce27716b0d97a17ef42ff91d8b1bb34bdfecdea9797ddc1f840eaa1c88957c42e39ef4ca41bdf17e0ec7b5a64b30f8a608ed3173f2e818f5c2013c
7
- data.tar.gz: 48e93b269189a67e654ca59d83a0c697ed4388a418f668e67857a6026e1448e5766e006ccadfadb3993a1adc59ab629426336000acc651d32f41a0352ac9d8f3
6
+ metadata.gz: 3308f77ed5f5dcaec0f83fdef87220d318466f7775e08e1f93deed41562ebb4ae307383545b621337b8f07570a387be185b3575ea16d8350a0cd9c9a5b0ac761
7
+ data.tar.gz: 2e3f360405f67c0d9a2c9e3674bb531ddfd7e8d91ef7a3975c1cf8ff19e890d0d231b16a1eaf10e6283c9d102519ddd09d183297e572a9190245f5d2676f8c29
data/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
+ ## [0.31.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.30.0...v0.31.0) (2022-02-20)
9
+ ### Features
10
+ - `Stellar::SEP10` uses 5 minutes grace period for challenge tx ([#256](https://github.com/astroband/ruby-stellar-sdk/issues/256)) ([0d02663](https://github.com/astroband/ruby-stellar-sdk/commit/0d02663ff41a878f5c4d373f31988313e6ee4f46))
11
+
12
+ ## [0.30.0](https://www.github.com/astroband/ruby-stellar-sdk/compare/v0.29.0...v0.30.0) (2021-10-14)
13
+ ### Features
14
+ - `stellar-sdk` now depends on `stellar-horizon`
15
+ ### Refactoring
16
+ - `Stellar::Amount` class moved to `stellar-base` gem
17
+ - `Stellar::Horizon::Problem` class moved to `stellar-horizon` gem
18
+ - `Stellar::TransactionPage` is now `Stellar::Horizon::TransactionPage` in `stellar-horizon` gem
19
+ - `toml-rb` gem is replaced with `tomlrb` gem to work around [segfaults in Ruby 3.0](https://github.com/mjackson/citrus/issues/60)
20
+
21
+ ## [0.29.0](https://www.github.com/astroband/ruby-stellar-sdk/compare/v0.28.0...v0.29.0) (2021-09-07)
22
+ ### Deprecations
23
+ - `Stellar::Client` class is marked as deprecated in favour of new `stellar-horizon` gem
24
+ ### Features
25
+ - support for client domain in SEP-10
26
+
27
+ ## [0.28.0](https://www.github.com/astroband/ruby-stellar-sdk/compare/v0.27.0...v0.28.0) (2021-07-17)
28
+ ### Deprecations
29
+ - `Stellar::Account.lookup` is deprecated, use `Stellar::Federation.lookup` instead
30
+ ### Refactoring
31
+ - `Stellar::Account` class is now part of `stellar-base` gem
32
+
8
33
  ## [0.27.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.26.0...v0.27.0) (2021-05-08)
9
34
  - No changes
10
35
 
@@ -0,0 +1,51 @@
1
+ require "tomlrb"
2
+ require "uri"
3
+ require "faraday"
4
+ require "json"
5
+
6
+ module Stellar
7
+ class Federation
8
+ def self.lookup(federated_name)
9
+ _, domain = federated_name.split("*")
10
+ if domain.nil?
11
+ raise InvalidFederationAddress.new
12
+ end
13
+
14
+ domain_req = Faraday.new("https://#{domain}/.well-known/stellar.toml").get
15
+
16
+ unless domain_req.status == 200
17
+ raise InvalidStellarDomain, "Domain does not contain stellar.toml file"
18
+ end
19
+
20
+ fed_server_url = Tomlrb.parse(domain_req.body)["FEDERATION_SERVER"]
21
+ if fed_server_url.nil?
22
+ raise InvalidStellarTOML, "Invalid Stellar TOML file"
23
+ end
24
+
25
+ unless fed_server_url&.match?(URI::DEFAULT_PARSER.make_regexp)
26
+ raise InvalidFederationURL, "Invalid Federation Server URL"
27
+ end
28
+
29
+ lookup_req = Faraday.new(fed_server_url).get { |req|
30
+ req.params[:q] = federated_name
31
+ req.params[:type] = "name"
32
+ }
33
+
34
+ unless lookup_req.status == 200
35
+ raise AccountNotFound, "Account not found"
36
+ end
37
+
38
+ JSON.parse(lookup_req.body)["account_id"]
39
+ end
40
+ end
41
+
42
+ class AccountNotFound < StandardError; end
43
+
44
+ class InvalidStellarTOML < StandardError; end
45
+
46
+ class InvalidFederationAddress < StandardError; end
47
+
48
+ class InvalidStellarDomain < StandardError; end
49
+
50
+ class InvalidFederationURL < StandardError; end
51
+ end
data/lib/stellar/sep10.rb CHANGED
@@ -4,6 +4,10 @@ module Stellar
4
4
  class SEP10
5
5
  include Stellar::DSL
6
6
 
7
+ # We use a small grace period for the challenge transaction time bounds
8
+ # to compensate possible clock drift on client's machine
9
+ GRACE_PERIOD = 5.minutes
10
+
7
11
  # Helper method to create a valid {SEP0010}[https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md]
8
12
  # challenge transaction which you can use for Stellar Web Authentication.
9
13
  #
@@ -64,6 +68,20 @@ module Stellar
64
68
  )
65
69
  end
66
70
 
71
+ if options[:client_domain].present?
72
+ if options[:client_domain_account].blank?
73
+ raise "`client_domain_account` is required, if `client_domain` is provided"
74
+ end
75
+
76
+ tb.add_operation(
77
+ Stellar::Operation.manage_data(
78
+ name: "client_domain",
79
+ value: options[:client_domain],
80
+ source_account: options[:client_domain_account]
81
+ )
82
+ )
83
+ end
84
+
67
85
  tb.build.to_envelope(server).to_xdr(:base64)
68
86
  end
69
87
 
@@ -125,16 +143,14 @@ module Stellar
125
143
 
126
144
  rest_ops.each do |op|
127
145
  body = op.body
146
+ op_params = body.value
128
147
 
129
148
  if body.arm != :manage_data_op
130
149
  raise InvalidSep10ChallengeError, "The transaction has operations that are not of type 'manageData'"
131
- elsif op.source_account != server.muxed_account
150
+ elsif op.source_account != server.muxed_account && op_params.data_name != "client_domain"
132
151
  raise InvalidSep10ChallengeError, "The transaction has operations that are unrecognized"
133
- else
134
- op_params = body.value
135
- if op_params.data_name == "web_auth_domain" && options.key?(:auth_domain) && op_params.data_value != options[:auth_domain]
136
- raise InvalidSep10ChallengeError, "The transaction has 'manageData' operation with 'web_auth_domain' key and invalid value"
137
- end
152
+ elsif op_params.data_name == "web_auth_domain" && options.key?(:auth_domain) && op_params.data_value != options[:auth_domain]
153
+ raise InvalidSep10ChallengeError, "The transaction has 'manageData' operation with 'web_auth_domain' key and invalid value"
138
154
  end
139
155
  end
140
156
 
@@ -145,7 +161,7 @@ module Stellar
145
161
  time_bounds = transaction.time_bounds
146
162
  now = Time.now.to_i
147
163
 
148
- if time_bounds.blank? || !now.between?(time_bounds.min_time, time_bounds.max_time)
164
+ if time_bounds.blank? || !now.between?(time_bounds.min_time - GRACE_PERIOD, time_bounds.max_time + GRACE_PERIOD)
149
165
  raise InvalidSep10ChallengeError, "The transaction has expired"
150
166
  end
151
167
 
@@ -210,6 +226,9 @@ module Stellar
210
226
  client_signers = signers.select { |s| s =~ /G[A-Z0-9]{55}/ && s != server.address }.to_set
211
227
  raise InvalidSep10ChallengeError, "at least one regular signer must be provided" if client_signers.empty?
212
228
 
229
+ client_domain_account_address = extract_client_domain_account(te.tx)
230
+ client_signers.add(client_domain_account_address) if client_domain_account_address.present?
231
+
213
232
  # verify all signatures in one pass
214
233
  client_signers.add(server.address)
215
234
  signers_found = verify_tx_signatures(tx_envelope: te, signers: client_signers)
@@ -229,6 +248,10 @@ module Stellar
229
248
  raise InvalidSep10ChallengeError, "Transaction has unrecognized signatures."
230
249
  end
231
250
 
251
+ if client_domain_account_address.present? && !signers_found.include?(client_domain_account_address)
252
+ raise InvalidSep10ChallengeError, "Transaction not signed by client domain account."
253
+ end
254
+
232
255
  signers_found
233
256
  end
234
257
 
@@ -249,7 +272,7 @@ module Stellar
249
272
  to_keypair = Stellar::DSL.method(:KeyPair)
250
273
  keys_by_hint = signers.map(&to_keypair).index_by(&:signature_hint)
251
274
 
252
- tx_envelope.signatures.each.with_object(Set.new) do |sig, result|
275
+ signatures.each_with_object(Set.new) do |sig, result|
253
276
  key = keys_by_hint.delete(sig.hint)
254
277
  result.add(key.address) if key&.verify(sig.signature, tx_hash)
255
278
  end
@@ -272,5 +295,16 @@ module Stellar
272
295
  keypair.verify(sig.signature, tx_hash)
273
296
  end
274
297
  end
298
+
299
+ def self.extract_client_domain_account(transaction)
300
+ client_domain_account_op =
301
+ transaction
302
+ .operations
303
+ .find { |op| op.body.value.data_name == "client_domain" }
304
+
305
+ return if client_domain_account_op.blank?
306
+
307
+ Util::StrKey.encode_muxed_account(client_domain_account_op.source_account)
308
+ end
275
309
  end
276
310
  end
data/lib/stellar-sdk.rb CHANGED
@@ -1,21 +1,12 @@
1
1
  require "stellar-base"
2
+ require "stellar-horizon"
2
3
 
3
4
  module Stellar
4
5
  module SDK
5
6
  VERSION = ::Stellar::VERSION
6
7
  end
8
+ Client = Horizon::Client
7
9
 
8
- autoload :Account
9
- autoload :Amount
10
- autoload :Client
10
+ autoload :Federation
11
11
  autoload :SEP10
12
-
13
- module Horizon
14
- extend ActiveSupport::Autoload
15
-
16
- autoload :Problem
17
- autoload :Result
18
- end
19
-
20
- autoload :TransactionPage
21
12
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stellar-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.0
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Fleckenstein
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-05-09 00:00:00.000000000 Z
13
+ date: 2022-02-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: stellar-base
@@ -18,61 +18,55 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.27.0
21
+ version: 0.31.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - '='
27
27
  - !ruby/object:Gem::Version
28
- version: 0.27.0
28
+ version: 0.31.0
29
29
  - !ruby/object:Gem::Dependency
30
- name: activesupport
30
+ name: stellar-horizon
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: 5.0.0
36
- - - "<"
33
+ - - '='
37
34
  - !ruby/object:Gem::Version
38
- version: '7.0'
35
+ version: 0.31.0
39
36
  type: :runtime
40
37
  prerelease: false
41
38
  version_requirements: !ruby/object:Gem::Requirement
42
39
  requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- version: 5.0.0
46
- - - "<"
40
+ - - '='
47
41
  - !ruby/object:Gem::Version
48
- version: '7.0'
42
+ version: 0.31.0
49
43
  - !ruby/object:Gem::Dependency
50
- name: excon
44
+ name: activesupport
51
45
  requirement: !ruby/object:Gem::Requirement
52
46
  requirements:
53
47
  - - ">="
54
48
  - !ruby/object:Gem::Version
55
- version: 0.71.0
49
+ version: 5.0.0
56
50
  - - "<"
57
51
  - !ruby/object:Gem::Version
58
- version: '1.0'
52
+ version: '8.0'
59
53
  type: :runtime
60
54
  prerelease: false
61
55
  version_requirements: !ruby/object:Gem::Requirement
62
56
  requirements:
63
57
  - - ">="
64
58
  - !ruby/object:Gem::Version
65
- version: 0.71.0
59
+ version: 5.0.0
66
60
  - - "<"
67
61
  - !ruby/object:Gem::Version
68
- version: '1.0'
62
+ version: '8.0'
69
63
  - !ruby/object:Gem::Dependency
70
- name: hyperclient
64
+ name: faraday
71
65
  requirement: !ruby/object:Gem::Requirement
72
66
  requirements:
73
67
  - - ">="
74
68
  - !ruby/object:Gem::Version
75
- version: 0.7.0
69
+ version: 1.6.0
76
70
  - - "<"
77
71
  - !ruby/object:Gem::Version
78
72
  version: '2.0'
@@ -82,17 +76,17 @@ dependencies:
82
76
  requirements:
83
77
  - - ">="
84
78
  - !ruby/object:Gem::Version
85
- version: 0.7.0
79
+ version: 1.6.0
86
80
  - - "<"
87
81
  - !ruby/object:Gem::Version
88
82
  version: '2.0'
89
83
  - !ruby/object:Gem::Dependency
90
- name: toml-rb
84
+ name: tomlrb
91
85
  requirement: !ruby/object:Gem::Requirement
92
86
  requirements:
93
87
  - - ">="
94
88
  - !ruby/object:Gem::Version
95
- version: 1.1.1
89
+ version: 2.0.1
96
90
  - - "<"
97
91
  - !ruby/object:Gem::Version
98
92
  version: '3.0'
@@ -102,52 +96,10 @@ dependencies:
102
96
  requirements:
103
97
  - - ">="
104
98
  - !ruby/object:Gem::Version
105
- version: 1.1.1
99
+ version: 2.0.1
106
100
  - - "<"
107
101
  - !ruby/object:Gem::Version
108
102
  version: '3.0'
109
- - !ruby/object:Gem::Dependency
110
- name: bundler
111
- requirement: !ruby/object:Gem::Requirement
112
- requirements:
113
- - - "~>"
114
- - !ruby/object:Gem::Version
115
- version: '2.2'
116
- type: :development
117
- prerelease: false
118
- version_requirements: !ruby/object:Gem::Requirement
119
- requirements:
120
- - - "~>"
121
- - !ruby/object:Gem::Version
122
- version: '2.2'
123
- - !ruby/object:Gem::Dependency
124
- name: rake
125
- requirement: !ruby/object:Gem::Requirement
126
- requirements:
127
- - - "~>"
128
- - !ruby/object:Gem::Version
129
- version: '13'
130
- type: :development
131
- prerelease: false
132
- version_requirements: !ruby/object:Gem::Requirement
133
- requirements:
134
- - - "~>"
135
- - !ruby/object:Gem::Version
136
- version: '13'
137
- - !ruby/object:Gem::Dependency
138
- name: rspec
139
- requirement: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - "~>"
142
- - !ruby/object:Gem::Version
143
- version: '3.9'
144
- type: :development
145
- prerelease: false
146
- version_requirements: !ruby/object:Gem::Requirement
147
- requirements:
148
- - - "~>"
149
- - !ruby/object:Gem::Version
150
- version: '3.9'
151
103
  description:
152
104
  email:
153
105
  executables: []
@@ -161,22 +113,18 @@ files:
161
113
  - LICENSE
162
114
  - README.md
163
115
  - lib/stellar-sdk.rb
164
- - lib/stellar/account.rb
165
- - lib/stellar/amount.rb
166
- - lib/stellar/client.rb
167
- - lib/stellar/horizon/problem.rb
116
+ - lib/stellar/federation.rb
168
117
  - lib/stellar/sep10.rb
169
- - lib/stellar/transaction_page.rb
170
118
  homepage: https://github.com/astroband/ruby-stellar-sdk
171
119
  licenses:
172
120
  - Apache-2.0
173
121
  metadata:
174
122
  bug_tracker_uri: https://github.com/astroband/ruby-stellar-sdk/issues
175
- changelog_uri: https://github.com/astroband/ruby-stellar-sdk/blob/v0.27.0/sdk/CHANGELOG.md
176
- documentation_uri: https://rubydoc.info/gems/stellar-sdk/0.27.0/
123
+ changelog_uri: https://github.com/astroband/ruby-stellar-sdk/blob/v0.31.0/sdk/CHANGELOG.md
124
+ documentation_uri: https://rubydoc.info/gems/stellar-sdk/0.31.0/
177
125
  github_repo: ssh://github.com/astroband/ruby-stellar-sdk
178
126
  homepage_uri: https://github.com/astroband/ruby-stellar-sdk/tree/main/sdk
179
- source_code_uri: https://github.com/astroband/ruby-stellar-sdk/tree/v0.27.0/sdk
127
+ source_code_uri: https://github.com/astroband/ruby-stellar-sdk/tree/v0.31.0/sdk
180
128
  post_install_message:
181
129
  rdoc_options: []
182
130
  require_paths:
@@ -192,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
140
  - !ruby/object:Gem::Version
193
141
  version: '0'
194
142
  requirements: []
195
- rubygems_version: 3.2.16
143
+ rubygems_version: 3.3.5
196
144
  signing_key:
197
145
  specification_version: 4
198
146
  summary: Stellar client library
@@ -1,89 +0,0 @@
1
- require "toml-rb"
2
- require "uri"
3
- require "faraday"
4
- require "json"
5
-
6
- module Stellar
7
- class Account
8
- delegate :address, to: :keypair
9
-
10
- def self.random
11
- keypair = Stellar::KeyPair.random
12
- new(keypair)
13
- end
14
-
15
- def self.from_seed(seed)
16
- keypair = Stellar::KeyPair.from_seed(seed)
17
- new(keypair)
18
- end
19
-
20
- def self.from_address(address)
21
- keypair = Stellar::KeyPair.from_address(address)
22
- new(keypair)
23
- end
24
-
25
- def self.lookup(federated_name)
26
- _, domain = federated_name.split("*")
27
- if domain.nil?
28
- raise InvalidFederationAddress.new
29
- end
30
-
31
- domain_req = Faraday.new("https://#{domain}/.well-known/stellar.toml").get
32
-
33
- unless domain_req.status == 200
34
- raise InvalidStellarDomain.new("Domain does not contain stellar.toml file")
35
- end
36
-
37
- fed_server_url = TomlRB.parse(domain_req.body)["FEDERATION_SERVER"]
38
- if fed_server_url.nil?
39
- raise InvalidStellarTOML.new("Invalid Stellar TOML file")
40
- end
41
-
42
- unless fed_server_url&.match?(URI::DEFAULT_PARSER.make_regexp)
43
- raise InvalidFederationURL.new("Invalid Federation Server URL")
44
- end
45
-
46
- lookup_req = Faraday.new(fed_server_url).get { |req|
47
- req.params[:q] = federated_name
48
- req.params[:type] = "name"
49
- }
50
-
51
- unless lookup_req.status == 200
52
- raise AccountNotFound.new("Account not found")
53
- end
54
-
55
- JSON.parse(lookup_req.body)["account_id"]
56
- end
57
-
58
- def self.master
59
- keypair = Stellar::KeyPair.from_raw_seed("allmylifemyhearthasbeensearching")
60
- new(keypair)
61
- end
62
-
63
- attr_reader :keypair
64
-
65
- # @param [Stellar::KeyPair] keypair
66
- def initialize(keypair)
67
- @keypair = keypair
68
- end
69
-
70
- def to_keypair
71
- keypair
72
- end
73
- end
74
-
75
- class AccountNotFound < StandardError
76
- end
77
-
78
- class InvalidStellarTOML < StandardError
79
- end
80
-
81
- class InvalidFederationAddress < StandardError
82
- end
83
-
84
- class InvalidStellarDomain < StandardError
85
- end
86
-
87
- class InvalidFederationURL < StandardError
88
- end
89
- end
@@ -1,36 +0,0 @@
1
- module Stellar
2
- class Amount
3
- attr_reader :amount
4
- attr_reader :asset
5
-
6
- # @param [Fixnum] amount
7
- # @param [Stellar::Asset] asset
8
- def initialize(amount, asset = Stellar::Asset.native)
9
- # TODO: how are we going to handle decimal considerations?
10
-
11
- @amount = amount
12
- @asset = asset
13
- end
14
-
15
- # @return [Array(Symbol, Fixnum)] in case of a native asset
16
- # @return [Array(Symbol, String, Stellar::KeyPair, Fixnum)] in case of alphanum asset
17
- def to_payment
18
- case asset.type
19
- when AssetType.asset_type_native
20
- [:native, amount]
21
- when AssetType.asset_type_credit_alphanum4
22
- keypair = KeyPair.from_public_key(asset.issuer.value)
23
- [:alphanum4, asset.code, keypair, amount]
24
- when AssetType.asset_type_credit_alphanum12
25
- keypair = KeyPair.from_public_key(asset.issuer.value)
26
- [:alphanum12, asset.code, keypair, amount]
27
- else
28
- raise "Unknown asset type: #{asset.type}"
29
- end
30
- end
31
-
32
- def inspect
33
- "#<Stellar::Amount #{asset}(#{amount})>"
34
- end
35
- end
36
- end
@@ -1,298 +0,0 @@
1
- require "hyperclient"
2
- require "active_support/core_ext/object/blank"
3
- require "securerandom"
4
-
5
- module Stellar
6
- class AccountRequiresMemoError < StandardError
7
- attr_reader :account_id, :operation_index
8
-
9
- def initialize(message, account_id, operation_index)
10
- super(message)
11
- @account_id = account_id
12
- @operation_index = operation_index
13
- end
14
- end
15
-
16
- class Client
17
- DEFAULT_FEE = 100
18
-
19
- HORIZON_LOCALHOST_URL = "http://127.0.0.1:8000"
20
- HORIZON_MAINNET_URL = "https://horizon.stellar.org"
21
- HORIZON_TESTNET_URL = "https://horizon-testnet.stellar.org"
22
- FRIENDBOT_URL = "https://friendbot.stellar.org".freeze
23
-
24
- def self.default(options = {})
25
- new options.merge(
26
- horizon: HORIZON_MAINNET_URL
27
- )
28
- end
29
-
30
- def self.default_testnet(options = {})
31
- new options.merge(
32
- horizon: HORIZON_TESTNET_URL,
33
- friendbot: HORIZON_TESTNET_URL
34
- )
35
- end
36
-
37
- def self.localhost(options = {})
38
- new options.merge(
39
- horizon: HORIZON_LOCALHOST_URL
40
- )
41
- end
42
-
43
- attr_reader :horizon
44
-
45
- # @option options [String] :horizon The Horizon server URL.
46
- def initialize(options)
47
- @options = options
48
- @horizon = Hyperclient.new(options[:horizon]) { |client|
49
- client.faraday_block = lambda do |conn|
50
- conn.use Faraday::Response::RaiseError
51
- conn.use FaradayMiddleware::FollowRedirects
52
- conn.request :url_encoded
53
- conn.response :hal_json, content_type: /\bjson$/
54
- conn.adapter :excon
55
- end
56
- client.headers = {
57
- "Accept" => "application/hal+json,application/problem+json,application/json",
58
- "X-Client-Name" => "ruby-stellar-sdk",
59
- "X-Client-Version" => VERSION
60
- }
61
- }
62
- end
63
-
64
- # @param [Stellar::Account|String] account_or_address
65
- def account_info(account_or_address)
66
- account_id = if account_or_address.is_a?(Stellar::Account)
67
- account_or_address.address
68
- else
69
- account_or_address
70
- end
71
- @horizon.account(account_id: account_id)._get
72
- end
73
-
74
- # @option options [Stellar::Account] :account
75
- # @option options [Stellar::Account] :destination
76
- def account_merge(options = {})
77
- account = options[:account]
78
- destination = options[:destination]
79
- sequence = options[:sequence] || (account_info(account).sequence.to_i + 1)
80
-
81
- transaction = Stellar::TransactionBuilder.account_merge(
82
- source_account: destination.keypair,
83
- sequence_number: sequence,
84
- destination: destination.keypair
85
- )
86
-
87
- envelope = transaction.to_envelope(account.keypair)
88
- submit_transaction(tx_envelope: envelope)
89
- end
90
-
91
- def friendbot(account)
92
- uri = URI.parse(FRIENDBOT_URL)
93
- uri.query = "addr=#{account.address}"
94
- Faraday.post(uri.to_s)
95
- end
96
-
97
- # @option options [Stellar::Account] :account
98
- # @option options [Stellar::Account] :funder
99
- # @option options [Integer] :starting_balance
100
- def create_account(options = {})
101
- funder = options[:funder]
102
- sequence = options[:sequence] || (account_info(funder).sequence.to_i + 1)
103
- # In the future, the fee should be grabbed from the network's last transactions,
104
- # instead of using a hard-coded default value.
105
- fee = options[:fee] || DEFAULT_FEE
106
-
107
- payment = Stellar::TransactionBuilder.create_account(
108
- source_account: funder.keypair,
109
- sequence_number: sequence,
110
- base_fee: fee,
111
- destination: options[:account].keypair,
112
- starting_balance: options[:starting_balance]
113
- )
114
- envelope = payment.to_envelope(funder.keypair)
115
- submit_transaction(tx_envelope: envelope)
116
- end
117
-
118
- # @option options [Stellar::Account] :from The source account
119
- # @option options [Stellar::Account] :to The destination account
120
- # @option options [Stellar::Amount] :amount The amount to send
121
- def send_payment(options = {})
122
- from_account = options[:from]
123
- tx_source_account = options[:transaction_source] || from_account
124
- op_source_account = from_account if tx_source_account.present?
125
-
126
- sequence = options[:sequence] ||
127
- (account_info(tx_source_account).sequence.to_i + 1)
128
-
129
- payment = Stellar::TransactionBuilder.new(
130
- source_account: tx_source_account.keypair,
131
- sequence_number: sequence
132
- ).add_operation(
133
- Stellar::Operation.payment(
134
- source_account: op_source_account.keypair,
135
- destination: options[:to].keypair,
136
- amount: options[:amount].to_payment
137
- )
138
- ).set_memo(options[:memo]).set_timeout(0).build
139
-
140
- signers = [tx_source_account, op_source_account].uniq(&:address)
141
- to_envelope_args = signers.map(&:keypair)
142
-
143
- envelope = payment.to_envelope(*to_envelope_args)
144
- submit_transaction(tx_envelope: envelope)
145
- end
146
-
147
- # @option options [Stellar::Account] :account
148
- # @option options [Integer] :limit
149
- # @option options [Integer] :cursor
150
- # @return [Stellar::TransactionPage]
151
- def transactions(options = {})
152
- args = options.slice(:limit, :cursor)
153
-
154
- resource = if options[:account]
155
- args = args.merge(account_id: options[:account].address)
156
- @horizon.account_transactions(args)
157
- else
158
- @horizon.transactions(args)
159
- end
160
-
161
- TransactionPage.new(resource)
162
- end
163
-
164
- # @param [Array(Symbol,String,Stellar::KeyPair|Stellar::Account)] asset
165
- # @param [Stellar::Account] source
166
- # @param [Integer] sequence
167
- # @param [Integer] fee
168
- # @param [Integer] limit
169
- def change_trust(
170
- asset:,
171
- source:,
172
- sequence: nil,
173
- fee: DEFAULT_FEE,
174
- limit: nil
175
- )
176
- sequence ||= (account_info(source).sequence.to_i + 1)
177
-
178
- op_args = {
179
- account: source.keypair,
180
- sequence: sequence,
181
- line: asset
182
- }
183
- op_args[:limit] = limit unless limit.nil?
184
-
185
- tx = Stellar::TransactionBuilder.change_trust(
186
- source_account: source.keypair,
187
- sequence_number: sequence,
188
- **op_args
189
- )
190
-
191
- envelope = tx.to_envelope(source.keypair)
192
- submit_transaction(tx_envelope: envelope)
193
- end
194
-
195
- # @param [Stellar::TransactionEnvelope] tx_envelope
196
- # @option options [Boolean] :skip_memo_required_check (false)
197
- def submit_transaction(tx_envelope:, options: {skip_memo_required_check: false})
198
- unless options[:skip_memo_required_check]
199
- check_memo_required(tx_envelope)
200
- end
201
- @horizon.transactions._post(tx: tx_envelope.to_xdr(:base64))
202
- end
203
-
204
- # Required by SEP-0029
205
- # @param [Stellar::TransactionEnvelope] tx_envelope
206
- def check_memo_required(tx_envelope)
207
- tx = tx_envelope.tx
208
-
209
- if tx.is_a?(Stellar::FeeBumpTransaction)
210
- tx = tx.inner_tx.v1!.tx
211
- end
212
-
213
- # Check transactions where the .memo field is nil or of type MemoType.memo_none
214
- if !tx.memo.nil? && tx.memo.type != Stellar::MemoType.memo_none
215
- return
216
- end
217
-
218
- destinations = Set.new
219
- ot = Stellar::OperationType
220
-
221
- tx.operations.each_with_index do |op, idx|
222
- destination = case op.body.type
223
- when ot.payment, ot.path_payment_strict_receive, ot.path_payment_strict_send
224
- op.body.value.destination
225
- when ot.account_merge
226
- # There is no AccountMergeOp, op.body is an Operation object
227
- # and op.body.value is a PublicKey (or AccountID) object.
228
- op.body.value
229
- else
230
- next
231
- end
232
-
233
- if destinations.include?(destination) || destination.switch == Stellar::CryptoKeyType.key_type_muxed_ed25519
234
- next
235
- end
236
-
237
- destinations.add(destination)
238
- kp = Stellar::KeyPair.from_public_key(destination.value)
239
-
240
- begin
241
- info = account_info(kp.address)
242
- rescue Faraday::ResourceNotFound
243
- # Don't raise an error if its a 404, but throw one otherwise
244
- next
245
- end
246
- if info.data["config.memo_required"] == "MQ=="
247
- # MQ== is the base64 encoded string for the string "1"
248
- raise AccountRequiresMemoError.new("account requires memo", destination, idx)
249
- end
250
- end
251
- end
252
-
253
- # DEPRECATED: this function has been moved Stellar::SEP10.build_challenge_tx and
254
- # will be removed in the next major version release.
255
- #
256
- # A wrapper function for Stellar::SEP10::build_challenge_tx.
257
- #
258
- # @param server [Stellar::KeyPair] Keypair for server's signing account.
259
- # @param client [Stellar::KeyPair] Keypair for the account whishing to authenticate with the server.
260
- # @param anchor_name [String] Anchor's name to be used in the manage_data key.
261
- # @param timeout [Integer] Challenge duration (default to 5 minutes).
262
- #
263
- # @return [String] A base64 encoded string of the raw TransactionEnvelope xdr struct for the transaction.
264
- def build_challenge_tx(server:, client:, anchor_name:, timeout: 300)
265
- Stellar::SEP10.build_challenge_tx(
266
- server: server, client: client, anchor_name: anchor_name, timeout: timeout
267
- )
268
- end
269
-
270
- # DEPRECATED: this function has been moved to Stellar::SEP10::read_challenge_tx and
271
- # will be removed in the next major version release.
272
- #
273
- # A wrapper function for Stellar::SEP10.verify_challenge_transaction
274
- #
275
- # @param challenge [String] SEP0010 transaction challenge in base64.
276
- # @param server [Stellar::KeyPair] Stellar::KeyPair for server where the challenge was generated.
277
- #
278
- # @return [Boolean]
279
- def verify_challenge_tx(challenge:, server:)
280
- Stellar::SEP10.verify_challenge_tx(challenge_xdr: challenge, server: server)
281
- true
282
- end
283
-
284
- # DEPRECATED: this function has been moved to Stellar::SEP10::verify_tx_signed_by and
285
- # will be removed in the next major version release.
286
- #
287
- # @param transaction_envelope [Stellar::TransactionEnvelope]
288
- # @param keypair [Stellar::KeyPair]
289
- #
290
- # @return [Boolean]
291
- #
292
- def verify_tx_signed_by(transaction_envelope:, keypair:)
293
- Stellar::SEP10.verify_tx_signed_by(
294
- tx_envelope: transaction_envelope, keypair: keypair
295
- )
296
- end
297
- end
298
- end
@@ -1,45 +0,0 @@
1
- module Stellar
2
- module Horizon
3
- class Problem
4
- def initialize(attributes)
5
- @attributes = attributes.reverse_merge({
6
- type: "about:blank",
7
- title: "Unknown Error",
8
- status: 500
9
- })
10
-
11
- @meta = @attributes.except!(:type, :title, :status, :detail, :instance)
12
- end
13
-
14
- # @return [String]
15
- def type
16
- @attributes[:type]
17
- end
18
-
19
- # @return [String]
20
- def title
21
- @attributes[:title]
22
- end
23
-
24
- # @return [Integer]
25
- def status
26
- @attributes[:status]
27
- end
28
-
29
- # @return [String]
30
- def detail
31
- @attributes[:detail]
32
- end
33
-
34
- # @return [String]
35
- def instance
36
- @attributes[:instance]
37
- end
38
-
39
- # @return [{String => Object}]
40
- def meta
41
- @attributes[:instance]
42
- end
43
- end
44
- end
45
- end
@@ -1,25 +0,0 @@
1
- module Stellar
2
- class TransactionPage
3
- include Enumerable
4
-
5
- # @param [Hyperclient::Link] resource
6
- def initialize(resource)
7
- @resource = resource
8
- end
9
-
10
- def each
11
- @resource.records.each do |tx|
12
- yield tx if block_given?
13
- end
14
- end
15
-
16
- # @return [Stellar::TransactionPage]
17
- def next_page
18
- self.class.new(@resource.next)
19
- end
20
-
21
- def next_page!
22
- @resource = @resource.next
23
- end
24
- end
25
- end