diff --git a/Cargo.lock b/Cargo.lock index 456879d..bc87067 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2227,7 +2227,7 @@ dependencies = [ [[package]] name = "plerkle_serialization" -version = "1.6.0+solana.1.17.20" +version = "1.7.0+solana.1.17.20" dependencies = [ "bs58", "chrono", diff --git a/plerkle_serialization/Cargo.toml b/plerkle_serialization/Cargo.toml index b69c554..045e9cd 100644 --- a/plerkle_serialization/Cargo.toml +++ b/plerkle_serialization/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plerkle_serialization" description = "Metaplex Flatbuffers Plerkle Serialization for Geyser plugin producer/consumer patterns." -version = "1.6.0+solana.1.17.20" +version = "1.7.0+solana.1.17.20" authors = ["Metaplex Developers "] repository = "https://github.com/metaplex-foundation/digital-asset-validator-plugin" license = "AGPL-3.0" diff --git a/plerkle_serialization/src/deserializer/mod.rs b/plerkle_serialization/src/deserializer/mod.rs new file mode 100644 index 0000000..3da226c --- /dev/null +++ b/plerkle_serialization/src/deserializer/mod.rs @@ -0,0 +1,3 @@ +mod solana; + +pub use solana::*; diff --git a/plerkle_serialization/src/deserializer/solana.rs b/plerkle_serialization/src/deserializer/solana.rs new file mode 100644 index 0000000..77176a3 --- /dev/null +++ b/plerkle_serialization/src/deserializer/solana.rs @@ -0,0 +1,152 @@ +use { + crate::{ + CompiledInnerInstructions as FBCompiledInnerInstructions, + CompiledInstruction as FBCompiledInstruction, Pubkey as FBPubkey, + }, + flatbuffers::{ForwardsUOffset, Vector}, + solana_sdk::{ + instruction::CompiledInstruction, + pubkey::Pubkey, + signature::{ParseSignatureError, Signature}, + }, + solana_transaction_status::{InnerInstruction, InnerInstructions}, +}; + +#[derive(Debug, thiserror::Error)] +pub enum SolanaDeserializerError { + #[error("solana pubkey deserialization error")] + Pubkey, + #[error("solana signature deserialization error")] + Signature, + #[error("expected data not found in FlatBuffer")] + NotFound, +} + +impl TryFrom<&FBPubkey> for Pubkey { + type Error = SolanaDeserializerError; + + fn try_from(value: &FBPubkey) -> Result { + Pubkey::try_from(value.0.as_slice()).map_err(|_| SolanaDeserializerError::Pubkey) + } +} + +impl TryFrom for Pubkey { + type Error = SolanaDeserializerError; + + fn try_from(value: FBPubkey) -> Result { + Pubkey::try_from(value.0).map_err(|_| SolanaDeserializerError::Pubkey) + } +} + +pub struct OptionalU8Vector<'a>(Option>); + +impl<'a> TryFrom> for &'a [u8] { + type Error = SolanaDeserializerError; + + fn try_from(value: OptionalU8Vector<'a>) -> Result { + value + .0 + .map(|data| data.bytes()) + .ok_or(SolanaDeserializerError::NotFound) + } +} + +struct OptionalStr<'a>(Option<&'a str>); + +impl<'a> TryFrom> for Signature { + type Error = SolanaDeserializerError; + + fn try_from(value: OptionalStr<'a>) -> Result { + value + .0 + .ok_or(ParseSignatureError::Invalid) + .and_then(|data| data.parse()) + .map_err(|_| SolanaDeserializerError::Signature) + } +} + +pub struct OptionalPubkeyVector<'a>(Option>); + +impl<'a> TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(value: OptionalPubkeyVector<'a>) -> Result { + let mut account_keys = vec![]; + for key in value.0.ok_or(SolanaDeserializerError::Pubkey)? { + account_keys.push(Pubkey::try_from(key)?); + } + Ok(account_keys) + } +} + +pub struct OptionalCompiledInstructionVector<'a>( + Option>>>, +); + +impl TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(value: OptionalCompiledInstructionVector<'_>) -> Result { + let mut message_instructions = vec![]; + for cix in value.0.ok_or(SolanaDeserializerError::NotFound)? { + message_instructions.push(CompiledInstruction { + program_id_index: cix.program_id_index(), + accounts: cix + .accounts() + .ok_or(SolanaDeserializerError::NotFound)? + .bytes() + .to_vec(), + data: cix + .data() + .ok_or(SolanaDeserializerError::NotFound)? + .bytes() + .to_vec(), + }) + } + Ok(message_instructions) + } +} + +pub struct OptionalCompiledInnerInstructionsVector<'a>( + Option>>>, +); + +impl TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(value: OptionalCompiledInnerInstructionsVector<'_>) -> Result { + let mut meta_inner_instructions = vec![]; + for ixs in value.0.ok_or(SolanaDeserializerError::NotFound)? { + let mut instructions = vec![]; + for ix in ixs + .instructions() + .ok_or(SolanaDeserializerError::NotFound)? + { + let cix = ix + .compiled_instruction() + .ok_or(SolanaDeserializerError::NotFound)?; + instructions.push(InnerInstruction { + instruction: CompiledInstruction { + program_id_index: cix.program_id_index(), + accounts: cix + .accounts() + .ok_or(SolanaDeserializerError::NotFound)? + .bytes() + .to_vec(), + data: cix + .data() + .ok_or(SolanaDeserializerError::NotFound)? + .bytes() + .to_vec(), + }, + stack_height: Some(ix.stack_height() as u32), + }); + } + meta_inner_instructions.push(InnerInstructions { + index: ixs.index(), + instructions, + }) + } + Ok(meta_inner_instructions) + } +} diff --git a/plerkle_serialization/src/lib.rs b/plerkle_serialization/src/lib.rs index 357e8e4..bc58218 100644 --- a/plerkle_serialization/src/lib.rs +++ b/plerkle_serialization/src/lib.rs @@ -16,6 +16,7 @@ mod slot_status_info_generated; #[allow(unused_imports)] mod transaction_info_generated; +pub mod deserializer; pub mod error; pub mod serializer; pub use account_info_generated::*;