Tools
The hpl-toolkit
Rust crate is an essential component for interacting with Honeycomb's compressed account structures. Below is a comprehensive guide on its functionalities and how to utilize them within your program.
ControlledMerkleTrees
A struct that encapsulates the management of multiple Merkle trees associated with a compressed account.
pub schema: Schema
- Defines the schema of the compressed account.merkle_trees: Vec<Pubkey>
- Holds the public keys of all Merkle trees created for a specific schema.pub active: u8
- Indicates the active Merkle tree pointer.
Schema and SchemaValue
Enums derived from the serde_json
crate that represent the JSON structure and value formats in Rust.
Schema
- Enum for defining the structure of compressed account data.SchemaValue
- Enum for defining the values within the structure.
Traits
ToSchema
Defines the schema of the compressed data.
fn schema() -> Schema;
- Returns the schema as aSchema
enum.fn schema_value(&self) -> SchemaValue;
- Returns the value of the data as aSchemaValue
enum.
ToNode
Converts the compressed data into a Merkle tree node.
fn to_node(&self) -> Node;
- Converts the data into a node, whereNode
is an array of 32 bytes.
CompressedData & CompressedDataChunk
Deals with event streams derived from the schema value.
fn event_stream
- Converts theschema_value
into a compression event that the DAS can understand.
Macros
compressed_account
- Automatically implements the
ToSchema
,ToNode
,CompressedData
, andCompressedDataChunk
traits for the specified struct.
CompressedDataEvent
An enum that outlines the events related to compressed data operations.
TreeSchemaValue
- Indicates the creation of a new tree with a specified schema for all leaves.Leaf
- Indicates the creation or update of a compressed account.
Examples
Compressed Account Structure
#[compressed_account]
pub struct ExampleAccount {
pub value: u64,
#[chunk]
pub inner_example: InnerExample
}
#[compressed_account(chunk = inner_example)]
pub struct InnerExample {
...
}
Creating a New Tree
// before creating a new tree, you must have a ControlledMerkleTree stored somewhere which can be created as follows
example_account_trees = ControlledMerkleTrees {
active: 0,
merkle_trees: Vec::new(),
schema: ExampleAccount::schema(),
};
// while creating new tree the ControlledMerkleTree must be already created
example_account_trees.merkle_trees.push(ctx.accounts.merkle_tree.key());
let event = CompressedDataEvent::tree(
merkle_tree_address,
example_account_trees.schema.clone(),
program_id,
String::from("ExampleAccount"),
);
event.wrap(&spl_noop_program)?;
Creating a New Account (Leaf)
// checks if the merkle tree provided is the current active merkle tree and also if it has only 1 capacity left and then increase active counter
let (leaf_idx, seq) = example_account_trees.assert_append(merkle_tree.to_account_info())?;
let event = CompressedDataEvent::Leaf {
slot: clock_slot,
tree_id: merkle_tree_address.to_bytes(),
leaf_idx,
seq: seq + 1,
stream_type: example_account.event_stream(),
};
event.wrap(log_wrapper)?;
// use the following function to get hash of the account to append leaf
append_leaf( ..., example_account.to_compressed().to_node(), ... )?;
Updating account chunk data
/// ExampleAccountCompressed is auto generated from `compressed_account` macro
let current_data = ExampleAccountCompressed {
value: current_value,
inner_example: current_inner_example_hash,
};
let mut updated_data = UserCompressed {
value: ...,
inner_example: updated_inner_example.to_node()
};
let event = CompressedDataEvent::Leaf {
slot: ctx.accounts.clock.slot,
tree_id: ctx.accounts.merkle_tree.key().to_bytes(),
leaf_idx: args.leaf_idx,
seq: merkle_tree_apply_fn!(merkle_tree_bytes get_seq) + 1,
stream_type: updated_inner_example.event_stream(),
};
event.wrap(&spl_noop_program)?;
// use the following function to get hash of the account to replace leaf
replace_leaf( ..., current_data.to_node(), updated_data.to_node(), ... )?;
Deleting account
let current_data = ExampleAccountCompressed {
value: current_value,
inner_example: current_inner_example_hash,
};
let event = CompressedDataEvent::Leaf {
slot: ctx.accounts.clock.slot,
tree_id: ctx.accounts.merkle_tree.key().to_bytes(),
leaf_idx: args.leaf_idx,
seq: merkle_tree_apply_fn!(merkle_tree_bytes get_seq) + 1,
stream_type: CompressedDataEventStream::Empty,
};
event.wrap(&spl_noop_program)?;
// use the following function to get hash of the account to replace leaf
replace_leaf( ..., current_data.to_node(), [0; u32], ... )?;