diff --git a/programs/mpl-asset/src/error.rs b/programs/mpl-asset/src/error.rs index 05679b12..b0e48b35 100644 --- a/programs/mpl-asset/src/error.rs +++ b/programs/mpl-asset/src/error.rs @@ -11,12 +11,22 @@ pub enum MplAssetError { /// 0 - Invalid System Program #[error("Invalid System Program")] InvalidSystemProgram, + /// 1 - Error deserializing account #[error("Error deserializing account")] DeserializationError, + /// 2 - Error serializing account #[error("Error serializing account")] SerializationError, + + /// 3 - Plugins not initialized + #[error("Plugins not initialized")] + PluginsNotInitialized, + + /// 4 - Plugin not found + #[error("Plugin not found")] + PluginNotFound, } impl PrintProgramError for MplAssetError { diff --git a/programs/mpl-asset/src/lib.rs b/programs/mpl-asset/src/lib.rs index 42695316..c3b46a5c 100644 --- a/programs/mpl-asset/src/lib.rs +++ b/programs/mpl-asset/src/lib.rs @@ -4,6 +4,7 @@ pub mod instruction; pub mod plugins; pub mod processor; pub mod state; +pub mod utils; pub use solana_program; diff --git a/programs/mpl-asset/src/plugins/mod.rs b/programs/mpl-asset/src/plugins/mod.rs index c7b7a58d..51ba983b 100644 --- a/programs/mpl-asset/src/plugins/mod.rs +++ b/programs/mpl-asset/src/plugins/mod.rs @@ -4,12 +4,17 @@ mod utils; pub use collection::*; pub use royalties::*; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, +}; pub use utils::*; use borsh::{BorshDeserialize, BorshSerialize}; -use std::collections::HashMap; -use crate::state::Authority; +use crate::{ + error::MplAssetError, + state::{Authority, Key}, +}; // macro_rules! plugin_instruction { // ($a:expr, $b:expr) => { @@ -35,20 +40,35 @@ pub enum Plugin { // Burn = plugin_instruction!(Plugin::Metadata, 2), //} +#[repr(C)] +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] pub struct RegistryData { pub offset: usize, pub authorities: Vec, } +#[repr(C)] +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] pub struct PluginRegistry { - pub registry: HashMap, + pub registry: Vec<(Key, RegistryData)>, + // pub third_party_registry: HashMap, } -pub trait DataStorage { - fn get_required_length(&self); - fn save(&self, data: &mut [u8]); - fn load(&self, data: &[u8]); - fn load_mut(&self, data: &mut [u8]); +impl PluginRegistry { + pub fn load(account: &AccountInfo, offset: usize) -> Result { + let mut bytes: &[u8] = &(*account.data).borrow()[offset..]; + PluginRegistry::deserialize(&mut bytes).map_err(|error| { + msg!("Error: {}", error); + MplAssetError::DeserializationError.into() + }) + } + + pub fn save(&self, account: &AccountInfo, offset: usize) -> ProgramResult { + borsh::to_writer(&mut account.data.borrow_mut()[offset..], self).map_err(|error| { + msg!("Error: {}", error); + MplAssetError::SerializationError.into() + }) + } } // pub trait PluginTrait diff --git a/programs/mpl-asset/src/plugins/utils.rs b/programs/mpl-asset/src/plugins/utils.rs index 0ad36d63..23bc8217 100644 --- a/programs/mpl-asset/src/plugins/utils.rs +++ b/programs/mpl-asset/src/plugins/utils.rs @@ -1,16 +1,77 @@ -use super::Plugin; +use borsh::BorshDeserialize; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, +}; -//TODO: Implement this function -pub fn create_idempotent() { - // Create plugin header and registry if it doesn't exist +use crate::{ + error::MplAssetError, + state::{Asset, Key, PluginHeader}, + utils::DataBlob, +}; + +use super::{Plugin, PluginRegistry}; + +//TODO:keith: Implement this function +/// Create plugin header and registry if it doesn't exist +pub fn create_idempotent(account: &AccountInfo) -> ProgramResult { + let mut bytes: &[u8] = &(*account.data).borrow(); + let asset = Asset::deserialize(&mut bytes)?; + + // Check if the plugin header and registry exist. + if asset.get_size() == account.data_len() { + // They don't exist, so create them. + let header = PluginHeader { + version: 1, + plugin_map_offset: asset.get_size() + PluginHeader::get_initial_size(), + }; + let registry = PluginRegistry { registry: vec![] }; + + header.save(account, asset.get_size())?; + registry.save(account, header.plugin_map_offset)?; + } + + Ok(()) } -//TODO: Implement this function -pub fn fetch_plugin(plugin: u8) -> Plugin { - // Create plugin header and registry if it doesn't exist +pub fn assert_plugins_initialized(account: &AccountInfo) -> ProgramResult { + let mut bytes: &[u8] = &(*account.data).borrow(); + let asset = Asset::deserialize(&mut bytes).unwrap(); + + if asset.get_size() == account.data_len() { + return Err(MplAssetError::PluginsNotInitialized.into()); + } + + Ok(()) +} + +//TODO:keith: Implement this function +pub fn fetch_plugin( + account: &AccountInfo, + plugin: Key, +) -> Result<((Vec, Plugin)), ProgramError> { + // Fetch the plugin from the registry. + let mut bytes: &[u8] = &(*account.data).borrow(); + let asset = Asset::deserialize(&mut bytes)?; + + let header = PluginHeader::load(account, asset.get_size())?; + let PluginRegistry { registry } = PluginRegistry::load(account, header.plugin_map_offset)?; + + let plugin_data = registry + .iter() + .find(|(key, _)| *key == plugin) + .map(|(_, data)| data) + .ok_or(MplAssetError::PluginNotFound)?; + + let authorities = plugin_data + .authorities + .iter() + .map(|authority| authority.key) + .collect(); } -//TODO: Implement this function +//TODO:keith: Implement this function pub fn list_plugins() -> Vec { // Create plugin header and registry if it doesn't exist + vec![] } diff --git a/programs/mpl-asset/src/processor/create.rs b/programs/mpl-asset/src/processor/create.rs index f5c3d5b6..d0c641b7 100644 --- a/programs/mpl-asset/src/processor/create.rs +++ b/programs/mpl-asset/src/processor/create.rs @@ -13,7 +13,7 @@ use crate::{ #[repr(C)] #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] -pub(crate) struct CreateArgs { +pub struct CreateArgs { pub data_state: DataState, pub name: String, pub uri: String, diff --git a/programs/mpl-asset/src/state/asset.rs b/programs/mpl-asset/src/state/asset.rs index b2ea03fa..ef9213ac 100644 --- a/programs/mpl-asset/src/state/asset.rs +++ b/programs/mpl-asset/src/state/asset.rs @@ -2,6 +2,8 @@ use borsh::{BorshDeserialize, BorshSerialize}; use shank::ShankAccount; use solana_program::{keccak, program_error::ProgramError, pubkey::Pubkey}; +use crate::utils::DataBlob; + use super::{Compressible, Key}; #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount)] @@ -13,6 +15,10 @@ pub struct Asset { pub uri: String, //4 } +impl Asset { + pub const BASE_LENGTH: usize = 1 + 32 + 32 + 4 + 4; +} + impl Compressible for Asset { fn hash(&self) -> Result<[u8; 32], ProgramError> { let serialized_data = self.try_to_vec()?; @@ -21,6 +27,16 @@ impl Compressible for Asset { } } +impl DataBlob for Asset { + fn get_initial_size() -> usize { + Asset::BASE_LENGTH + } + + fn get_size(&self) -> usize { + Asset::BASE_LENGTH + self.name.len() + self.uri.len() + } +} + #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount)] pub struct HashedAsset { pub key: Key, //1 diff --git a/programs/mpl-asset/src/state/plugin_header.rs b/programs/mpl-asset/src/state/plugin_header.rs index 559ab856..22596c1d 100644 --- a/programs/mpl-asset/src/state/plugin_header.rs +++ b/programs/mpl-asset/src/state/plugin_header.rs @@ -5,27 +5,38 @@ use solana_program::msg; use solana_program::program_error::ProgramError; use crate::error::MplAssetError; +use crate::utils::DataBlob; #[repr(C)] #[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] pub struct PluginHeader { - pub version: u8, - pub plugin_map_offset: usize, + pub version: u8, // 1 + pub plugin_map_offset: usize, // 8 } impl PluginHeader { - pub fn load(account: &AccountInfo) -> Result { - let mut bytes: &[u8] = &(*account.data).borrow(); + pub fn load(account: &AccountInfo, offset: usize) -> Result { + let mut bytes: &[u8] = &(*account.data).borrow()[offset..]; PluginHeader::deserialize(&mut bytes).map_err(|error| { msg!("Error: {}", error); MplAssetError::DeserializationError.into() }) } - pub fn save(&self, account: &AccountInfo) -> ProgramResult { - borsh::to_writer(&mut account.data.borrow_mut()[..], self).map_err(|error| { + pub fn save(&self, account: &AccountInfo, offset: usize) -> ProgramResult { + borsh::to_writer(&mut account.data.borrow_mut()[offset..], self).map_err(|error| { msg!("Error: {}", error); MplAssetError::SerializationError.into() }) } } + +impl DataBlob for PluginHeader { + fn get_initial_size() -> usize { + 1 + 8 + } + + fn get_size(&self) -> usize { + 1 + 8 + } +}