mixin_bot 0.12.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mixin_bot/api/address.rb +21 -0
  3. data/lib/mixin_bot/api/app.rb +5 -11
  4. data/lib/mixin_bot/api/asset.rb +9 -16
  5. data/lib/mixin_bot/api/attachment.rb +27 -22
  6. data/lib/mixin_bot/api/auth.rb +31 -53
  7. data/lib/mixin_bot/api/blaze.rb +4 -3
  8. data/lib/mixin_bot/api/collectible.rb +60 -58
  9. data/lib/mixin_bot/api/conversation.rb +29 -49
  10. data/lib/mixin_bot/api/encrypted_message.rb +17 -17
  11. data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
  12. data/lib/mixin_bot/api/legacy_output.rb +50 -0
  13. data/lib/mixin_bot/api/legacy_payment.rb +31 -0
  14. data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
  15. data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
  16. data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
  17. data/lib/mixin_bot/api/me.rb +13 -17
  18. data/lib/mixin_bot/api/message.rb +13 -10
  19. data/lib/mixin_bot/api/multisig.rb +16 -221
  20. data/lib/mixin_bot/api/output.rb +46 -0
  21. data/lib/mixin_bot/api/payment.rb +9 -20
  22. data/lib/mixin_bot/api/pin.rb +57 -65
  23. data/lib/mixin_bot/api/rpc.rb +9 -11
  24. data/lib/mixin_bot/api/snapshot.rb +15 -29
  25. data/lib/mixin_bot/api/tip.rb +43 -0
  26. data/lib/mixin_bot/api/transaction.rb +184 -60
  27. data/lib/mixin_bot/api/transfer.rb +64 -32
  28. data/lib/mixin_bot/api/user.rb +83 -53
  29. data/lib/mixin_bot/api/withdraw.rb +52 -53
  30. data/lib/mixin_bot/api.rb +78 -45
  31. data/lib/mixin_bot/cli/api.rb +151 -8
  32. data/lib/mixin_bot/cli/utils.rb +14 -4
  33. data/lib/mixin_bot/cli.rb +13 -10
  34. data/lib/mixin_bot/client.rb +76 -127
  35. data/lib/mixin_bot/configuration.rb +98 -0
  36. data/lib/mixin_bot/nfo.rb +174 -0
  37. data/lib/mixin_bot/transaction.rb +505 -0
  38. data/lib/mixin_bot/utils/address.rb +108 -0
  39. data/lib/mixin_bot/utils/crypto.rb +182 -0
  40. data/lib/mixin_bot/utils/decoder.rb +58 -0
  41. data/lib/mixin_bot/utils/encoder.rb +63 -0
  42. data/lib/mixin_bot/utils.rb +8 -109
  43. data/lib/mixin_bot/uuid.rb +41 -0
  44. data/lib/mixin_bot/version.rb +1 -1
  45. data/lib/mixin_bot.rb +39 -14
  46. data/lib/mvm/bridge.rb +2 -19
  47. data/lib/mvm/client.rb +11 -33
  48. data/lib/mvm/nft.rb +4 -4
  49. data/lib/mvm/registry.rb +9 -9
  50. data/lib/mvm/scan.rb +3 -5
  51. data/lib/mvm.rb +5 -6
  52. metadata +101 -44
  53. data/lib/mixin_bot/utils/nfo.rb +0 -176
  54. data/lib/mixin_bot/utils/transaction.rb +0 -478
  55. data/lib/mixin_bot/utils/uuid.rb +0 -43
@@ -3,37 +3,33 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Me
6
- # https://developers.mixin.one/api/beta-mixin-message/read-profile/
7
6
  def me(access_token: nil)
8
7
  path = '/me'
9
- access_token ||= access_token('GET', path, '')
10
- authorization = format('Bearer %<access_token>s', access_token: access_token)
11
- client.get(path, headers: { 'Authorization': authorization })
8
+ client.get path, access_token:
12
9
  end
13
- alias read_me me
14
10
 
15
- # https://developers.mixin.one/api/beta-mixin-message/update-profile/
16
11
  # avatar_base64:
17
- # String: Base64 of image, supports format png, jpeg and gif, base64 image size > 1024.
18
- def update_me(full_name:, avatar_base64: nil, access_token: nil)
12
+ # String: Base64 of image, supports format png, jpeg and gif, base64 image size > 1024.
13
+ def update_me(**kwargs)
19
14
  path = '/me'
20
15
  payload = {
21
- full_name: full_name,
22
- avatar_base64: avatar_base64
16
+ full_name: kwargs[:full_name],
17
+ avatar_base64: kwargs[:avatar_base64],
18
+ access_token: kwargs[:access_token]
23
19
  }
24
- access_token ||= access_token('POST', path, payload.to_json)
25
- authorization = format('Bearer %<access_token>s', access_token: access_token)
26
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
20
+ client.post path, **payload
27
21
  end
28
22
 
29
23
  # https://developers.mixin.one/api/beta-mixin-message/friends/
30
24
  def friends(access_token: nil)
31
25
  path = '/friends'
32
- access_token ||= access_token('GET', path, '')
33
- authorization = format('Bearer %<access_token>s', access_token: access_token)
34
- client.get(path, headers: { 'Authorization': authorization })
26
+ client.get path, access_token:
27
+ end
28
+
29
+ def safe_me(access_token: nil)
30
+ path = '/safe/me'
31
+ client.get path, access_token:
35
32
  end
36
- alias read_friends friends
37
33
  end
38
34
  end
39
35
  end
@@ -10,10 +10,10 @@ module MixinBot
10
10
 
11
11
  def acknowledge_message_receipt(message_id)
12
12
  params = {
13
- message_id: message_id,
13
+ message_id:,
14
14
  status: 'READ'
15
15
  }
16
- write_ws_message(action: 'ACKNOWLEDGE_MESSAGE_RECEIPT', params: params)
16
+ write_ws_message(action: 'ACKNOWLEDGE_MESSAGE_RECEIPT', params:)
17
17
  end
18
18
 
19
19
  def plain_text(options)
@@ -72,7 +72,7 @@ module MixinBot
72
72
  options.merge!(
73
73
  category: 'MESSAGE_RECALL',
74
74
  data: {
75
- message_id: message_id
75
+ message_id:
76
76
  }
77
77
  )
78
78
  base_message_params(options)
@@ -90,11 +90,11 @@ module MixinBot
90
90
  quote_message_id: options[:quote_message_id],
91
91
  message_id: options[:message_id] || SecureRandom.uuid,
92
92
  data: Base64.encode64(data)
93
- }
93
+ }.compact
94
94
  end
95
95
 
96
96
  # read the gzipped message form websocket
97
- def read_ws_message(data)
97
+ def ws_message(data)
98
98
  io = StringIO.new(data.pack('c*'), 'rb')
99
99
  gzip = Zlib::GzipReader.new io
100
100
  msg = gzip.read
@@ -107,8 +107,8 @@ module MixinBot
107
107
  def write_ws_message(params:, action: 'CREATE_MESSAGE')
108
108
  msg = {
109
109
  id: SecureRandom.uuid,
110
- action: action,
111
- params: params
110
+ action:,
111
+ params:
112
112
  }.to_json
113
113
 
114
114
  io = StringIO.new 'wb'
@@ -158,9 +158,12 @@ module MixinBot
158
158
  # http post request
159
159
  def send_message(payload)
160
160
  path = '/messages'
161
- access_token ||= access_token('POST', path, payload.to_json)
162
- authorization = format('Bearer %<access_token>s', access_token: access_token)
163
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
161
+
162
+ if payload.is_a? Hash
163
+ client.post path, **payload
164
+ elsif payload.is_a? Array
165
+ client.post path, *payload
166
+ end
164
167
  end
165
168
  end
166
169
  end
@@ -3,241 +3,36 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Multisig
6
- def outputs(**kwargs)
7
- limit = kwargs[:limit] || 100
8
- offset = kwargs[:offset] || ''
9
- state = kwargs[:state] || ''
10
- members = kwargs[:members] || []
11
- threshold = kwargs[:threshold] || ''
12
- access_token = kwargs[:access_token]
13
- members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
14
-
15
- path = format(
16
- '/multisigs/outputs?limit=%<limit>s&offset=%<offset>s&state=%<state>s&members=%<members>s&threshold=%<threshold>s',
17
- limit: limit,
18
- offset: offset,
19
- state: state,
20
- members: members,
21
- threshold: threshold
22
- )
23
- access_token ||= access_token('GET', path, '')
24
- authorization = format('Bearer %<access_token>s', access_token: access_token)
25
- client.get(path, headers: { 'Authorization': authorization })
26
- end
27
- alias multisigs outputs
28
- alias multisig_outputs outputs
29
-
30
- def create_output(receivers:, index:, hint: nil, access_token: nil)
31
- path = '/outputs'
32
- payload = {
33
- receivers: receivers,
34
- index: index,
35
- hint: hint
36
- }
37
- access_token ||= access_token('POST', path, payload.to_json)
38
- authorization = format('Bearer %<access_token>s', access_token: access_token)
39
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
40
- end
41
-
42
- MULTISIG_REQUEST_ACTIONS = %i[sign unlock].freeze
43
- def create_multisig_request(action, raw, access_token: nil)
44
- raise ArgumentError, "request action is limited in #{MULTISIG_REQUEST_ACTIONS.join(', ')}" unless MULTISIG_REQUEST_ACTIONS.include? action.to_sym
45
-
46
- path = '/multisigs/requests'
47
- payload = {
48
- action: action,
6
+ def create_safe_multisig_request(request_id, raw, access_token: nil)
7
+ path = '/safe/multisigs'
8
+ payload = [{
9
+ request_id: request_id,
49
10
  raw: raw
50
- }
51
- access_token ||= access_token('POST', path, payload.to_json)
52
- authorization = format('Bearer %<access_token>s', access_token: access_token)
53
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
54
- end
11
+ }]
55
12
 
56
- # transfer from the multisig address
57
- def create_sign_multisig_request(raw, access_token: nil)
58
- create_multisig_request 'sign', raw, access_token: access_token
59
- end
60
-
61
- # transfer from the multisig address
62
- # create a request for unlock a multi-sign
63
- def create_unlock_multisig_request(raw, access_token: nil)
64
- create_multisig_request 'unlock', raw, access_token: access_token
65
- end
66
-
67
- def sign_multisig_request(request_id, pin)
68
- path = format('/multisigs/requests/%<request_id>s/sign', request_id: request_id)
69
- payload = {
70
- pin: encrypt_pin(pin)
71
- }
72
- access_token ||= access_token('POST', path, payload.to_json)
73
- authorization = format('Bearer %<access_token>s', access_token: access_token)
74
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
75
- end
76
-
77
- def unlock_multisig_request(request_id, pin)
78
- path = format('/multisigs/requests/%<request_id>s/unlock', request_id: request_id)
79
- payload = {
80
- pin: encrypt_pin(pin)
81
- }
82
- access_token ||= access_token('POST', path, payload.to_json)
83
- authorization = format('Bearer %<access_token>s', access_token: access_token)
84
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
13
+ client.post path, *payload
85
14
  end
86
15
 
87
- def cancel_multisig_request(request_id, pin)
88
- path = format('/multisigs/requests/%<request_id>s/cancel', request_id: request_id)
89
- payload = {
90
- pin: encrypt_pin(pin)
91
- }
92
- access_token ||= access_token('POST', path, payload.to_json)
93
- authorization = format('Bearer %<access_token>s', access_token: access_token)
94
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
95
- end
16
+ def sign_safe_multisig_request(request_id, raw, access_token: nil)
17
+ path = format('/safe/multisigs/%<request_id>s/sign', request_id:)
96
18
 
97
- # pay to the multisig address
98
- # used for create multisig payment code_id
99
- def create_payment(**kwargs)
100
- path = '/payments'
101
19
  payload = {
102
- asset_id: kwargs[:asset_id],
103
- amount: format('%.8f', kwargs[:amount].to_d.to_r),
104
- trace_id: kwargs[:trace_id] || SecureRandom.uuid,
105
- memo: kwargs[:memo],
106
- opponent_multisig: {
107
- receivers: kwargs[:receivers],
108
- threshold: kwargs[:threshold]
109
- }
20
+ raw:
110
21
  }
111
- access_token = kwargs[:access_token]
112
- access_token ||= access_token('POST', path, payload.to_json)
113
- authorization = format('Bearer %<access_token>s', access_token: access_token)
114
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
115
- end
116
- alias create_multisig_payment create_payment
117
22
 
118
- def verify_multisig(code_id, access_token: nil)
119
- path = format('/codes/%<code_id>s', code_id: code_id)
120
- access_token ||= access_token('GET', path, '')
121
- authorization = format('Bearer %<access_token>s', access_token: access_token)
122
- client.get(path, headers: { 'Authorization': authorization })
23
+ client.post path, **payload
123
24
  end
124
25
 
125
- def build_threshold_script(threshold)
126
- s = threshold.to_s(16)
127
- s = s.length == 1 ? "0#{s}" : s
128
- raise 'NVALID THRESHOLD' if s.length > 2
26
+ def unlock_safe_multisig_request(request_id, access_token: nil)
27
+ path = format('/safe/multisigs/%<request_id>s/unlock', request_id:)
129
28
 
130
- "fffe#{s}"
29
+ client.post path, access_token: access_token
131
30
  end
132
31
 
133
- # kwargs:
134
- # {
135
- # senders: [ uuid ],
136
- # senders_threshold: integer,
137
- # receivers: [ uuid ],
138
- # receivers_threshold: integer,
139
- # asset_id: uuid,
140
- # amount: string / float,
141
- # memo: string,
142
- # }
143
- RAW_TRANSACTION_ARGUMENTS = %i[utxos senders senders_threshold receivers receivers_threshold amount].freeze
144
- def build_raw_transaction(**kwargs)
145
- raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
146
-
147
- senders = kwargs[:senders]
148
- senders_threshold = kwargs[:senders_threshold]
149
- receivers = kwargs[:receivers]
150
- receivers_threshold = kwargs[:receivers_threshold]
151
- amount = kwargs[:amount]
152
- asset_id = kwargs[:asset_id]
153
- asset_mixin_id = kwargs[:asset_mixin_id]
154
- utxos = kwargs[:utxos]
155
- memo = kwargs[:memo]
156
- extra = kwargs[:extra]
157
- access_token = kwargs[:access_token]
158
- outputs = kwargs[:outputs] || []
159
- hint = kwargs[:hint]
160
-
161
- raise 'access_token required!' if access_token.nil? && !senders.include?(client_id)
162
-
163
- amount = amount.to_d.round(8)
164
- input_amount = utxos.map(
165
- &lambda { |utxo|
166
- utxo['amount'].to_d
167
- }
168
- ).sum
169
-
170
- if input_amount < amount
171
- raise format(
172
- 'not enough amount! %<input_amount>s < %<amount>s',
173
- input_amount: input_amount,
174
- amount: amount
175
- )
176
- end
177
-
178
- inputs = utxos.map(
179
- &lambda { |utx|
180
- {
181
- 'hash' => utx['transaction_hash'],
182
- 'index' => utx['output_index']
183
- }
184
- }
185
- )
186
-
187
- if outputs.empty?
188
- receivers_threshold = 1 if receivers.size == 1
189
- output0 = build_output(
190
- receivers: receivers,
191
- index: 0,
192
- amount: amount,
193
- threshold: receivers_threshold,
194
- hint: hint
195
- )
196
- outputs.push output0
197
-
198
- if input_amount > amount
199
- output1 = build_output(
200
- receivers: senders,
201
- index: 1,
202
- amount: input_amount - amount,
203
- threshold: senders_threshold,
204
- hint: hint
205
- )
206
- outputs.push output1
207
- end
208
- end
209
-
210
- extra ||= Digest.hexencode(memo.to_s.slice(0, 140))
211
- asset = asset_mixin_id || SHA3::Digest::SHA256.hexdigest(asset_id)
212
- tx = {
213
- version: 2,
214
- asset: asset,
215
- inputs: inputs,
216
- outputs: outputs,
217
- extra: extra
218
- }
219
-
220
- tx.to_json
221
- end
222
-
223
- def build_output(receivers:, index:, amount:, threshold:, hint: nil)
224
- _output = create_output receivers: receivers, index: index, hint: hint
225
- {
226
- amount: format('%.8f', amount.to_d.to_r),
227
- script: build_threshold_script(threshold),
228
- mask: _output['mask'],
229
- keys: _output['keys']
230
- }
231
- end
232
-
233
- def str_to_bin(str)
234
- return if str.nil?
235
-
236
- str.scan(/../).map(&:hex).pack('c*')
237
- end
32
+ def safe_multisig_request(request_id, access_token: nil)
33
+ path = format('/safe/multisigs/%<request_id>s', request_id:)
238
34
 
239
- def generate_trace_from_hash(hash, output_index = 0)
240
- MixinBot::Utils.generate_trace_from_hash hash, output_index
35
+ client.get path, access_token:
241
36
  end
242
37
  end
243
38
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module Output
6
+ def build_threshold_script(threshold)
7
+ s = threshold.to_s(16)
8
+ s = "0#{s}" if s.length == 1
9
+ raise 'NVALID THRESHOLD' if s.length > 2
10
+
11
+ "fffe#{s}"
12
+ end
13
+
14
+ def safe_outputs(**kwargs)
15
+ asset = kwargs[:asset] || kwargs[:asset_id]
16
+ limit = kwargs[:limit] || 500
17
+ offset = kwargs[:offset] || ''
18
+ state = kwargs[:state] || ''
19
+ access_token = kwargs[:access_token]
20
+ order = kwargs[:order] || 'ASC'
21
+ members = kwargs[:members] || [config.app_id]
22
+ threshold = kwargs[:threshold] || members.length
23
+
24
+ members_hash = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
25
+
26
+ path = '/safe/outputs'
27
+ params = {
28
+ asset:,
29
+ limit:,
30
+ offset:,
31
+ state:,
32
+ members: members_hash,
33
+ threshold:,
34
+ order:
35
+ }.compact
36
+
37
+ client.get path, **params, access_token:
38
+ end
39
+
40
+ def safe_output(id, access_token: nil)
41
+ path = format('/safe/outputs/%<id>s', id:)
42
+ client.get path, access_token:
43
+ end
44
+ end
45
+ end
46
+ end
@@ -3,28 +3,17 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Payment
6
- def pay_url(options)
7
- format(
8
- 'https://mixin.one/pay?recipient=%<recipient_id>s&asset=%<asset>s&amount=%<amount>s&trace=%<trace>s&memo=%<memo>s',
9
- recipient_id: options[:recipient_id],
10
- asset: options[:asset_id],
11
- amount: options[:amount].to_s,
12
- trace: options[:trace],
13
- memo: options[:memo]
14
- )
15
- end
6
+ def safe_pay_url(**kwargs)
7
+ members = kwargs[:members]
8
+ threshold = kwargs[:threshold]
9
+ asset_id = kwargs[:asset_id]
10
+ amount = kwargs[:amount]
11
+ memo = kwargs[:memo] || ''
12
+ trace_id = kwargs[:trace_id] || SecureRandom.uuid
16
13
 
17
- # https://developers.mixin.one/api/alpha-mixin-network/verify-payment/
18
- def verify_payment(options)
19
- path = 'payments'
20
- payload = {
21
- asset_id: options[:asset_id],
22
- opponent_id: options[:opponent_id],
23
- amount: options[:amount].to_s,
24
- trace_id: options[:trace]
25
- }
14
+ mix_address = MixinBot.utils.build_mix_address(members, threshold)
26
15
 
27
- client.post(path, json: payload)
16
+ "https://mixin.one/pay/#{mix_address}?amount=#{amount}&asset=#{asset_id}&memo=#{memo}&trace=#{trace_id}"
28
17
  end
29
18
  end
30
19
  end
@@ -4,89 +4,81 @@ module MixinBot
4
4
  class API
5
5
  module Pin
6
6
  # https://developers.mixin.one/api/alpha-mixin-network/verify-pin/
7
- def verify_pin(pin_code)
7
+ def verify_pin(pin = nil)
8
+ pin ||= MixinBot.config.pin
9
+ raise ArgumentError, 'invalid pin' if pin.blank?
10
+
8
11
  path = '/pin/verify'
9
- payload = {
10
- pin: encrypt_pin(pin_code)
11
- }
12
12
 
13
- access_token = access_token('POST', path, payload.to_json)
14
- authorization = format('Bearer %<access_token>s', access_token: access_token)
15
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
13
+ payload =
14
+ if pin.length > 6
15
+ timestamp = (Time.now.utc.to_f * 1e9).to_i
16
+ pin_base64 = encrypt_tip_pin pin, 'TIP:VERIFY:', timestamp.to_s.rjust(32, '0')
17
+
18
+ {
19
+ pin_base64:,
20
+ timestamp:
21
+ }
22
+ else
23
+ {
24
+ pin: MixinBot.utils.encrypt_pin(pin)
25
+ }
26
+ end
27
+
28
+ client.post path, **payload
16
29
  end
17
30
 
18
31
  # https://developers.mixin.one/api/alpha-mixin-network/create-pin/
19
- def update_pin(old_pin:, pin:)
32
+ def update_pin(pin:, old_pin: nil)
33
+ old_pin ||= MixinBot.config.pin
34
+ raise ArgumentError, 'invalid old pin' if old_pin.present? && old_pin.length != 6
35
+
20
36
  path = '/pin/update'
21
37
  encrypted_old_pin = old_pin.nil? ? '' : encrypt_pin(old_pin, iterator: Time.now.utc.to_i)
38
+
22
39
  encrypted_pin = encrypt_pin(pin, iterator: Time.now.utc.to_i + 1)
23
40
  payload = {
24
- old_pin: encrypted_old_pin,
25
- pin: encrypted_pin
41
+ old_pin_base64: encrypted_old_pin,
42
+ pin_base64: encrypted_pin
26
43
  }
27
44
 
28
- access_token = access_token('POST', path, payload.to_json)
29
- authorization = format('Bearer %<access_token>s', access_token: access_token)
30
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
45
+ client.post path, **payload
31
46
  end
32
47
 
33
- # decrypt the encrpted pin, just for test
34
- def decrypt_pin(msg)
35
- msg = Base64.strict_decode64 msg
36
- iv = msg[0..15]
37
- cipher = msg[16..47]
38
- alg = 'AES-256-CBC'
39
- decode_cipher = OpenSSL::Cipher.new(alg)
40
- decode_cipher.decrypt
41
- decode_cipher.iv = iv
42
- decode_cipher.key = _generate_aes_key
43
- decoded = decode_cipher.update(cipher)
44
- decoded[0..5]
48
+ def prepare_tip_key(counter = 0)
49
+ ed25519_key = JOSE::JWA::Ed25519.keypair
50
+
51
+ private_key = ed25519_key[1].unpack1('H*')
52
+ public_key = (ed25519_key[0].bytes + MixinBot::Utils.encode_uint_64(counter + 1)).pack('c*').unpack1('H*')
53
+
54
+ {
55
+ private_key:,
56
+ public_key:
57
+ }
45
58
  end
46
59
 
47
- # https://developers.mixin.one/api/alpha-mixin-network/encrypted-pin/
48
- # use timestamp(timestamp) for iterator as default: must be bigger than the previous, the first time must be greater than 0. After a new session created, it will be reset to 0.
49
- def encrypt_pin(pin_code, iterator: nil)
50
- iterator ||= Time.now.utc.to_i
51
- tszero = iterator % 0x100
52
- tsone = (iterator % 0x10000) >> 8
53
- tstwo = (iterator % 0x1000000) >> 16
54
- tsthree = (iterator % 0x100000000) >> 24
55
- tsstring = "#{tszero.chr}#{tsone.chr}#{tstwo.chr}#{tsthree.chr}\u0000\u0000\u0000\u0000"
56
- encrypt_content = pin_code + tsstring + tsstring
57
- pad_count = 16 - encrypt_content.length % 16
58
- padded_content =
59
- if pad_count.positive?
60
- encrypt_content + pad_count.chr * pad_count
61
- else
62
- encrypt_content
63
- end
60
+ def encrypt_pin(pin, iterator: nil)
61
+ MixinBot.utils.encrypt_pin(pin, iterator:, shared_key: generate_shared_key_with_server)
62
+ end
64
63
 
65
- alg = 'AES-256-CBC'
66
- aes = OpenSSL::Cipher.new(alg)
67
- iv = OpenSSL::Cipher.new(alg).random_iv
68
- aes.encrypt
69
- aes.key = _generate_aes_key
70
- aes.iv = iv
71
- cipher = aes.update(padded_content)
72
- msg = iv + cipher
73
- Base64.strict_encode64 msg
64
+ def decrypt_pin(msg)
65
+ MixinBot.utils.decrypt_pin msg, shared_key: generate_shared_key_with_server
74
66
  end
75
- end
76
67
 
77
- def _generate_aes_key
78
- if pin_token.size == 32
79
- JOSE::JWA::X25519.x25519(
80
- JOSE::JWA::Ed25519.secret_to_curve25519(private_key[0..31]),
81
- pin_token
82
- )
83
- else
84
- JOSE::JWA::PKCS1.rsaes_oaep_decrypt(
85
- 'SHA256',
86
- pin_token,
87
- OpenSSL::PKey::RSA.new(private_key),
88
- session_id
89
- )
68
+ def generate_shared_key_with_server
69
+ if config.server_public_key.size == 32
70
+ JOSE::JWA::X25519.x25519(
71
+ config.session_private_key_curve25519,
72
+ config.server_public_key_curve25519
73
+ )
74
+ else
75
+ JOSE::JWA::PKCS1.rsaes_oaep_decrypt(
76
+ 'SHA256',
77
+ config.server_public_key,
78
+ OpenSSL::PKey::RSA.new(config.session_private_key),
79
+ session_id
80
+ )
81
+ end
90
82
  end
91
83
  end
92
84
  end
@@ -6,18 +6,16 @@ module MixinBot
6
6
  def rpc_proxy(method, params = [], access_token: nil)
7
7
  path = '/external/proxy'
8
8
  payload = {
9
- method: method,
10
- params: params
9
+ method:,
10
+ params:
11
11
  }
12
12
 
13
- access_token ||= access_token('POST', path, payload.to_json)
14
- authorization = format('Bearer %<access_token>s', access_token: access_token)
15
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
13
+ client.post path, **payload, access_token:
16
14
  end
17
15
 
18
16
  # send a signed transaction to main net
19
17
  def send_raw_transaction(raw, access_token: nil)
20
- rpc_proxy('sendrawtransaction', [raw], access_token: access_token)
18
+ rpc_proxy('sendrawtransaction', [raw], access_token:)
21
19
  end
22
20
 
23
21
  def get_transaction(hash, access_token: nil)
@@ -25,23 +23,23 @@ module MixinBot
25
23
  end
26
24
 
27
25
  def get_utxo(hash, index = 0, access_token: nil)
28
- rpc_proxy 'getutxo', [hash, index], access_token: access_token
26
+ rpc_proxy 'getutxo', [hash, index], access_token:
29
27
  end
30
28
 
31
29
  def get_snapshot(hash, access_token: nil)
32
- rpc_proxy 'getsnapshot', [hash], access_token: access_token
30
+ rpc_proxy 'getsnapshot', [hash], access_token:
33
31
  end
34
32
 
35
33
  def list_snapshots(offset = 0, count = 10, sig = false, tx = false, access_token: nil)
36
- rpc_proxy 'listsnapshots', [offset, count, sig, tx], access_token: access_token
34
+ rpc_proxy 'listsnapshots', [offset, count, sig, tx], access_token:
37
35
  end
38
36
 
39
37
  def list_mint_works(offset = 0, access_token: nil)
40
- rpc_proxy 'listmintworks', [offset], access_token: access_token
38
+ rpc_proxy 'listmintworks', [offset], access_token:
41
39
  end
42
40
 
43
41
  def list_mint_distributions(offset = 0, count = 10, tx = false, access_token: nil)
44
- rpc_proxy 'listmintdistributions', [offset, count, tx], access_token: access_token
42
+ rpc_proxy 'listmintdistributions', [offset, count, tx], access_token:
45
43
  end
46
44
  end
47
45
  end