Skip to content

Commit

Permalink
Get entity get package (#341)
Browse files Browse the repository at this point in the history
* Add package and amend get entity endpoint

* Repointing node dependencies

---------

Co-authored-by: Jacek Malec <[email protected]>
Co-authored-by: Jakub Zajkowski <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2024
1 parent 83bdb66 commit df5d9da
Show file tree
Hide file tree
Showing 9 changed files with 1,212 additions and 505 deletions.
619 changes: 342 additions & 277 deletions Cargo.lock

Large diffs are not rendered by default.

305 changes: 287 additions & 18 deletions resources/test/rpc_schema.json

Large diffs are not rendered by default.

15 changes: 11 additions & 4 deletions resources/test/speculative_rpc_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,13 @@
"name": "transaction",
"value": {
"Version1": {
"hash": "f5582cb81a5abda63ebaa4edb3b05210ecbd63ffb8dd17bfbeb3b867f4014468",
"serialization_version": 1,
"hash": "df4f6e95afd24c3bdac68862cfd888fea65912f0f3e3de9c42b24cee79b7a581",
"header": {
"chain_name": "casper-example",
"timestamp": "2020-11-17T00:39:24.072Z",
"ttl": "1h",
"body_hash": "aa24833ffbf31d62c8c8c4265349e7c09cd71952fcbce6f7b12daf5e340bf2cc",
"body_hash": "7bf1a4f736a9cbb2b692b74522d981213c3a5463d0095ded40d1454cf1b779e1",
"pricing_mode": {
"Fixed": {
"gas_price_tolerance": 5
Expand Down Expand Up @@ -236,7 +237,7 @@
"approvals": [
{
"signer": "01d9bf2148748a85c89da5aad8ee0b0fc2d105fd39d41a4c796536354f0ae2900c",
"signature": "0137d3f468d8f8a6e63f4110d79be29b8c8428e9cd858a92049660e7851ae16a299640d1fc1c930ab6cb424f1a6eec0b194df74bede14f4af1b5133106f1280d0b"
"signature": "015b407723d54bdfd376d43776d9b92ea465d7ec2e0d41e28b5f646fc17400193bc4e075cab4e8943de09935e3aa96d0bbe456382c2274689b6847a35a94d07309"
}
]
}
Expand Down Expand Up @@ -3735,9 +3736,15 @@
"approvals",
"body",
"hash",
"header"
"header",
"serialization_version"
],
"properties": {
"serialization_version": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
"hash": {
"$ref": "#/components/schemas/TransactionV1Hash"
},
Expand Down
3 changes: 2 additions & 1 deletion rpc_sidecar/src/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use casper_json_rpc::{CorsOrigin, RequestHandlersBuilder};
use crate::{
rpcs::{
info::{GetPeers, GetReward, GetStatus, GetTransaction},
state::{GetAddressableEntity, QueryBalanceDetails},
state::{GetAddressableEntity, GetPackage, QueryBalanceDetails},
},
NodeClient,
};
Expand Down Expand Up @@ -50,6 +50,7 @@ pub async fn run(
GetBalance::register_as_handler(node.clone(), &mut handlers);
GetAccountInfo::register_as_handler(node.clone(), &mut handlers);
GetAddressableEntity::register_as_handler(node.clone(), &mut handlers);
GetPackage::register_as_handler(node.clone(), &mut handlers);
GetDeploy::register_as_handler(node.clone(), &mut handlers);
GetTransaction::register_as_handler(node.clone(), &mut handlers);
GetPeers::register_as_handler(node.clone(), &mut handlers);
Expand Down
80 changes: 71 additions & 9 deletions rpc_sidecar/src/node_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ use std::{
use tokio_util::codec::Framed;

use casper_binary_port::{
BalanceResponse, BinaryMessage, BinaryMessageCodec, BinaryRequest, BinaryResponse,
BinaryResponseAndRequest, ConsensusValidatorChanges, DictionaryItemIdentifier,
DictionaryQueryResult, EraIdentifier, ErrorCode, GetRequest, GetTrieFullResult,
GlobalStateQueryResult, GlobalStateRequest, InformationRequest, KeyPrefix, NodeStatus,
PayloadEntity, PurseIdentifier, RecordId, RewardResponse, SpeculativeExecutionResult,
TransactionWithExecutionInfo,
AccountInformation, AddressableEntityInformation, BalanceResponse, BinaryMessage,
BinaryMessageCodec, BinaryRequest, BinaryResponse, BinaryResponseAndRequest,
ConsensusValidatorChanges, ContractInformation, DictionaryItemIdentifier,
DictionaryQueryResult, EntityIdentifier, EraIdentifier, ErrorCode, GetRequest,
GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest, InformationRequest, KeyPrefix,
NodeStatus, PackageIdentifier, PayloadEntity, PurseIdentifier, RecordId, ResponseType,
RewardResponse, SpeculativeExecutionResult, TransactionWithExecutionInfo, ValueWithProof,
};
use casper_types::{
bytesrepr::{self, FromBytes, ToBytes},
contracts::ContractPackage,
AvailableBlockRange, BlockHash, BlockHeader, BlockIdentifier, ChainspecRawBytes, Digest,
GlobalStateIdentifier, Key, KeyTag, Peers, ProtocolVersion, PublicKey, SignedBlock,
GlobalStateIdentifier, Key, KeyTag, Package, Peers, ProtocolVersion, PublicKey, SignedBlock,
StoredValue, Transaction, TransactionHash, Transfer,
};
use std::{
Expand Down Expand Up @@ -266,6 +268,53 @@ pub trait NodeClient: Send + Sync {
.await?;
parse_response::<RewardResponse>(&resp.into())
}

async fn read_package(
&self,
state_identifier: Option<GlobalStateIdentifier>,
identifier: PackageIdentifier,
) -> Result<Option<PackageResponse>, Error> {
let get = InformationRequest::Package {
state_identifier,
identifier,
};
let resp = self.read_info(get).await?;
match resp.response().returned_data_type_tag() {
Some(type_tag) if type_tag == ResponseType::ContractPackageWithProof as u8 => Ok(
parse_response::<ValueWithProof<ContractPackage>>(&resp.into())?
.map(PackageResponse::ContractPackage),
),
_ => Ok(parse_response::<ValueWithProof<Package>>(&resp.into())?
.map(PackageResponse::Package)),
}
}

async fn read_entity(
&self,
state_identifier: Option<GlobalStateIdentifier>,
identifier: EntityIdentifier,
include_bytecode: bool,
) -> Result<Option<EntityResponse>, Error> {
let get = InformationRequest::Entity {
state_identifier,
identifier,
include_bytecode,
};
let resp = self.read_info(get).await?;
match resp.response().returned_data_type_tag() {
Some(type_tag) if type_tag == ResponseType::ContractInformation as u8 => {
Ok(parse_response::<ContractInformation>(&resp.into())?
.map(EntityResponse::Contract))
}
Some(type_tag) if type_tag == ResponseType::AccountInformation as u8 => Ok(
parse_response::<AccountInformation>(&resp.into())?.map(EntityResponse::Account),
),
_ => Ok(
parse_response::<AddressableEntityInformation>(&resp.into())?
.map(EntityResponse::Entity),
),
}
}
}

#[derive(Debug, thiserror::Error, PartialEq, Eq)]
Expand Down Expand Up @@ -968,6 +1017,19 @@ impl NodeClient for FramedNodeClient {
}
}

#[derive(Debug)]
pub enum EntityResponse {
Entity(AddressableEntityInformation),
Account(AccountInformation),
Contract(ContractInformation),
}

#[derive(Debug)]
pub enum PackageResponse {
Package(ValueWithProof<Package>),
ContractPackage(ValueWithProof<ContractPackage>),
}

fn validate_response(
resp: BinaryResponseAndRequest,
expected_id: u16,
Expand Down Expand Up @@ -1002,7 +1064,7 @@ where
return Err(Error::from_error_code(resp.error_code()));
}
match resp.returned_data_type_tag() {
Some(found) if found == u8::from(A::PAYLOAD_TYPE) => {
Some(found) if found == u8::from(A::RESPONSE_TYPE) => {
bytesrepr::deserialize_from_slice(resp.payload())
.map(Some)
.map_err(|err| Error::Deserialization(err.to_string()))
Expand All @@ -1023,7 +1085,7 @@ where
return Err(Error::from_error_code(resp.error_code()));
}
match resp.returned_data_type_tag() {
Some(found) if found == u8::from(A::PAYLOAD_TYPE) => bincode::deserialize(resp.payload())
Some(found) if found == u8::from(A::RESPONSE_TYPE) => bincode::deserialize(resp.payload())
.map(Some)
.map_err(|err| Error::Deserialization(err.to_string())),
Some(other) => Err(Error::UnexpectedVariantReceived(other)),
Expand Down
155 changes: 54 additions & 101 deletions rpc_sidecar/src/rpcs/common.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::collections::BTreeMap;

use casper_binary_port::{GlobalStateQueryResult, KeyPrefix};
use casper_binary_port::KeyPrefix;
use once_cell::sync::Lazy;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::rpcs::error::Error;
use casper_types::{
account::AccountHash, addressable_entity::NamedKeys, bytesrepr::ToBytes,
addressable_entity::NamedKeys, bytesrepr::ToBytes, contracts::ContractPackage,
global_state::TrieMerkleProof, Account, AddressableEntity, AvailableBlockRange, BlockHeader,
BlockIdentifier, EntityAddr, EntryPointValue, GlobalStateIdentifier, Key, SignedBlock,
StoredValue,
BlockIdentifier, ByteCode, Contract, ContractWasm, EntityAddr, EntryPointValue,
GlobalStateIdentifier, Key, Package, SignedBlock, StoredValue,
};

use crate::NodeClient;
Expand Down Expand Up @@ -43,9 +43,9 @@ pub enum ErrorData {
},
}

/// An addressable entity or a legacy account.
/// An addressable entity or a legacy account or contract.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum EntityOrAccount {
pub enum EntityWithBackwardCompat {
/// An addressable entity.
AddressableEntity {
/// The addressable entity.
Expand All @@ -54,9 +54,55 @@ pub enum EntityOrAccount {
named_keys: NamedKeys,
/// The entry points of the addressable entity.
entry_points: Vec<EntryPointValue>,
/// The bytecode of the addressable entity. Returned when `include_bytecode` is `true`.
bytecode: Option<ByteCodeWithProof>,
},
/// A legacy account.
LegacyAccount(Account),
/// An account.
Account(Account),
/// A contract.
Contract {
/// The contract.
contract: Contract,
/// The Wasm code of the contract. Returned when `include_bytecode` is `true`.
wasm: Option<ContractWasmWithProof>,
},
}

/// A package or a legacy contract package.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum PackageWithBackwardCompat {
/// A package.
Package(Package),
/// A contract package.
ContractPackage(ContractPackage),
}

/// Byte code of an entity with a proof.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ByteCodeWithProof {
code: ByteCode,
merkle_proof: String,
}

impl ByteCodeWithProof {
/// Creates a new `ByteCodeWithProof`.
pub fn new(code: ByteCode, merkle_proof: String) -> Self {
Self { code, merkle_proof }
}
}

/// Wasm code of a contract with a proof.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ContractWasmWithProof {
wasm: ContractWasm,
merkle_proof: String,
}

impl ContractWasmWithProof {
/// Creates a new `ContractWasmWithProof`.
pub fn new(wasm: ContractWasm, merkle_proof: String) -> Self {
Self { wasm, merkle_proof }
}
}

pub async fn get_signed_block(
Expand Down Expand Up @@ -118,87 +164,6 @@ pub async fn get_latest_switch_block_header(
}
}

pub async fn resolve_account_hash(
node_client: &dyn NodeClient,
account_hash: AccountHash,
state_identifier: Option<GlobalStateIdentifier>,
) -> Result<Option<SuccessfulQueryResult<EntityOrAccount>>, Error> {
let account_key = Key::Account(account_hash);
let Some((stored_value, account_merkle_proof)) = node_client
.query_global_state(state_identifier, account_key, vec![])
.await
.map_err(|err| Error::NodeRequest("account stored value", err))?
.map(GlobalStateQueryResult::into_inner)
else {
return Ok(None);
};

let (value, merkle_proof) = match stored_value {
StoredValue::Account(account) => (
EntityOrAccount::LegacyAccount(account),
account_merkle_proof,
),
StoredValue::CLValue(entity_key_as_clvalue) => {
let key: Key = entity_key_as_clvalue
.into_t()
.map_err(|_| Error::InvalidAddressableEntity)?;
let Some((value, merkle_proof)) = node_client
.query_global_state(state_identifier, key, vec![])
.await
.map_err(|err| Error::NodeRequest("account owning a purse", err))?
.map(GlobalStateQueryResult::into_inner)
else {
return Ok(None);
};
let (Key::AddressableEntity(entity_addr), StoredValue::AddressableEntity(entity)) =
(key, value)
else {
return Err(Error::InvalidAddressableEntity);
};
let named_keys =
get_entity_named_keys(node_client, entity_addr, state_identifier).await?;
let entry_points =
get_entity_entry_points(node_client, entity_addr, state_identifier).await?;
(
EntityOrAccount::AddressableEntity {
entity,
named_keys,
entry_points,
},
merkle_proof,
)
}
_ => return Err(Error::InvalidAccountInfo),
};
Ok(Some(SuccessfulQueryResult {
value,
merkle_proof,
}))
}

pub async fn resolve_entity_addr(
node_client: &dyn NodeClient,
entity_addr: EntityAddr,
state_identifier: Option<GlobalStateIdentifier>,
) -> Result<Option<SuccessfulQueryResult<AddressableEntity>>, Error> {
let entity_key = Key::AddressableEntity(entity_addr);
let Some((value, merkle_proof)) = node_client
.query_global_state(state_identifier, entity_key, vec![])
.await
.map_err(|err| Error::NodeRequest("entity stored value", err))?
.map(GlobalStateQueryResult::into_inner)
else {
return Ok(None);
};

Ok(Some(SuccessfulQueryResult {
value: value
.into_addressable_entity()
.ok_or(Error::InvalidAddressableEntity)?,
merkle_proof,
}))
}

pub async fn get_entity_named_keys(
node_client: &dyn NodeClient,
entity_addr: EntityAddr,
Expand Down Expand Up @@ -271,15 +236,3 @@ pub fn encode_proof(proof: &Vec<TrieMerkleProof<Key, StoredValue>>) -> Result<St
&proof.to_bytes().map_err(Error::BytesreprFailure)?,
))
}

#[derive(Debug)]
pub struct SuccessfulQueryResult<A> {
pub value: A,
pub merkle_proof: Vec<TrieMerkleProof<Key, StoredValue>>,
}

impl<A> SuccessfulQueryResult<A> {
pub fn into_inner(self) -> (A, Vec<TrieMerkleProof<Key, StoredValue>>) {
(self.value, self.merkle_proof)
}
}
3 changes: 2 additions & 1 deletion rpc_sidecar/src/rpcs/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use super::{
},
state::{
GetAccountInfo, GetAddressableEntity, GetAuctionInfo, GetBalance, GetDictionaryItem,
GetItem, QueryBalance, QueryBalanceDetails, QueryGlobalState,
GetItem, GetPackage, QueryBalance, QueryBalanceDetails, QueryGlobalState,
},
ApiVersion, NodeClient, RpcError, RpcWithOptionalParams, RpcWithParams, RpcWithoutParams,
CURRENT_API_VERSION,
Expand Down Expand Up @@ -77,6 +77,7 @@ pub(crate) static OPEN_RPC_SCHEMA: Lazy<OpenRpcSchema> = Lazy::new(|| {
schema.push_with_params::<GetAccountInfo>("returns an Account from the network");
schema
.push_with_params::<GetAddressableEntity>("returns an AddressableEntity from the network");
schema.push_with_params::<GetPackage>("returns a Package from the network");
schema.push_with_params::<GetDictionaryItem>("returns an item from a Dictionary");
schema.push_with_params::<QueryGlobalState>(
"a query to global state using either a Block hash or state root hash",
Expand Down
Loading

0 comments on commit df5d9da

Please sign in to comment.