diff --git a/Cargo.lock b/Cargo.lock index 13a22a7d2ada..c1a5b924c291 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1935,6 +1935,7 @@ dependencies = [ "foundry-config", "foundry-evm", "foundry-test-utils", + "foundry-tweak", "foundry-wallets", "futures", "indicatif", @@ -3336,6 +3337,7 @@ dependencies = [ "foundry-evm", "foundry-linking", "foundry-test-utils", + "foundry-tweak", "foundry-wallets", "futures", "globset", @@ -3447,6 +3449,7 @@ dependencies = [ "foundry-debugger", "foundry-evm", "foundry-linking", + "foundry-tweak", "foundry-wallets", "futures", "indicatif", @@ -4057,6 +4060,31 @@ dependencies = [ "walkdir", ] +[[package]] +name = "foundry-tweak" +version = "0.2.0" +dependencies = [ + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types", + "eyre", + "foundry-block-explorers", + "foundry-cli", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-evm", + "indicatif", + "revm", + "serde", + "serde_json", + "tempfile", + "tokio", + "toml 0.8.14", + "vergen", + "yansi", +] + [[package]] name = "foundry-wallets" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 5c3ae60d585b..9701bfe7afaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "crates/forge/", "crates/macros/", "crates/test-utils/", + "crates/tweak/" ] resolver = "2" @@ -153,6 +154,7 @@ foundry-evm-fuzz = { path = "crates/evm/fuzz" } foundry-evm-traces = { path = "crates/evm/traces" } foundry-macros = { path = "crates/macros" } foundry-test-utils = { path = "crates/test-utils" } +foundry-tweak = { path = "crates/tweak" } foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } diff --git a/README.md b/README.md index 6ca5e9a6ee2b..418bc39a1b39 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ ![Github Actions][gha-badge] [![Telegram Chat][tg-badge]][tg-url] [![Telegram Support][tg-support-badge]][tg-support-url] +### Foundry tweak is available to tweak the code of on-chain contracts, please see [TWEAK.md](./TWEAK.md)! + [gha-badge]: https://img.shields.io/github/actions/workflow/status/foundry-rs/foundry/test.yml?branch=master [tg-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=chat&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Ffoundry_rs [tg-url]: https://t.me/foundry_rs diff --git a/TWEAK.md b/TWEAK.md new file mode 100644 index 000000000000..7e15db2b7787 --- /dev/null +++ b/TWEAK.md @@ -0,0 +1,72 @@ +# Foundry with on-chain contract Tweak! + +**Tweak** allows users/developers to alter the code of an on-chain contract with the on-chain state untouched. +Almost any modification is allowed, as long as the storage layout is preserved. + +With **Tweak**, you can: +- Add `console.log()` in any on-chain contracts with source code available on Etherscan. +- Change the logic of an on-chain contract (e.g., fixing bugs, conduct what-if analysis, etc.) + +Tweaking on-chain contracts are especially useful when: +- inspecting the execution on-chain transactions; +- investigating the logic of a complex contract (e.g., UniswapV3) on chain; +- debug the interaction with on-chain contracts which produce unexpected revert errors. + +Here's the demo! +[![asciicast](https://asciinema.org/a/650466.svg)](https://asciinema.org/a/650466) + +## Installation + +This project is a fork of foundry. +The easiest way to install is to compile this project and replace the foundry toolchain originally on your machine. + +``` +cargo build +``` + +The executables are available in `target/debug` folder. +All the tools that you are familiar in foundry are still there. + +What is enhanced is the `cast` and `forge` command. + +## Usage 1: Clone a verified contract code from Etherscan + +``` +forge clone
+``` + +`forge clone` is a new feature that downloads the source code of a verified contract at `
` from Etherscan (or other block explorers supported by foundry) and save as a foundry project in the specified ``. + +The cloned project is fully compilable foundry project, guaranteed to generate exactly the same bytecode as the on-chain contract. + +## Usage 2: Tweak one on-chain contract and replay a historical transaction invoking it + +``` +cd path/to/cloned/contract/project +forge replay +``` + +If the current working directory is a foundry project created by `forge clone`, then the command `forge replay` is available to replay a historical transaction with the on-chain contract's code tweaked by the current project. +Note that the cloned project "knows" which contract address it should tweak. + +You may want to modify the code of the cloned contract (e.g., add `console.log`s) before running `forge replay`. + +## Usage 3: Tweak multiple on-chain contracts and replay a historical transaction invoking them + +``` +cast run --tweak --tweak +``` + +`cast run` is also enhanced by allowing specified cloned projects to tweak the code of multiple on-chain contracts. +Note that each cloned project "knows" which contract address it should tweak. + +## Disclaimer + +The **Tweak** feature is still under active development as part of the __EtherDebug__ project, whose ultimate goal is improve the overall development and debugging experience of EVM-compatibla smart contracts. + +The functionality is not stable yet and may suffer from breaking change in the future. +Any contribution (bug reports, PRs, documentation, etc.) is welcome and greatly appreciated. + +## Acknowledgement + +Contributed by @Troublor and @ZhangZhuoSJTU with passion. \ No newline at end of file diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 602ec57ae495..07de3b3ca2f1 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -32,6 +32,7 @@ foundry-compilers.workspace = true foundry-config.workspace = true foundry-evm.workspace = true foundry-wallets.workspace = true +foundry-tweak.workspace = true alloy-chains.workspace = true alloy-consensus = { workspace = true, features = ["serde", "kzg"] } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index c830f5ab1aa4..ad960d3087d6 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,7 +1,9 @@ +use std::path::PathBuf; + use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::BlockTransactions; -use cast::{revm::primitives::EnvWithHandlerCfg, traces::TraceKind}; +use cast::{decode::decode_console_logs, revm::primitives::EnvWithHandlerCfg, traces::TraceKind}; use clap::Parser; use eyre::{Result, WrapErr}; use foundry_cli::{ @@ -16,6 +18,7 @@ use foundry_evm::{ opts::EvmOpts, utils::configure_tx_env, }; +use foundry_tweak::tweak_backend; /// CLI arguments for `cast run`. #[derive(Clone, Debug, Parser)] @@ -32,6 +35,7 @@ pub struct RunArgs { trace_printer: bool, /// Executes the transaction only with the state from the previous block. + /// Note that this also include transactions that are used for tweaking code. /// /// May result in different results than the live execution! #[arg(long, short)] @@ -71,6 +75,13 @@ pub struct RunArgs { /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second #[arg(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")] pub no_rate_limit: bool, + + /// One `forge clone`d project that will be used to tweak the code of the corresponding + /// on-chain contract. + /// + /// This option can be used multiple times to tweak multiple contracts. + #[arg(long, value_name = "CLONED_PROJECT")] + pub tweak: Vec, } impl RunArgs { @@ -80,8 +91,8 @@ impl RunArgs { /// /// Note: This executes the transaction(s) as is: Cheatcodes are disabled pub async fn run(self) -> Result<()> { - let figment = - Config::figment_with_root(find_project_root_path(None).unwrap()).merge(self.rpc); + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) + .merge(self.rpc.clone()); let evm_opts = figment.extract::()?; let mut config = Config::try_from(figment)?.sanitized(); @@ -143,6 +154,24 @@ impl RunArgs { } let mut executor = TracingExecutor::new(env.clone(), fork, evm_version, self.debug); + if !self.tweak.is_empty() { + // If user specified tweak projects, we need to tweak the code of the contracts + let mut cloned_projects: Vec = vec![]; + for path in self.tweak.iter() { + let path = dunce::canonicalize(path) + .map_err(|e| eyre::eyre!("failed to load tweak project: {:?}", e))?; + let project = + foundry_tweak::ClonedProject::load_with_root(&path).wrap_err_with(|| { + format!("failed to load tweak project from path: {:?}", &path) + })?; + cloned_projects.push(project); + } + let tweak_map = + foundry_tweak::build_tweak_data(&cloned_projects, &self.rpc, self.quick).await?; + tweak_backend(executor.backend_mut(), &tweak_map)?; + } + println!("Executing transaction: {:?}", tx.hash); + let mut env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id()); @@ -158,6 +187,10 @@ impl RunArgs { return Err(eyre::eyre!("Could not get block txs")) }; + let txs = txs.into_iter().take_while(|tx| tx.hash != tx_hash).collect::>(); + let pb = init_progress(txs.len() as u64, "tx"); + pb.set_position(0); + for (index, tx) in txs.into_iter().enumerate() { // System transactions such as on L2s don't contain any pricing info so // we skip them otherwise this would cause @@ -206,22 +239,44 @@ impl RunArgs { } // Execute our transaction - let result = { - executor.set_trace_printer(self.trace_printer); - + let (result, console_logs) = { configure_tx_env(&mut env, &tx); if let Some(to) = tx.to { trace!(tx=?tx.hash, to=?to, "executing call transaction"); - TraceResult::from_raw(executor.transact_with_env(env)?, TraceKind::Execution) + let result = executor.transact_with_env(env)?; + let logs = decode_console_logs(&result.logs); + (TraceResult::from_raw(result, TraceKind::Execution), logs) } else { trace!(tx=?tx.hash, "executing create transaction"); - TraceResult::try_from(executor.deploy_with_env(env, None))? + let deploy_result = executor.deploy_with_env(env, None); + match deploy_result { + Ok(res) => { + let logs = decode_console_logs(&res.logs); + (TraceResult::from(res), logs) + } + Err(ref err) => { + let logs = match &err { + EvmError::Execution(e) => decode_console_logs(&e.logs), + _ => vec![], + }; + (TraceResult::try_from(deploy_result)?, logs) + } + } } }; handle_traces(result, &config, chain, self.label, self.debug).await?; + // print logs if any + if !console_logs.is_empty() { + println!("Logs:"); + for log in console_logs { + println!(" {log}"); + } + println!(); + } + Ok(()) } } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 32403dceb51f..fe8c742f072b 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -136,6 +136,11 @@ impl Executor { &self.env.env } + /// Returns a reference to the EVM environment with the handler configuration. + pub fn env_with_handler_cfg(&self) -> &EnvWithHandlerCfg { + &self.env + } + /// Returns a mutable reference to the EVM environment. pub fn env_mut(&mut self) -> &mut Env { &mut self.env.env diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 93b15ed61d00..eedc6c5f47b8 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -33,6 +33,7 @@ foundry-config.workspace = true foundry-evm.workspace = true foundry-wallets.workspace = true foundry-linking.workspace = true +foundry-tweak.workspace = true ethers-contract-abigen = { workspace = true, features = ["providers"] } diff --git a/crates/forge/bin/cmd/debug.rs b/crates/forge/bin/cmd/debug.rs index 8fe1d2e32a25..eab4d970ab22 100644 --- a/crates/forge/bin/cmd/debug.rs +++ b/crates/forge/bin/cmd/debug.rs @@ -29,6 +29,20 @@ pub struct DebugArgs { #[arg(long, short, default_value = "run()", value_name = "SIGNATURE")] pub sig: String, + /// One `forge clone`d project that will be used to tweak the code of the corresponding + /// on-chain contract. + /// + /// This option can be used multiple times to tweak multiple contracts. + #[arg(long, value_name = "CLONED_PROJECT")] + pub tweak: Vec, + + /// When tweaking the contract, use the quick mode where we replay the creation transaction + /// only with the state from the previous block. + /// + /// May result in different results than the live execution! + #[arg(long, requires = "tweak")] + pub tweak_quick: bool, + /// Open the script in the debugger. #[arg(long)] pub debug: bool, @@ -50,8 +64,10 @@ impl DebugArgs { gas_estimate_multiplier: 130, opts: self.opts, evm_opts: self.evm_opts, - debug: true, + debug: self.debug, retry: RETRY_VERIFY_ON_CREATE, + tweak: self.tweak, + tweak_quick: self.tweak_quick, ..Default::default() }; script.run_script().await diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index d3e2f8b6d314..0b05f2480936 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -57,6 +57,7 @@ pub mod inspect; pub mod install; pub mod remappings; pub mod remove; +pub mod replay; pub mod selectors; pub mod snapshot; pub mod soldeer; diff --git a/crates/forge/bin/cmd/replay.rs b/crates/forge/bin/cmd/replay.rs new file mode 100644 index 000000000000..18f1b170d690 --- /dev/null +++ b/crates/forge/bin/cmd/replay.rs @@ -0,0 +1,374 @@ +use std::collections::BTreeMap; + +use alloy_network::TransactionResponse; +use alloy_primitives::{Address, Bytes, TxHash, U128, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{Block, BlockTransactions, BlockTransactionsKind, Transaction}; +use eyre::{eyre, Context, Result}; +use forge::{ + decode::decode_console_logs, + executors::{DeployResult, EvmError, Executor, ExecutorBuilder, RawCallResult}, + revm::primitives::EnvWithHandlerCfg, + traces::TraceKind, + utils::configure_tx_env, +}; +use foundry_cli::{ + opts::RpcOpts, + utils::{handle_traces, init_progress, TraceResult}, +}; +use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; +use foundry_compilers::artifacts::EvmVersion; +use foundry_evm::opts::EvmOpts; + +use clap::Parser; +use foundry_config::{find_project_root_path, Config}; +use foundry_tweak::{build_tweaked_backend, ClonedProject}; + +/// Replays an on-chain historical transaction locally on a fork of the blockchain with the on-chain +/// contract tweaked by the current cloned project. In other words, `forge replay`: +/// 1. Fork the blockchain, and replace the code of the on-chain contract associated with the +/// current cloned project (address defined in `.clone.meta`). +/// 2. Replays the historical transaction on the forked blockchain. +/// +/// NOTE: `forge replay` can only be used on a `forge clone`d project, which contains the metadata +/// of the on-chain instance of the contract. +#[derive(Clone, Debug, Parser, Default)] +pub struct ReplayArgs { + #[arg()] + transaction: String, + + /// Executes the transaction only with the state from the previous block. + /// Note that this also include transactions that are used for tweaking code. + /// + /// May result in different results than the live execution! + #[arg(long, short)] + quick: bool, + + /// Sets the number of assumed available compute units per second for this provider + /// + /// default value: 330 + /// + /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second + #[arg(long, alias = "cups", value_name = "CUPS")] + pub compute_units_per_second: Option, + + /// Disables rate limiting for this node's provider. + /// + /// default value: false + /// + /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second + #[arg(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")] + pub no_rate_limit: bool, + + /// Overrides the gas limit for the transaction. + #[arg( + long, + short, + value_name = "GAS", + help = "Override the gas limit for the transaction. If not set, two times of the gas limit from the transaction is used." + )] + pub gas: Option, + + /// Overrides the gas price (or base fee in EIP1559) used for the transaction + #[arg( + long, + value_name = "GAS_PRICE", + help = "Override the gas price for the transaction. If not set, the gas price from the transaction is used." + )] + pub gas_price: Option, + + /// The EVM version to use. + /// + /// Overrides the version specified in the config. + #[arg(long, short)] + evm_version: Option, + + #[command(flatten)] + pub rpc: RpcOpts, +} + +impl ReplayArgs { + /// Runs the `forge replay` command. + /// Logic is akin to the `cast run` command. + pub async fn run(mut self) -> Result<()> { + self.rpc.url = self.rpc.url.or(Some("http://localhost:8545".to_string())); + let root = find_project_root_path(None).unwrap(); + let root = dunce::canonicalize(root).expect("failed to convert to absolute path"); + let cloned_project = ClonedProject::load_with_root(&root) + .map_err(|e| eyre!("failed to load the cloned project: {}", e))?; + let tweaked_addr = cloned_project.metadata.address; + let tweaked_code = cloned_project.tweaked_code(&self.rpc, self.quick).await?; + + let figment = Config::figment_with_root(&root).merge(&self.rpc); + let evm_opts = figment.extract::()?; + let mut config = Config::try_from(figment)?.sanitized(); + config.evm_version = self.evm_version.unwrap_or_default(); + let compute_units_per_second = + if self.no_rate_limit { Some(u64::MAX) } else { self.compute_units_per_second }; + + let tx_hash: TxHash = self.transaction.parse().wrap_err("invalid transaction hash")?; + let r = self + .replay_tx_hash( + &config, + &evm_opts, + tx_hash, + &vec![(tweaked_addr, tweaked_code)].into_iter().collect(), + compute_units_per_second, + ) + .await?; + let console_logs = r.console_logs(); + // print call trace + let r = r.trace_result()?; + handle_traces(r, &config, config.chain, vec![], false).await?; + + // print logs if any + if !console_logs.is_empty() { + println!("Logs:"); + for log in console_logs { + println!(" {log}"); + } + println!(); + } + + Ok(()) + } + + async fn replay_tx_hash( + &self, + config: &Config, + evm_opts: &EvmOpts, + tx_hash: TxHash, + tweaks: &BTreeMap, + compute_units_per_second: Option, + ) -> Result { + let quick = self.quick; + let gas = self.gas; + let gas_price = self.gas_price; + + let mut config = config.clone(); + + // construct JSON-RPC provider + let provider = foundry_common::provider::ProviderBuilder::new( + &config.get_rpc_url_or_localhost_http()?, + ) + .compute_units_per_second_opt(compute_units_per_second) + .build()?; + + // get transactiond data + let mut tx = provider + .get_transaction_by_hash(tx_hash) + .await + .unwrap() + .ok_or(eyre::eyre!("no such transactikon"))?; + let tx_block_number: u64 = + tx.block_number.ok_or(eyre!("transaction may still be pending"))?; + // If the gas is not specified, we use 2 times of the gas limit from the transaction + tx.gas = gas.map(|x| x as u128).unwrap_or(tx.gas.saturating_add(tx.gas)); + // Set the gas price if specified + tx.gas_price = gas_price.map(|x| x.to()).or(tx.gas_price); + + // get preceeding transactions in the same block + let block = + provider.get_block(tx_block_number.into(), BlockTransactionsKind::Full).await.unwrap(); + let block = block.ok_or(eyre::eyre!("block not found"))?; + + config.fork_block_number = Some(tx_block_number - 1); // fork from the previous block + + // build the executor + let mut evm_opts = evm_opts.clone(); + evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); + evm_opts.fork_block_number = config.fork_block_number; + let env = evm_opts.evm_env().await?; + let fork = evm_opts.get_fork(&config, env.clone()); + let backend = build_tweaked_backend(fork, tweaks)?; + let mut executor = ExecutorBuilder::new() + .inspectors(|stack| stack.trace(true)) + .spec(config.evm_spec_id()) + .build(env, backend); + + // set the state to the moment right before the transaction + // we execute all transactions before the target transaction + let mut env = executor.env_with_handler_cfg().clone(); + adjust_block_env(&mut env, &block); + env.block.number = U256::from(tx_block_number); + let BlockTransactions::Full(txs) = block.transactions else { + return Err(eyre::eyre!("block transactions not found")); + }; + if !quick { + trace!("Executing transactions before the target transaction in the same block..."); + let txs = txs + .into_iter() + .take_while(|tx: &Transaction| tx.tx_hash() != tx_hash) + .collect::>(); + let pb = init_progress(txs.len() as u64, "replaying preceeding txs"); + pb.set_position(0); + for (index, tx) in txs.into_iter().enumerate() { + pb.set_position(index as u64 + 1); + + // System transactions such as on L2s don't contain any pricing info so + // we skip them otherwise this would cause + // reverts + if is_known_system_sender(tx.from) || + tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) + { + continue; + } + + if tx.hash == tx_hash { + // we reach the target transaction + break; + } + + // execute the transaction + trace!("Executing transaction: {:?}", tx.hash); + let _ = execute_tx(&mut executor, env.clone(), &tx)?; + } + } + + // execute the target transaction + trace!("Executing target transaction: {:?}", tx.hash); + let r = execute_tx(&mut executor, env, &tx)?; + Ok(r) + } +} + +fn adjust_block_env(env: &mut EnvWithHandlerCfg, block: &Block) { + env.block.timestamp = U256::from(block.header.timestamp); + env.block.coinbase = block.header.miner; + env.block.difficulty = block.header.difficulty; + env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); + env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); + env.block.gas_limit = U256::from(block.header.gas_limit); + if let Some(excess_blob_gas) = block.header.excess_blob_gas { + env.block.set_blob_excess_gas_and_price(excess_blob_gas as u64); + } +} + +enum ExecuteResult { + Call(RawCallResult), + Create(DeployResult), + Revert(EvmError), +} + +impl ExecuteResult { + pub fn trace_result(self) -> Result { + match self { + Self::Call(r) => Ok(TraceResult::from_raw(r, TraceKind::Execution)), + Self::Create(r) => Ok(TraceResult::from(r)), + Self::Revert(e) => TraceResult::try_from(Err(e)).map_err(|e| eyre!("revert: {}", e)), + } + } + + pub fn console_logs(&self) -> Vec { + let raw_logs = match self { + Self::Call(r) => &r.logs, + Self::Create(r) => &r.raw.logs, + Self::Revert(EvmError::Execution(e)) => &e.raw.logs, + _ => return vec![], + }; + decode_console_logs(raw_logs) + } +} + +fn execute_tx( + executor: &mut Executor, + mut env: EnvWithHandlerCfg, + tx: &Transaction, +) -> Result { + configure_tx_env(&mut env, tx); + // in case users overrides gas price below EIP1559 base fee, we disable base fee for the + // transaction + env.cfg.disable_block_gas_limit = true; + // in case users overrides gas price below the original gas price, we reset the gas priority fee + if env.tx.gas_price.lt(&env.tx.gas_priority_fee.unwrap_or_default()) { + env.tx.gas_priority_fee = Some(U256::ZERO); + } + // disable base fee when necessary + if env + .tx + .gas_price + .saturating_sub(env.tx.gas_priority_fee.unwrap_or_default()) + .lt(&env.block.basefee) + { + env.cfg.disable_base_fee = true; + } + if tx.to.is_some() { + let r = executor.transact_with_env(env.clone()).wrap_err_with(|| { + format!("Failed to execute transaction: {:?} in block {}", tx.hash, env.block.number) + })?; + Ok(ExecuteResult::Call(r)) + } else { + let r = executor.deploy_with_env(env, None); + match r { + Ok(r) => Ok(ExecuteResult::Create(r)), + Err(e) => Ok(ExecuteResult::Revert(e)), + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use alloy_primitives::{Address, Bytes, TxHash}; + use foundry_cli::utils::handle_traces; + use foundry_compilers::artifacts::EvmVersion; + use foundry_config::{figment::Figment, find_project_root_path, Config}; + use foundry_evm::opts::EvmOpts; + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_replay_tx_hash() { + let rpc = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string()); + let tx: TxHash = + "0xbfa440cd7df20320fe8400e4f61113379a018e3904eef7cf6085cf6cf22bcdb9".parse().unwrap(); + + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()); + let evm_opts = figment.extract::().unwrap(); + let mut config = Config::try_from(figment).unwrap().sanitized(); + config.eth_rpc_url = Some(rpc); + config.evm_version = EvmVersion::Shanghai; + + let args = + super::ReplayArgs { quick: false, gas: None, gas_price: None, ..Default::default() }; + let r = + args.replay_tx_hash(&config, &evm_opts, tx, &BTreeMap::default(), None).await.unwrap(); + let super::ExecuteResult::Call(result) = &r else { + panic!("expected ExecuteResult::Call"); + }; + assert!(!result.reverted); + assert_eq!(result.gas_used, 163_955); + let r = r.trace_result().unwrap(); + handle_traces(r, &config, config.chain, vec![], false).await.unwrap(); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_replay_tx_hash_with_tweak() { + let rpc = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string()); + let tx: TxHash = + "0xbfa440cd7df20320fe8400e4f61113379a018e3904eef7cf6085cf6cf22bcdb9".parse().unwrap(); + let factory: Address = "0x8d1fA935E5e8a5440c9Efc96C0d9fF387eBb179B".parse().unwrap(); + let tweaked_code: Bytes = "0xfe".parse().unwrap(); + + let mut config = Config::default(); + let figment: Figment = config.clone().into(); + let evm_opts = figment.extract::().unwrap(); + config.eth_rpc_url = Some(rpc); + + let args = + super::ReplayArgs { quick: false, gas: None, gas_price: None, ..Default::default() }; + + let r = args + .replay_tx_hash( + &config, + &evm_opts, + tx, + &BTreeMap::from([(factory, tweaked_code)]), + None, + ) + .await + .unwrap(); + let r = r.trace_result().unwrap(); + assert!(!r.success); + handle_traces(r, &config, config.chain, vec![], false).await.unwrap(); + } +} diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index aff2ad530d96..52a8cb47ebe3 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -65,6 +65,7 @@ fn main() -> Result<()> { ForgeSubcommand::Install(cmd) => cmd.run(), ForgeSubcommand::Remove(cmd) => cmd.run(), ForgeSubcommand::Remappings(cmd) => cmd.run(), + ForgeSubcommand::Replay(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::Init(cmd) => cmd.run(), ForgeSubcommand::Completions { shell } => { generate(shell, &mut Forge::command(), "forge", &mut std::io::stdout()); diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index a449bd75f46f..cf96e2d611fe 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -2,7 +2,7 @@ use crate::cmd::{ bind::BindArgs, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, config, coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, - selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update, + replay::ReplayArgs, selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update, }; use clap::{Parser, Subcommand, ValueHint}; use forge_script::ScriptArgs; @@ -79,6 +79,9 @@ pub enum ForgeSubcommand { #[command(visible_alias = "re")] Remappings(RemappingArgs), + #[command(visible_alias = "rp")] + Replay(ReplayArgs), + /// Verify smart contracts on Etherscan. #[command(visible_alias = "v")] VerifyContract(VerifyArgs), diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 3ef1f6b68bcf..becf3fccbb3e 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -23,6 +23,7 @@ foundry-debugger.workspace = true foundry-cheatcodes.workspace = true foundry-wallets.workspace = true foundry-linking.workspace = true +foundry-tweak.workspace = true serde.workspace = true eyre.workspace = true diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index bfa9de0d2bc1..e78e2f55109e 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -10,9 +10,12 @@ use alloy_json_abi::{Function, InternalType, JsonAbi}; use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider; use async_recursion::async_recursion; -use eyre::{OptionExt, Result}; +use eyre::{eyre, Context, OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; -use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; +use foundry_cli::{ + opts::RpcOpts, + utils::{ensure_clean_constructor, needs_setup}, +}; use foundry_common::{ fmt::{format_token, format_token_raw}, provider::get_http_provider, @@ -29,6 +32,7 @@ use foundry_evm::{ render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, }, }; +use foundry_tweak::{build_tweak_data, tweak_backend, TweakData}; use futures::future::join_all; use itertools::Itertools; use std::collections::{HashMap, HashSet}; @@ -70,6 +74,34 @@ impl LinkedState { ensure_clean_constructor(&target_contract.abi)?; + // prepare tweak + let tweak = if !args.tweak.is_empty() { + let cloned_projects = args + .tweak + .iter() + .map(|path| { + let path = dunce::canonicalize(path) + .map_err(|e| eyre!("Failed to load tweak project: {:?}", e))?; + foundry_tweak::ClonedProject::load_with_root(&path).wrap_err_with(|| { + format!("Failed to load tweak project from path: {:?}", &path) + }) + }) + .collect::, _>>()?; + + let rpc = RpcOpts { + url: script_config.evm_opts.fork_url.clone().or(args + .evm_opts + .fork_url + .clone() + .or(Some("http://localhost:8545".to_string()))), + ..Default::default() + }; + + build_tweak_data(&cloned_projects, &rpc, args.tweak_quick).await? + } else { + TweakData::new() + }; + Ok(PreExecutionState { args, script_config, @@ -81,6 +113,7 @@ impl LinkedState { abi: target_contract.abi.clone(), }, build_data, + tweak_data: tweak, }) } } @@ -93,6 +126,7 @@ pub struct PreExecutionState { pub script_wallets: ScriptWallets, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, + pub tweak_data: TweakData, } impl PreExecutionState { @@ -109,6 +143,7 @@ impl PreExecutionState { self.build_data.build_data.target.clone(), ) .await?; + tweak_backend(runner.executor.backend_mut(), &self.tweak_data)?; let result = self.execute_with_runner(&mut runner).await?; // If we have a new sender from execution, we need to use it to deploy libraries and relink diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index f03e15fa2988..9c2dd68d3f17 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -47,7 +47,7 @@ use foundry_evm::{ }; use foundry_wallets::MultiWalletOpts; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; use yansi::Paint; mod broadcast; @@ -178,6 +178,27 @@ pub struct ScriptArgs { )] pub with_gas_price: Option, + /// One `forge clone`d project that will be used to tweak the code of the corresponding + /// on-chain contract. + /// + /// This option can be used multiple times to tweak multiple contracts. + #[arg( + long, + value_name = "CLONED_PROJECT", + conflicts_with = "skip_simulation", + conflicts_with = "broadcast", + conflicts_with = "resume", + conflicts_with = "verify" + )] + pub tweak: Vec, + + /// When tweaking the contract, use the quick mode where we replay the creation transaction + /// only with the state from the previous block. + /// + /// May result in different results than the live execution! + #[arg(long, requires = "tweak")] + pub tweak_quick: bool, + #[command(flatten)] pub opts: CoreBuildArgs, diff --git a/crates/tweak/Cargo.toml b/crates/tweak/Cargo.toml new file mode 100644 index 000000000000..b1d5fe609167 --- /dev/null +++ b/crates/tweak/Cargo.toml @@ -0,0 +1,112 @@ +[package] +name = "foundry-tweak" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[build-dependencies] +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } + +[dependencies] +# lib +foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } +foundry-common.workspace = true +foundry-compilers = { workspace = true, features = ["full"] } +foundry-config.workspace = true +foundry-evm.workspace = true +# foundry-wallets.workspace = true +# foundry-linking.workspace = true + +# ethers-contract.workspace = true +# ethers-core.workspace = true +# ethers-middleware.workspace = true +# ethers-providers.workspace = true +# ethers-signers.workspace = true + +revm = { workspace = true, default-features = false, features = [ + "std", + "serde", + "memory_limit", + "optional_eip3607", + "optional_block_gas_limit", + "optional_no_base_fee", + "arbitrary", +] } +# revm-inspectors.workspace = true + +# comfy-table = "7" +eyre.workspace = true +# proptest = "1" +# rayon = "1" +serde.workspace = true +# tracing.workspace = true +yansi.workspace = true + +# # bin +# forge-doc.workspace = true +# forge-fmt.workspace = true +# forge-verify.workspace = true +# forge-script.workspace = true +foundry-cli.workspace = true +# foundry-debugger.workspace = true + +# alloy-dyn-abi.workspace = true +# alloy-json-abi.workspace = true +alloy-primitives = { workspace = true, features = ["serde"] } +alloy-provider.workspace = true +alloy-rpc-types.workspace = true + +# async-trait = "0.1" +# clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } +# clap_complete = "4" +# clap_complete_fig = "4" +# dialoguer = { version = "0.11", default-features = false } +# dunce = "1" +# futures = "0.3" +# hex.workspace = true +indicatif = "0.17" +# itertools.workspace = true +# once_cell = "1" +# parking_lot = "0.12" +# regex = { version = "1", default-features = false } +# reqwest = { version = "0.11", default-features = false, features = ["json"] } +# semver = "1" +serde_json.workspace = true +# similar = { version = "2", features = ["inline"] } +# solang-parser.workspace = true +# strum = { workspace = true, features = ["derive"] } +# thiserror = "1" +tokio = { version = "1", features = ["time"] } +toml = { version = "0.8", features = ["preserve_order"] } +# toml_edit = "0.21" +# watchexec = "2.3.2" +# evm-disassembler.workspace = true +# rustc-hash.workspace = true + +# # doc server +# axum = { workspace = true, features = ["ws"] } +# hyper.workspace = true +# tower-http = { workspace = true, features = ["fs"] } +# opener = "0.6" + +# [dev-dependencies] +# anvil.workspace = true +# foundry-test-utils.workspace = true + +# criterion = "0.5" +# globset = "0.4" +# paste = "1.0" +# path-slash = "0.2" +# pretty_assertions.workspace = true +# svm = { package = "svm-rs", version = "0.4", default-features = false, features = ["rustls"] } +tempfile = "3" +# tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } diff --git a/crates/tweak/README.md b/crates/tweak/README.md new file mode 100644 index 000000000000..e77ddeec963e --- /dev/null +++ b/crates/tweak/README.md @@ -0,0 +1,38 @@ +# foundry-tweak + +The functionalities to allow tweak the code of an on-chain contract. + +## Use Scenario + +1. Given an address of a contract on a blockchain (as well as a JSON-RPC endpoint), and a foundry project. +2. Find the creation transaction of that contract. +3. Resimulate the deployment of that contract with the original deployment arguments but with the code of the foundry project, and obtain the new deployed bytecode. +4. fork the blockchain and replace the code of the on-chain contract. +5. What is provided is a tweaked EVM runtime where the code of the contract is tweaked. +6. A lot of things can be done afterwards, including replay transaction or send a new transaction to invoke the tweaked contract. + +## Specification + +### Input + +- the local foundry project (and specify contract) +- contract address to tweak +- json-rpc to fork the chain +- etherscan API key (optional) + +### Pipeline + +0. Ensure that the local foundry project is compatible with the on-chain contract (no storage collision). +1. Fetch constructor arguments from etherscan +2. Fetch creation transaction from etherscan +3. Replay and inspect the creation transaction, and find the message call that CREATEs the contract. +4. Replay the creation transaction again and hijack the CREATE message call, replacing the call data with new creation bytecode and original arguments. + +### Output + +- the tweaked code of the contract + +### Downstream + +- Anvil may use this crate to tweak a forked blockchain. +- Cast may use this crate to simulate a transaction on a tweaked contract. \ No newline at end of file diff --git a/crates/tweak/build.rs b/crates/tweak/build.rs new file mode 100644 index 000000000000..c2f550fb6f82 --- /dev/null +++ b/crates/tweak/build.rs @@ -0,0 +1,3 @@ +fn main() { + vergen::EmitBuilder::builder().build_timestamp().git_sha(true).emit().unwrap(); +} diff --git a/crates/tweak/src/code.rs b/crates/tweak/src/code.rs new file mode 100644 index 000000000000..b9597e249384 --- /dev/null +++ b/crates/tweak/src/code.rs @@ -0,0 +1,585 @@ +//! Code generation for the contract we are going to tweak. +//! The contract should from a cloned project created by `forge clone` command. +//! The generation has to happen after the compatibility check. + +use alloy_primitives::{Address, Bytes, TxHash, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{ + serde_helpers::WithOtherFields, AnyReceiptEnvelope, BlockId, BlockTransactions, + BlockTransactionsKind, Log, Transaction, TransactionReceipt, +}; +use eyre::{eyre, Context, Result}; +use foundry_cli::{opts::RpcOpts, p_println, utils::init_progress}; +use foundry_common::{ + cli_warn, is_known_system_sender, provider::ProviderBuilder, SYSTEM_TRANSACTION_TYPE, +}; +use foundry_compilers::{artifacts::ConfigurableContractArtifact, Artifact}; +use foundry_config::{figment::Figment, Config, NamedChain}; +use foundry_evm::{ + backend::Backend, + executors::{EvmError, Executor, ExecutorBuilder}, + opts::EvmOpts, + utils::configure_tx_env, + InspectorExt, +}; +use revm::{ + interpreter::{CreateInputs, CreateOutcome, Interpreter, OpCode}, + primitives::{BlockEnv, EnvWithHandlerCfg, HashSet, SpecId}, + EvmContext, Inspector, +}; +use std::borrow::Cow; + +use crate::{constants::NonStandardPrecompiled, ClonedProject}; + +#[derive(Debug)] +struct TweakInspctor { + creation_count: u64, + creation_stack_depth: u64, + contract_address: Option
, + target_creation_tag: Option<(u64, u64)>, + tweaked_creation_code: Option, + tweaked_code: Option, + + // used to modify address(this) + to_update_address: bool, + created_address_in_tweak: Option
, + observed_created_addresses: HashSet
, +} + +type TransactionExt = WithOtherFields; +type TransactionReceiptExt = WithOtherFields>>; + +impl TweakInspctor { + pub fn new(contract_address: Option
, tweaked_creation_code: Option) -> Self { + Self { + creation_count: 0, + creation_stack_depth: 0, + contract_address, + target_creation_tag: None, + tweaked_creation_code, + tweaked_code: None, + + to_update_address: false, + created_address_in_tweak: None, + observed_created_addresses: HashSet::new(), + } + } + + pub fn prepare_for_pinpoint(&mut self) -> Result<()> { + self.creation_count = 0; + self.creation_stack_depth = 0; + + self.to_update_address = false; + self.created_address_in_tweak = None; + self.observed_created_addresses.clear(); + + eyre::ensure!(self.contract_address.is_some(), "the contract address is not found"); + + Ok(()) + } + + pub fn prepare_for_tweak(&mut self) -> Result<()> { + self.creation_count = 0; + self.creation_stack_depth = 0; + + self.to_update_address = false; + self.created_address_in_tweak = None; + self.observed_created_addresses.clear(); + + eyre::ensure!(self.contract_address.is_some(), "the contract address is not found"); + eyre::ensure!(self.target_creation_tag.is_some(), "the target creation count is not found"); + eyre::ensure!( + self.tweaked_creation_code.is_some(), + "the tweaked creation code is not found" + ); + + Ok(()) + } +} + +impl InspectorExt<&mut Backend> for TweakInspctor {} + +impl Inspector<&mut Backend> for TweakInspctor { + #[inline] + fn create( + &mut self, + _: &mut EvmContext<&mut Backend>, + inputs: &mut CreateInputs, + ) -> Option { + // first update the creation_count and creation_stack_depth + self.creation_count += 1; + self.creation_stack_depth += 1; + + // we then check creation count as the target one + if Some((self.creation_count, self.creation_stack_depth)) == self.target_creation_tag { + // we are going to tweak the creation code + if let Some(tweaked_creation_code) = &self.tweaked_creation_code { + inputs.init_code = tweaked_creation_code.clone(); + } + } + + None + } + + #[inline] + fn create_end( + &mut self, + context: &mut EvmContext<&mut Backend>, + _: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + // we shall first distinguish the replay stage + if let Some((target_count, target_stack_depth)) = self.target_creation_tag { + // record the tweaked contract address + if self.creation_count == target_count && + self.creation_stack_depth == target_stack_depth + { + if let Some(address) = outcome.address { + if let Ok((code, _)) = context.code(address) { + self.tweaked_code = Some(code.clone()); + } + } + } + } else { + // we are here to find the target creation count + if outcome.address == self.contract_address { + self.target_creation_tag = Some((self.creation_count, self.creation_stack_depth)); + } + } + + // we only decresae the creation_stack_depth + self.creation_stack_depth -= 1; + outcome + } + + #[inline] + fn step(&mut self, interp: &mut Interpreter, _: &mut EvmContext<&mut Backend>) { + if Some((self.creation_count, self.creation_stack_depth)) == self.target_creation_tag { + if self.created_address_in_tweak.is_none() { + // When the first time the creation_count and creation_stack_depth match, + // we know that we are in the CREATE call that creates the target contract. + // We record the address of the created contract, so that we can replace it + // with the original address of contract being tweaked. + self.created_address_in_tweak.replace(interp.contract().target_address); + } + // the `step_end` hook should replace the output of ADDRESS code only if the + // current execution is in the context of the target contract + self.to_update_address = interp.current_opcode() == OpCode::ADDRESS.get() && + self.created_address_in_tweak.unwrap() == interp.contract().target_address; + } + } + + #[inline] + fn step_end(&mut self, interp: &mut Interpreter, _: &mut EvmContext<&mut Backend>) { + if Some((self.creation_count, self.creation_stack_depth)) == self.target_creation_tag && + self.to_update_address + { + // we hook into the ADDRESS opcode to update the address + // note that we only update the address for the target contract + if let Some(original_address) = self.contract_address { + let stack = &mut interp.stack; + self.observed_created_addresses + .insert(Address::from_word(stack.pop().unwrap().into())); + stack.push(original_address.into_word().into()).unwrap(); + } + } + } +} + +pub async fn generate_tweaked_code( + rpc: &RpcOpts, + project: &ClonedProject, + quick: bool, +) -> Result { + println!("Tweaking the contract at {}...", project.metadata.address); + p_println!(!quick => "It may take time if the RPC has rate limits."); + // prepare the deployment bytecode (w/ parameters) + let artifact = project.main_artifact()?; + let tweaked_creation_code = prepare_tweaked_creation_code(project, &artifact)?; + + // let's tweak! + tweak(rpc, project, tweaked_creation_code, quick).await +} + +// tweak the contract creation code +async fn tweak( + rpc: &RpcOpts, + project: &ClonedProject, + tweaked_creation_code: Bytes, + quick: bool, +) -> Result { + // prepare the execution backend + let (mut db, mut env) = prepare_backend(rpc, project, quick).await?; + + // let hook into the creation process + let mut inspector = + TweakInspctor::new(Some(project.metadata.address), Some(tweaked_creation_code)); + + // round 1: pinpoint the target creation count + inspector.prepare_for_pinpoint()?; + let rv = db.inspect(&mut env.clone(), &mut inspector)?; + eyre::ensure!( + rv.result.is_success(), + "failed to pinpoint the target creation: {:?}", + rv.result + ); + eyre::ensure!( + inspector.creation_stack_depth == 0, + "unexpected creation count: {}", + inspector.creation_stack_depth + ); + + // round 2: tweak the creation code + inspector.prepare_for_tweak()?; + // disable gas_limit and decrease gas_fee for the inspector + env.cfg.disable_block_gas_limit = true; + env.cfg.disable_base_fee = true; + // increase gas_limit and decrease gas_price for the transaction + env.tx.gas_limit = env.tx.gas_limit.checked_mul(2).ok_or(eyre!("gas limit overflow"))?; + env.tx.gas_price = + env.tx.gas_price.checked_div(U256::from(2)).ok_or(eyre!("divided by zero"))?; + env.tx.gas_priority_fee = Some(U256::ZERO); + // we do not care about the execution result in this round + db.inspect(&mut env, &mut inspector)?; + eyre::ensure!( + inspector.observed_created_addresses.len() <= 1, + "hooked ADDRESS opcodes return different addresses" + ); + + inspector.tweaked_code.ok_or(eyre!("the tweaked code is not generated")) +} + +fn prepare_tweaked_creation_code( + project: &ClonedProject, + artifact: &ConfigurableContractArtifact, +) -> Result { + let bytecode = artifact.get_bytecode().ok_or(eyre!("the contract does not have bytecode"))?; + let deployment_bytecode = + bytecode.bytes().ok_or(eyre!("the bytecode transformation failed"))?; + let constructor_arguments = &project.metadata.constructor_arguments; + + // concate the deployment bytecode with the constructor arguments + let mut tweaked_creation_code = deployment_bytecode.to_vec(); + tweaked_creation_code.extend_from_slice(&constructor_arguments[..]); + + Ok(Bytes::from(tweaked_creation_code)) +} + +async fn prepare_backend( + rpc: &RpcOpts, + project: &ClonedProject, + quick: bool, +) -> Result<(Backend, EnvWithHandlerCfg)> { + // get rpc_url + let rpc_url = + &rpc.url(Some(&project.config))?.unwrap_or(Cow::Borrowed("http://localhost:8545")); + + // prepare the RPC provider + let provider = ProviderBuilder::new(rpc_url) + .chain( + NamedChain::try_from(project.metadata.chain_id) + .map_err(|_| eyre!("invalid chain id"))?, + ) + .build()?; + + // get block number + let tx_receipt = provider + .get_transaction_receipt(project.metadata.creation_transaction) + .await? + .ok_or(eyre!("the transaction is not mined"))?; + let block_number = tx_receipt.block_number.ok_or(eyre!("the transaction is not mined"))?; + + // then, we are going to replay all transactions before the creation transaction + let block = provider + .get_block(BlockId::Number(block_number.into()), BlockTransactionsKind::Full) + .await? + .ok_or(eyre!("block not found"))?; + + // prepare the block env + let mut block_env = BlockEnv { + number: U256::from( + block.header.number.expect("block number is not found. Maybe it is not mined yet?"), + ), + timestamp: U256::from(block.header.timestamp), + coinbase: block.header.miner, + difficulty: block.header.difficulty, + prevrandao: Some(block.header.mix_hash.unwrap_or_default()), + basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), + gas_limit: U256::from(block.header.gas_limit), + blob_excess_gas_and_price: None, + }; + if let Some(excess_blob_gas) = block.header.excess_blob_gas { + block_env.set_blob_excess_gas_and_price(excess_blob_gas as u64); + } + + // get all transactions with receipts + let txs_with_receipt = if !quick { + let BlockTransactions::Full(txs) = block.transactions else { + return Err(eyre::eyre!("block transactions not found")); + }; + let Some(recipts) = provider.get_block_receipts(block_number.into()).await? else { + return Err(eyre::eyre!("block receipts not found")); + }; + txs.into_iter() + .zip(recipts.into_iter()) + .map(|(a, b)| (WithOtherFields::new(a), b)) + .collect() + } else { + vec![( + provider + .get_transaction_by_hash(project.metadata.creation_transaction) + .await? + .ok_or(eyre!("the transaction is not mined"))?, + tx_receipt, + )] + }; + + // get the figment from the cloned project's config + let mut config = project.config.clone(); + config.fork_block_number = Some(block_number - 1); + let figment: Figment = config.into(); + let figment = figment.merge(rpc); + + // set evm options + let mut evm_opts = figment.extract::()?; + evm_opts.fork_url = Some(rpc_url.to_string()); + evm_opts.fork_block_number = Some(block_number - 1); + + // get an updated config + let config = Config::try_from(figment)?.sanitized(); + + // get env + let env = evm_opts.evm_env().await?; + + // a loop to probe the proper EVM version + let mut spec_id = config.evm_spec_id(); + let chain_id = + NamedChain::try_from(project.metadata.chain_id).map_err(|_| eyre!("invalid chain id"))?; + + if chain_id == NamedChain::BinanceSmartChain || chain_id == NamedChain::BinanceSmartChainTestnet + { + cli_warn!("Tweaking contracts on BinanceSmartChain (BSC) may not work due to its non-standard gas consumption. Please see more at https://github.com/foundry-rs/foundry/issues/4784#issuecomment-1520402694"); + } + + if !quick { + loop { + // get backend and executor + let db = Backend::spawn(evm_opts.get_fork(&config, env.clone())); + // create the executor and the corresponding env + let executor = ExecutorBuilder::new().spec(spec_id).build(env.clone(), db); + + match probe_evm_version(chain_id, executor, &block_env, &txs_with_receipt, None) { + Ok(_) => { + break; + } + Err(_) => { + spec_id = SpecId::try_from_u8((spec_id as u8) + 1) + .ok_or(eyre!("failed to probe a proper EVM version. You may want to use --quick option to skip EVM version probing."))?; + } + } + } + } + + let db = Backend::spawn(evm_opts.get_fork(&config, env.clone())); + let executor = ExecutorBuilder::new().spec(spec_id).build(env.clone(), db); + probe_evm_version( + chain_id, + executor, + &block_env, + &txs_with_receipt, + Some(project.metadata.creation_transaction), + ) +} + +fn probe_evm_version( + chain_id: NamedChain, + mut executor: Executor, + block_env: &BlockEnv, + txs: &[(TransactionExt, TransactionReceiptExt)], + target_tx: Option, +) -> Result<(Backend, EnvWithHandlerCfg)> { + let mut env = executor.env_with_handler_cfg().clone(); + + env.block = block_env.clone(); + + let non_standard_precompiled = NonStandardPrecompiled::get_precomiled_address(chain_id); + + let pb = + init_progress(txs.len() as u64, format!("investigating {:?}", executor.spec_id()).as_str()); + pb.set_position(0); + + for (index, (tx, receipt)) in txs.iter().enumerate() { + pb.set_position(index as u64 + 1); + + // skip system transactions + if is_known_system_sender(tx.from) || tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { + continue; + } + + // skip non-standard precompiled contracts + if let Some(precompiled) = non_standard_precompiled.as_ref() { + if precompiled.contains(&tx.to.unwrap_or_default()) { + continue; + } + } + + // always configure the transaction environment + configure_tx_env(&mut env, tx); + + // find the creation transaction + if Some(tx.hash) == target_tx { + return Ok((executor.backend().clone(), env)); + } + + // get the actual gas used + let real_gas_used = receipt.gas_used as u64; + + if tx.to.is_some() { + let rv = executor.transact_with_env(env.clone()); + + let rv = rv.wrap_err("Executing transaction fails")?; + + // check gas used + eyre::ensure!( + rv.gas_used == real_gas_used, + "Gas used mismatch: expected {}, got {} ({:?})", + real_gas_used, + rv.gas_used, + tx.hash + ); + + // check transaction status + if (**receipt).inner.inner.receipt.status.coerce_status() { + eyre::ensure!(rv.exit_reason.is_ok(), "Transaction should succeed ({:?})", tx.hash); + } else { + eyre::ensure!(!rv.exit_reason.is_ok(), "Transaction should fail ({:?})", tx.hash); + } + } else { + match executor.deploy_with_env(env.clone(), None) { + // Reverted transactions should be skipped + Err(EvmError::Execution(error)) => { + eyre::ensure!( + error.gas_used == real_gas_used, + "Gas used mismatch: expected {}, got {} ({:?})", + real_gas_used, + error.gas_used, + tx.hash + ); + eyre::ensure!( + !(**receipt).inner.inner.receipt.status.coerce_status(), + "Transaction should fail ({:?})", + tx.hash + ); + } + Err(error) => { + return Err(error).wrap_err_with(|| { + format!( + "Failed to deploy transaction: {:?} in block {}", + tx.hash, env.block.number + ) + }) + } + Ok(rv) => { + eyre::ensure!( + rv.gas_used == real_gas_used, + "Gas used mismatch: expected {}, got {} ({:?})", + real_gas_used, + rv.gas_used, + tx.hash + ); + eyre::ensure!( + (**receipt).inner.inner.receipt.status.coerce_status(), + "Transaction should succeed ({:?})", + tx.hash + ); + } + } + } + } + + if target_tx.is_none() { + Ok((executor.backend().clone(), env)) + } else { + Err(eyre!("the target transaction is not found")) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use crate::{code::tweak, metadata::CloneMetadata, ClonedProject}; + + use alloy_primitives::Bytes; + use foundry_cli::opts::RpcOpts; + use foundry_compilers::artifacts::EvmVersion; + use foundry_config::Config; + + fn get_fake_project(address: &str, tx: &str) -> ClonedProject { + let fake_root = tempfile::tempdir().unwrap().path().to_path_buf(); + + ClonedProject { + root: fake_root, + config: Config { evm_version: EvmVersion::Paris, ..Default::default() }, + metadata: CloneMetadata { + path: "src/FakeContract.sol".into(), + target_contract: "FakeContract".into(), + chain_id: 1, + address: address.parse().unwrap(), + creation_transaction: tx.parse().unwrap(), + deployer: Default::default(), + constructor_arguments: Default::default(), + storage_layout: Default::default(), + }, + ..Default::default() + } + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_direct_deployment() { + let fake_project = get_fake_project( + "0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545", + "0x79820495643caf5a1e7e96578361c9ddba0e0735cd684ada7450254f6fd58f51", + ); + let rpc_url = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string()); + let rpc = RpcOpts { url: Some(rpc_url), ..Default::default() }; + + let tweaked_code = format!( + "{:?}", + tweak(&rpc, &fake_project, Bytes::from_str(FAKE_CREATION_CODE).unwrap(), false) + .await + .unwrap() + ); + + assert!( + tweaked_code.starts_with(FAKE_DEPLOYED_CODE), + "the created code is not the same as the expected one" + ); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_factory_deployment() { + let fake_project = get_fake_project( + "0x6B880d3B1FA2475C30Dc583726c56B4aFc66bD0b", + "0x3812e48763d3631516206fb878007ed126223d5c31e8cc433c79659b8afbbf24", + ); + let rpc_url = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string()); + let rpc = RpcOpts { url: Some(rpc_url), ..Default::default() }; + + let tweaked_code = format!( + "{:?}", + tweak(&rpc, &fake_project, Bytes::from_str(FAKE_CREATION_CODE).unwrap(), false) + .await + .unwrap() + ); + + assert!( + tweaked_code.starts_with(FAKE_DEPLOYED_CODE), + "the created code is not the same as the expected one" + ); + } + + const FAKE_DEPLOYED_CODE: &str = "0x60806040526004361061032d5760003560e01c80639157921c116101a5578063b2975794116100ec578063d547741f11610095578063dafae4081161006f578063dafae4081461096e578063dff525e11461098e578063e400327c146109ae578063e75235b8146109ce5761033c565b8063d547741f14610901578063d55ed10314610921578063d64af2a61461094e5761033c565b8063cdb67444116100c6578063cdb674441461089c578063cdf64a76146108b4578063d19773d2146108d45761033c565b8063b29757941461082f578063b9c362091461085c578063ca15c8731461087c5761033c565b8063a3912ec81161014e578063affed0e011610128578063affed0e0146107cc578063b1a2567e146107e2578063b1d08a03146108025761033c565b8063a3912ec81461033a578063ab7965661461077f578063ac78dfe8146107ac5761033c565b8063994390891161017f57806399439089146107155780639dcc4da314610735578063a217fddf1461076a5761033c565b80639157921c1461068f57806391d14854146106af57806393c5678f146106f55761033c565b806336568abe116102745780635c975abb1161021d5780637de5dedd116101f75780637de5dedd146106115780638456cb59146106265780638f34e3471461063b5780639010d07c1461066f5761033c565b80635c975abb146105ac5780636932be98146105c45780636c1ce670146105f15761033c565b80634d0d66731161024e5780634d0d66731461052f5780634d493f4e1461054f57806359122f6b1461057f5761033c565b806336568abe146104e75780633f4ba83a146105075780634b14557e1461051c5761033c565b80631d4a7210116102d65780632f2ff15d116102b05780632f2ff15d1461049b578063302d12db146104bb5780633644e515146104d25761033c565b80631d4a721014610428578063248a9ca3146104555780632dfdf0b5146104855761033c565b8063180ff1e911610307578063180ff1e9146103d55780631a8e55b0146103e85780631b6e7594146104085761033c565b806301ffc9a71461034457806317ce2dd41461037957806317fcb39b1461039d5761033c565b3661033c5761033a6109e6565b005b61033a6109e6565b34801561035057600080fd5b5061036461035f366004614843565b610a69565b60405190151581526020015b60405180910390f35b34801561038557600080fd5b5061038f60755481565b604051908152602001610370565b3480156103a957600080fd5b506074546103bd906001600160a01b031681565b6040516001600160a01b039091168152602001610370565b61033a6103e33660046148f4565b610aad565b3480156103f457600080fd5b5061033a6104033660046149e6565b610dbd565b34801561041457600080fd5b5061033a610423366004614a52565b610e8f565b34801561043457600080fd5b5061038f610443366004614aec565b603e6020526000908152604090205481565b34801561046157600080fd5b5061038f610470366004614b09565b60009081526072602052604090206001015490565b34801561049157600080fd5b5061038f60765481565b3480156104a757600080fd5b5061033a6104b6366004614b22565b610f64565b3480156104c757600080fd5b5061038f620f424081565b3480156104de57600080fd5b5060775461038f565b3480156104f357600080fd5b5061033a610502366004614b22565b610f8f565b34801561051357600080fd5b5061033a61101b565b61033a61052a366004614b52565b611083565b34801561053b57600080fd5b5061036461054a366004614b7d565b6110e1565b34801561055b57600080fd5b5061036461056a366004614b09565b607a6020526000908152604090205460ff1681565b34801561058b57600080fd5b5061038f61059a366004614aec565b603a6020526000908152604090205481565b3480156105b857600080fd5b5060005460ff16610364565b3480156105d057600080fd5b5061038f6105df366004614b09565b60796020526000908152604090205481565b3480156105fd57600080fd5b5061036461060c366004614c06565b61118c565b34801561061d57600080fd5b5061038f61119f565b34801561063257600080fd5b5061033a611234565b34801561064757600080fd5b5061038f7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e481565b34801561067b57600080fd5b506103bd61068a366004614c32565b61129c565b34801561069b57600080fd5b5061033a6106aa366004614c54565b6112b4565b3480156106bb57600080fd5b506103646106ca366004614b22565b60009182526072602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561070157600080fd5b5061033a6107103660046149e6565b6115ca565b34801561072157600080fd5b506003546103bd906001600160a01b031681565b34801561074157600080fd5b50610755610750366004614c32565b611696565b60408051928352602083019190915201610370565b34801561077657600080fd5b5061038f600081565b34801561078b57600080fd5b5061038f61079a366004614aec565b603c6020526000908152604090205481565b3480156107b857600080fd5b506103646107c7366004614b09565b61172f565b3480156107d857600080fd5b5061038f60045481565b3480156107ee57600080fd5b5061033a6107fd3660046149e6565b6117ce565b34801561080e57600080fd5b5061038f61081d366004614aec565b60396020526000908152604090205481565b34801561083b57600080fd5b5061084f61084a366004614aec565b61189a565b6040516103709190614ca5565b34801561086857600080fd5b50610755610877366004614c32565b611992565b34801561088857600080fd5b5061038f610897366004614b09565b611a17565b3480156108a857600080fd5b50603754603854610755565b3480156108c057600080fd5b5061033a6108cf366004614aec565b611a2e565b3480156108e057600080fd5b5061038f6108ef366004614aec565b603b6020526000908152604090205481565b34801561090d57600080fd5b5061033a61091c366004614b22565b611a97565b34801561092d57600080fd5b5061038f61093c366004614aec565b603d6020526000908152604090205481565b34801561095a57600080fd5b5061033a610969366004614aec565b611abd565b34801561097a57600080fd5b50610364610989366004614b09565b611b26565b34801561099a57600080fd5b5061033a6109a9366004614cd2565b611bbd565b3480156109ba57600080fd5b5061033a6109c93660046149e6565b611cc7565b3480156109da57600080fd5b50600154600254610755565b60005460ff1615610a315760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b6074546001600160a01b03163314610a6757610a4b614802565b338152604080820151349101528051610a65908290611d93565b505b565b60006001600160e01b031982167f5a05180f000000000000000000000000000000000000000000000000000000001480610aa75750610aa78261210a565b92915050565b607154610100900460ff16610ac85760715460ff1615610acc565b303b155b610b3e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a28565b607154610100900460ff16158015610b60576071805461ffff19166101011790555b610b6b60008d612171565b6075899055610b798b61217b565b610b828a6121dd565b610c29604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f159f52c1e3a2b6a6aad3950adf713516211484e0516dad685ea662a094b7c43b918101919091527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260c00160408051601f198184030181529190528051602090910120607755565b610c338887612238565b5050610c3f87876122f8565b5050610c496123d3565b6000610c558680614da6565b90501115610d1657610c7e610c6a8680614da6565b610c776020890189614da6565b8787612467565b610ca4610c8b8680614da6565b8660005b602002810190610c9f9190614da6565b612666565b610cca610cb18680614da6565b8660015b602002810190610cc59190614da6565b612779565b610cf0610cd78680614da6565b8660025b602002810190610ceb9190614da6565b61288c565b610d16610cfd8680614da6565b8660035b602002810190610d119190614da6565b612a30565b60005b610d266040870187614da6565b9050811015610d9c57610d8a7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e4610d606040890189614da6565b84818110610d7057610d70614d90565b9050602002016020810190610d859190614aec565b612b43565b80610d9481614e06565b915050610d19565b508015610daf576071805461ff00191690555b505050505050505050505050565b6000805160206157b9833981519152546001600160a01b03163314610e1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82610e7d5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612779565b50505050565b6000805160206157b9833981519152546001600160a01b03163314610eef5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b84610f4e5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b610f5c868686868686612467565b505050505050565b600082815260726020526040902060010154610f808133612b65565b610f8a8383612b43565b505050565b6001600160a01b038116331461100d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610a28565b6110178282612be5565b5050565b6000805160206157b9833981519152546001600160a01b0316331461107b5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a67612c07565b60005460ff16156110c95760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b610a656110db36839003830183614ec0565b33611d93565b6000805460ff16156111285760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b611184848484808060200260200160405190810160405280939291908181526020016000905b8282101561117a5761116b60608302860136819003810190614f13565b8152602001906001019061114e565b5050505050612ca3565b949350505050565b600061119883836133bc565b9392505050565b600061122f600360009054906101000a90046001600160a01b03166001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b1580156111f257600080fd5b505afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a9190614f89565b613480565b905090565b6000805160206157b9833981519152546001600160a01b031633146112945760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a676134b6565b60008281526073602052604081206111989083613531565b7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e46112df8133612b65565b60006112f86112f336859003850185614ff0565b61353d565b905061130c6112f336859003850185614ff0565b8335600090815260796020526040902054146113765760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b82356000908152607a602052604090205460ff166113fc5760405162461bcd60e51b815260206004820152603160248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220617060448201527f70726f766564207769746864726177616c0000000000000000000000000000006064820152608401610a28565b82356000908152607a602052604090819020805460ff19169055517fd639511b37b3b002cca6cfe6bca0d833945a5af5a045578a0627fc43b79b26309061144690839086906150c4565b60405180910390a160006114606080850160608601614aec565b9050600061147661012086016101008701615151565b600181111561148757611487614c71565b141561154f5760006114a2368690038601610100870161516e565b6001600160a01b0383166000908152603b60205260409020549091506114ce90610140870135906135c6565b604082015260006114e8368790038701610100880161516e565b60408301519091506114ff9061014088013561518a565b604082015260745461151f908390339086906001600160a01b03166135e0565b6115486115326060880160408901614aec565b60745483919086906001600160a01b03166135e0565b505061158b565b61158b6115626060860160408701614aec565b60745483906001600160a01b03166115833689900389016101008a0161516e565b9291906135e0565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d82856040516115bc9291906150c4565b60405180910390a150505050565b6000805160206157b9833981519152546001600160a01b0316331461162a5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261168a5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612666565b6000806116b86000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146117115760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b84846122f8565b90925090506117286123d3565b9250929050565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b15801561177457600080fd5b505afa158015611788573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ac9190614f89565b6037546117b991906151a1565b6038546117c690846151a1565b101592915050565b6000805160206157b9833981519152546001600160a01b0316331461182e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261188e5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e898484848461288c565b60408051808201909152600080825260208201526001600160a01b0382166000908152607860205260409081902081518083019092528054829060ff1660018111156118e8576118e8614c71565b60018111156118f9576118f9614c71565b815290546001600160a01b036101009091048116602092830152908201519192501661198d5760405162461bcd60e51b815260206004820152602560248201527f4d61696e636861696e4761746577617956323a20756e737570706f727465642060448201527f746f6b656e0000000000000000000000000000000000000000000000000000006064820152608401610a28565b919050565b6000806119b46000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b031614611a0d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b8484612238565b6000818152607360205260408120610aa790613a13565b6000805160206157b9833981519152546001600160a01b03163314611a8e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a65816121dd565b600082815260726020526040902060010154611ab38133612b65565b610f8a8383612be5565b6000805160206157b9833981519152546001600160a01b03163314611b1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a658161217b565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b158015611b6b57600080fd5b505afa158015611b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba39190614f89565b600154611bb091906151a1565b6002546117c690846151a1565b6000805160206157b9833981519152546001600160a01b03163314611c1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b85611c7c5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b611c8a878787878787612467565b611c978787836000610c8f565b611ca48787836001610cb5565b611cb18787836002610cdb565b611cbe8787836003610d01565b50505050505050565b6000805160206157b9833981519152546001600160a01b03163314611d275760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82611d875760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612a30565b604080518082018252600080825260208201526074549184015190916001600160a01b031690611dc290613a1d565b60208401516001600160a01b0316611ee1573484604001516040015114611e375760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611e408161189a565b6040850151519092506001811115611e5a57611e5a614c71565b82516001811115611e6d57611e6d614c71565b14611ecd5760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b6001600160a01b0381166020850152612087565b3415611f3b5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611f48846020015161189a565b6040850151519092506001811115611f6257611f62614c71565b82516001811115611f7557611f75614c71565b14611fd55760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b60208401516040850151611fec9185903090613ac7565b83602001516001600160a01b0316816001600160a01b031614156120875760408481015181015190517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b15801561206e57600080fd5b505af1158015612082573d6000803e3d6000fd5b505050505b607680546000918261209883614e06565b91905055905060006120bf858386602001516075548a613ce190949392919063ffffffff16565b90507fd7b25068d9dc8d00765254cfb7f5070f98d263c8d68931d937c7362fa738048b6120eb8261353d565b826040516120fa9291906151c0565b60405180910390a1505050505050565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610aa757507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610aa7565b6110178282612b43565b6074805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f9d2334c23be647e994f27a72c5eee42a43d5bdcfe15bb88e939103c2b114cbaf906020015b60405180910390a150565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fef40dc07567635f84f5edbd2f8dbc16b40d9d282dd8e7e6f4ff58236b6836169906020016121d2565b6000808284111561228b5760405162461bcd60e51b815260206004820152601c60248201527f4761746577617956323a20696e76616c6964207468726573686f6c64000000006044820152606401610a28565b505060018054600280549285905583905560048054919291849186919060006122b383614e06565b9091555060408051868152602081018690527f976f8a9c5bdf8248dec172376d6e2b80a8e3df2f0328e381c6db8e1cf138c0f891015b60405180910390a49250929050565b600080828411156123715760405162461bcd60e51b815260206004820152602760248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64000000000000000000000000000000000000000000000000006064820152608401610a28565b5050603780546038805492859055839055600480549192918491869190600061239983614e06565b9091555060408051868152602081018690527f31312c97b89cc751b832d98fd459b967a2c3eef3b49757d1cf5ebaa12bb6eee191016122e9565b6002546037546123e391906151a1565b6038546001546123f391906151a1565b1115610a675760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64730000000000000000000000000000000000000000000000006064820152608401610a28565b848314801561247557508481145b6124e75760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206172726160448201527f79206c656e6774680000000000000000000000000000000000000000000000006064820152608401610a28565b60005b8581101561262c5784848281811061250457612504614d90565b90506020020160208101906125199190614aec565b6078600089898581811061252f5761252f614d90565b90506020020160208101906125449190614aec565b6001600160a01b039081168252602082019290925260400160002080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010093909216929092021790558282828181106125a3576125a3614d90565b90506020020160208101906125b89190615151565b607860008989858181106125ce576125ce614d90565b90506020020160208101906125e39190614aec565b6001600160a01b031681526020810191909152604001600020805460ff19166001838181111561261557612615614c71565b02179055508061262481614e06565b9150506124ea565b507fa4f03cc9c0e0aeb5b71b4ec800702753f65748c2cf3064695ba8e8b46be704448686868686866040516120fa969594939291906152c1565b8281146126c85760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612743578282828181106126e5576126e5614d90565b905060200201356039600087878581811061270257612702614d90565b90506020020160208101906127179190614aec565b6001600160a01b031681526020810191909152604001600020558061273b81614e06565b9150506126cb565b507f80bc635c452ae67f12f9b6f12ad4daa6dbbc04eeb9ebb87d354ce10c0e210dc0848484846040516115bc9493929190615339565b8281146127db5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612856578282828181106127f8576127f8614d90565b90506020020135603a600087878581811061281557612815614d90565b905060200201602081019061282a9190614aec565b6001600160a01b031681526020810191909152604001600020558061284e81614e06565b9150506127de565b507f64557254143204d91ba2d95acb9fda1e5fea55f77efd028685765bc1e94dd4b5848484846040516115bc9493929190615339565b8281146128ee5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b838110156129fa57620f424083838381811061290f5761290f614d90565b90506020020135111561298a5760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420706560448201527f7263656e746167650000000000000000000000000000000000000000000000006064820152608401610a28565b82828281811061299c5761299c614d90565b90506020020135603b60008787858181106129b9576129b9614d90565b90506020020160208101906129ce9190614aec565b6001600160a01b03168152602081019190915260400160002055806129f281614e06565b9150506128f1565b507fb05f5de88ae0294ebb6f67c5af2fcbbd593cc6bdfe543e2869794a4c8ce3ea50848484846040516115bc9493929190615339565b828114612a925760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612b0d57828282818110612aaf57612aaf614d90565b90506020020135603c6000878785818110612acc57612acc614d90565b9050602002016020810190612ae19190614aec565b6001600160a01b0316815260208101919091526040016000205580612b0581614e06565b915050612a95565b507fb5d2963614d72181b4df1f993d45b83edf42fa19710f0204217ba1b3e183bb73848484846040516115bc9493929190615339565b612b4d8282613db6565b6000828152607360205260409020610f8a9082613e58565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff1661101757612ba3816001600160a01b03166014613e6d565b612bae836020613e6d565b604051602001612bbf9291906153d0565b60408051601f198184030181529082905262461bcd60e51b8252610a2891600401615451565b612bef828261404e565b6000828152607360205260409020610f8a90826140d1565b60005460ff16612c595760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610a28565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000823561014084013582612cbe6080870160608801614aec565b9050612cdb612cd6368890038801610100890161516e565b613a1d565b6001612ced6040880160208901615151565b6001811115612cfe57612cfe614c71565b14612d715760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964207265636560448201527f697074206b696e640000000000000000000000000000000000000000000000006064820152608401610a28565b60808601354614612de95760405162461bcd60e51b8152602060048201526024808201527f4d61696e636861696e4761746577617956323a20696e76616c6964206368616960448201527f6e206964000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6000612dfe61084a6080890160608a01614aec565b9050612e1261012088016101008901615151565b6001811115612e2357612e23614c71565b81516001811115612e3657612e36614c71565b148015612e675750612e4e60e0880160c08901614aec565b6001600160a01b031681602001516001600160a01b0316145b612ebf5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b60008481526079602052604090205415612f415760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220707260448201527f6f636573736564207769746864726177616c00000000000000000000000000006064820152608401610a28565b6001612f5561012089016101008a01615151565b6001811115612f6657612f66614c71565b1480612f795750612f7782846133bc565b155b612feb5760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a2072656163686564206461696c60448201527f79207769746864726177616c206c696d697400000000000000000000000000006064820152608401610a28565b6000612fff6112f3368a90038a018a614ff0565b9050600061300f607754836140e6565b6003549091506001600160a01b0316600061303d6130356101208d016101008e01615151565b878985614142565b60408051606081018252600080825260208201819052918101829052919b50919250819081906000805b8f5181101561323c578f818151811061308257613082614d90565b6020908102919091018101518051818301516040808401518151600081529586018083528f905260ff9093169085015260608401526080830152935060019060a0016020604051602081039080840390855afa1580156130e6573d6000803e3d6000fd5b505050602060405103519450846001600160a01b0316846001600160a01b0316106131795760405162461bcd60e51b815260206004820152602160248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206f72646560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6040517f953865650000000000000000000000000000000000000000000000000000000081526001600160a01b03808716600483015286955089169063953865659060240160206040518083038186803b1580156131d657600080fd5b505afa1580156131ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320e9190614f89565b6132189083615484565b915086821061322a576001955061323c565b8061323481614e06565b915050613067565b50846132b05760405162461bcd60e51b815260206004820152603660248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220696e60448201527f73756666696369656e7420766f746520776569676874000000000000000000006064820152608401610a28565b50505060008a81526079602052604090208690555050881561332c576000888152607a602052604090819020805460ff19166001179055517f89e52969465b1f1866fc5d46fd62de953962e9cb33552443cd999eba05bd20dc906133179086908e906150c4565b60405180910390a15050505050505050610aa7565b6133368688614233565b61337561334960608d0160408e01614aec565b87607460009054906101000a90046001600160a01b03168e61010001803603810190611583919061516e565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d848c6040516133a69291906150c4565b60405180910390a1505050505050505092915050565b6001600160a01b0382166000908152603a602052604081205482106133e357506000610aa7565b60006133f2620151804261549c565b6001600160a01b0385166000908152603e60205260409020549091508111156134385750506001600160a01b0382166000908152603c6020526040902054811015610aa7565b6001600160a01b0384166000908152603d602052604090205461345c908490615484565b6001600160a01b0385166000908152603c602052604090205411159150610aa79050565b600060025460016002548460015461349891906151a1565b6134a29190615484565b6134ac919061518a565b610aa7919061549c565b60005460ff16156134fc5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612c863390565b600061119883836142c3565b60007fb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea60001b8260000151836020015161357a85604001516142ed565b61358786606001516142ed565b6135948760800151614350565b6040516020016135a9969594939291906154be565b604051602081830303815290604052805190602001209050919050565b6000620f42406135d683856151a1565b611198919061549c565b6000816001600160a01b0316836001600160a01b031614156136905760408086015190516001600160a01b0386169180156108fc02916000818181858888f1935050505061368b57816001600160a01b031663d0e30db086604001516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561366757600080fd5b505af115801561367b573d6000803e3d6000fd5b505050505061368b858585614393565b613a0c565b6000855160018111156136a5576136a5614c71565b1415613866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a082319060240160206040518083038186803b15801561370657600080fd5b505afa15801561371a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373e9190614f89565b9050856040015181101561385557836001600160a01b03166340c10f193083896040015161376c919061518a565b6040516001600160a01b03909216602483015260448201526064016040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516137c091906154f8565b6000604051808303816000865af19150503d80600081146137fd576040519150601f19603f3d011682016040523d82523d6000602084013e613802565b606091505b505080925050816138555760405162461bcd60e51b815260206004820152601b60248201527f546f6b656e3a204552433230206d696e74696e67206661696c656400000000006044820152606401610a28565b613860868686614393565b50613a0c565b60018551600181111561387b5761387b614c71565b141561399e5761389083858760200151614437565b61368b57602085810151604080516001600160a01b038881166024830152604480830194909452825180830390940184526064909101825292820180516001600160e01b03167f40c10f1900000000000000000000000000000000000000000000000000000000179052519185169161390991906154f8565b6000604051808303816000865af19150503d8060008114613946576040519150601f19603f3d011682016040523d82523d6000602084013e61394b565b606091505b5050809150508061368b5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e3a20455243373231206d696e74696e67206661696c6564000000006044820152606401610a28565b60405162461bcd60e51b815260206004820152602160248201527f546f6b656e3a20756e737570706f7274656420746f6b656e207374616e64617260448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b5050505050565b6000610aa7825490565b600081516001811115613a3257613a32614c71565b148015613a43575060008160400151115b8015613a5157506020810151155b80613a7b5750600181516001811115613a6c57613a6c614c71565b148015613a7b57506040810151155b610a655760405162461bcd60e51b815260206004820152601360248201527f546f6b656e3a20696e76616c696420696e666f000000000000000000000000006044820152606401610a28565b600060608186516001811115613adf57613adf614c71565b1415613bbd5760408681015181516001600160a01b038881166024830152878116604483015260648083019390935283518083039093018352608490910183526020820180516001600160e01b03166323b872dd60e01b179052915191851691613b4991906154f8565b6000604051808303816000865af19150503d8060008114613b86576040519150601f19603f3d011682016040523d82523d6000602084013e613b8b565b606091505b509092509050818015613bb6575080511580613bb6575080806020019051810190613bb69190615514565b9150613c84565b600186516001811115613bd257613bd2614c71565b141561399e57602086810151604080516001600160a01b0389811660248301528881166044830152606480830194909452825180830390940184526084909101825292820180516001600160e01b03166323b872dd60e01b1790525191851691613c3c91906154f8565b6000604051808303816000865af19150503d8060008114613c79576040519150601f19603f3d011682016040523d82523d6000602084013e613c7e565b606091505b50909250505b81610f5c57613c92866144e2565b613ca6866001600160a01b03166014613e6d565b613cba866001600160a01b03166014613e6d565b613cce866001600160a01b03166014613e6d565b604051602001612bbf9493929190615536565b613d516040805160a08101825260008082526020808301829052835160608082018652838252818301849052818601849052848601919091528451808201865283815280830184905280860184905281850152845190810185528281529081018290529283015290608082015290565b83815260006020820181905250604080820180516001600160a01b039788169052602080890151825190891690820152905146908301528751606084018051918916909152805195909716940193909352935182015292909201516080820152919050565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff166110175760008281526072602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613e143390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000611198836001600160a01b03841661454f565b60606000613e7c8360026151a1565b613e87906002615484565b67ffffffffffffffff811115613e9f57613e9f614e21565b6040519080825280601f01601f191660200182016040528015613ec9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110613f0057613f00614d90565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110613f4b57613f4b614d90565b60200101906001600160f81b031916908160001a9053506000613f6f8460026151a1565b613f7a906001615484565b90505b6001811115613fff577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110613fbb57613fbb614d90565b1a60f81b828281518110613fd157613fd1614d90565b60200101906001600160f81b031916908160001a90535060049490941c93613ff881615606565b9050613f7d565b5083156111985760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a28565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff16156110175760008281526072602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611198836001600160a01b03841661459e565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091526022820185905260428083018590528351808403909101815260629092019092528051910120600090611198565b6000806000836001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561418057600080fd5b505afa158015614194573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141b89190614f89565b90506141c381613480565b925060008760018111156141d9576141d9614c71565b1415614229576001600160a01b038616600090815260396020526040902054851061420a5761420781614691565b92505b6001600160a01b0386166000908152603a602052604090205485101591505b5094509492505050565b6000614242620151804261549c565b6001600160a01b0384166000908152603e6020526040902054909150811115614291576001600160a01b03929092166000908152603e6020908152604080832094909455603d90529190912055565b6001600160a01b0383166000908152603d6020526040812080548492906142b9908490615484565b9091555050505050565b60008260000182815481106142da576142da614d90565b9060005260206000200154905092915050565b805160208083015160408085015190516000946135a9947f353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e87649491939192019384526001600160a01b03928316602085015291166040830152606082015260800190565b805160208083015160408085015190516000946135a9947f1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d94919391920161561d565b600080845160018111156143a9576143a9614c71565b14156143c5576143be828486604001516146a9565b90506143ef565b6001845160018111156143da576143da614c71565b141561399e576143be82848660200151614437565b80610e89576143fd846144e2565b614411846001600160a01b03166014613e6d565b614425846001600160a01b03166014613e6d565b604051602001612bbf93929190615648565b604080513060248201526001600160a01b038481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092861691614495916154f8565b6000604051808303816000865af19150503d80600081146144d2576040519150601f19603f3d011682016040523d82523d6000602084013e6144d7565b606091505b509095945050505050565b606061450d826000015160018111156144fd576144fd614c71565b6001600160a01b03166001613e6d565b61451a8360200151614795565b6145278460400151614795565b604051602001614539939291906156d9565b6040516020818303038152906040529050919050565b600081815260018301602052604081205461459657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aa7565b506000610aa7565b600081815260018301602052604081205480156146875760006145c260018361518a565b85549091506000906145d69060019061518a565b905081811461463b5760008660000182815481106145f6576145f6614d90565b906000526020600020015490508087600001848154811061461957614619614d90565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061464c5761464c6157a2565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610aa7565b6000915050610aa7565b600060385460016038548460375461349891906151a1565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000926060929087169161471f91906154f8565b6000604051808303816000865af19150503d806000811461475c576040519150601f19603f3d011682016040523d82523d6000602084013e614761565b606091505b50909250905081801561478c57508051158061478c57508080602001905181019061478c9190615514565b95945050505050565b6060816147d557505060408051808201909152600481527f3078303000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156147f857806147e981614e06565b915050600882901c91506147d9565b6111848482613e6d565b604080516060810182526000808252602082015290810161483e6040805160608101909152806000815260200160008152602001600081525090565b905290565b60006020828403121561485557600080fd5b81356001600160e01b03198116811461119857600080fd5b6001600160a01b0381168114610a6557600080fd5b803561198d8161486d565b8060608101831015610aa757600080fd5b8060808101831015610aa757600080fd5b60008083601f8401126148c157600080fd5b50813567ffffffffffffffff8111156148d957600080fd5b6020830191508360208260051b850101111561172857600080fd5b60008060008060008060008060008060006101408c8e03121561491657600080fd5b61491f8c614882565b9a5061492d60208d01614882565b995061493b60408d01614882565b985060608c0135975060808c0135965060a08c0135955060c08c0135945067ffffffffffffffff8060e08e0135111561497357600080fd5b6149838e60e08f01358f0161488d565b9450806101008e0135111561499757600080fd5b6149a88e6101008f01358f0161489e565b9350806101208e013511156149bc57600080fd5b506149ce8d6101208e01358e016148af565b81935080925050509295989b509295989b9093969950565b600080600080604085870312156149fc57600080fd5b843567ffffffffffffffff80821115614a1457600080fd5b614a20888389016148af565b90965094506020870135915080821115614a3957600080fd5b50614a46878288016148af565b95989497509550505050565b60008060008060008060608789031215614a6b57600080fd5b863567ffffffffffffffff80821115614a8357600080fd5b614a8f8a838b016148af565b90985096506020890135915080821115614aa857600080fd5b614ab48a838b016148af565b90965094506040890135915080821115614acd57600080fd5b50614ada89828a016148af565b979a9699509497509295939492505050565b600060208284031215614afe57600080fd5b81356111988161486d565b600060208284031215614b1b57600080fd5b5035919050565b60008060408385031215614b3557600080fd5b823591506020830135614b478161486d565b809150509250929050565b600060a08284031215614b6457600080fd5b50919050565b60006101608284031215614b6457600080fd5b60008060006101808486031215614b9357600080fd5b614b9d8585614b6a565b925061016084013567ffffffffffffffff80821115614bbb57600080fd5b818601915086601f830112614bcf57600080fd5b813581811115614bde57600080fd5b876020606083028501011115614bf357600080fd5b6020830194508093505050509250925092565b60008060408385031215614c1957600080fd5b8235614c248161486d565b946020939093013593505050565b60008060408385031215614c4557600080fd5b50508035926020909101359150565b60006101608284031215614c6757600080fd5b6111988383614b6a565b634e487b7160e01b600052602160045260246000fd5b60028110610a6557634e487b7160e01b600052602160045260246000fd5b81516040820190614cb581614c87565b808352506001600160a01b03602084015116602083015292915050565b60008060008060008060006080888a031215614ced57600080fd5b873567ffffffffffffffff80821115614d0557600080fd5b614d118b838c016148af565b909950975060208a0135915080821115614d2a57600080fd5b614d368b838c016148af565b909750955060408a0135915080821115614d4f57600080fd5b614d5b8b838c016148af565b909550935060608a0135915080821115614d7457600080fd5b50614d818a828b0161489e565b91505092959891949750929550565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614dbd57600080fd5b83018035915067ffffffffffffffff821115614dd857600080fd5b6020019150600581901b360382131561172857600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415614e1a57614e1a614df0565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715614e6857634e487b7160e01b600052604160045260246000fd5b60405290565b60028110610a6557600080fd5b600060608284031215614e8d57600080fd5b614e95614e37565b90508135614ea281614e6e565b80825250602082013560208201526040820135604082015292915050565b600060a08284031215614ed257600080fd5b614eda614e37565b8235614ee58161486d565b81526020830135614ef58161486d565b6020820152614f078460408501614e7b565b60408201529392505050565b600060608284031215614f2557600080fd5b6040516060810181811067ffffffffffffffff82111715614f5657634e487b7160e01b600052604160045260246000fd5b604052823560ff81168114614f6a57600080fd5b8152602083810135908201526040928301359281019290925250919050565b600060208284031215614f9b57600080fd5b5051919050565b600060608284031215614fb457600080fd5b614fbc614e37565b90508135614fc98161486d565b81526020820135614fd98161486d565b806020830152506040820135604082015292915050565b6000610160828403121561500357600080fd5b60405160a0810181811067ffffffffffffffff8211171561503457634e487b7160e01b600052604160045260246000fd5b60405282358152602083013561504981614e6e565b602082015261505b8460408501614fa2565b604082015261506d8460a08501614fa2565b6060820152615080846101008501614e7b565b60808201529392505050565b80356150978161486d565b6001600160a01b0390811683526020820135906150b38261486d565b166020830152604090810135910152565b6000610180820190508382528235602083015260208301356150e581614e6e565b6150ee81614c87565b80604084015250615105606083016040850161508c565b61511560c0830160a0850161508c565b61012061010084013561512781614e6e565b61513081614c87565b81840152830135610140808401919091529092013561016090910152919050565b60006020828403121561516357600080fd5b813561119881614e6e565b60006060828403121561518057600080fd5b6111988383614e7b565b60008282101561519c5761519c614df0565b500390565b60008160001904831182151516156151bb576151bb614df0565b500290565b6000610180820190508382528251602083015260208301516151e181614c87565b6040838101919091528381015180516001600160a01b03908116606086015260208201511660808501529081015160a084015250606083015180516001600160a01b0390811660c085015260208201511660e08401526040810151610100840152506080830151805161525381614c87565b6101208401526020810151610140840152604001516101609092019190915292915050565b8183526000602080850194508260005b858110156152b657813561529b8161486d565b6001600160a01b031687529582019590820190600101615288565b509495945050505050565b6060815260006152d560608301888a615278565b6020838203818501526152e982888a615278565b8481036040860152858152869250810160005b8681101561532a57833561530f81614e6e565b61531881614c87565b825292820192908201906001016152fc565b509a9950505050505050505050565b60408152600061534d604083018688615278565b82810360208401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84111561538557600080fd5b8360051b80866020840137600091016020019081529695505050505050565b60005b838110156153bf5781810151838201526020016153a7565b83811115610e895750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516154088160178501602088016153a4565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516154458160288401602088016153a4565b01602801949350505050565b60208152600082518060208401526154708160408501602087016153a4565b601f01601f19169190910160400192915050565b6000821982111561549757615497614df0565b500190565b6000826154b957634e487b7160e01b600052601260045260246000fd5b500490565b8681526020810186905260c081016154d586614c87565b8560408301528460608301528360808301528260a0830152979650505050505050565b6000825161550a8184602087016153a4565b9190910192915050565b60006020828403121561552657600080fd5b8151801515811461119857600080fd5b7f546f6b656e3a20636f756c64206e6f74207472616e7366657220000000000000815260008551602061556f82601a8601838b016153a4565b7f2066726f6d200000000000000000000000000000000000000000000000000000601a9285019283015286516155aa81838501848b016153a4565b630103a37960e51b92018181019290925285516155cd81602485018985016153a4565b660103a37b5b2b7160cd1b6024939091019283015284516155f481602b85018489016153a4565b91909101602b01979650505050505050565b60008161561557615615614df0565b506000190190565b8481526080810161562d85614c87565b84602083015283604083015282606083015295945050505050565b7f546f6b656e3a20636f756c64206e6f74207472616e736665722000000000000081526000845161568081601a8501602089016153a4565b630103a37960e51b601a9184019182015284516156a481601e8401602089016153a4565b660103a37b5b2b7160cd1b601e929091019182015283516156cc8160258401602088016153a4565b0160250195945050505050565b7f546f6b656e496e666f280000000000000000000000000000000000000000000081526000845161571181600a8501602089016153a4565b80830190507f2c0000000000000000000000000000000000000000000000000000000000000080600a830152855161575081600b850160208a016153a4565b600b920191820152835161576b81600c8401602088016153a4565b7f2900000000000000000000000000000000000000000000000000000000000000600c9290910191820152600d0195945050505050565b634e487b7160e01b600052603160045260246000fdfeb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610348617350726f787941646d696e3a20756e617574686f72697a65642073656e64a2646970667358221220badebb50cc96ecc4d7c364bf224a8d7c089229c1d01abb009e0241a26ff0da2564736f6c63430008090033"; + + const FAKE_CREATION_CODE: &str = "608060405234801561001057600080fd5b506000805460ff1916905561582e806200002b6000396000f3fe60806040526004361061032d5760003560e01c80639157921c116101a5578063b2975794116100ec578063d547741f11610095578063dafae4081161006f578063dafae4081461096e578063dff525e11461098e578063e400327c146109ae578063e75235b8146109ce5761033c565b8063d547741f14610901578063d55ed10314610921578063d64af2a61461094e5761033c565b8063cdb67444116100c6578063cdb674441461089c578063cdf64a76146108b4578063d19773d2146108d45761033c565b8063b29757941461082f578063b9c362091461085c578063ca15c8731461087c5761033c565b8063a3912ec81161014e578063affed0e011610128578063affed0e0146107cc578063b1a2567e146107e2578063b1d08a03146108025761033c565b8063a3912ec81461033a578063ab7965661461077f578063ac78dfe8146107ac5761033c565b8063994390891161017f57806399439089146107155780639dcc4da314610735578063a217fddf1461076a5761033c565b80639157921c1461068f57806391d14854146106af57806393c5678f146106f55761033c565b806336568abe116102745780635c975abb1161021d5780637de5dedd116101f75780637de5dedd146106115780638456cb59146106265780638f34e3471461063b5780639010d07c1461066f5761033c565b80635c975abb146105ac5780636932be98146105c45780636c1ce670146105f15761033c565b80634d0d66731161024e5780634d0d66731461052f5780634d493f4e1461054f57806359122f6b1461057f5761033c565b806336568abe146104e75780633f4ba83a146105075780634b14557e1461051c5761033c565b80631d4a7210116102d65780632f2ff15d116102b05780632f2ff15d1461049b578063302d12db146104bb5780633644e515146104d25761033c565b80631d4a721014610428578063248a9ca3146104555780632dfdf0b5146104855761033c565b8063180ff1e911610307578063180ff1e9146103d55780631a8e55b0146103e85780631b6e7594146104085761033c565b806301ffc9a71461034457806317ce2dd41461037957806317fcb39b1461039d5761033c565b3661033c5761033a6109e6565b005b61033a6109e6565b34801561035057600080fd5b5061036461035f366004614843565b610a69565b60405190151581526020015b60405180910390f35b34801561038557600080fd5b5061038f60755481565b604051908152602001610370565b3480156103a957600080fd5b506074546103bd906001600160a01b031681565b6040516001600160a01b039091168152602001610370565b61033a6103e33660046148f4565b610aad565b3480156103f457600080fd5b5061033a6104033660046149e6565b610dbd565b34801561041457600080fd5b5061033a610423366004614a52565b610e8f565b34801561043457600080fd5b5061038f610443366004614aec565b603e6020526000908152604090205481565b34801561046157600080fd5b5061038f610470366004614b09565b60009081526072602052604090206001015490565b34801561049157600080fd5b5061038f60765481565b3480156104a757600080fd5b5061033a6104b6366004614b22565b610f64565b3480156104c757600080fd5b5061038f620f424081565b3480156104de57600080fd5b5060775461038f565b3480156104f357600080fd5b5061033a610502366004614b22565b610f8f565b34801561051357600080fd5b5061033a61101b565b61033a61052a366004614b52565b611083565b34801561053b57600080fd5b5061036461054a366004614b7d565b6110e1565b34801561055b57600080fd5b5061036461056a366004614b09565b607a6020526000908152604090205460ff1681565b34801561058b57600080fd5b5061038f61059a366004614aec565b603a6020526000908152604090205481565b3480156105b857600080fd5b5060005460ff16610364565b3480156105d057600080fd5b5061038f6105df366004614b09565b60796020526000908152604090205481565b3480156105fd57600080fd5b5061036461060c366004614c06565b61118c565b34801561061d57600080fd5b5061038f61119f565b34801561063257600080fd5b5061033a611234565b34801561064757600080fd5b5061038f7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e481565b34801561067b57600080fd5b506103bd61068a366004614c32565b61129c565b34801561069b57600080fd5b5061033a6106aa366004614c54565b6112b4565b3480156106bb57600080fd5b506103646106ca366004614b22565b60009182526072602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561070157600080fd5b5061033a6107103660046149e6565b6115ca565b34801561072157600080fd5b506003546103bd906001600160a01b031681565b34801561074157600080fd5b50610755610750366004614c32565b611696565b60408051928352602083019190915201610370565b34801561077657600080fd5b5061038f600081565b34801561078b57600080fd5b5061038f61079a366004614aec565b603c6020526000908152604090205481565b3480156107b857600080fd5b506103646107c7366004614b09565b61172f565b3480156107d857600080fd5b5061038f60045481565b3480156107ee57600080fd5b5061033a6107fd3660046149e6565b6117ce565b34801561080e57600080fd5b5061038f61081d366004614aec565b60396020526000908152604090205481565b34801561083b57600080fd5b5061084f61084a366004614aec565b61189a565b6040516103709190614ca5565b34801561086857600080fd5b50610755610877366004614c32565b611992565b34801561088857600080fd5b5061038f610897366004614b09565b611a17565b3480156108a857600080fd5b50603754603854610755565b3480156108c057600080fd5b5061033a6108cf366004614aec565b611a2e565b3480156108e057600080fd5b5061038f6108ef366004614aec565b603b6020526000908152604090205481565b34801561090d57600080fd5b5061033a61091c366004614b22565b611a97565b34801561092d57600080fd5b5061038f61093c366004614aec565b603d6020526000908152604090205481565b34801561095a57600080fd5b5061033a610969366004614aec565b611abd565b34801561097a57600080fd5b50610364610989366004614b09565b611b26565b34801561099a57600080fd5b5061033a6109a9366004614cd2565b611bbd565b3480156109ba57600080fd5b5061033a6109c93660046149e6565b611cc7565b3480156109da57600080fd5b50600154600254610755565b60005460ff1615610a315760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b6074546001600160a01b03163314610a6757610a4b614802565b338152604080820151349101528051610a65908290611d93565b505b565b60006001600160e01b031982167f5a05180f000000000000000000000000000000000000000000000000000000001480610aa75750610aa78261210a565b92915050565b607154610100900460ff16610ac85760715460ff1615610acc565b303b155b610b3e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a28565b607154610100900460ff16158015610b60576071805461ffff19166101011790555b610b6b60008d612171565b6075899055610b798b61217b565b610b828a6121dd565b610c29604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f159f52c1e3a2b6a6aad3950adf713516211484e0516dad685ea662a094b7c43b918101919091527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260c00160408051601f198184030181529190528051602090910120607755565b610c338887612238565b5050610c3f87876122f8565b5050610c496123d3565b6000610c558680614da6565b90501115610d1657610c7e610c6a8680614da6565b610c776020890189614da6565b8787612467565b610ca4610c8b8680614da6565b8660005b602002810190610c9f9190614da6565b612666565b610cca610cb18680614da6565b8660015b602002810190610cc59190614da6565b612779565b610cf0610cd78680614da6565b8660025b602002810190610ceb9190614da6565b61288c565b610d16610cfd8680614da6565b8660035b602002810190610d119190614da6565b612a30565b60005b610d266040870187614da6565b9050811015610d9c57610d8a7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e4610d606040890189614da6565b84818110610d7057610d70614d90565b9050602002016020810190610d859190614aec565b612b43565b80610d9481614e06565b915050610d19565b508015610daf576071805461ff00191690555b505050505050505050505050565b6000805160206157b9833981519152546001600160a01b03163314610e1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82610e7d5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612779565b50505050565b6000805160206157b9833981519152546001600160a01b03163314610eef5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b84610f4e5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b610f5c868686868686612467565b505050505050565b600082815260726020526040902060010154610f808133612b65565b610f8a8383612b43565b505050565b6001600160a01b038116331461100d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610a28565b6110178282612be5565b5050565b6000805160206157b9833981519152546001600160a01b0316331461107b5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a67612c07565b60005460ff16156110c95760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b610a656110db36839003830183614ec0565b33611d93565b6000805460ff16156111285760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b611184848484808060200260200160405190810160405280939291908181526020016000905b8282101561117a5761116b60608302860136819003810190614f13565b8152602001906001019061114e565b5050505050612ca3565b949350505050565b600061119883836133bc565b9392505050565b600061122f600360009054906101000a90046001600160a01b03166001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b1580156111f257600080fd5b505afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a9190614f89565b613480565b905090565b6000805160206157b9833981519152546001600160a01b031633146112945760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a676134b6565b60008281526073602052604081206111989083613531565b7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e46112df8133612b65565b60006112f86112f336859003850185614ff0565b61353d565b905061130c6112f336859003850185614ff0565b8335600090815260796020526040902054146113765760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b82356000908152607a602052604090205460ff166113fc5760405162461bcd60e51b815260206004820152603160248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220617060448201527f70726f766564207769746864726177616c0000000000000000000000000000006064820152608401610a28565b82356000908152607a602052604090819020805460ff19169055517fd639511b37b3b002cca6cfe6bca0d833945a5af5a045578a0627fc43b79b26309061144690839086906150c4565b60405180910390a160006114606080850160608601614aec565b9050600061147661012086016101008701615151565b600181111561148757611487614c71565b141561154f5760006114a2368690038601610100870161516e565b6001600160a01b0383166000908152603b60205260409020549091506114ce90610140870135906135c6565b604082015260006114e8368790038701610100880161516e565b60408301519091506114ff9061014088013561518a565b604082015260745461151f908390339086906001600160a01b03166135e0565b6115486115326060880160408901614aec565b60745483919086906001600160a01b03166135e0565b505061158b565b61158b6115626060860160408701614aec565b60745483906001600160a01b03166115833689900389016101008a0161516e565b9291906135e0565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d82856040516115bc9291906150c4565b60405180910390a150505050565b6000805160206157b9833981519152546001600160a01b0316331461162a5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261168a5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612666565b6000806116b86000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146117115760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b84846122f8565b90925090506117286123d3565b9250929050565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b15801561177457600080fd5b505afa158015611788573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ac9190614f89565b6037546117b991906151a1565b6038546117c690846151a1565b101592915050565b6000805160206157b9833981519152546001600160a01b0316331461182e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261188e5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e898484848461288c565b60408051808201909152600080825260208201526001600160a01b0382166000908152607860205260409081902081518083019092528054829060ff1660018111156118e8576118e8614c71565b60018111156118f9576118f9614c71565b815290546001600160a01b036101009091048116602092830152908201519192501661198d5760405162461bcd60e51b815260206004820152602560248201527f4d61696e636861696e4761746577617956323a20756e737570706f727465642060448201527f746f6b656e0000000000000000000000000000000000000000000000000000006064820152608401610a28565b919050565b6000806119b46000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b031614611a0d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b8484612238565b6000818152607360205260408120610aa790613a13565b6000805160206157b9833981519152546001600160a01b03163314611a8e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a65816121dd565b600082815260726020526040902060010154611ab38133612b65565b610f8a8383612be5565b6000805160206157b9833981519152546001600160a01b03163314611b1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a658161217b565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b158015611b6b57600080fd5b505afa158015611b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba39190614f89565b600154611bb091906151a1565b6002546117c690846151a1565b6000805160206157b9833981519152546001600160a01b03163314611c1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b85611c7c5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b611c8a878787878787612467565b611c978787836000610c8f565b611ca48787836001610cb5565b611cb18787836002610cdb565b611cbe8787836003610d01565b50505050505050565b6000805160206157b9833981519152546001600160a01b03163314611d275760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82611d875760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612a30565b604080518082018252600080825260208201526074549184015190916001600160a01b031690611dc290613a1d565b60208401516001600160a01b0316611ee1573484604001516040015114611e375760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611e408161189a565b6040850151519092506001811115611e5a57611e5a614c71565b82516001811115611e6d57611e6d614c71565b14611ecd5760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b6001600160a01b0381166020850152612087565b3415611f3b5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611f48846020015161189a565b6040850151519092506001811115611f6257611f62614c71565b82516001811115611f7557611f75614c71565b14611fd55760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b60208401516040850151611fec9185903090613ac7565b83602001516001600160a01b0316816001600160a01b031614156120875760408481015181015190517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b15801561206e57600080fd5b505af1158015612082573d6000803e3d6000fd5b505050505b607680546000918261209883614e06565b91905055905060006120bf858386602001516075548a613ce190949392919063ffffffff16565b90507fd7b25068d9dc8d00765254cfb7f5070f98d263c8d68931d937c7362fa738048b6120eb8261353d565b826040516120fa9291906151c0565b60405180910390a1505050505050565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610aa757507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610aa7565b6110178282612b43565b6074805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f9d2334c23be647e994f27a72c5eee42a43d5bdcfe15bb88e939103c2b114cbaf906020015b60405180910390a150565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fef40dc07567635f84f5edbd2f8dbc16b40d9d282dd8e7e6f4ff58236b6836169906020016121d2565b6000808284111561228b5760405162461bcd60e51b815260206004820152601c60248201527f4761746577617956323a20696e76616c6964207468726573686f6c64000000006044820152606401610a28565b505060018054600280549285905583905560048054919291849186919060006122b383614e06565b9091555060408051868152602081018690527f976f8a9c5bdf8248dec172376d6e2b80a8e3df2f0328e381c6db8e1cf138c0f891015b60405180910390a49250929050565b600080828411156123715760405162461bcd60e51b815260206004820152602760248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64000000000000000000000000000000000000000000000000006064820152608401610a28565b5050603780546038805492859055839055600480549192918491869190600061239983614e06565b9091555060408051868152602081018690527f31312c97b89cc751b832d98fd459b967a2c3eef3b49757d1cf5ebaa12bb6eee191016122e9565b6002546037546123e391906151a1565b6038546001546123f391906151a1565b1115610a675760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64730000000000000000000000000000000000000000000000006064820152608401610a28565b848314801561247557508481145b6124e75760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206172726160448201527f79206c656e6774680000000000000000000000000000000000000000000000006064820152608401610a28565b60005b8581101561262c5784848281811061250457612504614d90565b90506020020160208101906125199190614aec565b6078600089898581811061252f5761252f614d90565b90506020020160208101906125449190614aec565b6001600160a01b039081168252602082019290925260400160002080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010093909216929092021790558282828181106125a3576125a3614d90565b90506020020160208101906125b89190615151565b607860008989858181106125ce576125ce614d90565b90506020020160208101906125e39190614aec565b6001600160a01b031681526020810191909152604001600020805460ff19166001838181111561261557612615614c71565b02179055508061262481614e06565b9150506124ea565b507fa4f03cc9c0e0aeb5b71b4ec800702753f65748c2cf3064695ba8e8b46be704448686868686866040516120fa969594939291906152c1565b8281146126c85760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612743578282828181106126e5576126e5614d90565b905060200201356039600087878581811061270257612702614d90565b90506020020160208101906127179190614aec565b6001600160a01b031681526020810191909152604001600020558061273b81614e06565b9150506126cb565b507f80bc635c452ae67f12f9b6f12ad4daa6dbbc04eeb9ebb87d354ce10c0e210dc0848484846040516115bc9493929190615339565b8281146127db5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612856578282828181106127f8576127f8614d90565b90506020020135603a600087878581811061281557612815614d90565b905060200201602081019061282a9190614aec565b6001600160a01b031681526020810191909152604001600020558061284e81614e06565b9150506127de565b507f64557254143204d91ba2d95acb9fda1e5fea55f77efd028685765bc1e94dd4b5848484846040516115bc9493929190615339565b8281146128ee5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b838110156129fa57620f424083838381811061290f5761290f614d90565b90506020020135111561298a5760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420706560448201527f7263656e746167650000000000000000000000000000000000000000000000006064820152608401610a28565b82828281811061299c5761299c614d90565b90506020020135603b60008787858181106129b9576129b9614d90565b90506020020160208101906129ce9190614aec565b6001600160a01b03168152602081019190915260400160002055806129f281614e06565b9150506128f1565b507fb05f5de88ae0294ebb6f67c5af2fcbbd593cc6bdfe543e2869794a4c8ce3ea50848484846040516115bc9493929190615339565b828114612a925760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612b0d57828282818110612aaf57612aaf614d90565b90506020020135603c6000878785818110612acc57612acc614d90565b9050602002016020810190612ae19190614aec565b6001600160a01b0316815260208101919091526040016000205580612b0581614e06565b915050612a95565b507fb5d2963614d72181b4df1f993d45b83edf42fa19710f0204217ba1b3e183bb73848484846040516115bc9493929190615339565b612b4d8282613db6565b6000828152607360205260409020610f8a9082613e58565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff1661101757612ba3816001600160a01b03166014613e6d565b612bae836020613e6d565b604051602001612bbf9291906153d0565b60408051601f198184030181529082905262461bcd60e51b8252610a2891600401615451565b612bef828261404e565b6000828152607360205260409020610f8a90826140d1565b60005460ff16612c595760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610a28565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000823561014084013582612cbe6080870160608801614aec565b9050612cdb612cd6368890038801610100890161516e565b613a1d565b6001612ced6040880160208901615151565b6001811115612cfe57612cfe614c71565b14612d715760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964207265636560448201527f697074206b696e640000000000000000000000000000000000000000000000006064820152608401610a28565b60808601354614612de95760405162461bcd60e51b8152602060048201526024808201527f4d61696e636861696e4761746577617956323a20696e76616c6964206368616960448201527f6e206964000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6000612dfe61084a6080890160608a01614aec565b9050612e1261012088016101008901615151565b6001811115612e2357612e23614c71565b81516001811115612e3657612e36614c71565b148015612e675750612e4e60e0880160c08901614aec565b6001600160a01b031681602001516001600160a01b0316145b612ebf5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b60008481526079602052604090205415612f415760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220707260448201527f6f636573736564207769746864726177616c00000000000000000000000000006064820152608401610a28565b6001612f5561012089016101008a01615151565b6001811115612f6657612f66614c71565b1480612f795750612f7782846133bc565b155b612feb5760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a2072656163686564206461696c60448201527f79207769746864726177616c206c696d697400000000000000000000000000006064820152608401610a28565b6000612fff6112f3368a90038a018a614ff0565b9050600061300f607754836140e6565b6003549091506001600160a01b0316600061303d6130356101208d016101008e01615151565b878985614142565b60408051606081018252600080825260208201819052918101829052919b50919250819081906000805b8f5181101561323c578f818151811061308257613082614d90565b6020908102919091018101518051818301516040808401518151600081529586018083528f905260ff9093169085015260608401526080830152935060019060a0016020604051602081039080840390855afa1580156130e6573d6000803e3d6000fd5b505050602060405103519450846001600160a01b0316846001600160a01b0316106131795760405162461bcd60e51b815260206004820152602160248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206f72646560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6040517f953865650000000000000000000000000000000000000000000000000000000081526001600160a01b03808716600483015286955089169063953865659060240160206040518083038186803b1580156131d657600080fd5b505afa1580156131ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320e9190614f89565b6132189083615484565b915086821061322a576001955061323c565b8061323481614e06565b915050613067565b50846132b05760405162461bcd60e51b815260206004820152603660248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220696e60448201527f73756666696369656e7420766f746520776569676874000000000000000000006064820152608401610a28565b50505060008a81526079602052604090208690555050881561332c576000888152607a602052604090819020805460ff19166001179055517f89e52969465b1f1866fc5d46fd62de953962e9cb33552443cd999eba05bd20dc906133179086908e906150c4565b60405180910390a15050505050505050610aa7565b6133368688614233565b61337561334960608d0160408e01614aec565b87607460009054906101000a90046001600160a01b03168e61010001803603810190611583919061516e565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d848c6040516133a69291906150c4565b60405180910390a1505050505050505092915050565b6001600160a01b0382166000908152603a602052604081205482106133e357506000610aa7565b60006133f2620151804261549c565b6001600160a01b0385166000908152603e60205260409020549091508111156134385750506001600160a01b0382166000908152603c6020526040902054811015610aa7565b6001600160a01b0384166000908152603d602052604090205461345c908490615484565b6001600160a01b0385166000908152603c602052604090205411159150610aa79050565b600060025460016002548460015461349891906151a1565b6134a29190615484565b6134ac919061518a565b610aa7919061549c565b60005460ff16156134fc5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612c863390565b600061119883836142c3565b60007fb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea60001b8260000151836020015161357a85604001516142ed565b61358786606001516142ed565b6135948760800151614350565b6040516020016135a9969594939291906154be565b604051602081830303815290604052805190602001209050919050565b6000620f42406135d683856151a1565b611198919061549c565b6000816001600160a01b0316836001600160a01b031614156136905760408086015190516001600160a01b0386169180156108fc02916000818181858888f1935050505061368b57816001600160a01b031663d0e30db086604001516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561366757600080fd5b505af115801561367b573d6000803e3d6000fd5b505050505061368b858585614393565b613a0c565b6000855160018111156136a5576136a5614c71565b1415613866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a082319060240160206040518083038186803b15801561370657600080fd5b505afa15801561371a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373e9190614f89565b9050856040015181101561385557836001600160a01b03166340c10f193083896040015161376c919061518a565b6040516001600160a01b03909216602483015260448201526064016040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516137c091906154f8565b6000604051808303816000865af19150503d80600081146137fd576040519150601f19603f3d011682016040523d82523d6000602084013e613802565b606091505b505080925050816138555760405162461bcd60e51b815260206004820152601b60248201527f546f6b656e3a204552433230206d696e74696e67206661696c656400000000006044820152606401610a28565b613860868686614393565b50613a0c565b60018551600181111561387b5761387b614c71565b141561399e5761389083858760200151614437565b61368b57602085810151604080516001600160a01b038881166024830152604480830194909452825180830390940184526064909101825292820180516001600160e01b03167f40c10f1900000000000000000000000000000000000000000000000000000000179052519185169161390991906154f8565b6000604051808303816000865af19150503d8060008114613946576040519150601f19603f3d011682016040523d82523d6000602084013e61394b565b606091505b5050809150508061368b5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e3a20455243373231206d696e74696e67206661696c6564000000006044820152606401610a28565b60405162461bcd60e51b815260206004820152602160248201527f546f6b656e3a20756e737570706f7274656420746f6b656e207374616e64617260448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b5050505050565b6000610aa7825490565b600081516001811115613a3257613a32614c71565b148015613a43575060008160400151115b8015613a5157506020810151155b80613a7b5750600181516001811115613a6c57613a6c614c71565b148015613a7b57506040810151155b610a655760405162461bcd60e51b815260206004820152601360248201527f546f6b656e3a20696e76616c696420696e666f000000000000000000000000006044820152606401610a28565b600060608186516001811115613adf57613adf614c71565b1415613bbd5760408681015181516001600160a01b038881166024830152878116604483015260648083019390935283518083039093018352608490910183526020820180516001600160e01b03166323b872dd60e01b179052915191851691613b4991906154f8565b6000604051808303816000865af19150503d8060008114613b86576040519150601f19603f3d011682016040523d82523d6000602084013e613b8b565b606091505b509092509050818015613bb6575080511580613bb6575080806020019051810190613bb69190615514565b9150613c84565b600186516001811115613bd257613bd2614c71565b141561399e57602086810151604080516001600160a01b0389811660248301528881166044830152606480830194909452825180830390940184526084909101825292820180516001600160e01b03166323b872dd60e01b1790525191851691613c3c91906154f8565b6000604051808303816000865af19150503d8060008114613c79576040519150601f19603f3d011682016040523d82523d6000602084013e613c7e565b606091505b50909250505b81610f5c57613c92866144e2565b613ca6866001600160a01b03166014613e6d565b613cba866001600160a01b03166014613e6d565b613cce866001600160a01b03166014613e6d565b604051602001612bbf9493929190615536565b613d516040805160a08101825260008082526020808301829052835160608082018652838252818301849052818601849052848601919091528451808201865283815280830184905280860184905281850152845190810185528281529081018290529283015290608082015290565b83815260006020820181905250604080820180516001600160a01b039788169052602080890151825190891690820152905146908301528751606084018051918916909152805195909716940193909352935182015292909201516080820152919050565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff166110175760008281526072602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613e143390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000611198836001600160a01b03841661454f565b60606000613e7c8360026151a1565b613e87906002615484565b67ffffffffffffffff811115613e9f57613e9f614e21565b6040519080825280601f01601f191660200182016040528015613ec9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110613f0057613f00614d90565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110613f4b57613f4b614d90565b60200101906001600160f81b031916908160001a9053506000613f6f8460026151a1565b613f7a906001615484565b90505b6001811115613fff577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110613fbb57613fbb614d90565b1a60f81b828281518110613fd157613fd1614d90565b60200101906001600160f81b031916908160001a90535060049490941c93613ff881615606565b9050613f7d565b5083156111985760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a28565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff16156110175760008281526072602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611198836001600160a01b03841661459e565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091526022820185905260428083018590528351808403909101815260629092019092528051910120600090611198565b6000806000836001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561418057600080fd5b505afa158015614194573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141b89190614f89565b90506141c381613480565b925060008760018111156141d9576141d9614c71565b1415614229576001600160a01b038616600090815260396020526040902054851061420a5761420781614691565b92505b6001600160a01b0386166000908152603a602052604090205485101591505b5094509492505050565b6000614242620151804261549c565b6001600160a01b0384166000908152603e6020526040902054909150811115614291576001600160a01b03929092166000908152603e6020908152604080832094909455603d90529190912055565b6001600160a01b0383166000908152603d6020526040812080548492906142b9908490615484565b9091555050505050565b60008260000182815481106142da576142da614d90565b9060005260206000200154905092915050565b805160208083015160408085015190516000946135a9947f353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e87649491939192019384526001600160a01b03928316602085015291166040830152606082015260800190565b805160208083015160408085015190516000946135a9947f1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d94919391920161561d565b600080845160018111156143a9576143a9614c71565b14156143c5576143be828486604001516146a9565b90506143ef565b6001845160018111156143da576143da614c71565b141561399e576143be82848660200151614437565b80610e89576143fd846144e2565b614411846001600160a01b03166014613e6d565b614425846001600160a01b03166014613e6d565b604051602001612bbf93929190615648565b604080513060248201526001600160a01b038481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092861691614495916154f8565b6000604051808303816000865af19150503d80600081146144d2576040519150601f19603f3d011682016040523d82523d6000602084013e6144d7565b606091505b509095945050505050565b606061450d826000015160018111156144fd576144fd614c71565b6001600160a01b03166001613e6d565b61451a8360200151614795565b6145278460400151614795565b604051602001614539939291906156d9565b6040516020818303038152906040529050919050565b600081815260018301602052604081205461459657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aa7565b506000610aa7565b600081815260018301602052604081205480156146875760006145c260018361518a565b85549091506000906145d69060019061518a565b905081811461463b5760008660000182815481106145f6576145f6614d90565b906000526020600020015490508087600001848154811061461957614619614d90565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061464c5761464c6157a2565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610aa7565b6000915050610aa7565b600060385460016038548460375461349891906151a1565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000926060929087169161471f91906154f8565b6000604051808303816000865af19150503d806000811461475c576040519150601f19603f3d011682016040523d82523d6000602084013e614761565b606091505b50909250905081801561478c57508051158061478c57508080602001905181019061478c9190615514565b95945050505050565b6060816147d557505060408051808201909152600481527f3078303000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156147f857806147e981614e06565b915050600882901c91506147d9565b6111848482613e6d565b604080516060810182526000808252602082015290810161483e6040805160608101909152806000815260200160008152602001600081525090565b905290565b60006020828403121561485557600080fd5b81356001600160e01b03198116811461119857600080fd5b6001600160a01b0381168114610a6557600080fd5b803561198d8161486d565b8060608101831015610aa757600080fd5b8060808101831015610aa757600080fd5b60008083601f8401126148c157600080fd5b50813567ffffffffffffffff8111156148d957600080fd5b6020830191508360208260051b850101111561172857600080fd5b60008060008060008060008060008060006101408c8e03121561491657600080fd5b61491f8c614882565b9a5061492d60208d01614882565b995061493b60408d01614882565b985060608c0135975060808c0135965060a08c0135955060c08c0135945067ffffffffffffffff8060e08e0135111561497357600080fd5b6149838e60e08f01358f0161488d565b9450806101008e0135111561499757600080fd5b6149a88e6101008f01358f0161489e565b9350806101208e013511156149bc57600080fd5b506149ce8d6101208e01358e016148af565b81935080925050509295989b509295989b9093969950565b600080600080604085870312156149fc57600080fd5b843567ffffffffffffffff80821115614a1457600080fd5b614a20888389016148af565b90965094506020870135915080821115614a3957600080fd5b50614a46878288016148af565b95989497509550505050565b60008060008060008060608789031215614a6b57600080fd5b863567ffffffffffffffff80821115614a8357600080fd5b614a8f8a838b016148af565b90985096506020890135915080821115614aa857600080fd5b614ab48a838b016148af565b90965094506040890135915080821115614acd57600080fd5b50614ada89828a016148af565b979a9699509497509295939492505050565b600060208284031215614afe57600080fd5b81356111988161486d565b600060208284031215614b1b57600080fd5b5035919050565b60008060408385031215614b3557600080fd5b823591506020830135614b478161486d565b809150509250929050565b600060a08284031215614b6457600080fd5b50919050565b60006101608284031215614b6457600080fd5b60008060006101808486031215614b9357600080fd5b614b9d8585614b6a565b925061016084013567ffffffffffffffff80821115614bbb57600080fd5b818601915086601f830112614bcf57600080fd5b813581811115614bde57600080fd5b876020606083028501011115614bf357600080fd5b6020830194508093505050509250925092565b60008060408385031215614c1957600080fd5b8235614c248161486d565b946020939093013593505050565b60008060408385031215614c4557600080fd5b50508035926020909101359150565b60006101608284031215614c6757600080fd5b6111988383614b6a565b634e487b7160e01b600052602160045260246000fd5b60028110610a6557634e487b7160e01b600052602160045260246000fd5b81516040820190614cb581614c87565b808352506001600160a01b03602084015116602083015292915050565b60008060008060008060006080888a031215614ced57600080fd5b873567ffffffffffffffff80821115614d0557600080fd5b614d118b838c016148af565b909950975060208a0135915080821115614d2a57600080fd5b614d368b838c016148af565b909750955060408a0135915080821115614d4f57600080fd5b614d5b8b838c016148af565b909550935060608a0135915080821115614d7457600080fd5b50614d818a828b0161489e565b91505092959891949750929550565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614dbd57600080fd5b83018035915067ffffffffffffffff821115614dd857600080fd5b6020019150600581901b360382131561172857600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415614e1a57614e1a614df0565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715614e6857634e487b7160e01b600052604160045260246000fd5b60405290565b60028110610a6557600080fd5b600060608284031215614e8d57600080fd5b614e95614e37565b90508135614ea281614e6e565b80825250602082013560208201526040820135604082015292915050565b600060a08284031215614ed257600080fd5b614eda614e37565b8235614ee58161486d565b81526020830135614ef58161486d565b6020820152614f078460408501614e7b565b60408201529392505050565b600060608284031215614f2557600080fd5b6040516060810181811067ffffffffffffffff82111715614f5657634e487b7160e01b600052604160045260246000fd5b604052823560ff81168114614f6a57600080fd5b8152602083810135908201526040928301359281019290925250919050565b600060208284031215614f9b57600080fd5b5051919050565b600060608284031215614fb457600080fd5b614fbc614e37565b90508135614fc98161486d565b81526020820135614fd98161486d565b806020830152506040820135604082015292915050565b6000610160828403121561500357600080fd5b60405160a0810181811067ffffffffffffffff8211171561503457634e487b7160e01b600052604160045260246000fd5b60405282358152602083013561504981614e6e565b602082015261505b8460408501614fa2565b604082015261506d8460a08501614fa2565b6060820152615080846101008501614e7b565b60808201529392505050565b80356150978161486d565b6001600160a01b0390811683526020820135906150b38261486d565b166020830152604090810135910152565b6000610180820190508382528235602083015260208301356150e581614e6e565b6150ee81614c87565b80604084015250615105606083016040850161508c565b61511560c0830160a0850161508c565b61012061010084013561512781614e6e565b61513081614c87565b81840152830135610140808401919091529092013561016090910152919050565b60006020828403121561516357600080fd5b813561119881614e6e565b60006060828403121561518057600080fd5b6111988383614e7b565b60008282101561519c5761519c614df0565b500390565b60008160001904831182151516156151bb576151bb614df0565b500290565b6000610180820190508382528251602083015260208301516151e181614c87565b6040838101919091528381015180516001600160a01b03908116606086015260208201511660808501529081015160a084015250606083015180516001600160a01b0390811660c085015260208201511660e08401526040810151610100840152506080830151805161525381614c87565b6101208401526020810151610140840152604001516101609092019190915292915050565b8183526000602080850194508260005b858110156152b657813561529b8161486d565b6001600160a01b031687529582019590820190600101615288565b509495945050505050565b6060815260006152d560608301888a615278565b6020838203818501526152e982888a615278565b8481036040860152858152869250810160005b8681101561532a57833561530f81614e6e565b61531881614c87565b825292820192908201906001016152fc565b509a9950505050505050505050565b60408152600061534d604083018688615278565b82810360208401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84111561538557600080fd5b8360051b80866020840137600091016020019081529695505050505050565b60005b838110156153bf5781810151838201526020016153a7565b83811115610e895750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516154088160178501602088016153a4565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516154458160288401602088016153a4565b01602801949350505050565b60208152600082518060208401526154708160408501602087016153a4565b601f01601f19169190910160400192915050565b6000821982111561549757615497614df0565b500190565b6000826154b957634e487b7160e01b600052601260045260246000fd5b500490565b8681526020810186905260c081016154d586614c87565b8560408301528460608301528360808301528260a0830152979650505050505050565b6000825161550a8184602087016153a4565b9190910192915050565b60006020828403121561552657600080fd5b8151801515811461119857600080fd5b7f546f6b656e3a20636f756c64206e6f74207472616e7366657220000000000000815260008551602061556f82601a8601838b016153a4565b7f2066726f6d200000000000000000000000000000000000000000000000000000601a9285019283015286516155aa81838501848b016153a4565b630103a37960e51b92018181019290925285516155cd81602485018985016153a4565b660103a37b5b2b7160cd1b6024939091019283015284516155f481602b85018489016153a4565b91909101602b01979650505050505050565b60008161561557615615614df0565b506000190190565b8481526080810161562d85614c87565b84602083015283604083015282606083015295945050505050565b7f546f6b656e3a20636f756c64206e6f74207472616e736665722000000000000081526000845161568081601a8501602089016153a4565b630103a37960e51b601a9184019182015284516156a481601e8401602089016153a4565b660103a37b5b2b7160cd1b601e929091019182015283516156cc8160258401602088016153a4565b0160250195945050505050565b7f546f6b656e496e666f280000000000000000000000000000000000000000000081526000845161571181600a8501602089016153a4565b80830190507f2c0000000000000000000000000000000000000000000000000000000000000080600a830152855161575081600b850160208a016153a4565b600b920191820152835161576b81600c8401602088016153a4565b7f2900000000000000000000000000000000000000000000000000000000000000600c9290910191820152600d0195945050505050565b634e487b7160e01b600052603160045260246000fdfeb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610348617350726f787941646d696e3a20756e617574686f72697a65642073656e64a2646970667358221220badebb50cc96ecc4d7c364bf224a8d7c089229c1d01abb009e0241a26ff0da2564736f6c63430008090033"; +} diff --git a/crates/tweak/src/compatibility.rs b/crates/tweak/src/compatibility.rs new file mode 100644 index 000000000000..a93135e2e26a --- /dev/null +++ b/crates/tweak/src/compatibility.rs @@ -0,0 +1,355 @@ +//! Compatibility check of tweaking an on-chain contract with a local cloned project. +//! The local project usually should be created by `forge clone` command. +//! Users may modify the source code of the cloned project, but the storage layout should remain the +//! same as the original contract. + +use std::collections::BTreeMap; + +use foundry_compilers::artifacts::StorageLayout; + +use crate::metadata::ClonedProject; + +/// Check the tweak compatibility of the project with the given root. +/// The project is compatible if: +/// - the project's storage layout is the same as the original contract. +/// +/// If the project is not compatible, an error is returned. +pub fn check_storage_compatibility(cloned_project: &ClonedProject) -> eyre::Result<()> { + // to check the storage layout compatibility, we need to download the original contract's code + // from etherscan and compile. + let original_layout = cloned_project.metadata.storage_layout.to_owned(); + let current_layout = cloned_project + .main_artifact()? + .storage_layout + .to_owned() + .expect("storage layout is missing"); + check_storage_layout_compatibility(&original_layout, ¤t_layout) +} + +/// Check that the current storage layout is compatible with the original storage layout. +/// Each state variable in the original storage layout should have the same slot in the current +/// storage layout. +pub fn check_storage_layout_compatibility( + original: &StorageLayout, + current: &StorageLayout, +) -> eyre::Result<()> { + // TODO: need a more sophisticated comparison algorithm. + // The current implementation is a naive one that only checks the slot and offset of each + // storage variables. check storage variables + let current_storage_var_map = BTreeMap::from_iter( + current.storage.iter().map(|v| (format!("{}:{}", v.contract, v.label), v)), + ); + for original_var in original.storage.iter() { + let current_var = current_storage_var_map + .get(&format!("{}:{}", original_var.contract, original_var.label)); + if current_var.is_none() { + return Err(eyre::eyre!( + "the storage variable {} is missing in the current contract", + original_var.label + )); + } + let current_var = current_var.unwrap(); + // offset, slot, type should be the same + if original_var.offset != current_var.offset || original_var.slot != current_var.slot { + return Err(eyre::eyre!( + "the storage variable {} has different layout in the current contract", + original_var.label + )); + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use foundry_compilers::artifacts::StorageLayout; + + fn load_json_layout(json_str: &str) -> StorageLayout { + serde_json::from_str(json_str).unwrap() + } + + #[test] + pub fn test_same_storage_layout() { + let storage = load_json_layout( + r#"{ + "storage": [ + { + "astId": 3, + "contract": "contract.sol:C", + "label": "data", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 5, + "contract": "contract.sol:C", + "label": "owner", + "offset": 0, + "slot": "1", + "type": "t_address" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }"#, + ); + + assert!(super::check_storage_layout_compatibility(&storage, &storage).is_ok()); + } + + #[test] + pub fn test_storage_layout_shift() { + let original = load_json_layout( + r#"{ + "storage": [ + { + "astId": 3, + "contract": "contract.sol:C", + "label": "data", + "offset": 0, + "slot": "0", + "type": "t_uint256" + } + ], + "types": { + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }"#, + ); + let current = load_json_layout( + r#"{ + "storage": [ + { + "astId": 3, + "contract": "contract.sol:C", + "label": "data", + "offset": 0, + "slot": "1", + "type": "t_uint256" + } + ], + "types": { + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }"#, + ); + assert!(super::check_storage_layout_compatibility(&original, ¤t).is_err()); + } + + #[test] + pub fn test_storage_layout_different_type() { + let original = load_json_layout( + r#"{ + "storage": [ + { + "astId": 3, + "contract": "contract.sol:C", + "label": "data", + "offset": 0, + "slot": "0", + "type": "t_uint256" + } + ], + "types": { + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }"#, + ); + let current = load_json_layout( + r#"{ + "storage": [ + { + "astId": 3, + "contract": "contract.sol:C", + "label": "data", + "offset": 0, + "slot": "1", + "type": "t_address" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + } + }"#, + ); + assert!(super::check_storage_layout_compatibility(&original, ¤t).is_err()); + } + + #[test] + pub fn test_storage_layout_additional() { + let original = load_json_layout( + r#"{ + "storage": [ + { + "astId": 3, + "contract": "contract.sol:C", + "label": "data", + "offset": 0, + "slot": "0", + "type": "t_uint256" + } + ], + "types": { + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }"#, + ); + let current = load_json_layout( + r#"{ + "storage": [ + { + "astId": 3, + "contract": "contract.sol:C", + "label": "data", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 5, + "contract": "contract.sol:C", + "label": "owner", + "offset": 0, + "slot": "1", + "type": "t_address" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }"#, + ); + assert!(super::check_storage_layout_compatibility(&original, ¤t).is_ok()); + } + #[test] + pub fn test_storage_layout_struct() { + let original = load_json_layout( + r#"{ + "storage": [ + { + "astId": 1039, + "contract": "src/BatchSwap.sol:BatchSwap", + "label": "payment", + "offset": 0, + "slot": "14", + "type": "t_struct(paymentStruct)1028_storage" + } + ], + "types": { + "t_struct(paymentStruct)1028_storage": { + "encoding": "inplace", + "key": null, + "label": "struct BatchSwap.paymentStruct", + "numberOfBytes": "64", + "value": null, + "other": { + "members": [ + { + "astId": 1025, + "contract": "src/BatchSwap.sol:BatchSwap", + "label": "status", + "offset": "0", + "slot": "0", + "type": "t_bool" + }, + { + "astId": 1027, + "contract": "src/BatchSwap.sol:BatchSwap", + "label": "value", + "offset": "0", + "slot": "1", + "type": "t_uint256" + } + ] + } + } + } + }"#, + ); + let current = load_json_layout( + r#"{ + "storage": [ + { + "astId": 1039, + "contract": "src/BatchSwap.sol:BatchSwap", + "label": "payment", + "offset": 0, + "slot": "14", + "type": "t_struct(paymentStruct)666_storage" + } + ], + "types": { + "t_struct(paymentStruct)666_storage": { + "encoding": "inplace", + "key": null, + "label": "struct BatchSwap.paymentStruct", + "numberOfBytes": "64", + "value": null, + "other": { + "members": [ + { + "astId": 663, + "contract": "src/BatchSwap.sol:BatchSwap", + "label": "status", + "offset": "0", + "slot": "0", + "type": "t_bool" + }, + { + "astId": 664, + "contract": "src/BatchSwap.sol:BatchSwap", + "label": "value", + "offset": "0", + "slot": "1", + "type": "t_uint256" + } + ] + } + } + } + }"#, + ); + assert!(super::check_storage_layout_compatibility(&original, ¤t).is_ok()); + } +} diff --git a/crates/tweak/src/constants.rs b/crates/tweak/src/constants.rs new file mode 100644 index 000000000000..792d5d015d18 --- /dev/null +++ b/crates/tweak/src/constants.rs @@ -0,0 +1,45 @@ +//! Constant values used during tweaking the code. + +use alloy_primitives::Address; +use foundry_config::NamedChain; + +/// Non-standard precompiled contracts +pub enum NonStandardPrecompiled { + BinanceSmartChain([&'static str; 11]), +} + +impl NonStandardPrecompiled { + /// Binance Smart Chain non-standard precompiled contracts + /// Collected from + pub const BSC_NON_STANDARD_PRECOMPILED: NonStandardPrecompiled = + NonStandardPrecompiled::BinanceSmartChain([ + "0x0000000000000000000000000000000000001000", + "0x0000000000000000000000000000000000001001", + "0x0000000000000000000000000000000000001002", + "0x0000000000000000000000000000000000001003", + "0x0000000000000000000000000000000000001004", + "0x0000000000000000000000000000000000001005", + "0x0000000000000000000000000000000000001006", + "0x0000000000000000000000000000000000001007", + "0x0000000000000000000000000000000000001008", + "0x0000000000000000000000000000000000002000", + "0x0000000000000000000000000000000000002001", + ]); + + pub fn get_addresses(&self) -> Vec
{ + match self { + NonStandardPrecompiled::BinanceSmartChain(addresses) => { + addresses.iter().map(|a| a.parse().expect("incorrect address")).collect() + } + } + } + + pub fn get_precomiled_address(chain_id: NamedChain) -> Option> { + match chain_id { + NamedChain::BinanceSmartChain | NamedChain::BinanceSmartChainTestnet => { + Some(Self::BSC_NON_STANDARD_PRECOMPILED.get_addresses()) + } + _ => None, + } + } +} diff --git a/crates/tweak/src/lib.rs b/crates/tweak/src/lib.rs new file mode 100644 index 000000000000..de27570fada4 --- /dev/null +++ b/crates/tweak/src/lib.rs @@ -0,0 +1,76 @@ +pub mod code; +pub mod compatibility; +mod constants; +mod metadata; + +use std::collections::BTreeMap; + +use alloy_primitives::{keccak256, Address, Bytes, B256}; +use eyre::Result; + +use foundry_cli::opts::RpcOpts; +use foundry_evm::{backend::Backend, fork::CreateFork}; +pub use metadata::ClonedProject; +use revm::{ + primitives::{Bytecode, KECCAK_EMPTY}, + Database, +}; + +pub type TweakData = BTreeMap; + +pub async fn build_tweak_data( + projects: &Vec, + rpc: &RpcOpts, + quick: bool, +) -> Result { + let mut tweak_data = BTreeMap::new(); + for project in projects { + let metadata = &project.metadata; + let address = metadata.address; + let code = project.tweaked_code(rpc, quick).await?; + tweak_data.insert(address, code); + } + Ok(tweak_data) +} + +pub fn build_tweaked_backend(fork: Option, tweak_data: &TweakData) -> Result { + let mut backend = Backend::spawn(fork); + for (address, code) in tweak_data { + tweak_backend_once(&mut backend, *address, code.clone())?; + } + Ok(backend) +} + +/// Tweak the code of a contract in the blockchain backend. +pub fn tweak_backend_once( + backend: &mut Backend, + tweak_address: Address, + tweaked_code: Bytes, +) -> Result<()> { + let mut info = backend.basic(tweak_address)?.unwrap_or_default(); + let code_hash = if tweaked_code.as_ref().is_empty() { + KECCAK_EMPTY + } else { + B256::from_slice(&keccak256(tweaked_code.as_ref())[..]) + }; + info.code_hash = code_hash; + info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(tweaked_code.0))); + backend.insert_account_info(tweak_address, info); + Ok(()) +} + +pub fn tweak_backend(backend: &mut Backend, tweak_data: &TweakData) -> Result<()> { + for (tweak_address, tweaked_code) in tweak_data { + let mut info = backend.basic(*tweak_address)?.unwrap_or_default(); + let code_hash = if tweaked_code.as_ref().is_empty() { + revm::primitives::KECCAK_EMPTY + } else { + B256::from_slice(&alloy_primitives::keccak256(tweaked_code.as_ref())[..]) + }; + info.code_hash = code_hash; + info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(tweaked_code.clone().0))); + backend.insert_account_info(*tweak_address, info); + } + + Ok(()) +} diff --git a/crates/tweak/src/metadata.rs b/crates/tweak/src/metadata.rs new file mode 100644 index 000000000000..b6498a68e76f --- /dev/null +++ b/crates/tweak/src/metadata.rs @@ -0,0 +1,187 @@ +use std::{ + path::PathBuf, + sync::{Arc, Mutex}, +}; + +use alloy_primitives::{Address, Bytes, ChainId, TxHash}; +use eyre::{eyre, Result}; +use foundry_cli::opts::RpcOpts; +use foundry_common::compile::ProjectCompiler; +use foundry_compilers::{ + artifacts::{ + output_selection::ContractOutputSelection, ConfigurableContractArtifact, StorageLayout, + }, + ProjectCompileOutput, +}; +use foundry_config::Config; + +/// ClonedProject represents a foundry project that is cloned by the `forge clone` command. +/// It couples with an on-chain contract instance. +/// Users may modify the source code of the cloned project, but the storage layout should remain the +/// same as the original contract. The cloned project will be used to tweak the on-chain contract. +#[derive(Debug, Clone, Default)] +pub struct ClonedProject { + pub root: PathBuf, + pub config: Config, + pub metadata: CloneMetadata, + + // cache + pub(crate) _compile_output: Arc>>, + pub(crate) _main_artifact: Arc>>, +} + +impl PartialEq for ClonedProject { + fn eq(&self, other: &Self) -> bool { + self.root == other.root + } +} + +impl Eq for ClonedProject {} + +impl PartialOrd for ClonedProject { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ClonedProject { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.root.cmp(&other.root) + } +} + +impl ClonedProject { + fn set_cache(cache: Arc>>, compile_output: T) { + *cache.clone().lock().unwrap() = Some(compile_output); + } + fn get_cache(cache: Arc>>) -> T { + // cache.clone().lock().unwrap().unwrap() + let lock_result = cache.lock().expect("Failed to lock the cache"); + (*lock_result).clone().expect("Value not present") + } + fn is_cached(cache: Arc>>) -> bool { + cache.clone().lock().unwrap().is_some() + } + + /// Load the cloned project from the root directory of the project. + /// If the clone metadata file does not exist, an error is returned. + /// The root should be an absolute path. + pub fn load_with_root(root: impl Into) -> Result { + let root = root.into(); + assert!(root.is_absolute()); + let cwd = std::env::current_dir()?; + std::env::set_current_dir(&root)?; + let config = Config::load_with_root(&root); + std::env::set_current_dir(cwd)?; + let metadata = CloneMetadata::load_with_root(&root)?; + Ok(ClonedProject { + root, + config, + metadata, + _compile_output: Default::default(), + _main_artifact: Default::default(), + }) + } + + /// Compile the project and return the artifacts. + /// The compile output is cached. + /// A workaround for the insufficient implementation of Config::load_with_root. + pub fn compile_safe(&self) -> Result { + // check the cache + if Self::is_cached(self._compile_output.clone()) { + return Ok(Self::get_cache(self._compile_output.clone())); + } + + // load the foundry config + // XXX (ZZ): some insufficient implementation of Config::project_paths(). It depends on the + // current working directory, preventiong us from invoking this function directly + let cwd = std::env::current_dir()?; + std::env::set_current_dir(&self.root)?; + + // compile the project to get the current artifacts + let mut config = self.config.clone(); + config.extra_output.push(ContractOutputSelection::StorageLayout); + let project = config.project()?; + let output = ProjectCompiler::new().compile(&project)?; + + std::env::set_current_dir(cwd)?; + + // cache the output + Self::set_cache(self._compile_output.clone(), output); + Ok(Self::get_cache(self._compile_output.clone())) + } + + /// Get the artifact of the main contract of the project. + pub fn main_artifact(&self) -> Result { + // check the cache + if Self::is_cached(self._main_artifact.clone()) { + return Ok(Self::get_cache(self._main_artifact.clone())); + } + + let output = self.compile_safe()?; + let (_, _, artifact) = output + .artifacts_with_files() + .find(|(_, contract_name, _)| **contract_name == self.metadata.target_contract) + .ok_or_else(|| { + eyre!("the contract {} is not found in the project", self.metadata.target_contract) + })?; + + // cache the artifact + Self::set_cache(self._main_artifact.clone(), artifact.clone()); + Ok(Self::get_cache(self._main_artifact.clone())) + } + + /// Get the tweaked code of the main contract of the project. + pub async fn tweaked_code(&self, rpc: &RpcOpts, quick: bool) -> Result { + // check chain id + if self.config.chain.unwrap_or_default().id() != self.metadata.chain_id { + return Err(eyre!( + "the chain id of the project ({}) is different from the chain id of the on-chain contract ({})", + self.config.chain.unwrap_or_default().id(), + self.metadata.chain_id + )); + } + // check the storage compatibility + super::compatibility::check_storage_compatibility(self)?; + + // get tweaked code + let code = super::code::generate_tweaked_code(rpc, self, quick).await?; + Ok(code) + } +} + +/// CloneMetadata stores the metadata that are not included by `foundry.toml` but necessary for a +/// cloned contract. This struct is the twin of the `CloneMetadata` struct in the `clone` command of +/// `forge` crate. +#[derive(Debug, Clone, Default, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CloneMetadata { + /// The path to the source file that contains the contract declaration. + /// The path is relative to the root directory of the project. + pub path: PathBuf, + /// The name of the contract in the file. + pub target_contract: String, + /// The address of the contract on the blockchian. + pub address: Address, + /// The chain id. + pub chain_id: ChainId, + /// The transaction hash of the creation transaction. + pub creation_transaction: TxHash, + /// The address of the deployer (caller of the CREATE/CREATE2). + pub deployer: Address, + /// The constructor arguments of the contract. + pub constructor_arguments: Bytes, + /// The storage layout of the contract. + pub storage_layout: StorageLayout, +} + +impl CloneMetadata { + /// Load the metadata from the `clone.toml` file in the root directory of the project. + /// If the file does not exist, an error is returned. + pub fn load_with_root(root: impl Into) -> Result { + let path = root.into().join(".clone.meta"); + let metadata = std::fs::read_to_string(path)?; + let metadata = serde_json::from_str(&metadata)?; + Ok(metadata) + } +}