Takes &Network as an argument in `transparent_coinbase_spend()` and skips coinbase maturity check on regtest
This commit is contained in:
parent
2ac84f4728
commit
7db0de5e0c
|
@ -369,6 +369,7 @@ pub fn allow_all_transparent_coinbase_spends(
|
|||
_: transparent::OutPoint,
|
||||
_: transparent::CoinbaseSpendRestriction,
|
||||
_: &transparent::Utxo,
|
||||
_: &Network,
|
||||
) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -396,6 +397,7 @@ impl Block {
|
|||
transparent::OutPoint,
|
||||
transparent::CoinbaseSpendRestriction,
|
||||
&transparent::Utxo,
|
||||
&Network,
|
||||
) -> Result<(), E>
|
||||
+ Copy
|
||||
+ 'static,
|
||||
|
@ -564,6 +566,7 @@ where
|
|||
transparent::OutPoint,
|
||||
transparent::CoinbaseSpendRestriction,
|
||||
&transparent::Utxo,
|
||||
&Network,
|
||||
) -> Result<(), E>
|
||||
+ Copy
|
||||
+ 'static,
|
||||
|
@ -645,6 +648,7 @@ where
|
|||
transparent::OutPoint,
|
||||
transparent::CoinbaseSpendRestriction,
|
||||
&transparent::Utxo,
|
||||
&Network,
|
||||
) -> Result<(), E>
|
||||
+ Copy
|
||||
+ 'static,
|
||||
|
@ -668,6 +672,7 @@ where
|
|||
*candidate_outpoint,
|
||||
*spend_restriction,
|
||||
candidate_utxo.as_ref(),
|
||||
&Network::Mainnet,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
|
@ -677,6 +682,7 @@ where
|
|||
*candidate_outpoint,
|
||||
delete_transparent_outputs,
|
||||
candidate_utxo.as_ref(),
|
||||
&Network::Mainnet,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
|
|
|
@ -376,7 +376,7 @@ where
|
|||
// WONTFIX: Return an error for Request::Block as well to replace this check in
|
||||
// the state once #2336 has been implemented?
|
||||
if req.is_mempool() {
|
||||
Self::check_maturity_height(&req, &spent_utxos)?;
|
||||
Self::check_maturity_height(&req, &spent_utxos, &network)?;
|
||||
}
|
||||
|
||||
let cached_ffi_transaction =
|
||||
|
@ -586,12 +586,14 @@ where
|
|||
pub fn check_maturity_height(
|
||||
request: &Request,
|
||||
spent_utxos: &HashMap<transparent::OutPoint, transparent::Utxo>,
|
||||
network: &Network,
|
||||
) -> Result<(), TransactionError> {
|
||||
check::tx_transparent_coinbase_spends_maturity(
|
||||
request.transaction(),
|
||||
request.height(),
|
||||
request.known_utxos(),
|
||||
spent_utxos,
|
||||
network,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -480,6 +480,7 @@ pub fn tx_transparent_coinbase_spends_maturity(
|
|||
height: Height,
|
||||
block_new_outputs: Arc<HashMap<transparent::OutPoint, transparent::OrderedUtxo>>,
|
||||
spent_utxos: &HashMap<transparent::OutPoint, transparent::Utxo>,
|
||||
network: &Network,
|
||||
) -> Result<(), TransactionError> {
|
||||
for spend in tx.spent_outpoints() {
|
||||
let utxo = block_new_outputs
|
||||
|
@ -490,7 +491,7 @@ pub fn tx_transparent_coinbase_spends_maturity(
|
|||
|
||||
let spend_restriction = tx.coinbase_spend_restriction(height);
|
||||
|
||||
zebra_state::check::transparent_coinbase_spend(spend, spend_restriction, &utxo)?;
|
||||
zebra_state::check::transparent_coinbase_spend(spend, spend_restriction, &utxo, network)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -634,11 +634,15 @@ async fn mempool_request_with_immature_spend_is_rejected() {
|
|||
})
|
||||
.expect("known_utxos should contain the outpoint");
|
||||
|
||||
let expected_error =
|
||||
zebra_state::check::transparent_coinbase_spend(input_outpoint, spend_restriction, &utxo)
|
||||
.map_err(Box::new)
|
||||
.map_err(TransactionError::ValidateContextError)
|
||||
.expect_err("check should fail");
|
||||
let expected_error = zebra_state::check::transparent_coinbase_spend(
|
||||
input_outpoint,
|
||||
spend_restriction,
|
||||
&utxo,
|
||||
&Network::Mainnet,
|
||||
)
|
||||
.map_err(Box::new)
|
||||
.map_err(TransactionError::ValidateContextError)
|
||||
.expect_err("check should fail");
|
||||
|
||||
let mock_state_responses = || {
|
||||
let mut state = state.clone();
|
||||
|
|
|
@ -8,6 +8,7 @@ use zebra_chain::{
|
|||
amount::Amount,
|
||||
block::{Block, Height},
|
||||
fmt::TypeNameToDebug,
|
||||
parameters::Network,
|
||||
serialization::ZcashDeserializeInto,
|
||||
transaction::{self, LockTime, Transaction},
|
||||
transparent,
|
||||
|
@ -52,8 +53,12 @@ fn accept_shielded_mature_coinbase_utxo_spend() {
|
|||
spend_height: min_spend_height,
|
||||
};
|
||||
|
||||
let result =
|
||||
check::utxo::transparent_coinbase_spend(outpoint, spend_restriction, ordered_utxo.as_ref());
|
||||
let result = check::utxo::transparent_coinbase_spend(
|
||||
outpoint,
|
||||
spend_restriction,
|
||||
ordered_utxo.as_ref(),
|
||||
&Network::Mainnet,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
|
@ -80,8 +85,12 @@ fn reject_unshielded_coinbase_utxo_spend() {
|
|||
|
||||
let spend_restriction = transparent::CoinbaseSpendRestriction::SomeTransparentOutputs;
|
||||
|
||||
let result =
|
||||
check::utxo::transparent_coinbase_spend(outpoint, spend_restriction, ordered_utxo.as_ref());
|
||||
let result = check::utxo::transparent_coinbase_spend(
|
||||
outpoint,
|
||||
spend_restriction,
|
||||
ordered_utxo.as_ref(),
|
||||
&Network::Mainnet,
|
||||
);
|
||||
assert_eq!(result, Err(UnshieldedTransparentCoinbaseSpend { outpoint }));
|
||||
}
|
||||
|
||||
|
@ -106,8 +115,12 @@ fn reject_immature_coinbase_utxo_spend() {
|
|||
let spend_restriction =
|
||||
transparent::CoinbaseSpendRestriction::OnlyShieldedOutputs { spend_height };
|
||||
|
||||
let result =
|
||||
check::utxo::transparent_coinbase_spend(outpoint, spend_restriction, ordered_utxo.as_ref());
|
||||
let result = check::utxo::transparent_coinbase_spend(
|
||||
outpoint,
|
||||
spend_restriction,
|
||||
ordered_utxo.as_ref(),
|
||||
&Network::Mainnet,
|
||||
);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(ImmatureTransparentCoinbaseSpend {
|
||||
|
@ -122,7 +135,7 @@ fn reject_immature_coinbase_utxo_spend() {
|
|||
outpoint,
|
||||
spend_restriction,
|
||||
ordered_utxo.as_ref(),
|
||||
&Network::new_regtest(),
|
||||
&Network::new_regtest(None, None),
|
||||
)
|
||||
.expect("should pass check on Regtest");
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::collections::{HashMap, HashSet};
|
|||
|
||||
use zebra_chain::{
|
||||
amount,
|
||||
parameters::Network,
|
||||
transparent::{self, utxos_from_ordered_utxos, CoinbaseSpendRestriction::*},
|
||||
};
|
||||
|
||||
|
@ -53,6 +54,18 @@ pub fn transparent_spend(
|
|||
.iter()
|
||||
.filter_map(transparent::Input::outpoint);
|
||||
|
||||
// The state service returns UTXOs from pending blocks,
|
||||
// which can be rejected by later contextual checks.
|
||||
// This is a particular issue for v5 transactions,
|
||||
// because their authorizing data is only bound to the block data
|
||||
// during contextual validation (#2336).
|
||||
//
|
||||
// We don't want to use UTXOs from invalid pending blocks,
|
||||
// so we check transparent coinbase maturity and shielding
|
||||
// using known valid UTXOs during non-finalized chain validation.
|
||||
let spend_restriction =
|
||||
transaction.coinbase_spend_restriction(semantically_verified.height);
|
||||
|
||||
for spend in spends {
|
||||
let utxo = transparent_spend_chain_order(
|
||||
spend,
|
||||
|
@ -63,18 +76,12 @@ pub fn transparent_spend(
|
|||
finalized_state,
|
||||
)?;
|
||||
|
||||
// The state service returns UTXOs from pending blocks,
|
||||
// which can be rejected by later contextual checks.
|
||||
// This is a particular issue for v5 transactions,
|
||||
// because their authorizing data is only bound to the block data
|
||||
// during contextual validation (#2336).
|
||||
//
|
||||
// We don't want to use UTXOs from invalid pending blocks,
|
||||
// so we check transparent coinbase maturity and shielding
|
||||
// using known valid UTXOs during non-finalized chain validation.
|
||||
let spend_restriction =
|
||||
transaction.coinbase_spend_restriction(semantically_verified.height);
|
||||
transparent_coinbase_spend(spend, spend_restriction, utxo.as_ref())?;
|
||||
transparent_coinbase_spend(
|
||||
spend,
|
||||
spend_restriction,
|
||||
utxo.as_ref(),
|
||||
&finalized_state.network(),
|
||||
)?;
|
||||
|
||||
// We don't delete the UTXOs until the block is committed,
|
||||
// so we need to check for duplicate spends within the same block.
|
||||
|
@ -185,10 +192,13 @@ fn transparent_spend_chain_order(
|
|||
/// > Founders’ Reward outputs and transparent funding stream outputs.
|
||||
///
|
||||
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
|
||||
///
|
||||
/// Coinbase outputs _can_ be spent immediately on Regtest.
|
||||
pub fn transparent_coinbase_spend(
|
||||
outpoint: transparent::OutPoint,
|
||||
spend_restriction: transparent::CoinbaseSpendRestriction,
|
||||
utxo: &transparent::Utxo,
|
||||
network: &Network,
|
||||
) -> Result<(), ValidateContextError> {
|
||||
if !utxo.from_coinbase {
|
||||
return Ok(());
|
||||
|
@ -199,7 +209,7 @@ pub fn transparent_coinbase_spend(
|
|||
let min_spend_height = utxo.height + MIN_TRANSPARENT_COINBASE_MATURITY.into();
|
||||
let min_spend_height =
|
||||
min_spend_height.expect("valid UTXOs have coinbase heights far below Height::MAX");
|
||||
if spend_height >= min_spend_height {
|
||||
if spend_height >= min_spend_height || network.is_regtest() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ImmatureTransparentCoinbaseSpend {
|
||||
|
|
Loading…
Reference in New Issue