From 3907f35200a413dad4e67835ffc78d623229bcf8 Mon Sep 17 00:00:00 2001 From: Craig Mayhew Date: Tue, 19 Aug 2025 17:20:34 +0100 Subject: [PATCH 1/3] dynamic per fees in tests rather than hard coded Some(1) --- crates/types/src/irys.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/types/src/irys.rs b/crates/types/src/irys.rs index 1fe19b703e..b30280b2ad 100644 --- a/crates/types/src/irys.rs +++ b/crates/types/src/irys.rs @@ -1,3 +1,4 @@ +use crate::storage_pricing::TERM_FEE; use crate::{ generate_data_root, generate_leaves, resolve_proofs, Address, Base64, CommitmentTransaction, DataLedger, DataTransaction, DataTransactionHeader, IrysBlockHeader, IrysSignature, Signature, @@ -46,10 +47,32 @@ impl IrysSigner { ) -> Result { let mut transaction = self.merklize(data, self.chunk_size as usize)?; - // TODO: These should be calculated from some pricing params passed in - // as a parameter - transaction.header.perm_fee = Some(U256::from(1)); - transaction.header.term_fee = U256::from(1); + // Compute realistic fees using config defaults and pricing helpers + // Term fee: placeholder constant used across pricing until full dynamic pricing is wired + let term_fee = TERM_FEE; + + // Use consensus defaults and genesis price to approximate perm fee (suitable for tests) + let config = crate::ConsensusConfig::testing(); + let bytes_to_store = U256::from(transaction.header.data_size); + + // Calculate base network fee for permanent storage + let cost_per_gb_per_year = config + .annual_cost_per_gb + .cost_per_replica(config.safe_minimum_number_of_years, config.decay_rate)? + .replica_count(config.number_of_ingress_proofs)?; + let base_network_fee = + cost_per_gb_per_year.base_network_fee(bytes_to_store, config.genesis_price)?; + + // Add ingress proof rewards to get total perm_fee + let perm_fee = base_network_fee.add_ingress_proof_rewards( + term_fee, + config.number_of_ingress_proofs, + config.immediate_tx_inclusion_reward_percent, + )?; + + // Set computed fees on the header + transaction.header.term_fee = term_fee; + transaction.header.perm_fee = Some(perm_fee.amount); // Fetch and set last_tx if not provided (primarily for testing). #[expect(clippy::manual_unwrap_or_default, reason = "TODO")] From 6fc14b6564ae09fd48209506d169dbf558ed3431 Mon Sep 17 00:00:00 2001 From: Craig Mayhew Date: Wed, 20 Aug 2025 11:56:39 +0100 Subject: [PATCH 2/3] add tests --- crates/types/src/irys.rs | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/crates/types/src/irys.rs b/crates/types/src/irys.rs index b30280b2ad..67b1bd00cd 100644 --- a/crates/types/src/irys.rs +++ b/crates/types/src/irys.rs @@ -301,4 +301,53 @@ mod tests { assert_eq!(signer, tx.header.signer); } + + #[test] + fn create_transaction_sets_realistic_fees() { + let config = crate::ConsensusConfig::testing(); + let irys = IrysSigner::random_signer(&config); + let data_bytes = vec![1_u8; 1234]; + + let tx = irys.create_transaction(data_bytes, None).unwrap(); + + // Term fee should equal the constant TERM_FEE used by pricing helpers + assert_eq!(tx.header.term_fee, crate::storage_pricing::TERM_FEE); + + // Perm fee should equal base_network_fee + ingress proof rewards + let bytes_to_store = crate::U256::from(tx.header.data_size); + let cost_per_gb_per_year = config + .annual_cost_per_gb + .cost_per_replica(config.safe_minimum_number_of_years, config.decay_rate) + .unwrap() + .replica_count(config.number_of_ingress_proofs) + .unwrap(); + let base_network_fee = cost_per_gb_per_year + .base_network_fee(bytes_to_store, config.genesis_price) + .unwrap(); + let expected_perm = base_network_fee + .add_ingress_proof_rewards( + tx.header.term_fee, + config.number_of_ingress_proofs, + config.immediate_tx_inclusion_reward_percent, + ) + .unwrap() + .amount; + + assert_eq!(tx.header.perm_fee, Some(expected_perm)); + } + + #[test] + fn publish_fee_charges_accepts_computed_perm_fee() { + let config = crate::ConsensusConfig::testing(); + let irys = IrysSigner::random_signer(&config); + let data = vec![7_u8; 1024]; + + let tx = irys.create_transaction(data, None).unwrap(); + let perm = tx.header.perm_fee.expect("perm_fee set"); + let term = tx.header.term_fee; + + // Validate perm_fee against distribution logic + crate::transaction::fee_distribution::PublishFeeCharges::new(perm, term, &config) + .expect("perm_fee should be sufficient for ingress rewards + base cost"); + } } From f2fe302e63ae3b6eaf906ad327a1c51de0398d91 Mon Sep 17 00:00:00 2001 From: Craig Mayhew Date: Thu, 21 Aug 2025 08:57:18 +0100 Subject: [PATCH 3/3] WIP --- crates/chain/tests/api/tx_commitments.rs | 57 ++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/crates/chain/tests/api/tx_commitments.rs b/crates/chain/tests/api/tx_commitments.rs index e845d7f08a..7ec9f13a95 100644 --- a/crates/chain/tests/api/tx_commitments.rs +++ b/crates/chain/tests/api/tx_commitments.rs @@ -7,7 +7,8 @@ use irys_chain::IrysNodeCtx; use irys_domain::{CommitmentSnapshotStatus, EpochSnapshot}; use irys_testing_utils::initialize_tracing; use irys_types::{ - irys::IrysSigner, Address, CommitmentTransaction, CommitmentType, NodeConfig, H256, U256, + irys::IrysSigner, Address, CommitmentTransaction, CommitmentType, DataLedger, NodeConfig, H256, + U256, }; use std::sync::Arc; use tokio::time::Duration; @@ -254,6 +255,58 @@ async fn heavy_test_commitments_3epochs_test() -> eyre::Result<()> { &signer2_address, )); + // ===== PART 2: Force Submit ledger expansion via additional data ===== + // Post additional data that exceeds one full partition to trigger a new Submit ledger slot assignment + let extra_num_chunks = config.consensus.get_mut().num_chunks_in_partition + 2; + let extra_chunk_size = config.consensus.get_mut().chunk_size; + + let mut extra_data = Vec::with_capacity((extra_chunk_size * extra_num_chunks) as usize); + for chunk_index in 0..extra_num_chunks { + let chunk_data = vec![chunk_index as u8; extra_chunk_size as usize]; + extra_data.extend(chunk_data); + } + + // DATA TX (Part 2): Create and post another data transaction + let mut extra_tx = signer1 + .create_transaction(extra_data, Some(genesis_block.block_hash)) + .expect("To make an additional data transaction"); + + // Use realistic fee values (these are consistent with pricing helpers used elsewhere in tests) + extra_tx.header.perm_fee = Some(U256::from(4_000_000_000_000_u64)); + extra_tx.header.term_fee = U256::from(1_000_000_000_u32); + + // Sign and submit the data transaction + let extra_tx = signer1 + .sign_transaction(extra_tx) + .expect("extra data tx should be signable"); + + node.post_data_tx_raw(&extra_tx.header).await; + + let res = node.wait_for_mempool(extra_tx.header.id, 5).await; + assert_matches!(res, Ok(())); + + // Mine enough blocks to reach the next epoch boundary and apply assignments + info!("MINE THIRD EPOCH BLOCK (Submit expansion):"); + node.mine_blocks(num_blocks_in_epoch + 2).await?; + + // wait for the block to be migrated + node.wait_until_height_confirmed(10, 50).await?; + + // Validate that Submit ledger has at least one partition assigned to slot_index 1 + let epoch_snapshot = block_tree_guard.read().canonical_epoch_snapshot(); + let submit_assignments: Vec<_> = epoch_snapshot + .partition_assignments + .data_partitions + .values() + .filter(|pa| pa.ledger_id == Some(DataLedger::Submit as u32)) + .copied() + .collect(); + assert!( + submit_assignments.iter().any(|pa| pa.slot_index == Some(1)), + "Expected a Submit ledger partition assigned to slot_index 1 after exceeding one partition" + ); + + // Capture storage module infos after Part 2 expansion sm_infos_before = node .node_ctx .block_tree_guard @@ -261,8 +314,6 @@ async fn heavy_test_commitments_3epochs_test() -> eyre::Result<()> { .canonical_epoch_snapshot() .map_storage_modules_to_partition_assignments(); - node.wait_until_height_confirmed(6, 10).await?; - node };