diff --git a/index.js b/index.js index 3e9817d..6dfe239 100755 --- a/index.js +++ b/index.js @@ -1,25 +1,38 @@ #! /usr/bin/env node -var VanityEth = require('./libs/VanityEth'); +const VanityEth = require('./libs/VanityEth'); const ora = require('ora'); -var cluster = require('cluster') -var TimeFormat = require('hh-mm-ss') -var numCPUs = require('os').cpus().length +const cluster = require('cluster'); +const TimeFormat = require('hh-mm-ss'); +const numCPUs = require('os').cpus().length; +const fs = require('fs'); +var ethUtils = require('ethereumjs-util'); var argv = require('yargs') .usage('Usage: $0 [options]') .example('$0 -checksum -i B00B5', 'get a wallet where address matches B00B5 in checksum format') .example('$0 --contract -i ABC', 'get a wallet where 0 nonce contract address matches the vanity') + .example('$0 --create2 0xab5801a7d398351b8be11c439e05c5b3259aec9b -b ./my_bytecode.bin', 'get a vanity address for this bytecode to be deployed by a factory at address 0xab5801(...)') .example('$0 -n 25 -i ABC', 'get 25 vanity wallets') .example('$0 -n 1000', 'get 1000 random wallets') .alias('i', 'input') .string('i') .describe('i', 'input hex string') + .alias('p', 'pattern') + .number('p') + .describe('p', 'match with a pattern of repeated inputs N times (e.g.: "-i dead -p 2" would only match "0xdeaddead...")') .alias('c', 'checksum') .boolean('c') .describe('c', 'check against the checksum address') + .string('create2') + .describe('create2', 'contract address for contract deployment with create2 opcode') + .alias('b', 'bytecode') + .string('b') + .describe('b', 'path to file with bytecode to be deployed with CREATE2') .alias('n', 'count') .number('n') .describe('n', 'number of wallets') + .boolean('inf') + .describe('inf', 'run the prgoram indefinitely') .boolean('contract') .describe('contract', 'contract address for contract deployment') .alias('l', 'log') @@ -29,47 +42,89 @@ var argv = require('yargs') .alias('h', 'help') .epilog('copyright 2018') .argv; + if (cluster.isMaster) { const args = { input: argv.input ? argv.input : '', + acceptPattern: argv.pattern ? argv.pattern : 1, + isCreate2: argv.create2 ? true : false, + create2Address: argv.create2, + create2Bytecode: argv.create2 ? fs.readFileSync(argv.bytecode).toString('utf-8').trim() : '', isChecksum: argv.checksum ? true : false, numWallets: argv.count ? argv.count : 1, + runIndefinitely: argv.inf ? true : false, isContract: argv.contract ? true : false, log: argv.log ? true : false, logFname: argv.log ? 'VanityEth-log-' + Date.now() + '.txt' : '' } + if (args.isContract && args.isCreate2) { + console.error('Cannot use the options "--contract" and "--create2" at the same time'); + process.exit(1); + } + if (args.numWallets > 1 && args.runIndefinitely) { + console.error('No point in using the "-n" flag and the "--inf" flag at the same time! :D'); + process.exit(1); + } if (!VanityEth.isValidHex(args.input)) { console.error(args.input + ' is not valid hexadecimal'); process.exit(1); } + if (args.isCreate2) { + if (!VanityEth.isValidBytecode(args.create2Bytecode)) { + console.error(args.create2Bytecode + ' is not valid bytecode'); + process.exit(1); + } + if (!ethUtils.isValidAddress(args.create2Address)) { + console.error(args.create2 + ' is not a valid address'); + process.exit(1); + } + } if (args.log) { - var fs = require('fs'); console.log('logging into ' + args.logFname); var logStream = fs.createWriteStream(args.logFname, { 'flags': 'a' }); } var walletsFound = 0; - const spinner = ora('generating vanity address 1/' + args.numWallets).start(); + var initialStatusText = 'generating vanity address 1'; + if(!args.runIndefinitely) { + initialStatusText += '/' + args.numWallets; + } + console.log(initialStatusText); + + const spinner = ora("Starting to mine your vanity addresses! \\m/").start(); + let addps = 0; + let elapsedSeconds = 0; setInterval(function(){ - spinner.text ='Approximate ETA for an account ' + TimeFormat.fromS((Math.pow(16,20)/Math.pow(16,20-args.input.length))/addps, 'hh:mm:ss'); + spinner.text ='Approximate ETA for an account ' + TimeFormat.fromS((Math.pow(16, 20) / Math.pow(16, 20 - args.input.length * args.acceptPattern))/addps - elapsedSeconds, 'hh:mm:ss'); addps = 0; + elapsedSeconds++; },1000) + for (var i = 0; i < numCPUs; i++) { const worker_env = { input: args.input, + acceptPattern: args.acceptPattern, isChecksum: args.isChecksum, - isContract: args.isContract + isContract: args.isContract, + isCreate2: args.isCreate2, + create2Address: args.create2Address, + create2Bytecode: args.create2Bytecode } proc = cluster.fork(worker_env); proc.on('message', function(message) { if(message.account){ spinner.succeed(JSON.stringify(message)); + walletsFound++; + elapsedSeconds = 0; if (args.log) logStream.write(JSON.stringify(message) + "\n"); - walletsFound++; - if (walletsFound >= args.numWallets) { + if (!args.runIndefinitely && walletsFound >= args.numWallets) { cleanup(); } - spinner.text ='generating vanity address ' + (walletsFound + 1) +'/' + args.numWallets; + var statusText = 'generating vanity address ' + (walletsFound + 1); + if(!args.runIndefinitely) { + statusText += '/' + args.numWallets; + } + console.log(statusText); spinner.start(); } else if(message.counter){ addps++ @@ -79,13 +134,24 @@ if (cluster.isMaster) { } else { const worker_env = process.env; - while (true) { - process.send({ - account: VanityEth.getVanityWallet(worker_env.input, worker_env.isChecksum == 'true', worker_env.isContract == 'true', function (){ + if(worker_env.isCreate2) { + while (true) { + process.send({ + account: VanityEth.getVanityCreate2Address(worker_env.input, worker_env.isChecksum == 'true', worker_env.create2Address, worker_env.create2Bytecode, worker_env.acceptPattern, function (){ + process.send({ + counter: true + }) + })}) + } + } else { + while (true) { process.send({ - counter: true - }) - })}) + account: VanityEth.getVanityWallet(worker_env.input, worker_env.isChecksum == 'true', worker_env.isContract == 'true', worker_env.acceptPattern, function (){ + process.send({ + counter: true + }) + })}) + } } } process.stdin.resume(); diff --git a/libs/VanityEth.js b/libs/VanityEth.js index 975d05e..f3393d8 100644 --- a/libs/VanityEth.js +++ b/libs/VanityEth.js @@ -1,19 +1,34 @@ const crypto = require('crypto'); var ethUtils = require('ethereumjs-util'); var ERRORS = { - invalidHex: "Invalid hex input" + invalidHex: "Invalid hex input", + invalidAddress: "Invalid address input", + invalidBytecode: "Invalid byecode input" } var getRandomWallet = function() { var randbytes = crypto.randomBytes(32); var address = '0x' + ethUtils.privateToAddress(randbytes).toString('hex'); return { address: address, privKey: randbytes.toString('hex') } } +function getRandomSalt() { + var randbytes = crypto.randomBytes(32); + return randbytes.toString('hex'); +} var isValidHex = function(hex) { if (!hex.length) return true; - hex = hex.toUpperCase(); - var re = /^[0-9A-F]+$/g; + var re = /^(0x)?[0-9A-Fa-f]+$/g; return re.test(hex); } +var isValidBytecode = function(bytecode) { + // console.log(`Yoooo, it's me: ${bytecode}`); + // console.log(`and my char length is: ${bytecode.length}`); + if (bytecode.length == 0 || bytecode.length % 2 != 0) return false; + var re = /^(0x)?[0-9A-Fa-f]+$/g; + return re.test(bytecode); +} +var cleanHexPrefix = function(hex) { + return hex.replace(/^0x/, ""); +} var isValidVanityWallet = function(wallet, input, isChecksum, isContract) { var _add = wallet.address; if (isContract) { @@ -25,13 +40,52 @@ var isValidVanityWallet = function(wallet, input, isChecksum, isContract) { _add = isChecksum ? ethUtils.toChecksumAddress(_add) : _add; return _add.substr(2, input.length) == input; } -var getVanityWallet = function(input = '', isChecksum = false, isContract = false, counter = function(){}) { +function isValidVanityWalletPattern(wallet, input, isChecksum, isContract, multipleInputMatch) { + var _add = wallet.address; + var re = new RegExp(`0x^(${input}){${multipleInputMatch},}`, 'g'); + if (isContract) { + var _contractAdd = getDeterministicContractAddress(_add); + _contractAdd = isChecksum ? ethUtils.toChecksumAddress(_contractAdd) : _contractAdd; + wallet.contract = _contractAdd; + return re.test(_contractAdd); + } + _add = isChecksum ? ethUtils.toChecksumAddress(_add) : _add; + return re.test(_add); +} +var getVanityWallet = function(input = '', isChecksum = false, isContract = false, multipleInputMatch = 1, ounter = function(){}) { if (!isValidHex(input)) throw new Error(ERRORS.invalidHex); input = isChecksum ? input : input.toLowerCase(); var _wallet = getRandomWallet(); - while (!isValidVanityWallet(_wallet, input, isChecksum, isContract)) { + while (multipleInputMatch ? !isValidVanityWalletPattern(_wallet, input, isChecksum, isContract, multipleInputMatch) : !isValidVanityWallet(_wallet, input, isChecksum, isContract)) { counter() - _wallet = getRandomWallet(isChecksum); + _wallet = getRandomWallet(); + } + if (isChecksum) _wallet.address = ethUtils.toChecksumAddress(_wallet.address); + return _wallet; +} +function isValidCreate2Address(wallet, input, creatorAddress, bytecode, isChecksum) { + var _address = getCreate2ContractAddress(creatorAddress, wallet.saltHex, bytecode); + _address = isChecksum ? ethUtils.toChecksumAddress(_address) : _address; + wallet.address = _address; + return _address.substr(2, input.length) == input +} +function isValidCreate2AddressPattern(wallet, input, creatorAddress, bytecode, isChecksum, multipleInputMatch) { + var _address = getCreate2ContractAddress(creatorAddress, wallet.saltHex, bytecode); + _address = isChecksum ? ethUtils.toChecksumAddress(_address) : _address; + wallet.address = _address; + var re = new RegExp(`^0x(${input}){${multipleInputMatch},}`, 'g'); + return re.test(_address); +} +var getVanityCreate2Address = function(input = '', isChecksum = false, create2Address = '', create2Bytecode = '', multipleInputMatch = 1, counter = function(){}) { + if (!isValidHex(input)) throw new Error(ERRORS.invalidHex); + if (!ethUtils.isValidAddress(create2Address)) throw new Error(ERRORS.invalidAddress); + if (!isValidBytecode(create2Bytecode)) throw new Error(ERRORS.invalidBytecode); + input = isChecksum ? input : input.toLowerCase(); + var _wallet = {}; + _wallet.saltHex = getRandomSalt(); + while (multipleInputMatch ? !isValidCreate2AddressPattern(_wallet, input, create2Address, create2Bytecode, isChecksum, multipleInputMatch) : !isValidCreate2Address(_wallet, input, create2Address, create2Bytecode, isChecksum)) { + counter() + _wallet.saltHex = getRandomSalt(); } if (isChecksum) _wallet.address = ethUtils.toChecksumAddress(_wallet.address); return _wallet; @@ -39,8 +93,15 @@ var getVanityWallet = function(input = '', isChecksum = false, isContract = fals var getDeterministicContractAddress = function(address) { return '0x' + ethUtils.sha3(ethUtils.rlp.encode([address, 0])).slice(12).toString('hex'); } +function getCreate2ContractAddress (creatorAddress, saltHex, bytecode) { + var joinedParams = ['ff', creatorAddress, saltHex, ethUtils.sha3(bytecode).toString('hex')].map(x => x.replace(/0x/, '')).join(''); + return `0x${ethUtils.sha3(`0x${joinedParams}`).toString('hex').slice(-40)}`.toLowerCase() +} module.exports = { getVanityWallet: getVanityWallet, + getVanityCreate2Address: getVanityCreate2Address, isValidHex: isValidHex, + isValidBytecode: isValidBytecode, + cleanHexPrefix: cleanHexPrefix, ERRORS: ERRORS } diff --git a/package.json b/package.json index 5036b38..bde0f82 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,10 @@ { "name": "Boris K", "url": "https://github.com/bokub" + }, + { + "name": "GNSPS", + "url": "https://github.com/gnsps" } ], "license": "MIT",