Skip to content

Commit

Permalink
refactor: fees to use version system (#1911)
Browse files Browse the repository at this point in the history
Co-authored-by: Quantum Explorer <[email protected]>
  • Loading branch information
ogabrielides and QuantumExplorer authored Jul 14, 2024
1 parent f94d960 commit f5ae7a4
Show file tree
Hide file tree
Showing 142 changed files with 1,328 additions and 308 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/rs-dpp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ tokio = { version = "1.17", features = ["full"] }
pretty_assertions = { version = "1.3.0" }
dpp = { path = ".", features = ["all_features_without_client"] }
assert_matches = "1.5.0"
once_cell = "1.7"

[features]
default = ["platform-value", "state-transitions"]
Expand Down
141 changes: 95 additions & 46 deletions packages/rs-dpp/src/fee/default_costs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@
//! Fee costs for Known Platform operations
//!
use crate::block::epoch::Epoch;
use crate::block::epoch::EpochIndex;
use lazy_static::lazy_static;
use std::collections::HashMap;
use crate::block::epoch::{Epoch, EpochIndex};
use crate::fee::Credits;
use platform_version::version::fee::FeeVersion;
use platform_version::version::PlatformVersion;
use std::collections::BTreeMap;

pub mod constants;

pub type CachedEpochIndexFeeVersions = BTreeMap<EpochIndex, FeeVersion>;

/// A Known Cost Item is an item that changes costs depending on the Epoch
#[derive(Eq, PartialEq, Copy, Clone, Hash)]
pub enum KnownCostItem {
Expand All @@ -57,10 +60,10 @@ pub enum KnownCostItem {
FetchIdentityBalanceProcessingCost,
/// The cost for fetching an identity key
FetchSingleIdentityKeyProcessingCost,
/// The cost for a Double SHA256 operation
DoubleSHA256,
/// The cost for a Single SHA256 operation
SingleSHA256,
/// The cost for a Single SHA256 operation, with a specific size
SingleSHA256(usize),
/// The cost for a Blake3 operation, with a specific size
Blake3(usize),
/// The cost for a EcdsaSecp256k1 signature verification
VerifySignatureEcdsaSecp256k1,
/// The cost for a BLS12_381 signature verification
Expand All @@ -73,58 +76,104 @@ pub enum KnownCostItem {
VerifySignatureEddsa25519Hash160,
}

const EPOCH_COST_UPDATE_VERSIONS: [u16; 1] = [0];
impl KnownCostItem {
#[inline]
pub fn lookup_cost(&self, fee_version: &FeeVersion) -> Credits {
match self {
KnownCostItem::StorageDiskUsageCreditPerByte => {
fee_version.storage.storage_disk_usage_credit_per_byte
}
KnownCostItem::StorageProcessingCreditPerByte => {
fee_version.storage.storage_processing_credit_per_byte
}
KnownCostItem::StorageLoadCreditPerByte => {
fee_version.storage.storage_load_credit_per_byte
}
KnownCostItem::NonStorageLoadCreditPerByte => {
fee_version.storage.non_storage_load_credit_per_byte
}
KnownCostItem::StorageSeekCost => fee_version.storage.storage_seek_cost,
KnownCostItem::FetchIdentityBalanceProcessingCost => {
fee_version
.processing
.fetch_identity_balance_processing_cost
}
KnownCostItem::FetchSingleIdentityKeyProcessingCost => {
fee_version
.processing
.fetch_single_identity_key_processing_cost
}
KnownCostItem::Blake3(size) => {
fee_version.hashing.blake3_base
+ fee_version.hashing.blake3_per_block * *size as u64
}
KnownCostItem::SingleSHA256(size) => {
fee_version.hashing.single_sha256_base
+ fee_version.hashing.sha256_per_block * *size as u64
}
KnownCostItem::VerifySignatureEcdsaSecp256k1 => {
fee_version.signature.verify_signature_ecdsa_secp256k1
}
KnownCostItem::VerifySignatureBLS12_381 => {
fee_version.signature.verify_signature_bls12_381
}
KnownCostItem::VerifySignatureEcdsaHash160 => {
fee_version.signature.verify_signature_ecdsa_hash160
}
KnownCostItem::VerifySignatureBip13ScriptHash => {
fee_version.signature.verify_signature_bip13_script_hash
}
KnownCostItem::VerifySignatureEddsa25519Hash160 => {
fee_version.signature.verify_signature_eddsa25519_hash160
}
}
}

lazy_static! {
static ref EPOCH_COSTS: HashMap<EpochIndex, HashMap<KnownCostItem, u64>> = HashMap::from([(
0,
HashMap::from([
(KnownCostItem::StorageDiskUsageCreditPerByte, 27000u64),
(KnownCostItem::StorageProcessingCreditPerByte, 400u64),
(KnownCostItem::StorageLoadCreditPerByte, 400u64),
(KnownCostItem::NonStorageLoadCreditPerByte, 30u64),
(KnownCostItem::StorageSeekCost, 4000u64),
(KnownCostItem::FetchIdentityBalanceProcessingCost, 10000u64),
(
KnownCostItem::FetchSingleIdentityKeyProcessingCost,
10000u64
),
(KnownCostItem::DoubleSHA256, 800u64),
(KnownCostItem::SingleSHA256, 500u64),
(KnownCostItem::VerifySignatureEcdsaSecp256k1, 3000u64),
(KnownCostItem::VerifySignatureBLS12_381, 6000u64),
(KnownCostItem::VerifySignatureEcdsaHash160, 4000u64),
(KnownCostItem::VerifySignatureBip13ScriptHash, 6000u64),
(KnownCostItem::VerifySignatureEddsa25519Hash160, 3000u64),
])
)]);
pub fn lookup_cost_on_epoch<T: EpochCosts>(
&self,
epoch: &T,
cached_fee_version: &CachedEpochIndexFeeVersions,
) -> Credits {
let version = epoch.active_fee_version(cached_fee_version);
self.lookup_cost(&version)
}
}

/// Costs for Epochs
pub trait EpochCosts {
//todo: should just have a static lookup table
/// Get the closest epoch in the past that has a cost table
/// This is where the base costs last changed
fn get_closest_epoch_index_cost_update_version(&self) -> EpochIndex;
fn active_fee_version(&self, cached_fee_version: &CachedEpochIndexFeeVersions) -> FeeVersion;
/// Get the cost for the known cost item
fn cost_for_known_cost_item(&self, cost_item: KnownCostItem) -> u64;
fn cost_for_known_cost_item(
&self,
cached_fee_version: &CachedEpochIndexFeeVersions,
cost_item: KnownCostItem,
) -> Credits;
}

impl EpochCosts for Epoch {
//todo: should just have a static lookup table
/// Get the closest epoch in the past that has a cost table
/// This is where the base costs last changed
fn get_closest_epoch_index_cost_update_version(&self) -> EpochIndex {
match EPOCH_COST_UPDATE_VERSIONS.binary_search(&self.index) {
Ok(_) => self.index,
Err(pos) => EPOCH_COST_UPDATE_VERSIONS[pos - 1],
/// Get the active fee version for an epoch
fn active_fee_version(&self, cached_fee_version: &CachedEpochIndexFeeVersions) -> FeeVersion {
// If the exact EpochIndex is matching to a FeeVersion update
if let Some(fee_version) = cached_fee_version.get(&self.index) {
return fee_version.clone();
}
// else return the FeeVersion at lower adjacent EpochIndex (if available, else the FeeVersion of first PlatformVersion)
cached_fee_version
.range(..=self.index)
.next_back()
.map(|(_, fee_version)| fee_version)
.unwrap_or_else(|| &PlatformVersion::first().fee_version)
.clone()
}

/// Get the cost for the known cost item
fn cost_for_known_cost_item(&self, cost_item: KnownCostItem) -> u64 {
let epoch = self.get_closest_epoch_index_cost_update_version();
let specific_epoch_costs = EPOCH_COSTS.get(&epoch).unwrap();
*specific_epoch_costs.get(&cost_item).unwrap()
fn cost_for_known_cost_item(
&self,
cached_fee_version: &CachedEpochIndexFeeVersions,
cost_item: KnownCostItem,
) -> Credits {
cost_item.lookup_cost_on_epoch(self, cached_fee_version)
}
}
19 changes: 15 additions & 4 deletions packages/rs-dpp/src/fee/fee_result/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//!
use crate::block::epoch::{Epoch, EpochIndex};
use crate::fee::default_costs::EpochCosts;
use crate::fee::default_costs::KnownCostItem::StorageDiskUsageCreditPerByte;
use crate::fee::default_costs::{CachedEpochIndexFeeVersions, EpochCosts};
use crate::fee::epoch::distribution::calculate_storage_fee_refund_amount_and_leftovers;
use crate::fee::epoch::{BytesPerEpoch, CreditsPerEpoch};
use crate::fee::Credits;
Expand Down Expand Up @@ -38,6 +38,7 @@ impl FeeRefunds {
storage_removal: I,
current_epoch_index: EpochIndex,
epochs_per_era: u16,
previous_fee_versions: &CachedEpochIndexFeeVersions,
) -> Result<Self, ProtocolError>
where
I: IntoIterator<Item = ([u8; 32], C)>,
Expand All @@ -56,7 +57,7 @@ impl FeeRefunds {
// TODO We should use multipliers

let credits: Credits = (bytes as Credits)
.checked_mul(Epoch::new(current_epoch_index)?.cost_for_known_cost_item(StorageDiskUsageCreditPerByte))
.checked_mul(Epoch::new(current_epoch_index)?.cost_for_known_cost_item(previous_fee_versions, StorageDiskUsageCreditPerByte))
.ok_or(ProtocolError::Overflow("storage written bytes cost overflow"))?;

let (amount, _) = calculate_storage_fee_refund_amount_and_leftovers(
Expand Down Expand Up @@ -180,6 +181,11 @@ impl IntoIterator for FeeRefunds {
#[cfg(test)]
mod tests {
use super::*;
use once_cell::sync::Lazy;
use platform_version::version::PlatformVersion;

static EPOCH_CHANGE_FEE_VERSION_TEST: Lazy<CachedEpochIndexFeeVersions> =
Lazy::new(|| BTreeMap::from([(0, PlatformVersion::first().fee_version.clone())]));

mod from_storage_removal {
use super::*;
Expand All @@ -194,8 +200,13 @@ mod tests {
let storage_removal =
BytesPerEpochByIdentifier::from_iter([(identity_id, bytes_per_epoch)]);

let fee_refunds = FeeRefunds::from_storage_removal(storage_removal, 3, 20)
.expect("should create fee refunds");
let fee_refunds = FeeRefunds::from_storage_removal(
storage_removal,
3,
20,
&EPOCH_CHANGE_FEE_VERSION_TEST,
)
.expect("should create fee refunds");

let credits_per_epoch = fee_refunds.get(&identity_id).expect("should exists");

Expand Down
1 change: 1 addition & 0 deletions packages/rs-dpp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub use async_trait;
pub use bls::*;

pub mod prelude {

pub use crate::data_contract::DataContract;
#[cfg(feature = "extended-document")]
pub use crate::document::ExtendedDocument;
Expand Down
18 changes: 10 additions & 8 deletions packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ where
state_read_guard.last_block_info(),
transaction,
platform_ref.state.current_platform_version()?,
platform_ref.state.previous_fee_versions(),
)
} else {
Ok(UnpaidConsensusExecutionError(
Expand Down Expand Up @@ -177,6 +178,7 @@ where
platform_ref.state.last_block_info(),
None,
platform_version,
platform_ref.state.previous_fee_versions(),
)?;

let (estimated_fee_result, errors) = validation_result.into_data_and_errors()?;
Expand Down Expand Up @@ -475,7 +477,7 @@ mod tests {
)
.expect("expected to process state transition");

assert_eq!(processing_result.aggregated_fees().processing_fee, 3055480);
assert_eq!(processing_result.aggregated_fees().processing_fee, 3085850); // TODO: Readjust this test when FeeHashingVersion blake3_base, sha256_ripe_md160_base, blake3_per_block values are finalised

let check_result = platform
.check_tx(
Expand Down Expand Up @@ -677,7 +679,7 @@ mod tests {
// We have one invalid paid for state transition
assert_eq!(processing_result.invalid_paid_count(), 1);

assert_eq!(processing_result.aggregated_fees().processing_fee, 905380);
assert_eq!(processing_result.aggregated_fees().processing_fee, 909400); // TODO: Readjust this test when FeeHashingVersion blake3_base, sha256_ripe_md160_base, blake3_per_block values are finalised

let check_result = platform
.check_tx(
Expand Down Expand Up @@ -829,7 +831,7 @@ mod tests {
// since a fee multiplier of 100 means 100% more of 1 (gives 2)
assert_eq!(
processing_result.aggregated_fees().processing_fee,
3055480 * 2
3085850 * 2 // TODO: Readjust this test when FeeHashingVersion blake3_base, sha256_ripe_md160_base, blake3_per_block values are finalised
);

let check_result = platform
Expand Down Expand Up @@ -1087,7 +1089,7 @@ mod tests {
)
.expect("expected to process state transition");

assert_eq!(processing_result.aggregated_fees().processing_fee, 3055480);
assert_eq!(processing_result.aggregated_fees().processing_fee, 3085850); // TODO: Readjust this test when FeeHashingVersion blake3_base, sha256_ripe_md160_base, blake3_per_block values are finalised

platform
.drive
Expand Down Expand Up @@ -1171,8 +1173,8 @@ mod tests {

assert_eq!(
update_processing_result.aggregated_fees().processing_fee,
7420280
);
7446290
); // TODO: Readjust this test when FeeHashingVersion blake3_base, sha256_ripe_md160_base, blake3_per_block values are finalised

let check_result = platform
.check_tx(
Expand Down Expand Up @@ -1293,7 +1295,7 @@ mod tests {
)
.expect("expected to process state transition");

assert_eq!(processing_result.aggregated_fees().processing_fee, 3055480);
assert_eq!(processing_result.aggregated_fees().processing_fee, 3085850); // TODO: Readjust this test when FeeHashingVersion blake3_base, sha256_ripe_md160_base, blake3_per_block values are finalised

platform
.drive
Expand Down Expand Up @@ -1413,7 +1415,7 @@ mod tests {
// We have one invalid paid for state transition
assert_eq!(processing_result.invalid_paid_count(), 1);

assert_eq!(processing_result.aggregated_fees().processing_fee, 1231050);
assert_eq!(processing_result.aggregated_fees().processing_fee, 1238700); // TODO: Readjust this test when FeeHashingVersion blake3_base, sha256_ripe_md160_base, blake3_per_block values are finalised

let check_result = platform
.check_tx(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ where
block_fees_v0.into(),
transaction,
platform_version,
block_execution_context
.block_platform_state()
.previous_fee_versions(),
)?;

tracing::debug!(block_fees = ?processed_block_fees, "block fees are processed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ mod tests {
use crate::platform_types::platform_state::PlatformState;
use dpp::block::block_info::BlockInfo;
use dpp::fee::epoch::CreditsPerEpoch;

use drive::drive::defaults::INITIAL_PROTOCOL_VERSION;

/// Process and validate an epoch change
Expand Down Expand Up @@ -211,6 +212,7 @@ mod tests {
&BlockInfo::default(),
Some(transaction),
platform_version,
None,
)
.expect("should apply batch");
}
Expand Down Expand Up @@ -285,6 +287,7 @@ mod tests {
&BlockInfo::default(),
Some(transaction),
platform_version,
None,
)
.expect("should apply batch");

Expand Down
Loading

0 comments on commit f5ae7a4

Please sign in to comment.