Service Documentation
This commit is contained in:
parent
a624b6fa39
commit
1183e0cae7
|
@ -6,7 +6,7 @@ A Bitcoin full node for building applications and services with Node.js. A node
|
|||
## Install
|
||||
|
||||
```bash
|
||||
npm install -g bitcore-node@0.2.0-beta.X
|
||||
npm install -g bitcore-node@latest
|
||||
bitcore-node start
|
||||
```
|
||||
|
||||
|
@ -31,6 +31,13 @@ To start bitcore-node as a daemon:
|
|||
bitcore-node start --daemon
|
||||
```
|
||||
|
||||
## Add-on Services
|
||||
|
||||
There are several add-on services available to extend the functionality of Bitcore Node:
|
||||
|
||||
- [Insight API](https://github.com/bitpay/insight-api/tree/v0.3.0)
|
||||
- [Insight UI](https://github.com/bitpay/insight/tree/v0.3.0)
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Services](docs/services.md)
|
||||
|
|
15
docs/node.md
15
docs/node.md
|
@ -16,14 +16,23 @@ A node represents a collection of services that are loaded together. For more in
|
|||
|
||||
```js
|
||||
|
||||
var BitcoinNode = require('bitcore-node').Node;
|
||||
var index = require('bitcore-node');
|
||||
var Bitcoin = index.services.Bitcoin;
|
||||
var Node = index.Node;
|
||||
|
||||
var configuration = {
|
||||
datadir: '~/.bitcoin',
|
||||
network: 'testnet'
|
||||
network: 'testnet',
|
||||
services: [
|
||||
{
|
||||
name: 'bitcoind',
|
||||
module: Bitcoin,
|
||||
config: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var node = new BitcoinNode(configuration);
|
||||
var node = new Node(configuration);
|
||||
|
||||
node.on('ready', function() {
|
||||
console.log('Bitcoin Node Ready');
|
||||
|
|
|
@ -1,35 +1,142 @@
|
|||
# Address Service
|
||||
|
||||
The address service builds on the [Bitcoin Service](bitcoind.md) and the [Database Service](db.md) to add additional functionality for querying and subscribing to information based on bitcoin addresses.
|
||||
The address service builds on the [Bitcoin Service](bitcoind.md) and the [Database Service](db.md) to add additional functionality for querying and subscribing to information based on bitcoin addresses. This will typically represent the core functionality for wallet applications.
|
||||
|
||||
## API Documentation
|
||||
|
||||
Get Unspent Outputs
|
||||
These methods are exposed over the JSON-RPC interface and can be called directly from a node via:
|
||||
|
||||
```js
|
||||
var address = '15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2';
|
||||
node.services.address.<methodName>
|
||||
```
|
||||
|
||||
**Get Unspent Outputs**
|
||||
|
||||
One of the most common uses will be to retrieve unspent outputs necessary to create a transaction, here is how to get the unspent outputs for an address:
|
||||
|
||||
```js
|
||||
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
|
||||
var includeMempool = true;
|
||||
node.getUnspentOutputs(address, includeMempool, function(err, unspentOutputs) {
|
||||
//...
|
||||
node.services.address.getUnspentOutputs(address, includeMempool, function(err, unspentOutputs) {
|
||||
// see below
|
||||
});
|
||||
```
|
||||
|
||||
View Balances
|
||||
The `unspentOutputs` will have the format:
|
||||
|
||||
```js
|
||||
var address = '15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2';
|
||||
[
|
||||
{
|
||||
address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW',
|
||||
txid: '9d956c5d324a1c2b12133f3242deff264a9b9f61be701311373998681b8c1769',
|
||||
outputIndex: 1,
|
||||
height: 150,
|
||||
satoshis: 1000000000,
|
||||
script: '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac',
|
||||
confirmations: 3
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**View Balances**
|
||||
|
||||
```js
|
||||
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
|
||||
var includeMempool = true;
|
||||
node.getBalance(address, includeMempool, function(err, balance) {
|
||||
//...
|
||||
node.services.address.getBalance(address, includeMempool, function(err, balance) {
|
||||
// balance will be in satoshis
|
||||
});
|
||||
```
|
||||
|
||||
Get Outputs
|
||||
**View Address History**
|
||||
|
||||
This method will give history of an address limited by a range of block heights by using
|
||||
the "start" and "end" arguments. The "start" value is the more recent, and greater, block height.
|
||||
The "end" value is the older, and lesser, block height. This feature is most useful for synchronization
|
||||
as previous history can be omitted. Furthermore for large ranges of block heights, results can be
|
||||
paginated by using the "from" and "to" arguments.
|
||||
|
||||
```js
|
||||
var address = '15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2';
|
||||
var includeMempool = true;
|
||||
node.getOutputs(address, includeMempool, function(err, outputs) {
|
||||
//...
|
||||
var addresses = ['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'];
|
||||
var options = {
|
||||
start: 345000,
|
||||
end: 344000,
|
||||
queryMempool: true
|
||||
};
|
||||
node.services.address.getAddressHistory(addresses, options, function(err, history) {
|
||||
// see below
|
||||
});
|
||||
```
|
||||
|
||||
The history format will be:
|
||||
```js
|
||||
{
|
||||
totalCount: 1, // The total number of items within "start" and "end"
|
||||
items: [
|
||||
{
|
||||
addresses: {
|
||||
'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW': {
|
||||
inputIndexes: [],
|
||||
outputIndexes: [0]
|
||||
}
|
||||
},
|
||||
satoshis: 1000000000,
|
||||
height: 150, // the block height of the transaction
|
||||
confirmations: 3,
|
||||
timestamp: 1442948127, // in seconds
|
||||
fees: 191,
|
||||
tx: <Transaction> // the populated transaction
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**View Address Summary**
|
||||
|
||||
```js
|
||||
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
|
||||
var options = {
|
||||
noTxList: false
|
||||
};
|
||||
|
||||
node.services.address.getAddressSummary(address, options, function(err, summary) {
|
||||
// see below
|
||||
});
|
||||
```
|
||||
|
||||
The `summary` will have the format (values are in satoshis):
|
||||
```js
|
||||
{
|
||||
totalReceived: 1000000000,
|
||||
totalSpent: 0,
|
||||
balance: 1000000000,
|
||||
unconfirmedBalance: 1000000000,
|
||||
appearances: 1, // number of transactions
|
||||
unconfirmedAppearances: 0,
|
||||
txids: [
|
||||
'3f7d13efe12e82f873f4d41f7e63bb64708fc4c942eb8c6822fa5bd7606adb00'
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
For details on instantiating a bus for a node, see the [Bus Documentation](../bus.md).
|
||||
|
||||
- Name: `address/transaction`, Arguments: `[address, address...]`
|
||||
- Name: `address/balance`, Arguments: `[address, address...]`
|
||||
|
||||
**Examples:**
|
||||
|
||||
```js
|
||||
bus.subscribe('address/transaction', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
|
||||
bus.subscribe('address/balance', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
|
||||
|
||||
bus.on('address/transaction', function(transaction) {
|
||||
|
||||
});
|
||||
|
||||
bus.on('address/balance', function(balance) {
|
||||
|
||||
});
|
||||
```
|
|
@ -1,21 +1,121 @@
|
|||
# Bitcoin Service
|
||||
|
||||
The bitcoin service adds a native interface to Bitcoin Core for querying information about the Bitcoin blockchain. Bindings are linked to Bitcoin Core compiled as a static library.
|
||||
The Bitcoin Service adds a native Node.js interface to Bitcoin Core for querying information about the Bitcoin blockchain. Bindings are linked to Bitcoin Core compiled as a static library.
|
||||
|
||||
## API Documentation
|
||||
|
||||
- `bitcoind.start([options], [callback])` - Start the JavaScript Bitcoin node.
|
||||
- `bitcoind.getBlock(blockHash|blockHeight, callback)` - Get any block asynchronously by block hash or height as a node buffer.
|
||||
- `bitcoind.isSpent(txid, outputIndex)` - Returns a boolean if a txid and outputIndex is already spent.
|
||||
- `bitcoind.getBlockIndex(blockHash)` - Will return the block chain work and previous hash.
|
||||
- `bitcoind.isMainChain(blockHash)` - Returns true if block is on the main chain. Returns false if it is an orphan.
|
||||
- `bitcoind.estimateFee(blocks)` - Estimates the fees required to have a transaction included in the number of blocks specified as the first argument.
|
||||
- `bitcoind.sendTransaction(transaction, allowAbsurdFees)` - Will attempt to add a transaction to the mempool and broadcast to peers.
|
||||
- `bitcoind.getTransaction(txid, queryMempool, callback)` - Get any tx asynchronously by reading it from disk, with an argument to optionally not include the mempool.
|
||||
- `bitcoind.getTransactionWithBlockInfo(txid, queryMempool, callback)` - Similar to getTransaction but will also include the block timestamp and height.
|
||||
- `bitcoind.getMempoolTransactions()` - Will return an array of transaction buffers.
|
||||
These methods are currently only available via directly interfacing with a node:
|
||||
|
||||
```js
|
||||
node.services.bitcoind.<methodName>
|
||||
```
|
||||
|
||||
**Getting Block Information**
|
||||
|
||||
It's possible to query blocks by both block hash and by height. Blocks are given as Node.js buffers and can be parsed via Bitcore:
|
||||
|
||||
```js
|
||||
var blockHeight = 0;
|
||||
node.services.bitcoind.getBlock(blockHeight, function(err, blockBuffer) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
var block = bitcore.Block.fromBuffer(blockBuffer);
|
||||
console.log(block);
|
||||
};
|
||||
|
||||
// check if the block is part of the main chain
|
||||
var mainChain = node.services.bitcoind.isMainChain(block.hash);
|
||||
console.log(mainChain);
|
||||
|
||||
// get only the block index (including chain work and previous hash)
|
||||
var blockIndex = node.services.bitcoind.getBlockIndex(blockHeight);
|
||||
console.log(blockIndex);
|
||||
```
|
||||
|
||||
**Retrieving and Sending Transactions**
|
||||
|
||||
Get a transaction asynchronously by reading it from disk, with an argument to optionally not include the mempool:
|
||||
|
||||
```js
|
||||
var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623';
|
||||
var queryMempool = true;
|
||||
node.services.bitcoind.getTransaction(txid, queryMempool, function(err, transactionBuffer) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
var transaction = bitcore.Transaction().fromBuffer(transactionBuffer);
|
||||
});
|
||||
|
||||
|
||||
// also retrieve the block timestamp and height
|
||||
node.services.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, info) {
|
||||
console.log(info.blockHash);
|
||||
console.log(info.height);
|
||||
console.log(info.timestamp); // in seconds
|
||||
var transaction = bitcore.Transaction().fromBuffer(transactionBuffer);
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
Send a transaction to the network:
|
||||
|
||||
```js
|
||||
var numberOfBlocks = 3;
|
||||
var feesPerKilobyte = node.services.bitcoind.estimateFee(numberOfBlocks); // in satoshis
|
||||
|
||||
try {
|
||||
node.services.bitcoind.sendTransaction(transaction.serialize());
|
||||
} catch(err) {
|
||||
// handle error
|
||||
}
|
||||
```
|
||||
|
||||
Get all of the transactions in the mempool:
|
||||
|
||||
```js
|
||||
var mempool = node.services.bitcoind.getMempoolTransactions();
|
||||
var transactions = [];
|
||||
for (var i = 0; i < mempool.length; i++) {
|
||||
transactions.push(bitcore.Transaction().fromBuffer(transactions[i]));
|
||||
}
|
||||
```
|
||||
|
||||
Determine if an output is spent (excluding the mempool):
|
||||
|
||||
```js
|
||||
var spent = node.services.bitcoind.isSpent(txid, outputIndex);
|
||||
console.log(spent);
|
||||
```
|
||||
|
||||
**Miscellaneous**
|
||||
|
||||
- `bitcoind.start(callback)` - Start the JavaScript Bitcoin node, the callback is called when the daemon is ready.
|
||||
- `bitcoind.getInfo()` - Basic information about the chain including total number of blocks.
|
||||
- `bitcoind.getTxOutSetInfo()` - Basic information about the chain including total number of blocks.
|
||||
- `bitcoind.isSynced()` - Returns a boolean if the daemon is fully synced (not the initial block download)
|
||||
- `bitcoind.syncPercentage()` - Returns the current estimate of blockchain download as a percentage.
|
||||
- `bitcoind.stop([callback])` - Stop the JavaScript bitcoin node safely, the callback will be called when bitcoind is closed. This will also be done automatically on `process.exit`. It also takes the bitcoind node off the libuv event loop. If the daemon object is the only thing on the event loop. Node will simply close.
|
||||
- `bitcoind.stop(callback)` - Stop the JavaScript bitcoin node safely, the callback will be called when bitcoind is closed. This will also be done automatically on `process.exit`. It also takes the bitcoind node off the libuv event loop. If the daemon object is the only thing on the event loop. Node will simply close.
|
||||
|
||||
## Events
|
||||
|
||||
The Bitcoin Service doesn't expose any events via the Bus, however there are a few events that can be directly registered:
|
||||
|
||||
```js
|
||||
node.services.bitcoind.on('tip', function(blockHash) {
|
||||
// a new block tip has been added
|
||||
});
|
||||
|
||||
node.services.bitcoind.on('tx', function(txInfo) {
|
||||
// a new transaction has been broadcast in the network
|
||||
});
|
||||
```
|
||||
|
||||
The `txInfo` object will have the format:
|
||||
```js
|
||||
{
|
||||
buffer: <Buffer...>,
|
||||
mempool: true,
|
||||
hash: '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1,45 +1,119 @@
|
|||
# Database Service
|
||||
|
||||
An extensible interface to the bitcoin block chain. The service builds on the [Bitcoin Service](bitcoind.md), and includes additional methods for working with the block chain.
|
||||
This service synchronizes a leveldb database with the [Bitcoin Service](bitcoind.md) block chain by connecting and disconnecting blocks to build new indexes that can be queried. Other services can extend the data that is indexed by implementing a `blockHandler` method, similar to the built-in [Address Service](address.md).
|
||||
|
||||
## Adding Indexes
|
||||
|
||||
For a service to include additional block data, it can implement a `blockHandler` method that will be run to when there are new blocks added or removed.
|
||||
|
||||
```js
|
||||
CustomService.prototype.blockHandler = function(block, add, callback) {
|
||||
var transactions = block.transactions;
|
||||
var operations = [];
|
||||
operations.push({
|
||||
type: add ? 'put' : 'del',
|
||||
key: 'key',
|
||||
value: 'value'
|
||||
});
|
||||
callback(null, operations);
|
||||
};
|
||||
```
|
||||
|
||||
Take a look at the Address Service implementation for more details about how to encode the key, value for the best effeciency and ways to format the keys for streaming reads.
|
||||
|
||||
Additionally the mempool can have an index, the mempool index will be updated once bitcoind and the db have both fully synced. A service can implement a `resetMempoolIndex` method that will be run during this time, and the "synced" event will wait until this task has been finished:
|
||||
|
||||
```js
|
||||
CustomService.prototype.resetMempoolIndex = function(callback) {
|
||||
var transactionBuffers = this.node.services.bitcoind.getMempoolTransactions();
|
||||
// interact over the transactions asynchronously here
|
||||
callback();
|
||||
};
|
||||
```
|
||||
|
||||
## API Documentation
|
||||
|
||||
Get Transaction
|
||||
These methods are exposed over the JSON-RPC interface and can be called directly from a node via:
|
||||
|
||||
```js
|
||||
var txid = 'c349b124b820fe6e32136c30e99f6c4f115fce4d750838edf0c46d3cb4d7281e';
|
||||
var includeMempool = true;
|
||||
node.getTransaction(txid, includeMempool, function(err, transaction) {
|
||||
//...
|
||||
});
|
||||
node.services.db.<methodName>
|
||||
```
|
||||
|
||||
Get Transaction with Block Info
|
||||
|
||||
```js
|
||||
var txid = 'c349b124b820fe6e32136c30e99f6c4f115fce4d750838edf0c46d3cb4d7281e';
|
||||
var includeMempool = true;
|
||||
node.getTransactionWithBlockInfo(txid, includeMempool, function(err, transaction) {
|
||||
//...
|
||||
});
|
||||
```
|
||||
|
||||
Get Block
|
||||
|
||||
```js
|
||||
var blockHash = '00000000d17332a156a807b25bc5a2e041d2c730628ceb77e75841056082a2c2';
|
||||
node.getBlock(blockHash, function(err, block) {
|
||||
//...
|
||||
});
|
||||
```
|
||||
|
||||
Get Block Hashes by Timestamp Range
|
||||
**Query Blocks by Date**
|
||||
|
||||
One of the additional indexes created by the Database Service is querying for blocks by ranges of dates:
|
||||
```js
|
||||
var newest = 1441914000; // Notice time is in seconds not milliseconds
|
||||
var oldest = 1441911000;
|
||||
|
||||
node.getBlockHashesByTimestamp(newest, oldest, function(err, hashes) {
|
||||
//...
|
||||
node.services.db.getBlockHashesByTimestamp(newest, oldest, function(err, hashes) {
|
||||
// hashes will be an array of block hashes
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
**Working with Blocks and Transactions as Bitcore Instances**
|
||||
|
||||
```js
|
||||
|
||||
var txid = 'c349b124b820fe6e32136c30e99f6c4f115fce4d750838edf0c46d3cb4d7281e';
|
||||
var includeMempool = true;
|
||||
node.services.db.getTransaction(txid, includeMempool, function(err, transaction) {
|
||||
console.log(transaction.toObject());
|
||||
});
|
||||
|
||||
var txid = 'c349b124b820fe6e32136c30e99f6c4f115fce4d750838edf0c46d3cb4d7281e';
|
||||
var includeMempool = true;
|
||||
node.services.db.getTransactionWithBlockInfo(txid, includeMempool, function(err, transaction) {
|
||||
console.log(transaction.toObject());
|
||||
console.log(transaction.__blockHash);
|
||||
console.log(transaction.__height);
|
||||
console.log(transaction.__timestamp);
|
||||
});
|
||||
|
||||
var blockHash = '00000000d17332a156a807b25bc5a2e041d2c730628ceb77e75841056082a2c2';
|
||||
node.services.db.getBlock(blockHash, function(err, block) {
|
||||
console.log(block.toObject());
|
||||
});
|
||||
|
||||
// contruct a transaction
|
||||
var transaction = bitcore.Transaction(<serializedString>);
|
||||
|
||||
node.services.db.sendTransaction(transaction, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
// otherwise the transaction has been sent
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
For details on instantiating a bus for a node, see the [Bus Documentation](../bus.md).
|
||||
|
||||
- Name: `db/transaction`
|
||||
- Name: `db/block`
|
||||
|
||||
**Examples:**
|
||||
```js
|
||||
bus.subscribe('db/transaction');
|
||||
bus.subscribe('db/block');
|
||||
|
||||
bus.on('db/block', function(blockHash) {
|
||||
// blockHash will be a hex string of the block hash
|
||||
});
|
||||
|
||||
bus.on('db/transaction', function(txInfo) {
|
||||
// see below
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
The `txInfo` object will have the format:
|
||||
```js
|
||||
{
|
||||
rejected: true, // If the transaction was rejected into the mempool
|
||||
tx: <Transaction> // a Bitcore Transaction instance
|
||||
}
|
||||
```
|
|
@ -231,7 +231,7 @@ describe('Node Functionality', function() {
|
|||
var bus = node.openBus();
|
||||
var block;
|
||||
bus.subscribe('db/block');
|
||||
bus.on('block', function(data) {
|
||||
bus.on('db/block', function(data) {
|
||||
bus.unsubscribe('db/block');
|
||||
data.should.be.equal(block);
|
||||
done();
|
||||
|
@ -303,7 +303,6 @@ describe('Node Functionality', function() {
|
|||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
results.totalReceived.should.equal(1000000000);
|
||||
results.totalSpent.should.equal(0);
|
||||
results.balance.should.equal(1000000000);
|
||||
|
|
|
@ -446,4 +446,19 @@ describe('Daemon Binding Functionality', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#getInfo', function() {
|
||||
it('will get information', function() {
|
||||
var info = bitcoind.getInfo();
|
||||
should.exist(info);
|
||||
should.exist(info.version);
|
||||
should.exist(info.blocks);
|
||||
should.exist(info.timeoffset);
|
||||
should.exist(info.connections);
|
||||
should.exist(info.difficulty);
|
||||
should.exist(info.testnet);
|
||||
should.exist(info.relayfee);
|
||||
should.exist(info.errors);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
20
lib/bus.js
20
lib/bus.js
|
@ -3,6 +3,13 @@
|
|||
var events = require('events');
|
||||
var util = require('util');
|
||||
|
||||
/**
|
||||
* The bus represents a connection to node, decoupled from the transport layer, that can
|
||||
* listen and subscribe to any events that are exposed by available services. Services
|
||||
* can expose events that can be subscribed to by implementing a `getPublishEvents` method.
|
||||
* @param {Object} params
|
||||
* @param {Node} params.node - A reference to the node
|
||||
*/
|
||||
function Bus(params) {
|
||||
events.EventEmitter.call(this);
|
||||
this.node = params.node;
|
||||
|
@ -10,6 +17,12 @@ function Bus(params) {
|
|||
|
||||
util.inherits(Bus, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* This function will find the service that exposes the event by name and
|
||||
* call the associated subscribe method with the arguments excluding the
|
||||
* first argument of this function.
|
||||
* @param {String} name - The name of the event
|
||||
*/
|
||||
Bus.prototype.subscribe = function(name) {
|
||||
var events = [];
|
||||
|
||||
|
@ -28,6 +41,10 @@ Bus.prototype.subscribe = function(name) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The inverse of the subscribe method.
|
||||
* @param {String} name - The name of the event
|
||||
*/
|
||||
Bus.prototype.unsubscribe = function(name) {
|
||||
var events = [];
|
||||
|
||||
|
@ -46,6 +63,9 @@ Bus.prototype.unsubscribe = function(name) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will unsubscribe all events.
|
||||
*/
|
||||
Bus.prototype.close = function() {
|
||||
var events = [];
|
||||
|
||||
|
|
71
lib/node.js
71
lib/node.js
|
@ -12,12 +12,34 @@ var log = index.log;
|
|||
var Bus = require('./bus');
|
||||
var errors = require('./errors');
|
||||
|
||||
/**
|
||||
* A node is a hub of services, and will manage starting and stopping the services in
|
||||
* the correct order based the the dependency chain. The node also holds common configuration
|
||||
* properties that can be shared across services, such as network settings.
|
||||
*
|
||||
* The array of services should have the format:
|
||||
* {
|
||||
* name: 'bitcoind',
|
||||
* config: {}, // options to pass into constructor
|
||||
* module: <serviceConstructor>
|
||||
* }
|
||||
*
|
||||
* @param {Object} config - The configuration of the node
|
||||
* @param {Array} config.services - The array of services
|
||||
* @param {String} config.datadir - The directory for data (e.g. bitcoind datadir)
|
||||
* @param {Number} config.port - The HTTP port for services
|
||||
* @param {Boolean} config.https - Enable https
|
||||
* @param {Object} config.httpsOptions - Options for https
|
||||
* @param {String} config.httpsOptions.key - Path to key file
|
||||
* @param {String} config.httpsOptions.cert - Path to cert file
|
||||
* @param {}
|
||||
*/
|
||||
function Node(config) {
|
||||
/* jshint maxstatements: 20 */
|
||||
if(!(this instanceof Node)) {
|
||||
return new Node(config);
|
||||
}
|
||||
|
||||
this.errors = errors; // So services can use errors without having to have bitcore-node as a dependency
|
||||
this.errors = errors;
|
||||
this.log = log;
|
||||
this.network = null;
|
||||
this.services = {};
|
||||
|
@ -28,19 +50,21 @@ function Node(config) {
|
|||
$.checkArgument(Array.isArray(config.services));
|
||||
this._unloadedServices = config.services;
|
||||
}
|
||||
|
||||
$.checkState(config.datadir, 'Node config expects "datadir"');
|
||||
this.datadir = config.datadir;
|
||||
this.port = config.port;
|
||||
this.https = config.https;
|
||||
this.httpsOptions = config.httpsOptions;
|
||||
|
||||
this._setNetwork(config);
|
||||
|
||||
}
|
||||
|
||||
util.inherits(Node, EventEmitter);
|
||||
|
||||
/**
|
||||
* Will set the this.network based on a network string.
|
||||
* @param {Object} config
|
||||
* @param {String} config.network - Possible options "testnet", "regtest" or "livenet"
|
||||
*/
|
||||
Node.prototype._setNetwork = function(config) {
|
||||
if (config.network === 'testnet') {
|
||||
this.network = Networks.get('testnet');
|
||||
|
@ -65,10 +89,18 @@ Node.prototype._setNetwork = function(config) {
|
|||
$.checkState(this.network, 'Unrecognized network');
|
||||
};
|
||||
|
||||
/**
|
||||
* Will instantiate a new Bus for this node.
|
||||
* @returns {Bus}
|
||||
*/
|
||||
Node.prototype.openBus = function() {
|
||||
return new Bus({node: this});
|
||||
};
|
||||
|
||||
/**
|
||||
* Will get an array of API method descriptions from all of the available services.
|
||||
* @returns {Array}
|
||||
*/
|
||||
Node.prototype.getAllAPIMethods = function() {
|
||||
var methods = [];
|
||||
for(var i in this.services) {
|
||||
|
@ -78,6 +110,10 @@ Node.prototype.getAllAPIMethods = function() {
|
|||
return methods;
|
||||
};
|
||||
|
||||
/**
|
||||
* Will get an array of events from all of the available services.
|
||||
* @returns {Array}
|
||||
*/
|
||||
Node.prototype.getAllPublishEvents = function() {
|
||||
var events = [];
|
||||
for (var i in this.services) {
|
||||
|
@ -87,6 +123,11 @@ Node.prototype.getAllPublishEvents = function() {
|
|||
return events;
|
||||
};
|
||||
|
||||
/**
|
||||
* Will organize services into the order that they should be started
|
||||
* based on the service's dependencies.
|
||||
* @returns {Array}
|
||||
*/
|
||||
Node.prototype.getServiceOrder = function() {
|
||||
|
||||
var services = this._unloadedServices;
|
||||
|
@ -127,6 +168,17 @@ Node.prototype.getServiceOrder = function() {
|
|||
return stack;
|
||||
};
|
||||
|
||||
/**
|
||||
* Will instantiate an instance of the service module, add it to the node
|
||||
* services, start the service and add available API methods to the node and
|
||||
* checking for any conflicts.
|
||||
* @param {Object} serviceInfo
|
||||
* @param {String} serviceInfo.name - The name of the service
|
||||
* @param {Object} serviceInfo.module - The service module constructor
|
||||
* @param {Object} serviceInfo.config - Options to pass into the constructor
|
||||
* @param {Function} callback - Called when the service is started
|
||||
* @private
|
||||
*/
|
||||
Node.prototype._startService = function(serviceInfo, callback) {
|
||||
var self = this;
|
||||
|
||||
|
@ -176,6 +228,10 @@ Node.prototype._startService = function(serviceInfo, callback) {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* Will start all running services in the order based on the dependency chain.
|
||||
* @param {Function} callback - Called when all services are started
|
||||
*/
|
||||
Node.prototype.start = function(callback) {
|
||||
var self = this;
|
||||
var servicesOrder = this.getServiceOrder();
|
||||
|
@ -195,6 +251,11 @@ Node.prototype.start = function(callback) {
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will stop all running services in the reverse order that they
|
||||
* were initially started.
|
||||
* @param {Function} callback - Called when all services are stopped
|
||||
*/
|
||||
Node.prototype.stop = function(callback) {
|
||||
log.info('Beginning shutdown');
|
||||
var self = this;
|
||||
|
|
|
@ -8,8 +8,7 @@ var _ = bitcore.deps._;
|
|||
* This represents an instance that keeps track of data over a series of
|
||||
* asynchronous I/O calls to get the transaction history for a group of
|
||||
* addresses. History can be queried by start and end block heights to limit large sets
|
||||
* of results (uses leveldb key streaming). See AddressService.prototype.getAddressHistory
|
||||
* for complete documentation about options.
|
||||
* of results (uses leveldb key streaming).
|
||||
*/
|
||||
function AddressHistory(args) {
|
||||
this.node = args.node;
|
||||
|
@ -27,13 +26,15 @@ function AddressHistory(args) {
|
|||
|
||||
AddressHistory.MAX_ADDRESS_QUERIES = 20;
|
||||
|
||||
/**
|
||||
* This function will give detailed history for the configured
|
||||
* addresses. See AddressService.prototype.getAddressHistory
|
||||
* for complete documentation about options and response format.
|
||||
*/
|
||||
AddressHistory.prototype.get = function(callback) {
|
||||
var self = this;
|
||||
var totalCount;
|
||||
|
||||
// TODO check for mempool inputs and outputs by a group of addresses, currently
|
||||
// each address individually loops through the mempool and does not check input scripts.
|
||||
|
||||
async.eachLimit(
|
||||
self.addresses,
|
||||
AddressHistory.MAX_ADDRESS_QUERIES,
|
||||
|
@ -68,6 +69,12 @@ AddressHistory.prototype.get = function(callback) {
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will retrieve input and output information for an address
|
||||
* and set the property `this.transactionInfo`.
|
||||
* @param {String} address - A base58check encoded address
|
||||
* @param {Function} next
|
||||
*/
|
||||
AddressHistory.prototype.getTransactionInfo = function(address, next) {
|
||||
var self = this;
|
||||
|
||||
|
@ -109,9 +116,8 @@ AddressHistory.prototype.getTransactionInfo = function(address, next) {
|
|||
};
|
||||
|
||||
/**
|
||||
* This function combines results from getInputs and getOutputs by
|
||||
* combining inputIndexes and outputIndexes for address transaction
|
||||
* matching combinations.
|
||||
* This function combines results from getInputs and getOutputs at
|
||||
* `this.transactionInfo` to be "txid" unique at `this.combinedArray`.
|
||||
*/
|
||||
AddressHistory.prototype.combineTransactionInfo = function() {
|
||||
var combinedArrayMap = {};
|
||||
|
@ -154,6 +160,9 @@ AddressHistory.prototype.combineTransactionInfo = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function to sort and slice/paginate the `combinedArray`
|
||||
*/
|
||||
AddressHistory.prototype.sortAndPaginateCombinedArray = function() {
|
||||
this.combinedArray.sort(AddressHistory.sortByHeight);
|
||||
if (!_.isUndefined(this.options.from) && !_.isUndefined(this.options.to)) {
|
||||
|
@ -161,6 +170,12 @@ AddressHistory.prototype.sortAndPaginateCombinedArray = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper sort function to order by height and then by date
|
||||
* for transactions that are in the mempool.
|
||||
* @param {Object} a - An item from the `combinedArray`
|
||||
* @param {Object} b
|
||||
*/
|
||||
AddressHistory.sortByHeight = function(a, b) {
|
||||
if (a.height < 0 && b.height < 0) {
|
||||
// Both are from the mempool, compare timestamps
|
||||
|
@ -184,6 +199,12 @@ AddressHistory.sortByHeight = function(a, b) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will transform items from the combinedArray into
|
||||
* the detailedArray with the full transaction, satoshis and confirmation.
|
||||
* @param {Object} txInfo - An item from the `combinedArray`
|
||||
* @param {Function} next
|
||||
*/
|
||||
AddressHistory.prototype.getDetailedInfo = function(txInfo, next) {
|
||||
var self = this;
|
||||
var queryMempool = _.isUndefined(self.options.queryMempool) ? true : self.options.queryMempool;
|
||||
|
@ -218,6 +239,10 @@ AddressHistory.prototype.getDetailedInfo = function(txInfo, next) {
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function for `getDetailedInfo` for getting the confirmations.
|
||||
* @param {Transaction} transaction - A transaction with a populated __height value.
|
||||
*/
|
||||
AddressHistory.prototype.getConfirmationsDetail = function(transaction) {
|
||||
var confirmations = 0;
|
||||
if (transaction.__height >= 0) {
|
||||
|
@ -226,6 +251,11 @@ AddressHistory.prototype.getConfirmationsDetail = function(transaction) {
|
|||
return confirmations;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function for `getDetailedInfo` for getting the satoshis.
|
||||
* @param {Transaction} transaction - A transaction populated with previous outputs
|
||||
* @param {Object} txInfo - An item from `combinedArray`
|
||||
*/
|
||||
AddressHistory.prototype.getSatoshisDetail = function(transaction, txInfo) {
|
||||
var satoshis = txInfo.satoshis || 0;
|
||||
|
||||
|
|
|
@ -17,6 +17,14 @@ var PublicKey = bitcore.PublicKey;
|
|||
var Address = bitcore.Address;
|
||||
var AddressHistory = require('./history');
|
||||
|
||||
/**
|
||||
* The Address Service builds upon the Database Service and the Bitcoin Service to add additional
|
||||
* functionality for getting information by base58check encoded addresses. This includes getting the
|
||||
* balance for an address, the history for a collection of addresses, and unspent outputs for
|
||||
* contstructing transactions. This is typically the core functionality for building a wallet.
|
||||
* @param {Node} node - An instance of the node
|
||||
* @param {String} [name] - An optional name of the service
|
||||
*/
|
||||
var AddressService = function(options) {
|
||||
BaseService.call(this, options);
|
||||
|
||||
|
@ -47,6 +55,10 @@ AddressService.PREFIXES = {
|
|||
AddressService.SPACER_MIN = new Buffer('00', 'hex');
|
||||
AddressService.SPACER_MAX = new Buffer('ff', 'hex');
|
||||
|
||||
/**
|
||||
* Called by the Node to get the available API methods for this service,
|
||||
* that can be exposed over the JSON-RPC interface.
|
||||
*/
|
||||
AddressService.prototype.getAPIMethods = function() {
|
||||
return [
|
||||
['getBalance', this, this.getBalance, 2],
|
||||
|
@ -58,6 +70,9 @@ AddressService.prototype.getAPIMethods = function() {
|
|||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the Bus to get the available events for this service.
|
||||
*/
|
||||
AddressService.prototype.getPublishEvents = function() {
|
||||
return [
|
||||
{
|
||||
|
@ -114,8 +129,8 @@ AddressService.prototype.transactionOutputHandler = function(messages, tx, outpu
|
|||
|
||||
/**
|
||||
* This will handle data from the daemon "tx" event, go through each of the outputs
|
||||
* and send messages to any subscribers for a particular address.
|
||||
*
|
||||
* and send messages by calling `transactionEventHandler` to any subscribers for a
|
||||
* particular address.
|
||||
* @param {Object} txInfo - The data from the daemon.on('tx') event
|
||||
* @param {Buffer} txInfo.buffer - The transaction buffer
|
||||
* @param {Boolean} txInfo.mempool - If the transaction was accepted in the mempool
|
||||
|
@ -144,7 +159,27 @@ AddressService.prototype.transactionHandler = function(txInfo) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will update the mempool address index with the necessary
|
||||
* information for further lookups. There are three indexes:
|
||||
*
|
||||
* mempoolOutputIndex, an object keyed by base58check encoded addresses with values:
|
||||
* txid - A hex string of the transaction hash
|
||||
* outputIndex - A number of the corresponding output
|
||||
* satoshis - Total number of satoshis
|
||||
* script - The script as a hex string
|
||||
*
|
||||
* mempoolInputIndex, an object keyed by base58check encoded addreses with values:
|
||||
* txid - A hex string of the transaction hash
|
||||
* inputIndex - A number of the corresponding input
|
||||
*
|
||||
* mempoolSpentIndex, an object keyed by <prevTxId>-<outputIndex>
|
||||
*
|
||||
* @param {Transaction} - An instance of a Bitcore Transaction
|
||||
*/
|
||||
AddressService.prototype.updateMempoolIndex = function(tx) {
|
||||
/* jshint maxstatements: 30 */
|
||||
|
||||
var outputLength = tx.outputs.length;
|
||||
for (var outputIndex = 0; outputIndex < outputLength; outputIndex++) {
|
||||
var output = tx.outputs[outputIndex];
|
||||
|
@ -199,6 +234,12 @@ AddressService.prototype.updateMempoolIndex = function(tx) {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is called by the Database Service when the database itself
|
||||
* has finished synchronization. It will retrieve a copy of all transactions
|
||||
* from the mempool and create an address index for fast look-ups. The previous
|
||||
* index will be reset.
|
||||
*/
|
||||
AddressService.prototype.resetMempoolIndex = function(callback) {
|
||||
var self = this;
|
||||
var transactionBuffers = self.node.services.bitcoind.getMempoolTransactions();
|
||||
|
@ -217,6 +258,12 @@ AddressService.prototype.resetMempoolIndex = function(callback) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is optimized to return address information about an output script
|
||||
* without constructing a Bitcore Address instance.
|
||||
* @param {Script} - An instance of a Bitcore Script
|
||||
* @private
|
||||
*/
|
||||
AddressService.prototype._extractAddressInfoFromScript = function(script) {
|
||||
var hashBuffer;
|
||||
var addressType;
|
||||
|
@ -242,6 +289,13 @@ AddressService.prototype._extractAddressInfoFromScript = function(script) {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* The Database Service will run this function when blocks are connected and
|
||||
* disconnected to the chain during syncing and reorganizations.
|
||||
* @param {Block} block - An instance of a Bitcore Block
|
||||
* @param {Boolean} addOutput - If the block is being removed or added to the chain
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.blockHandler = function(block, addOutput, callback) {
|
||||
var txs = block.transactions;
|
||||
var height = block.__height;
|
||||
|
@ -450,6 +504,8 @@ AddressService.prototype._decodeInputValue = function(buffer) {
|
|||
};
|
||||
|
||||
/**
|
||||
* This function is responsible for emitting events to any subscribers to the
|
||||
* `address/transaction` event.
|
||||
* @param {Object} obj
|
||||
* @param {Transaction} obj.tx - The transaction
|
||||
* @param {Object} obj.addressInfo
|
||||
|
@ -485,6 +541,8 @@ AddressService.prototype.transactionEventHandler = function(obj) {
|
|||
};
|
||||
|
||||
/**
|
||||
* The function is responsible for emitting events to any subscribers for the
|
||||
* `address/balance` event.
|
||||
* @param {Block} block
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.hashHex
|
||||
|
@ -510,6 +568,14 @@ AddressService.prototype.balanceEventHandler = function(block, obj) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The Bus will use this function to subscribe to the available
|
||||
* events for this service. For information about the available events
|
||||
* please see `getPublishEvents`.
|
||||
* @param {String} name - The name of the event
|
||||
* @param {EventEmitter} emitter - An event emitter instance
|
||||
* @param {Array} addresses - An array of addresses to subscribe
|
||||
*/
|
||||
AddressService.prototype.subscribe = function(name, emitter, addresses) {
|
||||
$.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter');
|
||||
$.checkArgument(Array.isArray(addresses), 'Second argument is expected to be an Array of addresses');
|
||||
|
@ -523,6 +589,13 @@ AddressService.prototype.subscribe = function(name, emitter, addresses) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The Bus will use this function to unsubscribe to the available
|
||||
* events for this service.
|
||||
* @param {String} name - The name of the event
|
||||
* @param {EventEmitter} emitter - An event emitter instance
|
||||
* @param {Array} addresses - An array of addresses to subscribe
|
||||
*/
|
||||
AddressService.prototype.unsubscribe = function(name, emitter, addresses) {
|
||||
$.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter');
|
||||
$.checkArgument(Array.isArray(addresses) || _.isUndefined(addresses), 'Second argument is expected to be an Array of addresses or undefined');
|
||||
|
@ -543,6 +616,11 @@ AddressService.prototype.unsubscribe = function(name, emitter, addresses) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function for the `unsubscribe` method to unsubscribe from all addresses.
|
||||
* @param {String} name - The name of the event
|
||||
* @param {EventEmitter} emiter - An instance of an event emitter
|
||||
*/
|
||||
AddressService.prototype.unsubscribeAll = function(name, emitter) {
|
||||
$.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter');
|
||||
|
||||
|
@ -555,6 +633,13 @@ AddressService.prototype.unsubscribeAll = function(name, emitter) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Will sum the total of all unspent outputs to calculate the balance
|
||||
* for an address.
|
||||
* @param {String} address - The base58check encoded address
|
||||
* @param {Boolean} queryMempool - Include mempool in the results
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.getBalance = function(address, queryMempool, callback) {
|
||||
this.getUnspentOutputs(address, queryMempool, function(err, outputs) {
|
||||
if(err) {
|
||||
|
@ -574,6 +659,13 @@ AddressService.prototype.getBalance = function(address, queryMempool, callback)
|
|||
};
|
||||
|
||||
/**
|
||||
* Will give inputs that spend previous outputs for an address as an object with:
|
||||
* address - The base58check encoded address
|
||||
* txid - A string of the transaction hash
|
||||
* outputIndex - A number of corresponding transaction input
|
||||
* height - The height of the block the transaction was included, will be -1 for mempool transactions
|
||||
* confirmations - The number of confirmations, will equal 0 for mempool transactions
|
||||
*
|
||||
* @param {String} addressStr - The relevant address
|
||||
* @param {Object} options - Additional options for query the outputs
|
||||
* @param {Number} [options.start] - The relevant start block height
|
||||
|
@ -677,6 +769,15 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Will give outputs for an address as an object with:
|
||||
* address - The base58check encoded address
|
||||
* txid - A string of the transaction hash
|
||||
* outputIndex - A number of corresponding transaction output
|
||||
* height - The height of the block the transaction was included, will be -1 for mempool transactions
|
||||
* satoshis - The satoshis value of the output
|
||||
* script - The script of the output as a hex string
|
||||
* confirmations - The number of confirmations, will equal 0 for mempool transactions
|
||||
*
|
||||
* @param {String} addressStr - The relevant address
|
||||
* @param {Object} options - Additional options for query the outputs
|
||||
* @param {Number} [options.start] - The relevant start block height
|
||||
|
@ -779,6 +880,12 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* Will give unspent outputs for an address or an array of addresses.
|
||||
* @param {Array|String} addresses - An array of addresses
|
||||
* @param {Boolean} queryMempool - Include or exclude the mempool
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, callback) {
|
||||
var self = this;
|
||||
|
||||
|
@ -804,6 +911,12 @@ AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, c
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Will give unspent outputs for an address.
|
||||
* @param {String} address - An address in base58check encoding
|
||||
* @param {Boolean} queryMempool - Include or exclude the mempool
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMempool, callback) {
|
||||
|
||||
var self = this;
|
||||
|
@ -816,7 +929,7 @@ AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMe
|
|||
}
|
||||
|
||||
var isUnspent = function(output, callback) {
|
||||
self.isUnspent(output, queryMempool, callback);
|
||||
self.isUnspent(output, callback);
|
||||
};
|
||||
|
||||
async.filter(outputs, isUnspent, function(results) {
|
||||
|
@ -825,13 +938,23 @@ AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMe
|
|||
});
|
||||
};
|
||||
|
||||
AddressService.prototype.isUnspent = function(output, queryMempool, callback) {
|
||||
this.isSpent(output, queryMempool, function(spent) {
|
||||
/**
|
||||
* Will give the inverse of isSpent
|
||||
* @param {Object} output
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.isUnspent = function(output, callback) {
|
||||
this.isSpent(output, function(spent) {
|
||||
callback(!spent);
|
||||
});
|
||||
};
|
||||
|
||||
AddressService.prototype.isSpent = function(output, queryMempool, callback) {
|
||||
/**
|
||||
* Will determine if an output is spent, results do not include the mempool.
|
||||
* @param {Object} output - An output as returned from getOutputs
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.isSpent = function(output, callback) {
|
||||
var self = this;
|
||||
var txid = output.prevTxId ? output.prevTxId.toString('hex') : output.txid;
|
||||
|
||||
|
@ -883,18 +1006,18 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
|
|||
};
|
||||
|
||||
/**
|
||||
* This will return an object with:
|
||||
* This will give an object with:
|
||||
* balance - confirmed balance
|
||||
* unconfirmedBalance - unconfirmed balance
|
||||
* totalReceived - satoshis received
|
||||
* totalSpent - satoshis spent
|
||||
* appearances - number of times used in confirmed transactions
|
||||
* unconfirmedAppearances - number of times used in unconfirmed transactions
|
||||
* appearances - number of transactions
|
||||
* unconfirmedAppearances - number of unconfirmed transactions
|
||||
* txids - list of txids (unless noTxList is set)
|
||||
*
|
||||
* @param {String} address
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.noTxList - if set, txid array will not be included
|
||||
* @param {Boolean} [options.noTxList] - if set, txid array will not be included
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.getAddressSummary = function(address, options, callback) {
|
||||
|
|
|
@ -4,18 +4,16 @@ var fs = require('fs');
|
|||
var util = require('util');
|
||||
var bindings = require('bindings')('bitcoind.node');
|
||||
var mkdirp = require('mkdirp');
|
||||
var async = require('async');
|
||||
var bitcore = require('bitcore');
|
||||
var Transaction = require('../transaction');
|
||||
var $ = bitcore.util.preconditions;
|
||||
var index = require('../');
|
||||
var log = index.log;
|
||||
var Service = require('../service');
|
||||
|
||||
/**
|
||||
* Provides an interface to native bindings to Bitcoin Core
|
||||
* Provides an interface to native bindings to Bitcoin Core compiled as a static library.
|
||||
* The C++ bindings can be found at src/libbitcoind.cc
|
||||
* @param {Object} options
|
||||
* @param {String} options.datadir - The bitcoin data directory
|
||||
* @param {Node} options.node - A reference to the node
|
||||
*/
|
||||
function Bitcoin(options) {
|
||||
|
@ -140,6 +138,10 @@ Bitcoin.prototype._onReady = function(result, callback) {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Node to start the service
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Bitcoin.prototype.start = function(callback) {
|
||||
var self = this;
|
||||
|
||||
|
@ -176,70 +178,183 @@ Bitcoin.prototype.start = function(callback) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to determine the state of the database.
|
||||
* @returns {Boolean} If the database is fully synced
|
||||
*/
|
||||
Bitcoin.prototype.isSynced = function() {
|
||||
return bindings.isSynced();
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to determine the progress of the database.
|
||||
* @returns {Number} An estimated percentage of the syncronization status
|
||||
*/
|
||||
Bitcoin.prototype.syncPercentage = function() {
|
||||
return bindings.syncPercentage();
|
||||
};
|
||||
|
||||
Bitcoin.prototype.getBlock = function(blockhash, callback) {
|
||||
return bindings.getBlock(blockhash, callback);
|
||||
/**
|
||||
* Will retreive a block as a Node.js Buffer from disk.
|
||||
* @param {String|Number} block - A block hash or block height number
|
||||
*/
|
||||
Bitcoin.prototype.getBlock = function(block, callback) {
|
||||
return bindings.getBlock(block, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will return the spent status of an output (not including the mempool)
|
||||
* @param {String} txid - The transaction hash
|
||||
* @param {Number} outputIndex - The output index in the transaction
|
||||
* @returns {Boolean} If the output has been spent
|
||||
*/
|
||||
Bitcoin.prototype.isSpent = function(txid, outputIndex) {
|
||||
return bindings.isSpent(txid, outputIndex);
|
||||
};
|
||||
|
||||
Bitcoin.prototype.getBlockIndex = function(blockHash) {
|
||||
return bindings.getBlockIndex(blockHash);
|
||||
/**
|
||||
* Will return the block index information, the output will have the format:
|
||||
* {
|
||||
* prevHash: '7194fcf33f58c96720f88f21ab28c34ebc5638c5f88d7838517deb27313b59de',
|
||||
* hash: '7c5caf0af1bf16e3467b275a3b408bc1d251bff3c25be20cb727c47b66a7b216',
|
||||
* chainWork: '0000000000000000000000000000000000000000000000000000000000000016',
|
||||
* height: 10
|
||||
* }
|
||||
* @param {String|Number} block - A block hash or block height
|
||||
* @returns {Object}
|
||||
*/
|
||||
Bitcoin.prototype.getBlockIndex = function(block) {
|
||||
return bindings.getBlockIndex(block);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will return if the block is a part of the main chain.
|
||||
* @param {String} blockHash
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Bitcoin.prototype.isMainChain = function(blockHash) {
|
||||
return bindings.isMainChain(blockHash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will estimate the fee per kilobyte.
|
||||
* @param {Number} blocks - The number of blocks for the transaction to be confirmed.
|
||||
* @returns {Number}
|
||||
*/
|
||||
Bitcoin.prototype.estimateFee = function(blocks) {
|
||||
return bindings.estimateFee(blocks);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will add a transaction to the mempool and relay to connected peers, the function
|
||||
* will throw an error if there were validation problems.
|
||||
* @param {String} transaction - The hex string of the transaction
|
||||
* @param {Boolean} allowAbsurdFees - Enable large fees
|
||||
*/
|
||||
Bitcoin.prototype.sendTransaction = function(transaction, allowAbsurdFees) {
|
||||
return bindings.sendTransaction(transaction, allowAbsurdFees);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will get a transaction as a Node.js Buffer from disk and the mempool.
|
||||
* @param {String} txid - The transaction hash
|
||||
* @param {Boolean} queryMempool - Include the mempool
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Bitcoin.prototype.getTransaction = function(txid, queryMempool, callback) {
|
||||
return bindings.getTransaction(txid, queryMempool, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will get a transation with additional information about the block, in the format:
|
||||
* {
|
||||
* blockHash: '2725743288feae6bdaa976590af7cb12d7b535b5a242787de6d2789c73682ed1',
|
||||
* height: 48,
|
||||
* timestamp: 1442951110, // in seconds
|
||||
* buffer: <Buffer...> // transaction buffer
|
||||
* }
|
||||
* @param {String} txid - The transaction hash
|
||||
* @param {Boolean} queryMempool - Include the mempool
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Bitcoin.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) {
|
||||
return bindings.getTransactionWithBlockInfo(txid, queryMempool, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will return the entire mempool as an Array of transaction Buffers.
|
||||
* @returns {Array}
|
||||
*/
|
||||
Bitcoin.prototype.getMempoolTransactions = function() {
|
||||
return bindings.getMempoolTransactions();
|
||||
};
|
||||
|
||||
Bitcoin.prototype.addMempoolUncheckedTransaction = function(txBuffer) {
|
||||
return bindings.addMempoolUncheckedTransaction(txBuffer);
|
||||
/**
|
||||
* Will add a transaction to the mempool without any validation. This is used
|
||||
* exclusively for testing purposes.
|
||||
* @param {String} transaction - The hex string for the transaction
|
||||
*/
|
||||
Bitcoin.prototype.addMempoolUncheckedTransaction = function(transaction) {
|
||||
return bindings.addMempoolUncheckedTransaction(transaction);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will get the best block hash for the chain.
|
||||
* @returns {String}
|
||||
*/
|
||||
Bitcoin.prototype.getBestBlockHash = function() {
|
||||
return bindings.getBestBlockHash();
|
||||
};
|
||||
|
||||
/**
|
||||
* Will get the next block hash for a block hash.
|
||||
* @param {String} hash - The starting block hash
|
||||
* @returns {String}
|
||||
*/
|
||||
Bitcoin.prototype.getNextBlockHash = function(hash) {
|
||||
return bindings.getNextBlockHash(hash);
|
||||
};
|
||||
|
||||
/**
|
||||
* This query is expensive and can take quite a while to return. It will give
|
||||
* statistics about the database, in the format:
|
||||
* {
|
||||
* height: 151,
|
||||
* bestblock: '085241174bf5c9d606e591b3cbf52f9306f3d6aca50ea77604ca898472dc4669',
|
||||
* transactions: 151,
|
||||
* txouts: 151,
|
||||
* bytes_serialized: 10431,
|
||||
* hash_serialized: 'b1e42afb98d0d9f1978c048648685590a7ef0be46f83f509fc0d0668d6a39d8c',
|
||||
* total_amount: 750000000000
|
||||
* }
|
||||
* @returns {Object}
|
||||
*/
|
||||
Bitcoin.prototype.getTxOutSetInfo = function() {
|
||||
return bindings.getTxOutSetInfo();
|
||||
};
|
||||
|
||||
/**
|
||||
* This will return information about the database in the format:
|
||||
* {
|
||||
* version: 110000,
|
||||
* protocolversion: 70002,
|
||||
* blocks: 151,
|
||||
* timeoffset: 0,
|
||||
* connections: 0,
|
||||
* difficulty: 4.6565423739069247e-10,
|
||||
* testnet: false,
|
||||
* relayfee: 1000,
|
||||
* errors: ''
|
||||
* }
|
||||
*/
|
||||
Bitcoin.prototype.getInfo = function() {
|
||||
return bindings.getInfo();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Node to stop the service.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Bitcoin.prototype.stop = function(callback) {
|
||||
return bindings.stop(function(err, status) {
|
||||
if (err) {
|
||||
|
|
|
@ -18,12 +18,13 @@ var Transaction = require('../transaction');
|
|||
var Service = require('../service');
|
||||
|
||||
/**
|
||||
* Represents the current state of the bitcoin blockchain. Other services
|
||||
* can extend the data that is indexed by implementing a `blockHandler` method.
|
||||
* This service synchronizes a leveldb database with bitcoin block chain by connecting and
|
||||
* disconnecting blocks to build new indexes that can be queried. Other services can extend
|
||||
* the data that is indexed by implementing a `blockHandler` method.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {String} options.datadir - The bitcoin data directory
|
||||
* @param {Node} options.node - A reference to the node
|
||||
* @param {Node} options.store - A levelup backend store
|
||||
*/
|
||||
function DB(options) {
|
||||
/* jshint maxstatements: 20 */
|
||||
|
@ -64,6 +65,10 @@ DB.PREFIXES = {
|
|||
BLOCKS: new Buffer('01', 'hex')
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will set `this.dataPath` based on `this.node.network`.
|
||||
* @private
|
||||
*/
|
||||
DB.prototype._setDataPath = function() {
|
||||
$.checkState(this.node.datadir, 'Node is expected to have a "datadir" property');
|
||||
var regtest = Networks.get('regtest');
|
||||
|
@ -78,6 +83,10 @@ DB.prototype._setDataPath = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Node to start the service.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.start = function(callback) {
|
||||
|
||||
var self = this;
|
||||
|
@ -140,9 +149,12 @@ DB.prototype.start = function(callback) {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Node to stop the service
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.stop = function(callback) {
|
||||
var self = this;
|
||||
|
||||
|
@ -156,6 +168,10 @@ DB.prototype.stop = function(callback) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Will give information about the database from bitcoin.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.getInfo = function(callback) {
|
||||
var self = this;
|
||||
setImmediate(function() {
|
||||
|
@ -166,22 +182,32 @@ DB.prototype.getInfo = function(callback) {
|
|||
|
||||
/**
|
||||
* Closes the underlying store database
|
||||
* @param {Function} callback - A function that accepts: Error
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.close = function(callback) {
|
||||
this.store.close(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is responsible for emitting `db/transaction` events.
|
||||
* @param {Object} txInfo - The data from the bitcoind.on('tx') event
|
||||
* @param {Buffer} txInfo.buffer - The transaction buffer
|
||||
* @param {Boolean} txInfo.mempool - If the transaction was accepted in the mempool
|
||||
* @param {String} txInfo.hash - The hash of the transaction
|
||||
*/
|
||||
DB.prototype.transactionHandler = function(txInfo) {
|
||||
var tx = Transaction().fromBuffer(txInfo.buffer);
|
||||
for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
||||
this.subscriptions.transaction[i].emit('transaction', {
|
||||
this.subscriptions.transaction[i].emit('db/transaction', {
|
||||
rejected: !txInfo.mempool,
|
||||
tx: tx
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Node to determine the available API methods.
|
||||
*/
|
||||
DB.prototype.getAPIMethods = function() {
|
||||
var methods = [
|
||||
['getBlock', this, this.getBlock, 1],
|
||||
|
@ -194,17 +220,21 @@ DB.prototype.getAPIMethods = function() {
|
|||
return methods;
|
||||
};
|
||||
|
||||
/**
|
||||
* Will get a block from bitcoind and give a Bitcore Block
|
||||
* @param {String|Number} hash - A block hash or block height
|
||||
*/
|
||||
DB.prototype.getBlock = function(hash, callback) {
|
||||
this.node.services.bitcoind.getBlock(hash, function(err, blockData) {
|
||||
this.node.services.bitcoind.getBlock(hash, function(err, blockBuffer) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, Block.fromBuffer(blockData));
|
||||
callback(null, Block.fromBuffer(blockBuffer));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get block hashes between two timestamps
|
||||
* Get block hashes between two timestamps
|
||||
* @param {Number} high - high timestamp, in seconds, inclusive
|
||||
* @param {Number} low - low timestamp, in seconds, inclusive
|
||||
* @param {Function} callback
|
||||
|
@ -212,10 +242,12 @@ DB.prototype.getBlock = function(hash, callback) {
|
|||
DB.prototype.getBlockHashesByTimestamp = function(high, low, callback) {
|
||||
var self = this;
|
||||
var hashes = [];
|
||||
var lowKey;
|
||||
var highKey;
|
||||
|
||||
try {
|
||||
var lowKey = this._encodeBlockIndexKey(low);
|
||||
var highKey = this._encodeBlockIndexKey(high);
|
||||
lowKey = this._encodeBlockIndexKey(low);
|
||||
highKey = this._encodeBlockIndexKey(high);
|
||||
} catch(e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
@ -244,13 +276,18 @@ DB.prototype.getBlockHashesByTimestamp = function(high, low, callback) {
|
|||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
callback(null, hashes);
|
||||
});
|
||||
|
||||
return stream;
|
||||
};
|
||||
|
||||
/**
|
||||
* Will give a Bitcore Transaction from bitcoind by txid
|
||||
* @param {String} txid - A transaction hash
|
||||
* @param {Boolean} queryMempool - Include the mempool
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.getTransaction = function(txid, queryMempool, callback) {
|
||||
this.node.services.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) {
|
||||
if (err) {
|
||||
|
@ -264,6 +301,12 @@ DB.prototype.getTransaction = function(txid, queryMempool, callback) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Will give a Bitcore Transaction and populated information about the block included.
|
||||
* @param {String} txid - A transaction hash
|
||||
* @param {Boolean} queryMempool - Include the mempool
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) {
|
||||
this.node.services.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) {
|
||||
if (err) {
|
||||
|
@ -279,20 +322,34 @@ DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Will send a transaction to the Bitcoin network.
|
||||
* @param {Transaction} tx - An instance of a Bitcore Transaction
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.sendTransaction = function(tx, callback) {
|
||||
var txString;
|
||||
if (tx instanceof Transaction) {
|
||||
tx = tx.toString();
|
||||
txString = tx.serialize();
|
||||
} else {
|
||||
txString = tx;
|
||||
}
|
||||
$.checkArgument(typeof tx === 'string', 'Argument must be a hex string or Transaction');
|
||||
$.checkArgument(typeof txString === 'string', 'Argument must be a hex string or Transaction');
|
||||
|
||||
try {
|
||||
var txid = this.node.services.bitcoind.sendTransaction(tx);
|
||||
var txid = this.node.services.bitcoind.sendTransaction(txString);
|
||||
return callback(null, txid);
|
||||
} catch(err) {
|
||||
return callback(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Will estimate fees for a transaction and give a result in
|
||||
* satoshis per kilobyte. Similar to the bitcoind estimateFee method.
|
||||
* @param {Number} blocks - The number of blocks for the transaction to be included.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DB.prototype.estimateFee = function(blocks, callback) {
|
||||
var self = this;
|
||||
setImmediate(function() {
|
||||
|
@ -300,6 +357,9 @@ DB.prototype.estimateFee = function(blocks, callback) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the Bus to determine the available events.
|
||||
*/
|
||||
DB.prototype.getPublishEvents = function() {
|
||||
return [
|
||||
{
|
||||
|
@ -412,6 +472,10 @@ DB.prototype.disconnectBlock = function(block, callback) {
|
|||
this.runAllBlockHandlers(block, false, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will run all `resetMempoolIndex` methods implemented on sibling
|
||||
* services to update the mempool indexes.
|
||||
*/
|
||||
DB.prototype.runAllMempoolIndexes = function(callback) {
|
||||
async.eachSeries(
|
||||
this.node.services,
|
||||
|
@ -427,8 +491,8 @@ DB.prototype.runAllMempoolIndexes = function(callback) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Will collect all database operations for a block from other services
|
||||
* and save to the database.
|
||||
* Will collect all database operations for a block from other services that implement
|
||||
* `blockHandler` methods and then save operations to the database.
|
||||
* @param {Block} block - The bitcore block
|
||||
* @param {Boolean} add - If the block is being added/connected or removed/disconnected
|
||||
* @param {Function} callback
|
||||
|
@ -439,7 +503,7 @@ DB.prototype.runAllBlockHandlers = function(block, add, callback) {
|
|||
|
||||
// Notify block subscribers
|
||||
for (var i = 0; i < this.subscriptions.block.length; i++) {
|
||||
this.subscriptions.block[i].emit('block', block.hash);
|
||||
this.subscriptions.block[i].emit('db/block', block.hash);
|
||||
}
|
||||
|
||||
// Update block index
|
||||
|
@ -497,7 +561,7 @@ DB.prototype._decodeBlockIndexValue = function(value) {
|
|||
|
||||
/**
|
||||
* This function will find the common ancestor between the current chain and a forked block,
|
||||
* by moving backwards from the forked block until it meets the current chain.
|
||||
* by moving backwards on both chains until there is a meeting point.
|
||||
* @param {Block} block - The new tip that forks the current chain.
|
||||
* @param {Function} done - A callback function that is called when complete.
|
||||
*/
|
||||
|
|
|
@ -11,6 +11,20 @@ var index = require('../');
|
|||
var log = index.log;
|
||||
var fs = require('fs');
|
||||
|
||||
/**
|
||||
* This service respresents a hub for combining several services over a single HTTP port. Services
|
||||
* can extend routes by implementing the methods `getRoutePrefix` and `setupRoutes`. Additionally
|
||||
* events that are exposed via the `getPublishEvents` and API methods exposed via `getAPIMethods`
|
||||
* will be available over a socket.io connection.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Node} options.node - A reference to the node
|
||||
* @param {Boolean} options.https - Enable https, will default to node.https settings.
|
||||
* @param {Object} options.httpsOptions - Options passed into https.createServer, defaults to node settings.
|
||||
* @param {String} options.httpsOptions.key - Path to key file
|
||||
* @param {String} options.httpsOptions.cert - Path to cert file
|
||||
* @param {Number} options.port - The port for the service, defaults to node settings.
|
||||
*/
|
||||
var WebService = function(options) {
|
||||
var self = this;
|
||||
this.node = options.node;
|
||||
|
@ -30,8 +44,11 @@ inherits(WebService, BaseService);
|
|||
|
||||
WebService.dependencies = [];
|
||||
|
||||
/**
|
||||
* Called by Node to start the service
|
||||
* @param {Function} callback
|
||||
*/
|
||||
WebService.prototype.start = function(callback) {
|
||||
var self = this;
|
||||
this.app = express();
|
||||
this.app.use(bodyParser.json());
|
||||
|
||||
|
@ -48,6 +65,10 @@ WebService.prototype.start = function(callback) {
|
|||
setImmediate(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Node. stop the service
|
||||
* @param {Function} callback
|
||||
*/
|
||||
WebService.prototype.stop = function(callback) {
|
||||
var self = this;
|
||||
|
||||
|
@ -55,11 +76,14 @@ WebService.prototype.stop = function(callback) {
|
|||
if(self.server) {
|
||||
self.server.close();
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will iterate over all of the available services gathering
|
||||
* all of the exposed HTTP routes.
|
||||
*/
|
||||
WebService.prototype.setupAllRoutes = function() {
|
||||
for(var key in this.node.services) {
|
||||
var subApp = new express.Router();
|
||||
|
@ -67,13 +91,17 @@ WebService.prototype.setupAllRoutes = function() {
|
|||
|
||||
if(service.getRoutePrefix && service.setupRoutes) {
|
||||
this.app.use('/' + this.node.services[key].getRoutePrefix(), subApp);
|
||||
this.node.services[key].setupRoutes(subApp, express);
|
||||
this.node.services[key].setupRoutes(subApp, express);
|
||||
} else {
|
||||
log.debug('No routes defined for: ' + key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will contruct an API methods map of all of the
|
||||
* available methods that can be called from enable services.
|
||||
*/
|
||||
WebService.prototype.createMethodsMap = function() {
|
||||
var self = this;
|
||||
var methods = this.node.getAllAPIMethods();
|
||||
|
@ -93,6 +121,10 @@ WebService.prototype.createMethodsMap = function() {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will gather all of the available events exposed from
|
||||
* the enabled services.
|
||||
*/
|
||||
WebService.prototype.getEventNames = function() {
|
||||
var events = this.node.getAllPublishEvents();
|
||||
var eventNames = [];
|
||||
|
@ -101,9 +133,8 @@ WebService.prototype.getEventNames = function() {
|
|||
if(eventNames.indexOf(name) > -1) {
|
||||
throw new Error('Duplicate event ' + name);
|
||||
}
|
||||
|
||||
eventNames.push(name);
|
||||
};
|
||||
}
|
||||
|
||||
events.forEach(function(event) {
|
||||
addEventName(event.name);
|
||||
|
@ -118,9 +149,12 @@ WebService.prototype.getEventNames = function() {
|
|||
return eventNames;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is responsible for managing a socket.io connection, including
|
||||
* instantiating a new Bus, subscribing/unsubscribing and handling RPC commands.
|
||||
* @param {Socket} socket - A socket.io socket instance
|
||||
*/
|
||||
WebService.prototype.socketHandler = function(socket) {
|
||||
var self = this;
|
||||
|
||||
var bus = this.node.openBus();
|
||||
|
||||
socket.on('message', this.socketMessageHandler.bind(this));
|
||||
|
@ -153,6 +187,12 @@ WebService.prototype.socketHandler = function(socket) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This method will handle incoming RPC messages to a socket.io connection,
|
||||
* call the appropriate method, and respond with the result.
|
||||
* @param {Object} message - The socket.io "message" object
|
||||
* @param {Function} socketCallback
|
||||
*/
|
||||
WebService.prototype.socketMessageHandler = function(message, socketCallback) {
|
||||
if (this.methodsMap[message.method]) {
|
||||
var params = message.params;
|
||||
|
@ -195,6 +235,10 @@ WebService.prototype.socketMessageHandler = function(message, socketCallback) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method will read `key` and `cert` from disk based on `httpsOptions` and
|
||||
* replace the options with the files.
|
||||
*/
|
||||
WebService.prototype.transformHttpsOptions = function() {
|
||||
if(!this.httpsOptions || !this.httpsOptions.key || !this.httpsOptions.cert) {
|
||||
throw new Error('Missing https options');
|
||||
|
|
|
@ -454,7 +454,7 @@ describe('Address Service', function() {
|
|||
store: {
|
||||
createReadStream: sinon.stub().returns(testStream)
|
||||
}
|
||||
}
|
||||
};
|
||||
var testnode = {
|
||||
services: {
|
||||
db: db,
|
||||
|
@ -475,7 +475,7 @@ describe('Address Service', function() {
|
|||
height: -1,
|
||||
confirmations: 0
|
||||
}
|
||||
]
|
||||
];
|
||||
am.getInputs(address, args, function(err, inputs) {
|
||||
should.not.exist(err);
|
||||
inputs.length.should.equal(1);
|
||||
|
@ -657,7 +657,7 @@ describe('Address Service', function() {
|
|||
script: '76a914f6db95c81dea3d10f0ff8d890927751bf7b203c188ac',
|
||||
outputIndex: 0
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
am.getOutputs(address, options, function(err, outputs) {
|
||||
should.not.exist(err);
|
||||
|
@ -836,7 +836,7 @@ describe('Address Service', function() {
|
|||
|
||||
var am = new AddressService({node: mocknode});
|
||||
am.getOutputs = sinon.stub().callsArgWith(2, null, outputs);
|
||||
am.isUnspent = function(output, queryMempool, callback) {
|
||||
am.isUnspent = function(output, callback) {
|
||||
callback(!outputs[i].spent);
|
||||
i++;
|
||||
};
|
||||
|
@ -878,24 +878,24 @@ describe('Address Service', function() {
|
|||
});
|
||||
|
||||
it('should give true when isSpent() gives false', function(done) {
|
||||
am.isSpent = sinon.stub().callsArgWith(2, false);
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
am.isSpent = sinon.stub().callsArgWith(1, false);
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', function(unspent) {
|
||||
unspent.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should give false when isSpent() gives true', function(done) {
|
||||
am.isSpent = sinon.stub().callsArgWith(2, true);
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
am.isSpent = sinon.stub().callsArgWith(1, true);
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', function(unspent) {
|
||||
unspent.should.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should give false when isSpent() returns an error', function(done) {
|
||||
am.isSpent = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
am.isSpent = sinon.stub().callsArgWith(1, new Error('error'));
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', function(unspent) {
|
||||
unspent.should.equal(false);
|
||||
done();
|
||||
});
|
||||
|
@ -922,7 +922,7 @@ describe('Address Service', function() {
|
|||
});
|
||||
|
||||
it('should give true if bitcoind.isSpent gives true', function(done) {
|
||||
am.isSpent('output', true, function(spent) {
|
||||
am.isSpent('output', function(spent) {
|
||||
spent.should.equal(true);
|
||||
done();
|
||||
});
|
||||
|
@ -1001,30 +1001,30 @@ describe('Address Service', function() {
|
|||
};
|
||||
var inputs = [
|
||||
{
|
||||
"txid": "9f183412de12a6c1943fc86c390174c1cde38d709217fdb59dcf540230fa58a6",
|
||||
"height": -1,
|
||||
"confirmations": 0,
|
||||
"addresses": {
|
||||
"mpkDdnLq26djg17s6cYknjnysAm3QwRzu2": {
|
||||
"outputIndexes": [],
|
||||
"inputIndexes": [
|
||||
'txid': '9f183412de12a6c1943fc86c390174c1cde38d709217fdb59dcf540230fa58a6',
|
||||
'height': -1,
|
||||
'confirmations': 0,
|
||||
'addresses': {
|
||||
'mpkDdnLq26djg17s6cYknjnysAm3QwRzu2': {
|
||||
'outputIndexes': [],
|
||||
'inputIndexes': [
|
||||
3
|
||||
]
|
||||
}
|
||||
},
|
||||
"address": "mpkDdnLq26djg17s6cYknjnysAm3QwRzu2"
|
||||
'address': 'mpkDdnLq26djg17s6cYknjnysAm3QwRzu2'
|
||||
}
|
||||
];
|
||||
|
||||
var outputs = [
|
||||
{
|
||||
"address": "mpkDdnLq26djg17s6cYknjnysAm3QwRzu2",
|
||||
"txid": "689e9f543fa4aa5b2daa3b5bb65f9a00ad5aa1a2e9e1fc4e11061d85f2aa9bc5",
|
||||
"outputIndex": 0,
|
||||
"height": 556351,
|
||||
"satoshis": 3487110,
|
||||
"script": "76a914653b58493c2208481e0902a8ffb97b8112b13fe188ac",
|
||||
"confirmations": 13190
|
||||
'address': 'mpkDdnLq26djg17s6cYknjnysAm3QwRzu2',
|
||||
'txid': '689e9f543fa4aa5b2daa3b5bb65f9a00ad5aa1a2e9e1fc4e11061d85f2aa9bc5',
|
||||
'outputIndex': 0,
|
||||
'height': 556351,
|
||||
'satoshis': 3487110,
|
||||
'script': '76a914653b58493c2208481e0902a8ffb97b8112b13fe188ac',
|
||||
'confirmations': 13190
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -513,6 +513,21 @@ describe('DB Service', function() {
|
|||
});
|
||||
|
||||
describe('#sendTransaction', function() {
|
||||
it('should handle a basic serialized transaction hex string', function(done) {
|
||||
var db = new DB(baseConfig);
|
||||
db.node = {};
|
||||
db.node.services = {};
|
||||
db.node.services.bitcoind = {
|
||||
sendTransaction: sinon.stub().returns('txid')
|
||||
};
|
||||
|
||||
var tx = 'hexstring';
|
||||
db.sendTransaction(tx, function(err, txid) {
|
||||
should.not.exist(err);
|
||||
txid.should.equal('txid');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should give the txid on success', function(done) {
|
||||
var db = new DB(baseConfig);
|
||||
db.node = {};
|
||||
|
@ -522,8 +537,10 @@ describe('DB Service', function() {
|
|||
};
|
||||
|
||||
var tx = new Transaction();
|
||||
tx.serialize = sinon.stub().returns('txstring');
|
||||
db.sendTransaction(tx, function(err, txid) {
|
||||
should.not.exist(err);
|
||||
tx.serialize.callCount.should.equal(1);
|
||||
txid.should.equal('txid');
|
||||
done();
|
||||
});
|
||||
|
@ -537,7 +554,9 @@ describe('DB Service', function() {
|
|||
};
|
||||
|
||||
var tx = new Transaction();
|
||||
tx.serialize = sinon.stub().returns('txstring');
|
||||
db.sendTransaction(tx, function(err, txid) {
|
||||
tx.serialize.callCount.should.equal(1);
|
||||
should.exist(err);
|
||||
done();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue