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

contract/consensus: use previous proposal/block hash in vrf input

aggstam 3 лет назад
Родитель
Сommit
4e521f2826

+ 1 - 2
src/consensus/state.rs

@@ -599,8 +599,7 @@ impl ConsensusState {
         }
         }
 
 
         if hashes.is_empty() {
         if hashes.is_empty() {
-            let (_, hash) = self.blockchain.last().unwrap();
-            hashes.push(hash);
+            hashes.push(self.genesis_block);
         }
         }
 
 
         hashes
         hashes

+ 7 - 1
src/contract/consensus/src/client/proposal_v1.rs

@@ -115,6 +115,8 @@ pub struct ConsensusProposalCallBuilder {
     pub coin: ConsensusOwnCoin,
     pub coin: ConsensusOwnCoin,
     /// Rewarded slot checkpoint
     /// Rewarded slot checkpoint
     pub slot_checkpoint: SlotCheckpoint,
     pub slot_checkpoint: SlotCheckpoint,
+    /// Extending fork last proposal/block hash
+    pub previous_hash: blake3::Hash,
     /// Merkle tree of coins used to create inclusion proofs
     /// Merkle tree of coins used to create inclusion proofs
     pub tree: MerkleTree,
     pub tree: MerkleTree,
     /// `Proposal_V1` zkas circuit ZkBinary
     /// `Proposal_V1` zkas circuit ZkBinary
@@ -162,6 +164,7 @@ impl ConsensusProposalCallBuilder {
             &input,
             &input,
             &output,
             &output,
             &self.slot_checkpoint,
             &self.slot_checkpoint,
+            self.previous_hash,
         )?;
         )?;
 
 
         let input = ConsensusInput {
         let input = ConsensusInput {
@@ -205,6 +208,7 @@ impl ConsensusProposalCallBuilder {
             reward_blind,
             reward_blind,
             new_serial_commit,
             new_serial_commit,
             slot,
             slot,
+            previous_hash: self.previous_hash,
             vrf_proof,
             vrf_proof,
             y,
             y,
             rho,
             rho,
@@ -225,6 +229,7 @@ pub fn create_proposal_proof(
     input: &ConsensusBurnInputInfo,
     input: &ConsensusBurnInputInfo,
     output: &ConsensusMintOutputInfo,
     output: &ConsensusMintOutputInfo,
     slot_checkpoint: &SlotCheckpoint,
     slot_checkpoint: &SlotCheckpoint,
+    previous_hash: blake3::Hash,
 ) -> Result<(Proof, ConsensusProposalRevealed)> {
 ) -> Result<(Proof, ConsensusProposalRevealed)> {
     // Proof parameters
     // Proof parameters
     let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
     let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
@@ -278,8 +283,9 @@ pub fn create_proposal_proof(
 
 
     let slot_pallas = pallas::Base::from(slot_checkpoint.slot);
     let slot_pallas = pallas::Base::from(slot_checkpoint.slot);
     let seed = poseidon_hash([SEED_PREFIX, input.note.serial]);
     let seed = poseidon_hash([SEED_PREFIX, input.note.serial]);
-    let mut vrf_input = Vec::with_capacity(32 + 32);
+    let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
     vrf_input.extend_from_slice(&slot_checkpoint.previous_eta.to_repr());
     vrf_input.extend_from_slice(&slot_checkpoint.previous_eta.to_repr());
+    vrf_input.extend_from_slice(previous_hash.as_bytes());
     vrf_input.extend_from_slice(&slot_pallas.to_repr());
     vrf_input.extend_from_slice(&slot_pallas.to_repr());
     let vrf_proof = VrfProof::prove(input.secret, &vrf_input, &mut OsRng);
     let vrf_proof = VrfProof::prove(input.secret, &vrf_input, &mut OsRng);
     let mut eta = [0u8; 64];
     let mut eta = [0u8; 64];

+ 8 - 1
src/contract/consensus/src/entrypoint/proposal_v1.rs

@@ -95,10 +95,17 @@ pub(crate) fn consensus_proposal_get_metadata_v1(
     };
     };
     let slot_checkpoint: SlotCheckpoint = deserialize(&slot_checkpoint)?;
     let slot_checkpoint: SlotCheckpoint = deserialize(&slot_checkpoint)?;
 
 
+    // Verify proposal extends a known fork
+    if !slot_checkpoint.fork_hashes.contains(&params.previous_hash) {
+        msg!("[ConsensusProposalV1] Error: Proposal extends unknown fork {}", params.previous_hash);
+        return Err(ConsensusError::ProposalExtendsUnknownFork.into())
+    }
+
     // Verify eta VRF proof
     // Verify eta VRF proof
     let slot_pallas = pallas::Base::from(slot_checkpoint.slot);
     let slot_pallas = pallas::Base::from(slot_checkpoint.slot);
-    let mut vrf_input = Vec::with_capacity(32 + 32);
+    let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
     vrf_input.extend_from_slice(&slot_checkpoint.previous_eta.to_repr());
     vrf_input.extend_from_slice(&slot_checkpoint.previous_eta.to_repr());
+    vrf_input.extend_from_slice(params.previous_hash.as_bytes());
     vrf_input.extend_from_slice(&slot_pallas.to_repr());
     vrf_input.extend_from_slice(&slot_pallas.to_repr());
     let vrf_proof = &params.vrf_proof;
     let vrf_proof = &params.vrf_proof;
     if !vrf_proof.verify(params.input.signature_public, &vrf_input) {
     if !vrf_proof.verify(params.input.signature_public, &vrf_input) {

+ 7 - 3
src/contract/consensus/src/error.rs

@@ -23,6 +23,9 @@ pub enum ConsensusError {
     #[error("Missing slot checkpoint from db")]
     #[error("Missing slot checkpoint from db")]
     ProposalMissingSlotCheckpoint,
     ProposalMissingSlotCheckpoint,
 
 
+    #[error("Proposal extends unknown fork")]
+    ProposalExtendsUnknownFork,
+
     #[error("Eta VRF proof couldn't be verified")]
     #[error("Eta VRF proof couldn't be verified")]
     ProposalErroneousVrfProof,
     ProposalErroneousVrfProof,
 
 
@@ -37,9 +40,10 @@ impl From<ConsensusError> for ContractError {
     fn from(e: ConsensusError) -> Self {
     fn from(e: ConsensusError) -> Self {
         match e {
         match e {
             ConsensusError::ProposalMissingSlotCheckpoint => Self::Custom(1),
             ConsensusError::ProposalMissingSlotCheckpoint => Self::Custom(1),
-            ConsensusError::ProposalErroneousVrfProof => Self::Custom(2),
-            ConsensusError::CoinStillInGracePeriod => Self::Custom(3),
-            ConsensusError::CoinNotInUnstakeSet => Self::Custom(4),
+            ConsensusError::ProposalExtendsUnknownFork => Self::Custom(2),
+            ConsensusError::ProposalErroneousVrfProof => Self::Custom(3),
+            ConsensusError::CoinStillInGracePeriod => Self::Custom(4),
+            ConsensusError::CoinNotInUnstakeSet => Self::Custom(5),
         }
         }
     }
     }
 }
 }

+ 2 - 0
src/contract/consensus/src/model.rs

@@ -49,6 +49,8 @@ pub struct ConsensusProposalParamsV1 {
     pub new_serial_commit: pallas::Point,
     pub new_serial_commit: pallas::Point,
     /// Rewarded slot
     /// Rewarded slot
     pub slot: u64,
     pub slot: u64,
+    /// Extending fork last proposal/block hash
+    pub previous_hash: blake3::Hash,
     /// VRF proof for eta calculation
     /// VRF proof for eta calculation
     pub vrf_proof: VrfProof,
     pub vrf_proof: VrfProof,
     /// Coin y
     /// Coin y

+ 4 - 2
src/contract/consensus/tests/genesis_stake_unstake.rs

@@ -112,7 +112,9 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> {
     assert!(ALICE_INITIAL == alice_staked_oc.note.value);
     assert!(ALICE_INITIAL == alice_staked_oc.note.value);
 
 
     // We simulate the proposal of genesis slot
     // We simulate the proposal of genesis slot
-    let slot_checkpoint = th.get_slot_checkpoint_by_slot(current_slot).await?;
+    // We progress 1 slot and simulate its proposal
+    current_slot += 1;
+    let slot_checkpoint = th.generate_slot_checkpoint(current_slot).await?;
 
 
     // With alice's current coin value she can become the slot proposer,
     // With alice's current coin value she can become the slot proposer,
     // so she creates a proposal transaction to burn her staked coin,
     // so she creates a proposal transaction to burn her staked coin,
@@ -121,7 +123,7 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Alice] Building proposal tx");
     info!(target: "consensus", "[Alice] Building proposal tx");
     info!(target: "consensus", "[Alice] ====================");
     info!(target: "consensus", "[Alice] ====================");
     let (proposal_tx, proposal_params, proposal_secret_key) =
     let (proposal_tx, proposal_params, proposal_secret_key) =
-        th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone())?;
+        th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
 
 
     info!(target: "consensus", "[Faucet] ===========================");
     info!(target: "consensus", "[Faucet] ===========================");
     info!(target: "consensus", "[Faucet] Executing Alice proposal tx");
     info!(target: "consensus", "[Faucet] Executing Alice proposal tx");

+ 5 - 1
src/contract/consensus/tests/harness.rs

@@ -511,7 +511,7 @@ impl ConsensusTestHarness {
         Ok(())
         Ok(())
     }
     }
 
 
-    pub fn proposal(
+    pub async fn proposal(
         &mut self,
         &mut self,
         holder: Holder,
         holder: Holder,
         slot_checkpoint: SlotCheckpoint,
         slot_checkpoint: SlotCheckpoint,
@@ -523,10 +523,14 @@ impl ConsensusTestHarness {
         let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Proposal).unwrap();
         let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Proposal).unwrap();
         let timer = Instant::now();
         let timer = Instant::now();
 
 
+        // Proposals always extend genesis block
+        let previous_hash = wallet.state.read().await.consensus.genesis_block;
+
         // Building Consensus::Unstake params
         // Building Consensus::Unstake params
         let proposal_call_debris = ConsensusProposalCallBuilder {
         let proposal_call_debris = ConsensusProposalCallBuilder {
             coin: staked_oc,
             coin: staked_oc,
             slot_checkpoint,
             slot_checkpoint,
+            previous_hash,
             tree: wallet.consensus_merkle_tree.clone(),
             tree: wallet.consensus_merkle_tree.clone(),
             proposal_zkbin: proposal_zkbin.clone(),
             proposal_zkbin: proposal_zkbin.clone(),
             proposal_pk: proposal_pk.clone(),
             proposal_pk: proposal_pk.clone(),

+ 3 - 3
src/contract/consensus/tests/stake_unstake.rs

@@ -102,7 +102,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Malicious] Checking proposal before grace period");
     info!(target: "consensus", "[Malicious] Checking proposal before grace period");
     info!(target: "consensus", "[Malicious] =====================================");
     info!(target: "consensus", "[Malicious] =====================================");
     let (proposal_tx, _, _) =
     let (proposal_tx, _, _) =
-        th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone())?;
+        th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
     th.execute_erroneous_proposal_txs(Holder::Alice, vec![proposal_tx], current_slot, 1).await?;
     th.execute_erroneous_proposal_txs(Holder::Alice, vec![proposal_tx], current_slot, 1).await?;
 
 
     // We progress after grace period
     // We progress after grace period
@@ -116,7 +116,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Alice] Building proposal tx");
     info!(target: "consensus", "[Alice] Building proposal tx");
     info!(target: "consensus", "[Alice] ====================");
     info!(target: "consensus", "[Alice] ====================");
     let (proposal_tx, proposal_params, proposal_secret_key) =
     let (proposal_tx, proposal_params, proposal_secret_key) =
-        th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone())?;
+        th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
 
 
     info!(target: "consensus", "[Faucet] ===========================");
     info!(target: "consensus", "[Faucet] ===========================");
     info!(target: "consensus", "[Faucet] Executing Alice proposal tx");
     info!(target: "consensus", "[Faucet] Executing Alice proposal tx");
@@ -194,7 +194,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     info!(target: "consensus", "[Malicious] Checking using unstaked coin in proposal");
     info!(target: "consensus", "[Malicious] Checking using unstaked coin in proposal");
     info!(target: "consensus", "[Malicious] ========================================");
     info!(target: "consensus", "[Malicious] ========================================");
     let (proposal_tx, _, _) =
     let (proposal_tx, _, _) =
-        th.proposal(Holder::Alice, slot_checkpoint, alice_unstake_request_oc.clone())?;
+        th.proposal(Holder::Alice, slot_checkpoint, alice_unstake_request_oc.clone()).await?;
     th.execute_erroneous_proposal_txs(Holder::Alice, vec![proposal_tx], current_slot, 1).await?;
     th.execute_erroneous_proposal_txs(Holder::Alice, vec![proposal_tx], current_slot, 1).await?;
 
 
     info!(target: "consensus", "[Malicious] =============================");
     info!(target: "consensus", "[Malicious] =============================");