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

sdk/tx: implement contract call type identification

In this commit, we have added a `ContractCall` implementation with the inclusion of new functions aimed at identifying contract call types as summarized below:

- Reward Calls: The `is_money_pow_reward` function marks the presence of Proof-of-Work reward calls.
- Deployment Call: The `is_deployment` function identifies contract deployment calls.
- Monetary Call Types: Functions like `is_money_fee`, `is_money_genesis_mint`, `is_money_transfer`, `is_money_otc_swap`, `is_money_auth_token_mint`, `is_money_auth_token_freeze`, and `is_money_token_mint` focus on distinguishing different types of monetary transaction calls.
- DAO Call Types: DAO-related operations can be identified using `is_dao_mint`, `is_dao_propose`, `is_dao_vote`, `is_dao_exec`, and `is_dao_auth_money_transfer` functions.
- Other: The `matches_contract_call_type` function is being used to detect if contract call matches provided contract id and function code.

As usage example, updated all validator checks for call types to use new predicate functions.

Please note that to circumvent the issue of circular dependencies, we did not reference to function code enums defined in `darkfi_deployooor_contract::DeployFunction`, `darkfi_dao_contract::DaoFunction`, `darkfi_money_contract::MoneyFunction`. Instead, we have duplicated their latest values within the implementation.
kalm 1 год назад
Родитель
Сommit
8d3211fbd6
2 измененных файлов с 90 добавлено и 15 удалено
  1. 79 1
      src/sdk/src/tx.rs
  2. 11 14
      src/validator/verification.rs

+ 79 - 1
src/sdk/src/tx.rs

@@ -30,6 +30,7 @@ use super::{
     hex::{decode_hex_arr, AsHex},
     hex::{decode_hex_arr, AsHex},
     ContractError, GenericResult,
     ContractError, GenericResult,
 };
 };
+use crate::crypto::{DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID};
 
 
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, SerialEncodable, SerialDecodable)]
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, SerialEncodable, SerialDecodable)]
 // We have to introduce a type rather than using an alias so we can implement Display
 // We have to introduce a type rather than using an alias so we can implement Display
@@ -80,8 +81,85 @@ pub struct ContractCall {
 }
 }
 // ANCHOR_END: contractcall
 // ANCHOR_END: contractcall
 
 
+impl ContractCall {
+    /// Returns true if call is a money fee.
+    pub fn is_money_fee(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x00)
+    }
+
+    /// Returns true if call is a money genesis mint.
+    pub fn is_money_genesis_mint(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x01)
+    }
+
+    /// Returns true if call is a money PoW reward.
+    pub fn is_money_pow_reward(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x02)
+    }
+
+    /// Returns true if call is a money transfer.
+    pub fn is_money_transfer(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x03)
+    }
+
+    /// Returns true if call is a money over-the-counter swap.
+    pub fn is_money_otc_swap(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x04)
+    }
+
+    /// Returns true if call is a money token mint authorization.
+    pub fn is_money_auth_token_mint(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x05)
+    }
+
+    /// Returns true if call is a money token freeze authorization.
+    pub fn is_money_auth_token_freeze(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x06)
+    }
+
+    /// Returns true if call is a money token mint.
+    pub fn is_money_token_mint(&self) -> bool {
+        self.matches_contract_call_type(*MONEY_CONTRACT_ID, 0x07)
+    }
+
+    /// Returns true if call is a DAO mint.
+    pub fn is_dao_mint(&self) -> bool {
+        self.matches_contract_call_type(*DAO_CONTRACT_ID, 0x00)
+    }
+
+    /// Returns true if call is a DAO proposal.
+    pub fn is_dao_propose(&self) -> bool {
+        self.matches_contract_call_type(*DAO_CONTRACT_ID, 0x01)
+    }
+
+    /// Returns true if call is a DAO vote.
+    pub fn is_dao_vote(&self) -> bool {
+        self.matches_contract_call_type(*DAO_CONTRACT_ID, 0x02)
+    }
+
+    /// Returns true if call is a DAO execution.
+    pub fn is_dao_exec(&self) -> bool {
+        self.matches_contract_call_type(*DAO_CONTRACT_ID, 0x03)
+    }
+
+    /// Returns true if call is a DAO money transfer authorization.
+    pub fn is_dao_auth_money_transfer(&self) -> bool {
+        self.matches_contract_call_type(*DAO_CONTRACT_ID, 0x04)
+    }
+
+    /// Returns true if call is a deployoor deployment.
+    pub fn is_deployment(&self) -> bool {
+        self.matches_contract_call_type(*DEPLOYOOOR_CONTRACT_ID, 0x00)
+    }
+
+    /// Returns true if contract call matches provided contract id and function code.
+    pub fn matches_contract_call_type(&self, contract_id: ContractId, func_code: u8) -> bool {
+        !self.data.is_empty() && self.contract_id == contract_id && self.data[0] == func_code
+    }
+}
+
 // Avoid showing the data in the debug output since often the calldata is very long.
 // Avoid showing the data in the debug output since often the calldata is very long.
-impl std::fmt::Debug for ContractCall {
+impl Debug for ContractCall {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "ContractCall(id={:?}", self.contract_id.inner())?;
         write!(f, "ContractCall(id={:?}", self.contract_id.inner())?;
         let calldata = &self.data;
         let calldata = &self.data;

+ 11 - 14
src/validator/verification.rs

@@ -20,10 +20,7 @@ use std::collections::HashMap;
 
 
 use darkfi_sdk::{
 use darkfi_sdk::{
     blockchain::block_version,
     blockchain::block_version,
-    crypto::{
-        schnorr::SchnorrPublic, ContractId, MerkleTree, PublicKey, DEPLOYOOOR_CONTRACT_ID,
-        MONEY_CONTRACT_ID,
-    },
+    crypto::{schnorr::SchnorrPublic, ContractId, MerkleTree, PublicKey},
     dark_tree::dark_forest_leaf_vec_integrity_check,
     dark_tree::dark_forest_leaf_vec_integrity_check,
     deploy::DeployParamsV1,
     deploy::DeployParamsV1,
     pasta::pallas,
     pasta::pallas,
@@ -271,7 +268,7 @@ pub async fn verify_checkpoint_block(
         return Err(Error::BlockContainsNoTransactions(block_hash.as_string()))
         return Err(Error::BlockContainsNoTransactions(block_hash.as_string()))
     }
     }
 
 
-    // Apply transactions, exluding producer(last) one
+    // Apply transactions, excluding producer(last) one
     let mut tree = MerkleTree::new(1);
     let mut tree = MerkleTree::new(1);
     let txs = &block.txs[..block.txs.len() - 1];
     let txs = &block.txs[..block.txs.len() - 1];
     let e = apply_transactions(overlay, block.header.height, block_target, txs, &mut tree).await;
     let e = apply_transactions(overlay, block.header.height, block_target, txs, &mut tree).await;
@@ -321,7 +318,7 @@ pub fn verify_producer_signature(block: &BlockInfo, public_key: &PublicKey) -> R
     Ok(())
     Ok(())
 }
 }
 
 
-/// Verify provided producer producer [`Transaction`].
+/// Verify provided producer [`Transaction`].
 ///
 ///
 /// Verify WASM execution, signatures, and ZK proofs and apply it to the provided,
 /// Verify WASM execution, signatures, and ZK proofs and apply it to the provided,
 /// provided overlay. Returns transaction signature public key. Additionally,
 /// provided overlay. Returns transaction signature public key. Additionally,
@@ -343,8 +340,8 @@ pub async fn verify_producer_transaction(
 
 
     // Verify call based on version
     // Verify call based on version
     let call = &tx.calls[0];
     let call = &tx.calls[0];
-    // Block must contain a Money::PoWReward(0x02) call
-    if call.data.contract_id != *MONEY_CONTRACT_ID || call.data.data[0] != 0x02 {
+    // Call must be a PoW reward
+    if !call.data.is_money_pow_reward() {
         return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
         return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
     }
     }
 
 
@@ -576,9 +573,9 @@ pub async fn verify_transaction(
 
 
     if verify_fee {
     if verify_fee {
         let mut found_fee = false;
         let mut found_fee = false;
-        // Verify that there is a Money::FeeV1 (0x00) call in the transaction
+        // Verify that there is a money fee call in the transaction
         for (call_idx, call) in tx.calls.iter().enumerate() {
         for (call_idx, call) in tx.calls.iter().enumerate() {
-            if call.data.contract_id == *MONEY_CONTRACT_ID && call.data.data[0] == 0x00 {
+            if call.data.is_money_fee() {
                 found_fee = true;
                 found_fee = true;
                 fee_call_idx = call_idx;
                 fee_call_idx = call_idx;
                 break
                 break
@@ -599,8 +596,8 @@ pub async fn verify_transaction(
 
 
     // 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 Money::PoWReward(0x02) call
-        if call.data.contract_id == *MONEY_CONTRACT_ID && call.data.data[0] == 0x02 {
+        // Transaction must not contain a Pow reward call
+        if call.data.is_money_pow_reward() {
             error!(target: "validator::verification::verify_transaction", "Reward transaction detected");
             error!(target: "validator::verification::verify_transaction", "Reward transaction detected");
             return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
             return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
         }
         }
@@ -681,7 +678,7 @@ pub async fn verify_transaction(
 
 
         // If this call is supposed to deploy a new contract, we have to instantiate
         // If this call is supposed to deploy a new contract, we have to instantiate
         // a new `Runtime` and run its deploy function.
         // a new `Runtime` and run its deploy function.
-        if call.data.contract_id == *DEPLOYOOOR_CONTRACT_ID && call.data.data[0] == 0x00
+        if call.data.is_deployment()
         /* DeployV1 */
         /* DeployV1 */
         {
         {
             debug!(target: "validator::verification::verify_transaction", "Deploying new contract");
             debug!(target: "validator::verification::verify_transaction", "Deploying new contract");
@@ -848,7 +845,7 @@ async fn apply_transaction(
 
 
         // If this call is supposed to deploy a new contract, we have to instantiate
         // If this call is supposed to deploy a new contract, we have to instantiate
         // a new `Runtime` and run its deploy function.
         // a new `Runtime` and run its deploy function.
-        if call.data.contract_id == *DEPLOYOOOR_CONTRACT_ID && call.data.data[0] == 0x00
+        if call.data.is_deployment()
         /* DeployV1 */
         /* DeployV1 */
         {
         {
             debug!(target: "validator::verification::apply_transaction", "Deploying new contract");
             debug!(target: "validator::verification::apply_transaction", "Deploying new contract");