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.
- 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 +77 -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/legacy_transfer.rb +2 -2
- data/lib/mixin_bot/api/multisig.rb +5 -5
- data/lib/mixin_bot/api/output.rb +3 -1
- data/lib/mixin_bot/api/payment.rb +1 -1
- data/lib/mixin_bot/api/pin.rb +2 -2
- data/lib/mixin_bot/api/rpc.rb +6 -6
- data/lib/mixin_bot/api/transaction.rb +134 -23
- data/lib/mixin_bot/api/transfer.rb +28 -12
- data/lib/mixin_bot/api/user.rb +12 -7
- 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 +23 -10
- 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
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(
|
71
|
-
utils.encode_raw_transaction
|
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
|
data/lib/mixin_bot/client.rb
CHANGED
@@ -78,7 +78,7 @@ module MixinBot
|
|
78
78
|
return result
|
79
79
|
end
|
80
80
|
|
81
|
-
errmsg = "#{verb.upcase}
|
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
@@ -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
|
|
@@ -32,11 +31,12 @@ module MixinBot
|
|
32
31
|
payload
|
33
32
|
end
|
34
33
|
|
35
|
-
def build_mix_address(members
|
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
|
-
|
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
|
78
|
-
members = data[3
|
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
|
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
|
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
|