bleh, rushed commit
This commit is contained in:
parent
c0bbd76ad0
commit
9d766df519
26
latest/fund_p2sh_p2wpkh.js
Normal file
26
latest/fund_p2sh_p2wpkh.js
Normal 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
29
latest/fund_p2sh_p2wsh.js
Normal 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'))
|
||||
32
latest/fund_p2sh_p2wsh_multisig.js
Normal file
32
latest/fund_p2sh_p2wsh_multisig.js
Normal 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
29
latest/fund_p2wpkh.js
Normal 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
26
latest/fund_p2wsh.js
Normal 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'))
|
||||
46
latest/spend_p2sh_p2wpkh.js
Normal file
46
latest/spend_p2sh_p2wpkh.js
Normal 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'))
|
||||
39
latest/spend_p2sh_p2wsh.js
Normal file
39
latest/spend_p2sh_p2wsh.js
Normal 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'))
|
||||
47
latest/spend_p2sh_p2wsh_multisig.js
Normal file
47
latest/spend_p2sh_p2wsh_multisig.js
Normal 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
38
latest/spend_p2wpkh.js
Normal 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
42
latest/spend_p2wsh.js
Normal 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'))
|
||||
|
||||
@ -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'),
|
||||
|
||||
@ -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
516
src/transaction_signer.js
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user