diff --git a/dlp-api/src/consts.rs b/dlp-api/src/consts.rs index db940329..852d083f 100644 --- a/dlp-api/src/consts.rs +++ b/dlp-api/src/consts.rs @@ -2,17 +2,14 @@ use pinocchio::Address; use crate::compat::{pubkey, Pubkey}; -/// The delegation session fees (extracted in percentage from the delegation PDAs rent on closure). -pub const RENT_FEES_PERCENTAGE: u8 = 10; - /// The fees extracted from the validator earnings (extracted in percentage from the validator fees claims). pub const PROTOCOL_FEES_PERCENTAGE: u8 = 10; -/// Fixed fee per commit (charged for each commit after the first). -pub const COMMIT_FEE_LAMPORTS: u64 = 100_000; +/// Fixed fee per commit after the first. +pub const COMMIT_FEE_LAMPORTS: u64 = 1_000_000; -/// Fixed fee per delegation session (0.0003 SOL). -pub const SESSION_FEE_LAMPORTS: u64 = 300_000; +/// Fixed fee per delegation session. +pub const SESSION_FEE_LAMPORTS: u64 = 3_000_000; /// Default and minimum timeout for requested undelegation. /// Assuming 1 slot is roughly 400ms, then 9000 slots = 60min. diff --git a/src/processor/fast/commit_state.rs b/src/processor/fast/commit_state.rs index 5c1ee4db..408552eb 100644 --- a/src/processor/fast/commit_state.rs +++ b/src/processor/fast/commit_state.rs @@ -11,7 +11,7 @@ use crate::{ args::CommitStateArgs, error::DlpError, merge_diff_copy, pda, - processor::fast::utils::pda::create_pda, + processor::fast::utils::pda::{create_pda, AccountSpace}, requires::{ require_initialized_delegation_metadata, require_initialized_delegation_record, @@ -283,7 +283,7 @@ pub(crate) fn process_commit_state_internal( create_pda( args.commit_state_account, &crate::fast::ID, - args.commit_state_bytes.data_len(), + AccountSpace::CurrentRent(args.commit_state_bytes.data_len()), &[Signer::from(&seeds!( pda::COMMIT_STATE_TAG, args.delegated_account.address().as_ref(), @@ -296,7 +296,7 @@ pub(crate) fn process_commit_state_internal( create_pda( args.commit_record_account, &crate::fast::ID, - CommitRecord::size_with_discriminator(), + AccountSpace::CurrentRent(CommitRecord::size_with_discriminator()), &[Signer::from(&seeds!( pda::COMMIT_RECORD_TAG, args.delegated_account.address().as_ref(), diff --git a/src/processor/fast/delegate.rs b/src/processor/fast/delegate.rs index b768a22d..bc5e2209 100644 --- a/src/processor/fast/delegate.rs +++ b/src/processor/fast/delegate.rs @@ -15,7 +15,10 @@ use crate::{ error::DlpError, pda, processor::{ - fast::{to_pinocchio_program_error, utils::pda::create_pda}, + fast::{ + to_pinocchio_program_error, + utils::pda::{create_pda, AccountSpace}, + }, utils::curve::is_on_curve_fast, }, requires::{ @@ -215,7 +218,7 @@ fn process_delegate_inner( create_pda( delegation_record_account, &crate::fast::ID, - DelegationRecord::size_with_discriminator(), + AccountSpace::LegacyRent(DelegationRecord::size_with_discriminator()), &[Signer::from(&[ Seed::from(pda::DELEGATION_RECORD_TAG), Seed::from(delegated_account.address().as_ref()), @@ -250,7 +253,7 @@ fn process_delegate_inner( create_pda( delegation_metadata_account, &crate::fast::ID, - delegation_metadata.serialized_size(), + AccountSpace::LegacyRent(delegation_metadata.serialized_size()), &[Signer::from(&[ Seed::from(pda::DELEGATION_METADATA_TAG), Seed::from(delegated_account.address().as_ref()), diff --git a/src/processor/fast/delegate_with_actions.rs b/src/processor/fast/delegate_with_actions.rs index d78bfd0d..6e659a36 100644 --- a/src/processor/fast/delegate_with_actions.rs +++ b/src/processor/fast/delegate_with_actions.rs @@ -15,7 +15,10 @@ use crate::{ error::DlpError, pda, processor::{ - fast::{to_pinocchio_program_error, utils::pda::create_pda}, + fast::{ + to_pinocchio_program_error, + utils::pda::{create_pda, AccountSpace}, + }, utils::curve::is_on_curve_fast, }, require_n_accounts_with_optionals, @@ -217,7 +220,9 @@ pub fn process_delegate_with_actions( create_pda( delegation_record_account, &crate::fast::ID, - DelegationRecord::size_with_discriminator() + action_data.len(), + AccountSpace::LegacyRent( + DelegationRecord::size_with_discriminator() + action_data.len(), + ), &[Signer::from(&[ Seed::from(pda::DELEGATION_RECORD_TAG), Seed::from(delegated_account.address().as_ref()), @@ -262,7 +267,7 @@ pub fn process_delegate_with_actions( create_pda( delegation_metadata_account, &crate::fast::ID, - delegation_metadata.serialized_size(), + AccountSpace::LegacyRent(delegation_metadata.serialized_size()), &[Signer::from(&[ Seed::from(pda::DELEGATION_METADATA_TAG), Seed::from(delegated_account.address().as_ref()), diff --git a/src/processor/fast/request_undelegation.rs b/src/processor/fast/request_undelegation.rs index a7538b72..4d045216 100644 --- a/src/processor/fast/request_undelegation.rs +++ b/src/processor/fast/request_undelegation.rs @@ -12,7 +12,10 @@ use super::to_pinocchio_program_error; use crate::{ error::DlpError, pda, - processor::{fast::utils::pda::create_pda, utils::curve::is_on_curve_fast}, + processor::{ + fast::utils::pda::{create_pda, AccountSpace}, + utils::curve::is_on_curve_fast, + }, require, require_eq_keys, require_n_accounts, requires::{ is_uninitialized_account, require_initialized_delegation_metadata, @@ -125,7 +128,9 @@ pub fn process_request_undelegation( create_pda( undelegation_request_account, &crate::fast::ID, - UndelegationRequest::size_with_discriminator(), + AccountSpace::CurrentRent( + UndelegationRequest::size_with_discriminator(), + ), &[Signer::from(&seeds!( pda::UNDELEGATION_REQUEST_TAG, delegated_account.address().as_ref(), diff --git a/src/processor/fast/undelegate.rs b/src/processor/fast/undelegate.rs index f5dcfdfe..6a4d7ca6 100644 --- a/src/processor/fast/undelegate.rs +++ b/src/processor/fast/undelegate.rs @@ -20,7 +20,9 @@ use crate::{ }, error::DlpError, pda, - processor::fast::utils::pda::{close_pda, close_pda_with_fees, create_pda}, + processor::fast::utils::pda::{ + close_pda, close_pda_with_fees, create_pda, AccountSpace, + }, require_n_accounts_with_optionals, requires::{ require_initialized_delegation_metadata, @@ -256,7 +258,7 @@ pub fn process_undelegate( create_pda( undelegate_buffer_account, &crate::fast::ID, - delegated_account.data_len(), + AccountSpace::CurrentRent(delegated_account.data_len()), &[Signer::from(&seeds!( pda::UNDELEGATE_BUFFER_TAG, delegated_account.address().as_ref(), diff --git a/src/processor/fast/undelegate_confined_account.rs b/src/processor/fast/undelegate_confined_account.rs index 52879fe9..c44affb2 100644 --- a/src/processor/fast/undelegate_confined_account.rs +++ b/src/processor/fast/undelegate_confined_account.rs @@ -7,7 +7,7 @@ use super::{process_undelegation_with_cpi, to_pinocchio_program_error}; use crate::{ error::DlpError, pda, - processor::fast::utils::pda::{close_pda, create_pda}, + processor::fast::utils::pda::{close_pda, create_pda, AccountSpace}, require_eq_keys, requires::{ require_authorization, require_initialized_delegation_metadata, @@ -116,7 +116,7 @@ pub fn process_undelegate_confined_account( create_pda( undelegate_buffer_account, &crate::fast::ID, - delegated_account.data_len(), + AccountSpace::CurrentRent(delegated_account.data_len()), &[Signer::from(&seeds!( pda::UNDELEGATE_BUFFER_TAG, delegated_account.address().as_ref(), diff --git a/src/processor/fast/utils/pda.rs b/src/processor/fast/utils/pda.rs index c08b67ed..95eed020 100644 --- a/src/processor/fast/utils/pda.rs +++ b/src/processor/fast/utils/pda.rs @@ -1,31 +1,79 @@ use pinocchio::{ cpi::Signer, + error::ProgramError, sysvars::{rent::Rent, Sysvar}, AccountView, Address, ProgramResult, }; use pinocchio_system::instructions as system; -use crate::consts::PROTOCOL_FEES_PERCENTAGE; +use crate::{consts::PROTOCOL_FEES_PERCENTAGE, error::DlpError}; + +// Legacy rent math follows SIMD-0194, which defines the 6,960 +// lamports-per-byte value used by the simplified rent-exemption formula. +// +// ref: +// https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0194-deprecate-rent-exemption-threshold.md +const LEGACY_RENT_EXEMPT_LAMPORTS_PER_BYTE: u64 = 6960; +const LEGACY_RENT_ACCOUNT_STORAGE_OVERHEAD: u64 = 128; + +// AccountSpace represents the account allocation size. +// Its variant lets the caller choose how rent funding should be computed for that size. +pub(crate) enum AccountSpace { + CurrentRent(usize), + LegacyRent(usize), +} + +impl AccountSpace { + fn space(&self) -> usize { + match self { + Self::CurrentRent(space) | Self::LegacyRent(space) => *space, + } + } + + fn minimum_balance(&self) -> Result { + let current_rent = Rent::get()?.try_minimum_balance(self.space())?; + match self { + Self::CurrentRent(_) => Ok(current_rent), + // Legacy rent is a fee-budget floor, not a substitute for current + // rent exemption. Keep live rent as the lower bound if it ever + // exceeds the legacy formula. + Self::LegacyRent(_) => { + Ok(current_rent.max(legacy_rent(self.space())?)) + } + } + } +} + +// ref: +// https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0194-deprecate-rent-exemption-threshold.md +fn legacy_rent(space: usize) -> Result { + let space = u64::try_from(space).map_err(|_| DlpError::Overflow)?; + space + .checked_add(LEGACY_RENT_ACCOUNT_STORAGE_OVERHEAD) + .and_then(|bytes| { + bytes.checked_mul(LEGACY_RENT_EXEMPT_LAMPORTS_PER_BYTE) + }) + .ok_or(DlpError::Overflow.into()) +} /// Creates a new pda #[inline(always)] pub(crate) fn create_pda( target_account: &AccountView, owner: &Address, - space: usize, + account_space: AccountSpace, pda_signers: &[Signer], payer: &AccountView, ) -> ProgramResult { // Create the account manually or using the create instruction - let rent = Rent::get()?; if target_account.lamports().eq(&0) { // If balance is zero, create account system::CreateAccount { from: payer, to: target_account, - lamports: rent.try_minimum_balance(space)?, - space: space as u64, + lamports: account_space.minimum_balance()?, + space: account_space.space() as u64, owner, } .invoke_signed(pda_signers) @@ -33,8 +81,8 @@ pub(crate) fn create_pda( // Otherwise, if balance is nonzero: // 1) transfer sufficient lamports for rent exemption - let rent_exempt_balance = rent - .try_minimum_balance(space)? + let rent_exempt_balance = account_space + .minimum_balance()? .saturating_sub(target_account.lamports()); if rent_exempt_balance > 0 { system::Transfer { @@ -48,7 +96,7 @@ pub(crate) fn create_pda( // 2) allocate space for the account system::Allocate { account: target_account, - space: space as u64, + space: account_space.space() as u64, } .invoke_signed(pda_signers)?; @@ -115,3 +163,28 @@ pub(crate) fn close_pda_with_fees( target_account.resize(0) } + +#[cfg(test)] +mod tests { + use super::legacy_rent; + use crate::{ + state::{DelegationMetadata, DelegationRecord, UndelegationRequester}, + Pubkey, + }; + + #[test] + fn legacy_rent_uses_pre_reduction_rent_exempt_formula() { + let metadata = DelegationMetadata { + last_commit_id: 0, + undelegation_requester: UndelegationRequester::None, + seeds: vec![b"test-pda".to_vec()], + rent_payer: Pubkey::default(), + }; + + let delegation_rent = + legacy_rent(DelegationRecord::size_with_discriminator()).unwrap() + + legacy_rent(metadata.serialized_size()).unwrap(); + + assert_eq!(delegation_rent, 2_902_320); + } +} diff --git a/tests/test_delegate.rs b/tests/test_delegate.rs index 40e61964..9b96825e 100644 --- a/tests/test_delegate.rs +++ b/tests/test_delegate.rs @@ -76,23 +76,35 @@ async fn test_delegate() { .unwrap() .unwrap(); assert!(delegation_metadata_account.owner.eq(&dlp_api::id())); + assert_eq!( + delegation_metadata_account.lamports, + legacy_rent(delegation_metadata_account.data.len()) + ); // Assert that the delegation record exists and can be parsed - let delegation_record = banks + let delegation_record_account = banks .get_account(delegation_record_pda_from_delegated_account( &DELEGATED_PDA_ID, )) .await .unwrap() .unwrap(); + assert_eq!( + delegation_record_account.lamports, + legacy_rent(delegation_record_account.data.len()) + ); let delegation_record = DelegationRecord::try_from_bytes_with_discriminator( - &delegation_record.data, + &delegation_record_account.data, ) .unwrap(); assert_eq!(delegation_record.owner, DELEGATED_PDA_OWNER_ID); } +fn legacy_rent(space: usize) -> u64 { + (space as u64 + 128) * 6_960 +} + async fn setup_program_test_env() -> (BanksClient, Keypair, Keypair, Hash) { let mut program_test = ProgramTest::new("dlp", dlp_api::ID, None);