From eafe730a65b1de2d5a1df5be53ebb54d5df25ec2 Mon Sep 17 00:00:00 2001 From: Jeremy Rubin Date: Tue, 19 Feb 2019 02:02:59 -0800 Subject: [PATCH] refactor: allocation free cut through algorithms (#2567) --- core/src/core/transaction.rs | 64 ++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 0e67e6489..f6239a484 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -31,7 +31,6 @@ use crate::{consensus, global}; use enum_primitive::FromPrimitive; use std::cmp::Ordering; use std::cmp::{max, min}; -use std::collections::HashSet; use std::sync::Arc; use std::{error, fmt}; @@ -647,14 +646,21 @@ impl TransactionBody { } // Verify that no input is spending an output from the same block. + // Assumes inputs and outputs are sorted fn verify_cut_through(&self) -> Result<(), Error> { - let mut out_set = HashSet::new(); - for out in &self.outputs { - out_set.insert(out.commitment()); - } - for inp in &self.inputs { - if out_set.contains(&inp.commitment()) { - return Err(Error::CutThrough); + let mut inputs = self.inputs.iter().map(|x| x.hash()).peekable(); + let mut outputs = self.outputs.iter().map(|x| x.hash()).peekable(); + while let (Some(ih), Some(oh)) = (inputs.peek(), outputs.peek()) { + match ih.cmp(oh) { + Ordering::Less => { + inputs.next(); + } + Ordering::Greater => { + outputs.next(); + } + Ordering::Equal => { + return Err(Error::CutThrough); + } } } Ok(()) @@ -968,24 +974,34 @@ impl Transaction { /// and outputs. pub fn cut_through(inputs: &mut Vec, outputs: &mut Vec) -> Result<(), Error> { // assemble output commitments set, checking they're all unique - let mut out_set = HashSet::new(); - let all_uniq = { outputs.iter().all(|o| out_set.insert(o.commitment())) }; - if !all_uniq { + outputs.sort_unstable(); + if outputs.windows(2).any(|pair| pair[0] == pair[1]) { return Err(Error::AggregationError); } - - let in_set = inputs - .iter() - .map(|inp| inp.commitment()) - .collect::>(); - - let to_cut_through = in_set.intersection(&out_set).collect::>(); - - // filter and sort - inputs.retain(|inp| !to_cut_through.contains(&inp.commitment())); - outputs.retain(|out| !to_cut_through.contains(&out.commitment())); inputs.sort_unstable(); - outputs.sort_unstable(); + let mut inputs_idx = 0; + let mut outputs_idx = 0; + let mut ncut = 0; + while inputs_idx < inputs.len() && outputs_idx < outputs.len() { + match inputs[inputs_idx].hash().cmp(&outputs[outputs_idx].hash()) { + Ordering::Less => { + inputs[inputs_idx - ncut] = inputs[inputs_idx]; + inputs_idx += 1; + } + Ordering::Greater => { + outputs[outputs_idx - ncut] = outputs[outputs_idx]; + outputs_idx += 1; + } + Ordering::Equal => { + inputs_idx += 1; + outputs_idx += 1; + ncut += 1; + } + } + } + // Cut elements that have already been copied + outputs.drain(outputs_idx - ncut..outputs_idx); + inputs.drain(inputs_idx - ncut..inputs_idx); Ok(()) } @@ -1109,7 +1125,7 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec) -> Result