stellar-sdk 0.27.0 → 0.31.0

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: 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