mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 07:08:05 -05:00
validator: producer tx on last instead of first position in block txs vector and proper header Merkle tree validation
This commit is contained in:
@@ -76,8 +76,11 @@ impl Harness {
|
||||
// Generate default genesis block
|
||||
let mut genesis_block = BlockInfo::default();
|
||||
|
||||
// Retrieve genesis producer transaction
|
||||
let producer_tx = genesis_block.txs.pop().unwrap();
|
||||
|
||||
// Append genesis transactions
|
||||
genesis_block.txs.push(genesis_mint_tx);
|
||||
genesis_block.append_txs(vec![genesis_mint_tx, producer_tx])?;
|
||||
|
||||
// Generate validators configuration
|
||||
// NOTE: we are not using consensus constants here so we
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use darkfi_sdk::{
|
||||
crypto::{
|
||||
schnorr::{SchnorrSecret, Signature},
|
||||
SecretKey,
|
||||
MerkleTree, SecretKey,
|
||||
},
|
||||
pasta::{group::ff::FromUniformBytes, pallas},
|
||||
};
|
||||
@@ -119,11 +119,7 @@ impl BlockInfo {
|
||||
|
||||
/// Append a transaction to the block. Also adds it to the Merkle tree.
|
||||
pub fn append_tx(&mut self, tx: Transaction) -> Result<()> {
|
||||
let mut buf = [0u8; 64];
|
||||
buf[..blake3::OUT_LEN].copy_from_slice(tx.hash()?.as_bytes());
|
||||
let leaf = pallas::Base::from_uniform_bytes(&buf);
|
||||
|
||||
self.header.tree.append(leaf.into());
|
||||
append_tx_to_merkle_tree(&mut self.header.tree, &tx)?;
|
||||
self.txs.push(tx);
|
||||
|
||||
Ok(())
|
||||
@@ -662,3 +658,12 @@ impl BlockDifficultyStoreOverlay {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary function to append a transaction to a Merkle tree.
|
||||
pub fn append_tx_to_merkle_tree(tree: &mut MerkleTree, tx: &Transaction) -> Result<()> {
|
||||
let mut buf = [0u8; 64];
|
||||
buf[..blake3::OUT_LEN].copy_from_slice(tx.hash()?.as_bytes());
|
||||
let leaf = pallas::Base::from_uniform_bytes(&buf);
|
||||
tree.append(leaf.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -78,8 +78,15 @@ pub async fn append_fee_call(
|
||||
// First we will verify the fee-less transaction to see how much gas
|
||||
// it uses for execution and verification.
|
||||
let tx = tx_builder.build()?;
|
||||
let gas_used =
|
||||
verify_transaction(overlay, verifying_block_height, &tx, verifying_keys, false).await?;
|
||||
let gas_used = verify_transaction(
|
||||
overlay,
|
||||
verifying_block_height,
|
||||
&tx,
|
||||
&mut MerkleTree::new(1),
|
||||
verifying_keys,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// TODO: We could actually take a set of coins and then find one with
|
||||
// enough value, instead of expecting one. It depends, the API
|
||||
|
||||
@@ -194,6 +194,8 @@ impl TestHarness {
|
||||
let mut holders = HashMap::new();
|
||||
let mut genesis_block = BlockInfo::default();
|
||||
genesis_block.header.timestamp = Timestamp(1689772567);
|
||||
let producer_tx = genesis_block.txs.pop().unwrap();
|
||||
genesis_block.append_txs(vec![producer_tx])?;
|
||||
|
||||
// Deterministic PRNG
|
||||
let mut rng = Pcg32::new(42);
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use darkfi_sdk::{crypto::SecretKey, pasta::pallas};
|
||||
use darkfi_sdk::{
|
||||
crypto::{MerkleTree, SecretKey},
|
||||
pasta::pallas,
|
||||
};
|
||||
use darkfi_serial::{async_trait, serialize, SerialDecodable, SerialEncodable};
|
||||
use log::{debug, error, info};
|
||||
use num_bigint::BigUint;
|
||||
@@ -387,8 +390,14 @@ impl Fork {
|
||||
let overlay = self.overlay.lock().unwrap().full_clone()?;
|
||||
|
||||
// Verify transactions
|
||||
if let Err(e) =
|
||||
verify_transactions(&overlay, verifying_block_height, &unproposed_txs, false).await
|
||||
if let Err(e) = verify_transactions(
|
||||
&overlay,
|
||||
verifying_block_height,
|
||||
&unproposed_txs,
|
||||
&mut MerkleTree::new(1),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
match e {
|
||||
crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(erroneous_txs)) => {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use darkfi_sdk::crypto::PublicKey;
|
||||
use darkfi_sdk::crypto::{MerkleTree, PublicKey};
|
||||
use darkfi_serial::serialize_async;
|
||||
use log::{debug, error, info, warn};
|
||||
use num_bigint::BigUint;
|
||||
@@ -182,7 +182,15 @@ impl Validator {
|
||||
let next_block_height = fork.get_next_block_height()?;
|
||||
|
||||
// Verify transaction
|
||||
match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
|
||||
match verify_transactions(
|
||||
&overlay,
|
||||
next_block_height,
|
||||
&tx_vec,
|
||||
&mut MerkleTree::new(1),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => continue,
|
||||
Err(e) => return Err(e),
|
||||
@@ -200,7 +208,15 @@ impl Validator {
|
||||
let overlay = BlockchainOverlay::new(&self.blockchain)?;
|
||||
let next_block_height = self.blockchain.last_block()?.header.height + 1;
|
||||
let mut erroneous_txs = vec![];
|
||||
match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
|
||||
match verify_transactions(
|
||||
&overlay,
|
||||
next_block_height,
|
||||
&tx_vec,
|
||||
&mut MerkleTree::new(1),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => valid = true,
|
||||
Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(etx))) => {
|
||||
erroneous_txs = etx
|
||||
@@ -255,7 +271,15 @@ impl Validator {
|
||||
let next_block_height = fork.get_next_block_height()?;
|
||||
|
||||
// Verify transaction
|
||||
match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
|
||||
match verify_transactions(
|
||||
&overlay,
|
||||
next_block_height,
|
||||
&tx_vec,
|
||||
&mut MerkleTree::new(1),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
valid = true;
|
||||
continue
|
||||
@@ -271,7 +295,15 @@ impl Validator {
|
||||
// Verify transaction against canonical state
|
||||
let overlay = BlockchainOverlay::new(&self.blockchain)?;
|
||||
let next_block_height = self.blockchain.last_block()?.header.height + 1;
|
||||
match verify_transactions(&overlay, next_block_height, &tx_vec, false).await {
|
||||
match verify_transactions(
|
||||
&overlay,
|
||||
next_block_height,
|
||||
&tx_vec,
|
||||
&mut MerkleTree::new(1),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => valid = true,
|
||||
Err(crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => {}
|
||||
Err(e) => return Err(e),
|
||||
@@ -425,7 +457,14 @@ impl Validator {
|
||||
let overlay = BlockchainOverlay::new(&self.blockchain)?;
|
||||
|
||||
// Verify all transactions and get erroneous ones
|
||||
let e = verify_transactions(&overlay, verifying_block_height, txs, self.verify_fees).await;
|
||||
let e = verify_transactions(
|
||||
&overlay,
|
||||
verifying_block_height,
|
||||
txs,
|
||||
&mut MerkleTree::new(1),
|
||||
self.verify_fees,
|
||||
)
|
||||
.await;
|
||||
let lock = overlay.lock().unwrap();
|
||||
let mut overlay = lock.overlay.lock().unwrap();
|
||||
|
||||
@@ -461,7 +500,14 @@ impl Validator {
|
||||
|
||||
// Verify transaction
|
||||
let mut erroneous_txs = vec![];
|
||||
if let Err(e) = verify_producer_transaction(&overlay, verifying_block_height, tx).await {
|
||||
if let Err(e) = verify_producer_transaction(
|
||||
&overlay,
|
||||
verifying_block_height,
|
||||
tx,
|
||||
&mut MerkleTree::new(1),
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {}", e);
|
||||
erroneous_txs.push(tx.clone());
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ use std::collections::HashMap;
|
||||
use darkfi_sdk::{
|
||||
blockchain::{block_epoch, block_version},
|
||||
crypto::{
|
||||
schnorr::SchnorrPublic, ContractId, PublicKey, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID,
|
||||
schnorr::SchnorrPublic, ContractId, MerkleTree, PublicKey, DEPLOYOOOR_CONTRACT_ID,
|
||||
MONEY_CONTRACT_ID,
|
||||
},
|
||||
dark_tree::dark_forest_leaf_vec_integrity_check,
|
||||
deploy::DeployParamsV1,
|
||||
@@ -35,7 +36,9 @@ use num_bigint::BigUint;
|
||||
use smol::io::Cursor;
|
||||
|
||||
use crate::{
|
||||
blockchain::{BlockInfo, Blockchain, BlockchainOverlayPtr},
|
||||
blockchain::{
|
||||
block_store::append_tx_to_merkle_tree, BlockInfo, Blockchain, BlockchainOverlayPtr,
|
||||
},
|
||||
error::TxVerifyFailed,
|
||||
runtime::vm_runtime::Runtime,
|
||||
tx::{Transaction, MAX_TX_CALLS, MIN_TX_CALLS},
|
||||
@@ -78,15 +81,17 @@ pub async fn verify_genesis_block(overlay: &BlockchainOverlayPtr, block: &BlockI
|
||||
return Err(Error::BlockContainsNoTransactions(block_hash))
|
||||
}
|
||||
|
||||
// Genesis transaction must be the Transaction::default() one(empty)
|
||||
if block.txs[0] != Transaction::default() {
|
||||
error!(target: "validator::verification::verify_genesis_block", "Genesis proposal transaction is not default one");
|
||||
return Err(TxVerifyFailed::ErroneousTxs(vec![block.txs[0].clone()]).into())
|
||||
// Genesis producer transaction must be the Transaction::default() one(empty)
|
||||
let producer_tx = block.txs.last().unwrap();
|
||||
if producer_tx != &Transaction::default() {
|
||||
error!(target: "validator::verification::verify_genesis_block", "Genesis producer transaction is not default one");
|
||||
return Err(TxVerifyFailed::ErroneousTxs(vec![producer_tx.clone()]).into())
|
||||
}
|
||||
|
||||
// Verify transactions, exluding producer(first) one
|
||||
let txs = &block.txs[1..];
|
||||
if let Err(e) = verify_transactions(overlay, block.header.height, txs, false).await {
|
||||
// Verify transactions, exluding producer(last) one
|
||||
let mut tree = MerkleTree::new(1);
|
||||
let txs = &block.txs[..block.txs.len() - 1];
|
||||
if let Err(e) = verify_transactions(overlay, block.header.height, txs, &mut tree, false).await {
|
||||
warn!(
|
||||
target: "validator::verification::verify_genesis_block",
|
||||
"[VALIDATOR] Erroneous transactions found in set",
|
||||
@@ -95,6 +100,13 @@ pub async fn verify_genesis_block(overlay: &BlockchainOverlayPtr, block: &BlockI
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
// Append producer transaction to the tree and check tree matches header one
|
||||
append_tx_to_merkle_tree(&mut tree, producer_tx)?;
|
||||
if tree != block.header.tree {
|
||||
error!(target: "validator::verification::verify_genesis_block", "Genesis Merkle tree is invalid");
|
||||
return Err(Error::BlockIsInvalid(block_hash))
|
||||
}
|
||||
|
||||
// Insert block
|
||||
overlay.lock().unwrap().add_block(block)?;
|
||||
|
||||
@@ -189,14 +201,10 @@ pub async fn verify_block(
|
||||
return Err(Error::BlockContainsNoTransactions(block_hash))
|
||||
}
|
||||
|
||||
// Verify proposal transaction.
|
||||
let public_key =
|
||||
verify_producer_transaction(overlay, block.header.height, &block.txs[0]).await?;
|
||||
verify_producer_signature(block, &public_key)?;
|
||||
|
||||
// Verify transactions, exluding producer(first) one
|
||||
let txs = &block.txs[1..];
|
||||
let e = verify_transactions(overlay, block.header.height, txs, false).await;
|
||||
// Verify transactions, exluding producer(last) one
|
||||
let mut tree = MerkleTree::new(1);
|
||||
let txs = &block.txs[..block.txs.len() - 1];
|
||||
let e = verify_transactions(overlay, block.header.height, txs, &mut tree, false).await;
|
||||
if let Err(e) = e {
|
||||
warn!(
|
||||
target: "validator::verification::verify_block",
|
||||
@@ -206,6 +214,22 @@ pub async fn verify_block(
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
// Verify producer transaction
|
||||
let public_key = verify_producer_transaction(
|
||||
overlay,
|
||||
block.header.height,
|
||||
block.txs.last().unwrap(),
|
||||
&mut tree,
|
||||
)
|
||||
.await?;
|
||||
verify_producer_signature(block, &public_key)?;
|
||||
|
||||
// Verify tree matches header one
|
||||
if tree != block.header.tree {
|
||||
error!(target: "validator::verification::verify_block", "Block Merkle tree is invalid");
|
||||
return Err(Error::BlockIsInvalid(block_hash))
|
||||
}
|
||||
|
||||
// Insert block
|
||||
overlay.lock().unwrap().add_block(block)?;
|
||||
|
||||
@@ -226,10 +250,12 @@ pub fn verify_producer_signature(block: &BlockInfo, public_key: &PublicKey) -> R
|
||||
|
||||
/// Verify WASM execution, signatures, and ZK proofs for a given producer [`Transaction`],
|
||||
/// and apply it to the provided overlay. Returns transaction signature public key.
|
||||
/// Additionally, append its hash to the provided Merkle tree.
|
||||
pub async fn verify_producer_transaction(
|
||||
overlay: &BlockchainOverlayPtr,
|
||||
verifying_block_height: u64,
|
||||
tx: &Transaction,
|
||||
tree: &mut MerkleTree,
|
||||
) -> Result<PublicKey> {
|
||||
let tx_hash = tx.hash()?;
|
||||
debug!(target: "validator::verification::verify_producer_transaction", "Validating proposal transaction {}", tx_hash);
|
||||
@@ -342,19 +368,24 @@ pub async fn verify_producer_transaction(
|
||||
error!(target: "validator::verification::verify_proposal_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e);
|
||||
return Err(TxVerifyFailed::InvalidZkProof.into())
|
||||
}
|
||||
|
||||
debug!(target: "validator::verification::verify_producer_transaction", "ZK proof verification successful");
|
||||
|
||||
// Append hash to merkle tree
|
||||
append_tx_to_merkle_tree(tree, tx)?;
|
||||
|
||||
debug!(target: "validator::verification::verify_producer_transaction", "Proposal transaction {} verified successfully", tx_hash);
|
||||
|
||||
Ok(signature_public_key)
|
||||
}
|
||||
|
||||
/// Verify WASM execution, signatures, and ZK proofs for a given [`Transaction`],
|
||||
/// and apply it to the provided overlay.
|
||||
/// and apply it to the provided overlay. Additionally, append its hash to the
|
||||
/// provided Merkle tree.
|
||||
pub async fn verify_transaction(
|
||||
overlay: &BlockchainOverlayPtr,
|
||||
verifying_block_height: u64,
|
||||
tx: &Transaction,
|
||||
tree: &mut MerkleTree,
|
||||
verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
|
||||
verify_fee: bool,
|
||||
) -> Result<u64> {
|
||||
@@ -577,6 +608,9 @@ pub async fn verify_transaction(
|
||||
}
|
||||
debug!(target: "validator::verification::verify_transaction", "ZK proof verification successful");
|
||||
|
||||
// Append hash to merkle tree
|
||||
append_tx_to_merkle_tree(tree, tx)?;
|
||||
|
||||
debug!(target: "validator::verification::verify_transaction", "Transaction {} verified successfully", tx_hash);
|
||||
Ok(gas_used)
|
||||
}
|
||||
@@ -584,14 +618,18 @@ pub async fn verify_transaction(
|
||||
/// Verify a set of [`Transaction`] in sequence and apply them if all are valid.
|
||||
/// In case any of the transactions fail, they will be returned to the caller as an error.
|
||||
/// If all transactions are valid, the function will return the accumulated gas used from
|
||||
/// all the transactions.
|
||||
/// all the transactions. Additionally, their hash is appended to the provided Merkle tree.
|
||||
pub async fn verify_transactions(
|
||||
overlay: &BlockchainOverlayPtr,
|
||||
verifying_block_height: u64,
|
||||
txs: &[Transaction],
|
||||
tree: &mut MerkleTree,
|
||||
verify_fees: bool,
|
||||
) -> Result<u64> {
|
||||
debug!(target: "validator::verification::verify_transactions", "Verifying {} transactions", txs.len());
|
||||
if txs.is_empty() {
|
||||
return Ok(0)
|
||||
}
|
||||
|
||||
// Tracker for failed txs
|
||||
let mut erroneous_txs = vec![];
|
||||
@@ -612,12 +650,13 @@ 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, verifying_block_height, tx, &mut vks, verify_fees).await {
|
||||
match verify_transaction(overlay, verifying_block_height, tx, tree, &mut vks, verify_fees)
|
||||
.await
|
||||
{
|
||||
Ok(gas) => gas_used += gas,
|
||||
Err(e) => {
|
||||
warn!(target: "validator::verification::verify_transactions", "Transaction verification failed: {}", e);
|
||||
erroneous_txs.push(tx.clone());
|
||||
// TODO: verify this works as expected
|
||||
overlay.lock().unwrap().revert_to_checkpoint()?;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user