mixin_bot 2.3.0 → 2.4.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/AGENTS.md +1 -1
- data/CHANGELOG.md +19 -0
- data/README.md +1 -1
- data/lib/mixin_bot/address.rb +7 -7
- data/lib/mixin_bot/api/address.rb +1 -1
- data/lib/mixin_bot/api/blaze.rb +1 -1
- data/lib/mixin_bot/api/encrypted_message.rb +6 -6
- data/lib/mixin_bot/api/legacy_transaction.rb +1 -1
- data/lib/mixin_bot/api/legacy_user.rb +7 -4
- data/lib/mixin_bot/api/payment.rb +3 -0
- data/lib/mixin_bot/api/transaction.rb +1 -1
- data/lib/mixin_bot/api/user.rb +2 -2
- data/lib/mixin_bot/invoice.rb +13 -13
- data/lib/mixin_bot/nfo.rb +18 -18
- data/lib/mixin_bot/transaction/decoder.rb +1 -1
- data/lib/mixin_bot/transaction/encoder.rb +73 -76
- data/lib/mixin_bot/utils/address.rb +1 -1
- data/lib/mixin_bot/version.rb +1 -1
- data/lib/mvm/registry.rb +3 -3
- data/llms.txt +1 -1
- metadata +5 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06d699c83730cba7bfd93dedbc47f96b1d97c1cd2d4816babd0d310effce3f99
|
|
4
|
+
data.tar.gz: 8ac62f6fbd15d257d3fae717eb3e3ae1fbca0a46caf9f7f6a03b81797ec7fdf7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ded5aea75050e8ea99a1fd95de4473229fd0dc8c38fa819b01f039ed1917b791e4ccb8183e519521973447b82f909a333f467b438aacd1a9e50f8dff679ea85d
|
|
7
|
+
data.tar.gz: e4041c6a976a0127cb4d2740cda7315f5c16d72cd6edc0b0ca54f978fac8ce64d33d464e730863181473d361b1f5b69d98ae0903e746f5fea0ba979db8d0e354
|
data/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTS.md — MixinBot
|
|
2
2
|
|
|
3
|
-
Ruby gem (v2.
|
|
3
|
+
Ruby gem (v2.3.0): Mixin Network REST SDK + `mixinbot` CLI. Parity targets: [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client), [bot-api-nodejs-client](https://github.com/MixinNetwork/bot-api-nodejs-client).
|
|
4
4
|
|
|
5
5
|
## Commands
|
|
6
6
|
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.4.0] - 2026-07-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Blaze WebSocket User-Agent** — the client now sends a `User-Agent` header on Blaze WebSocket connections.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **`sha3` dependency** — upgraded from `~> 1.0` to `~> 2.2` (sha3 2.x renames `SHA3::Digest::SHA256` to `SHA3::Digest::SHA3_256`; hash output is unchanged).
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Payment URL formatting** — `safe_pay_url` now formats amounts without scientific notation for very small or large values.
|
|
23
|
+
|
|
24
|
+
### Performance
|
|
25
|
+
|
|
26
|
+
- **Encoder allocations** — replaced `Array` splat patterns with `Array#concat` in `Transaction::Encoder`, `Nfo`, `InvoiceEntry`, `EncryptedMessage`, and `MVM::Registry` to avoid O(n²) reallocation.
|
|
27
|
+
- **Bytes pack caching** — cached `bytes.pack` results in transaction encoders to reduce duplicate allocations.
|
|
28
|
+
|
|
10
29
|
## [2.3.0] - 2026-05-27
|
|
11
30
|
|
|
12
31
|
### Added
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Ruby SDK and CLI for [Mixin Network](https://developers.mixin.one/docs): authent
|
|
|
6
6
|
|
|
7
7
|
The gem aims for **parity with the official [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client)** Go SDK and **[bot-api-nodejs-client](https://github.com/MixinNetwork/bot-api-nodejs-client)** Node SDK. See [API_COVERAGE.md](API_COVERAGE.md) for the full mapping; run `rake mixin_bot:api_coverage` to confirm no gaps are marked missing.
|
|
8
8
|
|
|
9
|
-
Current gem version: **2.
|
|
9
|
+
Current gem version: **2.3.0** (see [CHANGELOG.md](CHANGELOG.md) for breaking changes and deprecations).
|
|
10
10
|
|
|
11
11
|
## Requirements
|
|
12
12
|
|
data/lib/mixin_bot/address.rb
CHANGED
|
@@ -109,7 +109,7 @@ module MixinBot
|
|
|
109
109
|
|
|
110
110
|
self.payload = prefix + msg
|
|
111
111
|
|
|
112
|
-
checksum = SHA3::Digest::
|
|
112
|
+
checksum = SHA3::Digest::SHA3_256.digest(MIX_ADDRESS_PREFIX + payload)
|
|
113
113
|
data = payload + checksum[0...4]
|
|
114
114
|
data = Base58.binary_to_base58 data, :bitcoin
|
|
115
115
|
self.address = "#{MIX_ADDRESS_PREFIX}#{data}"
|
|
@@ -126,10 +126,10 @@ module MixinBot
|
|
|
126
126
|
raise ArgumentError, 'invalid address, length invalid' if data.length < 3 + 16 + 4
|
|
127
127
|
|
|
128
128
|
self.payload = data[...-4]
|
|
129
|
-
checksum = SHA3::Digest::
|
|
129
|
+
checksum = SHA3::Digest::SHA3_256.digest(MIX_ADDRESS_PREFIX + payload)[0...4]
|
|
130
130
|
raise ArgumentError, 'invalid address, checksum invalid' unless checksum == data[-4..]
|
|
131
131
|
else
|
|
132
|
-
checksum = SHA3::Digest::
|
|
132
|
+
checksum = SHA3::Digest::SHA3_256.digest(MIX_ADDRESS_PREFIX + payload)[0...4]
|
|
133
133
|
data = payload + checksum
|
|
134
134
|
data = Base58.binary_to_base58 data, :bitcoin
|
|
135
135
|
self.address = "#{MIX_ADDRESS_PREFIX}#{data}"
|
|
@@ -171,7 +171,7 @@ module MixinBot
|
|
|
171
171
|
|
|
172
172
|
def encode
|
|
173
173
|
msg = MAIN_ADDRESS_PREFIX + public_key
|
|
174
|
-
checksum = SHA3::Digest::
|
|
174
|
+
checksum = SHA3::Digest::SHA3_256.digest msg
|
|
175
175
|
data = public_key + checksum[0...4]
|
|
176
176
|
base58 = Base58.binary_to_base58 data, :bitcoin
|
|
177
177
|
self.address = "#{MAIN_ADDRESS_PREFIX}#{base58}"
|
|
@@ -188,7 +188,7 @@ module MixinBot
|
|
|
188
188
|
payload = data[...-4]
|
|
189
189
|
|
|
190
190
|
msg = MAIN_ADDRESS_PREFIX + payload
|
|
191
|
-
checksum = SHA3::Digest::
|
|
191
|
+
checksum = SHA3::Digest::SHA3_256.digest msg
|
|
192
192
|
|
|
193
193
|
raise ArgumentError, 'invalid address' unless checksum[0...4] == data[-4..]
|
|
194
194
|
|
|
@@ -200,8 +200,8 @@ module MixinBot
|
|
|
200
200
|
def self.burning_address
|
|
201
201
|
seed = "\0" * 64
|
|
202
202
|
|
|
203
|
-
digest1 = SHA3::Digest::
|
|
204
|
-
digest2 = SHA3::Digest::
|
|
203
|
+
digest1 = SHA3::Digest::SHA3_256.digest seed
|
|
204
|
+
digest2 = SHA3::Digest::SHA3_256.digest digest1
|
|
205
205
|
src = digest1 + digest2
|
|
206
206
|
|
|
207
207
|
spend_key = MixinBot::Utils.shared_public_key(seed)
|
data/lib/mixin_bot/api/blaze.rb
CHANGED
|
@@ -10,7 +10,7 @@ module MixinBot
|
|
|
10
10
|
Faye::WebSocket::Client.new(
|
|
11
11
|
format('wss://%<host>s/', host: config.blaze_host),
|
|
12
12
|
['Mixin-Blaze-1'],
|
|
13
|
-
headers: { 'Authorization' => authorization },
|
|
13
|
+
headers: { 'Authorization' => authorization, 'User-Agent' => "mixin_bot/#{MixinBot::VERSION}" },
|
|
14
14
|
ping: 60
|
|
15
15
|
)
|
|
16
16
|
end
|
|
@@ -126,8 +126,8 @@ module MixinBot
|
|
|
126
126
|
ciphertext = encrypter.update(Base64.urlsafe_decode64(data)) + encrypter.final + encrypter.auth_tag
|
|
127
127
|
|
|
128
128
|
bytes = [1]
|
|
129
|
-
bytes
|
|
130
|
-
bytes
|
|
129
|
+
bytes.concat([sessions.size].pack('v*').bytes)
|
|
130
|
+
bytes.concat(JOSE::JWA::Ed25519.pk_to_curve25519(pk).bytes)
|
|
131
131
|
|
|
132
132
|
sessions.each do |session|
|
|
133
133
|
aes_key = JOSE::JWA::X25519.shared_secret(
|
|
@@ -143,12 +143,12 @@ module MixinBot
|
|
|
143
143
|
iv = encrypter.random_iv
|
|
144
144
|
encrypter.iv = iv
|
|
145
145
|
|
|
146
|
-
bytes
|
|
147
|
-
bytes
|
|
146
|
+
bytes.concat((MixinBot::UUID.new(hex: session['session_id']).packed + iv).bytes)
|
|
147
|
+
bytes.concat(encrypter.update(key + padtext).bytes)
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
-
bytes
|
|
151
|
-
bytes
|
|
150
|
+
bytes.concat(nounce.bytes)
|
|
151
|
+
bytes.concat(ciphertext.bytes)
|
|
152
152
|
|
|
153
153
|
Base64.urlsafe_encode64 bytes.pack('C*'), padding: false
|
|
154
154
|
end
|
|
@@ -10,12 +10,15 @@ module MixinBot
|
|
|
10
10
|
kl = keystore.with_indifferent_access
|
|
11
11
|
priv = OpenSSL::PKey::RSA.new(kl[:private_key])
|
|
12
12
|
token = Base64.decode64(kl[:pin_token])
|
|
13
|
+
# Use string keys with the dash form (`'oaep-label'`) for Ruby 4.0
|
|
14
|
+
# compatibility; the underscore form `oaep_label` is no longer
|
|
15
|
+
# recognized by the OpenSSL EVP_PKEY_CTX in Ruby 4.0.
|
|
13
16
|
key_bytes = priv.decrypt(
|
|
14
17
|
token,
|
|
15
|
-
rsa_padding_mode
|
|
16
|
-
rsa_oaep_md
|
|
17
|
-
rsa_mgf1_md
|
|
18
|
-
|
|
18
|
+
'rsa_padding_mode' => 'oaep',
|
|
19
|
+
'rsa_oaep_md' => 'sha256',
|
|
20
|
+
'rsa_mgf1_md' => 'sha1',
|
|
21
|
+
'oaep-label' => kl[:session_id]
|
|
19
22
|
)
|
|
20
23
|
|
|
21
24
|
pin_byte = kl[:pin].to_s.b
|
|
@@ -12,6 +12,9 @@ module MixinBot
|
|
|
12
12
|
trace_id = kwargs[:trace_id] || SecureRandom.uuid
|
|
13
13
|
|
|
14
14
|
mix_address = MixinBot.utils.build_mix_address(members:, threshold:)
|
|
15
|
+
# Format amount without scientific notation so the Mixin web UI's URL
|
|
16
|
+
# parser accepts it. Mirrors `MixinBot::Utils::Address#build_safe_recipient`.
|
|
17
|
+
amount = format('%.8f', amount.to_d.to_r).gsub(/\.?0+\z/, '')
|
|
15
18
|
|
|
16
19
|
"https://mixin.one/pay/#{mix_address}?amount=#{amount}&asset=#{asset_id}&memo=#{memo}&trace=#{trace_id}"
|
|
17
20
|
end
|
|
@@ -138,7 +138,7 @@ module MixinBot
|
|
|
138
138
|
aid = h[:asset_id]
|
|
139
139
|
raise ArgumentError, 'utxo asset_id or asset is required' if aid.blank?
|
|
140
140
|
|
|
141
|
-
SHA3::Digest::
|
|
141
|
+
SHA3::Digest::SHA3_256.hexdigest(aid)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
asset = mixin_asset_for.call(utxos[0])
|
data/lib/mixin_bot/api/user.rb
CHANGED
|
@@ -164,9 +164,9 @@ module MixinBot
|
|
|
164
164
|
signing_key_hex = keypair[1].unpack1('H*')
|
|
165
165
|
|
|
166
166
|
# NOTE: the Go SDK's +crypto.Sha256Hash+ is misleadingly named — it
|
|
167
|
-
# actually computes SHA3-256, so +SHA3::Digest::
|
|
167
|
+
# actually computes SHA3-256, so +SHA3::Digest::SHA3_256+ is the correct
|
|
168
168
|
# match. See bot-api-go-client safe_user.go +RegisterSafeBareUser+.
|
|
169
|
-
app_id_hash = SHA3::Digest::
|
|
169
|
+
app_id_hash = SHA3::Digest::SHA3_256.hexdigest config.app_id
|
|
170
170
|
signature = Base64.urlsafe_encode64(
|
|
171
171
|
JOSE::JWA::Ed25519.sign([app_id_hash].pack('H*'), keypair[1]),
|
|
172
172
|
padding: false
|
data/lib/mixin_bot/invoice.rb
CHANGED
|
@@ -54,7 +54,7 @@ module MixinBot
|
|
|
54
54
|
payload = payload.pack('C*')
|
|
55
55
|
|
|
56
56
|
# Calculate checksum
|
|
57
|
-
checksum = SHA3::Digest::
|
|
57
|
+
checksum = SHA3::Digest::SHA3_256.digest(INVOICE_PREFIX + payload)[0...4]
|
|
58
58
|
|
|
59
59
|
# Combine everything and encode to base64
|
|
60
60
|
self.address = INVOICE_PREFIX + Base64.urlsafe_encode64(payload + checksum, padding: false)
|
|
@@ -68,7 +68,7 @@ module MixinBot
|
|
|
68
68
|
raise MixinBot::InvalidInvoiceFormatError, 'invalid invoice payload size' if data.size < 3 + 23 + 1
|
|
69
69
|
|
|
70
70
|
payload = data[...-4]
|
|
71
|
-
checksum = SHA3::Digest::
|
|
71
|
+
checksum = SHA3::Digest::SHA3_256.digest(INVOICE_PREFIX + payload)[0...4]
|
|
72
72
|
raise MixinBot::InvalidInvoiceFormatError, 'invalid invoice checksum' unless checksum == data[-4..]
|
|
73
73
|
|
|
74
74
|
payload = payload.bytes
|
|
@@ -141,29 +141,29 @@ module MixinBot
|
|
|
141
141
|
def encode
|
|
142
142
|
bytes = []
|
|
143
143
|
|
|
144
|
-
bytes
|
|
145
|
-
bytes
|
|
144
|
+
bytes.concat MixinBot::UUID.new(hex: trace_id).packed.bytes
|
|
145
|
+
bytes.concat MixinBot::UUID.new(hex: asset_id).packed.bytes
|
|
146
146
|
|
|
147
147
|
amount_string = amount.to_d.to_s('F')
|
|
148
148
|
amount_bytes = amount_string.bytes
|
|
149
|
-
bytes
|
|
150
|
-
bytes
|
|
149
|
+
bytes.concat MixinBot.utils.encode_int(amount_bytes.size)
|
|
150
|
+
bytes.concat amount_bytes
|
|
151
151
|
|
|
152
152
|
extra_bytes = extra.bytes
|
|
153
|
-
bytes
|
|
154
|
-
bytes
|
|
153
|
+
bytes.concat MixinBot.utils.encode_uint16(extra_bytes.size)
|
|
154
|
+
bytes.concat extra_bytes
|
|
155
155
|
|
|
156
156
|
references_count = (index_references || []).size + (hash_references || []).size
|
|
157
|
-
bytes
|
|
157
|
+
bytes.concat MixinBot.utils.encode_int(references_count)
|
|
158
158
|
|
|
159
159
|
index_references&.each do |index|
|
|
160
|
-
bytes
|
|
161
|
-
bytes
|
|
160
|
+
bytes.concat MixinBot.utils.encode_int(1)
|
|
161
|
+
bytes.concat MixinBot.utils.encode_int(index)
|
|
162
162
|
end
|
|
163
163
|
|
|
164
164
|
hash_references&.each do |hash|
|
|
165
|
-
bytes
|
|
166
|
-
bytes
|
|
165
|
+
bytes.concat MixinBot.utils.encode_int(0)
|
|
166
|
+
bytes.concat [hash].pack('H*').bytes
|
|
167
167
|
end
|
|
168
168
|
|
|
169
169
|
bytes
|
data/lib/mixin_bot/nfo.rb
CHANGED
|
@@ -39,10 +39,10 @@ module MixinBot
|
|
|
39
39
|
|
|
40
40
|
def unique_token_id
|
|
41
41
|
bytes = []
|
|
42
|
-
bytes
|
|
43
|
-
bytes
|
|
44
|
-
bytes
|
|
45
|
-
bytes
|
|
42
|
+
bytes.concat MixinBot::UUID.new(hex: chain).packed.bytes
|
|
43
|
+
bytes.concat [nm_class].pack('H*').bytes
|
|
44
|
+
bytes.concat MixinBot::UUID.new(hex: collection).packed.bytes
|
|
45
|
+
bytes.concat MixinBot.utils.encode_int token
|
|
46
46
|
|
|
47
47
|
md5 = Digest::MD5.new
|
|
48
48
|
md5.update bytes.pack('c*')
|
|
@@ -67,33 +67,33 @@ module MixinBot
|
|
|
67
67
|
def encode
|
|
68
68
|
bytes = []
|
|
69
69
|
|
|
70
|
-
bytes
|
|
71
|
-
bytes
|
|
70
|
+
bytes.concat prefix.bytes
|
|
71
|
+
bytes.push(version)
|
|
72
72
|
|
|
73
73
|
if mask.zero?
|
|
74
|
-
bytes
|
|
74
|
+
bytes << 0
|
|
75
75
|
else
|
|
76
|
-
bytes
|
|
77
|
-
bytes
|
|
78
|
-
bytes
|
|
76
|
+
bytes << 1
|
|
77
|
+
bytes.concat MixinBot.utils.encode_uint64 mask
|
|
78
|
+
bytes.concat MixinBot::UUID.new(hex: chain).packed.bytes
|
|
79
79
|
|
|
80
80
|
class_bytes = [nm_class].pack('H*').bytes
|
|
81
|
-
bytes
|
|
82
|
-
bytes
|
|
81
|
+
bytes.concat MixinBot.utils.encode_int class_bytes.size
|
|
82
|
+
bytes.concat class_bytes
|
|
83
83
|
|
|
84
84
|
collection_bytes = collection.split('-').pack('H* H* H* H* H*').bytes
|
|
85
|
-
bytes
|
|
86
|
-
bytes
|
|
85
|
+
bytes.concat MixinBot.utils.encode_int collection_bytes.size
|
|
86
|
+
bytes.concat collection_bytes
|
|
87
87
|
|
|
88
88
|
# token_bytes = memo[:token].split('-').pack('H* H* H* H* H*').bytes
|
|
89
89
|
token_bytes = MixinBot.utils.encode_int token
|
|
90
|
-
bytes
|
|
91
|
-
bytes
|
|
90
|
+
bytes.concat MixinBot.utils.encode_int token_bytes.size
|
|
91
|
+
bytes.concat token_bytes
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
extra_bytes = [extra].pack('H*').bytes
|
|
95
|
-
bytes
|
|
96
|
-
bytes
|
|
95
|
+
bytes.concat MixinBot.utils.encode_int extra_bytes.size
|
|
96
|
+
bytes.concat extra_bytes
|
|
97
97
|
|
|
98
98
|
@raw = bytes.pack('C*')
|
|
99
99
|
@hex = raw.unpack1('H*')
|
|
@@ -17,28 +17,25 @@ module MixinBot
|
|
|
17
17
|
|
|
18
18
|
bytes = []
|
|
19
19
|
|
|
20
|
-
bytes
|
|
21
|
-
bytes
|
|
22
|
-
bytes
|
|
20
|
+
bytes.concat Transaction::MAGIC
|
|
21
|
+
bytes.push(0, @tx.version)
|
|
22
|
+
bytes.concat [@tx.asset].pack('H*').bytes
|
|
23
23
|
|
|
24
|
-
bytes
|
|
25
|
-
bytes
|
|
26
|
-
bytes
|
|
24
|
+
bytes.concat encode_inputs
|
|
25
|
+
bytes.concat encode_outputs
|
|
26
|
+
bytes.concat encode_references if @tx.version >= Transaction::REFERENCES_TX_VERSION
|
|
27
27
|
|
|
28
28
|
extra_bytes = @tx.extra.bytes
|
|
29
29
|
raise InvalidTransactionFormatError, 'extra is too long' if extra_bytes.size > Transaction::MAX_EXTRA_SIZE
|
|
30
30
|
|
|
31
|
-
bytes
|
|
32
|
-
bytes
|
|
31
|
+
bytes.concat MixinBot.utils.encode_uint32 extra_bytes.size
|
|
32
|
+
bytes.concat extra_bytes
|
|
33
33
|
|
|
34
|
-
bytes
|
|
35
|
-
encode_signatures
|
|
36
|
-
else
|
|
37
|
-
encode_aggregated_signature
|
|
38
|
-
end
|
|
34
|
+
bytes.concat(@tx.aggregated.nil? ? encode_signatures : encode_aggregated_signature)
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
@tx.
|
|
36
|
+
packed = bytes.pack('C*')
|
|
37
|
+
@tx.hash = SHA3::Digest::SHA3_256.hexdigest packed
|
|
38
|
+
@tx.hex = packed.unpack1('H*')
|
|
42
39
|
|
|
43
40
|
@tx
|
|
44
41
|
end
|
|
@@ -48,63 +45,63 @@ module MixinBot
|
|
|
48
45
|
def encode_inputs
|
|
49
46
|
bytes = []
|
|
50
47
|
|
|
51
|
-
bytes
|
|
48
|
+
bytes.concat MixinBot.utils.encode_uint16(@tx.inputs.size)
|
|
52
49
|
|
|
53
50
|
@tx.inputs.each do |input|
|
|
54
|
-
bytes
|
|
55
|
-
bytes
|
|
51
|
+
bytes.concat [input['hash']].pack('H*').bytes
|
|
52
|
+
bytes.concat MixinBot.utils.encode_uint16(input['index'])
|
|
56
53
|
|
|
57
54
|
genesis = input['genesis'] || ''
|
|
58
55
|
if genesis.empty?
|
|
59
|
-
bytes
|
|
56
|
+
bytes.concat Transaction::NULL_BYTES
|
|
60
57
|
else
|
|
61
58
|
genesis_bytes = [genesis].pack('H*').bytes
|
|
62
|
-
bytes
|
|
63
|
-
bytes
|
|
59
|
+
bytes.concat MixinBot.utils.encode_uint16 genesis_bytes.size
|
|
60
|
+
bytes.concat genesis_bytes
|
|
64
61
|
end
|
|
65
62
|
|
|
66
63
|
deposit = input['deposit']
|
|
67
64
|
if deposit.nil?
|
|
68
|
-
bytes
|
|
65
|
+
bytes.concat Transaction::NULL_BYTES
|
|
69
66
|
else
|
|
70
|
-
bytes
|
|
71
|
-
bytes
|
|
67
|
+
bytes.concat Transaction::MAGIC
|
|
68
|
+
bytes.concat [deposit['chain']].pack('H*').bytes
|
|
72
69
|
|
|
73
70
|
asset_bytes = [deposit['asset']].pack('H*')
|
|
74
|
-
bytes
|
|
75
|
-
bytes
|
|
71
|
+
bytes.concat MixinBot.utils.encode_uint16 asset_bytes.size
|
|
72
|
+
bytes.concat asset_bytes.bytes
|
|
76
73
|
|
|
77
74
|
transaction_bytes = [deposit['transaction']].pack('H*')
|
|
78
|
-
bytes
|
|
79
|
-
bytes
|
|
75
|
+
bytes.concat MixinBot.utils.encode_uint16 transaction_bytes.size
|
|
76
|
+
bytes.concat transaction_bytes.bytes
|
|
80
77
|
|
|
81
|
-
bytes
|
|
78
|
+
bytes.concat MixinBot.utils.encode_uint64 deposit['index']
|
|
82
79
|
|
|
83
80
|
amount_bytes = MixinBot.utils.bytes_of deposit['amount']
|
|
84
|
-
bytes
|
|
85
|
-
bytes
|
|
81
|
+
bytes.concat MixinBot.utils.encode_uint16 amount_bytes.size
|
|
82
|
+
bytes.concat amount_bytes
|
|
86
83
|
end
|
|
87
84
|
|
|
88
85
|
mint = input['mint']
|
|
89
86
|
if mint.nil?
|
|
90
|
-
bytes
|
|
87
|
+
bytes.concat Transaction::NULL_BYTES
|
|
91
88
|
else
|
|
92
|
-
bytes
|
|
89
|
+
bytes.concat Transaction::MAGIC
|
|
93
90
|
|
|
94
91
|
group = mint['group'] || ''
|
|
95
92
|
if group.empty?
|
|
96
|
-
bytes
|
|
93
|
+
bytes.concat Transaction::NULL_BYTES
|
|
97
94
|
else
|
|
98
95
|
group_bytes = [group].pack('H*')
|
|
99
|
-
bytes
|
|
100
|
-
bytes
|
|
96
|
+
bytes.concat MixinBot.utils.encode_uint16 group_bytes.size
|
|
97
|
+
bytes.concat group_bytes.bytes
|
|
101
98
|
end
|
|
102
99
|
|
|
103
|
-
bytes
|
|
100
|
+
bytes.concat MixinBot.utils.encode_uint64 mint['batch']
|
|
104
101
|
|
|
105
102
|
amount_bytes = MixinBot.utils.encode_int mint['amount']
|
|
106
|
-
bytes
|
|
107
|
-
bytes
|
|
103
|
+
bytes.concat MixinBot.utils.encode_uint16 amount_bytes.size
|
|
104
|
+
bytes.concat amount_bytes
|
|
108
105
|
end
|
|
109
106
|
end
|
|
110
107
|
|
|
@@ -114,55 +111,55 @@ module MixinBot
|
|
|
114
111
|
def encode_outputs
|
|
115
112
|
bytes = []
|
|
116
113
|
|
|
117
|
-
bytes
|
|
114
|
+
bytes.concat MixinBot.utils.encode_uint16 @tx.outputs.size
|
|
118
115
|
|
|
119
116
|
@tx.outputs.each do |output|
|
|
120
117
|
type = output['type'] || 0
|
|
121
|
-
bytes
|
|
118
|
+
bytes.push(0x00, type)
|
|
122
119
|
|
|
123
120
|
amount_bytes = MixinBot.utils.encode_int((output['amount'].to_d * 1e8).round)
|
|
124
|
-
bytes
|
|
125
|
-
bytes
|
|
121
|
+
bytes.concat MixinBot.utils.encode_uint16 amount_bytes.size
|
|
122
|
+
bytes.concat amount_bytes
|
|
126
123
|
|
|
127
|
-
bytes
|
|
124
|
+
bytes.concat MixinBot.utils.encode_uint16 output['keys'].size
|
|
128
125
|
output['keys'].each do |key|
|
|
129
|
-
bytes
|
|
126
|
+
bytes.concat [key].pack('H*').bytes
|
|
130
127
|
end
|
|
131
128
|
|
|
132
|
-
bytes
|
|
129
|
+
bytes.concat [output['mask']].pack('H*').bytes
|
|
133
130
|
|
|
134
131
|
script_bytes = [output['script']].pack('H*').bytes
|
|
135
|
-
bytes
|
|
136
|
-
bytes
|
|
132
|
+
bytes.concat MixinBot.utils.encode_uint16 script_bytes.size
|
|
133
|
+
bytes.concat script_bytes
|
|
137
134
|
|
|
138
135
|
withdrawal = output['withdrawal']
|
|
139
136
|
if withdrawal.nil?
|
|
140
|
-
bytes
|
|
137
|
+
bytes.concat Transaction::NULL_BYTES
|
|
141
138
|
else
|
|
142
|
-
bytes
|
|
139
|
+
bytes.concat Transaction::MAGIC
|
|
143
140
|
|
|
144
|
-
bytes
|
|
141
|
+
bytes.concat [withdrawal['chain']].pack('H*').bytes
|
|
145
142
|
|
|
146
143
|
asset_bytes = [withdrawal['asset']].pack('H*')
|
|
147
|
-
bytes
|
|
148
|
-
bytes
|
|
144
|
+
bytes.concat MixinBot.utils.encode_uint16 asset_bytes.bytesize
|
|
145
|
+
bytes.concat asset_bytes.bytes
|
|
149
146
|
|
|
150
147
|
address = withdrawal['address'] || ''
|
|
151
148
|
if address.empty?
|
|
152
|
-
bytes
|
|
149
|
+
bytes.concat Transaction::NULL_BYTES
|
|
153
150
|
else
|
|
154
151
|
address_bytes = [address].pack('H*').bytes
|
|
155
|
-
bytes
|
|
156
|
-
bytes
|
|
152
|
+
bytes.concat MixinBot.utils.encode_uint16 address_bytes.size
|
|
153
|
+
bytes.concat address_bytes
|
|
157
154
|
end
|
|
158
155
|
|
|
159
156
|
tag = withdrawal['tag'] || ''
|
|
160
157
|
if tag.empty?
|
|
161
|
-
bytes
|
|
158
|
+
bytes.concat Transaction::NULL_BYTES
|
|
162
159
|
else
|
|
163
160
|
tag_bytes = [tag].pack('H*').bytes
|
|
164
|
-
bytes
|
|
165
|
-
bytes
|
|
161
|
+
bytes.concat MixinBot.utils.encode_uint16 tag_bytes.size
|
|
162
|
+
bytes.concat tag_bytes
|
|
166
163
|
end
|
|
167
164
|
end
|
|
168
165
|
end
|
|
@@ -174,10 +171,10 @@ module MixinBot
|
|
|
174
171
|
bytes = []
|
|
175
172
|
|
|
176
173
|
references = Array(@tx.references)
|
|
177
|
-
bytes
|
|
174
|
+
bytes.concat MixinBot.utils.encode_uint16 references.size
|
|
178
175
|
|
|
179
176
|
references.each do |reference|
|
|
180
|
-
bytes
|
|
177
|
+
bytes.concat [reference].pack('H*').bytes
|
|
181
178
|
end
|
|
182
179
|
|
|
183
180
|
bytes
|
|
@@ -186,14 +183,14 @@ module MixinBot
|
|
|
186
183
|
def encode_aggregated_signature
|
|
187
184
|
bytes = []
|
|
188
185
|
|
|
189
|
-
bytes
|
|
190
|
-
bytes
|
|
191
|
-
bytes
|
|
186
|
+
bytes.concat MixinBot.utils.encode_uint16 Transaction::MAX_ENCODE_INT
|
|
187
|
+
bytes.concat MixinBot.utils.encode_uint16 Transaction::AGGREGATED_SIGNATURE_PREFIX
|
|
188
|
+
bytes.concat [@tx.aggregated['signature']].pack('H*').bytes
|
|
192
189
|
|
|
193
190
|
signers = @tx.aggregated['signers'] || []
|
|
194
191
|
if signers.empty?
|
|
195
|
-
bytes
|
|
196
|
-
bytes
|
|
192
|
+
bytes.concat Transaction::AGGREGATED_SIGNATURE_ORDINAY_MASK
|
|
193
|
+
bytes.concat Transaction::NULL_BYTES
|
|
197
194
|
return bytes
|
|
198
195
|
end
|
|
199
196
|
|
|
@@ -205,17 +202,17 @@ module MixinBot
|
|
|
205
202
|
max = signers.last
|
|
206
203
|
sig_byte_len = [@tx.aggregated['signature']].pack('H*').bytes.size
|
|
207
204
|
if ((max / 8) + 1) > sig_byte_len
|
|
208
|
-
bytes
|
|
209
|
-
bytes
|
|
210
|
-
signers.each { |signer| bytes
|
|
205
|
+
bytes.concat Transaction::AGGREGATED_SIGNATURE_SPARSE_MASK
|
|
206
|
+
bytes.concat MixinBot.utils.encode_uint16 signers.size
|
|
207
|
+
signers.each { |signer| bytes.concat MixinBot.utils.encode_uint16(signer) }
|
|
211
208
|
else
|
|
212
209
|
masks_bytes = Array.new((max / 8) + 1, 0)
|
|
213
210
|
signers.each do |signer|
|
|
214
211
|
masks_bytes[signer / 8] ^= (1 << (signer % 8))
|
|
215
212
|
end
|
|
216
|
-
bytes
|
|
217
|
-
bytes
|
|
218
|
-
bytes
|
|
213
|
+
bytes.concat Transaction::AGGREGATED_SIGNATURE_ORDINAY_MASK
|
|
214
|
+
bytes.concat MixinBot.utils.encode_uint16 masks_bytes.size
|
|
215
|
+
bytes.concat masks_bytes
|
|
219
216
|
end
|
|
220
217
|
|
|
221
218
|
bytes
|
|
@@ -233,18 +230,18 @@ module MixinBot
|
|
|
233
230
|
|
|
234
231
|
raise ArgumentError, 'signatures overflow' if sl == Transaction::MAX_ENCODE_INT
|
|
235
232
|
|
|
236
|
-
bytes
|
|
233
|
+
bytes.concat MixinBot.utils.encode_uint16 sl
|
|
237
234
|
|
|
238
235
|
if sl.positive?
|
|
239
236
|
@tx.signatures.each do |signature|
|
|
240
|
-
bytes
|
|
237
|
+
bytes.concat MixinBot.utils.encode_uint16 signature.keys.size
|
|
241
238
|
|
|
242
239
|
signature.keys.sort.each do |key|
|
|
243
240
|
signature_bytes = [signature[key]].pack('H*').bytes
|
|
244
241
|
raise ArgumentError, 'Signature should be 64 bytes' if signature_bytes.size != 64
|
|
245
242
|
|
|
246
|
-
bytes
|
|
247
|
-
bytes
|
|
243
|
+
bytes.concat MixinBot.utils.encode_uint16 key
|
|
244
|
+
bytes.concat signature_bytes
|
|
248
245
|
end
|
|
249
246
|
end
|
|
250
247
|
end
|
data/lib/mixin_bot/version.rb
CHANGED
data/lib/mvm/registry.rb
CHANGED
|
@@ -53,9 +53,9 @@ module MVM
|
|
|
53
53
|
|
|
54
54
|
def contract_from_multisig(user_ids, threshold)
|
|
55
55
|
bytes = []
|
|
56
|
-
bytes
|
|
57
|
-
bytes
|
|
58
|
-
bytes
|
|
56
|
+
bytes.concat(MixinBot.utils.encode_uint16(user_ids.length))
|
|
57
|
+
bytes.concat([user_ids.sort.join.gsub('-', '')].pack('H*').bytes)
|
|
58
|
+
bytes.concat(MixinBot.utils.encode_uint16(threshold))
|
|
59
59
|
|
|
60
60
|
hash = Eth::Util.bin_to_prefixed_hex(Eth::Util.keccak256(bytes.pack('C*')))
|
|
61
61
|
@rpc.call @registry, 'contracts', hash.to_i(16)
|
data/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MixinBot
|
|
2
2
|
|
|
3
|
-
> Ruby SDK and CLI for Mixin Network: Safe UTXO transfers, REST API, Blaze messaging, transaction crypto, optional MVM helpers. Ruby >= 3.2. Gem version 2.
|
|
3
|
+
> Ruby SDK and CLI for Mixin Network: Safe UTXO transfers, REST API, Blaze messaging, transaction crypto, optional MVM helpers. Ruby >= 3.2. Gem version 2.3.0.
|
|
4
4
|
|
|
5
5
|
Important notes:
|
|
6
6
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mixin_bot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- an-lee
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activesupport
|
|
@@ -232,14 +231,14 @@ dependencies:
|
|
|
232
231
|
requirements:
|
|
233
232
|
- - "~>"
|
|
234
233
|
- !ruby/object:Gem::Version
|
|
235
|
-
version: '
|
|
234
|
+
version: '2.2'
|
|
236
235
|
type: :runtime
|
|
237
236
|
prerelease: false
|
|
238
237
|
version_requirements: !ruby/object:Gem::Requirement
|
|
239
238
|
requirements:
|
|
240
239
|
- - "~>"
|
|
241
240
|
- !ruby/object:Gem::Version
|
|
242
|
-
version: '
|
|
241
|
+
version: '2.2'
|
|
243
242
|
- !ruby/object:Gem::Dependency
|
|
244
243
|
name: thor
|
|
245
244
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -375,7 +374,6 @@ licenses:
|
|
|
375
374
|
- MIT
|
|
376
375
|
metadata:
|
|
377
376
|
rubygems_mfa_required: 'true'
|
|
378
|
-
post_install_message:
|
|
379
377
|
rdoc_options: []
|
|
380
378
|
require_paths:
|
|
381
379
|
- lib
|
|
@@ -390,8 +388,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
390
388
|
- !ruby/object:Gem::Version
|
|
391
389
|
version: '0'
|
|
392
390
|
requirements: []
|
|
393
|
-
rubygems_version:
|
|
394
|
-
signing_key:
|
|
391
|
+
rubygems_version: 4.0.16
|
|
395
392
|
specification_version: 4
|
|
396
393
|
summary: A Ruby SDK for Mixin Network
|
|
397
394
|
test_files: []
|