mobius-client 0.1.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 +7 -0
- data/.gitignore +24 -0
- data/.rspec +3 -0
- data/.rubocop.yml +84 -0
- data/.travis.yml +22 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +259 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/auth/Gemfile +5 -0
- data/examples/auth/Gemfile.lock +106 -0
- data/examples/auth/auth.rb +33 -0
- data/examples/auth/public/app.js +35 -0
- data/examples/auth/views/index.slim +44 -0
- data/exe/mobius-cli +7 -0
- data/lib/mobius/cli/app.rb +15 -0
- data/lib/mobius/cli/auth.rb +95 -0
- data/lib/mobius/cli/base.rb +9 -0
- data/lib/mobius/cli/create.rb +85 -0
- data/lib/mobius/client.rb +117 -0
- data/lib/mobius/client/app.rb +136 -0
- data/lib/mobius/client/auth/challenge.rb +72 -0
- data/lib/mobius/client/auth/jwt.rb +37 -0
- data/lib/mobius/client/auth/sign.rb +48 -0
- data/lib/mobius/client/auth/token.rb +81 -0
- data/lib/mobius/client/blockchain/account.rb +88 -0
- data/lib/mobius/client/blockchain/add_cosigner.rb +58 -0
- data/lib/mobius/client/blockchain/create_trustline.rb +42 -0
- data/lib/mobius/client/blockchain/friend_bot.rb +16 -0
- data/lib/mobius/client/blockchain/key_pair_factory.rb +42 -0
- data/lib/mobius/client/error.rb +49 -0
- data/lib/mobius/client/friend_bot.rb +33 -0
- data/lib/mobius/client/version.rb +5 -0
- data/mobius-client.gemspec +51 -0
- data/template/dev-wallet.html.erb +109 -0
- metadata +385 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require "dry-initializer"
|
|
2
|
+
require "stellar-sdk"
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "faraday_middleware"
|
|
5
|
+
require "jwt"
|
|
6
|
+
|
|
7
|
+
require "mobius/client/version"
|
|
8
|
+
|
|
9
|
+
module Mobius
|
|
10
|
+
module Cli
|
|
11
|
+
autoload :Base, "mobius/cli/base"
|
|
12
|
+
autoload :App, "mobius/cli/app"
|
|
13
|
+
autoload :Auth, "mobius/cli/auth"
|
|
14
|
+
autoload :Create, "mobius/cli/create"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module Client
|
|
18
|
+
autoload :Error, "mobius/client/error"
|
|
19
|
+
autoload :FriendBot, "mobius/client/friend_bot"
|
|
20
|
+
autoload :App, "mobius/client/app"
|
|
21
|
+
|
|
22
|
+
module Auth
|
|
23
|
+
autoload :Challenge, "mobius/client/auth/challenge"
|
|
24
|
+
autoload :Jwt, "mobius/client/auth/jwt"
|
|
25
|
+
autoload :Sign, "mobius/client/auth/sign"
|
|
26
|
+
autoload :Token, "mobius/client/auth/token"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module Blockchain
|
|
30
|
+
autoload :Account, "mobius/client/blockchain/account"
|
|
31
|
+
autoload :AddCosigner, "mobius/client/blockchain/add_cosigner"
|
|
32
|
+
autoload :CreateTrustline, "mobius/client/blockchain/create_trustline"
|
|
33
|
+
autoload :FriendBot, "mobius/client/blockchain/friend_bot"
|
|
34
|
+
autoload :KeyPairFactory, "mobius/client/blockchain/key_pair_factory"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
attr_writer :mobius_host
|
|
39
|
+
|
|
40
|
+
# Mobius API host
|
|
41
|
+
def mobius_host
|
|
42
|
+
@mobius_host ||= "https://mobius.network"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def network=(value)
|
|
46
|
+
@network = value
|
|
47
|
+
Stellar.default_network = stellar_network
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Stellar network to use (:test || :public). See notes on thread-safety in ruby-stellar-base.
|
|
51
|
+
# Safe to set on startup.
|
|
52
|
+
def network
|
|
53
|
+
@network ||= :test
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# `Stellar::Client` instance
|
|
57
|
+
attr_writer :horizon_client
|
|
58
|
+
|
|
59
|
+
def horizon_client
|
|
60
|
+
@horizon_client ||= network == :test ? Stellar::Client.default_testnet : Stellar::Client.default
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Asset code used for payments (MOBI by default)
|
|
64
|
+
attr_writer :asset_code
|
|
65
|
+
|
|
66
|
+
def asset_code
|
|
67
|
+
@asset_code ||= "MOBI"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Asset issuer account
|
|
71
|
+
attr_writer :asset_issuer
|
|
72
|
+
|
|
73
|
+
def asset_issuer
|
|
74
|
+
return @asset_issuer if @asset_issuer
|
|
75
|
+
return "GA6HCMBLTZS5VYYBCATRBRZ3BZJMAFUDKYYF6AH6MVCMGWMRDNSWJPIH" if network == :public
|
|
76
|
+
"GDRWBLJURXUKM4RWDZDTPJNX6XBYFO3PSE4H4GPUL6H6RCUQVKTSD4AT"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Challenge expires in (seconds, 1d by default)
|
|
80
|
+
attr_writer :challenge_expires_in
|
|
81
|
+
|
|
82
|
+
def challenge_expires_in
|
|
83
|
+
@challenge_expires_in ||= 60 * 60 * 24
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Stellar::Asset instance of asset used for payments
|
|
87
|
+
def stellar_asset
|
|
88
|
+
@stellar_asset ||= Stellar::Asset.alphanum4(asset_code, Stellar::KeyPair.from_address(asset_issuer))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# In strict mode, session must be not older than seconds from now (10 by default)
|
|
92
|
+
attr_writer :strict_interval
|
|
93
|
+
|
|
94
|
+
def strict_interval
|
|
95
|
+
@strict_interval ||= 10
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Runs block on selected Stellar network
|
|
99
|
+
def on_network
|
|
100
|
+
Stellar.on_network(stellar_network) do
|
|
101
|
+
yield if block_given?
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Converts given argument to Stellar::KeyPair
|
|
106
|
+
def to_keypair(subject)
|
|
107
|
+
Mobius::Client::Blockchain::KeyPairFactory.produce(subject)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def stellar_network
|
|
113
|
+
Mobius::Client.network == :test ? Stellar::Networks::TESTNET : Stellar::Networks::PUBLIC
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Interface to user balance in application.
|
|
2
|
+
class Mobius::Client::App
|
|
3
|
+
extend Dry::Initializer
|
|
4
|
+
|
|
5
|
+
# @!method initialize(seed)
|
|
6
|
+
# @param seed [String] Developers private key.
|
|
7
|
+
# @param address [String] Users public key.
|
|
8
|
+
# @!scope instance
|
|
9
|
+
param :seed
|
|
10
|
+
param :address
|
|
11
|
+
|
|
12
|
+
# Checks if developer is authorized to use an application.
|
|
13
|
+
# @return [Bool] Authorization status.
|
|
14
|
+
def authorized?
|
|
15
|
+
user_account.authorized?(app_keypair)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Returns user balance.
|
|
19
|
+
# @return [Float] User balance.
|
|
20
|
+
def balance
|
|
21
|
+
validate!
|
|
22
|
+
balance_object["balance"].to_f
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns application balance.
|
|
26
|
+
# @return [Float] Application balance.
|
|
27
|
+
def app_balance
|
|
28
|
+
app_balance_object["balance"].to_f
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Makes payment.
|
|
32
|
+
# @param amount [Float] Payment amount.
|
|
33
|
+
# @param target_address [String] Optional: third party receiver address.
|
|
34
|
+
def pay(amount, target_address: nil)
|
|
35
|
+
current_balance = balance
|
|
36
|
+
raise Mobius::Client::Error::InsufficientFunds if current_balance < amount.to_f
|
|
37
|
+
envelope_base64 = payment_tx(amount.to_f, target_address).to_envelope(app_keypair).to_xdr(:base64)
|
|
38
|
+
post_tx(envelope_base64).tap do
|
|
39
|
+
[app_account, user_account].each(&:reload!)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Sends money from application account to third party.
|
|
44
|
+
# @param amount [Float] Payment amount.
|
|
45
|
+
# @param address [String] Target address.
|
|
46
|
+
def transfer(amount, address)
|
|
47
|
+
current_balance = app_balance
|
|
48
|
+
raise Mobius::Client::Error::InsufficientFunds if current_balance < amount.to_f
|
|
49
|
+
envelope_base64 = transfer_tx(amount.to_f, address).to_envelope(app_keypair).to_xdr(:base64)
|
|
50
|
+
post_tx(envelope_base64).tap do
|
|
51
|
+
[app_account, user_account].each(&:reload!)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def post_tx(txe)
|
|
58
|
+
Mobius::Client.horizon_client.horizon.transactions._post(tx: txe)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def payment_tx(amount, target_address)
|
|
62
|
+
Stellar::Transaction.for_account(
|
|
63
|
+
account: user_keypair,
|
|
64
|
+
sequence: user_account.next_sequence_value,
|
|
65
|
+
fee: target_address.nil? ? FEE : FEE * 2
|
|
66
|
+
).tap do |t|
|
|
67
|
+
t.operations << payment_op(amount.to_f)
|
|
68
|
+
t.operations << third_party_payment_op(target_address, amount) if target_address
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def payment_op(amount)
|
|
73
|
+
Stellar::Operation.payment(
|
|
74
|
+
destination: app_keypair,
|
|
75
|
+
amount: Stellar::Amount.new(amount.to_f, Mobius::Client.stellar_asset).to_payment
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def third_party_payment_op(target_address, amount)
|
|
80
|
+
Stellar::Operation.payment(
|
|
81
|
+
source_account: app_keypair,
|
|
82
|
+
destination: Mobius::Client.to_keypair(target_address),
|
|
83
|
+
amount: Stellar::Amount.new(amount.to_f, Mobius::Client.stellar_asset).to_payment
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def transfer_tx(amount, address)
|
|
88
|
+
Stellar::Transaction.payment(
|
|
89
|
+
account: user_keypair,
|
|
90
|
+
sequence: user_account.next_sequence_value,
|
|
91
|
+
destination: Mobius::Client.to_keypair(address),
|
|
92
|
+
amount: Stellar::Amount.new(amount.to_f, Mobius::Client.stellar_asset).to_payment
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def validate!
|
|
97
|
+
raise Mobius::Client::Error::AuthorisationMissing unless authorized?
|
|
98
|
+
raise Mobius::Client::Error::TrustlineMissing if balance_object.nil?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def limit
|
|
102
|
+
balance_object["limit"].to_f
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def balance_object
|
|
106
|
+
find_balance(user_account.info.balances)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def app_balance_object
|
|
110
|
+
find_balance(app_account.info.balances)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def find_balance(balances)
|
|
114
|
+
balances.find do |s|
|
|
115
|
+
s["asset_code"] == Mobius::Client.asset_code && s["asset_issuer"] == Mobius::Client.asset_issuer
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def app_keypair
|
|
120
|
+
@app_keypair ||= Mobius::Client.to_keypair(seed)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def user_keypair
|
|
124
|
+
@user_keypair ||= Mobius::Client.to_keypair(address)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def app_account
|
|
128
|
+
@app_account ||= Mobius::Client::Blockchain::Account.new(app_keypair)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def user_account
|
|
132
|
+
@user_account ||= Mobius::Client::Blockchain::Account.new(user_keypair)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
FEE = 100
|
|
136
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Generates challenge transaction on developer's side.
|
|
2
|
+
class Mobius::Client::Auth::Challenge
|
|
3
|
+
extend Dry::Initializer
|
|
4
|
+
|
|
5
|
+
# @!method initialize(seed)
|
|
6
|
+
# @param seed [String] Developers private key
|
|
7
|
+
# @!scope instance
|
|
8
|
+
param :seed
|
|
9
|
+
|
|
10
|
+
# Generates challenge transaction signed by developers private key. Minimum valid time bound is set to current time.
|
|
11
|
+
# Maximum valid time bound is set to `expire_in` seconds from now.
|
|
12
|
+
#
|
|
13
|
+
# @param expire_in [Integer] Session expiration time (seconds from now). 0 means "never".
|
|
14
|
+
# @return [String] base64-encoded transaction envelope
|
|
15
|
+
def call(expire_in = Mobius::Client.challenge_expires_in)
|
|
16
|
+
payment = Stellar::Transaction.payment(
|
|
17
|
+
source_account: keypair,
|
|
18
|
+
account: Stellar::KeyPair.random,
|
|
19
|
+
destination: keypair,
|
|
20
|
+
sequence: random_sequence,
|
|
21
|
+
amount: micro_xlm,
|
|
22
|
+
memo: memo
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
payment.time_bounds = build_time_bounds(expire_in)
|
|
26
|
+
|
|
27
|
+
payment.to_envelope(keypair).to_xdr(:base64)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# Shortcut to challenge generation method.
|
|
32
|
+
#
|
|
33
|
+
# @param seed [String] Developers private key
|
|
34
|
+
# @return [String] base64-encoded transaction envelope
|
|
35
|
+
def call(*args)
|
|
36
|
+
new(*args).call
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# @return [Stellar::Keypair] Stellar::Keypair object for given seed.
|
|
43
|
+
def keypair
|
|
44
|
+
@keypair ||= Stellar::KeyPair.from_seed(seed)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Integer] Random sequence number
|
|
48
|
+
def random_sequence
|
|
49
|
+
MAX_SEQ_NUMBER - SecureRandom.random_number(RANDOM_LIMITS)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [Stellar::TimeBounds] Current time..expire time
|
|
53
|
+
def build_time_bounds(expire_in)
|
|
54
|
+
Stellar::TimeBounds.new(
|
|
55
|
+
min_time: Time.now.to_i,
|
|
56
|
+
max_time: Time.now.to_i + expire_in.to_i || 0
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @return [Stellar::Amount] 1 XLM
|
|
61
|
+
def micro_xlm
|
|
62
|
+
Stellar::Amount.new(1).to_payment
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [Stellar::Memo] Auth transaction memo
|
|
66
|
+
def memo
|
|
67
|
+
Stellar::Memo.new(:memo_text, "Mobius authentication")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
MAX_SEQ_NUMBER = (2**128 - 1).freeze # MAX sequence number
|
|
71
|
+
RANDOM_LIMITS = 65535 # Sequence random limits
|
|
72
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Generates JWT token based on valid token transaction signed by both parties and decodes JWT token into hash.
|
|
2
|
+
class Mobius::Client::Auth::Jwt
|
|
3
|
+
extend Dry::Initializer
|
|
4
|
+
|
|
5
|
+
# @!method initialize(secret)
|
|
6
|
+
# @param secret [String] JWT secret
|
|
7
|
+
# @!scope instance
|
|
8
|
+
param :secret
|
|
9
|
+
|
|
10
|
+
# Returns JWT token.
|
|
11
|
+
# @param token [Mobius::Client::Auth::Token] Valid auth token
|
|
12
|
+
# @return [String] JWT token
|
|
13
|
+
def encode(token)
|
|
14
|
+
payload = {
|
|
15
|
+
hash: token.hash(:hex),
|
|
16
|
+
public_key: token.address,
|
|
17
|
+
min_time: token.time_bounds.min_time,
|
|
18
|
+
max_time: token.time_bounds.max_time
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
JWT.encode(payload, secret, ALG)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns decoded JWT token.
|
|
25
|
+
# @param jwt [String] JWT token
|
|
26
|
+
# @return [Hash] Decoded token params
|
|
27
|
+
def decode!(jwt)
|
|
28
|
+
OpenStruct.new(
|
|
29
|
+
JWT.decode(jwt, secret, true, algorithm: ALG).first
|
|
30
|
+
).tap do |payload|
|
|
31
|
+
raise TokenExpired unless (payload.min_time..payload.max_time).cover?(Time.now.to_i)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Used JWT algorithm
|
|
36
|
+
ALG = "HS512".freeze
|
|
37
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Signs challenge transaction on user's side.
|
|
2
|
+
class Mobius::Client::Auth::Sign
|
|
3
|
+
extend Dry::Initializer
|
|
4
|
+
|
|
5
|
+
# @!method initialize(seed, xdr)
|
|
6
|
+
# @param seed [String] Users private key
|
|
7
|
+
# @param xdr [String] Challenge transaction xdr
|
|
8
|
+
# @param address [String] Developers public key
|
|
9
|
+
# @!scope instance
|
|
10
|
+
param :seed
|
|
11
|
+
param :xdr
|
|
12
|
+
param :address
|
|
13
|
+
|
|
14
|
+
# Adds signature to given transaction.
|
|
15
|
+
#
|
|
16
|
+
# @return [String] base64-encoded transaction envelope
|
|
17
|
+
def call
|
|
18
|
+
validate!
|
|
19
|
+
envelope.dup.tap { |e| e.signatures << e.tx.sign_decorated(keypair) }.to_xdr(:base64)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def call(*args)
|
|
24
|
+
new(*args).call
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# @return [Stellar::Keypair] Stellar::Keypair object for given seed.
|
|
31
|
+
def keypair
|
|
32
|
+
@keypair ||= Stellar::KeyPair.from_seed(seed)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Stellar::Keypair] Stellar::Keypair object for given address.
|
|
36
|
+
def developer_keypair
|
|
37
|
+
@developer_keypair ||= Stellar::KeyPair.from_address(address)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Stellar::TransactionEnvelope] Stellar::TransactionEnvelope for given challenge.
|
|
41
|
+
def envelope
|
|
42
|
+
@envelope ||= Stellar::TransactionEnvelope.from_xdr(xdr, "base64")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def validate!
|
|
46
|
+
raise Mobius::Client::Error::Unauthorized unless envelope.signed_correctly?(developer_keypair)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Checks challenge transaction signed by user on developer's side.
|
|
2
|
+
class Mobius::Client::Auth::Token
|
|
3
|
+
extend Dry::Initializer
|
|
4
|
+
|
|
5
|
+
# @!method initialize(seed, xdr, address)
|
|
6
|
+
# @param seed [String] Developers private key.
|
|
7
|
+
# @param xdr [String] Auth transaction XDR.
|
|
8
|
+
# @param address [String] User public key.
|
|
9
|
+
# @!scope instance
|
|
10
|
+
param :seed
|
|
11
|
+
param :xdr
|
|
12
|
+
param :address
|
|
13
|
+
|
|
14
|
+
# Returns time bounds for given transaction.
|
|
15
|
+
#
|
|
16
|
+
# @return [Stellar::TimeBounds] Time bounds for given transaction (`.min_time` and `.max_time`).
|
|
17
|
+
# @raise [Unauthorized] if one of the signatures is invalid.
|
|
18
|
+
# @raise [Invalid] if transaction is malformed or time bounds are missing.
|
|
19
|
+
def time_bounds
|
|
20
|
+
bounds = envelope.tx.time_bounds
|
|
21
|
+
|
|
22
|
+
raise Mobius::Client::Error::Unauthorized unless signed_correctly?
|
|
23
|
+
raise Mobius::Client::Error::MalformedTransaction if bounds.nil?
|
|
24
|
+
|
|
25
|
+
bounds
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Validates transaction signed by developer and user.
|
|
29
|
+
#
|
|
30
|
+
# @param strict [Bool] if true, checks that lower time limit is within Mobius::Client.strict_interval seconds from now
|
|
31
|
+
# @return [Boolean] true if transaction is valid, raises exception otherwise
|
|
32
|
+
# @raise [Unauthorized] if one of the signatures is invalid
|
|
33
|
+
# @raise [Invalid] if transaction is malformed or time bounds are missing
|
|
34
|
+
# @raise [Expired] if transaction is expired (current time outside it's time bounds)
|
|
35
|
+
def validate!(strict = true)
|
|
36
|
+
bounds = time_bounds
|
|
37
|
+
raise Mobius::Client::Error::TokenExpired unless time_now_covers?(bounds)
|
|
38
|
+
raise Mobius::Client::Error::TokenTooOld if strict && too_old?(bounds)
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [String] transaction hash
|
|
43
|
+
def hash(format = :binary)
|
|
44
|
+
validate! # Guard!
|
|
45
|
+
h = envelope.tx.hash
|
|
46
|
+
return h if format == :binary
|
|
47
|
+
h.unpack("H*").first
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# @return [Stellar::KeyPair] Stellar::KeyPair object for given seed
|
|
53
|
+
def keypair
|
|
54
|
+
@keypair ||= Stellar::KeyPair.from_seed(seed)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @return [Stellar::KeyPair] Stellar::KeyPair of user being authorized
|
|
58
|
+
def their_keypair
|
|
59
|
+
@their_keypair ||= Stellar::KeyPair.from_address(address)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Stellar::TrnansactionEnvelope] Stellar::TrnansactionEnvelope of challenge transaction
|
|
63
|
+
def envelope
|
|
64
|
+
@envelope ||= Stellar::TransactionEnvelope.from_xdr(xdr, "base64")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Bool] true if transaction is signed by both parties
|
|
68
|
+
def signed_correctly?
|
|
69
|
+
envelope.signed_correctly?(keypair, their_keypair)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @return [Bool] true if current time is within transaction time bounds
|
|
73
|
+
def time_now_covers?(time_bounds)
|
|
74
|
+
(time_bounds.min_time..time_bounds.max_time).cover?(Time.now.to_i)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return [Bool] true if transaction is created more than n secods from now
|
|
78
|
+
def too_old?(time_bounds)
|
|
79
|
+
Time.now.to_i > time_bounds.min_time + Mobius::Client.strict_interval
|
|
80
|
+
end
|
|
81
|
+
end
|