2015-01-27 05:18:45 -08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var _ = require('lodash');
|
2015-02-02 06:55:03 -08:00
|
|
|
var util = require('util');
|
2015-02-08 06:47:04 -08:00
|
|
|
var $ = require('preconditions').singleton();
|
2015-02-07 08:13:29 -08:00
|
|
|
var Uuid = require('uuid');
|
2015-02-02 11:16:14 -08:00
|
|
|
|
|
|
|
var Address = require('./address');
|
2015-01-28 09:21:09 -08:00
|
|
|
var Copayer = require('./copayer');
|
2015-02-03 10:44:44 -08:00
|
|
|
var AddressManager = require('./addressmanager');
|
2015-10-30 13:16:20 -07:00
|
|
|
|
2015-11-03 07:21:31 -08:00
|
|
|
var Constants = require('../common/constants');
|
2015-02-02 06:55:03 -08:00
|
|
|
|
2015-08-27 06:47:32 -07:00
|
|
|
function Wallet() {};
|
2015-02-17 15:58:04 -08:00
|
|
|
|
|
|
|
Wallet.create = function(opts) {
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
|
|
var x = new Wallet();
|
|
|
|
|
2015-08-27 06:47:32 -07:00
|
|
|
x.version = '1.0.0';
|
2015-02-17 15:58:04 -08:00
|
|
|
x.createdOn = Math.floor(Date.now() / 1000);
|
2015-03-31 13:28:01 -07:00
|
|
|
x.id = opts.id || Uuid.v4();
|
2015-02-17 15:58:04 -08:00
|
|
|
x.name = opts.name;
|
|
|
|
x.m = opts.m;
|
|
|
|
x.n = opts.n;
|
|
|
|
x.status = 'pending';
|
|
|
|
x.publicKeyRing = [];
|
|
|
|
x.addressIndex = 0;
|
|
|
|
x.copayers = [];
|
|
|
|
x.pubKey = opts.pubKey;
|
|
|
|
x.network = opts.network;
|
2015-10-30 11:24:47 -07:00
|
|
|
x.derivationStrategy = opts.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45;
|
|
|
|
x.addressType = opts.addressType || Constants.SCRIPT_TYPES.P2SH;
|
2015-09-04 20:50:51 -07:00
|
|
|
|
2015-08-27 13:14:33 -07:00
|
|
|
x.addressManager = AddressManager.create({
|
2015-08-31 13:13:46 -07:00
|
|
|
derivationStrategy: x.derivationStrategy,
|
2015-08-27 13:14:33 -07:00
|
|
|
});
|
2015-04-14 11:19:12 -07:00
|
|
|
x.scanStatus = null;
|
2015-02-17 15:58:04 -08:00
|
|
|
|
|
|
|
return x;
|
|
|
|
};
|
|
|
|
|
|
|
|
Wallet.fromObj = function(obj) {
|
|
|
|
var x = new Wallet();
|
|
|
|
|
2015-08-27 06:47:32 -07:00
|
|
|
x.version = obj.version;
|
2015-02-17 15:58:04 -08:00
|
|
|
x.createdOn = obj.createdOn;
|
|
|
|
x.id = obj.id;
|
|
|
|
x.name = obj.name;
|
|
|
|
x.m = obj.m;
|
|
|
|
x.n = obj.n;
|
|
|
|
x.status = obj.status;
|
|
|
|
x.publicKeyRing = obj.publicKeyRing;
|
|
|
|
x.copayers = _.map(obj.copayers, function(copayer) {
|
|
|
|
return Copayer.fromObj(copayer);
|
|
|
|
});
|
|
|
|
x.pubKey = obj.pubKey;
|
|
|
|
x.network = obj.network;
|
2015-10-30 11:24:47 -07:00
|
|
|
x.derivationStrategy = obj.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45;
|
|
|
|
x.addressType = obj.addressType || Constants.SCRIPT_TYPES.P2SH;
|
2015-02-17 15:58:04 -08:00
|
|
|
x.addressManager = AddressManager.fromObj(obj.addressManager);
|
2015-04-14 11:19:12 -07:00
|
|
|
x.scanStatus = obj.scanStatus;
|
2015-02-17 15:58:04 -08:00
|
|
|
|
|
|
|
return x;
|
2015-01-27 05:18:45 -08:00
|
|
|
};
|
|
|
|
|
2015-02-02 10:29:14 -08:00
|
|
|
/* For compressed keys, m*73 + n*34 <= 496 */
|
|
|
|
Wallet.COPAYER_PAIR_LIMITS = {
|
|
|
|
1: 1,
|
|
|
|
2: 2,
|
|
|
|
3: 3,
|
|
|
|
4: 4,
|
|
|
|
5: 4,
|
|
|
|
6: 4,
|
|
|
|
7: 3,
|
|
|
|
8: 3,
|
|
|
|
9: 2,
|
|
|
|
10: 2,
|
|
|
|
11: 1,
|
|
|
|
12: 1,
|
|
|
|
};
|
2015-02-01 11:50:58 -08:00
|
|
|
|
2015-02-02 10:29:14 -08:00
|
|
|
/**
|
|
|
|
* Get the maximum allowed number of required copayers.
|
|
|
|
* This is a limit imposed by the maximum allowed size of the scriptSig.
|
|
|
|
* @param {number} totalCopayers - the total number of copayers
|
|
|
|
* @return {number}
|
|
|
|
*/
|
|
|
|
Wallet.getMaxRequiredCopayers = function(totalCopayers) {
|
|
|
|
return Wallet.COPAYER_PAIR_LIMITS[totalCopayers];
|
|
|
|
};
|
2015-02-01 06:41:16 -08:00
|
|
|
|
2015-02-02 15:13:13 -08:00
|
|
|
Wallet.verifyCopayerLimits = function(m, n) {
|
2015-02-02 12:07:18 -08:00
|
|
|
return (n >= 1 && n <= 12) && (m >= 1 && m <= Wallet.COPAYER_PAIR_LIMITS[n]);
|
2015-02-01 11:50:58 -08:00
|
|
|
};
|
|
|
|
|
2015-02-10 05:22:23 -08:00
|
|
|
Wallet.prototype.isShared = function() {
|
|
|
|
return this.n > 1;
|
|
|
|
};
|
|
|
|
|
2015-04-01 12:10:03 -07:00
|
|
|
|
|
|
|
Wallet.prototype._updatePublicKeyRing = function() {
|
|
|
|
this.publicKeyRing = _.map(this.copayers, function(copayer) {
|
2015-08-05 16:32:20 -07:00
|
|
|
return _.pick(copayer, ['xPubKey', 'requestPubKey']);
|
2015-04-01 12:10:03 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-02-02 15:13:13 -08:00
|
|
|
Wallet.prototype.addCopayer = function(copayer) {
|
2015-01-28 09:21:09 -08:00
|
|
|
|
2015-04-01 12:10:03 -07:00
|
|
|
this.copayers.push(copayer);
|
2015-02-02 12:07:18 -08:00
|
|
|
if (this.copayers.length < this.n) return;
|
2015-02-02 15:13:13 -08:00
|
|
|
|
2015-02-02 12:07:18 -08:00
|
|
|
this.status = 'complete';
|
2015-04-01 12:10:03 -07:00
|
|
|
this._updatePublicKeyRing();
|
2015-01-28 09:21:09 -08:00
|
|
|
};
|
|
|
|
|
2015-08-10 11:07:20 -07:00
|
|
|
Wallet.prototype.addCopayerRequestKey = function(copayerId, requestPubKey, signature, restrictions, name) {
|
2015-03-31 13:28:01 -07:00
|
|
|
$.checkState(this.copayers.length == this.n);
|
|
|
|
|
2015-08-05 12:53:06 -07:00
|
|
|
var c = this.getCopayer(copayerId);
|
2015-08-04 07:19:08 -07:00
|
|
|
|
2015-08-04 17:05:26 -07:00
|
|
|
//new ones go first
|
|
|
|
c.requestPubKeys.unshift({
|
2015-08-05 12:53:06 -07:00
|
|
|
key: requestPubKey.toString(),
|
2015-08-04 17:05:26 -07:00
|
|
|
signature: signature,
|
2015-08-05 12:53:06 -07:00
|
|
|
selfSigned: true,
|
2015-08-10 11:07:20 -07:00
|
|
|
restrictions: restrictions || {},
|
|
|
|
name: name || null,
|
2015-08-04 17:05:26 -07:00
|
|
|
});
|
2015-03-31 13:28:01 -07:00
|
|
|
};
|
|
|
|
|
2015-02-02 15:13:13 -08:00
|
|
|
Wallet.prototype.getCopayer = function(copayerId) {
|
|
|
|
return _.find(this.copayers, {
|
|
|
|
id: copayerId
|
|
|
|
});
|
2015-01-28 09:21:09 -08:00
|
|
|
};
|
|
|
|
|
2015-02-05 12:22:38 -08:00
|
|
|
Wallet.prototype.getNetworkName = function() {
|
2015-02-16 06:17:44 -08:00
|
|
|
return this.network;
|
2015-02-02 11:16:14 -08:00
|
|
|
};
|
|
|
|
|
2015-02-08 06:47:04 -08:00
|
|
|
Wallet.prototype.isComplete = function() {
|
|
|
|
return this.status == 'complete';
|
|
|
|
};
|
|
|
|
|
2015-04-02 07:18:39 -07:00
|
|
|
Wallet.prototype.isScanning = function() {
|
|
|
|
return this.scanning;
|
|
|
|
};
|
|
|
|
|
2015-02-03 10:44:44 -08:00
|
|
|
Wallet.prototype.createAddress = function(isChange) {
|
2015-02-08 06:47:04 -08:00
|
|
|
$.checkState(this.isComplete());
|
|
|
|
|
2015-09-07 13:46:45 -07:00
|
|
|
var self = this;
|
|
|
|
|
2015-02-03 10:44:44 -08:00
|
|
|
var path = this.addressManager.getNewAddressPath(isChange);
|
2015-11-03 07:21:31 -08:00
|
|
|
var address = Address.derive(self.id, this.addressType, this.publicKeyRing, path, this.m, this.network, isChange);
|
2015-02-21 14:13:15 -08:00
|
|
|
return address;
|
2015-02-02 11:16:14 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-01-27 05:18:45 -08:00
|
|
|
module.exports = Wallet;
|