Ver código fonte

validator: Implement basic tx fee validation

parazyd 2 anos atrás
pai
commit
64ef7c9634
2 arquivos alterados com 59 adições e 5 exclusões
  1. 8 2
      src/error.rs
  2. 51 3
      src/validator/verification.rs

+ 8 - 2
src/error.rs

@@ -620,11 +620,17 @@ pub enum TxVerifyFailed {
     #[error("Missing contract calls in transaction")]
     #[error("Missing contract calls in transaction")]
     MissingCalls,
     MissingCalls,
 
 
+    #[error("Invalid ZK proof in transaction")]
+    InvalidZkProof,
+
     #[error("Missing Money::Fee call in transaction")]
     #[error("Missing Money::Fee call in transaction")]
     MissingFee,
     MissingFee,
 
 
-    #[error("Invalid ZK proof in transaction")]
-    InvalidZkProof,
+    #[error("Invalid Money::Fee call in transaction")]
+    InvalidFee,
+
+    #[error("Insufficient fee paid")]
+    InsufficientFee,
 
 
     #[error("Erroneous transactions found")]
     #[error("Erroneous transactions found")]
     ErroneousTxs(Vec<crate::tx::Transaction>),
     ErroneousTxs(Vec<crate::tx::Transaction>),

+ 51 - 3
src/validator/verification.rs

@@ -24,7 +24,7 @@ use darkfi_sdk::{
     dark_tree::dark_leaf_vec_integrity_check,
     dark_tree::dark_leaf_vec_integrity_check,
     pasta::pallas,
     pasta::pallas,
 };
 };
-use darkfi_serial::{Decodable, Encodable, WriteExt};
+use darkfi_serial::{deserialize_async, Decodable, Encodable, WriteExt};
 use log::{debug, error, warn};
 use log::{debug, error, warn};
 
 
 use crate::{
 use crate::{
@@ -336,6 +336,7 @@ pub async fn verify_transaction(
     time_keeper: &TimeKeeper,
     time_keeper: &TimeKeeper,
     tx: &Transaction,
     tx: &Transaction,
     verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
     verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
+    verify_fee: bool, // FIXME: Remove this bool
 ) -> Result<u64> {
 ) -> Result<u64> {
     let tx_hash = tx.hash()?;
     let tx_hash = tx.hash()?;
     debug!(target: "validator::verification::verify_transaction", "Validating transaction {}", tx_hash);
     debug!(target: "validator::verification::verify_transaction", "Validating transaction {}", tx_hash);
@@ -351,6 +352,25 @@ pub async fn verify_transaction(
     // Table of public keys used for signature verification
     // Table of public keys used for signature verification
     let mut sig_table = vec![];
     let mut sig_table = vec![];
 
 
+    // Verify that the first call is the transaction fee and that it has no parents or children.
+    if verify_fee {
+        if tx.calls[0].data.contract_id != *MONEY_CONTRACT_ID || tx.calls[0].data.data[0] != 0x00 {
+            error!(
+                target: "validator::verification::verify_transaction",
+                "[VALIDATOR] First tx call is not Money::Fee",
+            );
+            return Err(TxVerifyFailed::MissingFee.into())
+        }
+
+        if tx.calls[0].parent_index.is_some() || !tx.calls[0].children_indexes.is_empty() {
+            error!(
+                target: "validator::verification::verify_transaction",
+                "[VALIDATOR] Fee call invalid (has parent/children)",
+            );
+            return Err(TxVerifyFailed::InvalidFee.into())
+        }
+    }
+
     // Iterate over all calls to get the metadata
     // Iterate over all calls to get the metadata
     for (idx, call) in tx.calls.iter().enumerate() {
     for (idx, call) in tx.calls.iter().enumerate() {
         // Transaction must not contain a reward call, Money::PoWReward(0x08) or Consensus::Proposal(0x02)
         // Transaction must not contain a reward call, Money::PoWReward(0x08) or Consensus::Proposal(0x02)
@@ -447,6 +467,34 @@ pub async fn verify_transaction(
         return Err(TxVerifyFailed::InvalidZkProof.into())
         return Err(TxVerifyFailed::InvalidZkProof.into())
     }
     }
 
 
+    if verify_fee {
+        // TODO: This counts 1 gas as 1 token unit. Pricing should be better specified.
+        // TODO: Currently this doesn't account for signatures or ZK proofs
+        // TODO: Currently this doesn't account for WASM host functions
+
+        // Deserialize the first call to find the paid fee
+        let fee: u64 = match deserialize_async(&tx.calls[0].data.data[1..9]).await {
+            Ok(v) => v,
+            Err(e) => {
+                error!(
+                    target: "validator::verification::verify_transaction",
+                    "[VALIDATOR] Failed deserializing tx {} fee call: {}", tx_hash, e,
+                );
+                return Err(TxVerifyFailed::InvalidFee.into())
+            }
+        };
+
+        // Check that enough fee has been paid for the used gas in this transaction.
+        if gas_used > fee {
+            error!(
+                target: "validator::verification::verify_transaction",
+                "[VALIDATOR] tx {} has insufficient fee. Required: {}, Paid: {}",
+                tx_hash, gas_used, fee,
+            );
+            return Err(TxVerifyFailed::InsufficientFee.into())
+        }
+    }
+
     debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful");
     debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful");
     debug!(target: "validator::verification::verify_transaction", "Transaction {} verified successfully", tx_hash);
     debug!(target: "validator::verification::verify_transaction", "Transaction {} verified successfully", tx_hash);
 
 
@@ -484,7 +532,7 @@ pub async fn verify_transactions(
     // Iterate over transactions and attempt to verify them
     // Iterate over transactions and attempt to verify them
     for tx in txs {
     for tx in txs {
         overlay.lock().unwrap().checkpoint();
         overlay.lock().unwrap().checkpoint();
-        match verify_transaction(overlay, time_keeper, tx, &mut vks).await {
+        match verify_transaction(overlay, time_keeper, tx, &mut vks, verify_fees).await {
             Ok(gas) => _gas_used += gas,
             Ok(gas) => _gas_used += gas,
             Err(e) => {
             Err(e) => {
                 warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
                 warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
@@ -495,7 +543,7 @@ pub async fn verify_transactions(
         }
         }
 
 
         if verify_fees {
         if verify_fees {
-            // Enforce that enough fee is paid
+            // Enforce that enough fee is paid.
             todo!()
             todo!()
         }
         }
     }
     }