1use anchor_lang::{
26 prelude::*,
27 solana_program::sysvar::{clock::Clock, rent::Rent},
28};
29use borsh::{BorshDeserialize, BorshSerialize};
30
31pub mod canopy;
32pub mod concurrent_tree_wrapper;
33pub mod error;
34pub mod events;
35#[macro_use]
36pub mod macros;
37mod noop;
38pub mod state;
39pub mod zero_copy;
40
41pub use crate::noop::{wrap_application_data_v1, Noop};
42
43use crate::canopy::{fill_in_proof_from_canopy, update_canopy};
44use crate::concurrent_tree_wrapper::*;
45pub use crate::error::AccountCompressionError;
46pub use crate::events::{AccountCompressionEvent, ChangeLogEvent};
47use crate::noop::wrap_event;
48use crate::state::{
49 merkle_tree_get_size, ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1,
50};
51use solana_security_txt::security_txt;
52
53pub use spl_concurrent_merkle_tree::{
55 concurrent_merkle_tree::{ConcurrentMerkleTree, FillEmptyOrAppendArgs},
56 error::ConcurrentMerkleTreeError,
57 node::Node,
58};
59
60declare_id!("cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK");
61
62security_txt! {
63 name: "SPL Account Compression",
65 project_url: "https://spl.solana.com/account-compression",
66 contacts: "link:https://github.com/solana-labs/solana-program-library/security/advisories/new,mailto:security@solana.com,discord:https://solana.com/discord",
67 policy: "https://github.com/solana-labs/solana-program-library/blob/master/SECURITY.md",
68
69 preferred_languages: "en",
71 source_code: "https://github.com/solana-labs/solana-program-library/tree/master/account-compression",
72 branch: "ac-mainnet-tag",
73 auditors: "https://github.com/solana-labs/security-audits#account-compression"
74}
75
76#[derive(Accounts)]
78pub struct Initialize<'info> {
79 pub merkle_tree: UncheckedAccount<'info>,
82
83 pub authority: Signer<'info>,
86
87 pub noop: Program<'info, Noop>,
89}
90
91#[derive(Accounts)]
96pub struct Modify<'info> {
97 #[account(mut)]
98 pub merkle_tree: UncheckedAccount<'info>,
100
101 pub authority: Signer<'info>,
104
105 pub noop: Program<'info, Noop>,
107}
108
109#[derive(Accounts)]
112pub struct VerifyLeaf<'info> {
113 pub merkle_tree: UncheckedAccount<'info>,
115}
116
117#[derive(Accounts)]
119pub struct TransferAuthority<'info> {
120 #[account(mut)]
121 pub merkle_tree: UncheckedAccount<'info>,
123
124 pub authority: Signer<'info>,
127}
128
129#[derive(Accounts)]
131pub struct CloseTree<'info> {
132 #[account(mut)]
133 pub merkle_tree: AccountInfo<'info>,
135
136 pub authority: Signer<'info>,
138
139 #[account(mut)]
141 pub recipient: AccountInfo<'info>,
142}
143
144#[program]
145pub mod spl_account_compression {
146 use super::*;
147
148 pub fn init_empty_merkle_tree(
161 ctx: Context<Initialize>,
162 max_depth: u32,
163 max_buffer_size: u32,
164 ) -> Result<()> {
165 require_eq!(
166 *ctx.accounts.merkle_tree.owner,
167 crate::id(),
168 AccountCompressionError::IncorrectAccountOwner
169 );
170 let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?;
171
172 let (mut header_bytes, rest) =
173 merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
174
175 let mut header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
176 header.initialize(
177 max_depth,
178 max_buffer_size,
179 &ctx.accounts.authority.key(),
180 Clock::get()?.slot,
181 );
182 header.serialize(&mut header_bytes)?;
183
184 let merkle_tree_size = merkle_tree_get_size(&header)?;
185 let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
186 let id = ctx.accounts.merkle_tree.key();
187
188 let change_log_event = merkle_tree_initialize_empty(&header, id, tree_bytes)?;
189
190 wrap_event(
191 &AccountCompressionEvent::ChangeLog(*change_log_event),
192 &ctx.accounts.noop,
193 )?;
194 update_canopy(canopy_bytes, header.get_max_depth(), None)
195 }
196
197 pub fn replace_leaf(
265 ctx: Context<Modify>,
266 root: [u8; 32],
267 previous_leaf: [u8; 32],
268 new_leaf: [u8; 32],
269 index: u32,
270 ) -> Result<()> {
271 require_eq!(
272 *ctx.accounts.merkle_tree.owner,
273 crate::id(),
274 AccountCompressionError::IncorrectAccountOwner
275 );
276 let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?;
277 let (header_bytes, rest) =
278 merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
279
280 let header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
281 header.assert_valid_authority(&ctx.accounts.authority.key())?;
282 header.assert_valid_leaf_index(index)?;
283
284 let merkle_tree_size = merkle_tree_get_size(&header)?;
285 let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
286
287 let mut proof = vec![];
288 for node in ctx.remaining_accounts.iter() {
289 proof.push(node.key().to_bytes());
290 }
291 fill_in_proof_from_canopy(canopy_bytes, header.get_max_depth(), index, &mut proof)?;
292 let id = ctx.accounts.merkle_tree.key();
293 let args = &SetLeafArgs {
295 current_root: root,
296 previous_leaf,
297 new_leaf,
298 proof_vec: proof,
299 index,
300 };
301 let change_log_event = merkle_tree_set_leaf(&header, id, tree_bytes, args)?;
302
303 update_canopy(
304 canopy_bytes,
305 header.get_max_depth(),
306 Some(&change_log_event),
307 )?;
308 wrap_event(
309 &AccountCompressionEvent::ChangeLog(*change_log_event),
310 &ctx.accounts.noop,
311 )
312 }
313
314 pub fn transfer_authority(
317 ctx: Context<TransferAuthority>,
318 new_authority: Pubkey,
319 ) -> Result<()> {
320 require_eq!(
321 *ctx.accounts.merkle_tree.owner,
322 crate::id(),
323 AccountCompressionError::IncorrectAccountOwner
324 );
325 let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?;
326 let (mut header_bytes, _) =
327 merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
328
329 let mut header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
330 header.assert_valid_authority(&ctx.accounts.authority.key())?;
331
332 header.set_new_authority(&new_authority);
333 header.serialize(&mut header_bytes)?;
334
335 Ok(())
336 }
337
338 pub fn verify_leaf(
341 ctx: Context<VerifyLeaf>,
342 root: [u8; 32],
343 leaf: [u8; 32],
344 index: u32,
345 ) -> Result<()> {
346 require_eq!(
347 *ctx.accounts.merkle_tree.owner,
348 crate::id(),
349 AccountCompressionError::IncorrectAccountOwner
350 );
351 let merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_data()?;
352 let (header_bytes, rest) =
353 merkle_tree_bytes.split_at(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
354
355 let header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
356 header.assert_valid()?;
357 header.assert_valid_leaf_index(index)?;
358
359 let merkle_tree_size = merkle_tree_get_size(&header)?;
360 let (tree_bytes, canopy_bytes) = rest.split_at(merkle_tree_size);
361
362 let mut proof = vec![];
363 for node in ctx.remaining_accounts.iter() {
364 proof.push(node.key().to_bytes());
365 }
366 fill_in_proof_from_canopy(canopy_bytes, header.get_max_depth(), index, &mut proof)?;
367 let id = ctx.accounts.merkle_tree.key();
368
369 let args = &ProveLeafArgs {
370 current_root: root,
371 leaf,
372 proof_vec: proof,
373 index,
374 };
375 merkle_tree_prove_leaf(&header, id, tree_bytes, args)?;
376
377 Ok(())
378 }
379
380 pub fn append(ctx: Context<Modify>, leaf: [u8; 32]) -> Result<()> {
387 require_eq!(
388 *ctx.accounts.merkle_tree.owner,
389 crate::id(),
390 AccountCompressionError::IncorrectAccountOwner
391 );
392 let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?;
393 let (header_bytes, rest) =
394 merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
395
396 let header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
397 header.assert_valid_authority(&ctx.accounts.authority.key())?;
398
399 let id = ctx.accounts.merkle_tree.key();
400 let merkle_tree_size = merkle_tree_get_size(&header)?;
401 let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
402 let change_log_event = merkle_tree_append_leaf(&header, id, tree_bytes, &leaf)?;
403 update_canopy(
404 canopy_bytes,
405 header.get_max_depth(),
406 Some(&change_log_event),
407 )?;
408 wrap_event(
409 &AccountCompressionEvent::ChangeLog(*change_log_event),
410 &ctx.accounts.noop,
411 )
412 }
413
414 pub fn insert_or_append(
419 ctx: Context<Modify>,
420 root: [u8; 32],
421 leaf: [u8; 32],
422 index: u32,
423 ) -> Result<()> {
424 require_eq!(
425 *ctx.accounts.merkle_tree.owner,
426 crate::id(),
427 AccountCompressionError::IncorrectAccountOwner
428 );
429 let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?;
430 let (header_bytes, rest) =
431 merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
432
433 let header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
434 header.assert_valid_authority(&ctx.accounts.authority.key())?;
435 header.assert_valid_leaf_index(index)?;
436
437 let merkle_tree_size = merkle_tree_get_size(&header)?;
438 let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
439
440 let mut proof = vec![];
441 for node in ctx.remaining_accounts.iter() {
442 proof.push(node.key().to_bytes());
443 }
444 fill_in_proof_from_canopy(canopy_bytes, header.get_max_depth(), index, &mut proof)?;
445 let id = ctx.accounts.merkle_tree.key();
447 let args = &FillEmptyOrAppendArgs {
448 current_root: root,
449 leaf,
450 proof_vec: proof,
451 index,
452 };
453 let change_log_event = merkle_tree_fill_empty_or_append(&header, id, tree_bytes, args)?;
454
455 update_canopy(
456 canopy_bytes,
457 header.get_max_depth(),
458 Some(&change_log_event),
459 )?;
460 wrap_event(
461 &AccountCompressionEvent::ChangeLog(*change_log_event),
462 &ctx.accounts.noop,
463 )
464 }
465
466 pub fn close_empty_tree(ctx: Context<CloseTree>) -> Result<()> {
467 require_eq!(
468 *ctx.accounts.merkle_tree.owner,
469 crate::id(),
470 AccountCompressionError::IncorrectAccountOwner
471 );
472 let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?;
473 let (header_bytes, rest) =
474 merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
475
476 let header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
477 header.assert_valid_authority(&ctx.accounts.authority.key())?;
478
479 let merkle_tree_size = merkle_tree_get_size(&header)?;
480 let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
481
482 let id = ctx.accounts.merkle_tree.key();
483 merkle_tree_prove_tree_is_empty(&header, id, tree_bytes)?;
484
485 let dest_starting_lamports = ctx.accounts.recipient.lamports();
488 **ctx.accounts.recipient.lamports.borrow_mut() = dest_starting_lamports
489 .checked_add(ctx.accounts.merkle_tree.lamports())
490 .unwrap();
491 **ctx.accounts.merkle_tree.lamports.borrow_mut() = 0;
492
493 header_bytes.fill(0);
495 tree_bytes.fill(0);
496 canopy_bytes.fill(0);
497
498 Ok(())
499 }
500}