Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions engine/src/accessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ use solana_pubkey::Pubkey;
use solana_transaction::TransactionResult;
use tokio::time;

use crate::{Engine, error::EngineError, error::Result, transaction};
use crate::{
Engine, IntoTransactionView,
error::{EngineError, Result},
transaction,
};

/// Upper bound on awaiting a submitted transaction's committed result.
const EXECUTION_TIMEOUT: Duration = Duration::from_secs(8);
Expand Down Expand Up @@ -63,7 +67,14 @@ impl AccountAccessor<'_> {
}
}

impl TransactionAccessor<'_> {
impl<'a> TransactionAccessor<'a> {
/// Composes a trusted local-ledger transaction without verifying its signatures.
pub(super) fn replay(engine: &'a Engine, transaction: Vec<u8>) -> Result<Self> {
let sanitized = TransactionView::try_new_sanitized(transaction.into(), true)?;
let transaction = sanitized.compose(engine)?;
Ok(Self { engine, transaction })
}

/// Submits `transaction` for execution and awaits its committed result.
/// A timeout does not cancel the submitted transaction.
pub async fn execute(self) -> Result<TransactionResult<()>> {
Expand Down
8 changes: 7 additions & 1 deletion engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ impl Engine {
T: IntoTransactionView,
{
let transaction = transaction.compose(self)?;
transaction::sigverify(&transaction)?;
Ok(TransactionAccessor { engine: self, transaction })
}

Expand Down Expand Up @@ -190,7 +191,12 @@ impl Engine {
terminating: Default::default(),
};
while let Some(entry) = replayer.rx.recv().await {
engine.replay(entry).await?;
match entry {
OwnedBlockstoreEntry::Transaction(transaction) => {
TransactionAccessor::replay(&engine, transaction)?.schedule().await?;
}
entry => engine.replay(entry).await?,
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
replayer
.response
Expand Down
10 changes: 3 additions & 7 deletions engine/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,15 @@ impl IntoTransactionView for TransactionView {
{
return Err(EngineError::SignatureVerification);
}
sigverify(&self)?;
Ok(self)
}
}

/// The engine's sole signature-verification point.
///
/// Execution is trustless: every submission funnels through the
/// [`TransactionView`] `compose` and is verified here, including replay and
/// replication of already-committed transactions. No path reaches the
/// sequencer unverified, so downstream code may assume the fee payer and every
/// required signer actually signed.
fn sigverify(view: &TransactionView) -> Result<()> {
/// Every public transaction accessor verifies here; trusted local replay is
/// the only bypass. TODO: Remove the bypass before replaying untrusted ledgers.
pub(super) fn sigverify(view: &TransactionView) -> Result<()> {
// Sanitization guarantees one static key for every required signature.
let message = view.message_data();
for (signature, key) in view.signatures().iter().zip(view.static_account_keys()) {
Expand Down
Loading