Browse Source

.

activeAddress
Ivan Socolsky 10 years ago
parent
commit
d03a16d659
  1. 11
      lib/model/txproposal.js
  2. 67
      lib/server.js
  3. 13
      lib/storage.js
  4. 6
      test/integration.js

11
lib/model/txproposal.js

@ -4,23 +4,34 @@ function TxProposal(opts) {
opts = opts || {}; opts = opts || {};
this.createdOn = Math.floor(Date.now() / 1000); this.createdOn = Math.floor(Date.now() / 1000);
this.id = opts.id;
this.creatorId = opts.creatorId; this.creatorId = opts.creatorId;
this.toAddress = opts.toAddress; this.toAddress = opts.toAddress;
this.amount = opts.amount; this.amount = opts.amount;
this.changeAddress = opts.changeAddress; this.changeAddress = opts.changeAddress;
this.inputs = opts.inputs; this.inputs = opts.inputs;
this.actions = [];
}; };
TxProposal.fromObj = function (obj) { TxProposal.fromObj = function (obj) {
var x = new TxProposal(); var x = new TxProposal();
x.createdOn = obj.createdOn; x.createdOn = obj.createdOn;
x.id = obj.id;
x.creatorId = obj.creatorId; x.creatorId = obj.creatorId;
x.toAddress = obj.toAddress; x.toAddress = obj.toAddress;
x.amount = obj.amount; x.amount = obj.amount;
x.changeAddress = obj.changeAddress; x.changeAddress = obj.changeAddress;
x.inputs = obj.inputs; x.inputs = obj.inputs;
x.raw = obj.raw; x.raw = obj.raw;
x.actions = _.map(obj.actions, function(a) {
return {
createdOn: a.createdOn,
copayerId: a.copayerId,
type: a.type,
signature: a.signature,
};
});
return x; return x;
}; };

67
lib/server.js

@ -41,7 +41,7 @@ function CopayServer(opts) {
CopayServer.prototype.createWallet = function (opts, cb) { CopayServer.prototype.createWallet = function (opts, cb) {
var self = this; var self = this;
self.getWallet({ id: opts.id }, function (err, wallet) { self.storage.fetchWallet(opts.id, function (err, wallet) {
if (err) return cb(err); if (err) return cb(err);
if (wallet) return cb('Wallet already exists'); if (wallet) return cb('Wallet already exists');
@ -69,7 +69,9 @@ CopayServer.prototype.createWallet = function (opts, cb) {
var self = this; var self = this;
self.storage.fetchWallet(opts.id, function (err, wallet) { self.storage.fetchWallet(opts.id, function (err, wallet) {
if (err || !wallet) return cb(err); if (err) return cb(err);
if (!wallet) return cb('Wallet not found');
if (opts.includeCopayers) { if (opts.includeCopayers) {
self.storage.fetchCopayers(wallet.id, function (err, copayers) { self.storage.fetchCopayers(wallet.id, function (err, copayers) {
if (err) return cb(err); if (err) return cb(err);
@ -109,8 +111,7 @@ CopayServer.prototype._runLocked = function (walletId, cb, task) {
self._runLocked(opts.walletId, cb, function (cb) { self._runLocked(opts.walletId, cb, function (cb) {
self.getWallet({ id: opts.walletId, includeCopayers: true }, function (err, wallet) { self.getWallet({ id: opts.walletId, includeCopayers: true }, function (err, wallet) {
if (err) return cb(err); if (err || !wallet) return cb(err);
if (!wallet) return cb('Wallet not found');
if (_.find(wallet.copayers, { xPubKey: opts.xPubKey })) return cb('Copayer already in wallet'); if (_.find(wallet.copayers, { xPubKey: opts.xPubKey })) return cb('Copayer already in wallet');
if (wallet.copayers.length == wallet.n) return cb('Wallet full'); if (wallet.copayers.length == wallet.n) return cb('Wallet full');
// TODO: validate copayer's extended public key using the public key from this wallet // TODO: validate copayer's extended public key using the public key from this wallet
@ -152,8 +153,7 @@ CopayServer.prototype._doCreateAddress = function (pkr, index, isChange) {
self._runLocked(opts.walletId, cb, function (cb) { self._runLocked(opts.walletId, cb, function (cb) {
self.getWallet({ id: opts.walletId }, function (err, wallet) { self.getWallet({ id: opts.walletId }, function (err, wallet) {
if (err) return cb(err); if (err || !wallet) return cb(err);
if (!wallet) return cb('Wallet not found');
var index = wallet.addressIndex++; var index = wallet.addressIndex++;
self.storage.storeWallet(wallet, function (err) { self.storage.storeWallet(wallet, function (err) {
@ -219,7 +219,7 @@ CopayServer.prototype._getUtxos = function (opts, cb) {
var self = this; var self = this;
// Get addresses for this wallet // Get addresses for this wallet
self.storage.getAddresses(opts.walletId, function (err, addresses) { self.storage.fetchAddresses(opts.walletId, function (err, addresses) {
if (err) return cb(err); if (err) return cb(err);
if (addresses.length == 0) return cb('The wallet has no addresses'); if (addresses.length == 0) return cb('The wallet has no addresses');
@ -268,51 +268,62 @@ CopayServer.prototype.createTx = function (opts, cb) {
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 || !wallet) 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(err);
self._doCreateTx(opts.copayerId, opts.toAddress, opts.amount, opts.changeAddress, utxos, function (err, tx) { var tx = self._doCreateTx(opts.copayerId, opts.toAddress, opts.amount, opts.changeAddress, utxos);
if (err) return cb('Could not create transaction');
self.storage.storeTx(tx, function (err) { self.storage.storeTx(tx, function (err) {
if (err) return cb(err); if (err) return cb(err);
return cb(null, tx); return cb(null, tx);
});
}); });
}); });
}); });
}; };
CopayServer.prototype._broadcastTx = function (tx, cb) {
cb = cb || function () {};
throw 'not implemented';
};
/** /**
* Sign a transaction proposal. * Sign a transaction proposal.
* @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 {string} opts.ntxid - The identifier of the transaction. * @param {string} opts.txProposalId - The identifier of the transaction.
* @param {string} opts.signature - The signature of the tx for this copayer. * @param {string} opts.signature - The signature of the tx for this copayer.
*/ */
CopayServer.prototype.signTx = function (opts, cb) { CopayServer.prototype.signTx = function (opts, cb) {
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 || !wallet) 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) { self.fetchTx(wallet.id, opts.txProposalId, function (err, tx) {
if (err) return cb('Could not create transaction'); if (err) return cb(err);
if (!tx) return cb('Transaction proposal not found');
var action = _.find(tx.actions, { copayerId: opts.copayerId });
if (action) return cb('Copayer already acted upon this transaction proposal');
if (tx.status != 'pending') return cb('The transaction proposal is not pending');
tx.sign(opts.copayerId, opts.signature);
if (tx.isRejected()) {
tx.status = 'rejected';
} else if (tx.isAccepted()) {
tx.status = 'accepted';
}
self.storage.storeTx(wallet.id, tx, function (err) {
if (err) return cb(err);
self.storage.storeTx(tx, function (err) { if (tx.status == 'accepted');
if (err) return cb(err); self._broadcastTx(tx);
return cb(null, tx); return cb();
});
}); });
}); });
}); });

13
lib/storage.js

@ -53,6 +53,15 @@ Storage.prototype.fetchCopayers = function (walletId, cb) {
}); });
}; };
Storage.prototype.fetchTx = function (walletId, txProposalId, cb) {
this.db.get('wallet-' + walletId + '-tx-' + txProposalId, function (err, data) {
if (err) {
if (err.notFound) return cb();
return cb(err);
}
return cb(null, TxProposal.fromObj(data));
});
};
Storage.prototype.storeWallet = function (wallet, cb) { Storage.prototype.storeWallet = function (wallet, cb) {
this.db.put('wallet-' + wallet.id, wallet, cb); this.db.put('wallet-' + wallet.id, wallet, cb);
@ -67,10 +76,10 @@ Storage.prototype.storeAddress = function (walletId, address, cb) {
}; };
Storage.prototype.storeTx = function (walletId, tx, cb) { Storage.prototype.storeTx = function (walletId, tx, cb) {
this.db.put('wallet-' + walletId + '-tx-' + tx.ntxid, tx, cb); this.db.put('wallet-' + walletId + '-tx-' + tx.txProposalId, tx, cb);
}; };
Storage.prototype.getAddresses = function (walletId, cb) { Storage.prototype.fetchAddresses = function (walletId, cb) {
var addresses = []; var addresses = [];
var key = 'wallet-' + walletId + '-address-'; var key = 'wallet-' + walletId + '-address-';
this.db.createReadStream({ gte: key, lt: key + '~' }) this.db.createReadStream({ gte: key, lt: key + '~' })

6
test/integration.js

@ -68,7 +68,7 @@ describe('Copay server', function() {
}); });
}); });
it('should return undefined when requesting non-existent wallet', function (done) { it('should fail when requesting non-existent wallet', function (done) {
var w1 = new Wallet({ var w1 = new Wallet({
id: '123', id: '123',
name: 'my wallet', name: 'my wallet',
@ -95,8 +95,8 @@ describe('Copay server', function() {
}]); }]);
server.getWallet({ id: '345' }, function (err, wallet) { server.getWallet({ id: '345' }, function (err, wallet) {
should.not.exist(err); should.exist(err);
should.not.exist(wallet); err.should.equal('Wallet not found');
done(); done();
}); });
}); });

Loading…
Cancel
Save