diff --git a/lib/bcoin/protocol/network.js b/lib/bcoin/protocol/network.js index 1ac88836..a4a9abc1 100644 --- a/lib/bcoin/protocol/network.js +++ b/lib/bcoin/protocol/network.js @@ -26,13 +26,6 @@ main.prefixes = { xprivkey: 0x0488ade4 }; -utils.merge(main.prefixes, { - normal: main.prefixes.pubkey, - p2pkh: main.prefixes.pubkey, - multisig: main.prefixes.pubkey, - p2sh: main.prefixes.script -}); - main.type = 'main'; main.seeds = [ @@ -115,13 +108,6 @@ testnet.prefixes = { xprivkey: 0x04358394 }; -utils.merge(testnet.prefixes, { - normal: testnet.prefixes.pubkey, - p2pkh: testnet.prefixes.pubkey, - multisig: testnet.prefixes.pubkey, - p2sh: testnet.prefixes.script -}); - testnet.seeds = [ 'testnet-seed.alexykot.me', 'testnet-seed.bitcoin.petertodd.org', diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 6f9ed62d..efb50872 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -604,7 +604,7 @@ script.execute = function execute(s, stack, tx, index) { return false; var n = stack.pop(); - if (n.length !== 1 || !(1 <= n[0] && n[0] <= 3)) + if (n.length !== 1 || !(1 <= n[0] && n[0] <= 15)) return false; n = n[0] || 0; @@ -718,7 +718,7 @@ script.multisig = function(keys, m, n) { throw new Error('wrong amount of pubkeys for multisig script'); assert(m >= 1 && m <= n); - assert(n >= 1 && n <= 7); + assert(n >= 1 && n <= 15); // Format: // op_[m] [pubkey1-len] [pubkey1] ... op_[n] op_checkmultisig @@ -778,7 +778,7 @@ script.isMultisig = function isMultisig(s, key) { return false; var m = s[0]; - if (typeof m === 'number' && m >= 1 && m <= 16) + if (typeof m === 'number' && m >= 1 && m <= 15) m = [m]; if (!Array.isArray(m) || m.length !== 1) return false; @@ -788,7 +788,7 @@ script.isMultisig = function isMultisig(s, key) { return false; var n = s[s.length - 2]; - if (typeof n === 'number' && n >= 1 && n <= 16) + if (typeof n === 'number' && n >= 1 && n <= 15) n = [n]; if (!Array.isArray(n) || n.length !== 1) return false; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 86c5eb68..a12d0d6d 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -332,7 +332,7 @@ TX.prototype.scriptOutput = function(options) { if (keys === options.address) { keys = keys.map(function(address) { - return bcoin.wallet.addr2hash(address, 'normal'); + return bcoin.wallet.addr2hash(address, 'pubkey'); }); } @@ -349,18 +349,18 @@ TX.prototype.scriptOutput = function(options) { assert(m >= 1 && m <= n); if (options.hash) - assert(n >= 1 && n <= 7); + assert(n >= 1 && n <= 15); else assert(n >= 1 && n <= 3); script = bcoin.script.multisig(keys, m, n); - } else if (bcoin.wallet.validateAddress(options.address, 'p2sh')) { + } else if (bcoin.wallet.validateAddress(options.address, 'script')) { // p2sh transaction // https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki // hash160 [20-byte-redeemscript-hash] equal script = [ 'hash160', - bcoin.wallet.addr2hash(options.address, 'p2sh'), + bcoin.wallet.addr2hash(options.address, 'script'), 'eq' ]; } else if (options.address) { @@ -369,7 +369,7 @@ TX.prototype.scriptOutput = function(options) { script = [ 'dup', 'hash160', - bcoin.wallet.addr2hash(options.address, 'normal'), + bcoin.wallet.addr2hash(options.address, 'pubkey'), 'eqverify', 'checksig' ]; @@ -525,11 +525,11 @@ TX.prototype.maxSize = function maxSize() { } else { // May end up in a higher fee if we // do not have the redeem script available. - m = 7; - n = 7; + m = 15; + n = 15; } assert(m >= 1 && m <= n); - assert(n >= 1 && n <= 7); + assert(n >= 1 && n <= 15); // Multisig // Empty byte size += 1; @@ -659,7 +659,7 @@ TX.prototype.inputAddrs = function inputAddrs() { }).map(function(input) { var pub = input.script[1]; var hash = utils.ripesha(pub); - return bcoin.wallet.hash2addr(hash, 'normal'); + return bcoin.wallet.hash2addr(hash, 'pubkey'); }); }; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index dc4864aa..138783a7 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -63,8 +63,8 @@ function Wallet(options, passphrase) { this.key = bcoin.ecdsa.genKeyPair(); } - this.addressType = 'normal'; - this.sharedKeys = []; + this.type = 'pubkey'; + this.pubkeys = []; this.m = 1; this.n = 1; @@ -117,31 +117,40 @@ Wallet.prototype._init = function init() { Wallet.prototype.multisig = function multisig(options) { var pub = this.getOwnPublicKey(); - options.type = options.type || options.addressType; - options.keys = options.keys || options.sharedKeys; + options.type = options.type || options.prefix; + options.keys = options.keys || options.pubkeys; - this.addressType = options.type || 'normal'; - - // Multisig - this.sharedKeys = (options.keys || []).map(utils.toKeyArray); + this.type = options.type || 'pubkey'; + this.pubkeys = (options.keys || []).map(utils.toKeyArray); this.m = options.m || 1; this.n = options.n || 1; + this.nmax = this.type === 'script' + ? (this.compressed ? 15 : 7) + : 3; - this.sharedKeys = this.sharedKeys.filter(function(key) { - return !utils.isEqual(key, pub); + var hasOwn = this.pubkeys.some(function(key) { + return utils.isEqual(key, pub); + }); + + if (!hasOwn) + this.pubkeys.push(pub); + + // Keys need to be in a predictable order. + this.pubkeys = this.pubkeys.sort(function(a, b) { + return new bn(a).cmp(new bn(b)) > 0; }); // Use p2sh multisig by default - if (!options.addressType && this.sharedKeys.length) - this.addressType = 'p2sh'; + if (!options.type && this.pubkeys.length > 1) + this.type = 'script'; if (this.m < 1 || this.m > this.n) throw new Error('m ranges between 1 and n'); - if (this.n < 1 || this.n > 7) - throw new Error('n ranges between 1 and 7'); + if (this.n < 1 || this.n > this.nmax) + throw new Error('n ranges between 1 and ' + this.nmax); - if (this.sharedKeys.length < this.m - 1) + if (this.pubkeys.length < this.m) throw new Error(this.m + ' public keys required'); }; @@ -185,7 +194,7 @@ Wallet.prototype.getPrivateKey = function getPrivateKey(enc) { Wallet.prototype.getFullPublicKey = function getFullPublicKey(enc) { var pub = this.getOwnPublicKey(); - if (this.addressType === 'p2sh') { + if (this.type === 'script') { var keys = this.getPublicKeys(); pub = bcoin.script.encode(bcoin.script.multisig(keys, this.m, this.n)); } @@ -214,15 +223,7 @@ Wallet.prototype.getPublicKey = function getPublicKey(enc) { }; Wallet.prototype.getPublicKeys = function() { - var pub = this.getOwnPublicKey(); - - this.sharedKeys = this.sharedKeys.filter(function(key) { - return !utils.isEqual(key, pub); - }); - - var keys = this.sharedKeys.slice().map(utils.toKeyArray); - - keys.push(pub); + var keys = this.pubkeys.slice().map(utils.toKeyArray); // Keys need to be in a predictable order. keys = keys.sort(function(a, b) { @@ -237,7 +238,7 @@ Wallet.prototype.getFullHash = function getFullHash() { }; Wallet.prototype.getFullAddress = function getFullAddress() { - return Wallet.hash2addr(this.getFullHash(), this.addressType); + return Wallet.hash2addr(this.getFullHash(), this.type); }; Wallet.prototype.getOwnHash = function getOwnHash() { @@ -245,7 +246,7 @@ Wallet.prototype.getOwnHash = function getOwnHash() { }; Wallet.prototype.getOwnAddress = function getOwnAddress() { - return Wallet.hash2addr(this.getOwnHash(), this.addressType); + return Wallet.hash2addr(this.getOwnHash(), this.type); }; Wallet.prototype.getHash = function getHash() { @@ -253,28 +254,28 @@ Wallet.prototype.getHash = function getHash() { }; Wallet.prototype.getAddress = function getAddress() { - return Wallet.hash2addr(this.getFullHash(), this.addressType); + return Wallet.hash2addr(this.getFullHash(), this.type); }; -Wallet.hash2addr = function hash2addr(hash, version) { +Wallet.hash2addr = function hash2addr(hash, prefix) { hash = utils.toArray(hash, 'hex'); - version = network.prefixes[version || 'normal']; - hash = [ version ].concat(hash); + prefix = network.prefixes[prefix || 'pubkey']; + hash = [ prefix ].concat(hash); var addr = hash.concat(utils.checksum(hash)); return utils.toBase58(addr); }; -Wallet.addr2hash = function addr2hash(addr, version) { +Wallet.addr2hash = function addr2hash(addr, prefix) { if (!Array.isArray(addr)) addr = utils.fromBase58(addr); - version = network.prefixes[version || 'normal']; + prefix = network.prefixes[prefix || 'pubkey']; if (addr.length !== 25) return []; - if (addr[0] !== version) + if (addr[0] !== prefix) return []; var chk = utils.checksum(addr.slice(0, -4)); @@ -284,10 +285,10 @@ Wallet.addr2hash = function addr2hash(addr, version) { return addr.slice(1, -4); }; -Wallet.prototype.validateAddress = function validateAddress(addr, version) { +Wallet.prototype.validateAddress = function validateAddress(addr, prefix) { if (!addr) return false; - var p = Wallet.addr2hash(addr, version); + var p = Wallet.addr2hash(addr, prefix); return p.length !== 0; }; Wallet.validateAddress = Wallet.prototype.validateAddress; @@ -423,8 +424,8 @@ Wallet.prototype.toJSON = function toJSON() { priv: this.getPrivateKey('base58'), tx: this.tx.toJSON(), multisig: { - type: this.addressType, - keys: this.sharedKeys.map(utils.toBase58), + type: this.type, + keys: this.pubkeys.map(utils.toHex), m: this.m, n: this.n } diff --git a/test/wallet-test.js b/test/wallet-test.js index 1b88b002..9b95b978 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -277,7 +277,7 @@ describe('Wallet', function() { var w1 = bcoin.wallet({ key: key1, multisig: { - type: 'p2sh', + type: 'script', keys: [pub2, pub3], m: 2, n: 3 @@ -286,7 +286,7 @@ describe('Wallet', function() { var w2 = bcoin.wallet({ key: key2, multisig: { - type: 'p2sh', + type: 'script', keys: [pub1, pub3], m: 2, n: 3 @@ -295,7 +295,7 @@ describe('Wallet', function() { var w3 = bcoin.wallet({ key: key3, multisig: { - type: 'p2sh', + type: 'script', keys: [pub1, pub2], m: 2, n: 3