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** is a bitcoin node which can act as an SPV node or a fully validating
|
||||
fullnode. Bcoin runs in node.js, but it can also be browserified.
|
||||
**BCoin** is a bitcoin library which can also act as an SPV node or a full
|
||||
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
|
||||
|
||||
- SPV mode
|
||||
- HD Wallets (using BIP44 (or optionally BIP45) derivation)
|
||||
- HD Wallets (using BIP44 derivation and accounts)
|
||||
- Fully browserifiable
|
||||
- Full block validation
|
||||
- Full block database
|
||||
- Fully validating mempool (stored in-memory or on-disk)
|
||||
- Fully validating mempool (stored in-memory or optionally on-disk)
|
||||
- Wallet database
|
||||
- HTTP server which acts as a wallet server and can also serve:
|
||||
blocks, txs (by hash/address), and utxos (by id/address).
|
||||
- Fast UTXO retrieval by address for wallets (10000 utxos from 10000 different
|
||||
addresses in ~700ms, 50000+ utxos from 10-100 addresses in ~400ms)
|
||||
- Segregated witness support for block/tx validation and wallets.
|
||||
- Versionbits suport.
|
||||
- Full segregated witness support for block/tx validation and wallets.
|
||||
- Versionbits, CSV, BIP151, BIP152, MAST support.
|
||||
- SPV mode
|
||||
|
||||
## Install
|
||||
|
||||
@ -41,16 +44,30 @@ Read the docs here: http://bcoin.io/docs/
|
||||
### Creating a blockchain and mempool
|
||||
|
||||
``` 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 mempool = new bcoin.mempool({ chain: chain, db: 'memory' });
|
||||
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) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
// Create a block "attempt".
|
||||
miner.createBlock(function(err, attempt) {
|
||||
if (err)
|
||||
throw err;
|
||||
@ -78,10 +95,17 @@ miner.open(function(err) {
|
||||
``` js
|
||||
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' });
|
||||
|
||||
// Create a network pool of peers with a limit of 8 peers.
|
||||
var pool = new bcoin.pool({ chain: chain, mempool: mempool, size: 8 });
|
||||
|
||||
// Open the pool (implicitly opens mempool and chain).
|
||||
pool.open(function(err) {
|
||||
if (err)
|
||||
throw err;
|
||||
@ -92,8 +116,9 @@ pool.open(function(err) {
|
||||
// Start the blockchain sync.
|
||||
pool.startSync();
|
||||
|
||||
// Watch the action
|
||||
chain.on('block', function(block) {
|
||||
console.log('Added block:');
|
||||
console.log('Connected block to blockchain:');
|
||||
console.log(block);
|
||||
});
|
||||
|
||||
@ -104,12 +129,12 @@ pool.open(function(err) {
|
||||
|
||||
pool.on('tx', function(tx) {
|
||||
console.log('Saw transaction:');
|
||||
console.log(tx);
|
||||
console.log(tx.rhash);
|
||||
});
|
||||
});
|
||||
|
||||
// Start up a segnet4 sync while
|
||||
// we're at it (because we can).
|
||||
// Start up a segnet4 sync in-memory
|
||||
// while we're at it (because we can).
|
||||
|
||||
var tchain = new bcoin.chain({
|
||||
network: 'segnet4',
|
||||
@ -162,10 +187,10 @@ tpool.open(function(err) {
|
||||
``` js
|
||||
var bcoin = require('bcoin').set('testnet');
|
||||
|
||||
// SPV chains only store the chain headers.
|
||||
var chain = new bcoin.chain({
|
||||
db: 'leveldb',
|
||||
// A custom chaindb location:
|
||||
location: process.env.HOME + '/chain.db',
|
||||
location: process.env.HOME + '/spvchain',
|
||||
spv: true
|
||||
});
|
||||
|
||||
@ -189,7 +214,7 @@ pool.open(function(err) {
|
||||
if (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.
|
||||
pool.watchAddress(wallet.getAddress());
|
||||
@ -223,7 +248,15 @@ var node = bcoin.fullnode({
|
||||
useCheckpoints: true,
|
||||
debug: true,
|
||||
// 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
|
||||
@ -243,25 +276,28 @@ node.open(function(err) {
|
||||
if (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
|
||||
// (this will take a while since we're a fullnode)
|
||||
node.startSync();
|
||||
|
||||
// Wait for balance and send it to a new address.
|
||||
wallet.once('balance', function(balance) {
|
||||
// Create a transaction, fill
|
||||
// it with coins, and sign it.
|
||||
var to = {
|
||||
address: newReceiving,
|
||||
value: balance.total
|
||||
var options = {
|
||||
subtractFee: true,
|
||||
outputs: [{
|
||||
address: newReceiving,
|
||||
value: balance.total
|
||||
}]
|
||||
};
|
||||
wallet.createTX({ subtractFee: true }, [to], function(err, tx) {
|
||||
wallet.createTX(options, function(err, tx) {
|
||||
if (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)
|
||||
throw err;
|
||||
|
||||
@ -283,15 +319,15 @@ node.open(function(err) {
|
||||
});
|
||||
|
||||
node.chain.on('block', function(block) {
|
||||
console.log(block);
|
||||
;
|
||||
});
|
||||
|
||||
node.mempool.on('tx', function(tx) {
|
||||
console.log(block);
|
||||
;
|
||||
});
|
||||
|
||||
node.chain.on('full', function() {
|
||||
node.mempool.getAll(function(err, txs) {
|
||||
node.mempool.getHistory(function(err, txs) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
@ -305,11 +341,16 @@ node.chain.on('full', function() {
|
||||
``` bash
|
||||
$ cd ~/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
|
||||
|
||||
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
|
||||
|
||||
``` bash
|
||||
@ -319,36 +360,316 @@ $ node bin/bcoin-cli block 0
|
||||
# View primary wallet
|
||||
$ node bin/bcoin-cli wallet primary
|
||||
# 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
|
||||
$ 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
|
||||
|
||||
### Scripting
|
||||
## HTTP API & Websocket Events
|
||||
|
||||
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
|
||||
|
||||
This software is licensed under the MIT License.
|
||||
|
||||
Copyright Fedor Indutny, 2014-2016.
|
||||
Copyright Christopher Jeffrey, 2014-2016.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
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
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
[bip37]: https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki
|
||||
[escrow]: https://en.bitcoin.it/wiki/Contract#Example_2:_Escrow_and_dispute_mediation
|
||||
[v8]: https://www.youtube.com/watch?v=UJPdhx5zTaw
|
||||
[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) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(wallet);
|
||||
utils.log(wallet);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -73,7 +73,7 @@ function addKey(callback) {
|
||||
client.addKey(id, argv.account, keys, function(err, wallet) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print('added');
|
||||
utils.log('added');
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -84,7 +84,7 @@ function createAccount(callback) {
|
||||
client.createWalletAccount(id, account, function(err, account) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(account);
|
||||
utils.log(account);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -94,7 +94,7 @@ function getAccounts(callback) {
|
||||
client.getWalletAccounts(id, function(err, accounts) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(accounts);
|
||||
utils.log(accounts);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -109,7 +109,7 @@ function removeKey(callback) {
|
||||
client.removeKey(id, argv.account, keys, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print('removed');
|
||||
utils.log('removed');
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -120,7 +120,7 @@ function getWallet(callback) {
|
||||
client.getWallet(id, argv.account, passphrase, function(err, wallet) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(wallet);
|
||||
utils.log(wallet);
|
||||
wallet.destroy();
|
||||
callback();
|
||||
});
|
||||
@ -132,7 +132,7 @@ function getTX(callback) {
|
||||
return client.getTXByAddress(hash, function(err, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(txs);
|
||||
utils.log(txs);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -142,11 +142,11 @@ function getTX(callback) {
|
||||
return callback(err);
|
||||
|
||||
if (!tx) {
|
||||
utils.print('TX not found.');
|
||||
utils.log('TX not found.');
|
||||
return callback();
|
||||
}
|
||||
|
||||
utils.print(tx);
|
||||
utils.log(tx);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -162,13 +162,13 @@ function getBlock(callback) {
|
||||
return callback(err);
|
||||
|
||||
if (!block) {
|
||||
utils.print('Block not found.');
|
||||
utils.log('Block not found.');
|
||||
return callback();
|
||||
}
|
||||
|
||||
utils.print(block);
|
||||
utils.print('Coinbase Data:');
|
||||
utils.print(block.txs[0].inputs[0].script.getCoinbaseFlags());
|
||||
utils.log(block);
|
||||
utils.log('Coinbase Data:');
|
||||
utils.log(block.txs[0].inputs[0].script.getCoinbaseFlags());
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -180,7 +180,7 @@ function getCoin(callback) {
|
||||
return client.getCoinsByAddress(hash, function(err, coins) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(coins);
|
||||
utils.log(coins);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -190,11 +190,11 @@ function getCoin(callback) {
|
||||
return callback(err);
|
||||
|
||||
if (!coin) {
|
||||
utils.print('Coin not found.');
|
||||
utils.log('Coin not found.');
|
||||
return callback();
|
||||
}
|
||||
|
||||
utils.print(coin);
|
||||
utils.log(coin);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -204,7 +204,7 @@ function getWalletHistory(callback) {
|
||||
client.getWalletHistory(id, argv.account, function(err, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(txs);
|
||||
utils.log(txs);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -213,29 +213,29 @@ function listenWallet(callback) {
|
||||
var id = getID();
|
||||
client.join(id);
|
||||
client.on('tx', function(tx, map) {
|
||||
utils.print('TX:');
|
||||
utils.print(tx);
|
||||
utils.print(map);
|
||||
utils.log('TX:');
|
||||
utils.log(tx);
|
||||
utils.log(map);
|
||||
});
|
||||
client.on('updated', function(tx, map) {
|
||||
utils.print('TX updated:');
|
||||
utils.print(tx);
|
||||
utils.print(map);
|
||||
utils.log('TX updated:');
|
||||
utils.log(tx);
|
||||
utils.log(map);
|
||||
});
|
||||
client.on('confirmed', function(tx, map) {
|
||||
utils.print('TX updated:');
|
||||
utils.print(tx);
|
||||
utils.print(map);
|
||||
utils.log('TX updated:');
|
||||
utils.log(tx);
|
||||
utils.log(map);
|
||||
});
|
||||
client.on('address', function(receive, change) {
|
||||
utils.print('New addresses allocated:');
|
||||
utils.print(receive);
|
||||
utils.print(change);
|
||||
utils.log('New addresses allocated:');
|
||||
utils.log(receive);
|
||||
utils.log(change);
|
||||
});
|
||||
client.on('balance', function(tx, map) {
|
||||
utils.print('Balance:');
|
||||
utils.print(tx);
|
||||
utils.print(map);
|
||||
utils.log('Balance:');
|
||||
utils.log(tx);
|
||||
utils.log(map);
|
||||
});
|
||||
}
|
||||
|
||||
@ -244,9 +244,9 @@ function getBalance(callback) {
|
||||
client.getWalletBalance(id, argv.account, function(err, balance) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print('Confirmed: %s', utils.btc(balance.confirmed));
|
||||
utils.print('Unconfirmed: %s', utils.btc(balance.unconfirmed));
|
||||
utils.print('Total: %s', utils.btc(balance.total));
|
||||
utils.log('Confirmed: %s', utils.btc(balance.confirmed));
|
||||
utils.log('Unconfirmed: %s', utils.btc(balance.unconfirmed));
|
||||
utils.log('Total: %s', utils.btc(balance.total));
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -255,7 +255,7 @@ function getMempool(callback) {
|
||||
client.getMempool(function(err, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(txs);
|
||||
utils.log(txs);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -274,8 +274,8 @@ function sendTX(callback) {
|
||||
client.walletSend(id, {outputs:[output]}, function(err, tx) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(tx);
|
||||
utils.print(tx.toRaw('hex'));
|
||||
utils.log(tx);
|
||||
utils.log(tx.toRaw('hex'));
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -294,8 +294,8 @@ function createTX(callback) {
|
||||
client.walletCreate(id, options, [output], function(err, tx) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(tx);
|
||||
utils.print(tx.toRaw('hex'));
|
||||
utils.log(tx);
|
||||
utils.log(tx.toRaw('hex'));
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -307,8 +307,8 @@ function signTX(callback) {
|
||||
client.walletSign(id, tx, options, function(err, tx) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(tx);
|
||||
utils.print(tx.toRaw('hex'));
|
||||
utils.log(tx);
|
||||
utils.log(tx.toRaw('hex'));
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -319,7 +319,7 @@ function zap(callback) {
|
||||
client.walletZap(id, argv.account, age, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print('Zapped!');
|
||||
utils.log('Zapped!');
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -329,8 +329,8 @@ function broadcast(callback) {
|
||||
client.broadcast(tx, function(err, tx) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print('Broadcasted:');
|
||||
utils.print(tx);
|
||||
utils.log('Broadcasted:');
|
||||
utils.log(tx);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -340,7 +340,7 @@ function view(callback) {
|
||||
client.walletFill(tx, function(err, tx) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
utils.print(tx);
|
||||
utils.log(tx);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -386,32 +386,32 @@ function main(callback) {
|
||||
case 'block':
|
||||
return getBlock(callback);
|
||||
default:
|
||||
utils.print('Unrecognized command.');
|
||||
utils.print('Commands:');
|
||||
utils.print(' $ wallet [id] --keys [hdkeys]'
|
||||
utils.log('Unrecognized command.');
|
||||
utils.log('Commands:');
|
||||
utils.log(' $ wallet [id] --keys [hdkeys]'
|
||||
+ ' --type [pubkeyhash/multisig] -m [m-value]'
|
||||
+ ' -n [n-value] --witness: View or create wallet by ID.');
|
||||
utils.print(' $ listen [id]: Listen for wallet events.');
|
||||
utils.print(' $ getwallet [id]: View wallet by ID.');
|
||||
utils.print(' $ addkey [id] --keys [hdkeys]: Add keys to wallet.');
|
||||
utils.print(' $ rmkey [id] --keys [hdkeys]: Remove keys from wallet.');
|
||||
utils.print(' $ balance [id]: Get wallet balance.');
|
||||
utils.print(' $ history [id]: View wallet TX history.');
|
||||
utils.print(' $ accounts [id]: List account names.');
|
||||
utils.print(' $ account [id] [acct]: Get account details.');
|
||||
utils.print(' $ send [id] [address] [value] --script [code]: Send transaction.');
|
||||
utils.print(' $ create [id] [address] [value] --script [code]: Create transaction.');
|
||||
utils.print(' $ sign [id] [tx-hex]: Sign transaction.');
|
||||
utils.print(' $ zap [id] --age [age]: Zap pending wallet TXs.');
|
||||
utils.print(' $ broadcast [tx-hex]: Broadcast transaction.');
|
||||
utils.print(' $ view [tx-hex]: View transaction.');
|
||||
utils.print(' $ mempool: Get mempool snapshot.');
|
||||
utils.print(' $ tx [hash/address]: View transactions.');
|
||||
utils.print(' $ coin [hash+index/address]: View coins.');
|
||||
utils.print(' $ block [hash/height]: View block.');
|
||||
utils.print('Other Options:');
|
||||
utils.print(' --passphrase [passphrase]: For signing and account creation.');
|
||||
utils.print(' --account [acctname]: Account name.');
|
||||
utils.log(' $ listen [id]: Listen for wallet events.');
|
||||
utils.log(' $ getwallet [id]: View wallet by ID.');
|
||||
utils.log(' $ addkey [id] --keys [hdkeys]: Add keys to wallet.');
|
||||
utils.log(' $ rmkey [id] --keys [hdkeys]: Remove keys from wallet.');
|
||||
utils.log(' $ balance [id]: Get wallet balance.');
|
||||
utils.log(' $ history [id]: View wallet TX history.');
|
||||
utils.log(' $ accounts [id]: List account names.');
|
||||
utils.log(' $ account [id] [acct]: Get account details.');
|
||||
utils.log(' $ send [id] [address] [value] --script [code]: Send transaction.');
|
||||
utils.log(' $ create [id] [address] [value] --script [code]: Create transaction.');
|
||||
utils.log(' $ sign [id] [tx-hex]: Sign transaction.');
|
||||
utils.log(' $ zap [id] --age [age]: Zap pending wallet TXs.');
|
||||
utils.log(' $ broadcast [tx-hex]: Broadcast transaction.');
|
||||
utils.log(' $ view [tx-hex]: View transaction.');
|
||||
utils.log(' $ mempool: Get mempool snapshot.');
|
||||
utils.log(' $ tx [hash/address]: View transactions.');
|
||||
utils.log(' $ coin [hash+index/address]: View coins.');
|
||||
utils.log(' $ block [hash/height]: View block.');
|
||||
utils.log('Other Options:');
|
||||
utils.log(' --passphrase [passphrase]: For signing and account creation.');
|
||||
utils.log(' --account [acctname]: Account name.');
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
@ -476,7 +476,7 @@ client.getInfo(function(err, info) {
|
||||
}
|
||||
|
||||
if (!argv.args[0])
|
||||
utils.print(info);
|
||||
utils.log(info);
|
||||
|
||||
main(function(err) {
|
||||
if (err) {
|
||||
|
||||
15
bin/node
15
bin/node
@ -2,28 +2,29 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('../').set({ debug: true, debugFile: true });
|
||||
var bcoin = require('../');
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
bcoin.debug(err.stack);
|
||||
bcoin.error(err);
|
||||
node.logger.debug(err.stack);
|
||||
node.logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
var node = new bcoin.fullnode({
|
||||
logLevel: 'debug',
|
||||
logFile: true,
|
||||
db: 'leveldb',
|
||||
prune: process.argv.indexOf('--prune') !== -1,
|
||||
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
|
||||
listen: process.argv.indexOf('--listen') !== -1,
|
||||
selfish: process.argv.indexOf('--selfish') !== -1,
|
||||
headers: process.argv.indexOf('--headers') !== -1,
|
||||
mine: process.argv.indexOf('--mine') !== -1,
|
||||
parallel: process.argv.indexOf('--parallel') !== -1
|
||||
});
|
||||
|
||||
node.on('error', function(err) {
|
||||
bcoin.error(err);
|
||||
;
|
||||
});
|
||||
|
||||
node.open(function(err) {
|
||||
@ -34,7 +35,7 @@ node.open(function(err) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
if (!node.options.mine) {
|
||||
if (process.argv.indexOf('--mine') === -1) {
|
||||
node.startSync();
|
||||
return;
|
||||
}
|
||||
|
||||
14
bin/spvnode
14
bin/spvnode
@ -2,18 +2,20 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('../').set({ debug: true, debugFile: true });
|
||||
var bcoin = require('../');
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
|
||||
var node = bcoin.spvnode({
|
||||
// passphrase: 'node',
|
||||
preload: process.argv.indexOf('--preload') !== -1,
|
||||
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1
|
||||
logLevel: 'debug',
|
||||
logFile: true,
|
||||
db: 'leveldb',
|
||||
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
|
||||
headers: process.argv.indexOf('--headers') !== -1
|
||||
});
|
||||
|
||||
node.on('error', function(err) {
|
||||
bcoin.debug(err.stack + '');
|
||||
;
|
||||
});
|
||||
|
||||
node.open(function(err) {
|
||||
@ -23,7 +25,7 @@ node.open(function(err) {
|
||||
if (process.argv.indexOf('--test') !== -1) {
|
||||
node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8');
|
||||
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));
|
||||
|
||||
if (this.info.pow) {
|
||||
bcoin.debug(
|
||||
utils.log(
|
||||
'Solving proof of work to create socket (%d, %s) -- please wait.',
|
||||
port, host);
|
||||
|
||||
@ -102,7 +102,7 @@ ProxySocket.prototype.connect = function connect(port, host) {
|
||||
pow.writeUInt32LE(nonce, 0, true);
|
||||
} 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);
|
||||
|
||||
@ -53,6 +53,12 @@ function Address(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(
|
||||
options.hash,
|
||||
options.type,
|
||||
|
||||
@ -59,6 +59,8 @@ AsyncObject.prototype.open = function open(callback) {
|
||||
callback = utils.wrap(callback, unlock);
|
||||
}
|
||||
|
||||
this.emit('preopen');
|
||||
|
||||
this.loading = true;
|
||||
|
||||
this._open(function(err) {
|
||||
@ -106,6 +108,8 @@ AsyncObject.prototype.close = function close(callback) {
|
||||
callback = utils.wrap(callback, unlock);
|
||||
}
|
||||
|
||||
this.emit('preclose');
|
||||
|
||||
this.closing = true;
|
||||
this.loaded = false;
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var bcoin = require('./env');
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var constants = bcoin.protocol.constants;
|
||||
var chachapoly = require('./chachapoly');
|
||||
|
||||
@ -65,6 +66,7 @@ BIP151.prototype.init = function init(publicKey) {
|
||||
};
|
||||
|
||||
BIP151.prototype.rekey = function rekey() {
|
||||
assert(this.mac, 'Cannot rekey before initialization.');
|
||||
this.mac = utils.hash256(this.mac);
|
||||
this.k1 = this.mac.slice(0, 32);
|
||||
this.k2 = this.mac.slice(32, 64);
|
||||
@ -166,12 +168,12 @@ BIP151.prototype.encack = function encack(data) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i === publicKey.length)
|
||||
if (i === publicKey.length) {
|
||||
this.rekey();
|
||||
else
|
||||
this.init(publicKey);
|
||||
return;
|
||||
}
|
||||
|
||||
return this;
|
||||
this.init(publicKey);
|
||||
};
|
||||
|
||||
BIP151.prototype.feed = function feed(data) {
|
||||
|
||||
@ -66,6 +66,8 @@ function Chain(options) {
|
||||
this.options = options;
|
||||
|
||||
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.total = 0;
|
||||
this.currentBlock = null;
|
||||
@ -101,7 +103,7 @@ Chain.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
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
|
||||
@ -112,12 +114,12 @@ Chain.prototype._init = function _init() {
|
||||
if (self.options.spv)
|
||||
return;
|
||||
|
||||
bcoin.debug('Block %s (%d) added to chain.',
|
||||
self.logger.info('Block %s (%d) added to chain.',
|
||||
utils.revHex(entry.hash), entry.height);
|
||||
});
|
||||
|
||||
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-hash=%s competitor-hash=%s'
|
||||
+ ' tip-chainwork=%s competitor-chainwork=%s'
|
||||
@ -133,15 +135,17 @@ Chain.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
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) {
|
||||
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) {
|
||||
bcoin.debug(
|
||||
self.logger.warning(
|
||||
'Fork at height %d: expected=%s received=%s',
|
||||
height,
|
||||
utils.revHex(expected),
|
||||
@ -150,19 +154,21 @@ Chain.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
@ -191,7 +197,7 @@ Chain.prototype._init = function _init() {
|
||||
Chain.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
|
||||
bcoin.debug('Chain is loading.');
|
||||
this.logger.info('Chain is loading.');
|
||||
|
||||
this.db.open(function(err) {
|
||||
if (err)
|
||||
@ -215,19 +221,25 @@ Chain.prototype._open = function open(callback) {
|
||||
self.network.updateHeight(tip.height);
|
||||
}
|
||||
|
||||
bcoin.profiler.snapshot();
|
||||
if (self.profiler)
|
||||
self.profiler.snapshot();
|
||||
|
||||
self.logger.memory();
|
||||
|
||||
self.getInitialState(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (self.csvActive)
|
||||
bcoin.debug('CSV is active.');
|
||||
self.logger.info('CSV is active.');
|
||||
|
||||
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);
|
||||
|
||||
@ -290,7 +302,7 @@ Chain.prototype.preload = function preload(callback) {
|
||||
if (this.network.type !== 'main')
|
||||
return callback();
|
||||
|
||||
bcoin.debug('Loading %s.', url);
|
||||
this.logger.info('Loading %s.', url);
|
||||
|
||||
function save(entry, header) {
|
||||
var unlock = locker.lock(save, [entry]);
|
||||
@ -305,7 +317,7 @@ Chain.prototype.preload = function preload(callback) {
|
||||
}
|
||||
|
||||
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)
|
||||
return callback();
|
||||
@ -330,7 +342,7 @@ Chain.prototype.preload = function preload(callback) {
|
||||
return callback(new Error('Bad response code: ' + res.statusCode));
|
||||
}
|
||||
if (chainHeight > height - 30000) {
|
||||
bcoin.debug('Preload height is %d. Skipping.', height);
|
||||
self.logger.info('Preload height is %d. Skipping.', height);
|
||||
stream.destroy();
|
||||
return callback();
|
||||
}
|
||||
@ -398,7 +410,7 @@ Chain.prototype.preload = function preload(callback) {
|
||||
save(entry, block);
|
||||
|
||||
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;
|
||||
height++;
|
||||
@ -1213,7 +1225,7 @@ Chain.prototype.setBestChain = function setBestChain(entry, block, prev, callbac
|
||||
|
||||
// A higher fork has arrived.
|
||||
// Time to reorganize the chain.
|
||||
bcoin.debug('WARNING: Reorganizing chain.');
|
||||
self.logger.warning('WARNING: Reorganizing chain.');
|
||||
return this.reorganize(entry, block, done);
|
||||
};
|
||||
|
||||
@ -1459,7 +1471,7 @@ Chain.prototype.add = function add(block, callback, force) {
|
||||
try {
|
||||
block = block.toBlock();
|
||||
} catch (e) {
|
||||
bcoin.error(e);
|
||||
self.logger.error(e);
|
||||
return done(new VerifyError(block,
|
||||
'malformed',
|
||||
'error parsing message',
|
||||
@ -1533,7 +1545,9 @@ Chain.prototype.add = function add(block, callback, force) {
|
||||
// Take heap snapshot for debugging.
|
||||
if (self.total === 1 || self.total % 20 === 0) {
|
||||
utils.gc();
|
||||
bcoin.profiler.snapshot();
|
||||
if (self.profiler)
|
||||
self.profiler.snapshot();
|
||||
self.logger.memory();
|
||||
}
|
||||
|
||||
utils.nextTick(function() {
|
||||
|
||||
@ -178,11 +178,10 @@ function ChainDB(chain, options) {
|
||||
|
||||
this.options = options;
|
||||
this.chain = chain;
|
||||
this.logger = chain.logger;
|
||||
this.network = this.chain.network;
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || (this.options.spv ? 'spvchain' : 'chain'),
|
||||
location: this.options.location,
|
||||
db: this.options.db,
|
||||
compression: true,
|
||||
@ -231,13 +230,13 @@ ChainDB.prototype._open = function open(callback) {
|
||||
var self = this;
|
||||
var genesis, block;
|
||||
|
||||
bcoin.debug('Starting chain load.');
|
||||
this.logger.info('Starting chain load.');
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
bcoin.debug('Chain successfully loaded.');
|
||||
self.logger.info('Chain successfully loaded.');
|
||||
|
||||
self.db.checkVersion('V', 0, callback);
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ var bcoin = require('./env');
|
||||
var utils = require('./utils');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var assert = utils.assert;
|
||||
var Output = bcoin.output;
|
||||
|
||||
/**
|
||||
* Represents an unspent output.
|
||||
@ -44,7 +45,7 @@ function Coin(options) {
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
utils.inherits(Coin, bcoin.output);
|
||||
utils.inherits(Coin, Output);
|
||||
|
||||
/**
|
||||
* Inject options into coin.
|
||||
|
||||
@ -240,6 +240,8 @@ ec.random = function random(size) {
|
||||
|
||||
/**
|
||||
* Generate a random number within a range.
|
||||
* Probably more cryptographically sound than
|
||||
* `Math.random()`.
|
||||
* @param {Number} min - Inclusive.
|
||||
* @param {Number} max - Exclusive.
|
||||
* @returns {Number}
|
||||
|
||||
328
lib/bcoin/env.js
328
lib/bcoin/env.js
@ -9,10 +9,6 @@
|
||||
|
||||
var utils = require('./utils');
|
||||
var global = utils.global;
|
||||
var fs;
|
||||
|
||||
if (!utils.isBrowser)
|
||||
fs = require('f' + 's');
|
||||
|
||||
/**
|
||||
* 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.db=leveldb] - Database backend.
|
||||
* @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.
|
||||
* @param {Boolean} [options.profile=false] - Enable profiler.
|
||||
* @param {Boolean} [options.useWorkers=false] - Enable workers.
|
||||
@ -55,9 +51,10 @@ if (!utils.isBrowser)
|
||||
* @property {Object} ec - {@link module:ec}.
|
||||
* @property {Function} lru - {@link LRU} 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} uri - See {@link module:uri}.
|
||||
* @property {Function} logger - {@link Logger} constructor.
|
||||
*
|
||||
* @property {Object} protocol
|
||||
* @property {Function} protocol.constants - See {@link module:constants}.
|
||||
@ -115,19 +112,7 @@ if (!utils.isBrowser)
|
||||
* @property {Workers?} workerPool - Default global worker pool.
|
||||
*/
|
||||
|
||||
function Environment(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
if (typeof options === 'string')
|
||||
options = { network: options };
|
||||
|
||||
this.options = options;
|
||||
|
||||
this._debug = null;
|
||||
|
||||
this.isBrowser = utils.isBrowser;
|
||||
|
||||
function Environment() {
|
||||
this.env = Environment;
|
||||
this.bn = require('bn.js');
|
||||
this.utils = require('./utils');
|
||||
@ -140,14 +125,15 @@ function Environment(options) {
|
||||
this.rbt = require('./rbt');
|
||||
this.lowlevelup = require('./lowlevelup');
|
||||
this.uri = require('./uri');
|
||||
this.logger = require('./logger');
|
||||
|
||||
this.protocol = require('./protocol');
|
||||
this.packets = this.protocol.packets;
|
||||
this.network = require('./network');
|
||||
this.errors = require('./errors');
|
||||
this.ldb = require('./ldb');
|
||||
this.profiler = require('./profiler');
|
||||
this.timedata = require('./timedata');
|
||||
this.sigcache = require('./sigcache')(0);
|
||||
this.script = require('./script');
|
||||
this.opcode = this.script.Opcode;
|
||||
this.stack = this.script.Stack;
|
||||
@ -167,6 +153,7 @@ function Environment(options) {
|
||||
this.block = require('./block');
|
||||
this.merkleblock = require('./merkleblock');
|
||||
this.headers = require('./headers');
|
||||
this.fees = require('./fees');
|
||||
this.node = require('./node');
|
||||
this.spvnode = require('./spvnode');
|
||||
this.fullnode = require('./fullnode');
|
||||
@ -188,28 +175,27 @@ function Environment(options) {
|
||||
this.miner = require('./miner');
|
||||
this.minerblock = this.miner.MinerBlock;
|
||||
this.http = require('./http');
|
||||
|
||||
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.workers = require('./workers');
|
||||
|
||||
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) {
|
||||
if (typeof options === 'string')
|
||||
options = { network: options };
|
||||
@ -217,209 +203,29 @@ Environment.prototype.set = function set(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
options = utils.merge({}, options);
|
||||
if (options.network)
|
||||
this.network.set(options.network);
|
||||
|
||||
options.network = options.network
|
||||
|| process.env.BCOIN_NETWORK
|
||||
|| 'main';
|
||||
if (typeof options.useWorkers === 'boolean')
|
||||
this.useWorkers = options.useWorkers;
|
||||
|
||||
options.prefix = options.prefix
|
||||
|| process.env.BCOIN_PREFIX;
|
||||
if (utils.isNumber(options.maxWorkers))
|
||||
this.workerPool.size = options.maxWorkers;
|
||||
|
||||
if (!options.prefix)
|
||||
options.prefix = utils.HOME + '/.bcoin';
|
||||
if (utils.isNumber(options.workerTimeout))
|
||||
this.workerPool.timeout = options.workerTimeout;
|
||||
|
||||
if (!options.db)
|
||||
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) {
|
||||
if (utils.isBrowser && this.useWorkers) {
|
||||
this.useWorkers = typeof global.Worker === 'function'
|
||||
|| typeof global.postMessage === 'function';
|
||||
}
|
||||
|
||||
if (this.useWorkers) {
|
||||
this.workers = require('./workers');
|
||||
this.workerPool = new this.workers({
|
||||
size: this.maxWorkers,
|
||||
timeout: this.workerTimeout,
|
||||
network: this.network.get(this.network.primary)
|
||||
});
|
||||
}
|
||||
if (utils.isNumber(options.sigcacheSize))
|
||||
this.sigcache.size = options.sigcacheSize;
|
||||
|
||||
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.
|
||||
* @returns {Number} Adjusted time.
|
||||
@ -429,74 +235,12 @@ Environment.prototype.now = function 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
|
||||
* Environment.
|
||||
*/
|
||||
|
||||
utils.merge(exports, Environment.prototype);
|
||||
exports.set = Environment.prototype.set;
|
||||
exports.now = Environment.prototype.now;
|
||||
|
||||
Environment.call(exports);
|
||||
|
||||
@ -48,12 +48,13 @@ var FREE_THRESHOLD = constants.tx.FREE_THRESHOLD;
|
||||
* @param {String} type
|
||||
*/
|
||||
|
||||
function ConfirmStats(buckets, maxConfirms, decay, type) {
|
||||
function ConfirmStats(buckets, maxConfirms, decay, type, logger) {
|
||||
var i;
|
||||
|
||||
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.decay = decay;
|
||||
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'
|
||||
// + ' %s %d need %s %s: %d from buckets %d - %d.'
|
||||
// + ' 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 blockIndex = height % this.unconfTX.length;
|
||||
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;
|
||||
};
|
||||
|
||||
@ -264,7 +265,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
||||
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;
|
||||
}
|
||||
|
||||
@ -272,7 +273,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
||||
if (this.oldUnconfTX[bucketIndex] > 0) {
|
||||
this.oldUnconfTX[bucketIndex]--;
|
||||
} else {
|
||||
bcoin.debug('estimatefee:'
|
||||
this.logger.debug('estimatefee:'
|
||||
+ ' Mempool tx removed >25 blocks (bucket=%d).',
|
||||
bucketIndex);
|
||||
}
|
||||
@ -281,7 +282,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
||||
if (this.unconfTX[blockIndex][bucketIndex] > 0) {
|
||||
this.unconfTX[blockIndex][bucketIndex]--;
|
||||
} else {
|
||||
bcoin.debug('estimatefee:'
|
||||
this.logger.debug('estimatefee:'
|
||||
+ ' Mempool tx removed (block=%d, bucket=%d).',
|
||||
blockIndex, bucketIndex);
|
||||
}
|
||||
@ -293,8 +294,8 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
ConfirmStats.prototype.toRaw = function toRaw() {
|
||||
var p = new BufferWriter();
|
||||
ConfirmStats.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i;
|
||||
|
||||
function writeArray(buckets) {
|
||||
@ -315,7 +316,10 @@ ConfirmStats.prototype.toRaw = function toRaw() {
|
||||
for (i = 0; i < this.maxConfirms; 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}
|
||||
*/
|
||||
|
||||
ConfirmStats.fromRaw = function fromRaw(data, type) {
|
||||
ConfirmStats.fromRaw = function fromRaw(data, type, logger) {
|
||||
var p = new BufferReader(data);
|
||||
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.');
|
||||
}
|
||||
|
||||
stats = new ConfirmStats(buckets, maxConfirms, decay, type);
|
||||
stats = new ConfirmStats(buckets, maxConfirms, decay, type, logger);
|
||||
|
||||
stats.avg = avg;
|
||||
stats.txAvg = txAvg;
|
||||
@ -386,19 +390,25 @@ ConfirmStats.fromRaw = function fromRaw(data, type) {
|
||||
* @param {Network|NetworkType} network
|
||||
*/
|
||||
|
||||
function PolicyEstimator(minRelay, network) {
|
||||
function PolicyEstimator(minRelay, network, logger) {
|
||||
var fee, priority, boundary;
|
||||
|
||||
if (!(this instanceof PolicyEstimator))
|
||||
return new PolicyEstimator(minRelay, network);
|
||||
return new PolicyEstimator(minRelay, network, logger);
|
||||
|
||||
fee = [];
|
||||
priority = [];
|
||||
|
||||
this.network = bcoin.network.get(network);
|
||||
this.logger = logger || bcoin.defaultLogger;
|
||||
|
||||
this.minTrackedFee = minRelay < MIN_FEERATE
|
||||
? MIN_FEERATE
|
||||
: minRelay;
|
||||
|
||||
fee = [];
|
||||
this.minTrackedPri = FREE_THRESHOLD < MIN_PRIORITY
|
||||
? MIN_PRIORITY
|
||||
: FREE_THRESHOLD;
|
||||
|
||||
for (boundary = this.minTrackedFee;
|
||||
boundary <= MAX_FEERATE;
|
||||
@ -408,16 +418,6 @@ function PolicyEstimator(minRelay, network) {
|
||||
|
||||
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;
|
||||
boundary <= MAX_PRIORITY;
|
||||
boundary *= PRI_SPACING) {
|
||||
@ -426,15 +426,22 @@ function PolicyEstimator(minRelay, network) {
|
||||
|
||||
priority.push(INF_PRIORITY);
|
||||
|
||||
this.feeStats = new ConfirmStats(
|
||||
fee, MAX_BLOCK_CONFIRMS,
|
||||
DEFAULT_DECAY, 'FeeRate',
|
||||
this.logger);
|
||||
|
||||
this.priStats = new ConfirmStats(
|
||||
priority, MAX_BLOCK_CONFIRMS,
|
||||
DEFAULT_DECAY, 'Priority');
|
||||
DEFAULT_DECAY, 'Priority',
|
||||
this.logger);
|
||||
|
||||
this.feeUnlikely = 0;
|
||||
this.feeLikely = INF_FEERATE;
|
||||
this.priUnlikely = 0;
|
||||
this.priLikely = INF_PRIORITY;
|
||||
this.map = {};
|
||||
this.mapSize = 0;
|
||||
this.bestHeight = 0;
|
||||
}
|
||||
|
||||
@ -447,7 +454,7 @@ PolicyEstimator.prototype.removeTX = function removeTX(hash) {
|
||||
var item = this.map[hash];
|
||||
|
||||
if (!item) {
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'estimatefee: Mempool tx %s not found.',
|
||||
utils.revHex(hash));
|
||||
return;
|
||||
@ -456,6 +463,7 @@ PolicyEstimator.prototype.removeTX = function removeTX(hash) {
|
||||
this.feeStats.removeTX(item.blockHeight, this.bestHeight, item.bucketIndex);
|
||||
|
||||
delete this.map[hash];
|
||||
this.mapSize--;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -500,7 +508,7 @@ PolicyEstimator.prototype.processTX = function processTX(entry, current) {
|
||||
var fee, rate, priority;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -520,23 +528,24 @@ PolicyEstimator.prototype.processTX = function processTX(entry, current) {
|
||||
rate = entry.getRate();
|
||||
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)) {
|
||||
this.map[hash] = {
|
||||
blockHeight: height,
|
||||
bucketIndex: this.priStats.addTX(height, priority)
|
||||
};
|
||||
this.mapSize++;
|
||||
} else if (this.isFeePoint(rate, priority)) {
|
||||
this.map[hash] = {
|
||||
blockHeight: height,
|
||||
bucketIndex: this.feeStats.addTX(height, rate)
|
||||
};
|
||||
bcoin.debug('estimatefee: Rate: %d.', this.estimateFee());
|
||||
this.mapSize++;
|
||||
this.logger.debug('estimatefee: Rate: %d.', this.estimateFee());
|
||||
} 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;
|
||||
if (blocks <= 0) {
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'estimatefee: Block tx %s had negative blocks to confirm (%d, %d).',
|
||||
entry.tx.rhash,
|
||||
height,
|
||||
@ -595,7 +604,7 @@ PolicyEstimator.prototype.processBlock = function processBlock(height, entries,
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
bcoin.debug('estimatefee: Recalculating dynamic cutoffs.');
|
||||
this.logger.debug('estimatefee: Recalculating dynamic cutoffs.');
|
||||
|
||||
this.feeLikely = this.feeStats.estimateMedian(
|
||||
2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT,
|
||||
@ -634,9 +643,9 @@ PolicyEstimator.prototype.processBlock = function processBlock(height, entries,
|
||||
this.feeStats.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.',
|
||||
entries.length, Object.keys(this.map).length);
|
||||
entries.length, this.mapSize);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -738,12 +747,18 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
PolicyEstimator.prototype.toRaw = function toRaw() {
|
||||
var p = new BufferWriter();
|
||||
PolicyEstimator.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
p.writeU32(this.network.magic);
|
||||
p.writeU32(this.bestHeight);
|
||||
p.writeVarBytes(this.feeStats.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}
|
||||
*/
|
||||
|
||||
PolicyEstimator.fromRaw = function fromRaw(data, minRelay, network) {
|
||||
PolicyEstimator.fromRaw = function fromRaw(minRelay, data, logger) {
|
||||
var p = new BufferReader(data);
|
||||
var network = bcoin.network.fromMagic(p.readU32());
|
||||
var bestHeight = p.readU32();
|
||||
var feeStats = ConfirmStats.fromRaw(p.readVarBytes(), 'FeeRate');
|
||||
var priStats = ConfirmStats.fromRaw(p.readVarBytes(), 'Priority');
|
||||
var estimator = new PolicyEstimator(minRelay, network);
|
||||
var estimator = new PolicyEstimator(minRelay, network, logger);
|
||||
var feeStats = ConfirmStats.fromRaw(p.readVarBytes(), 'FeeRate', logger);
|
||||
var priStats = ConfirmStats.fromRaw(p.readVarBytes(), 'Priority', logger);
|
||||
|
||||
estimator.bestHeight = bestHeight;
|
||||
estimator.feeStats = feeStats;
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('./env');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var Node = bcoin.node;
|
||||
@ -34,6 +35,7 @@ var Node = bcoin.node;
|
||||
* @param {Object?} options.wallet - Primary {@link Wallet} options.
|
||||
* @property {Boolean} loaded
|
||||
* @property {Chain} chain
|
||||
* @property {PolicyEstimator} fees
|
||||
* @property {Mempool} mempool
|
||||
* @property {Pool} pool
|
||||
* @property {Miner} miner
|
||||
@ -50,18 +52,33 @@ function Fullnode(options) {
|
||||
|
||||
Node.call(this, options);
|
||||
|
||||
// Instantiate blockchain.
|
||||
this.chain = new bcoin.chain({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
profiler: this.profiler,
|
||||
db: this.db,
|
||||
location: this.location('chain'),
|
||||
preload: false,
|
||||
spv: false,
|
||||
prune: this.options.prune,
|
||||
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.
|
||||
this.mempool = new bcoin.mempool({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
chain: this.chain,
|
||||
fees: this.fees,
|
||||
db: 'memory',
|
||||
location: this.location('mempool'),
|
||||
limitFree: this.options.limitFree,
|
||||
limitFreeRelay: this.options.limitFreeRelay,
|
||||
requireStandard: this.options.requireStandard,
|
||||
@ -72,26 +89,36 @@ function Fullnode(options) {
|
||||
// Pool needs access to the chain and mempool.
|
||||
this.pool = new bcoin.pool({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
chain: this.chain,
|
||||
mempool: this.mempool,
|
||||
witness: this.network.witness,
|
||||
selfish: this.options.selfish,
|
||||
headers: this.options.headers,
|
||||
proxyServer: this.options.proxyServer,
|
||||
preferredSeed: this.options.preferredSeed,
|
||||
spv: false
|
||||
});
|
||||
|
||||
// Miner needs access to the chain and mempool.
|
||||
this.miner = new bcoin.miner({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
chain: this.chain,
|
||||
mempool: this.mempool,
|
||||
fees: this.fees,
|
||||
address: this.options.payoutAddress,
|
||||
coinbaseFlags: this.options.coinbaseFlags,
|
||||
parallel: this.options.parallel
|
||||
});
|
||||
|
||||
// Wallet database needs access to fees.
|
||||
this.walletdb = new bcoin.walletdb({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
fees: this.fees,
|
||||
db: this.db,
|
||||
location: this.location('walletdb'),
|
||||
verify: false
|
||||
});
|
||||
|
||||
@ -99,6 +126,7 @@ function Fullnode(options) {
|
||||
if (!utils.isBrowser) {
|
||||
this.http = new bcoin.http.server({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
node: this,
|
||||
key: this.options.sslKey,
|
||||
cert: this.options.sslCert,
|
||||
@ -122,28 +150,28 @@ Fullnode.prototype._init = function _init() {
|
||||
|
||||
// Bind to errors
|
||||
this.mempool.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
this.miner.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
this.pool.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
this.chain.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
this.walletdb.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
if (this.http) {
|
||||
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) {
|
||||
self.walletdb.addTX(tx, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
});
|
||||
|
||||
@ -175,14 +203,14 @@ Fullnode.prototype._init = function _init() {
|
||||
return;
|
||||
self.mempool.addBlock(block, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
});
|
||||
|
||||
this.chain.on('remove block', function(block) {
|
||||
self.walletdb.removeBlock(block, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
if (!self.chain.isFull())
|
||||
@ -190,7 +218,7 @@ Fullnode.prototype._init = function _init() {
|
||||
|
||||
self.mempool.removeBlock(block, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
});
|
||||
|
||||
@ -214,7 +242,7 @@ Fullnode.prototype._open = function open(callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
bcoin.debug('Node is loaded.');
|
||||
self.logger.info('Node is loaded.');
|
||||
|
||||
callback();
|
||||
}
|
||||
@ -256,7 +284,7 @@ Fullnode.prototype._open = function open(callback) {
|
||||
return next(err);
|
||||
|
||||
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++)
|
||||
self.pool.broadcast(txs[i]);
|
||||
@ -385,13 +413,14 @@ Fullnode.prototype.stopSync = function stopSync() {
|
||||
*/
|
||||
|
||||
Fullnode.prototype.createWallet = function createWallet(options, callback) {
|
||||
var self = this;
|
||||
this.walletdb.ensure(options, function(err, wallet) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
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());
|
||||
|
||||
return callback(null, wallet);
|
||||
|
||||
@ -73,8 +73,6 @@ HTTPClient.prototype._open = function _open(callback) {
|
||||
|
||||
this.socket.on('connect', function() {
|
||||
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.');
|
||||
});
|
||||
|
||||
|
||||
@ -41,6 +41,7 @@ function HTTPServer(options) {
|
||||
this.walletdb = this.node.walletdb;
|
||||
this.mempool = this.node.mempool;
|
||||
this.pool = this.node.pool;
|
||||
this.logger = options.logger || this.node.logger;
|
||||
this.loaded = false;
|
||||
|
||||
options.sockets = true;
|
||||
@ -61,7 +62,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
@ -90,8 +91,8 @@ HTTPServer.prototype._init = function _init() {
|
||||
var params = utils.merge({}, req.params, req.query, req.body);
|
||||
var options = {};
|
||||
|
||||
bcoin.debug('Params:');
|
||||
bcoin.debug(params);
|
||||
self.logger.debug('Params:');
|
||||
self.logger.debug(params);
|
||||
|
||||
if (params.id) {
|
||||
assert(params.id !== '!all');
|
||||
@ -884,7 +885,7 @@ HTTPServer.prototype.listen = function listen(port, host, callback) {
|
||||
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);
|
||||
|
||||
self.loaded = true;
|
||||
|
||||
@ -9,11 +9,9 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('./env');
|
||||
var LowlevelUp = require('./lowlevelup');
|
||||
var utils = bcoin.utils;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var db = {};
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
@ -33,32 +31,28 @@ var db = {};
|
||||
function ldb(options) {
|
||||
options = ldb.parseOptions(options);
|
||||
|
||||
if (!db[options.location]) {
|
||||
if (options.backend !== 'rbt' && !bcoin.isBrowser)
|
||||
bcoin.mkdir(options.location, true);
|
||||
if (options.backend !== 'rbt')
|
||||
utils.mkdir(options.location, true);
|
||||
|
||||
db[options.location] = new LowlevelUp(options.location, {
|
||||
// LevelDB and others
|
||||
createIfMissing: true,
|
||||
errorIfExists: false,
|
||||
compression: options.compression !== false,
|
||||
cacheSize: options.cacheSize || (8 << 20),
|
||||
writeBufferSize: options.writeBufferSize || (4 << 20),
|
||||
maxOpenFiles: options.maxOpenFiles || 8192,
|
||||
filterBits: 0,
|
||||
paranoidChecks: false,
|
||||
memory: false,
|
||||
return new LowlevelUp(options.location, {
|
||||
// LevelDB and others
|
||||
createIfMissing: true,
|
||||
errorIfExists: false,
|
||||
compression: options.compression !== false,
|
||||
cacheSize: options.cacheSize || (8 << 20),
|
||||
writeBufferSize: options.writeBufferSize || (4 << 20),
|
||||
maxOpenFiles: options.maxOpenFiles || 8192,
|
||||
filterBits: 0,
|
||||
paranoidChecks: false,
|
||||
memory: false,
|
||||
|
||||
// For LMDB if we decide to use it:
|
||||
sync: options.sync || false,
|
||||
mapSize: options.mapSize || 300 * (1024 << 20),
|
||||
writeMap: options.writeMap || false,
|
||||
// For LMDB if we decide to use it:
|
||||
sync: options.sync || false,
|
||||
mapSize: options.mapSize || 300 * (1024 << 20),
|
||||
writeMap: options.writeMap || false,
|
||||
|
||||
db: options.db
|
||||
});
|
||||
}
|
||||
|
||||
return db[options.location];
|
||||
db: options.db
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,7 +65,7 @@ ldb.getBackend = function getBackend(db) {
|
||||
var name, ext;
|
||||
|
||||
if (!db)
|
||||
db = bcoin.db || 'leveldb';
|
||||
db = 'memory';
|
||||
|
||||
if (db === 'leveldb')
|
||||
name = 'leveldown';
|
||||
@ -112,30 +106,26 @@ ldb.getBackend = function getBackend(db) {
|
||||
*/
|
||||
|
||||
ldb.parseOptions = function parseOptions(options) {
|
||||
var network = bcoin.network.get(options.network);
|
||||
var backend = ldb.getBackend(options.db);
|
||||
var location = options.location;
|
||||
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')
|
||||
db = require('./rbt');
|
||||
else if (bcoin.isBrowser)
|
||||
else if (utils.isBrowser)
|
||||
db = require('level-js');
|
||||
else
|
||||
db = require(backend.name);
|
||||
|
||||
if (typeof location !== 'string') {
|
||||
assert(backend.name === 'rbt', 'Location required.');
|
||||
location = 'rbt';
|
||||
}
|
||||
|
||||
return utils.merge({}, options, {
|
||||
backend: backend.name,
|
||||
ext: backend.ext,
|
||||
location: location,
|
||||
location: location + '.' + backend.ext,
|
||||
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.chain = options.chain;
|
||||
this.fees = options.fees;
|
||||
|
||||
assert(this.chain, 'Mempool requires a blockchain.');
|
||||
|
||||
this.network = this.chain.network;
|
||||
this.logger = options.logger || this.chain.logger;
|
||||
this.loaded = false;
|
||||
|
||||
this.locker = new bcoin.locker(this, this.addTX);
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || 'mempool',
|
||||
location: this.options.location,
|
||||
db: this.options.db || 'memory'
|
||||
});
|
||||
@ -243,7 +243,8 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
|
||||
self.blockSinceBump = true;
|
||||
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();
|
||||
});
|
||||
@ -373,7 +374,7 @@ Mempool.prototype.limitOrphans = function limitOrphans() {
|
||||
hash = orphans[i];
|
||||
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);
|
||||
}
|
||||
@ -830,9 +831,10 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
|
||||
self.emit('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);
|
||||
|
||||
@ -841,7 +843,7 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
|
||||
self.verify(entry, function(err) {
|
||||
if (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
bcoin.debug('Could not resolve orphan %s: %s.',
|
||||
self.logger.debug('Could not resolve orphan %s: %s.',
|
||||
tx.rhash,
|
||||
err.message);
|
||||
self.emit('bad orphan', tx, entry);
|
||||
@ -855,7 +857,7 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
|
||||
self.emit('error', err);
|
||||
return next();
|
||||
}
|
||||
bcoin.debug('Resolved orphan %s in mempool.', entry.tx.rhash);
|
||||
self.logger.debug('Resolved orphan %s in mempool.', entry.tx.rhash);
|
||||
next();
|
||||
}, true);
|
||||
});
|
||||
@ -897,7 +899,8 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit, callb
|
||||
self.size -= self.memUsage(entry.tx);
|
||||
self.total--;
|
||||
|
||||
self.network.fees.removeTX(hash);
|
||||
if (self.fees)
|
||||
self.fees.removeTX(hash);
|
||||
|
||||
if (limit) {
|
||||
rate = bcoin.tx.getRate(entry.sizes, entry.fees);
|
||||
@ -1144,7 +1147,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx) {
|
||||
var i, hash, input, prev;
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -1171,7 +1174,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx) {
|
||||
this.orphans[hash] = tx.toExtended(true);
|
||||
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);
|
||||
|
||||
@ -1242,7 +1245,7 @@ Mempool.prototype.getOrphan = function getOrphan(hash) {
|
||||
orphan = bcoin.tx.fromExtended(orphan, true);
|
||||
} catch (e) {
|
||||
delete this.orphans[hash];
|
||||
bcoin.debug('%s %s',
|
||||
this.logger.warning('%s %s',
|
||||
'Warning: possible memory corruption.',
|
||||
'Orphan failed deserialization.');
|
||||
return;
|
||||
|
||||
@ -40,12 +40,14 @@ function Miner(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.pool = options.pool;
|
||||
this.chain = options.chain;
|
||||
this.logger = options.logger || this.chain.logger;
|
||||
this.mempool = options.mempool;
|
||||
this.fees = this.mempool ? this.mempool.fees : options.fees;
|
||||
|
||||
assert(this.chain, 'Miner requires a blockchain.');
|
||||
|
||||
@ -57,7 +59,6 @@ function Miner(options) {
|
||||
|
||||
if (bcoin.useWorkers) {
|
||||
this.workerPool = new bcoin.workers({
|
||||
network: this.network,
|
||||
size: this.options.parallel ? 2 : 1,
|
||||
timeout: -1
|
||||
});
|
||||
@ -103,12 +104,12 @@ Miner.prototype._init = function _init() {
|
||||
|
||||
this.on('block', function(block) {
|
||||
// Emit the block hex as a failsafe (in case we can't send it)
|
||||
bcoin.debug('Found block: %d (%s).', block.height, block.rhash);
|
||||
bcoin.debug('Raw: %s', block.toRaw().toString('hex'));
|
||||
self.logger.info('Found block: %d (%s).', block.height, block.rhash);
|
||||
self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
|
||||
});
|
||||
|
||||
this.on('status', function(stat) {
|
||||
bcoin.debug(
|
||||
self.logger.info(
|
||||
'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
|
||||
stat.hashrate / 1000 | 0,
|
||||
stat.hashes,
|
||||
@ -177,7 +178,7 @@ Miner.prototype.start = function start(version) {
|
||||
self.chain.add(block, function(err) {
|
||||
if (err) {
|
||||
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);
|
||||
return self.start();
|
||||
}
|
||||
@ -700,10 +701,11 @@ MinerBlock.prototype.toRaw = function toRaw(writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i;
|
||||
|
||||
p.writeU32(this.network.magic);
|
||||
p.writeBytes(this.tip.toRaw());
|
||||
p.writeU32(this.block.version);
|
||||
p.writeU32(this.block.bits);
|
||||
p.writeVarString(this.address, 'ascii');
|
||||
p.writeVarBytes(this.address.toRaw());
|
||||
p.writeVarString(this.coinbaseFlags, 'utf8');
|
||||
p.writeU8(this.witness ? 1 : 0);
|
||||
p.writeVarint(this.block.txs.length - 1);
|
||||
@ -725,10 +727,11 @@ MinerBlock.prototype.toRaw = function toRaw(writer) {
|
||||
|
||||
MinerBlock.fromRaw = function fromRaw(data) {
|
||||
var p = new BufferReader(data);
|
||||
var network = bcoin.network.fromMagic(p.readU32());
|
||||
var tip = bcoin.chainentry.fromRaw(null, p);
|
||||
var version = p.readU32();
|
||||
var bits = p.readU32();
|
||||
var address = p.readVarString('ascii');
|
||||
var address = bcoin.address.fromRaw(p.readVarBytes());
|
||||
var coinbaseFlags = p.readVarString('utf8');
|
||||
var witness = p.readU8() === 1;
|
||||
var count = p.readVarint();
|
||||
@ -738,7 +741,10 @@ MinerBlock.fromRaw = function fromRaw(data) {
|
||||
for (i = 0; i < count; i++)
|
||||
txs.push(bcoin.tx.fromRaw(p));
|
||||
|
||||
tip.network = network;
|
||||
|
||||
return new MinerBlock({
|
||||
network: network,
|
||||
tip: tip,
|
||||
version: version,
|
||||
target: bits,
|
||||
|
||||
@ -9,9 +9,7 @@
|
||||
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var constants = require('./protocol/constants');
|
||||
var networks = require('./protocol/network');
|
||||
var PolicyEstimator = require('./fees');
|
||||
|
||||
/**
|
||||
* Represents a network.
|
||||
@ -57,8 +55,6 @@ function Network(options) {
|
||||
this.maxRate = options.maxRate;
|
||||
this.selfConnect = options.selfConnect;
|
||||
this.requestMempool = options.requestMempool;
|
||||
|
||||
this.fees = new PolicyEstimator(constants.tx.MIN_RELAY, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
var bcoin = require('./env');
|
||||
var AsyncObject = require('./async');
|
||||
var utils = require('./utils');
|
||||
var Profiler;
|
||||
|
||||
/**
|
||||
* Base class from which every other
|
||||
@ -26,22 +27,213 @@ function Node(options) {
|
||||
|
||||
AsyncObject.call(this);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
options = this._parseOptions(options);
|
||||
|
||||
this.options = options;
|
||||
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.pool = null;
|
||||
this.chain = null;
|
||||
this.fees = null;
|
||||
this.miner = null;
|
||||
this.walletdb = 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
@ -76,6 +76,7 @@ function Peer(pool, options) {
|
||||
|
||||
this.options = options;
|
||||
this.pool = pool;
|
||||
this.logger = pool.logger;
|
||||
this.socket = null;
|
||||
this.host = null;
|
||||
this.port = 0;
|
||||
@ -236,7 +237,7 @@ Peer.prototype._onConnect = function _onConnect() {
|
||||
|
||||
// Wait for _their_ version.
|
||||
if (!self.version) {
|
||||
bcoin.debug(
|
||||
self.logger.debug(
|
||||
'Peer sent a verack without a version (%s).',
|
||||
self.hostname);
|
||||
self.request('version', callee);
|
||||
@ -287,7 +288,7 @@ Peer.prototype._onConnect = function _onConnect() {
|
||||
self.sendMempool();
|
||||
}
|
||||
|
||||
bcoin.debug('Received verack (%s).', self.hostname);
|
||||
self.logger.debug('Received verack (%s).', self.hostname);
|
||||
|
||||
// Finally we can let the pool know
|
||||
// that this peer is ready to go.
|
||||
@ -321,19 +322,19 @@ Peer.prototype.createSocket = function createSocket(port, host) {
|
||||
if (this._createSocket) {
|
||||
socket = this._createSocket(port, host);
|
||||
} else {
|
||||
if (bcoin.isBrowser) {
|
||||
if (utils.isBrowser) {
|
||||
proxy = require('../../browser/proxysocket');
|
||||
socket = proxy.connect(bcoin.proxyServer, port, host);
|
||||
socket = proxy.connect(this.pool.proxyServer, port, host);
|
||||
} else {
|
||||
net = require('n' + 'et');
|
||||
socket = net.connect(port, host);
|
||||
}
|
||||
}
|
||||
|
||||
bcoin.debug('Connecting to %s.', hostname);
|
||||
this.logger.debug('Connecting to %s.', hostname);
|
||||
|
||||
socket.once('connect', function() {
|
||||
bcoin.debug('Connected to %s.', hostname);
|
||||
self.logger.info('Connected to %s.', hostname);
|
||||
});
|
||||
|
||||
this._connectTimeout = setTimeout(function() {
|
||||
@ -413,7 +414,7 @@ Peer.prototype.sendInv = function sendInv(items) {
|
||||
if (items.length === 0)
|
||||
return;
|
||||
|
||||
bcoin.debug('Serving %d inv items to %s.',
|
||||
this.logger.debug('Serving %d inv items to %s.',
|
||||
items.length, this.hostname);
|
||||
|
||||
for (i = 0; i < items.length; i += 50000) {
|
||||
@ -442,7 +443,7 @@ Peer.prototype.sendHeaders = function sendHeaders(items) {
|
||||
if (items.length === 0)
|
||||
return;
|
||||
|
||||
bcoin.debug('Serving %d headers to %s.',
|
||||
this.logger.debug('Serving %d headers to %s.',
|
||||
items.length, this.hostname);
|
||||
|
||||
for (i = 0; i < items.length; i += 2000) {
|
||||
@ -485,7 +486,7 @@ Peer.prototype.sendPing = function sendPing() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -792,7 +793,7 @@ Peer.prototype._onPacket = function onPacket(packet) {
|
||||
this.fire(cmd, payload);
|
||||
break;
|
||||
default:
|
||||
bcoin.debug('Unknown packet: %s.', cmd);
|
||||
this.logger.warning('Unknown packet: %s.', cmd);
|
||||
this.fire(cmd, payload);
|
||||
break;
|
||||
}
|
||||
@ -876,7 +877,7 @@ Peer.prototype._handleFilterClear = function _handleFilterClear(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);
|
||||
this.fire('utxos', payload);
|
||||
};
|
||||
@ -1333,7 +1334,7 @@ Peer.prototype._handleMempool = function _handleMempool() {
|
||||
for (i = 0; i < hashes.length; 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);
|
||||
done();
|
||||
@ -1383,13 +1384,13 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
|
||||
}
|
||||
|
||||
if ((item.type & ~constants.WITNESS_MASK) !== entry.type) {
|
||||
bcoin.debug(
|
||||
self.logger.debug(
|
||||
'Peer requested an existing item with the wrong type (%s).',
|
||||
this.hostname);
|
||||
continue;
|
||||
}
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Peer requested %s %s as a %s packet (%s).',
|
||||
entry.type === constants.inv.TX ? 'tx' : 'block',
|
||||
utils.revHex(entry.hash),
|
||||
@ -1530,7 +1531,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
|
||||
bcoin.debug(
|
||||
self.logger.debug(
|
||||
'Served %d items with getdata (notfound=%d) (%s).',
|
||||
items.length - notfound.length,
|
||||
notfound.length,
|
||||
@ -1555,7 +1556,7 @@ Peer.prototype._handleAddr = function _handleAddr(addrs) {
|
||||
for (i = 0; i < addrs.length; i++)
|
||||
this.addrFilter.add(addrs[i].host, 'ascii');
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Received %d addrs (hosts=%d, peers=%d) (%s).',
|
||||
addrs.length,
|
||||
this.pool.hosts.length,
|
||||
@ -1586,17 +1587,17 @@ Peer.prototype._handlePong = function _handlePong(data) {
|
||||
var now = utils.ms();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (data.nonce.cmp(this.challenge) !== 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;
|
||||
return;
|
||||
}
|
||||
bcoin.debug('Peer sent the wrong nonce (%s).', this.hostname);
|
||||
this.logger.debug('Peer sent the wrong nonce (%s).', this.hostname);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1606,7 +1607,7 @@ Peer.prototype._handlePong = function _handlePong(data) {
|
||||
this.minPing = now - this.lastPing;
|
||||
this.minPing = Math.min(this.minPing, now - this.lastPing);
|
||||
} else {
|
||||
bcoin.debug('Timing mismatch (what?) (%s).', this.hostname);
|
||||
this.logger.debug('Timing mismatch (what?) (%s).', this.hostname);
|
||||
}
|
||||
|
||||
this.challenge = null;
|
||||
@ -1645,7 +1646,7 @@ Peer.prototype._handleGetAddr = function _handleGetAddr() {
|
||||
if (items.length === 0)
|
||||
return;
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Sending %d addrs to peer (%s)',
|
||||
items.length,
|
||||
this.hostname);
|
||||
@ -1691,7 +1692,7 @@ Peer.prototype._handleInv = function _handleInv(items) {
|
||||
this.emit('txs', txs);
|
||||
|
||||
if (unknown != null) {
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Peer sent an unknown inv type: %d (%s).',
|
||||
unknown, this.hostname);
|
||||
}
|
||||
@ -1767,11 +1768,11 @@ Peer.prototype.sendAlert = function sendAlert(alert) {
|
||||
Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
|
||||
var packet = new GetBlocksPacket(locator, stop);
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Requesting headers packet from peer with getheaders (%s).',
|
||||
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 ? utils.revHex(locator[0]) : 0,
|
||||
stop ? utils.revHex(stop) : 0);
|
||||
@ -1788,11 +1789,11 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
|
||||
Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
|
||||
var packet = new GetBlocksPacket(locator, stop);
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Requesting inv packet from peer with getblocks (%s).',
|
||||
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 ? utils.revHex(locator[0]) : 0,
|
||||
stop ? utils.revHex(stop) : 0);
|
||||
@ -1805,7 +1806,7 @@ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
|
||||
*/
|
||||
|
||||
Peer.prototype.sendMempool = function sendMempool() {
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Requesting inv packet from peer with mempool (%s).',
|
||||
this.hostname);
|
||||
|
||||
@ -1821,16 +1822,16 @@ Peer.prototype.sendReject = function sendReject(code, reason, obj) {
|
||||
var reject = RejectPacket.fromReason(code, reason, 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);
|
||||
|
||||
this.pool.rejects.add(obj.hash());
|
||||
} 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);
|
||||
}
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Sending reject packet to peer (%s).',
|
||||
this.hostname);
|
||||
|
||||
@ -1894,7 +1895,7 @@ Peer.prototype.resolveOrphan = function resolveOrphan(tip, orphan, callback) {
|
||||
|
||||
// Was probably resolved.
|
||||
if (!root) {
|
||||
bcoin.debug('Orphan root was already resolved.');
|
||||
self.logger.debug('Orphan root was already resolved.');
|
||||
return callback();
|
||||
}
|
||||
|
||||
|
||||
@ -92,6 +92,7 @@ function Pool(options) {
|
||||
|
||||
this.options = options;
|
||||
this.chain = options.chain;
|
||||
this.logger = options.logger || this.chain.logger;
|
||||
this.mempool = options.mempool;
|
||||
|
||||
assert(this.chain, 'Pool requires a blockchain.');
|
||||
@ -106,9 +107,9 @@ function Pool(options) {
|
||||
|
||||
seeds = options.seeds || this.network.seeds;
|
||||
|
||||
if (process.env.BCOIN_SEED) {
|
||||
if (options.preferredSeed) {
|
||||
seeds = seeds.slice();
|
||||
seeds.unshift(process.env.BCOIN_SEED);
|
||||
seeds.unshift(options.preferredSeed);
|
||||
}
|
||||
|
||||
this.seeds = [];
|
||||
@ -135,6 +136,7 @@ function Pool(options) {
|
||||
this.uid = 0;
|
||||
this._createServer = options.createServer;
|
||||
this.locker = new bcoin.locker(this);
|
||||
this.proxyServer = options.proxyServer;
|
||||
|
||||
this.syncing = false;
|
||||
this.synced = false;
|
||||
@ -273,7 +275,7 @@ Pool.prototype._init = function _init() {
|
||||
self.synced = true;
|
||||
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;
|
||||
this.getIP(function(err, ip) {
|
||||
if (err)
|
||||
bcoin.error(err);
|
||||
self.logger.error(err);
|
||||
|
||||
if (ip) {
|
||||
self.address.host = ip;
|
||||
bcoin.debug('External IP found: %s.', ip);
|
||||
self.logger.info('External IP found: %s.', ip);
|
||||
}
|
||||
|
||||
if (self.mempool)
|
||||
@ -404,7 +406,7 @@ Pool.prototype.listen = function listen(callback) {
|
||||
if (this._createServer) {
|
||||
this.server = this._createServer();
|
||||
} else {
|
||||
if (bcoin.isBrowser)
|
||||
if (utils.isBrowser)
|
||||
return utils.nextTick(callback);
|
||||
net = require('n' + 'et');
|
||||
this.server = new net.Server();
|
||||
@ -414,7 +416,7 @@ Pool.prototype.listen = function listen(callback) {
|
||||
var hostname, host;
|
||||
|
||||
if (!socket.remoteAddress) {
|
||||
bcoin.debug('Ignoring disconnected leech.');
|
||||
self.logger.debug('Ignoring disconnected leech.');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
@ -423,14 +425,14 @@ Pool.prototype.listen = function listen(callback) {
|
||||
|
||||
if (self.peers.leeches.length >= self.maxLeeches) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isMisbehaving(host)) {
|
||||
hostname = IP.hostname(host, socket.remotePort);
|
||||
bcoin.debug('Ignoring misbehaving leech (%s).', hostname);
|
||||
self.logger.debug('Ignoring misbehaving leech (%s).', hostname);
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
@ -440,7 +442,7 @@ Pool.prototype.listen = function listen(callback) {
|
||||
|
||||
this.server.on('listening', function() {
|
||||
var data = self.server.address();
|
||||
bcoin.debug(
|
||||
self.logger.info(
|
||||
'Bitcoin server listening on %s (port=%d).',
|
||||
data.address, data.port);
|
||||
});
|
||||
@ -456,7 +458,7 @@ Pool.prototype.listen = function listen(callback) {
|
||||
Pool.prototype.unlisten = function unlisten(callback) {
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (bcoin.isBrowser)
|
||||
if (utils.isBrowser)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
if (!this.server)
|
||||
@ -486,7 +488,7 @@ Pool.prototype._startTimer = function _startTimer() {
|
||||
|
||||
if (self.peers.load) {
|
||||
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())
|
||||
return self._startTimer();
|
||||
|
||||
bcoin.debug('Warning: Stalling.');
|
||||
self.logger.warning('Stalling.');
|
||||
}
|
||||
|
||||
this._interval = setInterval(load, this.load.interval);
|
||||
@ -573,7 +575,7 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
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.all.push(peer);
|
||||
@ -666,7 +668,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer, callback)
|
||||
if (!this.options.headers)
|
||||
return callback();
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Received %s headers from peer (%s).',
|
||||
headers.length,
|
||||
peer.hostname);
|
||||
@ -734,7 +736,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
|
||||
|
||||
assert(!this.options.headers);
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Received %s block hashes from peer (%s).',
|
||||
hashes.length,
|
||||
peer.hostname);
|
||||
@ -757,7 +759,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
|
||||
// should probably actually move to the
|
||||
// `exists` clause below if it is the last
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -775,11 +777,11 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
|
||||
if (exists && i === hashes.length - 1) {
|
||||
// Make sure we _actually_ have this block.
|
||||
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);
|
||||
}
|
||||
// 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();
|
||||
@ -850,7 +852,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
||||
// us requesting them.
|
||||
if (!requested) {
|
||||
peer.invFilter.add(block.hash());
|
||||
bcoin.debug(
|
||||
this.logger.warning(
|
||||
'Received unrequested block: %s (%s).',
|
||||
block.rhash, peer.hostname);
|
||||
return utils.nextTick(callback);
|
||||
@ -871,7 +873,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
||||
peer.setMisbehavior(10);
|
||||
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) {
|
||||
self.scheduleRequests(peer);
|
||||
return callback(e || err);
|
||||
@ -887,8 +889,8 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
||||
|
||||
self.emit('chain-progress', self.chain.getProgress(), peer);
|
||||
|
||||
if (self.chain.total % 20 === 0) {
|
||||
bcoin.debug('Status:'
|
||||
if (self.logger.level === 4 && self.chain.total % 20 === 0) {
|
||||
self.logger.debug('Status:'
|
||||
+ ' tip=%s ts=%s height=%d progress=%s'
|
||||
+ ' blocks=%d orphans=%d active=%d'
|
||||
+ ' queue=%d target=%s peers=%d'
|
||||
@ -908,6 +910,13 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
||||
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();
|
||||
});
|
||||
};
|
||||
@ -979,7 +988,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
||||
self._stopTimer();
|
||||
|
||||
if (self.peers.regular.length === 0) {
|
||||
bcoin.debug('%s %s %s',
|
||||
self.logger.warning('%s %s %s',
|
||||
'Could not connect to any peers.',
|
||||
'Do you have a network connection?',
|
||||
'Retrying in 5 seconds.');
|
||||
@ -1041,7 +1050,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
||||
? utils.revHex(payload.data)
|
||||
: null;
|
||||
|
||||
bcoin.debug(
|
||||
self.logger.warning(
|
||||
'Received reject (%s): msg=%s code=%s reason=%s data=%s.',
|
||||
peer.hostname,
|
||||
payload.message,
|
||||
@ -1121,7 +1130,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
||||
if (version.height > self.block.versionHeight)
|
||||
self.block.versionHeight = version.height;
|
||||
|
||||
bcoin.debug(
|
||||
self.logger.info(
|
||||
'Received version (%s): version=%d height=%d services=%s agent=%s',
|
||||
peer.hostname,
|
||||
version.version,
|
||||
@ -1171,21 +1180,21 @@ Pool.prototype._handleAlert = function _handleAlert(alert, peer) {
|
||||
return;
|
||||
|
||||
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?
|
||||
bcoin.debug(alert);
|
||||
this.logger.debug(alert);
|
||||
peer.setMisbehavior(100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (now >= alert.relayUntil || now >= alert.expiration) {
|
||||
bcoin.debug('Peer sent an expired alert packet (%s).', peer.hostname);
|
||||
bcoin.debug(alert);
|
||||
this.logger.warning('Peer sent an expired alert packet (%s).', peer.hostname);
|
||||
this.logger.debug(alert);
|
||||
return;
|
||||
}
|
||||
|
||||
bcoin.debug('Received alert from peer (%s).', peer.hostname);
|
||||
bcoin.debug(alert);
|
||||
this.logger.warning('Received alert from peer (%s).', peer.hostname);
|
||||
this.logger.warning(alert);
|
||||
|
||||
this.sendAlert(alert);
|
||||
|
||||
@ -1215,7 +1224,7 @@ Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
|
||||
if (!this.mempool)
|
||||
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);
|
||||
|
||||
if (this.rejects.test(tx.hash())) {
|
||||
@ -1268,7 +1277,7 @@ Pool.prototype._addLeech = function _addLeech(socket) {
|
||||
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.all.push(peer);
|
||||
@ -1340,7 +1349,7 @@ Pool.prototype._removePeer = function _removePeer(peer) {
|
||||
delete this.peers.map[peer.host];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1454,7 +1463,7 @@ Pool.prototype.getData = function getData(peer, type, hash, options, callback) {
|
||||
if (type === self.tx.type) {
|
||||
if (peer.queue.tx.length === 0) {
|
||||
utils.nextTick(function() {
|
||||
bcoin.debug(
|
||||
self.logger.debug(
|
||||
'Requesting %d/%d txs from peer with getdata (%s).',
|
||||
peer.queue.tx.length,
|
||||
self.request.activeTX,
|
||||
@ -1510,7 +1519,7 @@ Pool.prototype.has = function has(type, hash, force, callback) {
|
||||
} else {
|
||||
// If we recently rejected this item. Ignore.
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1603,7 +1612,7 @@ Pool.prototype._sendRequests = function _sendRequests(peer) {
|
||||
for (i = 0; i < items.length; i++)
|
||||
items[i] = items[i].start();
|
||||
|
||||
bcoin.debug(
|
||||
this.logger.debug(
|
||||
'Requesting %d/%d blocks from peer with getdata (%s).',
|
||||
items.length,
|
||||
this.request.activeBlocks,
|
||||
@ -1771,7 +1780,7 @@ Pool.prototype.fillCoins = function fillCoins(tx, callback) {
|
||||
Pool.prototype.getLoaderHost = function getLoaderHost() {
|
||||
var host;
|
||||
|
||||
if (!this.connected && process.env.BCOIN_SEED)
|
||||
if (!this.connected && this.options.preferredSeed)
|
||||
return this.seeds[0];
|
||||
|
||||
host = this.getRandom(this.seeds);
|
||||
@ -1892,7 +1901,7 @@ Pool.prototype.setMisbehavior = function setMisbehavior(peer, score) {
|
||||
if (peer.banScore >= constants.BAN_SCORE) {
|
||||
this.peers.misbehaving[peer.host] = utils.now();
|
||||
this.removeHost(peer.host);
|
||||
bcoin.debug('Ban threshold exceeded (%s).', peer.host);
|
||||
this.logger.debug('Ban threshold exceeded (%s).', peer.host);
|
||||
peer.destroy();
|
||||
return true;
|
||||
}
|
||||
@ -2035,7 +2044,8 @@ LoadRequest.prototype.destroy = function destroy() {
|
||||
LoadRequest.prototype._onTimeout = function _onTimeout() {
|
||||
if (this.type === this.pool.block.type
|
||||
&& 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();
|
||||
}
|
||||
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.
|
||||
if (this.msg.isCoinbase()) {
|
||||
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.'));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7,13 +7,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @exports profiler
|
||||
*/
|
||||
|
||||
var profiler = exports;
|
||||
|
||||
var bcoin = require('./env');
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var fs, v8profiler;
|
||||
@ -22,7 +15,7 @@ function ensure() {
|
||||
if (v8profiler)
|
||||
return;
|
||||
|
||||
if (bcoin.profile && !bcoin.isBrowser) {
|
||||
if (!utils.isBrowser) {
|
||||
v8profiler = require('v8-' + 'profiler');
|
||||
fs = require('f' + 's');
|
||||
}
|
||||
@ -32,14 +25,15 @@ function ensure() {
|
||||
* A CPU profile.
|
||||
* @exports Profile
|
||||
* @constructor
|
||||
* @param {String} prefix
|
||||
* @param {String?} name
|
||||
*/
|
||||
|
||||
function Profile(name) {
|
||||
if (v8profiler && bcoin.profile) {
|
||||
function Profile(prefix, name) {
|
||||
if (v8profiler) {
|
||||
name = 'profile-' + (name ? name + '-' : '') + utils.ms();
|
||||
bcoin.debug('Starting CPU profile: %s', name);
|
||||
v8profiler.startProfiling(name, true);
|
||||
this.prefix = prefix;
|
||||
this.name = name;
|
||||
this.profile = null;
|
||||
this.finished = false;
|
||||
@ -93,8 +87,6 @@ Profile.prototype.save = function save(callback) {
|
||||
if (!this.profile)
|
||||
this.stop();
|
||||
|
||||
bcoin.debug('Saving CPU profile: %s', this.name);
|
||||
|
||||
return this.profile['export'](function(err, result) {
|
||||
var file;
|
||||
|
||||
@ -105,12 +97,12 @@ Profile.prototype.save = function save(callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
file = bcoin.prefix
|
||||
file = self.prefix
|
||||
+ '/profiler/'
|
||||
+ self.name
|
||||
+ '.cpuprofile';
|
||||
|
||||
bcoin.mkdir(file, true);
|
||||
utils.mkdir(file, true);
|
||||
|
||||
fs.writeFile(file, result, callback);
|
||||
});
|
||||
@ -120,14 +112,15 @@ Profile.prototype.save = function save(callback) {
|
||||
* Memory Snapshot
|
||||
* @exports Snapshot
|
||||
* @constructor
|
||||
* @param {String} prefix
|
||||
* @param {String?} name
|
||||
*/
|
||||
|
||||
function Snapshot(name) {
|
||||
if (v8profiler && bcoin.profile) {
|
||||
function Snapshot(prefix, name) {
|
||||
if (v8profiler) {
|
||||
name = 'snapshot-' + (name ? name + '-' : '') + utils.ms();
|
||||
bcoin.debug('Taking heap snapshot: %s', name);
|
||||
this.snapshot = v8profiler.takeSnapshot(name);
|
||||
this.prefix = prefix;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@ -189,8 +182,6 @@ Snapshot.prototype.save = function save(callback) {
|
||||
|
||||
assert(this.snapshot);
|
||||
|
||||
bcoin.debug('Saving heap snapshot: %s', this.name);
|
||||
|
||||
return this.snapshot['export'](function(err, result) {
|
||||
var file;
|
||||
|
||||
@ -200,26 +191,40 @@ Snapshot.prototype.save = function save(callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
file = bcoin.prefix
|
||||
file = self.prefix
|
||||
+ '/profiler/'
|
||||
+ self.name
|
||||
+ '.heapsnapshot';
|
||||
|
||||
bcoin.mkdir(file, true);
|
||||
utils.mkdir(file, true);
|
||||
|
||||
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.
|
||||
* @param {String?} name
|
||||
* @returns {Profile}
|
||||
*/
|
||||
|
||||
profiler.startProfiling = function startProfiling(name) {
|
||||
Profiler.prototype.startProfiling = function startProfiling(name) {
|
||||
ensure();
|
||||
return new Profile(name);
|
||||
return new Profile(this.prefix, name);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -228,9 +233,9 @@ profiler.startProfiling = function startProfiling(name) {
|
||||
* @returns {Snapshot}
|
||||
*/
|
||||
|
||||
profiler.takeSnapshot = function takeSnapshot(name) {
|
||||
Profiler.prototype.takeSnapshot = function takeSnapshot(name) {
|
||||
ensure();
|
||||
return new Snapshot(name);
|
||||
return new Snapshot(this.prefix, name);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -239,28 +244,25 @@ profiler.takeSnapshot = function takeSnapshot(name) {
|
||||
* @param {Function?} callback
|
||||
*/
|
||||
|
||||
profiler.snapshot = function snapshot(name, callback) {
|
||||
var snapshot, mem;
|
||||
|
||||
ensure();
|
||||
|
||||
Profiler.prototype.snapshot = function snapshot(name, callback) {
|
||||
if (typeof name === 'function') {
|
||||
callback = name;
|
||||
name = null;
|
||||
}
|
||||
|
||||
if (bcoin.debugLogs && process.memoryUsage) {
|
||||
mem = process.memoryUsage();
|
||||
bcoin.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));
|
||||
ensure();
|
||||
|
||||
if (!v8profiler) {
|
||||
if (!callback)
|
||||
return;
|
||||
return utils.nextTick(callback);
|
||||
}
|
||||
|
||||
if (!v8profiler || !bcoin.profile)
|
||||
return utils.asyncify(callback)();
|
||||
|
||||
snapshot = new Snapshot(name);
|
||||
snapshot.save(callback);
|
||||
this.takeSnapshot(name).save(callback);
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Profiler;
|
||||
|
||||
@ -344,7 +344,6 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) {
|
||||
case 'feefilter':
|
||||
return Parser.parseFeeFilter(p);
|
||||
default:
|
||||
bcoin.debug('Unknown packet: %s', cmd);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
@ -4425,6 +4425,9 @@ Script.checksig = function checksig(msg, sig, key, flags) {
|
||||
if (!(flags & constants.flags.VERIFY_LOW_S))
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ SigCache.prototype.add = function add(hash, 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);
|
||||
k = this.keys[i];
|
||||
delete this.valid[k];
|
||||
|
||||
@ -42,6 +42,10 @@ function SPVNode(options) {
|
||||
|
||||
this.chain = new bcoin.chain({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
profiler: this.profiler,
|
||||
db: this.db,
|
||||
location: this.location('spvchain'),
|
||||
preload: this.options.preload,
|
||||
useCheckpoints: this.options.useCheckpoints,
|
||||
spv: true
|
||||
@ -49,8 +53,11 @@ function SPVNode(options) {
|
||||
|
||||
this.pool = new bcoin.pool({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
chain: this.chain,
|
||||
witness: this.network.witness,
|
||||
proxyServer: this.options.proxyServer,
|
||||
preferredSeed: this.options.preferredSeed,
|
||||
selfish: true,
|
||||
listen: false,
|
||||
spv: true
|
||||
@ -58,12 +65,16 @@ function SPVNode(options) {
|
||||
|
||||
this.walletdb = new bcoin.walletdb({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
db: this.db,
|
||||
location: this.location('walletdb'),
|
||||
verify: true
|
||||
});
|
||||
|
||||
if (!utils.isBrowser) {
|
||||
this.http = new bcoin.http.server({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
node: this,
|
||||
key: this.options.sslKey,
|
||||
cert: this.options.sslCert,
|
||||
@ -87,20 +98,20 @@ SPVNode.prototype._init = function _init() {
|
||||
|
||||
// Bind to errors
|
||||
this.pool.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
this.chain.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
this.walletdb.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
|
||||
if (this.http) {
|
||||
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) {
|
||||
self.walletdb.addTX(tx, function(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) {
|
||||
self.walletdb.removeBlockSPV(entry, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
self._error(err);
|
||||
});
|
||||
});
|
||||
|
||||
@ -154,7 +165,7 @@ SPVNode.prototype._open = function open(callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
bcoin.debug('Node is loaded.');
|
||||
self.logger.info('Node is loaded.');
|
||||
|
||||
callback();
|
||||
}
|
||||
@ -190,7 +201,7 @@ SPVNode.prototype._open = function open(callback) {
|
||||
return next(err);
|
||||
|
||||
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++)
|
||||
self.pool.watch(hashes[i], 'hex');
|
||||
@ -205,7 +216,7 @@ SPVNode.prototype._open = function open(callback) {
|
||||
return next(err);
|
||||
|
||||
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++)
|
||||
self.pool.broadcast(txs[i]);
|
||||
@ -226,7 +237,7 @@ SPVNode.prototype._open = function open(callback) {
|
||||
if (height === -1)
|
||||
return next();
|
||||
|
||||
bcoin.debug('Rewinding chain to height %s.', height);
|
||||
self.logger.info('Rewinding chain to height %s.', height);
|
||||
|
||||
self.chain.reset(height, next);
|
||||
});
|
||||
@ -327,13 +338,14 @@ SPVNode.prototype.stopSync = function stopSync() {
|
||||
*/
|
||||
|
||||
SPVNode.prototype.createWallet = function createWallet(options, callback) {
|
||||
var self = this;
|
||||
this.walletdb.ensure(options, function(err, wallet) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
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());
|
||||
|
||||
return callback(null, wallet);
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('./env');
|
||||
var utils = require('./utils');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
/**
|
||||
* An object which handles "adjusted time". This may not
|
||||
@ -28,6 +28,8 @@ function TimeData(limit) {
|
||||
if (!(this instanceof TimeData))
|
||||
return new TimeData(limit);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
if (limit == null)
|
||||
limit = 200;
|
||||
|
||||
@ -38,6 +40,8 @@ function TimeData(limit) {
|
||||
this._checked = false;
|
||||
}
|
||||
|
||||
utils.inherits(TimeData, EventEmitter);
|
||||
|
||||
/**
|
||||
* Add time data.
|
||||
* @param {String} host
|
||||
@ -58,8 +62,7 @@ TimeData.prototype.add = function add(host, time) {
|
||||
|
||||
utils.binaryInsert(this.samples, sample, compare);
|
||||
|
||||
bcoin.debug('Added time data: samples=%d, offset=%d (%d minutes)',
|
||||
this.samples.length, sample, sample / 60 | 0);
|
||||
this.emit('sample', sample, this.samples.length);
|
||||
|
||||
if (this.samples.length >= 5 && this.samples.length % 2 === 1) {
|
||||
median = this.samples[this.samples / 2 | 0];
|
||||
@ -79,13 +82,12 @@ TimeData.prototype.add = function add(host, time) {
|
||||
}
|
||||
if (!match) {
|
||||
this._checked = true;
|
||||
bcoin.debug('Please make sure your system clock is correct!');
|
||||
this.emit('mismatch');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bcoin.debug('Time offset: %d (%d minutes)',
|
||||
this.offset, this.offset / 60 | 0);
|
||||
this.emit('offset', this.offset);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -678,10 +678,8 @@ TX.prototype.verifyInput = function verifyInput(index, flags) {
|
||||
|
||||
assert(input, 'Input does not exist.');
|
||||
|
||||
if (!input.coin) {
|
||||
bcoin.debug('Coin is not available for verification.');
|
||||
if (!input.coin)
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Script.verify(
|
||||
@ -693,10 +691,8 @@ TX.prototype.verifyInput = function verifyInput(index, flags) {
|
||||
flags
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.type === 'ScriptError') {
|
||||
bcoin.debug('Script verification error: %s', e.message);
|
||||
if (e.type === 'ScriptError')
|
||||
return false;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -719,7 +715,7 @@ TX.prototype.verifyAsync = function verifyAsync(flags, callback) {
|
||||
flags = null;
|
||||
}
|
||||
|
||||
if (!bcoin.workerPool) {
|
||||
if (!bcoin.useWorkers) {
|
||||
callback = utils.asyncify(callback);
|
||||
try {
|
||||
result = this.verify(flags);
|
||||
|
||||
@ -57,6 +57,7 @@ function TXDB(db, options) {
|
||||
|
||||
this.walletdb = db;
|
||||
this.db = db.db;
|
||||
this.logger = db.logger;
|
||||
this.options = options;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
this.busy = false;
|
||||
@ -297,6 +298,12 @@ TXDB.prototype.add = function add(tx, callback, force) {
|
||||
if (!map)
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
@ -19,7 +19,7 @@ var assert = require('assert');
|
||||
var bn = require('bn.js');
|
||||
var util = require('util');
|
||||
var Number, Math, Date;
|
||||
var crypto, supersha, hash, aes;
|
||||
var fs, crypto, supersha, hash, aes;
|
||||
|
||||
/**
|
||||
* Reference to the global object.
|
||||
@ -52,6 +52,7 @@ utils.isBrowser =
|
||||
|| typeof window !== 'undefined';
|
||||
|
||||
if (!utils.isBrowser) {
|
||||
fs = require('f' + 's');
|
||||
crypto = require('cry' + 'pto');
|
||||
try {
|
||||
supersha = require('super' + 'sha');
|
||||
@ -968,7 +969,7 @@ utils.format = function format(args, color) {
|
||||
* @param {...String} args
|
||||
*/
|
||||
|
||||
utils.print = function print() {
|
||||
utils.log = function log() {
|
||||
var args = new Array(arguments.length);
|
||||
var i, msg;
|
||||
|
||||
@ -1004,7 +1005,7 @@ utils.error = function error() {
|
||||
msg = typeof args[0] !== 'object'
|
||||
? utils.format(args, false)
|
||||
: args[0];
|
||||
console.log(msg);
|
||||
console.error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2529,3 +2530,94 @@ utils.binaryRemove = function binaryRemove(items, item, compare) {
|
||||
items.splice(i, 1);
|
||||
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) {
|
||||
var self = this;
|
||||
var rate;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
@ -550,6 +551,15 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
||||
if (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 {
|
||||
tx.fill(coins, {
|
||||
selection: options.selection || 'age',
|
||||
@ -560,9 +570,7 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
||||
subtractFee: options.subtractFee,
|
||||
changeAddress: account.changeAddress.getAddress(),
|
||||
height: self.network.height,
|
||||
rate: options.rate != null
|
||||
? options.rate
|
||||
: self.network.fees.estimateFee(),
|
||||
rate: rate,
|
||||
wallet: self,
|
||||
m: self.m,
|
||||
n: self.n
|
||||
@ -2521,7 +2529,7 @@ MasterKey.prototype.decrypt = function decrypt(passphrase, callback) {
|
||||
var self = this;
|
||||
var unlock;
|
||||
|
||||
unlock = this.locker.lock(decrypt, [passphrase, callback]);
|
||||
unlock = this.locker.lock(decrypt, [passphrase, callback]);
|
||||
|
||||
if (!unlock)
|
||||
return;
|
||||
|
||||
@ -48,14 +48,14 @@ function WalletDB(options) {
|
||||
|
||||
this.options = options;
|
||||
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`.
|
||||
// It will hold locks specific to wallet ids.
|
||||
this.readLock = new ReadLock(this);
|
||||
|
||||
this.db = bcoin.ldb({
|
||||
network: this.network,
|
||||
name: this.options.name || 'wallet',
|
||||
location: this.options.location,
|
||||
db: this.options.db,
|
||||
cacheSize: 8 << 20,
|
||||
@ -523,6 +523,8 @@ WalletDB.prototype.create = function create(options, callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.logger.info('Created wallet %s.', wallet.id);
|
||||
|
||||
return callback(null, wallet);
|
||||
});
|
||||
});
|
||||
@ -703,6 +705,11 @@ WalletDB.prototype.createAccount = function createAccount(options, callback) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self.logger.info('Created account %s/%s/%d.',
|
||||
account.id,
|
||||
account.name,
|
||||
account.accountIndex);
|
||||
|
||||
return callback(null, account);
|
||||
});
|
||||
});
|
||||
|
||||
@ -19,18 +19,12 @@ if (typeof importScripts !== 'undefined') {
|
||||
|
||||
env = JSON.parse(event.data);
|
||||
|
||||
bcoin.set({ useWorkers: true });
|
||||
bcoin.network.set(env.BCOIN_WORKER_NETWORK);
|
||||
bcoin.workers.listen(+env.BCOIN_WORKER_ID, {
|
||||
debug: +env.BCOIN_WORKER_DEBUG === 1
|
||||
});
|
||||
bcoin.set(env.BCOIN_WORKER_NETWORK);
|
||||
bcoin.workers.listen();
|
||||
};
|
||||
} else {
|
||||
env = process.env;
|
||||
bcoin = require('./env');
|
||||
bcoin.set({ useWorkers: true });
|
||||
bcoin.network.set(env.BCOIN_WORKER_NETWORK);
|
||||
bcoin.workers.listen(+env.BCOIN_WORKER_ID, {
|
||||
debug: +env.BCOIN_WORKER_DEBUG === 1
|
||||
});
|
||||
bcoin.set(env.BCOIN_WORKER_NETWORK);
|
||||
bcoin.workers.listen();
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ var jobs;
|
||||
* @property {Number} size
|
||||
* @property {Number} timeout
|
||||
* @property {Object} children
|
||||
* @property {Number} uid
|
||||
* @property {Number} nonce
|
||||
*/
|
||||
|
||||
function Workers(options) {
|
||||
@ -36,11 +36,13 @@ function Workers(options) {
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.uid = 0;
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.size = Math.max(1, options.size || Workers.CORES);
|
||||
this.timeout = options.timeout || 60000;
|
||||
this.network = bcoin.network.get(options.network);
|
||||
this.children = [];
|
||||
this.nonce = 0;
|
||||
}
|
||||
|
||||
utils.inherits(Workers, EventEmitter);
|
||||
@ -60,7 +62,9 @@ Workers.CORES = getCores();
|
||||
Workers.children = [];
|
||||
|
||||
/**
|
||||
* Cleanup all workers on exit.
|
||||
* Destroy all workers.
|
||||
* Used for cleaning up workers on exit.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Workers.cleanup = function cleanup() {
|
||||
@ -86,11 +90,13 @@ Workers._bindExit = function _bindExit() {
|
||||
|
||||
function onExit(err) {
|
||||
Workers.cleanup();
|
||||
|
||||
if (err) {
|
||||
console.error(err.stack + '');
|
||||
utils.error(err.stack + '');
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@ -124,55 +130,38 @@ Workers._bindExit = function _bindExit() {
|
||||
|
||||
Workers.prototype.spawn = function spawn(id) {
|
||||
var self = this;
|
||||
var i, child;
|
||||
var child;
|
||||
|
||||
bcoin.debug('Spawning worker process: %d', id);
|
||||
|
||||
child = new Worker(this, id);
|
||||
child = new Worker(id);
|
||||
|
||||
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) {
|
||||
bcoin.debug('Worker %d exited: %s', child.id, code);
|
||||
self.emit('exit', code, child);
|
||||
if (self.children[child.id] === child)
|
||||
self.children[child.id] = null;
|
||||
i = Workers.children.indexOf(child);
|
||||
if (i !== -1)
|
||||
Workers.children.splice(i, 1);
|
||||
});
|
||||
|
||||
child.on('packet', function(job, body) {
|
||||
if (body.name === 'event') {
|
||||
child.emit.apply(child, body.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));
|
||||
child.on('event', function(items) {
|
||||
self.emit('event', items, child);
|
||||
self.emit.apply(self, items);
|
||||
});
|
||||
|
||||
Workers.children.push(child);
|
||||
|
||||
Workers._bindExit();
|
||||
this.emit('spawn', child);
|
||||
|
||||
return child;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate a new worker, will not go above `size` option
|
||||
* and will automatically load balance the workers based
|
||||
* on job ID.
|
||||
* @param {Number} job
|
||||
* and will automatically load balance the workers.
|
||||
* @returns {Worker}
|
||||
*/
|
||||
|
||||
Workers.prototype.alloc = function alloc(job) {
|
||||
var id = job % this.size;
|
||||
Workers.prototype.alloc = function alloc() {
|
||||
var id = this.nonce++ % this.size;
|
||||
if (!this.children[id])
|
||||
this.children[id] = this.spawn(id);
|
||||
return this.children[id];
|
||||
@ -228,20 +217,14 @@ Workers.prototype.destroy = function destroy() {
|
||||
*/
|
||||
|
||||
Workers.prototype.execute = function execute(method, args, timeout, callback) {
|
||||
var job = this.uid++;
|
||||
var child;
|
||||
|
||||
if (job > 0xffffffff) {
|
||||
this.uid = 0;
|
||||
job = this.uid++;
|
||||
}
|
||||
|
||||
if (!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;
|
||||
};
|
||||
@ -287,33 +270,29 @@ Workers.prototype.scrypt = function scrypt(passwd, salt, N, r, p, len, callback)
|
||||
* Represents a worker.
|
||||
* @exports Worker
|
||||
* @constructor
|
||||
* @param {Workers} pool
|
||||
* @param {Number} id - Worker ID.
|
||||
* @property {Number} id
|
||||
* @param {Number?} id
|
||||
*/
|
||||
|
||||
function Worker(pool, id) {
|
||||
function Worker(id) {
|
||||
var self = this;
|
||||
var penv, cp;
|
||||
|
||||
if (!(this instanceof Worker))
|
||||
return new Worker(pool, id);
|
||||
return new Worker();
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.id = id;
|
||||
this.pool = pool;
|
||||
this.framer = new Framer();
|
||||
this.parser = new Parser();
|
||||
this.setMaxListeners(utils.MAX_SAFE_INTEGER);
|
||||
this.uid = 0;
|
||||
this.id = id != null ? id : -1;
|
||||
|
||||
penv = {
|
||||
BCOIN_WORKER_ID: id + '',
|
||||
BCOIN_WORKER_NETWORK: this.pool.network.type,
|
||||
BCOIN_WORKER_DEBUG: (bcoin.debugLogs || bcoin.debugFile) ? '1' : '0'
|
||||
BCOIN_WORKER_NETWORK: bcoin.network.get().type
|
||||
};
|
||||
|
||||
if (bcoin.isBrowser) {
|
||||
if (utils.isBrowser) {
|
||||
this.child = new global.Worker('/bcoin-worker.js');
|
||||
|
||||
this.child.onerror = function onerror(err) {
|
||||
@ -368,11 +347,6 @@ function Worker(pool, id) {
|
||||
this.child.stdout.on('data', function(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) {
|
||||
@ -386,8 +360,63 @@ function Worker(pool, id) {
|
||||
this.parser.on('packet', function(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.
|
||||
* @param {Buffer} data
|
||||
@ -395,7 +424,7 @@ function Worker(pool, id) {
|
||||
*/
|
||||
|
||||
Worker.prototype.write = function write(data) {
|
||||
if (bcoin.isBrowser) {
|
||||
if (utils.isBrowser) {
|
||||
if (this.child.postMessage.length === 2) {
|
||||
data.__proto__ = Uint8Array.prototype;
|
||||
this.child.postMessage({ buf: data }, [data]);
|
||||
@ -458,12 +487,17 @@ Worker.prototype.destroy = function destroy() {
|
||||
* 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 event = 'response ' + job;
|
||||
var timer;
|
||||
var job = this.uid++;
|
||||
var event, timer;
|
||||
|
||||
assert(job <= 0xffffffff);
|
||||
if (job > 0xffffffff) {
|
||||
this.uid = 0;
|
||||
job = this.uid++;
|
||||
}
|
||||
|
||||
event = 'response ' + job;
|
||||
|
||||
function listener(err, result) {
|
||||
if (timer) {
|
||||
@ -499,25 +533,22 @@ utils.inherits(Worker, EventEmitter);
|
||||
* Represents the master process.
|
||||
* @exports Master
|
||||
* @constructor
|
||||
* @param {Number} id - Worker ID.
|
||||
* @param {Object?} options
|
||||
* @property {Number} id
|
||||
*/
|
||||
|
||||
function Master(id, options) {
|
||||
function Master(options) {
|
||||
var self = this;
|
||||
|
||||
if (!(this instanceof Master))
|
||||
return new Master(id);
|
||||
return new Master();
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.id = id;
|
||||
this.framer = new Framer();
|
||||
this.parser = new Parser();
|
||||
this.options = options || {};
|
||||
|
||||
if (bcoin.isBrowser) {
|
||||
if (utils.isBrowser) {
|
||||
global.onerror = function onerror(err) {
|
||||
self.emit('error', err);
|
||||
};
|
||||
@ -563,7 +594,7 @@ utils.inherits(Master, EventEmitter);
|
||||
*/
|
||||
|
||||
Master.prototype.write = function write(data) {
|
||||
if (bcoin.isBrowser) {
|
||||
if (utils.isBrowser) {
|
||||
if (global.postMessage.length === 2) {
|
||||
data.__proto__ = Uint8Array.prototype;
|
||||
global.postMessage({ buf: data }, [data]);
|
||||
@ -604,81 +635,53 @@ Master.prototype.sendEvent = function sendEvent() {
|
||||
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.
|
||||
*/
|
||||
|
||||
Master.prototype.destroy = function destroy() {
|
||||
if (bcoin.isBrowser)
|
||||
if (utils.isBrowser)
|
||||
return global.close();
|
||||
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).
|
||||
* @param {Number} id - Worker id.
|
||||
* @param {Object?} options
|
||||
* @returns {Master}
|
||||
*/
|
||||
|
||||
Master.listen = function listen(id, options) {
|
||||
var master = new Master(id, options);
|
||||
var debug = master.debug.bind(master);
|
||||
var error = master.error.bind(master);
|
||||
Master.listen = function listen(options) {
|
||||
var master = new Master(options);
|
||||
|
||||
bcoin.debug = debug;
|
||||
bcoin.error = error;
|
||||
utils.print = debug;
|
||||
utils.error = debug;
|
||||
utils.log = master.log.bind(master);
|
||||
utils.error = utils.log;
|
||||
|
||||
master.on('error', function(err) {
|
||||
bcoin.debug('Master error: %s', err.message);
|
||||
master.sendEvent('worker error', fromError(err));
|
||||
});
|
||||
|
||||
master.on('packet', function(job, body) {
|
||||
var result;
|
||||
|
||||
if (body.name === 'event') {
|
||||
master.emit('event', body.items);
|
||||
master.emit.apply(master, body.items);
|
||||
return;
|
||||
}
|
||||
@ -686,16 +689,14 @@ Master.listen = function listen(id, options) {
|
||||
try {
|
||||
result = jobs[body.name].apply(jobs, body.items);
|
||||
} catch (e) {
|
||||
bcoin.error(e);
|
||||
return master.send(job, 'response', [{
|
||||
message: e.message,
|
||||
stack: e.stack + ''
|
||||
}]);
|
||||
return master.send(job, 'response', [fromError(e)]);
|
||||
}
|
||||
|
||||
return master.send(job, 'response', [null, result]);
|
||||
});
|
||||
|
||||
bcoin.master = master;
|
||||
|
||||
return master;
|
||||
};
|
||||
|
||||
@ -727,13 +728,7 @@ jobs.verify = function verify(tx, flags) {
|
||||
|
||||
jobs.mine = function mine(attempt) {
|
||||
attempt.on('status', function(stat) {
|
||||
bcoin.debug(
|
||||
'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
|
||||
stat.hashrate / 1000 | 0,
|
||||
stat.hashes,
|
||||
stat.target,
|
||||
stat.height,
|
||||
stat.best);
|
||||
bcoin.master.sendEvent('status', stat);
|
||||
});
|
||||
return attempt.mineSync();
|
||||
};
|
||||
@ -794,7 +789,8 @@ Framer.prototype.body = function body(name, items) {
|
||||
return p.render();
|
||||
};
|
||||
|
||||
Framer.item = function _item(item, p) {
|
||||
Framer.item = function _item(item, writer) {
|
||||
var p = BufferWriter(writer);
|
||||
var i, keys;
|
||||
|
||||
switch (typeof item) {
|
||||
@ -834,7 +830,7 @@ Framer.item = function _item(item, p) {
|
||||
p.writeU8(45);
|
||||
item.toRaw(p);
|
||||
} else if (bn.isBN(item)) {
|
||||
p.writeU8(50);
|
||||
p.writeU8(10);
|
||||
p.writeVarBytes(item.toArrayLike(Buffer));
|
||||
} else if (Buffer.isBuffer(item)) {
|
||||
p.writeU8(4);
|
||||
@ -856,8 +852,13 @@ Framer.item = function _item(item, p) {
|
||||
}
|
||||
break;
|
||||
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);
|
||||
|
||||
Parser.prototype.feed = function feed(data) {
|
||||
var chunk, header, body, rest;
|
||||
var chunk, header, body;
|
||||
|
||||
this.pendingTotal += data.length;
|
||||
this.pending.push(data);
|
||||
while (data) {
|
||||
this.pendingTotal += data.length;
|
||||
this.pending.push(data);
|
||||
data = null;
|
||||
|
||||
if (this.pendingTotal < this.waiting)
|
||||
return;
|
||||
if (this.pendingTotal < this.waiting)
|
||||
break;
|
||||
|
||||
chunk = Buffer.concat(this.pending);
|
||||
chunk = Buffer.concat(this.pending);
|
||||
|
||||
if (chunk.length > this.waiting) {
|
||||
rest = chunk.slice(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 (chunk.length > this.waiting) {
|
||||
data = chunk.slice(this.waiting);
|
||||
chunk = chunk.slice(0, this.waiting);
|
||||
}
|
||||
|
||||
if (rest)
|
||||
this.feed(rest);
|
||||
if (!this.header) {
|
||||
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) {
|
||||
@ -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;
|
||||
|
||||
switch (p.readU8()) {
|
||||
@ -983,6 +982,8 @@ Parser.parseItem = function parseItem(p) {
|
||||
for (i = 0; i < count; i++)
|
||||
items[p.readVarString('utf8')] = Parser.parseItem(p);
|
||||
return items;
|
||||
case 10:
|
||||
return new bn(p.readVarBytes());
|
||||
case 40:
|
||||
return bcoin.block.fromRaw(p);
|
||||
case 41:
|
||||
@ -995,17 +996,13 @@ Parser.parseItem = function parseItem(p) {
|
||||
return bcoin.mempoolentry.fromRaw(p);
|
||||
case 45:
|
||||
return bcoin.minerblock.fromRaw(p);
|
||||
case 50:
|
||||
return new bn(p.readVarBytes());
|
||||
default:
|
||||
assert(false, 'Bad type.');
|
||||
throw new Error('Bad type.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to retrieve number of cores.
|
||||
* @memberof Workers
|
||||
* @returns {Number}
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function getCores() {
|
||||
@ -1019,6 +1016,17 @@ function getCores() {
|
||||
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
|
||||
*/
|
||||
|
||||
@ -13,9 +13,9 @@
|
||||
},
|
||||
"repository": "git://github.com/bcoin-org/bcoin.git",
|
||||
"keywords": [
|
||||
"bcoin",
|
||||
"bitcoin",
|
||||
"blockchain",
|
||||
"bcoin",
|
||||
"wallet"
|
||||
],
|
||||
"author": "Fedor Indutny <fedor@indutny.com>",
|
||||
@ -32,10 +32,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": "4.11.0",
|
||||
"elliptic": "6.2.3",
|
||||
"leveldown": "git://github.com/chjj/leveldown.git#staging"
|
||||
"elliptic": "6.2.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"leveldown": "git://github.com/chjj/leveldown.git#staging",
|
||||
"secp256k1": "3.0.0",
|
||||
"socket.io": "1.4.5",
|
||||
"socket.io-client": "1.4.5"
|
||||
|
||||
@ -103,28 +103,28 @@ var segnet4 = createGenesisBlock({
|
||||
nonce: 0
|
||||
});
|
||||
|
||||
utils.print(main);
|
||||
utils.print('');
|
||||
utils.print(testnet);
|
||||
utils.print('');
|
||||
utils.print(regtest);
|
||||
utils.print('');
|
||||
utils.print(segnet3);
|
||||
utils.print('');
|
||||
utils.print(segnet4);
|
||||
utils.print('');
|
||||
utils.print('');
|
||||
utils.print('main hash: %s', main.rhash);
|
||||
utils.print('main raw: %s', main.toRaw().toString('hex'));
|
||||
utils.print('');
|
||||
utils.print('testnet hash: %s', testnet.rhash);
|
||||
utils.print('testnet raw: %s', testnet.toRaw().toString('hex'));
|
||||
utils.print('');
|
||||
utils.print('regtest hash: %s', regtest.rhash);
|
||||
utils.print('regtest raw: %s', regtest.toRaw().toString('hex'));
|
||||
utils.print('');
|
||||
utils.print('segnet3 hash: %s', segnet3.rhash);
|
||||
utils.print('segnet3 raw: %s', segnet3.toRaw().toString('hex'));
|
||||
utils.print('');
|
||||
utils.print('segnet4 hash: %s', segnet4.rhash);
|
||||
utils.print('segnet4 raw: %s', segnet4.toRaw().toString('hex'));
|
||||
utils.log(main);
|
||||
utils.log('');
|
||||
utils.log(testnet);
|
||||
utils.log('');
|
||||
utils.log(regtest);
|
||||
utils.log('');
|
||||
utils.log(segnet3);
|
||||
utils.log('');
|
||||
utils.log(segnet4);
|
||||
utils.log('');
|
||||
utils.log('');
|
||||
utils.log('main hash: %s', main.rhash);
|
||||
utils.log('main raw: %s', main.toRaw().toString('hex'));
|
||||
utils.log('');
|
||||
utils.log('testnet hash: %s', testnet.rhash);
|
||||
utils.log('testnet raw: %s', testnet.toRaw().toString('hex'));
|
||||
utils.log('');
|
||||
utils.log('regtest hash: %s', regtest.rhash);
|
||||
utils.log('regtest raw: %s', regtest.toRaw().toString('hex'));
|
||||
utils.log('');
|
||||
utils.log('segnet3 hash: %s', segnet3.rhash);
|
||||
utils.log('segnet3 raw: %s', segnet3.toRaw().toString('hex'));
|
||||
utils.log('');
|
||||
utils.log('segnet4 hash: %s', segnet4.rhash);
|
||||
utils.log('segnet4 raw: %s', segnet4.toRaw().toString('hex'));
|
||||
|
||||
@ -63,7 +63,6 @@ describe('Script', function() {
|
||||
opcodes.OP_5
|
||||
]);
|
||||
var stack = new Stack();
|
||||
utils.print(inputScript);
|
||||
inputScript.execute(stack);
|
||||
var res = prevOutScript.execute(stack);
|
||||
assert(res);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user