diff --git a/src/contract/consensus/tests/stake_unstake.rs b/src/contract/consensus/tests/stake_unstake.rs index c7a389e86..fb07d96f2 100644 --- a/src/contract/consensus/tests/stake_unstake.rs +++ b/src/contract/consensus/tests/stake_unstake.rs @@ -61,7 +61,7 @@ fn consensus_contract_stake_unstake() -> Result<()> { // Now Alice can stake her owncoin let alice_staked_oc = - th.execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_oc, 489).await?; + th.execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_oc, 21).await?; // We progress after grace period current_slot += (calculate_grace_period() * EPOCH_LENGTH) + EPOCH_LENGTH; @@ -98,7 +98,7 @@ fn consensus_contract_stake_unstake() -> Result<()> { // Now Alice can stake her unstaked owncoin again to try some mallicious cases let alice_staked_oc = th - .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 15) + .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 121) .await?; // Alice tries to stake her coin again @@ -106,7 +106,7 @@ fn consensus_contract_stake_unstake() -> Result<()> { info!(target: "consensus", "[Malicious] Checking staking coin again"); info!(target: "consensus", "[Malicious] ==========================="); let (stake_tx, _, _) = th - .stake(&Holder::Alice, current_slot, &alice_unstaked_oc, pallas::Base::from(15)) + .stake(&Holder::Alice, current_slot, &alice_unstaked_oc, pallas::Base::from(121)) .await?; th.execute_erroneous_txs( TxAction::ConsensusStake, @@ -126,14 +126,7 @@ fn consensus_contract_stake_unstake() -> Result<()> { info!(target: "consensus", "[Malicious] Checking proposal before grace period"); info!(target: "consensus", "[Malicious] ====================================="); let (proposal_tx, _, _, _) = th.proposal(&Holder::Alice, slot, &alice_staked_oc).await?; - th.execute_erroneous_txs( - TxAction::ConsensusProposal, - &Holder::Alice, - &[proposal_tx], - current_slot, - 1, - ) - .await?; + th.execute_erroneous_proposal_tx(&Holder::Alice, &proposal_tx, current_slot).await?; // or be able to unstake the coin info!(target: "consensus", "[Malicious] ======================================"); @@ -195,7 +188,7 @@ fn consensus_contract_stake_unstake() -> Result<()> { // Now Alice can stake her unstaked owncoin again let alice_staked_oc = th - .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 65) + .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 181) .await?; // We progress after grace period @@ -214,14 +207,7 @@ fn consensus_contract_stake_unstake() -> Result<()> { info!(target: "consensus", "[Malicious] Checking using unstaked coin in proposal"); info!(target: "consensus", "[Malicious] ========================================"); let (proposal_tx, _, _, _) = th.proposal(&Holder::Alice, slot, &alice_staked_oc).await?; - th.execute_erroneous_txs( - TxAction::ConsensusProposal, - &Holder::Alice, - &[proposal_tx], - current_slot, - 1, - ) - .await?; + th.execute_erroneous_proposal_tx(&Holder::Alice, &proposal_tx, current_slot).await?; // We progress after grace period current_slot += (calculate_grace_period() * EPOCH_LENGTH) + EPOCH_LENGTH; diff --git a/src/contract/test-harness/src/consensus_genesis_stake.rs b/src/contract/test-harness/src/consensus_genesis_stake.rs index 073929946..2a5ea92c9 100644 --- a/src/contract/test-harness/src/consensus_genesis_stake.rs +++ b/src/contract/test-harness/src/consensus_genesis_stake.rs @@ -62,7 +62,7 @@ impl TestHarness { pallas::Scalar::random(&mut OsRng), pallas::Base::random(&mut OsRng), pallas::Scalar::random(&mut OsRng), - pallas::Base::from(138), + pallas::Base::from(4), )?; let (genesis_stake_params, genesis_stake_proofs) = diff --git a/src/contract/test-harness/src/consensus_proposal.rs b/src/contract/test-harness/src/consensus_proposal.rs index 51bbd6e3a..1c1e5564d 100644 --- a/src/contract/test-harness/src/consensus_proposal.rs +++ b/src/contract/test-harness/src/consensus_proposal.rs @@ -111,7 +111,7 @@ impl TestHarness { let timer = Instant::now(); - wallet.validator.read().await.add_transactions(&[tx.clone()], slot, true).await?; + wallet.validator.read().await.add_test_producer_transaction(tx, slot, true).await?; wallet.consensus_staked_merkle_tree.append(MerkleNode::from(params.output.coin.inner())); tx_action_benchmark.verify_times.push(timer.elapsed()); @@ -138,9 +138,9 @@ impl TestHarness { ) = self.proposal(holder, slot, staked_oc).await?; for h in holders { - info!(target: "consensus", "[{h:?}] ==========================="); + info!(target: "consensus", "[{h:?}] ================================"); info!(target: "consensus", "[{h:?}] Executing {holder:?} proposal tx"); - info!(target: "consensus", "[{h:?}] ==========================="); + info!(target: "consensus", "[{h:?}] ================================"); self.execute_proposal_tx(h, &proposal_tx, &proposal_params, current_slot).await?; } @@ -158,4 +158,27 @@ impl TestHarness { Ok(rewarded_staked_oc) } + + pub async fn execute_erroneous_proposal_tx( + &mut self, + holder: &Holder, + tx: &Transaction, + slot: u64, + ) -> Result<()> { + let wallet = self.holders.get_mut(holder).unwrap(); + let tx_action_benchmark = + self.tx_action_benchmarks.get_mut(&TxAction::ConsensusProposal).unwrap(); + let timer = Instant::now(); + + assert!(wallet + .validator + .read() + .await + .add_test_producer_transaction(tx, slot, true) + .await + .is_err()); + tx_action_benchmark.verify_times.push(timer.elapsed()); + + Ok(()) + } } diff --git a/src/validator/mod.rs b/src/validator/mod.rs index f59b1b30b..8da21d45c 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -43,7 +43,9 @@ pub mod pid; /// Verification functions pub mod verification; -use verification::{verify_block, verify_genesis_block, verify_transactions}; +use verification::{ + verify_block, verify_genesis_block, verify_producer_transaction, verify_transactions, +}; /// Validation functions pub mod validation; @@ -393,6 +395,54 @@ impl Validator { Ok(()) } + /// Validate a producer `Transaction` and apply it if valid. + /// In case the transactions fail, ir will be returned to the caller. + /// The function takes a boolean called `write` which tells it to actually write + /// the state transitions to the database. + /// This should be only used for test purposes. + pub async fn add_test_producer_transaction( + &self, + tx: &Transaction, + verifying_slot: u64, + write: bool, + ) -> Result<()> { + debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay"); + let overlay = BlockchainOverlay::new(&self.blockchain)?; + + // Generate a time keeper using transaction verifying slot + let time_keeper = TimeKeeper::new( + self.consensus.time_keeper.genesis_ts, + self.consensus.time_keeper.epoch_length, + self.consensus.time_keeper.slot_time, + verifying_slot, + ); + + // Verify transaction + let mut erroneous_txs = vec![]; + if let Err(e) = verify_producer_transaction(&overlay, &time_keeper, tx).await { + warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {}", e); + erroneous_txs.push(tx.clone()); + } + + let lock = overlay.lock().unwrap(); + let mut overlay = lock.overlay.lock().unwrap(); + if !erroneous_txs.is_empty() { + warn!(target: "validator::add_test_producer_transaction", "Erroneous transactions found in set"); + overlay.purge_new_trees()?; + return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into()) + } + + if !write { + debug!(target: "validator::add_test_producer_transaction", "Skipping apply of state updates because write=false"); + overlay.purge_new_trees()?; + return Ok(()) + } + + debug!(target: "validator::add_test_producer_transaction", "Applying overlay changes"); + overlay.apply()?; + Ok(()) + } + /// Retrieve all existing blocks and try to apply them /// to an in memory overlay to verify their correctness. /// Be careful as this will try to load everything in memory.