├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── address.rs ├── device.rs ├── main.rs ├── pattern.rs ├── sha256.rs └── util.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /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 = "ansi_term" 7 | version = "0.11.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 10 | dependencies = [ 11 | "winapi", 12 | ] 13 | 14 | [[package]] 15 | name = "atty" 16 | version = "0.2.14" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 19 | dependencies = [ 20 | "hermit-abi", 21 | "libc", 22 | "winapi", 23 | ] 24 | 25 | [[package]] 26 | name = "autocfg" 27 | version = "1.0.1" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 30 | 31 | [[package]] 32 | name = "bitflags" 33 | version = "1.3.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 36 | 37 | [[package]] 38 | name = "bs58" 39 | version = "0.2.5" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "c95ee6bba9d950218b6cc910cf62bc9e0a171d0f4537e3627b0f54d08549b188" 42 | 43 | [[package]] 44 | name = "byteorder" 45 | version = "1.4.3" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 48 | 49 | [[package]] 50 | name = "cfg-if" 51 | version = "1.0.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 54 | 55 | [[package]] 56 | name = "cl-sys" 57 | version = "0.4.2" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "e8573fa3ff8acd6c49e8e113296c54277e82376b96c6ca6307848632cce38e44" 60 | dependencies = [ 61 | "libc", 62 | ] 63 | 64 | [[package]] 65 | name = "clap" 66 | version = "2.33.3" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 69 | dependencies = [ 70 | "ansi_term", 71 | "atty", 72 | "bitflags", 73 | "strsim", 74 | "textwrap", 75 | "unicode-width", 76 | "vec_map", 77 | ] 78 | 79 | [[package]] 80 | name = "curve25519-dalek" 81 | version = "3.2.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" 84 | dependencies = [ 85 | "byteorder", 86 | "digest", 87 | "rand_core 0.5.1", 88 | "subtle", 89 | "zeroize", 90 | ] 91 | 92 | [[package]] 93 | name = "digest" 94 | version = "0.9.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 97 | dependencies = [ 98 | "generic-array", 99 | ] 100 | 101 | [[package]] 102 | name = "enum_primitive" 103 | version = "0.1.1" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" 106 | dependencies = [ 107 | "num-traits 0.1.43", 108 | ] 109 | 110 | [[package]] 111 | name = "fuchsia-cprng" 112 | version = "0.1.1" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 115 | 116 | [[package]] 117 | name = "gcc" 118 | version = "0.3.55" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" 121 | 122 | [[package]] 123 | name = "generic-array" 124 | version = "0.14.4" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 127 | dependencies = [ 128 | "typenum", 129 | "version_check", 130 | ] 131 | 132 | [[package]] 133 | name = "getrandom" 134 | version = "0.1.16" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 137 | dependencies = [ 138 | "cfg-if", 139 | "libc", 140 | "wasi 0.9.0+wasi-snapshot-preview1", 141 | ] 142 | 143 | [[package]] 144 | name = "getrandom" 145 | version = "0.2.3" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 148 | dependencies = [ 149 | "cfg-if", 150 | "libc", 151 | "wasi 0.10.2+wasi-snapshot-preview1", 152 | ] 153 | 154 | [[package]] 155 | name = "hermit-abi" 156 | version = "0.1.19" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 159 | dependencies = [ 160 | "libc", 161 | ] 162 | 163 | [[package]] 164 | name = "libc" 165 | version = "0.2.101" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" 168 | 169 | [[package]] 170 | name = "num" 171 | version = "0.1.42" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" 174 | dependencies = [ 175 | "num-bigint", 176 | "num-complex", 177 | "num-integer", 178 | "num-iter", 179 | "num-rational", 180 | "num-traits 0.2.14", 181 | ] 182 | 183 | [[package]] 184 | name = "num-bigint" 185 | version = "0.1.44" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" 188 | dependencies = [ 189 | "num-integer", 190 | "num-traits 0.2.14", 191 | "rand 0.4.6", 192 | "rustc-serialize", 193 | ] 194 | 195 | [[package]] 196 | name = "num-complex" 197 | version = "0.1.43" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" 200 | dependencies = [ 201 | "num-traits 0.2.14", 202 | "rustc-serialize", 203 | ] 204 | 205 | [[package]] 206 | name = "num-integer" 207 | version = "0.1.44" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 210 | dependencies = [ 211 | "autocfg", 212 | "num-traits 0.2.14", 213 | ] 214 | 215 | [[package]] 216 | name = "num-iter" 217 | version = "0.1.42" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" 220 | dependencies = [ 221 | "autocfg", 222 | "num-integer", 223 | "num-traits 0.2.14", 224 | ] 225 | 226 | [[package]] 227 | name = "num-rational" 228 | version = "0.1.42" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" 231 | dependencies = [ 232 | "num-bigint", 233 | "num-integer", 234 | "num-traits 0.2.14", 235 | "rustc-serialize", 236 | ] 237 | 238 | [[package]] 239 | name = "num-traits" 240 | version = "0.1.43" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 243 | dependencies = [ 244 | "num-traits 0.2.14", 245 | ] 246 | 247 | [[package]] 248 | name = "num-traits" 249 | version = "0.2.14" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 252 | dependencies = [ 253 | "autocfg", 254 | ] 255 | 256 | [[package]] 257 | name = "ocl-core" 258 | version = "0.7.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "b96826e7291b77c26804cdf283b5f38e0e45b988043dcc2dee92b656226cd742" 261 | dependencies = [ 262 | "bitflags", 263 | "cl-sys", 264 | "enum_primitive", 265 | "libc", 266 | "num", 267 | "ocl-core-vector", 268 | "rand 0.3.23", 269 | "rustc_version", 270 | ] 271 | 272 | [[package]] 273 | name = "ocl-core-vector" 274 | version = "0.1.0" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "b4072920739958adeec5abedec51af70febc58f7fff0601aaa0827c1f3c8fefd" 277 | dependencies = [ 278 | "num", 279 | ] 280 | 281 | [[package]] 282 | name = "rand" 283 | version = "0.3.23" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" 286 | dependencies = [ 287 | "libc", 288 | "rand 0.4.6", 289 | ] 290 | 291 | [[package]] 292 | name = "rand" 293 | version = "0.4.6" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 296 | dependencies = [ 297 | "fuchsia-cprng", 298 | "libc", 299 | "rand_core 0.3.1", 300 | "rdrand", 301 | "winapi", 302 | ] 303 | 304 | [[package]] 305 | name = "rand_core" 306 | version = "0.3.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 309 | dependencies = [ 310 | "rand_core 0.4.2", 311 | ] 312 | 313 | [[package]] 314 | name = "rand_core" 315 | version = "0.4.2" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 318 | 319 | [[package]] 320 | name = "rand_core" 321 | version = "0.5.1" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 324 | dependencies = [ 325 | "getrandom 0.1.16", 326 | ] 327 | 328 | [[package]] 329 | name = "rdrand" 330 | version = "0.4.0" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 333 | dependencies = [ 334 | "rand_core 0.3.1", 335 | ] 336 | 337 | [[package]] 338 | name = "rust-crypto" 339 | version = "0.2.36" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" 342 | dependencies = [ 343 | "gcc", 344 | "libc", 345 | "rand 0.3.23", 346 | "rustc-serialize", 347 | "time", 348 | ] 349 | 350 | [[package]] 351 | name = "rustc-serialize" 352 | version = "0.3.24" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 355 | 356 | [[package]] 357 | name = "rustc_version" 358 | version = "0.1.7" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" 361 | dependencies = [ 362 | "semver", 363 | ] 364 | 365 | [[package]] 366 | name = "semver" 367 | version = "0.1.20" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" 370 | 371 | [[package]] 372 | name = "strsim" 373 | version = "0.8.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 376 | 377 | [[package]] 378 | name = "subtle" 379 | version = "2.4.1" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 382 | 383 | [[package]] 384 | name = "textwrap" 385 | version = "0.11.0" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 388 | dependencies = [ 389 | "unicode-width", 390 | ] 391 | 392 | [[package]] 393 | name = "time" 394 | version = "0.1.43" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 397 | dependencies = [ 398 | "libc", 399 | "winapi", 400 | ] 401 | 402 | [[package]] 403 | name = "typenum" 404 | version = "1.13.0" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" 407 | 408 | [[package]] 409 | name = "unicode-width" 410 | version = "0.1.8" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 413 | 414 | [[package]] 415 | name = "vec_map" 416 | version = "0.8.2" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 419 | 420 | [[package]] 421 | name = "version_check" 422 | version = "0.9.3" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 425 | 426 | [[package]] 427 | name = "wasi" 428 | version = "0.9.0+wasi-snapshot-preview1" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 431 | 432 | [[package]] 433 | name = "wasi" 434 | version = "0.10.2+wasi-snapshot-preview1" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 437 | 438 | [[package]] 439 | name = "winapi" 440 | version = "0.3.9" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 443 | dependencies = [ 444 | "winapi-i686-pc-windows-gnu", 445 | "winapi-x86_64-pc-windows-gnu", 446 | ] 447 | 448 | [[package]] 449 | name = "winapi-i686-pc-windows-gnu" 450 | version = "0.4.0" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 453 | 454 | [[package]] 455 | name = "winapi-x86_64-pc-windows-gnu" 456 | version = "0.4.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 459 | 460 | [[package]] 461 | name = "zcash-vanity" 462 | version = "0.2.9" 463 | dependencies = [ 464 | "bs58", 465 | "byteorder", 466 | "clap", 467 | "curve25519-dalek", 468 | "getrandom 0.2.3", 469 | "ocl-core", 470 | "rust-crypto", 471 | ] 472 | 473 | [[package]] 474 | name = "zeroize" 475 | version = "1.4.1" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "377db0846015f7ae377174787dd452e1c5f5a9050bc6f954911d01f116daa0cd" 478 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zcash-vanity" 3 | version = "0.2.9" 4 | authors = ["Jason Davies "] 5 | description = "Zcash vanity address generator." 6 | homepage = "https://zcash.plutomonkey.com/vanity/" 7 | repository = "https://github.com/plutomonkey/zcash-vanity" 8 | readme = "README.md" 9 | license = "MIT" 10 | keywords = ["vanity", "zcash"] 11 | categories = ["command-line-utilities", "cryptography"] 12 | 13 | [dependencies] 14 | bs58 = "0.2" 15 | byteorder = "^1.1" 16 | clap = "2" 17 | curve25519-dalek = "3" 18 | ocl-core = "^0.7" 19 | getrandom = "0.2" 20 | rust-crypto = "^0.2" 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 PlutoMonkey Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Zcash Vanity Address Generator 2 | ============================== 3 | 4 | > I just thought of something. Eventually there'll be some interest in brute force scanning bitcoin addresses to find one with the first few characters customized to your name, kind of like getting a phone number that spells out something. Just by chance I have my initials. 5 | 6 | *—Satoshi Nakamoto in an email to Hal Finney in 2009, referring to his Bitcoin address 1NSwywA5Dvuyw89sfs3oLPvLiDNGf48cPD.* 7 | 8 | Zcash-vanity is a high-throughput vanity address generator for [Zcash](https://z.cash) shielded addresses (z-addrs), i.e. those starting with "zc". 9 | 10 | You can search for addresses with particular prefixes, e.g. "zcVANiTY". The engine is written in OpenCL so that it can run on devices such as GPUs. Currently, NVIDIA's GTX 1080 Ti can locate an exact 6-character prefix (excluding "zc") in around 30 seconds. 11 | 12 | Case-insensitive matching and multiple prefixes are also supported. 13 | 14 | ## Installation 15 | 16 | The engine wrapper is written in Rust. At the moment, the easiest way to install zcash-vanity is to install [Rust](https://www.rust-lang.org), which comes with a package manager called Cargo. Then run: 17 | 18 | cargo install zcash-vanity 19 | 20 | This should download all dependencies, build and add `zcash-vanity` to your path. You will also need OpenCL installed. 21 | 22 | ## Usage 23 | 24 | **Note:** not all 58 characters can be used as the third character of the prefix; only the following may be used: `[8-9,A-Z,a-h]`. 25 | 26 | The following should list all available options: 27 | 28 | zcash-vanity --help 29 | 30 | Note that unless otherwise specified, all available OpenCL devices are used, which may include your CPU on some platforms. 31 | 32 | ## Related Work 33 | 34 | * [zcash-mini](https://github.com/FiloSottile/zcash-mini), a portable wallet generator written in Go by [Filippo Valsorda](https://blog.filippo.io/hi/), which supports vanity address generation (z-addrs) too but not using the GPU. 35 | * [vanitygen_z](https://github.com/exploitagency/vanitygen_z), a modified version of Bitcoin's vanitygen, for generating transparent Zcash addresses (t-addrs) using OpenCL. 36 | 37 | Donations gratefully received at [zcVANiTYZ1VxZp9dr6CEqfesYyfak8d6ZDFh4LLQPtHdGUb47CkpHzspFg4YV4NqsfyWkUxs4rcrzhKGsqHhXkzZsWkDaLT](zcash:zcVANiTYZ1VxZp9dr6CEqfesYyfak8d6ZDFh4LLQPtHdGUb47CkpHzspFg4YV4NqsfyWkUxs4rcrzhKGsqHhXkzZsWkDaLT). 38 | 39 | Please see for further information and new releases. 40 | -------------------------------------------------------------------------------- /src/address.rs: -------------------------------------------------------------------------------- 1 | use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; 2 | use curve25519_dalek::scalar::Scalar; 3 | use sha256::sha256_compress; 4 | use std::fmt; 5 | use util::bs58_encode_check; 6 | 7 | const PAYMENT_ADDRESS_PREFIX_LENGTH: usize = 2; 8 | const SPENDING_KEY_PREFIX_LENGTH: usize = 2; 9 | const INVIEWING_KEY_PREFIX_LENGTH: usize = 3; 10 | pub const PAYMENT_ADDRESS_PREFIX: [u8; PAYMENT_ADDRESS_PREFIX_LENGTH] = [0x16, 0x9a]; 11 | pub const SPENDING_KEY_PREFIX: [u8; SPENDING_KEY_PREFIX_LENGTH] = [0xab, 0x36]; 12 | pub const INVIEWING_KEY_PREFIX: [u8; INVIEWING_KEY_PREFIX_LENGTH] = [0xa8, 0xab, 0xd3]; 13 | 14 | /// A Zcash spending key. 15 | pub struct SpendingKey { 16 | a_sk: [u8; 32], // 252 bits; first 4 bits are always 0 17 | } 18 | 19 | /// A Zcash incoming viewing key. 20 | pub struct ViewingKey { 21 | pub a_pk: [u8; 32], 22 | pub sk_enc: [u8; 32], 23 | } 24 | 25 | /// A Zcash payment address. 26 | pub struct PaymentAddress { 27 | pub a_pk: [u8; 32], 28 | pub pk_enc: [u8; 32], 29 | } 30 | 31 | impl fmt::Display for SpendingKey { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | let mut data = [0u8; SPENDING_KEY_PREFIX_LENGTH + 32]; 34 | { 35 | let (prefix, rest) = data.split_at_mut(SPENDING_KEY_PREFIX_LENGTH); 36 | prefix.copy_from_slice(&SPENDING_KEY_PREFIX); 37 | rest.copy_from_slice(&self.a_sk); 38 | } 39 | 40 | f.write_str(bs58_encode_check(&data).as_str()) 41 | } 42 | } 43 | 44 | impl fmt::Display for ViewingKey { 45 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 46 | let mut data = [0u8; INVIEWING_KEY_PREFIX_LENGTH + 32 * 2]; 47 | { 48 | let (prefix, rest) = data.split_at_mut(INVIEWING_KEY_PREFIX_LENGTH); 49 | prefix.copy_from_slice(&INVIEWING_KEY_PREFIX); 50 | rest[..32].copy_from_slice(&self.a_pk); 51 | rest[32..].copy_from_slice(&self.sk_enc); 52 | } 53 | 54 | f.write_str(bs58_encode_check(&data).as_str()) 55 | } 56 | } 57 | 58 | impl fmt::Display for PaymentAddress { 59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 | let mut data = [0u8; PAYMENT_ADDRESS_PREFIX_LENGTH + 32 * 2]; 61 | { 62 | let (prefix, rest) = data.split_at_mut(PAYMENT_ADDRESS_PREFIX_LENGTH); 63 | prefix.copy_from_slice(&PAYMENT_ADDRESS_PREFIX); 64 | rest[..32].copy_from_slice(&self.a_pk); 65 | rest[32..].copy_from_slice(&self.pk_enc); 66 | } 67 | 68 | f.write_str(bs58_encode_check(&data).as_str()) 69 | } 70 | } 71 | 72 | impl SpendingKey { 73 | /// Creates a new spending key instance for a given 252-bit *a_sk*. 74 | /// *a_sk* is represented by 32 bytes; the first four bits of the first byte are zeroed. 75 | pub fn new(a_sk_u256: [u8; 32]) -> SpendingKey { 76 | let mut a_sk_u252 = [0u8; 32]; 77 | a_sk_u252.copy_from_slice(&a_sk_u256); 78 | a_sk_u252[0] &= 0x0f; 79 | 80 | SpendingKey { a_sk: a_sk_u252 } 81 | } 82 | 83 | /// Calculates the payment address for this spending key 84 | pub fn address(&self) -> PaymentAddress { 85 | let viewing_key = self.viewing_key(); 86 | let pk = &Scalar::from_bits(viewing_key.sk_enc) * &ED25519_BASEPOINT_TABLE; 87 | let pk_enc = pk.to_montgomery().to_bytes(); 88 | 89 | PaymentAddress { 90 | a_pk: viewing_key.a_pk, 91 | pk_enc, 92 | } 93 | } 94 | 95 | /// Computes an incoming viewing key for this spending key. 96 | pub fn viewing_key(&self) -> ViewingKey { 97 | let mut a_pk = [0u8; 32]; 98 | let mut sk_enc = [0u8; 32]; 99 | 100 | pseudorandom_function_a_pk(&mut a_pk, &self.a_sk); 101 | pseudorandom_function_sk_enc(&mut sk_enc, &self.a_sk); 102 | clamp_curve25519(&mut sk_enc); 103 | 104 | ViewingKey { 105 | a_pk, 106 | sk_enc, 107 | } 108 | } 109 | } 110 | 111 | pub fn pseudorandom_function_a_pk(a_pk: &mut [u8; 32], a_sk: &[u8; 32]) { 112 | pseudorandom_function(a_pk, a_sk, 0) 113 | } 114 | 115 | pub fn pseudorandom_function_sk_enc(sk_enc: &mut [u8; 32], a_sk: &[u8; 32]) { 116 | pseudorandom_function(sk_enc, a_sk, 1) 117 | } 118 | 119 | fn pseudorandom_function(dst: &mut [u8; 32], input: &[u8; 32], t: u8) { 120 | let mut data = [0u8; 64]; 121 | data[..32].copy_from_slice(input); 122 | data[0] = 0xc0 | (data[0] & 0x0f); 123 | data[32] = t; 124 | sha256_compress(dst, &data); 125 | } 126 | 127 | fn clamp_curve25519(key: &mut [u8; 32]) { 128 | key[0] &= 248; 129 | key[31] &= 127; 130 | key[31] |= 64; 131 | } 132 | 133 | #[cfg(test)] 134 | mod test { 135 | use bs58; 136 | use super::*; 137 | 138 | static ENCODED: [(&str, &str, &str); 10] = [ 139 | ( 140 | "SKxt8pwrQipUL5KgZUcBAqyLj9R1YwMuRRR3ijGMCwCCqchmi8ut", 141 | "zcJLC7a3aRJohMNCVjSZQ8jFuofhAHJNAY4aX5soDkYfgNejzKnEZbucJmVibLWCwK8dyyfDhNhf3foXDDTouweC382LcX5", 142 | "ZiVKYQyUcyAJLKwcosSeDxkGRhygFdAPWsr3m8UgjC5X85yqNyLTtJJJYNH83Wf2AQKU6TZsd65MXBZLFj6eSCAFcnCFuVCFS", 143 | ), 144 | ( 145 | "SKxoo5QkFQgTbdc6EWRKyHPMdmtNDJhqudrAVhen9b4kjCwN6CeV", 146 | "zcRYvLiURno1LhXq95e8avXFcH2fKKToSFfhqaVKTy8mGH7i6SJbfuWcm4h9rEA6DvswrbxDhFGDQgpdDYV8zwUoHvwNvFX", 147 | "ZiVKfdhhmQ1fpXaxyW5zRXw4Dhg9cbKRgK7mNFoBLiKjiBZiHJYJTpV2gNMDMPY9sRC96vnKZcnTMSi65SKPyL4WNQNm9PT5H", 148 | ), 149 | ( 150 | "SKxsVGKsCESoVb3Gfm762psjRtGHmjmv7HVjHckud5MnESfktUuG", 151 | "zcWGguu2UPfNhh1ygWW9Joo3osvncsuehtz5ewvXd78vFDdnDCRNG6QeKSZpwZmYmkfEutPVf8HzCfBytqXWsEcF2iBAM1e", 152 | "ZiVKkMUGwx4GgtwxTedRHYewVVskWicz8APQgdcYmvUsiLYgSh3cLAa8TwiR3shyNngGbLiUbYMkZ8F1giXmmcED98rDMwNSG", 153 | ), 154 | ( 155 | "SKxp72QGQ2qtovHSoVnPp8jRFQpHBhG1xF8s27iRFjPXXkYMQUA6", 156 | "zcWZomPYMEjJ49S4UHcvTnhjYqogfdYJuEDMURDpbkrz94bkzdTdJEZKWkkpQ8nK62eyLkZCvLZDFtLC2Cq5BmEK3WCKGMN", 157 | "ZiVKkeb8STw7kpJQsjRCQKovQBciPcfjkpajuuS25DTXSQSVasnq4BkyaMLBBxAkZ8fv6f18woWgaA8W7kGvYp1C1ESaWGjwV", 158 | ), 159 | ( 160 | "SKxpmLdykLu3xxSXtw1EA7iLJnXu8hFh8hhmW1B2J2194ijh5CR4", 161 | "zcgjj3fJF59QGBufopx3F51jCjUpXbgEzec7YQT6jRt4Ebu5EV3AW4jHPN6ZdXhmygBvQDRJrXoZLa3Lkh5GqnsFUzt7Qok", 162 | "ZiVKvpWQiDpxAvWTMLkjjSbCiBGc4kXhtkgAJfW1JVbCTUY4YaAVvVZzCz6wspG9qttciRFLEXm3HLQAmssFbUp9uPEkP3uu5"), 163 | ( 164 | "SKxny894fJe2rmZjeuoE6GVfNkWoXfPp8337VrLLNWG56FfQtuS1", 165 | "zcbxovDeXGJJikZH5wQkcQvYx1gzsRt9mR5UnQir6NY8hhPHdgK7z7dE1vfa55Bq3JHJu7isfuWQGYrvMbLnud74z2vS4tS", 166 | "ZiVKr3bHGa79Kpy1zx2rC9xYd11tGvsY6fSvn2k1aEx97Z19WcMMW7KeSgE6VhJrAZ4RE6AmDU3oKqoXQiZfuHqzEBhW9o5UT", 167 | ), 168 | ( 169 | "SKxoo5QkFQgTbdc6EWRKyHPMdmtNDJhqudrAVhen9b4kjCwN6CeV", 170 | "zcRYvLiURno1LhXq95e8avXFcH2fKKToSFfhqaVKTy8mGH7i6SJbfuWcm4h9rEA6DvswrbxDhFGDQgpdDYV8zwUoHvwNvFX", 171 | "ZiVKfdhhmQ1fpXaxyW5zRXw4Dhg9cbKRgK7mNFoBLiKjiBZiHJYJTpV2gNMDMPY9sRC96vnKZcnTMSi65SKPyL4WNQNm9PT5H", 172 | ), 173 | ( 174 | "SKxsVGKsCESoVb3Gfm762psjRtGHmjmv7HVjHckud5MnESfktUuG", 175 | "zcWGguu2UPfNhh1ygWW9Joo3osvncsuehtz5ewvXd78vFDdnDCRNG6QeKSZpwZmYmkfEutPVf8HzCfBytqXWsEcF2iBAM1e", 176 | "ZiVKkMUGwx4GgtwxTedRHYewVVskWicz8APQgdcYmvUsiLYgSh3cLAa8TwiR3shyNngGbLiUbYMkZ8F1giXmmcED98rDMwNSG", 177 | ), 178 | ( 179 | "SKxp72QGQ2qtovHSoVnPp8jRFQpHBhG1xF8s27iRFjPXXkYMQUA6", 180 | "zcWZomPYMEjJ49S4UHcvTnhjYqogfdYJuEDMURDpbkrz94bkzdTdJEZKWkkpQ8nK62eyLkZCvLZDFtLC2Cq5BmEK3WCKGMN", 181 | "ZiVKkeb8STw7kpJQsjRCQKovQBciPcfjkpajuuS25DTXSQSVasnq4BkyaMLBBxAkZ8fv6f18woWgaA8W7kGvYp1C1ESaWGjwV", 182 | ), 183 | ( 184 | "SKxpmLdykLu3xxSXtw1EA7iLJnXu8hFh8hhmW1B2J2194ijh5CR4", 185 | "zcgjj3fJF59QGBufopx3F51jCjUpXbgEzec7YQT6jRt4Ebu5EV3AW4jHPN6ZdXhmygBvQDRJrXoZLa3Lkh5GqnsFUzt7Qok", 186 | "ZiVKvpWQiDpxAvWTMLkjjSbCiBGc4kXhtkgAJfW1JVbCTUY4YaAVvVZzCz6wspG9qttciRFLEXm3HLQAmssFbUp9uPEkP3uu5", 187 | ), 188 | ]; 189 | 190 | #[test] 191 | fn spending_keys_to_payment_addresses() { 192 | for &(spending_key_encoded, payment_address_encoded, _) in ENCODED.iter() { 193 | let mut a_sk = [0u8; 32]; 194 | a_sk.copy_from_slice( 195 | &bs58::decode(spending_key_encoded).into_vec().unwrap()[2..34], 196 | ); 197 | let spending_key = SpendingKey::new(a_sk); 198 | assert_eq!(spending_key_encoded, spending_key.to_string()); 199 | let payment_address = spending_key.address(); 200 | assert_eq!(payment_address_encoded, payment_address.to_string()); 201 | } 202 | } 203 | 204 | #[test] 205 | fn spending_keys_to_viewing_keys() { 206 | for &(spending_key_encoded, _, viewing_key_encoded) in ENCODED.iter() { 207 | let mut a_sk = [0u8; 32]; 208 | a_sk.copy_from_slice( 209 | &bs58::decode(spending_key_encoded).into_vec().unwrap()[2..34], 210 | ); 211 | let spending_key = SpendingKey::new(a_sk); 212 | assert_eq!(spending_key_encoded, spending_key.to_string()); 213 | let viewing_key = spending_key.viewing_key(); 214 | assert_eq!(viewing_key_encoded, viewing_key.to_string()); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/device.rs: -------------------------------------------------------------------------------- 1 | use address::{PAYMENT_ADDRESS_PREFIX, pseudorandom_function_a_pk, SpendingKey}; 2 | 3 | use bs58; 4 | use byteorder::{BigEndian, ByteOrder}; 5 | use core; 6 | use core::{KernelArg, CommandExecutionStatus, ContextProperties, Event, EventInfo, EventInfoResult, DeviceId, PlatformId}; 7 | use getrandom::getrandom; 8 | use std::{cmp, str, thread}; 9 | use std::io::{self, Write}; 10 | use std::sync::atomic; 11 | use std::sync::mpsc::Sender; 12 | use std::time::Duration; 13 | use std::ffi::CString; 14 | use util::clear_console_line_80; 15 | 16 | const MAX_DATA: usize = 1 + 1024; 17 | const ITERATIONS_PER_THREAD: usize = 1024; 18 | 19 | pub fn vanity_device(finished: &atomic::AtomicBool, tx: &Sender, platform: PlatformId, device: DeviceId, patterns_sorted: &[String], pattern_words: &[u64], single_match: bool) { 20 | 21 | let pattern_count = patterns_sorted.len(); 22 | let pattern_count_log2 = (32 - ((pattern_words.len()/2 - 1) as u32).leading_zeros()) as u32; 23 | 24 | let context_properties = ContextProperties::new().platform(platform); 25 | let context = core::create_context(Some(&context_properties), &[device], None, None).unwrap(); 26 | let queue = core::create_command_queue(&context, &device, None).unwrap(); 27 | let program = core::create_program_with_source(&context, &[ 28 | CString::new(SRC 29 | .replace("ITERATIONS_PER_THREAD", &ITERATIONS_PER_THREAD.to_string()) 30 | .replace("MAX_DATA", &MAX_DATA.to_string()) 31 | ).unwrap() 32 | ]).unwrap(); 33 | core::build_program(&program, None::<&[()]>, &CString::new("").unwrap(), None, None).unwrap(); 34 | let compress = core::create_kernel(&program, "compress").unwrap(); 35 | 36 | let mut seed = [0u8; 32]; 37 | let mut data = [0u32; MAX_DATA]; 38 | 39 | let dev_seed = unsafe { core::create_buffer(&context, core::MEM_READ_ONLY, 32, None::<&[u8]>).unwrap() }; 40 | let dev_patterns = unsafe { core::create_buffer(&context, core::MEM_READ_ONLY, pattern_words.len(), None::<&[u64]>).unwrap() }; 41 | let dev_out = unsafe { core::create_buffer(&context, core::MEM_READ_WRITE, MAX_DATA, None::<&[u32]>).unwrap() }; 42 | 43 | unsafe { 44 | core::enqueue_write_buffer(&queue, &dev_out, false, 0, &data, None::, None::<&mut Event>).unwrap(); 45 | core::enqueue_write_buffer(&queue, &dev_patterns, false, 0, pattern_words, None::, None::<&mut Event>).unwrap(); 46 | } 47 | 48 | let local_size = match core::get_kernel_work_group_info(&compress, &device, core::KernelWorkGroupInfo::WorkGroupSize) { 49 | core::KernelWorkGroupInfoResult::WorkGroupSize(size) => [size, 0, 0], 50 | _ => panic!("Unable to determine work group size."), 51 | }; 52 | 53 | let global_size = match core::get_device_info(&device, core::DeviceInfo::MaxComputeUnits) { 54 | core::DeviceInfoResult::MaxComputeUnits(compute_units) => [local_size[0] * compute_units as usize * 4, 1, 1], 55 | _ => panic!("Unable to determine number of compute units."), 56 | }; 57 | assert!(global_size[0] < (1 << 28)); 58 | 59 | let device_little_endian = match core::get_device_info(&device, core::DeviceInfo::EndianLittle) { 60 | core::DeviceInfoResult::EndianLittle(d) => d, 61 | _ => cfg!(target_endian = "little"), 62 | }; 63 | 64 | let mut a_pk = [0u8; 32]; 65 | let mut unencoded_data = [0u8; 2 + 32*2 + 4]; 66 | unencoded_data[..2].copy_from_slice(&PAYMENT_ADDRESS_PREFIX); 67 | 68 | let one_millis = Duration::from_millis(1); 69 | let mut stderr = io::stderr(); 70 | 'outer: loop { 71 | getrandom(&mut seed).unwrap(); 72 | if device_little_endian { 73 | seed[3] = 0xc0 | (seed[3] & 0x0f); 74 | } else { 75 | seed[0] = 0xc0 | (seed[0] & 0x0f); 76 | } 77 | 78 | unsafe { core::enqueue_write_buffer(&queue, &dev_seed, false, 0, &seed, None::, None::<&mut Event>).unwrap(); } 79 | 80 | core::set_kernel_arg(&compress, 0, KernelArg::Mem::(&dev_seed)).unwrap(); 81 | core::set_kernel_arg(&compress, 1, KernelArg::Mem::(&dev_patterns)).unwrap(); 82 | core::set_kernel_arg(&compress, 2, KernelArg::Scalar(pattern_count as u32)).unwrap(); 83 | core::set_kernel_arg(&compress, 3, KernelArg::Scalar(pattern_count_log2)).unwrap(); 84 | core::set_kernel_arg(&compress, 4, KernelArg::Mem::(&dev_out)).unwrap(); 85 | 86 | unsafe { core::enqueue_kernel(&queue, &compress, 1, None, &global_size, Some(local_size), None::, None::<&mut Event>).unwrap(); } 87 | 88 | let mut event = Event::null(); 89 | unsafe { core::enqueue_read_buffer(&queue, &dev_out, false, 0, &mut data, None::, Some(&mut event)).unwrap(); } 90 | 91 | core::flush(&queue).unwrap(); 92 | sleep_until_complete(&event, &one_millis); 93 | 94 | if finished.load(atomic::Ordering::Relaxed) { 95 | break; 96 | } 97 | 98 | if data[0] != 0 { 99 | 'next_candidate: for chunk in data[1..cmp::min(MAX_DATA, 1 + 8 * data[0] as usize)].chunks(8) { 100 | BigEndian::write_u32_into(chunk, &mut seed); 101 | pseudorandom_function_a_pk(&mut a_pk, &seed); 102 | unencoded_data[2..34].copy_from_slice(&a_pk); 103 | let encoded_data = bs58::encode(unencoded_data.as_ref()).into_string(); 104 | if match patterns_sorted.binary_search(&encoded_data) { 105 | Ok(_) => false, 106 | Err(d) => d == 0 || !encoded_data.starts_with(&patterns_sorted[d - 1]), 107 | } { 108 | continue 'next_candidate; 109 | } 110 | 111 | let spending_key = SpendingKey::new(seed); 112 | clear_console_line_80(&mut stderr); 113 | print!("{}\n{}\n{}\n", spending_key.address(), spending_key, spending_key.viewing_key()); 114 | io::stdout().flush().unwrap(); 115 | 116 | if single_match { 117 | break 'outer; 118 | } 119 | } 120 | } 121 | 122 | let zero_count = [0u32]; 123 | unsafe { core::enqueue_write_buffer(&queue, &dev_out, false, 0, &zero_count, None::, None::<&mut Event>).unwrap(); } 124 | 125 | tx.send((ITERATIONS_PER_THREAD * global_size[0]) as u64).unwrap(); 126 | } 127 | 128 | core::finish(&queue).unwrap(); 129 | } 130 | 131 | /// Avoid CPU busy-wait on NVIDIA platforms. 132 | fn sleep_until_complete(event: &Event, &sleep_time: &Duration) { 133 | loop { 134 | match core::get_event_info(&event, EventInfo::CommandExecutionStatus) { 135 | EventInfoResult::CommandExecutionStatus(CommandExecutionStatus::Complete) => break, 136 | EventInfoResult::CommandExecutionStatus(_) => thread::sleep(sleep_time), 137 | _ => panic!("Unexpected EventInfoResult."), 138 | } 139 | } 140 | } 141 | 142 | const SRC: &'static str = r#" 143 | typedef unsigned char uint8_t; 144 | typedef unsigned int uint32_t; 145 | typedef unsigned long uint64_t; 146 | 147 | __constant uint32_t K[64] = { 148 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 149 | 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 150 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 151 | 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 152 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 153 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 154 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 155 | 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 156 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 157 | 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 158 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 159 | }; 160 | 161 | __constant uint32_t H[8] = { 162 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 163 | 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 164 | }; 165 | 166 | inline uint32_t rotr(const uint32_t x, const uint32_t n) { 167 | return (x >> n) | (x << (32 - n)); 168 | } 169 | 170 | inline uint32_t shr(const uint32_t x, const uint32_t n) { 171 | return x >> n; 172 | } 173 | 174 | uint32_t Ch(uint32_t x, uint32_t y, uint32_t z) { return z ^ (x & (y ^ z)); } 175 | uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) { return (x & y) ^ (z & (x ^ y)); } 176 | 177 | uint32_t Sigma0(uint32_t x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); } 178 | uint32_t Sigma1(uint32_t x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); } 179 | uint32_t sigma0(uint32_t x) { return rotr(x, 7) ^ rotr(x, 18) ^ shr(x, 3); } 180 | uint32_t sigma1(uint32_t x) { return rotr(x, 17) ^ rotr(x, 19) ^ shr(x, 10); } 181 | 182 | __kernel 183 | void compress( 184 | __global uint8_t *restrict a_sk, 185 | __global uint64_t *restrict patterns, 186 | uint32_t pattern_count, 187 | uint32_t pattern_count_log2, 188 | __global uint32_t *restrict out) { 189 | 190 | uint32_t W[64]; 191 | 192 | // Copy the random a_sk seed to W[0..8]. 193 | for (size_t i = 0; i < 2; ++i) { 194 | ((uint4 *)W)[i] = ((__global uint4 *)a_sk)[i]; 195 | } 196 | 197 | // Toggle bits to ensure each thread starts with a different a_sk. The first 4 bits should not 198 | // be touched; this is enforced by ensuring the global work size is smaller than 1<<28, which is 199 | // done by an assert on the host. 200 | W[0] ^= get_global_id(0); 201 | 202 | // Set remainder of input words to zero (pk_enc) as we assume they won't affect our base-58 203 | // address prefix (and computing pk_enc is expensive). 204 | for (size_t i = 8; i < 16; ++i) { 205 | W[i] = 0; 206 | } 207 | 208 | for (size_t iteration = 0; iteration < ITERATIONS_PER_THREAD; ++iteration) { 209 | 210 | for (size_t i = 16; i < 64; ++i) { 211 | W[i] = sigma1(W[i-2]) + W[i-7] + sigma0(W[i-15]) + W[i-16]; 212 | } 213 | 214 | uint32_t state[8]; 215 | 216 | for (size_t i = 0; i < 8; ++i) { 217 | state[i] = H[i]; 218 | } 219 | 220 | for (size_t i = 0; i < 64; ++i) { 221 | uint32_t T1 = state[7] + Sigma1(state[4]) + Ch(state[4], state[5], state[6]) + K[i] + W[i], 222 | T2 = Sigma0(state[0]) + Maj(state[0], state[1], state[2]); 223 | 224 | state[7] = state[6]; 225 | state[6] = state[5]; 226 | state[5] = state[4]; 227 | state[4] = state[3] + T1; 228 | state[3] = state[2]; 229 | state[2] = state[1]; 230 | state[1] = state[0]; 231 | state[0] = T1 + T2; 232 | } 233 | 234 | // We only need the first two words. 235 | for (size_t i = 0; i < 2; i++) { 236 | state[i] += H[i]; 237 | } 238 | 239 | uint64_t word = ((uint64_t)state[0] << 32) | (uint64_t)state[1]; 240 | 241 | size_t lo = 0, mid = 0, hi = pattern_count; 242 | for (size_t depth = 0; depth <= pattern_count_log2; ++depth) { 243 | mid = (lo + hi) >> 1; 244 | if (patterns[2*mid] <= word) { 245 | lo = mid + (lo == hi ? 0 : 1); 246 | } else { 247 | hi = mid; 248 | } 249 | } 250 | if (0 < lo && word <= patterns[2*(lo-1)+1]) { 251 | uint32_t offset = atomic_inc(out); 252 | if (1 + 8*(offset+1) <= MAX_DATA) { 253 | for (size_t i = 0; i < 8; ++i) { 254 | out[1 + 8*offset + i] = W[i]; 255 | } 256 | } 257 | } 258 | 259 | ++W[1]; 260 | } 261 | }"#; 262 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "clippy", deny(warnings))] 2 | #![cfg_attr(feature = "clippy", feature(plugin))] 3 | #![cfg_attr(feature = "clippy", plugin(clippy))] 4 | #![cfg_attr(feature = "clippy", allow(too_many_arguments))] 5 | 6 | extern crate bs58; 7 | extern crate byteorder; 8 | extern crate clap; 9 | extern crate crypto; 10 | extern crate curve25519_dalek; 11 | extern crate ocl_core as core; 12 | extern crate getrandom; 13 | 14 | mod address; 15 | mod device; 16 | mod pattern; 17 | mod sha256; 18 | mod util; 19 | 20 | use clap::{App, Arg}; 21 | use core::{PlatformInfo, DeviceInfo, DeviceId, PlatformId}; 22 | use pattern::Pattern; 23 | use std::{thread, time}; 24 | use std::fs::File; 25 | use std::io::{self, Read, Write}; 26 | use std::sync::Arc; 27 | use std::sync::atomic; 28 | use std::sync::mpsc::{self, Sender, Receiver}; 29 | 30 | fn main() { 31 | let matches = App::new("Zcash Vanity Address Generator") 32 | .version(env!("CARGO_PKG_VERSION")) 33 | .about("Generates Zcash shielded addresses (\"z-addrs\") that match given prefixes.") 34 | .arg(Arg::with_name("device") 35 | .short("d") 36 | .takes_value(true) 37 | .number_of_values(1) 38 | .multiple(true) 39 | .help("OpenCL device string: :.\n\ 40 | Specify multiple times for multiple devices, e.g. -d 0:0 -d 0:1.\n\ 41 | If not specified, uses all available platforms and devices.")) 42 | .arg(Arg::with_name("file") 43 | .short("f") 44 | .takes_value(true) 45 | .help("Load prefixes from file, one per line.\nCan be combined with --insensitive.")) 46 | .arg(Arg::with_name("insensitive") 47 | .short("i") 48 | .help("Case-insensitive prefix search.")) 49 | .arg(Arg::with_name("continue") 50 | .short("c") 51 | .help("Continue searching after a match is found.")) 52 | .arg(Arg::with_name("pattern") 53 | .index(1) 54 | .help("Zcash address prefix to be matched.")) 55 | .after_help("Matches will be printed to stdout; 3 lines per match (z-addr, spending key, incoming viewing key).\n\ 56 | You can import the spending key to your Zcash client using `zcash-cli z_importkey `.") 57 | .get_matches(); 58 | 59 | let mut device_specifiers = if matches.is_present("device") { 60 | matches.values_of("device").unwrap().map(|s| { 61 | let mut values = s.split(':'); 62 | (values.next().unwrap().parse::().unwrap(), values.next().unwrap().parse::().unwrap()) 63 | }).collect() 64 | } else { 65 | vec![] 66 | }; 67 | device_specifiers.sort(); 68 | let mut device_specifiers_iter = device_specifiers.into_iter(); 69 | 70 | let mut stderr = io::stderr(); 71 | let mut devices = vec![]; 72 | let mut device_specifier = device_specifiers_iter.next(); 73 | let all_devices = device_specifier.is_none(); 74 | for (i, &platform) in core::get_platform_ids().unwrap().iter().enumerate() { 75 | writeln!(&mut stderr, "Available devices on platform {}:", core::get_platform_info(&platform, PlatformInfo::Name)).unwrap(); 76 | for (j, &device) in core::get_device_ids(&platform, None, None).unwrap().iter().enumerate() { 77 | writeln!(&mut stderr, " {}:{} {}", i, j, core::get_device_info(device, DeviceInfo::Name)).unwrap(); 78 | if all_devices || Some((i, j)) == device_specifier { 79 | devices.push((platform, device)); 80 | device_specifier = device_specifiers_iter.next(); 81 | } 82 | } 83 | } 84 | stderr.flush().unwrap(); 85 | 86 | let mut patterns = if let Some(filename) = matches.value_of("file") { 87 | let mut file = File::open(&filename).unwrap(); 88 | let mut contents = String::new(); 89 | file.read_to_string(&mut contents).unwrap(); 90 | contents.lines().map(|s| Pattern::new(s.to_string()).unwrap()).collect() 91 | } else { 92 | vec![Pattern::new(matches.value_of("pattern").unwrap_or("").to_string()).unwrap()] 93 | }; 94 | 95 | if matches.is_present("insensitive") { 96 | patterns = patterns.iter().flat_map(|p| p.case_insensitive()).collect() 97 | } 98 | 99 | vanity(&devices, &patterns, !matches.is_present("continue")); 100 | } 101 | 102 | fn vanity(devices: &[(PlatformId, DeviceId)], patterns: &[Pattern], single_match: bool) { 103 | let mut pattern_prefixes: Vec = patterns.into_iter().map(|p| p.prefix.to_owned()).collect(); 104 | pattern_prefixes.sort(); 105 | 106 | let mut pattern_ranges: Vec<(u64, u64)> = patterns.iter().map(|p| p.range).collect(); 107 | pattern_ranges.sort_by_key(|r| r.0); 108 | 109 | // TODO: the pairs in pattern_words are guaranteed to be sorted, but only by virtue of 110 | // lexicographic ordering of the default base58 alphabet being equivalent to ordering of the 111 | // big-endian data. 112 | let mut pattern_words = vec![]; 113 | let mut search_size = 0.0_f64; 114 | for (from, to) in pattern_ranges { 115 | pattern_words.push(from); 116 | pattern_words.push(to); 117 | search_size += 1.0_f64 + (to - from) as f64; 118 | } 119 | 120 | let (tx, rx): (Sender, Receiver) = mpsc::channel(); 121 | let pattern_prefixes = Arc::new(pattern_prefixes); 122 | let pattern_words = Arc::new(pattern_words); 123 | let finished = Arc::new(atomic::AtomicBool::new(false)); 124 | 125 | for &device_specifier in devices { 126 | let tx = tx.clone(); 127 | let pattern_prefixes = pattern_prefixes.clone(); 128 | let pattern_words = pattern_words.clone(); 129 | let finished = finished.clone(); 130 | thread::spawn(move || { 131 | device::vanity_device(&*finished, &tx, device_specifier.0, device_specifier.1, &*pattern_prefixes, &*pattern_words, single_match); 132 | finished.store(true, atomic::Ordering::Relaxed); 133 | }); 134 | } 135 | drop(tx); 136 | 137 | let difficulty = 2.0_f64.powi(64) / search_size; 138 | let start = time::Instant::now(); 139 | let mut cumulative_work = 0u64; 140 | let mut now = start; 141 | let mut stderr = io::stderr(); 142 | 143 | while let Ok(work) = rx.recv() { 144 | cumulative_work += work; 145 | let now_elapsed = now.elapsed(); 146 | 147 | if now_elapsed.as_secs() > 0 { 148 | let start_elapsed = start.elapsed(); 149 | let start_elapsed_secs = start_elapsed.as_secs() as f64 + f64::from(start_elapsed.subsec_nanos()) / 1_000_000_000.0; 150 | let now_elapsed_secs = now_elapsed.as_secs() as f64 + f64::from(now_elapsed.subsec_nanos()) / 1_000_000_000.0; 151 | 152 | let rate = cumulative_work as f64 / now_elapsed_secs; 153 | let lambda = rate / difficulty; 154 | let probability = 1. - (-start_elapsed_secs * lambda).exp(); 155 | 156 | write!(&mut stderr, "\rElapsed: {:.0}/{:.0}s Rate: {:.0}/s Prob: {:.2}%", start_elapsed_secs, 1. / lambda, rate, 100. * probability).unwrap(); 157 | stderr.flush().unwrap(); 158 | 159 | cumulative_work = 0; 160 | now += now_elapsed; 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/pattern.rs: -------------------------------------------------------------------------------- 1 | use address::PAYMENT_ADDRESS_PREFIX; 2 | use bs58; 3 | use byteorder::{BigEndian, ByteOrder}; 4 | use std::fmt; 5 | 6 | #[derive(Clone)] 7 | pub struct Pattern { 8 | pub prefix: String, 9 | pub range: (u64, u64), 10 | } 11 | 12 | impl fmt::Display for Pattern { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | f.write_str(self.prefix.as_str()) 15 | } 16 | } 17 | 18 | impl Pattern { 19 | pub fn new(prefix: String) -> Result { 20 | match prefix_to_range_u64(prefix.as_str()) { 21 | Ok(range) => Ok(Pattern { 22 | prefix, 23 | range, 24 | }), 25 | Err(err) => Err(err), 26 | } 27 | } 28 | 29 | pub fn case_insensitive(&self) -> Vec { 30 | let prefix_bytes = self.prefix.as_bytes().to_owned(); 31 | let mut patterns = vec![]; 32 | 33 | let alpha = bs58::alphabet::DEFAULT; 34 | let mut rev = [0xff; 128]; 35 | for (i, &c) in alpha.iter().enumerate() { 36 | rev[c as usize] = i; 37 | } 38 | for (i, &c) in alpha.iter().enumerate() { 39 | let lower = (c as char).to_lowercase().next().unwrap() as usize; 40 | if lower != c as usize && rev[lower] != 0xff { 41 | rev[c as usize] = rev[lower]; 42 | rev[lower] = i; 43 | } 44 | } 45 | 46 | let mut i = 0; 47 | let mut max = 1; 48 | while i < max { 49 | let mut tmp = prefix_bytes.clone().to_owned(); 50 | let mut k = 1u64; 51 | for c in &mut tmp { 52 | if alpha[rev[*c as usize]] != *c { 53 | if i & k != 0 { 54 | *c = alpha[rev[*c as usize]]; 55 | } 56 | k <<= 1; 57 | } 58 | } 59 | max = k; 60 | 61 | if let Ok(pattern) = Pattern::new(String::from_utf8(tmp).unwrap()) { 62 | patterns.push(pattern); 63 | } 64 | i += 1; 65 | } 66 | patterns 67 | } 68 | } 69 | 70 | fn prefix_to_range_u64(prefix: &str) -> Result<(u64, u64), String> { 71 | // 2-byte prefix, 32-byte a_pk, 32-byte pk_enc, 4-byte check 72 | let mut address_data = [0u8; 2 + 32 * 2 + 4]; 73 | address_data[..2].copy_from_slice(&PAYMENT_ADDRESS_PREFIX); 74 | let address_00 = bs58::encode(address_data.as_ref()).into_string(); 75 | for d in address_data.iter_mut().skip(2) { 76 | *d = 0xff; 77 | } 78 | let address_ff = bs58::encode(address_data.as_ref()).into_string(); 79 | 80 | let suffix_length = address_ff.len() - prefix.len(); 81 | let prefix_1 = prefix.to_owned() + &"1".repeat(suffix_length); 82 | let prefix_z = prefix.to_owned() + &"z".repeat(suffix_length); 83 | 84 | if prefix_z < address_00 { 85 | return Err(format!( 86 | "Invalid z-addr: {} < {}", 87 | prefix, 88 | &address_00[..prefix.len()] 89 | )); 90 | } 91 | if prefix_1 > address_ff { 92 | return Err(format!( 93 | "Invalid z-addr: {} > {}", 94 | prefix, 95 | &address_ff[..prefix.len()] 96 | )); 97 | } 98 | 99 | let pattern_1 = if prefix_1 < address_00 { 100 | ::min_value() 101 | } else { 102 | bs58::decode(prefix_1).into(address_data.as_mut()).unwrap(); 103 | BigEndian::read_u64(&address_data[2..10]) 104 | }; 105 | 106 | let pattern_z = if prefix_z > address_ff { 107 | ::max_value() 108 | } else { 109 | bs58::decode(prefix_z).into(address_data.as_mut()).unwrap(); 110 | BigEndian::read_u64(&address_data[2..10]) 111 | }; 112 | 113 | Ok((pattern_1, pattern_z)) 114 | } 115 | 116 | #[cfg(test)] 117 | mod test { 118 | use super::*; 119 | 120 | #[test] 121 | fn case_insensitive_vanity() { 122 | let pattern = Pattern::new("zcVANiTY".to_string()).unwrap(); 123 | assert_eq!( 124 | pattern 125 | .case_insensitive() 126 | .iter() 127 | .map(|p| p.prefix.as_str()) 128 | .collect::>(), 129 | vec![ 130 | "zcVANiTY", 131 | "zcVaNiTY", 132 | "zcVAniTY", 133 | "zcVaniTY", 134 | "zcVANitY", 135 | "zcVaNitY", 136 | "zcVAnitY", 137 | "zcVanitY", 138 | "zcVANiTy", 139 | "zcVaNiTy", 140 | "zcVAniTy", 141 | "zcVaniTy", 142 | "zcVANity", 143 | "zcVaNity", 144 | "zcVAnity", 145 | "zcVanity", 146 | ] 147 | ); 148 | } 149 | 150 | #[test] 151 | fn case_insensitive_a() { 152 | let pattern = Pattern::new("zcA".to_string()).unwrap(); 153 | assert_eq!( 154 | pattern 155 | .case_insensitive() 156 | .iter() 157 | .map(|p| p.prefix.as_str()) 158 | .collect::>(), 159 | vec![ 160 | "zcA", 161 | "zca", 162 | ] 163 | ); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/sha256.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{BigEndian, ByteOrder}; 2 | use crypto::sha2::sha256_digest_block; 3 | 4 | static H256: [u32; 8] = [ 5 | 0x6a09e667, 6 | 0xbb67ae85, 7 | 0x3c6ef372, 8 | 0xa54ff53a, 9 | 0x510e527f, 10 | 0x9b05688c, 11 | 0x1f83d9ab, 12 | 0x5be0cd19, 13 | ]; 14 | 15 | pub fn sha256_compress(dst: &mut [u8; 32], src: &[u8; 64]) { 16 | let mut state = H256; 17 | sha256_digest_block(&mut state, src); 18 | BigEndian::write_u32_into(&state, dst); 19 | } 20 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use bs58; 2 | use crypto::digest::Digest; 3 | use crypto::sha2::Sha256; 4 | use std::io::Write; 5 | 6 | pub fn bs58_encode_check(input: &[u8]) -> String { 7 | let mut check = [0u8; 32]; 8 | let mut sha = Sha256::new(); 9 | sha.input(input); 10 | sha.result(&mut check); 11 | sha.reset(); 12 | sha.input(&check); 13 | sha.result(&mut check); 14 | 15 | let mut output = input.to_vec(); 16 | for &b in check.iter().take(4) { 17 | output.push(b); 18 | } 19 | bs58::encode(output).into_string() 20 | } 21 | 22 | pub fn clear_console_line_80(out: &mut dyn Write) { 23 | write!(out, "\r{}\r", " ".repeat(80)).unwrap(); 24 | out.flush().unwrap(); 25 | } 26 | --------------------------------------------------------------------------------