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 +4 -4
- data/CHANGELOG.md +31 -5
- data/README.md +7 -7
- data/lib/stellar-sdk.rb +4 -1
- data/lib/stellar/account.rb +4 -0
- data/lib/stellar/client.rb +16 -17
- data/lib/stellar/sep10.rb +151 -222
- metadata +99 -30
- data/lib/stellar/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4956394163d9ef780494428a310af5d3a80b2b63caba45be39e42e55b7557298
|
4
|
+
data.tar.gz: 482c2efc7b68b1348551a08f9e8ab4dc17e723ae86b9dad76fd1bc94e42f72d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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/
|
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/
|
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
|
-
|
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/
|
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
|
2
|
-
|
3
|
-
[](https://badge.fury.io/rb/stellar-sdk)
|
3
|
+
[](https://github.com/astroband/ruby-stellar-sdk/actions?query=branch%3Amaster)
|
4
|
+
[](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/
|
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/
|
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
data/lib/stellar/account.rb
CHANGED
data/lib/stellar/client.rb
CHANGED
@@ -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.
|
81
|
+
transaction = Stellar::TransactionBuilder.account_merge(
|
82
82
|
source_account: destination.keypair,
|
83
|
-
sequence_number: sequence
|
84
|
-
|
85
|
-
|
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.
|
107
|
+
payment = Stellar::TransactionBuilder.create_account(
|
109
108
|
source_account: funder.keypair,
|
110
109
|
sequence_number: sequence,
|
111
|
-
base_fee: fee
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
178
|
+
op_args = {
|
184
179
|
account: source.keypair,
|
185
180
|
sequence: sequence,
|
186
181
|
line: asset
|
187
182
|
}
|
188
|
-
|
183
|
+
op_args[:limit] = limit unless limit.nil?
|
189
184
|
|
190
|
-
tx = Stellar::
|
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
|
-
# @
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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:,
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
40
|
+
tb = Stellar::TransactionBuilder.new(
|
33
41
|
source_account: server,
|
34
42
|
sequence_number: 0,
|
35
43
|
time_bounds: time_bounds
|
36
|
-
)
|
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: "#{
|
39
|
-
value:
|
51
|
+
name: "#{domain} auth",
|
52
|
+
value: SecureRandom.base64(48),
|
40
53
|
source_account: client
|
41
54
|
)
|
42
|
-
)
|
55
|
+
)
|
43
56
|
|
44
|
-
|
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.
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
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
|
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
|
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
|
85
|
-
raise InvalidSep10ChallengeError
|
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
|
-
|
91
|
-
client_account_id =
|
105
|
+
auth_op, *rest_ops = transaction.operations
|
106
|
+
client_account_id = auth_op.source_account
|
92
107
|
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
100
|
-
raise InvalidSep10ChallengeError
|
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
|
106
|
-
raise InvalidSep10ChallengeError
|
107
|
-
|
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
|
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.
|
121
|
-
raise InvalidSep10ChallengeError
|
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
|
140
|
-
# @param signers [SetOf[String]] The signers of client account.
|
198
|
+
# @param signers [<String>] The signers of client account.
|
141
199
|
#
|
142
|
-
# @
|
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
|
-
#
|
145
|
-
|
146
|
-
|
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
|
-
|
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
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
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
|
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
|
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.
|
199
|
-
raise InvalidSep10ChallengeError
|
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 [
|
239
|
+
# @param signers [<String>] The signers of client account.
|
293
240
|
#
|
294
|
-
# @return [
|
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
|
245
|
+
raise InvalidSep10ChallengeError, "Transaction has no signatures."
|
302
246
|
end
|
303
247
|
|
304
248
|
tx_hash = tx_envelope.tx.hash
|
305
|
-
|
306
|
-
|
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
|
-
|
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
|
-
|
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.
|
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:
|
11
|
+
bindir: exe
|
10
12
|
cert_chain: []
|
11
|
-
date:
|
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.
|
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.
|
28
|
+
version: 0.27.0
|
27
29
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
30
|
+
name: activesupport
|
29
31
|
requirement: !ruby/object:Gem::Requirement
|
30
32
|
requirements:
|
31
|
-
- - "
|
33
|
+
- - ">="
|
32
34
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
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
|
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:
|
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
|
68
|
+
version: '1.0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: hyperclient
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
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:
|
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
|
-
|
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:
|
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.
|
195
|
+
rubygems_version: 3.2.16
|
127
196
|
signing_key:
|
128
197
|
specification_version: 4
|
129
198
|
summary: Stellar client library
|
data/lib/stellar/version.rb
DELETED