From b6c8362c634eba8a69d4c94bc561199d4eb742d8 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 18 Aug 2016 15:06:02 -0700 Subject: [PATCH] mtx: more refactoring. --- lib/bcoin/mtx.js | 151 ++++++++++++++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 54 deletions(-) diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index c4f188de..7419dd4e 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -228,16 +228,12 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) { if (!input.coin) return false; - // Optimization: Don't bother with any below - // calculation if the output is already templated. - // Just say this is "our" output. + // Don't bother with any below calculation + // if the output is already templated. if (input.script.length !== 0 || input.witness.length !== 0) return true; - // Optimization: test output against the - // address map to avoid unnecessary calculation. - // A hash table lookup may be faster than all - // the nonsense below. + // Make sure this coin is ours. if (!ring.ownOutput(input.coin)) return false; @@ -333,7 +329,7 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) { vector.set(0, opcodes.OP_0); - return; + return true; } // P2PKH @@ -348,7 +344,7 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) { vector.set(0, opcodes.OP_0); vector.set(1, ring.publicKey); - return; + return true; } // Multisig @@ -372,7 +368,7 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) { for (i = 0; i < n; i++) vector.set(i + 1, opcodes.OP_0); - return; + return true; } if (prev.indexOf(ring.publicKey) === -1) @@ -435,38 +431,31 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type, */ MTX.prototype.signInput = function signInput(index, ring, key, type) { - var input, prev, signature, keyIndex, signatures, i; - var len, m, n, keys, vector, version; + var input = this.inputs[index]; + var version = 0; + var redeem = false; + var prev, vector, signature, result; - // Get the input - input = this.inputs[index]; assert(input); // We should have previous outputs by now. if (!input.coin) return false; - // Optimization: test output against the - // address map to avoid unnecessary calculation. - // A hash table lookup may be faster than all - // the nonsense below. + // Make sure this output is ours. if (!ring.ownOutput(input.coin)) return false; // Get the previous output's script prev = input.coin.script; - vector = input.script; - len = vector.length; - version = 0; - // We need to grab the redeem script when - // signing p2sh transactions. + // Grab regular p2sh redeem script. if (prev.isScripthash()) { prev = input.script.getRedeem(); if (!prev) throw new Error('Input has not been templated.'); - len = vector.length - 1; + redeem = true; } // If the output script is a witness program, @@ -480,18 +469,32 @@ MTX.prototype.signInput = function signInput(index, ring, key, type) { if (!prev) throw new Error('Input has not been templated.'); vector = input.witness; - len = vector.length - 1; + redeem = true; version = 1; } else if (prev.isWitnessPubkeyhash()) { prev = Script.fromPubkeyhash(prev.get(1)); vector = input.witness; - len = vector.length; + redeem = false; version = 1; } // Create our signature. signature = this.createSignature(index, prev, key, type, version); + if (redeem) { + redeem = vector.pop(); + result = this.signVector(prev, vector, signature, ring); + vector.push(redeem); + vector.compile(); + return result; + } + + return this.signVector(prev, vector, signature, ring); +}; + +MTX.prototype.signVector = function signVector(prev, vector, signature, ring) { + var keys, i, m, n, signatures, keyIndex; + // P2PK if (prev.isPubkey()) { // Already signed. @@ -545,11 +548,6 @@ MTX.prototype.signInput = function signInput(index, ring, key, type) { // Grab `n` value (number of keys). n = prev.getSmall(prev.length - 2); } else { - // Only allow non-standard signing for - // scripthash. - if (len !== vector.length - 1) - return false; - keys = []; for (i = 0; i < prev.length; i++) { @@ -566,29 +564,25 @@ MTX.prototype.signInput = function signInput(index, ring, key, type) { if (vector.getSmall(0) !== 0) throw new Error('Input has not been templated.'); - // Something is very wrong here. Abort. - if (len - 1 > n) + // Too many signature slots. Abort. + if (vector.length - 1 > n) return false; // Count the number of current signatures. signatures = 0; - for (i = 1; i < len; i++) { + for (i = 1; i < vector.length; i++) { if (Script.isSignature(vector.get(i))) signatures++; } // Signatures are already finalized. - if (signatures === m && len - 1 === m) + if (signatures === m && vector.length - 1 === m) return true; - // This can happen in a case where another - // implementation adds signatures willy-nilly - // or by `m`. Add some signature slots for - // us to use. - while (len - 1 < n) { - vector.insert(len, opcodes.OP_0); - len++; - } + // Add some signature slots for us to use if + // there was for some reason not enough. + while (vector.length - 1 < n) + vector.push(opcodes.OP_0); // Find the key index so we can place // the signature in the same index. @@ -608,7 +602,7 @@ MTX.prototype.signInput = function signInput(index, ring, key, type) { // Add our signature to the correct slot // and increment the total number of // signatures. - if (keyIndex < len && signatures < m) { + if (keyIndex < vector.length && signatures < m) { if (vector.getSmall(keyIndex) === 0) { vector.set(keyIndex, signature); signatures++; @@ -618,26 +612,21 @@ MTX.prototype.signInput = function signInput(index, ring, key, type) { // All signatures added. Finalize. if (signatures >= m) { // Remove empty slots left over. - for (i = len - 1; i >= 1; i--) { - if (vector.getSmall(i) === 0) { + for (i = vector.length - 1; i >= 1; i--) { + if (vector.getSmall(i) === 0) vector.remove(i); - len--; - } } // Remove signatures which are not required. - // This should never happen except when dealing - // with implementations that potentially handle - // signature slots differently. + // This should never happen. while (signatures > m) { - vector.remove(len - 1); + vector.pop(); signatures--; - len--; } // Sanity checks. assert(signatures === m); - assert(len - 1 === m); + assert(vector.length - 1 === m); } vector.compile(); @@ -645,6 +634,60 @@ MTX.prototype.signInput = function signInput(index, ring, key, type) { return signatures === m; }; +MTX.prototype.combineMultisig = function combineMultisig(index, prev, version, script, signature) { + var m = prev.getSmall(0); + var sigs = [signature]; + var map = {}; + var result; + var i, j, sig, type, msg, key, pub, res; + + for (i = 1; i < script.length; i++) { + sig = script.get(i); + if (Script.isSignature(sig)) + sigs.push(sig); + } + + for (i = 0; i < sigs.length; i++) { + sig = sigs[i]; + type = sig[sig.length - 1]; + + msg = this.signatureHash(index, prev, type, version); + + for (j = 1; j < prev.length - 2; j++) { + key = prev.get(j); + pub = key.toString('hex'); + + if (map[pub]) + continue; + + res = Script.checksig(msg, sig, key); + + if (res) { + map[pub] = sig; + if (utils.equal(sig, signature)) + result = true; + break; + } + } + } + + script.clear(); + script.push(opcodes.OP_0); + for (i = 1; i < prev.length - 2; i++) { + key = prev.get(i); + pub = key.toString('hex'); + sig = map[pub]; + if (sig) + script.push(sig); + } + + while (script.length - 1 < m) + script.push(opcodes.OP_0); + + script.compile(); + return result; +}; + /** * Test whether the transaction is fully-signed. * @returns {Boolean}