Add block_time to getConfirmedSignaturesForAddress2 and getConfirmedTransaction (#14572)

* add block_time to get_confirmed_signatures_for_address2 and protobuf implementation for tx_by_addr

* add tests for convert

* update cargo lock

* run cargo format after rebase

* introduce legacy TransactionByAddrInfo

* move LegacyTransactionByAddrInfo back to storage-bigtable
This commit is contained in:
Josh 2021-01-20 22:10:35 -08:00 committed by GitHub
parent 447e3de1f2
commit 1de6d28eaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1219 additions and 20 deletions

1
Cargo.lock generated
View File

@ -5017,6 +5017,7 @@ name = "solana-storage-proto"
version = "1.6.0"
dependencies = [
"bincode",
"bs58",
"prost",
"serde",
"serde_derive",

View File

@ -1,7 +1,7 @@
use crate::client_error;
use solana_account_decoder::{parse_token::UiTokenAmount, UiAccount};
use solana_sdk::{
clock::{Epoch, Slot},
clock::{Epoch, Slot, UnixTimestamp},
fee_calculator::{FeeCalculator, FeeRateGovernor},
inflation::Inflation,
transaction::{Result, TransactionError},
@ -286,6 +286,7 @@ pub struct RpcConfirmedTransactionStatusWithSignature {
pub slot: Slot,
pub err: Option<TransactionError>,
pub memo: Option<String>,
pub block_time: Option<UnixTimestamp>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -304,12 +305,14 @@ impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionSt
slot,
err,
memo,
block_time,
} = value;
Self {
signature: signature.to_string(),
slot,
err,
memo,
block_time,
}
}
}

View File

@ -2023,12 +2023,14 @@ impl Blockstore {
let transaction = self
.find_transaction_in_slot(slot, signature)?
.ok_or(BlockstoreError::TransactionStatusSlotMismatch)?; // Should not happen
let block_time = self.get_block_time(slot)?;
Ok(Some(ConfirmedTransaction {
slot,
transaction: TransactionWithStatusMeta {
transaction,
meta: Some(status),
},
block_time,
}))
} else {
Ok(None)
@ -2323,11 +2325,13 @@ impl Blockstore {
None => None,
Some((_slot, status)) => status.status.err(),
};
let block_time = self.get_block_time(slot)?;
infos.push(ConfirmedTransactionStatusWithSignature {
signature,
slot,
err,
memo: None,
block_time,
});
}
get_status_info_timer.stop();
@ -6590,7 +6594,11 @@ pub mod tests {
let signature = transaction.transaction.signatures[0];
assert_eq!(
blockstore.get_confirmed_transaction(signature).unwrap(),
Some(ConfirmedTransaction { slot, transaction })
Some(ConfirmedTransaction {
slot,
transaction,
block_time: None
})
);
}

View File

@ -8,9 +8,10 @@ use solana_sdk::{
transaction::{Transaction, TransactionError},
};
use solana_storage_proto::convert::generated;
use solana_storage_proto::convert::tx_by_addr;
use solana_transaction_status::{
ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature, Reward,
TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta,
TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta,
TransactionWithStatusMeta,
};
use std::{collections::HashMap, convert::TryInto};
@ -263,14 +264,31 @@ impl From<TransactionInfo> for TransactionStatus {
}
}
// A serialized `Vec<TransactionByAddrInfo>` is stored in the `tx-by-addr` table. The row keys are
// the one's compliment of the slot so that rows may be listed in reverse order
#[derive(Serialize, Deserialize)]
struct TransactionByAddrInfo {
signature: Signature, // The transaction signature
err: Option<TransactionError>, // None if the transaction executed successfully
index: u32, // Where the transaction is located in the block
memo: Option<String>, // Transaction memo
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct LegacyTransactionByAddrInfo {
pub signature: Signature, // The transaction signature
pub err: Option<TransactionError>, // None if the transaction executed successfully
pub index: u32, // Where the transaction is located in the block
pub memo: Option<String>, // Transaction memo
}
impl From<LegacyTransactionByAddrInfo> for TransactionByAddrInfo {
fn from(legacy: LegacyTransactionByAddrInfo) -> Self {
let LegacyTransactionByAddrInfo {
signature,
err,
index,
memo,
} = legacy;
Self {
signature,
err,
index,
memo,
block_time: None,
}
}
}
#[derive(Clone)]
@ -363,6 +381,7 @@ impl LedgerStorage {
Ok(Some(ConfirmedTransaction {
slot,
transaction: bucket_block_transaction,
block_time: block.block_time,
}))
}
}
@ -417,12 +436,17 @@ impl LedgerStorage {
let mut infos = vec![];
let starting_slot_tx_len = bigtable
.get_bincode_cell::<Vec<TransactionByAddrInfo>>(
.get_protobuf_or_bincode_cell::<Vec<LegacyTransactionByAddrInfo>, tx_by_addr::TransactionByAddr>(
"tx-by-addr",
format!("{}{}", address_prefix, slot_to_key(!first_slot)),
)
.await
.map(|txs| txs.len())
.map(|cell_data| {
match cell_data {
bigtable::CellData::Bincode(tx_by_addr) => tx_by_addr.len(),
bigtable::CellData::Protobuf(tx_by_addr) => tx_by_addr.tx_by_addrs.len(),
}
})
.unwrap_or(0);
// Return the next tx-by-addr data of amount `limit` plus extra to account for the largest
@ -443,8 +467,27 @@ impl LedgerStorage {
row_key
))
})?;
let mut cell_data: Vec<TransactionByAddrInfo> =
bigtable::deserialize_bincode_cell_data(&data, "tx-by-addr", row_key)?;
let deserialized_cell_data = bigtable::deserialize_protobuf_or_bincode_cell_data::<
Vec<LegacyTransactionByAddrInfo>,
tx_by_addr::TransactionByAddr,
>(&data, "tx-by-addr", row_key.clone())?;
let mut cell_data: Vec<TransactionByAddrInfo> = match deserialized_cell_data {
bigtable::CellData::Bincode(tx_by_addr) => {
tx_by_addr.into_iter().map(|legacy| legacy.into()).collect()
}
bigtable::CellData::Protobuf(tx_by_addr) => {
tx_by_addr.try_into().map_err(|error| {
bigtable::Error::ObjectCorrupt(format!(
"Failed to deserialize: {}: tx-by-addr/{}",
error,
row_key.clone()
))
})?
}
};
cell_data.reverse();
for tx_by_addr_info in cell_data.into_iter() {
// Filter out records before `before_transaction_index`
@ -461,6 +504,7 @@ impl LedgerStorage {
slot,
err: tx_by_addr_info.err,
memo: tx_by_addr_info.memo,
block_time: tx_by_addr_info.block_time,
},
tx_by_addr_info.index,
));
@ -500,6 +544,7 @@ impl LedgerStorage {
err: err.clone(),
index,
memo: None, // TODO
block_time: confirmed_block.block_time,
});
}
}
@ -520,7 +565,12 @@ impl LedgerStorage {
.map(|(address, transaction_info_by_addr)| {
(
format!("{}/{}", address, slot_to_key(!slot)),
transaction_info_by_addr,
tx_by_addr::TransactionByAddr {
tx_by_addrs: transaction_info_by_addr
.into_iter()
.map(|by_addr| by_addr.into())
.collect(),
},
)
})
.collect();
@ -535,7 +585,7 @@ impl LedgerStorage {
if !tx_by_addr_cells.is_empty() {
bytes_written += self
.connection
.put_bincode_cells_with_retry::<Vec<TransactionByAddrInfo>>(
.put_protobuf_cells_with_retry::<tx_by_addr::TransactionByAddr>(
"tx-by-addr",
&tx_by_addr_cells,
)

View File

@ -10,6 +10,7 @@ edition = "2018"
[dependencies]
bincode = "1.2.1"
bs58 = "0.3.1"
prost = "0.6.1"
serde = "1.0.112"
serde_derive = "1.0.103"

View File

@ -12,5 +12,11 @@ fn main() -> Result<(), std::io::Error> {
.build_server(false)
.format(true)
.out_dir(&out_dir)
.compile(&[proto_files.join("confirmed_block.proto")], &[proto_files])
.compile(
&[
proto_files.join("confirmed_block.proto"),
proto_files.join("transaction_by_addr.proto"),
],
&[proto_files],
)
}

View File

@ -0,0 +1,117 @@
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionByAddr {
#[prost(message, repeated, tag = "1")]
pub tx_by_addrs: ::std::vec::Vec<TransactionByAddrInfo>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionByAddrInfo {
#[prost(bytes, tag = "1")]
pub signature: std::vec::Vec<u8>,
#[prost(message, optional, tag = "2")]
pub err: ::std::option::Option<TransactionError>,
#[prost(uint32, tag = "3")]
pub index: u32,
#[prost(message, optional, tag = "4")]
pub memo: ::std::option::Option<Memo>,
#[prost(message, optional, tag = "5")]
pub block_time: ::std::option::Option<UnixTimestamp>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Memo {
#[prost(string, tag = "1")]
pub memo: std::string::String,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionError {
#[prost(enumeration = "TransactionErrorType", tag = "1")]
pub transaction_error: i32,
#[prost(message, optional, tag = "2")]
pub instruction_error: ::std::option::Option<InstructionError>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct InstructionError {
#[prost(uint32, tag = "1")]
pub index: u32,
#[prost(enumeration = "InstructionErrorType", tag = "2")]
pub error: i32,
#[prost(message, optional, tag = "3")]
pub custom: ::std::option::Option<CustomError>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UnixTimestamp {
#[prost(int64, tag = "1")]
pub timestamp: i64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CustomError {
#[prost(uint32, tag = "1")]
pub custom: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum TransactionErrorType {
AccountInUse = 0,
AccountLoadedTwice = 1,
AccountNotFound = 2,
ProgramAccountNotFound = 3,
InsufficientFundsForFee = 4,
InvalidAccountForFee = 5,
DuplicateSignature = 6,
BlockhashNotFound = 7,
InstructionError = 8,
CallChainTooDeep = 9,
MissingSignatureForFee = 10,
InvalidAccountIndex = 11,
SignatureFailure = 12,
InvalidProgramForExecution = 13,
SanitizeFailure = 14,
ClusterMaintenance = 15,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum InstructionErrorType {
GenericError = 0,
InvalidArgument = 1,
InvalidInstructionData = 2,
InvalidAccountData = 3,
AccountDataTooSmall = 4,
InsufficientFunds = 5,
IncorrectProgramId = 6,
MissingRequiredSignature = 7,
AccountAlreadyInitialized = 8,
UninitializedAccount = 9,
UnbalancedInstruction = 10,
ModifiedProgramId = 11,
ExternalAccountLamportSpend = 12,
ExternalAccountDataModified = 13,
ReadonlyLamportChange = 14,
ReadonlyDataModified = 15,
DuplicateAccountIndex = 16,
ExecutableModified = 17,
RentEpochModified = 18,
NotEnoughAccountKeys = 19,
AccountDataSizeChanged = 20,
AccountNotExecutable = 21,
AccountBorrowFailed = 22,
AccountBorrowOutstanding = 23,
DuplicateAccountOutOfSync = 24,
Custom = 25,
InvalidError = 26,
ExecutableDataModified = 27,
ExecutableLamportChange = 28,
ExecutableAccountNotRentExempt = 29,
UnsupportedProgramId = 30,
CallDepth = 31,
MissingAccount = 32,
ReentrancyNotAllowed = 33,
MaxSeedLengthExceeded = 34,
InvalidSeeds = 35,
InvalidRealloc = 36,
ComputationalBudgetExceeded = 37,
PrivilegeEscalation = 38,
ProgramEnvironmentSetupFailure = 39,
ProgramFailedToComplete = 40,
ProgramFailedToCompile = 41,
Immutable = 42,
IncorrectAuthority = 43,
}

View File

@ -3,14 +3,16 @@ use solana_account_decoder::parse_token::UiTokenAmount;
use solana_sdk::{
hash::Hash,
instruction::CompiledInstruction,
instruction::InstructionError,
message::{Message, MessageHeader},
pubkey::Pubkey,
signature::Signature,
transaction::Transaction,
transaction::TransactionError,
};
use solana_transaction_status::{
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionStatusMeta,
TransactionTokenBalance, TransactionWithStatusMeta,
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo,
TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta,
};
use std::convert::{TryFrom, TryInto};
@ -21,6 +23,13 @@ pub mod generated {
));
}
pub mod tx_by_addr {
include!(concat!(
env!("CARGO_MANIFEST_DIR"),
concat!("/proto/solana.storage.transaction_by_addr.rs")
));
}
impl From<Vec<Reward>> for generated::Rewards {
fn from(rewards: Vec<Reward>) -> Self {
Self {
@ -417,6 +426,356 @@ impl From<generated::CompiledInstruction> for CompiledInstruction {
}
}
impl TryFrom<tx_by_addr::TransactionError> for TransactionError {
type Error = &'static str;
fn try_from(transaction_error: tx_by_addr::TransactionError) -> Result<Self, Self::Error> {
if transaction_error.transaction_error == 8 {
if let Some(instruction_error) = transaction_error.instruction_error {
if let Some(custom) = instruction_error.custom {
return Ok(TransactionError::InstructionError(
instruction_error.index as u8,
InstructionError::Custom(custom.custom),
));
}
let ie = match instruction_error.error {
0 => InstructionError::GenericError,
1 => InstructionError::InvalidArgument,
2 => InstructionError::InvalidInstructionData,
3 => InstructionError::InvalidAccountData,
4 => InstructionError::AccountDataTooSmall,
5 => InstructionError::InsufficientFunds,
6 => InstructionError::IncorrectProgramId,
7 => InstructionError::MissingRequiredSignature,
8 => InstructionError::AccountAlreadyInitialized,
9 => InstructionError::UninitializedAccount,
10 => InstructionError::UnbalancedInstruction,
11 => InstructionError::ModifiedProgramId,
12 => InstructionError::ExternalAccountLamportSpend,
13 => InstructionError::ExternalAccountDataModified,
14 => InstructionError::ReadonlyLamportChange,
15 => InstructionError::ReadonlyDataModified,
16 => InstructionError::DuplicateAccountIndex,
17 => InstructionError::ExecutableModified,
18 => InstructionError::RentEpochModified,
19 => InstructionError::NotEnoughAccountKeys,
20 => InstructionError::AccountDataSizeChanged,
21 => InstructionError::AccountNotExecutable,
22 => InstructionError::AccountBorrowFailed,
23 => InstructionError::AccountBorrowOutstanding,
24 => InstructionError::DuplicateAccountOutOfSync,
26 => InstructionError::InvalidError,
27 => InstructionError::ExecutableDataModified,
28 => InstructionError::ExecutableLamportChange,
29 => InstructionError::ExecutableAccountNotRentExempt,
30 => InstructionError::UnsupportedProgramId,
31 => InstructionError::CallDepth,
32 => InstructionError::MissingAccount,
33 => InstructionError::ReentrancyNotAllowed,
34 => InstructionError::MaxSeedLengthExceeded,
35 => InstructionError::InvalidSeeds,
36 => InstructionError::InvalidRealloc,
37 => InstructionError::ComputationalBudgetExceeded,
38 => InstructionError::PrivilegeEscalation,
39 => InstructionError::ProgramEnvironmentSetupFailure,
40 => InstructionError::ProgramFailedToComplete,
41 => InstructionError::ProgramFailedToCompile,
42 => InstructionError::Immutable,
43 => InstructionError::IncorrectAuthority,
_ => return Err("Invalid InstructionError"),
};
return Ok(TransactionError::InstructionError(
instruction_error.index as u8,
ie,
));
}
}
Ok(match transaction_error.transaction_error {
0 => TransactionError::AccountInUse,
1 => TransactionError::AccountLoadedTwice,
2 => TransactionError::AccountNotFound,
3 => TransactionError::ProgramAccountNotFound,
4 => TransactionError::InsufficientFundsForFee,
5 => TransactionError::InvalidAccountForFee,
6 => TransactionError::DuplicateSignature,
7 => TransactionError::BlockhashNotFound,
9 => TransactionError::CallChainTooDeep,
10 => TransactionError::MissingSignatureForFee,
11 => TransactionError::InvalidAccountIndex,
12 => TransactionError::SignatureFailure,
13 => TransactionError::InvalidProgramForExecution,
14 => TransactionError::SanitizeFailure,
15 => TransactionError::ClusterMaintenance,
_ => return Err("Invalid TransactionError"),
})
}
}
impl From<TransactionError> for tx_by_addr::TransactionError {
fn from(transaction_error: TransactionError) -> Self {
Self {
transaction_error: match transaction_error {
TransactionError::AccountInUse => tx_by_addr::TransactionErrorType::AccountInUse,
TransactionError::AccountLoadedTwice => {
tx_by_addr::TransactionErrorType::AccountLoadedTwice
}
TransactionError::AccountNotFound => {
tx_by_addr::TransactionErrorType::AccountNotFound
}
TransactionError::ProgramAccountNotFound => {
tx_by_addr::TransactionErrorType::ProgramAccountNotFound
}
TransactionError::InsufficientFundsForFee => {
tx_by_addr::TransactionErrorType::InsufficientFundsForFee
}
TransactionError::InvalidAccountForFee => {
tx_by_addr::TransactionErrorType::InvalidAccountForFee
}
TransactionError::DuplicateSignature => {
tx_by_addr::TransactionErrorType::DuplicateSignature
}
TransactionError::BlockhashNotFound => {
tx_by_addr::TransactionErrorType::BlockhashNotFound
}
TransactionError::CallChainTooDeep => {
tx_by_addr::TransactionErrorType::CallChainTooDeep
}
TransactionError::MissingSignatureForFee => {
tx_by_addr::TransactionErrorType::MissingSignatureForFee
}
TransactionError::InvalidAccountIndex => {
tx_by_addr::TransactionErrorType::InvalidAccountIndex
}
TransactionError::SignatureFailure => {
tx_by_addr::TransactionErrorType::SignatureFailure
}
TransactionError::InvalidProgramForExecution => {
tx_by_addr::TransactionErrorType::InvalidProgramForExecution
}
TransactionError::SanitizeFailure => {
tx_by_addr::TransactionErrorType::SanitizeFailure
}
TransactionError::ClusterMaintenance => {
tx_by_addr::TransactionErrorType::ClusterMaintenance
}
TransactionError::InstructionError(_, _) => {
tx_by_addr::TransactionErrorType::InstructionError
}
} as i32,
instruction_error: match transaction_error {
TransactionError::InstructionError(index, ref instruction_error) => {
Some(tx_by_addr::InstructionError {
index: index as u32,
error: match instruction_error {
InstructionError::GenericError => {
tx_by_addr::InstructionErrorType::GenericError
}
InstructionError::InvalidArgument => {
tx_by_addr::InstructionErrorType::InvalidArgument
}
InstructionError::InvalidInstructionData => {
tx_by_addr::InstructionErrorType::InvalidInstructionData
}
InstructionError::InvalidAccountData => {
tx_by_addr::InstructionErrorType::InvalidAccountData
}
InstructionError::AccountDataTooSmall => {
tx_by_addr::InstructionErrorType::AccountDataTooSmall
}
InstructionError::InsufficientFunds => {
tx_by_addr::InstructionErrorType::InsufficientFunds
}
InstructionError::IncorrectProgramId => {
tx_by_addr::InstructionErrorType::IncorrectProgramId
}
InstructionError::MissingRequiredSignature => {
tx_by_addr::InstructionErrorType::MissingRequiredSignature
}
InstructionError::AccountAlreadyInitialized => {
tx_by_addr::InstructionErrorType::AccountAlreadyInitialized
}
InstructionError::UninitializedAccount => {
tx_by_addr::InstructionErrorType::UninitializedAccount
}
InstructionError::UnbalancedInstruction => {
tx_by_addr::InstructionErrorType::UnbalancedInstruction
}
InstructionError::ModifiedProgramId => {
tx_by_addr::InstructionErrorType::ModifiedProgramId
}
InstructionError::ExternalAccountLamportSpend => {
tx_by_addr::InstructionErrorType::ExternalAccountLamportSpend
}
InstructionError::ExternalAccountDataModified => {
tx_by_addr::InstructionErrorType::ExternalAccountDataModified
}
InstructionError::ReadonlyLamportChange => {
tx_by_addr::InstructionErrorType::ReadonlyLamportChange
}
InstructionError::ReadonlyDataModified => {
tx_by_addr::InstructionErrorType::ReadonlyDataModified
}
InstructionError::DuplicateAccountIndex => {
tx_by_addr::InstructionErrorType::DuplicateAccountIndex
}
InstructionError::ExecutableModified => {
tx_by_addr::InstructionErrorType::ExecutableModified
}
InstructionError::RentEpochModified => {
tx_by_addr::InstructionErrorType::RentEpochModified
}
InstructionError::NotEnoughAccountKeys => {
tx_by_addr::InstructionErrorType::NotEnoughAccountKeys
}
InstructionError::AccountDataSizeChanged => {
tx_by_addr::InstructionErrorType::AccountDataSizeChanged
}
InstructionError::AccountNotExecutable => {
tx_by_addr::InstructionErrorType::AccountNotExecutable
}
InstructionError::AccountBorrowFailed => {
tx_by_addr::InstructionErrorType::AccountBorrowFailed
}
InstructionError::AccountBorrowOutstanding => {
tx_by_addr::InstructionErrorType::AccountBorrowOutstanding
}
InstructionError::DuplicateAccountOutOfSync => {
tx_by_addr::InstructionErrorType::DuplicateAccountOutOfSync
}
InstructionError::Custom(_) => tx_by_addr::InstructionErrorType::Custom,
InstructionError::InvalidError => {
tx_by_addr::InstructionErrorType::InvalidError
}
InstructionError::ExecutableDataModified => {
tx_by_addr::InstructionErrorType::ExecutableDataModified
}
InstructionError::ExecutableLamportChange => {
tx_by_addr::InstructionErrorType::ExecutableLamportChange
}
InstructionError::ExecutableAccountNotRentExempt => {
tx_by_addr::InstructionErrorType::ExecutableAccountNotRentExempt
}
InstructionError::UnsupportedProgramId => {
tx_by_addr::InstructionErrorType::UnsupportedProgramId
}
InstructionError::CallDepth => {
tx_by_addr::InstructionErrorType::CallDepth
}
InstructionError::MissingAccount => {
tx_by_addr::InstructionErrorType::MissingAccount
}
InstructionError::ReentrancyNotAllowed => {
tx_by_addr::InstructionErrorType::ReentrancyNotAllowed
}
InstructionError::MaxSeedLengthExceeded => {
tx_by_addr::InstructionErrorType::MaxSeedLengthExceeded
}
InstructionError::InvalidSeeds => {
tx_by_addr::InstructionErrorType::InvalidSeeds
}
InstructionError::InvalidRealloc => {
tx_by_addr::InstructionErrorType::InvalidRealloc
}
InstructionError::ComputationalBudgetExceeded => {
tx_by_addr::InstructionErrorType::ComputationalBudgetExceeded
}
InstructionError::PrivilegeEscalation => {
tx_by_addr::InstructionErrorType::PrivilegeEscalation
}
InstructionError::ProgramEnvironmentSetupFailure => {
tx_by_addr::InstructionErrorType::ProgramEnvironmentSetupFailure
}
InstructionError::ProgramFailedToComplete => {
tx_by_addr::InstructionErrorType::ProgramFailedToComplete
}
InstructionError::ProgramFailedToCompile => {
tx_by_addr::InstructionErrorType::ProgramFailedToCompile
}
InstructionError::Immutable => {
tx_by_addr::InstructionErrorType::Immutable
}
InstructionError::IncorrectAuthority => {
tx_by_addr::InstructionErrorType::IncorrectAuthority
}
} as i32,
custom: match instruction_error {
InstructionError::Custom(custom) => {
Some(tx_by_addr::CustomError { custom: *custom })
}
_ => None,
},
})
}
_ => None,
},
}
}
}
impl From<TransactionByAddrInfo> for tx_by_addr::TransactionByAddrInfo {
fn from(by_addr: TransactionByAddrInfo) -> Self {
let TransactionByAddrInfo {
signature,
err,
index,
memo,
block_time,
} = by_addr;
Self {
signature: <Signature as AsRef<[u8]>>::as_ref(&signature).into(),
err: match err {
None => None,
Some(e) => Some(e.into()),
},
index,
memo: memo.map(|memo| tx_by_addr::Memo { memo }),
block_time: block_time.map(|timestamp| tx_by_addr::UnixTimestamp { timestamp }),
}
}
}
impl TryFrom<tx_by_addr::TransactionByAddrInfo> for TransactionByAddrInfo {
type Error = &'static str;
fn try_from(
transaction_by_addr: tx_by_addr::TransactionByAddrInfo,
) -> Result<Self, Self::Error> {
let err = if let Some(err) = transaction_by_addr.err {
Some(err.try_into()?)
} else {
None
};
Ok(Self {
signature: Signature::new(&transaction_by_addr.signature),
err,
index: transaction_by_addr.index,
memo: transaction_by_addr
.memo
.map(|tx_by_addr::Memo { memo }| memo),
block_time: transaction_by_addr
.block_time
.map(|tx_by_addr::UnixTimestamp { timestamp }| timestamp),
})
}
}
impl TryFrom<tx_by_addr::TransactionByAddr> for Vec<TransactionByAddrInfo> {
type Error = &'static str;
fn try_from(collection: tx_by_addr::TransactionByAddr) -> Result<Self, Self::Error> {
Ok(collection
.tx_by_addrs
.into_iter()
.map(|tx_by_addr| tx_by_addr.try_into())
.collect::<Result<Vec<TransactionByAddrInfo>, Self::Error>>()?)
}
}
#[cfg(test)]
mod test {
use super::*;
@ -448,4 +807,539 @@ mod test {
let gen_reward: generated::Reward = reward.clone().into();
assert_eq!(reward, gen_reward.into());
}
#[test]
fn test_transaction_by_addr_encode() {
let info = TransactionByAddrInfo {
signature: Signature::new(&bs58::decode("Nfo6rgemG1KLbk1xuNwfrQTsdxaGfLuWURHNRy9LYnDrubG7LFQZaA5obPNas9LQ6DdorJqxh2LxA3PsnWdkSrL").into_vec().unwrap()),
err: None,
index: 5,
memo: Some("string".to_string()),
block_time: Some(1610674861)
};
let tx_by_addr_transaction_info: tx_by_addr::TransactionByAddrInfo = info.clone().into();
assert_eq!(info, tx_by_addr_transaction_info.try_into().unwrap());
}
#[test]
fn test_transaction_error_encode() {
let transaction_error = TransactionError::AccountInUse;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::AccountLoadedTwice;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::AccountNotFound;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::BlockhashNotFound;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::CallChainTooDeep;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::ClusterMaintenance;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::DuplicateSignature;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InsufficientFundsForFee;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InvalidAccountForFee;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InvalidAccountIndex;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InvalidProgramForExecution;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::MissingSignatureForFee;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::ProgramAccountNotFound;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::SanitizeFailure;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::SignatureFailure;
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::AccountAlreadyInitialized);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::AccountBorrowFailed);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::AccountBorrowOutstanding);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::AccountDataSizeChanged);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::AccountDataTooSmall);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::AccountNotExecutable);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InstructionError(10, InstructionError::CallDepth);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ComputationalBudgetExceeded);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::DuplicateAccountIndex);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::DuplicateAccountOutOfSync);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InstructionError(
10,
InstructionError::ExecutableAccountNotRentExempt,
);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ExecutableDataModified);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ExecutableLamportChange);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ExecutableModified);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ExternalAccountDataModified);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ExternalAccountLamportSpend);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::GenericError);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InstructionError(10, InstructionError::Immutable);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::IncorrectAuthority);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::IncorrectProgramId);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::InsufficientFunds);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::InvalidAccountData);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::InvalidArgument);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::InvalidError);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::InvalidInstructionData);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::InvalidRealloc);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::InvalidSeeds);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::MaxSeedLengthExceeded);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::MissingAccount);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::MissingRequiredSignature);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ModifiedProgramId);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::NotEnoughAccountKeys);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::PrivilegeEscalation);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error = TransactionError::InstructionError(
10,
InstructionError::ProgramEnvironmentSetupFailure,
);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ProgramFailedToCompile);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ProgramFailedToComplete);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ReadonlyDataModified);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ReadonlyLamportChange);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::ReentrancyNotAllowed);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::RentEpochModified);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::UnbalancedInstruction);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::UninitializedAccount);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::UnsupportedProgramId);
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
let transaction_error =
TransactionError::InstructionError(10, InstructionError::Custom(10));
let tx_by_addr_transaction_error: tx_by_addr::TransactionError =
transaction_error.clone().into();
assert_eq!(
transaction_error,
tx_by_addr_transaction_error.try_into().unwrap()
);
}
}

View File

@ -0,0 +1,104 @@
syntax = "proto3";
package solana.storage.TransactionByAddr;
message TransactionByAddr {
repeated TransactionByAddrInfo tx_by_addrs = 1;
}
message TransactionByAddrInfo {
bytes signature = 1;
TransactionError err = 2;
uint32 index = 3;
Memo memo = 4;
UnixTimestamp block_time = 5;
}
message Memo {
string memo = 1;
}
message TransactionError {
TransactionErrorType transaction_error = 1;
InstructionError instruction_error = 2;
}
enum TransactionErrorType {
ACCOUNT_IN_USE = 0;
ACCOUNT_LOADED_TWICE = 1;
ACCOUNT_NOT_FOUND = 2;
PROGRAM_ACCOUNT_NOT_FOUND = 3;
INSUFFICIENT_FUNDS_FOR_FEE = 4;
INVALID_ACCOUNT_FOR_FEE = 5;
DUPLICATE_SIGNATURE = 6;
BLOCKHASH_NOT_FOUND = 7;
INSTRUCTION_ERROR = 8;
CALL_CHAIN_TOO_DEEP = 9;
MISSING_SIGNATURE_FOR_FEE = 10;
INVALID_ACCOUNT_INDEX = 11;
SIGNATURE_FAILURE = 12;
INVALID_PROGRAM_FOR_EXECUTION = 13;
SANITIZE_FAILURE = 14;
CLUSTER_MAINTENANCE = 15;
}
message InstructionError {
uint32 index = 1;
InstructionErrorType error = 2;
CustomError custom = 3;
}
enum InstructionErrorType {
GENERIC_ERROR = 0;
INVALID_ARGUMENT = 1;
INVALID_INSTRUCTION_DATA = 2;
INVALID_ACCOUNT_DATA = 3;
ACCOUNT_DATA_TOO_SMALL = 4;
INSUFFICIENT_FUNDS = 5;
INCORRECT_PROGRAM_ID = 6;
MISSING_REQUIRED_SIGNATURE = 7;
ACCOUNT_ALREADY_INITIALIZED = 8;
UNINITIALIZED_ACCOUNT = 9;
UNBALANCED_INSTRUCTION = 10;
MODIFIED_PROGRAM_ID = 11;
EXTERNAL_ACCOUNT_LAMPORT_SPEND = 12;
EXTERNAL_ACCOUNT_DATA_MODIFIED = 13;
READONLY_LAMPORT_CHANGE = 14;
READONLY_DATA_MODIFIED = 15;
DUPLICATE_ACCOUNT_INDEX = 16;
EXECUTABLE_MODIFIED = 17;
RENT_EPOCH_MODIFIED = 18;
NOT_ENOUGH_ACCOUNT_KEYS = 19;
ACCOUNT_DATA_SIZE_CHANGED = 20;
ACCOUNT_NOT_EXECUTABLE = 21;
ACCOUNT_BORROW_FAILED = 22;
ACCOUNT_BORROW_OUTSTANDING = 23;
DUPLICATE_ACCOUNT_OUT_OF_SYNC = 24;
CUSTOM = 25;
INVALID_ERROR = 26;
EXECUTABLE_DATA_MODIFIED = 27;
EXECUTABLE_LAMPORT_CHANGE = 28;
EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT = 29;
UNSUPPORTED_PROGRAM_ID = 30;
CALL_DEPTH = 31;
MISSING_ACCOUNT = 32;
REENTRANCY_NOT_ALLOWED = 33;
MAX_SEED_LENGTH_EXCEEDED = 34;
INVALID_SEEDS = 35;
INVALID_REALLOC = 36;
COMPUTATIONAL_BUDGET_EXCEEDED = 37;
PRIVILEGE_ESCALATION = 38;
PROGRAM_ENVIRONMENT_SETUP_FAILURE = 39;
PROGRAM_FAILED_TO_COMPLETE = 40;
PROGRAM_FAILED_TO_COMPILE = 41;
IMMUTABLE = 42;
INCORRECT_AUTHORITY = 43;
}
message UnixTimestamp {
int64 timestamp = 1;
}
message CustomError {
uint32 custom = 1;
}

View File

@ -324,6 +324,7 @@ pub struct ConfirmedTransactionStatusWithSignature {
pub slot: Slot,
pub err: Option<TransactionError>,
pub memo: Option<String>,
pub block_time: Option<UnixTimestamp>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -382,6 +383,7 @@ pub struct ConfirmedTransaction {
pub slot: Slot,
#[serde(flatten)]
pub transaction: TransactionWithStatusMeta,
pub block_time: Option<UnixTimestamp>,
}
impl ConfirmedTransaction {
@ -389,6 +391,7 @@ impl ConfirmedTransaction {
EncodedConfirmedTransaction {
slot: self.slot,
transaction: self.transaction.encode(encoding),
block_time: self.block_time,
}
}
}
@ -399,6 +402,7 @@ pub struct EncodedConfirmedTransaction {
pub slot: Slot,
#[serde(flatten)]
pub transaction: EncodedTransactionWithStatusMeta,
pub block_time: Option<UnixTimestamp>,
}
/// A duplicate representation of a Transaction for pretty JSON serialization
@ -575,6 +579,17 @@ impl EncodedTransaction {
}
}
// A serialized `Vec<TransactionByAddrInfo>` is stored in the `tx-by-addr` table. The row keys are
// the one's compliment of the slot so that rows may be listed in reverse order
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct TransactionByAddrInfo {
pub signature: Signature, // The transaction signature
pub err: Option<TransactionError>, // None if the transaction executed successfully
pub index: u32, // Where the transaction is located in the block
pub memo: Option<String>, // Transaction memo
pub block_time: Option<UnixTimestamp>,
}
#[cfg(test)]
mod test {
use super::*;