diff --git a/src/validator/fees.rs b/src/validator/fees.rs new file mode 100644 index 000000000..55879671a --- /dev/null +++ b/src/validator/fees.rs @@ -0,0 +1,20 @@ +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2024 Dyne.org foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/// Fixed fee for verifying Schnorr signatures using the Pallas elliptic curve +pub const PALLAS_SCHNORR_SIGNATURE_FEE: u64 = 1000; diff --git a/src/validator/mod.rs b/src/validator/mod.rs index b46d5942a..16930d537 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -56,6 +56,9 @@ use verification::{ verify_transactions, }; +/// Fee calculation helpers +pub mod fees; + /// Validation functions pub mod validation; diff --git a/src/validator/verification.rs b/src/validator/verification.rs index 7b15e32cc..6e0527bfe 100644 --- a/src/validator/verification.rs +++ b/src/validator/verification.rs @@ -28,7 +28,9 @@ use darkfi_sdk::{ deploy::DeployParamsV1, pasta::pallas, }; -use darkfi_serial::{deserialize_async, AsyncDecodable, AsyncEncodable, AsyncWriteExt, WriteExt}; +use darkfi_serial::{ + deserialize_async, serialize_async, AsyncDecodable, AsyncEncodable, AsyncWriteExt, WriteExt, +}; use log::{debug, error, warn}; use smol::io::Cursor; @@ -40,6 +42,7 @@ use crate::{ util::time::TimeKeeper, validator::{ consensus::{Consensus, Fork, Proposal, TXS_CAP}, + fees::PALLAS_SCHNORR_SIGNATURE_FEE, pow::PoWModule, validation::validate_block, }, @@ -493,34 +496,13 @@ pub async fn verify_transaction( gas_used += runtime.gas_used(); } - // When we're done looping and executing over the tx's contract calls, we now - // move on with verification. First we verify the signatures as that's cheaper, - // and then finally we verify the ZK proofs. - debug!(target: "validator::verification::verify_transaction", "Verifying signatures for transaction {}", tx_hash); - if sig_table.len() != tx.signatures.len() { - error!(target: "validator::verification::verify_transaction", "Incorrect number of signatures in tx {}", tx_hash); - return Err(TxVerifyFailed::MissingSignatures.into()) - } - - // TODO: Go through the ZK circuits that have to be verified and account for the opcodes. - - if let Err(e) = tx.verify_sigs(sig_table) { - error!(target: "validator::verification::verify_transaction", "Signature verification for tx {} failed: {}", tx_hash, e); - return Err(TxVerifyFailed::InvalidSignature.into()) - } - - debug!(target: "validator::verification::verify_transaction", "Signature verification successful"); - - debug!(target: "validator::verification::verify_transaction", "Verifying ZK proofs for transaction {}", tx_hash); - if let Err(e) = tx.verify_zkps(verifying_keys, zkp_table).await { - error!(target: "validator::verification::verify_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e); - return Err(TxVerifyFailed::InvalidZkProof.into()) - } - if verify_fee { + // The signature fee is tx_size + fixed_sig_fee * n_signatures + gas_used += (PALLAS_SCHNORR_SIGNATURE_FEE * tx.signatures.len() as u64) + + serialize_async(tx).await.len() as u64; + // 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 + // TODO: Currently this doesn't account for ZK proofs // Deserialize the fee call to find the paid fee let fee: u64 = match deserialize_async(&tx.calls[fee_call_idx].data.data[1..9]).await { @@ -545,6 +527,29 @@ pub async fn verify_transaction( } } + // When we're done looping and executing over the tx's contract calls and + // (optionally) made sure that enough fee was paid, we now move on with + // verification. First we verify the transaction signatures and then we + // verify any accompanying ZK proofs. + debug!(target: "validator::verification::verify_transaction", "Verifying signatures for transaction {}", tx_hash); + if sig_table.len() != tx.signatures.len() { + error!(target: "validator::verification::verify_transaction", "Incorrect number of signatures in tx {}", tx_hash); + return Err(TxVerifyFailed::MissingSignatures.into()) + } + + if let Err(e) = tx.verify_sigs(sig_table) { + error!(target: "validator::verification::verify_transaction", "Signature verification for tx {} failed: {}", tx_hash, e); + return Err(TxVerifyFailed::InvalidSignature.into()) + } + + debug!(target: "validator::verification::verify_transaction", "Signature verification successful"); + + debug!(target: "validator::verification::verify_transaction", "Verifying ZK proofs for transaction {}", tx_hash); + if let Err(e) = tx.verify_zkps(verifying_keys, zkp_table).await { + error!(target: "validator::verification::verify_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e); + return Err(TxVerifyFailed::InvalidZkProof.into()) + } + debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful"); debug!(target: "validator::verification::verify_transaction", "Transaction {} verified successfully", tx_hash);