Skip to content

Commit

Permalink
Initial compress implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
danenbm committed Feb 22, 2024
1 parent 006ba01 commit 744559b
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 33 deletions.
8 changes: 8 additions & 0 deletions programs/mpl-asset/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ pub enum MplAssetError {
/// 16 - Numerical overflow
#[error("Numerical overflow")]
NumericalOverflowError,

/// 17 - Already compressed account
#[error("Already compressed account")]
AlreadyCompressed,

/// 18 - Already decompressed
#[error("Already decompressed account")]
AlreadyDecompressed,
}

impl PrintProgramError for MplAssetError {
Expand Down
4 changes: 3 additions & 1 deletion programs/mpl-asset/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use borsh::{BorshDeserialize, BorshSerialize};

use crate::{
error::MplAssetError,
state::{Authority, DataBlob},
state::{Authority, Compressible, DataBlob},
};

#[repr(u16)]
Expand All @@ -53,6 +53,8 @@ impl Plugin {
}
}

impl Compressible for Plugin {}

#[repr(u16)]
#[derive(Clone, Copy, Debug, BorshSerialize, BorshDeserialize, Eq, PartialEq)]
pub enum PluginType {
Expand Down
18 changes: 13 additions & 5 deletions programs/mpl-asset/src/plugins/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ pub fn fetch_plugin(
account: &AccountInfo,
plugin_type: PluginType,
) -> Result<(Vec<Authority>, Plugin, usize), ProgramError> {
let mut bytes: &[u8] = &(*account.data).borrow();
let asset = Asset::deserialize(&mut bytes)?;
let asset = Asset::load(account, 0)?;

let header = PluginHeader::load(account, asset.get_size())?;
let PluginRegistry { registry, .. } =
Expand Down Expand Up @@ -102,10 +101,19 @@ pub fn fetch_plugin(
Ok((plugin_data.authorities.clone(), plugin, plugin_data.offset))
}

pub fn fetch_plugins(account: &AccountInfo) -> Result<Vec<RegistryRecord>, ProgramError> {
let asset = Asset::load(account, 0)?;

let header = PluginHeader::load(account, asset.get_size())?;
let PluginRegistry { registry, .. } =
PluginRegistry::load(account, header.plugin_registry_offset)?;

Ok(registry)
}

/// Create plugin header and registry if it doesn't exist
pub fn list_plugins(account: &AccountInfo) -> Result<Vec<PluginType>, ProgramError> {
let mut bytes: &[u8] = &(*account.data).borrow();
let asset = Asset::deserialize(&mut bytes)?;
let asset = Asset::load(account, 0)?;

let header = PluginHeader::load(account, asset.get_size())?;
let PluginRegistry { registry, .. } =
Expand Down Expand Up @@ -283,7 +291,7 @@ pub fn delete_plugin<'a>(
data: _,
}| type_iter == plugin_type,
) {
let registry_record = plugin_registry.registry.swap_remove(index);
let registry_record = plugin_registry.registry.remove(index);
let serialized_registry_record = registry_record.try_to_vec()?;

// Only allow the default authority to delete the plugin.
Expand Down
83 changes: 78 additions & 5 deletions programs/mpl-asset/src/processor/compress.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,93 @@
use borsh::{BorshDeserialize, BorshSerialize};
use mpl_utils::assert_signer;
use mpl_utils::resize_or_reallocate_account_raw;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program::invoke,
program_memory::sol_memcpy, rent::Rent, system_instruction, system_program, sysvar::Sysvar,
account_info::AccountInfo, entrypoint::ProgramResult, program_memory::sol_memcpy,
};

use crate::{
error::MplAssetError,
instruction::accounts::CreateAccounts,
state::{Asset, Compressible, DataState, HashedAsset, Key},
instruction::accounts::CompressAccounts,
plugins::{fetch_plugins, Plugin},
state::{
Asset, AuthorityVec, Compressible, DataBlob, HashedAsset, HashedAssetSchema, Key,
PluginHash, SolanaAccount,
},
utils::load_key,
};

#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
pub struct CompressArgs {}

pub(crate) fn compress<'a>(accounts: &'a [AccountInfo<'a>], args: CompressArgs) -> ProgramResult {
pub(crate) fn compress<'a>(accounts: &'a [AccountInfo<'a>], _args: CompressArgs) -> ProgramResult {
// Accounts.
let ctx = CompressAccounts::context(accounts)?;

// Guards.
assert_signer(ctx.accounts.owner)?;
let payer = if let Some(payer) = ctx.accounts.payer {
assert_signer(payer)?;
payer
} else {
ctx.accounts.owner
};

match load_key(ctx.accounts.asset_address, 0)? {
Key::Asset => {
let asset = Asset::load(ctx.accounts.asset_address, 0)?;

if ctx.accounts.owner.key != &asset.owner {
return Err(MplAssetError::InvalidAuthority.into());
}

// TODO: Delegated compress/decompress authority.

if asset.get_size() != ctx.accounts.asset_address.data_len() {
let registry_records = fetch_plugins(ctx.accounts.asset_address)?;

let mut plugin_hashes = Vec::with_capacity(registry_records.len());
for record in registry_records {
let authorities: AuthorityVec = record.data.authorities;
let plugin_authorities_hash = authorities.hash()?;

let plugin = Plugin::deserialize(
&mut &(*ctx.accounts.asset_address.data).borrow()[record.data.offset..],
)?;
let plugin_hash = plugin.hash()?;

plugin_hashes.push(PluginHash {
plugin_authorities_hash,
plugin_hash,
});
}

let asset_hash = asset.hash()?;
let hashed_asset_schema = HashedAssetSchema {
asset_hash,
plugin_hashes,
};

let hashed_asset = HashedAsset::new(hashed_asset_schema.hash()?);
let serialized_data = hashed_asset.try_to_vec()?;

resize_or_reallocate_account_raw(
ctx.accounts.asset_address,
payer,
ctx.accounts.system_program,
serialized_data.len(),
)?;

sol_memcpy(
&mut ctx.accounts.asset_address.try_borrow_mut_data()?,
&serialized_data,
serialized_data.len(),
);
}
}
Key::HashedAsset => return Err(MplAssetError::AlreadyCompressed.into()),
_ => return Err(MplAssetError::IncorrectAccount.into()),
}

Ok(())
}
17 changes: 2 additions & 15 deletions programs/mpl-asset/src/state/asset.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use borsh::{BorshDeserialize, BorshSerialize};
use shank::ShankAccount;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, keccak, program::invoke,
program_error::ProgramError, pubkey::Pubkey,
};
use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};

use crate::{
error::MplAssetError,
Expand Down Expand Up @@ -99,17 +96,7 @@ impl Asset {
}
}

impl Compressible for Asset {
fn hash(&self) -> Result<[u8; 32], ProgramError> {
let serialized_data = self.try_to_vec()?;
Ok(keccak::hash(serialized_data.as_slice()).to_bytes())
}

fn wrap(&self) -> ProgramResult {
let serialized_data = self.try_to_vec()?;
invoke(&spl_noop::instruction(serialized_data), &[])
}
}
impl Compressible for Asset {}

impl DataBlob for Asset {
fn get_initial_size() -> usize {
Expand Down
18 changes: 18 additions & 0 deletions programs/mpl-asset/src/state/hashed_asset_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use borsh::{BorshDeserialize, BorshSerialize};
use shank::ShankAccount;

use crate::state::Compressible;

#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount, PartialEq, Eq)]
pub struct PluginHash {
pub plugin_authorities_hash: [u8; 32],
pub plugin_hash: [u8; 32],
}

#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount, PartialEq, Eq)]
pub struct HashedAssetSchema {
pub asset_hash: [u8; 32],
pub plugin_hashes: Vec<PluginHash>,
}

impl Compressible for HashedAssetSchema {}
6 changes: 6 additions & 0 deletions programs/mpl-asset/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pub use asset::*;
mod hashed_asset;
pub use hashed_asset::*;

mod hashed_asset_schema;
pub use hashed_asset_schema::*;

mod traits;
pub use traits::*;

Expand All @@ -29,6 +32,9 @@ pub enum Authority {
Permanent { address: Pubkey },
}

pub type AuthorityVec = Vec<Authority>;
impl Compressible for AuthorityVec {}

#[repr(C)]
#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)]
pub enum ExtraAccounts {
Expand Down
21 changes: 14 additions & 7 deletions programs/mpl-asset/src/state/traits.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{error::MplAssetError, state::Key, utils::load_key};
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
use solana_program::msg;
use solana_program::program_error::ProgramError;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, keccak, msg, program::invoke,
program_error::ProgramError,
};

pub trait DataBlob: BorshSerialize + BorshDeserialize {
fn get_initial_size() -> usize;
Expand Down Expand Up @@ -35,7 +35,14 @@ pub trait SolanaAccount: BorshSerialize + BorshDeserialize {
}
}

pub trait Compressible {
fn hash(&self) -> Result<[u8; 32], ProgramError>;
fn wrap(&self) -> ProgramResult;
pub trait Compressible: BorshSerialize + BorshDeserialize {
fn hash(&self) -> Result<[u8; 32], ProgramError> {
let serialized_data = self.try_to_vec()?;
Ok(keccak::hash(serialized_data.as_slice()).to_bytes())
}

fn wrap(&self) -> ProgramResult {
let serialized_data = self.try_to_vec()?;
invoke(&spl_noop::instruction(serialized_data), &[])
}
}

0 comments on commit 744559b

Please sign in to comment.