From 45b7b65e993e16b74aa822167a882bc747b67c75 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Sat, 5 Sep 2015 00:50:51 -0300 Subject: [PATCH 1/5] allow p2pkh addresses in 1-of-1 --- lib/model/copayer.js | 2 +- lib/model/wallet.js | 5 ++++- lib/server.js | 5 +++++ test/integration/server.js | 14 +++++++++----- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/model/copayer.js b/lib/model/copayer.js index d7cfd3b..0e25001 100644 --- a/lib/model/copayer.js +++ b/lib/model/copayer.js @@ -82,7 +82,7 @@ Copayer.prototype.createAddress = function(wallet, isChange) { $.checkState(wallet.isComplete()); var path = this.addressManager.getNewAddressPath(isChange); - var raw = Address.create(WalletUtils.deriveAddress(wallet.publicKeyRing, path, wallet.m, wallet.network)); + var raw = Address.create(WalletUtils.deriveAddress(wallet.addressType, wallet.publicKeyRing, path, wallet.m, wallet.network)); var address = Address.create(_.extend(raw, { walletId: wallet.id, })); diff --git a/lib/model/wallet.js b/lib/model/wallet.js index 4639073..64bb775 100644 --- a/lib/model/wallet.js +++ b/lib/model/wallet.js @@ -30,6 +30,8 @@ Wallet.create = function(opts) { x.pubKey = opts.pubKey; x.network = opts.network; x.derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45; + x.addressType = opts.addressType || WalletUtils.SCRIPT_TYPES.P2SH; + x.addressManager = AddressManager.create({ derivationStrategy: x.derivationStrategy, }); @@ -55,6 +57,7 @@ Wallet.fromObj = function(obj) { x.pubKey = obj.pubKey; x.network = obj.network; x.derivationStrategy = obj.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45; + x.addressType = obj.addressType || WalletUtils.SCRIPT_TYPES.P2SH; x.addressManager = AddressManager.fromObj(obj.addressManager); x.scanStatus = obj.scanStatus; @@ -148,7 +151,7 @@ Wallet.prototype.createAddress = function(isChange) { $.checkState(this.isComplete()); var path = this.addressManager.getNewAddressPath(isChange); - var raw = WalletUtils.deriveAddress(this.publicKeyRing, path, this.m, this.network); + var raw = WalletUtils.deriveAddress(this.addressType, this.publicKeyRing, path, this.m, this.network); var address = Address.create(_.extend(raw, { walletId: this.id, })); diff --git a/lib/server.js b/lib/server.js index 7855e0c..379fefc 100644 --- a/lib/server.js +++ b/lib/server.js @@ -204,6 +204,7 @@ WalletService.prototype._runLocked = function(cb, task) { * @param {string} opts.pubKey - Public key to verify copayers joining have access to the wallet secret. * @param {string} [opts.network = 'livenet'] - The Bitcoin network for this wallet. * @param {string} [opts.supportBIP44 = false] - Client supports BIP44 paths for 1-of-1 wallets. + * @param {string} [opts.supportP2PKH = false] - Client supports P2PKH address type for 1-of-1 wallets. */ WalletService.prototype.createWallet = function(opts, cb) { var self = this, @@ -223,6 +224,9 @@ WalletService.prototype.createWallet = function(opts, cb) { var derivationStrategy = (opts.n == 1 && opts.supportBIP44) ? WalletUtils.DERIVATION_STRATEGIES.BIP44 : WalletUtils.DERIVATION_STRATEGIES.BIP45; + var addressType = (opts.n == 1 && opts.supportP2PKH) ? + WalletUtils.SCRIPT_TYPES.P2PKH : WalletUtils.SCRIPT_TYPES.P2SH; + try { pubKey = new PublicKey.fromString(opts.pubKey); } catch (ex) { @@ -250,6 +254,7 @@ WalletService.prototype.createWallet = function(opts, cb) { network: opts.network, pubKey: pubKey.toString(), derivationStrategy: derivationStrategy, + addressType: addressType, }); self.storage.storeWallet(wallet, function(err) { log.debug('Wallet created', wallet.id, opts.network); diff --git a/test/integration/server.js b/test/integration/server.js index 93eafa6..0b93e6c 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -97,12 +97,14 @@ helpers.createAndJoinWallet = function(m, n, opts, cb) { var offset = opts.offset || 0; var supportBIP44 = _.isBoolean(opts.supportBIP44) ? opts.supportBIP44 : true + var supportP2PKH = _.isBoolean(opts.supportP2PKH) ? opts.supportP2PKH : true var walletOpts = { name: 'a wallet', m: m, n: n, pubKey: TestData.keyPair.pub, supportBIP44: supportBIP44, + supportP2PKH: supportP2PKH, }; server.createWallet(walletOpts, function(err, walletId) { if (err) return cb(err); @@ -1412,7 +1414,7 @@ describe('Wallet service', function() { should.exist(address); address.walletId.should.equal(wallet.id); address.network.should.equal('livenet'); - address.address.should.equal('3J4J9nkFpzQjUGDh5hLKMKztFSPWMKejKE'); + address.address.should.equal('1L3z9LPd861FWQhf3vDn89Fnc9dkdBo2CG'); address.isChange.should.be.false; address.path.should.equal('m/0/0'); server.getNotifications({}, function(err, notifications) { @@ -4376,9 +4378,9 @@ describe('Wallet service', function() { it('should scan main addresses', function(done) { helpers.stubAddressActivity( - ['3J4J9nkFpzQjUGDh5hLKMKztFSPWMKejKE', // m/0/0 - '384JHSf9kVBs3yXsPwCzEScRs395u8hwxj', // m/0/2 - '3NgXBiMQvwcRU8khVoPFJ6gsbGg9ZYrRzH', // m/1/0 + ['1L3z9LPd861FWQhf3vDn89Fnc9dkdBo2CG', // m/0/0 + '1GdXraZ1gtoVAvBh49D4hK9xLm6SKgesoE', // m/0/2 + '1FUzgKcyPJsYwDLUEVJYeE2N3KVaoxTjGS', // m/1/0 ]); var expectedPaths = [ 'm/0/0', @@ -4562,7 +4564,9 @@ describe('Wallet service', function() { WalletService.SCAN_CONFIG.scanWindow = 2; WalletService.SCAN_CONFIG.derivationDelay = 0; - helpers.createAndJoinWallet(1, 1, function(s, w) { + helpers.createAndJoinWallet(1, 1, { + supportP2PKH: false + }, function(s, w) { server = s; wallet = w; done(); From 153e75db2c9c3fa168efa9cdfc1d7b98caa7aab7 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Sat, 5 Sep 2015 00:51:17 -0300 Subject: [PATCH 2/5] bump bwu v0.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e7511b1..5f1cb17 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "dependencies": { "async": "^0.9.0", "bitcore": "0.13.0", - "bitcore-wallet-utils": "0.2.0", + "bitcore-wallet-utils": "0.2.1", "body-parser": "^1.11.0", "coveralls": "^2.11.2", "email-validator": "^1.0.1", From a197d5c98b8216390c01eea2d640b3d07745686f Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Sat, 5 Sep 2015 01:19:21 -0300 Subject: [PATCH 3/5] add p2pkh flag to POST v2/wallets endpoint --- lib/expressapp.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/expressapp.js b/lib/expressapp.js index 8eae3b4..2146a95 100644 --- a/lib/expressapp.js +++ b/lib/expressapp.js @@ -133,6 +133,7 @@ ExpressApp.prototype.start = function(opts, cb) { router.post('/v1/wallets/', function(req, res) { var server = getServer(req, res); req.body.supportBIP44 = false; + req.body.supportP2PKH = false; server.createWallet(req.body, function(err, walletId) { if (err) return returnError(err, res, req); res.json({ @@ -144,6 +145,7 @@ ExpressApp.prototype.start = function(opts, cb) { router.post('/v2/wallets/', function(req, res) { var server = getServer(req, res); req.body.supportBIP44 = true; + req.body.supportP2PKH = true; server.createWallet(req.body, function(err, walletId) { if (err) return returnError(err, res, req); res.json({ From 595a54322bee681a26ea7ed318e6bb0d32e95f86 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Sat, 5 Sep 2015 18:49:43 -0300 Subject: [PATCH 4/5] correctly build tx proposal on P2PKH --- lib/model/address.js | 11 ---------- lib/model/txproposal.js | 2 ++ lib/server.js | 9 +------- test/integration/server.js | 43 +++++++++++++++++++++++++++----------- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/lib/model/address.js b/lib/model/address.js index 93b6fd9..9f4da97 100644 --- a/lib/model/address.js +++ b/lib/model/address.js @@ -34,15 +34,4 @@ Address.fromObj = function(obj) { return x; }; - -/** - * getScriptPubKey - * - * @param {number} threshold - amount of required signatures to spend the output - * @return {Script} - */ -Address.prototype.getScriptPubKey = function(threshold) { - return Bitcore.Script.buildMultisigOut(this.publicKeys, threshold).toScriptHashOut(); -}; - module.exports = Address; diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index 668e671..7802d89 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -72,6 +72,7 @@ TxProposal.create = function(opts) { x.proposalSignaturePubKey = opts.proposalSignaturePubKey; x.proposalSignaturePubKeySig = opts.proposalSignaturePubKeySig; x.derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45; + x.addressType = opts.addressType || WalletUtils.SCRIPT_TYPES.P2SH; x.customData = opts.customData; if (_.isFunction(TxProposal._create[x.type])) { @@ -120,6 +121,7 @@ TxProposal.fromObj = function(obj) { x.proposalSignaturePubKey = obj.proposalSignaturePubKey; x.proposalSignaturePubKeySig = obj.proposalSignaturePubKeySig; x.derivationStrategy = obj.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45; + x.addressType = obj.addressType || WalletUtils.SCRIPT_TYPES.P2SH; x.customData = obj.customData; return x; diff --git a/lib/server.js b/lib/server.js index 379fefc..2b0af0f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -542,14 +542,6 @@ WalletService.prototype._parseClientVersion = function() { return this.parsedClientVersion; }; -WalletService.prototype._clientSupportsBIP44 = function() { - var version = this._parseClientVersion(); - if (!version) return false; - if (version.agent != 'bwc') return true; // Asume 3rd party clients are up-to-date - if (version.major == 0 && version.minor <= 1) return false; - return true; -}; - WalletService.prototype._clientSupportsTXPv2 = function() { var version = this._parseClientVersion(); if (!version) return false; @@ -1216,6 +1208,7 @@ WalletService.prototype.createTx = function(opts, cb) { walletN: wallet.n, excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos, derivationStrategy: wallet.derivationStrategy, + addressType: wallet.addressType, customData: opts.customData }; diff --git a/test/integration/server.js b/test/integration/server.js index 0b93e6c..9636ad5 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -163,11 +163,23 @@ helpers.stubUtxos = function(server, wallet, amounts, cb) { } else { confirmations = Math.floor(Math.random() * 100 + 1); } + + var scriptPubKey; + switch (wallet.addressType) { + case WalletUtils.SCRIPT_TYPES.P2SH: + scriptPubKey = Bitcore.Script.buildMultisigOut(address.publicKeys, wallet.m).toScriptHashOut(); + break; + case WalletUtils.SCRIPT_TYPES.P2PKH: + scriptPubKey = Bitcore.Script.buildPublicKeyHashOut(address.address); + break; + } + should.exist(scriptPubKey); + return { txid: helpers.randomTXID(), vout: Math.floor(Math.random() * 10 + 1), satoshis: helpers.toSatoshi(amount).toString(), - scriptPubKey: address.getScriptPubKey(wallet.m).toBuffer().toString('hex'), + scriptPubKey: scriptPubKey.toBuffer().toString('hex'), address: address.address, confirmations: confirmations, }; @@ -1158,24 +1170,26 @@ describe('Wallet service', function() { beforeEach(function() { server = WalletService.getInstance(); }); - it('should use BIP44 for 1-of-1 wallet if supported', function(done) { + it('should use BIP44 & P2PKH for 1-of-1 wallet if supported', function(done) { var walletOpts = { name: 'my wallet', m: 1, n: 1, pubKey: TestData.keyPair.pub, supportBIP44: true, + supportP2PKH: true, }; server.createWallet(walletOpts, function(err, wid) { should.not.exist(err); server.storage.fetchWallet(wid, function(err, wallet) { should.not.exist(err); wallet.derivationStrategy.should.equal('BIP44'); + wallet.addressType.should.equal('P2PKH'); done(); }); }); }); - it('should use BIP45 for 1-of-1 wallet if BIP44 not supported', function(done) { + it('should use BIP45 & P2SH for 1-of-1 wallet if not supported', function(done) { var walletOpts = { name: 'my wallet', m: 1, @@ -1187,22 +1201,26 @@ describe('Wallet service', function() { server.storage.fetchWallet(wid, function(err, wallet) { should.not.exist(err); wallet.derivationStrategy.should.equal('BIP45'); + wallet.addressType.should.equal('P2SH'); done(); }); }); }); - it('should always use BIP45 for shared wallets', function(done) { + it('should always use BIP45 & P2SH for shared wallets', function(done) { var walletOpts = { name: 'my wallet', m: 2, n: 3, pubKey: TestData.keyPair.pub, + supportBIP44: true, + supportP2PKH: true, }; server.createWallet(walletOpts, function(err, wid) { should.not.exist(err); server.storage.fetchWallet(wid, function(err, wallet) { should.not.exist(err); wallet.derivationStrategy.should.equal('BIP45'); + wallet.addressType.should.equal('P2SH'); done(); }); }); @@ -1398,7 +1416,7 @@ describe('Wallet service', function() { }); }); - describe('1-of-1 (BIP44)', function() { + describe('1-of-1 (BIP44 & P2PKH)', function() { beforeEach(function(done) { helpers.createAndJoinWallet(1, 1, function(s, w) { server = s; @@ -2799,18 +2817,20 @@ describe('Wallet service', function() { }); describe('#signTx', function() { - describe('1-1', function() { + describe('1-of-1 (BIP44 & P2PKH)', function() { var server, wallet, txid; beforeEach(function(done) { helpers.createAndJoinWallet(1, 1, function(s, w) { server = s; wallet = w; - helpers.stubUtxos(server, wallet, _.range(1, 9), function() { - var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 20, TestData.copayers[0].privKey_1H_0); + helpers.stubUtxos(server, wallet, [1, 2], function() { + var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 2.5, TestData.copayers[0].privKey_1H_0); server.createTx(txOpts, function(err, tx) { should.not.exist(err); should.exist(tx); + tx.derivationStrategy.should.equal('BIP44'); + tx.addressType.should.equal('P2PKH'); txid = tx.id; done(); }); @@ -2844,10 +2864,9 @@ describe('Wallet service', function() { }); }); }); - - }); - describe('Multisign 2-3', function() { + + describe('Multisig', function() { var server, wallet, txid; beforeEach(function(done) { @@ -4360,7 +4379,7 @@ describe('Wallet service', function() { var server, wallet; var scanConfigOld = WalletService.SCAN_CONFIG; - describe('1-of-1 wallet (BIP44)', function() { + describe('1-of-1 wallet (BIP44 & P2PKH)', function() { beforeEach(function(done) { this.timeout(5000); WalletService.SCAN_CONFIG.scanWindow = 2; From 21e85e044c33419c2124d931c141726e5a19cd61 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Sun, 6 Sep 2015 09:42:48 -0300 Subject: [PATCH 5/5] v0.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f1cb17..d668b11 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-service", "description": "A service for Mutisig HD Bitcoin Wallets", "author": "BitPay Inc", - "version": "0.2.0", + "version": "0.2.1", "keywords": [ "bitcoin", "copay",