Skip to content

Commit

Permalink
Adding various docs to clarify structs / traits (solana-labs#140)
Browse files Browse the repository at this point in the history
* Adding various docs to clarify structs / traits

Intended to be viewed via `docs.rs`. Locally, run `cargo doc --open` to preview

* Addressing comments
  • Loading branch information
jnwng authored Jul 11, 2022
1 parent 4ec301e commit b407ddd
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 8 deletions.
11 changes: 11 additions & 0 deletions contracts/programs/gummyroll/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
use anchor_lang::prelude::*;
use concurrent_merkle_tree::error::CMTError;

/// Errors related to misconfiguration or misuse of the Merkle tree
#[error_code]
pub enum GummyrollError {
/// This error is currently not used.
#[msg("Incorrect leaf length. Expected vec of 32 bytes")]
IncorrectLeafLength,

/// A modification to the tree was invalid and a changelog was not emitted.
/// The proof may be invalid or out-of-date, or the provided leaf hash was invalid.
#[msg("Concurrent merkle tree error")]
ConcurrentMerkleTreeError,

/// An issue was detected with loading the provided account data for this Gummyroll tree.
#[msg("Issue zero copying concurrent merkle tree data")]
ZeroCopyError,

/// See [MerkleRollHeader](/gummyroll/state/struct.MerkleRollHeader.html) for valid configuration options.
#[msg("An unsupported max depth or max buffer size constant was provided")]
MerkleRollConstantsError,

/// When using Canopy, the stored byte length should a multiple of the node's byte length (32 bytes)
#[msg("Expected a different byte length for the merkle roll canopy")]
CanopyLengthMismatch,
}
Expand Down
62 changes: 56 additions & 6 deletions contracts/programs/gummyroll/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
//! Gummyroll is an on-chain Merkle tree that supports concurrent writes.
//!
//! A buffer of proof-like changelogs is stored on-chain that allow multiple proof-based writes to succeed within the same slot.
//! This is accomplished by fast-forwarding out-of-date (or possibly invalid) proofs based on information stored in the changelogs.
//! See a copy of the whitepaper [here](https://google.com)
//!
//! While Gummyroll trees can generically store arbitrary information,
//! one exemplified use-case is the [Bubblegum](https://google.com) contract,
//! which uses Gummyroll trees to store encoded information about NFTs.
//! The use of Gummyroll within Bubblegum allows for:
//! - up to 1 billion NFTs to be stored in a single account on-chain (10,000x decrease in on-chain cost)
//! - (by default) up to 1024 concurrent updates per slot (this number is not correct)
//!
//! Operationally, Gummyroll trees **must** be supplemented by off-chain indexers to cache information
//! about leafs and to power an API that can supply up-to-date proofs to allow updates to the tree.
//! All modifications to Gummyroll trees are settled on the Solana ledger via instructions against the Gummyroll contract.
//! A production-ready indexer (Plerkle) can be found in the [Metaplex program library](https://google.com)
use anchor_lang::{
emit,
prelude::*,
Expand All @@ -19,47 +37,78 @@ pub use concurrent_merkle_tree::{error::CMTError, merkle_roll::MerkleRoll, state

declare_id!("GRoLLMza82AiYN7W9S9KCCtCyyPRAQP2ifBy4v4D5RMD");

/// Context for initializing a new Merkle tere
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(zero)]
/// CHECK: This account will be zeroed out, and the size will be validated
pub merkle_roll: UncheckedAccount<'info>,

/// Authority that validates the content of the trees.
/// Typically a program, e.g., the Bubblegum contract validates that leaves are valid NFTs.
pub authority: Signer<'info>,

/// Authority that is responsible for signing for new additions to the tree.
/// CHECK: unsafe
pub append_authority: UncheckedAccount<'info>,

/// Program used to emit changelogs as instruction data.
/// See `WRAPYChf58WFCnyjXKJHtrPgzKXgHp6MD9aVDqJBbGh`
pub candy_wrapper: Program<'info, CandyWrapper>,
}

/// Context for inserting, appending, or replacing a leaf in the tree
#[derive(Accounts)]
pub struct Modify<'info> {
#[account(mut)]
/// CHECK: This account is validated in the instruction
pub merkle_roll: UncheckedAccount<'info>,

/// Authority that validates the content of the trees.
/// Typically a program, e.g., the Bubblegum contract validates that leaves are valid NFTs.
pub authority: Signer<'info>,

/// Program used to emit changelogs as instruction data.
/// See `WRAPYChf58WFCnyjXKJHtrPgzKXgHp6MD9aVDqJBbGh`
pub candy_wrapper: Program<'info, CandyWrapper>,
}

/// Context for appending a new leaf to the tree
#[derive(Accounts)]
pub struct Append<'info> {
#[account(mut)]
/// CHECK: This account is validated in the instruction
pub merkle_roll: UncheckedAccount<'info>,

/// Authority that validates the content of the trees.
/// Typically a program, e.g., the Bubblegum contract validates that leaves are valid NFTs.
pub authority: Signer<'info>,

/// Authority that is responsible for signing for new additions to the tree.
pub append_authority: Signer<'info>,

/// Program used to emit changelogs as instruction data.
/// See `WRAPYChf58WFCnyjXKJHtrPgzKXgHp6MD9aVDqJBbGh`
pub candy_wrapper: Program<'info, CandyWrapper>,
}

/// Context for validating a provided proof against the Merkle tree.
/// Throws an error if provided proof is invalid.
#[derive(Accounts)]
pub struct VerifyLeaf<'info> {
/// CHECK: This account is validated in the instruction
pub merkle_roll: UncheckedAccount<'info>,
}

/// Context for transferring `authority` or `append_authority`
#[derive(Accounts)]
pub struct TransferAuthority<'info> {
#[account(mut)]
/// CHECK: This account is validated in the instruction
pub merkle_roll: UncheckedAccount<'info>,

/// Authority that validates the content of the trees.
/// Typically a program, e.g., the Bubblegum contract validates that leaves are valid NFTs.
pub authority: Signer<'info>,
}

Expand Down Expand Up @@ -265,8 +314,8 @@ macro_rules! merkle_roll_apply_fn {
pub mod gummyroll {
use super::*;

/// Creates a new merkle tree with maximum leaf capacity of power(2, max_depth)
/// and a minimum concurrency limit of max_buffer_size.
/// Creates a new merkle tree with maximum leaf capacity of `power(2, max_depth)`
/// and a minimum concurrency limit of `max_buffer_size`.
///
/// Concurrency limit represents the # of replace instructions that can be successfully
/// executed with proofs dated for the same root. For example, a maximum buffer size of 1024
Expand Down Expand Up @@ -402,8 +451,8 @@ pub mod gummyroll {
update_canopy(canopy_bytes, header.max_depth, Some(change_log))
}

/// Transfers authority or append authority
/// requires `authority` to sign
/// Transfers `authority` or `append_authority`.
/// Requires `authority` to sign
pub fn transfer_authority(
ctx: Context<TransferAuthority>,
new_authority: Option<Pubkey>,
Expand Down Expand Up @@ -437,7 +486,8 @@ pub mod gummyroll {
Ok(())
}

/// If proof is invalid, error is thrown
/// Verifies a provided proof and leaf.
/// If invalid, throws an error.
pub fn verify_leaf(
ctx: Context<VerifyLeaf>,
root: [u8; 32],
Expand All @@ -461,7 +511,7 @@ pub mod gummyroll {
Ok(())
}

/// This instruction allows the tree's mint_authority to append a new leaf to the tree
/// This instruction allows the tree's `append_authority` to append a new leaf to the tree
/// without having to supply a valid proof.
///
/// This is accomplished by using the rightmost_proof of the merkle roll to construct a
Expand Down
36 changes: 35 additions & 1 deletion contracts/programs/gummyroll/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! State related to storing a buffer of Merkle tree roots on-chain.
//!
use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use concurrent_merkle_tree::state::{ChangeLog, Node};
Expand Down Expand Up @@ -28,9 +30,14 @@ pub struct NewLeafEvent {
pub struct ChangeLogEvent {
/// Public key of the Merkle Roll
pub id: Pubkey,

/// Nodes of off-chain merkle tree
pub path: Vec<PathNode>,

/// Index corresponding to the number of successful operations on this tree.
/// Used by the off-chain indexer to figure out when there are gaps to be backfilled.
pub seq: u64,

/// Bitmap of node parity (used when hashing)
pub index: u32,
}
Expand Down Expand Up @@ -62,13 +69,40 @@ impl<const MAX_DEPTH: usize> From<(Box<ChangeLog<MAX_DEPTH>>, Pubkey, u64)>
}
}

/// Initialization parameters for a Gummyroll Merkle tree.
///
/// Only the following permutations are valid:
///
/// | max_depth | max_buffer_size |
/// | --------- | --------------------- |
/// | 14 | (64, 256, 1024, 2048) |
/// | 20 | (64, 256, 1024, 2048) |
/// | 24 | (64, 256, 512, 1024, 2048) |
/// | 26 | (64, 256, 512, 1024, 2048) |
/// | 30 | (512, 1024, 2048) |
///
#[derive(BorshDeserialize, BorshSerialize)]
#[repr(C)]
pub struct MerkleRollHeader {
/// Buffer of changelogs stored on-chain.
/// Must be a power of 2; see above table for valid combinations.
pub max_buffer_size: u32,

/// Depth of the Merkle tree to store.
/// Tree capacity can be calculated as power(2, max_depth).
/// See above table for valid options.
pub max_depth: u32,

/// Authority that validates the content of the trees.
/// Typically a program, e.g., the Bubblegum contract validates that leaves are valid NFTs.
pub authority: Pubkey,

/// Authority that is responsible for signing for new additions to the tree.
/// DEPRECATED: Likely to be removed!
pub append_authority: Pubkey,

/// Slot corresponding to when the Merkle tree was created.
/// Provides a lower-bound on what slot to start (re-)building a tree from.
pub creation_slot: u64,
}

Expand Down Expand Up @@ -99,4 +133,4 @@ impl anchor_lang::Id for CandyWrapper {
fn id() -> Pubkey {
candy_wrapper::id()
}
}
}
4 changes: 3 additions & 1 deletion contracts/programs/gummyroll/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Various utilities for Gummyroll trees
//!
use crate::state::CandyWrapper;
use anchor_lang::{
prelude::*,
solana_program::{msg, program::invoke, program_error::ProgramError},
};
use crate::state::CandyWrapper;
use bytemuck::{Pod, PodCastError};
use concurrent_merkle_tree::merkle_roll::MerkleRoll;
use std::any::type_name;
Expand Down

0 comments on commit b407ddd

Please sign in to comment.