Improve compile time (#1989)

## Issue Addressed

Closes #1264

## Proposed Changes

* Milagro BLS: tweak the feature flags so that Milagro doesn't get compiled if we're using BLST. Profiling showed that it was consuming about 1 minute of CPU time out of 60 minutes of CPU time (real time ~15 mins). A 1.6% saving.
* Reduce monomorphization: compiling for 3 different `EthSpec` types causes a heck of a lot of generic functions to be instantiated (monomorphized). Removing 2 of 3 cuts the LLVM+linking step from around 250 seconds to 180 seconds, a saving of 70 seconds (real time!). This applies only to `make` and not the CI build, because we test with the minimal spec on CI.
* Update `web3` crate to v0.13. This is perhaps the most controversial change, because it requires axing some deposit contract tools from `lcli`. I suspect these tools weren't used much anyway, and could be maintained separately, but I'm also happy to revert this change. However, it does save us a lot of compile time. With #1839, we now have 3 versions of Tokio (and all of Tokio's deps). This change brings us down to 2 versions, but 1 should be achievable once web3 (and reqwest) move to Tokio 0.3.
* Remove `lcli` from the Docker image. It's a dev tool and can be built from the repo if required.
This commit is contained in:
Michael Sproul
2020-12-09 01:34:58 +00:00
parent 4f85371ce8
commit 82753f842d
20 changed files with 286 additions and 1114 deletions

View File

@@ -20,9 +20,8 @@ types = { path = "../consensus/types" }
state_processing = { path = "../consensus/state_processing" }
eth2_ssz = "0.1.2"
regex = "1.3.9"
futures = { version = "0.3.7", features = ["compat"] }
futures = "0.3.7"
environment = { path = "../lighthouse/environment" }
web3 = "0.11.0"
eth2_network_config = { path = "../common/eth2_network_config" }
dirs = "3.0.1"
genesis = { path = "../beacon_node/genesis" }

View File

@@ -1,71 +0,0 @@
use clap::ArgMatches;
use deposit_contract::{
testnet::{ABI, BYTECODE},
CONTRACT_DEPLOY_GAS,
};
use environment::Environment;
use futures::compat::Future01CompatExt;
use std::path::PathBuf;
use tokio_compat_02::FutureExt;
use types::EthSpec;
use web3::{
contract::{Contract, Options},
transports::Ipc,
types::{Address, U256},
Web3,
};
pub fn run<T: EthSpec>(env: Environment<T>, matches: &ArgMatches<'_>) -> Result<(), String> {
let eth1_ipc_path: PathBuf = clap_utils::parse_required(matches, "eth1-ipc")?;
let from_address: Address = clap_utils::parse_required(matches, "from-address")?;
let confirmations: usize = clap_utils::parse_required(matches, "confirmations")?;
let (_event_loop_handle, transport) =
Ipc::new(eth1_ipc_path).map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
let web3 = Web3::new(transport);
let bytecode = String::from_utf8(BYTECODE.to_vec()).map_err(|e| {
format!(
"Unable to parse deposit contract bytecode as utf-8: {:?}",
e
)
})?;
env.runtime().block_on(
async {
// It's unlikely that this will be the _actual_ deployment block, however it'll be close
// enough to serve our purposes.
//
// We only need the deposit block to put a lower bound on the block number we need to search
// for deposit logs.
let deploy_block = web3
.eth()
.block_number()
.compat()
.await
.map_err(|e| format!("Failed to get block number: {}", e))?;
let pending_contract = Contract::deploy(web3.eth(), &ABI)
.map_err(|e| format!("Unable to build contract deployer: {:?}", e))?
.confirmations(confirmations)
.options(Options {
gas: Some(U256::from(CONTRACT_DEPLOY_GAS)),
..Options::default()
})
.execute(bytecode, (), from_address)
.map_err(|e| format!("Unable to execute deployment: {:?}", e))?;
let address = pending_contract
.compat()
.await
.map_err(|e| format!("Unable to await pending contract: {:?}", e))?
.address();
println!("deposit_contract_address: {:?}", address);
println!("deposit_contract_deploy_block: {}", deploy_block);
Ok(())
}
.compat(),
)
}

View File

@@ -2,14 +2,12 @@
extern crate log;
mod change_genesis_time;
mod check_deposit_data;
mod deploy_deposit_contract;
mod eth1_genesis;
mod generate_bootnode_enr;
mod insecure_validators;
mod interop_genesis;
mod new_testnet;
mod parse_hex;
mod refund_deposit_contract;
mod skip_slots;
mod transition_blocks;
@@ -35,9 +33,7 @@ fn main() {
let matches = App::new("Lighthouse CLI Tool")
.version(lighthouse_version::VERSION)
.about(
"Performs various testing-related tasks, including defining testnets.",
)
.about("Performs various testing-related tasks, including defining testnets.")
.arg(
Arg::with_name("spec")
.short("s")
@@ -46,7 +42,7 @@ fn main() {
.takes_value(true)
.required(true)
.possible_values(&["minimal", "mainnet"])
.default_value("mainnet")
.default_value("mainnet"),
)
.arg(
Arg::with_name("testnet-dir")
@@ -87,7 +83,9 @@ fn main() {
)
.subcommand(
SubCommand::with_name("skip-slots")
.about("Performs a state transition from some state across some number of skip slots")
.about(
"Performs a state transition from some state across some number of skip slots",
)
.arg(
Arg::with_name("pre-state")
.value_name("BEACON_STATE")
@@ -156,77 +154,9 @@ fn main() {
.help("SSZ encoded as 0x-prefixed hex"),
),
)
.subcommand(
SubCommand::with_name("deploy-deposit-contract")
.about(
"Deploy a testing eth1 deposit contract.",
)
.arg(
Arg::with_name("eth1-ipc")
.long("eth1-ipc")
.short("e")
.value_name("ETH1_IPC_PATH")
.help("Path to an Eth1 JSON-RPC IPC endpoint")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("from-address")
.long("from-address")
.short("f")
.value_name("FROM_ETH1_ADDRESS")
.help("The address that will submit the contract creation. Must be unlocked.")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("confirmations")
.value_name("INTEGER")
.long("confirmations")
.takes_value(true)
.default_value("3")
.help("The number of block confirmations before declaring the contract deployed."),
)
)
.subcommand(
SubCommand::with_name("refund-deposit-contract")
.about(
"Calls the steal() function on a testnet eth1 contract.",
)
.arg(
Arg::with_name("eth1-ipc")
.long("eth1-ipc")
.short("e")
.value_name("ETH1_IPC_PATH")
.help("Path to an Eth1 JSON-RPC IPC endpoint")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("from-address")
.long("from-address")
.short("f")
.value_name("FROM_ETH1_ADDRESS")
.help("The address that will submit the contract creation. Must be unlocked.")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("contract-address")
.long("contract-address")
.short("c")
.value_name("CONTRACT_ETH1_ADDRESS")
.help("The address of the contract to be refunded. Its owner must match
--from-address.")
.takes_value(true)
.required(true)
)
)
.subcommand(
SubCommand::with_name("eth1-genesis")
.about(
"Listens to the eth1 chain and finds the genesis beacon state",
)
.about("Listens to the eth1 chain and finds the genesis beacon state")
.arg(
Arg::with_name("eth1-endpoint")
.short("e")
@@ -241,16 +171,16 @@ fn main() {
.value_name("HTTP_SERVER_LIST")
.takes_value(true)
.conflicts_with("eth1-endpoint")
.help("One or more comma-delimited URLs to eth1 JSON-RPC http APIs. \
.help(
"One or more comma-delimited URLs to eth1 JSON-RPC http APIs. \
If multiple endpoints are given the endpoints are used as \
fallback in the given order."),
)
fallback in the given order.",
),
),
)
.subcommand(
SubCommand::with_name("interop-genesis")
.about(
"Produces an interop-compatible genesis state using deterministic keypairs",
)
.about("Produces an interop-compatible genesis state using deterministic keypairs")
.arg(
Arg::with_name("validator-count")
.long("validator-count")
@@ -273,9 +203,11 @@ fn main() {
.long("genesis-fork-version")
.value_name("HEX")
.takes_value(true)
.help("Used to avoid reply attacks between testnets. Recommended to set to
non-default."),
)
.help(
"Used to avoid reply attacks between testnets. Recommended to set to
non-default.",
),
),
)
.subcommand(
SubCommand::with_name("change-genesis-time")
@@ -297,7 +229,7 @@ fn main() {
.takes_value(true)
.required(true)
.help("The value for state.genesis_time."),
)
),
)
.subcommand(
SubCommand::with_name("new-testnet")
@@ -317,8 +249,10 @@ fn main() {
.long("min-genesis-time")
.value_name("UNIX_SECONDS")
.takes_value(true)
.help("The minimum permitted genesis time. For non-eth1 testnets will be
the genesis time. Defaults to now."),
.help(
"The minimum permitted genesis time. For non-eth1 testnets will be
the genesis time. Defaults to now.",
),
)
.arg(
Arg::with_name("min-genesis-active-validator-count")
@@ -374,8 +308,10 @@ fn main() {
.long("genesis-fork-version")
.value_name("HEX")
.takes_value(true)
.help("Used to avoid reply attacks between testnets. Recommended to set to
non-default."),
.help(
"Used to avoid reply attacks between testnets. Recommended to set to
non-default.",
),
)
.arg(
Arg::with_name("deposit-contract-address")
@@ -391,15 +327,15 @@ fn main() {
.value_name("ETH1_BLOCK_NUMBER")
.takes_value(true)
.default_value("0")
.help("The block the deposit contract was deployed. Setting this is a huge
optimization for nodes, please do it."),
)
.help(
"The block the deposit contract was deployed. Setting this is a huge
optimization for nodes, please do it.",
),
),
)
.subcommand(
SubCommand::with_name("check-deposit-data")
.about(
"Checks the integrity of some deposit data.",
)
.about("Checks the integrity of some deposit data.")
.arg(
Arg::with_name("deposit-amount")
.index(1)
@@ -414,15 +350,15 @@ fn main() {
.value_name("HEX")
.takes_value(true)
.required(true)
.help("A 0x-prefixed hex string of the deposit data. Should include the
function signature."),
)
.help(
"A 0x-prefixed hex string of the deposit data. Should include the
function signature.",
),
),
)
.subcommand(
SubCommand::with_name("generate-bootnode-enr")
.about(
"Generates an ENR address to be used as a pre-genesis boot node.",
)
.about("Generates an ENR address to be used as a pre-genesis boot node.")
.arg(
Arg::with_name("ip")
.long("ip")
@@ -445,7 +381,9 @@ fn main() {
.value_name("TCP_PORT")
.takes_value(true)
.required(true)
.help("The TCP port to be included in the ENR and used for application comms"),
.help(
"The TCP port to be included in the ENR and used for application comms",
),
)
.arg(
Arg::with_name("output-dir")
@@ -461,15 +399,15 @@ fn main() {
.value_name("HEX")
.takes_value(true)
.required(true)
.help("Used to avoid reply attacks between testnets. Recommended to set to
non-default."),
)
.help(
"Used to avoid reply attacks between testnets. Recommended to set to
non-default.",
),
),
)
.subcommand(
SubCommand::with_name("insecure-validators")
.about(
"Produces validator directories with INSECURE, deterministic keypairs.",
)
.about("Produces validator directories with INSECURE, deterministic keypairs.")
.arg(
Arg::with_name("count")
.long("count")
@@ -490,7 +428,7 @@ fn main() {
.value_name("SECRETS_DIR")
.takes_value(true)
.help("The directory for storing secrets."),
)
),
)
.get_matches();
@@ -572,14 +510,6 @@ fn run<T: EthSpec>(
("pretty-hex", Some(matches)) => {
run_parse_hex::<T>(matches).map_err(|e| format!("Failed to pretty print hex: {}", e))
}
("deploy-deposit-contract", Some(matches)) => {
deploy_deposit_contract::run::<T>(env, matches)
.map_err(|e| format!("Failed to run deploy-deposit-contract command: {}", e))
}
("refund-deposit-contract", Some(matches)) => {
refund_deposit_contract::run::<T>(env, matches)
.map_err(|e| format!("Failed to run refund-deposit-contract command: {}", e))
}
("eth1-genesis", Some(matches)) => eth1_genesis::run::<T>(env, matches)
.map_err(|e| format!("Failed to run eth1-genesis command: {}", e)),
("interop-genesis", Some(matches)) => interop_genesis::run::<T>(env, matches)

View File

@@ -1,47 +0,0 @@
use clap::ArgMatches;
use environment::Environment;
use futures::compat::Future01CompatExt;
use std::path::PathBuf;
use tokio_compat_02::FutureExt;
use types::EthSpec;
use web3::{
transports::Ipc,
types::{Address, TransactionRequest, U256},
Web3,
};
/// `keccak("steal()")[0..4]`
pub const STEAL_FN_SIGNATURE: &[u8] = &[0xcf, 0x7a, 0x89, 0x65];
pub fn run<T: EthSpec>(env: Environment<T>, matches: &ArgMatches<'_>) -> Result<(), String> {
let eth1_ipc_path: PathBuf = clap_utils::parse_required(matches, "eth1-ipc")?;
let from: Address = clap_utils::parse_required(matches, "from-address")?;
let contract_address: Address = clap_utils::parse_required(matches, "contract-address")?;
let (_event_loop_handle, transport) =
Ipc::new(eth1_ipc_path).map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
let web3 = Web3::new(transport);
env.runtime().block_on(
async {
let _ = web3
.eth()
.send_transaction(TransactionRequest {
from,
to: Some(contract_address),
gas: Some(U256::from(400_000)),
gas_price: None,
value: Some(U256::zero()),
data: Some(STEAL_FN_SIGNATURE.into()),
nonce: None,
condition: None,
})
.compat()
.await
.map_err(|e| format!("Failed to call steal fn: {:?}", e))?;
Ok(())
}
.compat(),
)
}