vanity-eth/src/App.vue
2018-11-13 07:18:06 +01:00

382 lines
12 KiB
Vue

<template>
<div id="app" class="remodal-bg render">
<div class="container" id="content">
<!--Headline-->
<headline></headline>
<!--Description-->
<div class="row">
<div class="col-md-12">
<description></description>
</div>
</div>
<!--Error-->
<div v-if="error" class="row">
<div class="col-md-12">
<err :error="error"></err>
</div>
</div>
<div class="row">
<!--User input-->
<div class="col-md-6">
<userInput :running="running" :cores="cores"
@start="startGen" @stop="stopGen" @input-change="setInput"></userInput>
</div>
<!--Statistics-->
<div class="col-md-6">
<statistics :hex="input.hex" :checksum="input.checksum" :status="status"
:first-tick="firstTick"></statistics>
</div>
</div>
<!--Result-->
<div class="row">
<div class="col-md-12">
<result :address="result.address" :private-key="result.privateKey"></result>
</div>
</div>
</div>
<!--Save modal-->
<save :address="result.address.toLowerCase()" :private-key="result.privateKey"></save>
<!--Footer-->
<foot></foot>
<!--Github corner-->
<corner></corner>
</div>
</template>
<script>
import Worker from './js/vanity.js';
import Headline from './vue/Headline';
import Description from './vue/Description';
import Err from './vue/Error';
import UserInput from './vue/Input';
import Statistics from './vue/Statistics';
import Result from './vue/Result';
import Save from './vue/Save.vue';
import Corner from './vue/Corner';
import Foot from './vue/Footer';
export default {
components: {Headline, Description, Err, UserInput, Statistics, Result, Save, Corner, Foot},
data: function () {
return {
running: false,
status: 'Waiting',
workers: [],
threads: 4,
cores: 0,
result: {address: '', privateKey: ''},
input: {hex: '', checksum: true, suffix: false},
firstTick: null,
error: null
};
},
watch: {
threads: function () {
if (!this.running) {
this.initWorkers();
}
}
},
methods: {
setInput: function (inputType, value) {
// eslint-disable-next-line default-case
switch (inputType) {
case 'hex':
this.input.hex = value;
break;
case 'checksum':
this.input.checksum = value;
break;
case 'suffix':
this.input.suffix = value;
break;
case 'threads':
this.threads = value;
}
},
displayResult: function (result) {
this.$emit('increment-counter', result.attempts);
this.result.address = result.address;
this.result.privateKey = result.privKey;
this.status = 'Address found';
},
clearResult: function () {
this.result.address = '';
this.result.privateKey = '';
this.$emit('increment-counter', -1);
},
/**
* Create missing workers, remove the unwanted ones.
*/
initWorkers: function () {
const self = this;
if (this.workers.length === this.threads) {
return;
}
// Remove unwanted workers
if (this.workers.length > this.threads) {
for (let w = this.threads; w < this.workers.length; w++) {
this.workers[w].terminate();
}
this.workers = this.workers.slice(0, this.threads);
return;
}
// Create workers
for (let w = this.workers.length; w < this.threads; w++) {
try {
this.workers[w] = new Worker();
this.workers[w].onmessage = (event) => self.parseWorkerMessage(event.data);
} catch (err) {
this.error = err;
this.status = 'Error';
console.error(this.error);
break;
}
}
},
parseWorkerMessage: function (wallet) {
if (wallet.error) {
this.stopGen();
this.error = wallet.error;
this.status = 'Error';
console.error(this.error);
return;
}
if (wallet.address) {
this.stopGen();
return this.displayResult(wallet);
}
this.$emit('increment-counter', wallet.attempts);
},
startGen: function () {
if (!window.Worker) {
this.error = 'workers_unsupported';
return;
}
this.clearResult();
this.running = true;
for (let w = 0; w < this.workers.length; w++) {
this.workers[w].postMessage(this.input);
}
this.status = 'Running';
this.firstTick = performance.now();
},
stopGen: function () {
this.running = false;
this.status = 'Stopped';
for (let i = 0; i < this.workers.length; i++) {
this.workers[i].terminate();
}
this.workers = [];
this.initWorkers();
},
countCores: function () {
// Estimate number of cores on machine
let cores = 0;
try {
cores = parseInt(navigator.hardwareConcurrency, 10);
} catch (err) {
console.error(err);
}
if (cores) {
this.cores = cores;
this.threads = this.cores;
}
},
initFathom: function () {
if (window.location.hostname === 'localhost') {
return; // No stats when coding
}
// Fathom - simple website analytics - https://github.com/usefathom/fathom
/* eslint-disable */
(function (f, a, t, h, o, m) {
a[h] = a[h] || function () {
(a[h].q = a[h].q || []).push(arguments);
};
o = f.createElement('script');
m = f.getElementsByTagName('script')[0];
o.async = 1;
o.src = t;
o.id = 'fathom-script';
m.parentNode.insertBefore(o, m);
})(document, window, 'https://stats.vanity-eth.tk/tracker.js', 'fathom');
fathom('set', 'siteId', 'BUIGR');
fathom('trackPageview');
/* eslint-enable */
},
checkLocation() {
try {
this.error = window.self !== window.top ? 'insecure_location' : this.error;
} catch (e) {
this.error = 'insecure_location';
}
const hostname = window.location.hostname;
if (hostname && ['localhost', '127.0.0.1', 'vanity-eth.tk'].indexOf(hostname) === -1) {
this.error = 'insecure_location';
}
},
benchmark(max) {
max = max || 10000;
const step = 500;
const worker = new Worker();
let attempts = 0;
const times = [];
const durations = [];
const timeTaken = (a, d) => Math.round(1000 * a / d);
worker.onmessage = () => {
times.push(performance.now());
if (times.length === 1) {
return;
}
durations.push(times[times.length - 1] - times[times.length - 2]);
attempts += step;
console.info(attempts + '/' + max + '...' + timeTaken(step, durations[durations.length - 1]) + ' addr/s');
if(attempts >= max) {
console.info('\nSpeed range: ' + timeTaken(step, Math.max(...durations))
+ ' - ' + timeTaken(step, Math.min(...durations)) +' addr/s');
console.info('Average: ' + timeTaken(attempts, (times[times.length - 1] - times[0])) + ' addr/s');
worker.terminate();
}
};
const input = {checksum: true, hex: 'f'.repeat(5), suffix: false};
console.info('Starting benchmark with 1 core...');
worker.postMessage(input);
}
},
created: function () {
this.checkLocation();
this.countCores();
this.initWorkers();
this.initFathom();
window['benchmark'] = this.benchmark;
}
};
</script>
<style lang="sass">
// Bootstrap - Required
@import "~bootstrap/scss/functions"
@import "~bootstrap/scss/variables"
@import "~bootstrap/scss/mixins"
// Bootstrap - Optional
@import "~bootstrap/scss/reboot"
@import "~bootstrap/scss/grid"
@import "css/variables"
@import "css/fonts"
body
padding: 0
font-family: 'Lato', sans-serif
background: $bg-fallback
background: linear-gradient(140deg, $bg-2 0%, $bg-1 100%)
background-attachment: fixed
font-size: 16px
h1, h2, h3, h4, h5, h6, p, label
margin: 0
font-weight: normal
a, a:visited, a:hover
color: $text-alt
text-decoration: underline
a:hover
color: $text
.panel
padding: 1.5em 3em
background-color: $panel-background
margin-top: 2em
color: $text
font-weight: 400
box-shadow: $shadow
transition: box-shadow 0.2s ease-in-out
&:hover
box-shadow: $shadow-big
#content
margin-top: 8em
margin-bottom: 6em
.text-input-large
width: 100%
color: $text
background: $panel-background-alt
outline: none
font-size: 1.3em
padding: 0.5em
border: none
margin-bottom: 10px
-webkit-appearance: none
&::placeholder
color: $placeholder
.button-large
border: none
outline: none
color: $text-opposite
padding: 8px
font-size: 19px
font-weight: 500
margin: 1.3em 0 0 0
cursor: pointer
-webkit-appearance: none
background: $primary
width: 100%
&:hover
background: $secondary
&:disabled
background: $disabled
cursor: auto
/*-- Pre-render-specific --
#app.render .hide-render
display: none
#app.prerender .hide-prerender
display: none
/*-- Responsive design --
@media screen and (max-width: 1024px)
#content
margin-top: 7em
margin-bottom: 5em
@media screen and (max-width: 640px)
#content
margin-top: 5em
margin-bottom: 4em
@media screen and (max-width: 480px)
.panel
padding: 1em
</style>