Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comprehensive compute fees #20531

Merged
merged 2 commits into from
Oct 12, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions docs/src/proposals/comprehensive-compute-fees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: Comprehensive Compute Fees
---

## Motivation

The current fee structure lacks a comprehensive account of the work required by
a validator to process a transaction. The fee structure is only based on the
number of signatures in a transaction but is meant to account for the work that
the validator must perform to validate each transaction. The validator performs
a lot more user-defined work than just signature verification. Processing a
transaction typically includes signature verifications, account locking, account
loading, and instruction processing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we don't still introduce gas fee bidding, remotely inheriting our deterministic fee cost philosophy? https://docs.solana.com/implemented-proposals/transaction-fees

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fees will be deterministic, but governed based on cluster load so they may vary each slot

## Proposed Solution

The following solution does not specify what native token costs are to be
associated with the new fee structure. Instead, it sets the criteria and
provides the knobs that a cost model can use to determine those costs.

### Fee

The goal of the fees is to cover the computation cost of processing a
transaction. Each of the fee categories below will be represented as a compute
unit cost that, when added together, encompasses the entire cost of processing
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:

1. Number of signatures
- Fixed rate per signature
2. Number of write locks
- Fixed rate per writable account
3. Data byte cost
- Fixed rate per byte of the sum of the length all a transactions instruction
datas
Comment on lines +36 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, nice, this prose sounds to me that it's already generalized enough, including the upcoming extended (larger-than-MTU) ones. :) ref: #20691

4. Account sizes
- Account sizes can't be known up-front but can account for a considerable
amount of the load the transaction incurs on the network. The payer will
be charged for a maximum account size (10m) upfront and refunded the
difference after the actual account sizes are known.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the refund mechanism

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calc the fee based on the account size estimate (or overestimate) and verify that the payer has sufficient balance to cover it. Then after processing the transaction and the final measured cost is known deduct the final cost which may be lower than the original estimate. The same thing as planned for the compute budget, verify the payer's balance based on what they asked for (or default) and then deduct the actual amount used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I was under impression that before processing, the asked/estimated fund in payer's account will be deducted, or locked somehow, to make sure the fund will be there to cover expense. Then after processing, the remaining balance will be credited back to payer's account. Thought that is a lot of labor and chances for errors; what you described is easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fee is currently deducted from the payer account before processing any instructions. We can credit some of that back after processing depending on the difference between the requested and the actual.

5. Compute budget
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(please allow late comment)

I think slot height delta since the last access for each account might be a good candidate. we should discount large yet super frequently accessed accounts as a valid use case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example: vote transactions and dexes order books.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(please allow late comment)

I think slot height delta since the last access for each account might be a good candidate. we should discount large yet super frequently accessed accounts as a valid use case?

This is a good use case; but could discounting frequently accessed accounts potentially make DoS attacks easier/cheaper?

Copy link
Member

@ryoqun ryoqun Oct 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, maybe not likely? i thought the main theme of this revamped compute fees is to reflect the actual computing resource consumed to execute a particular tx. this means we closely model the performance characteristics of any modern machine to be metered as a single unit of amount called fees, right? Then, any frequently accessed data should be in some kind of cache. so, I thought txes touching the frequently accessed accounts should be cheaper.

Another context i heard is that 1 SOL/day for validators is too much pain. So, I want to avoid voting txes to be higher than now (source: @mvines )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then, any frequently accessed data should be in some kind of cache.

to be specific, those data will be in AccountDb's AccountCache and/or linux kernel's page cache, etc...

- Each transaction will be given a default transaction-wide compute budget of
200k units with the option of requesting a larger budget via a compute
budget instruction up to a maximum of 1m units. This budget is used to
limit the time it takes to process a transaction. The compute budget
portion of the fee will be charged up-front based on the default or
requested amount. After processing, the actual number of units consumed
will be known, and the payer will be refunded the difference, so the payer
only pays for what they used. Builtin programs will have a fixed cost
while BPF program's cost will be measured at runtime.
6. Precompiled programs
- Precompiled programs are performing compute-intensive operations. The work
incurred by a precompiled program is predictable based on the instruction's
data array. Therefore a cost will be assigned per precompiled program
based on the parsing of instruction data. Because precompiled programs are
processed outside of the bank, their compute cost will not be reflected in
the compute budget and will not be used in transaction scheduling
decisions. The methods used to determine the fixed cost of the components
above are described in
[#19627](https://github.com/solana-labs/solana/issues/19627)

### Cost model

The cost model is used to assess what load a transaction will incur during
in-slot processing and then make decisions on how to best schedule transaction
into batches.

The cost model's criteria are identical to the fee's criteria except for
signatures and precompiled programs. These two costs are incurred before a
transaction is scheduled and therefore do not affect how long a transaction
takes within a slot to process.

### Cache account sizes and use them instead of the max

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
[ComputeBudget](https://github.com/solana-labs/solana/blob/00929f836348d76cb3503d0ba5f76f0d275bcc66/sdk/src/compute_budget.rs#L34)
program can be used to request higher transaction-wide compute budget caps and
program heap sizes. The requested increases will be reflected in the
transaction's fee.

### Fees for precompiled program failures

https://github.com/solana-labs/solana/issues/20481

### Rate governing

Current rate governing needs to be re-assessed. Current fees are being rate
governed down to their minimums because the number of signatures in each slot is
far lower than the "target" signatures per slot.

Instead of using the number of signatures to rate govern, the cost model will
feed back information based on the batch/queue load it is seeing. The fees will
sit at a target rate and only increase if the load goes above a specified but to
be determined threshold. The governing will be applied across all the fee
criteria.

### How do clients calculate a transaction's fee

Transaction fees are currently calculated based on a fixed cost per signature
and implemented via an object in the SDK that took a transaction and returned a
cost. This object is looked up via a blockhash and returned via RPC to
calculate the fee offline.

The comprehensive fee calculations are more sophisticated and depend on a
greater amount of information (recent program compute meter measurements). To
move to the new fee structure the RPC APIs that return a FeeCalculator object
will be deprecated, and clients will send their transactions over RPC and be
returned the calculated fee.

Fees will no longer be calculated based on a blockhash since it is
cost-prohibitive to retain the fee cost inputs for each bank (program units,
account sizes). And mainly, the governed fee is based on network load now, not
at some time in the past. This will mean that during offline-signing the actual
fee charged will not be known until the transaction is submitted.