update compute budget docs (#26014)
This commit is contained in:
parent
467c840df8
commit
a76a7b8361
|
@ -45,37 +45,39 @@ The policy is as follows:
|
|||
|
||||
## Compute Budget
|
||||
|
||||
To prevent a program from abusing computation resources, each instruction in a
|
||||
transaction is given a compute budget. The budget consists of computation units
|
||||
that are consumed as the program performs various operations and bounds that the
|
||||
program may not exceed. When the program consumes its entire budget or exceeds
|
||||
a bound, the runtime halts the program and returns an error.
|
||||
|
||||
Note: The compute budget currently applies per-instruction, but is moving toward
|
||||
a per-transaction model. For more information see [Transaction-wide Compute
|
||||
Budget](#transaction-wide-compute-budget).
|
||||
To prevent abuse of computational resources, each transaction is allocated a
|
||||
compute budget. The budget specifies a maximum number of compute units that a
|
||||
transaction can consume, the costs associated with different types of operations
|
||||
the transaction may perform, and operational bounds the transaction must adhere
|
||||
to. As the transaction is processed compute units are consumed by its
|
||||
instruction's programs performing operations such as executing BPF instructions,
|
||||
calling syscalls, etc... When the transaction consumes its entire budget, or
|
||||
exceeds a bound such as attempting a call stack that is too deep, the runtime
|
||||
halts the transaction processing and returns an error.
|
||||
|
||||
The following operations incur a compute cost:
|
||||
|
||||
- Executing BPF instructions
|
||||
- Passing data between programs
|
||||
- Calling system calls
|
||||
- logging
|
||||
- creating program addresses
|
||||
- cross-program invocations
|
||||
- ...
|
||||
|
||||
For cross-program invocations, the programs invoked inherit the budget of their
|
||||
parent. If an invoked program consumes the budget or exceeds a bound, the entire
|
||||
invocation chain and the parent are halted.
|
||||
For cross-program invocations, the instructions invoked inherit the budget of
|
||||
their parent. If an invoked instruction consumes the transactions remaining
|
||||
budget, or exceeds a bound, the entire invocation chain and the top level
|
||||
transaction processing are halted.
|
||||
|
||||
The current [compute
|
||||
budget](https://github.com/solana-labs/solana/blob/db32549c00a1b5370fcaf128981ad3323bbd9570/program-runtime/src/compute_budget.rs)
|
||||
budget](https://github.com/solana-labs/solana/blob/db32549c00a1b5370fcaf128981ad3323bbd9570/program-runtime/src/compute_budget.rs#L23)
|
||||
can be found in the Solana Program Runtime.
|
||||
|
||||
For example, if the current budget is:
|
||||
|
||||
```rust
|
||||
max_units: 200,000,
|
||||
max_units: 1,400,000,
|
||||
log_u64_units: 100,
|
||||
create_program address units: 1500,
|
||||
invoke_units: 1000,
|
||||
|
@ -86,15 +88,15 @@ log_pubkey_units: 100,
|
|||
...
|
||||
```
|
||||
|
||||
Then the program
|
||||
Then the transaction
|
||||
|
||||
- Could execute 200,000 BPF instructions, if it does nothing else.
|
||||
- Could execute 1,400,000 BPF instructions, if it did nothing else.
|
||||
- Cannot exceed 4k of stack usage.
|
||||
- Cannot exceed a BPF call depth of 64.
|
||||
- Cannot exceed 4 levels of cross-program invocations.
|
||||
|
||||
Since the compute budget is consumed incrementally as the program executes, the
|
||||
total budget consumption will be a combination of the various costs of the
|
||||
Since the compute budget is consumed incrementally as the transaction executes,
|
||||
the total budget consumption will be a combination of the various costs of the
|
||||
operations it performs.
|
||||
|
||||
At runtime a program may log how much of the compute budget remains. See
|
||||
|
@ -102,56 +104,49 @@ At runtime a program may log how much of the compute budget remains. See
|
|||
for more information.
|
||||
|
||||
A transaction may set the maximum number of compute units it is allowed to
|
||||
consume by including a "request units"
|
||||
[`ComputeBudgetInstruction`](https://github.com/solana-labs/solana/blob/db32549c00a1b5370fcaf128981ad3323bbd9570/sdk/src/compute_budget.rs#L39).
|
||||
Note that a transaction's prioritization fee is calculated from multiplying the
|
||||
number of compute units requested by the compute unit price (measured in
|
||||
micro-lamports) set by the transaction. So transactions should request the
|
||||
minimum amount of compute units required for execution to minimize fees. Also
|
||||
note that fees are not adjusted when the number of requested compute units
|
||||
exceeds the number of compute units consumed by an executed transaction.
|
||||
consume and the compute unit price by including a `SetComputeUnitLimit` and a
|
||||
`SetComputeUnitPrice`
|
||||
[Compute budget instructions](https://github.com/solana-labs/solana/blob/db32549c00a1b5370fcaf128981ad3323bbd9570/sdk/src/compute_budget.rs#L22)
|
||||
respectively.
|
||||
|
||||
If no `SetComputeUnitLimit` is provided the limit will be calculated as the
|
||||
product of the number of instructions in the transaction (excluding the [Compute
|
||||
budget
|
||||
instructions](https://github.com/solana-labs/solana/blob/db32549c00a1b5370fcaf128981ad3323bbd9570/sdk/src/compute_budget.rs#L22))
|
||||
and the default per-instruction units, which is currently 200k.
|
||||
|
||||
Note that a transaction's prioritization fee is calculated by multiplying the
|
||||
number of compute units by the compute unit price (measured in micro-lamports)
|
||||
set by the transaction via compute budget instructions. So transactions should
|
||||
request the minimum amount of compute units required for execution to minimize
|
||||
fees. Also note that fees are not adjusted when the number of requested compute
|
||||
units exceeds the number of compute units actually consumed by an executed
|
||||
transaction.
|
||||
|
||||
Compute Budget instructions don't require any accounts and don't consume any
|
||||
compute units to process. Transactions can only contain one of each type of
|
||||
compute budget instruction, duplicate types will result in an error.
|
||||
|
||||
The `ComputeBudgetInstruction::set_compute_unit_limit` function can be used to create
|
||||
these instructions:
|
||||
The `ComputeBudgetInstruction::set_compute_unit_limit` and
|
||||
`ComputeBudgetInstruction::set_compute_unit_price` functions can be used to
|
||||
create these instructions:
|
||||
|
||||
```rust
|
||||
let instruction = ComputeBudgetInstruction::set_compute_unit_limit(300_000);
|
||||
```
|
||||
|
||||
## Transaction-wide Compute Budget
|
||||
|
||||
Transactions are processed as a single entity and are the primary unit of block
|
||||
scheduling. In order to facilitate better block scheduling and account for the
|
||||
computational cost of each transaction, the compute budget is moving to a
|
||||
transaction-wide budget rather than per-instruction.
|
||||
|
||||
For information on what the compute budget is and how it is applied see [Compute
|
||||
Budget](#compute-budget).
|
||||
|
||||
The transaction-wide compute budget applies the `max_units` cap to the entire
|
||||
transaction rather than to each instruction within the transaction. The default
|
||||
transaction-wide `max_units` will be calculated as the product of the number of
|
||||
instructions in the transaction (excluding [Compute Budget](#compute-budget)
|
||||
instructions) by the default per-instruction units, which is currently 200k.
|
||||
During processing, the sum of the compute units used by each instruction in the
|
||||
transaction must not exceed that value. This default value attempts to retain
|
||||
existing behavior to avoid breaking clients. Transactions can request a specific
|
||||
number of `max_units` via [Compute Budget](#compute-budget) instructions.
|
||||
Clients should request only what they need; requesting the minimum amount of
|
||||
units required to process the transaction will reduce overall transaction cost,
|
||||
which may include a prioritization-fee charged for every compute unit.
|
||||
```rust
|
||||
let instruction = ComputeBudgetInstruction::set_compute_unit_price(1);
|
||||
```
|
||||
|
||||
## New Features
|
||||
|
||||
As Solana evolves, new features or patches may be introduced that changes the
|
||||
behavior of the cluster and how programs run. Changes in behavior must be
|
||||
coordinated between the various nodes of the cluster. If nodes do not coordinate,
|
||||
then these changes can result in a break-down of consensus. Solana supports a
|
||||
mechanism called runtime features to facilitate the smooth adoption of changes.
|
||||
coordinated between the various nodes of the cluster. If nodes do not
|
||||
coordinate, then these changes can result in a break-down of consensus. Solana
|
||||
supports a mechanism called runtime features to facilitate the smooth adoption
|
||||
of changes.
|
||||
|
||||
Runtime features are epoch coordinated events where one or more behavior changes
|
||||
to the cluster will occur. New changes to Solana that will change behavior are
|
||||
|
|
|
@ -2,31 +2,18 @@
|
|||
title: Deterministic Transaction Fees
|
||||
---
|
||||
|
||||
Transactions currently include a fee field that indicates the maximum fee field a slot leader is permitted to charge to process a transaction. The cluster, on the other hand, agrees on a minimum fee. If the network is congested, the slot leader may prioritize the transactions offering higher fees. That means the client won't know how much was collected until the transaction is confirmed by the cluster and the remaining balance is checked. It smells of exactly what we dislike about Ethereum's "gas", non-determinism.
|
||||
|
||||
## Congestion-driven fees
|
||||
|
||||
Each validator uses _signatures per slot_ \(SPS\) to estimate network congestion and _SPS target_ to estimate the desired processing capacity of the cluster. The validator learns the SPS target from the genesis config, whereas it calculates SPS from recently processed transactions. The genesis config also defines a target `lamports_per_signature`, which is the fee to charge per signature when the cluster is operating at _SPS target_.
|
||||
|
||||
## Calculating fees
|
||||
|
||||
The client uses the JSON RPC API to query the cluster for the current fee parameters. Those parameters are tagged with a blockhash and remain valid until that blockhash is old enough to be rejected by the slot leader.
|
||||
|
||||
Before sending a transaction to the cluster, a client may submit the transaction and fee account data to an SDK module called the _fee calculator_. So long as the client's SDK version matches the slot leader's version, the client is assured that its account will be changed exactly the same number of lamports as returned by the fee calculator.
|
||||
Before sending a transaction to the cluster, a client may query the network to
|
||||
determine what the transaction's fee will be via the rpc request
|
||||
[getFeeForMessage](clients/jsonrpc-api#getfeeformessage).
|
||||
|
||||
## Fee Parameters
|
||||
|
||||
In the first implementation of this design, the only fee parameter is `lamports_per_signature`. The more signatures the cluster needs to verify, the higher the fee. The exact number of lamports is determined by the ratio of SPS to the SPS target. At the end of each slot, the cluster lowers `lamports_per_signature` when SPS is below the target and raises it when above the target. The minimum value for `lamports_per_signature` is 50% of the target `lamports_per_signature` and the maximum value is 10x the target \`lamports_per_signature'
|
||||
|
||||
Future parameters might include:
|
||||
|
||||
- `lamports_per_pubkey` - cost to load an account
|
||||
- `lamports_per_slot_distance` - higher cost to load very old accounts
|
||||
- `lamports_per_byte` - cost per size of account loaded
|
||||
- `lamports_per_bpf_instruction` - cost to run a program
|
||||
|
||||
## Attacks
|
||||
|
||||
### Hijacking the SPS Target
|
||||
|
||||
A group of validators can centralize the cluster if they can convince it to raise the SPS Target above a point where the rest of the validators can keep up. Raising the target will cause fees to drop, presumably creating more demand and therefore higher TPS. If the validator doesn't have hardware that can process that many transactions that fast, its confirmation votes will eventually get so long that the cluster will be forced to boot it.
|
||||
The fee is based on the number of signatures in the transaction, the more
|
||||
signatures a transaction contains, the higher the fee. In addition, a
|
||||
transaction can specify an additional fee that determines how the transaction is
|
||||
relatively prioritized against others. A transaction's prioritization fee is
|
||||
calculated by multiplying the number of compute units by the compute unit price
|
||||
(measured in micro-lamports) set by the transaction via compute budget
|
||||
instructions.
|
||||
|
|
|
@ -27,7 +27,7 @@ the transaction. By calculating the total cost of the transaction, the runtime
|
|||
can charge a more representative fee and make better transaction scheduling
|
||||
decisions.
|
||||
|
||||
A fee will be calculated based on:
|
||||
A fee could be calculated based on:
|
||||
|
||||
1. Number of signatures
|
||||
- Fixed rate per signature
|
||||
|
@ -77,17 +77,6 @@ takes within a slot to process.
|
|||
|
||||
https://github.com/solana-labs/solana/issues/20511
|
||||
|
||||
### Transaction-wide compute caps
|
||||
|
||||
The current compute budget caps are independently applied to each instruction
|
||||
within a transaction. This means the overall transaction cap varies depending on
|
||||
how many instructions are in the transaction. To more accurately schedule a
|
||||
transaction, the compute budget will be applied transaction-wide. One challenge
|
||||
of the transaction-wide cap is that each instruction (program) can no longer
|
||||
expect to be given an equal amount of compute units. Each instruction will be
|
||||
given the remaining units left over after processing earlier instructions. This
|
||||
will provide some additional tuning and composability challenges for developers.
|
||||
|
||||
### Requestable compute budget caps and heap sizes
|
||||
|
||||
The precompiled
|
||||
|
|
13698
docs/yarn.lock
13698
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue