bleh, rushed commit

This commit is contained in:
Thomas Kerin 2016-11-12 15:45:59 +01:00
parent c0bbd76ad0
commit 9d766df519
13 changed files with 873 additions and 2 deletions

View File

@ -0,0 +1,26 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var address = root.keyPair.getAddress()
var wif = root.keyPair.toWIF()
console.log(address)
console.log(wif)
var txid = '06fc7b675a31bfe3f05dab40d0cd8c044a9b2e890c696a53449d970a4adc6d52'
var vout = 0
var p2shScript = bscript.witnessPubKeyHash.output.encode(crypto.hash160(root.keyPair.getPublicKeyBuffer()))
var scriptPubKey = bscript.scriptHash.output.encode(crypto.hash160(p2shScript))
var amount = 22000
var txb = new TransactionBuilder(network)
txb.addInput(txid, vout, 0xffffffff)
txb.addOutput(scriptPubKey, amount - 5000)
txb.sign(0, root.keyPair)
var tx = txb.build()
console.log(tx.toBuffer().toString('hex'))

29
latest/fund_p2sh_p2wsh.js Normal file
View File

@ -0,0 +1,29 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
var txid = 'beb647db98bda750f8202e6bc3441781ea5cfc6e3630c9d0ae47b0bfb111c249'
var vout = 1
var txOut = {
script: bscript.pubKeyHash.output.encode(pubkeyhash),
value: 100000
}
var witnessScript = txOut.script
var p2shScript = bscript.witnessScriptHash.output.encode(crypto.sha256(witnessScript))
var scriptPubKey = bscript.scriptHash.output.encode(crypto.hash160(p2shScript))
var txb = new TransactionBuilder(network)
txb.addInput(txid, vout, 0xffffffff, txOut.script)
txb.addOutput(scriptPubKey, txOut.value - 10000)
txb.sign(0, root.keyPair)
var tx = txb.build()
console.log(tx.toBuffer().toString('hex'))

View File

@ -0,0 +1,32 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var root2 = root.derive(1)
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
var txid = 'aed14f8e918c6e7cc9347391b790f765030b07e6985fbb146bf3f6b25ddc0043'
var vout = 0
var txOut = {
script: bscript.pubKeyHash.output.encode(pubkeyhash),
value: 22000
}
var multisig = bscript.multisig.output.encode(2, [root.getPublicKeyBuffer(), root2.getPublicKeyBuffer()])
var p2shScript = bscript.witnessScriptHash.output.encode(crypto.sha256(multisig))
var scriptPubKey = bscript.scriptHash.output.encode(crypto.hash160(p2shScript))
var txb = new TransactionBuilder(network)
txb.addInput(txid, vout, 0xffffffff, txOut.script)
txb.addOutput(scriptPubKey, txOut.value - 5000)
txb.sign(0, root.keyPair)
var tx = txb.build()
console.log(tx.toBuffer().toString('hex'))
// b5e7c1d911b078c754770fc372bc92096ce2605ac6b785f91ec7df0a5034ec69

29
latest/fund_p2wpkh.js Normal file
View File

@ -0,0 +1,29 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var address = root.keyPair.getAddress()
var wif = root.keyPair.toWIF()
console.log(address)
console.log(wif)
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
var txid = '9aa8d1a1c5df0afccf76e84df1029062b65a98dad68e13cc765aef88ab378dd0'
var vout = 0
var scriptPubKey = bscript.pubKeyHash.output.encode(pubkeyhash)
var amount = 22000
var toSegwitPubkey = bscript.witnessPubKeyHash.output.encode(pubkeyhash)
var txb = new TransactionBuilder(network)
txb.addInput(txid, vout, 0xffffffff, scriptPubKey)
txb.addOutput(toSegwitPubkey, amount - 5000)
txb.sign(0, root.keyPair)
var tx = txb.build()
console.log(tx.toBuffer().toString('hex'))

26
latest/fund_p2wsh.js Normal file
View File

@ -0,0 +1,26 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
console.log(root.getAddress())
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
var txid = '79f560d078eacf4cf9381544b15c400773fddd6bbfb1064956e0c345d39be260'
var vout = 0
var scriptPubKey = bscript.pubKeyHash.output.encode(pubkeyhash)
var amount = 70000
var witnessScriptHash = crypto.sha256(scriptPubKey)
var toP2WSH = bscript.witnessScriptHash.output.encode(witnessScriptHash)
var txb = new TransactionBuilder(network)
txb.addInput(txid, vout, 0xffffffff, scriptPubKey)
txb.addOutput(toP2WSH, amount - 5000)
txb.sign(0, root.keyPair)
var tx = txb.build()
console.log(tx.toBuffer().toString('hex'))

View File

@ -0,0 +1,46 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
// var baddress = bitcoin.address
var TransactionBuilder = bitcoin.TransactionBuilder
var TxSigner = bitcoin.TxSigner
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac1100feac14bdfeac14bdfeac14bdfeac14bdfeac') // my entropy
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
// redeem script
var toSegwitPubkey = bscript.witnessPubKeyHash.output.encode(pubkeyhash)
// aixo es l-envoltori del p2sh crec
var p2sh = bscript.scriptHash.output.encode(crypto.hash160(toSegwitPubkey))
// on hem rebut la pasta inicial
// var receiveAddress = baddress.toBase58Check(crypto.hash160(toSegwitPubkey), network.scriptHash)
// txhash de la tx que estem gastant
var txhashUnspent = 'b085099291d44edecfb3a98384f4266282964fe7b0a12d6db9169698cb7e6487'
var vout = 0
// on gastarem la pasta despres
var myaddress = '2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ'
var txOut = {
script: p2sh,
value: 30000
}
var txb = new TransactionBuilder(network)
txb.addInput(txhashUnspent, vout, 0xffffffff, p2sh)
txb.addOutput(myaddress, txOut.value - 5000)
var unsigned = txb.buildIncomplete()
var signer = new TxSigner(unsigned)
signer.sign(0, root.keyPair, {
scriptPubKey: txOut.script,
redeemScript: toSegwitPubkey,
value: txOut.value
})
var txd = signer.done()
console.log(txd.toBufferWithWitness().toString('hex'))

View File

@ -0,0 +1,39 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var TxSigner = bitcoin.TxSigner
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
var witnessScript = bscript.pubKeyHash.output.encode(pubkeyhash)
var p2shScript = bscript.witnessScriptHash.output.encode(crypto.sha256(witnessScript))
var scriptPubKey = bscript.scriptHash.output.encode(crypto.hash160(p2shScript))
var txid = '2f789c63bb88c0ca844cf9ab5c59e1d6e935fa9ae6d6b5bc2c5251fca549f09d'
var vout = 0
var txOut = {
script: scriptPubKey,
value: 90000
}
var builder = new TransactionBuilder(network)
builder.addInput(txid, vout, 0xffffffff, txOut.script)
builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', 10000)
var unsigned = builder.buildIncomplete()
var signer = new TxSigner(unsigned)
signer.sign(0, root.keyPair, {
scriptPubKey: txOut.scriptPubKey,
redeemScript: p2shScript,
witnessScript: witnessScript,
value: txOut.value
})
var txd = signer.done()
console.log(txd.toBuffer().toString('hex'))

View File

@ -0,0 +1,47 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var TxSigner = bitcoin.TxSigner
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var root2 = root.derive(1)
// var address = root.keyPair.getAddress()
// var wif = root.keyPair.toWIF()
// console.log(address)
// console.log(wif)
var multisig = bscript.multisig.output.encode(2, [root.getPublicKeyBuffer(), root2.getPublicKeyBuffer()])
var p2shScript = bscript.witnessScriptHash.output.encode(crypto.sha256(multisig))
var scriptPubKey = bscript.scriptHash.output.encode(crypto.hash160(p2shScript))
var txid = '5d614b47c75ca29a16086e7866de7522e59a09491bbd7e914923f5aabc62616a'
var vout = 0
var txOut = {
script: scriptPubKey,
value: 15000
}
var builder = new TransactionBuilder(network)
builder.addInput(txid, vout, 0xffffffff, txOut.script)
builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', 10000)
var unsigned = builder.buildIncomplete()
var signer = new TxSigner(unsigned)
var data = {
scriptPubKey: txOut.script,
value: txOut.value,
redeemScript: p2shScript,
witnessScript: multisig
}
signer.sign(0, root.keyPair, data)
signer.sign(0, root2.keyPair, data)
var txd = signer.done()
console.log(txd.toBuffer().toString('hex'))

38
latest/spend_p2wpkh.js Normal file
View File

@ -0,0 +1,38 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var TxSigner = bitcoin.TxSigner
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
var address = root.keyPair.getAddress()
var wif = root.keyPair.toWIF()
console.log(address)
console.log(wif)
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
var txid = 'cca0f194d0ca770737121532497f82b3b4eb861df462bbb26978e53acfec7de8'
var vout = 0
var txOut = {
script: bscript.witnessPubKeyHash.output.encode(pubkeyhash),
value: 17000
}
var builder = new TransactionBuilder(network)
builder.addInput(txid, vout, 0xffffffff, txOut.script)
builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', txOut.value - 5000)
var unsigned = builder.buildIncomplete()
var signer = new TxSigner(unsigned)
signer.sign(0, root.keyPair, {
scriptPubKey: txOut.script,
value: txOut.value
})
var txd = signer.done()
console.log(txd.toBuffer().toString('hex'))

42
latest/spend_p2wsh.js Normal file
View File

@ -0,0 +1,42 @@
var bitcoin = require('../src/index.js')
var bscript = bitcoin.script
var crypto = bitcoin.crypto
var networks = bitcoin.networks
var TransactionBuilder = bitcoin.TransactionBuilder
var TxSigner = bitcoin.TxSigner
var network = networks.testnet
var entropy = new Buffer('14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac14bdfeac')
var root = bitcoin.HDNode.fromSeedBuffer(entropy, network)
// var address = root.keyPair.getAddress()
// var wif = root.keyPair.toWIF()
// console.log(address)
// console.log(wif)
var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
var witnessScript = bscript.pubKeyHash.output.encode(pubkeyhash)
var scriptHash = crypto.sha256(witnessScript)
var scriptPubKey = bscript.witnessScriptHash.output.encode(scriptHash)
var txid = '6d1c2682f553889e3887762fcf4669ab3844c6803d6c5c366bc2909bbe33cbf9'
var vout = 0
var txOut = {
script: scriptPubKey,
value: 65000
}
var builder = new TransactionBuilder(network)
builder.addInput(txid, vout, 0xffffffff, txOut.script)
builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', 10000)
var unsigned = builder.buildIncomplete()
var signer = new TxSigner(unsigned)
signer.sign(0, root.keyPair, {
scriptPubKey: scriptPubKey,
witnessScript: witnessScript,
value: txOut.value
})
var txd = signer.done()
console.log(txd.toBuffer().toString('hex'))

View File

@ -5,7 +5,7 @@ module.exports = {
HDNode: require('./hdnode'),
Transaction: require('./transaction'),
TransactionBuilder: require('./transaction_builder'),
TxSigner: require('./transaction_signer'),
address: require('./address'),
bufferutils: require('./bufferutils'),
crypto: require('./crypto'),

View File

@ -34,7 +34,8 @@ Transaction.SIGHASH_SINGLE = 0x03
Transaction.SIGHASH_ANYONECANPAY = 0x80
Transaction.ADVANCED_TRANSACTION_MARKER = 0x00
Transaction.ADVANCED_TRANSACTION_FLAG = 0x01
Transaction.SIG_V0 = 0
Transaction.SIG_V1 = 1
var EMPTY_SCRIPT = new Buffer(0)
var EMPTY_WITNESS = []
var ZERO = new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')

516
src/transaction_signer.js Normal file
View File

@ -0,0 +1,516 @@
var bscript = require('./script')
var OPS = require('./opcodes.json')
var bscriptNumber = require('./script_number')
var bcrypto = require('./crypto')
var bufferEquals = require('buffer-equals')
var typeforce = require('typeforce')
var types = require('./types')
var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var Transaction = require('./transaction')
var EMPTY_SCRIPT = new Buffer(0)
var SIGNABLE_SCRIPTS = [
bscript.types.MULTISIG,
bscript.types.P2PKH,
bscript.types.P2PK
]
var ALLOWED_P2SH_SCRIPTS = [
bscript.types.MULTISIG,
bscript.types.P2PKH,
bscript.types.P2PK,
bscript.types.P2WSH,
bscript.types.P2WPKH
]
function calculateSigHash (tx, nIn, scriptCode, sigHashType, sigVersion, txOutAmount) {
return sigVersion === 1
? tx.hashForWitnessV0(nIn, scriptCode, txOutAmount, sigHashType)
: tx.hashForSignature(nIn, scriptCode, sigHashType)
}
function sortMultisigs (tx, nIn, txOutValue, ecsigs, publicKeys, scriptCode, sigVersion) {
var results = []
var hash
var ikey = 0
var isig = 0
var sig, key
var success = true
var sigsCount = ecsigs.length
var keysCount = publicKeys.length
while (success && ecsigs.length > 0) {
sig = ECSignature.parseScriptSignature(ecsigs[isig])
key = ECPair.fromPublicKeyBuffer(publicKeys[ikey])
hash = calculateSigHash(tx, nIn, scriptCode, sig.hashType, sigVersion, txOutValue)
if (key.verify(hash, sig.signature)) {
isig++
results[key.getPublicKeyBuffer().toString('binary')] = ecsigs[isig]
}
ikey++
if (sigsCount > keysCount) {
success = false
}
}
return results
}
function calculateSignature (tx, nIn, key, scriptCode, sigHashType, sigVersion, txOutValue) {
var hash = calculateSigHash(tx, nIn, scriptCode, sigHashType, sigVersion, txOutValue)
return key.sign(hash).toScriptSignature(sigHashType)
}
function pushOnlyRead (op) {
if (op === OPS.OP_0) {
return new Buffer()
} else if (op instanceof Buffer) {
return op
} else if (op === OPS.OP_1NEGATE || op >= OPS.OP_1 && op <= OPS.OP_16) {
return bscriptNumber.encode(op - 0x50)
} else {
throw new Error('Should only be run on a push-only script')
}
}
function pushOnlyWrite (buffer) {
if (!(buffer instanceof Buffer)) {
throw new Error('Non-buffer passed to pushOnlyWrite')
}
if (buffer.length === 0) {
return OPS.OP_0
} else if (buffer.length === 1 && (buffer[0] === 0x81 || buffer[0] >= 1 && buffer[0] <= 16)) {
return bscriptNumber.decode(buffer)
} else {
return buffer
}
}
function evalPushOnly (script) {
return bscript.decompile(script).map(pushOnlyRead)
}
function pushAll (chunks) {
return bscript.compile(chunks.map(pushOnlyWrite))
}
function solveScript (scriptCode) {
if (!(scriptCode instanceof Buffer)) {
throw new Error('Argument 0 for solveScript must be a Buffer')
}
var outputType = bscript.classifyOutput(scriptCode)
var canSign = SIGNABLE_SCRIPTS.indexOf(outputType) !== -1
var solvedBy = null
var requiredSigs = null
switch (outputType) {
// We can only determine the relevant hash from these, not if it's signable
case bscript.types.P2SH:
solvedBy = bscript.scriptHash.output.decode(scriptCode)
break
case bscript.types.P2WSH:
solvedBy = bscript.witnessScriptHash.output.decode(scriptCode)
break
// We can immediately solve signatures for these
// When adding a new script type, edit here
case bscript.types.P2WPKH:
requiredSigs = 1
solvedBy = bscript.witnessPubKeyHash.output.decode(scriptCode)
break
case bscript.types.P2PK:
requiredSigs = 1
solvedBy = bscript.pubKey.output.decode(scriptCode)
break
case bscript.types.P2PKH:
requiredSigs = 1
solvedBy = bscript.pubKeyHash.output.decode(scriptCode)
break
case bscript.types.MULTISIG:
solvedBy = bscript.multisig.output.decode(scriptCode)
requiredSigs = solvedBy.m
break
}
return {
type: outputType,
script: scriptCode,
canSign: canSign,
solvedBy: solvedBy,
requiredSigs: requiredSigs
}
}
/**
* Design goals
*
* - tolerate arbitrary sigHashType's on signatures
* - given tx, nIn, txOut, we can reliably check a redeemScript and eventual witnessScript at signing
* - reliably extract signatures from a signed input
* - create, and re-serialize given minimal state
* - clear separation of 'standard scripts' and the various script-hash scripts
*
* @param tx - the transaction we want to sign
* @param nIn - the input we will sign here
* @param opts
*/
function InSigner (tx, nIn, opts) {
if ((tx instanceof Transaction) === false) {
throw new Error('A transaction is required for InSigner')
}
if (opts.scriptPubKey === undefined) {
throw new Error('A value for scriptPubKey is required')
}
this.tx = tx
this.nIn = nIn
this.publicKeys = []
this.signatures = []
this.requiredSigs = null
this.solve(opts)
this.extractSig()
}
InSigner.prototype.isFullySigned = function () {
return this.requiredSigs !== null && this.requiredSigs === this.signatures.length
}
InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) {
var signatures = []
var publicKeys = []
var decoded
// only SIGNABLE_SCRIPTS can be extracted here
if (solution.type === bscript.types.P2PK) {
if (bscript.pubKey.input.check(chunks)) {
decoded = bscript.pubKey.input.decode(chunks)
signatures[0] = decoded.signature
publicKeys[0] = solution.solvedBy
}
} else if (solution.type === bscript.types.P2PKH) {
if (bscript.pubKeyHash.input.check(chunks)) {
decoded = bscript.pubKeyHash.input.decode(chunks)
signatures[0] = decoded.signature
publicKeys[0] = decoded.pubKey
}
} else if (solution.type === bscript.types.MULTISIG) {
if (bscript.multisig.input.check(chunks)) {
publicKeys = solution.solvedBy.publicKeys
signatures = bscript.multisig.input.decode(chunks, true)
// We need to map signature to the pubkey index in order to re-serialize
var sigs = sortMultisigs(this.tx, this.nIn, this.txOut.value, signatures, publicKeys, solution.script, sigVersion)
for (var i = 0, l = publicKeys.length; i < l; i++) {
var str = publicKeys[ i ].getPublicKeyBuffer().toString('binary')
if (sigs[ str ] !== undefined && bscript.isCanonicalSignature(sigs[str])) {
signatures[ i ] = sigs[ str ]
}
}
}
} else {
throw new Error('Never call extractStandardFromChunks on a non-SIGNABLE script')
}
return [signatures, publicKeys]
}
InSigner.prototype.solve = function (opts) {
var solution = solveScript(opts.scriptPubKey)
if (solution.type === bscript.types.NONSTANDARD) {
throw new Error('txOut script is non-standard')
}
this.scriptPubKey = solution
if (solution.type === bscript.types.P2SH) {
var scriptHash = solution.solvedBy
if (!(opts.redeemScript instanceof Buffer)) {
throw new Error('Redeem script required to solve utxo')
}
if (!scriptHash.equals(bcrypto.hash160(opts.redeemScript))) {
throw new Error('Redeem script does not match txOut script hash')
}
solution = solveScript(opts.redeemScript)
if (ALLOWED_P2SH_SCRIPTS.indexOf(solution.type)) {
this.redeemScript = solution
} else {
throw new Error('Unsupported P2SH script')
}
}
if (solution.type === bscript.types.P2WPKH) {
if (!types.Satoshi(opts.value)) {
throw new Error('Value is required for witness-key-hash')
}
this.value = opts.value
} else if (solution.type === bscript.types.P2WSH) {
var witnessScriptHash = solution.solvedBy
if (!(opts.witnessScript instanceof Buffer)) {
throw new Error('P2WSH script required to solve utxo')
}
if (!types.Satoshi(opts.value)) {
throw new Error('Value is required for witness-script-hash')
}
if (!bufferEquals(bcrypto.sha256(opts.witnessScript), witnessScriptHash)) {
throw new Error('Witness script does not match txOut script hash')
}
this.witnessScript = solveScript(opts.witnessScript)
this.value = opts.value
if (SIGNABLE_SCRIPTS.indexOf(this.witnessScript.type) === -1) {
throw new Error('witness script is not supported')
}
}
}
InSigner.prototype.extractSig = function () {
// Attempt decoding of the input scriptSig and witness
var input = this.tx.ins[this.nIn]
var solution = this.scriptPubKey
if (solution.canSign) {
[this.signatures, this.publicKeys] = this.extractStandard(solution, evalPushOnly(input.script), Transaction.SIG_V0)
}
if (solution.type === bscript.types.P2SH) {
if (bscript.scriptHash.input.check(input.script)) {
var p2sh = bscript.scriptHash.input.decode(input.script)
if (!p2sh.redeemScript.equals(this.redeemScript.script)) {
throw new Error('Redeem script from scriptSig does not match')
}
if (this.redeemScript.canSign) {
[this.signatures, this.publicKeys] = this.extractStandard(solution, evalPushOnly(p2sh.redeemScriptSig), Transaction.SIG_V0)
}
solution = this.redeemScript
}
}
if (solution.type === bscript.types.P2WPKH) {
if (input.witness.length === 2) {
var witnessKeyHash = solution.solvedBy
if (!witnessKeyHash.equals(bcrypto.hash160(input.witness[1]))) {
throw new Error('Public key does not match key-hash')
}
[this.signatures, this.publicKeys] = this.extractStandard(bscript.types.P2PKH, input.witness)
}
} else if (solution.type === bscript.types.P2WSH) {
if (input.witness.length > 0) {
if (!this.witnessScript.equals(input.witness[ input.witness.length - 1 ])) {
throw new Error('Witness script does not match')
}
if (this.witnessScript.canSign) {
[ this.signatures, this.publicKeys ] = this.extractStandard(solution, input.witness.slice(0, -1), Transaction.SIG_V1)
}
}
}
}
function signStandard (tx, nIn, txOutValue, signatures, publicKeys, key, solution, sigHashType, sigVersion) {
// Only SIGNABLE_SCRIPTS can be signed here
var didSign = false
var keyBuffer = key.getPublicKeyBuffer()
if (solution.type === bscript.types.P2PK) {
if (bufferEquals(keyBuffer, solution.solvedBy)) {
signatures = [calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)]
publicKeys = [keyBuffer]
didSign = true
}
} else if (solution.type === bscript.types.P2PKH) {
if (bufferEquals(bcrypto.hash160(keyBuffer), solution.solvedBy)) {
signatures = [calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)]
publicKeys = [keyBuffer]
didSign = true
}
} else if (solution.type === bscript.types.MULTISIG) {
for (var i = 0, keyLen = solution.solvedBy.pubKeys.length; i < keyLen; i++) {
publicKeys[i] = solution.solvedBy.pubKeys[i]
if (bufferEquals(keyBuffer, publicKeys[i])) {
didSign = true
signatures[i] = calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)
}
}
} else {
throw new Error('signStandard can only sign SIGNABLE_SCRIPTS')
}
if (!didSign) {
throw new Error('Signing input with wrong private key')
}
return [signatures, publicKeys]
}
InSigner.prototype.sign = function (key, sigHashType) {
sigHashType = sigHashType || Transaction.SIGHASH_ALL
// Attempt to solve the txOut script
var solution = this.scriptPubKey
if (solution.canSign) {
[this.signatures, this.publicKeys] = signStandard(this.tx, this.nIn, undefined, this.signatures, this.publicKeys, key, solution, sigHashType, Transaction.SIG_V0)
}
// If the spkPubKeyHash was solvable, and the type is P2SH, we try again with the redeemScript
if (solution.type === bscript.types.P2SH) {
// solution updated, type is the type of the redeemScript
solution = this.redeemScript
if (bscript.P2SH_SCRIPTS.indexOf(solution.type)) {
if (solution.canSign) {
[this.signatures, this.publicKeys] = signStandard(this.tx, this.nIn, undefined, this.signatures, this.publicKeys, key, solution, sigHashType, Transaction.SIG_V0)
}
}
}
if (solution.type === bscript.types.P2WPKH) {
var p2wpkh = solveScript(bscript.pubKeyHash.output.encode(solution.solvedBy));
[this.signatures, this.publicKeys] = signStandard(this.tx, this.nIn, this.value, this.signatures, this.publicKeys, key, p2wpkh, sigHashType, Transaction.SIG_V1)
} else if (solution.type === bscript.types.P2WSH) {
solution = this.witnessScript
if (solution.canSign) {
[this.signatures, this.publicKeys] = signStandard(this.tx, this.nIn, this.value, this.signatures, this.publicKeys, key, solution, sigHashType, Transaction.SIG_V1)
}
}
this.requiredSigs = this.publicKeys.length
return solution
}
function serializeStandard (outputType, signatures, publicKeys) {
// When adding a new script type, edit here
var chunks = []
switch (outputType) {
case bscript.types.P2PK:
if (signatures.length === 1) {
chunks = [signatures[ 0 ]]
}
break
case bscript.types.P2PKH:
if (signatures.length === 1) {
chunks = [signatures[ 0 ], publicKeys[ 0 ]]
}
break
case bscript.types.MULTISIG:
chunks.push(new Buffer(0))
chunks = chunks.concat(signatures.map(function (signature) {
if (signature instanceof Buffer === false) {
throw new Error('debugging probably required')
}
return signature
}))
break
default:
throw new Error('serializeStandardChunks only works with a SIGNABLE_SCRIPT')
}
return chunks
}
function serializeSigData (signatures, publicKeys, scriptPubKey, redeemScript, witnessScript) {
var sigData = {
scriptSig: EMPTY_SCRIPT,
witness: []
}
var type = scriptPubKey.type
if (scriptPubKey.canSign) {
sigData.scriptSig = pushAll(serializeStandard(type, signatures, publicKeys))
}
var p2sh = false
if (type === bscript.types.P2SH) {
if (redeemScript === undefined) {
throw new Error('Redeem script not provided')
}
p2sh = true
type = redeemScript.type
if (redeemScript.canSign) {
sigData.scriptSig = pushAll(serializeStandard(type, signatures, publicKeys))
}
}
if (type === bscript.types.P2WPKH) {
sigData.witness = serializeStandard(bscript.types.P2PKH, signatures, publicKeys)
} else if (type === bscript.types.P2WSH) {
if (witnessScript === undefined) {
throw new Error('Witness script not provided')
}
type = witnessScript.type
if (witnessScript.canSign) {
sigData.scriptSig = EMPTY_SCRIPT
sigData.witness = serializeStandard(type, signatures, publicKeys)
sigData.witness.push(witnessScript.script)
}
}
if (p2sh) {
sigData.scriptSig = bscript.scriptHash.input.encode(sigData.scriptSig, redeemScript.script)
}
return sigData
}
InSigner.prototype.serializeSigData = function () {
return serializeSigData(this.signatures, this.publicKeys, this.scriptPubKey, this.redeemScript, this.witnessScript)
}
/**
* Create a TxSigner for this transaction instance
* @param tx
* @constructor
*/
function TxSigner (tx) {
if (tx === undefined || (tx instanceof Transaction) === false) {
throw new Error('A transaction is required for TxSigner')
}
this.tx = tx.clone()
this.states = []
}
/**
* Sign a transaction.
*
* @param nIn - the input to sign
* @param key - the private key to sign with
* @param sigHashType - SIGHASH type to sign with
* @param opts - optional data required to solve UTXO
*/
TxSigner.prototype.sign = function (nIn, key, opts, sigHashType) {
typeforce(types.Number, nIn)
typeforce(types.maybe(Number), sigHashType)
if (sigHashType === undefined) {
sigHashType = Transaction.SIGHASH_ALL
}
// You can probably make this work with the current library, if you can work out the witnessScript above!
// generate opts for the internal signer based off older way of positional arguments to TxSigner.sign
if (this.states[nIn] === undefined) {
this.states[nIn] = new InSigner(this.tx, nIn, opts)
}
if (!this.states[nIn].sign(key, sigHashType)) {
throw new Error('Unsignable input: ', nIn)
}
return true
}
/**
* Produce a Transaction with our changes.
*/
TxSigner.prototype.done = function () {
var tx = this.tx.clone()
var states = this.states
for (var i = 0, l = tx.ins.length; i < l; i++) {
if (this.states[i] !== undefined) {
var sigData = states[i].serializeSigData()
tx.ins[i].script = sigData.scriptSig
tx.ins[i].witness = sigData.witness
}
}
return tx
}
module.exports = TxSigner