stellar-sdk 0.23.1 → 0.27.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: 83d2fa69cc1464de295aea3252915d41750d7aa1300c778a45ddba48f5584d3e
4
- data.tar.gz: 350c012540421676d3b6367abca3f581ff83998150c454e5da8043eaa01eda37
3
+ metadata.gz: 4956394163d9ef780494428a310af5d3a80b2b63caba45be39e42e55b7557298
4
+ data.tar.gz: 482c2efc7b68b1348551a08f9e8ab4dc17e723ae86b9dad76fd1bc94e42f72d2
5
5
  SHA512:
6
- metadata.gz: c6641384a007ddcfd741c53a389b9ae00c41a9c960f23a21197d2013118d3c62001a0f413775f7bb081917d5d68a3eec9424b5b8d484b935e6db0d75e1484ed2
7
- data.tar.gz: 570cfe50af7ebe58a7741b918ede5f05ca733159c7608e9dfb8bf3cb66598f93c2728d0f0049555a8f43c357c8020f8391a9b12418ec5d180d165580a6f165bc
6
+ metadata.gz: 34dfeb9b87ce27716b0d97a17ef42ff91d8b1bb34bdfecdea9797ddc1f840eaa1c88957c42e39ef4ca41bdf17e0ec7b5a64b30f8a608ed3173f2e818f5c2013c
7
+ data.tar.gz: 48e93b269189a67e654ca59d83a0c697ed4388a418f668e67857a6026e1448e5766e006ccadfadb3993a1adc59ab629426336000acc651d32f41a0352ac9d8f3
data/CHANGELOG.md CHANGED
@@ -1,24 +1,50 @@
1
1
  # Change Log
2
+
2
3
  All notable changes to this project will be documented in this file.
3
4
 
4
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
7
 
7
- ## [0.23.1](https://github.com/stellar/ruby-stellar-sdk/compare/v0.23.1...v0.23.0) - 2020-06-18
8
+ ## [0.27.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.26.0...v0.27.0) (2021-05-08)
9
+ - No changes
10
+
11
+ ## [0.26.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.25.0...v0.26.0) - 2021-02-05
12
+ ### Changed
13
+ - `Stellar::SEP10` is updated to comply with SEP10 v3.0.0 and v3.1.0
14
+ - `read_challenge_tx`` now verifies `domain` in challenge auth operation, as per SEP10 v3.0.0
15
+ - it is now possible to provide `auth_domain` parameter to enforce auth server domain verification:
16
+ - `build_challenge_tx` will encode the extra auth domain operation into the challenge tx
17
+ - `read_challenge_tx` will verify that the challenge includes the correct auth domain operation
18
+
19
+ ## [0.25.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.24.0...v0.25.0) - 2020-10-30
20
+ ### Changed
21
+ - `Stellar::SEP10` is updated to comply with SEP10 v2.1.0
22
+ - `build_challenge_tx` now accepts `domain` instead of `anchor_name`, using the
23
+ old param name will now result in deprecation warning
24
+ - `read_challenge_tx` correctly validates multi-op challenge transactions
25
+ - `verify_challenge_tx_threshold` now expects simple `{'GA...' => weight, ... }` hash for `signers`
26
+ ### Removed:
27
+ - Deprecated `Stellar::SEP10.verify_challenge_tx` method is removed
28
+
29
+ ## [0.24.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.23.1...v0.24.0) - 2020-10-20
30
+ - Protocol 14 support
31
+
32
+ ## [0.23.1](https://github.com/astroband/ruby-stellar-sdk/compare/v0.23.0...v0.23.1) - 2020-06-18
8
33
  - Fix SEP10, considering muxed accounts
9
34
 
10
- ## [0.23.0](https://github.com/stellar/ruby-stellar-sdk/compare/v0.23.0...v0.9.0)
35
+ ## [0.23.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.9.0-rc.1...v0.23.0)
11
36
  - No changes. We bumped this version to sync `stellar-sdk` and `stellar-base` versions
12
37
 
13
- ## [0.9.0](https://github.com/stellar/ruby-stellar-sdk/compare/v0.9.0...v0.8.0)
38
+ ## [0.9.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.8.0...v0.9.0-rc.1)
14
39
  ### Added
15
40
  - Stellar Protocol 13 support
16
41
  - Fee-Bump transactions ([CAP-0015](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md))
17
42
  - Multiplexed accounts ([CAP-0027](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0027.md))
18
43
  - Fine-Grained control on trustline authorization ([CAP-0018](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0018.md))
19
- ## [0.8.0](https://github.com/stellar/ruby-stellar-sdk/compare/v0.7.0...v0.8.0)
44
+
45
+ ## [0.8.0](https://github.com/astroband/ruby-stellar-sdk/compare/v0.7.0...v0.8.0)
20
46
  ### Added
21
- - SEP-10 Multisig Support [#69](https://github.com/stellar/ruby-stellar-sdk/pull/69)
47
+ - SEP-10 Multisig Support [#69](https://github.com/astroband/ruby-stellar-sdk/pull/69)
22
48
  - `X-Client-Name` and `X-Client-Version` headers
23
49
 
24
50
  ## [0.7.0] - 2019-04-26
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # Ruby Stellar
2
-
3
- [![Build Status](https://travis-ci.org/bloom-solutions/ruby-stellar-sdk.svg)](https://travis-ci.org/bloom-solutions/ruby-stellar-sdk)
4
- [![Code Climate](https://codeclimate.com/github/bloom-solutions/ruby-stellar-sdk/badges/gpa.svg)](https://codeclimate.com/github/bloom-solutions/ruby-stellar-sdk)
1
+ # Stellar SDK for Ruby: Horizon Integration and Higher Level Abstractions
2
+ [![stellar-sdk](https://badge.fury.io/rb/stellar-sdk.svg)](https://badge.fury.io/rb/stellar-sdk)
3
+ [![Test](https://github.com/astroband/ruby-stellar-sdk/workflows/Test/badge.svg)](https://github.com/astroband/ruby-stellar-sdk/actions?query=branch%3Amaster)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/dadfcd9396aba493cb93/maintainability)](https://codeclimate.com/github/astroband/ruby-stellar-sdk/maintainability)
5
5
 
6
6
  This library helps you to integrate your application into the [Stellar network](http://stellar.org).
7
7
 
@@ -36,10 +36,10 @@ client.send_payment({
36
36
  from: account,
37
37
  to: recipient,
38
38
  amount: Stellar::Amount.new(100_000_000)
39
- })
39
+ })
40
40
  ```
41
41
 
42
- Be sure to set the network when submitting to the public network (more information in [stellar-base](https://www.github.com/bloom-solutions/ruby-stellar-base)):
42
+ Be sure to set the network when submitting to the public network (more information in [stellar-base](https://www.github.com/astroband/ruby-stellar-base)):
43
43
 
44
44
  ```ruby
45
45
  Stellar.default_network = Stellar::Networks::PUBLIC
@@ -57,7 +57,7 @@ Stellar.default_network = Stellar::Networks::PUBLIC
57
57
  ## Contributing
58
58
 
59
59
  1. Sign the [Contributor License Agreement](https://docs.google.com/forms/d/1g7EF6PERciwn7zfmfke5Sir2n10yddGGSXyZsq98tVY/viewform?usp=send_form)
60
- 2. Fork it ( https://github.com/bloom-solutions/ruby-stellar-lib/fork )
60
+ 2. Fork it ( https://github.com/astroband/ruby-stellar-sdk/fork )
61
61
  2. Create your feature branch (`git checkout -b my-new-feature`)
62
62
  3. Commit your changes (`git commit -am 'Add some feature'`)
63
63
  4. Push to the branch (`git push origin my-new-feature`)
data/lib/stellar-sdk.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  require "stellar-base"
2
- require_relative "./stellar/version"
3
2
 
4
3
  module Stellar
4
+ module SDK
5
+ VERSION = ::Stellar::VERSION
6
+ end
7
+
5
8
  autoload :Account
6
9
  autoload :Amount
7
10
  autoload :Client
@@ -66,6 +66,10 @@ module Stellar
66
66
  def initialize(keypair)
67
67
  @keypair = keypair
68
68
  end
69
+
70
+ def to_keypair
71
+ keypair
72
+ end
69
73
  end
70
74
 
71
75
  class AccountNotFound < StandardError
@@ -78,12 +78,11 @@ module Stellar
78
78
  destination = options[:destination]
79
79
  sequence = options[:sequence] || (account_info(account).sequence.to_i + 1)
80
80
 
81
- transaction = Stellar::TransactionBuilder.new(
81
+ transaction = Stellar::TransactionBuilder.account_merge(
82
82
  source_account: destination.keypair,
83
- sequence_number: sequence
84
- ).add_operation(
85
- Stellar::Operation.account_merge(destination: destination.keypair)
86
- ).set_timeout(0).build
83
+ sequence_number: sequence,
84
+ destination: destination.keypair
85
+ )
87
86
 
88
87
  envelope = transaction.to_envelope(account.keypair)
89
88
  submit_transaction(tx_envelope: envelope)
@@ -105,17 +104,13 @@ module Stellar
105
104
  # instead of using a hard-coded default value.
106
105
  fee = options[:fee] || DEFAULT_FEE
107
106
 
108
- payment = Stellar::TransactionBuilder.new(
107
+ payment = Stellar::TransactionBuilder.create_account(
109
108
  source_account: funder.keypair,
110
109
  sequence_number: sequence,
111
- base_fee: fee
112
- ).add_operation(
113
- Stellar::Operation.create_account({
114
- destination: options[:account].keypair,
115
- starting_balance: options[:starting_balance]
116
- })
117
- ).set_timeout(0).build
118
-
110
+ base_fee: fee,
111
+ destination: options[:account].keypair,
112
+ starting_balance: options[:starting_balance]
113
+ )
119
114
  envelope = payment.to_envelope(funder.keypair)
120
115
  submit_transaction(tx_envelope: envelope)
121
116
  end
@@ -180,14 +175,18 @@ module Stellar
180
175
  )
181
176
  sequence ||= (account_info(source).sequence.to_i + 1)
182
177
 
183
- args = {
178
+ op_args = {
184
179
  account: source.keypair,
185
180
  sequence: sequence,
186
181
  line: asset
187
182
  }
188
- args[:limit] = limit unless limit.nil?
183
+ op_args[:limit] = limit unless limit.nil?
189
184
 
190
- tx = Stellar::Transaction.change_trust(args)
185
+ tx = Stellar::TransactionBuilder.change_trust(
186
+ source_account: source.keypair,
187
+ sequence_number: sequence,
188
+ **op_args
189
+ )
191
190
 
192
191
  envelope = tx.to_envelope(source.keypair)
193
192
  submit_transaction(tx_envelope: envelope)
data/lib/stellar/sep10.rb CHANGED
@@ -2,26 +2,34 @@ module Stellar
2
2
  class InvalidSep10ChallengeError < StandardError; end
3
3
 
4
4
  class SEP10
5
+ include Stellar::DSL
6
+
5
7
  # Helper method to create a valid {SEP0010}[https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md]
6
8
  # challenge transaction which you can use for Stellar Web Authentication.
7
9
  #
8
- # @param server [Stellar::KeyPair] Keypair for server's signing account.
9
- # @param client [Stellar::KeyPair] Keypair for the account whishing to authenticate with the server.
10
- # @param anchor_name [String] Anchor's name to be used in the manage_data key.
11
- # @param timeout [Integer] Challenge duration (default to 5 minutes).
12
- #
13
- # @return [String] A base64 encoded string of the raw TransactionEnvelope xdr struct for the transaction.
10
+ # @example
11
+ # server = Stellar::KeyPair.random # SIGNING_KEY from your stellar.toml
12
+ # user = Stellar::KeyPair.from_address('G...')
13
+ # Stellar::SEP10.build_challenge_tx(server: server, client: user, domain: 'example.com', timeout: 300)
14
14
  #
15
- # = Example
15
+ # @param server [Stellar::KeyPair] server's signing keypair (SIGNING_KEY in service's stellar.toml)
16
+ # @param client [Stellar::KeyPair] account trying to authenticate with the server
17
+ # @param domain [String] service's domain to be used in the manage_data key
18
+ # @param timeout [Integer] challenge duration (default to 5 minutes)
16
19
  #
17
- # Stellar::SEP10.build_challenge_tx(server: server, client: user, anchor_name: anchor, timeout: timeout)
20
+ # @return [String] A base64 encoded string of the raw TransactionEnvelope xdr struct for the transaction.
18
21
  #
19
22
  # @see {SEP0010: Stellar Web Authentication}[https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md]
20
- def self.build_challenge_tx(server:, client:, anchor_name:, timeout: 300)
21
- # The value must be 64 bytes long. It contains a 48 byte
22
- # cryptographic-quality random string encoded using base64 (for a total of
23
- # 64 bytes after encoding).
24
- value = SecureRandom.base64(48)
23
+ def self.build_challenge_tx(server:, client:, domain: nil, timeout: 300, **options)
24
+ if domain.blank? && options.key?(:anchor_name)
25
+ ActiveSupport::Deprecation.new("next release", "stellar-sdk").warn <<~MSG
26
+ SEP-10 v2.0.0 requires usage of service home domain instead of anchor name in the challenge transaction.
27
+ Please update your implementation to use `Stellar::SEP10.build_challenge_tx(..., home_domain: 'example.com')`.
28
+ Using `anchor_name` parameter makes your service incompatible with SEP10-2.0 clients, support for this parameter
29
+ is deprecated and will be removed in the next major release of stellar-base.
30
+ MSG
31
+ domain = options[:anchor_name]
32
+ end
25
33
 
26
34
  now = Time.now.to_i
27
35
  time_bounds = Stellar::TimeBounds.new(
@@ -29,19 +37,34 @@ module Stellar
29
37
  max_time: now + timeout
30
38
  )
31
39
 
32
- tx = Stellar::TransactionBuilder.new(
40
+ tb = Stellar::TransactionBuilder.new(
33
41
  source_account: server,
34
42
  sequence_number: 0,
35
43
  time_bounds: time_bounds
36
- ).add_operation(
44
+ )
45
+
46
+ # The value must be 64 bytes long. It contains a 48 byte
47
+ # cryptographic-quality random string encoded using base64 (for a total of
48
+ # 64 bytes after encoding).
49
+ tb.add_operation(
37
50
  Stellar::Operation.manage_data(
38
- name: "#{anchor_name} auth",
39
- value: value,
51
+ name: "#{domain} auth",
52
+ value: SecureRandom.base64(48),
40
53
  source_account: client
41
54
  )
42
- ).build
55
+ )
43
56
 
44
- tx.to_envelope(server).to_xdr(:base64)
57
+ if options.key?(:auth_domain)
58
+ tb.add_operation(
59
+ Stellar::Operation.manage_data(
60
+ name: "web_auth_domain",
61
+ value: options[:auth_domain],
62
+ source_account: server
63
+ )
64
+ )
65
+ end
66
+
67
+ tb.build.to_envelope(server).to_xdr(:base64)
45
68
  end
46
69
 
47
70
  # Reads a SEP 10 challenge transaction and returns the decoded transaction envelope and client account ID contained within.
@@ -49,76 +72,81 @@ module Stellar
49
72
  # It also verifies that transaction is signed by the server.
50
73
  #
51
74
  # It does not verify that the transaction has been signed by the client or
52
- # that any signatures other than the servers on the transaction are valid. Use
53
- # one of the following functions to completely verify the transaction:
54
- # - Stellar::SEP10.verify_challenge_tx_threshold
55
- # - Stellar::SEP10.verify_challenge_tx_signers
75
+ # that any signatures other than the servers on the transaction are valid.
76
+ # Use either {.verify_challenge_tx_threshold} or {.verify_challenge_tx_signers} to completely verify
77
+ # the signed challenge
78
+ #
79
+ # @example
80
+ # sep10 = Stellar::SEP10
81
+ # server = Stellar::KeyPair.random # this should be the SIGNING_KEY from your stellar.toml
82
+ # challenge = sep10.build_challenge_tx(server: server, client: user, domain: domain, timeout: timeout)
83
+ # envelope, client_address = sep10.read_challenge_tx(server: server, challenge_xdr: challenge)
56
84
  #
57
85
  # @param challenge_xdr [String] SEP0010 transaction challenge in base64.
58
86
  # @param server [Stellar::KeyPair] keypair for server where the challenge was generated.
59
87
  #
60
- # @return [Stellar::TransactionEnvelope, String]
61
- #
62
- # = Example
63
- #
64
- # sep10 = Stellar::SEP10
65
- # challenge = sep10.build_challenge_tx(server: server, client: user, anchor_name: anchor, timeout: timeout)
66
- # envelope, client_address = sep10.read_challenge_tx(challenge: challenge, server: server)
67
- #
68
- def self.read_challenge_tx(challenge_xdr:, server:)
88
+ # @return [Array(Stellar::TransactionEnvelope, String)]
89
+ def self.read_challenge_tx(server:, challenge_xdr:, **options)
69
90
  envelope = Stellar::TransactionEnvelope.from_xdr(challenge_xdr, "base64")
70
91
  transaction = envelope.tx
71
92
 
72
93
  if transaction.seq_num != 0
73
- raise InvalidSep10ChallengeError.new(
74
- "The transaction sequence number should be zero"
75
- )
94
+ raise InvalidSep10ChallengeError, "The transaction sequence number should be zero"
76
95
  end
77
96
 
78
97
  if transaction.source_account != server.muxed_account
79
- raise InvalidSep10ChallengeError.new(
80
- "The transaction source account is not equal to the server's account"
81
- )
98
+ raise InvalidSep10ChallengeError, "The transaction source account is not equal to the server's account"
82
99
  end
83
100
 
84
- if transaction.operations.size != 1
85
- raise InvalidSep10ChallengeError.new(
86
- "The transaction should contain only one operation"
87
- )
101
+ if transaction.operations.size < 1
102
+ raise InvalidSep10ChallengeError, "The transaction should contain at least one operation"
88
103
  end
89
104
 
90
- operation = transaction.operations.first
91
- client_account_id = operation.source_account
105
+ auth_op, *rest_ops = transaction.operations
106
+ client_account_id = auth_op.source_account
92
107
 
93
- if client_account_id.nil?
94
- raise InvalidSep10ChallengeError.new(
95
- "The transaction's operation should contain a source account"
96
- )
108
+ auth_op_body = auth_op.body.value
109
+
110
+ if client_account_id.blank?
111
+ raise InvalidSep10ChallengeError, "The transaction's operation should contain a source account"
97
112
  end
98
113
 
99
- if operation.body.arm != :manage_data_op
100
- raise InvalidSep10ChallengeError.new(
101
- "The transaction's operation should be manageData"
102
- )
114
+ if auth_op.body.arm != :manage_data_op
115
+ raise InvalidSep10ChallengeError, "The transaction's first operation should be manageData"
103
116
  end
104
117
 
105
- if operation.body.value.data_value.unpack1("m").size != 48
106
- raise InvalidSep10ChallengeError.new(
107
- "The transaction's operation value should be a 64 bytes base64 random string"
108
- )
118
+ if options.key?(:domain) && auth_op_body.data_name != "#{options[:domain]} auth"
119
+ raise InvalidSep10ChallengeError, "The transaction's operation data name is invalid"
120
+ end
121
+
122
+ if auth_op_body.data_value.unpack1("m").size != 48
123
+ raise InvalidSep10ChallengeError, "The transaction's operation value should be a 64 bytes base64 random string"
124
+ end
125
+
126
+ rest_ops.each do |op|
127
+ body = op.body
128
+
129
+ if body.arm != :manage_data_op
130
+ raise InvalidSep10ChallengeError, "The transaction has operations that are not of type 'manageData'"
131
+ elsif op.source_account != server.muxed_account
132
+ 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
138
+ end
109
139
  end
110
140
 
111
141
  unless verify_tx_signed_by(tx_envelope: envelope, keypair: server)
112
- raise InvalidSep10ChallengeError.new(
113
- "The transaction is not signed by the server"
114
- )
142
+ raise InvalidSep10ChallengeError, "The transaction is not signed by the server"
115
143
  end
116
144
 
117
145
  time_bounds = transaction.time_bounds
118
146
  now = Time.now.to_i
119
147
 
120
- if time_bounds.nil? || !now.between?(time_bounds.min_time, time_bounds.max_time)
121
- raise InvalidSep10ChallengeError.new("The transaction has expired")
148
+ if time_bounds.blank? || !now.between?(time_bounds.min_time, time_bounds.max_time)
149
+ raise InvalidSep10ChallengeError, "The transaction has expired"
122
150
  end
123
151
 
124
152
  # Mirror the return type of the other SDK's and return a string
@@ -127,6 +155,36 @@ module Stellar
127
155
  [envelope, client_kp.address]
128
156
  end
129
157
 
158
+ # Verifies that for a SEP 10 challenge transaction all signatures on the transaction
159
+ # are accounted for and that the signatures meet a threshold on an account. A
160
+ # transaction is verified if it is signed by the server account, and all other
161
+ # signatures match a signer that has been provided as an argument, and those
162
+ # signatures meet a threshold on the account.
163
+ #
164
+ # @param server [Stellar::KeyPair] keypair for server's account.
165
+ # @param challenge_xdr [String] SEP0010 challenge transaction in base64.
166
+ # @param signers [{String => Integer}] The signers of client account.
167
+ # @param threshold [Integer] The medThreshold on the client account.
168
+ #
169
+ # @raise InvalidSep10ChallengeError if the transaction has unrecognized signatures (only server's
170
+ # signing key and keypairs found in the `signing` argument are recognized) or total weight of
171
+ # the signers does not meet the `threshold`
172
+ #
173
+ # @return [<String>] subset of input signers who have signed `challenge_xdr`
174
+ def self.verify_challenge_tx_threshold(server:, challenge_xdr:, signers:, threshold:)
175
+ signers_found = verify_challenge_tx_signers(
176
+ server: server, challenge_xdr: challenge_xdr, signers: signers.keys
177
+ )
178
+
179
+ total_weight = signers.values_at(*signers_found).sum
180
+
181
+ if total_weight < threshold
182
+ raise InvalidSep10ChallengeError, "signers with weight #{total_weight} do not meet threshold #{threshold}."
183
+ end
184
+
185
+ signers_found
186
+ end
187
+
130
188
  # Verifies that for a SEP 10 challenge transaction all signatures on the transaction are accounted for.
131
189
  #
132
190
  # A transaction is verified if it is signed by the server account, and all other signatures match a signer
@@ -135,211 +193,82 @@ module Stellar
135
193
  #
136
194
  # If verification succeeds a list of signers that were found is returned, excluding the server account ID.
137
195
  #
196
+ # @param server [Stellar::Keypair] server's signing key
138
197
  # @param challenge_xdr [String] SEP0010 transaction challenge transaction in base64.
139
- # @param server [Stellar::Keypair] keypair for server's account.
140
- # @param signers [SetOf[String]] The signers of client account.
198
+ # @param signers [<String>] The signers of client account.
141
199
  #
142
- # @return [SetOf[String]]
200
+ # @raise InvalidSep10ChallengeError one or more signatures in the transaction are not identifiable
201
+ # as the server account or one of the signers provided in the arguments
143
202
  #
144
- # Raises a InvalidSep10ChallengeError if:
145
- # - The transaction is invalid according to Stellar::SEP10.read_challenge_tx.
146
- # - One or more signatures in the transaction are not identifiable as the server account or one of the
147
- # signers provided in the arguments.
148
- def self.verify_challenge_tx_signers(
149
- challenge_xdr:,
150
- server:,
151
- signers:
152
- )
153
- if signers.empty?
154
- raise InvalidSep10ChallengeError.new("No signers provided.")
155
- end
156
-
157
- te, _ = read_challenge_tx(
158
- challenge_xdr: challenge_xdr, server: server
159
- )
203
+ # @return [<String>] subset of input signers who have signed `challenge_xdr`
204
+ def self.verify_challenge_tx_signers(server:, challenge_xdr:, signers:)
205
+ raise InvalidSep10ChallengeError, "no signers provided" if signers.empty?
160
206
 
161
- # deduplicate signers and ignore non-G addresses
162
- client_signers = Set.new
163
- signers.each do |signer|
164
- # ignore server kp if passed
165
- if signer == server.address
166
- next
167
- end
168
- begin
169
- Stellar::Util::StrKey.check_decode(:account_id, signer)
170
- rescue
171
- next
172
- else
173
- client_signers.add(signer)
174
- end
175
- end
207
+ te, _ = read_challenge_tx(server: server, challenge_xdr: challenge_xdr)
176
208
 
177
- if client_signers.empty?
178
- raise InvalidSep10ChallengeError.new("At least one signer with a G... address must be provied")
179
- end
209
+ # ignore non-G signers and server's own address
210
+ client_signers = signers.select { |s| s =~ /G[A-Z0-9]{55}/ && s != server.address }.to_set
211
+ raise InvalidSep10ChallengeError, "at least one regular signer must be provided" if client_signers.empty?
180
212
 
181
213
  # verify all signatures in one pass
182
- all_signers = client_signers + Set[server.address]
183
- signers_found = verify_tx_signatures(
184
- tx_envelope: te, signers: all_signers
185
- )
214
+ client_signers.add(server.address)
215
+ signers_found = verify_tx_signatures(tx_envelope: te, signers: client_signers)
186
216
 
187
217
  # ensure server signed transaction and remove it
188
218
  unless signers_found.delete?(server.address)
189
- raise InvalidSep10ChallengeError.new("Transaction not signed by server: #{server.address}")
219
+ raise InvalidSep10ChallengeError, "Transaction not signed by server: #{server.address}"
190
220
  end
191
221
 
192
222
  # Confirm we matched signatures to the client signers.
193
223
  if signers_found.empty?
194
- raise InvalidSep10ChallengeError.new("Transaction not signed by any client signer.")
224
+ raise InvalidSep10ChallengeError, "Transaction not signed by any client signer."
195
225
  end
196
226
 
197
227
  # Confirm all signatures were consumed by a signer.
198
- if signers_found.length != te.signatures.length - 1
199
- raise InvalidSep10ChallengeError.new("Transaction has unrecognized signatures.")
228
+ if signers_found.size != te.signatures.length - 1
229
+ raise InvalidSep10ChallengeError, "Transaction has unrecognized signatures."
200
230
  end
201
231
 
202
232
  signers_found
203
233
  end
204
234
 
205
- # Verifies that for a SEP 10 challenge transaction all signatures on the transaction
206
- # are accounted for and that the signatures meet a threshold on an account. A
207
- # transaction is verified if it is signed by the server account, and all other
208
- # signatures match a signer that has been provided as an argument, and those
209
- # signatures meet a threshold on the account.
210
- #
211
- # @param challenge_xdr [String] SEP0010 transaction challenge transaction in base64.
212
- # @param server [Stellar::KeyPair] keypair for server's account.
213
- # @param threshold [Integer] The medThreshold on the client account.
214
- # @param signers [SetOf[::Hash]]The signers of client account.
215
- #
216
- # @return [SetOf[::Hash]]
217
- #
218
- # Raises a InvalidSep10ChallengeError if:
219
- # - The transaction is invalid according to Stellar::SEP10.read_challenge_transaction.
220
- # - One or more signatures in the transaction are not identifiable as the server
221
- # account or one of the signers provided in the arguments.
222
- # - The signatures are all valid but do not meet the threshold.
223
- def self.verify_challenge_tx_threshold(
224
- challenge_xdr:,
225
- server:,
226
- threshold:,
227
- signers:
228
- )
229
- signer_str_set = signers.map { |s| s["key"] }.to_set
230
- signer_strs_found = verify_challenge_tx_signers(
231
- challenge_xdr: challenge_xdr,
232
- server: server,
233
- signers: signer_str_set
234
- )
235
-
236
- weight = 0
237
- signers_found = Set.new
238
- signers.each do |s|
239
- unless signer_strs_found.include?(s["key"])
240
- next
241
- end
242
- signer_strs_found.delete(s["key"])
243
- signers_found.add(s)
244
- weight += s["weight"]
245
- end
246
-
247
- if weight < threshold
248
- raise InvalidSep10ChallengeError.new(
249
- "signers with weight #{weight} do not meet threshold #{threshold}."
250
- )
251
- end
252
-
253
- signers_found
254
- end
255
-
256
- # DEPRECATED: Use verify_challenge_tx_signers instead.
257
- # This function does not support multiple client signatures.
258
- #
259
- # Verifies if a transaction is a valid per SEP-10 challenge transaction, if the validation
260
- # fails, an exception will be thrown.
261
- #
262
- # This function performs the following checks:
263
- # 1. verify that transaction sequenceNumber is equal to zero;
264
- # 2. verify that transaction source account is equal to the server's signing key;
265
- # 3. verify that transaction has time bounds set, and that current time is between the minimum and maximum bounds;
266
- # 4. verify that transaction contains a single Manage Data operation and it's source account is not null;
267
- # 5. verify that transaction envelope has a correct signature by server's signing key;
268
- # 6. verify that transaction envelope has a correct signature by the operation's source account;
269
- #
270
- # @param challenge_xdr [String] SEP0010 transaction challenge transaction in base64.
271
- # @param server [Stellar::KeyPair] keypair for server's account.
272
- #
273
- # Raises a InvalidSep10ChallengeError if the validation fails
274
- def self.verify_challenge_tx(
275
- challenge_xdr: String, server: Stellar::KeyPair
276
- )
277
- transaction_envelope, client_address = read_challenge_tx(
278
- challenge_xdr: challenge_xdr, server: server
279
- )
280
- client_keypair = Stellar::KeyPair.from_address(client_address)
281
- unless verify_tx_signed_by(tx_envelope: transaction_envelope, keypair: client_keypair)
282
- raise InvalidSep10ChallengeError.new(
283
- "Transaction not signed by client: %s" % [client_keypair.address]
284
- )
285
- end
286
- end
287
-
288
235
  # Verifies every signer passed matches a signature on the transaction exactly once,
289
236
  # returning a list of unique signers that were found to have signed the transaction.
290
237
  #
291
238
  # @param tx_envelope [Stellar::TransactionEnvelope] SEP0010 transaction challenge transaction envelope.
292
- # @param signers [SetOf[String]] The signers of client account.
239
+ # @param signers [<String>] The signers of client account.
293
240
  #
294
- # @return [SetOf[String]]
295
- def self.verify_tx_signatures(
296
- tx_envelope:,
297
- signers:
298
- )
241
+ # @return [Set<Stellar::KeyPair>]
242
+ def self.verify_tx_signatures(tx_envelope:, signers:)
299
243
  signatures = tx_envelope.signatures
300
244
  if signatures.empty?
301
- raise InvalidSep10ChallengeError.new("Transaction has no signatures.")
245
+ raise InvalidSep10ChallengeError, "Transaction has no signatures."
302
246
  end
303
247
 
304
248
  tx_hash = tx_envelope.tx.hash
305
- signatures_used = Set.new
306
- signers_found = Set.new
307
- signers.each do |signer|
308
- kp = Stellar::KeyPair.from_address(signer)
309
- tx_envelope.signatures.each_with_index do |sig, i|
310
- if signatures_used.include?(i)
311
- next
312
- end
313
- if sig.hint != kp.signature_hint
314
- next
315
- end
316
- if kp.verify(sig.signature, tx_hash)
317
- signatures_used.add(i)
318
- signers_found.add(signer)
319
- end
320
- end
321
- end
249
+ to_keypair = Stellar::DSL.method(:KeyPair)
250
+ keys_by_hint = signers.map(&to_keypair).index_by(&:signature_hint)
322
251
 
323
- signers_found
252
+ tx_envelope.signatures.each.with_object(Set.new) do |sig, result|
253
+ key = keys_by_hint.delete(sig.hint)
254
+ result.add(key.address) if key&.verify(sig.signature, tx_hash)
255
+ end
324
256
  end
325
257
 
326
258
  # Verifies if a Stellar::TransactionEnvelope was signed by the given Stellar::KeyPair
327
259
  #
260
+ # @example
261
+ # Stellar::SEP10.verify_tx_signed_by(tx_envelope: envelope, keypair: keypair)
262
+ #
328
263
  # @param tx_envelope [Stellar::TransactionEnvelope]
329
264
  # @param keypair [Stellar::KeyPair]
330
265
  #
331
266
  # @return [Boolean]
332
- #
333
- # = Example
334
- #
335
- # Stellar::SEP10.verify_tx_signed_by(tx_envelope: envelope, keypair: keypair)
336
- #
337
267
  def self.verify_tx_signed_by(tx_envelope:, keypair:)
338
268
  tx_hash = tx_envelope.tx.hash
339
269
  tx_envelope.signatures.any? do |sig|
340
- if sig.hint != keypair.signature_hint
341
- next
342
- end
270
+ next if sig.hint != keypair.signature_hint
271
+
343
272
  keypair.verify(sig.signature, tx_hash)
344
273
  end
345
274
  end
metadata CHANGED
@@ -1,14 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stellar-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.1
4
+ version: 0.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Fleckenstein
8
+ - Sergey Nebolsin
9
+ - Timur Ramazanov
8
10
  autorequire:
9
- bindir: bin
11
+ bindir: exe
10
12
  cert_chain: []
11
- date: 2020-06-18 00:00:00.000000000 Z
13
+ date: 2021-05-09 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: stellar-base
@@ -16,82 +18,144 @@ dependencies:
16
18
  requirements:
17
19
  - - '='
18
20
  - !ruby/object:Gem::Version
19
- version: 0.23.1
21
+ version: 0.27.0
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
23
25
  requirements:
24
26
  - - '='
25
27
  - !ruby/object:Gem::Version
26
- version: 0.23.1
28
+ version: 0.27.0
27
29
  - !ruby/object:Gem::Dependency
28
- name: hyperclient
30
+ name: activesupport
29
31
  requirement: !ruby/object:Gem::Requirement
30
32
  requirements:
31
- - - "~>"
33
+ - - ">="
32
34
  - !ruby/object:Gem::Version
33
- version: '0.7'
35
+ version: 5.0.0
36
+ - - "<"
37
+ - !ruby/object:Gem::Version
38
+ version: '7.0'
34
39
  type: :runtime
35
40
  prerelease: false
36
41
  version_requirements: !ruby/object:Gem::Requirement
37
42
  requirements:
38
- - - "~>"
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 5.0.0
46
+ - - "<"
39
47
  - !ruby/object:Gem::Version
40
- version: '0.7'
48
+ version: '7.0'
41
49
  - !ruby/object:Gem::Dependency
42
50
  name: excon
43
51
  requirement: !ruby/object:Gem::Requirement
44
52
  requirements:
45
- - - "~>"
53
+ - - ">="
46
54
  - !ruby/object:Gem::Version
47
- version: '0.71'
55
+ version: 0.71.0
56
+ - - "<"
57
+ - !ruby/object:Gem::Version
58
+ version: '1.0'
48
59
  type: :runtime
49
60
  prerelease: false
50
61
  version_requirements: !ruby/object:Gem::Requirement
51
62
  requirements:
52
- - - "~>"
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 0.71.0
66
+ - - "<"
53
67
  - !ruby/object:Gem::Version
54
- version: '0.71'
68
+ version: '1.0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: activesupport
70
+ name: hyperclient
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
60
74
  - !ruby/object:Gem::Version
61
- version: '5.0'
75
+ version: 0.7.0
76
+ - - "<"
77
+ - !ruby/object:Gem::Version
78
+ version: '2.0'
62
79
  type: :runtime
63
80
  prerelease: false
64
81
  version_requirements: !ruby/object:Gem::Requirement
65
82
  requirements:
66
83
  - - ">="
67
84
  - !ruby/object:Gem::Version
68
- version: '5.0'
85
+ version: 0.7.0
86
+ - - "<"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.0'
69
89
  - !ruby/object:Gem::Dependency
70
90
  name: toml-rb
71
91
  requirement: !ruby/object:Gem::Requirement
72
92
  requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '1.1'
76
93
  - - ">="
77
94
  - !ruby/object:Gem::Version
78
95
  version: 1.1.1
96
+ - - "<"
97
+ - !ruby/object:Gem::Version
98
+ version: '3.0'
79
99
  type: :runtime
80
100
  prerelease: false
81
101
  version_requirements: !ruby/object:Gem::Requirement
82
102
  requirements:
83
- - - "~>"
84
- - !ruby/object:Gem::Version
85
- version: '1.1'
86
103
  - - ">="
87
104
  - !ruby/object:Gem::Version
88
105
  version: 1.1.1
106
+ - - "<"
107
+ - !ruby/object:Gem::Version
108
+ 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'
89
151
  description:
90
152
  email:
91
- - scott@stellar.org
92
153
  executables: []
93
154
  extensions: []
94
- extra_rdoc_files: []
155
+ extra_rdoc_files:
156
+ - README.md
157
+ - LICENSE
158
+ - CHANGELOG.md
95
159
  files:
96
160
  - CHANGELOG.md
97
161
  - LICENSE
@@ -103,11 +167,16 @@ files:
103
167
  - lib/stellar/horizon/problem.rb
104
168
  - lib/stellar/sep10.rb
105
169
  - lib/stellar/transaction_page.rb
106
- - lib/stellar/version.rb
107
- homepage: http://github.com/stellar/ruby-stellar-sdk
170
+ homepage: https://github.com/astroband/ruby-stellar-sdk
108
171
  licenses:
109
172
  - Apache-2.0
110
- metadata: {}
173
+ metadata:
174
+ 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/
177
+ github_repo: ssh://github.com/astroband/ruby-stellar-sdk
178
+ 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
111
180
  post_install_message:
112
181
  rdoc_options: []
113
182
  require_paths:
@@ -116,14 +185,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
185
  requirements:
117
186
  - - ">="
118
187
  - !ruby/object:Gem::Version
119
- version: '0'
188
+ version: 2.5.0
120
189
  required_rubygems_version: !ruby/object:Gem::Requirement
121
190
  requirements:
122
191
  - - ">="
123
192
  - !ruby/object:Gem::Version
124
193
  version: '0'
125
194
  requirements: []
126
- rubygems_version: 3.1.4
195
+ rubygems_version: 3.2.16
127
196
  signing_key:
128
197
  specification_version: 4
129
198
  summary: Stellar client library
@@ -1,3 +0,0 @@
1
- module Stellar
2
- VERSION = "0.23.1"
3
- end