ZIP-213: Explain how Zebra validates shielded coinbase outputs like other shielded outputs (#2382)

* Describe how a ZIP-213 rule is implemented in the transaction verifier

* Move the only coinbase-specific check outside the ZIP-213 block

This change isn't required to implement the ZIP-213 rule, but it makes
it easier to identify the specific checks for coinbase transactions.

* Add a note about coinbase in the mempool

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
teor 2021-06-25 09:53:32 +10:00 committed by GitHub
parent 22f2f06bad
commit df7075e962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 33 additions and 24 deletions

View File

@ -87,6 +87,8 @@ pub enum Request {
height: block::Height,
},
/// Verify the supplied transaction as part of the mempool.
///
/// Note: coinbase transactions are invalid in the mempool
Mempool {
/// The transaction itself.
transaction: Arc<Transaction>,
@ -163,6 +165,19 @@ where
// Do basic checks first
check::has_inputs_and_outputs(&tx)?;
if tx.is_coinbase() {
check::coinbase_tx_no_prevout_joinsplit_spend(&tx)?;
}
// "The consensus rules applied to valueBalance, vShieldedOutput, and bindingSig
// in non-coinbase transactions MUST also be applied to coinbase transactions."
//
// This rule is implicitly implemented during Sapling and Orchard verification,
// because they do not distinguish between coinbase and non-coinbase transactions.
//
// Note: this rule originally applied to Sapling, but we assume it also applies to Orchard.
//
// https://zips.z.cash/zip-0213#specification
let async_checks = match tx.as_ref() {
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {
tracing::debug!(?tx, "got transaction with wrong version");
@ -460,33 +475,27 @@ where
) -> Result<AsyncChecks, TransactionError> {
let transaction = request.transaction();
if transaction.is_coinbase() {
check::coinbase_tx_no_prevout_joinsplit_spend(&transaction)?;
// feed all of the inputs to the script and shielded verifiers
// the script_verifier also checks transparent sighashes, using its own implementation
let cached_ffi_transaction = Arc::new(CachedFfiTransaction::new(transaction));
let known_utxos = request.known_utxos();
let upgrade = request.upgrade(network);
Ok(AsyncChecks::new())
} else {
// feed all of the inputs to the script and shielded verifiers
// the script_verifier also checks transparent sighashes, using its own implementation
let cached_ffi_transaction = Arc::new(CachedFfiTransaction::new(transaction));
let known_utxos = request.known_utxos();
let upgrade = request.upgrade(network);
let script_checks = (0..inputs.len())
.into_iter()
.map(move |input_index| {
let request = script::Request {
upgrade,
known_utxos: known_utxos.clone(),
cached_ffi_transaction: cached_ffi_transaction.clone(),
input_index,
};
let script_checks = (0..inputs.len())
.into_iter()
.map(move |input_index| {
let request = script::Request {
upgrade,
known_utxos: known_utxos.clone(),
cached_ffi_transaction: cached_ffi_transaction.clone(),
input_index,
};
script_verifier.clone().oneshot(request).boxed()
})
.collect();
script_verifier.clone().oneshot(request).boxed()
})
.collect();
Ok(script_checks)
}
Ok(script_checks)
}
/// Await a set of checks that should all succeed.