chain-sdk 1.0.0.pre

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.
@@ -0,0 +1,472 @@
1
+ require 'securerandom'
2
+ require 'time'
3
+
4
+ require_relative './client_module'
5
+ require_relative './query'
6
+ require_relative './response_object'
7
+
8
+ module Chain
9
+ class Transaction < ResponseObject
10
+
11
+ # @!attribute [r] id
12
+ # Unique transaction identifier.
13
+ # @return [String]
14
+ attrib :id
15
+
16
+ # @!attribute [r] timestamp
17
+ # Time of transaction.
18
+ # @return [Time]
19
+ attrib(:timestamp) { |raw| Time.parse(raw) }
20
+
21
+ # @!attribute [r] block_id
22
+ # Unique identifier, or block hash, of the block containing a transaction.
23
+ # @return [String]
24
+ attrib :block_id
25
+
26
+ # @!attribute [r] block_height
27
+ # Height of the block containing a transaction.
28
+ # @return [Integer]
29
+ attrib :block_height
30
+
31
+ # @!attribute [r] position
32
+ # Position of a transaction within the block.
33
+ # @return [Integer]
34
+ attrib :position
35
+
36
+ # @!attribute [r] reference_data
37
+ # User specified, unstructured data embedded within a transaction.
38
+ # @return [Hash]
39
+ attrib :reference_data
40
+
41
+ # @!attribute [r] is_local
42
+ # A flag indicating one or more inputs or outputs are local.
43
+ # @return [Boolean]
44
+ attrib :is_local
45
+
46
+ # @!attribute [r] inputs
47
+ # List of specified inputs for a transaction.
48
+ # @return [Array<Input>]
49
+ attrib(:inputs) { |raw| raw.map { |v| Input.new(v) } }
50
+
51
+ # @!attribute [r] outputs
52
+ # List of specified outputs for a transaction.
53
+ # @return [Array<Output>]
54
+ attrib(:outputs) { |raw| raw.map { |v| Output.new(v) } }
55
+
56
+ class ClientModule < Chain::ClientModule
57
+ # @param [Builder] builder
58
+ # @yield Block defining transaction actions.
59
+ # @return [Template]
60
+ def build(builder = nil, &block)
61
+ if builder.nil?
62
+ builder = Builder.new(&block)
63
+ end
64
+
65
+ client.conn.singleton_batch_request(
66
+ 'build-transaction',
67
+ [builder]
68
+ ) { |item| Template.new(item) }
69
+ end
70
+
71
+ # @param [Array<Builder>] builders
72
+ # @return [Array<Template>]
73
+ def build_batch(builders)
74
+ client.conn.batch_request(
75
+ 'build-transaction',
76
+ builders
77
+ ) { |item| Template.new(item) }
78
+ end
79
+
80
+ # @param [Template] template
81
+ # @return [SubmitResponse]
82
+ def submit(template)
83
+ client.conn.singleton_batch_request(
84
+ 'submit-transaction',
85
+ {transactions: [template]}
86
+ ) { |item| SubmitResponse.new(item) }
87
+ end
88
+
89
+ # @param [Array<Template>] templates
90
+ # @return [Array<SubmitResponse>]
91
+ def submit_batch(templates)
92
+ client.conn.batch_request(
93
+ 'submit-transaction',
94
+ {transactions: templates}
95
+ ) { |item| SubmitResponse.new(item) }
96
+ end
97
+
98
+ # @param [Hash] query
99
+ # @return [Query]
100
+ def query(query = {})
101
+ Query.new(client, query)
102
+ end
103
+ end
104
+
105
+ class Query < Chain::Query
106
+ def fetch(query)
107
+ client.conn.request('list-transactions', query)
108
+ end
109
+
110
+ def translate(raw)
111
+ Transaction.new(raw)
112
+ end
113
+ end
114
+
115
+ class Input < ResponseObject
116
+ # @!attribute [r] type
117
+ # The type of the input.
118
+ #
119
+ # Possible values are "issue", "spend".
120
+ # @return [String]
121
+ attrib :type
122
+
123
+ # @!attribute [r] asset_id
124
+ # The id of the asset being issued or spent.
125
+ # @return [String]
126
+ attrib :asset_id
127
+
128
+ # @!attribute [r] asset_alias
129
+ # The alias of the asset being issued or spent (possibly null).
130
+ # @return [String]
131
+ attrib :asset_alias
132
+
133
+ # @!attribute [r] asset_definition
134
+ # The definition of the asset being issued or spent (possibly null).
135
+ # @return [Hash]
136
+ attrib :asset_definition
137
+
138
+ # @!attribute [r] asset_tags
139
+ # The tags of the asset being issued or spent (possibly null).
140
+ # @return [Hash]
141
+ attrib :asset_tags
142
+
143
+ # @!attribute [r] asset_is_local
144
+ # A flag indicating whether the asset being issued or spent is local.
145
+ # @return [Boolean]
146
+ attrib :asset_is_local
147
+
148
+ # @!attribute [r] amount
149
+ # The number of units of the asset being issued or spent.
150
+ # @return [Integer]
151
+ attrib :amount
152
+
153
+ # @!attribute [r] spent_output
154
+ # The output consumed by this input.
155
+ # @return [SpentOutput]
156
+ attrib(:spent_output) { |raw| SpentOutput.new(raw) }
157
+
158
+ # @!attribute [r] account_id
159
+ # The id of the account transferring the asset (possibly null if the
160
+ # input is an issuance or an unspent output is specified).
161
+ # @return [String]
162
+ attrib :account_id
163
+
164
+ # @!attribute [r] account_alias
165
+ # The alias of the account transferring the asset (possibly null if the
166
+ # input is an issuance or an unspent output is specified).
167
+ # @return [String]
168
+ attrib :account_alias
169
+
170
+ # @!attribute [r] account_tags
171
+ # The tags associated with the account (possibly null).
172
+ # @return [String]
173
+ attrib :account_tags
174
+
175
+ # @!attribute [r] input_witness
176
+ # @return [String]
177
+ attrib :input_witness
178
+
179
+ # @!attribute [r] issuance_program
180
+ # A program specifying a predicate for issuing an asset (possibly null
181
+ # if input is not an issuance).
182
+ # @return [String]
183
+ attrib :issuance_program
184
+
185
+ # @!attribute [r] control_program
186
+ # @return [String]
187
+ attrib :control_program
188
+
189
+ # @!attribute [r] reference_data
190
+ # User specified, unstructured data embedded within an input
191
+ # (possibly null).
192
+ # @return [Hash]
193
+ attrib :reference_data
194
+
195
+ # @!attribute [r] is_local
196
+ # A flag indicating if the input is local.
197
+ # @return [Boolean]
198
+ attrib :is_local
199
+
200
+ class SpentOutput < ResponseObject
201
+ # @!attribute [r] transaction_id
202
+ # Unique transaction identifier.
203
+ # @return [String]
204
+ attrib :transaction_id
205
+
206
+ # @!attribute [r] position
207
+ # Position of an output within the transaction.
208
+ # @return [Integer]
209
+ attrib :position
210
+ end
211
+ end
212
+
213
+ class Output < ResponseObject
214
+ # @!attribute [r] type
215
+ # The type of the output.
216
+ #
217
+ # Possible values are "control" and "retire".
218
+ # @return [String]
219
+ attrib :type
220
+
221
+ # @!attribute [r] purpose
222
+ # The purpose of the output.
223
+ #
224
+ # Possible values are "receive" and "change".
225
+ # @return [String]
226
+ attrib :purpose
227
+
228
+ # @!attribute [r] position
229
+ # The output's position in a transaction's list of outputs.
230
+ # @return [Integer]
231
+ attrib :position
232
+
233
+ # @!attribute [r] asset_id
234
+ # The id of the asset being controlled.
235
+ # @return [String]
236
+ attrib :asset_id
237
+
238
+ # @!attribute [r] asset_alias
239
+ # The alias of the asset being controlled (possibly null).
240
+ # @return [String]
241
+ attrib :asset_alias
242
+
243
+ # @!attribute [r] asset_definition
244
+ # The definition of the asset being controlled (possibly null).
245
+ # @return [Hash]
246
+ attrib :asset_definition
247
+
248
+ # @!attribute [r] asset_tags
249
+ # The tags of the asset being controlled (possibly null).
250
+ # @return [Hash]
251
+ attrib :asset_tags
252
+
253
+ # @!attribute [r] asset_is_local
254
+ # A flag indicating whether the asset being controlled is local.
255
+ # @return [Boolean]
256
+ attrib :asset_is_local
257
+
258
+ # @!attribute [r] amount
259
+ # The number of units of the asset being controlled.
260
+ # @return [Integer]
261
+ attrib :amount
262
+
263
+ # @!attribute [r] account_id
264
+ # The id of the account controlling this output (possibly null if a
265
+ # control program is specified).
266
+ # @return [String]
267
+ attrib :account_id
268
+
269
+ # @!attribute [r] account_alias
270
+ # The alias of the account controlling this output (possibly null if
271
+ # a control program is specified).
272
+ # @return [String]
273
+ attrib :account_alias
274
+
275
+ # @!attribute [r] account_tags
276
+ # The tags associated with the account controlling this output
277
+ # (possibly null if a control program is specified).
278
+ # @return [Hash]
279
+ attrib :account_tags
280
+
281
+ # @!attribute [r] control_program
282
+ # The control program which must be satisfied to transfer this output.
283
+ # @return [String]
284
+ attrib :control_program
285
+
286
+ # @!attribute [r] reference_data
287
+ # User specified, unstructured data embedded within an input
288
+ # (possibly null).
289
+ # @return [Hash]
290
+ attrib :reference_data
291
+
292
+ # @!attribute [r] is_local
293
+ # A flag indicating if the output is local.
294
+ # @return [Boolean]
295
+ attrib :is_local
296
+ end
297
+
298
+ class Builder
299
+ def initialize(&block)
300
+ block.call(self) if block
301
+ end
302
+
303
+ # @return [Array<Hash>]
304
+ def actions
305
+ @actions ||= []
306
+ end
307
+
308
+ # @param [Template, String] template_or_raw_tx
309
+ # @return [Builder]
310
+ def base_transaction(template_or_raw_tx)
311
+ if template_or_raw_tx.is_a?(Transaction::Template)
312
+ @base_transaction = template_or_raw_tx.raw_transaction
313
+ else
314
+ @base_transaction = template_or_raw_tx
315
+ end
316
+ self
317
+ end
318
+
319
+ # @return [Builder]
320
+ def ttl(ttl)
321
+ @ttl = ttl
322
+ self
323
+ end
324
+
325
+ # @return [Hash]
326
+ def to_h
327
+ {
328
+ actions: actions,
329
+ base_transaction: @base_transaction,
330
+ ttl: @ttl,
331
+ }
332
+ end
333
+
334
+ # @return [String]
335
+ def to_json(opts = nil)
336
+ to_h.to_json(opts)
337
+ end
338
+
339
+ # Add an action to the tranasction builder
340
+ # @param [Hash] params Action parameters containing a type field and the
341
+ # required parameters for that type
342
+ # @return [Builder]
343
+ def add_action(params)
344
+ # Some actions require an idempotency token, so we'll add it here as a
345
+ # generic parameter.
346
+ params = {client_token: SecureRandom.uuid}.merge(params)
347
+ actions << params
348
+ self
349
+ end
350
+
351
+ # Sets the transaction-level reference data.
352
+ # May only be used once per transaction.
353
+ # @param [Hash] reference_data User specified, unstructured data to
354
+ # be embedded in a transaction
355
+ # @return [Builder]
356
+ def transaction_reference_data(reference_data)
357
+ add_action(
358
+ type: :set_transaction_reference_data,
359
+ reference_data: reference_data,
360
+ )
361
+ end
362
+
363
+ # Add an issuance action.
364
+ # @param [Hash] params Action parameters
365
+ # @option params [String] :asset_id Asset ID specifiying the asset to be issued.
366
+ # You must specify either an ID or an alias.
367
+ # @option params [String] :asset_alias Asset alias specifying the asset to be issued.
368
+ # You must specify either an ID or an alias.
369
+ # @option params [Integer] :amount amount of the asset to be issued
370
+ # @return [Builder]
371
+ def issue(params)
372
+ add_action(params.merge(type: :issue))
373
+ end
374
+
375
+ # Add a spend action taken on a particular account.
376
+ # @param [Hash] params Action parameters
377
+ # @option params [String] :asset_id Asset ID specifiying the asset to be spent.
378
+ # You must specify either an ID or an alias.
379
+ # @option params [String] :asset_alias Asset alias specifying the asset to be spent.
380
+ # You must specify either an ID or an alias.
381
+ # @option params [String] :account_id Account ID specifiying the account spending the asset.
382
+ # You must specify either an ID or an alias.
383
+ # @option params [String] :account_alias Account alias specifying the account spending the asset.
384
+ # You must specify either an ID or an alias.
385
+ # @option params [Integer] :amount amount of the asset to be spent.
386
+ # @return [Builder]
387
+ def spend_from_account(params)
388
+ add_action(params.merge(type: :spend_account))
389
+ end
390
+
391
+ # Add a spend action taken on a particular unspent output.
392
+ # @param [Hash] params Action parameters
393
+ # @option params [String] :transaction_id Transaction ID specifying the tranasction to select an output from.
394
+ # @option params [Integer] :position Position of the output within the transaction to be spent.
395
+ # @return [Builder]
396
+ def spend_account_unspent_output(params)
397
+ add_action(params.merge(type: :spend_account_unspent_output))
398
+ end
399
+
400
+ # Add a control action taken on a particular account.
401
+ # @param [Hash] params Action parameters
402
+ # @option params [String] :asset_id Asset ID specifiying the asset to be controlled.
403
+ # You must specify either an ID or an alias.
404
+ # @option params [String] :asset_alias Asset alias specifying the asset to be controlled.
405
+ # You must specify either an ID or an alias.
406
+ # @option params [String] :account_id Account ID specifiying the account controlling the asset.
407
+ # You must specify either an ID or an alias.
408
+ # @option params [String] :account_alias Account alias specifying the account controlling the asset.
409
+ # You must specify either an ID or an alias.
410
+ # @option params [Integer] :amount amount of the asset to be controlled.
411
+ # @return [Builder]
412
+ def control_with_account(params)
413
+ add_action(params.merge(type: :control_account))
414
+ end
415
+
416
+ # Add a control action taken on a control program.
417
+ # @param [Hash] params Action parameters
418
+ # @option params [String] :asset_id Asset ID specifiying the asset to be controlled.
419
+ # You must specify either an ID or an alias.
420
+ # @option params [String] :asset_alias Asset alias specifying the asset to be controlled.
421
+ # You must specify either an ID or an alias.
422
+ # @option params [String] :control_program The control program to be used
423
+ # @option params [Integer] :amount amount of the asset to be controlled.
424
+ # @return [Builder]
425
+ def control_with_program(params)
426
+ add_action(params.merge(type: :control_program))
427
+ end
428
+
429
+ # Add a retire action.
430
+ # @param [Hash] params Action parameters
431
+ # @option params [String] :asset_id Asset ID specifiying the asset to be retired.
432
+ # You must specify either an ID or an alias.
433
+ # @option params [String] :asset_alias Asset alias specifying the asset to be retired.
434
+ # You must specify either an ID or an alias.
435
+ # @option params [Integer] :amount Amount of the asset to be retired.
436
+ # @return [Builder]
437
+ def retire(params)
438
+ add_action(params.merge(
439
+ type: :control_program,
440
+ control_program: '6a'
441
+ ))
442
+ end
443
+ end
444
+
445
+ class SubmitResponse < ResponseObject
446
+ # @!attribute [r] id
447
+ # @return [String]
448
+ attrib :id
449
+ end
450
+
451
+ class Template < ResponseObject
452
+ # @!attribute [r] raw_transaction
453
+ # @return [String]
454
+ attrib :raw_transaction
455
+
456
+ # @!attribute [r] signing_instructions
457
+ # @return [String]
458
+ attrib :signing_instructions
459
+
460
+ # @return [Template]
461
+ def allow_additional_actions
462
+ @allow_additional_actions = true
463
+ self
464
+ end
465
+
466
+ # @return [Hash]
467
+ def to_h
468
+ super.merge(allow_additional_actions: @allow_additional_actions)
469
+ end
470
+ end
471
+ end
472
+ end
@@ -0,0 +1,100 @@
1
+ require 'securerandom'
2
+
3
+ require_relative './client_module'
4
+ require_relative './connection'
5
+ require_relative './constants'
6
+ require_relative './response_object'
7
+
8
+ module Chain
9
+ class TransactionFeed < ResponseObject
10
+
11
+ # @!attribute [r] id
12
+ # Unique transaction feed identifier.
13
+ # @return [String]
14
+ attrib :id
15
+
16
+ # @!attribute [r] alias
17
+ # User specified, unique identifier.
18
+ # @return [String]
19
+ attrib :alias
20
+
21
+ # @!attribute [r] filter
22
+ # @return [String]
23
+ attrib :filter
24
+
25
+ # @!attribute [r] after
26
+ # @return [String]
27
+ attrib :after
28
+
29
+ def initialize(raw_attribs, base_conn)
30
+ super(raw_attribs)
31
+
32
+ # The consume/ack cycle should run on its own thread, so make a copy of
33
+ # the base connection so this feed has an exclusive HTTP connection.
34
+ @conn = Connection.new(base_conn.opts)
35
+ end
36
+
37
+ # @param [Fixnum] timeout value in seconds
38
+ # @yield [Transaction] block process individual transactions
39
+ # @yieldparam [Transaction] tx
40
+ # @return [void]
41
+ def consume(timeout: 24*60*60)
42
+ query = {
43
+ filter: filter,
44
+ after: after,
45
+ timeout: (timeout * 1000).to_i, # milliseconds
46
+ ascending_with_long_poll: true
47
+ }
48
+
49
+ longpoll = Connection.new(@conn.opts.merge(read_timeout: timeout))
50
+
51
+ loop do
52
+ page = longpoll.request('list-transactions', query)
53
+ query = page['next']
54
+
55
+ page['items'].each do |raw_tx|
56
+ tx = Transaction.new(raw_tx)
57
+
58
+ # Memoize the cursor value for this transaction in case the user
59
+ # decides to ack. The format of the cursor value is specified in the
60
+ # core/query package.
61
+ @next_after = "#{tx.block_height}:#{tx.position}-#{MAX_BLOCK_HEIGHT}"
62
+
63
+ yield tx
64
+ end
65
+ end
66
+ end
67
+
68
+ def ack
69
+ raise 'ack must be called at most once per cycle in a consume loop' unless @next_after
70
+
71
+ @conn.request(
72
+ 'update-transaction-feed',
73
+ id: id,
74
+ after: @next_after,
75
+ previous_after: after,
76
+ )
77
+
78
+ self.after = @next_after
79
+ @next_after = nil
80
+ end
81
+
82
+ class ClientModule < Chain::ClientModule
83
+
84
+ # @param [Hash] opts
85
+ # @return [TransactionFeed]
86
+ def create(opts)
87
+ opts = {client_token: SecureRandom.uuid()}.merge(opts)
88
+ TransactionFeed.new(client.conn.request('create-transaction-feed', opts), client.conn)
89
+ end
90
+
91
+ # @param [Hash] opts
92
+ # @return [TransactionFeed]
93
+ def get(opts)
94
+ TransactionFeed.new(client.conn.request('get-transaction-feed', opts), client.conn)
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,90 @@
1
+ require_relative './client_module'
2
+ require_relative './response_object'
3
+ require_relative './query'
4
+
5
+ module Chain
6
+ class UnspentOutput < ResponseObject
7
+
8
+ # @!attribute [r] type
9
+ # @return [String]
10
+ attrib :type
11
+
12
+ # @!attribute [r] purpose
13
+ # @return [String]
14
+ attrib :purpose
15
+
16
+ # @!attribute [r] transaction_id
17
+ # @return [String]
18
+ attrib :transaction_id
19
+
20
+ # @!attribute [r] position
21
+ # @return [Integer]
22
+ attrib :position
23
+
24
+ # @!attribute [r] asset_id
25
+ # @return [String]
26
+ attrib :asset_id
27
+
28
+ # @!attribute [r] asset_alias
29
+ # @return [String]
30
+ attrib :asset_alias
31
+
32
+ # @!attribute [r] asset_definition
33
+ # @return [Hash]
34
+ attrib :asset_definition
35
+
36
+ # @!attribute [r] asset_tags
37
+ # @return [Hash]
38
+ attrib :asset_tags
39
+
40
+ # @!attribute [r] asset_is_local
41
+ # @return [Boolean]
42
+ attrib :asset_is_local
43
+
44
+ # @!attribute [r] amount
45
+ # @return [Integer]
46
+ attrib :amount
47
+
48
+ # @!attribute [r] account_id
49
+ # @return [String]
50
+ attrib :account_id
51
+
52
+ # @!attribute [r] account_alias
53
+ # @return [String]
54
+ attrib :account_alias
55
+
56
+ # @!attribute [r] account_tags
57
+ # @return [Hash]
58
+ attrib :account_tags
59
+
60
+ # @!attribute [r] control_program
61
+ # @return [String]
62
+ attrib :control_program
63
+
64
+ # @!attribute [r] reference_data
65
+ # @return [Hash]
66
+ attrib :reference_data
67
+
68
+ # @!attribute [r] is_local
69
+ # @return [Boolean]
70
+ attrib :is_local
71
+
72
+ class ClientModule < Chain::ClientModule
73
+ # @param [Hash] query
74
+ # @return Query
75
+ def query(query = {})
76
+ Query.new(client, query)
77
+ end
78
+ end
79
+
80
+ class Query < Chain::Query
81
+ def fetch(query)
82
+ client.conn.request('list-unspent-outputs', query)
83
+ end
84
+
85
+ def translate(raw)
86
+ UnspentOutput.new(raw)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,3 @@
1
+ module Chain
2
+ VERSION = '1.0.0.pre'
3
+ end
data/lib/chain.rb ADDED
@@ -0,0 +1,3 @@
1
+ require_relative './chain/client'
2
+ require_relative './chain/constants'
3
+ require_relative './chain/version'