From 94c40441afaa44429f5e72db9ad1f1b091db01cd Mon Sep 17 00:00:00 2001
From: Luke Childs <lukechilds123@gmail.com>
Date: Thu, 9 May 2019 07:15:31 +0700
Subject: [PATCH] Add p2wpkh-p2sh address format

---
 bench/index.js     | 40 +++++++++++++++--------------
 src/index.js       | 59 ++++++++++++++++++++++++++++++++++++------
 src/p2pkh.js       | 64 +++++++++++-----------------------------------
 src/p2wpkh-p2sh.js | 24 +++++++++++++++++
 4 files changed, 111 insertions(+), 76 deletions(-)
 create mode 100644 src/p2wpkh-p2sh.js

diff --git a/bench/index.js b/bench/index.js
index a0999d3..4323b9f 100644
--- a/bench/index.js
+++ b/bench/index.js
@@ -1,26 +1,28 @@
 const Vain = require('..');
 const prettyMs = require('pretty-ms');
 
-const prefix = 'BTC';
-console.log();
-console.log(`Searching for the prefix "${prefix}"...`);
+['p2pkh', 'p2wpkh-p2sh'].forEach(addressFormat => {
+	const prefix = 'BTC';
+	console.log();
+	console.log(`Searching for the prefix "${prefix}" for addres format "${addressFormat}"...`);
 
-const vain = new Vain({prefix});
+	const vain = new Vain({prefix, addressFormat});
 
-vain.on('update', data => {
-	const duration = prettyMs(data.duration);
-	const {attempts} = data;
-	const speed = `${data.addressesPerSecond} addr/s`;
-	console.log(`Duration: ${duration} | Attempts: ${attempts} | Speed: ${speed}`);
-});
+	vain.on('update', data => {
+		const duration = prettyMs(data.duration);
+		const {attempts} = data;
+		const speed = `${data.addressesPerSecond} addr/s`;
+		console.log(`Duration: ${duration} | Attempts: ${attempts} | Speed: ${speed}`);
+	});
 
-vain.on('found', data => {
-	console.log();
-	console.log(`Address: ${data.address}`);
-	console.log(`WIF: ${data.wif}`);
-	console.log();
-	console.log(`Found in ${prettyMs(data.duration)}`);
-	console.log(`Speed: ${data.addressesPerSecond} addr/s`);
-});
+	vain.on('found', data => {
+		console.log();
+		console.log(`Address: ${data.address}`);
+		console.log(`WIF: ${data.wif}`);
+		console.log();
+		console.log(`Found in ${prettyMs(data.duration)}`);
+		console.log(`Speed: ${data.addressesPerSecond} addr/s`);
+	});
 
-vain.start();
+	vain.start();
+});
diff --git a/src/index.js b/src/index.js
index 47692ef..2fcb7f7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,21 +1,64 @@
 const Emitter = require('tiny-emitter');
 
 const p2pkh = require('./p2pkh');
+const p2wpkhp2sh = require('./p2wpkh-p2sh');
+
+const addressFormats = new Map(Object.entries({
+	p2pkh,
+	'p2wpkh-p2sh': p2wpkhp2sh
+}));
+
+const ONE_SECOND = 1000;
 
 class Vain extends Emitter {
-	constructor({prefix}) {
+	constructor({prefix, addressFormat = 'p2pkh'}) {
 		super();
-		this.prefix = `1${prefix}`;
+		this.addressFormat = addressFormats.get(addressFormat);
+		this.prefix = `${this.addressFormat.prefix}${prefix}`;
 	}
 
 	start() {
 		return new Promise(resolve => {
-			const miner = p2pkh(this.prefix);
-			miner.on('update', data => this.emit('update', data));
-			miner.on('found', data => {
-				this.emit('found', data);
-				resolve(data);
-			});
+			const startTime = Date.now();
+
+			let found;
+			let attempts = 0;
+			let data;
+			let lastUpdate = Date.now();
+
+			while (!found) {
+				attempts++;
+
+				data = this.addressFormat.derive();
+
+				if (data.address.startsWith(this.prefix)) {
+					found = true;
+				}
+
+				const now = Date.now();
+				if ((now - lastUpdate) > ONE_SECOND) {
+					const duration = now - startTime;
+					const addressesPerSecond = Math.floor(attempts / (duration / ONE_SECOND));
+					this.emit('update', {
+						duration,
+						attempts,
+						addressesPerSecond
+					});
+					lastUpdate = now;
+				}
+			}
+
+			const endTime = Date.now();
+			const duration = endTime - startTime;
+			const addressesPerSecond = Math.floor(attempts / (duration / ONE_SECOND));
+
+			const result = {
+				duration,
+				addressesPerSecond,
+				...this.addressFormat.format(data)
+			};
+			this.emit('found', result);
+			resolve(result);
 		});
 	}
 }
diff --git a/src/p2pkh.js b/src/p2pkh.js
index 809e67b..52c88d8 100644
--- a/src/p2pkh.js
+++ b/src/p2pkh.js
@@ -1,56 +1,22 @@
-const Emitter = require('tiny-emitter');
 const bitcoin = require('bitcoinjs-lib');
 
-const ONE_SECOND = 1000;
-
-const p2pkh = prefix => {
-	const emitter = new Emitter();
-
-	process.nextTick(() => {
-		const startTime = Date.now();
-
-		let found;
-		let attempts = 0;
-		let keyPair;
-		let address;
-		let lastUpdate = Date.now();
-
-		while (!found) {
-			attempts++;
-			keyPair = bitcoin.ECPair.makeRandom();
-			({address} = bitcoin.payments.p2pkh({pubkey: keyPair.publicKey}));
-
-			if (address.startsWith(prefix)) {
-				found = true;
-			}
-
-			const now = Date.now();
-			if ((now - lastUpdate) > ONE_SECOND) {
-				const duration = now - startTime;
-				const addressesPerSecond = Math.floor(attempts / (duration / ONE_SECOND));
-				emitter.emit('update', {
-					duration,
-					attempts,
-					addressesPerSecond
-				});
-				lastUpdate = now;
-			}
-		}
-
-		const endTime = Date.now();
-		const duration = endTime - startTime;
-		const addressesPerSecond = Math.floor(attempts / (duration / ONE_SECOND));
+const p2pkh = {
+	prefix: '1'
+};
 
-		emitter.emit('found', {
-			duration,
-			attempts,
-			addressesPerSecond,
-			address,
-			wif: keyPair.toWIF()
-		});
-	});
+p2pkh.derive = () => {
+	const keyPair = bitcoin.ECPair.makeRandom();
+	const {address} = bitcoin.payments.p2pkh({pubkey: keyPair.publicKey});
 
-	return emitter;
+	return {
+		address,
+		keyPair
+	};
 };
 
+p2pkh.format = ({address, keyPair}) => ({
+	address,
+	wif: keyPair.toWIF()
+});
+
 module.exports = p2pkh;
diff --git a/src/p2wpkh-p2sh.js b/src/p2wpkh-p2sh.js
new file mode 100644
index 0000000..12658ef
--- /dev/null
+++ b/src/p2wpkh-p2sh.js
@@ -0,0 +1,24 @@
+const bitcoin = require('bitcoinjs-lib');
+
+const p2wpkhp2sh = {
+	prefix: '3'
+};
+
+p2wpkhp2sh.derive = () => {
+	const keyPair = bitcoin.ECPair.makeRandom();
+	const {address} = bitcoin.payments.p2sh({
+		redeem: bitcoin.payments.p2wpkh({pubkey: keyPair.publicKey})
+	});
+
+	return {
+		address,
+		keyPair
+	};
+};
+
+p2wpkhp2sh.format = ({address, keyPair}) => ({
+	address,
+	wif: keyPair.toWIF()
+});
+
+module.exports = p2wpkhp2sh;