Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wallet_contract now returns contract based on the hash #12041

Merged
merged 4 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(),
Copy link
Contributor

@VanBarbascu VanBarbascu Sep 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allowed the LOCALNET contract to be deployed on test chains. i.e. mocknet. What contract will be picked in this case?

}
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
Loading