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.
pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(), Error> {
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.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);
wallet_outputs.insert(commit, out.key_id.clone());
}
});
// build the necessary query params -
// ?id=xxx&id=yyy&id=zzz
// ?id=xxx&id=yyy&id=zzz
let query_params: Vec<String> = commits
.iter()
.map(|commit| {
@ -81,38 +83,56 @@ pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(),
format!("id={}", id)
})
.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
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 {
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));
// size of the batch size for the utxo query
let batch_query_size = 500;
let mut index_id = 0;
while index_id < query_params.len() {
let batch_query: Vec<String>;
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
// the corresponding api output (if it exists)
// and refresh it in-place in the wallet.
// Note: minimizing the time we spend holding the wallet lock.
WalletData::with_wallet(&config.data_file_dir, |wallet_data| for commit in commits {
let id = wallet_outputs.get(&commit).unwrap();
if let Entry::Occupied(mut output) = wallet_data.outputs.entry(id.to_hex()) {
match api_outputs.get(&commit) {
Some(api_output) => refresh_output(&mut output.get_mut(), api_output),
None => mark_spent_output(&mut output.get_mut()),
};
// the corresponding api output (if it exists)
// and refresh it in-place in the wallet.
// Note: minimizing the time we spend holding the wallet lock.
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
for commit in commits {
let id = wallet_outputs.get(&commit).unwrap();
if let Entry::Occupied(mut output) = wallet_data.outputs.entry(id.to_hex()) {
match api_outputs.get(&commit) {
Some(api_output) => refresh_output(&mut output.get_mut(), api_output),
None => mark_spent_output(&mut output.get_mut()),
};
}
}
})
}