remove globals. add logger.
This commit is contained in:
parent
ba6d4821cc
commit
28f6ebe43f
407
README.md
407
README.md
@ -1,23 +1,26 @@
|
|||||||
# BCoin
|
# BCoin
|
||||||
|
|
||||||
**BCoin** is a bitcoin node which can act as an SPV node or a fully validating
|
**BCoin** is a bitcoin library which can also act as an SPV node or a full
|
||||||
fullnode. Bcoin runs in node.js, but it can also be browserified.
|
node. It is consensus aware and is up to date with the latest BIPs: it supports
|
||||||
|
segregated witness, versionbits, and CSV. It also has preliminary support for
|
||||||
|
bip151 (peer-to-peer encryption), bip152 (compact block relay), and bip114
|
||||||
|
(MAST). It runs in node.js, but it can also be browserified.
|
||||||
|
|
||||||
|
Try it in the browser: http://bcoin.io/browser.html
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- SPV mode
|
- HD Wallets (using BIP44 derivation and accounts)
|
||||||
- HD Wallets (using BIP44 (or optionally BIP45) derivation)
|
|
||||||
- Fully browserifiable
|
- Fully browserifiable
|
||||||
- Full block validation
|
- Full block validation
|
||||||
- Full block database
|
- Full block database
|
||||||
- Fully validating mempool (stored in-memory or on-disk)
|
- Fully validating mempool (stored in-memory or optionally on-disk)
|
||||||
- Wallet database
|
- Wallet database
|
||||||
- HTTP server which acts as a wallet server and can also serve:
|
- HTTP server which acts as a wallet server and can also serve:
|
||||||
blocks, txs (by hash/address), and utxos (by id/address).
|
blocks, txs (by hash/address), and utxos (by id/address).
|
||||||
- Fast UTXO retrieval by address for wallets (10000 utxos from 10000 different
|
- Full segregated witness support for block/tx validation and wallets.
|
||||||
addresses in ~700ms, 50000+ utxos from 10-100 addresses in ~400ms)
|
- Versionbits, CSV, BIP151, BIP152, MAST support.
|
||||||
- Segregated witness support for block/tx validation and wallets.
|
- SPV mode
|
||||||
- Versionbits suport.
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@ -41,16 +44,30 @@ Read the docs here: http://bcoin.io/docs/
|
|||||||
### Creating a blockchain and mempool
|
### Creating a blockchain and mempool
|
||||||
|
|
||||||
``` js
|
``` js
|
||||||
var bcoin = require('bcoin').set('regtest');
|
var bcoin = require('bcoin');
|
||||||
|
|
||||||
|
bcoin.set({
|
||||||
|
// Default network (so we can avoid passing
|
||||||
|
// the `network` option into every object below.
|
||||||
|
network: 'regtest',
|
||||||
|
// Enable the global worker pool
|
||||||
|
// for mining and transaction verification.
|
||||||
|
useWorkers: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start up a blockchain, mempool, and miner using in-memory
|
||||||
|
// databases (stored in a red-black tree instead of on-disk).
|
||||||
var chain = new bcoin.chain({ db: 'memory' });
|
var chain = new bcoin.chain({ db: 'memory' });
|
||||||
var mempool = new bcoin.mempool({ chain: chain, db: 'memory' });
|
var mempool = new bcoin.mempool({ chain: chain, db: 'memory' });
|
||||||
var miner = new bcoin.miner({ chain: chain, mempool: mempool });
|
var miner = new bcoin.miner({ chain: chain, mempool: mempool });
|
||||||
|
|
||||||
// Create a block "attempt"
|
// Open the miner (initialize the databases, etc).
|
||||||
|
// Miner will implicitly call `open` on chain and mempool.
|
||||||
miner.open(function(err) {
|
miner.open(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
|
// Create a block "attempt".
|
||||||
miner.createBlock(function(err, attempt) {
|
miner.createBlock(function(err, attempt) {
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
@ -78,10 +95,17 @@ miner.open(function(err) {
|
|||||||
``` js
|
``` js
|
||||||
var bcoin = require('bcoin').set('main');
|
var bcoin = require('bcoin').set('main');
|
||||||
|
|
||||||
var chain = new bcoin.chain({ db: 'leveldb' });
|
// Create a blockchain and store it in leveldb.
|
||||||
|
// `db` also accepts `rocksdb` and `lmdb`.
|
||||||
|
var prefix = process.env.HOME + '/my-bcoin-environment';
|
||||||
|
var chain = new bcoin.chain({ db: 'leveldb', location: prefix + '/chain' });
|
||||||
|
|
||||||
var mempool = new bcoin.mempool({ chain: chain, db: 'memory' });
|
var mempool = new bcoin.mempool({ chain: chain, db: 'memory' });
|
||||||
|
|
||||||
|
// Create a network pool of peers with a limit of 8 peers.
|
||||||
var pool = new bcoin.pool({ chain: chain, mempool: mempool, size: 8 });
|
var pool = new bcoin.pool({ chain: chain, mempool: mempool, size: 8 });
|
||||||
|
|
||||||
|
// Open the pool (implicitly opens mempool and chain).
|
||||||
pool.open(function(err) {
|
pool.open(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
@ -92,8 +116,9 @@ pool.open(function(err) {
|
|||||||
// Start the blockchain sync.
|
// Start the blockchain sync.
|
||||||
pool.startSync();
|
pool.startSync();
|
||||||
|
|
||||||
|
// Watch the action
|
||||||
chain.on('block', function(block) {
|
chain.on('block', function(block) {
|
||||||
console.log('Added block:');
|
console.log('Connected block to blockchain:');
|
||||||
console.log(block);
|
console.log(block);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,12 +129,12 @@ pool.open(function(err) {
|
|||||||
|
|
||||||
pool.on('tx', function(tx) {
|
pool.on('tx', function(tx) {
|
||||||
console.log('Saw transaction:');
|
console.log('Saw transaction:');
|
||||||
console.log(tx);
|
console.log(tx.rhash);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start up a segnet4 sync while
|
// Start up a segnet4 sync in-memory
|
||||||
// we're at it (because we can).
|
// while we're at it (because we can).
|
||||||
|
|
||||||
var tchain = new bcoin.chain({
|
var tchain = new bcoin.chain({
|
||||||
network: 'segnet4',
|
network: 'segnet4',
|
||||||
@ -162,10 +187,10 @@ tpool.open(function(err) {
|
|||||||
``` js
|
``` js
|
||||||
var bcoin = require('bcoin').set('testnet');
|
var bcoin = require('bcoin').set('testnet');
|
||||||
|
|
||||||
|
// SPV chains only store the chain headers.
|
||||||
var chain = new bcoin.chain({
|
var chain = new bcoin.chain({
|
||||||
db: 'leveldb',
|
db: 'leveldb',
|
||||||
// A custom chaindb location:
|
location: process.env.HOME + '/spvchain',
|
||||||
location: process.env.HOME + '/chain.db',
|
|
||||||
spv: true
|
spv: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -189,7 +214,7 @@ pool.open(function(err) {
|
|||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
console.log('Created wallet with address %s', wallet.getAddress());
|
console.log('Created wallet with address %s', wallet.getAddress('base58'));
|
||||||
|
|
||||||
// Add our address to the spv filter.
|
// Add our address to the spv filter.
|
||||||
pool.watchAddress(wallet.getAddress());
|
pool.watchAddress(wallet.getAddress());
|
||||||
@ -223,7 +248,15 @@ var node = bcoin.fullnode({
|
|||||||
useCheckpoints: true,
|
useCheckpoints: true,
|
||||||
debug: true,
|
debug: true,
|
||||||
// Primary wallet passphrase
|
// Primary wallet passphrase
|
||||||
passsphrase: 'node'
|
passsphrase: 'node',
|
||||||
|
logLevel: 'info'
|
||||||
|
});
|
||||||
|
|
||||||
|
// We get a lot of errors sometimes,
|
||||||
|
// usually from peers hanging up on us.
|
||||||
|
// Just ignore them for now.
|
||||||
|
node.on('error', function(err) {
|
||||||
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start the node
|
// Start the node
|
||||||
@ -243,25 +276,28 @@ node.open(function(err) {
|
|||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
console.log('Created wallet with address: %s', wallet.getAddress());
|
console.log('Created wallet with address: %s', wallet.getAddress('base58'));
|
||||||
|
|
||||||
// Start syncing the blockchain
|
// Start syncing the blockchain
|
||||||
// (this will take a while since we're a fullnode)
|
|
||||||
node.startSync();
|
node.startSync();
|
||||||
|
|
||||||
// Wait for balance and send it to a new address.
|
// Wait for balance and send it to a new address.
|
||||||
wallet.once('balance', function(balance) {
|
wallet.once('balance', function(balance) {
|
||||||
// Create a transaction, fill
|
// Create a transaction, fill
|
||||||
// it with coins, and sign it.
|
// it with coins, and sign it.
|
||||||
var to = {
|
var options = {
|
||||||
address: newReceiving,
|
subtractFee: true,
|
||||||
value: balance.total
|
outputs: [{
|
||||||
|
address: newReceiving,
|
||||||
|
value: balance.total
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
wallet.createTX({ subtractFee: true }, [to], function(err, tx) {
|
wallet.createTX(options, function(err, tx) {
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
wallet.sign(tx, function(err) {
|
// Need to pass our passphrase back in to sign!
|
||||||
|
wallet.sign(tx, 'foo', function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
@ -283,15 +319,15 @@ node.open(function(err) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
node.chain.on('block', function(block) {
|
node.chain.on('block', function(block) {
|
||||||
console.log(block);
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
node.mempool.on('tx', function(tx) {
|
node.mempool.on('tx', function(tx) {
|
||||||
console.log(block);
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
node.chain.on('full', function() {
|
node.chain.on('full', function() {
|
||||||
node.mempool.getAll(function(err, txs) {
|
node.mempool.getHistory(function(err, txs) {
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
@ -305,11 +341,16 @@ node.chain.on('full', function() {
|
|||||||
``` bash
|
``` bash
|
||||||
$ cd ~/bcoin
|
$ cd ~/bcoin
|
||||||
$ make # Browserify bcoin
|
$ make # Browserify bcoin
|
||||||
$ node browser/server.js 80 # Start up a simple webserver and websocket->tcp bridge
|
$ node browser/server.js 8080 # Start up a simple webserver and websocket->tcp bridge
|
||||||
|
$ chromium http://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see something like this: http://i.imgur.com/0pWySyZ.png
|
You should see something like this: http://i.imgur.com/0pWySyZ.png
|
||||||
|
|
||||||
|
This is a simple proof-of-concept. It's not a pretty interface. I hope to see
|
||||||
|
others doing something far more interesting. A browser extension may be better:
|
||||||
|
the chrome extension API exposes raw TCP access.
|
||||||
|
|
||||||
### CLI Usage
|
### CLI Usage
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
@ -319,36 +360,316 @@ $ node bin/bcoin-cli block 0
|
|||||||
# View primary wallet
|
# View primary wallet
|
||||||
$ node bin/bcoin-cli wallet primary
|
$ node bin/bcoin-cli wallet primary
|
||||||
# Send a tx
|
# Send a tx
|
||||||
$ node bin/bcoin-cli send [address] 0.01
|
$ node bin/bcoin-cli send primary [address] 0.01
|
||||||
|
# View balance
|
||||||
|
$ node bin/bcoin-cli balance primary
|
||||||
# View the mempool
|
# View the mempool
|
||||||
$ node bin/bcoin-cli mempool
|
$ node bin/bcoin-cli mempool
|
||||||
```
|
```
|
||||||
|
|
||||||
### TX creation
|
## TX creation
|
||||||
|
|
||||||
|
Normal transactions in bcoin are immutable. The primary TX object contains a
|
||||||
|
bunch of consensus and policy checking methods. A lot of it is for internal use
|
||||||
|
and pretty boring for users of this library.
|
||||||
|
|
||||||
|
BCoin also offers a mutable transaction object (MTX). Mutable transactions
|
||||||
|
inherit from the TX object, but can also be signed and modified.
|
||||||
|
|
||||||
|
``` js
|
||||||
|
var bcoin = require('bcoin');
|
||||||
|
var assert = require('assert');
|
||||||
|
var constants = bcoin.protocol.constants;
|
||||||
|
|
||||||
|
// Create an HD master keypair with a mnemonic.
|
||||||
|
var master = bcoin.hd.fromMnemonic();
|
||||||
|
|
||||||
|
// Derive another private hd key (we don't want to use our master key!).
|
||||||
|
var key = master.derive('m/44/0/0/0/0');
|
||||||
|
|
||||||
|
// Create a "keyring" object. A keyring object is basically a key manager that
|
||||||
|
// is also able to tell you info such as: your redeem script, your scripthash,
|
||||||
|
// your program hash, your pubkey hash, your scripthash program hash, etc.
|
||||||
|
// In this case, we'll make it simple and just add one key for a
|
||||||
|
// pubkeyhash address. `getPublicKey` returns the non-hd public key.
|
||||||
|
var keyring = new bcoin.keyring({ key: key.getPublicKey() });
|
||||||
|
|
||||||
|
console.log(keyring.getAddress());
|
||||||
|
|
||||||
|
// Create a fake coinbase for our funding.
|
||||||
|
var cb = new bcoin.mtx();
|
||||||
|
|
||||||
|
// Add a typical coinbase input
|
||||||
|
cb.addInput({
|
||||||
|
prevout: {
|
||||||
|
hash: constants.NULL_HASH,
|
||||||
|
index: 0
|
||||||
|
},
|
||||||
|
script: new bcoin.script(),
|
||||||
|
sequence: 0xffffffff
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send 50,000 satoshis to ourself.
|
||||||
|
cb.addOutput({
|
||||||
|
address: keyring.getAddress(),
|
||||||
|
value: 50000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create our redeeming transaction.
|
||||||
|
var tx = new bcoin.mtx();
|
||||||
|
|
||||||
|
// Add output 0 from our coinbase.
|
||||||
|
tx.addInput(cb, 0);
|
||||||
|
|
||||||
|
// Send 10,000 satoshis to ourself,
|
||||||
|
// creating a fee of 40,000 satoshis.
|
||||||
|
tx.addOutput({
|
||||||
|
address: keyring.getAddress(),
|
||||||
|
value: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sign input 0: pass in our keyring and private key.
|
||||||
|
tx.sign(0, keyring, key);
|
||||||
|
|
||||||
|
// Commit our transaction and make it immutable.
|
||||||
|
// This turns it from an MTX into a TX object.
|
||||||
|
tx = tx.toTX();
|
||||||
|
|
||||||
|
// The transaction should now verify.
|
||||||
|
assert(tx.verify());
|
||||||
|
assert(tx.getFee() === 40000);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Coin Selection
|
||||||
|
|
||||||
|
The above method works, but is pretty contrived. In reality, you probably
|
||||||
|
wouldn't select inputs and calculate the fee by hand. You would want a
|
||||||
|
change output added. BCoin has a nice method of dealing with this.
|
||||||
|
|
||||||
|
Let's try it more realistically:
|
||||||
|
|
||||||
|
``` js
|
||||||
|
var bcoin = require('bcoin');
|
||||||
|
var assert = require('assert');
|
||||||
|
var constants = bcoin.protocol.constants;
|
||||||
|
|
||||||
|
var master = bcoin.hd.fromMnemonic();
|
||||||
|
var key = master.derive('m/44/0/0/0/0');
|
||||||
|
var keyring = new bcoin.keyring({ key: key.getPublicKey() });
|
||||||
|
var cb = new bcoin.mtx();
|
||||||
|
|
||||||
|
cb.addInput({
|
||||||
|
prevout: {
|
||||||
|
hash: constants.NULL_HASH,
|
||||||
|
index: 0
|
||||||
|
},
|
||||||
|
script: new bcoin.script(),
|
||||||
|
sequence: 0xffffffff
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send 50,000 satoshis to ourselves.
|
||||||
|
cb.addOutput({
|
||||||
|
address: keyring.getAddress(),
|
||||||
|
value: 50000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Our available coins.
|
||||||
|
var coins = [];
|
||||||
|
|
||||||
|
// Convert the coinbase output to a Coin
|
||||||
|
// object and add it to our available coins.
|
||||||
|
// In reality you might get these coins from a wallet.
|
||||||
|
var coin = bcoin.coin.fromTX(cb, 0);
|
||||||
|
coins.push(coin);
|
||||||
|
|
||||||
|
// Create our redeeming transaction.
|
||||||
|
var tx = new bcoin.mtx();
|
||||||
|
|
||||||
|
// Send 10,000 satoshis to ourself.
|
||||||
|
tx.addOutput({
|
||||||
|
address: keyring.getAddress(),
|
||||||
|
value: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now that we've created the output, we can do some coin selection (the output
|
||||||
|
// must be added first so we know how much money is needed and also so we can
|
||||||
|
// accurately estimate the size for fee calculation).
|
||||||
|
|
||||||
|
// Select coins from our array and add inputs.
|
||||||
|
// Calculate fee and add a change output.
|
||||||
|
tx.fill(coins, {
|
||||||
|
// Use a rate of 10,000 satoshis per kb.
|
||||||
|
// With the `fullnode` object, you can
|
||||||
|
// use the fee estimator for this instead
|
||||||
|
// of blindly guessing.
|
||||||
|
rate: 10000,
|
||||||
|
// Send the change back to ourselves.
|
||||||
|
changeAddress: keyring.getAddress()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sign input 0
|
||||||
|
tx.sign(0, keyring, key);
|
||||||
|
|
||||||
|
// Commit our transaction and make it immutable.
|
||||||
|
// This turns it from an MTX into a TX.
|
||||||
|
tx = tx.toTX();
|
||||||
|
|
||||||
|
// The transaction should now verify.
|
||||||
|
assert(tx.verify());
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scripting
|
||||||
|
|
||||||
|
Scripts are array-like objects with some helper functions.
|
||||||
|
|
||||||
|
``` js
|
||||||
|
var bcoin = require('bcoin');
|
||||||
|
var assert = require('assert');
|
||||||
|
var bn = bcoin.bn;
|
||||||
|
var opcodes = bcoin.script.opcodes;
|
||||||
|
|
||||||
|
var output = new bcoin.script();
|
||||||
|
output.push(opcodes.OP_DROP);
|
||||||
|
output.push(opcodes.OP_ADD);
|
||||||
|
output.push(new bn(7));
|
||||||
|
output.push(opcodes.OP_NUMEQUAL);
|
||||||
|
// Compile the script to its binary representation
|
||||||
|
// (you must do this if you change something!).
|
||||||
|
output.compile();
|
||||||
|
assert(output.getSmall(2) === 7); // compiled as OP_7
|
||||||
|
|
||||||
|
var input = new bcoin.script();
|
||||||
|
input.set(0, 'hello world'); // add some metadata
|
||||||
|
input.push(new bn(2));
|
||||||
|
input.push(new bn(5));
|
||||||
|
input.push(input.shift());
|
||||||
|
assert(input.getString(2) === 'hello world');
|
||||||
|
input.compile();
|
||||||
|
|
||||||
|
// A stack is another array-like object which contains
|
||||||
|
// only Buffers (whereas scripts contain Opcode objects).
|
||||||
|
var stack = new bcoin.stack();
|
||||||
|
input.execute(stack);
|
||||||
|
output.execute(stack);
|
||||||
|
// Verify the script was successful in its execution:
|
||||||
|
assert(stack.length === 1);
|
||||||
|
assert(bcoin.script.bool(stack.pop()) === true);
|
||||||
|
```
|
||||||
|
|
||||||
|
Using a witness would be similar, but witnesses do not get executed, they
|
||||||
|
simply _become_ the stack. The witness object itself is very similar to the
|
||||||
|
Stack object (an array-like object containing Buffers).
|
||||||
|
|
||||||
|
``` js
|
||||||
|
var witness = new bcoin.witness();
|
||||||
|
witness.push(new bn(2));
|
||||||
|
witness.push(new bn(5));
|
||||||
|
witness.push('hello world');
|
||||||
|
|
||||||
|
var stack = witness.toStack();
|
||||||
|
output.execute(stack);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wallet usage
|
||||||
|
|
||||||
|
BCoin maintains a wallet database which contains every wallet. Wallets are _not
|
||||||
|
usable_ without also using a wallet database. For testing, the wallet database
|
||||||
|
can be in-memory, but it must be there.
|
||||||
|
|
||||||
|
Wallets in bcoin use bip44. They also originally supported bip45 for multisig,
|
||||||
|
but support was removed to reduce code complexity, and also because bip45
|
||||||
|
doesn't seem to add any benefit in practice.
|
||||||
|
|
||||||
|
The wallet database can contain many different wallets, with many different
|
||||||
|
accounts, with many different addresses for each account. BCoin should
|
||||||
|
theoretically be able to scale to hundreds of thousands of
|
||||||
|
wallets/accounts/addresses.
|
||||||
|
|
||||||
|
Each account can be of a different type. You could have a pubkeyhash account,
|
||||||
|
as well as a multisig account, a witness pubkeyhash account, etc.
|
||||||
|
|
||||||
|
Note that accounts should not be accessed directly from the public API. They do
|
||||||
|
not have locks which can lead to race conditions during writes.
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
### Scripting
|
## HTTP API & Websocket Events
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
### Wallet usage
|
## Design
|
||||||
|
|
||||||
TODO
|
BCoin is thoroughly event driven. It has a fullnode object, but BCoin was
|
||||||
|
specifically designed so the mempool, blockchain, p2p pool, and wallet database
|
||||||
|
could all be used separately. All the fullnode object does is tie these things
|
||||||
|
together. It's essentially a huge proxying of events. The general communication
|
||||||
|
between these things looks something like this:
|
||||||
|
|
||||||
### Accessing the mempool
|
```
|
||||||
|
pool -> block event -> chain
|
||||||
|
pool -> tx event -> mempool
|
||||||
|
chain -> block event -> mempool/miner
|
||||||
|
chain -> tx event -> walletdb
|
||||||
|
chain -> reorg event -> walletdb/mempool/miner
|
||||||
|
mempool -> tx event -> walletdb/miner
|
||||||
|
miner -> block event -> chain
|
||||||
|
walletdb -> tx event -> websocket server
|
||||||
|
websocket server -> tx event -> websocket client
|
||||||
|
http client -> tx -> http server -> mempool
|
||||||
|
```
|
||||||
|
|
||||||
TODO
|
Not only does the loose coupling make testing easier, it ensures people can
|
||||||
|
utilize bcoin for many use cases.
|
||||||
|
|
||||||
### HTTP server/client
|
### Performance
|
||||||
|
|
||||||
TODO
|
Non-javscript people reading this may think using javascript isn't a wise
|
||||||
|
descision.
|
||||||
|
|
||||||
|
#### Javascript
|
||||||
|
|
||||||
|
Javascript is inherently slow due to how dynamic it is, but modern JITs have
|
||||||
|
solved this issue using very clever optimization and dynamic recompilation
|
||||||
|
techniques. v8 in some cases can [rival the speed of C++][v8] if the code is
|
||||||
|
well-written.
|
||||||
|
|
||||||
|
#### Concurrency
|
||||||
|
|
||||||
|
BCoin runs in node.js, so the javascript code is limited to one thread. We
|
||||||
|
solve this limitation by spinning up persistent worker processes for
|
||||||
|
transaction verification (webworkers when in the browser). This ensures the
|
||||||
|
blockchain and mempool do not block the master process very much. It also means
|
||||||
|
transaction verification can be parallelized.
|
||||||
|
|
||||||
|
Strangely enough, workers are faster in the browser than they are in node since
|
||||||
|
you are allowed to share memory between threads using the transferrable api
|
||||||
|
(Uint8Arrays can be "transferred" to another thread). In node, you have to pipe
|
||||||
|
data to another process.
|
||||||
|
|
||||||
|
But of course, there is a benefit to having a multi-process architecture: the
|
||||||
|
worker processes can die on their own without disturbing the master process.
|
||||||
|
|
||||||
|
BCoin uses [secp256k1-node][secp256k1-node] for ecdsa verification, which is a
|
||||||
|
node.js binding to Pieter Wuille's blazingly fast [libsecp256k1][libsecp256k1]
|
||||||
|
library.
|
||||||
|
|
||||||
|
In the browser, bcoin will use [elliptic][elliptic], the fastest javascript
|
||||||
|
ecdsa implementation. It will obviously never beat C and hand-optimized
|
||||||
|
assembly, but it's still usable.
|
||||||
|
|
||||||
|
#### Benefits
|
||||||
|
|
||||||
|
The real feature of javascript is that your code will run almost anywhere. With
|
||||||
|
bcoin, we now have a full node that will run on almost any browser, on laptops,
|
||||||
|
on servers, on smartphones, on most devices you can imagine, even by simply
|
||||||
|
visting a webpage.
|
||||||
|
|
||||||
## LICENSE
|
## LICENSE
|
||||||
|
|
||||||
This software is licensed under the MIT License.
|
This software is licensed under the MIT License.
|
||||||
|
|
||||||
Copyright Fedor Indutny, 2014-2016.
|
Copyright Fedor Indutny, 2014-2016.
|
||||||
|
Copyright Christopher Jeffrey, 2014-2016.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
copy of this software and associated documentation files (the
|
copy of this software and associated documentation files (the
|
||||||
@ -369,5 +690,7 @@ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|||||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
[bip37]: https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki
|
[v8]: https://www.youtube.com/watch?v=UJPdhx5zTaw
|
||||||
[escrow]: https://en.bitcoin.it/wiki/Contract#Example_2:_Escrow_and_dispute_mediation
|
[libsecp256k1]: https://github.com/bitcoin-core/secp256k1
|
||||||
|
[secp256k1-node]: https://github.com/cryptocoinjs/secp256k1-node
|
||||||
|
[elliptic]: https://github.com/indutny/elliptic
|
||||||
|
|||||||
142
bin/bcoin-cli
142
bin/bcoin-cli
@ -58,7 +58,7 @@ function createWallet(callback) {
|
|||||||
client.createWallet(options, function(err, wallet) {
|
client.createWallet(options, function(err, wallet) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(wallet);
|
utils.log(wallet);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ function addKey(callback) {
|
|||||||
client.addKey(id, argv.account, keys, function(err, wallet) {
|
client.addKey(id, argv.account, keys, function(err, wallet) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print('added');
|
utils.log('added');
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ function createAccount(callback) {
|
|||||||
client.createWalletAccount(id, account, function(err, account) {
|
client.createWalletAccount(id, account, function(err, account) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(account);
|
utils.log(account);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ function getAccounts(callback) {
|
|||||||
client.getWalletAccounts(id, function(err, accounts) {
|
client.getWalletAccounts(id, function(err, accounts) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(accounts);
|
utils.log(accounts);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ function removeKey(callback) {
|
|||||||
client.removeKey(id, argv.account, keys, function(err) {
|
client.removeKey(id, argv.account, keys, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print('removed');
|
utils.log('removed');
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ function getWallet(callback) {
|
|||||||
client.getWallet(id, argv.account, passphrase, function(err, wallet) {
|
client.getWallet(id, argv.account, passphrase, function(err, wallet) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(wallet);
|
utils.log(wallet);
|
||||||
wallet.destroy();
|
wallet.destroy();
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
@ -132,7 +132,7 @@ function getTX(callback) {
|
|||||||
return client.getTXByAddress(hash, function(err, txs) {
|
return client.getTXByAddress(hash, function(err, txs) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(txs);
|
utils.log(txs);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -142,11 +142,11 @@ function getTX(callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (!tx) {
|
if (!tx) {
|
||||||
utils.print('TX not found.');
|
utils.log('TX not found.');
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -162,13 +162,13 @@ function getBlock(callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (!block) {
|
if (!block) {
|
||||||
utils.print('Block not found.');
|
utils.log('Block not found.');
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.print(block);
|
utils.log(block);
|
||||||
utils.print('Coinbase Data:');
|
utils.log('Coinbase Data:');
|
||||||
utils.print(block.txs[0].inputs[0].script.getCoinbaseFlags());
|
utils.log(block.txs[0].inputs[0].script.getCoinbaseFlags());
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ function getCoin(callback) {
|
|||||||
return client.getCoinsByAddress(hash, function(err, coins) {
|
return client.getCoinsByAddress(hash, function(err, coins) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(coins);
|
utils.log(coins);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -190,11 +190,11 @@ function getCoin(callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (!coin) {
|
if (!coin) {
|
||||||
utils.print('Coin not found.');
|
utils.log('Coin not found.');
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.print(coin);
|
utils.log(coin);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ function getWalletHistory(callback) {
|
|||||||
client.getWalletHistory(id, argv.account, function(err, txs) {
|
client.getWalletHistory(id, argv.account, function(err, txs) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(txs);
|
utils.log(txs);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -213,29 +213,29 @@ function listenWallet(callback) {
|
|||||||
var id = getID();
|
var id = getID();
|
||||||
client.join(id);
|
client.join(id);
|
||||||
client.on('tx', function(tx, map) {
|
client.on('tx', function(tx, map) {
|
||||||
utils.print('TX:');
|
utils.log('TX:');
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
utils.print(map);
|
utils.log(map);
|
||||||
});
|
});
|
||||||
client.on('updated', function(tx, map) {
|
client.on('updated', function(tx, map) {
|
||||||
utils.print('TX updated:');
|
utils.log('TX updated:');
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
utils.print(map);
|
utils.log(map);
|
||||||
});
|
});
|
||||||
client.on('confirmed', function(tx, map) {
|
client.on('confirmed', function(tx, map) {
|
||||||
utils.print('TX updated:');
|
utils.log('TX updated:');
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
utils.print(map);
|
utils.log(map);
|
||||||
});
|
});
|
||||||
client.on('address', function(receive, change) {
|
client.on('address', function(receive, change) {
|
||||||
utils.print('New addresses allocated:');
|
utils.log('New addresses allocated:');
|
||||||
utils.print(receive);
|
utils.log(receive);
|
||||||
utils.print(change);
|
utils.log(change);
|
||||||
});
|
});
|
||||||
client.on('balance', function(tx, map) {
|
client.on('balance', function(tx, map) {
|
||||||
utils.print('Balance:');
|
utils.log('Balance:');
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
utils.print(map);
|
utils.log(map);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,9 +244,9 @@ function getBalance(callback) {
|
|||||||
client.getWalletBalance(id, argv.account, function(err, balance) {
|
client.getWalletBalance(id, argv.account, function(err, balance) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print('Confirmed: %s', utils.btc(balance.confirmed));
|
utils.log('Confirmed: %s', utils.btc(balance.confirmed));
|
||||||
utils.print('Unconfirmed: %s', utils.btc(balance.unconfirmed));
|
utils.log('Unconfirmed: %s', utils.btc(balance.unconfirmed));
|
||||||
utils.print('Total: %s', utils.btc(balance.total));
|
utils.log('Total: %s', utils.btc(balance.total));
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -255,7 +255,7 @@ function getMempool(callback) {
|
|||||||
client.getMempool(function(err, txs) {
|
client.getMempool(function(err, txs) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(txs);
|
utils.log(txs);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -274,8 +274,8 @@ function sendTX(callback) {
|
|||||||
client.walletSend(id, {outputs:[output]}, function(err, tx) {
|
client.walletSend(id, {outputs:[output]}, function(err, tx) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
utils.print(tx.toRaw('hex'));
|
utils.log(tx.toRaw('hex'));
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -294,8 +294,8 @@ function createTX(callback) {
|
|||||||
client.walletCreate(id, options, [output], function(err, tx) {
|
client.walletCreate(id, options, [output], function(err, tx) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
utils.print(tx.toRaw('hex'));
|
utils.log(tx.toRaw('hex'));
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -307,8 +307,8 @@ function signTX(callback) {
|
|||||||
client.walletSign(id, tx, options, function(err, tx) {
|
client.walletSign(id, tx, options, function(err, tx) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
utils.print(tx.toRaw('hex'));
|
utils.log(tx.toRaw('hex'));
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -319,7 +319,7 @@ function zap(callback) {
|
|||||||
client.walletZap(id, argv.account, age, function(err) {
|
client.walletZap(id, argv.account, age, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print('Zapped!');
|
utils.log('Zapped!');
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -329,8 +329,8 @@ function broadcast(callback) {
|
|||||||
client.broadcast(tx, function(err, tx) {
|
client.broadcast(tx, function(err, tx) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print('Broadcasted:');
|
utils.log('Broadcasted:');
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -340,7 +340,7 @@ function view(callback) {
|
|||||||
client.walletFill(tx, function(err, tx) {
|
client.walletFill(tx, function(err, tx) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -386,32 +386,32 @@ function main(callback) {
|
|||||||
case 'block':
|
case 'block':
|
||||||
return getBlock(callback);
|
return getBlock(callback);
|
||||||
default:
|
default:
|
||||||
utils.print('Unrecognized command.');
|
utils.log('Unrecognized command.');
|
||||||
utils.print('Commands:');
|
utils.log('Commands:');
|
||||||
utils.print(' $ wallet [id] --keys [hdkeys]'
|
utils.log(' $ wallet [id] --keys [hdkeys]'
|
||||||
+ ' --type [pubkeyhash/multisig] -m [m-value]'
|
+ ' --type [pubkeyhash/multisig] -m [m-value]'
|
||||||
+ ' -n [n-value] --witness: View or create wallet by ID.');
|
+ ' -n [n-value] --witness: View or create wallet by ID.');
|
||||||
utils.print(' $ listen [id]: Listen for wallet events.');
|
utils.log(' $ listen [id]: Listen for wallet events.');
|
||||||
utils.print(' $ getwallet [id]: View wallet by ID.');
|
utils.log(' $ getwallet [id]: View wallet by ID.');
|
||||||
utils.print(' $ addkey [id] --keys [hdkeys]: Add keys to wallet.');
|
utils.log(' $ addkey [id] --keys [hdkeys]: Add keys to wallet.');
|
||||||
utils.print(' $ rmkey [id] --keys [hdkeys]: Remove keys from wallet.');
|
utils.log(' $ rmkey [id] --keys [hdkeys]: Remove keys from wallet.');
|
||||||
utils.print(' $ balance [id]: Get wallet balance.');
|
utils.log(' $ balance [id]: Get wallet balance.');
|
||||||
utils.print(' $ history [id]: View wallet TX history.');
|
utils.log(' $ history [id]: View wallet TX history.');
|
||||||
utils.print(' $ accounts [id]: List account names.');
|
utils.log(' $ accounts [id]: List account names.');
|
||||||
utils.print(' $ account [id] [acct]: Get account details.');
|
utils.log(' $ account [id] [acct]: Get account details.');
|
||||||
utils.print(' $ send [id] [address] [value] --script [code]: Send transaction.');
|
utils.log(' $ send [id] [address] [value] --script [code]: Send transaction.');
|
||||||
utils.print(' $ create [id] [address] [value] --script [code]: Create transaction.');
|
utils.log(' $ create [id] [address] [value] --script [code]: Create transaction.');
|
||||||
utils.print(' $ sign [id] [tx-hex]: Sign transaction.');
|
utils.log(' $ sign [id] [tx-hex]: Sign transaction.');
|
||||||
utils.print(' $ zap [id] --age [age]: Zap pending wallet TXs.');
|
utils.log(' $ zap [id] --age [age]: Zap pending wallet TXs.');
|
||||||
utils.print(' $ broadcast [tx-hex]: Broadcast transaction.');
|
utils.log(' $ broadcast [tx-hex]: Broadcast transaction.');
|
||||||
utils.print(' $ view [tx-hex]: View transaction.');
|
utils.log(' $ view [tx-hex]: View transaction.');
|
||||||
utils.print(' $ mempool: Get mempool snapshot.');
|
utils.log(' $ mempool: Get mempool snapshot.');
|
||||||
utils.print(' $ tx [hash/address]: View transactions.');
|
utils.log(' $ tx [hash/address]: View transactions.');
|
||||||
utils.print(' $ coin [hash+index/address]: View coins.');
|
utils.log(' $ coin [hash+index/address]: View coins.');
|
||||||
utils.print(' $ block [hash/height]: View block.');
|
utils.log(' $ block [hash/height]: View block.');
|
||||||
utils.print('Other Options:');
|
utils.log('Other Options:');
|
||||||
utils.print(' --passphrase [passphrase]: For signing and account creation.');
|
utils.log(' --passphrase [passphrase]: For signing and account creation.');
|
||||||
utils.print(' --account [acctname]: Account name.');
|
utils.log(' --account [acctname]: Account name.');
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,7 +476,7 @@ client.getInfo(function(err, info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!argv.args[0])
|
if (!argv.args[0])
|
||||||
utils.print(info);
|
utils.log(info);
|
||||||
|
|
||||||
main(function(err) {
|
main(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
15
bin/node
15
bin/node
@ -2,28 +2,29 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bcoin = require('../').set({ debug: true, debugFile: true });
|
var bcoin = require('../');
|
||||||
var utils = bcoin.utils;
|
var utils = bcoin.utils;
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
|
|
||||||
process.on('uncaughtException', function(err) {
|
process.on('uncaughtException', function(err) {
|
||||||
bcoin.debug(err.stack);
|
node.logger.debug(err.stack);
|
||||||
bcoin.error(err);
|
node.logger.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
var node = new bcoin.fullnode({
|
var node = new bcoin.fullnode({
|
||||||
|
logLevel: 'debug',
|
||||||
|
logFile: true,
|
||||||
|
db: 'leveldb',
|
||||||
prune: process.argv.indexOf('--prune') !== -1,
|
prune: process.argv.indexOf('--prune') !== -1,
|
||||||
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
|
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
|
||||||
listen: process.argv.indexOf('--listen') !== -1,
|
|
||||||
selfish: process.argv.indexOf('--selfish') !== -1,
|
selfish: process.argv.indexOf('--selfish') !== -1,
|
||||||
headers: process.argv.indexOf('--headers') !== -1,
|
headers: process.argv.indexOf('--headers') !== -1,
|
||||||
mine: process.argv.indexOf('--mine') !== -1,
|
|
||||||
parallel: process.argv.indexOf('--parallel') !== -1
|
parallel: process.argv.indexOf('--parallel') !== -1
|
||||||
});
|
});
|
||||||
|
|
||||||
node.on('error', function(err) {
|
node.on('error', function(err) {
|
||||||
bcoin.error(err);
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
node.open(function(err) {
|
node.open(function(err) {
|
||||||
@ -34,7 +35,7 @@ node.open(function(err) {
|
|||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
if (!node.options.mine) {
|
if (process.argv.indexOf('--mine') === -1) {
|
||||||
node.startSync();
|
node.startSync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
14
bin/spvnode
14
bin/spvnode
@ -2,18 +2,20 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bcoin = require('../').set({ debug: true, debugFile: true });
|
var bcoin = require('../');
|
||||||
var utils = bcoin.utils;
|
var utils = bcoin.utils;
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
|
|
||||||
var node = bcoin.spvnode({
|
var node = bcoin.spvnode({
|
||||||
// passphrase: 'node',
|
logLevel: 'debug',
|
||||||
preload: process.argv.indexOf('--preload') !== -1,
|
logFile: true,
|
||||||
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1
|
db: 'leveldb',
|
||||||
|
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
|
||||||
|
headers: process.argv.indexOf('--headers') !== -1
|
||||||
});
|
});
|
||||||
|
|
||||||
node.on('error', function(err) {
|
node.on('error', function(err) {
|
||||||
bcoin.debug(err.stack + '');
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
node.open(function(err) {
|
node.open(function(err) {
|
||||||
@ -23,7 +25,7 @@ node.open(function(err) {
|
|||||||
if (process.argv.indexOf('--test') !== -1) {
|
if (process.argv.indexOf('--test') !== -1) {
|
||||||
node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8');
|
node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8');
|
||||||
node.on('tx', function(tx) {
|
node.on('tx', function(tx) {
|
||||||
utils.print(tx);
|
utils.log(tx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -85,7 +85,7 @@ ProxySocket.prototype.connect = function connect(port, host) {
|
|||||||
return this.once('info', connect.bind(this, port, host));
|
return this.once('info', connect.bind(this, port, host));
|
||||||
|
|
||||||
if (this.info.pow) {
|
if (this.info.pow) {
|
||||||
bcoin.debug(
|
utils.log(
|
||||||
'Solving proof of work to create socket (%d, %s) -- please wait.',
|
'Solving proof of work to create socket (%d, %s) -- please wait.',
|
||||||
port, host);
|
port, host);
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ ProxySocket.prototype.connect = function connect(port, host) {
|
|||||||
pow.writeUInt32LE(nonce, 0, true);
|
pow.writeUInt32LE(nonce, 0, true);
|
||||||
} while (utils.cmp(utils.dsha256(pow), this.target) >= 0);
|
} while (utils.cmp(utils.dsha256(pow), this.target) >= 0);
|
||||||
|
|
||||||
bcoin.debug('Solved proof of work: %d', nonce);
|
utils.log('Solved proof of work: %d', nonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.socket.emit('tcp connect', port, host, nonce);
|
this.socket.emit('tcp connect', port, host, nonce);
|
||||||
|
|||||||
@ -53,6 +53,12 @@ function Address(options) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Address.prototype.fromOptions = function fromOptions(options) {
|
Address.prototype.fromOptions = function fromOptions(options) {
|
||||||
|
if (typeof options === 'string')
|
||||||
|
return this.fromBase58(options);
|
||||||
|
|
||||||
|
if (Buffer.isBuffer(options))
|
||||||
|
return this.fromRaw(options);
|
||||||
|
|
||||||
return this.fromHash(
|
return this.fromHash(
|
||||||
options.hash,
|
options.hash,
|
||||||
options.type,
|
options.type,
|
||||||
|
|||||||
@ -59,6 +59,8 @@ AsyncObject.prototype.open = function open(callback) {
|
|||||||
callback = utils.wrap(callback, unlock);
|
callback = utils.wrap(callback, unlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.emit('preopen');
|
||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
this._open(function(err) {
|
this._open(function(err) {
|
||||||
@ -106,6 +108,8 @@ AsyncObject.prototype.close = function close(callback) {
|
|||||||
callback = utils.wrap(callback, unlock);
|
callback = utils.wrap(callback, unlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.emit('preclose');
|
||||||
|
|
||||||
this.closing = true;
|
this.closing = true;
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var bcoin = require('./env');
|
var bcoin = require('./env');
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
|
var assert = utils.assert;
|
||||||
var constants = bcoin.protocol.constants;
|
var constants = bcoin.protocol.constants;
|
||||||
var chachapoly = require('./chachapoly');
|
var chachapoly = require('./chachapoly');
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ BIP151.prototype.init = function init(publicKey) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
BIP151.prototype.rekey = function rekey() {
|
BIP151.prototype.rekey = function rekey() {
|
||||||
|
assert(this.mac, 'Cannot rekey before initialization.');
|
||||||
this.mac = utils.hash256(this.mac);
|
this.mac = utils.hash256(this.mac);
|
||||||
this.k1 = this.mac.slice(0, 32);
|
this.k1 = this.mac.slice(0, 32);
|
||||||
this.k2 = this.mac.slice(32, 64);
|
this.k2 = this.mac.slice(32, 64);
|
||||||
@ -166,12 +168,12 @@ BIP151.prototype.encack = function encack(data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i === publicKey.length)
|
if (i === publicKey.length) {
|
||||||
this.rekey();
|
this.rekey();
|
||||||
else
|
return;
|
||||||
this.init(publicKey);
|
}
|
||||||
|
|
||||||
return this;
|
this.init(publicKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
BIP151.prototype.feed = function feed(data) {
|
BIP151.prototype.feed = function feed(data) {
|
||||||
|
|||||||
@ -66,6 +66,8 @@ function Chain(options) {
|
|||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
this.network = bcoin.network.get(options.network);
|
this.network = bcoin.network.get(options.network);
|
||||||
|
this.logger = options.logger || bcoin.defaultLogger;
|
||||||
|
this.profiler = options.profiler;
|
||||||
this.db = new bcoin.chaindb(this, options);
|
this.db = new bcoin.chaindb(this, options);
|
||||||
this.total = 0;
|
this.total = 0;
|
||||||
this.currentBlock = null;
|
this.currentBlock = null;
|
||||||
@ -101,7 +103,7 @@ Chain.prototype._init = function _init() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.locker.on('purge', function(total, size) {
|
this.locker.on('purge', function(total, size) {
|
||||||
bcoin.debug('Warning: %dmb of pending objects. Purging.', utils.mb(size));
|
self.logger.warning('Warning: %dmb of pending objects. Purging.', utils.mb(size));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hook into events for debugging
|
// Hook into events for debugging
|
||||||
@ -112,12 +114,12 @@ Chain.prototype._init = function _init() {
|
|||||||
if (self.options.spv)
|
if (self.options.spv)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bcoin.debug('Block %s (%d) added to chain.',
|
self.logger.info('Block %s (%d) added to chain.',
|
||||||
utils.revHex(entry.hash), entry.height);
|
utils.revHex(entry.hash), entry.height);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('competitor', function(block, entry) {
|
this.on('competitor', function(block, entry) {
|
||||||
bcoin.debug('Heads up: Competing chain at height %d:'
|
self.logger.warning('Heads up: Competing chain at height %d:'
|
||||||
+ ' tip-height=%d competitor-height=%d'
|
+ ' tip-height=%d competitor-height=%d'
|
||||||
+ ' tip-hash=%s competitor-hash=%s'
|
+ ' tip-hash=%s competitor-hash=%s'
|
||||||
+ ' tip-chainwork=%s competitor-chainwork=%s'
|
+ ' tip-chainwork=%s competitor-chainwork=%s'
|
||||||
@ -133,15 +135,17 @@ Chain.prototype._init = function _init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.on('resolved', function(block, entry) {
|
this.on('resolved', function(block, entry) {
|
||||||
bcoin.debug('Orphan %s (%d) was resolved.', block.rhash, entry.height);
|
self.logger.debug('Orphan %s (%d) was resolved.',
|
||||||
|
block.rhash, entry.height);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('checkpoint', function(block, height) {
|
this.on('checkpoint', function(block, height) {
|
||||||
bcoin.debug('Hit checkpoint block %s (%d).', block.rhash, height);
|
self.logger.debug('Hit checkpoint block %s (%d).',
|
||||||
|
block.rhash, height);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('fork', function(block, height, expected) {
|
this.on('fork', function(block, height, expected) {
|
||||||
bcoin.debug(
|
self.logger.warning(
|
||||||
'Fork at height %d: expected=%s received=%s',
|
'Fork at height %d: expected=%s received=%s',
|
||||||
height,
|
height,
|
||||||
utils.revHex(expected),
|
utils.revHex(expected),
|
||||||
@ -150,19 +154,21 @@ Chain.prototype._init = function _init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.on('invalid', function(block, height) {
|
this.on('invalid', function(block, height) {
|
||||||
bcoin.debug('Invalid block at height %d: hash=%s', height, block.rhash);
|
self.logger.warning('Invalid block at height %d: hash=%s',
|
||||||
|
height, block.rhash);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('exists', function(block, height) {
|
this.on('exists', function(block, height) {
|
||||||
bcoin.debug('Already have block %s (%d).', block.rhash, height);
|
self.logger.debug('Already have block %s (%d).', block.rhash, height);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('orphan', function(block, height) {
|
this.on('orphan', function(block, height) {
|
||||||
bcoin.debug('Handled orphan %s (%d).', block.rhash, height);
|
self.logger.debug('Handled orphan %s (%d).', block.rhash, height);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('purge', function(count, size) {
|
this.on('purge', function(count, size) {
|
||||||
bcoin.debug('Warning: %d (%dmb) orphans cleared!', count, utils.mb(size));
|
self.logger.debug('Warning: %d (%dmb) orphans cleared!',
|
||||||
|
count, utils.mb(size));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.db.on('add entry', function(entry) {
|
this.db.on('add entry', function(entry) {
|
||||||
@ -191,7 +197,7 @@ Chain.prototype._init = function _init() {
|
|||||||
Chain.prototype._open = function open(callback) {
|
Chain.prototype._open = function open(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
bcoin.debug('Chain is loading.');
|
this.logger.info('Chain is loading.');
|
||||||
|
|
||||||
this.db.open(function(err) {
|
this.db.open(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
@ -215,19 +221,25 @@ Chain.prototype._open = function open(callback) {
|
|||||||
self.network.updateHeight(tip.height);
|
self.network.updateHeight(tip.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.profiler.snapshot();
|
if (self.profiler)
|
||||||
|
self.profiler.snapshot();
|
||||||
|
|
||||||
|
self.logger.memory();
|
||||||
|
|
||||||
self.getInitialState(function(err) {
|
self.getInitialState(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (self.csvActive)
|
if (self.csvActive)
|
||||||
bcoin.debug('CSV is active.');
|
self.logger.info('CSV is active.');
|
||||||
|
|
||||||
if (self.segwitActive)
|
if (self.segwitActive)
|
||||||
bcoin.debug('Segwit is active.');
|
self.logger.info('Segwit is active.');
|
||||||
|
|
||||||
bcoin.profiler.snapshot();
|
if (self.profiler)
|
||||||
|
self.profiler.snapshot();
|
||||||
|
|
||||||
|
self.logger.memory();
|
||||||
|
|
||||||
self.emit('tip', tip);
|
self.emit('tip', tip);
|
||||||
|
|
||||||
@ -290,7 +302,7 @@ Chain.prototype.preload = function preload(callback) {
|
|||||||
if (this.network.type !== 'main')
|
if (this.network.type !== 'main')
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
bcoin.debug('Loading %s.', url);
|
this.logger.info('Loading %s.', url);
|
||||||
|
|
||||||
function save(entry, header) {
|
function save(entry, header) {
|
||||||
var unlock = locker.lock(save, [entry]);
|
var unlock = locker.lock(save, [entry]);
|
||||||
@ -305,7 +317,7 @@ Chain.prototype.preload = function preload(callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((++flushed % 50000) === 0)
|
if ((++flushed % 50000) === 0)
|
||||||
utils.print('Flushed %d headers to DB.', flushed);
|
self.logger.info('Flushed %d headers to DB.', flushed);
|
||||||
|
|
||||||
if (locker.jobs.length === 0 && ended)
|
if (locker.jobs.length === 0 && ended)
|
||||||
return callback();
|
return callback();
|
||||||
@ -330,7 +342,7 @@ Chain.prototype.preload = function preload(callback) {
|
|||||||
return callback(new Error('Bad response code: ' + res.statusCode));
|
return callback(new Error('Bad response code: ' + res.statusCode));
|
||||||
}
|
}
|
||||||
if (chainHeight > height - 30000) {
|
if (chainHeight > height - 30000) {
|
||||||
bcoin.debug('Preload height is %d. Skipping.', height);
|
self.logger.info('Preload height is %d. Skipping.', height);
|
||||||
stream.destroy();
|
stream.destroy();
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
@ -398,7 +410,7 @@ Chain.prototype.preload = function preload(callback) {
|
|||||||
save(entry, block);
|
save(entry, block);
|
||||||
|
|
||||||
if ((height + 1) % 50000 === 0)
|
if ((height + 1) % 50000 === 0)
|
||||||
bcoin.debug('Received %d headers from electrum.org.', height + 1);
|
self.logger.info('Received %d headers.', height + 1);
|
||||||
|
|
||||||
lastEntry = entry;
|
lastEntry = entry;
|
||||||
height++;
|
height++;
|
||||||
@ -1213,7 +1225,7 @@ Chain.prototype.setBestChain = function setBestChain(entry, block, prev, callbac
|
|||||||
|
|
||||||
// A higher fork has arrived.
|
// A higher fork has arrived.
|
||||||
// Time to reorganize the chain.
|
// Time to reorganize the chain.
|
||||||
bcoin.debug('WARNING: Reorganizing chain.');
|
self.logger.warning('WARNING: Reorganizing chain.');
|
||||||
return this.reorganize(entry, block, done);
|
return this.reorganize(entry, block, done);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1459,7 +1471,7 @@ Chain.prototype.add = function add(block, callback, force) {
|
|||||||
try {
|
try {
|
||||||
block = block.toBlock();
|
block = block.toBlock();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
bcoin.error(e);
|
self.logger.error(e);
|
||||||
return done(new VerifyError(block,
|
return done(new VerifyError(block,
|
||||||
'malformed',
|
'malformed',
|
||||||
'error parsing message',
|
'error parsing message',
|
||||||
@ -1533,7 +1545,9 @@ Chain.prototype.add = function add(block, callback, force) {
|
|||||||
// Take heap snapshot for debugging.
|
// Take heap snapshot for debugging.
|
||||||
if (self.total === 1 || self.total % 20 === 0) {
|
if (self.total === 1 || self.total % 20 === 0) {
|
||||||
utils.gc();
|
utils.gc();
|
||||||
bcoin.profiler.snapshot();
|
if (self.profiler)
|
||||||
|
self.profiler.snapshot();
|
||||||
|
self.logger.memory();
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.nextTick(function() {
|
utils.nextTick(function() {
|
||||||
|
|||||||
@ -178,11 +178,10 @@ function ChainDB(chain, options) {
|
|||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.chain = chain;
|
this.chain = chain;
|
||||||
|
this.logger = chain.logger;
|
||||||
this.network = this.chain.network;
|
this.network = this.chain.network;
|
||||||
|
|
||||||
this.db = bcoin.ldb({
|
this.db = bcoin.ldb({
|
||||||
network: this.network,
|
|
||||||
name: this.options.name || (this.options.spv ? 'spvchain' : 'chain'),
|
|
||||||
location: this.options.location,
|
location: this.options.location,
|
||||||
db: this.options.db,
|
db: this.options.db,
|
||||||
compression: true,
|
compression: true,
|
||||||
@ -231,13 +230,13 @@ ChainDB.prototype._open = function open(callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
var genesis, block;
|
var genesis, block;
|
||||||
|
|
||||||
bcoin.debug('Starting chain load.');
|
this.logger.info('Starting chain load.');
|
||||||
|
|
||||||
function done(err) {
|
function done(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
bcoin.debug('Chain successfully loaded.');
|
self.logger.info('Chain successfully loaded.');
|
||||||
|
|
||||||
self.db.checkVersion('V', 0, callback);
|
self.db.checkVersion('V', 0, callback);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ var bcoin = require('./env');
|
|||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var constants = bcoin.protocol.constants;
|
var constants = bcoin.protocol.constants;
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
|
var Output = bcoin.output;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an unspent output.
|
* Represents an unspent output.
|
||||||
@ -44,7 +45,7 @@ function Coin(options) {
|
|||||||
this.fromOptions(options);
|
this.fromOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.inherits(Coin, bcoin.output);
|
utils.inherits(Coin, Output);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject options into coin.
|
* Inject options into coin.
|
||||||
|
|||||||
@ -240,6 +240,8 @@ ec.random = function random(size) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random number within a range.
|
* Generate a random number within a range.
|
||||||
|
* Probably more cryptographically sound than
|
||||||
|
* `Math.random()`.
|
||||||
* @param {Number} min - Inclusive.
|
* @param {Number} min - Inclusive.
|
||||||
* @param {Number} max - Exclusive.
|
* @param {Number} max - Exclusive.
|
||||||
* @returns {Number}
|
* @returns {Number}
|
||||||
|
|||||||
328
lib/bcoin/env.js
328
lib/bcoin/env.js
@ -9,10 +9,6 @@
|
|||||||
|
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var global = utils.global;
|
var global = utils.global;
|
||||||
var fs;
|
|
||||||
|
|
||||||
if (!utils.isBrowser)
|
|
||||||
fs = require('f' + 's');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A BCoin "environment" which is used for
|
* A BCoin "environment" which is used for
|
||||||
@ -32,7 +28,7 @@ if (!utils.isBrowser)
|
|||||||
* @param {String} [options.prefix=~/.bcoin] - Prefix for filesystem.
|
* @param {String} [options.prefix=~/.bcoin] - Prefix for filesystem.
|
||||||
* @param {String} [options.db=leveldb] - Database backend.
|
* @param {String} [options.db=leveldb] - Database backend.
|
||||||
* @param {Boolean} [options.debug=false] - Whether to display debug output.
|
* @param {Boolean} [options.debug=false] - Whether to display debug output.
|
||||||
* @param {String|Boolean} [options.debugFile=~/.bcoin/debug.log] - A file to
|
* @param {String|Boolean} [options.debugFile=~/.debug.log] - A file to
|
||||||
* pipe debug output to.
|
* pipe debug output to.
|
||||||
* @param {Boolean} [options.profile=false] - Enable profiler.
|
* @param {Boolean} [options.profile=false] - Enable profiler.
|
||||||
* @param {Boolean} [options.useWorkers=false] - Enable workers.
|
* @param {Boolean} [options.useWorkers=false] - Enable workers.
|
||||||
@ -55,9 +51,10 @@ if (!utils.isBrowser)
|
|||||||
* @property {Object} ec - {@link module:ec}.
|
* @property {Object} ec - {@link module:ec}.
|
||||||
* @property {Function} lru - {@link LRU} constructor.
|
* @property {Function} lru - {@link LRU} constructor.
|
||||||
* @property {Function} bloom - {@link Bloom} constructor.
|
* @property {Function} bloom - {@link Bloom} constructor.
|
||||||
* @property {Function} bst - {@link BST} constructor.
|
* @property {Function} rbt - {@link RBT} constructor.
|
||||||
* @property {Function} lowlevelup - See {@link LowlevelUp}.
|
* @property {Function} lowlevelup - See {@link LowlevelUp}.
|
||||||
* @property {Function} uri - See {@link module:uri}.
|
* @property {Function} uri - See {@link module:uri}.
|
||||||
|
* @property {Function} logger - {@link Logger} constructor.
|
||||||
*
|
*
|
||||||
* @property {Object} protocol
|
* @property {Object} protocol
|
||||||
* @property {Function} protocol.constants - See {@link module:constants}.
|
* @property {Function} protocol.constants - See {@link module:constants}.
|
||||||
@ -115,19 +112,7 @@ if (!utils.isBrowser)
|
|||||||
* @property {Workers?} workerPool - Default global worker pool.
|
* @property {Workers?} workerPool - Default global worker pool.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Environment(options) {
|
function Environment() {
|
||||||
if (!options)
|
|
||||||
options = {};
|
|
||||||
|
|
||||||
if (typeof options === 'string')
|
|
||||||
options = { network: options };
|
|
||||||
|
|
||||||
this.options = options;
|
|
||||||
|
|
||||||
this._debug = null;
|
|
||||||
|
|
||||||
this.isBrowser = utils.isBrowser;
|
|
||||||
|
|
||||||
this.env = Environment;
|
this.env = Environment;
|
||||||
this.bn = require('bn.js');
|
this.bn = require('bn.js');
|
||||||
this.utils = require('./utils');
|
this.utils = require('./utils');
|
||||||
@ -140,14 +125,15 @@ function Environment(options) {
|
|||||||
this.rbt = require('./rbt');
|
this.rbt = require('./rbt');
|
||||||
this.lowlevelup = require('./lowlevelup');
|
this.lowlevelup = require('./lowlevelup');
|
||||||
this.uri = require('./uri');
|
this.uri = require('./uri');
|
||||||
|
this.logger = require('./logger');
|
||||||
|
|
||||||
this.protocol = require('./protocol');
|
this.protocol = require('./protocol');
|
||||||
this.packets = this.protocol.packets;
|
this.packets = this.protocol.packets;
|
||||||
this.network = require('./network');
|
this.network = require('./network');
|
||||||
this.errors = require('./errors');
|
this.errors = require('./errors');
|
||||||
this.ldb = require('./ldb');
|
this.ldb = require('./ldb');
|
||||||
this.profiler = require('./profiler');
|
|
||||||
this.timedata = require('./timedata');
|
this.timedata = require('./timedata');
|
||||||
|
this.sigcache = require('./sigcache')(0);
|
||||||
this.script = require('./script');
|
this.script = require('./script');
|
||||||
this.opcode = this.script.Opcode;
|
this.opcode = this.script.Opcode;
|
||||||
this.stack = this.script.Stack;
|
this.stack = this.script.Stack;
|
||||||
@ -167,6 +153,7 @@ function Environment(options) {
|
|||||||
this.block = require('./block');
|
this.block = require('./block');
|
||||||
this.merkleblock = require('./merkleblock');
|
this.merkleblock = require('./merkleblock');
|
||||||
this.headers = require('./headers');
|
this.headers = require('./headers');
|
||||||
|
this.fees = require('./fees');
|
||||||
this.node = require('./node');
|
this.node = require('./node');
|
||||||
this.spvnode = require('./spvnode');
|
this.spvnode = require('./spvnode');
|
||||||
this.fullnode = require('./fullnode');
|
this.fullnode = require('./fullnode');
|
||||||
@ -188,28 +175,27 @@ function Environment(options) {
|
|||||||
this.miner = require('./miner');
|
this.miner = require('./miner');
|
||||||
this.minerblock = this.miner.MinerBlock;
|
this.minerblock = this.miner.MinerBlock;
|
||||||
this.http = require('./http');
|
this.http = require('./http');
|
||||||
|
this.workers = require('./workers');
|
||||||
this.workers = null;
|
|
||||||
this.workerPool = null;
|
|
||||||
|
|
||||||
this.prefix = null;
|
|
||||||
this.networkType = null;
|
|
||||||
this.db = null;
|
|
||||||
this.debugLogs = null;
|
|
||||||
this.debugFile = null;
|
|
||||||
this.profile = null;
|
|
||||||
this.useWorkers = null;
|
|
||||||
this.maxWorkers = null;
|
|
||||||
this.workerTimeout = null;
|
|
||||||
this.workerUri = null;
|
|
||||||
this.proxyServer = null;
|
|
||||||
this.logger = null;
|
|
||||||
|
|
||||||
this.time = new this.timedata();
|
this.time = new this.timedata();
|
||||||
|
this.defaultLogger = new this.logger('none');
|
||||||
|
this.useWorkers = false;
|
||||||
|
this.workerPool = new this.workers();
|
||||||
|
|
||||||
this.set(options);
|
this.set({
|
||||||
|
network: process.env.BCOIN_NETWORK || 'main',
|
||||||
|
useWorkers: +process.env.BCOIN_USE_WORKERS === 1,
|
||||||
|
maxWorkers: +process.env.BCOIN_MAX_WORKERS,
|
||||||
|
workerTimeout: +process.env.BCOIN_WORKER_TIMEOUT,
|
||||||
|
sigcacheSize: +process.env.BCOIN_SIGCACHE_SIZE
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default network.
|
||||||
|
* @param {String} options
|
||||||
|
*/
|
||||||
|
|
||||||
Environment.prototype.set = function set(options) {
|
Environment.prototype.set = function set(options) {
|
||||||
if (typeof options === 'string')
|
if (typeof options === 'string')
|
||||||
options = { network: options };
|
options = { network: options };
|
||||||
@ -217,209 +203,29 @@ Environment.prototype.set = function set(options) {
|
|||||||
if (!options)
|
if (!options)
|
||||||
options = {};
|
options = {};
|
||||||
|
|
||||||
options = utils.merge({}, options);
|
if (options.network)
|
||||||
|
this.network.set(options.network);
|
||||||
|
|
||||||
options.network = options.network
|
if (typeof options.useWorkers === 'boolean')
|
||||||
|| process.env.BCOIN_NETWORK
|
this.useWorkers = options.useWorkers;
|
||||||
|| 'main';
|
|
||||||
|
|
||||||
options.prefix = options.prefix
|
if (utils.isNumber(options.maxWorkers))
|
||||||
|| process.env.BCOIN_PREFIX;
|
this.workerPool.size = options.maxWorkers;
|
||||||
|
|
||||||
if (!options.prefix)
|
if (utils.isNumber(options.workerTimeout))
|
||||||
options.prefix = utils.HOME + '/.bcoin';
|
this.workerPool.timeout = options.workerTimeout;
|
||||||
|
|
||||||
if (!options.db)
|
if (utils.isBrowser && this.useWorkers) {
|
||||||
options.db = process.env.BCOIN_DB;
|
|
||||||
|
|
||||||
if (options.debug == null && process.env.BCOIN_DEBUG != null)
|
|
||||||
options.debug = +process.env.BCOIN_DEBUG === 1;
|
|
||||||
|
|
||||||
if (options.debugFile == null && process.env.BCOIN_DEBUGFILE != null) {
|
|
||||||
if (process.env.BCOIN_DEBUGFILE === '0'
|
|
||||||
|| process.env.BCOIN_DEBUGFILE === '1') {
|
|
||||||
options.debugFile = +process.env.BCOIN_DEBUGFILE !== 0;
|
|
||||||
} else {
|
|
||||||
options.debugFile = process.env.BCOIN_DEBUGFILE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.profile == null && process.env.BCOIN_PROFILE != null)
|
|
||||||
options.profile = +process.env.BCOIN_PROFILE === 1;
|
|
||||||
|
|
||||||
if (options.useWorkers == null && process.env.BCOIN_USE_WORKERS != null)
|
|
||||||
options.useWorkers = +process.env.BCOIN_USE_WORKERS === 1;
|
|
||||||
|
|
||||||
if (options.maxWorkers == null && process.env.BCOIN_MAX_WORKERS != null)
|
|
||||||
options.maxWorkers = +process.env.BCOIN_MAX_WORKERS;
|
|
||||||
|
|
||||||
if (options.workerTime == null && process.env.BCOIN_WORKER_TIMEOUT != null)
|
|
||||||
options.workerTimeout = +process.env.BCOIN_WORKER_TIMEOUT;
|
|
||||||
|
|
||||||
if (options.debugFile && typeof options.debugFile !== 'string') {
|
|
||||||
options.debugFile = options.prefix;
|
|
||||||
if (options.network !== 'main')
|
|
||||||
options.debugFile += '/' + options.network;
|
|
||||||
options.debugFile += '/debug.log';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.prefix = normalize(options.prefix);
|
|
||||||
this.networkType = options.network;
|
|
||||||
this.db = options.db;
|
|
||||||
this.debugLogs = !!options.debug;
|
|
||||||
this.debugFile = options.debugFile
|
|
||||||
? normalize(options.debugFile)
|
|
||||||
: null;
|
|
||||||
this.profile = options.profile;
|
|
||||||
this.useWorkers = !!options.useWorkers;
|
|
||||||
this.maxWorkers = options.maxWorkers;
|
|
||||||
this.workerTimeout = options.workerTimeout;
|
|
||||||
this.workerUri = options.workerUri || '/bcoin-worker.js';
|
|
||||||
this.proxyServer = options.proxyServer;
|
|
||||||
this.logger = options.logger;
|
|
||||||
|
|
||||||
this.network.set(this.networkType);
|
|
||||||
|
|
||||||
if (this.isBrowser && this.useWorkers) {
|
|
||||||
this.useWorkers = typeof global.Worker === 'function'
|
this.useWorkers = typeof global.Worker === 'function'
|
||||||
|| typeof global.postMessage === 'function';
|
|| typeof global.postMessage === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.useWorkers) {
|
if (utils.isNumber(options.sigcacheSize))
|
||||||
this.workers = require('./workers');
|
this.sigcache.size = options.sigcacheSize;
|
||||||
this.workerPool = new this.workers({
|
|
||||||
size: this.maxWorkers,
|
|
||||||
timeout: this.workerTimeout,
|
|
||||||
network: this.network.get(this.network.primary)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure a directory.
|
|
||||||
* @param {String} path
|
|
||||||
* @param {Boolean?} dirname
|
|
||||||
*/
|
|
||||||
|
|
||||||
Environment.prototype.mkdir = function mkdir(path, dirname) {
|
|
||||||
if (this.isBrowser)
|
|
||||||
return;
|
|
||||||
|
|
||||||
path = normalize(path, dirname);
|
|
||||||
|
|
||||||
if (!mkdir.paths)
|
|
||||||
mkdir.paths = {};
|
|
||||||
|
|
||||||
if (mkdir.paths[path])
|
|
||||||
return;
|
|
||||||
|
|
||||||
mkdir.paths[path] = true;
|
|
||||||
|
|
||||||
return mkdirp(path);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output a debug message.
|
|
||||||
* @param {Object|String} obj
|
|
||||||
* @param {...String} args
|
|
||||||
* @example
|
|
||||||
* bcoin.debug('foo: %d', 10);
|
|
||||||
*/
|
|
||||||
|
|
||||||
Environment.prototype.debug = function debug() {
|
|
||||||
var args = new Array(arguments.length);
|
|
||||||
var i, msg;
|
|
||||||
|
|
||||||
for (i = 0; i < args.length; i++)
|
|
||||||
args[i] = arguments[i];
|
|
||||||
|
|
||||||
if (this.logger) {
|
|
||||||
if (this.debugLogs)
|
|
||||||
this.logger.debug(args);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isBrowser) {
|
|
||||||
if (this.debugLogs) {
|
|
||||||
msg = typeof args[0] !== 'object'
|
|
||||||
? utils.format(args, false)
|
|
||||||
: args[0];
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.debugLogs) {
|
|
||||||
msg = utils.format(args, true);
|
|
||||||
process.stderr.write(msg + '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.debugFile) {
|
|
||||||
msg = utils.format(args, false);
|
|
||||||
this.write(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output an error.
|
|
||||||
* @param {Error} err
|
|
||||||
*/
|
|
||||||
|
|
||||||
Environment.prototype.error = function error(err) {
|
|
||||||
var msg;
|
|
||||||
|
|
||||||
if (!err)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (typeof err === 'string')
|
|
||||||
err = new Error(err);
|
|
||||||
|
|
||||||
if (this.logger) {
|
|
||||||
if (this.debugLogs)
|
|
||||||
this.logger.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isBrowser) {
|
|
||||||
if (this.debugLogs)
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.debugLogs) {
|
|
||||||
msg = (err.message + '').replace(/^ *Error: */, '');
|
|
||||||
|
|
||||||
if (process.stdout && process.stdout.isTTY)
|
|
||||||
msg = '\x1b[1;31m[Error]\x1b[m ' + msg;
|
|
||||||
else
|
|
||||||
msg = '[Error] ' + msg;
|
|
||||||
|
|
||||||
process.stderr.write(msg + '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.debugFile)
|
|
||||||
this.write(err.stack + '');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a message to the debug log.
|
|
||||||
* @param {String} msg
|
|
||||||
*/
|
|
||||||
|
|
||||||
Environment.prototype.write = function write(msg) {
|
|
||||||
if (this.isBrowser)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this._debug) {
|
|
||||||
this.mkdir(this.debugFile, true);
|
|
||||||
this._debug = fs.createWriteStream(this.debugFile, { flags: 'a' });
|
|
||||||
}
|
|
||||||
|
|
||||||
this._debug.write(process.pid + ' (' + utils.date() + '): ' + msg + '\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the adjusted time.
|
* Get the adjusted time.
|
||||||
* @returns {Number} Adjusted time.
|
* @returns {Number} Adjusted time.
|
||||||
@ -429,74 +235,12 @@ Environment.prototype.now = function now() {
|
|||||||
return this.time.now();
|
return this.time.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize a path.
|
|
||||||
* @param {String} path
|
|
||||||
* @param {Boolean?} dirname
|
|
||||||
*/
|
|
||||||
|
|
||||||
function normalize(path, dirname) {
|
|
||||||
var parts;
|
|
||||||
|
|
||||||
path = path.replace(/\\/g, '/');
|
|
||||||
path = path.replace(/\/+$/, '');
|
|
||||||
parts = path.split(/\/+/);
|
|
||||||
|
|
||||||
if (dirname)
|
|
||||||
parts.pop();
|
|
||||||
|
|
||||||
return parts.join('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a full directory structure.
|
|
||||||
* @param {String} path
|
|
||||||
*/
|
|
||||||
|
|
||||||
function mkdirp(path) {
|
|
||||||
var i, parts, stat;
|
|
||||||
|
|
||||||
if (!fs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
path = path.replace(/\\/g, '/');
|
|
||||||
path = path.replace(/\/+$/, '');
|
|
||||||
parts = path.split(/\/+/);
|
|
||||||
path = '';
|
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
if (parts[0].indexOf(':') !== -1)
|
|
||||||
path = parts.shift() + '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parts[0].length === 0) {
|
|
||||||
parts.shift();
|
|
||||||
path = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < parts.length; i++) {
|
|
||||||
path += parts[i];
|
|
||||||
|
|
||||||
try {
|
|
||||||
stat = fs.statSync(path);
|
|
||||||
if (!stat.isDirectory())
|
|
||||||
throw new Error('Could not create directory.');
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code === 'ENOENT')
|
|
||||||
fs.mkdirSync(path, 488 /* 0750 */);
|
|
||||||
else
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
path += '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose by converting `exports` to an
|
* Expose by converting `exports` to an
|
||||||
* Environment.
|
* Environment.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
utils.merge(exports, Environment.prototype);
|
exports.set = Environment.prototype.set;
|
||||||
|
exports.now = Environment.prototype.now;
|
||||||
|
|
||||||
Environment.call(exports);
|
Environment.call(exports);
|
||||||
|
|||||||
@ -48,12 +48,13 @@ var FREE_THRESHOLD = constants.tx.FREE_THRESHOLD;
|
|||||||
* @param {String} type
|
* @param {String} type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function ConfirmStats(buckets, maxConfirms, decay, type) {
|
function ConfirmStats(buckets, maxConfirms, decay, type, logger) {
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
if (!(this instanceof ConfirmStats))
|
if (!(this instanceof ConfirmStats))
|
||||||
return new ConfirmStats(buckets, maxConfirms, decay, type);
|
return new ConfirmStats(buckets, maxConfirms, decay, type, logger);
|
||||||
|
|
||||||
|
this.logger = logger || bcoin.defaultLogger;
|
||||||
this.maxConfirms = maxConfirms;
|
this.maxConfirms = maxConfirms;
|
||||||
this.decay = decay;
|
this.decay = decay;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -214,7 +215,7 @@ ConfirmStats.prototype.estimateMedian = function estimateMedian(target, needed,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcoin.debug('estimatefee: '
|
// this.logger.debug('estimatefee: '
|
||||||
// + ' For confirmation success in %d blocks'
|
// + ' For confirmation success in %d blocks'
|
||||||
// + ' %s %d need %s %s: %d from buckets %d - %d.'
|
// + ' %s %d need %s %s: %d from buckets %d - %d.'
|
||||||
// + ' Current bucket stats %d% %d/%d (%d mempool).',
|
// + ' Current bucket stats %d% %d/%d (%d mempool).',
|
||||||
@ -245,7 +246,7 @@ ConfirmStats.prototype.addTX = function addTX(height, val) {
|
|||||||
var bucketIndex = this.bucketMap.search(val);
|
var bucketIndex = this.bucketMap.search(val);
|
||||||
var blockIndex = height % this.unconfTX.length;
|
var blockIndex = height % this.unconfTX.length;
|
||||||
this.unconfTX[blockIndex][bucketIndex]++;
|
this.unconfTX[blockIndex][bucketIndex]++;
|
||||||
bcoin.debug('estimatefee: Adding tx to %s.', this.type);
|
this.logger.debug('estimatefee: Adding tx to %s.', this.type);
|
||||||
return bucketIndex;
|
return bucketIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -264,7 +265,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
|||||||
blocksAgo = 0;
|
blocksAgo = 0;
|
||||||
|
|
||||||
if (blocksAgo < 0) {
|
if (blocksAgo < 0) {
|
||||||
bcoin.debug('estimatefee: Blocks ago is negative for mempool tx.');
|
this.logger.debug('estimatefee: Blocks ago is negative for mempool tx.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +273,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
|||||||
if (this.oldUnconfTX[bucketIndex] > 0) {
|
if (this.oldUnconfTX[bucketIndex] > 0) {
|
||||||
this.oldUnconfTX[bucketIndex]--;
|
this.oldUnconfTX[bucketIndex]--;
|
||||||
} else {
|
} else {
|
||||||
bcoin.debug('estimatefee:'
|
this.logger.debug('estimatefee:'
|
||||||
+ ' Mempool tx removed >25 blocks (bucket=%d).',
|
+ ' Mempool tx removed >25 blocks (bucket=%d).',
|
||||||
bucketIndex);
|
bucketIndex);
|
||||||
}
|
}
|
||||||
@ -281,7 +282,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
|||||||
if (this.unconfTX[blockIndex][bucketIndex] > 0) {
|
if (this.unconfTX[blockIndex][bucketIndex] > 0) {
|
||||||
this.unconfTX[blockIndex][bucketIndex]--;
|
this.unconfTX[blockIndex][bucketIndex]--;
|
||||||
} else {
|
} else {
|
||||||
bcoin.debug('estimatefee:'
|
this.logger.debug('estimatefee:'
|
||||||
+ ' Mempool tx removed (block=%d, bucket=%d).',
|
+ ' Mempool tx removed (block=%d, bucket=%d).',
|
||||||
blockIndex, bucketIndex);
|
blockIndex, bucketIndex);
|
||||||
}
|
}
|
||||||
@ -293,8 +294,8 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
|||||||
* @returns {Buffer}
|
* @returns {Buffer}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ConfirmStats.prototype.toRaw = function toRaw() {
|
ConfirmStats.prototype.toRaw = function toRaw(writer) {
|
||||||
var p = new BufferWriter();
|
var p = new BufferWriter(writer);
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
function writeArray(buckets) {
|
function writeArray(buckets) {
|
||||||
@ -315,7 +316,10 @@ ConfirmStats.prototype.toRaw = function toRaw() {
|
|||||||
for (i = 0; i < this.maxConfirms; i++)
|
for (i = 0; i < this.maxConfirms; i++)
|
||||||
writeArray(this.confAvg[i]);
|
writeArray(this.confAvg[i]);
|
||||||
|
|
||||||
return p.render();
|
if (!writer)
|
||||||
|
p = p.render();
|
||||||
|
|
||||||
|
return p;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -325,7 +329,7 @@ ConfirmStats.prototype.toRaw = function toRaw() {
|
|||||||
* @returns {ConfirmStats}
|
* @returns {ConfirmStats}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ConfirmStats.fromRaw = function fromRaw(data, type) {
|
ConfirmStats.fromRaw = function fromRaw(data, type, logger) {
|
||||||
var p = new BufferReader(data);
|
var p = new BufferReader(data);
|
||||||
var i, decay, buckets, avg, txAvg, maxConfirms, confAvg, stats;
|
var i, decay, buckets, avg, txAvg, maxConfirms, confAvg, stats;
|
||||||
|
|
||||||
@ -369,7 +373,7 @@ ConfirmStats.fromRaw = function fromRaw(data, type) {
|
|||||||
throw new Error('Mismatch in fee/pri conf average bucket count.');
|
throw new Error('Mismatch in fee/pri conf average bucket count.');
|
||||||
}
|
}
|
||||||
|
|
||||||
stats = new ConfirmStats(buckets, maxConfirms, decay, type);
|
stats = new ConfirmStats(buckets, maxConfirms, decay, type, logger);
|
||||||
|
|
||||||
stats.avg = avg;
|
stats.avg = avg;
|
||||||
stats.txAvg = txAvg;
|
stats.txAvg = txAvg;
|
||||||
@ -386,19 +390,25 @@ ConfirmStats.fromRaw = function fromRaw(data, type) {
|
|||||||
* @param {Network|NetworkType} network
|
* @param {Network|NetworkType} network
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function PolicyEstimator(minRelay, network) {
|
function PolicyEstimator(minRelay, network, logger) {
|
||||||
var fee, priority, boundary;
|
var fee, priority, boundary;
|
||||||
|
|
||||||
if (!(this instanceof PolicyEstimator))
|
if (!(this instanceof PolicyEstimator))
|
||||||
return new PolicyEstimator(minRelay, network);
|
return new PolicyEstimator(minRelay, network, logger);
|
||||||
|
|
||||||
|
fee = [];
|
||||||
|
priority = [];
|
||||||
|
|
||||||
this.network = bcoin.network.get(network);
|
this.network = bcoin.network.get(network);
|
||||||
|
this.logger = logger || bcoin.defaultLogger;
|
||||||
|
|
||||||
this.minTrackedFee = minRelay < MIN_FEERATE
|
this.minTrackedFee = minRelay < MIN_FEERATE
|
||||||
? MIN_FEERATE
|
? MIN_FEERATE
|
||||||
: minRelay;
|
: minRelay;
|
||||||
|
|
||||||
fee = [];
|
this.minTrackedPri = FREE_THRESHOLD < MIN_PRIORITY
|
||||||
|
? MIN_PRIORITY
|
||||||
|
: FREE_THRESHOLD;
|
||||||
|
|
||||||
for (boundary = this.minTrackedFee;
|
for (boundary = this.minTrackedFee;
|
||||||
boundary <= MAX_FEERATE;
|
boundary <= MAX_FEERATE;
|
||||||
@ -408,16 +418,6 @@ function PolicyEstimator(minRelay, network) {
|
|||||||
|
|
||||||
fee.push(INF_FEERATE);
|
fee.push(INF_FEERATE);
|
||||||
|
|
||||||
this.feeStats = new ConfirmStats(
|
|
||||||
fee, MAX_BLOCK_CONFIRMS,
|
|
||||||
DEFAULT_DECAY, 'FeeRate');
|
|
||||||
|
|
||||||
this.minTrackedPri = FREE_THRESHOLD < MIN_PRIORITY
|
|
||||||
? MIN_PRIORITY
|
|
||||||
: FREE_THRESHOLD;
|
|
||||||
|
|
||||||
priority = [];
|
|
||||||
|
|
||||||
for (boundary = this.minTrackedPri;
|
for (boundary = this.minTrackedPri;
|
||||||
boundary <= MAX_PRIORITY;
|
boundary <= MAX_PRIORITY;
|
||||||
boundary *= PRI_SPACING) {
|
boundary *= PRI_SPACING) {
|
||||||
@ -426,15 +426,22 @@ function PolicyEstimator(minRelay, network) {
|
|||||||
|
|
||||||
priority.push(INF_PRIORITY);
|
priority.push(INF_PRIORITY);
|
||||||
|
|
||||||
|
this.feeStats = new ConfirmStats(
|
||||||
|
fee, MAX_BLOCK_CONFIRMS,
|
||||||
|
DEFAULT_DECAY, 'FeeRate',
|
||||||
|
this.logger);
|
||||||
|
|
||||||
this.priStats = new ConfirmStats(
|
this.priStats = new ConfirmStats(
|
||||||
priority, MAX_BLOCK_CONFIRMS,
|
priority, MAX_BLOCK_CONFIRMS,
|
||||||
DEFAULT_DECAY, 'Priority');
|
DEFAULT_DECAY, 'Priority',
|
||||||
|
this.logger);
|
||||||
|
|
||||||
this.feeUnlikely = 0;
|
this.feeUnlikely = 0;
|
||||||
this.feeLikely = INF_FEERATE;
|
this.feeLikely = INF_FEERATE;
|
||||||
this.priUnlikely = 0;
|
this.priUnlikely = 0;
|
||||||
this.priLikely = INF_PRIORITY;
|
this.priLikely = INF_PRIORITY;
|
||||||
this.map = {};
|
this.map = {};
|
||||||
|
this.mapSize = 0;
|
||||||
this.bestHeight = 0;
|
this.bestHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +454,7 @@ PolicyEstimator.prototype.removeTX = function removeTX(hash) {
|
|||||||
var item = this.map[hash];
|
var item = this.map[hash];
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'estimatefee: Mempool tx %s not found.',
|
'estimatefee: Mempool tx %s not found.',
|
||||||
utils.revHex(hash));
|
utils.revHex(hash));
|
||||||
return;
|
return;
|
||||||
@ -456,6 +463,7 @@ PolicyEstimator.prototype.removeTX = function removeTX(hash) {
|
|||||||
this.feeStats.removeTX(item.blockHeight, this.bestHeight, item.bucketIndex);
|
this.feeStats.removeTX(item.blockHeight, this.bestHeight, item.bucketIndex);
|
||||||
|
|
||||||
delete this.map[hash];
|
delete this.map[hash];
|
||||||
|
this.mapSize--;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -500,7 +508,7 @@ PolicyEstimator.prototype.processTX = function processTX(entry, current) {
|
|||||||
var fee, rate, priority;
|
var fee, rate, priority;
|
||||||
|
|
||||||
if (this.map[hash]) {
|
if (this.map[hash]) {
|
||||||
bcoin.debug('estimatefee: Mempool tx %s already tracked.', entry.tx.rhash);
|
this.logger.debug('estimatefee: Mempool tx %s already tracked.', entry.tx.rhash);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,23 +528,24 @@ PolicyEstimator.prototype.processTX = function processTX(entry, current) {
|
|||||||
rate = entry.getRate();
|
rate = entry.getRate();
|
||||||
priority = entry.getPriority(height);
|
priority = entry.getPriority(height);
|
||||||
|
|
||||||
bcoin.debug('estimatefee: Processing mempool tx %s.', entry.tx.rhash);
|
this.logger.debug('estimatefee: Processing mempool tx %s.', entry.tx.rhash);
|
||||||
|
|
||||||
if (fee === 0 || this.isPriPoint(rate, priority)) {
|
if (fee === 0 || this.isPriPoint(rate, priority)) {
|
||||||
this.map[hash] = {
|
this.map[hash] = {
|
||||||
blockHeight: height,
|
blockHeight: height,
|
||||||
bucketIndex: this.priStats.addTX(height, priority)
|
bucketIndex: this.priStats.addTX(height, priority)
|
||||||
};
|
};
|
||||||
|
this.mapSize++;
|
||||||
} else if (this.isFeePoint(rate, priority)) {
|
} else if (this.isFeePoint(rate, priority)) {
|
||||||
this.map[hash] = {
|
this.map[hash] = {
|
||||||
blockHeight: height,
|
blockHeight: height,
|
||||||
bucketIndex: this.feeStats.addTX(height, rate)
|
bucketIndex: this.feeStats.addTX(height, rate)
|
||||||
};
|
};
|
||||||
bcoin.debug('estimatefee: Rate: %d.', this.estimateFee());
|
this.mapSize++;
|
||||||
|
this.logger.debug('estimatefee: Rate: %d.', this.estimateFee());
|
||||||
} else {
|
} else {
|
||||||
bcoin.debug('estimatefee: Not adding tx %s.', entry.tx.rhash);
|
this.logger.debug('estimatefee: Not adding tx %s.', entry.tx.rhash);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -554,7 +563,7 @@ PolicyEstimator.prototype.processBlockTX = function processBlockTX(height, entry
|
|||||||
|
|
||||||
blocks = height - entry.height;
|
blocks = height - entry.height;
|
||||||
if (blocks <= 0) {
|
if (blocks <= 0) {
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'estimatefee: Block tx %s had negative blocks to confirm (%d, %d).',
|
'estimatefee: Block tx %s had negative blocks to confirm (%d, %d).',
|
||||||
entry.tx.rhash,
|
entry.tx.rhash,
|
||||||
height,
|
height,
|
||||||
@ -595,7 +604,7 @@ PolicyEstimator.prototype.processBlock = function processBlock(height, entries,
|
|||||||
if (!current)
|
if (!current)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bcoin.debug('estimatefee: Recalculating dynamic cutoffs.');
|
this.logger.debug('estimatefee: Recalculating dynamic cutoffs.');
|
||||||
|
|
||||||
this.feeLikely = this.feeStats.estimateMedian(
|
this.feeLikely = this.feeStats.estimateMedian(
|
||||||
2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT,
|
2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT,
|
||||||
@ -634,9 +643,9 @@ PolicyEstimator.prototype.processBlock = function processBlock(height, entries,
|
|||||||
this.feeStats.updateAverages();
|
this.feeStats.updateAverages();
|
||||||
this.priStats.updateAverages();
|
this.priStats.updateAverages();
|
||||||
|
|
||||||
bcoin.debug('estimatefee: Done updating estimates'
|
this.logger.debug('estimatefee: Done updating estimates'
|
||||||
+ ' for %d confirmed entries. New mempool map size %d.',
|
+ ' for %d confirmed entries. New mempool map size %d.',
|
||||||
entries.length, Object.keys(this.map).length);
|
entries.length, this.mapSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -738,12 +747,18 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s
|
|||||||
* @returns {Buffer}
|
* @returns {Buffer}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PolicyEstimator.prototype.toRaw = function toRaw() {
|
PolicyEstimator.prototype.toRaw = function toRaw(writer) {
|
||||||
var p = new BufferWriter();
|
var p = new BufferWriter(writer);
|
||||||
|
|
||||||
|
p.writeU32(this.network.magic);
|
||||||
p.writeU32(this.bestHeight);
|
p.writeU32(this.bestHeight);
|
||||||
p.writeVarBytes(this.feeStats.toRaw());
|
p.writeVarBytes(this.feeStats.toRaw());
|
||||||
p.writeVarBytes(this.priStats.toRaw());
|
p.writeVarBytes(this.priStats.toRaw());
|
||||||
return p.render();
|
|
||||||
|
if (!writer)
|
||||||
|
p = p.render();
|
||||||
|
|
||||||
|
return p;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -754,12 +769,13 @@ PolicyEstimator.prototype.toRaw = function toRaw() {
|
|||||||
* @returns {PolicyEstimator}
|
* @returns {PolicyEstimator}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PolicyEstimator.fromRaw = function fromRaw(data, minRelay, network) {
|
PolicyEstimator.fromRaw = function fromRaw(minRelay, data, logger) {
|
||||||
var p = new BufferReader(data);
|
var p = new BufferReader(data);
|
||||||
|
var network = bcoin.network.fromMagic(p.readU32());
|
||||||
var bestHeight = p.readU32();
|
var bestHeight = p.readU32();
|
||||||
var feeStats = ConfirmStats.fromRaw(p.readVarBytes(), 'FeeRate');
|
var estimator = new PolicyEstimator(minRelay, network, logger);
|
||||||
var priStats = ConfirmStats.fromRaw(p.readVarBytes(), 'Priority');
|
var feeStats = ConfirmStats.fromRaw(p.readVarBytes(), 'FeeRate', logger);
|
||||||
var estimator = new PolicyEstimator(minRelay, network);
|
var priStats = ConfirmStats.fromRaw(p.readVarBytes(), 'Priority', logger);
|
||||||
|
|
||||||
estimator.bestHeight = bestHeight;
|
estimator.bestHeight = bestHeight;
|
||||||
estimator.feeStats = feeStats;
|
estimator.feeStats = feeStats;
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bcoin = require('./env');
|
var bcoin = require('./env');
|
||||||
|
var constants = bcoin.protocol.constants;
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var Node = bcoin.node;
|
var Node = bcoin.node;
|
||||||
@ -34,6 +35,7 @@ var Node = bcoin.node;
|
|||||||
* @param {Object?} options.wallet - Primary {@link Wallet} options.
|
* @param {Object?} options.wallet - Primary {@link Wallet} options.
|
||||||
* @property {Boolean} loaded
|
* @property {Boolean} loaded
|
||||||
* @property {Chain} chain
|
* @property {Chain} chain
|
||||||
|
* @property {PolicyEstimator} fees
|
||||||
* @property {Mempool} mempool
|
* @property {Mempool} mempool
|
||||||
* @property {Pool} pool
|
* @property {Pool} pool
|
||||||
* @property {Miner} miner
|
* @property {Miner} miner
|
||||||
@ -50,18 +52,33 @@ function Fullnode(options) {
|
|||||||
|
|
||||||
Node.call(this, options);
|
Node.call(this, options);
|
||||||
|
|
||||||
|
// Instantiate blockchain.
|
||||||
this.chain = new bcoin.chain({
|
this.chain = new bcoin.chain({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
|
profiler: this.profiler,
|
||||||
|
db: this.db,
|
||||||
|
location: this.location('chain'),
|
||||||
preload: false,
|
preload: false,
|
||||||
spv: false,
|
spv: false,
|
||||||
prune: this.options.prune,
|
prune: this.options.prune,
|
||||||
useCheckpoints: this.options.useCheckpoints
|
useCheckpoints: this.options.useCheckpoints
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fee estimation.
|
||||||
|
this.fees = new bcoin.fees(
|
||||||
|
constants.tx.MIN_RELAY,
|
||||||
|
this.network,
|
||||||
|
this.logger);
|
||||||
|
|
||||||
// Mempool needs access to the chain.
|
// Mempool needs access to the chain.
|
||||||
this.mempool = new bcoin.mempool({
|
this.mempool = new bcoin.mempool({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
chain: this.chain,
|
chain: this.chain,
|
||||||
|
fees: this.fees,
|
||||||
|
db: 'memory',
|
||||||
|
location: this.location('mempool'),
|
||||||
limitFree: this.options.limitFree,
|
limitFree: this.options.limitFree,
|
||||||
limitFreeRelay: this.options.limitFreeRelay,
|
limitFreeRelay: this.options.limitFreeRelay,
|
||||||
requireStandard: this.options.requireStandard,
|
requireStandard: this.options.requireStandard,
|
||||||
@ -72,26 +89,36 @@ function Fullnode(options) {
|
|||||||
// Pool needs access to the chain and mempool.
|
// Pool needs access to the chain and mempool.
|
||||||
this.pool = new bcoin.pool({
|
this.pool = new bcoin.pool({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
chain: this.chain,
|
chain: this.chain,
|
||||||
mempool: this.mempool,
|
mempool: this.mempool,
|
||||||
witness: this.network.witness,
|
witness: this.network.witness,
|
||||||
selfish: this.options.selfish,
|
selfish: this.options.selfish,
|
||||||
headers: this.options.headers,
|
headers: this.options.headers,
|
||||||
|
proxyServer: this.options.proxyServer,
|
||||||
|
preferredSeed: this.options.preferredSeed,
|
||||||
spv: false
|
spv: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Miner needs access to the chain and mempool.
|
// Miner needs access to the chain and mempool.
|
||||||
this.miner = new bcoin.miner({
|
this.miner = new bcoin.miner({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
chain: this.chain,
|
chain: this.chain,
|
||||||
mempool: this.mempool,
|
mempool: this.mempool,
|
||||||
|
fees: this.fees,
|
||||||
address: this.options.payoutAddress,
|
address: this.options.payoutAddress,
|
||||||
coinbaseFlags: this.options.coinbaseFlags,
|
coinbaseFlags: this.options.coinbaseFlags,
|
||||||
parallel: this.options.parallel
|
parallel: this.options.parallel
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Wallet database needs access to fees.
|
||||||
this.walletdb = new bcoin.walletdb({
|
this.walletdb = new bcoin.walletdb({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
|
fees: this.fees,
|
||||||
|
db: this.db,
|
||||||
|
location: this.location('walletdb'),
|
||||||
verify: false
|
verify: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -99,6 +126,7 @@ function Fullnode(options) {
|
|||||||
if (!utils.isBrowser) {
|
if (!utils.isBrowser) {
|
||||||
this.http = new bcoin.http.server({
|
this.http = new bcoin.http.server({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
node: this,
|
node: this,
|
||||||
key: this.options.sslKey,
|
key: this.options.sslKey,
|
||||||
cert: this.options.sslCert,
|
cert: this.options.sslCert,
|
||||||
@ -122,28 +150,28 @@ Fullnode.prototype._init = function _init() {
|
|||||||
|
|
||||||
// Bind to errors
|
// Bind to errors
|
||||||
this.mempool.on('error', function(err) {
|
this.mempool.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.miner.on('error', function(err) {
|
this.miner.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pool.on('error', function(err) {
|
this.pool.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chain.on('error', function(err) {
|
this.chain.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.walletdb.on('error', function(err) {
|
this.walletdb.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.http) {
|
if (this.http) {
|
||||||
this.http.on('error', function(err) {
|
this.http.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +182,7 @@ Fullnode.prototype._init = function _init() {
|
|||||||
this.on('tx', function(tx) {
|
this.on('tx', function(tx) {
|
||||||
self.walletdb.addTX(tx, function(err) {
|
self.walletdb.addTX(tx, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -175,14 +203,14 @@ Fullnode.prototype._init = function _init() {
|
|||||||
return;
|
return;
|
||||||
self.mempool.addBlock(block, function(err) {
|
self.mempool.addBlock(block, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chain.on('remove block', function(block) {
|
this.chain.on('remove block', function(block) {
|
||||||
self.walletdb.removeBlock(block, function(err) {
|
self.walletdb.removeBlock(block, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!self.chain.isFull())
|
if (!self.chain.isFull())
|
||||||
@ -190,7 +218,7 @@ Fullnode.prototype._init = function _init() {
|
|||||||
|
|
||||||
self.mempool.removeBlock(block, function(err) {
|
self.mempool.removeBlock(block, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -214,7 +242,7 @@ Fullnode.prototype._open = function open(callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
bcoin.debug('Node is loaded.');
|
self.logger.info('Node is loaded.');
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -256,7 +284,7 @@ Fullnode.prototype._open = function open(callback) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
|
|
||||||
if (txs.length > 0)
|
if (txs.length > 0)
|
||||||
bcoin.debug('Rebroadcasting %d transactions.', txs.length);
|
self.logger.info('Rebroadcasting %d transactions.', txs.length);
|
||||||
|
|
||||||
for (i = 0; i < txs.length; i++)
|
for (i = 0; i < txs.length; i++)
|
||||||
self.pool.broadcast(txs[i]);
|
self.pool.broadcast(txs[i]);
|
||||||
@ -385,13 +413,14 @@ Fullnode.prototype.stopSync = function stopSync() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Fullnode.prototype.createWallet = function createWallet(options, callback) {
|
Fullnode.prototype.createWallet = function createWallet(options, callback) {
|
||||||
|
var self = this;
|
||||||
this.walletdb.ensure(options, function(err, wallet) {
|
this.walletdb.ensure(options, function(err, wallet) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
assert(wallet);
|
assert(wallet);
|
||||||
|
|
||||||
bcoin.debug('Loaded wallet with id=%s address=%s',
|
self.logger.info('Loaded wallet with id=%s address=%s',
|
||||||
wallet.id, wallet.getAddress());
|
wallet.id, wallet.getAddress());
|
||||||
|
|
||||||
return callback(null, wallet);
|
return callback(null, wallet);
|
||||||
|
|||||||
@ -73,8 +73,6 @@ HTTPClient.prototype._open = function _open(callback) {
|
|||||||
|
|
||||||
this.socket.on('connect', function() {
|
this.socket.on('connect', function() {
|
||||||
self.socket.on('version', function(info) {
|
self.socket.on('version', function(info) {
|
||||||
bcoin.debug('Connected to bcoin server: %s (%s)',
|
|
||||||
info.version, info.network);
|
|
||||||
assert(info.network === self.network.type, 'Wrong network.');
|
assert(info.network === self.network.type, 'Wrong network.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,7 @@ function HTTPServer(options) {
|
|||||||
this.walletdb = this.node.walletdb;
|
this.walletdb = this.node.walletdb;
|
||||||
this.mempool = this.node.mempool;
|
this.mempool = this.node.mempool;
|
||||||
this.pool = this.node.pool;
|
this.pool = this.node.pool;
|
||||||
|
this.logger = options.logger || this.node.logger;
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
options.sockets = true;
|
options.sockets = true;
|
||||||
@ -61,7 +62,7 @@ HTTPServer.prototype._init = function _init() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.server.on('request', function(req, res) {
|
this.server.on('request', function(req, res) {
|
||||||
bcoin.debug('Request from %s path=%s',
|
self.logger.debug('Request from %s path=%s',
|
||||||
req.socket.remoteAddress, req.pathname);
|
req.socket.remoteAddress, req.pathname);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -90,8 +91,8 @@ HTTPServer.prototype._init = function _init() {
|
|||||||
var params = utils.merge({}, req.params, req.query, req.body);
|
var params = utils.merge({}, req.params, req.query, req.body);
|
||||||
var options = {};
|
var options = {};
|
||||||
|
|
||||||
bcoin.debug('Params:');
|
self.logger.debug('Params:');
|
||||||
bcoin.debug(params);
|
self.logger.debug(params);
|
||||||
|
|
||||||
if (params.id) {
|
if (params.id) {
|
||||||
assert(params.id !== '!all');
|
assert(params.id !== '!all');
|
||||||
@ -884,7 +885,7 @@ HTTPServer.prototype.listen = function listen(port, host, callback) {
|
|||||||
return self.emit('error', err);
|
return self.emit('error', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.debug('HTTP server listening on %s (port=%d).',
|
self.logger.info('HTTP server listening on %s (port=%d).',
|
||||||
address.address, address.port);
|
address.address, address.port);
|
||||||
|
|
||||||
self.loaded = true;
|
self.loaded = true;
|
||||||
|
|||||||
@ -9,11 +9,9 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bcoin = require('./env');
|
|
||||||
var LowlevelUp = require('./lowlevelup');
|
var LowlevelUp = require('./lowlevelup');
|
||||||
var utils = bcoin.utils;
|
var utils = require('./utils');
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var db = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
@ -33,32 +31,28 @@ var db = {};
|
|||||||
function ldb(options) {
|
function ldb(options) {
|
||||||
options = ldb.parseOptions(options);
|
options = ldb.parseOptions(options);
|
||||||
|
|
||||||
if (!db[options.location]) {
|
if (options.backend !== 'rbt')
|
||||||
if (options.backend !== 'rbt' && !bcoin.isBrowser)
|
utils.mkdir(options.location, true);
|
||||||
bcoin.mkdir(options.location, true);
|
|
||||||
|
|
||||||
db[options.location] = new LowlevelUp(options.location, {
|
return new LowlevelUp(options.location, {
|
||||||
// LevelDB and others
|
// LevelDB and others
|
||||||
createIfMissing: true,
|
createIfMissing: true,
|
||||||
errorIfExists: false,
|
errorIfExists: false,
|
||||||
compression: options.compression !== false,
|
compression: options.compression !== false,
|
||||||
cacheSize: options.cacheSize || (8 << 20),
|
cacheSize: options.cacheSize || (8 << 20),
|
||||||
writeBufferSize: options.writeBufferSize || (4 << 20),
|
writeBufferSize: options.writeBufferSize || (4 << 20),
|
||||||
maxOpenFiles: options.maxOpenFiles || 8192,
|
maxOpenFiles: options.maxOpenFiles || 8192,
|
||||||
filterBits: 0,
|
filterBits: 0,
|
||||||
paranoidChecks: false,
|
paranoidChecks: false,
|
||||||
memory: false,
|
memory: false,
|
||||||
|
|
||||||
// For LMDB if we decide to use it:
|
// For LMDB if we decide to use it:
|
||||||
sync: options.sync || false,
|
sync: options.sync || false,
|
||||||
mapSize: options.mapSize || 300 * (1024 << 20),
|
mapSize: options.mapSize || 300 * (1024 << 20),
|
||||||
writeMap: options.writeMap || false,
|
writeMap: options.writeMap || false,
|
||||||
|
|
||||||
db: options.db
|
db: options.db
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return db[options.location];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,7 +65,7 @@ ldb.getBackend = function getBackend(db) {
|
|||||||
var name, ext;
|
var name, ext;
|
||||||
|
|
||||||
if (!db)
|
if (!db)
|
||||||
db = bcoin.db || 'leveldb';
|
db = 'memory';
|
||||||
|
|
||||||
if (db === 'leveldb')
|
if (db === 'leveldb')
|
||||||
name = 'leveldown';
|
name = 'leveldown';
|
||||||
@ -112,30 +106,26 @@ ldb.getBackend = function getBackend(db) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
ldb.parseOptions = function parseOptions(options) {
|
ldb.parseOptions = function parseOptions(options) {
|
||||||
var network = bcoin.network.get(options.network);
|
|
||||||
var backend = ldb.getBackend(options.db);
|
var backend = ldb.getBackend(options.db);
|
||||||
var location = options.location;
|
var location = options.location;
|
||||||
var db;
|
var db;
|
||||||
|
|
||||||
if (!location) {
|
|
||||||
assert(typeof options.name === 'string', 'Name or location required.');
|
|
||||||
location = bcoin.prefix;
|
|
||||||
if (network.type !== 'main')
|
|
||||||
location += '/' + network.type;
|
|
||||||
location += '/' + options.name + '.' + backend.ext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backend.name === 'rbt')
|
if (backend.name === 'rbt')
|
||||||
db = require('./rbt');
|
db = require('./rbt');
|
||||||
else if (bcoin.isBrowser)
|
else if (utils.isBrowser)
|
||||||
db = require('level-js');
|
db = require('level-js');
|
||||||
else
|
else
|
||||||
db = require(backend.name);
|
db = require(backend.name);
|
||||||
|
|
||||||
|
if (typeof location !== 'string') {
|
||||||
|
assert(backend.name === 'rbt', 'Location required.');
|
||||||
|
location = 'rbt';
|
||||||
|
}
|
||||||
|
|
||||||
return utils.merge({}, options, {
|
return utils.merge({}, options, {
|
||||||
backend: backend.name,
|
backend: backend.name,
|
||||||
ext: backend.ext,
|
ext: backend.ext,
|
||||||
location: location,
|
location: location + '.' + backend.ext,
|
||||||
db: db
|
db: db
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
311
lib/bcoin/logger.js
Normal file
311
lib/bcoin/logger.js
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
/*!
|
||||||
|
* logger.js - basic logger for bcoin
|
||||||
|
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||||
|
* https://github.com/bcoin-org/bcoin
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var utils = require('./utils');
|
||||||
|
var assert = require('assert');
|
||||||
|
var fs;
|
||||||
|
|
||||||
|
if (!utils.isBrowser)
|
||||||
|
fs = require('f' + 's');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic stdout and file logger.
|
||||||
|
* @exports Logger
|
||||||
|
* @constructor
|
||||||
|
* @param {(String|Object)?} options/level
|
||||||
|
* @param {String?} options.level
|
||||||
|
* @param {Boolean} [options.colors=true]
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Logger(options) {
|
||||||
|
if (!(this instanceof Logger))
|
||||||
|
return new Logger(options);
|
||||||
|
|
||||||
|
if (!options)
|
||||||
|
options = {};
|
||||||
|
|
||||||
|
if (typeof options === 'string')
|
||||||
|
options = { level: options };
|
||||||
|
|
||||||
|
this.level = Logger.levels.warning;
|
||||||
|
this.colors = options.colors !== false;
|
||||||
|
this.file = options.file;
|
||||||
|
this.stream = null;
|
||||||
|
this.closed = false;
|
||||||
|
|
||||||
|
if (!process.stdout || !process.stdout.isTTY)
|
||||||
|
this.colors = false;
|
||||||
|
|
||||||
|
if (options.level != null)
|
||||||
|
this.setLevel(options.level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available log levels.
|
||||||
|
* @enum {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.levels = {
|
||||||
|
none: 0,
|
||||||
|
error: 1,
|
||||||
|
warning: 2,
|
||||||
|
info: 3,
|
||||||
|
debug: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default CSI colors.
|
||||||
|
* @enum {String}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.colors = {
|
||||||
|
error: '1;31',
|
||||||
|
warning: '1;33',
|
||||||
|
info: '94',
|
||||||
|
debug: '90'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the logger.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.open = function open() {
|
||||||
|
this.closed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the write stream.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.close = function close() {
|
||||||
|
if (!this.stream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.stream.destroy();
|
||||||
|
} catch (e) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.closed = true;
|
||||||
|
this.stream = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or reset the log level.
|
||||||
|
* @param {String} level
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.setLevel = function setLevel(level) {
|
||||||
|
level = Logger.levels[level];
|
||||||
|
assert(level != null, 'Invalid log level.');
|
||||||
|
this.level = level;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a log to the `error` log level.
|
||||||
|
* @param {String|Object|Error} err
|
||||||
|
* @param {...Object} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.error = function error(err) {
|
||||||
|
var i, args;
|
||||||
|
|
||||||
|
if (this.level < Logger.levels.error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (err instanceof Error)
|
||||||
|
return this._error(err);
|
||||||
|
|
||||||
|
args = new Array(arguments.length);
|
||||||
|
|
||||||
|
for (i = 0; i < args.length; i++)
|
||||||
|
args[i] = arguments[i];
|
||||||
|
|
||||||
|
this.log('error', args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a log to the `warning` log level.
|
||||||
|
* @param {String|Object} obj
|
||||||
|
* @param {...Object} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.warning = function warning() {
|
||||||
|
var i, args;
|
||||||
|
|
||||||
|
if (this.level < Logger.levels.warning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args = new Array(arguments.length);
|
||||||
|
|
||||||
|
for (i = 0; i < args.length; i++)
|
||||||
|
args[i] = arguments[i];
|
||||||
|
|
||||||
|
this.log('warning', args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a log to the `info` log level.
|
||||||
|
* @param {String|Object} obj
|
||||||
|
* @param {...Object} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.info = function info() {
|
||||||
|
var i, args;
|
||||||
|
|
||||||
|
if (this.level < Logger.levels.info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args = new Array(arguments.length);
|
||||||
|
|
||||||
|
for (i = 0; i < args.length; i++)
|
||||||
|
args[i] = arguments[i];
|
||||||
|
|
||||||
|
this.log('info', args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a log to the `debug` log level.
|
||||||
|
* @param {String|Object} obj
|
||||||
|
* @param {...Object} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.debug = function debug() {
|
||||||
|
var i, args;
|
||||||
|
|
||||||
|
if (this.level < Logger.levels.debug)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args = new Array(arguments.length);
|
||||||
|
|
||||||
|
for (i = 0; i < args.length; i++)
|
||||||
|
args[i] = arguments[i];
|
||||||
|
|
||||||
|
this.log('debug', args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a log to the desired log level.
|
||||||
|
* Note that this bypasses the level check.
|
||||||
|
* @param {String} level
|
||||||
|
* @param {Object[]} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.log = function log(level, args) {
|
||||||
|
var prefix, color, msg;
|
||||||
|
|
||||||
|
if (this.closed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(Logger.levels[level] != null, 'Invalid log level.');
|
||||||
|
|
||||||
|
prefix = '[' + level + '] ';
|
||||||
|
|
||||||
|
if (utils.isBrowser) {
|
||||||
|
msg = typeof args[0] !== 'object'
|
||||||
|
? utils.format(args, false)
|
||||||
|
: args[0];
|
||||||
|
|
||||||
|
msg = prefix + msg;
|
||||||
|
|
||||||
|
if (level === 'error')
|
||||||
|
console.error(msg);
|
||||||
|
else
|
||||||
|
console.log(msg);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.colors) {
|
||||||
|
color = Logger.colors[level];
|
||||||
|
prefix = '\x1b[' + color + 'm' + prefix + '\x1b[m';
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = prefix + utils.format(args, this.colors);
|
||||||
|
|
||||||
|
if (level === 'error')
|
||||||
|
process.stderr.write(msg + '\n');
|
||||||
|
else
|
||||||
|
process.stdout.write(msg + '\n');
|
||||||
|
|
||||||
|
if (this.file) {
|
||||||
|
if (this.colors)
|
||||||
|
msg = prefix + utils.format(args, false);
|
||||||
|
this.write(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string to the output stream (usually a file).
|
||||||
|
* @param {String} msg
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.write = function write(msg) {
|
||||||
|
if (!fs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.closed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.stream) {
|
||||||
|
utils.mkdir(this.file, true);
|
||||||
|
this.stream = fs.createWriteStream(this.file, { flags: 'a' });
|
||||||
|
this.stream.on('error', function() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stream.write(process.pid + ' (' + utils.date() + '): ' + msg + '\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to parse an error into a nicer
|
||||||
|
* format. Call's `log` internally.
|
||||||
|
* @private
|
||||||
|
* @param {Error} err
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype._error = function error(err) {
|
||||||
|
var msg;
|
||||||
|
|
||||||
|
if (utils.isBrowser) {
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = (err.message + '').replace(/^ *Error: */, '');
|
||||||
|
|
||||||
|
this.log('error', [msg]);
|
||||||
|
|
||||||
|
if (this.file)
|
||||||
|
this.write(err.stack + '');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the current memory usage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Logger.prototype.memory = function memory() {
|
||||||
|
var mem;
|
||||||
|
|
||||||
|
if (!process.memoryUsage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mem = process.memoryUsage();
|
||||||
|
|
||||||
|
this.debug('Memory: rss=%dmb, js-heap=%d/%dmb native-heap=%dmb',
|
||||||
|
utils.mb(mem.rss),
|
||||||
|
utils.mb(mem.heapUsed),
|
||||||
|
utils.mb(mem.heapTotal),
|
||||||
|
utils.mb(mem.rss - mem.heapTotal));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = Logger;
|
||||||
@ -70,17 +70,17 @@ function Mempool(options) {
|
|||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.chain = options.chain;
|
this.chain = options.chain;
|
||||||
|
this.fees = options.fees;
|
||||||
|
|
||||||
assert(this.chain, 'Mempool requires a blockchain.');
|
assert(this.chain, 'Mempool requires a blockchain.');
|
||||||
|
|
||||||
this.network = this.chain.network;
|
this.network = this.chain.network;
|
||||||
|
this.logger = options.logger || this.chain.logger;
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
this.locker = new bcoin.locker(this, this.addTX);
|
this.locker = new bcoin.locker(this, this.addTX);
|
||||||
|
|
||||||
this.db = bcoin.ldb({
|
this.db = bcoin.ldb({
|
||||||
network: this.network,
|
|
||||||
name: this.options.name || 'mempool',
|
|
||||||
location: this.options.location,
|
location: this.options.location,
|
||||||
db: this.options.db || 'memory'
|
db: this.options.db || 'memory'
|
||||||
});
|
});
|
||||||
@ -243,7 +243,8 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
|
|||||||
self.blockSinceBump = true;
|
self.blockSinceBump = true;
|
||||||
self.lastFeeUpdate = utils.now();
|
self.lastFeeUpdate = utils.now();
|
||||||
|
|
||||||
self.network.fees.processBlock(block.height, entries, self.chain.isFull());
|
if (self.fees)
|
||||||
|
self.fees.processBlock(block.height, entries, self.chain.isFull());
|
||||||
|
|
||||||
return callback();
|
return callback();
|
||||||
});
|
});
|
||||||
@ -373,7 +374,7 @@ Mempool.prototype.limitOrphans = function limitOrphans() {
|
|||||||
hash = orphans[i];
|
hash = orphans[i];
|
||||||
orphans.splice(i, 1);
|
orphans.splice(i, 1);
|
||||||
|
|
||||||
bcoin.debug('Removing orphan %s from mempool.', utils.revHex(hash));
|
this.logger.debug('Removing orphan %s from mempool.', utils.revHex(hash));
|
||||||
|
|
||||||
this.removeOrphan(hash);
|
this.removeOrphan(hash);
|
||||||
}
|
}
|
||||||
@ -830,9 +831,10 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
|
|||||||
self.emit('tx', entry.tx);
|
self.emit('tx', entry.tx);
|
||||||
self.emit('add tx', entry.tx);
|
self.emit('add tx', entry.tx);
|
||||||
|
|
||||||
self.network.fees.processTX(entry, self.chain.isFull());
|
if (self.fees)
|
||||||
|
self.fees.processTX(entry, self.chain.isFull());
|
||||||
|
|
||||||
bcoin.debug('Added tx %s to the mempool.', entry.tx.rhash);
|
self.logger.debug('Added tx %s to the mempool.', entry.tx.rhash);
|
||||||
|
|
||||||
resolved = self.resolveOrphans(entry.tx);
|
resolved = self.resolveOrphans(entry.tx);
|
||||||
|
|
||||||
@ -841,7 +843,7 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
|
|||||||
self.verify(entry, function(err) {
|
self.verify(entry, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.type === 'VerifyError') {
|
if (err.type === 'VerifyError') {
|
||||||
bcoin.debug('Could not resolve orphan %s: %s.',
|
self.logger.debug('Could not resolve orphan %s: %s.',
|
||||||
tx.rhash,
|
tx.rhash,
|
||||||
err.message);
|
err.message);
|
||||||
self.emit('bad orphan', tx, entry);
|
self.emit('bad orphan', tx, entry);
|
||||||
@ -855,7 +857,7 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
|
|||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
bcoin.debug('Resolved orphan %s in mempool.', entry.tx.rhash);
|
self.logger.debug('Resolved orphan %s in mempool.', entry.tx.rhash);
|
||||||
next();
|
next();
|
||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
@ -897,7 +899,8 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit, callb
|
|||||||
self.size -= self.memUsage(entry.tx);
|
self.size -= self.memUsage(entry.tx);
|
||||||
self.total--;
|
self.total--;
|
||||||
|
|
||||||
self.network.fees.removeTX(hash);
|
if (self.fees)
|
||||||
|
self.fees.removeTX(hash);
|
||||||
|
|
||||||
if (limit) {
|
if (limit) {
|
||||||
rate = bcoin.tx.getRate(entry.sizes, entry.fees);
|
rate = bcoin.tx.getRate(entry.sizes, entry.fees);
|
||||||
@ -1144,7 +1147,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx) {
|
|||||||
var i, hash, input, prev;
|
var i, hash, input, prev;
|
||||||
|
|
||||||
if (tx.getSize() > 5000) {
|
if (tx.getSize() > 5000) {
|
||||||
bcoin.debug('Ignoring large orphan: %s', tx.rhash);
|
this.logger.debug('Ignoring large orphan: %s', tx.rhash);
|
||||||
this.emit('bad orphan', tx);
|
this.emit('bad orphan', tx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1171,7 +1174,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx) {
|
|||||||
this.orphans[hash] = tx.toExtended(true);
|
this.orphans[hash] = tx.toExtended(true);
|
||||||
this.totalOrphans++;
|
this.totalOrphans++;
|
||||||
|
|
||||||
bcoin.debug('Added orphan %s to mempool.', tx.rhash);
|
this.logger.debug('Added orphan %s to mempool.', tx.rhash);
|
||||||
|
|
||||||
this.emit('add orphan', tx);
|
this.emit('add orphan', tx);
|
||||||
|
|
||||||
@ -1242,7 +1245,7 @@ Mempool.prototype.getOrphan = function getOrphan(hash) {
|
|||||||
orphan = bcoin.tx.fromExtended(orphan, true);
|
orphan = bcoin.tx.fromExtended(orphan, true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
delete this.orphans[hash];
|
delete this.orphans[hash];
|
||||||
bcoin.debug('%s %s',
|
this.logger.warning('%s %s',
|
||||||
'Warning: possible memory corruption.',
|
'Warning: possible memory corruption.',
|
||||||
'Orphan failed deserialization.');
|
'Orphan failed deserialization.');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -40,12 +40,14 @@ function Miner(options) {
|
|||||||
options = {};
|
options = {};
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.address = this.options.address;
|
this.address = bcoin.address(this.options.address);
|
||||||
this.coinbaseFlags = this.options.coinbaseFlags || 'mined by bcoin';
|
this.coinbaseFlags = this.options.coinbaseFlags || 'mined by bcoin';
|
||||||
|
|
||||||
this.pool = options.pool;
|
this.pool = options.pool;
|
||||||
this.chain = options.chain;
|
this.chain = options.chain;
|
||||||
|
this.logger = options.logger || this.chain.logger;
|
||||||
this.mempool = options.mempool;
|
this.mempool = options.mempool;
|
||||||
|
this.fees = this.mempool ? this.mempool.fees : options.fees;
|
||||||
|
|
||||||
assert(this.chain, 'Miner requires a blockchain.');
|
assert(this.chain, 'Miner requires a blockchain.');
|
||||||
|
|
||||||
@ -57,7 +59,6 @@ function Miner(options) {
|
|||||||
|
|
||||||
if (bcoin.useWorkers) {
|
if (bcoin.useWorkers) {
|
||||||
this.workerPool = new bcoin.workers({
|
this.workerPool = new bcoin.workers({
|
||||||
network: this.network,
|
|
||||||
size: this.options.parallel ? 2 : 1,
|
size: this.options.parallel ? 2 : 1,
|
||||||
timeout: -1
|
timeout: -1
|
||||||
});
|
});
|
||||||
@ -103,12 +104,12 @@ Miner.prototype._init = function _init() {
|
|||||||
|
|
||||||
this.on('block', function(block) {
|
this.on('block', function(block) {
|
||||||
// Emit the block hex as a failsafe (in case we can't send it)
|
// Emit the block hex as a failsafe (in case we can't send it)
|
||||||
bcoin.debug('Found block: %d (%s).', block.height, block.rhash);
|
self.logger.info('Found block: %d (%s).', block.height, block.rhash);
|
||||||
bcoin.debug('Raw: %s', block.toRaw().toString('hex'));
|
self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('status', function(stat) {
|
this.on('status', function(stat) {
|
||||||
bcoin.debug(
|
self.logger.info(
|
||||||
'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
|
'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
|
||||||
stat.hashrate / 1000 | 0,
|
stat.hashrate / 1000 | 0,
|
||||||
stat.hashes,
|
stat.hashes,
|
||||||
@ -177,7 +178,7 @@ Miner.prototype.start = function start(version) {
|
|||||||
self.chain.add(block, function(err) {
|
self.chain.add(block, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.type === 'VerifyError')
|
if (err.type === 'VerifyError')
|
||||||
bcoin.debug('%s could not be added to chain.', block.rhash);
|
self.logger.warning('%s could not be added to chain.', block.rhash);
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
return self.start();
|
return self.start();
|
||||||
}
|
}
|
||||||
@ -700,10 +701,11 @@ MinerBlock.prototype.toRaw = function toRaw(writer) {
|
|||||||
var p = new BufferWriter(writer);
|
var p = new BufferWriter(writer);
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
|
p.writeU32(this.network.magic);
|
||||||
p.writeBytes(this.tip.toRaw());
|
p.writeBytes(this.tip.toRaw());
|
||||||
p.writeU32(this.block.version);
|
p.writeU32(this.block.version);
|
||||||
p.writeU32(this.block.bits);
|
p.writeU32(this.block.bits);
|
||||||
p.writeVarString(this.address, 'ascii');
|
p.writeVarBytes(this.address.toRaw());
|
||||||
p.writeVarString(this.coinbaseFlags, 'utf8');
|
p.writeVarString(this.coinbaseFlags, 'utf8');
|
||||||
p.writeU8(this.witness ? 1 : 0);
|
p.writeU8(this.witness ? 1 : 0);
|
||||||
p.writeVarint(this.block.txs.length - 1);
|
p.writeVarint(this.block.txs.length - 1);
|
||||||
@ -725,10 +727,11 @@ MinerBlock.prototype.toRaw = function toRaw(writer) {
|
|||||||
|
|
||||||
MinerBlock.fromRaw = function fromRaw(data) {
|
MinerBlock.fromRaw = function fromRaw(data) {
|
||||||
var p = new BufferReader(data);
|
var p = new BufferReader(data);
|
||||||
|
var network = bcoin.network.fromMagic(p.readU32());
|
||||||
var tip = bcoin.chainentry.fromRaw(null, p);
|
var tip = bcoin.chainentry.fromRaw(null, p);
|
||||||
var version = p.readU32();
|
var version = p.readU32();
|
||||||
var bits = p.readU32();
|
var bits = p.readU32();
|
||||||
var address = p.readVarString('ascii');
|
var address = bcoin.address.fromRaw(p.readVarBytes());
|
||||||
var coinbaseFlags = p.readVarString('utf8');
|
var coinbaseFlags = p.readVarString('utf8');
|
||||||
var witness = p.readU8() === 1;
|
var witness = p.readU8() === 1;
|
||||||
var count = p.readVarint();
|
var count = p.readVarint();
|
||||||
@ -738,7 +741,10 @@ MinerBlock.fromRaw = function fromRaw(data) {
|
|||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
txs.push(bcoin.tx.fromRaw(p));
|
txs.push(bcoin.tx.fromRaw(p));
|
||||||
|
|
||||||
|
tip.network = network;
|
||||||
|
|
||||||
return new MinerBlock({
|
return new MinerBlock({
|
||||||
|
network: network,
|
||||||
tip: tip,
|
tip: tip,
|
||||||
version: version,
|
version: version,
|
||||||
target: bits,
|
target: bits,
|
||||||
|
|||||||
@ -9,9 +9,7 @@
|
|||||||
|
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var constants = require('./protocol/constants');
|
|
||||||
var networks = require('./protocol/network');
|
var networks = require('./protocol/network');
|
||||||
var PolicyEstimator = require('./fees');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a network.
|
* Represents a network.
|
||||||
@ -57,8 +55,6 @@ function Network(options) {
|
|||||||
this.maxRate = options.maxRate;
|
this.maxRate = options.maxRate;
|
||||||
this.selfConnect = options.selfConnect;
|
this.selfConnect = options.selfConnect;
|
||||||
this.requestMempool = options.requestMempool;
|
this.requestMempool = options.requestMempool;
|
||||||
|
|
||||||
this.fees = new PolicyEstimator(constants.tx.MIN_RELAY, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
var bcoin = require('./env');
|
var bcoin = require('./env');
|
||||||
var AsyncObject = require('./async');
|
var AsyncObject = require('./async');
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
|
var Profiler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class from which every other
|
* Base class from which every other
|
||||||
@ -26,22 +27,213 @@ function Node(options) {
|
|||||||
|
|
||||||
AsyncObject.call(this);
|
AsyncObject.call(this);
|
||||||
|
|
||||||
if (!options)
|
options = this._parseOptions(options);
|
||||||
options = {};
|
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.network = bcoin.network.get(options.network);
|
this.network = bcoin.network.get(options.network);
|
||||||
|
this.prefix = options.prefix;
|
||||||
|
|
||||||
|
this.logger = options.logger;
|
||||||
|
this.db = options.db;
|
||||||
|
this.profiler = null;
|
||||||
this.mempool = null;
|
this.mempool = null;
|
||||||
this.pool = null;
|
this.pool = null;
|
||||||
this.chain = null;
|
this.chain = null;
|
||||||
|
this.fees = null;
|
||||||
this.miner = null;
|
this.miner = null;
|
||||||
this.walletdb = null;
|
this.walletdb = null;
|
||||||
this.wallet = null;
|
this.wallet = null;
|
||||||
|
|
||||||
|
this._bound = [];
|
||||||
|
|
||||||
|
if (!this.logger) {
|
||||||
|
this.logger = new bcoin.logger({
|
||||||
|
level: options.logLevel || 'none',
|
||||||
|
file: options.logFile
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.profile && !utils.isBrowser) {
|
||||||
|
Profiler = require('./prof' + 'iler');
|
||||||
|
this.profiler = new Profiler(this.prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__init();
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.inherits(Node, AsyncObject);
|
utils.inherits(Node, AsyncObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize node.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype.__init = function __init() {
|
||||||
|
var self = this;
|
||||||
|
this.on('preopen', function() {
|
||||||
|
self._onOpen();
|
||||||
|
});
|
||||||
|
this.on('close', function() {
|
||||||
|
self._onClose();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open node. Bind all events.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype._onOpen = function _onOpen() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.logger.open();
|
||||||
|
|
||||||
|
this._bind(bcoin.time, 'offset', function(offset) {
|
||||||
|
self.logger.info('Time offset: %d (%d minutes).', offset, offset / 60 | 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._bind(bcoin.time, 'sample', function(sample, total) {
|
||||||
|
self.logger.debug('Added time data: samples=%d, offset=%d (%d minutes).',
|
||||||
|
total, sample, sample / 60 | 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._bind(bcoin.time, 'mismatch', function() {
|
||||||
|
self.logger.warning('Please make sure your system clock is correct!');
|
||||||
|
});
|
||||||
|
|
||||||
|
this._bind(bcoin.workerPool, 'spawn', function(child) {
|
||||||
|
self.logger.info('Spawning worker process: %d.', child.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._bind(bcoin.workerPool, 'exit', function(code, child) {
|
||||||
|
self.logger.warning('Worker %d exited: %s.', child.id, code);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._bind(bcoin.workerPool, 'error', function(err, child) {
|
||||||
|
if (child) {
|
||||||
|
self.logger.error('Worker %d error: %s', child.id, err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.emit('error', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close node. Unbind all events.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype._onClose = function _onClose() {
|
||||||
|
var i, bound;
|
||||||
|
|
||||||
|
this.logger.close();
|
||||||
|
|
||||||
|
for (i = 0; i < this._bound.length; i++) {
|
||||||
|
bound = this._bound[i];
|
||||||
|
bound[0].removeListener(bound[1], bound[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._bound.length = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind to an event on `obj`, save listener for removal.
|
||||||
|
* @private
|
||||||
|
* @param {EventEmitter} obj
|
||||||
|
* @param {String} event
|
||||||
|
* @param {Function} listener
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype._bind = function _bind(obj, event, listener) {
|
||||||
|
this._bound.push([obj, event, listener]);
|
||||||
|
obj.on(event, listener);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit and log an error.
|
||||||
|
* @private
|
||||||
|
* @param {Error} err
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype._error = function _error(err) {
|
||||||
|
this.logger.error(err);
|
||||||
|
this.emit('error', err);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse options object.
|
||||||
|
* @private
|
||||||
|
* @param {Object} options
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype._parseOptions = function _parseOptions(options) {
|
||||||
|
if (!options)
|
||||||
|
options = {};
|
||||||
|
|
||||||
|
options = utils.merge({}, options);
|
||||||
|
|
||||||
|
if (process.env.BCOIN_PREFIX != null)
|
||||||
|
options.prefix = process.env.BCOIN_PREFIX;
|
||||||
|
|
||||||
|
if (process.env.BCOIN_DB != null)
|
||||||
|
options.db = process.env.BCOIN_DB;
|
||||||
|
|
||||||
|
if (process.env.BCOIN_LOGLEVEL != null)
|
||||||
|
options.logLevel = process.env.BCOIN_LOGLEVEL;
|
||||||
|
|
||||||
|
if (process.env.BCOIN_LOGFILE != null) {
|
||||||
|
if (process.env.BCOIN_LOGFILE === '0'
|
||||||
|
|| process.env.BCOIN_LOGFILE === '1') {
|
||||||
|
options.logFile = +process.env.BCOIN_LOGFILE === 1;
|
||||||
|
} else {
|
||||||
|
options.logFile = process.env.BCOIN_LOGFILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.BCOIN_SEED != null)
|
||||||
|
options.preferredSeed = process.env.BCOIN_SEED;
|
||||||
|
|
||||||
|
if (process.env.BCOIN_PROFILE != null)
|
||||||
|
options.profile = +process.env.BCOIN_PROFILE === 1;
|
||||||
|
|
||||||
|
if (!options.prefix)
|
||||||
|
options.prefix = utils.HOME + '/.bcoin';
|
||||||
|
|
||||||
|
if (!options.db)
|
||||||
|
options.db = 'memory';
|
||||||
|
|
||||||
|
options.prefix = utils.normalize(options.prefix);
|
||||||
|
|
||||||
|
if (options.logFile && typeof options.logFile !== 'string') {
|
||||||
|
options.logFile = options.prefix;
|
||||||
|
if (options.network !== 'main')
|
||||||
|
options.logFile += '/' + options.network;
|
||||||
|
options.logFile += '/debug.log';
|
||||||
|
}
|
||||||
|
|
||||||
|
options.logFile = options.logFile
|
||||||
|
? utils.normalize(options.logFile)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a file path from a name
|
||||||
|
* as well as the node's prefix.
|
||||||
|
* @param {String} name
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype.location = function location(name) {
|
||||||
|
var path = this.prefix;
|
||||||
|
if (this.network.type !== 'main')
|
||||||
|
path += '/' + this.network.type;
|
||||||
|
path += '/' + name;
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -76,6 +76,7 @@ function Peer(pool, options) {
|
|||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.pool = pool;
|
this.pool = pool;
|
||||||
|
this.logger = pool.logger;
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.host = null;
|
this.host = null;
|
||||||
this.port = 0;
|
this.port = 0;
|
||||||
@ -236,7 +237,7 @@ Peer.prototype._onConnect = function _onConnect() {
|
|||||||
|
|
||||||
// Wait for _their_ version.
|
// Wait for _their_ version.
|
||||||
if (!self.version) {
|
if (!self.version) {
|
||||||
bcoin.debug(
|
self.logger.debug(
|
||||||
'Peer sent a verack without a version (%s).',
|
'Peer sent a verack without a version (%s).',
|
||||||
self.hostname);
|
self.hostname);
|
||||||
self.request('version', callee);
|
self.request('version', callee);
|
||||||
@ -287,7 +288,7 @@ Peer.prototype._onConnect = function _onConnect() {
|
|||||||
self.sendMempool();
|
self.sendMempool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.debug('Received verack (%s).', self.hostname);
|
self.logger.debug('Received verack (%s).', self.hostname);
|
||||||
|
|
||||||
// Finally we can let the pool know
|
// Finally we can let the pool know
|
||||||
// that this peer is ready to go.
|
// that this peer is ready to go.
|
||||||
@ -321,19 +322,19 @@ Peer.prototype.createSocket = function createSocket(port, host) {
|
|||||||
if (this._createSocket) {
|
if (this._createSocket) {
|
||||||
socket = this._createSocket(port, host);
|
socket = this._createSocket(port, host);
|
||||||
} else {
|
} else {
|
||||||
if (bcoin.isBrowser) {
|
if (utils.isBrowser) {
|
||||||
proxy = require('../../browser/proxysocket');
|
proxy = require('../../browser/proxysocket');
|
||||||
socket = proxy.connect(bcoin.proxyServer, port, host);
|
socket = proxy.connect(this.pool.proxyServer, port, host);
|
||||||
} else {
|
} else {
|
||||||
net = require('n' + 'et');
|
net = require('n' + 'et');
|
||||||
socket = net.connect(port, host);
|
socket = net.connect(port, host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.debug('Connecting to %s.', hostname);
|
this.logger.debug('Connecting to %s.', hostname);
|
||||||
|
|
||||||
socket.once('connect', function() {
|
socket.once('connect', function() {
|
||||||
bcoin.debug('Connected to %s.', hostname);
|
self.logger.info('Connected to %s.', hostname);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._connectTimeout = setTimeout(function() {
|
this._connectTimeout = setTimeout(function() {
|
||||||
@ -413,7 +414,7 @@ Peer.prototype.sendInv = function sendInv(items) {
|
|||||||
if (items.length === 0)
|
if (items.length === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bcoin.debug('Serving %d inv items to %s.',
|
this.logger.debug('Serving %d inv items to %s.',
|
||||||
items.length, this.hostname);
|
items.length, this.hostname);
|
||||||
|
|
||||||
for (i = 0; i < items.length; i += 50000) {
|
for (i = 0; i < items.length; i += 50000) {
|
||||||
@ -442,7 +443,7 @@ Peer.prototype.sendHeaders = function sendHeaders(items) {
|
|||||||
if (items.length === 0)
|
if (items.length === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bcoin.debug('Serving %d headers to %s.',
|
this.logger.debug('Serving %d headers to %s.',
|
||||||
items.length, this.hostname);
|
items.length, this.hostname);
|
||||||
|
|
||||||
for (i = 0; i < items.length; i += 2000) {
|
for (i = 0; i < items.length; i += 2000) {
|
||||||
@ -485,7 +486,7 @@ Peer.prototype.sendPing = function sendPing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.challenge) {
|
if (this.challenge) {
|
||||||
bcoin.debug('Peer has not responded to ping (%s).', this.hostname);
|
this.logger.debug('Peer has not responded to ping (%s).', this.hostname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -792,7 +793,7 @@ Peer.prototype._onPacket = function onPacket(packet) {
|
|||||||
this.fire(cmd, payload);
|
this.fire(cmd, payload);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
bcoin.debug('Unknown packet: %s.', cmd);
|
this.logger.warning('Unknown packet: %s.', cmd);
|
||||||
this.fire(cmd, payload);
|
this.fire(cmd, payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -876,7 +877,7 @@ Peer.prototype._handleFilterClear = function _handleFilterClear(payload) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Peer.prototype._handleUTXOs = function _handleUTXOs(payload) {
|
Peer.prototype._handleUTXOs = function _handleUTXOs(payload) {
|
||||||
bcoin.debug('Received %d utxos (%s).',
|
this.logger.debug('Received %d utxos (%s).',
|
||||||
payload.coins.length, this.hostname);
|
payload.coins.length, this.hostname);
|
||||||
this.fire('utxos', payload);
|
this.fire('utxos', payload);
|
||||||
};
|
};
|
||||||
@ -1333,7 +1334,7 @@ Peer.prototype._handleMempool = function _handleMempool() {
|
|||||||
for (i = 0; i < hashes.length; i++)
|
for (i = 0; i < hashes.length; i++)
|
||||||
items.push(new InvItem(constants.inv.TX, hashes[i]));
|
items.push(new InvItem(constants.inv.TX, hashes[i]));
|
||||||
|
|
||||||
bcoin.debug('Sending mempool snapshot (%s).', self.hostname);
|
self.logger.debug('Sending mempool snapshot (%s).', self.hostname);
|
||||||
|
|
||||||
self.sendInv(items);
|
self.sendInv(items);
|
||||||
done();
|
done();
|
||||||
@ -1383,13 +1384,13 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((item.type & ~constants.WITNESS_MASK) !== entry.type) {
|
if ((item.type & ~constants.WITNESS_MASK) !== entry.type) {
|
||||||
bcoin.debug(
|
self.logger.debug(
|
||||||
'Peer requested an existing item with the wrong type (%s).',
|
'Peer requested an existing item with the wrong type (%s).',
|
||||||
this.hostname);
|
this.hostname);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Peer requested %s %s as a %s packet (%s).',
|
'Peer requested %s %s as a %s packet (%s).',
|
||||||
entry.type === constants.inv.TX ? 'tx' : 'block',
|
entry.type === constants.inv.TX ? 'tx' : 'block',
|
||||||
utils.revHex(entry.hash),
|
utils.revHex(entry.hash),
|
||||||
@ -1530,7 +1531,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
|
|||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
|
|
||||||
bcoin.debug(
|
self.logger.debug(
|
||||||
'Served %d items with getdata (notfound=%d) (%s).',
|
'Served %d items with getdata (notfound=%d) (%s).',
|
||||||
items.length - notfound.length,
|
items.length - notfound.length,
|
||||||
notfound.length,
|
notfound.length,
|
||||||
@ -1555,7 +1556,7 @@ Peer.prototype._handleAddr = function _handleAddr(addrs) {
|
|||||||
for (i = 0; i < addrs.length; i++)
|
for (i = 0; i < addrs.length; i++)
|
||||||
this.addrFilter.add(addrs[i].host, 'ascii');
|
this.addrFilter.add(addrs[i].host, 'ascii');
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Received %d addrs (hosts=%d, peers=%d) (%s).',
|
'Received %d addrs (hosts=%d, peers=%d) (%s).',
|
||||||
addrs.length,
|
addrs.length,
|
||||||
this.pool.hosts.length,
|
this.pool.hosts.length,
|
||||||
@ -1586,17 +1587,17 @@ Peer.prototype._handlePong = function _handlePong(data) {
|
|||||||
var now = utils.ms();
|
var now = utils.ms();
|
||||||
|
|
||||||
if (!this.challenge) {
|
if (!this.challenge) {
|
||||||
bcoin.debug('Peer sent an unsolicited pong (%s).', this.hostname);
|
this.logger.debug('Peer sent an unsolicited pong (%s).', this.hostname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.nonce.cmp(this.challenge) !== 0) {
|
if (data.nonce.cmp(this.challenge) !== 0) {
|
||||||
if (data.nonce.cmpn(0) === 0) {
|
if (data.nonce.cmpn(0) === 0) {
|
||||||
bcoin.debug('Peer sent a zero nonce (%s).', this.hostname);
|
this.logger.debug('Peer sent a zero nonce (%s).', this.hostname);
|
||||||
this.challenge = null;
|
this.challenge = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bcoin.debug('Peer sent the wrong nonce (%s).', this.hostname);
|
this.logger.debug('Peer sent the wrong nonce (%s).', this.hostname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1606,7 +1607,7 @@ Peer.prototype._handlePong = function _handlePong(data) {
|
|||||||
this.minPing = now - this.lastPing;
|
this.minPing = now - this.lastPing;
|
||||||
this.minPing = Math.min(this.minPing, now - this.lastPing);
|
this.minPing = Math.min(this.minPing, now - this.lastPing);
|
||||||
} else {
|
} else {
|
||||||
bcoin.debug('Timing mismatch (what?) (%s).', this.hostname);
|
this.logger.debug('Timing mismatch (what?) (%s).', this.hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.challenge = null;
|
this.challenge = null;
|
||||||
@ -1645,7 +1646,7 @@ Peer.prototype._handleGetAddr = function _handleGetAddr() {
|
|||||||
if (items.length === 0)
|
if (items.length === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Sending %d addrs to peer (%s)',
|
'Sending %d addrs to peer (%s)',
|
||||||
items.length,
|
items.length,
|
||||||
this.hostname);
|
this.hostname);
|
||||||
@ -1691,7 +1692,7 @@ Peer.prototype._handleInv = function _handleInv(items) {
|
|||||||
this.emit('txs', txs);
|
this.emit('txs', txs);
|
||||||
|
|
||||||
if (unknown != null) {
|
if (unknown != null) {
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Peer sent an unknown inv type: %d (%s).',
|
'Peer sent an unknown inv type: %d (%s).',
|
||||||
unknown, this.hostname);
|
unknown, this.hostname);
|
||||||
}
|
}
|
||||||
@ -1767,11 +1768,11 @@ Peer.prototype.sendAlert = function sendAlert(alert) {
|
|||||||
Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
|
Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
|
||||||
var packet = new GetBlocksPacket(locator, stop);
|
var packet = new GetBlocksPacket(locator, stop);
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Requesting headers packet from peer with getheaders (%s).',
|
'Requesting headers packet from peer with getheaders (%s).',
|
||||||
this.hostname);
|
this.hostname);
|
||||||
|
|
||||||
bcoin.debug('Height: %d, Hash: %s, Stop: %s',
|
this.logger.debug('Height: %d, Hash: %s, Stop: %s',
|
||||||
locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1,
|
locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1,
|
||||||
locator && locator.length ? utils.revHex(locator[0]) : 0,
|
locator && locator.length ? utils.revHex(locator[0]) : 0,
|
||||||
stop ? utils.revHex(stop) : 0);
|
stop ? utils.revHex(stop) : 0);
|
||||||
@ -1788,11 +1789,11 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
|
|||||||
Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
|
Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
|
||||||
var packet = new GetBlocksPacket(locator, stop);
|
var packet = new GetBlocksPacket(locator, stop);
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Requesting inv packet from peer with getblocks (%s).',
|
'Requesting inv packet from peer with getblocks (%s).',
|
||||||
this.hostname);
|
this.hostname);
|
||||||
|
|
||||||
bcoin.debug('Height: %d, Hash: %s, Stop: %s',
|
this.logger.debug('Height: %d, Hash: %s, Stop: %s',
|
||||||
locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1,
|
locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1,
|
||||||
locator && locator.length ? utils.revHex(locator[0]) : 0,
|
locator && locator.length ? utils.revHex(locator[0]) : 0,
|
||||||
stop ? utils.revHex(stop) : 0);
|
stop ? utils.revHex(stop) : 0);
|
||||||
@ -1805,7 +1806,7 @@ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Peer.prototype.sendMempool = function sendMempool() {
|
Peer.prototype.sendMempool = function sendMempool() {
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Requesting inv packet from peer with mempool (%s).',
|
'Requesting inv packet from peer with mempool (%s).',
|
||||||
this.hostname);
|
this.hostname);
|
||||||
|
|
||||||
@ -1821,16 +1822,16 @@ Peer.prototype.sendReject = function sendReject(code, reason, obj) {
|
|||||||
var reject = RejectPacket.fromReason(code, reason, obj);
|
var reject = RejectPacket.fromReason(code, reason, obj);
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
bcoin.debug('Rejecting %s %s (%s): ccode=%s reason=%s.',
|
this.logger.debug('Rejecting %s %s (%s): ccode=%s reason=%s.',
|
||||||
reject.message, obj.rhash, this.hostname, code, reason);
|
reject.message, obj.rhash, this.hostname, code, reason);
|
||||||
|
|
||||||
this.pool.rejects.add(obj.hash());
|
this.pool.rejects.add(obj.hash());
|
||||||
} else {
|
} else {
|
||||||
bcoin.debug('Rejecting packet from %s: ccode=%s reason=%s.',
|
this.logger.debug('Rejecting packet from %s: ccode=%s reason=%s.',
|
||||||
this.hostname, code, reason);
|
this.hostname, code, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Sending reject packet to peer (%s).',
|
'Sending reject packet to peer (%s).',
|
||||||
this.hostname);
|
this.hostname);
|
||||||
|
|
||||||
@ -1894,7 +1895,7 @@ Peer.prototype.resolveOrphan = function resolveOrphan(tip, orphan, callback) {
|
|||||||
|
|
||||||
// Was probably resolved.
|
// Was probably resolved.
|
||||||
if (!root) {
|
if (!root) {
|
||||||
bcoin.debug('Orphan root was already resolved.');
|
self.logger.debug('Orphan root was already resolved.');
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -92,6 +92,7 @@ function Pool(options) {
|
|||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.chain = options.chain;
|
this.chain = options.chain;
|
||||||
|
this.logger = options.logger || this.chain.logger;
|
||||||
this.mempool = options.mempool;
|
this.mempool = options.mempool;
|
||||||
|
|
||||||
assert(this.chain, 'Pool requires a blockchain.');
|
assert(this.chain, 'Pool requires a blockchain.');
|
||||||
@ -106,9 +107,9 @@ function Pool(options) {
|
|||||||
|
|
||||||
seeds = options.seeds || this.network.seeds;
|
seeds = options.seeds || this.network.seeds;
|
||||||
|
|
||||||
if (process.env.BCOIN_SEED) {
|
if (options.preferredSeed) {
|
||||||
seeds = seeds.slice();
|
seeds = seeds.slice();
|
||||||
seeds.unshift(process.env.BCOIN_SEED);
|
seeds.unshift(options.preferredSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.seeds = [];
|
this.seeds = [];
|
||||||
@ -135,6 +136,7 @@ function Pool(options) {
|
|||||||
this.uid = 0;
|
this.uid = 0;
|
||||||
this._createServer = options.createServer;
|
this._createServer = options.createServer;
|
||||||
this.locker = new bcoin.locker(this);
|
this.locker = new bcoin.locker(this);
|
||||||
|
this.proxyServer = options.proxyServer;
|
||||||
|
|
||||||
this.syncing = false;
|
this.syncing = false;
|
||||||
this.synced = false;
|
this.synced = false;
|
||||||
@ -273,7 +275,7 @@ Pool.prototype._init = function _init() {
|
|||||||
self.synced = true;
|
self.synced = true;
|
||||||
self.emit('full');
|
self.emit('full');
|
||||||
|
|
||||||
bcoin.debug('Chain is fully synced (height=%d).', self.chain.height);
|
self.logger.info('Chain is fully synced (height=%d).', self.chain.height);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -287,11 +289,11 @@ Pool.prototype._open = function open(callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
this.getIP(function(err, ip) {
|
this.getIP(function(err, ip) {
|
||||||
if (err)
|
if (err)
|
||||||
bcoin.error(err);
|
self.logger.error(err);
|
||||||
|
|
||||||
if (ip) {
|
if (ip) {
|
||||||
self.address.host = ip;
|
self.address.host = ip;
|
||||||
bcoin.debug('External IP found: %s.', ip);
|
self.logger.info('External IP found: %s.', ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.mempool)
|
if (self.mempool)
|
||||||
@ -404,7 +406,7 @@ Pool.prototype.listen = function listen(callback) {
|
|||||||
if (this._createServer) {
|
if (this._createServer) {
|
||||||
this.server = this._createServer();
|
this.server = this._createServer();
|
||||||
} else {
|
} else {
|
||||||
if (bcoin.isBrowser)
|
if (utils.isBrowser)
|
||||||
return utils.nextTick(callback);
|
return utils.nextTick(callback);
|
||||||
net = require('n' + 'et');
|
net = require('n' + 'et');
|
||||||
this.server = new net.Server();
|
this.server = new net.Server();
|
||||||
@ -414,7 +416,7 @@ Pool.prototype.listen = function listen(callback) {
|
|||||||
var hostname, host;
|
var hostname, host;
|
||||||
|
|
||||||
if (!socket.remoteAddress) {
|
if (!socket.remoteAddress) {
|
||||||
bcoin.debug('Ignoring disconnected leech.');
|
self.logger.debug('Ignoring disconnected leech.');
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -423,14 +425,14 @@ Pool.prototype.listen = function listen(callback) {
|
|||||||
|
|
||||||
if (self.peers.leeches.length >= self.maxLeeches) {
|
if (self.peers.leeches.length >= self.maxLeeches) {
|
||||||
hostname = IP.hostname(host, socket.remotePort);
|
hostname = IP.hostname(host, socket.remotePort);
|
||||||
bcoin.debug('Ignoring leech: too many leeches (%s).', hostname);
|
self.logger.debug('Ignoring leech: too many leeches (%s).', hostname);
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.isMisbehaving(host)) {
|
if (self.isMisbehaving(host)) {
|
||||||
hostname = IP.hostname(host, socket.remotePort);
|
hostname = IP.hostname(host, socket.remotePort);
|
||||||
bcoin.debug('Ignoring misbehaving leech (%s).', hostname);
|
self.logger.debug('Ignoring misbehaving leech (%s).', hostname);
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -440,7 +442,7 @@ Pool.prototype.listen = function listen(callback) {
|
|||||||
|
|
||||||
this.server.on('listening', function() {
|
this.server.on('listening', function() {
|
||||||
var data = self.server.address();
|
var data = self.server.address();
|
||||||
bcoin.debug(
|
self.logger.info(
|
||||||
'Bitcoin server listening on %s (port=%d).',
|
'Bitcoin server listening on %s (port=%d).',
|
||||||
data.address, data.port);
|
data.address, data.port);
|
||||||
});
|
});
|
||||||
@ -456,7 +458,7 @@ Pool.prototype.listen = function listen(callback) {
|
|||||||
Pool.prototype.unlisten = function unlisten(callback) {
|
Pool.prototype.unlisten = function unlisten(callback) {
|
||||||
callback = utils.ensure(callback);
|
callback = utils.ensure(callback);
|
||||||
|
|
||||||
if (bcoin.isBrowser)
|
if (utils.isBrowser)
|
||||||
return utils.nextTick(callback);
|
return utils.nextTick(callback);
|
||||||
|
|
||||||
if (!this.server)
|
if (!this.server)
|
||||||
@ -486,7 +488,7 @@ Pool.prototype._startTimer = function _startTimer() {
|
|||||||
|
|
||||||
if (self.peers.load) {
|
if (self.peers.load) {
|
||||||
self.peers.load.destroy();
|
self.peers.load.destroy();
|
||||||
bcoin.debug('Timer ran out. Finding new loader peer.');
|
self.logger.debug('Timer ran out. Finding new loader peer.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,7 +532,7 @@ Pool.prototype._startInterval = function _startInterval() {
|
|||||||
if (self.chain.isBusy())
|
if (self.chain.isBusy())
|
||||||
return self._startTimer();
|
return self._startTimer();
|
||||||
|
|
||||||
bcoin.debug('Warning: Stalling.');
|
self.logger.warning('Stalling.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._interval = setInterval(load, this.load.interval);
|
this._interval = setInterval(load, this.load.interval);
|
||||||
@ -573,7 +575,7 @@ Pool.prototype._addLoader = function _addLoader() {
|
|||||||
witness: this.options.witness
|
witness: this.options.witness
|
||||||
});
|
});
|
||||||
|
|
||||||
bcoin.debug('Added loader peer (%s).', peer.hostname);
|
this.logger.info('Added loader peer (%s).', peer.hostname);
|
||||||
|
|
||||||
this.peers.load = peer;
|
this.peers.load = peer;
|
||||||
this.peers.all.push(peer);
|
this.peers.all.push(peer);
|
||||||
@ -666,7 +668,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer, callback)
|
|||||||
if (!this.options.headers)
|
if (!this.options.headers)
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Received %s headers from peer (%s).',
|
'Received %s headers from peer (%s).',
|
||||||
headers.length,
|
headers.length,
|
||||||
peer.hostname);
|
peer.hostname);
|
||||||
@ -734,7 +736,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
|
|||||||
|
|
||||||
assert(!this.options.headers);
|
assert(!this.options.headers);
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Received %s block hashes from peer (%s).',
|
'Received %s block hashes from peer (%s).',
|
||||||
hashes.length,
|
hashes.length,
|
||||||
peer.hostname);
|
peer.hostname);
|
||||||
@ -757,7 +759,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
|
|||||||
// should probably actually move to the
|
// should probably actually move to the
|
||||||
// `exists` clause below if it is the last
|
// `exists` clause below if it is the last
|
||||||
// hash.
|
// hash.
|
||||||
bcoin.debug('Received known orphan hash (%s).', peer.hostname);
|
self.logger.debug('Received known orphan hash (%s).', peer.hostname);
|
||||||
return peer.resolveOrphan(null, hash, next);
|
return peer.resolveOrphan(null, hash, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,11 +777,11 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
|
|||||||
if (exists && i === hashes.length - 1) {
|
if (exists && i === hashes.length - 1) {
|
||||||
// Make sure we _actually_ have this block.
|
// Make sure we _actually_ have this block.
|
||||||
if (!self.request.map[hash]) {
|
if (!self.request.map[hash]) {
|
||||||
bcoin.debug('Received existing hash (%s).', peer.hostname);
|
self.logger.debug('Received existing hash (%s).', peer.hostname);
|
||||||
return peer.getBlocks(hash, null, next);
|
return peer.getBlocks(hash, null, next);
|
||||||
}
|
}
|
||||||
// Otherwise, we're still requesting it. Ignore.
|
// Otherwise, we're still requesting it. Ignore.
|
||||||
bcoin.debug('Received already-requested hash (%s).', peer.hostname);
|
self.logger.debug('Received requested hash (%s).', peer.hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
@ -850,7 +852,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
// us requesting them.
|
// us requesting them.
|
||||||
if (!requested) {
|
if (!requested) {
|
||||||
peer.invFilter.add(block.hash());
|
peer.invFilter.add(block.hash());
|
||||||
bcoin.debug(
|
this.logger.warning(
|
||||||
'Received unrequested block: %s (%s).',
|
'Received unrequested block: %s (%s).',
|
||||||
block.rhash, peer.hostname);
|
block.rhash, peer.hostname);
|
||||||
return utils.nextTick(callback);
|
return utils.nextTick(callback);
|
||||||
@ -871,7 +873,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
peer.setMisbehavior(10);
|
peer.setMisbehavior(10);
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
bcoin.debug('Peer sent an orphan block. Resolving.');
|
self.logger.debug('Peer sent an orphan block. Resolving.');
|
||||||
return peer.resolveOrphan(null, block.hash('hex'), function(e) {
|
return peer.resolveOrphan(null, block.hash('hex'), function(e) {
|
||||||
self.scheduleRequests(peer);
|
self.scheduleRequests(peer);
|
||||||
return callback(e || err);
|
return callback(e || err);
|
||||||
@ -887,8 +889,8 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
|
|
||||||
self.emit('chain-progress', self.chain.getProgress(), peer);
|
self.emit('chain-progress', self.chain.getProgress(), peer);
|
||||||
|
|
||||||
if (self.chain.total % 20 === 0) {
|
if (self.logger.level === 4 && self.chain.total % 20 === 0) {
|
||||||
bcoin.debug('Status:'
|
self.logger.debug('Status:'
|
||||||
+ ' tip=%s ts=%s height=%d progress=%s'
|
+ ' tip=%s ts=%s height=%d progress=%s'
|
||||||
+ ' blocks=%d orphans=%d active=%d'
|
+ ' blocks=%d orphans=%d active=%d'
|
||||||
+ ' queue=%d target=%s peers=%d'
|
+ ' queue=%d target=%s peers=%d'
|
||||||
@ -908,6 +910,13 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
self.chain.locker.jobs.length);
|
self.chain.locker.jobs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self.chain.total % 2000 === 0) {
|
||||||
|
self.logger.info(
|
||||||
|
'Received 2000 more blocks (height=%d, hash=%s).',
|
||||||
|
self.chain.height,
|
||||||
|
block.rhash);
|
||||||
|
}
|
||||||
|
|
||||||
return callback();
|
return callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -979,7 +988,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
|||||||
self._stopTimer();
|
self._stopTimer();
|
||||||
|
|
||||||
if (self.peers.regular.length === 0) {
|
if (self.peers.regular.length === 0) {
|
||||||
bcoin.debug('%s %s %s',
|
self.logger.warning('%s %s %s',
|
||||||
'Could not connect to any peers.',
|
'Could not connect to any peers.',
|
||||||
'Do you have a network connection?',
|
'Do you have a network connection?',
|
||||||
'Retrying in 5 seconds.');
|
'Retrying in 5 seconds.');
|
||||||
@ -1041,7 +1050,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
|||||||
? utils.revHex(payload.data)
|
? utils.revHex(payload.data)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
bcoin.debug(
|
self.logger.warning(
|
||||||
'Received reject (%s): msg=%s code=%s reason=%s data=%s.',
|
'Received reject (%s): msg=%s code=%s reason=%s data=%s.',
|
||||||
peer.hostname,
|
peer.hostname,
|
||||||
payload.message,
|
payload.message,
|
||||||
@ -1121,7 +1130,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
|||||||
if (version.height > self.block.versionHeight)
|
if (version.height > self.block.versionHeight)
|
||||||
self.block.versionHeight = version.height;
|
self.block.versionHeight = version.height;
|
||||||
|
|
||||||
bcoin.debug(
|
self.logger.info(
|
||||||
'Received version (%s): version=%d height=%d services=%s agent=%s',
|
'Received version (%s): version=%d height=%d services=%s agent=%s',
|
||||||
peer.hostname,
|
peer.hostname,
|
||||||
version.version,
|
version.version,
|
||||||
@ -1171,21 +1180,21 @@ Pool.prototype._handleAlert = function _handleAlert(alert, peer) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!alert.verify(this.network.alertKey)) {
|
if (!alert.verify(this.network.alertKey)) {
|
||||||
bcoin.debug('Peer sent a phony alert packet (%s).', peer.hostname);
|
this.logger.warning('Peer sent a phony alert packet (%s).', peer.hostname);
|
||||||
// Let's look at it because why not?
|
// Let's look at it because why not?
|
||||||
bcoin.debug(alert);
|
this.logger.debug(alert);
|
||||||
peer.setMisbehavior(100);
|
peer.setMisbehavior(100);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (now >= alert.relayUntil || now >= alert.expiration) {
|
if (now >= alert.relayUntil || now >= alert.expiration) {
|
||||||
bcoin.debug('Peer sent an expired alert packet (%s).', peer.hostname);
|
this.logger.warning('Peer sent an expired alert packet (%s).', peer.hostname);
|
||||||
bcoin.debug(alert);
|
this.logger.debug(alert);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.debug('Received alert from peer (%s).', peer.hostname);
|
this.logger.warning('Received alert from peer (%s).', peer.hostname);
|
||||||
bcoin.debug(alert);
|
this.logger.warning(alert);
|
||||||
|
|
||||||
this.sendAlert(alert);
|
this.sendAlert(alert);
|
||||||
|
|
||||||
@ -1215,7 +1224,7 @@ Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
|
|||||||
if (!this.mempool)
|
if (!this.mempool)
|
||||||
this.tx.filter.add(tx.hash());
|
this.tx.filter.add(tx.hash());
|
||||||
|
|
||||||
bcoin.debug('Peer sent unrequested tx: %s (%s).',
|
this.logger.warning('Peer sent unrequested tx: %s (%s).',
|
||||||
tx.rhash, peer.hostname);
|
tx.rhash, peer.hostname);
|
||||||
|
|
||||||
if (this.rejects.test(tx.hash())) {
|
if (this.rejects.test(tx.hash())) {
|
||||||
@ -1268,7 +1277,7 @@ Pool.prototype._addLeech = function _addLeech(socket) {
|
|||||||
witness: false
|
witness: false
|
||||||
});
|
});
|
||||||
|
|
||||||
bcoin.debug('Added leech peer (%s).', peer.hostname);
|
this.logger.info('Added leech peer (%s).', peer.hostname);
|
||||||
|
|
||||||
this.peers.leeches.push(peer);
|
this.peers.leeches.push(peer);
|
||||||
this.peers.all.push(peer);
|
this.peers.all.push(peer);
|
||||||
@ -1340,7 +1349,7 @@ Pool.prototype._removePeer = function _removePeer(peer) {
|
|||||||
delete this.peers.map[peer.host];
|
delete this.peers.map[peer.host];
|
||||||
|
|
||||||
if (this.peers.load === peer) {
|
if (this.peers.load === peer) {
|
||||||
bcoin.debug('Removed loader peer (%s).', peer.hostname);
|
this.logger.info('Removed loader peer (%s).', peer.hostname);
|
||||||
this.peers.load = null;
|
this.peers.load = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1454,7 +1463,7 @@ Pool.prototype.getData = function getData(peer, type, hash, options, callback) {
|
|||||||
if (type === self.tx.type) {
|
if (type === self.tx.type) {
|
||||||
if (peer.queue.tx.length === 0) {
|
if (peer.queue.tx.length === 0) {
|
||||||
utils.nextTick(function() {
|
utils.nextTick(function() {
|
||||||
bcoin.debug(
|
self.logger.debug(
|
||||||
'Requesting %d/%d txs from peer with getdata (%s).',
|
'Requesting %d/%d txs from peer with getdata (%s).',
|
||||||
peer.queue.tx.length,
|
peer.queue.tx.length,
|
||||||
self.request.activeTX,
|
self.request.activeTX,
|
||||||
@ -1510,7 +1519,7 @@ Pool.prototype.has = function has(type, hash, force, callback) {
|
|||||||
} else {
|
} else {
|
||||||
// If we recently rejected this item. Ignore.
|
// If we recently rejected this item. Ignore.
|
||||||
if (self.rejects.test(hash, 'hex')) {
|
if (self.rejects.test(hash, 'hex')) {
|
||||||
bcoin.debug('Peer sent a known reject: %s.', hash);
|
self.logger.debug('Peer sent a known reject: %s.', hash);
|
||||||
return callback(null, true);
|
return callback(null, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1603,7 +1612,7 @@ Pool.prototype._sendRequests = function _sendRequests(peer) {
|
|||||||
for (i = 0; i < items.length; i++)
|
for (i = 0; i < items.length; i++)
|
||||||
items[i] = items[i].start();
|
items[i] = items[i].start();
|
||||||
|
|
||||||
bcoin.debug(
|
this.logger.debug(
|
||||||
'Requesting %d/%d blocks from peer with getdata (%s).',
|
'Requesting %d/%d blocks from peer with getdata (%s).',
|
||||||
items.length,
|
items.length,
|
||||||
this.request.activeBlocks,
|
this.request.activeBlocks,
|
||||||
@ -1771,7 +1780,7 @@ Pool.prototype.fillCoins = function fillCoins(tx, callback) {
|
|||||||
Pool.prototype.getLoaderHost = function getLoaderHost() {
|
Pool.prototype.getLoaderHost = function getLoaderHost() {
|
||||||
var host;
|
var host;
|
||||||
|
|
||||||
if (!this.connected && process.env.BCOIN_SEED)
|
if (!this.connected && this.options.preferredSeed)
|
||||||
return this.seeds[0];
|
return this.seeds[0];
|
||||||
|
|
||||||
host = this.getRandom(this.seeds);
|
host = this.getRandom(this.seeds);
|
||||||
@ -1892,7 +1901,7 @@ Pool.prototype.setMisbehavior = function setMisbehavior(peer, score) {
|
|||||||
if (peer.banScore >= constants.BAN_SCORE) {
|
if (peer.banScore >= constants.BAN_SCORE) {
|
||||||
this.peers.misbehaving[peer.host] = utils.now();
|
this.peers.misbehaving[peer.host] = utils.now();
|
||||||
this.removeHost(peer.host);
|
this.removeHost(peer.host);
|
||||||
bcoin.debug('Ban threshold exceeded (%s).', peer.host);
|
this.logger.debug('Ban threshold exceeded (%s).', peer.host);
|
||||||
peer.destroy();
|
peer.destroy();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2035,7 +2044,8 @@ LoadRequest.prototype.destroy = function destroy() {
|
|||||||
LoadRequest.prototype._onTimeout = function _onTimeout() {
|
LoadRequest.prototype._onTimeout = function _onTimeout() {
|
||||||
if (this.type === this.pool.block.type
|
if (this.type === this.pool.block.type
|
||||||
&& this.peer === this.pool.peers.load) {
|
&& this.peer === this.pool.peers.load) {
|
||||||
bcoin.debug('Loader took too long serving a block. Finding a new one.');
|
this.pool.logger.debug(
|
||||||
|
'Loader took too long serving a block. Finding a new one.');
|
||||||
this.peer.destroy();
|
this.peer.destroy();
|
||||||
}
|
}
|
||||||
return this.finish(new Error('Timed out.'));
|
return this.finish(new Error('Timed out.'));
|
||||||
@ -2273,7 +2283,7 @@ BroadcastItem.prototype.send = function send(peer, witness) {
|
|||||||
// They are an insta-ban from any bitcoind node.
|
// They are an insta-ban from any bitcoind node.
|
||||||
if (this.msg.isCoinbase()) {
|
if (this.msg.isCoinbase()) {
|
||||||
peer.write(peer.framer.notFound([this.toInv()]));
|
peer.write(peer.framer.notFound([this.toInv()]));
|
||||||
bcoin.debug('Failsafe: tried to relay a coinbase.');
|
this.pool.logger.warning('Failsafe: tried to relay a coinbase.');
|
||||||
this.finish(new Error('Coinbase.'));
|
this.finish(new Error('Coinbase.'));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,6 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
|
||||||
* @exports profiler
|
|
||||||
*/
|
|
||||||
|
|
||||||
var profiler = exports;
|
|
||||||
|
|
||||||
var bcoin = require('./env');
|
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var fs, v8profiler;
|
var fs, v8profiler;
|
||||||
@ -22,7 +15,7 @@ function ensure() {
|
|||||||
if (v8profiler)
|
if (v8profiler)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (bcoin.profile && !bcoin.isBrowser) {
|
if (!utils.isBrowser) {
|
||||||
v8profiler = require('v8-' + 'profiler');
|
v8profiler = require('v8-' + 'profiler');
|
||||||
fs = require('f' + 's');
|
fs = require('f' + 's');
|
||||||
}
|
}
|
||||||
@ -32,14 +25,15 @@ function ensure() {
|
|||||||
* A CPU profile.
|
* A CPU profile.
|
||||||
* @exports Profile
|
* @exports Profile
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @param {String} prefix
|
||||||
* @param {String?} name
|
* @param {String?} name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Profile(name) {
|
function Profile(prefix, name) {
|
||||||
if (v8profiler && bcoin.profile) {
|
if (v8profiler) {
|
||||||
name = 'profile-' + (name ? name + '-' : '') + utils.ms();
|
name = 'profile-' + (name ? name + '-' : '') + utils.ms();
|
||||||
bcoin.debug('Starting CPU profile: %s', name);
|
|
||||||
v8profiler.startProfiling(name, true);
|
v8profiler.startProfiling(name, true);
|
||||||
|
this.prefix = prefix;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.profile = null;
|
this.profile = null;
|
||||||
this.finished = false;
|
this.finished = false;
|
||||||
@ -93,8 +87,6 @@ Profile.prototype.save = function save(callback) {
|
|||||||
if (!this.profile)
|
if (!this.profile)
|
||||||
this.stop();
|
this.stop();
|
||||||
|
|
||||||
bcoin.debug('Saving CPU profile: %s', this.name);
|
|
||||||
|
|
||||||
return this.profile['export'](function(err, result) {
|
return this.profile['export'](function(err, result) {
|
||||||
var file;
|
var file;
|
||||||
|
|
||||||
@ -105,12 +97,12 @@ Profile.prototype.save = function save(callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
file = bcoin.prefix
|
file = self.prefix
|
||||||
+ '/profiler/'
|
+ '/profiler/'
|
||||||
+ self.name
|
+ self.name
|
||||||
+ '.cpuprofile';
|
+ '.cpuprofile';
|
||||||
|
|
||||||
bcoin.mkdir(file, true);
|
utils.mkdir(file, true);
|
||||||
|
|
||||||
fs.writeFile(file, result, callback);
|
fs.writeFile(file, result, callback);
|
||||||
});
|
});
|
||||||
@ -120,14 +112,15 @@ Profile.prototype.save = function save(callback) {
|
|||||||
* Memory Snapshot
|
* Memory Snapshot
|
||||||
* @exports Snapshot
|
* @exports Snapshot
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @param {String} prefix
|
||||||
* @param {String?} name
|
* @param {String?} name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Snapshot(name) {
|
function Snapshot(prefix, name) {
|
||||||
if (v8profiler && bcoin.profile) {
|
if (v8profiler) {
|
||||||
name = 'snapshot-' + (name ? name + '-' : '') + utils.ms();
|
name = 'snapshot-' + (name ? name + '-' : '') + utils.ms();
|
||||||
bcoin.debug('Taking heap snapshot: %s', name);
|
|
||||||
this.snapshot = v8profiler.takeSnapshot(name);
|
this.snapshot = v8profiler.takeSnapshot(name);
|
||||||
|
this.prefix = prefix;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,8 +182,6 @@ Snapshot.prototype.save = function save(callback) {
|
|||||||
|
|
||||||
assert(this.snapshot);
|
assert(this.snapshot);
|
||||||
|
|
||||||
bcoin.debug('Saving heap snapshot: %s', this.name);
|
|
||||||
|
|
||||||
return this.snapshot['export'](function(err, result) {
|
return this.snapshot['export'](function(err, result) {
|
||||||
var file;
|
var file;
|
||||||
|
|
||||||
@ -200,26 +191,40 @@ Snapshot.prototype.save = function save(callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
file = bcoin.prefix
|
file = self.prefix
|
||||||
+ '/profiler/'
|
+ '/profiler/'
|
||||||
+ self.name
|
+ self.name
|
||||||
+ '.heapsnapshot';
|
+ '.heapsnapshot';
|
||||||
|
|
||||||
bcoin.mkdir(file, true);
|
utils.mkdir(file, true);
|
||||||
|
|
||||||
fs.writeFile(file, result, callback);
|
fs.writeFile(file, result, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profiler
|
||||||
|
* @exports Profiler
|
||||||
|
* @constructor
|
||||||
|
* @param {String} prefix
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Profiler(prefix) {
|
||||||
|
if (!(this instanceof Profiler))
|
||||||
|
return new Profiler(prefix);
|
||||||
|
|
||||||
|
this.prefix = prefix || utils.HOME;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new CPU profile and begin profiling.
|
* Create a new CPU profile and begin profiling.
|
||||||
* @param {String?} name
|
* @param {String?} name
|
||||||
* @returns {Profile}
|
* @returns {Profile}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
profiler.startProfiling = function startProfiling(name) {
|
Profiler.prototype.startProfiling = function startProfiling(name) {
|
||||||
ensure();
|
ensure();
|
||||||
return new Profile(name);
|
return new Profile(this.prefix, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,9 +233,9 @@ profiler.startProfiling = function startProfiling(name) {
|
|||||||
* @returns {Snapshot}
|
* @returns {Snapshot}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
profiler.takeSnapshot = function takeSnapshot(name) {
|
Profiler.prototype.takeSnapshot = function takeSnapshot(name) {
|
||||||
ensure();
|
ensure();
|
||||||
return new Snapshot(name);
|
return new Snapshot(this.prefix, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -239,28 +244,25 @@ profiler.takeSnapshot = function takeSnapshot(name) {
|
|||||||
* @param {Function?} callback
|
* @param {Function?} callback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
profiler.snapshot = function snapshot(name, callback) {
|
Profiler.prototype.snapshot = function snapshot(name, callback) {
|
||||||
var snapshot, mem;
|
|
||||||
|
|
||||||
ensure();
|
|
||||||
|
|
||||||
if (typeof name === 'function') {
|
if (typeof name === 'function') {
|
||||||
callback = name;
|
callback = name;
|
||||||
name = null;
|
name = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.debugLogs && process.memoryUsage) {
|
ensure();
|
||||||
mem = process.memoryUsage();
|
|
||||||
bcoin.debug('Memory: rss=%dmb, js-heap=%d/%dmb native-heap=%dmb',
|
if (!v8profiler) {
|
||||||
utils.mb(mem.rss),
|
if (!callback)
|
||||||
utils.mb(mem.heapUsed),
|
return;
|
||||||
utils.mb(mem.heapTotal),
|
return utils.nextTick(callback);
|
||||||
utils.mb(mem.rss - mem.heapTotal));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!v8profiler || !bcoin.profile)
|
this.takeSnapshot(name).save(callback);
|
||||||
return utils.asyncify(callback)();
|
|
||||||
|
|
||||||
snapshot = new Snapshot(name);
|
|
||||||
snapshot.save(callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = Profiler;
|
||||||
|
|||||||
@ -344,7 +344,6 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) {
|
|||||||
case 'feefilter':
|
case 'feefilter':
|
||||||
return Parser.parseFeeFilter(p);
|
return Parser.parseFeeFilter(p);
|
||||||
default:
|
default:
|
||||||
bcoin.debug('Unknown packet: %s', cmd);
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4425,6 +4425,9 @@ Script.checksig = function checksig(msg, sig, key, flags) {
|
|||||||
if (!(flags & constants.flags.VERIFY_LOW_S))
|
if (!(flags & constants.flags.VERIFY_LOW_S))
|
||||||
high = true;
|
high = true;
|
||||||
|
|
||||||
|
if (bcoin.sigcache)
|
||||||
|
return bcoin.sigcache.verify(msg, sig.slice(0, -1), key, historical, high);
|
||||||
|
|
||||||
return bcoin.ec.verify(msg, sig.slice(0, -1), key, historical, high);
|
return bcoin.ec.verify(msg, sig.slice(0, -1), key, historical, high);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -50,7 +50,7 @@ SigCache.prototype.add = function add(hash, sig, key) {
|
|||||||
|
|
||||||
this.valid[hash] = new SigCacheEntry(sig, key);
|
this.valid[hash] = new SigCacheEntry(sig, key);
|
||||||
|
|
||||||
if (this.keys.length === this.size) {
|
if (this.keys.length >= this.size) {
|
||||||
i = Math.floor(Math.random() * this.keys.length);
|
i = Math.floor(Math.random() * this.keys.length);
|
||||||
k = this.keys[i];
|
k = this.keys[i];
|
||||||
delete this.valid[k];
|
delete this.valid[k];
|
||||||
|
|||||||
@ -42,6 +42,10 @@ function SPVNode(options) {
|
|||||||
|
|
||||||
this.chain = new bcoin.chain({
|
this.chain = new bcoin.chain({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
|
profiler: this.profiler,
|
||||||
|
db: this.db,
|
||||||
|
location: this.location('spvchain'),
|
||||||
preload: this.options.preload,
|
preload: this.options.preload,
|
||||||
useCheckpoints: this.options.useCheckpoints,
|
useCheckpoints: this.options.useCheckpoints,
|
||||||
spv: true
|
spv: true
|
||||||
@ -49,8 +53,11 @@ function SPVNode(options) {
|
|||||||
|
|
||||||
this.pool = new bcoin.pool({
|
this.pool = new bcoin.pool({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
chain: this.chain,
|
chain: this.chain,
|
||||||
witness: this.network.witness,
|
witness: this.network.witness,
|
||||||
|
proxyServer: this.options.proxyServer,
|
||||||
|
preferredSeed: this.options.preferredSeed,
|
||||||
selfish: true,
|
selfish: true,
|
||||||
listen: false,
|
listen: false,
|
||||||
spv: true
|
spv: true
|
||||||
@ -58,12 +65,16 @@ function SPVNode(options) {
|
|||||||
|
|
||||||
this.walletdb = new bcoin.walletdb({
|
this.walletdb = new bcoin.walletdb({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
|
db: this.db,
|
||||||
|
location: this.location('walletdb'),
|
||||||
verify: true
|
verify: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!utils.isBrowser) {
|
if (!utils.isBrowser) {
|
||||||
this.http = new bcoin.http.server({
|
this.http = new bcoin.http.server({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
node: this,
|
node: this,
|
||||||
key: this.options.sslKey,
|
key: this.options.sslKey,
|
||||||
cert: this.options.sslCert,
|
cert: this.options.sslCert,
|
||||||
@ -87,20 +98,20 @@ SPVNode.prototype._init = function _init() {
|
|||||||
|
|
||||||
// Bind to errors
|
// Bind to errors
|
||||||
this.pool.on('error', function(err) {
|
this.pool.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chain.on('error', function(err) {
|
this.chain.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.walletdb.on('error', function(err) {
|
this.walletdb.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.http) {
|
if (this.http) {
|
||||||
this.http.on('error', function(err) {
|
this.http.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +122,7 @@ SPVNode.prototype._init = function _init() {
|
|||||||
this.on('tx', function(tx) {
|
this.on('tx', function(tx) {
|
||||||
self.walletdb.addTX(tx, function(err) {
|
self.walletdb.addTX(tx, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -130,7 +141,7 @@ SPVNode.prototype._init = function _init() {
|
|||||||
this.chain.on('remove entry', function(entry) {
|
this.chain.on('remove entry', function(entry) {
|
||||||
self.walletdb.removeBlockSPV(entry, function(err) {
|
self.walletdb.removeBlockSPV(entry, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -154,7 +165,7 @@ SPVNode.prototype._open = function open(callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
bcoin.debug('Node is loaded.');
|
self.logger.info('Node is loaded.');
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -190,7 +201,7 @@ SPVNode.prototype._open = function open(callback) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
|
|
||||||
if (hashes.length > 0)
|
if (hashes.length > 0)
|
||||||
bcoin.debug('Adding %d addresses to filter.', hashes.length);
|
self.logger.info('Adding %d addresses to filter.', hashes.length);
|
||||||
|
|
||||||
for (i = 0; i < hashes.length; i++)
|
for (i = 0; i < hashes.length; i++)
|
||||||
self.pool.watch(hashes[i], 'hex');
|
self.pool.watch(hashes[i], 'hex');
|
||||||
@ -205,7 +216,7 @@ SPVNode.prototype._open = function open(callback) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
|
|
||||||
if (txs.length > 0)
|
if (txs.length > 0)
|
||||||
bcoin.debug('Rebroadcasting %d transactions.', txs.length);
|
self.logger.info('Rebroadcasting %d transactions.', txs.length);
|
||||||
|
|
||||||
for (i = 0; i < txs.length; i++)
|
for (i = 0; i < txs.length; i++)
|
||||||
self.pool.broadcast(txs[i]);
|
self.pool.broadcast(txs[i]);
|
||||||
@ -226,7 +237,7 @@ SPVNode.prototype._open = function open(callback) {
|
|||||||
if (height === -1)
|
if (height === -1)
|
||||||
return next();
|
return next();
|
||||||
|
|
||||||
bcoin.debug('Rewinding chain to height %s.', height);
|
self.logger.info('Rewinding chain to height %s.', height);
|
||||||
|
|
||||||
self.chain.reset(height, next);
|
self.chain.reset(height, next);
|
||||||
});
|
});
|
||||||
@ -327,13 +338,14 @@ SPVNode.prototype.stopSync = function stopSync() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
SPVNode.prototype.createWallet = function createWallet(options, callback) {
|
SPVNode.prototype.createWallet = function createWallet(options, callback) {
|
||||||
|
var self = this;
|
||||||
this.walletdb.ensure(options, function(err, wallet) {
|
this.walletdb.ensure(options, function(err, wallet) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
assert(wallet);
|
assert(wallet);
|
||||||
|
|
||||||
bcoin.debug('Loaded wallet with id=%s address=%s',
|
self.logger.info('Loaded wallet with id=%s address=%s',
|
||||||
wallet.id, wallet.getAddress());
|
wallet.id, wallet.getAddress());
|
||||||
|
|
||||||
return callback(null, wallet);
|
return callback(null, wallet);
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bcoin = require('./env');
|
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object which handles "adjusted time". This may not
|
* An object which handles "adjusted time". This may not
|
||||||
@ -28,6 +28,8 @@ function TimeData(limit) {
|
|||||||
if (!(this instanceof TimeData))
|
if (!(this instanceof TimeData))
|
||||||
return new TimeData(limit);
|
return new TimeData(limit);
|
||||||
|
|
||||||
|
EventEmitter.call(this);
|
||||||
|
|
||||||
if (limit == null)
|
if (limit == null)
|
||||||
limit = 200;
|
limit = 200;
|
||||||
|
|
||||||
@ -38,6 +40,8 @@ function TimeData(limit) {
|
|||||||
this._checked = false;
|
this._checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils.inherits(TimeData, EventEmitter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add time data.
|
* Add time data.
|
||||||
* @param {String} host
|
* @param {String} host
|
||||||
@ -58,8 +62,7 @@ TimeData.prototype.add = function add(host, time) {
|
|||||||
|
|
||||||
utils.binaryInsert(this.samples, sample, compare);
|
utils.binaryInsert(this.samples, sample, compare);
|
||||||
|
|
||||||
bcoin.debug('Added time data: samples=%d, offset=%d (%d minutes)',
|
this.emit('sample', sample, this.samples.length);
|
||||||
this.samples.length, sample, sample / 60 | 0);
|
|
||||||
|
|
||||||
if (this.samples.length >= 5 && this.samples.length % 2 === 1) {
|
if (this.samples.length >= 5 && this.samples.length % 2 === 1) {
|
||||||
median = this.samples[this.samples / 2 | 0];
|
median = this.samples[this.samples / 2 | 0];
|
||||||
@ -79,13 +82,12 @@ TimeData.prototype.add = function add(host, time) {
|
|||||||
}
|
}
|
||||||
if (!match) {
|
if (!match) {
|
||||||
this._checked = true;
|
this._checked = true;
|
||||||
bcoin.debug('Please make sure your system clock is correct!');
|
this.emit('mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.debug('Time offset: %d (%d minutes)',
|
this.emit('offset', this.offset);
|
||||||
this.offset, this.offset / 60 | 0);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -678,10 +678,8 @@ TX.prototype.verifyInput = function verifyInput(index, flags) {
|
|||||||
|
|
||||||
assert(input, 'Input does not exist.');
|
assert(input, 'Input does not exist.');
|
||||||
|
|
||||||
if (!input.coin) {
|
if (!input.coin)
|
||||||
bcoin.debug('Coin is not available for verification.');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Script.verify(
|
Script.verify(
|
||||||
@ -693,10 +691,8 @@ TX.prototype.verifyInput = function verifyInput(index, flags) {
|
|||||||
flags
|
flags
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.type === 'ScriptError') {
|
if (e.type === 'ScriptError')
|
||||||
bcoin.debug('Script verification error: %s', e.message);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,7 +715,7 @@ TX.prototype.verifyAsync = function verifyAsync(flags, callback) {
|
|||||||
flags = null;
|
flags = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bcoin.workerPool) {
|
if (!bcoin.useWorkers) {
|
||||||
callback = utils.asyncify(callback);
|
callback = utils.asyncify(callback);
|
||||||
try {
|
try {
|
||||||
result = this.verify(flags);
|
result = this.verify(flags);
|
||||||
|
|||||||
@ -57,6 +57,7 @@ function TXDB(db, options) {
|
|||||||
|
|
||||||
this.walletdb = db;
|
this.walletdb = db;
|
||||||
this.db = db.db;
|
this.db = db.db;
|
||||||
|
this.logger = db.logger;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.network = bcoin.network.get(options.network);
|
this.network = bcoin.network.get(options.network);
|
||||||
this.busy = false;
|
this.busy = false;
|
||||||
@ -297,6 +298,12 @@ TXDB.prototype.add = function add(tx, callback, force) {
|
|||||||
if (!map)
|
if (!map)
|
||||||
return callback(null, false);
|
return callback(null, false);
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
'Incoming transaction for %d accounts.',
|
||||||
|
map.outputs.length);
|
||||||
|
|
||||||
|
self.logger.debug(map.outputs);
|
||||||
|
|
||||||
return self._add(tx, map, callback, force);
|
return self._add(tx, map, callback, force);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,7 +19,7 @@ var assert = require('assert');
|
|||||||
var bn = require('bn.js');
|
var bn = require('bn.js');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var Number, Math, Date;
|
var Number, Math, Date;
|
||||||
var crypto, supersha, hash, aes;
|
var fs, crypto, supersha, hash, aes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to the global object.
|
* Reference to the global object.
|
||||||
@ -52,6 +52,7 @@ utils.isBrowser =
|
|||||||
|| typeof window !== 'undefined';
|
|| typeof window !== 'undefined';
|
||||||
|
|
||||||
if (!utils.isBrowser) {
|
if (!utils.isBrowser) {
|
||||||
|
fs = require('f' + 's');
|
||||||
crypto = require('cry' + 'pto');
|
crypto = require('cry' + 'pto');
|
||||||
try {
|
try {
|
||||||
supersha = require('super' + 'sha');
|
supersha = require('super' + 'sha');
|
||||||
@ -968,7 +969,7 @@ utils.format = function format(args, color) {
|
|||||||
* @param {...String} args
|
* @param {...String} args
|
||||||
*/
|
*/
|
||||||
|
|
||||||
utils.print = function print() {
|
utils.log = function log() {
|
||||||
var args = new Array(arguments.length);
|
var args = new Array(arguments.length);
|
||||||
var i, msg;
|
var i, msg;
|
||||||
|
|
||||||
@ -1004,7 +1005,7 @@ utils.error = function error() {
|
|||||||
msg = typeof args[0] !== 'object'
|
msg = typeof args[0] !== 'object'
|
||||||
? utils.format(args, false)
|
? utils.format(args, false)
|
||||||
: args[0];
|
: args[0];
|
||||||
console.log(msg);
|
console.error(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2529,3 +2530,94 @@ utils.binaryRemove = function binaryRemove(items, item, compare) {
|
|||||||
items.splice(i, 1);
|
items.splice(i, 1);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a path.
|
||||||
|
* @param {String} path
|
||||||
|
* @param {Boolean?} dirname
|
||||||
|
*/
|
||||||
|
|
||||||
|
utils.normalize = function normalize(path, dirname) {
|
||||||
|
var parts;
|
||||||
|
|
||||||
|
path = path.replace(/\\/g, '/');
|
||||||
|
path = path.replace(/\/+$/, '');
|
||||||
|
parts = path.split(/\/+/);
|
||||||
|
|
||||||
|
if (dirname)
|
||||||
|
parts.pop();
|
||||||
|
|
||||||
|
return parts.join('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a full directory structure.
|
||||||
|
* @param {String} path
|
||||||
|
*/
|
||||||
|
|
||||||
|
utils.mkdirp = function mkdirp(path) {
|
||||||
|
var i, parts, stat;
|
||||||
|
|
||||||
|
if (!fs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
path = path.replace(/\\/g, '/');
|
||||||
|
path = path.replace(/\/+$/, '');
|
||||||
|
parts = path.split(/\/+/);
|
||||||
|
path = '';
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
if (parts[0].indexOf(':') !== -1)
|
||||||
|
path = parts.shift() + '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[0].length === 0) {
|
||||||
|
parts.shift();
|
||||||
|
path = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < parts.length; i++) {
|
||||||
|
path += parts[i];
|
||||||
|
|
||||||
|
try {
|
||||||
|
stat = fs.statSync(path);
|
||||||
|
if (!stat.isDirectory())
|
||||||
|
throw new Error('Could not create directory.');
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code === 'ENOENT')
|
||||||
|
fs.mkdirSync(path, 488 /* 0750 */);
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
path += '/';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure a directory.
|
||||||
|
* @param {String} path
|
||||||
|
* @param {Boolean?} dirname
|
||||||
|
*/
|
||||||
|
|
||||||
|
utils.mkdir = function mkdir(path, dirname) {
|
||||||
|
if (utils.isBrowser)
|
||||||
|
return;
|
||||||
|
|
||||||
|
path = utils.normalize(path, dirname);
|
||||||
|
|
||||||
|
if (utils._paths[path])
|
||||||
|
return;
|
||||||
|
|
||||||
|
utils._paths[path] = true;
|
||||||
|
|
||||||
|
return utils.mkdirp(path);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached mkdirp paths.
|
||||||
|
* @private
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
utils._paths = {};
|
||||||
|
|||||||
@ -521,6 +521,7 @@ Wallet.prototype.getPath = function getPath(address, callback) {
|
|||||||
|
|
||||||
Wallet.prototype.fill = function fill(tx, options, callback) {
|
Wallet.prototype.fill = function fill(tx, options, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var rate;
|
||||||
|
|
||||||
if (typeof options === 'function') {
|
if (typeof options === 'function') {
|
||||||
callback = options;
|
callback = options;
|
||||||
@ -550,6 +551,15 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
|
rate = options.rate;
|
||||||
|
|
||||||
|
if (rate == null) {
|
||||||
|
if (self.db.fees)
|
||||||
|
rate = self.db.fees.estimateFee();
|
||||||
|
else
|
||||||
|
rate = self.network.getRate();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tx.fill(coins, {
|
tx.fill(coins, {
|
||||||
selection: options.selection || 'age',
|
selection: options.selection || 'age',
|
||||||
@ -560,9 +570,7 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
|||||||
subtractFee: options.subtractFee,
|
subtractFee: options.subtractFee,
|
||||||
changeAddress: account.changeAddress.getAddress(),
|
changeAddress: account.changeAddress.getAddress(),
|
||||||
height: self.network.height,
|
height: self.network.height,
|
||||||
rate: options.rate != null
|
rate: rate,
|
||||||
? options.rate
|
|
||||||
: self.network.fees.estimateFee(),
|
|
||||||
wallet: self,
|
wallet: self,
|
||||||
m: self.m,
|
m: self.m,
|
||||||
n: self.n
|
n: self.n
|
||||||
@ -2521,7 +2529,7 @@ MasterKey.prototype.decrypt = function decrypt(passphrase, callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
var unlock;
|
var unlock;
|
||||||
|
|
||||||
unlock = this.locker.lock(decrypt, [passphrase, callback]);
|
unlock = this.locker.lock(decrypt, [passphrase, callback]);
|
||||||
|
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -48,14 +48,14 @@ function WalletDB(options) {
|
|||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.network = bcoin.network.get(options.network);
|
this.network = bcoin.network.get(options.network);
|
||||||
|
this.fees = options.fees;
|
||||||
|
this.logger = options.logger || bcoin.defaultLogger;
|
||||||
|
|
||||||
// We need one read lock for `get` and `create`.
|
// We need one read lock for `get` and `create`.
|
||||||
// It will hold locks specific to wallet ids.
|
// It will hold locks specific to wallet ids.
|
||||||
this.readLock = new ReadLock(this);
|
this.readLock = new ReadLock(this);
|
||||||
|
|
||||||
this.db = bcoin.ldb({
|
this.db = bcoin.ldb({
|
||||||
network: this.network,
|
|
||||||
name: this.options.name || 'wallet',
|
|
||||||
location: this.options.location,
|
location: this.options.location,
|
||||||
db: this.options.db,
|
db: this.options.db,
|
||||||
cacheSize: 8 << 20,
|
cacheSize: 8 << 20,
|
||||||
@ -523,6 +523,8 @@ WalletDB.prototype.create = function create(options, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
|
self.logger.info('Created wallet %s.', wallet.id);
|
||||||
|
|
||||||
return callback(null, wallet);
|
return callback(null, wallet);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -703,6 +705,11 @@ WalletDB.prototype.createAccount = function createAccount(options, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
|
self.logger.info('Created account %s/%s/%d.',
|
||||||
|
account.id,
|
||||||
|
account.name,
|
||||||
|
account.accountIndex);
|
||||||
|
|
||||||
return callback(null, account);
|
return callback(null, account);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -19,18 +19,12 @@ if (typeof importScripts !== 'undefined') {
|
|||||||
|
|
||||||
env = JSON.parse(event.data);
|
env = JSON.parse(event.data);
|
||||||
|
|
||||||
bcoin.set({ useWorkers: true });
|
bcoin.set(env.BCOIN_WORKER_NETWORK);
|
||||||
bcoin.network.set(env.BCOIN_WORKER_NETWORK);
|
bcoin.workers.listen();
|
||||||
bcoin.workers.listen(+env.BCOIN_WORKER_ID, {
|
|
||||||
debug: +env.BCOIN_WORKER_DEBUG === 1
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
env = process.env;
|
env = process.env;
|
||||||
bcoin = require('./env');
|
bcoin = require('./env');
|
||||||
bcoin.set({ useWorkers: true });
|
bcoin.set(env.BCOIN_WORKER_NETWORK);
|
||||||
bcoin.network.set(env.BCOIN_WORKER_NETWORK);
|
bcoin.workers.listen();
|
||||||
bcoin.workers.listen(+env.BCOIN_WORKER_ID, {
|
|
||||||
debug: +env.BCOIN_WORKER_DEBUG === 1
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ var jobs;
|
|||||||
* @property {Number} size
|
* @property {Number} size
|
||||||
* @property {Number} timeout
|
* @property {Number} timeout
|
||||||
* @property {Object} children
|
* @property {Object} children
|
||||||
* @property {Number} uid
|
* @property {Number} nonce
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Workers(options) {
|
function Workers(options) {
|
||||||
@ -36,11 +36,13 @@ function Workers(options) {
|
|||||||
|
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this.uid = 0;
|
if (!options)
|
||||||
|
options = {};
|
||||||
|
|
||||||
this.size = Math.max(1, options.size || Workers.CORES);
|
this.size = Math.max(1, options.size || Workers.CORES);
|
||||||
this.timeout = options.timeout || 60000;
|
this.timeout = options.timeout || 60000;
|
||||||
this.network = bcoin.network.get(options.network);
|
|
||||||
this.children = [];
|
this.children = [];
|
||||||
|
this.nonce = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.inherits(Workers, EventEmitter);
|
utils.inherits(Workers, EventEmitter);
|
||||||
@ -60,7 +62,9 @@ Workers.CORES = getCores();
|
|||||||
Workers.children = [];
|
Workers.children = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup all workers on exit.
|
* Destroy all workers.
|
||||||
|
* Used for cleaning up workers on exit.
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Workers.cleanup = function cleanup() {
|
Workers.cleanup = function cleanup() {
|
||||||
@ -86,11 +90,13 @@ Workers._bindExit = function _bindExit() {
|
|||||||
|
|
||||||
function onExit(err) {
|
function onExit(err) {
|
||||||
Workers.cleanup();
|
Workers.cleanup();
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err.stack + '');
|
utils.error(err.stack + '');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,55 +130,38 @@ Workers._bindExit = function _bindExit() {
|
|||||||
|
|
||||||
Workers.prototype.spawn = function spawn(id) {
|
Workers.prototype.spawn = function spawn(id) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var i, child;
|
var child;
|
||||||
|
|
||||||
bcoin.debug('Spawning worker process: %d', id);
|
child = new Worker(id);
|
||||||
|
|
||||||
child = new Worker(this, id);
|
|
||||||
|
|
||||||
child.on('error', function(err) {
|
child.on('error', function(err) {
|
||||||
bcoin.debug('Worker %d error: %s', child.id, err.message);
|
self.emit('error', err, child);
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on('exit', function(code) {
|
child.on('exit', function(code) {
|
||||||
bcoin.debug('Worker %d exited: %s', child.id, code);
|
self.emit('exit', code, child);
|
||||||
if (self.children[child.id] === child)
|
if (self.children[child.id] === child)
|
||||||
self.children[child.id] = null;
|
self.children[child.id] = null;
|
||||||
i = Workers.children.indexOf(child);
|
|
||||||
if (i !== -1)
|
|
||||||
Workers.children.splice(i, 1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on('packet', function(job, body) {
|
child.on('event', function(items) {
|
||||||
if (body.name === 'event') {
|
self.emit('event', items, child);
|
||||||
child.emit.apply(child, body.items);
|
self.emit.apply(self, items);
|
||||||
self.emit.apply(self, body.items);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (body.name === 'response') {
|
|
||||||
child.emit('response ' + job, body.items[0], body.items[1]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.emit('error', new Error('Unknown packet: ' + body.name));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Workers.children.push(child);
|
this.emit('spawn', child);
|
||||||
|
|
||||||
Workers._bindExit();
|
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a new worker, will not go above `size` option
|
* Allocate a new worker, will not go above `size` option
|
||||||
* and will automatically load balance the workers based
|
* and will automatically load balance the workers.
|
||||||
* on job ID.
|
|
||||||
* @param {Number} job
|
|
||||||
* @returns {Worker}
|
* @returns {Worker}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Workers.prototype.alloc = function alloc(job) {
|
Workers.prototype.alloc = function alloc() {
|
||||||
var id = job % this.size;
|
var id = this.nonce++ % this.size;
|
||||||
if (!this.children[id])
|
if (!this.children[id])
|
||||||
this.children[id] = this.spawn(id);
|
this.children[id] = this.spawn(id);
|
||||||
return this.children[id];
|
return this.children[id];
|
||||||
@ -228,20 +217,14 @@ Workers.prototype.destroy = function destroy() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Workers.prototype.execute = function execute(method, args, timeout, callback) {
|
Workers.prototype.execute = function execute(method, args, timeout, callback) {
|
||||||
var job = this.uid++;
|
|
||||||
var child;
|
var child;
|
||||||
|
|
||||||
if (job > 0xffffffff) {
|
|
||||||
this.uid = 0;
|
|
||||||
job = this.uid++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
timeout = this.timeout;
|
timeout = this.timeout;
|
||||||
|
|
||||||
child = this.alloc(job);
|
child = this.alloc();
|
||||||
|
|
||||||
child.execute(job, method, args, timeout, callback);
|
child.execute(method, args, timeout, callback);
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
};
|
};
|
||||||
@ -287,33 +270,29 @@ Workers.prototype.scrypt = function scrypt(passwd, salt, N, r, p, len, callback)
|
|||||||
* Represents a worker.
|
* Represents a worker.
|
||||||
* @exports Worker
|
* @exports Worker
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Workers} pool
|
* @param {Number?} id
|
||||||
* @param {Number} id - Worker ID.
|
|
||||||
* @property {Number} id
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Worker(pool, id) {
|
function Worker(id) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var penv, cp;
|
var penv, cp;
|
||||||
|
|
||||||
if (!(this instanceof Worker))
|
if (!(this instanceof Worker))
|
||||||
return new Worker(pool, id);
|
return new Worker();
|
||||||
|
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this.id = id;
|
|
||||||
this.pool = pool;
|
|
||||||
this.framer = new Framer();
|
this.framer = new Framer();
|
||||||
this.parser = new Parser();
|
this.parser = new Parser();
|
||||||
this.setMaxListeners(utils.MAX_SAFE_INTEGER);
|
this.setMaxListeners(utils.MAX_SAFE_INTEGER);
|
||||||
|
this.uid = 0;
|
||||||
|
this.id = id != null ? id : -1;
|
||||||
|
|
||||||
penv = {
|
penv = {
|
||||||
BCOIN_WORKER_ID: id + '',
|
BCOIN_WORKER_NETWORK: bcoin.network.get().type
|
||||||
BCOIN_WORKER_NETWORK: this.pool.network.type,
|
|
||||||
BCOIN_WORKER_DEBUG: (bcoin.debugLogs || bcoin.debugFile) ? '1' : '0'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (bcoin.isBrowser) {
|
if (utils.isBrowser) {
|
||||||
this.child = new global.Worker('/bcoin-worker.js');
|
this.child = new global.Worker('/bcoin-worker.js');
|
||||||
|
|
||||||
this.child.onerror = function onerror(err) {
|
this.child.onerror = function onerror(err) {
|
||||||
@ -368,11 +347,6 @@ function Worker(pool, id) {
|
|||||||
this.child.stdout.on('data', function(data) {
|
this.child.stdout.on('data', function(data) {
|
||||||
self.emit('data', data);
|
self.emit('data', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.child.stderr.setEncoding('utf8');
|
|
||||||
this.child.stderr.on('data', function(data) {
|
|
||||||
bcoin.debug(data.trim());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.on('data', function(data) {
|
this.on('data', function(data) {
|
||||||
@ -386,8 +360,63 @@ function Worker(pool, id) {
|
|||||||
this.parser.on('packet', function(job, body) {
|
this.parser.on('packet', function(job, body) {
|
||||||
self.emit('packet', job, body);
|
self.emit('packet', job, body);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize worker.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Worker.prototype._init = function _init() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.on('worker error', function(err) {
|
||||||
|
self.emit('error', toError(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('exit', function(code) {
|
||||||
|
var i = Workers.children.indexOf(self);
|
||||||
|
if (i !== -1)
|
||||||
|
Workers.children.splice(i, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('log', function(items) {
|
||||||
|
utils.log('Worker %d:', self.id);
|
||||||
|
utils.log.apply(utils, items);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('packet', function(job, body) {
|
||||||
|
var err, result;
|
||||||
|
|
||||||
|
if (body.name === 'event') {
|
||||||
|
self.emit.apply(self, body.items);
|
||||||
|
self.emit('event', body.items);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.name === 'response') {
|
||||||
|
err = body.items[0];
|
||||||
|
result = body.items[1];
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
err = toError(err);
|
||||||
|
|
||||||
|
self.emit('response ' + job, err, result);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = new Error('Unknown packet: ' + body.name);
|
||||||
|
self.emit('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
Workers.children.push(this);
|
||||||
|
|
||||||
|
Workers._bindExit();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send data to worker.
|
* Send data to worker.
|
||||||
* @param {Buffer} data
|
* @param {Buffer} data
|
||||||
@ -395,7 +424,7 @@ function Worker(pool, id) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Worker.prototype.write = function write(data) {
|
Worker.prototype.write = function write(data) {
|
||||||
if (bcoin.isBrowser) {
|
if (utils.isBrowser) {
|
||||||
if (this.child.postMessage.length === 2) {
|
if (this.child.postMessage.length === 2) {
|
||||||
data.__proto__ = Uint8Array.prototype;
|
data.__proto__ = Uint8Array.prototype;
|
||||||
this.child.postMessage({ buf: data }, [data]);
|
this.child.postMessage({ buf: data }, [data]);
|
||||||
@ -458,12 +487,17 @@ Worker.prototype.destroy = function destroy() {
|
|||||||
* the worker method specifies.
|
* the worker method specifies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Worker.prototype.execute = function execute(job, method, args, timeout, callback) {
|
Worker.prototype.execute = function execute(method, args, timeout, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var event = 'response ' + job;
|
var job = this.uid++;
|
||||||
var timer;
|
var event, timer;
|
||||||
|
|
||||||
assert(job <= 0xffffffff);
|
if (job > 0xffffffff) {
|
||||||
|
this.uid = 0;
|
||||||
|
job = this.uid++;
|
||||||
|
}
|
||||||
|
|
||||||
|
event = 'response ' + job;
|
||||||
|
|
||||||
function listener(err, result) {
|
function listener(err, result) {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
@ -499,25 +533,22 @@ utils.inherits(Worker, EventEmitter);
|
|||||||
* Represents the master process.
|
* Represents the master process.
|
||||||
* @exports Master
|
* @exports Master
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Number} id - Worker ID.
|
|
||||||
* @param {Object?} options
|
* @param {Object?} options
|
||||||
* @property {Number} id
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Master(id, options) {
|
function Master(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!(this instanceof Master))
|
if (!(this instanceof Master))
|
||||||
return new Master(id);
|
return new Master();
|
||||||
|
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this.id = id;
|
|
||||||
this.framer = new Framer();
|
this.framer = new Framer();
|
||||||
this.parser = new Parser();
|
this.parser = new Parser();
|
||||||
this.options = options || {};
|
this.options = options || {};
|
||||||
|
|
||||||
if (bcoin.isBrowser) {
|
if (utils.isBrowser) {
|
||||||
global.onerror = function onerror(err) {
|
global.onerror = function onerror(err) {
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
};
|
};
|
||||||
@ -563,7 +594,7 @@ utils.inherits(Master, EventEmitter);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Master.prototype.write = function write(data) {
|
Master.prototype.write = function write(data) {
|
||||||
if (bcoin.isBrowser) {
|
if (utils.isBrowser) {
|
||||||
if (global.postMessage.length === 2) {
|
if (global.postMessage.length === 2) {
|
||||||
data.__proto__ = Uint8Array.prototype;
|
data.__proto__ = Uint8Array.prototype;
|
||||||
global.postMessage({ buf: data }, [data]);
|
global.postMessage({ buf: data }, [data]);
|
||||||
@ -604,81 +635,53 @@ Master.prototype.sendEvent = function sendEvent() {
|
|||||||
return this.send(0, 'event', items);
|
return this.send(0, 'event', items);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a debug message, prefixing
|
|
||||||
* it with the worker ID.
|
|
||||||
* @param {...String} args
|
|
||||||
*/
|
|
||||||
|
|
||||||
Master.prototype.debug = function debug() {
|
|
||||||
var i, args, msg;
|
|
||||||
|
|
||||||
if (!this.options.debug)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args = new Array(arguments.length);
|
|
||||||
|
|
||||||
for (i = 0; i < args.length; i++)
|
|
||||||
args[i] = arguments[i];
|
|
||||||
|
|
||||||
msg = utils.format(args, false);
|
|
||||||
msg = utils.format(['Worker %d: %s', this.id, msg], false);
|
|
||||||
|
|
||||||
if (bcoin.isBrowser) {
|
|
||||||
console.error(msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.stderr.write(msg + '\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write an error as a debug message.
|
|
||||||
* @param {Error} err
|
|
||||||
*/
|
|
||||||
|
|
||||||
Master.prototype.error = function error(err) {
|
|
||||||
if (!err)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.debug(err.message);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the worker.
|
* Destroy the worker.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Master.prototype.destroy = function destroy() {
|
Master.prototype.destroy = function destroy() {
|
||||||
if (bcoin.isBrowser)
|
if (utils.isBrowser)
|
||||||
return global.close();
|
return global.close();
|
||||||
return process.exit(0);
|
return process.exit(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a message to stdout in the master process.
|
||||||
|
* @param {Object|String} obj
|
||||||
|
* @param {...String} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
Master.prototype.log = function log() {
|
||||||
|
var items = new Array(arguments.length);
|
||||||
|
var i;
|
||||||
|
|
||||||
|
for (i = 0; i < items.length; i++)
|
||||||
|
items[i] = arguments[i];
|
||||||
|
|
||||||
|
this.sendEvent('log', items);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen for messages from master process (only if worker).
|
* Listen for messages from master process (only if worker).
|
||||||
* @param {Number} id - Worker id.
|
|
||||||
* @param {Object?} options
|
* @param {Object?} options
|
||||||
* @returns {Master}
|
* @returns {Master}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Master.listen = function listen(id, options) {
|
Master.listen = function listen(options) {
|
||||||
var master = new Master(id, options);
|
var master = new Master(options);
|
||||||
var debug = master.debug.bind(master);
|
|
||||||
var error = master.error.bind(master);
|
|
||||||
|
|
||||||
bcoin.debug = debug;
|
utils.log = master.log.bind(master);
|
||||||
bcoin.error = error;
|
utils.error = utils.log;
|
||||||
utils.print = debug;
|
|
||||||
utils.error = debug;
|
|
||||||
|
|
||||||
master.on('error', function(err) {
|
master.on('error', function(err) {
|
||||||
bcoin.debug('Master error: %s', err.message);
|
master.sendEvent('worker error', fromError(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
master.on('packet', function(job, body) {
|
master.on('packet', function(job, body) {
|
||||||
var result;
|
var result;
|
||||||
|
|
||||||
if (body.name === 'event') {
|
if (body.name === 'event') {
|
||||||
|
master.emit('event', body.items);
|
||||||
master.emit.apply(master, body.items);
|
master.emit.apply(master, body.items);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -686,16 +689,14 @@ Master.listen = function listen(id, options) {
|
|||||||
try {
|
try {
|
||||||
result = jobs[body.name].apply(jobs, body.items);
|
result = jobs[body.name].apply(jobs, body.items);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
bcoin.error(e);
|
return master.send(job, 'response', [fromError(e)]);
|
||||||
return master.send(job, 'response', [{
|
|
||||||
message: e.message,
|
|
||||||
stack: e.stack + ''
|
|
||||||
}]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return master.send(job, 'response', [null, result]);
|
return master.send(job, 'response', [null, result]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bcoin.master = master;
|
||||||
|
|
||||||
return master;
|
return master;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -727,13 +728,7 @@ jobs.verify = function verify(tx, flags) {
|
|||||||
|
|
||||||
jobs.mine = function mine(attempt) {
|
jobs.mine = function mine(attempt) {
|
||||||
attempt.on('status', function(stat) {
|
attempt.on('status', function(stat) {
|
||||||
bcoin.debug(
|
bcoin.master.sendEvent('status', stat);
|
||||||
'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
|
|
||||||
stat.hashrate / 1000 | 0,
|
|
||||||
stat.hashes,
|
|
||||||
stat.target,
|
|
||||||
stat.height,
|
|
||||||
stat.best);
|
|
||||||
});
|
});
|
||||||
return attempt.mineSync();
|
return attempt.mineSync();
|
||||||
};
|
};
|
||||||
@ -794,7 +789,8 @@ Framer.prototype.body = function body(name, items) {
|
|||||||
return p.render();
|
return p.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
Framer.item = function _item(item, p) {
|
Framer.item = function _item(item, writer) {
|
||||||
|
var p = BufferWriter(writer);
|
||||||
var i, keys;
|
var i, keys;
|
||||||
|
|
||||||
switch (typeof item) {
|
switch (typeof item) {
|
||||||
@ -834,7 +830,7 @@ Framer.item = function _item(item, p) {
|
|||||||
p.writeU8(45);
|
p.writeU8(45);
|
||||||
item.toRaw(p);
|
item.toRaw(p);
|
||||||
} else if (bn.isBN(item)) {
|
} else if (bn.isBN(item)) {
|
||||||
p.writeU8(50);
|
p.writeU8(10);
|
||||||
p.writeVarBytes(item.toArrayLike(Buffer));
|
p.writeVarBytes(item.toArrayLike(Buffer));
|
||||||
} else if (Buffer.isBuffer(item)) {
|
} else if (Buffer.isBuffer(item)) {
|
||||||
p.writeU8(4);
|
p.writeU8(4);
|
||||||
@ -856,8 +852,13 @@ Framer.item = function _item(item, p) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false, 'Bad type: ' + typeof item);
|
throw new Error('Bad type.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!writer)
|
||||||
|
p = p.render();
|
||||||
|
|
||||||
|
return p;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -880,58 +881,55 @@ function Parser() {
|
|||||||
utils.inherits(Parser, EventEmitter);
|
utils.inherits(Parser, EventEmitter);
|
||||||
|
|
||||||
Parser.prototype.feed = function feed(data) {
|
Parser.prototype.feed = function feed(data) {
|
||||||
var chunk, header, body, rest;
|
var chunk, header, body;
|
||||||
|
|
||||||
this.pendingTotal += data.length;
|
while (data) {
|
||||||
this.pending.push(data);
|
this.pendingTotal += data.length;
|
||||||
|
this.pending.push(data);
|
||||||
|
data = null;
|
||||||
|
|
||||||
if (this.pendingTotal < this.waiting)
|
if (this.pendingTotal < this.waiting)
|
||||||
return;
|
break;
|
||||||
|
|
||||||
chunk = Buffer.concat(this.pending);
|
chunk = Buffer.concat(this.pending);
|
||||||
|
|
||||||
if (chunk.length > this.waiting) {
|
if (chunk.length > this.waiting) {
|
||||||
rest = chunk.slice(this.waiting);
|
data = chunk.slice(this.waiting);
|
||||||
chunk = chunk.slice(0, this.waiting);
|
chunk = chunk.slice(0, this.waiting);
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.header) {
|
|
||||||
this.header = this.parseHeader(chunk);
|
|
||||||
this.waiting = this.header.size;
|
|
||||||
this.pending.length = 0;
|
|
||||||
this.pendingTotal = 0;
|
|
||||||
|
|
||||||
if (this.header.magic !== 0xdeadbeef) {
|
|
||||||
this.header = null;
|
|
||||||
this.waiting = 12;
|
|
||||||
this.emit('error', new Error('Bad magic number.'));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rest)
|
if (!this.header) {
|
||||||
this.feed(rest);
|
this.header = this.parseHeader(chunk);
|
||||||
|
this.waiting = this.header.size;
|
||||||
|
this.pending.length = 0;
|
||||||
|
this.pendingTotal = 0;
|
||||||
|
|
||||||
return;
|
if (this.header.magic !== 0xdeadbeef) {
|
||||||
|
this.header = null;
|
||||||
|
this.waiting = 12;
|
||||||
|
this.emit('error', new Error('Bad magic number.'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
header = this.header;
|
||||||
|
|
||||||
|
this.pending.length = 0;
|
||||||
|
this.pendingTotal = 0;
|
||||||
|
this.waiting = 12;
|
||||||
|
this.header = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
body = this.parseBody(chunk);
|
||||||
|
} catch (e) {
|
||||||
|
this.emit('error', e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('packet', header.job, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
header = this.header;
|
|
||||||
|
|
||||||
this.pending.length = 0;
|
|
||||||
this.pendingTotal = 0;
|
|
||||||
this.waiting = 12;
|
|
||||||
this.header = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
body = this.parseBody(chunk);
|
|
||||||
} catch (e) {
|
|
||||||
this.emit('error', e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit('packet', header.job, body);
|
|
||||||
|
|
||||||
if (rest)
|
|
||||||
this.feed(rest);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseHeader = function parseHeader(data) {
|
Parser.prototype.parseHeader = function parseHeader(data) {
|
||||||
@ -957,7 +955,8 @@ Parser.prototype.parseBody = function parseBody(data) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.parseItem = function parseItem(p) {
|
Parser.parseItem = function parseItem(data) {
|
||||||
|
var p = BufferReader(data);
|
||||||
var i, count, items;
|
var i, count, items;
|
||||||
|
|
||||||
switch (p.readU8()) {
|
switch (p.readU8()) {
|
||||||
@ -983,6 +982,8 @@ Parser.parseItem = function parseItem(p) {
|
|||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
items[p.readVarString('utf8')] = Parser.parseItem(p);
|
items[p.readVarString('utf8')] = Parser.parseItem(p);
|
||||||
return items;
|
return items;
|
||||||
|
case 10:
|
||||||
|
return new bn(p.readVarBytes());
|
||||||
case 40:
|
case 40:
|
||||||
return bcoin.block.fromRaw(p);
|
return bcoin.block.fromRaw(p);
|
||||||
case 41:
|
case 41:
|
||||||
@ -995,17 +996,13 @@ Parser.parseItem = function parseItem(p) {
|
|||||||
return bcoin.mempoolentry.fromRaw(p);
|
return bcoin.mempoolentry.fromRaw(p);
|
||||||
case 45:
|
case 45:
|
||||||
return bcoin.minerblock.fromRaw(p);
|
return bcoin.minerblock.fromRaw(p);
|
||||||
case 50:
|
|
||||||
return new bn(p.readVarBytes());
|
|
||||||
default:
|
default:
|
||||||
assert(false, 'Bad type.');
|
throw new Error('Bad type.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Helper to retrieve number of cores.
|
* Helpers
|
||||||
* @memberof Workers
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getCores() {
|
function getCores() {
|
||||||
@ -1019,6 +1016,17 @@ function getCores() {
|
|||||||
return os.cpus().length;
|
return os.cpus().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toError(values) {
|
||||||
|
var err = new Error(values[0]);
|
||||||
|
err.stack = values[1];
|
||||||
|
err.type = values[2];
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromError(err) {
|
||||||
|
return [err.message, err.stack + '', err.type];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -13,9 +13,9 @@
|
|||||||
},
|
},
|
||||||
"repository": "git://github.com/bcoin-org/bcoin.git",
|
"repository": "git://github.com/bcoin-org/bcoin.git",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
"bcoin",
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"blockchain",
|
"blockchain",
|
||||||
"bcoin",
|
|
||||||
"wallet"
|
"wallet"
|
||||||
],
|
],
|
||||||
"author": "Fedor Indutny <fedor@indutny.com>",
|
"author": "Fedor Indutny <fedor@indutny.com>",
|
||||||
@ -32,10 +32,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bn.js": "4.11.0",
|
"bn.js": "4.11.0",
|
||||||
"elliptic": "6.2.3",
|
"elliptic": "6.2.3"
|
||||||
"leveldown": "git://github.com/chjj/leveldown.git#staging"
|
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
"leveldown": "git://github.com/chjj/leveldown.git#staging",
|
||||||
"secp256k1": "3.0.0",
|
"secp256k1": "3.0.0",
|
||||||
"socket.io": "1.4.5",
|
"socket.io": "1.4.5",
|
||||||
"socket.io-client": "1.4.5"
|
"socket.io-client": "1.4.5"
|
||||||
|
|||||||
@ -103,28 +103,28 @@ var segnet4 = createGenesisBlock({
|
|||||||
nonce: 0
|
nonce: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
utils.print(main);
|
utils.log(main);
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print(testnet);
|
utils.log(testnet);
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print(regtest);
|
utils.log(regtest);
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print(segnet3);
|
utils.log(segnet3);
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print(segnet4);
|
utils.log(segnet4);
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print('main hash: %s', main.rhash);
|
utils.log('main hash: %s', main.rhash);
|
||||||
utils.print('main raw: %s', main.toRaw().toString('hex'));
|
utils.log('main raw: %s', main.toRaw().toString('hex'));
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print('testnet hash: %s', testnet.rhash);
|
utils.log('testnet hash: %s', testnet.rhash);
|
||||||
utils.print('testnet raw: %s', testnet.toRaw().toString('hex'));
|
utils.log('testnet raw: %s', testnet.toRaw().toString('hex'));
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print('regtest hash: %s', regtest.rhash);
|
utils.log('regtest hash: %s', regtest.rhash);
|
||||||
utils.print('regtest raw: %s', regtest.toRaw().toString('hex'));
|
utils.log('regtest raw: %s', regtest.toRaw().toString('hex'));
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print('segnet3 hash: %s', segnet3.rhash);
|
utils.log('segnet3 hash: %s', segnet3.rhash);
|
||||||
utils.print('segnet3 raw: %s', segnet3.toRaw().toString('hex'));
|
utils.log('segnet3 raw: %s', segnet3.toRaw().toString('hex'));
|
||||||
utils.print('');
|
utils.log('');
|
||||||
utils.print('segnet4 hash: %s', segnet4.rhash);
|
utils.log('segnet4 hash: %s', segnet4.rhash);
|
||||||
utils.print('segnet4 raw: %s', segnet4.toRaw().toString('hex'));
|
utils.log('segnet4 raw: %s', segnet4.toRaw().toString('hex'));
|
||||||
|
|||||||
@ -63,7 +63,6 @@ describe('Script', function() {
|
|||||||
opcodes.OP_5
|
opcodes.OP_5
|
||||||
]);
|
]);
|
||||||
var stack = new Stack();
|
var stack = new Stack();
|
||||||
utils.print(inputScript);
|
|
||||||
inputScript.execute(stack);
|
inputScript.execute(stack);
|
||||||
var res = prevOutScript.execute(stack);
|
var res = prevOutScript.execute(stack);
|
||||||
assert(res);
|
assert(res);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user