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
This commit is contained in:
Ignotus Peverell 2018-03-22 00:10:11 +00:00 committed by GitHub
parent ff4d68d2be
commit ca8447f3bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 55 additions and 921 deletions

View file

@ -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<core::SwitchCommitHash, ser::Error> {
Ok(self.switch_commit_hash.clone())
}
pub fn commit(&self) -> Result<pedersen::Commitment, ser::Error> {
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\",\

View file

@ -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<OutputStoreable>)> {
pub fn get_last_n_output(&self, distance: u64) -> Vec<(Hash, Option<OutputIdentifier>)> {
let mut txhashset = self.txhashset.write().unwrap();
txhashset.last_n_output(distance)
}

View file

@ -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<OutputStoreable>,
output_pmmr_h: PMMRHandle<OutputIdentifier>,
rproof_pmmr_h: PMMRHandle<RangeProof>,
kernel_pmmr_h: PMMRHandle<TxKernel>,
@ -144,7 +143,7 @@ impl TxHashSet {
pub fn is_unspent(&mut self, output_id: &OutputIdentifier) -> Result<Hash, Error> {
match self.commit_index.get_output_pos(&output_id.commit) {
Ok(pos) => {
let output_pmmr: PMMR<OutputStoreable, _> =
let output_pmmr: PMMR<OutputIdentifier, _> =
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<OutputStoreable>)> {
let output_pmmr: PMMR<OutputStoreable, _> =
pub fn last_n_output(&mut self, distance: u64) -> Vec<(Hash, Option<OutputIdentifier>)> {
let output_pmmr: PMMR<OutputIdentifier, _> =
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<OutputStoreable, _> =
let output_pmmr: PMMR<OutputIdentifier, _> =
PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos);
let rproof_pmmr: PMMR<RangeProof, _> =
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<OutputStoreable>>,
output_pmmr: PMMR<'a, OutputIdentifier, PMMRBackend<OutputIdentifier>>,
rproof_pmmr: PMMR<'a, RangeProof, PMMRBackend<RangeProof>>,
kernel_pmmr: PMMR<'a, TxKernel, PMMRBackend<TxKernel>>,
@ -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);

View file

@ -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

View file

@ -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,);
}

View file

@ -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<Append> {
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<Append> {
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<Append> {
tx.with_output(Output {
features: OutputFeatures::DEFAULT_OUTPUT,
commit: commit,
switch_commit_hash: switch_commit_hash,
proof: rproof,
}),
kern,

View file

@ -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,);
}

View file

@ -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();

View file

@ -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<W: Writer>(&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<SwitchCommitHash, ser::Error> {
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<SwitchCommitHash, ser::Error> {
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<W: Writer>(&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<u64> {
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<W: Writer>(&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<OutputStoreable, ser::Error> {
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<W: Writer>(&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<ProofMessageElements, ser::Error> {
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<ProofMessageElements, ser::Error> {
let mut c = Cursor::new(proof_message.as_bytes());
ser::deserialize::<ProofMessageElements>(&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]

View file

@ -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

View file

@ -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;

View file

@ -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,
})
}
}

View file

@ -214,53 +214,15 @@ impl Keychain {
Ok(commit)
}
pub fn switch_commit(&self, key_id: &Identifier) -> Result<Commitment, Error> {
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<Commitment, Error> {
// 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<Vec<u8>>,
msg: ProofMessage,
) -> Result<RangeProof, Error> {
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

View file

@ -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(),
};

View file

@ -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,
}
}

View file

@ -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"),
}
}

View file

@ -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;

View file

@ -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<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 output_with_range_proof(
config: &WalletConfig,
commit_id: &str,
height: u64,
) -> Result<api::OutputPrintable, Error> {
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::<Vec<api::BlockOutputs>>(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<Vec<api::BlockOutputs>, 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::<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(
config: &WalletConfig,
keychain: &Keychain,
switch_commit_cache: &Vec<pedersen::Commitment>,
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<pedersen::Commitment> = 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(())
}