mixin_bot 0.9.0 → 0.11.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/encrypted_message.rb +204 -0
- data/lib/mixin_bot/api/message.rb +10 -181
- data/lib/mixin_bot/api/snapshot.rb +1 -1
- data/lib/mixin_bot/api.rb +2 -0
- data/lib/mixin_bot/cli/utils.rb +2 -2
- data/lib/mixin_bot/utils/nfo.rb +4 -5
- data/lib/mixin_bot/utils.rb +12 -13
- data/lib/mixin_bot/version.rb +1 -1
- data/lib/mvm/abis/bridge.json +154 -0
- data/lib/mvm/abis/erc20.json +188 -0
- data/lib/mvm/abis/erc721.json +317 -0
- data/lib/mvm/abis/mirror.json +163 -0
- data/lib/mvm/abis/registry.json +248 -0
- data/lib/mvm/bridge.rb +38 -0
- data/lib/mvm/client.rb +50 -0
- data/lib/mvm/nft.rb +53 -0
- data/lib/mvm/scan.rb +27 -0
- data/lib/mvm.rb +28 -0
- metadata +27 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7444d18a70abce961709af67cf6ad5a60b75c4ca42eefa00893bff00941c3b3
|
4
|
+
data.tar.gz: 8bc1bbc95ee57b65ed32e1ce88f6118eecc2651cbd6c6bea7a34f5365c0304bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b036b4f994283e0332f02c74155c51f7acf97c4fe69ba7df895d213945785dbb29af6f9784d383ce2c5c302b0592955ec233f8712ab0c6555188522bf416222
|
7
|
+
data.tar.gz: 1e5800db35abccd37683b427d6a607a71f73e294c6903da4ee929037c50673705acd6bdca86a451d6609d30c5aed9956b41a9fee72947d887497a2c9d3706295
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module MixinBot
|
4
|
+
class API
|
5
|
+
module EncryptedMessage
|
6
|
+
def encrypted_text(options)
|
7
|
+
options.merge!(category: 'ENCRYPTED_TEXT')
|
8
|
+
base_encrypted_message_params(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def encrypted_post(options)
|
12
|
+
options.merge!(category: 'ENCRYPTED_POST')
|
13
|
+
base_encrypted_message_params(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def encrypted_image(options)
|
17
|
+
options.merge!(category: 'ENCRYPTED_IMAGE')
|
18
|
+
base_encrypted_message_params(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def encrypted_data(options)
|
22
|
+
options.merge!(category: 'ENCRYPTED_DATA')
|
23
|
+
base_encrypted_message_params(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def encrypted_sticker(options)
|
27
|
+
options.merge!(category: 'ENCRYPTED_STICKER')
|
28
|
+
base_encrypted_message_params(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def encrypted_contact(options)
|
32
|
+
options.merge!(category: 'ENCRYPTED_CONTACT')
|
33
|
+
base_encrypted_message_params(options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def encrypted_audio(options)
|
37
|
+
options.merge!(category: 'ENCRYPTED_AUDIO')
|
38
|
+
base_encrypted_message_params(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def encrypted_video(options)
|
42
|
+
options.merge!(category: 'ENCRYPTED_VIDEO')
|
43
|
+
base_encrypted_message_params(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
# use HTTP to send message
|
47
|
+
def send_encrypted_text_message(options)
|
48
|
+
send_encrypted_message encrypted_text(options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_encrypted_post_message(options)
|
52
|
+
send_encrypted_message encrypted_post(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_encrypted_image_message(options)
|
56
|
+
send_encrypted_message encrypted_image(options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def send_encrypted_data_message(options)
|
60
|
+
send_encrypted_message encrypted_data(options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def send_encrypted_sticker_message(options)
|
64
|
+
send_encrypted_message encrypted_sticker(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_encrypted_contact_message(options)
|
68
|
+
send_encrypted_message encrypted_contact(options)
|
69
|
+
end
|
70
|
+
|
71
|
+
def send_encrypted_audio_message(options)
|
72
|
+
send_encrypted_message encrypted_audio(options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_encrypted_video_message(options)
|
76
|
+
send_encrypted_message encrypted_video(options)
|
77
|
+
end
|
78
|
+
|
79
|
+
# base format of message params
|
80
|
+
def base_encrypted_message_params(options)
|
81
|
+
data = options[:data].is_a?(String) ? options[:data] : options[:data].to_json
|
82
|
+
data_base64 = encrypt_message Base64.urlsafe_encode64(data, padding: false), options[:sessions]
|
83
|
+
session_ids = options[:sessions].map(&->(s) { s['session_id'] }).sort
|
84
|
+
checksum = Digest::MD5.hexdigest session_ids.join
|
85
|
+
|
86
|
+
{
|
87
|
+
conversation_id: options[:conversation_id],
|
88
|
+
recipient_id: options[:recipient_id],
|
89
|
+
representative_id: options[:representative_id],
|
90
|
+
category: options[:category],
|
91
|
+
quote_message_id: options[:quote_message_id],
|
92
|
+
message_id: options[:message_id] || SecureRandom.uuid,
|
93
|
+
data_base64: data_base64,
|
94
|
+
checksum: checksum,
|
95
|
+
recipient_sessions: session_ids.map(&->(s) { { session_id: s } }),
|
96
|
+
silent: false
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def send_encrypted_messages(messages)
|
101
|
+
send_encrypted_message messages
|
102
|
+
end
|
103
|
+
|
104
|
+
# http post request
|
105
|
+
def send_encrypted_message(payload)
|
106
|
+
path = '/encrypted_messages'
|
107
|
+
payload = [payload] unless payload.is_a?(Array)
|
108
|
+
access_token ||= access_token('POST', path, payload.to_json)
|
109
|
+
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
110
|
+
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
111
|
+
end
|
112
|
+
|
113
|
+
def encrypt_message(data, sessions = [], sk: nil, pk: nil)
|
114
|
+
raise ArgumentError, 'Wrong sessions format!' unless sessions.all?(&->(s) { s.key?('session_id') && s.key?('public_key') })
|
115
|
+
|
116
|
+
sk = private_key[0...32]
|
117
|
+
pk ||= private_key[32...]
|
118
|
+
|
119
|
+
checksum = Digest::MD5.hexdigest sessions.map(&->(s) { s['session_id'] }).sort.join
|
120
|
+
encrypter = OpenSSL::Cipher.new('AES-128-GCM').encrypt
|
121
|
+
key = encrypter.random_key
|
122
|
+
nounce = encrypter.random_iv
|
123
|
+
encrypter.key = key
|
124
|
+
encrypter.iv = nounce
|
125
|
+
encrypter.auth_data = ''
|
126
|
+
ciphertext = encrypter.update(Base64.urlsafe_decode64(data)) + encrypter.final + encrypter.auth_tag
|
127
|
+
|
128
|
+
bytes = [1]
|
129
|
+
bytes += [sessions.size].pack('v*').bytes
|
130
|
+
bytes += JOSE::JWA::Ed25519.pk_to_curve25519(pk).bytes
|
131
|
+
|
132
|
+
sessions.each do |session|
|
133
|
+
aes_key = JOSE::JWA::X25519.shared_secret(
|
134
|
+
Base64.urlsafe_decode64(session['public_key']),
|
135
|
+
JOSE::JWA::Ed25519.secret_to_curve25519(sk)
|
136
|
+
)
|
137
|
+
|
138
|
+
padding = 16 - key.size % 16
|
139
|
+
padtext = ([padding] * padding).pack('C*')
|
140
|
+
|
141
|
+
encrypter = OpenSSL::Cipher.new('AES-256-CBC').encrypt
|
142
|
+
encrypter.key = aes_key
|
143
|
+
iv = encrypter.random_iv
|
144
|
+
encrypter.iv = iv
|
145
|
+
|
146
|
+
bytes += (MixinBot::Utils::UUID.new(hex: session['session_id']).packed + iv).bytes
|
147
|
+
bytes += encrypter.update(key + padtext).bytes
|
148
|
+
end
|
149
|
+
|
150
|
+
bytes += nounce.bytes
|
151
|
+
bytes += ciphertext.bytes
|
152
|
+
|
153
|
+
Base64.urlsafe_encode64 bytes.pack('C*'), padding: false
|
154
|
+
end
|
155
|
+
|
156
|
+
def decrypt_message(data, sk: nil, si: nil)
|
157
|
+
bytes = Base64.urlsafe_decode64(data).bytes
|
158
|
+
|
159
|
+
si ||= session_id
|
160
|
+
sk ||= private_key[0...32]
|
161
|
+
|
162
|
+
size = 16 + 48
|
163
|
+
return '' if bytes.size < 1 + 2 + 32 + size + 12
|
164
|
+
|
165
|
+
session_length = bytes[1...3].pack('v*').unpack1('C*')
|
166
|
+
prefix_size = 35 + session_length * size
|
167
|
+
|
168
|
+
i = 35
|
169
|
+
key = ''
|
170
|
+
while i < prefix_size
|
171
|
+
uuid = MixinBot::Utils::UUID.new(raw: bytes[i...(i + 16)].pack('C*')).unpacked
|
172
|
+
if uuid == si
|
173
|
+
pub = bytes[3...35]
|
174
|
+
aes_key = JOSE::JWA::X25519.shared_secret(
|
175
|
+
pub.pack('C*'),
|
176
|
+
JOSE::JWA::Ed25519.secret_to_curve25519(sk)
|
177
|
+
)
|
178
|
+
iv = bytes[(i + 16)...(i + 16 + 16)].pack('C*')
|
179
|
+
encrypted_key = bytes[(i + 16 + 16)...(i + size)].pack('C*')
|
180
|
+
|
181
|
+
decrypter = OpenSSL::Cipher.new('AES-256-CBC').decrypt
|
182
|
+
decrypter.iv = iv
|
183
|
+
decrypter.key = aes_key
|
184
|
+
cipher = decrypter.update(encrypted_key)
|
185
|
+
key = cipher[...16]
|
186
|
+
break
|
187
|
+
end
|
188
|
+
i += size
|
189
|
+
end
|
190
|
+
|
191
|
+
return '' unless key.size == 16
|
192
|
+
|
193
|
+
decrypter = OpenSSL::Cipher.new('AES-128-GCM').decrypt
|
194
|
+
decrypter.key = key
|
195
|
+
decrypter.iv = bytes[prefix_size...(prefix_size + 12)].pack('C*')
|
196
|
+
decrypter.auth_tag = bytes.last(16).pack('C*')
|
197
|
+
decrypted = decrypter.update(bytes[(prefix_size + 12)...(bytes.size - 16)].pack('C*'))
|
198
|
+
decrypter.final
|
199
|
+
|
200
|
+
Base64.urlsafe_encode64 decrypted
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -8,15 +8,6 @@ module MixinBot
|
|
8
8
|
write_ws_message(action: 'LIST_PENDING_MESSAGES', params: {})
|
9
9
|
end
|
10
10
|
|
11
|
-
# ACKNOWLEDGE_MESSAGE_RECEIPT ack server received message
|
12
|
-
# {
|
13
|
-
# "id": "UUID",
|
14
|
-
# "action": "ACKNOWLEDGE_MESSAGE_RECEIPT",
|
15
|
-
# "params": {
|
16
|
-
# "message_id": "UUID // message_id is you received message's message_id",
|
17
|
-
# "status": "READ"
|
18
|
-
# }
|
19
|
-
# }
|
20
11
|
def acknowledge_message_receipt(message_id)
|
21
12
|
params = {
|
22
13
|
message_id: message_id,
|
@@ -25,200 +16,56 @@ module MixinBot
|
|
25
16
|
write_ws_message(action: 'ACKNOWLEDGE_MESSAGE_RECEIPT', params: params)
|
26
17
|
end
|
27
18
|
|
28
|
-
# {
|
29
|
-
# "id": "UUID // generated by client",
|
30
|
-
# "action": "CREATE_MESSAGE",
|
31
|
-
# "params": {
|
32
|
-
# "conversation_id": "UUID",
|
33
|
-
# "category": "PLAIN_TEXT",
|
34
|
-
# "status": "SENT",
|
35
|
-
# "message_id": "UUID // generated by client",
|
36
|
-
# "data": "Base64 encoded data" ,
|
37
|
-
# }
|
38
|
-
# }
|
39
19
|
def plain_text(options)
|
40
20
|
options.merge!(category: 'PLAIN_TEXT')
|
41
21
|
base_message_params(options)
|
42
22
|
end
|
43
23
|
|
44
|
-
# {
|
45
|
-
# "id": "UUID // generated by client",
|
46
|
-
# "action": "CREATE_MESSAGE",
|
47
|
-
# "params": {
|
48
|
-
# "conversation_id": "UUID",
|
49
|
-
# "category": "PLAIN_POST",
|
50
|
-
# "status": "SENT",
|
51
|
-
# "message_id": "UUID // generated by client",
|
52
|
-
# "data": "Base64 encoded data content is markdown" ,
|
53
|
-
# }
|
54
|
-
# }
|
55
24
|
def plain_post(options)
|
56
25
|
options.merge!(category: 'PLAIN_POST')
|
57
26
|
base_message_params(options)
|
58
27
|
end
|
59
28
|
|
60
|
-
# {
|
61
|
-
# "id": "UUID",
|
62
|
-
# "action": "CREATE_MESSAGE",
|
63
|
-
# "params": {
|
64
|
-
# "conversation_id": "UUID"
|
65
|
-
# "category": "PLAIN_IMAGE"
|
66
|
-
# "status": "SENT",
|
67
|
-
# "message_id": "UUID",
|
68
|
-
# "data": "Base64 encoded data"
|
69
|
-
# }
|
70
|
-
# }
|
71
|
-
# data format:
|
72
|
-
# {
|
73
|
-
# "attachment_id":
|
74
|
-
# "Read From POST /attachments",
|
75
|
-
# "mime_type": "",
|
76
|
-
# "width": 1024,
|
77
|
-
# "height": 1024,
|
78
|
-
# "size": 1024,
|
79
|
-
# "thumbnail": "base64 encoded"
|
80
|
-
# }
|
81
29
|
def plain_image(options)
|
82
30
|
options.merge!(category: 'PLAIN_IMAGE')
|
83
31
|
base_message_params(options)
|
84
32
|
end
|
85
33
|
|
86
|
-
# {
|
87
|
-
# "id": "UUID",
|
88
|
-
# "action": "CREATE_MESSAGE",
|
89
|
-
# "params": {
|
90
|
-
# "conversation_id": "UUID",
|
91
|
-
# "category": "PLAIN_DATA",
|
92
|
-
# "status": "SENT",
|
93
|
-
# "message_id": "UUID",
|
94
|
-
# "data": "Base64 encoded data",
|
95
|
-
# }
|
96
|
-
# }
|
97
|
-
# data format:
|
98
|
-
# {
|
99
|
-
# "attachment_id": "Read From POST /attachments",
|
100
|
-
# "mime_type": "",
|
101
|
-
# "size": 1024,
|
102
|
-
# "name": "Share"
|
103
|
-
# }
|
104
34
|
def plain_data(options)
|
105
35
|
options.merge!(category: 'PLAIN_DATA')
|
106
36
|
base_message_params(options)
|
107
37
|
end
|
108
38
|
|
109
|
-
# {
|
110
|
-
# "id": "UUID",
|
111
|
-
# "action": "CREATE_MESSAGE",
|
112
|
-
# "params": {
|
113
|
-
# "conversation_id": "UUID",
|
114
|
-
# "category": "PLAIN_STICKER",
|
115
|
-
# "status": "SENT",
|
116
|
-
# "message_id": "UUID",
|
117
|
-
# "data": "Base64 encoded data"
|
118
|
-
# }
|
119
|
-
# }
|
120
|
-
# data format:
|
121
|
-
# {
|
122
|
-
# "name": "hello",
|
123
|
-
# "album_id": "UUID"
|
124
|
-
# }
|
125
39
|
def plain_sticker(options)
|
126
40
|
options.merge!(category: 'PLAIN_STICKER')
|
127
41
|
base_message_params(options)
|
128
42
|
end
|
129
43
|
|
130
|
-
# {
|
131
|
-
# "id": "UUID",
|
132
|
-
# "action": "CREATE_MESSAGE",
|
133
|
-
# "params": {
|
134
|
-
# "conversation_id": "UUID",
|
135
|
-
# "category": "PLAIN_CONTACT"
|
136
|
-
# "status": "SENT",
|
137
|
-
# "message_id": "UUID",
|
138
|
-
# "data": "Base64 encoded data"
|
139
|
-
# }
|
140
|
-
# }
|
141
|
-
# data format:
|
142
|
-
# { "user_id": "UUID"}
|
143
44
|
def plain_contact(options)
|
144
45
|
options.merge!(category: 'PLAIN_CONTACT')
|
145
46
|
base_message_params(options)
|
146
47
|
end
|
147
48
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
# }
|
159
|
-
# data format:
|
160
|
-
# {
|
161
|
-
# "icon_url": "https://mixin.one/assets/98b586edb270556d1972112bd7985e9e.png",
|
162
|
-
# "title": "Mixin",
|
163
|
-
# "description": "A free and lightning fast peer-to-peer transactional network for digital assets.",
|
164
|
-
# "action": "https://mixin.one"
|
165
|
-
# }
|
49
|
+
def plain_audio(options)
|
50
|
+
options.merge!(category: 'PLAIN_AUDIO')
|
51
|
+
base_message_params(options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def plain_video(options)
|
55
|
+
options.merge!(category: 'PLAIN_VIDEO')
|
56
|
+
base_message_params(options)
|
57
|
+
end
|
58
|
+
|
166
59
|
def app_card(options)
|
167
60
|
options.merge!(category: 'APP_CARD')
|
168
61
|
base_message_params(options)
|
169
62
|
end
|
170
63
|
|
171
|
-
# {
|
172
|
-
# "id": "UUID",
|
173
|
-
# "action": "CREATE_MESSAGE",
|
174
|
-
# "params": {
|
175
|
-
# "conversation_id": "UUID",
|
176
|
-
# "category": "APP_BUTTON_GROUP",
|
177
|
-
# "status": "SENT",
|
178
|
-
# "message_id": "UUID",
|
179
|
-
# "data": "Base64 encoded data"
|
180
|
-
# }
|
181
|
-
# }
|
182
|
-
# data format:
|
183
|
-
# [
|
184
|
-
# {
|
185
|
-
# "label": "Mixin Website",
|
186
|
-
# "color": "#ABABAB",
|
187
|
-
# "action": "https://mixin.one"
|
188
|
-
# },
|
189
|
-
# ...
|
190
|
-
# ]
|
191
64
|
def app_button_group(options)
|
192
65
|
options.merge!(category: 'APP_BUTTON_GROUP')
|
193
66
|
base_message_params(options)
|
194
67
|
end
|
195
68
|
|
196
|
-
# {
|
197
|
-
# "id": "UUID",
|
198
|
-
# "action": "CREATE_MESSAGE",
|
199
|
-
# "params": {
|
200
|
-
# "conversation_id": "UUID",
|
201
|
-
# "category": "PLAIN_VIDEO",
|
202
|
-
# "status": "SENT",
|
203
|
-
# "message_id": "UUID",
|
204
|
-
# "data": "Base64 encoded data"
|
205
|
-
# }
|
206
|
-
# }
|
207
|
-
# data format:
|
208
|
-
# {
|
209
|
-
# "attachment_id": "Read From POST /attachments",
|
210
|
-
# "mime_type": "",
|
211
|
-
# "width": 1024,
|
212
|
-
# "height": 1024,
|
213
|
-
# "size": 1024,
|
214
|
-
# "duration": 1024,
|
215
|
-
# "thumbnail": "base64 encoded"
|
216
|
-
# }
|
217
|
-
def plain_video(options)
|
218
|
-
options.merge!(category: 'PLAIN_VIDEO')
|
219
|
-
base_message_params(options)
|
220
|
-
end
|
221
|
-
|
222
69
|
def recall_message_params(message_id, options)
|
223
70
|
raise 'recipient_id is required!' if options[:recipient_id].nil?
|
224
71
|
|
@@ -304,24 +151,6 @@ module MixinBot
|
|
304
151
|
send_message [recall_message_params(message_id, options)]
|
305
152
|
end
|
306
153
|
|
307
|
-
# {
|
308
|
-
# "id": "UUID",
|
309
|
-
# "action": "CREATE_PLAIN_MESSAGES",
|
310
|
-
# "params": {
|
311
|
-
# "messages": [
|
312
|
-
# {
|
313
|
-
# "conversation_id": "UUID",
|
314
|
-
# "recipient_id": "UUID",
|
315
|
-
# "message_id": "UUID",
|
316
|
-
# "representative_id": "UUID (optional, only supported in peer to peer conversation)",
|
317
|
-
# "quote_message_id": "UUID (optional, only supported text, e.g. PLAIN_TEXT)",
|
318
|
-
# "category": "Only support plain category e.g.: PLAIN_TEXT, PLAIN_STICKER etc",
|
319
|
-
# "data": "Correspond to category."
|
320
|
-
# },
|
321
|
-
# ...
|
322
|
-
# ]
|
323
|
-
# }
|
324
|
-
# }
|
325
154
|
def send_plain_messages(messages)
|
326
155
|
send_message messages
|
327
156
|
end
|
data/lib/mixin_bot/api.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative './api/auth'
|
|
8
8
|
require_relative './api/blaze'
|
9
9
|
require_relative './api/collectible'
|
10
10
|
require_relative './api/conversation'
|
11
|
+
require_relative './api/encrypted_message'
|
11
12
|
require_relative './api/me'
|
12
13
|
require_relative './api/message'
|
13
14
|
require_relative './api/multisig'
|
@@ -83,6 +84,7 @@ module MixinBot
|
|
83
84
|
include MixinBot::API::Blaze
|
84
85
|
include MixinBot::API::Collectible
|
85
86
|
include MixinBot::API::Conversation
|
87
|
+
include MixinBot::API::EncryptedMessage
|
86
88
|
include MixinBot::API::Me
|
87
89
|
include MixinBot::API::Message
|
88
90
|
include MixinBot::API::Multisig
|
data/lib/mixin_bot/cli/utils.rb
CHANGED
@@ -6,12 +6,12 @@ module MixinBot
|
|
6
6
|
option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
|
7
7
|
option :iterator, type: :string, aliases: '-i', desc: 'Iterator'
|
8
8
|
def encrypt(pin)
|
9
|
-
log api_instance.encrypt_pin
|
9
|
+
log api_instance.encrypt_pin pin.to_s, iterator: options[:iterator]
|
10
10
|
end
|
11
11
|
|
12
12
|
desc 'unique UUIDS', 'generate unique UUID for two or more UUIDs'
|
13
13
|
def unique(*uuids)
|
14
|
-
log MixinBot::Utils.unique_uuid
|
14
|
+
log MixinBot::Utils.unique_uuid(*uuids)
|
15
15
|
end
|
16
16
|
|
17
17
|
desc 'generatetrace HASH', 'generate trace ID from Tx hash'
|
data/lib/mixin_bot/utils/nfo.rb
CHANGED
@@ -31,7 +31,7 @@ module MixinBot
|
|
31
31
|
|
32
32
|
@collection = NULL_UUID if collection.blank?
|
33
33
|
@chain = NFT_MEMO_DEFAULT_CHAIN
|
34
|
-
@nm_class= NFT_MEMO_DEFAULT_CLASS
|
34
|
+
@nm_class = NFT_MEMO_DEFAULT_CLASS
|
35
35
|
mark 0
|
36
36
|
encode
|
37
37
|
|
@@ -59,9 +59,8 @@ module MixinBot
|
|
59
59
|
|
60
60
|
def mark(*indexes)
|
61
61
|
indexes.map do |index|
|
62
|
-
if index >= 64 || index
|
63
|
-
|
64
|
-
end
|
62
|
+
raise ArgumentError, "invalid NFO memo index #{index}" if index >= 64 || index.negative?
|
63
|
+
|
65
64
|
@mask = mask ^ (1 << index)
|
66
65
|
end
|
67
66
|
end
|
@@ -69,7 +68,7 @@ module MixinBot
|
|
69
68
|
def encode
|
70
69
|
bytes = []
|
71
70
|
|
72
|
-
bytes += prefix.bytes
|
71
|
+
bytes += prefix.bytes
|
73
72
|
bytes += [version]
|
74
73
|
|
75
74
|
if mask != 0
|
data/lib/mixin_bot/utils.rb
CHANGED
@@ -7,13 +7,13 @@ require_relative './utils/transaction'
|
|
7
7
|
module MixinBot
|
8
8
|
module Utils
|
9
9
|
class << self
|
10
|
-
MAGIC = [0x77, 0x77]
|
10
|
+
MAGIC = [0x77, 0x77].freeze
|
11
11
|
TX_VERSION = 2
|
12
12
|
MAX_ENCODE_INT = 0xFFFF
|
13
|
-
NULL_BYTES = [0x00, 0x00]
|
13
|
+
NULL_BYTES = [0x00, 0x00].freeze
|
14
14
|
AGGREGATED_SIGNATURE_PREFIX = 0xFF01
|
15
|
-
AGGREGATED_SIGNATURE_ORDINAY_MASK = [0x00]
|
16
|
-
AGGREGATED_SIGNATURE_SPARSE_MASK = [0x01]
|
15
|
+
AGGREGATED_SIGNATURE_ORDINAY_MASK = [0x00].freeze
|
16
|
+
AGGREGATED_SIGNATURE_SPARSE_MASK = [0x01].freeze
|
17
17
|
|
18
18
|
def generate_unique_uuid(uuid_1, uuid_2)
|
19
19
|
md5 = Digest::MD5.new
|
@@ -53,9 +53,7 @@ module MixinBot
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def sign_raw_transaction(tx)
|
56
|
-
if tx.is_a? String
|
57
|
-
tx = JSON.parse tx
|
58
|
-
end
|
56
|
+
tx = JSON.parse tx if tx.is_a? String
|
59
57
|
raise ArgumentError, "#{tx} is not a valid json" unless tx.is_a? Hash
|
60
58
|
|
61
59
|
tx = tx.with_indifferent_access
|
@@ -64,7 +62,7 @@ module MixinBot
|
|
64
62
|
asset: tx[:asset],
|
65
63
|
inputs: tx[:inputs],
|
66
64
|
outputs: tx[:outputs],
|
67
|
-
extra: tx[:extra]
|
65
|
+
extra: tx[:extra]
|
68
66
|
).encode.hex
|
69
67
|
end
|
70
68
|
|
@@ -74,15 +72,15 @@ module MixinBot
|
|
74
72
|
|
75
73
|
def nft_memo(collection, token, extra)
|
76
74
|
MixinBot::Utils::Nfo.new(
|
77
|
-
collection: collection,
|
78
|
-
token: token,
|
75
|
+
collection: collection,
|
76
|
+
token: token,
|
79
77
|
extra: extra
|
80
78
|
).mint_memo
|
81
79
|
end
|
82
80
|
|
83
81
|
def encode_int(int)
|
84
82
|
raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
|
85
|
-
raise ArgumentError,"int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
|
83
|
+
raise ArgumentError, "int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
|
86
84
|
|
87
85
|
[int].pack('S*').bytes.reverse
|
88
86
|
end
|
@@ -97,8 +95,9 @@ module MixinBot
|
|
97
95
|
bytes = []
|
98
96
|
loop do
|
99
97
|
break if int === 0
|
98
|
+
|
100
99
|
bytes.push int & 255
|
101
|
-
int = int / (2
|
100
|
+
int = int / (2**8) | 0
|
102
101
|
end
|
103
102
|
|
104
103
|
bytes.reverse
|
@@ -107,7 +106,7 @@ module MixinBot
|
|
107
106
|
def bytes_to_int(bytes)
|
108
107
|
int = 0
|
109
108
|
bytes.each do |byte|
|
110
|
-
int = int * (2
|
109
|
+
int = int * (2**8) + byte
|
111
110
|
end
|
112
111
|
|
113
112
|
int
|
data/lib/mixin_bot/version.rb
CHANGED