<commit_before>'use strict';
var Transaction;
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var _ = require('underscore');
var async = require('async');

var bcoin = require('bcoin');
var Tx = bcoin.tx;

var config = require('../config');
var WalletAddress = mongoose.model('WalletAddress');

var Input = new Schema({
  utxo: String,
  vout: Number,
  address: String,
  amount: Number,
  wallets: {type: [Schema.Types.ObjectId]},
  sequence: Number,
  scriptSigHex: String
});

var Output = new Schema({
  address: String,
  amount: Number,
  vout: Number,
  wallets: {type: [Schema.Types.ObjectId]},
  spent: Boolean,
  sequence: Number,
  scriptHex: String,
});

var TransactionSchema = new Schema({
  txid: String,
  network: String,
  mainChain: Boolean,
  mempool: Boolean,
  chain: String,
  blockHeight: Number,
  blockHash: String,
  blockTime: Date,
  blockTimeNormalized: Date,
  inputs: [Input],
  outputs: [Output],
  coinbase: Boolean,
  fee: Number,
  inputsProcessed: Boolean,
  wallets: {type: [Schema.Types.ObjectId]},
  size: Number,
  locktime: Number
});

TransactionSchema.index({txid: 1});
TransactionSchema.index({blockHeight: 1, wallets: 1});
TransactionSchema.index({blockHash: 1});
TransactionSchema.index({blockTime: 1});
TransactionSchema.index({blockTimeNormalized: 1, wallets: 1});
TransactionSchema.index({mempool: 1});

TransactionSchema.index({'outputs.address': 1});
TransactionSchema.index({'inputs.address': 1});
TransactionSchema.index({'wallets': 1}, {sparse: true});
TransactionSchema.index({'inputs.wallets': 1}, {sparse: true});
TransactionSchema.index({'outputs.wallets': 1, mainChain: 1, 'outputs.spent': 1}, {sparse: true});

TransactionSchema.statics.syncTransactionAndOutputs = function(data, callback) {
  var transaction = Tx.fromRaw(data.transaction, 'hex');
  Transaction.findOne({txid: transaction.rhash()}).lean().exec(function(err, hasTx) {
    if(err) {
      return callback(err);
    }
    if(hasTx) {
      if (!hasTx.mainChain && data.mainChain){
        return Transaction.update({txid: transaction.rhash()},
        {$set: {
          mainChain: true,
          mempool: false,
          blockHash: data.blockHash,
          blockHeight: data.blockHeight,
          blockTime: new Date(data.blockTime),
          blockTimeNormalized: new Date(data.blockTimeNormalized)
        }}, callback);
      }
      return callback();
    }

    var newTx = new Transaction();
    newTx.blockHeight = data.blockHeight;
    newTx.blockHash = data.blockHash;
    newTx.blockTime = data.blockTime && new Date(data.blockTime);
    newTx.blockTimeNormalized = data.blockTimeNormalized && new Date(data.blockTimeNormalized);
    newTx.txid = transaction.rhash();
    newTx.network = config.network;
    newTx.mainChain = data.mainChain;
    newTx.mempool = data.mempool;
    newTx.size = transaction._size;
    newTx.locktime = transaction.locktime;

    async.eachOfLimit(transaction.outputs, config.maxPoolSize, function(output, index, outputCb) {
      var address = output.getAddress();
      if(address){
        address = address.toString(config.network);
      }
      mongoose.model('WalletAddress').find({address: address}).lean().exec(function(err, wallets) {
        if(err) {
          return outputCb(err);
        }

        _.each(wallets, function(wallet) {
          newTx.wallets.addToSet(wallet.wallet);
        });

        newTx.outputs.push({
          vout: index,
          amount: parseFloat((output.value * 1e-8).toFixed(8)),
          address: address,
          wallets: _.pluck(wallets, 'wallet'),
          spent: false,
          scriptHex: output.script.toJSON()
        });
        outputCb();
      });
    }, function(err) {
      if(err) {
        return callback(err);
      }
      // Coinbase
      if(transaction.isCoinbase()) {
        newTx.coinbase = true;
        newTx.inputs.push({
          amount: _.reduce(newTx.outputs, function(total, output) {return total + output.amount;}, 0)
        });
        newTx.inputsProcessed = true;
      } else {
        transaction.inputs.forEach(function(input) {
          var prevTxId = input.prevout.rhash();
          newTx.inputs.push({
            utxo: prevTxId,
            vout: input.prevout.index,
            scriptSigHex: input.script.toJSON(),
          });
        });
        newTx.inputsProcessed = false;
      }
      newTx.save(callback);
    });
  });
};

TransactionSchema.statics.syncTransactionInputs = function(txid, callback) {
  Transaction.findOne({txid: txid}, function(err, transaction) {
    if(err) {
      return callback(err);
    }
    if(transaction.inputsProcessed) {
      return callback();
    }
    var spentUpdates = [];
    async.eachLimit(transaction.inputs, config.maxPoolSize, function(input, inputCb) {
      Transaction.findOne({txid: input.utxo}).select('outputs').lean().exec(function(err, utxo) {
        if(err) {
          return inputCb(err);
        }
        if(!utxo) {
          return callback(new Error('Couldnt find utxo'));
        }
        utxo = _.findWhere(utxo.outputs, {vout: input.vout});
        input.address = utxo.address;
        input.amount = parseFloat(utxo.amount.toFixed(8));
        spentUpdates.push({
          updateOne: {
            filter: {txid: input.utxo, 'outputs.vout': input.vout},
            update: {
              $set: {'outputs.$.spent': true}
            }
          }
        });
        if(err) {
          return inputCb(err);<commit_msg>migrate from findOne's<commit_after>'use strict';
var Transaction;
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var _ = require('underscore');
var async = require('async');

var bcoin = require('bcoin');
var Tx = bcoin.tx;

var config = require('../config');
var WalletAddress = mongoose.model('WalletAddress');

var Input = new Schema({
  utxo: String,
  vout: Number,
  address: String,
  amount: Number,
  wallets: {type: [Schema.Types.ObjectId]},
  sequence: Number,
  scriptSigHex: String
});

var Output = new Schema({
  address: String,
  amount: Number,
  vout: Number,
  wallets: {type: [Schema.Types.ObjectId]},
  spent: Boolean,
  sequence: Number,
  scriptHex: String,
});

var TransactionSchema = new Schema({
  txid: String,
  network: String,
  mainChain: Boolean,
  mempool: Boolean,
  chain: String,
  blockHeight: Number,
  blockHash: String,
  blockTime: Date,
  blockTimeNormalized: Date,
  inputs: [Input],
  outputs: [Output],
  coinbase: Boolean,
  fee: Number,
  inputsProcessed: Boolean,
  wallets: {type: [Schema.Types.ObjectId]},
  size: Number,
  locktime: Number
});

TransactionSchema.index({txid: 1});
TransactionSchema.index({blockHeight: 1, wallets: 1});
TransactionSchema.index({blockHash: 1});
TransactionSchema.index({blockTime: 1});
TransactionSchema.index({blockTimeNormalized: 1, wallets: 1});
TransactionSchema.index({mempool: 1});

TransactionSchema.index({'outputs.address': 1});
TransactionSchema.index({'inputs.address': 1});
TransactionSchema.index({'wallets': 1}, {sparse: true});
TransactionSchema.index({'inputs.wallets': 1}, {sparse: true});
TransactionSchema.index({'outputs.wallets': 1, mainChain: 1, 'outputs.spent': 1}, {sparse: true});

TransactionSchema.statics.syncTransactionAndOutputs = function(data, callback) {
  var transaction = Tx.fromRaw(data.transaction, 'hex');
  Transaction.find({txid: transaction.rhash()}).limit(1).lean().exec(function(err, hasTx) {
    if(err) {
      return callback(err);
    }
    hasTx = hasTx[0];
    if(hasTx) {
      if (!hasTx.mainChain && data.mainChain){
        return Transaction.update({txid: transaction.rhash()},
        {$set: {
          mainChain: true,
          mempool: false,
          blockHash: data.blockHash,
          blockHeight: data.blockHeight,
          blockTime: new Date(data.blockTime),
          blockTimeNormalized: new Date(data.blockTimeNormalized)
        }}, callback);
      }
      return callback();
    }

    var newTx = new Transaction();
    newTx.blockHeight = data.blockHeight;
    newTx.blockHash = data.blockHash;
    newTx.blockTime = data.blockTime && new Date(data.blockTime);
    newTx.blockTimeNormalized = data.blockTimeNormalized && new Date(data.blockTimeNormalized);
    newTx.txid = transaction.rhash();
    newTx.network = config.network;
    newTx.mainChain = data.mainChain;
    newTx.mempool = data.mempool;
    newTx.size = transaction._size;
    newTx.locktime = transaction.locktime;

    async.eachOfLimit(transaction.outputs, config.maxPoolSize, function(output, index, outputCb) {
      var address = output.getAddress();
      if(address){
        address = address.toString(config.network);
      }
      mongoose.model('WalletAddress').find({address: address}).lean().exec(function(err, wallets) {
        if(err) {
          return outputCb(err);
        }

        _.each(wallets, function(wallet) {
          newTx.wallets.addToSet(wallet.wallet);
        });

        newTx.outputs.push({
          vout: index,
          amount: parseFloat((output.value * 1e-8).toFixed(8)),
          address: address,
          wallets: _.pluck(wallets, 'wallet'),
          spent: false,
          scriptHex: output.script.toJSON()
        });
        outputCb();
      });
    }, function(err) {
      if(err) {
        return callback(err);
      }
      // Coinbase
      if(transaction.isCoinbase()) {
        newTx.coinbase = true;
        newTx.inputs.push({
          amount: _.reduce(newTx.outputs, function(total, output) {return total + output.amount;}, 0)
        });
        newTx.inputsProcessed = true;
      } else {
        transaction.inputs.forEach(function(input) {
          var prevTxId = input.prevout.rhash();
          newTx.inputs.push({
            utxo: prevTxId,
            vout: input.prevout.index,
            scriptSigHex: input.script.toJSON(),
          });
        });
        newTx.inputsProcessed = false;
      }
      newTx.save(callback);
    });
  });
};

TransactionSchema.statics.syncTransactionInputs = function(txid, callback) {
  Transaction.find({txid: txid}).limit().exec(function(err, transaction) {
    if(err) {
      return callback(err);
    }
    transaction = transaction[0];
    if(transaction.inputsProcessed) {
      return callback();
    }
    var spentUpdates = [];
    async.eachLimit(transaction.inputs, config.maxPoolSize, function(input, inputCb) {
      Transaction.find({txid: input.utxo}).limit(1).select('outputs').lean().exec(function(err, utxo) {
        if(err) {
          return inputCb(err);
        }
        utxo = utxo[0];
        if(!utxo) {
          return callback(new Error('Couldnt find utxo'));
        }
        utxo = _.findWhere(utxo.outputs, {vout: input.vout});
        input.address = utxo.address;
        input.amount = parseFloat(utxo.amount.toFixed(8));
        spentUpdates.push({
          updateOne: {
            filter: {txid: input.utxo, 'outputs.vout': input.vout},
            update: {
              $set: {'outputs.$.spent': true}
            }
          }
        });
        if(err) {
          return inputCb(err);
{'fraction_non_alphanumeric': 0.143204977906033, 'fraction_numerical': 0.004058075570385066, 'mean_word_length': 2.9118165784832453, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7117519', 'n_tokens_mistral': 3460, 'n_tokens_neox': 3447, 'n_words': 664}