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

contract/consensus/tests: moved benchmarking to harness and simplyfied tx execution

aggstam 3 лет назад
Родитель
Сommit
fb52d9b80f
2 измененных файлов с 469 добавлено и 402 удалено
  1. 439 10
      src/contract/consensus/tests/harness.rs
  2. 30 392
      src/contract/consensus/tests/stake_unstake.rs

+ 439 - 10
src/contract/consensus/tests/harness.rs

@@ -15,12 +15,15 @@
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-use std::collections::HashMap;
+use std::{
+    collections::HashMap,
+    time::{Duration, Instant},
+};
 
 use darkfi::{
     consensus::{
-        ValidatorState, ValidatorStatePtr, TESTNET_BOOTSTRAP_TIMESTAMP, TESTNET_GENESIS_HASH_BYTES,
-        TESTNET_GENESIS_TIMESTAMP, TESTNET_INITIAL_DISTRIBUTION,
+        SlotCheckpoint, ValidatorState, ValidatorStatePtr, TESTNET_BOOTSTRAP_TIMESTAMP,
+        TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP, TESTNET_INITIAL_DISTRIBUTION,
     },
     runtime::vm_runtime::SMART_CONTRACT_ZKAS_DB_NAME,
     tx::Transaction,
@@ -32,19 +35,33 @@ use darkfi::{
 use darkfi_money_contract::client::OwnCoin;
 use darkfi_sdk::{
     crypto::{
-        Keypair, MerkleTree, PublicKey, CONSENSUS_CONTRACT_ID, DARK_TOKEN_ID, MONEY_CONTRACT_ID,
+        merkle_prelude::*, Keypair, MerkleNode, MerkleTree, PublicKey, CONSENSUS_CONTRACT_ID,
+        DARK_TOKEN_ID, MONEY_CONTRACT_ID,
     },
     pasta::pallas,
     ContractCall,
 };
 use darkfi_serial::{deserialize, serialize, Encodable};
-use log::warn;
+use log::{info, warn};
 use rand::rngs::OsRng;
 
+use darkfi_consensus_contract::{
+    client::{
+        proposal_v1::ConsensusProposalCallBuilder, stake_v1::ConsensusStakeCallBuilder,
+        unstake_v1::ConsensusUnstakeCallBuilder,
+    },
+    model::ConsensusProposalMintParamsV1,
+    ConsensusFunction,
+};
 use darkfi_money_contract::{
-    client::transfer_v1::TransferCallBuilder, model::MoneyTransferParamsV1, MoneyFunction,
-    CONSENSUS_CONTRACT_ZKAS_PROPOSAL_MINT_NS_V1, CONSENSUS_CONTRACT_ZKAS_PROPOSAL_REWARD_NS_V1,
-    MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
+    client::{
+        stake_v1::MoneyStakeCallBuilder, transfer_v1::TransferCallBuilder,
+        unstake_v1::MoneyUnstakeCallBuilder,
+    },
+    model::{ConsensusStakeParamsV1, MoneyTransferParamsV1, MoneyUnstakeParamsV1},
+    MoneyFunction, CONSENSUS_CONTRACT_ZKAS_PROPOSAL_MINT_NS_V1,
+    CONSENSUS_CONTRACT_ZKAS_PROPOSAL_REWARD_NS_V1, MONEY_CONTRACT_ZKAS_BURN_NS_V1,
+    MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 
 pub fn init_logger() {
@@ -65,6 +82,60 @@ pub fn init_logger() {
     }
 }
 
+/// Enum representing configured wallet holders
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub enum Holder {
+    Faucet,
+    Alice,
+}
+
+/// Enum representing transaction actions
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub enum TxAction {
+    Airdrop,
+    Stake,
+    Proposal,
+    Unstake,
+}
+
+/// Auxiliary struct to calculate transaction actions benchmarks
+pub struct TxActionBenchmarks {
+    /// Vector holding each transaction size in Bytes
+    pub sizes: Vec<usize>,
+    /// Vector holding each transaction broadcasted size in Bytes
+    pub broadcasted_sizes: Vec<usize>,
+    /// Vector holding each transaction creation time
+    pub creation_times: Vec<Duration>,
+    /// Vector holding each transaction verify time
+    pub verify_times: Vec<Duration>,
+}
+
+impl TxActionBenchmarks {
+    pub fn new() -> Self {
+        Self {
+            sizes: vec![],
+            broadcasted_sizes: vec![],
+            creation_times: vec![],
+            verify_times: vec![],
+        }
+    }
+
+    pub fn statistics(&self, action: &TxAction) {
+        let avg = self.sizes.iter().sum::<usize>();
+        let avg = avg / self.sizes.len();
+        info!("Average {:?} size: {:?} Bytes", action, avg);
+        let avg = self.broadcasted_sizes.iter().sum::<usize>();
+        let avg = avg / self.broadcasted_sizes.len();
+        info!("Average {:?} broadcasted size: {:?} Bytes", action, avg);
+        let avg = self.creation_times.iter().sum::<Duration>();
+        let avg = avg / self.creation_times.len() as u32;
+        info!("Average {:?} creation time: {:?}", action, avg);
+        let avg = self.verify_times.iter().sum::<Duration>();
+        let avg = avg / self.verify_times.len() as u32;
+        info!("Average {:?} verification time: {:?}", action, avg);
+    }
+}
+
 pub struct Wallet {
     pub keypair: Keypair,
     pub state: ValidatorStatePtr,
@@ -107,6 +178,7 @@ pub struct ConsensusTestHarness {
     pub faucet: Wallet,
     pub alice: Wallet,
     pub proving_keys: HashMap<&'static str, (ProvingKey, ZkBinary)>,
+    pub tx_action_benchmarks: HashMap<TxAction, TxActionBenchmarks>,
 }
 
 impl ConsensusTestHarness {
@@ -152,16 +224,25 @@ impl ConsensusTestHarness {
         mkpk!(CONSENSUS_CONTRACT_ZKAS_PROPOSAL_REWARD_NS_V1);
         mkpk!(CONSENSUS_CONTRACT_ZKAS_PROPOSAL_MINT_NS_V1);
 
-        Ok(Self { faucet, alice, proving_keys })
+        // Build benchmarks map
+        let mut tx_action_benchmarks = HashMap::new();
+        tx_action_benchmarks.insert(TxAction::Airdrop, TxActionBenchmarks::new());
+        tx_action_benchmarks.insert(TxAction::Stake, TxActionBenchmarks::new());
+        tx_action_benchmarks.insert(TxAction::Proposal, TxActionBenchmarks::new());
+        tx_action_benchmarks.insert(TxAction::Unstake, TxActionBenchmarks::new());
+
+        Ok(Self { faucet, alice, proving_keys, tx_action_benchmarks })
     }
 
     pub fn airdrop_native(
-        &self,
+        &mut self,
         value: u64,
         recipient: PublicKey,
     ) -> Result<(Transaction, MoneyTransferParamsV1)> {
         let (mint_pk, mint_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
         let (burn_pk, burn_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Airdrop).unwrap();
+        let timer = Instant::now();
 
         let builder = TransferCallBuilder {
             keypair: self.faucet.keypair,
@@ -192,7 +273,355 @@ impl ConsensusTestHarness {
         let mut tx = Transaction { calls, proofs, signatures: vec![] };
         let sigs = tx.create_sigs(&mut OsRng, &debris.signature_secrets)?;
         tx.signatures = vec![sigs];
+        tx_action_benchmark.creation_times.push(timer.elapsed());
+
+        // Calculate transaction sizes
+        let encoded: Vec<u8> = serialize(&tx);
+        let size = ::std::mem::size_of_val(&*encoded);
+        tx_action_benchmark.sizes.push(size);
+        let base58 = bs58::encode(&encoded).into_string();
+        let size = ::std::mem::size_of_val(&*base58);
+        tx_action_benchmark.broadcasted_sizes.push(size);
 
         Ok((tx, debris.params))
     }
+
+    pub async fn execute_airdrop_native_tx(
+        &mut self,
+        holder: Holder,
+        tx: Transaction,
+        params: &MoneyTransferParamsV1,
+        slot: u64,
+    ) -> Result<()> {
+        let wallet = match holder {
+            Holder::Faucet => &mut self.faucet,
+            Holder::Alice => &mut self.alice,
+        };
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Airdrop).unwrap();
+        let timer = Instant::now();
+
+        let erroneous_txs =
+            wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
+        assert!(erroneous_txs.is_empty());
+        wallet.merkle_tree.append(&MerkleNode::from(params.outputs[0].coin.inner()));
+        tx_action_benchmark.verify_times.push(timer.elapsed());
+
+        Ok(())
+    }
+
+    pub fn stake_native(
+        &mut self,
+        holder: Holder,
+        owncoin: OwnCoin,
+    ) -> Result<(Transaction, ConsensusStakeParamsV1)> {
+        let wallet = match holder {
+            Holder::Faucet => &self.faucet,
+            Holder::Alice => &self.alice,
+        };
+        let (mint_pk, mint_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
+        let (burn_pk, burn_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Stake).unwrap();
+        let timer = Instant::now();
+
+        // Building Money::Stake params
+        let money_stake_call_debris = MoneyStakeCallBuilder {
+            coin: owncoin.clone(),
+            tree: wallet.merkle_tree.clone(),
+            burn_zkbin: burn_zkbin.clone(),
+            burn_pk: burn_pk.clone(),
+        }
+        .build()?;
+        let (
+            money_stake_params,
+            money_stake_proofs,
+            money_stake_secret_key,
+            money_stake_value_blind,
+        ) = (
+            money_stake_call_debris.params,
+            money_stake_call_debris.proofs,
+            money_stake_call_debris.signature_secret,
+            money_stake_call_debris.value_blind,
+        );
+
+        // Building Consensus::Stake params
+        let consensus_stake_call_debris = ConsensusStakeCallBuilder {
+            coin: owncoin,
+            recipient: wallet.keypair.public,
+            value_blind: money_stake_value_blind,
+            token_blind: money_stake_params.token_blind,
+            nullifier: money_stake_params.input.nullifier,
+            merkle_root: money_stake_params.input.merkle_root,
+            signature_public: money_stake_params.input.signature_public,
+            mint_zkbin: mint_zkbin.clone(),
+            mint_pk: mint_pk.clone(),
+        }
+        .build()?;
+        let (consensus_stake_params, consensus_stake_proofs) =
+            (consensus_stake_call_debris.params, consensus_stake_call_debris.proofs);
+
+        // Building stake tx
+        let mut data = vec![MoneyFunction::StakeV1 as u8];
+        money_stake_params.encode(&mut data)?;
+        let money_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
+
+        let mut data = vec![ConsensusFunction::StakeV1 as u8];
+        consensus_stake_params.encode(&mut data)?;
+        let consensus_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
+
+        let calls = vec![money_call, consensus_call];
+        let proofs = vec![money_stake_proofs, consensus_stake_proofs];
+        let mut stake_tx = Transaction { calls, proofs, signatures: vec![] };
+        let money_sigs = stake_tx.create_sigs(&mut OsRng, &[money_stake_secret_key])?;
+        let consensus_sigs = stake_tx.create_sigs(&mut OsRng, &[money_stake_secret_key])?;
+        stake_tx.signatures = vec![money_sigs, consensus_sigs];
+        tx_action_benchmark.creation_times.push(timer.elapsed());
+
+        // Calculate transaction sizes
+        let encoded: Vec<u8> = serialize(&stake_tx);
+        let size = ::std::mem::size_of_val(&*encoded);
+        tx_action_benchmark.sizes.push(size);
+        let base58 = bs58::encode(&encoded).into_string();
+        let size = ::std::mem::size_of_val(&*base58);
+        tx_action_benchmark.broadcasted_sizes.push(size);
+
+        Ok((stake_tx, consensus_stake_params))
+    }
+
+    pub async fn execute_stake_native_tx(
+        &mut self,
+        holder: Holder,
+        tx: Transaction,
+        params: &ConsensusStakeParamsV1,
+        slot: u64,
+    ) -> Result<()> {
+        let wallet = match holder {
+            Holder::Faucet => &mut self.faucet,
+            Holder::Alice => &mut self.alice,
+        };
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Stake).unwrap();
+        let timer = Instant::now();
+
+        let erroneous_txs =
+            wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
+        assert!(erroneous_txs.is_empty());
+        wallet.consensus_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
+        tx_action_benchmark.verify_times.push(timer.elapsed());
+
+        Ok(())
+    }
+
+    pub fn proposal(
+        &mut self,
+        holder: Holder,
+        slot_checkpoint: SlotCheckpoint,
+        staked_oc: OwnCoin,
+    ) -> Result<(Transaction, ConsensusProposalMintParamsV1)> {
+        let wallet = match holder {
+            Holder::Faucet => &self.faucet,
+            Holder::Alice => &self.alice,
+        };
+        let (burn_pk, burn_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
+        let (reward_pk, reward_zkbin) =
+            self.proving_keys.get(&CONSENSUS_CONTRACT_ZKAS_PROPOSAL_REWARD_NS_V1).unwrap();
+        let (mint_pk, mint_zkbin) =
+            self.proving_keys.get(&CONSENSUS_CONTRACT_ZKAS_PROPOSAL_MINT_NS_V1).unwrap();
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Proposal).unwrap();
+        let timer = Instant::now();
+
+        // Building Consensus::Unstake params
+        let proposal_call_debris = ConsensusProposalCallBuilder {
+            coin: staked_oc.clone(),
+            recipient: wallet.keypair.public,
+            slot_checkpoint,
+            tree: wallet.consensus_merkle_tree.clone(),
+            burn_zkbin: burn_zkbin.clone(),
+            burn_pk: burn_pk.clone(),
+            reward_zkbin: reward_zkbin.clone(),
+            reward_pk: reward_pk.clone(),
+            mint_zkbin: mint_zkbin.clone(),
+            mint_pk: mint_pk.clone(),
+        }
+        .build()?;
+        let (
+            burn_params,
+            burn_proofs,
+            reward_params,
+            reward_proofs,
+            mint_params,
+            mint_proofs,
+            proposal_secret_key,
+        ) = (
+            proposal_call_debris.burn_params,
+            proposal_call_debris.burn_proofs,
+            proposal_call_debris.reward_params,
+            proposal_call_debris.reward_proofs,
+            proposal_call_debris.mint_params,
+            proposal_call_debris.mint_proofs,
+            proposal_call_debris.signature_secret,
+        );
+
+        // Building proposal tx
+        let mut data = vec![ConsensusFunction::ProposalBurnV1 as u8];
+        burn_params.encode(&mut data)?;
+        let burn_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
+
+        let mut data = vec![ConsensusFunction::ProposalRewardV1 as u8];
+        reward_params.encode(&mut data)?;
+        let reward_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
+
+        let mut data = vec![ConsensusFunction::ProposalMintV1 as u8];
+        mint_params.encode(&mut data)?;
+        let mint_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
+
+        let calls = vec![burn_call, reward_call, mint_call];
+        let proofs = vec![burn_proofs, reward_proofs, mint_proofs];
+        let mut proposal_tx = Transaction { calls, proofs, signatures: vec![] };
+        let burn_sigs = proposal_tx.create_sigs(&mut OsRng, &[proposal_secret_key])?;
+        let reward_sigs = proposal_tx.create_sigs(&mut OsRng, &[proposal_secret_key])?;
+        let mint_sigs = proposal_tx.create_sigs(&mut OsRng, &[proposal_secret_key])?;
+        proposal_tx.signatures = vec![burn_sigs, reward_sigs, mint_sigs];
+        tx_action_benchmark.creation_times.push(timer.elapsed());
+
+        // Calculate transaction sizes
+        let encoded: Vec<u8> = serialize(&proposal_tx);
+        let size = ::std::mem::size_of_val(&*encoded);
+        tx_action_benchmark.sizes.push(size);
+        let base58 = bs58::encode(&encoded).into_string();
+        let size = ::std::mem::size_of_val(&*base58);
+        tx_action_benchmark.broadcasted_sizes.push(size);
+
+        Ok((proposal_tx, mint_params))
+    }
+
+    pub async fn execute_proposal_tx(
+        &mut self,
+        holder: Holder,
+        tx: Transaction,
+        params: &ConsensusProposalMintParamsV1,
+        slot: u64,
+    ) -> Result<()> {
+        let wallet = match holder {
+            Holder::Faucet => &mut self.faucet,
+            Holder::Alice => &mut self.alice,
+        };
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Proposal).unwrap();
+        let timer = Instant::now();
+
+        let erroneous_txs =
+            wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
+        assert!(erroneous_txs.is_empty());
+        wallet.consensus_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
+        tx_action_benchmark.verify_times.push(timer.elapsed());
+
+        Ok(())
+    }
+
+    pub fn unstake_native(
+        &mut self,
+        holder: Holder,
+        staked_oc: OwnCoin,
+    ) -> Result<(Transaction, MoneyUnstakeParamsV1)> {
+        let wallet = match holder {
+            Holder::Faucet => &self.faucet,
+            Holder::Alice => &self.alice,
+        };
+        let (burn_pk, burn_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
+        let (mint_pk, mint_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Unstake).unwrap();
+        let timer = Instant::now();
+
+        // Building Consensus::Unstake params
+        let consensus_unstake_call_debris = ConsensusUnstakeCallBuilder {
+            coin: staked_oc.clone(),
+            tree: wallet.consensus_merkle_tree.clone(),
+            burn_zkbin: burn_zkbin.clone(),
+            burn_pk: burn_pk.clone(),
+        }
+        .build()?;
+        let (
+            consensus_unstake_params,
+            consensus_unstake_proofs,
+            consensus_unstake_secret_key,
+            consensus_unstake_value_blind,
+        ) = (
+            consensus_unstake_call_debris.params,
+            consensus_unstake_call_debris.proofs,
+            consensus_unstake_call_debris.signature_secret,
+            consensus_unstake_call_debris.value_blind,
+        );
+
+        // Building Money::Unstake params
+        let money_unstake_call_debris = MoneyUnstakeCallBuilder {
+            coin: staked_oc,
+            recipient: wallet.keypair.public,
+            value_blind: consensus_unstake_value_blind,
+            token_blind: consensus_unstake_params.token_blind,
+            nullifier: consensus_unstake_params.input.nullifier,
+            merkle_root: consensus_unstake_params.input.merkle_root,
+            signature_public: consensus_unstake_params.input.signature_public,
+            mint_zkbin: mint_zkbin.clone(),
+            mint_pk: mint_pk.clone(),
+        }
+        .build()?;
+        let (money_unstake_params, money_unstake_proofs) =
+            (money_unstake_call_debris.params, money_unstake_call_debris.proofs);
+
+        // Building unstake tx
+        let mut data = vec![ConsensusFunction::UnstakeV1 as u8];
+        consensus_unstake_params.encode(&mut data)?;
+        let consensus_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
+
+        let mut data = vec![MoneyFunction::UnstakeV1 as u8];
+        money_unstake_params.encode(&mut data)?;
+        let money_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
+
+        let calls = vec![consensus_call, money_call];
+        let proofs = vec![consensus_unstake_proofs, money_unstake_proofs];
+        let mut unstake_tx = Transaction { calls, proofs, signatures: vec![] };
+        let consensus_sigs = unstake_tx.create_sigs(&mut OsRng, &[consensus_unstake_secret_key])?;
+        let money_sigs = unstake_tx.create_sigs(&mut OsRng, &[consensus_unstake_secret_key])?;
+        unstake_tx.signatures = vec![consensus_sigs, money_sigs];
+        tx_action_benchmark.creation_times.push(timer.elapsed());
+
+        // Calculate transaction sizes
+        let encoded: Vec<u8> = serialize(&unstake_tx);
+        let size = ::std::mem::size_of_val(&*encoded);
+        tx_action_benchmark.sizes.push(size);
+        let base58 = bs58::encode(&encoded).into_string();
+        let size = ::std::mem::size_of_val(&*base58);
+        tx_action_benchmark.broadcasted_sizes.push(size);
+
+        Ok((unstake_tx, money_unstake_params))
+    }
+
+    pub async fn execute_unstake_native_tx(
+        &mut self,
+        holder: Holder,
+        tx: Transaction,
+        params: &MoneyUnstakeParamsV1,
+        slot: u64,
+    ) -> Result<()> {
+        let wallet = match holder {
+            Holder::Faucet => &mut self.faucet,
+            Holder::Alice => &mut self.alice,
+        };
+        let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Unstake).unwrap();
+        let timer = Instant::now();
+
+        let erroneous_txs =
+            wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
+        assert!(erroneous_txs.is_empty());
+        wallet.merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
+        tx_action_benchmark.verify_times.push(timer.elapsed());
+
+        Ok(())
+    }
+
+    pub fn statistics(&self) {
+        info!("==================== Statistics ====================");
+        for (action, tx_action_benchmark) in &self.tx_action_benchmarks {
+            tx_action_benchmark.statistics(action);
+        }
+        info!("====================================================");
+    }
 }

+ 30 - 392
src/contract/consensus/tests/stake_unstake.rs

@@ -26,59 +26,20 @@
 //!
 //! TODO: Malicious cases
 
-use std::time::{Duration, Instant};
-
-use darkfi::{tx::Transaction, Result};
-use darkfi_sdk::{
-    crypto::{
-        merkle_prelude::*, poseidon_hash, Coin, MerkleNode, Nullifier, CONSENSUS_CONTRACT_ID,
-        MONEY_CONTRACT_ID,
-    },
-    ContractCall,
-};
-use darkfi_serial::{serialize, Encodable};
+use darkfi::Result;
+use darkfi_sdk::crypto::{merkle_prelude::*, poseidon_hash, Coin, Nullifier};
 use log::info;
-use rand::rngs::OsRng;
-
-use darkfi_money_contract::{
-    client::{
-        stake_v1::MoneyStakeCallBuilder, unstake_v1::MoneyUnstakeCallBuilder, MoneyNote, OwnCoin,
-    },
-    MoneyFunction, CONSENSUS_CONTRACT_ZKAS_PROPOSAL_MINT_NS_V1,
-    CONSENSUS_CONTRACT_ZKAS_PROPOSAL_REWARD_NS_V1, MONEY_CONTRACT_ZKAS_BURN_NS_V1,
-    MONEY_CONTRACT_ZKAS_MINT_NS_V1,
-};
-
-use darkfi_consensus_contract::{
-    client::{
-        proposal_v1::ConsensusProposalCallBuilder, stake_v1::ConsensusStakeCallBuilder,
-        unstake_v1::ConsensusUnstakeCallBuilder,
-    },
-    model::REWARD,
-    ConsensusFunction,
-};
+
+use darkfi_consensus_contract::model::REWARD;
+use darkfi_money_contract::client::{MoneyNote, OwnCoin};
 
 mod harness;
-use harness::{init_logger, ConsensusTestHarness};
+use harness::{init_logger, ConsensusTestHarness, Holder};
 
 #[async_std::test]
 async fn consensus_contract_stake_unstake() -> Result<()> {
     init_logger();
 
-    // Some benchmark averages
-    let mut stake_sizes = vec![];
-    let mut stake_broadcasted_sizes = vec![];
-    let mut stake_creation_times = vec![];
-    let mut stake_verify_times = vec![];
-    let mut proposal_sizes = vec![];
-    let mut proposal_broadcasted_sizes = vec![];
-    let mut proposal_creation_times = vec![];
-    let mut proposal_verify_times = vec![];
-    let mut unstake_sizes = vec![];
-    let mut unstake_broadcasted_sizes = vec![];
-    let mut unstake_creation_times = vec![];
-    let mut unstake_verify_times = vec![];
-
     // Some numbers we want to assert
     const ALICE_AIRDROP: u64 = 1000;
 
@@ -87,43 +48,21 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
 
     // Initialize harness
     let mut th = ConsensusTestHarness::new().await?;
-    info!(target: "consensus", "[Faucet] ===================================================");
-    info!(target: "consensus", "[Faucet] Building Money::Transfer params for Alice's airdrop");
-    info!(target: "consensus", "[Faucet] ===================================================");
+    info!(target: "consensus", "[Faucet] =========================");
+    info!(target: "consensus", "[Faucet] Building Alice airdrop tx");
+    info!(target: "consensus", "[Faucet] =========================");
     let (airdrop_tx, airdrop_params) = th.airdrop_native(ALICE_AIRDROP, th.alice.keypair.public)?;
-    let (money_mint_pk, money_mint_zkbin) =
-        th.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
-    let (money_burn_pk, money_burn_zkbin) =
-        th.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
-    let (proposal_reward_pk, proposal_reward_zkbin) =
-        th.proving_keys.get(&CONSENSUS_CONTRACT_ZKAS_PROPOSAL_REWARD_NS_V1).unwrap();
-    let (proposal_mint_pk, proposal_mint_zkbin) =
-        th.proving_keys.get(&CONSENSUS_CONTRACT_ZKAS_PROPOSAL_MINT_NS_V1).unwrap();
 
     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()], current_slot, true)
+    th.execute_airdrop_native_tx(Holder::Faucet, airdrop_tx.clone(), &airdrop_params, current_slot)
         .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()], current_slot, true)
-        .await?;
-    assert!(erroneous_txs.is_empty());
-    th.alice.merkle_tree.append(&MerkleNode::from(airdrop_params.outputs[0].coin.inner()));
+    th.execute_airdrop_native_tx(Holder::Alice, airdrop_tx, &airdrop_params, current_slot).await?;
 
     assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
 
@@ -139,107 +78,21 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     };
 
     // Now Alice can stake her owncoin
-    info!(target: "consensus", "[Alice] ============================");
-    info!(target: "consensus", "[Alice] Building Money::Stake params");
-    info!(target: "consensus", "[Alice] ============================");
-    let timer = Instant::now();
-    let alice_money_stake_call_debris = MoneyStakeCallBuilder {
-        coin: alice_oc.clone(),
-        tree: th.alice.merkle_tree.clone(),
-        burn_zkbin: money_burn_zkbin.clone(),
-        burn_pk: money_burn_pk.clone(),
-    }
-    .build()?;
-    let (
-        alice_money_stake_params,
-        alice_money_stake_proofs,
-        alice_money_stake_secret_key,
-        alice_money_stake_value_blind,
-    ) = (
-        alice_money_stake_call_debris.params,
-        alice_money_stake_call_debris.proofs,
-        alice_money_stake_call_debris.signature_secret,
-        alice_money_stake_call_debris.value_blind,
-    );
-
-    info!(target: "consensus", "[Alice] ================================");
-    info!(target: "consensus", "[Alice] Building Consensus::Stake params");
-    info!(target: "consensus", "[Alice] ================================");
-    let alice_consensus_stake_call_debris = ConsensusStakeCallBuilder {
-        coin: alice_oc.clone(),
-        recipient: th.alice.keypair.public,
-        value_blind: alice_money_stake_value_blind,
-        token_blind: alice_money_stake_params.token_blind,
-        nullifier: alice_money_stake_params.input.nullifier,
-        merkle_root: alice_money_stake_params.input.merkle_root,
-        signature_public: alice_money_stake_params.input.signature_public,
-        mint_zkbin: money_mint_zkbin.clone(),
-        mint_pk: money_mint_pk.clone(),
-    }
-    .build()?;
-    let (alice_consensus_stake_params, alice_consensus_stake_proofs) =
-        (alice_consensus_stake_call_debris.params, alice_consensus_stake_call_debris.proofs);
-
     info!(target: "consensus", "[Alice] =================");
     info!(target: "consensus", "[Alice] Building stake tx");
     info!(target: "consensus", "[Alice] =================");
-    let mut data = vec![MoneyFunction::StakeV1 as u8];
-    alice_money_stake_params.encode(&mut data)?;
-    let money_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
-
-    let mut data = vec![ConsensusFunction::StakeV1 as u8];
-    alice_consensus_stake_params.encode(&mut data)?;
-    let consensus_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
-
-    let calls = vec![money_call, consensus_call];
-    let proofs = vec![alice_money_stake_proofs, alice_consensus_stake_proofs];
-    let mut alice_stake_tx = Transaction { calls, proofs, signatures: vec![] };
-    let money_sigs = alice_stake_tx.create_sigs(&mut OsRng, &[alice_money_stake_secret_key])?;
-    let consensus_sigs = alice_stake_tx.create_sigs(&mut OsRng, &[alice_money_stake_secret_key])?;
-    alice_stake_tx.signatures = vec![money_sigs, consensus_sigs];
-    stake_creation_times.push(timer.elapsed());
-
-    // Calculate transaction sizes
-    let encoded: Vec<u8> = serialize(&alice_stake_tx);
-    let size = ::std::mem::size_of_val(&*encoded);
-    stake_sizes.push(size);
-    let base58 = bs58::encode(&encoded).into_string();
-    let size = ::std::mem::size_of_val(&*base58);
-    stake_broadcasted_sizes.push(size);
+    let (stake_tx, stake_params) = th.stake_native(Holder::Alice, alice_oc.clone())?;
 
     info!(target: "consensus", "[Faucet] ========================");
     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()], current_slot, true)
+    th.execute_stake_native_tx(Holder::Faucet, stake_tx.clone(), &stake_params, current_slot)
         .await?;
-    assert!(erroneous_txs.is_empty());
-    th.faucet
-        .consensus_merkle_tree
-        .append(&MerkleNode::from(alice_consensus_stake_params.output.coin.inner()));
-    stake_verify_times.push(timer.elapsed());
 
     info!(target: "consensus", "[Alice] ========================");
     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()], current_slot, true)
-        .await?;
-    assert!(erroneous_txs.is_empty());
-    th.alice
-        .consensus_merkle_tree
-        .append(&MerkleNode::from(alice_consensus_stake_params.output.coin.inner()));
-    stake_verify_times.push(timer.elapsed());
+    th.execute_stake_native_tx(Holder::Alice, stake_tx, &stake_params, current_slot).await?;
 
     assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
     assert!(
@@ -249,10 +102,9 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
 
     // Gather new staked owncoin
     let leaf_position = th.alice.consensus_merkle_tree.witness().unwrap();
-    let note: MoneyNote =
-        alice_consensus_stake_params.output.note.decrypt(&th.alice.keypair.secret)?;
+    let note: MoneyNote = stake_params.output.note.decrypt(&th.alice.keypair.secret)?;
     let alice_staked_oc = OwnCoin {
-        coin: Coin::from(alice_consensus_stake_params.output.coin),
+        coin: Coin::from(stake_params.output.coin),
         note: note.clone(),
         secret: th.alice.keypair.secret,
         nullifier: Nullifier::from(poseidon_hash([th.alice.keypair.secret.inner(), note.serial])),
@@ -271,114 +123,22 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     // With alice's current coin value she can become the slot proposer,
     // so she creates a proposal transaction to burn her staked coin,
     // reward herself and mint the new coin.
-    info!(target: "consensus", "[Alice] ====================================");
-    info!(target: "consensus", "[Alice] Building proposal transaction params");
-    info!(target: "consensus", "[Alice] ====================================");
-    let timer = Instant::now();
-    let alice_consensus_proposal_call_debris = ConsensusProposalCallBuilder {
-        coin: alice_staked_oc.clone(),
-        recipient: th.alice.keypair.public,
-        slot_checkpoint,
-        tree: th.alice.consensus_merkle_tree.clone(),
-        burn_zkbin: money_burn_zkbin.clone(),
-        burn_pk: money_burn_pk.clone(),
-        reward_zkbin: proposal_reward_zkbin.clone(),
-        reward_pk: proposal_reward_pk.clone(),
-        mint_zkbin: proposal_mint_zkbin.clone(),
-        mint_pk: proposal_mint_pk.clone(),
-    }
-    .build()?;
-    let (
-        alice_consensus_burn_params,
-        alice_consensus_burn_proofs,
-        alice_consensus_reward_params,
-        alice_consensus_reward_proofs,
-        alice_consensus_mint_params,
-        alice_consensus_mint_proofs,
-        alice_consensus_proposal_secret_key,
-    ) = (
-        alice_consensus_proposal_call_debris.burn_params,
-        alice_consensus_proposal_call_debris.burn_proofs,
-        alice_consensus_proposal_call_debris.reward_params,
-        alice_consensus_proposal_call_debris.reward_proofs,
-        alice_consensus_proposal_call_debris.mint_params,
-        alice_consensus_proposal_call_debris.mint_proofs,
-        alice_consensus_proposal_call_debris.signature_secret,
-    );
-
     info!(target: "consensus", "[Alice] ====================");
     info!(target: "consensus", "[Alice] Building proposal tx");
     info!(target: "consensus", "[Alice] ====================");
-    let mut data = vec![ConsensusFunction::ProposalBurnV1 as u8];
-    alice_consensus_burn_params.encode(&mut data)?;
-    let consensus_burn_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
-
-    let mut data = vec![ConsensusFunction::ProposalRewardV1 as u8];
-    alice_consensus_reward_params.encode(&mut data)?;
-    let consensus_reward_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
-
-    let mut data = vec![ConsensusFunction::ProposalMintV1 as u8];
-    alice_consensus_mint_params.encode(&mut data)?;
-    let consensus_mint_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
-
-    let calls = vec![consensus_burn_call, consensus_reward_call, consensus_mint_call];
-    let proofs = vec![
-        alice_consensus_burn_proofs,
-        alice_consensus_reward_proofs,
-        alice_consensus_mint_proofs,
-    ];
-    let mut alice_proposal_tx = Transaction { calls, proofs, signatures: vec![] };
-    let consensus_burn_sigs =
-        alice_proposal_tx.create_sigs(&mut OsRng, &[alice_consensus_proposal_secret_key])?;
-    let consensus_reward_sigs =
-        alice_proposal_tx.create_sigs(&mut OsRng, &[alice_consensus_proposal_secret_key])?;
-    let consensus_mint_sigs =
-        alice_proposal_tx.create_sigs(&mut OsRng, &[alice_consensus_proposal_secret_key])?;
-    alice_proposal_tx.signatures =
-        vec![consensus_burn_sigs, consensus_reward_sigs, consensus_mint_sigs];
-    proposal_creation_times.push(timer.elapsed());
-
-    // Calculate transaction sizes
-    let encoded: Vec<u8> = serialize(&alice_proposal_tx);
-    let size = ::std::mem::size_of_val(&*encoded);
-    proposal_sizes.push(size);
-    let base58 = bs58::encode(&encoded).into_string();
-    let size = ::std::mem::size_of_val(&*base58);
-    proposal_broadcasted_sizes.push(size);
+    let (proposal_tx, proposal_params) =
+        th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone())?;
 
     info!(target: "consensus", "[Faucet] ===========================");
     info!(target: "consensus", "[Faucet] Executing Alice proposal tx");
     info!(target: "consensus", "[Faucet] ===========================");
-    let timer = Instant::now();
-    let erroneous_txs = th
-        .faucet
-        .state
-        .read()
-        .await
-        .verify_transactions(&[alice_proposal_tx.clone()], current_slot, true)
+    th.execute_proposal_tx(Holder::Faucet, proposal_tx.clone(), &proposal_params, current_slot)
         .await?;
-    assert!(erroneous_txs.is_empty());
-    th.faucet
-        .consensus_merkle_tree
-        .append(&MerkleNode::from(alice_consensus_mint_params.output.coin.inner()));
-    proposal_verify_times.push(timer.elapsed());
 
     info!(target: "consensus", "[Alice] ===========================");
     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()], current_slot, true)
-        .await?;
-    assert!(erroneous_txs.is_empty());
-    th.alice
-        .consensus_merkle_tree
-        .append(&MerkleNode::from(alice_consensus_mint_params.output.coin.inner()));
-    proposal_verify_times.push(timer.elapsed());
+    th.execute_proposal_tx(Holder::Alice, proposal_tx, &proposal_params, current_slot).await?;
 
     assert!(
         th.faucet.consensus_merkle_tree.root(0).unwrap() ==
@@ -387,10 +147,9 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
 
     // Gather new staked owncoin which includes the reward
     let leaf_position = th.alice.consensus_merkle_tree.witness().unwrap();
-    let note: MoneyNote =
-        alice_consensus_mint_params.output.note.decrypt(&th.alice.keypair.secret)?;
+    let note: MoneyNote = proposal_params.output.note.decrypt(&th.alice.keypair.secret)?;
     let alice_rewarded_staked_oc = OwnCoin {
-        coin: Coin::from(alice_consensus_mint_params.output.coin),
+        coin: Coin::from(proposal_params.output.coin),
         note: note.clone(),
         secret: th.alice.keypair.secret,
         nullifier: Nullifier::from(poseidon_hash([th.alice.keypair.secret.inner(), note.serial])),
@@ -401,105 +160,22 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     assert!((alice_staked_oc.note.value + REWARD) == alice_rewarded_staked_oc.note.value);
 
     // Now Alice can unstake her owncoin
-    info!(target: "consensus", "[Alice] ==================================");
-    info!(target: "consensus", "[Alice] Building Consensus::Unstake params");
-    info!(target: "consensus", "[Alice] ==================================");
-    let timer = Instant::now();
-    let alice_consensus_unstake_call_debris = ConsensusUnstakeCallBuilder {
-        coin: alice_rewarded_staked_oc.clone(),
-        tree: th.alice.consensus_merkle_tree.clone(),
-        burn_zkbin: money_burn_zkbin.clone(),
-        burn_pk: money_burn_pk.clone(),
-    }
-    .build()?;
-    let (
-        alice_consensus_unstake_params,
-        alice_consensus_unstake_proofs,
-        alice_consensus_unstake_secret_key,
-        alice_consensus_unstake_value_blind,
-    ) = (
-        alice_consensus_unstake_call_debris.params,
-        alice_consensus_unstake_call_debris.proofs,
-        alice_consensus_unstake_call_debris.signature_secret,
-        alice_consensus_unstake_call_debris.value_blind,
-    );
-
-    info!(target: "consensus", "[Alice] ==============================");
-    info!(target: "consensus", "[Alice] Building Money::Unstake params");
-    info!(target: "consensus", "[Alice] ==============================");
-    let alice_money_unstake_call_debris = MoneyUnstakeCallBuilder {
-        coin: alice_rewarded_staked_oc.clone(),
-        recipient: th.alice.keypair.public,
-        value_blind: alice_consensus_unstake_value_blind,
-        token_blind: alice_consensus_unstake_params.token_blind,
-        nullifier: alice_consensus_unstake_params.input.nullifier,
-        merkle_root: alice_consensus_unstake_params.input.merkle_root,
-        signature_public: alice_consensus_unstake_params.input.signature_public,
-        mint_zkbin: money_mint_zkbin.clone(),
-        mint_pk: money_mint_pk.clone(),
-    }
-    .build()?;
-    let (alice_money_unstake_params, alice_money_unstake_proofs) =
-        (alice_money_unstake_call_debris.params, alice_money_unstake_call_debris.proofs);
-
     info!(target: "consensus", "[Alice] ===================");
     info!(target: "consensus", "[Alice] Building unstake tx");
     info!(target: "consensus", "[Alice] ===================");
-    let mut data = vec![ConsensusFunction::UnstakeV1 as u8];
-    alice_consensus_unstake_params.encode(&mut data)?;
-    let consensus_call = ContractCall { contract_id: *CONSENSUS_CONTRACT_ID, data };
-
-    let mut data = vec![MoneyFunction::UnstakeV1 as u8];
-    alice_money_unstake_params.encode(&mut data)?;
-    let money_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
-
-    let calls = vec![consensus_call, money_call];
-    let proofs = vec![alice_consensus_unstake_proofs, alice_money_unstake_proofs];
-    let mut alice_unstake_tx = Transaction { calls, proofs, signatures: vec![] };
-    let consensus_sigs =
-        alice_unstake_tx.create_sigs(&mut OsRng, &[alice_consensus_unstake_secret_key])?;
-    let money_sigs =
-        alice_unstake_tx.create_sigs(&mut OsRng, &[alice_consensus_unstake_secret_key])?;
-    alice_unstake_tx.signatures = vec![consensus_sigs, money_sigs];
-    unstake_creation_times.push(timer.elapsed());
-
-    // Calculate transaction sizes
-    let encoded: Vec<u8> = serialize(&alice_unstake_tx);
-    let size = ::std::mem::size_of_val(&*encoded);
-    unstake_sizes.push(size);
-    let base58 = bs58::encode(&encoded).into_string();
-    let size = ::std::mem::size_of_val(&*base58);
-    unstake_broadcasted_sizes.push(size);
+    let (unstake_tx, unstake_params) =
+        th.unstake_native(Holder::Alice, alice_rewarded_staked_oc.clone())?;
 
     info!(target: "consensus", "[Faucet] ==========================");
     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()], current_slot, true)
+    th.execute_unstake_native_tx(Holder::Faucet, unstake_tx.clone(), &unstake_params, current_slot)
         .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());
 
     info!(target: "consensus", "[Alice] ==========================");
     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()], 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());
+    th.execute_unstake_native_tx(Holder::Alice, unstake_tx, &unstake_params, current_slot).await?;
 
     assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
     assert!(
@@ -509,10 +185,9 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
 
     // Gather new unstaked owncoin
     let leaf_position = th.alice.merkle_tree.witness().unwrap();
-    let note: MoneyNote =
-        alice_money_unstake_params.output.note.decrypt(&th.alice.keypair.secret)?;
+    let note: MoneyNote = unstake_params.output.note.decrypt(&th.alice.keypair.secret)?;
     let alice_unstaked_oc = OwnCoin {
-        coin: Coin::from(alice_money_unstake_params.output.coin),
+        coin: Coin::from(unstake_params.output.coin),
         note: note.clone(),
         secret: th.alice.keypair.secret,
         nullifier: Nullifier::from(poseidon_hash([th.alice.keypair.secret.inner(), note.serial])),
@@ -523,44 +198,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     assert!(alice_rewarded_staked_oc.note.value == alice_unstaked_oc.note.value);
 
     // Statistics
-    let stake_avg = stake_sizes.iter().sum::<usize>();
-    let stake_avg = stake_avg / stake_sizes.len();
-    info!("Average Stake size: {:?} Bytes", stake_avg);
-    let stake_avg = stake_broadcasted_sizes.iter().sum::<usize>();
-    let stake_avg = stake_avg / stake_broadcasted_sizes.len();
-    info!("Average Stake broadcasted size: {:?} Bytes", stake_avg);
-    let stake_avg = stake_creation_times.iter().sum::<Duration>();
-    let stake_avg = stake_avg / stake_creation_times.len() as u32;
-    info!("Average Stake creation time: {:?}", stake_avg);
-    let stake_avg = stake_verify_times.iter().sum::<Duration>();
-    let stake_avg = stake_avg / stake_verify_times.len() as u32;
-    info!("Average Stake verification time: {:?}", stake_avg);
-
-    let proposal_avg = proposal_sizes.iter().sum::<usize>();
-    let proposal_avg = proposal_avg / proposal_sizes.len();
-    info!("Average Proposal size: {:?} Bytes", proposal_avg);
-    let proposal_avg = proposal_broadcasted_sizes.iter().sum::<usize>();
-    let proposal_avg = proposal_avg / proposal_broadcasted_sizes.len();
-    info!("Average Proposal broadcasted size: {:?} Bytes", proposal_avg);
-    let proposal_avg = proposal_creation_times.iter().sum::<Duration>();
-    let proposal_avg = proposal_avg / proposal_creation_times.len() as u32;
-    info!("Average Proposal creation time: {:?}", proposal_avg);
-    let proposal_avg = proposal_verify_times.iter().sum::<Duration>();
-    let proposal_avg = proposal_avg / proposal_verify_times.len() as u32;
-    info!("Average Proposal verification time: {:?}", proposal_avg);
-
-    let unstake_avg = unstake_sizes.iter().sum::<usize>();
-    let unstake_avg = unstake_avg / unstake_sizes.len();
-    info!("Average Unstake size: {:?} Bytes", unstake_avg);
-    let unstake_avg = unstake_broadcasted_sizes.iter().sum::<usize>();
-    let unstake_avg = unstake_avg / unstake_broadcasted_sizes.len();
-    info!("Average Unstake broadcasted size: {:?} Bytes", unstake_avg);
-    let unstake_avg = unstake_creation_times.iter().sum::<Duration>();
-    let unstake_avg = unstake_avg / unstake_creation_times.len() as u32;
-    info!("Average Unstake creation time: {:?}", unstake_avg);
-    let unstake_avg = unstake_verify_times.iter().sum::<Duration>();
-    let unstake_avg = unstake_avg / unstake_verify_times.len() as u32;
-    info!("Average Unstake verification time: {:?}", unstake_avg);
+    th.statistics();
 
     // Thanks for reading
     Ok(())