├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "eth-zero-finder" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "rust-crypto", 10 | ] 11 | 12 | [[package]] 13 | name = "fuchsia-cprng" 14 | version = "0.1.1" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 17 | 18 | [[package]] 19 | name = "gcc" 20 | version = "0.3.55" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" 23 | 24 | [[package]] 25 | name = "libc" 26 | version = "0.2.121" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" 29 | 30 | [[package]] 31 | name = "rand" 32 | version = "0.3.23" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" 35 | dependencies = [ 36 | "libc", 37 | "rand 0.4.6", 38 | ] 39 | 40 | [[package]] 41 | name = "rand" 42 | version = "0.4.6" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 45 | dependencies = [ 46 | "fuchsia-cprng", 47 | "libc", 48 | "rand_core 0.3.1", 49 | "rdrand", 50 | "winapi", 51 | ] 52 | 53 | [[package]] 54 | name = "rand_core" 55 | version = "0.3.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 58 | dependencies = [ 59 | "rand_core 0.4.2", 60 | ] 61 | 62 | [[package]] 63 | name = "rand_core" 64 | version = "0.4.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 67 | 68 | [[package]] 69 | name = "rdrand" 70 | version = "0.4.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 73 | dependencies = [ 74 | "rand_core 0.3.1", 75 | ] 76 | 77 | [[package]] 78 | name = "rust-crypto" 79 | version = "0.2.36" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" 82 | dependencies = [ 83 | "gcc", 84 | "libc", 85 | "rand 0.3.23", 86 | "rustc-serialize", 87 | "time", 88 | ] 89 | 90 | [[package]] 91 | name = "rustc-serialize" 92 | version = "0.3.24" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 95 | 96 | [[package]] 97 | name = "time" 98 | version = "0.1.44" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 101 | dependencies = [ 102 | "libc", 103 | "wasi", 104 | "winapi", 105 | ] 106 | 107 | [[package]] 108 | name = "wasi" 109 | version = "0.10.0+wasi-snapshot-preview1" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 112 | 113 | [[package]] 114 | name = "winapi" 115 | version = "0.3.9" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 118 | dependencies = [ 119 | "winapi-i686-pc-windows-gnu", 120 | "winapi-x86_64-pc-windows-gnu", 121 | ] 122 | 123 | [[package]] 124 | name = "winapi-i686-pc-windows-gnu" 125 | version = "0.4.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 128 | 129 | [[package]] 130 | name = "winapi-x86_64-pc-windows-gnu" 131 | version = "0.4.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 134 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "eth-zero-finder" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rust-crypto = "0.2" 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Functions are more efficient in solidity when their function selector has mostly zeros. 2 | 3 | For example 4 | 5 | `deposit278591A(uint256)` has a function selector of `00000070` 6 | but `deposit(uint256)` has a function selector of `b6b55f25` 7 | 8 | The function selector is computed as the first 4 bytes of the keccak256 of the function signature. The function signature excludes the variable names. For example, `sendValue(uint256 amount)` is not correct but `sendValue(uin256)` is. 9 | 10 | The gas cost of a function name is 4 times the number of zero bytes, and 16 times the number of nonzero bytes. So in the worst case, the gas cost is 64 gas (4 non-zero bytes), and in the best case 28 gas (3 zero and 1 non-zero). An all zero function selector won't compile because that conflicts with the fallback function. Thus, `mint_22F5A30(uint256)` is more efficient than `mint(uint256)` 11 | 12 | If the zeros are leading zeros, this will make the contract smaller and save gas on deployment. 13 | 14 | ## Install Rust 15 | [https://www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) 16 | 17 | ## Build 18 | `cargo build --release` 19 | 20 | ## Run 21 | `./target/release/eth-zero-finder "myFunctionName?(uint,address,...)" (true|false)` 22 | 23 | The question mark will be substituted with a hex number found by searching. Your function should follow the format for solidity function selectors. Eg: No arguments: "myFunction()". One address argument: "myFunction(address)". Two arguments: "anotherFunction(uint256,address)" etc. 24 | 25 | Num threads is the number of threads to use. The final true false is if you want to force the zeros to be leading zeros. Leading zeros lead to a lower gas cost on deployment. 26 | 27 | The search usually finds a solution in less than 30 seconds on a macbook pro with 8 threads. 28 | 29 | Example run: 30 | 31 | `./target/release/eth-zero-finder "withdraw_?()" 8 true` 32 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate crypto; 2 | 3 | use self::crypto::digest::Digest; 4 | use self::crypto::sha3::Sha3; 5 | use std::env; 6 | use std::process; 7 | use std::thread; 8 | 9 | fn num_zeros(result: &String, leading: &bool) -> i32 { 10 | let mut counter = 0; 11 | for i in (0..8).step_by(2) { 12 | if &result[i..(i+2)] == "00" { 13 | counter += 1; 14 | } 15 | else { 16 | if *leading { 17 | break; 18 | } 19 | } 20 | } 21 | return counter; 22 | } 23 | 24 | fn search(start: usize, step: &usize, function_name: &str, leading: &bool) { 25 | let mut best = 0; 26 | for i in (start..1000000000).step_by(*step) { 27 | let hex_num = format!("{:X}", i); 28 | let candidate = function_name.replace("?", &hex_num); 29 | let mut hasher = Sha3::keccak256(); 30 | 31 | hasher.input_str(&candidate); 32 | let hash_result = hasher.result_str(); 33 | let num_zero_bytes = num_zeros(&hash_result, &leading); 34 | if num_zero_bytes == 4 { 35 | continue; 36 | } 37 | if num_zero_bytes > best { 38 | best = num_zero_bytes; 39 | println!("{} {} {}", candidate, &hash_result[..8], &num_zero_bytes); 40 | } 41 | 42 | if num_zero_bytes == 3 { 43 | return; 44 | } 45 | } 46 | } 47 | 48 | fn main() { 49 | let args: Vec = env::args().collect(); 50 | if args.len() != 4 { 51 | println!("Wrong number of arguments"); 52 | process::exit(1); 53 | } 54 | let funn = &args[1]; 55 | let nt = match args[2].parse::() { 56 | Ok(i) => i, 57 | Err(_e) => 1, 58 | }; 59 | let num_threads = nt.to_owned(); 60 | let leading_arg = &args[3]; 61 | 62 | if !(leading_arg == "true" || leading_arg == "false") { 63 | println!("must specify if zero bytes are required to be leading bytes (true|false)"); 64 | process::exit(1); 65 | } 66 | let leading = if leading_arg == "true" { true } else { false }; 67 | 68 | let mut thread_handles: Vec> = Vec::new(); 69 | for i in 0..num_threads { 70 | let function_name = funn.to_owned(); 71 | let handle = thread::spawn(move || { 72 | search(i, &num_threads, &function_name, &leading); 73 | }); 74 | thread_handles.push(handle); 75 | } 76 | 77 | for handle in thread_handles { 78 | handle.join().unwrap() 79 | } 80 | } 81 | --------------------------------------------------------------------------------