Compare commits
10 Commits
09bdc1deee
...
3734cdefd5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3734cdefd5 | ||
|
|
b27b72d775 | ||
|
|
b0bda2e3f2 | ||
|
|
244c3d4f18 | ||
|
|
fdcd277ba9 | ||
|
|
155c9afe05 | ||
|
|
3a03691b7d | ||
|
|
b09cf89f97 | ||
|
|
c215f350fc | ||
|
|
c3ed014488 |
@ -3,24 +3,28 @@ 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 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 receivePK = bscript.pubKeyHash.output.encode(crypto.hash160(root.keyPair.getPublicKeyBuffer()))
|
||||
var receiveAmount = 22000
|
||||
|
||||
var fundP2shScript = bscript.witnessPubKeyHash.output.encode(crypto.hash160(root.keyPair.getPublicKeyBuffer()))
|
||||
var fundScriptPubKey = bscript.scriptHash.output.encode(crypto.hash160(fundP2shScript))
|
||||
|
||||
var txb = new TransactionBuilder(network)
|
||||
txb.addInput(txid, vout, 0xffffffff)
|
||||
txb.addOutput(scriptPubKey, amount - 5000)
|
||||
txb.sign(0, root.keyPair)
|
||||
var tx = txb.build()
|
||||
txb.addOutput(fundScriptPubKey, receiveAmount - 5000)
|
||||
|
||||
var unsigned = txb.buildIncomplete()
|
||||
var signer = new TxSigner(unsigned)
|
||||
signer.sign(0, root.keyPair, {
|
||||
scriptPubKey: receivePK
|
||||
})
|
||||
var tx = signer.done()
|
||||
console.log(tx.toBuffer().toString('hex'))
|
||||
|
||||
@ -3,6 +3,7 @@ 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')
|
||||
@ -12,18 +13,20 @@ var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
|
||||
|
||||
var txid = 'beb647db98bda750f8202e6bc3441781ea5cfc6e3630c9d0ae47b0bfb111c249'
|
||||
var vout = 1
|
||||
var txOut = {
|
||||
script: bscript.pubKeyHash.output.encode(pubkeyhash),
|
||||
value: 100000
|
||||
}
|
||||
var receiveAmount = 10000
|
||||
var receivePK = bscript.pubKeyHash.output.encode(pubkeyhash)
|
||||
|
||||
var witnessScript = txOut.script
|
||||
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 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()
|
||||
txb.addInput(txid, vout, 0xffffffff)
|
||||
txb.addOutput(scriptPubKey, receiveAmount - 10000)
|
||||
|
||||
var txs = new TxSigner(txb.buildIncomplete())
|
||||
txs.sign(0, root.keyPair, {
|
||||
scriptPubKey: receivePK
|
||||
})
|
||||
var tx = txs.done()
|
||||
console.log(tx.toBuffer().toString('hex'))
|
||||
|
||||
@ -3,6 +3,7 @@ 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')
|
||||
@ -13,20 +14,20 @@ var pubkeyhash = crypto.hash160(root.keyPair.getPublicKeyBuffer())
|
||||
|
||||
var txid = 'aed14f8e918c6e7cc9347391b790f765030b07e6985fbb146bf3f6b25ddc0043'
|
||||
var vout = 0
|
||||
var txOut = {
|
||||
script: bscript.pubKeyHash.output.encode(pubkeyhash),
|
||||
value: 22000
|
||||
}
|
||||
var receivePK = bscript.pubKeyHash.output.encode(pubkeyhash)
|
||||
var receiveAmount = 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'))
|
||||
txb.addInput(txid, vout, 0xffffffff)
|
||||
txb.addOutput(scriptPubKey, receiveAmount - 5000)
|
||||
|
||||
// b5e7c1d911b078c754770fc372bc92096ce2605ac6b785f91ec7df0a5034ec69
|
||||
var txs = new TxSigner(txb.buildIncomplete())
|
||||
txs.sign(0, root.keyPair, {
|
||||
scriptPubKey: receivePK
|
||||
})
|
||||
var tx = txs.done()
|
||||
console.log(tx.toBuffer().toString('hex'))
|
||||
|
||||
@ -3,27 +3,29 @@ 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 = '9aa8d1a1c5df0afccf76e84df1029062b65a98dad68e13cc765aef88ab378dd0'
|
||||
var vout = 0
|
||||
var scriptPubKey = bscript.pubKeyHash.output.encode(pubkeyhash)
|
||||
var amount = 22000
|
||||
var receivePK = bscript.pubKeyHash.output.encode(pubkeyhash)
|
||||
var receiveAmount = 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()
|
||||
txb.addInput(txid, vout, 0xffffffff)
|
||||
txb.addOutput(toSegwitPubkey, receiveAmount - 5000)
|
||||
|
||||
var txs = new TxSigner(txb.buildIncomplete())
|
||||
txs.sign(0, root.keyPair, {
|
||||
scriptPubKey: receivePK
|
||||
})
|
||||
|
||||
var tx = txs.done()
|
||||
console.log(tx.toBuffer().toString('hex'))
|
||||
|
||||
@ -3,24 +3,28 @@ 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)
|
||||
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 receivePK = bscript.pubKeyHash.output.encode(pubkeyhash)
|
||||
var receiveAmount = 70000
|
||||
|
||||
var witnessScriptHash = crypto.sha256(scriptPubKey)
|
||||
var toP2WSH = bscript.witnessScriptHash.output.encode(witnessScriptHash)
|
||||
var witnessScript = bscript.pubKeyHash.output.encode(pubkeyhash)
|
||||
var toP2WSH = bscript.witnessScriptHash.output.encode(crypto.sha256(witnessScript))
|
||||
|
||||
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()
|
||||
txb.addInput(txid, vout, 0xffffffff)
|
||||
txb.addOutput(toP2WSH, receiveAmount - 5000)
|
||||
|
||||
var txs = new TxSigner(txb.buildIncomplete())
|
||||
txs.sign(0, root.keyPair, {
|
||||
scriptPubKey: receivePK
|
||||
})
|
||||
var tx = txs.done()
|
||||
console.log(tx.toBuffer().toString('hex'))
|
||||
|
||||
@ -37,10 +37,17 @@ txb.addOutput(myaddress, txOut.value - 5000)
|
||||
|
||||
var unsigned = txb.buildIncomplete()
|
||||
var signer = new TxSigner(unsigned)
|
||||
signer.sign(0, root.keyPair, {
|
||||
|
||||
var opts = {
|
||||
scriptPubKey: txOut.script,
|
||||
redeemScript: toSegwitPubkey,
|
||||
value: txOut.value
|
||||
})
|
||||
}
|
||||
signer.sign(0, root.keyPair, opts)
|
||||
var txd = signer.done()
|
||||
console.log(txd.toBuffer().toString('hex'))
|
||||
|
||||
var testSigner = new TxSigner(txd)
|
||||
|
||||
console.log(testSigner.signer(0, opts).isFullySigned());
|
||||
console.log(testSigner.done().toBuffer().equals(txd.toBuffer()))
|
||||
@ -28,12 +28,17 @@ builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', 10000)
|
||||
|
||||
var unsigned = builder.buildIncomplete()
|
||||
var signer = new TxSigner(unsigned)
|
||||
signer.sign(0, root.keyPair, {
|
||||
var opts = {
|
||||
scriptPubKey: txOut.script,
|
||||
redeemScript: p2shScript,
|
||||
witnessScript: witnessScript,
|
||||
value: txOut.value
|
||||
})
|
||||
}
|
||||
signer.sign(0, root.keyPair, opts)
|
||||
|
||||
var txd = signer.done()
|
||||
console.log(txd.toBuffer().toString('hex'))
|
||||
var testSigner = new TxSigner(txd)
|
||||
|
||||
console.log(testSigner.signer(0, opts).isFullySigned());
|
||||
console.log(testSigner.done().toBuffer().equals(txd.toBuffer()))
|
||||
@ -33,15 +33,19 @@ builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', 10000)
|
||||
var unsigned = builder.buildIncomplete()
|
||||
var signer = new TxSigner(unsigned)
|
||||
|
||||
var data = {
|
||||
var opts = {
|
||||
scriptPubKey: txOut.script,
|
||||
value: txOut.value,
|
||||
redeemScript: p2shScript,
|
||||
witnessScript: multisig
|
||||
}
|
||||
|
||||
signer.sign(0, root.keyPair, data)
|
||||
signer.sign(0, root2.keyPair, data)
|
||||
signer.sign(0, root.keyPair, opts)
|
||||
signer.sign(0, root2.keyPair, opts)
|
||||
|
||||
var txd = signer.done()
|
||||
console.log(txd.toBuffer().toString('hex'))
|
||||
var testSigner = new TxSigner(txd)
|
||||
|
||||
console.log(testSigner.signer(0, opts).isFullySigned())
|
||||
console.log(testSigner.done().toBuffer().equals(txd.toBuffer()))
|
||||
@ -28,11 +28,16 @@ builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', txOut.value - 5000)
|
||||
|
||||
var unsigned = builder.buildIncomplete()
|
||||
var signer = new TxSigner(unsigned)
|
||||
signer.sign(0, root.keyPair, {
|
||||
var opts = {
|
||||
scriptPubKey: txOut.script,
|
||||
value: txOut.value
|
||||
})
|
||||
}
|
||||
signer.sign(0, root.keyPair, opts)
|
||||
|
||||
var txd = signer.done()
|
||||
|
||||
console.log(txd.toBuffer().toString('hex'))
|
||||
var testSigner = new TxSigner(txd)
|
||||
|
||||
console.log(testSigner.signer(0, opts).isFullySigned());
|
||||
console.log(testSigner.done().toBuffer().equals(txd.toBuffer()))
|
||||
@ -31,12 +31,17 @@ builder.addOutput('2N6stcWuMpLgt4nkiaEFXP6p9J9VKRHCwDJ', 10000)
|
||||
|
||||
var unsigned = builder.buildIncomplete()
|
||||
var signer = new TxSigner(unsigned)
|
||||
signer.sign(0, root.keyPair, {
|
||||
var opts = {
|
||||
scriptPubKey: scriptPubKey,
|
||||
witnessScript: witnessScript,
|
||||
value: txOut.value
|
||||
})
|
||||
}
|
||||
signer.sign(0, root.keyPair, opts)
|
||||
|
||||
var txd = signer.done()
|
||||
console.log(txd.toBuffer().toString('hex'))
|
||||
|
||||
var testSigner = new TxSigner(txd)
|
||||
|
||||
console.log(testSigner.signer(0, opts).isFullySigned())
|
||||
console.log(testSigner.done().toBuffer().equals(txd.toBuffer()))
|
||||
@ -5,7 +5,7 @@ var typeforce = require('typeforce')
|
||||
|
||||
function check (script, allowIncomplete) {
|
||||
var chunks = bscript.decompile(script)
|
||||
if (chunks.length < 2) return false
|
||||
if (chunks.length < 1) return false
|
||||
|
||||
var lastChunk = chunks[chunks.length - 1]
|
||||
if (!Buffer.isBuffer(lastChunk)) return false
|
||||
@ -13,11 +13,12 @@ function check (script, allowIncomplete) {
|
||||
var scriptSigChunks = chunks.slice(0, -1)
|
||||
var redeemScriptChunks = bscript.decompile(lastChunk)
|
||||
|
||||
// is redeemScript a valid script?
|
||||
if (redeemScriptChunks.length === 0) return false
|
||||
var outputType = bscript.classifyOutput(redeemScriptChunks)
|
||||
if (outputType === bscript.types.P2WSH || outputType === bscript.types.P2WPKH) {
|
||||
return true
|
||||
}
|
||||
|
||||
var inputType = bscript.classifyInput(scriptSigChunks, allowIncomplete)
|
||||
var outputType = bscript.classifyOutput(redeemScriptChunks)
|
||||
return inputType === outputType
|
||||
}
|
||||
check.toJSON = function () { return 'scriptHash input' }
|
||||
|
||||
@ -8,7 +8,6 @@ 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,
|
||||
@ -38,13 +37,14 @@ function sortMultisigs (tx, nIn, txOutValue, ecsigs, publicKeys, scriptCode, sig
|
||||
var success = true
|
||||
var sigsCount = ecsigs.length
|
||||
var keysCount = publicKeys.length
|
||||
while (success && ecsigs.length > 0) {
|
||||
|
||||
while (success && ecsigs.length > isig) {
|
||||
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]
|
||||
results[key.getPublicKeyBuffer().toString('binary')] = sig
|
||||
}
|
||||
ikey++
|
||||
if (sigsCount > keysCount) {
|
||||
@ -94,54 +94,6 @@ 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
|
||||
*
|
||||
@ -159,6 +111,9 @@ function InSigner (tx, nIn, opts) {
|
||||
if ((tx instanceof Transaction) === false) {
|
||||
throw new Error('A transaction is required for InSigner')
|
||||
}
|
||||
if (tx.ins[nIn] === undefined) {
|
||||
throw new Error('No transaction input at this index')
|
||||
}
|
||||
if (opts.scriptPubKey === undefined) {
|
||||
throw new Error('A value for scriptPubKey is required')
|
||||
}
|
||||
@ -167,16 +122,93 @@ function InSigner (tx, nIn, opts) {
|
||||
this.nIn = nIn
|
||||
this.publicKeys = []
|
||||
this.signatures = []
|
||||
this.requiredSigs = null
|
||||
this.requiredSigs = 0
|
||||
this.solve(opts)
|
||||
this.extractSig()
|
||||
}
|
||||
|
||||
InSigner.prototype.isFullySigned = function () {
|
||||
return this.requiredSigs !== null && this.requiredSigs === this.signatures.length
|
||||
InSigner.prototype.solve = function (opts) {
|
||||
// Link scriptPubKey, and check that redeemScript/witnessScript/amount are provided
|
||||
// Sets sigVersion, signScript, scriptPubKey (solution), redeemScript (solution), and witnessScript (solution)
|
||||
|
||||
// Determine scriptPubKey is solvable
|
||||
var scriptPubKey = bscript.solveOutput(opts.scriptPubKey)
|
||||
if (scriptPubKey.type !== bscript.types.P2SH && ALLOWED_P2SH_SCRIPTS.indexOf(scriptPubKey.type) === -1) {
|
||||
throw new Error('txOut script is non-standard')
|
||||
}
|
||||
|
||||
var solution = scriptPubKey
|
||||
var redeemScript
|
||||
var witnessScript
|
||||
var sigVersion = Transaction.SIG_V0
|
||||
var value = 0
|
||||
|
||||
if (solution.type === bscript.types.P2SH) {
|
||||
var scriptHash = solution.solvedBy
|
||||
// Redeem script must be provided by opts
|
||||
if (!(opts.redeemScript instanceof Buffer)) {
|
||||
throw new Error('Redeem script required to solve utxo')
|
||||
}
|
||||
// Check redeemScript against script-hash
|
||||
if (!scriptHash.equals(bcrypto.hash160(opts.redeemScript))) {
|
||||
throw new Error('Redeem script does not match txOut script hash')
|
||||
}
|
||||
// Prevent non-standard P2SH scripts
|
||||
solution = bscript.solveOutput(opts.redeemScript)
|
||||
if (!ALLOWED_P2SH_SCRIPTS.indexOf(solution.type)) {
|
||||
throw new Error('Unsupported P2SH script')
|
||||
}
|
||||
redeemScript = solution
|
||||
}
|
||||
|
||||
if (solution.type === bscript.types.P2WPKH) {
|
||||
// txOutValue is required here
|
||||
if (!types.Satoshi(opts.value)) {
|
||||
throw new Error('Value is required for witness-key-hash')
|
||||
}
|
||||
// We set solution to this to avoid work later, since P2WPKH is a special case of P2PKH
|
||||
solution = bscript.solveOutput(bscript.pubKeyHash.output.encode(solution.solvedBy))
|
||||
sigVersion = Transaction.SIG_V1
|
||||
value = opts.value
|
||||
} else if (solution.type === bscript.types.P2WSH) {
|
||||
var witnessScriptHash = solution.solvedBy
|
||||
// Witness script must be provided by opts
|
||||
if (!(opts.witnessScript instanceof Buffer)) {
|
||||
throw new Error('P2WSH script required to solve utxo')
|
||||
}
|
||||
// txOutValue is required here
|
||||
if (!types.Satoshi(opts.value)) {
|
||||
throw new Error('Value is required for witness-script-hash')
|
||||
}
|
||||
// Check witnessScript against script hash
|
||||
if (!bcrypto.sha256(opts.witnessScript).equals(witnessScriptHash)) {
|
||||
throw new Error('Witness script does not match txOut script hash')
|
||||
}
|
||||
// Prevent non-standard P2WSH scripts
|
||||
solution = bscript.solveOutput(opts.witnessScript)
|
||||
if (SIGNABLE_SCRIPTS.indexOf(solution.type) === -1) {
|
||||
throw new Error('Witness script is not supported')
|
||||
}
|
||||
witnessScript = solution
|
||||
sigVersion = Transaction.SIG_V1
|
||||
value = opts.value
|
||||
}
|
||||
|
||||
this.requiredSigs = solution.requiredSigs
|
||||
this.scriptPubKey = scriptPubKey
|
||||
this.redeemScript = redeemScript
|
||||
this.witnessScript = witnessScript
|
||||
this.value = value
|
||||
this.sigVersion = sigVersion
|
||||
this.signScript = solution
|
||||
}
|
||||
|
||||
InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) {
|
||||
InSigner.prototype.isFullySigned = function () {
|
||||
return this.requiredSigs !== null && this.signatures.length === this.requiredSigs
|
||||
}
|
||||
|
||||
InSigner.prototype.extractSignableChunks = function (solution, chunks, sigVersion) {
|
||||
// Only SIGNABLE_SCRIPTS can be extracted here
|
||||
var signatures = []
|
||||
var publicKeys = []
|
||||
var decoded
|
||||
@ -185,24 +217,31 @@ InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) {
|
||||
if (solution.type === bscript.types.P2PK) {
|
||||
if (bscript.pubKey.input.check(chunks)) {
|
||||
decoded = bscript.pubKey.input.decode(chunks)
|
||||
if (!decoded.pubKey.equals(solution.solvedBy)) {
|
||||
throw new Error('Key in scriptSig does not match output script key')
|
||||
}
|
||||
|
||||
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)
|
||||
if (!bcrypto.hash160(decoded.pubKey).equals(solution.solvedBy)) {
|
||||
throw new Error('Key in scriptSig does not match output script key-hash')
|
||||
}
|
||||
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)
|
||||
if (bscript.multisig.input.check(pushAll(chunks))) {
|
||||
publicKeys = solution.solvedBy.pubKeys
|
||||
signatures = bscript.multisig.input.decode(pushAll(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)
|
||||
var sigs = sortMultisigs(this.tx, this.nIn, this.value, signatures, publicKeys, solution.script, sigVersion)
|
||||
for (var i = 0, l = publicKeys.length; i < l; i++) {
|
||||
var str = publicKeys[ i ].getPublicKeyBuffer().toString('binary')
|
||||
var str = publicKeys[ i ].toString('binary')
|
||||
if (sigs[ str ] !== undefined && bscript.isCanonicalSignature(sigs[str])) {
|
||||
signatures[ i ] = sigs[ str ]
|
||||
}
|
||||
@ -215,73 +254,28 @@ InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) {
|
||||
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)
|
||||
var extractChunks = []
|
||||
if (SIGNABLE_SCRIPTS.indexOf(solution.type) !== -1) {
|
||||
extractChunks = evalPushOnly(input.script)
|
||||
}
|
||||
|
||||
if (solution.type === bscript.types.P2SH) {
|
||||
if (bscript.scriptHash.input.check(input.script)) {
|
||||
extractChunks = bscript.decompile(input.script)
|
||||
if (extractChunks.length > 0) {
|
||||
// If we go to extract a P2SH scriptSig, verify the provided redeemScript
|
||||
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 (SIGNABLE_SCRIPTS.indexOf(solution.type) !== -1) {
|
||||
// only do this if we can sign, otherwise we waste CPU
|
||||
extractChunks = evalPushOnly(p2sh.redeemScriptSig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,94 +285,83 @@ InSigner.prototype.extractSig = function () {
|
||||
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)
|
||||
extractChunks = input.witness
|
||||
}
|
||||
} else if (solution.type === bscript.types.P2WSH) {
|
||||
if (input.witness.length > 0) {
|
||||
if (!this.witnessScript.equals(input.witness[ input.witness.length - 1 ])) {
|
||||
if (!this.witnessScript.script.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)
|
||||
}
|
||||
extractChunks = input.witness.slice(0, -1)
|
||||
}
|
||||
}
|
||||
if (extractChunks.length > 0) {
|
||||
[this.signatures, this.publicKeys] = this.extractSignableChunks(this.signScript, extractChunks, this.sigVersion)
|
||||
}
|
||||
}
|
||||
|
||||
function signStandard (tx, nIn, txOutValue, signatures, publicKeys, key, solution, sigHashType, sigVersion) {
|
||||
function signSignable (tx, nIn, txOutValue, signatures, publicKeys, key, solution, sigHashType, sigVersion) {
|
||||
// Todo, skip if already signed
|
||||
// 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 {
|
||||
throw new Error('Signing input with wrong private key')
|
||||
}
|
||||
} 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 {
|
||||
throw new Error('Signing input with wrong private key')
|
||||
}
|
||||
} else if (solution.type === bscript.types.MULTISIG) {
|
||||
var match = false
|
||||
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
|
||||
match = true
|
||||
signatures[i] = calculateSignature(tx, nIn, key, solution.script, sigHashType, sigVersion, txOutValue)
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
throw new Error('Signing input with wrong private key')
|
||||
}
|
||||
} else {
|
||||
throw new Error('signStandard can only sign SIGNABLE_SCRIPTS')
|
||||
}
|
||||
|
||||
if (!didSign) {
|
||||
throw new Error('Signing input with wrong private key')
|
||||
}
|
||||
|
||||
return [signatures, publicKeys]
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs the input given a key/sigHashType
|
||||
* @param key
|
||||
* @param sigHashType
|
||||
* @returns {boolean}
|
||||
*/
|
||||
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 (ALLOWED_P2SH_SCRIPTS.indexOf(solution.type) !== -1) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
sigHashType = sigHashType || Transaction.SIGHASH_ALL;
|
||||
[this.signatures, this.publicKeys] = signSignable(this.tx, this.nIn, this.value, this.signatures, this.publicKeys, key, this.signScript, sigHashType, this.sigVersion)
|
||||
this.requiredSigs = this.publicKeys.length
|
||||
|
||||
return solution
|
||||
return true
|
||||
}
|
||||
|
||||
function serializeStandard (outputType, signatures, publicKeys) {
|
||||
// When adding a new script type, edit here
|
||||
/**
|
||||
* Creates an array of chunks for the scriptSig or witness
|
||||
* Future signable types go here (CSV, HTLC)
|
||||
*
|
||||
* @param outputType
|
||||
* @param signatures
|
||||
* @param publicKeys
|
||||
* @returns {Array}
|
||||
*/
|
||||
function serializeSignableChunks (outputType, signatures, publicKeys) {
|
||||
// Only SIGNABLE_SCRIPTS can be serialized here
|
||||
var chunks = []
|
||||
switch (outputType) {
|
||||
case bscript.types.P2PK:
|
||||
@ -407,42 +390,36 @@ function serializeStandard (outputType, signatures, publicKeys) {
|
||||
return chunks
|
||||
}
|
||||
|
||||
function serializeSigData (signatures, publicKeys, scriptPubKey, redeemScript, witnessScript) {
|
||||
InSigner.prototype.serializeSigData = function () {
|
||||
var scriptChunks = []
|
||||
var witnessChunks = []
|
||||
var type = scriptPubKey.type
|
||||
if (scriptPubKey.canSign) {
|
||||
scriptChunks = serializeStandard(type, signatures, publicKeys)
|
||||
var type = this.scriptPubKey.type
|
||||
if (SIGNABLE_SCRIPTS.indexOf(this.scriptPubKey.type) !== -1) {
|
||||
scriptChunks = serializeSignableChunks(type, this.signatures, this.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) {
|
||||
scriptChunks = serializeStandard(type, signatures, publicKeys)
|
||||
type = this.redeemScript.type
|
||||
if (SIGNABLE_SCRIPTS.indexOf(this.redeemScript.type) !== -1) {
|
||||
scriptChunks = serializeSignableChunks(type, this.signatures, this.publicKeys)
|
||||
}
|
||||
}
|
||||
|
||||
if (type === bscript.types.P2WPKH) {
|
||||
witnessChunks = serializeStandard(bscript.types.P2PKH, signatures, publicKeys)
|
||||
witnessChunks = serializeSignableChunks(bscript.types.P2PKH, this.signatures, this.publicKeys)
|
||||
} else if (type === bscript.types.P2WSH) {
|
||||
if (witnessScript === undefined) {
|
||||
throw new Error('Witness script not provided')
|
||||
}
|
||||
type = witnessScript.type
|
||||
if (witnessScript.canSign) {
|
||||
type = this.witnessScript.type
|
||||
if (SIGNABLE_SCRIPTS.indexOf(this.witnessScript.type) !== -1) {
|
||||
scriptChunks = []
|
||||
witnessChunks = serializeStandard(type, signatures, publicKeys)
|
||||
witnessChunks.push(witnessScript.script);
|
||||
witnessChunks = serializeSignableChunks(type, this.signatures, this.publicKeys)
|
||||
witnessChunks.push(this.witnessScript.script)
|
||||
}
|
||||
}
|
||||
|
||||
if (p2sh) {
|
||||
scriptChunks.push(redeemScript.script)
|
||||
scriptChunks.push(this.redeemScript.script)
|
||||
}
|
||||
|
||||
return {
|
||||
@ -451,10 +428,6 @@ function serializeSigData (signatures, publicKeys, scriptPubKey, redeemScript, w
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -469,6 +442,12 @@ function TxSigner (tx) {
|
||||
this.states = []
|
||||
}
|
||||
|
||||
TxSigner.prototype.signer = function (nIn, opts) {
|
||||
if (this.states[nIn] === undefined) {
|
||||
this.states[nIn] = new InSigner(this.tx, nIn, opts)
|
||||
}
|
||||
return this.states[nIn]
|
||||
}
|
||||
/**
|
||||
* Sign a transaction.
|
||||
*
|
||||
@ -485,11 +464,8 @@ TxSigner.prototype.sign = function (nIn, key, opts, sigHashType) {
|
||||
}
|
||||
// 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)) {
|
||||
if (!this.signer(nIn, opts).sign(key, sigHashType)) {
|
||||
throw new Error('Unsignable input: ', nIn)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user