Skip to content

Commit

Permalink
Generic Transaction Trait (#1918)
Browse files Browse the repository at this point in the history
  • Loading branch information
apfitzge authored Jul 12, 2024
1 parent 2961b9b commit a191a77
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ members = [
"streamer",
"svm",
"svm-conformance",
"svm-transaction",
"test-validator",
"thin-client",
"timings",
Expand Down Expand Up @@ -403,6 +404,7 @@ solana-storage-proto = { path = "storage-proto", version = "=2.1.0" }
solana-streamer = { path = "streamer", version = "=2.1.0" }
solana-svm = { path = "svm", version = "=2.1.0" }
solana-svm-conformance = { path = "svm-conformance", version = "=2.1.0" }
solana-svm-transaction = { path = "svm-transaction", version = "=2.1.0" }
solana-system-program = { path = "programs/system", version = "=2.1.0" }
solana-test-validator = { path = "test-validator", version = "=2.1.0" }
solana-thin-client = { path = "thin-client", version = "=2.1.0" }
Expand Down
13 changes: 13 additions & 0 deletions svm-transaction/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "solana-svm-transaction"
description = "Solana SVM Transaction"
documentation = "https://docs.rs/solana-svm-transaction"
version = { workspace = true }
authors = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
edition = { workspace = true }

[dependencies]
solana-sdk = { workspace = true }
24 changes: 24 additions & 0 deletions svm-transaction/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use solana_sdk::instruction::CompiledInstruction;

/// A non-owning version of [`CompiledInstruction`] that references
/// slices of account indexes and data.
// `program_id_index` is still owned, as it is a simple u8.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SVMInstruction<'a> {
/// Index into the transaction keys array indicating the program account that executes this instruction.
pub program_id_index: u8,
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program.
pub accounts: &'a [u8],
/// The program input data.
pub data: &'a [u8],
}

impl<'a> From<&'a CompiledInstruction> for SVMInstruction<'a> {
fn from(ix: &'a CompiledInstruction) -> Self {
Self {
program_id_index: ix.program_id_index,
accounts: ix.accounts.as_slice(),
data: ix.data.as_slice(),
}
}
}
4 changes: 4 additions & 0 deletions svm-transaction/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod instruction;
pub mod message_address_table_lookup;
pub mod svm_message;
pub mod svm_transaction;
23 changes: 23 additions & 0 deletions svm-transaction/src/message_address_table_lookup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use solana_sdk::{message::v0, pubkey::Pubkey};

/// A non-owning version of [`v0::MessageAddressTableLookup`].
/// This simply references the data in the original message.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SVMMessageAddressTableLookup<'a> {
/// Address lookup table account key
pub account_key: &'a Pubkey,
/// List of indexes used to load writable account addresses
pub writable_indexes: &'a [u8],
/// List of indexes used to load readonly account addresses
pub readonly_indexes: &'a [u8],
}

impl<'a> From<&'a v0::MessageAddressTableLookup> for SVMMessageAddressTableLookup<'a> {
fn from(lookup: &'a v0::MessageAddressTableLookup) -> Self {
Self {
account_key: &lookup.account_key,
writable_indexes: &lookup.writable_indexes,
readonly_indexes: &lookup.readonly_indexes,
}
}
}
66 changes: 66 additions & 0 deletions svm-transaction/src/svm_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use {
crate::{
instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup,
},
core::fmt::Debug,
solana_sdk::{hash::Hash, message::AccountKeys, pubkey::Pubkey},
};

mod sanitized_message;
mod sanitized_transaction;

// - Debug to support legacy logging
pub trait SVMMessage: Debug {
/// Return the number of signatures in the message.
fn num_signatures(&self) -> u64;

/// Returns the number of requested write-locks in this message.
/// This does not consider if write-locks are demoted.
fn num_write_locks(&self) -> u64;

/// Return the recent blockhash.
fn recent_blockhash(&self) -> &Hash;

/// Return the number of instructions in the message.
fn num_instructions(&self) -> usize;

/// Return an iterator over the instructions in the message.
fn instructions_iter(&self) -> impl Iterator<Item = SVMInstruction>;

/// Return an iterator over the instructions in the message, paired with
/// the pubkey of the program.
fn program_instructions_iter(&self) -> impl Iterator<Item = (&Pubkey, SVMInstruction)>;

/// Return the account keys.
fn account_keys(&self) -> AccountKeys;

/// Return the fee-payer
fn fee_payer(&self) -> &Pubkey;

/// Returns `true` if the account at `index` is writable.
fn is_writable(&self, index: usize) -> bool;

/// Returns `true` if the account at `index` is signer.
fn is_signer(&self, index: usize) -> bool;

/// Returns true if the account at the specified index is invoked as a
/// program in top-level instructions of this message.
fn is_invoked(&self, key_index: usize) -> bool;

/// Returns true if the account at the specified index is an input to some
/// program instruction in this message.
fn is_instruction_account(&self, key_index: usize) -> bool {
if let Ok(key_index) = u8::try_from(key_index) {
self.instructions_iter()
.any(|ix| ix.accounts.contains(&key_index))
} else {
false
}
}

/// Get the number of lookup tables.
fn num_lookup_tables(&self) -> usize;

/// Get message address table lookups used in the message
fn message_address_table_lookups(&self) -> impl Iterator<Item = SVMMessageAddressTableLookup>;
}
71 changes: 71 additions & 0 deletions svm-transaction/src/svm_message/sanitized_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use {
crate::{
instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup,
svm_message::SVMMessage,
},
solana_sdk::{
hash::Hash,
message::{AccountKeys, SanitizedMessage},
pubkey::Pubkey,
},
};

// Implement for the "reference" `SanitizedMessage` type.
impl SVMMessage for SanitizedMessage {
fn num_signatures(&self) -> u64 {
SanitizedMessage::num_signatures(self)
}

fn num_write_locks(&self) -> u64 {
SanitizedMessage::num_write_locks(self)
}

fn recent_blockhash(&self) -> &Hash {
SanitizedMessage::recent_blockhash(self)
}

fn num_instructions(&self) -> usize {
SanitizedMessage::instructions(self).len()
}

fn instructions_iter(&self) -> impl Iterator<Item = SVMInstruction> {
SanitizedMessage::instructions(self)
.iter()
.map(SVMInstruction::from)
}

fn program_instructions_iter(&self) -> impl Iterator<Item = (&Pubkey, SVMInstruction)> {
SanitizedMessage::program_instructions_iter(self)
.map(|(pubkey, ix)| (pubkey, SVMInstruction::from(ix)))
}

fn account_keys(&self) -> AccountKeys {
SanitizedMessage::account_keys(self)
}

fn fee_payer(&self) -> &Pubkey {
SanitizedMessage::fee_payer(self)
}

fn is_writable(&self, index: usize) -> bool {
SanitizedMessage::is_writable(self, index)
}

fn is_signer(&self, index: usize) -> bool {
SanitizedMessage::is_signer(self, index)
}

fn is_invoked(&self, key_index: usize) -> bool {
SanitizedMessage::is_invoked(self, key_index)
}

fn num_lookup_tables(&self) -> usize {
SanitizedMessage::message_address_table_lookups(self).len()
}

fn message_address_table_lookups(&self) -> impl Iterator<Item = SVMMessageAddressTableLookup> {
SanitizedMessage::message_address_table_lookups(self)
.iter()
.map(SVMMessageAddressTableLookup::from)
}
}
63 changes: 63 additions & 0 deletions svm-transaction/src/svm_message/sanitized_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use {
crate::{
instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup,
svm_message::SVMMessage,
},
solana_sdk::{
hash::Hash, message::AccountKeys, pubkey::Pubkey, transaction::SanitizedTransaction,
},
};

impl SVMMessage for SanitizedTransaction {
fn num_signatures(&self) -> u64 {
SVMMessage::num_signatures(SanitizedTransaction::message(self))
}

fn num_write_locks(&self) -> u64 {
SVMMessage::num_write_locks(SanitizedTransaction::message(self))
}

fn recent_blockhash(&self) -> &Hash {
SVMMessage::recent_blockhash(SanitizedTransaction::message(self))
}

fn num_instructions(&self) -> usize {
SVMMessage::num_instructions(SanitizedTransaction::message(self))
}

fn instructions_iter(&self) -> impl Iterator<Item = SVMInstruction> {
SVMMessage::instructions_iter(SanitizedTransaction::message(self))
}

fn program_instructions_iter(&self) -> impl Iterator<Item = (&Pubkey, SVMInstruction)> {
SVMMessage::program_instructions_iter(SanitizedTransaction::message(self))
}

fn account_keys(&self) -> AccountKeys {
SVMMessage::account_keys(SanitizedTransaction::message(self))
}

fn fee_payer(&self) -> &Pubkey {
SVMMessage::fee_payer(SanitizedTransaction::message(self))
}

fn is_writable(&self, index: usize) -> bool {
SVMMessage::is_writable(SanitizedTransaction::message(self), index)
}

fn is_signer(&self, index: usize) -> bool {
SVMMessage::is_signer(SanitizedTransaction::message(self), index)
}

fn is_invoked(&self, key_index: usize) -> bool {
SVMMessage::is_invoked(SanitizedTransaction::message(self), key_index)
}

fn num_lookup_tables(&self) -> usize {
SVMMessage::num_lookup_tables(SanitizedTransaction::message(self))
}

fn message_address_table_lookups(&self) -> impl Iterator<Item = SVMMessageAddressTableLookup> {
SVMMessage::message_address_table_lookups(SanitizedTransaction::message(self))
}
}
11 changes: 11 additions & 0 deletions svm-transaction/src/svm_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use {crate::svm_message::SVMMessage, solana_sdk::signature::Signature};

mod sanitized_transaction;

pub trait SVMTransaction: SVMMessage {
/// Get the first signature of the message.
fn signature(&self) -> &Signature;

/// Get all the signatures of the message.
fn signatures(&self) -> &[Signature];
}
14 changes: 14 additions & 0 deletions svm-transaction/src/svm_transaction/sanitized_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use {
crate::svm_transaction::SVMTransaction,
solana_sdk::{signature::Signature, transaction::SanitizedTransaction},
};

impl SVMTransaction for SanitizedTransaction {
fn signature(&self) -> &Signature {
SanitizedTransaction::signature(self)
}

fn signatures(&self) -> &[Signature] {
SanitizedTransaction::signatures(self)
}
}

0 comments on commit a191a77

Please sign in to comment.