ton-sdk-ruby 0.0.1
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/bin/ton-sdk-ruby +7 -0
- data/lib/ton-sdk-ruby/bit_array/bit_array.rb +169 -0
- data/lib/ton-sdk-ruby/boc/builder.rb +261 -0
- data/lib/ton-sdk-ruby/boc/cell.rb +362 -0
- data/lib/ton-sdk-ruby/boc/hashmap.rb +398 -0
- data/lib/ton-sdk-ruby/boc/mask.rb +46 -0
- data/lib/ton-sdk-ruby/boc/serializer.rb +428 -0
- data/lib/ton-sdk-ruby/boc/slice.rb +335 -0
- data/lib/ton-sdk-ruby/johnny_mnemonic/ton_mnemonic.rb +144 -0
- data/lib/ton-sdk-ruby/johnny_mnemonic/utils.rb +40 -0
- data/lib/ton-sdk-ruby/johnny_mnemonic/words/english.json +2050 -0
- data/lib/ton-sdk-ruby/providers/provider.rb +41 -0
- data/lib/ton-sdk-ruby/providers/toncenter.rb +71 -0
- data/lib/ton-sdk-ruby/types/address.rb +203 -0
- data/lib/ton-sdk-ruby/types/block.rb +388 -0
- data/lib/ton-sdk-ruby/types/coins.rb +188 -0
- data/lib/ton-sdk-ruby/utils/bits.rb +25 -0
- data/lib/ton-sdk-ruby/utils/checksum.rb +46 -0
- data/lib/ton-sdk-ruby/utils/hash.rb +15 -0
- data/lib/ton-sdk-ruby/utils/helpers.rb +161 -0
- data/lib/ton-sdk-ruby/utils/numbers.rb +42 -0
- data/lib/ton-sdk-ruby/version.rb +4 -0
- data/lib/ton-sdk-ruby.rb +29 -0
- metadata +137 -0
@@ -0,0 +1,398 @@
|
|
1
|
+
module TonSdkRuby
|
2
|
+
|
3
|
+
class Hashmap
|
4
|
+
attr_reader :hashmap, :key_size
|
5
|
+
|
6
|
+
def initialize(key_size, options = {})
|
7
|
+
serializers = options.fetch(:serializers, {})
|
8
|
+
deserializers = options.fetch(:deserializers, {})
|
9
|
+
|
10
|
+
@hashmap = {}
|
11
|
+
@key_size = key_size
|
12
|
+
@serialize_key = serializers.fetch(:key, -> (key) { key })
|
13
|
+
@serialize_value = serializers.fetch(:value, -> (value) { value })
|
14
|
+
@deserialize_key = deserializers.fetch(:key, -> (key) { key })
|
15
|
+
@deserialize_value = deserializers.fetch(:value, -> (value) { value })
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
result = []
|
20
|
+
@hashmap.each do |k, v|
|
21
|
+
key = deserialize_key(k.chars.map(&:to_i))
|
22
|
+
value = deserialize_value(v)
|
23
|
+
|
24
|
+
result << (yield [key, value])
|
25
|
+
end
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(key)
|
30
|
+
k = serialize_key(key).join('')
|
31
|
+
v = @hashmap[k]
|
32
|
+
|
33
|
+
v.nil? ? nil : deserialize_value(v)
|
34
|
+
end
|
35
|
+
|
36
|
+
def has(key)
|
37
|
+
!get(key).nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def set(key, value)
|
41
|
+
k = serialize_key(key).join('')
|
42
|
+
v = serialize_value(value)
|
43
|
+
@hashmap[k] = v
|
44
|
+
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def add(key, value)
|
49
|
+
has(key) ? self : set(key, value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def replace(key, value)
|
53
|
+
has(key) ? set(key, value) : self
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_set(key, value)
|
57
|
+
prev = get(key)
|
58
|
+
set(key, value)
|
59
|
+
prev
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_add(key, value)
|
63
|
+
prev = get(key)
|
64
|
+
add(key, value)
|
65
|
+
prev
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_replace(key, value)
|
69
|
+
prev = get(key)
|
70
|
+
replace(key, value)
|
71
|
+
prev
|
72
|
+
end
|
73
|
+
|
74
|
+
def delete(key)
|
75
|
+
k = serialize_key(key).join('')
|
76
|
+
hashmap.delete(k)
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def is_empty?
|
81
|
+
hashmap.size == 0
|
82
|
+
end
|
83
|
+
|
84
|
+
def for_each(&callback)
|
85
|
+
self.each { |key, value| callback.call(key, value) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_raw(key)
|
89
|
+
hashmap[key.join('')]
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_raw(key, value)
|
93
|
+
hashmap[key.join('')] = value
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def sort_hashmap
|
98
|
+
sorted = hashmap.reduce([]) do |acc, (bitstring, value)|
|
99
|
+
key = bitstring.chars.map(&:to_i)
|
100
|
+
# Sort keys by DESC to serialize labels correctly later
|
101
|
+
order = bitstring.to_i(2)
|
102
|
+
lt = acc.find_index { |el| order > el[:order] }
|
103
|
+
index = lt ? lt : acc.length
|
104
|
+
|
105
|
+
acc.insert(index, { order: order, key: key, value: value })
|
106
|
+
end
|
107
|
+
|
108
|
+
sorted.map { |el| [el[:key], el[:value]] }
|
109
|
+
end
|
110
|
+
|
111
|
+
def serialize
|
112
|
+
nodes = sort_hashmap
|
113
|
+
|
114
|
+
if nodes.empty?
|
115
|
+
raise 'Hashmap: can\'t be empty. It must contain at least 1 key-value pair.'
|
116
|
+
end
|
117
|
+
|
118
|
+
Hashmap.serialize_edge(nodes)
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.serialize_edge(nodes)
|
122
|
+
# hme_empty$0
|
123
|
+
if nodes.empty?
|
124
|
+
label = serialize_label_short([])
|
125
|
+
|
126
|
+
return Builder.new
|
127
|
+
.store_bits(label)
|
128
|
+
.cell
|
129
|
+
end
|
130
|
+
|
131
|
+
edge = Builder.new
|
132
|
+
label = serialize_label(nodes)
|
133
|
+
|
134
|
+
edge.store_bits(label)
|
135
|
+
|
136
|
+
# hmn_leaf#_
|
137
|
+
if nodes.length == 1
|
138
|
+
leaf = serialize_leaf(nodes[0])
|
139
|
+
|
140
|
+
edge.store_slice(leaf.parse)
|
141
|
+
end
|
142
|
+
|
143
|
+
# hmn_fork#_
|
144
|
+
if nodes.length > 1
|
145
|
+
# Left edge can be empty, anyway we need to create hme_empty$0 to support right one
|
146
|
+
left_nodes, right_nodes = serialize_fork(nodes)
|
147
|
+
left_edge = serialize_edge(left_nodes)
|
148
|
+
|
149
|
+
edge.store_ref(left_edge)
|
150
|
+
|
151
|
+
unless right_nodes.empty?
|
152
|
+
right_edge = serialize_edge(right_nodes)
|
153
|
+
|
154
|
+
edge.store_ref(right_edge)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
edge.cell
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.serialize_fork(nodes)
|
162
|
+
# Serialize nodes to edges
|
163
|
+
nodes.reduce([[], []]) do |acc, (key, value)|
|
164
|
+
# Sort nodes by left/right edges
|
165
|
+
acc[key.shift].push([key, value])
|
166
|
+
|
167
|
+
acc
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.serialize_leaf(node)
|
172
|
+
node[1]
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.serialize_label(nodes)
|
176
|
+
# Each label can always be serialized in at least two different fashions, using
|
177
|
+
# hml_short or hml_long constructors. Usually the shortest serialization (and
|
178
|
+
# in the case of a tie—the lexicographically smallest among the shortest) is
|
179
|
+
# preferred and is generated by TVM hashmap primitives, while the other
|
180
|
+
# variants are still considered valid.
|
181
|
+
|
182
|
+
# Get nodes keys
|
183
|
+
first = nodes[0][0]
|
184
|
+
last = nodes[nodes.length - 1][0]
|
185
|
+
# m = length at most possible bits of n (key)
|
186
|
+
m = first.length
|
187
|
+
|
188
|
+
same_bits_index = nil
|
189
|
+
first.each_with_index do |el, index|
|
190
|
+
if el != last[index]
|
191
|
+
same_bits_index = index
|
192
|
+
break
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
same_bits_length = same_bits_index.nil? ? first.length : same_bits_index
|
197
|
+
|
198
|
+
if first[0] != last[0] || m == 0
|
199
|
+
# hml_short for zero most possible bits
|
200
|
+
return serialize_label_short([])
|
201
|
+
end
|
202
|
+
|
203
|
+
label = first[0, same_bits_length]
|
204
|
+
repeated = label.join('').match(/(^0+)|(^1+)/)[0].split('').map { |b| b.to_i }
|
205
|
+
label_short = serialize_label_short(label)
|
206
|
+
label_long = serialize_label_long(label, m)
|
207
|
+
label_same = nodes.length > 1 && repeated.length > 1 ? serialize_label_same(repeated, m) : nil
|
208
|
+
|
209
|
+
labels = [
|
210
|
+
{ bits: label.length, label: label_short },
|
211
|
+
{ bits: label.length, label: label_long },
|
212
|
+
{ bits: repeated.length, label: label_same }
|
213
|
+
].reject { |el| el[:label].nil? }
|
214
|
+
|
215
|
+
# Sort labels by their length
|
216
|
+
labels.sort_by! { |el| el[:label].length }
|
217
|
+
|
218
|
+
# Get most compact label
|
219
|
+
choosen = labels[0]
|
220
|
+
|
221
|
+
# Remove label bits from nodes keys
|
222
|
+
nodes.each { |key, _| key.shift(choosen[:bits]) }
|
223
|
+
|
224
|
+
choosen[:label]
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.serialize_label_short(bits)
|
228
|
+
label = Builder.new
|
229
|
+
|
230
|
+
label.store_bit(0)
|
231
|
+
.store_bits(bits.map { 1 })
|
232
|
+
.store_bit(0)
|
233
|
+
.store_bits(bits)
|
234
|
+
|
235
|
+
label.bits
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.serialize_label_long(bits, m)
|
239
|
+
label = Builder.new
|
240
|
+
|
241
|
+
label.store_bits([1, 0])
|
242
|
+
.store_uint(bits.length, Math.log2(m + 1).ceil)
|
243
|
+
.store_bits(bits)
|
244
|
+
|
245
|
+
label.bits
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.serialize_label_same(bits, m)
|
249
|
+
label = Builder.new
|
250
|
+
|
251
|
+
label.store_bits([1, 1])
|
252
|
+
.store_bit(bits[0])
|
253
|
+
.store_uint(bits.length, Math.log2(m + 1).ceil)
|
254
|
+
|
255
|
+
label.bits
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.deserialize(key_size, slice, options = {})
|
259
|
+
if slice.bits.length < 2
|
260
|
+
raise 'Hashmap: can\'t be empty. It must contain at least 1 key-value pair.'
|
261
|
+
end
|
262
|
+
|
263
|
+
hashmap = Hashmap.new(key_size, options)
|
264
|
+
nodes = deserialize_edge(slice, key_size)
|
265
|
+
|
266
|
+
nodes.each do |key, value|
|
267
|
+
hashmap.set_raw(key, value)
|
268
|
+
end
|
269
|
+
|
270
|
+
hashmap
|
271
|
+
end
|
272
|
+
|
273
|
+
def self.deserialize_edge(edge, key_size, key = [])
|
274
|
+
nodes = []
|
275
|
+
|
276
|
+
key.concat(deserialize_label(edge, key_size - key.length))
|
277
|
+
|
278
|
+
if key.length == key_size
|
279
|
+
value = Builder.new.store_slice(edge).cell
|
280
|
+
|
281
|
+
return nodes.concat([[key, value]])
|
282
|
+
end
|
283
|
+
|
284
|
+
edge.refs.each_with_index do |_r, i|
|
285
|
+
fork_edge = edge.load_ref.parse
|
286
|
+
fork_key = key + [i]
|
287
|
+
|
288
|
+
nodes.concat(deserialize_edge(fork_edge, key_size, fork_key))
|
289
|
+
end
|
290
|
+
|
291
|
+
nodes
|
292
|
+
end
|
293
|
+
|
294
|
+
def self.deserialize_label(edge, m)
|
295
|
+
# m = length at most possible bits of n (key)
|
296
|
+
|
297
|
+
# hml_short$0
|
298
|
+
if edge.load_bit == 0
|
299
|
+
return deserialize_label_short(edge)
|
300
|
+
end
|
301
|
+
|
302
|
+
# hml_long$10
|
303
|
+
if edge.load_bit == 0
|
304
|
+
return deserialize_label_long(edge, m)
|
305
|
+
end
|
306
|
+
|
307
|
+
# hml_same$11
|
308
|
+
deserialize_label_same(edge, m)
|
309
|
+
end
|
310
|
+
|
311
|
+
def self.deserialize_label_short(edge)
|
312
|
+
length = edge.bits.index(0)
|
313
|
+
edge.skip(length + 1)
|
314
|
+
edge.load_bits(length)
|
315
|
+
end
|
316
|
+
|
317
|
+
def self.deserialize_label_long(edge, m)
|
318
|
+
length = edge.load_uint(Math.log2(m + 1).ceil)
|
319
|
+
edge.load_bits(length)
|
320
|
+
end
|
321
|
+
|
322
|
+
def self.deserialize_label_same(edge, m)
|
323
|
+
repeated = edge.load_bit
|
324
|
+
length = edge.load_uint(Math.log2(m + 1).ceil)
|
325
|
+
Array.new(length) { repeated }
|
326
|
+
end
|
327
|
+
|
328
|
+
def cell
|
329
|
+
serialize
|
330
|
+
end
|
331
|
+
|
332
|
+
def self.parse(key_size, slice, options = nil)
|
333
|
+
deserialize(key_size, slice, options)
|
334
|
+
end
|
335
|
+
|
336
|
+
private
|
337
|
+
|
338
|
+
def serialize_key(key)
|
339
|
+
@serialize_key.call(key)
|
340
|
+
end
|
341
|
+
|
342
|
+
def serialize_value(value)
|
343
|
+
@serialize_value.call(value)
|
344
|
+
end
|
345
|
+
|
346
|
+
def deserialize_key(key)
|
347
|
+
@deserialize_key.call(key)
|
348
|
+
end
|
349
|
+
|
350
|
+
def deserialize_value(value)
|
351
|
+
@deserialize_value.call(value)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
class HashmapE < Hashmap
|
356
|
+
def initialize(key_size, options = nil)
|
357
|
+
super(key_size, options)
|
358
|
+
end
|
359
|
+
|
360
|
+
protected
|
361
|
+
|
362
|
+
def serialize
|
363
|
+
nodes = sort_hashmap
|
364
|
+
result = Builder.new
|
365
|
+
|
366
|
+
if nodes.empty?
|
367
|
+
result.store_bit(0).cell
|
368
|
+
else
|
369
|
+
result.store_bit(1).store_ref(HashmapE.serialize_edge(nodes)).cell
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def self.deserialize(key_size, slice, options = nil)
|
374
|
+
if slice.bits.length != 1
|
375
|
+
raise 'HashmapE: bad hashmap size flag.'
|
376
|
+
end
|
377
|
+
|
378
|
+
if slice.load_bit == 0
|
379
|
+
HashmapE.new(key_size, options)
|
380
|
+
else
|
381
|
+
hashmap = HashmapE.new(key_size, options)
|
382
|
+
edge = slice.load_ref.parse
|
383
|
+
nodes = Hashmap.deserialize_edge(edge, key_size)
|
384
|
+
|
385
|
+
nodes.each do |key, value|
|
386
|
+
hashmap.set_raw(key, value)
|
387
|
+
end
|
388
|
+
|
389
|
+
hashmap
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def self.parse(key_size, slice, options = nil)
|
394
|
+
deserialize(key_size, slice, options)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module TonSdkRuby
|
2
|
+
class Mask
|
3
|
+
|
4
|
+
attr_accessor :hash_index, :hash_count, :value
|
5
|
+
|
6
|
+
def initialize(mask)
|
7
|
+
if mask.class.to_s.downcase == "mask"
|
8
|
+
@value = mask.value
|
9
|
+
elsif mask.class.to_s.downcase == "integer"
|
10
|
+
@value = mask
|
11
|
+
end
|
12
|
+
|
13
|
+
@hash_index = count_set_bits(value)
|
14
|
+
@hash_count = @hash_index + 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def level
|
18
|
+
32 - clz(value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_significant(level)
|
22
|
+
level == 0 || (self.value >> (level - 1)) % 2 != 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def apply(level)
|
26
|
+
Mask.new(self.value & ((1 << level) - 1))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def clz(number, size = 32)
|
32
|
+
bits_string = number.to_i.to_s(2)
|
33
|
+
if bits_string.size > size
|
34
|
+
bits_string.slice((bits_string.size - size)..bits_string.size)
|
35
|
+
else
|
36
|
+
size - bits_string.size
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def count_set_bits(n)
|
41
|
+
n = n - ((n >> 1) & 0x55555555)
|
42
|
+
n = (n & 0x33333333) + ((n >> 2) & 0x33333333)
|
43
|
+
((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|