Skip to content

Commit 133b0e3

Browse files
feat: return Ok() on valid commit/finalize without failing (#61)
* feat: return Ok() on valid commit/finalize without failing * chore: add magicblock prefix to the crate
1 parent 5e66222 commit 133b0e3

File tree

11 files changed

+75
-57
lines changed

11 files changed

+75
-57
lines changed

Cargo.lock

Lines changed: 21 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
2-
name = "delegation-program"
2+
name = "magicblock-delegation-program"
33
description = "Delegation program for the Ephemeral Rollups"
4-
version = "0.1.3"
4+
version = "1.0.0"
55
authors = ["Magicblock Labs <[email protected]>"]
66
edition = "2021"
77
license = "MIT"
@@ -37,4 +37,4 @@ rand = "0.8.5"
3737
solana-program-test = "1.18.26"
3838
solana-sdk = "1.18.26"
3939
tokio = { version = "1.40.0", features = ["full"] }
40-
delegation-program = { path = ".", features = ["unit_test_config"] }
40+
magicblock-delegation-program = { path = ".", features = ["unit_test_config"] }

src/consts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use solana_program::pubkey;
22
use solana_program::pubkey::Pubkey;
33

44
/// The delegation session fees (extracted in percentage from the delegation PDAs rent on closure).
5-
pub const RENT_FEES_PERCENTAGE: u8 = 30;
5+
pub const RENT_FEES_PERCENTAGE: u8 = 10;
66

77
/// The fees extracted from the validator earnings (extracted in percentage from the validator fees claims).
88
pub const PROTOCOL_FEES_PERCENTAGE: u8 = 10;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub fn process_instruction(
6565
processor::process_init_validator_fees_vault(program_id, accounts, data)?
6666
}
6767
discriminator::DlpDiscriminator::InitFeesVault => {
68-
processor::process_init_fees_vault(program_id, accounts, data)?
68+
processor::process_init_protocol_fees_vault(program_id, accounts, data)?
6969
}
7070
discriminator::DlpDiscriminator::ValidatorClaimFees => {
7171
processor::process_validator_claim_fees(program_id, accounts, data)?

src/processor/commit_state.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::processor::utils::loaders::{
66
load_signer, load_uninitialized_pda,
77
};
88
use crate::processor::utils::pda::create_pda;
9-
use crate::processor::utils::verify::verify_state;
109
use crate::state::{CommitRecord, DelegationMetadata, DelegationRecord, ProgramConfig};
1110
use crate::{
1211
commit_record_seeds_from_delegated_account, commit_state_seeds_from_delegated_account,
@@ -15,8 +14,8 @@ use borsh::BorshDeserialize;
1514
use solana_program::program::invoke;
1615
use solana_program::program_error::ProgramError;
1716
use solana_program::system_instruction::transfer;
18-
use solana_program::system_program;
1917
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
18+
use solana_program::{msg, system_program};
2019

2120
/// Commit a new state of a delegated Pda
2221
///
@@ -55,17 +54,24 @@ pub fn process_commit_state(
5554
let mut delegation_metadata =
5655
DelegationMetadata::try_from_bytes_with_discriminator(&delegation_metadata_data)?;
5756

57+
// If the commit slot is greater than the last update slot, we can proceed.
58+
// If the slot is equal or less, we simply do not commit.
59+
// Since commit instructions are typically bundled, we return without error
60+
// so that correct commits are executed.
61+
if commit_record_slot <= delegation_metadata.last_update_external_slot {
62+
msg!(
63+
"Slot {} is outdated, previous slot is {}. Skipping commit",
64+
commit_record_slot,
65+
delegation_metadata.last_update_external_slot
66+
);
67+
return Ok(());
68+
}
69+
5870
// Once the account is marked as undelegatable, any subsequent commit should fail
5971
if delegation_metadata.is_undelegatable {
6072
return Err(DlpError::AlreadyUndelegated.into());
6173
}
6274

63-
// If the commit slot is greater than the last update slot, we can proceed
64-
// If slot is equal or less, we simply do not commit
65-
if commit_record_slot <= delegation_metadata.last_update_external_slot {
66-
return Err(DlpError::OutdatedSlot.into());
67-
}
68-
6975
// Update delegation metadata undelegation flag
7076
delegation_metadata.is_undelegatable = args.allow_undelegation;
7177
delegation_metadata.to_bytes_with_discriminator(&mut delegation_metadata_data.as_mut())?;
@@ -160,14 +166,7 @@ pub fn process_commit_state(
160166
let mut commit_state_data = commit_state_account.try_borrow_mut_data()?;
161167
(*commit_state_data).copy_from_slice(commit_state_bytes);
162168

163-
// TODO - We'll need to implement state validation
164-
// TODO - fix: this logic should probably be done in either commit_state OR finalize IX, not both
165-
verify_state(
166-
validator,
167-
delegation_record,
168-
&commit_record,
169-
commit_state_account,
170-
)?;
169+
// TODO - Add additional validation for the commitment, e.g. sufficient validator stake
171170

172171
Ok(())
173172
}

src/processor/finalize.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::error::DlpError;
22
use crate::processor::utils::loaders::{
3-
load_initialized_commit_record, load_initialized_commit_state,
3+
is_uninitialized_account, load_initialized_commit_record, load_initialized_commit_state,
44
load_initialized_delegation_metadata, load_initialized_delegation_record,
55
load_initialized_validator_fees_vault, load_owned_pda, load_program, load_signer,
66
};
@@ -9,7 +9,7 @@ use crate::processor::utils::verify::verify_state;
99
use crate::state::{CommitRecord, DelegationMetadata, DelegationRecord};
1010
use solana_program::program_error::ProgramError;
1111
use solana_program::{
12-
account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey, system_program,
12+
account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey, system_program,
1313
};
1414

1515
/// Finalize a committed state, after validation, to a delegated account
@@ -33,12 +33,27 @@ pub fn process_finalize(
3333

3434
load_signer(validator)?;
3535
load_owned_pda(delegated_account, &crate::id())?;
36-
load_initialized_commit_state(delegated_account, commit_state_account, true)?;
37-
load_initialized_commit_record(delegated_account, commit_record_account, true)?;
3836
load_initialized_delegation_record(delegated_account, delegation_record_account, true)?;
3937
load_initialized_delegation_metadata(delegated_account, delegation_metadata_account, true)?;
4038
load_initialized_validator_fees_vault(validator, validator_fees_vault, true)?;
4139
load_program(system_program, system_program::id())?;
40+
let load_cs = load_initialized_commit_state(delegated_account, commit_state_account, true);
41+
let load_cr = load_initialized_commit_record(delegated_account, commit_record_account, true);
42+
43+
// Since finalize instructions are typically bundled, we return without error
44+
// if there is nothing to be finalized, so that correct finalizes are executed
45+
if let (Err(ProgramError::InvalidAccountOwner), Err(ProgramError::InvalidAccountOwner)) =
46+
(&load_cs, &load_cr)
47+
{
48+
if is_uninitialized_account(commit_state_account)
49+
&& is_uninitialized_account(commit_record_account)
50+
{
51+
msg!("No state to be finalized. Skipping finalize.");
52+
return Ok(());
53+
}
54+
}
55+
load_cs?;
56+
load_cr?;
4257

4358
// Load delegation metadata
4459
let mut delegation_metadata_data = delegation_metadata_account.try_borrow_mut_data()?;

src/processor/init_fees_vault.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,26 @@ use crate::processor::utils::pda::create_pda;
99

1010
/// Initialize the global fees vault
1111
///
12-
/// 1. Create the fees vault PDA
13-
pub fn process_init_fees_vault(
12+
/// 1. Create the protocol fees vault PDA
13+
pub fn process_init_protocol_fees_vault(
1414
_program_id: &Pubkey,
1515
accounts: &[AccountInfo],
1616
_data: &[u8],
1717
) -> ProgramResult {
18-
// TODO - nit: for naming convention this vault (account+IX) could be named the "protocol_fees_vault"
19-
2018
// Load Accounts
21-
let [payer, fees_vault, system_program] = accounts else {
19+
let [payer, protocol_fees_vault, system_program] = accounts else {
2220
return Err(ProgramError::NotEnoughAccountKeys);
2321
};
2422

2523
load_signer(payer)?;
2624
load_program(system_program, system_program::id())?;
2725

2826
let bump_fees_vault =
29-
load_uninitialized_pda(fees_vault, fees_vault_seeds!(), &crate::id(), true)?;
27+
load_uninitialized_pda(protocol_fees_vault, fees_vault_seeds!(), &crate::id(), true)?;
3028

3129
// Create the fees vault account
3230
create_pda(
33-
fees_vault,
31+
protocol_fees_vault,
3432
&crate::id(),
3533
8,
3634
fees_vault_seeds!(),

src/processor/undelegate.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,18 +275,16 @@ fn process_delegation_cleanup<'a, 'info>(
275275
fees_vault: &'a AccountInfo<'info>,
276276
validator_fees_vault: &'a AccountInfo<'info>,
277277
) -> ProgramResult {
278-
// TODO - fix: there is a double-fee being applied because validator_claim_fees also applies protocol level fees
279-
// TODO - fix: we should probably only let the validator apply fees there? or not apply extra fees during validator_fees_claim
280278
close_pda_with_fees(
281279
delegation_record_account,
282280
rent_reimbursement,
283-
&[fees_vault, validator_fees_vault],
281+
&[validator_fees_vault, fees_vault],
284282
RENT_FEES_PERCENTAGE,
285283
)?;
286284
close_pda_with_fees(
287285
delegation_metadata_account,
288286
rent_reimbursement,
289-
&[fees_vault, validator_fees_vault],
287+
&[validator_fees_vault, fees_vault],
290288
RENT_FEES_PERCENTAGE,
291289
)?;
292290
Ok(())

src/processor/utils/loaders.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ pub fn load_initialized_pda(
100100
Ok(pda.1)
101101
}
102102

103+
/// Returns true if the account is uninitialized based on the following conditions:
104+
/// - Owner is the system program.
105+
/// - Data is empty.
106+
pub fn is_uninitialized_account(info: &AccountInfo) -> bool {
107+
info.owner.eq(&system_program::id()) && info.data_is_empty()
108+
}
109+
103110
/// Errors if:
104111
/// - Owner is not the system program.
105112
/// - Data is not empty.

src/processor/utils/pda.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub(crate) fn close_pda_with_fees<'a, 'info>(
127127
let total_lamports = target_account.lamports();
128128
let mut remaining_amount = total_lamports;
129129

130+
// TODO - simplify fees charging logic
130131
for fee_address in fees_addresses.iter() {
131132
let fee_amount = (remaining_amount as u128)
132133
.checked_mul(fee_percentage as u128)

tests/buffers/test_delegation.so

-48 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)