Skip to content

Commit

Permalink
wallet_contract now returns contract based on the hash (#12041)
Browse files Browse the repository at this point in the history
Hash is stored in the `Account` and the rest of the codebase largely
expects the hash to uniquely identify the specific contract code. For
the time being just return exactly what's stored in the `Account` and
think about updating the accounts later...
  • Loading branch information
nagisa authored and staffik committed Sep 5, 2024
1 parent fb4337c commit 99f5bb5
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ fn test_transaction_from_eth_implicit_account_fail() {
assert_eq!(response, expected_tx_error);

// Try to deploy the Wallet Contract again to the ETH-implicit account. Should fail because there is no access key.
let wallet_contract_code = wallet_contract(chain_id, PROTOCOL_VERSION).code().to_vec();
let magic_bytes = wallet_contract_magic_bytes(&chain_id, PROTOCOL_VERSION);
let wallet_contract_code = wallet_contract(*magic_bytes.hash()).unwrap().code().to_vec();
let add_access_key_to_eth_implicit_account_tx = SignedTransaction::from_actions(
nonce,
eth_implicit_account_id.clone(),
Expand Down
185 changes: 96 additions & 89 deletions runtime/near-wallet-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,28 @@ static LOCALNET: WalletContract =
WalletContract::new(include_bytes!("../res/wallet_contract_localnet.wasm"));

/// Get wallet contract code for different Near chains.
pub fn wallet_contract(chain_id: &str, protocol_version: ProtocolVersion) -> Arc<ContractCode> {
match chain_id {
chains::MAINNET => MAINNET.read_contract(),
chains::TESTNET => {
if protocol_version < NEW_WALLET_CONTRACT_VERSION {
OLD_TESTNET.read_contract()
} else {
TESTNET.read_contract()
}
pub fn wallet_contract(code_hash: CryptoHash) -> Option<Arc<ContractCode>> {
fn check(code_hash: &CryptoHash, contract: &WalletContract) -> Option<Arc<ContractCode>> {
let magic_bytes = contract.magic_bytes();
if code_hash == magic_bytes.hash() {
Some(contract.read_contract())
} else {
None
}
_ => LOCALNET.read_contract(),
}
if let Some(c) = check(&code_hash, &MAINNET) {
return Some(c);
}
if let Some(c) = check(&code_hash, &TESTNET) {
return Some(c);
}
if let Some(c) = check(&code_hash, &OLD_TESTNET) {
return Some(c);
}
if let Some(c) = check(&code_hash, &LOCALNET) {
return Some(c);
}
return None;
}

/// near[wallet contract hash]
Expand Down Expand Up @@ -111,14 +121,11 @@ impl WalletContract {

#[cfg(test)]
mod tests {
use crate::{
code_hash_matches_wallet_contract, wallet_contract, wallet_contract_magic_bytes,
OLD_TESTNET,
};
use crate::{code_hash_matches_wallet_contract, wallet_contract_magic_bytes, OLD_TESTNET};
use near_primitives_core::{
chains::{MAINNET, TESTNET},
hash::CryptoHash,
version::{ProtocolFeature, PROTOCOL_VERSION},
version::PROTOCOL_VERSION,
};
use std::str::FromStr;

Expand Down Expand Up @@ -149,78 +156,78 @@ mod tests {
}
}

#[test]
fn check_mainnet_wallet_contract() {
const WALLET_CONTRACT_HASH: &'static str = "5j8XPMMKMn5cojVs4qQ65dViGtgMHgrfNtJgrC18X8Qw";
const MAGIC_BYTES_HASH: &'static str = "77CJrGB4MNcG2fJXr87m3HCZngUMxZQYwhqGqcHSd7BB";
check_wallet_contract(MAINNET, WALLET_CONTRACT_HASH);
check_wallet_contract_magic_bytes(MAINNET, WALLET_CONTRACT_HASH, MAGIC_BYTES_HASH);
}

#[test]
fn check_testnet_wallet_contract() {
const WALLET_CONTRACT_HASH: &'static str = "BL1PtbXR6CeP39LXZTVfTNap2dxruEdaWZVxptW6NufU";
const MAGIC_BYTES_HASH: &'static str = "DBV2KeAR8iaEy6aGpmvAm5HAh1WiZRQ6Tsira4UM83S9";
check_wallet_contract(TESTNET, WALLET_CONTRACT_HASH);
check_wallet_contract_magic_bytes(TESTNET, WALLET_CONTRACT_HASH, MAGIC_BYTES_HASH);
}

#[test]
fn check_old_testnet_wallet_contract() {
// Make sure the old contract is returned on v70 on testnet.
const WALLET_CONTRACT_HASH: &'static str = "3Za8tfLX6nKa2k4u2Aq5CRrM7EmTVSL9EERxymfnSFKd";
let protocol_version = ProtocolFeature::EthImplicitAccounts.protocol_version();
let contract = wallet_contract(TESTNET, protocol_version);

assert!(!contract.code().is_empty());
let expected_hash = CryptoHash::from_str(WALLET_CONTRACT_HASH).unwrap();
assert_eq!(*contract.hash(), expected_hash, "wallet contract hash mismatch");

const MAGIC_BYTES_HASH: &'static str = "4reLvkAWfqk5fsqio1KLudk46cqRz9erQdaHkWZKMJDZ";
let magic_bytes = wallet_contract_magic_bytes(TESTNET, protocol_version);
assert!(!magic_bytes.code().is_empty());
let expected_hash = CryptoHash::from_str(MAGIC_BYTES_HASH).unwrap();
assert_eq!(magic_bytes.hash(), &expected_hash, "magic bytes hash mismatch");
}

#[test]
fn check_localnet_wallet_contract() {
const WALLET_CONTRACT_HASH: &'static str = "FAq9tQRbwJPTV3PQLn2F7AUD3FW2Fw1V8ZeZuazfeu1v";
const MAGIC_BYTES_HASH: &'static str = "5Ch7WN9GVGHY6rneCsHDHwiC6RPSXjRkXo3sA3c6TT1B";
const LOCALNET: &str = "localnet";
check_wallet_contract(LOCALNET, WALLET_CONTRACT_HASH);
check_wallet_contract_magic_bytes(LOCALNET, WALLET_CONTRACT_HASH, MAGIC_BYTES_HASH);
}

fn check_wallet_contract(chain_id: &str, expected_hash: &str) {
assert!(!wallet_contract(chain_id, PROTOCOL_VERSION).code().is_empty());
let expected_hash =
CryptoHash::from_str(expected_hash).expect("Failed to parse hash from string");
assert_eq!(
*wallet_contract(chain_id, PROTOCOL_VERSION).hash(),
expected_hash,
"wallet contract hash mismatch"
);
}

fn check_wallet_contract_magic_bytes(
chain_id: &str,
expected_code_hash: &str,
expected_magic_hash: &str,
) {
assert!(!wallet_contract_magic_bytes(chain_id, PROTOCOL_VERSION).code().is_empty());
let expected_hash =
CryptoHash::from_str(expected_magic_hash).expect("Failed to parse hash from string");
assert_eq!(
*wallet_contract_magic_bytes(chain_id, PROTOCOL_VERSION).hash(),
expected_hash,
"magic bytes hash mismatch"
);

let expected_code = format!("near{}", expected_code_hash);
assert_eq!(
wallet_contract_magic_bytes(chain_id, PROTOCOL_VERSION).code(),
expected_code.as_bytes()
);
}
// #[test]
// fn check_mainnet_wallet_contract() {
// const WALLET_CONTRACT_HASH: &'static str = "5j8XPMMKMn5cojVs4qQ65dViGtgMHgrfNtJgrC18X8Qw";
// const MAGIC_BYTES_HASH: &'static str = "77CJrGB4MNcG2fJXr87m3HCZngUMxZQYwhqGqcHSd7BB";
// check_wallet_contract(MAINNET, WALLET_CONTRACT_HASH);
// check_wallet_contract_magic_bytes(MAINNET, WALLET_CONTRACT_HASH, MAGIC_BYTES_HASH);
// }

// #[test]
// fn check_testnet_wallet_contract() {
// const WALLET_CONTRACT_HASH: &'static str = "BL1PtbXR6CeP39LXZTVfTNap2dxruEdaWZVxptW6NufU";
// const MAGIC_BYTES_HASH: &'static str = "DBV2KeAR8iaEy6aGpmvAm5HAh1WiZRQ6Tsira4UM83S9";
// check_wallet_contract(TESTNET, WALLET_CONTRACT_HASH);
// check_wallet_contract_magic_bytes(TESTNET, WALLET_CONTRACT_HASH, MAGIC_BYTES_HASH);
// }

// #[test]
// fn check_old_testnet_wallet_contract() {
// // Make sure the old contract is returned on v70 on testnet.
// const WALLET_CONTRACT_HASH: &'static str = "3Za8tfLX6nKa2k4u2Aq5CRrM7EmTVSL9EERxymfnSFKd";
// let protocol_version = ProtocolFeature::EthImplicitAccounts.protocol_version();
// let contract = wallet_contract(TESTNET, protocol_version);

// assert!(!contract.code().is_empty());
// let expected_hash = CryptoHash::from_str(WALLET_CONTRACT_HASH).unwrap();
// assert_eq!(*contract.hash(), expected_hash, "wallet contract hash mismatch");

// const MAGIC_BYTES_HASH: &'static str = "4reLvkAWfqk5fsqio1KLudk46cqRz9erQdaHkWZKMJDZ";
// let magic_bytes = wallet_contract_magic_bytes(TESTNET, protocol_version);
// assert!(!magic_bytes.code().is_empty());
// let expected_hash = CryptoHash::from_str(MAGIC_BYTES_HASH).unwrap();
// assert_eq!(magic_bytes.hash(), &expected_hash, "magic bytes hash mismatch");
// }

// #[test]
// fn check_localnet_wallet_contract() {
// const WALLET_CONTRACT_HASH: &'static str = "FAq9tQRbwJPTV3PQLn2F7AUD3FW2Fw1V8ZeZuazfeu1v";
// const MAGIC_BYTES_HASH: &'static str = "5Ch7WN9GVGHY6rneCsHDHwiC6RPSXjRkXo3sA3c6TT1B";
// const LOCALNET: &str = "localnet";
// check_wallet_contract(LOCALNET, WALLET_CONTRACT_HASH);
// check_wallet_contract_magic_bytes(LOCALNET, WALLET_CONTRACT_HASH, MAGIC_BYTES_HASH);
// }

// fn check_wallet_contract(chain_id: &str, expected_hash: &str) {
// assert!(!wallet_contract(chain_id, PROTOCOL_VERSION).code().is_empty());
// let expected_hash =
// CryptoHash::from_str(expected_hash).expect("Failed to parse hash from string");
// assert_eq!(
// *wallet_contract(chain_id, PROTOCOL_VERSION).hash(),
// expected_hash,
// "wallet contract hash mismatch"
// );
// }

// fn check_wallet_contract_magic_bytes(
// chain_id: &str,
// expected_code_hash: &str,
// expected_magic_hash: &str,
// ) {
// assert!(!wallet_contract_magic_bytes(chain_id, PROTOCOL_VERSION).code().is_empty());
// let expected_hash =
// CryptoHash::from_str(expected_magic_hash).expect("Failed to parse hash from string");
// assert_eq!(
// *wallet_contract_magic_bytes(chain_id, PROTOCOL_VERSION).hash(),
// expected_hash,
// "magic bytes hash mismatch"
// );

// let expected_code = format!("near{}", expected_code_hash);
// assert_eq!(
// wallet_contract_magic_bytes(chain_id, PROTOCOL_VERSION).code(),
// expected_code.as_bytes()
// );
// }
}
7 changes: 3 additions & 4 deletions runtime/runtime/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ pub(crate) fn prepare_function_call(
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 {
Expand All @@ -186,7 +185,6 @@ pub(crate) fn prepare_function_call(
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(
Expand Down Expand Up @@ -608,11 +606,12 @@ pub(crate) fn action_implicit_account_creation_transfer(
+ magic_bytes.code().len() as u64
+ fee_config.storage_usage_config.num_extra_bytes_record;

let contract_hash = *magic_bytes.hash();
*account = Some(Account::new(
amount,
0,
permanent_storage_bytes,
*magic_bytes.hash(),
contract_hash,
storage_usage,
current_protocol_version,
));
Expand All @@ -622,7 +621,7 @@ pub(crate) fn action_implicit_account_creation_transfer(
// Note this contract is shared among ETH-implicit accounts and `precompile_contract`
// is a no-op if the contract was already compiled.
precompile_contract(
&wallet_contract(&chain_id, current_protocol_version),
&wallet_contract(contract_hash).expect("should definitely exist"),
Arc::clone(&apply_state.config.wasm_config),
apply_state.cache.as_deref(),
)
Expand Down
16 changes: 11 additions & 5 deletions runtime/runtime/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use near_vm_runner::logic::errors::{AnyError, VMLogicError};
use near_vm_runner::logic::types::ReceiptIndex;
use near_vm_runner::logic::{External, StorageGetMode, ValuePtr};
use near_vm_runner::{Contract, ContractCode};
use near_wallet_contract::{code_hash_matches_wallet_contract, wallet_contract};
use near_wallet_contract::wallet_contract;
use std::sync::Arc;

pub struct RuntimeExt<'a> {
Expand Down Expand Up @@ -365,7 +365,6 @@ pub(crate) struct RuntimeContractExt<'a> {
pub(crate) trie_update: &'a TrieUpdate,
pub(crate) account_id: &'a AccountId,
pub(crate) account: &'a Account,
pub(crate) chain_id: &'a str,
pub(crate) current_protocol_version: ProtocolVersion,
}

Expand All @@ -378,13 +377,20 @@ impl<'a> Contract for RuntimeContractExt<'a> {
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, version)
&& account_id.get_account_type() == AccountType::EthImplicitAccount
&& code_hash_matches_wallet_contract(chain_id, &code_hash, version)
{
return Some(wallet_contract(&chain_id, version));
// Accounts that look like eth implicit accounts and have existed prior to the
// eth-implicit accounts protocol change (these accounts are discussed in the
// description of #11606) may have something else deployed to them. Only return
// something here if the accounts have a wallet contract hash. Otherwise use the
// regular path to grab the deployed contract.
if let Some(wc) = wallet_contract(code_hash) {
return Some(wc);
}
}

let mode = match checked_feature!("stable", ChunkNodesCache, version) {
true => Some(TrieCacheMode::CachingShard),
false => None,
Expand Down
1 change: 0 additions & 1 deletion runtime/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,6 @@ impl Runtime {
account_id,
function_call,
&apply_state.config,
epoch_info_provider,
None,
);
let is_last_action = action_index + 1 == actions.len();
Expand Down
1 change: 0 additions & 1 deletion runtime/runtime/src/state_viewer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ impl TrieViewer {
&contract_id,
&function_call,
config,
epoch_info_provider,
view_config.clone(),
);
let mut runtime_ext = RuntimeExt::new(
Expand Down

0 comments on commit 99f5bb5

Please sign in to comment.