Fixed a problem with address balances.
This commit is contained in:
parent
678cb83d61
commit
f0360a78a0
@ -22,6 +22,9 @@ var AddressService = function(options) {
|
||||
if (this._network === 'livenet') {
|
||||
this._network = 'main';
|
||||
}
|
||||
if (this._network === 'regtest') {
|
||||
this._network = 'testnet';
|
||||
}
|
||||
};
|
||||
|
||||
inherits(AddressService, BaseService);
|
||||
@ -95,7 +98,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
|
||||
|
||||
var criteria = {
|
||||
gte: start,
|
||||
lt: end
|
||||
lte: end
|
||||
};
|
||||
|
||||
// txid stream
|
||||
@ -110,8 +113,9 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
|
||||
|
||||
txStream.on('end', function() {
|
||||
result.balance = Unit.fromSatoshis(result.balanceSat).toBTC();
|
||||
assert(result.balance >= 0, 'Balance can\'t be less than zero.');
|
||||
result.totalReceived = Unit.fromSatoshis(result.totalReceivedSat).toBTC();
|
||||
result.totalSent = result.totalSentSat;
|
||||
result.totalSent = Unit.fromSatoshis(result.totalSentSat).toBTC();
|
||||
result.unconfirmedBalance = result.unconfirmedBalanceSat;
|
||||
callback(null, result);
|
||||
});
|
||||
@ -121,6 +125,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
|
||||
|
||||
txStream._transform = function(chunk, enc, callback) {
|
||||
|
||||
// TODO: what if the address in tx more than once? such as an address sending from itself to itself
|
||||
var key = self._encoding.decodeAddressIndexKey(chunk);
|
||||
|
||||
self._tx.getTransaction(key.txid, options, function(err, tx) {
|
||||
@ -137,32 +142,22 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
|
||||
return;
|
||||
}
|
||||
|
||||
var confirmations = self._header.getBestHeight() - key.height;
|
||||
var confirmations = self._header.getBestHeight() - key.height + 1;
|
||||
|
||||
result.transactions.push(tx.txid());
|
||||
result.txApperances++;
|
||||
// is this an input?
|
||||
if (key.input) {
|
||||
|
||||
return self._transaction.getInputValues(key.txid, null, function(err, tx) {
|
||||
result.balanceSat -= tx.__inputValues[key.index];
|
||||
result.totalSentSat += tx.__inputValues[key.index];
|
||||
|
||||
if(err) {
|
||||
log.error(err);
|
||||
txStream.emit('error', err);
|
||||
return;
|
||||
}
|
||||
if (confirmations < 1) {
|
||||
result.unconfirmedBalanceSat -= tx.__inputValues[key.index];
|
||||
result.unconfirmedTxApperances++;
|
||||
}
|
||||
|
||||
result.balanceSat -= tx.__inputValues[key.index];
|
||||
result.totalSentSat += tx.__inputValues[key.index];
|
||||
|
||||
if (confirmations < 1) {
|
||||
result.unconfirmedBalanceSat -= tx.__inputValues[key.index];
|
||||
result.unconfirmedTxApperances++;
|
||||
}
|
||||
|
||||
callback();
|
||||
|
||||
});
|
||||
return callback();
|
||||
|
||||
}
|
||||
|
||||
@ -470,6 +465,7 @@ AddressService.prototype.onBlock = function(block, callback) {
|
||||
}
|
||||
|
||||
operations = _.flatten(operations);
|
||||
|
||||
callback(null, operations);
|
||||
};
|
||||
|
||||
@ -512,6 +508,7 @@ AddressService.prototype._processInput = function(tx, input, opts) {
|
||||
};
|
||||
|
||||
AddressService.prototype._processOutput = function(tx, output, index, opts) {
|
||||
|
||||
var address = output.getAddress();
|
||||
|
||||
if(!address) {
|
||||
@ -524,7 +521,7 @@ AddressService.prototype._processOutput = function(tx, output, index, opts) {
|
||||
var timestamp = this._timestamp.getTimestampSync(opts.block.rhash());
|
||||
assert(timestamp, 'Must have a timestamp in order to process output.');
|
||||
|
||||
var addressKey = this._encoding.encodeAddressIndexKey(address, opts.block.height, txid, opts.outputIndex, 0, timestamp);
|
||||
var addressKey = this._encoding.encodeAddressIndexKey(address, opts.block.height, txid, index, 0, timestamp);
|
||||
|
||||
var utxoKey = this._encoding.encodeUtxoIndexKey(address, txid, index);
|
||||
var utxoValue = this._encoding.encodeUtxoIndexValue(
|
||||
@ -568,7 +565,7 @@ AddressService.prototype._processTransaction = function(tx, opts) {
|
||||
|
||||
inputOperations = _.flatten(_.compact(inputOperations));
|
||||
|
||||
outputOperations.concat(inputOperations);
|
||||
outputOperations = outputOperations.concat(inputOperations);
|
||||
return outputOperations;
|
||||
|
||||
};
|
||||
|
||||
@ -235,6 +235,7 @@ HeaderService.prototype.getPublishEvents = function() {
|
||||
|
||||
};
|
||||
|
||||
// TODO: if blocks come in too rapidly, there can be an erroreous reorg situation
|
||||
HeaderService.prototype._onBlock = function(block) {
|
||||
|
||||
var self = this;
|
||||
@ -535,11 +536,13 @@ HeaderService.prototype._handleReorg = function(block, header, callback) {
|
||||
assert(self._lastHeader, 'Expected our reorg block to have a header entry, but it did not.');
|
||||
headers.set(hash, header); // appends to the end
|
||||
self.emit('reorg', hash, headers, block);
|
||||
return callback();
|
||||
}
|
||||
|
||||
assert(hash, 'To reorg, we need a hash to reorg to.');
|
||||
self.emit('reorg', hash, headers);
|
||||
callback();
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
373
test/regtest/address.js
Normal file
373
test/regtest/address.js
Normal file
@ -0,0 +1,373 @@
|
||||
'use strict';
|
||||
|
||||
var expect = require('chai').expect;
|
||||
var spawn = require('child_process').spawn;
|
||||
var path = require('path');
|
||||
var rimraf = require('rimraf');
|
||||
var mkdirp = require('mkdirp');
|
||||
var fs = require('fs');
|
||||
var async = require('async');
|
||||
var RPC = require('bitcoind-rpc');
|
||||
var http = require('http');
|
||||
|
||||
var rpc1Address;
|
||||
var rpc2Address;
|
||||
|
||||
var rpcConfig = {
|
||||
protocol: 'http',
|
||||
user: 'local',
|
||||
pass: 'localtest',
|
||||
host: '127.0.0.1',
|
||||
port: 58332,
|
||||
rejectUnauthorized: false
|
||||
};
|
||||
|
||||
var rpc1 = new RPC(rpcConfig);
|
||||
rpcConfig.port++;
|
||||
var rpc2 = new RPC(rpcConfig);
|
||||
var debug = true;
|
||||
var bitcoreDataDir = '/tmp/bitcore';
|
||||
var bitcoinDataDirs = ['/tmp/bitcoin1', '/tmp/bitcoin2'];
|
||||
|
||||
var bitcoin = {
|
||||
args: {
|
||||
datadir: null,
|
||||
listen: 1,
|
||||
regtest: 1,
|
||||
server: 1,
|
||||
rpcuser: 'local',
|
||||
rpcpassword: 'localtest',
|
||||
//printtoconsole: 1
|
||||
rpcport: 58332,
|
||||
},
|
||||
datadir: null,
|
||||
exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind
|
||||
processes: []
|
||||
};
|
||||
|
||||
var bitcore = {
|
||||
configFile: {
|
||||
file: bitcoreDataDir + '/bitcore-node.json',
|
||||
conf: {
|
||||
network: 'regtest',
|
||||
port: 53001,
|
||||
datadir: bitcoreDataDir,
|
||||
services: [
|
||||
'p2p',
|
||||
'db',
|
||||
'header',
|
||||
'block',
|
||||
'address',
|
||||
'transaction',
|
||||
'mempool',
|
||||
'web',
|
||||
'insight-api',
|
||||
'fee',
|
||||
'timestamp'
|
||||
],
|
||||
servicesConfig: {
|
||||
'p2p': {
|
||||
'peers': [
|
||||
{ 'ip': { 'v4': '127.0.0.1' }, port: 18444 }
|
||||
]
|
||||
},
|
||||
'insight-api': {
|
||||
'routePrefix': 'api'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
httpOpts: {
|
||||
protocol: 'http:',
|
||||
hostname: 'localhost',
|
||||
port: 53001,
|
||||
},
|
||||
opts: { cwd: bitcoreDataDir },
|
||||
datadir: bitcoreDataDir,
|
||||
exec: path.resolve(__dirname, '../../bin/bitcore-node'),
|
||||
args: ['start'],
|
||||
process: null
|
||||
};
|
||||
|
||||
var startBitcoind = function(count, callback) {
|
||||
|
||||
var listenCount = 0;
|
||||
console.log('starting ' + count + ' bitcoind\'s');
|
||||
async.timesSeries(count, function(n, next) {
|
||||
|
||||
var datadir = bitcoinDataDirs.shift();
|
||||
|
||||
bitcoin.datadir = datadir;
|
||||
bitcoin.args.datadir = datadir;
|
||||
|
||||
if (listenCount++ > 0) {
|
||||
bitcoin.args.listen = 0;
|
||||
bitcoin.args.rpcport++;
|
||||
bitcoin.args.connect = '127.0.0.1';
|
||||
}
|
||||
|
||||
rimraf(datadir, function(err) {
|
||||
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
mkdirp(datadir, function(err) {
|
||||
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var args = bitcoin.args;
|
||||
var argList = Object.keys(args).map(function(key) {
|
||||
return '-' + key + '=' + args[key];
|
||||
});
|
||||
|
||||
var bitcoinProcess = spawn(bitcoin.exec, argList, bitcoin.opts);
|
||||
bitcoin.processes.push(bitcoinProcess);
|
||||
|
||||
bitcoinProcess.stdout.on('data', function(data) {
|
||||
|
||||
if (debug) {
|
||||
process.stdout.write(data.toString());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
bitcoinProcess.stderr.on('data', function(data) {
|
||||
|
||||
if (debug) {
|
||||
process.stderr.write(data.toString());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
next();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}, function(err) {
|
||||
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var pids = bitcoin.processes.map(function(process) {
|
||||
return process.pid;
|
||||
});
|
||||
|
||||
console.log(count + ' bitcoind\'s started at pid(s): ' + pids);
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
var shutdownBitcoind = function(callback) {
|
||||
bitcoin.processes.forEach(function(process) {
|
||||
process.kill();
|
||||
});
|
||||
callback();
|
||||
};
|
||||
|
||||
var shutdownBitcore = function(callback) {
|
||||
if (bitcore.process) {
|
||||
bitcore.process.kill();
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
var txid;
|
||||
var buildInitialChain = function(callback) {
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
console.log('checking to see if bitcoind\'s are connected to each other.');
|
||||
rpc1.getinfo(function(err, res) {
|
||||
if (err || res.result.connections !== 1) {
|
||||
next(err || new Error('bitcoind\'s not connected to each other.'));
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
console.log('generating 101 blocks');
|
||||
rpc1.generate(101, next);
|
||||
},
|
||||
function(res, next) {
|
||||
console.log('getting new address from rpc2');
|
||||
rpc2.getNewAddress(function(err, res) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
rpc2Address = res.result;
|
||||
console.log(rpc2Address);
|
||||
next(null, rpc2Address);
|
||||
});
|
||||
},
|
||||
function(addr, next) {
|
||||
rpc1.sendToAddress(rpc2Address, 25, next);
|
||||
},
|
||||
function(res, next) {
|
||||
console.log('TXID: ' + res.result);
|
||||
console.log('generating 6 blocks');
|
||||
rpc1.generate(7, next);
|
||||
},
|
||||
function(res, next) {
|
||||
rpc2.getBalance(function(err, res) {
|
||||
console.log(res);
|
||||
next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
console.log('getting new address from rpc1');
|
||||
rpc1.getNewAddress(function(err, res) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
rpc1Address = res.result;
|
||||
next(null, rpc1Address);
|
||||
});
|
||||
},
|
||||
function(addr, next) {
|
||||
rpc2.sendToAddress(rpc1Address, 20, next);
|
||||
},
|
||||
function(res, next) {
|
||||
txid = res.result;
|
||||
console.log('sending from rpc2Address TXID: ', res);
|
||||
console.log('generating 6 blocks');
|
||||
rpc2.generate(6, next);
|
||||
}
|
||||
], function(err) {
|
||||
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
rpc1.getInfo(function(err, res) {
|
||||
console.log(res);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var startBitcore = function(callback) {
|
||||
|
||||
rimraf(bitcoreDataDir, function(err) {
|
||||
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
mkdirp(bitcoreDataDir, function(err) {
|
||||
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
fs.writeFileSync(bitcore.configFile.file, JSON.stringify(bitcore.configFile.conf));
|
||||
|
||||
var args = bitcore.args;
|
||||
bitcore.process = spawn(bitcore.exec, args, bitcore.opts);
|
||||
|
||||
bitcore.process.stdout.on('data', function(data) {
|
||||
|
||||
if (debug) {
|
||||
process.stdout.write(data.toString());
|
||||
}
|
||||
|
||||
});
|
||||
bitcore.process.stderr.on('data', function(data) {
|
||||
|
||||
if (debug) {
|
||||
process.stderr.write(data.toString());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
callback();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
describe('Address', function() {
|
||||
|
||||
this.timeout(60000);
|
||||
|
||||
before(function(done) {
|
||||
|
||||
async.series([
|
||||
function(next) {
|
||||
startBitcoind(2, next);
|
||||
},
|
||||
function(next) {
|
||||
setTimeout(function() {
|
||||
buildInitialChain(next);
|
||||
}, 8000);
|
||||
},
|
||||
function(next) {
|
||||
setTimeout(function() {
|
||||
startBitcore(next);
|
||||
}, 6000);
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
setTimeout(done, 2000);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
shutdownBitcoind(function() {
|
||||
shutdownBitcore(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should get address info correctly: /addr/:addr', function(done) {
|
||||
|
||||
|
||||
var request = http.request('http://localhost:53001/api/addr/' + rpc2Address, function(res) {
|
||||
|
||||
var error;
|
||||
if (res.statusCode !== 200 && res.statusCode !== 201) {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
return done('Error from bitcore-node webserver: ' + res.statusCode);
|
||||
}
|
||||
|
||||
var resError;
|
||||
var resData = '';
|
||||
|
||||
res.on('error', function(e) {
|
||||
resError = e;
|
||||
});
|
||||
|
||||
res.on('data', function(data) {
|
||||
resData += data;
|
||||
});
|
||||
|
||||
res.on('end', function() {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
var data = JSON.parse(resData);
|
||||
console.log(data);
|
||||
expect(data.balance).to.equal(0);
|
||||
expect(data.totalSent).to.equal(25);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
request.write('');
|
||||
request.end();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user