From 68c190d36d7f9a313e0430b29dd5946b862ae0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Chuda=C5=9B?= <18039094+staffik@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:52:07 +0100 Subject: [PATCH] Wallet Contract placeholder (#10269) ## Context NEP: https://github.com/near/NEPs/issues/518 Tracking issue: https://github.com/near/nearcore/issues/10018. ### Goal We want the NEAR Protocol's ecosystem to be closer with Ethereum by integrating Web3 wallet support. To accomplish that, some changes on the protocol level are needed with an emphasis on user experience continuity. Following the design, the main change on the protocol level would be to have ETH-style addresses managed by a new `Wallet Contract` integrated into the protocol. For seamless onboarding of EVM users, the contract will be free of charge. ### Previous work This PR is built on top of two PRs: * https://github.com/near/nearcore/pull/10020: no-op PR, laying groundwork for further changes. * https://github.com/near/nearcore/pull/10224: empty contract as a placeholder, literally deployed to new ETH accounts. ## Summary This PR adds `near-wallet-contract` crate (based on `runtime/near-test-contracts`) that exposes `Wallet Contract` WASM code to the runtime. It stops deploying a copy of the `Wallet Contract` to each newly created ETH-implicit account. Instead, it uses an in-memory cached contract code on `execute_function_call` action from such account. ### Changes - Add `wallet-contract` crate (separated from the workspace) with placeholder `Wallet Contract` implementation. - Add `near-wallet-contract` crate (part of the workspace) that generates (through `build.rs`) and exposes the `Wallet Contract` WASM code. - Do not literally deploy `Wallet Contract` when creating ETH-implicit account. Just store reference to the `Wallet Contract`. - Treat ETH-implicit accounts specially when retrieving contract code from an account (in such case returns in-memory cached `Wallet Contract` code). - Add tests calling `Wallet Contract` where a transfer from an ETH-implicit account is possible depending on the public key passed with `rlp_transaction` argument. --- Cargo.lock | 23 +- Cargo.toml | 3 + core/primitives/Cargo.toml | 1 - core/primitives/src/utils.rs | 7 - integration-tests/Cargo.toml | 4 + .../src/tests/client/features.rs | 1 + .../access_key_nonce_for_implicit_accounts.rs | 110 +- .../tests/client/features/delegate_action.rs | 11 +- .../tests/client/features/wallet_contract.rs | 346 ++++ .../src/tests/standard_cases/mod.rs | 2 +- .../src/tests/standard_cases/runtime.rs | 4 +- runtime/near-wallet-contract/Cargo.toml | 31 + runtime/near-wallet-contract/README.md | 15 + runtime/near-wallet-contract/build.rs | 67 + runtime/near-wallet-contract/res/.gitignore | 1 + .../res/wallet_contract.wasm | Bin 0 -> 95239 bytes runtime/near-wallet-contract/src/lib.rs | 68 + .../wallet-contract/Cargo.lock | 1529 +++++++++++++++++ .../wallet-contract/Cargo.toml | 30 + .../wallet-contract/src/lib.rs | 53 + runtime/runtime/Cargo.toml | 3 + runtime/runtime/src/actions.rs | 53 +- 22 files changed, 2226 insertions(+), 136 deletions(-) create mode 100644 integration-tests/src/tests/client/features/wallet_contract.rs create mode 100644 runtime/near-wallet-contract/Cargo.toml create mode 100644 runtime/near-wallet-contract/README.md create mode 100644 runtime/near-wallet-contract/build.rs create mode 100644 runtime/near-wallet-contract/res/.gitignore create mode 100755 runtime/near-wallet-contract/res/wallet_contract.wasm create mode 100644 runtime/near-wallet-contract/src/lib.rs create mode 100644 runtime/near-wallet-contract/wallet-contract/Cargo.lock create mode 100644 runtime/near-wallet-contract/wallet-contract/Cargo.toml create mode 100644 runtime/near-wallet-contract/wallet-contract/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 86a120243d6..7604a05dc9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2844,12 +2844,14 @@ dependencies = [ "near-test-contracts", "near-undo-block", "near-vm-runner", + "near-wallet-contract", "nearcore", "node-runtime", "once_cell", "parking_lot 0.12.1", "primitive-types", "rand 0.8.5", + "rlp", "serde", "serde_json", "smart-default", @@ -4216,7 +4218,6 @@ dependencies = [ "thiserror", "time", "tracing", - "wat", ] [[package]] @@ -4665,6 +4666,15 @@ dependencies = [ "wast", ] +[[package]] +name = "near-wallet-contract" +version = "0.0.0" +dependencies = [ + "anyhow", + "near-primitives-core", + "near-vm-runner", +] + [[package]] name = "nearcore" version = "0.0.0" @@ -4821,6 +4831,7 @@ dependencies = [ "near-store", "near-test-contracts", "near-vm-runner", + "near-wallet-contract", "num-bigint 0.3.3", "num-rational", "num-traits", @@ -5998,6 +6009,16 @@ dependencies = [ "libc", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rocksdb" version = "0.21.0" diff --git a/Cargo.toml b/Cargo.toml index a4cec2e43eb..4f0acd1a960 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ members = [ "runtime/near-vm/compiler-test-derive", "runtime/near-vm-runner", "runtime/near-vm-runner/fuzz", + "runtime/near-wallet-contract", "runtime/runtime", "runtime/runtime-params-estimator", "runtime/runtime-params-estimator/estimator-warehouse", @@ -246,6 +247,7 @@ near-vm-test-generator = { path = "runtime/near-vm/test-generator" } near-vm-types = { path = "runtime/near-vm/types" } near-vm-vm = { path = "runtime/near-vm/vm" } near-vm-wast = { path = "runtime/near-vm/wast" } +near-wallet-contract = { path = "runtime/near-wallet-contract" } nix = "0.24" node-runtime = { path = "runtime/runtime" } num-bigint = "0.3" @@ -287,6 +289,7 @@ reqwest = { version = "0.11.14", features = ["blocking"] } ripemd = "0.1.1" rkyv = "0.7.31" rlimit = "0.7" +rlp = "0.5.2" rocksdb = { version = "0.21.0", default-features = false, features = ["snappy", "lz4", "zstd", "zlib", "jemalloc"] } runtime-tester = { path = "test-utils/runtime-tester" } rusqlite = { version = "0.29.0", features = ["bundled", "chrono", "functions"] } diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index 57025c21000..2509f8e7f36 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -39,7 +39,6 @@ strum.workspace = true thiserror.workspace = true time.workspace = true tracing.workspace = true -wat.workspace = true near-crypto.workspace = true near-fmt.workspace = true diff --git a/core/primitives/src/utils.rs b/core/primitives/src/utils.rs index b62f0a544b9..8ecea70816a 100644 --- a/core/primitives/src/utils.rs +++ b/core/primitives/src/utils.rs @@ -19,7 +19,6 @@ use crate::version::{ use near_crypto::{ED25519PublicKey, Secp256K1PublicKey}; use near_primitives_core::account::id::{AccountId, AccountType}; -use near_vm_runner::ContractCode; use std::mem::size_of; use std::ops::Deref; @@ -471,12 +470,6 @@ where Serializable(object) } -// TODO(eth-implicit) Replace this function (and wat dependency) with a real Wallet Contract implementation. -pub fn wallet_contract_placeholder() -> ContractCode { - let code = wat::parse_str(r#"(module (func (export "main")))"#); - ContractCode::new(code.unwrap().to_vec(), None) -} - /// From `near-account-id` version `1.0.0-alpha.2`, `is_implicit` returns true for ETH-implicit accounts. /// This function is a wrapper for `is_implicit` method so that we can easily differentiate its behavior /// based on whether ETH-implicit accounts are enabled. diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 7463a1af386..45cf62e42e8 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -25,6 +25,7 @@ once_cell.workspace = true parking_lot.workspace = true primitive-types.workspace = true rand.workspace = true +rlp.workspace = true serde.workspace = true serde_json.workspace = true smart-default.workspace = true @@ -58,6 +59,7 @@ near-test-contracts.workspace = true near-performance-metrics.workspace = true near-undo-block.workspace = true near-vm-runner.workspace = true +near-wallet-contract.workspace = true nearcore.workspace = true node-runtime.workspace = true testlib.workspace = true @@ -105,6 +107,7 @@ nightly = [ "near-telemetry/nightly", "near-undo-block/nightly", "near-vm-runner/nightly", + "near-wallet-contract/nightly", "nearcore/nightly", "node-runtime/nightly", "testlib/nightly", @@ -130,6 +133,7 @@ nightly_protocol = [ "near-telemetry/nightly_protocol", "near-undo-block/nightly_protocol", "near-vm-runner/nightly_protocol", + "near-wallet-contract/nightly_protocol", "nearcore/nightly_protocol", "node-runtime/nightly_protocol", "testlib/nightly_protocol", diff --git a/integration-tests/src/tests/client/features.rs b/integration-tests/src/tests/client/features.rs index 4b4449eed9d..4ebb2b306f4 100644 --- a/integration-tests/src/tests/client/features.rs +++ b/integration-tests/src/tests/client/features.rs @@ -19,4 +19,5 @@ mod lower_storage_key_limit; mod nearvm; mod restore_receipts_after_fix_apply_chunks; mod restrict_tla; +mod wallet_contract; mod zero_balance_account; diff --git a/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs b/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs index acb1b3b4aa0..df0e461d022 100644 --- a/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs +++ b/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs @@ -12,17 +12,14 @@ use near_network::shards_manager::ShardsManagerRequestFromNetwork; use near_network::types::{NetworkRequests, PeerManagerMessageRequest}; use near_o11y::testonly::init_test_logger; use near_primitives::account::AccessKey; -use near_primitives::checked_feature; -use near_primitives::errors::{InvalidAccessKeyError, InvalidTxError}; +use near_primitives::errors::InvalidTxError; use near_primitives::runtime::config_store::RuntimeConfigStore; use near_primitives::shard_layout::ShardLayout; use near_primitives::sharding::ChunkHash; -use near_primitives::transaction::{Action, AddKeyAction, DeployContractAction, SignedTransaction}; +use near_primitives::transaction::SignedTransaction; use near_primitives::types::{AccountId, BlockHeight}; -use near_primitives::utils::{ - derive_eth_implicit_account_id, derive_near_implicit_account_id, wallet_contract_placeholder, -}; -use near_primitives::version::{ProtocolFeature, ProtocolVersion, PROTOCOL_VERSION}; +use near_primitives::utils::derive_near_implicit_account_id; +use near_primitives::version::{ProtocolFeature, ProtocolVersion}; use near_primitives::views::FinalExecutionStatus; use nearcore::config::GenesisExt; use nearcore::test_utils::TestEnvNightshadeSetupExt; @@ -239,105 +236,6 @@ fn test_transaction_hash_collision_for_near_implicit_account_ok() { ); } -/// Test that transactions from ETH-implicit accounts are rejected. -#[test] -fn test_transaction_from_eth_implicit_account_fail() { - if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { - return; - } - let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); - let mut env = TestEnv::builder(ChainGenesis::test()) - .real_epoch_managers(&genesis.config) - .nightshade_runtimes(&genesis) - .build(); - let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); - let deposit_for_account_creation = 10u128.pow(23); - let mut height = 1; - let blocks_number = 5; - let signer1 = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); - - let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); - let public_key = secret_key.public_key(); - let eth_implicit_account_id = derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()); - let eth_implicit_account_signer = - InMemorySigner::from_secret_key(eth_implicit_account_id.clone(), secret_key); - - // Send money to ETH-implicit account, invoking its creation. - let send_money_tx = SignedTransaction::send_money( - 1, - "test1".parse().unwrap(), - eth_implicit_account_id.clone(), - &signer1, - deposit_for_account_creation, - *genesis_block.hash(), - ); - // Check for tx success status and get new block height. - height = check_tx_processing(&mut env, send_money_tx, height, blocks_number); - let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); - - // Try to send money from ETH-implicit account using `(block_height - 1) * 1e6` as a nonce. - // That would be a good nonce for any access key, but the transaction should fail nonetheless because there is no access key. - let nonce = (height - 1) * AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; - let send_money_from_eth_implicit_account_tx = SignedTransaction::send_money( - nonce, - eth_implicit_account_id.clone(), - "test0".parse().unwrap(), - ð_implicit_account_signer, - 100, - *block.hash(), - ); - let response = env.clients[0].process_tx(send_money_from_eth_implicit_account_tx, false, false); - let expected_tx_error = ProcessTxResponse::InvalidTx(InvalidTxError::InvalidAccessKeyError( - InvalidAccessKeyError::AccessKeyNotFound { - account_id: eth_implicit_account_id.clone(), - public_key: public_key.clone(), - }, - )); - assert_eq!(response, expected_tx_error); - - // Try to delete ETH-implicit account. Should fail because there is no access key. - let delete_eth_implicit_account_tx = SignedTransaction::delete_account( - nonce, - eth_implicit_account_id.clone(), - eth_implicit_account_id.clone(), - "test0".parse().unwrap(), - ð_implicit_account_signer, - *block.hash(), - ); - let response = env.clients[0].process_tx(delete_eth_implicit_account_tx, false, false); - assert_eq!(response, expected_tx_error); - - // Try to add an access key to the ETH-implicit account. Should fail because there is no access key. - let add_access_key_to_eth_implicit_account_tx = SignedTransaction::from_actions( - nonce, - eth_implicit_account_id.clone(), - eth_implicit_account_id.clone(), - ð_implicit_account_signer, - vec![Action::AddKey(Box::new(AddKeyAction { - public_key, - access_key: AccessKey::full_access(), - }))], - *block.hash(), - ); - let response = - env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); - 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_placeholder().code().to_vec(); - let add_access_key_to_eth_implicit_account_tx = SignedTransaction::from_actions( - nonce, - eth_implicit_account_id.clone(), - eth_implicit_account_id, - ð_implicit_account_signer, - vec![Action::DeployContract(DeployContractAction { code: wallet_contract_code })], - *block.hash(), - ); - let response = - env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); - assert_eq!(response, expected_tx_error); -} - /// Test that chunks with transactions that have expired are considered invalid. #[test] fn test_chunk_transaction_validity() { diff --git a/integration-tests/src/tests/client/features/delegate_action.rs b/integration-tests/src/tests/client/features/delegate_action.rs index 5d90db2d7e8..f2626435f29 100644 --- a/integration-tests/src/tests/client/features/delegate_action.rs +++ b/integration-tests/src/tests/client/features/delegate_action.rs @@ -281,7 +281,7 @@ fn meta_tx_near_transfer() { let node = RuntimeNode::new(&relayer); let fee_helper = fee_helper(&node); - let amount = nearcore::NEAR_BASE; + let amount = NEAR_BASE; let actions = vec![Action::Transfer(TransferAction { deposit: amount })]; let tx_cost = fee_helper.transfer_cost(); check_meta_tx_no_fn_call(&node, actions, tx_cost, amount, sender, relayer, receiver); @@ -837,7 +837,7 @@ fn meta_tx_create_and_use_implicit_account(new_account: AccountId) { // Check the account doesn't exist, yet. We will attempt creating it. node.view_account(&new_account).expect_err("account already exists"); - let initial_amount = nearcore::NEAR_BASE; + let initial_amount = NEAR_BASE; let actions = vec![ Action::Transfer(TransferAction { deposit: initial_amount }), Action::DeployContract(DeployContractAction { code: ft_contract().to_vec() }), @@ -887,7 +887,12 @@ fn meta_tx_create_implicit_account(new_account: AccountId) { node.view_account(&new_account).expect_err("account already exists"); let fee_helper = fee_helper(&node); - let initial_amount = nearcore::NEAR_BASE; + let initial_amount = match new_account.get_account_type() { + AccountType::NearImplicitAccount => NEAR_BASE, + // ETH-implicit accounts fit within zero-balance account limit. + AccountType::EthImplicitAccount => 0u128, + AccountType::NamedAccount => panic!("must be implicit"), + }; let actions = vec![Action::Transfer(TransferAction { deposit: initial_amount })]; let tx_cost = match new_account.get_account_type() { diff --git a/integration-tests/src/tests/client/features/wallet_contract.rs b/integration-tests/src/tests/client/features/wallet_contract.rs new file mode 100644 index 00000000000..1c43a0dd4eb --- /dev/null +++ b/integration-tests/src/tests/client/features/wallet_contract.rs @@ -0,0 +1,346 @@ +use assert_matches::assert_matches; +use near_chain::ChainGenesis; +use near_chain_configs::Genesis; +use near_client::{test_utils::TestEnv, ProcessTxResponse}; +use near_crypto::{InMemorySigner, KeyType, SecretKey}; +use near_primitives::errors::{ + ActionError, ActionErrorKind, FunctionCallError, InvalidAccessKeyError, InvalidTxError, + TxExecutionError, +}; +use near_primitives::test_utils::eth_implicit_test_account; +use near_primitives::transaction::{ + Action, AddKeyAction, DeployContractAction, FunctionCallAction, SignedTransaction, + TransferAction, +}; +use near_primitives::utils::derive_eth_implicit_account_id; +use near_primitives::views::{ + FinalExecutionStatus, QueryRequest, QueryResponse, QueryResponseKind, +}; +use near_primitives_core::{ + account::AccessKey, checked_feature, types::BlockHeight, version::PROTOCOL_VERSION, +}; +use near_store::ShardUId; +use near_vm_runner::ContractCode; +use near_wallet_contract::{wallet_contract, wallet_contract_magic_bytes}; +use nearcore::{config::GenesisExt, test_utils::TestEnvNightshadeSetupExt, NEAR_BASE}; +use node_runtime::ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT; +use rlp::RlpStream; +use testlib::runtime_utils::{alice_account, bob_account, carol_account}; + +use crate::{ + node::{Node, RuntimeNode}, + tests::client::process_blocks::produce_blocks_from_height, +}; + +/// Try to process tx in the next blocks, check that tx and all generated receipts succeed. +/// Return height of the next block. +fn check_tx_processing( + env: &mut TestEnv, + tx: SignedTransaction, + height: BlockHeight, + blocks_number: u64, +) -> BlockHeight { + let tx_hash = tx.get_hash(); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + let next_height = produce_blocks_from_height(env, blocks_number, height); + let final_outcome = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!(final_outcome.status, FinalExecutionStatus::SuccessValue(_)); + next_height +} + +fn view_request(env: &TestEnv, request: QueryRequest) -> QueryResponse { + let head = env.clients[0].chain.head().unwrap(); + let head_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + env.clients[0] + .runtime_adapter + .query( + ShardUId::single_shard(), + &head_block.chunks()[0].prev_state_root(), + head.height, + 0, + &head.prev_block_hash, + &head.last_block_hash, + head_block.header().epoch_id(), + &request, + ) + .unwrap() +} + +/// Tests that ETH-implicit account is created correctly, with Wallet Contract hash. +#[test] +fn test_eth_implicit_account_creation() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let eth_implicit_account_id = eth_implicit_test_account(); + + // Make zero-transfer to ETH-implicit account, invoking its creation. + let transfer_tx = SignedTransaction::send_money( + 1, + signer.account_id.clone(), + eth_implicit_account_id.clone(), + &signer, + 0, + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(transfer_tx, false, false), ProcessTxResponse::ValidTx); + for i in 1..5 { + env.produce_block(0, i); + } + + let magic_bytes = wallet_contract_magic_bytes(); + + // Verify the ETH-implicit account has zero balance and appropriate code hash. + // Check that the account storage fits within zero balance account limit. + let request = QueryRequest::ViewAccount { account_id: eth_implicit_account_id.clone() }; + match view_request(&env, request).kind { + QueryResponseKind::ViewAccount(view) => { + assert_eq!(view.amount, 0); + assert_eq!(view.code_hash, *magic_bytes.hash()); + assert!(view.storage_usage <= ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT) + } + _ => panic!("wrong query response"), + } + + // Verify that contract code deployed to the ETH-implicit account is near[wallet contract hash]. + let request = QueryRequest::ViewCode { account_id: eth_implicit_account_id }; + match view_request(&env, request).kind { + QueryResponseKind::ViewCode(view) => { + let contract_code = ContractCode::new(view.code, None); + assert_eq!(contract_code.hash(), magic_bytes.hash()); + assert_eq!(contract_code.code(), magic_bytes.code()); + } + _ => panic!("wrong query response"), + } +} + +/// Test that transactions from ETH-implicit accounts are rejected. +#[test] +fn test_transaction_from_eth_implicit_account_fail() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let deposit_for_account_creation = NEAR_BASE; + let mut height = 1; + let blocks_number = 5; + let signer1 = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + + let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); + let public_key = secret_key.public_key(); + let eth_implicit_account_id = derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()); + let eth_implicit_account_signer = + InMemorySigner::from_secret_key(eth_implicit_account_id.clone(), secret_key); + + // Send money to ETH-implicit account, invoking its creation. + let send_money_tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + eth_implicit_account_id.clone(), + &signer1, + deposit_for_account_creation, + *genesis_block.hash(), + ); + // Check for tx success status and get new block height. + height = check_tx_processing(&mut env, send_money_tx, height, blocks_number); + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + + // Try to send money from ETH-implicit account using `(block_height - 1) * 1e6` as a nonce. + // That would be a good nonce for any access key, but the transaction should fail nonetheless because there is no access key. + let nonce = (height - 1) * AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; + let send_money_from_eth_implicit_account_tx = SignedTransaction::send_money( + nonce, + eth_implicit_account_id.clone(), + "test0".parse().unwrap(), + ð_implicit_account_signer, + 100, + *block.hash(), + ); + let response = env.clients[0].process_tx(send_money_from_eth_implicit_account_tx, false, false); + let expected_tx_error = ProcessTxResponse::InvalidTx(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: eth_implicit_account_id.clone(), + public_key: public_key.clone(), + }, + )); + assert_eq!(response, expected_tx_error); + + // Try to delete ETH-implicit account. Should fail because there is no access key. + let delete_eth_implicit_account_tx = SignedTransaction::delete_account( + nonce, + eth_implicit_account_id.clone(), + eth_implicit_account_id.clone(), + "test0".parse().unwrap(), + ð_implicit_account_signer, + *block.hash(), + ); + let response = env.clients[0].process_tx(delete_eth_implicit_account_tx, false, false); + assert_eq!(response, expected_tx_error); + + // Try to add an access key to the ETH-implicit account. Should fail because there is no access key. + let add_access_key_to_eth_implicit_account_tx = SignedTransaction::from_actions( + nonce, + eth_implicit_account_id.clone(), + eth_implicit_account_id.clone(), + ð_implicit_account_signer, + vec![Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: AccessKey::full_access(), + }))], + *block.hash(), + ); + let response = + env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); + 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().code().to_vec(); + let add_access_key_to_eth_implicit_account_tx = SignedTransaction::from_actions( + nonce, + eth_implicit_account_id.clone(), + eth_implicit_account_id, + ð_implicit_account_signer, + vec![Action::DeployContract(DeployContractAction { code: wallet_contract_code })], + *block.hash(), + ); + let response = + env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); + assert_eq!(response, expected_tx_error); +} + +// TODO(eth-implicit) Remove this test and replace it with tests that directly call the `Wallet Contract` when it is ready. +/// Creating an ETH-implicit account with meta-transaction, then attempting to use it with another meta-transaction. +/// +/// The `create_account` parameter controls whether we create ETH-implicit account +/// before attempting to use it by making a function call. +/// Depending on `rlp_transaction` blob that is sent to the `Wallet Contract` +/// the transaction is either authorized or unauthorized. +/// The `authorized` parameter controls which case will be tested. +fn meta_tx_call_wallet_contract(create_account: bool, authorized: bool) { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let genesis = Genesis::test(vec![alice_account(), bob_account(), carol_account()], 3); + let relayer = alice_account(); + let node = RuntimeNode::new_from_genesis(&relayer, genesis); + let sender = bob_account(); + + let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); + let public_key = secret_key.public_key(); + let eth_implicit_account = derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()); + let other_public_key = SecretKey::from_seed(KeyType::SECP256K1, "test2").public_key(); + + // Although ETH-implicit account can be zero-balance, we pick 1 here in order to make transfer later from this account. + let transfer_amount = 1u128; + let actions = vec![Action::Transfer(TransferAction { deposit: transfer_amount })]; + + if create_account { + // Create ETH-implicit account by funding it. + node.user() + .meta_tx(sender.clone(), eth_implicit_account.clone(), relayer.clone(), actions) + .unwrap() + .assert_success(); + } + + let target = carol_account(); + let initial_balance = node.view_balance(&target).expect("failed looking up balance"); + + // TODO(eth-implicit) Append appropriate values to the RLP stream when proper `Wallet Contract` is implemented. + let mut stream = RlpStream::new_list(3); + stream.append(&target.as_str()); + // The RLP trait `Encodable` is not implemented for `u128`. We must encode it as bytes. + // TODO(eth-implicit) Do not try to encode `u128` values directly, see https://github.com/near/nearcore/pull/10269#discussion_r1425585051. + stream.append(&transfer_amount.to_be_bytes().as_slice()); + if authorized { + stream.append(&public_key.key_data()); + } else { + stream.append(&other_public_key.key_data()); + } + let rlp_encoded_data = stream.out().to_vec(); + + let args = serde_json::json!({ + "target": target.to_string(), + "rlp_transaction": rlp_encoded_data, + }) + .to_string() + .into_bytes(); + + let actions = vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "execute_rlp".to_owned(), + args, + gas: 30_000_000_000_000, + deposit: 0, + }))]; + // Call Wallet Contract with JSON-encoded arguments: `target` and `rlp_transaction`. The `rlp_transaction`'s value is RLP-encoded. + let tx_result = + node.user().meta_tx(sender, eth_implicit_account.clone(), relayer, actions).unwrap(); + let wallet_contract_call_result = &tx_result.receipts_outcome[1].outcome.status; + + if create_account && authorized { + // If the public key recovered from the RLP transaction's signature is valid for this ETH-implicit account, + // the transaction will succeed. `target`'s balance will increase by `transfer_amount`. + tx_result.assert_success(); + let final_balance = node.view_balance(&target).expect("failed looking up balance"); + assert_eq!(final_balance, initial_balance + transfer_amount); + return; + } + + if create_account { + // The public key recovered from the RLP transaction's signature isn't valid for this ETH-implicit account. + // The Wallet Contract will reject this transaction. + let expected_error = near_primitives::views::ExecutionStatusView::Failure( + TxExecutionError::ActionError( + ActionError { + index: Some(0), + kind: ActionErrorKind::FunctionCallError { + 0: FunctionCallError::ExecutionError( + "Smart contract panicked: Public key does not match the Wallet Contract address." + .to_string() + ) + } + } + ) + ); + assert_eq!(wallet_contract_call_result, &expected_error); + } else { + // The Wallet Contract function call is not executed because the account does not exist. + let expected_error = near_primitives::views::ExecutionStatusView::Failure( + TxExecutionError::ActionError(ActionError { + index: Some(0), + kind: ActionErrorKind::AccountDoesNotExist { account_id: eth_implicit_account }, + }), + ); + assert_eq!(wallet_contract_call_result, &expected_error); + } +} + +/// Wallet Contract function call is rejected because the ETH-implicit account does not exist. +#[test] +fn meta_tx_call_wallet_contract_account_does_not_exist() { + meta_tx_call_wallet_contract(false, true); +} + +/// Wallet Contract function call fails because the provided public key does not match the ETH-implicit address. +#[test] +fn meta_tx_call_wallet_contract_unauthorized() { + meta_tx_call_wallet_contract(true, false); +} + +/// Wallet Contract function call is executed succesfully. +#[test] +fn meta_tx_call_wallet_contract_authorized() { + meta_tx_call_wallet_contract(true, true); +} diff --git a/integration-tests/src/tests/standard_cases/mod.rs b/integration-tests/src/tests/standard_cases/mod.rs index e358f8294a9..1483f882a69 100644 --- a/integration-tests/src/tests/standard_cases/mod.rs +++ b/integration-tests/src/tests/standard_cases/mod.rs @@ -330,7 +330,7 @@ pub fn test_send_money(node: impl Node) { ); } -pub fn transfer_tokens_implicit_account(node: impl Node, public_key: PublicKey) { +pub fn transfer_tokens_to_implicit_account(node: impl Node, public_key: PublicKey) { let account_id = &node.account_id().unwrap(); let node_user = node.user(); let root = node_user.get_state_root(); diff --git a/integration-tests/src/tests/standard_cases/runtime.rs b/integration-tests/src/tests/standard_cases/runtime.rs index c939a0b0e14..3fa8e5d8e68 100644 --- a/integration-tests/src/tests/standard_cases/runtime.rs +++ b/integration-tests/src/tests/standard_cases/runtime.rs @@ -120,7 +120,7 @@ fn test_send_money_runtime() { fn test_transfer_tokens_near_implicit_account_runtime() { let node = create_runtime_node(); let public_key = node.user().signer().public_key(); - transfer_tokens_implicit_account(node, public_key); + transfer_tokens_to_implicit_account(node, public_key); } #[test] @@ -130,7 +130,7 @@ fn test_transfer_tokens_eth_implicit_account_runtime() { } let node = create_runtime_node(); let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); - transfer_tokens_implicit_account(node, secret_key.public_key()); + transfer_tokens_to_implicit_account(node, secret_key.public_key()); } #[test] diff --git a/runtime/near-wallet-contract/Cargo.toml b/runtime/near-wallet-contract/Cargo.toml new file mode 100644 index 00000000000..c8978386a67 --- /dev/null +++ b/runtime/near-wallet-contract/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "near-wallet-contract" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +description = "Builds and exposes Wallet Contract code." +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +near-vm-runner.workspace = true + +[dev-dependencies] +near-primitives-core.workspace = true + +[build-dependencies] +anyhow.workspace = true + +[features] +nightly_protocol = [ + "near-vm-runner/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "near-vm-runner/nightly", +] diff --git a/runtime/near-wallet-contract/README.md b/runtime/near-wallet-contract/README.md new file mode 100644 index 00000000000..51ca7112bc9 --- /dev/null +++ b/runtime/near-wallet-contract/README.md @@ -0,0 +1,15 @@ +A temporary (placeholder) implementation of the `Wallet Contract`. + +See https://github.com/near/NEPs/issues/518. + +Must not use in production! + +Currently, the contract can only be used in nightly build. +The `build.rs` generates WASM file and saves it to the `./res` directory. + +If you want to use the contract from nearcore, add this crate as a dependency +to the Cargo.toml and use `near_wallet_contract::wallet_contract()`. + +In order to review changes to the WASM file, rebuild the wallet contract locally +(remove it beforehand to make sure it was rebuilt later) and check the hashes +by running `check_wallet_contract` and `check_wallet_contract_magic_bytes` tests in `src/lib.rs`. diff --git a/runtime/near-wallet-contract/build.rs b/runtime/near-wallet-contract/build.rs new file mode 100644 index 00000000000..33e440081c3 --- /dev/null +++ b/runtime/near-wallet-contract/build.rs @@ -0,0 +1,67 @@ +/// This file is run as a part of `cargo build` process and it builds the `Wallet Contract`. +/// The generated WASM file is put to the `./res` directory. +use anyhow::{anyhow, Context, Ok, Result}; + +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() -> Result<()> { + if cfg!(not(feature = "nightly")) { + return Ok(()); + } + build_contract("./wallet-contract", &[], "wallet_contract") +} + +fn build_contract(dir: &str, args: &[&str], output: &str) -> Result<()> { + let target_dir: PathBuf = + std::env::var("OUT_DIR").context("Failed to read OUT_DIR environment variable")?.into(); + + // We place the build artifacts in `target_dir` (workspace's build directory). + let mut cmd = cargo_build_cmd(&target_dir); + cmd.args(args); + cmd.current_dir(dir); + run_checking_status(cmd)?; + + let build_artifact_path = + format!("wasm32-unknown-unknown/release/{}.wasm", dir.replace('-', "_")); + let src = target_dir.join(build_artifact_path); + let wasm_target_path = format!("./res/{}.wasm", output); + + std::fs::copy(&src, &wasm_target_path) + .with_context(|| format!("Failed to copy `{}` to `{}`", src.display(), wasm_target_path))?; + + println!("cargo:rerun-if-changed={}", dir); + Ok(()) +} + +/// Creates `cargo build` command to compile the WASM file. +/// Note that we are in `build.rs` file, so this will be called as a part +/// of the global `cargo build` process that already has some flags set. +/// `env_remove` invocations will remove these flags from the nested `cargo build` +/// process, to avoid unexpected behaviors due to the workspace configurations. +// TODO(eth-implicit) Change it to have a reproducible hash of the WASM file. +// see https://github.com/near/nearcore/pull/10269#discussion_r1430139987. +fn cargo_build_cmd(target_dir: &Path) -> Command { + let mut res = Command::new("cargo"); + + res.env_remove("CARGO_BUILD_RUSTFLAGS"); + res.env_remove("CARGO_ENCODED_RUSTFLAGS"); + res.env_remove("RUSTC_WORKSPACE_WRAPPER"); + + res.env("RUSTFLAGS", "-Dwarnings"); + res.env("CARGO_TARGET_DIR", target_dir); + + res.args(["build", "--target=wasm32-unknown-unknown", "--release"]); + + res +} + +fn run_checking_status(mut cmd: Command) -> Result<()> { + cmd.status().with_context(|| format!("Failed to run command `{cmd:?}`")).and_then(|status| { + if status.success() { + Ok(()) + } else { + Err(anyhow!("Command `{cmd:?}` exited with non-zero status: {status:?}")) + } + }) +} diff --git a/runtime/near-wallet-contract/res/.gitignore b/runtime/near-wallet-contract/res/.gitignore new file mode 100644 index 00000000000..19e1bced9ad --- /dev/null +++ b/runtime/near-wallet-contract/res/.gitignore @@ -0,0 +1 @@ +*.wasm diff --git a/runtime/near-wallet-contract/res/wallet_contract.wasm b/runtime/near-wallet-contract/res/wallet_contract.wasm new file mode 100755 index 0000000000000000000000000000000000000000..95df1c33c4fbf34e858b0b48f38e7d91da1f4a1a GIT binary patch literal 95239 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 Arc { + static CONTRACT: OnceLock> = OnceLock::new(); + CONTRACT.get_or_init(|| Arc::new(read_contract())).clone() +} + +/// Include the WASM file content directly in the binary at compile time. +fn read_contract() -> ContractCode { + #[cfg(feature = "nightly")] + let code = include_bytes!("../res/wallet_contract.wasm"); + + #[cfg(not(feature = "nightly"))] + let code = &[]; + + ContractCode::new(code.to_vec(), None) +} + +/// near[wallet contract hash] +pub fn wallet_contract_magic_bytes() -> Arc { + static CONTRACT: OnceLock> = OnceLock::new(); + CONTRACT + .get_or_init(|| { + let wallet_contract_hash = *wallet_contract().hash(); + let magic_bytes = format!("near{}", wallet_contract_hash.to_string()); + Arc::new(ContractCode::new(magic_bytes.into(), None)) + }) + .clone() +} + +#[cfg(feature = "nightly")] +#[cfg(test)] +mod tests { + use crate::{wallet_contract, wallet_contract_magic_bytes}; + use near_primitives_core::hash::CryptoHash; + use std::str::FromStr; + + const WALLET_CONTRACT_HASH: &'static str = "5wJJ2YaCq75kVSfx8zoZpevg1uLAn4h7nqUd2njKUEXe"; + const MAGIC_BYTES_HASH: &'static str = "31PSU4diHE4cpWju91fb2zTqn5JSDRZ6xNGM2ub8Lgdg"; + + #[test] + #[ignore] + // TODO(eth-implicit) Do not ignore when Wallet Contract build becomes reproducible, + // see https://github.com/near/nearcore/pull/10269#discussion_r1430139987. + fn check_wallet_contract() { + assert!(!wallet_contract().code().is_empty()); + let expected_hash = + CryptoHash::from_str(WALLET_CONTRACT_HASH).expect("Failed to parse hash from string"); + assert_eq!(*wallet_contract().hash(), expected_hash); + } + + #[test] + #[ignore] + // TODO(eth-implicit) Do not ignore when Wallet Contract build becomes reproducible, + // see https://github.com/near/nearcore/pull/10269#discussion_r1430139987. + fn check_wallet_contract_magic_bytes() { + assert!(!wallet_contract_magic_bytes().code().is_empty()); + let expected_hash = + CryptoHash::from_str(MAGIC_BYTES_HASH).expect("Failed to parse hash from string"); + assert_eq!(*wallet_contract_magic_bytes().hash(), expected_hash); + + let expected_code = format!("near{}", WALLET_CONTRACT_HASH); + assert_eq!(wallet_contract_magic_bytes().code(), expected_code.as_bytes()); + } +} diff --git a/runtime/near-wallet-contract/wallet-contract/Cargo.lock b/runtime/near-wallet-contract/wallet-contract/Cargo.lock new file mode 100644 index 00000000000..7184a20c2ae --- /dev/null +++ b/runtime/near-wallet-contract/wallet-contract/Cargo.lock @@ -0,0 +1,1529 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom 0.2.11", + "once_cell", + "version_check", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" + +[[package]] +name = "c2-chacha" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80" +dependencies = [ + "cipher", + "ppv-lite86", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "easy-ext" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53aff6fdc1b181225acdcb5b14c47106726fd8e486707315b1b138baed68ee31" + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "near-abi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885db39b08518fa700b73fa2214e8adbbfba316ba82dd510f50519173eadaf73" +dependencies = [ + "borsh", + "schemars", + "semver", + "serde", +] + +[[package]] +name = "near-account-id" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d258582a1878e6db67400b0504a5099db85718d22c2e07f747fe1706ae7150" +dependencies = [ + "borsh", + "serde", +] + +[[package]] +name = "near-crypto" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e75673d69fd7365508f3d32483669fe45b03bfb34e4d9363e90adae9dfb416c" +dependencies = [ + "arrayref", + "blake2", + "borsh", + "bs58", + "c2-chacha", + "curve25519-dalek", + "derive_more", + "ed25519-dalek", + "near-account-id", + "once_cell", + "parity-secp256k1", + "primitive-types", + "rand 0.7.3", + "rand_core 0.5.1", + "serde", + "serde_json", + "subtle", + "thiserror", +] + +[[package]] +name = "near-primitives" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad1a9a1640539c81f065425c31bffcfbf6b31ef1aeaade59ce905f5df6ac860" +dependencies = [ + "borsh", + "byteorder", + "bytesize", + "chrono", + "derive_more", + "easy-ext", + "hex", + "near-crypto", + "near-primitives-core", + "near-rpc-error-macro", + "near-vm-errors", + "num-rational", + "once_cell", + "primitive-types", + "rand 0.7.3", + "reed-solomon-erasure", + "serde", + "serde_json", + "smart-default", + "strum", + "thiserror", +] + +[[package]] +name = "near-primitives-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d508f0fc340f6461e4e256417685720d3c4c00bb5a939b105160e49137caba" +dependencies = [ + "base64 0.11.0", + "borsh", + "bs58", + "derive_more", + "near-account-id", + "num-rational", + "serde", + "sha2 0.10.8", + "strum", +] + +[[package]] +name = "near-rpc-error-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ee0b41c75ef859c193a8ff1dadfa0c8207bc0ac447cc22259721ad769a1408" +dependencies = [ + "quote", + "serde", + "syn 1.0.109", +] + +[[package]] +name = "near-rpc-error-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e837bd4bacd807073ec5ceb85708da7f721b46a4c2a978de86027fb0034ce31" +dependencies = [ + "near-rpc-error-core", + "serde", + "syn 1.0.109", +] + +[[package]] +name = "near-sdk" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15eb3de2defe3626260cc209a6cdb985c6b27b0bd4619fad97dcfae002c3c5bd" +dependencies = [ + "base64 0.13.1", + "borsh", + "bs58", + "near-abi", + "near-crypto", + "near-primitives", + "near-primitives-core", + "near-sdk-macros", + "near-sys", + "near-vm-logic", + "once_cell", + "schemars", + "serde", + "serde_json", + "wee_alloc", +] + +[[package]] +name = "near-sdk-macros" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4907affc9f5ed559456509188ff0024f1f2099c0830e6bdb66eb61d5b75912c0" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "near-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397688591acf8d3ebf2c2485ba32d4b24fc10aad5334e3ad8ec0b7179bfdf06b" + +[[package]] +name = "near-vm-errors" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0da466a30f0446639cbd788c30865086fac3e8dcb07a79e51d2b0775ed4261e" +dependencies = [ + "borsh", + "near-account-id", + "near-rpc-error-macro", + "serde", +] + +[[package]] +name = "near-vm-logic" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b534828419bacbf1f7b11ef7b00420f248c548c485d3f0cfda8bb6931152f2" +dependencies = [ + "base64 0.13.1", + "borsh", + "bs58", + "byteorder", + "near-account-id", + "near-crypto", + "near-primitives", + "near-primitives-core", + "near-vm-errors", + "ripemd", + "serde", + "sha2 0.10.8", + "sha3", + "zeropool-bn", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-secp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fca4f82fccae37e8bbdaeb949a4a218a1bbc485d11598f193d2a908042e5fc1" +dependencies = [ + "arrayvec 0.5.2", + "cc", + "cfg-if 0.1.10", + "rand 0.7.3", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.11", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "reed-solomon-erasure" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a415a013dd7c5d4221382329a5a3482566da675737494935cbbbcdec04662f9d" +dependencies = [ + "smallvec", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190dcc8c3a512f1eef5d09bb8c84c7f39e1054e174d1795482e18f5272f2e73" +dependencies = [ + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wallet-contract" +version = "0.1.0" +dependencies = [ + "hex", + "near-sdk", + "rlp", + "serde_json", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "zeropool-bn" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e61de68ede9ffdd69c01664f65a178c5188b73f78faa21f0936016a888ff7c" +dependencies = [ + "borsh", + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] diff --git a/runtime/near-wallet-contract/wallet-contract/Cargo.toml b/runtime/near-wallet-contract/wallet-contract/Cargo.toml new file mode 100644 index 00000000000..a7c0259d244 --- /dev/null +++ b/runtime/near-wallet-contract/wallet-contract/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wallet-contract" +version = "0.1.0" +publish = false +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +hex = "0.4.2" +serde_json = "1.0.68" +near-sdk = "4.1.1" +rlp = "0.4.6" + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +strip = true +lto = true +debug = false +panic = "abort" +rpath = false +debug-assertions = false +incremental = false +overflow-checks = true + +[workspace] +members = [] diff --git a/runtime/near-wallet-contract/wallet-contract/src/lib.rs b/runtime/near-wallet-contract/wallet-contract/src/lib.rs new file mode 100644 index 00000000000..193e0942cc9 --- /dev/null +++ b/runtime/near-wallet-contract/wallet-contract/src/lib.rs @@ -0,0 +1,53 @@ +//! Temporary implementation of the Wallet Contract. +//! See https://github.com/near/NEPs/issues/518. +//! Must not use in production! +// TODO(eth-implicit) Change to a real Wallet Contract implementation. + +use hex; +use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::{env, near_bindgen, AccountId, Promise}; +use rlp::Rlp; + +#[near_bindgen] +#[derive(Default, BorshDeserialize, BorshSerialize)] +pub struct WalletContract {} + +#[near_bindgen] +impl WalletContract { + /// For the sake of this placeholder implementation, we assume simplified version of the `rlp_transaction` + /// that only has 3 values: `To`, `Value`, and `PublicKey`. We assume this is a transfer transaction. + /// The real implementation would obtain the public key from `Signature`. + pub fn execute_rlp(&self, target: AccountId, rlp_transaction: Vec) { + let rlp = Rlp::new(&rlp_transaction); + + let to: String = match rlp.val_at(0) { + Ok(to) => to, + _ => env::panic_str("Missing `to` field in RLP-encoded transaction."), + }; + if target.to_string() != to { + env::panic_str("`target` not equal to transaction's `To` address."); + } + + let value_bytes: Vec = match rlp.val_at(1) { + Ok(value_bytes) => value_bytes, + _ => env::panic_str("Missing `value` field in RLP-encoded transaction."), + }; + let value = u128::from_be_bytes( + value_bytes.try_into().expect("Incorrect `value` field in RLP-encoded transaction."), + ); + + let signer_public_key_bytes: Vec = match rlp.val_at(2) { + Ok(signer_public_key_bytes) => signer_public_key_bytes, + _ => env::panic_str("Signature extraction failed for RLP-encoded transaction."), + }; + + let hash = env::keccak256(&signer_public_key_bytes); + let signer_address = format!("0x{}", hex::encode(&hash[12..32])); + + if signer_address != env::current_account_id().to_string() { + env::panic_str("Public key does not match the Wallet Contract address."); + } + + Promise::new(target).transfer(value); + } +} diff --git a/runtime/runtime/Cargo.toml b/runtime/runtime/Cargo.toml index ed5cdb4f37b..a0c185b410e 100644 --- a/runtime/runtime/Cargo.toml +++ b/runtime/runtime/Cargo.toml @@ -33,6 +33,7 @@ near-primitives.workspace = true near-primitives-core.workspace = true near-store.workspace = true near-vm-runner.workspace = true +near-wallet-contract.workspace = true [features] nightly = [ @@ -43,6 +44,7 @@ nightly = [ "near-primitives/nightly", "near-store/nightly", "near-vm-runner/nightly", + "near-wallet-contract/nightly", ] default = [] nightly_protocol = [ @@ -52,6 +54,7 @@ nightly_protocol = [ "near-primitives/nightly_protocol", "near-store/nightly_protocol", "near-vm-runner/nightly_protocol", + "near-wallet-contract/nightly_protocol", ] no_cpu_compatibility_checks = ["near-vm-runner/no_cpu_compatibility_checks"] diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 7b4e63509b5..d03d5164016 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -22,9 +22,7 @@ use near_primitives::transaction::{ }; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::{AccountId, BlockHeight, EpochInfoProvider, Gas, TrieCacheMode}; -use near_primitives::utils::{ - account_is_implicit, create_random_seed, wallet_contract_placeholder, -}; +use near_primitives::utils::{account_is_implicit, create_random_seed}; use near_primitives::version::{ ProtocolFeature, ProtocolVersion, DELETE_KEY_STORAGE_USAGE_PROTOCOL_VERSION, }; @@ -41,6 +39,28 @@ use near_vm_runner::logic::types::PromiseResult; use near_vm_runner::logic::{VMContext, VMOutcome}; use near_vm_runner::precompile_contract; use near_vm_runner::ContractCode; +use near_wallet_contract::{wallet_contract, wallet_contract_magic_bytes}; + +use std::sync::Arc; + +/// Returns `ContractCode` (if exists) for the given `account` or returns `StorageError`. +/// For ETH-implicit accounts returns `Wallet Contract` implementation that it is a part +/// of the protocol and it's cached in memory. +fn get_contract_code( + runtime_ext: &RuntimeExt, + account: &Account, + protocol_version: ProtocolVersion, +) -> Result>, StorageError> { + let account_id = runtime_ext.account_id(); + let code_hash = account.code_hash(); + if checked_feature!("stable", EthImplicitAccounts, protocol_version) + && account_id.get_account_type() == AccountType::EthImplicitAccount + { + assert!(code_hash == *wallet_contract_magic_bytes().hash()); + return Ok(Some(wallet_contract())); + } + runtime_ext.get_code(code_hash).map(|option| option.map(Arc::new)) +} /// Runs given function call with given context / apply state. pub(crate) fn execute_function_call( @@ -58,7 +78,8 @@ pub(crate) fn execute_function_call( ) -> Result { let account_id = runtime_ext.account_id(); tracing::debug!(target: "runtime", %account_id, "Calling the contract"); - let code = match runtime_ext.get_code(account.code_hash()) { + let code = match get_contract_code(&runtime_ext, account, apply_state.current_protocol_version) + { Ok(Some(code)) => code, Ok(None) => { let error = FunctionCallError::CompilationError(CompilationError::CodeDoesNotExist { @@ -478,22 +499,24 @@ pub(crate) fn action_implicit_account_creation_transfer( // It holds because in the only calling site, we've checked the permissions before. AccountType::EthImplicitAccount => { if checked_feature!("stable", EthImplicitAccounts, current_protocol_version) { - // TODO(eth-implicit) Use real Wallet Contract. - let wallet_contract = wallet_contract_placeholder(); + // We deploy "near[wallet contract hash]" magic bytes as the contract code, + // to mark that this is a neard-defined contract. It will not be used on a function call. + // Instead, neard-defined Wallet Contract implementation will be used. + let magic_bytes = wallet_contract_magic_bytes(); + let storage_usage = fee_config.storage_usage_config.num_bytes_account - + fee_config.storage_usage_config.num_extra_bytes_record - + wallet_contract.code().len() as u64; + + magic_bytes.code().len() as u64 + + fee_config.storage_usage_config.num_extra_bytes_record; *account = - Some(Account::new(transfer.deposit, 0, *wallet_contract.hash(), storage_usage)); + Some(Account::new(transfer.deposit, 0, *magic_bytes.hash(), storage_usage)); + set_code(state_update, account_id.clone(), &magic_bytes); - // TODO(eth-implicit) Store a reference to the `Wallet Contract` instead of literally deploying it. - set_code(state_update, account_id.clone(), &wallet_contract); - // 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 Wallet Contract and store result (compiled code or error) in the database. + // 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, + &wallet_contract(), &apply_state.config.wasm_config, apply_state.cache.as_deref(), )