gpl_compression/instructions/
comment.rs

1use std::convert::AsRef;
2
3use gpl_core::state::{Post, Profile, User};
4
5use anchor_lang::prelude::*;
6use anchor_lang::solana_program::keccak::hashv;
7
8use gpl_core::constants::*;
9
10use gpl_core::errors::PostError;
11use gpl_core::program::GplCore;
12use gpl_core::state::MAX_LEN_URI;
13
14use spl_account_compression::program::SplAccountCompression;
15use spl_account_compression::wrap_application_data_v1;
16use spl_account_compression::Noop;
17
18use crate::events::CompressedCommentNew;
19use crate::state::TreeConfig;
20use crate::utils::LeafSchema;
21use crate::utils::{append_leaf, try_find_asset_id, verify_leaf};
22
23// Create Comment
24#[derive(Accounts)]
25#[instruction(reply_to: Pubkey, metadata_uri: String, random_hash: [u8; 32], post_root: [u8; 32], post_leaf: [u8; 32], post_index: u32)]
26pub struct CreateCompressedComment<'info> {
27    #[account(
28        seeds = [
29            PROFILE_PREFIX_SEED.as_bytes(),
30            from_profile.namespace.as_ref().as_bytes(),
31            user.to_account_info().key.as_ref(),
32        ],
33        seeds::program = gpl_core_program.key(),
34        bump,
35        has_one = user,
36    )]
37    pub from_profile: Account<'info, Profile>,
38
39    #[account(
40        seeds = [
41            USER_PREFIX_SEED.as_bytes(),
42            user.random_hash.as_ref(),
43        ],
44        seeds::program = gpl_core_program.key(),
45        bump,
46        has_one = authority,
47    )]
48    pub user: Account<'info, User>,
49
50    #[account(seeds = [merkle_tree.key.as_ref()], bump)]
51    pub tree_config: Account<'info, TreeConfig>,
52
53    #[account(mut)]
54    /// CHECK The account must have the same authority as that of the config
55    pub merkle_tree: UncheckedAccount<'info>,
56
57    // TODO: The seeds should be more descriptive
58    #[account(seeds = [target_merkle_tree.key.as_ref()], bump)]
59    pub target_tree_config: Account<'info, TreeConfig>,
60
61    /// CHECK The account must have the same authority as that of the config
62    pub target_merkle_tree: UncheckedAccount<'info>,
63
64    #[account(mut)]
65    pub authority: Signer<'info>,
66
67    pub compression_program: Program<'info, SplAccountCompression>,
68    pub log_wrapper_program: Program<'info, Noop>,
69    pub gpl_core_program: Program<'info, GplCore>,
70    pub system_program: Program<'info, System>,
71}
72
73// Handler to create a compressed comment
74pub fn create_compressed_comment_handler<'info>(
75    ctx: Context<'_, '_, '_, 'info, CreateCompressedComment<'info>>,
76    reply_to: Pubkey,
77    metadata_uri: String,
78    random_hash: [u8; 32],
79    post_root: [u8; 32],
80    post_leaf: [u8; 32],
81    post_index: u32,
82) -> Result<()> {
83    require!(metadata_uri.len() <= MAX_LEN_URI, PostError::URITooLong);
84
85    // Check if the reply_to exists
86    // FIXME:
87    // They can potentially pass any proof. How do we verify this belongs to the asset unless we
88    // construct the leaf ourselves?
89    verify_leaf(
90        ctx.accounts.target_merkle_tree.key,
91        ctx.bumps["target_tree_config"],
92        post_root,
93        post_leaf,
94        post_index,
95        ctx.remaining_accounts,
96        &ctx.accounts.target_merkle_tree,
97        &ctx.accounts.compression_program,
98    )?;
99
100    let post_seeds = [POST_PREFIX_SEED.as_bytes(), random_hash.as_ref()];
101
102    let (post_id, post_bump) =
103        Pubkey::try_find_program_address(&post_seeds, &GplCore::id()).unwrap();
104
105    let seed_hash = hashv(&post_seeds).to_bytes();
106
107    let asset_id = try_find_asset_id(ctx.accounts.merkle_tree.key, seed_hash)?;
108
109    let post = Post {
110        metadata_uri,
111        random_hash,
112        profile: *ctx.accounts.from_profile.to_account_info().key,
113        reply_to: Some(reply_to),
114    };
115
116    let leaf = LeafSchema {
117        asset_id,
118        seed_hash,
119        data_hash: hashv(&[&post.try_to_vec()?]).to_bytes(),
120    };
121
122    let leaf_node = leaf.to_node()?;
123
124    wrap_application_data_v1(leaf_node.to_vec(), &ctx.accounts.log_wrapper_program)?;
125
126    append_leaf(
127        ctx.accounts.merkle_tree.key,
128        ctx.bumps["tree_config"],
129        &ctx.accounts.authority.to_account_info(),
130        leaf_node,
131        &ctx.accounts.merkle_tree,
132        &ctx.accounts.compression_program,
133        &ctx.accounts.log_wrapper_program,
134    )?;
135
136    emit!(CompressedCommentNew {
137        asset_id,
138        post_id,
139        post_bump,
140        reply_to,
141        profile: *ctx.accounts.from_profile.to_account_info().key,
142        user: *ctx.accounts.user.to_account_info().key,
143        random_hash: random_hash,
144        metadata_uri: post.metadata_uri.clone(),
145        timestamp: Clock::get()?.unix_timestamp,
146        index: 0 // TODO: Get the index from the tree
147    });
148    Ok(())
149}