stellar-sdk 0.23.1 → 0.27.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: 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