Skip to content

Commit 5c4e16a

Browse files
add compressible_config draft
1 parent 27749d3 commit 5c4e16a

File tree

12 files changed

+639
-58
lines changed

12 files changed

+639
-58
lines changed

program-tests/anchor-compressible-user/src/lib.rs

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anchor_lang::prelude::*;
22
use light_sdk::{
3-
compressible::CompressionTiming,
3+
compressible::{CompressibleConfig, CompressionTiming},
44
cpi::CpiAccounts,
55
instruction::{account_meta::CompressedAccountMeta, PackedAddressTreeInfo, ValidityProof},
66
light_hasher::{DataHasher, Hasher},
@@ -24,7 +24,57 @@ pub mod anchor_compressible_user {
2424

2525
use super::*;
2626

27-
/// Creates a new compressed user record.
27+
/// Creates a new compressed user record using global config.
28+
pub fn create_record_with_config<'info>(
29+
ctx: Context<'_, '_, '_, 'info, CreateRecordWithConfig<'info>>,
30+
name: String,
31+
proof: ValidityProof,
32+
compressed_address: [u8; 32],
33+
address_tree_info: PackedAddressTreeInfo,
34+
output_state_tree_index: u8,
35+
) -> Result<()> {
36+
let user_record = &mut ctx.accounts.user_record;
37+
38+
// Load config from the config account
39+
let config = CompressibleConfig::load(&ctx.accounts.config)
40+
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
41+
42+
user_record.owner = ctx.accounts.user.key();
43+
user_record.name = name;
44+
user_record.score = 0;
45+
user_record.last_written_slot = Clock::get()?.slot;
46+
user_record.compression_delay = config.compression_delay as u64;
47+
48+
// Verify rent recipient matches config
49+
if ctx.accounts.rent_recipient.key() != config.rent_recipient {
50+
return err!(ErrorCode::InvalidRentRecipient);
51+
}
52+
53+
let cpi_accounts = CpiAccounts::new(
54+
&ctx.accounts.user,
55+
&ctx.remaining_accounts[..],
56+
LIGHT_CPI_SIGNER,
57+
);
58+
let new_address_params =
59+
address_tree_info.into_new_address_params_packed(user_record.key().to_bytes());
60+
61+
compress_pda_new::<UserRecord>(
62+
&user_record.to_account_info(),
63+
compressed_address,
64+
new_address_params,
65+
output_state_tree_index,
66+
proof,
67+
cpi_accounts,
68+
&crate::ID,
69+
&ctx.accounts.rent_recipient,
70+
&config.address_space,
71+
)
72+
.map_err(|e| anchor_lang::prelude::ProgramError::from(e))?;
73+
74+
Ok(())
75+
}
76+
77+
/// Creates a new compressed user record (legacy - uses hardcoded values).
2878
pub fn create_record<'info>(
2979
ctx: Context<'_, '_, '_, 'info, CreateRecord<'info>>,
3080
name: String,
@@ -193,12 +243,67 @@ pub mod anchor_compressible_user {
193243
cpi_accounts,
194244
&crate::ID,
195245
&ctx.accounts.rent_recipient,
246+
COMPRESSION_DELAY, // Use the hardcoded value for legacy function
247+
)
248+
.map_err(|e| anchor_lang::prelude::ProgramError::from(e))?;
249+
Ok(())
250+
}
251+
252+
pub fn compress_record_with_config<'info>(
253+
ctx: Context<'_, '_, '_, 'info, CompressRecordWithConfig<'info>>,
254+
proof: ValidityProof,
255+
compressed_account_meta: CompressedAccountMeta,
256+
) -> Result<()> {
257+
let user_record = &mut ctx.accounts.user_record;
258+
259+
// Load config from the config account
260+
let config = CompressibleConfig::load(&ctx.accounts.config)
261+
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
262+
263+
// Verify rent recipient matches config
264+
if ctx.accounts.rent_recipient.key() != config.rent_recipient {
265+
return err!(ErrorCode::InvalidRentRecipient);
266+
}
267+
268+
let cpi_accounts = CpiAccounts::new(
269+
&ctx.accounts.user,
270+
&ctx.remaining_accounts[..],
271+
LIGHT_CPI_SIGNER,
272+
);
273+
274+
compress_pda::<UserRecord>(
275+
&user_record.to_account_info(),
276+
&compressed_account_meta,
277+
proof,
278+
cpi_accounts,
279+
&crate::ID,
280+
&ctx.accounts.rent_recipient,
281+
config.compression_delay as u64,
196282
)
197283
.map_err(|e| anchor_lang::prelude::ProgramError::from(e))?;
198284
Ok(())
199285
}
200286
}
201287

288+
#[derive(Accounts)]
289+
pub struct CreateRecordWithConfig<'info> {
290+
#[account(mut)]
291+
pub user: Signer<'info>,
292+
#[account(
293+
init,
294+
payer = user,
295+
space = 8 + 32 + 4 + 32 + 8 + 8 + 8, // discriminator + owner + string len + name + score + last_written_slot + compression_delay
296+
seeds = [b"user_record", user.key().as_ref()],
297+
bump,
298+
)]
299+
pub user_record: Account<'info, UserRecord>,
300+
pub system_program: Program<'info, System>,
301+
/// The global config account
302+
pub config: AccountInfo<'info>,
303+
/// Rent recipient - must match config
304+
pub rent_recipient: AccountInfo<'info>,
305+
}
306+
202307
#[derive(Accounts)]
203308
pub struct CreateRecord<'info> {
204309
#[account(mut)]
@@ -229,6 +334,24 @@ pub struct UpdateRecord<'info> {
229334
pub user_record: Account<'info, UserRecord>,
230335
}
231336

337+
#[derive(Accounts)]
338+
pub struct CompressRecordWithConfig<'info> {
339+
#[account(mut)]
340+
pub user: Signer<'info>,
341+
#[account(
342+
mut,
343+
seeds = [b"user_record", user.key().as_ref()],
344+
bump,
345+
constraint = user_record.owner == user.key()
346+
)]
347+
pub user_record: Account<'info, UserRecord>,
348+
pub system_program: Program<'info, System>,
349+
/// The global config account
350+
pub config: AccountInfo<'info>,
351+
/// Rent recipient - must match config
352+
pub rent_recipient: AccountInfo<'info>,
353+
}
354+
232355
#[derive(Accounts)]
233356
pub struct CompressRecord<'info> {
234357
#[account(mut)]
@@ -371,4 +494,6 @@ impl CompressionTiming for GameSession {
371494
pub enum ErrorCode {
372495
#[msg("Invalid account count: PDAs and compressed accounts must match")]
373496
InvalidAccountCount,
497+
#[msg("Rent recipient does not match config")]
498+
InvalidRentRecipient,
374499
}

program-tests/sdk-test/src/compress_dynamic_pda.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use borsh::{BorshDeserialize, BorshSerialize};
22
use light_sdk::{
3-
compressible::compress_pda,
3+
compressible::{compress_pda, CompressibleConfig},
44
cpi::CpiAccounts,
55
error::LightSdkError,
66
instruction::{account_meta::CompressedAccountMeta, ValidityProof},
@@ -21,19 +21,23 @@ pub fn compress_dynamic_pda(
2121
.map_err(|_| LightSdkError::Borsh)?;
2222

2323
let pda_account = &accounts[1];
24-
25-
// CHECK: hardcoded rent recipient.
2624
let rent_recipient = &accounts[2];
27-
if rent_recipient.key != &crate::create_dynamic_pda::RENT_RECIPIENT {
25+
let config_account = &accounts[3];
26+
27+
// Load config
28+
let config = CompressibleConfig::load(config_account)?;
29+
30+
// CHECK: rent recipient from config
31+
if rent_recipient.key != &config.rent_recipient {
2832
return Err(LightSdkError::ConstraintViolation);
2933
}
3034

3135
// Cpi accounts
32-
let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER);
36+
let cpi_config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER);
3337
let cpi_accounts = CpiAccounts::new_with_config(
3438
&accounts[0],
3539
&accounts[instruction_data.system_accounts_offset as usize..],
36-
config,
40+
cpi_config,
3741
);
3842

3943
compress_pda::<MyPdaAccount>(
@@ -43,6 +47,7 @@ pub fn compress_dynamic_pda(
4347
cpi_accounts,
4448
&crate::ID,
4549
rent_recipient,
50+
config.compression_delay as u64,
4651
)?;
4752

4853
// any other program logic here...
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use borsh::{BorshDeserialize, BorshSerialize};
2+
use light_sdk::{
3+
compressible::{create_config, CompressibleConfig},
4+
error::LightSdkError,
5+
};
6+
use solana_program::account_info::AccountInfo;
7+
use solana_program::pubkey::Pubkey;
8+
9+
/// Creates a new compressible config PDA
10+
pub fn process_create_config(
11+
accounts: &[AccountInfo],
12+
instruction_data: &[u8],
13+
) -> Result<(), LightSdkError> {
14+
let mut instruction_data = instruction_data;
15+
let instruction_data = CreateConfigInstructionData::deserialize(&mut instruction_data)
16+
.map_err(|_| LightSdkError::Borsh)?;
17+
18+
// Get accounts
19+
let payer = &accounts[0];
20+
let config_account = &accounts[1];
21+
let system_program = &accounts[2];
22+
23+
// Verify the config PDA
24+
let (expected_pda, _) = CompressibleConfig::derive_pda(&crate::ID);
25+
if config_account.key != &expected_pda {
26+
return Err(LightSdkError::ConstraintViolation);
27+
}
28+
29+
// Create the config
30+
create_config(
31+
config_account,
32+
&instruction_data.update_authority,
33+
&instruction_data.rent_recipient,
34+
&instruction_data.address_space,
35+
instruction_data.compression_delay,
36+
payer,
37+
system_program,
38+
&crate::ID,
39+
)?;
40+
41+
Ok(())
42+
}
43+
44+
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize)]
45+
pub struct CreateConfigInstructionData {
46+
pub update_authority: Pubkey,
47+
pub rent_recipient: Pubkey,
48+
pub address_space: Pubkey,
49+
pub compression_delay: u32,
50+
}

program-tests/sdk-test/src/create_dynamic_pda.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
use borsh::{BorshDeserialize, BorshSerialize};
2-
use light_macros::pubkey;
32
use light_sdk::{
4-
compressible::compress_pda_new,
3+
compressible::{compress_pda_new, CompressibleConfig},
54
cpi::CpiAccounts,
65
error::LightSdkError,
76
instruction::{PackedAddressTreeInfo, ValidityProof},
87
};
98
use light_sdk_types::CpiAccountsConfig;
109
use solana_clock::Clock;
1110
use solana_program::account_info::AccountInfo;
12-
use solana_program::pubkey::Pubkey;
1311
use solana_sysvar::Sysvar;
1412

15-
use crate::decompress_dynamic_pda::{MyPdaAccount, COMPRESSION_DELAY};
16-
17-
pub const ADDRESS_SPACE: Pubkey = pubkey!("CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG");
18-
pub const RENT_RECIPIENT: Pubkey = pubkey!("CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG");
13+
use crate::decompress_dynamic_pda::MyPdaAccount;
1914

2015
/// INITS a PDA and compresses it into a new compressed account.
2116
pub fn create_dynamic_pda(
@@ -29,16 +24,21 @@ pub fn create_dynamic_pda(
2924
let fee_payer = &accounts[0];
3025
// UNCHECKED: ...caller program checks this.
3126
let pda_account = &accounts[1];
32-
// CHECK: hardcoded rent recipient.
3327
let rent_recipient = &accounts[2];
34-
if rent_recipient.key != &RENT_RECIPIENT {
28+
let config_account = &accounts[3];
29+
30+
// Load config
31+
let config = CompressibleConfig::load(config_account)?;
32+
33+
// CHECK: rent recipient from config
34+
if rent_recipient.key != &config.rent_recipient {
3535
return Err(LightSdkError::ConstraintViolation);
3636
}
3737

3838
// Cpi accounts
3939
let cpi_accounts_struct = CpiAccounts::new_with_config(
4040
fee_payer,
41-
&accounts[3..],
41+
&accounts[4..],
4242
CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER),
4343
);
4444

@@ -53,7 +53,7 @@ pub fn create_dynamic_pda(
5353
let mut pda_account_data = MyPdaAccount::try_from_slice(&pda_account.data.borrow())
5454
.map_err(|_| LightSdkError::Borsh)?;
5555
pda_account_data.last_written_slot = Clock::get()?.slot;
56-
pda_account_data.compression_delay = COMPRESSION_DELAY;
56+
pda_account_data.compression_delay = config.compression_delay as u64;
5757

5858
compress_pda_new::<MyPdaAccount>(
5959
pda_account,
@@ -64,7 +64,7 @@ pub fn create_dynamic_pda(
6464
cpi_accounts_struct,
6565
&crate::ID,
6666
rent_recipient,
67-
&ADDRESS_SPACE,
67+
&config.address_space,
6868
)?;
6969

7070
Ok(())

program-tests/sdk-test/src/decompress_dynamic_pda.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use arrayvec::ArrayVec;
21
use borsh::{BorshDeserialize, BorshSerialize};
32
use light_sdk::{
43
account::LightAccount,
@@ -102,24 +101,20 @@ pub fn decompress_multiple_dynamic_pdas(
102101
crate::LIGHT_CPI_SIGNER,
103102
);
104103

105-
// Can be passed in; the custom program does not have to check the seeds.
104+
// Store data and bumps to maintain ownership
106105
let mut compressed_accounts = Vec::new();
107106
let mut pda_account_refs = Vec::new();
107+
let mut stored_bumps = Vec::new();
108108
let mut all_signer_seeds = Vec::new();
109109

110-
for (i, compressed_account_data) in instruction_data.compressed_accounts.into_iter().enumerate()
111-
{
110+
// First pass: collect all the data we need
111+
for (i, compressed_account_data) in instruction_data.compressed_accounts.iter().enumerate() {
112112
let compressed_account = LightAccount::<'_, MyPdaAccount>::new_mut(
113113
&crate::ID,
114114
&compressed_account_data.meta,
115115
compressed_account_data.data.clone(),
116116
)?;
117117

118-
// Create signer seeds with ArrayVec
119-
let mut signer_seeds = ArrayVec::<&[u8], 3>::new();
120-
signer_seeds.push(b"test_pda");
121-
signer_seeds.push(&compressed_account_data.data.data);
122-
123118
// Derive bump for verification
124119
let seeds: Vec<&[u8]> = vec![b"test_pda", &compressed_account_data.data.data];
125120
let (derived_pda, bump) =
@@ -130,15 +125,21 @@ pub fn decompress_multiple_dynamic_pdas(
130125
return Err(LightSdkError::ConstraintViolation);
131126
}
132127

133-
// Add bump to signer seeds
134-
signer_seeds.push(&[bump]);
135-
136128
compressed_accounts.push(compressed_account);
137129
pda_account_refs.push(&pda_accounts[i]);
130+
stored_bumps.push(bump);
131+
}
132+
133+
// Second pass: build signer seeds with stable references
134+
for (i, compressed_account_data) in instruction_data.compressed_accounts.iter().enumerate() {
135+
let mut signer_seeds = Vec::new();
136+
signer_seeds.push(b"test_pda" as &[u8]);
137+
signer_seeds.push(&compressed_account_data.data.data as &[u8]);
138+
signer_seeds.push(&stored_bumps[i..i + 1] as &[u8]);
138139
all_signer_seeds.push(signer_seeds);
139140
}
140141

141-
// Convert ArrayVecs to the format needed by the SDK
142+
// Convert to the format needed by the SDK
142143
let signer_seeds_refs: Vec<&[&[u8]]> = all_signer_seeds
143144
.iter()
144145
.map(|seeds| seeds.as_slice())

0 commit comments

Comments
 (0)