Skip to content

Commit

Permalink
revert: "runtime: implement a receipt preparation pipeline (#11839)" (#…
Browse files Browse the repository at this point in the history
…11964)

This reverts commit 08cec9d.

Reason: mainnet nodes having the commit above get stuck at block
125824281-125824283
  • Loading branch information
Trisfald authored Aug 16, 2024
1 parent 5bf5a45 commit 4e93e46
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 545 deletions.
52 changes: 0 additions & 52 deletions core/store/src/contract.rs

This file was deleted.

10 changes: 9 additions & 1 deletion core/store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ use strum;
pub mod cold_storage;
mod columns;
pub mod config;
pub mod contract;
pub mod db;
pub mod flat;
pub mod genesis;
Expand Down Expand Up @@ -971,6 +970,15 @@ pub fn set_code(state_update: &mut TrieUpdate, account_id: AccountId, code: &Con
state_update.set(TrieKey::ContractCode { account_id }, code.code().to_vec());
}

pub fn get_code(
trie: &dyn TrieAccess,
account_id: &AccountId,
code_hash: Option<CryptoHash>,
) -> Result<Option<ContractCode>, StorageError> {
let key = TrieKey::ContractCode { account_id: account_id.clone() };
trie.get(&key).map(|opt| opt.map(|code| ContractCode::new(code, code_hash)))
}

/// Removes account, code and all access keys associated to it.
pub fn remove_account(
state_update: &mut TrieUpdate,
Expand Down
28 changes: 25 additions & 3 deletions core/store/src/trie/update.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
pub use self::iterator::TrieUpdateIterator;
use super::accounting_cache::TrieAccountingCacheSwitch;
use super::{OptimizedValueRef, Trie, TrieWithReadLock};
use crate::contract::ContractStorage;
use crate::trie::{KeyLookupMode, TrieChanges};
use crate::StorageError;
use crate::{StorageError, TrieStorage};
use near_primitives::hash::CryptoHash;
use near_primitives::trie_key::TrieKey;
use near_primitives::types::{
AccountId, RawStateChange, RawStateChanges, RawStateChangesWithTrieKey, StateChangeCause,
StateRoot, TrieCacheMode,
};
use near_vm_runner::ContractCode;
use std::collections::BTreeMap;
use std::sync::Arc;

mod iterator;

/// Reads contract code from the trie by its hash.
/// Currently, uses `TrieStorage`. Consider implementing separate logic for
/// requesting and compiling contracts, as any contract code read and
/// compilation is a major bottleneck during chunk execution.
struct ContractStorage {
storage: Arc<dyn TrieStorage>,
}

impl ContractStorage {
fn new(storage: Arc<dyn TrieStorage>) -> Self {
Self { storage }
}

pub fn get(&self, code_hash: CryptoHash) -> Option<ContractCode> {
match self.storage.retrieve_raw_bytes(&code_hash) {
Ok(raw_code) => Some(ContractCode::new(raw_code.to_vec(), Some(code_hash))),
Err(_) => None,
}
}
}

/// Key-value update. Contains a TrieKey and a value.
pub struct TrieKeyValueUpdate {
pub trie_key: TrieKey,
Expand All @@ -27,7 +49,7 @@ pub type TrieUpdates = BTreeMap<Vec<u8>, TrieKeyValueUpdate>;
/// TODO (#7327): rename to StateUpdate
pub struct TrieUpdate {
pub trie: Trie,
pub contract_storage: ContractStorage,
contract_storage: ContractStorage,
committed: RawStateChanges,
prospective: TrieUpdates,
}
Expand Down
9 changes: 3 additions & 6 deletions integration-tests/src/tests/runtime/state_viewer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::{collections::HashMap, io, sync::Arc};

use borsh::BorshDeserialize;
use near_vm_runner::ContractCode;

use crate::runtime_utils::{get_runtime_and_trie, get_test_trie_viewer, TEST_SHARD_UID};
use near_primitives::{
account::Account,
hash::CryptoHash,
hash::{hash as sha256, CryptoHash},
serialize::to_base64,
trie_key::trie_key_parsers,
types::{AccountId, StateRoot},
Expand Down Expand Up @@ -375,14 +374,12 @@ fn test_view_state_with_large_contract() {
let (_, tries, root) = get_runtime_and_trie();
let mut state_update = tries.new_trie_update(TEST_SHARD_UID, root);
let contract_code = [0; Account::MAX_ACCOUNT_DELETION_STORAGE_USAGE as usize].to_vec();
let code = ContractCode::new(contract_code, None);
set_account(
&mut state_update,
alice_account(),
&Account::new(0, 0, 0, *code.hash(), 50_001, PROTOCOL_VERSION),
&Account::new(0, 0, 0, sha256(&contract_code), 50_001, PROTOCOL_VERSION),
);
// FIXME: this really should use the deploy action.
state_update.contract_storage.store(code);
state_update.set(TrieKey::ContractCode { account_id: alice_account() }, contract_code);
let trie_viewer = TrieViewer::new(Some(50_000), None);
let result = trie_viewer.view_state(&state_update, &alice_account(), b"", false);
assert!(result.is_ok());
Expand Down
102 changes: 62 additions & 40 deletions runtime/runtime/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::config::{
safe_add_compute, safe_add_gas, total_prepaid_exec_fees, total_prepaid_gas,
total_prepaid_send_fees,
};
use crate::ext::{ExternalError, RuntimeExt};
use crate::ext::{ExternalError, RuntimeContractExt, RuntimeExt};
use crate::receipt_manager::ReceiptManager;
use crate::{metrics, ActionResult, ApplyState};
use near_crypto::PublicKey;
Expand Down Expand Up @@ -30,15 +30,16 @@ use near_primitives::version::{
};
use near_primitives_core::account::id::AccountType;
use near_store::{
enqueue_promise_yield_timeout, get_access_key, get_promise_yield_indices, remove_access_key,
remove_account, set_access_key, set_code, set_promise_yield_indices, StorageError, TrieUpdate,
enqueue_promise_yield_timeout, get_access_key, get_code, get_promise_yield_indices,
remove_access_key, remove_account, set_access_key, set_code, set_promise_yield_indices,
StorageError, TrieUpdate,
};
use near_vm_runner::logic::errors::{
CompilationError, FunctionCallError, InconsistentStateError, VMRunnerError,
};
use near_vm_runner::logic::{VMContext, VMOutcome};
use near_vm_runner::ContractCode;
use near_vm_runner::{precompile_contract, PreparedContract};
use near_vm_runner::{ContractCode, ContractRuntimeCache};
use near_wallet_contract::{wallet_contract, wallet_contract_magic_bytes};
use std::sync::Arc;

Expand Down Expand Up @@ -160,6 +161,44 @@ pub(crate) fn execute_function_call(
Ok(outcome)
}

pub(crate) fn prepare_function_call(
state_update: &TrieUpdate,
apply_state: &ApplyState,
account: &Account,
account_id: &AccountId,
function_call: &FunctionCallAction,
config: &RuntimeConfig,
epoch_info_provider: &(dyn EpochInfoProvider),
view_config: Option<ViewConfig>,
) -> Box<dyn PreparedContract> {
let max_gas_burnt = match view_config {
Some(ViewConfig { max_gas_burnt }) => max_gas_burnt,
None => config.wasm_config.limit_config.max_gas_burnt,
};
let gas_counter = near_vm_runner::logic::GasCounter::new(
config.wasm_config.ext_costs.clone(),
max_gas_burnt,
config.wasm_config.regular_op_cost,
function_call.gas,
view_config.is_some(),
);
let code_ext = RuntimeContractExt {
trie_update: state_update,
account_id,
account,
chain_id: &epoch_info_provider.chain_id(),
current_protocol_version: apply_state.current_protocol_version,
};
let contract = near_vm_runner::prepare(
&code_ext,
Arc::clone(&config.wasm_config),
apply_state.cache.as_deref(),
gas_counter,
&function_call.method_name,
);
contract
}

pub(crate) fn action_function_call(
state_update: &mut TrieUpdate,
apply_state: &ApplyState,
Expand Down Expand Up @@ -606,12 +645,11 @@ pub(crate) fn action_deploy_contract(
account: &mut Account,
account_id: &AccountId,
deploy_contract: &DeployContractAction,
config: Arc<near_parameters::vm::Config>,
cache: Option<&dyn ContractRuntimeCache>,
apply_state: &ApplyState,
) -> Result<(), StorageError> {
let _span = tracing::debug_span!(target: "runtime", "action_deploy_contract").entered();
let code = ContractCode::new(deploy_contract.code.clone(), None);
let prev_code = state_update.contract_storage.get(account.code_hash());
let prev_code = get_code(state_update, account_id, Some(account.code_hash()))?;
let prev_code_length = prev_code.map(|code| code.code().len() as u64).unwrap_or_default();
account.set_storage_usage(account.storage_usage().saturating_sub(prev_code_length));
account.set_storage_usage(
Expand All @@ -623,20 +661,16 @@ pub(crate) fn action_deploy_contract(
})?,
);
account.set_code_hash(*code.hash());
// Legacy: populate the mapping from `AccountId => sha256(code)` thus making contracts part of
// The State. For the time being we are also relying on the `TrieUpdate` to actually write the
// contracts into the storage as part of the commit routine, however no code should be relying
// that the contracts are written to The State.
set_code(state_update, account_id.clone(), &code);
// Precompile the contract and store result (compiled code or error) in the contract runtime
// cache.
// Note, that contract compilation costs are already accounted in deploy cost using special
// logic in estimator (see get_runtime_config() function).
precompile_contract(&code, config, cache).ok();
// Inform the `store::contract::Storage` about the new deploy (so that the `get` method can
// return the contract before the contract is written out to the underlying storage as part of
// the `TrieUpdate` commit.)
state_update.contract_storage.store(code);
// Precompile the contract and store result (compiled code or error) in the database.
// Note, that contract compilation costs are already accounted in deploy cost using
// special logic in estimator (see get_runtime_config() function).
precompile_contract(
&code,
Arc::clone(&apply_state.config.wasm_config),
apply_state.cache.as_deref(),
)
.ok();
Ok(())
}

Expand All @@ -653,7 +687,7 @@ pub(crate) fn action_delete_account(
if current_protocol_version >= ProtocolFeature::DeleteActionRestriction.protocol_version() {
let account = account.as_ref().unwrap();
let mut account_storage_usage = account.storage_usage();
let contract_code = state_update.contract_storage.get(account.code_hash());
let contract_code = get_code(state_update, account_id, Some(account.code_hash()))?;
if let Some(code) = contract_code {
// account storage usage should be larger than code size
let code_len = code.code().len() as u64;
Expand Down Expand Up @@ -1156,8 +1190,10 @@ mod tests {
use near_primitives::action::delegate::NonDelegateAction;
use near_primitives::congestion_info::BlockCongestionInfo;
use near_primitives::errors::InvalidAccessKeyError;
use near_primitives::hash::hash;
use near_primitives::runtime::migration_data::MigrationFlags;
use near_primitives::transaction::CreateAccountAction;
use near_primitives::trie_key::TrieKey;
use near_primitives::types::{EpochId, StateChangeCause};
use near_primitives_core::version::PROTOCOL_VERSION;
use near_store::set_account;
Expand Down Expand Up @@ -1319,25 +1355,11 @@ mod tests {
let mut state_update =
tries.new_trie_update(ShardUId::single_shard(), CryptoHash::default());
let account_id = "alice".parse::<AccountId>().unwrap();
let deploy_action = DeployContractAction { code: [0; 10_000].to_vec() };
let mut account =
Account::new(100, 0, 0, CryptoHash::default(), storage_usage, PROTOCOL_VERSION);
let apply_state = create_apply_state(0);
let res = action_deploy_contract(
&mut state_update,
&mut account,
&account_id,
&deploy_action,
Arc::clone(&apply_state.config.wasm_config),
None,
);
assert!(res.is_ok());
test_delete_large_account(
&account_id,
&account.code_hash(),
storage_usage,
&mut state_update,
)
let trie_key = TrieKey::ContractCode { account_id: account_id.clone() };
let empty_contract = [0; 10_000].to_vec();
let contract_hash = hash(&empty_contract);
state_update.set(trie_key, empty_contract);
test_delete_large_account(&account_id, &contract_hash, storage_usage, &mut state_update)
}

#[test]
Expand Down
17 changes: 11 additions & 6 deletions runtime/runtime/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ use near_primitives::checked_feature;
use near_primitives::errors::{EpochError, StorageError};
use near_primitives::hash::CryptoHash;
use near_primitives::trie_key::{trie_key_parsers, TrieKey};
use near_primitives::types::{AccountId, Balance, EpochId, EpochInfoProvider, Gas};
use near_primitives::types::{AccountId, Balance, EpochId, EpochInfoProvider, Gas, TrieCacheMode};
use near_primitives::utils::create_receipt_id_from_action_hash;
use near_primitives::version::ProtocolVersion;
use near_store::contract::ContractStorage;
use near_store::{has_promise_yield_receipt, KeyLookupMode, TrieUpdate, TrieUpdateValuePtr};
use near_vm_runner::logic::errors::{AnyError, VMLogicError};
use near_vm_runner::logic::types::ReceiptIndex;
Expand Down Expand Up @@ -363,28 +362,34 @@ impl<'a> External for RuntimeExt<'a> {
}

pub(crate) struct RuntimeContractExt<'a> {
pub(crate) storage: ContractStorage,
pub(crate) trie_update: &'a TrieUpdate,
pub(crate) account_id: &'a AccountId,
pub(crate) code_hash: CryptoHash,
pub(crate) account: &'a Account,
pub(crate) chain_id: &'a str,
pub(crate) current_protocol_version: ProtocolVersion,
}

impl<'a> Contract for RuntimeContractExt<'a> {
fn hash(&self) -> CryptoHash {
self.code_hash
self.account.code_hash()
}

fn get_code(&self) -> Option<Arc<ContractCode>> {
let account_id = self.account_id;
let code_hash = self.hash();
let version = self.current_protocol_version;
let chain_id = self.chain_id;
if checked_feature!("stable", EthImplicitAccounts, self.current_protocol_version)
&& account_id.get_account_type() == AccountType::EthImplicitAccount
&& &code_hash == wallet_contract_magic_bytes(&chain_id).hash()
{
return Some(wallet_contract(&chain_id));
}
self.storage.get(code_hash).map(Arc::new)
let mode = match checked_feature!("stable", ChunkNodesCache, version) {
true => Some(TrieCacheMode::CachingShard),
false => None,
};
let _guard = self.trie_update.with_trie_cache_mode(mode);
self.trie_update.get_code(self.account_id.clone(), code_hash).map(Arc::new)
}
}
Loading

0 comments on commit 4e93e46

Please sign in to comment.