diff --git a/chain/src/chain.rs b/chain/src/chain.rs
index e19298b58..70ab100ec 100644
--- a/chain/src/chain.rs
+++ b/chain/src/chain.rs
@@ -506,9 +506,9 @@ impl Chain {
/// that has not yet sufficiently matured.
pub fn verify_coinbase_maturity(&self, tx: &Transaction) -> Result<(), Error> {
let height = self.next_block_height()?;
- let mut txhashset = self.txhashset.write();
- txhashset::extending_readonly(&mut txhashset, |extension| {
- extension.verify_coinbase_maturity(&tx.inputs(), height)?;
+ let txhashset = self.txhashset.read();
+ txhashset::utxo_view(&txhashset, |utxo| {
+ utxo.verify_coinbase_maturity(&tx.inputs(), height)?;
Ok(())
})
}
diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs
index 3f949869c..3fcc55838 100644
--- a/chain/src/pipe.rs
+++ b/chain/src/pipe.rs
@@ -434,16 +434,10 @@ fn validate_block(block: &Block, ctx: &mut BlockContext<'_>) -> Result<(), Error
Ok(())
}
-/// TODO - This can move into the utxo_view.
-/// Verify the block is not attempting to spend coinbase outputs
-/// before they have sufficiently matured.
-/// Note: requires a txhashset extension.
-fn verify_coinbase_maturity(
- block: &Block,
- ext: &mut txhashset::Extension<'_>,
-) -> Result<(), Error> {
- ext.verify_coinbase_maturity(&block.inputs(), block.header.height)?;
- Ok(())
+/// Verify the block is not spending coinbase outputs before they have sufficiently matured.
+fn verify_coinbase_maturity(block: &Block, ext: &txhashset::Extension<'_>) -> Result<(), Error> {
+ ext.utxo_view()
+ .verify_coinbase_maturity(&block.inputs(), block.header.height)
}
/// Some "real magick" verification logic.
@@ -649,7 +643,5 @@ pub fn rewind_and_apply_fork(b: &Block, ext: &mut txhashset::Extension<'_>) -> R
}
fn validate_utxo(block: &Block, ext: &txhashset::Extension<'_>) -> Result<(), Error> {
- let utxo = ext.utxo_view();
- utxo.validate_block(block)?;
- Ok(())
+ ext.utxo_view().validate_block(block)
}
diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs
index b711caf1c..0ed33e087 100644
--- a/chain/src/txhashset/txhashset.rs
+++ b/chain/src/txhashset/txhashset.rs
@@ -20,7 +20,7 @@ use crate::core::core::hash::{Hash, Hashed};
use crate::core::core::merkle_proof::MerkleProof;
use crate::core::core::pmmr::{self, ReadonlyPMMR, RewindablePMMR, PMMR};
use crate::core::core::{
- Block, BlockHeader, Input, Output, OutputFeatures, OutputIdentifier, TxKernel, TxKernelEntry,
+ Block, BlockHeader, Input, Output, OutputIdentifier, TxKernel, TxKernelEntry,
};
use crate::core::global;
use crate::core::ser::{PMMRIndexHashable, PMMRable};
@@ -353,11 +353,13 @@ where
{
let output_pmmr =
ReadonlyPMMR::at(&trees.output_pmmr_h.backend, trees.output_pmmr_h.last_pos);
+ let header_pmmr =
+ ReadonlyPMMR::at(&trees.header_pmmr_h.backend, trees.header_pmmr_h.last_pos);
// Create a new batch here to pass into the utxo_view.
// Discard it (rollback) after we finish with the utxo_view.
let batch = trees.commit_index.batch()?;
- let utxo = UTXOView::new(output_pmmr, &batch);
+ let utxo = UTXOView::new(output_pmmr, header_pmmr, &batch);
res = inner(&utxo);
}
res
@@ -829,46 +831,11 @@ impl<'a> Extension<'a> {
/// Build a view of the current UTXO set based on the output PMMR.
pub fn utxo_view(&'a self) -> UTXOView<'a> {
- UTXOView::new(self.output_pmmr.readonly_pmmr(), self.batch)
- }
-
- /// Verify we are not attempting to spend any coinbase outputs
- /// that have not sufficiently matured.
- pub fn verify_coinbase_maturity(
- &mut self,
- inputs: &Vec,
- height: u64,
- ) -> Result<(), Error> {
- // Find the greatest output pos of any coinbase
- // outputs we are attempting to spend.
- let pos = inputs
- .iter()
- .filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
- .filter_map(|x| self.batch.get_output_pos(&x.commitment()).ok())
- .max()
- .unwrap_or(0);
-
- if pos > 0 {
- // If we have not yet reached 1,000 / 1,440 blocks then
- // we can fail immediately as coinbase cannot be mature.
- if height < global::coinbase_maturity() {
- return Err(ErrorKind::ImmatureCoinbase.into());
- }
-
- // Find the "cutoff" pos in the output MMR based on the
- // header from 1,000 blocks ago.
- let cutoff_height = height.checked_sub(global::coinbase_maturity()).unwrap_or(0);
- let cutoff_header = self.get_header_by_height(cutoff_height)?;
- let cutoff_pos = cutoff_header.output_mmr_size;
-
- // If any output pos exceed the cutoff_pos
- // we know they have not yet sufficiently matured.
- if pos > cutoff_pos {
- return Err(ErrorKind::ImmatureCoinbase.into());
- }
- }
-
- Ok(())
+ UTXOView::new(
+ self.output_pmmr.readonly_pmmr(),
+ self.header_pmmr.readonly_pmmr(),
+ self.batch,
+ )
}
/// Apply a new block to the existing state.
diff --git a/chain/src/txhashset/utxo_view.rs b/chain/src/txhashset/utxo_view.rs
index cd4c2d989..586ea108e 100644
--- a/chain/src/txhashset/utxo_view.rs
+++ b/chain/src/txhashset/utxo_view.rs
@@ -14,8 +14,10 @@
//! Lightweight readonly view into output MMR for convenience.
-use crate::core::core::pmmr::ReadonlyPMMR;
-use crate::core::core::{Block, Input, Output, Transaction};
+use crate::core::core::hash::Hash;
+use crate::core::core::pmmr::{self, ReadonlyPMMR};
+use crate::core::core::{Block, BlockHeader, Input, Output, OutputFeatures, Transaction};
+use crate::core::global;
use crate::core::ser::PMMRIndexHashable;
use crate::error::{Error, ErrorKind};
use crate::store::Batch;
@@ -23,17 +25,23 @@ use grin_store::pmmr::PMMRBackend;
/// Readonly view of the UTXO set (based on output MMR).
pub struct UTXOView<'a> {
- pmmr: ReadonlyPMMR<'a, Output, PMMRBackend