|
@@ -19,7 +19,7 @@
|
|
|
use std::{collections::HashMap, io::Cursor};
|
|
use std::{collections::HashMap, io::Cursor};
|
|
|
|
|
|
|
|
use darkfi_sdk::{
|
|
use darkfi_sdk::{
|
|
|
- crypto::{PublicKey, CONSENSUS_CONTRACT_ID, MONEY_CONTRACT_ID},
|
|
|
|
|
|
|
+ crypto::{schnorr::SchnorrPublic, PublicKey, CONSENSUS_CONTRACT_ID, MONEY_CONTRACT_ID},
|
|
|
pasta::pallas,
|
|
pasta::pallas,
|
|
|
};
|
|
};
|
|
|
use darkfi_serial::{Decodable, Encodable, WriteExt};
|
|
use darkfi_serial::{Decodable, Encodable, WriteExt};
|
|
@@ -139,8 +139,8 @@ pub async fn verify_block(
|
|
|
// Validate proposal transaction if not in testing mode
|
|
// Validate proposal transaction if not in testing mode
|
|
|
if !testing_mode {
|
|
if !testing_mode {
|
|
|
let tx = block.txs.last().unwrap();
|
|
let tx = block.txs.last().unwrap();
|
|
|
- verify_producer_transaction(overlay, time_keeper, tx).await?;
|
|
|
|
|
- verify_producer_signature(block)?;
|
|
|
|
|
|
|
+ let public_key = verify_producer_transaction(overlay, time_keeper, tx).await?;
|
|
|
|
|
+ verify_producer_signature(block, &public_key)?;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Verify transactions, exluding producer(last) one
|
|
// Verify transactions, exluding producer(last) one
|
|
@@ -160,24 +160,25 @@ pub async fn verify_block(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Validate block proposer signature, using the proposal transaction signature as signing key
|
|
/// Validate block proposer signature, using the proposal transaction signature as signing key
|
|
|
-/// over blocks header, transactions and slots.
|
|
|
|
|
-pub fn verify_producer_signature(_block: &BlockInfo) -> Result<()> {
|
|
|
|
|
- // TODO:
|
|
|
|
|
- // Grab public key from proposal transaction metadata on verify_proposal_transaction
|
|
|
|
|
- // and pass it here to verify the signature
|
|
|
|
|
|
|
+/// over blocks header hash.
|
|
|
|
|
+pub fn verify_producer_signature(block: &BlockInfo, public_key: &PublicKey) -> Result<()> {
|
|
|
|
|
+ if !public_key.verify(&block.header.hash()?.as_bytes()[..], &block.signature) {
|
|
|
|
|
+ warn!(target: "validator::verification::verify_producer_signature", "Proposer {} signature could not be verified", public_key);
|
|
|
|
|
+ return Err(Error::InvalidSignature)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Validate WASM execution, signatures, and ZK proofs for a given producer [`Transaction`],
|
|
/// Validate WASM execution, signatures, and ZK proofs for a given producer [`Transaction`],
|
|
|
-/// and apply it to the provided overlay.
|
|
|
|
|
|
|
+/// and apply it to the provided overlay. Returns transaction signature public key.
|
|
|
pub async fn verify_producer_transaction(
|
|
pub async fn verify_producer_transaction(
|
|
|
overlay: &BlockchainOverlayPtr,
|
|
overlay: &BlockchainOverlayPtr,
|
|
|
time_keeper: &TimeKeeper,
|
|
time_keeper: &TimeKeeper,
|
|
|
tx: &Transaction,
|
|
tx: &Transaction,
|
|
|
-) -> Result<()> {
|
|
|
|
|
|
|
+) -> Result<PublicKey> {
|
|
|
let tx_hash = tx.hash()?;
|
|
let tx_hash = tx.hash()?;
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Validating proposal transaction {}", tx_hash);
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Validating proposal transaction {}", tx_hash);
|
|
|
|
|
|
|
|
// Transaction must contain a single call, either Money::PoWReward(0x08) or Consensus::Proposal(0x02)
|
|
// Transaction must contain a single call, either Money::PoWReward(0x08) or Consensus::Proposal(0x02)
|
|
|
if tx.calls.len() != 1 ||
|
|
if tx.calls.len() != 1 ||
|
|
@@ -186,7 +187,7 @@ pub async fn verify_producer_transaction(
|
|
|
(tx.calls[0].contract_id == *MONEY_CONTRACT_ID && tx.calls[0].data[0] != 0x08) ||
|
|
(tx.calls[0].contract_id == *MONEY_CONTRACT_ID && tx.calls[0].data[0] != 0x08) ||
|
|
|
(tx.calls[0].contract_id == *CONSENSUS_CONTRACT_ID && tx.calls[0].data[0] != 0x02)
|
|
(tx.calls[0].contract_id == *CONSENSUS_CONTRACT_ID && tx.calls[0].data[0] != 0x02)
|
|
|
{
|
|
{
|
|
|
- error!(target: "validator::verification::verify_proposal_transaction", "Proposal transaction is malformed");
|
|
|
|
|
|
|
+ error!(target: "validator::verification::verify_producer_transaction", "Proposal transaction is malformed");
|
|
|
return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
|
|
return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -203,19 +204,19 @@ pub async fn verify_producer_transaction(
|
|
|
// Table of public keys used for signature verification
|
|
// Table of public keys used for signature verification
|
|
|
let mut sig_table = vec![];
|
|
let mut sig_table = vec![];
|
|
|
|
|
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Executing contract call");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Executing contract call");
|
|
|
|
|
|
|
|
// Write the actual payload data
|
|
// Write the actual payload data
|
|
|
let mut payload = vec![];
|
|
let mut payload = vec![];
|
|
|
payload.write_u32(0)?; // Call index
|
|
payload.write_u32(0)?; // Call index
|
|
|
tx.calls.encode(&mut payload)?; // Actual call data
|
|
tx.calls.encode(&mut payload)?; // Actual call data
|
|
|
|
|
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Instantiating WASM runtime");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Instantiating WASM runtime");
|
|
|
let wasm = overlay.lock().unwrap().wasm_bincode.get(call.contract_id)?;
|
|
let wasm = overlay.lock().unwrap().wasm_bincode.get(call.contract_id)?;
|
|
|
|
|
|
|
|
let mut runtime = Runtime::new(&wasm, overlay.clone(), call.contract_id, time_keeper.clone())?;
|
|
let mut runtime = Runtime::new(&wasm, overlay.clone(), call.contract_id, time_keeper.clone())?;
|
|
|
|
|
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Executing \"metadata\" call");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Executing \"metadata\" call");
|
|
|
let metadata = runtime.metadata(&payload)?;
|
|
let metadata = runtime.metadata(&payload)?;
|
|
|
|
|
|
|
|
// Decode the metadata retrieved from the execution
|
|
// Decode the metadata retrieved from the execution
|
|
@@ -224,11 +225,18 @@ pub async fn verify_producer_transaction(
|
|
|
// The tuple is (zkas_ns, public_inputs)
|
|
// The tuple is (zkas_ns, public_inputs)
|
|
|
let zkp_pub: Vec<(String, Vec<pallas::Base>)> = Decodable::decode(&mut decoder)?;
|
|
let zkp_pub: Vec<(String, Vec<pallas::Base>)> = Decodable::decode(&mut decoder)?;
|
|
|
let sig_pub: Vec<PublicKey> = Decodable::decode(&mut decoder)?;
|
|
let sig_pub: Vec<PublicKey> = Decodable::decode(&mut decoder)?;
|
|
|
|
|
+
|
|
|
|
|
+ // Check that only one ZK proof and signature public key exist
|
|
|
|
|
+ if zkp_pub.len() != 1 || sig_pub.len() != 1 {
|
|
|
|
|
+ error!(target: "validator::verification::verify_producer_transaction", "Proposal contains multiple ZK proofs or signature public keys");
|
|
|
|
|
+ return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// TODO: Make sure we've read all the bytes above.
|
|
// TODO: Make sure we've read all the bytes above.
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Successfully executed \"metadata\" call");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"metadata\" call");
|
|
|
|
|
|
|
|
// Here we'll look up verifying keys and insert them into the map.
|
|
// Here we'll look up verifying keys and insert them into the map.
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Performing VerifyingKey lookups from the sled db");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Performing VerifyingKey lookups from the sled db");
|
|
|
for (zkas_ns, _) in &zkp_pub {
|
|
for (zkas_ns, _) in &zkp_pub {
|
|
|
// TODO: verify this is correct behavior
|
|
// TODO: verify this is correct behavior
|
|
|
let inner_vk_map = verifying_keys.get_mut(&call.contract_id.to_bytes()).unwrap();
|
|
let inner_vk_map = verifying_keys.get_mut(&call.contract_id.to_bytes()).unwrap();
|
|
@@ -240,46 +248,47 @@ pub async fn verify_producer_transaction(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
zkp_table.push(zkp_pub);
|
|
zkp_table.push(zkp_pub);
|
|
|
|
|
+ let signature_public_key = *sig_pub.last().unwrap();
|
|
|
sig_table.push(sig_pub);
|
|
sig_table.push(sig_pub);
|
|
|
|
|
|
|
|
// After getting the metadata, we run the "exec" function with the same runtime
|
|
// After getting the metadata, we run the "exec" function with the same runtime
|
|
|
// and the same payload.
|
|
// and the same payload.
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Executing \"exec\" call");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Executing \"exec\" call");
|
|
|
let state_update = runtime.exec(&payload)?;
|
|
let state_update = runtime.exec(&payload)?;
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Successfully executed \"exec\" call");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"exec\" call");
|
|
|
|
|
|
|
|
// If that was successful, we apply the state update in the ephemeral overlay.
|
|
// If that was successful, we apply the state update in the ephemeral overlay.
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Executing \"apply\" call");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Executing \"apply\" call");
|
|
|
runtime.apply(&state_update)?;
|
|
runtime.apply(&state_update)?;
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Successfully executed \"apply\" call");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Successfully executed \"apply\" call");
|
|
|
|
|
|
|
|
// When we're done executing over the tx's contract call, we now move on with verification.
|
|
// When we're done executing over the tx's contract call, we now move on with verification.
|
|
|
// First we verify the signatures as that's cheaper, and then finally we verify the ZK proofs.
|
|
// First we verify the signatures as that's cheaper, and then finally we verify the ZK proofs.
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Verifying signatures for transaction {}", tx_hash);
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Verifying signatures for transaction {}", tx_hash);
|
|
|
if sig_table.len() != tx.signatures.len() {
|
|
if sig_table.len() != tx.signatures.len() {
|
|
|
- error!(target: "validator::verification::verify_proposal_transaction", "Incorrect number of signatures in tx {}", tx_hash);
|
|
|
|
|
|
|
+ error!(target: "validator::verification::verify_producer_transaction", "Incorrect number of signatures in tx {}", tx_hash);
|
|
|
return Err(TxVerifyFailed::MissingSignatures.into())
|
|
return Err(TxVerifyFailed::MissingSignatures.into())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// TODO: Go through the ZK circuits that have to be verified and account for the opcodes.
|
|
// 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) {
|
|
if let Err(e) = tx.verify_sigs(sig_table) {
|
|
|
- error!(target: "validator::verification::verify_proposal_transaction", "Signature verification for tx {} failed: {}", tx_hash, e);
|
|
|
|
|
|
|
+ error!(target: "validator::verification::verify_producer_transaction", "Signature verification for tx {} failed: {}", tx_hash, e);
|
|
|
return Err(TxVerifyFailed::InvalidSignature.into())
|
|
return Err(TxVerifyFailed::InvalidSignature.into())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Signature verification successful");
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Signature verification successful");
|
|
|
|
|
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Verifying ZK proofs for transaction {}", tx_hash);
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Verifying ZK proofs for transaction {}", tx_hash);
|
|
|
if let Err(e) = tx.verify_zkps(&verifying_keys, zkp_table).await {
|
|
if let Err(e) = tx.verify_zkps(&verifying_keys, zkp_table).await {
|
|
|
error!(target: "validator::verification::verify_proposal_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e);
|
|
error!(target: "validator::verification::verify_proposal_transaction", "ZK proof verification for tx {} failed: {}", tx_hash, e);
|
|
|
return Err(TxVerifyFailed::InvalidZkProof.into())
|
|
return Err(TxVerifyFailed::InvalidZkProof.into())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "ZK proof verification successful");
|
|
|
|
|
- debug!(target: "validator::verification::verify_proposal_transaction", "Proposal transaction {} verified successfully", tx_hash);
|
|
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "ZK proof verification successful");
|
|
|
|
|
+ debug!(target: "validator::verification::verify_producer_transaction", "Proposal transaction {} verified successfully", tx_hash);
|
|
|
|
|
|
|
|
- Ok(())
|
|
|
|
|
|
|
+ Ok(signature_public_key)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Validate WASM execution, signatures, and ZK proofs for a given [`Transaction`],
|
|
/// Validate WASM execution, signatures, and ZK proofs for a given [`Transaction`],
|