commit
d338f7cd64
28 changed files with 388 additions and 1009 deletions
|
@ -2,10 +2,10 @@ const Buffer = require('safe-buffer').Buffer
|
|||
const bech32 = require('bech32')
|
||||
const bs58check = require('bs58check')
|
||||
const bscript = require('./script')
|
||||
const btemplates = require('./templates')
|
||||
const networks = require('./networks')
|
||||
const typeforce = require('typeforce')
|
||||
const types = require('./types')
|
||||
const payments = require('./payments')
|
||||
|
||||
function fromBase58Check (address) {
|
||||
const payload = bs58check.decode(address)
|
||||
|
@ -48,15 +48,15 @@ function toBech32 (data, version, prefix) {
|
|||
return bech32.encode(prefix, words)
|
||||
}
|
||||
|
||||
function fromOutputScript (outputScript, network) {
|
||||
function fromOutputScript (output, network) {
|
||||
network = network || networks.bitcoin
|
||||
|
||||
if (btemplates.pubKeyHash.output.check(outputScript)) return toBase58Check(bscript.compile(outputScript).slice(3, 23), network.pubKeyHash)
|
||||
if (btemplates.scriptHash.output.check(outputScript)) return toBase58Check(bscript.compile(outputScript).slice(2, 22), network.scriptHash)
|
||||
if (btemplates.witnessPubKeyHash.output.check(outputScript)) return toBech32(bscript.compile(outputScript).slice(2, 22), 0, network.bech32)
|
||||
if (btemplates.witnessScriptHash.output.check(outputScript)) return toBech32(bscript.compile(outputScript).slice(2, 34), 0, network.bech32)
|
||||
try { return payments.p2pkh({ output, network }).address } catch (e) {}
|
||||
try { return payments.p2sh({ output, network }).address } catch (e) {}
|
||||
try { return payments.p2wpkh({ output, network }).address } catch (e) {}
|
||||
try { return payments.p2wsh({ output, network }).address } catch (e) {}
|
||||
|
||||
throw new Error(bscript.toASM(outputScript) + ' has no matching Address')
|
||||
throw new Error(bscript.toASM(output) + ' has no matching Address')
|
||||
}
|
||||
|
||||
function toOutputScript (address, network) {
|
||||
|
@ -68,8 +68,8 @@ function toOutputScript (address, network) {
|
|||
} catch (e) {}
|
||||
|
||||
if (decode) {
|
||||
if (decode.version === network.pubKeyHash) return btemplates.pubKeyHash.output.encode(decode.hash)
|
||||
if (decode.version === network.scriptHash) return btemplates.scriptHash.output.encode(decode.hash)
|
||||
if (decode.version === network.pubKeyHash) return payments.p2pkh({ hash: decode.hash }).output
|
||||
if (decode.version === network.scriptHash) return payments.p2sh({ hash: decode.hash }).output
|
||||
} else {
|
||||
try {
|
||||
decode = fromBech32(address)
|
||||
|
@ -78,8 +78,8 @@ function toOutputScript (address, network) {
|
|||
if (decode) {
|
||||
if (decode.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix')
|
||||
if (decode.version === 0) {
|
||||
if (decode.data.length === 20) return btemplates.witnessPubKeyHash.output.encode(decode.data)
|
||||
if (decode.data.length === 32) return btemplates.witnessScriptHash.output.encode(decode.data)
|
||||
if (decode.data.length === 20) return payments.p2wpkh({ hash: decode.data }).output
|
||||
if (decode.data.length === 32) return payments.p2wsh({ hash: decode.data }).output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
const decompile = require('../script').decompile
|
||||
const multisig = require('./multisig')
|
||||
const nullData = require('./nulldata')
|
||||
const pubKey = require('./pubkey')
|
||||
const pubKeyHash = require('./pubkeyhash')
|
||||
const scriptHash = require('./scripthash')
|
||||
const witnessPubKeyHash = require('./witnesspubkeyhash')
|
||||
const witnessScriptHash = require('./witnessscripthash')
|
||||
const witnessCommitment = require('./witnesscommitment')
|
||||
const decompile = require('./script').decompile
|
||||
const multisig = require('./templates/multisig')
|
||||
const nullData = require('./templates/nulldata')
|
||||
const pubKey = require('./templates/pubkey')
|
||||
const pubKeyHash = require('./templates/pubkeyhash')
|
||||
const scriptHash = require('./templates/scripthash')
|
||||
const witnessPubKeyHash = require('./templates/witnesspubkeyhash')
|
||||
const witnessScriptHash = require('./templates/witnessscripthash')
|
||||
const witnessCommitment = require('./templates/witnesscommitment')
|
||||
|
||||
const types = {
|
||||
MULTISIG: 'multisig',
|
||||
|
@ -63,16 +63,8 @@ function classifyWitness (script, allowIncomplete) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
classifyInput: classifyInput,
|
||||
classifyOutput: classifyOutput,
|
||||
classifyWitness: classifyWitness,
|
||||
multisig: multisig,
|
||||
nullData: nullData,
|
||||
pubKey: pubKey,
|
||||
pubKeyHash: pubKeyHash,
|
||||
scriptHash: scriptHash,
|
||||
witnessPubKeyHash: witnessPubKeyHash,
|
||||
witnessScriptHash: witnessScriptHash,
|
||||
witnessCommitment: witnessCommitment,
|
||||
input: classifyInput,
|
||||
output: classifyOutput,
|
||||
witness: classifyWitness,
|
||||
types: types
|
||||
}
|
|
@ -1,8 +1,4 @@
|
|||
const script = require('./script')
|
||||
const templates = require('./templates')
|
||||
for (let key in templates) {
|
||||
script[key] = templates[key]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Block: require('./block'),
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
let lazy = require('./lazy')
|
||||
let typef = require('typeforce')
|
||||
let OPS = require('bitcoin-ops')
|
||||
let ecc = require('tiny-secp256k1')
|
||||
const lazy = require('./lazy')
|
||||
const typef = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
const ecc = require('tiny-secp256k1')
|
||||
|
||||
let baddress = require('../address')
|
||||
let bcrypto = require('../crypto')
|
||||
let bscript = require('../script')
|
||||
let BITCOIN_NETWORK = require('../networks').bitcoin
|
||||
const bcrypto = require('../crypto')
|
||||
const bscript = require('../script')
|
||||
const BITCOIN_NETWORK = require('../networks').bitcoin
|
||||
const bs58check = require('bs58check')
|
||||
|
||||
// input: {signature} {pubkey}
|
||||
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
|
||||
|
@ -31,15 +31,24 @@ function p2pkh (a, opts) {
|
|||
input: typef.maybe(typef.Buffer)
|
||||
}, a)
|
||||
|
||||
let _address = lazy.value(function () { return baddress.fromBase58Check(a.address) })
|
||||
let _chunks = lazy.value(function () { return bscript.decompile(a.input) })
|
||||
const _address = lazy.value(function () {
|
||||
const payload = bs58check.decode(a.address)
|
||||
const version = payload.readUInt8(0)
|
||||
const hash = payload.slice(1)
|
||||
return { version, hash }
|
||||
})
|
||||
const _chunks = lazy.value(function () { return bscript.decompile(a.input) })
|
||||
|
||||
let network = a.network || BITCOIN_NETWORK
|
||||
let o = { network }
|
||||
const network = a.network || BITCOIN_NETWORK
|
||||
const o = { network }
|
||||
|
||||
lazy.prop(o, 'address', function () {
|
||||
if (!o.hash) return
|
||||
return baddress.toBase58Check(o.hash, network.pubKeyHash)
|
||||
|
||||
const payload = Buffer.allocUnsafe(21)
|
||||
payload.writeUInt8(network.pubKeyHash, 0)
|
||||
o.hash.copy(payload, 1)
|
||||
return bs58check.encode(payload)
|
||||
})
|
||||
lazy.prop(o, 'hash', function () {
|
||||
if (a.output) return a.output.slice(3, 23)
|
||||
|
|
|
@ -2,10 +2,10 @@ const lazy = require('./lazy')
|
|||
const typef = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
const baddress = require('../address')
|
||||
const bcrypto = require('../crypto')
|
||||
const bscript = require('../script')
|
||||
const BITCOIN_NETWORK = require('../networks').bitcoin
|
||||
const bs58check = require('bs58check')
|
||||
|
||||
function stacksEqual (a, b) {
|
||||
if (a.length !== b.length) return false
|
||||
|
@ -48,7 +48,12 @@ function p2sh (a, opts) {
|
|||
const network = a.network || BITCOIN_NETWORK
|
||||
const o = { network }
|
||||
|
||||
const _address = lazy.value(function () { return baddress.fromBase58Check(a.address) })
|
||||
const _address = lazy.value(function () {
|
||||
const payload = bs58check.decode(a.address)
|
||||
const version = payload.readUInt8(0)
|
||||
const hash = payload.slice(1)
|
||||
return { version, hash }
|
||||
})
|
||||
const _chunks = lazy.value(function () { return bscript.decompile(a.input) })
|
||||
const _redeem = lazy.value(function () {
|
||||
const chunks = _chunks()
|
||||
|
@ -63,7 +68,11 @@ function p2sh (a, opts) {
|
|||
// output dependents
|
||||
lazy.prop(o, 'address', function () {
|
||||
if (!o.hash) return
|
||||
return baddress.toBase58Check(o.hash, network.scriptHash)
|
||||
|
||||
const payload = Buffer.allocUnsafe(21)
|
||||
payload.writeUInt8(network.scriptHash, 0)
|
||||
o.hash.copy(payload, 1)
|
||||
return bs58check.encode(payload)
|
||||
})
|
||||
lazy.prop(o, 'hash', function () {
|
||||
// in order of least effort
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
let lazy = require('./lazy')
|
||||
let typef = require('typeforce')
|
||||
let OPS = require('bitcoin-ops')
|
||||
let ecc = require('tiny-secp256k1')
|
||||
const lazy = require('./lazy')
|
||||
const typef = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
const ecc = require('tiny-secp256k1')
|
||||
|
||||
let baddress = require('../address')
|
||||
let bcrypto = require('../crypto')
|
||||
let bscript = require('../script')
|
||||
let BITCOIN_NETWORK = require('../networks').bitcoin
|
||||
const bcrypto = require('../crypto')
|
||||
const bech32 = require('bech32')
|
||||
const bscript = require('../script')
|
||||
const BITCOIN_NETWORK = require('../networks').bitcoin
|
||||
|
||||
let EMPTY_BUFFER = Buffer.alloc(0)
|
||||
const EMPTY_BUFFER = Buffer.alloc(0)
|
||||
|
||||
// witness: {signature} {pubKey}
|
||||
// input: <>
|
||||
|
@ -34,14 +34,26 @@ function p2wpkh (a, opts) {
|
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
||||
}, a)
|
||||
|
||||
let _address = lazy.value(function () { return baddress.fromBech32(a.address) })
|
||||
const _address = lazy.value(function () {
|
||||
const result = bech32.decode(a.address)
|
||||
const version = result.words.shift()
|
||||
const data = bech32.fromWords(result.words)
|
||||
return {
|
||||
version,
|
||||
prefix: result.prefix,
|
||||
data: Buffer.from(data)
|
||||
}
|
||||
})
|
||||
|
||||
let network = a.network || BITCOIN_NETWORK
|
||||
let o = { network }
|
||||
const network = a.network || BITCOIN_NETWORK
|
||||
const o = { network }
|
||||
|
||||
lazy.prop(o, 'address', function () {
|
||||
if (!o.hash) return
|
||||
return baddress.toBech32(o.hash, 0x00, network.bech32)
|
||||
|
||||
const words = bech32.toWords(o.hash)
|
||||
words.unshift(0x00)
|
||||
return bech32.encode(network.bech32, words)
|
||||
})
|
||||
lazy.prop(o, 'hash', function () {
|
||||
if (a.output) return a.output.slice(2, 22)
|
||||
|
@ -86,7 +98,7 @@ function p2wpkh (a, opts) {
|
|||
}
|
||||
|
||||
if (a.pubkey) {
|
||||
let pkh = bcrypto.hash160(a.pubkey)
|
||||
const pkh = bcrypto.hash160(a.pubkey)
|
||||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch')
|
||||
else hash = pkh
|
||||
}
|
||||
|
@ -113,7 +125,7 @@ function p2wpkh (a, opts) {
|
|||
if (a.signature && !a.signature.equals(a.witness[0])) throw new TypeError('Signature mismatch')
|
||||
if (a.pubkey && !a.pubkey.equals(a.witness[1])) throw new TypeError('Pubkey mismatch')
|
||||
|
||||
let pkh = bcrypto.hash160(a.witness[1])
|
||||
const pkh = bcrypto.hash160(a.witness[1])
|
||||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
let lazy = require('./lazy')
|
||||
let typef = require('typeforce')
|
||||
let OPS = require('bitcoin-ops')
|
||||
const lazy = require('./lazy')
|
||||
const typef = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
let baddress = require('../address')
|
||||
let bcrypto = require('../crypto')
|
||||
let bscript = require('../script')
|
||||
let BITCOIN_NETWORK = require('../networks').bitcoin
|
||||
const bech32 = require('bech32')
|
||||
const bcrypto = require('../crypto')
|
||||
const bscript = require('../script')
|
||||
const BITCOIN_NETWORK = require('../networks').bitcoin
|
||||
|
||||
let EMPTY_BUFFER = Buffer.alloc(0)
|
||||
const EMPTY_BUFFER = Buffer.alloc(0)
|
||||
|
||||
function stacksEqual (a, b) {
|
||||
if (a.length !== b.length) return false
|
||||
|
@ -47,19 +47,30 @@ function p2wsh (a, opts) {
|
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
||||
}, a)
|
||||
|
||||
let _address = lazy.value(function () { return baddress.fromBech32(a.address) })
|
||||
let _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) })
|
||||
const _address = lazy.value(function () {
|
||||
const result = bech32.decode(a.address)
|
||||
const version = result.words.shift()
|
||||
const data = bech32.fromWords(result.words)
|
||||
return {
|
||||
version,
|
||||
prefix: result.prefix,
|
||||
data: Buffer.from(data)
|
||||
}
|
||||
})
|
||||
const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) })
|
||||
|
||||
let network = a.network || BITCOIN_NETWORK
|
||||
let o = { network }
|
||||
const network = a.network || BITCOIN_NETWORK
|
||||
const o = { network }
|
||||
|
||||
lazy.prop(o, 'address', function () {
|
||||
if (!o.hash) return
|
||||
return baddress.toBech32(o.hash, 0x00, network.bech32)
|
||||
const words = bech32.toWords(o.hash)
|
||||
words.unshift(0x00)
|
||||
return bech32.encode(network.bech32, words)
|
||||
})
|
||||
lazy.prop(o, 'hash', function () {
|
||||
if (a.output) return a.output.slice(2)
|
||||
if (a.address) return baddress.fromBech32(a.address).data
|
||||
if (a.address) return _address().data
|
||||
if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output)
|
||||
})
|
||||
lazy.prop(o, 'output', function () {
|
||||
|
@ -145,7 +156,7 @@ function p2wsh (a, opts) {
|
|||
if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid')
|
||||
|
||||
// match hash against other sources
|
||||
let hash2 = bcrypto.sha256(a.redeem.output)
|
||||
const hash2 = bcrypto.sha256(a.redeem.output)
|
||||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch')
|
||||
else hash = hash2
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
// OP_0 [signatures ...]
|
||||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const bscript = require('../../script')
|
||||
const p2mso = require('./output')
|
||||
const typeforce = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
function partialSignature (value) {
|
||||
|
@ -23,50 +20,4 @@ function check (script, allowIncomplete) {
|
|||
}
|
||||
check.toJSON = function () { return 'multisig input' }
|
||||
|
||||
const EMPTY_BUFFER = Buffer.allocUnsafe(0)
|
||||
|
||||
function encodeStack (signatures, scriptPubKey) {
|
||||
typeforce([partialSignature], signatures)
|
||||
|
||||
if (scriptPubKey) {
|
||||
const scriptData = p2mso.decode(scriptPubKey)
|
||||
|
||||
if (signatures.length < scriptData.m) {
|
||||
throw new TypeError('Not enough signatures provided')
|
||||
}
|
||||
|
||||
if (signatures.length > scriptData.pubKeys.length) {
|
||||
throw new TypeError('Too many signatures provided')
|
||||
}
|
||||
}
|
||||
|
||||
return [].concat(EMPTY_BUFFER, signatures.map(function (sig) {
|
||||
if (sig === OPS.OP_0) {
|
||||
return EMPTY_BUFFER
|
||||
}
|
||||
return sig
|
||||
}))
|
||||
}
|
||||
|
||||
function encode (signatures, scriptPubKey) {
|
||||
return bscript.compile(encodeStack(signatures, scriptPubKey))
|
||||
}
|
||||
|
||||
function decodeStack (stack, allowIncomplete) {
|
||||
typeforce(typeforce.Array, stack)
|
||||
typeforce(check, stack, allowIncomplete)
|
||||
return stack.slice(1)
|
||||
}
|
||||
|
||||
function decode (buffer, allowIncomplete) {
|
||||
const stack = bscript.decompile(buffer)
|
||||
return decodeStack(stack, allowIncomplete)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
decodeStack: decodeStack,
|
||||
encode: encode,
|
||||
encodeStack: encodeStack
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
const bscript = require('../../script')
|
||||
const types = require('../../types')
|
||||
const typeforce = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
||||
|
||||
|
@ -27,38 +26,4 @@ function check (script, allowIncomplete) {
|
|||
}
|
||||
check.toJSON = function () { return 'multi-sig output' }
|
||||
|
||||
function encode (m, pubKeys) {
|
||||
typeforce({
|
||||
m: types.Number,
|
||||
pubKeys: [bscript.isCanonicalPubKey]
|
||||
}, {
|
||||
m: m,
|
||||
pubKeys: pubKeys
|
||||
})
|
||||
|
||||
const n = pubKeys.length
|
||||
if (n < m) throw new TypeError('Not enough pubKeys provided')
|
||||
|
||||
return bscript.compile([].concat(
|
||||
OP_INT_BASE + m,
|
||||
pubKeys,
|
||||
OP_INT_BASE + n,
|
||||
OPS.OP_CHECKMULTISIG
|
||||
))
|
||||
}
|
||||
|
||||
function decode (buffer, allowIncomplete) {
|
||||
const chunks = bscript.decompile(buffer)
|
||||
typeforce(check, chunks, allowIncomplete)
|
||||
|
||||
return {
|
||||
m: chunks[0] - OP_INT_BASE,
|
||||
pubKeys: chunks.slice(1, -2)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
encode: encode
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// {signature}
|
||||
|
||||
const bscript = require('../../script')
|
||||
const typeforce = require('typeforce')
|
||||
|
||||
function check (script) {
|
||||
const chunks = bscript.decompile(script)
|
||||
|
@ -11,30 +10,6 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'pubKey input' }
|
||||
|
||||
function encodeStack (signature) {
|
||||
typeforce(bscript.isCanonicalScriptSignature, signature)
|
||||
return [signature]
|
||||
}
|
||||
|
||||
function encode (signature) {
|
||||
return bscript.compile(encodeStack(signature))
|
||||
}
|
||||
|
||||
function decodeStack (stack) {
|
||||
typeforce(typeforce.Array, stack)
|
||||
typeforce(check, stack)
|
||||
return stack[0]
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
const stack = bscript.decompile(buffer)
|
||||
return decodeStack(stack)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
decodeStack: decodeStack,
|
||||
encode: encode,
|
||||
encodeStack: encodeStack
|
||||
check: check
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// {pubKey} OP_CHECKSIG
|
||||
|
||||
const bscript = require('../../script')
|
||||
const typeforce = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
function check (script) {
|
||||
|
@ -13,21 +12,4 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'pubKey output' }
|
||||
|
||||
function encode (pubKey) {
|
||||
typeforce(bscript.isCanonicalPubKey, pubKey)
|
||||
|
||||
return bscript.compile([pubKey, OPS.OP_CHECKSIG])
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
const chunks = bscript.decompile(buffer)
|
||||
typeforce(check, chunks)
|
||||
|
||||
return chunks[0]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
encode: encode
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// {signature} {pubKey}
|
||||
|
||||
const bscript = require('../../script')
|
||||
const typeforce = require('typeforce')
|
||||
|
||||
function check (script) {
|
||||
const chunks = bscript.decompile(script)
|
||||
|
@ -12,41 +11,4 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'pubKeyHash input' }
|
||||
|
||||
function encodeStack (signature, pubKey) {
|
||||
typeforce({
|
||||
signature: bscript.isCanonicalScriptSignature,
|
||||
pubKey: bscript.isCanonicalPubKey
|
||||
}, {
|
||||
signature: signature,
|
||||
pubKey: pubKey
|
||||
})
|
||||
|
||||
return [signature, pubKey]
|
||||
}
|
||||
|
||||
function encode (signature, pubKey) {
|
||||
return bscript.compile(encodeStack(signature, pubKey))
|
||||
}
|
||||
|
||||
function decodeStack (stack) {
|
||||
typeforce(typeforce.Array, stack)
|
||||
typeforce(check, stack)
|
||||
|
||||
return {
|
||||
signature: stack[0],
|
||||
pubKey: stack[1]
|
||||
}
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
const stack = bscript.decompile(buffer)
|
||||
return decodeStack(stack)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
decodeStack: decodeStack,
|
||||
encode: encode,
|
||||
encodeStack: encodeStack
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
|
||||
|
||||
const bscript = require('../../script')
|
||||
const types = require('../../types')
|
||||
const typeforce = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
function check (script) {
|
||||
|
@ -17,26 +15,4 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'pubKeyHash output' }
|
||||
|
||||
function encode (pubKeyHash) {
|
||||
typeforce(types.Hash160bit, pubKeyHash)
|
||||
|
||||
return bscript.compile([
|
||||
OPS.OP_DUP,
|
||||
OPS.OP_HASH160,
|
||||
pubKeyHash,
|
||||
OPS.OP_EQUALVERIFY,
|
||||
OPS.OP_CHECKSIG
|
||||
])
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
typeforce(check, buffer)
|
||||
|
||||
return buffer.slice(3, 23)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
encode: encode
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const bscript = require('../../script')
|
||||
const typeforce = require('typeforce')
|
||||
|
||||
const p2ms = require('../multisig/')
|
||||
const p2pk = require('../pubkey/')
|
||||
|
@ -46,40 +45,4 @@ function check (script, allowIncomplete) {
|
|||
}
|
||||
check.toJSON = function () { return 'scriptHash input' }
|
||||
|
||||
function encodeStack (redeemScriptStack, redeemScript) {
|
||||
const serializedScriptPubKey = bscript.compile(redeemScript)
|
||||
|
||||
return [].concat(redeemScriptStack, serializedScriptPubKey)
|
||||
}
|
||||
|
||||
function encode (redeemScriptSig, redeemScript) {
|
||||
const redeemScriptStack = bscript.decompile(redeemScriptSig)
|
||||
|
||||
return bscript.compile(encodeStack(redeemScriptStack, redeemScript))
|
||||
}
|
||||
|
||||
function decodeStack (stack) {
|
||||
typeforce(typeforce.Array, stack)
|
||||
typeforce(check, stack)
|
||||
|
||||
return {
|
||||
redeemScriptStack: stack.slice(0, -1),
|
||||
redeemScript: stack[stack.length - 1]
|
||||
}
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
const stack = bscript.decompile(buffer)
|
||||
const result = decodeStack(stack)
|
||||
result.redeemScriptSig = bscript.compile(result.redeemScriptStack)
|
||||
delete result.redeemScriptStack
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
decodeStack: decodeStack,
|
||||
encode: encode,
|
||||
encodeStack: encodeStack
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// OP_HASH160 {scriptHash} OP_EQUAL
|
||||
|
||||
const bscript = require('../../script')
|
||||
const types = require('../../types')
|
||||
const typeforce = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
function check (script) {
|
||||
|
@ -15,20 +13,4 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'scriptHash output' }
|
||||
|
||||
function encode (scriptHash) {
|
||||
typeforce(types.Hash160bit, scriptHash)
|
||||
|
||||
return bscript.compile([OPS.OP_HASH160, scriptHash, OPS.OP_EQUAL])
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
typeforce(check, buffer)
|
||||
|
||||
return buffer.slice(2, 22)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
encode: encode
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// {signature} {pubKey}
|
||||
|
||||
const bscript = require('../../script')
|
||||
const typeforce = require('typeforce')
|
||||
|
||||
function isCompressedCanonicalPubKey (pubKey) {
|
||||
return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33
|
||||
|
@ -16,30 +15,4 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'witnessPubKeyHash input' }
|
||||
|
||||
function encodeStack (signature, pubKey) {
|
||||
typeforce({
|
||||
signature: bscript.isCanonicalScriptSignature,
|
||||
pubKey: isCompressedCanonicalPubKey
|
||||
}, {
|
||||
signature: signature,
|
||||
pubKey: pubKey
|
||||
})
|
||||
|
||||
return [signature, pubKey]
|
||||
}
|
||||
|
||||
function decodeStack (stack) {
|
||||
typeforce(typeforce.Array, stack)
|
||||
typeforce(check, stack)
|
||||
|
||||
return {
|
||||
signature: stack[0],
|
||||
pubKey: stack[1]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decodeStack: decodeStack,
|
||||
encodeStack: encodeStack
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// OP_0 {pubKeyHash}
|
||||
|
||||
const bscript = require('../../script')
|
||||
const types = require('../../types')
|
||||
const typeforce = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
function check (script) {
|
||||
|
@ -14,20 +12,6 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'Witness pubKeyHash output' }
|
||||
|
||||
function encode (pubKeyHash) {
|
||||
typeforce(types.Hash160bit, pubKeyHash)
|
||||
|
||||
return bscript.compile([OPS.OP_0, pubKeyHash])
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
typeforce(check, buffer)
|
||||
|
||||
return buffer.slice(2)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
encode: encode
|
||||
check
|
||||
}
|
||||
|
|
|
@ -36,29 +36,4 @@ function check (chunks, allowIncomplete) {
|
|||
}
|
||||
check.toJSON = function () { return 'witnessScriptHash input' }
|
||||
|
||||
function encodeStack (witnessData, witnessScript) {
|
||||
typeforce({
|
||||
witnessData: [types.Buffer],
|
||||
witnessScript: types.Buffer
|
||||
}, {
|
||||
witnessData: witnessData,
|
||||
witnessScript: witnessScript
|
||||
})
|
||||
|
||||
return [].concat(witnessData, witnessScript)
|
||||
}
|
||||
|
||||
function decodeStack (stack) {
|
||||
typeforce(typeforce.Array, stack)
|
||||
typeforce(check, stack)
|
||||
return {
|
||||
witnessData: stack.slice(0, -1),
|
||||
witnessScript: stack[stack.length - 1]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decodeStack: decodeStack,
|
||||
encodeStack: encodeStack
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// OP_0 {scriptHash}
|
||||
|
||||
const bscript = require('../../script')
|
||||
const types = require('../../types')
|
||||
const typeforce = require('typeforce')
|
||||
const OPS = require('bitcoin-ops')
|
||||
|
||||
function check (script) {
|
||||
|
@ -14,20 +12,4 @@ function check (script) {
|
|||
}
|
||||
check.toJSON = function () { return 'Witness scriptHash output' }
|
||||
|
||||
function encode (scriptHash) {
|
||||
typeforce(types.Hash256bit, scriptHash)
|
||||
|
||||
return bscript.compile([OPS.OP_0, scriptHash])
|
||||
}
|
||||
|
||||
function decode (buffer) {
|
||||
typeforce(check, buffer)
|
||||
|
||||
return buffer.slice(2)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check: check,
|
||||
decode: decode,
|
||||
encode: encode
|
||||
}
|
||||
module.exports = { check }
|
||||
|
|
|
@ -2,13 +2,13 @@ const Buffer = require('safe-buffer').Buffer
|
|||
const baddress = require('./address')
|
||||
const bcrypto = require('./crypto')
|
||||
const bscript = require('./script')
|
||||
const btemplates = require('./templates')
|
||||
const networks = require('./networks')
|
||||
const ops = require('bitcoin-ops')
|
||||
const payments = require('./payments')
|
||||
const SCRIPT_TYPES = btemplates.types
|
||||
const typeforce = require('typeforce')
|
||||
const types = require('./types')
|
||||
const classify = require('./classify')
|
||||
const SCRIPT_TYPES = classify.types
|
||||
|
||||
const ECPair = require('./ecpair')
|
||||
const Transaction = require('./transaction')
|
||||
|
@ -16,8 +16,8 @@ const Transaction = require('./transaction')
|
|||
function expandInput (scriptSig, witnessStack, type, scriptPubKey) {
|
||||
if (scriptSig.length === 0 && witnessStack.length === 0) return {}
|
||||
if (!type) {
|
||||
let ssType = btemplates.classifyInput(scriptSig, true)
|
||||
let wsType = btemplates.classifyWitness(witnessStack, true)
|
||||
let ssType = classify.input(scriptSig, true)
|
||||
let wsType = classify.witness(witnessStack, true)
|
||||
if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined
|
||||
if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined
|
||||
type = ssType || wsType
|
||||
|
@ -76,7 +76,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) {
|
|||
witness: witnessStack
|
||||
})
|
||||
|
||||
const outputType = btemplates.classifyOutput(redeem.output)
|
||||
const outputType = classify.output(redeem.output)
|
||||
const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output)
|
||||
if (!expanded.prevOutType) return {}
|
||||
|
||||
|
@ -98,7 +98,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) {
|
|||
input: scriptSig,
|
||||
witness: witnessStack
|
||||
})
|
||||
const outputType = btemplates.classifyOutput(redeem.output)
|
||||
const outputType = classify.output(redeem.output)
|
||||
let expanded
|
||||
if (outputType === SCRIPT_TYPES.P2WPKH) {
|
||||
expanded = expandInput(redeem.input, redeem.witness, outputType)
|
||||
|
@ -160,7 +160,7 @@ function fixMultisigOrder (input, transaction, vin) {
|
|||
|
||||
function expandOutput (script, ourPubKey) {
|
||||
typeforce(types.Buffer, script)
|
||||
const type = btemplates.classifyOutput(script)
|
||||
const type = classify.output(script)
|
||||
|
||||
switch (type) {
|
||||
case SCRIPT_TYPES.P2PKH: {
|
||||
|
@ -543,7 +543,7 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options)
|
|||
}
|
||||
|
||||
input.prevOutScript = options.prevOutScript
|
||||
input.prevOutType = prevOutType || btemplates.classifyOutput(options.prevOutScript)
|
||||
input.prevOutType = prevOutType || classify.output(options.prevOutScript)
|
||||
}
|
||||
|
||||
const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig)
|
||||
|
|
157
test/classify.js
Normal file
157
test/classify.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
/* global describe, it */
|
||||
|
||||
const assert = require('assert')
|
||||
const bscript = require('../src/script')
|
||||
const classify = require('../src/classify')
|
||||
|
||||
const fixtures = require('./fixtures/templates.json')
|
||||
|
||||
const multisig = require('../src/templates/multisig')
|
||||
const nullData = require('../src/templates/nulldata')
|
||||
const pubKey = require('../src/templates/pubkey')
|
||||
const pubKeyHash = require('../src/templates/pubkeyhash')
|
||||
const scriptHash = require('../src/templates/scripthash')
|
||||
const witnessPubKeyHash = require('../src/templates/witnesspubkeyhash')
|
||||
const witnessScriptHash = require('../src/templates/witnessscripthash')
|
||||
const witnessCommitment = require('../src/templates/witnesscommitment')
|
||||
|
||||
const tmap = {
|
||||
pubKey,
|
||||
pubKeyHash,
|
||||
scriptHash,
|
||||
witnessPubKeyHash,
|
||||
witnessScriptHash,
|
||||
multisig,
|
||||
nullData,
|
||||
witnessCommitment
|
||||
}
|
||||
|
||||
describe('classify', function () {
|
||||
describe('input', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (!f.input) return
|
||||
|
||||
it('classifies ' + f.input + ' as ' + f.type, function () {
|
||||
const input = bscript.fromASM(f.input)
|
||||
const type = classify.input(input)
|
||||
|
||||
assert.strictEqual(type, f.type)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (!f.input) return
|
||||
if (!f.typeIncomplete) return
|
||||
|
||||
it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, function () {
|
||||
const input = bscript.fromASM(f.input)
|
||||
const type = classify.input(input, true)
|
||||
|
||||
assert.strictEqual(type, f.typeIncomplete)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('classifyOutput', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (!f.output) return
|
||||
|
||||
it('classifies ' + f.output + ' as ' + f.type, function () {
|
||||
const output = bscript.fromASM(f.output)
|
||||
const type = classify.output(output)
|
||||
|
||||
assert.strictEqual(type, f.type)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
;[
|
||||
'pubKey',
|
||||
'pubKeyHash',
|
||||
'scriptHash',
|
||||
'witnessPubKeyHash',
|
||||
'witnessScriptHash',
|
||||
'multisig',
|
||||
'nullData',
|
||||
'witnessCommitment'
|
||||
].forEach(function (name) {
|
||||
const inputType = tmap[name].input
|
||||
const outputType = tmap[name].output
|
||||
|
||||
describe(name + '.input.check', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (name.toLowerCase() === classify.types.P2WPKH) return
|
||||
if (name.toLowerCase() === classify.types.P2WSH) return
|
||||
const expected = name.toLowerCase() === f.type.toLowerCase()
|
||||
|
||||
if (inputType && f.input) {
|
||||
const input = bscript.fromASM(f.input)
|
||||
|
||||
it('returns ' + expected + ' for ' + f.input, function () {
|
||||
assert.strictEqual(inputType.check(input), expected)
|
||||
})
|
||||
|
||||
if (f.typeIncomplete) {
|
||||
const expectedIncomplete = name.toLowerCase() === f.typeIncomplete
|
||||
|
||||
it('returns ' + expected + ' for ' + f.input, function () {
|
||||
assert.strictEqual(inputType.check(input, true), expectedIncomplete)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!(fixtures.invalid[name])) return
|
||||
|
||||
fixtures.invalid[name].inputs.forEach(function (f) {
|
||||
if (!f.input && !f.inputHex) return
|
||||
|
||||
it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', function () {
|
||||
let input
|
||||
|
||||
if (f.input) {
|
||||
input = bscript.fromASM(f.input)
|
||||
} else {
|
||||
input = Buffer.from(f.inputHex, 'hex')
|
||||
}
|
||||
|
||||
assert.strictEqual(inputType.check(input), false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(name + '.output.check', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
const expected = name.toLowerCase() === f.type
|
||||
|
||||
if (outputType && f.output) {
|
||||
it('returns ' + expected + ' for ' + f.output, function () {
|
||||
const output = bscript.fromASM(f.output)
|
||||
|
||||
if (name.toLowerCase() === 'nulldata' && f.type === classify.types.WITNESS_COMMITMENT) return
|
||||
if (name.toLowerCase() === 'witnesscommitment' && f.type === classify.types.NULLDATA) return
|
||||
assert.strictEqual(outputType.check(output), expected)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (!(fixtures.invalid[name])) return
|
||||
|
||||
fixtures.invalid[name].outputs.forEach(function (f) {
|
||||
if (!f.output && !f.outputHex) return
|
||||
|
||||
it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', function () {
|
||||
let output
|
||||
|
||||
if (f.output) {
|
||||
output = bscript.fromASM(f.output)
|
||||
} else {
|
||||
output = Buffer.from(f.outputHex, 'hex')
|
||||
}
|
||||
|
||||
assert.strictEqual(outputType.check(output), false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -76,12 +76,10 @@ describe('bitcoinjs-lib (BIP32)', function () {
|
|||
const path = "m/49'/1'/0'/0/0"
|
||||
const child = root.derivePath(path)
|
||||
|
||||
const keyhash = bitcoin.crypto.hash160(child.publicKey)
|
||||
const scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash)
|
||||
const addressBytes = bitcoin.crypto.hash160(scriptSig)
|
||||
const outputScript = bitcoin.script.scriptHash.output.encode(addressBytes)
|
||||
const address = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.testnet)
|
||||
|
||||
const { address } = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: bitcoin.networks.testnet }),
|
||||
network: bitcoin.networks.testnet
|
||||
})
|
||||
assert.equal(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2')
|
||||
})
|
||||
|
||||
|
|
|
@ -46,8 +46,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// 3 hours ago
|
||||
const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) })
|
||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
|
||||
const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
|
||||
const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest)
|
||||
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
|
||||
|
||||
// fund the P2SH(CLTV) address
|
||||
regtestUtils.faucet(address, 1e5, function (err, unspent) {
|
||||
|
@ -61,10 +60,15 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// {Alice's signature} OP_TRUE
|
||||
const tx = txb.buildIncomplete()
|
||||
const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
|
||||
const redeemScriptSig = bitcoin.script.scriptHash.input.encode([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
], redeemScript)
|
||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||
redeem: {
|
||||
input: bitcoin.script.compile([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
]),
|
||||
output: redeemScript
|
||||
}
|
||||
}).input
|
||||
tx.setInputScript(0, redeemScriptSig)
|
||||
|
||||
regtestUtils.broadcast(tx.toHex(), function (err) {
|
||||
|
@ -90,8 +94,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// 5 blocks from now
|
||||
const lockTime = bip65.encode({ blocks: height + 5 })
|
||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
|
||||
const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
|
||||
const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest)
|
||||
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
|
||||
|
||||
// fund the P2SH(CLTV) address
|
||||
regtestUtils.faucet(address, 1e5, function (err, unspent) {
|
||||
|
@ -105,10 +108,15 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// {Alice's signature} OP_TRUE
|
||||
const tx = txb.buildIncomplete()
|
||||
const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
|
||||
const redeemScriptSig = bitcoin.script.scriptHash.input.encode([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
], redeemScript)
|
||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||
redeem: {
|
||||
input: bitcoin.script.compile([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
]),
|
||||
output: redeemScript
|
||||
}
|
||||
}).input
|
||||
tx.setInputScript(0, redeemScriptSig)
|
||||
|
||||
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
|
||||
|
@ -139,8 +147,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// two hours ago
|
||||
const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) })
|
||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
|
||||
const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
|
||||
const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest)
|
||||
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
|
||||
|
||||
// fund the P2SH(CLTV) address
|
||||
regtestUtils.faucet(address, 2e5, function (err, unspent) {
|
||||
|
@ -154,11 +161,16 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// {Alice's signature} {Bob's signature} OP_FALSE
|
||||
const tx = txb.buildIncomplete()
|
||||
const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
|
||||
const redeemScriptSig = bitcoin.script.scriptHash.input.encode([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_FALSE
|
||||
], redeemScript)
|
||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||
redeem: {
|
||||
input: bitcoin.script.compile([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_FALSE
|
||||
]),
|
||||
output: redeemScript
|
||||
}
|
||||
}).input
|
||||
tx.setInputScript(0, redeemScriptSig)
|
||||
|
||||
regtestUtils.broadcast(tx.toHex(), function (err) {
|
||||
|
@ -181,8 +193,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// two hours from now
|
||||
const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) })
|
||||
const redeemScript = cltvCheckSigOutput(alice, bob, lockTime)
|
||||
const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
|
||||
const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest)
|
||||
const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest })
|
||||
|
||||
// fund the P2SH(CLTV) address
|
||||
regtestUtils.faucet(address, 2e4, function (err, unspent) {
|
||||
|
@ -196,11 +207,16 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
// {Alice's signature} OP_TRUE
|
||||
const tx = txb.buildIncomplete()
|
||||
const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
|
||||
const redeemScriptSig = bitcoin.script.scriptHash.input.encode([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
], redeemScript)
|
||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||
redeem: {
|
||||
input: bitcoin.script.compile([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
]),
|
||||
output: redeemScript
|
||||
}
|
||||
}).input
|
||||
tx.setInputScript(0, redeemScriptSig)
|
||||
|
||||
regtestUtils.broadcast(tx.toHex(), function (err) {
|
||||
|
|
|
@ -18,16 +18,11 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
const tx = bitcoin.Transaction.fromHex('01000000020b668015b32a6178d8524cfef6dc6fc0a4751915c2e9b2ed2d2eab02424341c8000000006a47304402205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022024bf5f506968f5f23f1835574d5afe0e9021b4a5b65cf9742332d5e4acb68f41012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffffa95fa69f11dc1cbb77ef64f25a95d4b12ebda57d19d843333819d95c9172ff89000000006b48304502205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022100832176b59e8f50c56631acbc824bcba936c9476c559c42a4468be98975d07562012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffff02b000eb04000000001976a91472956eed9a8ecb19ae7e3ebd7b06cae4668696a788ac303db000000000001976a9146c0bd55dd2592287cd9992ce3ba3fc1208fb76da88ac00000000')
|
||||
|
||||
tx.ins.forEach(function (input, vin) {
|
||||
const script = input.script
|
||||
const scriptChunks = bitcoin.script.decompile(script)
|
||||
const { output: prevOutput, pubkey, signature } = bitcoin.payments.p2pkh({ input: input.script })
|
||||
|
||||
assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script')
|
||||
const prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z')
|
||||
const scriptSignature = bitcoin.script.signature.decode(scriptChunks[0])
|
||||
const publicKey = bitcoin.ECPair.fromPublicKey(scriptChunks[1])
|
||||
|
||||
const m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType)
|
||||
assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m')
|
||||
const scriptSignature = bitcoin.script.signature.decode(signature)
|
||||
const m = tx.hashForSignature(vin, prevOutput, scriptSignature.hashType)
|
||||
assert(bitcoin.ECPair.fromPublicKey(pubkey).verify(m, scriptSignature.signature), 'Invalid m')
|
||||
|
||||
// store the required information
|
||||
input.signature = scriptSignature.signature
|
||||
|
|
|
@ -12,6 +12,7 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs
|
|||
describe('bitcoinjs-lib (transactions w/ CSV)', function () {
|
||||
// force update MTP
|
||||
before(function (done) {
|
||||
this.timeout(30000)
|
||||
regtestUtils.mine(11, done)
|
||||
})
|
||||
|
||||
|
@ -44,12 +45,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
|
|||
|
||||
// 5 blocks from now
|
||||
const sequence = bip68.encode({ blocks: 5 })
|
||||
const redeemScript = csvCheckSigOutput(alice, bob, sequence)
|
||||
const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
|
||||
const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest)
|
||||
const p2sh = bitcoin.payments.p2sh({
|
||||
redeem: {
|
||||
output: csvCheckSigOutput(alice, bob, sequence)
|
||||
},
|
||||
network: regtest
|
||||
})
|
||||
|
||||
// fund the P2SH(CSV) address
|
||||
regtestUtils.faucet(address, 1e5, function (err, unspent) {
|
||||
regtestUtils.faucet(p2sh.address, 1e5, function (err, unspent) {
|
||||
if (err) return done(err)
|
||||
|
||||
const txb = new bitcoin.TransactionBuilder(regtest)
|
||||
|
@ -58,11 +62,18 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
|
|||
|
||||
// {Alice's signature} OP_TRUE
|
||||
const tx = txb.buildIncomplete()
|
||||
const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
|
||||
const redeemScriptSig = bitcoin.script.scriptHash.input.encode([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
], redeemScript)
|
||||
const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType)
|
||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||
network: regtest,
|
||||
redeem: {
|
||||
network: regtest,
|
||||
output: p2sh.redeem.output,
|
||||
input: bitcoin.script.compile([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
])
|
||||
}
|
||||
}).input
|
||||
tx.setInputScript(0, redeemScriptSig)
|
||||
|
||||
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
|
||||
|
@ -92,12 +103,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
|
|||
|
||||
// two hours after confirmation
|
||||
const sequence = bip68.encode({ seconds: 7168 })
|
||||
const redeemScript = csvCheckSigOutput(alice, bob, sequence)
|
||||
const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
|
||||
const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest)
|
||||
const p2sh = bitcoin.payments.p2sh({
|
||||
network: regtest,
|
||||
redeem: {
|
||||
output: csvCheckSigOutput(alice, bob, sequence)
|
||||
}
|
||||
})
|
||||
|
||||
// fund the P2SH(CSV) address
|
||||
regtestUtils.faucet(address, 2e4, function (err, unspent) {
|
||||
regtestUtils.faucet(p2sh.address, 2e4, function (err, unspent) {
|
||||
if (err) return done(err)
|
||||
|
||||
const txb = new bitcoin.TransactionBuilder(regtest)
|
||||
|
@ -106,12 +120,19 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
|
|||
|
||||
// {Alice's signature} OP_TRUE
|
||||
const tx = txb.buildIncomplete()
|
||||
const signatureHash = tx.hashForSignature(0, redeemScript, hashType)
|
||||
const redeemScriptSig = bitcoin.script.scriptHash.input.encode([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
], redeemScript)
|
||||
const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType)
|
||||
const redeemScriptSig = bitcoin.payments.p2sh({
|
||||
network: regtest,
|
||||
redeem: {
|
||||
network: regtest,
|
||||
output: p2sh.redeem.output,
|
||||
input: bitcoin.script.compile([
|
||||
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
||||
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
||||
bitcoin.opcodes.OP_TRUE
|
||||
])
|
||||
}
|
||||
}).input
|
||||
tx.setInputScript(0, redeemScriptSig)
|
||||
|
||||
regtestUtils.broadcast(tx.toHex(), function (err) {
|
||||
|
|
|
@ -91,7 +91,7 @@ describe('bitcoinjs-lib (transactions)', function () {
|
|||
|
||||
const txb = new bitcoin.TransactionBuilder(regtest)
|
||||
const data = Buffer.from('bitcoinjs-lib', 'utf8')
|
||||
const dataScript = bitcoin.script.nullData.output.encode([data])
|
||||
const dataScript = require('../../src/templates/nulldata').output.encode([data])
|
||||
|
||||
txb.addInput(unspent.txId, unspent.vout)
|
||||
txb.addOutput(dataScript, 1000)
|
||||
|
|
|
@ -1,514 +0,0 @@
|
|||
/* global describe, it */
|
||||
|
||||
const assert = require('assert')
|
||||
const bcrypto = require('../src/crypto')
|
||||
const bscript = require('../src/script')
|
||||
const btemplates = require('../src/templates')
|
||||
const ops = require('bitcoin-ops')
|
||||
|
||||
const fixtures = require('./fixtures/templates.json')
|
||||
|
||||
function fromHex (x) { return Buffer.from(x, 'hex') }
|
||||
function toHex (x) { return x.toString('hex') }
|
||||
|
||||
describe('script-templates', function () {
|
||||
describe('classifyInput', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (!f.input) return
|
||||
|
||||
it('classifies ' + f.input + ' as ' + f.type, function () {
|
||||
const input = bscript.fromASM(f.input)
|
||||
const type = btemplates.classifyInput(input)
|
||||
|
||||
assert.strictEqual(type, f.type)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (!f.input) return
|
||||
if (!f.typeIncomplete) return
|
||||
|
||||
it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, function () {
|
||||
const input = bscript.fromASM(f.input)
|
||||
const type = btemplates.classifyInput(input, true)
|
||||
|
||||
assert.strictEqual(type, f.typeIncomplete)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('classifyOutput', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (!f.output) return
|
||||
|
||||
it('classifies ' + f.output + ' as ' + f.type, function () {
|
||||
const output = bscript.fromASM(f.output)
|
||||
const type = btemplates.classifyOutput(output)
|
||||
|
||||
assert.strictEqual(type, f.type)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
;[
|
||||
'pubKey',
|
||||
'pubKeyHash',
|
||||
'scriptHash',
|
||||
'witnessPubKeyHash',
|
||||
'witnessScriptHash',
|
||||
'multisig',
|
||||
'nullData',
|
||||
'witnessCommitment'
|
||||
].forEach(function (name) {
|
||||
const inputType = btemplates[name].input
|
||||
const outputType = btemplates[name].output
|
||||
|
||||
describe(name + '.input.check', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (name.toLowerCase() === btemplates.types.P2WPKH) return
|
||||
if (name.toLowerCase() === btemplates.types.P2WSH) return
|
||||
const expected = name.toLowerCase() === f.type.toLowerCase()
|
||||
|
||||
if (inputType && f.input) {
|
||||
const input = bscript.fromASM(f.input)
|
||||
|
||||
it('returns ' + expected + ' for ' + f.input, function () {
|
||||
assert.strictEqual(inputType.check(input), expected)
|
||||
})
|
||||
|
||||
if (f.typeIncomplete) {
|
||||
const expectedIncomplete = name.toLowerCase() === f.typeIncomplete
|
||||
|
||||
it('returns ' + expected + ' for ' + f.input, function () {
|
||||
assert.strictEqual(inputType.check(input, true), expectedIncomplete)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!(fixtures.invalid[name])) return
|
||||
|
||||
fixtures.invalid[name].inputs.forEach(function (f) {
|
||||
if (!f.input && !f.inputHex) return
|
||||
|
||||
it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', function () {
|
||||
let input
|
||||
|
||||
if (f.input) {
|
||||
input = bscript.fromASM(f.input)
|
||||
} else {
|
||||
input = Buffer.from(f.inputHex, 'hex')
|
||||
}
|
||||
|
||||
assert.strictEqual(inputType.check(input), false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(name + '.output.check', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
const expected = name.toLowerCase() === f.type
|
||||
|
||||
if (outputType && f.output) {
|
||||
it('returns ' + expected + ' for ' + f.output, function () {
|
||||
const output = bscript.fromASM(f.output)
|
||||
|
||||
if (name.toLowerCase() === 'nulldata' && f.type === btemplates.types.WITNESS_COMMITMENT) return
|
||||
if (name.toLowerCase() === 'witnesscommitment' && f.type === btemplates.types.NULLDATA) return
|
||||
assert.strictEqual(outputType.check(output), expected)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (!(fixtures.invalid[name])) return
|
||||
|
||||
fixtures.invalid[name].outputs.forEach(function (f) {
|
||||
if (!f.output && !f.outputHex) return
|
||||
|
||||
it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', function () {
|
||||
let output
|
||||
|
||||
if (f.output) {
|
||||
output = bscript.fromASM(f.output)
|
||||
} else {
|
||||
output = Buffer.from(f.outputHex, 'hex')
|
||||
}
|
||||
|
||||
assert.strictEqual(outputType.check(output), false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pubKey.input', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'pubkey') return
|
||||
|
||||
const signature = Buffer.from(f.signature, 'hex')
|
||||
const input = btemplates.pubKey.input.encode(signature)
|
||||
|
||||
it('encodes to ' + f.input, function () {
|
||||
assert.strictEqual(bscript.toASM(input), f.input)
|
||||
})
|
||||
|
||||
it('decodes to ' + f.signature, function () {
|
||||
assert.deepEqual(btemplates.pubKey.input.decode(input), signature)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pubKey.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'pubkey') return
|
||||
|
||||
const pubKey = Buffer.from(f.pubKey, 'hex')
|
||||
const output = btemplates.pubKey.output.encode(pubKey)
|
||||
|
||||
it('encodes to ' + f.output, function () {
|
||||
assert.strictEqual(bscript.toASM(output), f.output)
|
||||
})
|
||||
|
||||
it('decodes to ' + f.pubKey, function () {
|
||||
assert.deepEqual(btemplates.pubKey.output.decode(output), pubKey)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pubKeyHash.input', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'pubkeyhash') return
|
||||
|
||||
const pubKey = Buffer.from(f.pubKey, 'hex')
|
||||
const signature = Buffer.from(f.signature, 'hex')
|
||||
const input = btemplates.pubKeyHash.input.encode(signature, pubKey)
|
||||
|
||||
it('encodes to ' + f.input, function () {
|
||||
assert.strictEqual(bscript.toASM(input), f.input)
|
||||
})
|
||||
|
||||
it('decodes to original arguments', function () {
|
||||
assert.deepEqual(btemplates.pubKeyHash.input.decode(input), {
|
||||
signature: signature,
|
||||
pubKey: pubKey
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pubKeyHash.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'pubkeyhash') return
|
||||
|
||||
const pubKey = Buffer.from(f.pubKey, 'hex')
|
||||
const pubKeyHash = bcrypto.hash160(pubKey)
|
||||
const output = btemplates.pubKeyHash.output.encode(pubKeyHash)
|
||||
|
||||
it('encodes to ' + f.output, function () {
|
||||
assert.strictEqual(bscript.toASM(output), f.output)
|
||||
})
|
||||
|
||||
it('decodes to ' + pubKeyHash.toString('hex'), function () {
|
||||
assert.deepEqual(btemplates.pubKeyHash.output.decode(output), pubKeyHash)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.pubKeyHash.outputs.forEach(function (f) {
|
||||
if (!f.hash) return
|
||||
const hash = Buffer.from(f.hash, 'hex')
|
||||
|
||||
it('throws on ' + f.exception, function () {
|
||||
assert.throws(function () {
|
||||
btemplates.pubKeyHash.output.encode(hash)
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('multisig.input', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'multisig' && f.typeIncomplete !== 'multisig') return
|
||||
const allowIncomplete = f.typeIncomplete !== undefined
|
||||
|
||||
const signatures = f.signatures.map(function (signature) {
|
||||
return signature ? Buffer.from(signature, 'hex') : ops.OP_0
|
||||
})
|
||||
|
||||
const input = btemplates.multisig.input.encode(signatures)
|
||||
|
||||
it('encodes to ' + f.input, function () {
|
||||
assert.strictEqual(bscript.toASM(input), f.input)
|
||||
})
|
||||
|
||||
it('decodes to ' + signatures.map(function (x) { return x === ops.OP_0 ? 'OP_0' : x.toString('hex') }), function () {
|
||||
assert.deepEqual(btemplates.multisig.input.decode(input, allowIncomplete), signatures)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.multisig.inputs.forEach(function (f) {
|
||||
if (!f.output) return
|
||||
const output = bscript.fromASM(f.output)
|
||||
|
||||
it('throws on ' + f.exception, function () {
|
||||
const signatures = f.signatures.map(function (signature) {
|
||||
return signature ? Buffer.from(signature, 'hex') : ops.OP_0
|
||||
})
|
||||
|
||||
assert.throws(function () {
|
||||
btemplates.multisig.input.encode(signatures, output)
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('multisig.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'multisig') return
|
||||
|
||||
const pubKeys = f.pubKeys.map(function (p) { return Buffer.from(p, 'hex') })
|
||||
const m = pubKeys.length
|
||||
|
||||
const output = btemplates.multisig.output.encode(m, pubKeys)
|
||||
|
||||
it('encodes ' + f.output, function () {
|
||||
assert.strictEqual(bscript.toASM(output), f.output)
|
||||
})
|
||||
|
||||
it('decodes to original arguments', function () {
|
||||
assert.deepEqual(btemplates.multisig.output.decode(output), {
|
||||
m: m,
|
||||
pubKeys: pubKeys
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.multisig.outputs.forEach(function (f) {
|
||||
if (!f.pubKeys) return
|
||||
const pubKeys = f.pubKeys.map(function (p) {
|
||||
return Buffer.from(p, 'hex')
|
||||
})
|
||||
|
||||
it('throws on ' + f.exception, function () {
|
||||
assert.throws(function () {
|
||||
btemplates.multisig.output.encode(f.m, pubKeys)
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('scriptHash.input', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'scripthash') return
|
||||
|
||||
const redeemScriptSig = bscript.fromASM(f.redeemScriptSig)
|
||||
const redeemScript = bscript.fromASM(f.redeemScript)
|
||||
const input = btemplates.scriptHash.input.encode(redeemScriptSig, redeemScript)
|
||||
|
||||
it('encodes to ' + f.output, function () {
|
||||
if (f.input) {
|
||||
assert.strictEqual(bscript.toASM(input), f.input)
|
||||
} else {
|
||||
assert.strictEqual(input.toString('hex'), f.inputHex)
|
||||
}
|
||||
})
|
||||
|
||||
it('decodes to original arguments', function () {
|
||||
assert.deepEqual(btemplates.scriptHash.input.decode(input), {
|
||||
redeemScriptSig: redeemScriptSig,
|
||||
redeemScript: redeemScript
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('scriptHash.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'scripthash') return
|
||||
if (!f.output) return
|
||||
|
||||
const redeemScript = bscript.fromASM(f.redeemScript)
|
||||
const scriptHash = bcrypto.hash160(redeemScript)
|
||||
const output = btemplates.scriptHash.output.encode(scriptHash)
|
||||
|
||||
it('encodes to ' + f.output, function () {
|
||||
assert.strictEqual(bscript.toASM(output), f.output)
|
||||
})
|
||||
|
||||
it('decodes to ' + scriptHash.toString('hex'), function () {
|
||||
assert.deepEqual(btemplates.scriptHash.output.decode(output), scriptHash)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.scriptHash.outputs.forEach(function (f) {
|
||||
if (!f.hash) return
|
||||
const hash = Buffer.from(f.hash, 'hex')
|
||||
|
||||
it('throws on ' + f.exception, function () {
|
||||
assert.throws(function () {
|
||||
btemplates.scriptHash.output.encode(hash)
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('witnessPubKeyHash.input', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'pubkeyhash' && f.type !== 'witnesspubkeyhash') return
|
||||
if (!f.inputStack) return
|
||||
|
||||
const pubKey = Buffer.from(f.pubKey, 'hex')
|
||||
const signature = Buffer.from(f.signature, 'hex')
|
||||
|
||||
it('encodes to ' + f.input, function () {
|
||||
const inputStack = btemplates.witnessPubKeyHash.input.encodeStack(signature, pubKey)
|
||||
|
||||
assert.deepEqual(inputStack.map(toHex), f.inputStack)
|
||||
})
|
||||
|
||||
it('decodes to original arguments', function () {
|
||||
const fInputStack = f.inputStack.map(fromHex)
|
||||
|
||||
assert.deepEqual(btemplates.witnessPubKeyHash.input.decodeStack(fInputStack), {
|
||||
signature: signature,
|
||||
pubKey: pubKey
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('witnessPubKeyHash.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'witnesspubkeyhash') return
|
||||
if (!f.output) return
|
||||
|
||||
const pubKey = Buffer.from(f.pubKey, 'hex')
|
||||
const pubKeyHash = bcrypto.hash160(pubKey)
|
||||
const output = btemplates.witnessPubKeyHash.output.encode(pubKeyHash)
|
||||
|
||||
it('encodes to ' + f.output, function () {
|
||||
assert.strictEqual(bscript.toASM(output), f.output)
|
||||
})
|
||||
|
||||
it('decodes to ' + pubKeyHash.toString('hex'), function () {
|
||||
assert.deepEqual(btemplates.witnessPubKeyHash.output.decode(output), pubKeyHash)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.witnessPubKeyHash.outputs.forEach(function (f) {
|
||||
if (!f.hash) return
|
||||
const hash = Buffer.from(f.hash, 'hex')
|
||||
|
||||
it('throws on ' + f.exception, function () {
|
||||
assert.throws(function () {
|
||||
btemplates.witnessPubKeyHash.output.encode(hash)
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('witnessScriptHash.input', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'witnessscripthash') return
|
||||
if (!f.inputStack || !f.witnessData) return
|
||||
|
||||
const witnessData = f.witnessData.map(fromHex)
|
||||
const witnessScript = bscript.fromASM(f.witnessScript || f.redeemScript)
|
||||
|
||||
it('encodes to ' + f.input, function () {
|
||||
const inputStack = btemplates.witnessScriptHash.input.encodeStack(witnessData, witnessScript)
|
||||
|
||||
assert.deepEqual(inputStack.map(toHex), f.inputStack)
|
||||
})
|
||||
|
||||
it('decodes to original arguments', function () {
|
||||
const result = btemplates.witnessScriptHash.input.decodeStack(f.inputStack.map(fromHex))
|
||||
|
||||
assert.deepEqual(result.witnessData.map(toHex), f.witnessData)
|
||||
assert.strictEqual(bscript.toASM(result.witnessScript), f.witnessScript)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('witnessScriptHash.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'witnessscripthash') return
|
||||
if (!f.output) return
|
||||
|
||||
const witnessScriptPubKey = bscript.fromASM(f.witnessScript)
|
||||
const scriptHash = bcrypto.hash256(witnessScriptPubKey)
|
||||
const output = btemplates.witnessScriptHash.output.encode(scriptHash)
|
||||
|
||||
it('encodes to ' + f.output, function () {
|
||||
assert.strictEqual(bscript.toASM(output), f.output)
|
||||
})
|
||||
|
||||
it('decodes to ' + scriptHash.toString('hex'), function () {
|
||||
assert.deepEqual(btemplates.witnessScriptHash.output.decode(output), scriptHash)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.witnessScriptHash.outputs.forEach(function (f) {
|
||||
if (!f.hash) return
|
||||
const hash = Buffer.from(f.hash, 'hex')
|
||||
|
||||
it('throws on ' + f.exception, function () {
|
||||
assert.throws(function () {
|
||||
btemplates.witnessScriptHash.output.encode(hash)
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('witnessCommitment.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'witnesscommitment') return
|
||||
if (!f.scriptPubKey) return
|
||||
|
||||
const commitment = Buffer.from(f.witnessCommitment, 'hex')
|
||||
const scriptPubKey = btemplates.witnessCommitment.output.encode(commitment)
|
||||
|
||||
it('encodes to ' + f.scriptPubKey, function () {
|
||||
assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey)
|
||||
})
|
||||
|
||||
it('decodes to ' + commitment.toString('hex'), function () {
|
||||
assert.deepEqual(btemplates.witnessCommitment.output.decode(scriptPubKey), commitment)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.witnessCommitment.outputs.forEach(function (f) {
|
||||
if (f.commitment) {
|
||||
const hash = Buffer.from(f.commitment, 'hex')
|
||||
it('throws on bad encode data', function () {
|
||||
assert.throws(function () {
|
||||
btemplates.witnessCommitment.output.encode(hash)
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
}
|
||||
|
||||
if (f.scriptPubKeyHex) {
|
||||
it('.decode throws on ' + f.description, function () {
|
||||
assert.throws(function () {
|
||||
btemplates.witnessCommitment.output.decode(Buffer.from(f.scriptPubKeyHex, 'hex'))
|
||||
}, new RegExp(f.exception))
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('nullData.output', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
if (f.type !== 'nulldata') return
|
||||
|
||||
const data = f.data.map(function (x) { return Buffer.from(x, 'hex') })
|
||||
const output = btemplates.nullData.output.encode(data)
|
||||
|
||||
it('encodes to ' + f.output, function () {
|
||||
assert.strictEqual(bscript.toASM(output), f.output)
|
||||
})
|
||||
|
||||
it('decodes to ' + f.data, function () {
|
||||
assert.deepEqual(btemplates.nullData.output.decode(output), data)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -4,8 +4,8 @@ const assert = require('assert')
|
|||
const baddress = require('../src/address')
|
||||
const bcrypto = require('../src/crypto')
|
||||
const bscript = require('../src/script')
|
||||
const btemplates = require('../src/templates')
|
||||
const ops = require('bitcoin-ops')
|
||||
const payments = require('../src/payments')
|
||||
|
||||
const ECPair = require('../src/ecpair')
|
||||
const Transaction = require('../src/transaction')
|
||||
|
@ -468,10 +468,17 @@ describe('TransactionBuilder', function () {
|
|||
const scriptSig = tx.ins[i].script
|
||||
|
||||
// ignore OP_0 on the front, ignore redeemScript
|
||||
const signatures = bscript.decompile(scriptSig).slice(1, -1).filter(function (x) { return x !== ops.OP_0 })
|
||||
const signatures = bscript.decompile(scriptSig)
|
||||
.slice(1, -1)
|
||||
.filter(x => x !== ops.OP_0)
|
||||
|
||||
// rebuild/replace the scriptSig without them
|
||||
const replacement = btemplates.scriptHash.input.encode(btemplates.multisig.input.encode(signatures), redeemScript)
|
||||
const replacement = payments.p2sh({
|
||||
redeem: payments.p2ms({
|
||||
output: redeemScript,
|
||||
signatures
|
||||
}, { allowIncomplete: true })
|
||||
}).input
|
||||
assert.strictEqual(bscript.toASM(replacement), sign.scriptSigFiltered)
|
||||
|
||||
tx.ins[i].script = replacement
|
||||
|
|
Loading…
Add table
Reference in a new issue