Jonathan Underwood
6 years ago
committed by
GitHub
149 changed files with 8950 additions and 6986 deletions
@ -0,0 +1,4 @@ |
|||
{ |
|||
"singleQuote": true, |
|||
"trailingComma": "all" |
|||
} |
File diff suppressed because it is too large
@ -1,97 +1,100 @@ |
|||
const Buffer = require('safe-buffer').Buffer |
|||
const bech32 = require('bech32') |
|||
const bs58check = require('bs58check') |
|||
const bscript = require('./script') |
|||
const networks = require('./networks') |
|||
const typeforce = require('typeforce') |
|||
const types = require('./types') |
|||
const payments = require('./payments') |
|||
|
|||
function fromBase58Check (address) { |
|||
const payload = bs58check.decode(address) |
|||
|
|||
// TODO: 4.0.0, move to "toOutputScript"
|
|||
if (payload.length < 21) throw new TypeError(address + ' is too short') |
|||
if (payload.length > 21) throw new TypeError(address + ' is too long') |
|||
|
|||
const version = payload.readUInt8(0) |
|||
const hash = payload.slice(1) |
|||
|
|||
return { version: version, hash: hash } |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const networks = require("./networks"); |
|||
const payments = require("./payments"); |
|||
const bscript = require("./script"); |
|||
const types = require("./types"); |
|||
const bech32 = require('bech32'); |
|||
const bs58check = require('bs58check'); |
|||
const typeforce = require('typeforce'); |
|||
function fromBase58Check(address) { |
|||
const payload = bs58check.decode(address); |
|||
// TODO: 4.0.0, move to "toOutputScript"
|
|||
if (payload.length < 21) |
|||
throw new TypeError(address + ' is too short'); |
|||
if (payload.length > 21) |
|||
throw new TypeError(address + ' is too long'); |
|||
const version = payload.readUInt8(0); |
|||
const hash = payload.slice(1); |
|||
return { version, hash }; |
|||
} |
|||
|
|||
function fromBech32 (address) { |
|||
const result = bech32.decode(address) |
|||
const data = bech32.fromWords(result.words.slice(1)) |
|||
|
|||
return { |
|||
version: result.words[0], |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data) |
|||
} |
|||
exports.fromBase58Check = fromBase58Check; |
|||
function fromBech32(address) { |
|||
const result = bech32.decode(address); |
|||
const data = bech32.fromWords(result.words.slice(1)); |
|||
return { |
|||
version: result.words[0], |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data), |
|||
}; |
|||
} |
|||
|
|||
function toBase58Check (hash, version) { |
|||
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments) |
|||
|
|||
const payload = Buffer.allocUnsafe(21) |
|||
payload.writeUInt8(version, 0) |
|||
hash.copy(payload, 1) |
|||
|
|||
return bs58check.encode(payload) |
|||
exports.fromBech32 = fromBech32; |
|||
function toBase58Check(hash, version) { |
|||
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); |
|||
const payload = Buffer.allocUnsafe(21); |
|||
payload.writeUInt8(version, 0); |
|||
hash.copy(payload, 1); |
|||
return bs58check.encode(payload); |
|||
} |
|||
|
|||
function toBech32 (data, version, prefix) { |
|||
const words = bech32.toWords(data) |
|||
words.unshift(version) |
|||
|
|||
return bech32.encode(prefix, words) |
|||
exports.toBase58Check = toBase58Check; |
|||
function toBech32(data, version, prefix) { |
|||
const words = bech32.toWords(data); |
|||
words.unshift(version); |
|||
return bech32.encode(prefix, words); |
|||
} |
|||
|
|||
function fromOutputScript (output, network) { |
|||
network = network || networks.bitcoin |
|||
|
|||
try { return payments.p2pkh({ output, network }).address } catch (e) {} |
|||
try { return payments.p2sh({ output, network }).address } catch (e) {} |
|||
try { return payments.p2wpkh({ output, network }).address } catch (e) {} |
|||
try { return payments.p2wsh({ output, network }).address } catch (e) {} |
|||
|
|||
throw new Error(bscript.toASM(output) + ' has no matching Address') |
|||
} |
|||
|
|||
function toOutputScript (address, network) { |
|||
network = network || networks.bitcoin |
|||
|
|||
let decode |
|||
try { |
|||
decode = fromBase58Check(address) |
|||
} catch (e) {} |
|||
|
|||
if (decode) { |
|||
if (decode.version === network.pubKeyHash) return payments.p2pkh({ hash: decode.hash }).output |
|||
if (decode.version === network.scriptHash) return payments.p2sh({ hash: decode.hash }).output |
|||
} else { |
|||
exports.toBech32 = toBech32; |
|||
function fromOutputScript(output, network) { |
|||
// TODO: Network
|
|||
network = network || networks.bitcoin; |
|||
try { |
|||
return payments.p2pkh({ output, network }).address; |
|||
} |
|||
catch (e) { } |
|||
try { |
|||
decode = fromBech32(address) |
|||
} catch (e) {} |
|||
|
|||
if (decode) { |
|||
if (decode.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix') |
|||
if (decode.version === 0) { |
|||
if (decode.data.length === 20) return payments.p2wpkh({ hash: decode.data }).output |
|||
if (decode.data.length === 32) return payments.p2wsh({ hash: decode.data }).output |
|||
} |
|||
return payments.p2sh({ output, network }).address; |
|||
} |
|||
} |
|||
|
|||
throw new Error(address + ' has no matching Script') |
|||
catch (e) { } |
|||
try { |
|||
return payments.p2wpkh({ output, network }).address; |
|||
} |
|||
catch (e) { } |
|||
try { |
|||
return payments.p2wsh({ output, network }).address; |
|||
} |
|||
catch (e) { } |
|||
throw new Error(bscript.toASM(output) + ' has no matching Address'); |
|||
} |
|||
|
|||
module.exports = { |
|||
fromBase58Check: fromBase58Check, |
|||
fromBech32: fromBech32, |
|||
fromOutputScript: fromOutputScript, |
|||
toBase58Check: toBase58Check, |
|||
toBech32: toBech32, |
|||
toOutputScript: toOutputScript |
|||
exports.fromOutputScript = fromOutputScript; |
|||
function toOutputScript(address, network) { |
|||
network = network || networks.bitcoin; |
|||
let decodeBase58; |
|||
let decodeBech32; |
|||
try { |
|||
decodeBase58 = fromBase58Check(address); |
|||
} |
|||
catch (e) { } |
|||
if (decodeBase58) { |
|||
if (decodeBase58.version === network.pubKeyHash) |
|||
return payments.p2pkh({ hash: decodeBase58.hash }).output; |
|||
if (decodeBase58.version === network.scriptHash) |
|||
return payments.p2sh({ hash: decodeBase58.hash }).output; |
|||
} |
|||
else { |
|||
try { |
|||
decodeBech32 = fromBech32(address); |
|||
} |
|||
catch (e) { } |
|||
if (decodeBech32) { |
|||
if (decodeBech32.prefix !== network.bech32) |
|||
throw new Error(address + ' has an invalid prefix'); |
|||
if (decodeBech32.version === 0) { |
|||
if (decodeBech32.data.length === 20) |
|||
return payments.p2wpkh({ hash: decodeBech32.data }).output; |
|||
if (decodeBech32.data.length === 32) |
|||
return payments.p2wsh({ hash: decodeBech32.data }).output; |
|||
} |
|||
} |
|||
} |
|||
throw new Error(address + ' has no matching Script'); |
|||
} |
|||
exports.toOutputScript = toOutputScript; |
|||
|
@ -1,29 +1,42 @@ |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
// https://github.com/feross/buffer/blob/master/index.js#L1127
|
|||
function verifuint (value, max) { |
|||
if (typeof value !== 'number') throw new Error('cannot write a non-number as a number') |
|||
if (value < 0) throw new Error('specified a negative value for writing an unsigned value') |
|||
if (value > max) throw new Error('RangeError: value out of range') |
|||
if (Math.floor(value) !== value) throw new Error('value has a fractional component') |
|||
function verifuint(value, max) { |
|||
if (typeof value !== 'number') |
|||
throw new Error('cannot write a non-number as a number'); |
|||
if (value < 0) |
|||
throw new Error('specified a negative value for writing an unsigned value'); |
|||
if (value > max) |
|||
throw new Error('RangeError: value out of range'); |
|||
if (Math.floor(value) !== value) |
|||
throw new Error('value has a fractional component'); |
|||
} |
|||
|
|||
function readUInt64LE (buffer, offset) { |
|||
const a = buffer.readUInt32LE(offset) |
|||
let b = buffer.readUInt32LE(offset + 4) |
|||
b *= 0x100000000 |
|||
|
|||
verifuint(b + a, 0x001fffffffffffff) |
|||
return b + a |
|||
function readUInt64LE(buffer, offset) { |
|||
const a = buffer.readUInt32LE(offset); |
|||
let b = buffer.readUInt32LE(offset + 4); |
|||
b *= 0x100000000; |
|||
verifuint(b + a, 0x001fffffffffffff); |
|||
return b + a; |
|||
} |
|||
|
|||
function writeUInt64LE (buffer, value, offset) { |
|||
verifuint(value, 0x001fffffffffffff) |
|||
|
|||
buffer.writeInt32LE(value & -1, offset) |
|||
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4) |
|||
return offset + 8 |
|||
exports.readUInt64LE = readUInt64LE; |
|||
function writeUInt64LE(buffer, value, offset) { |
|||
verifuint(value, 0x001fffffffffffff); |
|||
buffer.writeInt32LE(value & -1, offset); |
|||
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); |
|||
return offset + 8; |
|||
} |
|||
|
|||
module.exports = { |
|||
readUInt64LE: readUInt64LE, |
|||
writeUInt64LE: writeUInt64LE |
|||
exports.writeUInt64LE = writeUInt64LE; |
|||
function reverseBuffer(buffer) { |
|||
if (buffer.length < 1) |
|||
return buffer; |
|||
let j = buffer.length - 1; |
|||
let tmp = 0; |
|||
for (let i = 0; i < buffer.length / 2; i++) { |
|||
tmp = buffer[i]; |
|||
buffer[i] = buffer[j]; |
|||
buffer[j] = tmp; |
|||
j--; |
|||
} |
|||
return buffer; |
|||
} |
|||
exports.reverseBuffer = reverseBuffer; |
|||
|
@ -1,70 +1,75 @@ |
|||
const decompile = require('./script').decompile |
|||
const multisig = require('./templates/multisig') |
|||
const nullData = require('./templates/nulldata') |
|||
const pubKey = require('./templates/pubkey') |
|||
const pubKeyHash = require('./templates/pubkeyhash') |
|||
const scriptHash = require('./templates/scripthash') |
|||
const witnessPubKeyHash = require('./templates/witnesspubkeyhash') |
|||
const witnessScriptHash = require('./templates/witnessscripthash') |
|||
const witnessCommitment = require('./templates/witnesscommitment') |
|||
|
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const script_1 = require("./script"); |
|||
const multisig = require("./templates/multisig"); |
|||
const nullData = require("./templates/nulldata"); |
|||
const pubKey = require("./templates/pubkey"); |
|||
const pubKeyHash = require("./templates/pubkeyhash"); |
|||
const scriptHash = require("./templates/scripthash"); |
|||
const witnessCommitment = require("./templates/witnesscommitment"); |
|||
const witnessPubKeyHash = require("./templates/witnesspubkeyhash"); |
|||
const witnessScriptHash = require("./templates/witnessscripthash"); |
|||
const types = { |
|||
P2MS: 'multisig', |
|||
NONSTANDARD: 'nonstandard', |
|||
NULLDATA: 'nulldata', |
|||
P2PK: 'pubkey', |
|||
P2PKH: 'pubkeyhash', |
|||
P2SH: 'scripthash', |
|||
P2WPKH: 'witnesspubkeyhash', |
|||
P2WSH: 'witnessscripthash', |
|||
WITNESS_COMMITMENT: 'witnesscommitment' |
|||
P2MS: 'multisig', |
|||
NONSTANDARD: 'nonstandard', |
|||
NULLDATA: 'nulldata', |
|||
P2PK: 'pubkey', |
|||
P2PKH: 'pubkeyhash', |
|||
P2SH: 'scripthash', |
|||
P2WPKH: 'witnesspubkeyhash', |
|||
P2WSH: 'witnessscripthash', |
|||
WITNESS_COMMITMENT: 'witnesscommitment', |
|||
}; |
|||
exports.types = types; |
|||
function classifyOutput(script) { |
|||
if (witnessPubKeyHash.output.check(script)) |
|||
return types.P2WPKH; |
|||
if (witnessScriptHash.output.check(script)) |
|||
return types.P2WSH; |
|||
if (pubKeyHash.output.check(script)) |
|||
return types.P2PKH; |
|||
if (scriptHash.output.check(script)) |
|||
return types.P2SH; |
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = script_1.decompile(script); |
|||
if (!chunks) |
|||
throw new TypeError('Invalid script'); |
|||
if (multisig.output.check(chunks)) |
|||
return types.P2MS; |
|||
if (pubKey.output.check(chunks)) |
|||
return types.P2PK; |
|||
if (witnessCommitment.output.check(chunks)) |
|||
return types.WITNESS_COMMITMENT; |
|||
if (nullData.output.check(chunks)) |
|||
return types.NULLDATA; |
|||
return types.NONSTANDARD; |
|||
} |
|||
|
|||
function classifyOutput (script) { |
|||
if (witnessPubKeyHash.output.check(script)) return types.P2WPKH |
|||
if (witnessScriptHash.output.check(script)) return types.P2WSH |
|||
if (pubKeyHash.output.check(script)) return types.P2PKH |
|||
if (scriptHash.output.check(script)) return types.P2SH |
|||
|
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = decompile(script) |
|||
if (!chunks) throw new TypeError('Invalid script') |
|||
|
|||
if (multisig.output.check(chunks)) return types.P2MS |
|||
if (pubKey.output.check(chunks)) return types.P2PK |
|||
if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT |
|||
if (nullData.output.check(chunks)) return types.NULLDATA |
|||
|
|||
return types.NONSTANDARD |
|||
exports.output = classifyOutput; |
|||
function classifyInput(script, allowIncomplete) { |
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = script_1.decompile(script); |
|||
if (!chunks) |
|||
throw new TypeError('Invalid script'); |
|||
if (pubKeyHash.input.check(chunks)) |
|||
return types.P2PKH; |
|||
if (scriptHash.input.check(chunks, allowIncomplete)) |
|||
return types.P2SH; |
|||
if (multisig.input.check(chunks, allowIncomplete)) |
|||
return types.P2MS; |
|||
if (pubKey.input.check(chunks)) |
|||
return types.P2PK; |
|||
return types.NONSTANDARD; |
|||
} |
|||
|
|||
function classifyInput (script, allowIncomplete) { |
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = decompile(script) |
|||
if (!chunks) throw new TypeError('Invalid script') |
|||
|
|||
if (pubKeyHash.input.check(chunks)) return types.P2PKH |
|||
if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH |
|||
if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS |
|||
if (pubKey.input.check(chunks)) return types.P2PK |
|||
|
|||
return types.NONSTANDARD |
|||
} |
|||
|
|||
function classifyWitness (script, allowIncomplete) { |
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = decompile(script) |
|||
if (!chunks) throw new TypeError('Invalid script') |
|||
|
|||
if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH |
|||
if (witnessScriptHash.input.check(chunks, allowIncomplete)) return types.P2WSH |
|||
|
|||
return types.NONSTANDARD |
|||
} |
|||
|
|||
module.exports = { |
|||
input: classifyInput, |
|||
output: classifyOutput, |
|||
witness: classifyWitness, |
|||
types: types |
|||
exports.input = classifyInput; |
|||
function classifyWitness(script, allowIncomplete) { |
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = script_1.decompile(script); |
|||
if (!chunks) |
|||
throw new TypeError('Invalid script'); |
|||
if (witnessPubKeyHash.input.check(chunks)) |
|||
return types.P2WPKH; |
|||
if (witnessScriptHash.input.check(chunks, allowIncomplete)) |
|||
return types.P2WSH; |
|||
return types.NONSTANDARD; |
|||
} |
|||
exports.witness = classifyWitness; |
|||
|
@ -1,29 +1,36 @@ |
|||
const createHash = require('create-hash') |
|||
|
|||
function ripemd160 (buffer) { |
|||
return createHash('rmd160').update(buffer).digest() |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const createHash = require('create-hash'); |
|||
function ripemd160(buffer) { |
|||
try { |
|||
return createHash('rmd160') |
|||
.update(buffer) |
|||
.digest(); |
|||
} |
|||
catch (err) { |
|||
return createHash('ripemd160') |
|||
.update(buffer) |
|||
.digest(); |
|||
} |
|||
} |
|||
|
|||
function sha1 (buffer) { |
|||
return createHash('sha1').update(buffer).digest() |
|||
exports.ripemd160 = ripemd160; |
|||
function sha1(buffer) { |
|||
return createHash('sha1') |
|||
.update(buffer) |
|||
.digest(); |
|||
} |
|||
|
|||
function sha256 (buffer) { |
|||
return createHash('sha256').update(buffer).digest() |
|||
exports.sha1 = sha1; |
|||
function sha256(buffer) { |
|||
return createHash('sha256') |
|||
.update(buffer) |
|||
.digest(); |
|||
} |
|||
|
|||
function hash160 (buffer) { |
|||
return ripemd160(sha256(buffer)) |
|||
exports.sha256 = sha256; |
|||
function hash160(buffer) { |
|||
return ripemd160(sha256(buffer)); |
|||
} |
|||
|
|||
function hash256 (buffer) { |
|||
return sha256(sha256(buffer)) |
|||
} |
|||
|
|||
module.exports = { |
|||
hash160: hash160, |
|||
hash256: hash256, |
|||
ripemd160: ripemd160, |
|||
sha1: sha1, |
|||
sha256: sha256 |
|||
exports.hash160 = hash160; |
|||
function hash256(buffer) { |
|||
return sha256(sha256(buffer)); |
|||
} |
|||
exports.hash256 = hash256; |
|||
|
@ -1,106 +1,98 @@ |
|||
const ecc = require('tiny-secp256k1') |
|||
const randomBytes = require('randombytes') |
|||
const typeforce = require('typeforce') |
|||
const types = require('./types') |
|||
const wif = require('wif') |
|||
|
|||
const NETWORKS = require('./networks') |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const NETWORKS = require("./networks"); |
|||
const types = require("./types"); |
|||
const ecc = require('tiny-secp256k1'); |
|||
const randomBytes = require('randombytes'); |
|||
const typeforce = require('typeforce'); |
|||
const wif = require('wif'); |
|||
const isOptions = typeforce.maybe(typeforce.compile({ |
|||
compressed: types.maybe(types.Boolean), |
|||
network: types.maybe(types.Network) |
|||
})) |
|||
|
|||
function ECPair (d, Q, options) { |
|||
options = options || {} |
|||
|
|||
this.compressed = options.compressed === undefined ? true : options.compressed |
|||
this.network = options.network || NETWORKS.bitcoin |
|||
|
|||
this.__d = d || null |
|||
this.__Q = null |
|||
if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) |
|||
compressed: types.maybe(types.Boolean), |
|||
network: types.maybe(types.Network), |
|||
})); |
|||
class ECPair { |
|||
constructor(__D, __Q, options) { |
|||
this.__D = __D; |
|||
this.__Q = __Q; |
|||
if (options === undefined) |
|||
options = {}; |
|||
this.compressed = |
|||
options.compressed === undefined ? true : options.compressed; |
|||
this.network = options.network || NETWORKS.bitcoin; |
|||
if (__Q !== undefined) |
|||
this.__Q = ecc.pointCompress(__Q, this.compressed); |
|||
} |
|||
get privateKey() { |
|||
return this.__D; |
|||
} |
|||
get publicKey() { |
|||
if (!this.__Q) |
|||
this.__Q = ecc.pointFromScalar(this.__D, this.compressed); |
|||
return this.__Q; |
|||
} |
|||
toWIF() { |
|||
if (!this.__D) |
|||
throw new Error('Missing private key'); |
|||
return wif.encode(this.network.wif, this.__D, this.compressed); |
|||
} |
|||
sign(hash) { |
|||
if (!this.__D) |
|||
throw new Error('Missing private key'); |
|||
return ecc.sign(hash, this.__D); |
|||
} |
|||
verify(hash, signature) { |
|||
return ecc.verify(hash, this.publicKey, signature); |
|||
} |
|||
} |
|||
|
|||
Object.defineProperty(ECPair.prototype, 'privateKey', { |
|||
enumerable: false, |
|||
get: function () { return this.__d } |
|||
}) |
|||
|
|||
Object.defineProperty(ECPair.prototype, 'publicKey', { get: function () { |
|||
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) |
|||
return this.__Q |
|||
}}) |
|||
|
|||
ECPair.prototype.toWIF = function () { |
|||
if (!this.__d) throw new Error('Missing private key') |
|||
return wif.encode(this.network.wif, this.__d, this.compressed) |
|||
function fromPrivateKey(buffer, options) { |
|||
typeforce(types.Buffer256bit, buffer); |
|||
if (!ecc.isPrivate(buffer)) |
|||
throw new TypeError('Private key not in range [1, n)'); |
|||
typeforce(isOptions, options); |
|||
return new ECPair(buffer, undefined, options); |
|||
} |
|||
|
|||
ECPair.prototype.sign = function (hash) { |
|||
if (!this.__d) throw new Error('Missing private key') |
|||
return ecc.sign(hash, this.__d) |
|||
exports.fromPrivateKey = fromPrivateKey; |
|||
function fromPublicKey(buffer, options) { |
|||
typeforce(ecc.isPoint, buffer); |
|||
typeforce(isOptions, options); |
|||
return new ECPair(undefined, buffer, options); |
|||
} |
|||
|
|||
ECPair.prototype.verify = function (hash, signature) { |
|||
return ecc.verify(hash, this.publicKey, signature) |
|||
exports.fromPublicKey = fromPublicKey; |
|||
function fromWIF(wifString, network) { |
|||
const decoded = wif.decode(wifString); |
|||
const version = decoded.version; |
|||
// list of networks?
|
|||
if (types.Array(network)) { |
|||
network = network |
|||
.filter((x) => { |
|||
return version === x.wif; |
|||
}) |
|||
.pop(); |
|||
if (!network) |
|||
throw new Error('Unknown network version'); |
|||
// otherwise, assume a network object (or default to bitcoin)
|
|||
} |
|||
else { |
|||
network = network || NETWORKS.bitcoin; |
|||
if (version !== network.wif) |
|||
throw new Error('Invalid network version'); |
|||
} |
|||
return fromPrivateKey(decoded.privateKey, { |
|||
compressed: decoded.compressed, |
|||
network: network, |
|||
}); |
|||
} |
|||
|
|||
function fromPrivateKey (buffer, options) { |
|||
typeforce(types.Buffer256bit, buffer) |
|||
if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)') |
|||
typeforce(isOptions, options) |
|||
|
|||
return new ECPair(buffer, null, options) |
|||
} |
|||
|
|||
function fromPublicKey (buffer, options) { |
|||
typeforce(ecc.isPoint, buffer) |
|||
typeforce(isOptions, options) |
|||
return new ECPair(null, buffer, options) |
|||
} |
|||
|
|||
function fromWIF (string, network) { |
|||
const decoded = wif.decode(string) |
|||
const version = decoded.version |
|||
|
|||
// list of networks?
|
|||
if (types.Array(network)) { |
|||
network = network.filter(function (x) { |
|||
return version === x.wif |
|||
}).pop() |
|||
|
|||
if (!network) throw new Error('Unknown network version') |
|||
|
|||
// otherwise, assume a network object (or default to bitcoin)
|
|||
} else { |
|||
network = network || NETWORKS.bitcoin |
|||
|
|||
if (version !== network.wif) throw new Error('Invalid network version') |
|||
} |
|||
|
|||
return fromPrivateKey(decoded.privateKey, { |
|||
compressed: decoded.compressed, |
|||
network: network |
|||
}) |
|||
} |
|||
|
|||
function makeRandom (options) { |
|||
typeforce(isOptions, options) |
|||
options = options || {} |
|||
const rng = options.rng || randomBytes |
|||
|
|||
let d |
|||
do { |
|||
d = rng(32) |
|||
typeforce(types.Buffer256bit, d) |
|||
} while (!ecc.isPrivate(d)) |
|||
|
|||
return fromPrivateKey(d, options) |
|||
} |
|||
|
|||
module.exports = { |
|||
makeRandom, |
|||
fromPrivateKey, |
|||
fromPublicKey, |
|||
fromWIF |
|||
exports.fromWIF = fromWIF; |
|||
function makeRandom(options) { |
|||
typeforce(isOptions, options); |
|||
if (options === undefined) |
|||
options = {}; |
|||
const rng = options.rng || randomBytes; |
|||
let d; |
|||
do { |
|||
d = rng(32); |
|||
typeforce(types.Buffer256bit, d); |
|||
} while (!ecc.isPrivate(d)); |
|||
return fromPrivateKey(d, options); |
|||
} |
|||
exports.makeRandom = makeRandom; |
|||
|
@ -1,16 +1,24 @@ |
|||
const script = require('./script') |
|||
|
|||
module.exports = { |
|||
Block: require('./block'), |
|||
ECPair: require('./ecpair'), |
|||
Transaction: require('./transaction'), |
|||
TransactionBuilder: require('./transaction_builder'), |
|||
|
|||
address: require('./address'), |
|||
bip32: require('bip32'), |
|||
crypto: require('./crypto'), |
|||
networks: require('./networks'), |
|||
opcodes: require('bitcoin-ops'), |
|||
payments: require('./payments'), |
|||
script: script |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bip32 = require("bip32"); |
|||
exports.bip32 = bip32; |
|||
const address = require("./address"); |
|||
exports.address = address; |
|||
const crypto = require("./crypto"); |
|||
exports.crypto = crypto; |
|||
const ECPair = require("./ecpair"); |
|||
exports.ECPair = ECPair; |
|||
const networks = require("./networks"); |
|||
exports.networks = networks; |
|||
const payments = require("./payments"); |
|||
exports.payments = payments; |
|||
const script = require("./script"); |
|||
exports.script = script; |
|||
var block_1 = require("./block"); |
|||
exports.Block = block_1.Block; |
|||
var script_1 = require("./script"); |
|||
exports.opcodes = script_1.OPS; |
|||
var transaction_1 = require("./transaction"); |
|||
exports.Transaction = transaction_1.Transaction; |
|||
var transaction_builder_1 = require("./transaction_builder"); |
|||
exports.TransactionBuilder = transaction_builder_1.TransactionBuilder; |
|||
|
@ -1,38 +1,35 @@ |
|||
// https://en.bitcoin.it/wiki/List_of_address_prefixes
|
|||
// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
|
|||
|
|||
module.exports = { |
|||
bitcoin: { |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
exports.bitcoin = { |
|||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
|||
bech32: 'bc', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4 |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x00, |
|||
scriptHash: 0x05, |
|||
wif: 0x80 |
|||
}, |
|||
regtest: { |
|||
wif: 0x80, |
|||
}; |
|||
exports.regtest = { |
|||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
|||
bech32: 'bcrt', |
|||
bip32: { |
|||
public: 0x043587cf, |
|||
private: 0x04358394 |
|||
public: 0x043587cf, |
|||
private: 0x04358394, |
|||
}, |
|||
pubKeyHash: 0x6f, |
|||
scriptHash: 0xc4, |
|||
wif: 0xef |
|||
}, |
|||
testnet: { |
|||
wif: 0xef, |
|||
}; |
|||
exports.testnet = { |
|||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
|||
bech32: 'tb', |
|||
bip32: { |
|||
public: 0x043587cf, |
|||
private: 0x04358394 |
|||
public: 0x043587cf, |
|||
private: 0x04358394, |
|||
}, |
|||
pubKeyHash: 0x6f, |
|||
scriptHash: 0xc4, |
|||
wif: 0xef |
|||
} |
|||
} |
|||
wif: 0xef, |
|||
}; |
|||
|
@ -1,56 +1,51 @@ |
|||
const lazy = require('./lazy') |
|||
const typef = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
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]) |
|||
}) |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const networks_1 = require("../networks"); |
|||
const bscript = require("../script"); |
|||
const lazy = require("./lazy"); |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
function stacksEqual(a, b) { |
|||
if (a.length !== b.length) |
|||
return false; |
|||
return a.every((x, i) => { |
|||
return x.equals(b[i]); |
|||
}); |
|||
} |
|||
|
|||
// output: OP_RETURN ...
|
|||
function p2data (a, opts) { |
|||
if ( |
|||
!a.data && |
|||
!a.output |
|||
) throw new TypeError('Not enough data') |
|||
opts = Object.assign({ validate: true }, opts || {}) |
|||
|
|||
typef({ |
|||
network: typef.maybe(typef.Object), |
|||
output: typef.maybe(typef.Buffer), |
|||
data: typef.maybe(typef.arrayOf(typef.Buffer)) |
|||
}, a) |
|||
|
|||
const network = a.network || BITCOIN_NETWORK |
|||
const o = { network } |
|||
|
|||
lazy.prop(o, 'output', function () { |
|||
if (!a.data) return |
|||
return bscript.compile([OPS.OP_RETURN].concat(a.data)) |
|||
}) |
|||
lazy.prop(o, 'data', function () { |
|||
if (!a.output) return |
|||
return bscript.decompile(a.output).slice(1) |
|||
}) |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
if (a.output) { |
|||
const chunks = bscript.decompile(a.output) |
|||
if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') |
|||
if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') |
|||
|
|||
if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') |
|||
function p2data(a, opts) { |
|||
if (!a.data && !a.output) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
typef({ |
|||
network: typef.maybe(typef.Object), |
|||
output: typef.maybe(typef.Buffer), |
|||
data: typef.maybe(typef.arrayOf(typef.Buffer)), |
|||
}, a); |
|||
const network = a.network || networks_1.bitcoin; |
|||
const o = { network }; |
|||
lazy.prop(o, 'output', () => { |
|||
if (!a.data) |
|||
return; |
|||
return bscript.compile([OPS.OP_RETURN].concat(a.data)); |
|||
}); |
|||
lazy.prop(o, 'data', () => { |
|||
if (!a.output) |
|||
return; |
|||
return bscript.decompile(a.output).slice(1); |
|||
}); |
|||
// extended validation
|
|||
if (opts.validate) { |
|||
if (a.output) { |
|||
const chunks = bscript.decompile(a.output); |
|||
if (chunks[0] !== OPS.OP_RETURN) |
|||
throw new TypeError('Output is invalid'); |
|||
if (!chunks.slice(1).every(typef.Buffer)) |
|||
throw new TypeError('Output is invalid'); |
|||
if (a.data && !stacksEqual(a.data, o.data)) |
|||
throw new TypeError('Data mismatch'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
return Object.assign(o, a); |
|||
} |
|||
|
|||
module.exports = p2data |
|||
exports.p2data = p2data; |
|||
|
@ -1,12 +1,18 @@ |
|||
const embed = require('./embed') |
|||
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 = { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } |
|||
|
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const embed_1 = require("./embed"); |
|||
exports.embed = embed_1.p2data; |
|||
const p2ms_1 = require("./p2ms"); |
|||
exports.p2ms = p2ms_1.p2ms; |
|||
const p2pk_1 = require("./p2pk"); |
|||
exports.p2pk = p2pk_1.p2pk; |
|||
const p2pkh_1 = require("./p2pkh"); |
|||
exports.p2pkh = p2pkh_1.p2pkh; |
|||
const p2sh_1 = require("./p2sh"); |
|||
exports.p2sh = p2sh_1.p2sh; |
|||
const p2wpkh_1 = require("./p2wpkh"); |
|||
exports.p2wpkh = p2wpkh_1.p2wpkh; |
|||
const p2wsh_1 = require("./p2wsh"); |
|||
exports.p2wsh = p2wsh_1.p2wsh; |
|||
// TODO
|
|||
// witness commitment
|
|||
|
@ -1,30 +1,32 @@ |
|||
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, { |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
function prop(object, name, f) { |
|||
Object.defineProperty(object, name, { |
|||
configurable: true, |
|||
enumerable: true, |
|||
value: value, |
|||
writable: true |
|||
}) |
|||
} |
|||
}) |
|||
get() { |
|||
const _value = f.call(this); |
|||
this[name] = _value; |
|||
return _value; |
|||
}, |
|||
set(_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 |
|||
} |
|||
exports.prop = prop; |
|||
function value(f) { |
|||
let _value; |
|||
return () => { |
|||
if (_value !== undefined) |
|||
return _value; |
|||
_value = f(); |
|||
return _value; |
|||
}; |
|||
} |
|||
|
|||
module.exports = { prop, value } |
|||
exports.value = value; |
|||
|
@ -1,140 +1,141 @@ |
|||
const lazy = require('./lazy') |
|||
const typef = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
const ecc = require('tiny-secp256k1') |
|||
|
|||
const bscript = require('../script') |
|||
const BITCOIN_NETWORK = require('../networks').bitcoin |
|||
const 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]) |
|||
}) |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const networks_1 = require("../networks"); |
|||
const bscript = require("../script"); |
|||
const lazy = require("./lazy"); |
|||
const OPS = bscript.OPS; |
|||
const typef = require('typeforce'); |
|||
const ecc = require('tiny-secp256k1'); |
|||
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
|
|||
function stacksEqual(a, b) { |
|||
if (a.length !== b.length) |
|||
return false; |
|||
return a.every((x, i) => { |
|||
return x.equals(b[i]); |
|||
}); |
|||
} |
|||
|
|||
// input: OP_0 [signatures ...]
|
|||
// output: m [pubKeys ...] n OP_CHECKMULTISIG
|
|||
function p2ms (a, opts) { |
|||
if ( |
|||
!a.input && |
|||
!a.output && |
|||
!(a.pubkeys && a.m !== undefined) && |
|||
!a.signatures |
|||
) throw new TypeError('Not enough data') |
|||
opts = Object.assign({ validate: true }, opts || {}) |
|||
|
|||
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) |
|||
|
|||
const network = a.network || BITCOIN_NETWORK |
|||
const o = { network } |
|||
|
|||
let chunks |
|||
let decoded = false |
|||
function decode (output) { |
|||
if (decoded) return |
|||
decoded = true |
|||
chunks = bscript.decompile(output) |
|||
o.m = chunks[0] - OP_INT_BASE |
|||
o.n = chunks[chunks.length - 2] - OP_INT_BASE |
|||
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') |
|||
function p2ms(a, opts) { |
|||
if (!a.input && |
|||
!a.output && |
|||
!(a.pubkeys && a.m !== undefined) && |
|||
!a.signatures) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
function isAcceptableSignature(x) { |
|||
return (bscript.isCanonicalScriptSignature(x) || |
|||
(opts.allowIncomplete && x === OPS.OP_0) !== undefined); |
|||
} |
|||
|
|||
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') |
|||
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); |
|||
const network = a.network || networks_1.bitcoin; |
|||
const o = { network }; |
|||
let chunks = []; |
|||
let decoded = false; |
|||
function decode(output) { |
|||
if (decoded) |
|||
return; |
|||
decoded = true; |
|||
chunks = bscript.decompile(output); |
|||
o.m = chunks[0] - OP_INT_BASE; |
|||
o.n = chunks[chunks.length - 2] - OP_INT_BASE; |
|||
o.pubkeys = chunks.slice(1, -2); |
|||
} |
|||
|
|||
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') |
|||
lazy.prop(o, 'output', () => { |
|||
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', () => { |
|||
if (!o.output) |
|||
return; |
|||
decode(o.output); |
|||
return o.m; |
|||
}); |
|||
lazy.prop(o, 'n', () => { |
|||
if (!o.pubkeys) |
|||
return; |
|||
return o.pubkeys.length; |
|||
}); |
|||
lazy.prop(o, 'pubkeys', () => { |
|||
if (!a.output) |
|||
return; |
|||
decode(a.output); |
|||
return o.pubkeys; |
|||
}); |
|||
lazy.prop(o, 'signatures', () => { |
|||
if (!a.input) |
|||
return; |
|||
return bscript.decompile(a.input).slice(1); |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.signatures) |
|||
return; |
|||
return bscript.compile([OPS.OP_0].concat(a.signatures)); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
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, o.signatures)) |
|||
throw new TypeError('Signature mismatch'); |
|||
if (a.m !== undefined && a.m !== a.signatures.length) |
|||
throw new TypeError('Signature count mismatch'); |
|||
} |
|||
} |
|||
|
|||
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, 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) |
|||
return Object.assign(o, a); |
|||
} |
|||
|
|||
module.exports = p2ms |
|||
exports.p2ms = p2ms; |
|||
|
@ -1,80 +1,75 @@ |
|||
const lazy = require('./lazy') |
|||
const typef = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
const ecc = require('tiny-secp256k1') |
|||
|
|||
const bscript = require('../script') |
|||
const BITCOIN_NETWORK = require('../networks').bitcoin |
|||
|
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const networks_1 = require("../networks"); |
|||
const bscript = require("../script"); |
|||
const lazy = require("./lazy"); |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const ecc = require('tiny-secp256k1'); |
|||
// input: {signature}
|
|||
// output: {pubKey} OP_CHECKSIG
|
|||
function p2pk (a, opts) { |
|||
if ( |
|||
!a.input && |
|||
!a.output && |
|||
!a.pubkey && |
|||
!a.input && |
|||
!a.signature |
|||
) throw new TypeError('Not enough data') |
|||
opts = Object.assign({ validate: true }, opts || {}) |
|||
|
|||
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) |
|||
|
|||
const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) |
|||
|
|||
const network = a.network || BITCOIN_NETWORK |
|||
const 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.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.pubkey && !a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch') |
|||
function p2pk(a, opts) { |
|||
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
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); |
|||
const _chunks = lazy.value(() => { |
|||
return bscript.decompile(a.input); |
|||
}); |
|||
const network = a.network || networks_1.bitcoin; |
|||
const o = { network }; |
|||
lazy.prop(o, 'output', () => { |
|||
if (!a.pubkey) |
|||
return; |
|||
return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); |
|||
}); |
|||
lazy.prop(o, 'pubkey', () => { |
|||
if (!a.output) |
|||
return; |
|||
return a.output.slice(1, -1); |
|||
}); |
|||
lazy.prop(o, 'signature', () => { |
|||
if (!a.input) |
|||
return; |
|||
return _chunks()[0]; |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.signature) |
|||
return; |
|||
return bscript.compile([a.signature]); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (!o.input) |
|||
return; |
|||
return []; |
|||
}); |
|||
// extended validation
|
|||
if (opts.validate) { |
|||
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.pubkey && !a.pubkey.equals(o.pubkey)) |
|||
throw new TypeError('Pubkey mismatch'); |
|||
} |
|||
if (a.signature) { |
|||
if (a.input && !a.input.equals(o.input)) |
|||
throw new TypeError('Signature mismatch'); |
|||
} |
|||
if (a.input) { |
|||
if (_chunks().length !== 1) |
|||
throw new TypeError('Input is invalid'); |
|||
if (!bscript.isCanonicalScriptSignature(o.signature)) |
|||
throw new TypeError('Input has invalid signature'); |
|||
} |
|||
} |
|||
|
|||
if (a.signature) { |
|||
if (a.input && !a.input.equals(o.input)) throw new TypeError('Signature mismatch') |
|||
} |
|||
|
|||
if (a.input) { |
|||
if (_chunks().length !== 1) throw new TypeError('Input is invalid') |
|||
if (!bscript.isCanonicalScriptSignature(o.signature)) throw new TypeError('Input has invalid signature') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
return Object.assign(o, a); |
|||
} |
|||
|
|||
module.exports = p2pk |
|||
exports.p2pk = p2pk; |
|||
|
@ -1,137 +1,142 @@ |
|||
const lazy = require('./lazy') |
|||
const typef = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
const ecc = require('tiny-secp256k1') |
|||
|
|||
const bcrypto = require('../crypto') |
|||
const bscript = require('../script') |
|||
const BITCOIN_NETWORK = require('../networks').bitcoin |
|||
const bs58check = require('bs58check') |
|||
|
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bcrypto = require("../crypto"); |
|||
const networks_1 = require("../networks"); |
|||
const bscript = require("../script"); |
|||
const lazy = require("./lazy"); |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const ecc = require('tiny-secp256k1'); |
|||
const bs58check = require('bs58check'); |
|||
// 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 = Object.assign({ validate: true }, opts || {}) |
|||
|
|||
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) |
|||
|
|||
const _address = lazy.value(function () { |
|||
const payload = bs58check.decode(a.address) |
|||
const version = payload.readUInt8(0) |
|||
const hash = payload.slice(1) |
|||
return { version, hash } |
|||
}) |
|||
const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) |
|||
|
|||
const network = a.network || BITCOIN_NETWORK |
|||
const o = { network } |
|||
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
|
|||
const payload = Buffer.allocUnsafe(21) |
|||
payload.writeUInt8(network.pubKeyHash, 0) |
|||
o.hash.copy(payload, 1) |
|||
return bs58check.encode(payload) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
if (a.output) return a.output.slice(3, 23) |
|||
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('Invalid version or Network mismatch') |
|||
if (_address().hash.length !== 20) throw new TypeError('Invalid address') |
|||
hash = _address().hash |
|||
function p2pkh(a, opts) { |
|||
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
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); |
|||
const _address = lazy.value(() => { |
|||
const payload = bs58check.decode(a.address); |
|||
const version = payload.readUInt8(0); |
|||
const hash = payload.slice(1); |
|||
return { version, hash }; |
|||
}); |
|||
const _chunks = lazy.value(() => { |
|||
return bscript.decompile(a.input); |
|||
}); |
|||
const network = a.network || networks_1.bitcoin; |
|||
const o = { network }; |
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) |
|||
return; |
|||
const payload = Buffer.allocUnsafe(21); |
|||
payload.writeUInt8(network.pubKeyHash, 0); |
|||
o.hash.copy(payload, 1); |
|||
return bs58check.encode(payload); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
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', () => { |
|||
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', () => { |
|||
if (!a.input) |
|||
return; |
|||
return _chunks()[1]; |
|||
}); |
|||
lazy.prop(o, 'signature', () => { |
|||
if (!a.input) |
|||
return; |
|||
return _chunks()[0]; |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.pubkey) |
|||
return; |
|||
if (!a.signature) |
|||
return; |
|||
return bscript.compile([a.signature, a.pubkey]); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (!o.input) |
|||
return; |
|||
return []; |
|||
}); |
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash = Buffer.from([]); |
|||
if (a.address) { |
|||
if (_address().version !== network.pubKeyHash) |
|||
throw new TypeError('Invalid version or Network mismatch'); |
|||
if (_address().hash.length !== 20) |
|||
throw new TypeError('Invalid address'); |
|||
hash = _address().hash; |
|||
} |
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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'); |
|||
const hash2 = a.output.slice(3, 23); |
|||
if (hash.length > 0 && !hash.equals(hash2)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else |
|||
hash = hash2; |
|||
} |
|||
if (a.pubkey) { |
|||
const pkh = bcrypto.hash160(a.pubkey); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else |
|||
hash = pkh; |
|||
} |
|||
if (a.input) { |
|||
const 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'); |
|||
const pkh = bcrypto.hash160(chunks[1]); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
} |
|||
} |
|||
|
|||
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') |
|||
|
|||
const hash2 = a.output.slice(3, 23) |
|||
if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') |
|||
else hash = hash2 |
|||
} |
|||
|
|||
if (a.pubkey) { |
|||
const pkh = bcrypto.hash160(a.pubkey) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
else hash = pkh |
|||
} |
|||
|
|||
if (a.input) { |
|||
const 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') |
|||
|
|||
const pkh = bcrypto.hash160(chunks[1]) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
return Object.assign(o, a); |
|||
} |
|||
|
|||
module.exports = p2pkh |
|||
exports.p2pkh = p2pkh; |
|||
|
@ -1,193 +1,185 @@ |
|||
const lazy = require('./lazy') |
|||
const typef = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
const bcrypto = require('../crypto') |
|||
const bscript = require('../script') |
|||
const BITCOIN_NETWORK = require('../networks').bitcoin |
|||
const bs58check = require('bs58check') |
|||
|
|||
function stacksEqual (a, b) { |
|||
if (a.length !== b.length) return false |
|||
|
|||
return a.every(function (x, i) { |
|||
return x.equals(b[i]) |
|||
}) |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bcrypto = require("../crypto"); |
|||
const networks_1 = require("../networks"); |
|||
const bscript = require("../script"); |
|||
const lazy = require("./lazy"); |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const bs58check = require('bs58check'); |
|||
function stacksEqual(a, b) { |
|||
if (a.length !== b.length) |
|||
return false; |
|||
return a.every((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 = Object.assign({ validate: true }, opts || {}) |
|||
|
|||
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.maybe(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) |
|||
|
|||
let network = a.network |
|||
if (!network) { |
|||
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK |
|||
} |
|||
|
|||
const o = { network } |
|||
|
|||
const _address = lazy.value(function () { |
|||
const payload = bs58check.decode(a.address) |
|||
const version = payload.readUInt8(0) |
|||
const hash = payload.slice(1) |
|||
return { version, hash } |
|||
}) |
|||
const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) |
|||
const _redeem = lazy.value(function () { |
|||
const chunks = _chunks() |
|||
return { |
|||
network, |
|||
output: chunks[chunks.length - 1], |
|||
input: bscript.compile(chunks.slice(0, -1)), |
|||
witness: a.witness || [] |
|||
function p2sh(a, opts) { |
|||
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
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.maybe(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); |
|||
let network = a.network; |
|||
if (!network) { |
|||
network = (a.redeem && a.redeem.network) || networks_1.bitcoin; |
|||
} |
|||
}) |
|||
|
|||
// output dependents
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
|
|||
const payload = Buffer.allocUnsafe(21) |
|||
payload.writeUInt8(network.scriptHash, 0) |
|||
o.hash.copy(payload, 1) |
|||
return bs58check.encode(payload) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
// in order of least effort
|
|||
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 || !a.redeem.output) 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('Invalid version or Network mismatch') |
|||
if (_address().hash.length !== 20) throw new TypeError('Invalid address') |
|||
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?
|
|||
if (redeem.output) { |
|||
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') |
|||
const o = { network }; |
|||
const _address = lazy.value(() => { |
|||
const payload = bs58check.decode(a.address); |
|||
const version = payload.readUInt8(0); |
|||
const hash = payload.slice(1); |
|||
return { version, hash }; |
|||
}); |
|||
const _chunks = lazy.value(() => { |
|||
return bscript.decompile(a.input); |
|||
}); |
|||
const _redeem = lazy.value(() => { |
|||
const chunks = _chunks(); |
|||
return { |
|||
network, |
|||
output: chunks[chunks.length - 1], |
|||
input: bscript.compile(chunks.slice(0, -1)), |
|||
witness: a.witness || [], |
|||
}; |
|||
}); |
|||
// output dependents
|
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) |
|||
return; |
|||
const payload = Buffer.allocUnsafe(21); |
|||
payload.writeUInt8(o.network.scriptHash, 0); |
|||
o.hash.copy(payload, 1); |
|||
return bs58check.encode(payload); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
// 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', () => { |
|||
if (!o.hash) |
|||
return; |
|||
return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); |
|||
}); |
|||
// input dependents
|
|||
lazy.prop(o, 'redeem', () => { |
|||
if (!a.input) |
|||
return; |
|||
return _redeem(); |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.redeem || !a.redeem.input || !a.redeem.output) |
|||
return; |
|||
return bscript.compile([].concat(bscript.decompile(a.redeem.input), a.redeem.output)); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (o.redeem && o.redeem.witness) |
|||
return o.redeem.witness; |
|||
if (o.input) |
|||
return []; |
|||
}); |
|||
if (opts.validate) { |
|||
let hash = Buffer.from([]); |
|||
if (a.address) { |
|||
if (_address().version !== network.scriptHash) |
|||
throw new TypeError('Invalid version or Network mismatch'); |
|||
if (_address().hash.length !== 20) |
|||
throw new TypeError('Invalid address'); |
|||
hash = _address().hash; |
|||
} |
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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.length > 0 && !hash.equals(hash2)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else |
|||
hash = hash2; |
|||
} |
|||
// inlined to prevent 'no-inner-declarations' failing
|
|||
const checkRedeem = (redeem) => { |
|||
// is the redeem output empty/invalid?
|
|||
if (redeem.output) { |
|||
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.length > 0 && !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 (a.input) { |
|||
const redeem = _redeem(); |
|||
if (a.redeem.output && !a.redeem.output.equals(redeem.output)) |
|||
throw new TypeError('Redeem.output mismatch'); |
|||
if (a.redeem.input && !a.redeem.input.equals(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'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
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 (a.input) { |
|||
const redeem = _redeem() |
|||
if (a.redeem.output && !a.redeem.output.equals(redeem.output)) throw new TypeError('Redeem.output mismatch') |
|||
if (a.redeem.input && !a.redeem.input.equals(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) |
|||
return Object.assign(o, a); |
|||
} |
|||
|
|||
module.exports = p2sh |
|||
exports.p2sh = p2sh; |
|||
|
@ -1,135 +1,138 @@ |
|||
const lazy = require('./lazy') |
|||
const typef = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
const ecc = require('tiny-secp256k1') |
|||
|
|||
const bcrypto = require('../crypto') |
|||
const bech32 = require('bech32') |
|||
const bscript = require('../script') |
|||
const BITCOIN_NETWORK = require('../networks').bitcoin |
|||
|
|||
const EMPTY_BUFFER = Buffer.alloc(0) |
|||
|
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bcrypto = require("../crypto"); |
|||
const networks_1 = require("../networks"); |
|||
const bscript = require("../script"); |
|||
const lazy = require("./lazy"); |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const ecc = require('tiny-secp256k1'); |
|||
const bech32 = require('bech32'); |
|||
const 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 = Object.assign({ validate: true }, opts || {}) |
|||
|
|||
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) |
|||
|
|||
const _address = lazy.value(function () { |
|||
const result = bech32.decode(a.address) |
|||
const version = result.words.shift() |
|||
const data = bech32.fromWords(result.words) |
|||
return { |
|||
version, |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data) |
|||
function p2wpkh(a, opts) { |
|||
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
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); |
|||
const _address = lazy.value(() => { |
|||
const result = bech32.decode(a.address); |
|||
const version = result.words.shift(); |
|||
const data = bech32.fromWords(result.words); |
|||
return { |
|||
version, |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data), |
|||
}; |
|||
}); |
|||
const network = a.network || networks_1.bitcoin; |
|||
const o = { network }; |
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) |
|||
return; |
|||
const words = bech32.toWords(o.hash); |
|||
words.unshift(0x00); |
|||
return bech32.encode(network.bech32, words); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
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', () => { |
|||
if (!o.hash) |
|||
return; |
|||
return bscript.compile([OPS.OP_0, o.hash]); |
|||
}); |
|||
lazy.prop(o, 'pubkey', () => { |
|||
if (a.pubkey) |
|||
return a.pubkey; |
|||
if (!a.witness) |
|||
return; |
|||
return a.witness[1]; |
|||
}); |
|||
lazy.prop(o, 'signature', () => { |
|||
if (!a.witness) |
|||
return; |
|||
return a.witness[0]; |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!o.witness) |
|||
return; |
|||
return EMPTY_BUFFER; |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (!a.pubkey) |
|||
return; |
|||
if (!a.signature) |
|||
return; |
|||
return [a.signature, a.pubkey]; |
|||
}); |
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash = Buffer.from([]); |
|||
if (a.address) { |
|||
if (network && network.bech32 !== _address().prefix) |
|||
throw new TypeError('Invalid prefix or Network mismatch'); |
|||
if (_address().version !== 0x00) |
|||
throw new TypeError('Invalid address version'); |
|||
if (_address().data.length !== 20) |
|||
throw new TypeError('Invalid address data'); |
|||
hash = _address().data; |
|||
} |
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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.length > 0 && !hash.equals(a.output.slice(2))) |
|||
throw new TypeError('Hash mismatch'); |
|||
else |
|||
hash = a.output.slice(2); |
|||
} |
|||
if (a.pubkey) { |
|||
const pkh = bcrypto.hash160(a.pubkey); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else |
|||
hash = pkh; |
|||
} |
|||
if (a.witness) { |
|||
if (a.witness.length !== 2) |
|||
throw new TypeError('Witness is invalid'); |
|||
if (!bscript.isCanonicalScriptSignature(a.witness[0])) |
|||
throw new TypeError('Witness has invalid signature'); |
|||
if (!ecc.isPoint(a.witness[1])) |
|||
throw new TypeError('Witness 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'); |
|||
const pkh = bcrypto.hash160(a.witness[1]); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
} |
|||
} |
|||
}) |
|||
|
|||
const network = a.network || BITCOIN_NETWORK |
|||
const o = { network } |
|||
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
|
|||
const words = bech32.toWords(o.hash) |
|||
words.unshift(0x00) |
|||
return bech32.encode(network.bech32, words) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
if (a.output) return a.output.slice(2, 22) |
|||
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('Invalid prefix or Network mismatch') |
|||
if (_address().version !== 0x00) throw new TypeError('Invalid address version') |
|||
if (_address().data.length !== 20) throw new TypeError('Invalid address data') |
|||
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 !== 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.pubkey) { |
|||
const pkh = bcrypto.hash160(a.pubkey) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
else hash = pkh |
|||
} |
|||
|
|||
if (a.witness) { |
|||
if (a.witness.length !== 2) throw new TypeError('Witness is invalid') |
|||
if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature') |
|||
if (!ecc.isPoint(a.witness[1])) throw new TypeError('Witness 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') |
|||
|
|||
const pkh = bcrypto.hash160(a.witness[1]) |
|||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
return Object.assign(o, a); |
|||
} |
|||
|
|||
module.exports = p2wpkh |
|||
exports.p2wpkh = p2wpkh; |
|||
|
@ -1,180 +1,177 @@ |
|||
const lazy = require('./lazy') |
|||
const typef = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
const bech32 = require('bech32') |
|||
const bcrypto = require('../crypto') |
|||
const bscript = require('../script') |
|||
const BITCOIN_NETWORK = require('../networks').bitcoin |
|||
|
|||
const 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]) |
|||
}) |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bcrypto = require("../crypto"); |
|||
const networks_1 = require("../networks"); |
|||
const bscript = require("../script"); |
|||
const lazy = require("./lazy"); |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const bech32 = require('bech32'); |
|||
const EMPTY_BUFFER = Buffer.alloc(0); |
|||
function stacksEqual(a, b) { |
|||
if (a.length !== b.length) |
|||
return false; |
|||
return a.every((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 = Object.assign({ validate: true }, opts || {}) |
|||
|
|||
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.maybe(typef.Buffer), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
|||
}), |
|||
input: typef.maybe(typef.BufferN(0)), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)) |
|||
}, a) |
|||
|
|||
const _address = lazy.value(function () { |
|||
const result = bech32.decode(a.address) |
|||
const version = result.words.shift() |
|||
const data = bech32.fromWords(result.words) |
|||
return { |
|||
version, |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data) |
|||
function p2wsh(a, opts) { |
|||
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
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.maybe(typef.Buffer), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)), |
|||
}), |
|||
input: typef.maybe(typef.BufferN(0)), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)), |
|||
}, a); |
|||
const _address = lazy.value(() => { |
|||
const result = bech32.decode(a.address); |
|||
const version = result.words.shift(); |
|||
const data = bech32.fromWords(result.words); |
|||
return { |
|||
version, |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data), |
|||
}; |
|||
}); |
|||
const _rchunks = lazy.value(() => { |
|||
return bscript.decompile(a.redeem.input); |
|||
}); |
|||
let network = a.network; |
|||
if (!network) { |
|||
network = (a.redeem && a.redeem.network) || networks_1.bitcoin; |
|||
} |
|||
}) |
|||
const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) }) |
|||
|
|||
let network = a.network |
|||
if (!network) { |
|||
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK |
|||
} |
|||
|
|||
const o = { network } |
|||
|
|||
lazy.prop(o, 'address', function () { |
|||
if (!o.hash) return |
|||
const words = bech32.toWords(o.hash) |
|||
words.unshift(0x00) |
|||
return bech32.encode(network.bech32, words) |
|||
}) |
|||
lazy.prop(o, 'hash', function () { |
|||
if (a.output) return a.output.slice(2) |
|||
if (a.address) return _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) |
|||
const o = { network }; |
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) |
|||
return; |
|||
const words = bech32.toWords(o.hash); |
|||
words.unshift(0x00); |
|||
return bech32.encode(network.bech32, words); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
if (a.output) |
|||
return a.output.slice(2); |
|||
if (a.address) |
|||
return _address().data; |
|||
if (o.redeem && o.redeem.output) |
|||
return bcrypto.sha256(o.redeem.output); |
|||
}); |
|||
lazy.prop(o, 'output', () => { |
|||
if (!o.hash) |
|||
return; |
|||
return bscript.compile([OPS.OP_0, o.hash]); |
|||
}); |
|||
lazy.prop(o, 'redeem', () => { |
|||
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', () => { |
|||
if (!o.witness) |
|||
return; |
|||
return EMPTY_BUFFER; |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
// transform redeem input to witness stack?
|
|||
if (a.redeem && |
|||
a.redeem.input && |
|||
a.redeem.input.length > 0 && |
|||
a.redeem.output && |
|||
a.redeem.output.length > 0) { |
|||
const 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.output) |
|||
return; |
|||
if (!a.redeem.witness) |
|||
return; |
|||
return [].concat(a.redeem.witness, a.redeem.output); |
|||
}); |
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash = Buffer.from([]); |
|||
if (a.address) { |
|||
if (_address().prefix !== network.bech32) |
|||
throw new TypeError('Invalid prefix or Network mismatch'); |
|||
if (_address().version !== 0x00) |
|||
throw new TypeError('Invalid address version'); |
|||
if (_address().data.length !== 32) |
|||
throw new TypeError('Invalid address data'); |
|||
hash = _address().data; |
|||
} |
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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'); |
|||
const hash2 = a.output.slice(2); |
|||
if (hash.length > 0 && !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 && |
|||
a.redeem.witness.length > 0) |
|||
throw new TypeError('Ambiguous witness source'); |
|||
// is the redeem output non-empty?
|
|||
if (a.redeem.output) { |
|||
if (bscript.decompile(a.redeem.output).length === 0) |
|||
throw new TypeError('Redeem.output is invalid'); |
|||
// match hash against other sources
|
|||
const hash2 = bcrypto.sha256(a.redeem.output); |
|||
if (hash.length > 0 && !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 && |
|||
!a.redeem.output.equals(a.witness[a.witness.length - 1])) |
|||
throw new TypeError('Witness and redeem.output mismatch'); |
|||
} |
|||
} |
|||
}) |
|||
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 && |
|||
a.redeem.output && |
|||
a.redeem.output.length > 0 |
|||
) { |
|||
const 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.output) 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('Invalid prefix or Network mismatch') |
|||
if (_address().version !== 0x00) throw new TypeError('Invalid address version') |
|||
if (_address().data.length !== 32) throw new TypeError('Invalid address data') |
|||
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') |
|||
const 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 && |
|||
a.redeem.witness.length > 0 |
|||
) throw new TypeError('Ambiguous witness source') |
|||
|
|||
// is the redeem output non-empty?
|
|||
if (a.redeem.output) { |
|||
if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid') |
|||
|
|||
// match hash against other sources
|
|||
const 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 && !a.redeem.output.equals(a.witness[a.witness.length - 1])) throw new TypeError('Witness and redeem.output mismatch') |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a) |
|||
return Object.assign(o, a); |
|||
} |
|||
|
|||
module.exports = p2wsh |
|||
exports.p2wsh = p2wsh; |
|||
|
@ -1,205 +1,191 @@ |
|||
const Buffer = require('safe-buffer').Buffer |
|||
const bip66 = require('bip66') |
|||
const ecc = require('tiny-secp256k1') |
|||
const pushdata = require('pushdata-bitcoin') |
|||
const typeforce = require('typeforce') |
|||
const types = require('./types') |
|||
const scriptNumber = require('./script_number') |
|||
|
|||
const OPS = require('bitcoin-ops') |
|||
const REVERSE_OPS = require('bitcoin-ops/map') |
|||
const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
|||
|
|||
function isOPInt (value) { |
|||
return types.Number(value) && |
|||
((value === OPS.OP_0) || |
|||
(value >= OPS.OP_1 && value <= OPS.OP_16) || |
|||
(value === OPS.OP_1NEGATE)) |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const scriptNumber = require("./script_number"); |
|||
const scriptSignature = require("./script_signature"); |
|||
const types = require("./types"); |
|||
const bip66 = require('bip66'); |
|||
const ecc = require('tiny-secp256k1'); |
|||
const pushdata = require('pushdata-bitcoin'); |
|||
const typeforce = require('typeforce'); |
|||
exports.OPS = require('bitcoin-ops'); |
|||
const REVERSE_OPS = require('bitcoin-ops/map'); |
|||
const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1
|
|||
function isOPInt(value) { |
|||
return (types.Number(value) && |
|||
(value === exports.OPS.OP_0 || |
|||
(value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || |
|||
value === exports.OPS.OP_1NEGATE)); |
|||
} |
|||
|
|||
function isPushOnlyChunk (value) { |
|||
return types.Buffer(value) || isOPInt(value) |
|||
function isPushOnlyChunk(value) { |
|||
return types.Buffer(value) || isOPInt(value); |
|||
} |
|||
|
|||
function isPushOnly (value) { |
|||
return types.Array(value) && value.every(isPushOnlyChunk) |
|||
function isPushOnly(value) { |
|||
return types.Array(value) && value.every(isPushOnlyChunk); |
|||
} |
|||
|
|||
function asMinimalOP (buffer) { |
|||
if (buffer.length === 0) return OPS.OP_0 |
|||
if (buffer.length !== 1) return |
|||
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0] |
|||
if (buffer[0] === 0x81) return OPS.OP_1NEGATE |
|||
exports.isPushOnly = isPushOnly; |
|||
function asMinimalOP(buffer) { |
|||
if (buffer.length === 0) |
|||
return exports.OPS.OP_0; |
|||
if (buffer.length !== 1) |
|||
return; |
|||
if (buffer[0] >= 1 && buffer[0] <= 16) |
|||
return OP_INT_BASE + buffer[0]; |
|||
if (buffer[0] === 0x81) |
|||
return exports.OPS.OP_1NEGATE; |
|||
} |
|||
|
|||
function compile (chunks) { |
|||
// TODO: remove me
|
|||
if (Buffer.isBuffer(chunks)) return chunks |
|||
|
|||
typeforce(types.Array, chunks) |
|||
|
|||
const bufferSize = chunks.reduce(function (accum, chunk) { |
|||
// data chunk
|
|||
if (Buffer.isBuffer(chunk)) { |
|||
// adhere to BIP62.3, minimal push policy
|
|||
if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { |
|||
return accum + 1 |
|||
} |
|||
|
|||
return accum + pushdata.encodingLength(chunk.length) + chunk.length |
|||
} |
|||
|
|||
// opcode
|
|||
return accum + 1 |
|||
}, 0.0) |
|||
|
|||
const buffer = Buffer.allocUnsafe(bufferSize) |
|||
let offset = 0 |
|||
|
|||
chunks.forEach(function (chunk) { |
|||
// data chunk
|
|||
if (Buffer.isBuffer(chunk)) { |
|||
// adhere to BIP62.3, minimal push policy
|
|||
const opcode = asMinimalOP(chunk) |
|||
if (opcode !== undefined) { |
|||
buffer.writeUInt8(opcode, offset) |
|||
offset += 1 |
|||
return |
|||
} |
|||
|
|||
offset += pushdata.encode(buffer, chunk.length, offset) |
|||
chunk.copy(buffer, offset) |
|||
offset += chunk.length |
|||
|
|||
// opcode
|
|||
} else { |
|||
buffer.writeUInt8(chunk, offset) |
|||
offset += 1 |
|||
} |
|||
}) |
|||
|
|||
if (offset !== buffer.length) throw new Error('Could not decode chunks') |
|||
return buffer |
|||
function chunksIsBuffer(buf) { |
|||
return Buffer.isBuffer(buf); |
|||
} |
|||
|
|||
function decompile (buffer) { |
|||
// TODO: remove me
|
|||
if (types.Array(buffer)) return buffer |
|||
|
|||
typeforce(types.Buffer, buffer) |
|||
|
|||
const chunks = [] |
|||
let i = 0 |
|||
|
|||
while (i < buffer.length) { |
|||
const opcode = buffer[i] |
|||
|
|||
// data chunk
|
|||
if ((opcode > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) { |
|||
const d = pushdata.decode(buffer, i) |
|||
|
|||
// did reading a pushDataInt fail?
|
|||
if (d === null) return null |
|||
i += d.size |
|||
|
|||
// attempt to read too much data?
|
|||
if (i + d.number > buffer.length) return null |
|||
|
|||
const data = buffer.slice(i, i + d.number) |
|||
i += d.number |
|||
|
|||
// decompile minimally
|
|||
const op = asMinimalOP(data) |
|||
if (op !== undefined) { |
|||
chunks.push(op) |
|||
} else { |
|||
chunks.push(data) |
|||
} |
|||
|
|||
// opcode
|
|||
} else { |
|||
chunks.push(opcode) |
|||
|
|||
i += 1 |
|||
} |
|||
} |
|||
|
|||
return chunks |
|||
function chunksIsArray(buf) { |
|||
return types.Array(buf); |
|||
} |
|||
|
|||
function toASM (chunks) { |
|||
if (Buffer.isBuffer(chunks)) { |
|||
chunks = decompile(chunks) |
|||
} |
|||
|
|||
return chunks.map(function (chunk) { |
|||
// data?
|
|||
if (Buffer.isBuffer(chunk)) { |
|||
const op = asMinimalOP(chunk) |
|||
if (op === undefined) return chunk.toString('hex') |
|||
chunk = op |
|||
function singleChunkIsBuffer(buf) { |
|||
return Buffer.isBuffer(buf); |
|||
} |
|||
function compile(chunks) { |
|||
// TODO: remove me
|
|||
if (chunksIsBuffer(chunks)) |
|||
return chunks; |
|||
typeforce(types.Array, chunks); |
|||
const bufferSize = chunks.reduce((accum, chunk) => { |
|||
// data chunk
|
|||
if (singleChunkIsBuffer(chunk)) { |
|||
// adhere to BIP62.3, minimal push policy
|
|||
if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { |
|||
return accum + 1; |
|||
} |
|||
return accum + pushdata.encodingLength(chunk.length) + chunk.length; |
|||
} |
|||
// opcode
|
|||
return accum + 1; |
|||
}, 0.0); |
|||
const buffer = Buffer.allocUnsafe(bufferSize); |
|||
let offset = 0; |
|||
chunks.forEach(chunk => { |
|||
// data chunk
|
|||
if (singleChunkIsBuffer(chunk)) { |
|||
// adhere to BIP62.3, minimal push policy
|
|||
const opcode = asMinimalOP(chunk); |
|||
if (opcode !== undefined) { |
|||
buffer.writeUInt8(opcode, offset); |
|||
offset += 1; |
|||
return; |
|||
} |
|||
offset += pushdata.encode(buffer, chunk.length, offset); |
|||
chunk.copy(buffer, offset); |
|||
offset += chunk.length; |
|||
// opcode
|
|||
} |
|||
else { |
|||
buffer.writeUInt8(chunk, offset); |
|||
offset += 1; |
|||
} |
|||
}); |
|||
if (offset !== buffer.length) |
|||
throw new Error('Could not decode chunks'); |
|||
return buffer; |
|||
} |
|||
exports.compile = compile; |
|||
function decompile(buffer) { |
|||
// TODO: remove me
|
|||
if (chunksIsArray(buffer)) |
|||
return buffer; |
|||
typeforce(types.Buffer, buffer); |
|||
const chunks = []; |
|||
let i = 0; |
|||
while (i < buffer.length) { |
|||
const opcode = buffer[i]; |
|||
// data chunk
|
|||
if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) { |
|||
const d = pushdata.decode(buffer, i); |
|||
// did reading a pushDataInt fail?
|
|||
if (d === null) |
|||
return null; |
|||
i += d.size; |
|||
// attempt to read too much data?
|
|||
if (i + d.number > buffer.length) |
|||
return null; |
|||
const data = buffer.slice(i, i + d.number); |
|||
i += d.number; |
|||
// decompile minimally
|
|||
const op = asMinimalOP(data); |
|||
if (op !== undefined) { |
|||
chunks.push(op); |
|||
} |
|||
else { |
|||
chunks.push(data); |
|||
} |
|||
// opcode
|
|||
} |
|||
else { |
|||
chunks.push(opcode); |
|||
i += 1; |
|||
} |
|||
} |
|||
|
|||
// opcode!
|
|||
return REVERSE_OPS[chunk] |
|||
}).join(' ') |
|||
return chunks; |
|||
} |
|||
|
|||
function fromASM (asm) { |
|||
typeforce(types.String, asm) |
|||
|
|||
return compile(asm.split(' ').map(function (chunkStr) { |
|||
// opcode?
|
|||
if (OPS[chunkStr] !== undefined) return OPS[chunkStr] |
|||
typeforce(types.Hex, chunkStr) |
|||
|
|||
// data!
|
|||
return Buffer.from(chunkStr, 'hex') |
|||
})) |
|||
exports.decompile = decompile; |
|||
function toASM(chunks) { |
|||
if (chunksIsBuffer(chunks)) { |
|||
chunks = decompile(chunks); |
|||
} |
|||
return chunks |
|||
.map(chunk => { |
|||
// data?
|
|||
if (singleChunkIsBuffer(chunk)) { |
|||
const op = asMinimalOP(chunk); |
|||
if (op === undefined) |
|||
return chunk.toString('hex'); |
|||
chunk = op; |
|||
} |
|||
// opcode!
|
|||
return REVERSE_OPS[chunk]; |
|||
}) |
|||
.join(' '); |
|||
} |
|||
|
|||
function toStack (chunks) { |
|||
chunks = decompile(chunks) |
|||
typeforce(isPushOnly, chunks) |
|||
|
|||
return chunks.map(function (op) { |
|||
if (Buffer.isBuffer(op)) return op |
|||
if (op === OPS.OP_0) return Buffer.allocUnsafe(0) |
|||
|
|||
return scriptNumber.encode(op - OP_INT_BASE) |
|||
}) |
|||
exports.toASM = toASM; |
|||
function fromASM(asm) { |
|||
typeforce(types.String, asm); |
|||
return compile(asm.split(' ').map(chunkStr => { |
|||
// opcode?
|
|||
if (exports.OPS[chunkStr] !== undefined) |
|||
return exports.OPS[chunkStr]; |
|||
typeforce(types.Hex, chunkStr); |
|||
// data!
|
|||
return Buffer.from(chunkStr, 'hex'); |
|||
})); |
|||
} |
|||
|
|||
function isCanonicalPubKey (buffer) { |
|||
return ecc.isPoint(buffer) |
|||
exports.fromASM = fromASM; |
|||
function toStack(chunks) { |
|||
chunks = decompile(chunks); |
|||
typeforce(isPushOnly, chunks); |
|||
return chunks.map(op => { |
|||
if (singleChunkIsBuffer(op)) |
|||
return op; |
|||
if (op === exports.OPS.OP_0) |
|||
return Buffer.allocUnsafe(0); |
|||
return scriptNumber.encode(op - OP_INT_BASE); |
|||
}); |
|||
} |
|||
|
|||
function isDefinedHashType (hashType) { |
|||
const hashTypeMod = hashType & ~0x80 |
|||
|
|||
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
|
|||
return hashTypeMod > 0x00 && hashTypeMod < 0x04 |
|||
exports.toStack = toStack; |
|||
function isCanonicalPubKey(buffer) { |
|||
return ecc.isPoint(buffer); |
|||
} |
|||
|
|||
function isCanonicalScriptSignature (buffer) { |
|||
if (!Buffer.isBuffer(buffer)) return false |
|||
if (!isDefinedHashType(buffer[buffer.length - 1])) return false |
|||
|
|||
return bip66.check(buffer.slice(0, -1)) |
|||
exports.isCanonicalPubKey = isCanonicalPubKey; |
|||
function isDefinedHashType(hashType) { |
|||
const hashTypeMod = hashType & ~0x80; |
|||
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
|
|||
return hashTypeMod > 0x00 && hashTypeMod < 0x04; |
|||
} |
|||
|
|||
module.exports = { |
|||
compile: compile, |
|||
decompile: decompile, |
|||
fromASM: fromASM, |
|||
toASM: toASM, |
|||
toStack: toStack, |
|||
|
|||
number: require('./script_number'), |
|||
signature: require('./script_signature'), |
|||
|
|||
isCanonicalPubKey: isCanonicalPubKey, |
|||
isCanonicalScriptSignature: isCanonicalScriptSignature, |
|||
isPushOnly: isPushOnly, |
|||
isDefinedHashType: isDefinedHashType |
|||
exports.isDefinedHashType = isDefinedHashType; |
|||
function isCanonicalScriptSignature(buffer) { |
|||
if (!Buffer.isBuffer(buffer)) |
|||
return false; |
|||
if (!isDefinedHashType(buffer[buffer.length - 1])) |
|||
return false; |
|||
return bip66.check(buffer.slice(0, -1)); |
|||
} |
|||
exports.isCanonicalScriptSignature = isCanonicalScriptSignature; |
|||
// tslint:disable-next-line variable-name
|
|||
exports.number = scriptNumber; |
|||
exports.signature = scriptSignature; |
|||
|
@ -1,67 +1,65 @@ |
|||
const Buffer = require('safe-buffer').Buffer |
|||
|
|||
function decode (buffer, maxLength, minimal) { |
|||
maxLength = maxLength || 4 |
|||
minimal = minimal === undefined ? true : minimal |
|||
|
|||
const length = buffer.length |
|||
if (length === 0) return 0 |
|||
if (length > maxLength) throw new TypeError('Script number overflow') |
|||
if (minimal) { |
|||
if ((buffer[length - 1] & 0x7f) === 0) { |
|||
if (length <= 1 || (buffer[length - 2] & 0x80) === 0) throw new Error('Non-minimally encoded script number') |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
function decode(buffer, maxLength, minimal) { |
|||
maxLength = maxLength || 4; |
|||
minimal = minimal === undefined ? true : minimal; |
|||
const length = buffer.length; |
|||
if (length === 0) |
|||
return 0; |
|||
if (length > maxLength) |
|||
throw new TypeError('Script number overflow'); |
|||
if (minimal) { |
|||
if ((buffer[length - 1] & 0x7f) === 0) { |
|||
if (length <= 1 || (buffer[length - 2] & 0x80) === 0) |
|||
throw new Error('Non-minimally encoded script number'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 40-bit
|
|||
if (length === 5) { |
|||
const a = buffer.readUInt32LE(0) |
|||
const b = buffer.readUInt8(4) |
|||
|
|||
if (b & 0x80) return -(((b & ~0x80) * 0x100000000) + a) |
|||
return (b * 0x100000000) + a |
|||
} |
|||
|
|||
// 32-bit / 24-bit / 16-bit / 8-bit
|
|||
let result = 0 |
|||
for (var i = 0; i < length; ++i) { |
|||
result |= buffer[i] << (8 * i) |
|||
} |
|||
|
|||
if (buffer[length - 1] & 0x80) return -(result & ~(0x80 << (8 * (length - 1)))) |
|||
return result |
|||
} |
|||
|
|||
function scriptNumSize (i) { |
|||
return i > 0x7fffffff ? 5 |
|||
: i > 0x7fffff ? 4 |
|||
: i > 0x7fff ? 3 |
|||
: i > 0x7f ? 2 |
|||
: i > 0x00 ? 1 |
|||
: 0 |
|||
// 40-bit
|
|||
if (length === 5) { |
|||
const a = buffer.readUInt32LE(0); |
|||
const b = buffer.readUInt8(4); |
|||
if (b & 0x80) |
|||
return -((b & ~0x80) * 0x100000000 + a); |
|||
return b * 0x100000000 + a; |
|||
} |
|||
// 32-bit / 24-bit / 16-bit / 8-bit
|
|||
let result = 0; |
|||
for (let i = 0; i < length; ++i) { |
|||
result |= buffer[i] << (8 * i); |
|||
} |
|||
if (buffer[length - 1] & 0x80) |
|||
return -(result & ~(0x80 << (8 * (length - 1)))); |
|||
return result; |
|||
} |
|||
|
|||
function encode (number) { |
|||
let value = Math.abs(number) |
|||
const size = scriptNumSize(value) |
|||
const buffer = Buffer.allocUnsafe(size) |
|||
const negative = number < 0 |
|||
|
|||
for (var i = 0; i < size; ++i) { |
|||
buffer.writeUInt8(value & 0xff, i) |
|||
value >>= 8 |
|||
} |
|||
|
|||
if (buffer[size - 1] & 0x80) { |
|||
buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1) |
|||
} else if (negative) { |
|||
buffer[size - 1] |= 0x80 |
|||
} |
|||
|
|||
return buffer |
|||
exports.decode = decode; |
|||
function scriptNumSize(i) { |
|||
return i > 0x7fffffff |
|||
? 5 |
|||
: i > 0x7fffff |
|||
? 4 |
|||
: i > 0x7fff |
|||
? 3 |
|||
: i > 0x7f |
|||
? 2 |
|||
: i > 0x00 |
|||
? 1 |
|||
: 0; |
|||
} |
|||
|
|||
module.exports = { |
|||
decode: decode, |
|||
encode: encode |
|||
function encode(_number) { |
|||
let value = Math.abs(_number); |
|||
const size = scriptNumSize(value); |
|||
const buffer = Buffer.allocUnsafe(size); |
|||
const negative = _number < 0; |
|||
for (let i = 0; i < size; ++i) { |
|||
buffer.writeUInt8(value & 0xff, i); |
|||
value >>= 8; |
|||
} |
|||
if (buffer[size - 1] & 0x80) { |
|||
buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); |
|||
} |
|||
else if (negative) { |
|||
buffer[size - 1] |= 0x80; |
|||
} |
|||
return buffer; |
|||
} |
|||
exports.encode = encode; |
|||
|
@ -1,64 +1,53 @@ |
|||
const bip66 = require('bip66') |
|||
const Buffer = require('safe-buffer').Buffer |
|||
const typeforce = require('typeforce') |
|||
const types = require('./types') |
|||
|
|||
const ZERO = Buffer.alloc(1, 0) |
|||
function toDER (x) { |
|||
let i = 0 |
|||
while (x[i] === 0) ++i |
|||
if (i === x.length) return ZERO |
|||
x = x.slice(i) |
|||
if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length) |
|||
return x |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const types = require("./types"); |
|||
const bip66 = require('bip66'); |
|||
const typeforce = require('typeforce'); |
|||
const ZERO = Buffer.alloc(1, 0); |
|||
function toDER(x) { |
|||
let i = 0; |
|||
while (x[i] === 0) |
|||
++i; |
|||
if (i === x.length) |
|||
return ZERO; |
|||
x = x.slice(i); |
|||
if (x[0] & 0x80) |
|||
return Buffer.concat([ZERO, x], 1 + x.length); |
|||
return x; |
|||
} |
|||
|
|||
function fromDER (x) { |
|||
if (x[0] === 0x00) x = x.slice(1) |
|||
const buffer = Buffer.alloc(32, 0) |
|||
const bstart = Math.max(0, 32 - x.length) |
|||
x.copy(buffer, bstart) |
|||
return buffer |
|||
function fromDER(x) { |
|||
if (x[0] === 0x00) |
|||
x = x.slice(1); |
|||
const buffer = Buffer.alloc(32, 0); |
|||
const bstart = Math.max(0, 32 - x.length); |
|||
x.copy(buffer, bstart); |
|||
return buffer; |
|||
} |
|||
|
|||
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
|
|||
function decode (buffer) { |
|||
const hashType = buffer.readUInt8(buffer.length - 1) |
|||
const hashTypeMod = hashType & ~0x80 |
|||
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) |
|||
|
|||
const decode = bip66.decode(buffer.slice(0, -1)) |
|||
const r = fromDER(decode.r) |
|||
const s = fromDER(decode.s) |
|||
|
|||
return { |
|||
signature: Buffer.concat([r, s], 64), |
|||
hashType: hashType |
|||
} |
|||
function decode(buffer) { |
|||
const hashType = buffer.readUInt8(buffer.length - 1); |
|||
const hashTypeMod = hashType & ~0x80; |
|||
if (hashTypeMod <= 0 || hashTypeMod >= 4) |
|||
throw new Error('Invalid hashType ' + hashType); |
|||
const decoded = bip66.decode(buffer.slice(0, -1)); |
|||
const r = fromDER(decoded.r); |
|||
const s = fromDER(decoded.s); |
|||
const signature = Buffer.concat([r, s], 64); |
|||
return { signature, hashType }; |
|||
} |
|||
|
|||
function encode (signature, hashType) { |
|||
typeforce({ |
|||
signature: types.BufferN(64), |
|||
hashType: types.UInt8 |
|||
}, { signature, hashType }) |
|||
|
|||
const hashTypeMod = hashType & ~0x80 |
|||
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) |
|||
|
|||
const hashTypeBuffer = Buffer.allocUnsafe(1) |
|||
hashTypeBuffer.writeUInt8(hashType, 0) |
|||
|
|||
const r = toDER(signature.slice(0, 32)) |
|||
const s = toDER(signature.slice(32, 64)) |
|||
|
|||
return Buffer.concat([ |
|||
bip66.encode(r, s), |
|||
hashTypeBuffer |
|||
]) |
|||
} |
|||
|
|||
module.exports = { |
|||
decode: decode, |
|||
encode: encode |
|||
exports.decode = decode; |
|||
function encode(signature, hashType) { |
|||
typeforce({ |
|||
signature: types.BufferN(64), |
|||
hashType: types.UInt8, |
|||
}, { signature, hashType }); |
|||
const hashTypeMod = hashType & ~0x80; |
|||
if (hashTypeMod <= 0 || hashTypeMod >= 4) |
|||
throw new Error('Invalid hashType ' + hashType); |
|||
const hashTypeBuffer = Buffer.allocUnsafe(1); |
|||
hashTypeBuffer.writeUInt8(hashType, 0); |
|||
const r = toDER(signature.slice(0, 32)); |
|||
const s = toDER(signature.slice(32, 64)); |
|||
return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); |
|||
} |
|||
exports.encode = encode; |
|||
|
@ -1,4 +1,6 @@ |
|||
module.exports = { |
|||
input: require('./input'), |
|||
output: require('./output') |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const input = require("./input"); |
|||
exports.input = input; |
|||
const output = require("./output"); |
|||
exports.output = output; |
|||
|
@ -1,23 +1,23 @@ |
|||
"use strict"; |
|||
// OP_0 [signatures ...]
|
|||
|
|||
const bscript = require('../../script') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
function partialSignature (value) { |
|||
return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value) |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
function partialSignature(value) { |
|||
return (value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value)); |
|||
} |
|||
|
|||
function check (script, allowIncomplete) { |
|||
const chunks = bscript.decompile(script) |
|||
if (chunks.length < 2) return false |
|||
if (chunks[0] !== OPS.OP_0) return false |
|||
|
|||
if (allowIncomplete) { |
|||
return chunks.slice(1).every(partialSignature) |
|||
} |
|||
|
|||
return chunks.slice(1).every(bscript.isCanonicalScriptSignature) |
|||
function check(script, allowIncomplete) { |
|||
const chunks = bscript.decompile(script); |
|||
if (chunks.length < 2) |
|||
return false; |
|||
if (chunks[0] !== script_1.OPS.OP_0) |
|||
return false; |
|||
if (allowIncomplete) { |
|||
return chunks.slice(1).every(partialSignature); |
|||
} |
|||
return chunks.slice(1).every(bscript.isCanonicalScriptSignature); |
|||
} |
|||
check.toJSON = function () { return 'multisig input' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'multisig input'; |
|||
}; |
|||
|
@ -1,29 +1,36 @@ |
|||
"use strict"; |
|||
// m [pubKeys ...] n OP_CHECKMULTISIG
|
|||
|
|||
const bscript = require('../../script') |
|||
const types = require('../../types') |
|||
const OPS = require('bitcoin-ops') |
|||
const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
|||
|
|||
function check (script, allowIncomplete) { |
|||
const chunks = bscript.decompile(script) |
|||
|
|||
if (chunks.length < 4) return false |
|||
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false |
|||
if (!types.Number(chunks[0])) return false |
|||
if (!types.Number(chunks[chunks.length - 2])) return false |
|||
const m = chunks[0] - OP_INT_BASE |
|||
const n = chunks[chunks.length - 2] - OP_INT_BASE |
|||
|
|||
if (m <= 0) return false |
|||
if (n > 16) return false |
|||
if (m > n) return false |
|||
if (n !== chunks.length - 3) return false |
|||
if (allowIncomplete) return true |
|||
|
|||
const keys = chunks.slice(1, -2) |
|||
return keys.every(bscript.isCanonicalPubKey) |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
const types = require("../../types"); |
|||
const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1
|
|||
function check(script, allowIncomplete) { |
|||
const chunks = bscript.decompile(script); |
|||
if (chunks.length < 4) |
|||
return false; |
|||
if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) |
|||
return false; |
|||
if (!types.Number(chunks[0])) |
|||
return false; |
|||
if (!types.Number(chunks[chunks.length - 2])) |
|||
return false; |
|||
const m = chunks[0] - OP_INT_BASE; |
|||
const n = chunks[chunks.length - 2] - OP_INT_BASE; |
|||
if (m <= 0) |
|||
return false; |
|||
if (n > 16) |
|||
return false; |
|||
if (m > n) |
|||
return false; |
|||
if (n !== chunks.length - 3) |
|||
return false; |
|||
if (allowIncomplete) |
|||
return true; |
|||
const keys = chunks.slice(1, -2); |
|||
return keys.every(bscript.isCanonicalPubKey); |
|||
} |
|||
check.toJSON = function () { return 'multi-sig output' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'multi-sig output'; |
|||
}; |
|||
|
@ -1,14 +1,15 @@ |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
// OP_RETURN {data}
|
|||
|
|||
const bscript = require('../script') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
function check (script) { |
|||
const buffer = bscript.compile(script) |
|||
|
|||
return buffer.length > 1 && |
|||
buffer[0] === OPS.OP_RETURN |
|||
const bscript = require("../script"); |
|||
const OPS = bscript.OPS; |
|||
function check(script) { |
|||
const buffer = bscript.compile(script); |
|||
return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; |
|||
} |
|||
check.toJSON = function () { return 'null data output' } |
|||
|
|||
module.exports = { output: { check: check } } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'null data output'; |
|||
}; |
|||
const output = { check }; |
|||
exports.output = output; |
|||
|
@ -1,4 +1,6 @@ |
|||
module.exports = { |
|||
input: require('./input'), |
|||
output: require('./output') |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const input = require("./input"); |
|||
exports.input = input; |
|||
const output = require("./output"); |
|||
exports.output = output; |
|||
|
@ -1,15 +1,13 @@ |
|||
"use strict"; |
|||
// {signature}
|
|||
|
|||
const bscript = require('../../script') |
|||
|
|||
function check (script) { |
|||
const chunks = bscript.decompile(script) |
|||
|
|||
return chunks.length === 1 && |
|||
bscript.isCanonicalScriptSignature(chunks[0]) |
|||
} |
|||
check.toJSON = function () { return 'pubKey input' } |
|||
|
|||
module.exports = { |
|||
check: check |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
function check(script) { |
|||
const chunks = bscript.decompile(script); |
|||
return (chunks.length === 1 && |
|||
bscript.isCanonicalScriptSignature(chunks[0])); |
|||
} |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'pubKey input'; |
|||
}; |
|||
|
@ -1,15 +1,15 @@ |
|||
"use strict"; |
|||
// {pubKey} OP_CHECKSIG
|
|||
|
|||
const bscript = require('../../script') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
function check (script) { |
|||
const chunks = bscript.decompile(script) |
|||
|
|||
return chunks.length === 2 && |
|||
bscript.isCanonicalPubKey(chunks[0]) && |
|||
chunks[1] === OPS.OP_CHECKSIG |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
function check(script) { |
|||
const chunks = bscript.decompile(script); |
|||
return (chunks.length === 2 && |
|||
bscript.isCanonicalPubKey(chunks[0]) && |
|||
chunks[1] === script_1.OPS.OP_CHECKSIG); |
|||
} |
|||
check.toJSON = function () { return 'pubKey output' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'pubKey output'; |
|||
}; |
|||
|
@ -1,4 +1,6 @@ |
|||
module.exports = { |
|||
input: require('./input'), |
|||
output: require('./output') |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const input = require("./input"); |
|||
exports.input = input; |
|||
const output = require("./output"); |
|||
exports.output = output; |
|||
|
@ -1,14 +1,14 @@ |
|||
"use strict"; |
|||
// {signature} {pubKey}
|
|||
|
|||
const bscript = require('../../script') |
|||
|
|||
function check (script) { |
|||
const chunks = bscript.decompile(script) |
|||
|
|||
return chunks.length === 2 && |
|||
bscript.isCanonicalScriptSignature(chunks[0]) && |
|||
bscript.isCanonicalPubKey(chunks[1]) |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
function check(script) { |
|||
const chunks = bscript.decompile(script); |
|||
return (chunks.length === 2 && |
|||
bscript.isCanonicalScriptSignature(chunks[0]) && |
|||
bscript.isCanonicalPubKey(chunks[1])); |
|||
} |
|||
check.toJSON = function () { return 'pubKeyHash input' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'pubKeyHash input'; |
|||
}; |
|||
|
@ -1,18 +1,18 @@ |
|||
"use strict"; |
|||
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
|
|||
|
|||
const bscript = require('../../script') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
function check (script) { |
|||
const buffer = bscript.compile(script) |
|||
|
|||
return buffer.length === 25 && |
|||
buffer[0] === OPS.OP_DUP && |
|||
buffer[1] === OPS.OP_HASH160 && |
|||
buffer[2] === 0x14 && |
|||
buffer[23] === OPS.OP_EQUALVERIFY && |
|||
buffer[24] === OPS.OP_CHECKSIG |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
function check(script) { |
|||
const buffer = bscript.compile(script); |
|||
return (buffer.length === 25 && |
|||
buffer[0] === script_1.OPS.OP_DUP && |
|||
buffer[1] === script_1.OPS.OP_HASH160 && |
|||
buffer[2] === 0x14 && |
|||
buffer[23] === script_1.OPS.OP_EQUALVERIFY && |
|||
buffer[24] === script_1.OPS.OP_CHECKSIG); |
|||
} |
|||
check.toJSON = function () { return 'pubKeyHash output' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'pubKeyHash output'; |
|||
}; |
|||
|
@ -1,4 +1,6 @@ |
|||
module.exports = { |
|||
input: require('./input'), |
|||
output: require('./output') |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const input = require("./input"); |
|||
exports.input = input; |
|||
const output = require("./output"); |
|||
exports.output = output; |
|||
|
@ -1,48 +1,44 @@ |
|||
"use strict"; |
|||
// <scriptSig> {serialized scriptPubKey script}
|
|||
|
|||
const Buffer = require('safe-buffer').Buffer |
|||
const bscript = require('../../script') |
|||
|
|||
const p2ms = require('../multisig/') |
|||
const p2pk = require('../pubkey/') |
|||
const p2pkh = require('../pubkeyhash/') |
|||
const p2wpkho = require('../witnesspubkeyhash/output') |
|||
const p2wsho = require('../witnessscripthash/output') |
|||
|
|||
function check (script, allowIncomplete) { |
|||
const chunks = bscript.decompile(script) |
|||
if (chunks.length < 1) return false |
|||
|
|||
const lastChunk = chunks[chunks.length - 1] |
|||
if (!Buffer.isBuffer(lastChunk)) return false |
|||
|
|||
const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))) |
|||
const redeemScriptChunks = bscript.decompile(lastChunk) |
|||
|
|||
// is redeemScript a valid script?
|
|||
if (!redeemScriptChunks) return false |
|||
|
|||
// is redeemScriptSig push only?
|
|||
if (!bscript.isPushOnly(scriptSigChunks)) return false |
|||
|
|||
// is witness?
|
|||
if (chunks.length === 1) { |
|||
return p2wsho.check(redeemScriptChunks) || |
|||
p2wpkho.check(redeemScriptChunks) |
|||
} |
|||
|
|||
// match types
|
|||
if (p2pkh.input.check(scriptSigChunks) && |
|||
p2pkh.output.check(redeemScriptChunks)) return true |
|||
|
|||
if (p2ms.input.check(scriptSigChunks, allowIncomplete) && |
|||
p2ms.output.check(redeemScriptChunks)) return true |
|||
|
|||
if (p2pk.input.check(scriptSigChunks) && |
|||
p2pk.output.check(redeemScriptChunks)) return true |
|||
|
|||
return false |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const p2ms = require("../multisig"); |
|||
const p2pk = require("../pubkey"); |
|||
const p2pkh = require("../pubkeyhash"); |
|||
const p2wpkho = require("../witnesspubkeyhash/output"); |
|||
const p2wsho = require("../witnessscripthash/output"); |
|||
function check(script, allowIncomplete) { |
|||
const chunks = bscript.decompile(script); |
|||
if (chunks.length < 1) |
|||
return false; |
|||
const lastChunk = chunks[chunks.length - 1]; |
|||
if (!Buffer.isBuffer(lastChunk)) |
|||
return false; |
|||
const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))); |
|||
const redeemScriptChunks = bscript.decompile(lastChunk); |
|||
// is redeemScript a valid script?
|
|||
if (!redeemScriptChunks) |
|||
return false; |
|||
// is redeemScriptSig push only?
|
|||
if (!bscript.isPushOnly(scriptSigChunks)) |
|||
return false; |
|||
// is witness?
|
|||
if (chunks.length === 1) { |
|||
return (p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks)); |
|||
} |
|||
// match types
|
|||
if (p2pkh.input.check(scriptSigChunks) && |
|||
p2pkh.output.check(redeemScriptChunks)) |
|||
return true; |
|||
if (p2ms.input.check(scriptSigChunks, allowIncomplete) && |
|||
p2ms.output.check(redeemScriptChunks)) |
|||
return true; |
|||
if (p2pk.input.check(scriptSigChunks) && |
|||
p2pk.output.check(redeemScriptChunks)) |
|||
return true; |
|||
return false; |
|||
} |
|||
check.toJSON = function () { return 'scriptHash input' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'scriptHash input'; |
|||
}; |
|||
|
@ -1,16 +1,16 @@ |
|||
"use strict"; |
|||
// OP_HASH160 {scriptHash} OP_EQUAL
|
|||
|
|||
const bscript = require('../../script') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
function check (script) { |
|||
const buffer = bscript.compile(script) |
|||
|
|||
return buffer.length === 23 && |
|||
buffer[0] === OPS.OP_HASH160 && |
|||
buffer[1] === 0x14 && |
|||
buffer[22] === OPS.OP_EQUAL |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
function check(script) { |
|||
const buffer = bscript.compile(script); |
|||
return (buffer.length === 23 && |
|||
buffer[0] === script_1.OPS.OP_HASH160 && |
|||
buffer[1] === 0x14 && |
|||
buffer[22] === script_1.OPS.OP_EQUAL); |
|||
} |
|||
check.toJSON = function () { return 'scriptHash output' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'scriptHash output'; |
|||
}; |
|||
|
@ -1,3 +1,4 @@ |
|||
module.exports = { |
|||
output: require('./output') |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const output = require("./output"); |
|||
exports.output = output; |
|||
|
@ -1,42 +1,32 @@ |
|||
"use strict"; |
|||
// OP_RETURN {aa21a9ed} {commitment}
|
|||
|
|||
const Buffer = require('safe-buffer').Buffer |
|||
const bscript = require('../../script') |
|||
const types = require('../../types') |
|||
const typeforce = require('typeforce') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
const HEADER = Buffer.from('aa21a9ed', 'hex') |
|||
|
|||
function check (script) { |
|||
const buffer = bscript.compile(script) |
|||
|
|||
return buffer.length > 37 && |
|||
buffer[0] === OPS.OP_RETURN && |
|||
buffer[1] === 0x24 && |
|||
buffer.slice(2, 6).equals(HEADER) |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
const types = require("../../types"); |
|||
const typeforce = require('typeforce'); |
|||
const HEADER = Buffer.from('aa21a9ed', 'hex'); |
|||
function check(script) { |
|||
const buffer = bscript.compile(script); |
|||
return (buffer.length > 37 && |
|||
buffer[0] === script_1.OPS.OP_RETURN && |
|||
buffer[1] === 0x24 && |
|||
buffer.slice(2, 6).equals(HEADER)); |
|||
} |
|||
|
|||
check.toJSON = function () { return 'Witness commitment output' } |
|||
|
|||
function encode (commitment) { |
|||
typeforce(types.Hash256bit, commitment) |
|||
|
|||
const buffer = Buffer.allocUnsafe(36) |
|||
HEADER.copy(buffer, 0) |
|||
commitment.copy(buffer, 4) |
|||
|
|||
return bscript.compile([OPS.OP_RETURN, buffer]) |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'Witness commitment output'; |
|||
}; |
|||
function encode(commitment) { |
|||
typeforce(types.Hash256bit, commitment); |
|||
const buffer = Buffer.allocUnsafe(36); |
|||
HEADER.copy(buffer, 0); |
|||
commitment.copy(buffer, 4); |
|||
return bscript.compile([script_1.OPS.OP_RETURN, buffer]); |
|||
} |
|||
|
|||
function decode (buffer) { |
|||
typeforce(check, buffer) |
|||
|
|||
return bscript.decompile(buffer)[1].slice(4, 36) |
|||
} |
|||
|
|||
module.exports = { |
|||
check: check, |
|||
decode: decode, |
|||
encode: encode |
|||
exports.encode = encode; |
|||
function decode(buffer) { |
|||
typeforce(check, buffer); |
|||
return bscript.decompile(buffer)[1].slice(4, 36); |
|||
} |
|||
exports.decode = decode; |
|||
|
@ -1,4 +1,6 @@ |
|||
module.exports = { |
|||
input: require('./input'), |
|||
output: require('./output') |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const input = require("./input"); |
|||
exports.input = input; |
|||
const output = require("./output"); |
|||
exports.output = output; |
|||
|
@ -1,18 +1,17 @@ |
|||
"use strict"; |
|||
// {signature} {pubKey}
|
|||
|
|||
const bscript = require('../../script') |
|||
|
|||
function isCompressedCanonicalPubKey (pubKey) { |
|||
return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33 |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
function isCompressedCanonicalPubKey(pubKey) { |
|||
return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; |
|||
} |
|||
|
|||
function check (script) { |
|||
const chunks = bscript.decompile(script) |
|||
|
|||
return chunks.length === 2 && |
|||
bscript.isCanonicalScriptSignature(chunks[0]) && |
|||
isCompressedCanonicalPubKey(chunks[1]) |
|||
function check(script) { |
|||
const chunks = bscript.decompile(script); |
|||
return (chunks.length === 2 && |
|||
bscript.isCanonicalScriptSignature(chunks[0]) && |
|||
isCompressedCanonicalPubKey(chunks[1])); |
|||
} |
|||
check.toJSON = function () { return 'witnessPubKeyHash input' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'witnessPubKeyHash input'; |
|||
}; |
|||
|
@ -1,17 +1,13 @@ |
|||
"use strict"; |
|||
// OP_0 {pubKeyHash}
|
|||
|
|||
const bscript = require('../../script') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
function check (script) { |
|||
const buffer = bscript.compile(script) |
|||
|
|||
return buffer.length === 22 && |
|||
buffer[0] === OPS.OP_0 && |
|||
buffer[1] === 0x14 |
|||
} |
|||
check.toJSON = function () { return 'Witness pubKeyHash output' } |
|||
|
|||
module.exports = { |
|||
check |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
function check(script) { |
|||
const buffer = bscript.compile(script); |
|||
return buffer.length === 22 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x14; |
|||
} |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'Witness pubKeyHash output'; |
|||
}; |
|||
|
@ -1,4 +1,6 @@ |
|||
module.exports = { |
|||
input: require('./input'), |
|||
output: require('./output') |
|||
} |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const input = require("./input"); |
|||
exports.input = input; |
|||
const output = require("./output"); |
|||
exports.output = output; |
|||
|
@ -1,39 +1,36 @@ |
|||
"use strict"; |
|||
// <scriptSig> {serialized scriptPubKey script}
|
|||
|
|||
const bscript = require('../../script') |
|||
const types = require('../../types') |
|||
const typeforce = require('typeforce') |
|||
|
|||
const p2ms = require('../multisig/') |
|||
const p2pk = require('../pubkey/') |
|||
const p2pkh = require('../pubkeyhash/') |
|||
|
|||
function check (chunks, allowIncomplete) { |
|||
typeforce(types.Array, chunks) |
|||
if (chunks.length < 1) return false |
|||
|
|||
const witnessScript = chunks[chunks.length - 1] |
|||
if (!Buffer.isBuffer(witnessScript)) return false |
|||
|
|||
const witnessScriptChunks = bscript.decompile(witnessScript) |
|||
|
|||
// is witnessScript a valid script?
|
|||
if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false |
|||
|
|||
const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)) |
|||
|
|||
// match types
|
|||
if (p2pkh.input.check(witnessRawScriptSig) && |
|||
p2pkh.output.check(witnessScriptChunks)) return true |
|||
|
|||
if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && |
|||
p2ms.output.check(witnessScriptChunks)) return true |
|||
|
|||
if (p2pk.input.check(witnessRawScriptSig) && |
|||
p2pk.output.check(witnessScriptChunks)) return true |
|||
|
|||
return false |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const typeforce = require('typeforce'); |
|||
const p2ms = require("../multisig"); |
|||
const p2pk = require("../pubkey"); |
|||
const p2pkh = require("../pubkeyhash"); |
|||
function check(chunks, allowIncomplete) { |
|||
typeforce(typeforce.Array, chunks); |
|||
if (chunks.length < 1) |
|||
return false; |
|||
const witnessScript = chunks[chunks.length - 1]; |
|||
if (!Buffer.isBuffer(witnessScript)) |
|||
return false; |
|||
const witnessScriptChunks = bscript.decompile(witnessScript); |
|||
// is witnessScript a valid script?
|
|||
if (!witnessScriptChunks || witnessScriptChunks.length === 0) |
|||
return false; |
|||
const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); |
|||
// match types
|
|||
if (p2pkh.input.check(witnessRawScriptSig) && |
|||
p2pkh.output.check(witnessScriptChunks)) |
|||
return true; |
|||
if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && |
|||
p2ms.output.check(witnessScriptChunks)) |
|||
return true; |
|||
if (p2pk.input.check(witnessRawScriptSig) && |
|||
p2pk.output.check(witnessScriptChunks)) |
|||
return true; |
|||
return false; |
|||
} |
|||
check.toJSON = function () { return 'witnessScriptHash input' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'witnessScriptHash input'; |
|||
}; |
|||
|
@ -1,15 +1,13 @@ |
|||
"use strict"; |
|||
// OP_0 {scriptHash}
|
|||
|
|||
const bscript = require('../../script') |
|||
const OPS = require('bitcoin-ops') |
|||
|
|||
function check (script) { |
|||
const buffer = bscript.compile(script) |
|||
|
|||
return buffer.length === 34 && |
|||
buffer[0] === OPS.OP_0 && |
|||
buffer[1] === 0x20 |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bscript = require("../../script"); |
|||
const script_1 = require("../../script"); |
|||
function check(script) { |
|||
const buffer = bscript.compile(script); |
|||
return buffer.length === 34 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x20; |
|||
} |
|||
check.toJSON = function () { return 'Witness scriptHash output' } |
|||
|
|||
module.exports = { check } |
|||
exports.check = check; |
|||
check.toJSON = () => { |
|||
return 'Witness scriptHash output'; |
|||
}; |
|||
|
@ -1,492 +1,452 @@ |
|||
const Buffer = require('safe-buffer').Buffer |
|||
const bcrypto = require('./crypto') |
|||
const bscript = require('./script') |
|||
const bufferutils = require('./bufferutils') |
|||
const opcodes = require('bitcoin-ops') |
|||
const typeforce = require('typeforce') |
|||
const types = require('./types') |
|||
const varuint = require('varuint-bitcoin') |
|||
|
|||
function varSliceSize (someScript) { |
|||
const length = someScript.length |
|||
|
|||
return varuint.encodingLength(length) + length |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bufferutils = require("./bufferutils"); |
|||
const bufferutils_1 = require("./bufferutils"); |
|||
const bcrypto = require("./crypto"); |
|||
const bscript = require("./script"); |
|||
const script_1 = require("./script"); |
|||
const types = require("./types"); |
|||
const typeforce = require('typeforce'); |
|||
const varuint = require('varuint-bitcoin'); |
|||
function varSliceSize(someScript) { |
|||
const length = someScript.length; |
|||
return varuint.encodingLength(length) + length; |
|||
} |
|||
|
|||
function vectorSize (someVector) { |
|||
const length = someVector.length |
|||
|
|||
return varuint.encodingLength(length) + someVector.reduce(function (sum, witness) { |
|||
return sum + varSliceSize(witness) |
|||
}, 0) |
|||
function vectorSize(someVector) { |
|||
const length = someVector.length; |
|||
return (varuint.encodingLength(length) + |
|||
someVector.reduce((sum, witness) => { |
|||
return sum + varSliceSize(witness); |
|||
}, 0)); |
|||
} |
|||
|
|||
function Transaction () { |
|||
this.version = 1 |
|||
this.locktime = 0 |
|||
this.ins = [] |
|||
this.outs = [] |
|||
} |
|||
|
|||
Transaction.DEFAULT_SEQUENCE = 0xffffffff |
|||
Transaction.SIGHASH_ALL = 0x01 |
|||
Transaction.SIGHASH_NONE = 0x02 |
|||
Transaction.SIGHASH_SINGLE = 0x03 |
|||
Transaction.SIGHASH_ANYONECANPAY = 0x80 |
|||
Transaction.ADVANCED_TRANSACTION_MARKER = 0x00 |
|||
Transaction.ADVANCED_TRANSACTION_FLAG = 0x01 |
|||
|
|||
const EMPTY_SCRIPT = Buffer.allocUnsafe(0) |
|||
const EMPTY_WITNESS = [] |
|||
const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') |
|||
const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') |
|||
const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex') |
|||
const EMPTY_SCRIPT = Buffer.allocUnsafe(0); |
|||
const EMPTY_WITNESS = []; |
|||
const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); |
|||
const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'); |
|||
const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex'); |
|||
const BLANK_OUTPUT = { |
|||
script: EMPTY_SCRIPT, |
|||
valueBuffer: VALUE_UINT64_MAX |
|||
script: EMPTY_SCRIPT, |
|||
valueBuffer: VALUE_UINT64_MAX, |
|||
}; |
|||
function isOutput(out) { |
|||
return out.value !== undefined; |
|||
} |
|||
|
|||
Transaction.fromBuffer = function (buffer, __noStrict) { |
|||
let offset = 0 |
|||
function readSlice (n) { |
|||
offset += n |
|||
return buffer.slice(offset - n, offset) |
|||
} |
|||
|
|||
function readUInt32 () { |
|||
const i = buffer.readUInt32LE(offset) |
|||
offset += 4 |
|||
return i |
|||
} |
|||
|
|||
function readInt32 () { |
|||
const i = buffer.readInt32LE(offset) |
|||
offset += 4 |
|||
return i |
|||
} |
|||
|
|||
function readUInt64 () { |
|||
const i = bufferutils.readUInt64LE(buffer, offset) |
|||
offset += 8 |
|||
return i |
|||
} |
|||
|
|||
function readVarInt () { |
|||
const vi = varuint.decode(buffer, offset) |
|||
offset += varuint.decode.bytes |
|||
return vi |
|||
} |
|||
|
|||
function readVarSlice () { |
|||
return readSlice(readVarInt()) |
|||
} |
|||
|
|||
function readVector () { |
|||
const count = readVarInt() |
|||
const vector = [] |
|||
for (var i = 0; i < count; i++) vector.push(readVarSlice()) |
|||
return vector |
|||
} |
|||
|
|||
const tx = new Transaction() |
|||
tx.version = readInt32() |
|||
|
|||
const marker = buffer.readUInt8(offset) |
|||
const flag = buffer.readUInt8(offset + 1) |
|||
|
|||
let hasWitnesses = false |
|||
if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && |
|||
flag === Transaction.ADVANCED_TRANSACTION_FLAG) { |
|||
offset += 2 |
|||
hasWitnesses = true |
|||
} |
|||
|
|||
const vinLen = readVarInt() |
|||
for (var i = 0; i < vinLen; ++i) { |
|||
tx.ins.push({ |
|||
hash: readSlice(32), |
|||
index: readUInt32(), |
|||
script: readVarSlice(), |
|||
sequence: readUInt32(), |
|||
witness: EMPTY_WITNESS |
|||
}) |
|||
} |
|||
|
|||
const voutLen = readVarInt() |
|||
for (i = 0; i < voutLen; ++i) { |
|||
tx.outs.push({ |
|||
value: readUInt64(), |
|||
script: readVarSlice() |
|||
}) |
|||
} |
|||
|
|||
if (hasWitnesses) { |
|||
for (i = 0; i < vinLen; ++i) { |
|||
tx.ins[i].witness = readVector() |
|||
class Transaction { |
|||
constructor() { |
|||
this.version = 1; |
|||
this.locktime = 0; |
|||
this.ins = []; |
|||
this.outs = []; |
|||
} |
|||
|
|||
// was this pointless?
|
|||
if (!tx.hasWitnesses()) throw new Error('Transaction has superfluous witness data') |
|||
} |
|||
|
|||
tx.locktime = readUInt32() |
|||
|
|||
if (__noStrict) return tx |
|||
if (offset !== buffer.length) throw new Error('Transaction has unexpected data') |
|||
|
|||
return tx |
|||
} |
|||
|
|||
Transaction.fromHex = function (hex) { |
|||
return Transaction.fromBuffer(Buffer.from(hex, 'hex')) |
|||
} |
|||
|
|||
Transaction.isCoinbaseHash = function (buffer) { |
|||
typeforce(types.Hash256bit, buffer) |
|||
for (var i = 0; i < 32; ++i) { |
|||
if (buffer[i] !== 0) return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
Transaction.prototype.isCoinbase = function () { |
|||
return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) |
|||
} |
|||
|
|||
Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { |
|||
typeforce(types.tuple( |
|||
types.Hash256bit, |
|||
types.UInt32, |
|||
types.maybe(types.UInt32), |
|||
types.maybe(types.Buffer) |
|||
), arguments) |
|||
|
|||
if (types.Null(sequence)) { |
|||
sequence = Transaction.DEFAULT_SEQUENCE |
|||
} |
|||
|
|||
// Add the input and return the input's index
|
|||
return (this.ins.push({ |
|||
hash: hash, |
|||
index: index, |
|||
script: scriptSig || EMPTY_SCRIPT, |
|||
sequence: sequence, |
|||
witness: EMPTY_WITNESS |
|||
}) - 1) |
|||
} |
|||
|
|||
Transaction.prototype.addOutput = function (scriptPubKey, value) { |
|||
typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) |
|||
|
|||
// Add the output and return the output's index
|
|||
return (this.outs.push({ |
|||
script: scriptPubKey, |
|||
value: value |
|||
}) - 1) |
|||
} |
|||
|
|||
Transaction.prototype.hasWitnesses = function () { |
|||
return this.ins.some(function (x) { |
|||
return x.witness.length !== 0 |
|||
}) |
|||
} |
|||
|
|||
Transaction.prototype.weight = function () { |
|||
const base = this.__byteLength(false) |
|||
const total = this.__byteLength(true) |
|||
return base * 3 + total |
|||
} |
|||
|
|||
Transaction.prototype.virtualSize = function () { |
|||
return Math.ceil(this.weight() / 4) |
|||
} |
|||
|
|||
Transaction.prototype.byteLength = function () { |
|||
return this.__byteLength(true) |
|||
} |
|||
|
|||
Transaction.prototype.__byteLength = function (__allowWitness) { |
|||
const hasWitnesses = __allowWitness && this.hasWitnesses() |
|||
|
|||
return ( |
|||
(hasWitnesses ? 10 : 8) + |
|||
varuint.encodingLength(this.ins.length) + |
|||
varuint.encodingLength(this.outs.length) + |
|||
this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) + |
|||
this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) + |
|||
(hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0) |
|||
) |
|||
} |
|||
|
|||
Transaction.prototype.clone = function () { |
|||
const newTx = new Transaction() |
|||
newTx.version = this.version |
|||
newTx.locktime = this.locktime |
|||
|
|||
newTx.ins = this.ins.map(function (txIn) { |
|||
return { |
|||
hash: txIn.hash, |
|||
index: txIn.index, |
|||
script: txIn.script, |
|||
sequence: txIn.sequence, |
|||
witness: txIn.witness |
|||
static fromBuffer(buffer, _NO_STRICT) { |
|||
let offset = 0; |
|||
function readSlice(n) { |
|||
offset += n; |
|||
return buffer.slice(offset - n, offset); |
|||
} |
|||
function readUInt32() { |
|||
const i = buffer.readUInt32LE(offset); |
|||
offset += 4; |
|||
return i; |
|||
} |
|||
function readInt32() { |
|||
const i = buffer.readInt32LE(offset); |
|||
offset += 4; |
|||
return i; |
|||
} |
|||
function readUInt64() { |
|||
const i = bufferutils.readUInt64LE(buffer, offset); |
|||
offset += 8; |
|||
return i; |
|||
} |
|||
function readVarInt() { |
|||
const vi = varuint.decode(buffer, offset); |
|||
offset += varuint.decode.bytes; |
|||
return vi; |
|||
} |
|||
function readVarSlice() { |
|||
return readSlice(readVarInt()); |
|||
} |
|||
function readVector() { |
|||
const count = readVarInt(); |
|||
const vector = []; |
|||
for (let i = 0; i < count; i++) |
|||
vector.push(readVarSlice()); |
|||
return vector; |
|||
} |
|||
const tx = new Transaction(); |
|||
tx.version = readInt32(); |
|||
const marker = buffer.readUInt8(offset); |
|||
const flag = buffer.readUInt8(offset + 1); |
|||
let hasWitnesses = false; |
|||
if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && |
|||
flag === Transaction.ADVANCED_TRANSACTION_FLAG) { |
|||
offset += 2; |
|||
hasWitnesses = true; |
|||
} |
|||
const vinLen = readVarInt(); |
|||
for (let i = 0; i < vinLen; ++i) { |
|||
tx.ins.push({ |
|||
hash: readSlice(32), |
|||
index: readUInt32(), |
|||
script: readVarSlice(), |
|||
sequence: readUInt32(), |
|||
witness: EMPTY_WITNESS, |
|||
}); |
|||
} |
|||
const voutLen = readVarInt(); |
|||
for (let i = 0; i < voutLen; ++i) { |
|||
tx.outs.push({ |
|||
value: readUInt64(), |
|||
script: readVarSlice(), |
|||
}); |
|||
} |
|||
if (hasWitnesses) { |
|||
for (let i = 0; i < vinLen; ++i) { |
|||
tx.ins[i].witness = readVector(); |
|||
} |
|||
// was this pointless?
|
|||
if (!tx.hasWitnesses()) |
|||
throw new Error('Transaction has superfluous witness data'); |
|||
} |
|||
tx.locktime = readUInt32(); |
|||
if (_NO_STRICT) |
|||
return tx; |
|||
if (offset !== buffer.length) |
|||
throw new Error('Transaction has unexpected data'); |
|||
return tx; |
|||
} |
|||
}) |
|||
|
|||
newTx.outs = this.outs.map(function (txOut) { |
|||
return { |
|||
script: txOut.script, |
|||
value: txOut.value |
|||
static fromHex(hex) { |
|||
return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false); |
|||
} |
|||
}) |
|||
|
|||
return newTx |
|||
} |
|||
|
|||
/** |
|||
* Hash transaction for signing a specific input. |
|||
* |
|||
* Bitcoin uses a different hash for each signed transaction input. |
|||
* This method copies the transaction, makes the necessary changes based on the |
|||
* hashType, and then hashes the result. |
|||
* This hash can then be used to sign the provided transaction input. |
|||
*/ |
|||
Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) { |
|||
typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) |
|||
|
|||
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
|
|||
if (inIndex >= this.ins.length) return ONE |
|||
|
|||
// ignore OP_CODESEPARATOR
|
|||
const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { |
|||
return x !== opcodes.OP_CODESEPARATOR |
|||
})) |
|||
|
|||
const txTmp = this.clone() |
|||
|
|||
// SIGHASH_NONE: ignore all outputs? (wildcard payee)
|
|||
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { |
|||
txTmp.outs = [] |
|||
|
|||
// ignore sequence numbers (except at inIndex)
|
|||
txTmp.ins.forEach(function (input, i) { |
|||
if (i === inIndex) return |
|||
|
|||
input.sequence = 0 |
|||
}) |
|||
|
|||
// SIGHASH_SINGLE: ignore all outputs, except at the same index?
|
|||
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { |
|||
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
|
|||
if (inIndex >= this.outs.length) return ONE |
|||
|
|||
// truncate outputs after
|
|||
txTmp.outs.length = inIndex + 1 |
|||
|
|||
// "blank" outputs before
|
|||
for (var i = 0; i < inIndex; i++) { |
|||
txTmp.outs[i] = BLANK_OUTPUT |
|||
static isCoinbaseHash(buffer) { |
|||
typeforce(types.Hash256bit, buffer); |
|||
for (let i = 0; i < 32; ++i) { |
|||
if (buffer[i] !== 0) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// ignore sequence numbers (except at inIndex)
|
|||
txTmp.ins.forEach(function (input, y) { |
|||
if (y === inIndex) return |
|||
|
|||
input.sequence = 0 |
|||
}) |
|||
} |
|||
|
|||
// SIGHASH_ANYONECANPAY: ignore inputs entirely?
|
|||
if (hashType & Transaction.SIGHASH_ANYONECANPAY) { |
|||
txTmp.ins = [txTmp.ins[inIndex]] |
|||
txTmp.ins[0].script = ourScript |
|||
|
|||
// SIGHASH_ALL: only ignore input scripts
|
|||
} else { |
|||
// "blank" others input scripts
|
|||
txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) |
|||
txTmp.ins[inIndex].script = ourScript |
|||
} |
|||
|
|||
// serialize and hash
|
|||
const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) |
|||
buffer.writeInt32LE(hashType, buffer.length - 4) |
|||
txTmp.__toBuffer(buffer, 0, false) |
|||
|
|||
return bcrypto.hash256(buffer) |
|||
} |
|||
|
|||
Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) { |
|||
typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) |
|||
|
|||
let tbuffer, toffset |
|||
function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) } |
|||
function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) } |
|||
function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } |
|||
function writeVarInt (i) { |
|||
varuint.encode(i, tbuffer, toffset) |
|||
toffset += varuint.encode.bytes |
|||
} |
|||
function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } |
|||
|
|||
let hashOutputs = ZERO |
|||
let hashPrevouts = ZERO |
|||
let hashSequence = ZERO |
|||
|
|||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { |
|||
tbuffer = Buffer.allocUnsafe(36 * this.ins.length) |
|||
toffset = 0 |
|||
|
|||
this.ins.forEach(function (txIn) { |
|||
writeSlice(txIn.hash) |
|||
writeUInt32(txIn.index) |
|||
}) |
|||
|
|||
hashPrevouts = bcrypto.hash256(tbuffer) |
|||
} |
|||
|
|||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && |
|||
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && |
|||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) { |
|||
tbuffer = Buffer.allocUnsafe(4 * this.ins.length) |
|||
toffset = 0 |
|||
|
|||
this.ins.forEach(function (txIn) { |
|||
writeUInt32(txIn.sequence) |
|||
}) |
|||
|
|||
hashSequence = bcrypto.hash256(tbuffer) |
|||
} |
|||
|
|||
if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && |
|||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) { |
|||
const txOutsSize = this.outs.reduce(function (sum, output) { |
|||
return sum + 8 + varSliceSize(output.script) |
|||
}, 0) |
|||
|
|||
tbuffer = Buffer.allocUnsafe(txOutsSize) |
|||
toffset = 0 |
|||
|
|||
this.outs.forEach(function (out) { |
|||
writeUInt64(out.value) |
|||
writeVarSlice(out.script) |
|||
}) |
|||
|
|||
hashOutputs = bcrypto.hash256(tbuffer) |
|||
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { |
|||
const output = this.outs[inIndex] |
|||
|
|||
tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) |
|||
toffset = 0 |
|||
writeUInt64(output.value) |
|||
writeVarSlice(output.script) |
|||
|
|||
hashOutputs = bcrypto.hash256(tbuffer) |
|||
} |
|||
|
|||
tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) |
|||
toffset = 0 |
|||
|
|||
const input = this.ins[inIndex] |
|||
writeUInt32(this.version) |
|||
writeSlice(hashPrevouts) |
|||
writeSlice(hashSequence) |
|||
writeSlice(input.hash) |
|||
writeUInt32(input.index) |
|||
writeVarSlice(prevOutScript) |
|||
writeUInt64(value) |
|||
writeUInt32(input.sequence) |
|||
writeSlice(hashOutputs) |
|||
writeUInt32(this.locktime) |
|||
writeUInt32(hashType) |
|||
return bcrypto.hash256(tbuffer) |
|||
} |
|||
|
|||
Transaction.prototype.getHash = function () { |
|||
return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) |
|||
} |
|||
|
|||
Transaction.prototype.getId = function () { |
|||
// transaction hash's are displayed in reverse order
|
|||
return this.getHash().reverse().toString('hex') |
|||
} |
|||
|
|||
Transaction.prototype.toBuffer = function (buffer, initialOffset) { |
|||
return this.__toBuffer(buffer, initialOffset, true) |
|||
} |
|||
|
|||
Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitness) { |
|||
if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) |
|||
|
|||
let offset = initialOffset || 0 |
|||
function writeSlice (slice) { offset += slice.copy(buffer, offset) } |
|||
function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) } |
|||
function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } |
|||
function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } |
|||
function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } |
|||
function writeVarInt (i) { |
|||
varuint.encode(i, buffer, offset) |
|||
offset += varuint.encode.bytes |
|||
} |
|||
function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } |
|||
function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) } |
|||
|
|||
writeInt32(this.version) |
|||
|
|||
const hasWitnesses = __allowWitness && this.hasWitnesses() |
|||
|
|||
if (hasWitnesses) { |
|||
writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) |
|||
writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) |
|||
} |
|||
|
|||
writeVarInt(this.ins.length) |
|||
|
|||
this.ins.forEach(function (txIn) { |
|||
writeSlice(txIn.hash) |
|||
writeUInt32(txIn.index) |
|||
writeVarSlice(txIn.script) |
|||
writeUInt32(txIn.sequence) |
|||
}) |
|||
|
|||
writeVarInt(this.outs.length) |
|||
this.outs.forEach(function (txOut) { |
|||
if (!txOut.valueBuffer) { |
|||
writeUInt64(txOut.value) |
|||
} else { |
|||
writeSlice(txOut.valueBuffer) |
|||
isCoinbase() { |
|||
return (this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)); |
|||
} |
|||
addInput(hash, index, sequence, scriptSig) { |
|||
typeforce(types.tuple(types.Hash256bit, types.UInt32, types.maybe(types.UInt32), types.maybe(types.Buffer)), arguments); |
|||
if (types.Null(sequence)) { |
|||
sequence = Transaction.DEFAULT_SEQUENCE; |
|||
} |
|||
// Add the input and return the input's index
|
|||
return (this.ins.push({ |
|||
hash, |
|||
index, |
|||
script: scriptSig || EMPTY_SCRIPT, |
|||
sequence: sequence, |
|||
witness: EMPTY_WITNESS, |
|||
}) - 1); |
|||
} |
|||
addOutput(scriptPubKey, value) { |
|||
typeforce(types.tuple(types.Buffer, types.Satoshi), arguments); |
|||
// Add the output and return the output's index
|
|||
return (this.outs.push({ |
|||
script: scriptPubKey, |
|||
value, |
|||
}) - 1); |
|||
} |
|||
hasWitnesses() { |
|||
return this.ins.some(x => { |
|||
return x.witness.length !== 0; |
|||
}); |
|||
} |
|||
weight() { |
|||
const base = this.__byteLength(false); |
|||
const total = this.__byteLength(true); |
|||
return base * 3 + total; |
|||
} |
|||
virtualSize() { |
|||
return Math.ceil(this.weight() / 4); |
|||
} |
|||
byteLength() { |
|||
return this.__byteLength(true); |
|||
} |
|||
clone() { |
|||
const newTx = new Transaction(); |
|||
newTx.version = this.version; |
|||
newTx.locktime = this.locktime; |
|||
newTx.ins = this.ins.map(txIn => { |
|||
return { |
|||
hash: txIn.hash, |
|||
index: txIn.index, |
|||
script: txIn.script, |
|||
sequence: txIn.sequence, |
|||
witness: txIn.witness, |
|||
}; |
|||
}); |
|||
newTx.outs = this.outs.map(txOut => { |
|||
return { |
|||
script: txOut.script, |
|||
value: txOut.value, |
|||
}; |
|||
}); |
|||
return newTx; |
|||
} |
|||
/** |
|||
* Hash transaction for signing a specific input. |
|||
* |
|||
* Bitcoin uses a different hash for each signed transaction input. |
|||
* This method copies the transaction, makes the necessary changes based on the |
|||
* hashType, and then hashes the result. |
|||
* This hash can then be used to sign the provided transaction input. |
|||
*/ |
|||
hashForSignature(inIndex, prevOutScript, hashType) { |
|||
typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments); |
|||
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
|
|||
if (inIndex >= this.ins.length) |
|||
return ONE; |
|||
// ignore OP_CODESEPARATOR
|
|||
const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(x => { |
|||
return x !== script_1.OPS.OP_CODESEPARATOR; |
|||
})); |
|||
const txTmp = this.clone(); |
|||
// SIGHASH_NONE: ignore all outputs? (wildcard payee)
|
|||
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { |
|||
txTmp.outs = []; |
|||
// ignore sequence numbers (except at inIndex)
|
|||
txTmp.ins.forEach((input, i) => { |
|||
if (i === inIndex) |
|||
return; |
|||
input.sequence = 0; |
|||
}); |
|||
// SIGHASH_SINGLE: ignore all outputs, except at the same index?
|
|||
} |
|||
else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { |
|||
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
|
|||
if (inIndex >= this.outs.length) |
|||
return ONE; |
|||
// truncate outputs after
|
|||
txTmp.outs.length = inIndex + 1; |
|||
// "blank" outputs before
|
|||
for (let i = 0; i < inIndex; i++) { |
|||
txTmp.outs[i] = BLANK_OUTPUT; |
|||
} |
|||
// ignore sequence numbers (except at inIndex)
|
|||
txTmp.ins.forEach((input, y) => { |
|||
if (y === inIndex) |
|||
return; |
|||
input.sequence = 0; |
|||
}); |
|||
} |
|||
// SIGHASH_ANYONECANPAY: ignore inputs entirely?
|
|||
if (hashType & Transaction.SIGHASH_ANYONECANPAY) { |
|||
txTmp.ins = [txTmp.ins[inIndex]]; |
|||
txTmp.ins[0].script = ourScript; |
|||
// SIGHASH_ALL: only ignore input scripts
|
|||
} |
|||
else { |
|||
// "blank" others input scripts
|
|||
txTmp.ins.forEach(input => { |
|||
input.script = EMPTY_SCRIPT; |
|||
}); |
|||
txTmp.ins[inIndex].script = ourScript; |
|||
} |
|||
// serialize and hash
|
|||
const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4); |
|||
buffer.writeInt32LE(hashType, buffer.length - 4); |
|||
txTmp.__toBuffer(buffer, 0, false); |
|||
return bcrypto.hash256(buffer); |
|||
} |
|||
hashForWitnessV0(inIndex, prevOutScript, value, hashType) { |
|||
typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments); |
|||
let tbuffer = Buffer.from([]); |
|||
let toffset = 0; |
|||
function writeSlice(slice) { |
|||
toffset += slice.copy(tbuffer, toffset); |
|||
} |
|||
function writeUInt32(i) { |
|||
toffset = tbuffer.writeUInt32LE(i, toffset); |
|||
} |
|||
function writeUInt64(i) { |
|||
toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset); |
|||
} |
|||
function writeVarInt(i) { |
|||
varuint.encode(i, tbuffer, toffset); |
|||
toffset += varuint.encode.bytes; |
|||
} |
|||
function writeVarSlice(slice) { |
|||
writeVarInt(slice.length); |
|||
writeSlice(slice); |
|||
} |
|||
let hashOutputs = ZERO; |
|||
let hashPrevouts = ZERO; |
|||
let hashSequence = ZERO; |
|||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { |
|||
tbuffer = Buffer.allocUnsafe(36 * this.ins.length); |
|||
toffset = 0; |
|||
this.ins.forEach(txIn => { |
|||
writeSlice(txIn.hash); |
|||
writeUInt32(txIn.index); |
|||
}); |
|||
hashPrevouts = bcrypto.hash256(tbuffer); |
|||
} |
|||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && |
|||
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && |
|||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) { |
|||
tbuffer = Buffer.allocUnsafe(4 * this.ins.length); |
|||
toffset = 0; |
|||
this.ins.forEach(txIn => { |
|||
writeUInt32(txIn.sequence); |
|||
}); |
|||
hashSequence = bcrypto.hash256(tbuffer); |
|||
} |
|||
if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && |
|||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) { |
|||
const txOutsSize = this.outs.reduce((sum, output) => { |
|||
return sum + 8 + varSliceSize(output.script); |
|||
}, 0); |
|||
tbuffer = Buffer.allocUnsafe(txOutsSize); |
|||
toffset = 0; |
|||
this.outs.forEach(out => { |
|||
writeUInt64(out.value); |
|||
writeVarSlice(out.script); |
|||
}); |
|||
hashOutputs = bcrypto.hash256(tbuffer); |
|||
} |
|||
else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && |
|||
inIndex < this.outs.length) { |
|||
const output = this.outs[inIndex]; |
|||
tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); |
|||
toffset = 0; |
|||
writeUInt64(output.value); |
|||
writeVarSlice(output.script); |
|||
hashOutputs = bcrypto.hash256(tbuffer); |
|||
} |
|||
tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); |
|||
toffset = 0; |
|||
const input = this.ins[inIndex]; |
|||
writeUInt32(this.version); |
|||
writeSlice(hashPrevouts); |
|||
writeSlice(hashSequence); |
|||
writeSlice(input.hash); |
|||
writeUInt32(input.index); |
|||
writeVarSlice(prevOutScript); |
|||
writeUInt64(value); |
|||
writeUInt32(input.sequence); |
|||
writeSlice(hashOutputs); |
|||
writeUInt32(this.locktime); |
|||
writeUInt32(hashType); |
|||
return bcrypto.hash256(tbuffer); |
|||
} |
|||
getHash(forWitness) { |
|||
// wtxid for coinbase is always 32 bytes of 0x00
|
|||
if (forWitness && this.isCoinbase()) |
|||
return Buffer.alloc(32, 0); |
|||
return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)); |
|||
} |
|||
getId() { |
|||
// transaction hash's are displayed in reverse order
|
|||
return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex'); |
|||
} |
|||
toBuffer(buffer, initialOffset) { |
|||
return this.__toBuffer(buffer, initialOffset, true); |
|||
} |
|||
toHex() { |
|||
return this.toBuffer(undefined, undefined).toString('hex'); |
|||
} |
|||
setInputScript(index, scriptSig) { |
|||
typeforce(types.tuple(types.Number, types.Buffer), arguments); |
|||
this.ins[index].script = scriptSig; |
|||
} |
|||
setWitness(index, witness) { |
|||
typeforce(types.tuple(types.Number, [types.Buffer]), arguments); |
|||
this.ins[index].witness = witness; |
|||
} |
|||
__byteLength(_ALLOW_WITNESS) { |
|||
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); |
|||
return ((hasWitnesses ? 10 : 8) + |
|||
varuint.encodingLength(this.ins.length) + |
|||
varuint.encodingLength(this.outs.length) + |
|||
this.ins.reduce((sum, input) => { |
|||
return sum + 40 + varSliceSize(input.script); |
|||
}, 0) + |
|||
this.outs.reduce((sum, output) => { |
|||
return sum + 8 + varSliceSize(output.script); |
|||
}, 0) + |
|||
(hasWitnesses |
|||
? this.ins.reduce((sum, input) => { |
|||
return sum + vectorSize(input.witness); |
|||
}, 0) |
|||
: 0)); |
|||
} |
|||
__toBuffer(buffer, initialOffset, _ALLOW_WITNESS) { |
|||
if (!buffer) |
|||
buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS)); |
|||
let offset = initialOffset || 0; |
|||
function writeSlice(slice) { |
|||
offset += slice.copy(buffer, offset); |
|||
} |
|||
function writeUInt8(i) { |
|||
offset = buffer.writeUInt8(i, offset); |
|||
} |
|||
function writeUInt32(i) { |
|||
offset = buffer.writeUInt32LE(i, offset); |
|||
} |
|||
function writeInt32(i) { |
|||
offset = buffer.writeInt32LE(i, offset); |
|||
} |
|||
function writeUInt64(i) { |
|||
offset = bufferutils.writeUInt64LE(buffer, i, offset); |
|||
} |
|||
function writeVarInt(i) { |
|||
varuint.encode(i, buffer, offset); |
|||
offset += varuint.encode.bytes; |
|||
} |
|||
function writeVarSlice(slice) { |
|||
writeVarInt(slice.length); |
|||
writeSlice(slice); |
|||
} |
|||
function writeVector(vector) { |
|||
writeVarInt(vector.length); |
|||
vector.forEach(writeVarSlice); |
|||
} |
|||
writeInt32(this.version); |
|||
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); |
|||
if (hasWitnesses) { |
|||
writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); |
|||
writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); |
|||
} |
|||
writeVarInt(this.ins.length); |
|||
this.ins.forEach(txIn => { |
|||
writeSlice(txIn.hash); |
|||
writeUInt32(txIn.index); |
|||
writeVarSlice(txIn.script); |
|||
writeUInt32(txIn.sequence); |
|||
}); |
|||
writeVarInt(this.outs.length); |
|||
this.outs.forEach(txOut => { |
|||
if (isOutput(txOut)) { |
|||
writeUInt64(txOut.value); |
|||
} |
|||
else { |
|||
writeSlice(txOut.valueBuffer); |
|||
} |
|||
writeVarSlice(txOut.script); |
|||
}); |
|||
if (hasWitnesses) { |
|||
this.ins.forEach(input => { |
|||
writeVector(input.witness); |
|||
}); |
|||
} |
|||
writeUInt32(this.locktime); |
|||
// avoid slicing unless necessary
|
|||
if (initialOffset !== undefined) |
|||
return buffer.slice(initialOffset, offset); |
|||
return buffer; |
|||
} |
|||
|
|||
writeVarSlice(txOut.script) |
|||
}) |
|||
|
|||
if (hasWitnesses) { |
|||
this.ins.forEach(function (input) { |
|||
writeVector(input.witness) |
|||
}) |
|||
} |
|||
|
|||
writeUInt32(this.locktime) |
|||
|
|||
// avoid slicing unless necessary
|
|||
if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) |
|||
return buffer |
|||
} |
|||
|
|||
Transaction.prototype.toHex = function () { |
|||
return this.toBuffer().toString('hex') |
|||
} |
|||
|
|||
Transaction.prototype.setInputScript = function (index, scriptSig) { |
|||
typeforce(types.tuple(types.Number, types.Buffer), arguments) |
|||
|
|||
this.ins[index].script = scriptSig |
|||
} |
|||
|
|||
Transaction.prototype.setWitness = function (index, witness) { |
|||
typeforce(types.tuple(types.Number, [types.Buffer]), arguments) |
|||
|
|||
this.ins[index].witness = witness |
|||
} |
|||
|
|||
module.exports = Transaction |
|||
Transaction.DEFAULT_SEQUENCE = 0xffffffff; |
|||
Transaction.SIGHASH_ALL = 0x01; |
|||
Transaction.SIGHASH_NONE = 0x02; |
|||
Transaction.SIGHASH_SINGLE = 0x03; |
|||
Transaction.SIGHASH_ANYONECANPAY = 0x80; |
|||
Transaction.ADVANCED_TRANSACTION_MARKER = 0x00; |
|||
Transaction.ADVANCED_TRANSACTION_FLAG = 0x01; |
|||
exports.Transaction = Transaction; |
|||
|
File diff suppressed because it is too large
@ -1,49 +1,50 @@ |
|||
const typeforce = require('typeforce') |
|||
|
|||
const UINT31_MAX = Math.pow(2, 31) - 1 |
|||
function UInt31 (value) { |
|||
return typeforce.UInt32(value) && value <= UINT31_MAX |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const typeforce = require('typeforce'); |
|||
const UINT31_MAX = Math.pow(2, 31) - 1; |
|||
function UInt31(value) { |
|||
return typeforce.UInt32(value) && value <= UINT31_MAX; |
|||
} |
|||
|
|||
function BIP32Path (value) { |
|||
return typeforce.String(value) && value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) |
|||
exports.UInt31 = UInt31; |
|||
function BIP32Path(value) { |
|||
return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); |
|||
} |
|||
BIP32Path.toJSON = function () { return 'BIP32 derivation path' } |
|||
|
|||
const SATOSHI_MAX = 21 * 1e14 |
|||
function Satoshi (value) { |
|||
return typeforce.UInt53(value) && value <= SATOSHI_MAX |
|||
exports.BIP32Path = BIP32Path; |
|||
BIP32Path.toJSON = () => { |
|||
return 'BIP32 derivation path'; |
|||
}; |
|||
const SATOSHI_MAX = 21 * 1e14; |
|||
function Satoshi(value) { |
|||
return typeforce.UInt53(value) && value <= SATOSHI_MAX; |
|||
} |
|||
|
|||
exports.Satoshi = Satoshi; |
|||
// external dependent types
|
|||
const ECPoint = typeforce.quacksLike('Point') |
|||
|
|||
exports.ECPoint = typeforce.quacksLike('Point'); |
|||
// exposed, external API
|
|||
const Network = typeforce.compile({ |
|||
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), |
|||
bip32: { |
|||
public: typeforce.UInt32, |
|||
private: typeforce.UInt32 |
|||
}, |
|||
pubKeyHash: typeforce.UInt8, |
|||
scriptHash: typeforce.UInt8, |
|||
wif: typeforce.UInt8 |
|||
}) |
|||
|
|||
// extend typeforce types with ours
|
|||
const types = { |
|||
BIP32Path: BIP32Path, |
|||
Buffer256bit: typeforce.BufferN(32), |
|||
ECPoint: ECPoint, |
|||
Hash160bit: typeforce.BufferN(20), |
|||
Hash256bit: typeforce.BufferN(32), |
|||
Network: Network, |
|||
Satoshi: Satoshi, |
|||
UInt31: UInt31 |
|||
} |
|||
|
|||
for (var typeName in typeforce) { |
|||
types[typeName] = typeforce[typeName] |
|||
} |
|||
|
|||
module.exports = types |
|||
exports.Network = typeforce.compile({ |
|||
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), |
|||
bip32: { |
|||
public: typeforce.UInt32, |
|||
private: typeforce.UInt32, |
|||
}, |
|||
pubKeyHash: typeforce.UInt8, |
|||
scriptHash: typeforce.UInt8, |
|||
wif: typeforce.UInt8, |
|||
}); |
|||
exports.Buffer256bit = typeforce.BufferN(32); |
|||
exports.Hash160bit = typeforce.BufferN(20); |
|||
exports.Hash256bit = typeforce.BufferN(32); |
|||
exports.Number = typeforce.Number; // tslint:disable-line variable-name
|
|||
exports.Array = typeforce.Array; |
|||
exports.Boolean = typeforce.Boolean; // tslint:disable-line variable-name
|
|||
exports.String = typeforce.String; // tslint:disable-line variable-name
|
|||
exports.Buffer = typeforce.Buffer; |
|||
exports.Hex = typeforce.Hex; |
|||
exports.maybe = typeforce.maybe; |
|||
exports.tuple = typeforce.tuple; |
|||
exports.UInt8 = typeforce.UInt8; |
|||
exports.UInt32 = typeforce.UInt32; |
|||
exports.Function = typeforce.Function; |
|||
exports.BufferN = typeforce.BufferN; |
|||
exports.Null = typeforce.Null; |
|||
exports.oneOf = typeforce.oneOf; |
|||
|
@ -0,0 +1,119 @@ |
|||
import { Network } from './networks'; |
|||
import * as networks from './networks'; |
|||
import * as payments from './payments'; |
|||
import * as bscript from './script'; |
|||
import * as types from './types'; |
|||
|
|||
const bech32 = require('bech32'); |
|||
const bs58check = require('bs58check'); |
|||
const typeforce = require('typeforce'); |
|||
|
|||
export interface Base58CheckResult { |
|||
hash: Buffer; |
|||
version: number; |
|||
} |
|||
|
|||
export interface Bech32Result { |
|||
version: number; |
|||
prefix: string; |
|||
data: Buffer; |
|||
} |
|||
|
|||
export function fromBase58Check(address: string): Base58CheckResult { |
|||
const payload = bs58check.decode(address); |
|||
|
|||
// TODO: 4.0.0, move to "toOutputScript"
|
|||
if (payload.length < 21) throw new TypeError(address + ' is too short'); |
|||
if (payload.length > 21) throw new TypeError(address + ' is too long'); |
|||
|
|||
const version = payload.readUInt8(0); |
|||
const hash = payload.slice(1); |
|||
|
|||
return { version, hash }; |
|||
} |
|||
|
|||
export function fromBech32(address: string): Bech32Result { |
|||
const result = bech32.decode(address); |
|||
const data = bech32.fromWords(result.words.slice(1)); |
|||
|
|||
return { |
|||
version: result.words[0], |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data), |
|||
}; |
|||
} |
|||
|
|||
export function toBase58Check(hash: Buffer, version: number): string { |
|||
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); |
|||
|
|||
const payload = Buffer.allocUnsafe(21); |
|||
payload.writeUInt8(version, 0); |
|||
hash.copy(payload, 1); |
|||
|
|||
return bs58check.encode(payload); |
|||
} |
|||
|
|||
export function toBech32( |
|||
data: Buffer, |
|||
version: number, |
|||
prefix: string, |
|||
): string { |
|||
const words = bech32.toWords(data); |
|||
words.unshift(version); |
|||
|
|||
return bech32.encode(prefix, words); |
|||
} |
|||
|
|||
export function fromOutputScript(output: Buffer, network: Network): string { |
|||
// TODO: Network
|
|||
network = network || networks.bitcoin; |
|||
|
|||
try { |
|||
return payments.p2pkh({ output, network }).address as string; |
|||
} catch (e) {} |
|||
try { |
|||
return payments.p2sh({ output, network }).address as string; |
|||
} catch (e) {} |
|||
try { |
|||
return payments.p2wpkh({ output, network }).address as string; |
|||
} catch (e) {} |
|||
try { |
|||
return payments.p2wsh({ output, network }).address as string; |
|||
} catch (e) {} |
|||
|
|||
throw new Error(bscript.toASM(output) + ' has no matching Address'); |
|||
} |
|||
|
|||
export function toOutputScript(address: string, network: Network): Buffer { |
|||
network = network || networks.bitcoin; |
|||
|
|||
let decodeBase58: Base58CheckResult | undefined; |
|||
let decodeBech32: Bech32Result | undefined; |
|||
try { |
|||
decodeBase58 = fromBase58Check(address); |
|||
} catch (e) {} |
|||
|
|||
if (decodeBase58) { |
|||
if (decodeBase58.version === network.pubKeyHash) |
|||
return payments.p2pkh({ hash: decodeBase58.hash }).output as Buffer; |
|||
if (decodeBase58.version === network.scriptHash) |
|||
return payments.p2sh({ hash: decodeBase58.hash }).output as Buffer; |
|||
} else { |
|||
try { |
|||
decodeBech32 = fromBech32(address); |
|||
} catch (e) {} |
|||
|
|||
if (decodeBech32) { |
|||
if (decodeBech32.prefix !== network.bech32) |
|||
throw new Error(address + ' has an invalid prefix'); |
|||
if (decodeBech32.version === 0) { |
|||
if (decodeBech32.data.length === 20) |
|||
return payments.p2wpkh({ hash: decodeBech32.data }).output as Buffer; |
|||
if (decodeBech32.data.length === 32) |
|||
return payments.p2wsh({ hash: decodeBech32.data }).output as Buffer; |
|||
} |
|||
} |
|||
} |
|||
|
|||
throw new Error(address + ' has no matching Script'); |
|||
} |
@ -0,0 +1,285 @@ |
|||
import { reverseBuffer } from './bufferutils'; |
|||
import * as bcrypto from './crypto'; |
|||
import { Transaction } from './transaction'; |
|||
import * as types from './types'; |
|||
|
|||
const fastMerkleRoot = require('merkle-lib/fastRoot'); |
|||
const typeforce = require('typeforce'); |
|||
const varuint = require('varuint-bitcoin'); |
|||
|
|||
const errorMerkleNoTxes = new TypeError( |
|||
'Cannot compute merkle root for zero transactions', |
|||
); |
|||
const errorWitnessNotSegwit = new TypeError( |
|||
'Cannot compute witness commit for non-segwit block', |
|||
); |
|||
|
|||
export class Block { |
|||
static fromBuffer(buffer: Buffer): Block { |
|||
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); |
|||
|
|||
let offset: number = 0; |
|||
const readSlice = (n: number): Buffer => { |
|||
offset += n; |
|||
return buffer.slice(offset - n, offset); |
|||
}; |
|||
|
|||
const readUInt32 = (): number => { |
|||
const i = buffer.readUInt32LE(offset); |
|||
offset += 4; |
|||
return i; |
|||
}; |
|||
|
|||
const readInt32 = (): number => { |
|||
const i = buffer.readInt32LE(offset); |
|||
offset += 4; |
|||
return i; |
|||
}; |
|||
|
|||
const block = new Block(); |
|||
block.version = readInt32(); |
|||
block.prevHash = readSlice(32); |
|||
block.merkleRoot = readSlice(32); |
|||
block.timestamp = readUInt32(); |
|||
block.bits = readUInt32(); |
|||
block.nonce = readUInt32(); |
|||
|
|||
if (buffer.length === 80) return block; |
|||
|
|||
const readVarInt = (): number => { |
|||
const vi = varuint.decode(buffer, offset); |
|||
offset += varuint.decode.bytes; |
|||
return vi; |
|||
}; |
|||
|
|||
const readTransaction = (): any => { |
|||
const tx = Transaction.fromBuffer(buffer.slice(offset), true); |
|||
offset += tx.byteLength(); |
|||
return tx; |
|||
}; |
|||
|
|||
const nTransactions = readVarInt(); |
|||
block.transactions = []; |
|||
|
|||
for (let i = 0; i < nTransactions; ++i) { |
|||
const tx = readTransaction(); |
|||
block.transactions.push(tx); |
|||
} |
|||
|
|||
const witnessCommit = block.getWitnessCommit(); |
|||
// This Block contains a witness commit
|
|||
if (witnessCommit) block.witnessCommit = witnessCommit; |
|||
|
|||
return block; |
|||
} |
|||
|
|||
static fromHex(hex: string): Block { |
|||
return Block.fromBuffer(Buffer.from(hex, 'hex')); |
|||
} |
|||
|
|||
static calculateTarget(bits: number): Buffer { |
|||
const exponent = ((bits & 0xff000000) >> 24) - 3; |
|||
const mantissa = bits & 0x007fffff; |
|||
const target = Buffer.alloc(32, 0); |
|||
target.writeUIntBE(mantissa, 29 - exponent, 3); |
|||
return target; |
|||
} |
|||
|
|||
static calculateMerkleRoot( |
|||
transactions: Transaction[], |
|||
forWitness?: boolean, |
|||
): Buffer { |
|||
typeforce([{ getHash: types.Function }], transactions); |
|||
if (transactions.length === 0) throw errorMerkleNoTxes; |
|||
if (forWitness && !txesHaveWitnessCommit(transactions)) |
|||
throw errorWitnessNotSegwit; |
|||
|
|||
const hashes = transactions.map(transaction => |
|||
transaction.getHash(forWitness!), |
|||
); |
|||
|
|||
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); |
|||
|
|||
return forWitness |
|||
? bcrypto.hash256( |
|||
Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]), |
|||
) |
|||
: rootHash; |
|||
} |
|||
|
|||
version: number = 1; |
|||
prevHash?: Buffer = undefined; |
|||
merkleRoot?: Buffer = undefined; |
|||
timestamp: number = 0; |
|||
witnessCommit?: Buffer = undefined; |
|||
bits: number = 0; |
|||
nonce: number = 0; |
|||
transactions?: Transaction[] = undefined; |
|||
|
|||
getWitnessCommit(): Buffer | null { |
|||
if (!txesHaveWitnessCommit(this.transactions!)) return null; |
|||
|
|||
// The merkle root for the witness data is in an OP_RETURN output.
|
|||
// There is no rule for the index of the output, so use filter to find it.
|
|||
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
|
|||
// If multiple commits are found, the output with highest index is assumed.
|
|||
const witnessCommits = this.transactions![0].outs.filter(out => |
|||
out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')), |
|||
).map(out => out.script.slice(6, 38)); |
|||
if (witnessCommits.length === 0) return null; |
|||
// Use the commit with the highest output (should only be one though)
|
|||
const result = witnessCommits[witnessCommits.length - 1]; |
|||
|
|||
if (!(result instanceof Buffer && result.length === 32)) return null; |
|||
return result; |
|||
} |
|||
|
|||
hasWitnessCommit(): boolean { |
|||
if ( |
|||
this.witnessCommit instanceof Buffer && |
|||
this.witnessCommit.length === 32 |
|||
) |
|||
return true; |
|||
if (this.getWitnessCommit() !== null) return true; |
|||
return false; |
|||
} |
|||
|
|||
hasWitness(): boolean { |
|||
return anyTxHasWitness(this.transactions!); |
|||
} |
|||
|
|||
byteLength(headersOnly: boolean): number { |
|||
if (headersOnly || !this.transactions) return 80; |
|||
|
|||
return ( |
|||
80 + |
|||
varuint.encodingLength(this.transactions.length) + |
|||
this.transactions.reduce((a, x) => a + x.byteLength(), 0) |
|||
); |
|||
} |
|||
|
|||
getHash(): Buffer { |
|||
return bcrypto.hash256(this.toBuffer(true)); |
|||
} |
|||
|
|||
getId(): string { |
|||
return reverseBuffer(this.getHash()).toString('hex'); |
|||
} |
|||
|
|||
getUTCDate(): Date { |
|||
const date = new Date(0); // epoch
|
|||
date.setUTCSeconds(this.timestamp); |
|||
|
|||
return date; |
|||
} |
|||
|
|||
// TODO: buffer, offset compatibility
|
|||
toBuffer(headersOnly: boolean): Buffer { |
|||
const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); |
|||
|
|||
let offset: number = 0; |
|||
const writeSlice = (slice: Buffer): void => { |
|||
slice.copy(buffer, offset); |
|||
offset += slice.length; |
|||
}; |
|||
|
|||
const writeInt32 = (i: number): void => { |
|||
buffer.writeInt32LE(i, offset); |
|||
offset += 4; |
|||
}; |
|||
const writeUInt32 = (i: number): void => { |
|||
buffer.writeUInt32LE(i, offset); |
|||
offset += 4; |
|||
}; |
|||
|
|||
writeInt32(this.version); |
|||
writeSlice(this.prevHash!); |
|||
writeSlice(this.merkleRoot!); |
|||
writeUInt32(this.timestamp); |
|||
writeUInt32(this.bits); |
|||
writeUInt32(this.nonce); |
|||
|
|||
if (headersOnly || !this.transactions) return buffer; |
|||
|
|||
varuint.encode(this.transactions.length, buffer, offset); |
|||
offset += varuint.encode.bytes; |
|||
|
|||
this.transactions.forEach(tx => { |
|||
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
|
|||
tx.toBuffer(buffer, offset); |
|||
offset += txSize; |
|||
}); |
|||
|
|||
return buffer; |
|||
} |
|||
|
|||
toHex(headersOnly: boolean): string { |
|||
return this.toBuffer(headersOnly).toString('hex'); |
|||
} |
|||
|
|||
checkTxRoots(): boolean { |
|||
// If the Block has segwit transactions but no witness commit,
|
|||
// there's no way it can be valid, so fail the check.
|
|||
const hasWitnessCommit = this.hasWitnessCommit(); |
|||
if (!hasWitnessCommit && this.hasWitness()) return false; |
|||
return ( |
|||
this.__checkMerkleRoot() && |
|||
(hasWitnessCommit ? this.__checkWitnessCommit() : true) |
|||
); |
|||
} |
|||
|
|||
checkProofOfWork(): boolean { |
|||
const hash: Buffer = reverseBuffer(this.getHash()); |
|||
const target = Block.calculateTarget(this.bits); |
|||
|
|||
return hash.compare(target) <= 0; |
|||
} |
|||
|
|||
private __checkMerkleRoot(): boolean { |
|||
if (!this.transactions) throw errorMerkleNoTxes; |
|||
|
|||
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); |
|||
return this.merkleRoot!.compare(actualMerkleRoot) === 0; |
|||
} |
|||
|
|||
private __checkWitnessCommit(): boolean { |
|||
if (!this.transactions) throw errorMerkleNoTxes; |
|||
if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit; |
|||
|
|||
const actualWitnessCommit = Block.calculateMerkleRoot( |
|||
this.transactions, |
|||
true, |
|||
); |
|||
return this.witnessCommit!.compare(actualWitnessCommit) === 0; |
|||
} |
|||
} |
|||
|
|||
function txesHaveWitnessCommit(transactions: Transaction[]): boolean { |
|||
return ( |
|||
transactions instanceof Array && |
|||
transactions[0] && |
|||
transactions[0].ins && |
|||
transactions[0].ins instanceof Array && |
|||
transactions[0].ins[0] && |
|||
transactions[0].ins[0].witness && |
|||
transactions[0].ins[0].witness instanceof Array && |
|||
transactions[0].ins[0].witness.length > 0 |
|||
); |
|||
} |
|||
|
|||
function anyTxHasWitness(transactions: Transaction[]): boolean { |
|||
return ( |
|||
transactions instanceof Array && |
|||
transactions.some( |
|||
tx => |
|||
typeof tx === 'object' && |
|||
tx.ins instanceof Array && |
|||
tx.ins.some( |
|||
input => |
|||
typeof input === 'object' && |
|||
input.witness instanceof Array && |
|||
input.witness.length > 0, |
|||
), |
|||
) |
|||
); |
|||
} |
@ -0,0 +1,44 @@ |
|||
// https://github.com/feross/buffer/blob/master/index.js#L1127
|
|||
function verifuint(value: number, max: number): void { |
|||
if (typeof value !== 'number') |
|||
throw new Error('cannot write a non-number as a number'); |
|||
if (value < 0) |
|||
throw new Error('specified a negative value for writing an unsigned value'); |
|||
if (value > max) throw new Error('RangeError: value out of range'); |
|||
if (Math.floor(value) !== value) |
|||
throw new Error('value has a fractional component'); |
|||
} |
|||
|
|||
export function readUInt64LE(buffer: Buffer, offset: number): number { |
|||
const a = buffer.readUInt32LE(offset); |
|||
let b = buffer.readUInt32LE(offset + 4); |
|||
b *= 0x100000000; |
|||
|
|||
verifuint(b + a, 0x001fffffffffffff); |
|||
return b + a; |
|||
} |
|||
|
|||
export function writeUInt64LE( |
|||
buffer: Buffer, |
|||
value: number, |
|||
offset: number, |
|||
): number { |
|||
verifuint(value, 0x001fffffffffffff); |
|||
|
|||
buffer.writeInt32LE(value & -1, offset); |
|||
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); |
|||
return offset + 8; |
|||
} |
|||
|
|||
export function reverseBuffer(buffer: Buffer): Buffer { |
|||
if (buffer.length < 1) return buffer; |
|||
let j = buffer.length - 1; |
|||
let tmp = 0; |
|||
for (let i = 0; i < buffer.length / 2; i++) { |
|||
tmp = buffer[i]; |
|||
buffer[i] = buffer[j]; |
|||
buffer[j] = tmp; |
|||
j--; |
|||
} |
|||
return buffer; |
|||
} |
@ -0,0 +1,71 @@ |
|||
import { decompile } from './script'; |
|||
import * as multisig from './templates/multisig'; |
|||
import * as nullData from './templates/nulldata'; |
|||
import * as pubKey from './templates/pubkey'; |
|||
import * as pubKeyHash from './templates/pubkeyhash'; |
|||
import * as scriptHash from './templates/scripthash'; |
|||
import * as witnessCommitment from './templates/witnesscommitment'; |
|||
import * as witnessPubKeyHash from './templates/witnesspubkeyhash'; |
|||
import * as witnessScriptHash from './templates/witnessscripthash'; |
|||
|
|||
const types = { |
|||
P2MS: 'multisig' as string, |
|||
NONSTANDARD: 'nonstandard' as string, |
|||
NULLDATA: 'nulldata' as string, |
|||
P2PK: 'pubkey' as string, |
|||
P2PKH: 'pubkeyhash' as string, |
|||
P2SH: 'scripthash' as string, |
|||
P2WPKH: 'witnesspubkeyhash' as string, |
|||
P2WSH: 'witnessscripthash' as string, |
|||
WITNESS_COMMITMENT: 'witnesscommitment' as string, |
|||
}; |
|||
|
|||
function classifyOutput(script: Buffer): string { |
|||
if (witnessPubKeyHash.output.check(script)) return types.P2WPKH; |
|||
if (witnessScriptHash.output.check(script)) return types.P2WSH; |
|||
if (pubKeyHash.output.check(script)) return types.P2PKH; |
|||
if (scriptHash.output.check(script)) return types.P2SH; |
|||
|
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = decompile(script); |
|||
if (!chunks) throw new TypeError('Invalid script'); |
|||
|
|||
if (multisig.output.check(chunks)) return types.P2MS; |
|||
if (pubKey.output.check(chunks)) return types.P2PK; |
|||
if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT; |
|||
if (nullData.output.check(chunks)) return types.NULLDATA; |
|||
|
|||
return types.NONSTANDARD; |
|||
} |
|||
|
|||
function classifyInput(script: Buffer, allowIncomplete: boolean): string { |
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = decompile(script); |
|||
if (!chunks) throw new TypeError('Invalid script'); |
|||
|
|||
if (pubKeyHash.input.check(chunks)) return types.P2PKH; |
|||
if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH; |
|||
if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS; |
|||
if (pubKey.input.check(chunks)) return types.P2PK; |
|||
|
|||
return types.NONSTANDARD; |
|||
} |
|||
|
|||
function classifyWitness(script: Buffer[], allowIncomplete: boolean): string { |
|||
// XXX: optimization, below functions .decompile before use
|
|||
const chunks = decompile(script); |
|||
if (!chunks) throw new TypeError('Invalid script'); |
|||
|
|||
if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH; |
|||
if (witnessScriptHash.input.check(chunks as Buffer[], allowIncomplete)) |
|||
return types.P2WSH; |
|||
|
|||
return types.NONSTANDARD; |
|||
} |
|||
|
|||
export { |
|||
classifyInput as input, |
|||
classifyOutput as output, |
|||
classifyWitness as witness, |
|||
types, |
|||
}; |
@ -0,0 +1,33 @@ |
|||
const createHash = require('create-hash'); |
|||
|
|||
export function ripemd160(buffer: Buffer): Buffer { |
|||
try { |
|||
return createHash('rmd160') |
|||
.update(buffer) |
|||
.digest(); |
|||
} catch (err) { |
|||
return createHash('ripemd160') |
|||
.update(buffer) |
|||
.digest(); |
|||
} |
|||
} |
|||
|
|||
export function sha1(buffer: Buffer): Buffer { |
|||
return createHash('sha1') |
|||
.update(buffer) |
|||
.digest(); |
|||
} |
|||
|
|||
export function sha256(buffer: Buffer): Buffer { |
|||
return createHash('sha256') |
|||
.update(buffer) |
|||
.digest(); |
|||
} |
|||
|
|||
export function hash160(buffer: Buffer): Buffer { |
|||
return ripemd160(sha256(buffer)); |
|||
} |
|||
|
|||
export function hash256(buffer: Buffer): Buffer { |
|||
return sha256(sha256(buffer)); |
|||
} |
@ -0,0 +1,131 @@ |
|||
import { Network } from './networks'; |
|||
import * as NETWORKS from './networks'; |
|||
import * as types from './types'; |
|||
const ecc = require('tiny-secp256k1'); |
|||
const randomBytes = require('randombytes'); |
|||
const typeforce = require('typeforce'); |
|||
const wif = require('wif'); |
|||
|
|||
const isOptions = typeforce.maybe( |
|||
typeforce.compile({ |
|||
compressed: types.maybe(types.Boolean), |
|||
network: types.maybe(types.Network), |
|||
}), |
|||
); |
|||
|
|||
interface ECPairOptions { |
|||
compressed?: boolean; |
|||
network?: Network; |
|||
rng?(arg0: Buffer): Buffer; |
|||
} |
|||
|
|||
export interface ECPairInterface { |
|||
compressed: boolean; |
|||
network: Network; |
|||
privateKey?: Buffer; |
|||
publicKey?: Buffer; |
|||
toWIF(): string; |
|||
sign(hash: Buffer): Buffer; |
|||
verify(hash: Buffer, signature: Buffer): Buffer; |
|||
getPublicKey?(): Buffer; |
|||
} |
|||
|
|||
class ECPair implements ECPairInterface { |
|||
compressed: boolean; |
|||
network: Network; |
|||
|
|||
constructor( |
|||
private __D?: Buffer, |
|||
private __Q?: Buffer, |
|||
options?: ECPairOptions, |
|||
) { |
|||
if (options === undefined) options = {}; |
|||
this.compressed = |
|||
options.compressed === undefined ? true : options.compressed; |
|||
this.network = options.network || NETWORKS.bitcoin; |
|||
|
|||
if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed); |
|||
} |
|||
|
|||
get privateKey(): Buffer | undefined { |
|||
return this.__D; |
|||
} |
|||
|
|||
get publicKey(): Buffer | undefined { |
|||
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed); |
|||
return this.__Q; |
|||
} |
|||
|
|||
toWIF(): string { |
|||
if (!this.__D) throw new Error('Missing private key'); |
|||
return wif.encode(this.network.wif, this.__D, this.compressed); |
|||
} |
|||
|
|||
sign(hash: Buffer): Buffer { |
|||
if (!this.__D) throw new Error('Missing private key'); |
|||
return ecc.sign(hash, this.__D); |
|||
} |
|||
|
|||
verify(hash: Buffer, signature: Buffer): Buffer { |
|||
return ecc.verify(hash, this.publicKey, signature); |
|||
} |
|||
} |
|||
|
|||
function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair { |
|||
typeforce(types.Buffer256bit, buffer); |
|||
if (!ecc.isPrivate(buffer)) |
|||
throw new TypeError('Private key not in range [1, n)'); |
|||
typeforce(isOptions, options); |
|||
|
|||
return new ECPair(buffer, undefined, options as ECPairOptions); |
|||
} |
|||
|
|||
function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair { |
|||
typeforce(ecc.isPoint, buffer); |
|||
typeforce(isOptions, options); |
|||
return new ECPair(undefined, buffer, options as ECPairOptions); |
|||
} |
|||
|
|||
function fromWIF(wifString: string, network?: Network | Network[]): ECPair { |
|||
const decoded = wif.decode(wifString); |
|||
const version = decoded.version; |
|||
|
|||
// list of networks?
|
|||
if (types.Array(network)) { |
|||
network = (network as Network[]) |
|||
.filter((x: Network) => { |
|||
return version === x.wif; |
|||
}) |
|||
.pop() as Network; |
|||
|
|||
if (!network) throw new Error('Unknown network version'); |
|||
|
|||
// otherwise, assume a network object (or default to bitcoin)
|
|||
} else { |
|||
network = network || NETWORKS.bitcoin; |
|||
|
|||
if (version !== (network as Network).wif) |
|||
throw new Error('Invalid network version'); |
|||
} |
|||
|
|||
return fromPrivateKey(decoded.privateKey, { |
|||
compressed: decoded.compressed, |
|||
network: network as Network, |
|||
}); |
|||
} |
|||
|
|||
function makeRandom(options?: ECPairOptions): ECPair { |
|||
typeforce(isOptions, options); |
|||
if (options === undefined) options = {}; |
|||
const rng = options.rng || randomBytes; |
|||
|
|||
let d; |
|||
do { |
|||
d = rng(32); |
|||
typeforce(types.Buffer256bit, d); |
|||
} while (!ecc.isPrivate(d)); |
|||
|
|||
return fromPrivateKey(d, options); |
|||
} |
|||
|
|||
export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF }; |
@ -0,0 +1,20 @@ |
|||
import * as bip32 from 'bip32'; |
|||
import * as address from './address'; |
|||
import * as crypto from './crypto'; |
|||
import * as ECPair from './ecpair'; |
|||
import * as networks from './networks'; |
|||
import * as payments from './payments'; |
|||
import * as script from './script'; |
|||
|
|||
export { ECPair, address, bip32, crypto, networks, payments, script }; |
|||
|
|||
export { Block } from './block'; |
|||
export { OPS as opcodes } from './script'; |
|||
export { Transaction } from './transaction'; |
|||
export { TransactionBuilder } from './transaction_builder'; |
|||
|
|||
export { BIP32Interface } from 'bip32'; |
|||
export { Network } from './networks'; |
|||
export { Payment, PaymentOpts } from './payments'; |
|||
export { OpCode } from './script'; |
|||
export { Input as TxInput, Output as TxOutput } from './transaction'; |
@ -0,0 +1,49 @@ |
|||
// https://en.bitcoin.it/wiki/List_of_address_prefixes
|
|||
// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
|
|||
export interface Network { |
|||
messagePrefix: string; |
|||
bech32: string; |
|||
bip32: Bip32; |
|||
pubKeyHash: number; |
|||
scriptHash: number; |
|||
wif: number; |
|||
} |
|||
|
|||
interface Bip32 { |
|||
public: number; |
|||
private: number; |
|||
} |
|||
|
|||
export const bitcoin: Network = { |
|||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
|||
bech32: 'bc', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x00, |
|||
scriptHash: 0x05, |
|||
wif: 0x80, |
|||
}; |
|||
export const regtest: Network = { |
|||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
|||
bech32: 'bcrt', |
|||
bip32: { |
|||
public: 0x043587cf, |
|||
private: 0x04358394, |
|||
}, |
|||
pubKeyHash: 0x6f, |
|||
scriptHash: 0xc4, |
|||
wif: 0xef, |
|||
}; |
|||
export const testnet: Network = { |
|||
messagePrefix: '\x18Bitcoin Signed Message:\n', |
|||
bech32: 'tb', |
|||
bip32: { |
|||
public: 0x043587cf, |
|||
private: 0x04358394, |
|||
}, |
|||
pubKeyHash: 0x6f, |
|||
scriptHash: 0xc4, |
|||
wif: 0xef, |
|||
}; |
@ -0,0 +1,58 @@ |
|||
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
|||
import * as bscript from '../script'; |
|||
import { Payment, PaymentOpts, Stack } from './index'; |
|||
import * as lazy from './lazy'; |
|||
|
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
|
|||
function stacksEqual(a: Buffer[], b: Buffer[]): boolean { |
|||
if (a.length !== b.length) return false; |
|||
|
|||
return a.every((x, i) => { |
|||
return x.equals(b[i]); |
|||
}); |
|||
} |
|||
|
|||
// output: OP_RETURN ...
|
|||
export function p2data(a: Payment, opts?: PaymentOpts): Payment { |
|||
if (!a.data && !a.output) throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
|
|||
typef( |
|||
{ |
|||
network: typef.maybe(typef.Object), |
|||
output: typef.maybe(typef.Buffer), |
|||
data: typef.maybe(typef.arrayOf(typef.Buffer)), |
|||
}, |
|||
a, |
|||
); |
|||
|
|||
const network = a.network || BITCOIN_NETWORK; |
|||
const o = { network } as Payment; |
|||
|
|||
lazy.prop(o, 'output', () => { |
|||
if (!a.data) return; |
|||
return bscript.compile(([OPS.OP_RETURN] as Stack).concat(a.data)); |
|||
}); |
|||
lazy.prop(o, 'data', () => { |
|||
if (!a.output) return; |
|||
return bscript.decompile(a.output)!.slice(1); |
|||
}); |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
if (a.output) { |
|||
const chunks = bscript.decompile(a.output); |
|||
if (chunks![0] !== OPS.OP_RETURN) |
|||
throw new TypeError('Output is invalid'); |
|||
if (!chunks!.slice(1).every(typef.Buffer)) |
|||
throw new TypeError('Output is invalid'); |
|||
|
|||
if (a.data && !stacksEqual(a.data, o.data as Buffer[])) |
|||
throw new TypeError('Data mismatch'); |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a); |
|||
} |
@ -0,0 +1,41 @@ |
|||
import { Network } from '../networks'; |
|||
import { p2data as embed } from './embed'; |
|||
import { p2ms } from './p2ms'; |
|||
import { p2pk } from './p2pk'; |
|||
import { p2pkh } from './p2pkh'; |
|||
import { p2sh } from './p2sh'; |
|||
import { p2wpkh } from './p2wpkh'; |
|||
import { p2wsh } from './p2wsh'; |
|||
|
|||
export interface Payment { |
|||
network?: Network; |
|||
output?: Buffer; |
|||
data?: Buffer[]; |
|||
m?: number; |
|||
n?: number; |
|||
pubkeys?: Buffer[]; |
|||
input?: Buffer; |
|||
signatures?: Buffer[]; |
|||
pubkey?: Buffer; |
|||
signature?: Buffer; |
|||
address?: string; |
|||
hash?: Buffer; |
|||
redeem?: Payment; |
|||
witness?: Buffer[]; |
|||
} |
|||
|
|||
export type PaymentFunction = () => Payment; |
|||
|
|||
export interface PaymentOpts { |
|||
validate?: boolean; |
|||
allowIncomplete?: boolean; |
|||
} |
|||
|
|||
export type StackElement = Buffer | number; |
|||
export type Stack = StackElement[]; |
|||
export type StackFunction = () => Stack; |
|||
|
|||
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; |
|||
|
|||
// TODO
|
|||
// witness commitment
|
@ -0,0 +1,28 @@ |
|||
export function prop(object: {}, name: string, f: () => any): void { |
|||
Object.defineProperty(object, name, { |
|||
configurable: true, |
|||
enumerable: true, |
|||
get(): any { |
|||
const _value = f.call(this); |
|||
this[name] = _value; |
|||
return _value; |
|||
}, |
|||
set(_value: any): void { |
|||
Object.defineProperty(this, name, { |
|||
configurable: true, |
|||
enumerable: true, |
|||
value: _value, |
|||
writable: true, |
|||
}); |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
export function value<T>(f: () => T): () => T { |
|||
let _value: T; |
|||
return (): T => { |
|||
if (_value !== undefined) return _value; |
|||
_value = f(); |
|||
return _value; |
|||
}; |
|||
} |
@ -0,0 +1,158 @@ |
|||
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
|||
import * as bscript from '../script'; |
|||
import { Payment, PaymentOpts, Stack } from './index'; |
|||
import * as lazy from './lazy'; |
|||
const OPS = bscript.OPS; |
|||
const typef = require('typeforce'); |
|||
const ecc = require('tiny-secp256k1'); |
|||
|
|||
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
|
|||
|
|||
function stacksEqual(a: Buffer[], b: Buffer[]): boolean { |
|||
if (a.length !== b.length) return false; |
|||
|
|||
return a.every((x, i) => { |
|||
return x.equals(b[i]); |
|||
}); |
|||
} |
|||
|
|||
// input: OP_0 [signatures ...]
|
|||
// output: m [pubKeys ...] n OP_CHECKMULTISIG
|
|||
export function p2ms(a: Payment, opts?: PaymentOpts): Payment { |
|||
if ( |
|||
!a.input && |
|||
!a.output && |
|||
!(a.pubkeys && a.m !== undefined) && |
|||
!a.signatures |
|||
) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
|
|||
function isAcceptableSignature(x: Buffer | number): boolean { |
|||
return ( |
|||
bscript.isCanonicalScriptSignature(x as Buffer) || |
|||
(opts!.allowIncomplete && (x as number) === OPS.OP_0) !== undefined |
|||
); |
|||
} |
|||
|
|||
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, |
|||
); |
|||
|
|||
const network = a.network || BITCOIN_NETWORK; |
|||
const o: Payment = { network }; |
|||
|
|||
let chunks: Stack = []; |
|||
let decoded = false; |
|||
function decode(output: Buffer | Stack): void { |
|||
if (decoded) return; |
|||
decoded = true; |
|||
chunks = bscript.decompile(output) as Stack; |
|||
o.m = (chunks[0] as number) - OP_INT_BASE; |
|||
o.n = (chunks[chunks.length - 2] as number) - OP_INT_BASE; |
|||
o.pubkeys = chunks.slice(1, -2) as Buffer[]; |
|||
} |
|||
|
|||
lazy.prop(o, 'output', () => { |
|||
if (!a.m) return; |
|||
if (!o.n) return; |
|||
if (!a.pubkeys) return; |
|||
return bscript.compile( |
|||
([] as Stack).concat( |
|||
OP_INT_BASE + a.m, |
|||
a.pubkeys, |
|||
OP_INT_BASE + o.n, |
|||
OPS.OP_CHECKMULTISIG, |
|||
), |
|||
); |
|||
}); |
|||
lazy.prop(o, 'm', () => { |
|||
if (!o.output) return; |
|||
decode(o.output); |
|||
return o.m; |
|||
}); |
|||
lazy.prop(o, 'n', () => { |
|||
if (!o.pubkeys) return; |
|||
return o.pubkeys.length; |
|||
}); |
|||
lazy.prop(o, 'pubkeys', () => { |
|||
if (!a.output) return; |
|||
decode(a.output); |
|||
return o.pubkeys; |
|||
}); |
|||
lazy.prop(o, 'signatures', () => { |
|||
if (!a.input) return; |
|||
return bscript.decompile(a.input)!.slice(1); |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.signatures) return; |
|||
return bscript.compile(([OPS.OP_0] as Stack).concat(a.signatures)); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
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, 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); |
|||
} |
@ -0,0 +1,80 @@ |
|||
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
|||
import * as bscript from '../script'; |
|||
import { Payment, PaymentOpts, StackFunction } from './index'; |
|||
import * as lazy from './lazy'; |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const ecc = require('tiny-secp256k1'); |
|||
|
|||
// input: {signature}
|
|||
// output: {pubKey} OP_CHECKSIG
|
|||
export function p2pk(a: Payment, opts?: PaymentOpts): Payment { |
|||
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
|
|||
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, |
|||
); |
|||
|
|||
const _chunks = lazy.value(() => { |
|||
return bscript.decompile(a.input!); |
|||
}) as StackFunction; |
|||
|
|||
const network = a.network || BITCOIN_NETWORK; |
|||
const o: Payment = { network }; |
|||
|
|||
lazy.prop(o, 'output', () => { |
|||
if (!a.pubkey) return; |
|||
return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); |
|||
}); |
|||
lazy.prop(o, 'pubkey', () => { |
|||
if (!a.output) return; |
|||
return a.output.slice(1, -1); |
|||
}); |
|||
lazy.prop(o, 'signature', () => { |
|||
if (!a.input) return; |
|||
return _chunks()[0] as Buffer; |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.signature) return; |
|||
return bscript.compile([a.signature]); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (!o.input) return; |
|||
return []; |
|||
}); |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
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.pubkey && !a.pubkey.equals(o.pubkey!)) |
|||
throw new TypeError('Pubkey mismatch'); |
|||
} |
|||
|
|||
if (a.signature) { |
|||
if (a.input && !a.input.equals(o.input!)) |
|||
throw new TypeError('Signature mismatch'); |
|||
} |
|||
|
|||
if (a.input) { |
|||
if (_chunks().length !== 1) throw new TypeError('Input is invalid'); |
|||
if (!bscript.isCanonicalScriptSignature(o.signature!)) |
|||
throw new TypeError('Input has invalid signature'); |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a); |
|||
} |
@ -0,0 +1,147 @@ |
|||
import * as bcrypto from '../crypto'; |
|||
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
|||
import * as bscript from '../script'; |
|||
import { Payment, PaymentOpts, StackFunction } from './index'; |
|||
import * as lazy from './lazy'; |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const ecc = require('tiny-secp256k1'); |
|||
|
|||
const bs58check = require('bs58check'); |
|||
|
|||
// input: {signature} {pubkey}
|
|||
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
|
|||
export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { |
|||
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
|
|||
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, |
|||
); |
|||
|
|||
const _address = lazy.value(() => { |
|||
const payload = bs58check.decode(a.address); |
|||
const version = payload.readUInt8(0); |
|||
const hash = payload.slice(1); |
|||
return { version, hash }; |
|||
}); |
|||
const _chunks = lazy.value(() => { |
|||
return bscript.decompile(a.input!); |
|||
}) as StackFunction; |
|||
|
|||
const network = a.network || BITCOIN_NETWORK; |
|||
const o: Payment = { network }; |
|||
|
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) return; |
|||
|
|||
const payload = Buffer.allocUnsafe(21); |
|||
payload.writeUInt8(network.pubKeyHash, 0); |
|||
o.hash.copy(payload, 1); |
|||
return bs58check.encode(payload); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
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', () => { |
|||
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', () => { |
|||
if (!a.input) return; |
|||
return _chunks()[1] as Buffer; |
|||
}); |
|||
lazy.prop(o, 'signature', () => { |
|||
if (!a.input) return; |
|||
return _chunks()[0] as Buffer; |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.pubkey) return; |
|||
if (!a.signature) return; |
|||
return bscript.compile([a.signature, a.pubkey]); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (!o.input) return; |
|||
return []; |
|||
}); |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash: Buffer = Buffer.from([]); |
|||
if (a.address) { |
|||
if (_address().version !== network.pubKeyHash) |
|||
throw new TypeError('Invalid version or Network mismatch'); |
|||
if (_address().hash.length !== 20) throw new TypeError('Invalid address'); |
|||
hash = _address().hash; |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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'); |
|||
|
|||
const hash2 = a.output.slice(3, 23); |
|||
if (hash.length > 0 && !hash.equals(hash2)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else hash = hash2; |
|||
} |
|||
|
|||
if (a.pubkey) { |
|||
const pkh = bcrypto.hash160(a.pubkey); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else hash = pkh; |
|||
} |
|||
|
|||
if (a.input) { |
|||
const chunks = _chunks(); |
|||
if (chunks.length !== 2) throw new TypeError('Input is invalid'); |
|||
if (!bscript.isCanonicalScriptSignature(chunks[0] as Buffer)) |
|||
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] as Buffer)) |
|||
throw new TypeError('Signature mismatch'); |
|||
if (a.pubkey && !a.pubkey.equals(chunks[1] as Buffer)) |
|||
throw new TypeError('Pubkey mismatch'); |
|||
|
|||
const pkh = bcrypto.hash160(chunks[1] as Buffer); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a); |
|||
} |
@ -0,0 +1,213 @@ |
|||
import * as bcrypto from '../crypto'; |
|||
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
|||
import * as bscript from '../script'; |
|||
import { |
|||
Payment, |
|||
PaymentFunction, |
|||
PaymentOpts, |
|||
Stack, |
|||
StackFunction, |
|||
} from './index'; |
|||
import * as lazy from './lazy'; |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
|
|||
const bs58check = require('bs58check'); |
|||
|
|||
function stacksEqual(a: Buffer[], b: Buffer[]): boolean { |
|||
if (a.length !== b.length) return false; |
|||
|
|||
return a.every((x, i) => { |
|||
return x.equals(b[i]); |
|||
}); |
|||
} |
|||
|
|||
// input: [redeemScriptSig ...] {redeemScript}
|
|||
// witness: <?>
|
|||
// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
|
|||
export function p2sh(a: Payment, opts?: PaymentOpts): Payment { |
|||
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
|
|||
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.maybe(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, |
|||
); |
|||
|
|||
let network = a.network; |
|||
if (!network) { |
|||
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK; |
|||
} |
|||
|
|||
const o: Payment = { network }; |
|||
|
|||
const _address = lazy.value(() => { |
|||
const payload = bs58check.decode(a.address); |
|||
const version = payload.readUInt8(0); |
|||
const hash = payload.slice(1); |
|||
return { version, hash }; |
|||
}); |
|||
const _chunks = lazy.value(() => { |
|||
return bscript.decompile(a.input!); |
|||
}) as StackFunction; |
|||
const _redeem = lazy.value( |
|||
(): Payment => { |
|||
const chunks = _chunks(); |
|||
return { |
|||
network, |
|||
output: chunks[chunks.length - 1] as Buffer, |
|||
input: bscript.compile(chunks.slice(0, -1)), |
|||
witness: a.witness || [], |
|||
}; |
|||
}, |
|||
) as PaymentFunction; |
|||
|
|||
// output dependents
|
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) return; |
|||
|
|||
const payload = Buffer.allocUnsafe(21); |
|||
payload.writeUInt8(o.network!.scriptHash, 0); |
|||
o.hash.copy(payload, 1); |
|||
return bs58check.encode(payload); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
// 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', () => { |
|||
if (!o.hash) return; |
|||
return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); |
|||
}); |
|||
|
|||
// input dependents
|
|||
lazy.prop(o, 'redeem', () => { |
|||
if (!a.input) return; |
|||
return _redeem(); |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!a.redeem || !a.redeem.input || !a.redeem.output) return; |
|||
return bscript.compile( |
|||
([] as Stack).concat( |
|||
bscript.decompile(a.redeem.input) as Stack, |
|||
a.redeem.output, |
|||
), |
|||
); |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (o.redeem && o.redeem.witness) return o.redeem.witness; |
|||
if (o.input) return []; |
|||
}); |
|||
|
|||
if (opts.validate) { |
|||
let hash: Buffer = Buffer.from([]); |
|||
if (a.address) { |
|||
if (_address().version !== network.scriptHash) |
|||
throw new TypeError('Invalid version or Network mismatch'); |
|||
if (_address().hash.length !== 20) throw new TypeError('Invalid address'); |
|||
hash = _address().hash; |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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.length > 0 && !hash.equals(hash2)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else hash = hash2; |
|||
} |
|||
|
|||
// inlined to prevent 'no-inner-declarations' failing
|
|||
const checkRedeem = (redeem: Payment): void => { |
|||
// is the redeem output empty/invalid?
|
|||
if (redeem.output) { |
|||
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.length > 0 && !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) as Stack; |
|||
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 (a.input) { |
|||
const redeem = _redeem(); |
|||
if (a.redeem.output && !a.redeem.output.equals(redeem.output!)) |
|||
throw new TypeError('Redeem.output mismatch'); |
|||
if (a.redeem.input && !a.redeem.input.equals(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); |
|||
} |
@ -0,0 +1,142 @@ |
|||
import * as bcrypto from '../crypto'; |
|||
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
|||
import * as bscript from '../script'; |
|||
import { Payment, PaymentOpts } from './index'; |
|||
import * as lazy from './lazy'; |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
const ecc = require('tiny-secp256k1'); |
|||
|
|||
const bech32 = require('bech32'); |
|||
|
|||
const EMPTY_BUFFER = Buffer.alloc(0); |
|||
|
|||
// witness: {signature} {pubKey}
|
|||
// input: <>
|
|||
// output: OP_0 {pubKeyHash}
|
|||
export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { |
|||
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
|
|||
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, |
|||
); |
|||
|
|||
const _address = lazy.value(() => { |
|||
const result = bech32.decode(a.address); |
|||
const version = result.words.shift(); |
|||
const data = bech32.fromWords(result.words); |
|||
return { |
|||
version, |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data), |
|||
}; |
|||
}); |
|||
|
|||
const network = a.network || BITCOIN_NETWORK; |
|||
const o: Payment = { network }; |
|||
|
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) return; |
|||
|
|||
const words = bech32.toWords(o.hash); |
|||
words.unshift(0x00); |
|||
return bech32.encode(network.bech32, words); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
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', () => { |
|||
if (!o.hash) return; |
|||
return bscript.compile([OPS.OP_0, o.hash]); |
|||
}); |
|||
lazy.prop(o, 'pubkey', () => { |
|||
if (a.pubkey) return a.pubkey; |
|||
if (!a.witness) return; |
|||
return a.witness[1]; |
|||
}); |
|||
lazy.prop(o, 'signature', () => { |
|||
if (!a.witness) return; |
|||
return a.witness[0]; |
|||
}); |
|||
lazy.prop(o, 'input', () => { |
|||
if (!o.witness) return; |
|||
return EMPTY_BUFFER; |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
if (!a.pubkey) return; |
|||
if (!a.signature) return; |
|||
return [a.signature, a.pubkey]; |
|||
}); |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash: Buffer = Buffer.from([]); |
|||
if (a.address) { |
|||
if (network && network.bech32 !== _address().prefix) |
|||
throw new TypeError('Invalid prefix or Network mismatch'); |
|||
if (_address().version !== 0x00) |
|||
throw new TypeError('Invalid address version'); |
|||
if (_address().data.length !== 20) |
|||
throw new TypeError('Invalid address data'); |
|||
hash = _address().data; |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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.length > 0 && !hash.equals(a.output.slice(2))) |
|||
throw new TypeError('Hash mismatch'); |
|||
else hash = a.output.slice(2); |
|||
} |
|||
|
|||
if (a.pubkey) { |
|||
const pkh = bcrypto.hash160(a.pubkey); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
else hash = pkh; |
|||
} |
|||
|
|||
if (a.witness) { |
|||
if (a.witness.length !== 2) throw new TypeError('Witness is invalid'); |
|||
if (!bscript.isCanonicalScriptSignature(a.witness[0])) |
|||
throw new TypeError('Witness has invalid signature'); |
|||
if (!ecc.isPoint(a.witness[1])) |
|||
throw new TypeError('Witness 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'); |
|||
|
|||
const pkh = bcrypto.hash160(a.witness[1]); |
|||
if (hash.length > 0 && !hash.equals(pkh)) |
|||
throw new TypeError('Hash mismatch'); |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a); |
|||
} |
@ -0,0 +1,198 @@ |
|||
import * as bcrypto from '../crypto'; |
|||
import { bitcoin as BITCOIN_NETWORK } from '../networks'; |
|||
import * as bscript from '../script'; |
|||
import { Payment, PaymentOpts, StackFunction } from './index'; |
|||
import * as lazy from './lazy'; |
|||
const typef = require('typeforce'); |
|||
const OPS = bscript.OPS; |
|||
|
|||
const bech32 = require('bech32'); |
|||
|
|||
const EMPTY_BUFFER = Buffer.alloc(0); |
|||
|
|||
function stacksEqual(a: Buffer[], b: Buffer[]): boolean { |
|||
if (a.length !== b.length) return false; |
|||
|
|||
return a.every((x, i) => { |
|||
return x.equals(b[i]); |
|||
}); |
|||
} |
|||
|
|||
// input: <>
|
|||
// witness: [redeemScriptSig ...] {redeemScript}
|
|||
// output: OP_0 {sha256(redeemScript)}
|
|||
export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { |
|||
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) |
|||
throw new TypeError('Not enough data'); |
|||
opts = Object.assign({ validate: true }, opts || {}); |
|||
|
|||
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.maybe(typef.Buffer), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)), |
|||
}), |
|||
input: typef.maybe(typef.BufferN(0)), |
|||
witness: typef.maybe(typef.arrayOf(typef.Buffer)), |
|||
}, |
|||
a, |
|||
); |
|||
|
|||
const _address = lazy.value(() => { |
|||
const result = bech32.decode(a.address); |
|||
const version = result.words.shift(); |
|||
const data = bech32.fromWords(result.words); |
|||
return { |
|||
version, |
|||
prefix: result.prefix, |
|||
data: Buffer.from(data), |
|||
}; |
|||
}); |
|||
const _rchunks = lazy.value(() => { |
|||
return bscript.decompile(a.redeem!.input!); |
|||
}) as StackFunction; |
|||
|
|||
let network = a.network; |
|||
if (!network) { |
|||
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK; |
|||
} |
|||
|
|||
const o: Payment = { network }; |
|||
|
|||
lazy.prop(o, 'address', () => { |
|||
if (!o.hash) return; |
|||
const words = bech32.toWords(o.hash); |
|||
words.unshift(0x00); |
|||
return bech32.encode(network!.bech32, words); |
|||
}); |
|||
lazy.prop(o, 'hash', () => { |
|||
if (a.output) return a.output.slice(2); |
|||
if (a.address) return _address().data; |
|||
if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output); |
|||
}); |
|||
lazy.prop(o, 'output', () => { |
|||
if (!o.hash) return; |
|||
return bscript.compile([OPS.OP_0, o.hash]); |
|||
}); |
|||
lazy.prop(o, 'redeem', () => { |
|||
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', () => { |
|||
if (!o.witness) return; |
|||
return EMPTY_BUFFER; |
|||
}); |
|||
lazy.prop(o, 'witness', () => { |
|||
// transform redeem input to witness stack?
|
|||
if ( |
|||
a.redeem && |
|||
a.redeem.input && |
|||
a.redeem.input.length > 0 && |
|||
a.redeem.output && |
|||
a.redeem.output.length > 0 |
|||
) { |
|||
const stack = bscript.toStack(_rchunks()); |
|||
|
|||
// assign, and blank the existing input
|
|||
o.redeem = Object.assign({ witness: stack }, a.redeem); |
|||
o.redeem.input = EMPTY_BUFFER; |
|||
return ([] as Buffer[]).concat(stack, a.redeem.output); |
|||
} |
|||
|
|||
if (!a.redeem) return; |
|||
if (!a.redeem.output) return; |
|||
if (!a.redeem.witness) return; |
|||
return ([] as Buffer[]).concat(a.redeem.witness, a.redeem.output); |
|||
}); |
|||
|
|||
// extended validation
|
|||
if (opts.validate) { |
|||
let hash: Buffer = Buffer.from([]); |
|||
if (a.address) { |
|||
if (_address().prefix !== network.bech32) |
|||
throw new TypeError('Invalid prefix or Network mismatch'); |
|||
if (_address().version !== 0x00) |
|||
throw new TypeError('Invalid address version'); |
|||
if (_address().data.length !== 32) |
|||
throw new TypeError('Invalid address data'); |
|||
hash = _address().data; |
|||
} |
|||
|
|||
if (a.hash) { |
|||
if (hash.length > 0 && !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'); |
|||
const hash2 = a.output.slice(2); |
|||
if (hash.length > 0 && !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 && |
|||
a.redeem.witness.length > 0 |
|||
) |
|||
throw new TypeError('Ambiguous witness source'); |
|||
|
|||
// is the redeem output non-empty?
|
|||
if (a.redeem.output) { |
|||
if (bscript.decompile(a.redeem.output)!.length === 0) |
|||
throw new TypeError('Redeem.output is invalid'); |
|||
|
|||
// match hash against other sources
|
|||
const hash2 = bcrypto.sha256(a.redeem.output); |
|||
if (hash.length > 0 && !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 && |
|||
!a.redeem.output.equals(a.witness[a.witness.length - 1]) |
|||
) |
|||
throw new TypeError('Witness and redeem.output mismatch'); |
|||
} |
|||
} |
|||
|
|||
return Object.assign(o, a); |
|||
} |
@ -0,0 +1,216 @@ |
|||
import { Stack } from './payments'; |
|||
import * as scriptNumber from './script_number'; |
|||
import * as scriptSignature from './script_signature'; |
|||
import * as types from './types'; |
|||
const bip66 = require('bip66'); |
|||
const ecc = require('tiny-secp256k1'); |
|||
const pushdata = require('pushdata-bitcoin'); |
|||
const typeforce = require('typeforce'); |
|||
|
|||
export type OpCode = number; |
|||
export const OPS = require('bitcoin-ops') as { [index: string]: OpCode }; |
|||
|
|||
const REVERSE_OPS = require('bitcoin-ops/map') as { [index: number]: string }; |
|||
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
|
|||
|
|||
function isOPInt(value: number): boolean { |
|||
return ( |
|||
types.Number(value) && |
|||
(value === OPS.OP_0 || |
|||
(value >= OPS.OP_1 && value <= OPS.OP_16) || |
|||
value === OPS.OP_1NEGATE) |
|||
); |
|||
} |
|||
|
|||
function isPushOnlyChunk(value: number | Buffer): boolean { |
|||
return types.Buffer(value) || isOPInt(value as number); |
|||
} |
|||
|
|||
export function isPushOnly(value: Stack): boolean { |
|||
return types.Array(value) && value.every(isPushOnlyChunk); |
|||
} |
|||
|
|||
function asMinimalOP(buffer: Buffer): number | void { |
|||
if (buffer.length === 0) return OPS.OP_0; |
|||
if (buffer.length !== 1) return; |
|||
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0]; |
|||
if (buffer[0] === 0x81) return OPS.OP_1NEGATE; |
|||
} |
|||
|
|||
function chunksIsBuffer(buf: Buffer | Stack): buf is Buffer { |
|||
return Buffer.isBuffer(buf); |
|||
} |
|||
|
|||
function chunksIsArray(buf: Buffer | Stack): buf is Stack { |
|||
return types.Array(buf); |
|||
} |
|||
|
|||
function singleChunkIsBuffer(buf: number | Buffer): buf is Buffer { |
|||
return Buffer.isBuffer(buf); |
|||
} |
|||
|
|||
export function compile(chunks: Buffer | Stack): Buffer { |
|||
// TODO: remove me
|
|||
if (chunksIsBuffer(chunks)) return chunks; |
|||
|
|||
typeforce(types.Array, chunks); |
|||
|
|||
const bufferSize = chunks.reduce((accum: number, chunk) => { |
|||
// data chunk
|
|||
if (singleChunkIsBuffer(chunk)) { |
|||
// adhere to BIP62.3, minimal push policy
|
|||
if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { |
|||
return accum + 1; |
|||
} |
|||
|
|||
return accum + pushdata.encodingLength(chunk.length) + chunk.length; |
|||
} |
|||
|
|||
// opcode
|
|||
return accum + 1; |
|||
}, 0.0); |
|||
|
|||
const buffer = Buffer.allocUnsafe(bufferSize); |
|||
let offset = 0; |
|||
|
|||
chunks.forEach(chunk => { |
|||
// data chunk
|
|||
if (singleChunkIsBuffer(chunk)) { |
|||
// adhere to BIP62.3, minimal push policy
|
|||
const opcode = asMinimalOP(chunk); |
|||
if (opcode !== undefined) { |
|||
buffer.writeUInt8(opcode, offset); |
|||
offset += 1; |
|||
return; |
|||
} |
|||
|
|||
offset += pushdata.encode(buffer, chunk.length, offset); |
|||
chunk.copy(buffer, offset); |
|||
offset += chunk.length; |
|||
|
|||
// opcode
|
|||
} else { |
|||
buffer.writeUInt8(chunk, offset); |
|||
offset += 1; |
|||
} |
|||
}); |
|||
|
|||
if (offset !== buffer.length) throw new Error('Could not decode chunks'); |
|||
return buffer; |
|||
} |
|||
|
|||
export function decompile( |
|||
buffer: Buffer | Array<number | Buffer>, |
|||
): Array<number | Buffer> | null { |
|||
// TODO: remove me
|
|||
if (chunksIsArray(buffer)) return buffer; |
|||
|
|||
typeforce(types.Buffer, buffer); |
|||
|
|||
const chunks: Array<number | Buffer> = []; |
|||
let i = 0; |
|||
|
|||
while (i < buffer.length) { |
|||
const opcode = buffer[i]; |
|||
|
|||
// data chunk
|
|||
if (opcode > OPS.OP_0 && opcode <= OPS.OP_PUSHDATA4) { |
|||
const d = pushdata.decode(buffer, i); |
|||
|
|||
// did reading a pushDataInt fail?
|
|||
if (d === null) return null; |
|||
i += d.size; |
|||
|
|||
// attempt to read too much data?
|
|||
if (i + d.number > buffer.length) return null; |
|||
|
|||
const data = buffer.slice(i, i + d.number); |
|||
i += d.number; |
|||
|
|||
// decompile minimally
|
|||
const op = asMinimalOP(data); |
|||
if (op !== undefined) { |
|||
chunks.push(op); |
|||
} else { |
|||
chunks.push(data); |
|||
} |
|||
|
|||
// opcode
|
|||
} else { |
|||
chunks.push(opcode); |
|||
|
|||
i += 1; |
|||
} |
|||
} |
|||
|
|||
return chunks; |
|||
} |
|||
|
|||
export function toASM(chunks: Buffer | Array<number | Buffer>): string { |
|||
if (chunksIsBuffer(chunks)) { |
|||
chunks = decompile(chunks) as Stack; |
|||
} |
|||
|
|||
return chunks |
|||
.map(chunk => { |
|||
// data?
|
|||
if (singleChunkIsBuffer(chunk)) { |
|||
const op = asMinimalOP(chunk); |
|||
if (op === undefined) return chunk.toString('hex'); |
|||
chunk = op as number; |
|||
} |
|||
|
|||
// opcode!
|
|||
return REVERSE_OPS[chunk]; |
|||
}) |
|||
.join(' '); |
|||
} |
|||
|
|||
export function fromASM(asm: string): Buffer { |
|||
typeforce(types.String, asm); |
|||
|
|||
return compile( |
|||
asm.split(' ').map(chunkStr => { |
|||
// opcode?
|
|||
if (OPS[chunkStr] !== undefined) return OPS[chunkStr]; |
|||
typeforce(types.Hex, chunkStr); |
|||
|
|||
// data!
|
|||
return Buffer.from(chunkStr, 'hex'); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
export function toStack(chunks: Buffer | Array<number | Buffer>): Buffer[] { |
|||
chunks = decompile(chunks) as Stack; |
|||
typeforce(isPushOnly, chunks); |
|||
|
|||
return chunks.map(op => { |
|||
if (singleChunkIsBuffer(op)) return op; |
|||
if (op === OPS.OP_0) return Buffer.allocUnsafe(0); |
|||
|
|||
return scriptNumber.encode(op - OP_INT_BASE); |
|||
}); |
|||
} |
|||
|
|||
export function isCanonicalPubKey(buffer: Buffer): boolean { |
|||
return ecc.isPoint(buffer); |
|||
} |
|||
|
|||
export function isDefinedHashType(hashType: number): boolean { |
|||
const hashTypeMod = hashType & ~0x80; |
|||
|
|||
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
|
|||
return hashTypeMod > 0x00 && hashTypeMod < 0x04; |
|||
} |
|||
|
|||
export function isCanonicalScriptSignature(buffer: Buffer): boolean { |
|||
if (!Buffer.isBuffer(buffer)) return false; |
|||
if (!isDefinedHashType(buffer[buffer.length - 1])) return false; |
|||
|
|||
return bip66.check(buffer.slice(0, -1)); |
|||
} |
|||
|
|||
// tslint:disable-next-line variable-name
|
|||
export const number = scriptNumber; |
|||
export const signature = scriptSignature; |
@ -0,0 +1,71 @@ |
|||
export function decode( |
|||
buffer: Buffer, |
|||
maxLength?: number, |
|||
minimal?: boolean, |
|||
): number { |
|||
maxLength = maxLength || 4; |
|||
minimal = minimal === undefined ? true : minimal; |
|||
|
|||
const length = buffer.length; |
|||
if (length === 0) return 0; |
|||
if (length > maxLength) throw new TypeError('Script number overflow'); |
|||
if (minimal) { |
|||
if ((buffer[length - 1] & 0x7f) === 0) { |
|||
if (length <= 1 || (buffer[length - 2] & 0x80) === 0) |
|||
throw new Error('Non-minimally encoded script number'); |
|||
} |
|||
} |
|||
|
|||
// 40-bit
|
|||
if (length === 5) { |
|||
const a = buffer.readUInt32LE(0); |
|||
const b = buffer.readUInt8(4); |
|||
|
|||
if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a); |
|||
return b * 0x100000000 + a; |
|||
} |
|||
|
|||
// 32-bit / 24-bit / 16-bit / 8-bit
|
|||
let result = 0; |
|||
for (let i = 0; i < length; ++i) { |
|||
result |= buffer[i] << (8 * i); |
|||
} |
|||
|
|||
if (buffer[length - 1] & 0x80) |
|||
return -(result & ~(0x80 << (8 * (length - 1)))); |
|||
return result; |
|||
} |
|||
|
|||
function scriptNumSize(i: number): number { |
|||
return i > 0x7fffffff |
|||
? 5 |
|||
: i > 0x7fffff |
|||
? 4 |
|||
: i > 0x7fff |
|||
? 3 |
|||
: i > 0x7f |
|||
? 2 |
|||
: i > 0x00 |
|||
? 1 |
|||
: 0; |
|||
} |
|||
|
|||
export function encode(_number: number): Buffer { |
|||
let value = Math.abs(_number); |
|||
const size = scriptNumSize(value); |
|||
const buffer = Buffer.allocUnsafe(size); |
|||
const negative = _number < 0; |
|||
|
|||
for (let i = 0; i < size; ++i) { |
|||
buffer.writeUInt8(value & 0xff, i); |
|||
value >>= 8; |
|||
} |
|||
|
|||
if (buffer[size - 1] & 0x80) { |
|||
buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); |
|||
} else if (negative) { |
|||
buffer[size - 1] |= 0x80; |
|||
} |
|||
|
|||
return buffer; |
|||
} |
@ -0,0 +1,64 @@ |
|||
import * as types from './types'; |
|||
const bip66 = require('bip66'); |
|||
|
|||
const typeforce = require('typeforce'); |
|||
|
|||
const ZERO = Buffer.alloc(1, 0); |
|||
function toDER(x: Buffer): Buffer { |
|||
let i = 0; |
|||
while (x[i] === 0) ++i; |
|||
if (i === x.length) return ZERO; |
|||
x = x.slice(i); |
|||
if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length); |
|||
return x; |
|||
} |
|||
|
|||
function fromDER(x: Buffer): Buffer { |
|||
if (x[0] === 0x00) x = x.slice(1); |
|||
const buffer = Buffer.alloc(32, 0); |
|||
const bstart = Math.max(0, 32 - x.length); |
|||
x.copy(buffer, bstart); |
|||
return buffer; |
|||
} |
|||
|
|||
interface ScriptSignature { |
|||
signature: Buffer; |
|||
hashType: number; |
|||
} |
|||
|
|||
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
|
|||
export function decode(buffer: Buffer): ScriptSignature { |
|||
const hashType = buffer.readUInt8(buffer.length - 1); |
|||
const hashTypeMod = hashType & ~0x80; |
|||
if (hashTypeMod <= 0 || hashTypeMod >= 4) |
|||
throw new Error('Invalid hashType ' + hashType); |
|||
|
|||
const decoded = bip66.decode(buffer.slice(0, -1)); |
|||
const r = fromDER(decoded.r); |
|||
const s = fromDER(decoded.s); |
|||
const signature = Buffer.concat([r, s], 64); |
|||
|
|||
return { signature, hashType }; |
|||
} |
|||
|
|||
export function encode(signature: Buffer, hashType: number): Buffer { |
|||
typeforce( |
|||
{ |
|||
signature: types.BufferN(64), |
|||
hashType: types.UInt8, |
|||
}, |
|||
{ signature, hashType }, |
|||
); |
|||
|
|||
const hashTypeMod = hashType & ~0x80; |
|||
if (hashTypeMod <= 0 || hashTypeMod >= 4) |
|||
throw new Error('Invalid hashType ' + hashType); |
|||
|
|||
const hashTypeBuffer = Buffer.allocUnsafe(1); |
|||
hashTypeBuffer.writeUInt8(hashType, 0); |
|||
|
|||
const r = toDER(signature.slice(0, 32)); |
|||
const s = toDER(signature.slice(32, 64)); |
|||
|
|||
return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); |
|||
} |
@ -0,0 +1,4 @@ |
|||
import * as input from './input'; |
|||
import * as output from './output'; |
|||
|
|||
export { input, output }; |
@ -0,0 +1,31 @@ |
|||
// OP_0 [signatures ...]
|
|||
|
|||
import { Stack } from '../../payments'; |
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
|
|||
function partialSignature(value: number | Buffer): boolean { |
|||
return ( |
|||
value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value as Buffer) |
|||
); |
|||
} |
|||
|
|||
export function check( |
|||
script: Buffer | Stack, |
|||
allowIncomplete?: boolean, |
|||
): boolean { |
|||
const chunks = bscript.decompile(script) as Stack; |
|||
if (chunks.length < 2) return false; |
|||
if (chunks[0] !== OPS.OP_0) return false; |
|||
|
|||
if (allowIncomplete) { |
|||
return chunks.slice(1).every(partialSignature); |
|||
} |
|||
|
|||
return (chunks.slice(1) as Buffer[]).every( |
|||
bscript.isCanonicalScriptSignature, |
|||
); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'multisig input'; |
|||
}; |
@ -0,0 +1,33 @@ |
|||
// m [pubKeys ...] n OP_CHECKMULTISIG
|
|||
|
|||
import { Stack } from '../../payments'; |
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
import * as types from '../../types'; |
|||
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
|
|||
|
|||
export function check( |
|||
script: Buffer | Stack, |
|||
allowIncomplete?: boolean, |
|||
): boolean { |
|||
const chunks = bscript.decompile(script) as Stack; |
|||
|
|||
if (chunks.length < 4) return false; |
|||
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false; |
|||
if (!types.Number(chunks[0])) return false; |
|||
if (!types.Number(chunks[chunks.length - 2])) return false; |
|||
const m = (chunks[0] as number) - OP_INT_BASE; |
|||
const n = (chunks[chunks.length - 2] as number) - OP_INT_BASE; |
|||
|
|||
if (m <= 0) return false; |
|||
if (n > 16) return false; |
|||
if (m > n) return false; |
|||
if (n !== chunks.length - 3) return false; |
|||
if (allowIncomplete) return true; |
|||
|
|||
const keys = chunks.slice(1, -2) as Buffer[]; |
|||
return keys.every(bscript.isCanonicalPubKey); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'multi-sig output'; |
|||
}; |
@ -0,0 +1,16 @@ |
|||
// OP_RETURN {data}
|
|||
import * as bscript from '../script'; |
|||
const OPS = bscript.OPS; |
|||
|
|||
export function check(script: Buffer | Array<number | Buffer>): boolean { |
|||
const buffer = bscript.compile(script); |
|||
|
|||
return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'null data output'; |
|||
}; |
|||
|
|||
const output = { check }; |
|||
|
|||
export { output }; |
@ -0,0 +1,4 @@ |
|||
import * as input from './input'; |
|||
import * as output from './output'; |
|||
|
|||
export { input, output }; |
@ -0,0 +1,16 @@ |
|||
// {signature}
|
|||
|
|||
import { Stack } from '../../payments'; |
|||
import * as bscript from '../../script'; |
|||
|
|||
export function check(script: Buffer | Stack): boolean { |
|||
const chunks = bscript.decompile(script) as Stack; |
|||
|
|||
return ( |
|||
chunks.length === 1 && |
|||
bscript.isCanonicalScriptSignature(chunks[0] as Buffer) |
|||
); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'pubKey input'; |
|||
}; |
@ -0,0 +1,18 @@ |
|||
// {pubKey} OP_CHECKSIG
|
|||
|
|||
import { Stack } from '../../payments'; |
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
|
|||
export function check(script: Buffer | Stack): boolean { |
|||
const chunks = bscript.decompile(script) as Stack; |
|||
|
|||
return ( |
|||
chunks.length === 2 && |
|||
bscript.isCanonicalPubKey(chunks[0] as Buffer) && |
|||
chunks[1] === OPS.OP_CHECKSIG |
|||
); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'pubKey output'; |
|||
}; |
@ -0,0 +1,4 @@ |
|||
import * as input from './input'; |
|||
import * as output from './output'; |
|||
|
|||
export { input, output }; |
@ -0,0 +1,17 @@ |
|||
// {signature} {pubKey}
|
|||
|
|||
import { Stack } from '../../payments'; |
|||
import * as bscript from '../../script'; |
|||
|
|||
export function check(script: Buffer | Stack): boolean { |
|||
const chunks = bscript.decompile(script) as Stack; |
|||
|
|||
return ( |
|||
chunks.length === 2 && |
|||
bscript.isCanonicalScriptSignature(chunks[0] as Buffer) && |
|||
bscript.isCanonicalPubKey(chunks[1] as Buffer) |
|||
); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'pubKeyHash input'; |
|||
}; |
@ -0,0 +1,20 @@ |
|||
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
|
|||
|
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
|
|||
export function check(script: Buffer | Array<number | Buffer>): boolean { |
|||
const buffer = bscript.compile(script); |
|||
|
|||
return ( |
|||
buffer.length === 25 && |
|||
buffer[0] === OPS.OP_DUP && |
|||
buffer[1] === OPS.OP_HASH160 && |
|||
buffer[2] === 0x14 && |
|||
buffer[23] === OPS.OP_EQUALVERIFY && |
|||
buffer[24] === OPS.OP_CHECKSIG |
|||
); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'pubKeyHash output'; |
|||
}; |
@ -0,0 +1,4 @@ |
|||
import * as input from './input'; |
|||
import * as output from './output'; |
|||
|
|||
export { input, output }; |
@ -0,0 +1,61 @@ |
|||
// <scriptSig> {serialized scriptPubKey script}
|
|||
|
|||
import * as bscript from '../../script'; |
|||
import * as p2ms from '../multisig'; |
|||
import * as p2pk from '../pubkey'; |
|||
import * as p2pkh from '../pubkeyhash'; |
|||
import * as p2wpkho from '../witnesspubkeyhash/output'; |
|||
import * as p2wsho from '../witnessscripthash/output'; |
|||
|
|||
export function check( |
|||
script: Buffer | Array<number | Buffer>, |
|||
allowIncomplete?: boolean, |
|||
): boolean { |
|||
const chunks = bscript.decompile(script)!; |
|||
if (chunks.length < 1) return false; |
|||
|
|||
const lastChunk = chunks[chunks.length - 1]; |
|||
if (!Buffer.isBuffer(lastChunk)) return false; |
|||
|
|||
const scriptSigChunks = bscript.decompile( |
|||
bscript.compile(chunks.slice(0, -1)), |
|||
)!; |
|||
const redeemScriptChunks = bscript.decompile(lastChunk); |
|||
|
|||
// is redeemScript a valid script?
|
|||
if (!redeemScriptChunks) return false; |
|||
|
|||
// is redeemScriptSig push only?
|
|||
if (!bscript.isPushOnly(scriptSigChunks)) return false; |
|||
|
|||
// is witness?
|
|||
if (chunks.length === 1) { |
|||
return ( |
|||
p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks) |
|||
); |
|||
} |
|||
|
|||
// match types
|
|||
if ( |
|||
p2pkh.input.check(scriptSigChunks) && |
|||
p2pkh.output.check(redeemScriptChunks) |
|||
) |
|||
return true; |
|||
|
|||
if ( |
|||
p2ms.input.check(scriptSigChunks, allowIncomplete) && |
|||
p2ms.output.check(redeemScriptChunks) |
|||
) |
|||
return true; |
|||
|
|||
if ( |
|||
p2pk.input.check(scriptSigChunks) && |
|||
p2pk.output.check(redeemScriptChunks) |
|||
) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'scriptHash input'; |
|||
}; |
@ -0,0 +1,18 @@ |
|||
// OP_HASH160 {scriptHash} OP_EQUAL
|
|||
|
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
|
|||
export function check(script: Buffer | Array<number | Buffer>): boolean { |
|||
const buffer = bscript.compile(script); |
|||
|
|||
return ( |
|||
buffer.length === 23 && |
|||
buffer[0] === OPS.OP_HASH160 && |
|||
buffer[1] === 0x14 && |
|||
buffer[22] === OPS.OP_EQUAL |
|||
); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'scriptHash output'; |
|||
}; |
@ -0,0 +1,3 @@ |
|||
import * as output from './output'; |
|||
|
|||
export { output }; |
@ -0,0 +1,40 @@ |
|||
// OP_RETURN {aa21a9ed} {commitment}
|
|||
|
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
import * as types from '../../types'; |
|||
|
|||
const typeforce = require('typeforce'); |
|||
|
|||
const HEADER: Buffer = Buffer.from('aa21a9ed', 'hex'); |
|||
|
|||
export function check(script: Buffer | Array<number | Buffer>): boolean { |
|||
const buffer = bscript.compile(script); |
|||
|
|||
return ( |
|||
buffer.length > 37 && |
|||
buffer[0] === OPS.OP_RETURN && |
|||
buffer[1] === 0x24 && |
|||
buffer.slice(2, 6).equals(HEADER) |
|||
); |
|||
} |
|||
|
|||
check.toJSON = (): string => { |
|||
return 'Witness commitment output'; |
|||
}; |
|||
|
|||
export function encode(commitment: Buffer): Buffer { |
|||
typeforce(types.Hash256bit, commitment); |
|||
|
|||
const buffer = Buffer.allocUnsafe(36); |
|||
HEADER.copy(buffer, 0); |
|||
commitment.copy(buffer, 4); |
|||
|
|||
return bscript.compile([OPS.OP_RETURN, buffer]); |
|||
} |
|||
|
|||
export function decode(buffer: Buffer): Buffer { |
|||
typeforce(check, buffer); |
|||
|
|||
return (bscript.decompile(buffer)![1] as Buffer).slice(4, 36); |
|||
} |
@ -0,0 +1,4 @@ |
|||
import * as input from './input'; |
|||
import * as output from './output'; |
|||
|
|||
export { input, output }; |
@ -0,0 +1,21 @@ |
|||
// {signature} {pubKey}
|
|||
|
|||
import { Stack } from '../../payments'; |
|||
import * as bscript from '../../script'; |
|||
|
|||
function isCompressedCanonicalPubKey(pubKey: Buffer): boolean { |
|||
return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; |
|||
} |
|||
|
|||
export function check(script: Buffer | Stack): boolean { |
|||
const chunks = bscript.decompile(script) as Stack; |
|||
|
|||
return ( |
|||
chunks.length === 2 && |
|||
bscript.isCanonicalScriptSignature(chunks[0] as Buffer) && |
|||
isCompressedCanonicalPubKey(chunks[1] as Buffer) |
|||
); |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'witnessPubKeyHash input'; |
|||
}; |
@ -0,0 +1,13 @@ |
|||
// OP_0 {pubKeyHash}
|
|||
|
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
|
|||
export function check(script: Buffer | Array<number | Buffer>): boolean { |
|||
const buffer = bscript.compile(script); |
|||
|
|||
return buffer.length === 22 && buffer[0] === OPS.OP_0 && buffer[1] === 0x14; |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'Witness pubKeyHash output'; |
|||
}; |
@ -0,0 +1,4 @@ |
|||
import * as input from './input'; |
|||
import * as output from './output'; |
|||
|
|||
export { input, output }; |
@ -0,0 +1,47 @@ |
|||
// <scriptSig> {serialized scriptPubKey script}
|
|||
|
|||
import * as bscript from '../../script'; |
|||
const typeforce = require('typeforce'); |
|||
|
|||
import * as p2ms from '../multisig'; |
|||
import * as p2pk from '../pubkey'; |
|||
import * as p2pkh from '../pubkeyhash'; |
|||
|
|||
export function check(chunks: Buffer[], allowIncomplete?: boolean): boolean { |
|||
typeforce(typeforce.Array, chunks); |
|||
if (chunks.length < 1) return false; |
|||
|
|||
const witnessScript = chunks[chunks.length - 1]; |
|||
if (!Buffer.isBuffer(witnessScript)) return false; |
|||
|
|||
const witnessScriptChunks = bscript.decompile(witnessScript); |
|||
|
|||
// is witnessScript a valid script?
|
|||
if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false; |
|||
|
|||
const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); |
|||
|
|||
// match types
|
|||
if ( |
|||
p2pkh.input.check(witnessRawScriptSig) && |
|||
p2pkh.output.check(witnessScriptChunks) |
|||
) |
|||
return true; |
|||
|
|||
if ( |
|||
p2ms.input.check(witnessRawScriptSig, allowIncomplete) && |
|||
p2ms.output.check(witnessScriptChunks) |
|||
) |
|||
return true; |
|||
|
|||
if ( |
|||
p2pk.input.check(witnessRawScriptSig) && |
|||
p2pk.output.check(witnessScriptChunks) |
|||
) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'witnessScriptHash input'; |
|||
}; |
@ -0,0 +1,13 @@ |
|||
// OP_0 {scriptHash}
|
|||
|
|||
import * as bscript from '../../script'; |
|||
import { OPS } from '../../script'; |
|||
|
|||
export function check(script: Buffer | Array<number | Buffer>): boolean { |
|||
const buffer = bscript.compile(script); |
|||
|
|||
return buffer.length === 34 && buffer[0] === OPS.OP_0 && buffer[1] === 0x20; |
|||
} |
|||
check.toJSON = (): string => { |
|||
return 'Witness scriptHash output'; |
|||
}; |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue