diff --git a/api/src/types.rs b/api/src/types.rs
index 6d7faf013..9bc0d0be1 100644
--- a/api/src/types.rs
+++ b/api/src/types.rs
@@ -21,7 +21,6 @@ use chain;
 use p2p;
 use util;
 use util::secp::pedersen;
-use util::secp::constants::MAX_PROOF_SIZE;
 use serde;
 use serde::ser::SerializeStruct;
 use serde::de::MapAccess;
@@ -230,7 +229,7 @@ pub struct OutputPrintable {
 	/// Whether the output has been spent
 	pub spent: bool,
 	/// Rangeproof (as hex string)
-	pub proof: Option<pedersen::RangeProof>,
+	pub proof: Option<String>,
 	/// Rangeproof hash (as hex string)
 	pub proof_hash: String,
 
@@ -257,7 +256,7 @@ impl OutputPrintable {
 		let spent = chain.is_unspent(&out_id).is_err();
 
 		let proof = if include_proof {
-			Some(output.proof)
+			Some(util::to_hex(output.proof.proof.to_vec()))
 		} else {
 			None
 		};
@@ -289,9 +288,19 @@ impl OutputPrintable {
 	}
 
 	pub fn range_proof(&self) -> Result<pedersen::RangeProof, ser::Error> {
-		self.proof
+		let proof_str = self.proof
 			.clone()
 			.ok_or_else(|| ser::Error::HexError(format!("output range_proof missing")))
+			.unwrap();
+		let p_vec = util::from_hex(proof_str).unwrap();
+		let mut p_bytes = [0; util::secp::constants::MAX_PROOF_SIZE];
+		for i in 0..p_bytes.len() {
+			p_bytes[i] = p_vec[i];
+		}
+		Ok(pedersen::RangeProof {
+			proof: p_bytes,
+			plen: p_bytes.len(),
+		})
 	}
 }
 
@@ -370,22 +379,7 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
 						}
 						Field::Proof => {
 							no_dup!(proof);
-
-							let val: Option<String> = map.next_value()?;
-
-							if val.is_some() {
-								let vec = util::from_hex(val.unwrap().clone())
-									.map_err(serde::de::Error::custom)?;
-								let mut bytes = [0; MAX_PROOF_SIZE];
-								for i in 0..vec.len() {
-									bytes[i] = vec[i];
-								}
-
-								proof = Some(pedersen::RangeProof {
-									proof: bytes,
-									plen: vec.len(),
-								})
-							}
+							proof = map.next_value()?
 						}
 						Field::ProofHash => {
 							no_dup!(proof_hash);
@@ -415,13 +409,8 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
 			}
 		}
 
-		const FIELDS: &'static [&'static str] = &[
-			"output_type",
-			"commit",
-			"spent",
-			"proof",
-			"proof_hash",
-		];
+		const FIELDS: &'static [&'static str] =
+			&["output_type", "commit", "spent", "proof", "proof_hash"];
 		deserializer.deserialize_struct("OutputPrintable", FIELDS, OutputPrintableVisitor)
 	}
 }
diff --git a/core/src/core/block.rs b/core/src/core/block.rs
index 35cab27d4..99869e04e 100644
--- a/core/src/core/block.rs
+++ b/core/src/core/block.rs
@@ -18,7 +18,7 @@ use time;
 use rand::{thread_rng, Rng};
 use std::collections::HashSet;
 
-use core::{Committed, Input, KernelFeatures, Output, OutputFeatures, Proof,
+use core::{Committed, Input, KernelFeatures, Output, OutputFeatures, Proof, ProofMessageElements,
            ShortId, Transaction, TxKernel};
 use consensus;
 use consensus::{exceeds_weight, reward, VerifySortOrder, REWARD};
@@ -169,7 +169,7 @@ impl Readable for BlockHeader {
 		let nonce = reader.read_u64()?;
 		let pow = Proof::read(reader)?;
 
-		if timestamp > (1 << 55) ||  timestamp < -(1 << 55) {
+		if timestamp > (1 << 55) || timestamp < -(1 << 55) {
 			return Err(ser::Error::CorruptedData);
 		}
 
@@ -784,20 +784,13 @@ impl Block {
 		fees: u64,
 		height: u64,
 	) -> Result<(Output, TxKernel), keychain::Error> {
-		let commit = keychain.commit(reward(fees), key_id)?;
+		let value = reward(fees);
+		let commit = keychain.commit(value, key_id)?;
+		let msg = ProofMessageElements::new(value, key_id);
 
-		trace!(
-			LOGGER,
-			"Block reward - Pedersen Commit is: {:?}",
-			commit,
-		);
+		trace!(LOGGER, "Block reward - Pedersen Commit is: {:?}", commit,);
 
-		let rproof = keychain.range_proof(
-			reward(fees),
-			key_id,
-			commit,
-			None,
-		)?;
+		let rproof = keychain.range_proof(value, key_id, commit, None, msg.to_proof_message())?;
 
 		let output = Output {
 			features: OutputFeatures::COINBASE_OUTPUT,
diff --git a/core/src/core/build.rs b/core/src/core/build.rs
index a57072dfc..737c3f4d0 100644
--- a/core/src/core/build.rs
+++ b/core/src/core/build.rs
@@ -27,7 +27,7 @@
 
 use util::{kernel_sig_msg, secp};
 
-use core::{Input, Output, OutputFeatures, Transaction, TxKernel};
+use core::{Input, Output, OutputFeatures, ProofMessageElements, Transaction, TxKernel};
 use core::hash::Hash;
 use core::pmmr::MerkleProof;
 use keychain;
@@ -101,20 +101,13 @@ pub fn output(value: u64, key_id: Identifier) -> Box<Append> {
 			debug!(LOGGER, "Building an output: {}, {}", value, key_id,);
 
 			let commit = build.keychain.commit(value, &key_id).unwrap();
-			trace!(
-				LOGGER,
-				"Builder - Pedersen Commit is: {:?}",
-				commit,
-			);
+			trace!(LOGGER, "Builder - Pedersen Commit is: {:?}", commit,);
+
+			let msg = ProofMessageElements::new(value, &key_id);
 
 			let rproof = build
 				.keychain
-				.range_proof(
-					value,
-					&key_id,
-					commit,
-					None,
-				)
+				.range_proof(value, &key_id, commit, None, msg.to_proof_message())
 				.unwrap();
 
 			(
diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs
index 672513a19..d97d77de6 100644
--- a/core/src/core/transaction.rs
+++ b/core/src/core/transaction.rs
@@ -15,10 +15,11 @@
 //! Transactions
 use util::secp::{self, Message, Signature};
 use util::{kernel_sig_msg, static_secp_instance};
-use util::secp::pedersen::{Commitment, RangeProof};
+use util::secp::pedersen::{Commitment, ProofMessage, RangeProof};
 use std::cmp::max;
 use std::cmp::Ordering;
 use std::{error, fmt};
+use std::io::Cursor;
 
 use consensus;
 use consensus::VerifySortOrder;
@@ -29,7 +30,7 @@ use core::hash::{Hash, Hashed, ZERO_HASH};
 use core::pmmr::MerkleProof;
 use keychain;
 use keychain::{BlindingFactor, Keychain};
-use ser::{self, read_and_verify_sorted, PMMRable, Readable, Reader, Writeable,
+use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable,
           WriteableSorted, Writer};
 use util;
 use util::LOGGER;
@@ -95,6 +96,9 @@ pub enum Error {
 	/// Error originating from an input attempting to spend an immature
 	/// coinbase output
 	ImmatureCoinbase,
+	/// Returns if the value hidden within the a RangeProof message isn't
+	/// repeated 3 times, indicating it's incorrect
+	InvalidProofMessage,
 }
 
 impl error::Error for Error {
@@ -739,12 +743,7 @@ impl Output {
 	pub fn verify_proof(&self) -> Result<(), secp::Error> {
 		let secp = static_secp_instance();
 		let secp = secp.lock().unwrap();
-		match Keychain::verify_range_proof(
-			&secp,
-			self.commit,
-			self.proof,
-			None,
-		) {
+		match Keychain::verify_range_proof(&secp, self.commit, self.proof, None) {
 			Ok(_) => Ok(()),
 			Err(e) => Err(e),
 		}
@@ -786,7 +785,7 @@ impl OutputIdentifier {
 			features: self.features,
 			commit: self.commit,
 			proof: proof,
-		}	
+		}
 	}
 
 	/// Build an output_identifier from an existing input.
@@ -833,6 +832,114 @@ impl Readable for OutputIdentifier {
 	}
 }
 
+/// A structure which contains fields that are to be commited to within
+/// an Output's range (bullet) proof.
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+pub struct ProofMessageElements {
+	/// The amount, stored to allow for wallet reconstruction as
+	/// rewinding isn't supported in bulletproofs just yet
+	/// This is going to be written 3 times, to facilitate checking
+	/// values on rewind
+	/// Note that rewinding with only the nonce will give you back
+	/// the first 32 bytes of the message. To get the second
+	/// 32 bytes, you need to provide the correct blinding factor as well
+	value: u64,
+	/// another copy of the value, to check on rewind
+	value_copy_1: u64,
+	/// another copy of the value
+	value_copy_2: u64,
+	/// the first 8 bytes of the blinding factor, used to avoid having to grind
+	/// through a proof each time you want to check against key possibilities
+	bf_first_8: Vec<u8>,
+	/// unused portion of message, used to test whether we have both nonce
+	/// and blinding correct
+	zeroes: Vec<u8>,
+}
+
+impl Writeable for ProofMessageElements {
+	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
+		writer.write_u64(self.value)?;
+		writer.write_u64(self.value_copy_1)?;
+		writer.write_u64(self.value_copy_2)?;
+		writer.write_fixed_bytes(&self.bf_first_8)?;
+		for i in 0..32 {
+			let _ = writer.write_u8(self.zeroes[i]);
+		}
+		Ok(())
+	}
+}
+
+impl Readable for ProofMessageElements {
+	fn read(reader: &mut Reader) -> Result<ProofMessageElements, ser::Error> {
+		// if the value isn't repeated 3 times, it's most likely not the value,
+		// so reject
+		Ok(ProofMessageElements {
+			value: reader.read_u64()?,
+			value_copy_1: reader.read_u64()?,
+			value_copy_2: reader.read_u64()?,
+			bf_first_8: reader.read_fixed_bytes(8)?,
+			zeroes: reader.read_fixed_bytes(32)?,
+		})
+	}
+}
+
+impl ProofMessageElements {
+	/// Create a new proof message
+	pub fn new(value: u64, blinding: &keychain::Identifier) -> ProofMessageElements {
+		ProofMessageElements {
+			value: value,
+			value_copy_1: value,
+			value_copy_2: value,
+			bf_first_8: blinding.to_bytes()[0..8].to_vec(),
+			zeroes: [0u8; 32].to_vec(),
+		}
+	}
+
+	/// Return the value if it's valid, an error otherwise
+	pub fn value(&self) -> Result<u64, Error> {
+		if self.value == self.value_copy_1 && self.value == self.value_copy_2 {
+			Ok(self.value)
+		} else {
+			Err(Error::InvalidProofMessage)
+		}
+	}
+
+	/// Compare given identifier with first 8 bytes of what's stored
+	pub fn compare_bf_first_8(&self, in_id: &keychain::Identifier) -> bool {
+		let in_id_vec = in_id.to_bytes()[0..8].to_vec();
+		for i in 0..8 {
+			if in_id_vec[i] != self.bf_first_8[i] {
+				return false;
+			}
+		}
+		true
+	}
+
+	/// Whether our remainder is zero (as it should be if the BF and nonce used to unwind
+	/// are correct
+	pub fn zeroes_correct(&self) -> bool {
+		for i in 0..self.zeroes.len() {
+			if self.zeroes[i] != 0 {
+				return false;
+			}
+		}
+		true
+	}
+
+	/// Serialise and return a ProofMessage
+	pub fn to_proof_message(&self) -> ProofMessage {
+		ProofMessage::from_bytes(&ser_vec(self).unwrap())
+	}
+
+	/// Deserialise and return the message elements
+	pub fn from_proof_message(
+		proof_message: ProofMessage,
+	) -> Result<ProofMessageElements, ser::Error> {
+		let mut c = Cursor::new(proof_message.as_bytes());
+		ser::deserialize::<ProofMessageElements>(&mut c)
+	}
+}
+
 #[cfg(test)]
 mod test {
 	use super::*;
@@ -891,14 +998,7 @@ mod test {
 		let key_id = keychain.derive_key_id(1).unwrap();
 		let commit = keychain.commit(5, &key_id).unwrap();
 		let msg = secp::pedersen::ProofMessage::empty();
-		let proof = keychain
-			.range_proof(
-				5,
-				&key_id,
-				commit,
-				None,
-			)
-			.unwrap();
+		let proof = keychain.range_proof(5, &key_id, commit, None, msg).unwrap();
 
 		let out = Output {
 			features: OutputFeatures::DEFAULT_OUTPUT,
diff --git a/grin/tests/simulnet.rs b/grin/tests/simulnet.rs
index aa7e9af01..594b68e3e 100644
--- a/grin/tests/simulnet.rs
+++ b/grin/tests/simulnet.rs
@@ -188,7 +188,7 @@ fn simulate_block_propagation() {
 	// instantiates 5 servers on different ports
 	let mut servers = vec![];
 	for n in 0..5 {
-		let s = grin::Server::new(config(10*n, test_name_dir, 0)).unwrap();
+		let s = grin::Server::new(config(10 * n, test_name_dir, 0)).unwrap();
 		servers.push(s);
 		thread::sleep(time::Duration::from_millis(100));
 	}
diff --git a/keychain/src/extkey.rs b/keychain/src/extkey.rs
index 52ce757a4..5bf0a3668 100644
--- a/keychain/src/extkey.rs
+++ b/keychain/src/extkey.rs
@@ -126,6 +126,10 @@ impl Identifier {
 		Identifier(identifier)
 	}
 
+	pub fn to_bytes(&self) -> [u8; IDENTIFIER_SIZE] {
+		self.0.clone()
+	}
+
 	pub fn from_pubkey(secp: &Secp256k1, pubkey: &PublicKey) -> Identifier {
 		let bytes = pubkey.serialize_vec(secp, true);
 		let identifier = blake2b(IDENTIFIER_SIZE, &[], &bytes[..]);
diff --git a/keychain/src/keychain.rs b/keychain/src/keychain.rs
index 038dd29df..6a63c58ef 100644
--- a/keychain/src/keychain.rs
+++ b/keychain/src/keychain.rs
@@ -214,15 +214,42 @@ impl Keychain {
 		Ok(commit)
 	}
 
+	pub fn rangeproof_create_nonce(&self, commit: &Commitment) -> SecretKey {
+		// hash(commit|masterkey) as nonce
+		let root_key = self.root_key_id().to_bytes();
+		let res = blake2::blake2b::blake2b(32, &commit.0, &root_key);
+		let res = res.as_bytes();
+		let mut ret_val = [0; 32];
+		for i in 0..res.len() {
+			ret_val[i] = res[i];
+		}
+		SecretKey::from_slice(&self.secp, &ret_val).unwrap()
+	}
+
 	pub fn range_proof(
 		&self,
 		amount: u64,
 		key_id: &Identifier,
 		_commit: Commitment,
 		extra_data: Option<Vec<u8>>,
+		msg: ProofMessage,
 	) -> Result<RangeProof, Error> {
+		let commit = self.commit(amount, key_id)?;
 		let skey = self.derived_key(key_id)?;
-		Ok(self.secp.bullet_proof(amount, skey, extra_data, None))
+		let nonce = self.rangeproof_create_nonce(&commit);
+		if msg.len() == 0 {
+			return Ok(self.secp
+				.bullet_proof(amount, skey, nonce, extra_data, None));
+		} else {
+			if msg.len() != 64 {
+				error!(LOGGER, "Bullet proof message must be 64 bytes.");
+				return Err(Error::RangeProof(
+					"Bullet proof message must be 64 bytes".to_string(),
+				));
+			}
+		}
+		return Ok(self.secp
+			.bullet_proof(amount, skey, nonce, extra_data, Some(msg)));
 	}
 
 	pub fn verify_range_proof(
@@ -245,9 +272,10 @@ impl Keychain {
 		extra_data: Option<Vec<u8>>,
 		proof: RangeProof,
 	) -> Result<ProofInfo, Error> {
-		let nonce = self.derived_key(key_id)?;
+		let skey = self.derived_key(key_id)?;
+		let nonce = self.rangeproof_create_nonce(&commit);
 		let proof_message = self.secp
-			.unwind_bullet_proof(commit, nonce, nonce, extra_data, proof);
+			.unwind_bullet_proof(commit, skey, nonce, extra_data, proof);
 		let proof_info = match proof_message {
 			Ok(p) => ProofInfo {
 				success: true,
@@ -975,4 +1003,75 @@ mod test {
 			assert!(sig_verifies);
 		}
 	}
+
+	#[test]
+	fn test_rewind_range_proof() {
+		let keychain = Keychain::from_random_seed().unwrap();
+		let key_id = keychain.derive_key_id(1).unwrap();
+		let commit = keychain.commit(5, &key_id).unwrap();
+		let mut msg = ProofMessage::from_bytes(&[0u8; 64]);
+		let extra_data = [99u8; 64];
+
+		let proof = keychain
+			.range_proof(5, &key_id, commit, Some(extra_data.to_vec().clone()), msg)
+			.unwrap();
+		let proof_info = keychain
+			.rewind_range_proof(&key_id, commit, Some(extra_data.to_vec().clone()), proof)
+			.unwrap();
+
+		assert_eq!(proof_info.success, true);
+
+		// now check the recovered message is "empty" (but not truncated) i.e. all
+		// zeroes
+		//Value is in the message in this case
+		assert_eq!(
+			proof_info.message,
+			secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::BULLET_PROOF_MSG_SIZE])
+		);
+
+		let key_id2 = keychain.derive_key_id(2).unwrap();
+
+		// cannot rewind with a different nonce
+		let proof_info = keychain
+			.rewind_range_proof(&key_id2, commit, Some(extra_data.to_vec().clone()), proof)
+			.unwrap();
+		// With bullet proofs, if you provide the wrong nonce you'll get gibberish back
+		// as opposed to a failure to recover the message
+		assert_ne!(
+			proof_info.message,
+			secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::BULLET_PROOF_MSG_SIZE])
+		);
+		assert_eq!(proof_info.value, 0);
+
+		// cannot rewind with a commitment to the same value using a different key
+		let commit2 = keychain.commit(5, &key_id2).unwrap();
+		let proof_info = keychain
+			.rewind_range_proof(&key_id, commit2, Some(extra_data.to_vec().clone()), proof)
+			.unwrap();
+		assert_eq!(proof_info.success, false);
+		assert_eq!(proof_info.value, 0);
+
+		// cannot rewind with a commitment to a different value
+		let commit3 = keychain.commit(4, &key_id).unwrap();
+		let proof_info = keychain
+			.rewind_range_proof(&key_id, commit3, Some(extra_data.to_vec().clone()), proof)
+			.unwrap();
+		assert_eq!(proof_info.success, false);
+		assert_eq!(proof_info.value, 0);
+
+		// cannot rewind with wrong extra committed data
+		let commit3 = keychain.commit(4, &key_id).unwrap();
+		let wrong_extra_data = [98u8; 64];
+		let should_err = keychain
+			.rewind_range_proof(
+				&key_id,
+				commit3,
+				Some(wrong_extra_data.to_vec().clone()),
+				proof,
+			)
+			.unwrap();
+
+		assert_eq!(proof_info.success, false);
+		assert_eq!(proof_info.value, 0);
+	}
 }
diff --git a/p2p/src/serv.rs b/p2p/src/serv.rs
index c93c575a4..1c9bccb50 100644
--- a/p2p/src/serv.rs
+++ b/p2p/src/serv.rs
@@ -205,7 +205,7 @@ impl ChainAdapter for DummyAdapter {
 	fn total_height(&self) -> u64 {
 		0
 	}
-	fn transaction_received(&self, _: core::Transaction, stem: bool) {}
+	fn transaction_received(&self, _: core::Transaction, _stem: bool) {}
 	fn compact_block_received(&self, _cb: core::CompactBlock, _addr: SocketAddr) -> bool {
 		true
 	}
diff --git a/pool/src/graph.rs b/pool/src/graph.rs
index 9c1de5302..1bf13ba47 100644
--- a/pool/src/graph.rs
+++ b/pool/src/graph.rs
@@ -297,6 +297,7 @@ mod tests {
 	use keychain::Keychain;
 	use rand;
 	use core::core::OutputFeatures;
+	use core::core::transaction::ProofMessageElements;
 
 	#[test]
 	fn test_add_entry() {
@@ -322,16 +323,13 @@ mod tests {
 			),
 		];
 
+		let msg = ProofMessageElements::new(100, &key_id1);
+
 		let output = core::transaction::Output {
 			features: OutputFeatures::DEFAULT_OUTPUT,
 			commit: output_commit,
 			proof: keychain
-				.range_proof(
-					100,
-					&key_id1,
-					output_commit,
-					None,
-				)
+				.range_proof(100, &key_id1, output_commit, None, msg.to_proof_message())
 				.unwrap(),
 		};
 
diff --git a/pool/src/pool.rs b/pool/src/pool.rs
index 1243b6cea..1cb1d0735 100644
--- a/pool/src/pool.rs
+++ b/pool/src/pool.rs
@@ -859,6 +859,7 @@ mod tests {
 	use core::core::hash::{Hash, Hashed};
 	use core::core::pmmr::MerkleProof;
 	use core::core::target::Difficulty;
+	use core::core::transaction::ProofMessageElements;
 	use types::PoolError::InvalidTx;
 
 	macro_rules! expect_output_parent {
@@ -1710,9 +1711,11 @@ mod tests {
 	fn test_output(value: u64) -> transaction::Output {
 		let keychain = keychain_for_tests();
 		let key_id = keychain.derive_key_id(value as u32).unwrap();
+		let msg = ProofMessageElements::new(value, &key_id);
 		let commit = keychain.commit(value, &key_id).unwrap();
-		let proof = keychain.range_proof(value, &key_id, commit, None).unwrap();
-
+		let proof = keychain
+			.range_proof(value, &key_id, commit, None, msg.to_proof_message())
+			.unwrap();
 		transaction::Output {
 			features: transaction::OutputFeatures::DEFAULT_OUTPUT,
 			commit: commit,
@@ -1724,9 +1727,11 @@ mod tests {
 	fn test_coinbase_output(value: u64) -> transaction::Output {
 		let keychain = keychain_for_tests();
 		let key_id = keychain.derive_key_id(value as u32).unwrap();
+		let msg = ProofMessageElements::new(value, &key_id);
 		let commit = keychain.commit(value, &key_id).unwrap();
-		let proof = keychain.range_proof(value, &key_id, commit, None).unwrap();
-
+		let proof = keychain
+			.range_proof(value, &key_id, commit, None, msg.to_proof_message())
+			.unwrap();
 		transaction::Output {
 			features: transaction::OutputFeatures::COINBASE_OUTPUT,
 			commit: commit,
diff --git a/src/bin/grin.rs b/src/bin/grin.rs
index 36390e832..e63a0f2d8 100644
--- a/src/bin/grin.rs
+++ b/src/bin/grin.rs
@@ -251,7 +251,11 @@ fn main() {
 			.about("basic wallet contents summary"))
 
 		.subcommand(SubCommand::with_name("init")
-			.about("Initialize a new wallet seed file.")))
+			.about("Initialize a new wallet seed file."))
+
+		.subcommand(SubCommand::with_name("restore")
+			.about("Attempt to restore wallet contents from the chain using seed and password. \
+				NOTE: Backup wallet.* and run `wallet listen` before running restore.")))
 
 	.get_matches();
 
@@ -490,6 +494,12 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) {
 		wallet_config.check_node_api_http_addr = sa.to_string().clone();
 	}
 
+	let key_derivations: u32 = wallet_args
+		.value_of("key_derivations")
+		.unwrap()
+		.parse()
+		.unwrap();
+
 	let mut show_spent = false;
 	if wallet_args.is_present("show_spent") {
 		show_spent = true;
@@ -611,6 +621,9 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) {
 		("outputs", Some(_)) => {
 			wallet::show_outputs(&wallet_config, &keychain, show_spent);
 		}
+		("restore", Some(_)) => {
+			let _ = wallet::restore(&wallet_config, &keychain, key_derivations);
+		}
 		_ => panic!("Unknown wallet command, use 'grin help wallet' for details"),
 	}
 }
diff --git a/util/Cargo.toml b/util/Cargo.toml
index 411f10e9d..b5e5c5557 100644
--- a/util/Cargo.toml
+++ b/util/Cargo.toml
@@ -20,6 +20,6 @@ zip = "^0.2.6"
 
 [dependencies.secp256k1zkp]
 git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
-tag = "grin_integration_15"
+tag = "grin_integration_16"
 #path = "../../rust-secp256k1-zkp"
 features = ["bullet-proof-sizing"]
diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs
index ce76ef60d..de7556f51 100644
--- a/wallet/src/lib.rs
+++ b/wallet/src/lib.rs
@@ -53,6 +53,7 @@ mod info;
 mod receiver;
 mod sender;
 mod types;
+mod restore;
 pub mod client;
 pub mod server;
 
@@ -62,3 +63,4 @@ pub use receiver::WalletReceiver;
 pub use sender::{issue_burn_tx, issue_send_tx};
 pub use types::{BlockFees, CbData, Error, ErrorKind, WalletConfig, WalletInfo,
                 WalletReceiveRequest, WalletSeed};
+pub use restore::restore;
diff --git a/wallet/src/restore.rs b/wallet/src/restore.rs
new file mode 100644
index 000000000..4a35de97d
--- /dev/null
+++ b/wallet/src/restore.rs
@@ -0,0 +1,248 @@
+// Copyright 2018 The Grin Developers
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+use failure::{Fail, ResultExt};
+use keychain::{Identifier, Keychain};
+use util::LOGGER;
+use util::secp::pedersen;
+use api;
+use core::global;
+use core::core::transaction::ProofMessageElements;
+use types::{Error, ErrorKind, OutputData, OutputStatus, WalletConfig, WalletData};
+use byteorder::{BigEndian, ByteOrder};
+
+pub fn get_chain_height(config: &WalletConfig) -> Result<u64, Error> {
+	let url = format!("{}/v1/chain", config.check_node_api_http_addr);
+
+	match api::client::get::<api::Tip>(url.as_str()) {
+		Ok(tip) => Ok(tip.height),
+		Err(e) => {
+			// if we got anything other than 200 back from server, bye
+			error!(
+				LOGGER,
+				"get_chain_height: Restore failed... unable to contact API {}. Error: {}",
+				config.check_node_api_http_addr,
+				e
+			);
+			Err(e.context(ErrorKind::Node).into())
+		}
+	}
+}
+
+fn coinbase_status(output: &api::OutputPrintable) -> bool {
+	match output.output_type {
+		api::OutputType::Coinbase => true,
+		api::OutputType::Transaction => false,
+	}
+}
+
+pub fn outputs_batch_block(
+	config: &WalletConfig,
+	start_height: u64,
+	end_height: u64,
+) -> Result<Vec<api::BlockOutputs>, Error> {
+	let query_param = format!(
+		"start_height={}&end_height={}&include_rp",
+		start_height, end_height
+	);
+
+	let url = format!(
+		"{}/v1/chain/outputs/byheight?{}",
+		config.check_node_api_http_addr, query_param,
+	);
+
+	match api::client::get::<Vec<api::BlockOutputs>>(url.as_str()) {
+		Ok(outputs) => Ok(outputs),
+		Err(e) => {
+			// if we got anything other than 200 back from server, bye
+			error!(
+				LOGGER,
+				"outputs_batch_block: Restore failed... unable to contact API {}. Error: {}",
+				config.check_node_api_http_addr,
+				e
+			);
+			Err(e.context(ErrorKind::Node))?
+		}
+	}
+}
+
+// TODO - wrap the many return values in a struct
+fn find_outputs_with_key(
+	keychain: &Keychain,
+	block_outputs: api::BlockOutputs,
+	key_iterations: &mut usize,
+) -> Vec<(pedersen::Commitment, Identifier, u32, u64, u64, u64, bool)> {
+	let mut wallet_outputs: Vec<(pedersen::Commitment, Identifier, u32, u64, u64, u64, bool)> =
+		Vec::new();
+
+	info!(
+		LOGGER,
+		"Scanning block {}, {} outputs, over {} key derivations",
+		block_outputs.header.height,
+		block_outputs.outputs.len(),
+		*key_iterations,
+	);
+
+	// skey doesn't matter in this case
+	let skey = keychain.derive_key_id(1).unwrap();
+	for output in block_outputs.outputs.iter().filter(|x| !x.spent) {
+		// attempt to unwind message from the RP and get a value.. note
+		// this will only return okay if the value is included in the
+		// message 3 times, indicating a strong match. Also, sec_key provided
+		// to unwind in this case will be meaningless. With only the nonce known
+		// only the first 32 bytes of the recovered message will be accurate
+		let info = keychain
+			.rewind_range_proof(&skey, output.commit, None, output.range_proof().unwrap())
+			.unwrap();
+		let message = ProofMessageElements::from_proof_message(info.message).unwrap();
+		let value = message.value();
+		if value.is_err() {
+			continue;
+		}
+		// we have a match, now check through our key iterations to find a partial match
+		let mut found = false;
+		for i in 1..*key_iterations {
+			let key_id = &keychain.derive_key_id(i as u32).unwrap();
+			if !message.compare_bf_first_8(key_id) {
+				continue;
+			}
+			found = true;
+			// we have a partial match, let's just confirm
+			let info = keychain
+				.rewind_range_proof(key_id, output.commit, None, output.range_proof().unwrap())
+				.unwrap();
+			let message = ProofMessageElements::from_proof_message(info.message).unwrap();
+			let value = message.value();
+			if value.is_err() || !message.zeroes_correct() {
+				continue;
+			}
+			let value = value.unwrap();
+			info!(
+				LOGGER,
+				"Output found: {:?}, key_index: {:?}", output.commit, i,
+			);
+
+			// add it to result set here
+			let commit_id = output.commit.0;
+
+			let is_coinbase = coinbase_status(output);
+
+			info!(LOGGER, "Amount: {}", value);
+
+			let commit = keychain
+				.commit_with_key_index(BigEndian::read_u64(&commit_id), i as u32)
+				.expect("commit with key index");
+
+			let height = block_outputs.header.height;
+			let lock_height = if is_coinbase {
+				height + global::coinbase_maturity()
+			} else {
+				0
+			};
+
+			wallet_outputs.push((
+				commit,
+				key_id.clone(),
+				i as u32,
+				value,
+				height,
+				lock_height,
+				is_coinbase,
+			));
+		}
+		if !found {
+			warn!(
+				LOGGER,
+				"Very probable matching output found with amount: {} \
+				 run restore again with a larger value of key_iterations to claim",
+				message.value().unwrap()
+			);
+		}
+	}
+	debug!(
+		LOGGER,
+		"Found {} wallet_outputs for block {}",
+		wallet_outputs.len(),
+		block_outputs.header.height,
+	);
+
+	wallet_outputs
+}
+
+pub fn restore(
+	config: &WalletConfig,
+	keychain: &Keychain,
+	key_derivations: u32,
+) -> Result<(), Error> {
+	// Don't proceed if wallet.dat has anything in it
+	let is_empty = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
+		Ok(wallet_data.outputs.len() == 0)
+	}).context(ErrorKind::WalletData("could not read wallet"))?;
+	if !is_empty {
+		error!(
+			LOGGER,
+			"Not restoring. Please back up and remove existing wallet.dat first."
+		);
+		return Ok(());
+	}
+
+	// Get height of chain from node (we'll check again when done)
+	let chain_height = get_chain_height(config)?;
+	info!(
+		LOGGER,
+		"Starting restore: Chain height is {}.", chain_height
+	);
+
+	let batch_size = 100;
+	// this will start here, then lower as outputs are found, moving backwards on
+	// the chain
+	let mut key_iterations = key_derivations as usize;
+	let mut h = chain_height;
+	while {
+		let end_batch = h;
+		if h >= batch_size {
+			h -= batch_size;
+		} else {
+			h = 0;
+		}
+		let mut blocks = outputs_batch_block(config, h + 1, end_batch)?;
+		blocks.reverse();
+
+		let _ = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
+			for block in blocks {
+				let result_vec = find_outputs_with_key(keychain, block, &mut key_iterations);
+				if result_vec.len() > 0 {
+					for output in result_vec.clone() {
+						let root_key_id = keychain.root_key_id();
+						// Just plonk it in for now, and refresh actual values via wallet info
+						// command later
+						wallet_data.add_output(OutputData {
+							root_key_id: root_key_id.clone(),
+							key_id: output.1.clone(),
+							n_child: output.2,
+							value: output.3,
+							status: OutputStatus::Unconfirmed,
+							height: output.4,
+							lock_height: output.5,
+							is_coinbase: output.6,
+							block: None,
+							merkle_proof: None,
+						});
+					}
+				}
+			}
+		});
+		h > 0
+	} {}
+	Ok(())
+}