Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get entity get package #341

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading