Skip to content

Commit

Permalink
feat: add receipt action for using global contract code (#12749)
Browse files Browse the repository at this point in the history
This PR is similar to #12737, but for using global contracts. 

Part of #12716.
  • Loading branch information
pugachAG authored Jan 16, 2025
1 parent 668b0d6 commit 8067cb6
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 60 deletions.
5 changes: 3 additions & 2 deletions chain/rosetta-rpc/src/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,9 @@ impl From<NearActions> for Vec<crate::models::Operation> {

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 contracts support, ignored for now.
}
}
}
Expand Down
36 changes: 36 additions & 0 deletions core/primitives/src/action/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -159,6 +160,40 @@ impl fmt::Debug for DeployGlobalContractAction {
}
}

#[serde_as]
#[derive(
BorshSerialize,
BorshDeserialize,
serde::Serialize,
serde::Deserialize,
PartialEq,
Eq,
Clone,
ProtocolSchema,
Debug,
)]
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,
Expand Down Expand Up @@ -270,6 +305,7 @@ pub enum Action {
DeleteAccount(DeleteAccountAction),
Delegate(Box<delegate::SignedDelegateAction>),
DeployGlobalContract(DeployGlobalContractAction),
UseGlobalContract(Box<UseGlobalContractAction>),
#[cfg(feature = "protocol_feature_nonrefundable_transfer_nep491")]
/// Makes a non-refundable transfer for storage allowance.
/// Only possible during new account creation.
Expand Down
29 changes: 28 additions & 1 deletion core/primitives/src/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
//! from the source structure in the relevant `From<SourceStruct>` 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;
Expand Down Expand Up @@ -1179,6 +1182,12 @@ pub enum ActionView {
#[serde_as(as = "Base64")]
code: Vec<u8>,
},
UseGlobalContract {
code_hash: CryptoHash,
},
UseGlobalContractByAccountId {
account_id: AccountId,
},
}

impl From<Action> for ActionView {
Expand Down Expand Up @@ -1224,6 +1233,14 @@ impl From<Action> 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 }
}
},
}
}
}
Expand Down Expand Up @@ -1277,6 +1294,16 @@ impl TryFrom<ActionView> 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),
}))
}
})
}
}
Expand Down
18 changes: 15 additions & 3 deletions runtime/runtime/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<Account>,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
8 changes: 4 additions & 4 deletions runtime/runtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
};
Expand Down Expand Up @@ -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
}
}
Expand Down
4 changes: 4 additions & 0 deletions runtime/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion runtime/runtime/src/pipelining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
30 changes: 24 additions & 6 deletions runtime/runtime/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 8067cb6

Please sign in to comment.