mobius-client 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +29 -23
- data/lib/mobius/client/app.rb +59 -65
- data/lib/mobius/client/blockchain/account.rb +3 -3
- data/lib/mobius/client/error.rb +4 -0
- data/lib/mobius/client/version.rb +1 -1
- data/lib/mobius/client.rb +7 -0
- data/mobius-client.gemspec +1 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f26fccdb1893d53cbb280832a4fe527a98f17c9d62bb4350cc07629caea51251
|
4
|
+
data.tar.gz: c2b3d55a4c15c334617d3571d1e1ad0ac1a8436685f5e965dcf1854d55d59f64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62078725168f732c21c720471ab26780dce72fd19869d0177715ff69caa49aee7e29eb815b0f44e91777c48d15a0473dd6976f5b38ee335af4aa0cc346e0d664
|
7
|
+
data.tar.gz: fadbdb441858930947ccf18895505fd5c5c44525acb99fe365f2212bbaeced1cf15e26426215a7ad3520e1caf792963704c25d5c16930141afd0ce031c72e548
|
data/README.md
CHANGED
@@ -22,6 +22,14 @@ An overview of the DApp Store architecture is:
|
|
22
22
|
1) Adds the application's public key as a signer so the application can access the MOBI and
|
23
23
|
2) Signs a challenge transaction from the app with its secret key to authenticate that this user owns the account. This prevents a different person from pretending they own the account and spending the MOBI (more below under Authentication).
|
24
24
|
|
25
|
+
## Sample Application
|
26
|
+
|
27
|
+
[Flappy Bird](https://github.com/mobius-network/flappy-bird-dapp) has been reimplemented using this new arhictecture and the above simple server code!
|
28
|
+
|
29
|
+
## Documentation
|
30
|
+
|
31
|
+
[[RDoc.info](http://www.rubydoc.info/github/mobius-network/mobius-client-ruby/master)]
|
32
|
+
|
25
33
|
## Installation
|
26
34
|
|
27
35
|
Add this line to your application's Gemfile:
|
@@ -50,41 +58,47 @@ You can also obtain free test network MOBI from https://mobius.network/friendbot
|
|
50
58
|
|
51
59
|
### Setting up test user accounts
|
52
60
|
|
53
|
-
1. Create empty Stellar account without a MOBI trustline.
|
61
|
+
1. Create an empty Stellar account without a MOBI trustline.
|
54
62
|
```
|
55
63
|
$ mobius-cli create account
|
56
64
|
```
|
57
|
-
2. Create stellar account with 1,000 test-net MOBI
|
65
|
+
2. Create a stellar account with 1,000 test-net MOBI
|
58
66
|
```
|
59
67
|
$ mobius-cli create dapp-account
|
60
68
|
```
|
61
|
-
3. Create stellar account with 1,000 test-net MOBI and the specified application public key added as a signer
|
69
|
+
3. Create a stellar account with 1,000 test-net MOBI and the specified application public key added as a signer
|
62
70
|
```
|
63
71
|
$ mobius-cli create dapp-account -a <Your application public key>
|
64
72
|
```
|
65
73
|
|
66
74
|
### Account Creation Wizard
|
67
75
|
|
68
|
-
|
76
|
+
The below command will create and setup the 4 account types above for testing and generate a simple HTML test interface that simulates the DApp Store authentication functionality (obtaining a challenge request from an app, signing it, and then opening the specified app passing in a JWT encoded token the application will use to verify this request is from the user that owns the specified MOBI account).
|
69
77
|
|
70
78
|
```
|
71
79
|
$ mobius-cli create dev-wallet
|
72
80
|
```
|
73
81
|
|
82
|
+
## Production Server Setup
|
83
|
+
|
84
|
+
Your production server must use HTTPS and set the below header on the `/auth` endpoint:
|
85
|
+
|
86
|
+
`Access-Control-Allow-Origin: *`
|
87
|
+
|
74
88
|
## Authentication
|
75
89
|
|
76
90
|
### Explanation
|
77
91
|
|
78
92
|
When a user opens an app through the DApp Store it tells the app what Mobius account it should use for payment.
|
79
93
|
|
80
|
-
The application needs to ensure that the user actually owns the secret key to the Mobius account and that this isn't a replay attack from a user who captured a previous request and is
|
94
|
+
The application needs to ensure that the user actually owns the secret key to the Mobius account and that this isn't a replay attack from a user who captured a previous request and is replaying it.
|
81
95
|
|
82
96
|
This authentication is accomplished through the following process:
|
83
97
|
|
84
98
|
* When the user opens an app in the DApp Store it requests a challenge from the application.
|
85
99
|
* The challenge is a payment transaction of 1 XLM from and to the application account. It is never sent to the network - it is just used for authentication.
|
86
|
-
* The application generates the challenge transaction on request, signs it with
|
87
|
-
*
|
100
|
+
* The application generates the challenge transaction on request, signs it with its own private key, and sends it to user.
|
101
|
+
* The user receives the challenge transaction and verifies it is signed by the application's secret key by checking it against the application's published public key (that it receives through the DApp Store). Then the user signs the transaction with its own private key and sends it back to application along with its public key.
|
88
102
|
* Application checks that challenge transaction is now signed by itself and the public key that was passed in. Time bounds are also checked to make sure this isn't a replay attack. If everything passes the server replies with a token the application can pass in to "login" with the specified public key and use it for payment (it would have previously given the app access to the public key by adding the app's public key as a signer).
|
89
103
|
|
90
104
|
Note: the challenge transaction also has time bounds to restrict the time window when it can be used.
|
@@ -97,7 +111,7 @@ See demo at:
|
|
97
111
|
|
98
112
|
### Sample Server Implementation
|
99
113
|
|
100
|
-
```
|
114
|
+
```ruby
|
101
115
|
class AuthController < ApplicationController
|
102
116
|
skip_before_action :verify_authenticity_token, :only => [:authenticate]
|
103
117
|
|
@@ -148,7 +162,7 @@ end
|
|
148
162
|
|
149
163
|
### Explanation
|
150
164
|
|
151
|
-
After the user completes the authentication process they have a token
|
165
|
+
After the user completes the authentication process they have a token. They now pass it to the application to "login" which tells the application which Mobius account to withdraw MOBI from (the user public key) when a payment is needed. For a web application the token is generally passed in via a `token` request parameter. Upon opening the website/loading the application it checks that the token is valid (within time bounds etc) and the account in the token has added the app as a signer so it can withdraw MOBI from it.
|
152
166
|
|
153
167
|
|
154
168
|
See demo at:
|
@@ -159,7 +173,7 @@ See demo at:
|
|
159
173
|
|
160
174
|
### Sample Server Implementation
|
161
175
|
|
162
|
-
```
|
176
|
+
```ruby
|
163
177
|
class AppController < ApplicationController
|
164
178
|
skip_before_action :verify_authenticity_token, :only => [:pay]
|
165
179
|
|
@@ -182,9 +196,9 @@ class AppController < ApplicationController
|
|
182
196
|
render plain: app.balance
|
183
197
|
end
|
184
198
|
|
185
|
-
# POST /
|
186
|
-
def
|
187
|
-
app.
|
199
|
+
# POST /charge
|
200
|
+
def charge
|
201
|
+
app.charge(ROUND_PRICE)
|
188
202
|
render plain: app.balance
|
189
203
|
rescue Mobius::Client::Error::InsufficientFunds
|
190
204
|
render :gone
|
@@ -211,13 +225,9 @@ class AppController < ApplicationController
|
|
211
225
|
end
|
212
226
|
```
|
213
227
|
|
214
|
-
## Sample Application
|
215
|
-
|
216
|
-
[Flappy Bird](https://github.com/mobius-network/flappy-bird-dapp) has been reimplemented using this new arhictecture and the above simple server code!
|
217
|
-
|
218
228
|
## CLI Test Implementation
|
219
229
|
|
220
|
-
Normally
|
230
|
+
Normally the Mobius DApp Store will request a challenge, validate and sign it, pass it back to the application to obtain an access token, and then open the application and pass in the token.
|
221
231
|
|
222
232
|
For development purposes you can use the simple HTML test interface generated via `mobius-cli create dev-wallet` as mentioned above in the "Account Creation Wizard" section or you can use the these CLI commands.
|
223
233
|
|
@@ -237,10 +247,6 @@ end
|
|
237
247
|
|
238
248
|
Check `lib/mobius/cli/auth.rb` for details.
|
239
249
|
|
240
|
-
## Documentation
|
241
|
-
|
242
|
-
[[RDoc.info](http://www.rubydoc.info/github/mobius-network/mobius-client-ruby/master)]
|
243
|
-
|
244
250
|
## Development
|
245
251
|
|
246
252
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -257,4 +263,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
257
263
|
|
258
264
|
## Code of Conduct
|
259
265
|
|
260
|
-
Everyone interacting in the Mobius::Client project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
266
|
+
Everyone interacting in the Mobius::Client project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/mobius-network/mobius-client-ruby/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/mobius/client/app.rb
CHANGED
@@ -19,109 +19,97 @@ class Mobius::Client::App
|
|
19
19
|
# @return [Float] User balance.
|
20
20
|
def balance
|
21
21
|
validate!
|
22
|
-
|
22
|
+
user_account.balance
|
23
23
|
end
|
24
24
|
|
25
25
|
# Returns application balance.
|
26
26
|
# @return [Float] Application balance.
|
27
27
|
def app_balance
|
28
|
-
|
28
|
+
app_account.balance
|
29
29
|
end
|
30
30
|
|
31
31
|
# Makes payment.
|
32
|
-
# @param amount [
|
32
|
+
# @param amount [Numeric, String] Payment amount.
|
33
33
|
# @param target_address [String] Optional: third party receiver address.
|
34
|
-
#
|
34
|
+
# @deprecated use {#charge} instead
|
35
35
|
def pay(amount, target_address: nil)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
warn "[DEPRECATED] method Mobius::Client::App#pay is deprecated and will be removed, use Mobius::Client::App#charge instead"
|
37
|
+
charge(amount, target_address)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Charges user's wallet.
|
41
|
+
# @param amount [Numeric, String] Payment amount.
|
42
|
+
# @param target_address [String] Optional: third party receiver address.
|
43
|
+
def charge(amount, target_address: nil)
|
44
|
+
amount = cast_amount(amount)
|
45
|
+
|
46
|
+
raise Mobius::Client::Error::InsufficientFunds if balance < amount
|
47
|
+
|
48
|
+
submit_tx do |operations|
|
49
|
+
operations << payment_op(amount, dest: app_keypair, src: user_keypair)
|
50
|
+
operations << payment_op(amount, dest: target_address, src: app_keypair) if target_address
|
41
51
|
end
|
42
52
|
rescue Faraday::ClientError => err
|
43
53
|
handle(err)
|
44
54
|
end
|
45
|
-
# rubocop:enable Metrics/AbcSize
|
46
55
|
|
47
|
-
# Sends money from
|
56
|
+
# Sends money from user's account to third party.
|
48
57
|
# @param amount [Float] Payment amount.
|
49
58
|
# @param address [String] Target address.
|
50
|
-
# rubocop:disable Metrics/AbcSize
|
51
59
|
def transfer(amount, address)
|
52
|
-
|
53
|
-
raise Mobius::Client::Error::InsufficientFunds if
|
54
|
-
|
55
|
-
post_tx(envelope_base64).tap do
|
56
|
-
[app_account, user_account].each(&:reload!)
|
57
|
-
end
|
60
|
+
amount = cast_amount(amount)
|
61
|
+
raise Mobius::Client::Error::InsufficientFunds if app_balance < amount
|
62
|
+
submit_tx { |operations| operations << payment_op(amount, dest: address, src: user_keypair) }
|
58
63
|
rescue Faraday::ClientError => err
|
59
64
|
handle(err)
|
60
65
|
end
|
61
|
-
# rubocop:enable Metrics/AbcSize
|
62
66
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
account: user_keypair,
|
72
|
-
sequence: user_account.next_sequence_value,
|
73
|
-
fee: target_address.nil? ? FEE : FEE * 2
|
74
|
-
).tap do |t|
|
75
|
-
t.operations << payment_op(amount.to_f)
|
76
|
-
t.operations << third_party_payment_op(target_address, amount) if target_address
|
67
|
+
# Sends money from application account to user's account or target_address, if given
|
68
|
+
# @param amount [Float] Payment amount.
|
69
|
+
# @param target_address [String] Optional: third party receiver address.
|
70
|
+
def payout(amount, target_address: user_keypair.address)
|
71
|
+
amount = cast_amount(amount)
|
72
|
+
raise Mobius::Client::Error::InsufficientFunds if app_balance < amount
|
73
|
+
submit_tx do |operations|
|
74
|
+
operations << payment_op(amount, dest: target_address, src: app_keypair)
|
77
75
|
end
|
76
|
+
rescue Faraday::ClientError => err
|
77
|
+
handle(err)
|
78
78
|
end
|
79
79
|
|
80
|
-
|
81
|
-
Stellar::Operation.payment(
|
82
|
-
destination: app_keypair,
|
83
|
-
amount: Stellar::Amount.new(amount.to_f, Mobius::Client.stellar_asset).to_payment
|
84
|
-
)
|
85
|
-
end
|
80
|
+
private
|
86
81
|
|
87
|
-
def
|
88
|
-
|
89
|
-
source_account: app_keypair,
|
90
|
-
destination: Mobius::Client.to_keypair(target_address),
|
91
|
-
amount: Stellar::Amount.new(amount.to_f, Mobius::Client.stellar_asset).to_payment
|
92
|
-
)
|
93
|
-
end
|
82
|
+
def submit_tx
|
83
|
+
return unless block_given?
|
94
84
|
|
95
|
-
|
96
|
-
Stellar::Transaction.payment(
|
85
|
+
tx = Stellar::Transaction.for_account(
|
97
86
|
account: user_keypair,
|
98
87
|
sequence: user_account.next_sequence_value,
|
99
|
-
destination: Mobius::Client.to_keypair(address),
|
100
|
-
amount: Stellar::Amount.new(amount.to_f, Mobius::Client.stellar_asset).to_payment
|
101
88
|
)
|
102
|
-
end
|
103
89
|
|
104
|
-
|
105
|
-
raise Mobius::Client::Error::AuthorisationMissing unless authorized?
|
106
|
-
raise Mobius::Client::Error::TrustlineMissing if balance_object.nil?
|
107
|
-
end
|
90
|
+
yield(tx.operations)
|
108
91
|
|
109
|
-
|
110
|
-
|
92
|
+
tx.fee = FEE * tx.operations.size
|
93
|
+
|
94
|
+
txe = tx.to_envelope(app_keypair).to_xdr(:base64)
|
95
|
+
post_txe(txe).tap { [app_account, user_account].each(&:reload!) }
|
111
96
|
end
|
112
97
|
|
113
|
-
def
|
114
|
-
|
98
|
+
def post_txe(txe)
|
99
|
+
Mobius::Client.horizon_client.horizon.transactions._post(tx: txe)
|
115
100
|
end
|
116
101
|
|
117
|
-
def
|
118
|
-
|
102
|
+
def payment_op(amount, dest:, src:)
|
103
|
+
Stellar::Operation.payment(
|
104
|
+
source_account: Mobius::Client.to_keypair(src),
|
105
|
+
destination: Mobius::Client.to_keypair(dest),
|
106
|
+
amount: Stellar::Amount.new(amount, Mobius::Client.stellar_asset).to_payment
|
107
|
+
)
|
119
108
|
end
|
120
109
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
end
|
110
|
+
def validate!
|
111
|
+
raise Mobius::Client::Error::AuthorisationMissing unless authorized?
|
112
|
+
raise Mobius::Client::Error::TrustlineMissing unless user_account.trustline_exists?
|
125
113
|
end
|
126
114
|
|
127
115
|
def app_keypair
|
@@ -147,5 +135,11 @@ class Mobius::Client::App
|
|
147
135
|
raise err
|
148
136
|
end
|
149
137
|
|
138
|
+
def cast_amount(amount)
|
139
|
+
Float(amount).to_d
|
140
|
+
rescue ArgumentError
|
141
|
+
raise Mobius::Client::Error::InvalidAmount, "Invalid amount provided: `#{amount}`"
|
142
|
+
end
|
143
|
+
|
150
144
|
FEE = 100
|
151
145
|
end
|
@@ -5,14 +5,14 @@ class Mobius::Client::Blockchain::Account
|
|
5
5
|
# @!method initialize(keypair)
|
6
6
|
# @param keypair [Stellar::Keypair] account keypair
|
7
7
|
# @!scope instance
|
8
|
-
param :keypair
|
8
|
+
param :keypair, Mobius::Client.method(:to_keypair)
|
9
9
|
|
10
10
|
# Returns true if trustline exists for given asset and limit is positive.
|
11
11
|
# @param asset [Stellar::Asset] Stellar asset to check or :native
|
12
12
|
# @return [Boolean] true if trustline exists
|
13
13
|
def trustline_exists?(asset = Mobius::Client.stellar_asset)
|
14
14
|
balance = find_balance(asset)
|
15
|
-
(balance && !balance.dig("limit").
|
15
|
+
(balance && !balance.dig("limit").to_d.zero?) || false
|
16
16
|
end
|
17
17
|
|
18
18
|
# Returns balance for given asset
|
@@ -20,7 +20,7 @@ class Mobius::Client::Blockchain::Account
|
|
20
20
|
# @return [Float] Balance value.
|
21
21
|
def balance(asset = Mobius::Client.stellar_asset)
|
22
22
|
balance = find_balance(asset)
|
23
|
-
balance && balance.dig("balance").
|
23
|
+
balance && balance.dig("balance").to_d
|
24
24
|
end
|
25
25
|
|
26
26
|
# Returns true if given keypair is added as cosigner to current account.
|
data/lib/mobius/client/error.rb
CHANGED
@@ -46,4 +46,8 @@ class Mobius::Client::Error < StandardError
|
|
46
46
|
# Raised if unknown or empty value has passed to KeyPairFactory
|
47
47
|
class UnknownKeyPairType < self
|
48
48
|
end
|
49
|
+
|
50
|
+
# Raised, when NaN provided as an amount for payment operation
|
51
|
+
class InvalidAmount < self
|
52
|
+
end
|
49
53
|
end
|
data/lib/mobius/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "bigdecimal"
|
2
|
+
require "bigdecimal/util"
|
1
3
|
require "constructor_shortcut"
|
2
4
|
require "dry-initializer"
|
3
5
|
require "stellar-sdk"
|
@@ -7,6 +9,11 @@ require "jwt"
|
|
7
9
|
|
8
10
|
require "mobius/client/version"
|
9
11
|
|
12
|
+
begin
|
13
|
+
require "pry-byebug"
|
14
|
+
rescue LoadError
|
15
|
+
end
|
16
|
+
|
10
17
|
module Mobius
|
11
18
|
module Cli
|
12
19
|
autoload :Base, "mobius/cli/base"
|
data/mobius-client.gemspec
CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
33
33
|
spec.add_development_dependency "rubocop", "~> 0.53"
|
34
34
|
spec.add_development_dependency "rubocop-rspec", "~> 1.23"
|
35
|
+
spec.add_development_dependency "pry-byebug"
|
35
36
|
spec.add_development_dependency "simplecov", ">= 0.16.1"
|
36
37
|
spec.add_development_dependency "simplecov-console", ">= 0.4.2"
|
37
38
|
spec.add_development_dependency "timecop", "~> 0.9", ">= 0.9.1"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mobius-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Viktor Sokolov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,6 +114,20 @@ dependencies:
|
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '1.23'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: pry-byebug
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
117
131
|
- !ruby/object:Gem::Dependency
|
118
132
|
name: simplecov
|
119
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -392,7 +406,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
392
406
|
version: '0'
|
393
407
|
requirements: []
|
394
408
|
rubyforge_project:
|
395
|
-
rubygems_version: 2.6
|
409
|
+
rubygems_version: 2.7.6
|
396
410
|
signing_key:
|
397
411
|
specification_version: 4
|
398
412
|
summary: Mobius Ruby Client
|