Explorar el Código

validator: producer tx on last instead of first position in block txs vector and proper header Merkle tree validation

skoupidi hace 2 años
padre
commit
2ce7f38880

+ 4 - 1
bin/darkfid/src/tests/harness.rs

@@ -76,8 +76,11 @@ impl Harness {
         // Generate default genesis block
         let mut genesis_block = BlockInfo::default();
 
+        // Retrieve genesis producer transaction
+        let producer_tx = genesis_block.txs.pop().unwrap();
+
         // Append genesis transactions
-        genesis_block.txs.push(genesis_mint_tx);
+        genesis_block.append_txs(vec![genesis_mint_tx, producer_tx])?;
 
         // Generate validators configuration
         // NOTE: we are not using consensus constants here so we

+ 11 - 6
src/blockchain/block_store.rs

@@ -19,7 +19,7 @@
 use darkfi_sdk::{
     crypto::{
         schnorr::{SchnorrSecret, Signature},
-        SecretKey,
+        MerkleTree, SecretKey,
     },
     pasta::{group::ff::FromUniformBytes, pallas},
 };
@@ -119,11 +119,7 @@ impl BlockInfo {
 
     /// Append a transaction to the block. Also adds it to the Merkle tree.
     pub fn append_tx(&mut self, tx: Transaction) -> Result<()> {
-        let mut buf = [0u8; 64];
-        buf[..blake3::OUT_LEN].copy_from_slice(tx.hash()?.as_bytes());
-        let leaf = pallas::Base::from_uniform_bytes(&buf);
-
-        self.header.tree.append(leaf.into());
+        append_tx_to_merkle_tree(&mut self.header.tree, &tx)?;
         self.txs.push(tx);
 
         Ok(())
@@ -662,3 +658,12 @@ impl BlockDifficultyStoreOverlay {
         Ok(())
     }
 }
+
+/// Auxiliary function to append a transaction to a Merkle tree.
+pub fn append_tx_to_merkle_tree(tree: &mut MerkleTree, tx: &Transaction) -> Result<()> {
+    let mut buf = [0u8; 64];
+    buf[..blake3::OUT_LEN].copy_from_slice(tx.hash()?.as_bytes());
+    let leaf = pallas::Base::from_uniform_bytes(&buf);
+    tree.append(leaf.into());
+    Ok(())
+}

+ 9 - 2
src/contract/money/src/client/fee_v1.rs

@@ -78,8 +78,15 @@ pub async fn append_fee_call(
     // First we will verify the fee-less transaction to see how much gas
     // it uses for execution and verification.
     let tx = tx_builder.build()?;
-    let gas_used =
-        verify_transaction(overlay, verifying_block_height, &tx, verifying_keys, false).await?;
+    let gas_used = verify_transaction(
+        overlay,
+        verifying_block_height,
+        &tx,
+        &mut MerkleTree::new(1),
+        verifying_keys,
+        false,
+    )
+    .await?;
 
     // TODO: We could actually take a set of coins and then find one with
     //       enough value, instead of expecting one. It depends, the API

+ 2 - 0
src/contract/test-harness/src/lib.rs

@@ -194,6 +194,8 @@ impl TestHarness {
         let mut holders = HashMap::new();
         let mut genesis_block = BlockInfo::default();
         genesis_block.header.timestamp = Timestamp(1689772567);
+        let producer_tx = genesis_block.txs.pop().unwrap();
+        genesis_block.append_txs(vec![producer_tx])?;
 
         // Deterministic PRNG
         let mut rng = Pcg32::new(42);

+ 12 - 3
src/validator/consensus.rs

@@ -16,7 +16,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi_sdk::{crypto::SecretKey, pasta::pallas};
+use darkfi_sdk::{
+    crypto::{MerkleTree, SecretKey},
+    pasta::pallas,
+};
 use darkfi_serial::{async_trait, serialize, SerialDecodable, SerialEncodable};
 use log::{debug, error, info};
 use num_bigint::BigUint;
@@ -387,8 +390,14 @@ impl Fork {
         let overlay = self.overlay.lock().unwrap().full_clone()?;
 
         // Verify transactions
-        if let Err(e) =
-            verify_transactions(&overlay, verifying_block_height, &unproposed_txs, false).await
+        if let Err(e) = verify_transactions(
+            &overlay,
+            verifying_block_height,
+            &unproposed_txs,
+            &mut MerkleTree::new(1),
+            false,
+        )
+        .await
         {
             match e {
                 crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(erroneous_txs)) => {

+ 53 - 7
src/validator/mod.rs

@@ -18,7 +18,7 @@
 
 use std::sync::Arc;
 
-use darkfi_sdk::crypto::PublicKey;
+use darkfi_sdk::crypto::{MerkleTree, PublicKey};
 use darkfi_serial::serialize_async;
 use log::{debug, error, info, warn};
 use num_bigint::BigUint;
@@ -182,7 +182,15 @@ impl Validator {
             let next_block_height = fork.get_next_block_height()?;
 
             // Verify transaction
-            match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
+            match verify_transactions(
+                &overlay,
+                next_block_height,
+                &tx_vec,
+                &mut MerkleTree::new(1),
+                false,
+            )
+            .await
+            {
                 Ok(_) => {}
                 Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => continue,
                 Err(e) => return Err(e),
@@ -200,7 +208,15 @@ impl Validator {
         let overlay = BlockchainOverlay::new(&self.blockchain)?;
         let next_block_height = self.blockchain.last_block()?.header.height + 1;
         let mut erroneous_txs = vec![];
-        match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
+        match verify_transactions(
+            &overlay,
+            next_block_height,
+            &tx_vec,
+            &mut MerkleTree::new(1),
+            false,
+        )
+        .await
+        {
             Ok(_) => valid = true,
             Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(etx))) => {
                 erroneous_txs = etx
@@ -255,7 +271,15 @@ impl Validator {
                 let next_block_height = fork.get_next_block_height()?;
 
                 // Verify transaction
-                match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
+                match verify_transactions(
+                    &overlay,
+                    next_block_height,
+                    &tx_vec,
+                    &mut MerkleTree::new(1),
+                    false,
+                )
+                .await
+                {
                     Ok(_) => {
                         valid = true;
                         continue
@@ -271,7 +295,15 @@ impl Validator {
             // Verify transaction against canonical state
             let overlay = BlockchainOverlay::new(&self.blockchain)?;
             let next_block_height = self.blockchain.last_block()?.header.height + 1;
-            match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
+            match verify_transactions(
+                &overlay,
+                next_block_height,
+                &tx_vec,
+                &mut MerkleTree::new(1),
+                false,
+            )
+            .await
+            {
                 Ok(_) => valid = true,
                 Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => {}
                 Err(e) => return Err(e),
@@ -425,7 +457,14 @@ impl Validator {
         let overlay = BlockchainOverlay::new(&self.blockchain)?;
 
         // Verify all transactions and get erroneous ones
-        let e = verify_transactions(&overlay, verifying_block_height, txs, self.verify_fees).await;
+        let e = verify_transactions(
+            &overlay,
+            verifying_block_height,
+            txs,
+            &mut MerkleTree::new(1),
+            self.verify_fees,
+        )
+        .await;
         let lock = overlay.lock().unwrap();
         let mut overlay = lock.overlay.lock().unwrap();
 
@@ -461,7 +500,14 @@ impl Validator {
 
         // Verify transaction
         let mut erroneous_txs = vec![];
-        if let Err(e) = verify_producer_transaction(&overlay, verifying_block_height, tx).await {
+        if let Err(e) = verify_producer_transaction(
+            &overlay,
+            verifying_block_height,
+            tx,
+            &mut MerkleTree::new(1),
+        )
+        .await
+        {
             warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {}", e);
             erroneous_txs.push(tx.clone());
         }

+ 61 - 22
src/validator/verification.rs

@@ -21,7 +21,8 @@ use std::collections::HashMap;
 use darkfi_sdk::{
     blockchain::{block_epoch, block_version},
     crypto::{
-        schnorr::SchnorrPublic, ContractId, PublicKey, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID,
+        schnorr::SchnorrPublic, ContractId, MerkleTree, PublicKey, DEPLOYOOOR_CONTRACT_ID,
+        MONEY_CONTRACT_ID,
     },
     dark_tree::dark_forest_leaf_vec_integrity_check,
     deploy::DeployParamsV1,
@@ -35,7 +36,9 @@ use num_bigint::BigUint;
 use smol::io::Cursor;
 
 use crate::{
-    blockchain::{BlockInfo, Blockchain, BlockchainOverlayPtr},
+    blockchain::{
+        block_store::append_tx_to_merkle_tree, BlockInfo, Blockchain, BlockchainOverlayPtr,
+    },
     error::TxVerifyFailed,
     runtime::vm_runtime::Runtime,
     tx::{Transaction, MAX_TX_CALLS, MIN_TX_CALLS},
@@ -78,15 +81,17 @@ pub async fn verify_genesis_block(overlay: &BlockchainOverlayPtr, block: &BlockI
         return Err(Error::BlockContainsNoTransactions(block_hash))
     }
 
-    // Genesis transaction must be the Transaction::default() one(empty)
-    if block.txs[0] != Transaction::default() {
-        error!(target: "validator::verification::verify_genesis_block", "Genesis proposal transaction is not default one");
-        return Err(TxVerifyFailed::ErroneousTxs(vec![block.txs[0].clone()]).into())
+    // Genesis producer transaction must be the Transaction::default() one(empty)
+    let producer_tx = block.txs.last().unwrap();
+    if producer_tx != &Transaction::default() {
+        error!(target: "validator::verification::verify_genesis_block", "Genesis producer transaction is not default one");
+        return Err(TxVerifyFailed::ErroneousTxs(vec![producer_tx.clone()]).into())
     }
 
-    // Verify transactions, exluding producer(first) one
-    let txs = &block.txs[1..];
-    if let Err(e) = verify_transactions(overlay, block.header.height, txs, false).await {
+    // Verify transactions, exluding producer(last) one
+    let mut tree = MerkleTree::new(1);
+    let txs = &block.txs[..block.txs.len() - 1];
+    if let Err(e) = verify_transactions(overlay, block.header.height, txs, &mut tree, false).await {
         warn!(
             target: "validator::verification::verify_genesis_block",
             "[VALIDATOR] Erroneous transactions found in set",
@@ -95,6 +100,13 @@ pub async fn verify_genesis_block(overlay: &BlockchainOverlayPtr, block: &BlockI
         return Err(e)
     }
 
+    // Append producer transaction to the tree and check tree matches header one
+    append_tx_to_merkle_tree(&mut tree, producer_tx)?;
+    if tree != block.header.tree {
+        error!(target: "validator::verification::verify_genesis_block", "Genesis Merkle tree is invalid");
+        return Err(Error::BlockIsInvalid(block_hash))
+    }
+
     // Insert block
     overlay.lock().unwrap().add_block(block)?;
 
@@ -189,14 +201,10 @@ pub async fn verify_block(
         return Err(Error::BlockContainsNoTransactions(block_hash))
     }
 
-    // Verify proposal transaction.
-    let public_key =
-        verify_producer_transaction(overlay, block.header.height, &block.txs[0]).await?;
-    verify_producer_signature(block, &public_key)?;
-
-    // Verify transactions, exluding producer(first) one
-    let txs = &block.txs[1..];
-    let e = verify_transactions(overlay, block.header.height, txs, false).await;
+    // Verify transactions, exluding producer(last) one
+    let mut tree = MerkleTree::new(1);
+    let txs = &block.txs[..block.txs.len() - 1];
+    let e = verify_transactions(overlay, block.header.height, txs, &mut tree, false).await;
     if let Err(e) = e {
         warn!(
             target: "validator::verification::verify_block",
@@ -206,6 +214,22 @@ pub async fn verify_block(
         return Err(e)
     }
 
+    // Verify producer transaction
+    let public_key = verify_producer_transaction(
+        overlay,
+        block.header.height,
+        block.txs.last().unwrap(),
+        &mut tree,
+    )
+    .await?;
+    verify_producer_signature(block, &public_key)?;
+
+    // Verify tree matches header one
+    if tree != block.header.tree {
+        error!(target: "validator::verification::verify_block", "Block Merkle tree is invalid");
+        return Err(Error::BlockIsInvalid(block_hash))
+    }
+
     // Insert block
     overlay.lock().unwrap().add_block(block)?;
 
@@ -226,10 +250,12 @@ pub fn verify_producer_signature(block: &BlockInfo, public_key: &PublicKey) -> R
 
 /// Verify WASM execution, signatures, and ZK proofs for a given producer [`Transaction`],
 /// and apply it to the provided overlay. Returns transaction signature public key.
+/// Additionally, append its hash to the provided Merkle tree.
 pub async fn verify_producer_transaction(
     overlay: &BlockchainOverlayPtr,
     verifying_block_height: u64,
     tx: &Transaction,
+    tree: &mut MerkleTree,
 ) -> Result<PublicKey> {
     let tx_hash = tx.hash()?;
     debug!(target: "validator::verification::verify_producer_transaction", "Validating proposal transaction {}", tx_hash);
@@ -342,19 +368,24 @@ pub async fn verify_producer_transaction(
         error!(target: "validator::verification::verify_proposal_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e);
         return Err(TxVerifyFailed::InvalidZkProof.into())
     }
-
     debug!(target: "validator::verification::verify_producer_transaction", "ZK proof verification successful");
+
+    // Append hash to merkle tree
+    append_tx_to_merkle_tree(tree, tx)?;
+
     debug!(target: "validator::verification::verify_producer_transaction", "Proposal transaction {} verified successfully", tx_hash);
 
     Ok(signature_public_key)
 }
 
 /// Verify WASM execution, signatures, and ZK proofs for a given [`Transaction`],
-/// and apply it to the provided overlay.
+/// and apply it to the provided overlay. Additionally, append its hash to the
+/// provided Merkle tree.
 pub async fn verify_transaction(
     overlay: &BlockchainOverlayPtr,
     verifying_block_height: u64,
     tx: &Transaction,
+    tree: &mut MerkleTree,
     verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
     verify_fee: bool,
 ) -> Result<u64> {
@@ -577,6 +608,9 @@ pub async fn verify_transaction(
     }
     debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful");
 
+    // Append hash to merkle tree
+    append_tx_to_merkle_tree(tree, tx)?;
+
     debug!(target: "validator::verification::verify_transaction", "Transaction {} verified successfully", tx_hash);
     Ok(gas_used)
 }
@@ -584,14 +618,18 @@ pub async fn verify_transaction(
 /// Verify a set of [`Transaction`] in sequence and apply them if all are valid.
 /// In case any of the transactions fail, they will be returned to the caller as an error.
 /// If all transactions are valid, the function will return the accumulated gas used from
-/// all the transactions.
+/// all the transactions. Additionally, their hash is appended to the provided Merkle tree.
 pub async fn verify_transactions(
     overlay: &BlockchainOverlayPtr,
     verifying_block_height: u64,
     txs: &[Transaction],
+    tree: &mut MerkleTree,
     verify_fees: bool,
 ) -> Result<u64> {
     debug!(target: "validator::verification::verify_transactions", "Verifying {} transactions", txs.len());
+    if txs.is_empty() {
+        return Ok(0)
+    }
 
     // Tracker for failed txs
     let mut erroneous_txs = vec![];
@@ -612,12 +650,13 @@ pub async fn verify_transactions(
     // Iterate over transactions and attempt to verify them
     for tx in txs {
         overlay.lock().unwrap().checkpoint();
-        match verify_transaction(overlay, verifying_block_height, tx, &mut vks, verify_fees).await {
+        match verify_transaction(overlay, verifying_block_height, tx, tree, &mut vks, verify_fees)
+            .await
+        {
             Ok(gas) => gas_used += gas,
             Err(e) => {
                 warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
                 erroneous_txs.push(tx.clone());
-                // TODO: verify this works as expected
                 overlay.lock().unwrap().revert_to_checkpoint()?;
             }
         }