diff --git a/Sign.js b/Sign.js new file mode 100644 index 000000000..24889dc56 --- /dev/null +++ b/Sign.js @@ -0,0 +1,133 @@ + +function signOne(hash, addrStr, keys) +{ + var keyObj = keys[addrStr]; + var rawPrivKey = new Buffer(keyObj.priv, 'hex'); + var key = new KeyModule.Key(); + key.private = rawPrivKey; + var signature = key.signSync(hash); + + return signature; +} + +function signTxIn(nIn, tx, txInputs, network, keys, scripts) +{ + // locate TX input needing a signature + var txin = tx.ins[nIn]; + var scriptSig = txin.getScript(); + + // locate TX output, within txInputs + var txoutHash = txin.getOutpointHash(); + if (!(txoutHash in txInputs)) + throw new Error("signTxIn missing input hash"); + var txFrom = txInputs[txoutHash]; + var txoutIndex = txin.getOutpointIndex(); + if (txFrom.outs.length >= txoutIndex) + throw new Error("signTxIn missing input index"); + var txout = txFrom.outs[txoutIndex]; + var scriptPubKey = txout.getScript(); + + // detect type of transaction, and extract useful elements + var txType = scriptPubKey.classify(); + if (txType == TX_UNKNOWN) + throw new Error("unknown TX type"); + var scriptData = scriptPubKey.capture(); + + // if P2SH, lookup the script + var subscriptRaw = undefined; + var subscript = undefined; + var subType = undefined; + var subData = undefined; + if (txType == TX_SCRIPTHASH) { + var addr = new Address(network.addressScript, scriptData[0]); + var addrStr = addr.toString(); + if (!(addrStr in scripts)) + throw new Error("unknown script hash address"); + + subscriptRaw = new Buffer(scripts[addrStr], 'hex'); + subscript = new Script(subscriptRaw); + subType = subscript.classify(); + if (subType == TX_UNKNOWN) + throw new Error("unknown subscript TX type"); + subData = subscript.capture(); + } + + var hash = tx.hashForSignature(scriptPubKey, i, 0); + + switch (txType) { + case TX_PUBKEY: + // already signed + if (scriptSig.chunks.length > 0) + return; + + var pubkeyhash = util.sha256ripe160(scriptData[0]); + var addr = new Address(network.addressPubkey, pubkeyhash); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + throw new Error("unknown pubkey"); + + var signature = signOne(hash, addrStr, keys); + scriptSig.writeBytes(signature); + break; + + case TX_PUBKEYHASH: + // already signed + if (scriptSig.chunks.length > 0) + return; + + var addr = new Address(network.addressPubkey, scriptData[0]); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + throw new Error("unknown pubkey hash address"); + + var signature = signOne(hash, addrStr, keys); + scriptSig.writeBytes(signature); + scriptSig.writeBytes(key.public); + break; + + case TX_SCRIPTHASH: + // already signed + if (scriptSig.chunks.length > 0) + return; + + var addr = new Address(network.addressPubkey, subData[0]); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + throw new Error("unknown script(pubkey hash) address"); + + var signature = signOne(hash, addrStr, keys); + scriptSig.writeBytes(signature); + scriptSig.writeBytes(key.public); + break; + + case TX_MULTISIG: + while (scriptSig.chunks.length < scriptData.length) { + scriptSig.writeBytes(util.EMPTY_BUFFER); + } + for (var i = 0; i < scriptData.length; i++) { + // skip already signed + if (scriptSig.chunks[i].length > 0) + continue; + + var pubkeyhash = util.sha256ripe160(scriptSig.chunks[i]); + var addr = new Address(network.addressPubkey, pubkeyhash); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + continue; + + var signature = signOne(hash, addrStr, keys); + scriptSig.chunks[i] = signature; + } + break; + } + + if (txtype == TX_SCRIPTHASH) + scriptSig.writeBytes(subscriptRaw); +} + +exports.Transaction = function Transaction(tx, txInputs, network, keys, scripts) +{ + for (var i = 0; i < tx.ins.length; i++) + signTxIn(i, tx, txInputs, network, keys, scripts); +}; + diff --git a/Transaction.js b/Transaction.js index b96dcef5c..b4580b3b9 100644 --- a/Transaction.js +++ b/Transaction.js @@ -45,7 +45,7 @@ function spec(b) { return Buffer.concat([this.o, slen, this.s, qbuf]); }; - TransactionIn.prototype.getOutpointHash = function getOutpointIndex() { + TransactionIn.prototype.getOutpointHash = function getOutpointHash() { if ("undefined" !== typeof this.o.outHashCache) { return this.o.outHashCache; }