Skip to content

Commit

Permalink
Modify Rust client to ignore unknown plugins (#52)
Browse files Browse the repository at this point in the history
* Modify Rust client to ignore unknown plugins

* Add full collection deserialization.

* Remove fetch_plugins function.

* Add fetching plugins tests

* Add create collection tests

* Add back updated fetch_plugins and tests

* Rename deserialize_collection
  • Loading branch information
danenbm authored Apr 3, 2024
1 parent 001301c commit 012d081
Show file tree
Hide file tree
Showing 10 changed files with 916 additions and 178 deletions.
63 changes: 60 additions & 3 deletions clients/rust/src/hooked/advanced_types.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use borsh::BorshDeserialize;
use solana_program::pubkey::Pubkey;
use std::{cmp::Ordering, io::ErrorKind};

use crate::{
accounts::{BaseAssetV1, BaseCollectionV1, PluginHeaderV1},
types::{
Attributes, BurnDelegate, FreezeDelegate, PermanentBurnDelegate, PermanentFreezeDelegate,
PermanentTransferDelegate, PluginAuthority, Royalties, TransferDelegate, UpdateDelegate,
Attributes, BurnDelegate, FreezeDelegate, Key, PermanentBurnDelegate,
PermanentFreezeDelegate, PermanentTransferDelegate, PluginAuthority, Royalties,
TransferDelegate, UpdateDelegate,
},
};

Expand Down Expand Up @@ -140,5 +143,59 @@ pub struct Asset {
pub struct Collection {
pub base: BaseCollectionV1,
pub plugin_list: PluginsList,
pub plugin_header: PluginHeaderV1,
pub plugin_header: Option<PluginHeaderV1>,
}

/// Registry record that can be used when the plugin type is not known (i.e. a `PluginType` that
/// is too new for this client to know about).
pub struct RegistryRecordSafe {
pub plugin_type: u8,
pub authority: PluginAuthority,
pub offset: u64,
}

impl RegistryRecordSafe {
/// Associated function for sorting `RegistryRecordIndexable` by offset.
pub fn compare_offsets(a: &RegistryRecordSafe, b: &RegistryRecordSafe) -> Ordering {
a.offset.cmp(&b.offset)
}
}

/// Plugin registry that an account can safely be deserialized into even if some plugins are
/// not known. Note this skips over external plugins for now, and will be updated when those
/// are defined.
pub struct PluginRegistryV1Safe {
pub _key: Key,
pub registry: Vec<RegistryRecordSafe>,
}

impl PluginRegistryV1Safe {
#[inline(always)]
pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
let mut data: &[u8] = data;
let key = Key::deserialize(&mut data)?;
if key != Key::PluginRegistryV1 {
return Err(ErrorKind::InvalidInput.into());
}

let registry_size = u32::deserialize(&mut data)?;

let mut registry = vec![];
for _ in 0..registry_size {
let plugin_type = u8::deserialize(&mut data)?;
let authority = PluginAuthority::deserialize(&mut data)?;
let offset = u64::deserialize(&mut data)?;

registry.push(RegistryRecordSafe {
plugin_type,
authority,
offset,
});
}

Ok(Self {
_key: key,
registry,
})
}
}
20 changes: 7 additions & 13 deletions clients/rust/src/hooked/asset.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
//! This code was AUTOGENERATED using the kinobi library.
//! Please DO NOT EDIT THIS FILE, instead use visitors
//! to add features, then rerun kinobi to update it.
//!
//! [https://github.com/metaplex-foundation/kinobi]
//!
use borsh::BorshSerialize;

use crate::{
accounts::{BaseAssetV1, PluginHeaderV1, PluginRegistryV1},
registry_records_to_plugin_list, Asset,
accounts::{BaseAssetV1, PluginHeaderV1},
registry_records_to_plugin_list, Asset, PluginRegistryV1Safe,
};

impl Asset {
pub fn deserialize_asset(data: &[u8]) -> Result<Asset, std::io::Error> {
pub fn deserialize(data: &[u8]) -> Result<Self, std::io::Error> {
let base = BaseAssetV1::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 = PluginRegistryV1::from_bytes(
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)?;

(Some(plugin_header), Some(plugin_list))
Expand All @@ -37,7 +31,7 @@ impl Asset {

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

Expand All @@ -48,6 +42,6 @@ impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Asset {
account_info: &solana_program::account_info::AccountInfo<'a>,
) -> Result<Self, Self::Error> {
let data: &[u8] = &(*account_info.data).borrow();
Self::deserialize_asset(data)
Self::deserialize(data)
}
}
47 changes: 47 additions & 0 deletions clients/rust/src/hooked/collection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use borsh::BorshSerialize;

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

impl Collection {
pub fn deserialize(data: &[u8]) -> Result<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)?;

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

Ok(Self {
base,
plugin_list: plugin_list.unwrap_or_default(),
plugin_header,
})
}

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

impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for 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)
}
}
22 changes: 21 additions & 1 deletion clients/rust/src/hooked/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ pub use advanced_types::*;
pub mod asset;
pub use asset::*;

use borsh::{BorshDeserialize, BorshSerialize};
pub mod collection;
pub use collection::*;

use borsh::{BorshDeserialize, BorshSerialize};
use std::{cmp::Ordering, mem::size_of};

use crate::{
Expand All @@ -18,6 +20,24 @@ use crate::{
};
use solana_program::account_info::AccountInfo;

impl PluginType {
// Needed to determine if a plugin is a known or unknown type.
pub fn from_u8(n: u8) -> Option<PluginType> {
match n {
0 => Some(PluginType::Royalties),
1 => Some(PluginType::FreezeDelegate),
2 => Some(PluginType::BurnDelegate),
3 => Some(PluginType::TransferDelegate),
4 => Some(PluginType::UpdateDelegate),
5 => Some(PluginType::PermanentFreezeDelegate),
6 => Some(PluginType::Attributes),
7 => Some(PluginType::PermanentTransferDelegate),
8 => Some(PluginType::PermanentBurnDelegate),
_ => None,
}
}
}

impl From<&Plugin> for PluginType {
fn from(plugin: &Plugin) -> Self {
match plugin {
Expand Down
Loading

0 comments on commit 012d081

Please sign in to comment.