mixin_bot 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
 
@@ -36,7 +35,8 @@ module MixinBot
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
 
@@ -103,6 +103,19 @@ module MixinBot
103
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
@@ -8,14 +8,12 @@ module MixinBot
8
8
 
9
9
  if key.match?(/\A[\h]{64,}\z/i)
10
10
  [key].pack('H*')
11
- elsif key.match?(/\A[a-zA-Z0-9\-\_=]{43,}\z/)
12
- Base64.urlsafe_decode64 key
13
11
  elsif key.match?(/^-----BEGIN RSA PRIVATE KEY-----/)
14
12
  key.gsub('\\r\\n', "\n").gsub("\r\n", "\n")
15
13
  elsif key.match?(/\d{6}/) || (key.size % 32).zero?
16
14
  key
17
15
  else
18
- raise ArgumentError, "Invalid key #{key}"
16
+ Base64.urlsafe_decode64 key
19
17
  end
20
18
  end
21
19
 
@@ -23,19 +21,19 @@ module MixinBot
23
21
  MixinBot::Transaction.new(hex:).decode.to_h
24
22
  end
25
23
 
26
- def decode_uint_16(bytes)
24
+ def decode_uint16(bytes)
27
25
  raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
28
26
 
29
27
  bytes.reverse.pack('C*').unpack1('S*')
30
28
  end
31
29
 
32
- def decode_uint_32(bytes)
30
+ def decode_uint32(bytes)
33
31
  raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
34
32
 
35
33
  bytes.reverse.pack('C*').unpack1('L*')
36
34
  end
37
35
 
38
- def decode_uint_64(bytes)
36
+ def decode_uint64(bytes)
39
37
  raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
40
38
 
41
39
  bytes.reverse.pack('C*').unpack1('Q*')
@@ -3,35 +3,35 @@
3
3
  module MixinBot
4
4
  module Utils
5
5
  module Encoder
6
- def encode_raw_transaction(tx)
7
- if tx.is_a? String
6
+ def encode_raw_transaction(txn)
7
+ if txn.is_a? String
8
8
  begin
9
- tx = JSON.parse tx
9
+ txn = JSON.parse txn
10
10
  rescue JSON::ParserError
11
- tx
11
+ txn
12
12
  end
13
13
  end
14
14
 
15
- raise ArgumentError, "#{tx} is not a valid json" unless tx.is_a? Hash
15
+ raise ArgumentError, "#{txn} is not a valid json" unless txn.is_a? Hash
16
16
 
17
- tx = tx.with_indifferent_access
17
+ txn = txn.with_indifferent_access
18
18
 
19
- MixinBot::Transaction.new(**tx).encode.hex
19
+ MixinBot::Transaction.new(**txn).encode.hex
20
20
  end
21
21
 
22
- def encode_uint_16(int)
22
+ def encode_uint16(int)
23
23
  raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
24
24
 
25
25
  [int].pack('S*').bytes.reverse
26
26
  end
27
27
 
28
- def encode_uint_32(int)
28
+ def encode_uint32(int)
29
29
  raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
30
30
 
31
31
  [int].pack('L*').bytes.reverse
32
32
  end
33
33
 
34
- def encode_uint_64(int)
34
+ def encode_uint64(int)
35
35
  raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
36
36
 
37
37
  [int].pack('Q*').bytes.reverse
@@ -42,7 +42,7 @@ module MixinBot
42
42
 
43
43
  bytes = []
44
44
  loop do
45
- break if int === 0
45
+ break if int.zero?
46
46
 
47
47
  bytes.push int & 255
48
48
  int = (int / (2**8)) | 0
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MixinBot
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/mvm/client.rb CHANGED
@@ -17,12 +17,12 @@ module MVM
17
17
  end
18
18
  end
19
19
 
20
- def get(path, **options)
21
- @conn.get(path, **options).body
20
+ def get(path, **)
21
+ @conn.get(path, **).body
22
22
  end
23
23
 
24
- def post(path, **options)
25
- @conn.post(path, **options).body
24
+ def post(path, **)
25
+ @conn.post(path, **).body
26
26
  end
27
27
  end
28
28
  end