'use strict'; var _ = require('lodash'); var util = require('util'); var $ = require('preconditions').singleton(); var Uuid = require('uuid'); var Address = require('./address'); var Copayer = require('./copayer'); var AddressManager = require('./addressmanager'); var Constants = require('../common/constants'); function Wallet() {}; Wallet.create = function(opts) { opts = opts || {}; var x = new Wallet(); $.shouldBeNumber(opts.m); $.shouldBeNumber(opts.n); x.version = '1.0.0'; x.createdOn = Math.floor(Date.now() / 1000); x.id = opts.id || Uuid.v4(); x.name = opts.name; x.m = opts.m; x.n = opts.n; x.singleAddress = !!opts.singleAddress; x.status = 'pending'; x.publicKeyRing = []; x.addressIndex = 0; x.copayers = []; x.pubKey = opts.pubKey; x.network = opts.network; x.derivationStrategy = opts.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45; x.addressType = opts.addressType || Constants.SCRIPT_TYPES.P2SH; x.addressManager = AddressManager.create({ derivationStrategy: x.derivationStrategy, }); x.scanStatus = null; return x; }; Wallet.fromObj = function(obj) { var x = new Wallet(); $.shouldBeNumber(obj.m); $.shouldBeNumber(obj.n); x.version = obj.version; x.createdOn = obj.createdOn; x.id = obj.id; x.name = obj.name; x.m = obj.m; x.n = obj.n; x.singleAddress = !!obj.singleAddress; 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; x.derivationStrategy = obj.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45; x.addressType = obj.addressType || Constants.SCRIPT_TYPES.P2SH; x.addressManager = AddressManager.fromObj(obj.addressManager); x.scanStatus = obj.scanStatus; return x; }; Wallet.prototype.toObject = function() { var x = _.cloneDeep(this); x.isShared = this.isShared(); return x; }; /** * 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]; }; Wallet.verifyCopayerLimits = function(m, n) { return (n >= 1 && n <= 15) && (m >= 1 && m <= n); }; Wallet.prototype.isShared = function() { return this.n > 1; }; Wallet.prototype._updatePublicKeyRing = function() { this.publicKeyRing = _.map(this.copayers, function(copayer) { return _.pick(copayer, ['xPubKey', 'requestPubKey']); }); }; Wallet.prototype.addCopayer = function(copayer) { this.copayers.push(copayer); if (this.copayers.length < this.n) return; this.status = 'complete'; this._updatePublicKeyRing(); }; Wallet.prototype.addCopayerRequestKey = function(copayerId, requestPubKey, signature, restrictions, name) { $.checkState(this.copayers.length == this.n); var c = this.getCopayer(copayerId); //new ones go first c.requestPubKeys.unshift({ key: requestPubKey.toString(), signature: signature, selfSigned: true, restrictions: restrictions || {}, name: name || null, }); }; Wallet.prototype.getCopayer = function(copayerId) { return _.find(this.copayers, { id: copayerId }); }; Wallet.prototype.getNetworkName = function() { return this.network; }; Wallet.prototype.isComplete = function() { return this.status == 'complete'; }; Wallet.prototype.isScanning = function() { return this.scanning; }; Wallet.prototype.createAddress = function(isChange) { $.checkState(this.isComplete()); var self = this; var path = this.addressManager.getNewAddressPath(isChange); var address = Address.derive(self.id, this.addressType, this.publicKeyRing, path, this.m, this.network, isChange); return address; }; module.exports = Wallet;