browser: improve webpack build.
This commit is contained in:
parent
e4ad8b457f
commit
11c7dab3c4
5
Makefile
5
Makefile
@ -1,6 +1,9 @@
|
||||
all:
|
||||
@npm run webpack
|
||||
|
||||
app:
|
||||
@npm run webpack-app
|
||||
|
||||
browser:
|
||||
@npm run webpack-browser
|
||||
|
||||
@ -22,4 +25,4 @@ lint:
|
||||
test:
|
||||
@npm test
|
||||
|
||||
.PHONY: all browser compat node clean docs lint test
|
||||
.PHONY: all app browser compat node clean docs lint test
|
||||
|
||||
@ -1,106 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>bcoin</title>
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
height: 90%;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
font: 3em monospace;
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
small {
|
||||
margin: 10px;
|
||||
width: 50%;
|
||||
display: block;
|
||||
}
|
||||
.log {
|
||||
padding: 5px;
|
||||
margin-left: 10px;
|
||||
overflow-y: scroll;
|
||||
border: 1px solid black;
|
||||
white-space: pre-wrap;
|
||||
height: 40%;
|
||||
width: 40%;
|
||||
font: 1em monospace;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.wallet {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
font: 1em monospace;
|
||||
}
|
||||
.rpc, .send {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
font: 1em monospace;
|
||||
}
|
||||
#newaddr{
|
||||
display: block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.tx {
|
||||
float: right;
|
||||
font: 1em monospace;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
margin-top: 10px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.floating {
|
||||
font: 1em monospace;
|
||||
white-space: pre-wrap;
|
||||
position: absolute;
|
||||
display: none;
|
||||
padding: 5px;
|
||||
background: white;
|
||||
border: 1px solid black;
|
||||
width: 40%;
|
||||
height: 30%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -20%;
|
||||
margin-top: -15%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<script src="/bcoin.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bcoin, the browser full node</h1>
|
||||
<small>Welcome. Your machine is currently validating the blockchain.
|
||||
The blocks and wallet are stored on your local disk with indexed DB. You are
|
||||
connecting to the actual bitcoin P2P network via a websocket->tcp proxy.
|
||||
Enjoy. (See the <a href="https://github.com/bcoin-org/bcoin" target="_blank">bcoin repo</a> for
|
||||
more bitcoin magic).</small>
|
||||
<div class="tx">
|
||||
<div>Chain State: <span id="state"></span></div>
|
||||
<div>Last 20 Blocks/TXs:</div>
|
||||
<div id="tx"></div>
|
||||
</div>
|
||||
<div id="log" class="log"></div>
|
||||
<form id="rpc" class="rpc" action="#">
|
||||
<input type="text" name="cmd" id="cmd"
|
||||
placeholder="RPC command (e.g. getblockchaininfo)">
|
||||
</form>
|
||||
<div id="wallet" class="wallet"></div>
|
||||
<form id="send" class="send" action="#">
|
||||
<input type="text" name="address" id="address" placeholder="Address">
|
||||
<input type="text" name="amount" id="amount" placeholder="Amount (BTC)">
|
||||
<input type="submit" value="Send">
|
||||
</form>
|
||||
<input type="button" id="newaddr" value="New Address">
|
||||
<div id="floating" class="floating"></div>
|
||||
<script src="/index.js"></script>
|
||||
</body>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>bcoin</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
height: 90%;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
font: 3em monospace;
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
small {
|
||||
margin: 10px;
|
||||
width: 50%;
|
||||
display: block;
|
||||
}
|
||||
.log {
|
||||
padding: 5px;
|
||||
margin-left: 10px;
|
||||
overflow-y: scroll;
|
||||
border: 1px solid black;
|
||||
white-space: pre-wrap;
|
||||
height: 40%;
|
||||
width: 40%;
|
||||
font: 1em monospace;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.wallet {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
font: 1em monospace;
|
||||
}
|
||||
.rpc, .send {
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
font: 1em monospace;
|
||||
}
|
||||
#newaddr{
|
||||
display: block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.tx {
|
||||
float: right;
|
||||
font: 1em monospace;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
margin-top: 10px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.floating {
|
||||
font: 1em monospace;
|
||||
white-space: pre-wrap;
|
||||
position: absolute;
|
||||
display: none;
|
||||
padding: 5px;
|
||||
background: white;
|
||||
border: 1px solid black;
|
||||
width: 40%;
|
||||
height: 30%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -20%;
|
||||
margin-top: -15%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<script src="/app.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bcoin, the browser full node</h1>
|
||||
<small>
|
||||
Welcome. Your machine is currently validating the blockchain. The blocks
|
||||
and wallet are stored on your local disk with indexed DB. You are
|
||||
connecting to the actual bitcoin P2P network via a websocket->tcp
|
||||
proxy. Enjoy. (See the
|
||||
<a href="https://github.com/bcoin-org/bcoin"
|
||||
target="_blank">bcoin repo</a>
|
||||
for more bitcoin magic).
|
||||
</small>
|
||||
<div class="tx">
|
||||
<div>Chain State: <span id="state"></span></div>
|
||||
<div>Last 20 Blocks/TXs:</div>
|
||||
<div id="tx"></div>
|
||||
</div>
|
||||
<div id="log" class="log"></div>
|
||||
<form id="rpc" class="rpc" action="#">
|
||||
<input type="text" name="cmd" id="cmd"
|
||||
placeholder="RPC command (e.g. getblockchaininfo)">
|
||||
</form>
|
||||
<div id="wallet" class="wallet"></div>
|
||||
<form id="send" class="send" action="#">
|
||||
<input type="text" name="address" id="address" placeholder="Address">
|
||||
<input type="text" name="amount" id="amount" placeholder="Amount (BTC)">
|
||||
<input type="submit" value="Send">
|
||||
</form>
|
||||
<input type="button" id="newaddr" value="New Address">
|
||||
<div id="floating" class="floating"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
262
browser/index.js
262
browser/index.js
@ -1,262 +0,0 @@
|
||||
;(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = bcoin.util;
|
||||
var body = document.getElementsByTagName('body')[0];
|
||||
var log = document.getElementById('log');
|
||||
var wdiv = document.getElementById('wallet');
|
||||
var tdiv = document.getElementById('tx');
|
||||
var floating = document.getElementById('floating');
|
||||
var send = document.getElementById('send');
|
||||
var newaddr = document.getElementById('newaddr');
|
||||
var chainState = document.getElementById('state');
|
||||
var rpc = document.getElementById('rpc');
|
||||
var cmd = document.getElementById('cmd');
|
||||
var items = [];
|
||||
var scrollback = 0;
|
||||
var logger, node, wdb;
|
||||
|
||||
window.onunhandledrejection = function(event) {
|
||||
throw event.reason;
|
||||
};
|
||||
|
||||
body.onmouseup = function() {
|
||||
floating.style.display = 'none';
|
||||
};
|
||||
|
||||
floating.onmouseup = function(ev) {
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
function show(obj) {
|
||||
const json = obj && obj.toJSON ? obj.toJSON() : null;
|
||||
floating.innerHTML = escape(JSON.stringify(json, null, 2));
|
||||
floating.style.display = 'block';
|
||||
}
|
||||
|
||||
logger = new bcoin.logger({ level: 'debug', console: true });
|
||||
logger.writeConsole = function(level, module, args) {
|
||||
var name = bcoin.logger.levelsByVal[level];
|
||||
var msg = this.fmt(args, false);
|
||||
if (++scrollback > 1000) {
|
||||
log.innerHTML = '';
|
||||
scrollback = 1;
|
||||
}
|
||||
log.innerHTML += '<span style="color:blue;">' + util.now() + '</span> ';
|
||||
if (name === 'error') {
|
||||
log.innerHTML += '<span style="color:red;">';
|
||||
log.innerHTML += '[';
|
||||
log.innerHTML += name
|
||||
log.innerHTML += '] ';
|
||||
if (module)
|
||||
log.innerHTML += '(' + module + ') ';
|
||||
log.innerHTML += '</span>';
|
||||
} else {
|
||||
log.innerHTML += '[';
|
||||
log.innerHTML += name
|
||||
log.innerHTML += '] ';
|
||||
if (module)
|
||||
log.innerHTML += '(' + module + ') ';
|
||||
}
|
||||
log.innerHTML += escape(msg) + '\n';
|
||||
log.scrollTop = log.scrollHeight;
|
||||
};
|
||||
|
||||
rpc.onsubmit = function(ev) {
|
||||
var text = cmd.value || '';
|
||||
var argv = text.trim().split(/\s+/);
|
||||
var method = argv.shift();
|
||||
var params = [];
|
||||
var i, arg, param;
|
||||
|
||||
cmd.value = '';
|
||||
|
||||
for (i = 0; i < argv.length; i++) {
|
||||
arg = argv[i];
|
||||
try {
|
||||
param = JSON.parse(arg);
|
||||
} catch (e) {
|
||||
param = arg;
|
||||
}
|
||||
params.push(param);
|
||||
}
|
||||
|
||||
node.rpc.execute({ method: method, params: params }).then(show, show);
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
send.onsubmit = function(ev) {
|
||||
var value = document.getElementById('amount').value;
|
||||
var address = document.getElementById('address').value;
|
||||
var tx, options;
|
||||
|
||||
options = {
|
||||
outputs: [{
|
||||
address: address,
|
||||
value: bcoin.amount.value(value)
|
||||
}]
|
||||
};
|
||||
|
||||
wdb.primary.createTX(options).then(function(mtx) {
|
||||
tx = mtx;
|
||||
return wdb.primary.sign(tx);
|
||||
}).then(function() {
|
||||
return node.sendTX(tx);
|
||||
}).then(function() {
|
||||
show(tx);
|
||||
});
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
newaddr.onmouseup = function() {
|
||||
wdb.primary.createReceive().then(function() {
|
||||
formatWallet(wdb.primary);
|
||||
});
|
||||
};
|
||||
|
||||
function kb(size) {
|
||||
size /= 1000;
|
||||
return size.toFixed(2) + 'kb';
|
||||
}
|
||||
|
||||
function create(html) {
|
||||
var el = document.createElement('div');
|
||||
el.innerHTML = html;
|
||||
return el.firstChild;
|
||||
}
|
||||
|
||||
function escape(html, encode) {
|
||||
return html
|
||||
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function addItem(item, entry) {
|
||||
var height = entry ? entry.height : -1;
|
||||
var el;
|
||||
|
||||
if (items.length === 20) {
|
||||
el = items.shift();
|
||||
tdiv.removeChild(el);
|
||||
el.onmouseup = null;
|
||||
}
|
||||
|
||||
el = create('<a style="display:block;" href="#'
|
||||
+ item.rhash() + '">' + item.rhash() + ' (' + height
|
||||
+ ' - ' + kb(item.getSize()) + ')</a>');
|
||||
tdiv.appendChild(el);
|
||||
|
||||
setMouseup(el, item);
|
||||
|
||||
items.push(el);
|
||||
|
||||
chainState.innerHTML = ''
|
||||
+ 'tx=' + node.chain.db.state.tx
|
||||
+ ' coin=' + node.chain.db.state.coin
|
||||
+ ' value=' + bcoin.amount.btc(node.chain.db.state.value);
|
||||
}
|
||||
|
||||
function setMouseup(el, obj) {
|
||||
el.onmouseup = function(ev) {
|
||||
show(obj);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
function formatWallet(wallet) {
|
||||
var html = '';
|
||||
var json = wallet.master.toJSON(true);
|
||||
var i, tx, el;
|
||||
|
||||
html += '<b>Wallet</b><br>';
|
||||
|
||||
if (wallet.account.witness) {
|
||||
html += 'Current Address (p2wpkh): <b>'
|
||||
+ wallet.getAddress()
|
||||
+ '</b><br>';
|
||||
html += 'Current Address (p2wpkh behind p2sh): <b>'
|
||||
+ wallet.getNestedAddress()
|
||||
+ '</b><br>';
|
||||
} else {
|
||||
html += 'Current Address: <b>' + wallet.getAddress() + '</b><br>';
|
||||
}
|
||||
|
||||
html += 'Extended Private Key: <b>' + json.key.xprivkey + '</b><br>';
|
||||
html += 'Mnemonic: <b>' + json.mnemonic.phrase + '</b><br>';
|
||||
|
||||
wallet.getBalance().then(function(balance) {
|
||||
html += 'Confirmed Balance: <b>'
|
||||
+ bcoin.amount.btc(balance.confirmed)
|
||||
+ '</b><br>';
|
||||
|
||||
html += 'Unconfirmed Balance: <b>'
|
||||
+ bcoin.amount.btc(balance.unconfirmed)
|
||||
+ '</b><br>';
|
||||
|
||||
return wallet.getHistory();
|
||||
}).then(function(txs) {
|
||||
return wallet.toDetails(txs);
|
||||
}).then(function(txs) {
|
||||
html += 'TXs:\n';
|
||||
wdiv.innerHTML = html;
|
||||
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
|
||||
el = create(
|
||||
'<a style="display:block;" href="#' + tx.hash + '">'
|
||||
+ tx.hash + '</a>');
|
||||
|
||||
wdiv.appendChild(el);
|
||||
setMouseup(el, tx.toJSON());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
node = new bcoin.fullnode({
|
||||
hash: true,
|
||||
query: true,
|
||||
prune: true,
|
||||
network: 'main',
|
||||
db: 'leveldb',
|
||||
coinCache: 30000000,
|
||||
logConsole: true,
|
||||
workers: true,
|
||||
workerFile: '/bcoin-worker.js',
|
||||
logger: logger
|
||||
});
|
||||
|
||||
wdb = node.use(bcoin.wallet.plugin);
|
||||
|
||||
node.chain.on('block', addItem);
|
||||
node.mempool.on('tx', addItem);
|
||||
|
||||
node.open().then(function() {
|
||||
return node.connect();
|
||||
}).then(function() {
|
||||
node.startSync();
|
||||
|
||||
wdb.primary.on('balance', function() {
|
||||
formatWallet(wdb.primary);
|
||||
});
|
||||
|
||||
formatWallet(wdb.primary);
|
||||
}).catch(function(err) {
|
||||
throw err;
|
||||
});
|
||||
|
||||
})();
|
||||
@ -5,13 +5,10 @@ const fs = require('bfile');
|
||||
const WSProxy = require('./wsproxy');
|
||||
|
||||
const index = fs.readFileSync(`${__dirname}/index.html`);
|
||||
const indexjs = fs.readFileSync(`${__dirname}/index.js`);
|
||||
const debug = fs.readFileSync(`${__dirname}/debug.html`);
|
||||
const bcoin = fs.readFileSync(`${__dirname}/bcoin.js`);
|
||||
const worker = fs.readFileSync(`${__dirname}/bcoin-worker.js`);
|
||||
const app = fs.readFileSync(`${__dirname}/app.js`);
|
||||
const worker = fs.readFileSync(`${__dirname}/worker.js`);
|
||||
|
||||
const proxy = new WSProxy({
|
||||
pow: process.argv.indexOf('--pow') !== -1,
|
||||
ports: [8333, 18333, 18444, 28333, 28901]
|
||||
});
|
||||
|
||||
@ -20,6 +17,8 @@ const server = bweb.server({
|
||||
sockets: false
|
||||
});
|
||||
|
||||
server.use(server.router());
|
||||
|
||||
proxy.on('error', (err) => {
|
||||
console.error(err.stack);
|
||||
});
|
||||
@ -28,27 +27,15 @@ server.on('error', (err) => {
|
||||
console.error(err.stack);
|
||||
});
|
||||
|
||||
server.get('/favicon.ico', (req, res) => {
|
||||
res.send(404, '', 'txt');
|
||||
});
|
||||
|
||||
server.get('/', (req, res) => {
|
||||
res.send(200, index, 'html');
|
||||
});
|
||||
|
||||
server.get('/index.js', (req, res) => {
|
||||
res.send(200, indexjs, 'js');
|
||||
server.get('/app.js', (req, res) => {
|
||||
res.send(200, app, 'js');
|
||||
});
|
||||
|
||||
server.get('/debug', (req, res) => {
|
||||
res.send(200, debug, 'html');
|
||||
});
|
||||
|
||||
server.get('/bcoin.js', (req, res) => {
|
||||
res.send(200, bcoin, 'js');
|
||||
});
|
||||
|
||||
server.get('/bcoin-worker.js', (req, res) => {
|
||||
server.get('/worker.js', (req, res) => {
|
||||
res.send(200, worker, 'js');
|
||||
});
|
||||
|
||||
|
||||
284
browser/src/app.js
Normal file
284
browser/src/app.js
Normal file
@ -0,0 +1,284 @@
|
||||
'use strict';
|
||||
|
||||
const Logger = require('blgr');
|
||||
const FullNode = require('../../lib/node/fullnode');
|
||||
const Amount = require('../../lib/btc/amount');
|
||||
const plugin = require('../../lib/wallet/plugin');
|
||||
const ProxySocket = require('./proxysocket');
|
||||
|
||||
const body = document.getElementsByTagName('body')[0];
|
||||
const log = document.getElementById('log');
|
||||
const wdiv = document.getElementById('wallet');
|
||||
const tdiv = document.getElementById('tx');
|
||||
const floating = document.getElementById('floating');
|
||||
const send = document.getElementById('send');
|
||||
const newaddr = document.getElementById('newaddr');
|
||||
const chainState = document.getElementById('state');
|
||||
const rpc = document.getElementById('rpc');
|
||||
const cmd = document.getElementById('cmd');
|
||||
const items = [];
|
||||
|
||||
let scrollback = 0;
|
||||
|
||||
const logger = new Logger({
|
||||
level: 'debug',
|
||||
console: true
|
||||
});
|
||||
|
||||
if (0)
|
||||
logger.writeConsole = function writeConsole(level, module, args) {
|
||||
const name = Logger.levelsByVal[level];
|
||||
const msg = this.fmt(args, false);
|
||||
|
||||
if (++scrollback > 1000) {
|
||||
log.innerHTML = '';
|
||||
scrollback = 1;
|
||||
}
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
log.innerHTML += `<span style="color:blue;">${now}</span> `;
|
||||
|
||||
if (name === 'error') {
|
||||
log.innerHTML += `<span style="color:red;">[${name}] `;
|
||||
if (module)
|
||||
log.innerHTML += `(${module}) `;
|
||||
log.innerHTML += '</span>';
|
||||
} else {
|
||||
log.innerHTML += `[${name}] `;
|
||||
if (module)
|
||||
log.innerHTML += `(${module}) `;
|
||||
}
|
||||
|
||||
log.innerHTML += escape(msg) + '\n';
|
||||
log.scrollTop = log.scrollHeight;
|
||||
};
|
||||
|
||||
const node = new FullNode({
|
||||
hash: true,
|
||||
query: true,
|
||||
prune: true,
|
||||
network: 'main',
|
||||
memory: false,
|
||||
coinCache: 30,
|
||||
logConsole: true,
|
||||
workers: true,
|
||||
workerFile: '/worker.js',
|
||||
createSocket: (port, host) => {
|
||||
return ProxySocket.connect('ws://127.0.0.1:8080', port, host);
|
||||
},
|
||||
logger: logger,
|
||||
plugins: [plugin]
|
||||
});
|
||||
|
||||
const {wdb} = node.require('walletdb');
|
||||
|
||||
window.onunhandledrejection = function onunhandledrejection(event) {
|
||||
throw event.reason;
|
||||
};
|
||||
|
||||
body.onmouseup = function onmouseup() {
|
||||
floating.style.display = 'none';
|
||||
};
|
||||
|
||||
floating.onmouseup = function onmouseup(ev) {
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
function show(obj) {
|
||||
const json = obj && obj.toJSON ? obj.toJSON() : null;
|
||||
floating.innerHTML = escape(JSON.stringify(json, null, 2));
|
||||
floating.style.display = 'block';
|
||||
}
|
||||
|
||||
rpc.onsubmit = function onsubmit(ev) {
|
||||
const text = cmd.value || '';
|
||||
const argv = text.trim().split(/\s+/);
|
||||
const method = argv.shift();
|
||||
const params = [];
|
||||
|
||||
cmd.value = '';
|
||||
|
||||
for (const arg of argv) {
|
||||
let param;
|
||||
try {
|
||||
param = JSON.parse(arg);
|
||||
} catch (e) {
|
||||
param = arg;
|
||||
}
|
||||
params.push(param);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const result = await node.rpc.execute({ method, params });
|
||||
show(result);
|
||||
} catch (e) {
|
||||
show(e);
|
||||
}
|
||||
})();
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
send.onsubmit = function onsubmit(ev) {
|
||||
const value = document.getElementById('amount').value;
|
||||
const address = document.getElementById('address').value;
|
||||
|
||||
const options = {
|
||||
outputs: [{
|
||||
address: address,
|
||||
value: Amount.value(value)
|
||||
}]
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const mtx = await wdb.primary.createTX(options);
|
||||
await wdb.primary.sign(mtx);
|
||||
await node.relay(mtx.toTX());
|
||||
show(mtx);
|
||||
} catch (e) {
|
||||
show(e);
|
||||
}
|
||||
})();
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
newaddr.onmouseup = function onmouseup() {
|
||||
(async () => {
|
||||
try {
|
||||
await wdb.primary.createReceive();
|
||||
formatWallet(wdb.primary);
|
||||
} catch (e) {
|
||||
show(e);
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
function kb(size) {
|
||||
size /= 1000;
|
||||
return size.toFixed(2) + 'kb';
|
||||
}
|
||||
|
||||
function create(html) {
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = html;
|
||||
return el.firstChild;
|
||||
}
|
||||
|
||||
function escape(html, encode) {
|
||||
return html
|
||||
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function addItem(item, entry) {
|
||||
const height = entry ? entry.height : -1;
|
||||
|
||||
if (items.length === 20) {
|
||||
const el = items.shift();
|
||||
tdiv.removeChild(el);
|
||||
el.onmouseup = null;
|
||||
}
|
||||
|
||||
const el = create(''
|
||||
+ `<a style="display:block;" href="#${item.rhash()}">`
|
||||
+ `${item.rhash()} (${height} - ${kb(item.getSize())})`
|
||||
+ '</a>'
|
||||
);
|
||||
|
||||
tdiv.appendChild(el);
|
||||
|
||||
setMouseup(el, item);
|
||||
|
||||
items.push(el);
|
||||
|
||||
chainState.innerHTML = ''
|
||||
+ `tx=${node.chain.db.state.tx} `
|
||||
+ `coin=${node.chain.db.state.coin} `
|
||||
+ `value=${Amount.btc(node.chain.db.state.value)}`;
|
||||
}
|
||||
|
||||
function setMouseup(el, obj) {
|
||||
el.onmouseup = function onmouseup(ev) {
|
||||
show(obj);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
async function formatWallet(wallet) {
|
||||
try {
|
||||
await _formatWallet(wallet);
|
||||
} catch (e) {
|
||||
show(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function _formatWallet(wallet) {
|
||||
const {key, mnemonic} = wallet.master.toJSON(node.network, true);
|
||||
const account = await wallet.getAccount('default');
|
||||
const receive = account.receiveAddress();
|
||||
const nested = account.nestedAddress();
|
||||
const raddr = receive.toString(node.network);
|
||||
const naddr = nested ? nested.toString(node.network) : null;
|
||||
|
||||
let html = '';
|
||||
|
||||
html += '<b>Wallet</b><br>';
|
||||
|
||||
if (naddr) {
|
||||
html += `Current Address (p2wpkh): <b>${raddr}</b><br>`;
|
||||
html += `Current Address (p2wpkh behind p2sh): <b>${naddr}</b><br>`;
|
||||
} else {
|
||||
html += `Current Address: <b>${raddr}</b><br>`;
|
||||
}
|
||||
|
||||
html += `Extended Private Key: <b>${key.xprivkey}</b><br>`;
|
||||
html += `Mnemonic: <b>${mnemonic.phrase}</b><br>`;
|
||||
|
||||
const balance = await wallet.getBalance();
|
||||
|
||||
html += `Confirmed Balance: <b>${Amount.btc(balance.confirmed)}</b><br>`;
|
||||
html += `Unconfirmed Balance: <b>${Amount.btc(balance.unconfirmed)}</b><br>`;
|
||||
|
||||
const txs = await wallet.getHistory();
|
||||
const det = await wallet.toDetails(txs);
|
||||
|
||||
html += 'TXs:\n';
|
||||
wdiv.innerHTML = html;
|
||||
|
||||
for (const tx of det) {
|
||||
const el = create(
|
||||
`<a style="display:block;" href="#${tx.hash}">${tx.hash}</a>`);
|
||||
wdiv.appendChild(el);
|
||||
setMouseup(el, tx.toJSON());
|
||||
}
|
||||
}
|
||||
|
||||
node.chain.on('block', addItem);
|
||||
node.mempool.on('tx', addItem);
|
||||
|
||||
(async () => {
|
||||
await node.open();
|
||||
await node.connect();
|
||||
node.startSync();
|
||||
wdb.primary.on('balance', () => {
|
||||
formatWallet(wdb.primary);
|
||||
});
|
||||
formatWallet(wdb.primary);
|
||||
})().catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
@ -9,20 +9,18 @@
|
||||
const assert = require('assert');
|
||||
const EventEmitter = require('events');
|
||||
const bsock = require('bsock');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const bio = require('bufio');
|
||||
|
||||
class ProxySocket extends EventEmitter {
|
||||
constructor(uri) {
|
||||
super();
|
||||
|
||||
this.info = null;
|
||||
this.socket = bsock.socket();
|
||||
this.socket.reconnection = false;
|
||||
this.socket.connect(uri);
|
||||
|
||||
this.socket = bsock.connect(uri);
|
||||
this.sendBuffer = [];
|
||||
this.recvBuffer = [];
|
||||
this.paused = false;
|
||||
this.snonce = null;
|
||||
this.bytesWritten = 0;
|
||||
this.bytesRead = 0;
|
||||
this.remoteAddress = null;
|
||||
@ -34,20 +32,6 @@ class ProxySocket extends EventEmitter {
|
||||
}
|
||||
|
||||
init() {
|
||||
this.socket.bind('info', (info) => {
|
||||
if (this.closed)
|
||||
return;
|
||||
|
||||
this.info = info;
|
||||
|
||||
if (info.pow) {
|
||||
this.snonce = Buffer.from(info.snonce, 'hex');
|
||||
this.target = Buffer.from(info.target, 'hex');
|
||||
}
|
||||
|
||||
this.emit('info', info);
|
||||
});
|
||||
|
||||
this.socket.on('error', (err) => {
|
||||
console.error(err);
|
||||
});
|
||||
@ -87,7 +71,7 @@ class ProxySocket extends EventEmitter {
|
||||
this.emit('timeout');
|
||||
});
|
||||
|
||||
this.socket.bind('disconnect', () => {
|
||||
this.socket.on('disconnect', () => {
|
||||
if (this.closed)
|
||||
return;
|
||||
this.closed = true;
|
||||
@ -104,37 +88,7 @@ class ProxySocket extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.info) {
|
||||
this.once('info', connect.bind(this, port, host));
|
||||
return;
|
||||
}
|
||||
|
||||
let nonce = 0;
|
||||
|
||||
if (this.info.pow) {
|
||||
const bw = bio.write();
|
||||
|
||||
bw.writeU32(nonce);
|
||||
bw.writeBytes(this.snonce);
|
||||
bw.writeU32(port);
|
||||
bw.writeString(host, 'ascii');
|
||||
|
||||
const pow = bw.render();
|
||||
|
||||
console.log(
|
||||
'Solving proof of work to create socket (%d, %s) -- please wait.',
|
||||
port, host);
|
||||
|
||||
do {
|
||||
nonce += 1;
|
||||
assert(nonce <= 0xffffffff, 'Could not create socket.');
|
||||
pow.writeUInt32LE(nonce, 0, true);
|
||||
} while (hash256.digest(pow).compare(this.target) > 0);
|
||||
|
||||
console.log('Solved proof of work: %d', nonce);
|
||||
}
|
||||
|
||||
this.socket.fire('tcp connect', port, host, nonce);
|
||||
this.socket.fire('tcp connect', port, host);
|
||||
|
||||
for (const chunk of this.sendBuffer)
|
||||
this.write(chunk);
|
||||
@ -157,15 +111,6 @@ class ProxySocket extends EventEmitter {
|
||||
}
|
||||
|
||||
write(data, callback) {
|
||||
if (!this.info) {
|
||||
this.sendBuffer.push(data);
|
||||
|
||||
if (callback)
|
||||
callback();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this.bytesWritten += data.length;
|
||||
|
||||
this.socket.fire('tcp data', data.toString('hex'));
|
||||
@ -4,13 +4,7 @@ const assert = require('assert');
|
||||
const net = require('net');
|
||||
const EventEmitter = require('events');
|
||||
const bsock = require('bsock');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const IP = require('binet');
|
||||
const bio = require('bufio');
|
||||
|
||||
const TARGET = Buffer.from(
|
||||
'0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
'hex');
|
||||
|
||||
class WSProxy extends EventEmitter {
|
||||
constructor(options) {
|
||||
@ -20,8 +14,6 @@ class WSProxy extends EventEmitter {
|
||||
options = {};
|
||||
|
||||
this.options = options;
|
||||
this.target = options.target || TARGET;
|
||||
this.pow = options.pow === true;
|
||||
this.ports = new Set();
|
||||
this.io = bsock.server();
|
||||
this.sockets = new WeakMap();
|
||||
@ -51,18 +43,16 @@ class WSProxy extends EventEmitter {
|
||||
// mutating the websocket object.
|
||||
this.sockets.set(ws, state);
|
||||
|
||||
ws.fire('info', state.toInfo());
|
||||
|
||||
ws.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
ws.bind('tcp connect', (port, host, nonce) => {
|
||||
this.handleConnect(ws, port, host, nonce);
|
||||
ws.bind('tcp connect', (port, host) => {
|
||||
this.handleConnect(ws, port, host);
|
||||
});
|
||||
}
|
||||
|
||||
handleConnect(ws, port, host, nonce) {
|
||||
handleConnect(ws, port, host) {
|
||||
const state = this.sockets.get(ws);
|
||||
assert(state);
|
||||
|
||||
@ -80,30 +70,6 @@ class WSProxy extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pow) {
|
||||
if ((nonce >>> 0) !== nonce) {
|
||||
this.log('Client did not solve proof of work (%s).', state.host);
|
||||
ws.fire('tcp close');
|
||||
ws.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const bw = bio.write();
|
||||
bw.writeU32(nonce);
|
||||
bw.writeBytes(state.snonce);
|
||||
bw.writeU32(port);
|
||||
bw.writeString(host, 'ascii');
|
||||
|
||||
const pow = bw.render();
|
||||
|
||||
if (hash256.digest(pow).compare(this.target) > 0) {
|
||||
this.log('Client did not solve proof of work (%s).', state.host);
|
||||
ws.fire('tcp close');
|
||||
ws.destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let raw, addr;
|
||||
try {
|
||||
raw = IP.toBuffer(host);
|
||||
@ -206,7 +172,7 @@ class WSProxy extends EventEmitter {
|
||||
socket.resume();
|
||||
});
|
||||
|
||||
ws.bind('disconnect', () => {
|
||||
ws.on('disconnect', () => {
|
||||
socket.destroy();
|
||||
});
|
||||
}
|
||||
@ -223,22 +189,11 @@ class WSProxy extends EventEmitter {
|
||||
|
||||
class SocketState {
|
||||
constructor(server, socket) {
|
||||
this.pow = server.pow;
|
||||
this.target = server.target;
|
||||
this.snonce = nonce();
|
||||
this.socket = null;
|
||||
this.host = socket.host;
|
||||
this.remoteHost = null;
|
||||
}
|
||||
|
||||
toInfo() {
|
||||
return {
|
||||
pow: this.pow,
|
||||
target: this.target.toString('hex'),
|
||||
snonce: this.snonce.toString('hex')
|
||||
};
|
||||
}
|
||||
|
||||
connect(port, host) {
|
||||
this.socket = net.connect(port, host);
|
||||
this.remoteHost = IP.toHostname(host, port);
|
||||
@ -246,11 +201,4 @@ class SocketState {
|
||||
}
|
||||
}
|
||||
|
||||
function nonce() {
|
||||
const buf = Buffer.allocUnsafe(8);
|
||||
buf.writeUInt32LE(Math.random() * 0x100000000, 0, true);
|
||||
buf.writeUInt32LE(Math.random() * 0x100000000, 4, true);
|
||||
return buf;
|
||||
}
|
||||
|
||||
module.exports = WSProxy;
|
||||
|
||||
@ -33,12 +33,10 @@ bcoin.set = function set(network) {
|
||||
* Expose
|
||||
*/
|
||||
|
||||
/*
|
||||
// Blockchain
|
||||
bcoin.blockchain = require('./blockchain');
|
||||
bcoin.Chain = require('./blockchain/chain');
|
||||
bcoin.ChainEntry = require('./blockchain/chainentry');
|
||||
*/
|
||||
|
||||
// BTC
|
||||
bcoin.btc = require('./btc');
|
||||
@ -57,7 +55,6 @@ bcoin.HDPrivateKey = require('./hd/private');
|
||||
bcoin.HDPublicKey = require('./hd/public');
|
||||
bcoin.Mnemonic = require('./hd/mnemonic');
|
||||
|
||||
/*
|
||||
// Mempool
|
||||
bcoin.mempool = require('./mempool');
|
||||
bcoin.Fees = require('./mempool/fees');
|
||||
@ -79,7 +76,6 @@ bcoin.node = require('./node');
|
||||
bcoin.Node = require('./node/node');
|
||||
bcoin.FullNode = require('./node/fullnode');
|
||||
bcoin.SPVNode = require('./node/spvnode');
|
||||
*/
|
||||
|
||||
// Primitives
|
||||
bcoin.primitives = require('./primitives');
|
||||
@ -117,7 +113,6 @@ bcoin.Witness = require('./script/witness');
|
||||
bcoin.utils = require('./utils');
|
||||
bcoin.util = require('./utils/util');
|
||||
|
||||
/*
|
||||
// Wallet
|
||||
bcoin.wallet = require('./wallet');
|
||||
bcoin.WalletDB = require('./wallet/walletdb');
|
||||
@ -125,7 +120,6 @@ bcoin.WalletDB = require('./wallet/walletdb');
|
||||
// Workers
|
||||
bcoin.workers = require('./workers');
|
||||
bcoin.WorkerPool = require('./workers/workerpool');
|
||||
*/
|
||||
|
||||
// Package Info
|
||||
bcoin.pkg = require('./pkg');
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
"bcoin-spvnode": "./bin/spvnode"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -f {browser/,}{bcoin.js,bcoin-worker.js}",
|
||||
"clean": "rm -f {browser/,}{bcoin.js,bcoin-worker.js,app.js,worker.js}",
|
||||
"docs": "jsdoc -c jsdoc.json",
|
||||
"lint": "eslint $(cat .eslintfiles) || exit 0",
|
||||
"lint-file": "eslint",
|
||||
@ -82,6 +82,6 @@
|
||||
"webpack": "webpack --config webpack.browser.js",
|
||||
"webpack-browser": "webpack --config webpack.browser.js",
|
||||
"webpack-compat": "webpack --config webpack.compat.js",
|
||||
"webpack-node": "webpack --config webpack.node.js"
|
||||
"webpack-app": "webpack --config webpack.app.js"
|
||||
}
|
||||
}
|
||||
|
||||
32
webpack.app.js
Normal file
32
webpack.app.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const Path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const str = JSON.stringify;
|
||||
const env = process.env;
|
||||
|
||||
module.exports = {
|
||||
target: 'web',
|
||||
entry: {
|
||||
'app': './browser/src/app',
|
||||
'worker': './lib/workers/worker'
|
||||
},
|
||||
output: {
|
||||
path: Path.join(__dirname, 'browser'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
extensions: ['-browser.js', '.js', '.json']
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.BCOIN_NETWORK':
|
||||
str(env.BCOIN_NETWORK || 'main'),
|
||||
'process.env.BCOIN_WORKER_FILE':
|
||||
str(env.BCOIN_WORKER_FILE || '/bcoin-worker.js')
|
||||
}),
|
||||
new UglifyJsPlugin()
|
||||
]
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const str = JSON.stringify;
|
||||
const env = process.env;
|
||||
@ -9,11 +9,13 @@ const env = process.env;
|
||||
module.exports = {
|
||||
target: 'web',
|
||||
entry: {
|
||||
'bcoin': './lib/bcoin-browser',
|
||||
'bcoin': './lib/bcoin',
|
||||
'bcoin-worker': './lib/workers/worker'
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'browser'),
|
||||
library: 'bcoin',
|
||||
libraryTarget: 'umd',
|
||||
path: Path.join(__dirname, 'browser'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
resolve: {
|
||||
@ -27,10 +29,6 @@ module.exports = {
|
||||
'process.env.BCOIN_WORKER_FILE':
|
||||
str(env.BCOIN_WORKER_FILE || '/bcoin-worker.js')
|
||||
}),
|
||||
new UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: true
|
||||
}
|
||||
})
|
||||
new UglifyJsPlugin()
|
||||
]
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const str = JSON.stringify;
|
||||
const env = process.env;
|
||||
@ -9,11 +9,13 @@ const env = process.env;
|
||||
module.exports = {
|
||||
target: 'web',
|
||||
entry: {
|
||||
'bcoin': './lib/bcoin-browser',
|
||||
'bcoin': './lib/bcoin',
|
||||
'bcoin-worker': './lib/workers/worker'
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'browser'),
|
||||
library: 'bcoin',
|
||||
libraryTarget: 'umd',
|
||||
path: Path.join(__dirname, 'browser'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
resolve: {
|
||||
@ -23,7 +25,6 @@ module.exports = {
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules\/(?!bcoin|elliptic|bn\.js|n64)/,
|
||||
loader: 'babel-loader'
|
||||
}]
|
||||
},
|
||||
@ -34,10 +35,6 @@ module.exports = {
|
||||
'process.env.BCOIN_WORKER_FILE':
|
||||
str(env.BCOIN_WORKER_FILE || '/bcoin-worker.js')
|
||||
}),
|
||||
new UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
})
|
||||
new UglifyJsPlugin()
|
||||
]
|
||||
};
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const str = JSON.stringify;
|
||||
const env = process.env;
|
||||
|
||||
module.exports = {
|
||||
target: 'node',
|
||||
entry: {
|
||||
'bcoin': './lib/bcoin-browser',
|
||||
'bcoin-worker': './lib/workers/worker'
|
||||
},
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: '[name].js',
|
||||
libraryTarget: 'commonjs2'
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
extensions: ['.node', '.js', '.json'],
|
||||
alias: {
|
||||
'bindings': path.resolve(__dirname, 'webpack', 'bindings.js')
|
||||
}
|
||||
},
|
||||
node: {
|
||||
__dirname: false,
|
||||
__filename: false
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.node$/,
|
||||
loader: 'node-loader'
|
||||
}]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.BCOIN_WORKER_FILE':
|
||||
str(env.BCOIN_WORKER_FILE || 'bcoin-worker.js')
|
||||
}),
|
||||
new webpack.IgnorePlugin(/^utf-8-validate|bufferutil$/),
|
||||
new UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: true
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function bindings(name) {
|
||||
switch (name) {
|
||||
case 'leveldown':
|
||||
return require('leveldown/build/Release/leveldown.node');
|
||||
case 'bcoin-native':
|
||||
return require('bcoin-native/build/Release/bcoin-native.node');
|
||||
case 'secp256k1':
|
||||
return require('secp256k1/build/Release/secp256k1.node');
|
||||
}
|
||||
throw new Error(`Cannot find module "${name}".`);
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user