From e48a26a8b5daf3128faba1fde983377bcaf91a84 Mon Sep 17 00:00:00 2001 From: Roman Zajic Date: Thu, 27 Jun 2024 09:03:59 +0200 Subject: [PATCH] chore: cryptarchia ledger unit tests update (#660) * test: conversions for LeaderProof * fix: formatting * test: ser/de for Nullifier and Commitment * test: cryptarchia ledger error cases - LedgerError::InvalidSlot * test: cryptarchia ledger error cases - LedgerError::CommitmentExists * test: add coin value check * test: add can_spend and total_stake check * fix: refactor error cases into more tests --- ledger/cryptarchia-ledger/Cargo.toml | 1 + ledger/cryptarchia-ledger/src/leader_proof.rs | 6 +- ledger/cryptarchia-ledger/src/lib.rs | 90 ++++++++++++++++++- 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/ledger/cryptarchia-ledger/Cargo.toml b/ledger/cryptarchia-ledger/Cargo.toml index fd819d29..d0b27194 100644 --- a/ledger/cryptarchia-ledger/Cargo.toml +++ b/ledger/cryptarchia-ledger/Cargo.toml @@ -10,6 +10,7 @@ blake2 = "0.10" rpds = "1" thiserror = "1" serde = { version = "1.0", features = ["derive"], optional = true } +serde_test = "1.0.176" # TODO: we only need types definition from this crate cryptarchia-engine = { path = "../../consensus/cryptarchia-engine" } nomos-utils = { path = "../../nomos-utils", optional = true } diff --git a/ledger/cryptarchia-ledger/src/leader_proof.rs b/ledger/cryptarchia-ledger/src/leader_proof.rs index c699929f..f20f2992 100644 --- a/ledger/cryptarchia-ledger/src/leader_proof.rs +++ b/ledger/cryptarchia-ledger/src/leader_proof.rs @@ -27,12 +27,12 @@ impl LeaderProof { } #[cfg(test)] - pub fn dummy(slot: Slot) -> Self { + pub fn dummy(slot: Slot, commitment: Commitment, evolved_commitment: Commitment) -> Self { Self { - commitment: Commitment([0; 32]), + commitment, nullifier: Nullifier([0; 32]), slot, - evolved_commitment: Commitment([0; 32]), + evolved_commitment, } } diff --git a/ledger/cryptarchia-ledger/src/lib.rs b/ledger/cryptarchia-ledger/src/lib.rs index 893a00e9..497c1949 100644 --- a/ledger/cryptarchia-ledger/src/lib.rs +++ b/ledger/cryptarchia-ledger/src/lib.rs @@ -386,10 +386,11 @@ impl core::fmt::Debug for LedgerState { #[cfg(test)] pub mod tests { - use super::{Coin, EpochState, Ledger, LedgerState}; + use super::{Coin, EpochState, LeaderProof, Ledger, LedgerState, Nullifier, Value}; use crate::{crypto::Blake2b, Commitment, Config, LedgerError}; use blake2::Digest; use cryptarchia_engine::Slot; + use serde_test::{assert_tokens, Configure, Token}; type HeaderId = [u8; 32]; @@ -741,4 +742,91 @@ pub mod tests { Err(LedgerError::NullifierExists) )); } + + #[test] + fn test_conversions_for_leader_proof() { + let commitment = Commitment::from([0u8; 32]); + let commitment_bytes: [u8; 32] = commitment.into(); + + let _zero_bytes = [0u8; 32]; + assert!(matches!(commitment_bytes, _zero_bytes)); + + let commitment_ref = commitment.as_ref(); + assert_eq!(commitment_ref, &_zero_bytes); + + let nullifier = Nullifier::from([0u8; 32]); + let _nullifier_bytes: [u8; 32] = nullifier.into(); + assert!(matches!(_nullifier_bytes, _zero_bytes)); + + let slot = Slot::genesis(); + let leader_proof = LeaderProof::dummy(slot, commitment, Commitment::from([0; 32])); + + assert_eq!(leader_proof.commitment(), &commitment); + assert_eq!(leader_proof.evolved_commitment(), &commitment); + assert_eq!(leader_proof.nullifier(), &nullifier); + + // Test ser/de of compact representation for Nullifier + assert_tokens(&nullifier.compact(), &[Token::BorrowedBytes(&[0; 32])]); + + // Test ser/de of compact representation for Commitment + assert_tokens(&commitment.compact(), &[Token::BorrowedBytes(&[0; 32])]); + } + + #[test] + fn test_update_epoch_state_with_outdated_slot_error() { + let coin = coin(0); + let commitment = coin.commitment(); + let (ledger, genesis) = ledger(&[commitment]); + + let ledger_state = ledger.state(&genesis).unwrap().clone(); + let ledger_config = ledger.config(); + + let slot = Slot::genesis() + 10; + let ledger_state2 = ledger_state + .update_epoch_state::(slot, ledger_config) + .expect("Ledger needs to move forward"); + + let slot2 = Slot::genesis() + 1; + let update_epoch_err = ledger_state2 + .update_epoch_state::(slot2, ledger_config) + .err(); + + // Time cannot flow backwards + match update_epoch_err { + Some(LedgerError::InvalidSlot { parent, block }) + if parent == slot && block == slot2 => {} + _ => panic!("error does not match the LedgerError::InvalidSlot pattern"), + }; + } + + #[test] + fn test_apply_proof_with_existing_commitment_error() { + let coin = coin(0); + let commitment = coin.commitment(); + let (ledger, genesis) = ledger(&[commitment]); + + let ledger_state = ledger.state(&genesis).unwrap().clone(); + let ledger_config = ledger.config(); + + let actual_slot = ledger_state.slot(); + let proof = LeaderProof::dummy(actual_slot, commitment, commitment); + let epoch_state = ledger_state.epoch_state(); + + assert!(ledger_state.can_spend(&commitment)); + assert_eq!(epoch_state.total_stake(), Value::from(1u32)); + assert_eq!(coin.value(), Value::from(1u32)); + + assert!(ledger_state.can_lead(&commitment)); + assert!(epoch_state.is_eligible_leader(&commitment)); + + let apply_proof_err = ledger_state + .try_apply_proof::(&proof, ledger_config) + .err(); + + // Same commitment value from another coin cannot be used again + assert!( + matches!(apply_proof_err, Some(LedgerError::CommitmentExists)), + "Error does not match LedgerError::CommitmentExists" + ); + } }