From 69a6b468b2165a6f9cc7eb62f140c68ff2a008cd Mon Sep 17 00:00:00 2001 From: Jakub Zajkowski Date: Tue, 19 Nov 2024 12:06:29 +0100 Subject: [PATCH 1/2] Fixed bug which caused calling entry points for smart contracts deployed as "Transaction::Deploy" (or created in v1.x) to always fail. The fix involves changing the way we fetch entry point data in DataAccessLayer. If entry point is not found under Key::EntryPoint we fallback to see if there is a contract entry for the given contract hash. If a Contract entry exists, we query it's `entry_points` field --- node/src/components/contract_runtime.rs | 10 +- .../components/contract_runtime/operations.rs | 24 ++-- node/src/components/contract_runtime/tests.rs | 128 ++++++++++++++++-- node/src/components/transaction_acceptor.rs | 65 +++------ .../components/transaction_acceptor/event.rs | 6 +- .../components/transaction_acceptor/tests.rs | 23 ++-- node/src/effect.rs | 20 +-- node/src/effect/requests.rs | 26 ++-- storage/src/data_access_layer.rs | 4 +- storage/src/data_access_layer/contract.rs | 40 ++++++ storage/src/data_access_layer/entry_points.rs | 55 +++++--- storage/src/global_state/state/mod.rs | 99 ++++++++++++-- types/src/contracts.rs | 14 +- 13 files changed, 366 insertions(+), 148 deletions(-) create mode 100644 storage/src/data_access_layer/contract.rs diff --git a/node/src/components/contract_runtime.rs b/node/src/components/contract_runtime.rs index 9850daec56..c3cd95b7f8 100644 --- a/node/src/components/contract_runtime.rs +++ b/node/src/components/contract_runtime.rs @@ -452,9 +452,10 @@ impl ContractRuntime { } .ignore() } - ContractRuntimeRequest::GetEntryPoint { + ContractRuntimeRequest::GetEntryPointExists { state_root_hash, - key, + contract_hash, + entry_point_name, responder, } => { trace!(?state_root_hash, "get entry point"); @@ -462,8 +463,9 @@ impl ContractRuntime { let data_access_layer = Arc::clone(&self.data_access_layer); async move { let start = Instant::now(); - let request = EntryPointsRequest::new(state_root_hash, key); - let result = data_access_layer.entry_point(request); + let request = + EntryPointsRequest::new(state_root_hash, entry_point_name, contract_hash); + let result = data_access_layer.entry_point_exists(request); metrics.entry_points.observe(start.elapsed().as_secs_f64()); trace!(?result, "get addressable entity"); responder.respond(result).await diff --git a/node/src/components/contract_runtime/operations.rs b/node/src/components/contract_runtime/operations.rs index a78b8a021c..d074d3a1ff 100644 --- a/node/src/components/contract_runtime/operations.rs +++ b/node/src/components/contract_runtime/operations.rs @@ -17,7 +17,7 @@ use casper_storage::{ mint::BalanceIdentifierTransferArgs, AuctionMethod, BalanceHoldKind, BalanceHoldRequest, BalanceIdentifier, BalanceRequest, BiddingRequest, BlockGlobalRequest, BlockGlobalResult, BlockRewardsRequest, - BlockRewardsResult, DataAccessLayer, EntryPointsRequest, EntryPointsResult, + BlockRewardsResult, DataAccessLayer, EntryPointResult, EntryPointsRequest, EraValidatorsRequest, EraValidatorsResult, EvictItem, FeeRequest, FeeResult, FlushRequest, HandleFeeMode, HandleFeeRequest, HandleRefundMode, HandleRefundRequest, InsufficientBalanceHandling, ProofHandling, PruneRequest, PruneResult, StepRequest, @@ -28,16 +28,14 @@ use casper_storage::{ StateProvider, StateReader, }, system::runtime_native::Config as NativeRuntimeConfig, - tracking_copy::TrackingCopyError, }; use casper_types::{ bytesrepr::{self, ToBytes, U32_SERIALIZED_LENGTH}, execution::{Effects, ExecutionResult, TransformKindV2, TransformV2}, system::handle_payment::ARG_AMOUNT, BlockHash, BlockHeader, BlockTime, BlockV2, CLValue, Chainspec, ChecksumRegistry, Digest, - EntityAddr, EntryPointAddr, EraEndV2, EraId, FeeHandling, Gas, InvalidTransaction, - InvalidTransactionV1, Key, ProtocolVersion, PublicKey, RefundHandling, Transaction, - AUCTION_LANE_ID, MINT_LANE_ID, U512, + EntityAddr, EraEndV2, EraId, FeeHandling, Gas, InvalidTransaction, InvalidTransactionV1, Key, + ProtocolVersion, PublicKey, RefundHandling, Transaction, AUCTION_LANE_ID, MINT_LANE_ID, U512, }; use super::{ @@ -1251,19 +1249,13 @@ fn invoked_contract_will_pay( Some((hash_addr, entry_point_name)) => (hash_addr, entry_point_name), }; let entity_addr = EntityAddr::new_smart_contract(hash_addr); - let entry_point_addr = - match EntryPointAddr::new_v1_entry_point_addr(entity_addr, &entry_point_name) { - Ok(addr) => addr, - Err(bre) => return Err(StateResultError::Failure(TrackingCopyError::BytesRepr(bre))), - }; - let entry_point_key = Key::entry_point(entry_point_addr); - let entry_point_request = EntryPointsRequest::new(state_root_hash, entry_point_key); + let entry_point_request = EntryPointsRequest::new(state_root_hash, entry_point_name, hash_addr); let entry_point_response = state_provider.entry_point(entry_point_request); match entry_point_response { - EntryPointsResult::RootNotFound => Err(StateResultError::RootNotFound), - EntryPointsResult::ValueNotFound(msg) => Err(StateResultError::ValueNotFound(msg)), - EntryPointsResult::Failure(tce) => Err(StateResultError::Failure(tce)), - EntryPointsResult::Success { entry_point } => { + EntryPointResult::RootNotFound => Err(StateResultError::RootNotFound), + EntryPointResult::ValueNotFound(msg) => Err(StateResultError::ValueNotFound(msg)), + EntryPointResult::Failure(tce) => Err(StateResultError::Failure(tce)), + EntryPointResult::Success { entry_point } => { if entry_point.will_pay_direct_invocation() { Ok(Some(entity_addr)) } else { diff --git a/node/src/components/contract_runtime/tests.rs b/node/src/components/contract_runtime/tests.rs index c3fe3d99a8..13e12ef9f2 100644 --- a/node/src/components/contract_runtime/tests.rs +++ b/node/src/components/contract_runtime/tests.rs @@ -397,24 +397,31 @@ async fn should_not_set_shared_pre_state_to_lower_block_height() { } #[cfg(test)] -mod trie_chunking_tests { +mod test_mod { use std::sync::Arc; use prometheus::Registry; + use rand::Rng; use tempfile::tempdir; - use casper_storage::global_state::{ - state::{CommitProvider, StateProvider}, - trie::Trie, + use casper_storage::{ + data_access_layer::{EntryPointExistsResult, EntryPointsRequest}, + global_state::{ + state::{CommitProvider, StateProvider}, + trie::Trie, + }, }; use casper_types::{ account::AccountHash, bytesrepr, + contracts::{ContractPackageHash, EntryPoint, EntryPoints}, execution::{TransformKindV2, TransformV2}, global_state::Pointer, testing::TestRng, - ActivationPoint, CLValue, Chainspec, ChunkWithProof, CoreConfig, Digest, EraId, Key, - ProtocolConfig, StoredValue, TimeDiff, DEFAULT_FEE_HANDLING, DEFAULT_GAS_HOLD_INTERVAL, + ActivationPoint, CLType, CLValue, Chainspec, ChunkWithProof, Contract, ContractWasmHash, + CoreConfig, Digest, EntityAddr, EntryPointAccess, EntryPointAddr, EntryPointPayment, + EntryPointType, EntryPointValue, EraId, HashAddr, Key, NamedKeys, ProtocolConfig, + ProtocolVersion, StoredValue, TimeDiff, DEFAULT_FEE_HANDLING, DEFAULT_GAS_HOLD_INTERVAL, DEFAULT_REFUND_HANDLING, }; @@ -428,14 +435,68 @@ mod trie_chunking_tests { #[derive(Debug, Clone)] struct TestPair(Key, StoredValue); + fn create_pre_condor_contract( + rng: &mut TestRng, + contract_hash: Key, + entry_point_name: &str, + protocol_version: ProtocolVersion, + ) -> Vec { + let mut entry_points = EntryPoints::new(); + let entry_point = EntryPoint::new( + entry_point_name, + vec![], + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Caller, + ); + entry_points.add_entry_point(entry_point); + + let contract_package_hash = ContractPackageHash::new(rng.gen()); + let contract_wasm_hash = ContractWasmHash::new(rng.gen()); + let named_keys = NamedKeys::new(); + let contract = Contract::new( + contract_package_hash, + contract_wasm_hash, + named_keys, + entry_points, + protocol_version, + ); + vec![TestPair(contract_hash, StoredValue::Contract(contract))] + } + + fn create_entry_point(entity_addr: EntityAddr, entry_point_name: &str) -> Vec { + let mut entry_points = EntryPoints::new(); + let entry_point = EntryPoint::new( + entry_point_name, + vec![], + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Caller, + ); + entry_points.add_entry_point(entry_point); + let key = Key::EntryPoint( + EntryPointAddr::new_v1_entry_point_addr(entity_addr, entry_point_name).unwrap(), + ); + let entry_point = casper_types::EntryPoint::new( + entry_point_name, + vec![], + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Caller, + EntryPointPayment::Caller, + ); + let entry_point_value = EntryPointValue::V1CasperVm(entry_point); + vec![TestPair(key, StoredValue::EntryPoint(entry_point_value))] + } + // Creates the test pairs that contain data of size // greater than the chunk limit. - fn create_test_pairs_with_large_data() -> [TestPair; 2] { + fn create_test_pairs_with_large_data() -> Vec { let val = CLValue::from_t( String::from_utf8(vec![b'a'; ChunkWithProof::CHUNK_SIZE_BYTES * 2]).unwrap(), ) .unwrap(); - [ + vec![ TestPair( Key::Account(AccountHash::new([1_u8; 32])), StoredValue::CLValue(val.clone()), @@ -472,7 +533,7 @@ mod trie_chunking_tests { // Creates a test ContractRuntime and feeds the underlying GlobalState with `test_pair`. // Returns [`ContractRuntime`] instance and the new Merkle root after applying the `test_pair`. - fn create_test_state(rng: &mut TestRng, test_pair: [TestPair; 2]) -> (ContractRuntime, Digest) { + fn create_test_state(rng: &mut TestRng, test_pair: Vec) -> (ContractRuntime, Digest) { let temp_dir = tempdir().unwrap(); let chainspec = Chainspec { protocol_config: ProtocolConfig { @@ -531,6 +592,55 @@ mod trie_chunking_tests { } } + #[test] + fn fetching_enty_points_falls_back_to_contract() { + let rng = &mut TestRng::new(); + let hash_addr: HashAddr = rng.gen(); + let contract_hash = Key::Hash(hash_addr); + let entry_point_name = "ep1"; + let initial_state = create_pre_condor_contract( + rng, + contract_hash, + entry_point_name, + ProtocolVersion::V2_0_0, + ); + let (contract_runtime, state_hash) = create_test_state(rng, initial_state); + let request = EntryPointsRequest::new(state_hash, entry_point_name.to_string(), hash_addr); + let res = contract_runtime + .data_access_layer() + .entry_point_exists(request); + assert!(matches!(res, EntryPointExistsResult::Success)); + } + + #[test] + fn fetching_enty_points_fetches_entry_point_from_v2() { + let rng = &mut TestRng::new(); + let hash_addr: HashAddr = rng.gen(); + let entity_addr = EntityAddr::new_smart_contract(hash_addr); + let entry_point_name = "ep1"; + let initial_state = create_entry_point(entity_addr, entry_point_name); + let (contract_runtime, state_hash) = create_test_state(rng, initial_state); + let request = EntryPointsRequest::new(state_hash, entry_point_name.to_string(), hash_addr); + let res = contract_runtime + .data_access_layer() + .entry_point_exists(request); + assert!(matches!(res, EntryPointExistsResult::Success)); + } + + #[test] + fn fetching_enty_points_fetches_fail_when_asking_for_non_existing() { + let rng = &mut TestRng::new(); + let hash_addr: HashAddr = rng.gen(); + let entity_addr = EntityAddr::new_smart_contract(hash_addr); + let initial_state = create_entry_point(entity_addr, "ep1"); + let (contract_runtime, state_hash) = create_test_state(rng, initial_state); + let request = EntryPointsRequest::new(state_hash, "ep2".to_string(), hash_addr); + let res = contract_runtime + .data_access_layer() + .entry_point_exists(request); + assert!(matches!(res, EntryPointExistsResult::ValueNotFound { .. })); + } + #[test] fn returns_trie_or_chunk() { let rng = &mut TestRng::new(); diff --git a/node/src/components/transaction_acceptor.rs b/node/src/components/transaction_acceptor.rs index 24dde46262..ea889d770e 100644 --- a/node/src/components/transaction_acceptor.rs +++ b/node/src/components/transaction_acceptor.rs @@ -16,7 +16,7 @@ use casper_storage::data_access_layer::{balance::BalanceHandling, BalanceRequest use casper_types::{ account::AccountHash, addressable_entity::AddressableEntity, system::auction::ARG_AMOUNT, AddressableEntityHash, AddressableEntityIdentifier, BlockHeader, Chainspec, EntityAddr, - EntityKind, EntityVersion, EntityVersionKey, EntryPoint, EntryPointAddr, ExecutableDeployItem, + EntityKind, EntityVersion, EntityVersionKey, ExecutableDeployItem, ExecutableDeployItemIdentifier, InitiatorAddr, Key, Package, PackageAddr, PackageHash, PackageIdentifier, Timestamp, Transaction, TransactionEntryPoint, TransactionInvocationTarget, TransactionRuntime, TransactionTarget, DEFAULT_ENTRY_POINT_NAME, U512, @@ -621,33 +621,20 @@ impl TransactionAcceptor { }; match maybe_entry_point_name { - Some(entry_point_name) => { - match EntryPointAddr::new_v1_entry_point_addr( - EntityAddr::SmartContract(contract_hash.value()), - &entry_point_name, - ) { - Ok(entry_point_addr) => effect_builder - .get_entry_point_value( - *block_header.state_root_hash(), - Key::EntryPoint(entry_point_addr), - ) - .event(move |entry_point_result| Event::GetEntryPointResult { - event_metadata, - block_header, - is_payment, - entry_point_name, - addressable_entity, - maybe_entry_point: entry_point_result.into_v1_entry_point(), - }), - Err(_) => { - let error = Error::parameter_failure( - &block_header, - ParameterFailure::NoSuchEntryPoint { entry_point_name }, - ); - self.reject_transaction(effect_builder, *event_metadata, error) - } - } - } + Some(entry_point_name) => effect_builder + .does_entry_point_exist( + *block_header.state_root_hash(), + contract_hash.value(), + entry_point_name.clone(), + ) + .event(move |entry_point_result| Event::GetEntryPointResult { + event_metadata, + block_header, + is_payment, + entry_point_name, + addressable_entity, + entry_point_exists: entry_point_result.is_some(), + }), None => { if is_payment { @@ -667,31 +654,19 @@ impl TransactionAcceptor { is_payment: bool, entry_point_name: String, addressable_entity: AddressableEntity, - maybe_entry_point: Option, + entry_point_exist: bool, ) -> Effects { match addressable_entity.kind() { EntityKind::SmartContract(TransactionRuntime::VmCasperV1) | EntityKind::Account(_) | EntityKind::System(_) => { - let entry_point = match maybe_entry_point { - Some(entry_point) => entry_point, - None => { - let error = Error::parameter_failure( - &block_header, - ParameterFailure::NoSuchEntryPoint { entry_point_name }, - ); - return self.reject_transaction(effect_builder, *event_metadata, error); - } - }; - - if entry_point_name != *entry_point.name() { + if !entry_point_exist { let error = Error::parameter_failure( &block_header, ParameterFailure::NoSuchEntryPoint { entry_point_name }, ); return self.reject_transaction(effect_builder, *event_metadata, error); - }; - + } if is_payment { return self.verify_body(effect_builder, event_metadata, block_header); } @@ -1027,7 +1002,7 @@ impl Component for TransactionAcceptor { is_payment, entry_point_name, addressable_entity, - maybe_entry_point, + entry_point_exists, } => self.handle_get_entry_point_result( effect_builder, event_metadata, @@ -1035,7 +1010,7 @@ impl Component for TransactionAcceptor { is_payment, entry_point_name, addressable_entity, - maybe_entry_point, + entry_point_exists, ), Event::PutToStorageResult { event_metadata, diff --git a/node/src/components/transaction_acceptor/event.rs b/node/src/components/transaction_acceptor/event.rs index 46380239ad..9824d96091 100644 --- a/node/src/components/transaction_acceptor/event.rs +++ b/node/src/components/transaction_acceptor/event.rs @@ -3,8 +3,8 @@ use std::fmt::{self, Display, Formatter}; use serde::Serialize; use casper_types::{ - AddressableEntity, AddressableEntityHash, BlockHeader, EntityVersion, EntryPoint, Package, - PackageHash, Timestamp, Transaction, U512, + AddressableEntity, AddressableEntityHash, BlockHeader, EntityVersion, Package, PackageHash, + Timestamp, Transaction, U512, }; use super::{Error, Source}; @@ -102,7 +102,7 @@ pub(crate) enum Event { is_payment: bool, entry_point_name: String, addressable_entity: AddressableEntity, - maybe_entry_point: Option, + entry_point_exists: bool, }, } diff --git a/node/src/components/transaction_acceptor/tests.rs b/node/src/components/transaction_acceptor/tests.rs index c58e358c1e..f591b9c9ba 100644 --- a/node/src/components/transaction_acceptor/tests.rs +++ b/node/src/components/transaction_acceptor/tests.rs @@ -23,8 +23,8 @@ use tokio::time; use casper_execution_engine::engine_state::MAX_PAYMENT_AMOUNT; use casper_storage::{ data_access_layer::{ - AddressableEntityResult, BalanceIdentifier, BalanceResult, EntryPointsResult, ProofsResult, - QueryResult, + AddressableEntityResult, BalanceIdentifier, BalanceResult, EntryPointExistsResult, + ProofsResult, QueryResult, }, tracking_copy::TrackingCopyError, }; @@ -35,11 +35,10 @@ use casper_types::{ contracts::NamedKeys, global_state::TrieMerkleProof, testing::TestRng, - Block, BlockV2, CLValue, Chainspec, ChainspecRawBytes, Contract, Deploy, EntryPointValue, - EraId, HashAddr, InvalidDeploy, InvalidTransaction, InvalidTransactionV1, Package, PricingMode, - ProtocolVersion, PublicKey, SecretKey, StoredValue, TestBlockBuilder, TimeDiff, Timestamp, - Transaction, TransactionConfig, TransactionRuntime, TransactionV1, TransactionV1Builder, URef, - U512, + Block, BlockV2, CLValue, Chainspec, ChainspecRawBytes, Contract, Deploy, EraId, HashAddr, + InvalidDeploy, InvalidTransaction, InvalidTransactionV1, Package, PricingMode, ProtocolVersion, + PublicKey, SecretKey, StoredValue, TestBlockBuilder, TimeDiff, Timestamp, Transaction, + TransactionConfig, TransactionRuntime, TransactionV1, TransactionV1Builder, URef, U512, }; use super::*; @@ -985,7 +984,7 @@ impl reactor::Reactor for Reactor { }; responder.respond(result).ignore() } - ContractRuntimeRequest::GetEntryPoint { + ContractRuntimeRequest::GetEntryPointExists { state_root_hash: _, responder, .. @@ -995,13 +994,13 @@ impl reactor::Reactor for Reactor { .contract_scenario() .expect("must get contract scenario"); let result = match contract_scenario { - ContractScenario::Valid => EntryPointsResult::Success { - entry_point: EntryPointValue::V1CasperVm(EntryPoint::default()), - }, + ContractScenario::Valid => EntryPointExistsResult::Success, ContractScenario::MissingContractAtHash | ContractScenario::MissingContractAtName | ContractScenario::MissingEntryPoint => { - EntryPointsResult::ValueNotFound("entry point not found".to_string()) + EntryPointExistsResult::ValueNotFound( + "entry point not found".to_string(), + ) } }; responder.respond(result).ignore() diff --git a/node/src/effect.rs b/node/src/effect.rs index f312d21b39..b1b6099fc2 100644 --- a/node/src/effect.rs +++ b/node/src/effect.rs @@ -133,9 +133,9 @@ use casper_types::{ execution::{Effects as ExecutionEffects, ExecutionResult}, Approval, AvailableBlockRange, Block, BlockHash, BlockHeader, BlockSignatures, BlockSynchronizerStatus, BlockV2, ChainspecRawBytes, DeployHash, Digest, EntityAddr, EraId, - ExecutionInfo, FinalitySignature, FinalitySignatureId, FinalitySignatureV2, Key, NextUpgrade, - Package, ProtocolUpgradeConfig, PublicKey, TimeDiff, Timestamp, Transaction, TransactionHash, - TransactionId, Transfer, U512, + ExecutionInfo, FinalitySignature, FinalitySignatureId, FinalitySignatureV2, HashAddr, Key, + NextUpgrade, Package, ProtocolUpgradeConfig, PublicKey, TimeDiff, Timestamp, Transaction, + TransactionHash, TransactionId, Transfer, U512, }; use crate::{ @@ -169,7 +169,7 @@ use announcements::{ PeerBehaviorAnnouncement, QueueDumpFormat, TransactionAcceptorAnnouncement, TransactionBufferAnnouncement, UnexecutedBlockAnnouncement, UpgradeWatcherAnnouncement, }; -use casper_storage::data_access_layer::EntryPointsResult; +use casper_storage::data_access_layer::EntryPointExistsResult; use diagnostics_port::DumpConsensusStateRequest; use requests::{ AcceptTransactionRequest, BeginGossipRequest, BlockAccumulatorRequest, @@ -1979,18 +1979,20 @@ impl EffectBuilder { } /// Retrieves an `EntryPointValue` from under the given key in global state if present. - pub(crate) async fn get_entry_point_value( + pub(crate) async fn does_entry_point_exist( self, state_root_hash: Digest, - key: Key, - ) -> EntryPointsResult + contract_hash: HashAddr, + entry_point_name: String, + ) -> EntryPointExistsResult where REv: From, { self.make_request( - |responder| ContractRuntimeRequest::GetEntryPoint { + |responder| ContractRuntimeRequest::GetEntryPointExists { state_root_hash, - key, + contract_hash, + entry_point_name, responder, }, QueueKind::ContractRuntime, diff --git a/node/src/effect/requests.rs b/node/src/effect/requests.rs index 5b8dfadf2d..5ad6b6858a 100644 --- a/node/src/effect/requests.rs +++ b/node/src/effect/requests.rs @@ -11,6 +11,7 @@ use std::{ }; use datasize::DataSize; +use hex_fmt::HexFmt; use serde::Serialize; use smallvec::SmallVec; use static_assertions::const_assert; @@ -23,7 +24,7 @@ use casper_storage::{ data_access_layer::{ prefixed_values::{PrefixedValuesRequest, PrefixedValuesResult}, tagged_values::{TaggedValuesRequest, TaggedValuesResult}, - AddressableEntityResult, BalanceRequest, BalanceResult, EntryPointsResult, + AddressableEntityResult, BalanceRequest, BalanceResult, EntryPointExistsResult, EraValidatorsRequest, EraValidatorsResult, ExecutionResultsChecksumResult, PutTrieRequest, PutTrieResult, QueryRequest, QueryResult, SeigniorageRecipientsRequest, SeigniorageRecipientsResult, TrieRequest, TrieResult, @@ -33,8 +34,8 @@ use casper_storage::{ use casper_types::{ execution::ExecutionResult, Approval, AvailableBlockRange, Block, BlockHash, BlockHeader, BlockSignatures, BlockSynchronizerStatus, BlockV2, ChainspecRawBytes, DeployHash, Digest, - DisplayIter, EntityAddr, EraId, ExecutionInfo, FinalitySignature, FinalitySignatureId, Key, - NextUpgrade, ProtocolUpgradeConfig, PublicKey, TimeDiff, Timestamp, Transaction, + DisplayIter, EntityAddr, EraId, ExecutionInfo, FinalitySignature, FinalitySignatureId, + HashAddr, NextUpgrade, ProtocolUpgradeConfig, PublicKey, TimeDiff, Timestamp, Transaction, TransactionHash, TransactionId, Transfer, }; @@ -831,12 +832,13 @@ pub(crate) enum ContractRuntimeRequest { entity_addr: EntityAddr, responder: Responder, }, - /// Returns a singular entry point based under the given state root hash and entry + /// Returns information if an entry point exists under the given state root hash and entry /// point key. - GetEntryPoint { + GetEntryPointExists { state_root_hash: Digest, - key: Key, - responder: Responder, + contract_hash: HashAddr, + entry_point_name: String, + responder: Responder, }, /// Get a trie or chunk by its ID. GetTrie { @@ -958,15 +960,17 @@ impl Display for ContractRuntimeRequest { ContractRuntimeRequest::GetEraGasPrice { era_id, .. } => { write!(formatter, "Get gas price for era {}", era_id) } - ContractRuntimeRequest::GetEntryPoint { + ContractRuntimeRequest::GetEntryPointExists { state_root_hash, - key, + contract_hash, + entry_point_name, .. } => { + let formatted_contract_hash = HexFmt(contract_hash); write!( formatter, - "get entry point {} under {}", - key, state_root_hash + "get entry point {}-{} under {}", + formatted_contract_hash, entry_point_name, state_root_hash ) } ContractRuntimeRequest::DoProtocolUpgrade { diff --git a/storage/src/data_access_layer.rs b/storage/src/data_access_layer.rs index 647f688fd0..04d361f53b 100644 --- a/storage/src/data_access_layer.rs +++ b/storage/src/data_access_layer.rs @@ -17,6 +17,7 @@ pub mod bids; mod block_global; /// Block rewards provider. pub mod block_rewards; +mod contract; mod entry_points; /// Era validators provider. pub mod era_validators; @@ -64,7 +65,8 @@ pub use balance_hold::{ pub use bids::{BidsRequest, BidsResult}; pub use block_global::{BlockGlobalKind, BlockGlobalRequest, BlockGlobalResult}; pub use block_rewards::{BlockRewardsError, BlockRewardsRequest, BlockRewardsResult}; -pub use entry_points::{EntryPointsRequest, EntryPointsResult}; +pub use contract::{ContractRequest, ContractResult}; +pub use entry_points::{EntryPointExistsResult, EntryPointResult, EntryPointsRequest}; pub use era_validators::{EraValidatorsRequest, EraValidatorsResult}; pub use execution_results_checksum::{ ExecutionResultsChecksumRequest, ExecutionResultsChecksumResult, diff --git a/storage/src/data_access_layer/contract.rs b/storage/src/data_access_layer/contract.rs new file mode 100644 index 0000000000..6adc1fd980 --- /dev/null +++ b/storage/src/data_access_layer/contract.rs @@ -0,0 +1,40 @@ +use crate::tracking_copy::TrackingCopyError; +use casper_types::{Contract, Digest, Key}; + +/// Represents a request to obtain contract. +pub struct ContractRequest { + state_hash: Digest, + key: Key, +} + +impl ContractRequest { + /// ctor + pub fn new(state_hash: Digest, key: Key) -> Self { + ContractRequest { state_hash, key } + } + + /// Returns key. + pub fn key(&self) -> Key { + self.key + } + /// Returns state root hash. + pub fn state_hash(&self) -> Digest { + self.state_hash + } +} + +/// Represents a result of a `contract` request. +#[derive(Debug)] +pub enum ContractResult { + /// Invalid state root hash. + RootNotFound, + /// Value not found. + ValueNotFound(String), + /// This variant will be returned if the contract was found. + Success { + /// A contract. + contract: Contract, + }, + /// Failure result. + Failure(TrackingCopyError), +} diff --git a/storage/src/data_access_layer/entry_points.rs b/storage/src/data_access_layer/entry_points.rs index c6fd4d93bc..fc9272ac36 100644 --- a/storage/src/data_access_layer/entry_points.rs +++ b/storage/src/data_access_layer/entry_points.rs @@ -1,17 +1,22 @@ use crate::tracking_copy::TrackingCopyError; -use casper_types::{Digest, EntryPoint, EntryPointValue, Key}; +use casper_types::{Digest, EntryPointValue, HashAddr}; /// Represents a request to obtain entry points. #[derive(Debug, Clone, PartialEq, Eq)] pub struct EntryPointsRequest { state_hash: Digest, - key: Key, + entry_point_name: String, + contract_hash: HashAddr, } impl EntryPointsRequest { - /// Creates new request. - pub fn new(state_hash: Digest, key: Key) -> Self { - EntryPointsRequest { state_hash, key } + /// ctor + pub fn new(state_hash: Digest, entry_point_name: String, contract_hash: HashAddr) -> Self { + EntryPointsRequest { + state_hash, + entry_point_name, + contract_hash, + } } /// Returns state root hash. @@ -19,15 +24,20 @@ impl EntryPointsRequest { self.state_hash } - /// Returns key. - pub fn key(&self) -> Key { - self.key + /// Returns entry_point_name. + pub fn entry_point_name(&self) -> &str { + &self.entry_point_name + } + + /// Returns contract_hash. + pub fn contract_hash(&self) -> HashAddr { + self.contract_hash } } /// Represents a result of a `entry_points` request. #[derive(Debug)] -pub enum EntryPointsResult { +pub enum EntryPointResult { /// Invalid state root hash. RootNotFound, /// Value not found. @@ -41,15 +51,22 @@ pub enum EntryPointsResult { Failure(TrackingCopyError), } -impl EntryPointsResult { - /// Returns the result based on a particular variant of entrypoint - pub fn into_v1_entry_point(self) -> Option { - if let Self::Success { entry_point } = self { - match entry_point { - EntryPointValue::V1CasperVm(entry_point) => Some(entry_point), - } - } else { - None - } +/// Represents a result of `entry_point_exists` request. +#[derive(Debug)] +pub enum EntryPointExistsResult { + /// Invalid state root hash. + RootNotFound, + /// Value not found. + ValueNotFound(String), + /// This variant will be returned if the entry point was found. + Success, + /// Failure result. + Failure(TrackingCopyError), +} + +impl EntryPointExistsResult { + /// Returns `true` if the result is `Success`. + pub fn is_some(self) -> bool { + matches!(self, Self::Success { .. }) } } diff --git a/storage/src/global_state/state/mod.rs b/storage/src/global_state/state/mod.rs index a8888a4c1f..1201b5a894 100644 --- a/storage/src/global_state/state/mod.rs +++ b/storage/src/global_state/state/mod.rs @@ -36,8 +36,9 @@ use casper_types::{ }, AUCTION, HANDLE_PAYMENT, MINT, }, - Account, AddressableEntity, BlockGlobalAddr, CLValue, Digest, EntityAddr, HoldsEpoch, Key, - KeyTag, Phase, PublicKey, RuntimeArgs, StoredValue, SystemHashRegistry, U512, + Account, AddressableEntity, BlockGlobalAddr, CLValue, Digest, EntityAddr, EntryPoint, + EntryPointAddr, EntryPointValue, HoldsEpoch, Key, KeyTag, Phase, PublicKey, RuntimeArgs, + StoredValue, SystemHashRegistry, U512, }; #[cfg(test)] @@ -59,7 +60,8 @@ use crate::{ BalanceHoldKind, BalanceHoldMode, BalanceHoldRequest, BalanceHoldResult, BalanceIdentifier, BalanceRequest, BalanceResult, BidsRequest, BidsResult, BlockGlobalKind, BlockGlobalRequest, BlockGlobalResult, BlockRewardsError, BlockRewardsRequest, - BlockRewardsResult, EntryPointsRequest, EntryPointsResult, EraValidatorsRequest, + BlockRewardsResult, ContractRequest, ContractResult, EntryPointExistsResult, + EntryPointResult, EntryPointsRequest, EraValidatorsRequest, ExecutionResultsChecksumRequest, ExecutionResultsChecksumResult, FeeError, FeeRequest, FeeResult, FlushRequest, FlushResult, GenesisRequest, GenesisResult, HandleRefundMode, HandleRefundRequest, HandleRefundResult, InsufficientBalanceHandling, MessageTopicsRequest, @@ -1885,27 +1887,94 @@ pub trait StateProvider: Send + Sync + Sized { } /// Gets an entry point value. - fn entry_point(&self, request: EntryPointsRequest) -> EntryPointsResult { - let state_hash = request.state_hash(); - let query_request = QueryRequest::new(state_hash, request.key(), vec![]); + fn entry_point(&self, request: EntryPointsRequest) -> EntryPointResult { + let state_root_hash = request.state_hash(); + let contract_hash = request.contract_hash(); + let entry_point_name = request.entry_point_name(); + match EntryPointAddr::new_v1_entry_point_addr( + EntityAddr::SmartContract(contract_hash), + entry_point_name, + ) { + Ok(entry_point_addr) => { + let key = Key::EntryPoint(entry_point_addr); + let query_request = QueryRequest::new(request.state_hash(), key, vec![]); + //We first check if the entry point exists as a stand alone 2.x entity + match self.query(query_request) { + QueryResult::RootNotFound => EntryPointResult::RootNotFound, + QueryResult::ValueNotFound(query_result_not_found_msg) => { + //If the entry point was not found as a 2.x entity, we check if it exists + // as part of a 1.x contract + let contract_key = Key::Hash(contract_hash); + let contract_request = ContractRequest::new(state_root_hash, contract_key); + match self.contract(contract_request) { + ContractResult::Failure(tce) => EntryPointResult::Failure(tce), + ContractResult::ValueNotFound(_) => { + EntryPointResult::ValueNotFound(query_result_not_found_msg) + } + ContractResult::RootNotFound => EntryPointResult::RootNotFound, + ContractResult::Success { contract } => { + match contract.entry_points().get(entry_point_name) { + Some(contract_entry_point) => EntryPointResult::Success { + entry_point: EntryPointValue::V1CasperVm(EntryPoint::from( + contract_entry_point, + )), + }, + None => { + EntryPointResult::ValueNotFound(query_result_not_found_msg) + } + } + } + } + } + QueryResult::Failure(tce) => EntryPointResult::Failure(tce), + QueryResult::Success { value, .. } => { + if let StoredValue::EntryPoint(entry_point) = *value { + EntryPointResult::Success { entry_point } + } else { + error!("Expected to get entry point value received other variant"); + EntryPointResult::Failure( + TrackingCopyError::UnexpectedStoredValueVariant, + ) + } + } + } + } + Err(_) => EntryPointResult::Failure( + //TODO maybe we can have a better error type here + TrackingCopyError::ValueNotFound("Entry point not found".to_string()), + ), + } + } + + /// Gets an contract value. + fn contract(&self, request: ContractRequest) -> ContractResult { + let query_request = QueryRequest::new(request.state_hash(), request.key(), vec![]); match self.query(query_request) { - QueryResult::RootNotFound => EntryPointsResult::RootNotFound, - QueryResult::ValueNotFound(msg) => EntryPointsResult::ValueNotFound(msg), - QueryResult::Failure(tce) => EntryPointsResult::Failure(tce), + QueryResult::RootNotFound => ContractResult::RootNotFound, + QueryResult::ValueNotFound(msg) => ContractResult::ValueNotFound(msg), + QueryResult::Failure(tce) => ContractResult::Failure(tce), QueryResult::Success { value, .. } => { - if let StoredValue::EntryPoint(entry_point_value) = *value { - EntryPointsResult::Success { - entry_point: entry_point_value, - } + if let StoredValue::Contract(contract) = *value { + ContractResult::Success { contract } } else { - error!("Expected to get entry point value received other variant"); - EntryPointsResult::Failure(TrackingCopyError::UnexpectedStoredValueVariant) + error!("Expected to get contract value received other variant"); + ContractResult::Failure(TrackingCopyError::UnexpectedStoredValueVariant) } } } } + /// Gets an entry point value. + fn entry_point_exists(&self, request: EntryPointsRequest) -> EntryPointExistsResult { + match self.entry_point(request) { + EntryPointResult::RootNotFound => EntryPointExistsResult::RootNotFound, + EntryPointResult::ValueNotFound(msg) => EntryPointExistsResult::ValueNotFound(msg), + EntryPointResult::Success { .. } => EntryPointExistsResult::Success, + EntryPointResult::Failure(error) => EntryPointExistsResult::Failure(error), + } + } + /// Gets total supply. fn total_supply(&self, request: TotalSupplyRequest) -> TotalSupplyResult { let state_hash = request.state_hash(); diff --git a/types/src/contracts.rs b/types/src/contracts.rs index 0809a2cb28..cd0b7c467b 100644 --- a/types/src/contracts.rs +++ b/types/src/contracts.rs @@ -1186,11 +1186,17 @@ impl Default for EntryPoints { impl From for EntityEntryPoint { fn from(value: EntryPoint) -> Self { + EntityEntryPoint::from(&value) + } +} + +impl From<&EntryPoint> for EntityEntryPoint { + fn from(value: &EntryPoint) -> Self { EntityEntryPoint::new( - value.name, - value.args, - value.ret, - value.access, + value.name.clone(), + value.args.clone(), + value.ret.clone(), + value.access.clone(), value.entry_point_type, EntryPointPayment::Caller, ) From 983beba94e80495f0192d7016b2c4c6988570f0b Mon Sep 17 00:00:00 2001 From: Jakub Zajkowski Date: Thu, 21 Nov 2024 11:06:46 +0100 Subject: [PATCH 2/2] Applied CR remarks --- node/src/components/contract_runtime.rs | 9 ++-- .../components/contract_runtime/operations.rs | 4 +- node/src/components/contract_runtime/tests.rs | 10 ++-- storage/src/data_access_layer.rs | 4 +- storage/src/data_access_layer/entry_points.rs | 54 +++++++++++++++++-- storage/src/global_state/state/mod.rs | 10 ++-- 6 files changed, 71 insertions(+), 20 deletions(-) diff --git a/node/src/components/contract_runtime.rs b/node/src/components/contract_runtime.rs index c3cd95b7f8..b78b0f38f1 100644 --- a/node/src/components/contract_runtime.rs +++ b/node/src/components/contract_runtime.rs @@ -32,7 +32,7 @@ use casper_execution_engine::engine_state::{EngineConfigBuilder, ExecutionEngine use casper_storage::{ data_access_layer::{ AddressableEntityRequest, AddressableEntityResult, BlockStore, DataAccessLayer, - EntryPointsRequest, ExecutionResultsChecksumRequest, FlushRequest, FlushResult, + EntryPointExistsRequest, ExecutionResultsChecksumRequest, FlushRequest, FlushResult, GenesisRequest, GenesisResult, TrieRequest, }, global_state::{ @@ -463,8 +463,11 @@ impl ContractRuntime { let data_access_layer = Arc::clone(&self.data_access_layer); async move { let start = Instant::now(); - let request = - EntryPointsRequest::new(state_root_hash, entry_point_name, contract_hash); + let request = EntryPointExistsRequest::new( + state_root_hash, + entry_point_name, + contract_hash, + ); let result = data_access_layer.entry_point_exists(request); metrics.entry_points.observe(start.elapsed().as_secs_f64()); trace!(?result, "get addressable entity"); diff --git a/node/src/components/contract_runtime/operations.rs b/node/src/components/contract_runtime/operations.rs index d074d3a1ff..95db0267ec 100644 --- a/node/src/components/contract_runtime/operations.rs +++ b/node/src/components/contract_runtime/operations.rs @@ -17,7 +17,7 @@ use casper_storage::{ mint::BalanceIdentifierTransferArgs, AuctionMethod, BalanceHoldKind, BalanceHoldRequest, BalanceIdentifier, BalanceRequest, BiddingRequest, BlockGlobalRequest, BlockGlobalResult, BlockRewardsRequest, - BlockRewardsResult, DataAccessLayer, EntryPointResult, EntryPointsRequest, + BlockRewardsResult, DataAccessLayer, EntryPointRequest, EntryPointResult, EraValidatorsRequest, EraValidatorsResult, EvictItem, FeeRequest, FeeResult, FlushRequest, HandleFeeMode, HandleFeeRequest, HandleRefundMode, HandleRefundRequest, InsufficientBalanceHandling, ProofHandling, PruneRequest, PruneResult, StepRequest, @@ -1249,7 +1249,7 @@ fn invoked_contract_will_pay( Some((hash_addr, entry_point_name)) => (hash_addr, entry_point_name), }; let entity_addr = EntityAddr::new_smart_contract(hash_addr); - let entry_point_request = EntryPointsRequest::new(state_root_hash, entry_point_name, hash_addr); + let entry_point_request = EntryPointRequest::new(state_root_hash, entry_point_name, hash_addr); let entry_point_response = state_provider.entry_point(entry_point_request); match entry_point_response { EntryPointResult::RootNotFound => Err(StateResultError::RootNotFound), diff --git a/node/src/components/contract_runtime/tests.rs b/node/src/components/contract_runtime/tests.rs index 13e12ef9f2..5d33ea1ab6 100644 --- a/node/src/components/contract_runtime/tests.rs +++ b/node/src/components/contract_runtime/tests.rs @@ -405,7 +405,7 @@ mod test_mod { use tempfile::tempdir; use casper_storage::{ - data_access_layer::{EntryPointExistsResult, EntryPointsRequest}, + data_access_layer::{EntryPointExistsRequest, EntryPointExistsResult}, global_state::{ state::{CommitProvider, StateProvider}, trie::Trie, @@ -605,7 +605,8 @@ mod test_mod { ProtocolVersion::V2_0_0, ); let (contract_runtime, state_hash) = create_test_state(rng, initial_state); - let request = EntryPointsRequest::new(state_hash, entry_point_name.to_string(), hash_addr); + let request = + EntryPointExistsRequest::new(state_hash, entry_point_name.to_string(), hash_addr); let res = contract_runtime .data_access_layer() .entry_point_exists(request); @@ -620,7 +621,8 @@ mod test_mod { let entry_point_name = "ep1"; let initial_state = create_entry_point(entity_addr, entry_point_name); let (contract_runtime, state_hash) = create_test_state(rng, initial_state); - let request = EntryPointsRequest::new(state_hash, entry_point_name.to_string(), hash_addr); + let request = + EntryPointExistsRequest::new(state_hash, entry_point_name.to_string(), hash_addr); let res = contract_runtime .data_access_layer() .entry_point_exists(request); @@ -634,7 +636,7 @@ mod test_mod { let entity_addr = EntityAddr::new_smart_contract(hash_addr); let initial_state = create_entry_point(entity_addr, "ep1"); let (contract_runtime, state_hash) = create_test_state(rng, initial_state); - let request = EntryPointsRequest::new(state_hash, "ep2".to_string(), hash_addr); + let request = EntryPointExistsRequest::new(state_hash, "ep2".to_string(), hash_addr); let res = contract_runtime .data_access_layer() .entry_point_exists(request); diff --git a/storage/src/data_access_layer.rs b/storage/src/data_access_layer.rs index 04d361f53b..85b83ff430 100644 --- a/storage/src/data_access_layer.rs +++ b/storage/src/data_access_layer.rs @@ -66,7 +66,9 @@ pub use bids::{BidsRequest, BidsResult}; pub use block_global::{BlockGlobalKind, BlockGlobalRequest, BlockGlobalResult}; pub use block_rewards::{BlockRewardsError, BlockRewardsRequest, BlockRewardsResult}; pub use contract::{ContractRequest, ContractResult}; -pub use entry_points::{EntryPointExistsResult, EntryPointResult, EntryPointsRequest}; +pub use entry_points::{ + EntryPointExistsRequest, EntryPointExistsResult, EntryPointRequest, EntryPointResult, +}; pub use era_validators::{EraValidatorsRequest, EraValidatorsResult}; pub use execution_results_checksum::{ ExecutionResultsChecksumRequest, ExecutionResultsChecksumResult, diff --git a/storage/src/data_access_layer/entry_points.rs b/storage/src/data_access_layer/entry_points.rs index fc9272ac36..5ebc37d830 100644 --- a/storage/src/data_access_layer/entry_points.rs +++ b/storage/src/data_access_layer/entry_points.rs @@ -1,18 +1,18 @@ use crate::tracking_copy::TrackingCopyError; use casper_types::{Digest, EntryPointValue, HashAddr}; -/// Represents a request to obtain entry points. +/// Represents a request to obtain entry point. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct EntryPointsRequest { +pub struct EntryPointRequest { state_hash: Digest, entry_point_name: String, contract_hash: HashAddr, } -impl EntryPointsRequest { +impl EntryPointRequest { /// ctor pub fn new(state_hash: Digest, entry_point_name: String, contract_hash: HashAddr) -> Self { - EntryPointsRequest { + EntryPointRequest { state_hash, entry_point_name, contract_hash, @@ -35,7 +35,17 @@ impl EntryPointsRequest { } } -/// Represents a result of a `entry_points` request. +impl From for EntryPointRequest { + fn from(value: EntryPointExistsRequest) -> Self { + EntryPointRequest { + state_hash: value.state_hash, + entry_point_name: value.entry_point_name, + contract_hash: value.contract_hash, + } + } +} + +/// Represents a result of a `entry_point` request. #[derive(Debug)] pub enum EntryPointResult { /// Invalid state root hash. @@ -51,6 +61,40 @@ pub enum EntryPointResult { Failure(TrackingCopyError), } +/// Represents a request to check entry point existence. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EntryPointExistsRequest { + state_hash: Digest, + entry_point_name: String, + contract_hash: HashAddr, +} + +impl EntryPointExistsRequest { + /// ctor + pub fn new(state_hash: Digest, entry_point_name: String, contract_hash: HashAddr) -> Self { + EntryPointExistsRequest { + state_hash, + entry_point_name, + contract_hash, + } + } + + /// Returns state root hash. + pub fn state_hash(&self) -> Digest { + self.state_hash + } + + /// Returns entry_point_name. + pub fn entry_point_name(&self) -> &str { + &self.entry_point_name + } + + /// Returns contract_hash. + pub fn contract_hash(&self) -> HashAddr { + self.contract_hash + } +} + /// Represents a result of `entry_point_exists` request. #[derive(Debug)] pub enum EntryPointExistsResult { diff --git a/storage/src/global_state/state/mod.rs b/storage/src/global_state/state/mod.rs index 1201b5a894..29a558d6c3 100644 --- a/storage/src/global_state/state/mod.rs +++ b/storage/src/global_state/state/mod.rs @@ -60,8 +60,8 @@ use crate::{ BalanceHoldKind, BalanceHoldMode, BalanceHoldRequest, BalanceHoldResult, BalanceIdentifier, BalanceRequest, BalanceResult, BidsRequest, BidsResult, BlockGlobalKind, BlockGlobalRequest, BlockGlobalResult, BlockRewardsError, BlockRewardsRequest, - BlockRewardsResult, ContractRequest, ContractResult, EntryPointExistsResult, - EntryPointResult, EntryPointsRequest, EraValidatorsRequest, + BlockRewardsResult, ContractRequest, ContractResult, EntryPointExistsRequest, + EntryPointExistsResult, EntryPointRequest, EntryPointResult, EraValidatorsRequest, ExecutionResultsChecksumRequest, ExecutionResultsChecksumResult, FeeError, FeeRequest, FeeResult, FlushRequest, FlushResult, GenesisRequest, GenesisResult, HandleRefundMode, HandleRefundRequest, HandleRefundResult, InsufficientBalanceHandling, MessageTopicsRequest, @@ -1887,7 +1887,7 @@ pub trait StateProvider: Send + Sync + Sized { } /// Gets an entry point value. - fn entry_point(&self, request: EntryPointsRequest) -> EntryPointResult { + fn entry_point(&self, request: EntryPointRequest) -> EntryPointResult { let state_root_hash = request.state_hash(); let contract_hash = request.contract_hash(); let entry_point_name = request.entry_point_name(); @@ -1966,8 +1966,8 @@ pub trait StateProvider: Send + Sync + Sized { } /// Gets an entry point value. - fn entry_point_exists(&self, request: EntryPointsRequest) -> EntryPointExistsResult { - match self.entry_point(request) { + fn entry_point_exists(&self, request: EntryPointExistsRequest) -> EntryPointExistsResult { + match self.entry_point(request.into()) { EntryPointResult::RootNotFound => EntryPointExistsResult::RootNotFound, EntryPointResult::ValueNotFound(msg) => EntryPointExistsResult::ValueNotFound(msg), EntryPointResult::Success { .. } => EntryPointExistsResult::Success,