Merge pull request #220 from matiu/feature/handle-double-spends
Feature/handle double spends
This commit is contained in:
commit
3c317d700b
@ -139,6 +139,8 @@ function spec(b) {
|
|||||||
|
|
||||||
|
|
||||||
BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
|
BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
rpc.getBlock(hash, function(err, info) {
|
rpc.getBlock(hash, function(err, info) {
|
||||||
// Not found?
|
// Not found?
|
||||||
if (err && err.code === -5) return cb();
|
if (err && err.code === -5) return cb();
|
||||||
@ -146,9 +148,16 @@ function spec(b) {
|
|||||||
|
|
||||||
if (info.result.height)
|
if (info.result.height)
|
||||||
info.result.reward = BitcoreBlock.getBlockValue(info.result.height) / util.COIN ;
|
info.result.reward = BitcoreBlock.getBlockValue(info.result.height) / util.COIN ;
|
||||||
return cb(null, {
|
|
||||||
hash: hash,
|
self.isMain(hash, function(err, val) {
|
||||||
info: info.result,
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
info.result.isMainChain = val ? true : false;
|
||||||
|
|
||||||
|
return cb(null, {
|
||||||
|
hash: hash,
|
||||||
|
info: info.result,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ function spec(b) {
|
|||||||
|
|
||||||
// to sum up addr balance
|
// to sum up addr balance
|
||||||
var ADDR_PREFIX = 'txouts-addr-'; //txouts-addr-<addr>-<ts>-<txid>-<n> => + btc_sat
|
var ADDR_PREFIX = 'txouts-addr-'; //txouts-addr-<addr>-<ts>-<txid>-<n> => + btc_sat
|
||||||
var SPEND_PREFIX = 'txouts-spend-';//txouts-spend-<txid(out)>-<n(out)> => [txid(in),n(in),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';
|
||||||
@ -70,10 +70,33 @@ function spec(b) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionDb.prototype.fromTxId = function(txid, cb) {
|
TransactionDb.prototype._addSpendInfo = function(r, txid, index) {
|
||||||
|
if (r.spendTxId) {
|
||||||
|
if (!r.multipleSpendAttempts) {
|
||||||
|
r.multipleSpendAttempts = [{
|
||||||
|
txid: r.spendTxId,
|
||||||
|
index: r.index,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
r.multipleSpendAttempts.push({
|
||||||
|
txid: txid,
|
||||||
|
index: parseInt(index),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
r.spendTxId = txid;
|
||||||
|
r.spendIndex = parseInt(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// This is not used now
|
||||||
|
TransactionDb.prototype.fromTxId = function(txid, cb) {
|
||||||
|
var self = this;
|
||||||
var k = OUTS_PREFIX + txid;
|
var k = OUTS_PREFIX + txid;
|
||||||
var ret=[];
|
var ret=[];
|
||||||
|
var idx={};
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
// outs.
|
// outs.
|
||||||
db.createReadStream({start: k, end: k + '~'})
|
db.createReadStream({start: k, end: k + '~'})
|
||||||
@ -85,27 +108,23 @@ function spec(b) {
|
|||||||
value_sat: parseInt(v[1]),
|
value_sat: parseInt(v[1]),
|
||||||
index: parseInt(k[2]),
|
index: parseInt(k[2]),
|
||||||
});
|
});
|
||||||
|
idx[parseInt(k[2])]= i++;
|
||||||
})
|
})
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
})
|
})
|
||||||
.on('end', function () {
|
.on('end', function () {
|
||||||
|
|
||||||
var k = SPEND_PREFIX + txid;
|
var k = SPEND_PREFIX + txid;
|
||||||
var l = ret.length;
|
|
||||||
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 v = data.value.split(':');
|
var j = idx[parseInt(k[3])];
|
||||||
var set=0;
|
|
||||||
for(var i=0; i<l; i++) {
|
assert(typeof j !== 'undefined','Spent could not be stored: tx ' + txid +
|
||||||
if (ret[i].index === parseInt(k[3])) {
|
'spend in TX:' + k[2] + ',' + k[3]+ ' j:' + j);
|
||||||
ret[i].spendTxId = v[0];
|
|
||||||
ret[i].spendIndex = parseInt(v[1]);
|
self._addSpendInfo(ret[j], k[4], k[5]);
|
||||||
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);
|
||||||
@ -130,7 +149,7 @@ function spec(b) {
|
|||||||
if (err || !addr || !valueSat ) {
|
if (err || !addr || !valueSat ) {
|
||||||
console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid);
|
console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid);
|
||||||
incompleteInputs = 1;
|
incompleteInputs = 1;
|
||||||
return c_in(); // error not scaled
|
return c_in(); // error not scalated
|
||||||
}
|
}
|
||||||
i.addr = addr;
|
i.addr = addr;
|
||||||
i.valueSat = valueSat;
|
i.valueSat = valueSat;
|
||||||
@ -197,12 +216,37 @@ function spec(b) {
|
|||||||
o.isConfirmed = is;
|
o.isConfirmed = is;
|
||||||
if (!o.spendTxId) return cb();
|
if (!o.spendTxId) return cb();
|
||||||
|
|
||||||
self.isConfirmed(o.spendTxId, function(err,is) {
|
if (o.multipleSpendAttempts) {
|
||||||
if (err) return cb(err);
|
|
||||||
|
|
||||||
o.spendIsConfirmed = is;
|
var isConfirmed = 0;
|
||||||
return cb();
|
var txid, index;
|
||||||
});
|
async.each(o.multipleSpendAttempts,
|
||||||
|
function (oi) {
|
||||||
|
self.isConfirmed(oi.spendTxId, function(err,is) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
isConfirmed = 1;
|
||||||
|
txid = oi.spendTxId;
|
||||||
|
index = oi.index;
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function (err) {
|
||||||
|
// write the spended TXid into main register
|
||||||
|
if (isConfirmed) {
|
||||||
|
o.spendTxId = txid;
|
||||||
|
o.index = index;
|
||||||
|
o.spendIsConfirmed = 1;
|
||||||
|
}
|
||||||
|
return cb(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.isConfirmed(o.spendTxId, function(err,is) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
o.spendIsConfirmed = is;
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -230,16 +274,17 @@ function spec(b) {
|
|||||||
|
|
||||||
async.each(ret, function(o, e_c) {
|
async.each(ret, function(o, e_c) {
|
||||||
var k = SPEND_PREFIX + o.txid + '-' + o.index;
|
var k = SPEND_PREFIX + o.txid + '-' + o.index;
|
||||||
db.get(k, function(err, val) {
|
db.createReadStream({start: k, end: k + '~'})
|
||||||
if (err && err.notFound) err=null;
|
.on('data', function (data) {
|
||||||
if (err || !val) return e_c(err);
|
var k = data.key.split('-');
|
||||||
|
self._addSpendInfo(o, k[4], k[5]);
|
||||||
var v = val.split(':');
|
})
|
||||||
o.spendTxId= v[0];
|
.on('error', function (err) {
|
||||||
o.spendIndex=parseInt(v[1]);
|
return e_c(err);
|
||||||
o.spendTs=parseInt(v[2]);
|
})
|
||||||
return e_c();
|
.on('end', function (err) {
|
||||||
});
|
return e_c(err);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
async.each(ret, function(o, e_c){
|
async.each(ret, function(o, e_c){
|
||||||
@ -335,8 +380,7 @@ 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 ,
|
.put( SPEND_PREFIX + i.txid + '-' + i.vout + '-' + tx.txid + '-' + i.n, ts)
|
||||||
tx.txid + ':' + i.n + ':' + ts)
|
|
||||||
.write(next_out);
|
.write(next_out);
|
||||||
},
|
},
|
||||||
function (err) {
|
function (err) {
|
||||||
|
|||||||
@ -32,11 +32,11 @@ function spec(b) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
var valueOut = 0;
|
var valueOutSat = 0;
|
||||||
info.vout.forEach( function(o) {
|
info.vout.forEach( function(o) {
|
||||||
valueOut += o.value;
|
valueOutSat += o.value * util.COIN;
|
||||||
});
|
});
|
||||||
info.valueOut = valueOut;
|
info.valueOut = parseInt(valueOutSat) / util.COIN;
|
||||||
info.size = b.length;
|
info.size = b.length;
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
|
|||||||
@ -45,7 +45,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> <strong> Height </strong></td>
|
<td> <strong> Height </strong></td>
|
||||||
<td class="text-right text-muted">{{block.height}}</td>
|
<td class="text-right text-muted">{{block.height}}
|
||||||
|
<span data-ng-show="block.isMainChain" class="text-success">(Mainchain)</span>
|
||||||
|
<span data-ng-show="!block.isMainChain" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> (Orphaned)</span>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> <strong> Block Reward </strong></td>
|
<td> <strong> Block Reward </strong></td>
|
||||||
|
|||||||
@ -43,9 +43,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
|
||||||
"txApperances": 6046,
|
"txApperances": 6047,
|
||||||
"balance": 1149.19744101,
|
"balance": 1199.74393851,
|
||||||
"totalReceived": 1149.19744101,
|
"totalReceived": 1199.74393851,
|
||||||
"totalSent": 0
|
"totalSent": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user