Merge pull request #325 from maraoz/bug/fix-address-socket
Bug/fix address socket
This commit is contained in:
commit
ab62abf8f5
|
@ -41,7 +41,6 @@ module.exports.broadcastBlock = function(block) {
|
|||
};
|
||||
|
||||
module.exports.broadcastAddressTx = function(address, tx) {
|
||||
console.log('bcatx = '+address+' '+tx);
|
||||
if (ios) ios.sockets. in (address).emit(address, tx);
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ require('classtool');
|
|||
|
||||
function spec(b) {
|
||||
|
||||
var superclass = b.superclass || require('events').EventEmitter;
|
||||
var TIMESTAMP_PREFIX = 'bts-'; // b-ts-<ts> => <hash>
|
||||
var PREV_PREFIX = 'bpr-'; // b-prev-<hash> => <prev_hash>
|
||||
var NEXT_PREFIX = 'bne-'; // b-next-<hash> => <next_hash>
|
||||
|
@ -22,7 +23,9 @@ function spec(b) {
|
|||
var Rpc = b.rpc || require('./Rpc').class();
|
||||
|
||||
var BlockDb = function() {
|
||||
BlockDb.super(this, arguments);
|
||||
};
|
||||
BlockDb.superclass = superclass;
|
||||
|
||||
BlockDb.prototype.close = function(cb) {
|
||||
db.close(cb);
|
||||
|
@ -42,6 +45,7 @@ function spec(b) {
|
|||
// the block prev to the new block, nor TIP pointer
|
||||
//
|
||||
BlockDb.prototype.add = function(b, cb) {
|
||||
var self = this;
|
||||
var time_key = TIMESTAMP_PREFIX +
|
||||
( b.time || Math.round(new Date().getTime() / 1000) );
|
||||
|
||||
|
@ -49,7 +53,12 @@ function spec(b) {
|
|||
.put(time_key, b.hash)
|
||||
.put(MAIN_PREFIX + b.hash, 1)
|
||||
.put(PREV_PREFIX + b.hash, b.previousblockhash)
|
||||
.write(cb);
|
||||
.write(function(err){
|
||||
if (!err) {
|
||||
self.emit('new_block', {blockid: b.hash});
|
||||
}
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
BlockDb.prototype.getTip = function(cb) {
|
||||
|
|
|
@ -6,12 +6,12 @@ function spec() {
|
|||
var CoinConst = require('bitcore/const');
|
||||
var coinUtil = require('bitcore/util/util');
|
||||
var Sync = require('./Sync').class();
|
||||
var Script = require('bitcore/Script').class();
|
||||
var Peer = require('bitcore/Peer').class();
|
||||
var config = require('../config/config');
|
||||
var networks = require('bitcore/networks');
|
||||
|
||||
var peerdb_fn = 'peerdb.json';
|
||||
|
||||
function PeerSync() {}
|
||||
|
||||
|
||||
|
@ -52,32 +52,16 @@ function spec() {
|
|||
|
||||
PeerSync.prototype.handleInv = function(info) {
|
||||
var invs = info.message.invs;
|
||||
invs.forEach(function(inv) {
|
||||
console.log('[p2p_sync] Handle inv for a ' + CoinConst.MSG.to_str(inv.type));
|
||||
});
|
||||
// TODO: should limit the invs to objects we haven't seen yet
|
||||
info.conn.sendGetData(invs);
|
||||
};
|
||||
|
||||
PeerSync.prototype.handleTx = function(info) {
|
||||
var self =this;
|
||||
var tx = info.message.tx.getStandardizedObject();
|
||||
tx.outs = info.message.tx.outs;
|
||||
tx.ins = info.message.tx.ins;
|
||||
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) {
|
||||
if (err) {
|
||||
console.log('[p2p_sync] Error in handle TX: ' + JSON.stringify(err));
|
||||
|
@ -110,7 +94,7 @@ function spec() {
|
|||
PeerSync.prototype.handle_connected = function(data) {
|
||||
var peerman = data.pm;
|
||||
var peers_n = peerman.peers.length;
|
||||
console.log('[p2p_sync] Connected to ' + peers_n + ' peer' + (peers_n !== 1 ? 's': ''));
|
||||
console.log('[p2p_sync] Connected to ' + peers_n + ' peer' + (peers_n !== 1 ? 's' : ''));
|
||||
};
|
||||
|
||||
PeerSync.prototype.run = function() {
|
||||
|
@ -145,4 +129,3 @@ function spec() {
|
|||
|
||||
}
|
||||
module.defineClass(spec);
|
||||
|
||||
|
|
132
lib/Sync.js
132
lib/Sync.js
|
@ -6,29 +6,25 @@ require('classtool');
|
|||
function spec() {
|
||||
var sockets = require('../app/controllers/socket.js');
|
||||
var BlockDb = require('./BlockDb').class();
|
||||
var bitutil = require('bitcore/util/util');
|
||||
|
||||
// This is 0.1.2 => c++ version of base57-native
|
||||
var base58 = require('base58-native');
|
||||
var encodedData = require('bitcore/util/EncodedData').class({base58: base58});
|
||||
var versionedData = require('bitcore/util/VersionedData').class({superclass: encodedData});
|
||||
var Address = require('bitcore/Address').class({superclass: versionedData});
|
||||
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');
|
||||
|
||||
|
||||
function Sync() {
|
||||
}
|
||||
function Sync() {}
|
||||
|
||||
Sync.prototype.init = function(opts, cb) {
|
||||
var self = this;
|
||||
self.opts = opts;
|
||||
this.bDb = new BlockDb(opts);
|
||||
this.txDb = new TransactionDb(opts);
|
||||
this.network = config.network === 'testnet' ? networks.testnet: networks.livenet;
|
||||
this.txDb.on('tx_for_address', this.handleTxForAddress.bind(this));
|
||||
this.txDb.on('new_tx', this.handleNewTx.bind(this));
|
||||
this.bDb.on('new_block', this.handleNewBlock.bind(this));
|
||||
|
||||
this.network = config.network === 'testnet' ? networks.testnet : networks.livenet;
|
||||
return cb();
|
||||
};
|
||||
|
||||
|
@ -43,8 +39,13 @@ function spec() {
|
|||
Sync.prototype.destroy = function(next) {
|
||||
var self = this;
|
||||
async.series([
|
||||
function(b) { self.bDb.drop(b); },
|
||||
function(b) { self.txDb.drop(b); },
|
||||
|
||||
function(b) {
|
||||
self.bDb.drop(b);
|
||||
},
|
||||
function(b) {
|
||||
self.txDb.drop(b);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
|
@ -88,13 +89,13 @@ function spec() {
|
|||
var self = this;
|
||||
var oldTip, oldNext, needReorg = false;
|
||||
var newPrev = b.previousblockhash;
|
||||
var updatedAddrs;
|
||||
|
||||
async.series([
|
||||
|
||||
function(c) {
|
||||
self.bDb.has(b.hash, function(err, val) {
|
||||
return c(err ||
|
||||
(val ? new Error('WARN: Ignoring already existing block:' + b.hash) : null ));
|
||||
(val ? new Error('WARN: Ignoring already existing block:' + b.hash) : null));
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
|
@ -103,12 +104,11 @@ function spec() {
|
|||
self.bDb.has(newPrev, function(err, val) {
|
||||
if (!val && newPrev.match(/^0+$/)) return c();
|
||||
return c(err ||
|
||||
(!val ? new Error('WARN: Ignoring block with non existing prev:' + b.hash) : null ));
|
||||
(!val ? new Error('WARN: Ignoring block with non existing prev:' + b.hash) : null));
|
||||
});
|
||||
},
|
||||
function(c) {
|
||||
self.txDb.createFromBlock(b, function(err, addrs) {
|
||||
updatedAddrs = addrs;
|
||||
self.txDb.createFromBlock(b, function(err) {
|
||||
return c(err);
|
||||
});
|
||||
},
|
||||
|
@ -123,7 +123,7 @@ function spec() {
|
|||
function(c) {
|
||||
if (!needReorg) return c();
|
||||
|
||||
self.bDb.getNext( newPrev, function(err, val) {
|
||||
self.bDb.getNext(newPrev, function(err, val) {
|
||||
if (err) return c(err);
|
||||
oldNext = val;
|
||||
return c();
|
||||
|
@ -144,11 +144,11 @@ function spec() {
|
|||
return c(err);
|
||||
});
|
||||
});
|
||||
}],
|
||||
}
|
||||
],
|
||||
function(err) {
|
||||
if (!err) self._handleBroadcast(b.hash, null, updatedAddrs);
|
||||
if (err && err.toString().match(/WARN/) ) {
|
||||
err=null;
|
||||
if (err && err.toString().match(/WARN/)) {
|
||||
err = null;
|
||||
}
|
||||
return cb(err);
|
||||
});
|
||||
|
@ -162,8 +162,9 @@ function spec() {
|
|||
var orphanizeFrom;
|
||||
|
||||
async.series([
|
||||
|
||||
function(c) {
|
||||
self.bDb.isMain(newPrev, function(err,val) {
|
||||
self.bDb.isMain(newPrev, function(err, val) {
|
||||
if (!val) return c();
|
||||
|
||||
console.log('# Reorg Case 1)');
|
||||
|
@ -211,11 +212,13 @@ function spec() {
|
|||
hashInterator = fromHash;
|
||||
|
||||
async.whilst(
|
||||
function() { return hashInterator; },
|
||||
function() {
|
||||
return hashInterator;
|
||||
},
|
||||
function(c) {
|
||||
self.setBlockMain(hashInterator, false, function(err) {
|
||||
if (err) return cb(err);
|
||||
self.bDb.getNext(hashInterator, function (err, val) {
|
||||
self.bDb.getNext(hashInterator, function(err, val) {
|
||||
hashInterator = val;
|
||||
return c(err);
|
||||
});
|
||||
|
@ -231,20 +234,22 @@ function spec() {
|
|||
|
||||
async.doWhilst(
|
||||
function(c) {
|
||||
self.setBlockMain(hashInterator, true, function (err) {
|
||||
self.setBlockMain(hashInterator, true, function(err) {
|
||||
if (err) return c(err);
|
||||
self.bDb.getPrev(hashInterator, function (err, val) {
|
||||
self.bDb.getPrev(hashInterator, function(err, val) {
|
||||
if (err) return c(err);
|
||||
lastHash = hashInterator;
|
||||
hashInterator = val;
|
||||
self.bDb.isMain(hashInterator, function (err, val) {
|
||||
self.bDb.isMain(hashInterator, function(err, val) {
|
||||
isMain = val;
|
||||
return c();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
function() { return hashInterator && !isMain; },
|
||||
function() {
|
||||
return hashInterator && !isMain;
|
||||
},
|
||||
function(err) {
|
||||
console.log('\tFound yBlock:', hashInterator);
|
||||
return cb(err, hashInterator, lastHash);
|
||||
|
@ -252,79 +257,34 @@ function spec() {
|
|||
);
|
||||
};
|
||||
|
||||
Sync.prototype._handleBroadcast = function(hash, updatedTxs, updatedAddrs) {
|
||||
var self = this;
|
||||
|
||||
if (self.opts.shouldBroadcast) {
|
||||
if (hash) {
|
||||
sockets.broadcastBlock(hash);
|
||||
Sync.prototype.handleTxForAddress = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastAddressTx(data.address, data.txid);
|
||||
}
|
||||
};
|
||||
|
||||
if (updatedTxs) {
|
||||
updatedTxs.forEach(function(tx) {
|
||||
sockets.broadcastTx(tx);
|
||||
});
|
||||
Sync.prototype.handleNewTx = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastTx(data.tx);
|
||||
}
|
||||
};
|
||||
|
||||
if (updatedAddrs ) {
|
||||
updatedAddrs.forEach(function(addr, txs){
|
||||
txs.forEach(function(addr, t){
|
||||
sockets.broadcastAddressTx(addr, t);
|
||||
});
|
||||
});
|
||||
}
|
||||
Sync.prototype.handleNewBlock = function(data) {
|
||||
if (this.opts.shouldBroadcast) {
|
||||
sockets.broadcastBlock(data.blockid);
|
||||
}
|
||||
};
|
||||
|
||||
Sync.prototype.storeTxs = function(txs, cb) {
|
||||
var self = this;
|
||||
|
||||
self.txDb.createFromArray(txs, null, function(err, updatedAddrs) {
|
||||
self.txDb.createFromArray(txs, null, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self._handleBroadcast(null, txs, updatedAddrs);
|
||||
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;
|
||||
}
|
||||
module.defineClass(spec);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ require('classtool');
|
|||
|
||||
function spec(b) {
|
||||
|
||||
var superclass = b.superclass || require('events').EventEmitter;
|
||||
// blockHash -> txid mapping
|
||||
var IN_BLK_PREFIX = 'txb-'; //txb-<txid>-<block> => 1/0 (connected or not)
|
||||
|
||||
|
@ -32,9 +33,27 @@ function spec(b) {
|
|||
config = require('../config/config'),
|
||||
assert = require('assert');
|
||||
var db = b.db || levelup(config.leveldb + '/txs');
|
||||
var Script = require('bitcore/Script').class();
|
||||
// This is 0.1.2 => c++ version of base57-native
|
||||
var base58 = require('base58-native');
|
||||
var encodedData = require('bitcore/util/EncodedData').class({
|
||||
// TODO: check why c++ implementation differs
|
||||
//base58: base58
|
||||
});
|
||||
var versionedData = require('bitcore/util/VersionedData').class({
|
||||
superclass: encodedData
|
||||
});
|
||||
var Address = require('bitcore/Address').class({
|
||||
superclass: versionedData
|
||||
});
|
||||
var bitutil = require('bitcore/util/util');
|
||||
var networks = require('bitcore/networks');
|
||||
|
||||
var TransactionDb = function() {
|
||||
TransactionDb.super(this, arguments);
|
||||
this.network = config.network === 'testnet' ? networks.testnet : networks.livenet;
|
||||
};
|
||||
TransactionDb.superclass = superclass;
|
||||
|
||||
TransactionDb.prototype.close = function(cb) {
|
||||
db.close(cb);
|
||||
|
@ -43,7 +62,7 @@ function spec(b) {
|
|||
TransactionDb.prototype.drop = function(cb) {
|
||||
var path = config.leveldb + '/txs';
|
||||
db.close(function() {
|
||||
require('leveldown').destroy(path, function () {
|
||||
require('leveldown').destroy(path, function() {
|
||||
db = levelup(path);
|
||||
return cb();
|
||||
});
|
||||
|
@ -54,7 +73,7 @@ function spec(b) {
|
|||
TransactionDb.prototype.has = function(txid, cb) {
|
||||
|
||||
var k = OUTS_PREFIX + txid;
|
||||
db.get(k, function (err,val) {
|
||||
db.get(k, function(err, val) {
|
||||
|
||||
var ret;
|
||||
|
||||
|
@ -81,8 +100,7 @@ function spec(b) {
|
|||
txid: txid,
|
||||
index: parseInt(index),
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
r.spendTxId = txid;
|
||||
r.spendIndex = parseInt(index);
|
||||
r.spendTs = parseInt(ts);
|
||||
|
@ -94,13 +112,16 @@ function spec(b) {
|
|||
TransactionDb.prototype.fromTxId = function(txid, cb) {
|
||||
var self = this;
|
||||
var k = OUTS_PREFIX + txid;
|
||||
var ret=[];
|
||||
var idx={};
|
||||
var ret = [];
|
||||
var idx = {};
|
||||
var i = 0;
|
||||
|
||||
// outs.
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
var k = data.key.split('-');
|
||||
var v = data.value.split(':');
|
||||
ret.push({
|
||||
|
@ -108,28 +129,31 @@ function spec(b) {
|
|||
value_sat: parseInt(v[1]),
|
||||
index: parseInt(k[2]),
|
||||
});
|
||||
idx[parseInt(k[2])]= i++;
|
||||
idx[parseInt(k[2])] = i++;
|
||||
})
|
||||
.on('error', function (err) {
|
||||
.on('error', function(err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function () {
|
||||
.on('end', function() {
|
||||
|
||||
var k = SPEND_PREFIX + txid;
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
var k = data.key.split('-');
|
||||
var j = idx[parseInt(k[2])];
|
||||
|
||||
assert(typeof j !== 'undefined','Spent could not be stored: tx ' + txid +
|
||||
'spend in TX:' + k[1] + ',' + k[2]+ ' j:' + j);
|
||||
assert(typeof j !== 'undefined', 'Spent could not be stored: tx ' + txid +
|
||||
'spend in TX:' + k[1] + ',' + k[2] + ' j:' + j);
|
||||
|
||||
self._addSpendInfo(ret[j], k[3], k[4], data.value);
|
||||
})
|
||||
.on('error', function (err) {
|
||||
.on('error', function(err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function (err) {
|
||||
.on('end', function(err) {
|
||||
return cb(err, ret);
|
||||
});
|
||||
});
|
||||
|
@ -142,15 +166,18 @@ function spec(b) {
|
|||
if (!info) return cb();
|
||||
|
||||
var k = SPEND_PREFIX + info.txid;
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
var k = data.key.split('-');
|
||||
self._addSpendInfo(info.vout[k[2]], k[3], k[4], data.value);
|
||||
})
|
||||
.on('error', function (err) {
|
||||
.on('error', function(err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function (err) {
|
||||
.on('end', function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
@ -167,7 +194,7 @@ function spec(b) {
|
|||
async.eachLimit(info.vin, CONCURRENCY, function(i, c_in) {
|
||||
self.fromTxIdN(i.txid, i.vout, function(err, ret) {
|
||||
//console.log('[TransactionDb.js.154:ret:]',ret); //TODO
|
||||
if (!ret || !ret.addr || !ret.valueSat ) {
|
||||
if (!ret || !ret.addr || !ret.valueSat) {
|
||||
console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid);
|
||||
if (ret) i.unconfirmedInput = ret.unconfirmedInput;
|
||||
incompleteInputs = 1;
|
||||
|
@ -181,27 +208,23 @@ function spec(b) {
|
|||
i.value = ret.valueSat / util.COIN;
|
||||
|
||||
// Double spend?
|
||||
if ( ret.multipleSpendAttempt ||
|
||||
!ret.spendTxId ||
|
||||
if (ret.multipleSpendAttempt || !ret.spendTxId ||
|
||||
(ret.spendTxId && ret.spendTxId !== info.txid)
|
||||
) {
|
||||
if (ret.multipleSpendAttempts ) {
|
||||
if (ret.multipleSpendAttempts) {
|
||||
ret.multipleSpendAttempts.each(function(mul) {
|
||||
if (mul.spendTxId !== info.txid) {
|
||||
i.doubleSpendTxID = ret.spendTxId;
|
||||
i.doubleSpendIndex = ret.spendIndex;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (!ret.spendTxId) {
|
||||
} else if (!ret.spendTxId) {
|
||||
i.dbError = 'Input spend not registered';
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
i.doubleSpendTxID = ret.spendTxId;
|
||||
i.doubleSpendIndex = ret.spendIndex;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
i.doubleSpendTxID = null;
|
||||
}
|
||||
|
||||
|
@ -209,12 +232,11 @@ function spec(b) {
|
|||
return c_in();
|
||||
});
|
||||
},
|
||||
function () {
|
||||
if (! incompleteInputs ) {
|
||||
function() {
|
||||
if (!incompleteInputs) {
|
||||
info.valueIn = valueIn / util.COIN;
|
||||
info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN ;
|
||||
}
|
||||
else {
|
||||
info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN;
|
||||
} else {
|
||||
info.incompleteInputs = 1;
|
||||
}
|
||||
return cb();
|
||||
|
@ -236,13 +258,16 @@ function spec(b) {
|
|||
};
|
||||
|
||||
|
||||
TransactionDb.prototype.fromIdWithInfo = function (txid, cb) {
|
||||
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} );
|
||||
if (!info) return cb();
|
||||
return cb(err, {
|
||||
txid: txid,
|
||||
info: info
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -250,9 +275,11 @@ function spec(b) {
|
|||
var self = this;
|
||||
var k = OUTS_PREFIX + txid + '-' + n;
|
||||
|
||||
db.get(k, function (err,val) {
|
||||
if (!val || (err && err.notFound) ) {
|
||||
return cb(null, { unconfirmedInput: 1} );
|
||||
db.get(k, function(err, val) {
|
||||
if (!val || (err && err.notFound)) {
|
||||
return cb(null, {
|
||||
unconfirmedInput: 1
|
||||
});
|
||||
}
|
||||
|
||||
var a = val.split(':');
|
||||
|
@ -263,16 +290,19 @@ function spec(b) {
|
|||
|
||||
// Spend?
|
||||
var k = SPEND_PREFIX + txid + '-' + n;
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
var k = data.key.split('-');
|
||||
self._addSpendInfo(ret, k[3], k[4], data.value);
|
||||
})
|
||||
.on('error', function (error) {
|
||||
.on('error', function(error) {
|
||||
return cb(error);
|
||||
})
|
||||
.on('end', function () {
|
||||
return cb(null,ret);
|
||||
.on('end', function() {
|
||||
return cb(null, ret);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -280,7 +310,7 @@ function spec(b) {
|
|||
TransactionDb.prototype.fillConfirmations = function(o, cb) {
|
||||
var self = this;
|
||||
|
||||
self.isConfirmed(o.txid, function(err,is) {
|
||||
self.isConfirmed(o.txid, function(err, is) {
|
||||
if (err) return cb(err);
|
||||
|
||||
o.isConfirmed = is;
|
||||
|
@ -289,8 +319,8 @@ function spec(b) {
|
|||
if (o.multipleSpendAttempts) {
|
||||
|
||||
async.each(o.multipleSpendAttempts,
|
||||
function (oi, e_c) {
|
||||
self.isConfirmed(oi.spendTxId, function(err,is) {
|
||||
function(oi, e_c) {
|
||||
self.isConfirmed(oi.spendTxId, function(err, is) {
|
||||
if (err) return;
|
||||
if (is) {
|
||||
o.spendTxId = oi.spendTxId;
|
||||
|
@ -300,9 +330,8 @@ function spec(b) {
|
|||
return e_c();
|
||||
});
|
||||
}, cb);
|
||||
}
|
||||
else {
|
||||
self.isConfirmed(o.spendTxId, function(err,is) {
|
||||
} else {
|
||||
self.isConfirmed(o.spendTxId, function(err, is) {
|
||||
if (err) return cb(err);
|
||||
o.spendIsConfirmed = is;
|
||||
return cb();
|
||||
|
@ -315,10 +344,13 @@ function spec(b) {
|
|||
var self = this;
|
||||
|
||||
var k = ADDR_PREFIX + addr;
|
||||
var ret=[];
|
||||
var ret = [];
|
||||
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
var k = data.key.split('-');
|
||||
var v = data.value.split(':');
|
||||
ret.push({
|
||||
|
@ -328,30 +360,33 @@ function spec(b) {
|
|||
ts: parseInt(v[1]),
|
||||
});
|
||||
})
|
||||
.on('error', function (err) {
|
||||
.on('error', function(err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function () {
|
||||
.on('end', function() {
|
||||
|
||||
async.each(ret, function(o, e_c) {
|
||||
var k = SPEND_PREFIX + o.txid + '-' + o.index;
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
var k = data.key.split('-');
|
||||
self._addSpendInfo(o, k[3], k[4], data.value);
|
||||
})
|
||||
.on('error', function (err) {
|
||||
.on('error', function(err) {
|
||||
return e_c(err);
|
||||
})
|
||||
.on('end', function (err) {
|
||||
.on('end', function(err) {
|
||||
return e_c(err);
|
||||
});
|
||||
},
|
||||
function() {
|
||||
async.each(ret, function(o, e_c){
|
||||
self.fillConfirmations(o,e_c);
|
||||
},function(err) {
|
||||
return cb(err,ret);
|
||||
async.each(ret, function(o, e_c) {
|
||||
self.fillConfirmations(o, e_c);
|
||||
}, function(err) {
|
||||
return cb(err, ret);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -362,12 +397,15 @@ function spec(b) {
|
|||
TransactionDb.prototype.removeFromTxId = function(txid, cb) {
|
||||
|
||||
async.series([
|
||||
|
||||
function(c) {
|
||||
db.createReadStream({
|
||||
start: OUTS_PREFIX + txid,
|
||||
end: OUTS_PREFIX + txid + '~',
|
||||
}).pipe(
|
||||
db.createWriteStream({type:'del'})
|
||||
db.createWriteStream({
|
||||
type: 'del'
|
||||
})
|
||||
).on('close', c);
|
||||
},
|
||||
function(c) {
|
||||
|
@ -376,9 +414,12 @@ function spec(b) {
|
|||
end: SPEND_PREFIX + txid + '~'
|
||||
})
|
||||
.pipe(
|
||||
db.createWriteStream({type:'del'})
|
||||
db.createWriteStream({
|
||||
type: 'del'
|
||||
})
|
||||
).on('close', c);
|
||||
}],
|
||||
}
|
||||
],
|
||||
function(err) {
|
||||
cb(err);
|
||||
});
|
||||
|
@ -386,22 +427,72 @@ function spec(b) {
|
|||
};
|
||||
|
||||
|
||||
// TODO. replace with
|
||||
// Script.prototype.getAddrStrs if that one get merged in bitcore
|
||||
TransactionDb.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.push(addr.toString());
|
||||
break;
|
||||
case Script.TX_PUBKEYHASH:
|
||||
addr = new Address(self.network.addressPubkey, s.captureOne());
|
||||
addrStrs.push(addr.toString());
|
||||
break;
|
||||
case Script.TX_SCRIPTHASH:
|
||||
addr = new Address(self.network.addressScript, s.captureOne());
|
||||
addrStrs.push(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;
|
||||
};
|
||||
|
||||
TransactionDb.prototype.adaptTxObject = function(txInfo) {
|
||||
|
||||
var self = this;
|
||||
// adapt bitcore TX object to bitcoind JSON response
|
||||
txInfo.txid = txInfo.hash;
|
||||
|
||||
|
||||
var to=0;
|
||||
var tx = txInfo;
|
||||
tx.outs.forEach( function(o) {
|
||||
var s = new Script(o.s);
|
||||
var addrs = self.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++;
|
||||
});
|
||||
|
||||
var count = 0;
|
||||
txInfo.vin = txInfo.in.map(function (txin) {
|
||||
txInfo.vin = txInfo.in.map(function(txin) {
|
||||
var i = {};
|
||||
|
||||
if (txin.coinbase) {
|
||||
txInfo.isCoinBase = true;
|
||||
}
|
||||
else {
|
||||
i.txid= txin.prev_out.hash;
|
||||
i.vout= txin.prev_out.n;
|
||||
} else {
|
||||
i.txid = txin.prev_out.hash;
|
||||
i.vout = txin.prev_out.n;
|
||||
}
|
||||
i.n = count++;
|
||||
return i;
|
||||
|
@ -409,13 +500,13 @@ function spec(b) {
|
|||
|
||||
|
||||
count = 0;
|
||||
txInfo.vout = txInfo.out.map(function (txout) {
|
||||
txInfo.vout = txInfo.out.map(function(txout) {
|
||||
var o = {};
|
||||
|
||||
o.value = txout.value;
|
||||
o.n = count++;
|
||||
|
||||
if (txout.addrStr){
|
||||
if (txout.addrStr) {
|
||||
o.scriptPubKey = {};
|
||||
o.scriptPubKey.addresses = [txout.addrStr];
|
||||
}
|
||||
|
@ -434,17 +525,17 @@ function spec(b) {
|
|||
var ts = tx.time;
|
||||
|
||||
async.series([
|
||||
// Input Outpoints (mark them as spended)
|
||||
// Input Outpoints (mark them as spent)
|
||||
function(p_c) {
|
||||
if (tx.isCoinBase) return p_c();
|
||||
async.forEachLimit(tx.vin, CONCURRENCY,
|
||||
function(i, next_out) {
|
||||
db.batch()
|
||||
.put( SPEND_PREFIX + i.txid + '-' + i.vout + '-' + tx.txid + '-' + i.n,
|
||||
.put(SPEND_PREFIX + i.txid + '-' + i.vout + '-' + tx.txid + '-' + i.n,
|
||||
ts || 0)
|
||||
.write(next_out);
|
||||
},
|
||||
function (err) {
|
||||
function(err) {
|
||||
return p_c(err);
|
||||
});
|
||||
},
|
||||
|
@ -454,37 +545,32 @@ function spec(b) {
|
|||
function(o, next_out) {
|
||||
if (o.value && o.scriptPubKey &&
|
||||
o.scriptPubKey.addresses &&
|
||||
o.scriptPubKey.addresses[0] &&
|
||||
! o.scriptPubKey.addresses[1] // TODO : not supported
|
||||
){
|
||||
// This is only to broadcast (WIP)
|
||||
if (addrs.indexOf(o.scriptPubKey.addresses[0]) === -1) {
|
||||
addrs.push(o.scriptPubKey.addresses[0]);
|
||||
}
|
||||
|
||||
o.scriptPubKey.addresses[0] && !o.scriptPubKey.addresses[1] // TODO : not supported
|
||||
) {
|
||||
var addr = o.scriptPubKey.addresses[0];
|
||||
var sat = Math.round(o.value * util.COIN);
|
||||
|
||||
if (addrs.indexOf(addr) === -1) {
|
||||
addrs.push(addr);
|
||||
}
|
||||
|
||||
// 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)
|
||||
.put(k, addr + ':' + sat)
|
||||
.put(ADDR_PREFIX + addr + '-' + tx.txid + '-' + o.n, sat + ':' + ts)
|
||||
.write(next_out);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return next_out();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
//console.log ('WARN in TX: %s could not parse OUTPUT %d', tx.txid, o.n);
|
||||
} else {
|
||||
return next_out();
|
||||
}
|
||||
},
|
||||
function (err) {
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.log('ERR at TX %s: %s', tx.txid, err);
|
||||
return cb(err);
|
||||
|
@ -492,12 +578,27 @@ function spec(b) {
|
|||
return p_c();
|
||||
});
|
||||
},
|
||||
function (p_c) {
|
||||
if (!blockhash) return p_c();
|
||||
return self.setConfirmation(tx.txid,blockhash, true, p_c);
|
||||
function(p_c) {
|
||||
if (!blockhash) {
|
||||
return p_c();
|
||||
}
|
||||
return self.setConfirmation(tx.txid, blockhash, true, p_c);
|
||||
},
|
||||
], function(err) {
|
||||
return cb(err, addrs);
|
||||
if (addrs.length > 0 && !blockhash) {
|
||||
// only emit if we are processing a single tx (not from a block)
|
||||
addrs.forEach(function(addr) {
|
||||
self.emit('tx_for_address', {
|
||||
address: addr,
|
||||
txid: tx.txid
|
||||
});
|
||||
});
|
||||
}
|
||||
self.emit('new_tx', {
|
||||
tx: tx
|
||||
});
|
||||
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -520,15 +621,18 @@ function spec(b) {
|
|||
var k = IN_BLK_PREFIX + txId;
|
||||
var ret = false;
|
||||
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
if (data.value === '1') ret = true;
|
||||
})
|
||||
.on('error', function (err) {
|
||||
.on('error', function(err) {
|
||||
return c(err);
|
||||
})
|
||||
.on('end', function (err) {
|
||||
return c(err,ret);
|
||||
.on('end', function(err) {
|
||||
return c(err, ret);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -539,21 +643,24 @@ function spec(b) {
|
|||
var k = FROM_BLK_PREFIX + hash;
|
||||
var k2 = IN_BLK_PREFIX;
|
||||
// This is slow, but prevent us to create a new block->tx index.
|
||||
db.createReadStream({start: k, end: k + '~'})
|
||||
.on('data', function (data) {
|
||||
db.createReadStream({
|
||||
start: k,
|
||||
end: k + '~'
|
||||
})
|
||||
.on('data', function(data) {
|
||||
var ks = data.key.split('-');
|
||||
toChange.push({
|
||||
key: k2 + ks[2] + '-' + ks[1],
|
||||
type: 'put',
|
||||
value: isMain?1:0,
|
||||
value: isMain ? 1 : 0,
|
||||
});
|
||||
})
|
||||
.on('error', function (err) {
|
||||
.on('error', function(err) {
|
||||
return cb(err);
|
||||
})
|
||||
.on('end', function (err) {
|
||||
.on('end', function(err) {
|
||||
if (err) return cb(err);
|
||||
console.log('\t%s %d Txs', isMain?'Confirming':'Invalidating',toChange.length);
|
||||
console.log('\t%s %d Txs', isMain ? 'Confirming' : 'Invalidating', toChange.length);
|
||||
db.batch(toChange, cb);
|
||||
});
|
||||
};
|
||||
|
@ -561,15 +668,10 @@ function spec(b) {
|
|||
// txs can be a [hashes] or [txObjects]
|
||||
TransactionDb.prototype.createFromArray = function(txs, blockHash, next) {
|
||||
var self = this;
|
||||
|
||||
if (!txs) return next();
|
||||
|
||||
var updatedAddrs = []; // TODO
|
||||
|
||||
async.forEachLimit(txs, CONCURRENCY, function(t, each_cb) {
|
||||
if (typeof t === 'string') {
|
||||
|
||||
// Is it from genesis block? (testnet==livenet)
|
||||
// TODO: parse it from networks.genesisTX?
|
||||
if (t === genesisTXID) return each_cb();
|
||||
|
||||
|
@ -578,13 +680,12 @@ function spec(b) {
|
|||
|
||||
return self.add(inInfo, blockHash, each_cb);
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return self.add(t, blockHash, each_cb);
|
||||
}
|
||||
},
|
||||
function(err) {
|
||||
return next(err, updatedAddrs);
|
||||
return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -598,10 +699,10 @@ function spec(b) {
|
|||
|
||||
|
||||
TransactionDb.prototype.setOrphan = function(blockHash, next) {
|
||||
// var self = this;
|
||||
// var self = this;
|
||||
|
||||
//Get Txs
|
||||
// TODO
|
||||
// TODO
|
||||
|
||||
//Mark Tx's output as fromOrphan
|
||||
//Mark Tx's outpoiunt as fromOrphan. Undo spents
|
||||
|
|
Loading…
Reference in New Issue