diff --git a/chain/rosetta-rpc/src/adapters/mod.rs b/chain/rosetta-rpc/src/adapters/mod.rs index 78335ae6949..0efd2cf9bde 100644 --- a/chain/rosetta-rpc/src/adapters/mod.rs +++ b/chain/rosetta-rpc/src/adapters/mod.rs @@ -500,8 +500,9 @@ impl From for Vec { operations.extend(delegated_operations); } // TODO(#8469): Implement delegate action support, for now they are ignored. - near_primitives::transaction::Action::DeployGlobalContract(_action) => { - // TODO(#12639): Implement global contract deploys support, ignored for now. + near_primitives::transaction::Action::DeployGlobalContract(_) + | near_primitives::transaction::Action::UseGlobalContract(_) => { + // TODO(#12639): Implement global contract support, ignored for now. } } } diff --git a/core/primitives/src/action/mod.rs b/core/primitives/src/action/mod.rs index a250f7465d1..f5dcbdcb156 100644 --- a/core/primitives/src/action/mod.rs +++ b/core/primitives/src/action/mod.rs @@ -4,6 +4,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use near_crypto::PublicKey; use near_primitives_core::{ account::AccessKey, + hash::CryptoHash, serialize::dec_format, types::{AccountId, Balance, Gas}, }; @@ -159,6 +160,41 @@ impl fmt::Debug for DeployGlobalContractAction { } } +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + PartialEq, + Eq, + Clone, + ProtocolSchema, + Debug, +)] +#[repr(u8)] +pub enum GlobalContractIdentifier { + CodeHash(CryptoHash), + AccountId(AccountId), +} + +/// Use global contract action +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + PartialEq, + Eq, + Clone, + ProtocolSchema, + Debug, +)] +pub struct UseGlobalContractAction { + pub contract_identifier: GlobalContractIdentifier, +} + #[serde_as] #[derive( BorshSerialize, @@ -270,6 +306,7 @@ pub enum Action { DeleteAccount(DeleteAccountAction), Delegate(Box), DeployGlobalContract(DeployGlobalContractAction), + UseGlobalContract(Box), #[cfg(feature = "protocol_feature_nonrefundable_transfer_nep491")] /// Makes a non-refundable transfer for storage allowance. /// Only possible during new account creation. diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs index bc247fe1c95..07c5bacd948 100644 --- a/core/primitives/src/views.rs +++ b/core/primitives/src/views.rs @@ -5,7 +5,10 @@ //! from the source structure in the relevant `From` impl. use crate::account::{AccessKey, AccessKeyPermission, Account, FunctionCallPermission}; use crate::action::delegate::{DelegateAction, SignedDelegateAction}; -use crate::action::{DeployGlobalContractAction, GlobalContractDeployMode}; +use crate::action::{ + DeployGlobalContractAction, GlobalContractDeployMode, GlobalContractIdentifier, + UseGlobalContractAction, +}; use crate::bandwidth_scheduler::BandwidthRequests; use crate::block::{Block, BlockHeader, Tip}; use crate::block_header::BlockHeaderInnerLite; @@ -1179,6 +1182,12 @@ pub enum ActionView { #[serde_as(as = "Base64")] code: Vec, }, + UseGlobalContract { + code_hash: CryptoHash, + }, + UseGlobalContractByAccountId { + account_id: AccountId, + }, } impl From for ActionView { @@ -1224,6 +1233,14 @@ impl From for ActionView { } } } + Action::UseGlobalContract(action) => match action.contract_identifier { + GlobalContractIdentifier::CodeHash(code_hash) => { + ActionView::UseGlobalContract { code_hash } + } + GlobalContractIdentifier::AccountId(account_id) => { + ActionView::UseGlobalContractByAccountId { account_id } + } + }, } } } @@ -1277,6 +1294,16 @@ impl TryFrom for Action { deploy_mode: GlobalContractDeployMode::AccountId, }) } + ActionView::UseGlobalContract { code_hash } => { + Action::UseGlobalContract(Box::new(UseGlobalContractAction { + contract_identifier: GlobalContractIdentifier::CodeHash(code_hash), + })) + } + ActionView::UseGlobalContractByAccountId { account_id } => { + Action::UseGlobalContract(Box::new(UseGlobalContractAction { + contract_identifier: GlobalContractIdentifier::AccountId(account_id), + })) + } }) } } diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 4ffecb82b36..6cfcdf6a649 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -9,7 +9,7 @@ use near_crypto::PublicKey; use near_parameters::{AccountCreationConfig, ActionCosts, RuntimeConfig, RuntimeFeesConfig}; use near_primitives::account::{AccessKey, AccessKeyPermission, Account}; use near_primitives::action::delegate::{DelegateAction, SignedDelegateAction}; -use near_primitives::action::DeployGlobalContractAction; +use near_primitives::action::{DeployGlobalContractAction, UseGlobalContractAction}; use near_primitives::checked_feature; use near_primitives::config::ViewConfig; use near_primitives::errors::{ActionError, ActionErrorKind, InvalidAccessKeyError, RuntimeError}; @@ -664,6 +664,16 @@ pub(crate) fn action_deploy_global_contract( Ok(()) } +pub(crate) fn action_use_global_contract( + _state_update: &mut TrieUpdate, + _account: &mut Account, + _action: &UseGlobalContractAction, +) -> Result<(), RuntimeError> { + let _span = tracing::debug_span!(target: "runtime", "action_use_global_contract").entered(); + // TODO(#12716): implement global contract usage + Ok(()) +} + pub(crate) fn action_delete_account( state_update: &mut TrieUpdate, account: &mut Option, @@ -1040,7 +1050,8 @@ pub(crate) fn check_actor_permissions( | Action::Stake(_) | Action::AddKey(_) | Action::DeleteKey(_) - | Action::DeployGlobalContract(_) => { + | Action::DeployGlobalContract(_) + | Action::UseGlobalContract(_) => { if actor_id != account_id { return Err(ActionErrorKind::ActorNoPermission { account_id: account_id.clone(), @@ -1153,7 +1164,8 @@ pub(crate) fn check_account_existence( | Action::DeleteKey(_) | Action::DeleteAccount(_) | Action::Delegate(_) - | Action::DeployGlobalContract(_) => { + | Action::DeployGlobalContract(_) + | Action::UseGlobalContract(_) => { if account.is_none() { return Err(ActionErrorKind::AccountDoesNotExist { account_id: account_id.clone(), diff --git a/runtime/runtime/src/config.rs b/runtime/runtime/src/config.rs index d2fca9d17e2..11cbf568a14 100644 --- a/runtime/runtime/src/config.rs +++ b/runtime/runtime/src/config.rs @@ -150,8 +150,8 @@ pub fn total_send_fees( &delegate_action.receiver_id, )? } - DeployGlobalContract(_deploy_global_contract_action) => { - // TODO(#12717): implement send fees for global contract deploy + DeployGlobalContract(_) | UseGlobalContract(_) => { + // TODO(#12717): implement send fees for global contracts 1 } }; @@ -245,8 +245,8 @@ pub fn exec_fee(config: &RuntimeConfig, action: &Action, receiver_id: &AccountId DeleteKey(_) => fees.fee(ActionCosts::delete_key).exec_fee(), DeleteAccount(_) => fees.fee(ActionCosts::delete_account).exec_fee(), Delegate(_) => fees.fee(ActionCosts::delegate).exec_fee(), - DeployGlobalContract(_deploy_global_contract_action) => { - // TODO(#12717): implement exec fees for global contract deploys + DeployGlobalContract(_) | UseGlobalContract(_) => { + // TODO(#12717): implement exec fees for global contracts 1 } } diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 443edfce462..ec7caa8c8b9 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -487,6 +487,10 @@ impl Runtime { Action::DeployGlobalContract(deploy_global_contract) => { action_deploy_global_contract(account_id, deploy_global_contract, &mut result)?; } + Action::UseGlobalContract(use_global_contract) => { + let account = account.as_mut().expect(EXPECT_ACCOUNT_EXISTS); + action_use_global_contract(state_update, account, use_global_contract)?; + } Action::FunctionCall(function_call) => { let account = account.as_mut().expect(EXPECT_ACCOUNT_EXISTS); let contract = preparation_pipeline.get_contract( diff --git a/runtime/runtime/src/pipelining.rs b/runtime/runtime/src/pipelining.rs index f1448537610..0f2cb2d5f73 100644 --- a/runtime/runtime/src/pipelining.rs +++ b/runtime/runtime/src/pipelining.rs @@ -126,7 +126,7 @@ impl ReceiptPreparationPipeline { for (action_index, action) in actions.iter().enumerate() { let account_id = account_id.clone(); match action { - Action::DeployContract(_) => { + Action::DeployContract(_) | Action::UseGlobalContract(_) => { // FIXME: instead of blocking these accounts, move the handling of // deploy action into here, so that the necessary data dependencies can be // established. diff --git a/runtime/runtime/src/verifier.rs b/runtime/runtime/src/verifier.rs index c625933f577..ffbd017e7cb 100644 --- a/runtime/runtime/src/verifier.rs +++ b/runtime/runtime/src/verifier.rs @@ -437,6 +437,9 @@ pub fn validate_action( Action::DeployGlobalContract(a) => { validate_deploy_global_contract_action(limit_config, a, current_protocol_version) } + Action::UseGlobalContract(_) => { + validate_use_global_contract_action(current_protocol_version) + } Action::FunctionCall(a) => validate_function_call_action(limit_config, a), Action::Transfer(_) => Ok(()), #[cfg(feature = "protocol_feature_nonrefundable_transfer_nep491")] @@ -482,12 +485,8 @@ fn validate_deploy_global_contract_action( action: &DeployGlobalContractAction, current_protocol_version: ProtocolVersion, ) -> Result<(), ActionsValidationError> { - if !checked_feature!("stable", GlobalContracts, current_protocol_version) { - return Err(ActionsValidationError::UnsupportedProtocolFeature { - protocol_feature: "GlobalContracts".to_owned(), - version: current_protocol_version, - }); - } + check_global_contracts_enabled(current_protocol_version)?; + if action.code.len() as u64 > limit_config.max_contract_size { return Err(ActionsValidationError::ContractSizeExceeded { size: action.code.len() as u64, @@ -498,6 +497,13 @@ fn validate_deploy_global_contract_action( Ok(()) } +/// Validates `UseGlobalContractAction`. +fn validate_use_global_contract_action( + current_protocol_version: ProtocolVersion, +) -> Result<(), ActionsValidationError> { + check_global_contracts_enabled(current_protocol_version) +} + /// Validates `FunctionCallAction`. Checks that the method name length doesn't exceed the limit and /// the length of the arguments doesn't exceed the limit. fn validate_function_call_action( @@ -618,6 +624,18 @@ fn truncate_string(s: &str, limit: usize) -> String { unreachable!() } +fn check_global_contracts_enabled( + current_protocol_version: ProtocolVersion, +) -> Result<(), ActionsValidationError> { + if !checked_feature!("stable", GlobalContracts, current_protocol_version) { + return Err(ActionsValidationError::UnsupportedProtocolFeature { + protocol_feature: "GlobalContracts".to_owned(), + version: current_protocol_version, + }); + } + Ok(()) +} + #[cfg(test)] mod tests { use std::sync::Arc; diff --git a/tools/state-viewer/src/contract_accounts.rs b/tools/state-viewer/src/contract_accounts.rs index cb230760258..3f55a18170e 100644 --- a/tools/state-viewer/src/contract_accounts.rs +++ b/tools/state-viewer/src/contract_accounts.rs @@ -137,6 +137,7 @@ pub(crate) enum ActionType { DataReceipt, Delegate, DeployGlobalContract, + UseGlobalContract, } impl ContractAccount { @@ -352,6 +353,7 @@ fn try_find_actions_spawned_by_receipt( Action::DeployGlobalContract(_) => { ActionType::DeployGlobalContract } + Action::UseGlobalContract(_) => ActionType::UseGlobalContract, }; entry .actions