sequence-sdk 1.0.3 → 1.0.4
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/README.md +11 -7
- data/lib/sequence.rb +0 -0
- data/lib/sequence/account.rb +54 -22
- data/lib/sequence/action.rb +158 -0
- data/lib/sequence/asset.rb +52 -22
- data/lib/sequence/balance.rb +20 -9
- data/lib/sequence/client.rb +19 -9
- data/lib/sequence/client_module.rb +4 -2
- data/lib/sequence/contract.rb +30 -15
- data/lib/sequence/dev_utils.rb +1 -1
- data/lib/sequence/errors.rb +13 -13
- data/lib/sequence/http_wrapper.rb +57 -23
- data/lib/sequence/key.rb +14 -7
- data/lib/sequence/page.rb +1 -0
- data/lib/sequence/query.rb +9 -6
- data/lib/sequence/response_object.rb +0 -0
- data/lib/sequence/session.rb +17 -26
- data/lib/sequence/stats.rb +1 -2
- data/lib/sequence/transaction.rb +192 -87
- data/lib/sequence/validations.rb +30 -0
- data/lib/sequence/version.rb +1 -1
- metadata +7 -5
data/lib/sequence/balance.rb
CHANGED
@@ -2,32 +2,43 @@ require_relative './client_module'
|
|
2
2
|
require_relative './response_object'
|
3
3
|
require_relative './query'
|
4
4
|
|
5
|
-
|
6
5
|
module Sequence
|
7
6
|
# A summation of contract amounts. Contracts are selected using a filter, and
|
8
7
|
# their values are summed using the common values of one or more contract
|
9
8
|
# fields.
|
10
9
|
class Balance < ResponseObject
|
11
|
-
|
12
10
|
# @!attribute [r] amount
|
13
|
-
#
|
11
|
+
# Summation of contract amounts.
|
14
12
|
# @return [Integer]
|
15
13
|
attrib :amount
|
16
14
|
|
17
15
|
# @!attribute [r] sum_by
|
18
|
-
#
|
16
|
+
# List of parameters along which contract amounts were summed.
|
19
17
|
# @return [Hash<String => String>]
|
20
18
|
attrib :sum_by
|
21
19
|
|
22
20
|
class ClientModule < Sequence::ClientModule
|
23
21
|
# Executes a query, returning an enumerable over individual balances.
|
24
|
-
# @param [Hash] opts
|
25
|
-
#
|
26
|
-
# @option opts [
|
27
|
-
#
|
28
|
-
# @option opts [Integer]
|
22
|
+
# @param [Hash] opts
|
23
|
+
# Options hash
|
24
|
+
# @option opts [String] filter
|
25
|
+
# A filter expression.
|
26
|
+
# @option opts [Array<String|Integer>] filter_params
|
27
|
+
# A list of values that will be interpolated into the filter expression.
|
28
|
+
# @option opts [Array<String>] sum_by
|
29
|
+
# A list of fields along which contract values will be summed.
|
30
|
+
# @option opts [Integer>] page_size
|
31
|
+
# The number of items to return in the result set.
|
29
32
|
# @return [Query]
|
30
33
|
def query(opts = {})
|
34
|
+
validate_inclusion_of!(
|
35
|
+
opts,
|
36
|
+
:filter,
|
37
|
+
:filter_params,
|
38
|
+
:sum_by,
|
39
|
+
:page_size,
|
40
|
+
:after,
|
41
|
+
)
|
31
42
|
Query.new(client, opts)
|
32
43
|
end
|
33
44
|
end
|
data/lib/sequence/client.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative './account'
|
2
|
+
require_relative './action'
|
2
3
|
require_relative './asset'
|
3
4
|
require_relative './balance'
|
4
5
|
require_relative './contract'
|
@@ -9,26 +10,30 @@ require_relative './transaction'
|
|
9
10
|
|
10
11
|
module Sequence
|
11
12
|
class Client
|
12
|
-
|
13
|
-
#
|
14
|
-
# @option opts [String]
|
15
|
-
#
|
13
|
+
# @param [Hash] opts
|
14
|
+
# Options hash
|
15
|
+
# @option opts [String] ledger
|
16
|
+
# Ledger name.
|
17
|
+
# @option opts [String] credential
|
18
|
+
# API credential secret.
|
16
19
|
# @return [Query]
|
17
20
|
def initialize(opts = {})
|
18
|
-
if
|
19
|
-
raise ArgumentError
|
21
|
+
if opts[:ledger].nil? || opts[:ledger].empty?
|
22
|
+
raise ArgumentError, ':ledger must be provided'
|
20
23
|
end
|
21
|
-
if
|
22
|
-
raise ArgumentError
|
24
|
+
if opts[:credential].nil? || opts[:credential].empty?
|
25
|
+
raise ArgumentError, ':credential must be provided'
|
23
26
|
end
|
24
27
|
|
25
28
|
@opts = opts
|
26
29
|
end
|
27
30
|
|
31
|
+
# @private
|
28
32
|
def opts
|
29
33
|
@opts.dup
|
30
34
|
end
|
31
35
|
|
36
|
+
# @private
|
32
37
|
# @return [Session]
|
33
38
|
def session
|
34
39
|
@session ||= Session.new(@opts)
|
@@ -44,6 +49,11 @@ module Sequence
|
|
44
49
|
@assets ||= Asset::ClientModule.new(self)
|
45
50
|
end
|
46
51
|
|
52
|
+
# @return [Action::ClientModule]
|
53
|
+
def actions
|
54
|
+
@actions ||= Action::ClientModule.new(self)
|
55
|
+
end
|
56
|
+
|
47
57
|
# @return [Balance::ClientModule]
|
48
58
|
def balances
|
49
59
|
@balances ||= Balance::ClientModule.new(self)
|
@@ -64,6 +74,7 @@ module Sequence
|
|
64
74
|
@contracts ||= Contract::ClientModule.new(self)
|
65
75
|
end
|
66
76
|
|
77
|
+
# @private
|
67
78
|
# @return [Stats::ClientModule]
|
68
79
|
def stats
|
69
80
|
@stats ||= Stats::ClientModule.new(self)
|
@@ -73,6 +84,5 @@ module Sequence
|
|
73
84
|
def dev_utils
|
74
85
|
@dev_utils ||= DevUtils::ClientModule.new(self)
|
75
86
|
end
|
76
|
-
|
77
87
|
end
|
78
88
|
end
|
@@ -1,13 +1,15 @@
|
|
1
|
+
require_relative './validations'
|
2
|
+
|
1
3
|
module Sequence
|
2
4
|
# Base class for ledger client components.
|
3
|
-
# @
|
5
|
+
# @private
|
4
6
|
class ClientModule
|
7
|
+
include Sequence::Validations
|
5
8
|
|
6
9
|
attr_reader :client
|
7
10
|
|
8
11
|
def initialize(client)
|
9
12
|
@client = client
|
10
13
|
end
|
11
|
-
|
12
14
|
end
|
13
15
|
end
|
data/lib/sequence/contract.rb
CHANGED
@@ -6,68 +6,83 @@ module Sequence
|
|
6
6
|
# An entry in the ledger that contains value that can be spent.
|
7
7
|
class Contract < ResponseObject
|
8
8
|
# @!attribute [r] id
|
9
|
-
#
|
9
|
+
# A unique ID.
|
10
10
|
# @return [String]
|
11
11
|
attrib :id
|
12
12
|
|
13
13
|
# @!attribute [r] type
|
14
|
-
#
|
14
|
+
# The type of the contract. Currently, this is always "account".
|
15
15
|
# @return [String]
|
16
16
|
attrib :type
|
17
17
|
|
18
18
|
# @!attribute [r] transaction_id
|
19
|
-
#
|
19
|
+
# The ID of the transaction in which the contract appears.
|
20
20
|
# @return [String]
|
21
21
|
attrib :transaction_id
|
22
22
|
|
23
23
|
# @!attribute [r] asset_id
|
24
|
-
#
|
24
|
+
# The ID of the asset held by the contract.
|
25
25
|
# @return [String]
|
26
26
|
attrib :asset_id
|
27
27
|
|
28
28
|
# @!attribute [r] asset_alias
|
29
|
-
#
|
29
|
+
# The alias of the asset held by the contract.
|
30
30
|
# @return [String]
|
31
31
|
attrib :asset_alias
|
32
32
|
|
33
33
|
# @!attribute [r] asset_tags
|
34
|
-
#
|
34
|
+
# The tags of the asset held by the contract.
|
35
35
|
# @return [Hash]
|
36
36
|
attrib :asset_tags
|
37
37
|
|
38
38
|
# @!attribute [r] amount
|
39
|
-
#
|
39
|
+
# The number of units of the asset held by the contract.
|
40
40
|
# @return [Integer]
|
41
41
|
attrib :amount
|
42
42
|
|
43
43
|
# @!attribute [r] account_id
|
44
|
-
#
|
44
|
+
# The ID of the account controlling the contract.
|
45
45
|
# @return [String]
|
46
46
|
attrib :account_id
|
47
47
|
|
48
48
|
# @!attribute [r] account_alias
|
49
|
-
#
|
49
|
+
# The alias of the account controlling the contract.
|
50
50
|
# @return [String]
|
51
51
|
attrib :account_alias
|
52
52
|
|
53
53
|
# @!attribute [r] account_tags
|
54
|
-
#
|
54
|
+
# The tags of the account controlling the contract.
|
55
55
|
# @return [Hash]
|
56
56
|
attrib :account_tags
|
57
57
|
|
58
58
|
# @!attribute [r] reference_data
|
59
|
-
#
|
59
|
+
# User-specified key-value data embedded in the contract.
|
60
60
|
# @return [Hash]
|
61
61
|
attrib :reference_data
|
62
62
|
|
63
63
|
class ClientModule < Sequence::ClientModule
|
64
64
|
# Executes a query, returning an enumerable over individual contracts.
|
65
|
-
# @param [Hash] opts
|
66
|
-
#
|
67
|
-
# @option opts [
|
68
|
-
#
|
65
|
+
# @param [Hash] opts
|
66
|
+
# Options hash
|
67
|
+
# @option opts [String] filter
|
68
|
+
# A filter expression.
|
69
|
+
# @option opts [Array<String|Integer>] filter_params
|
70
|
+
# A list of values that will be interpolated into the filter expression.
|
71
|
+
# @option opts [Integer] timestamp
|
72
|
+
# A millisecond Unix timestamp. Indicates that the query should be run
|
73
|
+
# over the state of the ledger at a given point in time.
|
74
|
+
# @option opts [Integer>] page_size
|
75
|
+
# The number of items to return in the result set.
|
69
76
|
# @return [Query]
|
70
77
|
def query(opts = {})
|
78
|
+
validate_inclusion_of!(
|
79
|
+
opts,
|
80
|
+
:filter,
|
81
|
+
:filter_params,
|
82
|
+
:timestamp,
|
83
|
+
:page_size,
|
84
|
+
:after,
|
85
|
+
)
|
71
86
|
Query.new(client, opts)
|
72
87
|
end
|
73
88
|
end
|
data/lib/sequence/dev_utils.rb
CHANGED
data/lib/sequence/errors.rb
CHANGED
@@ -34,23 +34,23 @@ module Sequence
|
|
34
34
|
# an error code, a message, and an optional detail field that provides
|
35
35
|
# additional context for the error.
|
36
36
|
class APIError < BaseError
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
attr_accessor :code, :chain_message, :detail, :data, :temporary, :request_id, :response
|
37
|
+
attr_accessor(
|
38
|
+
:chain_message,
|
39
|
+
:code,
|
40
|
+
:data,
|
41
|
+
:detail,
|
42
|
+
:request_id,
|
43
|
+
:response,
|
44
|
+
:retriable,
|
45
|
+
:temporary,
|
46
|
+
)
|
48
47
|
|
49
48
|
def initialize(body, response)
|
50
49
|
self.code = body['code']
|
51
50
|
self.chain_message = body['message']
|
52
51
|
self.detail = body['detail']
|
53
|
-
self.
|
52
|
+
self.retriable = body['retriable']
|
53
|
+
self.temporary = body['retriable']
|
54
54
|
|
55
55
|
self.response = response
|
56
56
|
self.request_id = response['Chain-Request-ID'] if response
|
@@ -59,7 +59,7 @@ module Sequence
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def retriable?
|
62
|
-
|
62
|
+
self.retriable
|
63
63
|
end
|
64
64
|
|
65
65
|
def self.format_error_message(code, message, detail, request_id)
|
@@ -2,14 +2,15 @@ require 'net/http'
|
|
2
2
|
require 'net/https'
|
3
3
|
require 'openssl'
|
4
4
|
require 'thread'
|
5
|
+
require 'securerandom'
|
5
6
|
|
6
7
|
module Sequence
|
8
|
+
# @private
|
7
9
|
class HttpWrapper
|
8
|
-
|
9
10
|
# Parameters to the retry exponential backoff function.
|
10
|
-
MAX_RETRIES = 10
|
11
11
|
RETRY_BASE_DELAY_MS = 40
|
12
|
-
RETRY_MAX_DELAY_MS =
|
12
|
+
RETRY_MAX_DELAY_MS = 20_000
|
13
|
+
RETRY_TIMEOUT_SECS = 120 # 2 minutes
|
13
14
|
|
14
15
|
NETWORK_ERRORS = [
|
15
16
|
InvalidRequestIDError,
|
@@ -22,46 +23,76 @@ module Sequence
|
|
22
23
|
Errno::ETIMEDOUT,
|
23
24
|
Errno::EHOSTUNREACH,
|
24
25
|
Errno::ECONNREFUSED,
|
25
|
-
]
|
26
|
+
].freeze
|
26
27
|
|
27
28
|
attr_accessor :dis_macaroon
|
28
29
|
|
29
|
-
def initialize(host, macaroon, opts={})
|
30
|
+
def initialize(host, macaroon, opts = {})
|
30
31
|
@mutex = Mutex.new
|
31
32
|
@host = URI(host)
|
32
33
|
@macaroon = macaroon
|
33
34
|
@dis_macaroon = nil
|
34
35
|
@opts = opts
|
35
|
-
@
|
36
|
+
@connection = setup_connection
|
36
37
|
end
|
37
38
|
|
38
|
-
def post(url, body)
|
39
|
+
def post(id, url, body)
|
40
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
39
41
|
attempts = 0
|
42
|
+
idempotency_key = SecureRandom.uuid
|
40
43
|
begin
|
41
44
|
attempts += 1
|
42
45
|
# If this is a retry and not the first attempt, sleep before making the
|
43
46
|
# retry request.
|
44
47
|
sleep(backoff_delay(attempts)) if attempts > 1
|
45
48
|
|
49
|
+
attempt_id = "#{id}/#{attempts}"
|
46
50
|
@mutex.synchronize do
|
47
51
|
req = Net::HTTP::Post.new(url)
|
48
52
|
req.body = JSON.dump(body)
|
49
53
|
req['Accept'] = 'application/json'
|
50
54
|
req['Content-Type'] = 'application/json'
|
55
|
+
req['Id'] = attempt_id
|
56
|
+
req['Idempotency-Key'] = idempotency_key
|
51
57
|
req['User-Agent'] = 'chain-sdk-ruby/' + Sequence::VERSION
|
52
|
-
if
|
58
|
+
if !@macaroon.nil? && !@dis_macaroon.nil?
|
53
59
|
req['Macaroon'] = @macaroon
|
54
60
|
req['Discharge-Macaroon'] = @dis_macaroon
|
55
61
|
end
|
56
|
-
|
62
|
+
unless @connection.started?
|
63
|
+
@connection.start
|
64
|
+
end
|
65
|
+
response = @connection.request(req)
|
66
|
+
|
67
|
+
if block_given?
|
68
|
+
yield response
|
69
|
+
end
|
70
|
+
|
71
|
+
# We must parse any APIErrors here so that
|
72
|
+
# the retry logic can handle them.
|
73
|
+
status = Integer(response.code)
|
74
|
+
parsed_body = nil
|
75
|
+
if status != 204 # No Content
|
76
|
+
begin
|
77
|
+
parsed_body = JSON.parse(response.body)
|
78
|
+
rescue JSON::JSONError
|
79
|
+
raise JSONError.new(attempt_id, response)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
if status / 100 != 2
|
83
|
+
klass = status == 401 ? UnauthorizedError : APIError
|
84
|
+
raise klass.new(parsed_body, response)
|
85
|
+
end
|
86
|
+
|
87
|
+
{ parsed_body: parsed_body, response: response }
|
57
88
|
end
|
58
89
|
rescue *NETWORK_ERRORS => e
|
59
|
-
raise e if
|
90
|
+
raise e if elapsed_secs(start_time) > RETRY_TIMEOUT_SECS
|
60
91
|
retry
|
61
92
|
rescue APIError => e
|
62
|
-
raise e
|
63
|
-
|
64
|
-
|
93
|
+
raise e unless e.retriable?
|
94
|
+
raise e if elapsed_secs(start_time) > RETRY_TIMEOUT_SECS
|
95
|
+
retry
|
65
96
|
end
|
66
97
|
end
|
67
98
|
|
@@ -69,14 +100,18 @@ module Sequence
|
|
69
100
|
|
70
101
|
MILLIS_TO_SEC = 0.001
|
71
102
|
|
103
|
+
def elapsed_secs(start_time)
|
104
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
105
|
+
end
|
106
|
+
|
72
107
|
def backoff_delay(attempt)
|
73
|
-
max = RETRY_BASE_DELAY_MS * 2**(attempt-1)
|
108
|
+
max = RETRY_BASE_DELAY_MS * 2**(attempt - 1)
|
74
109
|
max = [max, RETRY_MAX_DELAY_MS].min
|
75
110
|
millis = rand(max) + 1
|
76
111
|
millis * MILLIS_TO_SEC
|
77
112
|
end
|
78
113
|
|
79
|
-
def
|
114
|
+
def setup_connection
|
80
115
|
args = [@host.hostname, @host.port]
|
81
116
|
|
82
117
|
# Proxy configuration
|
@@ -87,25 +122,24 @@ module Sequence
|
|
87
122
|
end
|
88
123
|
end
|
89
124
|
|
90
|
-
|
91
|
-
|
125
|
+
connection = Net::HTTP.new(*args)
|
126
|
+
connection.set_debug_output($stdout) if ENV['DEBUG']
|
92
127
|
|
93
128
|
# TLS configuration
|
94
|
-
|
95
|
-
|
129
|
+
connection.use_ssl = true
|
130
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
96
131
|
[:ca_file, :cert, :key].each do |k|
|
97
132
|
next unless @opts.key?(:ssl_params) && @opts[:ssl_params].key?(k)
|
98
|
-
|
133
|
+
connection.send("#{k}=", @opts[:ssl_params][k])
|
99
134
|
end
|
100
135
|
|
101
136
|
# Timeout configuration
|
102
137
|
[:open_timeout, :read_timeout, :ssl_timeout].each do |k|
|
103
138
|
next unless @opts.key?(k)
|
104
|
-
|
139
|
+
connection.send "#{k}=", @opts[k]
|
105
140
|
end
|
106
141
|
|
107
|
-
|
142
|
+
connection
|
108
143
|
end
|
109
|
-
|
110
144
|
end
|
111
145
|
end
|