Properly include the non-JSON HTTP result in Monero's RpcError

The CI for 695d1f0ecf actually errored with a
non-JSON response, hence the value in this.
This commit is contained in:
Luke Parker
2023-11-29 00:36:58 -05:00
parent f0ff3a18d2
commit 51bb434239
3 changed files with 30 additions and 22 deletions

View File

@@ -42,12 +42,10 @@ impl HttpRpc {
) -> Result<Option<(WwwAuthenticateHeader, u64)>, RpcError> { ) -> Result<Option<(WwwAuthenticateHeader, u64)>, RpcError> {
Ok(if let Some(header) = response.headers().get("www-authenticate") { Ok(if let Some(header) = response.headers().get("www-authenticate") {
Some(( Some((
digest_auth::parse( digest_auth::parse(header.to_str().map_err(|_| {
header RpcError::InvalidNode("www-authenticate header wasn't a string".to_string())
.to_str() })?)
.map_err(|_| RpcError::InvalidNode("www-authenticate header wasn't a string"))?, .map_err(|_| RpcError::InvalidNode("invalid digest-auth response".to_string()))?,
)
.map_err(|_| RpcError::InvalidNode("invalid digest-auth response"))?,
0, 0,
)) ))
} else { } else {
@@ -161,7 +159,9 @@ impl HttpRpc {
HeaderValue::from_str( HeaderValue::from_str(
&challenge &challenge
.respond(&context) .respond(&context)
.map_err(|_| RpcError::InvalidNode("couldn't respond to digest-auth challenge"))? .map_err(|_| {
RpcError::InvalidNode("couldn't respond to digest-auth challenge".to_string())
})?
.to_header_string(), .to_header_string(),
) )
.unwrap(), .unwrap(),
@@ -192,7 +192,9 @@ impl HttpRpc {
if let Some(header) = response.headers().get("www-authenticate") { if let Some(header) = response.headers().get("www-authenticate") {
header header
.to_str() .to_str()
.map_err(|_| RpcError::InvalidNode("www-authenticate header wasn't a string"))? .map_err(|_| {
RpcError::InvalidNode("www-authenticate header wasn't a string".to_string())
})?
.contains("stale") .contains("stale")
} else { } else {
false false

View File

@@ -60,7 +60,7 @@ pub enum RpcError {
#[cfg_attr(feature = "std", error("connection error ({0})"))] #[cfg_attr(feature = "std", error("connection error ({0})"))]
ConnectionError(String), ConnectionError(String),
#[cfg_attr(feature = "std", error("invalid node ({0})"))] #[cfg_attr(feature = "std", error("invalid node ({0})"))]
InvalidNode(&'static str), InvalidNode(String),
#[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))] #[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))]
UnsupportedProtocol(usize), UnsupportedProtocol(usize),
#[cfg_attr(feature = "std", error("transactions not found"))] #[cfg_attr(feature = "std", error("transactions not found"))]
@@ -78,11 +78,11 @@ pub enum RpcError {
} }
fn rpc_hex(value: &str) -> Result<Vec<u8>, RpcError> { fn rpc_hex(value: &str) -> Result<Vec<u8>, RpcError> {
hex::decode(value).map_err(|_| RpcError::InvalidNode("expected hex wasn't hex")) hex::decode(value).map_err(|_| RpcError::InvalidNode("expected hex wasn't hex".to_string()))
} }
fn hash_hex(hash: &str) -> Result<[u8; 32], RpcError> { fn hash_hex(hash: &str) -> Result<[u8; 32], RpcError> {
rpc_hex(hash)?.try_into().map_err(|_| RpcError::InvalidNode("hash wasn't 32-bytes")) rpc_hex(hash)?.try_into().map_err(|_| RpcError::InvalidNode("hash wasn't 32-bytes".to_string()))
} }
fn rpc_point(point: &str) -> Result<EdwardsPoint, RpcError> { fn rpc_point(point: &str) -> Result<EdwardsPoint, RpcError> {
@@ -143,9 +143,9 @@ impl<R: RpcConnection> Rpc<R> {
) )
.await?; .await?;
let res_str = std_shims::str::from_utf8(&res) let res_str = std_shims::str::from_utf8(&res)
.map_err(|_| RpcError::InvalidNode("response wasn't utf-8"))?; .map_err(|_| RpcError::InvalidNode("response wasn't utf-8".to_string()))?;
serde_json::from_str(res_str) serde_json::from_str(res_str)
.map_err(|_| RpcError::InvalidNode("response wasn't json: {res_str}")) .map_err(|_| RpcError::InvalidNode(format!("response wasn't json: {res_str}")))
} }
/// Perform a JSON-RPC call with the specified method with the provided parameters /// Perform a JSON-RPC call with the specified method with the provided parameters
@@ -254,7 +254,9 @@ impl<R: RpcConnection> Rpc<R> {
// This does run a few keccak256 hashes, which is pointless if the node is trusted // This does run a few keccak256 hashes, which is pointless if the node is trusted
// In exchange, this provides resilience against invalid/malicious nodes // In exchange, this provides resilience against invalid/malicious nodes
if tx.hash() != hashes[i] { if tx.hash() != hashes[i] {
Err(RpcError::InvalidNode("replied with transaction wasn't the requested transaction"))?; Err(RpcError::InvalidNode(
"replied with transaction wasn't the requested transaction".to_string(),
))?;
} }
Ok(tx) Ok(tx)
@@ -295,9 +297,9 @@ impl<R: RpcConnection> Rpc<R> {
self.json_rpc_call("get_block", Some(json!({ "hash": hex::encode(hash) }))).await?; self.json_rpc_call("get_block", Some(json!({ "hash": hex::encode(hash) }))).await?;
let block = Block::read::<&[u8]>(&mut rpc_hex(&res.blob)?.as_ref()) let block = Block::read::<&[u8]>(&mut rpc_hex(&res.blob)?.as_ref())
.map_err(|_| RpcError::InvalidNode("invalid block"))?; .map_err(|_| RpcError::InvalidNode("invalid block".to_string()))?;
if block.hash() != hash { if block.hash() != hash {
Err(RpcError::InvalidNode("different block than requested (hash)"))?; Err(RpcError::InvalidNode("different block than requested (hash)".to_string()))?;
} }
Ok(block) Ok(block)
} }
@@ -312,7 +314,7 @@ impl<R: RpcConnection> Rpc<R> {
self.json_rpc_call("get_block", Some(json!({ "height": number }))).await?; self.json_rpc_call("get_block", Some(json!({ "height": number }))).await?;
let block = Block::read::<&[u8]>(&mut rpc_hex(&res.blob)?.as_ref()) let block = Block::read::<&[u8]>(&mut rpc_hex(&res.blob)?.as_ref())
.map_err(|_| RpcError::InvalidNode("invalid block"))?; .map_err(|_| RpcError::InvalidNode("invalid block".to_string()))?;
// Make sure this is actually the block for this number // Make sure this is actually the block for this number
match block.miner_tx.prefix.inputs.first() { match block.miner_tx.prefix.inputs.first() {
@@ -320,10 +322,12 @@ impl<R: RpcConnection> Rpc<R> {
if usize::try_from(*actual).unwrap() == number { if usize::try_from(*actual).unwrap() == number {
Ok(block) Ok(block)
} else { } else {
Err(RpcError::InvalidNode("different block than requested (number)")) Err(RpcError::InvalidNode("different block than requested (number)".to_string()))
} }
} }
_ => Err(RpcError::InvalidNode("block's miner_tx didn't have an input of kind Input::Gen")), _ => Err(RpcError::InvalidNode(
"block's miner_tx didn't have an input of kind Input::Gen".to_string(),
)),
} }
} }
@@ -493,7 +497,7 @@ impl<R: RpcConnection> Rpc<R> {
read_object(&mut indexes) read_object(&mut indexes)
})() })()
.map_err(|_| RpcError::InvalidNode("invalid binary response")) .map_err(|_| RpcError::InvalidNode("invalid binary response".to_string()))
} }
/// Get the output distribution, from the specified height to the specified height (both /// Get the output distribution, from the specified height to the specified height (both
@@ -582,7 +586,9 @@ impl<R: RpcConnection> Rpc<R> {
// invalid keys may honestly exist on the blockchain // invalid keys may honestly exist on the blockchain
// Only a recent hard fork checked output keys were valid points // Only a recent hard fork checked output keys were valid points
let Some(key) = CompressedEdwardsY( let Some(key) = CompressedEdwardsY(
rpc_hex(&out.key)?.try_into().map_err(|_| RpcError::InvalidNode("non-32-byte point"))?, rpc_hex(&out.key)?
.try_into()
.map_err(|_| RpcError::InvalidNode("non-32-byte point".to_string()))?,
) )
.decompress() else { .decompress() else {
return Ok(None); return Ok(None);

View File

@@ -234,7 +234,7 @@ impl SpendableOutput {
.await? .await?
.get(usize::from(self.output.absolute.o)) .get(usize::from(self.output.absolute.o))
.ok_or(RpcError::InvalidNode( .ok_or(RpcError::InvalidNode(
"node returned output indexes didn't include an index for this output", "node returned output indexes didn't include an index for this output".to_string(),
))?; ))?;
Ok(()) Ok(())
} }