use crate::{ConsensusMessage, TRANSACTION_VALIDITY_WINDOW_MS};
pub const MAX_FUTURE_VALID_FROM_MS: u64 = 60_000;
pub fn extract_validity(payload: &[u8]) -> (Option<u64>, Option<u64>) {
let Ok(msg) = bincode::deserialize::<ConsensusMessage>(payload) else {
return (None, None);
};
match msg {
ConsensusMessage::VersionedTransaction(vtx) => {
let valid_from_i64 = vtx.message.valid_from();
let Ok(valid_from) = u64::try_from(valid_from_i64) else {
return (None, None);
};
let Some(valid_until) = valid_from.checked_add(TRANSACTION_VALIDITY_WINDOW_MS) else {
return (None, None);
};
(Some(valid_from), Some(valid_until))
}
ConsensusMessage::RexUpdateResult(otx) => {
(None, Some(otx.target_timestamp))
}
ConsensusMessage::AdminTransaction(_) => {
(None, None)
}
}
}
pub fn extract_valid_from(payload: &[u8]) -> Option<u64> {
extract_validity(payload).0
}
pub fn extract_valid_until(payload: &[u8]) -> Option<u64> {
extract_validity(payload).1
}
#[inline]
pub fn is_expired(valid_until: u64, now: u64) -> bool {
now > valid_until
}
#[inline]
pub fn is_not_yet_valid(valid_from: u64, now: u64) -> bool {
now < valid_from
}
#[inline]
pub fn is_too_far_in_future(valid_from: u64, now: u64) -> bool {
valid_from > now.saturating_add(MAX_FUTURE_VALID_FROM_MS)
}
#[cfg(any(test, feature = "testing"))]
pub fn create_versioned_transaction_with_timestamp(valid_from: i64) -> Vec<u8> {
use rialo_s_message::{legacy::Message as LegacyMessage, ConfigHashPrefix, VersionedMessage};
use rialo_s_transaction::versioned::VersionedTransaction;
let message = LegacyMessage {
header: rialo_s_message::MessageHeader {
num_required_signatures: 1,
num_readonly_signed_accounts: 0,
num_readonly_unsigned_accounts: 0,
},
account_keys: vec![rialo_s_pubkey::Pubkey::new_unique()],
valid_from,
config_hash_prefix: ConfigHashPrefix::new(0),
occ: false,
instructions: vec![],
};
let vtx = VersionedTransaction {
signatures: vec![],
message: VersionedMessage::Legacy(message),
};
let consensus_msg = ConsensusMessage::VersionedTransaction(vtx);
bincode::serialize(&consensus_msg).expect("serialization should succeed")
}
#[cfg(any(test, feature = "testing"))]
pub fn create_rex_update_with_target_timestamp(target_timestamp: u64) -> Vec<u8> {
use crate::RexUpdateResult;
let rex_result = RexUpdateResult {
target_timestamp,
..Default::default()
};
let consensus_msg = ConsensusMessage::RexUpdateResult(Box::new(rex_result));
bincode::serialize(&consensus_msg).expect("serialization should succeed")
}
#[cfg(any(test, feature = "testing"))]
pub fn create_admin_transaction() -> Vec<u8> {
use crate::{AdminTransaction, EpochChangeConfig, EpochIdentifier};
let config = EpochChangeConfig {
current_epoch: EpochIdentifier::new(0),
new_epoch: EpochIdentifier::new(1),
validators: vec![],
consensus_config: None,
};
let admin_tx = AdminTransaction::EpochChange(Box::new(config));
let consensus_msg = ConsensusMessage::AdminTransaction(Box::new(admin_tx));
bincode::serialize(&consensus_msg).expect("serialization should succeed")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_validity_versioned_transaction() {
let valid_from: i64 = 1_000_000_000;
let payload = create_versioned_transaction_with_timestamp(valid_from);
let (from, until) = extract_validity(&payload);
assert_eq!(from, Some(valid_from as u64));
assert_eq!(
until,
Some(valid_from as u64 + TRANSACTION_VALIDITY_WINDOW_MS)
);
}
#[test]
fn test_extract_validity_rex_update() {
let target_timestamp: u64 = 1_000_000;
let payload = create_rex_update_with_target_timestamp(target_timestamp);
let (from, until) = extract_validity(&payload);
assert_eq!(from, None);
assert_eq!(until, Some(target_timestamp));
}
#[test]
fn test_extract_validity_admin_transaction() {
let payload = create_admin_transaction();
let (from, until) = extract_validity(&payload);
assert_eq!(from, None);
assert_eq!(until, None);
}
#[test]
fn test_extract_validity_invalid_payload() {
let payload = b"invalid payload";
let (from, until) = extract_validity(payload);
assert_eq!(from, None);
assert_eq!(until, None);
}
#[test]
fn test_extract_valid_from_versioned_transaction() {
let valid_from: i64 = 1_000_000_000;
let payload = create_versioned_transaction_with_timestamp(valid_from);
let result = extract_valid_from(&payload);
assert_eq!(result, Some(valid_from as u64));
}
#[test]
fn test_extract_valid_from_versioned_transaction_negative() {
let valid_from: i64 = -1000;
let payload = create_versioned_transaction_with_timestamp(valid_from);
let result = extract_valid_from(&payload);
assert_eq!(result, None);
}
#[test]
fn test_extract_valid_from_rex_update() {
let payload = create_rex_update_with_target_timestamp(1_000_000);
let result = extract_valid_from(&payload);
assert_eq!(result, None);
}
#[test]
fn test_extract_valid_from_admin_transaction() {
let payload = create_admin_transaction();
let result = extract_valid_from(&payload);
assert_eq!(result, None);
}
#[test]
fn test_extract_valid_from_invalid_payload() {
let payload = b"invalid payload";
let result = extract_valid_from(payload);
assert_eq!(result, None);
}
#[test]
fn test_extract_valid_from_accepts_far_future_timestamp() {
let year_2200: i64 = 7_258_118_400_000; let payload = create_versioned_transaction_with_timestamp(year_2200);
let result = extract_valid_from(&payload);
assert_eq!(result, Some(year_2200 as u64));
}
#[test]
fn test_extract_valid_until_versioned_transaction() {
let valid_from: i64 = 1_000_000_000;
let payload = create_versioned_transaction_with_timestamp(valid_from);
let result = extract_valid_until(&payload);
let expected = (valid_from as u64) + TRANSACTION_VALIDITY_WINDOW_MS;
assert_eq!(result, Some(expected));
}
#[test]
fn test_extract_valid_until_rex_update() {
let target_timestamp: u64 = 1_000_000;
let payload = create_rex_update_with_target_timestamp(target_timestamp);
let result = extract_valid_until(&payload);
assert_eq!(result, Some(target_timestamp));
}
#[test]
fn test_extract_valid_until_admin_transaction() {
let payload = create_admin_transaction();
let result = extract_valid_until(&payload);
assert_eq!(result, None);
}
#[test]
fn test_extract_valid_until_invalid_payload() {
let payload = b"invalid payload";
let result = extract_valid_until(payload);
assert_eq!(result, None);
}
#[test]
fn test_is_expired_past_deadline() {
let valid_until = 1000;
let now = 1001;
assert!(is_expired(valid_until, now));
}
#[test]
fn test_is_expired_at_deadline() {
let valid_until = 1000;
let now = 1000;
assert!(!is_expired(valid_until, now));
}
#[test]
fn test_is_expired_before_deadline() {
let valid_until = 1000;
let now = 999;
assert!(!is_expired(valid_until, now));
}
#[test]
fn test_is_not_yet_valid_before_valid_from() {
let valid_from = 1000;
let now = 999;
assert!(is_not_yet_valid(valid_from, now));
}
#[test]
fn test_is_not_yet_valid_at_valid_from() {
let valid_from = 1000;
let now = 1000;
assert!(!is_not_yet_valid(valid_from, now));
}
#[test]
fn test_is_not_yet_valid_after_valid_from() {
let valid_from = 1000;
let now = 1001;
assert!(!is_not_yet_valid(valid_from, now));
}
#[test]
fn test_is_too_far_in_future_within_limit() {
let now = 1_000_000;
let valid_from = now + MAX_FUTURE_VALID_FROM_MS;
assert!(!is_too_far_in_future(valid_from, now));
}
#[test]
fn test_is_too_far_in_future_past_limit() {
let now = 1_000_000;
let valid_from = now + MAX_FUTURE_VALID_FROM_MS + 1;
assert!(is_too_far_in_future(valid_from, now));
}
#[test]
fn test_is_too_far_in_future_well_within_limit() {
let now = 1_000_000;
let valid_from = now + 1000;
assert!(!is_too_far_in_future(valid_from, now));
}
#[test]
fn test_is_too_far_in_future_in_past() {
let now = 1_000_000;
let valid_from = now - 1000;
assert!(!is_too_far_in_future(valid_from, now));
}
}