Bläddra i källkod

consensus: Code cleanups

parazyd 3 år sedan
förälder
incheckning
83d700e85e

+ 1 - 1
src/consensus/block.rs

@@ -246,7 +246,7 @@ impl fmt::Display for BlockProposal {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         formatter.write_fmt(format_args!(
             "BlockProposal {{ leader addr: {}, hash: {}, epoch: {}, slot: {}, txs: {} }}",
-            self.block.metadata.address,
+            self.block.metadata.public_key,
             self.header,
             self.block.header.epoch,
             self.block.header.slot,

+ 0 - 282
src/consensus/coins.rs

@@ -1,282 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * 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 darkfi_sdk::{
-    crypto::{
-        constants::MERKLE_DEPTH_ORCHARD,
-        pedersen::{pedersen_commitment_base, pedersen_commitment_u64},
-        poseidon_hash,
-        util::mod_r_p,
-        MerkleNode, Nullifier, SecretKey, TokenId,
-    },
-    incrementalmerkletree::{bridgetree::BridgeTree, Tree},
-    pasta::{
-        arithmetic::CurveAffine,
-        group::{ff::PrimeField, Curve},
-        pallas,
-    },
-};
-use halo2_gadgets::poseidon::primitives as poseidon;
-use halo2_proofs::arithmetic::Field;
-use log::info;
-use rand::{rngs::OsRng, thread_rng, Rng};
-
-use super::{
-    constants::{EPOCH_LENGTH, LOTTERY_HEAD_START, P, RADIX_BITS, REWARD},
-    leadcoin::LeadCoin,
-    utils::fbig2base,
-    Float10,
-};
-use crate::{
-    crypto::{
-        coin::{Coin, OwnCoin},
-        note::Note,
-        types::{DrkCoinBlind, DrkSerial, DrkValueBlind},
-    },
-    wallet::walletdb::WalletDb,
-    Result,
-};
-
-const MERKLE_DEPTH: u8 = MERKLE_DEPTH_ORCHARD as u8;
-
-/// Retrieve previous epoch competing coins frequency.
-fn get_frequency() -> Float10 {
-    //TODO: Actually retrieve frequency of coins from the previous epoch.
-    let one: Float10 = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
-    let two: Float10 = Float10::from_str_native("2").unwrap().with_precision(RADIX_BITS).value();
-    one / two
-}
-
-/// Calculate nodes total stake for specific epoch and slot.
-fn total_stake(_epoch: u64, _slot: u64) -> u64 {
-    // TODO: fix this
-    //(epoch * EPOCH_LENGTH + slot + 1) * REWARD
-    REWARD
-}
-
-/// Generate epoch competing coins.
-pub fn create_epoch_coins(
-    eta: pallas::Base,
-    owned: &Vec<OwnCoin>,
-    epoch: u64,
-    slot: u64,
-) -> Vec<Vec<LeadCoin>> {
-    info!("Creating coins for epoch: {}", epoch);
-
-    // Retrieve previous epoch competing coins frequency
-    let frequency = get_frequency().with_precision(RADIX_BITS).value();
-    info!("Previous epoch frequency: {}", frequency);
-
-    // Generating sigmas
-    let total_stake = total_stake(epoch, slot); // only used for fine tunning
-    info!("Node total stake: {}", total_stake);
-    let one: Float10 = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
-    let two: Float10 = Float10::from_str_native("2").unwrap().with_precision(RADIX_BITS).value();
-    let field_p = Float10::from_str_native(P).unwrap().with_precision(RADIX_BITS).value();
-    let total_sigma = Float10::try_from(total_stake).unwrap().with_precision(RADIX_BITS).value();
-    let x = one - frequency;
-    info!("x: {}", x);
-    let c = x.ln();
-    info!("c: {}", c);
-    let sigma1_fbig = c.clone() / total_sigma.clone() * field_p.clone();
-    info!("sigma1: {}", sigma1_fbig);
-    let sigma1: pallas::Base = fbig2base(sigma1_fbig);
-    info!("sigma1 base: {:?}", sigma1);
-    let sigma2_fbig = (c / total_sigma).powf(two.clone()) * (field_p / two);
-    info!("sigma2: {}", sigma2_fbig);
-    let sigma2: pallas::Base = fbig2base(sigma2_fbig);
-    info!("sigma2 base: {:?}", sigma2);
-
-    create_coins(eta, owned, sigma1, sigma2)
-}
-
-/// Generate coins for provided sigmas.
-/// Note: the strategy here is single competing coin per slot.
-fn create_coins(
-    eta: pallas::Base,
-    owned: &Vec<OwnCoin>,
-    sigma1: pallas::Base,
-    sigma2: pallas::Base,
-) -> Vec<Vec<LeadCoin>> {
-    let mut rng = thread_rng();
-    let mut seeds: Vec<u64> = vec![];
-    for _i in 0..EPOCH_LENGTH {
-        let rho: u64 = rng.gen();
-        seeds.push(rho);
-    }
-    let (sks, root_sks, path_sks) = create_coins_sks();
-    let mut tree_cm = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(EPOCH_LENGTH as usize);
-    // Leadcoins matrix were each row represents a slot and contains its competing coins.
-    let mut coins: Vec<Vec<LeadCoin>> = vec![];
-
-    // Use existing stake
-    if !owned.is_empty() {
-        for i in 0..EPOCH_LENGTH {
-            let index = i as usize;
-            let mut slot_coins = vec![];
-            for elem in owned {
-                let coin = LeadCoin::new(
-                    eta,
-                    sigma1,
-                    sigma2,
-                    elem.note.value,
-                    index,
-                    root_sks[index],
-                    path_sks[index],
-                    seeds[index],
-                    sks[index],
-                    &mut tree_cm,
-                );
-                slot_coins.push(coin);
-            }
-            coins.push(slot_coins);
-            continue
-        }
-    } else {
-        for i in 0..EPOCH_LENGTH {
-            let index = i as usize;
-            // Compete with zero stake
-            let coin = LeadCoin::new(
-                eta,
-                sigma1,
-                sigma2,
-                LOTTERY_HEAD_START,
-                index,
-                root_sks[index],
-                path_sks[index],
-                seeds[index],
-                sks[index],
-                &mut tree_cm,
-            );
-            coins.push(vec![coin]);
-        }
-    }
-    coins
-}
-
-/// Generate epoch coins secret keys.
-/// First slot coin secret key is sampled at random,
-/// while the secret keys of the rest slots derive from previous slot secret.
-/// Clarification:
-///     sk[0] -> random,
-///     sk[1] -> derive_function(sk[0]),
-///     ...
-///     sk[n] -> derive_function(sk[n-1]),
-fn create_coins_sks() -> (Vec<SecretKey>, Vec<MerkleNode>, Vec<[MerkleNode; MERKLE_DEPTH_ORCHARD]>)
-{
-    let mut rng = thread_rng();
-    let mut tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(EPOCH_LENGTH as usize);
-    let mut sks: Vec<SecretKey> = vec![];
-    let mut root_sks: Vec<MerkleNode> = vec![];
-    let mut path_sks: Vec<[MerkleNode; MERKLE_DEPTH_ORCHARD]> = vec![];
-    let mut prev_sk_base: pallas::Base = pallas::Base::one();
-    for _i in 0..EPOCH_LENGTH {
-        let base: pallas::Point = if _i == 0 {
-            pedersen_commitment_u64(1, pallas::Scalar::random(&mut rng))
-        } else {
-            pedersen_commitment_u64(1, mod_r_p(prev_sk_base))
-        };
-        let coord = base.to_affine().coordinates().unwrap();
-        let sk_x = *coord.x();
-        let sk_y = *coord.y();
-        let sk_coord_ar = [sk_x, sk_y];
-        let sk_base: pallas::Base =
-            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
-                .hash(sk_coord_ar);
-        sks.push(SecretKey::from(sk_base));
-        prev_sk_base = sk_base;
-        let sk_bytes = sk_base.to_repr();
-        let node = MerkleNode::from_bytes(sk_bytes).unwrap();
-        tree.append(&node.clone());
-        let leaf_position = tree.witness();
-        let root = tree.root(0).unwrap();
-        let path = tree.authentication_path(leaf_position.unwrap(), &root).unwrap();
-        root_sks.push(root);
-        path_sks.push(path.as_slice().try_into().unwrap());
-    }
-    (sks, root_sks, path_sks)
-}
-
-/// Check that the provided participant/stakeholder coins win the slot lottery.
-/// If the stakeholder have multiple competing winning coins, only the highest value coin is selected,
-/// since the stakeholder can't give more than a proof per block(slot).
-/// * `slot` - slot relative index
-/// * `epoch_coins` - stakeholders epoch coins
-/// Returns: (check: bool, idx: usize) where idx is the winning coin index
-pub fn is_leader(slot: u64, epoch_coins: &Vec<Vec<LeadCoin>>) -> (bool, usize) {
-    let slot_usize = slot as usize;
-    info!("slot: {}, coins len: {}", slot, epoch_coins.len());
-    assert!(slot_usize < epoch_coins.len());
-    let competing_coins: &Vec<LeadCoin> = &epoch_coins[slot_usize];
-    let mut won = false;
-    let mut highest_stake = 0;
-    let mut highest_stake_idx: usize = 0;
-    for (winning_idx, coin) in competing_coins.iter().enumerate() {
-        let y_exp = [coin.coin1_sk_root.inner(), coin.nonce];
-        let y_exp_hash = poseidon_hash(y_exp);
-        let y_coordinates = pedersen_commitment_base(y_exp_hash, mod_r_p(coin.y_mu))
-            .to_affine()
-            .coordinates()
-            .unwrap();
-        //
-        let y_x: pallas::Base = *y_coordinates.x();
-        let y_y: pallas::Base = *y_coordinates.y();
-        let y_coord_arr = [y_x, y_y];
-        let y = poseidon_hash(y_coord_arr);
-        //
-        let val_base = pallas::Base::from(coin.value);
-        let target_base = coin.sigma1 * val_base + coin.sigma2 * val_base * val_base;
-        info!("y: {:?}", y);
-        info!("T: {:?}", target_base);
-        let first_winning = y < target_base;
-        if first_winning && !won {
-            highest_stake_idx = winning_idx;
-        }
-        won |= first_winning;
-        if won && coin.value > highest_stake {
-            highest_stake = coin.value;
-            highest_stake_idx = winning_idx;
-        }
-    }
-
-    (won, highest_stake_idx)
-}
-
-/// Generate staking coins for provided wallet.
-pub async fn generate_staking_coins(wallet: &WalletDb) -> Result<Vec<OwnCoin>> {
-    let keypair = wallet.get_default_keypair().await?;
-    let token_id = TokenId::from(pallas::Base::random(&mut OsRng));
-    let value = 420;
-    let serial = DrkSerial::random(&mut OsRng);
-    let note = Note {
-        serial,
-        value,
-        token_id,
-        coin_blind: DrkCoinBlind::random(&mut OsRng),
-        value_blind: DrkValueBlind::random(&mut OsRng),
-        token_blind: DrkValueBlind::random(&mut OsRng),
-        memo: vec![],
-    };
-    let coin = Coin(pallas::Base::random(&mut OsRng));
-    let nullifier = Nullifier::from(poseidon_hash::<2>([keypair.secret.inner(), serial]));
-    let leaf_position: incrementalmerkletree::Position = 0.into();
-    let coin = OwnCoin { coin, note, secret: keypair.secret, nullifier, leaf_position };
-    wallet.put_own_coin(coin.clone()).await?;
-
-    Ok(vec![coin])
-}

+ 1 - 1
src/consensus/constants.rs

@@ -44,7 +44,7 @@ pub const BLOCK_MAGIC_BYTES: [u8; 4] = [0x11, 0x6d, 0x75, 0x1f];
 pub const BLOCK_INFO_MAGIC_BYTES: [u8; 4] = [0x90, 0x44, 0xf1, 0xf6];
 
 /// Number of slots in one epoch
-pub const EPOCH_LENGTH: u64 = 10;
+pub const EPOCH_LENGTH: usize = 10;
 
 /// Block leader reward
 pub const REWARD: u64 = 420;

+ 59 - 3
src/consensus/leadcoin.rs

@@ -18,8 +18,10 @@
 
 use darkfi_sdk::{
     crypto::{
-        pedersen::pedersen_commitment_base, poseidon_hash, util::mod_r_p, MerkleNode, PublicKey,
-        SecretKey,
+        pedersen::{pedersen_commitment_base, pedersen_commitment_u64},
+        poseidon_hash,
+        util::mod_r_p,
+        MerkleNode, PublicKey, SecretKey,
     },
     pasta::{arithmetic::CurveAffine, group::Curve, pallas},
 };
@@ -28,7 +30,7 @@ use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
 use log::debug;
 use rand::rngs::OsRng;
 
-use super::constants::PRF_NULLIFIER_PREFIX;
+use super::constants::{EPOCH_LENGTH, PRF_NULLIFIER_PREFIX};
 use crate::{
     crypto::{proof::ProvingKey, Proof},
     zk::circuit::LeadContract,
@@ -272,3 +274,57 @@ impl LeadCoin {
         Ok(Proof::create(pk, &[circuit], &self.public_inputs(), &mut OsRng)?)
     }
 }
+
+/// This struct holds the secrets for creating LeadCoins during one epoch.
+pub struct LeadCoinSecrets {
+    pub secret_keys: Vec<SecretKey>,
+    pub merkle_roots: Vec<MerkleNode>,
+    pub merkle_paths: Vec<[MerkleNode; MERKLE_DEPTH_LEADCOIN]>,
+}
+
+impl LeadCoinSecrets {
+    /// Generate epoch coins secret keys.
+    /// First clot coin secret key is sampled at random, while the secret keys of the
+    /// remaining slots derive from the previous slot secret.
+    /// Clarification:
+    /// ```plaintext
+    /// sk[0] -> random,
+    /// sk[1] -> derive_function(sk[0]),
+    /// ...
+    /// sk[n] -> derive_function(sk[n-1]),
+    /// ```
+    pub fn generate() -> Self {
+        let mut tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(EPOCH_LENGTH);
+        let mut sks = Vec::with_capacity(EPOCH_LENGTH);
+        let mut root_sks = Vec::with_capacity(EPOCH_LENGTH);
+        let mut path_sks = Vec::with_capacity(EPOCH_LENGTH);
+
+        let mut prev_sk = SecretKey::from(pallas::Base::one());
+
+        for i in 0..EPOCH_LENGTH {
+            let secret = if i == 0 {
+                pedersen_commitment_u64(1, pallas::Scalar::random(&mut OsRng))
+            } else {
+                pedersen_commitment_u64(1, mod_r_p(prev_sk.inner()))
+            };
+
+            let secret_coords = secret.to_affine().coordinates().unwrap();
+            let secret_msg = [*secret_coords.x(), *secret_coords.y()];
+            let secret_key = SecretKey::from(poseidon_hash(secret_msg));
+
+            sks.push(secret_key);
+            prev_sk = secret_key;
+
+            let node = MerkleNode::from(secret_key.inner());
+            tree.append(&node);
+            let leaf_pos = tree.witness().unwrap();
+            let root = tree.root(0).unwrap();
+            let path = tree.authentication_path(leaf_pos, &root).unwrap();
+
+            root_sks.push(root);
+            path_sks.push(path.try_into().unwrap());
+        }
+
+        Self { secret_keys: sks, merkle_roots: root_sks, merkle_paths: path_sks }
+    }
+}

+ 7 - 7
src/consensus/metadata.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_sdk::{
-    crypto::{schnorr::Signature, Address, Keypair},
+    crypto::{schnorr::Signature, Keypair, PublicKey},
     pasta::pallas,
 };
 use darkfi_serial::{SerialDecodable, SerialEncodable};
@@ -38,8 +38,8 @@ use crate::{
 pub struct Metadata {
     /// Block owner signature
     pub signature: Signature,
-    /// Block owner address
-    pub address: Address,
+    /// Block owner public_key
+    pub public_key: PublicKey,
     /// Block owner slot competing coins public inputs
     pub public_inputs: Vec<pallas::Base>,
     /// Block owner newlly minted coin public inputs
@@ -56,10 +56,10 @@ pub struct Metadata {
     pub participants: Vec<Participant>,
 }
 
+// FIXME: Why do we even need default() ?
 impl Default for Metadata {
     fn default() -> Self {
         let keypair = Keypair::random(&mut OsRng);
-        let address = Address::from(keypair.public);
         let signature = Signature::dummy();
         let public_inputs = vec![];
         let new_public_inputs = vec![];
@@ -70,7 +70,7 @@ impl Default for Metadata {
         let participants = vec![];
         Self {
             signature,
-            address,
+            public_key: keypair.public,
             public_inputs,
             new_public_inputs,
             winning_index,
@@ -85,7 +85,7 @@ impl Default for Metadata {
 impl Metadata {
     pub fn new(
         signature: Signature,
-        address: Address,
+        public_key: PublicKey,
         public_inputs: Vec<pallas::Base>,
         new_public_inputs: Vec<pallas::Base>,
         winning_index: usize,
@@ -96,7 +96,7 @@ impl Metadata {
     ) -> Self {
         Self {
             signature,
-            address,
+            public_key,
             public_inputs,
             new_public_inputs,
             winning_index,

+ 2 - 5
src/consensus/mod.rs

@@ -45,9 +45,6 @@ pub mod task;
 pub mod clock;
 pub use clock::{Clock, Ticks};
 
-/// Ouroboros Crypsinous consensus coins functions
-pub mod coins;
-
 /// Consensus participation coin functions and definitions
 pub mod leadcoin;
 
@@ -58,5 +55,5 @@ pub use types::Float10;
 /// Utility functions
 pub mod utils;
 
-// Wallet functions
-//pub mod wallet;
+/// Wallet functions
+pub mod wallet;

+ 3 - 12
src/consensus/participant.rs

@@ -16,10 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi_sdk::{
-    crypto::{Address, PublicKey},
-    pasta::pallas,
-};
+use darkfi_sdk::{crypto::PublicKey, pasta::pallas};
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 
 use crate::net;
@@ -30,19 +27,13 @@ use crate::net;
 pub struct Participant {
     /// Node public key
     pub public_key: PublicKey,
-    /// Node wallet address
-    pub address: Address,
     /// Node current epoch competing coins public inputs
     pub coins: Vec<Vec<Vec<pallas::Base>>>,
 }
 
 impl Participant {
-    pub fn new(
-        public_key: PublicKey,
-        address: Address,
-        coins: Vec<Vec<Vec<pallas::Base>>>,
-    ) -> Self {
-        Self { public_key, address, coins }
+    pub fn new(public_key: PublicKey, coins: Vec<Vec<Vec<pallas::Base>>>) -> Self {
+        Self { public_key, coins }
     }
 }
 

+ 3 - 6
src/consensus/proto/protocol_participant.rs

@@ -75,12 +75,9 @@ impl ProtocolParticipant {
 
             debug!("ProtocolParticipant::handle_receive_participant() recv: {:?}", participant);
 
-            let participant_copy = (*participant).clone();
-
-            if self.state.write().await.append_participant(participant_copy.clone()) {
-                if let Err(e) =
-                    self.p2p.broadcast_with_exclude(participant_copy, &exclude_list).await
-                {
+            if self.state.write().await.append_participant(&participant) {
+                let p = (*participant).clone();
+                if let Err(e) = self.p2p.broadcast_with_exclude(p, &exclude_list).await {
                     error!("ProtocolParticipant::handle_receive_participant(): p2p broadcast failed: {}", e);
                 };
             }

+ 257 - 128
src/consensus/state.rs

@@ -23,36 +23,42 @@ use std::{
     time::Duration,
 };
 
-use async_std::sync::{Arc, Mutex, RwLock};
+use async_std::sync::{Arc, RwLock};
 use chrono::{NaiveDateTime, Utc};
 use darkfi_sdk::crypto::{
     constants::MERKLE_DEPTH,
+    pedersen::pedersen_commitment_base,
+    poseidon_hash,
     schnorr::{SchnorrPublic, SchnorrSecret},
-    Address, ContractId, MerkleNode, PublicKey, SecretKey,
+    util::mod_r_p,
+    ContractId, MerkleNode, PublicKey, SecretKey,
 };
 use darkfi_serial::{serialize, SerialDecodable, SerialEncodable};
 use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
-use lazy_init::Lazy;
 use log::{debug, error, info, warn};
-use pasta_curves::{group::ff::PrimeField, pallas};
-use rand::rngs::OsRng;
+use pasta_curves::{
+    arithmetic::CurveAffine,
+    group::{ff::PrimeField, Curve},
+    pallas,
+};
+use rand::{rngs::OsRng, thread_rng, Rng};
 
 use super::{
-    coins, leadcoin::LeadCoin, Block, BlockInfo, BlockProposal, Header, LeadProof, Metadata,
-    Participant, ProposalChain, DELTA, EPOCH_LENGTH, LEADER_PROOF_K,
+    constants::{DELTA, EPOCH_LENGTH, LEADER_PROOF_K, LOTTERY_HEAD_START, P, RADIX_BITS, REWARD},
+    leadcoin::{LeadCoin, LeadCoinSecrets},
+    utils::fbig2base,
+    Block, BlockInfo, BlockProposal, Float10, Header, LeadProof, Metadata, Participant,
+    ProposalChain,
 };
 
 use crate::{
     blockchain::Blockchain,
     crypto::proof::{ProvingKey, VerifyingKey},
     net,
-    node::{
-        state::{state_transition, ProgramState, StateUpdate},
-        Client, MemoryState, State,
-    },
     runtime::vm_runtime::Runtime,
     tx::Transaction,
     util::time::Timestamp,
+    wallet::WalletPtr,
     zk::circuit::LeadContract,
     Error, Result,
 };
@@ -67,7 +73,7 @@ pub struct ConsensusState {
     /// Fork chains containing block proposals
     pub proposals: Vec<ProposalChain>,
     /// Validators currently participating in the consensus
-    pub participants: BTreeMap<Address, Participant>,
+    pub participants: BTreeMap<[u8; 32], Participant>,
     /// Last slot participants where refreshed
     pub refreshed: u64,
     /// Current epoch
@@ -100,7 +106,7 @@ impl ConsensusState {
 #[derive(Debug, SerialEncodable, SerialDecodable)]
 pub struct ConsensusRequest {
     /// Validator wallet address
-    pub address: Address,
+    pub public_key: PublicKey,
 }
 
 impl net::Message for ConsensusRequest {
@@ -114,7 +120,7 @@ impl net::Message for ConsensusRequest {
 pub struct ConsensusResponse {
     /// Hot/live data used by the consensus algorithm
     pub proposals: Vec<ProposalChain>,
-    pub participants: BTreeMap<Address, Participant>,
+    pub participants: BTreeMap<[u8; 32], Participant>,
 }
 
 impl net::Message for ConsensusResponse {
@@ -128,28 +134,24 @@ pub type ValidatorStatePtr = Arc<RwLock<ValidatorState>>;
 
 /// This struct represents the state of a validator node.
 pub struct ValidatorState {
-    /// Node wallet address
-    pub address: Address,
-    /// Secret key, to sign messages
-    pub secret: SecretKey,
+    /// Node wallet public key
+    pub public_key: PublicKey,
+    /// Secret key used to sign messages
+    pub secret_key: SecretKey,
     /// Leader proof proving key
-    pub proving_key: ProvingKey,
+    pub lead_proving_key: ProvingKey,
     /// Leader proof verifying key
-    pub verifying_key: VerifyingKey,
-    /// Node public key
-    pub public: PublicKey,
+    pub lead_verifying_key: VerifyingKey,
     /// Hot/Live data used by the consensus algorithm
     pub consensus: ConsensusState,
     /// Canonical (finalized) blockchain
     pub blockchain: Blockchain,
-    /// Canonical state machine
-    pub state_machine: Arc<Mutex<State>>,
-    /// Client providing wallet access
-    pub client: Arc<Client>,
     /// Pending transactions
     pub unconfirmed_txs: Vec<Transaction>,
     /// Participating start slot
     pub participating: Option<u64>,
+    /// Wallet interface
+    pub wallet: WalletPtr,
 }
 
 impl ValidatorState {
@@ -157,15 +159,28 @@ impl ValidatorState {
         db: &sled::Db, // <-- TODO: Avoid this with some wrapping, sled should only be in blockchain
         genesis_ts: Timestamp,
         genesis_data: blake3::Hash,
-        client: Arc<Client>,
+        wallet: WalletPtr,
         cashier_pubkeys: Vec<PublicKey>,
         faucet_pubkeys: Vec<PublicKey>,
     ) -> Result<ValidatorStatePtr> {
-        let secret = SecretKey::random(&mut OsRng);
-        let public = PublicKey::from_secret(secret);
-        info!("Generating leader proof keys with k: {}", *LEADER_PROOF_K);
-        let proving_key = ProvingKey::build(*LEADER_PROOF_K, &LeadContract::default());
-        let verifying_key = VerifyingKey::build(*LEADER_PROOF_K, &LeadContract::default());
+        info!("Initializing ValidatorState");
+
+        info!("Initializing wallet tables for consensus");
+        // TODO: TESTNET: The stuff is kept entirely in memory for now, this should be written
+        //                into the wallet when necessary.
+        let consensus_tree_init_query = include_str!("../../script/sql/consensus_tree.sql");
+        let consensus_keys_init_query = include_str!("../../script/sql/consensus_keys.sql");
+        // TODO: TESTNET: consensus coin table
+        wallet.exec_sql(consensus_tree_init_query).await?;
+        wallet.exec_sql(consensus_keys_init_query).await?;
+
+        let secret_key = SecretKey::random(&mut OsRng);
+        let public_key = PublicKey::from_secret(secret_key);
+
+        info!("Generating leader proof keys with k: {}", LEADER_PROOF_K);
+        let lead_proving_key = ProvingKey::build(LEADER_PROOF_K, &LeadContract::default());
+        let lead_verifying_key = VerifyingKey::build(LEADER_PROOF_K, &LeadContract::default());
+
         let consensus = ConsensusState::new(genesis_ts, genesis_data)?;
         let blockchain = Blockchain::new(db, genesis_ts, genesis_data)?;
         let unconfirmed_txs = vec![];
@@ -187,17 +202,20 @@ impl ValidatorState {
         // transparent and generic, and the entire logic for this db protection is supposed to
         // be in the `init` function of the contract, so look there for a reference of the
         // databases and the state.
-        info!("ValidatorState::new(): Deploying \"money contract\"");
+        info!("ValidatorState::new(): Deploying \"money_contract.wasm\"");
         let money_contract_wasm_bincode = include_bytes!("../contract/money/money_contract.wasm");
         // XXX: FIXME: This ID should be something that does not solve the pallas curve equation,
         //             and/or just hardcoded and forbidden in non-native contract deployment.
         let cid = ContractId::from(pallas::Base::from(u64::MAX - 420));
         let mut runtime = Runtime::new(&money_contract_wasm_bincode[..], blockchain.clone(), cid)?;
+        // TODO: Faucet/Cashier keys as init payload
         runtime.deploy(&[])?;
         info!("Deployed Money Contract with ID: {}", cid);
         // -----END ARTIFACT-----
 
-        let address = client.wallet.get_default_address().await?;
+        // Implement consensus wallet/client
+        // FIXME TESTNET: let address = client.wallet.get_default_address().await?;
+        /*
         let state_machine = Arc::new(Mutex::new(State {
             tree: client.get_tree().await?,
             merkle_roots: blockchain.merkle_roots.clone(),
@@ -207,23 +225,22 @@ impl ValidatorState {
             mint_vk: Lazy::new(),
             burn_vk: Lazy::new(),
         }));
+        */
 
         // Create zk proof verification keys
-        let _ = state_machine.lock().await.mint_vk();
-        let _ = state_machine.lock().await.burn_vk();
+        //let _ = state_machine.lock().await.mint_vk();
+        //let _ = state_machine.lock().await.burn_vk();
 
         let state = Arc::new(RwLock::new(ValidatorState {
-            address,
-            secret,
-            public,
-            proving_key,
-            verifying_key,
+            public_key,
+            secret_key,
+            lead_proving_key,
+            lead_verifying_key,
             consensus,
             blockchain,
-            state_machine,
-            client,
             unconfirmed_txs,
             participating,
+            wallet,
         }));
 
         Ok(state)
@@ -247,15 +264,7 @@ impl ValidatorState {
         }
 
         debug!("append_tx(): Starting state transition validation");
-        let canon_state_clone = self.state_machine.lock().await.clone();
-        let mem_state = MemoryState::new(canon_state_clone);
-        match Self::validate_state_transitions(mem_state, &[tx.clone()]) {
-            Ok(_) => debug!("append_tx(): State transition valid"),
-            Err(e) => {
-                warn!("append_tx(): State transition fail: {}", e);
-                return false
-            }
-        }
+        // TODO TESTNET: Verify sigs, execute wasm, verify zk proofs
 
         debug!("append_tx(): Appended tx to mempool");
         self.unconfirmed_txs.push(tx);
@@ -270,7 +279,7 @@ impl ValidatorState {
     /// Calculates the epoch of the provided slot.
     /// Epoch duration is configured using the `EPOCH_LENGTH` value.
     pub fn slot_epoch(&self, slot: u64) -> u64 {
-        slot / EPOCH_LENGTH
+        slot / EPOCH_LENGTH as u64
     }
 
     /// Calculates current slot, based on elapsed time from the genesis block.
@@ -281,7 +290,7 @@ impl ValidatorState {
 
     /// Calculates the relative number of the provided slot.
     pub fn relative_slot(&self, slot: u64) -> u64 {
-        slot % EPOCH_LENGTH
+        slot % EPOCH_LENGTH as u64
     }
 
     /// Finds the last slot a proposal or block was generated.
@@ -323,8 +332,8 @@ impl ValidatorState {
     /// Epoch duration is configured using the EPOCH_LENGTH value.
     pub fn slots_to_next_n_epoch(&self, n: u64) -> u64 {
         assert!(n > 0);
-        let slots_till_next_epoch = EPOCH_LENGTH - self.relative_slot(self.current_slot());
-        ((n - 1) * EPOCH_LENGTH) + slots_till_next_epoch
+        let slots_till_next_epoch = EPOCH_LENGTH as u64 - self.relative_slot(self.current_slot());
+        ((n - 1) * EPOCH_LENGTH as u64) + slots_till_next_epoch
     }
 
     /// Calculates seconds until next Nth epoch starting time.
@@ -366,18 +375,160 @@ impl ValidatorState {
         }
         let eta = self.get_eta();
         // Retrieving nodes wallet coins
-        let owned = self.client.get_own_coins().await?;
+        // FIXME TESTNET: let owned = self.client.get_own_coins().await?;
+        //let owned = vec![];
         // TODO: slot parameter should be absolute slot, not relative.
         // At start of epoch, relative slot is 0.
-        self.consensus.coins = coins::create_epoch_coins(eta, &owned, epoch, 0);
+        self.consensus.coins = self.create_epoch_coins(eta, epoch, 0).await?;
         self.consensus.epoch = epoch;
         self.consensus.epoch_eta = eta;
         Ok(true)
     }
 
-    /// Wrapper for coins::is_leader
+    /// Generate epoch-competing coins
+    async fn create_epoch_coins(
+        &self,
+        eta: pallas::Base,
+        epoch: u64,
+        slot: u64,
+    ) -> Result<Vec<Vec<LeadCoin>>> {
+        info!("Consensus: Creating coins for epoch: {}", epoch);
+
+        // Retrieve previous epoch-competing coins' frequency
+        let frequency = Self::get_frequency().with_precision(RADIX_BITS).value();
+        info!("Consensus: Previous epoch frequency: {}", frequency);
+
+        // Generate sigmas
+        let total_stake = Self::total_stake(epoch, slot); // Only used for fine-tuning
+
+        let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
+        let two = Float10::from_str_native("2").unwrap().with_precision(RADIX_BITS).value();
+        let field_p = Float10::from_str_native(P).unwrap().with_precision(RADIX_BITS).value();
+        let total_sigma =
+            Float10::try_from(total_stake).unwrap().with_precision(RADIX_BITS).value();
+
+        let x = one - frequency;
+        let c = x.ln();
+
+        let sigma1_fbig = c.clone() / total_sigma.clone() * field_p.clone();
+        let sigma1 = fbig2base(sigma1_fbig);
+
+        let sigma2_fbig = (c / total_sigma).powf(two.clone()) * (field_p / two);
+        let sigma2 = fbig2base(sigma2_fbig);
+
+        self.create_coins(eta, sigma1, sigma2).await
+    }
+
+    /// Generate coins for provided sigmas.
+    /// NOTE: The strategy here is having a single competing coin per slot.
+    async fn create_coins(
+        &self,
+        eta: pallas::Base,
+        sigma1: pallas::Base,
+        sigma2: pallas::Base,
+    ) -> Result<Vec<Vec<LeadCoin>>> {
+        let mut rng = thread_rng();
+
+        let mut seeds: Vec<u64> = Vec::with_capacity(EPOCH_LENGTH);
+        for _ in 0..EPOCH_LENGTH {
+            seeds.push(rng.gen());
+        }
+
+        let epoch_secrets = LeadCoinSecrets::generate();
+
+        let mut tree_cm = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(EPOCH_LENGTH);
+        // LeadCoin matrix where each row represents a slot and contains its competing coins.
+        let mut coins: Vec<Vec<LeadCoin>> = Vec::with_capacity(EPOCH_LENGTH);
+
+        // TODO: TESTNET: Here we would look into the wallet to find coins we're able to use.
+        //                The wallet has specific tables for consensus coins.
+        // TODO: TESTNET: Token ID still has to be enforced properly in the consensus.
+
+        // Temporarily, we compete with zero stake
+        for i in 0..EPOCH_LENGTH {
+            let coin = LeadCoin::new(
+                eta,
+                sigma1,
+                sigma2,
+                LOTTERY_HEAD_START, // TODO: TESTNET: Why is this constant being used?
+                i,
+                epoch_secrets.merkle_roots[i],
+                epoch_secrets.merkle_paths[i],
+                seeds[i],
+                epoch_secrets.secret_keys[i],
+                &mut tree_cm,
+            );
+
+            coins.push(vec![coin]);
+        }
+
+        Ok(coins)
+    }
+
+    fn total_stake(epoch: u64, slot: u64) -> u64 {
+        // TODO: Fix this
+        // (epoch * EPOCH_LENGTH + slot + 1) * REWARD
+        REWARD
+    }
+
+    fn get_frequency() -> Float10 {
+        // TODO: Actually retrieve frequency of coins from the previous epoch.
+        let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
+        let two = Float10::from_str_native("2").unwrap().with_precision(RADIX_BITS).value();
+        one / two
+    }
+
+    /// Check that the provided participant/stakeholder coins win the slot lottery.
+    /// If the stakeholder has multiple competing winning coins, only the highest value
+    /// coin is selected, since the stakeholder can't give more than one proof per block/slot.
+    /// * `slot` - slot relative index
+    /// * `epoch_coins` - stakeholder's epoch coins
+    /// Returns: (check: bool, idx: usize) where idx is the winning coin's index
     pub fn is_slot_leader(&self) -> (bool, usize) {
-        coins::is_leader(self.relative_slot(self.current_slot()), &self.consensus.coins)
+        // Slot relative index
+        let slot = self.relative_slot(self.current_slot());
+        // Stakeholder's epoch coins
+        let coins = &self.consensus.coins;
+
+        info!("Consensus::is_leader(): slot: {}, coins len: {}", slot, coins.len());
+        assert!((slot as usize) < coins.len());
+
+        let competing_coins = &coins[slot as usize];
+
+        let mut won = false;
+        let mut highest_stake = 0;
+        let mut highest_stake_idx = 0;
+
+        for (winning_idx, coin) in competing_coins.iter().enumerate() {
+            let y_exp = [coin.coin1_sk_root.inner(), coin.nonce];
+            let y_exp_hash = poseidon_hash(y_exp);
+            let y_coords = pedersen_commitment_base(y_exp_hash, mod_r_p(coin.y_mu))
+                .to_affine()
+                .coordinates()
+                .unwrap();
+
+            let y_coords = [*y_coords.x(), *y_coords.y()];
+            let y = poseidon_hash(y_coords);
+
+            let value = pallas::Base::from(coin.value);
+            let target = coin.sigma1 * value + coin.sigma2 * value * value;
+
+            info!("Consensus::is_leader(): y = {:?}", y);
+            info!("Consensus::is_leader(): T = {:?}", target);
+
+            let first_winning = y < target;
+            if first_winning && !won {
+                highest_stake_idx = winning_idx;
+            }
+
+            won |= first_winning;
+            if won && coin.value > highest_stake {
+                highest_stake = coin.value;
+                highest_stake_idx = winning_idx;
+            }
+        }
+
+        (won, highest_stake_idx)
     }
 
     /// Generate a block proposal for the current slot, containing all
@@ -399,18 +550,18 @@ impl ValidatorState {
         let header =
             Header::new(prev_hash, self.slot_epoch(slot), slot, Timestamp::current_time(), root);
 
-        let signed_proposal = self.secret.sign(&mut OsRng, &header.headerhash().as_bytes()[..]);
+        let signed_proposal = self.secret_key.sign(&mut OsRng, &header.headerhash().as_bytes()[..]);
         let eta = self.consensus.epoch_eta.to_repr();
         // Generating leader proof
         let relative_slot = self.relative_slot(slot) as usize;
         let coin = self.consensus.coins[relative_slot][idx];
         // TODO: Generate new LeadCoin from newlly minted coin, will reuse original coin for now
         //let coin2 = something();
-        let proof = coin.create_lead_proof(&self.proving_key)?;
+        let proof = coin.create_lead_proof(&self.lead_proving_key)?;
         let participants = self.consensus.participants.values().cloned().collect();
         let metadata = Metadata::new(
             signed_proposal,
-            self.address,
+            self.public_key,
             coin.public_inputs(),
             coin.public_inputs(),
             idx,
@@ -495,31 +646,29 @@ impl ValidatorState {
             None => return Ok(None),
         }
 
+        let md = &proposal.block.metadata;
+        let hdr = &proposal.block.header;
+
         // Check if leader is a known consensus participant
-        let leader = self.consensus.participants.get(&proposal.block.metadata.address);
-        if leader.is_none() {
-            warn!(
-                "receive_proposal(): Received proposal from unknown node: ({})",
-                proposal.block.metadata.address
-            );
+        let Some(leader) = self.consensus.participants.get(&md.public_key.to_bytes()) else {
+            warn!("receive_proposal(): Received proposal from unknown node: ({})", md.public_key);
             return Err(Error::UnknownNodeError)
-        }
-        let mut leader = leader.unwrap().clone();
+        };
+        let mut leader = leader.clone();
 
         // Check if proposal header matches actual one
-        let proposal_header = proposal.block.header.headerhash();
+        let proposal_header = hdr.headerhash();
         if proposal.header != proposal_header {
             warn!(
-                "receive_proposal(): Received proposal contains missmatched headers: {} - {}",
+                "receive_proposal(): Received proposal contains mismatched headers: {} - {}",
                 proposal.header, proposal_header
             );
             return Err(Error::ProposalHeadersMissmatchError)
         }
 
         // Verify proposal winning coin public inputs match known ones
-        let public_inputs = &leader.coins[self.relative_slot(current) as usize]
-            [proposal.block.metadata.winning_index];
-        if public_inputs != &proposal.block.metadata.public_inputs {
+        let public_inputs = &leader.coins[self.relative_slot(current) as usize][md.winning_index];
+        if public_inputs != &md.public_inputs {
             warn!("receive_proposal(): Received proposal public inputs are invalid.");
             return Err(Error::InvalidPublicInputsError)
         }
@@ -527,53 +676,36 @@ impl ValidatorState {
         // TODO: Verify winning coin serial number
 
         // Verify proposal leader proof
-        match proposal.block.metadata.proof.verify(&self.verifying_key, public_inputs) {
-            Ok(_) => info!("receive_proposal(): Proof veryfied succsessfully!"),
-            Err(e) => {
-                error!("receive_proposal(): Error during leader proof verification: {}", e);
-                return Err(Error::LeaderProofVerificationError)
-            }
-        }
+        if let Err(e) = md.proof.verify(&self.lead_verifying_key, public_inputs) {
+            error!("receive_proposal(): Error during leader proof verification: {}", e);
+            return Err(Error::LeaderProofVerification)
+        };
+        info!("receive_proposal(): Leader proof verified successfully!");
 
         // Verify proposal signature is valid based on leader known valid key
-        if !leader.public_key.verify(proposal.header.as_bytes(), &proposal.block.metadata.signature)
-        {
-            warn!(
-                "receive_proposal(): Proposer ({}) signature could not be verified",
-                proposal.block.metadata.address
-            );
+        if !leader.public_key.verify(proposal.header.as_bytes(), &md.signature) {
+            warn!("receive_proposal(): Proposer {} signature could not be verified", md.public_key);
             return Err(Error::InvalidSignature)
         }
 
         // Check if proposal extends any existing fork chains
         let index = self.find_extended_chain_index(proposal)?;
         if index == -2 {
-            return Err(Error::ExtendedChainIndexNotFoundError)
+            return Err(Error::ExtendedChainIndexNotFound)
         }
 
         // Validate state transition against canonical state
         // TODO: This should be validated against fork state
         debug!("receive_proposal(): Starting state transition validation");
-        let canon_state_clone = self.state_machine.lock().await.clone();
-        let mem_state = MemoryState::new(canon_state_clone);
-
-        match Self::validate_state_transitions(mem_state, &proposal.block.txs) {
-            Ok(_) => {
-                debug!("receive_proposal(): State transition valid")
-            }
-            Err(e) => {
-                warn!("receive_proposal(): State transition fail: {}", e);
-                return Err(Error::StateTransitionError)
-            }
-        }
+        // TODO TESTNET: Verify sigs in block's transactions, execute wasm, verify zkps
 
         // TODO: [PLACEHOLDER] Add rewards validation
         // TODO: Append serial to merkle tree
 
         // Replacing participants public inputs with the newlly minted ones
-        leader.coins[self.relative_slot(current) as usize][proposal.block.metadata.winning_index] =
-            proposal.block.metadata.new_public_inputs.clone();
-        self.append_participant(leader);
+        leader.coins[self.relative_slot(current) as usize][md.winning_index] =
+            md.new_public_inputs.clone();
+        self.append_participant(&leader);
 
         // Check if proposal fork has can be finalized, to broadcast those blocks
         let mut to_broadcast = vec![];
@@ -709,13 +841,9 @@ impl ValidatorState {
 
         for proposal in &finalized {
             // TODO: Is this the right place? We're already doing this in protocol_sync.
-            // TODO: These state transitions have already been checked.
+            // TODO: These state transitions have already been checked. (I wrote this, but where?)
             debug!(target: "consensus", "Applying state transition for finalized block");
-            let canon_state_clone = self.state_machine.lock().await.clone();
-            let mem_st = MemoryState::new(canon_state_clone);
-            let state_updates = Self::validate_state_transitions(mem_st, &proposal.txs)?;
-            self.update_canon_state(state_updates, None).await?;
-            self.remove_txs(proposal.txs.clone())?;
+            // TODO TESTNET: verify all sigs, execute wasm, verify zkps in proposal.txs (see git diff)
         }
 
         let last_block = *blockhashes.last().unwrap();
@@ -737,14 +865,14 @@ impl ValidatorState {
     }
 
     /// Append a new participant to the participants list.
-    pub fn append_participant(&mut self, participant: Participant) -> bool {
-        if let Some(p) = self.consensus.participants.get(&participant.address) {
-            if p == &participant {
+    pub fn append_participant(&mut self, participant: &Participant) -> bool {
+        if let Some(p) = self.consensus.participants.get(&participant.public_key.to_bytes()) {
+            if p == participant {
                 return false
             }
         }
         // TODO: [PLACEHOLDER] don't blintly trust the public inputs/validate them
-        self.consensus.participants.insert(participant.address, participant);
+        self.consensus.participants.insert(participant.public_key.to_bytes(), participant.clone());
         true
     }
 
@@ -762,28 +890,25 @@ impl ValidatorState {
     // ==========================
     // State transition functions
     // ==========================
+    // TODO TESTNET: Write down all cases below
+    // State transition checks should be happening in the following cases for a sync node:
+    // 1) When a finalized block is received
+    // 2) When a transaction is being broadcasted to us
+    // State transition checks should be happening in the following cases for a consensus participating node:
+    // 1) When a finalized block is received
+    // 2) When a transaction is being broadcasted to us
+    // ==========================
 
     /// Validate and append to canonical state received blocks.
     pub async fn receive_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
         // Verify state transitions for all blocks and their respective transactions.
         debug!("receive_blocks(): Starting state transition validations");
-        let mut canon_updates = vec![];
-        let canon_state_clone = self.state_machine.lock().await.clone();
-        let mut mem_state = MemoryState::new(canon_state_clone);
-        for block in blocks {
-            let mut state_updates =
-                Self::validate_state_transitions(mem_state.clone(), &block.txs)?;
-
-            for update in &state_updates {
-                mem_state.apply(update.clone());
-            }
+        // TODO TESTNET: verify sigs, execute wasm, verify zkps (see git diff)
 
-            canon_updates.append(&mut state_updates);
-        }
         debug!("receive_blocks(): All state transitions passed");
 
         debug!("receive_blocks(): Updating canon state");
-        self.update_canon_state(canon_updates, None).await?;
+        //self.update_canon_state(canon_updates, None).await?;
 
         debug!("receive_blocks(): Appending blocks to ledger");
         self.blockchain.add(blocks)?;
@@ -847,6 +972,7 @@ impl ValidatorState {
         Ok(())
     }
 
+    /*
     /// Validate state transitions for given transactions and state and
     /// return a vector of [`StateUpdate`]
     pub fn validate_state_transitions(
@@ -870,7 +996,9 @@ impl ValidatorState {
 
         Ok(ret)
     }
+    */
 
+    /*
     /// Apply a vector of [`StateUpdate`] to the canonical state.
     pub async fn update_canon_state(
         &self,
@@ -893,4 +1021,5 @@ impl ValidatorState {
         debug!("update_canon_state(): Successfully applied state updates");
         Ok(())
     }
+    */
 }

+ 2 - 1
src/consensus/task/consensus_sync.rs

@@ -43,7 +43,7 @@ pub async fn consensus_sync_task(p2p: P2pPtr, state: ValidatorStatePtr) -> Resul
             let response_sub = channel.subscribe_msg::<ConsensusResponse>().await?;
 
             // Node creates a `ConsensusRequest` and sends it
-            let request = ConsensusRequest { address: state.read().await.address };
+            let request = ConsensusRequest { public_key: state.read().await.public_key };
             channel.send(request).await?;
 
             // Node verifies response came from a participating node.
@@ -53,6 +53,7 @@ pub async fn consensus_sync_task(p2p: P2pPtr, state: ValidatorStatePtr) -> Resul
                 warn!("Retrieved consensus state from a new node, retrying...");
                 continue
             }
+
             // Node stores response data.
             let mut lock = state.write().await;
             lock.consensus.proposals = response.proposals.clone();

+ 3 - 4
src/consensus/task/proposal.rs

@@ -73,8 +73,7 @@ pub async fn proposal_task(consensus_p2p: P2pPtr, sync_p2p: P2pPtr, state: Valid
             Ok(changed) => {
                 if changed {
                     info!("consensus: New epoch started: {}", state.read().await.current_epoch());
-                    let public = state.read().await.public;
-                    let address = state.read().await.address;
+                    let public_key = state.read().await.public_key;
                     let mut coins = vec![];
                     for slot_coins in &state.read().await.consensus.coins {
                         let mut slot_coins_inputs = vec![];
@@ -83,8 +82,8 @@ pub async fn proposal_task(consensus_p2p: P2pPtr, sync_p2p: P2pPtr, state: Valid
                         }
                         coins.push(slot_coins_inputs);
                     }
-                    let participant = Participant::new(public, address, coins);
-                    state.write().await.append_participant(participant.clone());
+                    let participant = Participant::new(public_key, coins);
+                    state.write().await.append_participant(&participant);
 
                     match consensus_p2p.broadcast(participant).await {
                         Ok(()) => {

+ 53 - 0
src/consensus/wallet.rs

@@ -0,0 +1,53 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2022 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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 async_trait::async_trait;
+use darkfi_sdk::crypto::{Keypair, PublicKey, SecretKey};
+use darkfi_serial::deserialize;
+use log::debug;
+use sqlx::Row;
+
+use crate::{wallet::WalletDb, Result};
+
+const CONSENSUS_KEYS_TABLE: &str = "consensus_keys";
+const CONSENSUS_KEYS_COLUMN_IS_DEFAULT: &str = "is_default";
+
+#[async_trait]
+pub trait ConsensusWallet {
+    async fn get_default_keypair(&self) -> Result<Keypair>;
+}
+
+#[async_trait]
+impl ConsensusWallet for WalletDb {
+    async fn get_default_keypair(&self) -> Result<Keypair> {
+        debug!("Returning default keypair");
+        let mut conn = self.conn.acquire().await?;
+
+        let row = sqlx::query(&format!(
+            "SELECT * FROM {} WHERE {} = 1",
+            CONSENSUS_KEYS_TABLE, CONSENSUS_KEYS_COLUMN_IS_DEFAULT
+        ))
+        .fetch_one(&mut conn)
+        .await?;
+
+        let public: PublicKey = deserialize(row.get("public"))?;
+        let secret: SecretKey = deserialize(row.get("secret"))?;
+
+        Ok(Keypair { secret, public })
+    }
+}