mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
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:
parent
310c7b0b48
commit
edc01bb05a
2 changed files with 42 additions and 3 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue