From c334c557aa3d920bf80dadeeded0fcc870d0bc45 Mon Sep 17 00:00:00 2001
From: Antioch Peverell <apeverell@protonmail.com>
Date: Tue, 28 Aug 2018 20:00:25 +0100
Subject: [PATCH] Simplify tx.validate() and transaction::aggregate() (#1436)

* simplify tx validation and aggregation
we *only* need to account for reward when building a block from txs

* rustfmt

* cleanup and tests passing

* rustfmt

* better comments in with_reward()

* fix wallet tests
---
 core/src/core/block.rs                |  14 ++-
 core/src/core/transaction.rs          |  58 ++++------
 core/tests/core.rs                    | 159 ++++++++++++--------------
 pool/src/pool.rs                      |   6 +-
 pool/src/transaction_pool.rs          |   2 +-
 pool/tests/transaction_pool.rs        |   2 +-
 servers/src/grin/dandelion_monitor.rs |   4 +-
 wallet/src/libtx/build.rs             |   6 +-
 wallet/src/libtx/slate.rs             |   2 +-
 wallet/src/libwallet/internal/tx.rs   |   2 +-
 10 files changed, 120 insertions(+), 135 deletions(-)

diff --git a/core/src/core/block.rs b/core/src/core/block.rs
index e8f811a8d..ec0dc5267 100644
--- a/core/src/core/block.rs
+++ b/core/src/core/block.rs
@@ -431,9 +431,14 @@ impl Block {
 		reward_kern: TxKernel,
 		difficulty: Difficulty,
 	) -> Result<Block, Error> {
-		// A block is just a big transaction, aggregate as such. Note that
-		// aggregate also runs validation and duplicate commitment checks.
-		let agg_tx = transaction::aggregate(txs, Some((reward_out, reward_kern)))?;
+		// A block is just a big transaction, aggregate as such.
+		// Note that aggregation also runs transaction validation
+		// and duplicate commitment checks.
+		let mut agg_tx = transaction::aggregate(txs)?;
+		// Now add the reward output and reward kernel to the aggregate tx.
+		// At this point the tx is technically invalid,
+		// but the tx body is valid if we account for the reward (i.e. as a block).
+		agg_tx = agg_tx.with_output(reward_out).with_kernel(reward_kern);
 
 		// Now add the kernel offset of the previous block for a total
 		let total_kernel_offset =
@@ -452,6 +457,9 @@ impl Block {
 		let now = Utc::now().timestamp();
 		let timestamp = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(now, 0), Utc);
 
+		// Now build the block with all the above information.
+		// Note: We have not validated the block here.
+		// Caller must validate the block as necessary.
 		Block {
 			header: BlockHeader {
 				height: prev.height + 1,
diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs
index 509dbe40b..d1cdf922d 100644
--- a/core/src/core/transaction.rs
+++ b/core/src/core/transaction.rs
@@ -613,8 +613,7 @@ impl Readable for Transaction {
 		// Treat any validation issues as data corruption.
 		// An example of this would be reading a tx
 		// that exceeded the allowed number of inputs.
-		tx.validate_read(false)
-			.map_err(|_| ser::Error::CorruptedData)?;
+		tx.validate_read().map_err(|_| ser::Error::CorruptedData)?;
 
 		Ok(tx)
 	}
@@ -749,23 +748,19 @@ impl Transaction {
 	/// * rangeproof verification (on the body)
 	/// * kernel signature verification (on the body)
 	/// * kernel sum verification
-	pub fn validate_read(&self, with_reward: bool) -> Result<(), Error> {
-		self.body.validate_read(with_reward)?;
-		if !with_reward {
-			self.body.verify_features()?;
-		}
+	pub fn validate_read(&self) -> Result<(), Error> {
+		self.body.validate_read(false)?;
+		self.body.verify_features()?;
 		Ok(())
 	}
 
 	/// Validates all relevant parts of a fully built transaction. Checks the
 	/// excess value against the signature as well as range proofs for each
 	/// output.
-	pub fn validate(&self, with_reward: bool) -> Result<(), Error> {
-		self.body.validate(with_reward)?;
-		if !with_reward {
-			self.body.verify_features()?;
-			self.verify_kernel_sums(self.overage(), self.offset)?;
-		}
+	pub fn validate(&self) -> Result<(), Error> {
+		self.body.validate(false)?;
+		self.body.verify_features()?;
+		self.verify_kernel_sums(self.overage(), self.offset)?;
 		Ok(())
 	}
 
@@ -812,16 +807,13 @@ pub fn cut_through(inputs: &mut Vec<Input>, outputs: &mut Vec<Output>) -> Result
 	Ok(())
 }
 
-/// Aggregate a vec of transactions into a multi-kernel transaction with
-/// cut_through. Optionally allows passing a reward output and kernel for
-/// block building.
-pub fn aggregate(
-	mut transactions: Vec<Transaction>,
-	reward: Option<(Output, TxKernel)>,
-) -> Result<Transaction, Error> {
+/// Aggregate a vec of txs into a multi-kernel tx with cut_through.
+pub fn aggregate(mut txs: Vec<Transaction>) -> Result<Transaction, Error> {
 	// convenience short-circuiting
-	if reward.is_none() && transactions.len() == 1 {
-		return Ok(transactions.pop().unwrap());
+	if txs.is_empty() {
+		return Ok(Transaction::empty());
+	} else if txs.len() == 1 {
+		return Ok(txs.pop().unwrap());
 	}
 
 	let mut inputs: Vec<Input> = vec![];
@@ -832,18 +824,13 @@ pub fn aggregate(
 	// transaction
 	let mut kernel_offsets: Vec<BlindingFactor> = vec![];
 
-	for mut transaction in transactions {
+	for mut tx in txs {
 		// we will sum these later to give a single aggregate offset
-		kernel_offsets.push(transaction.offset);
+		kernel_offsets.push(tx.offset);
 
-		inputs.append(&mut transaction.body.inputs);
-		outputs.append(&mut transaction.body.outputs);
-		kernels.append(&mut transaction.body.kernels);
-	}
-	let with_reward = reward.is_some();
-	if let Some((out, kernel)) = reward {
-		outputs.push(out);
-		kernels.push(kernel);
+		inputs.append(&mut tx.body.inputs);
+		outputs.append(&mut tx.body.outputs);
+		kernels.append(&mut tx.body.kernels);
 	}
 
 	// Sort inputs and outputs during cut_through.
@@ -867,7 +854,7 @@ pub fn aggregate(
 	// The resulting tx could be invalid for a variety of reasons -
 	//   * tx too large (too many inputs|outputs|kernels)
 	//   * cut-through may have invalidated the sums
-	tx.validate(with_reward)?;
+	tx.validate()?;
 
 	Ok(tx)
 }
@@ -883,7 +870,7 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec<Transaction>) -> Result<Transact
 	// transaction
 	let mut kernel_offsets = vec![];
 
-	let tx = aggregate(txs, None)?;
+	let tx = aggregate(txs)?;
 
 	for mk_input in mk_tx.body.inputs {
 		if !tx.body.inputs.contains(&mk_input) && !inputs.contains(&mk_input) {
@@ -935,8 +922,7 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec<Transaction>) -> Result<Transact
 	let tx = Transaction::new(inputs, outputs, kernels).with_offset(total_kernel_offset);
 
 	// Now validate the resulting tx to ensure we have not built something invalid.
-	tx.validate(false)?;
-
+	tx.validate()?;
 	Ok(tx)
 }
 
diff --git a/core/tests/core.rs b/core/tests/core.rs
index ca9b5eebf..e36ebd4d4 100644
--- a/core/tests/core.rs
+++ b/core/tests/core.rs
@@ -106,7 +106,7 @@ fn build_tx_kernel() {
 	).unwrap();
 
 	// check the tx is valid
-	tx.validate(false).unwrap();
+	tx.validate().unwrap();
 
 	// check the kernel is also itself valid
 	assert_eq!(tx.kernels().len(), 1);
@@ -124,13 +124,13 @@ fn transaction_cut_through() {
 	let tx1 = tx1i2o();
 	let tx2 = tx2i1o();
 
-	assert!(tx1.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
+	assert!(tx1.validate().is_ok());
+	assert!(tx2.validate().is_ok());
 
 	// now build a "cut_through" tx from tx1 and tx2
-	let tx3 = aggregate(vec![tx1, tx2], None).unwrap();
+	let tx3 = aggregate(vec![tx1, tx2]).unwrap();
 
-	assert!(tx3.validate(false).is_ok());
+	assert!(tx3.validate().is_ok());
 }
 
 // Attempt to deaggregate a multi-kernel transaction in a different way
@@ -141,29 +141,26 @@ fn multi_kernel_transaction_deaggregation() {
 	let tx3 = tx1i1o();
 	let tx4 = tx1i1o();
 
-	assert!(tx1.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
-	assert!(tx3.validate(false).is_ok());
-	assert!(tx4.validate(false).is_ok());
+	assert!(tx1.validate().is_ok());
+	assert!(tx2.validate().is_ok());
+	assert!(tx3.validate().is_ok());
+	assert!(tx4.validate().is_ok());
 
-	let tx1234 = aggregate(
-		vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
-		None,
-	).unwrap();
-	let tx12 = aggregate(vec![tx1.clone(), tx2.clone()], None).unwrap();
-	let tx34 = aggregate(vec![tx3.clone(), tx4.clone()], None).unwrap();
+	let tx1234 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()]).unwrap();
+	let tx12 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap();
+	let tx34 = aggregate(vec![tx3.clone(), tx4.clone()]).unwrap();
 
-	assert!(tx1234.validate(false).is_ok());
-	assert!(tx12.validate(false).is_ok());
-	assert!(tx34.validate(false).is_ok());
+	assert!(tx1234.validate().is_ok());
+	assert!(tx12.validate().is_ok());
+	assert!(tx34.validate().is_ok());
 
 	let deaggregated_tx34 = deaggregate(tx1234.clone(), vec![tx12.clone()]).unwrap();
-	assert!(deaggregated_tx34.validate(false).is_ok());
+	assert!(deaggregated_tx34.validate().is_ok());
 	assert_eq!(tx34, deaggregated_tx34);
 
 	let deaggregated_tx12 = deaggregate(tx1234.clone(), vec![tx34.clone()]).unwrap();
 
-	assert!(deaggregated_tx12.validate(false).is_ok());
+	assert!(deaggregated_tx12.validate().is_ok());
 	assert_eq!(tx12, deaggregated_tx12);
 }
 
@@ -173,18 +170,18 @@ fn multi_kernel_transaction_deaggregation_2() {
 	let tx2 = tx1i1o();
 	let tx3 = tx1i1o();
 
-	assert!(tx1.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
-	assert!(tx3.validate(false).is_ok());
+	assert!(tx1.validate().is_ok());
+	assert!(tx2.validate().is_ok());
+	assert!(tx3.validate().is_ok());
 
-	let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()], None).unwrap();
-	let tx12 = aggregate(vec![tx1.clone(), tx2.clone()], None).unwrap();
+	let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()]).unwrap();
+	let tx12 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap();
 
-	assert!(tx123.validate(false).is_ok());
-	assert!(tx12.validate(false).is_ok());
+	assert!(tx123.validate().is_ok());
+	assert!(tx12.validate().is_ok());
 
 	let deaggregated_tx3 = deaggregate(tx123.clone(), vec![tx12.clone()]).unwrap();
-	assert!(deaggregated_tx3.validate(false).is_ok());
+	assert!(deaggregated_tx3.validate().is_ok());
 	assert_eq!(tx3, deaggregated_tx3);
 }
 
@@ -194,19 +191,19 @@ fn multi_kernel_transaction_deaggregation_3() {
 	let tx2 = tx1i1o();
 	let tx3 = tx1i1o();
 
-	assert!(tx1.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
-	assert!(tx3.validate(false).is_ok());
+	assert!(tx1.validate().is_ok());
+	assert!(tx2.validate().is_ok());
+	assert!(tx3.validate().is_ok());
 
-	let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()], None).unwrap();
-	let tx13 = aggregate(vec![tx1.clone(), tx3.clone()], None).unwrap();
-	let tx2 = aggregate(vec![tx2.clone()], None).unwrap();
+	let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()]).unwrap();
+	let tx13 = aggregate(vec![tx1.clone(), tx3.clone()]).unwrap();
+	let tx2 = aggregate(vec![tx2.clone()]).unwrap();
 
-	assert!(tx123.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
+	assert!(tx123.validate().is_ok());
+	assert!(tx2.validate().is_ok());
 
 	let deaggregated_tx13 = deaggregate(tx123.clone(), vec![tx2.clone()]).unwrap();
-	assert!(deaggregated_tx13.validate(false).is_ok());
+	assert!(deaggregated_tx13.validate().is_ok());
 	assert_eq!(tx13, deaggregated_tx13);
 }
 
@@ -218,29 +215,26 @@ fn multi_kernel_transaction_deaggregation_4() {
 	let tx4 = tx1i1o();
 	let tx5 = tx1i1o();
 
-	assert!(tx1.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
-	assert!(tx3.validate(false).is_ok());
-	assert!(tx4.validate(false).is_ok());
-	assert!(tx5.validate(false).is_ok());
+	assert!(tx1.validate().is_ok());
+	assert!(tx2.validate().is_ok());
+	assert!(tx3.validate().is_ok());
+	assert!(tx4.validate().is_ok());
+	assert!(tx5.validate().is_ok());
 
-	let tx12345 = aggregate(
-		vec![
-			tx1.clone(),
-			tx2.clone(),
-			tx3.clone(),
-			tx4.clone(),
-			tx5.clone(),
-		],
-		None,
-	).unwrap();
-	assert!(tx12345.validate(false).is_ok());
+	let tx12345 = aggregate(vec![
+		tx1.clone(),
+		tx2.clone(),
+		tx3.clone(),
+		tx4.clone(),
+		tx5.clone(),
+	]).unwrap();
+	assert!(tx12345.validate().is_ok());
 
 	let deaggregated_tx5 = deaggregate(
 		tx12345.clone(),
 		vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
 	).unwrap();
-	assert!(deaggregated_tx5.validate(false).is_ok());
+	assert!(deaggregated_tx5.validate().is_ok());
 	assert_eq!(tx5, deaggregated_tx5);
 }
 
@@ -252,29 +246,26 @@ fn multi_kernel_transaction_deaggregation_5() {
 	let tx4 = tx1i1o();
 	let tx5 = tx1i1o();
 
-	assert!(tx1.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
-	assert!(tx3.validate(false).is_ok());
-	assert!(tx4.validate(false).is_ok());
-	assert!(tx5.validate(false).is_ok());
+	assert!(tx1.validate().is_ok());
+	assert!(tx2.validate().is_ok());
+	assert!(tx3.validate().is_ok());
+	assert!(tx4.validate().is_ok());
+	assert!(tx5.validate().is_ok());
 
-	let tx12345 = aggregate(
-		vec![
-			tx1.clone(),
-			tx2.clone(),
-			tx3.clone(),
-			tx4.clone(),
-			tx5.clone(),
-		],
-		None,
-	).unwrap();
-	let tx12 = aggregate(vec![tx1.clone(), tx2.clone()], None).unwrap();
-	let tx34 = aggregate(vec![tx3.clone(), tx4.clone()], None).unwrap();
+	let tx12345 = aggregate(vec![
+		tx1.clone(),
+		tx2.clone(),
+		tx3.clone(),
+		tx4.clone(),
+		tx5.clone(),
+	]).unwrap();
+	let tx12 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap();
+	let tx34 = aggregate(vec![tx3.clone(), tx4.clone()]).unwrap();
 
-	assert!(tx12345.validate(false).is_ok());
+	assert!(tx12345.validate().is_ok());
 
 	let deaggregated_tx5 = deaggregate(tx12345.clone(), vec![tx12.clone(), tx34.clone()]).unwrap();
-	assert!(deaggregated_tx5.validate(false).is_ok());
+	assert!(deaggregated_tx5.validate().is_ok());
 	assert_eq!(tx5, deaggregated_tx5);
 }
 
@@ -284,22 +275,22 @@ fn basic_transaction_deaggregation() {
 	let tx1 = tx1i2o();
 	let tx2 = tx2i1o();
 
-	assert!(tx1.validate(false).is_ok());
-	assert!(tx2.validate(false).is_ok());
+	assert!(tx1.validate().is_ok());
+	assert!(tx2.validate().is_ok());
 
 	// now build a "cut_through" tx from tx1 and tx2
-	let tx3 = aggregate(vec![tx1.clone(), tx2.clone()], None).unwrap();
+	let tx3 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap();
 
-	assert!(tx3.validate(false).is_ok());
+	assert!(tx3.validate().is_ok());
 
 	let deaggregated_tx1 = deaggregate(tx3.clone(), vec![tx2.clone()]).unwrap();
 
-	assert!(deaggregated_tx1.validate(false).is_ok());
+	assert!(deaggregated_tx1.validate().is_ok());
 	assert_eq!(tx1, deaggregated_tx1);
 
 	let deaggregated_tx2 = deaggregate(tx3.clone(), vec![tx1.clone()]).unwrap();
 
-	assert!(deaggregated_tx2.validate(false).is_ok());
+	assert!(deaggregated_tx2.validate().is_ok());
 	assert_eq!(tx2, deaggregated_tx2);
 }
 
@@ -329,7 +320,7 @@ fn hash_output() {
 #[test]
 fn blind_tx() {
 	let btx = tx2i1o();
-	assert!(btx.validate(false).is_ok());
+	assert!(btx.validate().is_ok());
 
 	// Ignored for bullet proofs, because calling range_proof_info
 	// with a bullet proof causes painful errors
@@ -391,7 +382,7 @@ fn tx_build_exchange() {
 		&keychain,
 	).unwrap();
 
-	tx_final.validate(false).unwrap();
+	tx_final.validate().unwrap();
 }
 
 #[test]
@@ -419,7 +410,7 @@ fn reward_with_tx_block() {
 	let zero_commit = secp_static::commit_to_zero_value();
 
 	let mut tx1 = tx2i1o();
-	tx1.validate(false).unwrap();
+	tx1.validate().unwrap();
 
 	let previous_header = BlockHeader::default();
 
@@ -505,11 +496,11 @@ fn test_block_with_timelocked_tx() {
 #[test]
 pub fn test_verify_1i1o_sig() {
 	let tx = tx1i1o();
-	tx.validate(false).unwrap();
+	tx.validate().unwrap();
 }
 
 #[test]
 pub fn test_verify_2i1o_sig() {
 	let tx = tx2i1o();
-	tx.validate(false).unwrap();
+	tx.validate().unwrap();
 }
diff --git a/pool/src/pool.rs b/pool/src/pool.rs
index f428e0d16..d306fad86 100644
--- a/pool/src/pool.rs
+++ b/pool/src/pool.rs
@@ -86,7 +86,7 @@ impl Pool {
 			.into_iter()
 			.filter_map(|mut bucket| {
 				bucket.truncate(MAX_TX_CHAIN);
-				transaction::aggregate(bucket, None).ok()
+				transaction::aggregate(bucket).ok()
 			})
 			.collect();
 
@@ -118,7 +118,7 @@ impl Pool {
 			return Ok(None);
 		}
 
-		let tx = transaction::aggregate(txs, None)?;
+		let tx = transaction::aggregate(txs)?;
 		Ok(Some(tx))
 	}
 
@@ -191,7 +191,7 @@ impl Pool {
 			// Create a single aggregated tx from the existing pool txs and the
 			// new entry
 			txs.push(entry.tx.clone());
-			transaction::aggregate(txs, None)?
+			transaction::aggregate(txs)?
 		};
 
 		// Validate aggregated tx against a known chain state (via txhashset
diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs
index 616c722fe..a870bf7e6 100644
--- a/pool/src/transaction_pool.rs
+++ b/pool/src/transaction_pool.rs
@@ -100,7 +100,7 @@ impl TransactionPool {
 		self.is_acceptable(&tx)?;
 
 		// Make sure the transaction is valid before anything else.
-		tx.validate(false).map_err(|e| PoolError::InvalidTx(e))?;
+		tx.validate().map_err(|e| PoolError::InvalidTx(e))?;
 
 		// Check the tx lock_time is valid based on current chain state.
 		self.blockchain.verify_tx_lock_height(&tx)?;
diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs
index 510ae2106..d4a93258c 100644
--- a/pool/tests/transaction_pool.rs
+++ b/pool/tests/transaction_pool.rs
@@ -219,7 +219,7 @@ fn test_the_transaction_pool() {
 		let tx4 = test_transaction(&keychain, vec![800], vec![799]);
 		// tx1 and tx2 are already in the txpool (in aggregated form)
 		// tx4 is the "new" part of this aggregated tx that we care about
-		let agg_tx = transaction::aggregate(vec![tx1.clone(), tx2.clone(), tx4], None).unwrap();
+		let agg_tx = transaction::aggregate(vec![tx1.clone(), tx2.clone(), tx4]).unwrap();
 		write_pool
 			.add_to_pool(test_source(), agg_tx, false, &header.hash())
 			.unwrap();
diff --git a/servers/src/grin/dandelion_monitor.rs b/servers/src/grin/dandelion_monitor.rs
index 41809c374..541feed6b 100644
--- a/servers/src/grin/dandelion_monitor.rs
+++ b/servers/src/grin/dandelion_monitor.rs
@@ -102,7 +102,7 @@ fn process_stem_phase(tx_pool: Arc<RwLock<TransactionPool>>) -> Result<(), PoolE
 			stem_txs.len()
 		);
 
-		let agg_tx = transaction::aggregate(stem_txs, None)?;
+		let agg_tx = transaction::aggregate(stem_txs)?;
 
 		let res = tx_pool.adapter.stem_tx_accepted(&agg_tx);
 		if res.is_err() {
@@ -142,7 +142,7 @@ fn process_fluff_phase(tx_pool: Arc<RwLock<TransactionPool>>) -> Result<(), Pool
 			stem_txs.len()
 		);
 
-		let agg_tx = transaction::aggregate(stem_txs, None)?;
+		let agg_tx = transaction::aggregate(stem_txs)?;
 
 		let src = TxSource {
 			debug_name: "fluff".to_string(),
diff --git a/wallet/src/libtx/build.rs b/wallet/src/libtx/build.rs
index b92f5a218..0e2c84b6a 100644
--- a/wallet/src/libtx/build.rs
+++ b/wallet/src/libtx/build.rs
@@ -299,7 +299,7 @@ mod test {
 			&keychain,
 		).unwrap();
 
-		tx.validate(false).unwrap();
+		tx.validate().unwrap();
 	}
 
 	#[test]
@@ -319,7 +319,7 @@ mod test {
 			&keychain,
 		).unwrap();
 
-		tx.validate(false).unwrap();
+		tx.validate().unwrap();
 	}
 
 	#[test]
@@ -333,6 +333,6 @@ mod test {
 			&keychain,
 		).unwrap();
 
-		tx.validate(false).unwrap();
+		tx.validate().unwrap();
 	}
 }
diff --git a/wallet/src/libtx/slate.rs b/wallet/src/libtx/slate.rs
index 724807195..f1e54c646 100644
--- a/wallet/src/libtx/slate.rs
+++ b/wallet/src/libtx/slate.rs
@@ -398,7 +398,7 @@ impl Slate {
 		final_tx.kernels()[0].verify()?;
 
 		// confirm the overall transaction is valid (including the updated kernel)
-		let _ = final_tx.validate(false)?;
+		let _ = final_tx.validate()?;
 
 		self.tx = final_tx;
 		Ok(())
diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs
index 9f612bd6e..a721a7969 100644
--- a/wallet/src/libwallet/internal/tx.rs
+++ b/wallet/src/libwallet/internal/tx.rs
@@ -196,7 +196,7 @@ where
 
 	// finalize the burn transaction and send
 	let tx_burn = build::transaction(parts, &keychain)?;
-	tx_burn.validate(false)?;
+	tx_burn.validate()?;
 	Ok(tx_burn)
 }