|
|
@ -5,11 +5,16 @@ var $ = require('preconditions').singleton(); |
|
|
|
var async = require('async'); |
|
|
|
var log = require('npmlog'); |
|
|
|
log.debug = log.verbose; |
|
|
|
var Bitcore = require('bitcore'); |
|
|
|
var Explorers = require('bitcore-explorers'); |
|
|
|
|
|
|
|
var Lock = require('./lock'); |
|
|
|
var Storage = require('./storage'); |
|
|
|
|
|
|
|
var Wallet = require('./model/wallet'); |
|
|
|
var Copayer = require('./model/copayer'); |
|
|
|
var Address = require('./model/address'); |
|
|
|
var TxProposal = require('./model/txproposal'); |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates an instance of the Copay server. |
|
|
@ -23,7 +28,7 @@ function CopayServer(opts) { |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates a new wallet. |
|
|
|
* @param {object} opts |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.id - The wallet id. |
|
|
|
* @param {string} opts.name - The wallet name. |
|
|
|
* @param {number} opts.m - Required copayers. |
|
|
@ -53,7 +58,7 @@ CopayServer.prototype.createWallet = function (opts, cb) { |
|
|
|
|
|
|
|
/** |
|
|
|
* Retrieves a wallet from storage. |
|
|
|
* @param {object} opts |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.id - The wallet id. |
|
|
|
* @param {truthy} opts.includeCopayers - Fetch wallet along with list of copayers. |
|
|
|
* @returns {Object} wallet |
|
|
@ -77,7 +82,7 @@ CopayServer.prototype.createWallet = function (opts, cb) { |
|
|
|
|
|
|
|
/** |
|
|
|
* Joins a wallet in creation. |
|
|
|
* @param {object} opts |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
* @param {string} opts.id - The copayer id. |
|
|
|
* @param {string} opts.name - The copayer name. |
|
|
@ -103,14 +108,13 @@ CopayServer.prototype.createWallet = function (opts, cb) { |
|
|
|
// Note: use Bitcore.crypto.ecdsa .verify()
|
|
|
|
|
|
|
|
var copayer = new Copayer({ |
|
|
|
walletId: wallet.id, |
|
|
|
id: opts.id, |
|
|
|
name: opts.name, |
|
|
|
xPubKey: opts.xPubKey, |
|
|
|
xPubKeySignature: opts.xPubKeySignature, |
|
|
|
}); |
|
|
|
|
|
|
|
self.storage.storeCopayer(copayer, function (err) { |
|
|
|
self.storage.storeCopayer(wallet.id, copayer, function (err) { |
|
|
|
if (err) return _cb(err); |
|
|
|
if ((wallet.copayers.length + 1) < wallet.n) return _cb(); |
|
|
|
|
|
|
@ -124,23 +128,35 @@ CopayServer.prototype.createWallet = function (opts, cb) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
CopayServer.prototype._doCreateAddress = function (pkr, isChange) { |
|
|
|
CopayServer.prototype._doCreateAddress = function (pkr, index, isChange) { |
|
|
|
throw 'not implemented'; |
|
|
|
}; |
|
|
|
|
|
|
|
// opts = {
|
|
|
|
// walletId,
|
|
|
|
// isChange,
|
|
|
|
// };
|
|
|
|
CopayServer.prototype.createAddress = function (opts, cb) { |
|
|
|
/** |
|
|
|
* Creates a new address. |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
* @param {truthy} opts.isChange - Indicates whether this is a regular address or a change address. |
|
|
|
* @returns {Address} address |
|
|
|
*/ |
|
|
|
CopayServer.prototype.createAddress = function (opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self.getWallet({ id: opts.walletId }, function (err, wallet) { |
|
|
|
if (err) return cb(err); |
|
|
|
if (!wallet) return cb('Wallet not found'); |
|
|
|
|
|
|
|
var address = self._doCreateAddress(wallet.publicKeyRing, opts.isChange); |
|
|
|
return cb(null, address); |
|
|
|
var index = wallet.addressIndex++; |
|
|
|
self.storage.storeWallet(wallet, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
var address = self._doCreateAddress(wallet.publicKeyRing, index, opts.isChange); |
|
|
|
self.storage.storeAddress(opts.walletId, address, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
return cb(null, address); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
@ -163,7 +179,7 @@ CopayServer.prototype._getBlockExplorer = function (provider, network) { |
|
|
|
url = 'https://test-insight.bitpay.com:443' |
|
|
|
break; |
|
|
|
} |
|
|
|
return new Bitcore.Insight(url, network); |
|
|
|
return new Explorers.Insight(url, network); |
|
|
|
break; |
|
|
|
} |
|
|
|
}; |
|
|
@ -178,7 +194,7 @@ CopayServer.prototype._getUtxos = function (opts, cb) { |
|
|
|
|
|
|
|
var addresses = _.pluck(addresses, 'address'); |
|
|
|
|
|
|
|
var bc = _getBlockExplorer('insight', opts.network); |
|
|
|
var bc = self._getBlockExplorer('insight', opts.network); |
|
|
|
bc.getUnspentUtxos(addresses, function (err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
@ -189,26 +205,27 @@ CopayServer.prototype._getUtxos = function (opts, cb) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
CopayServer.prototype._doCreateTx = function (opts, cb) { |
|
|
|
var tx = new Bitcore.Transaction() |
|
|
|
.from(opts.utxos) |
|
|
|
.to(opts.toAddress, opts.amount) |
|
|
|
.change(opts.changeAddress); |
|
|
|
CopayServer.prototype._doCreateTx = function (copayerId, toAddress, amount, changeAddress, utxos, cb) { |
|
|
|
var tx = new TxProposal({ |
|
|
|
creatorId: copayerId, |
|
|
|
toAddress: toAddress, |
|
|
|
amount: amount, |
|
|
|
changeAddress: changeAddress, |
|
|
|
inputs: utxos, |
|
|
|
}); |
|
|
|
|
|
|
|
tx.raw = new Bitcore.Transaction() |
|
|
|
.from(tx.inputs) |
|
|
|
.to(tx.toAddress, tx.amount) |
|
|
|
.change(tx.changeAddress); |
|
|
|
|
|
|
|
return tx; |
|
|
|
}; |
|
|
|
|
|
|
|
// requestSignature, // S(toAddress + amount + otToken) using this copayers privKey
|
|
|
|
// // using this signature, the server can
|
|
|
|
// };
|
|
|
|
|
|
|
|
// result = {
|
|
|
|
// ntxid,
|
|
|
|
// rawTx,
|
|
|
|
// };
|
|
|
|
/** |
|
|
|
* Creates a new transaction proposal. |
|
|
|
* @param {object} opts |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
* @param {string} opts.copayerId - The wallet id. |
|
|
|
* @param {truthy} opts.otToken - A one-time token used to avoid reply attacks. |
|
|
@ -216,9 +233,7 @@ CopayServer.prototype._doCreateTx = function (opts, cb) { |
|
|
|
* @param {number} opts.amount - Amount to transfer in satoshi. |
|
|
|
* @param {string} opts.message - A message to attach to this transaction. |
|
|
|
* @param {string} opts.requestSignature - Signature of the request (toAddress + amount + otToken). |
|
|
|
* @returns {Object} result |
|
|
|
* @returns {Object} result.ntxid - Id of the transaction proposal. |
|
|
|
* @returns {Object} result.rawTx - Raw transaction. |
|
|
|
* @returns {TxProposal} Transaction proposal. |
|
|
|
*/ |
|
|
|
CopayServer.prototype.createTx = function (opts, cb) { |
|
|
|
// Client generates a unique token and signs toAddress + amount + token.
|
|
|
@ -232,28 +247,17 @@ CopayServer.prototype.createTx = function (opts, cb) { |
|
|
|
var msg = '' + opts.toAddress + opts.amount + opts.otToken; |
|
|
|
if (!self._verifyMessageSignature(opts.copayerId, msg, opts.requestSignature)) return cb('Invalid request'); |
|
|
|
|
|
|
|
|
|
|
|
var txArgs = { |
|
|
|
toAddress: opts.toAddress, |
|
|
|
amount: opts.amount, |
|
|
|
changeAddress: opts.changeAddress, |
|
|
|
}; |
|
|
|
|
|
|
|
self._getUtxos({ walletId: wallet.id }, function (err, utxos) { |
|
|
|
if (err) return cb('Could not retrieve UTXOs'); |
|
|
|
txArgs.utxos = utxos; |
|
|
|
self._doCreateTx(txArgs, function (err, tx) { |
|
|
|
|
|
|
|
self._doCreateTx(opts.copayerId, opts.toAddress, opts.amount, opts.changeAddress, utxos, function (err, tx) { |
|
|
|
if (err) return cb('Could not create transaction'); |
|
|
|
|
|
|
|
self.storage.storeTx(tx, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
return cb(null, { |
|
|
|
ntxid: tx.ntxid, |
|
|
|
rawTx: tx.raw, |
|
|
|
}); |
|
|
|
return cb(null, tx); |
|
|
|
}); |
|
|
|
|
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|