气体计数。燃烧过的气体和用过的气体有什么区别?
Gas counting. What is the difference between burnt gas and used gas?
在交易执行期间,我们正在计算我们 "gas burned" 和 "gas used" 的数量。为什么我们必须单独跟踪这些计数器?
- 使用的气体包括燃烧的气体,因此
gas_used >= gas_burnt
,总是;
- 当应用任何类型的费用时,它都计入
gas_burnt
和 gas_used
;
- 当合约执行跨合约调用并将
X
的 gas 量附加到此调用时,则 X
计入 gas_used
而不是 gas_burnt
。这是 gas_used
和 gas_burnt
之间唯一的差异来源。如果智能合约在完成执行之前失败,则执行 none 的跨合约调用(或由合约创建的交易)并退还附加的气体;
- 根据之前的要点,当合约失败时,
gas_used - gas_burnt
将被退回账户,而 gas_burnt
将永远丢失(因为 gas_burnt
对应于实际上花费了验证者一些计算工作。
while writing this answer, @MaksymZavershynskyi answered as well.
his answer should be considered authoritative while mine is a guess.
查看源代码让我觉得
gas_used
是生产存储和计算的成本(完成不会失败的事情)
gas_burnt
是非生产性传输、存储和计算的成本(从 A 点到 B 点获取数据,尝试执行一个函数但在执行过程中失败或导致错误等)
这里有一堆带有源代码链接的片段给我留下了这样的印象
来自 nearcore/runtime/runtime/src/lib.rs
fn generate_refund_receipts(
&self,
receipt: &Receipt,
action_receipt: &ActionReceipt,
result: &mut ActionResult,
) -> Result<(), RuntimeError> {
/// ... snip ...
let gas_refund = if result.result.is_err() {
safe_add_gas(prepaid_gas, exec_gas)? - result.gas_burnt
} else {
safe_add_gas(prepaid_gas, exec_gas)? - result.gas_used
};
/// ... snip ...
}
来自 nearcore/runtime/runtime/src/actions.rs
pub(crate) fn action_function_call(
state_update: &mut TrieUpdate,
apply_state: &ApplyState,
account: &mut Option<Account>,
receipt: &Receipt,
action_receipt: &ActionReceipt,
promise_results: &[PromiseResult],
result: &mut ActionResult,
account_id: &AccountId,
function_call: &FunctionCallAction,
action_hash: &CryptoHash,
config: &RuntimeConfig,
is_last_action: bool,
) -> Result<(), StorageError> {
/// ... snip ...
if let Some(outcome) = outcome {
result.gas_burnt += outcome.burnt_gas;
result.gas_burnt_for_function_call += outcome.burnt_gas;
// Runtime in `generate_refund_receipts` takes care of using proper value for refunds.
// It uses `gas_used` for success and `gas_burnt` for failures. So it's not an issue to
// return a real `gas_used` instead of the `gas_burnt` into `ActionResult` for
// `FunctionCall`s.
result.gas_used += outcome.used_gas;
result.logs.extend(outcome.logs.into_iter());
}
/// ... snip ...
}
来自 nearcore/core/primitives/src/types.rs
pub struct ChunkExtra {
/// ... snip ...
/// Actually how much gas were used.
pub gas_used: Gas
/// ... snip ...
}
来自 nearcore/core/primitives/src/sharding.rs
pub struct ShardChunkHeaderInner {
/// ... snip ...
/// Gas used in this chunk.
pub gas_used: Gas,
/// ... snip ...
}
来自 nearcore/runtime/runtime/src/config.rs
/// ... snip ...
pub fn tx_cost(
config: &RuntimeFeesConfig,
transaction: &Transaction,
gas_price: Balance,
sender_is_receiver: bool,
) -> Result<(Gas, Gas, Balance), IntegerOverflowError> {
let mut gas_burnt: Gas = config.action_receipt_creation_config.send_fee(sender_is_receiver);
gas_burnt = safe_add_gas(
gas_burnt,
total_send_fees(&config, sender_is_receiver, &transaction.actions)?,
)?;
let mut gas_used = safe_add_gas(gas_burnt, config.action_receipt_creation_config.exec_fee())?;
gas_used = safe_add_gas(gas_used, total_exec_fees(&config, &transaction.actions)?)?;
gas_used = safe_add_gas(gas_used, total_prepaid_gas(&transaction.actions)?)?;
let mut total_cost = safe_gas_to_balance(gas_price, gas_used)?;
total_cost = safe_add_balance(total_cost, total_deposit(&transaction.actions)?)?;
Ok((gas_burnt, gas_used, total_cost))
}
/// ... snip ...
/// Total sum of gas that would need to be burnt before we start executing the given actions.
pub fn total_exec_fees(
config: &RuntimeFeesConfig,
actions: &[Action],
) -> Result<Gas, IntegerOverflowError> {
/// ... snip ...
}
/// Get the total sum of deposits for given actions.
pub fn total_deposit(
actions: &[Action]
) -> Result<Balance, IntegerOverflowError> {
/// ... snip ...
}
/// Get the total sum of prepaid gas for given actions.
pub fn total_prepaid_gas(
actions: &[Action]
) -> Result<Gas, IntegerOverflowError> {
/// ... snip ...
}
在交易执行期间,我们正在计算我们 "gas burned" 和 "gas used" 的数量。为什么我们必须单独跟踪这些计数器?
- 使用的气体包括燃烧的气体,因此
gas_used >= gas_burnt
,总是; - 当应用任何类型的费用时,它都计入
gas_burnt
和gas_used
; - 当合约执行跨合约调用并将
X
的 gas 量附加到此调用时,则X
计入gas_used
而不是gas_burnt
。这是gas_used
和gas_burnt
之间唯一的差异来源。如果智能合约在完成执行之前失败,则执行 none 的跨合约调用(或由合约创建的交易)并退还附加的气体; - 根据之前的要点,当合约失败时,
gas_used - gas_burnt
将被退回账户,而gas_burnt
将永远丢失(因为gas_burnt
对应于实际上花费了验证者一些计算工作。
while writing this answer, @MaksymZavershynskyi answered as well.
his answer should be considered authoritative while mine is a guess.
查看源代码让我觉得
gas_used
是生产存储和计算的成本(完成不会失败的事情)gas_burnt
是非生产性传输、存储和计算的成本(从 A 点到 B 点获取数据,尝试执行一个函数但在执行过程中失败或导致错误等)
这里有一堆带有源代码链接的片段给我留下了这样的印象
来自 nearcore/runtime/runtime/src/lib.rs
fn generate_refund_receipts(
&self,
receipt: &Receipt,
action_receipt: &ActionReceipt,
result: &mut ActionResult,
) -> Result<(), RuntimeError> {
/// ... snip ...
let gas_refund = if result.result.is_err() {
safe_add_gas(prepaid_gas, exec_gas)? - result.gas_burnt
} else {
safe_add_gas(prepaid_gas, exec_gas)? - result.gas_used
};
/// ... snip ...
}
来自 nearcore/runtime/runtime/src/actions.rs
pub(crate) fn action_function_call(
state_update: &mut TrieUpdate,
apply_state: &ApplyState,
account: &mut Option<Account>,
receipt: &Receipt,
action_receipt: &ActionReceipt,
promise_results: &[PromiseResult],
result: &mut ActionResult,
account_id: &AccountId,
function_call: &FunctionCallAction,
action_hash: &CryptoHash,
config: &RuntimeConfig,
is_last_action: bool,
) -> Result<(), StorageError> {
/// ... snip ...
if let Some(outcome) = outcome {
result.gas_burnt += outcome.burnt_gas;
result.gas_burnt_for_function_call += outcome.burnt_gas;
// Runtime in `generate_refund_receipts` takes care of using proper value for refunds.
// It uses `gas_used` for success and `gas_burnt` for failures. So it's not an issue to
// return a real `gas_used` instead of the `gas_burnt` into `ActionResult` for
// `FunctionCall`s.
result.gas_used += outcome.used_gas;
result.logs.extend(outcome.logs.into_iter());
}
/// ... snip ...
}
来自 nearcore/core/primitives/src/types.rs
pub struct ChunkExtra {
/// ... snip ...
/// Actually how much gas were used.
pub gas_used: Gas
/// ... snip ...
}
来自 nearcore/core/primitives/src/sharding.rs
pub struct ShardChunkHeaderInner {
/// ... snip ...
/// Gas used in this chunk.
pub gas_used: Gas,
/// ... snip ...
}
来自 nearcore/runtime/runtime/src/config.rs
/// ... snip ...
pub fn tx_cost(
config: &RuntimeFeesConfig,
transaction: &Transaction,
gas_price: Balance,
sender_is_receiver: bool,
) -> Result<(Gas, Gas, Balance), IntegerOverflowError> {
let mut gas_burnt: Gas = config.action_receipt_creation_config.send_fee(sender_is_receiver);
gas_burnt = safe_add_gas(
gas_burnt,
total_send_fees(&config, sender_is_receiver, &transaction.actions)?,
)?;
let mut gas_used = safe_add_gas(gas_burnt, config.action_receipt_creation_config.exec_fee())?;
gas_used = safe_add_gas(gas_used, total_exec_fees(&config, &transaction.actions)?)?;
gas_used = safe_add_gas(gas_used, total_prepaid_gas(&transaction.actions)?)?;
let mut total_cost = safe_gas_to_balance(gas_price, gas_used)?;
total_cost = safe_add_balance(total_cost, total_deposit(&transaction.actions)?)?;
Ok((gas_burnt, gas_used, total_cost))
}
/// ... snip ...
/// Total sum of gas that would need to be burnt before we start executing the given actions.
pub fn total_exec_fees(
config: &RuntimeFeesConfig,
actions: &[Action],
) -> Result<Gas, IntegerOverflowError> {
/// ... snip ...
}
/// Get the total sum of deposits for given actions.
pub fn total_deposit(
actions: &[Action]
) -> Result<Balance, IntegerOverflowError> {
/// ... snip ...
}
/// Get the total sum of prepaid gas for given actions.
pub fn total_prepaid_gas(
actions: &[Action]
) -> Result<Gas, IntegerOverflowError> {
/// ... snip ...
}