-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Init With Root #6441
WIP: Init With Root #6441
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "spl-account-compression" | ||
version = "0.3.0" | ||
version = "0.4.0" | ||
description = "Solana Program Library Account Compression Program" | ||
authors = ["Solana Labs Maintainers <[email protected]>"] | ||
repository = "https://github.com/solana-labs/solana-program-library" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,6 @@ use anchor_lang::{ | |
solana_program::sysvar::{clock::Clock, rent::Rent}, | ||
}; | ||
use borsh::{BorshDeserialize, BorshSerialize}; | ||
|
||
pub mod canopy; | ||
pub mod error; | ||
pub mod events; | ||
|
@@ -47,6 +46,9 @@ use crate::state::{ | |
merkle_tree_get_size, ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1, | ||
}; | ||
use crate::zero_copy::ZeroCopy; | ||
use anchor_lang::solana_program::{ | ||
program::invoke, system_instruction::create_account, system_program, | ||
}; | ||
|
||
/// Exported for Anchor / Solita | ||
pub use spl_concurrent_merkle_tree::{ | ||
|
@@ -69,6 +71,20 @@ pub struct Initialize<'info> { | |
/// Program used to emit changelogs as cpi instruction data. | ||
pub noop: Program<'info, Noop>, | ||
} | ||
/// Context for initializing a new SPL ConcurrentMerkleTree | ||
#[derive(Accounts)] | ||
pub struct InitializeWithRoot<'info> { | ||
#[account(zero)] | ||
/// CHECK: This account will be zeroed out, and the size will be validated | ||
pub merkle_tree: UncheckedAccount<'info>, | ||
|
||
/// Authority that controls write-access to the tree | ||
/// Typically a program, e.g., the Bubblegum contract validates that leaves are valid NFTs. | ||
pub authority: Signer<'info>, | ||
|
||
/// Program used to emit changelogs as cpi instruction data. | ||
pub noop: Program<'info, Noop>, | ||
} | ||
|
||
/// Context for inserting, appending, or replacing a leaf in the tree | ||
/// | ||
|
@@ -173,69 +189,59 @@ pub mod spl_account_compression { | |
update_canopy(canopy_bytes, header.get_max_depth(), None) | ||
} | ||
|
||
/// Note: | ||
/// Supporting this instruction open a security vulnerability for indexers. | ||
/// This instruction has been deemed unusable for publicly indexed compressed NFTs. | ||
/// Indexing batched data in this way requires indexers to read in the `uri`s onto physical storage | ||
/// and then into their database. This opens up a DOS attack vector, whereby this instruction is | ||
/// repeatedly invoked, causing indexers to fail. | ||
/// | ||
/// Because this instruction was deemed insecure, this instruction has been removed | ||
/// until secure usage is available on-chain. | ||
// pub fn init_merkle_tree_with_root( | ||
// ctx: Context<Initialize>, | ||
// max_depth: u32, | ||
// max_buffer_size: u32, | ||
// root: [u8; 32], | ||
// leaf: [u8; 32], | ||
// index: u32, | ||
// _changelog_db_uri: String, | ||
// _metadata_db_uri: String, | ||
// ) -> Result<()> { | ||
// require_eq!( | ||
// *ctx.accounts.merkle_tree.owner, | ||
// crate::id(), | ||
// AccountCompressionError::IncorrectAccountOwner | ||
// ); | ||
// let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?; | ||
|
||
// let (mut header_bytes, rest) = | ||
// merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1); | ||
|
||
// let mut header = ConcurrentMerkleTreeHeader::try_from_slice(&header_bytes)?; | ||
// header.initialize( | ||
// max_depth, | ||
// max_buffer_size, | ||
// &ctx.accounts.authority.key(), | ||
// Clock::get()?.slot, | ||
// ); | ||
// header.serialize(&mut header_bytes)?; | ||
// let merkle_tree_size = merkle_tree_get_size(&header)?; | ||
// let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size); | ||
|
||
// // Get rightmost proof from accounts | ||
// let mut proof = vec![]; | ||
// for node in ctx.remaining_accounts.iter() { | ||
// proof.push(node.key().to_bytes()); | ||
// } | ||
// fill_in_proof_from_canopy(canopy_bytes, header.max_depth, index, &mut proof)?; | ||
// assert_eq!(proof.len(), max_depth as usize); | ||
|
||
// let id = ctx.accounts.merkle_tree.key(); | ||
// // A call is made to ConcurrentMerkleTree::initialize_with_root(root, leaf, proof, index) | ||
// let change_log = merkle_tree_apply_fn!( | ||
// header, | ||
// id, | ||
// tree_bytes, | ||
// initialize_with_root, | ||
// root, | ||
// leaf, | ||
// &proof, | ||
// index | ||
// )?; | ||
// wrap_event(change_log.try_to_vec()?, &ctx.accounts.log_wrapper)?; | ||
// update_canopy(canopy_bytes, header.max_depth, Some(change_log)) | ||
// } | ||
// Creates a new merkle tree with an existing root in the buffer. | ||
// The indexers for the specific tree use case are in charge of handling the indexing or shadow indexing of the tree. | ||
// Shadow indexing is a technique that allows for the indexing of a tree incrementally with update operations. | ||
pub fn init_merkle_tree_with_root( | ||
ctx: Context<InitializeWithRoot>, | ||
max_depth: u32, | ||
max_buffer_size: u32, | ||
root: [u8; 32], | ||
leaf: [u8; 32], // the first leaf | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: comment or param name should be |
||
leaf_index: u32, | ||
) -> Result<()> { | ||
require_eq!( | ||
*ctx.accounts.merkle_tree.owner, | ||
crate::id(), | ||
AccountCompressionError::IncorrectAccountOwner | ||
); | ||
let mut merkle_tree_bytes: std::cell::RefMut<'_, &mut [u8]> = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is the type added for |
||
ctx.accounts.merkle_tree.try_borrow_mut_data()?; | ||
|
||
let proof: Vec<[u8; 32]> = ctx.remaining_accounts | ||
.into_iter() | ||
.map(|a| a.key().to_bytes()) | ||
.collect(); | ||
assert_eq!(proof.len(), max_depth as usize); | ||
let (mut header_bytes, rest) = | ||
merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1); | ||
let mut header = ConcurrentMerkleTreeHeader::try_from_slice(&header_bytes)?; | ||
header.initialize( | ||
max_depth, | ||
max_buffer_size, | ||
&ctx.accounts.authority.key(), | ||
Clock::get()?.slot, | ||
); | ||
header.serialize(&mut header_bytes)?; | ||
let merkle_tree_size = merkle_tree_get_size(&header)?; | ||
let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size); | ||
let id = ctx.accounts.merkle_tree.key(); | ||
let change_log_event = merkle_tree_apply_fn_mut!( | ||
header, | ||
id, | ||
tree_bytes, | ||
initialize_with_root, | ||
root, | ||
leaf, | ||
&proof, | ||
leaf_index, | ||
)?; | ||
wrap_event( | ||
&AccountCompressionEvent::ChangeLog(*change_log_event), | ||
&ctx.accounts.noop, | ||
)?; | ||
update_canopy(canopy_bytes, header.get_max_depth(), None) | ||
} | ||
|
||
/// Executes an instruction that overwrites a leaf node. | ||
/// Composing programs should check that the data hashed into previous_leaf | ||
|
@@ -461,10 +467,11 @@ pub mod spl_account_compression { | |
// Close merkle tree account | ||
// 1. Move lamports | ||
let dest_starting_lamports = ctx.accounts.recipient.lamports(); | ||
**ctx.accounts.recipient.lamports.borrow_mut() = dest_starting_lamports | ||
let tree_lamports = dest_starting_lamports | ||
.checked_add(ctx.accounts.merkle_tree.lamports()) | ||
.unwrap(); | ||
**ctx.accounts.merkle_tree.lamports.borrow_mut() = 0; | ||
**ctx.accounts.recipient.try_borrow_mut_lamports()? = tree_lamports; | ||
**ctx.accounts.merkle_tree.try_borrow_mut_lamports()? = 0; | ||
austbot marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 2. Set all CMT account bytes to 0 | ||
header_bytes.fill(0); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just use
Initialize
as its a duplicate struct?