diff --git a/Cargo.lock b/Cargo.lock index 6a48fc9bdb..1379493b06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4710,6 +4710,7 @@ dependencies = [ "serde", "serde_bytes", "sha2 0.9.2", + "solana-account-decoder", "solana-bpf-loader-program", "solana-budget-program", "solana-frozen-abi 1.6.0", diff --git a/core/src/transaction_status_service.rs b/core/src/transaction_status_service.rs index 620ab76ce6..1b1f07b16e 100644 --- a/core/src/transaction_status_service.rs +++ b/core/src/transaction_status_service.rs @@ -124,7 +124,7 @@ impl TransactionStatusService { transaction.signatures[0], writable_keys, readonly_keys, - &TransactionStatusMeta { + TransactionStatusMeta { status, fee, pre_balances, diff --git a/ledger/Cargo.toml b/ledger/Cargo.toml index b88b01b63c..57e7853255 100644 --- a/ledger/Cargo.toml +++ b/ledger/Cargo.toml @@ -64,6 +64,7 @@ features = ["lz4"] [dev-dependencies] assert_matches = "1.3.0" matches = "0.1.6" +solana-account-decoder = { path = "../account-decoder", version = "1.6.0" } solana-budget-program = { path = "../programs/budget", version = "1.6.0" } [build-dependencies] diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 884efc6b4c..4ec6321e77 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -35,7 +35,7 @@ use solana_sdk::{ timing::timestamp, transaction::Transaction, }; -use solana_storage_proto::StoredExtendedRewards; +use solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta}; use solana_transaction_status::{ ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature, Rewards, TransactionStatusMeta, TransactionWithStatusMeta, @@ -44,6 +44,7 @@ use std::{ cell::RefCell, cmp, collections::{HashMap, HashSet}, + convert::TryInto, fs, io::{Error as IoError, ErrorKind}, path::{Path, PathBuf}, @@ -1783,7 +1784,8 @@ impl Blockstore { transaction, meta: self .read_transaction_status((signature, slot)) - .expect("Expect database get to succeed"), + .ok() + .flatten(), } }) .collect() @@ -1799,10 +1801,9 @@ impl Blockstore { self.transaction_status_index_cf .put(1, &TransactionStatusIndexMeta::default())?; // This dummy status improves compaction performance - self.transaction_status_cf.put( - cf::TransactionStatus::as_index(2), - &TransactionStatusMeta::default(), - )?; + let default_status = TransactionStatusMeta::default().into(); + self.transaction_status_cf + .put_protobuf(cf::TransactionStatus::as_index(2), &default_status)?; self.address_signatures_cf.put( cf::AddressSignatures::as_index(2), &AddressSignatureMeta::default(), @@ -1878,11 +1879,16 @@ impl Blockstore { index: (Signature, Slot), ) -> Result> { let (signature, slot) = index; - let result = self.transaction_status_cf.get((0, signature, slot))?; + let result = self + .transaction_status_cf + .get_protobuf_or_bincode::((0, signature, slot))?; if result.is_none() { - Ok(self.transaction_status_cf.get((1, signature, slot))?) + Ok(self + .transaction_status_cf + .get_protobuf_or_bincode::((1, signature, slot))? + .and_then(|meta| meta.try_into().ok())) } else { - Ok(result) + Ok(result.and_then(|meta| meta.try_into().ok())) } } @@ -1892,15 +1898,16 @@ impl Blockstore { signature: Signature, writable_keys: Vec<&Pubkey>, readonly_keys: Vec<&Pubkey>, - status: &TransactionStatusMeta, + status: TransactionStatusMeta, ) -> Result<()> { + let status = status.into(); // This write lock prevents interleaving issues with the transaction_status_index_cf by gating // writes to that column let mut w_active_transaction_status_index = self.active_transaction_status_index.write().unwrap(); let primary_index = self.get_primary_index(slot, &mut w_active_transaction_status_index)?; self.transaction_status_cf - .put((primary_index, signature, slot), status)?; + .put_protobuf((primary_index, signature, slot), &status)?; for address in writable_keys { self.address_signatures_cf.put( (primary_index, *address, slot, signature), @@ -1928,14 +1935,18 @@ impl Blockstore { (transaction_status_cf_primary_index, signature, 0), IteratorDirection::Forward, ))?; - for ((i, sig, slot), data) in index_iterator { + for ((i, sig, slot), _data) in index_iterator { counter += 1; if i != transaction_status_cf_primary_index || sig != signature { break; } if self.is_root(slot) { - let status: TransactionStatusMeta = deserialize(&data)?; - return Ok((Some((slot, status)), counter)); + let status = self + .transaction_status_cf + .get_protobuf_or_bincode::((i, sig, slot))? + .and_then(|status| status.try_into().ok()) + .map(|status| (slot, status)); + return Ok((status, counter)); } } } @@ -3531,6 +3542,7 @@ pub mod tests { use bincode::serialize; use itertools::Itertools; use rand::{seq::SliceRandom, thread_rng}; + use solana_account_decoder::parse_token::UiTokenAmount; use solana_runtime::bank::{Bank, RewardType}; use solana_sdk::{ hash::{self, hash, Hash}, @@ -3541,7 +3553,7 @@ pub mod tests { transaction::TransactionError, }; use solana_storage_proto::convert::generated; - use solana_transaction_status::{InnerInstructions, Reward, Rewards}; + use solana_transaction_status::{InnerInstructions, Reward, Rewards, TransactionTokenBalance}; use std::time::Duration; // used for tests only @@ -5706,37 +5718,35 @@ pub mod tests { post_balances.push(i as u64 * 11); } let signature = transaction.signatures[0]; + let status = TransactionStatusMeta { + status: Ok(()), + fee: 42, + pre_balances: pre_balances.clone(), + post_balances: post_balances.clone(), + inner_instructions: Some(vec![]), + log_messages: Some(vec![]), + pre_token_balances: Some(vec![]), + post_token_balances: Some(vec![]), + } + .into(); ledger .transaction_status_cf - .put( - (0, signature, slot), - &TransactionStatusMeta { - status: Ok(()), - fee: 42, - pre_balances: pre_balances.clone(), - post_balances: post_balances.clone(), - inner_instructions: Some(vec![]), - log_messages: Some(vec![]), - pre_token_balances: Some(vec![]), - post_token_balances: Some(vec![]), - }, - ) + .put_protobuf((0, signature, slot), &status) .unwrap(); + let status = TransactionStatusMeta { + status: Ok(()), + fee: 42, + pre_balances: pre_balances.clone(), + post_balances: post_balances.clone(), + inner_instructions: Some(vec![]), + log_messages: Some(vec![]), + pre_token_balances: Some(vec![]), + post_token_balances: Some(vec![]), + } + .into(); ledger .transaction_status_cf - .put( - (0, signature, slot + 1), - &TransactionStatusMeta { - status: Ok(()), - fee: 42, - pre_balances: pre_balances.clone(), - post_balances: post_balances.clone(), - inner_instructions: Some(vec![]), - log_messages: Some(vec![]), - pre_token_balances: Some(vec![]), - post_token_balances: Some(vec![]), - }, - ) + .put_protobuf((0, signature, slot + 1), &status) .unwrap(); TransactionWithStatusMeta { transaction, @@ -5826,27 +5836,30 @@ pub mod tests { // result not found assert!(transaction_status_cf - .get((0, Signature::default(), 0)) + .get_protobuf_or_bincode::(( + 0, + Signature::default(), + 0 + )) .unwrap() .is_none()); // insert value + let status = TransactionStatusMeta { + status: solana_sdk::transaction::Result::<()>::Err( + TransactionError::AccountNotFound, + ), + fee: 5u64, + pre_balances: pre_balances_vec.clone(), + post_balances: post_balances_vec.clone(), + inner_instructions: Some(inner_instructions_vec.clone()), + log_messages: Some(log_messages_vec.clone()), + pre_token_balances: Some(pre_token_balances_vec.clone()), + post_token_balances: Some(post_token_balances_vec.clone()), + } + .into(); assert!(transaction_status_cf - .put( - (0, Signature::default(), 0), - &TransactionStatusMeta { - status: solana_sdk::transaction::Result::<()>::Err( - TransactionError::AccountNotFound - ), - fee: 5u64, - pre_balances: pre_balances_vec.clone(), - post_balances: post_balances_vec.clone(), - inner_instructions: Some(inner_instructions_vec.clone()), - log_messages: Some(log_messages_vec.clone()), - pre_token_balances: Some(pre_token_balances_vec.clone()), - post_token_balances: Some(post_token_balances_vec.clone()) - }, - ) + .put_protobuf((0, Signature::default(), 0), &status,) .is_ok()); // result found @@ -5860,8 +5873,14 @@ pub mod tests { pre_token_balances, post_token_balances, } = transaction_status_cf - .get((0, Signature::default(), 0)) + .get_protobuf_or_bincode::(( + 0, + Signature::default(), + 0, + )) .unwrap() + .unwrap() + .try_into() .unwrap(); assert_eq!(status, Err(TransactionError::AccountNotFound)); assert_eq!(fee, 5u64); @@ -5873,20 +5892,19 @@ pub mod tests { assert_eq!(post_token_balances.unwrap(), post_token_balances_vec); // insert value + let status = TransactionStatusMeta { + status: solana_sdk::transaction::Result::<()>::Ok(()), + fee: 9u64, + pre_balances: pre_balances_vec.clone(), + post_balances: post_balances_vec.clone(), + inner_instructions: Some(inner_instructions_vec.clone()), + log_messages: Some(log_messages_vec.clone()), + pre_token_balances: Some(pre_token_balances_vec.clone()), + post_token_balances: Some(post_token_balances_vec.clone()), + } + .into(); assert!(transaction_status_cf - .put( - (0, Signature::new(&[2u8; 64]), 9), - &TransactionStatusMeta { - status: solana_sdk::transaction::Result::<()>::Ok(()), - fee: 9u64, - pre_balances: pre_balances_vec.clone(), - post_balances: post_balances_vec.clone(), - inner_instructions: Some(inner_instructions_vec.clone()), - log_messages: Some(log_messages_vec.clone()), - pre_token_balances: Some(pre_token_balances_vec.clone()), - post_token_balances: Some(post_token_balances_vec.clone()) - }, - ) + .put_protobuf((0, Signature::new(&[2u8; 64]), 9), &status,) .is_ok()); // result found @@ -5900,8 +5918,14 @@ pub mod tests { pre_token_balances, post_token_balances, } = transaction_status_cf - .get((0, Signature::new(&[2u8; 64]), 9)) + .get_protobuf_or_bincode::(( + 0, + Signature::new(&[2u8; 64]), + 9, + )) .unwrap() + .unwrap() + .try_into() .unwrap(); // deserialize @@ -5938,7 +5962,7 @@ pub mod tests { Signature::new(&random_bytes), vec![&Pubkey::new(&random_bytes[0..32])], vec![&Pubkey::new(&random_bytes[32..])], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -6004,7 +6028,7 @@ pub mod tests { Signature::new(&random_bytes), vec![&Pubkey::new(&random_bytes[0..32])], vec![&Pubkey::new(&random_bytes[32..])], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -6143,7 +6167,8 @@ pub mod tests { log_messages: Some(vec![]), pre_token_balances: Some(vec![]), post_token_balances: Some(vec![]), - }; + } + .into(); let signature1 = Signature::new(&[1u8; 64]); let signature2 = Signature::new(&[2u8; 64]); @@ -6157,46 +6182,46 @@ pub mod tests { // signature4 in 2 non-roots, // extra entries transaction_status_cf - .put((0, signature2, 1), &status) + .put_protobuf((0, signature2, 1), &status) .unwrap(); transaction_status_cf - .put((0, signature2, 2), &status) + .put_protobuf((0, signature2, 2), &status) .unwrap(); transaction_status_cf - .put((0, signature4, 0), &status) + .put_protobuf((0, signature4, 0), &status) .unwrap(); transaction_status_cf - .put((0, signature4, 1), &status) + .put_protobuf((0, signature4, 1), &status) .unwrap(); transaction_status_cf - .put((0, signature5, 0), &status) + .put_protobuf((0, signature5, 0), &status) .unwrap(); transaction_status_cf - .put((0, signature5, 1), &status) + .put_protobuf((0, signature5, 1), &status) .unwrap(); // Initialize index 1, including: // signature4 in non-root and root, // extra entries transaction_status_cf - .put((1, signature4, 1), &status) + .put_protobuf((1, signature4, 1), &status) .unwrap(); transaction_status_cf - .put((1, signature4, 2), &status) + .put_protobuf((1, signature4, 2), &status) .unwrap(); transaction_status_cf - .put((1, signature5, 0), &status) + .put_protobuf((1, signature5, 0), &status) .unwrap(); transaction_status_cf - .put((1, signature5, 1), &status) + .put_protobuf((1, signature5, 1), &status) .unwrap(); blockstore.set_roots(&[2]).unwrap(); @@ -6280,21 +6305,20 @@ pub mod tests { let pre_token_balances = Some(vec![]); let post_token_balances = Some(vec![]); let signature = transaction.signatures[0]; + let status = TransactionStatusMeta { + status: Ok(()), + fee: 42, + pre_balances: pre_balances.clone(), + post_balances: post_balances.clone(), + inner_instructions: inner_instructions.clone(), + log_messages: log_messages.clone(), + pre_token_balances: pre_token_balances.clone(), + post_token_balances: post_token_balances.clone(), + } + .into(); blockstore .transaction_status_cf - .put( - (0, signature, slot), - &TransactionStatusMeta { - status: Ok(()), - fee: 42, - pre_balances: pre_balances.clone(), - post_balances: post_balances.clone(), - inner_instructions: inner_instructions.clone(), - log_messages: log_messages.clone(), - pre_token_balances: pre_token_balances.clone(), - post_token_balances: post_token_balances.clone(), - }, - ) + .put_protobuf((0, signature, slot), &status) .unwrap(); TransactionWithStatusMeta { transaction, @@ -6366,7 +6390,7 @@ pub mod tests { signature, vec![&address0], vec![&address1], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -6381,7 +6405,7 @@ pub mod tests { signature, vec![&address0], vec![&address1], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -6474,7 +6498,7 @@ pub mod tests { signature, vec![&address0], vec![&address1], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -6534,7 +6558,7 @@ pub mod tests { transaction.signatures[0], transaction.message.account_keys.iter().collect(), vec![], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -6740,22 +6764,21 @@ pub mod tests { vec![solana_sdk::pubkey::new_rand()], vec![CompiledInstruction::new(1, &(), vec![0])], ); + let status = TransactionStatusMeta { + status: solana_sdk::transaction::Result::<()>::Err( + TransactionError::AccountNotFound, + ), + fee: x, + pre_balances: vec![], + post_balances: vec![], + inner_instructions: Some(vec![]), + log_messages: Some(vec![]), + pre_token_balances: Some(vec![]), + post_token_balances: Some(vec![]), + } + .into(); transaction_status_cf - .put( - (0, transaction.signatures[0], slot), - &TransactionStatusMeta { - status: solana_sdk::transaction::Result::<()>::Err( - TransactionError::AccountNotFound, - ), - fee: x, - pre_balances: vec![], - post_balances: vec![], - inner_instructions: Some(vec![]), - log_messages: Some(vec![]), - pre_token_balances: Some(vec![]), - post_token_balances: Some(vec![]), - }, - ) + .put_protobuf((0, transaction.signatures[0], slot), &status) .unwrap(); transactions.push(transaction); } @@ -7264,6 +7287,73 @@ pub mod tests { Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); } + #[test] + fn test_transaction_status_protobuf_backward_compatability() { + let blockstore_path = get_tmp_ledger_path!(); + { + let blockstore = Blockstore::open(&blockstore_path).unwrap(); + let status = TransactionStatusMeta { + status: Ok(()), + fee: 42, + pre_balances: vec![1, 2, 3], + post_balances: vec![1, 2, 3], + inner_instructions: Some(vec![]), + log_messages: Some(vec![]), + pre_token_balances: Some(vec![TransactionTokenBalance { + account_index: 0, + mint: Pubkey::new_unique().to_string(), + ui_token_amount: UiTokenAmount { + ui_amount: Some(1.1), + decimals: 1, + amount: "11".to_string(), + ui_amount_string: "1.1".to_string(), + }, + }]), + post_token_balances: Some(vec![TransactionTokenBalance { + account_index: 0, + mint: Pubkey::new_unique().to_string(), + ui_token_amount: UiTokenAmount { + ui_amount: None, + decimals: 1, + amount: "11".to_string(), + ui_amount_string: "1.1".to_string(), + }, + }]), + }; + let deprecated_status: StoredTransactionStatusMeta = status.clone().into(); + let protobuf_status: generated::TransactionStatusMeta = status.into(); + + for slot in 0..2 { + let data = serialize(&deprecated_status).unwrap(); + blockstore + .transaction_status_cf + .put_bytes((0, Signature::default(), slot), &data) + .unwrap(); + } + for slot in 2..4 { + blockstore + .transaction_status_cf + .put_protobuf((0, Signature::default(), slot), &protobuf_status) + .unwrap(); + } + for slot in 0..4 { + assert_eq!( + blockstore + .transaction_status_cf + .get_protobuf_or_bincode::(( + 0, + Signature::default(), + slot + )) + .unwrap() + .unwrap(), + protobuf_status + ); + } + } + Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); + } + #[test] fn test_remove_shred_data_complete_flag() { let (mut shreds, entries) = make_slot_entries(0, 0, 1); diff --git a/ledger/src/blockstore/blockstore_purge.rs b/ledger/src/blockstore/blockstore_purge.rs index f39390ac42..bd7e41e122 100644 --- a/ledger/src/blockstore/blockstore_purge.rs +++ b/ledger/src/blockstore/blockstore_purge.rs @@ -504,7 +504,7 @@ pub mod tests { Signature::new(&random_bytes), vec![&Pubkey::new(&random_bytes[0..32])], vec![&Pubkey::new(&random_bytes[32..])], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -519,7 +519,7 @@ pub mod tests { Signature::new(&random_bytes), vec![&Pubkey::new(&random_bytes[0..32])], vec![&Pubkey::new(&random_bytes[32..])], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -556,7 +556,7 @@ pub mod tests { Signature::new(&random_bytes), vec![&Pubkey::new(&random_bytes[0..32])], vec![&Pubkey::new(&random_bytes[32..])], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -745,7 +745,7 @@ pub mod tests { signature, vec![&Pubkey::new(&random_bytes[0..32])], vec![&Pubkey::new(&random_bytes[32..])], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } @@ -781,7 +781,7 @@ pub mod tests { signature, vec![&Pubkey::new(&random_bytes[0..32])], vec![&Pubkey::new(&random_bytes[32..])], - &TransactionStatusMeta::default(), + TransactionStatusMeta::default(), ) .unwrap(); } diff --git a/ledger/src/blockstore_db.rs b/ledger/src/blockstore_db.rs index 14466a2333..ff014dac39 100644 --- a/ledger/src/blockstore_db.rs +++ b/ledger/src/blockstore_db.rs @@ -17,7 +17,6 @@ use solana_sdk::{ signature::Signature, }; use solana_storage_proto::convert::generated; -use solana_transaction_status::TransactionStatusMeta; use std::{collections::HashMap, fs, marker::PhantomData, path::Path, sync::Arc}; use thiserror::Error; @@ -418,10 +417,6 @@ pub trait TypedColumn: Column { type Type: Serialize + DeserializeOwned; } -impl TypedColumn for columns::TransactionStatus { - type Type = TransactionStatusMeta; -} - impl TypedColumn for columns::AddressSignatures { type Type = blockstore_meta::AddressSignatureMeta; } @@ -492,6 +487,9 @@ impl Column for columns::TransactionStatus { impl ColumnName for columns::TransactionStatus { const NAME: &'static str = TRANSACTION_STATUS_CF; } +impl ProtobufColumn for columns::TransactionStatus { + type Type = generated::TransactionStatusMeta; +} impl Column for columns::AddressSignatures { type Index = (u64, Pubkey, Slot, Signature); diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 9541ab0c0b..9750ab1fee 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -1,4 +1,4 @@ -use crate::StoredExtendedRewards; +use crate::{StoredExtendedRewards, StoredTransactionStatusMeta}; use solana_account_decoder::parse_token::{real_number_string_trimmed, UiTokenAmount}; use solana_sdk::{ hash::Hash, @@ -312,6 +312,13 @@ impl From for generated::TransactionStatusMeta { } } +impl From for generated::TransactionStatusMeta { + fn from(meta: StoredTransactionStatusMeta) -> Self { + let meta: TransactionStatusMeta = meta.into(); + meta.into() + } +} + impl TryFrom for TransactionStatusMeta { type Error = bincode::Error; diff --git a/storage-proto/src/lib.rs b/storage-proto/src/lib.rs index 810a31d8df..1a4148987f 100644 --- a/storage-proto/src/lib.rs +++ b/storage-proto/src/lib.rs @@ -1,6 +1,13 @@ use serde::{Deserialize, Serialize}; -use solana_sdk::deserialize_utils::default_on_eof; -use solana_transaction_status::{Reward, RewardType}; +use solana_account_decoder::{ + parse_token::{real_number_string_trimmed, UiTokenAmount}, + StringAmount, +}; +use solana_sdk::{deserialize_utils::default_on_eof, transaction::Result}; +use solana_transaction_status::{ + InnerInstructions, Reward, RewardType, TransactionStatusMeta, TransactionTokenBalance, +}; +use std::str::FromStr; pub mod convert; @@ -50,3 +57,151 @@ impl From for StoredExtendedReward { } } } + +#[derive(Serialize, Deserialize)] +pub struct StoredTokenAmount { + pub ui_amount: f64, + pub decimals: u8, + pub amount: StringAmount, +} + +impl From for UiTokenAmount { + fn from(value: StoredTokenAmount) -> Self { + let StoredTokenAmount { + ui_amount, + decimals, + amount, + } = value; + let ui_amount_string = + real_number_string_trimmed(u64::from_str(&amount).unwrap_or(0), decimals); + Self { + ui_amount: Some(ui_amount), + decimals, + amount, + ui_amount_string, + } + } +} + +impl From for StoredTokenAmount { + fn from(value: UiTokenAmount) -> Self { + let UiTokenAmount { + ui_amount, + decimals, + amount, + .. + } = value; + Self { + ui_amount: ui_amount.unwrap_or(0.0), + decimals, + amount, + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct StoredTransactionTokenBalance { + pub account_index: u8, + pub mint: String, + pub ui_token_amount: StoredTokenAmount, +} + +impl From for TransactionTokenBalance { + fn from(value: StoredTransactionTokenBalance) -> Self { + let StoredTransactionTokenBalance { + account_index, + mint, + ui_token_amount, + } = value; + Self { + account_index, + mint, + ui_token_amount: ui_token_amount.into(), + } + } +} + +impl From for StoredTransactionTokenBalance { + fn from(value: TransactionTokenBalance) -> Self { + let TransactionTokenBalance { + account_index, + mint, + ui_token_amount, + } = value; + Self { + account_index, + mint, + ui_token_amount: ui_token_amount.into(), + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct StoredTransactionStatusMeta { + pub status: Result<()>, + pub fee: u64, + pub pre_balances: Vec, + pub post_balances: Vec, + #[serde(deserialize_with = "default_on_eof")] + pub inner_instructions: Option>, + #[serde(deserialize_with = "default_on_eof")] + pub log_messages: Option>, + #[serde(deserialize_with = "default_on_eof")] + pub pre_token_balances: Option>, + #[serde(deserialize_with = "default_on_eof")] + pub post_token_balances: Option>, +} + +impl From for TransactionStatusMeta { + fn from(value: StoredTransactionStatusMeta) -> Self { + let StoredTransactionStatusMeta { + status, + fee, + pre_balances, + post_balances, + inner_instructions, + log_messages, + pre_token_balances, + post_token_balances, + } = value; + Self { + status, + fee, + pre_balances, + post_balances, + inner_instructions, + log_messages, + pre_token_balances: pre_token_balances + .map(|balances| balances.into_iter().map(|balance| balance.into()).collect()), + post_token_balances: post_token_balances + .map(|balances| balances.into_iter().map(|balance| balance.into()).collect()), + } + } +} + +impl From for StoredTransactionStatusMeta { + fn from(value: TransactionStatusMeta) -> Self { + let TransactionStatusMeta { + status, + fee, + pre_balances, + post_balances, + inner_instructions, + log_messages, + pre_token_balances, + post_token_balances, + } = value; + Self { + status, + fee, + pre_balances, + post_balances, + inner_instructions, + log_messages, + pre_token_balances: pre_token_balances + .map(|balances| balances.into_iter().map(|balance| balance.into()).collect()), + post_token_balances: post_token_balances + .map(|balances| balances.into_iter().map(|balance| balance.into()).collect()), + } + } +}