diff --git a/copay.js b/copay.js index fe6b4dbbd..8e8b03089 100644 --- a/copay.js +++ b/copay.js @@ -3,8 +3,8 @@ module.exports.PublicKeyRing = require('./js/models/core/PublicKeyRing'); module.exports.TxProposals = require('./js/models/core/TxProposals'); module.exports.PrivateKey = require('./js/models/core/PrivateKey'); module.exports.Passphrase = require('./js/models/core/Passphrase'); -module.exports.Structure = require('./js/models/core/Structure'); -module.exports.AddressIndex = require('./js/models/core/AddressIndex'); +module.exports.HDPath = require('./js/models/core/HDPath'); +module.exports.HDParams = require('./js/models/core/HDParams'); // components diff --git a/js/controllers/uriPayment.js b/js/controllers/uriPayment.js index 18c02d000..c6bd02f8d 100644 --- a/js/controllers/uriPayment.js +++ b/js/controllers/uriPayment.js @@ -2,7 +2,7 @@ angular.module('copayApp.controllers').controller('UriPaymentController', function($rootScope, $scope, $routeParams, $timeout, $location) { var data = decodeURIComponent($routeParams.data); - $rootScope.pendingPayment = copay.Structure.parseBitcoinURI($routeParams.data); + $rootScope.pendingPayment = copay.HDPath.parseBitcoinURI($routeParams.data); $scope.protocol = $rootScope.pendingPayment.protocol; $scope.address = $rootScope.pendingPayment.address; diff --git a/js/models/core/AddressIndex.js b/js/models/core/AddressIndex.js deleted file mode 100644 index d6f953a53..000000000 --- a/js/models/core/AddressIndex.js +++ /dev/null @@ -1,99 +0,0 @@ -'use strict'; - -var imports = require('soop').imports(); -var preconditions = require('preconditions').singleton(); -var Structure = require('./Structure'); - -function AddressIndex(opts) { - opts = opts || {}; - this.cosigner = opts.cosigner - this.changeIndex = opts.changeIndex || 0; - this.receiveIndex = opts.receiveIndex || 0; - - if (typeof this.cosigner === 'undefined') { - this.cosigner = Structure.SHARED_INDEX; - } -} - -AddressIndex.init = function(totalCopayers) { - preconditions.shouldBeNumber(totalCopayers); - var indexes = [new AddressIndex()]; - for (var i = 0 ; i < totalCopayers ; i++) { - indexes.push(new AddressIndex({cosigner: i})); - } - return indexes; -} - -AddressIndex.fromList = function(indexes) { - return indexes.map(function(i) { return AddressIndex.fromObj(i); }); -} - -AddressIndex.fromObj = function(data) { - if (data instanceof AddressIndex) { - throw new Error('bad data format: Did you use .toObj()?'); - } - return new AddressIndex(data); -}; - -AddressIndex.serialize = function(indexes) { - return indexes.map(function(i) { return i.toObj(); }); -} - -AddressIndex.update = function(shared, totalCopayers) { - var indexes = this.init(totalCopayers); - indexes[0].changeIndex = shared.changeIndex; - indexes[0].receiveIndex = shared.receiveIndex; - return this.serialize(indexes); -}; - -AddressIndex.prototype.toObj = function() { - return { - cosigner: this.cosigner, - changeIndex: this.changeIndex, - receiveIndex: this.receiveIndex - }; -}; - -AddressIndex.prototype.checkRange = function(index, isChange) { - if ((isChange && index > this.changeIndex) || - (!isChange && index > this.receiveIndex)) { - throw new Error('Out of bounds at index ' + index + ' isChange: ' + isChange); - } -}; - -AddressIndex.prototype.getChangeIndex = function() { - return this.changeIndex; -}; - -AddressIndex.prototype.getReceiveIndex = function() { - return this.receiveIndex; -}; - -AddressIndex.prototype.increment = function(isChange) { - if (isChange) { - this.changeIndex++; - } else { - this.receiveIndex++; - } -}; - -AddressIndex.prototype.merge = function(inAddressIndex) { - preconditions.shouldBeObject(inAddressIndex) - .checkArgument(this.cosigner == inAddressIndex.cosigner); - - var hasChanged = false; - - // Indexes - if (inAddressIndex.changeIndex > this.changeIndex) { - this.changeIndex = inAddressIndex.changeIndex; - hasChanged = true; - } - - if (inAddressIndex.receiveIndex > this.receiveIndex) { - this.receiveIndex = inAddressIndex.receiveIndex; - hasChanged = true; - } - return hasChanged; -}; - -module.exports = require('soop')(AddressIndex); diff --git a/js/models/core/HDParams.js b/js/models/core/HDParams.js new file mode 100644 index 000000000..9c84108b9 --- /dev/null +++ b/js/models/core/HDParams.js @@ -0,0 +1,99 @@ +'use strict'; + +var preconditions = require('preconditions').singleton(); +var HDPath = require('./HDPath'); + +function HDParams(opts) { + opts = opts || {}; + + //opts.cosigner is for backwards compatibility only + this.copayerIndex = typeof opts.copayerIndex === 'undefined' ? opts.cosigner : opts.copayerIndex; + this.changeIndex = opts.changeIndex || 0; + this.receiveIndex = opts.receiveIndex || 0; + + if (typeof this.copayerIndex === 'undefined') { + this.copayerIndex = HDPath.SHARED_INDEX; + } +} + +HDParams.init = function(totalCopayers) { + preconditions.shouldBeNumber(totalCopayers); + var ret = [new HDParams()]; + for (var i = 0 ; i < totalCopayers ; i++) { + ret.push(new HDParams({copayerIndex: i})); + } + return ret; +} + +HDParams.fromList = function(hdParams) { + return hdParams.map(function(i) { return HDParams.fromObj(i); }); +} + +HDParams.fromObj = function(data) { + if (data instanceof HDParams) { + throw new Error('bad data format: Did you use .toObj()?'); + } + return new HDParams(data); +}; + +HDParams.serialize = function(hdParams) { + return hdParams.map(function(i) { return i.toObj(); }); +} + +HDParams.update = function(shared, totalCopayers) { + var hdParams = this.init(totalCopayers); + hdParams[0].changeIndex = shared.changeIndex; + hdParams[0].receiveIndex = shared.receiveIndex; + return this.serialize(hdParams); +}; + +HDParams.prototype.toObj = function() { + return { + copayerIndex: this.copayerIndex, + changeIndex: this.changeIndex, + receiveIndex: this.receiveIndex + }; +}; + +HDParams.prototype.checkRange = function(index, isChange) { + if ((isChange && index > this.changeIndex) || + (!isChange && index > this.receiveIndex)) { + throw new Error('Out of bounds at index ' + index + ' isChange: ' + isChange); + } +}; + +HDParams.prototype.getChangeIndex = function() { + return this.changeIndex; +}; + +HDParams.prototype.getReceiveIndex = function() { + return this.receiveIndex; +}; + +HDParams.prototype.increment = function(isChange) { + if (isChange) { + this.changeIndex++; + } else { + this.receiveIndex++; + } +}; + +HDParams.prototype.merge = function(inHDParams) { + preconditions.shouldBeObject(inHDParams) + .checkArgument(this.copayerIndex == inHDParams.copayerIndex); + + var hasChanged = false; + + if (inHDParams.changeIndex > this.changeIndex) { + this.changeIndex = inHDParams.changeIndex; + hasChanged = true; + } + + if (inHDParams.receiveIndex > this.receiveIndex) { + this.receiveIndex = inHDParams.receiveIndex; + hasChanged = true; + } + return hasChanged; +}; + +module.exports = HDParams; diff --git a/js/models/core/HDPath.js b/js/models/core/HDPath.js new file mode 100644 index 000000000..2e32ade7e --- /dev/null +++ b/js/models/core/HDPath.js @@ -0,0 +1,75 @@ +'use strict'; + +var preconditions = require('preconditions').singleton(); + +function HDPath() {} + +/* + * Based on https://github.com/maraoz/bips/blob/master/bip-NNNN.mediawiki + * m / purpose' / copayerIndex / change / addressIndex + */ +var PURPOSE = 45; +var MAX_NON_HARDENED = 0x80000000 - 1; + +var SHARED_INDEX = MAX_NON_HARDENED - 0; +var ID_INDEX = MAX_NON_HARDENED - 1; + +var BIP45_PUBLIC_PREFIX = 'm/' + PURPOSE + '\''; +HDPath.BIP45_PUBLIC_PREFIX = BIP45_PUBLIC_PREFIX; + +HDPath.Branch = function(addressIndex, isChange, copayerIndex) { + preconditions.shouldBeNumber(addressIndex); + preconditions.shouldBeBoolean(isChange); + var ret = 'm/' + + (typeof copayerIndex !== 'undefined' ? copayerIndex : SHARED_INDEX) + '/' + + (isChange ? 1 : 0) + '/' + + addressIndex; + return ret; +}; + +HDPath.FullBranch = function(addressIndex, isChange, copayerIndex) { + var sub = HDPath.Branch(addressIndex, isChange, copayerIndex); + sub = sub.substring(2); + return BIP45_PUBLIC_PREFIX + '/' + sub; +}; + +HDPath.indicesForPath = function(path) { + preconditions.shouldBeString(path); + var s = path.split('/'); + return { + isChange: s[3] === '1', + index: parseInt(s[4]), + copayerIndex: parseInt(s[2]) + }; +}; + +HDPath.IdFullBranch = HDPath.FullBranch(0, false, ID_INDEX); +HDPath.IdBranch = HDPath.Branch(0, false, ID_INDEX); +HDPath.PURPOSE = PURPOSE; +HDPath.MAX_NON_HARDENED = MAX_NON_HARDENED; +HDPath.SHARED_INDEX = SHARED_INDEX; +HDPath.ID_INDEX = ID_INDEX; + +HDPath.parseBitcoinURI = function(uri) { + var ret = {}; + var data = decodeURIComponent(uri); + var splitDots = data.split(':'); + ret.protocol = splitDots[0]; + data = splitDots[1]; + var splitQuestion = data.split('?'); + ret.address = splitQuestion[0]; + + if (splitQuestion.length > 1) { + var search = splitQuestion[1]; + data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}', + function(key, value) { + return key === "" ? value : decodeURIComponent(value); + }); + ret.amount = parseFloat(data.amount); + ret.message = data.message; + } + + return ret; +}; + +module.exports = HDPath; diff --git a/js/models/core/PrivateKey.js b/js/models/core/PrivateKey.js index 9abd30e95..25c57fd60 100644 --- a/js/models/core/PrivateKey.js +++ b/js/models/core/PrivateKey.js @@ -1,13 +1,12 @@ 'use strict'; -var imports = require('soop').imports(); var bitcore = require('bitcore'); var HK = bitcore.HierarchicalKey; var WalletKey = bitcore.WalletKey; var networks = bitcore.networks; var util = bitcore.util; -var Structure = require('./Structure'); +var HDPath = require('./HDPath'); function PrivateKey(opts) { opts = opts || {}; @@ -41,7 +40,7 @@ PrivateKey.prototype.getIdKey = function() { }; PrivateKey.prototype.cacheId = function() { - var path = Structure.IdFullBranch; + var path = HDPath.IdFullBranch; var idhk = this.bip.derive(path); this.idkey = idhk.eckey; this.id = idhk.eckey.public.toString('hex'); @@ -50,7 +49,7 @@ PrivateKey.prototype.cacheId = function() { PrivateKey.prototype.deriveBIP45Branch = function() { if (!this.bip45Branch) { - this.bip45Branch = this.bip.derive(Structure.BIP45_PUBLIC_PREFIX); + this.bip45Branch = this.bip.derive(HDPath.BIP45_PUBLIC_PREFIX); } return this.bip45Branch; } @@ -103,7 +102,7 @@ PrivateKey.prototype.getForPath = function(path) { }; PrivateKey.prototype.get = function(index, isChange, cosigner) { - var path = Structure.FullBranch(index, isChange, cosigner); + var path = HDPath.FullBranch(index, isChange, cosigner); return this.getForPath(path); }; @@ -123,4 +122,4 @@ PrivateKey.prototype.getAll = function(receiveIndex, changeIndex, cosigner) { -module.exports = require('soop')(PrivateKey); +module.exports = PrivateKey; diff --git a/js/models/core/PublicKeyRing.js b/js/models/core/PublicKeyRing.js index 423c083eb..ad7fe6f15 100644 --- a/js/models/core/PublicKeyRing.js +++ b/js/models/core/PublicKeyRing.js @@ -1,13 +1,12 @@ 'use strict'; -var imports = require('soop').imports(); var preconditions = require('preconditions').instance(); var bitcore = require('bitcore'); var HK = bitcore.HierarchicalKey; var PrivateKey = require('./PrivateKey'); -var Structure = require('./Structure'); -var AddressIndex = require('./AddressIndex'); +var HDPath = require('./HDPath'); +var HDParams = require('./HDParams'); var Address = bitcore.Address; var Script = bitcore.Script; @@ -24,14 +23,14 @@ function PublicKeyRing(opts) { this.copayersHK = opts.copayersHK || []; - this.indexes = opts.indexes ? AddressIndex.fromList(opts.indexes) - : AddressIndex.init(this.totalCopayers); + this.indexes = opts.indexes ? HDParams.fromList(opts.indexes) + : HDParams.init(this.totalCopayers); - this.publicKeysCache = opts.publicKeysCache || {}; - this.nicknameFor = opts.nicknameFor || {}; - this.copayerIds = []; - this.copayersBackup = opts.copayersBackup || []; - this.addressToPath = {}; + this.publicKeysCache = opts.publicKeysCache || {}; + this.nicknameFor = opts.nicknameFor || {}; + this.copayerIds = []; + this.copayersBackup = opts.copayersBackup || []; + this.addressToPath = {}; } PublicKeyRing.fromObj = function(data) { @@ -41,7 +40,7 @@ PublicKeyRing.fromObj = function(data) { // Support old indexes schema if (!Array.isArray(data.indexes)) { - data.indexes = AddressIndex.update(data.indexes, data.totalCopayers); + data.indexes = HDParams.update(data.indexes, data.totalCopayers); } var ret = new PublicKeyRing(data); @@ -59,7 +58,7 @@ PublicKeyRing.prototype.toObj = function() { networkName: this.network.name, requiredCopayers: this.requiredCopayers, totalCopayers: this.totalCopayers, - indexes: AddressIndex.serialize(this.indexes), + indexes: HDParams.serialize(this.indexes), copayersBackup: this.copayersBackup, copayersExtPubKeys: this.copayersHK.map(function(b) { @@ -103,14 +102,14 @@ PublicKeyRing.prototype._checkKeys = function() { PublicKeyRing.prototype._newExtendedPublicKey = function() { return new PrivateKey({ - networkName: this.network.name - }) - .deriveBIP45Branch() - .extendedPublicKeyString(); + networkName: this.network.name + }) + .deriveBIP45Branch() + .extendedPublicKeyString(); }; PublicKeyRing.prototype._updateBip = function(index) { - var hk = this.copayersHK[index].derive(Structure.IdBranch); + var hk = this.copayersHK[index].derive(HDPath.IdBranch); this.copayerIds[index] = hk.eckey.public.toString('hex'); }; @@ -149,10 +148,10 @@ PublicKeyRing.prototype.addCopayer = function(newEpk, nickname) { return newEpk; }; -PublicKeyRing.prototype.getPubKeys = function(index, isChange, cosigner) { +PublicKeyRing.prototype.getPubKeys = function(index, isChange, copayerIndex) { this._checkKeys(); - var path = Structure.Branch(index, isChange, cosigner); + var path = HDPath.Branch(index, isChange, copayerIndex); var pubKeys = this.publicKeysCache[path]; if (!pubKeys) { pubKeys = []; @@ -175,26 +174,27 @@ PublicKeyRing.prototype.getPubKeys = function(index, isChange, cosigner) { }; // TODO this could be cached -PublicKeyRing.prototype.getRedeemScript = function(index, isChange, cosigner) { - var pubKeys = this.getPubKeys(index, isChange, cosigner); +PublicKeyRing.prototype.getRedeemScript = function(index, isChange, copayerIndex) { + var pubKeys = this.getPubKeys(index, isChange, copayerIndex); var script = Script.createMultisig(this.requiredCopayers, pubKeys); return script; }; // TODO this could be cached PublicKeyRing.prototype.getAddress = function(index, isChange, id) { - var cosigner = this.getCosigner(id); - var script = this.getRedeemScript(index, isChange, cosigner); + var copayerIndex = this.getCosigner(id); + var script = this.getRedeemScript(index, isChange, copayerIndex); var address = Address.fromScript(script, this.network.name); - this.addressToPath[address.toString()] = Structure.FullBranch(index, isChange, cosigner); + this.addressToPath[address.toString()] = HDPath.FullBranch(index, isChange, copayerIndex); return address; }; // Overloaded to receive a PubkeyString or a consigner index -PublicKeyRing.prototype.getIndex = function(id) { - var cosigner = this.getCosigner(id); - var index = this.indexes.filter(function(i) { return i.cosigner == cosigner }); - if (index.length != 1) throw new Error('no index for cosigner'); +PublicKeyRing.prototype.getHDParams = function(id) { + var copayerIndex = this.getCosigner(id); + var index = this.indexes.filter(function(i) { return i.copayerIndex == copayerIndex }); + if (index.length != 1) throw new Error('no index for copayerIndex'); + return index[0]; }; @@ -206,18 +206,18 @@ PublicKeyRing.prototype.pathForAddress = function(address) { // TODO this could be cached PublicKeyRing.prototype.getScriptPubKeyHex = function(index, isChange, pubkey) { - var cosigner = this.getCosigner(pubkey); - var addr = this.getAddress(index, isChange, cosigner); + var copayerIndex = this.getCosigner(pubkey); + var addr = this.getAddress(index, isChange, copayerIndex); return Script.createP2SH(addr.payload()).getBuffer().toString('hex'); }; //generate a new address, update index. PublicKeyRing.prototype.generateAddress = function(isChange, pubkey) { isChange = !!isChange; - var addrIndex = this.getIndex(pubkey); - var index = isChange ? addrIndex.getChangeIndex() : addrIndex.getReceiveIndex(); - var ret = this.getAddress(index, isChange, addrIndex.cosigner); - addrIndex.increment(isChange); + var HDParams = this.getHDParams(pubkey); + var index = isChange ? HDParams.getChangeIndex() : HDParams.getReceiveIndex(); + var ret = this.getAddress(index, isChange, HDParams.copayerIndex); + HDParams.increment(isChange); return ret; }; @@ -228,7 +228,7 @@ PublicKeyRing.prototype.getAddresses = function(opts) { }; PublicKeyRing.prototype.getCosigner = function(pubKey) { - if (typeof pubKey == 'undefined') return Structure.SHARED_INDEX; + if (typeof pubKey == 'undefined') return HDPath.SHARED_INDEX; if (typeof pubKey == 'number') return pubKey; var sorted = this.copayersHK.map(function(h, i){ @@ -245,51 +245,51 @@ PublicKeyRing.prototype.getCosigner = function(pubKey) { PublicKeyRing.prototype.getAddressesInfo = function(opts, pubkey) { var ret = []; var self = this; - var cosigner = pubkey && this.getCosigner(pubkey); + var copayerIndex = pubkey && this.getCosigner(pubkey); this.indexes.forEach(function(index) { - ret = ret.concat(self.getAddressesInfoForIndex(index, opts, cosigner)); + ret = ret.concat(self.getAddressesInfoForIndex(index, opts, copayerIndex)); }); return ret; } -PublicKeyRing.prototype.getAddressesInfoForIndex = function(index, opts, cosigner) { +PublicKeyRing.prototype.getAddressesInfoForIndex = function(index, opts, copayerIndex) { opts = opts || {}; - var isOwned = index.cosigner == Structure.SHARED_INDEX - || index.cosigner == cosigner; + var isOwned = index.copayerIndex == HDPath.SHARED_INDEX + || index.copayerIndex == copayerIndex; - var ret = []; - if (!opts.excludeChange) { - for (var i = 0; i < index.changeIndex; i++) { - var a = this.getAddress(i, true, index.cosigner); - ret.unshift({ - address: a, - addressStr: a.toString(), - isChange: true, - owned: isOwned - }); + var ret = []; + if (!opts.excludeChange) { + for (var i = 0; i < index.changeIndex; i++) { + var a = this.getAddress(i, true, index.copayerIndex); + ret.unshift({ + address: a, + addressStr: a.toString(), + isChange: true, + owned: isOwned + }); + } } - } - if (!opts.excludeMain) { - for (var i = 0; i < index.receiveIndex; i++) { - var a = this.getAddress(i, false, index.cosigner); - ret.unshift({ - address: a, - addressStr: a.toString(), - isChange: false, - owned: isOwned - }); + if (!opts.excludeMain) { + for (var i = 0; i < index.receiveIndex; i++) { + var a = this.getAddress(i, false, index.copayerIndex); + ret.unshift({ + address: a, + addressStr: a.toString(), + isChange: false, + owned: isOwned + }); + } } - } - return ret; + return ret; }; // TODO this could be cached PublicKeyRing.prototype._addScriptMap = function(map, path) { - var p = Structure.indicesForPath(path); - var script = this.getRedeemScript(p.index, p.isChange, p.cosigner); + var p = HDPath.indicesForPath(path); + var script = this.getRedeemScript(p.index, p.isChange, p.copayerIndex); map[Address.fromScript(script, this.network.name).toString()] = script.getBuffer().toString('hex'); }; @@ -310,18 +310,18 @@ PublicKeyRing.prototype._checkInPKR = function(inPKR, ignoreId) { if (this.network.name !== inPKR.network.name) { throw new Error('Network mismatch. Should be ' + this.network.name + - ' and found ' + inPKR.network.name); + ' and found ' + inPKR.network.name); } if ( this.requiredCopayers && inPKR.requiredCopayers && - (this.requiredCopayers !== inPKR.requiredCopayers)) - throw new Error('inPKR requiredCopayers mismatch ' + this.requiredCopayers + '!=' + inPKR.requiredCopayers); + (this.requiredCopayers !== inPKR.requiredCopayers)) + throw new Error('inPKR requiredCopayers mismatch ' + this.requiredCopayers + '!=' + inPKR.requiredCopayers); - if ( - this.totalCopayers && inPKR.totalCopayers && - (this.totalCopayers !== inPKR.totalCopayers)) - throw new Error('inPKR totalCopayers mismatch' + this.totalCopayers + '!=' + inPKR.requiredCopayers); + if ( + this.totalCopayers && inPKR.totalCopayers && + (this.totalCopayers !== inPKR.totalCopayers)) + throw new Error('inPKR totalCopayers mismatch' + this.totalCopayers + '!=' + inPKR.requiredCopayers); }; @@ -393,7 +393,7 @@ PublicKeyRing.prototype.mergeIndexes = function(indexes) { var hasChanged = false; indexes.forEach(function(theirs) { - var mine = self.getIndex(theirs.cosigner); + var mine = self.getHDParams(theirs.copayerIndex); hasChanged |= mine.merge(theirs); }); @@ -414,4 +414,4 @@ PublicKeyRing.prototype.mergeBackups = function(backups) { } -module.exports = require('soop')(PublicKeyRing); +module.exports = PublicKeyRing; diff --git a/js/models/core/Structure.js b/js/models/core/Structure.js deleted file mode 100644 index 54b51e4da..000000000 --- a/js/models/core/Structure.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -var imports = require('soop').imports(); -var preconditions = require('preconditions').singleton(); - -function Structure() {} - -/* - * Based on https://github.com/maraoz/bips/blob/master/bip-NNNN.mediawiki - * m / purpose' / cosigner_index / change / address_index - */ -var PURPOSE = 45; -var MAX_NON_HARDENED = 0x80000000 - 1; - -var SHARED_INDEX = MAX_NON_HARDENED - 0; -var ID_INDEX = MAX_NON_HARDENED - 1; - -var BIP45_PUBLIC_PREFIX = 'm/' + PURPOSE + '\''; -Structure.BIP45_PUBLIC_PREFIX = BIP45_PUBLIC_PREFIX; - -Structure.Branch = function(address_index, isChange, cosigner_index) { - preconditions.shouldBeNumber(address_index); - preconditions.shouldBeBoolean(isChange); - var ret = 'm/' + - (typeof cosigner_index !== 'undefined' ? cosigner_index : SHARED_INDEX) + '/' + - (isChange ? 1 : 0) + '/' + - address_index; - return ret; -}; - -Structure.FullBranch = function(address_index, isChange, cosigner_index) { - var sub = Structure.Branch(address_index, isChange, cosigner_index); - sub = sub.substring(2); - return BIP45_PUBLIC_PREFIX + '/' + sub; -}; - -Structure.indicesForPath = function(path) { - preconditions.shouldBeString(path); - var s = path.split('/'); - return { - isChange: s[3] === '1', - index: parseInt(s[4]), - cosigner: parseInt(s[2]) - }; -}; - -Structure.IdFullBranch = Structure.FullBranch(0, false, ID_INDEX); -Structure.IdBranch = Structure.Branch(0, false, ID_INDEX); -Structure.PURPOSE = PURPOSE; -Structure.MAX_NON_HARDENED = MAX_NON_HARDENED; -Structure.SHARED_INDEX = SHARED_INDEX; -Structure.ID_INDEX = ID_INDEX; - -Structure.parseBitcoinURI = function(uri) { - var ret = {}; - var data = decodeURIComponent(uri); - var splitDots = data.split(':'); - ret.protocol = splitDots[0]; - data = splitDots[1]; - var splitQuestion = data.split('?'); - ret.address = splitQuestion[0]; - - if (splitQuestion.length > 1) { - var search = splitQuestion[1]; - data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}', - function(key, value) { - return key === "" ? value : decodeURIComponent(value); - }); - ret.amount = parseFloat(data.amount); - ret.message = data.message; - } - - return ret; -}; - -module.exports = require('soop')(Structure); diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index cf5e13839..db37355cf 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -15,7 +15,7 @@ var SecureRandom = bitcore.SecureRandom; var Base58Check = bitcore.Base58.base58Check; var Address = bitcore.Address; -var AddressIndex = require('./AddressIndex'); +var HDParams = require('./HDParams'); var PublicKeyRing = require('./PublicKeyRing'); var TxProposals = require('./TxProposals'); var PrivateKey = require('./PrivateKey'); @@ -93,7 +93,7 @@ Wallet.prototype.connectToAll = function() { Wallet.prototype._handleIndexes = function(senderId, data, isInbound) { this.log('RECV INDEXES:', data); - var inIndexes = AddressIndex.fromList(data.indexes); + var inIndexes = HDParams.fromList(data.indexes); var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes); if (hasChanged) { this.emit('publicKeyRingUpdated'); @@ -132,8 +132,10 @@ Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) { Wallet.prototype._handleTxProposal = function(senderId, data) { this.log('RECV TXPROPOSAL: ', data); - var inTxp = TxProposals.TxProposal.fromObj(data.txProposal, Wallet.builderOpts); + + + var valid = inTxp.isValid(); if (!valid) { var corruptEvent = { @@ -458,7 +460,7 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) { }); }; Wallet.prototype.sendIndexes = function(recipients) { - var indexes = AddressIndex.serialize(this.publicKeyRing.indexes); + var indexes = HDParams.serialize(this.publicKeyRing.indexes); this.log('### INDEXES TO:', recipients || 'All', indexes); this.send(recipients, { diff --git a/test/test.AddressIndex.js b/test/test.HDParams.js similarity index 68% rename from test/test.AddressIndex.js rename to test/test.HDParams.js index 2ba38bfd0..c55b3365a 100644 --- a/test/test.AddressIndex.js +++ b/test/test.HDParams.js @@ -11,8 +11,8 @@ try { var copay = require('../copay'); //node } var PublicKeyRing = copay.PublicKeyRing; -var AddressIndex = copay.AddressIndex; -var Structure = copay.Structure; +var HDParams = copay.HDParams; +var HDPath = copay.HDPath; var config = { @@ -20,42 +20,42 @@ var config = { }; var createAI = function() { - var i = new AddressIndex(); + var i = new HDParams(); should.exist(i); - i.cosigner = 1; + i.copayerIndex = 1; return i; }; -describe('AddressIndex model', function() { +describe('HDParams model', function() { it('should create an instance (livenet)', function() { - var i = new AddressIndex(); + var i = new HDParams(); should.exist(i); }); it('should init indexes', function() { - var is = AddressIndex.init(2); + var is = HDParams.init(2); should.exist(is); is.length.should.equal(3); - var cosigners = is.map(function(i) { return i.cosigner; }); - cosigners.indexOf(Structure.SHARED_INDEX).should.not.equal(-1); + var cosigners = is.map(function(i) { return i.copayerIndex; }); + cosigners.indexOf(HDPath.SHARED_INDEX).should.not.equal(-1); cosigners.indexOf(0).should.not.equal(-1); cosigners.indexOf(1).should.not.equal(-1); cosigners.indexOf(2).should.equal(-1); }); it('should serialize to object list and back', function() { - var is = AddressIndex.init(3); + var is = HDParams.init(3); should.exist(is); is.length.should.equal(4); - var list = AddressIndex.serialize(is); + var list = HDParams.serialize(is); list.length.should.equal(4); - var is2 = AddressIndex.fromList(list); + var is2 = HDParams.fromList(list); is2.length.should.equal(4); }); @@ -73,8 +73,8 @@ describe('AddressIndex model', function() { var data = i.toObj(); should.exist(data); - var i2 = AddressIndex.fromObj(data); - i2.cosigner.should.equal(i.cosigner); + var i2 = HDParams.fromObj(data); + i2.copayerIndex.should.equal(i.copayerIndex); i2.getChangeIndex().should.equal(changeN); i2.getReceiveIndex().should.equal(addressN); @@ -83,9 +83,9 @@ describe('AddressIndex model', function() { it('should count generation indexes', function() { var j = createAI(); for (var i = 0; i < 3; i++) - j.increment(true); + j.increment(true); for (var i = 0; i < 2; i++) - j.increment(false); + j.increment(false); j.changeIndex.should.equal(3); j.receiveIndex.should.equal(2); @@ -95,11 +95,11 @@ describe('AddressIndex model', function() { var j = createAI(); for (var i = 0; i < 15; i++) - j.increment(true); + j.increment(true); for (var i = 0; i < 7; i++) - j.increment(false); - var j2 = new AddressIndex({ - cosigner: j.cosigner, + j.increment(false); + var j2 = new HDParams({ + copayerIndex: j.copayerIndex, }); j2.merge(j).should.equal(true); j2.changeIndex.should.equal(15); @@ -108,9 +108,9 @@ describe('AddressIndex model', function() { j2.merge(j).should.equal(false); }); - it('#merge should fail with different cosigner index', function() { - var j1 = new AddressIndex({ walletId: '1234', cosigner: 2 }); - var j2 = new AddressIndex({ walletId: '1234', cosigner: 3 }); + it('#merge should fail with different copayerIndex index', function() { + var j1 = new HDParams({ walletId: '1234', copayerIndex: 2 }); + var j2 = new HDParams({ walletId: '1234', copayerIndex: 3 }); var merge = function() { j2.merge(j1); }; merge.should.throw(Error); diff --git a/test/test.Structure.js b/test/test.HDPath.js similarity index 50% rename from test/test.Structure.js rename to test/test.HDPath.js index 3541c0f9c..500ba348d 100644 --- a/test/test.Structure.js +++ b/test/test.HDPath.js @@ -8,77 +8,77 @@ try { } catch (e) { var copay = require('../copay'); //node } -var Structure = require('../js/models/core/Structure'); +var HDPath = require('../js/models/core/HDPath'); -describe('Structure model', function() { +describe('HDPath model', function() { it('should have the correct constants', function() { - Structure.MAX_NON_HARDENED.should.equal(Math.pow(2, 31) - 1); - Structure.SHARED_INDEX.should.equal(Structure.MAX_NON_HARDENED); - Structure.ID_INDEX.should.equal(Structure.SHARED_INDEX - 1); - Structure.IdFullBranch.should.equal('m/45\'/2147483646/0/0'); + HDPath.MAX_NON_HARDENED.should.equal(Math.pow(2, 31) - 1); + HDPath.SHARED_INDEX.should.equal(HDPath.MAX_NON_HARDENED); + HDPath.ID_INDEX.should.equal(HDPath.SHARED_INDEX - 1); + HDPath.IdFullBranch.should.equal('m/45\'/2147483646/0/0'); }); it('should get the correct branches', function() { // shared branch (no cosigner index specified) - Structure.FullBranch(0, false).should.equal('m/45\'/2147483647/0/0'); + HDPath.FullBranch(0, false).should.equal('m/45\'/2147483647/0/0'); // copayer 0, address 0, external address (receiving) - Structure.FullBranch(0, false, 0).should.equal('m/45\'/0/0/0'); + HDPath.FullBranch(0, false, 0).should.equal('m/45\'/0/0/0'); // copayer 0, address 10, external address (receiving) - Structure.FullBranch(0, false, 10).should.equal('m/45\'/10/0/0'); + HDPath.FullBranch(0, false, 10).should.equal('m/45\'/10/0/0'); // copayer 0, address 0, internal address (change) - Structure.FullBranch(0, true, 0).should.equal('m/45\'/0/1/0'); + HDPath.FullBranch(0, true, 0).should.equal('m/45\'/0/1/0'); // copayer 0, address 10, internal address (change) - Structure.FullBranch(10, true, 0).should.equal('m/45\'/0/1/10'); + HDPath.FullBranch(10, true, 0).should.equal('m/45\'/0/1/10'); // copayer 7, address 10, internal address (change) - Structure.FullBranch(10, true, 7).should.equal('m/45\'/7/1/10'); + HDPath.FullBranch(10, true, 7).should.equal('m/45\'/7/1/10'); }); [ ['m/45\'/0/0/0', { - index: 0, - isChange: false - }], - ['m/45\'/0/0/1', { - index: 1, - isChange: false - }], - ['m/45\'/0/0/2', { - index: 2, - isChange: false - }], - ['m/45\'/0/1/0', { - index: 0, - isChange: true - }], - ['m/45\'/0/1/1', { - index: 1, - isChange: true - }], - ['m/45\'/0/1/2', { - index: 2, - isChange: true - }], - ['m/45\'/0/0/900', { - index: 900, - isChange: false - }], + index: 0, + isChange: false + }], + ['m/45\'/0/0/1', { + index: 1, + isChange: false + }], + ['m/45\'/0/0/2', { + index: 2, + isChange: false + }], + ['m/45\'/0/1/0', { + index: 0, + isChange: true + }], + ['m/45\'/0/1/1', { + index: 1, + isChange: true + }], + ['m/45\'/0/1/2', { + index: 2, + isChange: true + }], + ['m/45\'/0/0/900', { + index: 900, + isChange: false + }], ].forEach(function(datum) { var path = datum[0]; var result = datum[1]; it('should get the correct indices for path ' + path, function() { - var i = Structure.indicesForPath(path); + var i = HDPath.indicesForPath(path); i.index.should.equal(result.index); i.isChange.should.equal(result.isChange); }); }); it('should get the correct result for bitcoin uri', function() { var uri = 'bitcoin:19mP9FKrXqL46Si58pHdhGKow88SUPy1V8%3Famount=0.1&message=a%20bitcoin%20donation'; - var result = Structure.parseBitcoinURI(uri); + var result = HDPath.parseBitcoinURI(uri); result.address.should.equal('19mP9FKrXqL46Si58pHdhGKow88SUPy1V8'); result.amount.should.equal(0.1); result.message.should.equal('a bitcoin donation'); diff --git a/test/test.PublicKeyRing.js b/test/test.PublicKeyRing.js index 5404653c9..088c1f819 100644 --- a/test/test.PublicKeyRing.js +++ b/test/test.PublicKeyRing.js @@ -6,7 +6,7 @@ var bitcore = bitcore || require('bitcore'); var Address = bitcore.Address; var buffertools = bitcore.buffertools; -var Structure = require('../js/models/core/Structure'); +var HDPath = require('../js/models/core/HDPath'); try { var copay = require('copay'); //browser @@ -118,8 +118,8 @@ describe('PublicKeyRing model', function() { }).should.throw(); } - w2.getIndex(k.pub).getChangeIndex().should.equal(changeN); - w2.getIndex(k.pub).getReceiveIndex().should.equal(addressN); + w2.getHDParams(k.pub).getChangeIndex().should.equal(changeN); + w2.getHDParams(k.pub).getReceiveIndex().should.equal(addressN); }); @@ -135,7 +135,7 @@ describe('PublicKeyRing model', function() { a.network().name.should.equal('livenet'); if (i > 1) { w.getAddress(i - 1, isChange).toString().should - .not.equal(w.getAddress(i - 2, isChange).toString()); + .not.equal(w.getAddress(i - 2, isChange).toString()); } } }); @@ -170,12 +170,12 @@ describe('PublicKeyRing model', function() { var w = k.w; for (var i = 0; i < 3; i++) - w.generateAddress(true, k.pub); + w.generateAddress(true, k.pub); for (var i = 0; i < 2; i++) - w.generateAddress(false, k.pub); + w.generateAddress(false, k.pub); - w.getIndex(k.pub).getChangeIndex().should.equal(3); - w.getIndex(k.pub).getReceiveIndex().should.equal(2); + w.getHDParams(k.pub).getChangeIndex().should.equal(3); + w.getHDParams(k.pub).getReceiveIndex().should.equal(2); }); it('should set backup ready', function() { @@ -240,9 +240,9 @@ describe('PublicKeyRing model', function() { var w = k.w; for (var i = 0; i < 2; i++) - w.generateAddress(true, k.pub); + w.generateAddress(true, k.pub); for (var i = 0; i < 3; i++) - w.generateAddress(false, k.pub); + w.generateAddress(false, k.pub); var w2 = new PublicKeyRing({ networkName: 'livenet', @@ -251,8 +251,8 @@ describe('PublicKeyRing model', function() { w2.merge(w).should.equal(true); w2.requiredCopayers.should.equal(3); w2.totalCopayers.should.equal(5); - w2.getIndex(k.pub).getChangeIndex().should.equal(2); - w2.getIndex(k.pub).getReceiveIndex().should.equal(3); + w2.getHDParams(k.pub).getChangeIndex().should.equal(2); + w2.getHDParams(k.pub).getReceiveIndex().should.equal(3); w2.merge(w).should.equal(false); }); @@ -440,24 +440,24 @@ describe('PublicKeyRing model', function() { }); - it('#getIndex should return the right one', function() { + it('#getHDParams should return the right one', function() { var config = { networkName: 'livenet', }; var p = new PublicKeyRing(config); - var i = p.getIndex(Structure.SHARED_INDEX); + var i = p.getHDParams(HDPath.SHARED_INDEX); should.exist(i); - i.cosigner.should.equal(Structure.SHARED_INDEX); + i.copayerIndex.should.equal(HDPath.SHARED_INDEX); }); - it('#getIndex should throw error', function() { + it('#getHDParams should throw error', function() { var config = { networkName: 'livenet', }; var p = new PublicKeyRing(config); (function badCosigner() { - return p.getIndex(54); + return p.getHDParams(54); }).should.throw(); }); @@ -467,9 +467,9 @@ describe('PublicKeyRing model', function() { var amount = 2; for (var i = 0; i < amount; i++) - w.generateAddress(true, k.pub); + w.generateAddress(true, k.pub); for (var i = 0; i < amount; i++) - w.generateAddress(false, k.pub); + w.generateAddress(false, k.pub); var m = w.getRedeemScriptMap([ 'm/45\'/2147483647/1/0', diff --git a/test/test.TxProposals.js b/test/test.TxProposals.js index ec7cd7981..4ce94e9f6 100644 --- a/test/test.TxProposals.js +++ b/test/test.TxProposals.js @@ -23,8 +23,8 @@ var TxProposals = copay.TxProposals || require('../js/models/TxProposal'); var is_browser = (typeof process == 'undefined' || typeof process.versions === 'undefined') var PublicKeyRing = is_browser ? copay.PublicKeyRing : require('soop').load('../js/models/core/PublicKeyRing', { - Storage: fakeStorage - }); + Storage: fakeStorage +}); var config = { networkName: 'testnet', @@ -73,7 +73,7 @@ var vopts = { describe('TxProposals model', function() { var isChange = false; - var index = 0; + var addressIndex = 0; it('verify TXs', function(done) { @@ -94,8 +94,8 @@ describe('TxProposals model', function() { networkName: config.networkName, }); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -109,9 +109,9 @@ describe('TxProposals model', function() { var tx = b.build(); tx.isComplete().should.equal(false); - var ringIndex = pkr.getIndex(pub); - b.sign(priv2.getAll(ringIndex.getReceiveIndex(), ringIndex.getChangeIndex(), ringIndex.cosigner)); - b.sign(priv3.getAll(ringIndex.getReceiveIndex(), ringIndex.getChangeIndex(), ringIndex.cosigner)); + var ringIndex = pkr.getHDParams(pub); + b.sign(priv2.getAll(ringIndex.getReceiveIndex(), ringIndex.getChangeIndex(), ringIndex.copayerIndex)); + b.sign(priv3.getAll(ringIndex.getReceiveIndex(), ringIndex.getChangeIndex(), ringIndex.copayerIndex)); tx = b.build(); tx.isComplete().should.equal(true); @@ -152,22 +152,23 @@ describe('TxProposals model', function() { }; var b = new TransactionBuilder(opts) - .setUnspent(utxos) - .setOutputs([{ - address: toAddress, - amountSatStr: amountSatStr, - }]); + .setUnspent(utxos) + .setOutputs([{ + address: toAddress, + amountSatStr: amountSatStr, + }]); var selectedUtxos = b.getSelectedUnspent(); var inputChainPaths = selectedUtxos.map(function(utxo) { return pkr.pathForAddress(utxo.address); }); var selectedUtxos = b.getSelectedUnspent(); + var inputChainPaths = selectedUtxos.map(function(utxo) { return pkr.pathForAddress(utxo.address); }); + b.setHashToScriptMap(pkr.getRedeemScriptMap(inputChainPaths)); - var signRet; if (priv) { var pkeys = priv.getForPaths(inputChainPaths); @@ -199,8 +200,8 @@ describe('TxProposals model', function() { var start = new Date().getTime(); var pkr = createPKR([priv]); var ts = Date.now(); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -225,8 +226,8 @@ describe('TxProposals model', function() { var pkr = createPKR([priv]); var ts = Date.now(); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -274,8 +275,8 @@ describe('TxProposals model', function() { var w = new TxProposals({ networkName: config.networkName, }); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -299,8 +300,8 @@ describe('TxProposals model', function() { networkName: config.networkName, publicKeyRing: w.publicKeyRing, }); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w2.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -375,7 +376,7 @@ describe('TxProposals model', function() { }; var addressToSign = pkr.generateAddress(false, pub); unspentTest[0].address = addressToSign.toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); var tx, txb; var w = new TxProposals({ @@ -490,8 +491,8 @@ describe('TxProposals model', function() { var w = new TxProposals({ networkName: config.networkName, }); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -511,8 +512,8 @@ describe('TxProposals model', function() { var w2 = new TxProposals({ networkName: config.networkName, }); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w2.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -530,8 +531,8 @@ describe('TxProposals model', function() { var w3 = new TxProposals({ networkName: config.networkName, }); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w3.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -593,8 +594,8 @@ describe('TxProposals model', function() { }); var ts = Date.now(); - unspentTest[0].address = pkr.getAddress(index, isChange, pub).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange, pub); + unspentTest[0].address = pkr.getAddress(addressIndex, isChange, pub).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(addressIndex, isChange, pub); w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', @@ -682,46 +683,46 @@ describe('TxProposals model', function() { var txpv1 = { - "creator": "0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0", - "createdTs": 1406310417996, - "seenBy": { - "0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0": 1406310417996, - "02ba1599c64da4d80e25985be46c50e944b65f02e2b48c930528ce763d6710158f": 1406310418162 - }, - "signedBy": { - "0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0": 1406310417996, - "02ba1599c64da4d80e25985be46c50e944b65f02e2b48c930528ce763d6710158f": 1406310645549 - }, - "rejectedBy": {}, - "sentTs": 1406310645873, - "sentTxid": "87296c50e8601437d63d556afb27c3b8e3819214be0a9d756d401a8286c0ec43", - "inputChainPaths": ["m/45'/0/1/1"], - "comment": "test 6", - "builderObj": { - "version": 1, - "outs": [{ - "address": "mph66bnLvcn9KUSMrpikUBUZZkN2C1Z5tg", - "amountSatStr": 100 - }], - "utxos": [{ - "address": "2NEodmgBa4SH3VwE2asgW34vMYe8VThBZNo", - "txid": "8f8deda12dad6248e655054632a27f6891ebb37e8d2b3dd1bff87e71fd451ac7", - "vout": 1, - "ts": 1406312717, - "scriptPubKey": "a914ec7bce12d0e82a7d2b5431f6d89ca70af317f5a187", - "amount": 0.009798, - "confirmations": 0, - "confirmationsFromCache": false - }], - "opts": { - "spendUnconfirmed": true, - "remainderOut": { - "address": "2N74XAozMH3JB3XgeBkRvRw1J8TtfLTtvny" - } - }, - "scriptSig": ["00483045022100f167ad33b8bef4c65af8d19c1a849d1770cc8d1e35bffebe6b5459dcbe655c7802207b37370b308ba668fe19f8e8bc462c9fbdc6c67f79900670758d228d83ea96da014730440220038ad3f4cc7b0738b593454ec189913ae4b442bc83da153d68d9a0077bd1b09102202b5728a08f302e97de61ea37280b48ccdd575f0d235c22f5e0ecac6a4ab0f46401475221024739614847d5233a46913482c17c6860194ad78abb3bf47de46223047d8a0b5821024c6dc65a52c5eaaa080b96888091544f8ab8712caa7e0b69ea4b45f6f059557452ae"], - "hashToScriptMap": { - "2NEodmgBa4SH3VwE2asgW34vMYe8VThBZNo": "5221024739614847d5233a46913482c17c6860194ad78abb3bf47de46223047d8a0b5821024c6dc65a52c5eaaa080b96888091544f8ab8712caa7e0b69ea4b45f6f059557452ae" + "creator": "0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0", + "createdTs": 1406310417996, + "seenBy": { + "0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0": 1406310417996, + "02ba1599c64da4d80e25985be46c50e944b65f02e2b48c930528ce763d6710158f": 1406310418162 + }, + "signedBy": { + "0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0": 1406310417996, + "02ba1599c64da4d80e25985be46c50e944b65f02e2b48c930528ce763d6710158f": 1406310645549 + }, + "rejectedBy": {}, + "sentTs": 1406310645873, + "sentTxid": "87296c50e8601437d63d556afb27c3b8e3819214be0a9d756d401a8286c0ec43", + "inputChainPaths": ["m/45'/0/1/1"], + "comment": "test 6", + "builderObj": { + "version": 1, + "outs": [{ + "address": "mph66bnLvcn9KUSMrpikUBUZZkN2C1Z5tg", + "amountSatStr": 100 + }], + "utxos": [{ + "address": "2NEodmgBa4SH3VwE2asgW34vMYe8VThBZNo", + "txid": "8f8deda12dad6248e655054632a27f6891ebb37e8d2b3dd1bff87e71fd451ac7", + "vout": 1, + "ts": 1406312717, + "scriptPubKey": "a914ec7bce12d0e82a7d2b5431f6d89ca70af317f5a187", + "amount": 0.009798, + "confirmations": 0, + "confirmationsFromCache": false + }], + "opts": { + "spendUnconfirmed": true, + "remainderOut": { + "address": "2N74XAozMH3JB3XgeBkRvRw1J8TtfLTtvny" } + }, + "scriptSig": ["00483045022100f167ad33b8bef4c65af8d19c1a849d1770cc8d1e35bffebe6b5459dcbe655c7802207b37370b308ba668fe19f8e8bc462c9fbdc6c67f79900670758d228d83ea96da014730440220038ad3f4cc7b0738b593454ec189913ae4b442bc83da153d68d9a0077bd1b09102202b5728a08f302e97de61ea37280b48ccdd575f0d235c22f5e0ecac6a4ab0f46401475221024739614847d5233a46913482c17c6860194ad78abb3bf47de46223047d8a0b5821024c6dc65a52c5eaaa080b96888091544f8ab8712caa7e0b69ea4b45f6f059557452ae"], + "hashToScriptMap": { + "2NEodmgBa4SH3VwE2asgW34vMYe8VThBZNo": "5221024739614847d5233a46913482c17c6860194ad78abb3bf47de46223047d8a0b5821024c6dc65a52c5eaaa080b96888091544f8ab8712caa7e0b69ea4b45f6f059557452ae" } - }; + } +}; diff --git a/test/test.Wallet.js b/test/test.Wallet.js index 405992a9d..46cc15cd8 100644 --- a/test/test.Wallet.js +++ b/test/test.Wallet.js @@ -39,7 +39,7 @@ describe('Wallet model', function() { (function() { new Wallet(config) }).should. - throw(); + throw(); }); it('should getNetworkName', function() { var w = cachedCreateW(); @@ -266,7 +266,7 @@ describe('Wallet model', function() { var w = cachedCreateW2(); var l = w.getAddressesStr(); for (var i = 0; i < l.length; i++) - w.addressIsOwn(l[i]).should.equal(true); + w.addressIsOwn(l[i]).should.equal(true); w.addressIsOwn(l[0], { excludeMain: true @@ -315,14 +315,14 @@ describe('Wallet model', function() { o.opts.reconnectDelay = 100; var w2 = Wallet.fromObj(o, - new Storage(config.storage), - new Network(config.network), - new Blockchain(config.blockchain)); - should.exist(w2); - w2.publicKeyRing.requiredCopayers.should.equal(w.publicKeyRing.requiredCopayers); - should.exist(w2.publicKeyRing.getCopayerId); - should.exist(w2.txProposals.toObj); - should.exist(w2.privateKey.toObj); + new Storage(config.storage), + new Network(config.network), + new Blockchain(config.blockchain)); + should.exist(w2); + w2.publicKeyRing.requiredCopayers.should.equal(w.publicKeyRing.requiredCopayers); + should.exist(w2.publicKeyRing.getCopayerId); + should.exist(w2.txProposals.toObj); + should.exist(w2.privateKey.toObj); }); it('#getSecret decodeSecret', function() { @@ -339,15 +339,15 @@ describe('Wallet model', function() { (function() { Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoKM'); }).should.not. - throw(); + throw(); (function() { Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoK'); }).should. - throw(); + throw(); (function() { Wallet.decodeSecret('12345'); }).should. - throw(); + throw(); }); //this test fails randomly @@ -381,14 +381,14 @@ describe('Wallet model', function() { var w = createW(); var aiObj = { indexes: [{ - cosigner: 0, + copayerIndex: 0, changeIndex: 3, receiveIndex: 2 }] }; w._handleIndexes('senderID', aiObj, true); - w.publicKeyRing.getIndex(0).getReceiveIndex(2); - w.publicKeyRing.getIndex(0).getChangeIndex(3); + w.publicKeyRing.getHDParams(0).getReceiveIndex(2); + w.publicKeyRing.getHDParams(0).getChangeIndex(3); }); it('handle network pubKeyRings correctly', function() { @@ -405,7 +405,7 @@ describe('Wallet model', function() { requiredCopayers: w.requiredCopayers, totalCopayers: w.totalCopayers, indexes: [{ - cosigner: 0, + copayerIndex: 0, changeIndex: 2, receiveIndex: 3 }], @@ -415,8 +415,8 @@ describe('Wallet model', function() { w._handlePublicKeyRing('senderID', { publicKeyRing: pkrObj }, true); - w.publicKeyRing.getIndex(0).getReceiveIndex(2); - w.publicKeyRing.getIndex(0).getChangeIndex(3); + w.publicKeyRing.getHDParams(0).getReceiveIndex(2); + w.publicKeyRing.getHDParams(0).getChangeIndex(3); for (var i = 0; i < w.requiredCopayers; i++) { w.publicKeyRing.toObj().copayersExtPubKeys[i].should.equal(cepk[i]); } @@ -557,21 +557,21 @@ describe('Wallet model', function() { }); var roundErrorChecks = [{ - unspent: [1.0001], - balance: 100010000 - }, { - unspent: [1.0002, 1.0003, 1.0004], - balance: 300090000 - }, { - unspent: [0.000002, 1.000003, 2.000004], - balance: 300000900 - }, { - unspent: [0.0001, 0.0003], - balance: 40000 - }, { - unspent: [0.0001, 0.0003, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002], - balance: 110000 - }, + unspent: [1.0001], + balance: 100010000 + }, { + unspent: [1.0002, 1.0003, 1.0004], + balance: 300090000 + }, { + unspent: [0.000002, 1.000003, 2.000004], + balance: 300000900 + }, { + unspent: [0.0001, 0.0003], + balance: 40000 + }, { + unspent: [0.0001, 0.0003, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002], + balance: 110000 + }, ]; var roundWallet = cachedCreateW2(); @@ -788,8 +788,8 @@ describe('Wallet model', function() { // check updated all indexes var cosignersChecked = [] updateIndex.args.forEach(function(i) { - cosignersChecked.indexOf(i[0].cosigner).should.equal(-1); - cosignersChecked.push(i[0].cosigner); + cosignersChecked.indexOf(i[0].copayerIndex).should.equal(-1); + cosignersChecked.push(i[0].copayerIndex); }); sinon.assert.callCount(updateIndex, 4); @@ -810,7 +810,7 @@ describe('Wallet model', function() { var index = { changeIndex: 1, receiveIndex: 2, - cosigner: 2, + copayerIndex: 2, } w.updateIndex(index, function(err) { index.receiveIndex.should.equal(9); @@ -873,7 +873,7 @@ describe('Wallet model', function() { (function() { w.setAddressBook(contacts[0].address, contacts[0].label); }).should. - throw(); + throw(); }); it('should show/hide everywhere', function() { @@ -1049,11 +1049,11 @@ describe('Wallet model', function() { var opts = {}; opts.signhash = signhash; var txb = new TransactionBuilder(opts) - .setUnspent(utxos) - .setOutputs(outs) - .sign(['cVBtNonMyTydnS3NnZyipbduXo9KZfF1aUZ3uQHcvJB6UARZbiWG', - 'cRVF68hhZp1PUQCdjr2k6aVYb2cn6uabbySDPBizAJ3PXF7vDXTL' - ]); + .setUnspent(utxos) + .setOutputs(outs) + .sign(['cVBtNonMyTydnS3NnZyipbduXo9KZfF1aUZ3uQHcvJB6UARZbiWG', + 'cRVF68hhZp1PUQCdjr2k6aVYb2cn6uabbySDPBizAJ3PXF7vDXTL' + ]); var txp = { 'txProposal': { 'builderObj': txb.toObj() diff --git a/test/test.WalletFactory.js b/test/test.WalletFactory.js index 3bf8bde52..00cad01ce 100644 --- a/test/test.WalletFactory.js +++ b/test/test.WalletFactory.js @@ -13,6 +13,70 @@ var WalletFactory = require('../js/models/core/WalletFactory'); var Passphrase = require('../js/models/core/Passphrase'); +/** + * A better way to compare two objects in Javascript + **/ +function getKeys(obj) { + var keys; + if(obj.keys) { + keys = obj.keys(); + } else { + keys = []; + + for(var k in obj) { + if(Object.prototype.hasOwnProperty.call(obj, k)) { + keys.push(k); + } + } + } + + return keys; +} + +/** + * Create a new object so the keys appear in the provided order. + * @param {Object} obj The object to be the base for the new object + * @param {Array} keys The order in which properties of the new object should appear + **/ +function reconstructObject(obj, keys) { + var result = {}; + for (var i = 0, l = keys.length; i < l; i++) { + if (Object.prototype.hasOwnProperty.call(obj, keys[i])) { + result[keys[i]] = obj[keys[i]]; + } + } + + return result; +} + +function assertObjectEqual(a, b, msg) { + msg = msg || ''; + if( Object.prototype.toString.call( a ) === '[object Array]' && Object.prototype.toString.call( b ) === '[object Array]') { + // special case: array of objects + if (a.filter(function(e) { return Object.prototype.toString.call( e ) === '[object Object]' }).length > 0 || + b.filter(function(e) { return Object.prototype.toString.call( e ) === '[object Object]' }).length > 0 ){ + + if (a.length !== b.length) { + JSON.stringify(a).should.equal(JSON.stringify(b), msg); + } else { + for(var i = 0, l = a.length; i < l; i++) { + assertObjectEqual(a[i], b[i], msg + '[elements at index ' + i + ' should be equal]'); + } + } + // simple array of primitives + } else { + JSON.stringify(a).should.equal(JSON.stringify(b), msg); + } + } else { + var orderedA = reconstructObject(a, getKeys(a).sort()), + orderedB = reconstructObject(b, getKeys(b).sort()); + + // compare as strings for diff tolls to show us the difference + JSON.stringify(orderedA).should.equal(JSON.stringify(orderedB), msg) + } +} + + describe('WalletFactory model', function() { var config = { Network: FakeNetwork, @@ -83,7 +147,23 @@ describe('WalletFactory model', function() { w2.id.should.equal(w.id); }); + it('#fromObj #toObj round trip', function() { + var wf = new WalletFactory(config, '0.0.5'); + var o2 = o.replace(/cosigner/g,'copayerIndex'); + var w = wf.fromObj(JSON.parse(o2)); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + assertObjectEqual(w.toObj(), JSON.parse(o2)); + }); + + + + it('#fromObj #toObj round trip, using old cosigner', function() { var wf = new WalletFactory(config, '0.0.5'); var w = wf.fromObj(JSON.parse(o)); @@ -92,8 +172,8 @@ describe('WalletFactory model', function() { should.exist(w.publicKeyRing.getCopayerId); should.exist(w.txProposals.toObj()); should.exist(w.privateKey.toObj()); - - JSON.stringify(w.toObj()).should.equal(o); + var expected = JSON.parse(o.replace(/cosigner/g,'copayerIndex')); + assertObjectEqual(w.toObj(), expected); }); it('support old index schema: #fromObj #toObj round trip', function() { @@ -109,7 +189,9 @@ describe('WalletFactory model', function() { should.exist(w.txProposals.toObj); should.exist(w.privateKey.toObj); - JSON.stringify(w.toObj()).should.equal(o2); + // + var expected = JSON.parse(o2.replace(/cosigner/g,'copayerIndex')); + assertObjectEqual(w.toObj(), expected); }); it('should create wallet from encrypted object', function() { diff --git a/util/build.js b/util/build.js index 003c1e43b..4447e1dd1 100755 --- a/util/build.js +++ b/util/build.js @@ -93,8 +93,8 @@ var createBundle = function(opts) { b.require('./config', { expose: '../config' }); - b.require('./js/models/core/Structure', { - expose: '../js/models/core/Structure' + b.require('./js/models/core/HDPath', { + expose: '../js/models/core/HDPath' }); if (opts.dontminify) {