Skip to content

Commit

Permalink
feat: Add helpers for each compressed account action
Browse files Browse the repository at this point in the history
Remove the necessity of constructing `InstructionDataInvokeCpi` manually
by adding utility functions for each type of singular account action:

- **Create** (no inputs, only output)
  - `new_compressed_account` - creates an output compressed account
  - `create_cpi_inputs_for_new_account`
- **Update** (input with old data, output with new data)
  - `input_compressed_account`
  - `output_compressed_accoint`
  - `create_cpi_inputs_for_account_update`
- **Delete** (input with data to delete, no output)
  - `input_compressed_account`
  - `create_cpi_inputs_for_account_deletion`

While this abstraction doesn't fully hide the UTXO model full yet, it
aims to make it clear which kind of inputs and outputs are needed for
each action.
  • Loading branch information
vadorovsky committed Aug 20, 2024
1 parent 3a99b01 commit 14f1554
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 160 deletions.
196 changes: 51 additions & 145 deletions examples/name-service/programs/name-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,27 @@ use std::net::{Ipv4Addr, Ipv6Addr};

use anchor_lang::{prelude::*, solana_program::hash};
use borsh::{BorshDeserialize, BorshSerialize};
use light_hasher::{bytes::AsByteVec, DataHasher, Discriminator, Poseidon};
use light_hasher::bytes::AsByteVec;
use light_sdk::{
light_accounts,
merkle_context::{PackedAddressMerkleContext, PackedMerkleContext, PackedMerkleOutputContext},
utils::create_cpi_inputs_for_new_address,
utils::{create_cpi_inputs_for_account_deletion, create_cpi_inputs_for_new_account},
verify::verify,
LightDiscriminator, LightHasher, LightTraits,
};
use light_system_program::{
invoke::processor::CompressedProof,
sdk::{
address::derive_address,
compressed_account::{
CompressedAccount, CompressedAccountData, PackedCompressedAccountWithMerkleContext,
},
CompressedCpiContext,
},
InstructionDataInvokeCpi, NewAddressParamsPacked, OutputCompressedAccountWithPackedContext,
};
use light_system_program::{invoke::processor::CompressedProof, sdk::CompressedCpiContext};

declare_id!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");

#[program]
pub mod name_service {
use light_sdk::{
compressed_account::{
input_compressed_account, new_compressed_account, output_compressed_account,
},
utils::create_cpi_inputs_for_account_update,
};

use super::*;

#[allow(clippy::too_many_arguments)]
Expand All @@ -46,20 +43,21 @@ pub mod name_service {
name,
rdata,
};
let (compressed_account, new_address_params) = create_compressed_account(
&ctx,
let (compressed_account, new_address_params) = new_compressed_account(
&record,
address_seed,
&address_seed,
&crate::ID,
&merkle_output_context,
&address_merkle_context,
address_merkle_tree_root_index,
ctx.remaining_accounts,
)?;

let signer_seed = b"cpi_signer".as_slice();
let bump = Pubkey::find_program_address(&[signer_seed], &ctx.accounts.self_program.key()).1;
let signer_seeds = [signer_seed, &[bump]];

let inputs = create_cpi_inputs_for_new_address(
let inputs = create_cpi_inputs_for_new_account(
proof,
new_address_params,
compressed_account,
Expand All @@ -78,12 +76,13 @@ pub mod name_service {
proof: CompressedProof,
merkle_context: PackedMerkleContext,
merkle_tree_root_index: u16,
address: [u8; 32],
address_merkle_context: PackedAddressMerkleContext,
name: String,
old_rdata: RData,
new_rdata: RData,
cpi_context: Option<CompressedCpiContext>,
) -> Result<()> {
let address_seed = hash::hash(name.as_bytes()).to_bytes();
let owner = ctx.accounts.signer.key();

// Re-create the old compressed account. It's needed as an input for
Expand All @@ -93,36 +92,41 @@ pub mod name_service {
name: name.clone(),
rdata: old_rdata,
};
let old_compressed_account = compressed_input_account_with_address(
old_record,
address,
let old_compressed_account = input_compressed_account(
&old_record,
&address_seed,
&crate::ID,
&merkle_context,
merkle_tree_root_index,
&address_merkle_context,
ctx.remaining_accounts,
)?;

let new_record = NameRecord {
owner,
name,
rdata: new_rdata,
};
let new_compressed_account =
compressed_output_account_with_address(&new_record, address, &merkle_context)?;
let new_compressed_account = output_compressed_account(
&new_record,
&address_seed,
&crate::ID,
&merkle_context,
&address_merkle_context,
ctx.remaining_accounts,
)?;

let signer_seed = b"cpi_signer".as_slice();
let bump = Pubkey::find_program_address(&[signer_seed], &ctx.accounts.self_program.key()).1;
let signer_seeds = [signer_seed, &[bump]];

let inputs = InstructionDataInvokeCpi {
proof: Some(proof),
new_address_params: vec![],
input_compressed_accounts_with_merkle_context: vec![old_compressed_account],
output_compressed_accounts: vec![new_compressed_account],
relay_fee: None,
compress_or_decompress_lamports: None,
is_compress: false,
signer_seeds: signer_seeds.iter().map(|seed| seed.to_vec()).collect(),
let inputs = create_cpi_inputs_for_account_update(
proof,
old_compressed_account,
new_compressed_account,
&signer_seeds,
cpi_context,
};
);

verify(ctx, &inputs, &[&signer_seeds])?;

Expand All @@ -135,38 +139,38 @@ pub mod name_service {
proof: CompressedProof,
merkle_context: PackedMerkleContext,
merkle_tree_root_index: u16,
address: [u8; 32],
address_merkle_context: PackedAddressMerkleContext,
name: String,
rdata: RData,
cpi_context: Option<CompressedCpiContext>,
) -> Result<()> {
let address_seed = hash::hash(name.as_bytes()).to_bytes();

let record = NameRecord {
owner: ctx.accounts.signer.key(),
name,
rdata,
};
let compressed_account = compressed_input_account_with_address(
record,
address,
let compressed_account = input_compressed_account(
&record,
&address_seed,
&crate::ID,
&merkle_context,
merkle_tree_root_index,
&address_merkle_context,
ctx.remaining_accounts,
)?;

let signer_seed = b"cpi_signer".as_slice();
let bump = Pubkey::find_program_address(&[signer_seed], &ctx.accounts.self_program.key()).1;
let signer_seeds = [signer_seed, &[bump]];

let inputs = InstructionDataInvokeCpi {
proof: Some(proof),
new_address_params: vec![],
input_compressed_accounts_with_merkle_context: vec![compressed_account],
output_compressed_accounts: vec![],
relay_fee: None,
compress_or_decompress_lamports: None,
is_compress: false,
signer_seeds: signer_seeds.iter().map(|seed| seed.to_vec()).collect(),
let inputs = create_cpi_inputs_for_account_deletion(
proof,
compressed_account,
&signer_seeds,
cpi_context,
};
);

verify(ctx, &inputs, &[&signer_seeds])?;

Expand Down Expand Up @@ -224,101 +228,3 @@ pub struct NameService<'info> {
#[authority]
pub cpi_signer: AccountInfo<'info>,
}

fn create_compressed_account(
ctx: &Context<'_, '_, '_, '_, NameService<'_>>,
record: &NameRecord,
address_seed: [u8; 32],
merkle_output_context: &PackedMerkleOutputContext,
address_merkle_context: &PackedAddressMerkleContext,
address_merkle_tree_root_index: u16,
) -> Result<(
OutputCompressedAccountWithPackedContext,
NewAddressParamsPacked,
)> {
let data = record.try_to_vec()?;
let data_hash = record.hash::<Poseidon>().map_err(ProgramError::from)?;
let compressed_account_data = CompressedAccountData {
discriminator: NameRecord::discriminator(),
data,
data_hash,
};
let address = derive_address(
&ctx.remaining_accounts[address_merkle_context.address_merkle_tree_pubkey_index as usize]
.key(),
&address_seed,
)
.map_err(|_| ProgramError::InvalidArgument)?;
let compressed_account = CompressedAccount {
owner: crate::ID,
lamports: 0,
address: Some(address),
data: Some(compressed_account_data),
};
let compressed_account = OutputCompressedAccountWithPackedContext {
compressed_account,
merkle_tree_index: merkle_output_context.merkle_tree_pubkey_index,
};

let new_address_params = NewAddressParamsPacked {
seed: address_seed,
address_merkle_tree_account_index: address_merkle_context.address_merkle_tree_pubkey_index,
address_queue_account_index: address_merkle_context.address_queue_pubkey_index,
address_merkle_tree_root_index,
};

Ok((compressed_account, new_address_params))
}

fn compressed_input_account_with_address(
record: NameRecord,
address: [u8; 32],
merkle_context: &PackedMerkleContext,
merkle_tree_root_index: u16,
) -> Result<PackedCompressedAccountWithMerkleContext> {
let data = record.try_to_vec()?;
let data_hash = record.hash::<Poseidon>().map_err(ProgramError::from)?;
let compressed_account_data = CompressedAccountData {
discriminator: NameRecord::discriminator(),
data,
data_hash,
};
let compressed_account = CompressedAccount {
owner: crate::ID,
lamports: 0,
address: Some(address),
data: Some(compressed_account_data),
};

Ok(PackedCompressedAccountWithMerkleContext {
compressed_account,
merkle_context: *merkle_context,
root_index: merkle_tree_root_index,
read_only: false,
})
}

fn compressed_output_account_with_address(
record: &NameRecord,
address: [u8; 32],
merkle_context: &PackedMerkleContext,
) -> Result<OutputCompressedAccountWithPackedContext> {
let data = record.try_to_vec()?;
let data_hash = record.hash::<Poseidon>().map_err(ProgramError::from)?;
let compressed_account_data = CompressedAccountData {
discriminator: NameRecord::discriminator(),
data,
data_hash,
};
let compressed_account = CompressedAccount {
owner: crate::ID,
lamports: 0,
address: Some(address),
data: Some(compressed_account_data),
};

Ok(OutputCompressedAccountWithPackedContext {
compressed_account,
merkle_tree_index: merkle_context.merkle_tree_pubkey_index,
})
}
26 changes: 20 additions & 6 deletions examples/name-service/programs/name-service/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ async fn test_name_service() {
update_record(
&mut rpc,
&mut test_indexer,
&env,
&rdata_1,
&rdata_2,
&payer,
compressed_account,
&address,
&account_compression_authority,
&registered_program_pda,
)
Expand All @@ -107,10 +107,10 @@ async fn test_name_service() {
delete_record(
&mut rpc,
&mut test_indexer,
&env,
&rdata_2,
&payer,
compressed_account,
&address,
&account_compression_authority,
&registered_program_pda,
)
Expand Down Expand Up @@ -189,11 +189,11 @@ async fn create_record<R: RpcConnection>(
async fn update_record<R: RpcConnection>(
rpc: &mut R,
test_indexer: &mut TestIndexer<R>,
env: &EnvAccounts,
old_rdata: &RData,
new_rdata: &RData,
payer: &Keypair,
compressed_account: &CompressedAccountWithMerkleContext,
address: &[u8; 32],
account_compression_authority: &Pubkey,
registered_program_pda: &Pubkey,
) {
Expand All @@ -215,11 +215,18 @@ async fn update_record<R: RpcConnection>(
let merkle_context =
pack_merkle_context(compressed_account.merkle_context, &mut remaining_accounts);

let address_merkle_context = AddressMerkleContext {
address_merkle_tree_pubkey: env.address_merkle_tree_pubkey,
address_queue_pubkey: env.address_merkle_tree_queue_pubkey,
};
let address_merkle_context =
pack_address_merkle_context(address_merkle_context, &mut remaining_accounts);

let instruction_data = name_service::instruction::UpdateRecord {
proof: rpc_result.proof,
merkle_context,
merkle_tree_root_index: rpc_result.root_indices[0],
address: *address,
address_merkle_context,
name: "example.io".to_string(),
old_rdata: old_rdata.clone(),
new_rdata: new_rdata.clone(),
Expand Down Expand Up @@ -253,10 +260,10 @@ async fn update_record<R: RpcConnection>(
async fn delete_record<R: RpcConnection>(
rpc: &mut R,
test_indexer: &mut TestIndexer<R>,
env: &EnvAccounts,
rdata: &RData,
payer: &Keypair,
compressed_account: &CompressedAccountWithMerkleContext,
address: &[u8; 32],
account_compression_authority: &Pubkey,
registered_program_pda: &Pubkey,
) {
Expand All @@ -278,11 +285,18 @@ async fn delete_record<R: RpcConnection>(
let merkle_context =
pack_merkle_context(compressed_account.merkle_context, &mut remaining_accounts);

let address_merkle_context = AddressMerkleContext {
address_merkle_tree_pubkey: env.address_merkle_tree_pubkey,
address_queue_pubkey: env.address_merkle_tree_queue_pubkey,
};
let address_merkle_context =
pack_address_merkle_context(address_merkle_context, &mut remaining_accounts);

let instruction_data = name_service::instruction::DeleteRecord {
proof: rpc_result.proof,
merkle_context,
merkle_tree_root_index: rpc_result.root_indices[0],
address: *address,
address_merkle_context,
name: "example.io".to_string(),
rdata: rdata.clone(),
cpi_context: None,
Expand Down
Loading

0 comments on commit 14f1554

Please sign in to comment.