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

[stakeholder/stakeholder] leadcoin based off owncoins, slot with multiple competing coins

mohab metwally 3 лет назад
Родитель
Сommit
2af9a7cd8d
2 измененных файлов с 191 добавлено и 150 удалено
  1. 169 121
      src/blockchain/epoch.rs
  2. 22 29
      src/stakeholder/stakeholder.rs

+ 169 - 121
src/blockchain/epoch.rs

@@ -20,6 +20,7 @@ use crate::crypto::{
     proof::{Proof, ProvingKey},
     types::DrkValueBlind,
     util::{mod_r_p, pedersen_commitment_base, pedersen_commitment_u64},
+    coin::OwnCoin,
 };
 
 const PRF_NULLIFIER_PREFIX: u64 = 0;
@@ -68,7 +69,7 @@ impl EpochConsensus {
         self.e_len
     }
 
-    /// getter for the
+    /// getter for the ticks length in terms of seconds
     pub fn get_tick_len(&self) -> u64 {
         self.tick_len
     }
@@ -76,24 +77,42 @@ impl EpochConsensus {
 
 #[derive(Debug, Default, Clone)]
 pub struct Epoch {
+    pub consensus: EpochConsensus,
     // should have ep, slot, current block, etc.
-    //epoch metadata
-    pub len: Option<usize>, // number of slots in the epoch
-    pub value: Option<u64>, // the stake value is static during the epoch.
     pub eta: pallas::Base,    // CRS for the leader selection.
-    pub coins: Vec<LeadCoin>, // competing coins
+    coins: Vec<Vec<LeadCoin>>, // competing coins
 }
 
 impl Epoch {
     pub fn new(consensus: EpochConsensus, true_random: pallas::Base) -> Self {
         Self {
-            len: Some(consensus.get_slot_len() as usize),
-            value: Some(consensus.reward),
+            consensus: consensus,
             eta: true_random,
             coins: vec![],
         }
     }
-    fn create_coins_election_seeds(&self, sl: pallas::Base) -> (pallas::Base, pallas::Base) {
+
+
+    /// retrive leadership lottary coins of static stake,
+    /// retrived for for commitment in the genesis data
+    pub fn get_coins(&self) -> Vec<Vec<LeadCoin>> {
+        self.coins.clone()
+    }
+
+    pub fn len(&self)  -> usize {
+        self.consensus.get_epoch_len() as usize
+    }
+
+    pub fn col(&self) -> usize {
+        if self.coins.len()==0 {
+            0
+        } else {
+            self.coins[0].len()
+        }
+    }
+
+    //
+    fn create_coins_election_seeds(&self,  sl: pallas::Base) -> (pallas::Base, pallas::Base) {
         let election_seed_nonce: pallas::Base = pallas::Base::from(3);
         let election_seed_lead: pallas::Base = pallas::Base::from(22);
 
@@ -117,11 +136,11 @@ impl Epoch {
         for sk (secret key) at time i+1 is derived from secret key at time i.
          */
         let mut rng = thread_rng();
-        let mut tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(self.len.unwrap() as usize);
+        let mut tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(self.len());
         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..self.len.unwrap() {
+        for _i in 0..self.len(){
             //TODO (fix) add sk for the coin struct to be used in txs decryption of tx notes.
             let sk_bytes = if _i == 0 {
                 let base = pedersen_commitment_u64(1, pallas::Scalar::random(&mut rng));
@@ -152,140 +171,169 @@ impl Epoch {
         (root_sks, path_sks)
     }
     //note! the strategy here is single competing coin per slot.
-    pub fn create_coins(&mut self, sigma: pallas::Base) -> Vec<LeadCoin> {
+    pub fn create_coins (&mut self, sigma: pallas::Base, owned : Vec<OwnCoin>) -> Vec<Vec<LeadCoin>> {
         let mut rng = thread_rng();
         let mut seeds: Vec<u64> = vec![];
-        for _i in 0..self.len.unwrap() {
+        for _i in 0..self.len() {
             let rho: u64 = rng.gen();
             seeds.push(rho);
         }
         let (root_sks, path_sks) = self.create_coins_sks();
+
+        // matrix of leadcoins, each row has competing coins per slot.
+        let mut coins: Vec<Vec<LeadCoin>> = vec![];
+        for i in 0..self.len() {
+            // if you have any stake used is for competition
+            if owned.len()>0 {
+                let mut slot_coins = vec![];
+                for j in  0..owned.len() {
+                    let coin = self.create_leadcoin(sigma, owned[j].note.value, i, root_sks[i], path_sks[i], seeds[i]);
+                    slot_coins.push(coin.clone());
+                }
+                self.coins.push(slot_coins);
+            }
+            // otherwise compete with zero stake
+            else {
+                let coin = self.create_leadcoin(sigma, 0, i, root_sks[i], path_sks[i], seeds[i]);
+                self.coins.push(vec!(coin));
+            }
+        }
+        self.coins.clone()
+    }
+
+    pub fn create_leadcoin(&self, sigma: pallas::Base,
+                           value : u64,
+                           i: usize,
+                           c_root_sk: MerkleNode,
+                           c_path_sk: [MerkleNode; MERKLE_DEPTH_ORCHARD],
+                           seed: u64) -> LeadCoin {
         //random commitment blinding values
+        let mut rng = thread_rng();
         let c_cm1_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
         let c_cm2_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
+        let mut tree_cm = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(self.len());
+        let c_v = pallas::Base::from(value);
+        // coin relative slot index in the epoch
+        let c_sl = pallas::Base::from(u64::try_from(i).unwrap());
+        //
+        //let's assume it's sl for simplicity
+        let c_tau = pallas::Base::from(u64::try_from(i).unwrap());
+        //
 
-        let mut tree_cm = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(self.len.unwrap() as usize);
-        let mut coins: Vec<LeadCoin> = vec![];
-        for i in 0..self.len.unwrap() {
-            let c_v = pallas::Base::from(self.value.unwrap());
-            //random sampling of the same size of prf,
-            //pseudo random sampling that is the size of pederson commitment
-            // coin slot number
-
-            let c_sl = pallas::Base::from(u64::try_from(i).unwrap());
-            //
-            //let's assume it's sl for simplicity
-            let c_tau = pallas::Base::from(u64::try_from(i).unwrap());
-            //
-            let c_root_sk: MerkleNode = root_sks[i];
-
-            let coin_pk_msg = [c_tau, c_root_sk.inner()];
-            let c_pk: pallas::Base =
-                poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init(
-                )
-                .hash(coin_pk_msg);
-
-            let c_seed = pallas::Base::from(seeds[i]);
-            let sn_msg = [c_seed, c_root_sk.inner()];
-            let c_sn: pallas::Base =
-                poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init(
-                )
-                .hash(sn_msg);
-
-            let coin_commit_msg_input =
-                [pallas::Base::from(PRF_NULLIFIER_PREFIX), c_pk, c_v, c_seed];
-            let coin_commit_msg: pallas::Base =
-                poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init(
-                )
-                .hash(coin_commit_msg_input);
-            let c_cm: pallas::Point = pedersen_commitment_base(coin_commit_msg, c_cm1_blind);
-            let c_cm_coordinates = c_cm.to_affine().coordinates().unwrap();
-            let c_cm_base: pallas::Base = c_cm_coordinates.x() * c_cm_coordinates.y();
-            let c_cm_node = MerkleNode(c_cm_base);
-            tree_cm.append(&c_cm_node.clone());
-            let leaf_position = tree_cm.witness();
-            let c_root_cm = tree_cm.root(0).unwrap();
-            let c_cm_path =
-                tree_cm.authentication_path(leaf_position.unwrap(), &c_root_cm).unwrap();
+        let coin_pk_msg = [c_tau, c_root_sk.inner()];
+        let c_pk: pallas::Base =
+            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
+            .hash(coin_pk_msg);
 
-            let coin_nonce2_msg = [c_seed, c_root_sk.inner()];
-            let c_seed2: pallas::Base =
-                poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init(
-                )
-                .hash(coin_nonce2_msg);
+        let c_seed = pallas::Base::from(seed);
+        let sn_msg = [c_seed, c_root_sk.inner()];
+        let c_sn: pallas::Base =
+            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
+            .hash(sn_msg);
 
-            let coin2_commit_msg_input =
-                [pallas::Base::from(PRF_NULLIFIER_PREFIX), c_pk, c_v, c_seed2];
-            let coin2_commit_msg: pallas::Base =
-                poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init(
-                )
-                .hash(coin2_commit_msg_input);
-            let c_cm2 = pedersen_commitment_base(coin2_commit_msg, c_cm2_blind);
+        let coin_commit_msg_input =
+            [pallas::Base::from(PRF_NULLIFIER_PREFIX), c_pk, c_v, c_seed];
+        let coin_commit_msg: pallas::Base =
+            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init()
+            .hash(coin_commit_msg_input);
+        let c_cm: pallas::Point = pedersen_commitment_base(coin_commit_msg, c_cm1_blind);
+        let c_cm_coordinates = c_cm.to_affine().coordinates().unwrap();
+        let c_cm_base: pallas::Base = c_cm_coordinates.x() * c_cm_coordinates.y();
+        let c_cm_node = MerkleNode(c_cm_base);
+        tree_cm.append(&c_cm_node.clone());
+        let leaf_position = tree_cm.witness();
+        let c_root_cm = tree_cm.root(0).unwrap();
+        let c_cm_path =
+            tree_cm.authentication_path(leaf_position.unwrap(), &c_root_cm).unwrap();
 
-            let c_root_sk = root_sks[i];
-            let c_path_sk = path_sks[i];
+        let coin_nonce2_msg = [c_seed, c_root_sk.inner()];
+        let c_seed2: pallas::Base =
+            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init(
+            )
+            .hash(coin_nonce2_msg);
 
-            // election seeds
-            let (y_mu, rho_mu) = self.create_coins_election_seeds(c_sl);
-            let coin = LeadCoin {
-                value: Some(c_v),
-                cm: Some(c_cm),
-                cm2: Some(c_cm2),
-                idx: u32::try_from(i).unwrap(),
-                sl: Some(c_sl),
-                tau: Some(c_tau),
-                nonce: Some(c_seed),
-                nonce_cm: Some(c_seed2),
-                sn: Some(c_sn),
-                pk: Some(c_pk),
-                root_cm: Some(mod_r_p(c_root_cm.inner())),
-                root_sk: Some(c_root_sk.inner()),
-                path: Some(c_cm_path.as_slice().try_into().unwrap()),
-                path_sk: Some(c_path_sk),
-                c1_blind: Some(c_cm1_blind),
-                c2_blind: Some(c_cm2_blind),
-                y_mu: Some(y_mu),
-                rho_mu: Some(rho_mu),
-                sigma_scalar: Some(sigma),
-            };
-            coins.push(coin);
-        }
-        self.coins = coins.clone();
-        coins
-    }
+        let coin2_commit_msg_input =
+            [pallas::Base::from(PRF_NULLIFIER_PREFIX), c_pk, c_v, c_seed2];
+        let coin2_commit_msg: pallas::Base =
+            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init(
+            )
+            .hash(coin2_commit_msg_input);
+        let c_cm2 = pedersen_commitment_base(coin2_commit_msg, c_cm2_blind);
 
-    /// retrive leadership lottary coins of static stake,
-    /// retrived for for commitment in the genesis data
-    pub fn get_coins(&self) -> Vec<LeadCoin> {
-        self.coins.clone()
+        // election seeds
+        let (y_mu, rho_mu) = self.create_coins_election_seeds(c_sl);
+        let coin = LeadCoin {
+            value: Some(c_v),
+            cm: Some(c_cm),
+            cm2: Some(c_cm2),
+            idx: u32::try_from(i).unwrap(), //TODO should be abs slot
+            sl: Some(c_sl),
+            tau: Some(c_tau),
+            nonce: Some(c_seed),
+            nonce_cm: Some(c_seed2),
+            sn: Some(c_sn),
+            pk: Some(c_pk),
+            root_cm: Some(mod_r_p(c_root_cm.inner())),
+            root_sk: Some(c_root_sk.inner()),
+            path: Some(c_cm_path.as_slice().try_into().unwrap()),
+            path_sk: Some(c_path_sk),
+            c1_blind: Some(c_cm1_blind),
+            c2_blind: Some(c_cm2_blind),
+            y_mu: Some(y_mu),
+            rho_mu: Some(rho_mu),
+            sigma_scalar: Some(sigma),
+        };
+        coin
     }
-
     /// see if the participant stakeholder of this epoch is
-    /// winning the lottery, in case of success return True
-    pub fn is_leader(&self, sl: u64) -> bool {
+    /// winning the lottery
+    /// if stakeholder with multiple coins have multiple competing winning coins,
+    /// only the highest values coin is selected, since the stakeholder can't give more
+    /// than a proof per block.
+    /// * `sl` - slot relative index
+    /// * `idx` - index of the winning coin
+    /// returns true if the stakeholder is a leader for the current slot, else otherwise
+    pub fn is_leader(&self, sl: u64, idx:  &mut usize) -> bool {
         let slusize = sl as usize;
         debug!("slot: {}, coin len: {}", sl, self.coins.len());
         assert!(slusize < self.coins.len());
-        let coin = self.coins[sl as usize];
-        let y_exp = [coin.root_sk.unwrap(), coin.nonce.unwrap()];
-        let y_exp_hash: pallas::Base =
-            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
+        let competing_coins : &Vec<LeadCoin>= &self.coins.clone()[sl as usize];
+        let mut am_leader = vec![];
+        let mut highest_stake = pallas::Base::zero();
+        let mut highest_stake_idx : usize= 0;
+        for (idx, coin) in competing_coins.iter().enumerate() {
+            let y_exp = [coin.root_sk.unwrap(), coin.nonce.unwrap()];
+            let y_exp_hash: pallas::Base =
+                poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
                 .hash(y_exp);
-        // pick x coordiante of y for comparison
-        let y_x: pallas::Base = *pedersen_commitment_base(coin.y_mu.unwrap(), mod_r_p(y_exp_hash))
-            .to_affine()
-            .coordinates()
-            .unwrap()
-            .x();
-        let ord = pallas::Base::from(10241024); //TODO fine tune this scalar.
-        let target = ord * coin.value.unwrap();
-        debug!("y_x: {:?}, target: {:?}", y_x, target);
-        //TODO (FIX) reversed for testin
-        target < y_x
+            // pick x coordinate of y for comparison
+            let y_x: pallas::Base = *pedersen_commitment_base(coin.y_mu.unwrap(), mod_r_p(y_exp_hash))
+                .to_affine()
+                .coordinates()
+                .unwrap()
+                .x();
+            let ord = pallas::Base::from(10241024); //TODO fine tune this scalar.
+            let target = ord * coin.value.unwrap();
+            debug!("y_x: {:?}, target: {:?}", y_x, target);
+            //TODO (FIX) reversed for testin
+            let iam_leader =  target < y_x;
+            if iam_leader && coin.value.unwrap() > highest_stake {
+                highest_stake = coin.value.unwrap();
+                highest_stake_idx = idx;
+            }
+            am_leader.push(iam_leader);
+        }
+        am_leader.len() > 0
     }
 
-    pub fn get_proof(&self, sl: u64, pk: &ProvingKey) -> Proof {
-        let coin = self.coins[sl as usize];
+    /// * `sl` - relative slot index (zero based)
+    /// * `idx` - idex of the highest winning coin
+    /// * `pk` - proving key
+    /// returns  the of proof of the winning coin of slot `sl` at index `idx` with
+    /// proving key `pk`
+     pub fn get_proof(&self, sl: u64, idx : usize, pk: &ProvingKey) -> Proof {
+        let competing_coins : &Vec<LeadCoin> = &self.coins.clone()[sl as usize];
+        let coin = competing_coins[idx];
         lead_proof::create_lead_proof(pk, coin).unwrap()
     }
 }

+ 22 - 29
src/stakeholder/stakeholder.rs

@@ -21,6 +21,7 @@ use crate::{
         merkle_node::MerkleNode,
         proof::{Proof, ProvingKey, VerifyingKey},
         schnorr::{SchnorrPublic, SchnorrSecret, Signature},
+        coin::OwnCoin,
     },
     net::{MessageSubscription, P2p, Settings, SettingsPtr},
     tx::Transaction,
@@ -46,8 +47,7 @@ pub struct SlotWorkspace {
     pub e: u64,                // epoch index
     pub sl: u64,               // relative slot index
     pub txs: Vec<Transaction>, // unpublished block transactions
-    pub root: MerkleNode,
-    /// merkle root of txs
+    pub root: MerkleNode, /// merkle root of txs
     pub m: StakeholderMetadata,
     pub om: OuroborosMetadata,
     pub is_leader: bool,
@@ -116,13 +116,14 @@ impl SlotWorkspace {
     pub fn set_leader(&mut self, alead: bool) {
         self.is_leader = alead;
     }
+
 }
 
 pub struct Stakeholder {
     pub blockchain: Blockchain, // stakeholder view of the blockchain
     pub net: Arc<P2p>,
     pub clock: Clock,
-    pub coins: Vec<LeadCoin>,            // owned stakes
+    pub ownedcoins: Vec<OwnCoin>,        // owned stakes
     pub epoch: Epoch,                    // current epoch
     pub epoch_consensus: EpochConsensus, // configuration for the epoch
     pub pk: ProvingKey,
@@ -148,22 +149,14 @@ impl Stakeholder {
         let db = sled::open(&path)?;
         let ts = Timestamp::current_time();
         let genesis_hash = blake3::hash(b"");
-        //TODO lisen and add transactions
-
         let bc = Blockchain::new(&db, ts, genesis_hash).unwrap();
-
-        //TODO replace with const
         let eta = pallas::Base::one();
         let epoch = Epoch::new(consensus, eta);
 
         let lead_pk = ProvingKey::build(k.unwrap(), &LeadContract::default());
         let lead_vk = VerifyingKey::build(k.unwrap(), &LeadContract::default());
         let p2p = P2p::new(settings.clone()).await;
-
-        //TODO
         let workspace = SlotWorkspace::default();
-
-        //
         let clock = Clock::new(
             Some(consensus.get_epoch_len()),
             Some(consensus.get_slot_len()),
@@ -176,8 +169,7 @@ impl Stakeholder {
             blockchain: bc,
             net: p2p,
             clock,
-            coins: vec![], //constructed with empty coins for sake of simulation only
-            // but should be populated from wallet db.
+            ownedcoins: vec![], //TODO should be read from wallet db.
             epoch,
             epoch_consensus: consensus,
             pk: lead_pk,
@@ -369,7 +361,7 @@ impl Stakeholder {
         let reward = pallas::Base::one();
         let num_slots = num_slots + epochs * epoch_len;
         let sigma: pallas::Base = pallas::Base::from(num_slots) * reward;
-        epoch.create_coins(sigma); // set epoch interal fields working space with competing coins
+        epoch.create_coins(sigma, self.ownedcoins.clone()); // set epoch interal fields working space with competing coins
         self.epoch = epoch.clone();
     }
 
@@ -389,26 +381,27 @@ impl Stakeholder {
         } else {
             blake3::hash(b"")
         };
-        let is_leader: bool = self.epoch.is_leader(sl);
-        // if is leader create proof
-        let proof =
-            if is_leader { self.epoch.get_proof(sl, &self.pk.clone()) } else { Proof::new(vec![]) };
         // set workspace
         self.workspace.set_sl(sl);
         self.workspace.set_e(e);
         self.workspace.set_st(st);
-        self.workspace.set_leader(is_leader);
+        let mut winning_coin_idx :  usize = 0;
+        let won = self.epoch.is_leader(sl, &mut winning_coin_idx);
+        let proof = if won {
+            self.epoch.get_proof(sl, winning_coin_idx,  &self.pk.clone())
+        } else {
+            Proof::new(vec![])
+        };
+        self.workspace.set_leader(won);
         self.workspace.set_proof(proof.clone());
-        //
-        if is_leader {
-            let addr = Address::from(self.keypair.public);
-            let sign = self.sign(proof.as_ref());
-            let stakeholder_meta = StakeholderMetadata::new(sign, addr);
-            let ouroboros_meta =
-                OuroborosMetadata::new(self.get_eta().to_repr(), TransactionLeadProof::from(proof));
-            self.workspace.set_stakeholdermetadata(stakeholder_meta);
-            self.workspace.set_ouroborosmetadata(ouroboros_meta);
-        }
+
+        let addr = Address::from(self.keypair.public);
+        let sign = self.sign(proof.as_ref());
+        let stakeholder_meta = StakeholderMetadata::new(sign, addr);
+        let ouroboros_meta =
+            OuroborosMetadata::new(self.get_eta().to_repr(), TransactionLeadProof::from(proof));
+        self.workspace.set_stakeholdermetadata(stakeholder_meta);
+        self.workspace.set_ouroborosmetadata(ouroboros_meta);
     }
 }