stellar_spectrum 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +4 -0
  4. data/Gemfile.lock +10 -7
  5. data/README.md +8 -0
  6. data/lib/stellar_spectrum/client.rb +20 -130
  7. data/lib/stellar_spectrum/services/get_available_channels.rb +9 -4
  8. data/lib/stellar_spectrum/services/get_channel_account_info.rb +23 -0
  9. data/lib/stellar_spectrum/services/get_channel_accounts.rb +15 -0
  10. data/lib/stellar_spectrum/services/get_locked_accounts.rb +9 -2
  11. data/lib/stellar_spectrum/services/get_sequence_number.rb +17 -0
  12. data/lib/stellar_spectrum/services/increment_tries.rb +13 -0
  13. data/lib/stellar_spectrum/services/init_redis.rb +6 -2
  14. data/lib/stellar_spectrum/services/init_stellar_client.rb +13 -0
  15. data/lib/stellar_spectrum/services/lock_channel.rb +25 -0
  16. data/lib/stellar_spectrum/services/pick_channel.rb +13 -0
  17. data/lib/stellar_spectrum/services/send_payment.rb +64 -0
  18. data/lib/stellar_spectrum/services/sending_payment/attempt_release_lock.rb +18 -0
  19. data/lib/stellar_spectrum/services/sending_payment/retry.rb +45 -0
  20. data/lib/stellar_spectrum/services/sending_payment/send_asset.rb +57 -0
  21. data/lib/stellar_spectrum/services/unlocking/attempt_release.rb +37 -0
  22. data/lib/stellar_spectrum/services/unlocking/check_sequence_number.rb +25 -0
  23. data/lib/stellar_spectrum/services/unlocking/get_account_to_unlock.rb +23 -0
  24. data/lib/stellar_spectrum/services/unlocking/unlock.rb +16 -0
  25. data/lib/stellar_spectrum/version.rb +1 -1
  26. data/lib/stellar_spectrum.rb +16 -0
  27. data/stellar_spectrum.gemspec +1 -0
  28. metadata +33 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0973e01fef131fdb5026e04c8ca49939c230a54d2086bd3838b84fe7bdb2e693'
4
- data.tar.gz: c5f9da4f3eb02163675fb64745b4b1dfa52a7500d4f9c0e0352cccf7bbfd60a1
3
+ metadata.gz: 82171102575548c02b4566a049a7959544daea5bb1e4509df7a5ce1e78dd1a2e
4
+ data.tar.gz: 88a64d56566dacc96206c78be2fb0f4aa37ba36a0565009c9db048f55feaa3f8
5
5
  SHA512:
6
- metadata.gz: f896ac727003556d6116c8bcc7609dac568ab3d99d163dad267f67119184bff303b53c01da8556ca4eb5cd3622b48b5b24460922572d2067cd15de917ebcb45d
7
- data.tar.gz: d93ee85a47ec55cf62ea627a67af50ee25a7ca4592939046c6996e7e8e1e2cd5a895b80e1a0c87142944a4ce9714f992a098570188d01433384c06950ec3c46e
6
+ metadata.gz: 548ca98342523d4b589461d1a8f5ea210778a0d97acc6e0844b83c38d9ca36c9d250ef04e73b9dd5fa906ee063f5f103ac409f4f7dbfaeb1fa496e4e80260e38
7
+ data.tar.gz: 398063e01bf565768e527af8eba2dce1dae26d0c5f914363a76de8a7a3bde7398d1581315e032647ea33f991610cbee71892ffe3104d3e261776b26fed4ced46
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.5.3
data/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.1.0] - 2018-12-12
8
+ ### Added
9
+ - Retry when timeout is encountered [#6](https://github.com/bloom-solutions/stellar_spectrum-ruby/pull/6)
10
+
7
11
  ## [1.0.0] - 2018-11-30
8
12
  ### Changed
9
13
  - Use stellar-sdk => 0.6.0
data/Gemfile.lock CHANGED
@@ -1,18 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stellar_spectrum (1.0.0)
4
+ stellar_spectrum (1.1.0)
5
5
  activesupport
6
6
  gem_config
7
+ light-service
7
8
  redis
8
9
  stellar-sdk (>= 0.6.0)
9
10
 
10
11
  GEM
11
12
  remote: https://rubygems.org/
12
13
  specs:
13
- activemodel (5.2.1.1)
14
- activesupport (= 5.2.1.1)
15
- activesupport (5.2.1.1)
14
+ activemodel (5.2.2)
15
+ activesupport (= 5.2.2)
16
+ activesupport (5.2.2)
16
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
17
18
  i18n (>= 0.7, < 2)
18
19
  minitest (~> 5.1)
@@ -49,13 +50,15 @@ GEM
49
50
  faraday_middleware
50
51
  net-http-digest_auth
51
52
  uri_template
52
- i18n (1.1.1)
53
+ i18n (1.2.0)
53
54
  concurrent-ruby (~> 1.0)
54
- method_source (0.9.0)
55
+ light-service (0.11.0)
56
+ activesupport (>= 3.0)
57
+ method_source (0.9.2)
55
58
  minitest (5.11.3)
56
59
  multipart-post (2.0.0)
57
60
  net-http-digest_auth (1.4.1)
58
- pry (0.11.3)
61
+ pry (0.12.2)
59
62
  coderay (~> 1.1.0)
60
63
  method_source (~> 0.9.0)
61
64
  pry-byebug (3.6.0)
data/README.md CHANGED
@@ -61,6 +61,14 @@ When another transaction is created, an available (unlocked) payment channel wil
61
61
 
62
62
  If all payment channels are unavailable (locked), then the call will block the Ruby process/thread until a channel is available. Therefore, unless you have way more channels than you will ever consume, we strongly recommend you send transactions in a background worker like Sidekiq.
63
63
 
64
+ #### Retries
65
+
66
+ When the call to Horizon [times out](https://www.stellar.org/developers/horizon/reference/errors/timeout.html), we do not know whether or not the asset was sent.
67
+
68
+ Because there are many payment channels to choose from, and each channel has their own sequence number, it would be complicated to control it from outside the gem. Therefore, this gem will retry for you. When a timeout is encountered the same channel and the same sequence number will be used to retry, as suggested in the Stellar docs.
69
+
70
+ If the response is successful, the gem will mark the request as successful, and return the response.
71
+
64
72
  ## Development
65
73
 
66
74
  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.
@@ -21,143 +21,33 @@ module StellarSpectrum
21
21
  self.logger = logger
22
22
  end
23
23
 
24
- def send_payment(from:, to:, amount:, memo: nil, tries: 0)
25
- tries += 1
26
- available_channels = GetAvailableChannels.execute(
27
- redis: redis,
28
- locked_accounts: locked_accounts,
29
- channel_accounts: channel_accounts,
30
- )
31
-
32
- if available_channels.empty?
33
- log "No available channels, retry for the #{tries.ordinalize} time..."
34
- attempt_release_lock!
35
-
36
- return send_payment(
37
- from: from,
38
- to: to,
39
- amount: amount,
40
- memo: memo,
41
- tries: tries,
42
- )
43
- end
44
-
45
- log "Sending payment. #{available_channels.count} available channels: " \
46
- "#{available_channels.map(&:address).inspect}"
47
-
48
- channel_account = available_channels.first
49
-
50
- next_sequence_number = next_sequence_number_for(channel_account)
51
-
52
- successfully_locked = lock!(channel_account.address, {
53
- sequence_number: next_sequence_number,
54
- })
55
-
56
- if !successfully_locked
57
- log "Unable to lock #{channel_account.address}, " \
58
- "retry for the #{tries.ordinalize} time..."
59
-
60
- attempt_release_lock!
61
-
62
- return send_payment(
63
- from: from,
64
- to: to,
65
- amount: amount,
66
- memo: memo,
67
- tries: tries,
68
- )
69
- end
70
-
71
- stellar_client.send_payment(
24
+ def send_payment(
25
+ from:,
26
+ to:,
27
+ amount:,
28
+ memo: nil,
29
+ tries: 0,
30
+ transaction_source: nil,
31
+ sequence: nil
32
+ )
33
+ result = SendPayment.(
72
34
  from: from,
73
35
  to: to,
74
36
  amount: amount,
75
37
  memo: memo,
76
- transaction_source: channel_account,
77
- sequence: next_sequence_number,
38
+ seeds: seeds,
39
+ redis_url: redis_url,
40
+ horizon_url: horizon_url,
41
+ force_transaction_source: transaction_source,
42
+ force_sequence_number: sequence,
43
+ force_lock: false,
78
44
  )
79
- end
80
-
81
- def attempt_release_lock!(except_address: nil)
82
- if logger
83
- log_message = "Attempting to release a lock"
84
- if except_address.present?
85
- log_message << "(except for address #{except_address})"
86
- end
87
- log log_message
88
- end
89
-
90
- address, address_info = locked_accounts.except(except_address).
91
- sort_by {|address, info| info[:pttl]}.last
92
-
93
- return false if address.nil?
94
-
95
- current_sequence_number = current_sequence_number_for(address)
96
- address_info = locked_accounts[address]
97
-
98
- # Someone else may have unlocked it while we were taking our sweet
99
- # time fetching the sequence number
100
- return false if address_info.nil?
101
-
102
- address_sequence_number = address_info[:sequence_number].to_i
103
- if current_sequence_number < address_sequence_number
104
- log "Not releasing #{address}: sequence number locked at " \
105
- "#{address_sequence_number} is > current sequence number " \
106
- "#{current_sequence_number}"
107
- return false
108
- end
109
45
 
110
- log "Releasing #{address}: sequence number locked at " \
111
- "#{address_sequence_number} is <= current sequence number " \
112
- "#{current_sequence_number}"
113
- unlock!(address)
114
- end
115
-
116
- def channel_accounts
117
- @channel_accounts ||= seeds.map { |s| Stellar::Account.from_seed(s) }
118
- end
119
-
120
- def locked_accounts
121
- GetLockedAccounts.execute(redis: redis, channel_accounts: channel_accounts)
122
- end
46
+ # if result.failure?
47
+ # return false
48
+ # end
123
49
 
124
- def stellar_client
125
- @stellar_client ||= Stellar::Client.new(horizon: self.horizon_url)
50
+ result.send_asset_response
126
51
  end
127
-
128
- def redis
129
- @redis ||= InitRedis.execute(redis_url: self.redis_url)
130
- end
131
-
132
- def lock!(address, sequence_number:)
133
- redis.set(GetKeyForAddress.execute(address), sequence_number, {
134
- nx: true,
135
- ex: MAX_LOCK_TIME_IN_SECONDS,
136
- })
137
- end
138
-
139
- def unlock!(address)
140
- log "Unlocking address #{address}"
141
- redis.del(GetKeyForAddress.execute(address))
142
- end
143
-
144
- def next_sequence_number_for(address_or_account)
145
- current_sequence_number_for(address_or_account) + 1
146
- end
147
-
148
- def current_sequence_number_for(address_or_account)
149
- account = address_or_account
150
- if address_or_account.is_a?(String)
151
- account = Stellar::Account.from_address(address_or_account)
152
- end
153
-
154
- stellar_client.account_info(account).sequence.to_i
155
- end
156
-
157
- def log(message)
158
- return if logger.nil?
159
- logger.info [LOG_TAG, message].join(" ")
160
- end
161
-
162
52
  end
163
53
  end
@@ -1,11 +1,16 @@
1
1
  module StellarSpectrum
2
2
  class GetAvailableChannels
3
3
 
4
- def self.execute(redis:, locked_accounts:, channel_accounts:)
5
- locked_addresses = locked_accounts.keys
6
- channel_accounts.each_with_object([]) do |account, available_channels|
4
+ extend LightService::Action
5
+ expects :redis, :locked_accounts, :channel_accounts
6
+ promises :available_channels
7
+
8
+ executed do |c|
9
+ locked_addresses = c.locked_accounts.keys
10
+
11
+ c.available_channels = c.channel_accounts.each_with_object([]) do |account, channels|
7
12
  next if locked_addresses.include?(account.address)
8
- available_channels << account
13
+ channels << account
9
14
  end
10
15
  end
11
16
 
@@ -0,0 +1,23 @@
1
+ module StellarSpectrum
2
+ class GetChannelAccountInfo
3
+
4
+ extend LightService::Action
5
+ expects :locked_accounts, :channel_account
6
+ promises :channel_account_info, :unlock_response
7
+
8
+ executed do |c|
9
+ c.channel_account_info = c.locked_accounts[c.channel_account.address]
10
+
11
+ c.unlock_response = nil
12
+ next c if c.channel_account_info.present?
13
+
14
+ c.unlock_response = true
15
+
16
+ message = "#{c.channel_account.address} has been unlocked by some " \
17
+ "other mechanism like ttl expiration or by something else, " \
18
+ "so this is a success"
19
+ c.skip_remaining!(message)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module StellarSpectrum
2
+ class GetChannelAccounts
3
+
4
+ extend LightService::Action
5
+ expects :seeds
6
+ promises :channel_accounts
7
+
8
+ executed do |c|
9
+ c.channel_accounts = c.seeds.map do |seed|
10
+ Stellar::Account.from_seed(seed)
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -1,9 +1,16 @@
1
1
  module StellarSpectrum
2
2
  class GetLockedAccounts
3
3
 
4
+ extend LightService::Action
5
+ expects :redis, :channel_accounts
6
+ promises :locked_accounts
7
+
4
8
  REDIS_PTTL_KEY_NON_PRESENT = -2
5
9
 
6
- def self.execute(redis:, channel_accounts:)
10
+ executed do |c|
11
+ redis = c.redis
12
+ channel_accounts = c.channel_accounts
13
+
7
14
  addresses = channel_accounts.map(&:address)
8
15
  address_keys = addresses.map {|a| GetKeyForAddress.execute(a) }
9
16
  redis_response = redis.multi do
@@ -15,7 +22,7 @@ module StellarSpectrum
15
22
  address_sequence_numbers = redis_response[0]
16
23
  address_pttls = redis_response[1..-1]
17
24
 
18
- addresses.each_with_object({}).with_index do |(address, hash), i|
25
+ c.locked_accounts = addresses.each_with_object({}).with_index do |(address, hash), i|
19
26
  pttl = address_pttls[i]
20
27
 
21
28
  next if pttl == REDIS_PTTL_KEY_NON_PRESENT
@@ -0,0 +1,17 @@
1
+ module StellarSpectrum
2
+ class GetSequenceNumber
3
+
4
+ extend LightService::Action
5
+ expects :stellar_client, :channel_account
6
+ promises :current_sequence_number, :next_sequence_number
7
+
8
+ executed do |c|
9
+ account_info = c.stellar_client.account_info(c.channel_account)
10
+ c.current_sequence_number = account_info.sequence.to_i
11
+
12
+ c.next_sequence_number = c[:force_sequence_number] ||
13
+ c.current_sequence_number + 1
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module StellarSpectrum
2
+ class IncrementTries
3
+
4
+ extend LightService::Action
5
+ expects :tries
6
+ promises :tries
7
+
8
+ executed do |c|
9
+ c.tries += 1
10
+ end
11
+
12
+ end
13
+ end
@@ -1,8 +1,12 @@
1
1
  module StellarSpectrum
2
2
  class InitRedis
3
3
 
4
- def self.execute(redis_url:)
5
- Redis.new(redis_url: redis_url)
4
+ extend LightService::Action
5
+ expects :redis_url
6
+ promises :redis
7
+
8
+ executed do |c|
9
+ c.redis = Redis.new(redis_url: c.redis_url)
6
10
  end
7
11
 
8
12
  end
@@ -0,0 +1,13 @@
1
+ module StellarSpectrum
2
+ class InitStellarClient
3
+
4
+ extend LightService::Action
5
+ expects :horizon_url
6
+ promises :stellar_client
7
+
8
+ executed do |c|
9
+ c.stellar_client = Stellar::Client.new(horizon: c.horizon_url)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module StellarSpectrum
2
+ class LockChannel
3
+
4
+ extend LightService::Action
5
+ expects :channel_account, :next_sequence_number, :redis, :force_lock
6
+ promises :successfully_locked
7
+
8
+ SUCCESS_NON_NX_RESULT = "OK".freeze
9
+
10
+ executed do |c|
11
+ address = c.channel_account.address
12
+ address_key = GetKeyForAddress.execute(address)
13
+
14
+ c.successfully_locked = c.redis.set(address_key, c.next_sequence_number, {
15
+ nx: !c.force_lock,
16
+ ex: Client::MAX_LOCK_TIME_IN_SECONDS,
17
+ })
18
+
19
+ if c.successfully_locked == SUCCESS_NON_NX_RESULT
20
+ c.successfully_locked = true
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module StellarSpectrum
2
+ class PickChannel
3
+
4
+ extend LightService::Action
5
+ expects :available_channels
6
+ promises :channel_account
7
+
8
+ executed do |c|
9
+ c.channel_account = c.available_channels.first
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,64 @@
1
+ module StellarSpectrum
2
+ class SendPayment
3
+
4
+ extend LightService::Organizer
5
+
6
+ def self.call(
7
+ from:,
8
+ to:,
9
+ amount:,
10
+ memo: nil,
11
+ tries: 0,
12
+ seeds:,
13
+ redis_url:,
14
+ horizon_url:,
15
+ force_transaction_source: nil,
16
+ force_sequence_number: nil,
17
+ force_lock: false
18
+ )
19
+ with(
20
+ from: from,
21
+ to: to,
22
+ amount: amount,
23
+ memo: memo,
24
+ tries: tries,
25
+ seeds: seeds,
26
+ redis_url: redis_url,
27
+ horizon_url: horizon_url,
28
+ force_transaction_source: force_transaction_source,
29
+ force_sequence_number: force_sequence_number,
30
+ force_lock: force_lock,
31
+ ).reduce(actions)
32
+ end
33
+
34
+ def self.actions
35
+ [
36
+ IncrementTries,
37
+ InitStellarClient,
38
+ InitRedis,
39
+ GetChannelAccounts,
40
+ reduce_if(->(c) {c[:force_transaction_source].nil?}, [
41
+ GetLockedAccounts,
42
+ GetAvailableChannels,
43
+ reduce_if(->(c) { c.available_channels.empty? }, retry_actions),
44
+ PickChannel,
45
+ ]),
46
+ reduce_if(->(c) {c[:force_transaction_source].present?}, [
47
+ execute(->(c) {c[:channel_account] = c[:force_transaction_source]}),
48
+ ]),
49
+ GetSequenceNumber,
50
+ LockChannel,
51
+ reduce_if(->(c) {!c.successfully_locked}, retry_actions),
52
+ SendingPayment::SendAsset,
53
+ ]
54
+ end
55
+
56
+ def self.retry_actions
57
+ [
58
+ SendingPayment::AttemptReleaseLock,
59
+ SendingPayment::Retry,
60
+ ]
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,18 @@
1
+ module StellarSpectrum
2
+ module SendingPayment
3
+ class AttemptReleaseLock
4
+
5
+ extend LightService::Action
6
+ expects :stellar_client, :redis, :channel_accounts
7
+
8
+ executed do |c|
9
+ Unlocking::AttemptRelease.(
10
+ stellar_client: c.stellar_client,
11
+ redis: c.redis,
12
+ channel_accounts: c.channel_accounts,
13
+ )
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module StellarSpectrum
2
+ module SendingPayment
3
+ class Retry
4
+
5
+ extend LightService::Action
6
+ EXPECTS = %i[
7
+ from
8
+ to
9
+ amount
10
+ memo
11
+ tries
12
+ seeds
13
+ horizon_url
14
+ redis_url
15
+ force_transaction_source
16
+ force_sequence_number
17
+ force_lock
18
+ ]
19
+ expects *EXPECTS
20
+ promises :send_asset_response
21
+ WAIT_TIME_IN_SECONDS = 5
22
+
23
+ executed do |c|
24
+ sleep WAIT_TIME_IN_SECONDS
25
+
26
+ args = EXPECTS.each_with_object({}) do |attr, hash|
27
+ hash[attr] = c.send(attr)
28
+ end
29
+
30
+ c.send_asset_response = SendPayment.(args).send_asset_response
31
+
32
+ # THIS IS IMPORTANT or else it will continue with the rest of the
33
+ # actions and possibly send out the asset just as many times as `tries`
34
+ # https://github.com/adomokos/light-service/pull/164
35
+ # fail_and_return for now until we figure out a way to stop all actions
36
+ # from reduce_if
37
+ # See https://github.com/adomokos/light-service/pull/164
38
+ message = "Closing try #{c.tries}"
39
+ c.fail_and_return! message
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,57 @@
1
+ module StellarSpectrum
2
+ module SendingPayment
3
+ class SendAsset
4
+
5
+ extend LightService::Action
6
+ TIMEOUT_CODE = 504.freeze
7
+
8
+ expects *%i[
9
+ from
10
+ to
11
+ amount
12
+ memo
13
+ channel_account
14
+ next_sequence_number
15
+ stellar_client
16
+ tries
17
+ seeds
18
+ horizon_url
19
+ redis_url
20
+ ]
21
+
22
+ promises :send_asset_response
23
+
24
+ executed do |c|
25
+ c.send_asset_response = c.stellar_client.send_payment(
26
+ from: c.from,
27
+ to: c.to,
28
+ amount: c.amount,
29
+ memo: c.memo,
30
+ transaction_source: c.channel_account,
31
+ sequence: c.next_sequence_number,
32
+ )
33
+ rescue Faraday::ClientError => e
34
+ if e.response[:status] == TIMEOUT_CODE
35
+ retry_result = Retry.execute(
36
+ stellar_client: c.stellar_client,
37
+ from: c.from,
38
+ to: c.to,
39
+ amount: c.amount,
40
+ memo: c.memo,
41
+ tries: c.tries,
42
+ seeds: c.seeds,
43
+ horizon_url: c.horizon_url,
44
+ redis_url: c.redis_url,
45
+ force_transaction_source: c.channel_account,
46
+ force_sequence_number: c.next_sequence_number,
47
+ force_lock: true,
48
+ )
49
+ c.send_asset_response = retry_result[:send_asset_response]
50
+ else
51
+ fail
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,37 @@
1
+ module StellarSpectrum
2
+ module Unlocking
3
+ class AttemptRelease
4
+
5
+ extend LightService::Organizer
6
+ def self.call(redis:, channel_accounts:, stellar_client:)
7
+ result = with(
8
+ stellar_client: stellar_client,
9
+ redis: redis,
10
+ channel_accounts: channel_accounts,
11
+ ).reduce(actions)
12
+
13
+ if result.failure?
14
+ result[:unlock_response] = false
15
+ end
16
+
17
+ result
18
+ end
19
+
20
+ def self.actions
21
+ [
22
+ GetLockedAccounts,
23
+ GetAccountToUnlock,
24
+ GetSequenceNumber,
25
+ # Someone else may have unlocked it while we were taking
26
+ # our sweet time fetching the sequence number, so fetch
27
+ # the locked accounts again:
28
+ GetLockedAccounts,
29
+ GetChannelAccountInfo,
30
+ CheckSequenceNumber,
31
+ Unlock,
32
+ ]
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ module StellarSpectrum
2
+ module Unlocking
3
+ class CheckSequenceNumber
4
+
5
+ extend LightService::Action
6
+ expects :current_sequence_number, :channel_account, :channel_account_info
7
+
8
+ executed do |c|
9
+ address_sequence_number = c.channel_account_info[:sequence_number].to_i
10
+ current_sequence_number = c.current_sequence_number
11
+
12
+ next c if current_sequence_number >= address_sequence_number
13
+
14
+ address = c.channel_account.address
15
+
16
+ message = "Not releasing #{address}: sequence number locked at " \
17
+ "#{address_sequence_number} is > current sequence number " \
18
+ "#{current_sequence_number}"
19
+
20
+ c.fail_and_return! message
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module StellarSpectrum
2
+ module Unlocking
3
+ class GetAccountToUnlock
4
+
5
+ extend LightService::Action
6
+ expects :locked_accounts
7
+ promises :channel_account
8
+
9
+ executed do |c|
10
+ address, info = c.locked_accounts.except(c[:except_address])
11
+ .sort_by {|address, info| info[:pttl]}.last
12
+
13
+ if address.present?
14
+ c.channel_account = Stellar::Account.from_address(address)
15
+ else
16
+ c.channel_account = nil
17
+ c.fail_and_return! "No addresses to unlock"
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module StellarSpectrum
2
+ module Unlocking
3
+ class Unlock
4
+
5
+ extend LightService::Action
6
+ expects :redis, :channel_account
7
+ promises :unlock_response
8
+
9
+ executed do |c|
10
+ address_key = GetKeyForAddress.execute(c.channel_account.address)
11
+ c.unlock_response = c.redis.del(address_key)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module StellarSpectrum
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require "gem_config"
2
+ require "light-service"
2
3
  require "stellar_spectrum/version"
3
4
  require "stellar_spectrum/client"
4
5
  require "stellar-sdk"
@@ -6,10 +7,25 @@ require "redis"
6
7
  require "active_support/core_ext/hash/except"
7
8
  require "active_support/core_ext/object/blank"
8
9
  require 'active_support/core_ext/integer/inflections'
10
+ require "stellar_spectrum/services/get_channel_account_info"
9
11
  require "stellar_spectrum/services/get_available_channels"
12
+ require "stellar_spectrum/services/get_channel_accounts"
10
13
  require "stellar_spectrum/services/get_key_for_address"
11
14
  require "stellar_spectrum/services/get_locked_accounts"
15
+ require "stellar_spectrum/services/get_sequence_number"
16
+ require "stellar_spectrum/services/increment_tries"
12
17
  require "stellar_spectrum/services/init_redis"
18
+ require "stellar_spectrum/services/init_stellar_client"
19
+ require "stellar_spectrum/services/lock_channel"
20
+ require "stellar_spectrum/services/pick_channel"
21
+ require "stellar_spectrum/services/send_payment"
22
+ require "stellar_spectrum/services/sending_payment/attempt_release_lock"
23
+ require "stellar_spectrum/services/sending_payment/retry"
24
+ require "stellar_spectrum/services/sending_payment/send_asset"
25
+ require "stellar_spectrum/services/unlocking/attempt_release"
26
+ require "stellar_spectrum/services/unlocking/check_sequence_number"
27
+ require "stellar_spectrum/services/unlocking/get_account_to_unlock"
28
+ require "stellar_spectrum/services/unlocking/unlock"
13
29
 
14
30
  module StellarSpectrum
15
31
 
@@ -39,6 +39,7 @@ Gem::Specification.new do |spec|
39
39
  spec.add_dependency "gem_config"
40
40
  spec.add_dependency "redis"
41
41
  spec.add_dependency "stellar-sdk", ">= 0.6.0"
42
+ spec.add_dependency "light-service"
42
43
 
43
44
  spec.add_development_dependency "bundler", "~> 1.16"
44
45
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stellar_spectrum
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ramon Tayag
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-11-30 00:00:00.000000000 Z
12
+ date: 2018-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -67,6 +67,20 @@ dependencies:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: 0.6.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: light-service
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
70
84
  - !ruby/object:Gem::Dependency
71
85
  name: bundler
72
86
  requirement: !ruby/object:Gem::Requirement
@@ -147,6 +161,7 @@ extra_rdoc_files: []
147
161
  files:
148
162
  - ".gitignore"
149
163
  - ".rspec"
164
+ - ".ruby-version"
150
165
  - ".travis.yml"
151
166
  - CHANGELOG.md
152
167
  - CODE_OF_CONDUCT.md
@@ -161,9 +176,24 @@ files:
161
176
  - lib/stellar_spectrum.rb
162
177
  - lib/stellar_spectrum/client.rb
163
178
  - lib/stellar_spectrum/services/get_available_channels.rb
179
+ - lib/stellar_spectrum/services/get_channel_account_info.rb
180
+ - lib/stellar_spectrum/services/get_channel_accounts.rb
164
181
  - lib/stellar_spectrum/services/get_key_for_address.rb
165
182
  - lib/stellar_spectrum/services/get_locked_accounts.rb
183
+ - lib/stellar_spectrum/services/get_sequence_number.rb
184
+ - lib/stellar_spectrum/services/increment_tries.rb
166
185
  - lib/stellar_spectrum/services/init_redis.rb
186
+ - lib/stellar_spectrum/services/init_stellar_client.rb
187
+ - lib/stellar_spectrum/services/lock_channel.rb
188
+ - lib/stellar_spectrum/services/pick_channel.rb
189
+ - lib/stellar_spectrum/services/send_payment.rb
190
+ - lib/stellar_spectrum/services/sending_payment/attempt_release_lock.rb
191
+ - lib/stellar_spectrum/services/sending_payment/retry.rb
192
+ - lib/stellar_spectrum/services/sending_payment/send_asset.rb
193
+ - lib/stellar_spectrum/services/unlocking/attempt_release.rb
194
+ - lib/stellar_spectrum/services/unlocking/check_sequence_number.rb
195
+ - lib/stellar_spectrum/services/unlocking/get_account_to_unlock.rb
196
+ - lib/stellar_spectrum/services/unlocking/unlock.rb
167
197
  - lib/stellar_spectrum/version.rb
168
198
  - stellar_spectrum.gemspec
169
199
  homepage: https://github.com/bloom-solutions/stellar_spectrum-ruby
@@ -190,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
220
  version: '0'
191
221
  requirements: []
192
222
  rubyforge_project:
193
- rubygems_version: 2.7.7
223
+ rubygems_version: 2.7.8
194
224
  signing_key:
195
225
  specification_version: 4
196
226
  summary: Use Stellar payment channels in Ruby with ease