From ca8447f3bd49e80578770da841e5fbbac2c23cde Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Thu, 22 Mar 2018 00:10:11 +0000 Subject: [PATCH] Removed all switch commitment usages, including restore (#841) * Removed all switch commitment usages, including restore * Fixed pool tests * Fix keychain tests * Get rid of the switch key in keychain --- api/src/types.rs | 23 --- chain/src/chain.rs | 5 +- chain/src/txhashset.rs | 19 +- chain/src/types.rs | 4 + core/src/core/block.rs | 38 ++-- core/src/core/build.rs | 20 +- core/src/core/mod.rs | 2 +- core/src/core/pmmr.rs | 1 + core/src/core/transaction.rs | 345 ++--------------------------------- core/src/global.rs | 1 - core/src/ser.rs | 6 - keychain/src/extkey.rs | 34 ---- keychain/src/keychain.rs | 111 +---------- pool/src/graph.rs | 11 +- pool/src/pool.rs | 20 +- src/bin/grin.rs | 15 +- wallet/src/lib.rs | 2 - wallet/src/restore.rs | 319 -------------------------------- 18 files changed, 55 insertions(+), 921 deletions(-) delete mode 100644 wallet/src/restore.rs diff --git a/api/src/types.rs b/api/src/types.rs index 23dd4806b..6d7faf013 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -17,7 +17,6 @@ use std::sync::Arc; use core::{core, ser}; use core::core::hash::Hashed; use core::core::pmmr::MerkleProof; -use core::core::SwitchCommitHash; use chain; use p2p; use util; @@ -228,8 +227,6 @@ pub struct OutputPrintable { /// The homomorphic commitment representing the output's amount /// (as hex string) pub commit: pedersen::Commitment, - /// switch commit hash - pub switch_commit_hash: SwitchCommitHash, /// Whether the output has been spent pub spent: bool, /// Rangeproof (as hex string) @@ -280,7 +277,6 @@ impl OutputPrintable { OutputPrintable { output_type, commit: output.commit, - switch_commit_hash: output.switch_commit_hash, spent, proof, proof_hash: util::to_hex(output.proof.hash().to_vec()), @@ -288,11 +284,6 @@ impl OutputPrintable { } } - // Convert the hex string back into a switch_commit_hash instance - pub fn switch_commit_hash(&self) -> Result { - Ok(self.switch_commit_hash.clone()) - } - pub fn commit(&self) -> Result { Ok(self.commit.clone()) } @@ -312,7 +303,6 @@ impl serde::ser::Serialize for OutputPrintable { let mut state = serializer.serialize_struct("OutputPrintable", 7)?; state.serialize_field("output_type", &self.output_type)?; state.serialize_field("commit", &util::to_hex(self.commit.0.to_vec()))?; - state.serialize_field("switch_commit_hash", &self.switch_commit_hash.to_hex())?; state.serialize_field("spent", &self.spent)?; state.serialize_field("proof", &self.proof)?; state.serialize_field("proof_hash", &self.proof_hash)?; @@ -334,7 +324,6 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable { enum Field { OutputType, Commit, - SwitchCommitHash, Spent, Proof, ProofHash, @@ -356,7 +345,6 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable { { let mut output_type = None; let mut commit = None; - let mut switch_commit_hash = None; let mut spent = None; let mut proof = None; let mut proof_hash = None; @@ -376,14 +364,6 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable { util::from_hex(val.clone()).map_err(serde::de::Error::custom)?; commit = Some(pedersen::Commitment::from_vec(vec)); } - Field::SwitchCommitHash => { - no_dup!(switch_commit_hash); - - let val: String = map.next_value()?; - let hash = core::SwitchCommitHash::from_hex(&val.clone()) - .map_err(serde::de::Error::custom)?; - switch_commit_hash = Some(hash) - } Field::Spent => { no_dup!(spent); spent = Some(map.next_value()?) @@ -427,7 +407,6 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable { Ok(OutputPrintable { output_type: output_type.unwrap(), commit: commit.unwrap(), - switch_commit_hash: switch_commit_hash.unwrap(), spent: spent.unwrap(), proof: proof, proof_hash: proof_hash.unwrap(), @@ -439,7 +418,6 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable { const FIELDS: &'static [&'static str] = &[ "output_type", "commit", - "switch_commit_hash", "spent", "proof", "proof_hash", @@ -645,7 +623,6 @@ mod test { "{\ \"output_type\":\"Coinbase\",\ \"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\",\ - \"switch_commit_hash\":\"85daaf11011dc11e52af84ebe78e2f2d19cbdc76000000000000000000000000\",\ \"spent\":false,\ \"proof\":null,\ \"proof_hash\":\"ed6ba96009b86173bade6a9227ed60422916593fa32dd6d78b25b7a4eeef4946\",\ diff --git a/chain/src/chain.rs b/chain/src/chain.rs index c15e7d686..83159f129 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -20,8 +20,7 @@ use std::fs::File; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; -use core::core::{Block, BlockHeader, Input, OutputFeatures, OutputIdentifier, OutputStoreable, - TxKernel}; +use core::core::{Block, BlockHeader, Input, OutputFeatures, OutputIdentifier, TxKernel}; use core::core::hash::{Hash, Hashed}; use core::core::pmmr::MerkleProof; use core::core::target::Difficulty; @@ -625,7 +624,7 @@ impl Chain { } /// returns the last n nodes inserted into the output sum tree - pub fn get_last_n_output(&self, distance: u64) -> Vec<(Hash, Option)> { + pub fn get_last_n_output(&self, distance: u64) -> Vec<(Hash, Option)> { let mut txhashset = self.txhashset.write().unwrap(); txhashset.last_n_output(distance) } diff --git a/chain/src/txhashset.rs b/chain/src/txhashset.rs index 441d8af00..fbe538ad7 100644 --- a/chain/src/txhashset.rs +++ b/chain/src/txhashset.rs @@ -26,8 +26,7 @@ use util::static_secp_instance; use util::secp::pedersen::{Commitment, RangeProof}; use core::consensus::REWARD; -use core::core::{Block, BlockHeader, Input, Output, OutputFeatures, OutputIdentifier, - OutputStoreable, TxKernel}; +use core::core::{Block, BlockHeader, Input, Output, OutputFeatures, OutputIdentifier, TxKernel}; use core::core::pmmr::{self, MerkleProof, PMMR}; use core::global; use core::core::hash::{Hash, Hashed}; @@ -90,7 +89,7 @@ where /// pruning enabled. pub struct TxHashSet { - output_pmmr_h: PMMRHandle, + output_pmmr_h: PMMRHandle, rproof_pmmr_h: PMMRHandle, kernel_pmmr_h: PMMRHandle, @@ -144,7 +143,7 @@ impl TxHashSet { pub fn is_unspent(&mut self, output_id: &OutputIdentifier) -> Result { match self.commit_index.get_output_pos(&output_id.commit) { Ok(pos) => { - let output_pmmr: PMMR = + let output_pmmr: PMMR = PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos); if let Some((hash, _)) = output_pmmr.get(pos, false) { if hash == output_id.hash_with_index(pos) { @@ -164,8 +163,8 @@ impl TxHashSet { /// returns the last N nodes inserted into the tree (i.e. the 'bottom' /// nodes at level 0 /// TODO: These need to return the actual data from the flat-files instead of hashes now - pub fn last_n_output(&mut self, distance: u64) -> Vec<(Hash, Option)> { - let output_pmmr: PMMR = + pub fn last_n_output(&mut self, distance: u64) -> Vec<(Hash, Option)> { + let output_pmmr: PMMR = PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos); output_pmmr.get_last_n_insertions(distance) } @@ -201,7 +200,7 @@ impl TxHashSet { /// Get sum tree roots /// TODO: Return data instead of hashes pub fn roots(&mut self) -> (Hash, Hash, Hash) { - let output_pmmr: PMMR = + let output_pmmr: PMMR = PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos); let rproof_pmmr: PMMR = PMMR::at(&mut self.rproof_pmmr_h.backend, self.rproof_pmmr_h.last_pos); @@ -298,7 +297,7 @@ where /// reversible manner within a unit of work provided by the `extending` /// function. pub struct Extension<'a> { - output_pmmr: PMMR<'a, OutputStoreable, PMMRBackend>, + output_pmmr: PMMR<'a, OutputIdentifier, PMMRBackend>, rproof_pmmr: PMMR<'a, RangeProof, PMMRBackend>, kernel_pmmr: PMMR<'a, TxKernel, PMMRBackend>, @@ -440,14 +439,14 @@ impl<'a> Extension<'a> { // processing a new fork so we may get a position on the old // fork that exists but matches a different node // filtering that case out - if hash == OutputStoreable::from_output(out).hash() { + if hash == OutputIdentifier::from_output(out).hash() { return Err(Error::DuplicateCommitment(commit)); } } } // push new outputs in their MMR and save them in the index let pos = self.output_pmmr - .push(OutputStoreable::from_output(out)) + .push(OutputIdentifier::from_output(out)) .map_err(&Error::TxHashSetErr)?; self.new_output_commits.insert(out.commitment(), pos); diff --git a/chain/src/types.rs b/chain/src/types.rs index 519d80d9a..373ddcd91 100644 --- a/chain/src/types.rs +++ b/chain/src/types.rs @@ -299,10 +299,14 @@ pub trait ChainStore: Send + Sync { /// Deletes the MMR position of an output. fn delete_output_pos(&self, commit: &[u8]) -> Result<(), store::Error>; + /// Saves a marker associated with a block recording the MMR positions of its + /// last elements. fn save_block_marker(&self, bh: &Hash, marker: &(u64, u64)) -> Result<(), store::Error>; + /// Retrieves a block marker from a block hash. fn get_block_marker(&self, bh: &Hash) -> Result<(u64, u64), store::Error>; + /// Deletes a block marker associated with the provided hash fn delete_block_marker(&self, bh: &Hash) -> Result<(), store::Error>; /// Saves information about the last written PMMR file positions for each diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 16757af61..35cab27d4 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -18,8 +18,8 @@ use time; use rand::{thread_rng, Rng}; use std::collections::HashSet; -use core::{Committed, Input, KernelFeatures, Output, OutputFeatures, Proof, ProofMessageElements, - ShortId, SwitchCommitHash, Transaction, TxKernel}; +use core::{Committed, Input, KernelFeatures, Output, OutputFeatures, Proof, + ShortId, Transaction, TxKernel}; use consensus; use consensus::{exceeds_weight, reward, VerifySortOrder, REWARD}; use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH}; @@ -785,37 +785,23 @@ impl Block { height: u64, ) -> Result<(Output, TxKernel), keychain::Error> { let commit = keychain.commit(reward(fees), key_id)?; - let switch_commit = keychain.switch_commit(key_id)?; - let switch_commit_hash = - SwitchCommitHash::from_switch_commit(switch_commit, keychain, key_id); trace!( LOGGER, - "Block reward - Pedersen Commit is: {:?}, Switch Commit is: {:?}", + "Block reward - Pedersen Commit is: {:?}", commit, - switch_commit ); - trace!( - LOGGER, - "Block reward - Switch Commit Hash is: {:?}", - switch_commit_hash - ); - - let value = reward(fees); - let msg = (ProofMessageElements { value: value }).to_proof_message(); let rproof = keychain.range_proof( - value, + reward(fees), key_id, commit, - Some(switch_commit_hash.as_ref().to_vec()), - msg, + None, )?; let output = Output { features: OutputFeatures::COINBASE_OUTPUT, commit: commit, - switch_commit_hash: switch_commit_hash, proof: rproof, }; @@ -1059,7 +1045,7 @@ mod test { let b = new_block(vec![], &keychain, &prev); let mut vec = Vec::new(); ser::serialize(&mut vec, &b).expect("serialization failed"); - let target_len = 1_248; + let target_len = 1_216; assert_eq!(vec.len(), target_len,); } @@ -1071,8 +1057,8 @@ mod test { let b = new_block(vec![&tx1], &keychain, &prev); let mut vec = Vec::new(); ser::serialize(&mut vec, &b).expect("serialization failed"); - let target_len = 2_892; - assert_eq!(vec.len(), target_len,); + let target_len = 2_796; + assert_eq!(vec.len(), target_len); } #[test] @@ -1082,7 +1068,7 @@ mod test { let b = new_block(vec![], &keychain, &prev); let mut vec = Vec::new(); ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed"); - let target_len = 1_256; + let target_len = 1_224; assert_eq!(vec.len(), target_len,); } @@ -1094,7 +1080,7 @@ mod test { let b = new_block(vec![&tx1], &keychain, &prev); let mut vec = Vec::new(); ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed"); - let target_len = 1_262; + let target_len = 1_230; assert_eq!(vec.len(), target_len,); } @@ -1111,7 +1097,7 @@ mod test { let b = new_block(txs.iter().collect(), &keychain, &prev); let mut vec = Vec::new(); ser::serialize(&mut vec, &b).expect("serialization failed"); - let target_len = 17_688; + let target_len = 17_016; assert_eq!(vec.len(), target_len,); } @@ -1128,7 +1114,7 @@ mod test { let b = new_block(txs.iter().collect(), &keychain, &prev); let mut vec = Vec::new(); ser::serialize(&mut vec, &b.as_compact_block()).expect("serialization failed"); - let target_len = 1_316; + let target_len = 1_284; assert_eq!(vec.len(), target_len,); } diff --git a/core/src/core/build.rs b/core/src/core/build.rs index 432f0a32b..a57072dfc 100644 --- a/core/src/core/build.rs +++ b/core/src/core/build.rs @@ -27,8 +27,7 @@ use util::{kernel_sig_msg, secp}; -use core::{Input, Output, OutputFeatures, ProofMessageElements, SwitchCommitHash, Transaction, - TxKernel}; +use core::{Input, Output, OutputFeatures, Transaction, TxKernel}; use core::hash::Hash; use core::pmmr::MerkleProof; use keychain; @@ -102,22 +101,11 @@ pub fn output(value: u64, key_id: Identifier) -> Box { debug!(LOGGER, "Building an output: {}, {}", value, key_id,); let commit = build.keychain.commit(value, &key_id).unwrap(); - let switch_commit = build.keychain.switch_commit(&key_id).unwrap(); - let switch_commit_hash = - SwitchCommitHash::from_switch_commit(switch_commit, build.keychain, &key_id); trace!( LOGGER, - "Builder - Pedersen Commit is: {:?}, Switch Commit is: {:?}", + "Builder - Pedersen Commit is: {:?}", commit, - switch_commit, ); - trace!( - LOGGER, - "Builder - Switch Commit Hash is: {:?}", - switch_commit_hash - ); - - let msg = (ProofMessageElements { value: value }).to_proof_message(); let rproof = build .keychain @@ -125,8 +113,7 @@ pub fn output(value: u64, key_id: Identifier) -> Box { value, &key_id, commit, - Some(switch_commit_hash.as_ref().to_vec()), - msg, + None, ) .unwrap(); @@ -134,7 +121,6 @@ pub fn output(value: u64, key_id: Identifier) -> Box { tx.with_output(Output { features: OutputFeatures::DEFAULT_OUTPUT, commit: commit, - switch_commit_hash: switch_commit_hash, proof: rproof, }), kern, diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 9956abe26..9b07996b3 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -279,7 +279,7 @@ mod test { let tx = tx2i1o(); let mut vec = Vec::new(); ser::serialize(&mut vec, &tx).expect("serialization failed"); - let target_len = 986; + let target_len = 954; assert_eq!(vec.len(), target_len,); } diff --git a/core/src/core/pmmr.rs b/core/src/core/pmmr.rs index a268e9009..e68feb41c 100644 --- a/core/src/core/pmmr.rs +++ b/core/src/core/pmmr.rs @@ -567,6 +567,7 @@ where } } + /// Prints PMMR statistics to the logs, used for debugging. pub fn dump_stats(&self) { debug!(LOGGER, "pmmr: unpruned - {}", self.unpruned_size()); self.backend.dump_stats(); diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index ef5ad0c11..672513a19 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -13,11 +13,10 @@ // limitations under the License. //! Transactions -use blake2::blake2b::blake2b; use util::secp::{self, Message, Signature}; use util::{kernel_sig_msg, static_secp_instance}; -use util::secp::pedersen::{Commitment, ProofMessage, RangeProof}; -use std::cmp::{max, min}; +use util::secp::pedersen::{Commitment, RangeProof}; +use std::cmp::max; use std::cmp::Ordering; use std::{error, fmt}; @@ -29,20 +28,12 @@ use core::BlockHeader; use core::hash::{Hash, Hashed, ZERO_HASH}; use core::pmmr::MerkleProof; use keychain; -use keychain::{BlindingFactor, Identifier, Keychain}; -use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable, +use keychain::{BlindingFactor, Keychain}; +use ser::{self, read_and_verify_sorted, PMMRable, Readable, Reader, Writeable, WriteableSorted, Writer}; -use std::io::Cursor; use util; use util::LOGGER; -/// The size of the blake2 hash of a switch commitment (256 bits) -pub const SWITCH_COMMIT_HASH_SIZE: usize = 32; - -/// The size of the secret key used in to generate blake2 switch commitment -/// hash (256 bits) -pub const SWITCH_COMMIT_KEY_SIZE: usize = 32; - bitflags! { /// Options for a kernel's structure or use pub struct KernelFeatures: u8 { @@ -97,8 +88,6 @@ pub enum Error { ConsensusError(consensus::Error), /// Error originating from an invalid lock-height LockHeight(u64), - /// Error originating from an invalid switch commitment - SwitchCommitment, /// Range proof validation error RangeProof, /// Error originating from an invalid Merkle proof @@ -494,8 +483,6 @@ impl Transaction { /// A transaction input. /// /// Primarily a reference to an output being spent by the transaction. -/// But also information required to verify coinbase maturity through -/// the lock_height hashed in the switch_commit_hash. #[derive(Debug, Clone)] pub struct Input { /// The features of the output being spent. @@ -678,123 +665,6 @@ bitflags! { } } -/// Definition of the switch commitment hash -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] -pub struct SwitchCommitHashKey([u8; SWITCH_COMMIT_KEY_SIZE]); - -impl SwitchCommitHashKey { - /// We use a zero value key for regular transactions. - pub fn zero() -> SwitchCommitHashKey { - SwitchCommitHashKey([0; SWITCH_COMMIT_KEY_SIZE]) - } - - /// Generate a switch commit hash key from the provided keychain and key id. - pub fn from_keychain(keychain: &Keychain, key_id: &Identifier) -> SwitchCommitHashKey { - SwitchCommitHashKey( - keychain - .switch_commit_hash_key(key_id) - .expect("failed to derive switch commit hash key"), - ) - } - - /// Reconstructs a switch commit hash key from a byte slice. - pub fn from_bytes(bytes: &[u8]) -> SwitchCommitHashKey { - assert!( - bytes.len() == 32, - "switch_commit_hash_key requires 32 bytes" - ); - - let mut key = [0; SWITCH_COMMIT_KEY_SIZE]; - for i in 0..min(SWITCH_COMMIT_KEY_SIZE, bytes.len()) { - key[i] = bytes[i]; - } - SwitchCommitHashKey(key) - } -} - -/// Definition of the switch commitment hash -#[derive(Copy, Clone, Hash, PartialEq, Serialize, Deserialize)] -pub struct SwitchCommitHash([u8; SWITCH_COMMIT_HASH_SIZE]); - -/// Implementation of Writeable for a switch commitment hash -impl Writeable for SwitchCommitHash { - fn write(&self, writer: &mut W) -> Result<(), ser::Error> { - writer.write_fixed_bytes(&self.0)?; - Ok(()) - } -} - -/// Implementation of Readable for a switch commitment hash -/// an Output from a binary stream. -impl Readable for SwitchCommitHash { - fn read(reader: &mut Reader) -> Result { - let a = try!(reader.read_fixed_bytes(SWITCH_COMMIT_HASH_SIZE)); - let mut c = [0; SWITCH_COMMIT_HASH_SIZE]; - for i in 0..SWITCH_COMMIT_HASH_SIZE { - c[i] = a[i]; - } - Ok(SwitchCommitHash(c)) - } -} -// As Ref for AsFixedBytes -impl AsRef<[u8]> for SwitchCommitHash { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl ::std::fmt::Debug for SwitchCommitHash { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - try!(write!(f, "{}(", stringify!(SwitchCommitHash))); - try!(write!(f, "{}", self.to_hex())); - write!(f, ")") - } -} - -impl SwitchCommitHash { - /// Builds a switch commit hash from a switch commit using blake2 - pub fn from_switch_commit( - switch_commit: Commitment, - keychain: &Keychain, - key_id: &Identifier, - ) -> SwitchCommitHash { - let key = SwitchCommitHashKey::from_keychain(keychain, key_id); - let switch_commit_hash = blake2b(SWITCH_COMMIT_HASH_SIZE, &key.0, &switch_commit.0); - let switch_commit_hash_bytes = switch_commit_hash.as_bytes(); - let mut h = [0; SWITCH_COMMIT_HASH_SIZE]; - for i in 0..SWITCH_COMMIT_HASH_SIZE { - h[i] = switch_commit_hash_bytes[i]; - } - SwitchCommitHash(h) - } - - /// Reconstructs a switch commit hash from a byte slice. - pub fn from_bytes(bytes: &[u8]) -> SwitchCommitHash { - let mut hash = [0; SWITCH_COMMIT_HASH_SIZE]; - for i in 0..min(SWITCH_COMMIT_HASH_SIZE, bytes.len()) { - hash[i] = bytes[i]; - } - SwitchCommitHash(hash) - } - - /// Hex string representation of a switch commitment hash. - pub fn to_hex(&self) -> String { - util::to_hex(self.0.to_vec()) - } - - /// Reconstructs a switch commit hash from a hex string. - pub fn from_hex(hex: &str) -> Result { - let bytes = util::from_hex(hex.to_string()) - .map_err(|_| ser::Error::HexError(format!("switch_commit_hash from_hex error")))?; - Ok(SwitchCommitHash::from_bytes(&bytes)) - } - - /// Build an "zero" switch commitment hash - pub fn zero() -> SwitchCommitHash { - SwitchCommitHash([0; SWITCH_COMMIT_HASH_SIZE]) - } -} - /// Output for a transaction, defining the new ownership of coins that are being /// transferred. The commitment is a blinded value for the output while the /// range proof guarantees the commitment includes a positive value without @@ -808,8 +678,6 @@ pub struct Output { pub features: OutputFeatures, /// The homomorphic commitment representing the output amount pub commit: Commitment, - /// The switch commitment hash, a 256 bit length blake2 hash of blind*J - pub switch_commit_hash: SwitchCommitHash, /// A proof that the commitment is in the right range pub proof: RangeProof, } @@ -832,11 +700,6 @@ impl Writeable for Output { fn write(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(self.features.bits())?; self.commit.write(writer)?; - // Hash of an output doesn't cover the switch commit, it should - // be wound into the range proof separately - if writer.serialization_mode() != ser::SerializationMode::Hash { - writer.write_fixed_bytes(&self.switch_commit_hash)?; - } // The hash of an output doesn't include the range proof, which // is commit to separately if writer.serialization_mode() == ser::SerializationMode::Full { @@ -856,7 +719,6 @@ impl Readable for Output { Ok(Output { features: features, commit: Commitment::read(reader)?, - switch_commit_hash: SwitchCommitHash::read(reader)?, proof: RangeProof::read(reader)?, }) } @@ -868,11 +730,6 @@ impl Output { self.commit } - /// Switch commitment hash for the output - pub fn switch_commit_hash(&self) -> SwitchCommitHash { - self.switch_commit_hash - } - /// Range proof for the output pub fn proof(&self) -> RangeProof { self.proof @@ -886,34 +743,12 @@ impl Output { &secp, self.commit, self.proof, - Some(self.switch_commit_hash.as_ref().to_vec()), + None, ) { Ok(_) => Ok(()), Err(e) => Err(e), } } - - /// Given the original blinding factor we can recover the - /// value from the range proof and the commitment - pub fn recover_value(&self, keychain: &Keychain, key_id: &Identifier) -> Option { - match keychain.rewind_range_proof( - key_id, - self.commit, - Some(self.switch_commit_hash.as_ref().to_vec()), - self.proof, - ) { - Ok(proof_info) => { - if proof_info.success { - let elements = - ProofMessageElements::from_proof_message(proof_info.message).unwrap(); - Some(elements.value) - } else { - None - } - } - Err(_) => None, - } - } } /// An output_identifier can be build from either an input _or_ an output and @@ -945,6 +780,15 @@ impl OutputIdentifier { } } + /// Converts this identifier to a full output, provided a RangeProof + pub fn to_output(self, proof: RangeProof) -> Output { + Output { + features: self.features, + commit: self.commit, + proof: proof, + } + } + /// Build an output_identifier from an existing input. pub fn from_input(input: &Input) -> OutputIdentifier { OutputIdentifier { @@ -966,7 +810,7 @@ impl OutputIdentifier { /// Ensure this is implemented to centralize hashing with indexes impl PMMRable for OutputIdentifier { fn len() -> usize { - 1 + secp::constants::PEDERSEN_COMMITMENT_SIZE + SWITCH_COMMIT_HASH_SIZE + 1 + secp::constants::PEDERSEN_COMMITMENT_SIZE } } @@ -989,111 +833,6 @@ impl Readable for OutputIdentifier { } } -/// Yet another output version to read/write from disk. Ends up being far too awkward -/// to use the write serialisation property to do this -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct OutputStoreable { - /// Output features (coinbase vs. regular transaction output) - /// We need to include this when hashing to ensure coinbase maturity can be enforced. - pub features: OutputFeatures, - /// Output commitment - pub commit: Commitment, - /// Switch commit hash - pub switch_commit_hash: SwitchCommitHash, -} - -impl OutputStoreable { - /// Build a StoreableOutput from an existing output. - pub fn from_output(output: &Output) -> OutputStoreable { - OutputStoreable { - features: output.features, - commit: output.commit, - switch_commit_hash: output.switch_commit_hash, - } - } - - /// Return a regular output - pub fn to_output(self, rproof: RangeProof) -> Output { - Output { - features: self.features, - commit: self.commit, - switch_commit_hash: self.switch_commit_hash, - proof: rproof, - } - } -} - -impl PMMRable for OutputStoreable { - fn len() -> usize { - 1 + secp::constants::PEDERSEN_COMMITMENT_SIZE + SWITCH_COMMIT_HASH_SIZE - } -} - -impl Writeable for OutputStoreable { - fn write(&self, writer: &mut W) -> Result<(), ser::Error> { - writer.write_u8(self.features.bits())?; - self.commit.write(writer)?; - if writer.serialization_mode() != ser::SerializationMode::Hash { - self.switch_commit_hash.write(writer)?; - } - Ok(()) - } -} - -impl Readable for OutputStoreable { - fn read(reader: &mut Reader) -> Result { - let features = - OutputFeatures::from_bits(reader.read_u8()?).ok_or(ser::Error::CorruptedData)?; - Ok(OutputStoreable { - commit: Commitment::read(reader)?, - switch_commit_hash: SwitchCommitHash::read(reader)?, - features: features, - }) - } -} - -/// 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 - pub value: u64, -} - -impl Writeable for ProofMessageElements { - fn write(&self, writer: &mut W) -> Result<(), ser::Error> { - writer.write_u64(self.value)?; - for _ in 8..64 { - let _ = writer.write_u8(0); - } - Ok(()) - } -} - -impl Readable for ProofMessageElements { - fn read(reader: &mut Reader) -> Result { - Ok(ProofMessageElements { - value: reader.read_u64()?, - }) - } -} - -impl ProofMessageElements { - /// 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 { - let mut c = Cursor::new(proof_message.as_bytes()); - ser::deserialize::(&mut c) - } -} - #[cfg(test)] mod test { use super::*; @@ -1151,24 +890,19 @@ mod test { 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 switch_commit = keychain.switch_commit(&key_id).unwrap(); - let switch_commit_hash = - SwitchCommitHash::from_switch_commit(switch_commit, &keychain, &key_id); let msg = secp::pedersen::ProofMessage::empty(); let proof = keychain .range_proof( 5, &key_id, commit, - Some(switch_commit_hash.as_ref().to_vec()), - msg, + None, ) .unwrap(); let out = Output { features: OutputFeatures::DEFAULT_OUTPUT, commit: commit, - switch_commit_hash: switch_commit_hash, proof: proof, }; @@ -1181,66 +915,19 @@ mod test { assert_eq!(dout.proof, out.proof); } - #[test] - fn test_output_value_recovery() { - let keychain = Keychain::from_random_seed().unwrap(); - let key_id = keychain.derive_key_id(1).unwrap(); - let value = 1003; - - let commit = keychain.commit(value, &key_id).unwrap(); - let switch_commit = keychain.switch_commit(&key_id).unwrap(); - let switch_commit_hash = - SwitchCommitHash::from_switch_commit(switch_commit, &keychain, &key_id); - let msg = (ProofMessageElements { value: value }).to_proof_message(); - - let proof = keychain - .range_proof( - value, - &key_id, - commit, - Some(switch_commit_hash.as_ref().to_vec()), - msg, - ) - .unwrap(); - - let output = Output { - features: OutputFeatures::DEFAULT_OUTPUT, - commit: commit, - switch_commit_hash: switch_commit_hash, - proof: proof, - }; - - // check we can successfully recover the value with the original blinding factor - let result = output.recover_value(&keychain, &key_id); - // TODO: Remove this check once value recovery is supported within bullet proofs - if let Some(v) = result { - assert_eq!(v, 1003); - } else { - return; - } - - // Bulletproofs message unwind will just be gibberish given the wrong blinding - // factor - } - #[test] fn commit_consistency() { let keychain = Keychain::from_seed(&[0; 32]).unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let commit = keychain.commit(1003, &key_id).unwrap(); - let switch_commit = keychain.switch_commit(&key_id).unwrap(); - println!("Switch commit: {:?}", switch_commit); println!("commit: {:?}", commit); let key_id = keychain.derive_key_id(1).unwrap(); - let switch_commit_2 = keychain.switch_commit(&key_id).unwrap(); let commit_2 = keychain.commit(1003, &key_id).unwrap(); - println!("Switch commit 2: {:?}", switch_commit_2); println!("commit2 : {:?}", commit_2); assert!(commit == commit_2); - assert!(switch_commit == switch_commit_2); } #[test] diff --git a/core/src/global.rs b/core/src/global.rs index b50554849..766d9d598 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -29,7 +29,6 @@ use consensus::{BLOCK_TIME_SEC, CUT_THROUGH_HORIZON, DIFFICULTY_ADJUST_WINDOW, I MEDIAN_TIME_WINDOW}; use core::target::Difficulty; use consensus::TargetError; -use util::LOGGER; /// Define these here, as they should be developer-set, not really tweakable /// by users diff --git a/core/src/ser.rs b/core/src/ser.rs index c4e031533..b18910bdd 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -26,7 +26,6 @@ use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE}; use consensus; use consensus::VerifySortOrder; use core::hash::{Hash, Hashed}; -use core::transaction::{SwitchCommitHash, SWITCH_COMMIT_HASH_SIZE}; use util::secp::pedersen::Commitment; use util::secp::pedersen::RangeProof; use util::secp::Signature; @@ -658,11 +657,6 @@ impl AsFixedBytes for BlindingFactor { return SECRET_KEY_SIZE; } } -impl AsFixedBytes for SwitchCommitHash { - fn len(&self) -> usize { - return SWITCH_COMMIT_HASH_SIZE; - } -} impl AsFixedBytes for ::keychain::Identifier { fn len(&self) -> usize { return IDENTIFIER_SIZE; diff --git a/keychain/src/extkey.rs b/keychain/src/extkey.rs index 91bdb6a53..52ce757a4 100644 --- a/keychain/src/extkey.rs +++ b/keychain/src/extkey.rs @@ -180,8 +180,6 @@ pub struct ChildKey { pub key_id: Identifier, /// The private key pub key: SecretKey, - /// The key used for generating the associated switch_commit_hash - pub switch_key: [u8; 32], } /// An ExtendedKey is a secret key which can be used to derive new @@ -201,10 +199,6 @@ pub struct ExtendedKey { pub key: SecretKey, /// The chain code for the key derivation chain pub chain_code: [u8; 32], - /// The key used for generating the associated switch_commit_hash - pub switch_key: [u8; 32], - /// The chain code for the switch key derivation chain - pub switch_chain_code: [u8; 32], } impl ExtendedKey { @@ -226,19 +220,6 @@ impl ExtendedKey { let key_id = Identifier::from_secret_key(secp, &key)?; - // Now derive the switch_key and switch_chain_code in a similar fashion - // but using a different key to ensure there is nothing linking - // the secret key and the switch commit hash key for any extended key - // we subsequently derive - let switch_derived = blake2b(64, b"Grin/MW Switch Seed", seed); - let switch_slice = switch_derived.as_bytes(); - - let mut switch_key: [u8; 32] = Default::default(); - (&mut switch_key).copy_from_slice(&switch_slice[0..32]); - - let mut switch_chain_code: [u8; 32] = Default::default(); - (&mut switch_chain_code).copy_from_slice(&switch_slice[32..64]); - let ext_key = ExtendedKey { n_child: 0, root_key_id: key_id.clone(), @@ -247,10 +228,6 @@ impl ExtendedKey { // key and extended chain code for the key itself key, chain_code, - - // key and extended chain code for the key for hashed switch commitments - switch_key, - switch_chain_code, }; Ok(ext_key) @@ -275,22 +252,11 @@ impl ExtendedKey { let key_id = Identifier::from_secret_key(secp, &key)?; - let mut switch_seed = self.switch_key[..].to_vec(); - switch_seed.extend_from_slice(&n_bytes); - - // only need a 32 byte digest here as we only need the bytes for the key itself - // we do not need additional bytes for a derived (and unused) chain code - let switch_derived = blake2b(32, &self.switch_chain_code[..], &switch_seed[..]); - - let mut switch_key: [u8; 32] = Default::default(); - (&mut switch_key).copy_from_slice(&switch_derived.as_bytes()[..]); - Ok(ChildKey { n_child: n, root_key_id: self.root_key_id.clone(), key_id, key, - switch_key, }) } } diff --git a/keychain/src/keychain.rs b/keychain/src/keychain.rs index 18aea28c9..038dd29df 100644 --- a/keychain/src/keychain.rs +++ b/keychain/src/keychain.rs @@ -214,53 +214,15 @@ impl Keychain { Ok(commit) } - pub fn switch_commit(&self, key_id: &Identifier) -> Result { - let skey = self.derived_key(key_id)?; - let commit = self.secp.switch_commit(skey)?; - Ok(commit) - } - - pub fn switch_commit_from_index(&self, index: u32) -> Result { - // just do this directly, because cache seems really slow for wallet reconstruct - let skey = self.extkey.derive(&self.secp, index)?; - let skey = skey.key; - let commit = self.secp.switch_commit(skey)?; - Ok(commit) - } - - pub fn switch_commit_hash_key(&self, key_id: &Identifier) -> Result<[u8; 32], Error> { - // first check our overrides and just return zero key if we have an override - // we allow keys to be overridden for testing - // and do not care about switch_commit_hash_keys in this case - if let Some(_) = self.key_overrides.get(key_id) { - let key: [u8; 32] = Default::default(); - return Ok(key); - } - - let child_key = self.derived_child_key(key_id)?; - Ok(child_key.switch_key) - } - pub fn range_proof( &self, amount: u64, key_id: &Identifier, _commit: Commitment, extra_data: Option>, - msg: ProofMessage, ) -> Result { let skey = self.derived_key(key_id)?; - if msg.len() == 0 { - return Ok(self.secp.bullet_proof(amount, skey, 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, extra_data, Some(msg))); + Ok(self.secp.bullet_proof(amount, skey, extra_data, None)) } pub fn verify_range_proof( @@ -623,77 +585,6 @@ mod test { secp.verify_from_commit(&msg, &sig, &commit).unwrap(); } - #[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); - } - // We plan to "offset" the key used in the kernel commitment // so we are going to be doing some key addition/subtraction. // This test is mainly to demonstrate that idea that summing commitments diff --git a/pool/src/graph.rs b/pool/src/graph.rs index 298968b54..9c1de5302 100644 --- a/pool/src/graph.rs +++ b/pool/src/graph.rs @@ -294,10 +294,9 @@ pub fn transaction_identifier(tx: &core::transaction::Transaction) -> core::hash #[cfg(test)] mod tests { use super::*; - use util::secp; use keychain::Keychain; use rand; - use core::core::{OutputFeatures, SwitchCommitHash}; + use core::core::OutputFeatures; #[test] fn test_add_entry() { @@ -307,9 +306,6 @@ mod tests { let key_id3 = keychain.derive_key_id(3).unwrap(); let output_commit = keychain.commit(70, &key_id1).unwrap(); - let switch_commit = keychain.switch_commit(&key_id1).unwrap(); - let switch_commit_hash = - SwitchCommitHash::from_switch_commit(switch_commit, &keychain, &key_id1); let inputs = vec![ core::transaction::Input::new( @@ -325,19 +321,16 @@ mod tests { None, ), ]; - let msg = secp::pedersen::ProofMessage::empty(); let output = core::transaction::Output { features: OutputFeatures::DEFAULT_OUTPUT, commit: output_commit, - switch_commit_hash: switch_commit_hash, proof: keychain .range_proof( 100, &key_id1, output_commit, - Some(switch_commit_hash.as_ref().to_vec()), - msg, + None, ) .unwrap(), }; diff --git a/pool/src/pool.rs b/pool/src/pool.rs index 85939c183..38ad77248 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -641,7 +641,6 @@ where } let freed_txs = self.sweep_transactions(marked_transactions, false); - let freed_stem_txs = self.sweep_transactions(marked_stem_transactions, true); self.reconcile_orphans().unwrap(); @@ -852,12 +851,11 @@ mod tests { use core::core::build; use core::global; use blockchain::{DummyChain, DummyChainImpl, DummyOutputSet}; - use util::secp; use keychain::Keychain; use std::sync::{Arc, RwLock}; use blake2; use core::global::ChainTypes; - use core::core::{Proof, SwitchCommitHash}; + use core::core::Proof; use core::core::hash::{Hash, Hashed}; use core::core::pmmr::MerkleProof; use core::core::target::Difficulty; @@ -1713,24 +1711,18 @@ mod tests { let keychain = keychain_for_tests(); let key_id = keychain.derive_key_id(value as u32).unwrap(); let commit = keychain.commit(value, &key_id).unwrap(); - let switch_commit = keychain.switch_commit(&key_id).unwrap(); - let switch_commit_hash = - SwitchCommitHash::from_switch_commit(switch_commit, &keychain, &key_id); - let msg = secp::pedersen::ProofMessage::empty(); let proof = keychain .range_proof( value, &key_id, commit, - Some(switch_commit_hash.as_ref().to_vec()), - msg, + None, ) .unwrap(); transaction::Output { features: transaction::OutputFeatures::DEFAULT_OUTPUT, commit: commit, - switch_commit_hash: switch_commit_hash, proof: proof, } } @@ -1740,24 +1732,18 @@ mod tests { let keychain = keychain_for_tests(); let key_id = keychain.derive_key_id(value as u32).unwrap(); let commit = keychain.commit(value, &key_id).unwrap(); - let switch_commit = keychain.switch_commit(&key_id).unwrap(); - let switch_commit_hash = - SwitchCommitHash::from_switch_commit(switch_commit, &keychain, &key_id); - let msg = secp::pedersen::ProofMessage::empty(); let proof = keychain .range_proof( value, &key_id, commit, - Some(switch_commit_hash.as_ref().to_vec()), - msg, + None, ) .unwrap(); transaction::Output { features: transaction::OutputFeatures::COINBASE_OUTPUT, commit: commit, - switch_commit_hash: switch_commit_hash, proof: proof, } } diff --git a/src/bin/grin.rs b/src/bin/grin.rs index 070bd7722..653758d4d 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -251,11 +251,7 @@ fn main() { .about("basic wallet contents summary")) .subcommand(SubCommand::with_name("init") - .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."))) + .about("Initialize a new wallet seed file."))) .get_matches(); @@ -494,12 +490,6 @@ 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; @@ -621,9 +611,6 @@ 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/wallet/src/lib.rs b/wallet/src/lib.rs index de7556f51..ce76ef60d 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -53,7 +53,6 @@ mod info; mod receiver; mod sender; mod types; -mod restore; pub mod client; pub mod server; @@ -63,4 +62,3 @@ 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 deleted file mode 100644 index 884522fef..000000000 --- a/wallet/src/restore.rs +++ /dev/null @@ -1,319 +0,0 @@ -// 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::{to_hex, LOGGER}; -use util::secp::pedersen; -use api; -use core::global; -use core::core::{Output, SwitchCommitHash}; -use core::core::transaction::OutputFeatures; -use types::{Error, ErrorKind, OutputData, OutputStatus, WalletConfig, WalletData}; -use byteorder::{BigEndian, ByteOrder}; - -pub fn get_chain_height(config: &WalletConfig) -> Result { - let url = format!("{}/v1/chain", config.check_node_api_http_addr); - - match api::client::get::(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 output_with_range_proof( - config: &WalletConfig, - commit_id: &str, - height: u64, -) -> Result { - let url = format!( - "{}/v1/chain/outputs/byheight?start_height={}&end_height={}&id={}&include_rp", - config.check_node_api_http_addr, height, height, commit_id, - ); - - match api::client::get::>(url.as_str()) { - Ok(block_outputs) => { - if let Some(block_output) = block_outputs.first() { - if let Some(output) = block_output.outputs.first() { - Ok(output.clone()) - } else { - Err(ErrorKind::Node)? - } - } else { - Err(ErrorKind::Node)? - } - } - Err(e) => { - // if we got anything other than 200 back from server, don't attempt to refresh - // the wallet - // data after - Err(e.context(ErrorKind::Node))? - } - } -} - -fn retrieve_amount_and_coinbase_status( - config: &WalletConfig, - keychain: &Keychain, - key_id: Identifier, - commit_id: &str, - height: u64, -) -> Result<(u64, bool), Error> { - let output = output_with_range_proof(config, commit_id, height)?; - - let core_output = Output { - features: match output.output_type { - api::OutputType::Coinbase => OutputFeatures::COINBASE_OUTPUT, - api::OutputType::Transaction => OutputFeatures::DEFAULT_OUTPUT, - }, - proof: output - .range_proof() - .context(ErrorKind::GenericError("range proof error"))?, - switch_commit_hash: output - .switch_commit_hash() - .context(ErrorKind::GenericError("switch commit hash error"))?, - commit: output - .commit() - .context(ErrorKind::GenericError("commit error"))?, - }; - - if let Some(amount) = core_output.recover_value(keychain, &key_id) { - let is_coinbase = match output.output_type { - api::OutputType::Coinbase => true, - api::OutputType::Transaction => false, - }; - Ok((amount, is_coinbase)) - } else { - Err(ErrorKind::GenericError("cannot recover value"))? - } -} - -pub fn outputs_batch_block( - config: &WalletConfig, - start_height: u64, - end_height: u64, -) -> Result, Error> { - let query_param = format!("start_height={}&end_height={}", start_height, end_height); - - let url = format!( - "{}/v1/chain/outputs/byheight?{}", - config.check_node_api_http_addr, query_param, - ); - - match api::client::get::>(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( - config: &WalletConfig, - keychain: &Keychain, - switch_commit_cache: &Vec, - block_outputs: api::BlockOutputs, - key_iterations: &mut usize, - padding: &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, - ); - - for output in block_outputs.outputs.iter().filter(|x| !x.spent) { - for i in 1..*key_iterations { - let key_id = &keychain.derive_key_id(i as u32).unwrap(); - - if let Ok(x) = output.switch_commit_hash() { - let expected_hash = SwitchCommitHash::from_switch_commit( - switch_commit_cache[i as usize], - &keychain, - &key_id, - ); - - if x == expected_hash { - info!(LOGGER, "Output found: {:?}, key_index: {:?}", output, i,); - - // add it to result set here - let commit_id = output.commit.0; - - let res = retrieve_amount_and_coinbase_status( - config, - keychain, - key_id.clone(), - &to_hex(output.commit.0.to_vec()), - block_outputs.header.height, - ); - - if let Ok((amount, is_coinbase)) = res { - info!(LOGGER, "Amount: {}", amount); - - 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, - amount, - height, - lock_height, - is_coinbase, - )); - - // probably don't have to look for indexes greater than this now - *key_iterations = i + *padding; - if *key_iterations > switch_commit_cache.len() { - *key_iterations = switch_commit_cache.len(); - } - info!(LOGGER, "Setting max key index to: {}", *key_iterations); - break; - } else { - info!( - LOGGER, - "Unable to retrieve the amount (needs investigating) {:?}", res, - ); - } - } - } - } - } - 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 mut switch_commit_cache: Vec = vec![]; - info!( - LOGGER, - "Building key derivation cache ({}) ...", key_derivations, - ); - for i in 0..key_derivations { - let switch_commit = keychain.switch_commit_from_index(i as u32).unwrap(); - switch_commit_cache.push(switch_commit); - } - debug!(LOGGER, "... done"); - - 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; - // set to a percentage of the key_derivation value - let mut padding = (key_iterations as f64 * 0.25) 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( - config, - keychain, - &switch_commit_cache, - block, - &mut key_iterations, - &mut padding, - ); - 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(()) -}