PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo-express/node_modules/bitgo/src

Просмотр файла: pendingapproval.js

//
// Pending Approval Object
// Handles approving, rejecting and getting information on pending approvals
//
// Copyright 2015, BitGo, Inc.  All Rights Reserved.
//
var common = require('./common');
var Util = require('./util');
var assert = require('assert');

var Address = require('bitcoinjs-lib/src/address');
var Transaction = require('bitcoinjs-lib/src/transaction');
var networks = require('bitcoinjs-lib/src/networks');

var Q = require('q');
var _ = require('lodash');

//
// Constructor
//
var PendingApproval = function(bitgo, pendingApproval, wallet) {
  this.bitgo = bitgo;
  this.pendingApproval = pendingApproval;
  this.wallet = wallet;
};

//
// id
// Get the id of this pending approval.
//
PendingApproval.prototype.id = function() {
  return this.pendingApproval.id;
};

//
// ownerType
// Get the owner type (wallet or enterprise)
// Pending approvals can be approved or modified by different scopes (depending on how they were created)
// If a pending approval is owned by a wallet, then it can be approved by administrators of the wallet
// If a pending approval is owned by an enterprise, then it can be approved by administrators of the enterprise
//
PendingApproval.prototype.ownerType = function(params, callback) {
  params = params || {};
  common.validateParams(params, [], [], callback);

  if (this.pendingApproval.walletId) {
    return 'wallet';
  } else if (this.pendingApproval.enterprise) {
    return 'enterprise';
  } else {
    throw new Error('unexpected pending approval owner: neither walletId nor enterprise was present');
  }
};

//
// walletId
// Get the wallet ID that owns / is associated with the pending approval
//
PendingApproval.prototype.walletId = function() {
  return this.pendingApproval.walletId;
};

//
// enterpriseId
// Get the enterprise ID that owns / is associated with the pending approval
//
PendingApproval.prototype.enterpriseId = function() {
  return this.pendingApproval.enterprise;
};

//
// state
// Get the state of the pending approval
//
PendingApproval.prototype.state = function() {
  return this.pendingApproval.state;
};

//
// creator
// Get the id of the user that performed the action resulting in this pending approval
//
PendingApproval.prototype.creator = function() {
  return this.pendingApproval.creator;
};

//
// type
// Get the type of the pending approval (what it approves)
// Example: transactionRequest, tagUpdateRequest, policyRuleRequest
//
PendingApproval.prototype.type = function() {
  assert(this.pendingApproval.info);
  return this.pendingApproval.info.type;
};

//
// type
// Get information about the pending approval
//
PendingApproval.prototype.info = function() {
  return this.pendingApproval.info;
};

//
// approvalsRequired
// get the number of approvals that are required for this pending approval to be approved.
// Defaults to 1 if approvalsRequired doesn't exist on the object
//
PendingApproval.prototype.approvalsRequired = function() {
  return this.pendingApproval.approvalsRequired || 1;
};

//
// url
// Gets the url for this pending approval
//
PendingApproval.prototype.url = function(extra) {
  extra = extra || '';
  return this.bitgo.url('/pendingapprovals/' + this.id() + extra);
};

//
// get
// Refetches this pending approval and returns it
//
PendingApproval.prototype.get = function(params, callback) {
  params = params || {};
  common.validateParams(params, [], [], callback);

  var self = this;
  return this.bitgo.get(this.url())
  .result()
  .then(function(res) {
    self.pendingApproval = res;
    return self;
  })
  .nodeify(callback);
};

//
// Helper function to ensure that self.wallet is set
//
PendingApproval.prototype.populateWallet = function() {
  var self = this;
  if (!self.wallet) {
    return self.bitgo.wallets().get({ id: self.info().transactionRequest.sourceWallet })
    .then(function(wallet) {
      if (!wallet) {
        throw new Error('unexpected - unable to get wallet using sourcewallet');
      }
      self.wallet = wallet;
    });
  }

  if (self.wallet.id() != self.info().transactionRequest.sourceWallet) {
    throw new Error('unexpected source wallet for pending approval');
  }

  return Q(); // otherwise returns undefined
};

//
// helper function to recreate and sign a transaction on a wallet
// we should hopefully be able to move this logic server side soon
//
PendingApproval.prototype.recreateAndSignTransaction = function(params, callback) {
  params = _.extend({}, params);
  common.validateParams(params, ['txHex'], [], callback);

  var transaction = Transaction.fromHex(params.txHex);
  if (!transaction.outs) {
    throw new Error('transaction had no outputs or failed to parse successfully');
  }

  var network = networks[common.getNetwork()];
  params.recipients = {};

  var self = this;

  return Q()
  .then(function() {
    if (self.info().transactionRequest.recipients) {
      // recipients object found on the pending approvals - use it
      params.recipients = self.info().transactionRequest.recipients;
      return;
    }
    if (transaction.outs.length <= 2) {
      transaction.outs.forEach(function (out) {
        var outAddress = Address.fromOutputScript(out.script, network).toBase58Check();
        if (self.info().transactionRequest.destinationAddress == outAddress) {
          // If this is the destination, then spend to it
          params.recipients[outAddress] = out.value;
        }
      });
      return;
    }

    // This looks like a sendmany
    // Attempt to figure out the outputs by choosing all outputs that were not going back to the wallet as change addresses
    return self.wallet.addresses({chain: 1, sort: -1, limit:500})
    .then(function(result) {
      var changeAddresses = _.keyBy(result.addresses, 'address');
      transaction.outs.forEach(function (out) {
        var outAddress = Address.fromOutputScript(out.script, network).toBase58Check();
        if (!changeAddresses[outAddress]) {
          // If this is not a change address, then spend to it
          params.recipients[outAddress] = out.value;
        }
      });
    });
  })
  .then(function() {
    return self.wallet.createAndSignTransaction(params);
  });
};

//
// constructApprovalTx
// constructs/signs a transaction for this pending approval, returning the txHex (but not sending it)
//
PendingApproval.prototype.constructApprovalTx = function(params, callback) {
  params = params || {};
  common.validateParams(params, [], ['walletPassphrase'], callback);

  if (this.type() === 'transactionRequest' && !(params.walletPassphrase || params.xprv)) {
    throw new Error('wallet passphrase or xprv required to approve a transactionRequest');
  }

  if (params.useOriginalFee) {
    if (typeof(params.useOriginalFee) != 'boolean') {
      throw new Error('invalid type for useOriginalFeeRate');
    }
    if (params.fee || params.feeRate || params.feeTxConfirmTarget) {
      throw new Error('cannot specify a fee/feerate/feeTxConfirmTarget as well as useOriginalFee');
    }
  }

  var self = this;
  return Q()
  .then(function() {
    if (self.type() === 'transactionRequest') {
      var extendParams = { txHex: self.info().transactionRequest.transaction };
      if (params.useOriginalFee) {
        extendParams.fee = self.info().transactionRequest.fee;
      }
      return self.populateWallet()
      .then(function() {
        return self.recreateAndSignTransaction(_.extend(params, extendParams));
      });
    }
  });
};

//
// approve
// sets the pending approval to an approved state
//
PendingApproval.prototype.approve = function(params, callback) {
  params = params || {};
  common.validateParams(params, [], ['walletPassphrase', 'otp'], callback);

  var canRecreateTransaction = true;
  if (this.type() === 'transactionRequest' && !(params.walletPassphrase || params.xprv)) {
    canRecreateTransaction = false;
  }

  var self = this;
  return Q()
  .then(function() {
    if (self.type() === 'transactionRequest') {
      if (params.tx) {
        // the approval tx was reconstructed and explicitly specified - pass it through
        return {
          tx: params.tx
        };
      }

      // this user may not have spending privileges or a passphrase may not have been passed in
      if (!canRecreateTransaction) {
        return {
          tx: self.info().transactionRequest.transaction
        };
      }

      return self.populateWallet()
      .then(function() {
        return self.recreateAndSignTransaction(_.extend(params, { txHex: self.info().transactionRequest.transaction }));
      });
    }
  })
  .then(function(transaction) {
    var approvalParams = { 'state': 'approved', 'otp': params.otp };
    if (transaction) {
      approvalParams.tx = transaction.tx;
    }
    return self.bitgo.put(self.url())
    .send(approvalParams)
    .result()
    .nodeify(callback);
  })
  .catch(function(error) {
    if (!canRecreateTransaction &&
    (
      error.message.indexOf('could not find unspent output for input') !== -1 ||
      error.message.indexOf('transaction conflicts with an existing transaction in the send queue') !== -1)
    ) {
      throw new Error('unspents expired, wallet passphrase or xprv required to recreate transaction')
    }
    throw error;
  });
};

//
// rejected
// sets the pending approval to a rejected state
//
PendingApproval.prototype.reject = function(params, callback) {
  params = params || {};
  common.validateParams(params, [], [], callback);

  var self = this;

  return this.bitgo.put(this.url())
  .send({'state': 'rejected'})
  .result()
  .nodeify(callback);
};

//
// cancel
// rejects the pending approval
//
PendingApproval.prototype.cancel = function(params, callback) {
  return this.reject(params, callback);
};

module.exports = PendingApproval;

Выполнить команду


Для локальной разработки. Не используйте в интернете!