Просмотр исходного кода

util/time/TimeKeeper: added verifying slot attribute that can be used by the runtime for validations

aggstam 3 лет назад
Родитель
Сommit
1bd504eaa0

+ 6 - 3
bin/darkfid/src/rpc_tx.rs

@@ -64,7 +64,9 @@ impl Darkfid {
         };
 
         // Simulate state transition
-        match self.validator_state.read().await.verify_transactions(&[tx], false).await {
+        let lock = self.validator_state.read().await;
+        let current_slot = lock.consensus.time_keeper.current_slot();
+        match lock.verify_transactions(&[tx], current_slot, false).await {
             Ok(erroneous_txs) => {
                 if !erroneous_txs.is_empty() {
                     error!("[RPC] tx.simulate: invalid transaction provided");
@@ -124,8 +126,9 @@ impl Darkfid {
             }
         } else {
             // We'll perform the state transition check here.
-            match self.validator_state.read().await.verify_transactions(&[tx.clone()], false).await
-            {
+            let lock = self.validator_state.read().await;
+            let current_slot = lock.consensus.time_keeper.current_slot();
+            match lock.verify_transactions(&[tx.clone()], current_slot, false).await {
                 Ok(erroneous_txs) => {
                     if !erroneous_txs.is_empty() {
                         error!("[RPC] tx.broadcast: invalid transaction provided");

+ 3 - 3
bin/faucetd/src/main.rs

@@ -573,9 +573,9 @@ impl Faucetd {
         tx.signatures = vec![sigs];
 
         // Safety check to see if the transaction is actually valid.
-        if let Err(e) =
-            self.validator_state.read().await.verify_transactions(&[tx.clone()], false).await
-        {
+        let lock = self.validator_state.read().await;
+        let current_slot = lock.consensus.time_keeper.current_slot();
+        if let Err(e) = lock.verify_transactions(&[tx.clone()], current_slot, false).await {
             error!("airdrop(): Failed to verify transaction before broadcasting: {}", e);
             return JsonError::new(InternalError, None, id).into()
         }

+ 1 - 1
src/consensus/state.rs

@@ -103,7 +103,7 @@ impl ConsensusState {
     ) -> Self {
         let genesis_block = Block::genesis_block(genesis_ts, genesis_data).blockhash();
         let time_keeper =
-            TimeKeeper::new(genesis_ts, constants::EPOCH_LENGTH as u64, constants::SLOT_TIME);
+            TimeKeeper::new(genesis_ts, constants::EPOCH_LENGTH as u64, constants::SLOT_TIME, 0);
         Self {
             wallet,
             blockchain,

+ 40 - 10
src/consensus/validator.rs

@@ -48,7 +48,7 @@ use crate::{
     runtime::vm_runtime::Runtime,
     system::{Subscriber, SubscriberPtr},
     tx::Transaction,
-    util::time::Timestamp,
+    util::time::{TimeKeeper, Timestamp},
     wallet::WalletPtr,
     zk::{
         proof::{ProvingKey, VerifyingKey},
@@ -238,7 +238,10 @@ impl ValidatorState {
         }
 
         info!(target: "consensus::validator", "append_tx(): Starting state transition validation");
-        match self.verify_transactions(&[tx.clone()], false).await {
+        match self
+            .verify_transactions(&[tx.clone()], self.consensus.time_keeper.current_slot(), false)
+            .await
+        {
             Ok(erroneous_txs) => {
                 if !erroneous_txs.is_empty() {
                     error!(target: "consensus::validator", "append_tx(): Erroneous transaction detected");
@@ -292,7 +295,14 @@ impl ValidatorState {
 
         // Verify transactions and filter erroneous ones
         info!(target: "consensus::validator", "append_pending_txs(): Starting state transition validation");
-        let erroneous_txs = match self.verify_transactions(&filtered_txs[..], false).await {
+        let erroneous_txs = match self
+            .verify_transactions(
+                &filtered_txs[..],
+                self.consensus.time_keeper.current_slot(),
+                false,
+            )
+            .await
+        {
             Ok(erroneous_txs) => erroneous_txs,
             Err(e) => {
                 error!(target: "consensus::validator", "append_pending_txs(): Failed to verify transactions: {}", e);
@@ -318,7 +328,9 @@ impl ValidatorState {
             info!(target: "consensus::validator", "purge_pending_txs(): No pending transactions found");
             return Ok(())
         }
-        let erroneous_txs = self.verify_transactions(&pending_txs[..], false).await?;
+        let erroneous_txs = self
+            .verify_transactions(&pending_txs[..], self.consensus.time_keeper.current_slot(), false)
+            .await?;
         if erroneous_txs.is_empty() {
             info!(target: "consensus::validator", "purge_pending_txs(): No erroneous transactions found");
             return Ok(())
@@ -359,7 +371,13 @@ impl ValidatorState {
         // Generate proposal
         let mut unproposed_txs = self.unproposed_txs(fork_index)?;
         // Verify transactions and filter erroneous ones
-        let erroneous_txs = self.verify_transactions(&unproposed_txs[..], false).await?;
+        let erroneous_txs = self
+            .verify_transactions(
+                &unproposed_txs[..],
+                self.consensus.time_keeper.current_slot(),
+                false,
+            )
+            .await?;
         if !erroneous_txs.is_empty() {
             unproposed_txs.retain(|x| !erroneous_txs.contains(x));
         }
@@ -642,7 +660,7 @@ impl ValidatorState {
         // Validate state transition against canonical state
         // TODO: This should be validated against fork state
         info!(target: "consensus::validator", "receive_proposal(): Starting state transition validation");
-        match self.verify_transactions(&proposal.block.txs, false).await {
+        match self.verify_transactions(&proposal.block.txs, current, false).await {
             Ok(erroneous_txs) => {
                 if !erroneous_txs.is_empty() {
                     error!(target: "consensus::validator", "Proposal contains erroneous transactions");
@@ -776,7 +794,7 @@ impl ValidatorState {
             // TODO: FIXME: The state transitions have already been written, they have to be in memory
             //              until this point.
             info!(target: "consensus::validator", "Applying state transition for finalized block");
-            match self.verify_transactions(&proposal.txs, true).await {
+            match self.verify_transactions(&proposal.txs, proposal.header.slot, true).await {
                 Ok(erroneous_txs) => {
                     if !erroneous_txs.is_empty() {
                         error!(target: "consensus::validator", "Finalized block contains erroneous transactions");
@@ -861,7 +879,7 @@ impl ValidatorState {
         info!(target: "consensus::validator", "receive_blocks(): Starting state transition validations");
 
         for block in blocks {
-            match self.verify_transactions(&block.txs, true).await {
+            match self.verify_transactions(&block.txs, block.header.slot, true).await {
                 Ok(erroneous_txs) => {
                     if !erroneous_txs.is_empty() {
                         error!(target: "consensus::validator", "receive_blocks(): Block contains erroneous transactions");
@@ -973,6 +991,7 @@ impl ValidatorState {
         &self,
         blockchain_overlay: BlockchainOverlayPtr,
         tx: &Transaction,
+        verifying_slot: u64,
     ) -> Result<()> {
         let mut runtimes = HashMap::new();
         let tx_hash = blake3::hash(&serialize(tx));
@@ -992,6 +1011,14 @@ impl ValidatorState {
             verifying_keys.insert(call.contract_id.to_bytes(), HashMap::new());
         }
 
+        // 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,
+        );
+
         // Iterate over all calls to get the metadata
         for (idx, call) in tx.calls.iter().enumerate() {
             info!(target: "consensus::validator", "Executing contract call {}", idx);
@@ -1009,7 +1036,7 @@ impl ValidatorState {
                     &wasm,
                     blockchain_overlay.clone(),
                     call.contract_id,
-                    self.consensus.time_keeper.clone(),
+                    time_keeper.clone(),
                 )?;
                 runtimes.insert(runtime_key.clone(), r);
             }
@@ -1116,6 +1143,7 @@ impl ValidatorState {
     pub async fn verify_transactions(
         &self,
         txs: &[Transaction],
+        verifying_slot: u64,
         write: bool,
     ) -> Result<Vec<Transaction>> {
         info!(target: "consensus::validator", "Verifying {} transaction(s)", txs.len());
@@ -1124,7 +1152,9 @@ impl ValidatorState {
         let blockchain_overlay = BlockchainOverlay::new(&self.blockchain)?;
 
         for tx in txs {
-            if let Err(e) = self.verify_transaction(blockchain_overlay.clone(), tx).await {
+            if let Err(e) =
+                self.verify_transaction(blockchain_overlay.clone(), tx, verifying_slot).await
+            {
                 warn!(target: "consensus::validator", "Transaction verification failed: {}", e);
                 erroneous_txs.push(tx.clone());
             }

+ 54 - 16
src/contract/consensus/tests/stake_unstake.rs

@@ -82,6 +82,9 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     // Some numbers we want to assert
     const ALICE_AIRDROP: u64 = 1000;
 
+    // Slot to verify against
+    let current_slot = 0;
+
     // Initialize harness
     let mut th = ConsensusTestHarness::new().await?;
     info!(target: "consensus", "[Faucet] ===================================================");
@@ -100,15 +103,25 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Faucet] ==========================");
     info!(target: "consensus", "[Faucet] Executing Alice airdrop tx");
     info!(target: "consensus", "[Faucet] ==========================");
-    let erroneous_txs =
-        th.faucet.state.read().await.verify_transactions(&[airdrop_tx.clone()], true).await?;
+    let erroneous_txs = th
+        .faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[airdrop_tx.clone()], current_slot, true)
+        .await?;
     assert!(erroneous_txs.is_empty());
     th.faucet.merkle_tree.append(&MerkleNode::from(airdrop_params.outputs[0].coin.inner()));
     info!(target: "consensus", "[Alice] ==========================");
     info!(target: "consensus", "[Alice] Executing Alice airdrop tx");
     info!(target: "consensus", "[Alice] ==========================");
-    let erroneous_txs =
-        th.alice.state.read().await.verify_transactions(&[airdrop_tx.clone()], true).await?;
+    let erroneous_txs = th
+        .alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[airdrop_tx.clone()], current_slot, true)
+        .await?;
     assert!(erroneous_txs.is_empty());
     th.alice.merkle_tree.append(&MerkleNode::from(airdrop_params.outputs[0].coin.inner()));
 
@@ -198,8 +211,13 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Faucet] Executing Alice stake tx");
     info!(target: "consensus", "[Faucet] ========================");
     let timer = Instant::now();
-    let erroneous_txs =
-        th.faucet.state.read().await.verify_transactions(&[alice_stake_tx.clone()], true).await?;
+    let erroneous_txs = th
+        .faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_stake_tx.clone()], current_slot, true)
+        .await?;
     assert!(erroneous_txs.is_empty());
     th.faucet
         .consensus_merkle_tree
@@ -210,8 +228,13 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Alice] Executing Alice stake tx");
     info!(target: "consensus", "[Alice] ========================");
     let timer = Instant::now();
-    let erroneous_txs =
-        th.alice.state.read().await.verify_transactions(&[alice_stake_tx.clone()], true).await?;
+    let erroneous_txs = th
+        .alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_stake_tx.clone()], current_slot, true)
+        .await?;
     assert!(erroneous_txs.is_empty());
     th.alice
         .consensus_merkle_tree
@@ -241,7 +264,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
 
     // We simulate the proposal of genesis slot
     let slot_checkpoint =
-        th.alice.state.read().await.blockchain.get_slot_checkpoints_by_slot(&[0])?[0]
+        th.alice.state.read().await.blockchain.get_slot_checkpoints_by_slot(&[current_slot])?[0]
             .clone()
             .unwrap();
 
@@ -332,7 +355,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
         .state
         .read()
         .await
-        .verify_transactions(&[alice_proposal_tx.clone()], true)
+        .verify_transactions(&[alice_proposal_tx.clone()], current_slot, true)
         .await?;
     assert!(erroneous_txs.is_empty());
     th.faucet
@@ -344,8 +367,13 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Alice] Executing Alice proposal tx");
     info!(target: "consensus", "[Alice] ===========================");
     let timer = Instant::now();
-    let erroneous_txs =
-        th.alice.state.read().await.verify_transactions(&[alice_proposal_tx.clone()], true).await?;
+    let erroneous_txs = th
+        .alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_proposal_tx.clone()], current_slot, true)
+        .await?;
     assert!(erroneous_txs.is_empty());
     th.alice
         .consensus_merkle_tree
@@ -447,8 +475,13 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Faucet] Executing Alice unstake tx");
     info!(target: "consensus", "[Faucet] ==========================");
     let timer = Instant::now();
-    let erroneous_txs =
-        th.faucet.state.read().await.verify_transactions(&[alice_unstake_tx.clone()], true).await?;
+    let erroneous_txs = th
+        .faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_unstake_tx.clone()], current_slot, true)
+        .await?;
     assert!(erroneous_txs.is_empty());
     th.faucet.merkle_tree.append(&MerkleNode::from(alice_money_unstake_params.output.coin.inner()));
     unstake_verify_times.push(timer.elapsed());
@@ -457,8 +490,13 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Alice] Executing Alice unstake tx");
     info!(target: "consensus", "[Alice] ==========================");
     let timer = Instant::now();
-    let erroneous_txs =
-        th.alice.state.read().await.verify_transactions(&[alice_unstake_tx.clone()], true).await?;
+    let erroneous_txs = th
+        .alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_unstake_tx.clone()], current_slot, true)
+        .await?;
     assert!(erroneous_txs.is_empty());
     th.alice.merkle_tree.append(&MerkleNode::from(alice_money_unstake_params.output.coin.inner()));
     unstake_verify_times.push(timer.elapsed());

+ 11 - 8
src/contract/dao/tests/integration.rs

@@ -65,6 +65,9 @@ async fn integration_test() -> Result<()> {
     let mut vote_verify_times = vec![];
     let mut exec_verify_times = vec![];
 
+    // Slot to verify against
+    let current_slot = 0;
+
     let dao_th = DaoTestHarness::new().await?;
 
     // Money parameters
@@ -113,7 +116,7 @@ async fn integration_test() -> Result<()> {
     tx.signatures = vec![sigs];
 
     let timer = Instant::now();
-    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
+    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
     mint_verify_times.push(timer.elapsed());
     // TODO: Witness and add to wallet merkle tree?
 
@@ -185,7 +188,7 @@ async fn integration_test() -> Result<()> {
     let sigs = tx.create_sigs(&mut OsRng, &vec![dao_th.faucet_kp.secret])?;
     tx.signatures = vec![sigs];
 
-    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
+    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
 
     // Wallet stuff
 
@@ -300,7 +303,7 @@ async fn integration_test() -> Result<()> {
         .alice_state
         .read()
         .await
-        .verify_transactions(&[tx1.clone(), tx2.clone(), tx3.clone()], true)
+        .verify_transactions(&[tx1.clone(), tx2.clone(), tx3.clone()], current_slot, true)
         .await?;
 
     // Wallet
@@ -432,7 +435,7 @@ async fn integration_test() -> Result<()> {
     tx.signatures = vec![sigs];
 
     let timer = Instant::now();
-    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
+    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
     propose_verify_times.push(timer.elapsed());
 
     //// Wallet
@@ -539,7 +542,7 @@ async fn integration_test() -> Result<()> {
     tx.signatures = vec![sigs];
 
     let timer = Instant::now();
-    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
+    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
     vote_verify_times.push(timer.elapsed());
 
     // Secret vote info. Needs to be revealed at some point.
@@ -609,7 +612,7 @@ async fn integration_test() -> Result<()> {
     tx.signatures = vec![sigs];
 
     let timer = Instant::now();
-    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
+    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
     vote_verify_times.push(timer.elapsed());
 
     let vote_note_2 = {
@@ -676,7 +679,7 @@ async fn integration_test() -> Result<()> {
     tx.signatures = vec![sigs];
 
     let timer = Instant::now();
-    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
+    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
     vote_verify_times.push(timer.elapsed());
 
     // Secret vote info. Needs to be revealed at some point.
@@ -869,7 +872,7 @@ async fn integration_test() -> Result<()> {
     tx.signatures = vec![xfer_sigs, exec_sigs];
 
     let timer = Instant::now();
-    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
+    dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
     exec_verify_times.push(timer.elapsed());
 
     // Statistics

+ 75 - 12
src/contract/money/tests/integration.rs

@@ -46,6 +46,9 @@ use harness::{init_logger, MoneyTestHarness};
 async fn money_integration() -> Result<()> {
     init_logger();
 
+    // Slot to verify against
+    let current_slot = 0;
+
     let mut th = MoneyTestHarness::new().await?;
 
     // Let's first airdrop some tokens to Alice.
@@ -53,21 +56,41 @@ async fn money_integration() -> Result<()> {
         th.airdrop_native(200, th.alice.keypair.public)?;
 
     info!("[Faucet] Executing Alice airdrop tx");
-    th.faucet.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_airdrop_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
 
     info!("[Alice] Executing Alice airdrop tx");
-    th.alice.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_airdrop_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
     // Alice has to witness this coin because it's hers.
     let leaf_position = th.alice.merkle_tree.witness().unwrap();
 
     info!("[Bob] Executing Alice airdrop tx");
-    th.bob.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_airdrop_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
 
     info!("[Charlie] Executing Alice airdrop tx");
-    th.charlie.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
+    th.charlie
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_airdrop_tx.clone()], current_slot, true)
+        .await?;
     th.charlie.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
 
     assert_eq!(th.alice.merkle_tree.root(0).unwrap(), th.bob.merkle_tree.root(0).unwrap());
@@ -91,19 +114,39 @@ async fn money_integration() -> Result<()> {
         th.mint_token(bob_token_authority, 500, th.charlie.keypair.public)?;
 
     info!("[Faucet] Executing BOBTOKEN mint to Charlie");
-    th.faucet.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_charlie_mint_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
 
     info!("[Alice] Executing BOBTOKEN mint to Charlie");
-    th.alice.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_charlie_mint_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
 
     info!("[Bob] Executing BOBTOKEN mint to Charlie");
-    th.bob.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_charlie_mint_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
 
     info!("[Charlie] Executing BOBTOKEN mint to Charlie");
-    th.charlie.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
+    th.charlie
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_charlie_mint_tx.clone()], current_slot, true)
+        .await?;
     th.charlie.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
     // Charlie has to witness this coin because it's his.
     let leaf_position = th.charlie.merkle_tree.witness().unwrap();
@@ -130,16 +173,36 @@ async fn money_integration() -> Result<()> {
     let (bob_frz_tx, _) = th.freeze_token(bob_token_authority)?;
 
     info!("[Faucet] Executing BOBTOKEN freeze");
-    th.faucet.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_frz_tx.clone()], current_slot, true)
+        .await?;
 
     info!("[Alice] Executing BOBTOKEN freeze");
-    th.alice.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_frz_tx.clone()], current_slot, true)
+        .await?;
 
     info!("[Bob] Executing BOBTOKEN freeze");
-    th.bob.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_frz_tx.clone()], current_slot, true)
+        .await?;
 
     info!("[Charlie] Executing BOBTOKEN freeze");
-    th.charlie.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
+    th.charlie
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_frz_tx.clone()], current_slot, true)
+        .await?;
 
     // Thanks for reading
     Ok(())

+ 147 - 24
src/contract/money/tests/mint_pay_swap.rs

@@ -80,6 +80,9 @@ async fn money_contract_transfer() -> Result<()> {
     // Bob = 20 BOB + 50 ALICE
     const BOB_FIRST_SEND: u64 = BOB_INITIAL - 20;
 
+    // Slot to verify against
+    let current_slot = 0;
+
     // Initialize harness
     let mut th = MoneyTestHarness::new().await?;
     let (mint_pk, mint_zkbin) = th.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
@@ -130,7 +133,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing Alice token mint tx");
     info!(target: "money", "[Faucet] =============================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[alice_mint_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_mint_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(alice_params.output.coin.inner()));
     mint_verify_times.push(timer.elapsed());
 
@@ -138,7 +146,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing Bob token mint tx");
     info!(target: "money", "[Faucet] ===========================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[bob_mint_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_mint_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(bob_params.output.coin.inner()));
     mint_verify_times.push(timer.elapsed());
 
@@ -146,7 +159,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing Alice token mint tx");
     info!(target: "money", "[Alice] =============================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[alice_mint_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_mint_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(alice_params.output.coin.inner()));
     // Alice has to witness this coin because it's hers.
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
@@ -156,7 +174,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing Bob token mint tx");
     info!(target: "money", "[Alice] ===========================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[bob_mint_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_mint_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(bob_params.output.coin.inner()));
     mint_verify_times.push(timer.elapsed());
 
@@ -164,7 +187,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing Alice token mint tx");
     info!(target: "money", "[Bob] =============================");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[alice_mint_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_mint_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(alice_params.output.coin.inner()));
     mint_verify_times.push(timer.elapsed());
 
@@ -172,7 +200,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing Bob token mint tx");
     info!(target: "money", "[Bob] ===========================");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[bob_mint_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob_mint_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(bob_params.output.coin.inner()));
     let bob_leaf_pos = th.bob.merkle_tree.witness().unwrap();
     mint_verify_times.push(timer.elapsed());
@@ -266,7 +299,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing Alice2Bob payment tx");
     info!(target: "money", "[Faucet] ==============================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[alice2bob_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice2bob_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[0].coin.inner()));
     th.faucet.merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[1].coin.inner()));
     transfer_verify_times.push(timer.elapsed());
@@ -275,7 +313,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing Alice2Bob payment tx");
     info!(target: "money", "[Alice] ==============================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[alice2bob_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice2bob_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[0].coin.inner()));
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
     th.alice.merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[1].coin.inner()));
@@ -285,7 +328,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing Alice2Bob payment tx");
     info!(target: "money", "[Bob] ==============================");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[alice2bob_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice2bob_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[0].coin.inner()));
     th.bob.merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[1].coin.inner()));
     let bob_leaf_pos = th.bob.merkle_tree.witness().unwrap();
@@ -383,7 +431,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing Bob2Alice payment tx");
     info!(target: "money", "[Faucet] ==============================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[bob2alice_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob2alice_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[0].coin.inner()));
     th.faucet.merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[1].coin.inner()));
     transfer_verify_times.push(timer.elapsed());
@@ -392,7 +445,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing Bob2Alice payment tx");
     info!(target: "money", "[Alice] ==============================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[bob2alice_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob2alice_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[0].coin.inner()));
     th.alice.merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[1].coin.inner()));
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
@@ -402,7 +460,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing Bob2Alice payment tx");
     info!(target: "money", "[Bob] ==================+===========");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[bob2alice_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob2alice_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[0].coin.inner()));
     let bob_leaf_pos = th.bob.merkle_tree.witness().unwrap();
     th.bob.merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[1].coin.inner()));
@@ -559,7 +622,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing AliceBob swap tx");
     info!(target: "money", "[Faucet] ==========================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alicebob_swap_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin.inner()));
     th.faucet.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin.inner()));
     swap_verify_times.push(timer.elapsed());
@@ -568,7 +636,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing AliceBob swap tx");
     info!(target: "money", "[Alice] ==========================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alicebob_swap_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin.inner()));
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
     th.alice.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin.inner()));
@@ -578,7 +651,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing AliceBob swap tx");
     info!(target: "money", "[Bob] ==========================");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alicebob_swap_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin.inner()));
     th.bob.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin.inner()));
     let bob_leaf_pos = th.bob.merkle_tree.witness().unwrap();
@@ -680,7 +758,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing Alice2Alice payment tx");
     info!(target: "money", "[Faucet] ================================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[alice2alice_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice2alice_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(alice2alice_params.outputs[0].coin.inner()));
     transfer_verify_times.push(timer.elapsed());
 
@@ -688,7 +771,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing Alice2Alice payment tx");
     info!(target: "money", "[Alice] ================================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[alice2alice_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice2alice_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(alice2alice_params.outputs[0].coin.inner()));
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
     transfer_verify_times.push(timer.elapsed());
@@ -697,7 +785,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing Alice2Alice payment tx");
     info!(target: "money", "[Bob] ================================");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[alice2alice_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice2alice_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(alice2alice_params.outputs[0].coin.inner()));
     transfer_verify_times.push(timer.elapsed());
 
@@ -782,7 +875,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing Bob2Bob payment tx");
     info!(target: "money", "[Faucet] ============================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[bob2bob_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob2bob_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(bob2bob_params.outputs[0].coin.inner()));
     transfer_verify_times.push(timer.elapsed());
 
@@ -790,7 +888,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing Bob2Bob payment tx");
     info!(target: "money", "[Alice] ============================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[bob2bob_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob2bob_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(bob2bob_params.outputs[0].coin.inner()));
     transfer_verify_times.push(timer.elapsed());
 
@@ -798,7 +901,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing Bob2Bob payment tx");
     info!(target: "money", "[Bob] ============================");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[bob2bob_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[bob2bob_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(bob2bob_params.outputs[0].coin.inner()));
     let bob_leaf_pos = th.bob.merkle_tree.witness().unwrap();
     transfer_verify_times.push(timer.elapsed());
@@ -932,7 +1040,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Faucet] Executing AliceBob swap tx");
     info!(target: "money", "[Faucet] ==========================");
     let timer = Instant::now();
-    th.faucet.state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alicebob_swap_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin.inner()));
     th.faucet.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin.inner()));
     swap_verify_times.push(timer.elapsed());
@@ -941,7 +1054,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Alice] Executing AliceBob swap tx");
     info!(target: "money", "[Alice] ==========================");
     let timer = Instant::now();
-    th.alice.state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alicebob_swap_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin.inner()));
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
     th.alice.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin.inner()));
@@ -951,7 +1069,12 @@ async fn money_contract_transfer() -> Result<()> {
     info!(target: "money", "[Bob] Executing AliceBob swap tx");
     info!(target: "money", "[Bob] ==========================");
     let timer = Instant::now();
-    th.bob.state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alicebob_swap_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin.inner()));
     th.bob.merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin.inner()));
     let bob_leaf_pos = th.bob.merkle_tree.witness().unwrap();

+ 46 - 12
src/contract/money/tests/txs_verification.rs

@@ -56,6 +56,9 @@ async fn txs_verification() -> Result<()> {
     // Bob = 50 ALICE
     const ALICE_FIRST_SEND: u64 = ALICE_INITIAL - 50;
 
+    // Slot to verify against
+    let current_slot = 0;
+
     // Initialize harness
     let mut th = MoneyTestHarness::new().await?;
     let (mint_pk, mint_zkbin) = th.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
@@ -82,13 +85,23 @@ async fn txs_verification() -> Result<()> {
     info!(target: "money", "[Faucet] =============================");
     info!(target: "money", "[Faucet] Executing Alice token mint tx");
     info!(target: "money", "[Faucet] =============================");
-    th.faucet.state.read().await.verify_transactions(&[alice_mint_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_mint_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(alice_params.output.coin.inner()));
 
     info!(target: "money", "[Alice] =============================");
     info!(target: "money", "[Alice] Executing Alice token mint tx");
     info!(target: "money", "[Alice] =============================");
-    th.alice.state.read().await.verify_transactions(&[alice_mint_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_mint_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(alice_params.output.coin.inner()));
     // Alice has to witness this coin because it's hers.
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
@@ -96,7 +109,12 @@ async fn txs_verification() -> Result<()> {
     info!(target: "money", "[Bob] =============================");
     info!(target: "money", "[Bob] Executing Alice token mint tx");
     info!(target: "money", "[Bob] =============================");
-    th.bob.state.read().await.verify_transactions(&[alice_mint_tx.clone()], true).await?;
+    th.bob
+        .state
+        .read()
+        .await
+        .verify_transactions(&[alice_mint_tx.clone()], current_slot, true)
+        .await?;
     th.bob.merkle_tree.append(&MerkleNode::from(alice_params.output.coin.inner()));
 
     assert!(th.alice.merkle_tree.root(0).unwrap() == th.bob.merkle_tree.root(0).unwrap());
@@ -170,17 +188,32 @@ async fn txs_verification() -> Result<()> {
         info!(target: "money", "[Faucet] ==================================");
         info!(target: "money", "[Faucet] Verifying Alice2Bob payment tx {i}");
         info!(target: "money", "[Faucet] ==================================");
-        th.faucet.state.read().await.verify_transactions(&[alice2bob_tx.clone()], false).await?;
+        th.faucet
+            .state
+            .read()
+            .await
+            .verify_transactions(&[alice2bob_tx.clone()], current_slot, false)
+            .await?;
 
         info!(target: "money", "[Alice] ==================================");
         info!(target: "money", "[Alice] Verifying Alice2Bob payment tx {i}");
         info!(target: "money", "[Alice] ==================================");
-        th.alice.state.read().await.verify_transactions(&[alice2bob_tx.clone()], false).await?;
+        th.alice
+            .state
+            .read()
+            .await
+            .verify_transactions(&[alice2bob_tx.clone()], current_slot, false)
+            .await?;
 
         info!(target: "money", "[Bob] ==================================");
         info!(target: "money", "[Bob] Verifying Alice2Bob payment tx {i}");
         info!(target: "money", "[Bob] ==================================");
-        th.bob.state.read().await.verify_transactions(&[alice2bob_tx.clone()], false).await?;
+        th.bob
+            .state
+            .read()
+            .await
+            .verify_transactions(&[alice2bob_tx.clone()], current_slot, false)
+            .await?;
 
         transactions.push(alice2bob_tx);
         txs_params.push(alice2bob_params);
@@ -197,9 +230,9 @@ async fn txs_verification() -> Result<()> {
     info!(target: "money", "[Faucet] Executing Alice2Bob payment tx");
     info!(target: "money", "[Faucet] ==============================");
     let erroneous_txs =
-        th.faucet.state.read().await.verify_transactions(&transactions, true).await?;
+        th.faucet.state.read().await.verify_transactions(&transactions, current_slot, true).await?;
     assert_eq!(erroneous_txs.len(), duplicates - 1);
-    th.faucet.state.read().await.verify_transactions(&valid_txs, true).await?;
+    th.faucet.state.read().await.verify_transactions(&valid_txs, current_slot, true).await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(txs_params[0].outputs[0].coin.inner()));
     th.faucet.merkle_tree.append(&MerkleNode::from(txs_params[0].outputs[1].coin.inner()));
 
@@ -207,9 +240,9 @@ async fn txs_verification() -> Result<()> {
     info!(target: "money", "[Alice] Executing Alice2Bob payment tx");
     info!(target: "money", "[Alice] ==============================");
     let erroneous_txs =
-        th.alice.state.read().await.verify_transactions(&transactions, true).await?;
+        th.alice.state.read().await.verify_transactions(&transactions, current_slot, true).await?;
     assert_eq!(erroneous_txs.len(), duplicates - 1);
-    th.alice.state.read().await.verify_transactions(&valid_txs, true).await?;
+    th.alice.state.read().await.verify_transactions(&valid_txs, current_slot, true).await?;
     th.alice.merkle_tree.append(&MerkleNode::from(txs_params[0].outputs[0].coin.inner()));
     let alice_leaf_pos = th.alice.merkle_tree.witness().unwrap();
     th.alice.merkle_tree.append(&MerkleNode::from(txs_params[0].outputs[1].coin.inner()));
@@ -217,9 +250,10 @@ async fn txs_verification() -> Result<()> {
     info!(target: "money", "[Bob] ==============================");
     info!(target: "money", "[Bob] Executing Alice2Bob payment tx");
     info!(target: "money", "[Bob] ==============================");
-    let erroneous_txs = th.bob.state.read().await.verify_transactions(&transactions, true).await?;
+    let erroneous_txs =
+        th.bob.state.read().await.verify_transactions(&transactions, current_slot, true).await?;
     assert_eq!(erroneous_txs.len(), duplicates - 1);
-    th.bob.state.read().await.verify_transactions(&valid_txs, true).await?;
+    th.bob.state.read().await.verify_transactions(&valid_txs, current_slot, true).await?;
     th.bob.merkle_tree.append(&MerkleNode::from(txs_params[0].outputs[0].coin.inner()));
     th.bob.merkle_tree.append(&MerkleNode::from(txs_params[0].outputs[1].coin.inner()));
     let bob_leaf_pos = th.bob.merkle_tree.witness().unwrap();

+ 34 - 8
src/contract/money/tests/verification_bench.rs

@@ -45,6 +45,9 @@ async fn alice2alice_random_amounts() -> Result<()> {
 
     const ALICE_AIRDROP: u64 = 1000;
 
+    // Slot to verify against
+    let current_slot = 0;
+
     // n transactions to loop
     let mut n = 3;
     for arg in env::args() {
@@ -71,12 +74,22 @@ async fn alice2alice_random_amounts() -> Result<()> {
     info!(target: "money", "[Faucet] ==========================");
     info!(target: "money", "[Faucet] Executing Alice airdrop tx");
     info!(target: "money", "[Faucet] ==========================");
-    th.faucet.state.read().await.verify_transactions(&[airdrop_tx.clone()], true).await?;
+    th.faucet
+        .state
+        .read()
+        .await
+        .verify_transactions(&[airdrop_tx.clone()], current_slot, true)
+        .await?;
     th.faucet.merkle_tree.append(&MerkleNode::from(airdrop_params.outputs[0].coin.inner()));
     info!(target: "money", "[Alice] ==========================");
     info!(target: "money", "[Alice] Executing Alice airdrop tx");
     info!(target: "money", "[Alice] ==========================");
-    th.alice.state.read().await.verify_transactions(&[airdrop_tx.clone()], true).await?;
+    th.alice
+        .state
+        .read()
+        .await
+        .verify_transactions(&[airdrop_tx.clone()], current_slot, true)
+        .await?;
     th.alice.merkle_tree.append(&MerkleNode::from(airdrop_params.outputs[0].coin.inner()));
 
     assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
@@ -152,14 +165,14 @@ async fn alice2alice_random_amounts() -> Result<()> {
         info!(target: "money", "[Faucet] ================================");
         info!(target: "money", "[Faucet] Executing Alice2Alice payment tx");
         info!(target: "money", "[Faucet] ================================");
-        th.faucet.state.read().await.verify_transactions(&[tx.clone()], true).await?;
+        th.faucet.state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
         for output in &params.outputs {
             th.faucet.merkle_tree.append(&MerkleNode::from(output.coin.inner()));
         }
         info!(target: "money", "[Alice] ================================");
         info!(target: "money", "[Alice] Executing Alice2Alice payment tx");
         info!(target: "money", "[Alice] ================================");
-        th.alice.state.read().await.verify_transactions(&[tx.clone()], true).await?;
+        th.alice.state.read().await.verify_transactions(&[tx.clone()], current_slot, true).await?;
         // Gather new owncoins and apply the state transitions
         for output in params.outputs {
             th.alice.merkle_tree.append(&MerkleNode::from(output.coin.inner()));
@@ -190,6 +203,9 @@ async fn alice2alice_random_amounts() -> Result<()> {
 async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
     init_logger();
 
+    // Slot to verify against
+    let current_slot = 0;
+
     // N blocks to simulate
     let mut n = 3;
     for arg in env::args() {
@@ -223,12 +239,22 @@ async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
         info!(target: "money", "[Faucet] =======================");
         info!(target: "money", "[Faucet] Executing Alice mint tx");
         info!(target: "money", "[Faucet] =======================");
-        th.faucet.state.read().await.verify_transactions(&[mint_tx.clone()], true).await?;
+        th.faucet
+            .state
+            .read()
+            .await
+            .verify_transactions(&[mint_tx.clone()], current_slot, true)
+            .await?;
         th.faucet.merkle_tree.append(&MerkleNode::from(mint_params.output.coin.inner()));
         info!(target: "money", "[Alice] =======================");
         info!(target: "money", "[Alice] Executing Alice mint tx");
         info!(target: "money", "[Alice] =======================");
-        th.alice.state.read().await.verify_transactions(&[mint_tx.clone()], true).await?;
+        th.alice
+            .state
+            .read()
+            .await
+            .verify_transactions(&[mint_tx.clone()], current_slot, true)
+            .await?;
         th.alice.merkle_tree.append(&MerkleNode::from(mint_params.output.coin.inner()));
 
         assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
@@ -351,11 +377,11 @@ async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
         info!(target: "money", "[Faucet] ================================");
         info!(target: "money", "[Faucet] Executing Alice2Alice payment tx");
         info!(target: "money", "[Faucet] ================================");
-        th.faucet.state.read().await.verify_transactions(&txs, true).await?;
+        th.faucet.state.read().await.verify_transactions(&txs, current_slot, true).await?;
         info!(target: "money", "[Alice] ================================");
         info!(target: "money", "[Alice] Executing Alice2Alice payment tx");
         info!(target: "money", "[Alice] ================================");
-        th.alice.state.read().await.verify_transactions(&txs, true).await?;
+        th.alice.state.read().await.verify_transactions(&txs, current_slot, true).await?;
 
         assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
     }

+ 5 - 0
src/runtime/import/util.rs

@@ -160,6 +160,11 @@ pub(crate) fn get_current_slot(ctx: FunctionEnvMut<Env>) -> u64 {
     ctx.data().time_keeper.current_slot()
 }
 
+/// Will return current runtime configured verifying slot number.
+pub(crate) fn get_verifying_slot(ctx: FunctionEnvMut<Env>) -> u64 {
+    ctx.data().time_keeper.verifying_slot
+}
+
 /// Will return requested slot checkpoint from `SlotCheckpointStore`.
 pub(crate) fn get_slot_checkpoint(ctx: FunctionEnvMut<Env>, slot: u64) -> i64 {
     let env = ctx.data();

+ 6 - 0
src/runtime/vm_runtime.rs

@@ -270,6 +270,12 @@ impl Runtime {
                     import::util::get_current_slot,
                 ),
 
+                "get_verifying_slot_" => Function::new_typed_with_env(
+                    &mut store,
+                    &ctx,
+                    import::util::get_verifying_slot,
+                ),
+
                 "get_slot_checkpoint_" => Function::new_typed_with_env(
                     &mut store,
                     &ctx,

+ 11 - 0
src/sdk/src/util.rs

@@ -79,6 +79,16 @@ pub fn get_current_slot() -> u64 {
     unsafe { get_current_slot_() }
 }
 
+/// Everyone can call this. Will return runtime configured
+/// verifying slot.
+///
+/// ```
+/// slot = get_current_slot();
+/// ```
+pub fn get_verifying_slot() -> u64 {
+    unsafe { get_verifying_slot_() }
+}
+
 /// Everyone can call this. Will return requested slot checkpoint from `SlotCheckpointStore`.
 ///
 /// ```
@@ -106,6 +116,7 @@ extern "C" {
 
     fn get_current_epoch_() -> u64;
     fn get_current_slot_() -> u64;
+    fn get_verifying_slot_() -> u64;
     fn get_slot_checkpoint_(slot: u64) -> i64;
     fn get_blockchain_time_() -> u64;
 }

+ 11 - 4
src/util/time.rs

@@ -32,15 +32,22 @@ const SECS_IN_HOUR: u64 = 3600;
 pub struct TimeKeeper {
     /// Genesis block creation timestamp
     pub genesis_ts: Timestamp,
-    /// Currently configured epoch duration.
+    /// Currently configured epoch duration
     pub epoch_length: u64,
-    /// Currently configured slot duration.
+    /// Currently configured slot duration
     pub slot_time: u64,
+    /// Slot number runtime can access to verify against
+    pub verifying_slot: u64,
 }
 
 impl TimeKeeper {
-    pub fn new(genesis_ts: Timestamp, epoch_length: u64, slot_time: u64) -> Self {
-        Self { genesis_ts, epoch_length, slot_time }
+    pub fn new(
+        genesis_ts: Timestamp,
+        epoch_length: u64,
+        slot_time: u64,
+        verifying_slot: u64,
+    ) -> Self {
+        Self { genesis_ts, epoch_length, slot_time, verifying_slot }
     }
 
     /// Calculates current epoch.