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
10 changes: 5 additions & 5 deletions .pnp.cjs

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

Binary file not shown.
Binary file added .yarn/cache/fsevents-patch-19706e7e35-10.zip
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@
"path-to-regexp": "^1.9.0",
"cookie": "^0.7.0",
"cross-spawn": "^7.0.5",
"nanoid": "^3.3.8"
"nanoid": "^3.3.8",
"base-x": "^3.0.11"
},
"dependencies": {
"node-gyp": "^10.0.1"
Expand Down
4 changes: 2 additions & 2 deletions packages/platform-test-suite/test/e2e/contacts.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ describe('e2e', () => {
describe('Bob', () => {
it('should create user wallet and identity', async () => {
// Create Bob wallet
bobClient = await createClientWithFundedWallet(500000);
bobClient = await createClientWithFundedWallet(150000000); // 1.5 Dash

bobIdentity = await bobClient.platform.identities.register(400000);
bobIdentity = await bobClient.platform.identities.register(140000000); // 1.4 Dash

// Additional wait time to mitigate testnet latency
await waitForSTPropagated();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ describe('Platform', () => {
let identity;

before(async () => {
client = await createClientWithFundedWallet(35000000);
client = await createClientWithFundedWallet(350000000); // 3.5 Dash

// Looks like updating the contact and keeping history requires about
// 7 million credits in fees. Investigate this further.
identity = await client.platform.identities.register(30000000);
identity = await client.platform.identities.register(300000000); // 3 Dash
const nextNonce = await client.platform
.nonceManager.bumpIdentityNonce(identity.getId());
dataContractFixture = await getDataContractFixture(nextNonce);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ describe('Platform', () => {
let document;

before(async () => {
client = await createClientWithFundedWallet(1010000);
client = await createClientWithFundedWallet(101000000); // 1 Dash

identity = await client.platform.identities.register(1000000);
identity = await client.platform.identities.register(100000000);

// Additional wait time to mitigate testnet latency
await waitForSTPropagated();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('Platform', () => {
let walletAccount;

before(async () => {
client = await createClientWithFundedWallet(20000000);
client = await createClientWithFundedWallet(150000000); // 1.5 Dash

walletAccount = await client.getWalletAccount();
});
Expand All @@ -50,7 +50,7 @@ describe('Platform', () => {
});

it('should create an identity', async () => {
identity = await client.platform.identities.register(400000);
identity = await client.platform.identities.register(140000000); // 1.4 Dash

expect(identity).to.exist();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod create_document_types_from_document_schemas;
mod try_from_schema;

#[inline]
fn consensus_or_protocol_data_contract_error(
pub(crate) fn consensus_or_protocol_data_contract_error(
data_contract_error: DataContractError,
) -> ProtocolError {
#[cfg(feature = "validation")]
Expand All @@ -25,7 +25,9 @@ fn consensus_or_protocol_data_contract_error(
}

#[inline]
fn consensus_or_protocol_value_error(platform_value_error: platform_value::Error) -> ProtocolError {
pub(crate) fn consensus_or_protocol_value_error(
platform_value_error: platform_value::Error,
) -> ProtocolError {
#[cfg(feature = "validation")]
{
ProtocolError::ConsensusError(
Expand Down
2 changes: 1 addition & 1 deletion packages/rs-dpp/src/data_contract/document_type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub const EMPTY_TREE_STORAGE_SIZE: usize = 33;
pub const MAX_INDEX_SIZE: usize = 255;
pub const STORAGE_FLAGS_SIZE: usize = 2;

mod property_names {
pub(crate) mod property_names {
pub const DOCUMENTS_KEEP_HISTORY: &str = "documentsKeepHistory";
pub const DOCUMENTS_MUTABLE: &str = "documentsMutable";

Expand Down
1 change: 1 addition & 0 deletions packages/rs-dpp/src/data_contract/methods/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod equal_ignoring_time_based_fields;
mod registration_cost;
pub mod schema;
#[cfg(feature = "validation")]
pub mod validate_document;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
mod v1;

use crate::data_contract::serialized_version::DataContractInSerializationFormat;
use crate::data_contract::DataContract;
use crate::ProtocolError;
use platform_version::version::PlatformVersion;

impl DataContract {
/// Returns the registration cost of the data contract based on the current platform version
/// and the number of associated search keywords.
///
/// This method dispatches to a version-specific implementation based on the
/// platform version configuration. If the version is unrecognized, it returns a version mismatch error.
///
/// # Arguments
/// - `platform_version`: A reference to the platform version, used to determine which
/// registration cost algorithm to apply.
///
/// # Returns
/// - `Ok(u64)`: The total registration cost in credits for this contract.
/// - `Err(ProtocolError)`: If the platform version is unrecognized or if the fee computation overflows.
///
/// # Version Behavior
/// - Version 0: Always returns `0` (used before protocol version 9, ie before 2.0, where registration cost was not charged).
/// - Version 1: Uses a detailed cost model based on document types, indexes, tokens, and keyword count.
pub fn registration_cost(
&self,
platform_version: &PlatformVersion,
) -> Result<u64, ProtocolError> {
match platform_version
.dpp
.contract_versions
.methods
.registration_cost
{
0 => Ok(0), // Before 2.0 it's just 0 (There was some validation cost)
1 => Ok(self.registration_cost_v1(platform_version)),
version => Err(ProtocolError::UnknownVersionMismatch {
method: "DataContract::registration_cost".to_string(),
known_versions: vec![0, 1],
received: version,
}),
}
}
}
Comment thread
QuantumExplorer marked this conversation as resolved.

impl DataContractInSerializationFormat {
/// Returns the registration cost of the data contract based on the current platform version
/// and the number of associated search keywords.
///
/// This method dispatches to a version-specific implementation based on the
/// platform version configuration. If the version is unrecognized, it returns a version mismatch error.
///
/// # Arguments
/// - `platform_version`: A reference to the platform version, used to determine which
/// registration cost algorithm to apply.
///
/// # Returns
/// - `Ok(u64)`: The total registration cost in credits for this contract.
/// - `Err(ProtocolError)`: If the platform version is unrecognized or if the fee computation overflows.
///
/// # Version Behavior
/// - Version 0: Always returns `0` (used before protocol version 9, ie before 2.0, where registration cost was not charged).
/// - Version 1: Uses a detailed cost model based on document types, indexes, tokens, and keyword count.
pub fn registration_cost(
&self,
platform_version: &PlatformVersion,
) -> Result<u64, ProtocolError> {
match platform_version
.dpp
.contract_versions
.methods
.registration_cost
{
0 => Ok(0), // Before 2.0 it's just 0 (There was some validation cost)
1 => Ok(self.registration_cost_v1(platform_version)),
version => Err(ProtocolError::UnknownVersionMismatch {
method: "DataContract::registration_cost".to_string(),
known_versions: vec![0, 1],
received: version,
}),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use crate::data_contract::accessors::v0::DataContractV0Getters;
use crate::data_contract::accessors::v1::DataContractV1Getters;
use crate::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters;
use crate::data_contract::associated_token::token_distribution_rules::accessors::v0::TokenDistributionRulesV0Getters;
use crate::data_contract::document_type::accessors::DocumentTypeV0Getters;
use crate::data_contract::document_type::Index;
use crate::data_contract::serialized_version::DataContractInSerializationFormat;
use crate::fee::Credits;
use crate::prelude::DataContract;
use platform_value::Value;
use platform_version::version::PlatformVersion;

impl DataContract {
/// Computes the registration cost of a data contract based on its document types,
/// indexes, token configurations, and keyword count.
///
/// # Parameters
/// - `platform_version`: A reference to the current platform version providing fee parameters.
///
/// # Returns
/// - `Ok(Credits)`: The total registration cost in credits if all additions are overflow-safe.
/// - `Err(ProtocolError)`: If any arithmetic operation results in overflow.
///
/// # Fee Components
/// - Base contract registration fee.
/// - Per document type registration fee.
/// - Per index registration fee (unique and non-unique).
/// - Token registration fee per token.
/// - Additional fees for tokens using perpetual or pre-programmed distribution.
/// - Search keyword fees (`keyword_count * search_keyword_fee`).
pub(super) fn registration_cost_v1(&self, platform_version: &PlatformVersion) -> Credits {
let fee_version = &platform_version.fee_version.data_contract_registration;
let mut cost = fee_version.base_contract_registration_fee;

for document_type in self.document_types().values() {
cost = cost.saturating_add(fee_version.document_type_registration_fee);

for index in document_type.indexes().values() {
let base_index_fee = if index.contested_index.is_some() {
fee_version.document_type_base_contested_index_registration_fee
} else if index.unique {
fee_version.document_type_base_unique_index_registration_fee
} else {
fee_version.document_type_base_non_unique_index_registration_fee
};

cost = cost.saturating_add(base_index_fee);
}
}

for token_config in self.tokens().values() {
cost = cost.saturating_add(fee_version.token_registration_fee);

if token_config
.distribution_rules()
.perpetual_distribution()
.is_some()
{
cost = cost.saturating_add(fee_version.token_uses_perpetual_distribution_fee);
}

if token_config
.distribution_rules()
.pre_programmed_distribution()
.is_some()
{
cost = cost.saturating_add(fee_version.token_uses_pre_programmed_distribution_fee);
}
}

let keyword_cost = fee_version
.search_keyword_fee
.saturating_mul(self.keywords().len() as u64);

cost = cost.saturating_add(keyword_cost);

cost
}
Comment thread
QuantumExplorer marked this conversation as resolved.
}

impl DataContractInSerializationFormat {
/// Computes the registration cost of a data contract based on its document types,
/// indexes, token configurations, and keyword count.
///
/// # Parameters
/// - `platform_version`: A reference to the current platform version providing fee parameters.
///
/// # Returns
/// - `Ok(Credits)`: The total registration cost in credits if all additions are overflow-safe.
/// - `Err(ProtocolError)`: If any arithmetic operation results in overflow.
///
/// # Fee Components
/// - Base contract registration fee.
/// - Per document type registration fee.
/// - Per index registration fee (unique and non-unique).
/// - Token registration fee per token.
/// - Additional fees for tokens using perpetual or pre-programmed distribution.
/// - Search keyword fees (`keyword_count * search_keyword_fee`).
pub(super) fn registration_cost_v1(&self, platform_version: &PlatformVersion) -> Credits {
let fee_version = &platform_version.fee_version.data_contract_registration;
let mut cost = fee_version.base_contract_registration_fee;

for document_type_schema in self.document_schemas().values() {
cost = cost.saturating_add(fee_version.document_type_registration_fee);

// If this is not okay the registration will fail on basic validation
if let Ok(schema_map) = document_type_schema.to_map() {
// Initialize indices
if let Ok(Some(index_values)) = Value::inner_optional_array_slice_value(
schema_map,
crate::data_contract::document_type::property_names::INDICES,
) {
for index_value in index_values {
if let Ok(index_value_map) = index_value.to_map() {
if let Ok(index) = Index::try_from(index_value_map.as_slice()) {
let base_index_fee = if index.contested_index.is_some() {
fee_version.document_type_base_contested_index_registration_fee
} else if index.unique {
fee_version.document_type_base_unique_index_registration_fee
} else {
fee_version.document_type_base_non_unique_index_registration_fee
};
cost = cost.saturating_add(base_index_fee);
}
}
}
}
};
}

for token_config in self.tokens().values() {
cost = cost.saturating_add(fee_version.token_registration_fee);

if token_config
.distribution_rules()
.perpetual_distribution()
.is_some()
{
cost = cost.saturating_add(fee_version.token_uses_perpetual_distribution_fee);
}

if token_config
.distribution_rules()
.pre_programmed_distribution()
.is_some()
{
cost = cost.saturating_add(fee_version.token_uses_pre_programmed_distribution_fee);
}
}

let keyword_cost = fee_version
.search_keyword_fee
.saturating_mul(self.keywords().len() as u64);

cost = cost.saturating_add(keyword_cost);

cost
}
}
2 changes: 1 addition & 1 deletion packages/rs-dpp/src/fee/fee_result/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl FeeResult {

/// Convenience method to get total fee
pub fn total_base_fee(&self) -> Credits {
self.storage_fee + self.processing_fee
self.storage_fee.saturating_add(self.processing_fee)
}

/// Convenience method to get required removed balance
Expand Down
Loading
Loading