This commit is contained in:
parent
2bf9c4da48
commit
50e936379f
|
@ -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';
|
||||
};
|
||||
|
||||
|
@ -238,25 +259,18 @@ CopayServer.prototype._doCreateTx = function (copayerId, toAddress, amount, chan
|
|||
* @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.
|
||||
* @param {string} opts.toAddress - Destination address.
|
||||
* @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 {TxProposal} Transaction proposal.
|
||||
*/
|
||||
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;
|
||||
|
||||
self.getWallet({ id: opts.walletId }, function (err, wallet) {
|
||||
if (err) return cb(err);
|
||||
if (!wallet) return cb('Wallet not found');
|
||||
|
||||
var msg = '' + opts.toAddress + opts.amount + opts.otToken;
|
||||
if (!self._verifyMessageSignature(opts.copayerId, msg, opts.requestSignature)) return cb('Invalid request');
|
||||
|
||||
self._getUtxos({ walletId: wallet.id }, function (err, utxos) {
|
||||
if (err) return cb('Could not retrieve UTXOs');
|
||||
|
||||
|
@ -273,10 +287,49 @@ CopayServer.prototype.createTx = function (opts, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var self = this;
|
||||
|
||||
//self.storage.get
|
||||
throw 'not implemented';
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,16 @@ Storage.prototype.fetchWallet = function (id, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
Storage.prototype.fetchCopayer = function (walletId, copayerId, cb) {
|
||||
this.db.get('wallet-' + walletId + '-copayer-' + copayerId, function (err, data) {
|
||||
if (err) {
|
||||
if (err.notFound) return cb();
|
||||
return cb(err);
|
||||
}
|
||||
return cb(null, Copayer.fromObj(data));
|
||||
});
|
||||
};
|
||||
|
||||
Storage.prototype.fetchCopayers = function (walletId, cb) {
|
||||
var copayers = [];
|
||||
var key = 'wallet-' + walletId + '-copayer-';
|
||||
|
|
|
@ -324,6 +324,46 @@ describe('Copay server', function() {
|
|||
});
|
||||
};
|
||||
|
||||
describe('#_verifyMessageSignature', function() {
|
||||
beforeEach(function() {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully verify message signature', function (done) {
|
||||
server._doVerifyMessageSignature = sinon.stub().returns(true);
|
||||
helpers.createAndJoinWallet('123', 2, 2, function (err, wallet) {
|
||||
var opts = {
|
||||
walletId: '123',
|
||||
copayerId: '1',
|
||||
message: 'hello world',
|
||||
signature: 'dummy',
|
||||
};
|
||||
server.verifyMessageSignature(opts, function (err, isValid) {
|
||||
should.not.exist(err);
|
||||
isValid.should.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to verify message signature when copayer does not exist', function (done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function (err, wallet) {
|
||||
var opts = {
|
||||
walletId: '123',
|
||||
copayerId: '999',
|
||||
message: 'hello world',
|
||||
signature: 'dummy',
|
||||
};
|
||||
server.verifyMessageSignature(opts, function (err, isValid) {
|
||||
err.should.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createAddress', function() {
|
||||
beforeEach(function() {
|
||||
server = new CopayServer({
|
||||
|
|
Loading…
Reference in New Issue