mixin_bot 0.12.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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