solana-ruby-kit 7.1.1.2 → 8.2.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/README.md +24 -0
- data/lib/solana/ruby/kit/errors.rb +6 -0
- data/lib/solana/ruby/kit/instruction_plans/transaction_plan_executor.rb +105 -0
- data/lib/solana/ruby/kit/rpc/api/get_ag_genesis_cert.rb +72 -0
- data/lib/solana/ruby/kit/rpc/client.rb +2 -0
- data/lib/solana/ruby/kit/transaction_messages/compute_budget.rb +2 -0
- data/lib/solana/ruby/kit/transaction_messages/resource_limit_validation.rb +70 -0
- data/lib/solana/ruby/kit/transaction_messages.rb +1 -0
- data/lib/solana/ruby/kit/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba7d2659d83b01af6565c7af483364d61e61fcc9761cbca9d63d8d41f6b4d73a
|
|
4
|
+
data.tar.gz: bd8402f845e9d8db5845be0e358676d00e308b2a0777afc1973e8814175e1c1c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 589ade790ebb62b97f9905e8fe98113efc93deb5a6fad37239d377ab427915838eaa3d461644d213944ee8b80796134b28d5a6da43529a3920bb5037940a20a1
|
|
7
|
+
data.tar.gz: 3c03649c3b4314b93e7321f74b77150cd19bd775535b8431c14b678fdf35d3e7d4703fac43f341c3480ec5031b0c78bd0c0e3b7e385bb01f1822fc57fa5afaec
|
data/README.md
CHANGED
|
@@ -189,9 +189,17 @@ msg = TxMsg.append_instructions(msg, [instruction])
|
|
|
189
189
|
msg = TxMsg.prepend_instructions(msg, [priority_fee_ix])
|
|
190
190
|
|
|
191
191
|
# Compute unit limit (SetComputeUnitLimit instruction from the Compute Budget program)
|
|
192
|
+
# The limit is validated: it must be an integer in [0, MAX_COMPUTE_UNIT_LIMIT] (1,400,000).
|
|
193
|
+
# The runtime silently clamps a larger request rather than failing, so asking for more than
|
|
194
|
+
# the maximum would run on a budget you did not ask for — this raises instead.
|
|
192
195
|
msg = TxMsg.set_transaction_message_compute_unit_limit(200_000, msg)
|
|
193
196
|
TxMsg.get_transaction_message_compute_unit_limit(msg) # => 200_000
|
|
194
197
|
|
|
198
|
+
# The check is also available on its own, alongside one for heap frame sizes — a whole
|
|
199
|
+
# number of KiB in [MIN_HEAP_SIZE, MAX_HEAP_SIZE] (32 KiB to 256 KiB).
|
|
200
|
+
TxMsg.assert_is_valid_compute_unit_limit(200_000)
|
|
201
|
+
TxMsg.assert_is_valid_heap_size(64 * 1024)
|
|
202
|
+
|
|
195
203
|
# Loaded accounts data size limit
|
|
196
204
|
msg = TxMsg.set_transaction_message_loaded_accounts_data_size_limit(64_000, msg)
|
|
197
205
|
TxMsg.get_transaction_message_loaded_accounts_data_size_limit(msg) # => 64_000
|
|
@@ -274,6 +282,7 @@ rpc.get_block_time(slot)
|
|
|
274
282
|
rpc.get_inflation_reward([address])
|
|
275
283
|
rpc.get_signatures_for_address(address)
|
|
276
284
|
rpc.get_vote_accounts
|
|
285
|
+
rpc.get_ag_genesis_cert # Alpenglow genesis cert, or nil
|
|
277
286
|
rpc.simulate_transaction(encoded_tx)
|
|
278
287
|
rpc.send_transaction(encoded_tx)
|
|
279
288
|
rpc.request_airdrop(address, lamports) # devnet / testnet only
|
|
@@ -640,6 +649,21 @@ result = executor.call(transaction_plan)
|
|
|
640
649
|
# A one-argument lambda returning { transaction:, context: } — the shape this method
|
|
641
650
|
# required before v7.1.0 — is still accepted and adapted onto the flow above.
|
|
642
651
|
|
|
652
|
+
# ── 3b. Executing every leaf concurrently ────────────────────────────────────
|
|
653
|
+
# For work that has no execution dependencies — signing or serializing, say —
|
|
654
|
+
# create_transaction_plan_executor_with_concurrent_leaves takes the same callback
|
|
655
|
+
# contract but starts every leaf immediately, each on its own thread. It preserves
|
|
656
|
+
# the plan's nesting, order and divisibility in the result, supports non-divisible
|
|
657
|
+
# sequential plans, and cancels nothing: a leaf that raises does not stop the others,
|
|
658
|
+
# so every leaf runs to completion before the failure is reported. Because the leaves
|
|
659
|
+
# run at the same time, the callback must be safe to call concurrently with itself.
|
|
660
|
+
concurrent = Plans.create_transaction_plan_executor_with_concurrent_leaves(
|
|
661
|
+
execute_transaction_message: ->(_context, message) {
|
|
662
|
+
{ transaction: Kit::Transactions.compile_transaction_message(message) }
|
|
663
|
+
}
|
|
664
|
+
)
|
|
665
|
+
result = concurrent.call(transaction_plan)
|
|
666
|
+
|
|
643
667
|
# ── 4. Plan types ─────────────────────────────────────────────────────────────
|
|
644
668
|
Plans.single_instruction_plan(ix) # wrap one instruction
|
|
645
669
|
Plans.sequential_instruction_plan([ix1, ix2]) # ordered, divisible
|
|
@@ -60,6 +60,10 @@ module Solana::Ruby::Kit
|
|
|
60
60
|
TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE = :SOLANA_ERROR__TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE
|
|
61
61
|
# context: { unsupported_version: }
|
|
62
62
|
TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED = :SOLANA_ERROR__TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED
|
|
63
|
+
# context: { compute_unit_limit:, max_compute_unit_limit: }
|
|
64
|
+
TRANSACTION__COMPUTE_UNIT_LIMIT_OUT_OF_RANGE = :SOLANA_ERROR__TRANSACTION__COMPUTE_UNIT_LIMIT_OUT_OF_RANGE
|
|
65
|
+
# context: { heap_size:, max_heap_size:, min_heap_size:, multiple_of: }
|
|
66
|
+
TRANSACTION__INVALID_HEAP_SIZE = :SOLANA_ERROR__TRANSACTION__INVALID_HEAP_SIZE
|
|
63
67
|
TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE = :SOLANA_ERROR__TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE
|
|
64
68
|
TRANSACTIONS__BLOCKHASH_NOT_FOUND = :SOLANA_ERROR__TRANSACTIONS__BLOCKHASH_NOT_FOUND
|
|
65
69
|
TRANSACTIONS__FAILED_TRANSACTION_PLAN = :SOLANA_ERROR__TRANSACTIONS__FAILED_TRANSACTION_PLAN
|
|
@@ -207,6 +211,8 @@ module Solana::Ruby::Kit
|
|
|
207
211
|
TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND => 'Failed to decompile instruction: program address not found at index %{index}',
|
|
208
212
|
TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE => 'Failed to decompile instruction: account index %{index} is out of range',
|
|
209
213
|
TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED => 'Transaction version %{unsupported_version} is not supported',
|
|
214
|
+
TRANSACTION__COMPUTE_UNIT_LIMIT_OUT_OF_RANGE => 'Transaction compute unit limit must be an integer in the range [0, %{max_compute_unit_limit}]. `%{compute_unit_limit}` given',
|
|
215
|
+
TRANSACTION__INVALID_HEAP_SIZE => 'Transaction heap size must be an integer multiple of %{multiple_of} bytes in the range [%{min_heap_size}, %{max_heap_size}]. `%{heap_size}` given',
|
|
210
216
|
TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE => 'Transaction simulation failed: %{message}',
|
|
211
217
|
TRANSACTIONS__BLOCKHASH_NOT_FOUND => 'Blockhash not found',
|
|
212
218
|
TRANSACTIONS__FAILED_TRANSACTION_PLAN => 'Failed to execute transaction plan',
|
|
@@ -58,6 +58,57 @@ module Solana::Ruby::Kit
|
|
|
58
58
|
}
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
+
# Creates a TransactionPlanExecutor that processes every leaf concurrently.
|
|
62
|
+
#
|
|
63
|
+
# It takes the same configuration as +create_transaction_plan_executor+ and its
|
|
64
|
+
# +execute_transaction_message+ callback follows the same contract: it receives a fresh
|
|
65
|
+
# mutable +context+ Hash for every leaf and returns the Hash a successful result should
|
|
66
|
+
# carry. On success the two are merged with the returned value winning; when the callback
|
|
67
|
+
# raises, the context accumulated so far is preserved on the failed result. The legacy
|
|
68
|
+
# one-argument callable shape is accepted here too.
|
|
69
|
+
#
|
|
70
|
+
# The difference is the traversal. This executor preserves the input plan's nesting, order
|
|
71
|
+
# and divisibility in the returned result, but it does not enforce the execution
|
|
72
|
+
# dependencies expressed by sequential plans: every leaf is started immediately, each on
|
|
73
|
+
# its own thread, with no concurrency limit. That makes it suitable for work that can be
|
|
74
|
+
# performed independently, such as signing or serializing transactions. For the same
|
|
75
|
+
# reason a callback that raises does not cancel the other leaves — each one runs to
|
|
76
|
+
# completion — and non-divisible sequential plans are supported rather than rejected.
|
|
77
|
+
#
|
|
78
|
+
# Once every leaf has settled, a result tree containing any failure raises
|
|
79
|
+
# INSTRUCTION_PLANS__FAILED_TO_EXECUTE_TRANSACTION_PLAN carrying the complete tree on its
|
|
80
|
+
# +transaction_plan_result+ reader.
|
|
81
|
+
#
|
|
82
|
+
# Because each leaf runs on its own thread, the callback must be safe to run concurrently
|
|
83
|
+
# with itself. Each leaf gets its own context Hash, so the contexts do not need guarding,
|
|
84
|
+
# but anything the callback shares between invocations does.
|
|
85
|
+
#
|
|
86
|
+
# Mirrors `createTransactionPlanExecutorWithConcurrentLeaves(config)` from
|
|
87
|
+
# @solana/instruction-plans.
|
|
88
|
+
# NOTE: Ruby translation has no abort signal; leaves are never canceled, only failed.
|
|
89
|
+
sig { params(execute_transaction_message: T.untyped).returns(T.untyped) }
|
|
90
|
+
def create_transaction_plan_executor_with_concurrent_leaves(execute_transaction_message:)
|
|
91
|
+
->(transaction_plan) {
|
|
92
|
+
# Traversal spawns every leaf's thread on the way down and returns a tree of thunks;
|
|
93
|
+
# resolving them afterwards is what joins the threads, so all leaves are already in
|
|
94
|
+
# flight before the first one is waited on.
|
|
95
|
+
result = concurrent_executor_traverse(transaction_plan, execute_transaction_message).call
|
|
96
|
+
|
|
97
|
+
cause = executor_find_error(result)
|
|
98
|
+
if cause
|
|
99
|
+
err = SolanaError.new(
|
|
100
|
+
SolanaError::INSTRUCTION_PLANS__FAILED_TO_EXECUTE_TRANSACTION_PLAN,
|
|
101
|
+
{ cause: cause }
|
|
102
|
+
)
|
|
103
|
+
err.instance_variable_set(:@transaction_plan_result, result)
|
|
104
|
+
err.define_singleton_method(:transaction_plan_result) { result }
|
|
105
|
+
Kernel.raise err
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
result
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
|
|
61
112
|
# ── Private helpers ────────────────────────────────────────────────────────
|
|
62
113
|
|
|
63
114
|
module_function
|
|
@@ -145,6 +196,58 @@ module Solana::Ruby::Kit
|
|
|
145
196
|
returned
|
|
146
197
|
end
|
|
147
198
|
|
|
199
|
+
# Returns a thunk that resolves to this plan's result. Leaf threads are started as the
|
|
200
|
+
# tree is walked, so the whole plan is in flight by the time the outermost thunk is called.
|
|
201
|
+
sig { params(plan: T.untyped, execute_fn: T.untyped).returns(T.untyped) }
|
|
202
|
+
|
|
203
|
+
def concurrent_executor_traverse(plan, execute_fn)
|
|
204
|
+
case plan.kind
|
|
205
|
+
when :single
|
|
206
|
+
thread = Thread.new { concurrent_executor_execute_single(plan, execute_fn) }
|
|
207
|
+
-> { thread.value }
|
|
208
|
+
when :sequential, :parallel
|
|
209
|
+
thunks = plan.plans.map { |sub| concurrent_executor_traverse(sub, execute_fn) }
|
|
210
|
+
Kernel.lambda do
|
|
211
|
+
results = thunks.map(&:call)
|
|
212
|
+
if plan.kind == :parallel
|
|
213
|
+
parallel_transaction_plan_result(results)
|
|
214
|
+
elsif plan.divisible
|
|
215
|
+
sequential_transaction_plan_result(results)
|
|
216
|
+
else
|
|
217
|
+
# Unlike the sequential executor, non-divisible plans are supported: nothing
|
|
218
|
+
# about running every leaf independently depends on the plan being divisible.
|
|
219
|
+
non_divisible_sequential_transaction_plan_result(results)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
Kernel.raise SolanaError.new(
|
|
224
|
+
SolanaError::INVARIANT_VIOLATION__INVALID_TRANSACTION_PLAN_KIND,
|
|
225
|
+
{ kind: plan.kind }
|
|
226
|
+
)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Runs one leaf. Identical to +executor_traverse_single+ except that a failure is recorded
|
|
231
|
+
# and nothing else — there is no shared cancellation state for it to trip.
|
|
232
|
+
sig { params(plan: T.untyped, execute_fn: T.untyped).returns(T.untyped) }
|
|
233
|
+
|
|
234
|
+
def concurrent_executor_execute_single(plan, execute_fn)
|
|
235
|
+
context = {}
|
|
236
|
+
|
|
237
|
+
begin
|
|
238
|
+
returned = executor_invoke(execute_fn, context, plan.message)
|
|
239
|
+
successful_single_transaction_plan_result(plan.message, nil, context.merge(returned))
|
|
240
|
+
rescue SolanaError => e
|
|
241
|
+
failed_single_transaction_plan_result(plan.message, e, context)
|
|
242
|
+
rescue => e
|
|
243
|
+
wrapped = SolanaError.new(
|
|
244
|
+
SolanaError::INSTRUCTION_PLANS__FAILED_TO_EXECUTE_TRANSACTION_PLAN,
|
|
245
|
+
{ cause: e }
|
|
246
|
+
)
|
|
247
|
+
failed_single_transaction_plan_result(plan.message, wrapped, context)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
148
251
|
sig { params(result: T.untyped).returns(T.untyped) }
|
|
149
252
|
|
|
150
253
|
def executor_find_error(result)
|
|
@@ -164,5 +267,7 @@ module Solana::Ruby::Kit
|
|
|
164
267
|
private_class_method :executor_traverse_single
|
|
165
268
|
private_class_method :executor_invoke
|
|
166
269
|
private_class_method :executor_find_error
|
|
270
|
+
private_class_method :concurrent_executor_traverse
|
|
271
|
+
private_class_method :concurrent_executor_execute_single
|
|
167
272
|
end
|
|
168
273
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'has_transport'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
module Rpc
|
|
8
|
+
module Api
|
|
9
|
+
# The block an Alpenglow genesis certificate certifies.
|
|
10
|
+
AgGenesisCertBlock = T.let(
|
|
11
|
+
Struct.new(
|
|
12
|
+
:block_id, # Array<Integer> — block id of the certified block, as a 32-byte array
|
|
13
|
+
:slot, # Integer — slot of the certified block
|
|
14
|
+
keyword_init: true
|
|
15
|
+
),
|
|
16
|
+
T.untyped
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# The aggregate signature over an Alpenglow genesis certificate.
|
|
20
|
+
AgGenesisCertSignature = T.let(
|
|
21
|
+
Struct.new(
|
|
22
|
+
:bitmap, # Array<Integer> — ranks of the validators in the aggregate signature
|
|
23
|
+
:signature, # Array<Integer> — the 192-byte aggregate BLS signature
|
|
24
|
+
keyword_init: true
|
|
25
|
+
),
|
|
26
|
+
T.untyped
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# The Alpenglow genesis certificate, as gossiped between validators.
|
|
30
|
+
# Mirrors the `WireBlockCertMessage` type in the Agave validator.
|
|
31
|
+
AgGenesisCert = T.let(
|
|
32
|
+
Struct.new(
|
|
33
|
+
:block, # AgGenesisCertBlock
|
|
34
|
+
:signature, # AgGenesisCertSignature
|
|
35
|
+
keyword_init: true
|
|
36
|
+
),
|
|
37
|
+
T.untyped
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Returns the Alpenglow genesis certificate — the certificate over the block at which the
|
|
41
|
+
# Alpenglow consensus protocol was activated — or nil if the node does not have one.
|
|
42
|
+
# Mirrors TypeScript's GetAgGenesisCertApi.getAgGenesisCert.
|
|
43
|
+
# @see https://solana.com/docs/rpc/http/getaggenesiscert
|
|
44
|
+
module GetAgGenesisCert
|
|
45
|
+
extend T::Sig
|
|
46
|
+
extend T::Helpers
|
|
47
|
+
|
|
48
|
+
requires_ancestor { HasTransport }
|
|
49
|
+
|
|
50
|
+
sig { returns(T.untyped) }
|
|
51
|
+
def get_ag_genesis_cert
|
|
52
|
+
raw = transport.request('getAgGenesisCert', [])
|
|
53
|
+
return nil if raw.nil?
|
|
54
|
+
|
|
55
|
+
block = raw['block'] || {}
|
|
56
|
+
signature = raw['signature'] || {}
|
|
57
|
+
|
|
58
|
+
AgGenesisCert.new(
|
|
59
|
+
block: AgGenesisCertBlock.new(
|
|
60
|
+
block_id: Kernel.Array(block['blockId']).map { |b| Kernel.Integer(b) },
|
|
61
|
+
slot: Kernel.Integer(block['slot'])
|
|
62
|
+
),
|
|
63
|
+
signature: AgGenesisCertSignature.new(
|
|
64
|
+
bitmap: Kernel.Array(signature['bitmap']).map { |b| Kernel.Integer(b) },
|
|
65
|
+
signature: Kernel.Array(signature['signature']).map { |b| Kernel.Integer(b) }
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -7,6 +7,7 @@ require_relative 'api/get_slot'
|
|
|
7
7
|
require_relative 'api/get_balance'
|
|
8
8
|
require_relative 'api/get_latest_blockhash'
|
|
9
9
|
require_relative 'api/get_account_info'
|
|
10
|
+
require_relative 'api/get_ag_genesis_cert'
|
|
10
11
|
require_relative 'api/get_block_height'
|
|
11
12
|
require_relative 'api/get_signature_statuses'
|
|
12
13
|
require_relative 'api/send_transaction'
|
|
@@ -51,6 +52,7 @@ module Solana::Ruby::Kit
|
|
|
51
52
|
include Api::GetBalance
|
|
52
53
|
include Api::GetLatestBlockhash
|
|
53
54
|
include Api::GetAccountInfo
|
|
55
|
+
include Api::GetAgGenesisCert
|
|
54
56
|
include Api::GetBlockHeight
|
|
55
57
|
include Api::GetSignatureStatuses
|
|
56
58
|
include Api::SendTransaction
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
require_relative '../addresses/address'
|
|
5
5
|
require_relative '../instructions/instruction'
|
|
6
6
|
require_relative 'transaction_message'
|
|
7
|
+
require_relative 'resource_limit_validation'
|
|
7
8
|
|
|
8
9
|
module Solana::Ruby::Kit
|
|
9
10
|
module TransactionMessages
|
|
@@ -63,6 +64,7 @@ module Solana::Ruby::Kit
|
|
|
63
64
|
# SetComputeUnitLimit instruction. Mirrors `setTransactionMessageComputeUnitLimit`.
|
|
64
65
|
sig { params(limit: Integer, message: TransactionMessage).returns(TransactionMessage) }
|
|
65
66
|
def set_transaction_message_compute_unit_limit(limit, message)
|
|
67
|
+
assert_is_valid_compute_unit_limit(limit)
|
|
66
68
|
existing_idx = message.instructions.index { |i| set_compute_unit_limit_instruction?(i) }
|
|
67
69
|
new_ix = get_set_compute_unit_limit_instruction(limit)
|
|
68
70
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../errors'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
# NOTE: `MAX_COMPUTE_UNIT_LIMIT` lives in `compute_budget.rb`, which requires this file, so it
|
|
8
|
+
# is referenced from a method body rather than at load time to keep the two files acyclic.
|
|
9
|
+
module TransactionMessages
|
|
10
|
+
# The smallest heap frame size that a transaction may request (32 KiB).
|
|
11
|
+
MIN_HEAP_SIZE = T.let(32 * 1024, Integer)
|
|
12
|
+
|
|
13
|
+
# The largest heap frame size that a transaction may request (256 KiB).
|
|
14
|
+
MAX_HEAP_SIZE = T.let(256 * 1024, Integer)
|
|
15
|
+
|
|
16
|
+
# A requested heap frame size must be a whole number of KiB.
|
|
17
|
+
HEAP_SIZE_MULTIPLE_OF = T.let(1024, Integer)
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# Raises if the given compute unit limit is one the runtime will not honor as written.
|
|
22
|
+
#
|
|
23
|
+
# A transaction may request at most MAX_COMPUTE_UNIT_LIMIT compute units. Requesting more
|
|
24
|
+
# does not fail the transaction; the runtime clamps the request down to that maximum, so
|
|
25
|
+
# the budget the transaction runs with is silently not the one that was asked for. Values
|
|
26
|
+
# that are not integers are a separate hazard, since they cannot be encoded as a `u32` and
|
|
27
|
+
# are not clamped. Failing here surfaces both at the point the value is set.
|
|
28
|
+
#
|
|
29
|
+
# Mirrors `assertIsValidComputeUnitLimit` from @solana/transaction-messages.
|
|
30
|
+
sig { params(compute_unit_limit: T.untyped).void }
|
|
31
|
+
def assert_is_valid_compute_unit_limit(compute_unit_limit)
|
|
32
|
+
return if compute_unit_limit.is_a?(Integer) &&
|
|
33
|
+
compute_unit_limit >= 0 &&
|
|
34
|
+
compute_unit_limit <= MAX_COMPUTE_UNIT_LIMIT
|
|
35
|
+
|
|
36
|
+
Kernel.raise SolanaError.new(
|
|
37
|
+
SolanaError::TRANSACTION__COMPUTE_UNIT_LIMIT_OUT_OF_RANGE,
|
|
38
|
+
{ compute_unit_limit: compute_unit_limit, max_compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT }
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Raises if the given heap frame size is one the runtime will not accept.
|
|
43
|
+
#
|
|
44
|
+
# The requested heap size must be a multiple of HEAP_SIZE_MULTIPLE_OF bytes and lie between
|
|
45
|
+
# MIN_HEAP_SIZE and MAX_HEAP_SIZE inclusive. This mirrors the transaction sanitization check
|
|
46
|
+
# performed by the runtime.
|
|
47
|
+
#
|
|
48
|
+
# Mirrors `assertIsValidHeapSize` from @solana/transaction-messages. Ruby has no heap-size
|
|
49
|
+
# setter of its own — the `RequestHeapFrame` instruction and the version 1 transaction config
|
|
50
|
+
# are both untranslated — so this is offered as a standalone check for callers building that
|
|
51
|
+
# instruction themselves.
|
|
52
|
+
sig { params(heap_size: T.untyped).void }
|
|
53
|
+
def assert_is_valid_heap_size(heap_size)
|
|
54
|
+
return if heap_size.is_a?(Integer) &&
|
|
55
|
+
heap_size >= MIN_HEAP_SIZE &&
|
|
56
|
+
heap_size <= MAX_HEAP_SIZE &&
|
|
57
|
+
(heap_size % HEAP_SIZE_MULTIPLE_OF).zero?
|
|
58
|
+
|
|
59
|
+
Kernel.raise SolanaError.new(
|
|
60
|
+
SolanaError::TRANSACTION__INVALID_HEAP_SIZE,
|
|
61
|
+
{
|
|
62
|
+
heap_size: heap_size,
|
|
63
|
+
max_heap_size: MAX_HEAP_SIZE,
|
|
64
|
+
min_heap_size: MIN_HEAP_SIZE,
|
|
65
|
+
multiple_of: HEAP_SIZE_MULTIPLE_OF
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -3,4 +3,5 @@
|
|
|
3
3
|
|
|
4
4
|
# Transaction message construction utilities — mirrors @solana/transaction-messages.
|
|
5
5
|
require_relative 'transaction_messages/transaction_message'
|
|
6
|
+
require_relative 'transaction_messages/resource_limit_validation'
|
|
6
7
|
require_relative 'transaction_messages/compute_budget'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solana-ruby-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 8.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paul Zupan, Idhra Inc.
|
|
@@ -262,6 +262,7 @@ files:
|
|
|
262
262
|
- lib/solana/ruby/kit/resource_limit_estimation.rb
|
|
263
263
|
- lib/solana/ruby/kit/rpc.rb
|
|
264
264
|
- lib/solana/ruby/kit/rpc/api/get_account_info.rb
|
|
265
|
+
- lib/solana/ruby/kit/rpc/api/get_ag_genesis_cert.rb
|
|
265
266
|
- lib/solana/ruby/kit/rpc/api/get_balance.rb
|
|
266
267
|
- lib/solana/ruby/kit/rpc/api/get_block_height.rb
|
|
267
268
|
- lib/solana/ruby/kit/rpc/api/get_block_time.rb
|
|
@@ -337,6 +338,7 @@ files:
|
|
|
337
338
|
- lib/solana/ruby/kit/transaction_introspection/walk_instructions.rb
|
|
338
339
|
- lib/solana/ruby/kit/transaction_messages.rb
|
|
339
340
|
- lib/solana/ruby/kit/transaction_messages/compute_budget.rb
|
|
341
|
+
- lib/solana/ruby/kit/transaction_messages/resource_limit_validation.rb
|
|
340
342
|
- lib/solana/ruby/kit/transaction_messages/transaction_message.rb
|
|
341
343
|
- lib/solana/ruby/kit/transactions.rb
|
|
342
344
|
- lib/solana/ruby/kit/transactions/compiler.rb
|