Add verify_cut_through to both transaction and block (#1130)

We have no code that will produce a non-cut-through version of either
so this should be safe to add in testnet2
This commit is contained in:
Antioch Peverell 2018-06-10 20:59:58 +01:00 committed by Ignotus Peverell
parent 310c7b0b48
commit edc01bb05a
2 changed files with 42 additions and 3 deletions

View file

@ -70,6 +70,9 @@ pub enum Error {
MerkleProof, MerkleProof,
/// Error when verifying kernel sums via committed trait. /// Error when verifying kernel sums via committed trait.
Committed(committed::Error), Committed(committed::Error),
/// Validation error relating to cut-through.
/// Specifically the tx is spending its own output, which is not valid.
CutThrough,
/// Other unspecified error condition /// Other unspecified error condition
Other(String), Other(String),
} }
@ -682,6 +685,7 @@ impl Block {
) -> Result<((Commitment, Commitment)), Error> { ) -> Result<((Commitment, Commitment)), Error> {
self.verify_weight()?; self.verify_weight()?;
self.verify_sorted()?; self.verify_sorted()?;
self.verify_cut_through()?;
self.verify_coinbase()?; self.verify_coinbase()?;
self.verify_inputs()?; self.verify_inputs()?;
self.verify_kernel_lock_heights()?; self.verify_kernel_lock_heights()?;
@ -705,6 +709,7 @@ impl Block {
Ok(()) Ok(())
} }
// Verify that inputs|outputs|kernels are all sorted in lexicographical order.
fn verify_sorted(&self) -> Result<(), Error> { fn verify_sorted(&self) -> Result<(), Error> {
self.inputs.verify_sort_order()?; self.inputs.verify_sort_order()?;
self.outputs.verify_sort_order()?; self.outputs.verify_sort_order()?;
@ -712,6 +717,19 @@ impl Block {
Ok(()) Ok(())
} }
// Verify that no input is spending an ouput from the same block.
fn verify_cut_through(&self) -> Result<(), Error> {
for inp in &self.inputs {
if self.outputs
.iter()
.any(|out| out.commitment() == inp.commitment())
{
return Err(Error::CutThrough);
}
}
Ok(())
}
/// We can verify the Merkle proof (for coinbase inputs) here in isolation. /// We can verify the Merkle proof (for coinbase inputs) here in isolation.
/// But we cannot check the following as we need data from the index and /// But we cannot check the following as we need data from the index and
/// the PMMR. So we must be sure to check these at the appropriate point /// the PMMR. So we must be sure to check these at the appropriate point

View file

@ -14,8 +14,8 @@
//! Transactions //! Transactions
use std::cmp::Ordering;
use std::cmp::max; use std::cmp::max;
use std::cmp::Ordering;
use std::collections::HashSet; use std::collections::HashSet;
use std::io::Cursor; use std::io::Cursor;
use std::{error, fmt}; use std::{error, fmt};
@ -26,12 +26,12 @@ use util::{kernel_sig_msg, static_secp_instance};
use consensus; use consensus;
use consensus::VerifySortOrder; use consensus::VerifySortOrder;
use core::BlockHeader;
use core::committed; use core::committed;
use core::committed::Committed;
use core::global; use core::global;
use core::hash::{Hash, Hashed, ZERO_HASH}; use core::hash::{Hash, Hashed, ZERO_HASH};
use core::pmmr::MerkleProof; use core::pmmr::MerkleProof;
use core::BlockHeader;
use core::Committed;
use keychain; use keychain;
use keychain::BlindingFactor; use keychain::BlindingFactor;
use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable, use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable,
@ -78,6 +78,12 @@ pub enum Error {
InvalidProofMessage, InvalidProofMessage,
/// Error when verifying kernel sums via committed trait. /// Error when verifying kernel sums via committed trait.
Committed(committed::Error), Committed(committed::Error),
/// Error when sums do not verify correctly during tx aggregation.
/// Likely a "double spend" across two unconfirmed txs.
AggregationError,
/// Validation error relating to cut-through (tx is spending its own
/// output).
CutThrough,
} }
impl error::Error for Error { impl error::Error for Error {
@ -430,6 +436,7 @@ impl Transaction {
return Err(Error::TooManyInputs); return Err(Error::TooManyInputs);
} }
self.verify_sorted()?; self.verify_sorted()?;
self.verify_cut_through()?;
self.verify_kernel_sums(self.overage(), self.offset, None, None)?; self.verify_kernel_sums(self.overage(), self.offset, None, None)?;
self.verify_rangeproofs()?; self.verify_rangeproofs()?;
self.verify_kernel_signatures()?; self.verify_kernel_signatures()?;
@ -464,12 +471,26 @@ impl Transaction {
tx_weight as u32 tx_weight as u32
} }
// Verify that inputs|outputs|kernels are all sorted in lexicographical order.
fn verify_sorted(&self) -> Result<(), Error> { fn verify_sorted(&self) -> Result<(), Error> {
self.inputs.verify_sort_order()?; self.inputs.verify_sort_order()?;
self.outputs.verify_sort_order()?; self.outputs.verify_sort_order()?;
self.kernels.verify_sort_order()?; self.kernels.verify_sort_order()?;
Ok(()) Ok(())
} }
// Verify that no input is spending an ouput from the same block.
fn verify_cut_through(&self) -> Result<(), Error> {
for inp in &self.inputs {
if self.outputs
.iter()
.any(|out| out.commitment() == inp.commitment())
{
return Err(Error::CutThrough);
}
}
Ok(())
}
} }
/// Aggregate a vec of transactions into a multi-kernel transaction with /// Aggregate a vec of transactions into a multi-kernel transaction with