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

feat: adds account configuration options #388

Merged
merged 12 commits into from
Nov 20, 2024
Merged
222 changes: 139 additions & 83 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ zksync_web3_decl = { git = "https://github.com/matter-labs/zksync-era.git", rev
] }
sha3 = "0.10.6"

# alloy
alloy-signer-local = { version = "0.5.4", features = ["mnemonic"] }
alloy-signer = { version = "0.5.4", default-features = false }

openssl-sys = { version = "0.9", features = ["vendored"] }

Expand Down Expand Up @@ -57,6 +60,7 @@ indexmap = "2.0.1"
chrono = { version = "0.4.31", default-features = false }
time = "0.3.36"
toml = "0.8.13"
rand = "0.8"

[dev-dependencies]
httptest = "0.15.4"
Expand Down
40 changes: 0 additions & 40 deletions e2e-tests/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,44 +39,4 @@ export const RichAccounts = [
Account: "0xE90E12261CCb0F3F7976Ae611A29e84a6A85f424",
PrivateKey: "0x3eb15da85647edd9a1159a4a13b9e7c56877c4eb33f614546d4db06a51868b1c",
},
{
Account: "0xBC989fDe9e54cAd2aB4392Af6dF60f04873A033A",
PrivateKey: "0x3d3cbc973389cb26f657686445bcc75662b415b656078503592ac8c1abb8810e",
},
{
Account: "0x55bE1B079b53962746B2e86d12f158a41DF294A6",
PrivateKey: "0x509ca2e9e6acf0ba086477910950125e698d4ea70fa6f63e000c5a22bda9361c",
},
{
Account: "0xCE9e6063674DC585F6F3c7eaBe82B9936143Ba6C",
PrivateKey: "0x71781d3a358e7a65150e894264ccc594993fbc0ea12d69508a340bc1d4f5bfbc",
},
{
Account: "0xd986b0cB0D1Ad4CCCF0C4947554003fC0Be548E9",
PrivateKey: "0x379d31d4a7031ead87397f332aab69ef5cd843ba3898249ca1046633c0c7eefe",
},
{
Account: "0x87d6ab9fE5Adef46228fB490810f0F5CB16D6d04",
PrivateKey: "0x105de4e75fe465d075e1daae5647a02e3aad54b8d23cf1f70ba382b9f9bee839",
},
{
Account: "0x78cAD996530109838eb016619f5931a03250489A",
PrivateKey: "0x7becc4a46e0c3b512d380ca73a4c868f790d1055a7698f38fb3ca2b2ac97efbb",
},
{
Account: "0xc981b213603171963F81C687B9fC880d33CaeD16",
PrivateKey: "0xe0415469c10f3b1142ce0262497fe5c7a0795f0cbfd466a6bfa31968d0f70841",
},
{
Account: "0x42F3dc38Da81e984B92A95CBdAAA5fA2bd5cb1Ba",
PrivateKey: "0x4d91647d0a8429ac4433c83254fb9625332693c848e578062fe96362f32bfe91",
},
{
Account: "0x64F47EeD3dC749d13e49291d46Ea8378755fB6DF",
PrivateKey: "0x41c9f9518aa07b50cb1c0cc160d45547f57638dd824a8d85b5eb3bf99ed2bdeb",
},
{
Account: "0xe2b8Cb53a43a56d4d2AB6131C81Bd76B86D3AFe5",
PrivateKey: "0xb0680d66303a0163a19294f1ef8c95cd69a9d7902a4aca99c05f3e134e68a11a",
},
] as const;
34 changes: 32 additions & 2 deletions e2e-tests/test/eth-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ethers } from "ethers";
const provider = getTestProvider();

describe("eth_accounts", function () {
it("Should return rich accounts", async function () {
it("Should return legacy rich accounts", async function () {
// Arrange
const richAccounts = RichAccounts.map((ra) => ethers.utils.getAddress(ra.Account)).sort();

Expand All @@ -16,7 +16,37 @@ describe("eth_accounts", function () {
const accounts = response.map((addr) => ethers.utils.getAddress(addr)).sort();

// Assert
expect(accounts).to.deep.equal(richAccounts);
expect(accounts).to.include.members(richAccounts);
});

it("Should return genesis accounts with sufficient balances", async function () {
// Arrange
const genesisAccounts = [
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"0x90F79bf6EB2c4f870365E785982E1f101E93b906",
"0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65",
"0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"0x976EA74026E726554dB657fA54763abd0C3a0aa9",
"0x14dC79964da2C08b23698B3D3cc7Ca32193d9955",
"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f",
"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
];
const expectedBalance = ethers.utils.parseEther("100");

// Act
const response: string[] = await provider.send("eth_accounts", []);
const accounts = response.map((addr) => ethers.utils.getAddress(addr));

// Assert
expect(accounts).to.include.members(genesisAccounts);

// Assert
for (const account of genesisAccounts) {
const balance = await provider.getBalance(account);
expect(balance.toString()).to.equal(expectedBalance.toString());
}
});

it("Should have required fields in transaction receipt", async function () {
Expand Down
49 changes: 0 additions & 49 deletions examples/config.toml

This file was deleted.

105 changes: 93 additions & 12 deletions src/config/cli.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use std::env;

use clap::{arg, command, Parser, Subcommand};
use zksync_types::H256;
use rand::{rngs::StdRng, SeedableRng};
use zksync_types::{H256, U256};

use crate::config::constants::{DEFAULT_MNEMONIC, TEST_NODE_NETWORK_ID};
use crate::config::{
CacheConfig, CacheType, ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails,
TestNodeConfig,
AccountGenerator, CacheConfig, CacheType, ShowCalls, ShowGasDetails, ShowStorageLogs,
ShowVMDetails, TestNodeConfig,
};
use crate::observability::LogLevel;
use crate::system_contracts::Options as SystemContractsOptions;

use super::DEFAULT_DISK_CACHE_DIR;
use alloy_signer_local::coins_bip39::{English, Mnemonic};

#[derive(Debug, Parser)]
#[command(
Expand All @@ -22,9 +27,9 @@ pub struct Cli {
pub command: Option<Command>,

// General Options
#[arg(short, long, help_heading = "General Options")]
/// Path to the configuration file. If not supplied, defaults will be used.
pub config: Option<String>,
#[arg(long, help_heading = "General Options")]
/// Run in offline mode (disables all network requests).
pub offline: bool,

#[arg(long, default_value = "8011", help_heading = "Network Options")]
/// Port to listen on (default: 8011).
Expand All @@ -34,10 +39,6 @@ pub struct Cli {
/// Specify chain ID (default: 260).
pub chain_id: Option<u32>,

#[arg(long, help_heading = "General Options")]
/// Run in offline mode (disables all network requests).
pub offline: bool,

#[arg(short, long, help_heading = "Debugging Options")]
/// Enable default settings for debugging contracts.
pub debug_mode: bool,
Expand Down Expand Up @@ -127,6 +128,49 @@ pub struct Cli {
#[arg(long, help_heading = "Cache Options")]
/// Cache directory location for disk cache (default: .cache).
pub cache_dir: Option<String>,

/// Number of dev accounts to generate and configure.
#[arg(
long,
short,
default_value = "10",
value_name = "NUM",
help_heading = "Account Configuration"
)]
pub accounts: u64,

/// The balance of every dev account in Ether.
#[arg(
long,
default_value = "10000",
value_name = "NUM",
help_heading = "Account Configuration"
)]
pub balance: u64,

/// BIP39 mnemonic phrase used for generating accounts.
/// Cannot be used if `mnemonic_random` or `mnemonic_seed` are used.
#[arg(long, short, conflicts_with_all = &["mnemonic_seed", "mnemonic_random"], help_heading = "Account Configuration")]
pub mnemonic: Option<String>,

/// Automatically generates a BIP39 mnemonic phrase and derives accounts from it.
/// Cannot be used with other `mnemonic` options.
/// You can specify the number of words you want in the mnemonic.
/// [default: 12]
#[arg(long, conflicts_with_all = &["mnemonic", "mnemonic_seed"], default_missing_value = "12", num_args(0..=1), help_heading = "Account Configuration")]
pub mnemonic_random: Option<usize>,

/// Generates a BIP39 mnemonic phrase from a given seed.
/// Cannot be used with other `mnemonic` options.
/// CAREFUL: This is NOT SAFE and should only be used for testing.
/// Never use the private keys generated in production.
#[arg(long = "mnemonic-seed-unsafe", conflicts_with_all = &["mnemonic", "mnemonic_random"], help_heading = "Account Configuration")]
pub mnemonic_seed: Option<u64>,

/// Sets the derivation path of the child key to be derived.
/// [default: m/44'/60'/0'/0/]
#[arg(long, help_heading = "Account Configuration")]
pub derivation_path: Option<String>,
}

#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -186,8 +230,19 @@ pub struct ReplayArgs {
}

impl Cli {
/// Converts the CLI arguments into a `TestNodeConfig`.
pub fn into_test_node_config(self) -> eyre::Result<TestNodeConfig> {
/// Checks for deprecated options and warns users.
pub fn deprecated_config_option() {
if env::args().any(|arg| arg == "--config" || arg.starts_with("--config=")) {
eprintln!(
"Warning: The '--config' option has been removed. \
Please migrate to using other configuration options or defaults."
);
}
}
/// Converts the CLI arguments to a `TestNodeConfig`.
pub fn to_test_node_config(&self) -> eyre::Result<TestNodeConfig> {
let genesis_balance = U256::from(100u128 * 10u128.pow(18));

let vm_log_detail = if let Some(output) = self.show_outputs {
if output {
Some(ShowVMDetails::All)
Expand Down Expand Up @@ -217,6 +272,8 @@ impl Cli {
.with_override_bytecodes_dir(self.override_bytecodes_dir.clone()) // Added
.with_log_level(self.log)
.with_log_file_path(self.log_file_path.clone())
.with_account_generator(self.account_generator())
.with_genesis_balance(genesis_balance)
.with_cache_config(self.cache.map(|cache_type| {
match cache_type {
CacheType::None => CacheConfig::None,
Expand Down Expand Up @@ -248,4 +305,28 @@ impl Cli {
Ok(config)
}
}

fn account_generator(&self) -> AccountGenerator {
let mut gen = AccountGenerator::new(self.accounts as usize)
.phrase(DEFAULT_MNEMONIC)
.chain_id(self.chain_id.unwrap_or(TEST_NODE_NETWORK_ID));
if let Some(ref mnemonic) = self.mnemonic {
gen = gen.phrase(mnemonic);
} else if let Some(count) = self.mnemonic_random {
let mut rng = rand::thread_rng();
let mnemonic = match Mnemonic::<English>::new_with_count(&mut rng, count) {
Ok(mnemonic) => mnemonic.to_phrase(),
Err(_) => DEFAULT_MNEMONIC.to_string(),
};
gen = gen.phrase(mnemonic);
} else if let Some(seed) = self.mnemonic_seed {
let mut seed = StdRng::seed_from_u64(seed);
let mnemonic = Mnemonic::<English>::new(&mut seed).to_phrase();
gen = gen.phrase(mnemonic);
}
if let Some(ref derivation) = self.derivation_path {
gen = gen.derivation_path(derivation);
}
gen
}
}
8 changes: 4 additions & 4 deletions src/config/constants.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
/// Directory where configuration files are stored
pub const CONFIG_DIR: &str = ".era_test_node";
/// Default name of the configuration file
pub const CONFIG_FILE_NAME: &str = "config.toml";
/// Default directory for disk cache
pub const DEFAULT_DISK_CACHE_DIR: &str = ".cache";
/// Default L1 gas price for transactions
Expand All @@ -18,8 +14,12 @@ pub const DEFAULT_ESTIMATE_GAS_SCALE_FACTOR: f32 = 1.3;
pub const NODE_PORT: u16 = 8011;
/// Network ID for the test node
pub const TEST_NODE_NETWORK_ID: u32 = 260;
/// Default derivation path for the test node
pub const DERIVATION_PATH: &str = "m/44'/60'/0'/0/0";
/// Default log file path for the test node
pub const DEFAULT_LOG_FILE_PATH: &str = "era_test_node.log";
/// Default mnemonic phrase for the test node
pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
// List of wallets (address, private key, mnemonic) that we seed with tokens at start.
pub const RICH_WALLETS: [(&str, &str, &str); 10] = [
(
Expand Down
Loading
Loading