Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CREATE2 vanity address creation #22

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 83 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -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 <command> [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')
Expand All @@ -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++
Expand All @@ -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();
Expand Down
73 changes: 67 additions & 6 deletions libs/VanityEth.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -25,22 +40,68 @@ 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;
}
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
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
{
"name": "Boris K",
"url": "https://github.com/bokub"
},
{
"name": "GNSPS",
"url": "https://github.com/gnsps"
}
],
"license": "MIT",
Expand Down