Jonathan Underwood
7 years ago
committed by
GitHub
21 changed files with 2687 additions and 5 deletions
@ -0,0 +1,19 @@ |
|||
const p2ms = require('./p2ms') |
|||
const p2pk = require('./p2pk') |
|||
const p2pkh = require('./p2pkh') |
|||
const p2sh = require('./p2sh') |
|||
const p2wpkh = require('./p2wpkh') |
|||
const p2wsh = require('./p2wsh') |
|||
|
|||
module.exports = { |
|||
p2ms: p2ms, |
|||
p2pk: p2pk, |
|||
p2pkh: p2pkh, |
|||
p2sh: p2sh, |
|||
p2wpkh: p2wpkh, |
|||
p2wsh: p2wsh |
|||
} |
|||
|
|||
// TODO
|
|||
// OP_RETURN
|
|||
// witness commitment
|
@ -0,0 +1,30 @@ |
|||
function prop (object, name, f) { |
|||
Object.defineProperty(object, name, { |
|||
configurable: true, |
|||
enumerable: true, |
|||
get: function () { |
|||
let value = f.call(this) |
|||
this[name] = value |
|||
return value |
|||
}, |
|||
set: function (value) { |
|||
Object.defineProperty(this, name, { |
|||
configurable: true, |
|||
enumerable: true, |
|||
value: value, |
|||
writable: true |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
function value (f) { |
|||
let value |
|||
return function () { |
|||
if (value !== undefined) return value |
|||
value = f() |
|||
return value |
|||
} |
|||
} |
|||
|
|||
module.exports = { prop, value } |
@ -0,0 +1,140 @@ |
|||
let lazy = require('./lazy') |
|||
let typef = require('typeforce') |
|||
let OPS = require('bitcoin-ops') |
|||
let ecc = require('tiny-secp256k1') |
|||
|
|||
let bscript = require('../script') |
|||
let BITCOIN_NETWORK = require('../networks').bitcoin |
|||
let OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
|||
|
|||
function stacksEqual (a, b) { |
|||
if (a.length !== b.length) return false |
|||
|
|||
return a.every(function (x, i) { |
|||
return x.equals(b[i]) |
|||
}) |
|||
} |
|||
|
|||
// input: OP_0 [signatures ...]
|
|||
// output: m [pubKeys ...] n OP_CHECKMULTISIG
|
|||
function p2ms (a, opts) { |
|||
if ( |
|||
!a.output && |
|||
!(a.pubkeys && a.m !== undefined) |
|||
) throw new TypeError('Not enough data') |
|||
opts = opts || { validate: true } |
|||
|
|||
function isAcceptableSignature (x) { |
|||
return bscript.isCanonicalScriptSignature(x) || (opts.allowIncomplete && (x === OPS.OP_0)) |
|||
} |
|||
|
|||
typef({ |
|||
network: typef.maybe(typef.Object), |
|||
m: typef.maybe(typef.Number), |
|||
n: typef.maybe(typef.Number), |
|||
output: typef.maybe(typef.Buffer), |
|||
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), |
|||
|
|||
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), |
|||
input: typef.maybe(typef.Buffer) |
|||
}, a) |
|||
|
|||
let network = a.network || BITCOIN_NETWORK |
|||
let o = { network } |
|||
|
|||
let chunks |
|||
let decoded = false |
|||
function decode (output) { |
|||
if (decoded) return |
|||
decoded = true |
|||
chunks = bscript.decompile(output) |
|||
let om = chunks[0] - OP_INT_BASE |
|||
let on = chunks[chunks.length - 2] - OP_INT_BASE |
|||
o.m = om |
|||
o.n = on |
|||
o.pubkeys = chunks.slice(1, -2) |
|||
} |
|||
|
|||
lazy.prop(o, 'output', function () { |
|||
if (!a.m) return |
|||
if (!o.n) return |
|||
if (!a.pubkeys) return |
|||
return bscript.compile([].concat( |
|||
OP_INT_BASE + a.m, |
|||
a.pubkeys, |
|||
OP_INT_BASE + o.n, |
|||
OPS.OP_CHECKMULTISIG |
|||
)) |
|||
}) |
|||
lazy.prop(o, 'm', function () { |
|||
if (!o.output) return |
|||
decode(o.output) |
|||
return o.m |
|||
}) |
|||
lazy.prop(o, 'n', function () { |
|||
if (!o.pubkeys) return |
|||
return o.pubkeys.length |
|||
}) |
|||
lazy.prop(o, 'pubkeys', function () { |
|||
if (!a.output) return |
|||
decode(a.output) |
|||
return o.pubkeys |
|||
}) |
|||
lazy.prop(o, 'signatures', function () { |
|||
if (!a.input) return |
|||
return bscript.decompile(a.input).slice(1) |
|||
}) |
|||
lazy.prop(o, 'input', function () { |
|||
if (!a.signatures) return |
|||
return bscript.compile([OPS.OP_0].concat(a.signatures)) |
|||
}) |
|||
lazy.prop(o, 'witness', function () { |
|||
if (!o.input) return |
|||
return [] |
|||
}) |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
if (a.output) { |
|||
decode(a.output) |
|||
if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid') |
|||
if (!typef.Number(chunks[chunks.length - 2])) throw new TypeError('Output is invalid') |
|||
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid') |
|||
|
|||
if ( |
|||
o.m <= 0 || |
|||
o.n > 16 || |
|||
o.m > o.n || |
|||
o.n !== chunks.length - 3) throw new TypeError('Output is invalid') |
|||
if (!o.pubkeys.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') |
|||
|
|||
if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch') |
|||
if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch') |
|||
if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) throw new TypeError('Pubkeys mismatch') |
|||
} |
|||
|
|||
if (a.pubkeys) { |
|||
if (a.n !== undefined && a.n !== a.pubkeys.length) throw new TypeError('Pubkey count mismatch') |
|||
o.n = a.pubkeys.length |
|||
|
|||
if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m') |
|||
} |
|||
|
|||
if (a.signatures) { |
|||
if (a.signatures.length < o.m) throw new TypeError('Not enough signatures provided') |
|||
if (a.signatures.length > o.m) throw new TypeError('Too many signatures provided') |
|||
} |
|||
|
|||
if (a.input) { |
|||
if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') |
|||
if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') |
|||
|
|||
if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures))) throw new TypeError('Signature mismatch') |
|||
if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
} |
|||
|
|||
module.exports = p2ms |
@ -0,0 +1,80 @@ |
|||
let lazy = require('./lazy') |
|||
let typef = require('typeforce') |
|||
let OPS = require('bitcoin-ops') |
|||
let ecc = require('tiny-secp256k1') |
|||
|
|||
let bscript = require('../script') |
|||
let BITCOIN_NETWORK = require('../networks').bitcoin |
|||
|
|||
// input: {signature}
|
|||
// output: {pubKey} OP_CHECKSIG
|
|||
function p2pk (a, opts) { |
|||
if ( |
|||
!a.output && |
|||
!a.pubkey |
|||
) throw new TypeError('Not enough data') |
|||
opts = opts || { validate: true } |
|||
|
|||
typef({ |
|||
network: typef.maybe(typef.Object), |
|||
output: typef.maybe(typef.Buffer), |
|||
pubkey: typef.maybe(ecc.isPoint), |
|||
|
|||
signature: typef.maybe(bscript.isCanonicalScriptSignature), |
|||
input: typef.maybe(typef.Buffer) |
|||
}, a) |
|||
|
|||
let _chunks = lazy.value(function () { return bscript.decompile(a.input) }) |
|||
|
|||
let network = a.network || BITCOIN_NETWORK |
|||
let o = { network } |
|||
|
|||
lazy.prop(o, 'output', function () { |
|||
if (!a.pubkey) return |
|||
return bscript.compile([ |
|||
a.pubkey, |
|||
OPS.OP_CHECKSIG |
|||
]) |
|||
}) |
|||
lazy.prop(o, 'pubkey', function () { |
|||
if (!a.output) return |
|||
return a.output.slice(1, -1) |
|||
}) |
|||
lazy.prop(o, 'signature', function () { |
|||
if (!a.input) return |
|||
return _chunks()[0] |
|||
}) |
|||
lazy.prop(o, 'input', function () { |
|||
if (!a.signature) return |
|||
return bscript.compile([a.signature]) |
|||
}) |
|||
lazy.prop(o, 'witness', function () { |
|||
if (!o.input) return |
|||
return [] |
|||
}) |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
if (a.pubkey && a.output) { |
|||
if (!a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch') |
|||
} |
|||
|
|||
if (a.output) { |
|||
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') |
|||
if (!ecc.isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid') |
|||
} |
|||
|
|||
if (a.signature) { |
|||
if (a.input && !a.input.equals(o.input)) throw new TypeError('Input mismatch') |
|||
} |
|||
|
|||
if (a.input) { |
|||
if (_chunks().length !== 1) throw new TypeError('Input is invalid') |
|||
if (!bscript.isCanonicalScriptSignature(_chunks()[0])) throw new TypeError('Input has invalid signature') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
} |
|||
|
|||
module.exports = p2pk |
@ -0,0 +1,127 @@ |
|||
let lazy = require('./lazy') |
|||
let typef = require('typeforce') |
|||
let OPS = require('bitcoin-ops') |
|||
let ecc = require('tiny-secp256k1') |
|||
|
|||
let baddress = require('../address') |
|||
let bcrypto = require('../crypto') |
|||
let bscript = require('../script') |
|||
let BITCOIN_NETWORK = require('../networks').bitcoin |
|||
|
|||
// input: {signature} {pubkey}
|
|||
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
|
|||
function p2pkh (a, opts) { |
|||
if ( |
|||
!a.address && |
|||
!a.hash && |
|||
!a.output && |
|||
!a.pubkey && |
|||
!a.input |
|||
) throw new TypeError('Not enough data') |
|||
opts = opts || { validate: true } |
|||
|
|||
typef({ |
|||
network: typef.maybe(typef.Object), |
|||
address: typef.maybe(typef.String), |
|||
hash: typef.maybe(typef.BufferN(20)), |
|||
output: typef.maybe(typef.BufferN(25)), |
|||
|
|||
pubkey: typef.maybe(ecc.isPoint), |
|||
signature: typef.maybe(bscript.isCanonicalScriptSignature), |
|||
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) }) |
|||
|
|||
let network = a.network || BITCOIN_NETWORK |
|||
let o = { network } |
|||
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
return baddress.toBase58Check(o.hash, network.pubKeyHash) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
if (a.output) return a.output.slice(3, 23) |
|||
if (a.address) return _address().hash |
|||
if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey) |
|||
}) |
|||
lazy.prop(o, 'output', function () { |
|||
if (!o.hash) return |
|||
return bscript.compile([ |
|||
OPS.OP_DUP, |
|||
OPS.OP_HASH160, |
|||
o.hash, |
|||
OPS.OP_EQUALVERIFY, |
|||
OPS.OP_CHECKSIG |
|||
]) |
|||
}) |
|||
lazy.prop(o, 'pubkey', function () { |
|||
if (!a.input) return |
|||
return _chunks()[1] |
|||
}) |
|||
lazy.prop(o, 'signature', function () { |
|||
if (!a.input) return |
|||
return _chunks()[0] |
|||
}) |
|||
lazy.prop(o, 'input', function () { |
|||
if (!a.pubkey) return |
|||
if (!a.signature) return |
|||
return bscript.compile([a.signature, a.pubkey]) |
|||
}) |
|||
lazy.prop(o, 'witness', function () { |
|||
if (!o.input) return |
|||
return [] |
|||
}) |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash |
|||
if (a.address) { |
|||
if (_address().version !== network.pubKeyHash) throw new TypeError('Network mismatch') |
|||
if (_address().hash.length !== 20) throw new TypeError('Invalid address') |
|||
else hash = _address().hash |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') |
|||
else hash = a.hash |
|||
} |
|||
|
|||
if (a.output) { |
|||
if ( |
|||
a.output.length !== 25 || |
|||
a.output[0] !== OPS.OP_DUP || |
|||
a.output[1] !== OPS.OP_HASH160 || |
|||
a.output[2] !== 0x14 || |
|||
a.output[23] !== OPS.OP_EQUALVERIFY || |
|||
a.output[24] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') |
|||
|
|||
if (hash && !hash.equals(a.output.slice(3, 23))) throw new TypeError('Hash mismatch') |
|||
else hash = a.output.slice(3, 23) |
|||
} |
|||
|
|||
if (a.pubkey) { |
|||
let pkh = bcrypto.hash160(a.pubkey) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
else hash = pkh |
|||
} |
|||
|
|||
if (a.input) { |
|||
let chunks = _chunks() |
|||
if (chunks.length !== 2) throw new TypeError('Input is invalid') |
|||
if (!bscript.isCanonicalScriptSignature(chunks[0])) throw new TypeError('Input has invalid signature') |
|||
if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey') |
|||
|
|||
if (a.signature && !a.signature.equals(chunks[0])) throw new TypeError('Signature mismatch') |
|||
if (a.pubkey && !a.pubkey.equals(chunks[1])) throw new TypeError('Pubkey mismatch') |
|||
|
|||
let pkh = bcrypto.hash160(chunks[1]) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
} |
|||
|
|||
module.exports = p2pkh |
@ -0,0 +1,176 @@ |
|||
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 |
|||
|
|||
function stacksEqual (a, b) { |
|||
if (a.length !== b.length) return false |
|||
|
|||
return a.every(function (x, i) { |
|||
return x.equals(b[i]) |
|||
}) |
|||
} |
|||
|
|||
// input: [redeemScriptSig ...] {redeemScript}
|
|||
// witness: <?>
|
|||
// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
|
|||
function p2sh (a, opts) { |
|||
if ( |
|||
!a.address && |
|||
!a.hash && |
|||
!a.output && |
|||
!a.redeem && |
|||
!a.input |
|||
) throw new TypeError('Not enough data') |
|||
opts = opts || { validate: true } |
|||
|
|||
typef({ |
|||
network: typef.maybe(typef.Object), |
|||
|
|||
address: typef.maybe(typef.String), |
|||
hash: typef.maybe(typef.BufferN(20)), |
|||
output: typef.maybe(typef.BufferN(23)), |
|||
|
|||
redeem: typef.maybe({ |
|||
network: typef.maybe(typef.Object), |
|||
output: typef.Buffer, |
|||
input: typef.maybe(typef.Buffer), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
|||
}), |
|||
input: typef.maybe(typef.Buffer), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
|||
}, a) |
|||
|
|||
const network = a.network || BITCOIN_NETWORK |
|||
const o = { network } |
|||
|
|||
const _address = lazy.value(function () { return baddress.fromBase58Check(a.address) }) |
|||
const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) |
|||
const _redeem = lazy.value(function () { |
|||
const chunks = _chunks() |
|||
return { |
|||
network: network, |
|||
output: chunks[chunks.length - 1], |
|||
input: bscript.compile(chunks.slice(0, -1)), |
|||
witness: a.witness || [] |
|||
} |
|||
}) |
|||
|
|||
// output dependents
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
return baddress.toBase58Check(o.hash, network.scriptHash) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
// in order of least effort
|
|||
if (a.output) return a.output.slice(2, 22) |
|||
if (a.address) return _address().hash |
|||
if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output) |
|||
}) |
|||
lazy.prop(o, 'output', function () { |
|||
if (!o.hash) return |
|||
return bscript.compile([ |
|||
OPS.OP_HASH160, |
|||
o.hash, |
|||
OPS.OP_EQUAL |
|||
]) |
|||
}) |
|||
|
|||
// input dependents
|
|||
lazy.prop(o, 'redeem', function () { |
|||
if (!a.input) return |
|||
return _redeem() |
|||
}) |
|||
lazy.prop(o, 'input', function () { |
|||
if (!a.redeem || !a.redeem.input) return |
|||
return bscript.compile([].concat( |
|||
bscript.decompile(a.redeem.input), |
|||
a.redeem.output |
|||
)) |
|||
}) |
|||
lazy.prop(o, 'witness', function () { |
|||
if (o.redeem && o.redeem.witness) return o.redeem.witness |
|||
if (o.input) return [] |
|||
}) |
|||
|
|||
if (opts.validate) { |
|||
let hash |
|||
if (a.address) { |
|||
if (_address().version !== network.scriptHash) throw new TypeError('Network mismatch') |
|||
if (_address().hash.length !== 20) throw new TypeError('Invalid address') |
|||
else hash = _address().hash |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') |
|||
else hash = a.hash |
|||
} |
|||
|
|||
if (a.output) { |
|||
if ( |
|||
a.output.length !== 23 || |
|||
a.output[0] !== OPS.OP_HASH160 || |
|||
a.output[1] !== 0x14 || |
|||
a.output[22] !== OPS.OP_EQUAL) throw new TypeError('Output is invalid') |
|||
const hash2 = a.output.slice(2, 22) |
|||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') |
|||
else hash = hash2 |
|||
} |
|||
|
|||
// inlined to prevent 'no-inner-declarations' failing
|
|||
const checkRedeem = function (redeem) { |
|||
// is the redeem output empty/invalid?
|
|||
const decompile = bscript.decompile(redeem.output) |
|||
if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short') |
|||
|
|||
// match hash against other sources
|
|||
const hash2 = bcrypto.hash160(redeem.output) |
|||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') |
|||
else hash = hash2 |
|||
|
|||
if (redeem.input) { |
|||
const hasInput = redeem.input.length > 0 |
|||
const hasWitness = redeem.witness && redeem.witness.length > 0 |
|||
if (!hasInput && !hasWitness) throw new TypeError('Empty input') |
|||
if (hasInput && hasWitness) throw new TypeError('Input and witness provided') |
|||
if (hasInput) { |
|||
const richunks = bscript.decompile(redeem.input) |
|||
if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig') |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (a.input) { |
|||
const chunks = _chunks() |
|||
if (!chunks || chunks.length < 1) throw new TypeError('Input too short') |
|||
if (!Buffer.isBuffer(_redeem().output)) throw new TypeError('Input is invalid') |
|||
|
|||
checkRedeem(_redeem()) |
|||
} |
|||
|
|||
if (a.redeem) { |
|||
if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') |
|||
if (o.redeem) { |
|||
if (a.redeem.output && !a.redeem.output.equals(o.redeem.output)) throw new TypeError('Redeem.output mismatch') |
|||
if (a.redeem.input && !a.redeem.input.equals(o.redeem.input)) throw new TypeError('Redeem.input mismatch') |
|||
} |
|||
|
|||
checkRedeem(a.redeem) |
|||
} |
|||
|
|||
if (a.witness) { |
|||
if ( |
|||
a.redeem && |
|||
a.redeem.witness && |
|||
!stacksEqual(a.redeem.witness, a.witness)) throw new TypeError('Witness and redeem.witness mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
} |
|||
|
|||
module.exports = p2sh |
@ -0,0 +1,124 @@ |
|||
let lazy = require('./lazy') |
|||
let typef = require('typeforce') |
|||
let OPS = require('bitcoin-ops') |
|||
let ecc = require('tiny-secp256k1') |
|||
|
|||
let baddress = require('../address') |
|||
let bcrypto = require('../crypto') |
|||
let bscript = require('../script') |
|||
let BITCOIN_NETWORK = require('../networks').bitcoin |
|||
|
|||
let EMPTY_BUFFER = Buffer.alloc(0) |
|||
|
|||
// witness: {signature} {pubKey}
|
|||
// input: <>
|
|||
// output: OP_0 {pubKeyHash}
|
|||
function p2wpkh (a, opts) { |
|||
if ( |
|||
!a.address && |
|||
!a.hash && |
|||
!a.output && |
|||
!a.pubkey && |
|||
!a.witness |
|||
) throw new TypeError('Not enough data') |
|||
opts = opts || { validate: true } |
|||
|
|||
typef({ |
|||
address: typef.maybe(typef.String), |
|||
hash: typef.maybe(typef.BufferN(20)), |
|||
input: typef.maybe(typef.BufferN(0)), |
|||
network: typef.maybe(typef.Object), |
|||
output: typef.maybe(typef.BufferN(22)), |
|||
pubkey: typef.maybe(ecc.isPoint), |
|||
signature: typef.maybe(bscript.isCanonicalScriptSignature), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
|||
}, a) |
|||
|
|||
let _address = lazy.value(function () { return baddress.fromBech32(a.address) }) |
|||
|
|||
let network = a.network || BITCOIN_NETWORK |
|||
let o = { network } |
|||
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
return baddress.toBech32(o.hash, 0x00, network.bech32) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
if (a.output) return a.output.slice(2, 22) |
|||
if (a.address) return _address().data |
|||
if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey) |
|||
}) |
|||
lazy.prop(o, 'output', function () { |
|||
if (!o.hash) return |
|||
return bscript.compile([ |
|||
OPS.OP_0, |
|||
o.hash |
|||
]) |
|||
}) |
|||
lazy.prop(o, 'pubkey', function () { |
|||
if (a.pubkey) return a.pubkey |
|||
if (!a.witness) return |
|||
return a.witness[1] |
|||
}) |
|||
lazy.prop(o, 'signature', function () { |
|||
if (!a.witness) return |
|||
return a.witness[0] |
|||
}) |
|||
lazy.prop(o, 'input', function () { |
|||
if (!o.witness) return |
|||
return EMPTY_BUFFER |
|||
}) |
|||
lazy.prop(o, 'witness', function () { |
|||
if (!a.pubkey) return |
|||
if (!a.signature) return |
|||
return [a.signature, a.pubkey] |
|||
}) |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash |
|||
if (a.address) { |
|||
if (network && network.bech32 !== _address().prefix) throw new TypeError('Network mismatch') |
|||
if (_address().version !== 0x00) throw new TypeError('Invalid version') |
|||
if (_address().data.length !== 20) throw new TypeError('Invalid data') |
|||
if (hash && !hash.equals(_address().data)) throw new TypeError('Hash mismatch') |
|||
else hash = _address().data |
|||
} |
|||
|
|||
if (a.pubkey) { |
|||
let pkh = bcrypto.hash160(a.pubkey) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
else hash = pkh |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') |
|||
else hash = a.hash |
|||
} |
|||
|
|||
if (a.output) { |
|||
if ( |
|||
a.output.length !== 22 || |
|||
a.output[0] !== OPS.OP_0 || |
|||
a.output[1] !== 0x14) throw new TypeError('Output is invalid') |
|||
if (hash && !hash.equals(a.output.slice(2))) throw new TypeError('Hash mismatch') |
|||
else hash = a.output.slice(2) |
|||
} |
|||
|
|||
if (a.witness) { |
|||
if (a.witness.length !== 2) throw new TypeError('Input is invalid') |
|||
if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Input has invalid signature') |
|||
if (!ecc.isPoint(a.witness[1])) throw new TypeError('Input has invalid pubkey') |
|||
|
|||
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]) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
} |
|||
|
|||
module.exports = p2wpkh |
@ -0,0 +1,154 @@ |
|||
let lazy = require('./lazy') |
|||
let typef = require('typeforce') |
|||
let OPS = require('bitcoin-ops') |
|||
|
|||
let baddress = require('../address') |
|||
let bcrypto = require('../crypto') |
|||
let bscript = require('../script') |
|||
let BITCOIN_NETWORK = require('../networks').bitcoin |
|||
|
|||
let EMPTY_BUFFER = Buffer.alloc(0) |
|||
|
|||
function stacksEqual (a, b) { |
|||
if (a.length !== b.length) return false |
|||
|
|||
return a.every(function (x, i) { |
|||
return x.equals(b[i]) |
|||
}) |
|||
} |
|||
|
|||
// input: <>
|
|||
// witness: [redeemScriptSig ...] {redeemScript}
|
|||
// output: OP_0 {sha256(redeemScript)}
|
|||
function p2wsh (a, opts) { |
|||
if ( |
|||
!a.address && |
|||
!a.hash && |
|||
!a.output && |
|||
!a.redeem && |
|||
!a.witness |
|||
) throw new TypeError('Not enough data') |
|||
opts = opts || { validate: true } |
|||
|
|||
typef({ |
|||
network: typef.maybe(typef.Object), |
|||
|
|||
address: typef.maybe(typef.String), |
|||
hash: typef.maybe(typef.BufferN(32)), |
|||
output: typef.maybe(typef.BufferN(34)), |
|||
|
|||
redeem: typef.maybe({ |
|||
input: typef.maybe(typef.Buffer), |
|||
network: typef.maybe(typef.Object), |
|||
output: typef.Buffer, |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
|||
}), |
|||
input: typef.maybe(typef.BufferN(0)), |
|||
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) }) |
|||
|
|||
let network = a.network || BITCOIN_NETWORK |
|||
let o = { network } |
|||
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
return baddress.toBech32(o.hash, 0x00, network.bech32) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
if (a.output) return a.output.slice(2) |
|||
if (a.address) return baddress.fromBech32(a.address).data |
|||
if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output) |
|||
}) |
|||
lazy.prop(o, 'output', function () { |
|||
if (!o.hash) return |
|||
return bscript.compile([ |
|||
OPS.OP_0, |
|||
o.hash |
|||
]) |
|||
}) |
|||
lazy.prop(o, 'redeem', function () { |
|||
if (!a.witness) return |
|||
return { |
|||
output: a.witness[a.witness.length - 1], |
|||
input: EMPTY_BUFFER, |
|||
witness: a.witness.slice(0, -1) |
|||
} |
|||
}) |
|||
lazy.prop(o, 'input', function () { |
|||
if (!o.witness) return |
|||
return EMPTY_BUFFER |
|||
}) |
|||
lazy.prop(o, 'witness', function () { |
|||
// transform redeem input to witness stack?
|
|||
if (a.redeem && a.redeem.input && a.redeem.input.length > 0) { |
|||
let stack = bscript.toStack(_rchunks()) |
|||
|
|||
// assign, and blank the existing input
|
|||
o.redeem = Object.assign({ witness: stack }, a.redeem) |
|||
o.redeem.input = EMPTY_BUFFER |
|||
return [].concat(stack, a.redeem.output) |
|||
} |
|||
|
|||
if (!a.redeem) return |
|||
if (!a.redeem.witness) return |
|||
return [].concat(a.redeem.witness, a.redeem.output) |
|||
}) |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash |
|||
if (a.address) { |
|||
if (_address().prefix !== network.bech32) throw new TypeError('Network mismatch') |
|||
if (_address().version !== 0x00) throw new TypeError('Invalid version') |
|||
if (_address().data.length !== 32) throw new TypeError('Invalid data') |
|||
else hash = _address().data |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') |
|||
else hash = a.hash |
|||
} |
|||
|
|||
if (a.output) { |
|||
if ( |
|||
a.output.length !== 34 || |
|||
a.output[0] !== OPS.OP_0 || |
|||
a.output[1] !== 0x20) throw new TypeError('Output is invalid') |
|||
let hash2 = a.output.slice(2) |
|||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') |
|||
else hash = hash2 |
|||
} |
|||
|
|||
if (a.redeem) { |
|||
if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') |
|||
|
|||
// is there two redeem sources?
|
|||
if ( |
|||
a.redeem.input && |
|||
a.redeem.input.length > 0 && |
|||
a.redeem.witness) throw new TypeError('Ambiguous witness source') |
|||
|
|||
// is the redeem output non-empty?
|
|||
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) |
|||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') |
|||
else hash = hash2 |
|||
|
|||
if (a.redeem.input && !bscript.isPushOnly(_rchunks())) throw new TypeError('Non push-only scriptSig') |
|||
if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness)) throw new TypeError('Witness and redeem.witness mismatch') |
|||
} |
|||
|
|||
if (a.witness) { |
|||
if (a.redeem && !a.redeem.output.equals(a.witness[a.witness.length - 1])) throw new TypeError('Witness and redeem.output mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
} |
|||
|
|||
module.exports = p2wsh |
@ -0,0 +1,40 @@ |
|||
{ |
|||
"name": "bitcoinjs-playground", |
|||
"version": "1.0.0", |
|||
"description": "Go nuts!", |
|||
"main": "_testnet.js", |
|||
"scripts": { |
|||
"test": "echo \"Error: no test specified\" && exit 1" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/bitcoinjs/bitcoinjs-playground.git" |
|||
}, |
|||
"author": "", |
|||
"license": "ISC", |
|||
"bugs": { |
|||
"url": "https://github.com/bitcoinjs/bitcoinjs-playground/issues" |
|||
}, |
|||
"homepage": "https://github.com/bitcoinjs/bitcoinjs-playground#readme", |
|||
"dependencies": { |
|||
"async": "^2.5.0", |
|||
"bech32": "^1.1.3", |
|||
"bip21": "^2.0.1", |
|||
"bip32-utils": "^0.11.1", |
|||
"bip38": "^2.0.2", |
|||
"bip39": "^2.5.0", |
|||
"bip69": "^2.1.1", |
|||
"bitcoin-ops": "^1.4.1", |
|||
"bitcoinjs-lib": "^3.3.2", |
|||
"bs58": "^4.0.1", |
|||
"bs58check": "^2.1.1", |
|||
"cb-http-client": "^0.2.3", |
|||
"coinselect": "^3.1.11", |
|||
"dhttp": "^2.4.2", |
|||
"merkle-lib": "^2.0.10", |
|||
"mocha": "^5.0.5", |
|||
"tape": "^4.9.0", |
|||
"typeforce": "^1.11.4", |
|||
"utxo": "^2.0.4" |
|||
} |
|||
} |
@ -0,0 +1,378 @@ |
|||
{ |
|||
"valid": [ |
|||
{ |
|||
"description": "output from output", |
|||
"arguments": { |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG" |
|||
}, |
|||
"expected": { |
|||
"m": 2, |
|||
"n": 2, |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
], |
|||
"signatures": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from m/pubkeys", |
|||
"arguments": { |
|||
"m": 1, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
] |
|||
}, |
|||
"expected": { |
|||
"m": 1, |
|||
"n": 2, |
|||
"output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
], |
|||
"signatures": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from m/pubkeys/signatures", |
|||
"arguments": { |
|||
"m": 2, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002", |
|||
"030000000000000000000000000000000000000000000000000000000000000003" |
|||
], |
|||
"signatures": [ |
|||
"300602010002010001", |
|||
"300602010102010001" |
|||
] |
|||
}, |
|||
"expected": { |
|||
"m": 2, |
|||
"n": 3, |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002", |
|||
"030000000000000000000000000000000000000000000000000000000000000003" |
|||
], |
|||
"signatures": [ |
|||
"300602010002010001", |
|||
"300602010102010001" |
|||
], |
|||
"input": "OP_0 300602010002010001 300602010102010001", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from output/signatures", |
|||
"arguments": { |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", |
|||
"signatures": [ |
|||
"300602010002010001", |
|||
"300602010102010001" |
|||
] |
|||
}, |
|||
"expected": { |
|||
"m": 2, |
|||
"n": 3, |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002", |
|||
"030000000000000000000000000000000000000000000000000000000000000003" |
|||
], |
|||
"signatures": [ |
|||
"300602010002010001", |
|||
"300602010102010001" |
|||
], |
|||
"input": "OP_0 300602010002010001 300602010102010001", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from input/output", |
|||
"arguments": { |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", |
|||
"input": "OP_0 300602010002010001 300602010102010001" |
|||
}, |
|||
"expected": { |
|||
"m": 2, |
|||
"n": 3, |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002", |
|||
"030000000000000000000000000000000000000000000000000000000000000003" |
|||
], |
|||
"signatures": [ |
|||
"300602010002010001", |
|||
"300602010102010001" |
|||
], |
|||
"input": "OP_0 300602010002010001 300602010102010001", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from input/output, even if incomplete", |
|||
"arguments": { |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", |
|||
"input": "OP_0 OP_0 300602010102010001" |
|||
}, |
|||
"options": { |
|||
"allowIncomplete": true |
|||
}, |
|||
"expected": { |
|||
"m": 2, |
|||
"n": 2, |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
], |
|||
"signatures": [ |
|||
0, |
|||
"300602010102010001" |
|||
], |
|||
"input": "OP_0 OP_0 300602010102010001", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from output/signatures, even if incomplete", |
|||
"arguments": { |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", |
|||
"signatures": [ |
|||
0, |
|||
"300602010102010001" |
|||
] |
|||
}, |
|||
"options": { |
|||
"allowIncomplete": true |
|||
}, |
|||
"expected": { |
|||
"m": 2, |
|||
"n": 2, |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
], |
|||
"signatures": [ |
|||
0, |
|||
"300602010102010001" |
|||
], |
|||
"input": "OP_0 OP_0 300602010102010001", |
|||
"witness": [] |
|||
} |
|||
} |
|||
], |
|||
"invalid": [ |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": {} |
|||
}, |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": { |
|||
"m": 2 |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": { |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Non OP_INT chunk (m)", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Non OP_INT chunk (n)", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_1 OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Missing OP_CHECKMULTISIG", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_1 OP_2 OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "m is 0", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_0 OP_2 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "n is 0 (m > n)", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_2 OP_0 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "m > n", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_3 OP_2 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "n !== output pubkeys", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_2 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Non-canonical output public key", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_1 ffff OP_1 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "n mismatch", |
|||
"arguments": { |
|||
"n": 2, |
|||
"output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_1 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "m mismatch", |
|||
"arguments": { |
|||
"m": 2, |
|||
"output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_1 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Pubkeys mismatch", |
|||
"arguments": { |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
], |
|||
"output": "OP_1 030000000000000000000000000000000000000000000000000000000000000002 OP_1 OP_CHECKMULTISIG" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Pubkey count mismatch", |
|||
"arguments": { |
|||
"m": 2, |
|||
"n": 3, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Pubkey count cannot be less than m", |
|||
"arguments": { |
|||
"m": 4, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Not enough signatures provided", |
|||
"arguments": { |
|||
"m": 2, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
], |
|||
"signatures": [ |
|||
"300602010002010001" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Too many signatures provided", |
|||
"arguments": { |
|||
"m": 2, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
], |
|||
"signatures": [ |
|||
"300602010002010001", |
|||
"300602010002010001", |
|||
"300602010002010001" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Missing OP_0", |
|||
"exception": "Input is invalid", |
|||
"arguments": { |
|||
"m": 2, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
], |
|||
"input": "OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input has invalid signature\\(s\\)", |
|||
"arguments": { |
|||
"m": 1, |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
], |
|||
"input": "OP_0 ffffffffffffffff" |
|||
} |
|||
} |
|||
], |
|||
"dynamic": { |
|||
"depends": { |
|||
"m": [ "output" ], |
|||
"n": [ "output", [ "m", "pubkeys" ] ], |
|||
"output": [ "output", [ "m", "pubkeys" ] ], |
|||
"pubkeys": [ "output" ], |
|||
"signatures": [ ["input", "output"] ], |
|||
"input": [ ["signatures", "output"] ], |
|||
"witness": [ ["input", "output"] ] |
|||
}, |
|||
"details": [ |
|||
{ |
|||
"description": "p2ms", |
|||
"m": 2, |
|||
"n": 3, |
|||
"output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", |
|||
"pubkeys": [ |
|||
"030000000000000000000000000000000000000000000000000000000000000001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002", |
|||
"030000000000000000000000000000000000000000000000000000000000000003" |
|||
], |
|||
"signatures": [ |
|||
"300602010002010001", |
|||
"300602010102010001" |
|||
], |
|||
"input": "OP_0 300602010002010001 300602010102010001", |
|||
"witness": [] |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,140 @@ |
|||
{ |
|||
"valid": [ |
|||
{ |
|||
"description": "output from output", |
|||
"arguments": { |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG" |
|||
}, |
|||
"expected": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signatures": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from pubkey", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001" |
|||
}, |
|||
"expected": { |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", |
|||
"signatures": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from output/signature", |
|||
"arguments": { |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", |
|||
"signature": "300602010002010001" |
|||
}, |
|||
"expected": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"input": "300602010002010001", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from pubkey/signature", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001" |
|||
}, |
|||
"expected": { |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", |
|||
"input": "300602010002010001", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from input/output", |
|||
"arguments": { |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", |
|||
"input": "300602010002010001" |
|||
}, |
|||
"expected": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001", |
|||
"witness": [] |
|||
} |
|||
} |
|||
], |
|||
"invalid": [ |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": {} |
|||
}, |
|||
{ |
|||
"description": "Non-canonical signature", |
|||
"exception": "Expected property \"signature\" of type \\?isCanonicalScriptSignature, got Buffer", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "3044" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Unexpected OP_RESERVED", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Non-canonical output public key", |
|||
"exception": "Output pubkey is invalid", |
|||
"arguments": { |
|||
"output": "ffff OP_CHECKSIG" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Unexpected OP_0 (at end)", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG OP_0" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Pubkey mismatch", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000002 OP_CHECKSIG" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Too many chunks", |
|||
"exception": "Input is invalid", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"input": "300602010002010001 OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input has invalid signature", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"input": "ffffffffffffffff" |
|||
} |
|||
} |
|||
], |
|||
"dynamic": { |
|||
"depends": { |
|||
"output": [ "pubkey" ], |
|||
"pubkey": [ "output" ], |
|||
"signature": [ ["input", "output"] ], |
|||
"input": [ ["signature", "output"] ], |
|||
"witness": [ ["input", "output"] ] |
|||
}, |
|||
"details": [ |
|||
{ |
|||
"description": "p2pk", |
|||
"output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001", |
|||
"input": "300602010002010001", |
|||
"witness": [] |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,214 @@ |
|||
{ |
|||
"valid": [ |
|||
{ |
|||
"description": "output from address", |
|||
"arguments": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh" |
|||
}, |
|||
"expected": { |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from hash", |
|||
"arguments": { |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1" |
|||
}, |
|||
"expected": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from output", |
|||
"arguments": { |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG" |
|||
}, |
|||
"expected": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from pubkey", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001" |
|||
}, |
|||
"expected": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from pubkey/signature", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001" |
|||
}, |
|||
"expected": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "input/output from input", |
|||
"arguments": { |
|||
"input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" |
|||
}, |
|||
"expected": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001", |
|||
"witness": [] |
|||
} |
|||
} |
|||
], |
|||
"invalid": [ |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": {} |
|||
}, |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": { |
|||
"signature": "300602010002010001" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Unexpected OP_RESERVED", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Unexpected OP_DUP", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_DUP OP_DUP 168b992bcfc44050310b3a94bd0771136d0b28d137 OP_EQUALVERIFY" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Hash too short (too many chunks)", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_DUP OP_DUP 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_TRUE OP_EQUALVERIFY" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Non-minimally encoded (non BIP62 compliant)", |
|||
"exception": "Expected property \"output\" of type Buffer\\(Length: 25\\), got Buffer\\(Length: 26\\)", |
|||
"arguments": { |
|||
"outputHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Pubkey mismatch", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000002" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input has invalid signature", |
|||
"arguments": { |
|||
"input": "ffffffffffffffffff 030000000000000000000000000000000000000000000000000000000000000001" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input has invalid pubkey", |
|||
"arguments": { |
|||
"input": "300602010002010001 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Input has unexpected data", |
|||
"exception": "Input is invalid", |
|||
"arguments": { |
|||
"input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001 ffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "H(pubkey) != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "address.hash != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "address.hash != output.hash", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"output": "OP_DUP OP_HASH160 ffffffffffffffffffffffffffffffffffffffff OP_EQUALVERIFY OP_CHECKSIG" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output.hash != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "H(input.pubkey) != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff", |
|||
"input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" |
|||
} |
|||
} |
|||
], |
|||
"dynamic": { |
|||
"depends": { |
|||
"address": [ "address", "output", "hash", "pubkey", "input" ], |
|||
"hash": [ "address", "output", "hash", "pubkey", "input" ], |
|||
"output": [ "address", "output", "hash", "pubkey", "input" ], |
|||
"pubkey": [ "input" ], |
|||
"signature": [ "input" ], |
|||
"input": [ [ "pubkey", "signature" ] ], |
|||
"witness": [ "input" ] |
|||
}, |
|||
"details": [ |
|||
{ |
|||
"description": "p2pkh", |
|||
"address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001", |
|||
"input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001", |
|||
"witness": [] |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,346 @@ |
|||
{ |
|||
"valid": [ |
|||
{ |
|||
"description": "p2sh-*, out (from address)", |
|||
"arguments": { |
|||
"address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT" |
|||
}, |
|||
"expected": { |
|||
"hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", |
|||
"output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", |
|||
"redeem": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-*, out (from hash)", |
|||
"arguments": { |
|||
"hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086" |
|||
}, |
|||
"expected": { |
|||
"address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", |
|||
"output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", |
|||
"redeem": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-*, out (from output)", |
|||
"arguments": { |
|||
"output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL" |
|||
}, |
|||
"expected": { |
|||
"address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", |
|||
"hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", |
|||
"redeem": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2pkh, out (from redeem)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"address": "this is P2PKH context, unknown and ignored by P2SH", |
|||
"output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", |
|||
"hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", |
|||
"output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2wpkh, out (from redeem)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"hash": "this is P2WPKH context, unknown and ignored by P2SH", |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", |
|||
"hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", |
|||
"output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2pk, out (from redeem)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", |
|||
"pubkey": "this is P2WPKH context, unknown and ignored by P2SH" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", |
|||
"hash": "3454c084887afe854e80221c69d6282926f809c4", |
|||
"output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2pkh, in and out (from redeem)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", |
|||
"hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", |
|||
"output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac", |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2wpkh, in and out (from redeem w/ witness)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", |
|||
"hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", |
|||
"output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", |
|||
"input": "0014c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2pk, in and out (from input)", |
|||
"arguments": { |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 2103e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058ac" |
|||
}, |
|||
"expected": { |
|||
"address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", |
|||
"hash": "3454c084887afe854e80221c69d6282926f809c4", |
|||
"output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", |
|||
"redeem": { |
|||
"output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"witness": [] |
|||
}, |
|||
"witness": [] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2wpkh, in and out (from input AND witness)", |
|||
"arguments": { |
|||
"input": "0014c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
}, |
|||
"expected": { |
|||
"address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", |
|||
"hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", |
|||
"output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", |
|||
"redeem": { |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"invalid": [ |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": {} |
|||
}, |
|||
{ |
|||
"description": "Non-minimally encoded (non BIP62 compliant)", |
|||
"exception": "Expected property \"output\" of type Buffer\\(Length: 23\\), got Buffer\\(Length: 24\\)", |
|||
"arguments": { |
|||
"outputHex": "a94c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Expected OP_HASH160", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffffff OP_EQUAL" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "Unexpected OP_RESERVED", |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffff OP_EQUAL OP_RESERVED" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "address.hash != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "address.hash != output.hash", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", |
|||
"output": "OP_HASH160 ffffffffffffffffffffffffffffffffffffffff OP_EQUAL" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output.hash != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff", |
|||
"output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "H(redeem.output) != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff", |
|||
"redeem": { |
|||
"output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input too short", |
|||
"arguments": { |
|||
"input": "" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input too short", |
|||
"arguments": { |
|||
"inputHex": "01ff02ff" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input is invalid", |
|||
"arguments": { |
|||
"input": "OP_0 OP_0" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Redeem.input mismatch", |
|||
"arguments": { |
|||
"input": "OP_0 02ffff", |
|||
"redeem": { |
|||
"input": "OP_CHECKSIG", |
|||
"output": "ffff" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Redeem.output mismatch", |
|||
"arguments": { |
|||
"input": "OP_0 02ffff", |
|||
"redeem": { |
|||
"input": "OP_0", |
|||
"output": "fff3" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Redeem.output too short", |
|||
"arguments": { |
|||
"redeem": { |
|||
"input": "OP_0", |
|||
"output": "" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Redeem.output too short", |
|||
"arguments": { |
|||
"inputHex": "021000" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff", |
|||
"redeem": { |
|||
"input": "OP_0", |
|||
"output": "ffff" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Empty input", |
|||
"arguments": { |
|||
"inputHex": "01ff" |
|||
} |
|||
} |
|||
], |
|||
"dynamic": { |
|||
"depends": { |
|||
"address": [ "address", "output", "hash", "redeem.output", [ "input", "witness" ] ], |
|||
"hash": [ "address", "output", "hash", "redeem.output", [ "input", "witness" ] ], |
|||
"output": [ "address", "output", "hash", "redeem.output", [ "input", "witness" ] ], |
|||
"redeem.output": [ [ "input", "witness" ] ], |
|||
"redeem.input": [ [ "input", "witness" ] ], |
|||
"redeem.witness": [ [ "input", "witness" ] ], |
|||
"input": [ "redeem" ], |
|||
"witness": [ "redeem" ] |
|||
}, |
|||
"details": [ |
|||
{ |
|||
"description": "p2sh-p2pkh", |
|||
"address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", |
|||
"hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", |
|||
"output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", |
|||
"redeem": { |
|||
"output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", |
|||
"witness": [] |
|||
}, |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac", |
|||
"witness": [] |
|||
}, |
|||
{ |
|||
"description": "p2sh-p2wpkh", |
|||
"address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", |
|||
"hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", |
|||
"output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", |
|||
"redeem": { |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
}, |
|||
"input": "0014c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,195 @@ |
|||
{ |
|||
"valid": [ |
|||
{ |
|||
"description": "output from address", |
|||
"arguments": { |
|||
"address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d" |
|||
}, |
|||
"expected": { |
|||
"hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", |
|||
"output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from hash", |
|||
"arguments": { |
|||
"hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727" |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", |
|||
"output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from output", |
|||
"arguments": { |
|||
"output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727" |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", |
|||
"hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output from pubkey", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001" |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"signature": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "witness/output from pubkey/signature", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001" |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"input": "", |
|||
"witness": [ |
|||
"300602010002010001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "witness/output from witness", |
|||
"arguments": { |
|||
"witness": [ |
|||
"300602010002010001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
] |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001", |
|||
"input": "" |
|||
} |
|||
} |
|||
], |
|||
"invalid": [ |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": {} |
|||
}, |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": { |
|||
"signature": "300602010002010001" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Output is invalid", |
|||
"description": "Unexpected OP", |
|||
"arguments": { |
|||
"output": "OP_RESERVED ea6d525c0c955d90d3dbd29a81ef8bfb79003727" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Pubkey mismatch", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"witness": [ |
|||
"300602010002010001", |
|||
"030000000000000000000000000000000000000000000000000000000000000002" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffff", |
|||
"witness": [ |
|||
"300602010002010001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input has invalid signature", |
|||
"arguments": { |
|||
"witness": [ |
|||
"ffffffffffffffffff", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Input has invalid pubkey", |
|||
"arguments": { |
|||
"witness": [ |
|||
"300602010002010001", |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
] |
|||
} |
|||
} |
|||
], |
|||
"dynamic": { |
|||
"depends": { |
|||
"address": [ "address", "output", "hash", "pubkey", "witness" ], |
|||
"hash": [ "address", "output", "hash", "pubkey", "witness" ], |
|||
"output": [ "address", "output", "hash", "pubkey", "witness" ], |
|||
"pubkey": [ "witness" ], |
|||
"signature": [ "witness" ], |
|||
"input": [ "witness" ], |
|||
"witness": [ [ "pubkey", "signature" ] ] |
|||
}, |
|||
"details": [ |
|||
{ |
|||
"description": "p2wpkh", |
|||
"address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", |
|||
"hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", |
|||
"pubkey": "030000000000000000000000000000000000000000000000000000000000000001", |
|||
"signature": "300602010002010001", |
|||
"input": "", |
|||
"witness": [ |
|||
"300602010002010001", |
|||
"030000000000000000000000000000000000000000000000000000000000000001" |
|||
] |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,326 @@ |
|||
{ |
|||
"valid": [ |
|||
{ |
|||
"description": "p2wsh-*, out (from address)", |
|||
"arguments": { |
|||
"address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q" |
|||
}, |
|||
"expected": { |
|||
"hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"redeem": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-*, out (from hash)", |
|||
"arguments": { |
|||
"hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" |
|||
}, |
|||
"expected": { |
|||
"address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", |
|||
"output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"redeem": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-*, out (from output)", |
|||
"arguments": { |
|||
"output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" |
|||
}, |
|||
"expected": { |
|||
"address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", |
|||
"hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"redeem": null, |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2pkh, out (from redeem)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"address": "this is P2PKH context, unknown and ignored by p2wsh", |
|||
"output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", |
|||
"hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", |
|||
"output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2wpkh, out (from redeem)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"hash": "this is P2WPKH context, unknown and ignored by p2wsh", |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", |
|||
"hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2pk, out (from redeem)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", |
|||
"pubkey": "this is P2WPKH context, unknown and ignored by p2wsh" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", |
|||
"hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"input": null, |
|||
"witness": null |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2pkh, in and out (from redeem, transformed to witness)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", |
|||
"hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", |
|||
"output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", |
|||
"redeem": { |
|||
"input": "" |
|||
}, |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", |
|||
"76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2wpkh, in and out (from redeem w/ witness)", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
} |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", |
|||
"hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", |
|||
"0014c30afa58ae0673b00a45b5c17dff4633780f1400" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2pk, in and out (from witness)", |
|||
"arguments": { |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"2103e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058ac" |
|||
] |
|||
}, |
|||
"expected": { |
|||
"address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", |
|||
"hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", |
|||
"redeem": { |
|||
"output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501" |
|||
] |
|||
}, |
|||
"input": "" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2wpkh, in and out (from witness)", |
|||
"arguments": { |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", |
|||
"0014c30afa58ae0673b00a45b5c17dff4633780f1400" |
|||
] |
|||
}, |
|||
"expected": { |
|||
"address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", |
|||
"hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"redeem": { |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"invalid": [ |
|||
{ |
|||
"exception": "Not enough data", |
|||
"arguments": {} |
|||
}, |
|||
{ |
|||
"description": "address.hash != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "address.hash != output.hash", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", |
|||
"output": "OP_0 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "output.hash != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
|||
"output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" |
|||
} |
|||
}, |
|||
{ |
|||
"description": "H(redeem.output) != H", |
|||
"exception": "Hash mismatch", |
|||
"arguments": { |
|||
"hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
|||
"redeem": { |
|||
"output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Output is invalid", |
|||
"arguments": { |
|||
"output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff OP_EQUAL" |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Redeem.output is invalid", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Non push-only scriptSig", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "OP_TRUE", |
|||
"input": "OP_HASH256" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Witness and redeem.output mismatch", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "OP_TRUE", |
|||
"input": "OP_0" |
|||
}, |
|||
"witness": [ |
|||
"02ffff" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"exception": "Witness and redeem.witness mismatch", |
|||
"arguments": { |
|||
"redeem": { |
|||
"output": "OP_TRUE", |
|||
"witness": [ |
|||
"01" |
|||
] |
|||
}, |
|||
"witness": [ |
|||
"00", |
|||
"02ffff" |
|||
] |
|||
} |
|||
} |
|||
], |
|||
"dynamic": { |
|||
"depends": { |
|||
"address": [ "address", "output", "hash", "redeem.output", "witness" ], |
|||
"hash": [ "address", "output", "hash", "redeem.output", "witness" ], |
|||
"output": [ "address", "output", "hash", "redeem.output", "witness" ], |
|||
"redeem.output": [ "witness" ], |
|||
"redeem.input": [ [ "input", "witness" ], "witness" ], |
|||
"input": [ "witness" ], |
|||
"witness": [ "redeem" ] |
|||
}, |
|||
"details": [ |
|||
{ |
|||
"description": "p2wsh-p2pkh", |
|||
"disabled": [ |
|||
"redeem.input" |
|||
], |
|||
"address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", |
|||
"hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", |
|||
"output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", |
|||
"redeem": { |
|||
"output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", |
|||
"input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", |
|||
"witness": null |
|||
}, |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", |
|||
"76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac" |
|||
] |
|||
}, |
|||
{ |
|||
"description": "p2wsh-p2wpkh", |
|||
"address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", |
|||
"hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", |
|||
"redeem": { |
|||
"output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" |
|||
] |
|||
}, |
|||
"input": "", |
|||
"witness": [ |
|||
"3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", |
|||
"03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", |
|||
"0014c30afa58ae0673b00a45b5c17dff4633780f1400" |
|||
] |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,65 @@ |
|||
/* global describe, it */ |
|||
|
|||
const assert = require('assert') |
|||
const u = require('./payments.utils') |
|||
|
|||
;['p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { |
|||
describe(p, function () { |
|||
const fn = require('../src/payments/' + p) |
|||
const fixtures = require('./fixtures/' + p) |
|||
|
|||
fixtures.valid.forEach(function (f, i) { |
|||
const args = u.preform(f.arguments) |
|||
|
|||
it(f.description + ' as expected', function () { |
|||
const actual = fn(args, f.options) |
|||
u.equate(actual, f.expected, f.arguments) |
|||
}) |
|||
|
|||
it(f.description + ' as expected (no validation)', function () { |
|||
const actual = fn(args, Object.assign({}, f.options, { |
|||
validate: false |
|||
})) |
|||
|
|||
u.equate(actual, f.expected, f.arguments) |
|||
}) |
|||
}) |
|||
|
|||
fixtures.invalid.forEach(function (f) { |
|||
it('throws ' + f.exception + (f.description ? ('for ' + f.description) : ''), function () { |
|||
const args = u.preform(f.arguments) |
|||
|
|||
assert.throws(function () { |
|||
fn(args, f.options) |
|||
}, new RegExp(f.exception)) |
|||
}) |
|||
}) |
|||
|
|||
// cross-verify dynamically too
|
|||
if (!fixtures.dynamic) return |
|||
const { depends, details } = fixtures.dynamic |
|||
|
|||
details.forEach(function (f) { |
|||
const detail = u.preform(f) |
|||
const disabled = {} |
|||
if (f.disabled) f.disabled.forEach(function (k) { disabled[k] = true }) |
|||
|
|||
for (let key in depends) { |
|||
if (key in disabled) continue |
|||
const dependencies = depends[key] |
|||
|
|||
dependencies.forEach(function (dependency) { |
|||
if (!Array.isArray(dependency)) dependency = [dependency] |
|||
|
|||
const args = {} |
|||
dependency.forEach(function (d) { u.from(d, detail, args) }) |
|||
const expected = u.from(key, detail) |
|||
|
|||
it(f.description + ', ' + key + ' derives from ' + JSON.stringify(dependency), function () { |
|||
u.equate(fn(args), expected) |
|||
}) |
|||
}) |
|||
} |
|||
}) |
|||
}) |
|||
}) |
@ -0,0 +1,128 @@ |
|||
let t = require('assert') |
|||
let bscript = require('../src/script') |
|||
let bnetworks = require('../src/networks') |
|||
|
|||
function tryHex (x) { |
|||
if (Buffer.isBuffer(x)) return x.toString('hex') |
|||
if (Array.isArray(x)) return x.map(tryHex) |
|||
return x |
|||
} |
|||
function tryASM (x) { |
|||
if (Buffer.isBuffer(x)) return bscript.toASM(x) |
|||
return x |
|||
} |
|||
function asmToBuffer (x) { |
|||
if (x === '') return Buffer.alloc(0) |
|||
return bscript.fromASM(x) |
|||
} |
|||
function carryOver (a, b) { |
|||
for (let k in b) { |
|||
if (k in a && k === 'redeem') { |
|||
carryOver(a[k], b[k]) |
|||
continue |
|||
} |
|||
|
|||
// don't, the value was specified
|
|||
if (k in a) continue |
|||
|
|||
// otherwise, expect match
|
|||
a[k] = b[k] |
|||
} |
|||
} |
|||
|
|||
function equateBase (a, b, context) { |
|||
if ('output' in b) t.strictEqual(tryASM(a.output), tryASM(b.output), `Inequal ${context}output`) |
|||
if ('input' in b) t.strictEqual(tryASM(a.input), tryASM(b.input), `Inequal ${context}input`) |
|||
if ('witness' in b) t.deepEqual(tryHex(a.witness), tryHex(b.witness), `Inequal ${context}witness`) |
|||
} |
|||
|
|||
function equate (a, b, args) { |
|||
b = Object.assign({}, b) |
|||
carryOver(b, args) |
|||
|
|||
// by null, we mean 'undefined', but JSON
|
|||
if (b.input === null) b.input = undefined |
|||
if (b.output === null) b.output = undefined |
|||
if (b.witness === null) b.witness = undefined |
|||
if (b.redeem) { |
|||
if (b.redeem.input === null) b.redeem.input = undefined |
|||
if (b.redeem.output === null) b.redeem.output = undefined |
|||
if (b.redeem.witness === null) b.redeem.witness = undefined |
|||
} |
|||
|
|||
equateBase(a, b, '') |
|||
if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.') |
|||
if (b.network) t.deepEqual(a.network, b.network, 'Inequal *.network') |
|||
|
|||
// contextual
|
|||
if (b.signature === null) b.signature = undefined |
|||
if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address') |
|||
if ('hash' in b) t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash') |
|||
if ('pubkey' in b) t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey') |
|||
if ('signature' in b) t.strictEqual(tryHex(a.signature), tryHex(b.signature), 'Inequal signature') |
|||
if ('m' in b) t.strictEqual(a.m, b.m, 'Inequal *.m') |
|||
if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n') |
|||
if ('pubkeys' in b) t.deepEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') |
|||
if ('signatures' in b) t.deepEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') |
|||
} |
|||
|
|||
function preform (x) { |
|||
x = Object.assign({}, x) |
|||
|
|||
if (x.network) x.network = bnetworks[x.network] |
|||
if (typeof x.inputHex === 'string') { |
|||
x.input = Buffer.from(x.inputHex, 'hex') |
|||
delete x.inputHex |
|||
} |
|||
if (typeof x.outputHex === 'string') { |
|||
x.output = Buffer.from(x.outputHex, 'hex') |
|||
delete x.outputHex |
|||
} |
|||
if (typeof x.output === 'string') x.output = asmToBuffer(x.output) |
|||
if (typeof x.input === 'string') x.input = asmToBuffer(x.input) |
|||
if (Array.isArray(x.witness)) { |
|||
x.witness = x.witness.map(function (y) { |
|||
return Buffer.from(y, 'hex') |
|||
}) |
|||
} |
|||
|
|||
if (x.hash) x.hash = Buffer.from(x.hash, 'hex') |
|||
if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex') |
|||
if (x.signature) x.signature = Buffer.from(x.signature, 'hex') |
|||
if (x.pubkeys) x.pubkeys = x.pubkeys.map(function (y) { return Buffer.from(y, 'hex') }) |
|||
if (x.signatures) x.signatures = x.signatures.map(function (y) { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) |
|||
if (x.redeem) { |
|||
if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) |
|||
if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) |
|||
if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(function (y) { return Buffer.from(y, 'hex') }) |
|||
x.redeem.network = bnetworks[x.redeem.network] || x.network || bnetworks.bitcoin |
|||
} |
|||
|
|||
return x |
|||
} |
|||
|
|||
function from (path, object, result) { |
|||
path = path.split('.') |
|||
result = result || {} |
|||
|
|||
let r = result |
|||
path.forEach((k, i) => { |
|||
if (i < path.length - 1) { |
|||
r[k] = r[k] || {} |
|||
|
|||
// recurse
|
|||
r = r[k] |
|||
object = object[k] |
|||
} else { |
|||
r[k] = object[k] |
|||
} |
|||
}) |
|||
|
|||
return result |
|||
} |
|||
|
|||
module.exports = { |
|||
from, |
|||
equate, |
|||
preform |
|||
} |
Loading…
Reference in new issue