diff --git a/binary_port/src/binary_response.rs b/binary_port/src/binary_response.rs index 407aa5b4d6..38cd9339e9 100644 --- a/binary_port/src/binary_response.rs +++ b/binary_port/src/binary_response.rs @@ -6,7 +6,7 @@ use casper_types::{ use crate::{ binary_response_header::BinaryResponseHeader, error_code::ErrorCode, - payload_type::{PayloadEntity, PayloadType}, + response_type::{PayloadEntity, ResponseType}, }; #[cfg(test)] @@ -40,7 +40,7 @@ impl BinaryResponse { /// Creates new binary response from raw bytes. pub fn from_raw_bytes( - payload_type: PayloadType, + payload_type: ResponseType, payload: Vec, protocol_version: ProtocolVersion, ) -> Self { @@ -59,7 +59,7 @@ impl BinaryResponse { BinaryResponse::new_error(ErrorCode::InternalError, protocol_version), |payload| BinaryResponse { payload, - header: BinaryResponseHeader::new(Some(V::PAYLOAD_TYPE), protocol_version), + header: BinaryResponseHeader::new(Some(V::RESPONSE_TYPE), protocol_version), }, ) } diff --git a/binary_port/src/binary_response_and_request.rs b/binary_port/src/binary_response_and_request.rs index 88a55315a3..c92bd2e7f7 100644 --- a/binary_port/src/binary_response_and_request.rs +++ b/binary_port/src/binary_response_and_request.rs @@ -5,7 +5,7 @@ use casper_types::{ use crate::{ binary_response::BinaryResponse, original_request_context::OriginalRequestContext, - payload_type::PayloadEntity, PayloadType, + response_type::PayloadEntity, ResponseType, }; use crate::record_id::RecordId; @@ -37,14 +37,14 @@ impl BinaryResponseAndRequest { } } - /// Returns a new binary response with specified data and no original request. + /// Returns a new binary response with specified data and no original request. pub fn new_test_response( record_id: RecordId, data: &A, protocol_version: ProtocolVersion, ) -> BinaryResponseAndRequest { let response = BinaryResponse::from_raw_bytes( - PayloadType::from_record_id(record_id, false), + ResponseType::from_record_id(record_id, false), data.to_bytes().unwrap(), protocol_version, ); @@ -58,7 +58,7 @@ impl BinaryResponseAndRequest { protocol_version: ProtocolVersion, ) -> BinaryResponseAndRequest { let response = BinaryResponse::from_raw_bytes( - PayloadType::from_record_id(record_id, true), + ResponseType::from_record_id(record_id, true), bincode::serialize(data).unwrap(), protocol_version, ); diff --git a/binary_port/src/binary_response_header.rs b/binary_port/src/binary_response_header.rs index 131ac77bb3..e816c7990f 100644 --- a/binary_port/src/binary_response_header.rs +++ b/binary_port/src/binary_response_header.rs @@ -1,4 +1,4 @@ -use crate::{error_code::ErrorCode, payload_type::PayloadType}; +use crate::{error_code::ErrorCode, response_type::ResponseType}; use casper_types::{ bytesrepr::{self, FromBytes, ToBytes}, ProtocolVersion, @@ -19,7 +19,10 @@ pub struct BinaryResponseHeader { impl BinaryResponseHeader { /// Creates new binary response header representing success. - pub fn new(returned_data_type: Option, protocol_version: ProtocolVersion) -> Self { + pub fn new( + returned_data_type: Option, + protocol_version: ProtocolVersion, + ) -> Self { Self { protocol_version, error: ErrorCode::NoError as u16, diff --git a/binary_port/src/information_request.rs b/binary_port/src/information_request.rs index d389525d01..351066666a 100644 --- a/binary_port/src/information_request.rs +++ b/binary_port/src/information_request.rs @@ -7,8 +7,10 @@ use crate::{get_request::GetRequest, EraIdentifier}; #[cfg(test)] use casper_types::testing::TestRng; use casper_types::{ - bytesrepr::{self, FromBytes, ToBytes}, - BlockIdentifier, PublicKey, TransactionHash, + account::AccountHash, + bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH}, + contracts::{ContractHash, ContractPackageHash}, + BlockIdentifier, EntityAddr, GlobalStateIdentifier, PackageAddr, PublicKey, TransactionHash, }; /// Request for information from the node. @@ -64,6 +66,22 @@ pub enum InformationRequest { }, /// Returns the current Casper protocol version. ProtocolVersion, + /// Returns the contract package by an identifier. + Package { + /// Global state identifier, `None` means "latest block state". + state_identifier: Option, + /// Identifier of the contract package to retrieve. + identifier: PackageIdentifier, + }, + /// Returns the entity by an identifier. + Entity { + /// Global state identifier, `None` means "latest block state". + state_identifier: Option, + /// Identifier of the entity to retrieve. + identifier: EntityIdentifier, + /// Whether to return the bytecode with the entity. + include_bytecode: bool, + }, } impl InformationRequest { @@ -94,6 +112,8 @@ impl InformationRequest { } InformationRequest::Reward { .. } => InformationRequestTag::Reward, InformationRequest::ProtocolVersion => InformationRequestTag::ProtocolVersion, + InformationRequest::Package { .. } => InformationRequestTag::Package, + InformationRequest::Entity { .. } => InformationRequestTag::Entity, } } @@ -135,6 +155,19 @@ impl InformationRequest { delegator: rng.gen::().then(|| PublicKey::random(rng).into()), }, InformationRequestTag::ProtocolVersion => InformationRequest::ProtocolVersion, + InformationRequestTag::Package => InformationRequest::Package { + state_identifier: rng + .gen::() + .then(|| GlobalStateIdentifier::random(rng)), + identifier: PackageIdentifier::random(rng), + }, + InformationRequestTag::Entity => InformationRequest::Entity { + state_identifier: rng + .gen::() + .then(|| GlobalStateIdentifier::random(rng)), + identifier: EntityIdentifier::random(rng), + include_bytecode: rng.gen(), + }, } } } @@ -185,6 +218,22 @@ impl ToBytes for InformationRequest { delegator.as_deref().write_bytes(writer)?; Ok(()) } + InformationRequest::Package { + state_identifier, + identifier, + } => { + state_identifier.write_bytes(writer)?; + identifier.write_bytes(writer) + } + InformationRequest::Entity { + state_identifier, + identifier, + include_bytecode, + } => { + state_identifier.write_bytes(writer)?; + identifier.write_bytes(writer)?; + include_bytecode.write_bytes(writer) + } } } @@ -223,6 +272,19 @@ impl ToBytes for InformationRequest { + validator.serialized_length() + delegator.as_deref().serialized_length() } + InformationRequest::Package { + state_identifier, + identifier, + } => state_identifier.serialized_length() + identifier.serialized_length(), + InformationRequest::Entity { + state_identifier, + identifier, + include_bytecode, + } => { + state_identifier.serialized_length() + + identifier.serialized_length() + + include_bytecode.serialized_length() + } } } } @@ -292,6 +354,30 @@ impl TryFrom<(InformationRequestTag, &[u8])> for InformationRequest { InformationRequestTag::ProtocolVersion => { (InformationRequest::ProtocolVersion, key_bytes) } + InformationRequestTag::Package => { + let (state_identifier, remainder) = FromBytes::from_bytes(key_bytes)?; + let (identifier, remainder) = FromBytes::from_bytes(remainder)?; + ( + InformationRequest::Package { + state_identifier, + identifier, + }, + remainder, + ) + } + InformationRequestTag::Entity => { + let (state_identifier, remainder) = FromBytes::from_bytes(key_bytes)?; + let (identifier, remainder) = FromBytes::from_bytes(remainder)?; + let (include_bytecode, remainder) = FromBytes::from_bytes(remainder)?; + ( + InformationRequest::Entity { + state_identifier, + identifier, + include_bytecode, + }, + remainder, + ) + } }; if !remainder.is_empty() { return Err(bytesrepr::Error::LeftOverBytes); @@ -351,12 +437,16 @@ pub enum InformationRequestTag { Reward = 16, /// Protocol version request. ProtocolVersion = 17, + /// Contract package request. + Package = 18, + /// Addressable entity request. + Entity = 19, } impl InformationRequestTag { #[cfg(test)] pub(crate) fn random(rng: &mut TestRng) -> Self { - match rng.gen_range(0..18) { + match rng.gen_range(0..20) { 0 => InformationRequestTag::BlockHeader, 1 => InformationRequestTag::SignedBlock, 2 => InformationRequestTag::Transaction, @@ -375,6 +465,8 @@ impl InformationRequestTag { 15 => InformationRequestTag::LatestSwitchBlockHeader, 16 => InformationRequestTag::Reward, 17 => InformationRequestTag::ProtocolVersion, + 18 => InformationRequestTag::Package, + 19 => InformationRequestTag::Entity, _ => unreachable!(), } } @@ -403,6 +495,8 @@ impl TryFrom for InformationRequestTag { 15 => Ok(InformationRequestTag::LatestSwitchBlockHeader), 16 => Ok(InformationRequestTag::Reward), 17 => Ok(InformationRequestTag::ProtocolVersion), + 18 => Ok(InformationRequestTag::Package), + 19 => Ok(InformationRequestTag::Entity), _ => Err(UnknownInformationRequestTag(value)), } } @@ -414,10 +508,154 @@ impl From for u16 { } } -/// Error returned when trying to convert a `u16` into a `DbId`. -#[derive(Debug, PartialEq, Eq)] +/// Error returned when trying to convert a `u16` into a `RecordId`. +#[derive(Debug, Clone, PartialEq)] pub struct UnknownInformationRequestTag(u16); +#[derive(Debug, Clone, PartialEq)] +pub enum EntityIdentifier { + ContractHash(ContractHash), + AccountHash(AccountHash), + PublicKey(PublicKey), + EntityAddr(EntityAddr), +} + +impl EntityIdentifier { + #[cfg(test)] + pub(crate) fn random(rng: &mut TestRng) -> Self { + match rng.gen_range(0..4) { + 0 => EntityIdentifier::ContractHash(ContractHash::new(rng.gen())), + 1 => EntityIdentifier::PublicKey(PublicKey::random(rng)), + 2 => EntityIdentifier::AccountHash(AccountHash::new(rng.gen())), + 3 => EntityIdentifier::EntityAddr(rng.gen()), + _ => unreachable!(), + } + } +} + +impl FromBytes for EntityIdentifier { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { + let (tag, remainder) = u8::from_bytes(bytes)?; + let (identifier, remainder) = match tag { + 0 => { + let (hash, remainder) = FromBytes::from_bytes(remainder)?; + (EntityIdentifier::ContractHash(hash), remainder) + } + 1 => { + let (key, remainder) = FromBytes::from_bytes(remainder)?; + (EntityIdentifier::PublicKey(key), remainder) + } + 2 => { + let (hash, remainder) = FromBytes::from_bytes(remainder)?; + (EntityIdentifier::AccountHash(hash), remainder) + } + 3 => { + let (entity, remainder) = FromBytes::from_bytes(remainder)?; + (EntityIdentifier::EntityAddr(entity), remainder) + } + _ => return Err(bytesrepr::Error::Formatting), + }; + Ok((identifier, remainder)) + } +} + +impl ToBytes for EntityIdentifier { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + let tag: u8 = match self { + EntityIdentifier::ContractHash(_) => 0, + EntityIdentifier::PublicKey(_) => 1, + EntityIdentifier::AccountHash(_) => 2, + EntityIdentifier::EntityAddr(_) => 3, + }; + tag.write_bytes(writer)?; + match self { + EntityIdentifier::ContractHash(hash) => hash.write_bytes(writer), + EntityIdentifier::PublicKey(key) => key.write_bytes(writer), + EntityIdentifier::AccountHash(hash) => hash.write_bytes(writer), + EntityIdentifier::EntityAddr(entity) => entity.write_bytes(writer), + } + } + + fn serialized_length(&self) -> usize { + let identifier_length = match self { + EntityIdentifier::ContractHash(hash) => hash.serialized_length(), + EntityIdentifier::PublicKey(key) => key.serialized_length(), + EntityIdentifier::AccountHash(hash) => hash.serialized_length(), + EntityIdentifier::EntityAddr(entity) => entity.serialized_length(), + }; + U8_SERIALIZED_LENGTH + identifier_length + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PackageIdentifier { + ContractPackageHash(ContractPackageHash), + PackageAddr(PackageAddr), +} + +impl PackageIdentifier { + #[cfg(test)] + pub(crate) fn random(rng: &mut TestRng) -> Self { + match rng.gen_range(0..2) { + 0 => PackageIdentifier::ContractPackageHash(ContractPackageHash::new(rng.gen())), + 1 => PackageIdentifier::PackageAddr(rng.gen()), + _ => unreachable!(), + } + } +} + +impl FromBytes for PackageIdentifier { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { + let (tag, remainder) = u8::from_bytes(bytes)?; + let (identifier, remainder) = match tag { + 0 => { + let (hash, remainder) = FromBytes::from_bytes(remainder)?; + (PackageIdentifier::ContractPackageHash(hash), remainder) + } + 1 => { + let (addr, remainder) = FromBytes::from_bytes(remainder)?; + (PackageIdentifier::PackageAddr(addr), remainder) + } + _ => return Err(bytesrepr::Error::Formatting), + }; + Ok((identifier, remainder)) + } +} + +impl ToBytes for PackageIdentifier { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + let tag: u8 = match self { + PackageIdentifier::ContractPackageHash(_) => 0, + PackageIdentifier::PackageAddr(_) => 1, + }; + tag.write_bytes(writer)?; + match self { + PackageIdentifier::ContractPackageHash(hash) => hash.write_bytes(writer), + PackageIdentifier::PackageAddr(addr) => addr.write_bytes(writer), + } + } + + fn serialized_length(&self) -> usize { + let identifier_length = match self { + PackageIdentifier::ContractPackageHash(hash) => hash.serialized_length(), + PackageIdentifier::PackageAddr(addr) => addr.serialized_length(), + }; + U8_SERIALIZED_LENGTH + identifier_length + } +} + #[cfg(test)] mod tests { use super::*; @@ -443,4 +681,22 @@ mod tests { Ok(val) ); } + + #[test] + fn entity_identifier_bytesrepr_roundtrip() { + let rng = &mut TestRng::new(); + + let val = EntityIdentifier::random(rng); + let bytes = val.to_bytes().expect("should serialize"); + assert_eq!(bytesrepr::deserialize_from_slice(bytes), Ok(val)); + } + + #[test] + fn package_identifier_bytesrepr_roundtrip() { + let rng = &mut TestRng::new(); + + let val = PackageIdentifier::random(rng); + let bytes = val.to_bytes().expect("should serialize"); + assert_eq!(bytesrepr::deserialize_from_slice(bytes), Ok(val)); + } } diff --git a/binary_port/src/lib.rs b/binary_port/src/lib.rs index e50d2d764b..ec0a173ccc 100644 --- a/binary_port/src/lib.rs +++ b/binary_port/src/lib.rs @@ -17,9 +17,9 @@ mod key_prefix; mod minimal_block_info; mod node_status; mod original_request_context; -mod payload_type; mod purse_identifier; pub mod record_id; +mod response_type; mod speculative_execution_result; mod state_request; mod type_wrappers; @@ -36,17 +36,19 @@ pub use error::Error; pub use error_code::ErrorCode; pub use get_request::GetRequest; pub use global_state_query_result::GlobalStateQueryResult; -pub use information_request::{InformationRequest, InformationRequestTag}; +pub use information_request::{ + EntityIdentifier, InformationRequest, InformationRequestTag, PackageIdentifier, +}; pub use key_prefix::KeyPrefix; pub use minimal_block_info::MinimalBlockInfo; pub use node_status::NodeStatus; -pub use payload_type::{PayloadEntity, PayloadType}; pub use purse_identifier::PurseIdentifier; pub use record_id::{RecordId, UnknownRecordId}; +pub use response_type::{PayloadEntity, ResponseType}; pub use speculative_execution_result::SpeculativeExecutionResult; pub use state_request::GlobalStateRequest; pub use type_wrappers::{ - ConsensusStatus, ConsensusValidatorChanges, DictionaryQueryResult, GetTrieFullResult, - LastProgress, NetworkName, ReactorStateName, RewardResponse, TransactionWithExecutionInfo, - Uptime, + AccountInformation, AddressableEntityInformation, ConsensusStatus, ConsensusValidatorChanges, + ContractInformation, DictionaryQueryResult, GetTrieFullResult, LastProgress, NetworkName, + ReactorStateName, RewardResponse, TransactionWithExecutionInfo, Uptime, ValueWithProof, }; diff --git a/binary_port/src/payload_type.rs b/binary_port/src/payload_type.rs deleted file mode 100644 index 51000d7300..0000000000 --- a/binary_port/src/payload_type.rs +++ /dev/null @@ -1,414 +0,0 @@ -//! The payload type. - -use core::{convert::TryFrom, fmt}; - -#[cfg(test)] -use rand::Rng; -#[cfg(feature = "json-schema")] -use schemars::JsonSchema; - -#[cfg(test)] -use casper_types::testing::TestRng; -use casper_types::{ - execution::{ExecutionResult, ExecutionResultV1}, - AvailableBlockRange, BlockBody, BlockBodyV1, BlockHeader, BlockHeaderV1, BlockSignatures, - BlockSignaturesV1, BlockSynchronizerStatus, ChainspecRawBytes, Deploy, NextUpgrade, Peers, - ProtocolVersion, SignedBlock, StoredValue, Transaction, Transfer, -}; - -use crate::{ - global_state_query_result::GlobalStateQueryResult, - node_status::NodeStatus, - speculative_execution_result::SpeculativeExecutionResult, - type_wrappers::{ - ConsensusStatus, ConsensusValidatorChanges, GetTrieFullResult, LastProgress, NetworkName, - ReactorStateName, RewardResponse, - }, - BalanceResponse, DictionaryQueryResult, RecordId, TransactionWithExecutionInfo, Uptime, -}; - -/// A type of the payload being returned in a binary response. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[repr(u8)] -#[cfg_attr(feature = "json-schema", derive(JsonSchema))] -pub enum PayloadType { - /// Legacy version of the block header. - BlockHeaderV1, - /// Block header. - BlockHeader, - /// Legacy version of the block body. - BlockBodyV1, - /// Block body. - BlockBody, - /// Legacy version of the approvals hashes. - ApprovalsHashesV1, - /// Approvals hashes - ApprovalsHashes, - /// Legacy version of the block signatures. - BlockSignaturesV1, - /// Block signatures. - BlockSignatures, - /// Deploy. - Deploy, - /// Transaction. - Transaction, - /// Legacy version of the execution result. - ExecutionResultV1, - /// Execution result. - ExecutionResult, - /// Wasm V1 execution result. - WasmV1Result, - /// Transfers. - Transfers, - /// Finalized deploy approvals. - FinalizedDeployApprovals, - /// Finalized approvals. - FinalizedApprovals, - /// Block with signatures. - SignedBlock, - /// Transaction with approvals and execution info. - TransactionWithExecutionInfo, - /// Peers. - Peers, - /// Last progress. - LastProgress, - /// State of the reactor. - ReactorState, - /// Network name. - NetworkName, - /// Consensus validator changes. - ConsensusValidatorChanges, // return type in `effects.rs` will be turned into dedicated type. - /// Status of the block synchronizer. - BlockSynchronizerStatus, - /// Available block range. - AvailableBlockRange, - /// Information about the next network upgrade. - NextUpgrade, - /// Consensus status. - ConsensusStatus, // return type in `effects.rs` will be turned into dedicated type. - /// Chainspec represented as raw bytes. - ChainspecRawBytes, - /// Uptime. - Uptime, - /// Result of checking if given block is in the highest available block range. - HighestBlockSequenceCheckResult, - /// Result of the speculative execution, - SpeculativeExecutionResult, - /// Result of querying global state, - GlobalStateQueryResult, - /// Result of querying global state for all values under a specified key. - StoredValues, - /// Result of querying global state for a full trie. - GetTrieFullResult, - /// Node status. - NodeStatus, - /// Result of querying for a dictionary item. - DictionaryQueryResult, - /// Balance query response. - BalanceResponse, - /// Reward response. - Reward, - /// Protocol version. - ProtocolVersion, -} - -impl PayloadType { - pub fn from_record_id(record_id: RecordId, is_legacy: bool) -> Self { - match (is_legacy, record_id) { - (true, RecordId::BlockHeader) => Self::BlockHeaderV1, - (true, RecordId::BlockBody) => Self::BlockBodyV1, - (true, RecordId::ApprovalsHashes) => Self::ApprovalsHashesV1, - (true, RecordId::BlockMetadata) => Self::BlockSignaturesV1, - (true, RecordId::Transaction) => Self::Deploy, - (true, RecordId::ExecutionResult) => Self::ExecutionResultV1, - (true, RecordId::Transfer) => Self::Transfers, - (true, RecordId::FinalizedTransactionApprovals) => Self::FinalizedDeployApprovals, - (false, RecordId::BlockHeader) => Self::BlockHeader, - (false, RecordId::BlockBody) => Self::BlockBody, - (false, RecordId::ApprovalsHashes) => Self::ApprovalsHashes, - (false, RecordId::BlockMetadata) => Self::BlockSignatures, - (false, RecordId::Transaction) => Self::Transaction, - (false, RecordId::ExecutionResult) => Self::ExecutionResult, - (false, RecordId::Transfer) => Self::Transfers, - (false, RecordId::FinalizedTransactionApprovals) => Self::FinalizedApprovals, - } - } - - #[cfg(test)] - pub(crate) fn random(rng: &mut TestRng) -> Self { - Self::try_from(rng.gen_range(0..38)).unwrap() - } -} - -impl TryFrom for PayloadType { - type Error = (); - - fn try_from(v: u8) -> Result { - match v { - x if x == PayloadType::BlockHeaderV1 as u8 => Ok(PayloadType::BlockHeaderV1), - x if x == PayloadType::BlockHeader as u8 => Ok(PayloadType::BlockHeader), - x if x == PayloadType::BlockBodyV1 as u8 => Ok(PayloadType::BlockBodyV1), - x if x == PayloadType::BlockBody as u8 => Ok(PayloadType::BlockBody), - x if x == PayloadType::ApprovalsHashesV1 as u8 => Ok(PayloadType::ApprovalsHashesV1), - x if x == PayloadType::ApprovalsHashes as u8 => Ok(PayloadType::ApprovalsHashes), - x if x == PayloadType::BlockSignaturesV1 as u8 => Ok(PayloadType::BlockSignaturesV1), - x if x == PayloadType::BlockSignatures as u8 => Ok(PayloadType::BlockSignatures), - x if x == PayloadType::Deploy as u8 => Ok(PayloadType::Deploy), - x if x == PayloadType::Transaction as u8 => Ok(PayloadType::Transaction), - x if x == PayloadType::ExecutionResultV1 as u8 => Ok(PayloadType::ExecutionResultV1), - x if x == PayloadType::ExecutionResult as u8 => Ok(PayloadType::ExecutionResult), - x if x == PayloadType::Transfers as u8 => Ok(PayloadType::Transfers), - x if x == PayloadType::FinalizedDeployApprovals as u8 => { - Ok(PayloadType::FinalizedDeployApprovals) - } - x if x == PayloadType::FinalizedApprovals as u8 => Ok(PayloadType::FinalizedApprovals), - x if x == PayloadType::SignedBlock as u8 => Ok(PayloadType::SignedBlock), - x if x == PayloadType::TransactionWithExecutionInfo as u8 => { - Ok(PayloadType::TransactionWithExecutionInfo) - } - x if x == PayloadType::Peers as u8 => Ok(PayloadType::Peers), - x if x == PayloadType::Uptime as u8 => Ok(PayloadType::Uptime), - x if x == PayloadType::LastProgress as u8 => Ok(PayloadType::LastProgress), - x if x == PayloadType::ReactorState as u8 => Ok(PayloadType::ReactorState), - x if x == PayloadType::NetworkName as u8 => Ok(PayloadType::NetworkName), - x if x == PayloadType::ConsensusValidatorChanges as u8 => { - Ok(PayloadType::ConsensusValidatorChanges) - } - x if x == PayloadType::BlockSynchronizerStatus as u8 => { - Ok(PayloadType::BlockSynchronizerStatus) - } - x if x == PayloadType::AvailableBlockRange as u8 => { - Ok(PayloadType::AvailableBlockRange) - } - x if x == PayloadType::NextUpgrade as u8 => Ok(PayloadType::NextUpgrade), - x if x == PayloadType::ConsensusStatus as u8 => Ok(PayloadType::ConsensusStatus), - x if x == PayloadType::ChainspecRawBytes as u8 => Ok(PayloadType::ChainspecRawBytes), - x if x == PayloadType::HighestBlockSequenceCheckResult as u8 => { - Ok(PayloadType::HighestBlockSequenceCheckResult) - } - x if x == PayloadType::SpeculativeExecutionResult as u8 => { - Ok(PayloadType::SpeculativeExecutionResult) - } - x if x == PayloadType::GlobalStateQueryResult as u8 => { - Ok(PayloadType::GlobalStateQueryResult) - } - x if x == PayloadType::StoredValues as u8 => Ok(PayloadType::StoredValues), - x if x == PayloadType::GetTrieFullResult as u8 => Ok(PayloadType::GetTrieFullResult), - x if x == PayloadType::NodeStatus as u8 => Ok(PayloadType::NodeStatus), - x if x == PayloadType::DictionaryQueryResult as u8 => { - Ok(PayloadType::DictionaryQueryResult) - } - x if x == PayloadType::WasmV1Result as u8 => Ok(PayloadType::WasmV1Result), - x if x == PayloadType::BalanceResponse as u8 => Ok(PayloadType::BalanceResponse), - x if x == PayloadType::Reward as u8 => Ok(PayloadType::Reward), - x if x == PayloadType::ProtocolVersion as u8 => Ok(PayloadType::ProtocolVersion), - _ => Err(()), - } - } -} - -impl From for u8 { - fn from(value: PayloadType) -> Self { - value as u8 - } -} - -impl fmt::Display for PayloadType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - PayloadType::BlockHeaderV1 => write!(f, "BlockHeaderV1"), - PayloadType::BlockHeader => write!(f, "BlockHeader"), - PayloadType::BlockBodyV1 => write!(f, "BlockBodyV1"), - PayloadType::BlockBody => write!(f, "BlockBody"), - PayloadType::ApprovalsHashesV1 => write!(f, "ApprovalsHashesV1"), - PayloadType::ApprovalsHashes => write!(f, "ApprovalsHashes"), - PayloadType::BlockSignaturesV1 => write!(f, "BlockSignaturesV1"), - PayloadType::BlockSignatures => write!(f, "BlockSignatures"), - PayloadType::Deploy => write!(f, "Deploy"), - PayloadType::Transaction => write!(f, "Transaction"), - PayloadType::ExecutionResultV1 => write!(f, "ExecutionResultV1"), - PayloadType::ExecutionResult => write!(f, "ExecutionResult"), - PayloadType::Transfers => write!(f, "Transfers"), - PayloadType::FinalizedDeployApprovals => write!(f, "FinalizedDeployApprovals"), - PayloadType::FinalizedApprovals => write!(f, "FinalizedApprovals"), - PayloadType::SignedBlock => write!(f, "SignedBlock"), - PayloadType::TransactionWithExecutionInfo => write!(f, "TransactionWithExecutionInfo"), - PayloadType::Peers => write!(f, "Peers"), - PayloadType::LastProgress => write!(f, "LastProgress"), - PayloadType::ReactorState => write!(f, "ReactorState"), - PayloadType::NetworkName => write!(f, "NetworkName"), - PayloadType::ConsensusValidatorChanges => write!(f, "ConsensusValidatorChanges"), - PayloadType::BlockSynchronizerStatus => write!(f, "BlockSynchronizerStatus"), - PayloadType::AvailableBlockRange => write!(f, "AvailableBlockRange"), - PayloadType::NextUpgrade => write!(f, "NextUpgrade"), - PayloadType::ConsensusStatus => write!(f, "ConsensusStatus"), - PayloadType::ChainspecRawBytes => write!(f, "ChainspecRawBytes"), - PayloadType::Uptime => write!(f, "Uptime"), - PayloadType::HighestBlockSequenceCheckResult => { - write!(f, "HighestBlockSequenceCheckResult") - } - PayloadType::SpeculativeExecutionResult => write!(f, "SpeculativeExecutionResult"), - PayloadType::GlobalStateQueryResult => write!(f, "GlobalStateQueryResult"), - PayloadType::StoredValues => write!(f, "StoredValues"), - PayloadType::GetTrieFullResult => write!(f, "GetTrieFullResult"), - PayloadType::NodeStatus => write!(f, "NodeStatus"), - PayloadType::WasmV1Result => write!(f, "WasmV1Result"), - PayloadType::DictionaryQueryResult => write!(f, "DictionaryQueryResult"), - PayloadType::BalanceResponse => write!(f, "BalanceResponse"), - PayloadType::Reward => write!(f, "Reward"), - PayloadType::ProtocolVersion => write!(f, "ProtocolVersion"), - } - } -} - -/// Represents an entity that can be sent as a payload. -pub trait PayloadEntity { - /// Returns the payload type of the entity. - const PAYLOAD_TYPE: PayloadType; -} - -impl PayloadEntity for Transaction { - const PAYLOAD_TYPE: PayloadType = PayloadType::Transaction; -} - -impl PayloadEntity for Deploy { - const PAYLOAD_TYPE: PayloadType = PayloadType::Deploy; -} - -impl PayloadEntity for BlockHeader { - const PAYLOAD_TYPE: PayloadType = PayloadType::BlockHeader; -} - -impl PayloadEntity for BlockHeaderV1 { - const PAYLOAD_TYPE: PayloadType = PayloadType::BlockHeaderV1; -} - -impl PayloadEntity for BlockBody { - const PAYLOAD_TYPE: PayloadType = PayloadType::BlockBody; -} - -impl PayloadEntity for BlockBodyV1 { - const PAYLOAD_TYPE: PayloadType = PayloadType::BlockBodyV1; -} - -impl PayloadEntity for BlockSignatures { - const PAYLOAD_TYPE: PayloadType = PayloadType::BlockSignatures; -} - -impl PayloadEntity for BlockSignaturesV1 { - const PAYLOAD_TYPE: PayloadType = PayloadType::BlockSignaturesV1; -} - -impl PayloadEntity for ExecutionResult { - const PAYLOAD_TYPE: PayloadType = PayloadType::ExecutionResult; -} - -impl PayloadEntity for ExecutionResultV1 { - const PAYLOAD_TYPE: PayloadType = PayloadType::ExecutionResultV1; -} - -impl PayloadEntity for SignedBlock { - const PAYLOAD_TYPE: PayloadType = PayloadType::SignedBlock; -} - -impl PayloadEntity for TransactionWithExecutionInfo { - const PAYLOAD_TYPE: PayloadType = PayloadType::TransactionWithExecutionInfo; -} - -impl PayloadEntity for Peers { - const PAYLOAD_TYPE: PayloadType = PayloadType::Peers; -} - -impl PayloadEntity for Vec { - const PAYLOAD_TYPE: PayloadType = PayloadType::Transfers; -} - -impl PayloadEntity for AvailableBlockRange { - const PAYLOAD_TYPE: PayloadType = PayloadType::AvailableBlockRange; -} - -impl PayloadEntity for ChainspecRawBytes { - const PAYLOAD_TYPE: PayloadType = PayloadType::ChainspecRawBytes; -} - -impl PayloadEntity for ConsensusValidatorChanges { - const PAYLOAD_TYPE: PayloadType = PayloadType::ConsensusValidatorChanges; -} - -impl PayloadEntity for GlobalStateQueryResult { - const PAYLOAD_TYPE: PayloadType = PayloadType::GlobalStateQueryResult; -} - -impl PayloadEntity for DictionaryQueryResult { - const PAYLOAD_TYPE: PayloadType = PayloadType::DictionaryQueryResult; -} - -impl PayloadEntity for Vec { - const PAYLOAD_TYPE: PayloadType = PayloadType::StoredValues; -} - -impl PayloadEntity for GetTrieFullResult { - const PAYLOAD_TYPE: PayloadType = PayloadType::GetTrieFullResult; -} - -impl PayloadEntity for SpeculativeExecutionResult { - const PAYLOAD_TYPE: PayloadType = PayloadType::SpeculativeExecutionResult; -} - -impl PayloadEntity for NodeStatus { - const PAYLOAD_TYPE: PayloadType = PayloadType::NodeStatus; -} - -impl PayloadEntity for NextUpgrade { - const PAYLOAD_TYPE: PayloadType = PayloadType::NextUpgrade; -} - -impl PayloadEntity for Uptime { - const PAYLOAD_TYPE: PayloadType = PayloadType::Uptime; -} - -impl PayloadEntity for LastProgress { - const PAYLOAD_TYPE: PayloadType = PayloadType::LastProgress; -} - -impl PayloadEntity for ReactorStateName { - const PAYLOAD_TYPE: PayloadType = PayloadType::ReactorState; -} - -impl PayloadEntity for NetworkName { - const PAYLOAD_TYPE: PayloadType = PayloadType::NetworkName; -} - -impl PayloadEntity for BlockSynchronizerStatus { - const PAYLOAD_TYPE: PayloadType = PayloadType::BlockSynchronizerStatus; -} - -impl PayloadEntity for ConsensusStatus { - const PAYLOAD_TYPE: PayloadType = PayloadType::ConsensusStatus; -} - -impl PayloadEntity for BalanceResponse { - const PAYLOAD_TYPE: PayloadType = PayloadType::BalanceResponse; -} - -impl PayloadEntity for RewardResponse { - const PAYLOAD_TYPE: PayloadType = PayloadType::Reward; -} - -impl PayloadEntity for ProtocolVersion { - const PAYLOAD_TYPE: PayloadType = PayloadType::ProtocolVersion; -} - -#[cfg(test)] -mod tests { - use super::*; - use casper_types::testing::TestRng; - - #[test] - fn convert_u8_roundtrip() { - let rng = &mut TestRng::new(); - - let val = PayloadType::random(rng); - assert_eq!(PayloadType::try_from(val as u8), Ok(val)); - } -} diff --git a/binary_port/src/response_type.rs b/binary_port/src/response_type.rs new file mode 100644 index 0000000000..7f58f3869a --- /dev/null +++ b/binary_port/src/response_type.rs @@ -0,0 +1,468 @@ +//! The payload type. + +use core::{convert::TryFrom, fmt}; + +#[cfg(test)] +use rand::Rng; +#[cfg(feature = "json-schema")] +use schemars::JsonSchema; + +#[cfg(test)] +use casper_types::testing::TestRng; +use casper_types::{ + contracts::ContractPackage, + execution::{ExecutionResult, ExecutionResultV1}, + AvailableBlockRange, BlockBody, BlockBodyV1, BlockHeader, BlockHeaderV1, BlockSignatures, + BlockSignaturesV1, BlockSynchronizerStatus, ChainspecRawBytes, Deploy, NextUpgrade, Package, + Peers, ProtocolVersion, SignedBlock, StoredValue, Transaction, Transfer, +}; + +use crate::{ + global_state_query_result::GlobalStateQueryResult, + node_status::NodeStatus, + speculative_execution_result::SpeculativeExecutionResult, + type_wrappers::{ + ConsensusStatus, ConsensusValidatorChanges, GetTrieFullResult, LastProgress, NetworkName, + ReactorStateName, RewardResponse, + }, + AccountInformation, AddressableEntityInformation, BalanceResponse, ContractInformation, + DictionaryQueryResult, RecordId, TransactionWithExecutionInfo, Uptime, ValueWithProof, +}; + +/// A type of the payload being returned in a binary response. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +#[cfg_attr(feature = "json-schema", derive(JsonSchema))] +pub enum ResponseType { + /// Legacy version of the block header. + BlockHeaderV1, + /// Block header. + BlockHeader, + /// Legacy version of the block body. + BlockBodyV1, + /// Block body. + BlockBody, + /// Legacy version of the approvals hashes. + ApprovalsHashesV1, + /// Approvals hashes + ApprovalsHashes, + /// Legacy version of the block signatures. + BlockSignaturesV1, + /// Block signatures. + BlockSignatures, + /// Deploy. + Deploy, + /// Transaction. + Transaction, + /// Legacy version of the execution result. + ExecutionResultV1, + /// Execution result. + ExecutionResult, + /// Wasm V1 execution result. + WasmV1Result, + /// Transfers. + Transfers, + /// Finalized deploy approvals. + FinalizedDeployApprovals, + /// Finalized approvals. + FinalizedApprovals, + /// Block with signatures. + SignedBlock, + /// Transaction with approvals and execution info. + TransactionWithExecutionInfo, + /// Peers. + Peers, + /// Last progress. + LastProgress, + /// State of the reactor. + ReactorState, + /// Network name. + NetworkName, + /// Consensus validator changes. + ConsensusValidatorChanges, // return type in `effects.rs` will be turned into dedicated type. + /// Status of the block synchronizer. + BlockSynchronizerStatus, + /// Available block range. + AvailableBlockRange, + /// Information about the next network upgrade. + NextUpgrade, + /// Consensus status. + ConsensusStatus, // return type in `effects.rs` will be turned into dedicated type. + /// Chainspec represented as raw bytes. + ChainspecRawBytes, + /// Uptime. + Uptime, + /// Result of checking if given block is in the highest available block range. + HighestBlockSequenceCheckResult, + /// Result of the speculative execution, + SpeculativeExecutionResult, + /// Result of querying global state, + GlobalStateQueryResult, + /// Result of querying global state for all values under a specified key. + StoredValues, + /// Result of querying global state for a full trie. + GetTrieFullResult, + /// Node status. + NodeStatus, + /// Result of querying for a dictionary item. + DictionaryQueryResult, + /// Balance query response. + BalanceResponse, + /// Reward response. + Reward, + /// Protocol version. + ProtocolVersion, + /// Contract package with Merkle proof. + ContractPackageWithProof, + /// Contract information. + ContractInformation, + /// Account information. + AccountInformation, + /// Package with Merkle proof. + PackageWithProof, + /// Addressable entity information. + AddressableEntityInformation, +} + +impl ResponseType { + pub fn from_record_id(record_id: RecordId, is_legacy: bool) -> Self { + match (is_legacy, record_id) { + (true, RecordId::BlockHeader) => Self::BlockHeaderV1, + (true, RecordId::BlockBody) => Self::BlockBodyV1, + (true, RecordId::ApprovalsHashes) => Self::ApprovalsHashesV1, + (true, RecordId::BlockMetadata) => Self::BlockSignaturesV1, + (true, RecordId::Transaction) => Self::Deploy, + (true, RecordId::ExecutionResult) => Self::ExecutionResultV1, + (true, RecordId::Transfer) => Self::Transfers, + (true, RecordId::FinalizedTransactionApprovals) => Self::FinalizedDeployApprovals, + (false, RecordId::BlockHeader) => Self::BlockHeader, + (false, RecordId::BlockBody) => Self::BlockBody, + (false, RecordId::ApprovalsHashes) => Self::ApprovalsHashes, + (false, RecordId::BlockMetadata) => Self::BlockSignatures, + (false, RecordId::Transaction) => Self::Transaction, + (false, RecordId::ExecutionResult) => Self::ExecutionResult, + (false, RecordId::Transfer) => Self::Transfers, + (false, RecordId::FinalizedTransactionApprovals) => Self::FinalizedApprovals, + } + } + + #[cfg(test)] + pub(crate) fn random(rng: &mut TestRng) -> Self { + Self::try_from(rng.gen_range(0..45)).unwrap() + } +} + +impl TryFrom for ResponseType { + type Error = (); + + fn try_from(v: u8) -> Result { + match v { + x if x == ResponseType::BlockHeaderV1 as u8 => Ok(ResponseType::BlockHeaderV1), + x if x == ResponseType::BlockHeader as u8 => Ok(ResponseType::BlockHeader), + x if x == ResponseType::BlockBodyV1 as u8 => Ok(ResponseType::BlockBodyV1), + x if x == ResponseType::BlockBody as u8 => Ok(ResponseType::BlockBody), + x if x == ResponseType::ApprovalsHashesV1 as u8 => Ok(ResponseType::ApprovalsHashesV1), + x if x == ResponseType::ApprovalsHashes as u8 => Ok(ResponseType::ApprovalsHashes), + x if x == ResponseType::BlockSignaturesV1 as u8 => Ok(ResponseType::BlockSignaturesV1), + x if x == ResponseType::BlockSignatures as u8 => Ok(ResponseType::BlockSignatures), + x if x == ResponseType::Deploy as u8 => Ok(ResponseType::Deploy), + x if x == ResponseType::Transaction as u8 => Ok(ResponseType::Transaction), + x if x == ResponseType::ExecutionResultV1 as u8 => Ok(ResponseType::ExecutionResultV1), + x if x == ResponseType::ExecutionResult as u8 => Ok(ResponseType::ExecutionResult), + x if x == ResponseType::Transfers as u8 => Ok(ResponseType::Transfers), + x if x == ResponseType::FinalizedDeployApprovals as u8 => { + Ok(ResponseType::FinalizedDeployApprovals) + } + x if x == ResponseType::FinalizedApprovals as u8 => { + Ok(ResponseType::FinalizedApprovals) + } + x if x == ResponseType::SignedBlock as u8 => Ok(ResponseType::SignedBlock), + x if x == ResponseType::TransactionWithExecutionInfo as u8 => { + Ok(ResponseType::TransactionWithExecutionInfo) + } + x if x == ResponseType::Peers as u8 => Ok(ResponseType::Peers), + x if x == ResponseType::Uptime as u8 => Ok(ResponseType::Uptime), + x if x == ResponseType::LastProgress as u8 => Ok(ResponseType::LastProgress), + x if x == ResponseType::ReactorState as u8 => Ok(ResponseType::ReactorState), + x if x == ResponseType::NetworkName as u8 => Ok(ResponseType::NetworkName), + x if x == ResponseType::ConsensusValidatorChanges as u8 => { + Ok(ResponseType::ConsensusValidatorChanges) + } + x if x == ResponseType::BlockSynchronizerStatus as u8 => { + Ok(ResponseType::BlockSynchronizerStatus) + } + x if x == ResponseType::AvailableBlockRange as u8 => { + Ok(ResponseType::AvailableBlockRange) + } + x if x == ResponseType::NextUpgrade as u8 => Ok(ResponseType::NextUpgrade), + x if x == ResponseType::ConsensusStatus as u8 => Ok(ResponseType::ConsensusStatus), + x if x == ResponseType::ChainspecRawBytes as u8 => Ok(ResponseType::ChainspecRawBytes), + x if x == ResponseType::HighestBlockSequenceCheckResult as u8 => { + Ok(ResponseType::HighestBlockSequenceCheckResult) + } + x if x == ResponseType::SpeculativeExecutionResult as u8 => { + Ok(ResponseType::SpeculativeExecutionResult) + } + x if x == ResponseType::GlobalStateQueryResult as u8 => { + Ok(ResponseType::GlobalStateQueryResult) + } + x if x == ResponseType::StoredValues as u8 => Ok(ResponseType::StoredValues), + x if x == ResponseType::GetTrieFullResult as u8 => Ok(ResponseType::GetTrieFullResult), + x if x == ResponseType::NodeStatus as u8 => Ok(ResponseType::NodeStatus), + x if x == ResponseType::DictionaryQueryResult as u8 => { + Ok(ResponseType::DictionaryQueryResult) + } + x if x == ResponseType::WasmV1Result as u8 => Ok(ResponseType::WasmV1Result), + x if x == ResponseType::BalanceResponse as u8 => Ok(ResponseType::BalanceResponse), + x if x == ResponseType::Reward as u8 => Ok(ResponseType::Reward), + x if x == ResponseType::ProtocolVersion as u8 => Ok(ResponseType::ProtocolVersion), + x if x == ResponseType::ContractPackageWithProof as u8 => { + Ok(ResponseType::ContractPackageWithProof) + } + x if x == ResponseType::ContractInformation as u8 => { + Ok(ResponseType::ContractInformation) + } + x if x == ResponseType::AccountInformation as u8 => { + Ok(ResponseType::AccountInformation) + } + x if x == ResponseType::PackageWithProof as u8 => Ok(ResponseType::PackageWithProof), + x if x == ResponseType::AddressableEntityInformation as u8 => { + Ok(ResponseType::AddressableEntityInformation) + } + _ => Err(()), + } + } +} + +impl From for u8 { + fn from(value: ResponseType) -> Self { + value as u8 + } +} + +impl fmt::Display for ResponseType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ResponseType::BlockHeaderV1 => write!(f, "BlockHeaderV1"), + ResponseType::BlockHeader => write!(f, "BlockHeader"), + ResponseType::BlockBodyV1 => write!(f, "BlockBodyV1"), + ResponseType::BlockBody => write!(f, "BlockBody"), + ResponseType::ApprovalsHashesV1 => write!(f, "ApprovalsHashesV1"), + ResponseType::ApprovalsHashes => write!(f, "ApprovalsHashes"), + ResponseType::BlockSignaturesV1 => write!(f, "BlockSignaturesV1"), + ResponseType::BlockSignatures => write!(f, "BlockSignatures"), + ResponseType::Deploy => write!(f, "Deploy"), + ResponseType::Transaction => write!(f, "Transaction"), + ResponseType::ExecutionResultV1 => write!(f, "ExecutionResultV1"), + ResponseType::ExecutionResult => write!(f, "ExecutionResult"), + ResponseType::Transfers => write!(f, "Transfers"), + ResponseType::FinalizedDeployApprovals => write!(f, "FinalizedDeployApprovals"), + ResponseType::FinalizedApprovals => write!(f, "FinalizedApprovals"), + ResponseType::SignedBlock => write!(f, "SignedBlock"), + ResponseType::TransactionWithExecutionInfo => write!(f, "TransactionWithExecutionInfo"), + ResponseType::Peers => write!(f, "Peers"), + ResponseType::LastProgress => write!(f, "LastProgress"), + ResponseType::ReactorState => write!(f, "ReactorState"), + ResponseType::NetworkName => write!(f, "NetworkName"), + ResponseType::ConsensusValidatorChanges => write!(f, "ConsensusValidatorChanges"), + ResponseType::BlockSynchronizerStatus => write!(f, "BlockSynchronizerStatus"), + ResponseType::AvailableBlockRange => write!(f, "AvailableBlockRange"), + ResponseType::NextUpgrade => write!(f, "NextUpgrade"), + ResponseType::ConsensusStatus => write!(f, "ConsensusStatus"), + ResponseType::ChainspecRawBytes => write!(f, "ChainspecRawBytes"), + ResponseType::Uptime => write!(f, "Uptime"), + ResponseType::HighestBlockSequenceCheckResult => { + write!(f, "HighestBlockSequenceCheckResult") + } + ResponseType::SpeculativeExecutionResult => write!(f, "SpeculativeExecutionResult"), + ResponseType::GlobalStateQueryResult => write!(f, "GlobalStateQueryResult"), + ResponseType::StoredValues => write!(f, "StoredValues"), + ResponseType::GetTrieFullResult => write!(f, "GetTrieFullResult"), + ResponseType::NodeStatus => write!(f, "NodeStatus"), + ResponseType::WasmV1Result => write!(f, "WasmV1Result"), + ResponseType::DictionaryQueryResult => write!(f, "DictionaryQueryResult"), + ResponseType::BalanceResponse => write!(f, "BalanceResponse"), + ResponseType::Reward => write!(f, "Reward"), + ResponseType::ProtocolVersion => write!(f, "ProtocolVersion"), + ResponseType::ContractPackageWithProof => write!(f, "ContractPackageWithProof"), + ResponseType::ContractInformation => write!(f, "ContractInformation"), + ResponseType::AccountInformation => write!(f, "AccountInformation"), + ResponseType::PackageWithProof => write!(f, "PackageWithProof"), + ResponseType::AddressableEntityInformation => { + write!(f, "AddressableEntityInformation") + } + } + } +} + +/// Represents an entity that can be sent as a payload. +pub trait PayloadEntity { + /// Returns the payload type of the entity. + const RESPONSE_TYPE: ResponseType; +} + +impl PayloadEntity for Transaction { + const RESPONSE_TYPE: ResponseType = ResponseType::Transaction; +} + +impl PayloadEntity for Deploy { + const RESPONSE_TYPE: ResponseType = ResponseType::Deploy; +} + +impl PayloadEntity for BlockHeader { + const RESPONSE_TYPE: ResponseType = ResponseType::BlockHeader; +} + +impl PayloadEntity for BlockHeaderV1 { + const RESPONSE_TYPE: ResponseType = ResponseType::BlockHeaderV1; +} + +impl PayloadEntity for BlockBody { + const RESPONSE_TYPE: ResponseType = ResponseType::BlockBody; +} + +impl PayloadEntity for BlockBodyV1 { + const RESPONSE_TYPE: ResponseType = ResponseType::BlockBodyV1; +} + +impl PayloadEntity for BlockSignatures { + const RESPONSE_TYPE: ResponseType = ResponseType::BlockSignatures; +} + +impl PayloadEntity for BlockSignaturesV1 { + const RESPONSE_TYPE: ResponseType = ResponseType::BlockSignaturesV1; +} + +impl PayloadEntity for ExecutionResult { + const RESPONSE_TYPE: ResponseType = ResponseType::ExecutionResult; +} + +impl PayloadEntity for ExecutionResultV1 { + const RESPONSE_TYPE: ResponseType = ResponseType::ExecutionResultV1; +} + +impl PayloadEntity for SignedBlock { + const RESPONSE_TYPE: ResponseType = ResponseType::SignedBlock; +} + +impl PayloadEntity for TransactionWithExecutionInfo { + const RESPONSE_TYPE: ResponseType = ResponseType::TransactionWithExecutionInfo; +} + +impl PayloadEntity for Peers { + const RESPONSE_TYPE: ResponseType = ResponseType::Peers; +} + +impl PayloadEntity for Vec { + const RESPONSE_TYPE: ResponseType = ResponseType::Transfers; +} + +impl PayloadEntity for AvailableBlockRange { + const RESPONSE_TYPE: ResponseType = ResponseType::AvailableBlockRange; +} + +impl PayloadEntity for ChainspecRawBytes { + const RESPONSE_TYPE: ResponseType = ResponseType::ChainspecRawBytes; +} + +impl PayloadEntity for ConsensusValidatorChanges { + const RESPONSE_TYPE: ResponseType = ResponseType::ConsensusValidatorChanges; +} + +impl PayloadEntity for GlobalStateQueryResult { + const RESPONSE_TYPE: ResponseType = ResponseType::GlobalStateQueryResult; +} + +impl PayloadEntity for DictionaryQueryResult { + const RESPONSE_TYPE: ResponseType = ResponseType::DictionaryQueryResult; +} + +impl PayloadEntity for Vec { + const RESPONSE_TYPE: ResponseType = ResponseType::StoredValues; +} + +impl PayloadEntity for GetTrieFullResult { + const RESPONSE_TYPE: ResponseType = ResponseType::GetTrieFullResult; +} + +impl PayloadEntity for SpeculativeExecutionResult { + const RESPONSE_TYPE: ResponseType = ResponseType::SpeculativeExecutionResult; +} + +impl PayloadEntity for NodeStatus { + const RESPONSE_TYPE: ResponseType = ResponseType::NodeStatus; +} + +impl PayloadEntity for NextUpgrade { + const RESPONSE_TYPE: ResponseType = ResponseType::NextUpgrade; +} + +impl PayloadEntity for Uptime { + const RESPONSE_TYPE: ResponseType = ResponseType::Uptime; +} + +impl PayloadEntity for LastProgress { + const RESPONSE_TYPE: ResponseType = ResponseType::LastProgress; +} + +impl PayloadEntity for ReactorStateName { + const RESPONSE_TYPE: ResponseType = ResponseType::ReactorState; +} + +impl PayloadEntity for NetworkName { + const RESPONSE_TYPE: ResponseType = ResponseType::NetworkName; +} + +impl PayloadEntity for BlockSynchronizerStatus { + const RESPONSE_TYPE: ResponseType = ResponseType::BlockSynchronizerStatus; +} + +impl PayloadEntity for ConsensusStatus { + const RESPONSE_TYPE: ResponseType = ResponseType::ConsensusStatus; +} + +impl PayloadEntity for BalanceResponse { + const RESPONSE_TYPE: ResponseType = ResponseType::BalanceResponse; +} + +impl PayloadEntity for RewardResponse { + const RESPONSE_TYPE: ResponseType = ResponseType::Reward; +} + +impl PayloadEntity for ProtocolVersion { + const RESPONSE_TYPE: ResponseType = ResponseType::ProtocolVersion; +} + +impl PayloadEntity for ValueWithProof { + const RESPONSE_TYPE: ResponseType = ResponseType::ContractPackageWithProof; +} + +impl PayloadEntity for ContractInformation { + const RESPONSE_TYPE: ResponseType = ResponseType::ContractInformation; +} + +impl PayloadEntity for AccountInformation { + const RESPONSE_TYPE: ResponseType = ResponseType::AccountInformation; +} + +impl PayloadEntity for ValueWithProof { + const RESPONSE_TYPE: ResponseType = ResponseType::PackageWithProof; +} + +impl PayloadEntity for AddressableEntityInformation { + const RESPONSE_TYPE: ResponseType = ResponseType::AddressableEntityInformation; +} + +#[cfg(test)] +mod tests { + use super::*; + use casper_types::testing::TestRng; + + #[test] + fn convert_u8_roundtrip() { + let rng = &mut TestRng::new(); + + let val = ResponseType::random(rng); + assert_eq!(ResponseType::try_from(val as u8), Ok(val)); + } +} diff --git a/binary_port/src/type_wrappers.rs b/binary_port/src/type_wrappers.rs index 82e9ea1908..11d4e40e37 100644 --- a/binary_port/src/type_wrappers.rs +++ b/binary_port/src/type_wrappers.rs @@ -6,9 +6,12 @@ use datasize::DataSize; use casper_types::{ bytesrepr::{self, Bytes, FromBytes, ToBytes}, + contracts::ContractHash, + global_state::TrieMerkleProof, system::auction::DelegationRate, - BlockHash, EraId, ExecutionInfo, Key, PublicKey, TimeDiff, Timestamp, Transaction, - ValidatorChange, U512, + Account, AddressableEntity, BlockHash, ByteCode, Contract, ContractWasm, EntityAddr, EraId, + ExecutionInfo, Key, PublicKey, StoredValue, TimeDiff, Timestamp, Transaction, ValidatorChange, + U512, }; use serde::Serialize; @@ -407,6 +410,289 @@ impl FromBytes for DictionaryQueryResult { } } +/// An account with its associated merkle proof. +#[derive(Debug, PartialEq)] +pub struct AccountInformation { + account: Account, + merkle_proof: Vec>, +} + +impl AccountInformation { + /// Constructs a new `AccountResponse`. + pub fn new(account: Account, merkle_proof: Vec>) -> Self { + Self { + account, + merkle_proof, + } + } + + /// Returns the inner `Account`. + pub fn account(&self) -> &Account { + &self.account + } + + /// Returns the merkle proof. + pub fn merkle_proof(&self) -> &Vec> { + &self.merkle_proof + } + + /// Converts `self` into the account and merkle proof. + pub fn into_inner(self) -> (Account, Vec>) { + (self.account, self.merkle_proof) + } +} + +impl ToBytes for AccountInformation { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + self.account.write_bytes(writer)?; + self.merkle_proof.write_bytes(writer) + } + + fn serialized_length(&self) -> usize { + self.account.serialized_length() + self.merkle_proof.serialized_length() + } +} + +impl FromBytes for AccountInformation { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { + let (account, remainder) = FromBytes::from_bytes(bytes)?; + let (merkle_proof, remainder) = FromBytes::from_bytes(remainder)?; + Ok((AccountInformation::new(account, merkle_proof), remainder)) + } +} + +/// A contract with its associated Wasm and merkle proof. +#[derive(Debug, PartialEq)] +pub struct ContractInformation { + hash: ContractHash, + contract: ValueWithProof, + wasm: Option>, +} + +impl ContractInformation { + /// Constructs new `ContractInformation`. + pub fn new( + hash: ContractHash, + contract: ValueWithProof, + wasm: Option>, + ) -> Self { + Self { + hash, + contract, + wasm, + } + } + + /// Returns the hash of the contract. + pub fn hash(&self) -> ContractHash { + self.hash + } + + /// Returns the inner `Contract`. + pub fn contract(&self) -> &Contract { + &self.contract.value + } + + /// Returns the Merkle proof of the contract. + pub fn contract_proof(&self) -> &Vec> { + &self.contract.merkle_proof + } + + /// Returns the inner `ContractWasm` with its proof. + pub fn wasm(&self) -> Option<&ValueWithProof> { + self.wasm.as_ref() + } + + /// Converts `self` into the contract hash, contract and Wasm. + pub fn into_inner( + self, + ) -> ( + ContractHash, + ValueWithProof, + Option>, + ) { + (self.hash, self.contract, self.wasm) + } +} + +impl ToBytes for ContractInformation { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + self.hash.write_bytes(writer)?; + self.contract.write_bytes(writer)?; + self.wasm.write_bytes(writer) + } + + fn serialized_length(&self) -> usize { + self.hash.serialized_length() + + self.contract.serialized_length() + + self.wasm.serialized_length() + } +} + +impl FromBytes for ContractInformation { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { + let (hash, remainder) = FromBytes::from_bytes(bytes)?; + let (contract, remainder) = FromBytes::from_bytes(remainder)?; + let (wasm, remainder) = FromBytes::from_bytes(remainder)?; + Ok((ContractInformation::new(hash, contract, wasm), remainder)) + } +} + +/// A contract entity with its associated ByteCode. +#[derive(Debug, PartialEq)] +pub struct AddressableEntityInformation { + addr: EntityAddr, + entity: ValueWithProof, + bytecode: Option>, +} + +impl AddressableEntityInformation { + /// Constructs new contract entity with ByteCode. + pub fn new( + addr: EntityAddr, + entity: ValueWithProof, + bytecode: Option>, + ) -> Self { + Self { + addr, + entity, + bytecode, + } + } + + /// Returns the entity address. + pub fn addr(&self) -> EntityAddr { + self.addr + } + + /// Returns the inner `AddressableEntity`. + pub fn entity(&self) -> &AddressableEntity { + &self.entity.value + } + + /// Returns the inner `ByteCodeWithProof`. + pub fn entity_merkle_proof(&self) -> &Vec> { + &self.entity.merkle_proof + } + + /// Returns the inner `ByteCode`. + pub fn bytecode(&self) -> Option<&ValueWithProof> { + self.bytecode.as_ref() + } + + /// Converts `self` into the entity address, entity and ByteCode. + pub fn into_inner( + self, + ) -> ( + EntityAddr, + ValueWithProof, + Option>, + ) { + (self.addr, self.entity, self.bytecode) + } +} + +impl ToBytes for AddressableEntityInformation { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + self.addr.write_bytes(writer)?; + self.entity.write_bytes(writer)?; + self.bytecode.write_bytes(writer) + } + + fn serialized_length(&self) -> usize { + self.addr.serialized_length() + + self.entity.serialized_length() + + self.bytecode.serialized_length() + } +} + +impl FromBytes for AddressableEntityInformation { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { + let (addr, remainder) = FromBytes::from_bytes(bytes)?; + let (entity, remainder) = FromBytes::from_bytes(remainder)?; + let (bytecode, remainder) = FromBytes::from_bytes(remainder)?; + Ok(( + AddressableEntityInformation::new(addr, entity, bytecode), + remainder, + )) + } +} + +/// A value with its associated Merkle proof. +#[derive(Debug, PartialEq)] +pub struct ValueWithProof { + value: T, + merkle_proof: Vec>, +} + +impl ValueWithProof { + /// Constructs a new `ValueWithProof`. + pub fn new(value: T, merkle_proof: Vec>) -> Self { + Self { + value, + merkle_proof, + } + } + + /// Returns the value. + pub fn value(&self) -> &T { + &self.value + } + + /// Returns the Merkle proof. + pub fn merkle_proof(&self) -> &[TrieMerkleProof] { + &self.merkle_proof + } + + /// Converts `self` into the value and Merkle proof. + pub fn into_inner(self) -> (T, Vec>) { + (self.value, self.merkle_proof) + } +} + +impl ToBytes for ValueWithProof { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + self.value.write_bytes(writer)?; + self.merkle_proof.write_bytes(writer) + } + + fn serialized_length(&self) -> usize { + self.value.serialized_length() + self.merkle_proof.serialized_length() + } +} + +impl FromBytes for ValueWithProof { + fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { + let (value, remainder) = FromBytes::from_bytes(bytes)?; + let (merkle_proof, remainder) = FromBytes::from_bytes(remainder)?; + Ok((ValueWithProof::new(value, merkle_proof), remainder)) + } +} + impl_bytesrepr_for_type_wrapper!(Uptime); impl_bytesrepr_for_type_wrapper!(ConsensusValidatorChanges); impl_bytesrepr_for_type_wrapper!(NetworkName); @@ -421,7 +707,8 @@ mod tests { use super::*; use casper_types::{ - execution::ExecutionResult, testing::TestRng, BlockHash, CLValue, StoredValue, + contracts::ContractPackageHash, execution::ExecutionResult, testing::TestRng, BlockHash, + CLValue, ContractWasmHash, StoredValue, }; #[test] @@ -508,4 +795,43 @@ mod tests { ), )); } + + #[test] + fn contract_with_wasm_roundtrip() { + let rng = &mut TestRng::new(); + bytesrepr::test_serialization_roundtrip(&ContractInformation::new( + ContractHash::new(rng.gen()), + ValueWithProof::new( + Contract::new( + ContractPackageHash::new(rng.gen()), + ContractWasmHash::new(rng.gen()), + Default::default(), + Default::default(), + Default::default(), + ), + Default::default(), + ), + rng.gen::().then(|| { + ValueWithProof::new( + ContractWasm::new(rng.random_vec(10..50)), + Default::default(), + ) + }), + )); + } + + #[test] + fn addressable_entity_with_byte_code_roundtrip() { + let rng = &mut TestRng::new(); + bytesrepr::test_serialization_roundtrip(&AddressableEntityInformation::new( + rng.gen(), + ValueWithProof::new(AddressableEntity::example().clone(), Default::default()), + rng.gen::().then(|| { + ValueWithProof::new( + ByteCode::new(rng.gen(), rng.random_vec(10..50)), + Default::default(), + ) + }), + )); + } } diff --git a/node/BINARY_PORT_PROTOCOL.md b/node/BINARY_PORT_PROTOCOL.md index d1fc124cd8..3f974dea83 100644 --- a/node/BINARY_PORT_PROTOCOL.md +++ b/node/BINARY_PORT_PROTOCOL.md @@ -5,12 +5,12 @@ This page specifies the communication protocol between the [RPC Sidecar](https:/ The communication protocol between the Sidecar and the binary port is a binary protocol that follows a simple request-response model. The protocol consists of one party (the client) sending requests to another party (the server) and the server sending responses back to the client. Both requests and responses are wrapped in envelopes containing a version and a payload type tag. The versioning scheme is based on [SemVer](https://semver.org/). See [versioning](#versioning) for more details. The payload type tags are used to interpret the contents of the payloads. ### Request format -| Size in bytes | Field | Description | -|---------------|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Size in bytes | Field | Description | +|---------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 2 | Version of the binary port header | Version of the binary port header serialized as a single u16 number. Upon receiving the request, the binary port component will first read data from this field and check it against the currently supported version. In case of a version mismatch, the appropriate error response will be sent. | -| 12 | Chain protocol version | Chain protocol version as a u32 triplet (major, minor, patch). This parameter is used to determine whether an incoming request is compatible (according to semver rules) with the current chain protocol version. If not, the appropriate error response will be sent. | -| 1 | BinaryRequestTag | Tag identifying the request. | -| Variable | RequestPayload | Payload to be interpreted according to the `BinaryRequestTag`. | +| 12 | Chain protocol version | Chain protocol version as a u32 triplet (major, minor, patch). This parameter is used to determine whether an incoming request is compatible (according to semver rules) with the current chain protocol version. If not, the appropriate error response will be sent. | +| 1 | BinaryRequestTag | Tag identifying the request. | +| Variable | RequestPayload | Payload to be interpreted according to the `BinaryRequestTag`. | Request bytes can be constructed from the bytesrepr-serialized `BinaryRequestHeader` followed by the bytesrepr-serialized `BinaryRequest`. @@ -19,13 +19,13 @@ Request bytes can be constructed from the bytesrepr-serialized `BinaryRequestHea ### Response format | Size in bytes | Field | Description | |-----------------|-----------------|--------------------------------------------------------------------------| -| 2 | Request ID | Request ID as a u16 number. | -| 4 | LengthOfRequest | Length of the request (encoded as bytes) for this response. | -| LengthOfRequest | RequestBytes | The request, encoded as bytes, corresponding to this response. | +| 2 | Request ID | Request ID as a u16 number. | +| 4 | LengthOfRequest | Length of the request (encoded as bytes) for this response. | +| LengthOfRequest | RequestBytes | The request, encoded as bytes, corresponding to this response. | | 12 | ProtocolVersion | Protocol version as a u32 triplet (major, minor, patch). | | 2 | ErrorCode | Error code, where 0 indicates success. | -| 1-2 | PayloadType | Optional payload type tag (first byte being 1 indicates that it exists). | -| Variable | Payload | Payload to be interpreted according to the `PayloadTag`. | +| 1-2 | ResponseType | Optional payload type tag (first byte being 1 indicates that it exists).| +| Variable | Payload | Payload to be interpreted according to the `PayloadTag`. | `BinaryResponseAndRequest` object can be bytesrepr-deserialized from these bytes. @@ -34,7 +34,7 @@ Request bytes can be constructed from the bytesrepr-serialized `BinaryRequestHea ## Versioning Versioning is based on the protocol version of the Casper Platform. The request/response model was designed to support **backward-compatible** changes to some parts, which are allowed to change between **MINOR** versions: - addition of a new [`BinaryRequestTag`](#request-format) with its own payload -- addition of a new [`PayloadType`](#response-format) with its own payload +- addition of a new [`ResponseType`](#response-format) with its own payload - addition of a new [`RecordId`](#request-model-details) - addition of a new [`InformationRequestTag`](#request-model-details) - addition of a new [`ErrorCode`](#response-format) diff --git a/node/src/components/binary_port.rs b/node/src/components/binary_port.rs index e92339e45a..2012a74608 100644 --- a/node/src/components/binary_port.rs +++ b/node/src/components/binary_port.rs @@ -9,12 +9,13 @@ mod tests; use std::{convert::TryFrom, net::SocketAddr, sync::Arc}; use casper_binary_port::{ - BalanceResponse, BinaryMessage, BinaryMessageCodec, BinaryRequest, BinaryRequestHeader, - BinaryRequestTag, BinaryResponse, BinaryResponseAndRequest, DictionaryItemIdentifier, - DictionaryQueryResult, EraIdentifier, ErrorCode, GetRequest, GetTrieFullResult, + AccountInformation, AddressableEntityInformation, BalanceResponse, BinaryMessage, + BinaryMessageCodec, BinaryRequest, BinaryRequestHeader, BinaryRequestTag, BinaryResponse, + BinaryResponseAndRequest, ContractInformation, DictionaryItemIdentifier, DictionaryQueryResult, + EntityIdentifier, EraIdentifier, ErrorCode, GetRequest, GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest, InformationRequest, InformationRequestTag, - KeyPrefix, NodeStatus, PayloadType, PurseIdentifier, ReactorStateName, RecordId, - RewardResponse, TransactionWithExecutionInfo, + KeyPrefix, NodeStatus, PackageIdentifier, PurseIdentifier, ReactorStateName, RecordId, + ResponseType, RewardResponse, TransactionWithExecutionInfo, ValueWithProof, }; use casper_storage::{ data_access_layer::{ @@ -31,13 +32,17 @@ use casper_storage::{ KeyPrefix as StorageKeyPrefix, }; use casper_types::{ + account::AccountHash, addressable_entity::NamedKeyAddr, bytesrepr::{self, FromBytes, ToBytes}, - BlockHeader, BlockIdentifier, Chainspec, Digest, EntityAddr, GlobalStateIdentifier, Key, Peers, - ProtocolVersion, Rewards, SignedBlock, StoredValue, TimeDiff, Transaction, + contracts::{ContractHash, ContractPackage, ContractPackageHash}, + BlockHeader, BlockIdentifier, ByteCode, ByteCodeAddr, ByteCodeHash, Chainspec, ContractWasm, + ContractWasmHash, Digest, EntityAddr, GlobalStateIdentifier, Key, Package, PackageAddr, Peers, + ProtocolVersion, Rewards, SignedBlock, StoredValue, TimeDiff, Transaction, URef, }; use datasize::DataSize; +use either::Either; use futures::{future::BoxFuture, FutureExt, SinkExt, StreamExt}; use once_cell::sync::OnceCell; use prometheus::Registry; @@ -215,7 +220,7 @@ where let Ok(serialized) = bincode::serialize(&transfers) else { return BinaryResponse::new_error(ErrorCode::InternalError, protocol_version); }; - BinaryResponse::from_raw_bytes(PayloadType::Transfers, serialized, protocol_version) + BinaryResponse::from_raw_bytes(ResponseType::Transfers, serialized, protocol_version) } GetRequest::Record { record_type_tag, @@ -227,7 +232,8 @@ where let Some(db_bytes) = effect_builder.get_raw_data(record_id, key).await else { return BinaryResponse::new_empty(protocol_version); }; - let payload_type = PayloadType::from_record_id(record_id, db_bytes.is_legacy()); + let payload_type = + ResponseType::from_record_id(record_id, db_bytes.is_legacy()); BinaryResponse::from_raw_bytes( payload_type, db_bytes.into_raw_bytes(), @@ -710,6 +716,264 @@ where } } +async fn get_contract_package( + effect_builder: EffectBuilder, + state_root_hash: Digest, + hash: ContractPackageHash, +) -> Result, ValueWithProof>>, ErrorCode> +where + REv: From + + From + + From + + From, +{ + let key = Key::Hash(hash.value()); + let Some(result) = get_global_state_item(effect_builder, state_root_hash, key, vec![]).await? + else { + return Ok(None); + }; + match result.into_inner() { + (StoredValue::ContractPackage(contract), proof) => { + Ok(Some(Either::Left(ValueWithProof::new(contract, proof)))) + } + (other, _) => { + let Some((Key::Package(addr), _)) = other + .as_cl_value() + .and_then(|cl_val| cl_val.to_t::<(Key, URef)>().ok()) + else { + debug!( + ?other, + "unexpected stored value found when querying for a contract package" + ); + return Err(ErrorCode::InternalError); + }; + let package = get_package(effect_builder, state_root_hash, addr).await?; + Ok(package.map(Either::Right)) + } + } +} + +async fn get_package( + effect_builder: EffectBuilder, + state_root_hash: Digest, + package_addr: PackageAddr, +) -> Result>, ErrorCode> +where + REv: From + + From + + From + + From, +{ + let key = Key::Package(package_addr); + let Some(result) = get_global_state_item(effect_builder, state_root_hash, key, vec![]).await? + else { + return Ok(None); + }; + match result.into_inner() { + (StoredValue::Package(contract), proof) => Ok(Some(ValueWithProof::new(contract, proof))), + other => { + debug!( + ?other, + "unexpected stored value found when querying for a package" + ); + Err(ErrorCode::InternalError) + } + } +} + +async fn get_contract( + effect_builder: EffectBuilder, + state_root_hash: Digest, + hash: ContractHash, + include_wasm: bool, +) -> Result>, ErrorCode> +where + REv: From + + From + + From + + From, +{ + let key = Key::Hash(hash.value()); + let Some(result) = get_global_state_item(effect_builder, state_root_hash, key, vec![]).await? + else { + return Ok(None); + }; + match result.into_inner() { + (StoredValue::Contract(contract), proof) + if include_wasm && contract.contract_wasm_hash() != ContractWasmHash::default() => + { + let wasm_hash = contract.contract_wasm_hash(); + let Some(wasm) = get_contract_wasm(effect_builder, state_root_hash, wasm_hash).await? + else { + return Ok(None); + }; + Ok(Some(Either::Left(ContractInformation::new( + hash, + ValueWithProof::new(contract, proof), + Some(wasm), + )))) + } + (StoredValue::Contract(contract), proof) => Ok(Some(Either::Left( + ContractInformation::new(hash, ValueWithProof::new(contract, proof), None), + ))), + (other, _) => { + let Some(Key::AddressableEntity(addr)) = other + .as_cl_value() + .and_then(|cl_val| cl_val.to_t::().ok()) + else { + debug!( + ?other, + "unexpected stored value found when querying for a contract" + ); + return Err(ErrorCode::InternalError); + }; + let entity = get_entity(effect_builder, state_root_hash, addr, include_wasm).await?; + Ok(entity.map(Either::Right)) + } + } +} + +async fn get_account( + effect_builder: EffectBuilder, + state_root_hash: Digest, + hash: AccountHash, + include_bytecode: bool, +) -> Result>, ErrorCode> +where + REv: From + + From + + From + + From, +{ + let key = Key::Account(hash); + let Some(result) = get_global_state_item(effect_builder, state_root_hash, key, vec![]).await? + else { + return Ok(None); + }; + match result.into_inner() { + (StoredValue::Account(account), proof) => { + Ok(Some(Either::Left(AccountInformation::new(account, proof)))) + } + (other, _) => { + let Some(Key::AddressableEntity(addr)) = other + .as_cl_value() + .and_then(|cl_val| cl_val.to_t::().ok()) + else { + debug!( + ?other, + "unexpected stored value found when querying for an account" + ); + return Err(ErrorCode::InternalError); + }; + let entity = + get_entity(effect_builder, state_root_hash, addr, include_bytecode).await?; + Ok(entity.map(Either::Right)) + } + } +} + +async fn get_entity( + effect_builder: EffectBuilder, + state_root_hash: Digest, + addr: EntityAddr, + include_bytecode: bool, +) -> Result, ErrorCode> +where + REv: From + + From + + From + + From, +{ + let key = Key::from(addr); + let Some(result) = get_global_state_item(effect_builder, state_root_hash, key, vec![]).await? + else { + return Ok(None); + }; + match result.into_inner() { + (StoredValue::AddressableEntity(entity), proof) + if include_bytecode && entity.byte_code_hash() != ByteCodeHash::default() => + { + let Some(bytecode) = + get_contract_bytecode(effect_builder, state_root_hash, entity.byte_code_hash()) + .await? + else { + return Ok(None); + }; + Ok(Some(AddressableEntityInformation::new( + addr, + ValueWithProof::new(entity, proof), + Some(bytecode), + ))) + } + (StoredValue::AddressableEntity(entity), proof) => Ok(Some( + AddressableEntityInformation::new(addr, ValueWithProof::new(entity, proof), None), + )), + (other, _) => { + debug!( + ?other, + "unexpected stored value found when querying for an entity" + ); + Err(ErrorCode::InternalError) + } + } +} + +async fn get_contract_wasm( + effect_builder: EffectBuilder, + state_root_hash: Digest, + hash: ContractWasmHash, +) -> Result>, ErrorCode> +where + REv: From + + From + + From + + From, +{ + let key = Key::from(hash); + let Some(value) = get_global_state_item(effect_builder, state_root_hash, key, vec![]).await? + else { + return Ok(None); + }; + match value.into_inner() { + (StoredValue::ContractWasm(wasm), proof) => Ok(Some(ValueWithProof::new(wasm, proof))), + other => { + debug!( + ?other, + "unexpected stored value found when querying for Wasm" + ); + Err(ErrorCode::InternalError) + } + } +} + +async fn get_contract_bytecode( + effect_builder: EffectBuilder, + state_root_hash: Digest, + addr: ByteCodeHash, +) -> Result>, ErrorCode> +where + REv: From + + From + + From + + From, +{ + let key = Key::ByteCode(ByteCodeAddr::new_wasm_addr(addr.value())); + let Some(value) = get_global_state_item(effect_builder, state_root_hash, key, vec![]).await? + else { + return Ok(None); + }; + match value.into_inner() { + (StoredValue::ByteCode(bytecode), proof) => Ok(Some(ValueWithProof::new(bytecode, proof))), + other => { + debug!( + ?other, + "unexpected stored value found when querying for bytecode" + ); + Err(ErrorCode::InternalError) + } + } +} + async fn handle_info_request( req: InformationRequest, effect_builder: EffectBuilder, @@ -976,6 +1240,98 @@ where InformationRequest::ProtocolVersion => { BinaryResponse::from_value(protocol_version, protocol_version) } + InformationRequest::Package { + state_identifier, + identifier, + } => { + let Some(state_root_hash) = + resolve_state_root_hash(effect_builder, state_identifier).await + else { + return BinaryResponse::new_error(ErrorCode::RootNotFound, protocol_version); + }; + let either = match identifier { + PackageIdentifier::ContractPackageHash(hash) => { + get_contract_package(effect_builder, state_root_hash, hash).await + } + PackageIdentifier::PackageAddr(addr) => { + get_package(effect_builder, state_root_hash, addr) + .await + .map(|opt| opt.map(Either::Right)) + } + }; + match either { + Ok(Some(Either::Left(contract_package))) => { + BinaryResponse::from_value(contract_package, protocol_version) + } + Ok(Some(Either::Right(package))) => { + BinaryResponse::from_value(package, protocol_version) + } + Ok(None) => BinaryResponse::new_empty(protocol_version), + Err(err) => BinaryResponse::new_error(err, protocol_version), + } + } + InformationRequest::Entity { + state_identifier, + identifier, + include_bytecode, + } => { + let Some(state_root_hash) = + resolve_state_root_hash(effect_builder, state_identifier).await + else { + return BinaryResponse::new_error(ErrorCode::RootNotFound, protocol_version); + }; + match identifier { + EntityIdentifier::ContractHash(hash) => { + match get_contract(effect_builder, state_root_hash, hash, include_bytecode) + .await + { + Ok(Some(Either::Left(contract))) => { + BinaryResponse::from_value(contract, protocol_version) + } + Ok(Some(Either::Right(entity))) => { + BinaryResponse::from_value(entity, protocol_version) + } + Ok(None) => BinaryResponse::new_empty(protocol_version), + Err(err) => BinaryResponse::new_error(err, protocol_version), + } + } + EntityIdentifier::AccountHash(hash) => { + match get_account(effect_builder, state_root_hash, hash, include_bytecode).await + { + Ok(Some(Either::Left(account))) => { + BinaryResponse::from_value(account, protocol_version) + } + Ok(Some(Either::Right(entity))) => { + BinaryResponse::from_value(entity, protocol_version) + } + Ok(None) => BinaryResponse::new_empty(protocol_version), + Err(err) => BinaryResponse::new_error(err, protocol_version), + } + } + EntityIdentifier::PublicKey(pub_key) => { + let hash = pub_key.to_account_hash(); + match get_account(effect_builder, state_root_hash, hash, include_bytecode).await + { + Ok(Some(Either::Left(account))) => { + BinaryResponse::from_value(account, protocol_version) + } + Ok(Some(Either::Right(entity))) => { + BinaryResponse::from_value(entity, protocol_version) + } + Ok(None) => BinaryResponse::new_empty(protocol_version), + Err(err) => BinaryResponse::new_error(err, protocol_version), + } + } + EntityIdentifier::EntityAddr(addr) => { + match get_entity(effect_builder, state_root_hash, addr, include_bytecode).await + { + Ok(Some(entity)) => BinaryResponse::from_value(entity, protocol_version), + Ok(None) => BinaryResponse::new_empty(protocol_version), + Err(err) => BinaryResponse::new_error(err, protocol_version), + } + } + } + } } } diff --git a/node/src/reactor/main_reactor/tests/binary_port.rs b/node/src/reactor/main_reactor/tests/binary_port.rs index 076e9d6561..234ae65753 100644 --- a/node/src/reactor/main_reactor/tests/binary_port.rs +++ b/node/src/reactor/main_reactor/tests/binary_port.rs @@ -7,25 +7,32 @@ use std::{ }; use casper_binary_port::{ - BalanceResponse, BinaryMessage, BinaryMessageCodec, BinaryRequest, BinaryRequestHeader, - BinaryResponse, BinaryResponseAndRequest, ConsensusStatus, ConsensusValidatorChanges, - DictionaryItemIdentifier, DictionaryQueryResult, EraIdentifier, ErrorCode, GetRequest, - GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest, InformationRequest, - InformationRequestTag, KeyPrefix, LastProgress, NetworkName, NodeStatus, PayloadType, - PurseIdentifier, ReactorStateName, RecordId, RewardResponse, Uptime, + AccountInformation, AddressableEntityInformation, BalanceResponse, BinaryMessage, + BinaryMessageCodec, BinaryRequest, BinaryRequestHeader, BinaryResponse, + BinaryResponseAndRequest, ConsensusStatus, ConsensusValidatorChanges, ContractInformation, + DictionaryItemIdentifier, DictionaryQueryResult, EntityIdentifier, EraIdentifier, ErrorCode, + GetRequest, GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest, InformationRequest, + InformationRequestTag, KeyPrefix, LastProgress, NetworkName, NodeStatus, PackageIdentifier, + PurseIdentifier, ReactorStateName, RecordId, ResponseType, RewardResponse, Uptime, + ValueWithProof, }; use casper_storage::global_state::state::CommitProvider; use casper_types::{ - account::{AccountHash, ActionThresholds, AssociatedKeys}, - addressable_entity::{NamedKeyAddr, NamedKeyValue}, + account::AccountHash, + addressable_entity::{ + ActionThresholds, AssociatedKeys, MessageTopics, NamedKeyAddr, NamedKeyValue, + }, bytesrepr::{FromBytes, ToBytes}, + contracts::{ContractHash, ContractPackage, ContractPackageHash}, execution::{Effects, TransformKindV2, TransformV2}, testing::TestRng, - Account, AvailableBlockRange, Block, BlockHash, BlockHeader, BlockIdentifier, - BlockSynchronizerStatus, CLValue, CLValueDictionary, ChainspecRawBytes, DictionaryAddr, Digest, - EntityAddr, GlobalStateIdentifier, Key, KeyTag, NextUpgrade, Peers, ProtocolVersion, PublicKey, - Rewards, SecretKey, SignedBlock, StoredValue, Transaction, TransactionV1Builder, Transfer, - URef, U512, + Account, AddressableEntity, AvailableBlockRange, Block, BlockHash, BlockHeader, + BlockIdentifier, BlockSynchronizerStatus, ByteCode, ByteCodeAddr, ByteCodeHash, ByteCodeKind, + CLValue, CLValueDictionary, ChainspecRawBytes, Contract, ContractWasm, ContractWasmHash, + DictionaryAddr, Digest, EntityAddr, EntityKind, EntityVersions, GlobalStateIdentifier, Key, + KeyTag, NextUpgrade, Package, PackageAddr, PackageHash, Peers, ProtocolVersion, PublicKey, + Rewards, SecretKey, SignedBlock, StoredValue, Transaction, TransactionRuntime, + TransactionV1Builder, Transfer, URef, U512, }; use futures::{SinkExt, StreamExt}; use rand::Rng; @@ -55,9 +62,7 @@ struct TestData { highest_block: Block, secret_signing_key: Arc, state_root_hash: Digest, - test_account_hash: AccountHash, - test_entity_addr: EntityAddr, - test_dict_seed_uref: URef, + effects: TestEffects, era_one_validator: PublicKey, } @@ -140,7 +145,7 @@ async fn setup() -> ( .main_reactor() .contract_runtime() .data_access_layer() - .commit_effects(*highest_block.state_root_hash(), effects.effects) + .commit_effects(*highest_block.state_root_hash(), effects.effects.clone()) .expect("should commit effects"); // Get the binary port address. @@ -171,9 +176,7 @@ async fn setup() -> ( highest_block, secret_signing_key, state_root_hash, - test_account_hash: effects.test_account_hash, - test_entity_addr: effects.test_entity_addr, - test_dict_seed_uref: effects.test_dict_seed_uref, + effects, era_one_validator: rewards .last_key_value() .expect("should have at least one reward") @@ -186,34 +189,53 @@ async fn setup() -> ( fn test_effects(rng: &mut TestRng) -> TestEffects { // we set up some basic data for global state tests, including an account and a dictionary - let account_hash = AccountHash::new(rng.gen()); - let entity_addr = rng.gen(); - let dict_seed_uref = rng.gen(); + let pre_migration_account_hash = AccountHash::new(rng.gen()); + let post_migration_account_hash = AccountHash::new(rng.gen()); + let main_purse: URef = rng.gen(); + + let pre_migration_contract_package_hash = ContractPackageHash::new(rng.gen()); + let pre_migration_contract_hash = ContractHash::new(rng.gen()); + let post_migration_contract_package_hash = ContractPackageHash::new(rng.gen()); + let post_migration_contract_hash = ContractHash::new(rng.gen()); + let wasm_hash = ContractWasmHash::new(rng.gen()); + + let package_addr: PackageAddr = rng.gen(); + let package_access_key: URef = rng.gen(); + let entity_addr: EntityAddr = rng.gen(); + let entity_bytecode_hash: ByteCodeHash = ByteCodeHash::new(rng.gen()); + + let dict_seed_uref: URef = rng.gen(); let dict_key = Key::dictionary(dict_seed_uref, TEST_DICT_ITEM_KEY.as_bytes()); let dict_value = CLValueDictionary::new( - CLValue::from_t(0).unwrap(), + CLValue::from_t(rng.gen::()).unwrap(), dict_seed_uref.addr().to_vec(), TEST_DICT_ITEM_KEY.as_bytes().to_vec(), ); - let main_purse = rng.gen(); let mut effects = Effects::new(); + effects.push(TransformV2::new( - dict_key, - TransformKindV2::Write(StoredValue::CLValue(CLValue::from_t(dict_value).unwrap())), - )); - effects.push(TransformV2::new( - Key::Account(account_hash), + Key::Account(pre_migration_account_hash), TransformKindV2::Write(StoredValue::Account(Account::new( - account_hash, + pre_migration_account_hash, iter::once((TEST_DICT_NAME.to_owned(), Key::URef(dict_seed_uref))) .collect::>() .into(), main_purse, - AssociatedKeys::default(), - ActionThresholds::default(), + Default::default(), + Default::default(), ))), )); + effects.push(TransformV2::new( + Key::Account(post_migration_account_hash), + TransformKindV2::Write(StoredValue::CLValue( + CLValue::from_t(Key::AddressableEntity(entity_addr)).expect("should create CLValue"), + )), + )); + effects.push(TransformV2::new( + dict_key, + TransformKindV2::Write(StoredValue::CLValue(CLValue::from_t(dict_value).unwrap())), + )); effects.push(TransformV2::new( Key::NamedKey( NamedKeyAddr::new_from_string(entity_addr, TEST_DICT_NAME.to_owned()) @@ -233,19 +255,104 @@ fn test_effects(rng: &mut TestRng) -> TestEffects { CLValue::from_t(U512::one()).expect("should create CLValue"), )), )); + + effects.push(TransformV2::new( + Key::Hash(pre_migration_contract_package_hash.value()), + TransformKindV2::Write(StoredValue::ContractPackage(ContractPackage::new( + package_access_key, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ))), + )); + effects.push(TransformV2::new( + Key::Hash(post_migration_contract_package_hash.value()), + TransformKindV2::Write(StoredValue::CLValue( + CLValue::from_t((Key::Package(package_addr), package_access_key)) + .expect("should create CLValue"), + )), + )); + + effects.push(TransformV2::new( + Key::Hash(pre_migration_contract_hash.value()), + TransformKindV2::Write(StoredValue::Contract(Contract::new( + pre_migration_contract_package_hash, + wasm_hash, + Default::default(), + Default::default(), + ProtocolVersion::V2_0_0, + ))), + )); + effects.push(TransformV2::new( + Key::Hash(post_migration_contract_hash.value()), + TransformKindV2::Write(StoredValue::CLValue( + CLValue::from_t(Key::AddressableEntity(entity_addr)).expect("should create CLValue"), + )), + )); + + effects.push(TransformV2::new( + Key::Hash(wasm_hash.value()), + TransformKindV2::Write(StoredValue::ContractWasm(ContractWasm::new( + rng.random_vec(10..100), + ))), + )); + + effects.push(TransformV2::new( + Key::Package(package_addr), + TransformKindV2::Write(StoredValue::Package(Package::new( + EntityVersions::default(), + Default::default(), + Default::default(), + Default::default(), + ))), + )); + effects.push(TransformV2::new( + Key::AddressableEntity(entity_addr), + TransformKindV2::Write(StoredValue::AddressableEntity(AddressableEntity::new( + PackageHash::new(package_addr), + entity_bytecode_hash, + ProtocolVersion::V2_0_0, + main_purse, + AssociatedKeys::default(), + ActionThresholds::default(), + MessageTopics::default(), + EntityKind::SmartContract(TransactionRuntime::VmCasperV1), + ))), + )); + effects.push(TransformV2::new( + Key::ByteCode(ByteCodeAddr::new_wasm_addr(entity_bytecode_hash.value())), + TransformKindV2::Write(StoredValue::ByteCode(ByteCode::new( + ByteCodeKind::V1CasperWasm, + rng.random_vec(10..100), + ))), + )); + TestEffects { effects, - test_account_hash: account_hash, - test_entity_addr: entity_addr, - test_dict_seed_uref: dict_seed_uref, + pre_migration_account_hash, + post_migration_account_hash, + pre_migration_contract_package_hash, + post_migration_contract_package_hash, + pre_migration_contract_hash, + post_migration_contract_hash, + package_addr, + entity_addr, + dict_seed_uref, } } struct TestEffects { effects: Effects, - test_account_hash: AccountHash, - test_entity_addr: EntityAddr, - test_dict_seed_uref: URef, + pre_migration_account_hash: AccountHash, + post_migration_account_hash: AccountHash, + pre_migration_contract_package_hash: ContractPackageHash, + post_migration_contract_package_hash: ContractPackageHash, + pre_migration_contract_hash: ContractHash, + post_migration_contract_hash: ContractHash, + package_addr: PackageAddr, + entity_addr: EntityAddr, + dict_seed_uref: URef, } struct TestCase { @@ -256,7 +363,7 @@ struct TestCase { fn validate_metadata( response: &BinaryResponse, - expected_payload_type: Option, + expected_payload_type: Option, ) -> bool { response.is_success() && response.returned_data_type_tag() @@ -278,7 +385,7 @@ where fn assert_response( response: &BinaryResponse, - payload_type: Option, + payload_type: Option, validator: F, ) -> bool where @@ -306,9 +413,7 @@ async fn binary_port_component_handles_all_requests() { highest_block, secret_signing_key, state_root_hash, - test_account_hash, - test_entity_addr, - test_dict_seed_uref, + effects, era_one_validator, }, ), @@ -337,34 +442,34 @@ async fn binary_port_component_handles_all_requests() { get_trie(state_root_hash), get_dictionary_item_by_addr( state_root_hash, - *Key::dictionary(test_dict_seed_uref, TEST_DICT_ITEM_KEY.as_bytes()) + *Key::dictionary(effects.dict_seed_uref, TEST_DICT_ITEM_KEY.as_bytes()) .as_dictionary() .unwrap(), ), get_dictionary_item_by_seed_uref( state_root_hash, - test_dict_seed_uref, + effects.dict_seed_uref, TEST_DICT_ITEM_KEY.to_owned(), ), get_dictionary_item_by_legacy_named_key( state_root_hash, - test_account_hash, + effects.pre_migration_account_hash, TEST_DICT_NAME.to_owned(), TEST_DICT_ITEM_KEY.to_owned(), ), get_dictionary_item_by_named_key( state_root_hash, - test_entity_addr, + effects.entity_addr, TEST_DICT_NAME.to_owned(), TEST_DICT_ITEM_KEY.to_owned(), ), try_spec_exec_invalid(&mut rng), try_accept_transaction_invalid(&mut rng), try_accept_transaction(&secret_signing_key), - get_balance(state_root_hash, test_account_hash), + get_balance(state_root_hash, effects.pre_migration_account_hash), get_balance_account_not_found(state_root_hash), get_balance_purse_uref_not_found(state_root_hash), - get_named_keys_by_prefix(state_root_hash, test_entity_addr), + get_named_keys_by_prefix(state_root_hash, effects.entity_addr), get_reward( Some(EraIdentifier::Era(ERA_ONE)), era_one_validator.clone(), @@ -376,6 +481,18 @@ async fn binary_port_component_handles_all_requests() { None, ), get_protocol_version(protocol_version), + get_entity(state_root_hash, effects.entity_addr), + get_entity_without_bytecode(state_root_hash, effects.entity_addr), + get_entity_pre_migration_account(state_root_hash, effects.pre_migration_account_hash), + get_entity_post_migration_account(state_root_hash, effects.post_migration_account_hash), + get_entity_pre_migration_contract(state_root_hash, effects.pre_migration_contract_hash), + get_entity_post_migration_contract(state_root_hash, effects.post_migration_contract_hash), + get_package(state_root_hash, effects.package_addr), + get_package_pre_migration(state_root_hash, effects.pre_migration_contract_package_hash), + get_package_post_migration( + state_root_hash, + effects.post_migration_contract_package_hash, + ), ]; for ( @@ -446,7 +563,7 @@ fn block_header_info(hash: BlockHash) -> TestCase { .expect("should convert"), ), asserter: Box::new(move |response| { - assert_response::(response, Some(PayloadType::BlockHeader), |header| { + assert_response::(response, Some(ResponseType::BlockHeader), |header| { header.block_hash() == hash }) }), @@ -462,7 +579,7 @@ fn signed_block_info(hash: BlockHash) -> TestCase { .expect("should convert"), ), asserter: Box::new(move |response| { - assert_response::(response, Some(PayloadType::SignedBlock), |header| { + assert_response::(response, Some(ResponseType::SignedBlock), |header| { *header.block().hash() == hash }) }), @@ -477,7 +594,7 @@ fn peers() -> TestCase { key: vec![], }), asserter: Box::new(|response| { - assert_response::(response, Some(PayloadType::Peers), |peers| { + assert_response::(response, Some(ResponseType::Peers), |peers| { !peers.into_inner().is_empty() }) }), @@ -492,7 +609,7 @@ fn uptime() -> TestCase { key: vec![], }), asserter: Box::new(|response| { - assert_response::(response, Some(PayloadType::Uptime), |uptime| { + assert_response::(response, Some(ResponseType::Uptime), |uptime| { uptime.into_inner() > 0 }) }), @@ -509,7 +626,7 @@ fn last_progress() -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::LastProgress), + Some(ResponseType::LastProgress), |last_progress| last_progress.into_inner().millis() > 0, ) }), @@ -526,7 +643,7 @@ fn reactor_state() -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::ReactorState), + Some(ResponseType::ReactorState), |reactor_state| matches!(reactor_state.into_inner().as_str(), "Validate"), ) }), @@ -543,7 +660,7 @@ fn network_name() -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::NetworkName), + Some(ResponseType::NetworkName), |network_name| &network_name.into_inner() == "casper-example", ) }), @@ -560,7 +677,7 @@ fn consensus_validator_changes() -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::ConsensusValidatorChanges), + Some(ResponseType::ConsensusValidatorChanges), |cvc| cvc.into_inner().is_empty(), ) }), @@ -577,7 +694,7 @@ fn block_synchronizer_status() -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::BlockSynchronizerStatus), + Some(ResponseType::BlockSynchronizerStatus), |bss| bss.historical().is_none() && bss.forward().is_none(), ) }), @@ -594,7 +711,7 @@ fn available_block_range(expected_height: u64) -> TestCase { asserter: Box::new(move |response| { assert_response::( response, - Some(PayloadType::AvailableBlockRange), + Some(ResponseType::AvailableBlockRange), |abr| abr.low() == 0 && abr.high() >= expected_height, ) }), @@ -622,7 +739,7 @@ fn consensus_status() -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::ConsensusStatus), + Some(ResponseType::ConsensusStatus), |_| true, ) }), @@ -639,7 +756,7 @@ fn chainspec_raw_bytes(network_chainspec_raw_bytes: ChainspecRawBytes) -> TestCa asserter: Box::new(move |response| { assert_response::( response, - Some(PayloadType::ChainspecRawBytes), + Some(ResponseType::ChainspecRawBytes), |crb| crb == network_chainspec_raw_bytes, ) }), @@ -654,7 +771,7 @@ fn latest_switch_block_header() -> TestCase { key: vec![], }), asserter: Box::new(move |response| { - assert_response::(response, Some(PayloadType::BlockHeader), |header| { + assert_response::(response, Some(ResponseType::BlockHeader), |header| { header.is_switch_block() }) }), @@ -671,7 +788,7 @@ fn node_status(expected_version: ProtocolVersion) -> TestCase { asserter: Box::new(move |response| { assert_response::( response, - Some(PayloadType::NodeStatus), + Some(ResponseType::NodeStatus), |node_status| { node_status.protocol_version == expected_version && !node_status.peers.into_inner().is_empty() @@ -696,7 +813,7 @@ fn get_block_header(expected: BlockHeader) -> TestCase { key: expected.block_hash().to_bytes().unwrap(), }), asserter: Box::new(move |response| { - assert_response::(response, Some(PayloadType::BlockHeader), |header| { + assert_response::(response, Some(ResponseType::BlockHeader), |header| { header == expected }) }), @@ -711,7 +828,7 @@ fn get_block_transfers(expected: BlockHeader) -> TestCase { key: expected.block_hash().to_bytes().unwrap(), }), asserter: Box::new(move |response| { - validate_metadata(response, Some(PayloadType::Transfers)) + validate_metadata(response, Some(ResponseType::Transfers)) && bincode::deserialize::>(response.payload()).is_ok() }), } @@ -728,7 +845,7 @@ fn get_era_summary(state_root_hash: Digest) -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::GlobalStateQueryResult), + Some(ResponseType::GlobalStateQueryResult), |res| { let (value, _) = res.into_inner(); matches!(value, StoredValue::EraInfo(_)) @@ -748,7 +865,7 @@ fn get_all_bids(state_root_hash: Digest) -> TestCase { asserter: Box::new(|response| { assert_response::, _>( response, - Some(PayloadType::StoredValues), + Some(ResponseType::StoredValues), |res| res.iter().all(|v| matches!(v, StoredValue::BidKind(_))), ) }), @@ -764,7 +881,7 @@ fn get_trie(digest: Digest) -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::GetTrieFullResult), + Some(ResponseType::GetTrieFullResult), |res| res.into_inner().is_some(), ) }), @@ -783,7 +900,7 @@ fn get_dictionary_item_by_addr(state_root_hash: Digest, addr: DictionaryAddr) -> asserter: Box::new(move |response| { assert_response::( response, - Some(PayloadType::DictionaryQueryResult), + Some(ResponseType::DictionaryQueryResult), |res| { matches!( res.into_inner(), @@ -814,7 +931,7 @@ fn get_dictionary_item_by_seed_uref( asserter: Box::new(move |response| { assert_response::( response, - Some(PayloadType::DictionaryQueryResult), + Some(ResponseType::DictionaryQueryResult), |res| { let expected_key = Key::dictionary(seed_uref, dictionary_item_key.as_bytes()); matches!( @@ -848,7 +965,7 @@ fn get_dictionary_item_by_legacy_named_key( asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::DictionaryQueryResult), + Some(ResponseType::DictionaryQueryResult), |res| matches!(res.into_inner(),(_, res) if res.value().as_cl_value().is_some()), ) }), @@ -876,7 +993,7 @@ fn get_dictionary_item_by_named_key( asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::DictionaryQueryResult), + Some(ResponseType::DictionaryQueryResult), |res| matches!(res.into_inner(),(_, res) if res.value().as_cl_value().is_some()), ) }), @@ -893,7 +1010,7 @@ fn get_balance(state_root_hash: Digest, account_hash: AccountHash) -> TestCase { asserter: Box::new(|response| { assert_response::( response, - Some(PayloadType::BalanceResponse), + Some(ResponseType::BalanceResponse), |res| res.available_balance == U512::one(), ) }), @@ -934,7 +1051,7 @@ fn get_named_keys_by_prefix(state_root_hash: Digest, entity_addr: EntityAddr) -> asserter: Box::new(|response| { assert_response::, _>( response, - Some(PayloadType::StoredValues), + Some(ResponseType::StoredValues), |res| res.iter().all(|v| matches!(v, StoredValue::NamedKey(_))), ) }), @@ -959,7 +1076,7 @@ fn get_reward( key: key.to_bytes().expect("should serialize key"), }), asserter: Box::new(move |response| { - assert_response::(response, Some(PayloadType::Reward), |reward| { + assert_response::(response, Some(ResponseType::Reward), |reward| { // test fixture sets delegation rate to 0 reward.amount() > U512::zero() && reward.delegation_rate() == 0 }) @@ -979,13 +1096,235 @@ fn get_protocol_version(expected: ProtocolVersion) -> TestCase { asserter: Box::new(move |response| { assert_response::( response, - Some(PayloadType::ProtocolVersion), + Some(ResponseType::ProtocolVersion), |version| expected == version, ) }), } } +fn get_entity(state_root_hash: Digest, entity_addr: EntityAddr) -> TestCase { + let key = InformationRequest::Entity { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: EntityIdentifier::EntityAddr(entity_addr), + include_bytecode: true, + }; + + TestCase { + name: "get_entity", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(|response| { + assert_response::( + response, + Some(ResponseType::AddressableEntityInformation), + |res| res.bytecode().is_some(), + ) + }), + } +} + +fn get_entity_without_bytecode(state_root_hash: Digest, entity_addr: EntityAddr) -> TestCase { + let key = InformationRequest::Entity { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: EntityIdentifier::EntityAddr(entity_addr), + include_bytecode: false, + }; + + TestCase { + name: "get_entity_without_bytecode", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(|response| { + assert_response::( + response, + Some(ResponseType::AddressableEntityInformation), + |res| res.bytecode().is_none(), + ) + }), + } +} + +fn get_entity_pre_migration_account( + state_root_hash: Digest, + account_hash: AccountHash, +) -> TestCase { + let key = InformationRequest::Entity { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: EntityIdentifier::AccountHash(account_hash), + include_bytecode: false, + }; + + TestCase { + name: "get_entity_pre_migration_account", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(move |response| { + assert_response::( + response, + Some(ResponseType::AccountInformation), + |res| res.account().account_hash() == account_hash, + ) + }), + } +} + +fn get_entity_post_migration_account( + state_root_hash: Digest, + account_hash: AccountHash, +) -> TestCase { + let key = InformationRequest::Entity { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: EntityIdentifier::AccountHash(account_hash), + include_bytecode: false, + }; + + TestCase { + name: "get_entity_post_migration_account", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(move |response| { + assert_response::( + response, + Some(ResponseType::AddressableEntityInformation), + |_| true, + ) + }), + } +} + +fn get_entity_pre_migration_contract( + state_root_hash: Digest, + contract_hash: ContractHash, +) -> TestCase { + let key = InformationRequest::Entity { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: EntityIdentifier::ContractHash(contract_hash), + include_bytecode: true, + }; + + TestCase { + name: "get_entity_pre_migration_contract", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(move |response| { + assert_response::( + response, + Some(ResponseType::ContractInformation), + |res| res.wasm().is_some(), + ) + }), + } +} + +fn get_entity_post_migration_contract( + state_root_hash: Digest, + contract_hash: ContractHash, +) -> TestCase { + let key = InformationRequest::Entity { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: EntityIdentifier::ContractHash(contract_hash), + include_bytecode: true, + }; + + TestCase { + name: "get_entity_post_migration_contract", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(move |response| { + assert_response::( + response, + Some(ResponseType::AddressableEntityInformation), + |res| res.bytecode().is_some(), + ) + }), + } +} + +fn get_package(state_root_hash: Digest, package_addr: PackageAddr) -> TestCase { + let key = InformationRequest::Package { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: PackageIdentifier::PackageAddr(package_addr), + }; + + TestCase { + name: "get_package", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(move |response| { + assert_response::, _>( + response, + Some(ResponseType::PackageWithProof), + |_| true, + ) + }), + } +} + +fn get_package_pre_migration( + state_root_hash: Digest, + contract_package_hash: ContractPackageHash, +) -> TestCase { + let key = InformationRequest::Package { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: PackageIdentifier::ContractPackageHash(contract_package_hash), + }; + + TestCase { + name: "get_package_pre_migration", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(move |response| { + assert_response::, _>( + response, + Some(ResponseType::ContractPackageWithProof), + |_| true, + ) + }), + } +} + +fn get_package_post_migration( + state_root_hash: Digest, + contract_package_hash: ContractPackageHash, +) -> TestCase { + let key = InformationRequest::Package { + state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)), + identifier: PackageIdentifier::ContractPackageHash(contract_package_hash), + }; + + TestCase { + name: "get_package_post_migration", + request: BinaryRequest::Get(GetRequest::Information { + info_type_tag: key.tag().into(), + key: key.to_bytes().expect("should serialize key"), + }), + asserter: Box::new(move |response| { + assert_response::, _>( + response, + Some(ResponseType::PackageWithProof), + |_| true, + ) + }), + } +} + fn try_accept_transaction(key: &SecretKey) -> TestCase { let transaction = Transaction::V1( TransactionV1Builder::new_targeting_invocable_entity_via_alias("Test", "call") diff --git a/types/src/contract_wasm.rs b/types/src/contract_wasm.rs index ac9eb24a48..cf23f70928 100644 --- a/types/src/contract_wasm.rs +++ b/types/src/contract_wasm.rs @@ -247,7 +247,8 @@ pub struct ContractWasm { } impl ContractWasm { - #[cfg(test)] + /// Creates a new `ContractWasm`. + #[cfg(any(feature = "testing", test))] pub fn new(bytes: Vec) -> Self { Self { bytes: bytes.into(), diff --git a/types/src/lib.rs b/types/src/lib.rs index 7c2ccf33a5..2607e9f45b 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -149,7 +149,7 @@ pub use chainspec::{ DEFAULT_MUL_COST, DEFAULT_NEW_DICTIONARY_COST, DEFAULT_NOP_COST, DEFAULT_STORE_COST, DEFAULT_TRANSFER_COST, DEFAULT_UNREACHABLE_COST, DEFAULT_WASM_MAX_MEMORY, }; -pub use contract_wasm::ContractWasm; +pub use contract_wasm::{ContractWasm, ContractWasmHash}; #[doc(inline)] pub use contracts::Contract; pub use crypto::*;