insight-ui-zcash/app/models/TransactionOut.js

216 lines
5.3 KiB
JavaScript

'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
async = require('async'),
util = require('bitcore/util/util'),
TransactionRpc = require('../../lib/TransactionRpc').class(),
Schema = mongoose.Schema;
var CONCURRENCY = 15;
// TODO: use bitcore networks module
var genesisTXID = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b';
var TransactionOutSchema = new Schema({
txidBuf: {
type: Buffer,
index: true,
},
index: Number,
addr: {
type: String,
index: true,
},
value_sat: Number,
spendTxIdBuf: Buffer,
spendIndex: Number,
});
// Compound index
TransactionOutSchema.index({txidBuf: 1, index: 1}, {unique: true, sparse: true});
TransactionOutSchema.index({spendTxIdBuf: 1, spendIndex: 1}, {unique: true, sparse: true});
TransactionOutSchema.virtual('txid').get(function () {
return this.txidBuf.toString('hex');
});
TransactionOutSchema.virtual('spendTxid').get(function () {
if (!this.spendTxIdBuf) return (null);
return this.spendTxIdBuf.toString('hex');
});
TransactionOutSchema.virtual('txid').set(function (txidStr) {
if (txidStr)
this.txidBuf = new Buffer(txidStr,'hex');
else
this.txidBuf = null;
});
TransactionOutSchema.statics.fromTxId = function(txid, cb) {
var txidBuf = new Buffer(txid, 'hex');
this.find({
txidBuf: txidBuf,
}).exec(function (err,items) {
// sort by index
return cb(err,items.sort(function(a,b){
return a.index - b.index;
}));
});
};
TransactionOutSchema.statics.fromTxIdOne = function(txid, cb) {
var txidBuf = new Buffer(txid, 'hex');
this.find({
txidBuf: txidBuf,
}).exec(function (err,item) {
return cb(err, item[0]);
});
};
TransactionOutSchema.statics.fromTxIdN = function(txid, n, cb) {
var txidBuf = new Buffer(txid, 'hex');
this.findOne({
txidBuf: txidBuf, index: n
}).exec(cb);
};
TransactionOutSchema.statics.removeFromTxId = function(txid, cb) {
var txidBuf = new Buffer(txid, 'hex');
this.remove({ txidBuf: txidBuf }).exec(cb);
};
TransactionOutSchema.statics._explodeTransactionOuts = function(txid, cb) {
var Self = this;
var addrs = [];
var is_new = true;
// Is it from genesis block? (testnet==livenet)
// TODO: parse it from networks.genesisTX
if (txid === genesisTXID) return cb();
TransactionRpc.getRpcInfo(txid, function(err, info) {
if (err || !info) return cb(err);
var bTxId = new Buffer(txid,'hex');
async.series([
// Input Outputs (mark them as spended)
function(p_c) {
if (info.isCoinBase) return p_c();
async.forEachLimit(info.vin, CONCURRENCY,
function(i, next_out) {
var b = new Buffer(i.txid,'hex');
var data = {
txidBuf: b,
index: i.vout,
spendTxIdBuf: bTxId,
spendIndex: i.n,
};
Self.update({txidBuf: b, index: i.vout}, data, {upsert: true}, next_out);
},
function (err) {
if (err) {
if (!err.message.match(/E11000/)) {
console.log('ERR at TX %s: %s', txid, err);
return cb(err);
}
}
return p_c();
});
},
// Parse Outputs
function(p_c) {
async.forEachLimit(info.vout, CONCURRENCY,
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
if (addrs.indexOf(o.scriptPubKey.addresses[0]) === -1) {
addrs.push(o.scriptPubKey.addresses[0]);
}
var data = {
txidBuf: bTxId,
index : o.n,
value_sat : o.value * util.COIN,
addr : o.scriptPubKey.addresses[0],
};
Self.update({txidBuf: bTxId, index: o.n}, data, {upsert: true}, next_out);
}
else {
console.log ('WARN in TX: %s could not parse OUTPUT %d', txid, o.n);
return next_out();
}
},
function (err) {
if (err) {
if (err.message.match(/E11000/)) {
is_new = false;
}
else {
console.log('ERR at TX %s: %s', txid, err);
return cb(err);
}
}
return p_c();
});
}], function() {
return cb(null, addrs, is_new);
});
});
};
TransactionOutSchema.statics.createFromArray = function(txs, next) {
var Self = this;
if (!txs) return next();
var inserted_txs = [];
var updated_addrs = {};
async.forEachLimit(txs, CONCURRENCY, function(txid, cb, was_new) {
Self._explodeTransactionOuts( txid, function(err, addrs) {
if (err) return next(err);
if (was_new) {
inserted_txs.push(txid);
addrs.each(function(a) {
if ( !updated_addrs[a]) updated_addrs[a] = [];
updated_addrs[a].push(txid);
});
}
return cb();
});
},
function(err) {
return next(err, inserted_txs, updated_addrs);
});
};
module.exports = mongoose.model('TransactionOut', TransactionOutSchema);