Fix typos and add detailed documentation to services.md with examples for programatic use of services as well as using indexes within services.
This commit is contained in:
parent
2caadd1955
commit
46578659a8
@ -4,7 +4,7 @@ description: Errors for Bitcore Node
|
|||||||
---
|
---
|
||||||
# Errors
|
# Errors
|
||||||
|
|
||||||
Many times there are cases where an error condition can be gracefully handled depending on a particular use. To assist in better error handling, errors will have different types so that it's possible to determine the type of error and handle appropriatly.
|
Many times there are cases where an error condition can be gracefully handled depending on a particular use. To assist in better error handling, errors will have different types so that it's possible to determine the type of error and handle appropriately.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
node.services.address.getUnspentOutputs('00000000839a8...', function(err, outputs) {
|
node.services.address.getUnspentOutputs('00000000839a8...', function(err, outputs) {
|
||||||
|
|||||||
157
docs/services.md
157
docs/services.md
@ -37,6 +37,59 @@ Services correspond with a Node.js module as described in 'package.json', for ex
|
|||||||
|
|
||||||
*Note:* If you already have a bitcore-node database, and you want to query data from previous blocks in the blockchain, you will need to reindex. Reindexing right now means deleting your bitcore-node database and resyncing.
|
*Note:* If you already have a bitcore-node database, and you want to query data from previous blocks in the blockchain, you will need to reindex. Reindexing right now means deleting your bitcore-node database and resyncing.
|
||||||
|
|
||||||
|
## Using Services Programmatically
|
||||||
|
If instead you would like to run a custom node, you can include services by including them in your configuration object when initializing a new node.
|
||||||
|
|
||||||
|
```js
|
||||||
|
//Require bitcore
|
||||||
|
var bitcore = require('bitcore-node');
|
||||||
|
|
||||||
|
//Services
|
||||||
|
var Address = bitcore.services.Address;
|
||||||
|
var Bitcoin = bitcore.services.Bitcoin;
|
||||||
|
var DB = bitcore.services.DB;
|
||||||
|
var Web = bitcore.services.Web;
|
||||||
|
|
||||||
|
var myNode = new bitcore.Node({
|
||||||
|
datadir: '~/.bitcore',
|
||||||
|
network: {
|
||||||
|
name: 'livenet'
|
||||||
|
},
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
name: "address",
|
||||||
|
module: Address,
|
||||||
|
config: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bitcoind',
|
||||||
|
module: Bitcoin,
|
||||||
|
config: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'db',
|
||||||
|
module: DB,
|
||||||
|
config: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'web',
|
||||||
|
module: Web,
|
||||||
|
config: {
|
||||||
|
port: 3001
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
Now that you've loaded your services you can access them via `myNode.services.<service-name>.<method-name>`. For example
|
||||||
|
if you wanted to check the balance of an address, you could access the address service like so.
|
||||||
|
|
||||||
|
```js
|
||||||
|
myNode.services.address.getBalance('1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v', false, function(err, total) {
|
||||||
|
console.log(total); //Satoshi amount of this address
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Writing a Service
|
## Writing a Service
|
||||||
|
|
||||||
A new service can be created by inheriting from `Node.Service` and implementing these methods and properties:
|
A new service can be created by inheriting from `Node.Service` and implementing these methods and properties:
|
||||||
@ -51,4 +104,106 @@ A new service can be created by inheriting from `Node.Service` and implementing
|
|||||||
|
|
||||||
The `package.json` for the service module can either export the `Node.Service` directly, or specify a specific module to load by including `"bitcoreNode": "lib/bitcore-node.js"`.
|
The `package.json` for the service module can either export the `Node.Service` directly, or specify a specific module to load by including `"bitcoreNode": "lib/bitcore-node.js"`.
|
||||||
|
|
||||||
Please take a look at some of the existing services for implemenation specifics.
|
Please take a look at some of the existing services for implementation specifics.
|
||||||
|
|
||||||
|
### Adding an index
|
||||||
|
|
||||||
|
One quite useful feature exposed to services is the ability to index arbitrary data in the blockchain. To do so we make
|
||||||
|
use of leveldb, a simple key-value store. As a service we can expose a 'blockHandler' function which is called each time
|
||||||
|
a new block is added or removed from the blockchain. This gives us access to every new transaction received, allowing
|
||||||
|
us to index them. Let's take a look at an example where we will index the time that a transaction was confirmed.
|
||||||
|
|
||||||
|
```js
|
||||||
|
//Index prefix, so that we can determine the difference between our index
|
||||||
|
//and the indexes provided by other services
|
||||||
|
MyService.datePrefix = new Buffer('10');
|
||||||
|
|
||||||
|
MyService.minPosition = new Buffer('00000');
|
||||||
|
MyService.maxPosition = new Buffer('99999');
|
||||||
|
|
||||||
|
//This function is automatically called when a block is added or receieved
|
||||||
|
MyService.prototype.prototype.blockHandler = function(block, addOutput, callback) {
|
||||||
|
|
||||||
|
//Determine if the block is added or removed, and therefore whether we are adding
|
||||||
|
//or deleting indexes
|
||||||
|
var databaseAction = 'put';
|
||||||
|
if (!addOutput) {
|
||||||
|
databaseAction = 'del';
|
||||||
|
}
|
||||||
|
|
||||||
|
//An array of all leveldb operations we will be committing
|
||||||
|
var operations = [];
|
||||||
|
|
||||||
|
//Timestamp of the current block
|
||||||
|
var blocktime = new Buffer(block.header.time);
|
||||||
|
|
||||||
|
for (var i = 0; i < block.transactions.length; i++) {
|
||||||
|
var transaction = block.transactions[i];
|
||||||
|
var txid = new Buffer(transaction.id, 'hex');
|
||||||
|
var position = new Buffer(('0000' + i).slice(-5));
|
||||||
|
|
||||||
|
//To be able to query this txid by the block date we create an index, leading with the prefix we
|
||||||
|
//defined earlier, the the current blocktime, and finally a differentiator, in this case the index
|
||||||
|
//of this transaction in the block's transaction list
|
||||||
|
var indexOperation = {
|
||||||
|
type: databaseAction,
|
||||||
|
key: Buffer.concat([this.datePrefix, blockTime, position]),
|
||||||
|
value: txid
|
||||||
|
};
|
||||||
|
|
||||||
|
//Now we push this index into our list of operations that should be performed
|
||||||
|
operations.push(indexOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Send the list of db operations back so they can be performed
|
||||||
|
setImmediate(function() {
|
||||||
|
callback(null, operations);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Retrieving data using an index
|
||||||
|
With our block handler code every transaction in the blockchain will now be indexed. However, if we want to query this
|
||||||
|
data we need to add a method to our service to expose it.
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
MyService.prototype.getTransactionIdsByDate = function(startDate, endDate, callback) {
|
||||||
|
|
||||||
|
var error;
|
||||||
|
var transactions = [];
|
||||||
|
|
||||||
|
//Read data from leveldb which is between our startDate and endDate
|
||||||
|
var stream = this.node.services.db.store.createReadStream({
|
||||||
|
gte: Buffer.concat([
|
||||||
|
MyService.datePrefix,
|
||||||
|
new Buffer(startDate),
|
||||||
|
MyService.minPosition
|
||||||
|
]),
|
||||||
|
lte: Buffer.concat([
|
||||||
|
MyService.datePrefix,
|
||||||
|
new Buffer(endDate),
|
||||||
|
MyService.maxPosition
|
||||||
|
]),
|
||||||
|
valueEncoding: 'binary',
|
||||||
|
keyEncoding: 'binary'
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('data', function(data) {
|
||||||
|
transactions.push(data.value.toString('hex'));
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('error', function(streamError) {
|
||||||
|
if (streamError) {
|
||||||
|
error = streamError;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('close', function() {
|
||||||
|
if (error) {
|
||||||
|
return callback(error);
|
||||||
|
}
|
||||||
|
callback(null, transactions);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
@ -23,7 +23,7 @@ CustomService.prototype.blockHandler = function(block, add, callback) {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
Take a look at the Address Service implementation for more details about how to encode the key, value for the best efficiency 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:
|
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:
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user