commit
3e1e94de62
@ -35,8 +35,8 @@ module.exports = function(grunt) {
|
|||||||
livereload: true,
|
livereload: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
js2: {
|
assets: {
|
||||||
files: ['public/src/**/*.js'],
|
files: ['public/src/**/*.js', 'public/**/*.css'],
|
||||||
tasks: ['compile'],
|
tasks: ['compile'],
|
||||||
options: {
|
options: {
|
||||||
livereload: true,
|
livereload: true,
|
||||||
|
|||||||
@ -104,24 +104,20 @@ exports.list = function(req, res) {
|
|||||||
bdb.getBlocksByDate(gte, lte, function(err, blocks) {
|
bdb.getBlocksByDate(gte, lte, function(err, blocks) {
|
||||||
if (err) {
|
if (err) {
|
||||||
res.status(500).send(err);
|
res.status(500).send(err);
|
||||||
} else {
|
}
|
||||||
var blockshashList = [];
|
else {
|
||||||
var limit = parseInt(req.query.limit || blocks.length);
|
var l = blocks.length;
|
||||||
if (blocks.length < limit) {
|
var limit = parseInt(req.query.limit || l);
|
||||||
limit = blocks.length;
|
if (l < limit) limit = l;
|
||||||
}
|
|
||||||
for (var i = 0; i < limit; i++) {
|
async.mapSeries(blocks,
|
||||||
blockshashList.push(blocks[i].hash);
|
function(b, cb) {
|
||||||
}
|
getBlock(b.hash, function(err, info) {
|
||||||
async.mapSeries(blockshashList,
|
return cb(err,{
|
||||||
function(hash, cb) {
|
|
||||||
getBlock(hash, function(err, info) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
return cb(err, {
|
|
||||||
height: info.height,
|
height: info.height,
|
||||||
size: info.size,
|
size: info.size,
|
||||||
hash: info.hash,
|
hash: b.hash,
|
||||||
time: info.time,
|
time: b.ts || info.time,
|
||||||
txlength: info.tx.length,
|
txlength: info.tx.length,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -44,5 +44,8 @@ module.exports.broadcastAddressTx = function(address, tx) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports.broadcastSyncInfo = function(historicSync) {
|
module.exports.broadcastSyncInfo = function(historicSync) {
|
||||||
if (ios) ios.sockets.in('sync').emit('status', historicSync);
|
|
||||||
|
if (ios) {
|
||||||
|
ios.sockets.in('sync').emit('status', historicSync);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,7 +13,11 @@ function spec() {
|
|||||||
this.balanceSat = 0;
|
this.balanceSat = 0;
|
||||||
this.totalReceivedSat = 0;
|
this.totalReceivedSat = 0;
|
||||||
this.totalSentSat = 0;
|
this.totalSentSat = 0;
|
||||||
this.txApperances = 0;
|
|
||||||
|
this.unconfirmedBalanceSat = 0;
|
||||||
|
|
||||||
|
this.txApperances = 0;
|
||||||
|
this.unconfirmedTxApperances= 0;
|
||||||
|
|
||||||
// TODO store only txids? +index? +all?
|
// TODO store only txids? +index? +all?
|
||||||
this.transactions = [];
|
this.transactions = [];
|
||||||
@ -51,12 +55,25 @@ function spec() {
|
|||||||
},
|
},
|
||||||
enumerable: 1,
|
enumerable: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'unconfirmedBalance', {
|
||||||
|
get: function() {
|
||||||
|
return parseFloat(this.unconfirmedBalanceSat) / parseFloat(BitcoreUtil.COIN);
|
||||||
|
},
|
||||||
|
set: function(i) {
|
||||||
|
this.unconfirmedBalanceSat = i * BitcoreUtil.COIN;
|
||||||
|
},
|
||||||
|
enumerable: 1,
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Address.prototype.update = function(next) {
|
Address.prototype.update = function(next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!self.addrStr) return next();
|
if (!self.addrStr) return next();
|
||||||
|
|
||||||
|
var txs = [];
|
||||||
var db = new TransactionDb();
|
var db = new TransactionDb();
|
||||||
async.series([
|
async.series([
|
||||||
function (cb) {
|
function (cb) {
|
||||||
@ -64,27 +81,52 @@ function spec() {
|
|||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
txOut.forEach(function(txItem){
|
txOut.forEach(function(txItem){
|
||||||
|
|
||||||
|
var v = txItem.value_sat;
|
||||||
|
|
||||||
|
txs.push({txid: txItem.txid, ts: txItem.ts});
|
||||||
|
|
||||||
|
if (txItem.spendTxId) {
|
||||||
|
txs.push({txid: txItem.spendTxId, ts: txItem.spendTs});
|
||||||
|
}
|
||||||
|
|
||||||
if (txItem.isConfirmed) {
|
if (txItem.isConfirmed) {
|
||||||
var v = txItem.value_sat;
|
self.txApperances += 1;
|
||||||
self.totalReceivedSat += v;
|
self.totalReceivedSat += v;
|
||||||
self.transactions.push(txItem.txid);
|
if (! txItem.spendTxId ) {
|
||||||
if (! txItem.spendTxId || !txItem.spendIsConfirmed) {
|
//unspend
|
||||||
|
self.balanceSat += v;
|
||||||
|
}
|
||||||
|
else if(!txItem.spendIsConfirmed) {
|
||||||
// unspent
|
// unspent
|
||||||
self.balanceSat += v;
|
self.balanceSat += v;
|
||||||
self.txApperances +=1;
|
self.unconfirmedBalanceSat -= v;
|
||||||
|
self.unconfirmedTxApperances += 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// spent
|
// spent
|
||||||
self.totalSentSat += v;
|
self.totalSentSat += v;
|
||||||
self.transactions.push(txItem.spendTxId);
|
self.txApperances += 1;
|
||||||
self.txApperances +=2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
self.unconfirmedBalanceSat += v;
|
||||||
|
self.unconfirmedTxApperances += 1;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return cb();
|
return cb();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
], function (err) {
|
], function (err) {
|
||||||
|
|
||||||
|
// sort input and outputs togheter
|
||||||
|
txs.sort(
|
||||||
|
function compare(a,b) {
|
||||||
|
if (a.ts < b.ts) return 1;
|
||||||
|
if (a.ts > b.ts) return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
self.transactions = txs.map(function(i) { return i.txid; } );
|
||||||
return next(err);
|
return next(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,7 +17,7 @@ hash = 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df34dfa443e9a41160';
|
|||||||
|
|
||||||
var rpc = new RpcClient(config.bitcoind);
|
var rpc = new RpcClient(config.bitcoind);
|
||||||
|
|
||||||
rpc.getRawTransaction( hash, 1, function(err, ret) {
|
rpc.getBlockCount( function(err, ret) {
|
||||||
|
|
||||||
console.log('Err:');
|
console.log('Err:');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|||||||
@ -5,11 +5,11 @@ require('classtool');
|
|||||||
|
|
||||||
function spec(b) {
|
function spec(b) {
|
||||||
|
|
||||||
var TIMESTAMP_PREFIX = 'b-ts-'; // b-ts-<ts> => <hash>
|
var TIMESTAMP_PREFIX = 'bts-'; // b-ts-<ts> => <hash>
|
||||||
var PREV_PREFIX = 'b-prev-'; // b-prev-<hash> => <prev_hash>
|
var PREV_PREFIX = 'bpr-'; // b-prev-<hash> => <prev_hash>
|
||||||
var NEXT_PREFIX = 'b-next-'; // b-next-<hash> => <next_hash>
|
var NEXT_PREFIX = 'bne-'; // b-next-<hash> => <next_hash>
|
||||||
var MAIN_PREFIX = 'b-main-'; // b-main-<hash> => 1/0
|
var MAIN_PREFIX = 'bma-'; // b-main-<hash> => 1/0
|
||||||
var TIP = 'b-tip-'; // last block on the chain
|
var TIP = 'bti-'; // last block on the chain
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,14 +125,11 @@ function spec(b) {
|
|||||||
BlockDb.prototype.has = function(hash, cb) {
|
BlockDb.prototype.has = function(hash, cb) {
|
||||||
var k = PREV_PREFIX + hash;
|
var k = PREV_PREFIX + hash;
|
||||||
db.get(k, function (err,val) {
|
db.get(k, function (err,val) {
|
||||||
var ret;
|
var ret = true;
|
||||||
if (err && err.notFound) {
|
if (err && err.notFound) {
|
||||||
err = null;
|
err = null;
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
if (typeof val !== 'undefined') {
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
return cb(err, ret);
|
return cb(err, ret);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -170,8 +167,9 @@ function spec(b) {
|
|||||||
fillCache: true
|
fillCache: true
|
||||||
})
|
})
|
||||||
.on('data', function (data) {
|
.on('data', function (data) {
|
||||||
|
var k = data.key.split('-');
|
||||||
list.push({
|
list.push({
|
||||||
ts: data.key.replace(TIMESTAMP_PREFIX, ''),
|
ts: k[1],
|
||||||
hash: data.value,
|
hash: data.value,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@ -7,8 +7,6 @@ require('classtool');
|
|||||||
function spec() {
|
function spec() {
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var RpcClient = require('bitcore/RpcClient').class();
|
var RpcClient = require('bitcore/RpcClient').class();
|
||||||
var bitutil = require('bitcore/util/util');
|
|
||||||
var Address = require('bitcore/Address').class();
|
|
||||||
var Script = require('bitcore/Script').class();
|
var Script = require('bitcore/Script').class();
|
||||||
var networks = require('bitcore/networks');
|
var networks = require('bitcore/networks');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
@ -35,7 +33,6 @@ function spec() {
|
|||||||
|
|
||||||
this.syncPercentage = 0;
|
this.syncPercentage = 0;
|
||||||
this.syncedBlocks = 0;
|
this.syncedBlocks = 0;
|
||||||
this.skippedBlocks = 0;
|
|
||||||
this.orphanBlocks = 0;
|
this.orphanBlocks = 0;
|
||||||
this.type ='';
|
this.type ='';
|
||||||
}
|
}
|
||||||
@ -95,7 +92,6 @@ function spec() {
|
|||||||
status: this.status,
|
status: this.status,
|
||||||
blockChainHeight: this.blockChainHeight,
|
blockChainHeight: this.blockChainHeight,
|
||||||
syncPercentage: this.syncPercentage,
|
syncPercentage: this.syncPercentage,
|
||||||
skippedBlocks: this.skippedBlocks,
|
|
||||||
syncedBlocks: this.syncedBlocks,
|
syncedBlocks: this.syncedBlocks,
|
||||||
orphanBlocks: this.orphanBlocks,
|
orphanBlocks: this.orphanBlocks,
|
||||||
syncTipHash: this.sync.tip,
|
syncTipHash: this.sync.tip,
|
||||||
@ -105,7 +101,7 @@ function spec() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HistoricSync.prototype.updatePercentage = function() {
|
HistoricSync.prototype.updatePercentage = function() {
|
||||||
var r = (this.syncedBlocks + this.skippedBlocks) / this.blockChainHeight;
|
var r = this.syncedBlocks / this.blockChainHeight;
|
||||||
this.syncPercentage = parseFloat(100 * r).toFixed(3);
|
this.syncPercentage = parseFloat(100 * r).toFixed(3);
|
||||||
if (this.syncPercentage > 100) this.syncPercentage = 100;
|
if (this.syncPercentage > 100) this.syncPercentage = 100;
|
||||||
};
|
};
|
||||||
@ -113,13 +109,15 @@ function spec() {
|
|||||||
HistoricSync.prototype.showProgress = function() {
|
HistoricSync.prototype.showProgress = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if ( ( self.syncedBlocks + self.skippedBlocks) % self.step !== 1) return;
|
if ( self.status ==='syncing' &&
|
||||||
|
( self.syncedBlocks ) % self.step !== 1) return;
|
||||||
|
|
||||||
if (self.error) {
|
if (self.error) {
|
||||||
p('ERROR: ' + self.error);
|
p('ERROR: ' + self.error);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p(util.format('status: [%d%%] skipped: %d ', self.syncPercentage, self.skippedBlocks));
|
self.updatePercentage();
|
||||||
|
p(util.format('status: [%d%%]', self.syncPercentage));
|
||||||
}
|
}
|
||||||
if (self.opts.shouldBroadcastSync) {
|
if (self.opts.shouldBroadcastSync) {
|
||||||
sockets.broadcastSyncInfo(self.info());
|
sockets.broadcastSyncInfo(self.info());
|
||||||
@ -194,22 +192,21 @@ function spec() {
|
|||||||
self.status = 'syncing';
|
self.status = 'syncing';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (scanOpts.upToExisting && existed &&
|
if ( blockEnd && blockEnd === blockHash) {
|
||||||
self.syncedBlocks >= self.blockChainHeight) ||
|
p('blockEnd found!:' + blockEnd);
|
||||||
(blockEnd && blockEnd === blockHash)) {
|
self.found=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( self.found && self.syncedBlocks >= self.blockChainHeight ) {
|
||||||
self.status = 'finished';
|
self.status = 'finished';
|
||||||
p('DONE. Found block: ', blockHash);
|
p('DONE. Height: ' , self.syncedBlocks);
|
||||||
self.showProgress();
|
|
||||||
return cb(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue
|
// Continue
|
||||||
if (blockInfo) {
|
if (blockInfo) {
|
||||||
|
|
||||||
if (existed)
|
self.syncedBlocks++;
|
||||||
self.skippedBlocks++;
|
|
||||||
else
|
|
||||||
self.syncedBlocks++;
|
|
||||||
|
|
||||||
// recursion
|
// recursion
|
||||||
if (scanOpts.prev && blockInfo.previousblockhash)
|
if (scanOpts.prev && blockInfo.previousblockhash)
|
||||||
@ -223,42 +220,6 @@ function spec() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// TODO. replace with
|
|
||||||
// Script.prototype.getAddrStrs if that one get merged in bitcore
|
|
||||||
HistoricSync.prototype.getAddrStr = function(s) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var addrStrs = [];
|
|
||||||
var type = s.classify();
|
|
||||||
var addr;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case Script.TX_PUBKEY:
|
|
||||||
var chunk = s.captureOne();
|
|
||||||
addr = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk));
|
|
||||||
addrStrs = [ addr.toString() ];
|
|
||||||
break;
|
|
||||||
case Script.TX_PUBKEYHASH:
|
|
||||||
addr = new Address(self.network.addressPubkey, s.captureOne());
|
|
||||||
addrStrs = [ addr.toString() ];
|
|
||||||
break;
|
|
||||||
case Script.TX_SCRIPTHASH:
|
|
||||||
addr = new Address(self.network.addressScript, s.captureOne());
|
|
||||||
addrStrs = [ addr.toString() ];
|
|
||||||
break;
|
|
||||||
case Script.TX_MULTISIG:
|
|
||||||
var chunks = s.capture();
|
|
||||||
chunks.forEach(function(chunk) {
|
|
||||||
var a = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk));
|
|
||||||
addrStrs.push(a.toString());
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case Script.TX_UNKNOWN:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return addrStrs;
|
|
||||||
};
|
|
||||||
|
|
||||||
HistoricSync.prototype.getBlockFromFile = function(cb) {
|
HistoricSync.prototype.getBlockFromFile = function(cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -289,7 +250,7 @@ function spec() {
|
|||||||
|
|
||||||
|
|
||||||
var s = new Script(o.s);
|
var s = new Script(o.s);
|
||||||
var addrs = self.getAddrStr(s);
|
var addrs = self.sync.getAddrStr(s);
|
||||||
|
|
||||||
// support only for p2pubkey p2pubkeyhash and p2sh
|
// support only for p2pubkey p2pubkeyhash and p2sh
|
||||||
if (addrs.length === 1) {
|
if (addrs.length === 1) {
|
||||||
@ -308,7 +269,6 @@ function spec() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.showProgress();
|
self.showProgress();
|
||||||
|
|
||||||
self.getBlockFromFile(function(err, blockInfo) {
|
self.getBlockFromFile(function(err, blockInfo) {
|
||||||
if (err) {
|
if (err) {
|
||||||
self.setError(util.format('ERROR: @%s: %s [count: syncedBlocks: %d]',
|
self.setError(util.format('ERROR: @%s: %s [count: syncedBlocks: %d]',
|
||||||
@ -363,6 +323,7 @@ function spec() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var lastBlock;
|
var lastBlock;
|
||||||
|
var tip;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
function(cb) {
|
function(cb) {
|
||||||
@ -376,17 +337,25 @@ function spec() {
|
|||||||
function (cb) { return self.getBlockCount(cb); },
|
function (cb) { return self.getBlockCount(cb); },
|
||||||
function(cb) {
|
function(cb) {
|
||||||
if (!scanOpts.reverse) return cb();
|
if (!scanOpts.reverse) return cb();
|
||||||
|
|
||||||
self.rpc.getBlockHash(self.blockChainHeight, function(err, res) {
|
self.rpc.getBlockHash(self.blockChainHeight, function(err, res) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
lastBlock = res.result;
|
lastBlock = res.result;
|
||||||
|
|
||||||
return cb();
|
return cb();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(cb) {
|
function(cb) {
|
||||||
if (scanOpts.upToExisting) {
|
if (!scanOpts.reverse) return cb();
|
||||||
// should be isOrphan = true or null to be more accurate.
|
self.sync.bDb.getTip(function(err, res) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
tip = res;
|
||||||
|
|
||||||
|
console.log('Old Tip:', tip);
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
function(cb) {
|
||||||
|
if (scanOpts.reverse) {
|
||||||
self.countNotOrphan(function(err, count) {
|
self.countNotOrphan(function(err, count) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
@ -421,7 +390,7 @@ function spec() {
|
|||||||
|
|
||||||
if (scanOpts.reverse) {
|
if (scanOpts.reverse) {
|
||||||
start = lastBlock;
|
start = lastBlock;
|
||||||
end = self.genesis;
|
end = tip || self.genesis;
|
||||||
scanOpts.prev = true;
|
scanOpts.prev = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -448,12 +417,14 @@ function spec() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
|
self.showProgress();
|
||||||
return next(err);
|
return next(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.type = 'from RPC calls';
|
self.type = 'from RPC calls';
|
||||||
self.getPrevNextBlock(start, end, scanOpts, function(err) {
|
self.getPrevNextBlock(start, end, scanOpts, function(err) {
|
||||||
|
self.showProgress();
|
||||||
return next(err);
|
return next(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -490,10 +461,9 @@ function spec() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p('Genesis block found. Syncing upto known blocks.');
|
p('Genesis block found. Syncing upto old TIP.');
|
||||||
p('Got ' + count + ' out of ' + self.blockChainHeight + ' blocks');
|
p('Got ' + count + ' out of ' + self.blockChainHeight + ' blocks');
|
||||||
scanOpts.reverse = true;
|
scanOpts.reverse = true;
|
||||||
scanOpts.upToExisting = true;
|
|
||||||
}
|
}
|
||||||
return self.importHistory(scanOpts, next);
|
return self.importHistory(scanOpts, next);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,6 +6,7 @@ function spec() {
|
|||||||
var CoinConst = require('bitcore/const');
|
var CoinConst = require('bitcore/const');
|
||||||
var coinUtil = require('bitcore/util/util');
|
var coinUtil = require('bitcore/util/util');
|
||||||
var Sync = require('./Sync').class();
|
var Sync = require('./Sync').class();
|
||||||
|
var Script = require('bitcore/Script').class();
|
||||||
var Peer = require('bitcore/Peer').class();
|
var Peer = require('bitcore/Peer').class();
|
||||||
var config = require('../config/config');
|
var config = require('../config/config');
|
||||||
var networks = require('bitcore/networks');
|
var networks = require('bitcore/networks');
|
||||||
@ -57,8 +58,23 @@ function spec() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PeerSync.prototype.handleTx = function(info) {
|
PeerSync.prototype.handleTx = function(info) {
|
||||||
|
var self =this;
|
||||||
var tx = info.message.tx.getStandardizedObject();
|
var tx = info.message.tx.getStandardizedObject();
|
||||||
console.log('[p2p_sync] Handle tx: ' + tx.hash);
|
console.log('[p2p_sync] Handle tx: ' + tx.hash);
|
||||||
|
tx.time = tx.time || Math.round(new Date().getTime() / 1000);
|
||||||
|
|
||||||
|
var to=0;
|
||||||
|
info.message.tx.outs.forEach( function(o) {
|
||||||
|
var s = new Script(o.s);
|
||||||
|
var addrs = self.sync.getAddrStr(s);
|
||||||
|
|
||||||
|
// support only for p2pubkey p2pubkeyhash and p2sh
|
||||||
|
if (addrs.length === 1) {
|
||||||
|
tx.out[to].addrStr = addrs[0];
|
||||||
|
tx.out[to].n = to;
|
||||||
|
}
|
||||||
|
to++;
|
||||||
|
});
|
||||||
|
|
||||||
this.sync.storeTxs([tx], function(err) {
|
this.sync.storeTxs([tx], function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
44
lib/Sync.js
44
lib/Sync.js
@ -6,7 +6,12 @@ require('classtool');
|
|||||||
function spec() {
|
function spec() {
|
||||||
var sockets = require('../app/controllers/socket.js');
|
var sockets = require('../app/controllers/socket.js');
|
||||||
var BlockDb = require('./BlockDb').class();
|
var BlockDb = require('./BlockDb').class();
|
||||||
|
var bitutil = require('bitcore/util/util');
|
||||||
|
var Address = require('bitcore/Address').class();
|
||||||
var TransactionDb = require('./TransactionDb').class();
|
var TransactionDb = require('./TransactionDb').class();
|
||||||
|
var config = require('../config/config');
|
||||||
|
var networks = require('bitcore/networks');
|
||||||
|
var Script = require('bitcore/Script').class();
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
|
|
||||||
@ -18,6 +23,7 @@ function spec() {
|
|||||||
self.opts = opts;
|
self.opts = opts;
|
||||||
this.bDb = new BlockDb(opts);
|
this.bDb = new BlockDb(opts);
|
||||||
this.txDb = new TransactionDb(opts);
|
this.txDb = new TransactionDb(opts);
|
||||||
|
this.network = config.network === 'testnet' ? networks.testnet: networks.livenet;
|
||||||
return cb();
|
return cb();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -276,6 +282,44 @@ function spec() {
|
|||||||
return cb(err);
|
return cb(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// TODO. replace with
|
||||||
|
// Script.prototype.getAddrStrs if that one get merged in bitcore
|
||||||
|
Sync.prototype.getAddrStr = function(s) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var addrStrs = [];
|
||||||
|
var type = s.classify();
|
||||||
|
var addr;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case Script.TX_PUBKEY:
|
||||||
|
var chunk = s.captureOne();
|
||||||
|
addr = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk));
|
||||||
|
addrStrs = [ addr.toString() ];
|
||||||
|
break;
|
||||||
|
case Script.TX_PUBKEYHASH:
|
||||||
|
addr = new Address(self.network.addressPubkey, s.captureOne());
|
||||||
|
addrStrs = [ addr.toString() ];
|
||||||
|
break;
|
||||||
|
case Script.TX_SCRIPTHASH:
|
||||||
|
addr = new Address(self.network.addressScript, s.captureOne());
|
||||||
|
addrStrs = [ addr.toString() ];
|
||||||
|
break;
|
||||||
|
case Script.TX_MULTISIG:
|
||||||
|
var chunks = s.capture();
|
||||||
|
chunks.forEach(function(chunk) {
|
||||||
|
var a = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk));
|
||||||
|
addrStrs.push(a.toString());
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Script.TX_UNKNOWN:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addrStrs;
|
||||||
|
};
|
||||||
return Sync;
|
return Sync;
|
||||||
}
|
}
|
||||||
module.defineClass(spec);
|
module.defineClass(spec);
|
||||||
|
|||||||
@ -6,17 +6,17 @@ require('classtool');
|
|||||||
function spec(b) {
|
function spec(b) {
|
||||||
|
|
||||||
// blockHash -> txid mapping
|
// blockHash -> txid mapping
|
||||||
var IN_BLK_PREFIX = 'tx-b-'; //tx-b-<txid>-<block> => 1/0 (connected or not)
|
var IN_BLK_PREFIX = 'txb-'; //txb-<txid>-<block> => 1/0 (connected or not)
|
||||||
|
|
||||||
// Only for orphan blocks
|
// Only for orphan blocks
|
||||||
var FROM_BLK_PREFIX = 'tx-'; //tx-<block>-<txid> => 1
|
var FROM_BLK_PREFIX = 'tx-'; //tx-<block>-<txid> => 1
|
||||||
|
|
||||||
// to show tx outs
|
// to show tx outs
|
||||||
var OUTS_PREFIX = 'txouts-'; //txouts-<txid>-<n> => [addr, btc_sat]
|
var OUTS_PREFIX = 'txo-'; //txo-<txid>-<n> => [addr, btc_sat]
|
||||||
|
var SPEND_PREFIX = 'txs-'; //txs-<txid(out)>-<n(out)>-<txid(in)>-<n(in)> = ts
|
||||||
|
|
||||||
// to sum up addr balance
|
// to sum up addr balance (only outs, spends are gotten later)
|
||||||
var ADDR_PREFIX = 'txouts-addr-'; //txouts-addr-<addr>-<ts>-<txid>-<n> => + btc_sat
|
var ADDR_PREFIX = 'txa-'; //txa-<addr>-<txid>-<n> => + btc_sat:ts
|
||||||
var SPEND_PREFIX = 'txouts-spend-';//txouts-spend-<txid(out)>-<n(out)>-<txid(in)>-<n(in)> = ts
|
|
||||||
|
|
||||||
// TODO: use bitcore networks module
|
// TODO: use bitcore networks module
|
||||||
var genesisTXID = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b';
|
var genesisTXID = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b';
|
||||||
@ -69,7 +69,7 @@ function spec(b) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDb.prototype._addSpendInfo = function(r, txid, index) {
|
TransactionDb.prototype._addSpendInfo = function(r, txid, index, ts) {
|
||||||
if (r.spendTxId) {
|
if (r.spendTxId) {
|
||||||
if (!r.multipleSpendAttempts) {
|
if (!r.multipleSpendAttempts) {
|
||||||
r.multipleSpendAttempts = [{
|
r.multipleSpendAttempts = [{
|
||||||
@ -85,6 +85,7 @@ function spec(b) {
|
|||||||
else {
|
else {
|
||||||
r.spendTxId = txid;
|
r.spendTxId = txid;
|
||||||
r.spendIndex = parseInt(index);
|
r.spendIndex = parseInt(index);
|
||||||
|
r.spendTs = parseInt(ts);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,12 +119,12 @@ function spec(b) {
|
|||||||
db.createReadStream({start: k, end: k + '~'})
|
db.createReadStream({start: k, end: k + '~'})
|
||||||
.on('data', function (data) {
|
.on('data', function (data) {
|
||||||
var k = data.key.split('-');
|
var k = data.key.split('-');
|
||||||
var j = idx[parseInt(k[3])];
|
var j = idx[parseInt(k[2])];
|
||||||
|
|
||||||
assert(typeof j !== 'undefined','Spent could not be stored: tx ' + txid +
|
assert(typeof j !== 'undefined','Spent could not be stored: tx ' + txid +
|
||||||
'spend in TX:' + k[2] + ',' + k[3]+ ' j:' + j);
|
'spend in TX:' + k[1] + ',' + k[2]+ ' j:' + j);
|
||||||
|
|
||||||
self._addSpendInfo(ret[j], k[4], k[5]);
|
self._addSpendInfo(ret[j], k[3], k[4], data.value);
|
||||||
})
|
})
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
@ -144,7 +145,7 @@ function spec(b) {
|
|||||||
db.createReadStream({start: k, end: k + '~'})
|
db.createReadStream({start: k, end: k + '~'})
|
||||||
.on('data', function (data) {
|
.on('data', function (data) {
|
||||||
var k = data.key.split('-');
|
var k = data.key.split('-');
|
||||||
self._addSpendInfo(info.vout[k[3]], k[4], k[5]);
|
self._addSpendInfo(info.vout[k[2]], k[3], k[4], data.value);
|
||||||
})
|
})
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
@ -173,6 +174,7 @@ function spec(b) {
|
|||||||
return c_in(); // error not scalated
|
return c_in(); // error not scalated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.firstSeenTs = ret.spendTs;
|
||||||
i.unconfirmedInput = i.unconfirmedInput;
|
i.unconfirmedInput = i.unconfirmedInput;
|
||||||
i.addr = ret.addr;
|
i.addr = ret.addr;
|
||||||
i.valueSat = ret.valueSat;
|
i.valueSat = ret.valueSat;
|
||||||
@ -264,7 +266,7 @@ function spec(b) {
|
|||||||
db.createReadStream({start: k, end: k + '~'})
|
db.createReadStream({start: k, end: k + '~'})
|
||||||
.on('data', function (data) {
|
.on('data', function (data) {
|
||||||
var k = data.key.split('-');
|
var k = data.key.split('-');
|
||||||
self._addSpendInfo(ret, k[4], k[5]);
|
self._addSpendInfo(ret, k[3], k[4], data.value);
|
||||||
})
|
})
|
||||||
.on('error', function (error) {
|
.on('error', function (error) {
|
||||||
return cb(error);
|
return cb(error);
|
||||||
@ -320,10 +322,10 @@ function spec(b) {
|
|||||||
var k = data.key.split('-');
|
var k = data.key.split('-');
|
||||||
var v = data.value.split(':');
|
var v = data.value.split(':');
|
||||||
ret.push({
|
ret.push({
|
||||||
|
txid: k[2],
|
||||||
|
index: parseInt(k[3]),
|
||||||
value_sat: parseInt(v[0]),
|
value_sat: parseInt(v[0]),
|
||||||
ts: parseInt(k[3]),
|
ts: parseInt(v[1]),
|
||||||
txid: k[4],
|
|
||||||
index: parseInt(k[5]),
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
@ -336,7 +338,7 @@ function spec(b) {
|
|||||||
db.createReadStream({start: k, end: k + '~'})
|
db.createReadStream({start: k, end: k + '~'})
|
||||||
.on('data', function (data) {
|
.on('data', function (data) {
|
||||||
var k = data.key.split('-');
|
var k = data.key.split('-');
|
||||||
self._addSpendInfo(o, k[4], k[5]);
|
self._addSpendInfo(o, k[3], k[4], data.value);
|
||||||
})
|
})
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
return e_c(err);
|
return e_c(err);
|
||||||
@ -438,17 +440,12 @@ function spec(b) {
|
|||||||
async.forEachLimit(tx.vin, CONCURRENCY,
|
async.forEachLimit(tx.vin, CONCURRENCY,
|
||||||
function(i, next_out) {
|
function(i, next_out) {
|
||||||
db.batch()
|
db.batch()
|
||||||
.put( SPEND_PREFIX + i.txid + '-' + i.vout + '-' + tx.txid + '-' + i.n, ts || 0)
|
.put( SPEND_PREFIX + i.txid + '-' + i.vout + '-' + tx.txid + '-' + i.n,
|
||||||
|
ts || 0)
|
||||||
.write(next_out);
|
.write(next_out);
|
||||||
},
|
},
|
||||||
function (err) {
|
function (err) {
|
||||||
if (err) {
|
return p_c(err);
|
||||||
if (!err.message.match(/E11000/)) {
|
|
||||||
console.log('ERR at TX %s: %s', tx.txid, err);
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p_c();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Parse Outputs
|
// Parse Outputs
|
||||||
@ -467,12 +464,20 @@ function spec(b) {
|
|||||||
|
|
||||||
var addr = o.scriptPubKey.addresses[0];
|
var addr = o.scriptPubKey.addresses[0];
|
||||||
var sat = Math.round(o.value * util.COIN);
|
var sat = Math.round(o.value * util.COIN);
|
||||||
db.batch()
|
|
||||||
.put( OUTS_PREFIX + tx.txid + '-' + o.n, addr + ':' + sat)
|
|
||||||
.put( ADDR_PREFIX + addr + '-' + ts + '-' + tx.txid +
|
|
||||||
'-' + o.n, sat)
|
|
||||||
.write(next_out);
|
|
||||||
|
|
||||||
|
// existed?
|
||||||
|
var k = OUTS_PREFIX + tx.txid + '-' + o.n;
|
||||||
|
db.get(k, function(err) {
|
||||||
|
if (err && err.notFound) {
|
||||||
|
db.batch()
|
||||||
|
.put( k, addr + ':' + sat)
|
||||||
|
.put( ADDR_PREFIX + addr + '-' + tx.txid + '-' + o.n, sat+':'+ts)
|
||||||
|
.write(next_out);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return next_out();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//console.log ('WARN in TX: %s could not parse OUTPUT %d', tx.txid, o.n);
|
//console.log ('WARN in TX: %s could not parse OUTPUT %d', tx.txid, o.n);
|
||||||
|
|||||||
@ -578,3 +578,24 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
|||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes rotateThis {
|
||||||
|
from { transform: scale( 1 ) rotate( 0deg ); }
|
||||||
|
to { transform: scale( 1 ) rotate( 360deg ); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes rotateThis {
|
||||||
|
from { -webkit-transform: scale( 1 ) rotate( 0deg ); }
|
||||||
|
to { -webkit-transform: scale( 1 ) rotate( 360deg ); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-rotate {
|
||||||
|
animation-name: rotateThis;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
-webkit-animation-name: rotateThis;
|
||||||
|
-webkit-animation-duration: 2s;
|
||||||
|
-webkit-animation-iteration-count: infinite;
|
||||||
|
-webkit-animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,10 @@ ScopedSocket.prototype.emit = function(event, data, callback) {
|
|||||||
|
|
||||||
angular.module('insight.socket').factory('getSocket',
|
angular.module('insight.socket').factory('getSocket',
|
||||||
function($rootScope) {
|
function($rootScope) {
|
||||||
var socket = io.connect();
|
var socket = io.connect(null, {
|
||||||
|
'reconnect': true,
|
||||||
|
'reconnection delay': 500,
|
||||||
|
});
|
||||||
return function(scope) {
|
return function(scope) {
|
||||||
var scopedSocket = new ScopedSocket(socket, $rootScope);
|
var scopedSocket = new ScopedSocket(socket, $rootScope);
|
||||||
scope.$on('$routeChangeStart', function() {
|
scope.$on('$routeChangeStart', function() {
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="m20v">
|
<div class="m20v">
|
||||||
<h4>Summary</h4>
|
<h4>Summary</h4>
|
||||||
|
<h5>Confirmed</h5>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@ -30,8 +31,25 @@
|
|||||||
<td class="small">No. Transactions</td>
|
<td class="small">No. Transactions</td>
|
||||||
<td class="address ellipsis text-right">{{address.txApperances}}</td>
|
<td class="address ellipsis text-right">{{address.txApperances}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div data-ng-show="address.unconfirmedTxApperances">
|
||||||
|
<h5>Unconfirmed</h5>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="small">Unconfirmed Txs Balance</td>
|
||||||
|
<td class="address ellipsis text-right">{{$root.currency.getConvertion(address.unconfirmedBalance)}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="small">No. Transactions</td>
|
||||||
|
<td class="address ellipsis text-right">{{address.unconfirmedTxApperances}}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div> <!-- END OF TRANSACTIONS TABLE -->
|
</div> <!-- END OF TRANSACTIONS TABLE -->
|
||||||
<div data-ng-include src="'/views/includes/currency.html'"></div>
|
<div data-ng-include src="'/views/includes/currency.html'"></div>
|
||||||
</div> <!-- END OF COL-MD-3 -->
|
</div> <!-- END OF COL-MD-3 -->
|
||||||
|
|||||||
@ -24,11 +24,18 @@
|
|||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li>
|
<li>
|
||||||
<div class="status" data-ng-controller="StatusController">
|
<div class="status" data-ng-controller="StatusController">
|
||||||
<a href="#" data-ng-init="getSync()">
|
<div data-ng-init="getSync()" class="pull-left">
|
||||||
<span class="t text-danger" data-ng-show="sync.error" tooltip="{{sync.error}}" tooltip-placement="bottom"> ERROR </span>
|
<span class="t text-danger" data-ng-show="sync.error" tooltip="{{sync.error}}" tooltip-placement="bottom">
|
||||||
<span class="t text-warning " tooltip="{{sync.syncedBlocks}} / {{sync.blockChainHeight}} synced. {{sync.skippedBlocks}} skipped" tooltip-placement="bottom" data-ng-show="sync.status==='syncing'"> {{sync.status}} {{sync.syncPercentage}}%</span>
|
<span class="glyphicon glyphicon-warning-sign"></span>
|
||||||
<span class="t text-default" tooltip="Historic sync finished" tooltip-placement="bottom" data-ng-show="sync.status==='finished'"> On sync</span>
|
ERROR
|
||||||
</a> ·
|
</span>
|
||||||
|
<span class="t" tooltip="{{sync.syncedBlocks}} / {{sync.blockChainHeight}} synced. {{sync.skippedBlocks}} skipped" tooltip-placement="bottom" data-ng-show="sync.status==='syncing'">
|
||||||
|
<span class="glyphicon glyphicon-refresh icon-rotate"></span>
|
||||||
|
{{sync.status}} {{sync.syncPercentage}}%
|
||||||
|
</span>
|
||||||
|
<span class="t text-default" tooltip="Historic sync finished" tooltip-placement="bottom" data-ng-show="sync.status==='finished'"> On sync </span>
|
||||||
|
</div>
|
||||||
|
·
|
||||||
<span data-ng-init="getStatus('Info')">
|
<span data-ng-init="getStatus('Info')">
|
||||||
<strong>Conn</strong> {{info.connections}}
|
<strong>Conn</strong> {{info.connections}}
|
||||||
</span> ·
|
</span> ·
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
and websocket APIs that can be used for writing web wallets and other apps
|
and websocket APIs that can be used for writing web wallets and other apps
|
||||||
that need more advanced blockchain queries than provided by bitcoind RPC.
|
that need more advanced blockchain queries than provided by bitcoind RPC.
|
||||||
Check out the <a href="http://github.com/bitpay/insight" target="_blank">source code</a>.</p>
|
Check out the <a href="http://github.com/bitpay/insight" target="_blank">source code</a>.</p>
|
||||||
<p>Insight is still in development, so be sure to report any bugs and provide feedback for improvement at our <a href="https://github.com/bitpay/insight/issues">github issue tracker</a>.</p>
|
<p>Insight is still in development, so be sure to report any bugs and provide feedback for improvement at our <a href="https://github.com/bitpay/insight/issues" target="_blank">github issue tracker</a>.</p>
|
||||||
<div id="powered" class="row">
|
<div id="powered" class="row">
|
||||||
<div class="powered-text">
|
<div class="powered-text">
|
||||||
<small class="text-muted">Powered by</small>
|
<small class="text-muted">Powered by</small>
|
||||||
|
|||||||
@ -42,8 +42,31 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<h2>Last Block</h2>
|
||||||
|
<table class="table" style="table-layout: fixed" data-ng-controller="StatusController" data-ng-init="getStatus('LastBlockHash')">
|
||||||
|
<thead data-ng-include src="'/views/includes/infoStatus.html'"> </thead>
|
||||||
|
<tr>
|
||||||
|
<td>Last Block Hash (Bitcoind)</td>
|
||||||
|
<td class="text-right ellipsis"><a href="/block/{{lastblockhash}}">{{lastblockhash}}</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Current Blockchain Tip(Insight)</td>
|
||||||
|
<td class="text-right ellipsis"><a href="/block/{{syncTipHash}}">{{syncTipHash}}</a></td>
|
||||||
|
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<h2>Transaction Output Set Information</h2>
|
<h2>Transaction Output Set Information</h2>
|
||||||
<table class="table" style="table-layout: fixed" data-ng-controller="StatusController" data-ng-init="getStatus('TxOutSetInfo')">
|
|
||||||
|
<div data-ng-controller="StatusController">
|
||||||
|
<button data-ng-click="txoutLoading=1;getStatus('TxOutSetInfo')" class="btn btn-default" data-ng-show="!txoutsetinfo.height">
|
||||||
|
Show Transaction Output data
|
||||||
|
<span data-ng-show="txoutLoading" class="glyphicon glyphicon-refresh icon-rotate"></span>
|
||||||
|
</button >
|
||||||
|
|
||||||
|
<table class="table" data-ng-show="txoutsetinfo.height" style="table-layout: fixed" >
|
||||||
<thead data-ng-include src="'/views/includes/infoStatus.html'"> </thead>
|
<thead data-ng-include src="'/views/includes/infoStatus.html'"> </thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@ -76,23 +99,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Last Block</h2>
|
|
||||||
<table class="table" style="table-layout: fixed" data-ng-controller="StatusController" data-ng-init="getStatus('LastBlockHash')">
|
|
||||||
<thead data-ng-include src="'/views/includes/infoStatus.html'"> </thead>
|
|
||||||
<tr>
|
|
||||||
<td>Last Block Hash (Bitcoind)</td>
|
|
||||||
<td class="text-right ellipsis"><a href="/block/{{lastblockhash}}">{{lastblockhash}}</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Current Blockchain Tip(Insight)</td>
|
|
||||||
<td class="text-right ellipsis"><a href="/block/{{syncTipHash}}">{{syncTipHash}}</a></td>
|
|
||||||
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div> <!-- END OF COL-8 -->
|
</div> <!-- END OF COL-8 -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="col-xs-12 col-md-4 col-gray">
|
<div class="col-xs-12 col-md-4 col-gray">
|
||||||
<h2>Bitcoin node information</h2>
|
<h2>Bitcoin node information</h2>
|
||||||
<table class="table" data-ng-controller="StatusController" data-ng-init="getStatus('Info')">
|
<table class="table" data-ng-controller="StatusController" data-ng-init="getStatus('Info')">
|
||||||
@ -118,11 +130,7 @@
|
|||||||
<td>Connections to other nodes</td>
|
<td>Connections to other nodes</td>
|
||||||
<td class="text-right">{{info.connections}}</td>
|
<td class="text-right">{{info.connections}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Proxy setting</td>
|
|
||||||
<td class="text-right">{{info.proxy}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Mining Difficulty</td>
|
<td>Mining Difficulty</td>
|
||||||
<td class="text-right">{{info.difficulty}}</td>
|
<td class="text-right">{{info.difficulty}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -131,16 +139,8 @@
|
|||||||
<td class="text-right">{{info.testnet}}</td>
|
<td class="text-right">{{info.testnet}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Keypool Oldest Date</td>
|
<td>Proxy setting</td>
|
||||||
<td class="text-right">{{info.keypoololdest*1000 | date:'medium' }}</td>
|
<td class="text-right">{{info.proxy}}</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Keypool Size</td>
|
|
||||||
<td class="text-right">{{info.keypoolsize}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Default Transaction Fee (BTC)</td>
|
|
||||||
<td class="text-right">{{info.paytxfee}}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Info Errors</td>
|
<td>Info Errors</td>
|
||||||
|
|||||||
@ -21,8 +21,15 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Received Time </strong></td>
|
<td><strong>Received Time </strong></td>
|
||||||
<td class="text-muted text-right">{{tx.time * 1000|date:'medium'}}</td>
|
<td data-ng-show="tx.firstSeenTs" class="text-muted text-right">{{tx.firstSeenTs * 1000|date:'medium'}}</td>
|
||||||
|
<td data-ng-show="!tx.firstSeenTs" class="text-muted text-right">N/A</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Mined Time </strong></td>
|
||||||
|
<td data-ng-show="tx.time" class="text-muted text-right">{{tx.time * 1000|date:'medium'}}</td>
|
||||||
|
<td data-ng-show="!tx.time" class="text-muted text-right">N/A</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div data-ng-include src="'/views/includes/currency.html'"></div>
|
<div data-ng-include src="'/views/includes/currency.html'"></div>
|
||||||
|
|||||||
@ -6,7 +6,13 @@
|
|||||||
<a class="txid" href="/tx/{{tx.txid}}">{{tx.txid}}</a>
|
<a class="txid" href="/tx/{{tx.txid}}">{{tx.txid}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6 text-right" data-ng-show="tx.time">
|
|
||||||
|
<div class="col-xs-12 col-md-6 text-right" data-ng-show="tx.firstSeenTs">
|
||||||
|
first seen at
|
||||||
|
<time>{{tx.firstSeenTs * 1000 | date:'medium'}}</time>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-6 text-right" data-ng-show="tx.time && !tx.firstSeenTs">
|
||||||
|
mined at
|
||||||
<time>{{tx.time * 1000 | date:'medium'}}</time>
|
<time>{{tx.time * 1000 | date:'medium'}}</time>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -30,6 +30,8 @@ describe('Address balances', function() {
|
|||||||
a.update(function(err) {
|
a.update(function(err) {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
assert.equal(v.addr, a.addrStr);
|
assert.equal(v.addr, a.addrStr);
|
||||||
|
assert.equal(a.unconfirmedTxApperances ,0, 'unconfirmedTxApperances: 0');
|
||||||
|
assert.equal(a.unconfirmedBalanceSat ,0, 'unconfirmedBalanceSat: 0');
|
||||||
if (v.txApperances)
|
if (v.txApperances)
|
||||||
assert.equal(v.txApperances, a.txApperances, 'txApperances: ' + a.txApperances);
|
assert.equal(v.txApperances, a.txApperances, 'txApperances: ' + a.txApperances);
|
||||||
if (v.totalReceived) assert.equal(v.totalReceived, a.totalReceived, 'received: ' + a.totalReceived);
|
if (v.totalReceived) assert.equal(v.totalReceived, a.totalReceived, 'received: ' + a.totalReceived);
|
||||||
|
|||||||
@ -43,10 +43,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
||||||
"txApperances": 2041,
|
|
||||||
"balance": 1199.74393853,
|
"balance": 1199.74393853,
|
||||||
"totalReceived": 1199.74393853,
|
"totalReceived": 1199.74393853,
|
||||||
"totalSent": 0
|
"totalSent": 0,
|
||||||
|
"txApperances": 5763
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H",
|
"addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H",
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
{
|
{
|
||||||
"txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237",
|
"txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237",
|
||||||
"toRm": [
|
"toRm": [
|
||||||
"txouts-spend-86a03cac7d87f596008c6d5a8d3fd8b88842932ea6f0337673eda16f6b472f7f-0",
|
"txs-86a03cac7d87f596008c6d5a8d3fd8b88842932ea6f0337673eda16f6b472f7f-0",
|
||||||
"txouts-spend-bcd8da8ee847da377f8aaca92502c05e5f914c6a2452753146013b0e642a25a0-0"
|
"txs-bcd8da8ee847da377f8aaca92502c05e5f914c6a2452753146013b0e642a25a0-0"
|
||||||
],
|
],
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
@ -25,7 +25,7 @@
|
|||||||
{
|
{
|
||||||
"txid": "b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee",
|
"txid": "b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee",
|
||||||
"toRm": [
|
"toRm": [
|
||||||
"txouts-spend-01621403689cb4a95699a3dbae029d7031c5667678ef14e2054793954fb27917-0"
|
"txs-01621403689cb4a95699a3dbae029d7031c5667678ef14e2054793954fb27917-0"
|
||||||
],
|
],
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
@ -43,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"txid": "ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b",
|
"txid": "ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b",
|
||||||
"toRm": [
|
"toRm": [
|
||||||
"txouts-spend-2d7b680fb06e4d7eeb65ca49ac7522276586e0090b7fe662fc708129429c5e6a-0"
|
"txs-2d7b680fb06e4d7eeb65ca49ac7522276586e0090b7fe662fc708129429c5e6a-0"
|
||||||
],
|
],
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -13,11 +13,11 @@ var async = require('async');
|
|||||||
program
|
program
|
||||||
.version(SYNC_VERSION)
|
.version(SYNC_VERSION)
|
||||||
.option('-N --network [livenet]', 'Set bitcoin network [testnet]', 'testnet')
|
.option('-N --network [livenet]', 'Set bitcoin network [testnet]', 'testnet')
|
||||||
.option('-S --smart', 'genesis stored? uptoexisting = 1', 1)
|
|
||||||
.option('-D --destroy', 'Remove current DB (and start from there)', 0)
|
.option('-D --destroy', 'Remove current DB (and start from there)', 0)
|
||||||
.option('-R --reverse', 'Sync backwards', 0)
|
.option('-R --reverse', 'Sync backwards', 0)
|
||||||
.option('-U --uptoexisting', 'Sync only until an existing block is found', 0)
|
.option('-U --uptoexisting', 'Sync only until old Tip block is found', 0)
|
||||||
.option('-F --fromfiles', 'Sync using bitcoind .dat block files (faster)', 0)
|
.option('-F --fromfiles', 'Sync using bitcoind .dat block files (faster)', 0)
|
||||||
|
.option('-S --smart', 'genesis stored? uptoexisting = 1, fromFiles=1 [default]', true)
|
||||||
.option('-v --verbose', 'Verbose 0/1', 0)
|
.option('-v --verbose', 'Verbose 0/1', 0)
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ async.series([
|
|||||||
},
|
},
|
||||||
function(cb) {
|
function(cb) {
|
||||||
|
|
||||||
if (program.smart) {
|
if (typeof program.smart === 'undefined' || parseInt(program.smart) ) {
|
||||||
historicSync.smartImport({
|
historicSync.smartImport({
|
||||||
destroy: program.destroy,
|
destroy: program.destroy,
|
||||||
},cb);
|
},cb);
|
||||||
@ -43,7 +43,6 @@ async.series([
|
|||||||
historicSync.importHistory({
|
historicSync.importHistory({
|
||||||
destroy: program.destroy,
|
destroy: program.destroy,
|
||||||
reverse: program.reverse,
|
reverse: program.reverse,
|
||||||
upToExisting: program.uptoexisting,
|
|
||||||
fromFiles: program.fromfiles,
|
fromFiles: program.fromfiles,
|
||||||
}, cb);
|
}, cb);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user