mixin_bot 0.12.1 → 1.0.0

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mixin_bot/api/address.rb +21 -0
  3. data/lib/mixin_bot/api/app.rb +5 -11
  4. data/lib/mixin_bot/api/asset.rb +9 -16
  5. data/lib/mixin_bot/api/attachment.rb +27 -22
  6. data/lib/mixin_bot/api/auth.rb +29 -51
  7. data/lib/mixin_bot/api/blaze.rb +4 -3
  8. data/lib/mixin_bot/api/collectible.rb +60 -58
  9. data/lib/mixin_bot/api/conversation.rb +29 -49
  10. data/lib/mixin_bot/api/encrypted_message.rb +17 -17
  11. data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
  12. data/lib/mixin_bot/api/legacy_output.rb +50 -0
  13. data/lib/mixin_bot/api/legacy_payment.rb +31 -0
  14. data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
  15. data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
  16. data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
  17. data/lib/mixin_bot/api/me.rb +13 -17
  18. data/lib/mixin_bot/api/message.rb +13 -10
  19. data/lib/mixin_bot/api/multisig.rb +16 -221
  20. data/lib/mixin_bot/api/output.rb +46 -0
  21. data/lib/mixin_bot/api/payment.rb +9 -20
  22. data/lib/mixin_bot/api/pin.rb +57 -65
  23. data/lib/mixin_bot/api/rpc.rb +9 -11
  24. data/lib/mixin_bot/api/snapshot.rb +15 -29
  25. data/lib/mixin_bot/api/tip.rb +43 -0
  26. data/lib/mixin_bot/api/transaction.rb +184 -60
  27. data/lib/mixin_bot/api/transfer.rb +64 -32
  28. data/lib/mixin_bot/api/user.rb +83 -53
  29. data/lib/mixin_bot/api/withdraw.rb +52 -53
  30. data/lib/mixin_bot/api.rb +78 -45
  31. data/lib/mixin_bot/cli/api.rb +149 -5
  32. data/lib/mixin_bot/cli/utils.rb +14 -4
  33. data/lib/mixin_bot/cli.rb +13 -10
  34. data/lib/mixin_bot/client.rb +76 -127
  35. data/lib/mixin_bot/configuration.rb +98 -0
  36. data/lib/mixin_bot/nfo.rb +174 -0
  37. data/lib/mixin_bot/transaction.rb +505 -0
  38. data/lib/mixin_bot/utils/address.rb +108 -0
  39. data/lib/mixin_bot/utils/crypto.rb +182 -0
  40. data/lib/mixin_bot/utils/decoder.rb +58 -0
  41. data/lib/mixin_bot/utils/encoder.rb +63 -0
  42. data/lib/mixin_bot/utils.rb +8 -109
  43. data/lib/mixin_bot/uuid.rb +41 -0
  44. data/lib/mixin_bot/version.rb +1 -1
  45. data/lib/mixin_bot.rb +39 -14
  46. data/lib/mvm/bridge.rb +2 -19
  47. data/lib/mvm/client.rb +11 -33
  48. data/lib/mvm/nft.rb +4 -4
  49. data/lib/mvm/registry.rb +9 -9
  50. data/lib/mvm/scan.rb +3 -5
  51. data/lib/mvm.rb +5 -6
  52. metadata +101 -44
  53. data/lib/mixin_bot/utils/nfo.rb +0 -176
  54. data/lib/mixin_bot/utils/transaction.rb +0 -478
  55. data/lib/mixin_bot/utils/uuid.rb +0 -43
@@ -1,478 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MixinBot
4
- module Utils
5
- class Transaction
6
- DEAULT_VERSION = 2
7
- MAGIC = [0x77, 0x77]
8
- TX_VERSION = 2
9
- MAX_ENCODE_INT = 0xFFFF
10
- NULL_BYTES = [0x00, 0x00]
11
- AGGREGATED_SIGNATURE_PREFIX = 0xFF01
12
- AGGREGATED_SIGNATURE_ORDINAY_MASK = [0x00]
13
- AGGREGATED_SIGNATURE_SPARSE_MASK = [0x01]
14
-
15
- attr_accessor :version, :asset, :inputs, :outputs, :extra, :signatures, :aggregated, :hex, :hash
16
-
17
- def initialize(**kwargs)
18
- @version = kwargs[:version] || DEAULT_VERSION
19
- @asset = kwargs[:asset]
20
- @inputs = kwargs[:inputs]
21
- @outputs = kwargs[:outputs]
22
- @extra = kwargs[:extra]
23
- @hex = kwargs[:hex]
24
- @signatures = kwargs[:signatures]
25
- @aggregated = kwargs[:aggregated]
26
- end
27
-
28
- def encode
29
- raise InvalidTransactionFormatError, 'asset is required' if asset.blank?
30
- raise InvalidTransactionFormatError, 'inputs is required' if inputs.blank?
31
- raise InvalidTransactionFormatError, 'outputs is required' if outputs.blank?
32
-
33
- bytes = []
34
-
35
- # magic number
36
- bytes += MAGIC
37
-
38
- # version
39
- bytes += [0, version]
40
-
41
- # asset
42
- bytes += [asset].pack('H*').bytes
43
-
44
- # inputs
45
- bytes += encode_inputs
46
-
47
- # output
48
- bytes += encode_outputs
49
-
50
- # extra
51
- extra_bytes = [extra].pack('H*').bytes
52
- bytes += MixinBot::Utils.encode_int extra_bytes.size
53
- bytes += extra_bytes
54
-
55
- # aggregated
56
- if aggregated.nil?
57
- # signatures
58
- bytes += encode_signatures
59
- else
60
- bytes += encode_aggregated_signature
61
- end
62
-
63
- @hash = SHA3::Digest::SHA256.hexdigest bytes.pack('C*')
64
- @hex = bytes.pack('C*').unpack1('H*')
65
-
66
- self
67
- end
68
-
69
- def decode
70
- @bytes = [hex].pack('H*').bytes
71
- @hash = SHA3::Digest::SHA256.hexdigest @bytes.pack('C*')
72
-
73
- magic = @bytes.shift(2)
74
- raise ArgumentError, 'Not valid raw' unless magic == MAGIC
75
-
76
- version = @bytes.shift(2)
77
- @version = MixinBot::Utils.bytes_to_int version
78
-
79
- asset = @bytes.shift(32)
80
- @asset = asset.pack('C*').unpack1('H*')
81
-
82
- # read inputs
83
- decode_inputs
84
-
85
- # read outputs
86
- decode_outputs
87
-
88
- extra_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
89
- @extra = @bytes.shift(extra_size).pack('C*').unpack1('H*')
90
-
91
- num = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
92
- if num == MAX_ENCODE_INT
93
- # aggregated
94
- @aggregated = {}
95
-
96
- raise ArgumentError, 'invalid aggregated' unless @bytes.shift(2).reverse.pack('C*').unpack1('S*') == AGGREGATED_SIGNATURE_PREFIX
97
-
98
- @aggregated['signature'] = @bytes.shift(64).pack('C*').unpack1('H*')
99
-
100
- byte = @bytes.shift
101
- case byte
102
- when AGGREGATED_SIGNATURE_ORDINAY_MASK.first
103
- @aggregated['signers'] = []
104
- masks_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
105
- masks = @bytes.shift(masks_size)
106
- masks = [masks] unless masks.is_a? Array
107
-
108
- masks.each_with_index do |mask, i|
109
- 8.times do |j|
110
- k = 1 << j
111
- aggregated['signers'].push(i * 8 + j) if mask & k == k
112
- end
113
- end
114
- when AGGREGATED_SIGNATURE_SPARSE_MASK.first
115
- signers_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
116
- return if signers_size == 0
117
-
118
- aggregated['signers'] = []
119
- signers_size.times do
120
- aggregated['signers'].push @bytes.shift(2).reverse.pack('C*').unpack1('S*')
121
- end
122
- end
123
- else
124
- if !@bytes.empty? && @bytes[...2] != NULL_BYTES
125
- signatures_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
126
- @signatures = @bytes.shift(signatures_size).pack('C*').unpack1('H*')
127
- end
128
- end
129
-
130
- self
131
- end
132
-
133
- def to_h
134
- {
135
- version: version,
136
- asset: asset,
137
- inputs: inputs,
138
- outputs: outputs,
139
- extra: extra,
140
- signatures: signatures,
141
- aggregated: aggregated,
142
- hash: hash
143
- }.compact
144
- end
145
-
146
- private
147
-
148
- def encode_inputs
149
- bytes = []
150
-
151
- bytes += MixinBot::Utils.encode_int(inputs.size)
152
-
153
- inputs.each do |input|
154
- bytes += [input['hash']].pack('H*').bytes
155
- bytes += MixinBot::Utils.encode_int(input['index'])
156
-
157
- # genesis
158
- genesis = input['genesis'] || ''
159
- if genesis.empty?
160
- bytes += NULL_BYTES
161
- else
162
- genesis_bytes = [genesis].pack('H*').bytes
163
- bytes += MixinBot::Utils.encode_int genesis_bytes.size
164
- bytes += genesis_bytes
165
- end
166
-
167
- # deposit
168
- deposit = input['deposit']
169
- if deposit.nil?
170
- bytes += NULL_BYTES
171
- else
172
- bytes += MAGIC
173
- bytes += [deposit['chain']].pack('H*').bytes
174
-
175
- asset_bytes = [deposit['asset']].pack('H*')
176
- bytes += MixinBot::Utils.encode_int asset_bytes.size
177
- bytes += asset_bytes
178
-
179
- transaction_bytes = [deposit['transaction']].pack('H*')
180
- bytes += MixinBot::Utils.encode_int transaction_bytes.size
181
- bytes += transaction_bytes
182
-
183
- bytes += MixinBot::Utils.encode_unit_64 deposit['index']
184
-
185
- amount_bytes = MixinBot::Utils.bytes_of deposit['amount']
186
- bytes += MixinBot::Utils.encode_int amount_bytes.size
187
- bytes += amount_bytes
188
- end
189
-
190
- # mint
191
- mint = input['mint']
192
- if mint.nil?
193
- bytes += NULL_BYTES
194
- else
195
- bytes += MAGIC
196
-
197
- # group
198
- group = mint['group'] || ''
199
- if group.empty?
200
- bytes += MixinBot::Utils.encode_int NULL_BYTES
201
- else
202
- group_bytes = [group].pack('H*')
203
- bytes += MixinBot::Utils.encode_int group_bytes.size
204
- bytes += group_bytes
205
- end
206
-
207
- bytes += MixinBot::Utils.encode_unit_64 mint['batch']
208
-
209
- amount_bytes = MixinBot::Utils.bytes_of mint['amount']
210
- bytes += MixinBot::Utils.encode_int amount_bytes.size
211
- bytes += amount_bytes
212
- end
213
- end
214
-
215
- bytes
216
- end
217
-
218
- def encode_outputs
219
- bytes = []
220
-
221
- bytes += MixinBot::Utils.encode_int outputs.size
222
-
223
- outputs.each do |output|
224
- type = output['type'] || 0
225
- bytes += [0x00, type]
226
-
227
- # amount
228
- amount_bytes = MixinBot::Utils.bytes_of (output['amount'].to_d * 1e8).round
229
- bytes += MixinBot::Utils.encode_int amount_bytes.size
230
- bytes += amount_bytes
231
-
232
- # keys
233
- bytes += MixinBot::Utils.encode_int output['keys'].size
234
- output['keys'].each do |key|
235
- bytes += [key].pack('H*').bytes
236
- end
237
-
238
- # mask
239
- bytes += [output['mask']].pack('H*').bytes
240
-
241
- # script
242
- script_bytes = [output['script']].pack('H*').bytes
243
- bytes += MixinBot::Utils.encode_int script_bytes.size
244
- bytes += script_bytes
245
-
246
- # withdrawal
247
- withdrawal = output['withdrawal']
248
- if withdrawal.nil?
249
- bytes += NULL_BYTES
250
- else
251
- bytes += MAGIC
252
-
253
- # chain
254
- bytes += [withdrawal['chain']].pack('H*').bytes
255
-
256
- # asset
257
- @asset_bytes = [withdrawal['asset']].pack('H*')
258
- bytes += MixinBot::Utils.encode_int asset_bytes.size
259
- bytes += asset_bytes
260
-
261
- # address
262
- address = withdrawal['address'] || ''
263
- if address.empty?
264
- bytes += NULL_BYTES
265
- else
266
- address_bytes = [address].pack('H*').bytes
267
- bytes += MixinBot::Utils.encode_int address.size
268
- bytes += address_bytes
269
- end
270
-
271
- # tag
272
- tag = withdrawal['tag'] || ''
273
- if tag.empty?
274
- bytes += NULL_BYTES
275
- else
276
- address_bytes = [tag].pack('H*').bytes
277
- bytes += MixinBot::Utils.encode_int tag.size
278
- bytes += address_bytes
279
- end
280
- end
281
- end
282
-
283
- bytes
284
- end
285
-
286
- def encode_aggregated_signature
287
- bytes = []
288
-
289
- bytes += MixinBot::Utils.encode_int MAX_ENCODE_INT
290
- bytes += MixinBot::Utils.encode_int AGGREGATED_SIGNATURE_PREFIX
291
- bytes += [aggregated['signature']].pack('H*').bytes
292
-
293
- signers = aggregated['signers']
294
- if signers.size == 0
295
- bytes += AGGREGATED_SIGNATURE_ORDINAY_MASK
296
- bytes += NULL_BYTES
297
- else
298
- signers.each do |sig, i|
299
- raise ArgumentError, 'signers not sorted' if i > 0 && sig <= signers[i - 1]
300
- raise ArgumentError, 'signers not sorted' if sig > MAX_ENCODE_INT
301
- end
302
-
303
- max = signers.last
304
- if (((max / 8 | 0) + 1 | 0) > aggregated['signature'].size * 2)
305
- bytes += AGGREGATED_SIGNATURE_SPARSE_MASK
306
- bytes += MixinBot::Utils.encode_int aggregated['signers'].size
307
- signers.map(&->(signer) { bytes += MixinBot::Utils.encode_int(signer) })
308
- end
309
-
310
- masks_bytes = Array.new(max / 8 + 1, 0)
311
- signers.each do |signer|
312
- masks[signer/8] = masks[signer/8] ^ (1 << (signer % 8))
313
- end
314
- bytes += AGGREGATED_SIGNATURE_ORDINAY_MASK
315
- bytes += MixinBot::Utils.encode_int masks_bytes.size
316
- bytes += masks_bytes
317
- end
318
-
319
- bytes
320
- end
321
-
322
- def encode_signatures
323
- bytes = []
324
-
325
- sl =
326
- if signatures.is_a? Hash
327
- signatures.keys.size
328
- else
329
- 0
330
- end
331
-
332
- raise ArgumentError, 'signatures overflow' if sl == MAX_ENCODE_INT
333
- bytes += MixinBot::Utils.encode_int sl
334
-
335
- if sl > 0
336
- bytes += MixinBot::Utils.encode_int signatures.keys.size
337
- signatures.keys.sort.each do |key|
338
- bytes += MixinBot::Utils.encode_int key
339
- bytes += [signatures[key]].pack('H*').bytes
340
- end
341
- end
342
-
343
- bytes
344
- end
345
-
346
- def decode_inputs
347
- inputs_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
348
- @inputs = []
349
- inputs_size.times do
350
- input = {}
351
- hash = @bytes.shift(32)
352
- input['hash'] = hash.pack('C*').unpack1('H*')
353
-
354
- index = @bytes.shift(2)
355
- input['index'] = index.reverse.pack('C*').unpack1('S*')
356
-
357
- if @bytes[...2] != NULL_BYTES
358
- genesis_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
359
- genesis = @bytes.shift(genesis_size)
360
- input['genesis'] = genesis.pack('C*').unpack1('H*')
361
- else
362
- @bytes.shift 2
363
- end
364
-
365
- if @bytes[...2] != NULL_BYTES
366
- magic = @bytes.shift(2)
367
- raise ArgumentError, 'Not valid input' unless magic == MAGIC
368
-
369
- deposit = {}
370
- deposit['chain'] = @bytes.shift(32).pack('C*').unpack1('H*')
371
-
372
- asset_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
373
- deposit['asset'] = @bytes.shift(asset_size).unpack1('H*')
374
-
375
- transaction_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
376
- deposit['transaction'] = @bytes.shift(transaction_size).unpack1('H*')
377
-
378
- deposit['index'] = @bytes.shift(8).reverse.pack('C*').unpack1('Q*')
379
-
380
- amount_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
381
- deposit['amount'] = MixinBot::Utils.bytes_to_int @bytes.shift(amount_size)
382
-
383
- input['deposit'] = deposit
384
- else
385
- @bytes.shift 2
386
- end
387
-
388
- if @bytes[...2] != NULL_BYTES
389
- magic = @bytes.shift(2)
390
- raise ArgumentError, 'Not valid input' unless magic == MAGIC
391
-
392
- mint = {}
393
- if bytes[...2] != NULL_BYTES
394
- group_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
395
- mint['group'] = @bytes.shift(group_size).unpack1('H*')
396
- else
397
- @bytes.shift 2
398
- end
399
-
400
- mint['batch'] = @bytes.shift(8).reverse.pack('C*').unpack1('Q*')
401
- _amount_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
402
- mint['amount'] = MixinBot::Utils.bytes_to_int bytes.shift(_amount_size)
403
-
404
- input['mint'] = mint
405
- else
406
- @bytes.shift 2
407
- end
408
-
409
- @inputs.push input
410
- end
411
-
412
- self
413
- end
414
-
415
- def decode_outputs
416
- outputs_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
417
- @outputs = []
418
- outputs_size.times do
419
- output = {}
420
-
421
- @bytes.shift
422
- type = @bytes.shift
423
- output['type'] = type
424
-
425
- amount_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
426
- output['amount'] = format('%.8f', MixinBot::Utils.bytes_to_int(@bytes.shift(amount_size)).to_f / 1e8)
427
-
428
- output['keys'] = []
429
- keys_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
430
- keys_size.times do
431
- output['keys'].push @bytes.shift(32).pack('C*').unpack1('H*')
432
- end
433
-
434
- output['mask'] = @bytes.shift(32).pack('C*').unpack1('H*')
435
-
436
- script_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
437
- output['script'] = @bytes.shift(script_size).pack('C*').unpack1('H*')
438
-
439
- if @bytes[...2] != NULL_BYTES
440
- magic = @bytes.shift(2)
441
- raise ArgumentError, 'Not valid output' unless magic == MAGIC
442
-
443
- withdraw = {}
444
-
445
- output['chain'] = @bytes.shift(32).pack('C*').unpack1('H*')
446
-
447
- asset_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
448
- output['asset'] = @bytes.shift(asset_size).unpack1('H*')
449
-
450
- if @bytes[...2] != NULL_BYTES
451
- address = {}
452
-
453
- adderss_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
454
- output['adderss'] = @bytes.shift(adderss_size).pack('C*').unpack1('H*')
455
- else
456
- @bytes.shift 2
457
- end
458
-
459
- if @bytes[...2] != NULL_BYTES
460
- tag = {}
461
-
462
- tag_size = @bytes.shift(2).reverse.pack('C*').unpack1('S*')
463
- output['tag'] = @bytes.shift(tag_size).pack('C*').unpack1('H*')
464
- else
465
- @bytes.shift 2
466
- end
467
- else
468
- @bytes.shift 2
469
- end
470
-
471
- @outputs.push output
472
- end
473
-
474
- self
475
- end
476
- end
477
- end
478
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MixinBot
4
- module Utils
5
- class UUID
6
- attr_accessor :hex, :raw
7
-
8
- def initialize(**args)
9
- @hex = args[:hex]
10
- @raw = args[:raw]
11
-
12
- raise MixinBot::InvalidUuidFormatError if raw.present? && raw.size != 16
13
- raise MixinBot::InvalidUuidFormatError if hex.present? && hex.gsub('-', '').size != 32
14
- end
15
-
16
- def packed
17
- if raw.present?
18
- raw
19
- elsif hex.present?
20
- [hex.gsub('-', '')].pack('H*')
21
- end
22
- end
23
-
24
- def unpacked
25
- _hex =
26
- if hex.present?
27
- hex.gsub('-', '')
28
- elsif raw.present?
29
- _hex = raw.unpack1('H*')
30
- end
31
-
32
- format(
33
- '%<first>s-%<second>s-%<third>s-%<forth>s-%<fifth>s',
34
- first: _hex[0..7],
35
- second: _hex[8..11],
36
- third: _hex[12..15],
37
- forth: _hex[16..19],
38
- fifth: _hex[20..]
39
- )
40
- end
41
- end
42
- end
43
- end