From 55fa547661800e7eb04238b143336614a5fead7c Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Mon, 26 Feb 2024 14:44:01 +0100 Subject: [PATCH 1/6] feat: add deserializers for transforming plerkle structs into solana structs --- Cargo.lock | 2 +- plerkle_serialization/Cargo.toml | 6 +- .../src/deserializer/common.rs | 27 ++++ plerkle_serialization/src/deserializer/mod.rs | 5 + .../src/deserializer/solana.rs | 150 ++++++++++++++++++ plerkle_serialization/src/lib.rs | 3 +- 6 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 plerkle_serialization/src/deserializer/common.rs 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 fc21b7bf..10fbfe82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2168,7 +2168,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 3968f41f..77a61029 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.17"} +serde = { version = "1.0.152" } +solana-sdk = { version = "~1.17" } solana-transaction-status = { version = "~1.17" } bs58 = "0.4.0" thiserror = "1.0.38" diff --git a/plerkle_serialization/src/deserializer/common.rs b/plerkle_serialization/src/deserializer/common.rs new file mode 100644 index 00000000..12144239 --- /dev/null +++ b/plerkle_serialization/src/deserializer/common.rs @@ -0,0 +1,27 @@ +use crate::{ + CompiledInnerInstructions as FBCompiledInnerInstructions, + CompiledInstruction as FBCompiledInstruction, Pubkey as FBPubkey, +}; +use flatbuffers::{ForwardsUOffset, Vector}; + +/// Represents an optional reference to a FBPubkey. +pub struct OptionalPubkey<'a>(pub Option<&'a FBPubkey>); + +/// Represents an optional Vector of u8 values. +pub struct OptionalU8Vector<'a>(pub Option>); + +/// Represents an optional reference to a string. +pub struct OptionalStr<'a>(pub Option<&'a str>); + +/// Represents an optional Vector of FBPubkey values. +pub struct OptionalPubkeyVector<'a>(pub Option>); + +/// Represents an optional Vector of compiled instructions. +pub struct OptionalCompiledInstructionVector<'a>( + pub Option>>>, +); + +/// Represents an optional Vector of compiled inner instructions. +pub struct OptionalCompiledInnerInstructionsVector<'a>( + pub Option>>>, +); diff --git a/plerkle_serialization/src/deserializer/mod.rs b/plerkle_serialization/src/deserializer/mod.rs new file mode 100644 index 00000000..b7050bd0 --- /dev/null +++ b/plerkle_serialization/src/deserializer/mod.rs @@ -0,0 +1,5 @@ +mod common; +mod solana; + +pub use common::*; +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..603e89bd --- /dev/null +++ b/plerkle_serialization/src/deserializer/solana.rs @@ -0,0 +1,150 @@ +use super::{ + OptionalCompiledInnerInstructionsVector, OptionalCompiledInstructionVector, OptionalPubkey, + OptionalPubkeyVector, OptionalStr, OptionalU8Vector, +}; +use crate::Pubkey as FBPubkey; +use solana_sdk::{ + instruction::CompiledInstruction, + pubkey::Pubkey, + signature::{ParseSignatureError, Signature}, +}; +use 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<'a> TryFrom> for Pubkey { + type Error = SolanaDeserializerError; + + fn try_from(value: OptionalPubkey<'a>) -> Result { + value + .0 + .ok_or(SolanaDeserializerError::NotFound) + .and_then(|data| Pubkey::try_from(data)) + } +} + +impl TryFrom<&FBPubkey> for Pubkey { + type Error = SolanaDeserializerError; + + fn try_from(value: &FBPubkey) -> Result { + (value.0) + .try_into() + .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) + } +} + +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) + } +} + +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) + } +} + +impl<'a> TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(value: OptionalPubkeyVector<'a>) -> Result { + value + .0 + .ok_or(SolanaDeserializerError::Pubkey)? + .iter() + .map(|key| Pubkey::try_from(key)) + .collect::>() + } +} + +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) + } +} + +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::*; From a0dbc4102936d08e55fe930a2cb4d574cc1a0e5e Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Tue, 27 Feb 2024 12:30:13 +0100 Subject: [PATCH 2/6] refactor: transfer parser fn from nft_ingester associated with the swap to solana structs for program_transform to preserve usage of references. Using impl required copies. --- .../src/deserializer/common.rs | 27 --- plerkle_serialization/src/deserializer/mod.rs | 2 - .../src/deserializer/solana.rs | 215 ++++++++---------- 3 files changed, 89 insertions(+), 155 deletions(-) delete mode 100644 plerkle_serialization/src/deserializer/common.rs diff --git a/plerkle_serialization/src/deserializer/common.rs b/plerkle_serialization/src/deserializer/common.rs deleted file mode 100644 index 12144239..00000000 --- a/plerkle_serialization/src/deserializer/common.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{ - CompiledInnerInstructions as FBCompiledInnerInstructions, - CompiledInstruction as FBCompiledInstruction, Pubkey as FBPubkey, -}; -use flatbuffers::{ForwardsUOffset, Vector}; - -/// Represents an optional reference to a FBPubkey. -pub struct OptionalPubkey<'a>(pub Option<&'a FBPubkey>); - -/// Represents an optional Vector of u8 values. -pub struct OptionalU8Vector<'a>(pub Option>); - -/// Represents an optional reference to a string. -pub struct OptionalStr<'a>(pub Option<&'a str>); - -/// Represents an optional Vector of FBPubkey values. -pub struct OptionalPubkeyVector<'a>(pub Option>); - -/// Represents an optional Vector of compiled instructions. -pub struct OptionalCompiledInstructionVector<'a>( - pub Option>>>, -); - -/// Represents an optional Vector of compiled inner instructions. -pub struct OptionalCompiledInnerInstructionsVector<'a>( - pub Option>>>, -); diff --git a/plerkle_serialization/src/deserializer/mod.rs b/plerkle_serialization/src/deserializer/mod.rs index b7050bd0..3da226cc 100644 --- a/plerkle_serialization/src/deserializer/mod.rs +++ b/plerkle_serialization/src/deserializer/mod.rs @@ -1,5 +1,3 @@ -mod common; mod solana; -pub use common::*; pub use solana::*; diff --git a/plerkle_serialization/src/deserializer/solana.rs b/plerkle_serialization/src/deserializer/solana.rs index 603e89bd..3d42858c 100644 --- a/plerkle_serialization/src/deserializer/solana.rs +++ b/plerkle_serialization/src/deserializer/solana.rs @@ -1,150 +1,113 @@ -use super::{ - OptionalCompiledInnerInstructionsVector, OptionalCompiledInstructionVector, OptionalPubkey, - OptionalPubkeyVector, OptionalStr, OptionalU8Vector, -}; -use crate::Pubkey as FBPubkey; -use solana_sdk::{ - instruction::CompiledInstruction, - pubkey::Pubkey, - signature::{ParseSignatureError, Signature}, +use crate::{ + CompiledInnerInstructions as FBCompiledInnerInstructions, + CompiledInstruction as FBCompiledInstruction, Pubkey as FBPubkey, }; +use flatbuffers::{ForwardsUOffset, Vector}; +use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey, signature::Signature}; use solana_transaction_status::{InnerInstruction, InnerInstructions}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Clone, PartialEq, thiserror::Error)] pub enum SolanaDeserializerError { - #[error("solana pubkey deserialization error")] - Pubkey, - #[error("solana signature deserialization error")] - Signature, - #[error("expected data not found in FlatBuffer")] + #[error("Deserialization error")] + DeserializationError, + #[error("Not found")] NotFound, + #[error("Invalid FlatBuffer key")] + InvalidFlatBufferKey, } -impl<'a> TryFrom> for Pubkey { - type Error = SolanaDeserializerError; +pub type SolanaDeserializeResult = Result; - fn try_from(value: OptionalPubkey<'a>) -> Result { - value +pub fn parse_pubkey(pubkey: Option<&FBPubkey>) -> SolanaDeserializeResult { + Pubkey::try_from( + pubkey + .ok_or(SolanaDeserializerError::NotFound)? .0 - .ok_or(SolanaDeserializerError::NotFound) - .and_then(|data| Pubkey::try_from(data)) - } -} - -impl TryFrom<&FBPubkey> for Pubkey { - type Error = SolanaDeserializerError; - - fn try_from(value: &FBPubkey) -> Result { - (value.0) - .try_into() - .map_err(|_| SolanaDeserializerError::Pubkey) - } + .as_slice(), + ) + .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) } -impl TryFrom for Pubkey { - type Error = SolanaDeserializerError; - - fn try_from(value: FBPubkey) -> Result { - Pubkey::try_from(value.0).map_err(|_| SolanaDeserializerError::Pubkey) - } +pub fn parse_slice(data: Option>) -> SolanaDeserializeResult<&[u8]> { + data.map(|data| data.bytes()) + .ok_or(SolanaDeserializerError::NotFound) } -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) - } +pub fn parse_signature(data: Option<&str>) -> SolanaDeserializeResult { + data.ok_or(SolanaDeserializerError::NotFound)? + .parse() + .map_err(|_error| SolanaDeserializerError::DeserializationError) } -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 fn parse_account_keys( + keys: Option>, +) -> SolanaDeserializeResult> { + keys.ok_or(SolanaDeserializerError::NotFound)? + .iter() + .map(|key| { + Pubkey::try_from(key.0.as_slice()) + .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) + }) + .collect::>>() } -impl<'a> TryFrom> for Vec { - type Error = SolanaDeserializerError; - - fn try_from(value: OptionalPubkeyVector<'a>) -> Result { - value - .0 - .ok_or(SolanaDeserializerError::Pubkey)? - .iter() - .map(|key| Pubkey::try_from(key)) - .collect::>() +pub fn parse_message_instructions( + vec_cix: Option>>, +) -> SolanaDeserializeResult> { + let mut message_instructions = vec![]; + for cix in vec_cix.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(), + }) } -} - -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) - } + Ok(message_instructions) } -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, - }) +pub fn parse_meta_inner_instructions( + vec_ixs: Option>>, +) -> SolanaDeserializeResult> { + let mut meta_inner_instructions = vec![]; + for ixs in vec_ixs.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), + }); } - Ok(meta_inner_instructions) + meta_inner_instructions.push(InnerInstructions { + index: ixs.index(), + instructions, + }) } + Ok(meta_inner_instructions) } From 6c883b67472d7331ae15d04cd9c2d3f1f3d24e15 Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Wed, 28 Feb 2024 14:19:02 +0100 Subject: [PATCH 3/6] refactor: add another parser for fb inner instructions to solana inner instructions --- .../src/deserializer/solana.rs | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/plerkle_serialization/src/deserializer/solana.rs b/plerkle_serialization/src/deserializer/solana.rs index 3d42858c..a0a4cc7a 100644 --- a/plerkle_serialization/src/deserializer/solana.rs +++ b/plerkle_serialization/src/deserializer/solana.rs @@ -1,6 +1,7 @@ use crate::{ CompiledInnerInstructions as FBCompiledInnerInstructions, - CompiledInstruction as FBCompiledInstruction, Pubkey as FBPubkey, + CompiledInstruction as FBCompiledInstruction, InnerInstructions as FBInnerInstructions, + Pubkey as FBPubkey, }; use flatbuffers::{ForwardsUOffset, Vector}; use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey, signature::Signature}; @@ -51,7 +52,7 @@ pub fn parse_account_keys( .collect::>>() } -pub fn parse_message_instructions( +pub fn parse_compiled_instructions( vec_cix: Option>>, ) -> SolanaDeserializeResult> { let mut message_instructions = vec![]; @@ -74,7 +75,7 @@ pub fn parse_message_instructions( Ok(message_instructions) } -pub fn parse_meta_inner_instructions( +pub fn parse_compiled_inner_instructions( vec_ixs: Option>>, ) -> SolanaDeserializeResult> { let mut meta_inner_instructions = vec![]; @@ -111,3 +112,41 @@ pub fn parse_meta_inner_instructions( } Ok(meta_inner_instructions) } + +pub fn parse_inner_instructions( + vec_ixs: Option>>, +) -> SolanaDeserializeResult> { + vec_ixs + .ok_or(SolanaDeserializerError::NotFound)? + .iter() + .map(|iixs| { + let instructions = iixs + .instructions() + .ok_or(SolanaDeserializerError::NotFound)? + .iter() + .map(|cix| { + Ok(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(0), + }) + }) + .collect::>>()?; + Ok(InnerInstructions { + index: iixs.index(), + instructions, + }) + }) + .collect::>>() +} From cbf6ca58fc20ea5c76fc3174c0ef01d4d602d255 Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Wed, 28 Feb 2024 19:23:57 +0100 Subject: [PATCH 4/6] refactor: handle optionality outside of parsers --- Cargo.lock | 2 +- plerkle_serialization/Cargo.toml | 2 +- .../src/deserializer/solana.rs | 39 ++++++++----------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10fbfe82..faecf606 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2168,7 +2168,7 @@ dependencies = [ [[package]] name = "plerkle_serialization" -version = "1.7.0" +version = "1.8.0" dependencies = [ "bs58", "chrono", diff --git a/plerkle_serialization/Cargo.toml b/plerkle_serialization/Cargo.toml index 77a61029..dfdda406 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.7.0" +version = "1.8.0" authors = ["Metaplex Developers "] repository = "https://github.com/metaplex-foundation/digital-asset-validator-plugin" license = "AGPL-3.0" diff --git a/plerkle_serialization/src/deserializer/solana.rs b/plerkle_serialization/src/deserializer/solana.rs index a0a4cc7a..7a0913cf 100644 --- a/plerkle_serialization/src/deserializer/solana.rs +++ b/plerkle_serialization/src/deserializer/solana.rs @@ -19,31 +19,24 @@ pub enum SolanaDeserializerError { pub type SolanaDeserializeResult = Result; -pub fn parse_pubkey(pubkey: Option<&FBPubkey>) -> SolanaDeserializeResult { - Pubkey::try_from( - pubkey - .ok_or(SolanaDeserializerError::NotFound)? - .0 - .as_slice(), - ) - .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) +pub fn parse_pubkey(pubkey: &FBPubkey) -> SolanaDeserializeResult { + Pubkey::try_from(pubkey.0.as_slice()) + .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) } -pub fn parse_slice(data: Option>) -> SolanaDeserializeResult<&[u8]> { - data.map(|data| data.bytes()) - .ok_or(SolanaDeserializerError::NotFound) +pub fn parse_slice(data: Vector<'_, u8>) -> SolanaDeserializeResult<&[u8]> { + Ok(data.bytes()) } -pub fn parse_signature(data: Option<&str>) -> SolanaDeserializeResult { - data.ok_or(SolanaDeserializerError::NotFound)? - .parse() +pub fn parse_signature(data: &str) -> SolanaDeserializeResult { + data.parse() .map_err(|_error| SolanaDeserializerError::DeserializationError) } pub fn parse_account_keys( - keys: Option>, + public_keys: Vector<'_, FBPubkey>, ) -> SolanaDeserializeResult> { - keys.ok_or(SolanaDeserializerError::NotFound)? + public_keys .iter() .map(|key| { Pubkey::try_from(key.0.as_slice()) @@ -53,10 +46,11 @@ pub fn parse_account_keys( } pub fn parse_compiled_instructions( - vec_cix: Option>>, + vec_cix: Vector<'_, ForwardsUOffset>, ) -> SolanaDeserializeResult> { let mut message_instructions = vec![]; - for cix in vec_cix.ok_or(SolanaDeserializerError::NotFound)? { + + for cix in vec_cix { message_instructions.push(CompiledInstruction { program_id_index: cix.program_id_index(), accounts: cix @@ -76,10 +70,11 @@ pub fn parse_compiled_instructions( } pub fn parse_compiled_inner_instructions( - vec_ixs: Option>>, + vec_ixs: Vector<'_, ForwardsUOffset>, ) -> SolanaDeserializeResult> { let mut meta_inner_instructions = vec![]; - for ixs in vec_ixs.ok_or(SolanaDeserializerError::NotFound)? { + + for ixs in vec_ixs { let mut instructions = vec![]; for ix in ixs .instructions() @@ -110,14 +105,14 @@ pub fn parse_compiled_inner_instructions( instructions, }) } + Ok(meta_inner_instructions) } pub fn parse_inner_instructions( - vec_ixs: Option>>, + vec_ixs: Vector<'_, ForwardsUOffset>, ) -> SolanaDeserializeResult> { vec_ixs - .ok_or(SolanaDeserializerError::NotFound)? .iter() .map(|iixs| { let instructions = iixs From 53bce9048ac0bbaa43246185edf785ab16b5c07c Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Thu, 29 Feb 2024 18:03:33 +0100 Subject: [PATCH 5/6] refactor: move back to handling option --- .../src/deserializer/solana.rs | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/plerkle_serialization/src/deserializer/solana.rs b/plerkle_serialization/src/deserializer/solana.rs index 7a0913cf..ec2f6ea2 100644 --- a/plerkle_serialization/src/deserializer/solana.rs +++ b/plerkle_serialization/src/deserializer/solana.rs @@ -19,24 +19,31 @@ pub enum SolanaDeserializerError { pub type SolanaDeserializeResult = Result; -pub fn parse_pubkey(pubkey: &FBPubkey) -> SolanaDeserializeResult { - Pubkey::try_from(pubkey.0.as_slice()) - .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) +pub fn parse_pubkey(pubkey: Option<&FBPubkey>) -> SolanaDeserializeResult { + Pubkey::try_from( + pubkey + .ok_or(SolanaDeserializerError::NotFound)? + .0 + .as_slice(), + ) + .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) } -pub fn parse_slice(data: Vector<'_, u8>) -> SolanaDeserializeResult<&[u8]> { - Ok(data.bytes()) +pub fn parse_slice(data: Option>) -> SolanaDeserializeResult<&[u8]> { + Ok(data.ok_or(SolanaDeserializerError::NotFound)?.bytes()) } -pub fn parse_signature(data: &str) -> SolanaDeserializeResult { - data.parse() +pub fn parse_signature(data: Option<&str>) -> SolanaDeserializeResult { + data.ok_or(SolanaDeserializerError::NotFound)? + .parse() .map_err(|_error| SolanaDeserializerError::DeserializationError) } pub fn parse_account_keys( - public_keys: Vector<'_, FBPubkey>, + public_keys: Option>, ) -> SolanaDeserializeResult> { public_keys + .ok_or(SolanaDeserializerError::NotFound)? .iter() .map(|key| { Pubkey::try_from(key.0.as_slice()) @@ -46,24 +53,26 @@ pub fn parse_account_keys( } pub fn parse_compiled_instructions( - vec_cix: Vector<'_, ForwardsUOffset>, + vec_cix: Option>>, ) -> SolanaDeserializeResult> { let mut message_instructions = vec![]; - for cix in vec_cix { - 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(), - }) + if let Some(vec_cix) = vec_cix { + for cix in vec_cix { + 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) From a623576e6022b9d4a0ae4c6b0bd1b1a4dc8c2b93 Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Mon, 4 Mar 2024 15:16:26 +0100 Subject: [PATCH 6/6] refactor: which to try from trait for doing struct conversion --- .../src/deserializer/solana.rs | 241 ++++++++++-------- 1 file changed, 141 insertions(+), 100 deletions(-) diff --git a/plerkle_serialization/src/deserializer/solana.rs b/plerkle_serialization/src/deserializer/solana.rs index ec2f6ea2..445fa3be 100644 --- a/plerkle_serialization/src/deserializer/solana.rs +++ b/plerkle_serialization/src/deserializer/solana.rs @@ -1,3 +1,5 @@ +use std::convert::TryFrom; + use crate::{ CompiledInnerInstructions as FBCompiledInnerInstructions, CompiledInstruction as FBCompiledInstruction, InnerInstructions as FBInnerInstructions, @@ -19,46 +21,71 @@ pub enum SolanaDeserializerError { pub type SolanaDeserializeResult = Result; -pub fn parse_pubkey(pubkey: Option<&FBPubkey>) -> SolanaDeserializeResult { - Pubkey::try_from( - pubkey - .ok_or(SolanaDeserializerError::NotFound)? - .0 - .as_slice(), - ) - .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) +impl<'a> TryFrom<&FBPubkey> for Pubkey { + type Error = SolanaDeserializerError; + + fn try_from(pubkey: &FBPubkey) -> SolanaDeserializeResult { + Pubkey::try_from(pubkey.0.as_slice()) + .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) + } } -pub fn parse_slice(data: Option>) -> SolanaDeserializeResult<&[u8]> { - Ok(data.ok_or(SolanaDeserializerError::NotFound)?.bytes()) +pub struct PlerkleOptionalU8Vector<'a>(pub Option>); + +impl<'a> TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(data: PlerkleOptionalU8Vector<'a>) -> SolanaDeserializeResult { + Ok(data + .0 + .ok_or(SolanaDeserializerError::NotFound)? + .bytes() + .to_vec()) + } } -pub fn parse_signature(data: Option<&str>) -> SolanaDeserializeResult { - data.ok_or(SolanaDeserializerError::NotFound)? - .parse() - .map_err(|_error| SolanaDeserializerError::DeserializationError) +pub struct PlerkleOptionalStr<'a>(pub Option<&'a str>); + +impl<'a> TryFrom> for Signature { + type Error = SolanaDeserializerError; + + fn try_from(data: PlerkleOptionalStr<'a>) -> SolanaDeserializeResult { + data.0 + .ok_or(SolanaDeserializerError::NotFound)? + .parse::() + .map_err(|_error| SolanaDeserializerError::DeserializationError) + } } -pub fn parse_account_keys( - public_keys: Option>, -) -> SolanaDeserializeResult> { - public_keys - .ok_or(SolanaDeserializerError::NotFound)? - .iter() - .map(|key| { - Pubkey::try_from(key.0.as_slice()) - .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) - }) - .collect::>>() +pub struct PlerkleOptionalPubkeyVector<'a>(pub Option>); + +impl<'a> TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(public_keys: PlerkleOptionalPubkeyVector<'a>) -> SolanaDeserializeResult { + public_keys + .0 + .ok_or(SolanaDeserializerError::NotFound)? + .iter() + .map(|key| { + Pubkey::try_from(key.0.as_slice()) + .map_err(|_error| SolanaDeserializerError::InvalidFlatBufferKey) + }) + .collect::>>() + } } -pub fn parse_compiled_instructions( - vec_cix: Option>>, -) -> SolanaDeserializeResult> { - let mut message_instructions = vec![]; +pub struct PlerkleCompiledInstructionVector<'a>( + pub Vector<'a, ForwardsUOffset>>, +); - if let Some(vec_cix) = vec_cix { - for cix in vec_cix { +impl<'a> TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(vec_cix: PlerkleCompiledInstructionVector<'a>) -> SolanaDeserializeResult { + let mut message_instructions = vec![]; + + for cix in vec_cix.0 { message_instructions.push(CompiledInstruction { program_id_index: cix.program_id_index(), accounts: cix @@ -73,84 +100,98 @@ pub fn parse_compiled_instructions( .to_vec(), }) } - } - Ok(message_instructions) + Ok(message_instructions) + } } -pub fn parse_compiled_inner_instructions( - vec_ixs: Vector<'_, ForwardsUOffset>, -) -> SolanaDeserializeResult> { - let mut meta_inner_instructions = vec![]; +pub struct PlerkleCompiledInnerInstructionVector<'a>( + pub Vector<'a, ForwardsUOffset>>, +); +impl<'a> TryFrom> for Vec { + type Error = SolanaDeserializerError; - for ixs in vec_ixs { - 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), - }); + fn try_from( + vec_ixs: PlerkleCompiledInnerInstructionVector<'a>, + ) -> SolanaDeserializeResult { + let mut meta_inner_instructions = vec![]; + + for ixs in vec_ixs.0 { + 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, + }) } - meta_inner_instructions.push(InnerInstructions { - index: ixs.index(), - instructions, - }) - } - Ok(meta_inner_instructions) + Ok(meta_inner_instructions) + } } -pub fn parse_inner_instructions( - vec_ixs: Vector<'_, ForwardsUOffset>, -) -> SolanaDeserializeResult> { - vec_ixs - .iter() - .map(|iixs| { - let instructions = iixs - .instructions() - .ok_or(SolanaDeserializerError::NotFound)? - .iter() - .map(|cix| { - Ok(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(0), +pub struct PlerkleInnerInstructionsVector<'a>( + pub Vector<'a, ForwardsUOffset>>, +); + +impl<'a> TryFrom> for Vec { + type Error = SolanaDeserializerError; + + fn try_from(vec_ixs: PlerkleInnerInstructionsVector<'a>) -> SolanaDeserializeResult { + vec_ixs + .0 + .iter() + .map(|iixs| { + let instructions = iixs + .instructions() + .ok_or(SolanaDeserializerError::NotFound)? + .iter() + .map(|cix| { + Ok(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(0), + }) }) + .collect::>>()?; + Ok(InnerInstructions { + index: iixs.index(), + instructions, }) - .collect::>>()?; - Ok(InnerInstructions { - index: iixs.index(), - instructions, }) - }) - .collect::>>() + .collect::>>() + } }