gpl_compression/instructions/
connection.rs

1use crate::events::{CompressedConnectionDeleted, CompressedConnectionNew};
2use crate::state::TreeConfig;
3use crate::utils::{append_leaf, replace_leaf, try_find_asset_id, LeafSchema};
4use anchor_lang::Discriminator;
5use gpl_core::errors::ConnectionError;
6use spl_account_compression::wrap_application_data_v1;
7use spl_account_compression::Node;
8
9use gpl_core::state::{Connection, Profile, User};
10
11use anchor_lang::prelude::*;
12use std::convert::AsRef;
13
14use gpl_core::constants::*;
15
16use anchor_lang::solana_program::keccak::hashv;
17use gpl_core::program::GplCore;
18use spl_account_compression::program::SplAccountCompression;
19use spl_account_compression::Noop;
20
21// Create Connection
22#[derive(Accounts)]
23pub struct CreateCompressedConnection<'info> {
24    #[account(
25        seeds = [
26            PROFILE_PREFIX_SEED.as_bytes(),
27            from_profile.namespace.as_ref().as_bytes(),
28            user.to_account_info().key.as_ref(),
29        ],
30        seeds::program = gpl_core_program.key(),
31        bump,
32        has_one = user,
33    )]
34    pub from_profile: Account<'info, Profile>,
35    #[account(
36        seeds = [
37            PROFILE_PREFIX_SEED.as_bytes(),
38            to_profile.namespace.as_ref().as_bytes(),
39            to_profile.user.as_ref(),
40        ],
41        seeds::program = gpl_core_program.key(),
42        bump,
43    )]
44    pub to_profile: Account<'info, Profile>,
45    #[account(
46        seeds = [
47            USER_PREFIX_SEED.as_bytes(),
48            user.random_hash.as_ref(),
49        ],
50        seeds::program = gpl_core_program.key(),
51        bump,
52        has_one = authority,
53    )]
54    pub user: Account<'info, User>,
55
56    #[account(seeds = [merkle_tree.key.as_ref()], bump)]
57    pub tree_config: Account<'info, TreeConfig>,
58
59    #[account(mut)]
60    /// CHECK The account must have the same authority as that of the config
61    pub merkle_tree: UncheckedAccount<'info>,
62
63    #[account(mut)]
64    pub authority: Signer<'info>,
65
66    pub compression_program: Program<'info, SplAccountCompression>,
67    pub log_wrapper_program: Program<'info, Noop>,
68    pub gpl_core_program: Program<'info, GplCore>,
69    pub system_program: Program<'info, System>,
70}
71
72// Handler to create a new Connection
73pub fn create_compressed_connection_handler(
74    ctx: Context<CreateCompressedConnection>,
75) -> Result<()> {
76    let from_profile = &ctx.accounts.from_profile;
77    let to_profile = &ctx.accounts.to_profile;
78
79    // CHECK that the from_profile and to_profile are not the same
80    require_neq!(
81        from_profile.key(),
82        to_profile.key(),
83        ConnectionError::CannotConnectToSelf
84    );
85
86    let connection_seeds = [
87        CONNECTION_PREFIX_SEED.as_bytes(),
88        from_profile.to_account_info().key.as_ref(),
89        to_profile.to_account_info().key.as_ref(),
90    ];
91
92    let (connection_id, connection_bump) =
93        Pubkey::try_find_program_address(&connection_seeds, &GplCore::id()).unwrap();
94
95    let seed_hash = hashv(&connection_seeds).to_bytes();
96
97    let asset_id = try_find_asset_id(ctx.accounts.merkle_tree.key, seed_hash)?;
98
99    let connection = Connection {
100        from_profile: *from_profile.to_account_info().key,
101        to_profile: *to_profile.to_account_info().key,
102    };
103
104    let leaf = LeafSchema {
105        asset_id,
106        seed_hash,
107        data_hash: hashv(&[&Connection::DISCRIMINATOR, &connection.try_to_vec()?]).to_bytes(),
108    };
109
110    let leaf_node = leaf.to_node()?;
111
112    wrap_application_data_v1(leaf_node.to_vec(), &ctx.accounts.log_wrapper_program)?;
113
114    append_leaf(
115        ctx.accounts.merkle_tree.key,
116        ctx.bumps["tree_config"],
117        &ctx.accounts.authority.to_account_info(),
118        leaf_node,
119        &ctx.accounts.merkle_tree,
120        &ctx.accounts.compression_program,
121        &ctx.accounts.log_wrapper_program,
122    )?;
123
124    // emit a compressed connection event
125    emit!(CompressedConnectionNew {
126        connection_id,
127        connection_bump,
128        from_profile: *from_profile.to_account_info().key,
129        to_profile: *to_profile.to_account_info().key,
130        asset_id,
131        user: *ctx.accounts.user.to_account_info().key,
132        timestamp: Clock::get()?.unix_timestamp,
133        index: 0 // TODO: Get the index from the tree
134    });
135
136    Ok(())
137}
138
139// Delete a Connection
140#[derive(Accounts)]
141// Ideally this should be compacted down to asset_id, root, index
142#[instruction(root: [u8;32], index: u32)]
143pub struct DeleteCompressedConnection<'info> {
144    #[account(
145        seeds = [
146            PROFILE_PREFIX_SEED.as_bytes(),
147            from_profile.namespace.as_ref().as_bytes(),
148            user.to_account_info().key.as_ref(),
149        ],
150        seeds::program = gpl_core_program.key(),
151        bump,
152        has_one = user,
153    )]
154    pub from_profile: Account<'info, Profile>,
155    #[account(
156        seeds = [
157            PROFILE_PREFIX_SEED.as_bytes(),
158            to_profile.namespace.as_ref().as_bytes(),
159            to_profile.user.as_ref(),
160        ],
161        seeds::program = gpl_core_program.key(),
162        bump,
163    )]
164    pub to_profile: Account<'info, Profile>,
165    #[account(
166        seeds = [
167            USER_PREFIX_SEED.as_bytes(),
168            user.random_hash.as_ref(),
169        ],
170        seeds::program = gpl_core_program.key(),
171        bump,
172        has_one = authority,
173    )]
174    pub user: Account<'info, User>,
175
176    #[account(seeds = [merkle_tree.key.as_ref()], bump)]
177    pub tree_config: Account<'info, TreeConfig>,
178
179    #[account(mut)]
180    /// CHECK The account must have the same authority as that of the config
181    pub merkle_tree: UncheckedAccount<'info>,
182
183    #[account(mut)]
184    pub authority: Signer<'info>,
185    pub compression_program: Program<'info, SplAccountCompression>,
186    pub log_wrapper_program: Program<'info, Noop>,
187    pub gpl_core_program: Program<'info, GplCore>,
188    pub system_program: Program<'info, System>,
189}
190
191// Handler to delete a compressed Connection
192pub fn delete_compressed_connection_handler<'info>(
193    ctx: Context<'_, '_, '_, 'info, DeleteCompressedConnection<'info>>,
194    root: [u8; 32],
195    index: u32,
196) -> Result<()> {
197    let from_profile = &ctx.accounts.from_profile;
198    let to_profile = &ctx.accounts.to_profile;
199
200    // CHECK that the from_profile and to_profile are not the same
201    require_neq!(
202        from_profile.key(),
203        to_profile.key(),
204        ConnectionError::CannotConnectToSelf
205    );
206
207    let connection_seeds = [
208        CONNECTION_PREFIX_SEED.as_bytes(),
209        from_profile.to_account_info().key.as_ref(),
210        to_profile.to_account_info().key.as_ref(),
211    ];
212
213    let (connection_id, connection_bump) =
214        Pubkey::try_find_program_address(&connection_seeds, &GplCore::id()).unwrap();
215
216    let seed_hash = hashv(&connection_seeds).to_bytes();
217
218    let asset_id = try_find_asset_id(ctx.accounts.merkle_tree.key, seed_hash)?;
219
220    let old_connection = Connection {
221        from_profile: *from_profile.to_account_info().key,
222        to_profile: *to_profile.to_account_info().key,
223    };
224
225    let old_leaf = LeafSchema {
226        asset_id,
227        seed_hash,
228        data_hash: hashv(&[&Connection::DISCRIMINATOR, &old_connection.try_to_vec()?]).to_bytes(),
229    };
230
231    let old_leaf_node = old_leaf.to_node()?;
232
233    let new_leaf_node = Node::default();
234
235    wrap_application_data_v1(new_leaf_node.to_vec(), &ctx.accounts.log_wrapper_program)?;
236
237    replace_leaf(
238        ctx.accounts.merkle_tree.key,
239        ctx.bumps["tree_config"],
240        &ctx.accounts.authority.to_account_info(),
241        &ctx.accounts.merkle_tree,
242        root,
243        old_leaf_node,
244        new_leaf_node,
245        index,
246        ctx.remaining_accounts,
247        &ctx.accounts.compression_program,
248        &ctx.accounts.log_wrapper_program,
249    )?;
250
251    // emit a compressed connection event
252    emit!(CompressedConnectionDeleted {
253        connection_id,
254        connection_bump,
255        from_profile: *from_profile.to_account_info().key,
256        to_profile: *to_profile.to_account_info().key,
257        asset_id,
258        user: *ctx.accounts.user.to_account_info().key,
259        timestamp: Clock::get()?.unix_timestamp,
260        index
261    });
262
263    Ok(())
264}