mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-04-08 20:23:47 +00:00
fix docs. refactor sequencer logic to check size before executing
This commit is contained in:
parent
ed1926b38a
commit
d9ddd5e3f6
@ -44,23 +44,6 @@ impl NSSATransaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the canonical Clock Program invocation transaction for the given block
|
||||
/// timestamp. Every valid block must end with exactly one occurrence of this transaction.
|
||||
#[must_use]
|
||||
pub fn clock_invocation(timestamp: clock_core::Instruction) -> Self {
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
nssa::program::Program::clock().id(),
|
||||
clock_core::CLOCK_PROGRAM_ACCOUNT_IDS.to_vec(),
|
||||
vec![],
|
||||
timestamp,
|
||||
)
|
||||
.expect("Clock invocation message should always be constructable");
|
||||
Self::Public(nssa::PublicTransaction::new(
|
||||
message,
|
||||
nssa::public_transaction::WitnessSet::from_raw_parts(vec![]),
|
||||
))
|
||||
}
|
||||
|
||||
// TODO: Introduce type-safe wrapper around checked transaction, e.g. AuthenticatedTransaction
|
||||
pub fn transaction_stateless_check(self) -> Result<Self, TransactionMalformationError> {
|
||||
// Stateless checks here
|
||||
@ -170,3 +153,20 @@ pub enum TransactionMalformationError {
|
||||
#[error("Transaction size {size} exceeds maximum allowed size of {max} bytes")]
|
||||
TransactionTooLarge { size: usize, max: usize },
|
||||
}
|
||||
|
||||
/// Returns the canonical Clock Program invocation transaction for the given block timestamp.
|
||||
/// Every valid block must end with exactly one occurrence of this transaction.
|
||||
#[must_use]
|
||||
pub fn clock_invocation(timestamp: clock_core::Instruction) -> nssa::PublicTransaction {
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
nssa::program::Program::clock().id(),
|
||||
clock_core::CLOCK_PROGRAM_ACCOUNT_IDS.to_vec(),
|
||||
vec![],
|
||||
timestamp,
|
||||
)
|
||||
.expect("Clock invocation message should always be constructable");
|
||||
nssa::PublicTransaction::new(
|
||||
message,
|
||||
nssa::public_transaction::WitnessSet::from_raw_parts(vec![]),
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||
use bedrock_client::HeaderId;
|
||||
use common::{
|
||||
block::{BedrockStatus, Block},
|
||||
transaction::NSSATransaction,
|
||||
transaction::{NSSATransaction, clock_invocation},
|
||||
};
|
||||
use nssa::{Account, AccountId, V03State};
|
||||
use nssa_core::BlockId;
|
||||
@ -129,7 +129,7 @@ impl IndexerStore {
|
||||
.ok_or_else(|| anyhow::anyhow!("Block has no transactions"))?;
|
||||
|
||||
anyhow::ensure!(
|
||||
*clock_tx == NSSATransaction::clock_invocation(block.header.timestamp),
|
||||
*clock_tx == NSSATransaction::Public(clock_invocation(block.header.timestamp)),
|
||||
"Last transaction in block must be the clock invocation for the block timestamp"
|
||||
);
|
||||
|
||||
@ -236,7 +236,7 @@ mod tests {
|
||||
);
|
||||
let block_id = u64::try_from(i).unwrap();
|
||||
let block_timestamp = block_id.saturating_mul(100);
|
||||
let clock_tx = NSSATransaction::clock_invocation(block_timestamp);
|
||||
let clock_tx = NSSATransaction::Public(clock_invocation(block_timestamp));
|
||||
|
||||
let next_block = common::test_utils::produce_dummy_block(
|
||||
block_id,
|
||||
|
||||
@ -22,7 +22,7 @@ pub const CLOCK_PROGRAM_ACCOUNT_IDS: [AccountId; 3] = [
|
||||
/// The instruction type for the Clock Program. The sequencer passes the current block timestamp.
|
||||
pub type Instruction = Timestamp;
|
||||
|
||||
/// The data stored in a clock account: `[block_id: u64 LE | timestamp: u64 LE]`.
|
||||
/// The data stored in a clock account.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ClockAccountData {
|
||||
pub block_id: u64,
|
||||
|
||||
@ -7,7 +7,7 @@ use common::PINATA_BASE58;
|
||||
use common::{
|
||||
HashType,
|
||||
block::{BedrockStatus, Block, HashableBlockData},
|
||||
transaction::NSSATransaction,
|
||||
transaction::{NSSATransaction, clock_invocation},
|
||||
};
|
||||
use config::SequencerConfig;
|
||||
use log::{error, info, warn};
|
||||
@ -205,20 +205,6 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
while let Some(tx) = self.mempool.pop() {
|
||||
let tx_hash = tx.hash();
|
||||
|
||||
let validated_diff = match tx.validate_on_state(
|
||||
&self.state,
|
||||
new_block_height,
|
||||
new_block_timestamp,
|
||||
) {
|
||||
Ok(diff) => diff,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Transaction with hash {tx_hash} failed execution check with error: {err:#?}, skipping it",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Check if block size exceeds limit
|
||||
let temp_valid_transactions =
|
||||
[valid_transactions.as_slice(), std::slice::from_ref(&tx)].concat();
|
||||
@ -244,6 +230,20 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
break;
|
||||
}
|
||||
|
||||
let validated_diff = match tx.validate_on_state(
|
||||
&self.state,
|
||||
new_block_height,
|
||||
new_block_timestamp,
|
||||
) {
|
||||
Ok(diff) => diff,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Transaction with hash {tx_hash} failed execution check with error: {err:#?}, skipping it",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
self.state.apply_state_diff(validated_diff);
|
||||
|
||||
valid_transactions.push(tx);
|
||||
@ -253,22 +253,12 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
}
|
||||
}
|
||||
|
||||
// Append the Block Context Program invocation as the mandatory last transaction.
|
||||
let clock_nssa_tx = NSSATransaction::clock_invocation(new_block_timestamp);
|
||||
// Append the Clock Program invocation as the mandatory last transaction.
|
||||
let clock_tx = clock_invocation(new_block_timestamp);
|
||||
self.state
|
||||
.transition_from_public_transaction(
|
||||
match &clock_nssa_tx {
|
||||
NSSATransaction::Public(tx) => tx,
|
||||
NSSATransaction::PrivacyPreserving(_)
|
||||
| NSSATransaction::ProgramDeployment(_) => {
|
||||
unreachable!("clock_invocation always returns Public")
|
||||
}
|
||||
},
|
||||
new_block_height,
|
||||
new_block_timestamp,
|
||||
)
|
||||
.transition_from_public_transaction(&clock_tx, new_block_height, new_block_timestamp)
|
||||
.context("Clock transaction failed. Aborting block production.")?;
|
||||
valid_transactions.push(clock_nssa_tx);
|
||||
valid_transactions.push(NSSATransaction::Public(clock_tx));
|
||||
|
||||
let hashable_data = HashableBlockData {
|
||||
block_id: new_block_height,
|
||||
@ -393,7 +383,10 @@ mod tests {
|
||||
use std::{pin::pin, time::Duration};
|
||||
|
||||
use bedrock_client::BackoffConfig;
|
||||
use common::{test_utils::sequencer_sign_key_for_testing, transaction::NSSATransaction};
|
||||
use common::{
|
||||
test_utils::sequencer_sign_key_for_testing,
|
||||
transaction::{NSSATransaction, clock_invocation},
|
||||
};
|
||||
use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
||||
use mempool::MemPoolHandle;
|
||||
use testnet_initial_state::{initial_accounts, initial_pub_accounts_private_keys};
|
||||
@ -656,7 +649,7 @@ mod tests {
|
||||
block.body.transactions,
|
||||
vec![
|
||||
tx.clone(),
|
||||
NSSATransaction::clock_invocation(block.header.timestamp)
|
||||
NSSATransaction::Public(clock_invocation(block.header.timestamp))
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -688,7 +681,7 @@ mod tests {
|
||||
block.body.transactions,
|
||||
vec![
|
||||
tx.clone(),
|
||||
NSSATransaction::clock_invocation(block.header.timestamp)
|
||||
NSSATransaction::Public(clock_invocation(block.header.timestamp))
|
||||
]
|
||||
);
|
||||
|
||||
@ -705,7 +698,9 @@ mod tests {
|
||||
// The replay is rejected, so only the clock tx is in the block.
|
||||
assert_eq!(
|
||||
block.body.transactions,
|
||||
vec![NSSATransaction::clock_invocation(block.header.timestamp)]
|
||||
vec![NSSATransaction::Public(clock_invocation(
|
||||
block.header.timestamp
|
||||
))]
|
||||
);
|
||||
}
|
||||
|
||||
@ -745,7 +740,7 @@ mod tests {
|
||||
block.body.transactions,
|
||||
vec![
|
||||
tx.clone(),
|
||||
NSSATransaction::clock_invocation(block.header.timestamp)
|
||||
NSSATransaction::Public(clock_invocation(block.header.timestamp))
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -879,7 +874,7 @@ mod tests {
|
||||
new_block.body.transactions,
|
||||
vec![
|
||||
tx,
|
||||
NSSATransaction::clock_invocation(new_block.header.timestamp)
|
||||
NSSATransaction::Public(clock_invocation(new_block.header.timestamp))
|
||||
],
|
||||
"New block should contain the submitted transaction and the clock invocation"
|
||||
);
|
||||
@ -905,7 +900,7 @@ mod tests {
|
||||
))
|
||||
};
|
||||
mempool_handle
|
||||
.push(NSSATransaction::clock_invocation(0))
|
||||
.push(NSSATransaction::Public(clock_invocation(0)))
|
||||
.await
|
||||
.unwrap();
|
||||
mempool_handle.push(crafted_clock_tx).await.unwrap();
|
||||
@ -922,7 +917,9 @@ mod tests {
|
||||
// Both transactions were dropped. Only the system-appended clock tx remains.
|
||||
assert_eq!(
|
||||
block.body.transactions,
|
||||
vec![NSSATransaction::clock_invocation(block.header.timestamp)]
|
||||
vec![NSSATransaction::Public(clock_invocation(
|
||||
block.header.timestamp
|
||||
))]
|
||||
);
|
||||
}
|
||||
|
||||
@ -1027,7 +1024,9 @@ mod tests {
|
||||
// The user tx must have been dropped; only the mandatory clock invocation remains.
|
||||
assert_eq!(
|
||||
block.body.transactions,
|
||||
vec![NSSATransaction::clock_invocation(block.header.timestamp)]
|
||||
vec![NSSATransaction::Public(clock_invocation(
|
||||
block.header.timestamp
|
||||
))]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//! Expected pre-states (in order):
|
||||
//! 0 - pinata account (authorized, owned by this program)
|
||||
//! 1 - winner account
|
||||
//! 2 - clock account (read-only, e.g. `CLOCK_01`).
|
||||
//! 2 - clock account `CLOCK_01`.
|
||||
//!
|
||||
//! Pinata account data layout (24 bytes):
|
||||
//! [prize: u64 LE | `cooldown_ms`: u64 LE | `last_claim_timestamp`: u64 LE].
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user