diff --git a/README.md b/README.md index 2b898f91..4edabf57 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,177 @@ Read the docs here: http://bcoin.io/docs/ ## Example Usage +### Creating a blockchain and mempool + +``` js +var bcoin = require('bcoin').set('regtest'); +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" +miner.open(function(err) { + if (err) + throw err; + + miner.createBlock(function(err, attempt) { + if (err) + throw err; + + // Mine the block on the worker pool (use mine() for the master process) + attempt.mineAsync(function(err, block) { + if (err) + throw err; + + // Add the block to the chain + chain.add(block, function(err) { + if (err) + throw err; + + console.log('Added %s to the blockchain.', block.rhash); + console.log(block); + }); + }); + }); +}); +``` + +### Connecting to the P2P network + +``` js +var bcoin = require('bcoin').set('main'); + +var chain = new bcoin.chain({ db: 'leveldb' }); +var mempool = new bcoin.mempool({ chain: chain, db: 'memory' }); +var pool = new bcoin.pool({ chain: chain, mempool: mempool, size: 8 }); + +pool.open(function(err) { + if (err) + throw err; + + // Connect, start retrieving and relaying txs + pool.connect(); + + // Start the blockchain sync. + pool.startSync(); + + chain.on('block', function(block) { + console.log('Added block:'); + console.log(block); + }); + + mempool.on('tx', function(tx) { + console.log('Added tx to mempool:'); + console.log(tx); + }); + + pool.on('tx', function(tx) { + console.log('Saw transaction:'); + console.log(tx); + }); +}); + +// Start up a segnet4 sync while +// we're at it (because we can). + +var tchain = new bcoin.chain({ + network: 'segnet4', + db: 'memory' +}); + +var tmempool = new bcoin.mempool({ + network: 'segnet4', + chain: tchain, + db: 'memory' +}); + +var tpool = new bcoin.pool({ + network: 'segnet4', + chain: tchain, + mempool: tmempool, + size: 8 +}); + +tpool.open(function(err) { + if (err) + throw err; + + // Connect, start retrieving and relaying txs + tpool.connect(); + + // Start the blockchain sync. + tpool.startSync(); + + tchain.on('block', function(block) { + console.log('Added segnet4 block:'); + console.log(block); + }); + + tmempool.on('tx', function(tx) { + console.log('Added segnet4 tx to mempool:'); + console.log(tx); + }); + + tpool.on('tx', function(tx) { + console.log('Saw segnet4 transaction:'); + console.log(tx); + }); +}); + +``` + +### Doing an SPV sync + +``` js +var bcoin = require('bcoin').set('testnet'); + +var chain = new bcoin.chain({ + db: 'leveldb', + // A custom chaindb location: + location: process.env.HOME + '/chain.db', + spv: true +}); + +var pool = new bcoin.pool({ + chain: chain, + spv: true, + size: 8 +}); + +var walletdb = new bcoin.walletdb({ db: 'memory' }); + +pool.open(function(err) { + if (err) + throw err; + + var wallet = new bcoin.wallet({ + provider: walletdb.provider(), + derivation: 'bip44', + type: 'pubkeyhash' + }); + + walletdb.save(wallet); + pool.watchWallet(wallet); + + console.log('Created wallet with address %s', wallet.getAddress()); + + // Connect, start retrieving and relaying txs + pool.connect(); + + // Start the blockchain sync. + pool.startSync(); + + pool.on('tx', function(tx) { + wallet.addTX(tx); + }); + + wallet.on('balance', function(balance) { + console.log('Balance updated.'); + console.log(bcoin.utils.btc(balance.unconfirmed)); + }); +}); +``` + ### High-level usage for Node object ``` js @@ -52,10 +223,11 @@ node.open(function(err) { // Create a new wallet (or get an existing one with the same ID) var options = { id: 'mywallet', + passphrase: 'foo', witness: false, type: 'pubkeyhash', derivation: 'bip44', - passphrase: 'foo' + accountIndex: 0 }; node.createWallet(options, function(err, wallet) { @@ -72,6 +244,7 @@ node.open(function(err) { wallet.once('balance', function(balance) { var newReceiving = wallet.createAddress(); console.log('Created new receiving address: %s', newReceiving); + // Create a transaction, fill // it with coins, and sign it. wallet.createTX({ @@ -133,115 +306,6 @@ $ node bin/bcoin-cli wallet primary --passphrase=node $ node bin/bcoin-cli mempool ``` -### Creating a blockchain and mempool - -``` js -var bcoin = require('bcoin').set('regtest'); -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" -miner.createBlock(function(err, attempt) { - if (err) - throw err; - - // Mine the block on the worker pool (use mine() for the master process) - attempt.mineAsync(function(err, block) { - if (err) - throw err; - - // Add the block to the chain - chain.add(block, function(err) { - if (err) - throw err; - - console.log('Added %s to the blockchain.', block.rhash); - console.log(block); - }); - }); -}); -``` - -### Connecting to the P2P network - -``` js -var bcoin = require('bcoin').set('testnet'); - -var chain = new bcoin.chain({ db: 'leveldb' }); -var mempool = new bcoin.mempool({ chain: chain, db: 'memory' }); -var pool = new bcoin.pool({ chain: chain, mempool: mempool, size: 8 }); - -// Connect, start retrieving and relaying txs -pool.connect(); - -// Start the blockchain sync. -pool.startSync(); - -chain.on('block', function(block) { - console.log('Added block:'); - console.log(block); -}); - -mempool.on('tx', function(tx) { - console.log('Added tx to mempool:'); - console.log(tx); -}); - -pool.on('tx', function(tx) { - console.log('Saw transaction:'); - console.log(tx); -}); -``` - -### Doing an SPV sync - -``` js -var bcoin = require('bcoin').set('testnet'); - -var chain = new bcoin.chain({ - db: 'leveldb', - location: process.env.HOME + '/chain.db', - spv: true -}); - -var pool = new bcoin.pool({ - chain: chain, - spv: true, - size: 8 -}); - -var walletdb = new bcoin.walletdb({ db: 'memory' }); - -var wallet = new bcoin.wallet({ - provider: walletdb.provider(), - derivation: 'bip44', - type: 'pubkeyhash' -}); - -walletdb.save(wallet); -pool.watchWallet(wallet); - -console.log('Created wallet with address %s', wallet.getAddress()); - -// Connect, start retrieving and relaying txs -pool.connect(); - -// Start the blockchain sync. -pool.startSync(); - -pool.on('tx', function(tx) { - wallet.addTX(tx); -}); - -wallet.on('balance', function(balance) { - console.log('Balance updated.'); - console.log(bcoin.utils.btc(balance.unconfirmed)); -}); -``` - - - ### TX creation TODO diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index efec861e..e3927a49 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -1359,29 +1359,27 @@ Pool.prototype.removeWallet = function removeWallet(wallet) { if (!this.options.spv) return; - assert(this.loaded); + assert(this.loaded, 'Pool is not loaded.'); this.unwatchWallet(wallet); }; /** * Add an address to the bloom filter (SPV-only). - * @param {Base58Address} address + * @param {Address|Base58Address} address */ Pool.prototype.watchAddress = function watchAddress(address) { - var hash = bcoin.address.getHash(address); - this.watch(hash); + this.watch(bcoin.address.getHash(address)); }; /** * Remove an address from the bloom filter (SPV-only). - * @param {Base58Address} address + * @param {Address|Base58Address} address */ Pool.prototype.unwatchAddress = function unwatchAddress(address) { - var hash = bcoin.address.getHash(address); - this.unwatch(hash); + this.unwatch(bcoin.address.getHash(address)); }; /** @@ -1416,7 +1414,7 @@ Pool.prototype.unwatchWallet = function unwatchWallet(wallet) { Pool.prototype.searchWallet = function(wallet, callback) { var self = this; - assert(this.loaded); + assert(this.loaded, 'Pool is not loaded.'); callback = utils.asyncify(callback); @@ -1473,75 +1471,6 @@ Pool.prototype.searchWallet = function(wallet, callback) { }); }; -/** - * Search for a specific piece of data within a time range (SPV-only). - * @param {Buffer|Hash} id - * @param {Object} range - Range in the format - * of `{start:Number, end:Number}`. - * @param {Function} callback - */ - -Pool.prototype.search = function search(id, range, callback) { - var self = this; - - assert(this.loaded); - - if (!this.options.spv) - return; - - if (range == null) { - range = id; - id = null; - } - - if (typeof id === 'string') - id = new Buffer(id, 'hex'); - - if (typeof range === 'number') - range = { start: range, end: null }; - else if (range) - range = { start: range.start, end: range.end }; - else - range = { start: 0, end: 0 }; - - if (!range.end) - range.end = utils.now(); - - if (!range.start) - range.start = utils.now() - 432000; - - if (id) - this.watch(id); - - callback = utils.asyncify(callback); - - function done(err, completed) { - self.removeListener('block', onBlock); - if (id) - self.unwatch(id); - callback(err, completed); - } - - function onBlock(block) { - if (block.ts >= range.end) - done(null, true); - } - - this.on('block', onBlock); - - if (range.start < this.chain.tip.ts) { - this.chain.resetTime(range.start, function(err) { - if (err) - return done(err); - - self.stopSync(); - self.startSync(); - }); - } else { - done(null, false); - } -}; - /** * Queue a `getdata` request to be sent. Checks existence * in the chain before requesting. @@ -1710,23 +1639,6 @@ Pool.prototype.fulfill = function fulfill(hash) { return item; }; -/** - * Send `getdata` for a block, wait for response. - * @param {Hash} - * @param {Function} callback - Returns [Error, {@link Block}]. - */ - -Pool.prototype.getBlock = function getBlock(hash, callback) { - if (!this.peers.load) - return setTimeout(this.getBlock.bind(this, hash, callback), 1000); - - this.getData(this.peers.load, this.block.type, hash, true, function(block) { - callback(null, block); - }); - - this.scheduleRequests(this.peers.load); -}; - /** * Broadcast a block. * @param {Block} block @@ -1738,84 +1650,6 @@ Pool.prototype.sendBlock = function sendBlock(block, callback) { return this.broadcast(block, callback); }; -/** - * Search for a transaction, wait for response (SPV-only). - * @param {Hash} - * @param {Object} range - Time range in the form - * of `{start:Number, end:Number}`. - * @param {Function} callback - Returns [Error, {@link Block}]. - */ - -Pool.prototype.getTX = function getTX(hash, range, callback) { - var self = this; - var cbs, tx, found, delta; - - if (!this.peers.load) - return setTimeout(this.getTX.bind(this, hash, range, callback), 1000); - - if (!this.options.spv) - return callback(new Error('Cannot get tx with full node')); - - if (typeof range === 'function') { - callback = range; - range = null; - } - - // Do not perform duplicate searches - if (this.validate.map[hash]) - return this.validate.map[hash].push(callback); - - cbs = [callback]; - this.validate.map[hash] = cbs; - - // Add request without queueing it to get notification at the time of load - tx = null; - found = false; - this.getData(this.peers.load, self.tx.type, hash, { noQueue: true }, function(t) { - found = true; - tx = t; - }); - - // Do incremental search until the TX is found - delta = this.validate.delta; - - // Start from the existing range if given - if (range) - range = { start: range.start, end: range.end }; - else - range = { start: utils.now() - delta, end: 0 }; - - function done(err, tx, range) { - var i; - - delete self.validate.map[hash]; - - for (i = 0; i < cbs.length; i++) - cbs[i](err, tx, range); - } - - (function next() { - self.search(hash, range, function(err, completed) { - if (err) - return done(err); - - if (found) - return done(null, tx, range); - - if (!completed) - return done(); - - // Not found yet, continue scanning - range.end = range.start; - range.start -= delta; - if (range.start < 0) - range.start = 0; - - next(); - }); - })(); -}; - /** * Broadcast a transaction. * @param {TX} tx