mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-03-25 19:53:07 +00:00
feat: extend ValidityWindow with Unix timestamp bounds
This commit is contained in:
parent
414abe32ba
commit
0043f29dc2
@ -4,6 +4,7 @@ use nssa::{AccountId, V03State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{HashType, block::BlockId};
|
||||
use nssa_core::program::Timestamp;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub enum NSSATransaction {
|
||||
@ -69,11 +70,14 @@ impl NSSATransaction {
|
||||
self,
|
||||
state: &mut V03State,
|
||||
block_id: BlockId,
|
||||
timestamp_ms: Timestamp,
|
||||
) -> Result<Self, nssa::error::NssaError> {
|
||||
match &self {
|
||||
Self::Public(tx) => state.transition_from_public_transaction(tx, block_id),
|
||||
Self::Public(tx) => {
|
||||
state.transition_from_public_transaction(tx, block_id, timestamp_ms)
|
||||
}
|
||||
Self::PrivacyPreserving(tx) => {
|
||||
state.transition_from_privacy_preserving_transaction(tx, block_id)
|
||||
state.transition_from_privacy_preserving_transaction(tx, block_id, timestamp_ms)
|
||||
}
|
||||
Self::ProgramDeployment(tx) => state.transition_from_program_deployment_transaction(tx),
|
||||
}
|
||||
|
||||
@ -183,11 +183,24 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
signatures_and_public_keys: _,
|
||||
proof,
|
||||
} = witness_set;
|
||||
let validity_window_formatted = match validity_window.0 {
|
||||
(Some(start), Some(end)) => format!("from {start} to {end}"),
|
||||
(Some(start), None) => format!("from {start}"),
|
||||
(None, Some(end)) => format!("until {end}"),
|
||||
(None, None) => "unbounded".to_owned(),
|
||||
let (block_from, block_to, ts_from, ts_to) = validity_window.0;
|
||||
let block_part = match (block_from, block_to) {
|
||||
(Some(start), Some(end)) => format!("block {start}..{end}"),
|
||||
(Some(start), None) => format!("block {start}.."),
|
||||
(None, Some(end)) => format!("block ..{end}"),
|
||||
(None, None) => String::new(),
|
||||
};
|
||||
let ts_part = match (ts_from, ts_to) {
|
||||
(Some(start), Some(end)) => format!("ts {start}..{end}"),
|
||||
(Some(start), None) => format!("ts {start}.."),
|
||||
(None, Some(end)) => format!("ts ..{end}"),
|
||||
(None, None) => String::new(),
|
||||
};
|
||||
let validity_window_formatted = match (block_part.is_empty(), ts_part.is_empty()) {
|
||||
(true, true) => "unbounded".to_owned(),
|
||||
(false, true) => block_part,
|
||||
(true, false) => ts_part,
|
||||
(false, false) => format!("{block_part}, {ts_part}"),
|
||||
};
|
||||
|
||||
let proof_len = proof.map_or(0, |p| p.0.len());
|
||||
|
||||
@ -125,7 +125,7 @@ impl IndexerStore {
|
||||
transaction
|
||||
.clone()
|
||||
.transaction_stateless_check()?
|
||||
.execute_check_on_state(&mut state_guard, block.header.block_id)?;
|
||||
.execute_check_on_state(&mut state_guard, block.header.block_id, block.header.timestamp)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -302,7 +302,12 @@ impl From<nssa::privacy_preserving_transaction::message::Message> for PrivacyPre
|
||||
.into_iter()
|
||||
.map(|(n, d)| (n.into(), d.into()))
|
||||
.collect(),
|
||||
validity_window: ValidityWindow((validity_window.from(), validity_window.to())),
|
||||
validity_window: ValidityWindow((
|
||||
validity_window.from(),
|
||||
validity_window.to(),
|
||||
validity_window.from_timestamp(),
|
||||
validity_window.to_timestamp(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,7 +302,9 @@ pub struct Nullifier(
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ValidityWindow(pub (Option<BlockId>, Option<BlockId>));
|
||||
pub struct ValidityWindow(
|
||||
pub (Option<BlockId>, Option<BlockId>, Option<TimeStamp>, Option<TimeStamp>),
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CommitmentSetDigest(
|
||||
|
||||
@ -124,7 +124,7 @@ impl MockIndexerService {
|
||||
indexer_service_protocol::Nullifier([tx_idx as u8; 32]),
|
||||
CommitmentSetDigest([0xff; 32]),
|
||||
)],
|
||||
validity_window: ValidityWindow((None, None)),
|
||||
validity_window: ValidityWindow((None, None, None, None)),
|
||||
},
|
||||
witness_set: WitnessSet {
|
||||
signatures_and_public_keys: vec![],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[cfg(any(feature = "host", test))]
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use risc0_zkvm::{DeserializeOwned, guest::env, serde::Deserializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -154,6 +154,8 @@ impl AccountPostState {
|
||||
}
|
||||
|
||||
pub type BlockId = u64;
|
||||
/// Unix timestamp in milliseconds.
|
||||
pub type Timestamp = u64;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy)]
|
||||
#[cfg_attr(
|
||||
@ -163,6 +165,8 @@ pub type BlockId = u64;
|
||||
pub struct ValidityWindow {
|
||||
from: Option<BlockId>,
|
||||
to: Option<BlockId>,
|
||||
from_timestamp: Option<Timestamp>,
|
||||
to_timestamp: Option<Timestamp>,
|
||||
}
|
||||
|
||||
impl ValidityWindow {
|
||||
@ -171,10 +175,22 @@ impl ValidityWindow {
|
||||
Self {
|
||||
from: None,
|
||||
to: None,
|
||||
from_timestamp: None,
|
||||
to_timestamp: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid for block IDs in the range [from, to) and timestamps in [from_timestamp, to_timestamp).
|
||||
#[must_use]
|
||||
pub fn is_valid_for(&self, block_id: BlockId, timestamp_ms: Timestamp) -> bool {
|
||||
self.from.is_none_or(|start| block_id >= start)
|
||||
&& self.to.is_none_or(|end| block_id < end)
|
||||
&& self.from_timestamp.is_none_or(|t| timestamp_ms >= t)
|
||||
&& self.to_timestamp.is_none_or(|t| timestamp_ms < t)
|
||||
}
|
||||
|
||||
/// Valid for block IDs in the range [from, to), where `from` is included and `to` is excluded.
|
||||
/// Ignores timestamp bounds.
|
||||
#[must_use]
|
||||
pub fn is_valid_for_block_id(&self, id: BlockId) -> bool {
|
||||
self.from.is_none_or(|start| id >= start) && self.to.is_none_or(|end| id < end)
|
||||
@ -184,10 +200,14 @@ impl ValidityWindow {
|
||||
if let (Some(from_id), Some(until_id)) = (self.from, self.to)
|
||||
&& from_id >= until_id
|
||||
{
|
||||
Err(InvalidWindow)
|
||||
} else {
|
||||
Ok(())
|
||||
return Err(InvalidWindow);
|
||||
}
|
||||
if let (Some(from_ts), Some(until_ts)) = (self.from_timestamp, self.to_timestamp)
|
||||
&& from_ts >= until_ts
|
||||
{
|
||||
return Err(InvalidWindow);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -199,7 +219,18 @@ impl ValidityWindow {
|
||||
pub const fn to(&self) -> Option<BlockId> {
|
||||
self.to
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn from_timestamp(&self) -> Option<Timestamp> {
|
||||
self.from_timestamp
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn to_timestamp(&self) -> Option<Timestamp> {
|
||||
self.to_timestamp
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(Option<BlockId>, Option<BlockId>)> for ValidityWindow {
|
||||
type Error = InvalidWindow;
|
||||
|
||||
@ -207,6 +238,37 @@ impl TryFrom<(Option<BlockId>, Option<BlockId>)> for ValidityWindow {
|
||||
let this = Self {
|
||||
from: value.0,
|
||||
to: value.1,
|
||||
from_timestamp: None,
|
||||
to_timestamp: None,
|
||||
};
|
||||
this.check_window()?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
TryFrom<(
|
||||
Option<BlockId>,
|
||||
Option<BlockId>,
|
||||
Option<Timestamp>,
|
||||
Option<Timestamp>,
|
||||
)> for ValidityWindow
|
||||
{
|
||||
type Error = InvalidWindow;
|
||||
|
||||
fn try_from(
|
||||
value: (
|
||||
Option<BlockId>,
|
||||
Option<BlockId>,
|
||||
Option<Timestamp>,
|
||||
Option<Timestamp>,
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let this = Self {
|
||||
from: value.0,
|
||||
to: value.1,
|
||||
from_timestamp: value.2,
|
||||
to_timestamp: value.3,
|
||||
};
|
||||
this.check_window()?;
|
||||
Ok(this)
|
||||
@ -271,6 +333,18 @@ impl ProgramOutput {
|
||||
self.validity_window.check_window()?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn valid_from_timestamp(mut self, ts: Option<Timestamp>) -> Result<Self, InvalidWindow> {
|
||||
self.validity_window.from_timestamp = ts;
|
||||
self.validity_window.check_window()?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn valid_until_timestamp(mut self, ts: Option<Timestamp>) -> Result<Self, InvalidWindow> {
|
||||
self.validity_window.to_timestamp = ts;
|
||||
self.validity_window.check_window()?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of a number as `lo + hi * 2^128`.
|
||||
|
||||
@ -7,7 +7,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput,
|
||||
account::{Account, AccountWithMetadata},
|
||||
program::{BlockId, ValidityWindow},
|
||||
program::{BlockId, Timestamp, ValidityWindow},
|
||||
};
|
||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
@ -37,6 +37,7 @@ impl PrivacyPreservingTransaction {
|
||||
&self,
|
||||
state: &V03State,
|
||||
block_id: BlockId,
|
||||
timestamp_ms: Timestamp,
|
||||
) -> Result<HashMap<AccountId, Account>, NssaError> {
|
||||
let message = &self.message;
|
||||
let witness_set = &self.witness_set;
|
||||
@ -94,7 +95,7 @@ impl PrivacyPreservingTransaction {
|
||||
}
|
||||
|
||||
// Verify validity window
|
||||
if !message.validity_window.is_valid_for_block_id(block_id) {
|
||||
if !message.validity_window.is_valid_for(block_id, timestamp_ms) {
|
||||
return Err(NssaError::OutOfValidityWindow);
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use log::debug;
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
program::{BlockId, ChainedCall, DEFAULT_PROGRAM_ID, validate_execution},
|
||||
program::{BlockId, ChainedCall, DEFAULT_PROGRAM_ID, Timestamp, validate_execution},
|
||||
};
|
||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
@ -71,6 +71,7 @@ impl PublicTransaction {
|
||||
&self,
|
||||
state: &V03State,
|
||||
block_id: BlockId,
|
||||
timestamp_ms: Timestamp,
|
||||
) -> Result<HashMap<AccountId, Account>, NssaError> {
|
||||
let message = self.message();
|
||||
let witness_set = self.witness_set();
|
||||
@ -195,7 +196,7 @@ impl PublicTransaction {
|
||||
ensure!(
|
||||
program_output
|
||||
.validity_window
|
||||
.is_valid_for_block_id(block_id),
|
||||
.is_valid_for(block_id, timestamp_ms),
|
||||
NssaError::OutOfValidityWindow
|
||||
);
|
||||
|
||||
@ -368,7 +369,7 @@ pub mod tests {
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, &[&key1, &key1]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1, 0);
|
||||
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
||||
}
|
||||
|
||||
@ -388,7 +389,7 @@ pub mod tests {
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1, 0);
|
||||
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
||||
}
|
||||
|
||||
@ -409,7 +410,7 @@ pub mod tests {
|
||||
let mut witness_set = WitnessSet::for_message(&message, &[&key1, &key2]);
|
||||
witness_set.signatures_and_public_keys[0].0 = Signature::new_for_tests([1; 64]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1, 0);
|
||||
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
||||
}
|
||||
|
||||
@ -429,7 +430,7 @@ pub mod tests {
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1, 0);
|
||||
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
||||
}
|
||||
|
||||
@ -445,7 +446,7 @@ pub mod tests {
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1);
|
||||
let result = tx.validate_and_produce_public_state_diff(&state, 1, 0);
|
||||
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
||||
account::{Account, AccountId, Nonce},
|
||||
program::{BlockId, ProgramId},
|
||||
program::{BlockId, ProgramId, Timestamp},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -158,8 +158,9 @@ impl V03State {
|
||||
&mut self,
|
||||
tx: &PublicTransaction,
|
||||
block_id: BlockId,
|
||||
timestamp_ms: Timestamp,
|
||||
) -> Result<(), NssaError> {
|
||||
let state_diff = tx.validate_and_produce_public_state_diff(self, block_id)?;
|
||||
let state_diff = tx.validate_and_produce_public_state_diff(self, block_id, timestamp_ms)?;
|
||||
|
||||
#[expect(
|
||||
clippy::iter_over_hash_type,
|
||||
@ -183,9 +184,10 @@ impl V03State {
|
||||
&mut self,
|
||||
tx: &PrivacyPreservingTransaction,
|
||||
block_id: BlockId,
|
||||
timestamp_ms: Timestamp,
|
||||
) -> Result<(), NssaError> {
|
||||
// 1. Verify the transaction satisfies acceptance criteria
|
||||
let public_state_diff = tx.validate_and_produce_public_state_diff(self, block_id)?;
|
||||
let public_state_diff = tx.validate_and_produce_public_state_diff(self, block_id, timestamp_ms)?;
|
||||
|
||||
let message = tx.message();
|
||||
|
||||
@ -340,7 +342,7 @@ pub mod tests {
|
||||
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data},
|
||||
encryption::{EphemeralPublicKey, Scalar, ViewingPublicKey},
|
||||
program::{BlockId, PdaSeed, ProgramId},
|
||||
program::{BlockId, PdaSeed, ProgramId, Timestamp},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -570,7 +572,7 @@ pub mod tests {
|
||||
let balance_to_move = 5;
|
||||
|
||||
let tx = transfer_transaction(from, &key, 0, to, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
assert_eq!(state.get_account_by_id(from).balance, 95);
|
||||
assert_eq!(state.get_account_by_id(to).balance, 5);
|
||||
@ -591,7 +593,7 @@ pub mod tests {
|
||||
assert!(state.get_account_by_id(from).balance < balance_to_move);
|
||||
|
||||
let tx = transfer_transaction(from, &from_key, 0, to, balance_to_move);
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
||||
assert_eq!(state.get_account_by_id(from).balance, 100);
|
||||
@ -615,7 +617,7 @@ pub mod tests {
|
||||
let balance_to_move = 8;
|
||||
|
||||
let tx = transfer_transaction(from, &from_key, 0, to, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
assert_eq!(state.get_account_by_id(from).balance, 192);
|
||||
assert_eq!(state.get_account_by_id(to).balance, 108);
|
||||
@ -635,10 +637,10 @@ pub mod tests {
|
||||
let balance_to_move = 5;
|
||||
|
||||
let tx = transfer_transaction(account_id1, &key1, 0, account_id2, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
let balance_to_move = 3;
|
||||
let tx = transfer_transaction(account_id2, &key2, 0, account_id3, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
assert_eq!(state.get_account_by_id(account_id1).balance, 95);
|
||||
assert_eq!(state.get_account_by_id(account_id2).balance, 2);
|
||||
@ -660,7 +662,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -677,7 +679,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -694,7 +696,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -718,7 +720,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -742,7 +744,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -766,7 +768,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -790,7 +792,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -818,7 +820,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -843,7 +845,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -861,7 +863,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -890,7 +892,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -1083,7 +1085,7 @@ pub mod tests {
|
||||
assert!(!state.private_state.0.contains(&expected_new_commitment));
|
||||
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1)
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1, 0)
|
||||
.unwrap();
|
||||
|
||||
let sender_post = state.get_account_by_id(sender_keys.account_id());
|
||||
@ -1153,7 +1155,7 @@ pub mod tests {
|
||||
assert!(!state.private_state.1.contains(&expected_new_nullifier));
|
||||
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1)
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1, 0)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(state.public_state, previous_public_state);
|
||||
@ -1217,7 +1219,7 @@ pub mod tests {
|
||||
assert!(!state.private_state.1.contains(&expected_new_nullifier));
|
||||
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1)
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1, 0)
|
||||
.unwrap();
|
||||
|
||||
let recipient_post = state.get_account_by_id(recipient_keys.account_id());
|
||||
@ -2145,7 +2147,7 @@ pub mod tests {
|
||||
);
|
||||
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1)
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1, 0)
|
||||
.unwrap();
|
||||
|
||||
let sender_private_account = Account {
|
||||
@ -2163,7 +2165,7 @@ pub mod tests {
|
||||
&state,
|
||||
);
|
||||
|
||||
let result = state.transition_from_privacy_preserving_transaction(&tx, 1);
|
||||
let result = state.transition_from_privacy_preserving_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
||||
let NssaError::InvalidInput(error_message) = result.err().unwrap() else {
|
||||
@ -2240,7 +2242,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let recipient_post = state.get_account_by_id(to);
|
||||
|
||||
@ -2283,7 +2285,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let from_post = state.get_account_by_id(from);
|
||||
let to_post = state.get_account_by_id(to);
|
||||
@ -2323,7 +2325,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(NssaError::MaxChainedCallsDepthExceeded)
|
||||
@ -2364,7 +2366,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let from_post = state.get_account_by_id(from);
|
||||
let to_post = state.get_account_by_id(to);
|
||||
@ -2420,7 +2422,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let from_post = state.get_account_by_id(from);
|
||||
let to_post = state.get_account_by_id(to);
|
||||
@ -2529,7 +2531,7 @@ pub mod tests {
|
||||
let transaction = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(&transaction, 1)
|
||||
.transition_from_privacy_preserving_transaction(&transaction, 1, 0)
|
||||
.unwrap();
|
||||
|
||||
// Assert
|
||||
@ -2585,7 +2587,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
// Execution of winner's token holding account initialization
|
||||
let instruction = token_core::Instruction::InitializeAccount;
|
||||
@ -2598,7 +2600,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
// Submit a solution to the pinata program to claim the prize
|
||||
let solution: u128 = 989_106;
|
||||
@ -2615,7 +2617,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let winner_token_holding_post = state.get_account_by_id(winner_token_holding_id);
|
||||
assert_eq!(
|
||||
@ -2645,7 +2647,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
}
|
||||
@ -2691,7 +2693,7 @@ pub mod tests {
|
||||
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&sender_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
let res = state.transition_from_public_transaction(&tx, 1);
|
||||
let res = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
assert!(matches!(res, Err(NssaError::InvalidProgramBehavior)));
|
||||
|
||||
let sender_post = state.get_account_by_id(sender_id);
|
||||
@ -2760,7 +2762,7 @@ pub mod tests {
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
let result = state.transition_from_privacy_preserving_transaction(&tx, 1);
|
||||
let result = state.transition_from_privacy_preserving_transaction(&tx, 1, 0);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
|
||||
@ -2813,7 +2815,7 @@ pub mod tests {
|
||||
// Claim should succeed
|
||||
assert!(
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1)
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1, 0)
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
@ -2862,7 +2864,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
// Should succeed - no changes made, no claim needed
|
||||
assert!(result.is_ok());
|
||||
@ -2887,7 +2889,7 @@ pub mod tests {
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1);
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
// Should fail - cannot modify data without claiming the account
|
||||
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
||||
@ -3024,17 +3026,18 @@ pub mod tests {
|
||||
let account_ids = vec![pre.account_id];
|
||||
let nonces = vec![];
|
||||
let program_id = validity_window_program.id();
|
||||
let instruction = (validity_window.0, validity_window.1, None::<Timestamp>, None::<Timestamp>);
|
||||
let message = public_transaction::Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
validity_window,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
PublicTransaction::new(message, witness_set)
|
||||
};
|
||||
let result = state.transition_from_public_transaction(&tx, block_id);
|
||||
let result = state.transition_from_public_transaction(&tx, block_id, 0);
|
||||
let is_inside_validity_window = match validity_window {
|
||||
(Some(s), Some(e)) => s <= block_id && block_id < e,
|
||||
(Some(s), None) => s <= block_id,
|
||||
@ -3074,9 +3077,10 @@ pub mod tests {
|
||||
let shared_secret = SharedSecretKey::new(&esk, &account_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
|
||||
let instruction = (validity_window.0, validity_window.1, None::<Timestamp>, None::<Timestamp>);
|
||||
let (output, proof) = circuit::execute_and_prove(
|
||||
vec![pre],
|
||||
Program::serialize_instruction(validity_window).unwrap(),
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![2],
|
||||
vec![(account_keys.npk(), shared_secret)],
|
||||
vec![],
|
||||
@ -3096,7 +3100,7 @@ pub mod tests {
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
PrivacyPreservingTransaction::new(message, witness_set)
|
||||
};
|
||||
let result = state.transition_from_privacy_preserving_transaction(&tx, block_id);
|
||||
let result = state.transition_from_privacy_preserving_transaction(&tx, block_id, 0);
|
||||
let is_inside_validity_window = match validity_window {
|
||||
(Some(s), Some(e)) => s <= block_id && block_id < e,
|
||||
(Some(s), None) => s <= block_id,
|
||||
|
||||
@ -34,10 +34,21 @@ impl ExecutionState {
|
||||
.iter()
|
||||
.filter_map(|output| output.validity_window.to())
|
||||
.min();
|
||||
let valid_from_ts = program_outputs
|
||||
.iter()
|
||||
.filter_map(|output| output.validity_window.from_timestamp())
|
||||
.max();
|
||||
let valid_until_ts = program_outputs
|
||||
.iter()
|
||||
.filter_map(|output| output.validity_window.to_timestamp())
|
||||
.min();
|
||||
|
||||
let validity_window = (valid_from_id, valid_until_id).try_into().expect(
|
||||
"There should be non empty intersection in the program output validity windows",
|
||||
);
|
||||
let validity_window =
|
||||
(valid_from_id, valid_until_id, valid_from_ts, valid_until_ts)
|
||||
.try_into()
|
||||
.expect(
|
||||
"There should be non empty intersection in the program output validity windows",
|
||||
);
|
||||
|
||||
let mut execution_state = Self {
|
||||
pre_states: Vec::new(),
|
||||
|
||||
@ -2733,7 +2733,7 @@ fn simple_amm_remove() {
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let pool_post = state.get_account_by_id(IdForExeTests::pool_definition_id());
|
||||
let vault_a_post = state.get_account_by_id(IdForExeTests::vault_a_id());
|
||||
@ -2813,7 +2813,7 @@ fn simple_amm_new_definition_inactive_initialized_pool_and_uninit_user_lp() {
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let pool_post = state.get_account_by_id(IdForExeTests::pool_definition_id());
|
||||
let vault_a_post = state.get_account_by_id(IdForExeTests::vault_a_id());
|
||||
@ -2897,7 +2897,7 @@ fn simple_amm_new_definition_inactive_initialized_pool_init_user_lp() {
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let pool_post = state.get_account_by_id(IdForExeTests::pool_definition_id());
|
||||
let vault_a_post = state.get_account_by_id(IdForExeTests::vault_a_id());
|
||||
@ -2969,7 +2969,7 @@ fn simple_amm_new_definition_uninitialized_pool() {
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let pool_post = state.get_account_by_id(IdForExeTests::pool_definition_id());
|
||||
let vault_a_post = state.get_account_by_id(IdForExeTests::vault_a_id());
|
||||
@ -3031,7 +3031,7 @@ fn simple_amm_add() {
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let pool_post = state.get_account_by_id(IdForExeTests::pool_definition_id());
|
||||
let vault_a_post = state.get_account_by_id(IdForExeTests::vault_a_id());
|
||||
@ -3088,7 +3088,7 @@ fn simple_amm_swap_1() {
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let pool_post = state.get_account_by_id(IdForExeTests::pool_definition_id());
|
||||
let vault_a_post = state.get_account_by_id(IdForExeTests::vault_a_id());
|
||||
@ -3138,7 +3138,7 @@ fn simple_amm_swap_2() {
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let pool_post = state.get_account_by_id(IdForExeTests::pool_definition_id());
|
||||
let vault_a_post = state.get_account_by_id(IdForExeTests::vault_a_id());
|
||||
|
||||
@ -142,17 +142,24 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
(sequencer_core, mempool_handle)
|
||||
}
|
||||
|
||||
fn next_block_timestamp_ms(&self) -> nssa_core::program::Timestamp {
|
||||
u64::try_from(chrono::Utc::now().timestamp_millis())
|
||||
.expect("Timestamp must be positive")
|
||||
}
|
||||
|
||||
fn execute_check_transaction_on_state(
|
||||
&mut self,
|
||||
tx: NSSATransaction,
|
||||
) -> Result<NSSATransaction, nssa::error::NssaError> {
|
||||
let block_id = self.next_block_id();
|
||||
let timestamp_ms = self.next_block_timestamp_ms();
|
||||
match &tx {
|
||||
NSSATransaction::Public(tx) => self
|
||||
.state
|
||||
.transition_from_public_transaction(tx, self.next_block_id()),
|
||||
.transition_from_public_transaction(tx, block_id, timestamp_ms),
|
||||
NSSATransaction::PrivacyPreserving(tx) => self
|
||||
.state
|
||||
.transition_from_privacy_preserving_transaction(tx, self.next_block_id()),
|
||||
.transition_from_privacy_preserving_transaction(tx, block_id, timestamp_ms),
|
||||
NSSATransaction::ProgramDeployment(tx) => self
|
||||
.state
|
||||
.transition_from_program_deployment_transaction(tx),
|
||||
|
||||
@ -677,7 +677,7 @@ impl RocksDBIO {
|
||||
"transaction pre check failed with err {err:?}"
|
||||
))
|
||||
})?
|
||||
.execute_check_on_state(&mut breakpoint, block.header.block_id)
|
||||
.execute_check_on_state(&mut breakpoint, block.header.block_id, block.header.timestamp)
|
||||
.map_err(|err| {
|
||||
DbError::db_interaction_error(format!(
|
||||
"transaction execution failed with err {err:?}"
|
||||
|
||||
@ -1,14 +1,19 @@
|
||||
use nssa_core::program::{
|
||||
AccountPostState, BlockId, ProgramInput, ProgramOutput, read_nssa_inputs,
|
||||
AccountPostState, BlockId, ProgramInput, ProgramOutput, Timestamp, read_nssa_inputs,
|
||||
};
|
||||
|
||||
type Instruction = (Option<BlockId>, Option<BlockId>);
|
||||
type Instruction = (
|
||||
Option<BlockId>,
|
||||
Option<BlockId>,
|
||||
Option<Timestamp>,
|
||||
Option<Timestamp>,
|
||||
);
|
||||
|
||||
fn main() {
|
||||
let (
|
||||
ProgramInput {
|
||||
pre_states,
|
||||
instruction: (from_id, until_id),
|
||||
instruction: (from_id, until_id, from_ts, until_ts),
|
||||
},
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
@ -27,6 +32,10 @@ fn main() {
|
||||
.valid_from_id(from_id)
|
||||
.unwrap()
|
||||
.valid_until_id(until_id)
|
||||
.unwrap()
|
||||
.valid_from_timestamp(from_ts)
|
||||
.unwrap()
|
||||
.valid_until_timestamp(until_ts)
|
||||
.unwrap();
|
||||
|
||||
output.write();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user