Fix #321 Limit length of API utxo queries (#579)

* Limit url length #321

* Formatting with rustfmt

* Typo
This commit is contained in:
Quentin Le Sceller 2018-01-05 16:03:53 -05:00 committed by AntiochP
parent 26c2669fe8
commit 7212a0e743

View file

@ -50,7 +50,7 @@ fn mark_spent_output(out: &mut OutputData) {
} }
} }
/// Builds a single api query to retrieve the latest output data from the node. /// Builds multiple api queries to retrieve the latest output data from the node.
/// So we can refresh the local wallet outputs. /// So we can refresh the local wallet outputs.
pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(), Error> { pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(), Error> {
debug!(LOGGER, "Refreshing wallet outputs"); debug!(LOGGER, "Refreshing wallet outputs");
@ -66,14 +66,16 @@ pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(),
.filter(|out| out.root_key_id == keychain.root_key_id()) .filter(|out| out.root_key_id == keychain.root_key_id())
.filter(|out| out.status != OutputStatus::Spent) .filter(|out| out.status != OutputStatus::Spent)
{ {
let commit = keychain.commit_with_key_index(out.value, out.n_child).unwrap(); let commit = keychain
.commit_with_key_index(out.value, out.n_child)
.unwrap();
commits.push(commit); commits.push(commit);
wallet_outputs.insert(commit, out.key_id.clone()); wallet_outputs.insert(commit, out.key_id.clone());
} }
}); });
// build the necessary query params - // build the necessary query params -
// ?id=xxx&id=yyy&id=zzz // ?id=xxx&id=yyy&id=zzz
let query_params: Vec<String> = commits let query_params: Vec<String> = commits
.iter() .iter()
.map(|commit| { .map(|commit| {
@ -81,38 +83,56 @@ pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(),
format!("id={}", id) format!("id={}", id)
}) })
.collect(); .collect();
let query_string = query_params.join("&");
let url = format!(
"{}/v1/chain/utxos/byids?{}",
config.check_node_api_http_addr,
query_string,
);
// 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, api::Output> = HashMap::new(); let mut api_outputs: HashMap<pedersen::Commitment, api::Output> = HashMap::new();
match api::client::get::<Vec<api::Output>>(url.as_str()) {
Ok(outputs) => for out in outputs { // size of the batch size for the utxo query
api_outputs.insert(out.commit, out); let batch_query_size = 500;
},
Err(e) => { let mut index_id = 0;
// if we got anything other than 200 back from server, don't attempt to refresh the wallet while index_id < query_params.len() {
// data after let batch_query: Vec<String>;
return Err(Error::Node(e)); if index_id + batch_query_size > query_params.len() {
batch_query = query_params[index_id..query_params.len()].to_vec();
index_id = query_params.len();
} else {
batch_query = query_params[index_id..index_id + batch_query_size].to_vec();
index_id = index_id + batch_query_size;
} }
};
let query_string = batch_query.join("&");
let url = format!(
"{}/v1/chain/utxos/byids?{}",
config.check_node_api_http_addr, query_string,
);
match api::client::get::<Vec<api::Output>>(url.as_str()) {
Ok(outputs) => for out in outputs {
api_outputs.insert(out.commit, out);
},
Err(e) => {
// if we got anything other than 200 back from server, don't attempt to refresh
// the wallet data after
return Err(Error::Node(e));
}
};
}
// now for each commit, find the output in the wallet and // now for each commit, find the output in the wallet and
// the corresponding api output (if it exists) // the corresponding api output (if it exists)
// and refresh it in-place in the wallet. // and refresh it in-place in the wallet.
// Note: minimizing the time we spend holding the wallet lock. // Note: minimizing the time we spend holding the wallet lock.
WalletData::with_wallet(&config.data_file_dir, |wallet_data| for commit in commits { WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
let id = wallet_outputs.get(&commit).unwrap(); for commit in commits {
if let Entry::Occupied(mut output) = wallet_data.outputs.entry(id.to_hex()) { let id = wallet_outputs.get(&commit).unwrap();
match api_outputs.get(&commit) { if let Entry::Occupied(mut output) = wallet_data.outputs.entry(id.to_hex()) {
Some(api_output) => refresh_output(&mut output.get_mut(), api_output), match api_outputs.get(&commit) {
None => mark_spent_output(&mut output.get_mut()), Some(api_output) => refresh_output(&mut output.get_mut(), api_output),
}; None => mark_spent_output(&mut output.get_mut()),
};
}
} }
}) })
} }