mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Recover outputs from multiple wallets using same seed (#2348)
* adding optional mmr index to key ids * rustfmt * update index * add mmr index to output display * change restore to match on commit instead of ID, add extensive restore/check tests for multiple wallets using same seed * rustfmt * ensure check restores unknown accounts as well * rustfmt * remove storage of commit from wallet
This commit is contained in:
parent
f9a20aef0d
commit
e93b380a06
23 changed files with 649 additions and 140 deletions
|
@ -94,6 +94,9 @@ impl TxHashSetHandler {
|
||||||
id
|
id
|
||||||
)))?;
|
)))?;
|
||||||
let commit = Commitment::from_vec(c);
|
let commit = Commitment::from_vec(c);
|
||||||
|
let output_pos = w(&self.chain)
|
||||||
|
.get_output_pos(&commit)
|
||||||
|
.context(ErrorKind::NotFound)?;
|
||||||
let merkle_proof = chain::Chain::get_merkle_proof_for_pos(&w(&self.chain), commit)
|
let merkle_proof = chain::Chain::get_merkle_proof_for_pos(&w(&self.chain), commit)
|
||||||
.map_err(|_| ErrorKind::NotFound)?;
|
.map_err(|_| ErrorKind::NotFound)?;
|
||||||
Ok(OutputPrintable {
|
Ok(OutputPrintable {
|
||||||
|
@ -104,6 +107,7 @@ impl TxHashSetHandler {
|
||||||
proof_hash: "".to_string(),
|
proof_hash: "".to_string(),
|
||||||
block_height: None,
|
block_height: None,
|
||||||
merkle_proof: Some(merkle_proof),
|
merkle_proof: Some(merkle_proof),
|
||||||
|
mmr_index: output_pos,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,8 @@ pub fn get_output(
|
||||||
for x in outputs.iter() {
|
for x in outputs.iter() {
|
||||||
if let Ok(_) = w(chain).is_unspent(&x) {
|
if let Ok(_) = w(chain).is_unspent(&x) {
|
||||||
let block_height = w(chain).get_header_for_output(&x).unwrap().height;
|
let block_height = w(chain).get_header_for_output(&x).unwrap().height;
|
||||||
return Ok((Output::new(&commit, block_height), x.clone()));
|
let output_pos = w(chain).get_output_pos(&x.commit).unwrap_or(0);
|
||||||
|
return Ok((Output::new(&commit, block_height, output_pos), x.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ErrorKind::NotFound)?
|
Err(ErrorKind::NotFound)?
|
||||||
|
|
|
@ -159,15 +159,18 @@ pub struct Output {
|
||||||
pub commit: PrintableCommitment,
|
pub commit: PrintableCommitment,
|
||||||
/// Height of the block which contains the output
|
/// Height of the block which contains the output
|
||||||
pub height: u64,
|
pub height: u64,
|
||||||
|
/// MMR Index of output
|
||||||
|
pub mmr_index: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output {
|
impl Output {
|
||||||
pub fn new(commit: &pedersen::Commitment, height: u64) -> Output {
|
pub fn new(commit: &pedersen::Commitment, height: u64, mmr_index: u64) -> Output {
|
||||||
Output {
|
Output {
|
||||||
commit: PrintableCommitment {
|
commit: PrintableCommitment {
|
||||||
commit: commit.clone(),
|
commit: commit.clone(),
|
||||||
},
|
},
|
||||||
height: height,
|
height: height,
|
||||||
|
mmr_index: mmr_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,6 +245,8 @@ pub struct OutputPrintable {
|
||||||
pub block_height: Option<u64>,
|
pub block_height: Option<u64>,
|
||||||
/// Merkle Proof
|
/// Merkle Proof
|
||||||
pub merkle_proof: Option<MerkleProof>,
|
pub merkle_proof: Option<MerkleProof>,
|
||||||
|
/// MMR Position
|
||||||
|
pub mmr_index: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputPrintable {
|
impl OutputPrintable {
|
||||||
|
@ -279,6 +284,8 @@ impl OutputPrintable {
|
||||||
merkle_proof = chain.get_merkle_proof(&out_id, &block_header.unwrap()).ok()
|
merkle_proof = chain.get_merkle_proof(&out_id, &block_header.unwrap()).ok()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let output_pos = chain.get_output_pos(&output.commit).unwrap_or(0);
|
||||||
|
|
||||||
OutputPrintable {
|
OutputPrintable {
|
||||||
output_type,
|
output_type,
|
||||||
commit: output.commit,
|
commit: output.commit,
|
||||||
|
@ -287,6 +294,7 @@ impl OutputPrintable {
|
||||||
proof_hash: util::to_hex(output.proof.hash().to_vec()),
|
proof_hash: util::to_hex(output.proof.hash().to_vec()),
|
||||||
block_height,
|
block_height,
|
||||||
merkle_proof,
|
merkle_proof,
|
||||||
|
mmr_index: output_pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +335,7 @@ impl serde::ser::Serialize for OutputPrintable {
|
||||||
|
|
||||||
let hex_merkle_proof = &self.merkle_proof.clone().map(|x| x.to_hex());
|
let hex_merkle_proof = &self.merkle_proof.clone().map(|x| x.to_hex());
|
||||||
state.serialize_field("merkle_proof", &hex_merkle_proof)?;
|
state.serialize_field("merkle_proof", &hex_merkle_proof)?;
|
||||||
|
state.serialize_field("mmr_index", &self.mmr_index)?;
|
||||||
|
|
||||||
state.end()
|
state.end()
|
||||||
}
|
}
|
||||||
|
@ -347,6 +356,7 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
|
||||||
ProofHash,
|
ProofHash,
|
||||||
BlockHeight,
|
BlockHeight,
|
||||||
MerkleProof,
|
MerkleProof,
|
||||||
|
MmrIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OutputPrintableVisitor;
|
struct OutputPrintableVisitor;
|
||||||
|
@ -369,6 +379,7 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
|
||||||
let mut proof_hash = None;
|
let mut proof_hash = None;
|
||||||
let mut block_height = None;
|
let mut block_height = None;
|
||||||
let mut merkle_proof = None;
|
let mut merkle_proof = None;
|
||||||
|
let mut mmr_index = None;
|
||||||
|
|
||||||
while let Some(key) = map.next_key()? {
|
while let Some(key) = map.next_key()? {
|
||||||
match key {
|
match key {
|
||||||
|
@ -410,6 +421,10 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Field::MmrIndex => {
|
||||||
|
no_dup!(mmr_index);
|
||||||
|
mmr_index = Some(map.next_value()?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,12 +436,19 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
|
||||||
proof_hash: proof_hash.unwrap(),
|
proof_hash: proof_hash.unwrap(),
|
||||||
block_height: block_height,
|
block_height: block_height,
|
||||||
merkle_proof: merkle_proof,
|
merkle_proof: merkle_proof,
|
||||||
|
mmr_index: mmr_index.unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIELDS: &'static [&'static str] =
|
const FIELDS: &'static [&'static str] = &[
|
||||||
&["output_type", "commit", "spent", "proof", "proof_hash"];
|
"output_type",
|
||||||
|
"commit",
|
||||||
|
"spent",
|
||||||
|
"proof",
|
||||||
|
"proof_hash",
|
||||||
|
"mmr_index",
|
||||||
|
];
|
||||||
deserializer.deserialize_struct("OutputPrintable", FIELDS, OutputPrintableVisitor)
|
deserializer.deserialize_struct("OutputPrintable", FIELDS, OutputPrintableVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,7 +684,8 @@ mod test {
|
||||||
\"proof\":null,\
|
\"proof\":null,\
|
||||||
\"proof_hash\":\"ed6ba96009b86173bade6a9227ed60422916593fa32dd6d78b25b7a4eeef4946\",\
|
\"proof_hash\":\"ed6ba96009b86173bade6a9227ed60422916593fa32dd6d78b25b7a4eeef4946\",\
|
||||||
\"block_height\":0,\
|
\"block_height\":0,\
|
||||||
\"merkle_proof\":null\
|
\"merkle_proof\":null,\
|
||||||
|
\"mmr_index\":0\
|
||||||
}";
|
}";
|
||||||
let deserialized: OutputPrintable = serde_json::from_str(&hex_output).unwrap();
|
let deserialized: OutputPrintable = serde_json::from_str(&hex_output).unwrap();
|
||||||
let serialized = serde_json::to_string(&deserialized).unwrap();
|
let serialized = serde_json::to_string(&deserialized).unwrap();
|
||||||
|
@ -674,7 +697,8 @@ mod test {
|
||||||
let hex_commit =
|
let hex_commit =
|
||||||
"{\
|
"{\
|
||||||
\"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\",\
|
\"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\",\
|
||||||
\"height\":0\
|
\"height\":0,\
|
||||||
|
\"mmr_index\":0\
|
||||||
}";
|
}";
|
||||||
let deserialized: Output = serde_json::from_str(&hex_commit).unwrap();
|
let deserialized: Output = serde_json::from_str(&hex_commit).unwrap();
|
||||||
let serialized = serde_json::to_string(&deserialized).unwrap();
|
let serialized = serde_json::to_string(&deserialized).unwrap();
|
||||||
|
|
|
@ -1041,6 +1041,11 @@ impl Chain {
|
||||||
self.txhashset.read().last_n_kernel(distance)
|
self.txhashset.read().last_n_kernel(distance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// as above, for kernels
|
||||||
|
pub fn get_output_pos(&self, commit: &Commitment) -> Result<u64, Error> {
|
||||||
|
Ok(self.txhashset.read().get_output_pos(commit)?)
|
||||||
|
}
|
||||||
|
|
||||||
/// outputs by insertion index
|
/// outputs by insertion index
|
||||||
pub fn unspent_outputs_by_insertion_index(
|
pub fn unspent_outputs_by_insertion_index(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -257,6 +257,11 @@ impl TxHashSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return Commit's MMR position
|
||||||
|
pub fn get_output_pos(&self, commit: &Commitment) -> Result<u64, Error> {
|
||||||
|
Ok(self.commit_index.get_output_pos(&commit)?)
|
||||||
|
}
|
||||||
|
|
||||||
/// build a new merkle proof for the given position.
|
/// build a new merkle proof for the given position.
|
||||||
pub fn merkle_proof(&mut self, commit: Commitment) -> Result<MerkleProof, String> {
|
pub fn merkle_proof(&mut self, commit: Commitment) -> Result<MerkleProof, String> {
|
||||||
let pos = self.commit_index.get_output_pos(&commit).unwrap();
|
let pos = self.commit_index.get_output_pos(&commit).unwrap();
|
||||||
|
|
|
@ -44,6 +44,7 @@ pub fn outputs(
|
||||||
|
|
||||||
table.set_titles(row![
|
table.set_titles(row![
|
||||||
bMG->"Output Commitment",
|
bMG->"Output Commitment",
|
||||||
|
bMG->"MMR Index",
|
||||||
bMG->"Block Height",
|
bMG->"Block Height",
|
||||||
bMG->"Locked Until",
|
bMG->"Locked Until",
|
||||||
bMG->"Status",
|
bMG->"Status",
|
||||||
|
@ -55,6 +56,10 @@ pub fn outputs(
|
||||||
|
|
||||||
for (out, commit) in outputs {
|
for (out, commit) in outputs {
|
||||||
let commit = format!("{}", util::to_hex(commit.as_ref().to_vec()));
|
let commit = format!("{}", util::to_hex(commit.as_ref().to_vec()));
|
||||||
|
let index = match out.mmr_index {
|
||||||
|
None => "None".to_owned(),
|
||||||
|
Some(t) => t.to_string(),
|
||||||
|
};
|
||||||
let height = format!("{}", out.height);
|
let height = format!("{}", out.height);
|
||||||
let lock_height = format!("{}", out.lock_height);
|
let lock_height = format!("{}", out.lock_height);
|
||||||
let is_coinbase = format!("{}", out.is_coinbase);
|
let is_coinbase = format!("{}", out.is_coinbase);
|
||||||
|
@ -75,6 +80,7 @@ pub fn outputs(
|
||||||
if dark_background_color_scheme {
|
if dark_background_color_scheme {
|
||||||
table.add_row(row![
|
table.add_row(row![
|
||||||
bFC->commit,
|
bFC->commit,
|
||||||
|
bFB->index,
|
||||||
bFB->height,
|
bFB->height,
|
||||||
bFB->lock_height,
|
bFB->lock_height,
|
||||||
bFR->status,
|
bFR->status,
|
||||||
|
|
|
@ -32,13 +32,14 @@ where
|
||||||
pub fn retrieve_existing_key<T: ?Sized, C, K>(
|
pub fn retrieve_existing_key<T: ?Sized, C, K>(
|
||||||
wallet: &T,
|
wallet: &T,
|
||||||
key_id: Identifier,
|
key_id: Identifier,
|
||||||
|
mmr_index: Option<u64>,
|
||||||
) -> Result<(Identifier, u32), Error>
|
) -> Result<(Identifier, u32), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let existing = wallet.get(&key_id)?;
|
let existing = wallet.get(&key_id, &mmr_index)?;
|
||||||
let key_id = existing.key_id.clone();
|
let key_id = existing.key_id.clone();
|
||||||
let derivation = existing.n_child;
|
let derivation = existing.n_child;
|
||||||
Ok((key_id, derivation))
|
Ok((key_id, derivation))
|
||||||
|
|
|
@ -32,6 +32,8 @@ struct OutputResult {
|
||||||
///
|
///
|
||||||
pub n_child: u32,
|
pub n_child: u32,
|
||||||
///
|
///
|
||||||
|
pub mmr_index: u64,
|
||||||
|
///
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
///
|
///
|
||||||
pub height: u64,
|
pub height: u64,
|
||||||
|
@ -45,7 +47,7 @@ struct OutputResult {
|
||||||
|
|
||||||
fn identify_utxo_outputs<T, C, K>(
|
fn identify_utxo_outputs<T, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||||
) -> Result<Vec<OutputResult>, Error>
|
) -> Result<Vec<OutputResult>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
|
@ -60,7 +62,7 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
for output in outputs.iter() {
|
for output in outputs.iter() {
|
||||||
let (commit, proof, is_coinbase, height) = output;
|
let (commit, proof, is_coinbase, height, mmr_index) = output;
|
||||||
// attempt to unwind message from the RP and get a value
|
// attempt to unwind message from the RP and get a value
|
||||||
// will fail if it's not ours
|
// will fail if it's not ours
|
||||||
let info = proof::rewind(wallet.keychain(), *commit, None, *proof)?;
|
let info = proof::rewind(wallet.keychain(), *commit, None, *proof)?;
|
||||||
|
@ -80,8 +82,8 @@ where
|
||||||
let key_id = Identifier::from_serialized_path(3u8, &info.message.as_bytes());
|
let key_id = Identifier::from_serialized_path(3u8, &info.message.as_bytes());
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Output found: {:?}, amount: {:?}, key_id: {:?}",
|
"Output found: {:?}, amount: {:?}, key_id: {:?}, mmr_index: {},",
|
||||||
commit, info.value, key_id
|
commit, info.value, key_id, mmr_index,
|
||||||
);
|
);
|
||||||
|
|
||||||
wallet_outputs.push(OutputResult {
|
wallet_outputs.push(OutputResult {
|
||||||
|
@ -93,6 +95,7 @@ where
|
||||||
lock_height: lock_height,
|
lock_height: lock_height,
|
||||||
is_coinbase: *is_coinbase,
|
is_coinbase: *is_coinbase,
|
||||||
blinding: info.blinding,
|
blinding: info.blinding,
|
||||||
|
mmr_index: *mmr_index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(wallet_outputs)
|
Ok(wallet_outputs)
|
||||||
|
@ -163,6 +166,7 @@ where
|
||||||
root_key_id: parent_key_id.clone(),
|
root_key_id: parent_key_id.clone(),
|
||||||
key_id: output.key_id,
|
key_id: output.key_id,
|
||||||
n_child: output.n_child,
|
n_child: output.n_child,
|
||||||
|
mmr_index: Some(output.mmr_index),
|
||||||
value: output.value,
|
value: output.value,
|
||||||
status: OutputStatus::Unspent,
|
status: OutputStatus::Unspent,
|
||||||
height: output.height,
|
height: output.height,
|
||||||
|
@ -246,7 +250,7 @@ where
|
||||||
|
|
||||||
// check all definitive outputs exist in the wallet outputs
|
// check all definitive outputs exist in the wallet outputs
|
||||||
for deffo in chain_outs.into_iter() {
|
for deffo in chain_outs.into_iter() {
|
||||||
let matched_out = wallet_outputs.iter().find(|wo| wo.0.key_id == deffo.key_id);
|
let matched_out = wallet_outputs.iter().find(|wo| wo.1 == deffo.commit);
|
||||||
match matched_out {
|
match matched_out {
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
if s.0.status == OutputStatus::Spent {
|
if s.0.status == OutputStatus::Spent {
|
||||||
|
@ -317,10 +321,30 @@ where
|
||||||
);
|
);
|
||||||
cancel_tx_log_entry(wallet, &o)?;
|
cancel_tx_log_entry(wallet, &o)?;
|
||||||
let mut batch = wallet.batch()?;
|
let mut batch = wallet.batch()?;
|
||||||
batch.delete(&o.key_id)?;
|
batch.delete(&o.key_id, &o.mmr_index)?;
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restore labels, account paths and child derivation indices
|
||||||
|
let label_base = "account";
|
||||||
|
let mut index = 1;
|
||||||
|
for (path, max_child_index) in found_parents.iter() {
|
||||||
|
if *path == ExtKeychain::derive_key_id(2, 0, 0, 0, 0) {
|
||||||
|
//default path already exists
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let res = wallet.acct_path_iter().find(|e| e.path == *path);
|
||||||
|
if let None = res {
|
||||||
|
let label = format!("{}_{}", label_base, index);
|
||||||
|
keys::set_acct_path(wallet, &label, path)?;
|
||||||
|
index = index + 1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut batch = wallet.batch()?;
|
||||||
|
batch.save_child_index(path, max_child_index + 1)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,12 +82,12 @@ where
|
||||||
|
|
||||||
// Store our private identifiers for each input
|
// Store our private identifiers for each input
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
context.add_input(&input.key_id);
|
context.add_input(&input.key_id, &input.mmr_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store change output(s)
|
// Store change output(s)
|
||||||
for (_, id) in &change_amounts_derivations {
|
for (_, id, mmr_index) in &change_amounts_derivations {
|
||||||
context.add_output(&id);
|
context.add_output(&id, &mmr_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lock_inputs = context.get_inputs().clone();
|
let lock_inputs = context.get_inputs().clone();
|
||||||
|
@ -107,7 +107,7 @@ where
|
||||||
let mut amount_debited = 0;
|
let mut amount_debited = 0;
|
||||||
t.num_inputs = lock_inputs.len();
|
t.num_inputs = lock_inputs.len();
|
||||||
for id in lock_inputs {
|
for id in lock_inputs {
|
||||||
let mut coin = batch.get(&id).unwrap();
|
let mut coin = batch.get(&id.0, &id.1).unwrap();
|
||||||
coin.tx_log_entry = Some(log_id);
|
coin.tx_log_entry = Some(log_id);
|
||||||
amount_debited = amount_debited + coin.value;
|
amount_debited = amount_debited + coin.value;
|
||||||
batch.lock_output(&mut coin)?;
|
batch.lock_output(&mut coin)?;
|
||||||
|
@ -116,13 +116,14 @@ where
|
||||||
t.amount_debited = amount_debited;
|
t.amount_debited = amount_debited;
|
||||||
|
|
||||||
// write the output representing our change
|
// write the output representing our change
|
||||||
for (change_amount, id) in &change_amounts_derivations {
|
for (change_amount, id, _) in &change_amounts_derivations {
|
||||||
t.num_outputs += 1;
|
t.num_outputs += 1;
|
||||||
t.amount_credited += change_amount;
|
t.amount_credited += change_amount;
|
||||||
batch.save(OutputData {
|
batch.save(OutputData {
|
||||||
root_key_id: parent_key_id.clone(),
|
root_key_id: parent_key_id.clone(),
|
||||||
key_id: id.clone(),
|
key_id: id.clone(),
|
||||||
n_child: id.to_path().last_path_index(),
|
n_child: id.to_path().last_path_index(),
|
||||||
|
mmr_index: None,
|
||||||
value: change_amount.clone(),
|
value: change_amount.clone(),
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: current_height,
|
height: current_height,
|
||||||
|
@ -183,7 +184,7 @@ where
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
context.add_output(&key_id);
|
context.add_output(&key_id, &None);
|
||||||
|
|
||||||
// Create closure that adds the output to recipient's wallet
|
// Create closure that adds the output to recipient's wallet
|
||||||
// (up to the caller to decide when to do)
|
// (up to the caller to decide when to do)
|
||||||
|
@ -197,6 +198,7 @@ where
|
||||||
batch.save(OutputData {
|
batch.save(OutputData {
|
||||||
root_key_id: parent_key_id.clone(),
|
root_key_id: parent_key_id.clone(),
|
||||||
key_id: key_id_inner.clone(),
|
key_id: key_id_inner.clone(),
|
||||||
|
mmr_index: None,
|
||||||
n_child: key_id_inner.to_path().last_path_index(),
|
n_child: key_id_inner.to_path().last_path_index(),
|
||||||
value: amount,
|
value: amount,
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
|
@ -229,7 +231,7 @@ pub fn select_send_tx<T: ?Sized, C, K>(
|
||||||
(
|
(
|
||||||
Vec<Box<build::Append<K>>>,
|
Vec<Box<build::Append<K>>>,
|
||||||
Vec<OutputData>,
|
Vec<OutputData>,
|
||||||
Vec<(u64, Identifier)>, // change amounts and derivations
|
Vec<(u64, Identifier, Option<u64>)>, // change amounts and derivations
|
||||||
u64, // amount
|
u64, // amount
|
||||||
u64, // fee
|
u64, // fee
|
||||||
),
|
),
|
||||||
|
@ -337,7 +339,13 @@ pub fn inputs_and_change<T: ?Sized, C, K>(
|
||||||
amount: u64,
|
amount: u64,
|
||||||
fee: u64,
|
fee: u64,
|
||||||
num_change_outputs: usize,
|
num_change_outputs: usize,
|
||||||
) -> Result<(Vec<Box<build::Append<K>>>, Vec<(u64, Identifier)>), Error>
|
) -> Result<
|
||||||
|
(
|
||||||
|
Vec<Box<build::Append<K>>>,
|
||||||
|
Vec<(u64, Identifier, Option<u64>)>,
|
||||||
|
),
|
||||||
|
Error,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
|
@ -387,7 +395,7 @@ where
|
||||||
|
|
||||||
let change_key = wallet.next_child().unwrap();
|
let change_key = wallet.next_child().unwrap();
|
||||||
|
|
||||||
change_amounts_derivations.push((change_amount, change_key.clone()));
|
change_amounts_derivations.push((change_amount, change_key.clone(), None));
|
||||||
parts.push(build::output(change_amount, change_key));
|
parts.push(build::output(change_amount, change_key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,11 +69,12 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs.sort_by_key(|out| out.n_child);
|
outputs.sort_by_key(|out| out.n_child);
|
||||||
|
let keychain = wallet.keychain().clone();
|
||||||
|
|
||||||
let res = outputs
|
let res = outputs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|out| {
|
.map(|out| {
|
||||||
let commit = wallet.get_commitment(&out.key_id).unwrap();
|
let commit = keychain.commit(out.value, &out.key_id).unwrap();
|
||||||
(out, commit)
|
(out, commit)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -138,13 +139,14 @@ pub fn map_wallet_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
update_all: bool,
|
update_all: bool,
|
||||||
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
|
) -> Result<HashMap<pedersen::Commitment, (Identifier, Option<u64>)>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = HashMap::new();
|
let mut wallet_outputs: HashMap<pedersen::Commitment, (Identifier, Option<u64>)> =
|
||||||
|
HashMap::new();
|
||||||
let keychain = wallet.keychain().clone();
|
let keychain = wallet.keychain().clone();
|
||||||
let unspents: Vec<OutputData> = wallet
|
let unspents: Vec<OutputData> = wallet
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -175,7 +177,7 @@ where
|
||||||
|
|
||||||
for out in unspents {
|
for out in unspents {
|
||||||
let commit = keychain.commit(out.value, &out.key_id)?;
|
let commit = keychain.commit(out.value, &out.key_id)?;
|
||||||
wallet_outputs.insert(commit, out.key_id.clone());
|
wallet_outputs.insert(commit, (out.key_id.clone(), out.mmr_index));
|
||||||
}
|
}
|
||||||
Ok(wallet_outputs)
|
Ok(wallet_outputs)
|
||||||
}
|
}
|
||||||
|
@ -197,7 +199,7 @@ where
|
||||||
for mut o in outputs {
|
for mut o in outputs {
|
||||||
// unlock locked outputs
|
// unlock locked outputs
|
||||||
if o.status == OutputStatus::Unconfirmed {
|
if o.status == OutputStatus::Unconfirmed {
|
||||||
batch.delete(&o.key_id)?;
|
batch.delete(&o.key_id, &o.mmr_index)?;
|
||||||
}
|
}
|
||||||
if o.status == OutputStatus::Locked {
|
if o.status == OutputStatus::Locked {
|
||||||
o.status = OutputStatus::Unconfirmed;
|
o.status = OutputStatus::Unconfirmed;
|
||||||
|
@ -219,8 +221,8 @@ where
|
||||||
/// Apply refreshed API output data to the wallet
|
/// Apply refreshed API output data to the wallet
|
||||||
pub fn apply_api_outputs<T: ?Sized, C, K>(
|
pub fn apply_api_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
wallet_outputs: &HashMap<pedersen::Commitment, Identifier>,
|
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>)>,
|
||||||
api_outputs: &HashMap<pedersen::Commitment, (String, u64)>,
|
api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>,
|
||||||
height: u64,
|
height: u64,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<(), libwallet::Error>
|
) -> Result<(), libwallet::Error>
|
||||||
|
@ -245,8 +247,8 @@ where
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut batch = wallet.batch()?;
|
let mut batch = wallet.batch()?;
|
||||||
for (commit, id) in wallet_outputs.iter() {
|
for (commit, (id, mmr_index)) in wallet_outputs.iter() {
|
||||||
if let Ok(mut output) = batch.get(id) {
|
if let Ok(mut output) = batch.get(id, mmr_index) {
|
||||||
match api_outputs.get(&commit) {
|
match api_outputs.get(&commit) {
|
||||||
Some(o) => {
|
Some(o) => {
|
||||||
// if this is a coinbase tx being confirmed, it's recordable in tx log
|
// if this is a coinbase tx being confirmed, it's recordable in tx log
|
||||||
|
@ -345,7 +347,7 @@ where
|
||||||
}
|
}
|
||||||
let mut batch = wallet.batch()?;
|
let mut batch = wallet.batch()?;
|
||||||
for id in ids_to_del {
|
for id in ids_to_del {
|
||||||
batch.delete(&id)?;
|
batch.delete(&id, &None)?;
|
||||||
}
|
}
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -458,7 +460,7 @@ where
|
||||||
let parent_key_id = wallet.parent_key_id();
|
let parent_key_id = wallet.parent_key_id();
|
||||||
|
|
||||||
let key_id = match key_id {
|
let key_id = match key_id {
|
||||||
Some(key_id) => keys::retrieve_existing_key(wallet, key_id)?.0,
|
Some(key_id) => keys::retrieve_existing_key(wallet, key_id, None)?.0,
|
||||||
None => keys::next_available_key(wallet)?,
|
None => keys::next_available_key(wallet)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -469,6 +471,7 @@ where
|
||||||
root_key_id: parent_key_id,
|
root_key_id: parent_key_id,
|
||||||
key_id: key_id.clone(),
|
key_id: key_id.clone(),
|
||||||
n_child: key_id.to_path().last_path_index(),
|
n_child: key_id.to_path().last_path_index(),
|
||||||
|
mmr_index: None,
|
||||||
value: reward(block_fees.fees),
|
value: reward(block_fees.fees),
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: height,
|
height: height,
|
||||||
|
|
|
@ -81,10 +81,7 @@ where
|
||||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a>;
|
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a>;
|
||||||
|
|
||||||
/// Get output data by id
|
/// Get output data by id
|
||||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error>;
|
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error>;
|
||||||
|
|
||||||
/// Get associated output commitment by id.
|
|
||||||
fn get_commitment(&mut self, id: &Identifier) -> Result<pedersen::Commitment, Error>;
|
|
||||||
|
|
||||||
/// Get an (Optional) tx log entry by uuid
|
/// Get an (Optional) tx log entry by uuid
|
||||||
fn get_tx_log_entry(&self, uuid: &Uuid) -> Result<Option<TxLogEntry>, Error>;
|
fn get_tx_log_entry(&self, uuid: &Uuid) -> Result<Option<TxLogEntry>, Error>;
|
||||||
|
@ -139,13 +136,13 @@ where
|
||||||
fn save(&mut self, out: OutputData) -> Result<(), Error>;
|
fn save(&mut self, out: OutputData) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Gets output data by id
|
/// Gets output data by id
|
||||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error>;
|
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error>;
|
||||||
|
|
||||||
/// Iterate over all output data stored by the backend
|
/// Iterate over all output data stored by the backend
|
||||||
fn iter(&self) -> Box<dyn Iterator<Item = OutputData>>;
|
fn iter(&self) -> Box<dyn Iterator<Item = OutputData>>;
|
||||||
|
|
||||||
/// Delete data about an output from the backend
|
/// Delete data about an output from the backend
|
||||||
fn delete(&mut self, id: &Identifier) -> Result<(), Error>;
|
fn delete(&mut self, id: &Identifier, mmr_index: &Option<u64>) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Save last stored child index of a given parent
|
/// Save last stored child index of a given parent
|
||||||
fn save_child_index(&mut self, parent_key_id: &Identifier, child_n: u32) -> Result<(), Error>;
|
fn save_child_index(&mut self, parent_key_id: &Identifier, child_n: u32) -> Result<(), Error>;
|
||||||
|
@ -211,13 +208,13 @@ pub trait NodeClient: Sync + Send + Clone {
|
||||||
fn get_outputs_from_node(
|
fn get_outputs_from_node(
|
||||||
&self,
|
&self,
|
||||||
wallet_outputs: Vec<pedersen::Commitment>,
|
wallet_outputs: Vec<pedersen::Commitment>,
|
||||||
) -> Result<HashMap<pedersen::Commitment, (String, u64)>, Error>;
|
) -> Result<HashMap<pedersen::Commitment, (String, u64, u64)>, Error>;
|
||||||
|
|
||||||
/// Get a list of outputs from the node by traversing the UTXO
|
/// Get a list of outputs from the node by traversing the UTXO
|
||||||
/// set in PMMR index order.
|
/// set in PMMR index order.
|
||||||
/// Returns
|
/// Returns
|
||||||
/// (last available output index, last insertion index retrieved,
|
/// (last available output index, last insertion index retrieved,
|
||||||
/// outputs(commit, proof, is_coinbase, height))
|
/// outputs(commit, proof, is_coinbase, height, mmr_index))
|
||||||
fn get_outputs_by_pmmr_index(
|
fn get_outputs_by_pmmr_index(
|
||||||
&self,
|
&self,
|
||||||
start_height: u64,
|
start_height: u64,
|
||||||
|
@ -226,7 +223,7 @@ pub trait NodeClient: Sync + Send + Clone {
|
||||||
(
|
(
|
||||||
u64,
|
u64,
|
||||||
u64,
|
u64,
|
||||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||||
),
|
),
|
||||||
Error,
|
Error,
|
||||||
>;
|
>;
|
||||||
|
@ -244,6 +241,9 @@ pub struct OutputData {
|
||||||
pub key_id: Identifier,
|
pub key_id: Identifier,
|
||||||
/// How many derivations down from the root key
|
/// How many derivations down from the root key
|
||||||
pub n_child: u32,
|
pub n_child: u32,
|
||||||
|
/// PMMR Index, used on restore in case of duplicate wallets using the same
|
||||||
|
/// key_id (2 wallets using same seed, for instance
|
||||||
|
pub mmr_index: Option<u64>,
|
||||||
/// Value of the output, necessary to rebuild the commitment
|
/// Value of the output, necessary to rebuild the commitment
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
/// Current status of the output
|
/// Current status of the output
|
||||||
|
@ -368,9 +368,9 @@ pub struct Context {
|
||||||
/// (basically a SecretKey)
|
/// (basically a SecretKey)
|
||||||
pub sec_nonce: SecretKey,
|
pub sec_nonce: SecretKey,
|
||||||
/// store my outputs between invocations
|
/// store my outputs between invocations
|
||||||
pub output_ids: Vec<Identifier>,
|
pub output_ids: Vec<(Identifier, Option<u64>)>,
|
||||||
/// store my inputs
|
/// store my inputs
|
||||||
pub input_ids: Vec<Identifier>,
|
pub input_ids: Vec<(Identifier, Option<u64>)>,
|
||||||
/// store the calculated fee
|
/// store the calculated fee
|
||||||
pub fee: u64,
|
pub fee: u64,
|
||||||
}
|
}
|
||||||
|
@ -391,23 +391,23 @@ impl Context {
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Tracks an output contributing to my excess value (if it needs to
|
/// Tracks an output contributing to my excess value (if it needs to
|
||||||
/// be kept between invocations
|
/// be kept between invocations
|
||||||
pub fn add_output(&mut self, output_id: &Identifier) {
|
pub fn add_output(&mut self, output_id: &Identifier, mmr_index: &Option<u64>) {
|
||||||
self.output_ids.push(output_id.clone());
|
self.output_ids.push((output_id.clone(), mmr_index.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all stored outputs
|
/// Returns all stored outputs
|
||||||
pub fn get_outputs(&self) -> Vec<Identifier> {
|
pub fn get_outputs(&self) -> Vec<(Identifier, Option<u64>)> {
|
||||||
self.output_ids.clone()
|
self.output_ids.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tracks IDs of my inputs into the transaction
|
/// Tracks IDs of my inputs into the transaction
|
||||||
/// be kept between invocations
|
/// be kept between invocations
|
||||||
pub fn add_input(&mut self, input_id: &Identifier) {
|
pub fn add_input(&mut self, input_id: &Identifier, mmr_index: &Option<u64>) {
|
||||||
self.input_ids.push(input_id.clone());
|
self.input_ids.push((input_id.clone(), mmr_index.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all stored input identifiers
|
/// Returns all stored input identifiers
|
||||||
pub fn get_inputs(&self) -> Vec<Identifier> {
|
pub fn get_inputs(&self) -> Vec<(Identifier, Option<u64>)> {
|
||||||
self.input_ids.clone()
|
self.input_ids.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,13 +38,11 @@ use crate::libwallet::{internal, Error, ErrorKind};
|
||||||
use crate::types::{WalletConfig, WalletSeed};
|
use crate::types::{WalletConfig, WalletSeed};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::util::secp::constants::SECRET_KEY_SIZE;
|
use crate::util::secp::constants::SECRET_KEY_SIZE;
|
||||||
use crate::util::secp::pedersen;
|
|
||||||
use crate::util::ZeroingString;
|
use crate::util::ZeroingString;
|
||||||
|
|
||||||
pub const DB_DIR: &'static str = "db";
|
pub const DB_DIR: &'static str = "db";
|
||||||
pub const TX_SAVE_DIR: &'static str = "saved_txs";
|
pub const TX_SAVE_DIR: &'static str = "saved_txs";
|
||||||
|
|
||||||
const COMMITMENT_PREFIX: u8 = 'C' as u8;
|
|
||||||
const OUTPUT_PREFIX: u8 = 'o' as u8;
|
const OUTPUT_PREFIX: u8 = 'o' as u8;
|
||||||
const DERIV_PREFIX: u8 = 'd' as u8;
|
const DERIV_PREFIX: u8 = 'd' as u8;
|
||||||
const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8;
|
const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8;
|
||||||
|
@ -222,38 +220,14 @@ where
|
||||||
self.parent_key_id.clone()
|
self.parent_key_id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
|
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
|
||||||
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
let key = match mmr_index {
|
||||||
|
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
|
||||||
|
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
|
||||||
|
};
|
||||||
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id)).map_err(|e| e.into())
|
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id)).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_commitment(&mut self, id: &Identifier) -> Result<pedersen::Commitment, Error> {
|
|
||||||
let key = to_key(COMMITMENT_PREFIX, &mut id.to_bytes().to_vec());
|
|
||||||
|
|
||||||
let res: Result<pedersen::Commitment, Error> =
|
|
||||||
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id))
|
|
||||||
.map_err(|e| e.into());
|
|
||||||
|
|
||||||
// "cache hit" and return the commitment
|
|
||||||
if let Ok(commit) = res {
|
|
||||||
Ok(commit)
|
|
||||||
} else {
|
|
||||||
let out = self.get(id)?;
|
|
||||||
|
|
||||||
// Save the output data back to the db
|
|
||||||
// which builds and saves the associated commitment.
|
|
||||||
{
|
|
||||||
let mut batch = self.batch()?;
|
|
||||||
batch.save(out)?;
|
|
||||||
batch.commit()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now retrieve the saved commitment and return it.
|
|
||||||
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id))
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a> {
|
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a> {
|
||||||
Box::new(self.db.iter(&[OUTPUT_PREFIX]).unwrap())
|
Box::new(self.db.iter(&[OUTPUT_PREFIX]).unwrap())
|
||||||
}
|
}
|
||||||
|
@ -402,22 +376,21 @@ where
|
||||||
fn save(&mut self, out: OutputData) -> Result<(), Error> {
|
fn save(&mut self, out: OutputData) -> Result<(), Error> {
|
||||||
// Save the output data to the db.
|
// Save the output data to the db.
|
||||||
{
|
{
|
||||||
let key = to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec());
|
let key = match out.mmr_index {
|
||||||
|
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec(), i),
|
||||||
|
None => to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec()),
|
||||||
|
};
|
||||||
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
|
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the associated output commitment.
|
|
||||||
{
|
|
||||||
let key = to_key(COMMITMENT_PREFIX, &mut out.key_id.to_bytes().to_vec());
|
|
||||||
let commit = self.keychain().commit(out.value, &out.key_id)?;
|
|
||||||
self.db.borrow().as_ref().unwrap().put_ser(&key, &commit)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
|
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
|
||||||
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
let key = match mmr_index {
|
||||||
|
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
|
||||||
|
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
|
||||||
|
};
|
||||||
option_to_not_found(
|
option_to_not_found(
|
||||||
self.db.borrow().as_ref().unwrap().get_ser(&key),
|
self.db.borrow().as_ref().unwrap().get_ser(&key),
|
||||||
&format!("Key ID: {}", id),
|
&format!("Key ID: {}", id),
|
||||||
|
@ -436,16 +409,13 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(&mut self, id: &Identifier) -> Result<(), Error> {
|
fn delete(&mut self, id: &Identifier, mmr_index: &Option<u64>) -> Result<(), Error> {
|
||||||
// Delete the output data.
|
// Delete the output data.
|
||||||
{
|
{
|
||||||
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
let key = match mmr_index {
|
||||||
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
|
||||||
}
|
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
|
||||||
|
};
|
||||||
// Delete the associated output commitment.
|
|
||||||
{
|
|
||||||
let key = to_key(COMMITMENT_PREFIX, &mut id.to_bytes().to_vec());
|
|
||||||
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
fn get_outputs_from_node(
|
fn get_outputs_from_node(
|
||||||
&self,
|
&self,
|
||||||
wallet_outputs: Vec<pedersen::Commitment>,
|
wallet_outputs: Vec<pedersen::Commitment>,
|
||||||
) -> Result<HashMap<pedersen::Commitment, (String, u64)>, libwallet::Error> {
|
) -> Result<HashMap<pedersen::Commitment, (String, u64, u64)>, libwallet::Error> {
|
||||||
let addr = self.node_url();
|
let addr = self.node_url();
|
||||||
// build the necessary query params -
|
// build the necessary query params -
|
||||||
// ?id=xxx&id=yyy&id=zzz
|
// ?id=xxx&id=yyy&id=zzz
|
||||||
|
@ -99,7 +99,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// build a map of api outputs by commit so we can look them up efficiently
|
// build a map of api outputs by commit so we can look them up efficiently
|
||||||
let mut api_outputs: HashMap<pedersen::Commitment, (String, u64)> = HashMap::new();
|
let mut api_outputs: HashMap<pedersen::Commitment, (String, u64, u64)> = HashMap::new();
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
for query_chunk in query_params.chunks(500) {
|
for query_chunk in query_params.chunks(500) {
|
||||||
|
@ -125,7 +125,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
for out in res {
|
for out in res {
|
||||||
api_outputs.insert(
|
api_outputs.insert(
|
||||||
out.commit.commit(),
|
out.commit.commit(),
|
||||||
(util::to_hex(out.commit.to_vec()), out.height),
|
(util::to_hex(out.commit.to_vec()), out.height, out.mmr_index),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
(
|
(
|
||||||
u64,
|
u64,
|
||||||
u64,
|
u64,
|
||||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||||
),
|
),
|
||||||
libwallet::Error,
|
libwallet::Error,
|
||||||
> {
|
> {
|
||||||
|
@ -149,7 +149,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
|
|
||||||
let url = format!("{}/v1/txhashset/outputs?{}", addr, query_param,);
|
let url = format!("{}/v1/txhashset/outputs?{}", addr, query_param,);
|
||||||
|
|
||||||
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)> =
|
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)> =
|
||||||
Vec::new();
|
Vec::new();
|
||||||
|
|
||||||
match api::client::get::<api::OutputListing>(url.as_str(), self.node_api_secret()) {
|
match api::client::get::<api::OutputListing>(url.as_str(), self.node_api_secret()) {
|
||||||
|
@ -164,6 +164,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
out.range_proof().unwrap(),
|
out.range_proof().unwrap(),
|
||||||
is_coinbase,
|
is_coinbase,
|
||||||
out.block_height.unwrap(),
|
out.block_height.unwrap(),
|
||||||
|
out.mmr_index,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ use self::core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
||||||
use self::core::{consensus, global, pow, ser};
|
use self::core::{consensus, global, pow, ser};
|
||||||
use self::util::secp::pedersen;
|
use self::util::secp::pedersen;
|
||||||
use self::util::Mutex;
|
use self::util::Mutex;
|
||||||
use crate::libwallet::types::{BlockFees, CbData, NodeClient, WalletInst};
|
use crate::libwallet::api::APIOwner;
|
||||||
|
use crate::libwallet::types::{BlockFees, CbData, NodeClient, WalletInfo, WalletInst};
|
||||||
use crate::lmdb_wallet::LMDBBackend;
|
use crate::lmdb_wallet::LMDBBackend;
|
||||||
use crate::{controller, libwallet, WalletSeed};
|
use crate::{controller, libwallet, WalletSeed};
|
||||||
use crate::{WalletBackend, WalletConfig};
|
use crate::{WalletBackend, WalletConfig};
|
||||||
|
@ -52,7 +53,8 @@ fn get_output_local(chain: &chain::Chain, commit: &pedersen::Commitment) -> Opti
|
||||||
for x in outputs.iter() {
|
for x in outputs.iter() {
|
||||||
if let Ok(_) = chain.is_unspent(&x) {
|
if let Ok(_) = chain.is_unspent(&x) {
|
||||||
let block_height = chain.get_header_for_output(&x).unwrap().height;
|
let block_height = chain.get_header_for_output(&x).unwrap().height;
|
||||||
return Some(api::Output::new(&commit, block_height));
|
let output_pos = chain.get_output_pos(&x.commit).unwrap_or(0);
|
||||||
|
return Some(api::Output::new(&commit, block_height, output_pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -153,14 +155,22 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// dispatch a db wallet
|
/// dispatch a db wallet
|
||||||
pub fn create_wallet<C, K>(dir: &str, n_client: C) -> Arc<Mutex<dyn WalletInst<C, K>>>
|
pub fn create_wallet<C, K>(
|
||||||
|
dir: &str,
|
||||||
|
n_client: C,
|
||||||
|
rec_phrase: Option<&str>,
|
||||||
|
) -> Arc<Mutex<dyn WalletInst<C, K>>>
|
||||||
where
|
where
|
||||||
C: NodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
K: keychain::Keychain + 'static,
|
K: keychain::Keychain + 'static,
|
||||||
{
|
{
|
||||||
|
let z_string = match rec_phrase {
|
||||||
|
Some(s) => Some(util::ZeroingString::from(s)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
let mut wallet_config = WalletConfig::default();
|
let mut wallet_config = WalletConfig::default();
|
||||||
wallet_config.data_file_dir = String::from(dir);
|
wallet_config.data_file_dir = String::from(dir);
|
||||||
let _ = WalletSeed::init_file(&wallet_config, 32, None, "");
|
let _ = WalletSeed::init_file(&wallet_config, 32, z_string, "");
|
||||||
let mut wallet = LMDBBackend::new(wallet_config.clone(), "", n_client)
|
let mut wallet = LMDBBackend::new(wallet_config.clone(), "", n_client)
|
||||||
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config));
|
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config));
|
||||||
wallet.open_with_credentials().unwrap_or_else(|e| {
|
wallet.open_with_credentials().unwrap_or_else(|e| {
|
||||||
|
@ -171,3 +181,45 @@ where
|
||||||
});
|
});
|
||||||
Arc::new(Mutex::new(wallet))
|
Arc::new(Mutex::new(wallet))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// send an amount to a destination
|
||||||
|
pub fn send_to_dest<T: ?Sized, C, K>(
|
||||||
|
client: LocalWalletClient,
|
||||||
|
api: &mut APIOwner<T, C, K>,
|
||||||
|
dest: &str,
|
||||||
|
amount: u64,
|
||||||
|
) -> Result<(), libwallet::Error>
|
||||||
|
where
|
||||||
|
T: WalletBackend<C, K>,
|
||||||
|
C: NodeClient,
|
||||||
|
K: keychain::Keychain,
|
||||||
|
{
|
||||||
|
let (slate_i, lock_fn) = api.initiate_tx(
|
||||||
|
None, // account
|
||||||
|
amount, // amount
|
||||||
|
2, // minimum confirmations
|
||||||
|
500, // max outputs
|
||||||
|
1, // num change outputs
|
||||||
|
true, // select all outputs
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
let mut slate = client.send_tx_slate_direct(dest, &slate_i)?;
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
|
api.finalize_tx(&mut slate)?;
|
||||||
|
api.post_tx(&slate.tx, false)?; // mines a block
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get wallet info totals
|
||||||
|
pub fn wallet_info<T: ?Sized, C, K>(
|
||||||
|
api: &mut APIOwner<T, C, K>,
|
||||||
|
) -> Result<WalletInfo, libwallet::Error>
|
||||||
|
where
|
||||||
|
T: WalletBackend<C, K>,
|
||||||
|
C: NodeClient,
|
||||||
|
K: keychain::Keychain,
|
||||||
|
{
|
||||||
|
let (wallet_refreshed, wallet_info) = api.retrieve_summary_info(true, 1)?;
|
||||||
|
assert!(wallet_refreshed);
|
||||||
|
Ok(wallet_info)
|
||||||
|
}
|
||||||
|
|
|
@ -442,7 +442,7 @@ impl NodeClient for LocalWalletClient {
|
||||||
fn get_outputs_from_node(
|
fn get_outputs_from_node(
|
||||||
&self,
|
&self,
|
||||||
wallet_outputs: Vec<pedersen::Commitment>,
|
wallet_outputs: Vec<pedersen::Commitment>,
|
||||||
) -> Result<HashMap<pedersen::Commitment, (String, u64)>, libwallet::Error> {
|
) -> Result<HashMap<pedersen::Commitment, (String, u64, u64)>, libwallet::Error> {
|
||||||
let query_params: Vec<String> = wallet_outputs
|
let query_params: Vec<String> = wallet_outputs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|commit| format!("{}", util::to_hex(commit.as_ref().to_vec())))
|
.map(|commit| format!("{}", util::to_hex(commit.as_ref().to_vec())))
|
||||||
|
@ -463,11 +463,11 @@ impl NodeClient for LocalWalletClient {
|
||||||
let r = self.rx.lock();
|
let r = self.rx.lock();
|
||||||
let m = r.recv().unwrap();
|
let m = r.recv().unwrap();
|
||||||
let outputs: Vec<api::Output> = serde_json::from_str(&m.body).unwrap();
|
let outputs: Vec<api::Output> = serde_json::from_str(&m.body).unwrap();
|
||||||
let mut api_outputs: HashMap<pedersen::Commitment, (String, u64)> = HashMap::new();
|
let mut api_outputs: HashMap<pedersen::Commitment, (String, u64, u64)> = HashMap::new();
|
||||||
for out in outputs {
|
for out in outputs {
|
||||||
api_outputs.insert(
|
api_outputs.insert(
|
||||||
out.commit.commit(),
|
out.commit.commit(),
|
||||||
(util::to_hex(out.commit.to_vec()), out.height),
|
(util::to_hex(out.commit.to_vec()), out.height, out.mmr_index),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(api_outputs)
|
Ok(api_outputs)
|
||||||
|
@ -481,7 +481,7 @@ impl NodeClient for LocalWalletClient {
|
||||||
(
|
(
|
||||||
u64,
|
u64,
|
||||||
u64,
|
u64,
|
||||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||||
),
|
),
|
||||||
libwallet::Error,
|
libwallet::Error,
|
||||||
> {
|
> {
|
||||||
|
@ -504,7 +504,7 @@ impl NodeClient for LocalWalletClient {
|
||||||
let m = r.recv().unwrap();
|
let m = r.recv().unwrap();
|
||||||
let o: api::OutputListing = serde_json::from_str(&m.body).unwrap();
|
let o: api::OutputListing = serde_json::from_str(&m.body).unwrap();
|
||||||
|
|
||||||
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)> =
|
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)> =
|
||||||
Vec::new();
|
Vec::new();
|
||||||
|
|
||||||
for out in o.outputs {
|
for out in o.outputs {
|
||||||
|
@ -517,6 +517,7 @@ impl NodeClient for LocalWalletClient {
|
||||||
out.range_proof().unwrap(),
|
out.range_proof().unwrap(),
|
||||||
is_coinbase,
|
is_coinbase,
|
||||||
out.block_height.unwrap(),
|
out.block_height.unwrap(),
|
||||||
|
out.mmr_index,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok((o.highest_index, o.last_retrieved_index, api_outputs))
|
Ok((o.highest_index, o.last_retrieved_index, api_outputs))
|
||||||
|
|
|
@ -48,12 +48,14 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let wallet2 = test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
let wallet2 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
use self::core::consensus;
|
||||||
use self::core::global;
|
use self::core::global;
|
||||||
use self::core::global::ChainTypes;
|
use self::core::global::ChainTypes;
|
||||||
use self::keychain::ExtKeychain;
|
use self::keychain::ExtKeychain;
|
||||||
|
@ -38,7 +39,7 @@ fn setup(test_dir: &str) {
|
||||||
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Various tests on accounts within the same wallet
|
/// Various tests on checking functionality
|
||||||
fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
// Create a new proxy to simulate server and wallet responses
|
// Create a new proxy to simulate server and wallet responses
|
||||||
|
@ -48,12 +49,14 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let wallet2 = test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
let wallet2 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
@ -65,7 +68,7 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
|
|
||||||
// few values to keep things shorter
|
// few values to keep things shorter
|
||||||
let reward = core::consensus::REWARD;
|
let reward = core::consensus::REWARD;
|
||||||
let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height
|
let cm = global::coinbase_maturity() as u64; // assume all testing precedes soft fork height
|
||||||
|
|
||||||
// add some accounts
|
// add some accounts
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
@ -115,8 +118,8 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
{
|
{
|
||||||
let mut batch = w.batch()?;
|
let mut batch = w.batch()?;
|
||||||
batch.delete(&w1_outputs[4].key_id)?;
|
batch.delete(&w1_outputs[4].key_id, &None)?;
|
||||||
batch.delete(&w1_outputs[10].key_id)?;
|
batch.delete(&w1_outputs[10].key_id, &None)?;
|
||||||
let mut accidental_spent = w1_outputs[13].clone();
|
let mut accidental_spent = w1_outputs[13].clone();
|
||||||
accidental_spent.status = libwallet::types::OutputStatus::Spent;
|
accidental_spent.status = libwallet::types::OutputStatus::Spent;
|
||||||
batch.save(accidental_spent)?;
|
batch.save(accidental_spent)?;
|
||||||
|
@ -193,6 +196,385 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
|
setup(test_dir);
|
||||||
|
let seed_phrase =
|
||||||
|
"affair pistol cancel crush garment candy ancient flag work \
|
||||||
|
market crush dry stand focus mutual weapon offer ceiling rival turn team spring \
|
||||||
|
where swift";
|
||||||
|
|
||||||
|
// Create a new proxy to simulate server and wallet responses
|
||||||
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
|
||||||
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
|
// proxy
|
||||||
|
let m_client = LocalWalletClient::new("miner", wallet_proxy.tx.clone());
|
||||||
|
let miner =
|
||||||
|
test_framework::create_wallet(&format!("{}/miner", test_dir), m_client.clone(), None);
|
||||||
|
wallet_proxy.add_wallet("miner", m_client.get_send_instance(), miner.clone());
|
||||||
|
|
||||||
|
// non-mining recipient wallets
|
||||||
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
|
let wallet1 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet1", test_dir),
|
||||||
|
client1.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
|
let wallet2 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet2", test_dir),
|
||||||
|
client2.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
|
// we'll restore into here
|
||||||
|
let client3 = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone());
|
||||||
|
let wallet3 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet3", test_dir),
|
||||||
|
client3.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet3", client3.get_send_instance(), wallet3.clone());
|
||||||
|
|
||||||
|
// also restore into here
|
||||||
|
let client4 = LocalWalletClient::new("wallet4", wallet_proxy.tx.clone());
|
||||||
|
let wallet4 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet4", test_dir),
|
||||||
|
client4.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet4", client4.get_send_instance(), wallet4.clone());
|
||||||
|
|
||||||
|
// Simulate a recover from seed without restore into here
|
||||||
|
let client5 = LocalWalletClient::new("wallet5", wallet_proxy.tx.clone());
|
||||||
|
let wallet5 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet5", test_dir),
|
||||||
|
client5.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet5", client5.get_send_instance(), wallet5.clone());
|
||||||
|
|
||||||
|
//simulate a recover from seed without restore into here
|
||||||
|
let client6 = LocalWalletClient::new("wallet6", wallet_proxy.tx.clone());
|
||||||
|
let wallet6 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet6", test_dir),
|
||||||
|
client6.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet6", client6.get_send_instance(), wallet6.clone());
|
||||||
|
|
||||||
|
let client7 = LocalWalletClient::new("wallet7", wallet_proxy.tx.clone());
|
||||||
|
let wallet7 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet7", test_dir),
|
||||||
|
client7.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet7", client7.get_send_instance(), wallet7.clone());
|
||||||
|
|
||||||
|
let client8 = LocalWalletClient::new("wallet8", wallet_proxy.tx.clone());
|
||||||
|
let wallet8 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet8", test_dir),
|
||||||
|
client8.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet8", client8.get_send_instance(), wallet8.clone());
|
||||||
|
|
||||||
|
let client9 = LocalWalletClient::new("wallet9", wallet_proxy.tx.clone());
|
||||||
|
let wallet9 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet9", test_dir),
|
||||||
|
client9.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet9", client9.get_send_instance(), wallet9.clone());
|
||||||
|
|
||||||
|
let client10 = LocalWalletClient::new("wallet10", wallet_proxy.tx.clone());
|
||||||
|
let wallet10 = test_framework::create_wallet(
|
||||||
|
&format!("{}/wallet10", test_dir),
|
||||||
|
client10.clone(),
|
||||||
|
Some(seed_phrase),
|
||||||
|
);
|
||||||
|
wallet_proxy.add_wallet("wallet10", client10.get_send_instance(), wallet10.clone());
|
||||||
|
|
||||||
|
// Set the wallet proxy listener running
|
||||||
|
thread::spawn(move || {
|
||||||
|
if let Err(e) = wallet_proxy.run() {
|
||||||
|
error!("Wallet Proxy error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// few values to keep things shorter
|
||||||
|
let _reward = core::consensus::REWARD;
|
||||||
|
let cm = global::coinbase_maturity() as usize; // assume all testing precedes soft fork height
|
||||||
|
|
||||||
|
// Do some mining
|
||||||
|
let mut bh = 20u64;
|
||||||
|
let base_amount = consensus::GRIN_BASE;
|
||||||
|
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), bh as usize);
|
||||||
|
|
||||||
|
// send some funds to wallets 1
|
||||||
|
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 1)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 2)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 3)?;
|
||||||
|
bh += 3;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 0) Check repair when all is okay should leave wallet contents alone
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
api.check_repair()?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||||
|
assert_eq!(info.total, base_amount * 6);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// send some funds to wallet 2
|
||||||
|
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 4)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 5)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 6)?;
|
||||||
|
bh += 3;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm);
|
||||||
|
bh += cm as u64;
|
||||||
|
|
||||||
|
// confirm balances
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||||
|
assert_eq!(info.total, base_amount * 6);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 15);
|
||||||
|
assert_eq!(info.total, base_amount * 15);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Now there should be outputs on the chain using the same
|
||||||
|
// seed + BIP32 path.
|
||||||
|
|
||||||
|
// 1) a full restore should recover all of them:
|
||||||
|
wallet::controller::owner_single_use(wallet3.clone(), |api| {
|
||||||
|
api.restore()?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet3.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 6);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||||
|
assert_eq!(info.total, base_amount * 21);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 2) check_repair should recover them into a single wallet
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
api.check_repair()?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 6);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 3) If I recover from seed and start using the wallet without restoring,
|
||||||
|
// check_repair should restore the older outputs
|
||||||
|
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 7)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 8)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 9)?;
|
||||||
|
bh += 3;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm);
|
||||||
|
bh += cm as u64;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet4.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 3);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 24);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet5.clone(), |api| {
|
||||||
|
api.restore()?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet5.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 9);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * (45));
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 4) If I recover from seed and start using the wallet without restoring,
|
||||||
|
// check_repair should restore the older outputs
|
||||||
|
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 10)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 11)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 12)?;
|
||||||
|
bh += 3;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm as usize);
|
||||||
|
bh += cm as u64;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet6.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 3);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 33);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet6.clone(), |api| {
|
||||||
|
api.check_repair()?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet6.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 12);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * (78));
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 5) Start using same seed with a different account, amounts should
|
||||||
|
// be distinct and restore should return funds from other account
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 13)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 14)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 15)?;
|
||||||
|
bh += 3;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// mix it up a bit
|
||||||
|
wallet::controller::owner_single_use(wallet7.clone(), |api| {
|
||||||
|
api.create_account_path("account_1")?;
|
||||||
|
api.set_active_account("account_1")?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 1)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 2)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 3)?;
|
||||||
|
bh += 3;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// check balances
|
||||||
|
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm);
|
||||||
|
bh += cm as u64;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet7.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 3);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||||
|
api.set_active_account("default")?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 3);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 42);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet8.clone(), |api| {
|
||||||
|
api.restore()?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 15);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 120);
|
||||||
|
api.set_active_account("account_1")?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 3);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 6);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 6) Start using same seed with a different account, now overwriting
|
||||||
|
// ids on account 2 as well, check_repair should get all outputs created
|
||||||
|
// to now into 2 accounts
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet9.clone(), |api| {
|
||||||
|
api.create_account_path("account_1")?;
|
||||||
|
api.set_active_account("account_1")?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
wallet::controller::owner_single_use(miner.clone(), |api| {
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 4)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 5)?;
|
||||||
|
test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 6)?;
|
||||||
|
bh += 3;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
wallet::controller::owner_single_use(wallet9.clone(), |api| {
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 3);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 15);
|
||||||
|
api.check_repair()?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 6);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||||
|
|
||||||
|
api.set_active_account("default")?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 15);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 120);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm);
|
||||||
|
bh += cm as u64;
|
||||||
|
|
||||||
|
// 7) Ensure check_repair creates missing accounts
|
||||||
|
wallet::controller::owner_single_use(wallet10.clone(), |api| {
|
||||||
|
api.check_repair()?;
|
||||||
|
api.set_active_account("account_1")?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 6);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 21);
|
||||||
|
|
||||||
|
api.set_active_account("default")?;
|
||||||
|
let info = test_framework::wallet_info(api)?;
|
||||||
|
let outputs = api.retrieve_outputs(true, false, None)?.1;
|
||||||
|
assert_eq!(outputs.len(), 15);
|
||||||
|
assert_eq!(info.amount_currently_spendable, base_amount * 120);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// let logging finish
|
||||||
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn check_repair() {
|
fn check_repair() {
|
||||||
let test_dir = "test_output/check_repair";
|
let test_dir = "test_output/check_repair";
|
||||||
|
@ -200,3 +582,11 @@ fn check_repair() {
|
||||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_wallets_one_seed() {
|
||||||
|
let test_dir = "test_output/two_wallets_one_seed";
|
||||||
|
if let Err(e) = two_wallets_one_seed_impl(test_dir) {
|
||||||
|
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -45,11 +45,13 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
let chain = wallet_proxy.chain.clone();
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
let wallet2 = test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
let wallet2 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
|
|
@ -89,7 +89,7 @@ fn aggsig_sender_receiver_interaction() {
|
||||||
|
|
||||||
rx_cx = Context::new(&keychain.secp(), blind);
|
rx_cx = Context::new(&keychain.secp(), blind);
|
||||||
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
||||||
rx_cx.add_output(&key_id);
|
rx_cx.add_output(&key_id, &None);
|
||||||
|
|
||||||
pub_nonce_sum = PublicKey::from_combination(
|
pub_nonce_sum = PublicKey::from_combination(
|
||||||
keychain.secp(),
|
keychain.secp(),
|
||||||
|
@ -305,7 +305,7 @@ fn aggsig_sender_receiver_interaction_offset() {
|
||||||
|
|
||||||
rx_cx = Context::new(&keychain.secp(), blind);
|
rx_cx = Context::new(&keychain.secp(), blind);
|
||||||
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
||||||
rx_cx.add_output(&key_id);
|
rx_cx.add_output(&key_id, &None);
|
||||||
|
|
||||||
pub_nonce_sum = PublicKey::from_combination(
|
pub_nonce_sum = PublicKey::from_combination(
|
||||||
keychain.secp(),
|
keychain.secp(),
|
||||||
|
|
|
@ -47,11 +47,13 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
let chain = wallet_proxy.chain.clone();
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
let wallet2 = test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
let wallet2 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
|
|
@ -49,7 +49,7 @@ fn restore_wallet(base_dir: &str, wallet_dir: &str) -> Result<(), libwallet::Err
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
|
||||||
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
||||||
|
|
||||||
let wallet = test_framework::create_wallet(&dest_dir, client.clone());
|
let wallet = test_framework::create_wallet(&dest_dir, client.clone(), None);
|
||||||
|
|
||||||
wallet_proxy.add_wallet(wallet_dir, client.get_send_instance(), wallet.clone());
|
wallet_proxy.add_wallet(wallet_dir, client.get_send_instance(), wallet.clone());
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ fn compare_wallet_restore(
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
|
||||||
|
|
||||||
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
||||||
let wallet_source = test_framework::create_wallet(&source_dir, client.clone());
|
let wallet_source = test_framework::create_wallet(&source_dir, client.clone(), None);
|
||||||
wallet_proxy.add_wallet(
|
wallet_proxy.add_wallet(
|
||||||
&wallet_dir,
|
&wallet_dir,
|
||||||
client.get_send_instance(),
|
client.get_send_instance(),
|
||||||
|
@ -90,7 +90,7 @@ fn compare_wallet_restore(
|
||||||
);
|
);
|
||||||
|
|
||||||
let client = LocalWalletClient::new(&restore_name, wallet_proxy.tx.clone());
|
let client = LocalWalletClient::new(&restore_name, wallet_proxy.tx.clone());
|
||||||
let wallet_dest = test_framework::create_wallet(&dest_dir, client.clone());
|
let wallet_dest = test_framework::create_wallet(&dest_dir, client.clone(), None);
|
||||||
wallet_proxy.add_wallet(
|
wallet_proxy.add_wallet(
|
||||||
&restore_name,
|
&restore_name,
|
||||||
client.get_send_instance(),
|
client.get_send_instance(),
|
||||||
|
@ -178,12 +178,14 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
let wallet2 = test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
let wallet2 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
// wallet 2 will use another account
|
// wallet 2 will use another account
|
||||||
|
@ -201,7 +203,8 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
|
|
||||||
// Another wallet
|
// Another wallet
|
||||||
let client3 = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone());
|
let client3 = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone());
|
||||||
let wallet3 = test_framework::create_wallet(&format!("{}/wallet3", test_dir), client3.clone());
|
let wallet3 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet3", test_dir), client3.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet3", client3.get_send_instance(), wallet3.clone());
|
wallet_proxy.add_wallet("wallet3", client3.get_send_instance(), wallet3.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
|
|
@ -48,7 +48,8 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
|
|
@ -51,12 +51,14 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let wallet2 = test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
let wallet2 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
@ -299,12 +301,14 @@ fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
let wallet1 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
let wallet2 = test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
let wallet2 =
|
||||||
|
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
|
||||||
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
|
|
Loading…
Reference in a new issue