mixin_bot 1.0.0 → 1.2.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.
data/lib/mixin_bot/api.rb CHANGED
@@ -8,9 +8,10 @@ require_relative 'api/asset'
8
8
  require_relative 'api/attachment'
9
9
  require_relative 'api/auth'
10
10
  require_relative 'api/blaze'
11
- require_relative 'api/collectible'
12
11
  require_relative 'api/conversation'
13
12
  require_relative 'api/encrypted_message'
13
+ require_relative 'api/inscription'
14
+ require_relative 'api/legacy_collectible'
14
15
  require_relative 'api/legacy_multisig'
15
16
  require_relative 'api/legacy_output'
16
17
  require_relative 'api/legacy_payment'
@@ -67,8 +68,8 @@ module MixinBot
67
68
  )
68
69
  end
69
70
 
70
- def encode_raw_transaction(tx)
71
- utils.encode_raw_transaction tx
71
+ def encode_raw_transaction(txn)
72
+ utils.encode_raw_transaction txn
72
73
  end
73
74
 
74
75
  def decode_raw_transaction(raw)
@@ -107,9 +108,10 @@ module MixinBot
107
108
  include MixinBot::API::Attachment
108
109
  include MixinBot::API::Auth
109
110
  include MixinBot::API::Blaze
110
- include MixinBot::API::Collectible
111
111
  include MixinBot::API::Conversation
112
112
  include MixinBot::API::EncryptedMessage
113
+ include MixinBot::API::Inscription
114
+ include MixinBot::API::LegacyCollectible
113
115
  include MixinBot::API::LegacyMultisig
114
116
  include MixinBot::API::LegacyOutput
115
117
  include MixinBot::API::LegacyPayment
@@ -78,7 +78,7 @@ module MixinBot
78
78
  return result
79
79
  end
80
80
 
81
- errmsg = "#{verb.upcase}|#{path}|#{body}, errcode: #{result['error']['code']}, errmsg: #{result['error']['description']}, request_id: #{response&.[]('X-Request-Id')}, server_time: #{response&.[]('X-Server-Time')}'"
81
+ errmsg = "#{verb.upcase} | #{path} | #{body}, errcode: #{result['error']['code']}, errmsg: #{result['error']['description']}, request_id: #{response&.[]('X-Request-Id')}, server_time: #{response&.[]('X-Server-Time')}'"
82
82
 
83
83
  case result['error']['code']
84
84
  when 401, 20121
@@ -87,8 +87,6 @@ module MixinBot
87
87
  raise ForbiddenError, errmsg
88
88
  when 404
89
89
  raise NotFoundError, errmsg
90
- when 400, 10006, 20133, 500, 7000, 7001
91
- raise ResponseError, errmsg
92
90
  when 20117
93
91
  raise InsufficientBalanceError, errmsg
94
92
  when 20118, 20119
data/lib/mixin_bot/nfo.rb CHANGED
@@ -74,7 +74,7 @@ module MixinBot
74
74
  bytes += [0]
75
75
  else
76
76
  bytes += [1]
77
- bytes += MixinBot.utils.encode_uint_64 mask
77
+ bytes += MixinBot.utils.encode_uint64 mask
78
78
  bytes += MixinBot::UUID.new(hex: chain).packed.bytes
79
79
 
80
80
  class_bytes = [nm_class].pack('H*').bytes
@@ -51,13 +51,13 @@ module MixinBot
51
51
  bytes += encode_outputs
52
52
 
53
53
  # placeholder for `references`
54
- bytes += NULL_BYTES if version >= REFERENCES_TX_VERSION
54
+ bytes += encode_references if version >= REFERENCES_TX_VERSION
55
55
 
56
56
  # extra
57
57
  extra_bytes = extra.bytes
58
58
  raise InvalidTransactionFormatError, 'extra is too long' if extra_bytes.size > MAX_EXTRA_SIZE
59
59
 
60
- bytes += MixinBot.utils.encode_uint_32 extra_bytes.size
60
+ bytes += MixinBot.utils.encode_uint32 extra_bytes.size
61
61
  bytes += extra_bytes
62
62
 
63
63
  # aggregated
@@ -93,24 +93,20 @@ module MixinBot
93
93
  # read outputs
94
94
  decode_outputs
95
95
 
96
- # TODO:
97
96
  # read references
98
- if version >= REFERENCES_TX_VERSION
99
- references_size = @bytes.shift 2
100
- raise ArgumentError, 'Not support references yet' unless references_size == NULL_BYTES
101
- end
97
+ decode_references if version >= REFERENCES_TX_VERSION
102
98
 
103
99
  # read extra
104
100
  # unsigned 32 endian for extra size
105
- extra_size = MixinBot.utils.decode_uint_32 @bytes.shift(4)
101
+ extra_size = MixinBot.utils.decode_uint32 @bytes.shift(4)
106
102
  @extra = @bytes.shift(extra_size).pack('C*')
107
103
 
108
- num = MixinBot.utils.decode_uint_16 @bytes.shift(2)
104
+ num = MixinBot.utils.decode_uint16 @bytes.shift(2)
109
105
  if num == MAX_ENCODE_INT
110
106
  # aggregated
111
107
  @aggregated = {}
112
108
 
113
- raise ArgumentError, 'invalid aggregated' unless MixinBot.utils.decode_uint_16(@bytes.shift(2)) == AGGREGATED_SIGNATURE_PREFIX
109
+ raise ArgumentError, 'invalid aggregated' unless MixinBot.utils.decode_uint16(@bytes.shift(2)) == AGGREGATED_SIGNATURE_PREFIX
114
110
 
115
111
  @aggregated['signature'] = @bytes.shift(64).pack('C*').unpack1('H*')
116
112
 
@@ -118,7 +114,7 @@ module MixinBot
118
114
  case byte
119
115
  when AGGREGATED_SIGNATURE_ORDINAY_MASK.first
120
116
  @aggregated['signers'] = []
121
- masks_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
117
+ masks_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
122
118
  masks = @bytes.shift(masks_size)
123
119
  masks = Array(masks)
124
120
 
@@ -129,12 +125,12 @@ module MixinBot
129
125
  end
130
126
  end
131
127
  when AGGREGATED_SIGNATURE_SPARSE_MASK.first
132
- signers_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
128
+ signers_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
133
129
  return if signers_size.zero?
134
130
 
135
131
  aggregated['signers'] = []
136
132
  signers_size.times do
137
- aggregated['signers'].push MixinBot.utils.decode_uint_16(@bytes.shift(2))
133
+ aggregated['signers'].push MixinBot.utils.decode_uint16(@bytes.shift(2))
138
134
  end
139
135
  end
140
136
  elsif num.present? && num.positive? && @bytes.size.positive?
@@ -142,10 +138,10 @@ module MixinBot
142
138
  num.times do
143
139
  signature = {}
144
140
 
145
- keys_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
141
+ keys_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
146
142
 
147
143
  keys_size.times do
148
- index = MixinBot.utils.decode_uint_16 @bytes.shift(2)
144
+ index = MixinBot.utils.decode_uint16 @bytes.shift(2)
149
145
  signature[index] = @bytes.shift(64).pack('C*').unpack1('H*')
150
146
  end
151
147
 
@@ -175,11 +171,11 @@ module MixinBot
175
171
  def encode_inputs
176
172
  bytes = []
177
173
 
178
- bytes += MixinBot.utils.encode_uint_16(inputs.size)
174
+ bytes += MixinBot.utils.encode_uint16(inputs.size)
179
175
 
180
176
  inputs.each do |input|
181
177
  bytes += [input['hash']].pack('H*').bytes
182
- bytes += MixinBot.utils.encode_uint_16(input['index'])
178
+ bytes += MixinBot.utils.encode_uint16(input['index'])
183
179
 
184
180
  # genesis
185
181
  genesis = input['genesis'] || ''
@@ -187,7 +183,7 @@ module MixinBot
187
183
  bytes += NULL_BYTES
188
184
  else
189
185
  genesis_bytes = [genesis].pack('H*').bytes
190
- bytes += MixinBot.utils.encode_uint_16 genesis_bytes.size
186
+ bytes += MixinBot.utils.encode_uint16 genesis_bytes.size
191
187
  bytes += genesis_bytes
192
188
  end
193
189
 
@@ -200,17 +196,17 @@ module MixinBot
200
196
  bytes += [deposit['chain']].pack('H*').bytes
201
197
 
202
198
  asset_bytes = [deposit['asset']].pack('H*')
203
- bytes += MixinBot.utils.encode_uint_16 asset_bytes.size
199
+ bytes += MixinBot.utils.encode_uint16 asset_bytes.size
204
200
  bytes += asset_bytes
205
201
 
206
202
  transaction_bytes = [deposit['transaction']].pack('H*')
207
- bytes += MixinBot.utils.encode_uint_16 transaction_bytes.size
203
+ bytes += MixinBot.utils.encode_uint16 transaction_bytes.size
208
204
  bytes += transaction_bytes
209
205
 
210
- bytes += MixinBot.utils.encode_uint_64 deposit['index']
206
+ bytes += MixinBot.utils.encode_uint64 deposit['index']
211
207
 
212
208
  amount_bytes = MixinBot.utils.bytes_of deposit['amount']
213
- bytes += MixinBot.utils.encode_uint_16 amount_bytes.size
209
+ bytes += MixinBot.utils.encode_uint16 amount_bytes.size
214
210
  bytes += amount_bytes
215
211
  end
216
212
 
@@ -224,17 +220,17 @@ module MixinBot
224
220
  # group
225
221
  group = mint['group'] || ''
226
222
  if group.empty?
227
- bytes += MixinBot.utils.encode_uint_16 NULL_BYTES
223
+ bytes += MixinBot.utils.encode_uint16 NULL_BYTES
228
224
  else
229
225
  group_bytes = [group].pack('H*')
230
- bytes += MixinBot.utils.encode_uint_16 group_bytes.size
226
+ bytes += MixinBot.utils.encode_uint16 group_bytes.size
231
227
  bytes += group_bytes
232
228
  end
233
229
 
234
- bytes += MixinBot.utils.encode_uint_64 mint['batch']
230
+ bytes += MixinBot.utils.encode_uint64 mint['batch']
235
231
 
236
232
  amount_bytes = MixinBot.utils.encode_int mint['amount']
237
- bytes += MixinBot.utils.encode_uint_16 amount_bytes.size
233
+ bytes += MixinBot.utils.encode_uint16 amount_bytes.size
238
234
  bytes += amount_bytes
239
235
  end
240
236
  end
@@ -245,7 +241,7 @@ module MixinBot
245
241
  def encode_outputs
246
242
  bytes = []
247
243
 
248
- bytes += MixinBot.utils.encode_uint_16 outputs.size
244
+ bytes += MixinBot.utils.encode_uint16 outputs.size
249
245
 
250
246
  outputs.each do |output|
251
247
  type = output['type'] || 0
@@ -253,11 +249,11 @@ module MixinBot
253
249
 
254
250
  # amount
255
251
  amount_bytes = MixinBot.utils.encode_int (output['amount'].to_d * 1e8).round
256
- bytes += MixinBot.utils.encode_uint_16 amount_bytes.size
252
+ bytes += MixinBot.utils.encode_uint16 amount_bytes.size
257
253
  bytes += amount_bytes
258
254
 
259
255
  # keys
260
- bytes += MixinBot.utils.encode_uint_16 output['keys'].size
256
+ bytes += MixinBot.utils.encode_uint16 output['keys'].size
261
257
  output['keys'].each do |key|
262
258
  bytes += [key].pack('H*').bytes
263
259
  end
@@ -267,7 +263,7 @@ module MixinBot
267
263
 
268
264
  # script
269
265
  script_bytes = [output['script']].pack('H*').bytes
270
- bytes += MixinBot.utils.encode_uint_16 script_bytes.size
266
+ bytes += MixinBot.utils.encode_uint16 script_bytes.size
271
267
  bytes += script_bytes
272
268
 
273
269
  # withdrawal
@@ -282,7 +278,7 @@ module MixinBot
282
278
 
283
279
  # asset
284
280
  @asset_bytes = [withdrawal['asset']].pack('H*')
285
- bytes += MixinBot.utils.encode_uint_16 asset_bytes.size
281
+ bytes += MixinBot.utils.encode_uint16 asset_bytes.size
286
282
  bytes += asset_bytes
287
283
 
288
284
  # address
@@ -291,7 +287,7 @@ module MixinBot
291
287
  bytes += NULL_BYTES
292
288
  else
293
289
  address_bytes = [address].pack('H*').bytes
294
- bytes += MixinBot.utils.encode_uint_16 address.size
290
+ bytes += MixinBot.utils.encode_uint16 address.size
295
291
  bytes += address_bytes
296
292
  end
297
293
 
@@ -301,7 +297,7 @@ module MixinBot
301
297
  bytes += NULL_BYTES
302
298
  else
303
299
  address_bytes = [tag].pack('H*').bytes
304
- bytes += MixinBot.utils.encode_uint_16 tag.size
300
+ bytes += MixinBot.utils.encode_uint16 tag.size
305
301
  bytes += address_bytes
306
302
  end
307
303
  end
@@ -310,11 +306,23 @@ module MixinBot
310
306
  bytes
311
307
  end
312
308
 
309
+ def encode_references
310
+ bytes = []
311
+
312
+ bytes += MixinBot.utils.encode_uint16 references.size
313
+
314
+ references.each do |reference|
315
+ bytes += [reference].pack('H*').bytes
316
+ end
317
+
318
+ bytes
319
+ end
320
+
313
321
  def encode_aggregated_signature
314
322
  bytes = []
315
323
 
316
- bytes += MixinBot.utils.encode_uint_16 MAX_ENCODE_INT
317
- bytes += MixinBot.utils.encode_uint_16 AGGREGATED_SIGNATURE_PREFIX
324
+ bytes += MixinBot.utils.encode_uint16 MAX_ENCODE_INT
325
+ bytes += MixinBot.utils.encode_uint16 AGGREGATED_SIGNATURE_PREFIX
318
326
  bytes += [aggregated['signature']].pack('H*').bytes
319
327
 
320
328
  signers = aggregated['signers']
@@ -330,8 +338,8 @@ module MixinBot
330
338
  max = signers.last
331
339
  if ((((max / 8) | 0) + 1) | 0) > aggregated['signature'].size * 2
332
340
  bytes += AGGREGATED_SIGNATURE_SPARSE_MASK
333
- bytes += MixinBot.utils.encode_uint_16 aggregated['signers'].size
334
- signers.map(&->(signer) { bytes += MixinBot.utils.encode_uint_16(signer) })
341
+ bytes += MixinBot.utils.encode_uint16 aggregated['signers'].size
342
+ signers.map(&->(signer) { bytes += MixinBot.utils.encode_uint16(signer) })
335
343
  end
336
344
 
337
345
  masks_bytes = Array.new((max / 8) + 1, 0)
@@ -339,7 +347,7 @@ module MixinBot
339
347
  masks[signer / 8] = masks[signer / 8] ^ (1 << (signer % 8))
340
348
  end
341
349
  bytes += AGGREGATED_SIGNATURE_ORDINAY_MASK
342
- bytes += MixinBot.utils.encode_uint_16 masks_bytes.size
350
+ bytes += MixinBot.utils.encode_uint16 masks_bytes.size
343
351
  bytes += masks_bytes
344
352
  end
345
353
 
@@ -358,15 +366,15 @@ module MixinBot
358
366
 
359
367
  raise ArgumentError, 'signatures overflow' if sl == MAX_ENCODE_INT
360
368
 
361
- bytes += MixinBot.utils.encode_uint_16 sl
369
+ bytes += MixinBot.utils.encode_uint16 sl
362
370
 
363
371
  if sl.positive?
364
372
  signatures.each do |signature|
365
- bytes += MixinBot.utils.encode_uint_16 signature.keys.size
373
+ bytes += MixinBot.utils.encode_uint16 signature.keys.size
366
374
 
367
375
  signature.keys.sort.each do |key|
368
376
  signature_bytes = [signature[key]].pack('H*').bytes
369
- bytes += MixinBot.utils.encode_uint_16 key
377
+ bytes += MixinBot.utils.encode_uint16 key
370
378
  bytes += signature_bytes
371
379
  end
372
380
  end
@@ -376,7 +384,7 @@ module MixinBot
376
384
  end
377
385
 
378
386
  def decode_inputs
379
- inputs_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
387
+ inputs_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
380
388
  @inputs = []
381
389
  inputs_size.times do
382
390
  input = {}
@@ -384,12 +392,12 @@ module MixinBot
384
392
  input['hash'] = hash.pack('C*').unpack1('H*')
385
393
 
386
394
  index = @bytes.shift(2)
387
- input['index'] = MixinBot.utils.decode_uint_16 index
395
+ input['index'] = MixinBot.utils.decode_uint16 index
388
396
 
389
397
  if @bytes[...2] == NULL_BYTES
390
398
  @bytes.shift 2
391
399
  else
392
- genesis_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
400
+ genesis_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
393
401
  genesis = @bytes.shift genesis_size
394
402
  input['genesis'] = genesis.pack('C*').unpack1('H*')
395
403
  end
@@ -403,15 +411,15 @@ module MixinBot
403
411
  deposit = {}
404
412
  deposit['chain'] = @bytes.shift(32).pack('C*').unpack1('H*')
405
413
 
406
- asset_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
414
+ asset_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
407
415
  deposit['asset'] = @bytes.shift(asset_size).unpack1('H*')
408
416
 
409
- transaction_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
417
+ transaction_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
410
418
  deposit['transaction'] = @bytes.shift(transaction_size).unpack1('H*')
411
419
 
412
- deposit['index'] = MixinBot.utils.decode_uint_64 @bytes.shift(8)
420
+ deposit['index'] = MixinBot.utils.decode_uint64 @bytes.shift(8)
413
421
 
414
- amount_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
422
+ amount_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
415
423
  deposit['amount'] = MixinBot.utils.decode_int @bytes.shift(amount_size)
416
424
 
417
425
  input['deposit'] = deposit
@@ -427,12 +435,12 @@ module MixinBot
427
435
  if bytes[...2] == NULL_BYTES
428
436
  @bytes.shift 2
429
437
  else
430
- group_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
438
+ group_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
431
439
  mint['group'] = @bytes.shift(group_size).unpack1('H*')
432
440
  end
433
441
 
434
- mint['batch'] = MixinBot.utils.decode_uint_64 @bytes.shift(8)
435
- _amount_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
442
+ mint['batch'] = MixinBot.utils.decode_uint64 @bytes.shift(8)
443
+ _amount_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
436
444
  mint['amount'] = MixinBot.utils.decode_int bytes.shift(_amount_size)
437
445
 
438
446
  input['mint'] = mint
@@ -445,7 +453,7 @@ module MixinBot
445
453
  end
446
454
 
447
455
  def decode_outputs
448
- outputs_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
456
+ outputs_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
449
457
  @outputs = []
450
458
  outputs_size.times do
451
459
  output = {}
@@ -454,18 +462,18 @@ module MixinBot
454
462
  type = @bytes.shift
455
463
  output['type'] = type
456
464
 
457
- amount_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
465
+ amount_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
458
466
  output['amount'] = format('%.8f', MixinBot.utils.decode_int(@bytes.shift(amount_size)).to_f / 1e8).gsub(/\.?0+$/, '')
459
467
 
460
468
  output['keys'] = []
461
- keys_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
469
+ keys_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
462
470
  keys_size.times do
463
471
  output['keys'].push @bytes.shift(32).pack('C*').unpack1('H*')
464
472
  end
465
473
 
466
474
  output['mask'] = @bytes.shift(32).pack('C*').unpack1('H*')
467
475
 
468
- script_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
476
+ script_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
469
477
  output['script'] = @bytes.shift(script_size).pack('C*').unpack1('H*')
470
478
 
471
479
  if @bytes[...2] == NULL_BYTES
@@ -476,14 +484,14 @@ module MixinBot
476
484
 
477
485
  output['chain'] = @bytes.shift(32).pack('C*').unpack1('H*')
478
486
 
479
- asset_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
487
+ asset_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
480
488
  output['asset'] = @bytes.shift(asset_size).unpack1('H*')
481
489
 
482
490
  if @bytes[...2] == NULL_BYTES
483
491
  @bytes.shift 2
484
492
  else
485
493
 
486
- adderss_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
494
+ adderss_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
487
495
  output['adderss'] = @bytes.shift(adderss_size).pack('C*').unpack1('H*')
488
496
  end
489
497
 
@@ -491,7 +499,7 @@ module MixinBot
491
499
  @bytes.shift 2
492
500
  else
493
501
 
494
- tag_size = MixinBot.utils.decode_uint_16 @bytes.shift(2)
502
+ tag_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
495
503
  output['tag'] = @bytes.shift(tag_size).pack('C*').unpack1('H*')
496
504
  end
497
505
  end
@@ -501,5 +509,16 @@ module MixinBot
501
509
 
502
510
  self
503
511
  end
512
+
513
+ def decode_references
514
+ references_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
515
+ @references = []
516
+
517
+ references_size.times do
518
+ @references.push @bytes.shift(32).pack('C*').unpack1('H*')
519
+ end
520
+
521
+ self
522
+ end
504
523
  end
505
524
  end
@@ -20,7 +20,6 @@ module MixinBot
20
20
 
21
21
  data = address[MAIN_ADDRESS_PREFIX.length..]
22
22
  data = Base58.base58_to_binary data, :bitcoin
23
- raise ArgumentError, 'invalid address' unless data.length == 68
24
23
 
25
24
  payload = data[...-4]
26
25
 
@@ -32,11 +31,12 @@ module MixinBot
32
31
  payload
33
32
  end
34
33
 
35
- def build_mix_address(members, threshold)
34
+ def build_mix_address(members:, threshold:)
36
35
  raise ArgumentError, 'members should be an array' unless members.is_a? Array
37
36
  raise ArgumentError, 'members should not be empty' if members.empty?
38
37
  raise ArgumentError, 'members length should less than 256' if members.length > 255
39
- raise ArgumentError, "invalid threshold: #{threshold}" if threshold > members.length
38
+
39
+ # raise ArgumentError, "invalid threshold: #{threshold}" if threshold > members.length
40
40
 
41
41
  prefix = [MIX_ADDRESS_VERSION].pack('C*') + [threshold].pack('C*') + [members.length].pack('C*')
42
42
 
@@ -61,24 +61,24 @@ module MixinBot
61
61
 
62
62
  data = address[MIX_ADDRESS_PREFIX.length..]
63
63
  data = Base58.base58_to_binary data, :bitcoin
64
- raise ArgumentError, 'invalid address' if data.length < 3 + 16 + 4
64
+ raise ArgumentError, 'invalid address, length invalid' if data.length < 3 + 16 + 4
65
65
 
66
66
  msg = data[...-4]
67
67
  checksum = SHA3::Digest::SHA256.digest((MIX_ADDRESS_PREFIX + msg))[0...4]
68
68
 
69
- raise ArgumentError, 'invalid address' unless checksum[0...4] == data[-4..]
69
+ raise ArgumentError, 'invalid address, checksum invalid' unless checksum[0...4] == data[-4..]
70
70
 
71
71
  version = data[0].ord
72
- raise ArgumentError, 'invalid address' unless version == MIX_ADDRESS_VERSION
72
+ raise ArgumentError, 'invalid address, version invalid' unless version == MIX_ADDRESS_VERSION
73
73
 
74
74
  threshold = data[1].ord
75
75
  members_count = data[2].ord
76
76
 
77
- if data[3..].length == members_count * 16
78
- members = data[3..].scan(/.{16}/)
77
+ if data[3...-4].length == members_count * 16
78
+ members = data[3...-4].chars.each_slice(16).map(&:join)
79
79
  members = members.map(&->(member) { MixinBot::UUID.new(raw: member).unpacked })
80
80
  else
81
- members = data[3..].scan(/.{64}/)
81
+ members = data[3...-4].chars.each_slice(64).map(&:join)
82
82
  members = members.map(&->(member) { build_main_address(member) })
83
83
  end
84
84
 
@@ -100,9 +100,22 @@ module MixinBot
100
100
  members:,
101
101
  threshold:,
102
102
  amount:,
103
- mix_address: build_mix_address(members, threshold)
103
+ mix_address: build_mix_address(members:, threshold:)
104
104
  }
105
105
  end
106
+
107
+ def burning_address
108
+ seed = "\0" * 64
109
+
110
+ digest1 = SHA3::Digest::SHA256.digest seed
111
+ digest2 = SHA3::Digest::SHA256.digest digest1
112
+ src = digest1 + digest2
113
+
114
+ spend_key = MixinBot::Utils.shared_public_key(seed)
115
+ view_key = MixinBot::Utils.shared_public_key(src)
116
+
117
+ MixinBot::Utils.build_main_address spend_key + view_key
118
+ end
106
119
  end
107
120
  end
108
121
  end
@@ -53,49 +53,45 @@ module MixinBot
53
53
  }
54
54
  end
55
55
 
56
- def generate_public_key(key)
57
- point = JOSE::JWA::FieldElement.new(
58
- OpenSSL::BN.new(key.reverse, 2),
56
+ def shared_public_key(key)
57
+ (JOSE::JWA::Edwards25519Point.stdbase * scalar_from_bytes(key[...64]).x.to_i).encode
58
+ end
59
+
60
+ def scalar_from_bytes(raw)
61
+ JOSE::JWA::FieldElement.new(
62
+ # https://github.com/potatosalad/ruby-jose/blob/e1be589b889f1e59ac233a5d19a3fa13f1e4b8a0/lib/jose/jwa/x25519.rb#L122C14-L122C48
63
+ OpenSSL::BN.new(raw.reverse, 2),
59
64
  JOSE::JWA::Edwards25519Point::L
60
65
  )
61
-
62
- (JOSE::JWA::Edwards25519Point.stdbase * point.x.to_i).encode
63
66
  end
64
67
 
65
68
  def sign(msg, key:)
66
69
  msg = Digest::Blake3.digest msg
67
70
 
68
- pub = generate_public_key key
71
+ pub = shared_public_key key
69
72
 
70
- y_point = JOSE::JWA::FieldElement.new(
71
- OpenSSL::BN.new(key.reverse, 2),
72
- JOSE::JWA::Edwards25519Point::L
73
- )
73
+ y_scalar = scalar_from_bytes key
74
74
 
75
75
  key_digest = Digest::SHA512.digest key
76
76
  msg_digest = Digest::SHA512.digest(key_digest[-32...] + msg)
77
77
 
78
- z_point = JOSE::JWA::FieldElement.new(
79
- OpenSSL::BN.new(msg_digest[...64].reverse, 2),
80
- JOSE::JWA::Edwards25519Point::L
81
- )
78
+ z_scalar = scalar_from_bytes msg_digest[...64]
79
+
80
+ r_point = JOSE::JWA::Edwards25519Point.stdbase * z_scalar.x.to_i
82
81
 
83
- r_point = JOSE::JWA::Edwards25519Point.stdbase * z_point.x.to_i
84
82
  hram_digest = Digest::SHA512.digest(r_point.encode + pub + msg)
85
83
 
86
- x_point = JOSE::JWA::FieldElement.new(
87
- OpenSSL::BN.new(hram_digest[...64].reverse, 2),
88
- JOSE::JWA::Edwards25519Point::L
89
- )
90
- s_point = (x_point * y_point) + z_point
84
+ x_scalar = scalar_from_bytes hram_digest[...64]
85
+
86
+ s_scalar = (x_scalar * y_scalar) + z_scalar
91
87
 
92
- r_point.encode + s_point.to_bytes(36)
88
+ r_point.encode + s_scalar.to_bytes(36)
93
89
  end
94
90
 
95
- def generate_unique_uuid(uuid_1, uuid_2)
91
+ def generate_unique_uuid(uuid1, uuid2)
96
92
  md5 = Digest::MD5.new
97
- md5 << [uuid_1, uuid_2].min
98
- md5 << [uuid_1, uuid_2].max
93
+ md5 << [uuid1, uuid2].min
94
+ md5 << [uuid1, uuid2].max
99
95
  digest = md5.digest
100
96
  digest6 = ((digest[6].ord & 0x0f) | 0x30).chr
101
97
  digest8 = ((digest[8].ord & 0x3f) | 0x80).chr
@@ -175,7 +171,47 @@ module MixinBot
175
171
  def tip_public_key(key, counter: 0)
176
172
  raise ArgumentError, 'invalid key' if key.size < 32
177
173
 
178
- (key[0...32].bytes + MixinBot::Utils.encode_uint_64(counter + 1)).pack('c*').unpack1('H*')
174
+ (key[0...32].bytes + MixinBot::Utils.encode_uint64(counter + 1)).pack('c*').unpack1('H*')
175
+ end
176
+
177
+ def hash_scalar(pkey, output_index)
178
+ tmp = [output_index].pack('Q<').reverse
179
+
180
+ hash1 = Digest::Blake3.digest(pkey + tmp)
181
+ hash2 = Digest::Blake3.digest hash1
182
+
183
+ hash3 = Digest::Blake3.digest(hash1 + hash2)
184
+ hash4 = Digest::Blake3.digest hash3
185
+
186
+ hash3 + hash4
187
+ end
188
+
189
+ def multiply_keys(public_key:, private_key:)
190
+ public_point = JOSE::JWA::Edwards25519Point.stdbase.decode public_key
191
+ private_scalar = scalar_from_bytes private_key
192
+ (public_point * private_scalar.x.to_i).encode
193
+ end
194
+
195
+ def derive_ghost_public_key(private_key, view_key, spend_key, index)
196
+ mult_value = multiply_keys(public_key: view_key, private_key:)
197
+
198
+ x = hash_scalar mult_value, index
199
+
200
+ p1 = JOSE::JWA::Edwards25519Point.stdbase.decode spend_key
201
+ p2 = JOSE::JWA::Edwards25519Point.stdbase * scalar_from_bytes(x).x.to_i
202
+
203
+ (p1 + p2).encode
204
+ end
205
+
206
+ def derive_ghost_private_key(public_key, view_key, spend_key, index)
207
+ mult_value = multiply_keys(public_key:, private_key: view_key)
208
+
209
+ x = hash_scalar mult_value, index
210
+
211
+ x_scalar = scalar_from_bytes x
212
+ y_scalar = scalar_from_bytes spend_key
213
+
214
+ (x_scalar + y_scalar).to_bytes(36)
179
215
  end
180
216
  end
181
217
  end