Bläddra i källkod

[consensus/state] verify eta/mu public values

mohab metwally 3 år sedan
förälder
incheckning
6c27aae820
6 ändrade filer med 68 tillägg och 40 borttagningar
  1. 2 1
      ChangeLog
  2. 17 18
      proof/lead.zk
  3. 19 12
      src/consensus/leadcoin.rs
  4. 26 5
      src/consensus/state.rs
  5. 2 4
      src/consensus/utils.rs
  6. 2 0
      src/error.rs

+ 2 - 1
ChangeLog

@@ -2,4 +2,5 @@
 	* wasm: implemented basic db functions needed for persistent state storage
 	* wasm: merge branch and implement prototype working smart contracts.
 	* doc: improved formatting and reorganized the structure.
-
+	* consensus: auto tuning lottery for single leader per slot.
+	* consensus: change y,rho to PRF rather than commitment to be able to constrain it's pallas::Base public values.

+ 17 - 18
proof/lead.zk

@@ -16,8 +16,8 @@ contract "Lead" {
          Scalar c1_opening,
          Base value,
          Scalar c2_opening,
-         Scalar rho_opening,
-         Scalar y_opening,
+         Base mu_rho,
+         Base mu_y,
          Base sigma1,
          Base sigma2,
 }
@@ -30,6 +30,14 @@ circuit "Lead" {
         PREFIX_CM = witness_base(4);
         PREFIX_PK = witness_base(5);
         PREFIX_SN = witness_base(6);
+        
+        # constrain public values
+        
+        # sigma1
+        #constrain_instance(sigma1);
+        # sigma2
+        #constrain_instance(sigma2);
+
         # coin (1) pk
         pk = poseidon_hash(PREFIX_PK, c1_sk_root, c1_tau, ZERO);
         constrain_instance(pk);
@@ -66,27 +74,18 @@ circuit "Lead" {
         # lottery seed
         seed = poseidon_hash(PREFIX_SEED, c1_sk_root, c1_rho, ZERO);
         # y
-        y_v = ec_mul_base(seed, NULLIFIER_K);
-        y_r = ec_mul(y_opening, VALUE_COMMIT_RANDOM);
-        y = ec_add(y_v, y_r);
-        y_x = ec_get_x(y);
-        y_y = ec_get_y(y);
-        y_hash = poseidon_hash(y_x, y_y);
-        constrain_instance(y_x);
-        constrain_instance(y_y);
+        y = poseidon_hash(seed, mu_y);
+        constrain_instance(mu_y);
+        constrain_instance(y);
         # rho
-        rho_v = ec_mul_base(seed, NULLIFIER_K);
-        rho_r = ec_mul(rho_opening, VALUE_COMMIT_RANDOM);
-        rho = ec_add(rho_v, rho_r);
-        rho_x = ec_get_x(rho);
-        rho_y = ec_get_y(rho);
-        constrain_instance(rho_x);
-        constrain_instance(rho_y);
+        rho = poseidon_hash(seed, mu_rho);
+        constrain_instance(mu_rho);
+        constrain_instance(rho);
         # target
         term1 =  base_mul(sigma1, value);
         term2_1 = base_mul(sigma2, value);
         term2 = base_mul(term2_1, value);
         target = base_add(term1, term2);
         #lottery
-        less_than_loose(y_hash, target);
+        less_than_loose(y, target);
 }

+ 19 - 12
src/consensus/leadcoin.rs

@@ -100,7 +100,7 @@ impl LeadCoin {
         eta: pallas::Base,
         // Stake value
         value: u64,
-        // Slot index in the epoch
+        // Slot absolute index
         slot_index: u64,
         // coin1 sk
         coin1_sk: pallas::Base,
@@ -184,7 +184,8 @@ impl LeadCoin {
     }
 
     /// Derive election seeds from given parameters
-    fn election_seeds(eta: pallas::Base, slot: pallas::Base) -> (pallas::Base, pallas::Base) {
+    pub fn election_seeds(eta: pallas::Base, slot: pallas::Base) -> (pallas::Base, pallas::Base) {
+        info!("LeadCoin::election_seeds(): eta: {:?}, slot: {:?}", eta, slot);
         let election_seed_nonce = pallas::Base::from(3);
         let election_seed_lead = pallas::Base::from(22);
 
@@ -223,11 +224,17 @@ impl LeadCoin {
         ];
         let seed = poseidon_hash(seed_msg);
         // y
-        let y = pedersen_commitment_base(seed, mod_r_p(self.y_mu));
-        let y_coords = y.to_affine().coordinates().unwrap();
+        let y_msg = [
+            seed,
+            self.y_mu,
+        ];
+        let y = poseidon_hash(y_msg);
         // rho
-        let rho = pedersen_commitment_base(seed, mod_r_p(self.rho_mu));
-        let rho_coord = rho.to_affine().coordinates().unwrap();
+        let rho_msg = [
+            seed,
+            self.rho_mu,
+        ];
+        let rho = poseidon_hash(rho_msg);
         vec![
             pk,
             *c1_cm.x(),
@@ -237,10 +244,10 @@ impl LeadCoin {
             self.coin1_commitment_root.inner(),
             self.coin1_sk_root.inner(),
             self.sn(),
-            *y_coords.x(),
-            *y_coords.y(),
-            *rho_coord.x(),
-            *rho_coord.y(),
+            self.y_mu,
+            y,
+            self.rho_mu,
+            rho,
         ]
     }
 
@@ -349,8 +356,8 @@ impl LeadCoin {
             Witness::Scalar(Value::known(self.coin1_blind)),
             Witness::Base(Value::known(pallas::Base::from(self.value))),
             Witness::Scalar(Value::known(self.coin2_blind)),
-            Witness::Scalar(Value::known(mod_r_p(self.rho_mu))),
-            Witness::Scalar(Value::known(mod_r_p(self.y_mu))),
+            Witness::Base(Value::known(self.rho_mu)),
+            Witness::Base(Value::known(self.y_mu)),
             Witness::Base(Value::known(sigma1)),
             Witness::Base(Value::known(sigma2)),
         ];

+ 26 - 5
src/consensus/state.rs

@@ -64,6 +64,8 @@ use crate::{
 const PI_NULLIFIER_INDEX: usize = 7;
 const PI_COMMITMENT_X_INDEX: usize = 1;
 const PI_COMMITMENT_Y_INDEX: usize = 2;
+const PI_MU_Y_INDEX: usize = 8;
+const PI_MU_RHO_INDEX: usize = 10;
 /// This struct represents the information required by the consensus algorithm
 #[derive(Debug)]
 pub struct ConsensusState {
@@ -428,9 +430,7 @@ impl ValidatorState {
     /// `epoch: absolute epoch index
     /// `slot: relative slot index
     fn sigmas(&mut self, epoch: u64, slot: u64) -> (pallas::Base, pallas::Base) {
-        //let f = Self::leadership_probability_with_all_stake().with_precision(RADIX_BITS).value();
         let f = self.win_prob_with_full_stake();
-        info!("Consensus: f: {}", f);
 
         // Generate sigmas
         let total_stake = self.total_stake_plus(epoch, slot); // Only used for fine-tuning
@@ -466,6 +466,7 @@ impl ValidatorState {
     /// 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) -> Result<Vec<Vec<LeadCoin>>> {
+        let slot = self.current_slot();
         let mut rng = thread_rng();
 
         let mut seeds: Vec<u64> = Vec::with_capacity(EPOCH_LENGTH);
@@ -488,10 +489,10 @@ impl ValidatorState {
             let coin = LeadCoin::new(
                 eta,
                 LOTTERY_HEAD_START, // TODO: TESTNET: Why is this constant being used?
-                i as u64,
+                slot as u64,
                 epoch_secrets.secret_keys[i].inner(),
                 epoch_secrets.merkle_roots[i],
-                i, //TODO same as idx now for simplicity.
+                i,
                 epoch_secrets.merkle_paths[i],
                 seeds[i],
                 epoch_secrets.secret_keys[i],
@@ -603,6 +604,7 @@ impl ValidatorState {
         let p = self.f_dif();
         let i = self.f_int();
         let d = self.f_der();
+        info!("Consensus::win_prob_with_full_stake(): Kp: {}", self.Kp.clone());
         while f <= zero || f >= one {
             f = self.Kp.clone() *
                 (p.clone() +
@@ -613,6 +615,7 @@ impl ValidatorState {
             } else if f <= zero {
                 self.Kp += step.clone();
             }
+            info!("Consensus::win_prob_with_full_stake(): f: {}", f);
         }
         f
     }
@@ -630,7 +633,7 @@ impl ValidatorState {
         // Stakeholder's epoch coins
         let coins = &self.consensus.coins;
 
-        info!("consensus::is_leader(): slot: {}, coins len: {}", slot, coins.len());
+        info!("Consensus::is_leader(): slot: {}, coins len: {}", slot, coins.len());
         assert!((slot as usize) < coins.len());
 
         let competing_coins = &coins[slot as usize];
@@ -766,6 +769,9 @@ impl ValidatorState {
     /// it extends. If the proposal extends the canonical blockchain, a new fork chain is created.
     pub async fn receive_proposal(&mut self, proposal: &BlockProposal) -> Result<()> {
         let current = self.current_slot();
+
+        let eta = self.consensus.epoch_eta;
+        let (mu_y, mu_rho) = LeadCoin::election_seeds(eta, pallas::Base::from(current));
         // Node hasn't started participating
         match self.consensus.participating {
             Some(start) => {
@@ -829,6 +835,21 @@ impl ValidatorState {
         };
         info!("receive_proposal(): Leader proof verified successfully!");
 
+        // verify proposal public values
+        // mu values
+        // y
+        let prop_mu_y = md.public_inputs[PI_MU_Y_INDEX];
+        if mu_y != prop_mu_y {
+            error!("failed to verify mu_y: {:?}, proposed: {:?}", mu_y, prop_mu_y);
+            return Err(Error::ProposalPublicValuesMismatched)
+        }
+        // rho
+        let prop_mu_rho = md.public_inputs[PI_MU_RHO_INDEX];
+        if mu_rho != prop_mu_rho {
+            error!("failed to verify mu_rho: {:?}, proposed: {:?}", mu_rho, prop_mu_rho);
+            return Err(Error::ProposalPublicValuesMismatched)
+        }
+
         // Verify proposal public inputs
         let prop_sn = md.public_inputs[PI_NULLIFIER_INDEX];
         for sn in &self.leaders_nullifiers {

+ 2 - 4
src/consensus/utils.rs

@@ -18,7 +18,7 @@
 
 use super::Float10;
 use dashu::integer::{IBig, Sign};
-use log::{debug, info};
+use log::{debug};
 use pasta_curves::pallas;
 //use pasta_curves::{group::ff::PrimeField};
 //use dashu::integer::{UBig};
@@ -49,13 +49,11 @@ pub fn base2ibig(base: pallas::Base) -> IBig {
 }
 */
 pub fn fbig2base(f: Float10) -> pallas::Base {
-    info!("fbig -> base (f): {}", f);
+    debug!("fbig -> base (f): {}", f);
     let val: IBig = fbig2ibig(f);
     let (sign, word) = val.as_sign_words();
-    //TODO (res) set pallas base sign, i.e sigma1 is negative.
     let mut words: [u64; 4] = [0, 0, 0, 0];
     words[..word.len()].copy_from_slice(word);
-
     match sign {
         Sign::Positive => pallas::Base::from_raw(words),
         Sign::Negative => pallas::Base::from_raw(words).neg(),

+ 2 - 0
src/error.rs

@@ -256,6 +256,8 @@ pub enum Error {
     #[error("unable to verify transfer transaction")]
     TransferTxVerification,
 
+    #[error("unable to verify proposed mu values")]
+    ProposalPublicValuesMismatched,
     // ===============
     // Database errors
     // ===============