diff --git a/AGENTS.md b/AGENTS.md index ba5ab93..4a7efe4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ ## OVERVIEW -Rust library + CLI for crypto puzzle/bounty data. Build-time JSONC→Rust codegen with JSON Schema validation. Nine collections: arweave (11 bounties), b1000 (256 puzzles), ballet (3 puzzles), bitaps (1 SSSS puzzle), bitimage (2 puzzles), gsmg (1 puzzle), hash_collision (6 bounties), warp (6 WarpWallet challenges), zden (15 visual puzzles). +Rust library + CLI for crypto puzzle/bounty data. Build-time JSONC→Rust codegen with JSON Schema validation. Ten collections: arweave (11 bounties), b1000 (256 puzzles), ballet (3 puzzles), bitaps (1 SSSS puzzle), bitimage (2 puzzles), gsmg (1 puzzle), hash_collision (6 bounties), rushwallet (30 brainwallet contest), warp (6 WarpWallet challenges), zden (15 visual puzzles). ## STRUCTURE @@ -17,9 +17,9 @@ boha/ │ ├── puzzle.rs # Puzzle, Address, Key, Status, Chain, Profile structs │ ├── balance.rs # Multi-chain async balance fetch (BTC/LTC/ETH) │ ├── verify.rs # Cryptographic key→address verification (--features cli) -│ └── collections/ # Nine collection modules with generated data +│ └── collections/ # Ten collection modules with generated data ├── data/ -│ ├── *.jsonc # Source of truth (arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision, warp, zden) +│ ├── *.jsonc # Source of truth (arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision, rushwallet, warp, zden) │ ├── solvers.jsonc # Solver definitions (referenced by ID in puzzle files) │ ├── schemas/ # JSON Schema files for validation │ └── cache/ # API response cache for scripts @@ -130,6 +130,7 @@ Data-driven validation (254 tests, 3 test files): - bitaps: Shamir Secret Sharing - 2 of 3 shares published, third unknown - bitimage: Keys derived from files using SHA256(Base64(file)) as BIP39 entropy - hash_collision: Peter Todd's P2SH bounties for finding hash collisions +- rushwallet: Dmitri Kryptokov / Kryptokit 2014 brainwallet contest, 30 targets, derivation `sha256(passphrase)` → uncompressed P2PKH; 28 passphrases recovered locally, #26 claimed on-chain with passphrase still unknown, #30 unclaimed/unsolved; all 30 UTXOs funded by `1GShq18eb4V6uBtqgwxkmuPTUHCtyBcNYA` - warp: Keybase WarpWallet challenges - deterministic brainwallet (scrypt+pbkdf2) security tests - zden: Visual puzzles - keys encoded in images/animations - arweave: Tiamat's bounties on Arweave blockchain (chronobot.io) diff --git a/Cargo.lock b/Cargo.lock index 7bc192e..2572e5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -37,6 +51,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -239,6 +259,21 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcoin_hashes" version = "0.14.1" @@ -312,6 +347,7 @@ dependencies = [ "hex", "human-panic", "json-strip-comments", + "jsonschema", "k256", "num-bigint", "num-traits", @@ -331,6 +367,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "borrow-or-share" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" + [[package]] name = "bs58" version = "0.5.1" @@ -535,6 +577,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + [[package]] name = "der" version = "0.7.10" @@ -622,6 +670,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -647,6 +704,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "fancy-regex" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -678,12 +746,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "fluent-uri" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -708,6 +793,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076045bb43dac435333ed5f04caf35c7463631d0dae2deb2638d94dd0a5b872" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "futures" version = "0.3.31" @@ -826,9 +921,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -872,6 +969,11 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "heck" @@ -1255,6 +1357,33 @@ dependencies = [ "memchr", ] +[[package]] +name = "jsonschema" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44c9bb95f6ac9270bf4fd38d71c2f8704b9fe0323a293af7a5284cbd60a39b2" +dependencies = [ + "ahash", + "bytecount", + "data-encoding", + "email_address", + "fancy-regex", + "fraction", + "getrandom 0.3.4", + "idna", + "itoa", + "num-cmp", + "num-traits", + "percent-encoding", + "referencing", + "regex", + "regex-syntax", + "serde", + "serde_json", + "unicode-general-category", + "uuid-simd", +] + [[package]] name = "k256" version = "0.13.4" @@ -1269,6 +1398,12 @@ dependencies = [ "signature", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.178" @@ -1287,6 +1422,15 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -1373,6 +1517,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1383,6 +1541,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -1392,6 +1565,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1496,6 +1691,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "owo-colors" version = "4.2.3" @@ -1515,6 +1716,29 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + [[package]] name = "pathdiff" version = "0.2.3" @@ -1689,6 +1913,50 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.112", +] + +[[package]] +name = "referencing" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d4124f489451bb67c59d67fa16f3ae9b5690b290406a7538e38458632666df" +dependencies = [ + "ahash", + "fluent-uri", + "getrandom 0.3.4", + "hashbrown", + "parking_lot", + "percent-encoding", + "serde_json", +] + [[package]] name = "regex" version = "1.12.2" @@ -1873,6 +2141,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "scrypt" version = "0.11.0" @@ -2448,6 +2722,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "unicode-general-category" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -2514,6 +2794,16 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "uuid-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -2526,6 +2816,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "vte" version = "0.10.1" diff --git a/Cargo.toml b/Cargo.toml index 96e01bd..eece061 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,6 +143,7 @@ ripemd = "0.1" k256 = { version = "0.13", features = ["ecdsa"] } bip38 = "1.1" chrono = "0.4" +jsonschema = { version = "0.42", default-features = false } [features] default = [] diff --git a/build.rs b/build.rs index 4026801..541098e 100644 --- a/build.rs +++ b/build.rs @@ -33,6 +33,78 @@ struct WithSchema { inner: T, } +fn rewrite_schema_refs(value: &mut serde_json::Value) { + match value { + serde_json::Value::Object(map) => { + if let Some(serde_json::Value::String(reference)) = map.get_mut("$ref") { + if let Some(fragment) = reference.strip_prefix("./definitions.schema.json#") { + *reference = format!("#{fragment}"); + } + } + for child in map.values_mut() { + rewrite_schema_refs(child); + } + } + serde_json::Value::Array(items) => { + for child in items { + rewrite_schema_refs(child); + } + } + _ => {} + } +} + +fn validate_collection_schema(value: &serde_json::Value, data_path: &str) { + let mut schema: serde_json::Value = serde_json::from_str( + &fs::read_to_string("data/schemas/collection.schema.json") + .expect("Failed to read data/schemas/collection.schema.json"), + ) + .expect("Failed to parse data/schemas/collection.schema.json"); + let definitions: serde_json::Value = serde_json::from_str( + &fs::read_to_string("data/schemas/definitions.schema.json") + .expect("Failed to read data/schemas/definitions.schema.json"), + ) + .expect("Failed to parse data/schemas/definitions.schema.json"); + + let schema_defs = schema + .get_mut("$defs") + .and_then(serde_json::Value::as_object_mut) + .expect("collection.schema.json must define $defs"); + let shared_defs = definitions + .get("$defs") + .and_then(serde_json::Value::as_object) + .expect("definitions.schema.json must define $defs"); + for (key, definition) in shared_defs { + schema_defs.insert(key.clone(), definition.clone()); + } + rewrite_schema_refs(&mut schema); + + let validator = + jsonschema::validator_for(&schema).expect("Failed to compile collection schema"); + let errors: Vec = validator + .iter_errors(value) + .map(|error| format!("{}: {}", error.instance_path(), error)) + .collect(); + if !errors.is_empty() { + panic!( + "{} failed data/schemas/collection.schema.json validation:\n{}", + data_path, + errors.join("\n") + ); + } +} + +fn parse_validated_collection(content: &str, data_path: &str) -> WithSchema +where + T: for<'de> Deserialize<'de>, +{ + let value: serde_json::Value = serde_json::from_str(content) + .unwrap_or_else(|err| panic!("Failed to parse {data_path}: {err}")); + validate_collection_schema(&value, data_path); + serde_json::from_value(value) + .unwrap_or_else(|err| panic!("Failed to deserialize {data_path}: {err}")) +} + fn bits_from_private_key(private_key: &str) -> Option { let bytes = hex::decode(private_key).ok()?; let key = BigUint::from_bytes_be(&bytes); @@ -640,6 +712,39 @@ struct BalletMetadata { source_url: Option, } +#[derive(Debug, Deserialize)] +struct RushwalletMetadata { + source_url: Option, + solved_count: Option, + total_puzzles: Option, +} + +#[derive(Debug, Deserialize)] +struct RushwalletFile { + author: Option, + metadata: Option, + puzzles: Vec, +} + +#[derive(Debug, Deserialize)] +struct RushwalletPuzzle { + name: String, + address: Address, + pubkey: Option, + status: String, + prize: Option, + currency: Option, + key: Option, + start_date: Option, + solve_date: Option, + solve_time: Option, + source_url: Option, + #[serde(default)] + transactions: Vec, + solver: Option, + assets: Option, +} + #[derive(Debug, Deserialize)] struct BitimageFile { author: Option, @@ -971,7 +1076,16 @@ fn generate_key_code_required(key: &TomlKey, puzzle_id: &str, expected_address: } let (hex_val, derived_decrypted) = match (&key.hex, decrypted_wif) { - (Some(h), Some(_)) => (Some(h.clone()), None), + (Some(h), Some(w)) => { + let wif_hex = wif_to_hex(w).unwrap_or_else(|| { + panic!("Puzzle '{puzzle_id}' has decrypted WIF that cannot be decoded to hex") + }); + assert_eq!( + h, &wif_hex, + "Puzzle '{puzzle_id}' has key.hex that does not match key.wif.decrypted" + ); + (Some(h.clone()), None) + } (Some(h), None) => { let derived_wif = hex_to_wif(h, true); (Some(h.clone()), derived_wif) @@ -1167,6 +1281,7 @@ fn generate_data_version(out_dir: &str) { "data/bitimage.jsonc", "data/gsmg.jsonc", "data/hash_collision.jsonc", + "data/rushwallet.jsonc", "data/solvers.jsonc", "data/warp.jsonc", "data/zden.jsonc", @@ -1208,7 +1323,10 @@ fn main() { println!("cargo:rerun-if-changed=data/bitaps.jsonc"); println!("cargo:rerun-if-changed=data/bitimage.jsonc"); println!("cargo:rerun-if-changed=data/ballet.jsonc"); + println!("cargo:rerun-if-changed=data/rushwallet.jsonc"); println!("cargo:rerun-if-changed=data/solvers.jsonc"); + println!("cargo:rerun-if-changed=data/schemas/collection.schema.json"); + println!("cargo:rerun-if-changed=data/schemas/definitions.schema.json"); println!("cargo:rerun-if-changed=assets"); println!("cargo:rerun-if-changed=.git/HEAD"); println!("cargo:rerun-if-changed=.git/refs/heads"); @@ -1225,6 +1343,7 @@ fn main() { generate_bitaps(&out_dir, &solvers); generate_bitimage(&out_dir, &solvers); generate_ballet(&out_dir, &solvers); + generate_rushwallet(&out_dir, &solvers); generate_warp(&out_dir, &solvers); } @@ -2171,6 +2290,173 @@ fn generate_ballet(out_dir: &str, solvers: &HashMap) { fs::write(&dest_path, output).expect("Failed to write ballet_data.rs"); } +fn validate_rushwallet_metadata(data: &RushwalletFile) { + let metadata = data + .metadata + .as_ref() + .expect("rushwallet metadata block is required"); + + let total_puzzles = metadata + .total_puzzles + .expect("rushwallet metadata.total_puzzles is required"); + assert_eq!( + total_puzzles, + data.puzzles.len(), + "rushwallet metadata total_puzzles={} does not match actual puzzle count={}", + total_puzzles, + data.puzzles.len() + ); + + let solved_count = metadata + .solved_count + .expect("rushwallet metadata.solved_count is required"); + let actual_solved = data + .puzzles + .iter() + .filter(|puzzle| puzzle.status == "solved") + .count(); + assert_eq!( + solved_count, actual_solved, + "rushwallet metadata solved_count={} does not match actual solved count={}", + solved_count, actual_solved + ); +} + +fn generate_rushwallet(out_dir: &str, solvers: &HashMap) { + let dest_path = Path::new(out_dir).join("rushwallet_data.rs"); + + let mut content = + fs::read_to_string("data/rushwallet.jsonc").expect("Failed to read data/rushwallet.jsonc"); + strip(&mut content).expect("Failed to strip comments from rushwallet.jsonc"); + let wrapped: WithSchema = + parse_validated_collection(&content, "data/rushwallet.jsonc"); + let data = wrapped.inner; + + let default_source_url = data.metadata.as_ref().and_then(|m| m.source_url.as_ref()); + validate_rushwallet_metadata(&data); + + for puzzle in &data.puzzles { + let puzzle_id = format!("rushwallet/{}", puzzle.name); + validate_pubkey_for_claimed_puzzles( + &puzzle_id, + &puzzle.status, + &puzzle.transactions, + &puzzle.address.kind, + &puzzle.pubkey, + ); + } + + let mut output = String::new(); + output.push_str(&generate_author_code(&data.author)); + output.push('\n'); + output.push_str("static PUZZLES: &[Puzzle] = &[\n"); + + for puzzle in &data.puzzles { + let status = match puzzle.status.as_str() { + "solved" => "Status::Solved", + "claimed" => "Status::Claimed", + "swept" => "Status::Swept", + "expired" => "Status::Expired", + _ => "Status::Unsolved", + }; + + let prize = match puzzle.prize { + Some(p) => format!("Some({:.8})", p), + None => "None".to_string(), + }; + + let currency = puzzle + .currency + .as_ref() + .map(|c| format!("Some(\"{}\")", c)) + .unwrap_or_else(|| "None".to_string()); + + let start_date = match &puzzle.start_date { + Some(d) => format!("Some(\"{}\")", d), + None => "None".to_string(), + }; + + let solve_date = match &puzzle.solve_date { + Some(d) => format!("Some(\"{}\")", d), + None => "None".to_string(), + }; + + let solve_time = match puzzle.solve_time { + Some(t) => format!("Some({})", t), + None => "None".to_string(), + }; + + let source_url = puzzle + .source_url + .as_ref() + .or(default_source_url) + .map(|url| format!("Some(\"{}\")", url)) + .unwrap_or_else(|| "None".to_string()); + + let puzzle_id = format!("rushwallet/{}", puzzle.name); + let hash160 = format_hash160(&puzzle.address, "bitcoin", &puzzle_id); + let witness_program = format_witness_program(&puzzle.address, &puzzle_id); + let redeem_script = generate_redeem_script_code(&puzzle.address.redeem_script); + let key = generate_key_code(&puzzle.key, &puzzle_id, &puzzle.address.value); + let pubkey = format_pubkey(&puzzle.pubkey, &puzzle_id); + + let transactions = generate_transactions_code(&puzzle.transactions); + let solver = generate_solver_code(&puzzle.solver, solvers); + let assets = generate_assets_code(&puzzle.assets, "rushwallet", &puzzle_id); + + output.push_str(&format!( + r#" Puzzle {{ + id: "rushwallet/{}", + chain: Chain::Bitcoin, + address: Address {{ + value: "{}", + chain: Chain::Bitcoin, + kind: "{}", + hash160: {}, + witness_program: {}, + redeem_script: {}, + }}, + status: {}, + pubkey: {}, + key: {}, + prize: {}, + currency: {}, + start_date: {}, + solve_date: {}, + solve_time: {}, + pre_genesis: false, + source_url: {}, + transactions: {}, + solver: {}, + assets: {}, + }}, +"#, + puzzle.name, + puzzle.address.value, + puzzle.address.kind, + hash160, + witness_program, + redeem_script, + status, + pubkey, + key, + prize, + currency, + start_date, + solve_date, + solve_time, + source_url, + transactions, + solver, + assets, + )); + } + + output.push_str("];\n"); + + fs::write(&dest_path, output).expect("Failed to write rushwallet_data.rs"); +} + fn generate_arweave(out_dir: &str, solvers: &HashMap) { let dest_path = Path::new(out_dir).join("arweave_data.rs"); diff --git a/data/rushwallet.jsonc b/data/rushwallet.jsonc new file mode 100644 index 0000000..fd5e341 --- /dev/null +++ b/data/rushwallet.jsonc @@ -0,0 +1,1149 @@ +{ + "$schema": "./schemas/collection.schema.json", + "author": { + "addresses": [ + "1GShq18eb4V6uBtqgwxkmuPTUHCtyBcNYA" + ], + "name": "Dmitri Kryptokov", + "profiles": [ + { + "name": "soundcloud", + "url": "https://soundcloud.com/thisisarushwalletbrainwalletnooot" + }, + { + "name": "codepen", + "url": "https://codepen.io/dmitrikryptokov" + } + ] + }, + "metadata": { + "derivation": "sha256(passphrase) -> uncompressed P2PKH", + "solved_count": 28, + "source_url": "https://web.archive.org/web/20150208172337/https://rushwallet.com/contest", + "total_puzzles": 30 + }, + "puzzles": [ + { + "name": "1", + "address": { + "hash160": "e9d91b8f3f402170262431718fbb0ba9611e10a0", + "kind": "p2pkh", + "value": "1NKUXbr2URfQyzREPUzoj4MR4ytF5mEm8u" + }, + "pubkey": { + "format": "uncompressed", + "value": "040f5492295b3374ac3d746beb5b1e3629f19e4b7caa228e7d02a1862430e237a7c406b11d339dea846001feb79410a4bd61212f2022538c13cb24b5ad0cb44d52" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "af9a17713338d255ca023b7014c2c9dfcbef656d61a3370156bb804269b74a0d", + "wif": { + "decrypted": "5K9d6a9ivmDKRe77hnzrSrg2iGkwFuvg1cCGDGBpsgyk6is9U7g", + "passphrase": "5784623964023 578462396402" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2018-01-10 19:55:58", + "solve_time": 104197124, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "202f1b15595f3821578fe73bc65a9ce4bcc46011e9481d60a518d471b453a995", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2018-01-10 19:55:58", + "txid": "a904300bbadf6fde7ee6ef273aaa6536899aee5f6f1f27aefa1c56962bd4da14", + "type": "claim" + } + ] + }, + { + "name": "2", + "address": { + "hash160": "90d3b6f30e3cd2afd0a8927b73ad80e48ac27c14", + "kind": "p2pkh", + "value": "1ECmvpGxQWaRf73xxRejJAKEPA57NA77WN" + }, + "pubkey": { + "format": "uncompressed", + "value": "04d8e760ace95152182ac423d6e1ecf273dd340004242a305352806ce4d630ee9ed19242ca6c6b43f7adaedf9f67dae3c267f42488ccee6244a2dd18c37984ca39" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "f52279589f480c41864aad5641300b2f92f0cc8488f90339564cfee4f2d1c31e", + "wif": { + "decrypted": "5KgFDZv8uSJS9mEsyKKs2iyPBfhbGLft8yw8PCoLgh3VugM98jv", + "passphrase": "AGKPX AGKPX AGKPX AGKPX AGKPX AGKPX" + } + }, + "start_date": "2014-09-22 17:36:08", + "solve_date": "2014-09-23 15:42:08", + "solve_time": 79560, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 17:36:08", + "txid": "3a923499b984493a67892aeedf6feb6c230467e72ad9da88e173542e8326dbfe", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 15:42:08", + "txid": "be8f7425ab201b15d2e145436d05e61108ab64002e43b3066af66629b4348823", + "type": "claim" + } + ] + }, + { + "name": "3", + "address": { + "hash160": "ebf664fc424c230f94ae587df554c189834b0ad5", + "kind": "p2pkh", + "value": "1NWexsN7HZjNbmZcpQ8b37LiMSJ3Trvuz5" + }, + "pubkey": { + "format": "uncompressed", + "value": "04456ec2ed4201faf57edb6b513caaa0e1a88c160b1e0677c4fb2eb3fe7501617b0a0a723e92bf0740aabae6b5bd2b3115f7b65220787a685715624a798b5d24f6" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "9807a135993ae881d0dc9179ad73a36c6618707a74e25504af0551ab645da0fc", + "wif": { + "decrypted": "5JyEyivg11jUGPp5R9GE93HmN28rxkXQusBhNTtg9p1PsHWcp3n", + "passphrase": "decentral.ca D.Nakamoto" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 16:40:13", + "solve_time": 73379, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "8c8111bc62d562bc445cf97112ac77e5a2894430349d5e9e92e771a6e7f9bd29", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 16:40:13", + "txid": "5668307acaa269b7dbc12ed870866fae290367442e8abdf2f2c594e723da747b", + "type": "claim" + } + ] + }, + { + "name": "4", + "address": { + "hash160": "94db752d09f855fcdc195a1c44706c82835edcb3", + "kind": "p2pkh", + "value": "1Ea5uafoorbr74iw7Xo764x2MKi8BF7uYK" + }, + "pubkey": { + "format": "uncompressed", + "value": "04b59099bebdcb23940a81223302234b4b1acc2473d4e116b4d96e9e44e5c9c50a6ff85a4df5d1ca86229ce85007b360e6a0af16fdefa7734e0afd21c0d2f9298d" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "874f1a008f432a402c5bdb0b4374ebda7f120ba57d9edc820d9757d9818b42cb", + "wif": { + "decrypted": "5JqssJS94cr9ibjFY97p6TD1ehBaFiR3Bhc2DdYgsz4wk6tSVqK", + "passphrase": "The honey badger of money The honey badger of money" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 13:00:27", + "solve_time": 60193, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "06378529826fee10c00812603abaabfa352a7226d3d29c06139a35d83098c638", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:00:27", + "txid": "534de85113e9cb79ccc8fdb313935599059793733b3aaf35548a2b431f8b04a4", + "type": "claim" + } + ] + }, + { + "name": "5", + "address": { + "hash160": "8bb6df46035958a8ef62b0cadcce2201fde053fb", + "kind": "p2pkh", + "value": "1Djjzr1bCyys1mkEDf9qDCX9kngRv7MzQM" + }, + "pubkey": { + "format": "uncompressed", + "value": "048ba996e8476dee155eddb754da1549954ccfd940404061d9abe788c1b8bfa6f933be0a169620ccd865d8600e0395dc2a54e0b544f7c4f5d1adc2e05b159a8657" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "c0184e16d5bd2c4988897f4c03bd50eacec5d5df1ab8be127ff44b72b5060baa", + "wif": { + "decrypted": "5KGtPWkqLuEGYnq3nQsbWiP21a4yBN42ujXTub6nxwbhFSQZWc1", + "passphrase": "Bitcoin wallet and tools built right into your browser." + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 15:12:09", + "solve_time": 68095, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "30afdc625f46bf9c2d6a73cbdf1fe9caffbd8b28555bfea13dbea9d37b37bd16", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 15:12:09", + "txid": "136c4bec93999054e3f4df0097a1debee8f5d7a27eb86f6968178aef8a18b4bf", + "type": "claim" + } + ] + }, + { + "name": "6", + "address": { + "hash160": "de1f4880fe69ef588334a94ecf31558287fadcea", + "kind": "p2pkh", + "value": "1MFUVBapEBURA3jT9TGNCJQyAChqbYrvvf" + }, + "pubkey": { + "format": "uncompressed", + "value": "04b02b822cd3d224ce405e0682fccde6805cad430252e4d29fad73ed752b56d946587072d39c8cc0f1693e0dd3be23c53ba01d3921603f1c72a6a29bc4abb5e22a" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "305c10eb89edbd0e35d7359362ed18d3ee5b0df7fd6a6c400002dbfeefe2e950", + "wif": { + "decrypted": "5JBas2qrscDAMPao26kM9amYP8JYpooWYEKy5kYpptfgNErVjy8", + "passphrase": "this is a rushwallet brainwallet" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 13:23:26", + "solve_time": 61572, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "b758f391a4dc2b0ebc16667b209348389019981d97582e28aa69f41064330c93", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:23:26", + "txid": "62bf890257f4c71691c78ed289b1259983939e2582210ce6623819a7102d941a", + "type": "claim" + } + ] + }, + { + "name": "7", + "address": { + "hash160": "3cbcd24af4baa702dc4145bc01442f302a9fd3e4", + "kind": "p2pkh", + "value": "16Y9k4AhDJG43dSGMKu6GAVigAfTH2t96x" + }, + "pubkey": { + "format": "uncompressed", + "value": "047abacd375a5f4687f1db45d28b17fcd431ac4130d135ffa0be3f416645d2c6d166924c4c3537e52cff0527dc0bd09de0dc73ecaf4bdb4ada9f71167db2582e75" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "6196a23ae9b913a15f1676d7c5d535fe594bc91644ed02b95c8a72742ff9a2ad", + "wif": { + "decrypted": "5JZGM41oXvaFZ4JUNh4AhDFdDdoziPdHsQPuEJBPCMf3Qo6Cc3w", + "passphrase": "New Quiet Keyboard for Enrique" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 13:23:26", + "solve_time": 61572, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "36f74ab06b156937f242f04f8ce4aae3b7ad28b1cecc393259fbee0a5388b4aa", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:23:26", + "txid": "6161cf6778dc7a82092cef5a29e4d7bcf52ab86336aa3403bb6a5d3a61ca9a6f", + "type": "claim" + } + ] + }, + { + "name": "8", + "address": { + "hash160": "751bbc218d4d05d818f36c840149789f90124f77", + "kind": "p2pkh", + "value": "1BgDHVsu56WwTtCTSsUkiE9Goc4KZcrgvR" + }, + "pubkey": { + "format": "uncompressed", + "value": "04e6d5be924603485a80cccb51f8c2bfbb4acdfbf9d65acc58c3b40970c18166a92a609a2c03054ca17a4ad24cc7309e7f9f6a5258eb334815e6e52a3e32284787" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "78ac7258680117f5292c011eb9a823cedd7cf063564b3c1e84361a7ea40890a4", + "wif": { + "decrypted": "5JjS2XduetuMWs22rCnKbDABvv2UBmTVq8snNwWSikyVfXSSXkn", + "passphrase": "Let's get a new keyboard for this guy, my ears bleed when I sleep." + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-29 20:26:35", + "solve_time": 605361, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "187e6d6a8c5f5bdce59e3fe3fa95320550a86844870d7860b53a9bfb7447c1cd", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-29 20:26:35", + "txid": "977c6fe974874110e1b1b321d852db8525fe588db9aae9c065cf059f74081d34", + "type": "claim" + } + ] + }, + { + "name": "9", + "address": { + "hash160": "e3d776c3d2e1e60fdcb697e6336ddd2b6db41517", + "kind": "p2pkh", + "value": "1MmiWF1RaDN3yVwQFZpLRfxzFPRF68D1cT" + }, + "pubkey": { + "format": "uncompressed", + "value": "04353554bc50692bc7c37229c061fa30d11bba34777f0513fdb39596f2f06f95e6671f93fc7a14dda650957c083198f56be1b1de8b149c122599088efe9fd39683" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "b33618c91eb43dc38ac3d81f008983be0dad8818a761b5e08bfb47d349e09f1f", + "wif": { + "decrypted": "5KBDJ1QjDuqUm8a6K6hkJ3g1zjb7hpcHGZuDs2wbjtgazFvbqjf", + "passphrase": "supercalifragilisticexpialidocious supercalifragilisticexpialidocious supercalifragilisticexpialidocious" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-22 23:40:53", + "solve_time": 12219, + "transactions": [ + { + "amount": 0.05, + "date": "2014-09-22 20:17:14", + "txid": "7ce047c201fe2edbce71dd9cb9e52c34a0860125bbccc0da54dee64fd6815b35", + "type": "funding" + }, + { + "amount": 0.05, + "date": "2014-09-22 23:40:53", + "txid": "9c2e31753906a0410a37c9d75654fbb2b328b3c53ffbfe92787bfaa5e38b78d7", + "type": "claim" + } + ] + }, + { + "name": "10", + "address": { + "hash160": "91cec2a35b8d01cd8a263b1e4f3edfb7df02f85a", + "kind": "p2pkh", + "value": "1EHxfmrG74HSv1dji6uxiKn1ts1pSPr4h7" + }, + "pubkey": { + "format": "uncompressed", + "value": "049f5a904e465dd5666b90069c7564994a20f8a1cccac86fa8f65bca875f536e62742afee3904bea3f577735ef75a318c95c18ba752de569b12c85b80a7d1d3162" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "8416ea3c7bd1201df15410bb0e8a6b6cab676502004fa68bced5f42bbb6cf6b1", + "wif": { + "decrypted": "5JpTdYGejXzzGdKbBR36SpSAAWxMgcyQckpYFoMbSBYnzFFUZcn", + "passphrase": "happy birthday celebrate tony tony" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 13:16:42", + "solve_time": 61168, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "de7026fd9171e3a532cedae504536c2cc19ea6568a18775616756079735dbd1c", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:16:42", + "txid": "eec469a9d837a90c2becd06afda0f937f744880f82ccb9198211e2fdda8f4f62", + "type": "claim" + } + ] + }, + { + "name": "11", + "address": { + "hash160": "4c119f35b75fe1598c02e81b4b6b431a54b606ae", + "kind": "p2pkh", + "value": "17wDTyKr6pEn6mXUku9vh89JucE1L3SMjb" + }, + "pubkey": { + "format": "uncompressed", + "value": "04ee8021595c123587ca98d7682a5f175bfda0326133b572db455756910a0b277a4b19feee6c4d84834d319c3cd72a6c5059f9620dbad21f62b630d774403cde89" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "0f8489b2c250110704e4877b9ac033156ac325908ceee2a8857b14d80207789b", + "wif": { + "decrypted": "5Hw7xh3T7TVewfSWfmLFJTEki9NSvFbnqVxXg8HMCpLPC3JjNXL", + "passphrase": "www.kryptokit.com" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 16:40:13", + "solve_time": 73379, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "991cdeea72a797cffce875f761d82e50e16bfc000cc52f35578a6d9e76d31397", + "type": "funding" + }, + { + "amount": 0.0008, + "date": "2014-09-23 16:40:13", + "txid": "6bdff5bd447cd9e33e135e2f4293ef17e55e5884fcb2d9ed20ab5246a6341760", + "type": "claim" + } + ] + }, + { + "name": "12", + "address": { + "hash160": "943eb3516d30cea4b98a20052548e1601868616d", + "kind": "p2pkh", + "value": "1EWr7tvs8efFu15nvuL4RezVVoHFpxH2nF" + }, + "pubkey": { + "format": "uncompressed", + "value": "040f6968c1f39b2f869891b742a34d2f0153e8c6a538e6a0d50095d03f16257226138b9f0a359ff4899e5249d15a6ac71bc13c19591dc0da2ca032bfc9efbbcb94" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "142e3512dd89fbc3f7fe005d87ff49a58cca7c82a50a2ff1b0f5e64e86b16b48", + "wif": { + "decrypted": "5HyB4jgZUTrkawvPY872koRsNy2f1VY3RT5DnEpBNsmV6HdPCd9", + "passphrase": "www.rushwallet.com" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 12:44:13", + "solve_time": 59219, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "49d7d7d1af718c1eb39d01b574c2dcb41d7aded066167aa0b1765e8d21f1ac60", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 12:44:13", + "txid": "8466270fd586c3d56bf49f64c9165cac88fc96262a994db5152ad012c4238ece", + "type": "claim" + } + ] + }, + { + "name": "13", + "address": { + "hash160": "aed3ac72970ce184ee6e0e67e5b67bfc9197450a", + "kind": "p2pkh", + "value": "1GwQ9ik3PH6g4BzwShFdwf2AEhUyfQVBpn" + }, + "pubkey": { + "format": "uncompressed", + "value": "044ab4e97848419c137b1c9e652bd4dfafb6f07cfd2e006a4f073c655550386c4c8c39465fc432d30d2ae6b43b4369162903b900de8adeae9f5dc3bf262368fa07" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "f236c7a3bdde16d8264ea2e9d82347721a831a324fc5f42870eed8a720270100", + "wif": { + "decrypted": "5KexcVAqjA8KcXuU49V2newSVFVQb69BPM1hMft6tGyKQnLTWim", + "passphrase": "rushwallet brainwallets are easy to use" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-10-03 21:02:28", + "solve_time": 953114, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:14", + "txid": "86158ded0cd15f16348319e75687015bc82d167f4f4c013ca9703a2adde41905", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-10-03 21:02:28", + "txid": "14710d11301c0ce117eca620051e81e919470989b74fbfcaa6172ff5cd9e2b40", + "type": "claim" + } + ] + }, + { + "name": "14", + "address": { + "hash160": "220604c119a6a57f5d88429fbf1e499e10e2d8f0", + "kind": "p2pkh", + "value": "146uBhbW8pvwW3HVcqV6MWhRMiAj9jZj7W" + }, + "pubkey": { + "format": "uncompressed", + "value": "04df7861cd84fd334f2c456e63c154fc6bab65a1d07b67b765c156e859a3795b82f1bf19d94c2007e73ff99ab657d45e0154a1e50110468d4900161c4b165519c0" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "385ec4f7ddaca0501099f902a540e6e09a2d16c7b76616dd44c5d49204e400b0", + "wif": { + "decrypted": "5JF7UwA2yQKYkjqiuzWHxFSthGKCJCkpdu86FrBh5TNFhAQZn18", + "passphrase": "the kryptokit hardware wallet is coming soon" + } + }, + "start_date": "2014-09-22 20:16:50", + "solve_date": "2014-09-23 13:58:23", + "solve_time": 63693, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:16:50", + "txid": "96086d20eb4627b72a91db3b28148499c9431f0478fc75c42ba582b2a6a01112", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:58:23", + "txid": "348ee174016be54657af3128f5a56b69da33ce621cdb6c9e18d3b50cb028e3ed", + "type": "claim" + } + ] + }, + { + "name": "15", + "address": { + "hash160": "46c75de29b97b959d8f3b634ee7cd0c606d75c9b", + "kind": "p2pkh", + "value": "17TF8fyceXiL81iJz4UhuT5sCiCJCPQSUz" + }, + "pubkey": { + "format": "uncompressed", + "value": "04b33bbd2d597fe950c5d0b0ea40976f0559ed8095358b314ff7539c4fb4b550960f2e466aa6f851af1bec92f0792e488eb33ac2cf36eb87c9ae54628de9ef45f6" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "89c11d8b682a4ef7fdf4ad7ce2e937f13afefefb0215d4aaf3b2a20b9eca0f77", + "wif": { + "decrypted": "5JrxLC1LjDWhFYJGQQoC9FAMinChKFFssvDqZfhWSSCGQWdqE7Q", + "passphrase": "MAKERBLOCK MAKERBLOCK MAKERBLOCK MAKERBLOCK MAKERBLOCK" + } + }, + "start_date": "2014-09-22 20:17:35", + "solve_date": "2014-09-23 15:12:09", + "solve_time": 68074, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:35", + "txid": "7384b84f01718e57793237cc0ab0dee2e19ac99b7ee56d35780a02af3b342210", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 15:12:09", + "txid": "519b60499045c5a1b5f249ca416564ded46cea7b1cb7a8f2751a31964bc0d214", + "type": "claim" + } + ] + }, + { + "name": "16", + "address": { + "hash160": "525191db0fe6dfe778d704d246c7a972cfe2d835", + "kind": "p2pkh", + "value": "18WG8J9R7pKiAJ441msJPcawQ9BKp2Rox7" + }, + "pubkey": { + "format": "uncompressed", + "value": "045c858b5674ffd7d4e0d265a862447725ed9d74e267dd6329afa85160c4527d6a1ba4b73e3ff2ee3430a0ff0d714a1c588c90895e8a4b1ac5a53173197a702e13" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "83eeb2123d29ae80d6e5bebd3525ed057e5afd6a055ba0e2ff23b9448a64088c", + "wif": { + "decrypted": "5JpPcnF5fHdmhYJqyU4GRTTK2j72EBRDc9J5mHAX1yWro2twj8q", + "passphrase": "Tony's 3d Printer" + } + }, + "start_date": "2014-09-22 20:17:35", + "solve_date": "2014-09-23 13:58:23", + "solve_time": 63648, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:17:35", + "txid": "91f591cd63a596d5da52cf458485599390487bdb497cff58282833e3cd85e699", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:58:23", + "txid": "a49b00d400cef8c98c80e433b9dd2691de018e549f7f8bab0e605bdce3c2ec70", + "type": "claim" + } + ] + }, + { + "name": "17", + "address": { + "hash160": "408aa2a4c5df589979a83316fbafff4da68eb1a3", + "kind": "p2pkh", + "value": "16tGKq48tGq3Td1wcDoQRuWtPtXoEfZpBC" + }, + "pubkey": { + "format": "uncompressed", + "value": "0458e33c33b9eec4d9c4da0080c1cf40d4a7a8aac4c3d3ce80f429491a4e3fca9980c3a10106bd0686097880cb8162f6a8835c0fdc91e7af0eb9aef46743513003" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "4dfc9787478140cb8b57f18e51ea7ce0acc032f52d27d4245573d6cfd10212c8", + "wif": { + "decrypted": "5JQdeTxtRErwuxpQbGfJFWz1ECw3BR4h12zJ5EiPQzw9xusScLw", + "passphrase": "Dmitri Nancy Enrique" + } + }, + "start_date": "2014-09-22 20:32:38", + "solve_date": "2014-09-25 20:16:57", + "solve_time": 258259, + "transactions": [ + { + "amount": 0.15, + "date": "2014-09-22 20:32:38", + "txid": "982f472395906eb47afd40a265e663bca81ef5bbbee12672bcb3cb307a994f27", + "type": "funding" + }, + { + "amount": 0.15, + "date": "2014-09-25 20:16:57", + "txid": "59ae3210bce2a6703572940b1fa651d526c2c30d0b82c6be671e80cd1c84eaff", + "type": "claim" + } + ] + }, + { + "name": "18", + "address": { + "hash160": "eb9dc8a9d4e7c34342a2c0e3166c24b607800298", + "kind": "p2pkh", + "value": "1NUpp6S2LjpDBVu76r9LuhCrtrC6cXQNxp" + }, + "pubkey": { + "format": "uncompressed", + "value": "04e864a09c1516ed07696a02e299ad8442be91636f455862098ff55e89355e21e54d4b0d09e6219622bacc1c05f497c7cadefec322448d5623736382e4965db6b0" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "570e7d1dac0f27426f47463b3cd0ec492034450b7a732fe04de49444a21fc336", + "wif": { + "decrypted": "5JUdKpzUHD8TumDnp2Tirz5JEtxFPkbpeTv3qPWMfRPWMX77zw8", + "passphrase": "BITCOIN ACROSS AMERICA BITCOIN ACROSS AMERICA" + } + }, + "start_date": "2014-09-22 20:32:38", + "solve_date": "2014-09-23 13:06:18", + "solve_time": 59620, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:32:38", + "txid": "82ebba32667639f6e8a2877e5a5f8cf75f02113f7667426653af644e499fe7f3", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:06:18", + "txid": "4345f9e968fb09eea1257d343502ffa12998aa082cea88cf382730488dcb8b58", + "type": "claim" + } + ] + }, + { + "name": "19", + "address": { + "hash160": "780d6fe40e291ca159e48e2e372331b43d4d81ab", + "kind": "p2pkh", + "value": "1BwnBeM3xHh8cVmQDUmQjWMENxB1APLDSE" + }, + "pubkey": { + "format": "uncompressed", + "value": "045459e22b853b2419db97609a030bfe06c936b47a973fa8f9dae407f6b05966cb3888b1771ac4b3b5443c49e45060ab79033d48c033a796cbd782c5fa06aa11e5" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "d2cddeb1e8778dbf975f00b32a68e20fe3ab73565d7737c76a0217191afef8cd", + "wif": { + "decrypted": "5KR8HrKgG9TefHX1YGk8d9AXKA8EWe19SHH6cMUQTeX1dRQJ8r2", + "passphrase": "you thought this was a clue but its not that easy" + } + }, + "start_date": "2014-09-22 20:32:38", + "solve_date": "2014-09-23 14:03:49", + "solve_time": 63071, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:32:38", + "txid": "6c1331573603362f808cc88692829128835a703125547c91eb3d648b19ca02e8", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 14:03:49", + "txid": "26a69b8756fc4c58499af364e4fe9b8496f50854a72c87ddd62257a1b8bd3144", + "type": "claim" + } + ] + }, + { + "name": "20", + "address": { + "hash160": "9a2a57bedcaf2c1097d4615d2acc43eb907385bb", + "kind": "p2pkh", + "value": "1F49nZxGdLbqKH3TvMnfy9xohZXc8xJBAU" + }, + "pubkey": { + "format": "uncompressed", + "value": "04fdc55c34aa085f649ec5c6fd749aa139cb748f610dfca5b40c7120fd4859371b460caedef96ef2018968dc0c12adacdc58ce2c6cb0addff299c797dbfdaf6a85" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "7758e815ecb5c738e8dccfe55213a5f5b4491c882860352629dde0dd66613f8d", + "wif": { + "decrypted": "5Jir9XjvcYW5hTug66ToQoWmN8EarSuwGgFVZAEqoMwoyZGYwCU", + "passphrase": "RushWallet Fundraiser by Kryptokit" + } + }, + "start_date": "2014-09-22 20:32:38", + "solve_date": "2014-09-23 15:57:26", + "solve_time": 69888, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:32:38", + "txid": "1c57279c898ba2deeb520abc8feb84eb3d0135a175656805271be1c199fa67a3", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 15:57:26", + "txid": "fcd06a4c7bfe4afb8db8fe4bf0bf95371298622cb432e375d0bd9403866a5fcb", + "type": "claim" + } + ] + }, + { + "name": "21", + "address": { + "hash160": "895c02a38d28ba8f34a8a0baef0c4445682b3bf3", + "kind": "p2pkh", + "value": "1DXHoGc61UE4x2LLkHfPetrJwcQjyMi9dE" + }, + "pubkey": { + "format": "uncompressed", + "value": "0481f959abed78e0c12c2aab725f481d5b3a6d0dbf6ffe215bd3e27261bddce738352d188830d7faacea73213434542c68056d73daa2019cd9c3a4d3075dbb7be7" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "822c57a30037fd4771c354b8f71f93a7b9e3460a2795334eabea2317f84e532e", + "wif": { + "decrypted": "5JocgUqQg1c6yP9SQ6PSwWoo28BjHiazTVqSdaJCb2fJzW7hyQd", + "passphrase": "Hey try out Kryptokit today" + } + }, + "start_date": "2014-09-22 21:24:40", + "solve_date": "2014-09-23 13:58:23", + "solve_time": 59623, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 21:24:40", + "txid": "a78fd1563a9058bec48f669a1acd24dbf9bd3d50dbfd979b0af4bbd81f7f6177", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:58:23", + "txid": "10862911685dcd04ea8280420d6fc900e18e9fff44da3da0c64c4a8feeac2dbd", + "type": "claim" + } + ] + }, + { + "name": "22", + "address": { + "hash160": "5e1730539a07c68d2360ac6af647e5287c282f4f", + "kind": "p2pkh", + "value": "19aWJDZ6i4FGLBrx5u57Qee8bSkmsVdFf6" + }, + "pubkey": { + "format": "uncompressed", + "value": "044fcd0443c927ea612bf0f6308d825623df4f145fea77b54d85e78f8fbe19dd9323b3488f2478d6c8d58d4c94de1986c9ec10e2155b7bf286f0a23528d6ee92d7" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "c8f5cf0d0fca8f31ecde1f93f052c4800248b783c36db01b9c5b74ca8fde637f", + "wif": { + "decrypted": "5KLnqfYcHiQFtX4eH43ZaEcT7J9rYEgPyNMdgXd55U2bDAtRNoq", + "passphrase": "CLiCK CLACK Click CLiCK CLUCK" + } + }, + "start_date": "2014-09-22 20:40:50", + "solve_date": "2014-09-23 15:12:09", + "solve_time": 66679, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:40:50", + "txid": "26a69d437296d77c3dbed173c7f504f5085ab24cf0de80a01ff031bdabe33f91", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 15:12:09", + "txid": "47b998851a34f600cd480834c1a1e413e7bb87e3702006428fc6b9b5ce331c61", + "type": "claim" + } + ] + }, + { + "name": "23", + "address": { + "hash160": "4204477fd856845fd7521e9002af910ff2ef272e", + "kind": "p2pkh", + "value": "1724izW5beNyKGXv4a3zcoTySq8fZoo2RL" + }, + "pubkey": { + "format": "uncompressed", + "value": "04f5980512adb9a9d8dd7fe5b711b4619cdc16aedccc67d959e64bed57c6d89c68174a52e2f1b83874ac58b80aa25c362e634f431b996ae9a37803cd241d7a882f" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "24fe8073b8b2bf67f7f327e6ecd7ef360fb3c651a18013b3b17cd7a568b08818", + "wif": { + "decrypted": "5J6aYhZSY26EbZ9Xf6PhoMP8UcCBUa6TghUvGWEfxccDtehxky7", + "passphrase": "seans's outpost seans's outpost" + } + }, + "start_date": "2014-09-22 20:40:50", + "solve_date": "2014-09-29 20:43:00", + "solve_time": 604930, + "transactions": [ + { + "amount": 0.05, + "date": "2014-09-22 20:40:50", + "txid": "55ccf3b8b62247922c554ab9d34d2d2092f997002b97fdacbea3f4fc025ad528", + "type": "funding" + }, + { + "amount": 0.0009, + "date": "2014-09-29 20:43:00", + "txid": "8ff24eb89410c9f76a8ffa290cc098437859e61633b8d5929d42a1227cf96ef5", + "type": "claim" + } + ] + }, + { + "name": "24", + "address": { + "hash160": "55887e8dd8a2eed87de2d58af089e3069c298c7f", + "kind": "p2pkh", + "value": "18oFx6mSd3YeywSnXLNpqgG7XdY63JUTnP" + }, + "pubkey": { + "format": "uncompressed", + "value": "04666f17c0d0e0357da1e7c9f8d0f8e42c201dd202d59249b8527a31d73eec5e30feee4f9aaf0456a4e26162287d9e7fe51b1ed99f2b61295ac30d893545453422" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "809893892074b92fbb501182cbf462396e52823002a1c6cc8df6e78ad124f5a5", + "wif": { + "decrypted": "5JnvPntAhvg5CZbhSsNVfEtnzj1z1UxneRRtg91u4VNMPyEEizi", + "passphrase": "seansoutpost.com seansoutpost.com" + } + }, + "start_date": "2014-09-22 20:40:50", + "solve_date": "2014-09-23 19:51:42", + "solve_time": 83452, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:40:50", + "txid": "ee55db336839aebc8d5091a75352fb00f03631812653a030a8a0e0984e96bfb2", + "type": "funding" + }, + { + "amount": 0.0009, + "date": "2014-09-23 19:51:42", + "txid": "ca8bcdcf3a7a67dd76fd00959a40108291787151c3bbc482fb7c30b6e649ab1c", + "type": "claim" + } + ] + }, + { + "name": "25", + "address": { + "hash160": "e18d50fd2c48599eebb1430017a30da131b8bf9c", + "kind": "p2pkh", + "value": "1MZcKzhoptp5ZCqNneRfZeEgstvjHGa8xR" + }, + "pubkey": { + "format": "uncompressed", + "value": "04ab7a4be6048064e7395d0d2b669f9c981d2d72a8ecd15b324f9c06b94c3ae5ce03a4665473b027f617015e46aa5180bf805f5f48bf658a9cefcf0cd08a73363b" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "9fb12e3a0dd90c7256667aa4bb1505b980502da3bad129bf9e10e74fae24221e", + "wif": { + "decrypted": "5K2chgMds89aLfpAVSeQgGrfMhtzmXMpSb6uCfGr7myx8fXNXop", + "passphrase": "kryptokit.com/fundraiser" + } + }, + "start_date": "2014-09-22 20:48:06", + "solve_date": "2014-09-23 13:16:42", + "solve_time": 59316, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:48:06", + "txid": "4d7d7fe525d215c4d5c474ac13dcb739ac2a552443e8ba887215b75b4ac1b94a", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 13:16:42", + "txid": "189cfb8f7328d59ebb2eea4dc49817e9dd5365a1ae9594c10bb5d1732c0abeb8", + "type": "claim" + } + ] + }, + { + "name": "26", + "address": { + "hash160": "448ca165ceb9c1cbb93e3deb7dd74907f25e2508", + "kind": "p2pkh", + "value": "17FTRDmSymwBvwiBAx3ds4iVHaCbUCNpKS" + }, + "pubkey": { + "format": "uncompressed", + "value": "04a0746f2492fddfbd35285b3cb6f15af41fc787596d7e5a42f98e8b11d7648e8879bed40267dda13e5dcbbf2447c99036a0efca56c89c96707aefa07ce392b384" + }, + "status": "claimed", + "start_date": "2014-09-22 20:48:06", + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:48:06", + "txid": "21163ad50a28d77165ddf4b528dd32a6902a56ab326e6744df0f71e691836d17", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-25 06:53:06", + "txid": "d8d2825459f70a17f9807a5c4b7088b248228316101cb097044e7fafb5ac5c99", + "type": "claim" + } + ] + }, + { + "name": "27", + "address": { + "hash160": "a3be11dae74c0dc927dbb8627f2525bd2e2dff4a", + "kind": "p2pkh", + "value": "1FvnqYfHXEBAbtoghXvubjdpkCxnYVYdWW" + }, + "pubkey": { + "format": "uncompressed", + "value": "0474a738d294281c7489c36a18bc3c54fae1dc793cbea76a79523d415961d015afeb923c190ec40dd32abcd157d895b968acbf2e58317a042ec43697612979ec60" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "41a45f595d71790def095a1add8d4a8a5ffa91ae54bdf867eff0e57d48513e8f", + "wif": { + "decrypted": "5JKCKXsknfUcC8AudrvaereEhocbWieoTsvzUoNXA8XFn4aD92J", + "passphrase": "babooshka babooshka babooshka babooshka babooshka" + } + }, + "start_date": "2014-09-22 20:48:06", + "solve_date": "2014-09-23 18:16:50", + "solve_time": 77324, + "transactions": [ + { + "amount": 0.025, + "date": "2014-09-22 20:48:06", + "txid": "a9cf4c676178f158ad01fdcab5923ba2b340353057c28c143934bfcdeb6dc1a1", + "type": "funding" + }, + { + "amount": 0.025, + "date": "2014-09-23 18:16:50", + "txid": "774d1e10cefa5767d9fbed138af22ad25048b26b780098557c00f0a3f0e2079b", + "type": "claim" + } + ] + }, + { + "name": "28", + "address": { + "hash160": "2b9ba0c9f11722716ba57b4e5a6d1aacacfe5d3d", + "kind": "p2pkh", + "value": "14yaVUgstpqavPEFtyit3wEWsyumysqxZV" + }, + "pubkey": { + "format": "uncompressed", + "value": "0421bb390cdfda9ea9aebbabd1bd944337a98a54d627fe2c51b7873653f591d5d2b945391f85e7d9219bf4f95bea387795ddb05fe937280700737607c0194ae02e" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "fcf72cce6f8d959c27007355e04e3af112cbe7d7ed4d7ef971327c8b77b22961", + "wif": { + "decrypted": "5KjhFF7i5bYENHvsvtWjLpjuvrgybEeL4VuyLn6mpUGA2Q8Lv1o", + "passphrase": "Dmitri Enrique Nancy" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-24 23:14:24", + "solve_time": 183430, + "transactions": [ + { + "amount": 0.05, + "date": "2014-09-22 20:17:14", + "txid": "f76ca64ec5751717c4f87f3d1a5cd0f89b1572cdcb42ef3c164ae9b18ef8c452", + "type": "funding" + }, + { + "amount": 0.05, + "date": "2014-09-24 23:14:24", + "txid": "f015d230ce56ca9d740e71527f425aa7d610125046d31ac2f219ce0bc97e3f10", + "type": "claim" + } + ] + }, + { + "name": "29", + "address": { + "hash160": "e674ffcc0f885de20bc4c122b97dd7e0dd0d6048", + "kind": "p2pkh", + "value": "1N1YaNNTNw54g1djCAwcstrVGpgjDqrnsQ" + }, + "pubkey": { + "format": "uncompressed", + "value": "044c63eba3e8037e65156bc1f7659b6a0019dee7dd9992914b30b9e5949259aa6a2de3bdd46bb63977f45f2873c6de4732419e79be66818078d66ff55d187bc90b" + }, + "status": "solved", + "key": { + "bits": 256, + "hex": "e1b404bb5fdd9941b0743b3fa49fe28d076211ac7dd4462a3274df8d67dd5f5e", + "wif": { + "decrypted": "5KXgsE2AkJjtuJiEqTvxc5zJMZjbAZD6qBGU3ZhnExBcUPioUR8", + "passphrase": "THE MYSTERY MAN BEHIND THE CRYPTO-CURRENCY" + } + }, + "start_date": "2014-09-22 20:17:14", + "solve_date": "2014-09-23 17:17:35", + "solve_time": 75621, + "transactions": [ + { + "amount": 0.1, + "date": "2014-09-22 20:17:14", + "txid": "574876a325f5ffda9569a4df9e3574b1019e4f773204f61e37bf83b90c17093d", + "type": "funding" + }, + { + "amount": 0.1, + "date": "2014-09-23 17:17:35", + "txid": "c4fa1a199e2f2a08a425c2dc11bfeb39c6ceaa6ce10746fdf7cf6d8b430cf283", + "type": "claim" + } + ] + }, + { + "name": "30", + "address": { + "hash160": "1a503dfb4e93103bd54218ba7d0e65a95bf397eb", + "kind": "p2pkh", + "value": "13Q8hJqagtd77ojTJcEZPjTz2sBFSsYxyj" + }, + "status": "unsolved", + "start_date": "2014-09-22 20:48:06", + "transactions": [ + { + "amount": 1.0, + "date": "2014-09-22 20:48:06", + "txid": "87fec08c933c04fe5b60c14cc8e5a7dabe10774380e06458c88f1d194e06828c", + "type": "funding" + }, + { + "amount": 0.9999, + "date": "2015-03-25 21:33:55", + "txid": "73950f9a1f0e1fa512194b753a6e2d836154ff407293bedea4fcd4b22f109653", + "type": "decrease" + }, + { + "amount": 0.01, + "date": "2015-04-24 18:28:44", + "txid": "64278e7126e83831f91e84fa27f2bc6e88442d2dfa0361dd52a7c9abf0852cbf", + "type": "increase" + } + ] + } + ] +} diff --git a/data/schemas/collection.schema.json b/data/schemas/collection.schema.json index 6e6741a..6742dc6 100644 --- a/data/schemas/collection.schema.json +++ b/data/schemas/collection.schema.json @@ -66,37 +66,6 @@ } } } - }, - { - "if": { - "properties": { - "puzzles": { - "contains": { - "required": ["key"], - "properties": { - "key": { - "type": "object", - "required": ["bits"] - } - } - } - } - } - }, - "then": { - "properties": { - "puzzles": { - "items": { - "required": ["key"], - "properties": { - "key": { - "required": ["bits"] - } - } - } - } - } - } } ], "additionalProperties": true, diff --git a/scripts/src/bin/add_timestamps.rs b/scripts/src/bin/add_timestamps.rs index bf30a19..30d4ade 100644 --- a/scripts/src/bin/add_timestamps.rs +++ b/scripts/src/bin/add_timestamps.rs @@ -537,7 +537,7 @@ fn main() -> Result<(), Box> { .collect::>(); let collections = if collections.is_empty() { - vec!["b1000", "gsmg", "hash_collision"] + vec!["b1000", "gsmg", "hash_collision", "rushwallet"] } else { collections }; diff --git a/scripts/src/bin/extract_pubkey.rs b/scripts/src/bin/extract_pubkey.rs index 6995469..537331a 100644 --- a/scripts/src/bin/extract_pubkey.rs +++ b/scripts/src/bin/extract_pubkey.rs @@ -508,7 +508,7 @@ struct PuzzleToProcess { fn find_puzzles_needing_pubkey(data_dir: &Path) -> Vec { let mut puzzles = Vec::new(); - let collections = ["zden", "bitimage", "b1000", "gsmg", "bitaps", "ballet"]; + let collections = ["zden", "bitimage", "b1000", "gsmg", "bitaps", "ballet", "rushwallet"]; for collection_name in &collections { let path = data_dir.join(format!("{}.jsonc", collection_name)); diff --git a/scripts/src/bin/generate_h160.rs b/scripts/src/bin/generate_h160.rs index 22008ad..d84456c 100644 --- a/scripts/src/bin/generate_h160.rs +++ b/scripts/src/bin/generate_h160.rs @@ -90,7 +90,7 @@ fn process_jsonc_file(path: &Path) -> Result<(), Box> { fn main() -> Result<(), Box> { let data_dir = Path::new("../data"); - let files = ["b1000.jsonc", "gsmg.jsonc"]; + let files = ["b1000.jsonc", "gsmg.jsonc", "rushwallet.jsonc"]; for file in &files { let path = data_dir.join(file); diff --git a/scripts/src/bin/generate_transactions.rs b/scripts/src/bin/generate_transactions.rs index 0177e99..97e1f54 100644 --- a/scripts/src/bin/generate_transactions.rs +++ b/scripts/src/bin/generate_transactions.rs @@ -456,6 +456,7 @@ async fn main() -> Result<(), Box> { "b1000".to_string(), "gsmg".to_string(), "hash_collision".to_string(), + "rushwallet".to_string(), "zden".to_string(), ]; } diff --git a/scripts/src/bin/generate_wif.rs b/scripts/src/bin/generate_wif.rs index 4249b54..cbb4a0d 100644 --- a/scripts/src/bin/generate_wif.rs +++ b/scripts/src/bin/generate_wif.rs @@ -59,39 +59,82 @@ fn add_wif_to_key(key_table: &mut Value, wif: &str) { key_table["wif"] = json!({ "decrypted": wif }); } -fn update_puzzles_array(doc: &mut Value) -> usize { +fn puzzle_label(puzzle: &Value, fallback: &str) -> String { + puzzle + .get("name") + .and_then(Value::as_str) + .map_or_else(|| fallback.to_string(), ToString::to_string) +} + +fn compressed_for_puzzle(puzzle: &Value, label: &str) -> Result { + match puzzle + .get("pubkey") + .and_then(|pubkey| pubkey.get("format")) + .and_then(Value::as_str) + { + Some("compressed") | None => Ok(true), + Some("uncompressed") => Ok(false), + Some(other) => Err(format!( + "puzzle {label} has unsupported pubkey.format '{other}'" + )), + } +} + +fn update_puzzles_array(doc: &mut Value) -> Result> { let mut count = 0; if let Some(puzzles) = doc.get_mut("puzzles") { if let Some(array) = puzzles.as_array_mut() { - for puzzle in array.iter_mut() { + for (idx, puzzle) in array.iter_mut().enumerate() { + let fallback = format!("[{idx}]"); + let label = puzzle_label(puzzle, &fallback); + println!(" Processing puzzle {label}"); + + let compressed = compressed_for_puzzle(puzzle, &label)?; if let Some(key_item) = puzzle.get_mut("key") { if let Some(hex) = needs_wif(key_item) { - if let Some(wif) = hex_to_wif(&hex, true) { + if let Some(wif) = hex_to_wif(&hex, compressed) { add_wif_to_key(key_item, &wif); count += 1; + println!(" Added wif.decrypted"); + } else { + println!(" Skipped: invalid 32-byte hex key"); } + } else { + println!(" Skipped: wif.decrypted already present or missing hex"); } + } else { + println!(" Skipped: missing key block"); } } } } - count + Ok(count) } -fn update_single_puzzle(doc: &mut Value) -> usize { +fn update_single_puzzle(doc: &mut Value) -> Result> { if let Some(puzzle) = doc.get_mut("puzzle") { + let label = puzzle_label(puzzle, "single"); + println!(" Processing puzzle {label}"); + + let compressed = compressed_for_puzzle(puzzle, &label)?; if let Some(key_item) = puzzle.get_mut("key") { if let Some(hex) = needs_wif(key_item) { - if let Some(wif) = hex_to_wif(&hex, true) { + if let Some(wif) = hex_to_wif(&hex, compressed) { add_wif_to_key(key_item, &wif); - return 1; + println!(" Added wif.decrypted"); + return Ok(1); } + println!(" Skipped: invalid 32-byte hex key"); + } else { + println!(" Skipped: wif.decrypted already present or missing hex"); } + } else { + println!(" Skipped: missing key block"); } } - 0 + Ok(0) } fn process_jsonc_file(path: &Path) -> Result<(), Box> { @@ -102,9 +145,9 @@ fn process_jsonc_file(path: &Path) -> Result<(), Box> { .ok_or_else(|| "Failed to parse JSONC")?; let count = if doc.get("puzzles").is_some() { - update_puzzles_array(&mut doc) + update_puzzles_array(&mut doc)? } else if doc.get("puzzle").is_some() { - update_single_puzzle(&mut doc) + update_single_puzzle(&mut doc)? } else { println!(" No puzzles found"); return Ok(()); @@ -131,12 +174,15 @@ fn main() -> Result<(), Box> { "gsmg.jsonc", "bitaps.jsonc", "hash_collision.jsonc", + "rushwallet.jsonc", ]; for file in &files { let path = data_dir.join(file); if path.exists() { - process_jsonc_file(&path)?; + if let Err(err) = process_jsonc_file(&path) { + eprintln!("Failed to process {}: {err}", path.display()); + } } else { eprintln!("File not found: {}", path.display()); } diff --git a/scripts/src/main.rs b/scripts/src/main.rs index 94f9817..04b6496 100644 --- a/scripts/src/main.rs +++ b/scripts/src/main.rs @@ -149,7 +149,7 @@ async fn main() -> Result<(), Box> { let data_dir = Path::new("../data"); - let files = ["b1000.jsonc", "hash_collision.jsonc"]; + let files = ["b1000.jsonc", "hash_collision.jsonc", "rushwallet.jsonc"]; for file in &files { let path = data_dir.join(file); diff --git a/skills/boha/SKILL.md b/skills/boha/SKILL.md index 2e076e5..cf9ce33 100644 --- a/skills/boha/SKILL.md +++ b/skills/boha/SKILL.md @@ -6,7 +6,7 @@ metadata: version: "0.18.0" --- -Rust library for crypto bounties, puzzles and challenges data. Nine collections across six blockchains (Bitcoin, Ethereum, Litecoin, Monero, Decred, Arweave). All data embedded at compile time as `&'static` references. +Rust library for crypto bounties, puzzles and challenges data. Ten collections across six blockchains (Bitcoin, Ethereum, Litecoin, Monero, Decred, Arweave). All data embedded at compile time as `&'static` references. ## Install @@ -32,6 +32,7 @@ IDs follow `collection/identifier` pattern. Two exceptions have no slash. | bitimage | `bitimage/kitten` | Name string | | gsmg | `gsmg` | Single puzzle, no slash | | hash_collision | `hash_collision/sha256` | sha1, sha256, ripemd160, hash160, hash256, op_abs | +| rushwallet | `rushwallet/9` | Number 1-30 (RushWallet 2014 brainwallet contest) | | warp | `warp/challenge_1` | challenge_1-4, warp_challenge_1-2 | | zden | `zden/level_1` | snake_case level names | diff --git a/skills/boha/references/collections.md b/skills/boha/references/collections.md index 5404b80..3f3977e 100644 --- a/skills/boha/references/collections.md +++ b/skills/boha/references/collections.md @@ -58,6 +58,15 @@ let p = hash_collision::get("sha256").unwrap(); let p = boha::get("peter_todd/sha256").unwrap(); // alias works ``` +## rushwallet + +Dmitri Kryptokov / Kryptokit's RushWallet brainwallet contest (Sept 2014). 30 P2PKH targets, derivation `sha256(passphrase)` → uncompressed key. 28 passphrases recovered from contest videos / OCR / morse audio / social clue carriers; #26 is claimed on-chain with passphrase still unknown, and #30 remains unclaimed/unsolved. All 30 contest UTXOs were funded by `1GShq18eb4V6uBtqgwxkmuPTUHCtyBcNYA`. + +```rust +let p = rushwallet::get("9").unwrap(); +let phrase = p.key.and_then(|k| k.wif).and_then(|w| w.passphrase); +``` + ## warp Keybase WarpWallet challenges. 6 puzzles total - 4 solved, 2 expired and later reclaimed by Keybase. diff --git a/src/cli.rs b/src/cli.rs index 230196d..39dc4cb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1904,11 +1904,11 @@ mod tests { fn collection_help_lists_registry_names() { assert_eq!( collection_help(false), - "arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision (peter_todd), warp (warpwallet), zden" + "arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision (peter_todd), rushwallet, warp (warpwallet), zden" ); assert_eq!( collection_help(true), - "arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision (peter_todd), warp (warpwallet), zden, all" + "arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision (peter_todd), rushwallet, warp (warpwallet), zden, all" ); } } diff --git a/src/collections/mod.rs b/src/collections/mod.rs index 761308f..2b56fda 100644 --- a/src/collections/mod.rs +++ b/src/collections/mod.rs @@ -5,5 +5,6 @@ pub mod bitaps; pub mod bitimage; pub mod gsmg; pub mod hash_collision; +pub mod rushwallet; pub mod warp; pub mod zden; diff --git a/src/collections/rushwallet.rs b/src/collections/rushwallet.rs new file mode 100644 index 0000000..041531c --- /dev/null +++ b/src/collections/rushwallet.rs @@ -0,0 +1,53 @@ +//! RushWallet - Dmitri Kryptokov's 30 video brainwallet puzzles (2014). +//! +//! Each puzzle uses `sha256(passphrase)` as a private-key scalar and the +//! resulting uncompressed P2PKH address as the target. Twenty-eight +//! passphrases have been recovered locally from the contest videos, +//! frame artifacts, audio, and social clue carriers. Wallet #26 is claimed +//! on-chain with the passphrase still unknown; wallet #30 remains unclaimed. + +#[allow(unused_imports)] +use crate::{ + Address, Assets, Author, Chain, Entropy, EntropySource, Error, Key, Passphrase, Profile, + Pubkey, PubkeyFormat, Puzzle, RedeemScript, Result, Seed, Solver, Status, Transaction, + TransactionType, Wif, +}; + +include!(concat!(env!("OUT_DIR"), "/rushwallet_data.rs")); + +pub fn author() -> &'static Author { + &AUTHOR +} + +pub fn get(name: &str) -> Result<&'static Puzzle> { + let search_id = if name.contains('/') { + name.to_string() + } else { + format!("rushwallet/{}", name) + }; + + PUZZLES + .iter() + .find(|p| p.id == search_id) + .ok_or(Error::NotFound(search_id)) +} + +pub fn slice() -> &'static [Puzzle] { + PUZZLES +} + +pub fn all() -> impl Iterator { + slice().iter() +} + +pub fn solved() -> impl Iterator { + PUZZLES.iter().filter(|p| p.status == Status::Solved) +} + +pub fn unsolved() -> impl Iterator { + PUZZLES.iter().filter(|p| p.status == Status::Unsolved) +} + +pub const fn count() -> usize { + PUZZLES.len() +} diff --git a/src/lib.rs b/src/lib.rs index 7cd27a5..bdd8d21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,9 @@ pub mod version { include!(concat!(env!("OUT_DIR"), "/data_version.rs")); } -pub use collections::{arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision, warp, zden}; +pub use collections::{ + arweave, b1000, ballet, bitaps, bitimage, gsmg, hash_collision, rushwallet, warp, zden, +}; pub use puzzle::{ Address, Assets, Author, Chain, Entropy, EntropySource, IntoPuzzleNum, Key, Passphrase, Profile, Pubkey, PubkeyFormat, Puzzle, RedeemScript, Seed, Share, Shares, Solver, Status, @@ -42,12 +44,13 @@ pub enum Collection { Bitimage, Gsmg, HashCollision, + Rushwallet, Warp, Zden, } impl Collection { - pub const ALL: [Self; 9] = [ + pub const ALL: [Self; 10] = [ Self::Arweave, Self::B1000, Self::Ballet, @@ -55,6 +58,7 @@ impl Collection { Self::Bitimage, Self::Gsmg, Self::HashCollision, + Self::Rushwallet, Self::Warp, Self::Zden, ]; @@ -68,6 +72,7 @@ impl Collection { Self::Bitimage => "bitimage", Self::Gsmg => "gsmg", Self::HashCollision => "hash_collision", + Self::Rushwallet => "rushwallet", Self::Warp => "warp", Self::Zden => "zden", } @@ -82,6 +87,7 @@ impl Collection { "bitimage" => Ok(Self::Bitimage), "gsmg" => Ok(Self::Gsmg), "hash_collision" | "peter_todd" => Ok(Self::HashCollision), + "rushwallet" => Ok(Self::Rushwallet), "warp" | "warpwallet" => Ok(Self::Warp), "zden" => Ok(Self::Zden), _ => Err(Error::InvalidCollection(name.to_string())), @@ -97,6 +103,7 @@ impl Collection { Self::Bitimage => bitimage::slice(), Self::Gsmg => gsmg::slice(), Self::HashCollision => hash_collision::slice(), + Self::Rushwallet => rushwallet::slice(), Self::Warp => warp::slice(), Self::Zden => zden::slice(), } @@ -115,6 +122,7 @@ impl Collection { Self::Bitimage => bitimage::author(), Self::Gsmg => gsmg::author(), Self::HashCollision => hash_collision::author(), + Self::Rushwallet => rushwallet::author(), Self::Warp => warp::author(), Self::Zden => zden::author(), } @@ -146,6 +154,7 @@ impl Collection { } } Self::HashCollision => hash_collision::get(name), + Self::Rushwallet => rushwallet::get(name), Self::Warp => warp::get(name), Self::Zden => zden::get(name), }