Browse Source

.

activeAddress
Ivan Socolsky 10 years ago
parent
commit
0a2a5a8873
  1. 2
      lib/model/copayer.js
  2. 2
      lib/model/wallet.js
  3. 94
      lib/server.js
  4. 8
      lib/storage.js
  5. 9
      package.json
  6. 60
      test/integration.js

2
lib/model/copayer.js

@ -5,7 +5,6 @@ var _ = require('lodash');
function Copayer(opts) {
opts = opts || {};
this.walletId = opts.walletId;
this.id = opts.id;
this.name = opts.name;
this.xPubKey = opts.xPubKey;
@ -15,7 +14,6 @@ function Copayer(opts) {
Copayer.fromObj = function (obj) {
var x = new Copayer();
x.walletId = obj.walletId;
x.id = obj.id;
x.name = obj.name;
x.xPubKey = obj.xPubKey;

2
lib/model/wallet.js

@ -11,6 +11,7 @@ function Wallet(opts) {
this.n = opts.n;
this.status = 'pending';
this.publicKeyRing = [];
this.addressIndex = 0;
};
Wallet.fromObj = function (obj) {
@ -22,6 +23,7 @@ Wallet.fromObj = function (obj) {
x.n = obj.n;
x.status = obj.status;
x.publicKeyRing = obj.publicKeyRing;
x.addressIndex = obj.addressIndex;
return x;
};

94
lib/server.js

@ -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);
});
});
});
});

8
lib/storage.js

@ -48,8 +48,12 @@ Storage.prototype.storeWallet = function (wallet, cb) {
this.db.put('wallet-' + wallet.id, wallet, cb);
};
Storage.prototype.storeCopayer = function (copayer, cb) {
this.db.put('wallet-' + copayer.walletId + '-copayer-' + copayer.id, copayer, cb);
Storage.prototype.storeCopayer = function (walletId, copayer, cb) {
this.db.put('wallet-' + walletId + '-copayer-' + copayer.id, copayer, cb);
};
Storage.prototype.storeAddress = function (walletId, address, cb) {
this.db.put('wallet-' + walletId + '-address-' + address.address, address, cb);
};
Storage.prototype.getAddresses = function (walletId, cb) {

9
package.json

@ -17,14 +17,15 @@
"url": "https://github.com/isocolsky/copay-lib/issues"
},
"dependencies": {
"bitcore": "^0.8.6",
"async": "^0.9.0",
"lodash": "^2.4.1",
"preconditions": "^1.0.7",
"bitcore": "^0.8.6",
"bitcore-explorers": "^0.9.1",
"express": "^4.10.0",
"leveldown": "^0.10.0",
"levelup": "^0.19.0",
"npmlog": "^0.1.1"
"lodash": "^2.4.1",
"npmlog": "^0.1.1",
"preconditions": "^1.0.7"
},
"devDependencies": {
"chai": "^1.9.1",

60
test/integration.js

@ -10,6 +10,7 @@ var levelup = require('levelup');
var memdown = require('memdown');
var Wallet = require('../lib/model/wallet');
var Address = require('../lib/model/address');
var Copayer = require('../lib/model/copayer');
var CopayServer = require('../lib/server');
@ -320,32 +321,61 @@ describe('Copay server', function() {
});
};
describe('#createTx', function() {
describe('#createAddress', function() {
beforeEach(function() {
server = new CopayServer({
db: db,
});
});
it.skip('should create tx', function (done) {
server._verifyMessageSignature = sinon.stub().returns(true);
it('should create address', function (done) {
server._doCreateAddress = sinon.stub().returns(new Address({ address: 'addr1', path: 'path1' }));
helpers.createAndJoinWallet('123', 2, 2, function (err, wallet) {
var txOpts = {
copayerId: '1',
walletId: '123',
toAddress: 'dummy',
amount: 100,
message: 'some message',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts, function (err, res) {
server.createAddress({ walletId: '123' }, function (err, address) {
should.not.exist(err);
res.ntxid.should.exist;
res.txRaw.should.exist;
address.should.exist;
address.address.should.equal('addr1');
address.path.should.equal('path1');
done();
});
});
});
});
describe('#createTx', function() {
beforeEach(function(done) {
server = new CopayServer({
db: db,
});
server._doCreateAddress = sinon.stub().returns(new Address({ address: 'addr1', path: 'path1' }));
helpers.createAndJoinWallet('123', 2, 2, function (err, wallet) {
server.createAddress({ walletId: '123' }, function (err, address) {
done();
});
});
});
it.skip('should create tx', function (done) {
server._verifyMessageSignature = sinon.stub().returns(true);
var bc = sinon.stub();
bc.getUnspentUtxos = sinon.stub().yields(null, ['utxo1', 'utxo2']);
server._getBlockExplorer = sinon.stub().returns(bc);
var txOpts = {
copayerId: '1',
walletId: '123',
toAddress: 'dummy',
amount: 100,
message: 'some message',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts, function (err, tx) {
should.not.exist(err);
tx.should.exist;
tx.raw.should.exist;
done();
});
});
});
});

Loading…
Cancel
Save