API: don't error on missing output (#3256)
* Node API: don't error on missing output * Propagate errors from get_output* * Rename is_unspent and small refactor * Forgot to rename function in tests * Change Batch get_output_pos_height type signature
This commit is contained in:
@@ -57,7 +57,10 @@ impl HeaderHandler {
|
||||
}
|
||||
|
||||
fn get_header_for_output(&self, commit_id: String) -> Result<BlockHeaderPrintable, Error> {
|
||||
let oid = get_output(&self.chain, &commit_id)?.1;
|
||||
let oid = match get_output(&self.chain, &commit_id)? {
|
||||
Some((_, o)) => o,
|
||||
None => return Err(ErrorKind::NotFound.into()),
|
||||
};
|
||||
match w(&self.chain)?.get_header_for_output(&oid) {
|
||||
Ok(header) => Ok(BlockHeaderPrintable::from_header(&header)),
|
||||
Err(_) => Err(ErrorKind::NotFound.into()),
|
||||
@@ -87,7 +90,10 @@ impl HeaderHandler {
|
||||
return Ok(hash);
|
||||
}
|
||||
if let Some(commit) = commit {
|
||||
let oid = get_output_v2(&self.chain, &commit, false, false)?.1;
|
||||
let oid = match get_output_v2(&self.chain, &commit, false, false)? {
|
||||
Some((_, o)) => o,
|
||||
None => return Err(ErrorKind::NotFound.into()),
|
||||
};
|
||||
match w(&self.chain)?.get_header_for_output(&oid) {
|
||||
Ok(header) => return Ok(header.hash()),
|
||||
Err(_) => return Err(ErrorKind::NotFound.into()),
|
||||
@@ -169,7 +175,10 @@ impl BlockHandler {
|
||||
return Ok(hash);
|
||||
}
|
||||
if let Some(commit) = commit {
|
||||
let oid = get_output_v2(&self.chain, &commit, false, false)?.1;
|
||||
let oid = match get_output_v2(&self.chain, &commit, false, false)? {
|
||||
Some((_, o)) => o,
|
||||
None => return Err(ErrorKind::NotFound.into()),
|
||||
};
|
||||
match w(&self.chain)?.get_header_for_output(&oid) {
|
||||
Ok(header) => return Ok(header.hash()),
|
||||
Err(_) => return Err(ErrorKind::NotFound.into()),
|
||||
|
||||
@@ -108,21 +108,6 @@ pub struct OutputHandler {
|
||||
}
|
||||
|
||||
impl OutputHandler {
|
||||
fn get_output(&self, id: &str) -> Result<Output, Error> {
|
||||
let res = get_output(&self.chain, id)?;
|
||||
Ok(res.0)
|
||||
}
|
||||
|
||||
fn get_output_v2(
|
||||
&self,
|
||||
id: &str,
|
||||
include_proof: bool,
|
||||
include_merkle_proof: bool,
|
||||
) -> Result<OutputPrintable, Error> {
|
||||
let res = get_output_v2(&self.chain, id, include_proof, include_merkle_proof)?;
|
||||
Ok(res.0)
|
||||
}
|
||||
|
||||
pub fn get_outputs_v2(
|
||||
&self,
|
||||
commits: Option<Vec<String>>,
|
||||
@@ -144,17 +129,23 @@ impl OutputHandler {
|
||||
}
|
||||
}
|
||||
for commit in commits {
|
||||
match self.get_output_v2(
|
||||
match get_output_v2(
|
||||
&self.chain,
|
||||
&commit,
|
||||
include_proof.unwrap_or(false),
|
||||
include_merkle_proof.unwrap_or(false),
|
||||
) {
|
||||
Ok(output) => outputs.push(output),
|
||||
// do not crash here simply do not retrieve this output
|
||||
Err(e) => error!(
|
||||
"Failure to get output for commitment {} with error {}",
|
||||
commit, e
|
||||
),
|
||||
Ok(Some((output, _))) => outputs.push(output),
|
||||
Ok(None) => {
|
||||
// Ignore outputs that are not found
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failure to get output for commitment {} with error {}",
|
||||
commit, e
|
||||
);
|
||||
return Err(e.into());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -219,12 +210,18 @@ impl OutputHandler {
|
||||
|
||||
let mut outputs: Vec<Output> = vec![];
|
||||
for x in commitments {
|
||||
match self.get_output(&x) {
|
||||
Ok(output) => outputs.push(output),
|
||||
Err(e) => error!(
|
||||
"Failure to get output for commitment {} with error {}",
|
||||
x, e
|
||||
),
|
||||
match get_output(&self.chain, &x) {
|
||||
Ok(Some((output, _))) => outputs.push(output),
|
||||
Ok(None) => {
|
||||
// Ignore outputs that are not found
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failure to get output for commitment {} with error {}",
|
||||
x, e
|
||||
);
|
||||
return Err(e.into());
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(outputs)
|
||||
|
||||
+46
-80
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::chain;
|
||||
use crate::chain::types::CommitPos;
|
||||
use crate::core::core::{OutputFeatures, OutputIdentifier};
|
||||
use crate::rest::*;
|
||||
use crate::types::*;
|
||||
@@ -29,11 +30,11 @@ pub fn w<T>(weak: &Weak<T>) -> Result<Arc<T>, Error> {
|
||||
.ok_or_else(|| ErrorKind::Internal("failed to upgrade weak refernce".to_owned()).into())
|
||||
}
|
||||
|
||||
/// Retrieves an output from the chain given a commit id (a tiny bit iteratively)
|
||||
pub fn get_output(
|
||||
chain: &Weak<chain::Chain>,
|
||||
/// Internal function to retrieves an output by a given commitment
|
||||
fn get_unspent(
|
||||
chain: &Arc<chain::Chain>,
|
||||
id: &str,
|
||||
) -> Result<(Output, OutputIdentifier), Error> {
|
||||
) -> Result<Option<(CommitPos, OutputIdentifier)>, Error> {
|
||||
let c = util::from_hex(String::from(id)).context(ErrorKind::Argument(format!(
|
||||
"Not a valid commitment: {}",
|
||||
id
|
||||
@@ -49,28 +50,29 @@ pub fn get_output(
|
||||
OutputIdentifier::new(OutputFeatures::Coinbase, &commit),
|
||||
];
|
||||
|
||||
let chain = w(chain)?;
|
||||
|
||||
for x in outputs.iter() {
|
||||
let res = chain.is_unspent(x);
|
||||
match res {
|
||||
Ok(output_pos) => {
|
||||
return Ok((
|
||||
Output::new(&commit, output_pos.height, output_pos.pos),
|
||||
x.clone(),
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
trace!(
|
||||
"get_output: err: {} for commit: {:?} with feature: {:?}",
|
||||
e.to_string(),
|
||||
x.commit,
|
||||
x.features
|
||||
);
|
||||
}
|
||||
if let Some(output_pos) = chain.get_unspent(x)? {
|
||||
return Ok(Some((output_pos, x.clone())));
|
||||
}
|
||||
}
|
||||
Err(ErrorKind::NotFound.into())
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Retrieves an output from the chain given a commit id (a tiny bit iteratively)
|
||||
pub fn get_output(
|
||||
chain: &Weak<chain::Chain>,
|
||||
id: &str,
|
||||
) -> Result<Option<(Output, OutputIdentifier)>, Error> {
|
||||
let chain = w(chain)?;
|
||||
let (output_pos, identifier) = match get_unspent(&chain, id)? {
|
||||
Some(x) => x,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
Ok(Some((
|
||||
Output::new(&identifier.commit, output_pos.height, output_pos.pos),
|
||||
identifier,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Retrieves an output from the chain given a commit id (a tiny bit iteratively)
|
||||
@@ -79,63 +81,27 @@ pub fn get_output_v2(
|
||||
id: &str,
|
||||
include_proof: bool,
|
||||
include_merkle_proof: bool,
|
||||
) -> Result<(OutputPrintable, OutputIdentifier), Error> {
|
||||
let c = util::from_hex(String::from(id)).context(ErrorKind::Argument(format!(
|
||||
"Not a valid commitment: {}",
|
||||
id
|
||||
)))?;
|
||||
let commit = Commitment::from_vec(c);
|
||||
|
||||
// We need the features here to be able to generate the necessary hash
|
||||
// to compare against the hash in the output MMR.
|
||||
// For now we can just try both (but this probably needs to be part of the api
|
||||
// params)
|
||||
let outputs = [
|
||||
OutputIdentifier::new(OutputFeatures::Plain, &commit),
|
||||
OutputIdentifier::new(OutputFeatures::Coinbase, &commit),
|
||||
];
|
||||
|
||||
) -> Result<Option<(OutputPrintable, OutputIdentifier)>, Error> {
|
||||
let chain = w(chain)?;
|
||||
let (output_pos, identifier) = match get_unspent(&chain, id)? {
|
||||
Some(x) => x,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
for x in outputs.iter() {
|
||||
let res = chain.is_unspent(x);
|
||||
match res {
|
||||
Ok(output_pos) => match chain.get_unspent_output_at(output_pos.pos) {
|
||||
Ok(output) => {
|
||||
let header = if include_merkle_proof && output.is_coinbase() {
|
||||
chain.get_header_by_height(output_pos.height).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
match OutputPrintable::from_output(
|
||||
&output,
|
||||
chain.clone(),
|
||||
header.as_ref(),
|
||||
include_proof,
|
||||
include_merkle_proof,
|
||||
) {
|
||||
Ok(output_printable) => return Ok((output_printable, x.clone())),
|
||||
Err(e) => {
|
||||
trace!(
|
||||
"get_output: err: {} for commit: {:?} with feature: {:?}",
|
||||
e.to_string(),
|
||||
x.commit,
|
||||
x.features
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(ErrorKind::NotFound.into()),
|
||||
},
|
||||
Err(e) => {
|
||||
trace!(
|
||||
"get_output: err: {} for commit: {:?} with feature: {:?}",
|
||||
e.to_string(),
|
||||
x.commit,
|
||||
x.features
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ErrorKind::NotFound.into())
|
||||
let output = chain.get_unspent_output_at(output_pos.pos)?;
|
||||
let header = if include_merkle_proof && output.is_coinbase() {
|
||||
chain.get_header_by_height(output_pos.height).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let output_printable = OutputPrintable::from_output(
|
||||
&output,
|
||||
chain.clone(),
|
||||
header.as_ref(),
|
||||
include_proof,
|
||||
include_merkle_proof,
|
||||
)?;
|
||||
|
||||
Ok(Some((output_printable, identifier)))
|
||||
}
|
||||
|
||||
@@ -104,6 +104,14 @@ impl From<RouterError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::chain::Error> for Error {
|
||||
fn from(error: crate::chain::Error) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::Internal(error.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TLS config
|
||||
#[derive(Clone)]
|
||||
pub struct TLSConfig {
|
||||
|
||||
+2
-2
@@ -289,8 +289,8 @@ impl OutputPrintable {
|
||||
};
|
||||
|
||||
let out_id = core::OutputIdentifier::from(output);
|
||||
let res = chain.is_unspent(&out_id);
|
||||
let (spent, block_height) = if let Ok(output_pos) = res {
|
||||
let res = chain.get_unspent(&out_id)?;
|
||||
let (spent, block_height) = if let Some(output_pos) = res {
|
||||
(false, Some(output_pos.height))
|
||||
} else {
|
||||
(true, None)
|
||||
|
||||
Reference in New Issue
Block a user