diff --git a/README.md b/README.md index 494ea5b8..74903062 100644 --- a/README.md +++ b/README.md @@ -580,6 +580,26 @@ pool.on('fork', function(tip1, tip2) { }); ``` +### Transaction Building + +Transaction building happens in 4 stages: + +1. __Outputs__: Adding of the outputs (where we want to send the coins). +2. __Filling__: Filling of the unspent outputs as the inputs, calculation of + fee, and potential addition of a change address. +3. __Scripting__: Compilation of the of the input scripts (minus the + signatures). This will fill `n` empty signature slots (`OP_0`). + - __Potential recalculation of the fee__: Now that the redeem script is + available to the primitive TX object, it can likely calculate a lower fee. + Add value to the change address if the fee is lower than we thought. +4. __Signing__: Signing of the inputs. If this is a multisig transaction, we + have to wait for other signers to sign it before it is finalized. + - __Finalization__: Once the final signer signs the transaction, empty + signature slots will be removed as they are no longer necessary. + +Once these steps are completed, the transaction should verify and is able to be +broadcast to the pool. + ## API Documentation ### Objects diff --git a/lib/bcoin/address.js b/lib/bcoin/address.js index 35d31771..a40da666 100644 --- a/lib/bcoin/address.js +++ b/lib/bcoin/address.js @@ -109,6 +109,7 @@ Address.prototype.setRedeem = function setRedeem(redeem) { Address.prototype.addKey = function addKey(key) { var old = this.getScriptAddress(); + var cur; key = utils.toBuffer(key); @@ -122,11 +123,16 @@ Address.prototype.addKey = function addKey(key) { this.keys.push(key); this.keys = utils.sortKeys(this.keys); - this.emit('scriptaddress', old, this.getScriptAddress()); + + cur = this.getScriptAddress(); + + if (old !== cur) + this.emit('scriptaddress', old, cur); }; Address.prototype.removeKey = function removeKey(key) { var old = this.getScriptAddress(); + var cur; key = utils.toBuffer(key); @@ -142,7 +148,11 @@ Address.prototype.removeKey = function removeKey(key) { this.keys.splice(index, 1); this.keys = utils.sortKeys(this.keys); - this.emit('scriptaddress', old, this.getScriptAddress()); + + cur = this.getScriptAddress(); + + if (old !== cur) + this.emit('scriptaddress', old, this.getScriptAddress()); }; Address.prototype.getPrivateKey = function getPrivateKey(enc) { diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 72f4816b..a7af378f 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -168,153 +168,178 @@ function HDPrivateKey(options) { this.master = options.master; } - this.purpose = options.purpose != null - ? options.purpose - : 44; - - this.coinType = options.coinType != null - ? options.coinType - : (network.type === 'main' ? 0 : 1); - - this.accountIndex = options.accountIndex != null - ? options.accountIndex - : 0; - - this.isChange = options.isChange != null - ? options.isChange - : false; - - // this.addressIndex = options.addressIndex != null - // ? options.addressIndex - // : 0; - this.isPrivate = true; } -HDPrivateKey.prototype.scan = function scan(txs) { +HDPrivateKey.prototype.scan = function scan(options, txByAddress, callback) { var keys = []; + var purpose, coinType, root; - assert(this.isMaster); + if (!callback) { + callback = txByAddress; + txByAddress = options; + options = null; + } + + if (!options) + options = {}; + + purpose = options.purpose; + coinType = options.coinType; + + if (purpose == null) + purpose = 44; + + if (coinType == null) + coinType = network.type === 'main' ? 0 : 1; + + assert(utils.isFinite(purpose)); + assert(utils.isFinite(coinType)); // 0. get the root node - var root = master - .derive(44, true) - .derive(network.type === 'main' ? 0 : 1, true); + root = this + .derive(purpose, true) + .derive(coinType, true); return (function scanner(accountIndex) { - var account, chain, addressIndex, address, addr, i; + var addressIndex = 0; + var total = 0; var gap = 0; // 1. derive the first account's node (index = 0) - account = root.derive(accountIndex, true); + var account = root.derive(accountIndex, true); // 2. derive the external chain node of this account - chain = account.derive(0); + var chain = account.derive(0); // 3. scan addresses of the external chain; // respect the gap limit described below - addressIndex = 0; -main: - for (;;) { - address = chain.derive(addressIndex++); - addr = bcoin.address.hash2addr( + return (function next() { + var address = chain.derive(addressIndex++); + var addr = bcoin.address.hash2addr( bcoin.address.key2hash(address.publicKey), 'pubkey'); - for (i = 0; i < txs.length; i++) { - txs[i].fillPrevout(txs); - if (txs[i].testInputs(addr) || txs[i].testOutputs(addr)) { - keys.push(address); - gap = 0; - } else { - // 4. if no transactions are found on the - // external chain, stop discovery - if (++gap >= 20) - return keys; - } - } - } + return txByAddress(addr, function(err, txs) { + var result; - // 5. if there are some transactions, increase - // the account index and go to step 1 - return scanner(accountIndex + 1); + if (err) + return callback(err); + + if (txs) { + if (typeof txs === 'boolean') + result = txs; + else if (Array.isArray(txs)) + result = txs.length > 0; + else + result = false; + } + + if (result) { + keys.push(address); + total++; + gap = 0; + return next(); + } + + if (++gap < 20) + return next(); + + // 4. if no transactions are found on the + // external chain, stop discovery + if (total === 0) + return callback(null, keys); + + // 5. if there are some transactions, increase + // the account index and go to step 1 + return scanner(accountIndex + 1); + }); + })(); })(0); }; -HDPrivateKey.prototype.__defineGetter__('addressIndex', function() { - var index = this.childIndex; - assert(index < contants.hd.hardened); - return index; -}); - -HDPrivateKey.prototype._deriveBIP44 = function _deriveBIP44() { +HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(options) { + var purpose = options.purpose; + var coinType = options.coinType; + var accountIndex = options.accountIndex; + var chain = options.chain; + var addressIndex = options.addressIndex; var child; - assert(this.isMaster); + if (purpose == null) + purpose = 44; - if (options.purpose == null) - options.purpose = 44; + if (coinType == null) + coinType = network.type === 'main' ? 0 : 1; - if (options.coinType == null) - options.coinType = network.type === 'main' ? 0 : 1; + if (chain == null) + chain = options.change ? 1 : 0; + + assert(utils.isFinite(purpose)); + assert(utils.isFinite(coinType)); + assert(utils.isFinite(accountIndex)); + assert(utils.isFinite(chain)); + assert(utils.isFinite(addressIndex)); child = this - .derive(options.purpose, true) - .derive(options.coinType, true) - .derive(options.accountIndex, true) - .derive(options.isChange ? 1 : 0) - .derive(options.addressIndex); - - child.purpose = options.purpose; - child.coinType = options.coinType; - child.accountIndex = options.accountIndex; - child.isChange = options.isChange; - child.addressIndex = options.addressIndex; + .derive(purpose, true) + .derive(coinType, true) + .derive(accountIndex, true) + .derive(chain) + .derive(addressIndex); return child; }; HDPrivateKey.prototype.deriveAccount = function deriveAccount(accountIndex) { - return this._deriveBIP44({ - purpose: this.purpose, - coinType: this.coinType, - accountIndex: accountIndex != null ? accountIndex : this.accountIndex + 1, - isChange: false, + return this.deriveBIP44({ + accountIndex: accountIndex, + chain: 0, addressIndex: 0 }); }; -HDPrivateKey.prototype.deriveChange = function deriveChange(addressIndex, accountIndex) { - return this._deriveBIP44({ - purpose: this.purpose, - coinType: this.coinType, - accountIndex: accountIndex != null ? accountIndex : this.accountIndex, - isChange: true, - addressIndex: addressIndex != null ? addressIndex : this.addressIndex + 1 +HDPrivateKey.prototype.deriveChange = function deriveChange(accountIndex, addressIndex) { + return this.deriveBIP44({ + accountIndex: accountIndex, + chain: 1, + addressIndex: addressIndex }); }; -HDPrivateKey.prototype.deriveAddress = function deriveAddress(addressIndex, accountIndex) { - return this._deriveBIP44({ - purpose: this.purpose, - coinType: this.coinType, - accountIndex: accountIndex != null ? accountIndex : this.accountIndex, - isChange: false, - addressIndex: addressIndex != null ? addressIndex : this.addressIndex + 1 +HDPrivateKey.prototype.deriveAddress = function deriveAddress(accountIndex, addressIndex) { + return this.deriveBIP44({ + accountIndex: accountIndex, + chain: 0, + addressIndex: addressIndex }); }; -HDPrivateKey.prototype.toPath = function toPath(data) { - assert(!this.isMaster); - return HDPrivateKey.getPath( - this.accountIndex, this.addressIndex, this.isChange); -}; +HDPrivateKey.getPath = function getPath(options) { + var purpose, coinType, accountIndex, chain, addressIndex; -HDPrivateKey.getPath = function toPath(accountIndex, addressIndex, isChange) { - return 'm/44\'/' - + (network.type === 'main' ? '0' : '1') + '\'' + '/' + if (!options) + options = {}; + + purpose = options.purpose; + coinType = options.coinType; + accountIndex = options.accountIndex; + chain = options.chain; + addressIndex = options.addressIndex; + + if (purpose == null) + purpose = 44; + + if (coinType == null) + coinType = network.type === 'main' ? 0 : 1; + + if (chain == null) + chain = options.change ? 1 : 0; + + return 'm/' + purpose + '\'/' + + coinType + '\'/' + accountIndex + '\'/' - + (isChange ? '1' : '0') + '/' + + chain + '/' + addressIndex; }; @@ -638,40 +663,13 @@ function HDPublicKey(options) { this.master = options.master; } - this.purpose = options.purpose != null - ? options.purpose - : 44; - - this.coinType = options.coinType != null - ? options.coinType - : (network.type === 'main' ? 0 : 1); - - this.accountIndex = options.accountIndex != null - ? options.accountIndex - : 0; - - this.isChange = options.isChange != null - ? options.isChange - : false; - - // this.addressIndex = options.addressIndex != null - // ? options.addressIndex - // : 0; - this.isPublic = true; } -HDPublicKey.prototype.__defineGetter__('addressIndex', function() { - var index = this.childIndex; - assert(index < contants.hd.hardened); - return index; -}); - -HDPublicKey.prototype._deriveBIP44 = HDPrivateKey.prototype._deriveBIP44; +HDPublicKey.prototype.deriveBIP44 = HDPrivateKey.prototype.deriveBIP44; HDPublicKey.prototype.deriveAccount = HDPrivateKey.prototype.deriveAccount; HDPublicKey.prototype.deriveChange = HDPrivateKey.prototype.deriveChange; HDPublicKey.prototype.deriveAddress = HDPrivateKey.prototype.deriveAddress; -HDPublicKey.prototype.toPath = HDPrivateKey.prototype.toPath; HDPublicKey.isExtended = function isExtended(data) { if (typeof data !== 'string') diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index 105da862..3549d699 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -264,6 +264,23 @@ Input.prototype.getID = function getID() { return '[' + this.type + ':' + hash.slice(0, 7) + ']'; }; +Input.prototype.getLocktime = function getLocktime() { + var output, redeem, lock, type; + + assert(this.prevout.tx); + + output = this.prevout.tx.outputs[this.prevout.index]; + redeem = output.script; + + if (bcoin.script.isScripthash(redeem)) + redeem = bcoin.script.getRedeem(this.script); + + if (redeem[1] !== 'checklocktimeverify') + return; + + return bcoin.script.getLocktime(redeem); +}; + Input.prototype.inspect = function inspect() { var output = this.output ? this.output.inspect() diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 377184dd..8c77dd62 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -1254,22 +1254,27 @@ script._lockTime = function _lockTime(s) { return; for (i = 0; i < s.length; i++) { - if (Array.isArray(s[i]) && s[i + 1] === 'checklocktimeverify') + if (utils.isBuffer(s[i]) && s[i + 1] === 'checklocktimeverify') return s[i]; } }; script.isLockTime = function isLockTime(s) { - return script._lockTime(s) != null; + return !!script._lockTime(s); }; script.getLockTime = function getLockTime(s) { var lockTime = script._lockTime(s); if (!lockTime) - return 0; + return; - return script.num(lockTime, true); + lockTime = script.num(lockTime, true); + + if (lockTime < constants.locktimeThreshold) + return { type: 'height', value: lockTime }; + + return { type: 'time', value: lockTime }; }; script.getInputData = function getData(s, prev) { diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 956052e5..364097f2 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -548,6 +548,82 @@ TX.prototype.scriptSig = function scriptSig(index, key, pub, redeem, type) { return input.script; }; +TX.prototype.isSigned = function isSigned(index, required) { + var i, input, s, len, m, j, total; + + for (i = 0; i < this.inputs.length; i++) { + input = this.inputs[i]; + + if (index != null && i !== index) + continue; + + // We can't check for signatures unless + // we have the previous output. + assert(input.prevout.tx); + + // Get the prevout's subscript + s = input.prevout.tx.getSubscript(input.prevout.index); + + // Script length, needed for multisig + len = input.script.length; + + // Grab the redeem script if P2SH + if (bcoin.script.isScripthash(s)) { + s = bcoin.script.getRedeem(input.script); + // Decrement `len` to avoid the redeem script + len--; + } + + // Check for signatures. + // P2PK + if (bcoin.script.isPubkey(s)) { + if (!bcoin.script.isSignature(input.script[0])) + return false; + continue; + } + + // P2PK + if (bcoin.script.isPubkeyhash(s)) { + if (!bcoin.script.isSignature(input.script[0])) + return false; + continue; + } + + // Multisig + if (bcoin.script.isMultisig(s)) { + // Grab `m` value (number of required sigs). + m = s[0]; + if (Array.isArray(m)) + m = m[0] || 0; + + // Ensure all members are signatures. + for (j = 1; j < len; j++) { + if (!bcoin.script.isSignature(input.script[j])) + return false; + } + + // Ensure we have the correct number + // of required signatures. + if (len - 1 !== m) + return false; + + continue; + } + + // Unknown + total = 0; + for (j = 0; j < input.script.length; j++) { + if (bcoin.script.isSignatureEncoding(input.script[j])) + total++; + } + + if (total !== required) + return false; + } + + return true; +}; + TX.prototype.addOutput = function addOutput(obj, value) { var options, output; @@ -1170,6 +1246,34 @@ TX.prototype.getFunds = function getFunds(side) { // Legacy TX.prototype.funds = TX.prototype.getFunds; +TX.prototype.getTargetTime = function getTargetTime() { + var bestValue = 0; + var i, lockTime, bestType; + + for (i = 0; i < this.inputs.length; i++) { + lockTime = this.inputs[i].getLocktime(); + + if (!lockTime) + continue; + + // Incompatible types + if (bestType && bestType !== lockTime.type) + return; + + bestType = lockTime.type; + + if (lockTime.value < bestValue) + continue; + + bestValue = lockTime.value; + } + + return { + type: bestType || 'height', + value: bestValue + }; +}; + TX.prototype.testInputs = function testInputs(addressTable, index, collect) { var inputs = []; var i, input, prev, data, j; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 5b565994..49cf1023 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -19,6 +19,8 @@ var network = bcoin.protocol.network; */ function Wallet(options) { + var i; + if (!(this instanceof Wallet)) return new Wallet(options); @@ -29,21 +31,44 @@ function Wallet(options) { this.options = options; this.addresses = []; - this.master = null; + this.master = options.master || null; this._addressTable = {}; + this._labelMap = {}; this.accountIndex = options.accountIndex || 0; this.addressIndex = options.addressIndex || 0; this.changeIndex = options.changeIndex || 0; - if (options.addresses) { + if (options.addresses && options.addresses.length > 0) { options.addresses.forEach(function(address) { this.addAddress(address); }, this); } else { - this.addAddress(options); + this.createNewAddress(options); } + // Create a non-master account address if we don't have one. + // Might not be necessary now. + if (this.master) { + for (i = 0; i < this.addresses.length; i++) { + if (this.addresses[i].key.hd && !this.addresses[i].change) + break; + } + if (i === this.addresses.length) + this.createNewAddress(options); + } + + // Find the last change address if there is one. + for (i = this.addresses.length - 1; i >= 0; i--) { + if (this.addresses[i].change) + break; + } + + if (i === -1) + this.changeAddress = this.createChangeAddress(); + else + this.changeAddress = this.addresses[i]; + this.storage = options.storage; this.label = options.label || ''; this.loaded = false; @@ -77,6 +102,10 @@ Wallet.prototype._init = function init() { }); this.tx.on('tx', function(tx) { + // TX using this change address was + // confirmed. Allocate a new change address. + if (self.changeAddress.ownOutput(tx)) + self.changeAddress = self.createChangeAddress(); self.emit('tx', tx); }); @@ -129,25 +158,29 @@ Wallet.prototype._addressIndex = function _addressIndex(address) { return -1; }; -Wallet.prototype.createChangeAddress = function createChangeAddress(address) { +Wallet.prototype.createChangeAddress = function createChangeAddress(options) { + if (!options) + options = {}; + + options.change = true; + + if (this.master) { + options.priv = + this.master.key.hd.deriveChange(this.accountIndex, this.changeIndex++); + } + + return this.addAddress(options); +}; + +Wallet.prototype.createNewAddress = function createNewAddress(options) { if (!options) options = {}; if (this.master) { - options.change = true; - options.priv = this.master.deriveAddress(this.addressIndex++, this.accountIndex); + options.priv = + this.master.key.hd.deriveAddress(this.accountIndex, this.addressIndex++); } - return this.addAddress({ change: true }); -}; - -Wallet.prototype.createNewAddress = function createNewAddress(address, options) { - if (!options) - options = {}; - - if (this.master) - options.priv = this.master.deriveAddress(this.addressIndex++, this.accountIndex); - return this.addAddress(options); }; @@ -174,6 +207,12 @@ Wallet.prototype.addAddress = function addAddress(address) { if (this._addressIndex(address) !== -1) return; + if (address.key.hd && address.key.hd.isMaster) { + assert(!this.master); + this.master = address; + return; + } + if (address._wallet) address._wallet.removeAddress(address); @@ -181,14 +220,10 @@ Wallet.prototype.addAddress = function addAddress(address) { index = this.addresses.push(address) - 1; - if (address.hd && address.hd.isMaster && adress.hd.isPrivate) { - assert(!this.master); - this.master = address; - } - address.on('scriptaddress', address._onUpdate = function(old, cur) { self._addressTable[cur] = self._addressTable[old]; delete self._addressTable[old]; + self.emit('add address', address); }); if (address.type === 'scripthash') @@ -236,10 +271,6 @@ Wallet.prototype.removeKey = function removeKey(key) { return this.address.removeKey(key); }; -Wallet.prototype.derive = function derive() { - this.addAddress(this.address.derive.apply(this.address, arguments)); -}; - Wallet.prototype.getPrivateKey = function getPrivateKey(enc) { return this.address.getPrivateKey(enc); }; @@ -286,12 +317,33 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) { }; Wallet.prototype.fill = function fill(tx, address, fee) { - var result; + var unspent, items, result; if (!address) - address = this.createChangeAddress(); + address = this.changeAddress.getKeyAddress(); - result = tx.fill(this.getUnspent(), address, fee); + unspent = this.getUnspent(); + + // Avoid multisig if first address is not multisig + items = unspent.filter(function(item) { + var output = item.tx.outputs[item.index]; + if (bcoin.script.isScripthash(output.script)) { + if (this.address.type === 'scripthash') + return true; + return false; + } + if (bcoin.script.isMultisig(output.script)) { + if (this.address.n > 1) + return true; + return false; + } + return true; + }, this); + + if (tx.getInputs(unspent, address, fee).inputs) + unspent = items; + + result = tx.fill(unspent, address, fee); if (!result.inputs) return false; @@ -310,23 +362,62 @@ Wallet.prototype.fillPrevout = function fillPrevout(tx) { // Legacy Wallet.prototype.fillTX = Wallet.prototype.fillPrevout; +Wallet.prototype.createTX = function createTX(outputs, fee) { + var tx = bcoin.tx(); + var target; + + if (!Array.isArray(outputs)) + outputs = [outputs]; + + outputs.forEach(function(output) { + tx.addOutput(output); + }); + + if (!this.fill(tx, null, fee)) + return; + + // Find the necessary locktime if there is + // a checklocktimeverify script in the unspents. + target = tx.getTargetTime(); + + // No target value. The unspents have an + // incompatible locktime type. + if (!target) + return; + + if (target.value > 0) + tx.setLockTime(target.value); + else + tx.avoidFeeSnipping(); + + this.sign(tx); + + return tx; +}; + Wallet.prototype.scriptInputs = function scriptInputs(tx) { + var self = this; + return this.addresses.reduce(function(total, address) { var pub = address.getPublicKey(); var redeem = address.getScript(); - tx.inputs.forEach(function(input, i) { - if (!input.prevout.tx && this.tx._all[input.prevout.hash]) - input.prevout.tx = this.tx._all[input.prevout.hash]; - if (!input.prevout.tx || !this.ownOutput(input.prevout.tx)) + tx.inputs.forEach(function(input, i) { + if (!input.prevout.tx && self.tx._all[input.prevout.hash]) + input.prevout.tx = self.tx._all[input.prevout.hash]; + + if (!input.prevout.tx) + return; + + if (!address.ownOutput(input.prevout.tx, input.prevout.index)) return; if (tx.scriptInput(i, pub, redeem)) total++; - }, this); + }); return total; - }, 0, this); + }, 0); }; Wallet.prototype.signInputs = function signInputs(tx, type) { @@ -340,7 +431,10 @@ Wallet.prototype.signInputs = function signInputs(tx, type) { if (!input.prevout.tx && self.tx._all[input.prevout.hash]) input.prevout.tx = self.tx._all[input.prevout.hash]; - if (!input.prevout.tx || !self.ownOutput(input.prevout.tx)) + if (!input.prevout.tx) + return; + + if (!address.ownOutput(input.prevout.tx, input.prevout.index)) return; if (tx.signInput(i, address.key, type)) @@ -368,7 +462,10 @@ Wallet.prototype.sign = function sign(tx, type) { input.prevout.tx = self.tx._all[input.prevout.hash]; // Filter inputs that this wallet own - if (!input.prevout.tx || !self.ownOutput(input.prevout.tx)) + if (!input.prevout.tx) + return; + + if (!address.ownOutput(input.prevout.tx, input.prevout.index)) return; if (tx.scriptSig(i, key, pub, redeem, type)) @@ -445,8 +542,9 @@ Wallet.prototype.toJSON = function toJSON(encrypt) { accountIndex: this.accountIndex, addressIndex: this.addressIndex, changeIndex: this.changeIndex, + master: this.master ? this.master.toJSON(encrypt) : null, addresses: this.addresses.filter(function(address) { - if (!address.hd) + if (!address.key.hd) return true; if (address.change) @@ -475,6 +573,9 @@ Wallet.fromJSON = function fromJSON(json, decrypt) { accountIndex: json.accountIndex, addressIndex: json.addressIndex, changeIndex: json.changeIndex, + master: json.master + ? bcoin.address.fromJSON(json.master, decrypt) + : null, addresses: json.addresses.map(function(address) { return bcoin.address.fromJSON(address, decrypt); }) @@ -483,12 +584,14 @@ Wallet.fromJSON = function fromJSON(json, decrypt) { w.tx.fromJSON(json.tx); // Make sure we have all the change - // addresses (we don't save them) - for (i = 0; i < w.changeIndex; i++) { - w.addKey({ - change: true, - key: w.master.deriveChange(i, w.accountIndex) - }); + // addresses (we don't save them). + if (w.master) { + for (i = 0; i < w.changeIndex; i++) { + w.addAddress({ + change: true, + key: w.master.key.hd.deriveChange(w.accountIndex, i) + }); + } } return w; diff --git a/test/wallet-test.js b/test/wallet-test.js index 204dc73c..de63e017 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -263,55 +263,39 @@ describe('Wallet', function() { }); it('should verify 2-of-3 p2sh tx', function(cb) { - var hd = bcoin.hd.priv(); - var hd1 = hd.derive(0); - var hd2 = hd.derive(1); - var hd3 = hd.derive(2); - - // Generate 3 key pairs - var key1 = bcoin.ecdsa.genKeyPair(); - var key2 = bcoin.ecdsa.genKeyPair(); - var key3 = bcoin.ecdsa.genKeyPair(); - - // var key1 = hd1; - // var key2 = hd2; - // var key3 = hd3; - - // Grab the 3 pubkeys - var pub1 = key1.getPublic(true, 'array'); - var pub2 = key2.getPublic(true, 'array'); - var pub3 = key3.getPublic(true, 'array'); - // Create 3 2-of-3 wallets with our pubkeys as "shared keys" var w1 = bcoin.wallet({ - key: key1, multisig: { type: 'scripthash', - keys: [pub2, pub3], m: 2, n: 3 } }); var w2 = bcoin.wallet({ - key: key2, multisig: { type: 'scripthash', - keys: [pub1, pub3], m: 2, n: 3 } }); var w3 = bcoin.wallet({ - key: key3, + hd: true, multisig: { type: 'scripthash', - keys: [pub1, pub2], m: 2, n: 3 } }); + var receive = bcoin.wallet(); + w1.addKey(w2.getPublicKey()); + w1.addKey(w3.getPublicKey()); + w2.addKey(w1.getPublicKey()); + w2.addKey(w3.getPublicKey()); + w3.addKey(w1.getPublicKey()); + w3.addKey(w2.getPublicKey()); + // Our p2sh address var addr = w1.getAddress(); assert.equal(w1.getAddress(), addr);