mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-14 20:19:51 +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
|
// TODO: Introduce type-safe wrapper around checked transaction, e.g. AuthenticatedTransaction
|
||||||
pub fn transaction_stateless_check(self) -> Result<Self, TransactionMalformationError> {
|
pub fn transaction_stateless_check(self) -> Result<Self, TransactionMalformationError> {
|
||||||
// Stateless checks here
|
// Stateless checks here
|
||||||
@ -170,3 +153,20 @@ pub enum TransactionMalformationError {
|
|||||||
#[error("Transaction size {size} exceeds maximum allowed size of {max} bytes")]
|
#[error("Transaction size {size} exceeds maximum allowed size of {max} bytes")]
|
||||||
TransactionTooLarge { size: usize, max: usize },
|
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 bedrock_client::HeaderId;
|
||||||
use common::{
|
use common::{
|
||||||
block::{BedrockStatus, Block},
|
block::{BedrockStatus, Block},
|
||||||
transaction::NSSATransaction,
|
transaction::{NSSATransaction, clock_invocation},
|
||||||
};
|
};
|
||||||
use nssa::{Account, AccountId, V03State};
|
use nssa::{Account, AccountId, V03State};
|
||||||
use nssa_core::BlockId;
|
use nssa_core::BlockId;
|
||||||
@ -129,7 +129,7 @@ impl IndexerStore {
|
|||||||
.ok_or_else(|| anyhow::anyhow!("Block has no transactions"))?;
|
.ok_or_else(|| anyhow::anyhow!("Block has no transactions"))?;
|
||||||
|
|
||||||
anyhow::ensure!(
|
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"
|
"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_id = u64::try_from(i).unwrap();
|
||||||
let block_timestamp = block_id.saturating_mul(100);
|
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(
|
let next_block = common::test_utils::produce_dummy_block(
|
||||||
block_id,
|
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.
|
/// The instruction type for the Clock Program. The sequencer passes the current block timestamp.
|
||||||
pub type Instruction = 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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||||
pub struct ClockAccountData {
|
pub struct ClockAccountData {
|
||||||
pub block_id: u64,
|
pub block_id: u64,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use common::PINATA_BASE58;
|
|||||||
use common::{
|
use common::{
|
||||||
HashType,
|
HashType,
|
||||||
block::{BedrockStatus, Block, HashableBlockData},
|
block::{BedrockStatus, Block, HashableBlockData},
|
||||||
transaction::NSSATransaction,
|
transaction::{NSSATransaction, clock_invocation},
|
||||||
};
|
};
|
||||||
use config::SequencerConfig;
|
use config::SequencerConfig;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
@ -205,20 +205,6 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
|||||||
while let Some(tx) = self.mempool.pop() {
|
while let Some(tx) = self.mempool.pop() {
|
||||||
let tx_hash = tx.hash();
|
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
|
// Check if block size exceeds limit
|
||||||
let temp_valid_transactions =
|
let temp_valid_transactions =
|
||||||
[valid_transactions.as_slice(), std::slice::from_ref(&tx)].concat();
|
[valid_transactions.as_slice(), std::slice::from_ref(&tx)].concat();
|
||||||
@ -244,6 +230,20 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
|||||||
break;
|
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);
|
self.state.apply_state_diff(validated_diff);
|
||||||
|
|
||||||
valid_transactions.push(tx);
|
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.
|
// Append the Clock Program invocation as the mandatory last transaction.
|
||||||
let clock_nssa_tx = NSSATransaction::clock_invocation(new_block_timestamp);
|
let clock_tx = clock_invocation(new_block_timestamp);
|
||||||
self.state
|
self.state
|
||||||
.transition_from_public_transaction(
|
.transition_from_public_transaction(&clock_tx, new_block_height, new_block_timestamp)
|
||||||
match &clock_nssa_tx {
|
|
||||||
NSSATransaction::Public(tx) => tx,
|
|
||||||
NSSATransaction::PrivacyPreserving(_)
|
|
||||||
| NSSATransaction::ProgramDeployment(_) => {
|
|
||||||
unreachable!("clock_invocation always returns Public")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new_block_height,
|
|
||||||
new_block_timestamp,
|
|
||||||
)
|
|
||||||
.context("Clock transaction failed. Aborting block production.")?;
|
.context("Clock transaction failed. Aborting block production.")?;
|
||||||
valid_transactions.push(clock_nssa_tx);
|
valid_transactions.push(NSSATransaction::Public(clock_tx));
|
||||||
|
|
||||||
let hashable_data = HashableBlockData {
|
let hashable_data = HashableBlockData {
|
||||||
block_id: new_block_height,
|
block_id: new_block_height,
|
||||||
@ -393,7 +383,10 @@ mod tests {
|
|||||||
use std::{pin::pin, time::Duration};
|
use std::{pin::pin, time::Duration};
|
||||||
|
|
||||||
use bedrock_client::BackoffConfig;
|
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 logos_blockchain_core::mantle::ops::channel::ChannelId;
|
||||||
use mempool::MemPoolHandle;
|
use mempool::MemPoolHandle;
|
||||||
use testnet_initial_state::{initial_accounts, initial_pub_accounts_private_keys};
|
use testnet_initial_state::{initial_accounts, initial_pub_accounts_private_keys};
|
||||||
@ -656,7 +649,7 @@ mod tests {
|
|||||||
block.body.transactions,
|
block.body.transactions,
|
||||||
vec![
|
vec![
|
||||||
tx.clone(),
|
tx.clone(),
|
||||||
NSSATransaction::clock_invocation(block.header.timestamp)
|
NSSATransaction::Public(clock_invocation(block.header.timestamp))
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -688,7 +681,7 @@ mod tests {
|
|||||||
block.body.transactions,
|
block.body.transactions,
|
||||||
vec![
|
vec![
|
||||||
tx.clone(),
|
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.
|
// The replay is rejected, so only the clock tx is in the block.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
block.body.transactions,
|
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,
|
block.body.transactions,
|
||||||
vec![
|
vec![
|
||||||
tx.clone(),
|
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,
|
new_block.body.transactions,
|
||||||
vec![
|
vec![
|
||||||
tx,
|
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"
|
"New block should contain the submitted transaction and the clock invocation"
|
||||||
);
|
);
|
||||||
@ -905,7 +900,7 @@ mod tests {
|
|||||||
))
|
))
|
||||||
};
|
};
|
||||||
mempool_handle
|
mempool_handle
|
||||||
.push(NSSATransaction::clock_invocation(0))
|
.push(NSSATransaction::Public(clock_invocation(0)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
mempool_handle.push(crafted_clock_tx).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.
|
// Both transactions were dropped. Only the system-appended clock tx remains.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
block.body.transactions,
|
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.
|
// The user tx must have been dropped; only the mandatory clock invocation remains.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
block.body.transactions,
|
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):
|
//! Expected pre-states (in order):
|
||||||
//! 0 - pinata account (authorized, owned by this program)
|
//! 0 - pinata account (authorized, owned by this program)
|
||||||
//! 1 - winner account
|
//! 1 - winner account
|
||||||
//! 2 - clock account (read-only, e.g. `CLOCK_01`).
|
//! 2 - clock account `CLOCK_01`.
|
||||||
//!
|
//!
|
||||||
//! Pinata account data layout (24 bytes):
|
//! Pinata account data layout (24 bytes):
|
||||||
//! [prize: u64 LE | `cooldown_ms`: u64 LE | `last_claim_timestamp`: u64 LE].
|
//! [prize: u64 LE | `cooldown_ms`: u64 LE | `last_claim_timestamp`: u64 LE].
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user