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.
- checksums.yaml +4 -4
- data/lib/mixin_bot/api/auth.rb +5 -5
- data/lib/mixin_bot/api/encrypted_message.rb +2 -2
- data/lib/mixin_bot/api/inscription.rb +71 -0
- data/lib/mixin_bot/api/{collectible.rb → legacy_collectible.rb} +4 -4
- data/lib/mixin_bot/api/legacy_multisig.rb +1 -1
- data/lib/mixin_bot/api/legacy_output.rb +3 -3
- data/lib/mixin_bot/api/multisig.rb +5 -5
- data/lib/mixin_bot/api/output.rb +3 -1
- data/lib/mixin_bot/api/pin.rb +1 -1
- data/lib/mixin_bot/api/rpc.rb +5 -5
- data/lib/mixin_bot/api/transaction.rb +134 -23
- data/lib/mixin_bot/api/transfer.rb +16 -10
- data/lib/mixin_bot/api/user.rb +8 -3
- data/lib/mixin_bot/api.rb +6 -4
- data/lib/mixin_bot/client.rb +1 -3
- data/lib/mixin_bot/nfo.rb +1 -1
- data/lib/mixin_bot/transaction.rb +77 -58
- data/lib/mixin_bot/utils/address.rb +15 -2
- data/lib/mixin_bot/utils/crypto.rb +61 -25
- data/lib/mixin_bot/utils/decoder.rb +4 -6
- data/lib/mixin_bot/utils/encoder.rb +11 -11
- data/lib/mixin_bot/version.rb +1 -1
- data/lib/mvm/client.rb +4 -4
- data/lib/mvm/registry.rb +2 -2
- metadata +19 -102
@@ -51,13 +51,13 @@ module MixinBot
|
|
51
51
|
bytes += encode_outputs
|
52
52
|
|
53
53
|
# placeholder for `references`
|
54
|
-
bytes +=
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
141
|
+
keys_size = MixinBot.utils.decode_uint16 @bytes.shift(2)
|
146
142
|
|
147
143
|
keys_size.times do
|
148
|
-
index = MixinBot.utils.
|
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.
|
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.
|
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.
|
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.
|
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.
|
203
|
+
bytes += MixinBot.utils.encode_uint16 transaction_bytes.size
|
208
204
|
bytes += transaction_bytes
|
209
205
|
|
210
|
-
bytes += MixinBot.utils.
|
206
|
+
bytes += MixinBot.utils.encode_uint64 deposit['index']
|
211
207
|
|
212
208
|
amount_bytes = MixinBot.utils.bytes_of deposit['amount']
|
213
|
-
bytes += MixinBot.utils.
|
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.
|
223
|
+
bytes += MixinBot.utils.encode_uint16 NULL_BYTES
|
228
224
|
else
|
229
225
|
group_bytes = [group].pack('H*')
|
230
|
-
bytes += MixinBot.utils.
|
226
|
+
bytes += MixinBot.utils.encode_uint16 group_bytes.size
|
231
227
|
bytes += group_bytes
|
232
228
|
end
|
233
229
|
|
234
|
-
bytes += MixinBot.utils.
|
230
|
+
bytes += MixinBot.utils.encode_uint64 mint['batch']
|
235
231
|
|
236
232
|
amount_bytes = MixinBot.utils.encode_int mint['amount']
|
237
|
-
bytes += MixinBot.utils.
|
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.
|
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.
|
252
|
+
bytes += MixinBot.utils.encode_uint16 amount_bytes.size
|
257
253
|
bytes += amount_bytes
|
258
254
|
|
259
255
|
# keys
|
260
|
-
bytes += MixinBot.utils.
|
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.
|
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.
|
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.
|
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.
|
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.
|
317
|
-
bytes += MixinBot.utils.
|
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.
|
334
|
-
signers.map(&->(signer) { bytes += MixinBot.utils.
|
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.
|
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.
|
369
|
+
bytes += MixinBot.utils.encode_uint16 sl
|
362
370
|
|
363
371
|
if sl.positive?
|
364
372
|
signatures.each do |signature|
|
365
|
-
bytes += MixinBot.utils.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
420
|
+
deposit['index'] = MixinBot.utils.decode_uint64 @bytes.shift(8)
|
413
421
|
|
414
|
-
amount_size = MixinBot.utils.
|
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.
|
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.
|
435
|
-
_amount_size = MixinBot.utils.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
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
|
57
|
-
|
58
|
-
|
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 =
|
71
|
+
pub = shared_public_key key
|
69
72
|
|
70
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
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 +
|
88
|
+
r_point.encode + s_scalar.to_bytes(36)
|
93
89
|
end
|
94
90
|
|
95
|
-
def generate_unique_uuid(
|
91
|
+
def generate_unique_uuid(uuid1, uuid2)
|
96
92
|
md5 = Digest::MD5.new
|
97
|
-
md5 << [
|
98
|
-
md5 << [
|
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.
|
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
|
-
|
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
|
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
|
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
|
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(
|
7
|
-
if
|
6
|
+
def encode_raw_transaction(txn)
|
7
|
+
if txn.is_a? String
|
8
8
|
begin
|
9
|
-
|
9
|
+
txn = JSON.parse txn
|
10
10
|
rescue JSON::ParserError
|
11
|
-
|
11
|
+
txn
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
raise ArgumentError, "#{
|
15
|
+
raise ArgumentError, "#{txn} is not a valid json" unless txn.is_a? Hash
|
16
16
|
|
17
|
-
|
17
|
+
txn = txn.with_indifferent_access
|
18
18
|
|
19
|
-
MixinBot::Transaction.new(**
|
19
|
+
MixinBot::Transaction.new(**txn).encode.hex
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
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
|
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
|
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
|
45
|
+
break if int.zero?
|
46
46
|
|
47
47
|
bytes.push int & 255
|
48
48
|
int = (int / (2**8)) | 0
|
data/lib/mixin_bot/version.rb
CHANGED
data/lib/mvm/client.rb
CHANGED
@@ -17,12 +17,12 @@ module MVM
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def get(path, **
|
21
|
-
@conn.get(path, **
|
20
|
+
def get(path, **)
|
21
|
+
@conn.get(path, **).body
|
22
22
|
end
|
23
23
|
|
24
|
-
def post(path, **
|
25
|
-
@conn.post(path, **
|
24
|
+
def post(path, **)
|
25
|
+
@conn.post(path, **).body
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|