validator: Implement basic tx fee validation

This commit is contained in:
parazyd
2023-12-19 18:01:44 +01:00
parent 6e64c6df19
commit 64ef7c9634
2 changed files with 59 additions and 5 deletions

View File

@@ -620,11 +620,17 @@ pub enum TxVerifyFailed {
#[error("Missing contract calls in transaction")]
MissingCalls,
#[error("Invalid ZK proof in transaction")]
InvalidZkProof,
#[error("Missing Money::Fee call in transaction")]
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")]
ErroneousTxs(Vec<crate::tx::Transaction>),

View File

@@ -24,7 +24,7 @@ use darkfi_sdk::{
dark_tree::dark_leaf_vec_integrity_check,
pasta::pallas,
};
use darkfi_serial::{Decodable, Encodable, WriteExt};
use darkfi_serial::{deserialize_async, Decodable, Encodable, WriteExt};
use log::{debug, error, warn};
use crate::{
@@ -336,6 +336,7 @@ pub async fn verify_transaction(
time_keeper: &TimeKeeper,
tx: &Transaction,
verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
verify_fee: bool, // FIXME: Remove this bool
) -> Result<u64> {
let tx_hash = 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
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
for (idx, call) in tx.calls.iter().enumerate() {
// 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())
}
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", "Transaction {} verified successfully", tx_hash);
@@ -484,7 +532,7 @@ 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, time_keeper, tx, &mut vks).await {
match verify_transaction(overlay, time_keeper, tx, &mut vks, verify_fees).await {
Ok(gas) => _gas_used += gas,
Err(e) => {
warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
@@ -495,7 +543,7 @@ pub async fn verify_transactions(
}
if verify_fees {
// Enforce that enough fee is paid
// Enforce that enough fee is paid.
todo!()
}
}