From 72602ba89f474900ccef8b00beab41e45e645809 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Thu, 18 May 2017 17:13:50 -0400 Subject: [PATCH] wip --- child-pay-for-parent.js | 83 ---------- .../block/{sync.js => block_handler.js} | 15 +- lib/services/block/index.js | 151 ++++++++++++------ regtest/block.js | 149 +---------------- 4 files changed, 114 insertions(+), 284 deletions(-) delete mode 100644 child-pay-for-parent.js rename lib/services/block/{sync.js => block_handler.js} (96%) diff --git a/child-pay-for-parent.js b/child-pay-for-parent.js deleted file mode 100644 index cc6cf676..00000000 --- a/child-pay-for-parent.js +++ /dev/null @@ -1,83 +0,0 @@ -var unmentionables = require('./keys.json'); -var assert = require('assert'); -var p2p = require('bitcore-p2p'); -var Peer = p2p.Peer; -var messages = new p2p.Messages(); - -var peer = new Peer({host: '68.101.164.118'}); - -// 1. prepare a low fee ts. -// 2. send low fee tx. The low fee tx should be enough to be relayed -// 3. 20 - 50 sats/byte is what we usually see -// 4. later, spend those outputs using an approproate fee - -var bitcore = require('bitcore-lib'); -var key1 = new bitcore.PrivateKey(unmentionables.key1); -var key2 = new bitcore.PrivateKey(unmentionables.key2); -var lowFeeRate = 40; //sat/byte -var highFeeRate = 260; - -var parentUtxo = { - txid: '100304043f19ea9c4faf0810c9432b806cf383de38d9138b004c8a8df7f76249', - outputIndex: 0, - address: '1DxgVtn7xUwX9Jwqx7YW7JfsDDDVHDxTwL', - script: '76a9148e295bd3b705aac6ba0cb02bb582f98c451b83ee88ac', - satoshis: 17532710 -}; - -var parentTx = new bitcore.Transaction(); - -var lowFee = lowFeeRate*193; -var highFee = highFeeRate*193; -var childToAddress = '12Awugz6fhM2BW4dH7Xx1ZKxy3CHWM6a8f'; - -parentTx.from(parentUtxo).to(childToAddress, (parentUtxo.satoshis - lowFee)).fee(lowFee).sign(key1); -console.log(parentTx.getFee()); -console.log(parentTx.verify()); -assert((parentTx.inputs[0].output.satoshis - parentTx.outputs[0].satoshis) === parentTx.getFee()); - -console.log(parentTx.toObject()); -console.log(parentTx.serialize()); - -peer.on('ready', function() { - console.log(peer.version, peer.subversion, peer.bestHeight); - setTimeout(function() { - peer.sendMessage(messages.Transaction(parentTx)); - setTimeout(function() { peer.disconnect(); }, 2000); - }, 2000); -}); - -peer.on('disconnect', function() { - console.log('connection closed'); -}); -peer.connect(); -//var childUtxo = { -// txid: parentTx.id, -// outputIndex: 0, -// address: childToAddress, -// script: parentTx.outputs[0].script.toHex(), -// satoshis: (parentUtxo.satoshis - lowFee) -//}; -// -//var childTx = new bitcore.Transaction(); -//childTx.from(childUtxo).to(childToAddress, (childUtxo.satoshis - highFee)).fee(highFee).sign(key2); -//console.log(childTx.getFee()); -//console.log(childTx.toObject()); - - - - - -//01000000 -//01 -//49 -//62f7f78d8a4c008b13d938de83f36c802b43c91008af4f9cea193f04040310000000006a47304402200c98dee6e5a2db276e24ac45c153aa5586455894efee060e95e0e7d017569df30220258b48ebd0253ca6b4b8b35d8f18b4bcb41040fb060f1ed59f7989185dd296170121031c8ed8aead402b7f6e02617d50b7a7ba3b07a489da0702f65f985bf0ebb64f3a -//ffffffff -//01 -//26 -//87 -//0b01000000001976a9140cd9b466eb74f45e3290b47fbbb622e458601267 -//88 -//ac -//00000000 - diff --git a/lib/services/block/sync.js b/lib/services/block/block_handler.js similarity index 96% rename from lib/services/block/sync.js rename to lib/services/block/block_handler.js index f4e79641..1ce37e96 100644 --- a/lib/services/block/sync.js +++ b/lib/services/block/block_handler.js @@ -62,19 +62,18 @@ function WriteStream(highWaterMark, sync) { inherits(WriteStream, Writable); -function Sync(node, block) { +function BlockHandler(node, block) { this.node = node; this.db = this.node.services.db; this.block = block; - console.log(block); this.syncing = false; this.paused = false; //we can't sync while one of our indexes is reading/writing separate from us this.highWaterMark = 10; } -inherits(Sync, EventEmitter); +inherits(BlockHandler, EventEmitter); -Sync.prototype.sync = function() { +BlockHandler.prototype.sync = function() { var self = this; if(this.syncing || this.paused) { @@ -104,7 +103,7 @@ Sync.prototype.sync = function() { }; -Sync.prototype._onFinish = function() { +BlockHandler.prototype._onFinish = function() { var self = this; self.syncing = false; @@ -122,7 +121,7 @@ Sync.prototype._onFinish = function() { }; -Sync.prototype._startSubscriptions = function() { +BlockHandler.prototype._startSubscriptions = function() { var self = this; @@ -140,7 +139,7 @@ Sync.prototype._startSubscriptions = function() { }; -Sync.prototype._handleErrors = function(stream) { +BlockHandler.prototype._handleErrors = function(stream) { var self = this; stream.on('error', function(err) { @@ -390,4 +389,4 @@ ProcessBoth.prototype._write = function(block, encoding, callback) { }); }; -module.exports = Sync; +module.exports = BlockHandler; diff --git a/lib/services/block/index.js b/lib/services/block/index.js index 23ab4ae9..bdfef894 100644 --- a/lib/services/block/index.js +++ b/lib/services/block/index.js @@ -1,5 +1,6 @@ 'use strict'; +var assert = require('assert'); var BaseService = require('../../service'); var levelup = require('levelup'); var bitcore = require('bitcore-lib'); @@ -13,14 +14,13 @@ var utils = require('../../utils'); var Reorg = require('./reorg'); var $ = bitcore.util.preconditions; var async = require('async'); -var Sync = require('./sync'); +var BlockHandler = require('./block_handler'); var BlockService = function(options) { BaseService.call(this, options); this.bitcoind = this.node.services.bitcoind; this.db = this.node.services.db; - this._sync = new Sync(this.node, this); - this.syncing = true; + this._blockHandler = new BlockHandler(this.node, this); this._lockTimes = []; }; @@ -31,11 +31,8 @@ BlockService.dependencies = [ 'db' ]; -BlockService.prototype._log = function(msg, fn) { - if (!fn) { - return log.info('BlockService: ', msg); - } - +BlockService.prototype._log = function(msg) { + return log.info('BlockService: ', msg); }; BlockService.prototype.start = function(callback) { @@ -55,22 +52,96 @@ BlockService.prototype.start = function(callback) { }); }; -BlockService.prototype._setHandlers = function() { - var self = this; - self.node.once('ready', function() { +BlockService.prototype._sync = function() { - self.genesis = Block.fromBuffer(self.bitcoind.genesisBuffer); - self._loadTips(function(err) { + var self = this; + self._loadTips(function(err) { + + if(err) { + throw err; + } + + self._log('Bitcoin network tip is currently: ' + self.bitcoind.tiphash + ' at height: ' + self.bitcoind.height); + + self._detectReorg(function(err, header) { if(err) { throw err; } - self._log('Bitcoin network tip is currently: ' + self.bitcoind.tiphash + ' at height: ' + self.bitcoind.height); - self._sync.sync(); + self._handleReorg(header, function + }); + var blocksDiff = self.bitcoind.height - self.tip.__height - 1; + + if (blocksDiff < 0) { + self._log('Peer\'s height is less than our own. The peer may be syncing. The system is usable, but chain may have reorg in future blocks from our peers. We may not answer queries about blocks at heights greater than ' + self.bitcoind.height); + self._blockHandler.sync(); + return; + } + + self._log('Syncing: ' + blocksDiff + ' blocks from the network.'); + + self._getBlocks(blocksDiff, function(err) { + + if(err) { + throw err; + } + self._blockHandler.sync(); + + }); + + }); + +}; + +BlockService.prototype._getBlocks = function(blocksDiff, callback) { + + var self = this; + var operations = []; + + async.timesLimit(blocksDiff, 8, function(n, next) { + + var blockNumber = n + self.tip.__height + 2; + self.bitcoind.getBlockHeader(blockNumber, function(err, header) { + + if(err) { + return next(err); + } + + operations.push({ + type: 'put', + key: self.encoding.encodeBlockHashKey(header.hash), + value: self.encoding.encodeBlockHashValue(header.height) + }); + + operations.push({ + type: 'put', + key: self.encoding.encodeBlockHeightKey(header.height), + value: self.encoding.encodeBlockHeightValue(header.hash) + }); + + next(); + }); + + }, function(err, headers) { + + if(err) { + return callback(err); + } + + self.db.batch(operations, callback); + + }); +}; + +BlockService.prototype._setHandlers = function() { + var self = this; + self.node.once('ready', function() { + self.genesis = Block.fromBuffer(self.bitcoind.genesisBuffer); + self._sync(); }); }; @@ -104,46 +175,32 @@ BlockService.prototype.resumeSync = function() { } }; -BlockService.prototype.detectReorg = function(blocks) { +BlockService.prototype._detectReorg = function(callback) { - var self = this; - - if (!blocks || blocks.length === 0) { - return; + if (this.tip.__height <= 0) { + return callback(); } - var tipHash = self.reorgTipHash || self.tip.hash; - var chainMembers = []; - - var loopIndex = 0; - var overallCounter = 0; - - while(overallCounter < blocks.length) { - - if (loopIndex >= blocks.length) { - overallCounter++; - loopIndex = 0; - } - - var prevHash = BufferUtil.reverse(blocks[loopIndex].header.prevHash).toString('hex'); - if (prevHash === tipHash) { - tipHash = blocks[loopIndex].hash; - chainMembers.push(blocks[loopIndex]); - } - loopIndex++; - + if (this.tip.hash === this.bitcoind.tiphash && this.tip.__height === this.bitcoind.height) { + return callback(); } - for(var i = 0; i < blocks.length; i++) { - if (chainMembers.indexOf(blocks[i]) === -1) { - return blocks[i]; - } - self.reorgTipHash = blocks[i].hash; - } + self.bitcoind.getBlockHeader(self.tip.__height, function(err, header) { + if(err) { + return callback(err); + } + + if (header.hash === self.tip.hash) { + return callback(); + } + + callback(null, header); + + }); }; -BlockService.prototype.handleReorg = function(forkBlock, callback) { +BlockService.prototype.handleReorg = function(header, callback) { var self = this; self.printTipInfo('Reorg detected!'); diff --git a/regtest/block.js b/regtest/block.js index ec9dbede..1b0e166d 100644 --- a/regtest/block.js +++ b/regtest/block.js @@ -85,24 +85,15 @@ var opts = { bitcoinDataDir: bitcoinDataDir, bitcoreDataDir: bitcoreDataDir, rpc: new BitcoinRPC(rpcConfig), - walletPassphrase: 'test', - txCount: 0, blockHeight: 0, - walletPrivKeys: [], - initialTxs: [], - fee: 100000, - feesReceived: 0, - satoshisSent: 0, - walletId: crypto.createHash('sha256').update('test').digest('hex'), - satoshisReceived: 0, initialHeight: 150 }; -describe('Wallet Operations', function() { +describe('Block Operations', function() { this.timeout(60000); - describe('Register, Upload, GetTransactions', function() { + describe('Sync Block Headers', function() { var self = this; @@ -115,149 +106,15 @@ describe('Wallet Operations', function() { async.series([ utils.startBitcoind.bind(utils, self.opts), utils.waitForBitcoinReady.bind(utils, self.opts), - utils.unlockWallet.bind(utils, self.opts), - utils.setupInitialTxs.bind(utils, self.opts), utils.startBitcoreNode.bind(utils, self.opts), utils.waitForBitcoreNode.bind(utils, self.opts) ], done); }); - it('should register wallet', function(done) { + it('should sync block headers', function(done) { - utils.registerWallet.call(utils, self.opts, function(err, res) { - - if (err) { - return done(err); - } - - res.should.deep.equal(JSON.stringify({ - walletId: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' - })); done(); - }); }); - - it('should upload a wallet', function(done) { - - utils.uploadWallet.call(utils, self.opts, done); - - }); - - it('should get a list of transactions', function(done) { - - //the wallet should be fully uploaded and indexed by the time this happens - utils.sendTxs.call(utils, self.opts, function(err) { - - if(err) { - return done(err); - } - utils.waitForBitcoreNode.call(utils, self.opts, function(err) { - - if(err) { - return done(err); - } - utils.getListOfTxs.call(utils, self.opts, done); - }); - }); - - }); - }); - describe('Load addresses after syncing the blockchain', function() { - - var self = this; - - self.opts = Object.assign({}, opts); - - after(utils.cleanup.bind(utils, self.opts)); - - before(function(done) { - async.series([ - utils.startBitcoind.bind(utils, self.opts), - utils.waitForBitcoinReady.bind(utils, self.opts), - utils.unlockWallet.bind(utils, self.opts), - utils.setupInitialTxs.bind(utils, self.opts), - utils.sendTxs.bind(utils, self.opts), - utils.startBitcoreNode.bind(utils, self.opts), - utils.waitForBitcoreNode.bind(utils, self.opts), - utils.registerWallet.bind(utils, self.opts), - utils.uploadWallet.bind(utils, self.opts) - ], done); - }); - - it('should get list of transactions', function(done) { - - utils.getListOfTxs.call(utils, self.opts, done); - - }); - - it('should get the balance of a wallet', function(done) { - - var httpOpts = utils.getHttpOpts.call( - utils, - self.opts, - { path: '/wallet-api/wallets/' + self.opts.walletId + '/balance' }); - - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - if(err) { - return done(err); - } - var results = JSON.parse(res); - results.satoshis.should.equal(self.opts.satoshisReceived); - done(); - }); - - }); - - it('should get the set of utxos for the wallet', function(done) { - - var httpOpts = utils.getHttpOpts.call( - utils, - self.opts, - { path: '/wallet-api/wallets/' + opts.walletId + '/utxos' }); - - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - - if(err) { - return done(err); - } - - var results = JSON.parse(res); - var balance = 0; - - results.utxos.forEach(function(utxo) { - balance += utxo.satoshis; - }); - - results.height.should.equal(self.opts.blockHeight); - balance.should.equal(self.opts.satoshisReceived); - done(); - }); - }); - - it('should get the list of jobs', function(done) { - var httpOpts = utils.getHttpOpts.call(utils, self.opts, { path: '/wallet-api/jobs' }); - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - if(err) { - return done(err); - } - var results = JSON.parse(res); - results.jobCount.should.equal(1); - done(); - }); - }); - - it('should remove all wallets', function(done) { - var httpOpts = utils.getHttpOpts.call(utils, self.opts, { path: '/wallet-api/wallets', method: 'DELETE' }); - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - if(err) { - return done(err); - } - var results = JSON.parse(res); - results.numberRemoved.should.equal(152); - done(); - }); - }); - }); });