use thiserror::Error; use casper_types::{bytesrepr, Block, BlockHash, Digest}; use crate::rpcs::{common::BlockIdentifier, results::GetBlockResult}; /// Error that can be returned when validating data returned from a JSON-RPC method. #[derive(Error, Debug)] pub enum ValidateResponseError { /// Failed to marshall value. #[error("failed to marshall value {0}")] BytesRepr(bytesrepr::Error), /// Error from serde. #[error(transparent)] Serde(#[from] serde_json::Error), /// Failed to parse JSON. #[error("validate response failed to parse")] ValidateResponseFailedToParse, /// The body hash in the header is not the same as the hash of the body of the block. #[error( "block header has incorrect body hash. \ actual block body hash: {actual_block_body_hash}, \ block: {block}" )] BodyHashMismatch { /// The `Block` with the `BlockHeader` with the incorrect block body hash. block: Box, /// The actual hash of the block's `BlockBody`. actual_block_body_hash: Digest, }, /// The block's hash is not the same as the header's hash. #[error( "block has incorrect block hash. \ actual block body hash: {actual_block_header_hash}, \ block: {block}" )] BlockHashMismatch { /// The `Block` with the incorrect `BlockHeaderHash` block: Box, /// The actual hash of the block's `BlockHeader` actual_block_header_hash: BlockHash, }, /// Serialized value not contained in proof. #[error("serialized value not contained in proof")] SerializedValueNotContainedInProof, /// No block in response. #[error("no block in response")] NoBlockInResponse, /// Block hash requested does not correspond to response. #[error("block hash requested does not correspond to response")] UnexpectedBlockHash, /// Block height was not as requested. #[error("block height was not as requested")] UnexpectedBlockHeight, /// An invalid combination of state identifier and block header response #[error("invalid combination of state identifier and block header in response")] InvalidGlobalStateResponse, } impl From for ValidateResponseError { fn from(e: bytesrepr::Error) -> Self { ValidateResponseError::BytesRepr(e) } } pub(crate) fn validate_get_block_result( _maybe_block_identifier: Option, _result: &GetBlockResult, ) -> Result<(), ValidateResponseError> { // TODO: There will likely be a further change made in `casper-types` which will allow the // `JsonBlock` type to be removed in favour of the actual `Block` type. `Block` provides // a public `verify()` method which will be suitable for usage here, meaning the // commented code below can be removed. In case this doesn't happen, the code below // should be reinstated. // // let block = if let Some(block) = result.block.as_ref() { // block // } else { // return Ok(()); // }; // // match types::validate_block_hashes_v1(block) { // Ok(()) => {} // Err(v1_error) => match types::validate_block_hashes_v2(block) { // Ok(()) => {} // Err(_v2_error) => return Err(v1_error), // }, // } // // match maybe_block_identifier { // Some(BlockIdentifier::Hash(block_hash)) => { // if block_hash.inner() != block.hash().inner() { // return Err(ValidateResponseError::UnexpectedBlockHash); // } // } // Some(BlockIdentifier::Height(height)) => { // // More is necessary here to mitigate a MITM attack // if height != block.header().height() { // return Err(ValidateResponseError::UnexpectedBlockHeight); // } // } // // More is necessary here to mitigate a MITM attack. In this case we would want to validate // // `block.proofs()` to make sure that 1/3 of the validator weight signed the block, and we // // would have to know the latest validators through some trustworthy means // None => (), // } Ok(()) }