ton_sdk_client 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ module TonSdk
2
+ class Helper
3
+
4
+ # converts a symbol which may contain _, into a capital-case string
5
+ # :aaa_bbb_ccc -> AaaBbbCcc
6
+ def self.sym_to_capitalized_case_str(symb)
7
+ symb.to_s.split('_').map(&:capitalize).join
8
+ end
9
+
10
+
11
+ # converts a capital-case string
12
+ # into a symbol
13
+ # AaaBbbCcc --> :aaa_bbb_ccc
14
+ def self.capitalized_case_str_to_snake_case_sym(str)
15
+ str.split(/(?=[A-Z])/).map(&:downcase).join("_").to_sym
16
+ end
17
+
18
+ def self.base64_from_hex(hex_digest) = [[hex_digest].pack("H*")].pack("m0")
19
+ end
20
+ end
@@ -0,0 +1,230 @@
1
+ require 'ffi'
2
+ require 'rbconfig'
3
+ require 'concurrent'
4
+ require 'logger'
5
+
6
+ module TonSdk
7
+ module Interop
8
+ extend FFI::Library
9
+
10
+ class << self
11
+ attr_reader :logger
12
+ end
13
+
14
+ @logger = Logger.new(STDOUT)
15
+
16
+ class TcStringData < FFI::Struct
17
+ layout :content, :pointer,
18
+ :len, :uint32
19
+
20
+ def self.from_string(s)
21
+ tcs = TcStringData.new
22
+ bytes_count = s.unpack("C*").size
23
+ ptr1 = FFI::MemoryPointer.new(:char, bytes_count)
24
+ ptr1.put_bytes(0, s, 0, bytes_count)
25
+ tcs[:content] = ptr1
26
+ tcs[:len] = ptr1.size
27
+ tcs
28
+ end
29
+ end
30
+
31
+ class TcResponse < FFI::Struct
32
+ layout :result_json, TcStringData,
33
+ :error_json, TcStringData
34
+ end
35
+
36
+ DEFAULT_LIB_NAME = 'tonclient'
37
+ base_lib_name2 = case RbConfig::CONFIG['host_os']
38
+ when /linux/
39
+ 'linux'
40
+ when /darwin/
41
+ 'darwin'
42
+ when /mswin|mingw32|windows/
43
+ 'win32'
44
+ else
45
+ raise "unsupported OS: #{RbConfig::CONFIG['host_os']}"
46
+ end
47
+
48
+ lib_full_name = if !ENV['TON_CLIENT_NATIVE_LIB_NAME'].nil?
49
+ ENV['TON_CLIENT_NATIVE_LIB_NAME']
50
+ else
51
+ fl_nm = "#{DEFAULT_LIB_NAME}.#{FFI::Platform::LIBSUFFIX}"
52
+ File.join(File.expand_path(File.dirname(File.dirname(__dir__))), fl_nm)
53
+ end
54
+
55
+ ffi_lib(lib_full_name)
56
+
57
+
58
+
59
+
60
+ #
61
+ # in C
62
+ #
63
+ # enum tc_response_types_t {
64
+ # tc_response_success = 0,
65
+ # tc_response_error = 1,
66
+ # tc_response_nop = 2,
67
+ # tc_response_custom = 100,
68
+ # };
69
+ module TcResponseCodes
70
+ SUCCESS = 0
71
+ ERROR = 1
72
+ NOP = 2
73
+ APP_REQUEST = 3
74
+ APP_NOTIFY = 4
75
+ CUSTOM = 100
76
+ MAX = 999
77
+ end
78
+
79
+
80
+ #
81
+ # in C
82
+ #
83
+ # tc_string_handle_t* tc_create_context(tc_string_data_t config);
84
+ # void tc_destroy_context(uint32_t context);
85
+
86
+ attach_function(:tc_create_context, [TcStringData.by_value], :pointer)
87
+ attach_function(:tc_destroy_context, [:uint32], :void)
88
+
89
+
90
+ #
91
+ # in C
92
+ #
93
+ # tc_string_data_t tc_read_string(const tc_string_handle_t* string);
94
+ # void tc_destroy_string(const tc_string_handle_t* string);
95
+
96
+ attach_function(:tc_read_string, [:pointer], TcStringData.by_value)
97
+ attach_function(:tc_destroy_string, [:pointer], :void)
98
+
99
+
100
+ #
101
+ # in C
102
+ #
103
+ # void tc_request(
104
+ # uint32_t context,
105
+ # tc_string_data_t function_name,
106
+ # tc_string_data_t function_params_json,
107
+ # uint32_t request_id,
108
+ # tc_response_handler_t response_handler);
109
+
110
+ # typedef void (*tc_response_handler_t)(
111
+ # uint32_t request_id,
112
+ # tc_string_data_t params_json,
113
+ # uint32_t response_type,
114
+ # bool finished);
115
+
116
+ callback(:tc_response_handler, [:uint32, TcStringData.by_value, :uint32, :bool], :void)
117
+ attach_function(:tc_request, [:uint32, TcStringData.by_value, TcStringData.by_value, :uint32, :tc_response_handler], :void) # TODO possibly blocking: true
118
+
119
+ #
120
+ # in C
121
+ #
122
+ # tc_string_handle_t* tc_request_sync(
123
+ # uint32_t context,
124
+ # tc_string_data_t function_name,
125
+ # tc_string_data_t function_params_json);
126
+ attach_function(:tc_request_sync, [:uint32, TcStringData.by_value, TcStringData.by_value], :pointer)
127
+
128
+
129
+
130
+ @@request_counter = Concurrent::AtomicFixnum.new(1)
131
+
132
+ def self.request_to_native_lib(
133
+ ctx,
134
+ function_name,
135
+ function_params_json = nil,
136
+ client_callback: nil,
137
+ is_single_thread_only: trfue
138
+ )
139
+ function_name_tc_str = TcStringData.from_string(function_name)
140
+ function_params_json_str = function_params_json || ""
141
+ function_params_json_tc_str = TcStringData.from_string(function_params_json_str)
142
+
143
+ @sm = Concurrent::Semaphore.new(1)
144
+ if is_single_thread_only == true
145
+ @sm.acquire()
146
+ end
147
+
148
+
149
+ # using @@request_counter here to pass a @@request_counter and handlers and then retrieve them
150
+ # is probably isn't needed.
151
+ # Thanks to the way Ruby is, the same affect can be achived by a block which is an easier way.
152
+ # Nonetheless, @@request_counter is incremented with each request and then sent out to a server
153
+ # in order to keep a server happy,
154
+ # because otherwise a server will, probably, reply in a wrong way.
155
+
156
+ self.tc_request(
157
+ ctx,
158
+ function_name_tc_str,
159
+ function_params_json_tc_str,
160
+ @@request_counter.value
161
+ ) do |req_id, params_json, response_type, is_finished|
162
+
163
+ tc_data_json_content = if params_json[:len] > 0
164
+ res = params_json[:content].read_string(params_json[:len])
165
+ JSON.parse(res)
166
+ else
167
+ ''
168
+ end
169
+
170
+ begin
171
+ case response_type
172
+
173
+ when TcResponseCodes::SUCCESS
174
+ if block_given?
175
+ yield NativeLibResponsetResult.new(result: tc_data_json_content)
176
+ end
177
+
178
+ when TcResponseCodes::ERROR
179
+ if block_given?
180
+ yield NativeLibResponsetResult.new(error: tc_data_json_content)
181
+ end
182
+
183
+ when TcResponseCodes::NOP
184
+ nil
185
+
186
+ when TcResponseCodes::APP_REQUEST
187
+ if !client_callback.nil?
188
+ client_callback.call(:request, tc_data_json_content)
189
+ end
190
+
191
+ when TcResponseCodes::APP_NOTIFY
192
+ if !client_callback.nil?
193
+ client_callback.call(:notify, tc_data_json_content)
194
+ end
195
+
196
+ when TcResponseCodes::CUSTOM..TcResponseCodes::MAX
197
+ if !client_callback.nil?
198
+ client_callback.call(:custom, tc_data_json_content)
199
+ end
200
+
201
+ else
202
+ raise ArgumentError.new("unsupported response type: #{response_type}")
203
+ end
204
+
205
+ rescue => e
206
+ @logger.error(e)
207
+ ensure
208
+ if is_single_thread_only == true
209
+ @sm.release()
210
+ end
211
+ end
212
+ end
213
+
214
+
215
+
216
+ if is_single_thread_only == true
217
+ @sm.acquire()
218
+ end
219
+
220
+ @@request_counter.increment()
221
+ nil
222
+ end
223
+
224
+ def self.request_to_native_lib_sync(ctx, function_name, function_params_json)
225
+ function_name_tc_str = TcStringData.from_string(function_name)
226
+ function_params_json_tc_str = TcStringData.from_string(function_params_json)
227
+ self.tc_request_sync(ctx, function_name_tc_str, function_params_json_tc_str)
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,452 @@
1
+ module TonSdk
2
+ module Net
3
+
4
+ #
5
+ # types
6
+ #
7
+
8
+ module ErrorCode
9
+ QUERY_FAILED = 601
10
+ SUBSCRIBE_FAILED = 602
11
+ WAIT_FOR_FAILED = 603
12
+ GET_SUBSCRIPTION_FAILED = 604
13
+ INVALID_SERVER_RESPONSE = 605
14
+ CLOCK_OUT_OF_SYNC = 606
15
+ WAIT_FOR_TIMEOUT = 607
16
+ GRAPHQL_ERROR = 608
17
+ NETWORK_MODULE_SUSPENDED = 609
18
+ WEBSOCKET_DISCONNECTED = 610
19
+ NOT_SUPPORTED = 611
20
+ NO_ENDPOINTS_PROVIDED = 612
21
+ GRAPHQL_WEBSOCKET_INIT_ERROR = 613,
22
+ NETWORK_MODULE_RESUMED = 614
23
+ end
24
+
25
+ class OrderBy
26
+ SORT_DIRECTION_VALUES = [:asc, :desc]
27
+
28
+ attr_reader :path, :direction
29
+
30
+ def initialize(path:, direction:)
31
+ @path = path
32
+ unless SORT_DIRECTION_VALUES.include?(direction)
33
+ raise ArgumentError.new("direction #{direction} doesn't exist; existing values: #{SORT_DIRECTION_VALUES}")
34
+ end
35
+
36
+ @direction = direction
37
+ end
38
+ end
39
+
40
+ class ParamsOfQueryCollection
41
+ attr_reader :collection, :filter, :result, :order, :limit
42
+
43
+ def initialize(collection: , filter: nil, result: , order: [], limit: nil)
44
+ @collection = collection
45
+ @filter = filter
46
+ @result = result
47
+ @order = order
48
+ @limit = limit
49
+ end
50
+
51
+ def to_h
52
+ ord_h_s = if !@order.nil?
53
+ @order.map do |x|
54
+ {
55
+ path: x.path,
56
+ direction: x.direction.to_s.upcase
57
+ }
58
+ end
59
+ end
60
+
61
+ {
62
+ collection: @collection,
63
+ filter: @filter,
64
+ result: @result,
65
+ order: ord_h_s,
66
+ limit: @limit
67
+ }
68
+ end
69
+ end
70
+
71
+ class ResultOfQueryCollection
72
+ attr_reader :result
73
+
74
+ def initialize(a)
75
+ @result = a
76
+ end
77
+ end
78
+
79
+ class ParamsOfWaitForCollection
80
+ attr_reader :collection, :filter, :result, :timeout
81
+
82
+ def initialize(collection:, filter: nil, result:, timeout: nil)
83
+ @collection = collection
84
+ @filter = filter
85
+ @result = result
86
+ @timeout = timeout
87
+ end
88
+
89
+ def to_h
90
+ {
91
+ collection: @collection,
92
+ filter: @filter,
93
+ result: @result,
94
+ timeout: @timeout
95
+ }
96
+ end
97
+ end
98
+
99
+ class ResultOfWaitForCollection
100
+ attr_reader :result
101
+
102
+ def initialize(a)
103
+ @result = a
104
+ end
105
+ end
106
+
107
+ class ParamsOfSubscribeCollection
108
+ attr_reader :collection, :filter, :result
109
+
110
+ def initialize(collection:, filter: nil, result:)
111
+ @collection = collection
112
+ @filter = filter
113
+ @result = result
114
+ end
115
+
116
+ def to_h
117
+ {
118
+ collection: @collection,
119
+ filter: @filter,
120
+ result: @result
121
+ }
122
+ end
123
+ end
124
+
125
+ class ResultOfSubscribeCollection
126
+ attr_reader :handle
127
+
128
+ def initialize(a)
129
+ @handle = a
130
+ end
131
+
132
+ def to_h = { handle: @handle }
133
+ end
134
+
135
+ class ParamsOfQuery
136
+ attr_reader :query, :variables
137
+
138
+ def initialize(query:, variables: nil)
139
+ @query = query
140
+ @variables = variables
141
+ end
142
+
143
+ def to_h
144
+ {
145
+ query: @query,
146
+ variables: @variables
147
+ }
148
+ end
149
+ end
150
+
151
+ class ResultOfQuery
152
+ attr_reader :result
153
+
154
+ def initialize(a)
155
+ @result = a
156
+ end
157
+ end
158
+
159
+ class ParamsOfFindLastShardBlock
160
+ attr_reader :address
161
+
162
+ def initialize(a)
163
+ @address = a
164
+ end
165
+
166
+ def to_h = { address: @address }
167
+ end
168
+
169
+ class ResultOfFindLastShardBlock
170
+ attr_reader :block_id
171
+
172
+ def initialize(a)
173
+ @block_id = a
174
+ end
175
+ end
176
+
177
+ class EndpointsSet
178
+ attr_reader :endpoints
179
+
180
+ def initialize(a)
181
+ @endpoints = a
182
+ end
183
+
184
+ def to_h = { endpoints: @endpoints }
185
+ end
186
+
187
+ class ParamsOfQueryOperation
188
+ attr_reader :type_, :params
189
+
190
+ def new_with_type_query_collection(params)
191
+ @type_ = :query_collection
192
+ @params = params
193
+ end
194
+
195
+ def new_with_type_wait_for_collection(params)
196
+ @type_ = :wait_for_collection
197
+ @params = params
198
+ end
199
+
200
+ def new_with_type_aggregate_collection(params)
201
+ @type_ = :aggregate_collection
202
+ @params = params
203
+ end
204
+
205
+ def to_h
206
+ tp = {
207
+ type: Helper.sym_to_capitalized_case_str(@type_)
208
+ }
209
+
210
+ param_keys = @params.to_h
211
+ tp.merge(param_keys)
212
+ end
213
+ end
214
+
215
+ class ParamsOfBatchQuery
216
+ attr_reader :operations
217
+
218
+ def initialize(a)
219
+ @operations = a
220
+ end
221
+
222
+ def to_h = { operations: @operations.compact.map(&:to_h) }
223
+ end
224
+
225
+ class ResultOfBatchQuery
226
+ attr_reader :results
227
+
228
+ def initialize(a)
229
+ @results = a
230
+ end
231
+
232
+ def to_h = { results: @results }
233
+ end
234
+
235
+ class ParamsOfAggregateCollection
236
+ attr_reader :collection, :filter, :fields
237
+
238
+ def initialize(collection:, filter: nil, fields: [])
239
+ @collection = collection
240
+ @filter = filter
241
+ @fields = fields
242
+ end
243
+
244
+ def to_h
245
+ {
246
+ collection: @collection,
247
+ filter: @filter,
248
+ fields: @fields.map(&:to_h)
249
+ }
250
+ end
251
+ end
252
+
253
+ class FieldAggregation
254
+ AGGREGATION_FN_VALUES = [
255
+ :count,
256
+ :min,
257
+ :max,
258
+ :sum,
259
+ :average
260
+ ]
261
+
262
+ attr_reader :field, :fn
263
+
264
+ def initialize(field:, fn:)
265
+ unless AGGREGATION_FN_VALUES.include?(fn)
266
+ raise ArgumentError.new("aggregate function #{fn} doesn't exist; existing values: #{AGGREGATION_FN_VALUES}")
267
+ end
268
+ @field = field
269
+ @fn = fn
270
+ end
271
+
272
+ def to_h
273
+ {
274
+ field: @field,
275
+ fn: @fn.to_s.upcase
276
+ }
277
+ end
278
+ end
279
+
280
+ class ResultOfAggregateCollection
281
+ attr_reader :values
282
+
283
+ def initialize(a)
284
+ @values = a
285
+ end
286
+
287
+ def to_h = { values: @values }
288
+ end
289
+
290
+
291
+
292
+ #
293
+ # functions
294
+ #
295
+
296
+ def self.query_collection(ctx, params)
297
+ Interop::request_to_native_lib(
298
+ ctx,
299
+ "net.query_collection",
300
+ params.to_h.to_json,
301
+ is_single_thread_only: false
302
+ ) do |resp|
303
+ if resp.success?
304
+ yield NativeLibResponsetResult.new(
305
+ result: ResultOfQueryCollection.new(resp.result["result"])
306
+ )
307
+ else
308
+ yield resp
309
+ end
310
+ end
311
+ end
312
+
313
+ def self.wait_for_collection(ctx, params)
314
+ Interop::request_to_native_lib(
315
+ ctx,
316
+ "net.wait_for_collection",
317
+ params.to_h.to_json,
318
+ is_single_thread_only: false
319
+ ) do |resp|
320
+ if resp.success?
321
+ yield NativeLibResponsetResult.new(
322
+ result: ResultOfWaitForCollection.new(resp.result["result"])
323
+ )
324
+ else
325
+ yield resp
326
+ end
327
+ end
328
+ end
329
+
330
+ def self.unsubscribe(ctx, params)
331
+ Interop::request_to_native_lib(ctx, "net.unsubscribe", params.to_h.to_json) do |resp|
332
+ if resp.success?
333
+ yield NativeLibResponsetResult.new(
334
+ result: ""
335
+ )
336
+ else
337
+ yield resp
338
+ end
339
+ end
340
+ end
341
+
342
+ def self.subscribe_collection(ctx, params, client_callback: nil)
343
+ Interop::request_to_native_lib(
344
+ ctx,
345
+ "net.subscribe_collection",
346
+ params.to_h.to_json,
347
+ client_callback: client_callback,
348
+ is_single_thread_only: false
349
+ ) do |resp|
350
+ if resp.success?
351
+ yield NativeLibResponsetResult.new(
352
+ result: ResultOfSubscribeCollection.new(resp.result["handle"])
353
+ )
354
+ else
355
+ yield resp
356
+ end
357
+ end
358
+ end
359
+
360
+ def self.query(ctx, params)
361
+ Interop::request_to_native_lib(ctx, "net.query", params.to_h.to_json) do |resp|
362
+ if resp.success?
363
+ yield NativeLibResponsetResult.new(
364
+ result: ResultOfQuery.new(resp.result["result"])
365
+ )
366
+ else
367
+ yield resp
368
+ end
369
+ end
370
+ end
371
+
372
+ def self.suspend(ctx)
373
+ Interop::request_to_native_lib(ctx, "net.suspend", "") do |resp|
374
+ if resp.success?
375
+ yield NativeLibResponsetResult.new(result: "")
376
+ else
377
+ yield resp
378
+ end
379
+ end
380
+ end
381
+
382
+ def self.resume(ctx)
383
+ Interop::request_to_native_lib(ctx, "net.resume", "") do |resp|
384
+ if resp.success?
385
+ yield NativeLibResponsetResult.new(result: "")
386
+ else
387
+ yield resp
388
+ end
389
+ end
390
+ end
391
+
392
+ def self.find_last_shard_block(ctx, params)
393
+ Interop::request_to_native_lib(ctx, "net.find_last_shard_block", params.to_h.to_json) do |resp|
394
+ if resp.success?
395
+ yield NativeLibResponsetResult.new(
396
+ result: ResultOfFindLastShardBlock.new(resp.result["block_id"])
397
+ )
398
+ else
399
+ yield resp
400
+ end
401
+ end
402
+ end
403
+
404
+ def self.fetch_endpoints(ctx)
405
+ Interop::request_to_native_lib(ctx, "net.fetch_endpoints", nil) do |resp|
406
+ if resp.success?
407
+ yield NativeLibResponsetResult.new(
408
+ result: EndpointsSet.new(resp.result["endpoints"])
409
+ )
410
+ else
411
+ yield resp
412
+ end
413
+ end
414
+ end
415
+
416
+ def self.set_endpoints(ctx, params)
417
+ Interop::request_to_native_lib(ctx, "net.set_endpoints", params.to_h.to_json) do |resp|
418
+ if resp.success?
419
+ yield NativeLibResponsetResult.new(
420
+ result: nil
421
+ )
422
+ else
423
+ yield resp
424
+ end
425
+ end
426
+ end
427
+ end
428
+
429
+ def self.batch_query(ctx, params)
430
+ Interop::request_to_native_lib(ctx, "net.batch_query", params.to_h.to_json) do |resp|
431
+ if resp.success?
432
+ yield NativeLibResponsetResult.new(
433
+ result: ResultOfBatchQuery.new(resp.result["results"])
434
+ )
435
+ else
436
+ yield resp
437
+ end
438
+ end
439
+ end
440
+
441
+ def self.aggregate_collection(ctx, params)
442
+ Interop::request_to_native_lib(ctx, "net.aggregate_collection", params.to_h.to_json) do |resp|
443
+ if resp.success?
444
+ yield NativeLibResponsetResult.new(
445
+ result: ResultOfAggregateCollection.new(resp.result["values"])
446
+ )
447
+ else
448
+ yield resp
449
+ end
450
+ end
451
+ end
452
+ end