mixin_bot 1.4.0 → 2.0.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 +75 -0
- data/API_COVERAGE.md +143 -0
- data/CHANGELOG.md +112 -0
- data/README.md +375 -0
- data/docs/agent/cli.md +149 -0
- data/docs/agent/cookbook.md +152 -0
- data/examples/blaze.rb +43 -0
- data/examples/config.yml.example +21 -0
- data/lib/mixin_bot/address.rb +43 -3
- data/lib/mixin_bot/api/app.rb +7 -0
- data/lib/mixin_bot/api/asset.rb +114 -3
- data/lib/mixin_bot/api/auth.rb +19 -10
- data/lib/mixin_bot/api/blaze.rb +81 -0
- data/lib/mixin_bot/api/chain.rb +94 -0
- data/lib/mixin_bot/api/code.rb +16 -0
- data/lib/mixin_bot/api/computer_api.rb +60 -0
- data/lib/mixin_bot/api/conversation.rb +7 -1
- data/lib/mixin_bot/api/deposit.rb +12 -0
- data/lib/mixin_bot/api/encrypted_message.rb +1 -1
- data/lib/mixin_bot/api/fiat.rb +12 -0
- data/lib/mixin_bot/api/inscription.rb +2 -2
- data/lib/mixin_bot/api/legacy_collectible.rb +26 -27
- data/lib/mixin_bot/api/legacy_multisig.rb +20 -21
- data/lib/mixin_bot/api/legacy_output.rb +10 -3
- data/lib/mixin_bot/api/legacy_payment.rb +2 -0
- data/lib/mixin_bot/api/legacy_snapshot.rb +16 -0
- data/lib/mixin_bot/api/legacy_transaction.rb +28 -13
- data/lib/mixin_bot/api/legacy_transfer.rb +11 -8
- data/lib/mixin_bot/api/legacy_user.rb +51 -0
- data/lib/mixin_bot/api/me.rb +99 -3
- data/lib/mixin_bot/api/message.rb +18 -27
- data/lib/mixin_bot/api/multisig.rb +19 -0
- data/lib/mixin_bot/api/network.rb +17 -0
- data/lib/mixin_bot/api/network_asset.rb +27 -0
- data/lib/mixin_bot/api/output.rb +1 -1
- data/lib/mixin_bot/api/pin.rb +16 -3
- data/lib/mixin_bot/api/pin_payload.rb +26 -0
- data/lib/mixin_bot/api/session.rb +14 -0
- data/lib/mixin_bot/api/snapshot.rb +6 -0
- data/lib/mixin_bot/api/tip.rb +74 -1
- data/lib/mixin_bot/api/transaction.rb +106 -17
- data/lib/mixin_bot/api/transfer.rb +141 -14
- data/lib/mixin_bot/api/turn.rb +12 -0
- data/lib/mixin_bot/api/user.rb +148 -45
- data/lib/mixin_bot/api/withdraw.rb +24 -23
- data/lib/mixin_bot/api.rb +248 -3
- data/lib/mixin_bot/bot_auth.rb +71 -0
- data/lib/mixin_bot/cli/api.rb +224 -143
- data/lib/mixin_bot/cli/base.rb +77 -0
- data/lib/mixin_bot/cli/call.rb +71 -0
- data/lib/mixin_bot/cli/errors.rb +56 -0
- data/lib/mixin_bot/cli/node.rb +9 -2
- data/lib/mixin_bot/cli/output.rb +196 -0
- data/lib/mixin_bot/cli/schema.rb +274 -0
- data/lib/mixin_bot/cli/schema_command.rb +21 -0
- data/lib/mixin_bot/cli/utils.rb +114 -18
- data/lib/mixin_bot/cli.rb +124 -48
- data/lib/mixin_bot/client/error_mapper.rb +40 -0
- data/lib/mixin_bot/client.rb +94 -64
- data/lib/mixin_bot/computer.rb +132 -0
- data/lib/mixin_bot/configuration.rb +108 -1
- data/lib/mixin_bot/errors.rb +102 -0
- data/lib/mixin_bot/models/address.rb +11 -0
- data/lib/mixin_bot/models/api_envelope.rb +67 -0
- data/lib/mixin_bot/models/asset.rb +11 -0
- data/lib/mixin_bot/models/ghost_keys.rb +14 -0
- data/lib/mixin_bot/models/output.rb +11 -0
- data/lib/mixin_bot/models/safe_multisig_request.rb +11 -0
- data/lib/mixin_bot/models/sequencer_transaction_request.rb +11 -0
- data/lib/mixin_bot/models/user.rb +11 -0
- data/lib/mixin_bot/models.rb +10 -0
- data/lib/mixin_bot/monitor.rb +77 -0
- data/lib/mixin_bot/transaction/buffer.rb +34 -0
- data/lib/mixin_bot/transaction/decoder.rb +227 -0
- data/lib/mixin_bot/transaction/encoder.rb +255 -0
- data/lib/mixin_bot/transaction.rb +6 -475
- data/lib/mixin_bot/url_scheme.rb +63 -0
- data/lib/mixin_bot/utils/address.rb +17 -80
- data/lib/mixin_bot/utils/crypto.rb +173 -1
- data/lib/mixin_bot/utils/decoder.rb +1 -1
- data/lib/mixin_bot/utils/encoder.rb +13 -0
- data/lib/mixin_bot/utils.rb +45 -0
- data/lib/mixin_bot/uuid.rb +78 -1
- data/lib/mixin_bot/version.rb +11 -1
- data/lib/mixin_bot.rb +172 -18
- data/lib/mvm/bridge.rb +46 -0
- data/lib/mvm/client.rb +60 -0
- data/lib/mvm/nft.rb +4 -2
- data/lib/mvm/registry.rb +2 -1
- data/lib/mvm.rb +93 -0
- data/lib/tasks/api_coverage.rake +20 -0
- data/llms.txt +29 -0
- metadata +77 -9
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MixinBot
|
|
4
|
+
class Transaction
|
|
5
|
+
##
|
|
6
|
+
# Parses raw transaction bytes into a {Transaction} instance.
|
|
7
|
+
#
|
|
8
|
+
class Decoder
|
|
9
|
+
def initialize(transaction)
|
|
10
|
+
@tx = transaction
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def decode
|
|
14
|
+
raw = [@tx.hex].pack('H*').bytes
|
|
15
|
+
@tx.hash = SHA3::Digest::SHA256.hexdigest(raw.pack('C*'))
|
|
16
|
+
@buf = Buffer.new(raw)
|
|
17
|
+
|
|
18
|
+
magic = @buf.shift(2)
|
|
19
|
+
raise ArgumentError, 'Not valid raw' unless magic == Transaction::MAGIC
|
|
20
|
+
|
|
21
|
+
_version = @buf.shift(2)
|
|
22
|
+
@tx.version = MixinBot.utils.decode_int _version
|
|
23
|
+
|
|
24
|
+
asset = @buf.shift(32)
|
|
25
|
+
@tx.asset = asset.pack('C*').unpack1('H*')
|
|
26
|
+
|
|
27
|
+
decode_inputs
|
|
28
|
+
decode_outputs
|
|
29
|
+
decode_references if @tx.version >= Transaction::REFERENCES_TX_VERSION && @tx.references.present?
|
|
30
|
+
|
|
31
|
+
extra_size = MixinBot.utils.decode_uint32 @buf.shift(4)
|
|
32
|
+
@tx.extra = @buf.shift(extra_size).pack('C*')
|
|
33
|
+
|
|
34
|
+
num = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
35
|
+
if num == Transaction::MAX_ENCODE_INT
|
|
36
|
+
@tx.aggregated = {}
|
|
37
|
+
|
|
38
|
+
prefix = MixinBot.utils.decode_uint16(@buf.shift(2))
|
|
39
|
+
raise ArgumentError, 'invalid aggregated' unless prefix == Transaction::AGGREGATED_SIGNATURE_PREFIX
|
|
40
|
+
|
|
41
|
+
@tx.aggregated['signature'] = @buf.shift(64).pack('C*').unpack1('H*')
|
|
42
|
+
|
|
43
|
+
byte = @buf.shift
|
|
44
|
+
case byte
|
|
45
|
+
when Transaction::AGGREGATED_SIGNATURE_ORDINAY_MASK.first
|
|
46
|
+
@tx.aggregated['signers'] = []
|
|
47
|
+
masks_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
48
|
+
masks = @buf.shift(masks_size)
|
|
49
|
+
masks = Array(masks)
|
|
50
|
+
|
|
51
|
+
masks.each_with_index do |mask, i|
|
|
52
|
+
8.times do |j|
|
|
53
|
+
k = 1 << j
|
|
54
|
+
@tx.aggregated['signers'].push((i * 8) + j) if mask & k == k
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
when Transaction::AGGREGATED_SIGNATURE_SPARSE_MASK.first
|
|
58
|
+
signers_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
59
|
+
@tx.aggregated['signers'] = []
|
|
60
|
+
unless signers_size.zero?
|
|
61
|
+
signers_size.times do
|
|
62
|
+
@tx.aggregated['signers'].push MixinBot.utils.decode_uint16(@buf.shift(2))
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
elsif num.present? && num.positive? && @buf.size.positive?
|
|
67
|
+
@tx.signatures = []
|
|
68
|
+
num.times do
|
|
69
|
+
signature = {}
|
|
70
|
+
|
|
71
|
+
keys_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
72
|
+
|
|
73
|
+
keys_size.times do
|
|
74
|
+
index = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
75
|
+
signature[index] = @buf.shift(64).pack('C*').unpack1('H*')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@tx.signatures << signature
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@tx
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def decode_inputs
|
|
88
|
+
inputs_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
89
|
+
@tx.inputs = []
|
|
90
|
+
inputs_size.times do
|
|
91
|
+
input = {}
|
|
92
|
+
hash = @buf.shift(32)
|
|
93
|
+
input['hash'] = hash.pack('C*').unpack1('H*')
|
|
94
|
+
|
|
95
|
+
index = @buf.shift(2)
|
|
96
|
+
input['index'] = MixinBot.utils.decode_uint16 index
|
|
97
|
+
|
|
98
|
+
if @buf.peek(2) == Transaction::NULL_BYTES
|
|
99
|
+
@buf.shift(2)
|
|
100
|
+
else
|
|
101
|
+
genesis_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
102
|
+
genesis = @buf.shift genesis_size
|
|
103
|
+
input['genesis'] = genesis.pack('C*').unpack1('H*')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if @buf.peek(2) == Transaction::NULL_BYTES
|
|
107
|
+
@buf.shift(2)
|
|
108
|
+
else
|
|
109
|
+
magic = @buf.shift(2)
|
|
110
|
+
raise ArgumentError, 'Not valid input' unless magic == Transaction::MAGIC
|
|
111
|
+
|
|
112
|
+
deposit = {}
|
|
113
|
+
deposit['chain'] = @buf.shift(32).pack('C*').unpack1('H*')
|
|
114
|
+
|
|
115
|
+
asset_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
116
|
+
deposit['asset'] = @buf.shift(asset_size).unpack1('H*')
|
|
117
|
+
|
|
118
|
+
transaction_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
119
|
+
deposit['transaction'] = @buf.shift(transaction_size).unpack1('H*')
|
|
120
|
+
|
|
121
|
+
deposit['index'] = MixinBot.utils.decode_uint64 @buf.shift(8)
|
|
122
|
+
|
|
123
|
+
amount_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
124
|
+
deposit['amount'] = MixinBot.utils.decode_int @buf.shift(amount_size)
|
|
125
|
+
|
|
126
|
+
input['deposit'] = deposit
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if @buf.peek(2) == Transaction::NULL_BYTES
|
|
130
|
+
@buf.shift(2)
|
|
131
|
+
else
|
|
132
|
+
magic = @buf.shift(2)
|
|
133
|
+
raise ArgumentError, 'Not valid input' unless magic == Transaction::MAGIC
|
|
134
|
+
|
|
135
|
+
mint = {}
|
|
136
|
+
if @buf.peek(2) == Transaction::NULL_BYTES
|
|
137
|
+
@buf.shift(2)
|
|
138
|
+
else
|
|
139
|
+
group_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
140
|
+
mint['group'] = @buf.shift(group_size).unpack1('H*')
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
mint['batch'] = MixinBot.utils.decode_uint64 @buf.shift(8)
|
|
144
|
+
_amount_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
145
|
+
mint['amount'] = MixinBot.utils.decode_int @buf.shift(_amount_size)
|
|
146
|
+
|
|
147
|
+
input['mint'] = mint
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
@tx.inputs.push input
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
@tx
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def decode_outputs
|
|
157
|
+
outputs_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
158
|
+
@tx.outputs = []
|
|
159
|
+
outputs_size.times do
|
|
160
|
+
output = {}
|
|
161
|
+
|
|
162
|
+
@buf.shift
|
|
163
|
+
type = @buf.shift
|
|
164
|
+
output['type'] = type
|
|
165
|
+
|
|
166
|
+
amount_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
167
|
+
output['amount'] = format('%.8f', MixinBot.utils.decode_int(@buf.shift(amount_size)).to_f / 1e8).gsub(/\.?0+$/, '')
|
|
168
|
+
|
|
169
|
+
output['keys'] = []
|
|
170
|
+
keys_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
171
|
+
keys_size.times do
|
|
172
|
+
output['keys'].push @buf.shift(32).pack('C*').unpack1('H*')
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
output['mask'] = @buf.shift(32).pack('C*').unpack1('H*')
|
|
176
|
+
|
|
177
|
+
script_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
178
|
+
output['script'] = @buf.shift(script_size).pack('C*').unpack1('H*')
|
|
179
|
+
|
|
180
|
+
if @buf.peek(2) == Transaction::NULL_BYTES
|
|
181
|
+
@buf.shift(2)
|
|
182
|
+
else
|
|
183
|
+
magic = @buf.shift(2)
|
|
184
|
+
raise ArgumentError, 'Not valid output' unless magic == Transaction::MAGIC
|
|
185
|
+
|
|
186
|
+
withdrawal = {}
|
|
187
|
+
withdrawal['chain'] = @buf.shift(32).pack('C*').unpack1('H*')
|
|
188
|
+
|
|
189
|
+
asset_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
190
|
+
withdrawal['asset'] = @buf.shift(asset_size).unpack1('H*')
|
|
191
|
+
|
|
192
|
+
if @buf.peek(2) == Transaction::NULL_BYTES
|
|
193
|
+
@buf.shift(2)
|
|
194
|
+
else
|
|
195
|
+
address_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
196
|
+
withdrawal['address'] = @buf.shift(address_size).pack('C*').unpack1('H*')
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
if @buf.peek(2) == Transaction::NULL_BYTES
|
|
200
|
+
@buf.shift(2)
|
|
201
|
+
else
|
|
202
|
+
tag_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
203
|
+
withdrawal['tag'] = @buf.shift(tag_size).pack('C*').unpack1('H*')
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
output['withdrawal'] = withdrawal
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
@tx.outputs.push output
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
@tx
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def decode_references
|
|
216
|
+
references_size = MixinBot.utils.decode_uint16 @buf.shift(2)
|
|
217
|
+
@tx.references = []
|
|
218
|
+
|
|
219
|
+
references_size.times do
|
|
220
|
+
@tx.references.push @buf.shift(32).pack('C*').unpack1('H*')
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
@tx
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MixinBot
|
|
4
|
+
class Transaction
|
|
5
|
+
##
|
|
6
|
+
# Builds the binary encoding for a {Transaction} instance.
|
|
7
|
+
#
|
|
8
|
+
class Encoder
|
|
9
|
+
def initialize(transaction)
|
|
10
|
+
@tx = transaction
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def encode
|
|
14
|
+
raise InvalidTransactionFormatError, 'asset is required' if @tx.asset.blank?
|
|
15
|
+
raise InvalidTransactionFormatError, 'inputs is required' if @tx.inputs.blank?
|
|
16
|
+
raise InvalidTransactionFormatError, 'outputs is required' if @tx.outputs.blank?
|
|
17
|
+
|
|
18
|
+
bytes = []
|
|
19
|
+
|
|
20
|
+
bytes += Transaction::MAGIC
|
|
21
|
+
bytes += [0, @tx.version]
|
|
22
|
+
bytes += [@tx.asset].pack('H*').bytes
|
|
23
|
+
|
|
24
|
+
bytes += encode_inputs
|
|
25
|
+
bytes += encode_outputs
|
|
26
|
+
bytes += encode_references if @tx.version >= Transaction::REFERENCES_TX_VERSION && @tx.references.present?
|
|
27
|
+
|
|
28
|
+
extra_bytes = @tx.extra.bytes
|
|
29
|
+
raise InvalidTransactionFormatError, 'extra is too long' if extra_bytes.size > Transaction::MAX_EXTRA_SIZE
|
|
30
|
+
|
|
31
|
+
bytes += MixinBot.utils.encode_uint32 extra_bytes.size
|
|
32
|
+
bytes += extra_bytes
|
|
33
|
+
|
|
34
|
+
bytes += if @tx.aggregated.nil?
|
|
35
|
+
encode_signatures
|
|
36
|
+
else
|
|
37
|
+
encode_aggregated_signature
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@tx.hash = SHA3::Digest::SHA256.hexdigest bytes.pack('C*')
|
|
41
|
+
@tx.hex = bytes.pack('C*').unpack1('H*')
|
|
42
|
+
|
|
43
|
+
@tx
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def encode_inputs
|
|
49
|
+
bytes = []
|
|
50
|
+
|
|
51
|
+
bytes += MixinBot.utils.encode_uint16(@tx.inputs.size)
|
|
52
|
+
|
|
53
|
+
@tx.inputs.each do |input|
|
|
54
|
+
bytes += [input['hash']].pack('H*').bytes
|
|
55
|
+
bytes += MixinBot.utils.encode_uint16(input['index'])
|
|
56
|
+
|
|
57
|
+
genesis = input['genesis'] || ''
|
|
58
|
+
if genesis.empty?
|
|
59
|
+
bytes += Transaction::NULL_BYTES
|
|
60
|
+
else
|
|
61
|
+
genesis_bytes = [genesis].pack('H*').bytes
|
|
62
|
+
bytes += MixinBot.utils.encode_uint16 genesis_bytes.size
|
|
63
|
+
bytes += genesis_bytes
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
deposit = input['deposit']
|
|
67
|
+
if deposit.nil?
|
|
68
|
+
bytes += Transaction::NULL_BYTES
|
|
69
|
+
else
|
|
70
|
+
bytes += Transaction::MAGIC
|
|
71
|
+
bytes += [deposit['chain']].pack('H*').bytes
|
|
72
|
+
|
|
73
|
+
asset_bytes = [deposit['asset']].pack('H*')
|
|
74
|
+
bytes += MixinBot.utils.encode_uint16 asset_bytes.size
|
|
75
|
+
bytes += asset_bytes.bytes
|
|
76
|
+
|
|
77
|
+
transaction_bytes = [deposit['transaction']].pack('H*')
|
|
78
|
+
bytes += MixinBot.utils.encode_uint16 transaction_bytes.size
|
|
79
|
+
bytes += transaction_bytes.bytes
|
|
80
|
+
|
|
81
|
+
bytes += MixinBot.utils.encode_uint64 deposit['index']
|
|
82
|
+
|
|
83
|
+
amount_bytes = MixinBot.utils.bytes_of deposit['amount']
|
|
84
|
+
bytes += MixinBot.utils.encode_uint16 amount_bytes.size
|
|
85
|
+
bytes += amount_bytes
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
mint = input['mint']
|
|
89
|
+
if mint.nil?
|
|
90
|
+
bytes += Transaction::NULL_BYTES
|
|
91
|
+
else
|
|
92
|
+
bytes += Transaction::MAGIC
|
|
93
|
+
|
|
94
|
+
group = mint['group'] || ''
|
|
95
|
+
if group.empty?
|
|
96
|
+
bytes += Transaction::NULL_BYTES
|
|
97
|
+
else
|
|
98
|
+
group_bytes = [group].pack('H*')
|
|
99
|
+
bytes += MixinBot.utils.encode_uint16 group_bytes.size
|
|
100
|
+
bytes += group_bytes.bytes
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
bytes += MixinBot.utils.encode_uint64 mint['batch']
|
|
104
|
+
|
|
105
|
+
amount_bytes = MixinBot.utils.encode_int mint['amount']
|
|
106
|
+
bytes += MixinBot.utils.encode_uint16 amount_bytes.size
|
|
107
|
+
bytes += amount_bytes
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
bytes
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def encode_outputs
|
|
115
|
+
bytes = []
|
|
116
|
+
|
|
117
|
+
bytes += MixinBot.utils.encode_uint16 @tx.outputs.size
|
|
118
|
+
|
|
119
|
+
@tx.outputs.each do |output|
|
|
120
|
+
type = output['type'] || 0
|
|
121
|
+
bytes += [0x00, type]
|
|
122
|
+
|
|
123
|
+
amount_bytes = MixinBot.utils.encode_int((output['amount'].to_d * 1e8).round)
|
|
124
|
+
bytes += MixinBot.utils.encode_uint16 amount_bytes.size
|
|
125
|
+
bytes += amount_bytes
|
|
126
|
+
|
|
127
|
+
bytes += MixinBot.utils.encode_uint16 output['keys'].size
|
|
128
|
+
output['keys'].each do |key|
|
|
129
|
+
bytes += [key].pack('H*').bytes
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
bytes += [output['mask']].pack('H*').bytes
|
|
133
|
+
|
|
134
|
+
script_bytes = [output['script']].pack('H*').bytes
|
|
135
|
+
bytes += MixinBot.utils.encode_uint16 script_bytes.size
|
|
136
|
+
bytes += script_bytes
|
|
137
|
+
|
|
138
|
+
withdrawal = output['withdrawal']
|
|
139
|
+
if withdrawal.nil?
|
|
140
|
+
bytes += Transaction::NULL_BYTES
|
|
141
|
+
else
|
|
142
|
+
bytes += Transaction::MAGIC
|
|
143
|
+
|
|
144
|
+
bytes += [withdrawal['chain']].pack('H*').bytes
|
|
145
|
+
|
|
146
|
+
asset_bytes = [withdrawal['asset']].pack('H*')
|
|
147
|
+
bytes += MixinBot.utils.encode_uint16 asset_bytes.bytesize
|
|
148
|
+
bytes += asset_bytes.bytes
|
|
149
|
+
|
|
150
|
+
address = withdrawal['address'] || ''
|
|
151
|
+
if address.empty?
|
|
152
|
+
bytes += Transaction::NULL_BYTES
|
|
153
|
+
else
|
|
154
|
+
address_bytes = [address].pack('H*').bytes
|
|
155
|
+
bytes += MixinBot.utils.encode_uint16 address_bytes.size
|
|
156
|
+
bytes += address_bytes
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
tag = withdrawal['tag'] || ''
|
|
160
|
+
if tag.empty?
|
|
161
|
+
bytes += Transaction::NULL_BYTES
|
|
162
|
+
else
|
|
163
|
+
tag_bytes = [tag].pack('H*').bytes
|
|
164
|
+
bytes += MixinBot.utils.encode_uint16 tag_bytes.size
|
|
165
|
+
bytes += tag_bytes
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
bytes
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def encode_references
|
|
174
|
+
bytes = []
|
|
175
|
+
|
|
176
|
+
bytes += MixinBot.utils.encode_uint16 @tx.references.size
|
|
177
|
+
|
|
178
|
+
@tx.references.each do |reference|
|
|
179
|
+
bytes += [reference].pack('H*').bytes
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
bytes
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def encode_aggregated_signature
|
|
186
|
+
bytes = []
|
|
187
|
+
|
|
188
|
+
bytes += MixinBot.utils.encode_uint16 Transaction::MAX_ENCODE_INT
|
|
189
|
+
bytes += MixinBot.utils.encode_uint16 Transaction::AGGREGATED_SIGNATURE_PREFIX
|
|
190
|
+
bytes += [@tx.aggregated['signature']].pack('H*').bytes
|
|
191
|
+
|
|
192
|
+
signers = @tx.aggregated['signers'] || []
|
|
193
|
+
if signers.empty?
|
|
194
|
+
bytes += Transaction::AGGREGATED_SIGNATURE_ORDINAY_MASK
|
|
195
|
+
bytes += Transaction::NULL_BYTES
|
|
196
|
+
return bytes
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
signers.each_with_index do |sig, i|
|
|
200
|
+
raise ArgumentError, 'signers not sorted' if i.positive? && sig <= signers[i - 1]
|
|
201
|
+
raise ArgumentError, 'signers not sorted' if sig > Transaction::MAX_ENCODE_INT
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
max = signers.last
|
|
205
|
+
sig_byte_len = [@tx.aggregated['signature']].pack('H*').bytes.size
|
|
206
|
+
if ((max / 8) + 1) > sig_byte_len
|
|
207
|
+
bytes += Transaction::AGGREGATED_SIGNATURE_SPARSE_MASK
|
|
208
|
+
bytes += MixinBot.utils.encode_uint16 signers.size
|
|
209
|
+
signers.each { |signer| bytes += MixinBot.utils.encode_uint16(signer) }
|
|
210
|
+
else
|
|
211
|
+
masks_bytes = Array.new((max / 8) + 1, 0)
|
|
212
|
+
signers.each do |signer|
|
|
213
|
+
masks_bytes[signer / 8] ^= (1 << (signer % 8))
|
|
214
|
+
end
|
|
215
|
+
bytes += Transaction::AGGREGATED_SIGNATURE_ORDINAY_MASK
|
|
216
|
+
bytes += MixinBot.utils.encode_uint16 masks_bytes.size
|
|
217
|
+
bytes += masks_bytes
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
bytes
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def encode_signatures
|
|
224
|
+
bytes = []
|
|
225
|
+
|
|
226
|
+
sl =
|
|
227
|
+
if @tx.signatures.is_a? Array
|
|
228
|
+
@tx.signatures.size
|
|
229
|
+
else
|
|
230
|
+
0
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
raise ArgumentError, 'signatures overflow' if sl == Transaction::MAX_ENCODE_INT
|
|
234
|
+
|
|
235
|
+
bytes += MixinBot.utils.encode_uint16 sl
|
|
236
|
+
|
|
237
|
+
if sl.positive?
|
|
238
|
+
@tx.signatures.each do |signature|
|
|
239
|
+
bytes += MixinBot.utils.encode_uint16 signature.keys.size
|
|
240
|
+
|
|
241
|
+
signature.keys.sort.each do |key|
|
|
242
|
+
signature_bytes = [signature[key]].pack('H*').bytes
|
|
243
|
+
raise ArgumentError, 'Signature should be 64 bytes' if signature_bytes.size != 64
|
|
244
|
+
|
|
245
|
+
bytes += MixinBot.utils.encode_uint16 key
|
|
246
|
+
bytes += signature_bytes
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
bytes
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|