From 7f49ecc0024462069f63282c41e5cc15388348f7 Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Mon, 26 Feb 2024 14:44:01 +0100 Subject: [PATCH] feat: add deserializers for transforming plerkle structs into solana structs --- Cargo.lock | 2 +- plerkle_serialization/Cargo.toml | 6 +- plerkle_serialization/src/deserializer/mod.rs | 3 + .../src/deserializer/solana.rs | 152 ++++++++++++++++++ plerkle_serialization/src/lib.rs | 3 +- 5 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 plerkle_serialization/src/deserializer/mod.rs create mode 100644 plerkle_serialization/src/deserializer/solana.rs diff --git a/Cargo.lock b/Cargo.lock index adf26300..a665bb77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2162,7 +2162,7 @@ dependencies = [ [[package]] name = "plerkle_serialization" -version = "1.6.0" +version = "1.7.0" dependencies = [ "bs58", "chrono", diff --git a/plerkle_serialization/Cargo.toml b/plerkle_serialization/Cargo.toml index 1f14b1dd..f0e96982 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" +version = "1.7.0" authors = ["Metaplex Developers "] repository = "https://github.com/metaplex-foundation/digital-asset-validator-plugin" license = "AGPL-3.0" @@ -11,8 +11,8 @@ readme = "Readme.md" [dependencies] flatbuffers = "23.1.21" chrono = "0.4.22" -serde = { version = "1.0.152"} -solana-sdk = { version = "~1.16"} +serde = { version = "1.0.152" } +solana-sdk = { version = "~1.16" } solana-transaction-status = { version = "~1.16" } bs58 = "0.4.0" thiserror = "1.0.38" diff --git a/plerkle_serialization/src/deserializer/mod.rs b/plerkle_serialization/src/deserializer/mod.rs new file mode 100644 index 00000000..3da226cc --- /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 00000000..77176a38 --- /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 b5ecb4b3..19db1cc2 100644 --- a/plerkle_serialization/src/lib.rs +++ b/plerkle_serialization/src/lib.rs @@ -11,8 +11,9 @@ mod slot_status_info_generated; #[allow(unused_imports)] mod transaction_info_generated; -pub mod serializer; +pub mod deserializer; pub mod error; +pub mod serializer; pub use account_info_generated::*; pub use block_info_generated::*; pub use common_generated::*;