diff --git a/Cargo.lock b/Cargo.lock index 9c96ea7601..1bdddde0fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5017,6 +5017,7 @@ name = "solana-storage-proto" version = "1.6.0" dependencies = [ "bincode", + "bs58", "prost", "serde", "serde_derive", diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index 1dc8ec8994..42d85b7bb7 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -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, pub memo: Option, + pub block_time: Option, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -304,12 +305,14 @@ impl From for RpcConfirmedTransactionSt slot, err, memo, + block_time, } = value; Self { signature: signature.to_string(), slot, err, memo, + block_time, } } } diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index d28fa2068c..2e1f915e5e 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -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 + }) ); } diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index b5289e9d4b..f808bd0161 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -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 for TransactionStatus { } } -// A serialized `Vec` 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, // None if the transaction executed successfully - index: u32, // Where the transaction is located in the block - memo: Option, // Transaction memo +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct LegacyTransactionByAddrInfo { + pub signature: Signature, // The transaction signature + pub err: Option, // None if the transaction executed successfully + pub index: u32, // Where the transaction is located in the block + pub memo: Option, // Transaction memo +} + +impl From 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::>( + .get_protobuf_or_bincode_cell::, 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 = - bigtable::deserialize_bincode_cell_data(&data, "tx-by-addr", row_key)?; + + let deserialized_cell_data = bigtable::deserialize_protobuf_or_bincode_cell_data::< + Vec, + tx_by_addr::TransactionByAddr, + >(&data, "tx-by-addr", row_key.clone())?; + + let mut cell_data: Vec = 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::>( + .put_protobuf_cells_with_retry::( "tx-by-addr", &tx_by_addr_cells, ) diff --git a/storage-proto/Cargo.toml b/storage-proto/Cargo.toml index 1c0ec33ffa..2ed3992f95 100644 --- a/storage-proto/Cargo.toml +++ b/storage-proto/Cargo.toml @@ -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" diff --git a/storage-proto/build-proto/src/main.rs b/storage-proto/build-proto/src/main.rs index ccc3cf677c..50acaceb7a 100644 --- a/storage-proto/build-proto/src/main.rs +++ b/storage-proto/build-proto/src/main.rs @@ -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], + ) } diff --git a/storage-proto/proto/solana.storage.transaction_by_addr.rs b/storage-proto/proto/solana.storage.transaction_by_addr.rs new file mode 100644 index 0000000000..b8293b2251 --- /dev/null +++ b/storage-proto/proto/solana.storage.transaction_by_addr.rs @@ -0,0 +1,117 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionByAddr { + #[prost(message, repeated, tag = "1")] + pub tx_by_addrs: ::std::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionByAddrInfo { + #[prost(bytes, tag = "1")] + pub signature: std::vec::Vec, + #[prost(message, optional, tag = "2")] + pub err: ::std::option::Option, + #[prost(uint32, tag = "3")] + pub index: u32, + #[prost(message, optional, tag = "4")] + pub memo: ::std::option::Option, + #[prost(message, optional, tag = "5")] + pub block_time: ::std::option::Option, +} +#[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, +} +#[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, +} +#[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, +} diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 4e1cc6fde6..3f68d09382 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -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> for generated::Rewards { fn from(rewards: Vec) -> Self { Self { @@ -417,6 +426,356 @@ impl From for CompiledInstruction { } } +impl TryFrom for TransactionError { + type Error = &'static str; + + fn try_from(transaction_error: tx_by_addr::TransactionError) -> Result { + 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 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 for tx_by_addr::TransactionByAddrInfo { + fn from(by_addr: TransactionByAddrInfo) -> Self { + let TransactionByAddrInfo { + signature, + err, + index, + memo, + block_time, + } = by_addr; + + Self { + signature: >::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 for TransactionByAddrInfo { + type Error = &'static str; + + fn try_from( + transaction_by_addr: tx_by_addr::TransactionByAddrInfo, + ) -> Result { + 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 for Vec { + type Error = &'static str; + + fn try_from(collection: tx_by_addr::TransactionByAddr) -> Result { + Ok(collection + .tx_by_addrs + .into_iter() + .map(|tx_by_addr| tx_by_addr.try_into()) + .collect::, 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() + ); + } } diff --git a/storage-proto/src/transaction_by_addr.proto b/storage-proto/src/transaction_by_addr.proto new file mode 100644 index 0000000000..63ffccecf7 --- /dev/null +++ b/storage-proto/src/transaction_by_addr.proto @@ -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; +} diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index f74bff5130..464fc104d8 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -324,6 +324,7 @@ pub struct ConfirmedTransactionStatusWithSignature { pub slot: Slot, pub err: Option, pub memo: Option, + pub block_time: Option, } #[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, } 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, } /// A duplicate representation of a Transaction for pretty JSON serialization @@ -575,6 +579,17 @@ impl EncodedTransaction { } } +// A serialized `Vec` 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, // None if the transaction executed successfully + pub index: u32, // Where the transaction is located in the block + pub memo: Option, // Transaction memo + pub block_time: Option, +} + #[cfg(test)] mod test { use super::*;