Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
4628: Expose dictionary item key in lookup response r=jacek-casper a=jacek-casper

The current RPC response contains the dictionary item key as well as the value, so this addition is needed to maintain compatibility.

Co-authored-by: Jacek Malec <[email protected]>
  • Loading branch information
casperlabs-bors-ng[bot] and jacek-casper authored Apr 2, 2024
2 parents cbe0304 + 766c005 commit a9afd2e
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 47 deletions.
51 changes: 28 additions & 23 deletions node/src/components/binary_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ use casper_types::{
addressable_entity::NamedKeyAddr,
binary_port::{
self, BinaryRequest, BinaryRequestHeader, BinaryRequestTag, BinaryResponse,
BinaryResponseAndRequest, DbRawBytesSpec, DictionaryItemIdentifier, GetRequest,
GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest, InformationRequest,
InformationRequestTag, NodeStatus, ReactorStateName, RecordId,
BinaryResponseAndRequest, DbRawBytesSpec, DictionaryItemIdentifier, DictionaryQueryResult,
GetRequest, GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest,
InformationRequest, InformationRequestTag, NodeStatus, ReactorStateName, RecordId,
TransactionWithExecutionInfo,
},
bytesrepr::{self, FromBytes, ToBytes},
Expand Down Expand Up @@ -514,22 +514,16 @@ where
seed_uref,
dictionary_item_key,
} => {
get_global_state_item(
effect_builder,
state_root_hash,
Key::dictionary(seed_uref, dictionary_item_key.as_bytes()),
vec![],
)
.await
let key = Key::dictionary(seed_uref, dictionary_item_key.as_bytes());
get_global_state_item(effect_builder, state_root_hash, key, vec![])
.await
.map(|maybe_res| maybe_res.map(|res| DictionaryQueryResult::new(key, res)))
}
DictionaryItemIdentifier::DictionaryItem(addr) => {
get_global_state_item(
effect_builder,
state_root_hash,
Key::Dictionary(addr),
vec![],
)
.await
let key = Key::Dictionary(addr);
get_global_state_item(effect_builder, state_root_hash, key, vec![])
.await
.map(|maybe_res| maybe_res.map(|res| DictionaryQueryResult::new(key, res)))
}
};
match result {
Expand All @@ -547,7 +541,7 @@ async fn get_dictionary_item_by_legacy_named_key<REv>(
entity_key: Key,
dictionary_name: String,
dictionary_item_key: String,
) -> Result<Option<GlobalStateQueryResult>, binary_port::ErrorCode>
) -> Result<Option<DictionaryQueryResult>, binary_port::ErrorCode>
where
REv: From<Event> + From<ContractRuntimeRequest> + From<StorageRequest>,
{
Expand All @@ -564,8 +558,14 @@ where
let Some(uref) = named_keys.get(&dictionary_name).and_then(Key::as_uref) else {
return Err(binary_port::ErrorCode::DictionaryURefNotFound);
};
let dictionary_key = Key::dictionary(*uref, dictionary_item_key.as_bytes());
get_global_state_item(effect_builder, state_root_hash, dictionary_key, vec![]).await
let key = Key::dictionary(*uref, dictionary_item_key.as_bytes());
let Some(query_result) =
get_global_state_item(effect_builder, state_root_hash, key, vec![]).await?
else {
return Ok(None);
};

Ok(Some(DictionaryQueryResult::new(key, query_result)))
}
QueryResult::RootNotFound | QueryResult::ValueNotFound(_) => {
Err(binary_port::ErrorCode::DictionaryURefNotFound)
Expand All @@ -580,7 +580,7 @@ async fn get_dictionary_item_by_named_key<REv>(
entity_addr: EntityAddr,
dictionary_name: String,
dictionary_item_key: String,
) -> Result<Option<GlobalStateQueryResult>, binary_port::ErrorCode>
) -> Result<Option<DictionaryQueryResult>, binary_port::ErrorCode>
where
REv: From<Event> + From<ContractRuntimeRequest> + From<StorageRequest>,
{
Expand All @@ -596,8 +596,13 @@ where
let Ok(Key::URef(uref)) = key_val.get_key() else {
return Err(binary_port::ErrorCode::DictionaryURefNotFound);
};
let dictionary_key = Key::dictionary(uref, dictionary_item_key.as_bytes());
get_global_state_item(effect_builder, state_root_hash, dictionary_key, vec![]).await
let key = Key::dictionary(uref, dictionary_item_key.as_bytes());
let Some(query_result) =
get_global_state_item(effect_builder, state_root_hash, key, vec![]).await?
else {
return Ok(None);
};
Ok(Some(DictionaryQueryResult::new(key, query_result)))
}
QueryResult::RootNotFound | QueryResult::ValueNotFound(_) => {
Err(binary_port::ErrorCode::DictionaryURefNotFound)
Expand Down
49 changes: 30 additions & 19 deletions node/src/reactor/main_reactor/tests/binary_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use casper_types::{
addressable_entity::{NamedKeyAddr, NamedKeyValue},
binary_port::{
BinaryRequest, BinaryRequestHeader, BinaryResponse, BinaryResponseAndRequest,
ConsensusStatus, ConsensusValidatorChanges, DictionaryItemIdentifier, ErrorCode,
GetRequest, GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest,
InformationRequest, InformationRequestTag, LastProgress, NetworkName, NodeStatus,
PayloadType, ReactorStateName, RecordId, Uptime,
ConsensusStatus, ConsensusValidatorChanges, DictionaryItemIdentifier,
DictionaryQueryResult, ErrorCode, GetRequest, GetTrieFullResult, GlobalStateQueryResult,
GlobalStateRequest, InformationRequest, InformationRequestTag, LastProgress, NetworkName,
NodeStatus, PayloadType, ReactorStateName, RecordId, Uptime,
},
bytesrepr::{FromBytes, ToBytes},
execution::{Effects, TransformKindV2, TransformV2},
Expand Down Expand Up @@ -741,11 +741,16 @@ fn get_dictionary_item_by_addr(state_root_hash: Digest, addr: DictionaryAddr) ->
state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)),
identifier: DictionaryItemIdentifier::DictionaryItem(addr),
})),
asserter: Box::new(|response| {
assert_response::<GlobalStateQueryResult, _>(
asserter: Box::new(move |response| {
assert_response::<DictionaryQueryResult, _>(
response,
Some(PayloadType::GlobalStateQueryResult),
|res| matches!(res.into_inner(), (StoredValue::CLValue(_), _)),
Some(PayloadType::DictionaryQueryResult),
|res| {
matches!(
res.into_inner(),
(key, res) if key == Key::Dictionary(addr) && res.value().as_cl_value().is_some()
)
},
)
}),
}
Expand All @@ -762,14 +767,20 @@ fn get_dictionary_item_by_seed_uref(
state_identifier: Some(GlobalStateIdentifier::StateRootHash(state_root_hash)),
identifier: DictionaryItemIdentifier::URef {
seed_uref,
dictionary_item_key,
dictionary_item_key: dictionary_item_key.clone(),
},
})),
asserter: Box::new(|response| {
assert_response::<GlobalStateQueryResult, _>(
asserter: Box::new(move |response| {
assert_response::<DictionaryQueryResult, _>(
response,
Some(PayloadType::GlobalStateQueryResult),
|res| matches!(res.into_inner(), (StoredValue::CLValue(_), _)),
Some(PayloadType::DictionaryQueryResult),
|res| {
let expected_key = Key::dictionary(seed_uref, dictionary_item_key.as_bytes());
matches!(
res.into_inner(),
(key, res) if key == expected_key && res.value().as_cl_value().is_some()
)
},
)
}),
}
Expand All @@ -792,10 +803,10 @@ fn get_dictionary_item_by_legacy_named_key(
},
})),
asserter: Box::new(|response| {
assert_response::<GlobalStateQueryResult, _>(
assert_response::<DictionaryQueryResult, _>(
response,
Some(PayloadType::GlobalStateQueryResult),
|res| matches!(res.into_inner(), (StoredValue::CLValue(_), _)),
Some(PayloadType::DictionaryQueryResult),
|res| matches!(res.into_inner(),(_, res) if res.value().as_cl_value().is_some()),
)
}),
}
Expand All @@ -818,10 +829,10 @@ fn get_dictionary_item_by_named_key(
},
})),
asserter: Box::new(|response| {
assert_response::<GlobalStateQueryResult, _>(
assert_response::<DictionaryQueryResult, _>(
response,
Some(PayloadType::GlobalStateQueryResult),
|res| matches!(res.into_inner(), (StoredValue::CLValue(_), _)),
Some(PayloadType::DictionaryQueryResult),
|res| matches!(res.into_inner(),(_, res) if res.value().as_cl_value().is_some()),
)
}),
}
Expand Down
5 changes: 3 additions & 2 deletions types/src/binary_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ pub use payload_type::{PayloadEntity, PayloadType};
pub use record_id::RecordId;
pub use state_request::GlobalStateRequest;
pub use type_wrappers::{
ConsensusStatus, ConsensusValidatorChanges, GetTrieFullResult, LastProgress, NetworkName,
ReactorStateName, SpeculativeExecutionResult, TransactionWithExecutionInfo, Uptime,
ConsensusStatus, ConsensusValidatorChanges, DictionaryQueryResult, GetTrieFullResult,
LastProgress, NetworkName, ReactorStateName, SpeculativeExecutionResult,
TransactionWithExecutionInfo, Uptime,
};

use alloc::vec::Vec;
Expand Down
5 changes: 5 additions & 0 deletions types/src/binary_port/global_state_query_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ impl GlobalStateQueryResult {
}
}

/// Returns the stored value.
pub fn value(&self) -> &StoredValue {
&self.value
}

/// Returns the stored value and the merkle proof.
pub fn into_inner(self) -> (StoredValue, Vec<TrieMerkleProof<Key, StoredValue>>) {
(self.value, self.merkle_proof)
Expand Down
15 changes: 14 additions & 1 deletion types/src/binary_port/payload_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use super::{
ConsensusStatus, ConsensusValidatorChanges, GetTrieFullResult, LastProgress, NetworkName,
ReactorStateName, SpeculativeExecutionResult,
},
TransactionWithExecutionInfo, Uptime,
DictionaryQueryResult, TransactionWithExecutionInfo, Uptime,
};

/// A type of the payload being returned in a binary response.
Expand Down Expand Up @@ -107,6 +107,8 @@ pub enum PayloadType {
GetTrieFullResult,
/// Node status.
NodeStatus,
/// Result of querying for a dictionary item.
DictionaryQueryResult,
}

impl PayloadType {
Expand Down Expand Up @@ -192,6 +194,9 @@ impl TryFrom<u8> for PayloadType {
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)
}
_ => Err(()),
}
}
Expand Down Expand Up @@ -242,6 +247,7 @@ impl fmt::Display for PayloadType {
PayloadType::StoredValues => write!(f, "StoredValues"),
PayloadType::GetTrieFullResult => write!(f, "GetTrieFullResult"),
PayloadType::NodeStatus => write!(f, "NodeStatus"),
PayloadType::DictionaryQueryResult => write!(f, "DictionaryQueryResult"),
}
}
}
Expand Down Expand Up @@ -280,6 +286,7 @@ const GLOBAL_STATE_QUERY_RESULT_TAG: u8 = 30;
const STORED_VALUES_TAG: u8 = 31;
const GET_TRIE_FULL_RESULT_TAG: u8 = 32;
const NODE_STATUS_TAG: u8 = 33;
const DICTIONARY_QUERY_RESULT_TAG: u8 = 34;

impl ToBytes for PayloadType {
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
Expand Down Expand Up @@ -324,6 +331,7 @@ impl ToBytes for PayloadType {
PayloadType::StoredValues => STORED_VALUES_TAG,
PayloadType::GetTrieFullResult => GET_TRIE_FULL_RESULT_TAG,
PayloadType::NodeStatus => NODE_STATUS_TAG,
PayloadType::DictionaryQueryResult => DICTIONARY_QUERY_RESULT_TAG,
}
.write_bytes(writer)
}
Expand Down Expand Up @@ -371,6 +379,7 @@ impl FromBytes for PayloadType {
STORED_VALUES_TAG => PayloadType::StoredValues,
GET_TRIE_FULL_RESULT_TAG => PayloadType::GetTrieFullResult,
NODE_STATUS_TAG => PayloadType::NodeStatus,
DICTIONARY_QUERY_RESULT_TAG => PayloadType::DictionaryQueryResult,
_ => return Err(bytesrepr::Error::Formatting),
};
Ok((record_id, remainder))
Expand Down Expand Up @@ -456,6 +465,10 @@ impl PayloadEntity for GlobalStateQueryResult {
const PAYLOAD_TYPE: PayloadType = PayloadType::GlobalStateQueryResult;
}

impl PayloadEntity for DictionaryQueryResult {
const PAYLOAD_TYPE: PayloadType = PayloadType::DictionaryQueryResult;
}

impl PayloadEntity for Vec<StoredValue> {
const PAYLOAD_TYPE: PayloadType = PayloadType::StoredValues;
}
Expand Down
76 changes: 74 additions & 2 deletions types/src/binary_port/type_wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ use crate::{
bytesrepr::{self, Bytes, FromBytes, ToBytes},
contract_messages::Messages,
execution::ExecutionResultV2,
EraId, ExecutionInfo, PublicKey, TimeDiff, Timestamp, Transaction, ValidatorChange,
EraId, ExecutionInfo, Key, PublicKey, TimeDiff, Timestamp, Transaction, ValidatorChange,
};

use super::GlobalStateQueryResult;

// `bytesrepr` implementations for type wrappers are repetitive, hence this macro helper. We should
// get rid of this after we introduce the proper "bytesrepr-derive" proc macro.
macro_rules! impl_bytesrepr_for_type_wrapper {
Expand Down Expand Up @@ -298,6 +300,51 @@ impl FromBytes for TransactionWithExecutionInfo {
}
}

/// A query result for a dictionary item, contains the dictionary item key and a global state query
/// result.
#[derive(Debug, Clone, PartialEq)]
pub struct DictionaryQueryResult {
key: Key,
query_result: GlobalStateQueryResult,
}

impl DictionaryQueryResult {
/// Constructs new dictionary query result.
pub fn new(key: Key, query_result: GlobalStateQueryResult) -> Self {
Self { key, query_result }
}

/// Converts `self` into the dictionary item key and global state query result.
pub fn into_inner(self) -> (Key, GlobalStateQueryResult) {
(self.key, self.query_result)
}
}

impl ToBytes for DictionaryQueryResult {
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
let mut buffer = bytesrepr::allocate_buffer(self)?;
self.write_bytes(&mut buffer)?;
Ok(buffer)
}

fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
self.key.write_bytes(writer)?;
self.query_result.write_bytes(writer)
}

fn serialized_length(&self) -> usize {
self.key.serialized_length() + self.query_result.serialized_length()
}
}

impl FromBytes for DictionaryQueryResult {
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
let (key, remainder) = FromBytes::from_bytes(bytes)?;
let (query_result, remainder) = FromBytes::from_bytes(remainder)?;
Ok((DictionaryQueryResult::new(key, query_result), remainder))
}
}

impl_bytesrepr_for_type_wrapper!(Uptime);
impl_bytesrepr_for_type_wrapper!(ConsensusValidatorChanges);
impl_bytesrepr_for_type_wrapper!(NetworkName);
Expand All @@ -312,7 +359,7 @@ mod tests {
use rand::Rng;

use super::*;
use crate::testing::TestRng;
use crate::{execution::ExecutionResult, testing::TestRng, BlockHash, CLValue, StoredValue};

#[test]
fn uptime_roundtrip() {
Expand Down Expand Up @@ -375,4 +422,29 @@ mod tests {
Some(TimeDiff::from_millis(rng.gen())),
));
}

#[test]
fn transaction_with_execution_info_roundtrip() {
let rng = &mut TestRng::new();
bytesrepr::test_serialization_roundtrip(&TransactionWithExecutionInfo::new(
Transaction::random(rng),
rng.gen::<bool>().then(|| ExecutionInfo {
block_hash: BlockHash::random(rng),
block_height: rng.gen(),
execution_result: rng.gen::<bool>().then(|| ExecutionResult::random(rng)),
}),
));
}

#[test]
fn dictionary_query_result_roundtrip() {
let rng = &mut TestRng::new();
bytesrepr::test_serialization_roundtrip(&DictionaryQueryResult::new(
Key::Account(rng.gen()),
GlobalStateQueryResult::new(
StoredValue::CLValue(CLValue::from_t(rng.gen::<i32>()).unwrap()),
vec![],
),
));
}
}

0 comments on commit a9afd2e

Please sign in to comment.