Ver Fonte

contract/money/pow_reward: vrf proof added

aggstam há 2 anos atrás
pai
commit
15adf6f9cb

+ 1 - 0
Cargo.lock

@@ -1600,6 +1600,7 @@ dependencies = [
 name = "darkfi-money-contract"
 version = "0.4.1"
 dependencies = [
+ "blake3",
  "bs58",
  "chacha20poly1305",
  "darkfi",

+ 1 - 0
src/contract/money/Cargo.toml

@@ -9,6 +9,7 @@ edition = "2021"
 crate-type = ["cdylib", "rlib"]
 
 [dependencies]
+blake3 = "1.4.1"
 bs58 = "0.5.0"
 darkfi-sdk = { path = "../../sdk" }
 darkfi-serial = { path = "../../serial", features = ["derive", "crypto"] }

+ 27 - 4
src/contract/money/src/client/pow_reward_v1.rs

@@ -23,7 +23,10 @@ use darkfi::{
 };
 use darkfi_sdk::{
     blockchain::expected_reward,
-    crypto::{note::AeadEncryptedNote, pasta_prelude::*, Keypair, PublicKey, DARK_TOKEN_ID},
+    crypto::{
+        ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::*, Keypair, PublicKey,
+        DARK_TOKEN_ID,
+    },
     pasta::pallas,
 };
 use log::{debug, info};
@@ -37,11 +40,11 @@ use crate::{
         },
         MoneyNote,
     },
-    model::{ClearInput, Coin, MoneyTokenMintParamsV1, Output},
+    model::{ClearInput, Coin, MoneyPoWRewardParamsV1, Output},
 };
 
 pub struct PoWRewardCallDebris {
-    pub params: MoneyTokenMintParamsV1,
+    pub params: MoneyPoWRewardParamsV1,
     pub proofs: Vec<Proof>,
 }
 
@@ -67,6 +70,13 @@ pub struct PoWRewardCallBuilder {
     pub keypair: Keypair,
     /// Rewarded block height(slot)
     pub block_height: u64,
+    /// Extending fork last proposal/block nonce
+    pub last_nonce: pallas::Base,
+    /// Extending fork last proposal/block hash
+    pub fork_hash: blake3::Hash,
+    /// Extending fork second to last proposal/block hash
+    pub fork_previous_hash: blake3::Hash,
+    /// Merkle tree of coins used to create inclusion proofs
     /// Spend hook for the output
     pub spend_hook: pallas::Base,
     /// User data for the output
@@ -142,7 +152,20 @@ impl PoWRewardCallBuilder {
             note: encrypted_note,
         };
 
-        let params = MoneyTokenMintParamsV1 { input: c_input, output: c_output };
+        info!("Building Consensus::ProposalV1 VRF proof");
+        let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
+        vrf_input.extend_from_slice(&self.last_nonce.to_repr());
+        vrf_input.extend_from_slice(self.fork_previous_hash.as_bytes());
+        vrf_input.extend_from_slice(&pallas::Base::from(self.block_height).to_repr());
+        let vrf_proof = VrfProof::prove(self.keypair.secret, &vrf_input, &mut OsRng);
+
+        let params = MoneyPoWRewardParamsV1 {
+            input: c_input,
+            output: c_output,
+            fork_hash: self.fork_hash,
+            fork_previous_hash: self.fork_previous_hash,
+            vrf_proof,
+        };
         let debris = PoWRewardCallDebris { params, proofs: vec![proof] };
         Ok(debris)
     }

+ 34 - 2
src/contract/money/src/entrypoint/pow_reward_v1.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_sdk::{
-    blockchain::{expected_reward, POW_CUTOFF},
+    blockchain::{expected_reward, Slot, POW_CUTOFF},
     crypto::{
         pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, MerkleNode,
         DARK_TOKEN_ID,
@@ -26,7 +26,7 @@ use darkfi_sdk::{
     error::{ContractError, ContractResult},
     merkle_add, msg,
     pasta::pallas,
-    util::get_verifying_slot,
+    util::{get_slot, get_verifying_slot},
     ContractCall,
 };
 use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
@@ -95,6 +95,38 @@ pub(crate) fn money_pow_reward_process_instruction_v1(
         return Err(MoneyError::PoWRewardCallAfterCutoffSlot.into())
     }
 
+    // Grab the slot to validate consensus params against
+    let Some(slot) = get_slot(verifying_slot)? else {
+        msg!("[PoWRewardV1] Error: Missing slot {} from db", verifying_slot);
+        return Err(MoneyError::PoWRewardMissingSlot.into())
+    };
+    let slot: Slot = deserialize(&slot)?;
+
+    // Verify proposal extends a known fork
+    if !slot.previous.last_hashes.contains(&params.fork_hash) {
+        msg!("[PoWRewardV1] Error: Block extends unknown fork {}", params.fork_hash);
+        return Err(MoneyError::PoWRewardExtendsUnknownFork.into())
+    }
+
+    // Verify sequence is correct
+    if !slot.previous.second_to_last_hashes.contains(&params.fork_previous_hash) {
+        let fork_prev = &params.fork_previous_hash;
+        msg!("[PoWRewardV1] Error: Block extends unknown fork {}", fork_prev);
+        return Err(MoneyError::PoWRewardExtendsUnknownFork.into())
+    }
+
+    // Construct VRF input
+    let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
+    vrf_input.extend_from_slice(&slot.last_nonce.to_repr());
+    vrf_input.extend_from_slice(params.fork_previous_hash.as_bytes());
+    vrf_input.extend_from_slice(&pallas::Base::from(slot.id).to_repr());
+
+    // Verify VRF proof
+    if !params.vrf_proof.verify(params.input.signature_public, &vrf_input) {
+        msg!("[PoWRewardV1] Error: VRF proof couldn't be verified");
+        return Err(MoneyError::PoWRewardErroneousVrfProof.into())
+    }
+
     // Only DARK_TOKEN_ID can be minted as PoW reward.
     if params.input.token_id != *DARK_TOKEN_ID {
         msg!("[PoWRewardV1] Error: Clear input used non-native token");

+ 12 - 0
src/contract/money/src/error.rs

@@ -120,6 +120,15 @@ pub enum MoneyError {
 
     #[error("Call is executed after cutoff slot")]
     PoWRewardCallAfterCutoffSlot,
+
+    #[error("Missing slot from db")]
+    PoWRewardMissingSlot,
+
+    #[error("Block extends unknown fork")]
+    PoWRewardExtendsUnknownFork,
+
+    #[error("Eta VRF proof couldn't be verified")]
+    PoWRewardErroneousVrfProof,
 }
 
 impl From<MoneyError> for ContractError {
@@ -158,6 +167,9 @@ impl From<MoneyError> for ContractError {
             MoneyError::GenesisCallNonGenesisSlot => Self::Custom(31),
             MoneyError::MissingNullifier => Self::Custom(32),
             MoneyError::PoWRewardCallAfterCutoffSlot => Self::Custom(33),
+            MoneyError::PoWRewardMissingSlot => Self::Custom(34),
+            MoneyError::PoWRewardExtendsUnknownFork => Self::Custom(35),
+            MoneyError::PoWRewardErroneousVrfProof => Self::Custom(36),
         }
     }
 }

+ 8 - 2
src/contract/money/src/model.rs

@@ -18,8 +18,8 @@
 
 use darkfi_sdk::{
     crypto::{
-        note::AeadEncryptedNote, pasta_prelude::PrimeField, MerkleNode, Nullifier, PublicKey,
-        TokenId,
+        ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::PrimeField, MerkleNode, Nullifier,
+        PublicKey, TokenId,
     },
     error::ContractError,
     pasta::pallas,
@@ -213,6 +213,12 @@ pub struct MoneyPoWRewardParamsV1 {
     pub input: ClearInput,
     /// Anonymous output
     pub output: Output,
+    /// Extending fork last proposal/block hash
+    pub fork_hash: blake3::Hash,
+    /// Extending fork second to last proposal/block hash
+    pub fork_previous_hash: blake3::Hash,
+    /// VRF proof for block rank calculation
+    pub vrf_proof: VrfProof,
 }
 
 /// State update for `Money::PoWReward`

+ 1 - 1
src/contract/test-harness/src/consensus_proposal.rs

@@ -54,7 +54,7 @@ impl TestHarness {
         let timer = Instant::now();
 
         // Proposals always extend genesis block
-        let fork_hash = self.genesis_block;
+        let fork_hash = self.genesis_block.hash()?;
 
         // Building Consensus::Propose params
         let proposal_call_debris = ConsensusProposalCallBuilder {

+ 3 - 8
src/contract/test-harness/src/lib.rs

@@ -201,7 +201,7 @@ pub struct TestHarness {
     pub holders: HashMap<Holder, Wallet>,
     pub proving_keys: HashMap<String, (ProvingKey, ZkBinary)>,
     pub tx_action_benchmarks: HashMap<TxAction, TxActionBenchmarks>,
-    pub genesis_block: blake3::Hash,
+    pub genesis_block: BlockInfo,
 }
 
 impl TestHarness {
@@ -270,12 +270,7 @@ impl TestHarness {
         tx_action_benchmarks.insert(TxAction::DaoVote, TxActionBenchmarks::default());
         tx_action_benchmarks.insert(TxAction::DaoExec, TxActionBenchmarks::default());
 
-        Ok(Self {
-            holders,
-            proving_keys,
-            tx_action_benchmarks,
-            genesis_block: genesis_block.hash()?,
-        })
+        Ok(Self { holders, proving_keys, tx_action_benchmarks, genesis_block })
     }
 
     pub async fn execute_erroneous_txs(
@@ -458,7 +453,7 @@ impl TestHarness {
     pub async fn generate_slot(&self, id: u64) -> Result<Slot> {
         // We grab the genesis slot to generate slot
         // using same consensus parameters
-        let genesis_block = self.genesis_block;
+        let genesis_block = self.genesis_block.hash()?;
         let genesis_slot = self.get_slot_by_slot(0).await?;
         let previous = PreviousSlot::new(0, vec![genesis_block], vec![genesis_block], 0.0);
         let pid = PidOutput::new(0.0, 0.0, genesis_slot.pid.sigma1, genesis_slot.pid.sigma2);

+ 10 - 3
src/contract/test-harness/src/money_pow_reward.rs

@@ -20,7 +20,7 @@ use std::time::Instant;
 
 use darkfi::{tx::Transaction, Result};
 use darkfi_money_contract::{
-    client::pow_reward_v1::PoWRewardCallBuilder, model::MoneyTokenMintParamsV1, MoneyFunction,
+    client::pow_reward_v1::PoWRewardCallBuilder, model::MoneyPoWRewardParamsV1, MoneyFunction,
     MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
@@ -39,7 +39,7 @@ impl TestHarness {
         holder: &Holder,
         block_height: u64,
         reward: Option<u64>,
-    ) -> Result<(Transaction, MoneyTokenMintParamsV1)> {
+    ) -> Result<(Transaction, MoneyPoWRewardParamsV1)> {
         let wallet = self.holders.get(holder).unwrap();
 
         let (mint_pk, mint_zkbin) =
@@ -50,6 +50,10 @@ impl TestHarness {
 
         let timer = Instant::now();
 
+        // Proposals always extend genesis block
+        let last_nonce = self.genesis_block.header.nonce;
+        let fork_hash = self.genesis_block.hash()?;
+
         // We're just going to be using a zero spend-hook and user-data
         let spend_hook = pallas::Base::zero();
         let user_data = pallas::Base::zero();
@@ -57,6 +61,9 @@ impl TestHarness {
         let builder = PoWRewardCallBuilder {
             keypair: wallet.keypair,
             block_height,
+            last_nonce,
+            fork_hash,
+            fork_previous_hash: fork_hash,
             spend_hook,
             user_data,
             mint_zkbin: mint_zkbin.clone(),
@@ -92,7 +99,7 @@ impl TestHarness {
         &mut self,
         holder: &Holder,
         tx: &Transaction,
-        params: &MoneyTokenMintParamsV1,
+        params: &MoneyPoWRewardParamsV1,
         block_height: u64,
     ) -> Result<()> {
         let wallet = self.holders.get_mut(holder).unwrap();