mixin_bot 0.9.0 → 0.10.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/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
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cc49837c37f9c79f664694d45f0ba3e6299fa06f38ef4fe51531eb0798f97e5
|
4
|
+
data.tar.gz: 183932450eac9eff899aa29c2a99278890b2a2fc6f09a36f4d2f602526022dfc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ace6e9270492ee1d3c18154408bfd76a09c35bf4bd4cffc434b070d89a65957b2f38553f62f61854aa8976907546ddec504a4281104fd9b3cb6034e55846911
|
7
|
+
data.tar.gz: d41892aa1a7014c77b32cda0b27db3a967f97b7547c90e222b55eefb3229d175fefd98738e31e0560e7c78e558d47cee0256cc9caddd87cdf9233284a5e0a47b
|
@@ -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
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixin_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- an-lee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -281,6 +281,7 @@ files:
|
|
281
281
|
- lib/mixin_bot/api/blaze.rb
|
282
282
|
- lib/mixin_bot/api/collectible.rb
|
283
283
|
- lib/mixin_bot/api/conversation.rb
|
284
|
+
- lib/mixin_bot/api/encrypted_message.rb
|
284
285
|
- lib/mixin_bot/api/me.rb
|
285
286
|
- lib/mixin_bot/api/message.rb
|
286
287
|
- lib/mixin_bot/api/multisig.rb
|