diff --git a/Cargo.lock b/Cargo.lock index 17bf65c..29f255c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "alloy-primitives" version = "0.6.2" @@ -440,6 +449,7 @@ dependencies = [ "ocl", "rand", "rayon", + "rstest", "separator", "sha3", "terminal_size", @@ -722,6 +732,101 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -744,6 +849,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "group" version = "0.13.0" @@ -966,7 +1077,7 @@ version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1c3ce118fd2f00eeb3c01f8073db1ee127cac0b2f79848192c7889b2bd7fe40" dependencies = [ - "futures", + "futures 0.1.31", "nodrop", "num-traits 0.2.17", "ocl-core", @@ -1048,6 +1159,18 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkcs8" version = "0.10.2" @@ -1160,7 +1283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda4a51ba3d773c196f9450a6b239077ad8dda608b15263b4c9f29e58909883f" dependencies = [ "crossbeam", - "futures", + "futures 0.1.31", ] [[package]] @@ -1237,12 +1360,41 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "relative-path" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" + [[package]] name = "rfc6979" version = "0.4.0" @@ -1263,6 +1415,35 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures 0.3.30", + "futures-timer", + "rstest_macros", + "rustc_version 0.4.0", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.0", + "syn 2.0.48", + "unicode-ident", +] + [[package]] name = "ruint" version = "1.11.1" @@ -1447,6 +1628,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "spki" version = "0.7.3" diff --git a/Cargo.toml b/Cargo.toml index 933adfa..560a421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,6 @@ separator = "0.4.1" sha3 = "0.10.8" terminal_size = "0.3.0" tiny-keccak = "2.0.2" + +[dev-dependencies] +rstest = "0.18.2" diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..b1ac3fd --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,472 @@ +use alloy_primitives::hex::{decode, encode}; +use alloy_primitives::FixedBytes; +use byteorder::{ByteOrder, LittleEndian}; +use createxcrunch::{mk_kernel_src, Config, CreateXVariant, RewardVariant, SaltVariant}; +use itertools::chain; +use ocl::{Buffer, Context, Device, MemFlags, Platform, ProQue, Program, Queue}; +use rstest::*; + +#[fixture] +fn try_nonce( + #[default(SaltVariant::Random)] salt_variant: SaltVariant, + #[default(CreateXVariant::Create3)] create_variant: CreateXVariant, + #[default(RewardVariant::LeadingZeros { zeros_threshold: 1 })] reward: RewardVariant, + #[default([0; 1])] nonce: [u32; 1], +) -> ocl::Result { + let config = Config { + gpu_device: 0, + // 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed + factory_address: [ + 186, 94, 208, 153, 99, 61, 59, 49, 62, 77, 95, 123, 220, 19, 5, 211, 194, 139, 165, 237, + ], + salt_variant, + create_variant, + reward, + // This field will be ignored for tests + output: "output.txt", + }; + // set up a platform to use + let platform = Platform::new(ocl::core::default_platform()?); + + let device = Device::by_idx_wrap(platform, config.gpu_device as usize)?; + + // set up the context to use + let context = Context::builder() + .platform(platform) + .devices(device) + .build()?; + + let program = Program::builder() + .devices(device) + .src(mk_kernel_src(&config)) + .build(&context)?; + + // set up the queue to use + let queue = Queue::new(&context, device, None)?; + + // set up the "proqueue" (or amalgamation of various elements) to use + let ocl_pq = ProQue::new(context, queue, program, Some(1)); + + // construct the 4-byte message to hash, leaving last 8 of salt empty + let salt = FixedBytes::<4>::try_from(&[0u8; 4]).unwrap(); + + // build a corresponding buffer for passing the message to the kernel + let message_buffer = Buffer::builder() + .queue(ocl_pq.queue().clone()) + .flags(MemFlags::new().read_only()) + .len(4) + .copy_host_slice(&salt[..]) + .build()?; + + // reset nonce & create a buffer to view it in little-endian + // for more uniformly distributed nonces, we shall initialize it to a random value + let mut view_buf = [0; 8]; + + LittleEndian::write_u64(&mut view_buf, (nonce[0] as u64) << 32); + + // build a corresponding buffer for passing the nonce to the kernel + let nonce_buffer = Buffer::builder() + .queue(ocl_pq.queue().clone()) + .flags(MemFlags::new().read_only()) + .len(1) + .copy_host_slice(&nonce) + .build()?; + + // establish a buffer for nonces that result in desired addresses + let mut solutions: Vec = vec![0; 4]; + let solutions_buffer = Buffer::builder() + .queue(ocl_pq.queue().clone()) + .flags(MemFlags::new().write_only()) + .len(4) + .copy_host_slice(&solutions) + .build()?; + + // build the kernel and define the type of each buffer + let kern = ocl_pq + .kernel_builder("hashMessage") + .arg_named("message", None::<&Buffer>) + .arg_named("nonce", None::<&Buffer>) + .arg_named("solutions", None::<&Buffer>) + .build()?; + + // set each buffer + kern.set_arg("message", Some(&message_buffer))?; + kern.set_arg("nonce", Some(&nonce_buffer))?; + kern.set_arg("solutions", &solutions_buffer)?; + + let global_work_size = [1, 1, 1]; // This effectively sets get_global_id(0) to 0 for a single work item + + // enqueue the kernel + unsafe { + kern.cmd().global_work_size(global_work_size).enq()?; + } + + // read the solutions from the device + solutions_buffer.read(&mut solutions).enq()?; + + let solution = solutions[0]; + let solution = solution.to_le_bytes(); + + println!("Solution: {:?}", solution); + + let mined_salt = chain!(salt, solution[..7].iter().copied()); + + let salt: Vec = match config.salt_variant { + SaltVariant::CrosschainSender { + chain_id: _, + calling_address, + } => chain!(calling_address, [1u8], mined_salt).collect(), + SaltVariant::Crosschain { chain_id: _ } => chain!([0u8; 20], [1u8], mined_salt).collect(), + SaltVariant::Sender { calling_address } => { + chain!(calling_address, [0u8], mined_salt).collect() + } + SaltVariant::Random => chain!(mined_salt, [0u8; 21]).collect(), + }; + + println!("Salt: {:?}", salt); + + // get the address that results from the hash + let mut address = encode( + solutions[1] + .to_be_bytes() + .into_iter() + .chain(solutions[2].to_be_bytes()) + .chain(solutions[3].to_be_bytes()[..4].to_vec()) + .collect::>(), + ); + + address.insert_str(0, "0x"); + + Ok(address) +} + +#[rstest] +fn test_create3_random() { + let address = try_nonce( + SaltVariant::Random, + CreateXVariant::Create3, + RewardVariant::LeadingZeros { zeros_threshold: 1 }, + [61u32; 1], + ) + .unwrap(); + + assert_eq!("0x00945498be46467fee556bf2f2f3dcfbd1a6765a", address); + + let address = try_nonce( + SaltVariant::Random, + CreateXVariant::Create3, + RewardVariant::TotalZeros { zeros_threshold: 2 }, + [357u32; 1], + ) + .unwrap(); + + assert_eq!("0x4c788c0e302910a2c95a000684d47d2d00591809", address); + + let address = try_nonce( + SaltVariant::Random, + CreateXVariant::Create3, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 1, + total_zeros_threshold: 2, + }, + [61u32; 1], + ) + .unwrap(); + + assert_eq!("0x00945498be46467fee556bf2f2f3dcfbd1a6765a", address); + + let address = try_nonce( + SaltVariant::Random, + CreateXVariant::Create3, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 5, + total_zeros_threshold: 2, + }, + [357u32; 1], + ) + .unwrap(); + + assert_eq!("0x4c788c0e302910a2c95a000684d47d2d00591809", address); + + let address = try_nonce( + SaltVariant::Random, + CreateXVariant::Create3, + RewardVariant::Matching { + pattern: "bbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + .to_owned() + .into_boxed_str(), + }, + [87u32; 1], + ) + .unwrap(); + + assert_eq!("0xbb10c35fdadda68390f7f58b4378ad07826a5471", address); +} + +#[rstest] +fn test_create3_caller() { + let calling_address = string_to_addr_bytes("0x34A50a7A272E86EE30b7A74E36f3f02AF18B1eB5"); + + let address = try_nonce( + SaltVariant::Sender { calling_address }, + CreateXVariant::Create3, + RewardVariant::LeadingZeros { zeros_threshold: 1 }, + [66u32; 1], + ) + .unwrap(); + + assert_eq!("0x0060e8253a9f9b04d9126b79d77bd022a59e7f9a", address); + + let address = try_nonce( + SaltVariant::Sender { calling_address }, + CreateXVariant::Create3, + RewardVariant::TotalZeros { zeros_threshold: 2 }, + [1579u32; 1], + ) + .unwrap(); + + assert_eq!("0x00ebab0f93b64b8714006f13872816beca04ee88", address); + + let address = try_nonce( + SaltVariant::Sender { calling_address }, + CreateXVariant::Create3, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 1, + total_zeros_threshold: 2, + }, + [66u32; 1], + ) + .unwrap(); + + assert_eq!("0x0060e8253a9f9b04d9126b79d77bd022a59e7f9a", address); + + let address = try_nonce( + SaltVariant::Sender { calling_address }, + CreateXVariant::Create3, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 5, + total_zeros_threshold: 2, + }, + [1579u32; 1], + ) + .unwrap(); + + assert_eq!("0x00ebab0f93b64b8714006f13872816beca04ee88", address); + + let address = try_nonce( + SaltVariant::Sender { calling_address }, + CreateXVariant::Create3, + RewardVariant::LeadingAndTotalZeros { + leading_zeros_threshold: 1, + total_zeros_threshold: 2, + }, + [1579u32; 1], + ) + .unwrap(); + + assert_eq!("0x00ebab0f93b64b8714006f13872816beca04ee88", address); + + let address = try_nonce( + SaltVariant::Sender { calling_address }, + CreateXVariant::Create3, + RewardVariant::Matching { + pattern: "bbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + .to_owned() + .into_boxed_str(), + }, + [152u32; 1], + ) + .unwrap(); + + assert_eq!("0xbb660249e599b0d9b21015fa7ebd97fd78141737", address); +} + +#[rstest] +fn test_create2_crosschain() { + let mut chain_id = [0u8; 32]; + chain_id[31] = 1; + + let init_code_hash = [0u8; 32]; + + let address = try_nonce( + SaltVariant::Crosschain { chain_id }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingZeros { zeros_threshold: 1 }, + [126u32; 1], + ) + .unwrap(); + + assert_eq!("0x006b3047dc49181a8cf360813681ab36246c5b85", address); + + let address = try_nonce( + SaltVariant::Crosschain { chain_id }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::TotalZeros { zeros_threshold: 2 }, + [746u32; 1], + ) + .unwrap(); + + assert_eq!("0xb62e9ad35c5c7865a6090a00ba5a0074b2100947", address); + + let address = try_nonce( + SaltVariant::Crosschain { chain_id }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 1, + total_zeros_threshold: 2, + }, + [126u32; 1], + ) + .unwrap(); + + assert_eq!("0x006b3047dc49181a8cf360813681ab36246c5b85", address); + + let address = try_nonce( + SaltVariant::Crosschain { chain_id }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 5, + total_zeros_threshold: 2, + }, + [746u32; 1], + ) + .unwrap(); + + assert_eq!("0xb62e9ad35c5c7865a6090a00ba5a0074b2100947", address); + + let address = try_nonce( + SaltVariant::Crosschain { chain_id }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingAndTotalZeros { + leading_zeros_threshold: 1, + total_zeros_threshold: 2, + }, + [2091u32; 1], + ) + .unwrap(); + + assert_eq!("0x00005d7c0b23ffc4036554dea00ecbb6b5f82ba0", address); + + let address = try_nonce( + SaltVariant::Crosschain { chain_id }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::Matching { + pattern: "bbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + .to_owned() + .into_boxed_str(), + }, + [45u32; 1], + ) + .unwrap(); + + assert_eq!("0xbbf5e44c1302d0228d95ff916ee5aa3ee39334bb", address); +} + +#[rstest] +fn test_create2_crosschain_caller() { + let mut chain_id = [0u8; 32]; + chain_id[31] = 1; + + let init_code_hash = [0u8; 32]; + + let calling_address = string_to_addr_bytes("0x34A50a7A272E86EE30b7A74E36f3f02AF18B1eB5"); + + let address = try_nonce( + SaltVariant::CrosschainSender { + chain_id, + calling_address, + }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingZeros { zeros_threshold: 1 }, + [343u32; 1], + ) + .unwrap(); + + assert_eq!("0x00abb8aa06547cd6c2f4cf447448ba19f18f7155", address); + + let address = try_nonce( + SaltVariant::CrosschainSender { + chain_id, + calling_address, + }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::TotalZeros { zeros_threshold: 2 }, + [487u32; 1], + ) + .unwrap(); + + assert_eq!("0xa3827c31ec59d70000e091d390670750f3b0e804", address); + + let address = try_nonce( + SaltVariant::CrosschainSender { + chain_id, + calling_address, + }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 1, + total_zeros_threshold: 2, + }, + [343u32; 1], + ) + .unwrap(); + + assert_eq!("0x00abb8aa06547cd6c2f4cf447448ba19f18f7155", address); + + let address = try_nonce( + SaltVariant::CrosschainSender { + chain_id, + calling_address, + }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingOrTotalZeros { + leading_zeros_threshold: 5, + total_zeros_threshold: 2, + }, + [487u32; 1], + ) + .unwrap(); + + assert_eq!("0xa3827c31ec59d70000e091d390670750f3b0e804", address); + + let address = try_nonce( + SaltVariant::CrosschainSender { + chain_id, + calling_address, + }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::LeadingAndTotalZeros { + leading_zeros_threshold: 1, + total_zeros_threshold: 2, + }, + [759u32; 1], + ) + .unwrap(); + + assert_eq!("0x004e286d958dffee00dfdccfd438483516fc0c93", address); + + let address = try_nonce( + SaltVariant::CrosschainSender { + chain_id, + calling_address, + }, + CreateXVariant::Create2 { init_code_hash }, + RewardVariant::Matching { + pattern: "bbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + .to_owned() + .into_boxed_str(), + }, + [50u32; 1], + ) + .unwrap(); + + assert_eq!("0xbbfaecabdd12e01f3a4ce699095ab6dbd1a62b1c", address); +} + +fn string_to_addr_bytes(s: &str) -> [u8; 20] { + let mut addr = [0u8; 20]; + let s = s.trim_start_matches("0x"); + let bytes = decode(s).unwrap(); + addr.copy_from_slice(&bytes); + addr +}