Skip to content

Commit

Permalink
Backport Rust client improvements from jun-2024-feature-staging (#158)
Browse files Browse the repository at this point in the history
* Add anchor trait implementations for BaseAssetV1 and BaseCollectionV1

* Box Asset deserialization and use helper

* Box Collection deserialization, use helper, add ext plugins
  • Loading branch information
danenbm authored Jul 3, 2024
1 parent 7de477b commit 749b256
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 52 deletions.
1 change: 1 addition & 0 deletions clients/rust/src/hooked/advanced_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ pub struct Asset {
pub struct Collection {
pub base: BaseCollectionV1,
pub plugin_list: PluginsList,
pub external_plugin_adapter_list: ExternalPluginAdaptersList,
pub plugin_header: Option<PluginHeaderV1>,
}

Expand Down
72 changes: 39 additions & 33 deletions clients/rust/src/hooked/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,63 @@ use borsh::BorshSerialize;
use crate::{
accounts::{BaseAssetV1, PluginHeaderV1},
registry_records_to_external_plugin_adapter_list, registry_records_to_plugin_list, Asset,
PluginRegistryV1Safe,
ExternalPluginAdaptersList, PluginRegistryV1Safe, PluginsList,
};

impl Asset {
pub fn deserialize(data: &[u8]) -> Result<Self, std::io::Error> {
pub fn deserialize(data: &[u8]) -> Result<Box<Self>, std::io::Error> {
let base = BaseAssetV1::from_bytes(data)?;
let base_data = base.try_to_vec()?;
let (plugin_header, plugin_list, external_plugin_adapter_list) = if base_data.len()
!= data.len()
{
let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?;
let plugin_registry = PluginRegistryV1Safe::from_bytes(
&data[plugin_header.plugin_registry_offset as usize..],
)?;

let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?;
let external_plugin_adapter_list = registry_records_to_external_plugin_adapter_list(
&plugin_registry.external_registry,
data,
)?;

(
Some(plugin_header),
Some(plugin_list),
Some(external_plugin_adapter_list),
)
} else {
(None, None, None)
};

Ok(Self {

if base_data.len() != data.len() {
return Self::deserialize_with_plugins(data, base, base_data);
}

Ok(Box::new(Self {
base,
plugin_list: PluginsList::default(),
external_plugin_adapter_list: ExternalPluginAdaptersList::default(),
plugin_header: None,
}))
}

fn deserialize_with_plugins(
data: &[u8],
base: BaseAssetV1,
base_data: Vec<u8>,
) -> Result<Box<Self>, std::io::Error> {
let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?;
let plugin_registry = PluginRegistryV1Safe::from_bytes(
&data[plugin_header.plugin_registry_offset as usize..],
)?;

let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?;
let external_plugin_adapter_list = registry_records_to_external_plugin_adapter_list(
&plugin_registry.external_registry,
data,
)?;

Ok(Box::new(Self {
base,
plugin_list: plugin_list.unwrap_or_default(),
external_plugin_adapter_list: external_plugin_adapter_list.unwrap_or_default(),
plugin_header,
})
plugin_list,
external_plugin_adapter_list,
plugin_header: Some(plugin_header),
}))
}

#[inline(always)]
pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
pub fn from_bytes(data: &[u8]) -> Result<Box<Self>, std::io::Error> {
Self::deserialize(data)
}
}

impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Asset {
impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Box<Asset> {
type Error = std::io::Error;

fn try_from(
account_info: &solana_program::account_info::AccountInfo<'a>,
) -> Result<Self, Self::Error> {
let data: &[u8] = &(*account_info.data).borrow();
Self::deserialize(data)
Asset::deserialize(data)
}
}
56 changes: 37 additions & 19 deletions clients/rust/src/hooked/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,64 @@ use borsh::BorshSerialize;

use crate::{
accounts::{BaseCollectionV1, PluginHeaderV1},
registry_records_to_plugin_list, Collection, PluginRegistryV1Safe,
registry_records_to_external_plugin_adapter_list, registry_records_to_plugin_list, Collection,
ExternalPluginAdaptersList, PluginRegistryV1Safe, PluginsList,
};

impl Collection {
pub fn deserialize(data: &[u8]) -> Result<Self, std::io::Error> {
pub fn deserialize(data: &[u8]) -> Result<Box<Self>, std::io::Error> {
let base = BaseCollectionV1::from_bytes(data)?;
let base_data = base.try_to_vec()?;
let (plugin_header, plugin_list) = if base_data.len() != data.len() {
let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?;
let plugin_registry = PluginRegistryV1Safe::from_bytes(
&data[plugin_header.plugin_registry_offset as usize..],
)?;

let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?;
if base_data.len() != data.len() {
return Self::deserialize_with_plugins(data, base, base_data);
}

(Some(plugin_header), Some(plugin_list))
} else {
(None, None)
};
Ok(Box::new(Self {
base,
plugin_list: PluginsList::default(),
external_plugin_adapter_list: ExternalPluginAdaptersList::default(),
plugin_header: None,
}))
}

fn deserialize_with_plugins(
data: &[u8],
base: BaseCollectionV1,
base_data: Vec<u8>,
) -> Result<Box<Self>, std::io::Error> {
let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?;
let plugin_registry = PluginRegistryV1Safe::from_bytes(
&data[plugin_header.plugin_registry_offset as usize..],
)?;

Ok(Self {
let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?;
let external_plugin_adapter_list = registry_records_to_external_plugin_adapter_list(
&plugin_registry.external_registry,
data,
)?;

Ok(Box::new(Self {
base,
plugin_list: plugin_list.unwrap_or_default(),
plugin_header,
})
plugin_list,
external_plugin_adapter_list,
plugin_header: Some(plugin_header),
}))
}

#[inline(always)]
pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
pub fn from_bytes(data: &[u8]) -> Result<Box<Self>, std::io::Error> {
Self::deserialize(data)
}
}

impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Collection {
impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Box<Collection> {
type Error = std::io::Error;

fn try_from(
account_info: &solana_program::account_info::AccountInfo<'a>,
) -> Result<Self, Self::Error> {
let data: &[u8] = &(*account_info.data).borrow();
Self::deserialize(data)
Collection::deserialize(data)
}
}
55 changes: 55 additions & 0 deletions clients/rust/src/hooked/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,61 @@ impl BaseCollectionV1 {
pub const BASE_LENGTH: usize = 1 + 32 + 4 + 4 + 4 + 4;
}

/// Anchor implementations that enable using `Account<BaseAssetV1>` and `Account<BaseCollectionV1>`
/// in Anchor programs.
#[cfg(feature = "anchor")]
mod anchor_impl {
use super::*;
use anchor_lang::{
prelude::{Owner, Pubkey},
AccountDeserialize, AccountSerialize, Discriminator,
};

impl AccountDeserialize for BaseAssetV1 {
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
let base_asset = Self::from_bytes(buf)?;
Ok(base_asset)
}
}

// Not used as an Anchor program using Account<BaseAssetV1> would not have permission to
// reserialize the account as it's owned by mpl-core.
impl AccountSerialize for BaseAssetV1 {}

// Not used but needed for Anchor.
impl Discriminator for BaseAssetV1 {
const DISCRIMINATOR: [u8; 8] = [0; 8];
}

impl Owner for BaseAssetV1 {
fn owner() -> Pubkey {
crate::ID
}
}

impl AccountDeserialize for BaseCollectionV1 {
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
let base_asset = Self::from_bytes(buf)?;
Ok(base_asset)
}
}

// Not used as an Anchor program using Account<BaseCollectionV1> would not have permission to
// reserialize the account as it's owned by mpl-core.
impl AccountSerialize for BaseCollectionV1 {}

// Not used but needed for Anchor.
impl Discriminator for BaseCollectionV1 {
const DISCRIMINATOR: [u8; 8] = [0; 8];
}

impl Owner for BaseCollectionV1 {
fn owner() -> Pubkey {
crate::ID
}
}
}

impl DataBlob for BaseAssetV1 {
fn get_initial_size() -> usize {
BaseAssetV1::BASE_LENGTH
Expand Down

0 comments on commit 749b256

Please sign in to comment.