mirror of
https://github.com/logos-co/nomos-node.git
synced 2026-08-31 03:21:15 +00:00
fix(ledger): accumulate storage gas consumption (#3181)
This commit is contained in:
@@ -9,7 +9,7 @@ use lb_core::{
|
||||
events::TxEvent,
|
||||
mantle::{
|
||||
NoteId, Utxo, Value,
|
||||
gas::{Gas, GasConstants, GasCost, GasPrice},
|
||||
gas::{Gas, GasConstants, GasCost, GasOverflow, GasPrice},
|
||||
ledger::Operation as _,
|
||||
ops::transfer::TransferOp,
|
||||
traits::GenesisTx,
|
||||
@@ -413,6 +413,18 @@ impl LedgerState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Accumulates the storage gas consumed by an applied block into the
|
||||
/// current epoch's counter, which drives the storage price update at the
|
||||
/// next epoch rotation.
|
||||
pub fn add_storage_gas_consumed(self, storage_gas: Gas) -> Result<Self, GasOverflow> {
|
||||
Ok(Self {
|
||||
storage_gas_consumed_in_epoch: self
|
||||
.storage_gas_consumed_in_epoch
|
||||
.checked_add(storage_gas)?,
|
||||
..self
|
||||
})
|
||||
}
|
||||
|
||||
fn try_apply_proof<LeaderProof, Id>(
|
||||
self,
|
||||
slot: Slot,
|
||||
@@ -568,6 +580,12 @@ impl LedgerState {
|
||||
&self.storage_gas_price
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[must_use]
|
||||
pub(crate) const fn storage_gas_consumed_in_epoch(&self) -> Gas {
|
||||
self.storage_gas_consumed_in_epoch
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn aged_utxos(&self) -> &UtxoTree {
|
||||
&self.epoch_state.utxos
|
||||
@@ -2006,4 +2024,30 @@ pub mod tests {
|
||||
(30_289.into(), 1_720_000.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_accumulated_storage_gas_drives_next_epoch_price() {
|
||||
let config = config();
|
||||
let mut ledger = genesis_state(&[utxo()]);
|
||||
|
||||
// Seed a known storage-market state, then accumulate the storage gas
|
||||
// that applied transactions consume during the epoch.
|
||||
ledger.storage_gas_price = 113.into();
|
||||
ledger.storage_gas_ema = 300.into();
|
||||
let ledger = ledger.add_storage_gas_consumed(600.into()).unwrap();
|
||||
|
||||
// Cross a single epoch boundary so the storage price is recomputed.
|
||||
let slot: Slot = (config.epoch_length() + 1).into();
|
||||
assert_eq!(config.epoch(slot), 1);
|
||||
let rotated = ledger
|
||||
.update_epoch_state::<HeaderId>(slot, &SdpLedger::new(0.into()), &config)
|
||||
.unwrap();
|
||||
|
||||
// The accumulated 600 must reach the price update: with a starting price
|
||||
// of 113 and EMA 300 that yields (127, 450).
|
||||
assert_eq!(rotated.storage_gas_price, 127.into());
|
||||
assert_eq!(rotated.storage_gas_ema, 450.into());
|
||||
// The counter resets for the new epoch.
|
||||
assert_eq!(rotated.storage_gas_consumed_in_epoch, 0.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +357,21 @@ impl LedgerState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Accumulates the storage gas consumed by the block into the current
|
||||
/// epoch's counter, which drives the storage price update at the next
|
||||
/// epoch rotation.
|
||||
fn add_storage_gas_consumed<Id>(
|
||||
self,
|
||||
block_storage_gas_consumed: Gas,
|
||||
) -> Result<Self, LedgerError<Id>> {
|
||||
Ok(Self {
|
||||
cryptarchia_ledger: self
|
||||
.cryptarchia_ledger
|
||||
.add_storage_gas_consumed(block_storage_gas_consumed)?,
|
||||
..self
|
||||
})
|
||||
}
|
||||
|
||||
/// Apply the contents of an update to the ledger state.
|
||||
pub fn try_apply_contents<'tx, Tx, Id, Constants: GasConstants>(
|
||||
mut self,
|
||||
@@ -367,6 +382,7 @@ impl LedgerState {
|
||||
Tx: PreverifiedMantleTx<Context = GasPrices> + 'tx,
|
||||
{
|
||||
let mut total_block_execution_gas: Gas = 0.into();
|
||||
let mut total_block_storage_gas: Gas = 0.into();
|
||||
let mut total_fee_burned: GasCost = 0.into();
|
||||
let mut total_fee_tip: GasCost = 0.into();
|
||||
let mut tx_events = Vec::new();
|
||||
@@ -409,6 +425,8 @@ impl LedgerState {
|
||||
total_fee_tip = total_fee_tip.checked_add(tx_fee_tip)?;
|
||||
total_block_execution_gas = total_block_execution_gas
|
||||
.checked_add(tx.execution_gas_consumption::<Constants>(&gas_prices)?)?;
|
||||
total_block_storage_gas =
|
||||
total_block_storage_gas.checked_add(tx.storage_gas_consumption(&gas_prices)?)?;
|
||||
|
||||
// Check that the block is not exceeding the Gas limit
|
||||
if total_block_execution_gas > EXECUTION_GAS_LIMIT {
|
||||
@@ -422,6 +440,9 @@ impl LedgerState {
|
||||
self = self.compute_block_rewards(total_fee_burned, total_fee_tip)?;
|
||||
// Update Execution market state
|
||||
self = self.update_execution_market(total_block_execution_gas);
|
||||
// Accumulate storage gas consumed so the storage market can update the
|
||||
// price at the next epoch rotation.
|
||||
self = self.add_storage_gas_consumed(total_block_storage_gas)?;
|
||||
Ok((self, tx_events))
|
||||
}
|
||||
|
||||
@@ -1731,6 +1752,39 @@ mod tests {
|
||||
assert!(events.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_contents_accumulates_storage_gas() {
|
||||
let utxo = utxo();
|
||||
let config = config();
|
||||
let mut ledger = LedgerState::from_utxos([utxo], &config);
|
||||
update_ledger_prices(&mut ledger, 1, 1);
|
||||
|
||||
// No outputs: the whole input covers the gas cost and the remainder is
|
||||
// tipped. We only assert on the storage counter, not the tip, so the
|
||||
// exact balance is irrelevant.
|
||||
let sk = ZkKey::from(BigUint::from(0u8));
|
||||
let tx = create_tx(vec![utxo.id()], vec![], std::slice::from_ref(&sk))
|
||||
.preverify()
|
||||
.unwrap();
|
||||
|
||||
// The tx must consume a non-zero amount of storage gas for the check to
|
||||
// be meaningful.
|
||||
let storage_gas = tx
|
||||
.storage_gas_consumption(&ledger.get_gas_prices())
|
||||
.unwrap();
|
||||
assert!(storage_gas.into_inner() > 0);
|
||||
|
||||
let (applied, _) = ledger
|
||||
.try_apply_contents::<_, HeaderId, MainnetGasConstants>(&config, std::iter::once(&tx))
|
||||
.unwrap();
|
||||
|
||||
// Storage gas consumed by the tx should be accumulated in the ledger
|
||||
assert_eq!(
|
||||
applied.cryptarchia_ledger.storage_gas_consumed_in_epoch(),
|
||||
storage_gas
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leader_claim_operation() {
|
||||
let leaders = LeaderState::new();
|
||||
|
||||
Reference in New Issue
Block a user