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 placeholder #10269

Merged
merged 17 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
28 changes: 25 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -287,6 +289,7 @@ reqwest = { version = "0.11.14", features = ["blocking"] }
ripemd = "0.1.1"
rkyv = "0.7.31"
rlimit = "0.7"
rlp = "0.4.6"
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"] }
Expand Down
1 change: 0 additions & 1 deletion core/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 0 additions & 7 deletions core/primitives/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ use near_primitives::shard_layout::ShardLayout;
use near_primitives::sharding::ChunkHash;
use near_primitives::transaction::{Action, AddKeyAction, DeployContractAction, 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::utils::{derive_eth_implicit_account_id, derive_near_implicit_account_id};
use near_primitives::version::{ProtocolFeature, ProtocolVersion, PROTOCOL_VERSION};
use near_primitives::views::FinalExecutionStatus;
use near_wallet_contract::wallet_contract;
use nearcore::config::GenesisExt;
use nearcore::test_utils::TestEnvNightshadeSetupExt;
use nearcore::NEAR_BASE;
Expand Down Expand Up @@ -251,7 +250,7 @@ fn test_transaction_from_eth_implicit_account_fail() {
.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 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");
Expand Down Expand Up @@ -324,7 +323,7 @@ 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_placeholder().code().to_vec();
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(),
Expand Down
116 changes: 110 additions & 6 deletions integration-tests/src/tests/client/features/delegate_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ use crate::tests::standard_cases::fee_helper;
use near_chain::ChainGenesis;
use near_chain_configs::Genesis;
use near_client::test_utils::TestEnv;
use near_crypto::{KeyType, PublicKey, Signer};
use near_crypto::{KeyType, PublicKey, SecretKey, Signer};
use near_primitives::account::{
id::AccountType, AccessKey, AccessKeyPermission, FunctionCallPermission,
};
use near_primitives::checked_feature;
use near_primitives::config::ActionCosts;
use near_primitives::errors::{
ActionError, ActionErrorKind, ActionsValidationError, InvalidAccessKeyError, InvalidTxError,
TxExecutionError,
ActionError, ActionErrorKind, ActionsValidationError, FunctionCallError, InvalidAccessKeyError,
InvalidTxError, TxExecutionError,
};
use near_primitives::test_utils::{
create_user_test_signer, eth_implicit_test_account, near_implicit_test_account,
Expand All @@ -26,6 +26,7 @@ use near_primitives::transaction::{
DeployContractAction, FunctionCallAction, StakeAction, TransferAction,
};
use near_primitives::types::{AccountId, Balance};
use near_primitives::utils::derive_eth_implicit_account_id;
use near_primitives::version::{ProtocolFeature, ProtocolVersion, PROTOCOL_VERSION};
use near_primitives::views::{
AccessKeyPermissionView, ExecutionStatusView, FinalExecutionOutcomeView, FinalExecutionStatus,
Expand All @@ -34,6 +35,7 @@ use near_test_contracts::{ft_contract, smallest_rs_contract};
use nearcore::config::GenesisExt;
use nearcore::test_utils::TestEnvNightshadeSetupExt;
use nearcore::NEAR_BASE;
use rlp::RlpStream;
use testlib::runtime_utils::{
add_account_with_access_key, add_contract, add_test_contract, alice_account, bob_account,
carol_account, eve_dot_alice_account,
Expand Down Expand Up @@ -281,7 +283,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);
Expand Down Expand Up @@ -837,7 +839,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() }),
Expand Down Expand Up @@ -887,7 +889,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() {
Expand Down Expand Up @@ -945,3 +952,100 @@ fn meta_tx_create_eth_implicit_account() {
}
meta_tx_create_implicit_account(eth_implicit_test_account());
}

// 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.
///
/// Depending on `rlp_transaction` blob that is sent to the `Wallet Contract`
/// the transaction is either authorized or unauthorized.
/// The parameter `authorized` controls which case will be tested.
fn meta_tx_call_wallet_contract(authorized: bool) {
staffik marked this conversation as resolved.
Show resolved Hide resolved
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 })];
// 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.
staffik marked this conversation as resolved.
Show resolved Hide resolved
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, relayer, actions).unwrap();
let wallet_contract_call_result = &tx_result.receipts_outcome[1].outcome.status;

if authorized {
// If the public key recovered from the RLP transaction's signature is valid for this ETH-implicit account,
// the transaction will succeed. `target` 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);
} else {
// 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);
}
}

#[test]
fn meta_tx_call_wallet_contract_authorized() {
meta_tx_call_wallet_contract(true);
}

#[test]
fn meta_tx_call_wallet_contract_unauthorized() {
meta_tx_call_wallet_contract(false);
}
2 changes: 1 addition & 1 deletion integration-tests/src/tests/standard_cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/src/tests/standard_cases/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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]
Expand Down
1 change: 1 addition & 0 deletions runtime/near-vm-runner/src/code.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use near_primitives_core::hash::{hash as sha256, CryptoHash};

#[derive(Clone)]
staffik marked this conversation as resolved.
Show resolved Hide resolved
pub struct ContractCode {
code: Vec<u8>,
hash: CryptoHash,
Expand Down
Loading
Loading