diff --git a/api/src/handlers.rs b/api/src/handlers.rs
index b9ad55f55..f87ebe5f9 100644
--- a/api/src/handlers.rs
+++ b/api/src/handlers.rs
@@ -134,7 +134,7 @@ impl OutputHandler {
 		// in the period between accepting the block and refreshing the wallet
 		if let Ok(block) = w(&self.chain).get_block(&header.hash()) {
 			let outputs = block
-				.outputs
+				.outputs()
 				.iter()
 				.filter(|output| commitments.is_empty() || commitments.contains(&output.commit))
 				.map(|output| {
@@ -226,7 +226,8 @@ impl OutputHandler {
 
 impl Handler for OutputHandler {
 	fn get(&self, req: Request<Body>) -> ResponseFuture {
-		match req.uri()
+		match req
+			.uri()
 			.path()
 			.trim_right_matches("/")
 			.rsplit("/")
@@ -362,7 +363,8 @@ impl Handler for TxHashSetHandler {
 				}
 			}
 		}
-		match req.uri()
+		match req
+			.uri()
 			.path()
 			.trim_right()
 			.trim_right_matches("/")
@@ -418,7 +420,8 @@ pub struct PeerHandler {
 
 impl Handler for PeerHandler {
 	fn get(&self, req: Request<Body>) -> ResponseFuture {
-		if let Ok(addr) = req.uri()
+		if let Ok(addr) = req
+			.uri()
 			.path()
 			.trim_right_matches("/")
 			.rsplit("/")
@@ -604,7 +607,8 @@ fn check_block_param(input: &String) -> Result<(), Error> {
 
 impl Handler for BlockHandler {
 	fn get(&self, req: Request<Body>) -> ResponseFuture {
-		let el = req.uri()
+		let el = req
+			.uri()
 			.path()
 			.trim_right_matches("/")
 			.rsplit("/")
@@ -649,7 +653,8 @@ impl Handler for BlockHandler {
 
 impl Handler for HeaderHandler {
 	fn get(&self, req: Request<Body>) -> ResponseFuture {
-		let el = req.uri()
+		let el = req
+			.uri()
 			.path()
 			.trim_right_matches("/")
 			.rsplit("/")
@@ -736,8 +741,8 @@ where
 					info!(
 						LOGGER,
 						"Pushing transaction with {} inputs and {} outputs to pool.",
-						tx.inputs.len(),
-						tx.outputs.len()
+						tx.inputs().len(),
+						tx.outputs().len()
 					);
 
 					//  Push to tx pool.
diff --git a/api/src/types.rs b/api/src/types.rs
index dcd768c8d..fe006e8dd 100644
--- a/api/src/types.rs
+++ b/api/src/types.rs
@@ -292,7 +292,8 @@ impl OutputPrintable {
 	}
 
 	pub fn range_proof(&self) -> Result<pedersen::RangeProof, ser::Error> {
-		let proof_str = self.proof
+		let proof_str = self
+			.proof
 			.clone()
 			.ok_or_else(|| ser::Error::HexError(format!("output range_proof missing")))
 			.unwrap();
@@ -531,12 +532,12 @@ impl BlockPrintable {
 		include_proof: bool,
 	) -> BlockPrintable {
 		let inputs = block
-			.inputs
+			.inputs()
 			.iter()
 			.map(|x| util::to_hex(x.commitment().0.to_vec()))
 			.collect();
 		let outputs = block
-			.outputs
+			.outputs()
 			.iter()
 			.map(|output| {
 				OutputPrintable::from_output(
@@ -548,7 +549,7 @@ impl BlockPrintable {
 			})
 			.collect();
 		let kernels = block
-			.kernels
+			.kernels()
 			.iter()
 			.map(|kernel| TxKernelPrintable::from_txkernel(kernel))
 			.collect();
@@ -581,11 +582,13 @@ impl CompactBlockPrintable {
 		chain: Arc<chain::Chain>,
 	) -> CompactBlockPrintable {
 		let block = chain.get_block(&cb.hash()).unwrap();
-		let out_full = cb.out_full
+		let out_full = cb
+			.out_full
 			.iter()
 			.map(|x| OutputPrintable::from_output(x, chain.clone(), Some(&block.header), false))
 			.collect();
-		let kern_full = cb.kern_full
+		let kern_full = cb
+			.kern_full
 			.iter()
 			.map(|x| TxKernelPrintable::from_txkernel(x))
 			.collect();
diff --git a/chain/src/chain.rs b/chain/src/chain.rs
index 6120b1ab2..ce90523fc 100644
--- a/chain/src/chain.rs
+++ b/chain/src/chain.rs
@@ -421,7 +421,7 @@ impl Chain {
 		let height = self.next_block_height()?;
 		let mut txhashset = self.txhashset.write().unwrap();
 		txhashset::extending_readonly(&mut txhashset, |extension| {
-			extension.verify_coinbase_maturity(&tx.inputs, height)?;
+			extension.verify_coinbase_maturity(&tx.inputs(), height)?;
 			Ok(())
 		})
 	}
diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs
index 35687ed36..5caea1379 100644
--- a/chain/src/pipe.rs
+++ b/chain/src/pipe.rs
@@ -66,9 +66,9 @@ pub fn process_block(b: &Block, ctx: &mut BlockContext) -> Result<Option<Tip>, E
 		"pipe: process_block {} at {} with {} inputs, {} outputs, {} kernels",
 		b.hash(),
 		b.header.height,
-		b.inputs.len(),
-		b.outputs.len(),
-		b.kernels.len(),
+		b.inputs().len(),
+		b.outputs().len(),
+		b.kernels().len(),
 	);
 	check_known(b.hash(), ctx)?;
 
@@ -328,7 +328,7 @@ fn validate_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
 fn validate_block_via_txhashset(b: &Block, ext: &mut txhashset::Extension) -> Result<(), Error> {
 	// First check we are not attempting to spend any coinbase outputs
 	// before they have matured sufficiently.
-	ext.verify_coinbase_maturity(&b.inputs, b.header.height)?;
+	ext.verify_coinbase_maturity(&b.inputs(), b.header.height)?;
 
 	// apply the new block to the MMR trees and check the new root hashes
 	ext.apply_block(&b)?;
diff --git a/chain/src/store.rs b/chain/src/store.rs
index 04657c7ad..b410708e5 100644
--- a/chain/src/store.rs
+++ b/chain/src/store.rs
@@ -157,7 +157,7 @@ impl ChainStore {
 
 	pub fn build_block_input_bitmap(&self, block: &Block) -> Result<Bitmap, Error> {
 		let bitmap = block
-			.inputs
+			.inputs()
 			.iter()
 			.filter_map(|x| self.get_output_pos(&x.commitment()).ok())
 			.map(|x| x as u32)
diff --git a/chain/src/txhashset.rs b/chain/src/txhashset.rs
index 517491808..2425bf1ef 100644
--- a/chain/src/txhashset.rs
+++ b/chain/src/txhashset.rs
@@ -474,27 +474,27 @@ impl<'a> Extension<'a> {
 
 		// Build bitmap of output pos spent (as inputs) by this tx for rewind.
 		let rewind_rm_pos = tx
-			.inputs
+			.inputs()
 			.iter()
 			.filter_map(|x| self.get_output_pos(&x.commitment()).ok())
 			.map(|x| x as u32)
 			.collect();
 
-		for ref output in &tx.outputs {
+		for ref output in tx.outputs() {
 			if let Err(e) = self.apply_output(output) {
 				self.rewind_raw_tx(output_pos, kernel_pos, &rewind_rm_pos)?;
 				return Err(e);
 			}
 		}
 
-		for ref input in &tx.inputs {
+		for ref input in tx.inputs() {
 			if let Err(e) = self.apply_input(input) {
 				self.rewind_raw_tx(output_pos, kernel_pos, &rewind_rm_pos)?;
 				return Err(e);
 			}
 		}
 
-		for ref kernel in &tx.kernels {
+		for ref kernel in tx.kernels() {
 			if let Err(e) = self.apply_kernel(kernel) {
 				self.rewind_raw_tx(output_pos, kernel_pos, &rewind_rm_pos)?;
 				return Err(e);
@@ -591,15 +591,15 @@ impl<'a> Extension<'a> {
 		// A block is not valid if it has not been fully cut-through.
 		// So we can safely apply outputs first (we will not spend these in the same
 		// block).
-		for out in &b.outputs {
+		for out in b.outputs() {
 			self.apply_output(out)?;
 		}
 
-		for input in &b.inputs {
+		for input in b.inputs() {
 			self.apply_input(input)?;
 		}
 
-		for kernel in &b.kernels {
+		for kernel in b.kernels() {
 			self.apply_kernel(kernel)?;
 		}
 
diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs
index 0bd4a84df..a0b153dc0 100644
--- a/chain/tests/mine_simple_chain.rs
+++ b/chain/tests/mine_simple_chain.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+extern crate chrono;
 extern crate grin_chain as chain;
 extern crate grin_core as core;
 extern crate grin_keychain as keychain;
@@ -19,14 +20,13 @@ extern crate grin_store as store;
 extern crate grin_util as util;
 extern crate grin_wallet as wallet;
 extern crate rand;
-extern crate chrono;
 
+use chrono::Duration;
 use std::fs;
 use std::sync::Arc;
-use chrono::Duration;
 
-use chain::Chain;
 use chain::types::NoopAdapter;
+use chain::Chain;
 use core::core::hash::Hashed;
 use core::core::target::Difficulty;
 use core::core::{Block, BlockHeader, OutputFeatures, OutputIdentifier, Transaction};
@@ -74,12 +74,7 @@ fn mine_empty_chain() {
 			global::min_sizeshift()
 		};
 		b.header.pow.cuckoo_sizeshift = sizeshift;
-		pow::pow_size(
-			&mut b.header,
-			difficulty,
-			global::proofsize(),
-			sizeshift,
-		).unwrap();
+		pow::pow_size(&mut b.header, difficulty, global::proofsize(), sizeshift).unwrap();
 		b.header.pow.cuckoo_sizeshift = sizeshift;
 
 		let bhash = b.hash();
@@ -99,7 +94,7 @@ fn mine_empty_chain() {
 		let block = chain.get_block(&header.hash()).unwrap();
 		assert_eq!(block.header.height, n);
 		assert_eq!(block.hash(), bhash);
-		assert_eq!(block.outputs.len(), 1);
+		assert_eq!(block.outputs().len(), 1);
 
 		// now check the block height index
 		let header_by_height = chain.get_header_by_height(n).unwrap();
@@ -248,7 +243,7 @@ fn spend_in_fork_and_compact() {
 	// so we can spend the coinbase later
 	let b = prepare_block(&kc, &fork_head, &chain, 2);
 	let block_hash = b.hash();
-	let out_id = OutputIdentifier::from_output(&b.outputs[0]);
+	let out_id = OutputIdentifier::from_output(&b.outputs()[0]);
 	assert!(out_id.features.contains(OutputFeatures::COINBASE_OUTPUT));
 	fork_head = b.header.clone();
 	chain
@@ -269,10 +264,7 @@ fn spend_in_fork_and_compact() {
 
 	let tx1 = build::transaction(
 		vec![
-			build::coinbase_input(
-				consensus::REWARD,
-				kc.derive_key_id(2).unwrap(),
-			),
+			build::coinbase_input(consensus::REWARD, kc.derive_key_id(2).unwrap()),
 			build::output(consensus::REWARD - 20000, kc.derive_key_id(30).unwrap()),
 			build::with_fee(20000),
 		],
@@ -321,12 +313,12 @@ fn spend_in_fork_and_compact() {
 	assert_eq!(head.hash(), prev_main.hash());
 	assert!(
 		chain
-			.is_unspent(&OutputIdentifier::from_output(&tx2.outputs[0]))
+			.is_unspent(&OutputIdentifier::from_output(&tx2.outputs()[0]))
 			.is_ok()
 	);
 	assert!(
 		chain
-			.is_unspent(&OutputIdentifier::from_output(&tx1.outputs[0]))
+			.is_unspent(&OutputIdentifier::from_output(&tx1.outputs()[0]))
 			.is_err()
 	);
 
@@ -344,12 +336,12 @@ fn spend_in_fork_and_compact() {
 	assert_eq!(head.hash(), prev_fork.hash());
 	assert!(
 		chain
-			.is_unspent(&OutputIdentifier::from_output(&tx2.outputs[0]))
+			.is_unspent(&OutputIdentifier::from_output(&tx2.outputs()[0]))
 			.is_ok()
 	);
 	assert!(
 		chain
-			.is_unspent(&OutputIdentifier::from_output(&tx1.outputs[0]))
+			.is_unspent(&OutputIdentifier::from_output(&tx1.outputs()[0]))
 			.is_err()
 	);
 
diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs
index e82ec5bea..beef0716f 100644
--- a/chain/tests/test_coinbase_maturity.rs
+++ b/chain/tests/test_coinbase_maturity.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+extern crate chrono;
 extern crate env_logger;
 extern crate grin_chain as chain;
 extern crate grin_core as core;
@@ -19,11 +20,10 @@ extern crate grin_keychain as keychain;
 extern crate grin_store as store;
 extern crate grin_wallet as wallet;
 extern crate rand;
-extern crate chrono;
 
+use chrono::Duration;
 use std::fs;
 use std::sync::Arc;
-use chrono::Duration;
 
 use chain::types::NoopAdapter;
 use chain::{Error, ErrorKind};
@@ -78,8 +78,8 @@ fn test_coinbase_maturity() {
 		global::min_sizeshift(),
 	).unwrap();
 
-	assert_eq!(block.outputs.len(), 1);
-	let coinbase_output = block.outputs[0];
+	assert_eq!(block.outputs().len(), 1);
+	let coinbase_output = block.outputs()[0];
 	assert!(
 		coinbase_output
 			.features
diff --git a/core/src/core/block.rs b/core/src/core/block.rs
index 59a85e629..d325e7e79 100644
--- a/core/src/core/block.rs
+++ b/core/src/core/block.rs
@@ -21,14 +21,14 @@ use std::collections::HashSet;
 use std::fmt;
 use std::iter::FromIterator;
 
-use consensus::{self, reward, VerifySortOrder, REWARD};
+use consensus::{self, reward, REWARD};
 use core::committed::{self, Committed};
 use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH};
 use core::id::ShortIdentifiable;
 use core::target::Difficulty;
 use core::{
 	transaction, Commitment, Input, KernelFeatures, Output, OutputFeatures, Proof, ShortId,
-	Transaction, TxKernel,
+	Transaction, TransactionBody, TxKernel,
 };
 use global;
 use keychain::{self, BlindingFactor};
@@ -350,13 +350,8 @@ impl Readable for CompactBlock {
 pub struct Block {
 	/// The header with metadata and commitments to the rest of the data
 	pub header: BlockHeader,
-	/// List of transaction inputs
-	pub inputs: Vec<Input>,
-	/// List of transaction outputs
-	pub outputs: Vec<Output>,
-	/// List of kernels with associated proofs (note these are offset from
-	/// tx_kernels)
-	pub kernels: Vec<TxKernel>,
+	/// The body - inputs/outputs/kernels
+	body: TransactionBody,
 }
 
 /// Implementation of Writeable for a block, defines how to write the block to a
@@ -367,22 +362,7 @@ impl Writeable for Block {
 		self.header.write(writer)?;
 
 		if writer.serialization_mode() != ser::SerializationMode::Hash {
-			ser_multiwrite!(
-				writer,
-				[write_u64, self.inputs.len() as u64],
-				[write_u64, self.outputs.len() as u64],
-				[write_u64, self.kernels.len() as u64]
-			);
-
-			let mut inputs = self.inputs.clone();
-			let mut outputs = self.outputs.clone();
-			let mut kernels = self.kernels.clone();
-
-			// Consensus rule that everything is sorted in lexicographical order on the
-			// wire.
-			inputs.write_sorted(writer)?;
-			outputs.write_sorted(writer)?;
-			kernels.write_sorted(writer)?;
+			self.body.write(writer)?;
 		}
 		Ok(())
 	}
@@ -394,25 +374,11 @@ impl Readable for Block {
 	fn read(reader: &mut Reader) -> Result<Block, ser::Error> {
 		let header = BlockHeader::read(reader)?;
 
-		let (input_len, output_len, kernel_len) =
-			ser_multiread!(reader, read_u64, read_u64, read_u64);
-
-		let inputs = read_and_verify_sorted(reader, input_len)?;
-		let outputs = read_and_verify_sorted(reader, output_len)?;
-		let kernels = read_and_verify_sorted(reader, kernel_len)?;
-
-		// TODO - we do not verify the input|output|kernel counts here.
-		// I think should call block.validate() as part of a call to read()
-		// but block.validate() as it stands currently requires the previous sums etc.
-		// So there is no easy way to do this in isolation.
-		// Maybe we need two variations of validate() where one handles the validation
-		// rules that *can* be done in isolation.
-
+		let body = TransactionBody::read(reader)?;
+		body.validate(true).map_err(|_| ser::Error::CorruptedData)?;
 		Ok(Block {
 			header: header,
-			inputs: inputs,
-			outputs: outputs,
-			kernels: kernels,
+			body: body,
 		})
 	}
 }
@@ -421,15 +387,15 @@ impl Readable for Block {
 /// Pedersen commitment.
 impl Committed for Block {
 	fn inputs_committed(&self) -> Vec<Commitment> {
-		self.inputs.iter().map(|x| x.commitment()).collect()
+		self.body.inputs_committed()
 	}
 
 	fn outputs_committed(&self) -> Vec<Commitment> {
-		self.outputs.iter().map(|x| x.commitment()).collect()
+		self.body.outputs_committed()
 	}
 
 	fn kernels_committed(&self) -> Vec<Commitment> {
-		self.kernels.iter().map(|x| x.excess()).collect()
+		self.body.kernels_committed()
 	}
 }
 
@@ -438,9 +404,7 @@ impl Default for Block {
 	fn default() -> Block {
 		Block {
 			header: Default::default(),
-			inputs: vec![],
-			outputs: vec![],
-			kernels: vec![],
+			body: Default::default(),
 		}
 	}
 }
@@ -488,9 +452,10 @@ impl Block {
 
 		// collect all the inputs, outputs and kernels from the txs
 		for tx in txs {
-			all_inputs.extend(tx.inputs);
-			all_outputs.extend(tx.outputs);
-			all_kernels.extend(tx.kernels);
+			let tb: TransactionBody = tx.into();
+			all_inputs.extend(tb.inputs);
+			all_outputs.extend(tb.outputs);
+			all_kernels.extend(tb.kernels);
 		}
 
 		// include the coinbase output(s) and kernel(s) from the compact_block
@@ -512,9 +477,7 @@ impl Block {
 		// leave it to the caller to actually validate the block
 		Block {
 			header: cb.header,
-			inputs: all_inputs,
-			outputs: all_outputs,
-			kernels: all_kernels,
+			body: TransactionBody::new(all_inputs, all_outputs, all_kernels),
 		}.cut_through()
 	}
 
@@ -524,6 +487,7 @@ impl Block {
 		let nonce = thread_rng().next_u64();
 
 		let mut out_full = self
+			.body
 			.outputs
 			.iter()
 			.filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
@@ -533,7 +497,7 @@ impl Block {
 		let mut kern_full = vec![];
 		let mut kern_ids = vec![];
 
-		for k in &self.kernels {
+		for k in self.kernels() {
 			if k.features.contains(KernelFeatures::COINBASE_KERNEL) {
 				kern_full.push(k.clone());
 			} else {
@@ -555,6 +519,14 @@ impl Block {
 		}
 	}
 
+	/// Build a new empty block from a specified header
+	pub fn with_header(header: BlockHeader) -> Block {
+		Block {
+			header: header,
+			..Default::default()
+		}
+	}
+
 	/// Builds a new block ready to mine from the header of the previous block,
 	/// a vector of transactions and the reward information. Checks
 	/// that all transactions are valid and calculates the Merkle tree.
@@ -577,7 +549,7 @@ impl Block {
 			let zero_commit = secp_static::commit_to_zero_value();
 			let secp = static_secp_instance();
 			let secp = secp.lock().unwrap();
-			let mut excesses = map_vec!(agg_tx.kernels, |x| x.excess());
+			let mut excesses = map_vec!(agg_tx.kernels(), |x| x.excess());
 			excesses.push(prev.total_kernel_sum);
 			excesses.retain(|x| *x != zero_commit);
 			secp.commit_sum(excesses, vec![])?
@@ -593,12 +565,40 @@ impl Block {
 				total_kernel_sum,
 				..Default::default()
 			},
-			inputs: agg_tx.inputs,
-			outputs: agg_tx.outputs,
-			kernels: agg_tx.kernels,
+			body: agg_tx.into(),
 		}.cut_through())
 	}
 
+	/// Get inputs
+	pub fn inputs(&self) -> &Vec<Input> {
+		&self.body.inputs
+	}
+
+	/// Get inputs mutable
+	pub fn inputs_mut(&mut self) -> &mut Vec<Input> {
+		&mut self.body.inputs
+	}
+
+	/// Get outputs
+	pub fn outputs(&self) -> &Vec<Output> {
+		&self.body.outputs
+	}
+
+	/// Get outputs mutable
+	pub fn outputs_mut(&mut self) -> &mut Vec<Output> {
+		&mut self.body.outputs
+	}
+
+	/// Get kernels
+	pub fn kernels(&self) -> &Vec<TxKernel> {
+		&self.body.kernels
+	}
+
+	/// Get kernels mut
+	pub fn kernels_mut(&mut self) -> &mut Vec<TxKernel> {
+		&mut self.body.kernels
+	}
+
 	/// Blockhash, computed using only the POW
 	pub fn hash(&self) -> Hash {
 		self.header.hash()
@@ -606,7 +606,7 @@ impl Block {
 
 	/// Sum of all fees (inputs less outputs) in the block
 	pub fn total_fees(&self) -> u64 {
-		self.kernels.iter().map(|p| p.fee).sum()
+		self.body.kernels.iter().map(|p| p.fee).sum()
 	}
 
 	/// Matches any output with a potential spending input, eliminating them
@@ -621,12 +621,14 @@ impl Block {
 	///
 	pub fn cut_through(self) -> Block {
 		let in_set = self
+			.body
 			.inputs
 			.iter()
 			.map(|inp| inp.commitment())
 			.collect::<HashSet<_>>();
 
 		let out_set = self
+			.body
 			.outputs
 			.iter()
 			.filter(|out| !out.features.contains(OutputFeatures::COINBASE_OUTPUT))
@@ -636,12 +638,14 @@ impl Block {
 		let to_cut_through = in_set.intersection(&out_set).collect::<HashSet<_>>();
 
 		let new_inputs = self
+			.body
 			.inputs
 			.into_iter()
 			.filter(|inp| !to_cut_through.contains(&inp.commitment()))
 			.collect::<Vec<_>>();
 
 		let new_outputs = self
+			.body
 			.outputs
 			.into_iter()
 			.filter(|out| !to_cut_through.contains(&out.commitment()))
@@ -653,9 +657,7 @@ impl Block {
 				total_difficulty: self.header.total_difficulty,
 				..self.header
 			},
-			inputs: new_inputs,
-			outputs: new_outputs,
-			kernels: self.kernels,
+			body: TransactionBody::new(new_inputs, new_outputs, self.body.kernels),
 		}
 	}
 
@@ -667,17 +669,13 @@ impl Block {
 		prev_kernel_offset: &BlindingFactor,
 		prev_kernel_sum: &Commitment,
 	) -> Result<(Commitment), Error> {
-		// Verify we do not exceed the max number of inputs|outputs|kernels
-		// and that the "weight" based on these does not exceed the max permitted weight.
-		self.verify_weight()?;
+		self.body.validate(true)?;
 
-		self.verify_sorted()?;
-		self.verify_cut_through()?;
-		self.verify_coinbase()?;
 		self.verify_kernel_lock_heights()?;
+		self.verify_coinbase()?;
 
 		// take the kernel offset for this block (block offset minus previous) and
-		// verify outputs and kernel sums
+		// verify.body.outputs and kernel sums
 		let block_kernel_offset = if self.header.total_kernel_offset() == prev_kernel_offset.clone()
 		{
 			// special case when the sum hasn't changed (typically an empty block),
@@ -698,85 +696,22 @@ impl Block {
 			return Err(Error::InvalidTotalKernelSum);
 		}
 
-		self.verify_rangeproofs()?;
-		self.verify_kernel_signatures()?;
 		Ok(kernel_sum)
 	}
 
-	// Verify the block is not too big in terms of number of inputs|outputs|kernels.
-	fn verify_weight(&self) -> Result<(), Error> {
-		let tx_block_weight = self.inputs.len() * consensus::BLOCK_INPUT_WEIGHT
-			+ self.outputs.len() * consensus::BLOCK_OUTPUT_WEIGHT
-			+ self.kernels.len() * consensus::BLOCK_KERNEL_WEIGHT;
-
-		if tx_block_weight > consensus::MAX_BLOCK_WEIGHT {
-			return Err(Error::TooHeavy);
-		}
-		Ok(())
-	}
-
-	// Verify that inputs|outputs|kernels are all sorted in lexicographical order.
-	fn verify_sorted(&self) -> Result<(), Error> {
-		self.inputs.verify_sort_order()?;
-		self.outputs.verify_sort_order()?;
-		self.kernels.verify_sort_order()?;
-		Ok(())
-	}
-
-	// Verify that no input is spending an output 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(())
-	}
-
-	fn verify_kernel_lock_heights(&self) -> Result<(), Error> {
-		for k in &self.kernels {
-			// check we have no kernels with lock_heights greater than current height
-			// no tx can be included in a block earlier than its lock_height
-			if k.lock_height > self.header.height {
-				return Err(Error::KernelLockHeight(k.lock_height));
-			}
-		}
-		Ok(())
-	}
-
-	/// Verify the kernel signatures.
-	/// Note: this is expensive.
-	fn verify_kernel_signatures(&self) -> Result<(), Error> {
-		for x in &self.kernels {
-			x.verify()?;
-		}
-		Ok(())
-	}
-
-	/// Verify all the output rangeproofs.
-	/// Note: this is expensive.
-	fn verify_rangeproofs(&self) -> Result<(), Error> {
-		for x in &self.outputs {
-			x.verify_proof()?;
-		}
-		Ok(())
-	}
-
-	/// Validate the coinbase outputs generated by miners.
+	/// Validate the coinbase.body.outputs generated by miners.
 	/// Check the sum of coinbase-marked outputs match
 	/// the sum of coinbase-marked kernels accounting for fees.
 	pub fn verify_coinbase(&self) -> Result<(), Error> {
 		let cb_outs = self
+			.body
 			.outputs
 			.iter()
 			.filter(|out| out.features.contains(OutputFeatures::COINBASE_OUTPUT))
 			.collect::<Vec<&Output>>();
 
 		let cb_kerns = self
+			.body
 			.kernels
 			.iter()
 			.filter(|kernel| kernel.features.contains(KernelFeatures::COINBASE_KERNEL))
@@ -802,4 +737,15 @@ impl Block {
 
 		Ok(())
 	}
+
+	fn verify_kernel_lock_heights(&self) -> Result<(), Error> {
+		for k in &self.body.kernels {
+			// check we have no kernels with lock_heights greater than current height
+			// no tx can be included in a block earlier than its lock_height
+			if k.lock_height > self.header.height {
+				return Err(Error::KernelLockHeight(k.lock_height));
+			}
+		}
+		Ok(())
+	}
 }
diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs
index eec26e55e..29f5aa58b 100644
--- a/core/src/core/transaction.rs
+++ b/core/src/core/transaction.rs
@@ -43,7 +43,7 @@ bitflags! {
 	}
 }
 
-/// Errors thrown by Block validation
+/// Errors thrown by Transaction validation
 #[derive(Clone, Eq, Debug, PartialEq)]
 pub enum Error {
 	/// Underlying Secp256k1 error (signature validation or invalid public key
@@ -240,35 +240,28 @@ impl PMMRable for TxKernel {
 	}
 }
 
-/// A transaction
+/// TransactionBody is acommon abstraction for transaction and block
 #[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct Transaction {
+pub struct TransactionBody {
 	/// List of inputs spent by the transaction.
 	pub inputs: Vec<Input>,
 	/// List of outputs the transaction produces.
 	pub outputs: Vec<Output>,
 	/// List of kernels that make up this transaction (usually a single kernel).
 	pub kernels: Vec<TxKernel>,
-	/// The kernel "offset" k2
-	/// excess is k1G after splitting the key k = k1 + k2
-	pub offset: BlindingFactor,
 }
 
 /// PartialEq
-impl PartialEq for Transaction {
-	fn eq(&self, tx: &Transaction) -> bool {
-		self.inputs == tx.inputs
-			&& self.outputs == tx.outputs
-			&& self.kernels == tx.kernels
-			&& self.offset == tx.offset
+impl PartialEq for TransactionBody {
+	fn eq(&self, l: &TransactionBody) -> bool {
+		self.inputs == l.inputs && self.outputs == l.outputs && self.kernels == l.kernels
 	}
 }
 
-/// Implementation of Writeable for a fully blinded transaction, defines how to
-/// write the transaction as binary.
-impl Writeable for Transaction {
+/// Implementation of Writeable for a body, defines how to
+/// write the body as binary.
+impl Writeable for TransactionBody {
 	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
-		self.offset.write(writer)?;
 		ser_multiwrite!(
 			writer,
 			[write_u64, self.inputs.len() as u64],
@@ -290,12 +283,10 @@ impl Writeable for Transaction {
 	}
 }
 
-/// Implementation of Readable for a transaction, defines how to read a full
-/// transaction from a binary stream.
-impl Readable for Transaction {
-	fn read(reader: &mut Reader) -> Result<Transaction, ser::Error> {
-		let offset = BlindingFactor::read(reader)?;
-
+/// Implementation of Readable for a body, defines how to read a
+/// body from a binary stream.
+impl Readable for TransactionBody {
+	fn read(reader: &mut Reader) -> Result<TransactionBody, ser::Error> {
 		let (input_len, output_len, kernel_len) =
 			ser_multiread!(reader, read_u64, read_u64, read_u64);
 
@@ -303,24 +294,17 @@ impl Readable for Transaction {
 		let outputs = read_and_verify_sorted(reader, output_len)?;
 		let kernels = read_and_verify_sorted(reader, kernel_len)?;
 
-		let tx = Transaction {
-			offset,
+		let body = TransactionBody {
 			inputs,
 			outputs,
 			kernels,
 		};
 
-		// Now validate the tx.
-		// 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(false).map_err(|_| ser::Error::CorruptedData)?;
-
-		Ok(tx)
+		Ok(body)
 	}
 }
 
-impl Committed for Transaction {
+impl Committed for TransactionBody {
 	fn inputs_committed(&self) -> Vec<Commitment> {
 		self.inputs.iter().map(|x| x.commitment()).collect()
 	}
@@ -334,17 +318,16 @@ impl Committed for Transaction {
 	}
 }
 
-impl Default for Transaction {
-	fn default() -> Transaction {
-		Transaction::empty()
+impl Default for TransactionBody {
+	fn default() -> TransactionBody {
+		TransactionBody::empty()
 	}
 }
 
-impl Transaction {
+impl TransactionBody {
 	/// Creates a new empty transaction (no inputs or outputs, zero fee).
-	pub fn empty() -> Transaction {
-		Transaction {
-			offset: BlindingFactor::zero(),
+	pub fn empty() -> TransactionBody {
+		TransactionBody {
 			inputs: vec![],
 			outputs: vec![],
 			kernels: vec![],
@@ -353,49 +336,43 @@ impl Transaction {
 
 	/// Creates a new transaction initialized with
 	/// the provided inputs, outputs, kernels
-	pub fn new(inputs: Vec<Input>, outputs: Vec<Output>, kernels: Vec<TxKernel>) -> Transaction {
-		Transaction {
-			offset: BlindingFactor::zero(),
+	pub fn new(
+		inputs: Vec<Input>,
+		outputs: Vec<Output>,
+		kernels: Vec<TxKernel>,
+	) -> TransactionBody {
+		TransactionBody {
 			inputs: inputs,
 			outputs: outputs,
 			kernels: kernels,
 		}
 	}
 
-	/// Creates a new transaction using this transaction as a template
-	/// and with the specified offset.
-	pub fn with_offset(self, offset: BlindingFactor) -> Transaction {
-		Transaction {
-			offset: offset,
-			..self
-		}
-	}
-
-	/// Builds a new transaction with the provided inputs added. Existing
+	/// Builds a new body with the provided inputs added. Existing
 	/// inputs, if any, are kept intact.
-	pub fn with_input(self, input: Input) -> Transaction {
+	pub fn with_input(self, input: Input) -> TransactionBody {
 		let mut new_ins = self.inputs;
 		new_ins.push(input);
 		new_ins.sort();
-		Transaction {
+		TransactionBody {
 			inputs: new_ins,
 			..self
 		}
 	}
 
-	/// Builds a new transaction with the provided output added. Existing
+	/// Builds a new TransactionBody with the provided output added. Existing
 	/// outputs, if any, are kept intact.
-	pub fn with_output(self, output: Output) -> Transaction {
+	pub fn with_output(self, output: Output) -> TransactionBody {
 		let mut new_outs = self.outputs;
 		new_outs.push(output);
 		new_outs.sort();
-		Transaction {
+		TransactionBody {
 			outputs: new_outs,
 			..self
 		}
 	}
 
-	/// Total fee for a transaction is the sum of fees of all kernels.
+	/// Total fee for a TransactionBody is the sum of fees of all kernels.
 	pub fn fee(&self) -> u64 {
 		self.kernels
 			.iter()
@@ -406,7 +383,21 @@ impl Transaction {
 		self.fee() as i64
 	}
 
-	/// Lock height of a transaction is the max lock height of the kernels.
+	/// Calculate transaction weight
+	pub fn body_weight(&self) -> u32 {
+		TransactionBody::weight(self.inputs.len(), self.outputs.len())
+	}
+
+	/// Calculate transaction weight from transaction details
+	pub fn weight(input_len: usize, output_len: usize) -> u32 {
+		let mut body_weight = -1 * (input_len as i32) + (4 * output_len as i32) + 1;
+		if body_weight < 1 {
+			body_weight = 1;
+		}
+		body_weight as u32
+	}
+
+	/// Lock height of a body is the max lock height of the kernels.
 	pub fn lock_height(&self) -> u64 {
 		self.kernels
 			.iter()
@@ -431,13 +422,14 @@ impl Transaction {
 		Ok(())
 	}
 
-	// Verify the tx is not too big in terms of number of inputs|outputs|kernels.
-	fn verify_weight(&self) -> Result<(), Error> {
-		// check the tx as if it was a block, with an additional output and
+	// Verify the body is not too big in terms of number of inputs|outputs|kernels.
+	fn verify_weight(&self, with_reward: bool) -> Result<(), Error> {
+		// if as_block check the body as if it was a block, with an additional output and
 		// kernel for reward
+		let reserve = if with_reward { 0 } else { 1 };
 		let tx_block_weight = self.inputs.len() * consensus::BLOCK_INPUT_WEIGHT
-			+ (self.outputs.len() + 1) * consensus::BLOCK_OUTPUT_WEIGHT
-			+ (self.kernels.len() + 1) * consensus::BLOCK_KERNEL_WEIGHT;
+			+ (self.outputs.len() + reserve) * consensus::BLOCK_OUTPUT_WEIGHT
+			+ (self.kernels.len() + reserve) * consensus::BLOCK_KERNEL_WEIGHT;
 
 		if tx_block_weight > consensus::MAX_BLOCK_WEIGHT {
 			return Err(Error::TooHeavy);
@@ -445,36 +437,6 @@ impl Transaction {
 		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, as_block: bool) -> Result<(), Error> {
-		if !as_block {
-			self.verify_features()?;
-			self.verify_weight()?;
-			self.verify_kernel_sums(self.overage(), self.offset)?;
-		}
-		self.verify_sorted()?;
-		self.verify_cut_through()?;
-		self.verify_rangeproofs()?;
-		self.verify_kernel_signatures()?;
-		Ok(())
-	}
-
-	/// Calculate transaction weight
-	pub fn tx_weight(&self) -> u32 {
-		Transaction::weight(self.inputs.len(), self.outputs.len())
-	}
-
-	/// Calculate transaction weight from transaction details
-	pub fn weight(input_len: usize, output_len: usize) -> u32 {
-		let mut tx_weight = -1 * (input_len as i32) + (4 * output_len as i32) + 1;
-		if tx_weight < 1 {
-			tx_weight = 1;
-		}
-		tx_weight as u32
-	}
-
 	// Verify that inputs|outputs|kernels are all sorted in lexicographical order.
 	fn verify_sorted(&self) -> Result<(), Error> {
 		self.inputs.verify_sort_order()?;
@@ -497,10 +459,10 @@ impl Transaction {
 		Ok(())
 	}
 
-	// Verify we have no invalid outputs or kernels in the transaction
-	// due to invalid features.
-	// Specifically, a transaction cannot contain a coinbase output or a coinbase kernel.
-	fn verify_features(&self) -> Result<(), Error> {
+	/// Verify we have no invalid outputs or kernels in the transaction
+	/// due to invalid features.
+	/// Specifically, a transaction cannot contain a coinbase output or a coinbase kernel.
+	pub fn verify_features(&self) -> Result<(), Error> {
 		self.verify_output_features()?;
 		self.verify_kernel_features()?;
 		Ok(())
@@ -529,6 +491,202 @@ impl Transaction {
 		}
 		Ok(())
 	}
+
+	/// Validates all relevant parts of a transaction body. 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.verify_weight(with_reward)?;
+		self.verify_sorted()?;
+		self.verify_cut_through()?;
+		self.verify_rangeproofs()?;
+		self.verify_kernel_signatures()?;
+		Ok(())
+	}
+}
+
+/// A transaction
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Transaction {
+	/// The kernel "offset" k2
+	/// excess is k1G after splitting the key k = k1 + k2
+	pub offset: BlindingFactor,
+	/// The transaction body - inputs/outputs/kernels
+	body: TransactionBody,
+}
+
+/// PartialEq
+impl PartialEq for Transaction {
+	fn eq(&self, tx: &Transaction) -> bool {
+		self.body == tx.body && self.offset == tx.offset
+	}
+}
+
+impl Into<TransactionBody> for Transaction {
+	fn into(self) -> TransactionBody {
+		self.body
+	}
+}
+
+/// Implementation of Writeable for a fully blinded transaction, defines how to
+/// write the transaction as binary.
+impl Writeable for Transaction {
+	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
+		self.offset.write(writer)?;
+		self.body.write(writer)?;
+		Ok(())
+	}
+}
+
+/// Implementation of Readable for a transaction, defines how to read a full
+/// transaction from a binary stream.
+impl Readable for Transaction {
+	fn read(reader: &mut Reader) -> Result<Transaction, ser::Error> {
+		let offset = BlindingFactor::read(reader)?;
+
+		let body = TransactionBody::read(reader)?;
+		let tx = Transaction { offset, body };
+
+		// Now validate the tx.
+		// 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(false).map_err(|_| ser::Error::CorruptedData)?;
+
+		Ok(tx)
+	}
+}
+
+impl Committed for Transaction {
+	fn inputs_committed(&self) -> Vec<Commitment> {
+		self.body.inputs_committed()
+	}
+
+	fn outputs_committed(&self) -> Vec<Commitment> {
+		self.body.outputs_committed()
+	}
+
+	fn kernels_committed(&self) -> Vec<Commitment> {
+		self.body.kernels_committed()
+	}
+}
+
+impl Default for Transaction {
+	fn default() -> Transaction {
+		Transaction::empty()
+	}
+}
+
+impl Transaction {
+	/// Creates a new empty transaction (no inputs or outputs, zero fee).
+	pub fn empty() -> Transaction {
+		Transaction {
+			offset: BlindingFactor::zero(),
+			body: Default::default(),
+		}
+	}
+
+	/// Creates a new transaction initialized with
+	/// the provided inputs, outputs, kernels
+	pub fn new(inputs: Vec<Input>, outputs: Vec<Output>, kernels: Vec<TxKernel>) -> Transaction {
+		Transaction {
+			offset: BlindingFactor::zero(),
+			body: TransactionBody::new(inputs, outputs, kernels),
+		}
+	}
+
+	/// Creates a new transaction using this transaction as a template
+	/// and with the specified offset.
+	pub fn with_offset(self, offset: BlindingFactor) -> Transaction {
+		Transaction {
+			offset: offset,
+			..self
+		}
+	}
+
+	/// Builds a new transaction with the provided inputs added. Existing
+	/// inputs, if any, are kept intact.
+	pub fn with_input(self, input: Input) -> Transaction {
+		Transaction {
+			body: self.body.with_input(input),
+			..self
+		}
+	}
+
+	/// Builds a new transaction with the provided output added. Existing
+	/// outputs, if any, are kept intact.
+	pub fn with_output(self, output: Output) -> Transaction {
+		Transaction {
+			body: self.body.with_output(output),
+			..self
+		}
+	}
+
+	/// Get inputs
+	pub fn inputs(&self) -> &Vec<Input> {
+		&self.body.inputs
+	}
+
+	/// Get inputs mutable
+	pub fn inputs_mut(&mut self) -> &mut Vec<Input> {
+		&mut self.body.inputs
+	}
+
+	/// Get outputs
+	pub fn outputs(&self) -> &Vec<Output> {
+		&self.body.outputs
+	}
+
+	/// Get outputs mutable
+	pub fn outputs_mut(&mut self) -> &mut Vec<Output> {
+		&mut self.body.outputs
+	}
+
+	/// Get kernels
+	pub fn kernels(&self) -> &Vec<TxKernel> {
+		&self.body.kernels
+	}
+
+	/// Get kernels mut
+	pub fn kernels_mut(&mut self) -> &mut Vec<TxKernel> {
+		&mut self.body.kernels
+	}
+
+	/// Total fee for a transaction is the sum of fees of all kernels.
+	pub fn fee(&self) -> u64 {
+		self.body.fee()
+	}
+
+	fn overage(&self) -> i64 {
+		self.body.overage()
+	}
+
+	/// Lock height of a transaction is the max lock height of the kernels.
+	pub fn lock_height(&self) -> u64 {
+		self.body.lock_height()
+	}
+
+	/// 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)?;
+		}
+		Ok(())
+	}
+
+	/// Calculate transaction weight
+	pub fn tx_weight(&self) -> u32 {
+		self.body.body_weight()
+	}
+
+	/// Calculate transaction weight from transaction details
+	pub fn weight(input_len: usize, output_len: usize) -> u32 {
+		TransactionBody::weight(input_len, output_len)
+	}
 }
 
 /// Aggregate a vec of transactions into a multi-kernel transaction with
@@ -550,11 +708,11 @@ pub fn aggregate(
 		// we will sum these later to give a single aggregate offset
 		kernel_offsets.push(transaction.offset);
 
-		inputs.append(&mut transaction.inputs);
-		outputs.append(&mut transaction.outputs);
-		kernels.append(&mut transaction.kernels);
+		inputs.append(&mut transaction.body.inputs);
+		outputs.append(&mut transaction.body.outputs);
+		kernels.append(&mut transaction.body.kernels);
 	}
-	let as_block = reward.is_some();
+	let with_reward = reward.is_some();
 	if let Some((out, kernel)) = reward {
 		outputs.push(out);
 		kernels.push(kernel);
@@ -604,7 +762,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(as_block)?;
+	tx.validate(with_reward)?;
 
 	Ok(tx)
 }
@@ -622,18 +780,18 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec<Transaction>) -> Result<Transact
 
 	let tx = aggregate(txs, None)?;
 
-	for mk_input in mk_tx.inputs {
-		if !tx.inputs.contains(&mk_input) && !inputs.contains(&mk_input) {
+	for mk_input in mk_tx.body.inputs {
+		if !tx.body.inputs.contains(&mk_input) && !inputs.contains(&mk_input) {
 			inputs.push(mk_input);
 		}
 	}
-	for mk_output in mk_tx.outputs {
-		if !tx.outputs.contains(&mk_output) && !outputs.contains(&mk_output) {
+	for mk_output in mk_tx.body.outputs {
+		if !tx.body.outputs.contains(&mk_output) && !outputs.contains(&mk_output) {
 			outputs.push(mk_output);
 		}
 	}
-	for mk_kernel in mk_tx.kernels {
-		if !tx.kernels.contains(&mk_kernel) && !kernels.contains(&mk_kernel) {
+	for mk_kernel in mk_tx.body.kernels {
+		if !tx.body.kernels.contains(&mk_kernel) && !kernels.contains(&mk_kernel) {
 			kernels.push(mk_kernel);
 		}
 	}
diff --git a/core/src/genesis.rs b/core/src/genesis.rs
index 529b2c226..7d39aa8ae 100644
--- a/core/src/genesis.rs
+++ b/core/src/genesis.rs
@@ -25,109 +25,83 @@ use global;
 /// is small enough to mine it on the fly, so it does not contain its own
 /// proof of work solution. Can also be easily mutated for different tests.
 pub fn genesis_dev() -> core::Block {
-	core::Block {
-		header: core::BlockHeader {
-			height: 0,
-			previous: core::hash::Hash([0xff; 32]),
-			timestamp: Utc.ymd(1997, 8, 4).and_hms(0, 0, 0),
-			nonce: global::get_genesis_nonce(),
-			..Default::default()
-		},
-		inputs: vec![],
-		outputs: vec![],
-		kernels: vec![],
-	}
+	core::Block::with_header(core::BlockHeader {
+		height: 0,
+		previous: core::hash::Hash([0xff; 32]),
+		timestamp: Utc.ymd(1997, 8, 4).and_hms(0, 0, 0),
+		nonce: global::get_genesis_nonce(),
+		..Default::default()
+	})
 }
 
 /// First testnet genesis block, still subject to change (especially the date,
 /// will hopefully come before Christmas).
 pub fn genesis_testnet1() -> core::Block {
-	core::Block {
-		header: core::BlockHeader {
-			height: 0,
-			previous: core::hash::Hash([0xff; 32]),
-			timestamp: Utc.ymd(2017, 11, 16).and_hms(20, 0, 0),
-			nonce: 28205,
-			pow: core::Proof::new(vec![
-				0x21e, 0x7a2, 0xeae, 0x144e, 0x1b1c, 0x1fbd, 0x203a, 0x214b, 0x293b, 0x2b74,
-				0x2bfa, 0x2c26, 0x32bb, 0x346a, 0x34c7, 0x37c5, 0x4164, 0x42cc, 0x4cc3, 0x55af,
-				0x5a70, 0x5b14, 0x5e1c, 0x5f76, 0x6061, 0x60f9, 0x61d7, 0x6318, 0x63a1, 0x63fb,
-				0x649b, 0x64e5, 0x65a1, 0x6b69, 0x70f8, 0x71c7, 0x71cd, 0x7492, 0x7b11, 0x7db8,
-				0x7f29, 0x7ff8,
-			]),
-			..Default::default()
-		},
-		inputs: vec![],
-		outputs: vec![],
-		kernels: vec![],
-	}
+	core::Block::with_header(core::BlockHeader {
+		height: 0,
+		previous: core::hash::Hash([0xff; 32]),
+		timestamp: Utc.ymd(2017, 11, 16).and_hms(20, 0, 0),
+		nonce: 28205,
+		pow: core::Proof::new(vec![
+			0x21e, 0x7a2, 0xeae, 0x144e, 0x1b1c, 0x1fbd, 0x203a, 0x214b, 0x293b, 0x2b74, 0x2bfa,
+			0x2c26, 0x32bb, 0x346a, 0x34c7, 0x37c5, 0x4164, 0x42cc, 0x4cc3, 0x55af, 0x5a70, 0x5b14,
+			0x5e1c, 0x5f76, 0x6061, 0x60f9, 0x61d7, 0x6318, 0x63a1, 0x63fb, 0x649b, 0x64e5, 0x65a1,
+			0x6b69, 0x70f8, 0x71c7, 0x71cd, 0x7492, 0x7b11, 0x7db8, 0x7f29, 0x7ff8,
+		]),
+		..Default::default()
+	})
 }
 
 /// Second testnet genesis block (cuckoo30).
 pub fn genesis_testnet2() -> core::Block {
-	core::Block {
-		header: core::BlockHeader {
-			height: 0,
-			previous: core::hash::Hash([0xff; 32]),
-			timestamp: Utc.ymd(2018, 3, 26).and_hms(16, 0, 0),
-			total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
-			nonce: 1060,
-			pow: core::Proof::new(vec![
-				0x1940730, 0x333b9d0, 0x4739d6f, 0x4c6cfb1, 0x6e3d6c3, 0x74408a3, 0x7ba2bd2,
-				0x83e2024, 0x8ca22b5, 0x9d39ab8, 0xb6646dd, 0xc6698b6, 0xc6f78fe, 0xc99b662,
-				0xcf2ae8c, 0xcf41eed, 0xdd073e6, 0xded6af8, 0xf08d1a5, 0x1156a144, 0x11d1160a,
-				0x131bb0a5, 0x137ad703, 0x13b0831f, 0x1421683f, 0x147e3c1f, 0x1496fda0, 0x150ba22b,
-				0x15cc5bc6, 0x16edf697, 0x17ced40c, 0x17d84f9e, 0x18a515c1, 0x19320d9c, 0x19da4f6d,
-				0x1b50bcb1, 0x1b8bc72f, 0x1c7b6964, 0x1d07b3a9, 0x1d189d4d, 0x1d1f9a15, 0x1dafcd41,
-			]),
-			..Default::default()
-		},
-		inputs: vec![],
-		outputs: vec![],
-		kernels: vec![],
-	}
+	core::Block::with_header(core::BlockHeader {
+		height: 0,
+		previous: core::hash::Hash([0xff; 32]),
+		timestamp: Utc.ymd(2018, 3, 26).and_hms(16, 0, 0),
+		total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
+		nonce: 1060,
+		pow: core::Proof::new(vec![
+			0x1940730, 0x333b9d0, 0x4739d6f, 0x4c6cfb1, 0x6e3d6c3, 0x74408a3, 0x7ba2bd2, 0x83e2024,
+			0x8ca22b5, 0x9d39ab8, 0xb6646dd, 0xc6698b6, 0xc6f78fe, 0xc99b662, 0xcf2ae8c, 0xcf41eed,
+			0xdd073e6, 0xded6af8, 0xf08d1a5, 0x1156a144, 0x11d1160a, 0x131bb0a5, 0x137ad703,
+			0x13b0831f, 0x1421683f, 0x147e3c1f, 0x1496fda0, 0x150ba22b, 0x15cc5bc6, 0x16edf697,
+			0x17ced40c, 0x17d84f9e, 0x18a515c1, 0x19320d9c, 0x19da4f6d, 0x1b50bcb1, 0x1b8bc72f,
+			0x1c7b6964, 0x1d07b3a9, 0x1d189d4d, 0x1d1f9a15, 0x1dafcd41,
+		]),
+		..Default::default()
+	})
 }
 
 /// Second testnet genesis block (cuckoo30). Temporary values for now.
 pub fn genesis_testnet3() -> core::Block {
-	core::Block {
-		header: core::BlockHeader {
-			height: 0,
-			previous: core::hash::Hash([0xff; 32]),
-			timestamp: Utc.ymd(2018, 7, 8).and_hms(18, 0, 0),
-			total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
-			nonce: 4956988373127691,
-			pow: core::Proof::new(vec![
-				0xa420dc, 0xc8ffee, 0x10e433e, 0x1de9428, 0x2ed4cea, 0x52d907b, 0x5af0e3f,
-				0x6b8fcae, 0x8319b53, 0x845ca8c, 0x8d2a13e, 0x8d6e4cc, 0x9349e8d, 0xa7a33c5,
-				0xaeac3cb, 0xb193e23, 0xb502e19, 0xb5d9804, 0xc9ac184, 0xd4f4de3, 0xd7a23b8,
-				0xf1d8660, 0xf443756, 0x10b833d2, 0x11418fc5, 0x11b8aeaf, 0x131836ec, 0x132ab818,
-				0x13a46a55, 0x13df89fe, 0x145d65b5, 0x166f9c3a, 0x166fe0ef, 0x178cb36f, 0x185baf68,
-				0x1bbfe563, 0x1bd637b4, 0x1cfc8382, 0x1d1ed012, 0x1e391ca5, 0x1e999b4c, 0x1f7c6d21,
-			]),
-			..Default::default()
-		},
-		inputs: vec![],
-		outputs: vec![],
-		kernels: vec![],
-	}
+	core::Block::with_header(core::BlockHeader {
+		height: 0,
+		previous: core::hash::Hash([0xff; 32]),
+		timestamp: Utc.ymd(2018, 7, 8).and_hms(18, 0, 0),
+		total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
+		nonce: 4956988373127691,
+		pow: core::Proof::new(vec![
+			0xa420dc, 0xc8ffee, 0x10e433e, 0x1de9428, 0x2ed4cea, 0x52d907b, 0x5af0e3f, 0x6b8fcae,
+			0x8319b53, 0x845ca8c, 0x8d2a13e, 0x8d6e4cc, 0x9349e8d, 0xa7a33c5, 0xaeac3cb, 0xb193e23,
+			0xb502e19, 0xb5d9804, 0xc9ac184, 0xd4f4de3, 0xd7a23b8, 0xf1d8660, 0xf443756,
+			0x10b833d2, 0x11418fc5, 0x11b8aeaf, 0x131836ec, 0x132ab818, 0x13a46a55, 0x13df89fe,
+			0x145d65b5, 0x166f9c3a, 0x166fe0ef, 0x178cb36f, 0x185baf68, 0x1bbfe563, 0x1bd637b4,
+			0x1cfc8382, 0x1d1ed012, 0x1e391ca5, 0x1e999b4c, 0x1f7c6d21,
+		]),
+		..Default::default()
+	})
 }
 
 /// Placeholder for mainnet genesis block, will definitely change before
 /// release so no use trying to pre-mine it.
 pub fn genesis_main() -> core::Block {
-	core::Block {
-		header: core::BlockHeader {
-			height: 0,
-			previous: core::hash::Hash([0xff; 32]),
-			timestamp: Utc.ymd(2018, 8, 14).and_hms(0, 0, 0),
-			total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
-			nonce: global::get_genesis_nonce(),
-			pow: core::Proof::zero(consensus::PROOFSIZE),
-			..Default::default()
-		},
-		inputs: vec![],
-		outputs: vec![],
-		kernels: vec![],
-	}
+	core::Block::with_header(core::BlockHeader {
+		height: 0,
+		previous: core::hash::Hash([0xff; 32]),
+		timestamp: Utc.ymd(2018, 8, 14).and_hms(0, 0, 0),
+		total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
+		nonce: global::get_genesis_nonce(),
+		pow: core::Proof::zero(consensus::PROOFSIZE),
+		..Default::default()
+	})
 }
diff --git a/core/tests/block.rs b/core/tests/block.rs
index 0388467f5..b6bee2b93 100644
--- a/core/tests/block.rs
+++ b/core/tests/block.rs
@@ -12,25 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+extern crate chrono;
 extern crate grin_core;
 extern crate grin_keychain as keychain;
 extern crate grin_util as util;
 extern crate grin_wallet as wallet;
-extern crate chrono;
 
 pub mod common;
 
+use chrono::Duration;
 use common::{new_block, tx1i2o, tx2i1o, txspend1i1o};
 use grin_core::consensus::{BLOCK_OUTPUT_WEIGHT, MAX_BLOCK_WEIGHT};
-use grin_core::core::Committed;
 use grin_core::core::block::Error;
 use grin_core::core::hash::Hashed;
 use grin_core::core::id::{ShortId, ShortIdentifiable};
+use grin_core::core::Committed;
 use grin_core::core::{Block, BlockHeader, CompactBlock, KernelFeatures, OutputFeatures};
 use grin_core::{global, ser};
 use keychain::{BlindingFactor, ExtKeychain, Keychain};
 use std::time::Instant;
-use chrono::Duration;
 use util::{secp, secp_static};
 use wallet::libtx::build::{self, input, output, with_fee};
 
@@ -68,12 +68,7 @@ fn too_large_block() {
 // block with no inputs/outputs/kernels
 // no fees, no reward, no coinbase
 fn very_empty_block() {
-	let b = Block {
-		header: BlockHeader::default(),
-		inputs: vec![],
-		outputs: vec![],
-		kernels: vec![],
-	};
+	let b = Block::with_header(BlockHeader::default());
 
 	assert_eq!(
 		b.verify_coinbase(),
@@ -113,8 +108,8 @@ fn block_with_cut_through() {
 	// output) and should still be valid
 	println!("3");
 	b.validate(&BlindingFactor::zero(), &zero_commit).unwrap();
-	assert_eq!(b.inputs.len(), 3);
-	assert_eq!(b.outputs.len(), 3);
+	assert_eq!(b.inputs().len(), 3);
+	assert_eq!(b.outputs().len(), 3);
 	println!("4");
 }
 
@@ -126,18 +121,20 @@ fn empty_block_with_coinbase_is_valid() {
 	let key_id = keychain.derive_key_id(1).unwrap();
 	let b = new_block(vec![], &keychain, &prev, &key_id);
 
-	assert_eq!(b.inputs.len(), 0);
-	assert_eq!(b.outputs.len(), 1);
-	assert_eq!(b.kernels.len(), 1);
+	assert_eq!(b.inputs().len(), 0);
+	assert_eq!(b.outputs().len(), 1);
+	assert_eq!(b.kernels().len(), 1);
 
-	let coinbase_outputs = b.outputs
+	let coinbase_outputs = b
+		.outputs()
 		.iter()
 		.filter(|out| out.features.contains(OutputFeatures::COINBASE_OUTPUT))
 		.map(|o| o.clone())
 		.collect::<Vec<_>>();
 	assert_eq!(coinbase_outputs.len(), 1);
 
-	let coinbase_kernels = b.kernels
+	let coinbase_kernels = b
+		.kernels()
 		.iter()
 		.filter(|out| out.features.contains(KernelFeatures::COINBASE_KERNEL))
 		.map(|o| o.clone())
@@ -161,11 +158,11 @@ fn remove_coinbase_output_flag() {
 	let mut b = new_block(vec![], &keychain, &prev, &key_id);
 
 	assert!(
-		b.outputs[0]
+		b.outputs()[0]
 			.features
 			.contains(OutputFeatures::COINBASE_OUTPUT)
 	);
-	b.outputs[0]
+	b.outputs_mut()[0]
 		.features
 		.remove(OutputFeatures::COINBASE_OUTPUT);
 
@@ -191,11 +188,11 @@ fn remove_coinbase_kernel_flag() {
 	let mut b = new_block(vec![], &keychain, &prev, &key_id);
 
 	assert!(
-		b.kernels[0]
+		b.kernels()[0]
 			.features
 			.contains(KernelFeatures::COINBASE_KERNEL)
 	);
-	b.kernels[0]
+	b.kernels_mut()[0]
 		.features
 		.remove(KernelFeatures::COINBASE_KERNEL);
 
@@ -224,12 +221,13 @@ fn serialize_deserialize_block() {
 	// After header serialization, timestamp will lose 'nanos' info, that's the designed behavior.
 	// To suppress 'nanos' difference caused assertion fail, we force b.header also lose 'nanos'.
 	let origin_ts = b.header.timestamp;
-	b.header.timestamp = origin_ts - Duration::nanoseconds(origin_ts.timestamp_subsec_nanos() as i64);
+	b.header.timestamp =
+		origin_ts - Duration::nanoseconds(origin_ts.timestamp_subsec_nanos() as i64);
 
 	assert_eq!(b.header, b2.header);
-	assert_eq!(b.inputs, b2.inputs);
-	assert_eq!(b.outputs, b2.outputs);
-	assert_eq!(b.kernels, b2.kernels);
+	assert_eq!(b.inputs(), b2.inputs());
+	assert_eq!(b.outputs(), b2.outputs());
+	assert_eq!(b.kernels(), b2.kernels());
 }
 
 #[test]
@@ -341,11 +339,11 @@ fn compact_block_hash_with_nonce() {
 	// correctly in both of the compact_blocks
 	assert_eq!(
 		cb1.kern_ids[0],
-		tx.kernels[0].short_id(&cb1.hash(), cb1.nonce)
+		tx.kernels()[0].short_id(&cb1.hash(), cb1.nonce)
 	);
 	assert_eq!(
 		cb2.kern_ids[0],
-		tx.kernels[0].short_id(&cb2.hash(), cb2.nonce)
+		tx.kernels()[0].short_id(&cb2.hash(), cb2.nonce)
 	);
 }
 
@@ -364,7 +362,7 @@ fn convert_block_to_compact_block() {
 
 	assert_eq!(
 		cb.kern_ids[0],
-		b.kernels
+		b.kernels()
 			.iter()
 			.find(|x| !x.features.contains(KernelFeatures::COINBASE_KERNEL))
 			.unwrap()
@@ -381,8 +379,8 @@ fn hydrate_empty_compact_block() {
 	let cb = b.as_compact_block();
 	let hb = Block::hydrate_from(cb, vec![]);
 	assert_eq!(hb.header, b.header);
-	assert_eq!(hb.outputs, b.outputs);
-	assert_eq!(hb.kernels, b.kernels);
+	assert_eq!(hb.outputs(), b.outputs());
+	assert_eq!(hb.kernels(), b.kernels());
 }
 
 #[test]
diff --git a/core/tests/core.rs b/core/tests/core.rs
index 189c4161d..fed368b2b 100644
--- a/core/tests/core.rs
+++ b/core/tests/core.rs
@@ -28,8 +28,9 @@ use grin_core::core::{aggregate, deaggregate, KernelFeatures, Output, Transactio
 use grin_core::ser;
 use keychain::{BlindingFactor, ExtKeychain, Keychain};
 use util::{secp_static, static_secp_instance};
-use wallet::libtx::build::{self, initial_tx, input, output, with_excess, with_fee,
-                           with_lock_height};
+use wallet::libtx::build::{
+	self, initial_tx, input, output, with_excess, with_fee, with_lock_height,
+};
 
 #[test]
 fn simple_tx_ser() {
@@ -47,8 +48,8 @@ fn simple_tx_ser_deser() {
 	ser::serialize(&mut vec, &tx).expect("serialization failed");
 	let dtx: Transaction = ser::deserialize(&mut &vec[..]).unwrap();
 	assert_eq!(dtx.fee(), 2);
-	assert_eq!(dtx.inputs.len(), 2);
-	assert_eq!(dtx.outputs.len(), 1);
+	assert_eq!(dtx.inputs().len(), 2);
+	assert_eq!(dtx.outputs().len(), 1);
 	assert_eq!(tx.hash(), dtx.hash());
 }
 
@@ -108,8 +109,8 @@ fn build_tx_kernel() {
 	tx.validate(false).unwrap();
 
 	// check the kernel is also itself valid
-	assert_eq!(tx.kernels.len(), 1);
-	let kern = &tx.kernels[0];
+	assert_eq!(tx.kernels().len(), 1);
+	let kern = &tx.kernels()[0];
 	kern.verify().unwrap();
 
 	assert_eq!(kern.features, KernelFeatures::DEFAULT_KERNEL);
@@ -145,7 +146,10 @@ fn multi_kernel_transaction_deaggregation() {
 	assert!(tx3.validate(false).is_ok());
 	assert!(tx4.validate(false).is_ok());
 
-	let tx1234 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()], None).unwrap();
+	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();
 
@@ -220,13 +224,16 @@ fn multi_kernel_transaction_deaggregation_4() {
 	assert!(tx4.validate(false).is_ok());
 	assert!(tx5.validate(false).is_ok());
 
-	let tx12345 = aggregate(vec![
-		tx1.clone(),
-		tx2.clone(),
-		tx3.clone(),
-		tx4.clone(),
-		tx5.clone(),
-	], None).unwrap();
+	let tx12345 = aggregate(
+		vec![
+			tx1.clone(),
+			tx2.clone(),
+			tx3.clone(),
+			tx4.clone(),
+			tx5.clone(),
+		],
+		None,
+	).unwrap();
 	assert!(tx12345.validate(false).is_ok());
 
 	let deaggregated_tx5 = deaggregate(
@@ -251,13 +258,16 @@ fn multi_kernel_transaction_deaggregation_5() {
 	assert!(tx4.validate(false).is_ok());
 	assert!(tx5.validate(false).is_ok());
 
-	let tx12345 = aggregate(vec![
-		tx1.clone(),
-		tx2.clone(),
-		tx3.clone(),
-		tx4.clone(),
-		tx5.clone(),
-	], None).unwrap();
+	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();
 
@@ -309,9 +319,9 @@ fn hash_output() {
 		],
 		&keychain,
 	).unwrap();
-	let h = tx.outputs[0].hash();
+	let h = tx.outputs()[0].hash();
 	assert!(h != ZERO_HASH);
-	let h2 = tx.outputs[1].hash();
+	let h2 = tx.outputs()[1].hash();
 	assert!(h != h2);
 }
 
@@ -325,7 +335,7 @@ fn blind_tx() {
 	// with a bullet proof causes painful errors
 
 	// checks that the range proof on our blind output is sufficiently hiding
-	let Output { proof, .. } = btx.outputs[0];
+	let Output { proof, .. } = btx.outputs()[0];
 
 	let secp = static_secp_instance();
 	let secp = secp.lock().unwrap();
diff --git a/pool/src/pool.rs b/pool/src/pool.rs
index 51672d201..ade274069 100644
--- a/pool/src/pool.rs
+++ b/pool/src/pool.rs
@@ -53,7 +53,7 @@ where
 		let mut txs = vec![];
 
 		for x in &self.entries {
-			for kernel in &x.tx.kernels {
+			for kernel in x.tx.kernels() {
 				// rehash each kernel to calculate the block specific short_id
 				let short_id = kernel.short_id(&cb.hash(), cb.nonce);
 
@@ -97,7 +97,8 @@ where
 		to_state: PoolEntryState,
 		extra_tx: Option<Transaction>,
 	) -> Result<Vec<Transaction>, PoolError> {
-		let entries = &mut self.entries
+		let entries = &mut self
+			.entries
 			.iter_mut()
 			.filter(|x| x.state == from_state)
 			.collect::<Vec<_>>();
@@ -189,8 +190,8 @@ where
 	fn remaining_transactions(&self, block: &Block) -> Vec<Transaction> {
 		self.entries
 			.iter()
-			.filter(|x| !x.tx.kernels.iter().any(|y| block.kernels.contains(y)))
-			.filter(|x| !x.tx.inputs.iter().any(|y| block.inputs.contains(y)))
+			.filter(|x| !x.tx.kernels().iter().any(|y| block.kernels().contains(y)))
+			.filter(|x| !x.tx.inputs().iter().any(|y| block.inputs().contains(y)))
 			.map(|x| x.tx.clone())
 			.collect()
 	}
@@ -205,7 +206,7 @@ where
 
 		// Check each transaction in the pool
 		for entry in &self.entries {
-			let entry_kernel_set = entry.tx.kernels.iter().cloned().collect::<HashSet<_>>();
+			let entry_kernel_set = entry.tx.kernels().iter().cloned().collect::<HashSet<_>>();
 			if entry_kernel_set.is_subset(&kernel_set) {
 				found_txs.push(entry.tx.clone());
 			}
diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs
index ad8e1a70f..a88e29c11 100644
--- a/pool/src/transaction_pool.rs
+++ b/pool/src/transaction_pool.rs
@@ -17,8 +17,8 @@
 //! resulting tx pool can be added to the current chain state to produce a
 //! valid chain state.
 
+use chrono::prelude::Utc;
 use std::sync::Arc;
-use chrono::prelude::{Utc};
 
 use core::core::hash::Hashed;
 use core::core::{transaction, Block, CompactBlock, Transaction};
@@ -69,9 +69,10 @@ where
 
 	fn add_to_txpool(&mut self, mut entry: PoolEntry) -> Result<(), PoolError> {
 		// First deaggregate the tx based on current txpool txs.
-		if entry.tx.kernels.len() > 1 {
-			let txs = self.txpool
-				.find_matching_transactions(entry.tx.kernels.clone());
+		if entry.tx.kernels().len() > 1 {
+			let txs = self
+				.txpool
+				.find_matching_transactions(entry.tx.kernels().clone());
 			if !txs.is_empty() {
 				entry.tx = transaction::deaggregate(entry.tx, txs)?;
 				entry.src.debug_name = "deagg".to_string();
@@ -100,7 +101,7 @@ where
 			LOGGER,
 			"pool: add_to_pool: {:?}, kernels - {}, stem? {}",
 			tx.hash(),
-			tx.kernels.len(),
+			tx.kernels().len(),
 			stem,
 		);
 
diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs
index f90764b43..aa52f7ac6 100644
--- a/pool/tests/transaction_pool.rs
+++ b/pool/tests/transaction_pool.rs
@@ -20,8 +20,8 @@ extern crate grin_pool as pool;
 extern crate grin_util as util;
 extern crate grin_wallet as wallet;
 
-extern crate rand;
 extern crate chrono;
+extern crate rand;
 
 pub mod common;
 
@@ -29,8 +29,10 @@ use std::sync::{Arc, RwLock};
 
 use chain::types::Tip;
 use chain::{txhashset, ChainStore};
-use common::{clean_output_dir, test_setup, test_source, test_transaction,
-             test_transaction_spending_coinbase, ChainAdapter};
+use common::{
+	clean_output_dir, test_setup, test_source, test_transaction,
+	test_transaction_spending_coinbase, ChainAdapter,
+};
 use core::core::target::Difficulty;
 use core::core::{transaction, Block, BlockHeader};
 use keychain::{ExtKeychain, Keychain};
@@ -171,7 +173,7 @@ fn test_the_transaction_pool() {
 			.aggregate_transaction()
 			.unwrap()
 			.unwrap();
-		assert_eq!(agg_tx.kernels.len(), 2);
+		assert_eq!(agg_tx.kernels().len(), 2);
 		write_pool
 			.add_to_pool(test_source(), agg_tx, false)
 			.unwrap();
@@ -194,7 +196,7 @@ fn test_the_transaction_pool() {
 			.unwrap();
 		assert_eq!(write_pool.total_size(), 3);
 		let entry = write_pool.txpool.entries.last().unwrap();
-		assert_eq!(entry.tx.kernels.len(), 1);
+		assert_eq!(entry.tx.kernels().len(), 1);
 		assert_eq!(entry.src.debug_name, "deagg");
 	}
 
diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs
index 7794724db..f8969a9c1 100644
--- a/servers/src/mining/mine_block.rs
+++ b/servers/src/mining/mine_block.rs
@@ -15,12 +15,12 @@
 //! Build a block to mine: gathers transactions from the pool, assembles
 //! them into a block and returns it.
 
+use chrono::prelude::{DateTime, NaiveDateTime, Utc};
 use itertools::Itertools;
 use rand::{self, Rng};
 use std::sync::{Arc, RwLock};
 use std::thread;
 use std::time::Duration;
-use chrono::prelude::{DateTime, NaiveDateTime, Utc};
 
 use chain;
 use common::adapters::PoolToChainAdapter;
@@ -178,8 +178,8 @@ fn build_block(
 	debug!(
 		LOGGER,
 		"Built new block with {} inputs and {} outputs, network difficulty: {}, cumulative difficulty {}",
-		b.inputs.len(),
-		b.outputs.len(),
+		b.inputs().len(),
+		b.outputs().len(),
 		b_difficulty,
 		b.header.clone().total_difficulty.to_num(),
 	);
diff --git a/wallet/src/libtx/build.rs b/wallet/src/libtx/build.rs
index 31d493a26..1214693ff 100644
--- a/wallet/src/libtx/build.rs
+++ b/wallet/src/libtx/build.rs
@@ -170,8 +170,8 @@ pub fn initial_tx<K>(mut tx: Transaction) -> Box<Append<K>>
 where
 	K: Keychain,
 {
-	assert_eq!(tx.kernels.len(), 1);
-	let kern = tx.kernels.remove(0);
+	assert_eq!(tx.kernels().len(), 1);
+	let kern = tx.kernels_mut().remove(0);
 	Box::new(
 		move |_build, (_, _, sum)| -> (Transaction, TxKernel, BlindSum) {
 			(tx.clone(), kern.clone(), sum)
@@ -204,8 +204,8 @@ where
 	let blind_sum = ctx.keychain.blind_sum(&sum)?;
 
 	// we only support building a tx with a single kernel via build::transaction()
-	assert!(tx.kernels.is_empty());
-	tx.kernels.push(kern);
+	assert!(tx.kernels().is_empty());
+	tx.kernels_mut().push(kern);
 
 	Ok((tx, blind_sum))
 }
@@ -219,16 +219,16 @@ where
 	K: Keychain,
 {
 	let (mut tx, blind_sum) = partial_transaction(elems, keychain)?;
-	assert_eq!(tx.kernels.len(), 1);
+	assert_eq!(tx.kernels().len(), 1);
 
-	let mut kern = tx.kernels.remove(0);
+	let mut kern = tx.kernels_mut().remove(0);
 	let msg = secp::Message::from_slice(&kernel_sig_msg(kern.fee, kern.lock_height))?;
 
 	let skey = blind_sum.secret_key(&keychain.secp())?;
 	kern.excess = keychain.secp().commit(0, skey)?;
 	kern.excess_sig = aggsig::sign_with_blinding(&keychain.secp(), &msg, &blind_sum).unwrap();
 
-	tx.kernels.push(kern);
+	tx.kernels_mut().push(kern);
 
 	Ok(tx)
 }
@@ -264,8 +264,8 @@ where
 	// commitments will sum correctly when including the offset
 	tx.offset = k2.clone();
 
-	assert!(tx.kernels.is_empty());
-	tx.kernels.push(kern);
+	assert!(tx.kernels().is_empty());
+	tx.kernels_mut().push(kern);
 
 	Ok(tx)
 }
diff --git a/wallet/src/libtx/slate.rs b/wallet/src/libtx/slate.rs
index fc6eb4ab0..c070d3dbf 100644
--- a/wallet/src/libtx/slate.rs
+++ b/wallet/src/libtx/slate.rs
@@ -113,7 +113,7 @@ impl Slate {
 		K: Keychain,
 	{
 		// Append to the exiting transaction
-		if self.tx.kernels.len() != 0 {
+		if self.tx.kernels().len() != 0 {
 			elems.insert(0, build::initial_tx(self.tx.clone()));
 		}
 		let (tx, blind) = build::partial_transaction(elems, keychain)?;
@@ -179,7 +179,8 @@ impl Slate {
 
 	/// Return the sum of public nonces
 	fn pub_nonce_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> {
-		let pub_nonces = self.participant_data
+		let pub_nonces = self
+			.participant_data
 			.iter()
 			.map(|p| &p.public_nonce)
 			.collect();
@@ -191,7 +192,8 @@ impl Slate {
 
 	/// Return the sum of public blinding factors
 	fn pub_blind_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> {
-		let pub_blinds = self.participant_data
+		let pub_blinds = self
+			.participant_data
 			.iter()
 			.map(|p| &p.public_blind_excess)
 			.collect();
@@ -250,9 +252,11 @@ impl Slate {
 		// the aggsig context with the "split" key
 		self.tx.offset =
 			BlindingFactor::from_secret_key(SecretKey::new(&keychain.secp(), &mut thread_rng()));
-		let blind_offset = keychain.blind_sum(&BlindSum::new()
-			.add_blinding_factor(BlindingFactor::from_secret_key(sec_key.clone()))
-			.sub_blinding_factor(self.tx.offset))?;
+		let blind_offset = keychain.blind_sum(
+			&BlindSum::new()
+				.add_blinding_factor(BlindingFactor::from_secret_key(sec_key.clone()))
+				.sub_blinding_factor(self.tx.offset),
+		)?;
 		*sec_key = blind_offset.secret_key(&keychain.secp())?;
 		Ok(())
 	}
@@ -262,7 +266,7 @@ impl Slate {
 		// double check the fee amount included in the partial tx
 		// we don't necessarily want to just trust the sender
 		// we could just overwrite the fee here (but we won't) due to the sig
-		let fee = tx_fee(self.tx.inputs.len(), self.tx.outputs.len(), None);
+		let fee = tx_fee(self.tx.inputs().len(), self.tx.outputs().len(), None);
 		if fee > self.tx.fee() {
 			return Err(ErrorKind::Fee(
 				format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,).to_string(),
@@ -361,7 +365,7 @@ impl Slate {
 		// build the final excess based on final tx and offset
 		let final_excess = {
 			// TODO - do we need to verify rangeproofs here?
-			for x in &final_tx.outputs {
+			for x in final_tx.outputs() {
 				x.verify_proof()?;
 			}
 
@@ -379,13 +383,13 @@ impl Slate {
 		};
 
 		// update the tx kernel to reflect the offset excess and sig
-		assert_eq!(final_tx.kernels.len(), 1);
-		final_tx.kernels[0].excess = final_excess.clone();
-		final_tx.kernels[0].excess_sig = final_sig.clone();
+		assert_eq!(final_tx.kernels().len(), 1);
+		final_tx.kernels_mut()[0].excess = final_excess.clone();
+		final_tx.kernels_mut()[0].excess_sig = final_sig.clone();
 
 		// confirm the kernel verifies successfully before proceeding
 		debug!(LOGGER, "Validating final transaction");
-		final_tx.kernels[0].verify()?;
+		final_tx.kernels()[0].verify()?;
 
 		// confirm the overall transaction is valid (including the updated kernel)
 		let _ = final_tx.validate(false)?;
diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs
index 73a44273f..265671f1e 100644
--- a/wallet/src/libwallet/internal/tx.rs
+++ b/wallet/src/libwallet/internal/tx.rs
@@ -217,7 +217,7 @@ mod test {
 		let tx1 = build::transaction(vec![build::output(105, key_id1.clone())], &keychain).unwrap();
 		let tx2 = build::transaction(vec![build::input(105, key_id1.clone())], &keychain).unwrap();
 
-		assert_eq!(tx1.outputs[0].features, tx2.inputs[0].features);
-		assert_eq!(tx1.outputs[0].commitment(), tx2.inputs[0].commitment());
+		assert_eq!(tx1.outputs()[0].features, tx2.inputs()[0].features);
+		assert_eq!(tx1.outputs()[0].commitment(), tx2.inputs()[0].commitment());
 	}
 }