Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
2,693 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.