|
@ -170,7 +170,28 @@ CopayServer.prototype._doCreateAddress = function (pkr, index, isChange) { |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
CopayServer.prototype._verifyMessageSignature = function (copayerId, message, signature) { |
|
|
/** |
|
|
|
|
|
* Verifies that a given message was actually sent by an authorized copayer. |
|
|
|
|
|
* @param {Object} opts |
|
|
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
|
|
* @param {string} opts.copayerId - The wallet id. |
|
|
|
|
|
* @param {string} opts.message - The message to verify. |
|
|
|
|
|
* @param {string} opts.signature - The signature of message to verify. |
|
|
|
|
|
* @returns {truthy} The result of the verification. |
|
|
|
|
|
*/ |
|
|
|
|
|
CopayServer.prototype.verifyMessageSignature = function (opts, cb) { |
|
|
|
|
|
var self = this; |
|
|
|
|
|
|
|
|
|
|
|
self.storage.fetchCopayer(opts.walletId, opts.copayerId, function (err, copayer) { |
|
|
|
|
|
if (err) return cb(err); |
|
|
|
|
|
if (!copayer) return cb('Copayer not found'); |
|
|
|
|
|
|
|
|
|
|
|
var isValid = self._doVerifyMessageSignature(copayer.xPubKey, opts.message, opts.signature); |
|
|
|
|
|
return cb(null, isValid); |
|
|
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
CopayServer.prototype._doVerifyMessageSignature = function (pubKey, message, signature) { |
|
|
throw 'not implemented'; |
|
|
throw 'not implemented'; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
@ -238,24 +259,48 @@ CopayServer.prototype._doCreateTx = function (copayerId, toAddress, amount, chan |
|
|
* @param {Object} opts |
|
|
* @param {Object} opts |
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
* @param {string} opts.copayerId - The wallet id. |
|
|
* @param {string} opts.copayerId - The wallet id. |
|
|
* @param {truthy} opts.otToken - A one-time token used to avoid reply attacks. |
|
|
|
|
|
* @param {string} opts.toAddress - Destination address. |
|
|
* @param {string} opts.toAddress - Destination address. |
|
|
* @param {number} opts.amount - Amount to transfer in satoshi. |
|
|
* @param {number} opts.amount - Amount to transfer in satoshi. |
|
|
* @param {string} opts.message - A message to attach to this transaction. |
|
|
* @param {string} opts.message - A message to attach to this transaction. |
|
|
* @param {string} opts.requestSignature - Signature of the request (toAddress + amount + otToken). |
|
|
|
|
|
* @returns {TxProposal} Transaction proposal. |
|
|
* @returns {TxProposal} Transaction proposal. |
|
|
*/ |
|
|
*/ |
|
|
CopayServer.prototype.createTx = function (opts, cb) { |
|
|
CopayServer.prototype.createTx = function (opts, cb) { |
|
|
// Client generates a unique token and signs toAddress + amount + token.
|
|
|
|
|
|
// This way we authenticate + avoid replay attacks.
|
|
|
|
|
|
var self = this; |
|
|
var self = this; |
|
|
|
|
|
|
|
|
self.getWallet({ id: opts.walletId }, function (err, wallet) { |
|
|
self.getWallet({ id: opts.walletId }, function (err, wallet) { |
|
|
if (err) return cb(err); |
|
|
if (err) return cb(err); |
|
|
if (!wallet) return cb('Wallet not found'); |
|
|
if (!wallet) return cb('Wallet not found'); |
|
|
|
|
|
|
|
|
var msg = '' + opts.toAddress + opts.amount + opts.otToken; |
|
|
self._getUtxos({ walletId: wallet.id }, function (err, utxos) { |
|
|
if (!self._verifyMessageSignature(opts.copayerId, msg, opts.requestSignature)) return cb('Invalid request'); |
|
|
if (err) return cb('Could not retrieve UTXOs'); |
|
|
|
|
|
|
|
|
|
|
|
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, tx); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Sign a transaction proposal. |
|
|
|
|
|
* @param {Object} opts |
|
|
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
|
|
* @param {string} opts.copayerId - The wallet id. |
|
|
|
|
|
* @param {string} opts.ntxid - The identifier of the transaction. |
|
|
|
|
|
* @param {string} opts.signature - The signature of the tx for this copayer. |
|
|
|
|
|
*/ |
|
|
|
|
|
CopayServer.prototype.signTx = 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'); |
|
|
|
|
|
|
|
|
self._getUtxos({ walletId: wallet.id }, function (err, utxos) { |
|
|
self._getUtxos({ walletId: wallet.id }, function (err, utxos) { |
|
|
if (err) return cb('Could not retrieve UTXOs'); |
|
|
if (err) return cb('Could not retrieve UTXOs'); |
|
@ -273,10 +318,18 @@ CopayServer.prototype.createTx = function (opts, cb) { |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Retrieves all pending transaction proposals. |
|
|
|
|
|
* @param {Object} opts |
|
|
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
|
|
* @param {string} opts.copayerId - The wallet id. |
|
|
|
|
|
* @returns {TxProposal[]} Transaction proposal. |
|
|
|
|
|
*/ |
|
|
CopayServer.prototype.getPendingTxs = function (opts, cb) { |
|
|
CopayServer.prototype.getPendingTxs = function (opts, cb) { |
|
|
var self = this; |
|
|
var self = this; |
|
|
|
|
|
|
|
|
//self.storage.get
|
|
|
throw 'not implemented'; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|