فهرست منبع

darkfid: reduce contracts execution fees

skoupidi 1 سال پیش
والد
کامیت
dba73bbe28

+ 1 - 1
bin/darkfid/src/rpc.rs

@@ -105,7 +105,7 @@ impl RequestHandler<DefaultRpcHandler> for DarkfiNode {
             "tx.broadcast" => self.tx_broadcast(req.id, req.params).await,
             "tx.pending" => self.tx_pending(req.id, req.params).await,
             "tx.clean_pending" => self.tx_pending(req.id, req.params).await,
-            "tx.calculate_gas" => self.tx_calculate_gas(req.id, req.params).await,
+            "tx.calculate_fee" => self.tx_calculate_fee(req.id, req.params).await,
 
             // ==============
             // Invalid method

+ 8 - 8
bin/darkfid/src/rpc_tx.rs

@@ -214,16 +214,16 @@ impl DarkfiNode {
     // Returns the gas value if the transaction is valid, otherwise, a corresponding
     // error.
     //
-    // --> {"jsonrpc": "2.0", "method": "tx.calculate_gas", "params": ["base64encodedTX", "include_fee"], "id": 1}
+    // --> {"jsonrpc": "2.0", "method": "tx.calculate_fee", "params": ["base64encodedTX", "include_fee"], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
-    pub async fn tx_calculate_gas(&self, id: u16, params: JsonValue) -> JsonResult {
+    pub async fn tx_calculate_fee(&self, id: u16, params: JsonValue) -> JsonResult {
         let params = params.get::<Vec<JsonValue>>().unwrap();
         if params.len() != 2 || !params[0].is_string() || !params[1].is_bool() {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
         if !*self.validator.synced.read().await {
-            error!(target: "darkfid::rpc::tx_calculate_gas", "Blockchain is not synced");
+            error!(target: "darkfid::rpc::tx_calculate_fee", "Blockchain is not synced");
             return server_error(RpcError::NotSynced, id, None)
         }
 
@@ -232,7 +232,7 @@ impl DarkfiNode {
         let tx_bytes = match base64::decode(tx_enc) {
             Some(v) => v,
             None => {
-                error!(target: "darkfid::rpc::tx_calculate_gas", "Failed decoding base64 transaction");
+                error!(target: "darkfid::rpc::tx_calculate_fee", "Failed decoding base64 transaction");
                 return server_error(RpcError::ParseError, id, None)
             }
         };
@@ -240,7 +240,7 @@ impl DarkfiNode {
         let tx: Transaction = match deserialize_async(&tx_bytes).await {
             Ok(v) => v,
             Err(e) => {
-                error!(target: "darkfid::rpc::tx_calculate_gas", "Failed deserializing bytes into Transaction: {}", e);
+                error!(target: "darkfid::rpc::tx_calculate_fee", "Failed deserializing bytes into Transaction: {}", e);
                 return server_error(RpcError::ParseError, id, None)
             }
         };
@@ -249,15 +249,15 @@ impl DarkfiNode {
         let include_fee = params[1].get::<bool>().unwrap();
 
         // Simulate state transition
-        let result = self.validator.calculate_gas(&tx, *include_fee).await;
+        let result = self.validator.calculate_fee(&tx, *include_fee).await;
         if result.is_err() {
             error!(
-                target: "darkfid::rpc::tx_calculate_gas", "Failed to validate state transition: {}",
+                target: "darkfid::rpc::tx_calculate_fee", "Failed to validate state transition: {}",
                 result.err().unwrap()
             );
             return server_error(RpcError::TxGasCalculationFail, id, None)
         };
 
-        JsonResponse::new(JsonValue::Number(result.unwrap().total_gas_used() as f64), id).into()
+        JsonResponse::new(JsonValue::Number(result.unwrap() as f64), id).into()
     }
 }

+ 1 - 1
bin/drk/Cargo.toml

@@ -10,7 +10,7 @@ edition = "2021"
 
 [dependencies]
 # Darkfi
-darkfi = {path = "../../", features = ["async-daemonize", "bs58", "rpc"]}
+darkfi = {path = "../../", features = ["async-daemonize", "bs58", "rpc", "validator"]}
 darkfi_money_contract = {path = "../../src/contract/money", features = ["no-entrypoint", "client"]}
 darkfi_dao_contract = {path = "../../src/contract/dao", features = ["no-entrypoint", "client"]}
 darkfi_deployooor_contract = {path = "../../src/contract/deployooor", features = ["no-entrypoint", "client"]}

+ 6 - 5
bin/drk/src/money.rs

@@ -25,6 +25,7 @@ use rusqlite::types::Value;
 
 use darkfi::{
     tx::Transaction,
+    validator::fees::compute_fee,
     zk::{halo2::Field, proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses, Proof},
     zkas::ZkBinary,
     Error, Result,
@@ -1182,14 +1183,14 @@ impl Drk {
         fee_zkbin: &ZkBinary,
         spent_coins: Option<&[OwnCoin]>,
     ) -> Result<(ContractCall, Vec<Proof>, Vec<SecretKey>)> {
-        // First we verify the fee-less transaction to see how much gas it uses for execution
+        // First we verify the fee-less transaction to see how much fee it requires for execution
         // and verification.
-        let gas_used = FEE_CALL_GAS + self.get_tx_gas(tx, false).await?;
+        let required_fee = compute_fee(&FEE_CALL_GAS) + self.get_tx_fee(tx, false).await?;
 
         // Knowing the total gas, we can now find an OwnCoin of enough value
         // so that we can create a valid Money::Fee call.
         let mut available_coins = self.get_token_coins(&DARK_TOKEN_ID).await?;
-        available_coins.retain(|x| x.note.value > gas_used);
+        available_coins.retain(|x| x.note.value > required_fee);
         if let Some(spent_coins) = spent_coins {
             available_coins.retain(|x| !spent_coins.contains(x));
         }
@@ -1198,7 +1199,7 @@ impl Drk {
         }
 
         let coin = &available_coins[0];
-        let change_value = coin.note.value - gas_used;
+        let change_value = coin.note.value - required_fee;
 
         // Input and output setup
         let input = FeeCallInput {
@@ -1275,7 +1276,7 @@ impl Drk {
 
         // Encode the contract call
         let mut data = vec![MoneyFunction::FeeV1 as u8];
-        gas_used.encode_async(&mut data).await?;
+        required_fee.encode_async(&mut data).await?;
         params.encode_async(&mut data).await?;
         let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
 

+ 5 - 5
bin/drk/src/rpc.rs

@@ -475,17 +475,17 @@ impl Drk {
         Ok(ret)
     }
 
-    /// Queries darkfid for given transaction's gas.
-    pub async fn get_tx_gas(&self, tx: &Transaction, include_fee: bool) -> Result<u64> {
+    /// Queries darkfid for given transaction's required fee.
+    pub async fn get_tx_fee(&self, tx: &Transaction, include_fee: bool) -> Result<u64> {
         let params = JsonValue::Array(vec![
             JsonValue::String(base64::encode(&serialize_async(tx).await)),
             JsonValue::Boolean(include_fee),
         ]);
-        let rep = self.darkfid_daemon_request("tx.calculate_gas", &params).await?;
+        let rep = self.darkfid_daemon_request("tx.calculate_fee", &params).await?;
 
-        let gas = *rep.get::<f64>().unwrap() as u64;
+        let fee = *rep.get::<f64>().unwrap() as u64;
 
-        Ok(gas)
+        Ok(fee)
     }
 
     /// Queries darkfid for current best fork next height.

+ 26 - 0
contrib/localnet/darkfid-single-node/README.md

@@ -78,3 +78,29 @@ of the guide can be added for future regressions.
 | 38 | Transaction simulate         | explorer simulate-tx                                            | Pass   |
 | 39 | Transaction broadcast        | broadcast                                                       | Pass   |
 | 40 | Transaction attach fee       | attach-fee                                                      | Pass   |
+
+## Transactions fees
+
+This table contains each executed transaction fee in `DRK`.
+
+| Type        | Description                                             | Fee        |
+|-------------|---------------------------------------------------------|------------|
+| Transfer    | Native token transfer with single input and output      | 0.00525303 |
+| Transfer    | Native token transfer with single input and two outputs | 0.00557027 |
+| Transfer    | Native token transfer with two inputs and single output | 0.00570562 |
+| Transfer    | Native token transfer with two inputs and outputs       | 0.00602301 |
+| Token mint  | Custom token mint                                       | 0.00518391 |
+| Transfer    | Custom token transfer with single input and two outputs | 0.00557027 |
+| OTC swap    | Atomic swap between two custom tokens                   | 0.00601657 |
+| DAO mint    | Mint a generated DAO onchain                            | 0.00474321 |
+| Transfer    | Send tokens to a DAO treasury                           | 0.00602301 |
+| DAO propose | Mint a generated DAO transfer proposal onchain          | 0.00574667 |
+| DAO vote    | Vote for a minted DAO transfer proposal                 | 0.00601218 |
+| DAO exec    | Execute (early) a passed DAO transfer proposal          | 0.00988316 |
+| DAO propose | Mint a generated DAO generic proposal onchain           | 0.00574445 |
+| DAO vote    | Vote for a minted DAO generic proposal                  | 0.00601218 |
+| DAO exec    | Execute (early) a passed DAO generic proposal           | 0.00530605 |
+| Token mint  | Custom token mint for a DAO treasury                    | 0.00518391 |
+| DAO propose | Mint a generated DAO to DAO transfer proposal onchain   | 0.00574667 |
+| DAO vote    | Vote for a minted DAO to DAO transfer proposal          | 0.00601218 |
+| DAO exec    | Execute (early) a passed DAO to DAO transfer proposal   | 0.00988316 |

+ 2 - 2
doc/src/testnet/dao.md

@@ -226,7 +226,7 @@ instead of transfering some to the DAO, we will mint them
 directly into it:
 
 ```
-$ ./drk token mint MLDY 20 {DAO_PUBLIC_KEY} \
+$ ./drk token mint MLDY 20 {DAO_NOTES_PUBLIC_KEY} \
     {DAO_CONTRACT_SPEND_HOOK} {DAO_BULLA} > mint_dao_mldy_tx
 $ ./drk broadcast < mint_dao_mldy_tx
 ```
@@ -252,7 +252,7 @@ from the DAO treasury to the new DAO we created:
 
 ```
 $ ./drk dao list WickedDAO
-$ ./drk dao propose-transfer MiladyMakerDAO 1 6.9 MLDY {WICKED_DAO_PUBLIC_KEY} \
+$ ./drk dao propose-transfer MiladyMakerDAO 1 6.9 MLDY {WICKED_DAO_NOTES_PUBLIC_KEY} \
     {DAO_CONTRACT_SPEND_HOOK} {WICKED_DAO_BULLA}
 $ ./drk dao proposal {PROPOSAL_BULLA} --mint-proposal > dao_mldy_transfer_proposal_wckd_mint_tx
 $ ./drk broadcast < dao_mldy_transfer_proposal_wckd_mint_tx

+ 1 - 1
src/contract/money/src/client/fee_v1.rs

@@ -39,7 +39,7 @@ use crate::{
 
 /// Fixed gas used by the fee call.
 /// This is the minimum gas any fee-paying transaction will use.
-pub const FEE_CALL_GAS: u64 = 41_000_000;
+pub const FEE_CALL_GAS: u64 = 42_000_000;
 
 /// Private values related to the Fee call
 pub struct FeeCallSecrets {

+ 7 - 4
src/contract/money/tests/delayed_tx.rs

@@ -18,6 +18,7 @@
 
 use darkfi::{
     tx::{ContractCallLeaf, TransactionBuilder},
+    validator::fees::compute_fee,
     zk::halo2::Field,
     Result,
 };
@@ -135,8 +136,7 @@ fn delayed_tx() -> Result<()> {
 
         // First we verify the fee-less transaction to see how much gas it uses for execution
         // and verification.
-        let mut gas_used = FEE_CALL_GAS;
-        gas_used += wallet
+        let gas_used = wallet
             .validator
             .add_test_transactions(
                 &[tx],
@@ -148,8 +148,11 @@ fn delayed_tx() -> Result<()> {
             .await?
             .0;
 
+        // Compute the required fee
+        let required_fee = compute_fee(&(gas_used + FEE_CALL_GAS));
+
         let coin = &output_coins[0];
-        let change_value = coin.note.value - gas_used;
+        let change_value = coin.note.value - required_fee;
 
         // Input and output setup
         let input = FeeCallInput {
@@ -227,7 +230,7 @@ fn delayed_tx() -> Result<()> {
 
         // Encode the contract call
         let mut data = vec![MoneyFunction::FeeV1 as u8];
-        gas_used.encode_async(&mut data).await?;
+        required_fee.encode_async(&mut data).await?;
         fee_call_params.encode_async(&mut data).await?;
         let fee_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
 

+ 15 - 8
src/contract/test-harness/src/money_fee.rs

@@ -20,6 +20,7 @@ use std::{collections::HashSet, hash::RandomState};
 
 use darkfi::{
     tx::{ContractCallLeaf, Transaction, TransactionBuilder},
+    validator::fees::compute_fee,
     zk::{halo2::Field, Proof},
     Result,
 };
@@ -56,11 +57,14 @@ impl TestHarness {
     ) -> Result<(Transaction, MoneyFeeParamsV1)> {
         let wallet = self.holders.get(holder).unwrap();
 
+        // Compute fee call required fee
+        let required_fee = compute_fee(&FEE_CALL_GAS);
+
         // Find a compatible OwnCoin
         let coin = wallet
             .unspent_money_coins
             .iter()
-            .find(|x| x.note.token_id == *DARK_TOKEN_ID && x.note.value > FEE_CALL_GAS)
+            .find(|x| x.note.token_id == *DARK_TOKEN_ID && x.note.value > required_fee)
             .unwrap();
 
         // Input and output setup
@@ -72,7 +76,7 @@ impl TestHarness {
 
         let output = FeeCallOutput {
             public_key: wallet.keypair.public,
-            value: coin.note.value - FEE_CALL_GAS,
+            value: coin.note.value - required_fee,
             token_id: coin.note.token_id,
             blind: Blind::random(&mut OsRng),
             spend_hook: FuncId::none(),
@@ -139,7 +143,7 @@ impl TestHarness {
         };
 
         let mut data = vec![MoneyFunction::FeeV1 as u8];
-        FEE_CALL_GAS.encode_async(&mut data).await?;
+        required_fee.encode_async(&mut data).await?;
         params.encode_async(&mut data).await?;
         let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
         let mut tx_builder =
@@ -217,8 +221,7 @@ impl TestHarness {
         // First we verify the fee-less transaction to see how much gas it uses for execution
         // and verification.
         let wallet = self.holders.get(holder).unwrap();
-        let mut gas_used = FEE_CALL_GAS;
-        gas_used += wallet
+        let gas_used = wallet
             .validator
             .add_test_transactions(
                 &[tx],
@@ -230,16 +233,20 @@ impl TestHarness {
             .await?
             .0;
 
+        // Compute the required fee
+        let required_fee = compute_fee(&(gas_used + FEE_CALL_GAS));
+
         // Knowing the total gas, we can now find an OwnCoin of enough value
         // so that we can create a valid Money::Fee call.
         let spent_coins: HashSet<&OwnCoin, RandomState> = HashSet::from_iter(spent_coins);
         let mut available_coins = wallet.unspent_money_coins.clone();
-        available_coins.retain(|x| x.note.token_id == *DARK_TOKEN_ID && x.note.value > gas_used);
+        available_coins
+            .retain(|x| x.note.token_id == *DARK_TOKEN_ID && x.note.value > required_fee);
         available_coins.retain(|x| !spent_coins.contains(x));
         assert!(!available_coins.is_empty());
 
         let coin = &available_coins[0];
-        let change_value = coin.note.value - gas_used;
+        let change_value = coin.note.value - required_fee;
 
         // Input and output setup
         let input = FeeCallInput {
@@ -318,7 +325,7 @@ impl TestHarness {
 
         // Encode the contract call
         let mut data = vec![MoneyFunction::FeeV1 as u8];
-        gas_used.encode_async(&mut data).await?;
+        required_fee.encode_async(&mut data).await?;
         params.encode_async(&mut data).await?;
         let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
 

+ 8 - 0
src/validator/fees.rs

@@ -132,3 +132,11 @@ impl std::fmt::Debug for GasData {
             .finish()
     }
 }
+
+/// Auxiliary function to compute the corresponding fee value
+/// for the provided gas.
+///
+/// Currently we simply divide the gas value by 100.
+pub fn compute_fee(gas: &u64) -> u64 {
+    gas / 100
+}

+ 4 - 4
src/validator/mod.rs

@@ -31,7 +31,6 @@ use crate::{
     },
     error::TxVerifyFailed,
     tx::Transaction,
-    validator::fees::GasData,
     zk::VerifyingKey,
     Error, Result,
 };
@@ -53,6 +52,7 @@ use verification::{
 
 /// Fee calculation helpers
 pub mod fees;
+use fees::compute_fee;
 
 /// Helper utilities
 pub mod utils;
@@ -130,11 +130,11 @@ impl Validator {
         Ok(state)
     }
 
-    /// Auxiliary function to compute provided transaction's total gas,
+    /// Auxiliary function to compute provided transaction's required fee,
     /// against current best fork.
     /// The function takes a boolean called `verify_fee` to overwrite
     /// the nodes configured `verify_fees` flag.
-    pub async fn calculate_gas(&self, tx: &Transaction, verify_fee: bool) -> Result<GasData> {
+    pub async fn calculate_fee(&self, tx: &Transaction, verify_fee: bool) -> Result<u64> {
         // Grab the best fork to verify against
         let forks = self.consensus.forks.read().await;
         let fork = forks[best_fork_index(&forks)?].full_clone()?;
@@ -164,7 +164,7 @@ impl Validator {
         // Purge new trees
         fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
 
-        Ok(verify_result)
+        Ok(compute_fee(&verify_result.total_gas_used()))
     }
 
     /// The node retrieves a transaction, validates its state transition,

+ 7 - 5
src/validator/verification.rs

@@ -40,7 +40,7 @@ use crate::{
     tx::{Transaction, MAX_TX_CALLS, MIN_TX_CALLS},
     validator::{
         consensus::{Consensus, Fork, Proposal, GAS_LIMIT_UNPROPOSED_TXS},
-        fees::{circuit_gas_use, GasData, PALLAS_SCHNORR_SIGNATURE_FEE},
+        fees::{circuit_gas_use, compute_fee, GasData, PALLAS_SCHNORR_SIGNATURE_FEE},
         pow::PoWModule,
     },
     zk::VerifyingKey,
@@ -738,13 +738,15 @@ pub async fn verify_transaction(
             }
         };
 
-        // TODO: This counts 1 gas as 1 token unit. Pricing should be better specified.
-        // Check that enough fee has been paid for the used gas in this transaction.
-        if total_gas_used > fee {
+        // Compute the required fee for this transaction
+        let required_fee = compute_fee(&total_gas_used);
+
+        // Check that enough fee has been paid for the used gas in this transaction
+        if required_fee > fee {
             error!(
                 target: "validator::verification::verify_transaction",
                 "[VALIDATOR] Transaction {} has insufficient fee. Required: {}, Paid: {}",
-                tx_hash, total_gas_used, fee,
+                tx_hash, required_fee, fee,
             );
             return Err(TxVerifyFailed::InsufficientFee.into())
         }