Skip to content

Commit

Permalink
Merge pull request #3 from n00m4d/feat/canopy-size-check
Browse files Browse the repository at this point in the history
Add canopy size check
  • Loading branch information
StanChe authored Jun 5, 2024
2 parents db4a868 + 7aaf57d commit 20478c6
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 69 deletions.
10 changes: 5 additions & 5 deletions clients/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mplx-staking
16 changes: 8 additions & 8 deletions programs/bubblegum/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions programs/bubblegum/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ pub mod bubblegum {
}

/// Creates a new tree.
pub fn create_tree(
ctx: Context<CreateTree>,
pub fn create_tree<'info>(
ctx: Context<'_, '_, '_, 'info, CreateTree<'info>>,
max_depth: u32,
max_buffer_size: u32,
public: Option<bool>,
Expand Down
51 changes: 5 additions & 46 deletions programs/bubblegum/program/src/processor/create_tree.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
use std::mem::size_of;

use anchor_lang::{prelude::*, system_program::System};
use spl_account_compression::{
program::SplAccountCompression,
state::{
merkle_tree_get_size, ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1,
},
Node, Noop,
Noop,
};

use crate::{
error::BubblegumError,
state::{DecompressibleState, TreeConfig, TREE_AUTHORITY_SIZE},
state::{DecompressibleState, TreeConfig, TREE_AUTHORITY_SIZE}, utils::check_canopy_size,
};

pub const MAX_ACC_PROOFS_SIZE: u32 = 17;

#[derive(Accounts)]
pub struct CreateTree<'info> {
#[account(
Expand All @@ -37,15 +29,15 @@ pub struct CreateTree<'info> {
pub system_program: Program<'info, System>,
}

pub(crate) fn create_tree(
ctx: Context<CreateTree>,
pub(crate) fn create_tree<'info>(
ctx: Context<'_, '_, '_, 'info, CreateTree<'info>>,
max_depth: u32,
max_buffer_size: u32,
public: Option<bool>,
) -> Result<()> {
let merkle_tree = ctx.accounts.merkle_tree.to_account_info();

check_canopy_size(&ctx, max_depth, max_buffer_size)?;
check_canopy_size(ctx.accounts.merkle_tree.to_account_info(), ctx.accounts.tree_authority.to_account_info(), max_depth, max_buffer_size)?;

let seed = merkle_tree.key();
let seeds = &[seed.as_ref(), &[ctx.bumps.tree_authority]];
Expand All @@ -70,36 +62,3 @@ pub(crate) fn create_tree(
);
spl_account_compression::cpi::init_empty_merkle_tree(cpi_ctx, max_depth, max_buffer_size)
}

fn check_canopy_size(
ctx: &Context<CreateTree>,
max_depth: u32,
max_buffer_size: u32,
) -> Result<()> {
let merkle_tree_bytes = ctx.accounts.merkle_tree.data.borrow();

let (header_bytes, rest) = merkle_tree_bytes.split_at(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);

let mut header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
header.initialize(
max_depth,
max_buffer_size,
&ctx.accounts.tree_authority.key(),
Clock::get()?.slot,
);

let merkle_tree_size = merkle_tree_get_size(&header)?;

let (_tree_bytes, canopy_bytes) = rest.split_at(merkle_tree_size);

let required_canopy = max_depth.saturating_sub(MAX_ACC_PROOFS_SIZE);

let actual_canopy_size = canopy_bytes.len() / size_of::<Node>();

require!(
(actual_canopy_size as u32) >= required_canopy,
BubblegumError::InvalidCanopySize
);

Ok(())
}
8 changes: 6 additions & 2 deletions programs/bubblegum/program/src/processor/prepare_tree.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use anchor_lang::{prelude::*, system_program::System};
use spl_account_compression::{program::SplAccountCompression, Noop};

use crate::state::{DecompressibleState, TreeConfig, TREE_AUTHORITY_SIZE};
use crate::{
state::{
DecompressibleState, TreeConfig, TREE_AUTHORITY_SIZE,
}, utils::check_canopy_size,
};

#[derive(Accounts)]
pub struct PrepareTree<'info> {
Expand Down Expand Up @@ -31,7 +35,7 @@ pub(crate) fn prepare_tree<'info>(
max_buffer_size: u32,
public: Option<bool>,
) -> Result<()> {
// TODO: check canopy size
check_canopy_size(ctx.accounts.merkle_tree.to_account_info(), ctx.accounts.tree_authority.to_account_info(), max_depth, max_buffer_size)?;

let merkle_tree = ctx.accounts.merkle_tree.to_account_info();
let seed = merkle_tree.key();
Expand Down
2 changes: 2 additions & 0 deletions programs/bubblegum/program/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub const VOUCHER_PREFIX: &str = "voucher";
pub const ASSET_PREFIX: &str = "asset";
pub const COLLECTION_CPI_PREFIX: &str = "collection_cpi";

pub const MAX_ACC_PROOFS_SIZE: u32 = 17;

// TODO: set real keys before mainnet deploy
pub const REALM: Pubkey = solana_program::pubkey!("EzsKaQq68FLZwRaiUx7t17LWVVzsE8wRkhBghFrZGGwG");
pub const REALM_GOVERNING_MINT: Pubkey =
Expand Down
44 changes: 40 additions & 4 deletions programs/bubblegum/program/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use crate::state::{
use std::mem::size_of;

use crate::{error::BubblegumError, state::{
metaplex_adapter::{Creator, MetadataArgs},
ASSET_PREFIX,
};
ASSET_PREFIX, MAX_ACC_PROOFS_SIZE,
}};
use anchor_lang::{
prelude::*,
solana_program::{program_memory::sol_memcmp, pubkey::PUBKEY_BYTES},
};
use solana_program::keccak;
use spl_account_compression::Node;
use spl_account_compression::{state::{merkle_tree_get_size, ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1}, Node};

pub fn hash_creators(creators: &[Creator]) -> Result<[u8; 32]> {
// Convert creator Vec to bytes Vec.
Expand Down Expand Up @@ -106,3 +108,37 @@ pub fn get_asset_id(tree_id: &Pubkey, nonce: u64) -> Pubkey {
)
.0
}

pub(crate) fn check_canopy_size<'info>(
merkle_tree: AccountInfo<'info>,
tree_authority: AccountInfo<'info>,
max_depth: u32,
max_buffer_size: u32,
) -> Result<()> {
let merkle_tree_bytes = merkle_tree.data.borrow();

let (header_bytes, rest) = merkle_tree_bytes.split_at(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);

let mut header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
header.initialize(
max_depth,
max_buffer_size,
&tree_authority.key(),
Clock::get()?.slot,
);

let merkle_tree_size = merkle_tree_get_size(&header)?;

let (_tree_bytes, canopy_bytes) = rest.split_at(merkle_tree_size);

let required_canopy = max_depth.saturating_sub(MAX_ACC_PROOFS_SIZE);

let actual_canopy_size = canopy_bytes.len() / size_of::<Node>();

require!(
(actual_canopy_size as u32) >= required_canopy,
BubblegumError::InvalidCanopySize
);

Ok(())
}
Loading

0 comments on commit 20478c6

Please sign in to comment.