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
4 changes: 3 additions & 1 deletion packages/rs-dpp/src/errors/consensus/fee.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use thiserror::Error;

use crate::state_transition::fee::Credits;

#[derive(Error, Debug)]
pub enum FeeError {
#[error("Current credits balance {balance} is not enough to pay {fee} fee")]
BalanceIsNotEnoughError { balance: u64, fee: i64 },
BalanceIsNotEnoughError { balance: Credits, fee: Credits },
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use crate::{
};

use super::{
fee::calculate_state_transition_fee::calculate_state_transition_fee,
state_transition_execution_context::StateTransitionExecutionContext, StateTransition,
StateTransitionType,
};
Expand Down Expand Up @@ -54,10 +53,6 @@ pub trait StateTransitionLike:
fn get_signature(&self) -> &BinaryData;
/// set a new signature
fn set_signature(&mut self, signature: BinaryData);
/// Calculates the ST fee in credits
fn calculate_fee(&self) -> i64 {
calculate_state_transition_fee(self)
}
/// get modified ids list
fn get_modified_data_ids(&self) -> Vec<Identifier>;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use super::{
operations::{Operation, OperationLike},
DummyFeesResult, Refunds,
};

pub fn calculate_operation_fees(operations: &[Operation]) -> DummyFeesResult {
let mut storage_fee = 0;
let mut processing_fee = 0;
let mut fee_refunds: Vec<Refunds> = Vec::new();

for operation in operations {
storage_fee += operation.get_storage_cost();
processing_fee += operation.get_processing_cost();

// Merge refunds
if let Some(operation_refunds) = operation.get_refunds() {
for identity_refunds in operation_refunds {
let mut existing_identity_refunds = fee_refunds
.iter_mut()
.find(|refund| refund.identifier == identity_refunds.identifier);

if existing_identity_refunds.is_none() {
fee_refunds.push(identity_refunds.clone());
continue;
}

for (epoch_index, credits) in identity_refunds.credits_per_epoch.iter() {
if let Some(ref mut refunds) = existing_identity_refunds {
let epoch = refunds
.credits_per_epoch
.entry(epoch_index.to_string())
.or_default();
*epoch += credits
}
}
}
}
}

DummyFeesResult {
storage: storage_fee,
processing: processing_fee,
fee_refunds,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::state_transition::{
fee::calculate_state_transition_fee_from_operations_factory::calculate_state_transition_fee_from_operations,
StateTransition, StateTransitionLike,
};

use super::FeeResult;

pub fn calculate_state_transition_fee(state_transition: &StateTransition) -> FeeResult {
let execution_context = state_transition.get_execution_context();

calculate_state_transition_fee_from_operations(
&execution_context.get_operations(),
state_transition.get_owner_id(),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use crate::prelude::Identifier;

use super::{
calculate_operation_fees::calculate_operation_fees, constants::DEFAULT_USER_TIP,
operations::Operation, DummyFeesResult, FeeResult,
};

pub fn calculate_state_transition_fee_from_operations(
operations: &[Operation],
identity_id: &Identifier,
) -> FeeResult {
calculate_state_transition_fee_from_operations_with_custom_calculator(
operations,
identity_id,
calculate_operation_fees,
)
}

fn calculate_state_transition_fee_from_operations_with_custom_calculator(
operations: &[Operation],
identity_id: &Identifier,
calculate_operation_fees_fn: impl FnOnce(&[Operation]) -> DummyFeesResult,
) -> FeeResult {
let calculated_fees = calculate_operation_fees_fn(operations);

let storage_fee = calculated_fees.storage;
let processing_fee = calculated_fees.processing;
let fee_refunds = calculated_fees.fee_refunds;

let mut total_refunds = 0;

let owner_refunds = fee_refunds
.iter()
.find(|refunds| identity_id == &refunds.identifier);

if let Some(owner_refunds) = owner_refunds {
total_refunds = owner_refunds
.credits_per_epoch
.iter()
.fold(0, |sum, (_, credits)| sum + credits);
}

let required_amount = (storage_fee - total_refunds) + DEFAULT_USER_TIP;
let desired_amount = (storage_fee + processing_fee - total_refunds) + DEFAULT_USER_TIP;

FeeResult {
storage_fee,
processing_fee,
fee_refunds,
total_refunds,
required_amount,
desired_amount,
}
}

#[cfg(test)]
mod test {
use std::collections::HashMap;

use crate::{
state_transition::fee::{
operations::Operation, Credits, DummyFeesResult, FeeResult, Refunds,
},
tests::utils::generate_random_identifier_struct,
};

use super::calculate_state_transition_fee_from_operations_with_custom_calculator;

#[test]
fn should_calculate_fee_based_on_executed_operations() {
let identifier = generate_random_identifier_struct();
let storage_fee = 10000;
let processing_fee = 1000;
let total_refunds = 1000 + 500;
let required_amount = storage_fee - total_refunds;
let desired_amount = storage_fee + processing_fee - total_refunds;

let mut credits_per_epoch: HashMap<String, Credits> = Default::default();
credits_per_epoch.insert("0".to_string(), 1000);
credits_per_epoch.insert("1".to_string(), 500);

let refunds = Refunds {
identifier,
credits_per_epoch,
};

let mock = |_operations: &[Operation]| -> DummyFeesResult {
DummyFeesResult {
storage: storage_fee,
processing: processing_fee,
fee_refunds: vec![refunds.clone()],
}
};

let result = calculate_state_transition_fee_from_operations_with_custom_calculator(
&[],
&identifier,
mock,
);
let expected = FeeResult {
storage_fee,
processing_fee,
desired_amount,
required_amount,
fee_refunds: vec![refunds],
total_refunds: 1500,
};
assert_eq!(expected, result);
}
}
20 changes: 11 additions & 9 deletions packages/rs-dpp/src/state_transition/fee/constants.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use crate::identity::KeyType;

pub const BASE_ST_PROCESSING_FEE: i64 = 10000; // 84000
pub const FEE_MULTIPLIER: i64 = 2;
pub const DEFAULT_USER_TIP: i64 = 0;
pub const STORAGE_CREDIT_PER_BYTE: i64 = 5000;
pub const PROCESSING_CREDIT_PER_BYTE: i64 = 12;
pub const DELETE_BASE_PROCESSING_COST: i64 = 2000; // 20000
pub const READ_BASE_PROCESSING_COST: i64 = 8400; // 8400
pub const WRITE_BASE_PROCESSING_COST: i64 = 6000; // 60000
use super::Credits;

pub const fn signature_verify_cost(key_type: KeyType) -> i64 {
pub const BASE_ST_PROCESSING_FEE: Credits = 10000; // 84000
pub const FEE_MULTIPLIER: Credits = 2;
pub const DEFAULT_USER_TIP: Credits = 0;
pub const STORAGE_CREDIT_PER_BYTE: Credits = 5000;
pub const PROCESSING_CREDIT_PER_BYTE: Credits = 12;
pub const DELETE_BASE_PROCESSING_COST: Credits = 2000; // 20000
pub const READ_BASE_PROCESSING_COST: Credits = 8400; // 8400
pub const WRITE_BASE_PROCESSING_COST: Credits = 6000; // 60000

pub const fn signature_verify_cost(key_type: KeyType) -> Credits {
match key_type {
KeyType::ECDSA_SECP256K1 => 3000,
KeyType::BLS12_381 => 6000,
Expand Down
52 changes: 28 additions & 24 deletions packages/rs-dpp/src/state_transition/fee/mod.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
use std::borrow::Borrow;
use std::collections::HashMap;

use self::{
constants::FEE_MULTIPLIER,
operations::{Operation, OperationLike},
};
use serde::{Deserialize, Serialize};

pub mod calculate_state_transition_fee;
use crate::prelude::Identifier;

pub mod calculate_operation_fees;
pub mod calculate_state_transition_fee_factory;
pub mod calculate_state_transition_fee_from_operations_factory;
pub mod constants;
pub mod operations;

#[derive(Default)]
pub struct Fees {
storage: i64,
processing: i64,
}

pub fn calculate_operations_fees(
operations: impl IntoIterator<Item = impl Borrow<Operation>>,
) -> Fees {
let mut fees = Fees::default();
pub type Credits = u64;

for operation in operations.into_iter() {
let operation = operation.borrow();
fees.processing += operation.get_processing_cost();
fees.storage += operation.get_storage_cost();
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct FeeResult {
pub storage_fee: Credits,
pub processing_fee: Credits,
pub fee_refunds: Vec<Refunds>,
pub total_refunds: Credits,
pub desired_amount: Credits,
pub required_amount: Credits,
}

fees.storage *= FEE_MULTIPLIER;
fees.processing *= FEE_MULTIPLIER;
#[derive(Default)]
pub struct DummyFeesResult {
storage: Credits,
processing: Credits,
fee_refunds: Vec<Refunds>,
}

fees
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename = "camelCase")]
pub struct Refunds {
pub identifier: Identifier,
pub credits_per_epoch: HashMap<String, Credits>,
}

This file was deleted.

Loading