mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11: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
|
||||
)))?;
|
||||
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)
|
||||
.map_err(|_| ErrorKind::NotFound)?;
|
||||
Ok(OutputPrintable {
|
||||
|
@ -104,6 +107,7 @@ impl TxHashSetHandler {
|
|||
proof_hash: "".to_string(),
|
||||
block_height: None,
|
||||
merkle_proof: Some(merkle_proof),
|
||||
mmr_index: output_pos,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ pub fn get_output(
|
|||
for x in outputs.iter() {
|
||||
if let Ok(_) = w(chain).is_unspent(&x) {
|
||||
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)?
|
||||
|
|
|
@ -159,15 +159,18 @@ pub struct Output {
|
|||
pub commit: PrintableCommitment,
|
||||
/// Height of the block which contains the output
|
||||
pub height: u64,
|
||||
/// MMR Index of output
|
||||
pub mmr_index: u64,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn new(commit: &pedersen::Commitment, height: u64) -> Output {
|
||||
pub fn new(commit: &pedersen::Commitment, height: u64, mmr_index: u64) -> Output {
|
||||
Output {
|
||||
commit: PrintableCommitment {
|
||||
commit: commit.clone(),
|
||||
},
|
||||
height: height,
|
||||
mmr_index: mmr_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +245,8 @@ pub struct OutputPrintable {
|
|||
pub block_height: Option<u64>,
|
||||
/// Merkle Proof
|
||||
pub merkle_proof: Option<MerkleProof>,
|
||||
/// MMR Position
|
||||
pub mmr_index: u64,
|
||||
}
|
||||
|
||||
impl OutputPrintable {
|
||||
|
@ -279,6 +284,8 @@ impl OutputPrintable {
|
|||
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 {
|
||||
output_type,
|
||||
commit: output.commit,
|
||||
|
@ -287,6 +294,7 @@ impl OutputPrintable {
|
|||
proof_hash: util::to_hex(output.proof.hash().to_vec()),
|
||||
block_height,
|
||||
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());
|
||||
state.serialize_field("merkle_proof", &hex_merkle_proof)?;
|
||||
state.serialize_field("mmr_index", &self.mmr_index)?;
|
||||
|
||||
state.end()
|
||||
}
|
||||
|
@ -347,6 +356,7 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
|
|||
ProofHash,
|
||||
BlockHeight,
|
||||
MerkleProof,
|
||||
MmrIndex,
|
||||
}
|
||||
|
||||
struct OutputPrintableVisitor;
|
||||
|
@ -369,6 +379,7 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
|
|||
let mut proof_hash = None;
|
||||
let mut block_height = None;
|
||||
let mut merkle_proof = None;
|
||||
let mut mmr_index = None;
|
||||
|
||||
while let Some(key) = map.next_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(),
|
||||
block_height: block_height,
|
||||
merkle_proof: merkle_proof,
|
||||
mmr_index: mmr_index.unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const FIELDS: &'static [&'static str] =
|
||||
&["output_type", "commit", "spent", "proof", "proof_hash"];
|
||||
const FIELDS: &'static [&'static str] = &[
|
||||
"output_type",
|
||||
"commit",
|
||||
"spent",
|
||||
"proof",
|
||||
"proof_hash",
|
||||
"mmr_index",
|
||||
];
|
||||
deserializer.deserialize_struct("OutputPrintable", FIELDS, OutputPrintableVisitor)
|
||||
}
|
||||
}
|
||||
|
@ -662,7 +684,8 @@ mod test {
|
|||
\"proof\":null,\
|
||||
\"proof_hash\":\"ed6ba96009b86173bade6a9227ed60422916593fa32dd6d78b25b7a4eeef4946\",\
|
||||
\"block_height\":0,\
|
||||
\"merkle_proof\":null\
|
||||
\"merkle_proof\":null,\
|
||||
\"mmr_index\":0\
|
||||
}";
|
||||
let deserialized: OutputPrintable = serde_json::from_str(&hex_output).unwrap();
|
||||
let serialized = serde_json::to_string(&deserialized).unwrap();
|
||||
|
@ -674,7 +697,8 @@ mod test {
|
|||
let hex_commit =
|
||||
"{\
|
||||
\"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\",\
|
||||
\"height\":0\
|
||||
\"height\":0,\
|
||||
\"mmr_index\":0\
|
||||
}";
|
||||
let deserialized: Output = serde_json::from_str(&hex_commit).unwrap();
|
||||
let serialized = serde_json::to_string(&deserialized).unwrap();
|
||||
|
|
|
@ -1041,6 +1041,11 @@ impl Chain {
|
|||
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
|
||||
pub fn unspent_outputs_by_insertion_index(
|
||||
&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.
|
||||
pub fn merkle_proof(&mut self, commit: Commitment) -> Result<MerkleProof, String> {
|
||||
let pos = self.commit_index.get_output_pos(&commit).unwrap();
|
||||
|
|
|
@ -44,6 +44,7 @@ pub fn outputs(
|
|||
|
||||
table.set_titles(row![
|
||||
bMG->"Output Commitment",
|
||||
bMG->"MMR Index",
|
||||
bMG->"Block Height",
|
||||
bMG->"Locked Until",
|
||||
bMG->"Status",
|
||||
|
@ -55,6 +56,10 @@ pub fn outputs(
|
|||
|
||||
for (out, commit) in outputs {
|
||||
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 lock_height = format!("{}", out.lock_height);
|
||||
let is_coinbase = format!("{}", out.is_coinbase);
|
||||
|
@ -75,6 +80,7 @@ pub fn outputs(
|
|||
if dark_background_color_scheme {
|
||||
table.add_row(row![
|
||||
bFC->commit,
|
||||
bFB->index,
|
||||
bFB->height,
|
||||
bFB->lock_height,
|
||||
bFR->status,
|
||||
|
|
|
@ -32,13 +32,14 @@ where
|
|||
pub fn retrieve_existing_key<T: ?Sized, C, K>(
|
||||
wallet: &T,
|
||||
key_id: Identifier,
|
||||
mmr_index: Option<u64>,
|
||||
) -> Result<(Identifier, u32), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let existing = wallet.get(&key_id)?;
|
||||
let existing = wallet.get(&key_id, &mmr_index)?;
|
||||
let key_id = existing.key_id.clone();
|
||||
let derivation = existing.n_child;
|
||||
Ok((key_id, derivation))
|
||||
|
|
|
@ -32,6 +32,8 @@ struct OutputResult {
|
|||
///
|
||||
pub n_child: u32,
|
||||
///
|
||||
pub mmr_index: u64,
|
||||
///
|
||||
pub value: u64,
|
||||
///
|
||||
pub height: u64,
|
||||
|
@ -45,7 +47,7 @@ struct OutputResult {
|
|||
|
||||
fn identify_utxo_outputs<T, C, K>(
|
||||
wallet: &mut T,
|
||||
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
||||
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||
) -> Result<Vec<OutputResult>, Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
|
@ -60,7 +62,7 @@ where
|
|||
);
|
||||
|
||||
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
|
||||
// will fail if it's not ours
|
||||
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());
|
||||
|
||||
info!(
|
||||
"Output found: {:?}, amount: {:?}, key_id: {:?}",
|
||||
commit, info.value, key_id
|
||||
"Output found: {:?}, amount: {:?}, key_id: {:?}, mmr_index: {},",
|
||||
commit, info.value, key_id, mmr_index,
|
||||
);
|
||||
|
||||
wallet_outputs.push(OutputResult {
|
||||
|
@ -93,6 +95,7 @@ where
|
|||
lock_height: lock_height,
|
||||
is_coinbase: *is_coinbase,
|
||||
blinding: info.blinding,
|
||||
mmr_index: *mmr_index,
|
||||
});
|
||||
}
|
||||
Ok(wallet_outputs)
|
||||
|
@ -163,6 +166,7 @@ where
|
|||
root_key_id: parent_key_id.clone(),
|
||||
key_id: output.key_id,
|
||||
n_child: output.n_child,
|
||||
mmr_index: Some(output.mmr_index),
|
||||
value: output.value,
|
||||
status: OutputStatus::Unspent,
|
||||
height: output.height,
|
||||
|
@ -246,7 +250,7 @@ where
|
|||
|
||||
// check all definitive outputs exist in the wallet outputs
|
||||
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 {
|
||||
Some(s) => {
|
||||
if s.0.status == OutputStatus::Spent {
|
||||
|
@ -317,10 +321,30 @@ where
|
|||
);
|
||||
cancel_tx_log_entry(wallet, &o)?;
|
||||
let mut batch = wallet.batch()?;
|
||||
batch.delete(&o.key_id)?;
|
||||
batch.delete(&o.key_id, &o.mmr_index)?;
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -82,12 +82,12 @@ where
|
|||
|
||||
// Store our private identifiers for each input
|
||||
for input in inputs {
|
||||
context.add_input(&input.key_id);
|
||||
context.add_input(&input.key_id, &input.mmr_index);
|
||||
}
|
||||
|
||||
// Store change output(s)
|
||||
for (_, id) in &change_amounts_derivations {
|
||||
context.add_output(&id);
|
||||
for (_, id, mmr_index) in &change_amounts_derivations {
|
||||
context.add_output(&id, &mmr_index);
|
||||
}
|
||||
|
||||
let lock_inputs = context.get_inputs().clone();
|
||||
|
@ -107,7 +107,7 @@ where
|
|||
let mut amount_debited = 0;
|
||||
t.num_inputs = lock_inputs.len();
|
||||
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);
|
||||
amount_debited = amount_debited + coin.value;
|
||||
batch.lock_output(&mut coin)?;
|
||||
|
@ -116,13 +116,14 @@ where
|
|||
t.amount_debited = amount_debited;
|
||||
|
||||
// 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.amount_credited += change_amount;
|
||||
batch.save(OutputData {
|
||||
root_key_id: parent_key_id.clone(),
|
||||
key_id: id.clone(),
|
||||
n_child: id.to_path().last_path_index(),
|
||||
mmr_index: None,
|
||||
value: change_amount.clone(),
|
||||
status: OutputStatus::Unconfirmed,
|
||||
height: current_height,
|
||||
|
@ -183,7 +184,7 @@ where
|
|||
.unwrap(),
|
||||
);
|
||||
|
||||
context.add_output(&key_id);
|
||||
context.add_output(&key_id, &None);
|
||||
|
||||
// Create closure that adds the output to recipient's wallet
|
||||
// (up to the caller to decide when to do)
|
||||
|
@ -197,6 +198,7 @@ where
|
|||
batch.save(OutputData {
|
||||
root_key_id: parent_key_id.clone(),
|
||||
key_id: key_id_inner.clone(),
|
||||
mmr_index: None,
|
||||
n_child: key_id_inner.to_path().last_path_index(),
|
||||
value: amount,
|
||||
status: OutputStatus::Unconfirmed,
|
||||
|
@ -229,9 +231,9 @@ pub fn select_send_tx<T: ?Sized, C, K>(
|
|||
(
|
||||
Vec<Box<build::Append<K>>>,
|
||||
Vec<OutputData>,
|
||||
Vec<(u64, Identifier)>, // change amounts and derivations
|
||||
u64, // amount
|
||||
u64, // fee
|
||||
Vec<(u64, Identifier, Option<u64>)>, // change amounts and derivations
|
||||
u64, // amount
|
||||
u64, // fee
|
||||
),
|
||||
Error,
|
||||
>
|
||||
|
@ -337,7 +339,13 @@ pub fn inputs_and_change<T: ?Sized, C, K>(
|
|||
amount: u64,
|
||||
fee: u64,
|
||||
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
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
|
@ -387,7 +395,7 @@ where
|
|||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,11 +69,12 @@ where
|
|||
}
|
||||
|
||||
outputs.sort_by_key(|out| out.n_child);
|
||||
let keychain = wallet.keychain().clone();
|
||||
|
||||
let res = outputs
|
||||
.into_iter()
|
||||
.map(|out| {
|
||||
let commit = wallet.get_commitment(&out.key_id).unwrap();
|
||||
let commit = keychain.commit(out.value, &out.key_id).unwrap();
|
||||
(out, commit)
|
||||
})
|
||||
.collect();
|
||||
|
@ -138,13 +139,14 @@ pub fn map_wallet_outputs<T: ?Sized, C, K>(
|
|||
wallet: &mut T,
|
||||
parent_key_id: &Identifier,
|
||||
update_all: bool,
|
||||
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
|
||||
) -> Result<HashMap<pedersen::Commitment, (Identifier, Option<u64>)>, Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: NodeClient,
|
||||
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 unspents: Vec<OutputData> = wallet
|
||||
.iter()
|
||||
|
@ -175,7 +177,7 @@ where
|
|||
|
||||
for out in unspents {
|
||||
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)
|
||||
}
|
||||
|
@ -197,7 +199,7 @@ where
|
|||
for mut o in outputs {
|
||||
// unlock locked outputs
|
||||
if o.status == OutputStatus::Unconfirmed {
|
||||
batch.delete(&o.key_id)?;
|
||||
batch.delete(&o.key_id, &o.mmr_index)?;
|
||||
}
|
||||
if o.status == OutputStatus::Locked {
|
||||
o.status = OutputStatus::Unconfirmed;
|
||||
|
@ -219,8 +221,8 @@ where
|
|||
/// Apply refreshed API output data to the wallet
|
||||
pub fn apply_api_outputs<T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
wallet_outputs: &HashMap<pedersen::Commitment, Identifier>,
|
||||
api_outputs: &HashMap<pedersen::Commitment, (String, u64)>,
|
||||
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>)>,
|
||||
api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>,
|
||||
height: u64,
|
||||
parent_key_id: &Identifier,
|
||||
) -> Result<(), libwallet::Error>
|
||||
|
@ -245,8 +247,8 @@ where
|
|||
return Ok(());
|
||||
}
|
||||
let mut batch = wallet.batch()?;
|
||||
for (commit, id) in wallet_outputs.iter() {
|
||||
if let Ok(mut output) = batch.get(id) {
|
||||
for (commit, (id, mmr_index)) in wallet_outputs.iter() {
|
||||
if let Ok(mut output) = batch.get(id, mmr_index) {
|
||||
match api_outputs.get(&commit) {
|
||||
Some(o) => {
|
||||
// if this is a coinbase tx being confirmed, it's recordable in tx log
|
||||
|
@ -345,7 +347,7 @@ where
|
|||
}
|
||||
let mut batch = wallet.batch()?;
|
||||
for id in ids_to_del {
|
||||
batch.delete(&id)?;
|
||||
batch.delete(&id, &None)?;
|
||||
}
|
||||
batch.commit()?;
|
||||
Ok(())
|
||||
|
@ -458,7 +460,7 @@ where
|
|||
let parent_key_id = wallet.parent_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)?,
|
||||
};
|
||||
|
||||
|
@ -469,6 +471,7 @@ where
|
|||
root_key_id: parent_key_id,
|
||||
key_id: key_id.clone(),
|
||||
n_child: key_id.to_path().last_path_index(),
|
||||
mmr_index: None,
|
||||
value: reward(block_fees.fees),
|
||||
status: OutputStatus::Unconfirmed,
|
||||
height: height,
|
||||
|
|
|
@ -81,10 +81,7 @@ where
|
|||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a>;
|
||||
|
||||
/// Get output data by id
|
||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error>;
|
||||
|
||||
/// Get associated output commitment by id.
|
||||
fn get_commitment(&mut self, id: &Identifier) -> Result<pedersen::Commitment, Error>;
|
||||
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error>;
|
||||
|
||||
/// Get an (Optional) tx log entry by uuid
|
||||
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>;
|
||||
|
||||
/// 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
|
||||
fn iter(&self) -> Box<dyn Iterator<Item = OutputData>>;
|
||||
|
||||
/// 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
|
||||
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(
|
||||
&self,
|
||||
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
|
||||
/// set in PMMR index order.
|
||||
/// Returns
|
||||
/// (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(
|
||||
&self,
|
||||
start_height: u64,
|
||||
|
@ -226,7 +223,7 @@ pub trait NodeClient: Sync + Send + Clone {
|
|||
(
|
||||
u64,
|
||||
u64,
|
||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||
),
|
||||
Error,
|
||||
>;
|
||||
|
@ -244,6 +241,9 @@ pub struct OutputData {
|
|||
pub key_id: Identifier,
|
||||
/// How many derivations down from the root key
|
||||
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
|
||||
pub value: u64,
|
||||
/// Current status of the output
|
||||
|
@ -368,9 +368,9 @@ pub struct Context {
|
|||
/// (basically a SecretKey)
|
||||
pub sec_nonce: SecretKey,
|
||||
/// store my outputs between invocations
|
||||
pub output_ids: Vec<Identifier>,
|
||||
pub output_ids: Vec<(Identifier, Option<u64>)>,
|
||||
/// store my inputs
|
||||
pub input_ids: Vec<Identifier>,
|
||||
pub input_ids: Vec<(Identifier, Option<u64>)>,
|
||||
/// store the calculated fee
|
||||
pub fee: u64,
|
||||
}
|
||||
|
@ -391,23 +391,23 @@ impl Context {
|
|||
impl Context {
|
||||
/// Tracks an output contributing to my excess value (if it needs to
|
||||
/// be kept between invocations
|
||||
pub fn add_output(&mut self, output_id: &Identifier) {
|
||||
self.output_ids.push(output_id.clone());
|
||||
pub fn add_output(&mut self, output_id: &Identifier, mmr_index: &Option<u64>) {
|
||||
self.output_ids.push((output_id.clone(), mmr_index.clone()));
|
||||
}
|
||||
|
||||
/// Returns all stored outputs
|
||||
pub fn get_outputs(&self) -> Vec<Identifier> {
|
||||
pub fn get_outputs(&self) -> Vec<(Identifier, Option<u64>)> {
|
||||
self.output_ids.clone()
|
||||
}
|
||||
|
||||
/// Tracks IDs of my inputs into the transaction
|
||||
/// be kept between invocations
|
||||
pub fn add_input(&mut self, input_id: &Identifier) {
|
||||
self.input_ids.push(input_id.clone());
|
||||
pub fn add_input(&mut self, input_id: &Identifier, mmr_index: &Option<u64>) {
|
||||
self.input_ids.push((input_id.clone(), mmr_index.clone()));
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
|
|
|
@ -38,13 +38,11 @@ use crate::libwallet::{internal, Error, ErrorKind};
|
|||
use crate::types::{WalletConfig, WalletSeed};
|
||||
use crate::util;
|
||||
use crate::util::secp::constants::SECRET_KEY_SIZE;
|
||||
use crate::util::secp::pedersen;
|
||||
use crate::util::ZeroingString;
|
||||
|
||||
pub const DB_DIR: &'static str = "db";
|
||||
pub const TX_SAVE_DIR: &'static str = "saved_txs";
|
||||
|
||||
const COMMITMENT_PREFIX: u8 = 'C' as u8;
|
||||
const OUTPUT_PREFIX: u8 = 'o' as u8;
|
||||
const DERIV_PREFIX: u8 = 'd' as u8;
|
||||
const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8;
|
||||
|
@ -222,38 +220,14 @@ where
|
|||
self.parent_key_id.clone()
|
||||
}
|
||||
|
||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
|
||||
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
||||
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
|
||||
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())
|
||||
}
|
||||
|
||||
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> {
|
||||
Box::new(self.db.iter(&[OUTPUT_PREFIX]).unwrap())
|
||||
}
|
||||
|
@ -402,22 +376,21 @@ where
|
|||
fn save(&mut self, out: OutputData) -> Result<(), Error> {
|
||||
// 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)?;
|
||||
}
|
||||
|
||||
// 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(())
|
||||
}
|
||||
|
||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
|
||||
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
||||
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
|
||||
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.borrow().as_ref().unwrap().get_ser(&key),
|
||||
&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.
|
||||
{
|
||||
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
||||
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
||||
}
|
||||
|
||||
// Delete the associated output commitment.
|
||||
{
|
||||
let key = to_key(COMMITMENT_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()),
|
||||
};
|
||||
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ impl NodeClient for HTTPNodeClient {
|
|||
fn get_outputs_from_node(
|
||||
&self,
|
||||
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();
|
||||
// build the necessary query params -
|
||||
// ?id=xxx&id=yyy&id=zzz
|
||||
|
@ -99,7 +99,7 @@ impl NodeClient for HTTPNodeClient {
|
|||
.collect();
|
||||
|
||||
// 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();
|
||||
|
||||
for query_chunk in query_params.chunks(500) {
|
||||
|
@ -125,7 +125,7 @@ impl NodeClient for HTTPNodeClient {
|
|||
for out in res {
|
||||
api_outputs.insert(
|
||||
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,
|
||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||
),
|
||||
libwallet::Error,
|
||||
> {
|
||||
|
@ -149,7 +149,7 @@ impl NodeClient for HTTPNodeClient {
|
|||
|
||||
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();
|
||||
|
||||
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(),
|
||||
is_coinbase,
|
||||
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::util::secp::pedersen;
|
||||
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::{controller, libwallet, WalletSeed};
|
||||
use crate::{WalletBackend, WalletConfig};
|
||||
|
@ -52,7 +53,8 @@ fn get_output_local(chain: &chain::Chain, commit: &pedersen::Commitment) -> Opti
|
|||
for x in outputs.iter() {
|
||||
if let Ok(_) = chain.is_unspent(&x) {
|
||||
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
|
||||
|
@ -153,14 +155,22 @@ where
|
|||
}
|
||||
|
||||
/// 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
|
||||
C: NodeClient + '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();
|
||||
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)
|
||||
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config));
|
||||
wallet.open_with_credentials().unwrap_or_else(|e| {
|
||||
|
@ -171,3 +181,45 @@ where
|
|||
});
|
||||
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(
|
||||
&self,
|
||||
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
|
||||
.iter()
|
||||
.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 m = r.recv().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 {
|
||||
api_outputs.insert(
|
||||
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)
|
||||
|
@ -481,7 +481,7 @@ impl NodeClient for LocalWalletClient {
|
|||
(
|
||||
u64,
|
||||
u64,
|
||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
||||
Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||
),
|
||||
libwallet::Error,
|
||||
> {
|
||||
|
@ -504,7 +504,7 @@ impl NodeClient for LocalWalletClient {
|
|||
let m = r.recv().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();
|
||||
|
||||
for out in o.outputs {
|
||||
|
@ -517,6 +517,7 @@ impl NodeClient for LocalWalletClient {
|
|||
out.range_proof().unwrap(),
|
||||
is_coinbase,
|
||||
out.block_height.unwrap(),
|
||||
out.mmr_index,
|
||||
));
|
||||
}
|
||||
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
|
||||
// proxy
|
||||
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());
|
||||
|
||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||
// 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());
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use self::core::consensus;
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::ExtKeychain;
|
||||
|
@ -38,7 +39,7 @@ fn setup(test_dir: &str) {
|
|||
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> {
|
||||
setup(test_dir);
|
||||
// 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
|
||||
// proxy
|
||||
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());
|
||||
|
||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||
// 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());
|
||||
|
||||
// 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
|
||||
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
|
||||
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()?;
|
||||
{
|
||||
let mut batch = w.batch()?;
|
||||
batch.delete(&w1_outputs[4].key_id)?;
|
||||
batch.delete(&w1_outputs[10].key_id)?;
|
||||
batch.delete(&w1_outputs[4].key_id, &None)?;
|
||||
batch.delete(&w1_outputs[10].key_id, &None)?;
|
||||
let mut accidental_spent = w1_outputs[13].clone();
|
||||
accidental_spent.status = libwallet::types::OutputStatus::Spent;
|
||||
batch.save(accidental_spent)?;
|
||||
|
@ -193,6 +196,385 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
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]
|
||||
fn check_repair() {
|
||||
let test_dir = "test_output/check_repair";
|
||||
|
@ -200,3 +582,11 @@ fn check_repair() {
|
|||
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 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());
|
||||
|
||||
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());
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
|
|
|
@ -89,7 +89,7 @@ fn aggsig_sender_receiver_interaction() {
|
|||
|
||||
rx_cx = Context::new(&keychain.secp(), blind);
|
||||
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(
|
||||
keychain.secp(),
|
||||
|
@ -305,7 +305,7 @@ fn aggsig_sender_receiver_interaction_offset() {
|
|||
|
||||
rx_cx = Context::new(&keychain.secp(), blind);
|
||||
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(
|
||||
keychain.secp(),
|
||||
|
|
|
@ -47,11 +47,13 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
let chain = wallet_proxy.chain.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());
|
||||
|
||||
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());
|
||||
|
||||
// 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 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());
|
||||
|
||||
|
@ -82,7 +82,7 @@ fn compare_wallet_restore(
|
|||
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
|
||||
|
||||
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_dir,
|
||||
client.get_send_instance(),
|
||||
|
@ -90,7 +90,7 @@ fn compare_wallet_restore(
|
|||
);
|
||||
|
||||
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(
|
||||
&restore_name,
|
||||
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
|
||||
// proxy
|
||||
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());
|
||||
|
||||
// define recipient wallet, add to proxy
|
||||
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 2 will use another account
|
||||
|
@ -201,7 +203,8 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
|||
|
||||
// Another wallet
|
||||
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());
|
||||
|
||||
// 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
|
||||
// proxy
|
||||
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());
|
||||
|
||||
// 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
|
||||
// proxy
|
||||
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());
|
||||
|
||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||
// 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());
|
||||
|
||||
// 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
|
||||
// proxy
|
||||
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());
|
||||
|
||||
// define recipient wallet, add to proxy
|
||||
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());
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
|
|
Loading…
Reference in a new issue