diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs
index 92bbf8201..8d7fdd05a 100644
--- a/chain/src/pipe.rs
+++ b/chain/src/pipe.rs
@@ -44,7 +44,6 @@ pub struct BlockContext {
 	store: Arc<ChainStore>,
 	adapter: Arc<ChainAdapter>,
 	head: Tip,
-	tip: Option<Tip>,
 }
 
 #[derive(Debug)]
@@ -63,6 +62,8 @@ pub enum Error {
 	InvalidBlockProof(secp::Error),
 	/// Block time is too old
 	InvalidBlockTime,
+	/// Block height is invalid (not previous + 1)
+	InvalidBlockHeight,
 	/// Internal issue when trying to save or load data from store
 	StoreErr(types::Error),
 }
@@ -85,7 +86,6 @@ pub fn process_block(b: &Block,
 		store: store,
 		adapter: adapter,
 		head: head,
-		tip: None,
 	};
 
 	info!("Starting validation pipeline for block {} at {}.",
@@ -93,22 +93,19 @@ pub fn process_block(b: &Block,
 	      b.header.height);
 	try!(check_known(b.hash(), &mut ctx));
 	try!(validate_header(&b, &mut ctx));
-	try!(set_tip(&b.header, &mut ctx));
 	try!(validate_block(b, &mut ctx));
 	info!("Block at {} with hash {} is valid, going to save and append.",
 	      b.header.height,
 	      b.hash());
 	try!(add_block(b, &mut ctx));
 	// TODO a global lock should be set before that step or even earlier
-	try!(update_tips(&mut ctx));
-
-	// TODO make sure we always return the head, and not a fork that just got longer
-	Ok(ctx.tip)
+	update_head(b, &mut ctx)
 }
 
 /// Quick in-memory check to fast-reject any block we've already handled
 /// recently. Keeps duplicates from the network in check.
 fn check_known(bh: Hash, ctx: &mut BlockContext) -> Result<(), Error> {
+	// TODO ring buffer of the last few blocks that came through here
 	if bh == ctx.head.last_block_h || bh == ctx.head.prev_block_h {
 		return Err(Error::Unfit("already known".to_string()));
 	}
@@ -128,6 +125,9 @@ fn validate_header(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
 
 	let prev = try!(ctx.store.get_block_header(&header.previous).map_err(&Error::StoreErr));
 
+	if header.height != prev.height + 1 {
+		return Err(Error::InvalidBlockHeight);
+	}
 	if header.timestamp <= prev.timestamp {
 		// prevent time warp attacks and some timestamp manipulations by forcing strict
 		// time progression
@@ -140,8 +140,7 @@ fn validate_header(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
 		return Err(Error::InvalidBlockTime);
 	}
 
-	if b.header.total_difficulty !=
-	   prev.total_difficulty.clone() + Difficulty::from_hash(&prev.hash()) {
+	if header.total_difficulty != prev.total_difficulty.clone() + prev.pow.to_difficulty() {
 		return Err(Error::WrongTotalDifficulty);
 	}
 
@@ -168,26 +167,17 @@ fn validate_header(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
 	Ok(())
 }
 
-fn set_tip(h: &BlockHeader, ctx: &mut BlockContext) -> Result<(), Error> {
-	// TODO actually support more than one branch
-	if h.previous != ctx.head.last_block_h {
-		return Err(Error::Unfit("Just don't know where to put it right now".to_string()));
-	}
-	// TODO validate block header height
-	ctx.tip = Some(ctx.head.clone());
-	Ok(())
-}
-
+/// Fully validate the block content.
 fn validate_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
 	// TODO check tx merkle tree
 	let curve = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
 	try!(b.verify(&curve).map_err(&Error::InvalidBlockProof));
+	// TODO check every input exists
 	Ok(())
 }
 
+/// Officially adds the block to our chain.
 fn add_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
-	// save the block and appends it to the selected tip
-	ctx.tip = ctx.tip.as_ref().map(|t| t.append(b.hash()));
 	ctx.store.save_block(b).map_err(&Error::StoreErr);
 
 	// broadcast the block
@@ -196,7 +186,18 @@ fn add_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
 	Ok(())
 }
 
-fn update_tips(ctx: &mut BlockContext) -> Result<(), Error> {
-	let tip = ctx.tip.as_ref().unwrap();
-	ctx.store.save_head(tip).map_err(&Error::StoreErr)
+/// Directly updates the head if we've just appended a new block to it or handle
+/// the situation where we've just added enough work to have a fork with more
+/// work than the head.
+fn update_head(b: &Block, ctx: &mut BlockContext) -> Result<Option<Tip>, Error> {
+	// if we made a fork with more work than the head (which should also be true
+	// when extending the head), update it
+	let tip = Tip::from_block(b);
+	if tip.total_difficulty > ctx.head.total_difficulty {
+		try!(ctx.store.save_head(&tip).map_err(&Error::StoreErr));
+		ctx.head = tip.clone();
+		Ok(Some(tip))
+	} else {
+		Ok(None)
+	}
 }
diff --git a/chain/src/store.rs b/chain/src/store.rs
index a3c5415d8..002eb36d9 100644
--- a/chain/src/store.rs
+++ b/chain/src/store.rs
@@ -27,7 +27,6 @@ const SEP: u8 = ':' as u8;
 
 const BLOCK_HEADER_PREFIX: u8 = 'h' as u8;
 const BLOCK_PREFIX: u8 = 'b' as u8;
-const TIP_PREFIX: u8 = 'T' as u8;
 const HEAD_PREFIX: u8 = 'H' as u8;
 
 /// An implementation of the ChainStore trait backed by a simple key-value
@@ -69,16 +68,8 @@ impl ChainStore for ChainKVStore {
 	}
 
 	fn save_head(&self, t: &Tip) -> Result<(), Error> {
-		try!(self.save_tip(t));
 		self.db.put_ser(&vec![HEAD_PREFIX], t).map_err(&to_store_err)
 	}
-
-	fn save_tip(&self, t: &Tip) -> Result<(), Error> {
-		let last_branch = t.lineage.last_branch();
-		let mut k = vec![TIP_PREFIX, SEP];
-		k.write_u32::<BigEndian>(last_branch);
-		self.db.put_ser(&mut k, t).map_err(&to_store_err)
-	}
 }
 
 fn to_key(prefix: u8, val: &mut Vec<u8>) -> &mut Vec<u8> {
diff --git a/chain/src/types.rs b/chain/src/types.rs
index 1075005bd..eebdcd69f 100644
--- a/chain/src/types.rs
+++ b/chain/src/types.rs
@@ -14,56 +14,15 @@
 
 //! Base types that the block chain pipeline requires.
 
-use core::core::hash::Hash;
 use core::core::{Block, BlockHeader};
+use core::core::hash::Hash;
+use core::core::target::Difficulty;
 use core::ser;
 
-/// The lineage of a fork, defined as a series of numbers. Each new branch gets
-/// a new number that gets added to a fork's ancestry to form a new fork.
-/// Example:
-///   head [1] -> fork1 [1, 2]
-///               fork2 [1, 3]
-#[derive(Debug, Clone)]
-pub struct Lineage(Vec<u32>);
-
-impl Lineage {
-	/// New lineage initialized just with branch 0
-	pub fn new() -> Lineage {
-		Lineage(vec![0])
-	}
-	/// The last branch that was added to the lineage. Also the only branch
-	/// that's
-	/// unique to this lineage.
-	pub fn last_branch(&self) -> u32 {
-		*self.0.last().unwrap()
-	}
-}
-
-/// Serialization for lineage, necessary to serialize fork tips.
-impl ser::Writeable for Lineage {
-	fn write(&self, writer: &mut ser::Writer) -> Result<(), ser::Error> {
-		try!(writer.write_u32(self.0.len() as u32));
-		for num in &self.0 {
-			try!(writer.write_u32(*num));
-		}
-		Ok(())
-	}
-}
-/// Deserialization for lineage, necessary to deserialize fork tips.
-impl ser::Readable<Lineage> for Lineage {
-	fn read(reader: &mut ser::Reader) -> Result<Lineage, ser::Error> {
-		let len = try!(reader.read_u32());
-		let mut branches = Vec::with_capacity(len as usize);
-		for _ in 0..len {
-			branches.push(try!(reader.read_u32()));
-		}
-		Ok(Lineage(branches))
-	}
-}
-
 /// The tip of a fork. A handle to the fork ancestry from its leaf in the
-/// blockchain tree. References both the lineage of the fork as well as its max
-/// height and its latest and previous blocks for convenience.
+/// blockchain tree. References the max height and the latest and previous
+/// blocks
+/// for convenience and the total difficulty.
 #[derive(Debug, Clone)]
 pub struct Tip {
 	/// Height of the tip (max height of the fork)
@@ -72,8 +31,8 @@ pub struct Tip {
 	pub last_block_h: Hash,
 	/// Block previous to last
 	pub prev_block_h: Hash,
-	/// Lineage in branch numbers of the fork
-	pub lineage: Lineage,
+	/// Total difficulty accumulated on that fork
+	pub total_difficulty: Difficulty,
 }
 
 impl Tip {
@@ -83,17 +42,17 @@ impl Tip {
 			height: 0,
 			last_block_h: gbh,
 			prev_block_h: gbh,
-			lineage: Lineage::new(),
+			total_difficulty: Difficulty::one(),
 		}
 	}
 
-	/// Append a new block hash to this tip, returning a new updated tip.
-	pub fn append(&self, bh: Hash) -> Tip {
+	/// Append a new block to this tip, returning a new updated tip.
+	pub fn from_block(b: &Block) -> Tip {
 		Tip {
-			height: self.height + 1,
-			last_block_h: bh,
-			prev_block_h: self.last_block_h,
-			lineage: self.lineage.clone(),
+			height: b.header.height,
+			last_block_h: b.hash(),
+			prev_block_h: b.header.previous,
+			total_difficulty: b.header.total_difficulty.clone() + Difficulty::from_hash(&b.hash()),
 		}
 	}
 }
@@ -104,7 +63,7 @@ impl ser::Writeable for Tip {
 		try!(writer.write_u64(self.height));
 		try!(writer.write_fixed_bytes(&self.last_block_h));
 		try!(writer.write_fixed_bytes(&self.prev_block_h));
-		self.lineage.write(writer)
+		self.total_difficulty.write(writer)
 	}
 }
 
@@ -113,12 +72,12 @@ impl ser::Readable<Tip> for Tip {
 		let height = try!(reader.read_u64());
 		let last = try!(Hash::read(reader));
 		let prev = try!(Hash::read(reader));
-		let line = try!(Lineage::read(reader));
+		let diff = try!(Difficulty::read(reader));
 		Ok(Tip {
 			height: height,
 			last_block_h: last,
 			prev_block_h: prev,
-			lineage: line,
+			total_difficulty: diff,
 		})
 	}
 }
@@ -148,9 +107,6 @@ pub trait ChainStore: Send + Sync {
 
 	/// Save the provided tip as the current head of our chain
 	fn save_head(&self, t: &Tip) -> Result<(), Error>;
-
-	/// Save the provided tip without setting it as head
-	fn save_tip(&self, t: &Tip) -> Result<(), Error>;
 }
 
 /// Bridge between the chain pipeline and the rest of the system. Handles
diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs
index 596d178f1..c13f0cbb4 100644
--- a/chain/tests/mine_simple_chain.rs
+++ b/chain/tests/mine_simple_chain.rs
@@ -23,7 +23,6 @@ use rand::os::OsRng;
 
 use grin_chain::types::*;
 use grin_core::core::hash::Hashed;
-use grin_core::core::target::Difficulty;
 use grin_core::pow;
 use grin_core::core;
 use grin_core::consensus;
@@ -33,42 +32,46 @@ fn mine_empty_chain() {
 	let mut rng = OsRng::new().unwrap();
 	let store = grin_chain::store::ChainKVStore::new(".grin".to_string()).unwrap();
 
-  // save a genesis block
-  let mut gen = grin_core::genesis::genesis(); 
-  gen.header.cuckoo_len = 16;
-  store.save_block(&gen).unwrap();
+	// save a genesis block
+	let mut gen = grin_core::genesis::genesis();
+	gen.header.cuckoo_len = 16;
+	let diff = gen.header.difficulty.clone();
+	pow::pow(&mut gen, diff).unwrap();
+	store.save_block(&gen).unwrap();
 
-  // setup a new head tip
-  let tip = Tip::new(gen.hash());
-  store.save_head(&tip).unwrap();
+	// setup a new head tip
+	let tip = Tip::new(gen.hash());
+	store.save_head(&tip).unwrap();
 
-  // mine and add a few blocks
-  let mut prev = gen;
+	// mine and add a few blocks
+	let mut prev = gen;
 	let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
 	let reward_key = secp::key::SecretKey::new(&secp, &mut rng);
-  let arc_store = Arc::new(store);
-  let adapter = Arc::new(NoopAdapter{});
+	let arc_store = Arc::new(store);
+	let adapter = Arc::new(NoopAdapter {});
 
-  for n in 1..4 {
-    let mut b = core::Block::new(&prev.header, vec![], reward_key).unwrap();
+	for n in 1..4 {
+		let mut b = core::Block::new(&prev.header, vec![], reward_key).unwrap();
 		b.header.timestamp = prev.header.timestamp + time::Duration::seconds(60);
 
-    let (difficulty, _) = consensus::next_target(b.header.timestamp.to_timespec().sec,
-                                                      prev.header.timestamp.to_timespec().sec,
-                                                      prev.header.difficulty.clone(),
-                                                      prev.header.cuckoo_len);
+		let (difficulty, _) = consensus::next_target(b.header.timestamp.to_timespec().sec,
+		                                             prev.header.timestamp.to_timespec().sec,
+		                                             prev.header.difficulty.clone(),
+		                                             prev.header.cuckoo_len);
+		b.header.difficulty = difficulty.clone();
 
-    let (proof, nonce) = pow::pow_size(&b, difficulty.clone(), prev.header.cuckoo_len as u32).unwrap();
-    b.header.pow = proof;
-    b.header.nonce = nonce;
-    b.header.difficulty = difficulty;
-    grin_chain::pipe::process_block(&b, arc_store.clone(), adapter.clone(), grin_chain::pipe::EASY_POW).unwrap();
+		pow::pow(&mut b, difficulty).unwrap();
+		grin_chain::pipe::process_block(&b,
+		                                arc_store.clone(),
+		                                adapter.clone(),
+		                                grin_chain::pipe::EASY_POW)
+			.unwrap();
 
-    // checking our new head
-    let head = arc_store.clone().head().unwrap();
-    assert_eq!(head.height, n);
-    assert_eq!(head.last_block_h, b.hash());
+		// checking our new head
+		let head = arc_store.clone().head().unwrap();
+		assert_eq!(head.height, n);
+		assert_eq!(head.last_block_h, b.hash());
 
-    prev = b;
-  }
+		prev = b;
+	}
 }
diff --git a/core/src/core/block.rs b/core/src/core/block.rs
index 90e32910a..97c210544 100644
--- a/core/src/core/block.rs
+++ b/core/src/core/block.rs
@@ -78,14 +78,14 @@ impl Writeable for BlockHeader {
 		                [write_fixed_bytes, &self.utxo_merkle],
 		                [write_fixed_bytes, &self.tx_merkle]);
 
-    try!(writer.write_u64(self.nonce));
+		try!(writer.write_u64(self.nonce));
 		try!(self.difficulty.write(writer));
 		try!(self.total_difficulty.write(writer));
 
-    if writer.serialization_mode() != ser::SerializationMode::Hash {
-      try!(self.pow.write(writer));
-    }
-    Ok(())
+		if writer.serialization_mode() != ser::SerializationMode::Hash {
+			try!(self.pow.write(writer));
+		}
+		Ok(())
 	}
 }
 
@@ -97,9 +97,9 @@ impl Readable<BlockHeader> for BlockHeader {
 		let (timestamp, cuckoo_len) = ser_multiread!(reader, read_i64, read_u8);
 		let utxo_merkle = try!(Hash::read(reader));
 		let tx_merkle = try!(Hash::read(reader));
+		let nonce = try!(reader.read_u64());
 		let difficulty = try!(Difficulty::read(reader));
 		let total_difficulty = try!(Difficulty::read(reader));
-		let nonce = try!(reader.read_u64());
 		let pow = try!(Proof::read(reader));
 
 		Ok(BlockHeader {
@@ -139,22 +139,22 @@ impl Writeable for Block {
 	fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> {
 		try!(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.proofs.len() as u64]);
+		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.proofs.len() as u64]);
 
-      for inp in &self.inputs {
-        try!(inp.write(writer));
-      }
-      for out in &self.outputs {
-        try!(out.write(writer));
-      }
-      for proof in &self.proofs {
-        try!(proof.write(writer));
-      }
-    }
+			for inp in &self.inputs {
+				try!(inp.write(writer));
+			}
+			for out in &self.outputs {
+				try!(out.write(writer));
+			}
+			for proof in &self.proofs {
+				try!(proof.write(writer));
+			}
+		}
 		Ok(())
 	}
 }
@@ -254,8 +254,7 @@ impl Block {
 					height: prev.height + 1,
 					timestamp: time::now(),
 					previous: prev.hash(),
-					total_difficulty: Difficulty::from_hash(&prev.hash()) +
-					                  prev.total_difficulty.clone(),
+					total_difficulty: prev.pow.to_difficulty() + prev.total_difficulty.clone(),
 					cuckoo_len: prev.cuckoo_len,
 					..Default::default()
 				},
diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs
index 07986d3e4..b5ed3ad40 100644
--- a/core/src/pow/mod.rs
+++ b/core/src/pow/mod.rs
@@ -54,7 +54,7 @@ pub fn verify_size(b: &Block, cuckoo_sz: u32) -> bool {
 /// block, until the required difficulty target is reached. May take a
 /// while for a low target...
 pub fn pow(b: &mut Block, diff: Difficulty) -> Result<(), Error> {
-  let cuckoo_len = b.header.cuckoo_len as u32;
+	let cuckoo_len = b.header.cuckoo_len as u32;
 	pow_size(b, diff, cuckoo_len)
 }
 
@@ -77,7 +77,7 @@ pub fn pow_size(b: &mut Block, diff: Difficulty, sizeshift: u32) -> Result<(), E
 		// diff, we're all good
 		if let Ok(proof) = Miner::new(pow_hash.to_slice(), EASINESS, sizeshift).mine() {
 			if proof.to_difficulty() >= diff {
-        b.header.pow = proof;
+				b.header.pow = proof;
 				return Ok(());
 			}
 		}
@@ -88,7 +88,7 @@ pub fn pow_size(b: &mut Block, diff: Difficulty, sizeshift: u32) -> Result<(), E
 		// and if we're back where we started, update the time (changes the hash as
 		// well)
 		if b.header.nonce == start_nonce {
-		  b.header.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 });
+			b.header.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 });
 		}
 	}
 }
@@ -103,7 +103,7 @@ mod test {
 	#[test]
 	fn genesis_pow() {
 		let mut b = genesis::genesis();
-    b.header.nonce = 310;
+		b.header.nonce = 310;
 		pow20(&mut b, Difficulty::one()).unwrap();
 		assert!(b.header.nonce != 310);
 		assert!(b.header.pow.to_difficulty() >= Difficulty::one());
diff --git a/doc/pruning.md b/doc/pruning.md
index 90c3893c1..33bb77edb 100644
--- a/doc/pruning.md
+++ b/doc/pruning.md
@@ -36,8 +36,8 @@ There may be several contexts in which data can be pruned:
 
 * A fully validating node may get rid of some data it has already validated to
 free space.
-* A partially validating node (similar to SPV) may not do full validation and
-hence not be interested in either receiving or keeping all the data.
+* A partially validating node (similar to SPV) may not be interested in either
+receiving or keeping all the data.
 * When a new node joins the network, it may temporarily behave as a partially
 validating node to make it available for use faster, even if it ultimately becomes
 a fully validating node.
diff --git a/grin/src/miner.rs b/grin/src/miner.rs
index 30840f213..52967576c 100644
--- a/grin/src/miner.rs
+++ b/grin/src/miner.rs
@@ -63,7 +63,6 @@ impl Miner {
 				latest_hash = self.chain_head.lock().unwrap().last_block_h;
 			}
 			let mut b = self.build_block(&head);
-			let mut pow_header = pow::PowHeader::from_block(&b);
 
 			// look for a pow for at most 2 sec on the same block (to give a chance to new
 			// transactions) and as long as the head hasn't changed
@@ -74,7 +73,7 @@ impl Miner {
 			       latest_hash);
 			let mut iter_count = 0;
 			while head.hash() == latest_hash && time::get_time().sec < deadline {
-				let pow_hash = pow_header.hash();
+				let pow_hash = b.hash();
 				let mut miner = cuckoo::Miner::new(pow_hash.to_slice(),
 				                                   consensus::EASINESS,
 				                                   b.header.cuckoo_len as u32);
@@ -84,7 +83,7 @@ impl Miner {
 						break;
 					}
 				}
-				pow_header.nonce += 1;
+				b.header.nonce += 1;
 				{
 					latest_hash = self.chain_head.lock().unwrap().last_block_h;
 				}
@@ -95,7 +94,6 @@ impl Miner {
 			if let Some(proof) = sol {
 				info!("Found valid proof of work, adding block {}.", b.hash());
 				b.header.pow = proof;
-				b.header.nonce = pow_header.nonce;
 				let res = chain::process_block(&b,
 				                               self.chain_store.clone(),
 				                               self.chain_adapter.clone(),