fix conflicts
This commit is contained in:
commit
e16fb98c91
|
@ -9,8 +9,8 @@ function spec() {
|
||||||
var BitcoreUtil = require('bitcore/util/util');
|
var BitcoreUtil = require('bitcore/util/util');
|
||||||
var TransactionDb = require('../../lib/TransactionDb').class();
|
var TransactionDb = require('../../lib/TransactionDb').class();
|
||||||
|
|
||||||
function Address(addrStr) {
|
function Address(addrStr, txDb) {
|
||||||
this.tdb = new TransactionDb();
|
this.txDb = txDb || new TransactionDb();
|
||||||
this.balanceSat = 0;
|
this.balanceSat = 0;
|
||||||
this.totalReceivedSat = 0;
|
this.totalReceivedSat = 0;
|
||||||
this.totalSentSat = 0;
|
this.totalSentSat = 0;
|
||||||
|
@ -58,35 +58,24 @@ function spec() {
|
||||||
Address.prototype.update = function(next) {
|
Address.prototype.update = function(next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
async.series([
|
async.series([
|
||||||
/* function (cb) {
|
|
||||||
TransactionIn.find({addr:self.addrStr}).exec(function(err,txIn){
|
|
||||||
if (err) return cb(err);
|
|
||||||
|
|
||||||
txIn.forEach(function(txItem){
|
|
||||||
|
|
||||||
self.balanceSat += txItem.value_sat;
|
|
||||||
self.totalReceivedSat += txItem.value_sat;
|
|
||||||
});
|
|
||||||
return cb();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
function (cb) {
|
function (cb) {
|
||||||
self.tdb.fromAddr(self.addrStr, function(err,txOut){
|
self.txDb.fromAddr(self.addrStr, function(err,txOut){
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
txOut.forEach(function(txItem){
|
txOut.forEach(function(txItem){
|
||||||
|
|
||||||
self.totalReceivedSat += txItem.value_sat;
|
var v = parseInt(txItem.value_sat);
|
||||||
|
|
||||||
|
self.totalReceivedSat += v;
|
||||||
self.transactions.push(txItem.txid);
|
self.transactions.push(txItem.txid);
|
||||||
if (! txItem.spendTxId) {
|
if (! txItem.spendTxId) {
|
||||||
// unspent
|
// unspent
|
||||||
self.balanceSat += txItem.value_sat;
|
self.balanceSat += v;
|
||||||
self.txApperances +=1;
|
self.txApperances +=1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// spent
|
// spent
|
||||||
self.totalSentSat += txItem.value_sat;
|
self.totalSentSat += v;
|
||||||
self.transactions.push(txItem.spendTxid);
|
self.transactions.push(txItem.spendTxid);
|
||||||
self.txApperances +=2;
|
self.txApperances +=2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
require('classtool');
|
|
||||||
|
|
||||||
|
|
||||||
function spec() {
|
|
||||||
|
|
||||||
var util = require('bitcore/util/util'),
|
|
||||||
TransactionRpc = require('../../lib/TransactionRpc').class(),
|
|
||||||
TransactionOut = require('../../lib/TransactionDb'),
|
|
||||||
async = require('async');
|
|
||||||
|
|
||||||
var CONCURRENCY = 20;
|
|
||||||
|
|
||||||
function Transaction() {
|
|
||||||
this.txid = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Transaction.fromIdWithInfo = function (txid,cb) {
|
|
||||||
var tx = new Transaction();
|
|
||||||
tx.txid = txid;
|
|
||||||
|
|
||||||
tx._fillInfo(function(err) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
if (! tx.info ) return cb();
|
|
||||||
|
|
||||||
return cb(err,tx);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Transaction.prototype._fillInfo = function(next) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
TransactionRpc.getRpcInfo(self.txid, function(err, info) {
|
|
||||||
if (err) return next(err);
|
|
||||||
Transaction._fillOutpoints(info, function() {
|
|
||||||
self.info = info;
|
|
||||||
return next();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Transaction._fillOutpoints = function(info, cb) {
|
|
||||||
|
|
||||||
if (!info || info.isCoinBase) return cb();
|
|
||||||
|
|
||||||
var valueIn = 0;
|
|
||||||
var incompleteInputs = 0;
|
|
||||||
async.eachLimit(info.vin, CONCURRENCY, function(i, c_in) {
|
|
||||||
TransactionOut.fromTxIdN(i.txid, i.vout, function(err, addr, valueSat) {
|
|
||||||
|
|
||||||
|
|
||||||
if (err || !addr || !valueSat ) {
|
|
||||||
console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid);
|
|
||||||
incompleteInputs = 1;
|
|
||||||
return c_in(); // error not scaled
|
|
||||||
}
|
|
||||||
i.addr = addr;
|
|
||||||
i.valueSat = valueSat;
|
|
||||||
i.value = valueSat / util.COIN;
|
|
||||||
|
|
||||||
valueIn += i.valueSat;
|
|
||||||
return c_in();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
if (! incompleteInputs ) {
|
|
||||||
info.valueIn = valueIn / util.COIN;
|
|
||||||
info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN ;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
info.incompleteInputs = 1;
|
|
||||||
}
|
|
||||||
return cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return Transaction;
|
|
||||||
}
|
|
||||||
module.defineClass(spec);
|
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var T = require('../app/models/Transaction').class();
|
var T = require('../lib/TransactionDb').class();
|
||||||
var mongoose= require('mongoose'),
|
|
||||||
config = require('../config/config');
|
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
@ -14,12 +12,8 @@ var hash = process.argv[2] || 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var t = new T();
|
||||||
mongoose.connect(config.db);
|
t.fromIdWithInfo(hash, function(err, ret) {
|
||||||
mongoose.connection.on('error', function(err) { console.log('error!', err); });
|
|
||||||
mongoose.connection.on('open', function() {
|
|
||||||
|
|
||||||
T.fromIdWithInfo(hash, function(err, ret) {
|
|
||||||
|
|
||||||
console.log('Err:');
|
console.log('Err:');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
@ -27,8 +21,6 @@ mongoose.connection.on('open', function() {
|
||||||
|
|
||||||
console.log('Ret:');
|
console.log('Ret:');
|
||||||
console.log(util.inspect(ret,{depth:null}));
|
console.log(util.inspect(ret,{depth:null}));
|
||||||
mongoose.connection.close();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Sync = require('../lib/Sync').class();
|
||||||
|
|
||||||
|
|
||||||
|
var s = new Sync();
|
||||||
|
|
||||||
|
|
||||||
|
s.setOrphan(
|
||||||
|
'0000000000c2b1e8dab92a72741289e5ef0d4f375fd1b26f729da2ba979c028a',
|
||||||
|
'000000000228f9d02654459e09998c7557afa9082784c11226853f5feb805df9',
|
||||||
|
function (err) {
|
||||||
|
console.log('[sync-level.js.15]',err); //TODO
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var
|
||||||
|
config = require('../config/config'),
|
||||||
|
levelup = require('levelup');
|
||||||
|
|
||||||
|
|
||||||
|
db = levelup(config.leveldb + '/txs');
|
||||||
|
|
||||||
|
var s = 'txouts-addr-mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ';
|
||||||
|
db.createReadStream({start: s, end: s+'~'})
|
||||||
|
.on('data', function (data) {
|
||||||
|
console.log('[block-level.js.11:data:]',data); //TODO
|
||||||
|
if (data==false) c++;
|
||||||
|
})
|
||||||
|
.on('end', function () {
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ require('classtool');
|
||||||
|
|
||||||
function spec() {
|
function spec() {
|
||||||
|
|
||||||
var TIMESTAMP_ROOT = 'b-ts-';
|
var TIMESTAMP_ROOT = 'b-ts-'; // b-ts-<ts> => <hash>
|
||||||
var PREV_ROOT = 'b-prev-'; // b-prev-<hash> => <prev_hash> (0 if orphan)
|
var PREV_ROOT = 'b-prev-'; // b-prev-<hash> => <prev_hash> (0 if orphan)
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,11 +44,12 @@ function spec() {
|
||||||
|
|
||||||
self.db.batch()
|
self.db.batch()
|
||||||
.put(time_key, b.hash)
|
.put(time_key, b.hash)
|
||||||
.put(PREV_ROOT + b.hash, b.prev_block)
|
.put(PREV_ROOT + b.hash, b.previousblockhash)
|
||||||
.write(cb);
|
.write(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BlockDb.prototype.setOrphan = function(hash, cb) {
|
BlockDb.prototype.setOrphan = function(hash, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -80,9 +81,9 @@ function spec() {
|
||||||
|
|
||||||
|
|
||||||
BlockDb.prototype.countNotOrphan = function(cb) {
|
BlockDb.prototype.countNotOrphan = function(cb) {
|
||||||
var self = this;
|
|
||||||
var c = 0;
|
var c = 0;
|
||||||
this.db.createReadStream({start: PREV_ROOT})
|
console.log('Counting connected blocks. This could take some minutes');
|
||||||
|
this.db.createReadStream({start: PREV_ROOT, end: PREV_ROOT + '~' })
|
||||||
.on('data', function (data) {
|
.on('data', function (data) {
|
||||||
if (data.value !== 0) c++;
|
if (data.value !== 0) c++;
|
||||||
})
|
})
|
||||||
|
@ -90,7 +91,7 @@ function spec() {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
})
|
})
|
||||||
.on('end', function () {
|
.on('end', function () {
|
||||||
return cb(null);
|
return cb(null, c);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ function spec() {
|
||||||
magic = self.currentParser ? self.currentParser.buffer(4).toString('hex')
|
magic = self.currentParser ? self.currentParser.buffer(4).toString('hex')
|
||||||
: null ;
|
: null ;
|
||||||
|
|
||||||
if (!self.currentParser || self.currentParser.eof()) {
|
if (!self.currentParser || self.currentParser.eof() || magic === '00000000') {
|
||||||
magic = null;
|
magic = null;
|
||||||
if (self.nextFile()) {
|
if (self.nextFile()) {
|
||||||
console.log('Moving forward to file:' + self.currentFile() );
|
console.log('Moving forward to file:' + self.currentFile() );
|
||||||
|
@ -121,7 +121,8 @@ function spec() {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log('Finished all files');
|
console.log('Finished all files');
|
||||||
return cb();
|
magic = null;
|
||||||
|
return w_cb();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -130,6 +131,8 @@ function spec() {
|
||||||
}, a_cb);
|
}, a_cb);
|
||||||
},
|
},
|
||||||
function (a_cb) {
|
function (a_cb) {
|
||||||
|
if (!magic) return a_cb();
|
||||||
|
|
||||||
if (magic !== self.magic) {
|
if (magic !== self.magic) {
|
||||||
var e = new Error('CRITICAL ERROR: Magic number mismatch: ' +
|
var e = new Error('CRITICAL ERROR: Magic number mismatch: ' +
|
||||||
magic + '!=' + self.magic);
|
magic + '!=' + self.magic);
|
||||||
|
@ -141,6 +144,8 @@ function spec() {
|
||||||
return a_cb();
|
return a_cb();
|
||||||
},
|
},
|
||||||
function (a_cb) {
|
function (a_cb) {
|
||||||
|
if (!magic) return a_cb();
|
||||||
|
|
||||||
b = new Block();
|
b = new Block();
|
||||||
b.parse(self.currentParser);
|
b.parse(self.currentParser);
|
||||||
b.getHash();
|
b.getHash();
|
||||||
|
|
|
@ -9,7 +9,6 @@ function spec() {
|
||||||
var RpcClient = require('bitcore/RpcClient').class();
|
var RpcClient = require('bitcore/RpcClient').class();
|
||||||
var bitutil = require('bitcore/util/util');
|
var bitutil = require('bitcore/util/util');
|
||||||
var Address = require('bitcore/Address').class();
|
var Address = require('bitcore/Address').class();
|
||||||
var Deserialize = require('bitcore/Deserialize');
|
|
||||||
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');
|
||||||
|
@ -17,6 +16,7 @@ function spec() {
|
||||||
var Sync = require('./Sync').class();
|
var Sync = require('./Sync').class();
|
||||||
var sockets = require('../app/controllers/socket.js');
|
var sockets = require('../app/controllers/socket.js');
|
||||||
var BlockExtractor = require('./BlockExtractor.js').class();
|
var BlockExtractor = require('./BlockExtractor.js').class();
|
||||||
|
//var Deserialize = require('bitcore/Deserialize');
|
||||||
|
|
||||||
|
|
||||||
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:';
|
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:';
|
||||||
|
@ -37,6 +37,7 @@ function spec() {
|
||||||
this.syncedBlocks = 0;
|
this.syncedBlocks = 0;
|
||||||
this.skippedBlocks = 0;
|
this.skippedBlocks = 0;
|
||||||
this.orphanBlocks = 0;
|
this.orphanBlocks = 0;
|
||||||
|
this.type ='';
|
||||||
}
|
}
|
||||||
|
|
||||||
function p() {
|
function p() {
|
||||||
|
@ -97,6 +98,7 @@ function spec() {
|
||||||
syncedBlocks: this.syncedBlocks,
|
syncedBlocks: this.syncedBlocks,
|
||||||
orphanBlocks: this.orphanBlocks,
|
orphanBlocks: this.orphanBlocks,
|
||||||
error: this.error,
|
error: this.error,
|
||||||
|
type: this.type,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,13 +117,6 @@ function spec() {
|
||||||
if (self.opts.shouldBroadcast) {
|
if (self.opts.shouldBroadcast) {
|
||||||
sockets.broadcastSyncInfo(self.info());
|
sockets.broadcastSyncInfo(self.info());
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
|
||||||
/*
|
|
||||||
if (self.syncPercentage>10) {
|
|
||||||
console.log(self.info());
|
|
||||||
process.exit(1);
|
|
||||||
}*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
HistoricSync.prototype.getPrevNextBlock = function(blockHash, blockEnd, scanOpts, cb) {
|
HistoricSync.prototype.getPrevNextBlock = function(blockHash, blockEnd, scanOpts, cb) {
|
||||||
|
@ -136,7 +131,7 @@ if (self.syncPercentage>10) {
|
||||||
async.series([
|
async.series([
|
||||||
// Already got it?
|
// Already got it?
|
||||||
function(c) {
|
function(c) {
|
||||||
self.sync.hasBlock(blockHash, function(err, ret) {
|
self.sync.blockDb.has(blockHash, function(err, ret) {
|
||||||
if (err) {
|
if (err) {
|
||||||
p(err);
|
p(err);
|
||||||
return c(err);
|
return c(err);
|
||||||
|
@ -168,11 +163,7 @@ if (self.syncPercentage>10) {
|
||||||
if (existed) return c();
|
if (existed) return c();
|
||||||
|
|
||||||
self.sync.storeBlock(blockInfo, function(err) {
|
self.sync.storeBlock(blockInfo, function(err) {
|
||||||
|
return c(err);
|
||||||
existed = err && err.toString().match(/E11000/);
|
|
||||||
|
|
||||||
if (err && ! existed) return c(err);
|
|
||||||
return c();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/* TODO: Should Start to sync backwards? (this is for partial syncs)
|
/* TODO: Should Start to sync backwards? (this is for partial syncs)
|
||||||
|
@ -247,9 +238,7 @@ if (self.syncPercentage>10) {
|
||||||
addrStrs = [ addr.toString() ];
|
addrStrs = [ addr.toString() ];
|
||||||
break;
|
break;
|
||||||
case Script.TX_MULTISIG:
|
case Script.TX_MULTISIG:
|
||||||
var addrs = [];
|
|
||||||
var chunks = s.capture();
|
var chunks = s.capture();
|
||||||
|
|
||||||
chunks.forEach(function(chunk) {
|
chunks.forEach(function(chunk) {
|
||||||
var a = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk));
|
var a = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk));
|
||||||
addrStrs.push(a.toString());
|
addrStrs.push(a.toString());
|
||||||
|
@ -262,15 +251,10 @@ if (self.syncPercentage>10) {
|
||||||
return addrStrs;
|
return addrStrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
var kk=0;
|
|
||||||
|
|
||||||
HistoricSync.prototype.getBlockFromFile = function(scanOpts, cb) {
|
HistoricSync.prototype.getBlockFromFile = function(scanOpts, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var nextHash;
|
|
||||||
var blockInfo;
|
var blockInfo;
|
||||||
var existed;
|
var existed;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//show some (inacurate) status
|
//show some (inacurate) status
|
||||||
function(c) {
|
function(c) {
|
||||||
|
@ -287,12 +271,20 @@ var kk=0;
|
||||||
if (err || ! b) return c(err);
|
if (err || ! b) return c(err);
|
||||||
|
|
||||||
blockInfo = b.getStandardizedObject(b.txs, self.network);
|
blockInfo = b.getStandardizedObject(b.txs, self.network);
|
||||||
blockInfo.curWork = Deserialize.intFromCompact(b.bits);
|
// blockInfo.curWork = Deserialize.intFromCompact(b.bits);
|
||||||
|
// We keep the RPC field names
|
||||||
|
blockInfo.previousblockhash = blockInfo.prev_block;
|
||||||
|
|
||||||
var ti=0;
|
var ti=0;
|
||||||
// Get TX Address
|
// Get TX Address
|
||||||
b.txs.forEach(function(t) {
|
b.txs.forEach(function(t) {
|
||||||
|
|
||||||
|
|
||||||
var objTx = blockInfo.tx[ti++];
|
var objTx = blockInfo.tx[ti++];
|
||||||
|
|
||||||
|
//add time from block
|
||||||
|
objTx.time = blockInfo.time;
|
||||||
|
|
||||||
var to=0;
|
var to=0;
|
||||||
t.outs.forEach( function(o) {
|
t.outs.forEach( function(o) {
|
||||||
|
|
||||||
|
@ -311,30 +303,39 @@ var kk=0;
|
||||||
return c();
|
return c();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//store it
|
//check prev
|
||||||
function(c) {
|
function(c) {
|
||||||
if (self.prevHash && blockInfo.prev_block !== self.prevHash) {
|
if (blockInfo && self.prevHash && blockInfo.previousblockhash !== self.prevHash) {
|
||||||
|
|
||||||
console.log('Orphans found: @%s', blockInfo.hash);
|
console.log('Hole found @%s', blockInfo.hash);
|
||||||
|
console.log('From: %s To: %s', self.prevHash, blockInfo.previousblockhash);
|
||||||
console.log('From: %s To: %s', self.prevHash, blockInfo.prev_block);
|
self.sync.checkOrphan(self.prevHash, blockInfo.previousblockhash, c);
|
||||||
process.exit(1);
|
|
||||||
self.sync.setOrphan(self.prevHash, blockInfo.prev_block, c);
|
|
||||||
}
|
}
|
||||||
else return c();
|
else return c();
|
||||||
},
|
},
|
||||||
|
//check it
|
||||||
|
function(c) {
|
||||||
|
if (!blockInfo) return c();
|
||||||
|
|
||||||
|
self.sync.blockDb.has(blockInfo.hash, function(err, had) {
|
||||||
|
existed = had;
|
||||||
|
return c(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
//store it
|
//store it
|
||||||
function(c) {
|
function(c) {
|
||||||
self.sync.storeBlock(blockInfo, function(err) {
|
if (!blockInfo || existed) return c();
|
||||||
existed = err && err.toString().match(/E11000/);
|
|
||||||
|
|
||||||
if (err && ! existed) return c(err);
|
self.sync.storeBlock(blockInfo, function(err) {
|
||||||
return c();
|
return c(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(c) {
|
function(c) {
|
||||||
if (blockInfo && blockInfo.hash) {
|
if (blockInfo && blockInfo.hash) {
|
||||||
self.prevHash = blockInfo.hash;
|
self.prevHash = blockInfo.hash;
|
||||||
|
if (existed)
|
||||||
|
self.skippedBlocks++;
|
||||||
|
else
|
||||||
self.syncedBlocks++;
|
self.syncedBlocks++;
|
||||||
} else
|
} else
|
||||||
self.status = 'finished';
|
self.status = 'finished';
|
||||||
|
@ -343,13 +344,27 @@ process.exit(1);
|
||||||
},
|
},
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
self.setError(util.format('ERROR: @%s: %s [count: syncedBlocks: %d]', blockInfo.hash, err, self.syncedBlocks));
|
self.setError(util.format('ERROR: @%s: %s [count: syncedBlocks: %d]', blockInfo ? blockInfo.hash : '-', err, self.syncedBlocks));
|
||||||
}
|
}
|
||||||
return cb(err);
|
return cb(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
HistoricSync.prototype.countNotOrphan = function(cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (self.notOrphanCount) return cb(null, self.notOrphanCount);
|
||||||
|
|
||||||
|
|
||||||
|
self.sync.blockDb.countNotOrphan(function(err, count) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
self.notOrphanCount = count;
|
||||||
|
return cb(null, self.notOrphanCount);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
HistoricSync.prototype.getBlockCount = function(cb) {
|
HistoricSync.prototype.getBlockCount = function(cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -390,7 +405,7 @@ process.exit(1);
|
||||||
function(cb) {
|
function(cb) {
|
||||||
if (scanOpts.upToExisting) {
|
if (scanOpts.upToExisting) {
|
||||||
// should be isOrphan = true or null to be more accurate.
|
// should be isOrphan = true or null to be more accurate.
|
||||||
self.sync.countNotOrphan(function(err, count) {
|
self.countNotOrphan(function(err, count) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
self.syncedBlocks = count || 0;
|
self.syncedBlocks = count || 0;
|
||||||
|
@ -437,10 +452,8 @@ process.exit(1);
|
||||||
p(' scanOpts: ', JSON.stringify(scanOpts));
|
p(' scanOpts: ', JSON.stringify(scanOpts));
|
||||||
|
|
||||||
if (scanOpts.fromFiles) {
|
if (scanOpts.fromFiles) {
|
||||||
|
|
||||||
var height = 0;
|
|
||||||
|
|
||||||
self.status = 'syncing';
|
self.status = 'syncing';
|
||||||
|
self.type = 'from .dat Files';
|
||||||
async.whilst(function() {
|
async.whilst(function() {
|
||||||
return self.status === 'syncing';
|
return self.status === 'syncing';
|
||||||
}, function (w_cb) {
|
}, function (w_cb) {
|
||||||
|
@ -454,6 +467,7 @@ process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
self.type = 'from RPC calls';
|
||||||
self.getPrevNextBlock(start, end, scanOpts, function(err) {
|
self.getPrevNextBlock(start, end, scanOpts, function(err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
});
|
});
|
||||||
|
@ -466,19 +480,19 @@ process.exit(1);
|
||||||
HistoricSync.prototype.smartImport = function(scanOpts, next) {
|
HistoricSync.prototype.smartImport = function(scanOpts, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.sync.hasBlock(self.genesis, function(err, b) {
|
self.sync.blockDb.has(self.genesis, function(err, b) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
self.sync.countNotOrphan(function(err, count) {
|
self.countNotOrphan(function(err, count) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
self.getBlockCount(function(err) {
|
self.getBlockCount(function(err) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (!b || scanOpts.destroy || self.blockChainHeight > count * 0.7 ) {
|
if (!b || scanOpts.destroy || count < self.blockChainHeight * 0.8 ) {
|
||||||
|
|
||||||
if (!b)
|
if (!b)
|
||||||
p('Could not find Genesis block. Running FULL SYNC');
|
p('Could not find Genesis block. Running FULL SYNC');
|
||||||
else
|
else
|
||||||
p('Less that 70% of current blockchain is stored. Running FULL SYNC',
|
p('Less that 80% of current blockchain is stored. Running FULL SYNC',
|
||||||
parseInt(count/self.blockChainHeight*100));
|
parseInt(count/self.blockChainHeight*100));
|
||||||
|
|
||||||
if (config.bitcoind.dataDir) {
|
if (config.bitcoind.dataDir) {
|
||||||
|
@ -492,6 +506,7 @@ process.exit(1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p('Genesis block found. Syncing upto known blocks.');
|
p('Genesis block found. Syncing upto known blocks.');
|
||||||
|
p('Got %d out of %d blocks', count, self.blockChainHeight);
|
||||||
scanOpts.reverse = true;
|
scanOpts.reverse = true;
|
||||||
scanOpts.upToExisting = true;
|
scanOpts.upToExisting = true;
|
||||||
}
|
}
|
||||||
|
|
42
lib/Sync.js
42
lib/Sync.js
|
@ -34,24 +34,12 @@ function spec() {
|
||||||
Sync.prototype.destroy = function(next) {
|
Sync.prototype.destroy = function(next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
async.series([
|
async.series([
|
||||||
function(b) { try {self.blockDb.drop(b);} catch (e) { return b(); } },
|
function(b) { self.blockDb.drop(b); },
|
||||||
function(b) { try {self.TransactionDb.drop(b);} catch (e) { return b(); } },
|
function(b) { self.txDb.drop(b); },
|
||||||
], next);
|
], next);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Sync.prototype.hasBlock = function(hash, cb) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
return self.blockDb.has(hash, cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
Sync.prototype.countNotOrphan = function(hash, cb) {
|
|
||||||
var self = this;
|
|
||||||
return self.blockDb.countNotOrphan(hash, cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Sync.prototype.storeBlock = function(block, cb) {
|
Sync.prototype.storeBlock = function(block, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -66,24 +54,42 @@ function spec() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Sync.prototype.setOrphan = function(fromBlock, toBlock, c) {
|
Sync.prototype.checkOrphan = function(fromBlock, toBlock, c) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var hash = fromBlock;
|
var hash = fromBlock;
|
||||||
|
|
||||||
|
var co = 0;
|
||||||
|
var limit = 10;
|
||||||
|
var cont = 1;
|
||||||
|
|
||||||
async.whilst(
|
async.whilst(
|
||||||
function () {
|
function () {
|
||||||
return hash && hash !== toBlock;
|
if (++co > limit) {
|
||||||
|
console.log('[Sync.js.109] WARN: Reach reog depth limit'); //TODO
|
||||||
|
}
|
||||||
|
return cont && hash && hash !== toBlock && co < limit;
|
||||||
},
|
},
|
||||||
function (w_c) {
|
function (w_c) {
|
||||||
self.txDb.setOrphan(hash, function(err, insertedTxs, updateAddrs) {
|
//check with RPC if the block is mainchain
|
||||||
|
self.blockDb.fromHashWithInfo(hash, function (err, info) {
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
console.log('[Sync.js.107:hash:ORPHAN]',hash); //TODO
|
||||||
|
self.txDb.setOrphan(hash, function(err) {
|
||||||
if (err) return w_c(err);
|
if (err) return w_c(err);
|
||||||
self.blockDb.setOrphan(hash, function(err, prevHash){
|
self.blockDb.setOrphan(hash, function(err, prevHash){
|
||||||
|
|
||||||
hash = prevHash;
|
hash = prevHash;
|
||||||
return w_c(err);
|
return w_c(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('[Sync.js.107:hash:NOT ORPHAN]',hash); //TODO
|
||||||
|
cont = 0;
|
||||||
|
return w_c();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
function (err) {
|
function (err) {
|
||||||
return c(err);
|
return c(err);
|
||||||
|
|
|
@ -5,8 +5,8 @@ require('classtool');
|
||||||
|
|
||||||
function spec() {
|
function spec() {
|
||||||
|
|
||||||
// blockHash -> txid mapping (to orphanize )/
|
// blockHash -> txid mapping (to reorgs)
|
||||||
var ROOT = 'tx-b-'; //tx-b-<block> => txid
|
var ROOT = 'tx-b-'; //tx-b-<txid>-<block> => 1
|
||||||
|
|
||||||
// to show tx outs
|
// to show tx outs
|
||||||
var OUTS_ROOT = 'txouts-'; //txouts-<txid>-<n> => [addr, btc_sat]
|
var OUTS_ROOT = 'txouts-'; //txouts-<txid>-<n> => [addr, btc_sat]
|
||||||
|
@ -26,7 +26,8 @@ function spec() {
|
||||||
util = require('bitcore/util/util'),
|
util = require('bitcore/util/util'),
|
||||||
levelup = require('levelup'),
|
levelup = require('levelup'),
|
||||||
async = require('async'),
|
async = require('async'),
|
||||||
config = require('../config/config');
|
config = require('../config/config'),
|
||||||
|
assert = require('assert');
|
||||||
|
|
||||||
var TransactionDb = function() {
|
var TransactionDb = function() {
|
||||||
this.db = levelup(config.leveldb + '/txs');
|
this.db = levelup(config.leveldb + '/txs');
|
||||||
|
@ -91,12 +92,16 @@ function spec() {
|
||||||
.on('data', function (data) {
|
.on('data', function (data) {
|
||||||
var k = data.key.split('-');
|
var k = data.key.split('-');
|
||||||
var v = data.value.split(':');
|
var v = data.value.split(':');
|
||||||
|
var set=0;
|
||||||
for(var i=0; i<l; i++) {
|
for(var i=0; i<l; i++) {
|
||||||
if (ret[i].index === k[3]) {
|
if (ret[i].index === k[3]) {
|
||||||
ret[i].spendTxId= v[0];
|
ret[i].spendTxId= v[0];
|
||||||
ret[i].spendIndex=v[1];
|
ret[i].spendIndex=v[1];
|
||||||
|
set=1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(set,'Spent could not be stored: tx ' + txid +
|
||||||
|
'spend in TX:' + k[2] + ',' + k[3]);
|
||||||
})
|
})
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
|
@ -107,6 +112,65 @@ function spec() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TransactionDb.prototype._fillOutpoints = function(info, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (!info || info.isCoinBase) return cb();
|
||||||
|
|
||||||
|
var valueIn = 0;
|
||||||
|
var incompleteInputs = 0;
|
||||||
|
async.eachLimit(info.vin, CONCURRENCY, function(i, c_in) {
|
||||||
|
self.fromTxIdN(i.txid, i.vout, function(err, addr, valueSat) {
|
||||||
|
|
||||||
|
|
||||||
|
if (err || !addr || !valueSat ) {
|
||||||
|
console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid);
|
||||||
|
incompleteInputs = 1;
|
||||||
|
return c_in(); // error not scaled
|
||||||
|
}
|
||||||
|
i.addr = addr;
|
||||||
|
i.valueSat = valueSat;
|
||||||
|
i.value = valueSat / util.COIN;
|
||||||
|
|
||||||
|
valueIn += i.valueSat;
|
||||||
|
return c_in();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
if (! incompleteInputs ) {
|
||||||
|
info.valueIn = valueIn / util.COIN;
|
||||||
|
info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN ;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
info.incompleteInputs = 1;
|
||||||
|
}
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TransactionDb.prototype._getInfo = function(txid, next) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
TransactionRpc.getRpcInfo(txid, function(err, info) {
|
||||||
|
if (err) return next(err);
|
||||||
|
|
||||||
|
self._fillOutpoints(info, function() {
|
||||||
|
return next(null, info);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TransactionDb.prototype.fromIdWithInfo = function (txid, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self._getInfo(txid, function(err, info) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
if (!info ) return cb();
|
||||||
|
|
||||||
|
return cb(err, {txid: txid, info: info} );
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
TransactionDb.prototype.fromTxIdN = function(txid, n, cb) {
|
TransactionDb.prototype.fromTxIdN = function(txid, n, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -117,8 +181,8 @@ function spec() {
|
||||||
if (err && err.notFound) {
|
if (err && err.notFound) {
|
||||||
err = null;
|
err = null;
|
||||||
}
|
}
|
||||||
var a = val.split('-');
|
var a = val.split(':');
|
||||||
return cb(err, val, a[0], a[1]);
|
return cb(err, a[0], a[1]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,7 +221,6 @@ function spec() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(err) {
|
function(err) {
|
||||||
|
|
||||||
console.log('[TransactionDb.js.165]', ret); //TODO
|
console.log('[TransactionDb.js.165]', ret); //TODO
|
||||||
return cb(err,ret);
|
return cb(err,ret);
|
||||||
});
|
});
|
||||||
|
@ -166,8 +229,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Only for testing. Very slow (toRm outs to rm, only to speedup)
|
TransactionDb.prototype.removeFromTxId = function(txid, cb) {
|
||||||
TransactionDb.prototype.removeFromTxId = function(txid, toRm, cb) {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
|
@ -179,27 +241,14 @@ console.log('[TransactionDb.js.165]', ret); //TODO
|
||||||
self.db.createWriteStream({type:'del'})
|
self.db.createWriteStream({type:'del'})
|
||||||
).on('close', c);
|
).on('close', c);
|
||||||
},
|
},
|
||||||
function(s_c) {
|
function(c) {
|
||||||
if (toRm && toRm.length) return s_c();
|
|
||||||
|
|
||||||
toRm = [];
|
|
||||||
self.db.createReadStream({
|
self.db.createReadStream({
|
||||||
start: SPEND_ROOT
|
start: SPEND_ROOT + txid,
|
||||||
|
end: SPEND_ROOT + txid + '~'
|
||||||
})
|
})
|
||||||
.on('data', function(data) {
|
.pipe(
|
||||||
if (data.value.indexOf(txid) >= 0) {
|
self.db.createWriteStream({type:'del'})
|
||||||
toRm.push(data.key);
|
).on('close', c);
|
||||||
console.log('To Remove Found', data.key); //TODO
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('end', function() {
|
|
||||||
return s_c();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(s_c) {
|
|
||||||
async.each(toRm, function(k,e_c) {
|
|
||||||
self.db.del(k,e_c);
|
|
||||||
}, s_c);
|
|
||||||
}],
|
}],
|
||||||
function(err) {
|
function(err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
|
@ -291,7 +340,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO
|
||||||
// }
|
// }
|
||||||
|
|
||||||
var addr = o.scriptPubKey.addresses[0];
|
var addr = o.scriptPubKey.addresses[0];
|
||||||
var sat = o.value * util.COIN;
|
var sat = Math.round(o.value * util.COIN);
|
||||||
self.db.batch()
|
self.db.batch()
|
||||||
.put( OUTS_ROOT + tx.txid + '-' + o.n, addr + ':' + sat)
|
.put( OUTS_ROOT + tx.txid + '-' + o.n, addr + ':' + sat)
|
||||||
.put( ADDR_ROOT + addr + '-' + ts + '-' + tx.txid +
|
.put( ADDR_ROOT + addr + '-' + ts + '-' + tx.txid +
|
||||||
|
@ -343,7 +392,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO
|
||||||
self.add(inInfo, function(err) {
|
self.add(inInfo, function(err) {
|
||||||
if (err || !blockHash) return each_cb(err);
|
if (err || !blockHash) return each_cb(err);
|
||||||
|
|
||||||
self.db.put(ROOT + blockHash, t, function(err) {
|
self.db.put(ROOT + t + '-' + blockHash, 1, function(err) {
|
||||||
return each_cb(err);
|
return each_cb(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -353,7 +402,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO
|
||||||
self.add(t, function(err) {
|
self.add(t, function(err) {
|
||||||
if (err) return each_cb(err);
|
if (err) return each_cb(err);
|
||||||
|
|
||||||
self.db.put(ROOT + blockHash, t.txid, function(err) {
|
self.db.put(ROOT + t.txid + '-' + blockHash, 1, function(err) {
|
||||||
return each_cb(err);
|
return each_cb(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -368,7 +417,7 @@ console.log('[TransactionDb.js.165]', ret); //TODO
|
||||||
// txs can be a [hashes] or [txObjects]
|
// txs can be a [hashes] or [txObjects]
|
||||||
TransactionDb.prototype.createFromBlock = function(b, next) {
|
TransactionDb.prototype.createFromBlock = function(b, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!b.tx) return next();
|
if (!b || !b.tx) return next();
|
||||||
|
|
||||||
return self.createFromArray(b.tx, b.hash, next);
|
return self.createFromArray(b.tx, b.hash, next);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,18 +5,116 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var
|
var assert = require('assert'),
|
||||||
assert = require('assert'),
|
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
config = require('../../config/config'),
|
|
||||||
TransactionDb = require('../../lib/TransactionDb').class();
|
TransactionDb = require('../../lib/TransactionDb').class();
|
||||||
|
|
||||||
var txItemsValid = JSON.parse(fs.readFileSync('test/integration/txitems.json'));
|
var txItemsValid = JSON.parse(fs.readFileSync('test/integration/txitems.json'));
|
||||||
|
var txDb;
|
||||||
|
|
||||||
describe('TransactionDb', function(){
|
describe('TransactionDb fromIdWithInfo', function(){
|
||||||
|
|
||||||
var tdb = new TransactionDb();
|
before(function(c) {
|
||||||
|
txDb = new TransactionDb();
|
||||||
|
return c();
|
||||||
|
});
|
||||||
|
|
||||||
|
var txid = '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c';
|
||||||
|
it('tx info ' + txid, function(done) {
|
||||||
|
txDb.fromIdWithInfo(txid, function(err, tx) {
|
||||||
|
|
||||||
|
if (err) done(err);
|
||||||
|
assert.equal(tx.txid, txid);
|
||||||
|
assert(!tx.info.isCoinBase);
|
||||||
|
|
||||||
|
for(var i=0; i<20; i++)
|
||||||
|
assert(parseFloat(tx.info.vin[i].value) === parseFloat(50), 'input '+i);
|
||||||
|
assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59', 'addr 0');
|
||||||
|
assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P', 'addr 1');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tx info', function(done) {
|
||||||
|
var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237';
|
||||||
|
txDb.fromIdWithInfo(txid, function(err, tx) {
|
||||||
|
if (err) done(err);
|
||||||
|
assert.equal(tx.txid, txid);
|
||||||
|
assert(!tx.info.isCoinBase);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pool tx\'s info from bitcoind', function(done) {
|
||||||
|
var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237';
|
||||||
|
txDb.fromIdWithInfo(txid, function(err, tx) {
|
||||||
|
if (err) done(err);
|
||||||
|
assert.equal(tx.info.txid, txid);
|
||||||
|
assert.equal(tx.info.blockhash, '000000000185678d3d7ecc9962c96418174431f93fe20bf216d5565272423f74');
|
||||||
|
assert.equal(tx.info.valueOut, 1.66174);
|
||||||
|
assert.equal(tx.info.fees, 0.0005 );
|
||||||
|
assert.equal(tx.info.size, 226 );
|
||||||
|
assert(!tx.info.isCoinBase);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var txid1 = '2a104bab1782e9b6445583296d4a0ecc8af304e4769ceb64b890e8219c562399';
|
||||||
|
it('test a coinbase TX ' + txid1, function(done) {
|
||||||
|
txDb.fromIdWithInfo(txid1, function(err, tx) {
|
||||||
|
if (err) done(err);
|
||||||
|
assert(tx.info.isCoinBase);
|
||||||
|
assert.equal(tx.info.txid, txid1);
|
||||||
|
assert(!tx.info.feeds);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var txid22 = '666';
|
||||||
|
it('test invalid TX ' + txid22, function(done) {
|
||||||
|
txDb.fromIdWithInfo(txid22, function(err, tx) {
|
||||||
|
if (err && err.message.match(/must.be.hexadecimal/)) {
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var txid23 = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca227';
|
||||||
|
it('test unexisting TX ' + txid23, function(done) {
|
||||||
|
|
||||||
|
txDb.fromIdWithInfo(txid23, function(err, tx) {
|
||||||
|
assert(!err);
|
||||||
|
assert(!tx);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608';
|
||||||
|
it('create TX on the fly ' + txid2, function(done) {
|
||||||
|
txDb.fromIdWithInfo(txid2, function(err, tx) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assert.equal(tx.info.txid, txid2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608';
|
||||||
|
it('test a broken TX ' + txid2, function(done) {
|
||||||
|
txDb.fromIdWithInfo(txid2, function(err, tx) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assert.equal(tx.info.txid, txid2);
|
||||||
|
assert.equal(tx.info.vin[0].addr, 'n1JagbRWBDi6VMvG7HfZmXX74dB9eiHJzU');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('TransactionDb Outs', function(){
|
||||||
|
|
||||||
txItemsValid.forEach( function(v) {
|
txItemsValid.forEach( function(v) {
|
||||||
if (v.disabled) return;
|
if (v.disabled) return;
|
||||||
|
@ -24,16 +122,16 @@ describe('TransactionDb', function(){
|
||||||
this.timeout(60000);
|
this.timeout(60000);
|
||||||
|
|
||||||
// Remove first
|
// Remove first
|
||||||
tdb.removeFromTxId(v.txid, v.toRm, function() {
|
txDb.removeFromTxId(v.txid, function() {
|
||||||
|
|
||||||
tdb.fromTxId( v.txid, function(err, readItems) {
|
txDb.fromTxId( v.txid, function(err, readItems) {
|
||||||
assert.equal(readItems.length,0);
|
assert.equal(readItems.length,0);
|
||||||
|
|
||||||
var unmatch=[];
|
var unmatch=[];
|
||||||
tdb.createFromArray([v.txid], null, function(err) {
|
txDb.createFromArray([v.txid], null, function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
|
||||||
tdb.fromTxId( v.txid, function(err, readItems) {
|
txDb.fromTxId( v.txid, function(err, readItems) {
|
||||||
|
|
||||||
v.items.forEach(function(validItem){
|
v.items.forEach(function(validItem){
|
||||||
unmatch[validItem.addr] =1;
|
unmatch[validItem.addr] =1;
|
||||||
|
@ -64,4 +162,3 @@ describe('TransactionDb', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
var
|
var assert = require('assert'),
|
||||||
assert = require('assert'),
|
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
config = require('../../config/config'),
|
Address = require('../../app/models/Address').class(),
|
||||||
Address = require('../../app/models/Address').class();
|
TransactionDb = require('../../lib/TransactionDb').class(),
|
||||||
addrValid = JSON.parse(fs.readFileSync('test/integration/addr.json'));
|
addrValid = JSON.parse(fs.readFileSync('test/integration/addr.json'));
|
||||||
|
|
||||||
|
var txDb;
|
||||||
describe('Address balances', function(){
|
describe('Address balances', function(){
|
||||||
|
|
||||||
|
before(function(c) {
|
||||||
|
txDb = new TransactionDb();
|
||||||
|
return c();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
addrValid.forEach( function(v) {
|
addrValid.forEach( function(v) {
|
||||||
if (v.disabled) {
|
if (v.disabled) {
|
||||||
console.log(v.addr + " => disabled in JSON");
|
console.log(v.addr + " => disabled in JSON");
|
||||||
|
@ -19,7 +26,7 @@ describe('Address balances', function(){
|
||||||
it('Info for: ' + v.addr, function(done) {
|
it('Info for: ' + v.addr, function(done) {
|
||||||
this.timeout(5000);
|
this.timeout(5000);
|
||||||
|
|
||||||
var a = new Address(v.addr);
|
var a = new Address(v.addr, txDb);
|
||||||
|
|
||||||
a.update(function(err) {
|
a.update(function(err) {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
[
|
[
|
||||||
|
|
||||||
{
|
{
|
||||||
"disabled":1,
|
|
||||||
"addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ",
|
|
||||||
"balance": 43.1,
|
|
||||||
"txApperances": 19
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"disabled":1,
|
|
||||||
"addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS",
|
"addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS",
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"totalReceived": 50,
|
"totalReceived": 50,
|
||||||
|
@ -14,7 +8,6 @@
|
||||||
"txApperances": 2
|
"txApperances": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"disabled":1,
|
|
||||||
"addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH",
|
"addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH",
|
||||||
"balance": 0.38571339,
|
"balance": 0.38571339,
|
||||||
"totalReceived": 0.38571339,
|
"totalReceived": 0.38571339,
|
||||||
|
@ -29,7 +22,6 @@
|
||||||
"txApperances": 13
|
"txApperances": 13
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"disabled":1,
|
|
||||||
"addr": "n47CfqnKWdNwqY1UWxTmNJAqYutFxdH3zY",
|
"addr": "n47CfqnKWdNwqY1UWxTmNJAqYutFxdH3zY",
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"totalReceived":26.4245,
|
"totalReceived":26.4245,
|
||||||
|
@ -37,7 +29,6 @@
|
||||||
"txApperances": 4
|
"txApperances": 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"disabled":1,
|
|
||||||
"addr": "mzSyyXgofoBxpr6gYcU3cV345G8hJpixRd",
|
"addr": "mzSyyXgofoBxpr6gYcU3cV345G8hJpixRd",
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"totalReceived":3.9775,
|
"totalReceived":3.9775,
|
||||||
|
@ -45,7 +36,11 @@
|
||||||
"txApperances": 2
|
"txApperances": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"disabled":1,
|
"addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ",
|
||||||
|
"balance": 43.1,
|
||||||
|
"txApperances": 19
|
||||||
|
},
|
||||||
|
{
|
||||||
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
||||||
"txApperances": 2034,
|
"txApperances": 2034,
|
||||||
"balance": 1049.69744099,
|
"balance": 1049.69744099,
|
||||||
|
@ -53,7 +48,6 @@
|
||||||
"totalSent": 0
|
"totalSent": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"disabled":1,
|
|
||||||
"addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H",
|
"addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H",
|
||||||
"txApperances": 13343,
|
"txApperances": 13343,
|
||||||
"balance": 46413.0,
|
"balance": 46413.0,
|
||||||
|
@ -61,7 +55,6 @@
|
||||||
"totalSent": 310717.17644359
|
"totalSent": 310717.17644359
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"disabled":1,
|
|
||||||
"addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM",
|
"addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM",
|
||||||
"txApperances": 1,
|
"txApperances": 1,
|
||||||
"balance": 0.01979459,
|
"balance": 0.01979459,
|
||||||
|
@ -70,7 +63,6 @@
|
||||||
"transactions": [ "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5" ]
|
"transactions": [ "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"disabled":1,
|
|
||||||
"addr": "mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5",
|
"addr": "mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5",
|
||||||
"balance": 10580.50027254,
|
"balance": 10580.50027254,
|
||||||
"totalReceived": 12157.65075053,
|
"totalReceived": 12157.65075053,
|
||||||
|
|
|
@ -42,8 +42,25 @@ var test = function(s,cb) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var testNo = function(s,cb) {
|
||||||
|
async.each([2,3,4], function(i,c) {
|
||||||
|
s.blockDb.getPrev(b[i], function(err, p) {
|
||||||
|
assert.equal(p,b[i-1]);
|
||||||
|
return c();
|
||||||
|
});
|
||||||
|
}, function() {
|
||||||
|
s.blockDb.getPrev(b[1], function(err, p) {
|
||||||
|
assert.equal(p,b[0]);
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var s;
|
var s;
|
||||||
describe('Sync setOrphan', function(){
|
describe('Sync checkOrphan', function(){
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
s = new Sync();
|
s = new Sync();
|
||||||
|
@ -54,15 +71,15 @@ describe('Sync setOrphan', function(){
|
||||||
fix(s,done);
|
fix(s,done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setOrphan', function(done) {
|
it('checkOrphan', function(done) {
|
||||||
this.timeout(100000);
|
this.timeout(100000);
|
||||||
|
|
||||||
s.blockDb.has(b[0], function(err, has) {
|
s.blockDb.has(b[0], function(err, has) {
|
||||||
assert(has);
|
assert(has);
|
||||||
s.blockDb.has(b[1], function(err, has) {
|
s.blockDb.has(b[1], function(err, has) {
|
||||||
assert(has);
|
assert(has);
|
||||||
s.setOrphan(b[4],b[1], function() {
|
s.checkOrphan(b[4],b[1], function() {
|
||||||
test(s,done);
|
testNo(s,done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var mongoose= require('mongoose'),
|
|
||||||
assert = require('assert'),
|
|
||||||
config = require('../../config/config'),
|
|
||||||
Transaction = require('../../app/models/Transaction').class();
|
|
||||||
|
|
||||||
|
|
||||||
mongoose.connection.on('error', function(err) { console.log(err); });
|
|
||||||
|
|
||||||
describe('Transaction', function(){
|
|
||||||
|
|
||||||
before(function(done) {
|
|
||||||
mongoose.connect(config.db);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
after(function(done) {
|
|
||||||
mongoose.connection.close();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
var txid = '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c';
|
|
||||||
it('txid ' + txid, function(done) {
|
|
||||||
Transaction.fromIdWithInfo(txid, function(err, tx) {
|
|
||||||
if (err) done(err);
|
|
||||||
assert.equal(tx.txid, txid);
|
|
||||||
assert(!tx.info.isCoinBase);
|
|
||||||
|
|
||||||
for(var i=0; i<20; i++)
|
|
||||||
assert(parseFloat(tx.info.vin[i].value) === parseFloat(50), 'input '+i);
|
|
||||||
assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59', 'addr 0');
|
|
||||||
assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P', 'addr 1');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should pool tx\'s object from mongoose', function(done) {
|
|
||||||
var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237';
|
|
||||||
Transaction.fromIdWithInfo(txid, function(err, tx) {
|
|
||||||
if (err) done(err);
|
|
||||||
assert.equal(tx.txid, txid);
|
|
||||||
assert(!tx.info.isCoinBase);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should pool tx\'s info from bitcoind', function(done) {
|
|
||||||
var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237';
|
|
||||||
Transaction.fromIdWithInfo(txid, function(err, tx) {
|
|
||||||
if (err) done(err);
|
|
||||||
assert.equal(tx.info.txid, txid);
|
|
||||||
assert.equal(tx.info.blockhash, '000000000185678d3d7ecc9962c96418174431f93fe20bf216d5565272423f74');
|
|
||||||
assert.equal(tx.info.valueOut, 1.66174);
|
|
||||||
assert.equal(tx.info.fees, 0.0005 );
|
|
||||||
assert.equal(tx.info.size, 226 );
|
|
||||||
assert(!tx.info.isCoinBase);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var txid1 = '2a104bab1782e9b6445583296d4a0ecc8af304e4769ceb64b890e8219c562399';
|
|
||||||
it('test a coinbase TX ' + txid1, function(done) {
|
|
||||||
Transaction.fromIdWithInfo(txid1, function(err, tx) {
|
|
||||||
if (err) done(err);
|
|
||||||
assert(tx.info.isCoinBase);
|
|
||||||
assert.equal(tx.info.txid, txid1);
|
|
||||||
assert(!tx.info.feeds);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var txid22 = '666';
|
|
||||||
it('test invalid TX ' + txid22, function(done) {
|
|
||||||
Transaction.fromIdWithInfo(txid22, function(err, tx) {
|
|
||||||
if (err && err.message.match(/must.be.hexadecimal/)) {
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var txid23 = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca227';
|
|
||||||
it('test unexisting TX ' + txid23, function(done) {
|
|
||||||
|
|
||||||
Transaction.fromIdWithInfo(txid23, function(err, tx) {
|
|
||||||
assert(!err);
|
|
||||||
assert(!tx);
|
|
||||||
return done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608';
|
|
||||||
it('create TX on the fly ' + txid2, function(done) {
|
|
||||||
Transaction.fromIdWithInfo(txid2, function(err, tx) {
|
|
||||||
if (err) return done(err);
|
|
||||||
assert.equal(tx.info.txid, txid2);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608';
|
|
||||||
it('test a broken TX ' + txid2, function(done) {
|
|
||||||
Transaction.fromIdWithInfo(txid2, function(err, tx) {
|
|
||||||
if (err) return done(err);
|
|
||||||
assert.equal(tx.info.txid, txid2);
|
|
||||||
assert.equal(tx.info.vin[0].addr, 'n1JagbRWBDi6VMvG7HfZmXX74dB9eiHJzU');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var mongoose = require('mongoose'),
|
|
||||||
assert = require('assert'),
|
|
||||||
fs = require('fs'),
|
|
||||||
util = require('util'),
|
|
||||||
async = require('async'),
|
|
||||||
config = require('../../config/config'),
|
|
||||||
TransactionOut = require('../../app/models/TransactionOut');
|
|
||||||
|
|
||||||
var spentValid = JSON.parse(fs.readFileSync('test/model/spent.json'));
|
|
||||||
|
|
||||||
mongoose.connection.on('error', function(err) { console.log(err); });
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe('TransactionOut Expenses', function(){
|
|
||||||
|
|
||||||
before(function(done) {
|
|
||||||
mongoose.connect(config.db);
|
|
||||||
|
|
||||||
// lets spend!
|
|
||||||
async.each(Object.keys(spentValid),
|
|
||||||
function(txid,c_out) {
|
|
||||||
async.each(spentValid[txid],
|
|
||||||
function(i,c_in) {
|
|
||||||
TransactionOut._explodeTransactionOuts(i.txid, function(err) {
|
|
||||||
return c_in();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
return c_out();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
console.log('[transactionouts.js.88]'); //TODO
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
after(function(done) {
|
|
||||||
mongoose.connection.close();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(spentValid).forEach( function(txid) {
|
|
||||||
it('test result of spending tx ' + txid, function(done) {
|
|
||||||
var s = spentValid[txid];
|
|
||||||
var c=0;
|
|
||||||
TransactionOut.fromTxId( txid, function(err, readItems) {
|
|
||||||
s.forEach( function(v) {
|
|
||||||
assert.equal(readItems[c].spendTxIdBuf.toString('hex'),v.txid);
|
|
||||||
assert.equal(readItems[c].spendIndex,v.n);
|
|
||||||
c++;
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -6,7 +6,8 @@
|
||||||
{
|
{
|
||||||
"txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237",
|
"txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237",
|
||||||
"toRm": [
|
"toRm": [
|
||||||
"txouts-spend-86a03cac7d87f596008c6d5a8d3fd8b88842932ea6f0337673eda16f6b472f7f-0"
|
"txouts-spend-86a03cac7d87f596008c6d5a8d3fd8b88842932ea6f0337673eda16f6b472f7f-0",
|
||||||
|
"txouts-spend-bcd8da8ee847da377f8aaca92502c05e5f914c6a2452753146013b0e642a25a0-0"
|
||||||
],
|
],
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue