mixin_bot 0.12.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) 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 +34 -56
  7. data/lib/mixin_bot/api/blaze.rb +4 -3
  8. data/lib/mixin_bot/api/conversation.rb +29 -49
  9. data/lib/mixin_bot/api/encrypted_message.rb +19 -19
  10. data/lib/mixin_bot/api/inscription.rb +71 -0
  11. data/lib/mixin_bot/api/legacy_collectible.rb +140 -0
  12. data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
  13. data/lib/mixin_bot/api/legacy_output.rb +50 -0
  14. data/lib/mixin_bot/api/legacy_payment.rb +31 -0
  15. data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
  16. data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
  17. data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
  18. data/lib/mixin_bot/api/me.rb +13 -17
  19. data/lib/mixin_bot/api/message.rb +13 -10
  20. data/lib/mixin_bot/api/multisig.rb +17 -222
  21. data/lib/mixin_bot/api/output.rb +48 -0
  22. data/lib/mixin_bot/api/payment.rb +9 -20
  23. data/lib/mixin_bot/api/pin.rb +57 -65
  24. data/lib/mixin_bot/api/rpc.rb +12 -14
  25. data/lib/mixin_bot/api/snapshot.rb +15 -29
  26. data/lib/mixin_bot/api/tip.rb +43 -0
  27. data/lib/mixin_bot/api/transaction.rb +295 -60
  28. data/lib/mixin_bot/api/transfer.rb +69 -31
  29. data/lib/mixin_bot/api/user.rb +88 -53
  30. data/lib/mixin_bot/api/withdraw.rb +52 -53
  31. data/lib/mixin_bot/api.rb +81 -46
  32. data/lib/mixin_bot/cli/api.rb +149 -5
  33. data/lib/mixin_bot/cli/utils.rb +14 -4
  34. data/lib/mixin_bot/cli.rb +13 -10
  35. data/lib/mixin_bot/client.rb +74 -127
  36. data/lib/mixin_bot/configuration.rb +98 -0
  37. data/lib/mixin_bot/nfo.rb +174 -0
  38. data/lib/mixin_bot/transaction.rb +524 -0
  39. data/lib/mixin_bot/utils/address.rb +121 -0
  40. data/lib/mixin_bot/utils/crypto.rb +218 -0
  41. data/lib/mixin_bot/utils/decoder.rb +56 -0
  42. data/lib/mixin_bot/utils/encoder.rb +63 -0
  43. data/lib/mixin_bot/utils.rb +8 -109
  44. data/lib/mixin_bot/uuid.rb +41 -0
  45. data/lib/mixin_bot/version.rb +1 -1
  46. data/lib/mixin_bot.rb +39 -14
  47. data/lib/mvm/bridge.rb +2 -19
  48. data/lib/mvm/client.rb +11 -33
  49. data/lib/mvm/nft.rb +4 -4
  50. data/lib/mvm/registry.rb +9 -9
  51. data/lib/mvm/scan.rb +3 -5
  52. data/lib/mvm.rb +5 -6
  53. metadata +77 -103
  54. data/lib/mixin_bot/api/collectible.rb +0 -138
  55. data/lib/mixin_bot/utils/nfo.rb +0 -176
  56. data/lib/mixin_bot/utils/transaction.rb +0 -478
  57. data/lib/mixin_bot/utils/uuid.rb +0 -43
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ module Utils
5
+ module Crypto
6
+ def access_token(method, uri, body = '', **kwargs)
7
+ sig = Digest::SHA256.hexdigest(method + uri + body.to_s)
8
+ iat = Time.now.utc.to_i
9
+ exp = (Time.now.utc + (kwargs[:exp_in] || 600)).to_i
10
+ scp = kwargs[:scp] || 'FULL'
11
+ jti = SecureRandom.uuid
12
+ uid = kwargs[:app_id] || MixinBot.config.app_id
13
+ sid = kwargs[:session_id] || MixinBot.config.session_id
14
+ private_key = kwargs[:private_key] || MixinBot.config.session_private_key
15
+
16
+ payload = {
17
+ uid:,
18
+ sid:,
19
+ iat:,
20
+ exp:,
21
+ jti:,
22
+ sig:,
23
+ scp:
24
+ }
25
+
26
+ if private_key.blank?
27
+ raise ConfigurationNotValidError, 'private_key is required'
28
+ elsif private_key.size == 64
29
+ jwk = JOSE::JWK.from_okp [:Ed25519, private_key]
30
+ jws = JOSE::JWS.from({ 'alg' => 'EdDSA' })
31
+ else
32
+ jwk = JOSE::JWK.from_pem private_key
33
+ jws = JOSE::JWS.from({ 'alg' => 'RS512' })
34
+ end
35
+
36
+ jwt = JOSE::JWT.from payload
37
+ JOSE::JWT.sign(jwk, jws, jwt).compact
38
+ end
39
+
40
+ def generate_ed25519_key
41
+ ed25519_key = JOSE::JWA::Ed25519.keypair
42
+ {
43
+ private_key: Base64.urlsafe_encode64(ed25519_key[1], padding: false),
44
+ public_key: Base64.urlsafe_encode64(ed25519_key[0], padding: false)
45
+ }
46
+ end
47
+
48
+ def generate_rsa_key
49
+ rsa_key = OpenSSL::PKey::RSA.new 1024
50
+ {
51
+ private_key: rsa_key.to_pem,
52
+ public_key: rsa_key.public_key.to_pem
53
+ }
54
+ end
55
+
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),
64
+ JOSE::JWA::Edwards25519Point::L
65
+ )
66
+ end
67
+
68
+ def sign(msg, key:)
69
+ msg = Digest::Blake3.digest msg
70
+
71
+ pub = shared_public_key key
72
+
73
+ y_scalar = scalar_from_bytes key
74
+
75
+ key_digest = Digest::SHA512.digest key
76
+ msg_digest = Digest::SHA512.digest(key_digest[-32...] + msg)
77
+
78
+ z_scalar = scalar_from_bytes msg_digest[...64]
79
+
80
+ r_point = JOSE::JWA::Edwards25519Point.stdbase * z_scalar.x.to_i
81
+
82
+ hram_digest = Digest::SHA512.digest(r_point.encode + pub + msg)
83
+
84
+ x_scalar = scalar_from_bytes hram_digest[...64]
85
+
86
+ s_scalar = (x_scalar * y_scalar) + z_scalar
87
+
88
+ r_point.encode + s_scalar.to_bytes(36)
89
+ end
90
+
91
+ def generate_unique_uuid(uuid1, uuid2)
92
+ md5 = Digest::MD5.new
93
+ md5 << [uuid1, uuid2].min
94
+ md5 << [uuid1, uuid2].max
95
+ digest = md5.digest
96
+ digest6 = ((digest[6].ord & 0x0f) | 0x30).chr
97
+ digest8 = ((digest[8].ord & 0x3f) | 0x80).chr
98
+ cipher = digest[0...6] + digest6 + digest[7] + digest8 + digest[9..]
99
+
100
+ MixinBot::UUID.new(raw: cipher).unpacked
101
+ end
102
+
103
+ def unique_uuid(*uuids)
104
+ uuids = uuids.flatten.compact
105
+ uuids.sort
106
+ r = uuids.first
107
+ uuids.each_with_index do |uuid, i|
108
+ r = generate_unique_uuid(r, uuid) if i.positive?
109
+ end
110
+
111
+ r
112
+ end
113
+
114
+ def generate_trace_from_hash(hash, output_index = 0)
115
+ md5 = Digest::MD5.new
116
+ md5 << hash
117
+ md5 << [output_index].pack('c*') if output_index.positive? && output_index < 256
118
+ digest = md5.digest
119
+ digest[6] = ((digest[6].ord & 0x0f) | 0x30).chr
120
+ digest[8] = ((digest[8].ord & 0x3f) | 0x80).chr
121
+
122
+ MixinBot::UUID.new(raw: digest).unpacked
123
+ end
124
+
125
+ # decrypt the encrpted pin, just for test
126
+ def decrypt_pin(msg, shared_key:)
127
+ msg = Base64.urlsafe_decode64 msg
128
+ iv = msg[0..15]
129
+ cipher = msg[16..47]
130
+ alg = 'AES-256-CBC'
131
+ decode_cipher = OpenSSL::Cipher.new(alg)
132
+ decode_cipher.decrypt
133
+ decode_cipher.iv = iv
134
+ decode_cipher.key = shared_key
135
+ decode_cipher.update(cipher)
136
+ end
137
+
138
+ # 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.
139
+ def encrypt_pin(pin, **kwargs)
140
+ pin = MixinBot.utils.decode_key pin
141
+
142
+ shared_key = kwargs[:shared_key]
143
+ raise ArgumentError, 'shared_key is required' if shared_key.blank?
144
+
145
+ iterator ||= kwargs[:iterator] || Time.now.utc.to_i
146
+ tszero = iterator % 0x100
147
+ tsone = (iterator % 0x10000) >> 8
148
+ tstwo = (iterator % 0x1000000) >> 16
149
+ tsthree = (iterator % 0x100000000) >> 24
150
+ tsstring = "#{tszero.chr}#{tsone.chr}#{tstwo.chr}#{tsthree.chr}\u0000\u0000\u0000\u0000"
151
+ encrypt_content = pin + tsstring + tsstring
152
+ pad_count = 16 - (encrypt_content.length % 16)
153
+ padded_content =
154
+ if pad_count.positive?
155
+ encrypt_content + (pad_count.chr * pad_count)
156
+ else
157
+ encrypt_content
158
+ end
159
+
160
+ alg = 'AES-256-CBC'
161
+ aes = OpenSSL::Cipher.new(alg)
162
+ iv = OpenSSL::Cipher.new(alg).random_iv
163
+ aes.encrypt
164
+ aes.key = shared_key
165
+ aes.iv = iv
166
+ cipher = aes.update(padded_content)
167
+ msg = iv + cipher
168
+ Base64.urlsafe_encode64 msg, padding: false
169
+ end
170
+
171
+ def tip_public_key(key, counter: 0)
172
+ raise ArgumentError, 'invalid key' if key.size < 32
173
+
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)
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ module Utils
5
+ module Decoder
6
+ def decode_key(key)
7
+ return if key.blank?
8
+
9
+ if key.match?(/\A[\h]{64,}\z/i)
10
+ [key].pack('H*')
11
+ elsif key.match?(/^-----BEGIN RSA PRIVATE KEY-----/)
12
+ key.gsub('\\r\\n', "\n").gsub("\r\n", "\n")
13
+ elsif key.match?(/\d{6}/) || (key.size % 32).zero?
14
+ key
15
+ else
16
+ Base64.urlsafe_decode64 key
17
+ end
18
+ end
19
+
20
+ def decode_raw_transaction(hex)
21
+ MixinBot::Transaction.new(hex:).decode.to_h
22
+ end
23
+
24
+ def decode_uint16(bytes)
25
+ raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
26
+
27
+ bytes.reverse.pack('C*').unpack1('S*')
28
+ end
29
+
30
+ def decode_uint32(bytes)
31
+ raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
32
+
33
+ bytes.reverse.pack('C*').unpack1('L*')
34
+ end
35
+
36
+ def decode_uint64(bytes)
37
+ raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
38
+
39
+ bytes.reverse.pack('C*').unpack1('Q*')
40
+ end
41
+
42
+ def decode_int(bytes)
43
+ int = 0
44
+ bytes.each do |byte|
45
+ int = (int * (2**8)) + byte
46
+ end
47
+
48
+ int
49
+ end
50
+
51
+ def hex_to_uuid(hex)
52
+ MixinBot::UUID.new(hex:).unpacked
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ module Utils
5
+ module Encoder
6
+ def encode_raw_transaction(txn)
7
+ if txn.is_a? String
8
+ begin
9
+ txn = JSON.parse txn
10
+ rescue JSON::ParserError
11
+ txn
12
+ end
13
+ end
14
+
15
+ raise ArgumentError, "#{txn} is not a valid json" unless txn.is_a? Hash
16
+
17
+ txn = txn.with_indifferent_access
18
+
19
+ MixinBot::Transaction.new(**txn).encode.hex
20
+ end
21
+
22
+ def encode_uint16(int)
23
+ raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
24
+
25
+ [int].pack('S*').bytes.reverse
26
+ end
27
+
28
+ def encode_uint32(int)
29
+ raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
30
+
31
+ [int].pack('L*').bytes.reverse
32
+ end
33
+
34
+ def encode_uint64(int)
35
+ raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
36
+
37
+ [int].pack('Q*').bytes.reverse
38
+ end
39
+
40
+ def encode_int(int)
41
+ raise ArgumentError, 'not integer' unless int.is_a?(Integer)
42
+
43
+ bytes = []
44
+ loop do
45
+ break if int.zero?
46
+
47
+ bytes.push int & 255
48
+ int = (int / (2**8)) | 0
49
+ end
50
+
51
+ bytes.reverse
52
+ end
53
+
54
+ def nft_memo(collection, token, extra)
55
+ MixinBot::Nfo.new(
56
+ collection:,
57
+ token:,
58
+ extra:
59
+ ).mint_memo
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,116 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './utils/nfo'
4
- require_relative './utils/uuid'
5
- require_relative './utils/transaction'
3
+ require_relative 'utils/address'
4
+ require_relative 'utils/crypto'
5
+ require_relative 'utils/decoder'
6
+ require_relative 'utils/encoder'
6
7
 
7
8
  module MixinBot
8
9
  module Utils
9
- class << self
10
- MAGIC = [0x77, 0x77].freeze
11
- TX_VERSION = 2
12
- MAX_ENCODE_INT = 0xFFFF
13
- NULL_BYTES = [0x00, 0x00].freeze
14
- AGGREGATED_SIGNATURE_PREFIX = 0xFF01
15
- AGGREGATED_SIGNATURE_ORDINAY_MASK = [0x00].freeze
16
- AGGREGATED_SIGNATURE_SPARSE_MASK = [0x01].freeze
17
-
18
- def generate_unique_uuid(uuid_1, uuid_2)
19
- md5 = Digest::MD5.new
20
- md5 << [uuid_1, uuid_2].min
21
- md5 << [uuid_1, uuid_2].max
22
- digest = md5.digest
23
- digest6 = (digest[6].ord & 0x0f | 0x30).chr
24
- digest8 = (digest[8].ord & 0x3f | 0x80).chr
25
- cipher = digest[0...6] + digest6 + digest[7] + digest8 + digest[9..]
26
-
27
- UUID.new(raw: cipher).unpacked
28
- end
29
-
30
- def unique_uuid(*uuids)
31
- uuids.sort
32
- r = uuids.first
33
- uuids.each_with_index do |uuid, i|
34
- r = MixinBot::Utils.generate_unique_uuid(r, uuid) if i.positive?
35
- end
36
-
37
- r
38
- end
39
-
40
- def generate_trace_from_hash(hash, output_index = 0)
41
- md5 = Digest::MD5.new
42
- md5 << hash
43
- md5 << [output_index].pack('c*') if output_index.positive? && output_index < 256
44
- digest = md5.digest
45
- digest[6] = ((digest[6].ord & 0x0f) | 0x30).chr
46
- digest[8] = ((digest[8].ord & 0x3f) | 0x80).chr
47
-
48
- UUID.new(raw: digest).unpacked
49
- end
50
-
51
- def hex_to_uuid(hex)
52
- UUID.new(hex: hex).unpacked
53
- end
54
-
55
- def sign_raw_transaction(tx)
56
- tx = JSON.parse tx if tx.is_a? String
57
- raise ArgumentError, "#{tx} is not a valid json" unless tx.is_a? Hash
58
-
59
- tx = tx.with_indifferent_access
60
-
61
- Transaction.new(
62
- asset: tx[:asset],
63
- inputs: tx[:inputs],
64
- outputs: tx[:outputs],
65
- extra: tx[:extra]
66
- ).encode.hex
67
- end
68
-
69
- def decode_raw_transaction(hex)
70
- Transaction.new(hex: hex).decode.to_h
71
- end
72
-
73
- def nft_memo(collection, token, extra)
74
- MixinBot::Utils::Nfo.new(
75
- collection: collection,
76
- token: token,
77
- extra: extra
78
- ).mint_memo
79
- end
80
-
81
- def encode_int(int)
82
- raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
83
- raise ArgumentError, "int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
84
-
85
- [int].pack('S*').bytes.reverse
86
- end
87
-
88
- def encode_unit_64(int)
89
- [int].pack('Q*').bytes.reverse
90
- end
91
-
92
- def bytes_of(int)
93
- raise ArgumentError, 'not integer' unless int.is_a?(Integer)
94
-
95
- bytes = []
96
- loop do
97
- break if int === 0
98
-
99
- bytes.push int & 255
100
- int = int / (2**8) | 0
101
- end
102
-
103
- bytes.reverse
104
- end
105
-
106
- def bytes_to_int(bytes)
107
- int = 0
108
- bytes.each do |byte|
109
- int = int * (2**8) + byte
110
- end
111
-
112
- int
113
- end
114
- end
10
+ extend MixinBot::Utils::Address
11
+ extend MixinBot::Utils::Crypto
12
+ extend MixinBot::Utils::Decoder
13
+ extend MixinBot::Utils::Encoder
115
14
  end
116
15
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class UUID
5
+ attr_accessor :hex, :raw
6
+
7
+ def initialize(**args)
8
+ @hex = args[:hex]
9
+ @raw = args[:raw]
10
+
11
+ raise MixinBot::InvalidUuidFormatError if raw.present? && raw.size != 16
12
+ raise MixinBot::InvalidUuidFormatError if hex.present? && hex.gsub('-', '').size != 32
13
+ end
14
+
15
+ def packed
16
+ if raw.present?
17
+ raw
18
+ elsif hex.present?
19
+ [hex.gsub('-', '')].pack('H*')
20
+ end
21
+ end
22
+
23
+ def unpacked
24
+ _hex =
25
+ if hex.present?
26
+ hex.gsub('-', '')
27
+ elsif raw.present?
28
+ _hex = raw.unpack1('H*')
29
+ end
30
+
31
+ format(
32
+ '%<first>s-%<second>s-%<third>s-%<forth>s-%<fifth>s',
33
+ first: _hex[0..7],
34
+ second: _hex[8..11],
35
+ third: _hex[12..15],
36
+ forth: _hex[16..19],
37
+ fifth: _hex[20..]
38
+ )
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MixinBot
4
- VERSION = '0.12.1'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/mixin_bot.rb CHANGED
@@ -3,12 +3,16 @@
3
3
  # third-party dependencies
4
4
  require 'English'
5
5
  require 'active_support/all'
6
+ require 'base58'
6
7
  require 'base64'
7
8
  require 'bigdecimal'
8
9
  require 'bigdecimal/util'
9
10
  require 'digest'
11
+ require 'digest/blake3'
10
12
  require 'faye/websocket'
11
- require 'http'
13
+ require 'faraday'
14
+ require 'faraday/multipart'
15
+ require 'faraday/retry'
12
16
  require 'jose'
13
17
  require 'msgpack'
14
18
  require 'open3'
@@ -16,19 +20,38 @@ require 'openssl'
16
20
  require 'rbnacl'
17
21
  require 'sha3'
18
22
 
19
- require_relative './mixin_bot/api'
20
- require_relative './mixin_bot/cli'
21
- require_relative './mixin_bot/utils'
22
- require_relative './mixin_bot/version'
23
- require_relative './mvm'
23
+ require_relative 'mixin_bot/api'
24
+ require_relative 'mixin_bot/cli'
25
+ require_relative 'mixin_bot/utils'
26
+ require_relative 'mixin_bot/nfo'
27
+ require_relative 'mixin_bot/uuid'
28
+ require_relative 'mixin_bot/transaction'
29
+ require_relative 'mixin_bot/version'
30
+ require_relative 'mvm'
24
31
 
25
32
  module MixinBot
26
- class<< self
27
- attr_accessor :client_id, :client_secret, :session_id, :pin_token, :private_key, :scope, :api_host, :blaze_host
28
- end
33
+ class << self
34
+ def api
35
+ return @api if defined?(@api)
36
+
37
+ @api = MixinBot::API.new
38
+ @api
39
+ end
40
+
41
+ def config
42
+ return @config if defined?(@config)
43
+
44
+ @config = MixinBot::Configuration.new
45
+ @config
46
+ end
47
+
48
+ def configure(&)
49
+ config.instance_exec(&)
50
+ end
29
51
 
30
- def self.api
31
- @api ||= MixinBot::API.new
52
+ def utils
53
+ MixinBot::Utils
54
+ end
32
55
  end
33
56
 
34
57
  class Error < StandardError; end
@@ -37,12 +60,14 @@ module MixinBot
37
60
  class RequestError < Error; end
38
61
  class ResponseError < Error; end
39
62
  class NotFoundError < Error; end
63
+ class UserNotFoundError < Error; end
40
64
  class UnauthorizedError < Error; end
41
65
  class ForbiddenError < Error; end
42
66
  class InsufficientBalanceError < Error; end
43
67
  class InsufficientPoolError < Error; end
44
68
  class PinError < Error; end
45
- class InvalidNfoFormatError < MixinBot::Error; end
46
- class InvalidUuidFormatError < MixinBot::Error; end
47
- class InvalidTransactionFormatError < MixinBot::Error; end
69
+ class InvalidNfoFormatError < Error; end
70
+ class InvalidUuidFormatError < Error; end
71
+ class InvalidTransactionFormatError < Error; end
72
+ class ConfigurationNotValidError < Error; end
48
73
  end
data/lib/mvm/bridge.rb CHANGED
@@ -16,27 +16,10 @@ module MVM
16
16
  path = '/users'
17
17
 
18
18
  payload = {
19
- public_key: public_key
19
+ public_key:
20
20
  }
21
21
 
22
- client.post path, json: payload
23
- end
24
-
25
- def extra(receivers: [], threshold: 1, extra: '')
26
- return if receivers.blank?
27
-
28
- path = '/extra'
29
-
30
- payload = {
31
- receivers: receivers,
32
- threshold: threshold,
33
- extra: extra
34
- }
35
-
36
- client.post path, json: payload
37
- end
38
-
39
- def mirror
22
+ client.post path, **payload
40
23
  end
41
24
  end
42
25
  end
data/lib/mvm/client.rb CHANGED
@@ -8,43 +8,21 @@ module MVM
8
8
 
9
9
  def initialize(host)
10
10
  @host = host
11
- end
12
-
13
- def get(path, options = {})
14
- request(:get, path, options)
15
- end
16
-
17
- def post(path, options = {})
18
- request(:post, path, options)
19
- end
20
-
21
- private
22
-
23
- def request(verb, path, options = {})
24
- uri = uri_for path
25
-
26
- options[:headers] ||= {}
27
- options[:headers]['Content-Type'] ||= 'application/json'
28
-
29
- begin
30
- response = HTTP.timeout(connect: 5, write: 5, read: 5).request(verb, uri, options)
31
- rescue HTTP::Error => e
32
- raise HttpError, e.message
11
+ @conn = Faraday.new(url: "#{SERVER_SCHEME}://#{host}") do |f|
12
+ f.request :json
13
+ f.request :retry
14
+ f.response :raise_error
15
+ f.response :logger
16
+ f.response :json
33
17
  end
18
+ end
34
19
 
35
- raise ResponseError, response.to_s if response.status.server_error?
36
- return response.status.to_s unless response.status.success?
37
-
38
- JSON.parse(response.body.to_s)
20
+ def get(path, **)
21
+ @conn.get(path, **).body
39
22
  end
40
23
 
41
- def uri_for(path)
42
- uri_options = {
43
- scheme: SERVER_SCHEME,
44
- host: host,
45
- path: path
46
- }
47
- Addressable::URI.new(uri_options)
24
+ def post(path, **)
25
+ @conn.post(path, **).body
48
26
  end
49
27
  end
50
28
  end