Skip to content
Merged
Show file tree
Hide file tree
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
20 changes: 16 additions & 4 deletions book/src/fees/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,11 @@ cannot make a transition signed against the old one pay the new one.
contract owner when they sponsor the gas. A sponsor's balance has to cover the
gas *and* the fees they would owe; one that insisted and falls short is the
same unpaid refusal as before (40222), and one that only preferred hands the
gas and the fees back to the signer. Fee validation and execution ask that one
question through one function (`gas_sponsor_pays`), on one estimate, so they
always name the same payer. The contract owner never pays the `owner` part:
gas and the fees back to the signer. The gas is estimated for the payer:
first with the sponsor paying, then, when the sponsor does not pay, with the
signer paying. Fee validation settles the payer through one function
(`gas_sponsor_pays`) and hands it to execution, which charges that payer, so
they always name the same one. The contract owner never pays the `owner` part:
it would travel through the owner pot back to them and only cost writes. A
sponsor is always the contract owner, so a sponsored action pays into the
moderators pot only, which a contract that sponsors gas should price in. The
Expand All @@ -402,7 +404,17 @@ ran.

**The fee is not part of the `FeeResult`.** It moves as balance operations in
the batch's own operation list: one removal from the payer, one addition per
pot. The fee pools and the proposers see exactly what they saw before. The
pot. The transition may already write the payer's balance: a purchase moves
its price, a contested document its voting fund, and a sale pays a contract
owner who may be sponsoring the gas. A balance operation computes the new
balance from the one committed before its batch and GroveDB keeps only the
last write of a key, so `apply_drive_operations` (generation 1, protocol
version 14) merges every write of one identity balance, one fee pot or one
prefunded specialized balance in a batch into a single net operation. Token
writes cannot be merged that way (a transfer writes two balances, a mint or a
burn a balance and the supply), so a batch that writes one token balance or
supply twice is refused; no state transition makes one. The fee pools and the proposers see
exactly what they saw before. The
pots sit under the `PreFundedSpecializedBalances` root sum tree, which the
per-block total credits check already sums, so the credits stay accounted for
while they wait to be claimed.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::error::execution::ExecutionError;
use crate::error::Error;
use crate::execution::platform_events::state_transition_processing::record_added_balance_outputs::AddedBalanceOutputsOrigin;
use crate::execution::platform_events::state_transition_processing::validate_fees_of_event::v1::gas_sponsor_pays;
use crate::execution::platform_events::state_transition_processing::validate_fees_of_event::v1::SettledFees;
use crate::execution::types::execution_event::ExecutionEvent;
use crate::execution::types::execution_operation::ValidationOperation;
use crate::execution::types::signing_key_limits::SigningKeyLimits;
Expand Down Expand Up @@ -38,8 +38,9 @@ where
/// the contract's fee pots with the batch's own operations, and count against a budgeted key
/// when its identity pays them. They are no part of the fee, which goes to the fee pools.
///
/// The fee is charged to the gas sponsor when their balance covers the estimated fee, the
/// same question fee validation asked, and to the identity otherwise. Storage refunds still
/// The fee is charged to whoever fee validation settled on (`settle_fees_of_event_v1`, of the
/// same generation): the gas sponsor when their balance covers the gas estimated with them
/// paying, and the identity otherwise. Storage refunds still
/// go to whoever paid the storage originally, so a sponsored document refunds its owner when
/// it is deleted. A failed batch (`consensus_errors`) is never sponsored: its signer pays for
/// the work that ran.
Expand Down Expand Up @@ -104,7 +105,7 @@ where
);
}

let mut fee_validation_result = self.validate_fees_of_event(
let mut fee_validation_result = self.settle_fees_of_event_v1(
&event,
block_info,
Some(transaction),
Expand All @@ -121,7 +122,7 @@ where
additional_fixed_fee_cost,
user_fee_increase,
signing_key_limits,
gas_sponsor,
gas_sponsor: _,
action_fees,
} = event
else {
Expand All @@ -131,35 +132,21 @@ where
};

let result = if fee_validation_result.is_valid_with_data() {
// Fee validation admitted the sponsor on this estimate; charging follows the same
// answer, so validation and execution never name different payers.
let estimated_required_balance = fee_validation_result
.data
.as_ref()
.ok_or(Error::Execution(ExecutionError::CorruptedCodeExecution(
"a valid fee validation result carries the estimated fee",
)))?
.total_base_fee()
.saturating_add(additional_fixed_fee_cost.unwrap_or_default());
let paying_sponsor = match gas_sponsor {
Some(gas_sponsor)
if gas_sponsor_pays(
&gas_sponsor,
estimated_required_balance,
&action_fees,
)? =>
{
Some(gas_sponsor)
}
_ => None,
};
// Charge the payer fee validation settled on, so validation and execution never
// name different payers.
let SettledFees {
estimated_fee_result,
paying_sponsor,
} = fee_validation_result.into_data()?;
let payer_id = paying_sponsor
.map(|gas_sponsor| gas_sponsor.identity_id)
.unwrap_or(identity.id);

// Whoever pays the gas pays the document action fees: they leave the payer's
// balance for the contract's fee pots in the same batch as the documents. They are
// no part of the fee below, which goes to the fee pools.
// balance for the contract's fee pots in the same batch as the documents, which
// merges them with any other write of that balance (a purchase price, a voting
// fund, a sale to a sponsoring contract owner). They are no part of the fee below,
// which goes to the fee pools.
//
// They are a price the contract set, like the price of a purchase, and move as that
// principal does: with the operations, before the gas is metered and debited. Fee
Expand Down Expand Up @@ -252,13 +239,10 @@ where
)?;

if consensus_errors.is_empty() {
SuccessfulPaidExecution(
Some(fee_validation_result.into_data()?),
outcome.actual_fee_paid_owned(),
)
SuccessfulPaidExecution(Some(estimated_fee_result), outcome.actual_fee_paid_owned())
} else {
UnsuccessfulPaidExecution(
Some(fee_validation_result.into_data()?),
Some(estimated_fee_result),
outcome.actual_fee_paid_owned(),
consensus_errors,
)
Expand Down
Loading
Loading