Merge pull request #220 from matiu/feature/handle-double-spends

Feature/handle double spends
This commit is contained in:
Matias Alejo Garcia 2014-02-10 18:15:31 -02:00
commit 3c317d700b
6 changed files with 97 additions and 42 deletions

View File

@ -139,6 +139,8 @@ function spec(b) {
BlockDb.prototype.fromHashWithInfo = function(hash, cb) {
var self = this;
rpc.getBlock(hash, function(err, info) {
// Not found?
if (err && err.code === -5) return cb();
@ -146,9 +148,16 @@ function spec(b) {
if (info.result.height)
info.result.reward = BitcoreBlock.getBlockValue(info.result.height) / util.COIN ;
return cb(null, {
hash: hash,
info: info.result,
self.isMain(hash, function(err, val) {
if (err) return cb(err);
info.result.isMainChain = val ? true : false;
return cb(null, {
hash: hash,
info: info.result,
});
});
});
};

View File

@ -16,7 +16,7 @@ function spec(b) {
// to sum up addr balance
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
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 ret=[];
var idx={};
var i = 0;
// outs.
db.createReadStream({start: k, end: k + '~'})
@ -85,27 +108,23 @@ function spec(b) {
value_sat: parseInt(v[1]),
index: parseInt(k[2]),
});
idx[parseInt(k[2])]= i++;
})
.on('error', function (err) {
return cb(err);
})
.on('end', function () {
var k = SPEND_PREFIX + txid;
var l = ret.length;
db.createReadStream({start: k, end: k + '~'})
.on('data', function (data) {
var k = data.key.split('-');
var v = data.value.split(':');
var set=0;
for(var i=0; i<l; i++) {
if (ret[i].index === parseInt(k[3])) {
ret[i].spendTxId = v[0];
ret[i].spendIndex = parseInt(v[1]);
set=1;
}
}
assert(set,'Spent could not be stored: tx ' + txid +
'spend in TX:' + k[2] + ',' + k[3]);
var j = idx[parseInt(k[3])];
assert(typeof j !== 'undefined','Spent could not be stored: tx ' + txid +
'spend in TX:' + k[2] + ',' + k[3]+ ' j:' + j);
self._addSpendInfo(ret[j], k[4], k[5]);
})
.on('error', function (err) {
return cb(err);
@ -130,7 +149,7 @@ function spec(b) {
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
return c_in(); // error not scalated
}
i.addr = addr;
i.valueSat = valueSat;
@ -197,12 +216,37 @@ function spec(b) {
o.isConfirmed = is;
if (!o.spendTxId) return cb();
self.isConfirmed(o.spendTxId, function(err,is) {
if (err) return cb(err);
if (o.multipleSpendAttempts) {
o.spendIsConfirmed = is;
return cb();
});
var isConfirmed = 0;
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) {
var k = SPEND_PREFIX + o.txid + '-' + o.index;
db.get(k, function(err, val) {
if (err && err.notFound) err=null;
if (err || !val) return e_c(err);
var v = val.split(':');
o.spendTxId= v[0];
o.spendIndex=parseInt(v[1]);
o.spendTs=parseInt(v[2]);
return e_c();
});
db.createReadStream({start: k, end: k + '~'})
.on('data', function (data) {
var k = data.key.split('-');
self._addSpendInfo(o, k[4], k[5]);
})
.on('error', function (err) {
return e_c(err);
})
.on('end', function (err) {
return e_c(err);
});
},
function() {
async.each(ret, function(o, e_c){
@ -335,8 +380,7 @@ function spec(b) {
async.forEachLimit(tx.vin, CONCURRENCY,
function(i, next_out) {
db.batch()
.put( SPEND_PREFIX + i.txid + '-' + i.vout ,
tx.txid + ':' + i.n + ':' + ts)
.put( SPEND_PREFIX + i.txid + '-' + i.vout + '-' + tx.txid + '-' + i.n, ts)
.write(next_out);
},
function (err) {

View File

@ -32,11 +32,11 @@ function spec(b) {
});
// Outputs
var valueOut = 0;
var valueOutSat = 0;
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;
return info;

View File

@ -45,7 +45,9 @@
</tr>
<tr>
<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>
<td> <strong> Block Reward </strong></td>

View File

@ -43,9 +43,9 @@
},
{
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29",
"txApperances": 6046,
"balance": 1149.19744101,
"totalReceived": 1149.19744101,
"txApperances": 6047,
"balance": 1199.74393851,
"totalReceived": 1199.74393851,
"totalSent": 0
},
{