Browse Source

Use Prettier to make JS easier to read/audit

trimSpace
junderw 6 years ago
parent
commit
e28e04427e
No known key found for this signature in database GPG Key ID: B256185D3A971908
  1. 5
      package.json
  2. 145
      src/address.js
  3. 444
      src/block.js
  4. 58
      src/bufferutils.js
  5. 110
      src/classify.js
  6. 39
      src/crypto.js
  7. 149
      src/ecpair.js
  8. 26
      src/index.js
  9. 58
      src/networks.js
  10. 82
      src/payments/embed.js
  11. 18
      src/payments/index.js
  12. 47
      src/payments/lazy.js
  13. 254
      src/payments/p2ms.js
  14. 129
      src/payments/p2pk.js
  15. 254
      src/payments/p2pkh.js
  16. 329
      src/payments/p2sh.js
  17. 242
      src/payments/p2wpkh.js
  18. 321
      src/payments/p2wsh.js
  19. 264
      src/script.js
  20. 108
      src/script_number.js
  21. 77
      src/script_signature.js
  22. 8
      src/templates/multisig/index.js
  23. 30
      src/templates/multisig/input.js
  24. 49
      src/templates/multisig/output.js
  25. 12
      src/templates/nulldata.js
  26. 8
      src/templates/pubkey/index.js
  27. 13
      src/templates/pubkey/input.js
  28. 20
      src/templates/pubkey/output.js
  29. 8
      src/templates/pubkeyhash/index.js
  30. 18
      src/templates/pubkeyhash/input.js
  31. 26
      src/templates/pubkeyhash/output.js
  32. 8
      src/templates/scripthash/index.js
  33. 82
      src/templates/scripthash/input.js
  34. 22
      src/templates/scripthash/output.js
  35. 6
      src/templates/witnesscommitment/index.js
  36. 38
      src/templates/witnesscommitment/output.js
  37. 8
      src/templates/witnesspubkeyhash/index.js
  38. 20
      src/templates/witnesspubkeyhash/input.js
  39. 18
      src/templates/witnesspubkeyhash/output.js
  40. 8
      src/templates/witnessscripthash/index.js
  41. 61
      src/templates/witnessscripthash/input.js
  42. 18
      src/templates/witnessscripthash/output.js
  43. 882
      src/transaction.js
  44. 1379
      src/transaction_builder.js
  45. 28
      src/types.js

5
package.json

@ -15,11 +15,13 @@
"bitcoinjs" "bitcoinjs"
], ],
"scripts": { "scripts": {
"build": "tsc -p ./tsconfig.json", "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs",
"clean": "rm -rf src/",
"coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-report": "npm run build && npm run nobuild:coverage-report",
"coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage-html": "npm run build && npm run nobuild:coverage-html",
"coverage": "npm run build && npm run nobuild:coverage", "coverage": "npm run build && npm run nobuild:coverage",
"format": "npm run prettier -- --write", "format": "npm run prettier -- --write",
"formatjs": "npm run prettierjs -- --write > /dev/null 2>&1",
"format:ci": "npm run prettier -- --check", "format:ci": "npm run prettier -- --check",
"gitdiff:ci": "npm run build && git diff --exit-code", "gitdiff:ci": "npm run build && git diff --exit-code",
"integration": "npm run build && npm run nobuild:integration", "integration": "npm run build && npm run nobuild:integration",
@ -30,6 +32,7 @@
"nobuild:integration": "mocha --timeout 50000 test/integration/", "nobuild:integration": "mocha --timeout 50000 test/integration/",
"nobuild:unit": "mocha", "nobuild:unit": "mocha",
"prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore",
"prettierjs": "prettier 'src/**/*.js' --ignore-path ./.prettierignore",
"test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage", "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage",
"unit": "npm run build && npm run nobuild:unit" "unit": "npm run build && npm run nobuild:unit"
}, },

145
src/address.js

@ -1,100 +1,91 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const networks = require("./networks"); const networks = require('./networks');
const payments = require("./payments"); const payments = require('./payments');
const bscript = require("./script"); const bscript = require('./script');
const types = require("./types"); const types = require('./types');
const bech32 = require('bech32'); const bech32 = require('bech32');
const bs58check = require('bs58check'); const bs58check = require('bs58check');
const typeforce = require('typeforce'); const typeforce = require('typeforce');
function fromBase58Check(address) { function fromBase58Check(address) {
const payload = bs58check.decode(address); const payload = bs58check.decode(address);
// TODO: 4.0.0, move to "toOutputScript" // TODO: 4.0.0, move to "toOutputScript"
if (payload.length < 21) if (payload.length < 21) throw new TypeError(address + ' is too short');
throw new TypeError(address + ' is too short'); if (payload.length > 21) throw new TypeError(address + ' is too long');
if (payload.length > 21) const version = payload.readUInt8(0);
throw new TypeError(address + ' is too long'); const hash = payload.slice(1);
const version = payload.readUInt8(0); return { version, hash };
const hash = payload.slice(1);
return { version, hash };
} }
exports.fromBase58Check = fromBase58Check; exports.fromBase58Check = fromBase58Check;
function fromBech32(address) { function fromBech32(address) {
const result = bech32.decode(address); const result = bech32.decode(address);
const data = bech32.fromWords(result.words.slice(1)); const data = bech32.fromWords(result.words.slice(1));
return { return {
version: result.words[0], version: result.words[0],
prefix: result.prefix, prefix: result.prefix,
data: Buffer.from(data), data: Buffer.from(data),
}; };
} }
exports.fromBech32 = fromBech32; exports.fromBech32 = fromBech32;
function toBase58Check(hash, version) { function toBase58Check(hash, version) {
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments);
const payload = Buffer.allocUnsafe(21); const payload = Buffer.allocUnsafe(21);
payload.writeUInt8(version, 0); payload.writeUInt8(version, 0);
hash.copy(payload, 1); hash.copy(payload, 1);
return bs58check.encode(payload); return bs58check.encode(payload);
} }
exports.toBase58Check = toBase58Check; exports.toBase58Check = toBase58Check;
function toBech32(data, version, prefix) { function toBech32(data, version, prefix) {
const words = bech32.toWords(data); const words = bech32.toWords(data);
words.unshift(version); words.unshift(version);
return bech32.encode(prefix, words); return bech32.encode(prefix, words);
} }
exports.toBech32 = toBech32; exports.toBech32 = toBech32;
function fromOutputScript(output, network) { function fromOutputScript(output, network) {
// TODO: Network // TODO: Network
network = network || networks.bitcoin; network = network || networks.bitcoin;
try { try {
return payments.p2pkh({ output, network }).address; return payments.p2pkh({ output, network }).address;
} } catch (e) {}
catch (e) { } try {
try { return payments.p2sh({ output, network }).address;
return payments.p2sh({ output, network }).address; } catch (e) {}
} try {
catch (e) { } return payments.p2wpkh({ output, network }).address;
try { } catch (e) {}
return payments.p2wpkh({ output, network }).address; try {
} return payments.p2wsh({ output, network }).address;
catch (e) { } } catch (e) {}
try { throw new Error(bscript.toASM(output) + ' has no matching Address');
return payments.p2wsh({ output, network }).address;
}
catch (e) { }
throw new Error(bscript.toASM(output) + ' has no matching Address');
} }
exports.fromOutputScript = fromOutputScript; exports.fromOutputScript = fromOutputScript;
function toOutputScript(address, network) { function toOutputScript(address, network) {
network = network || networks.bitcoin; network = network || networks.bitcoin;
let decodeBase58; let decodeBase58;
let decodeBech32; 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 { try {
decodeBase58 = fromBase58Check(address); decodeBech32 = fromBech32(address);
} } catch (e) {}
catch (e) { } if (decodeBech32) {
if (decodeBase58) { if (decodeBech32.prefix !== network.bech32)
if (decodeBase58.version === network.pubKeyHash) throw new Error(address + ' has an invalid prefix');
return payments.p2pkh({ hash: decodeBase58.hash }).output; if (decodeBech32.version === 0) {
if (decodeBase58.version === network.scriptHash) if (decodeBech32.data.length === 20)
return payments.p2sh({ hash: decodeBase58.hash }).output; return payments.p2wpkh({ hash: decodeBech32.data }).output;
} if (decodeBech32.data.length === 32)
else { return payments.p2wsh({ hash: decodeBech32.data }).output;
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'); }
throw new Error(address + ' has no matching Script');
} }
exports.toOutputScript = toOutputScript; exports.toOutputScript = toOutputScript;

444
src/block.js

@ -1,222 +1,242 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bufferutils_1 = require("./bufferutils"); const bufferutils_1 = require('./bufferutils');
const bcrypto = require("./crypto"); const bcrypto = require('./crypto');
const transaction_1 = require("./transaction"); const transaction_1 = require('./transaction');
const types = require("./types"); const types = require('./types');
const fastMerkleRoot = require('merkle-lib/fastRoot'); const fastMerkleRoot = require('merkle-lib/fastRoot');
const typeforce = require('typeforce'); const typeforce = require('typeforce');
const varuint = require('varuint-bitcoin'); const varuint = require('varuint-bitcoin');
const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions'); const errorMerkleNoTxes = new TypeError(
const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block'); 'Cannot compute merkle root for zero transactions',
);
const errorWitnessNotSegwit = new TypeError(
'Cannot compute witness commit for non-segwit block',
);
class Block { class Block {
constructor() { constructor() {
this.version = 1; this.version = 1;
this.prevHash = undefined; this.prevHash = undefined;
this.merkleRoot = undefined; this.merkleRoot = undefined;
this.timestamp = 0; this.timestamp = 0;
this.witnessCommit = undefined; this.witnessCommit = undefined;
this.bits = 0; this.bits = 0;
this.nonce = 0; this.nonce = 0;
this.transactions = undefined; this.transactions = undefined;
} }
static fromBuffer(buffer) { static fromBuffer(buffer) {
if (buffer.length < 80) if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
throw new Error('Buffer too small (< 80 bytes)'); let offset = 0;
let offset = 0; const readSlice = n => {
const readSlice = (n) => { offset += n;
offset += n; return buffer.slice(offset - n, offset);
return buffer.slice(offset - n, offset); };
}; const readUInt32 = () => {
const readUInt32 = () => { const i = buffer.readUInt32LE(offset);
const i = buffer.readUInt32LE(offset); offset += 4;
offset += 4; return i;
return i; };
}; const readInt32 = () => {
const readInt32 = () => { const i = buffer.readInt32LE(offset);
const i = buffer.readInt32LE(offset); offset += 4;
offset += 4; return i;
return i; };
}; const block = new Block();
const block = new Block(); block.version = readInt32();
block.version = readInt32(); block.prevHash = readSlice(32);
block.prevHash = readSlice(32); block.merkleRoot = readSlice(32);
block.merkleRoot = readSlice(32); block.timestamp = readUInt32();
block.timestamp = readUInt32(); block.bits = readUInt32();
block.bits = readUInt32(); block.nonce = readUInt32();
block.nonce = readUInt32(); if (buffer.length === 80) return block;
if (buffer.length === 80) const readVarInt = () => {
return block; const vi = varuint.decode(buffer, offset);
const readVarInt = () => { offset += varuint.decode.bytes;
const vi = varuint.decode(buffer, offset); return vi;
offset += varuint.decode.bytes; };
return vi; const readTransaction = () => {
}; const tx = transaction_1.Transaction.fromBuffer(
const readTransaction = () => { buffer.slice(offset),
const tx = transaction_1.Transaction.fromBuffer(buffer.slice(offset), true); true,
offset += tx.byteLength(); );
return tx; offset += tx.byteLength();
}; return tx;
const nTransactions = readVarInt(); };
block.transactions = []; const nTransactions = readVarInt();
for (let i = 0; i < nTransactions; ++i) { block.transactions = [];
const tx = readTransaction(); for (let i = 0; i < nTransactions; ++i) {
block.transactions.push(tx); const tx = readTransaction();
} block.transactions.push(tx);
const witnessCommit = block.getWitnessCommit(); }
// This Block contains a witness commit const witnessCommit = block.getWitnessCommit();
if (witnessCommit) // This Block contains a witness commit
block.witnessCommit = witnessCommit; if (witnessCommit) block.witnessCommit = witnessCommit;
return block; return block;
} }
static fromHex(hex) { static fromHex(hex) {
return Block.fromBuffer(Buffer.from(hex, 'hex')); return Block.fromBuffer(Buffer.from(hex, 'hex'));
} }
static calculateTarget(bits) { static calculateTarget(bits) {
const exponent = ((bits & 0xff000000) >> 24) - 3; const exponent = ((bits & 0xff000000) >> 24) - 3;
const mantissa = bits & 0x007fffff; const mantissa = bits & 0x007fffff;
const target = Buffer.alloc(32, 0); const target = Buffer.alloc(32, 0);
target.writeUIntBE(mantissa, 29 - exponent, 3); target.writeUIntBE(mantissa, 29 - exponent, 3);
return target; return target;
} }
static calculateMerkleRoot(transactions, forWitness) { static calculateMerkleRoot(transactions, forWitness) {
typeforce([{ getHash: types.Function }], transactions); typeforce([{ getHash: types.Function }], transactions);
if (transactions.length === 0) if (transactions.length === 0) throw errorMerkleNoTxes;
throw errorMerkleNoTxes; if (forWitness && !txesHaveWitnessCommit(transactions))
if (forWitness && !txesHaveWitnessCommit(transactions)) throw errorWitnessNotSegwit;
throw errorWitnessNotSegwit; const hashes = transactions.map(transaction =>
const hashes = transactions.map(transaction => transaction.getHash(forWitness)); transaction.getHash(forWitness),
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); );
return forWitness const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) return forWitness
: rootHash; ? bcrypto.hash256(
} Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]),
getWitnessCommit() { )
if (!txesHaveWitnessCommit(this.transactions)) : rootHash;
return null; }
// The merkle root for the witness data is in an OP_RETURN output. getWitnessCommit() {
// There is no rule for the index of the output, so use filter to find it. if (!txesHaveWitnessCommit(this.transactions)) return null;
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed // The merkle root for the witness data is in an OP_RETURN output.
// If multiple commits are found, the output with highest index is assumed. // There is no rule for the index of the output, so use filter to find it.
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)); // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
if (witnessCommits.length === 0) // If multiple commits are found, the output with highest index is assumed.
return null; const witnessCommits = this.transactions[0].outs
// Use the commit with the highest output (should only be one though) .filter(out =>
const result = witnessCommits[witnessCommits.length - 1]; out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
if (!(result instanceof Buffer && result.length === 32)) )
return null; .map(out => out.script.slice(6, 38));
return result; if (witnessCommits.length === 0) return null;
} // Use the commit with the highest output (should only be one though)
hasWitnessCommit() { const result = witnessCommits[witnessCommits.length - 1];
if (this.witnessCommit instanceof Buffer && if (!(result instanceof Buffer && result.length === 32)) return null;
this.witnessCommit.length === 32) return result;
return true; }
if (this.getWitnessCommit() !== null) hasWitnessCommit() {
return true; if (
return false; this.witnessCommit instanceof Buffer &&
} this.witnessCommit.length === 32
hasWitness() { )
return anyTxHasWitness(this.transactions); return true;
} if (this.getWitnessCommit() !== null) return true;
byteLength(headersOnly) { return false;
if (headersOnly || !this.transactions) }
return 80; hasWitness() {
return (80 + return anyTxHasWitness(this.transactions);
varuint.encodingLength(this.transactions.length) + }
this.transactions.reduce((a, x) => a + x.byteLength(), 0)); byteLength(headersOnly) {
} if (headersOnly || !this.transactions) return 80;
getHash() { return (
return bcrypto.hash256(this.toBuffer(true)); 80 +
} varuint.encodingLength(this.transactions.length) +
getId() { this.transactions.reduce((a, x) => a + x.byteLength(), 0)
return bufferutils_1.reverseBuffer(this.getHash()).toString('hex'); );
} }
getUTCDate() { getHash() {
const date = new Date(0); // epoch return bcrypto.hash256(this.toBuffer(true));
date.setUTCSeconds(this.timestamp); }
return date; getId() {
} return bufferutils_1.reverseBuffer(this.getHash()).toString('hex');
// TODO: buffer, offset compatibility }
toBuffer(headersOnly) { getUTCDate() {
const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); const date = new Date(0); // epoch
let offset = 0; date.setUTCSeconds(this.timestamp);
const writeSlice = (slice) => { return date;
slice.copy(buffer, offset); }
offset += slice.length; // TODO: buffer, offset compatibility
}; toBuffer(headersOnly) {
const writeInt32 = (i) => { const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
buffer.writeInt32LE(i, offset); let offset = 0;
offset += 4; const writeSlice = slice => {
}; slice.copy(buffer, offset);
const writeUInt32 = (i) => { offset += slice.length;
buffer.writeUInt32LE(i, offset); };
offset += 4; const writeInt32 = i => {
}; buffer.writeInt32LE(i, offset);
writeInt32(this.version); offset += 4;
writeSlice(this.prevHash); };
writeSlice(this.merkleRoot); const writeUInt32 = i => {
writeUInt32(this.timestamp); buffer.writeUInt32LE(i, offset);
writeUInt32(this.bits); offset += 4;
writeUInt32(this.nonce); };
if (headersOnly || !this.transactions) writeInt32(this.version);
return buffer; writeSlice(this.prevHash);
varuint.encode(this.transactions.length, buffer, offset); writeSlice(this.merkleRoot);
offset += varuint.encode.bytes; writeUInt32(this.timestamp);
this.transactions.forEach(tx => { writeUInt32(this.bits);
const txSize = tx.byteLength(); // TODO: extract from toBuffer? writeUInt32(this.nonce);
tx.toBuffer(buffer, offset); if (headersOnly || !this.transactions) return buffer;
offset += txSize; varuint.encode(this.transactions.length, buffer, offset);
}); offset += varuint.encode.bytes;
return buffer; this.transactions.forEach(tx => {
} const txSize = tx.byteLength(); // TODO: extract from toBuffer?
toHex(headersOnly) { tx.toBuffer(buffer, offset);
return this.toBuffer(headersOnly).toString('hex'); offset += txSize;
} });
checkTxRoots() { return buffer;
// If the Block has segwit transactions but no witness commit, }
// there's no way it can be valid, so fail the check. toHex(headersOnly) {
const hasWitnessCommit = this.hasWitnessCommit(); return this.toBuffer(headersOnly).toString('hex');
if (!hasWitnessCommit && this.hasWitness()) }
return false; checkTxRoots() {
return (this.__checkMerkleRoot() && // If the Block has segwit transactions but no witness commit,
(hasWitnessCommit ? this.__checkWitnessCommit() : true)); // there's no way it can be valid, so fail the check.
} const hasWitnessCommit = this.hasWitnessCommit();
checkProofOfWork() { if (!hasWitnessCommit && this.hasWitness()) return false;
const hash = bufferutils_1.reverseBuffer(this.getHash()); return (
const target = Block.calculateTarget(this.bits); this.__checkMerkleRoot() &&
return hash.compare(target) <= 0; (hasWitnessCommit ? this.__checkWitnessCommit() : true)
} );
__checkMerkleRoot() { }
if (!this.transactions) checkProofOfWork() {
throw errorMerkleNoTxes; const hash = bufferutils_1.reverseBuffer(this.getHash());
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); const target = Block.calculateTarget(this.bits);
return this.merkleRoot.compare(actualMerkleRoot) === 0; return hash.compare(target) <= 0;
} }
__checkWitnessCommit() { __checkMerkleRoot() {
if (!this.transactions) if (!this.transactions) throw errorMerkleNoTxes;
throw errorMerkleNoTxes; const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
if (!this.hasWitnessCommit()) return this.merkleRoot.compare(actualMerkleRoot) === 0;
throw errorWitnessNotSegwit; }
const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true); __checkWitnessCommit() {
return this.witnessCommit.compare(actualWitnessCommit) === 0; if (!this.transactions) throw errorMerkleNoTxes;
} if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit;
const actualWitnessCommit = Block.calculateMerkleRoot(
this.transactions,
true,
);
return this.witnessCommit.compare(actualWitnessCommit) === 0;
}
} }
exports.Block = Block; exports.Block = Block;
function txesHaveWitnessCommit(transactions) { function txesHaveWitnessCommit(transactions) {
return (transactions instanceof Array && return (
transactions[0] && transactions instanceof Array &&
transactions[0].ins && transactions[0] &&
transactions[0].ins instanceof Array && transactions[0].ins &&
transactions[0].ins[0] && transactions[0].ins instanceof Array &&
transactions[0].ins[0].witness && transactions[0].ins[0] &&
transactions[0].ins[0].witness instanceof Array && transactions[0].ins[0].witness &&
transactions[0].ins[0].witness.length > 0); transactions[0].ins[0].witness instanceof Array &&
transactions[0].ins[0].witness.length > 0
);
} }
function anyTxHasWitness(transactions) { function anyTxHasWitness(transactions) {
return (transactions instanceof Array && return (
transactions.some(tx => typeof tx === 'object' && transactions instanceof Array &&
tx.ins instanceof Array && transactions.some(
tx.ins.some(input => typeof input === 'object' && tx =>
input.witness instanceof Array && typeof tx === 'object' &&
input.witness.length > 0))); tx.ins instanceof Array &&
tx.ins.some(
input =>
typeof input === 'object' &&
input.witness instanceof Array &&
input.witness.length > 0,
),
)
);
} }

58
src/bufferutils.js

@ -1,42 +1,40 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
// https://github.com/feross/buffer/blob/master/index.js#L1127 // https://github.com/feross/buffer/blob/master/index.js#L1127
function verifuint(value, max) { function verifuint(value, max) {
if (typeof value !== 'number') if (typeof value !== 'number')
throw new Error('cannot write a non-number as a number'); throw new Error('cannot write a non-number as a number');
if (value < 0) if (value < 0)
throw new Error('specified a negative value for writing an unsigned value'); throw new Error('specified a negative value for writing an unsigned value');
if (value > max) if (value > max) throw new Error('RangeError: value out of range');
throw new Error('RangeError: value out of range'); if (Math.floor(value) !== value)
if (Math.floor(value) !== value) throw new Error('value has a fractional component');
throw new Error('value has a fractional component');
} }
function readUInt64LE(buffer, offset) { function readUInt64LE(buffer, offset) {
const a = buffer.readUInt32LE(offset); const a = buffer.readUInt32LE(offset);
let b = buffer.readUInt32LE(offset + 4); let b = buffer.readUInt32LE(offset + 4);
b *= 0x100000000; b *= 0x100000000;
verifuint(b + a, 0x001fffffffffffff); verifuint(b + a, 0x001fffffffffffff);
return b + a; return b + a;
} }
exports.readUInt64LE = readUInt64LE; exports.readUInt64LE = readUInt64LE;
function writeUInt64LE(buffer, value, offset) { function writeUInt64LE(buffer, value, offset) {
verifuint(value, 0x001fffffffffffff); verifuint(value, 0x001fffffffffffff);
buffer.writeInt32LE(value & -1, offset); buffer.writeInt32LE(value & -1, offset);
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4);
return offset + 8; return offset + 8;
} }
exports.writeUInt64LE = writeUInt64LE; exports.writeUInt64LE = writeUInt64LE;
function reverseBuffer(buffer) { function reverseBuffer(buffer) {
if (buffer.length < 1) if (buffer.length < 1) return buffer;
return buffer; let j = buffer.length - 1;
let j = buffer.length - 1; let tmp = 0;
let tmp = 0; for (let i = 0; i < buffer.length / 2; i++) {
for (let i = 0; i < buffer.length / 2; i++) { tmp = buffer[i];
tmp = buffer[i]; buffer[i] = buffer[j];
buffer[i] = buffer[j]; buffer[j] = tmp;
buffer[j] = tmp; j--;
j--; }
} return buffer;
return buffer;
} }
exports.reverseBuffer = reverseBuffer; exports.reverseBuffer = reverseBuffer;

110
src/classify.js

@ -1,75 +1,59 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const script_1 = require("./script"); const script_1 = require('./script');
const multisig = require("./templates/multisig"); const multisig = require('./templates/multisig');
const nullData = require("./templates/nulldata"); const nullData = require('./templates/nulldata');
const pubKey = require("./templates/pubkey"); const pubKey = require('./templates/pubkey');
const pubKeyHash = require("./templates/pubkeyhash"); const pubKeyHash = require('./templates/pubkeyhash');
const scriptHash = require("./templates/scripthash"); const scriptHash = require('./templates/scripthash');
const witnessCommitment = require("./templates/witnesscommitment"); const witnessCommitment = require('./templates/witnesscommitment');
const witnessPubKeyHash = require("./templates/witnesspubkeyhash"); const witnessPubKeyHash = require('./templates/witnesspubkeyhash');
const witnessScriptHash = require("./templates/witnessscripthash"); const witnessScriptHash = require('./templates/witnessscripthash');
const types = { const types = {
P2MS: 'multisig', P2MS: 'multisig',
NONSTANDARD: 'nonstandard', NONSTANDARD: 'nonstandard',
NULLDATA: 'nulldata', NULLDATA: 'nulldata',
P2PK: 'pubkey', P2PK: 'pubkey',
P2PKH: 'pubkeyhash', P2PKH: 'pubkeyhash',
P2SH: 'scripthash', P2SH: 'scripthash',
P2WPKH: 'witnesspubkeyhash', P2WPKH: 'witnesspubkeyhash',
P2WSH: 'witnessscripthash', P2WSH: 'witnessscripthash',
WITNESS_COMMITMENT: 'witnesscommitment', WITNESS_COMMITMENT: 'witnesscommitment',
}; };
exports.types = types; exports.types = types;
function classifyOutput(script) { function classifyOutput(script) {
if (witnessPubKeyHash.output.check(script)) if (witnessPubKeyHash.output.check(script)) return types.P2WPKH;
return types.P2WPKH; if (witnessScriptHash.output.check(script)) return types.P2WSH;
if (witnessScriptHash.output.check(script)) if (pubKeyHash.output.check(script)) return types.P2PKH;
return types.P2WSH; if (scriptHash.output.check(script)) return types.P2SH;
if (pubKeyHash.output.check(script)) // XXX: optimization, below functions .decompile before use
return types.P2PKH; const chunks = script_1.decompile(script);
if (scriptHash.output.check(script)) if (!chunks) throw new TypeError('Invalid script');
return types.P2SH; if (multisig.output.check(chunks)) return types.P2MS;
// XXX: optimization, below functions .decompile before use if (pubKey.output.check(chunks)) return types.P2PK;
const chunks = script_1.decompile(script); if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT;
if (!chunks) if (nullData.output.check(chunks)) return types.NULLDATA;
throw new TypeError('Invalid script'); return types.NONSTANDARD;
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; exports.output = classifyOutput;
function classifyInput(script, allowIncomplete) { function classifyInput(script, allowIncomplete) {
// XXX: optimization, below functions .decompile before use // XXX: optimization, below functions .decompile before use
const chunks = script_1.decompile(script); const chunks = script_1.decompile(script);
if (!chunks) if (!chunks) throw new TypeError('Invalid script');
throw new TypeError('Invalid script'); if (pubKeyHash.input.check(chunks)) return types.P2PKH;
if (pubKeyHash.input.check(chunks)) if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH;
return types.P2PKH; if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS;
if (scriptHash.input.check(chunks, allowIncomplete)) if (pubKey.input.check(chunks)) return types.P2PK;
return types.P2SH; return types.NONSTANDARD;
if (multisig.input.check(chunks, allowIncomplete))
return types.P2MS;
if (pubKey.input.check(chunks))
return types.P2PK;
return types.NONSTANDARD;
} }
exports.input = classifyInput; exports.input = classifyInput;
function classifyWitness(script, allowIncomplete) { function classifyWitness(script, allowIncomplete) {
// XXX: optimization, below functions .decompile before use // XXX: optimization, below functions .decompile before use
const chunks = script_1.decompile(script); const chunks = script_1.decompile(script);
if (!chunks) if (!chunks) throw new TypeError('Invalid script');
throw new TypeError('Invalid script'); if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH;
if (witnessPubKeyHash.input.check(chunks)) if (witnessScriptHash.input.check(chunks, allowIncomplete))
return types.P2WPKH; return types.P2WSH;
if (witnessScriptHash.input.check(chunks, allowIncomplete)) return types.NONSTANDARD;
return types.P2WSH;
return types.NONSTANDARD;
} }
exports.witness = classifyWitness; exports.witness = classifyWitness;

39
src/crypto.js

@ -1,36 +1,35 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const createHash = require('create-hash'); const createHash = require('create-hash');
function ripemd160(buffer) { function ripemd160(buffer) {
try { try {
return createHash('rmd160') return createHash('rmd160')
.update(buffer) .update(buffer)
.digest(); .digest();
} } catch (err) {
catch (err) { return createHash('ripemd160')
return createHash('ripemd160') .update(buffer)
.update(buffer) .digest();
.digest(); }
}
} }
exports.ripemd160 = ripemd160; exports.ripemd160 = ripemd160;
function sha1(buffer) { function sha1(buffer) {
return createHash('sha1') return createHash('sha1')
.update(buffer) .update(buffer)
.digest(); .digest();
} }
exports.sha1 = sha1; exports.sha1 = sha1;
function sha256(buffer) { function sha256(buffer) {
return createHash('sha256') return createHash('sha256')
.update(buffer) .update(buffer)
.digest(); .digest();
} }
exports.sha256 = sha256; exports.sha256 = sha256;
function hash160(buffer) { function hash160(buffer) {
return ripemd160(sha256(buffer)); return ripemd160(sha256(buffer));
} }
exports.hash160 = hash160; exports.hash160 = hash160;
function hash256(buffer) { function hash256(buffer) {
return sha256(sha256(buffer)); return sha256(sha256(buffer));
} }
exports.hash256 = hash256; exports.hash256 = hash256;

149
src/ecpair.js

@ -1,98 +1,91 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const NETWORKS = require("./networks"); const NETWORKS = require('./networks');
const types = require("./types"); const types = require('./types');
const ecc = require('tiny-secp256k1'); const ecc = require('tiny-secp256k1');
const randomBytes = require('randombytes'); const randomBytes = require('randombytes');
const typeforce = require('typeforce'); const typeforce = require('typeforce');
const wif = require('wif'); const wif = require('wif');
const isOptions = typeforce.maybe(typeforce.compile({ const isOptions = typeforce.maybe(
typeforce.compile({
compressed: types.maybe(types.Boolean), compressed: types.maybe(types.Boolean),
network: types.maybe(types.Network), network: types.maybe(types.Network),
})); }),
);
class ECPair { class ECPair {
constructor(__D, __Q, options) { constructor(__D, __Q, options) {
this.__D = __D; this.__D = __D;
this.__Q = __Q; this.__Q = __Q;
if (options === undefined) if (options === undefined) options = {};
options = {}; this.compressed =
this.compressed = options.compressed === undefined ? true : options.compressed;
options.compressed === undefined ? true : options.compressed; this.network = options.network || NETWORKS.bitcoin;
this.network = options.network || NETWORKS.bitcoin; if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed);
if (__Q !== undefined) }
this.__Q = ecc.pointCompress(__Q, this.compressed); get privateKey() {
} return this.__D;
get privateKey() { }
return this.__D; get publicKey() {
} if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed);
get publicKey() { return this.__Q;
if (!this.__Q) }
this.__Q = ecc.pointFromScalar(this.__D, this.compressed); toWIF() {
return this.__Q; if (!this.__D) throw new Error('Missing private key');
} return wif.encode(this.network.wif, this.__D, this.compressed);
toWIF() { }
if (!this.__D) sign(hash) {
throw new Error('Missing private key'); if (!this.__D) throw new Error('Missing private key');
return wif.encode(this.network.wif, this.__D, this.compressed); return ecc.sign(hash, this.__D);
} }
sign(hash) { verify(hash, signature) {
if (!this.__D) return ecc.verify(hash, this.publicKey, signature);
throw new Error('Missing private key'); }
return ecc.sign(hash, this.__D);
}
verify(hash, signature) {
return ecc.verify(hash, this.publicKey, signature);
}
} }
function fromPrivateKey(buffer, options) { function fromPrivateKey(buffer, options) {
typeforce(types.Buffer256bit, buffer); typeforce(types.Buffer256bit, buffer);
if (!ecc.isPrivate(buffer)) if (!ecc.isPrivate(buffer))
throw new TypeError('Private key not in range [1, n)'); throw new TypeError('Private key not in range [1, n)');
typeforce(isOptions, options); typeforce(isOptions, options);
return new ECPair(buffer, undefined, options); return new ECPair(buffer, undefined, options);
} }
exports.fromPrivateKey = fromPrivateKey; exports.fromPrivateKey = fromPrivateKey;
function fromPublicKey(buffer, options) { function fromPublicKey(buffer, options) {
typeforce(ecc.isPoint, buffer); typeforce(ecc.isPoint, buffer);
typeforce(isOptions, options); typeforce(isOptions, options);
return new ECPair(undefined, buffer, options); return new ECPair(undefined, buffer, options);
} }
exports.fromPublicKey = fromPublicKey; exports.fromPublicKey = fromPublicKey;
function fromWIF(wifString, network) { function fromWIF(wifString, network) {
const decoded = wif.decode(wifString); const decoded = wif.decode(wifString);
const version = decoded.version; const version = decoded.version;
// list of networks? // list of networks?
if (types.Array(network)) { if (types.Array(network)) {
network = network network = network
.filter((x) => { .filter(x => {
return version === x.wif; return version === x.wif;
}) })
.pop(); .pop();
if (!network) if (!network) throw new Error('Unknown network version');
throw new Error('Unknown network version'); // otherwise, assume a network object (or default to bitcoin)
// otherwise, assume a network object (or default to bitcoin) } else {
} network = network || NETWORKS.bitcoin;
else { if (version !== network.wif) throw new Error('Invalid network version');
network = network || NETWORKS.bitcoin; }
if (version !== network.wif) return fromPrivateKey(decoded.privateKey, {
throw new Error('Invalid network version'); compressed: decoded.compressed,
} network: network,
return fromPrivateKey(decoded.privateKey, { });
compressed: decoded.compressed,
network: network,
});
} }
exports.fromWIF = fromWIF; exports.fromWIF = fromWIF;
function makeRandom(options) { function makeRandom(options) {
typeforce(isOptions, options); typeforce(isOptions, options);
if (options === undefined) if (options === undefined) options = {};
options = {}; const rng = options.rng || randomBytes;
const rng = options.rng || randomBytes; let d;
let d; do {
do { d = rng(32);
d = rng(32); typeforce(types.Buffer256bit, d);
typeforce(types.Buffer256bit, d); } while (!ecc.isPrivate(d));
} while (!ecc.isPrivate(d)); return fromPrivateKey(d, options);
return fromPrivateKey(d, options);
} }
exports.makeRandom = makeRandom; exports.makeRandom = makeRandom;

26
src/index.js

@ -1,24 +1,24 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bip32 = require("bip32"); const bip32 = require('bip32');
exports.bip32 = bip32; exports.bip32 = bip32;
const address = require("./address"); const address = require('./address');
exports.address = address; exports.address = address;
const crypto = require("./crypto"); const crypto = require('./crypto');
exports.crypto = crypto; exports.crypto = crypto;
const ECPair = require("./ecpair"); const ECPair = require('./ecpair');
exports.ECPair = ECPair; exports.ECPair = ECPair;
const networks = require("./networks"); const networks = require('./networks');
exports.networks = networks; exports.networks = networks;
const payments = require("./payments"); const payments = require('./payments');
exports.payments = payments; exports.payments = payments;
const script = require("./script"); const script = require('./script');
exports.script = script; exports.script = script;
var block_1 = require("./block"); var block_1 = require('./block');
exports.Block = block_1.Block; exports.Block = block_1.Block;
var script_1 = require("./script"); var script_1 = require('./script');
exports.opcodes = script_1.OPS; exports.opcodes = script_1.OPS;
var transaction_1 = require("./transaction"); var transaction_1 = require('./transaction');
exports.Transaction = transaction_1.Transaction; exports.Transaction = transaction_1.Transaction;
var transaction_builder_1 = require("./transaction_builder"); var transaction_builder_1 = require('./transaction_builder');
exports.TransactionBuilder = transaction_builder_1.TransactionBuilder; exports.TransactionBuilder = transaction_builder_1.TransactionBuilder;

58
src/networks.js

@ -1,35 +1,35 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
exports.bitcoin = { exports.bitcoin = {
messagePrefix: '\x18Bitcoin Signed Message:\n', messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'bc', bech32: 'bc',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x00, pubKeyHash: 0x00,
scriptHash: 0x05, scriptHash: 0x05,
wif: 0x80, wif: 0x80,
}; };
exports.regtest = { exports.regtest = {
messagePrefix: '\x18Bitcoin Signed Message:\n', messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'bcrt', bech32: 'bcrt',
bip32: { bip32: {
public: 0x043587cf, public: 0x043587cf,
private: 0x04358394, private: 0x04358394,
}, },
pubKeyHash: 0x6f, pubKeyHash: 0x6f,
scriptHash: 0xc4, scriptHash: 0xc4,
wif: 0xef, wif: 0xef,
}; };
exports.testnet = { exports.testnet = {
messagePrefix: '\x18Bitcoin Signed Message:\n', messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'tb', bech32: 'tb',
bip32: { bip32: {
public: 0x043587cf, public: 0x043587cf,
private: 0x04358394, private: 0x04358394,
}, },
pubKeyHash: 0x6f, pubKeyHash: 0x6f,
scriptHash: 0xc4, scriptHash: 0xc4,
wif: 0xef, wif: 0xef,
}; };

82
src/payments/embed.js

@ -1,51 +1,49 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const networks_1 = require("../networks"); const networks_1 = require('../networks');
const bscript = require("../script"); const bscript = require('../script');
const lazy = require("./lazy"); const lazy = require('./lazy');
const typef = require('typeforce'); const typef = require('typeforce');
const OPS = bscript.OPS; const OPS = bscript.OPS;
function stacksEqual(a, b) { function stacksEqual(a, b) {
if (a.length !== b.length) if (a.length !== b.length) return false;
return false; return a.every((x, i) => {
return a.every((x, i) => { return x.equals(b[i]);
return x.equals(b[i]); });
});
} }
// output: OP_RETURN ... // output: OP_RETURN ...
function p2data(a, opts) { function p2data(a, opts) {
if (!a.data && !a.output) if (!a.data && !a.output) throw new TypeError('Not enough data');
throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {});
opts = Object.assign({ validate: true }, opts || {}); typef(
typef({ {
network: typef.maybe(typef.Object), network: typef.maybe(typef.Object),
output: typef.maybe(typef.Buffer), output: typef.maybe(typef.Buffer),
data: typef.maybe(typef.arrayOf(typef.Buffer)), data: typef.maybe(typef.arrayOf(typef.Buffer)),
}, a); },
const network = a.network || networks_1.bitcoin; a,
const o = { network }; );
lazy.prop(o, 'output', () => { const network = a.network || networks_1.bitcoin;
if (!a.data) const o = { network };
return; lazy.prop(o, 'output', () => {
return bscript.compile([OPS.OP_RETURN].concat(a.data)); if (!a.data) return;
}); return bscript.compile([OPS.OP_RETURN].concat(a.data));
lazy.prop(o, 'data', () => { });
if (!a.output) lazy.prop(o, 'data', () => {
return; if (!a.output) return;
return bscript.decompile(a.output).slice(1); return bscript.decompile(a.output).slice(1);
}); });
// extended validation // extended validation
if (opts.validate) { if (opts.validate) {
if (a.output) { if (a.output) {
const chunks = bscript.decompile(a.output); const chunks = bscript.decompile(a.output);
if (chunks[0] !== OPS.OP_RETURN) if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid');
throw new TypeError('Output is invalid'); if (!chunks.slice(1).every(typef.Buffer))
if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid');
throw new TypeError('Output is invalid'); if (a.data && !stacksEqual(a.data, o.data))
if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch');
throw new TypeError('Data mismatch');
}
} }
return Object.assign(o, a); }
return Object.assign(o, a);
} }
exports.p2data = p2data; exports.p2data = p2data;

18
src/payments/index.js

@ -1,18 +1,18 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const embed_1 = require("./embed"); const embed_1 = require('./embed');
exports.embed = embed_1.p2data; exports.embed = embed_1.p2data;
const p2ms_1 = require("./p2ms"); const p2ms_1 = require('./p2ms');
exports.p2ms = p2ms_1.p2ms; exports.p2ms = p2ms_1.p2ms;
const p2pk_1 = require("./p2pk"); const p2pk_1 = require('./p2pk');
exports.p2pk = p2pk_1.p2pk; exports.p2pk = p2pk_1.p2pk;
const p2pkh_1 = require("./p2pkh"); const p2pkh_1 = require('./p2pkh');
exports.p2pkh = p2pkh_1.p2pkh; exports.p2pkh = p2pkh_1.p2pkh;
const p2sh_1 = require("./p2sh"); const p2sh_1 = require('./p2sh');
exports.p2sh = p2sh_1.p2sh; exports.p2sh = p2sh_1.p2sh;
const p2wpkh_1 = require("./p2wpkh"); const p2wpkh_1 = require('./p2wpkh');
exports.p2wpkh = p2wpkh_1.p2wpkh; exports.p2wpkh = p2wpkh_1.p2wpkh;
const p2wsh_1 = require("./p2wsh"); const p2wsh_1 = require('./p2wsh');
exports.p2wsh = p2wsh_1.p2wsh; exports.p2wsh = p2wsh_1.p2wsh;
// TODO // TODO
// witness commitment // witness commitment

47
src/payments/lazy.js

@ -1,32 +1,31 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
function prop(object, name, f) { function prop(object, name, f) {
Object.defineProperty(object, name, { Object.defineProperty(object, name, {
configurable: true,
enumerable: true,
get() {
const _value = f.call(this);
this[name] = _value;
return _value;
},
set(_value) {
Object.defineProperty(this, name, {
configurable: true, configurable: true,
enumerable: true, enumerable: true,
get() { value: _value,
const _value = f.call(this); writable: true,
this[name] = _value; });
return _value; },
}, });
set(_value) {
Object.defineProperty(this, name, {
configurable: true,
enumerable: true,
value: _value,
writable: true,
});
},
});
} }
exports.prop = prop; exports.prop = prop;
function value(f) { function value(f) {
let _value; let _value;
return () => { return () => {
if (_value !== undefined) if (_value !== undefined) return _value;
return _value; _value = f();
_value = f(); return _value;
return _value; };
};
} }
exports.value = value; exports.value = value;

254
src/payments/p2ms.js

@ -1,141 +1,141 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const networks_1 = require("../networks"); const networks_1 = require('../networks');
const bscript = require("../script"); const bscript = require('../script');
const lazy = require("./lazy"); const lazy = require('./lazy');
const OPS = bscript.OPS; const OPS = bscript.OPS;
const typef = require('typeforce'); const typef = require('typeforce');
const ecc = require('tiny-secp256k1'); const ecc = require('tiny-secp256k1');
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
function stacksEqual(a, b) { function stacksEqual(a, b) {
if (a.length !== b.length) if (a.length !== b.length) return false;
return false; return a.every((x, i) => {
return a.every((x, i) => { return x.equals(b[i]);
return x.equals(b[i]); });
});
} }
// input: OP_0 [signatures ...] // input: OP_0 [signatures ...]
// output: m [pubKeys ...] n OP_CHECKMULTISIG // output: m [pubKeys ...] n OP_CHECKMULTISIG
function p2ms(a, opts) { function p2ms(a, opts) {
if (!a.input && if (
!a.output && !a.input &&
!(a.pubkeys && a.m !== undefined) && !a.output &&
!a.signatures) !(a.pubkeys && a.m !== undefined) &&
throw new TypeError('Not enough data'); !a.signatures
opts = Object.assign({ validate: true }, opts || {}); )
function isAcceptableSignature(x) { throw new TypeError('Not enough data');
return (bscript.isCanonicalScriptSignature(x) || opts = Object.assign({ validate: true }, opts || {});
(opts.allowIncomplete && x === OPS.OP_0) !== undefined); function isAcceptableSignature(x) {
return (
bscript.isCanonicalScriptSignature(x) ||
(opts.allowIncomplete && x === 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 || 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);
}
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');
} }
typef({ if (a.pubkeys) {
network: typef.maybe(typef.Object), if (a.n !== undefined && a.n !== a.pubkeys.length)
m: typef.maybe(typef.Number), throw new TypeError('Pubkey count mismatch');
n: typef.maybe(typef.Number), o.n = a.pubkeys.length;
output: typef.maybe(typef.Buffer), if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m');
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);
} }
lazy.prop(o, 'output', () => { if (a.signatures) {
if (!a.m) if (a.signatures.length < o.m)
return; throw new TypeError('Not enough signatures provided');
if (!o.n) if (a.signatures.length > o.m)
return; throw new TypeError('Too many signatures provided');
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');
}
} }
return Object.assign(o, a); 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);
} }
exports.p2ms = p2ms; exports.p2ms = p2ms;

129
src/payments/p2pk.js

@ -1,75 +1,72 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const networks_1 = require("../networks"); const networks_1 = require('../networks');
const bscript = require("../script"); const bscript = require('../script');
const lazy = require("./lazy"); const lazy = require('./lazy');
const typef = require('typeforce'); const typef = require('typeforce');
const OPS = bscript.OPS; const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1'); const ecc = require('tiny-secp256k1');
// input: {signature} // input: {signature}
// output: {pubKey} OP_CHECKSIG // output: {pubKey} OP_CHECKSIG
function p2pk(a, opts) { function p2pk(a, opts) {
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
throw new TypeError('Not enough data'); throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {}); opts = Object.assign({ validate: true }, opts || {});
typef({ typef(
network: typef.maybe(typef.Object), {
output: typef.maybe(typef.Buffer), network: typef.maybe(typef.Object),
pubkey: typef.maybe(ecc.isPoint), output: typef.maybe(typef.Buffer),
signature: typef.maybe(bscript.isCanonicalScriptSignature), pubkey: typef.maybe(ecc.isPoint),
input: typef.maybe(typef.Buffer), signature: typef.maybe(bscript.isCanonicalScriptSignature),
}, a); input: typef.maybe(typef.Buffer),
const _chunks = lazy.value(() => { },
return bscript.decompile(a.input); a,
}); );
const network = a.network || networks_1.bitcoin; const _chunks = lazy.value(() => {
const o = { network }; return bscript.decompile(a.input);
lazy.prop(o, 'output', () => { });
if (!a.pubkey) const network = a.network || networks_1.bitcoin;
return; const o = { network };
return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); lazy.prop(o, 'output', () => {
}); if (!a.pubkey) return;
lazy.prop(o, 'pubkey', () => { return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]);
if (!a.output) });
return; lazy.prop(o, 'pubkey', () => {
return a.output.slice(1, -1); if (!a.output) return;
}); return a.output.slice(1, -1);
lazy.prop(o, 'signature', () => { });
if (!a.input) lazy.prop(o, 'signature', () => {
return; if (!a.input) return;
return _chunks()[0]; return _chunks()[0];
}); });
lazy.prop(o, 'input', () => { lazy.prop(o, 'input', () => {
if (!a.signature) if (!a.signature) return;
return; return bscript.compile([a.signature]);
return bscript.compile([a.signature]); });
}); lazy.prop(o, 'witness', () => {
lazy.prop(o, 'witness', () => { if (!o.input) return;
if (!o.input) return [];
return; });
return []; // extended validation
}); if (opts.validate) {
// extended validation if (a.output) {
if (opts.validate) { if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
if (a.output) { throw new TypeError('Output is invalid');
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) if (!ecc.isPoint(o.pubkey))
throw new TypeError('Output is invalid'); throw new TypeError('Output pubkey is invalid');
if (!ecc.isPoint(o.pubkey)) if (a.pubkey && !a.pubkey.equals(o.pubkey))
throw new TypeError('Output pubkey is invalid'); throw new TypeError('Pubkey mismatch');
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); 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);
} }
exports.p2pk = p2pk; exports.p2pk = p2pk;

254
src/payments/p2pkh.js

@ -1,9 +1,9 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bcrypto = require("../crypto"); const bcrypto = require('../crypto');
const networks_1 = require("../networks"); const networks_1 = require('../networks');
const bscript = require("../script"); const bscript = require('../script');
const lazy = require("./lazy"); const lazy = require('./lazy');
const typef = require('typeforce'); const typef = require('typeforce');
const OPS = bscript.OPS; const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1'); const ecc = require('tiny-secp256k1');
@ -11,132 +11,122 @@ const bs58check = require('bs58check');
// input: {signature} {pubkey} // input: {signature} {pubkey}
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
function p2pkh(a, opts) { function p2pkh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
throw new TypeError('Not enough data'); throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {}); opts = Object.assign({ validate: true }, opts || {});
typef({ typef(
network: typef.maybe(typef.Object), {
address: typef.maybe(typef.String), network: typef.maybe(typef.Object),
hash: typef.maybe(typef.BufferN(20)), address: typef.maybe(typef.String),
output: typef.maybe(typef.BufferN(25)), hash: typef.maybe(typef.BufferN(20)),
pubkey: typef.maybe(ecc.isPoint), output: typef.maybe(typef.BufferN(25)),
signature: typef.maybe(bscript.isCanonicalScriptSignature), pubkey: typef.maybe(ecc.isPoint),
input: typef.maybe(typef.Buffer), signature: typef.maybe(bscript.isCanonicalScriptSignature),
}, a); input: typef.maybe(typef.Buffer),
const _address = lazy.value(() => { },
const payload = bs58check.decode(a.address); a,
const version = payload.readUInt8(0); );
const hash = payload.slice(1); const _address = lazy.value(() => {
return { version, hash }; const payload = bs58check.decode(a.address);
}); const version = payload.readUInt8(0);
const _chunks = lazy.value(() => { const hash = payload.slice(1);
return bscript.decompile(a.input); return { version, hash };
}); });
const network = a.network || networks_1.bitcoin; const _chunks = lazy.value(() => {
const o = { network }; return bscript.decompile(a.input);
lazy.prop(o, 'address', () => { });
if (!o.hash) const network = a.network || networks_1.bitcoin;
return; const o = { network };
const payload = Buffer.allocUnsafe(21); lazy.prop(o, 'address', () => {
payload.writeUInt8(network.pubKeyHash, 0); if (!o.hash) return;
o.hash.copy(payload, 1); const payload = Buffer.allocUnsafe(21);
return bs58check.encode(payload); payload.writeUInt8(network.pubKeyHash, 0);
}); o.hash.copy(payload, 1);
lazy.prop(o, 'hash', () => { return bs58check.encode(payload);
if (a.output) });
return a.output.slice(3, 23); lazy.prop(o, 'hash', () => {
if (a.address) if (a.output) return a.output.slice(3, 23);
return _address().hash; if (a.address) return _address().hash;
if (a.pubkey || o.pubkey) if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey);
return bcrypto.hash160(a.pubkey || o.pubkey); });
}); lazy.prop(o, 'output', () => {
lazy.prop(o, 'output', () => { if (!o.hash) return;
if (!o.hash) return bscript.compile([
return; OPS.OP_DUP,
return bscript.compile([ OPS.OP_HASH160,
OPS.OP_DUP, o.hash,
OPS.OP_HASH160, OPS.OP_EQUALVERIFY,
o.hash, OPS.OP_CHECKSIG,
OPS.OP_EQUALVERIFY, ]);
OPS.OP_CHECKSIG, });
]); lazy.prop(o, 'pubkey', () => {
}); if (!a.input) return;
lazy.prop(o, 'pubkey', () => { return _chunks()[1];
if (!a.input) });
return; lazy.prop(o, 'signature', () => {
return _chunks()[1]; if (!a.input) return;
}); return _chunks()[0];
lazy.prop(o, 'signature', () => { });
if (!a.input) lazy.prop(o, 'input', () => {
return; if (!a.pubkey) return;
return _chunks()[0]; if (!a.signature) return;
}); return bscript.compile([a.signature, a.pubkey]);
lazy.prop(o, 'input', () => { });
if (!a.pubkey) lazy.prop(o, 'witness', () => {
return; if (!o.input) return;
if (!a.signature) return [];
return; });
return bscript.compile([a.signature, a.pubkey]); // extended validation
}); if (opts.validate) {
lazy.prop(o, 'witness', () => { let hash = Buffer.from([]);
if (!o.input) if (a.address) {
return; if (_address().version !== network.pubKeyHash)
return []; throw new TypeError('Invalid version or Network mismatch');
}); if (_address().hash.length !== 20) throw new TypeError('Invalid address');
// extended validation hash = _address().hash;
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');
}
} }
return Object.assign(o, a); 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');
}
}
return Object.assign(o, a);
} }
exports.p2pkh = p2pkh; exports.p2pkh = p2pkh;

329
src/payments/p2sh.js

@ -1,185 +1,178 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bcrypto = require("../crypto"); const bcrypto = require('../crypto');
const networks_1 = require("../networks"); const networks_1 = require('../networks');
const bscript = require("../script"); const bscript = require('../script');
const lazy = require("./lazy"); const lazy = require('./lazy');
const typef = require('typeforce'); const typef = require('typeforce');
const OPS = bscript.OPS; const OPS = bscript.OPS;
const bs58check = require('bs58check'); const bs58check = require('bs58check');
function stacksEqual(a, b) { function stacksEqual(a, b) {
if (a.length !== b.length) if (a.length !== b.length) return false;
return false; return a.every((x, i) => {
return a.every((x, i) => { return x.equals(b[i]);
return x.equals(b[i]); });
});
} }
// input: [redeemScriptSig ...] {redeemScript} // input: [redeemScriptSig ...] {redeemScript}
// witness: <?> // witness: <?>
// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL // output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL
function p2sh(a, opts) { function p2sh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) if (!a.address && !a.hash && !a.output && !a.redeem && !a.input)
throw new TypeError('Not enough data'); throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {}); opts = Object.assign({ validate: true }, opts || {});
typef({ 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), network: typef.maybe(typef.Object),
address: typef.maybe(typef.String), output: typef.maybe(typef.Buffer),
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), input: typef.maybe(typef.Buffer),
witness: typef.maybe(typef.arrayOf(typef.Buffer)), witness: typef.maybe(typef.arrayOf(typef.Buffer)),
}, a); }),
let network = a.network; input: typef.maybe(typef.Buffer),
if (!network) { witness: typef.maybe(typef.arrayOf(typef.Buffer)),
network = (a.redeem && a.redeem.network) || networks_1.bitcoin; },
a,
);
let network = a.network;
if (!network) {
network = (a.redeem && a.redeem.network) || networks_1.bitcoin;
}
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;
} }
const o = { network }; if (a.hash) {
const _address = lazy.value(() => { if (hash.length > 0 && !hash.equals(a.hash))
const payload = bs58check.decode(a.address); throw new TypeError('Hash mismatch');
const version = payload.readUInt8(0); else hash = a.hash;
const hash = payload.slice(1); }
return { version, hash }; if (a.output) {
}); if (
const _chunks = lazy.value(() => { a.output.length !== 23 ||
return bscript.decompile(a.input); a.output[0] !== OPS.OP_HASH160 ||
}); a.output[1] !== 0x14 ||
const _redeem = lazy.value(() => { a.output[22] !== OPS.OP_EQUAL
const chunks = _chunks(); )
return { throw new TypeError('Output is invalid');
network, const hash2 = a.output.slice(2, 22);
output: chunks[chunks.length - 1], if (hash.length > 0 && !hash.equals(hash2))
input: bscript.compile(chunks.slice(0, -1)), throw new TypeError('Hash mismatch');
witness: a.witness || [], else hash = hash2;
}; }
}); // inlined to prevent 'no-inner-declarations' failing
// output dependents const checkRedeem = redeem => {
lazy.prop(o, 'address', () => { // is the redeem output empty/invalid?
if (!o.hash) if (redeem.output) {
return; const decompile = bscript.decompile(redeem.output);
const payload = Buffer.allocUnsafe(21); if (!decompile || decompile.length < 1)
payload.writeUInt8(o.network.scriptHash, 0); throw new TypeError('Redeem.output too short');
o.hash.copy(payload, 1); // match hash against other sources
return bs58check.encode(payload); const hash2 = bcrypto.hash160(redeem.output);
}); if (hash.length > 0 && !hash.equals(hash2))
lazy.prop(o, 'hash', () => { throw new TypeError('Hash mismatch');
// in order of least effort else hash = hash2;
if (a.output) }
return a.output.slice(2, 22); if (redeem.input) {
if (a.address) const hasInput = redeem.input.length > 0;
return _address().hash; const hasWitness = redeem.witness && redeem.witness.length > 0;
if (o.redeem && o.redeem.output) if (!hasInput && !hasWitness) throw new TypeError('Empty input');
return bcrypto.hash160(o.redeem.output); if (hasInput && hasWitness)
}); throw new TypeError('Input and witness provided');
lazy.prop(o, 'output', () => { if (hasInput) {
if (!o.hash) const richunks = bscript.decompile(redeem.input);
return; if (!bscript.isPushOnly(richunks))
return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); throw new TypeError('Non push-only scriptSig');
});
// 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);
} }
exports.p2sh = p2sh; exports.p2sh = p2sh;

242
src/payments/p2wpkh.js

@ -1,9 +1,9 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bcrypto = require("../crypto"); const bcrypto = require('../crypto');
const networks_1 = require("../networks"); const networks_1 = require('../networks');
const bscript = require("../script"); const bscript = require('../script');
const lazy = require("./lazy"); const lazy = require('./lazy');
const typef = require('typeforce'); const typef = require('typeforce');
const OPS = bscript.OPS; const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1'); const ecc = require('tiny-secp256k1');
@ -13,126 +13,116 @@ const EMPTY_BUFFER = Buffer.alloc(0);
// input: <> // input: <>
// output: OP_0 {pubKeyHash} // output: OP_0 {pubKeyHash}
function p2wpkh(a, opts) { function p2wpkh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
throw new TypeError('Not enough data'); throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {}); opts = Object.assign({ validate: true }, opts || {});
typef({ typef(
address: typef.maybe(typef.String), {
hash: typef.maybe(typef.BufferN(20)), address: typef.maybe(typef.String),
input: typef.maybe(typef.BufferN(0)), hash: typef.maybe(typef.BufferN(20)),
network: typef.maybe(typef.Object), input: typef.maybe(typef.BufferN(0)),
output: typef.maybe(typef.BufferN(22)), network: typef.maybe(typef.Object),
pubkey: typef.maybe(ecc.isPoint), output: typef.maybe(typef.BufferN(22)),
signature: typef.maybe(bscript.isCanonicalScriptSignature), pubkey: typef.maybe(ecc.isPoint),
witness: typef.maybe(typef.arrayOf(typef.Buffer)), signature: typef.maybe(bscript.isCanonicalScriptSignature),
}, a); witness: typef.maybe(typef.arrayOf(typef.Buffer)),
const _address = lazy.value(() => { },
const result = bech32.decode(a.address); a,
const version = result.words.shift(); );
const data = bech32.fromWords(result.words); const _address = lazy.value(() => {
return { const result = bech32.decode(a.address);
version, const version = result.words.shift();
prefix: result.prefix, const data = bech32.fromWords(result.words);
data: Buffer.from(data), return {
}; version,
}); prefix: result.prefix,
const network = a.network || networks_1.bitcoin; data: Buffer.from(data),
const o = { network }; };
lazy.prop(o, 'address', () => { });
if (!o.hash) const network = a.network || networks_1.bitcoin;
return; const o = { network };
const words = bech32.toWords(o.hash); lazy.prop(o, 'address', () => {
words.unshift(0x00); if (!o.hash) return;
return bech32.encode(network.bech32, words); const words = bech32.toWords(o.hash);
}); words.unshift(0x00);
lazy.prop(o, 'hash', () => { return bech32.encode(network.bech32, words);
if (a.output) });
return a.output.slice(2, 22); lazy.prop(o, 'hash', () => {
if (a.address) if (a.output) return a.output.slice(2, 22);
return _address().data; if (a.address) return _address().data;
if (a.pubkey || o.pubkey) if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey);
return bcrypto.hash160(a.pubkey || o.pubkey); });
}); lazy.prop(o, 'output', () => {
lazy.prop(o, 'output', () => { if (!o.hash) return;
if (!o.hash) return bscript.compile([OPS.OP_0, o.hash]);
return; });
return bscript.compile([OPS.OP_0, o.hash]); lazy.prop(o, 'pubkey', () => {
}); if (a.pubkey) return a.pubkey;
lazy.prop(o, 'pubkey', () => { if (!a.witness) return;
if (a.pubkey) return a.witness[1];
return a.pubkey; });
if (!a.witness) lazy.prop(o, 'signature', () => {
return; if (!a.witness) return;
return a.witness[1]; return a.witness[0];
}); });
lazy.prop(o, 'signature', () => { lazy.prop(o, 'input', () => {
if (!a.witness) if (!o.witness) return;
return; return EMPTY_BUFFER;
return a.witness[0]; });
}); lazy.prop(o, 'witness', () => {
lazy.prop(o, 'input', () => { if (!a.pubkey) return;
if (!o.witness) if (!a.signature) return;
return; return [a.signature, a.pubkey];
return EMPTY_BUFFER; });
}); // extended validation
lazy.prop(o, 'witness', () => { if (opts.validate) {
if (!a.pubkey) let hash = Buffer.from([]);
return; if (a.address) {
if (!a.signature) if (network && network.bech32 !== _address().prefix)
return; throw new TypeError('Invalid prefix or Network mismatch');
return [a.signature, a.pubkey]; if (_address().version !== 0x00)
}); throw new TypeError('Invalid address version');
// extended validation if (_address().data.length !== 20)
if (opts.validate) { throw new TypeError('Invalid address data');
let hash = Buffer.from([]); hash = _address().data;
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); 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);
} }
exports.p2wpkh = p2wpkh; exports.p2wpkh = p2wpkh;

321
src/payments/p2wsh.js

@ -1,177 +1,176 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bcrypto = require("../crypto"); const bcrypto = require('../crypto');
const networks_1 = require("../networks"); const networks_1 = require('../networks');
const bscript = require("../script"); const bscript = require('../script');
const lazy = require("./lazy"); const lazy = require('./lazy');
const typef = require('typeforce'); const typef = require('typeforce');
const OPS = bscript.OPS; const OPS = bscript.OPS;
const bech32 = require('bech32'); const bech32 = require('bech32');
const EMPTY_BUFFER = Buffer.alloc(0); const EMPTY_BUFFER = Buffer.alloc(0);
function stacksEqual(a, b) { function stacksEqual(a, b) {
if (a.length !== b.length) if (a.length !== b.length) return false;
return false; return a.every((x, i) => {
return a.every((x, i) => { return x.equals(b[i]);
return x.equals(b[i]); });
});
} }
// input: <> // input: <>
// witness: [redeemScriptSig ...] {redeemScript} // witness: [redeemScriptSig ...] {redeemScript}
// output: OP_0 {sha256(redeemScript)} // output: OP_0 {sha256(redeemScript)}
function p2wsh(a, opts) { function p2wsh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
throw new TypeError('Not enough data'); throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {}); opts = Object.assign({ validate: true }, opts || {});
typef({ 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), network: typef.maybe(typef.Object),
address: typef.maybe(typef.String), output: typef.maybe(typef.Buffer),
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)), witness: typef.maybe(typef.arrayOf(typef.Buffer)),
}, a); }),
const _address = lazy.value(() => { input: typef.maybe(typef.BufferN(0)),
const result = bech32.decode(a.address); witness: typef.maybe(typef.arrayOf(typef.Buffer)),
const version = result.words.shift(); },
const data = bech32.fromWords(result.words); a,
return { );
version, const _address = lazy.value(() => {
prefix: result.prefix, const result = bech32.decode(a.address);
data: Buffer.from(data), const version = result.words.shift();
}; const data = bech32.fromWords(result.words);
}); return {
const _rchunks = lazy.value(() => { version,
return bscript.decompile(a.redeem.input); prefix: result.prefix,
}); data: Buffer.from(data),
let network = a.network; };
if (!network) { });
network = (a.redeem && a.redeem.network) || networks_1.bitcoin; 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 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);
} }
const o = { network }; if (!a.redeem) return;
lazy.prop(o, 'address', () => { if (!a.redeem.output) return;
if (!o.hash) if (!a.redeem.witness) return;
return; return [].concat(a.redeem.witness, a.redeem.output);
const words = bech32.toWords(o.hash); });
words.unshift(0x00); // extended validation
return bech32.encode(network.bech32, words); if (opts.validate) {
}); let hash = Buffer.from([]);
lazy.prop(o, 'hash', () => { if (a.address) {
if (a.output) if (_address().prefix !== network.bech32)
return a.output.slice(2); throw new TypeError('Invalid prefix or Network mismatch');
if (a.address) if (_address().version !== 0x00)
return _address().data; throw new TypeError('Invalid address version');
if (o.redeem && o.redeem.output) if (_address().data.length !== 32)
return bcrypto.sha256(o.redeem.output); throw new TypeError('Invalid address data');
}); hash = _address().data;
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');
}
} }
return Object.assign(o, a); 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);
} }
exports.p2wsh = p2wsh; exports.p2wsh = p2wsh;

264
src/script.js

@ -1,8 +1,8 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const scriptNumber = require("./script_number"); const scriptNumber = require('./script_number');
const scriptSignature = require("./script_signature"); const scriptSignature = require('./script_signature');
const types = require("./types"); const types = require('./types');
const bip66 = require('bip66'); const bip66 = require('bip66');
const ecc = require('tiny-secp256k1'); const ecc = require('tiny-secp256k1');
const pushdata = require('pushdata-bitcoin'); const pushdata = require('pushdata-bitcoin');
@ -11,179 +11,165 @@ exports.OPS = require('bitcoin-ops');
const REVERSE_OPS = require('bitcoin-ops/map'); const REVERSE_OPS = require('bitcoin-ops/map');
const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1 const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1
function isOPInt(value) { function isOPInt(value) {
return (types.Number(value) && return (
(value === exports.OPS.OP_0 || types.Number(value) &&
(value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || (value === exports.OPS.OP_0 ||
value === exports.OPS.OP_1NEGATE)); (value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) ||
value === exports.OPS.OP_1NEGATE)
);
} }
function isPushOnlyChunk(value) { function isPushOnlyChunk(value) {
return types.Buffer(value) || isOPInt(value); return types.Buffer(value) || isOPInt(value);
} }
function isPushOnly(value) { function isPushOnly(value) {
return types.Array(value) && value.every(isPushOnlyChunk); return types.Array(value) && value.every(isPushOnlyChunk);
} }
exports.isPushOnly = isPushOnly; exports.isPushOnly = isPushOnly;
function asMinimalOP(buffer) { function asMinimalOP(buffer) {
if (buffer.length === 0) if (buffer.length === 0) return exports.OPS.OP_0;
return exports.OPS.OP_0; if (buffer.length !== 1) return;
if (buffer.length !== 1) if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
return; if (buffer[0] === 0x81) return exports.OPS.OP_1NEGATE;
if (buffer[0] >= 1 && buffer[0] <= 16)
return OP_INT_BASE + buffer[0];
if (buffer[0] === 0x81)
return exports.OPS.OP_1NEGATE;
} }
function chunksIsBuffer(buf) { function chunksIsBuffer(buf) {
return Buffer.isBuffer(buf); return Buffer.isBuffer(buf);
} }
function chunksIsArray(buf) { function chunksIsArray(buf) {
return types.Array(buf); return types.Array(buf);
} }
function singleChunkIsBuffer(buf) { function singleChunkIsBuffer(buf) {
return Buffer.isBuffer(buf); return Buffer.isBuffer(buf);
} }
function compile(chunks) { function compile(chunks) {
// TODO: remove me // TODO: remove me
if (chunksIsBuffer(chunks)) if (chunksIsBuffer(chunks)) return chunks;
return chunks; typeforce(types.Array, chunks);
typeforce(types.Array, chunks); const bufferSize = chunks.reduce((accum, chunk) => {
const bufferSize = chunks.reduce((accum, chunk) => { // data chunk
// data chunk if (singleChunkIsBuffer(chunk)) {
if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy
// adhere to BIP62.3, minimal push policy if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) {
if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) {
return accum + 1;
}
return accum + pushdata.encodingLength(chunk.length) + chunk.length;
}
// opcode
return accum + 1; return accum + 1;
}, 0.0); }
const buffer = Buffer.allocUnsafe(bufferSize); return accum + pushdata.encodingLength(chunk.length) + chunk.length;
let offset = 0; }
chunks.forEach(chunk => { // opcode
// data chunk return accum + 1;
if (singleChunkIsBuffer(chunk)) { }, 0.0);
// adhere to BIP62.3, minimal push policy const buffer = Buffer.allocUnsafe(bufferSize);
const opcode = asMinimalOP(chunk); let offset = 0;
if (opcode !== undefined) { chunks.forEach(chunk => {
buffer.writeUInt8(opcode, offset); // data chunk
offset += 1; if (singleChunkIsBuffer(chunk)) {
return; // adhere to BIP62.3, minimal push policy
} const opcode = asMinimalOP(chunk);
offset += pushdata.encode(buffer, chunk.length, offset); if (opcode !== undefined) {
chunk.copy(buffer, offset); buffer.writeUInt8(opcode, offset);
offset += chunk.length; offset += 1;
// opcode return;
} }
else { offset += pushdata.encode(buffer, chunk.length, offset);
buffer.writeUInt8(chunk, offset); chunk.copy(buffer, offset);
offset += 1; offset += chunk.length;
} // opcode
}); } else {
if (offset !== buffer.length) buffer.writeUInt8(chunk, offset);
throw new Error('Could not decode chunks'); offset += 1;
return buffer; }
});
if (offset !== buffer.length) throw new Error('Could not decode chunks');
return buffer;
} }
exports.compile = compile; exports.compile = compile;
function decompile(buffer) { function decompile(buffer) {
// TODO: remove me // TODO: remove me
if (chunksIsArray(buffer)) if (chunksIsArray(buffer)) return buffer;
return buffer; typeforce(types.Buffer, buffer);
typeforce(types.Buffer, buffer); const chunks = [];
const chunks = []; let i = 0;
let i = 0; while (i < buffer.length) {
while (i < buffer.length) { const opcode = buffer[i];
const opcode = buffer[i]; // data chunk
// data chunk if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) {
if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) { const d = pushdata.decode(buffer, i);
const d = pushdata.decode(buffer, i); // did reading a pushDataInt fail?
// did reading a pushDataInt fail? if (d === null) return null;
if (d === null) i += d.size;
return null; // attempt to read too much data?
i += d.size; if (i + d.number > buffer.length) return null;
// attempt to read too much data? const data = buffer.slice(i, i + d.number);
if (i + d.number > buffer.length) i += d.number;
return null; // decompile minimally
const data = buffer.slice(i, i + d.number); const op = asMinimalOP(data);
i += d.number; if (op !== undefined) {
// decompile minimally chunks.push(op);
const op = asMinimalOP(data); } else {
if (op !== undefined) { chunks.push(data);
chunks.push(op); }
} // opcode
else { } else {
chunks.push(data); chunks.push(opcode);
} i += 1;
// opcode
}
else {
chunks.push(opcode);
i += 1;
}
} }
return chunks; }
return chunks;
} }
exports.decompile = decompile; exports.decompile = decompile;
function toASM(chunks) { function toASM(chunks) {
if (chunksIsBuffer(chunks)) { if (chunksIsBuffer(chunks)) {
chunks = decompile(chunks); chunks = decompile(chunks);
} }
return chunks return chunks
.map(chunk => { .map(chunk => {
// data? // data?
if (singleChunkIsBuffer(chunk)) { if (singleChunkIsBuffer(chunk)) {
const op = asMinimalOP(chunk); const op = asMinimalOP(chunk);
if (op === undefined) if (op === undefined) return chunk.toString('hex');
return chunk.toString('hex'); chunk = op;
chunk = op; }
} // opcode!
// opcode! return REVERSE_OPS[chunk];
return REVERSE_OPS[chunk];
}) })
.join(' '); .join(' ');
} }
exports.toASM = toASM; exports.toASM = toASM;
function fromASM(asm) { function fromASM(asm) {
typeforce(types.String, asm); typeforce(types.String, asm);
return compile(asm.split(' ').map(chunkStr => { return compile(
// opcode? asm.split(' ').map(chunkStr => {
if (exports.OPS[chunkStr] !== undefined) // opcode?
return exports.OPS[chunkStr]; if (exports.OPS[chunkStr] !== undefined) return exports.OPS[chunkStr];
typeforce(types.Hex, chunkStr); typeforce(types.Hex, chunkStr);
// data! // data!
return Buffer.from(chunkStr, 'hex'); return Buffer.from(chunkStr, 'hex');
})); }),
);
} }
exports.fromASM = fromASM; exports.fromASM = fromASM;
function toStack(chunks) { function toStack(chunks) {
chunks = decompile(chunks); chunks = decompile(chunks);
typeforce(isPushOnly, chunks); typeforce(isPushOnly, chunks);
return chunks.map(op => { return chunks.map(op => {
if (singleChunkIsBuffer(op)) if (singleChunkIsBuffer(op)) return op;
return op; if (op === exports.OPS.OP_0) return Buffer.allocUnsafe(0);
if (op === exports.OPS.OP_0) return scriptNumber.encode(op - OP_INT_BASE);
return Buffer.allocUnsafe(0); });
return scriptNumber.encode(op - OP_INT_BASE);
});
} }
exports.toStack = toStack; exports.toStack = toStack;
function isCanonicalPubKey(buffer) { function isCanonicalPubKey(buffer) {
return ecc.isPoint(buffer); return ecc.isPoint(buffer);
} }
exports.isCanonicalPubKey = isCanonicalPubKey; exports.isCanonicalPubKey = isCanonicalPubKey;
function isDefinedHashType(hashType) { function isDefinedHashType(hashType) {
const hashTypeMod = hashType & ~0x80; const hashTypeMod = hashType & ~0x80;
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
return hashTypeMod > 0x00 && hashTypeMod < 0x04; return hashTypeMod > 0x00 && hashTypeMod < 0x04;
} }
exports.isDefinedHashType = isDefinedHashType; exports.isDefinedHashType = isDefinedHashType;
function isCanonicalScriptSignature(buffer) { function isCanonicalScriptSignature(buffer) {
if (!Buffer.isBuffer(buffer)) if (!Buffer.isBuffer(buffer)) return false;
return false; if (!isDefinedHashType(buffer[buffer.length - 1])) return false;
if (!isDefinedHashType(buffer[buffer.length - 1])) return bip66.check(buffer.slice(0, -1));
return false;
return bip66.check(buffer.slice(0, -1));
} }
exports.isCanonicalScriptSignature = isCanonicalScriptSignature; exports.isCanonicalScriptSignature = isCanonicalScriptSignature;
// tslint:disable-next-line variable-name // tslint:disable-next-line variable-name

108
src/script_number.js

@ -1,65 +1,61 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
function decode(buffer, maxLength, minimal) { function decode(buffer, maxLength, minimal) {
maxLength = maxLength || 4; maxLength = maxLength || 4;
minimal = minimal === undefined ? true : minimal; minimal = minimal === undefined ? true : minimal;
const length = buffer.length; const length = buffer.length;
if (length === 0) if (length === 0) return 0;
return 0; if (length > maxLength) throw new TypeError('Script number overflow');
if (length > maxLength) if (minimal) {
throw new TypeError('Script number overflow'); if ((buffer[length - 1] & 0x7f) === 0) {
if (minimal) { if (length <= 1 || (buffer[length - 2] & 0x80) === 0)
if ((buffer[length - 1] & 0x7f) === 0) { throw new Error('Non-minimally encoded script number');
if (length <= 1 || (buffer[length - 2] & 0x80) === 0)
throw new Error('Non-minimally encoded script number');
}
} }
// 40-bit }
if (length === 5) { // 40-bit
const a = buffer.readUInt32LE(0); if (length === 5) {
const b = buffer.readUInt8(4); const a = buffer.readUInt32LE(0);
if (b & 0x80) const b = buffer.readUInt8(4);
return -((b & ~0x80) * 0x100000000 + a); if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a);
return b * 0x100000000 + a; return b * 0x100000000 + a;
} }
// 32-bit / 24-bit / 16-bit / 8-bit // 32-bit / 24-bit / 16-bit / 8-bit
let result = 0; let result = 0;
for (let i = 0; i < length; ++i) { for (let i = 0; i < length; ++i) {
result |= buffer[i] << (8 * i); result |= buffer[i] << (8 * i);
} }
if (buffer[length - 1] & 0x80) if (buffer[length - 1] & 0x80)
return -(result & ~(0x80 << (8 * (length - 1)))); return -(result & ~(0x80 << (8 * (length - 1))));
return result; return result;
} }
exports.decode = decode; exports.decode = decode;
function scriptNumSize(i) { function scriptNumSize(i) {
return i > 0x7fffffff return i > 0x7fffffff
? 5 ? 5
: i > 0x7fffff : i > 0x7fffff
? 4 ? 4
: i > 0x7fff : i > 0x7fff
? 3 ? 3
: i > 0x7f : i > 0x7f
? 2 ? 2
: i > 0x00 : i > 0x00
? 1 ? 1
: 0; : 0;
} }
function encode(_number) { function encode(_number) {
let value = Math.abs(_number); let value = Math.abs(_number);
const size = scriptNumSize(value); const size = scriptNumSize(value);
const buffer = Buffer.allocUnsafe(size); const buffer = Buffer.allocUnsafe(size);
const negative = _number < 0; const negative = _number < 0;
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
buffer.writeUInt8(value & 0xff, i); buffer.writeUInt8(value & 0xff, i);
value >>= 8; value >>= 8;
} }
if (buffer[size - 1] & 0x80) { if (buffer[size - 1] & 0x80) {
buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1);
} } else if (negative) {
else if (negative) { buffer[size - 1] |= 0x80;
buffer[size - 1] |= 0x80; }
} return buffer;
return buffer;
} }
exports.encode = encode; exports.encode = encode;

77
src/script_signature.js

@ -1,53 +1,52 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const types = require("./types"); const types = require('./types');
const bip66 = require('bip66'); const bip66 = require('bip66');
const typeforce = require('typeforce'); const typeforce = require('typeforce');
const ZERO = Buffer.alloc(1, 0); const ZERO = Buffer.alloc(1, 0);
function toDER(x) { function toDER(x) {
let i = 0; let i = 0;
while (x[i] === 0) while (x[i] === 0) ++i;
++i; if (i === x.length) return ZERO;
if (i === x.length) x = x.slice(i);
return ZERO; if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length);
x = x.slice(i); return x;
if (x[0] & 0x80)
return Buffer.concat([ZERO, x], 1 + x.length);
return x;
} }
function fromDER(x) { function fromDER(x) {
if (x[0] === 0x00) if (x[0] === 0x00) x = x.slice(1);
x = x.slice(1); const buffer = Buffer.alloc(32, 0);
const buffer = Buffer.alloc(32, 0); const bstart = Math.max(0, 32 - x.length);
const bstart = Math.max(0, 32 - x.length); x.copy(buffer, bstart);
x.copy(buffer, bstart); return buffer;
return buffer;
} }
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
function decode(buffer) { function decode(buffer) {
const hashType = buffer.readUInt8(buffer.length - 1); const hashType = buffer.readUInt8(buffer.length - 1);
const hashTypeMod = hashType & ~0x80; const hashTypeMod = hashType & ~0x80;
if (hashTypeMod <= 0 || hashTypeMod >= 4) if (hashTypeMod <= 0 || hashTypeMod >= 4)
throw new Error('Invalid hashType ' + hashType); throw new Error('Invalid hashType ' + hashType);
const decoded = bip66.decode(buffer.slice(0, -1)); const decoded = bip66.decode(buffer.slice(0, -1));
const r = fromDER(decoded.r); const r = fromDER(decoded.r);
const s = fromDER(decoded.s); const s = fromDER(decoded.s);
const signature = Buffer.concat([r, s], 64); const signature = Buffer.concat([r, s], 64);
return { signature, hashType }; return { signature, hashType };
} }
exports.decode = decode; exports.decode = decode;
function encode(signature, hashType) { function encode(signature, hashType) {
typeforce({ typeforce(
signature: types.BufferN(64), {
hashType: types.UInt8, signature: types.BufferN(64),
}, { signature, hashType }); hashType: types.UInt8,
const hashTypeMod = hashType & ~0x80; },
if (hashTypeMod <= 0 || hashTypeMod >= 4) { signature, hashType },
throw new Error('Invalid hashType ' + hashType); );
const hashTypeBuffer = Buffer.allocUnsafe(1); const hashTypeMod = hashType & ~0x80;
hashTypeBuffer.writeUInt8(hashType, 0); if (hashTypeMod <= 0 || hashTypeMod >= 4)
const r = toDER(signature.slice(0, 32)); throw new Error('Invalid hashType ' + hashType);
const s = toDER(signature.slice(32, 64)); const hashTypeBuffer = Buffer.allocUnsafe(1);
return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); 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; exports.encode = encode;

8
src/templates/multisig/index.js

@ -1,6 +1,6 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const input = require("./input"); const input = require('./input');
exports.input = input; exports.input = input;
const output = require("./output"); const output = require('./output');
exports.output = output; exports.output = output;

30
src/templates/multisig/input.js

@ -1,23 +1,23 @@
"use strict"; 'use strict';
// OP_0 [signatures ...] // OP_0 [signatures ...]
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
function partialSignature(value) { function partialSignature(value) {
return (value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value)); return (
value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value)
);
} }
function check(script, allowIncomplete) { function check(script, allowIncomplete) {
const chunks = bscript.decompile(script); const chunks = bscript.decompile(script);
if (chunks.length < 2) if (chunks.length < 2) return false;
return false; if (chunks[0] !== script_1.OPS.OP_0) return false;
if (chunks[0] !== script_1.OPS.OP_0) if (allowIncomplete) {
return false; return chunks.slice(1).every(partialSignature);
if (allowIncomplete) { }
return chunks.slice(1).every(partialSignature); return chunks.slice(1).every(bscript.isCanonicalScriptSignature);
}
return chunks.slice(1).every(bscript.isCanonicalScriptSignature);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'multisig input'; return 'multisig input';
}; };

49
src/templates/multisig/output.js

@ -1,36 +1,27 @@
"use strict"; 'use strict';
// m [pubKeys ...] n OP_CHECKMULTISIG // m [pubKeys ...] n OP_CHECKMULTISIG
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
const types = require("../../types"); const types = require('../../types');
const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1 const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1
function check(script, allowIncomplete) { function check(script, allowIncomplete) {
const chunks = bscript.decompile(script); const chunks = bscript.decompile(script);
if (chunks.length < 4) if (chunks.length < 4) return false;
return false; if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) return false;
if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) if (!types.Number(chunks[0])) return false;
return false; if (!types.Number(chunks[chunks.length - 2])) return false;
if (!types.Number(chunks[0])) const m = chunks[0] - OP_INT_BASE;
return false; const n = chunks[chunks.length - 2] - OP_INT_BASE;
if (!types.Number(chunks[chunks.length - 2])) if (m <= 0) return false;
return false; if (n > 16) return false;
const m = chunks[0] - OP_INT_BASE; if (m > n) return false;
const n = chunks[chunks.length - 2] - OP_INT_BASE; if (n !== chunks.length - 3) return false;
if (m <= 0) if (allowIncomplete) return true;
return false; const keys = chunks.slice(1, -2);
if (n > 16) return keys.every(bscript.isCanonicalPubKey);
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);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'multi-sig output'; return 'multi-sig output';
}; };

12
src/templates/nulldata.js

@ -1,15 +1,15 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
// OP_RETURN {data} // OP_RETURN {data}
const bscript = require("../script"); const bscript = require('../script');
const OPS = bscript.OPS; const OPS = bscript.OPS;
function check(script) { function check(script) {
const buffer = bscript.compile(script); const buffer = bscript.compile(script);
return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; return buffer.length > 1 && buffer[0] === OPS.OP_RETURN;
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'null data output'; return 'null data output';
}; };
const output = { check }; const output = { check };
exports.output = output; exports.output = output;

8
src/templates/pubkey/index.js

@ -1,6 +1,6 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const input = require("./input"); const input = require('./input');
exports.input = input; exports.input = input;
const output = require("./output"); const output = require('./output');
exports.output = output; exports.output = output;

13
src/templates/pubkey/input.js

@ -1,13 +1,12 @@
"use strict"; 'use strict';
// {signature} // {signature}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
function check(script) { function check(script) {
const chunks = bscript.decompile(script); const chunks = bscript.decompile(script);
return (chunks.length === 1 && return chunks.length === 1 && bscript.isCanonicalScriptSignature(chunks[0]);
bscript.isCanonicalScriptSignature(chunks[0]));
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'pubKey input'; return 'pubKey input';
}; };

20
src/templates/pubkey/output.js

@ -1,15 +1,17 @@
"use strict"; 'use strict';
// {pubKey} OP_CHECKSIG // {pubKey} OP_CHECKSIG
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
function check(script) { function check(script) {
const chunks = bscript.decompile(script); const chunks = bscript.decompile(script);
return (chunks.length === 2 && return (
bscript.isCanonicalPubKey(chunks[0]) && chunks.length === 2 &&
chunks[1] === script_1.OPS.OP_CHECKSIG); bscript.isCanonicalPubKey(chunks[0]) &&
chunks[1] === script_1.OPS.OP_CHECKSIG
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'pubKey output'; return 'pubKey output';
}; };

8
src/templates/pubkeyhash/index.js

@ -1,6 +1,6 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const input = require("./input"); const input = require('./input');
exports.input = input; exports.input = input;
const output = require("./output"); const output = require('./output');
exports.output = output; exports.output = output;

18
src/templates/pubkeyhash/input.js

@ -1,14 +1,16 @@
"use strict"; 'use strict';
// {signature} {pubKey} // {signature} {pubKey}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
function check(script) { function check(script) {
const chunks = bscript.decompile(script); const chunks = bscript.decompile(script);
return (chunks.length === 2 && return (
bscript.isCanonicalScriptSignature(chunks[0]) && chunks.length === 2 &&
bscript.isCanonicalPubKey(chunks[1])); bscript.isCanonicalScriptSignature(chunks[0]) &&
bscript.isCanonicalPubKey(chunks[1])
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'pubKeyHash input'; return 'pubKeyHash input';
}; };

26
src/templates/pubkeyhash/output.js

@ -1,18 +1,20 @@
"use strict"; 'use strict';
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
function check(script) { function check(script) {
const buffer = bscript.compile(script); const buffer = bscript.compile(script);
return (buffer.length === 25 && return (
buffer[0] === script_1.OPS.OP_DUP && buffer.length === 25 &&
buffer[1] === script_1.OPS.OP_HASH160 && buffer[0] === script_1.OPS.OP_DUP &&
buffer[2] === 0x14 && buffer[1] === script_1.OPS.OP_HASH160 &&
buffer[23] === script_1.OPS.OP_EQUALVERIFY && buffer[2] === 0x14 &&
buffer[24] === script_1.OPS.OP_CHECKSIG); buffer[23] === script_1.OPS.OP_EQUALVERIFY &&
buffer[24] === script_1.OPS.OP_CHECKSIG
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'pubKeyHash output'; return 'pubKeyHash output';
}; };

8
src/templates/scripthash/index.js

@ -1,6 +1,6 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const input = require("./input"); const input = require('./input');
exports.input = input; exports.input = input;
const output = require("./output"); const output = require('./output');
exports.output = output; exports.output = output;

82
src/templates/scripthash/input.js

@ -1,44 +1,50 @@
"use strict"; 'use strict';
// <scriptSig> {serialized scriptPubKey script} // <scriptSig> {serialized scriptPubKey script}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const p2ms = require("../multisig"); const p2ms = require('../multisig');
const p2pk = require("../pubkey"); const p2pk = require('../pubkey');
const p2pkh = require("../pubkeyhash"); const p2pkh = require('../pubkeyhash');
const p2wpkho = require("../witnesspubkeyhash/output"); const p2wpkho = require('../witnesspubkeyhash/output');
const p2wsho = require("../witnessscripthash/output"); const p2wsho = require('../witnessscripthash/output');
function check(script, allowIncomplete) { function check(script, allowIncomplete) {
const chunks = bscript.decompile(script); const chunks = bscript.decompile(script);
if (chunks.length < 1) if (chunks.length < 1) return false;
return false; const lastChunk = chunks[chunks.length - 1];
const lastChunk = chunks[chunks.length - 1]; if (!Buffer.isBuffer(lastChunk)) return false;
if (!Buffer.isBuffer(lastChunk)) const scriptSigChunks = bscript.decompile(
return false; bscript.compile(chunks.slice(0, -1)),
const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))); );
const redeemScriptChunks = bscript.decompile(lastChunk); const redeemScriptChunks = bscript.decompile(lastChunk);
// is redeemScript a valid script? // is redeemScript a valid script?
if (!redeemScriptChunks) if (!redeemScriptChunks) return false;
return false; // is redeemScriptSig push only?
// is redeemScriptSig push only? if (!bscript.isPushOnly(scriptSigChunks)) return false;
if (!bscript.isPushOnly(scriptSigChunks)) // is witness?
return false; if (chunks.length === 1) {
// is witness? return (
if (chunks.length === 1) { p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks)
return (p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks)); );
} }
// match types // match types
if (p2pkh.input.check(scriptSigChunks) && if (
p2pkh.output.check(redeemScriptChunks)) p2pkh.input.check(scriptSigChunks) &&
return true; p2pkh.output.check(redeemScriptChunks)
if (p2ms.input.check(scriptSigChunks, allowIncomplete) && )
p2ms.output.check(redeemScriptChunks)) return true;
return true; if (
if (p2pk.input.check(scriptSigChunks) && p2ms.input.check(scriptSigChunks, allowIncomplete) &&
p2pk.output.check(redeemScriptChunks)) p2ms.output.check(redeemScriptChunks)
return true; )
return false; return true;
if (
p2pk.input.check(scriptSigChunks) &&
p2pk.output.check(redeemScriptChunks)
)
return true;
return false;
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'scriptHash input'; return 'scriptHash input';
}; };

22
src/templates/scripthash/output.js

@ -1,16 +1,18 @@
"use strict"; 'use strict';
// OP_HASH160 {scriptHash} OP_EQUAL // OP_HASH160 {scriptHash} OP_EQUAL
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
function check(script) { function check(script) {
const buffer = bscript.compile(script); const buffer = bscript.compile(script);
return (buffer.length === 23 && return (
buffer[0] === script_1.OPS.OP_HASH160 && buffer.length === 23 &&
buffer[1] === 0x14 && buffer[0] === script_1.OPS.OP_HASH160 &&
buffer[22] === script_1.OPS.OP_EQUAL); buffer[1] === 0x14 &&
buffer[22] === script_1.OPS.OP_EQUAL
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'scriptHash output'; return 'scriptHash output';
}; };

6
src/templates/witnesscommitment/index.js

@ -1,4 +1,4 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const output = require("./output"); const output = require('./output');
exports.output = output; exports.output = output;

38
src/templates/witnesscommitment/output.js

@ -1,32 +1,34 @@
"use strict"; 'use strict';
// OP_RETURN {aa21a9ed} {commitment} // OP_RETURN {aa21a9ed} {commitment}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
const types = require("../../types"); const types = require('../../types');
const typeforce = require('typeforce'); const typeforce = require('typeforce');
const HEADER = Buffer.from('aa21a9ed', 'hex'); const HEADER = Buffer.from('aa21a9ed', 'hex');
function check(script) { function check(script) {
const buffer = bscript.compile(script); const buffer = bscript.compile(script);
return (buffer.length > 37 && return (
buffer[0] === script_1.OPS.OP_RETURN && buffer.length > 37 &&
buffer[1] === 0x24 && buffer[0] === script_1.OPS.OP_RETURN &&
buffer.slice(2, 6).equals(HEADER)); buffer[1] === 0x24 &&
buffer.slice(2, 6).equals(HEADER)
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'Witness commitment output'; return 'Witness commitment output';
}; };
function encode(commitment) { function encode(commitment) {
typeforce(types.Hash256bit, commitment); typeforce(types.Hash256bit, commitment);
const buffer = Buffer.allocUnsafe(36); const buffer = Buffer.allocUnsafe(36);
HEADER.copy(buffer, 0); HEADER.copy(buffer, 0);
commitment.copy(buffer, 4); commitment.copy(buffer, 4);
return bscript.compile([script_1.OPS.OP_RETURN, buffer]); return bscript.compile([script_1.OPS.OP_RETURN, buffer]);
} }
exports.encode = encode; exports.encode = encode;
function decode(buffer) { function decode(buffer) {
typeforce(check, buffer); typeforce(check, buffer);
return bscript.decompile(buffer)[1].slice(4, 36); return bscript.decompile(buffer)[1].slice(4, 36);
} }
exports.decode = decode; exports.decode = decode;

8
src/templates/witnesspubkeyhash/index.js

@ -1,6 +1,6 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const input = require("./input"); const input = require('./input');
exports.input = input; exports.input = input;
const output = require("./output"); const output = require('./output');
exports.output = output; exports.output = output;

20
src/templates/witnesspubkeyhash/input.js

@ -1,17 +1,19 @@
"use strict"; 'use strict';
// {signature} {pubKey} // {signature} {pubKey}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
function isCompressedCanonicalPubKey(pubKey) { function isCompressedCanonicalPubKey(pubKey) {
return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33;
} }
function check(script) { function check(script) {
const chunks = bscript.decompile(script); const chunks = bscript.decompile(script);
return (chunks.length === 2 && return (
bscript.isCanonicalScriptSignature(chunks[0]) && chunks.length === 2 &&
isCompressedCanonicalPubKey(chunks[1])); bscript.isCanonicalScriptSignature(chunks[0]) &&
isCompressedCanonicalPubKey(chunks[1])
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'witnessPubKeyHash input'; return 'witnessPubKeyHash input';
}; };

18
src/templates/witnesspubkeyhash/output.js

@ -1,13 +1,17 @@
"use strict"; 'use strict';
// OP_0 {pubKeyHash} // OP_0 {pubKeyHash}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
function check(script) { function check(script) {
const buffer = bscript.compile(script); const buffer = bscript.compile(script);
return buffer.length === 22 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x14; return (
buffer.length === 22 &&
buffer[0] === script_1.OPS.OP_0 &&
buffer[1] === 0x14
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'Witness pubKeyHash output'; return 'Witness pubKeyHash output';
}; };

8
src/templates/witnessscripthash/index.js

@ -1,6 +1,6 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const input = require("./input"); const input = require('./input');
exports.input = input; exports.input = input;
const output = require("./output"); const output = require('./output');
exports.output = output; exports.output = output;

61
src/templates/witnessscripthash/input.js

@ -1,36 +1,39 @@
"use strict"; 'use strict';
// <scriptSig> {serialized scriptPubKey script} // <scriptSig> {serialized scriptPubKey script}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const typeforce = require('typeforce'); const typeforce = require('typeforce');
const p2ms = require("../multisig"); const p2ms = require('../multisig');
const p2pk = require("../pubkey"); const p2pk = require('../pubkey');
const p2pkh = require("../pubkeyhash"); const p2pkh = require('../pubkeyhash');
function check(chunks, allowIncomplete) { function check(chunks, allowIncomplete) {
typeforce(typeforce.Array, chunks); typeforce(typeforce.Array, chunks);
if (chunks.length < 1) if (chunks.length < 1) return false;
return false; const witnessScript = chunks[chunks.length - 1];
const witnessScript = chunks[chunks.length - 1]; if (!Buffer.isBuffer(witnessScript)) return false;
if (!Buffer.isBuffer(witnessScript)) const witnessScriptChunks = bscript.decompile(witnessScript);
return false; // is witnessScript a valid script?
const witnessScriptChunks = bscript.decompile(witnessScript); if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false;
// is witnessScript a valid script? const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1));
if (!witnessScriptChunks || witnessScriptChunks.length === 0) // match types
return false; if (
const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); p2pkh.input.check(witnessRawScriptSig) &&
// match types p2pkh.output.check(witnessScriptChunks)
if (p2pkh.input.check(witnessRawScriptSig) && )
p2pkh.output.check(witnessScriptChunks)) return true;
return true; if (
if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && p2ms.input.check(witnessRawScriptSig, allowIncomplete) &&
p2ms.output.check(witnessScriptChunks)) p2ms.output.check(witnessScriptChunks)
return true; )
if (p2pk.input.check(witnessRawScriptSig) && return true;
p2pk.output.check(witnessScriptChunks)) if (
return true; p2pk.input.check(witnessRawScriptSig) &&
return false; p2pk.output.check(witnessScriptChunks)
)
return true;
return false;
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'witnessScriptHash input'; return 'witnessScriptHash input';
}; };

18
src/templates/witnessscripthash/output.js

@ -1,13 +1,17 @@
"use strict"; 'use strict';
// OP_0 {scriptHash} // OP_0 {scriptHash}
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bscript = require("../../script"); const bscript = require('../../script');
const script_1 = require("../../script"); const script_1 = require('../../script');
function check(script) { function check(script) {
const buffer = bscript.compile(script); const buffer = bscript.compile(script);
return buffer.length === 34 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x20; return (
buffer.length === 34 &&
buffer[0] === script_1.OPS.OP_0 &&
buffer[1] === 0x20
);
} }
exports.check = check; exports.check = check;
check.toJSON = () => { check.toJSON = () => {
return 'Witness scriptHash output'; return 'Witness scriptHash output';
}; };

882
src/transaction.js

@ -1,446 +1,472 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const bufferutils = require("./bufferutils"); const bufferutils = require('./bufferutils');
const bufferutils_1 = require("./bufferutils"); const bufferutils_1 = require('./bufferutils');
const bcrypto = require("./crypto"); const bcrypto = require('./crypto');
const bscript = require("./script"); const bscript = require('./script');
const script_1 = require("./script"); const script_1 = require('./script');
const types = require("./types"); const types = require('./types');
const typeforce = require('typeforce'); const typeforce = require('typeforce');
const varuint = require('varuint-bitcoin'); const varuint = require('varuint-bitcoin');
function varSliceSize(someScript) { function varSliceSize(someScript) {
const length = someScript.length; const length = someScript.length;
return varuint.encodingLength(length) + length; return varuint.encodingLength(length) + length;
} }
function vectorSize(someVector) { function vectorSize(someVector) {
const length = someVector.length; const length = someVector.length;
return (varuint.encodingLength(length) + return (
someVector.reduce((sum, witness) => { varuint.encodingLength(length) +
return sum + varSliceSize(witness); someVector.reduce((sum, witness) => {
}, 0)); return sum + varSliceSize(witness);
}, 0)
);
} }
const EMPTY_SCRIPT = Buffer.allocUnsafe(0); const EMPTY_SCRIPT = Buffer.allocUnsafe(0);
const EMPTY_WITNESS = []; const EMPTY_WITNESS = [];
const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); const ZERO = Buffer.from(
const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'); '0000000000000000000000000000000000000000000000000000000000000000',
'hex',
);
const ONE = Buffer.from(
'0000000000000000000000000000000000000000000000000000000000000001',
'hex',
);
const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex'); const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex');
const BLANK_OUTPUT = { const BLANK_OUTPUT = {
script: EMPTY_SCRIPT, script: EMPTY_SCRIPT,
valueBuffer: VALUE_UINT64_MAX, valueBuffer: VALUE_UINT64_MAX,
}; };
function isOutput(out) { function isOutput(out) {
return out.value !== undefined; return out.value !== undefined;
} }
class Transaction { class Transaction {
constructor() { constructor() {
this.version = 1; this.version = 1;
this.locktime = 0; this.locktime = 0;
this.ins = []; this.ins = [];
this.outs = []; this.outs = [];
} }
static fromBuffer(buffer, _NO_STRICT) { static fromBuffer(buffer, _NO_STRICT) {
let offset = 0; let offset = 0;
function readSlice(n) { function readSlice(n) {
offset += n; offset += n;
return buffer.slice(offset - n, offset); 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;
}
static fromHex(hex) {
return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
}
static isCoinbaseHash(buffer) {
typeforce(types.Hash256bit, buffer);
for (let i = 0; i < 32; ++i) {
if (buffer[i] !== 0)
return false;
}
return true;
}
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;
} }
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;
}
static fromHex(hex) {
return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
}
static isCoinbaseHash(buffer) {
typeforce(types.Hash256bit, buffer);
for (let i = 0; i < 32; ++i) {
if (buffer[i] !== 0) return false;
}
return true;
}
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;
}
} }
Transaction.DEFAULT_SEQUENCE = 0xffffffff; Transaction.DEFAULT_SEQUENCE = 0xffffffff;
Transaction.SIGHASH_ALL = 0x01; Transaction.SIGHASH_ALL = 0x01;

1379
src/transaction_builder.js

File diff suppressed because it is too large

28
src/types.js

@ -1,35 +1,35 @@
"use strict"; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
const typeforce = require('typeforce'); const typeforce = require('typeforce');
const UINT31_MAX = Math.pow(2, 31) - 1; const UINT31_MAX = Math.pow(2, 31) - 1;
function UInt31(value) { function UInt31(value) {
return typeforce.UInt32(value) && value <= UINT31_MAX; return typeforce.UInt32(value) && value <= UINT31_MAX;
} }
exports.UInt31 = UInt31; exports.UInt31 = UInt31;
function BIP32Path(value) { function BIP32Path(value) {
return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/);
} }
exports.BIP32Path = BIP32Path; exports.BIP32Path = BIP32Path;
BIP32Path.toJSON = () => { BIP32Path.toJSON = () => {
return 'BIP32 derivation path'; return 'BIP32 derivation path';
}; };
const SATOSHI_MAX = 21 * 1e14; const SATOSHI_MAX = 21 * 1e14;
function Satoshi(value) { function Satoshi(value) {
return typeforce.UInt53(value) && value <= SATOSHI_MAX; return typeforce.UInt53(value) && value <= SATOSHI_MAX;
} }
exports.Satoshi = Satoshi; exports.Satoshi = Satoshi;
// external dependent types // external dependent types
exports.ECPoint = typeforce.quacksLike('Point'); exports.ECPoint = typeforce.quacksLike('Point');
// exposed, external API // exposed, external API
exports.Network = typeforce.compile({ exports.Network = typeforce.compile({
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String),
bip32: { bip32: {
public: typeforce.UInt32, public: typeforce.UInt32,
private: typeforce.UInt32, private: typeforce.UInt32,
}, },
pubKeyHash: typeforce.UInt8, pubKeyHash: typeforce.UInt8,
scriptHash: typeforce.UInt8, scriptHash: typeforce.UInt8,
wif: typeforce.UInt8, wif: typeforce.UInt8,
}); });
exports.Buffer256bit = typeforce.BufferN(32); exports.Buffer256bit = typeforce.BufferN(32);
exports.Hash160bit = typeforce.BufferN(20); exports.Hash160bit = typeforce.BufferN(20);

Loading…
Cancel
Save