From adb900f96e1493da63a722da0cb361e4cbe96229 Mon Sep 17 00:00:00 2001 From: Marcin M <128217157+mm-zk@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:43:50 +0200 Subject: [PATCH] feat: Allow bytecode replacement (#336) * feat: add option to replace bytecodes * apply earlier * updated comments * cleaned up error handling * clippy --- README.md | 13 + ...0BF5CE80Ba4ED8C3A18E03567C40d04B05358.json | 251 ++++++++++++++++++ example_override/README.md | 6 + src/bytecode_override.rs | 56 ++++ src/config/cli.rs | 3 + src/main.rs | 6 + src/node/in_memory.rs | 27 +- 7 files changed, 358 insertions(+), 4 deletions(-) create mode 100644 example_override/0x8340BF5CE80Ba4ED8C3A18E03567C40d04B05358.json create mode 100644 example_override/README.md create mode 100644 src/bytecode_override.rs diff --git a/README.md b/README.md index 9949ca5d..d2c044ac 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,19 @@ If you wish to replay a remote transaction locally for deep debugging, use the f era_test_node replay_tx ``` +## Replacing bytecodes + +You can also replace / override the contract bytecode with the local version. This is especially useful if you are replaying some mainnet transactions and would like to see how they would behave on the different bytecode. Or when you want to fork mainnet to see how your code would +behave on mainnet state. + +You have to prepare a directory, with files in format `0xabc..93f.json` that contain the json outputs that you can get from zkout directories from your compiler. + +Then you have to add `--override-bytecodes-dir=XX` flag to point at that directory. See the `example_override` dir for more details. + +```bash +cargo run -- --override-bytecodes-dir=example_override --show-storage-logs all fork mainnet +``` + ## 📞 Sending Network Calls You can send network calls against a running `era-test-node`. For example, to check the testnet LINK balance or mainnet USDT, use `curl` or `foundry-zksync`. diff --git a/example_override/0x8340BF5CE80Ba4ED8C3A18E03567C40d04B05358.json b/example_override/0x8340BF5CE80Ba4ED8C3A18E03567C40d04B05358.json new file mode 100644 index 00000000..29abfb31 --- /dev/null +++ b/example_override/0x8340BF5CE80Ba4ED8C3A18E03567C40d04B05358.json @@ -0,0 +1,251 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "allowance", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + }, + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "name": "_spender", + "type": "address", + "internalType": "address" + }, + { + "name": "_value", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "success", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "increment", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "number", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setNumber", + "inputs": [ + { + "name": "newNumber", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { + "name": "_to", + "type": "address", + "internalType": "address" + }, + { + "name": "_value", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "success", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "name": "_from", + "type": "address", + "internalType": "address" + }, + { + "name": "_to", + "type": "address", + "internalType": "address" + }, + { + "name": "_value", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "success", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + } + ], + "bytecode": { + "object": "00030000000000020000008003000039000000400030043f00000001002001900000001e0000c13d000000000201001900000060022002700000007d02200197000000040020008c0000013b0000413d000000000301043b000000e003300270000000800030009c000000350000213d000000870030009c000000520000a13d000000880030009c000000660000613d000000890030009c000000ae0000613d0000008a0030009c0000013b0000c13d000000240020008c0000013b0000413d0000000002000416000000000002004b0000013b0000c13d0000000401100370000000000101043b0000013d0000013d0000000001000416000000000001004b0000013b0000c13d0000000101000039000000000201041a0000009b0220019700000012022001bf000000000021041b0000007e020000410000000201000039000000000021041b000000000100041100000000001004350000000301000039000000200010043f01ee01d70000040f0000007e02000041000000000021041b0000002001000039000001000010044300000120000004430000007f01000041000001ef0001042e000000810030009c0000005b0000a13d000000820030009c000000b70000613d000000830030009c000000e60000613d000000840030009c0000013b0000c13d000000440020008c0000013b0000413d0000000002000416000000000002004b0000013b0000c13d0000000402100370000000000202043b0000008d0020009c0000013b0000213d0000002401100370000000000101043b0000008d0010009c0000013b0000213d0000000000200435000300000001001d0000000401000039000000200010043f01ee01d70000040f00000003020000290000000000200435000001420000013d0000008b0030009c000000f20000613d0000008c0030009c0000013b0000c13d0000000001000416000000000001004b0000013b0000c13d0000000201000039000001440000013d000000850030009c000001320000613d000000860030009c0000013b0000c13d0000000001000416000000000001004b0000013b0000c13d000000000100041a000000800010043f0000008e01000041000001ef0001042e000000640020008c0000013b0000413d0000000002000416000000000002004b0000013b0000c13d0000000402100370000000000302043b0000008d0030009c0000013b0000213d0000002402100370000000000202043b000200000002001d0000008d0020009c0000013b0000213d0000004401100370000000000101043b000100000001001d00000000003004350000000301000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c70000801002000039000300000003001d01ee01e90000040f000000030300002900000001002001900000013b0000613d000000000101043b000000000101041a000000010010006c000000d50000413d00000000003004350000000402000039000000200020043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b00000000020004110000000000200435000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f000000030300002900000001002001900000013b0000613d000000000101043b000000000101041a000000010010006c0000017d0000813d000000400100043d00000044021000390000009403000041000000000032043500000024021000390000001203000039000000db0000013d0000000001000416000000000001004b0000013b0000c13d0000000101000039000000000101041a000000ff0110018f000000800010043f0000008e01000041000001ef0001042e000000440020008c0000013b0000413d0000000002000416000000000002004b0000013b0000c13d0000000402100370000000000202043b000300000002001d0000008d0020009c0000013b0000213d0000002401100370000000000101043b000200000001001d000000000100041100000000001004350000000301000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b000000000101041a000000020010006c000001480000813d000000400100043d000000440210003900000097030000410000000000320435000000240210003900000014030000390000000000320435000000950200004100000000002104350000000402100039000000200300003900000000003204350000007d0010009c0000007d01008041000000400110021000000096011001c7000001f0000104300000000001000416000000000001004b0000013b0000c13d000000000100041a000000010110003a0000013d0000c13d000000920100004100000000001004350000001101000039000000040010043f0000009301000041000001f000010430000000440020008c0000013b0000413d0000000002000416000000000002004b0000013b0000c13d0000000402100370000000000202043b000300000002001d0000008d0020009c0000013b0000213d0000002401100370000000000101043b000200000001001d000000000100041100000000001004350000000401000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b00000003020000290000000000200435000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b0000000202000029000000000021041b000000800020043f00000000010004140000007d0010009c0000007d01008041000000c00110021000000098011001c70000800d02000039000000030300003900000099040000410000000005000411000000030600002901ee01e40000040f00000001002001900000013b0000613d000000400100043d000000010200003900000000002104350000007d0010009c0000007d0100804100000040011002100000009a011001c7000001ef0001042e000000240020008c0000013b0000413d0000000002000416000000000002004b0000013b0000c13d0000000401100370000000000101043b0000008d0010009c000001400000a13d0000000001000019000001f000010430000000000010041b0000000001000019000001ef0001042e00000000001004350000000301000039000000200010043f01ee01d70000040f000000000101041a000000800010043f0000008e01000041000001ef0001042e000000000100041100000000001004350000000301000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b000000000201041a000000020220006c000000ec0000413d000000000021041b000000030100002900000000001004350000000301000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b000000000201041a0000000203000029000000000032001a000000ec0000413d0000000002320019000000000021041b000000400100043d00000000003104350000007d0010009c0000007d01008041000000400110021000000000020004140000007d0020009c0000007d02008041000000c002200210000000000112019f00000090011001c70000800d0200003900000003030000390000009104000041000001250000013d00000000003004350000000301000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f000000010020019000000001030000290000013b0000613d000000000101043b000000000201041a000000000232004b000000ec0000413d000000000021041b000000020100002900000000001004350000000301000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f000000010020019000000001030000290000013b0000613d000000000101043b000000000201041a000000000032001a000000ec0000413d0000000102200029000000000021041b000000030100002900000000001004350000000401000039000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b00000000020004110000000000200435000000200010043f00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f00000001002001900000013b0000613d000000000101043b000000000201041a000000010220006c000000ec0000413d000000000021041b000000400100043d000000010200002900000000002104350000007d0010009c0000007d01008041000000400110021000000000020004140000007d0020009c0000007d02008041000000c002200210000000000112019f00000090011001c70000800d02000039000000030300003900000091040000410000000305000029000000020600002901ee01e40000040f00000001002001900000012a0000c13d0000013b0000013d00000000010004140000007d0010009c0000007d01008041000000c0011002100000008f011001c7000080100200003901ee01e90000040f0000000100200190000001e20000613d000000000101043b000000000001042d0000000001000019000001f000010430000001e7002104210000000102000039000000000001042d0000000002000019000000000001042d000001ec002104230000000102000039000000000001042d0000000002000019000000000001042d000001ee00000432000001ef0001042e000001f00001043000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000056bc75e2d6310000000000002000000000000000000000000000000400000010000000000000000000000000000000000000000000000000000000000000000000000000070a0823000000000000000000000000000000000000000000000000000000000a9059cba00000000000000000000000000000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000d09de08a00000000000000000000000000000000000000000000000000000000dd62ed3e0000000000000000000000000000000000000000000000000000000070a08231000000000000000000000000000000000000000000000000000000008381f58a0000000000000000000000000000000000000000000000000000000023b872dc0000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000313ce567000000000000000000000000000000000000000000000000000000003fb5c1cb00000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000000000000000000000000000000000000018160ddd000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000002000000080000000000000000002000000000000000000000000000000000000400000000000000000000000000200000000000000000000000000000000000020000000000000000000000000ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef4e487b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000416c6c6f77616e6365206578636565646564000000000000000000000000000008c379a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000496e73756666696369656e742062616c616e636500000000000000000000000002000000000000000000000000000000000000200000008000000000000000008c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9250000000000000000000000000000000000000020000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00520781c411bbb044b01855813fe7b40104c56efcd9ba8229a0a14b7a855c54d7" + }, + "methodIdentifiers": {}, + "storageLayout": { + "storage": [], + "types": {} + }, + "userdoc": {}, + "devdoc": {}, + "hash": "0100009dc2f62e1e7c8a5e427d423113eaef083c064b5a38b4eab009712a7ef7", + "factoryDependencies": {}, + "id": 0 +} \ No newline at end of file diff --git a/example_override/README.md b/example_override/README.md new file mode 100644 index 00000000..02939393 --- /dev/null +++ b/example_override/README.md @@ -0,0 +1,6 @@ +# Example override directory + +This directory is an example on how to use the `override-bytecodes-dir` flag. + +You put your bytecodes in this directory, under a proper file name - that should match the address that you're overriding, and run +the era-test-node with `--override-bytecodes-dir=example_override` flag. \ No newline at end of file diff --git a/src/bytecode_override.rs b/src/bytecode_override.rs new file mode 100644 index 00000000..28badf5f --- /dev/null +++ b/src/bytecode_override.rs @@ -0,0 +1,56 @@ +use std::fs; + +use crate::fork::ForkSource; +use crate::node::InMemoryNode; +use eyre::Context; +use hex::FromHex; +use serde::Deserialize; +use std::str::FromStr; +use zksync_types::Address; + +#[derive(Debug, Deserialize)] +struct ContractJson { + bytecode: Bytecode, +} + +#[derive(Debug, Deserialize)] +struct Bytecode { + object: String, +} + +// Loads a list of bytecodes and addresses from the directory and then inserts them directly +// into the Node's storage. +pub fn override_bytecodes( + node: &InMemoryNode, + bytecodes_dir: String, +) -> eyre::Result<()> { + for entry in fs::read_dir(bytecodes_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let filename = match path.file_name().and_then(|name| name.to_str()) { + Some(name) => name, + None => eyre::bail!("Invalid filename {}", path.display().to_string()), + }; + + // Look only at .json files. + if let Some(filename) = filename.strip_suffix(".json") { + let address = Address::from_str(filename) + .wrap_err(format!("Cannot parse {} as address", filename))?; + + let file_content = fs::read_to_string(&path)?; + let contract: ContractJson = serde_json::from_str(&file_content) + .wrap_err(format!("Failed to parse json file {:?}", path))?; + + let bytecode = Vec::from_hex(contract.bytecode.object) + .wrap_err(format!("Failed to parse hex from {:?}", path))?; + + node.override_bytecode(&address, &bytecode) + .expect("Failed to override bytecode"); + tracing::info!("+++++ Replacing bytecode at address {:?} +++++", address); + } + } + } + Ok(()) +} diff --git a/src/config/cli.rs b/src/config/cli.rs index 49db0bb2..b6ddb85d 100644 --- a/src/config/cli.rs +++ b/src/config/cli.rs @@ -93,6 +93,9 @@ pub struct Cli { /// Cache directory location for `disk` cache - default: ".cache" #[arg(long)] pub cache_dir: Option, + + #[arg(long)] + pub override_bytecodes_dir: Option, } #[derive(Debug, Subcommand)] diff --git a/src/main.rs b/src/main.rs index 4f22d27c..6d5ef0bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use crate::observability::Observability; use anyhow::anyhow; +use bytecode_override::override_bytecodes; use clap::Parser; use colored::Colorize; use config::cli::{Cli, Command}; @@ -10,6 +11,7 @@ use logging_middleware::LoggingMiddleware; use tracing_subscriber::filter::LevelFilter; mod bootloader_debug; +mod bytecode_override; mod cache; mod config; mod console_log; @@ -171,6 +173,10 @@ async fn main() -> anyhow::Result<()> { let node: InMemoryNode = InMemoryNode::new(fork_details, Some(observability), config.node, config.gas); + if let Some(bytecodes_dir) = opt.override_bytecodes_dir { + override_bytecodes(&node, bytecodes_dir).unwrap(); + } + if !transactions_to_replay.is_empty() { let _ = node.apply_txs(transactions_to_replay); } diff --git a/src/node/in_memory.rs b/src/node/in_memory.rs index b3544932..5f86a127 100644 --- a/src/node/in_memory.rs +++ b/src/node/in_memory.rs @@ -60,16 +60,15 @@ use zksync_types::{ block::{unpack_block_info, L2BlockHasher}, fee::Fee, fee_model::{BatchFeeInput, PubdataIndependentBatchFeeModelInput}, - get_nonce_key, - l2::L2Tx, - l2::TransactionType, + get_code_key, get_nonce_key, + l2::{L2Tx, TransactionType}, utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance}, vm_trace::Call, PackedEthSignature, StorageKey, StorageLogQueryType, StorageValue, Transaction, ACCOUNT_CODE_STORAGE_ADDRESS, MAX_L2_TX_GAS_LIMIT, SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_BLOCK_INFO_POSITION, }; -use zksync_utils::{h256_to_account_address, h256_to_u256, u256_to_h256}; +use zksync_utils::{bytecode::hash_bytecode, h256_to_account_address, h256_to_u256, u256_to_h256}; use zksync_web3_decl::error::Web3Error; /// Max possible size of an ABI encoded tx (in bytes). @@ -1699,6 +1698,26 @@ impl InMemoryNode { Ok(()) } + + // Forcefully stores the given bytecode at a given account. + pub fn override_bytecode(&self, address: &Address, bytecode: &[u8]) -> Result<(), String> { + let mut inner = self + .inner + .write() + .map_err(|e| format!("Failed to acquire write lock: {}", e))?; + + let code_key = get_code_key(address); + + let bytecode_hash = hash_bytecode(bytecode); + + inner + .fork_storage + .store_factory_dep(bytecode_hash, bytecode.to_owned()); + + inner.fork_storage.set_value(code_key, bytecode_hash); + + Ok(()) + } } /// Keeps track of a block's batch number, miniblock number and timestamp.