mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-03-28 21:23:36 +00:00
refactor validity window with generic
This commit is contained in:
parent
4746548994
commit
caf74b8346
@ -177,7 +177,8 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
encrypted_private_post_states,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
validity_window
|
||||
block_validity_window,
|
||||
timestamp_validity_window,
|
||||
} = message;
|
||||
let WitnessSet {
|
||||
signatures_and_public_keys: _,
|
||||
@ -214,8 +215,12 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
<span class="info-value">{format!("{proof_len} bytes")}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Validity Window:"</span>
|
||||
<span class="info-value">{validity_window.to_string()}</span>
|
||||
<span class="info-label">"Block Validity Window:"</span>
|
||||
<span class="info-value">{block_validity_window.to_string()}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Timestamp Validity Window:"</span>
|
||||
<span class="info-value">{timestamp_validity_window.to_string()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -287,7 +287,8 @@ impl From<nssa::privacy_preserving_transaction::message::Message> for PrivacyPre
|
||||
encrypted_private_post_states,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
validity_window,
|
||||
block_validity_window,
|
||||
timestamp_validity_window,
|
||||
} = value;
|
||||
Self {
|
||||
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
|
||||
@ -302,7 +303,8 @@ impl From<nssa::privacy_preserving_transaction::message::Message> for PrivacyPre
|
||||
.into_iter()
|
||||
.map(|(n, d)| (n.into(), d.into()))
|
||||
.collect(),
|
||||
validity_window: validity_window.into(),
|
||||
block_validity_window: block_validity_window.into(),
|
||||
timestamp_validity_window: timestamp_validity_window.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -318,7 +320,8 @@ impl TryFrom<PrivacyPreservingMessage> for nssa::privacy_preserving_transaction:
|
||||
encrypted_private_post_states,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
validity_window,
|
||||
block_validity_window,
|
||||
timestamp_validity_window,
|
||||
} = value;
|
||||
Ok(Self {
|
||||
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
|
||||
@ -340,7 +343,10 @@ impl TryFrom<PrivacyPreservingMessage> for nssa::privacy_preserving_transaction:
|
||||
.into_iter()
|
||||
.map(|(n, d)| (n.into(), d.into()))
|
||||
.collect(),
|
||||
validity_window: validity_window
|
||||
block_validity_window: block_validity_window
|
||||
.try_into()
|
||||
.map_err(|e| nssa::error::NssaError::InvalidInput(format!("{e}")))?,
|
||||
timestamp_validity_window: timestamp_validity_window
|
||||
.try_into()
|
||||
.map_err(|e| nssa::error::NssaError::InvalidInput(format!("{e}")))?,
|
||||
})
|
||||
@ -692,18 +698,13 @@ impl From<HashType> for common::HashType {
|
||||
// ValidityWindow conversions
|
||||
// ============================================================================
|
||||
|
||||
impl From<nssa_core::program::ValidityWindow> for ValidityWindow {
|
||||
fn from(value: nssa_core::program::ValidityWindow) -> Self {
|
||||
Self((
|
||||
value.start(),
|
||||
value.end(),
|
||||
value.from_timestamp(),
|
||||
value.to_timestamp(),
|
||||
))
|
||||
impl From<nssa_core::program::ValidityWindow<u64>> for ValidityWindow {
|
||||
fn from(value: nssa_core::program::ValidityWindow<u64>) -> Self {
|
||||
Self((value.start(), value.end()))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ValidityWindow> for nssa_core::program::ValidityWindow {
|
||||
impl TryFrom<ValidityWindow> for nssa_core::program::ValidityWindow<u64> {
|
||||
type Error = nssa_core::program::InvalidWindow;
|
||||
|
||||
fn try_from(value: ValidityWindow) -> Result<Self, Self::Error> {
|
||||
|
||||
@ -235,7 +235,8 @@ pub struct PrivacyPreservingMessage {
|
||||
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
|
||||
pub new_commitments: Vec<Commitment>,
|
||||
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
|
||||
pub validity_window: ValidityWindow,
|
||||
pub block_validity_window: ValidityWindow,
|
||||
pub timestamp_validity_window: ValidityWindow,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
@ -302,22 +303,15 @@ pub struct Nullifier(
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ValidityWindow(
|
||||
pub (
|
||||
Option<BlockId>,
|
||||
Option<BlockId>,
|
||||
Option<TimeStamp>,
|
||||
Option<TimeStamp>,
|
||||
),
|
||||
);
|
||||
pub struct ValidityWindow(pub (Option<BlockId>, Option<BlockId>));
|
||||
|
||||
impl Display for ValidityWindow {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.0 {
|
||||
(Some(start), Some(end), ..) => write!(f, "[{start}, {end})"),
|
||||
(Some(start), None, ..) => write!(f, "[{start}, \u{221e})"),
|
||||
(None, Some(end), ..) => write!(f, "(-\u{221e}, {end})"),
|
||||
(None, None, ..) => write!(f, "(-\u{221e}, \u{221e})"),
|
||||
(Some(start), Some(end)) => write!(f, "[{start}, {end})"),
|
||||
(Some(start), None) => write!(f, "[{start}, \u{221e})"),
|
||||
(None, Some(end)) => write!(f, "(-\u{221e}, {end})"),
|
||||
(None, None) => write!(f, "(-\u{221e}, \u{221e})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +124,8 @@ impl MockIndexerService {
|
||||
indexer_service_protocol::Nullifier([tx_idx as u8; 32]),
|
||||
CommitmentSetDigest([0xff; 32]),
|
||||
)],
|
||||
validity_window: ValidityWindow((None, None, None, None)),
|
||||
block_validity_window: ValidityWindow((None, None)),
|
||||
timestamp_validity_window: ValidityWindow((None, None)),
|
||||
},
|
||||
witness_set: WitnessSet {
|
||||
signatures_and_public_keys: vec![],
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::{
|
||||
NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::Ciphertext,
|
||||
program::{ProgramId, ProgramOutput, ValidityWindow},
|
||||
program::{BlockId, ProgramId, ProgramOutput, Timestamp, ValidityWindow},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -36,7 +36,8 @@ pub struct PrivacyPreservingCircuitOutput {
|
||||
pub ciphertexts: Vec<Ciphertext>,
|
||||
pub new_commitments: Vec<Commitment>,
|
||||
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
|
||||
pub validity_window: ValidityWindow,
|
||||
pub block_validity_window: ValidityWindow<BlockId>,
|
||||
pub timestamp_validity_window: ValidityWindow<Timestamp>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
@ -102,7 +103,8 @@ mod tests {
|
||||
),
|
||||
[0xab; 32],
|
||||
)],
|
||||
validity_window: (Some(1), None).try_into().unwrap(),
|
||||
block_validity_window: (Some(1u64), None).try_into().unwrap(),
|
||||
timestamp_validity_window: ValidityWindow::new_unbounded(),
|
||||
};
|
||||
let bytes = output.to_bytes();
|
||||
let output_from_slice: PrivacyPreservingCircuitOutput = from_slice(&bytes).unwrap();
|
||||
|
||||
@ -162,155 +162,93 @@ pub type Timestamp = u64;
|
||||
any(feature = "host", test),
|
||||
derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)
|
||||
)]
|
||||
pub struct ValidityWindow {
|
||||
from: Option<BlockId>,
|
||||
to: Option<BlockId>,
|
||||
from_timestamp: Option<Timestamp>,
|
||||
to_timestamp: Option<Timestamp>,
|
||||
pub struct ValidityWindow<T> {
|
||||
from: Option<T>,
|
||||
to: Option<T>,
|
||||
}
|
||||
|
||||
impl ValidityWindow {
|
||||
/// Creates a window with no bounds, valid for every block ID.
|
||||
impl<T> ValidityWindow<T> {
|
||||
/// Creates a window with no bounds.
|
||||
#[must_use]
|
||||
pub const fn new_unbounded() -> Self {
|
||||
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`).
|
||||
impl<T: Copy + PartialOrd> ValidityWindow<T> {
|
||||
/// Valid for values in the range [from, to), where `from` is included and `to` is excluded.
|
||||
#[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)
|
||||
pub fn is_valid_for(&self, value: T) -> bool {
|
||||
self.from.is_none_or(|start| value >= start)
|
||||
&& self.to.is_none_or(|end| value < end)
|
||||
}
|
||||
|
||||
/// Returns `Err(InvalidWindow)` if both bounds are set and `from >= to`.
|
||||
const fn check_window(&self) -> Result<(), InvalidWindow> {
|
||||
if let (Some(from_id), Some(until_id)) = (self.from, self.to)
|
||||
&& from_id >= until_id
|
||||
{
|
||||
return Err(InvalidWindow);
|
||||
}
|
||||
if let (Some(from_ts), Some(until_ts)) = (self.from_timestamp, self.to_timestamp)
|
||||
&& from_ts >= until_ts
|
||||
fn check_window(&self) -> Result<(), InvalidWindow> {
|
||||
if let (Some(from), Some(to)) = (self.from, self.to)
|
||||
&& from >= to
|
||||
{
|
||||
return Err(InvalidWindow);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Inclusive lower bound. `None` means the window starts at the beginning of the chain.
|
||||
/// Inclusive lower bound. `None` means no lower bound.
|
||||
#[must_use]
|
||||
pub const fn start(&self) -> Option<BlockId> {
|
||||
pub fn start(&self) -> Option<T> {
|
||||
self.from
|
||||
}
|
||||
|
||||
/// Exclusive upper bound. `None` means the window has no expiry.
|
||||
/// Exclusive upper bound. `None` means no upper bound.
|
||||
#[must_use]
|
||||
pub const fn end(&self) -> Option<BlockId> {
|
||||
pub fn end(&self) -> Option<T> {
|
||||
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 {
|
||||
impl<T: Copy + PartialOrd> TryFrom<(Option<T>, Option<T>)> for ValidityWindow<T> {
|
||||
type Error = InvalidWindow;
|
||||
|
||||
fn try_from(value: (Option<BlockId>, Option<BlockId>)) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: (Option<T>, Option<T>)) -> Result<Self, Self::Error> {
|
||||
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
|
||||
{
|
||||
impl<T: Copy + PartialOrd> TryFrom<std::ops::Range<T>> for ValidityWindow<T> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<std::ops::Range<BlockId>> for ValidityWindow {
|
||||
type Error = InvalidWindow;
|
||||
|
||||
fn try_from(value: std::ops::Range<BlockId>) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: std::ops::Range<T>) -> Result<Self, Self::Error> {
|
||||
(Some(value.start), Some(value.end)).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeFrom<BlockId>> for ValidityWindow {
|
||||
fn from(value: std::ops::RangeFrom<BlockId>) -> Self {
|
||||
impl<T: Copy + PartialOrd> From<std::ops::RangeFrom<T>> for ValidityWindow<T> {
|
||||
fn from(value: std::ops::RangeFrom<T>) -> Self {
|
||||
Self {
|
||||
from: Some(value.start),
|
||||
to: None,
|
||||
from_timestamp: None,
|
||||
to_timestamp: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeTo<BlockId>> for ValidityWindow {
|
||||
fn from(value: std::ops::RangeTo<BlockId>) -> Self {
|
||||
impl<T: Copy + PartialOrd> From<std::ops::RangeTo<T>> for ValidityWindow<T> {
|
||||
fn from(value: std::ops::RangeTo<T>) -> Self {
|
||||
Self {
|
||||
from: None,
|
||||
to: Some(value.end),
|
||||
from_timestamp: None,
|
||||
to_timestamp: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeFull> for ValidityWindow {
|
||||
impl<T> From<std::ops::RangeFull> for ValidityWindow<T> {
|
||||
fn from(_: std::ops::RangeFull) -> Self {
|
||||
Self::new_unbounded()
|
||||
}
|
||||
@ -332,8 +270,10 @@ pub struct ProgramOutput {
|
||||
pub post_states: Vec<AccountPostState>,
|
||||
/// The list of chained calls to other programs.
|
||||
pub chained_calls: Vec<ChainedCall>,
|
||||
/// The window where the program output is valid.
|
||||
pub validity_window: ValidityWindow,
|
||||
/// The block ID window where the program output is valid.
|
||||
pub block_validity_window: ValidityWindow<BlockId>,
|
||||
/// The timestamp window where the program output is valid.
|
||||
pub timestamp_validity_window: ValidityWindow<Timestamp>,
|
||||
}
|
||||
|
||||
impl ProgramOutput {
|
||||
@ -347,7 +287,8 @@ impl ProgramOutput {
|
||||
pre_states,
|
||||
post_states,
|
||||
chained_calls: Vec::new(),
|
||||
validity_window: ValidityWindow::new_unbounded(),
|
||||
block_validity_window: ValidityWindow::new_unbounded(),
|
||||
timestamp_validity_window: ValidityWindow::new_unbounded(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,31 +301,45 @@ impl ProgramOutput {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the validity window from an infallible range conversion (`1..`, `..5`, `..`).
|
||||
pub fn with_validity_window<W: Into<ValidityWindow>>(mut self, window: W) -> Self {
|
||||
self.validity_window = window.into();
|
||||
/// Sets the block ID validity window from an infallible range conversion (`1..`, `..5`, `..`).
|
||||
pub fn with_block_validity_window<W: Into<ValidityWindow<BlockId>>>(mut self, window: W) -> Self {
|
||||
self.block_validity_window = window.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the validity window from a fallible range conversion (`1..5`).
|
||||
/// Sets the block ID validity window from a fallible range conversion (`1..5`).
|
||||
/// Returns `Err` if the range is empty.
|
||||
pub fn try_with_validity_window<W: TryInto<ValidityWindow, Error = InvalidWindow>>(
|
||||
pub fn try_with_block_validity_window<W: TryInto<ValidityWindow<BlockId>, Error = InvalidWindow>>(
|
||||
mut self,
|
||||
window: W,
|
||||
) -> Result<Self, InvalidWindow> {
|
||||
self.validity_window = window.try_into()?;
|
||||
self.block_validity_window = window.try_into()?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Sets the timestamp validity window from an infallible range conversion.
|
||||
pub fn with_timestamp_validity_window<W: Into<ValidityWindow<Timestamp>>>(mut self, window: W) -> Self {
|
||||
self.timestamp_validity_window = window.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the timestamp validity window from a fallible range conversion.
|
||||
/// Returns `Err` if the range is empty.
|
||||
pub fn try_with_timestamp_validity_window<W: TryInto<ValidityWindow<Timestamp>, Error = InvalidWindow>>(
|
||||
mut self,
|
||||
window: W,
|
||||
) -> Result<Self, InvalidWindow> {
|
||||
self.timestamp_validity_window = window.try_into()?;
|
||||
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()?;
|
||||
self.timestamp_validity_window = (ts, self.timestamp_validity_window.end()).try_into()?;
|
||||
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()?;
|
||||
self.timestamp_validity_window = (self.timestamp_validity_window.start(), ts).try_into()?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@ -541,128 +496,131 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn validity_window_unbounded_accepts_any_block() {
|
||||
let w = ValidityWindow::new_unbounded();
|
||||
assert!(w.is_valid_for_block_id(0));
|
||||
assert!(w.is_valid_for_block_id(u64::MAX));
|
||||
fn validity_window_unbounded_accepts_any_value() {
|
||||
let w: ValidityWindow<u64> = ValidityWindow::new_unbounded();
|
||||
assert!(w.is_valid_for(0));
|
||||
assert!(w.is_valid_for(u64::MAX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_bounded_range_includes_from_excludes_to() {
|
||||
let w: ValidityWindow = (Some(5), Some(10)).try_into().unwrap();
|
||||
assert!(!w.is_valid_for_block_id(4));
|
||||
assert!(w.is_valid_for_block_id(5));
|
||||
assert!(w.is_valid_for_block_id(9));
|
||||
assert!(!w.is_valid_for_block_id(10));
|
||||
let w: ValidityWindow<u64> = (Some(5), Some(10)).try_into().unwrap();
|
||||
assert!(!w.is_valid_for(4));
|
||||
assert!(w.is_valid_for(5));
|
||||
assert!(w.is_valid_for(9));
|
||||
assert!(!w.is_valid_for(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_only_from_bound() {
|
||||
let w: ValidityWindow = (Some(5), None).try_into().unwrap();
|
||||
assert!(!w.is_valid_for_block_id(4));
|
||||
assert!(w.is_valid_for_block_id(5));
|
||||
assert!(w.is_valid_for_block_id(u64::MAX));
|
||||
let w: ValidityWindow<u64> = (Some(5), None).try_into().unwrap();
|
||||
assert!(!w.is_valid_for(4));
|
||||
assert!(w.is_valid_for(5));
|
||||
assert!(w.is_valid_for(u64::MAX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_only_to_bound() {
|
||||
let w: ValidityWindow = (None, Some(5)).try_into().unwrap();
|
||||
assert!(w.is_valid_for_block_id(0));
|
||||
assert!(w.is_valid_for_block_id(4));
|
||||
assert!(!w.is_valid_for_block_id(5));
|
||||
let w: ValidityWindow<u64> = (None, Some(5)).try_into().unwrap();
|
||||
assert!(w.is_valid_for(0));
|
||||
assert!(w.is_valid_for(4));
|
||||
assert!(!w.is_valid_for(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_adjacent_bounds_are_invalid() {
|
||||
// [5, 5) is an empty range — from == to
|
||||
assert!(ValidityWindow::try_from((Some(5), Some(5))).is_err());
|
||||
assert!(ValidityWindow::<u64>::try_from((Some(5), Some(5))).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_inverted_bounds_are_invalid() {
|
||||
assert!(ValidityWindow::try_from((Some(10), Some(5))).is_err());
|
||||
assert!(ValidityWindow::<u64>::try_from((Some(10), Some(5))).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_getters_match_construction() {
|
||||
let w: ValidityWindow = (Some(3), Some(7)).try_into().unwrap();
|
||||
let w: ValidityWindow<u64> = (Some(3), Some(7)).try_into().unwrap();
|
||||
assert_eq!(w.start(), Some(3));
|
||||
assert_eq!(w.end(), Some(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_getters_for_unbounded() {
|
||||
let w = ValidityWindow::new_unbounded();
|
||||
let w: ValidityWindow<u64> = ValidityWindow::new_unbounded();
|
||||
assert_eq!(w.start(), None);
|
||||
assert_eq!(w.end(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_from_range() {
|
||||
let w = ValidityWindow::try_from(5_u64..10).unwrap();
|
||||
let w: ValidityWindow<u64> = ValidityWindow::try_from(5_u64..10).unwrap();
|
||||
assert_eq!(w.start(), Some(5));
|
||||
assert_eq!(w.end(), Some(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_from_range_empty_is_invalid() {
|
||||
assert!(ValidityWindow::try_from(5_u64..5).is_err());
|
||||
assert!(ValidityWindow::<u64>::try_from(5_u64..5).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_from_range_inverted_is_invalid() {
|
||||
let from = 10_u64;
|
||||
let to = 5_u64;
|
||||
assert!(ValidityWindow::try_from(from..to).is_err());
|
||||
assert!(ValidityWindow::<u64>::try_from(from..to).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_from_range_from() {
|
||||
let w: ValidityWindow = (5_u64..).into();
|
||||
let w: ValidityWindow<u64> = (5_u64..).into();
|
||||
assert_eq!(w.start(), Some(5));
|
||||
assert_eq!(w.end(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_from_range_to() {
|
||||
let w: ValidityWindow = (..10_u64).into();
|
||||
let w: ValidityWindow<u64> = (..10_u64).into();
|
||||
assert_eq!(w.start(), None);
|
||||
assert_eq!(w.end(), Some(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validity_window_from_range_full() {
|
||||
let w: ValidityWindow = (..).into();
|
||||
let w: ValidityWindow<u64> = (..).into();
|
||||
assert_eq!(w.start(), None);
|
||||
assert_eq!(w.end(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn program_output_try_with_validity_window_range() {
|
||||
fn program_output_try_with_block_validity_window_range() {
|
||||
let output = ProgramOutput::new(vec![], vec![], vec![])
|
||||
.try_with_validity_window(10_u64..100)
|
||||
.try_with_block_validity_window(10_u64..100)
|
||||
.unwrap();
|
||||
assert_eq!(output.validity_window.start(), Some(10));
|
||||
assert_eq!(output.validity_window.end(), Some(100));
|
||||
assert_eq!(output.block_validity_window.start(), Some(10));
|
||||
assert_eq!(output.block_validity_window.end(), Some(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn program_output_with_validity_window_range_from() {
|
||||
let output = ProgramOutput::new(vec![], vec![], vec![]).with_validity_window(10_u64..);
|
||||
assert_eq!(output.validity_window.start(), Some(10));
|
||||
assert_eq!(output.validity_window.end(), None);
|
||||
fn program_output_with_block_validity_window_range_from() {
|
||||
let output =
|
||||
ProgramOutput::new(vec![], vec![], vec![]).with_block_validity_window(10_u64..);
|
||||
assert_eq!(output.block_validity_window.start(), Some(10));
|
||||
assert_eq!(output.block_validity_window.end(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn program_output_with_validity_window_range_to() {
|
||||
let output = ProgramOutput::new(vec![], vec![], vec![]).with_validity_window(..100_u64);
|
||||
assert_eq!(output.validity_window.start(), None);
|
||||
assert_eq!(output.validity_window.end(), Some(100));
|
||||
fn program_output_with_block_validity_window_range_to() {
|
||||
let output =
|
||||
ProgramOutput::new(vec![], vec![], vec![]).with_block_validity_window(..100_u64);
|
||||
assert_eq!(output.block_validity_window.start(), None);
|
||||
assert_eq!(output.block_validity_window.end(), Some(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn program_output_try_with_validity_window_empty_range_fails() {
|
||||
let result = ProgramOutput::new(vec![], vec![], vec![]).try_with_validity_window(5_u64..5);
|
||||
fn program_output_try_with_block_validity_window_empty_range_fails() {
|
||||
let result =
|
||||
ProgramOutput::new(vec![], vec![], vec![]).try_with_block_validity_window(5_u64..5);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitOutput,
|
||||
account::{Account, Nonce},
|
||||
encryption::{Ciphertext, EphemeralPublicKey, ViewingPublicKey},
|
||||
program::ValidityWindow,
|
||||
program::{BlockId, Timestamp, ValidityWindow},
|
||||
};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
|
||||
@ -53,7 +53,8 @@ pub struct Message {
|
||||
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
|
||||
pub new_commitments: Vec<Commitment>,
|
||||
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
|
||||
pub validity_window: ValidityWindow,
|
||||
pub block_validity_window: ValidityWindow<BlockId>,
|
||||
pub timestamp_validity_window: ValidityWindow<Timestamp>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Message {
|
||||
@ -79,7 +80,8 @@ impl std::fmt::Debug for Message {
|
||||
)
|
||||
.field("new_commitments", &self.new_commitments)
|
||||
.field("new_nullifiers", &nullifiers)
|
||||
.field("validity_window", &self.validity_window)
|
||||
.field("block_validity_window", &self.block_validity_window)
|
||||
.field("timestamp_validity_window", &self.timestamp_validity_window)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@ -112,7 +114,8 @@ impl Message {
|
||||
encrypted_private_post_states,
|
||||
new_commitments: output.new_commitments,
|
||||
new_nullifiers: output.new_nullifiers,
|
||||
validity_window: output.validity_window,
|
||||
block_validity_window: output.block_validity_window,
|
||||
timestamp_validity_window: output.timestamp_validity_window,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -123,6 +126,7 @@ pub mod tests {
|
||||
Commitment, EncryptionScheme, Nullifier, NullifierPublicKey, SharedSecretKey,
|
||||
account::Account,
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
program::ValidityWindow,
|
||||
};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
|
||||
@ -165,7 +169,8 @@ pub mod tests {
|
||||
encrypted_private_post_states,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
validity_window: (None, None).try_into().unwrap(),
|
||||
block_validity_window: ValidityWindow::new_unbounded(),
|
||||
timestamp_validity_window: ValidityWindow::new_unbounded(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -95,7 +95,9 @@ impl PrivacyPreservingTransaction {
|
||||
}
|
||||
|
||||
// Verify validity window
|
||||
if !message.validity_window.is_valid_for(block_id, timestamp_ms) {
|
||||
if !message.block_validity_window.is_valid_for(block_id)
|
||||
|| !message.timestamp_validity_window.is_valid_for(timestamp_ms)
|
||||
{
|
||||
return Err(NssaError::OutOfValidityWindow);
|
||||
}
|
||||
|
||||
@ -120,7 +122,8 @@ impl PrivacyPreservingTransaction {
|
||||
&message.encrypted_private_post_states,
|
||||
&message.new_commitments,
|
||||
&message.new_nullifiers,
|
||||
&message.validity_window,
|
||||
&message.block_validity_window,
|
||||
&message.timestamp_validity_window,
|
||||
)?;
|
||||
|
||||
// 5. Commitment freshness
|
||||
@ -182,7 +185,8 @@ fn check_privacy_preserving_circuit_proof_is_valid(
|
||||
encrypted_private_post_states: &[EncryptedAccountData],
|
||||
new_commitments: &[Commitment],
|
||||
new_nullifiers: &[(Nullifier, CommitmentSetDigest)],
|
||||
validity_window: &ValidityWindow,
|
||||
block_validity_window: &ValidityWindow<BlockId>,
|
||||
timestamp_validity_window: &ValidityWindow<Timestamp>,
|
||||
) -> Result<(), NssaError> {
|
||||
let output = PrivacyPreservingCircuitOutput {
|
||||
public_pre_states: public_pre_states.to_vec(),
|
||||
@ -194,7 +198,8 @@ fn check_privacy_preserving_circuit_proof_is_valid(
|
||||
.collect(),
|
||||
new_commitments: new_commitments.to_vec(),
|
||||
new_nullifiers: new_nullifiers.to_vec(),
|
||||
validity_window: validity_window.to_owned(),
|
||||
block_validity_window: block_validity_window.to_owned(),
|
||||
timestamp_validity_window: timestamp_validity_window.to_owned(),
|
||||
};
|
||||
proof
|
||||
.is_valid_for(&output)
|
||||
|
||||
@ -194,9 +194,8 @@ impl PublicTransaction {
|
||||
|
||||
// Verify validity window
|
||||
ensure!(
|
||||
program_output
|
||||
.validity_window
|
||||
.is_valid_for(block_id, timestamp_ms),
|
||||
program_output.block_validity_window.is_valid_for(block_id)
|
||||
&& program_output.timestamp_validity_window.is_valid_for(timestamp_ms),
|
||||
NssaError::OutOfValidityWindow
|
||||
);
|
||||
|
||||
|
||||
@ -3021,7 +3021,7 @@ pub mod tests {
|
||||
validity_window: (Option<BlockId>, Option<BlockId>),
|
||||
block_id: BlockId,
|
||||
) {
|
||||
let validity_window: ValidityWindow = validity_window.try_into().unwrap();
|
||||
let validity_window: ValidityWindow<BlockId> = validity_window.try_into().unwrap();
|
||||
let validity_window_program = Program::validity_window();
|
||||
let account_keys = test_public_account_keys_1();
|
||||
let pre = AccountWithMetadata::new(Account::default(), false, account_keys.account_id());
|
||||
@ -3068,7 +3068,7 @@ pub mod tests {
|
||||
validity_window: (Option<BlockId>, Option<BlockId>),
|
||||
block_id: BlockId,
|
||||
) {
|
||||
let validity_window: ValidityWindow = validity_window.try_into().unwrap();
|
||||
let validity_window: ValidityWindow<BlockId> = validity_window.try_into().unwrap();
|
||||
let validity_window_program = Program::validity_window();
|
||||
let account_keys = test_private_account_keys_1();
|
||||
let pre = AccountWithMetadata::new(Account::default(), false, &account_keys.npk());
|
||||
|
||||
@ -10,8 +10,8 @@ use nssa_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
||||
compute_digest_for_path,
|
||||
program::{
|
||||
AccountPostState, ChainedCall, DEFAULT_PROGRAM_ID, MAX_NUMBER_CHAINED_CALLS, ProgramId,
|
||||
ProgramOutput, ValidityWindow, validate_execution,
|
||||
AccountPostState, BlockId, ChainedCall, DEFAULT_PROGRAM_ID, MAX_NUMBER_CHAINED_CALLS,
|
||||
ProgramId, ProgramOutput, Timestamp, ValidityWindow, validate_execution,
|
||||
},
|
||||
};
|
||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
@ -20,39 +20,47 @@ use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
struct ExecutionState {
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
post_states: HashMap<AccountId, Account>,
|
||||
validity_window: ValidityWindow,
|
||||
block_validity_window: ValidityWindow<BlockId>,
|
||||
timestamp_validity_window: ValidityWindow<Timestamp>,
|
||||
}
|
||||
|
||||
impl ExecutionState {
|
||||
/// Validate program outputs and derive the overall execution state.
|
||||
pub fn derive_from_outputs(program_id: ProgramId, program_outputs: Vec<ProgramOutput>) -> Self {
|
||||
let valid_from_id = program_outputs
|
||||
let block_valid_from = program_outputs
|
||||
.iter()
|
||||
.filter_map(|output| output.validity_window.start())
|
||||
.filter_map(|output| output.block_validity_window.start())
|
||||
.max();
|
||||
let valid_until_id = program_outputs
|
||||
let block_valid_until = program_outputs
|
||||
.iter()
|
||||
.filter_map(|output| output.validity_window.end())
|
||||
.filter_map(|output| output.block_validity_window.end())
|
||||
.min();
|
||||
let valid_from_ts = program_outputs
|
||||
let ts_valid_from = program_outputs
|
||||
.iter()
|
||||
.filter_map(|output| output.validity_window.from_timestamp())
|
||||
.filter_map(|output| output.timestamp_validity_window.start())
|
||||
.max();
|
||||
let valid_until_ts = program_outputs
|
||||
let ts_valid_until = program_outputs
|
||||
.iter()
|
||||
.filter_map(|output| output.validity_window.to_timestamp())
|
||||
.filter_map(|output| output.timestamp_validity_window.end())
|
||||
.min();
|
||||
|
||||
let validity_window = (valid_from_id, valid_until_id, valid_from_ts, valid_until_ts)
|
||||
let block_validity_window: ValidityWindow<BlockId> = (block_valid_from, block_valid_until)
|
||||
.try_into()
|
||||
.expect(
|
||||
"There should be non empty intersection in the program output validity windows",
|
||||
"There should be non empty intersection in the program output block validity windows",
|
||||
);
|
||||
let timestamp_validity_window: ValidityWindow<Timestamp> =
|
||||
(ts_valid_from, ts_valid_until)
|
||||
.try_into()
|
||||
.expect(
|
||||
"There should be non empty intersection in the program output timestamp validity windows",
|
||||
);
|
||||
|
||||
let mut execution_state = Self {
|
||||
pre_states: Vec::new(),
|
||||
post_states: HashMap::new(),
|
||||
validity_window,
|
||||
block_validity_window,
|
||||
timestamp_validity_window,
|
||||
};
|
||||
|
||||
let Some(first_output) = program_outputs.first() else {
|
||||
@ -235,7 +243,8 @@ fn compute_circuit_output(
|
||||
ciphertexts: Vec::new(),
|
||||
new_commitments: Vec::new(),
|
||||
new_nullifiers: Vec::new(),
|
||||
validity_window: execution_state.validity_window,
|
||||
block_validity_window: execution_state.block_validity_window,
|
||||
timestamp_validity_window: execution_state.timestamp_validity_window,
|
||||
};
|
||||
|
||||
let states_iter = execution_state.into_states_iter();
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
use nssa_core::program::{
|
||||
AccountPostState, ProgramInput, ProgramOutput, ValidityWindow, read_nssa_inputs,
|
||||
AccountPostState, BlockId, ProgramInput, ProgramOutput, ValidityWindow, read_nssa_inputs,
|
||||
};
|
||||
|
||||
type Instruction = ValidityWindow;
|
||||
type Instruction = ValidityWindow<BlockId>;
|
||||
|
||||
fn main() {
|
||||
let (
|
||||
ProgramInput {
|
||||
pre_states,
|
||||
instruction: validity_window,
|
||||
instruction: block_validity_window,
|
||||
},
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
@ -24,6 +24,6 @@ fn main() {
|
||||
vec![pre],
|
||||
vec![AccountPostState::new(post)],
|
||||
)
|
||||
.with_validity_window(validity_window)
|
||||
.with_block_validity_window(block_validity_window)
|
||||
.write();
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
use nssa_core::program::{
|
||||
AccountPostState, ChainedCall, ProgramId, ProgramInput, ProgramOutput, ValidityWindow,
|
||||
AccountPostState, BlockId, ChainedCall, ProgramId, ProgramInput, ProgramOutput, ValidityWindow,
|
||||
read_nssa_inputs,
|
||||
};
|
||||
use risc0_zkvm::serde::to_vec;
|
||||
|
||||
/// A program that sets a validity window on its output and chains to another program with a
|
||||
/// potentially different validity window.
|
||||
/// A program that sets a block validity window on its output and chains to another program with a
|
||||
/// potentially different block validity window.
|
||||
///
|
||||
/// Instruction: (`window`, `chained_program_id`, `chained_window`)
|
||||
/// The initial output uses `window` and chains to `chained_program_id` with `chained_window`.
|
||||
type Instruction = (ValidityWindow, ProgramId, ValidityWindow);
|
||||
type Instruction = (ValidityWindow<BlockId>, ProgramId, ValidityWindow<BlockId>);
|
||||
|
||||
fn main() {
|
||||
let (
|
||||
ProgramInput {
|
||||
pre_states,
|
||||
instruction: (validity_window, chained_program_id, chained_validity_window),
|
||||
instruction: (block_validity_window, chained_program_id, chained_block_validity_window),
|
||||
},
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
@ -23,7 +23,7 @@ fn main() {
|
||||
let [pre] = <[_; 1]>::try_from(pre_states.clone()).expect("Expected exactly one pre state");
|
||||
let post = pre.account.clone();
|
||||
|
||||
let chained_instruction = to_vec(&chained_validity_window).unwrap();
|
||||
let chained_instruction = to_vec(&chained_block_validity_window).unwrap();
|
||||
let chained_call = ChainedCall {
|
||||
program_id: chained_program_id,
|
||||
instruction_data: chained_instruction,
|
||||
@ -36,7 +36,7 @@ fn main() {
|
||||
vec![pre],
|
||||
vec![AccountPostState::new(post)],
|
||||
)
|
||||
.with_validity_window(validity_window)
|
||||
.with_block_validity_window(block_validity_window)
|
||||
.with_chained_calls(vec![chained_call])
|
||||
.write();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user