From 440fc9309e214ae43d4c8beef24fbc997d8e61d8 Mon Sep 17 00:00:00 2001 From: jacek-casper <145967538+jacek-casper@users.noreply.github.com> Date: Tue, 14 May 2024 13:04:34 +0100 Subject: [PATCH 1/2] Remove unwrap in state_get_auction_info (#302) --- rpc_sidecar/src/rpcs/state.rs | 54 ++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/rpc_sidecar/src/rpcs/state.rs b/rpc_sidecar/src/rpcs/state.rs index 8487e02a..938b83cb 100644 --- a/rpc_sidecar/src/rpcs/state.rs +++ b/rpc_sidecar/src/rpcs/state.rs @@ -334,11 +334,7 @@ impl RpcWithOptionalParams for GetAuctionInfo { maybe_params: Option, ) -> Result { let block_identifier = maybe_params.map(|params| params.block_identifier); - let block_header = node_client - .read_block_header(block_identifier) - .await - .map_err(|err| Error::NodeRequest("block header", err))? - .unwrap(); + let block_header = common::get_block_header(&*node_client, block_identifier).await?; let state_identifier = block_identifier.map(GlobalStateIdentifier::from); let legacy_bid_stored_values = node_client @@ -1128,8 +1124,8 @@ mod tests { global_state::{TrieMerkleProof, TrieMerkleProofStep}, system::auction::{Bid, BidKind, ValidatorBid}, testing::TestRng, - AccessRights, AddressableEntity, Block, ByteCodeHash, EntityKind, EntryPoints, PackageHash, - ProtocolVersion, TestBlockBuilder, + AccessRights, AddressableEntity, AvailableBlockRange, Block, ByteCodeHash, EntityKind, + EntryPoints, PackageHash, ProtocolVersion, TestBlockBuilder, }; use pretty_assertions::assert_eq; use rand::Rng; @@ -1351,6 +1347,50 @@ mod tests { ); } + #[tokio::test] + async fn should_fail_auction_info_when_block_not_found() { + struct ClientMock; + + #[async_trait] + impl NodeClient for ClientMock { + async fn send_request( + &self, + req: BinaryRequest, + ) -> Result { + match req { + BinaryRequest::Get(GetRequest::Information { info_type_tag, .. }) + if InformationRequestTag::try_from(info_type_tag) + == Ok(InformationRequestTag::BlockHeader) => + { + Ok(BinaryResponseAndRequest::new( + BinaryResponse::new_empty(SUPPORTED_PROTOCOL_VERSION), + &[], + )) + } + BinaryRequest::Get(GetRequest::Information { info_type_tag, .. }) + if InformationRequestTag::try_from(info_type_tag) + == Ok(InformationRequestTag::AvailableBlockRange) => + { + Ok(BinaryResponseAndRequest::new( + BinaryResponse::from_value( + AvailableBlockRange::RANGE_0_0, + SUPPORTED_PROTOCOL_VERSION, + ), + &[], + )) + } + req => unimplemented!("unexpected request: {:?}", req), + } + } + } + + let err = GetAuctionInfo::do_handle_request(Arc::new(ClientMock), None) + .await + .expect_err("should reject request"); + + assert_eq!(err.code(), ErrorCode::NoSuchBlock as i64); + } + #[tokio::test] async fn should_read_entity() { use casper_types::addressable_entity::{ActionThresholds, AssociatedKeys}; From aa0a219d167b4a83923c5593f43abeaf50d2db18 Mon Sep 17 00:00:00 2001 From: jacek-casper <145967538+jacek-casper@users.noreply.github.com> Date: Tue, 14 May 2024 14:07:16 +0100 Subject: [PATCH 2/2] Retrieve entity named keys and entry points (#296) * Retrieve entity named keys * Bump casper-node deps * Fix post-merge error * Bump casper-node dependencies * Retrieve entry points * Point at feat-2.0 --- Cargo.lock | 4 +- Cargo.toml | 4 +- resources/test/rpc_schema.json | 244 +++++++++++++++--- resources/test/speculative_rpc_schema.json | 174 +++++++++++-- rpc_sidecar/src/node_client.rs | 69 ++++- rpc_sidecar/src/rpcs/account.rs | 2 +- rpc_sidecar/src/rpcs/common.rs | 108 +++++++- rpc_sidecar/src/rpcs/error.rs | 6 + rpc_sidecar/src/rpcs/state.rs | 141 ++++++++-- types/Cargo.toml | 2 +- .../translate_execution_result.rs | 1 + 11 files changed, 656 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0f0752a..1f860035 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -468,7 +468,7 @@ dependencies = [ [[package]] name = "casper-binary-port" version = "1.0.0" -source = "git+https://github.com/darthsiroftardis/casper-node.git?branch=block-restructure#fc3b7d9a6c17582a230caffc23646783b4c344a6" +source = "git+https://github.com/casper-network/casper-node.git?branch=feat-2.0#39dcb74d97879321a9008e238cb11bb4b5276c68" dependencies = [ "bincode", "bytes", @@ -670,7 +670,7 @@ dependencies = [ [[package]] name = "casper-types" version = "5.0.0" -source = "git+https://github.com/darthsiroftardis/casper-node.git?branch=block-restructure#fc3b7d9a6c17582a230caffc23646783b4c344a6" +source = "git+https://github.com/casper-network/casper-node.git?branch=feat-2.0#39dcb74d97879321a9008e238cb11bb4b5276c68" dependencies = [ "base16", "base64 0.13.1", diff --git a/Cargo.toml b/Cargo.toml index 7f0f59b5..4a8f6c46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ members = [ anyhow = "1" async-stream = "0.3.4" async-trait = "0.1.77" -casper-types = { workspace = true, features = ["std"], git = "https://github.com/darthsiroftardis/casper-node.git" , branch = "block-restructure"} -casper-binary-port = { git = "https://github.com/darthsiroftardis/casper-node.git", branch = "block-restructure" } +casper-types = { git = "https://github.com/casper-network/casper-node.git", branch = "feat-2.0" } +casper-binary-port = { git = "https://github.com/casper-network/casper-node.git", branch = "feat-2.0" } casper-event-sidecar = { path = "./event_sidecar", version = "1.0.0" } casper-event-types = { path = "./types", version = "1.0.0" } casper-rpc-sidecar = { path = "./rpc_sidecar", version = "1.0.0" } diff --git a/resources/test/rpc_schema.json b/resources/test/rpc_schema.json index 9a1019e4..29e6c91b 100644 --- a/resources/test/rpc_schema.json +++ b/resources/test/rpc_schema.json @@ -800,41 +800,49 @@ "api_version": "2.0.0", "entity": { "AddressableEntity": { - "protocol_version": "2.0.0", - "entity_kind": { - "Account": "account-hash-e94daaff79c2ab8d9c31d9c3058d7d0a0dd31204a5638dc1451fa67b2e3fb88c" + "entity": { + "protocol_version": "2.0.0", + "entity_kind": { + "Account": "account-hash-e94daaff79c2ab8d9c31d9c3058d7d0a0dd31204a5638dc1451fa67b2e3fb88c" + }, + "package_hash": "package-0000000000000000000000000000000000000000000000000000000000000000", + "byte_code_hash": "byte-code-0000000000000000000000000000000000000000000000000000000000000000", + "main_purse": "uref-09480c3248ef76b603d386f3f4f8a5f87f597d4eaffd475433f861af187ab5db-007", + "associated_keys": [ + { + "account_hash": "account-hash-e94daaff79c2ab8d9c31d9c3058d7d0a0dd31204a5638dc1451fa67b2e3fb88c", + "weight": 1 + } + ], + "action_thresholds": { + "deployment": 1, + "upgrade_management": 1, + "key_management": 1 + }, + "message_topics": [ + { + "topic_name": "topic", + "topic_name_hash": "0000000000000000000000000000000000000000000000000000000000000000" + } + ] }, - "package_hash": "package-0000000000000000000000000000000000000000000000000000000000000000", - "byte_code_hash": "byte-code-0000000000000000000000000000000000000000000000000000000000000000", - "main_purse": "uref-09480c3248ef76b603d386f3f4f8a5f87f597d4eaffd475433f861af187ab5db-007", + "named_keys": [ + { + "name": "key", + "key": "hash-0000000000000000000000000000000000000000000000000000000000000000" + } + ], "entry_points": [ { - "name": "call", - "entry_point": { - "name": "call", + "V1CasperVm": { + "name": "entry_point", "args": [], "ret": "Unit", "access": "Public", - "entry_point_type": "Caller" + "entry_point_type": "Caller", + "entry_point_payment": "Caller" } } - ], - "associated_keys": [ - { - "account_hash": "account-hash-e94daaff79c2ab8d9c31d9c3058d7d0a0dd31204a5638dc1451fa67b2e3fb88c", - "weight": 1 - } - ], - "action_thresholds": { - "deployment": 1, - "upgrade_management": 1, - "key_management": 1 - }, - "message_topics": [ - { - "topic_name": "topic", - "topic_name_hash": "0000000000000000000000000000000000000000000000000000000000000000" - } ] } }, @@ -3459,6 +3467,13 @@ "enum": [ "VmCasperV1" ] + }, + { + "description": "The Casper Version 2 Virtual Machine.", + "type": "string", + "enum": [ + "VmCasperV2" + ] } ] }, @@ -5551,6 +5566,19 @@ } }, "additionalProperties": false + }, + { + "description": "An entrypoint record.", + "type": "object", + "required": [ + "EntryPoint" + ], + "properties": { + "EntryPoint": { + "$ref": "#/components/schemas/EntryPointValue" + } + }, + "additionalProperties": false } ] }, @@ -6004,7 +6032,6 @@ "associated_keys", "byte_code_hash", "entity_kind", - "entry_points", "main_purse", "message_topics", "package_hash", @@ -6026,9 +6053,6 @@ "main_purse": { "$ref": "#/components/schemas/URef" }, - "entry_points": { - "$ref": "#/components/schemas/Array_of_NamedEntryPoint" - }, "associated_keys": { "$ref": "#/components/schemas/EntityAssociatedKeys" }, @@ -6071,10 +6095,16 @@ }, { "description": "Packages associated with Wasm stored on chain.", - "type": "string", - "enum": [ + "type": "object", + "required": [ "SmartContract" - ] + ], + "properties": { + "SmartContract": { + "$ref": "#/components/schemas/TransactionRuntime" + } + }, + "additionalProperties": false } ] }, @@ -6424,6 +6454,120 @@ } } }, + "EntryPointValue": { + "description": "The encaspulated representation of entrypoints.", + "oneOf": [ + { + "description": "Entrypoints to be executed against the V1 Casper VM.", + "type": "object", + "required": [ + "V1CasperVm" + ], + "properties": { + "V1CasperVm": { + "$ref": "#/components/schemas/EntryPoint2" + } + }, + "additionalProperties": false + }, + { + "description": "Entrypoints to be executed against the V2 Casper VM.", + "type": "object", + "required": [ + "V2CasperVm" + ], + "properties": { + "V2CasperVm": { + "$ref": "#/components/schemas/EntryPointV2" + } + }, + "additionalProperties": false + } + ] + }, + "EntryPoint2": { + "description": "Type signature of a method. Order of arguments matter since can be referenced by index as well as name.", + "type": "object", + "required": [ + "access", + "args", + "entry_point_payment", + "entry_point_type", + "name", + "ret" + ], + "properties": { + "name": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Parameter" + } + }, + "ret": { + "$ref": "#/components/schemas/CLType" + }, + "access": { + "$ref": "#/components/schemas/EntryPointAccess" + }, + "entry_point_type": { + "$ref": "#/components/schemas/EntryPointType" + }, + "entry_point_payment": { + "$ref": "#/components/schemas/EntryPointPayment" + } + } + }, + "EntryPointPayment": { + "description": "An enum specifying who pays for the invocation and execution of the entrypoint.", + "oneOf": [ + { + "description": "The caller must cover cost", + "type": "string", + "enum": [ + "Caller" + ] + }, + { + "description": "Will cover cost to execute self but not cost of any subsequent invoked contracts", + "type": "string", + "enum": [ + "SelfOnly" + ] + }, + { + "description": "will cover cost to execute self and the cost of any subsequent invoked contracts", + "type": "string", + "enum": [ + "SelfOnward" + ] + } + ] + }, + "EntryPointV2": { + "description": "The entry point for the V2 Casper VM.", + "type": "object", + "required": [ + "flags", + "function_index" + ], + "properties": { + "function_index": { + "description": "The selector.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "flags": { + "description": "The flags.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + }, "TransformError": { "description": "Error type for applying and combining transforms.\n\nA `TypeMismatch` occurs when a transform cannot be applied because the types are not compatible (e.g. trying to add a number to a string).", "oneOf": [ @@ -6653,7 +6797,37 @@ ], "properties": { "AddressableEntity": { - "$ref": "#/components/schemas/AddressableEntity" + "type": "object", + "required": [ + "entity", + "entry_points", + "named_keys" + ], + "properties": { + "entity": { + "description": "The addressable entity.", + "allOf": [ + { + "$ref": "#/components/schemas/AddressableEntity" + } + ] + }, + "named_keys": { + "description": "The named keys of the addressable entity.", + "allOf": [ + { + "$ref": "#/components/schemas/NamedKeys" + } + ] + }, + "entry_points": { + "description": "The entry points of the addressable entity.", + "type": "array", + "items": { + "$ref": "#/components/schemas/EntryPointValue" + } + } + } } }, "additionalProperties": false diff --git a/resources/test/speculative_rpc_schema.json b/resources/test/speculative_rpc_schema.json index c164e5e3..d2f01418 100644 --- a/resources/test/speculative_rpc_schema.json +++ b/resources/test/speculative_rpc_schema.json @@ -1726,6 +1726,19 @@ } }, "additionalProperties": false + }, + { + "description": "An entrypoint record.", + "type": "object", + "required": [ + "EntryPoint" + ], + "properties": { + "EntryPoint": { + "$ref": "#/components/schemas/EntryPointValue" + } + }, + "additionalProperties": false } ] }, @@ -2646,7 +2659,6 @@ "associated_keys", "byte_code_hash", "entity_kind", - "entry_points", "main_purse", "message_topics", "package_hash", @@ -2668,9 +2680,6 @@ "main_purse": { "$ref": "#/components/schemas/URef" }, - "entry_points": { - "$ref": "#/components/schemas/Array_of_NamedEntryPoint" - }, "associated_keys": { "$ref": "#/components/schemas/EntityAssociatedKeys" }, @@ -2713,10 +2722,16 @@ }, { "description": "Packages associated with Wasm stored on chain.", - "type": "string", - "enum": [ + "type": "object", + "required": [ "SmartContract" - ] + ], + "properties": { + "SmartContract": { + "$ref": "#/components/schemas/TransactionRuntime" + } + }, + "additionalProperties": false } ] }, @@ -2753,6 +2768,25 @@ } ] }, + "TransactionRuntime": { + "description": "Runtime used to execute a Transaction.", + "oneOf": [ + { + "description": "The Casper Version 1 Virtual Machine.", + "type": "string", + "enum": [ + "VmCasperV1" + ] + }, + { + "description": "The Casper Version 2 Virtual Machine.", + "type": "string", + "enum": [ + "VmCasperV2" + ] + } + ] + }, "ByteCodeHash": { "description": "The hash address of the contract wasm", "type": "string" @@ -3218,6 +3252,120 @@ } } }, + "EntryPointValue": { + "description": "The encaspulated representation of entrypoints.", + "oneOf": [ + { + "description": "Entrypoints to be executed against the V1 Casper VM.", + "type": "object", + "required": [ + "V1CasperVm" + ], + "properties": { + "V1CasperVm": { + "$ref": "#/components/schemas/EntryPoint2" + } + }, + "additionalProperties": false + }, + { + "description": "Entrypoints to be executed against the V2 Casper VM.", + "type": "object", + "required": [ + "V2CasperVm" + ], + "properties": { + "V2CasperVm": { + "$ref": "#/components/schemas/EntryPointV2" + } + }, + "additionalProperties": false + } + ] + }, + "EntryPoint2": { + "description": "Type signature of a method. Order of arguments matter since can be referenced by index as well as name.", + "type": "object", + "required": [ + "access", + "args", + "entry_point_payment", + "entry_point_type", + "name", + "ret" + ], + "properties": { + "name": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Parameter" + } + }, + "ret": { + "$ref": "#/components/schemas/CLType" + }, + "access": { + "$ref": "#/components/schemas/EntryPointAccess" + }, + "entry_point_type": { + "$ref": "#/components/schemas/EntryPointType" + }, + "entry_point_payment": { + "$ref": "#/components/schemas/EntryPointPayment" + } + } + }, + "EntryPointPayment": { + "description": "An enum specifying who pays for the invocation and execution of the entrypoint.", + "oneOf": [ + { + "description": "The caller must cover cost", + "type": "string", + "enum": [ + "Caller" + ] + }, + { + "description": "Will cover cost to execute self but not cost of any subsequent invoked contracts", + "type": "string", + "enum": [ + "SelfOnly" + ] + }, + { + "description": "will cover cost to execute self and the cost of any subsequent invoked contracts", + "type": "string", + "enum": [ + "SelfOnward" + ] + } + ] + }, + "EntryPointV2": { + "description": "The entry point for the V2 Casper VM.", + "type": "object", + "required": [ + "flags", + "function_index" + ], + "properties": { + "function_index": { + "description": "The selector.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "flags": { + "description": "The flags.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + }, "U128": { "description": "Decimal representation of a 128-bit integer.", "type": "string" @@ -3831,18 +3979,6 @@ } ] }, - "TransactionRuntime": { - "description": "Runtime used to execute a Transaction.", - "oneOf": [ - { - "description": "The Casper Version 1 Virtual Machine.", - "type": "string", - "enum": [ - "VmCasperV1" - ] - } - ] - }, "TransactionSessionKind": { "description": "Session kind of a Transaction.", "oneOf": [ diff --git a/rpc_sidecar/src/node_client.rs b/rpc_sidecar/src/node_client.rs index c3c36b26..64f3a0df 100644 --- a/rpc_sidecar/src/node_client.rs +++ b/rpc_sidecar/src/node_client.rs @@ -15,8 +15,8 @@ use casper_binary_port::{ BalanceResponse, BinaryMessage, BinaryMessageCodec, BinaryRequest, BinaryRequestHeader, BinaryResponse, BinaryResponseAndRequest, ConsensusValidatorChanges, DictionaryItemIdentifier, DictionaryQueryResult, ErrorCode, GetRequest, GetTrieFullResult, GlobalStateQueryResult, - GlobalStateRequest, InformationRequest, NodeStatus, PayloadEntity, PurseIdentifier, RecordId, - SpeculativeExecutionResult, TransactionWithExecutionInfo, + GlobalStateRequest, InformationRequest, KeyPrefix, NodeStatus, PayloadEntity, PurseIdentifier, + RecordId, SpeculativeExecutionResult, TransactionWithExecutionInfo, }; use casper_types::{ bytesrepr::{self, FromBytes, ToBytes}, @@ -87,12 +87,27 @@ pub trait NodeClient: Send + Sync { parse_response::>(&resp.into())?.ok_or(Error::EmptyEnvelope) } + async fn query_global_state_by_prefix( + &self, + state_identifier: Option, + key_prefix: KeyPrefix, + ) -> Result, Error> { + let get = GlobalStateRequest::ItemsByPrefix { + state_identifier, + key_prefix, + }; + let resp = self + .send_request(BinaryRequest::Get(GetRequest::State(Box::new(get)))) + .await?; + parse_response::>(&resp.into())?.ok_or(Error::EmptyEnvelope) + } + async fn read_balance( &self, state_identifier: Option, purse_identifier: PurseIdentifier, ) -> Result { - let get = GlobalStateRequest::BalanceByStateRoot { + let get = GlobalStateRequest::Balance { state_identifier, purse_identifier, }; @@ -261,9 +276,55 @@ impl Error { fn from_error_code(code: u8) -> Self { match ErrorCode::try_from(code) { Ok(ErrorCode::FunctionDisabled) => Self::FunctionIsDisabled, - Ok(ErrorCode::InvalidTransaction) => Self::InvalidTransaction, Ok(ErrorCode::RootNotFound) => Self::UnknownStateRootHash, Ok(ErrorCode::FailedQuery) => Self::QueryFailedToExecute, + Ok( + ErrorCode::InvalidDeployChainName + | ErrorCode::InvalidDeployDependenciesNoLongerSupported + | ErrorCode::InvalidDeployExcessiveSize + | ErrorCode::InvalidDeployExcessiveTimeToLive + | ErrorCode::InvalidDeployTimestampInFuture + | ErrorCode::InvalidDeployBodyHash + | ErrorCode::InvalidDeployHash + | ErrorCode::InvalidDeployEmptyApprovals + | ErrorCode::InvalidDeployApproval + | ErrorCode::InvalidDeployExcessiveSessionArgsLength + | ErrorCode::InvalidDeployExcessivePaymentArgsLength + | ErrorCode::InvalidDeployMissingPaymentAmount + | ErrorCode::InvalidDeployFailedToParsePaymentAmount + | ErrorCode::InvalidDeployExceededBlockGasLimit + | ErrorCode::InvalidDeployMissingTransferAmount + | ErrorCode::InvalidDeployFailedToParseTransferAmount + | ErrorCode::InvalidDeployInsufficientTransferAmount + | ErrorCode::InvalidDeployExcessiveApprovals + | ErrorCode::InvalidDeployUnableToCalculateGasLimit + | ErrorCode::InvalidDeployUnableToCalculateGasCost + | ErrorCode::InvalidDeployUnspecified + | ErrorCode::InvalidTransactionChainName + | ErrorCode::InvalidTransactionExcessiveSize + | ErrorCode::InvalidTransactionExcessiveTimeToLive + | ErrorCode::InvalidTransactionTimestampInFuture + | ErrorCode::InvalidTransactionBodyHash + | ErrorCode::InvalidTransactionHash + | ErrorCode::InvalidTransactionEmptyApprovals + | ErrorCode::InvalidTransactionInvalidApproval + | ErrorCode::InvalidTransactionExcessiveArgsLength + | ErrorCode::InvalidTransactionExcessiveApprovals + | ErrorCode::InvalidTransactionExceedsBlockGasLimit + | ErrorCode::InvalidTransactionMissingArg + | ErrorCode::InvalidTransactionUnexpectedArgType + | ErrorCode::InvalidTransactionInvalidArg + | ErrorCode::InvalidTransactionInsufficientTransferAmount + | ErrorCode::InvalidTransactionEntryPointCannotBeCustom + | ErrorCode::InvalidTransactionEntryPointMustBeCustom + | ErrorCode::InvalidTransactionEmptyModuleBytes + | ErrorCode::InvalidTransactionGasPriceConversion + | ErrorCode::InvalidTransactionUnableToCalculateGasLimit + | ErrorCode::InvalidTransactionUnableToCalculateGasCost + | ErrorCode::InvalidTransactionPricingMode + | ErrorCode::InvalidTransactionUnspecified + | ErrorCode::InvalidTransactionOrDeployUnspecified, + ) => Self::InvalidTransaction, // TODO: map transaction errors to proper variants Ok(err @ (ErrorCode::WasmPreprocessing | ErrorCode::InvalidItemVariant)) => { Self::SpecExecutionFailed(err.to_string()) } diff --git a/rpc_sidecar/src/rpcs/account.rs b/rpc_sidecar/src/rpcs/account.rs index 79b851bd..8b1395c2 100644 --- a/rpc_sidecar/src/rpcs/account.rs +++ b/rpc_sidecar/src/rpcs/account.rs @@ -257,7 +257,7 @@ mod tests { BinaryRequest::TryAcceptTransaction { .. } => { Ok(BinaryResponseAndRequest::new( BinaryResponse::new_error( - BinaryPortErrorCode::InvalidTransaction, + BinaryPortErrorCode::InvalidTransactionBodyHash, SUPPORTED_PROTOCOL_VERSION, ), &[], diff --git a/rpc_sidecar/src/rpcs/common.rs b/rpc_sidecar/src/rpcs/common.rs index 9a247de5..74c64751 100644 --- a/rpc_sidecar/src/rpcs/common.rs +++ b/rpc_sidecar/src/rpcs/common.rs @@ -1,13 +1,16 @@ -use casper_binary_port::GlobalStateQueryResult; +use std::collections::BTreeMap; + +use casper_binary_port::{GlobalStateQueryResult, KeyPrefix}; use once_cell::sync::Lazy; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::rpcs::error::Error; use casper_types::{ - account::AccountHash, bytesrepr::ToBytes, global_state::TrieMerkleProof, Account, - AddressableEntity, AvailableBlockRange, BlockHeader, BlockIdentifier, EntityAddr, - GlobalStateIdentifier, Key, SignedBlock, StoredValue, + account::AccountHash, addressable_entity::NamedKeys, bytesrepr::ToBytes, + global_state::TrieMerkleProof, Account, AddressableEntity, AvailableBlockRange, BlockHeader, + BlockIdentifier, EntityAddr, EntryPointValue, GlobalStateIdentifier, Key, SignedBlock, + StoredValue, }; use crate::NodeClient; @@ -44,7 +47,14 @@ pub enum ErrorData { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub enum EntityOrAccount { /// An addressable entity. - AddressableEntity(AddressableEntity), + AddressableEntity { + /// The addressable entity. + entity: AddressableEntity, + /// The named keys of the addressable entity. + named_keys: NamedKeys, + /// The entry points of the addressable entity. + entry_points: Vec, + }, /// A legacy account. LegacyAccount(Account), } @@ -140,10 +150,23 @@ pub async fn resolve_account_hash( else { return Ok(None); }; - let entity = value - .into_addressable_entity() - .ok_or(Error::InvalidAddressableEntity)?; - (EntityOrAccount::AddressableEntity(entity), merkle_proof) + 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), }; @@ -176,6 +199,73 @@ pub async fn resolve_entity_addr( })) } +pub async fn get_entity_named_keys( + node_client: &dyn NodeClient, + entity_addr: EntityAddr, + state_identifier: Option, +) -> Result { + let stored_values = node_client + .query_global_state_by_prefix(state_identifier, KeyPrefix::NamedKeysByEntity(entity_addr)) + .await + .map_err(|err| Error::NodeRequest("entity named keys", err))?; + let named_keys = stored_values + .into_iter() + .map(|stored_value| { + if let StoredValue::NamedKey(named_key) = stored_value { + let key = named_key + .get_key() + .map_err(|err| Error::InvalidNamedKeys(err.to_string()))?; + let name = named_key + .get_name() + .map_err(|err| Error::InvalidNamedKeys(err.to_string()))?; + Ok((name, key)) + } else { + Err(Error::InvalidNamedKeys(format!( + "unexpected stored value: {}", + stored_value.type_name() + ))) + } + }) + .collect::, Error>>()?; + Ok(NamedKeys::from(named_keys)) +} + +pub async fn get_entity_entry_points( + node_client: &dyn NodeClient, + entity_addr: EntityAddr, + state_identifier: Option, +) -> Result, Error> { + let stored_values_v1 = node_client + .query_global_state_by_prefix( + state_identifier, + KeyPrefix::EntryPointsV1ByEntity(entity_addr), + ) + .await + .map_err(|err| Error::NodeRequest("entity named keys", err))?; + let stored_values_v2 = node_client + .query_global_state_by_prefix( + state_identifier, + KeyPrefix::EntryPointsV2ByEntity(entity_addr), + ) + .await + .map_err(|err| Error::NodeRequest("entity named keys", err))?; + + stored_values_v1 + .into_iter() + .chain(stored_values_v2) + .map(|stored_value| { + if let StoredValue::EntryPoint(entry_point) = stored_value { + Ok(entry_point) + } else { + Err(Error::InvalidNamedKeys(format!( + "unexpected stored value: {}", + stored_value.type_name() + ))) + } + }) + .collect::>() +} + pub fn encode_proof(proof: &Vec>) -> Result { Ok(base16::encode_lower( &proof.to_bytes().map_err(Error::BytesreprFailure)?, diff --git a/rpc_sidecar/src/rpcs/error.rs b/rpc_sidecar/src/rpcs/error.rs index 6d600030..49bfcb85 100644 --- a/rpc_sidecar/src/rpcs/error.rs +++ b/rpc_sidecar/src/rpcs/error.rs @@ -55,6 +55,10 @@ pub enum Error { InvalidAddressableEntity, #[error("the auction state was invalid")] InvalidAuctionState, + #[error("the named keys were invalid: {0}")] + InvalidNamedKeys(String), + #[error("the entry points were invalid: {0}")] + InvalidEntryPoints(String), #[error("speculative execution returned nothing")] SpecExecReturnedNothing, #[error("unexpected bytesrepr failure: {0}")] @@ -98,6 +102,8 @@ impl Error { Error::InvalidAccountInfo | Error::InvalidAddressableEntity | Error::InvalidAuctionState + | Error::InvalidNamedKeys(_) + | Error::InvalidEntryPoints(_) | Error::BytesreprFailure(_) => None, } } diff --git a/rpc_sidecar/src/rpcs/state.rs b/rpc_sidecar/src/rpcs/state.rs index 938b83cb..7b89aafa 100644 --- a/rpc_sidecar/src/rpcs/state.rs +++ b/rpc_sidecar/src/rpcs/state.rs @@ -29,8 +29,8 @@ use casper_types::{ AUCTION, }, AddressableEntity, AddressableEntityHash, AuctionState, BlockHash, BlockHeader, BlockHeaderV2, - BlockIdentifier, BlockTime, BlockV2, CLValue, Digest, EntityAddr, GlobalStateIdentifier, Key, - KeyTag, PublicKey, SecretKey, StoredValue, URef, U512, + BlockIdentifier, BlockTime, BlockV2, CLValue, Digest, EntityAddr, EntryPoint, EntryPointValue, + GlobalStateIdentifier, Key, KeyTag, PublicKey, SecretKey, StoredValue, URef, U512, }; #[cfg(test)] use rand::Rng; @@ -87,7 +87,17 @@ static GET_ADDRESSABLE_ENTITY_RESULT: Lazy = Lazy::new(|| GetAddressableEntityResult { api_version: DOCS_EXAMPLE_API_VERSION, merkle_proof: MERKLE_PROOF.clone(), - entity: EntityOrAccount::AddressableEntity(AddressableEntity::example().clone()), + entity: EntityOrAccount::AddressableEntity { + entity: AddressableEntity::example().clone(), + named_keys: [("key".to_string(), Key::Hash([0u8; 32]))] + .iter() + .cloned() + .collect::>() + .into(), + entry_points: vec![EntryPointValue::new_v1_entry_point_value( + EntryPoint::default_with_name("entry_point"), + )], + }, }); static GET_DICTIONARY_ITEM_PARAMS: Lazy = Lazy::new(|| GetDictionaryItemParams { @@ -576,8 +586,16 @@ impl RpcWithParams for GetAddressableEntity { let result = common::resolve_entity_addr(&*node_client, addr, state_identifier) .await? .ok_or(Error::AddressableEntityNotFound)?; + let named_keys = + common::get_entity_named_keys(&*node_client, addr, state_identifier).await?; + let entry_points = + common::get_entity_entry_points(&*node_client, addr, state_identifier).await?; ( - EntityOrAccount::AddressableEntity(result.value), + EntityOrAccount::AddressableEntity { + entity: result.value, + named_keys, + entry_points, + }, result.merkle_proof, ) } @@ -1117,15 +1135,15 @@ mod tests { use casper_binary_port::{ BalanceResponse, BinaryRequest, BinaryResponse, BinaryResponseAndRequest, DictionaryQueryResult, GetRequest, GlobalStateQueryResult, GlobalStateRequest, - InformationRequestTag, + InformationRequestTag, KeyPrefix, }; use casper_types::{ - addressable_entity::{MessageTopics, NamedKeys}, + addressable_entity::{MessageTopics, NamedKeyValue, NamedKeys}, global_state::{TrieMerkleProof, TrieMerkleProofStep}, system::auction::{Bid, BidKind, ValidatorBid}, testing::TestRng, AccessRights, AddressableEntity, AvailableBlockRange, Block, ByteCodeHash, EntityKind, - EntryPoints, PackageHash, ProtocolVersion, TestBlockBuilder, + PackageHash, ProtocolVersion, TestBlockBuilder, TransactionRuntime, }; use pretty_assertions::assert_eq; use rand::Rng; @@ -1396,8 +1414,9 @@ mod tests { use casper_types::addressable_entity::{ActionThresholds, AssociatedKeys}; struct ClientMock { - block: Block, entity: AddressableEntity, + named_keys: NamedKeys, + entry_points: Vec, entity_hash: AddressableEntityHash, } @@ -1408,18 +1427,6 @@ mod tests { req: BinaryRequest, ) -> Result { match req { - BinaryRequest::Get(GetRequest::Information { info_type_tag, .. }) - if InformationRequestTag::try_from(info_type_tag) - == Ok(InformationRequestTag::BlockHeader) => - { - Ok(BinaryResponseAndRequest::new( - BinaryResponse::from_value( - self.block.clone_header(), - SUPPORTED_PROTOCOL_VERSION, - ), - &[], - )) - } BinaryRequest::Get(GetRequest::State(req)) if matches!( &*req, @@ -1463,31 +1470,109 @@ mod tests { &[], )) } + BinaryRequest::Get(GetRequest::State(req)) + if matches!( + &*req, + GlobalStateRequest::ItemsByPrefix { + key_prefix: KeyPrefix::NamedKeysByEntity(_), + .. + } + ) => + { + Ok(BinaryResponseAndRequest::new( + BinaryResponse::from_value( + self.named_keys + .iter() + .map(|(name, key)| { + StoredValue::NamedKey( + NamedKeyValue::from_concrete_values(*key, name.clone()) + .expect("should create named key"), + ) + }) + .collect::>(), + SUPPORTED_PROTOCOL_VERSION, + ), + &[], + )) + } + BinaryRequest::Get(GetRequest::State(req)) + if matches!( + &*req, + GlobalStateRequest::ItemsByPrefix { + key_prefix: KeyPrefix::EntryPointsV1ByEntity(_), + .. + } + ) => + { + Ok(BinaryResponseAndRequest::new( + BinaryResponse::from_value( + self.entry_points + .iter() + .cloned() + .map(StoredValue::EntryPoint) + .collect::>(), + SUPPORTED_PROTOCOL_VERSION, + ), + &[], + )) + } + BinaryRequest::Get(GetRequest::State(req)) + if matches!( + &*req, + GlobalStateRequest::ItemsByPrefix { + key_prefix: KeyPrefix::EntryPointsV2ByEntity(_), + .. + } + ) => + { + Ok(BinaryResponseAndRequest::new( + BinaryResponse::from_value( + Vec::::new(), + SUPPORTED_PROTOCOL_VERSION, + ), + &[], + )) + } req => unimplemented!("unexpected request: {:?}", req), } } } let rng = &mut TestRng::new(); - let block = Block::V2(TestBlockBuilder::new().build(rng)); let entity = AddressableEntity::new( PackageHash::new(rng.gen()), ByteCodeHash::new(rng.gen()), - EntryPoints::new_with_default_entry_point(), ProtocolVersion::V1_0_0, rng.gen(), AssociatedKeys::default(), ActionThresholds::default(), MessageTopics::default(), - EntityKind::SmartContract, + EntityKind::SmartContract(TransactionRuntime::VmCasperV2), ); let entity_hash: AddressableEntityHash = rng.gen(); + + let named_key_count = rng.gen_range(0..10); + let named_keys: NamedKeys = + iter::repeat_with(|| (rng.random_string(1..36), Key::Hash(rng.gen()))) + .take(named_key_count) + .collect::>() + .into(); + let entry_point_count = rng.gen_range(0..10); + let entry_points = iter::repeat_with(|| { + EntryPointValue::new_v1_entry_point_value(EntryPoint::default_with_name( + rng.random_string(1..10), + )) + }) + .take(entry_point_count) + .collect::>(); + let entity_identifier = EntityIdentifier::random(rng); let resp = GetAddressableEntity::do_handle_request( Arc::new(ClientMock { - block: block.clone(), entity: entity.clone(), + named_keys: named_keys.clone(), + entry_points: entry_points.clone(), entity_hash, }), GetAddressableEntityParams { @@ -1502,7 +1587,11 @@ mod tests { resp, GetAddressableEntityResult { api_version: CURRENT_API_VERSION, - entity: EntityOrAccount::AddressableEntity(entity), + entity: EntityOrAccount::AddressableEntity { + entity, + named_keys, + entry_points + }, merkle_proof: String::from("00000000"), } ); @@ -2080,7 +2169,7 @@ mod tests { ) -> Result { match req { BinaryRequest::Get(GetRequest::State(req)) - if matches!(&*req, GlobalStateRequest::BalanceByStateRoot { .. }) => + if matches!(&*req, GlobalStateRequest::Balance { .. }) => { Ok(BinaryResponseAndRequest::new( BinaryResponse::from_value(self.0.clone(), SUPPORTED_PROTOCOL_VERSION), diff --git a/types/Cargo.toml b/types/Cargo.toml index 7d10792d..19a710d6 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/casper-network/casper-sidecar/" [dependencies] base16 = "0.2.1" blake2 = { version = "0.9.0", optional = true } -casper-types = { workspace = true, features = ["std"], git = "https://github.com/darthsiroftardis/casper-node.git" , branch = "block-restructure"} +casper-types = { workspace = true, features = ["std"] } hex-buffer-serde = "0.3.0" hex_fmt = "0.3.0" itertools = { workspace = true } diff --git a/types/src/legacy_sse_data/translate_execution_result.rs b/types/src/legacy_sse_data/translate_execution_result.rs index b8b4a443..b35b1c5e 100644 --- a/types/src/legacy_sse_data/translate_execution_result.rs +++ b/types/src/legacy_sse_data/translate_execution_result.rs @@ -165,6 +165,7 @@ fn maybe_tanslate_stored_value(stored_value: &StoredValue) -> Option None, StoredValue::Message(_) => None, StoredValue::Reservation(_) => None, + StoredValue::EntryPoint(_) => None, } }