Merge pull request #1387 from bitcoinjs/trimSpace
Use Prettier to make JS easier to read/audit
This commit is contained in:
commit
94872f9bfe
45 changed files with 2969 additions and 2972 deletions
|
@ -15,13 +15,14 @@
|
||||||
"bitcoinjs"
|
"bitcoinjs"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run clean && tsc -p ./tsconfig.json",
|
"build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs",
|
||||||
"clean": "rimraf src",
|
"clean": "rimraf 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",
|
||||||
"format:ci": "npm run prettier -- --check",
|
"formatjs": "npm run prettierjs -- --write > /dev/null 2>&1",
|
||||||
|
"format:ci": "npm run prettier -- --check && npm run prettierjs -- --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",
|
||||||
"lint": "tslint -p tsconfig.json -c tslint.json",
|
"lint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
|
@ -31,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
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) {}
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) { }
|
}
|
||||||
if (decodeBase58) {
|
throw new Error(address + ' has no matching Script');
|
||||||
if (decodeBase58.version === network.pubKeyHash)
|
|
||||||
return payments.p2pkh({ hash: decodeBase58.hash }).output;
|
|
||||||
if (decodeBase58.version === network.scriptHash)
|
|
||||||
return payments.p2sh({ hash: decodeBase58.hash }).output;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
decodeBech32 = fromBech32(address);
|
|
||||||
}
|
|
||||||
catch (e) { }
|
|
||||||
if (decodeBech32) {
|
|
||||||
if (decodeBech32.prefix !== network.bech32)
|
|
||||||
throw new Error(address + ' has an invalid prefix');
|
|
||||||
if (decodeBech32.version === 0) {
|
|
||||||
if (decodeBech32.data.length === 20)
|
|
||||||
return payments.p2wpkh({ hash: decodeBech32.data }).output;
|
|
||||||
if (decodeBech32.data.length === 32)
|
|
||||||
return payments.p2wsh({ hash: decodeBech32.data }).output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(address + ' has no matching Script');
|
|
||||||
}
|
}
|
||||||
exports.toOutputScript = toOutputScript;
|
exports.toOutputScript = toOutputScript;
|
||||||
|
|
442
src/block.js
442
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
|
|
||||||
if (witnessCommit)
|
|
||||||
block.witnessCommit = witnessCommit;
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
static fromHex(hex) {
|
|
||||||
return Block.fromBuffer(Buffer.from(hex, 'hex'));
|
|
||||||
}
|
|
||||||
static calculateTarget(bits) {
|
|
||||||
const exponent = ((bits & 0xff000000) >> 24) - 3;
|
|
||||||
const mantissa = bits & 0x007fffff;
|
|
||||||
const target = Buffer.alloc(32, 0);
|
|
||||||
target.writeUIntBE(mantissa, 29 - exponent, 3);
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
static calculateMerkleRoot(transactions, forWitness) {
|
|
||||||
typeforce([{ getHash: types.Function }], transactions);
|
|
||||||
if (transactions.length === 0)
|
|
||||||
throw errorMerkleNoTxes;
|
|
||||||
if (forWitness && !txesHaveWitnessCommit(transactions))
|
|
||||||
throw errorWitnessNotSegwit;
|
|
||||||
const hashes = transactions.map(transaction => transaction.getHash(forWitness));
|
|
||||||
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
|
|
||||||
return forWitness
|
|
||||||
? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]))
|
|
||||||
: rootHash;
|
|
||||||
}
|
|
||||||
getWitnessCommit() {
|
|
||||||
if (!txesHaveWitnessCommit(this.transactions))
|
|
||||||
return null;
|
|
||||||
// The merkle root for the witness data is in an OP_RETURN output.
|
|
||||||
// There is no rule for the index of the output, so use filter to find it.
|
|
||||||
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
|
|
||||||
// If multiple commits are found, the output with highest index is assumed.
|
|
||||||
const witnessCommits = this.transactions[0].outs.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))).map(out => out.script.slice(6, 38));
|
|
||||||
if (witnessCommits.length === 0)
|
|
||||||
return null;
|
|
||||||
// Use the commit with the highest output (should only be one though)
|
|
||||||
const result = witnessCommits[witnessCommits.length - 1];
|
|
||||||
if (!(result instanceof Buffer && result.length === 32))
|
|
||||||
return null;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
hasWitnessCommit() {
|
|
||||||
if (this.witnessCommit instanceof Buffer &&
|
|
||||||
this.witnessCommit.length === 32)
|
|
||||||
return true;
|
|
||||||
if (this.getWitnessCommit() !== null)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
hasWitness() {
|
|
||||||
return anyTxHasWitness(this.transactions);
|
|
||||||
}
|
|
||||||
byteLength(headersOnly) {
|
|
||||||
if (headersOnly || !this.transactions)
|
|
||||||
return 80;
|
|
||||||
return (80 +
|
|
||||||
varuint.encodingLength(this.transactions.length) +
|
|
||||||
this.transactions.reduce((a, x) => a + x.byteLength(), 0));
|
|
||||||
}
|
|
||||||
getHash() {
|
|
||||||
return bcrypto.hash256(this.toBuffer(true));
|
|
||||||
}
|
|
||||||
getId() {
|
|
||||||
return bufferutils_1.reverseBuffer(this.getHash()).toString('hex');
|
|
||||||
}
|
|
||||||
getUTCDate() {
|
|
||||||
const date = new Date(0); // epoch
|
|
||||||
date.setUTCSeconds(this.timestamp);
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
// TODO: buffer, offset compatibility
|
|
||||||
toBuffer(headersOnly) {
|
|
||||||
const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
|
|
||||||
let offset = 0;
|
|
||||||
const writeSlice = (slice) => {
|
|
||||||
slice.copy(buffer, offset);
|
|
||||||
offset += slice.length;
|
|
||||||
};
|
|
||||||
const writeInt32 = (i) => {
|
|
||||||
buffer.writeInt32LE(i, offset);
|
|
||||||
offset += 4;
|
|
||||||
};
|
|
||||||
const writeUInt32 = (i) => {
|
|
||||||
buffer.writeUInt32LE(i, offset);
|
|
||||||
offset += 4;
|
|
||||||
};
|
|
||||||
writeInt32(this.version);
|
|
||||||
writeSlice(this.prevHash);
|
|
||||||
writeSlice(this.merkleRoot);
|
|
||||||
writeUInt32(this.timestamp);
|
|
||||||
writeUInt32(this.bits);
|
|
||||||
writeUInt32(this.nonce);
|
|
||||||
if (headersOnly || !this.transactions)
|
|
||||||
return buffer;
|
|
||||||
varuint.encode(this.transactions.length, buffer, offset);
|
|
||||||
offset += varuint.encode.bytes;
|
|
||||||
this.transactions.forEach(tx => {
|
|
||||||
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
|
|
||||||
tx.toBuffer(buffer, offset);
|
|
||||||
offset += txSize;
|
|
||||||
});
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
toHex(headersOnly) {
|
|
||||||
return this.toBuffer(headersOnly).toString('hex');
|
|
||||||
}
|
|
||||||
checkTxRoots() {
|
|
||||||
// If the Block has segwit transactions but no witness commit,
|
|
||||||
// there's no way it can be valid, so fail the check.
|
|
||||||
const hasWitnessCommit = this.hasWitnessCommit();
|
|
||||||
if (!hasWitnessCommit && this.hasWitness())
|
|
||||||
return false;
|
|
||||||
return (this.__checkMerkleRoot() &&
|
|
||||||
(hasWitnessCommit ? this.__checkWitnessCommit() : true));
|
|
||||||
}
|
|
||||||
checkProofOfWork() {
|
|
||||||
const hash = bufferutils_1.reverseBuffer(this.getHash());
|
|
||||||
const target = Block.calculateTarget(this.bits);
|
|
||||||
return hash.compare(target) <= 0;
|
|
||||||
}
|
|
||||||
__checkMerkleRoot() {
|
|
||||||
if (!this.transactions)
|
|
||||||
throw errorMerkleNoTxes;
|
|
||||||
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
|
|
||||||
return this.merkleRoot.compare(actualMerkleRoot) === 0;
|
|
||||||
}
|
|
||||||
__checkWitnessCommit() {
|
|
||||||
if (!this.transactions)
|
|
||||||
throw errorMerkleNoTxes;
|
|
||||||
if (!this.hasWitnessCommit())
|
|
||||||
throw errorWitnessNotSegwit;
|
|
||||||
const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true);
|
|
||||||
return this.witnessCommit.compare(actualWitnessCommit) === 0;
|
|
||||||
}
|
}
|
||||||
|
const witnessCommit = block.getWitnessCommit();
|
||||||
|
// This Block contains a witness commit
|
||||||
|
if (witnessCommit) block.witnessCommit = witnessCommit;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
static fromHex(hex) {
|
||||||
|
return Block.fromBuffer(Buffer.from(hex, 'hex'));
|
||||||
|
}
|
||||||
|
static calculateTarget(bits) {
|
||||||
|
const exponent = ((bits & 0xff000000) >> 24) - 3;
|
||||||
|
const mantissa = bits & 0x007fffff;
|
||||||
|
const target = Buffer.alloc(32, 0);
|
||||||
|
target.writeUIntBE(mantissa, 29 - exponent, 3);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
static calculateMerkleRoot(transactions, forWitness) {
|
||||||
|
typeforce([{ getHash: types.Function }], transactions);
|
||||||
|
if (transactions.length === 0) throw errorMerkleNoTxes;
|
||||||
|
if (forWitness && !txesHaveWitnessCommit(transactions))
|
||||||
|
throw errorWitnessNotSegwit;
|
||||||
|
const hashes = transactions.map(transaction =>
|
||||||
|
transaction.getHash(forWitness),
|
||||||
|
);
|
||||||
|
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
|
||||||
|
return forWitness
|
||||||
|
? bcrypto.hash256(
|
||||||
|
Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]),
|
||||||
|
)
|
||||||
|
: rootHash;
|
||||||
|
}
|
||||||
|
getWitnessCommit() {
|
||||||
|
if (!txesHaveWitnessCommit(this.transactions)) return null;
|
||||||
|
// The merkle root for the witness data is in an OP_RETURN output.
|
||||||
|
// There is no rule for the index of the output, so use filter to find it.
|
||||||
|
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
|
||||||
|
// If multiple commits are found, the output with highest index is assumed.
|
||||||
|
const witnessCommits = this.transactions[0].outs
|
||||||
|
.filter(out =>
|
||||||
|
out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
|
||||||
|
)
|
||||||
|
.map(out => out.script.slice(6, 38));
|
||||||
|
if (witnessCommits.length === 0) return null;
|
||||||
|
// Use the commit with the highest output (should only be one though)
|
||||||
|
const result = witnessCommits[witnessCommits.length - 1];
|
||||||
|
if (!(result instanceof Buffer && result.length === 32)) return null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
hasWitnessCommit() {
|
||||||
|
if (
|
||||||
|
this.witnessCommit instanceof Buffer &&
|
||||||
|
this.witnessCommit.length === 32
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
if (this.getWitnessCommit() !== null) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hasWitness() {
|
||||||
|
return anyTxHasWitness(this.transactions);
|
||||||
|
}
|
||||||
|
byteLength(headersOnly) {
|
||||||
|
if (headersOnly || !this.transactions) return 80;
|
||||||
|
return (
|
||||||
|
80 +
|
||||||
|
varuint.encodingLength(this.transactions.length) +
|
||||||
|
this.transactions.reduce((a, x) => a + x.byteLength(), 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
getHash() {
|
||||||
|
return bcrypto.hash256(this.toBuffer(true));
|
||||||
|
}
|
||||||
|
getId() {
|
||||||
|
return bufferutils_1.reverseBuffer(this.getHash()).toString('hex');
|
||||||
|
}
|
||||||
|
getUTCDate() {
|
||||||
|
const date = new Date(0); // epoch
|
||||||
|
date.setUTCSeconds(this.timestamp);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
// TODO: buffer, offset compatibility
|
||||||
|
toBuffer(headersOnly) {
|
||||||
|
const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
|
||||||
|
let offset = 0;
|
||||||
|
const writeSlice = slice => {
|
||||||
|
slice.copy(buffer, offset);
|
||||||
|
offset += slice.length;
|
||||||
|
};
|
||||||
|
const writeInt32 = i => {
|
||||||
|
buffer.writeInt32LE(i, offset);
|
||||||
|
offset += 4;
|
||||||
|
};
|
||||||
|
const writeUInt32 = i => {
|
||||||
|
buffer.writeUInt32LE(i, offset);
|
||||||
|
offset += 4;
|
||||||
|
};
|
||||||
|
writeInt32(this.version);
|
||||||
|
writeSlice(this.prevHash);
|
||||||
|
writeSlice(this.merkleRoot);
|
||||||
|
writeUInt32(this.timestamp);
|
||||||
|
writeUInt32(this.bits);
|
||||||
|
writeUInt32(this.nonce);
|
||||||
|
if (headersOnly || !this.transactions) return buffer;
|
||||||
|
varuint.encode(this.transactions.length, buffer, offset);
|
||||||
|
offset += varuint.encode.bytes;
|
||||||
|
this.transactions.forEach(tx => {
|
||||||
|
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
|
||||||
|
tx.toBuffer(buffer, offset);
|
||||||
|
offset += txSize;
|
||||||
|
});
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
toHex(headersOnly) {
|
||||||
|
return this.toBuffer(headersOnly).toString('hex');
|
||||||
|
}
|
||||||
|
checkTxRoots() {
|
||||||
|
// If the Block has segwit transactions but no witness commit,
|
||||||
|
// there's no way it can be valid, so fail the check.
|
||||||
|
const hasWitnessCommit = this.hasWitnessCommit();
|
||||||
|
if (!hasWitnessCommit && this.hasWitness()) return false;
|
||||||
|
return (
|
||||||
|
this.__checkMerkleRoot() &&
|
||||||
|
(hasWitnessCommit ? this.__checkWitnessCommit() : true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
checkProofOfWork() {
|
||||||
|
const hash = bufferutils_1.reverseBuffer(this.getHash());
|
||||||
|
const target = Block.calculateTarget(this.bits);
|
||||||
|
return hash.compare(target) <= 0;
|
||||||
|
}
|
||||||
|
__checkMerkleRoot() {
|
||||||
|
if (!this.transactions) throw errorMerkleNoTxes;
|
||||||
|
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
|
||||||
|
return this.merkleRoot.compare(actualMerkleRoot) === 0;
|
||||||
|
}
|
||||||
|
__checkWitnessCommit() {
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
176
src/ecpair.js
176
src/ecpair.js
|
@ -1,113 +1,105 @@
|
||||||
"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, lowR = false) {
|
||||||
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);
|
if (lowR === false) {
|
||||||
}
|
return ecc.sign(hash, this.__D);
|
||||||
sign(hash, lowR = false) {
|
} else {
|
||||||
if (!this.__D)
|
let sig = ecc.sign(hash, this.__D);
|
||||||
throw new Error('Missing private key');
|
const extraData = Buffer.alloc(32, 0);
|
||||||
if (lowR === false) {
|
let counter = 0;
|
||||||
return ecc.sign(hash, this.__D);
|
// if first try is lowR, skip the loop
|
||||||
}
|
// for second try and on, add extra entropy counting up
|
||||||
else {
|
while (sig[0] > 0x7f) {
|
||||||
let sig = ecc.sign(hash, this.__D);
|
counter++;
|
||||||
const extraData = Buffer.alloc(32, 0);
|
extraData.writeUIntLE(counter, 0, 6);
|
||||||
let counter = 0;
|
sig = ecc.signWithEntropy(hash, this.__D, extraData);
|
||||||
// if first try is lowR, skip the loop
|
}
|
||||||
// for second try and on, add extra entropy counting up
|
return sig;
|
||||||
while (sig[0] > 0x7f) {
|
|
||||||
counter++;
|
|
||||||
extraData.writeUIntLE(counter, 0, 6);
|
|
||||||
sig = ecc.signWithEntropy(hash, this.__D, extraData);
|
|
||||||
}
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verify(hash, signature) {
|
|
||||||
return ecc.verify(hash, this.publicKey, signature);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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
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;
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 };
|
|
||||||
});
|
|
||||||
const _chunks = lazy.value(() => {
|
|
||||||
return bscript.decompile(a.input);
|
|
||||||
});
|
|
||||||
const _redeem = lazy.value(() => {
|
|
||||||
const chunks = _chunks();
|
|
||||||
return {
|
|
||||||
network,
|
|
||||||
output: chunks[chunks.length - 1],
|
|
||||||
input: bscript.compile(chunks.slice(0, -1)),
|
|
||||||
witness: a.witness || [],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
// output dependents
|
|
||||||
lazy.prop(o, 'address', () => {
|
|
||||||
if (!o.hash)
|
|
||||||
return;
|
|
||||||
const payload = Buffer.allocUnsafe(21);
|
|
||||||
payload.writeUInt8(o.network.scriptHash, 0);
|
|
||||||
o.hash.copy(payload, 1);
|
|
||||||
return bs58check.encode(payload);
|
|
||||||
});
|
|
||||||
lazy.prop(o, 'hash', () => {
|
|
||||||
// in order of least effort
|
|
||||||
if (a.output)
|
|
||||||
return a.output.slice(2, 22);
|
|
||||||
if (a.address)
|
|
||||||
return _address().hash;
|
|
||||||
if (o.redeem && o.redeem.output)
|
|
||||||
return bcrypto.hash160(o.redeem.output);
|
|
||||||
});
|
|
||||||
lazy.prop(o, 'output', () => {
|
|
||||||
if (!o.hash)
|
|
||||||
return;
|
|
||||||
return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]);
|
|
||||||
});
|
|
||||||
// input dependents
|
|
||||||
lazy.prop(o, 'redeem', () => {
|
|
||||||
if (!a.input)
|
|
||||||
return;
|
|
||||||
return _redeem();
|
|
||||||
});
|
|
||||||
lazy.prop(o, 'input', () => {
|
|
||||||
if (!a.redeem || !a.redeem.input || !a.redeem.output)
|
|
||||||
return;
|
|
||||||
return bscript.compile([].concat(bscript.decompile(a.redeem.input), a.redeem.output));
|
|
||||||
});
|
|
||||||
lazy.prop(o, 'witness', () => {
|
|
||||||
if (o.redeem && o.redeem.witness)
|
|
||||||
return o.redeem.witness;
|
|
||||||
if (o.input)
|
|
||||||
return [];
|
|
||||||
});
|
|
||||||
if (opts.validate) {
|
|
||||||
let hash = Buffer.from([]);
|
|
||||||
if (a.address) {
|
|
||||||
if (_address().version !== network.scriptHash)
|
|
||||||
throw new TypeError('Invalid version or Network mismatch');
|
|
||||||
if (_address().hash.length !== 20)
|
|
||||||
throw new TypeError('Invalid address');
|
|
||||||
hash = _address().hash;
|
|
||||||
}
|
|
||||||
if (a.hash) {
|
|
||||||
if (hash.length > 0 && !hash.equals(a.hash))
|
|
||||||
throw new TypeError('Hash mismatch');
|
|
||||||
else
|
|
||||||
hash = a.hash;
|
|
||||||
}
|
|
||||||
if (a.output) {
|
|
||||||
if (a.output.length !== 23 ||
|
|
||||||
a.output[0] !== OPS.OP_HASH160 ||
|
|
||||||
a.output[1] !== 0x14 ||
|
|
||||||
a.output[22] !== OPS.OP_EQUAL)
|
|
||||||
throw new TypeError('Output is invalid');
|
|
||||||
const hash2 = a.output.slice(2, 22);
|
|
||||||
if (hash.length > 0 && !hash.equals(hash2))
|
|
||||||
throw new TypeError('Hash mismatch');
|
|
||||||
else
|
|
||||||
hash = hash2;
|
|
||||||
}
|
|
||||||
// inlined to prevent 'no-inner-declarations' failing
|
|
||||||
const checkRedeem = (redeem) => {
|
|
||||||
// is the redeem output empty/invalid?
|
|
||||||
if (redeem.output) {
|
|
||||||
const decompile = bscript.decompile(redeem.output);
|
|
||||||
if (!decompile || decompile.length < 1)
|
|
||||||
throw new TypeError('Redeem.output too short');
|
|
||||||
// match hash against other sources
|
|
||||||
const hash2 = bcrypto.hash160(redeem.output);
|
|
||||||
if (hash.length > 0 && !hash.equals(hash2))
|
|
||||||
throw new TypeError('Hash mismatch');
|
|
||||||
else
|
|
||||||
hash = hash2;
|
|
||||||
}
|
|
||||||
if (redeem.input) {
|
|
||||||
const hasInput = redeem.input.length > 0;
|
|
||||||
const hasWitness = redeem.witness && redeem.witness.length > 0;
|
|
||||||
if (!hasInput && !hasWitness)
|
|
||||||
throw new TypeError('Empty input');
|
|
||||||
if (hasInput && hasWitness)
|
|
||||||
throw new TypeError('Input and witness provided');
|
|
||||||
if (hasInput) {
|
|
||||||
const richunks = bscript.decompile(redeem.input);
|
|
||||||
if (!bscript.isPushOnly(richunks))
|
|
||||||
throw new TypeError('Non push-only scriptSig');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (a.input) {
|
|
||||||
const chunks = _chunks();
|
|
||||||
if (!chunks || chunks.length < 1)
|
|
||||||
throw new TypeError('Input too short');
|
|
||||||
if (!Buffer.isBuffer(_redeem().output))
|
|
||||||
throw new TypeError('Input is invalid');
|
|
||||||
checkRedeem(_redeem());
|
|
||||||
}
|
|
||||||
if (a.redeem) {
|
|
||||||
if (a.redeem.network && a.redeem.network !== network)
|
|
||||||
throw new TypeError('Network mismatch');
|
|
||||||
if (a.input) {
|
|
||||||
const redeem = _redeem();
|
|
||||||
if (a.redeem.output && !a.redeem.output.equals(redeem.output))
|
|
||||||
throw new TypeError('Redeem.output mismatch');
|
|
||||||
if (a.redeem.input && !a.redeem.input.equals(redeem.input))
|
|
||||||
throw new TypeError('Redeem.input mismatch');
|
|
||||||
}
|
|
||||||
checkRedeem(a.redeem);
|
|
||||||
}
|
|
||||||
if (a.witness) {
|
|
||||||
if (a.redeem &&
|
|
||||||
a.redeem.witness &&
|
|
||||||
!stacksEqual(a.redeem.witness, a.witness))
|
|
||||||
throw new TypeError('Witness and redeem.witness mismatch');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Object.assign(o, a);
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
exports.p2sh = p2sh;
|
exports.p2sh = p2sh;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
||||||
|
let offset = 0;
|
||||||
|
function readSlice(n) {
|
||||||
|
offset += n;
|
||||||
|
return buffer.slice(offset - n, offset);
|
||||||
}
|
}
|
||||||
static fromBuffer(buffer, _NO_STRICT) {
|
function readUInt32() {
|
||||||
let offset = 0;
|
const i = buffer.readUInt32LE(offset);
|
||||||
function readSlice(n) {
|
offset += 4;
|
||||||
offset += n;
|
return i;
|
||||||
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) {
|
function readInt32() {
|
||||||
return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
|
const i = buffer.readInt32LE(offset);
|
||||||
|
offset += 4;
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
static isCoinbaseHash(buffer) {
|
function readUInt64() {
|
||||||
typeforce(types.Hash256bit, buffer);
|
const i = bufferutils.readUInt64LE(buffer, offset);
|
||||||
for (let i = 0; i < 32; ++i) {
|
offset += 8;
|
||||||
if (buffer[i] !== 0)
|
return i;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
isCoinbase() {
|
function readVarInt() {
|
||||||
return (this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash));
|
const vi = varuint.decode(buffer, offset);
|
||||||
|
offset += varuint.decode.bytes;
|
||||||
|
return vi;
|
||||||
}
|
}
|
||||||
addInput(hash, index, sequence, scriptSig) {
|
function readVarSlice() {
|
||||||
typeforce(types.tuple(types.Hash256bit, types.UInt32, types.maybe(types.UInt32), types.maybe(types.Buffer)), arguments);
|
return readSlice(readVarInt());
|
||||||
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) {
|
function readVector() {
|
||||||
typeforce(types.tuple(types.Buffer, types.Satoshi), arguments);
|
const count = readVarInt();
|
||||||
// Add the output and return the output's index
|
const vector = [];
|
||||||
return (this.outs.push({
|
for (let i = 0; i < count; i++) vector.push(readVarSlice());
|
||||||
script: scriptPubKey,
|
return vector;
|
||||||
value,
|
|
||||||
}) - 1);
|
|
||||||
}
|
}
|
||||||
hasWitnesses() {
|
const tx = new Transaction();
|
||||||
return this.ins.some(x => {
|
tx.version = readInt32();
|
||||||
return x.witness.length !== 0;
|
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;
|
||||||
}
|
}
|
||||||
weight() {
|
const vinLen = readVarInt();
|
||||||
const base = this.__byteLength(false);
|
for (let i = 0; i < vinLen; ++i) {
|
||||||
const total = this.__byteLength(true);
|
tx.ins.push({
|
||||||
return base * 3 + total;
|
hash: readSlice(32),
|
||||||
|
index: readUInt32(),
|
||||||
|
script: readVarSlice(),
|
||||||
|
sequence: readUInt32(),
|
||||||
|
witness: EMPTY_WITNESS,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
virtualSize() {
|
const voutLen = readVarInt();
|
||||||
return Math.ceil(this.weight() / 4);
|
for (let i = 0; i < voutLen; ++i) {
|
||||||
|
tx.outs.push({
|
||||||
|
value: readUInt64(),
|
||||||
|
script: readVarSlice(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
byteLength() {
|
if (hasWitnesses) {
|
||||||
return this.__byteLength(true);
|
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');
|
||||||
}
|
}
|
||||||
clone() {
|
tx.locktime = readUInt32();
|
||||||
const newTx = new Transaction();
|
if (_NO_STRICT) return tx;
|
||||||
newTx.version = this.version;
|
if (offset !== buffer.length)
|
||||||
newTx.locktime = this.locktime;
|
throw new Error('Transaction has unexpected data');
|
||||||
newTx.ins = this.ins.map(txIn => {
|
return tx;
|
||||||
return {
|
}
|
||||||
hash: txIn.hash,
|
static fromHex(hex) {
|
||||||
index: txIn.index,
|
return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
|
||||||
script: txIn.script,
|
}
|
||||||
sequence: txIn.sequence,
|
static isCoinbaseHash(buffer) {
|
||||||
witness: txIn.witness,
|
typeforce(types.Hash256bit, buffer);
|
||||||
};
|
for (let i = 0; i < 32; ++i) {
|
||||||
});
|
if (buffer[i] !== 0) return false;
|
||||||
newTx.outs = this.outs.map(txOut => {
|
|
||||||
return {
|
|
||||||
script: txOut.script,
|
|
||||||
value: txOut.value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return newTx;
|
|
||||||
}
|
}
|
||||||
/**
|
return true;
|
||||||
* Hash transaction for signing a specific input.
|
}
|
||||||
*
|
isCoinbase() {
|
||||||
* Bitcoin uses a different hash for each signed transaction input.
|
return (
|
||||||
* This method copies the transaction, makes the necessary changes based on the
|
this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
|
||||||
* hashType, and then hashes the result.
|
);
|
||||||
* This hash can then be used to sign the provided transaction input.
|
}
|
||||||
*/
|
addInput(hash, index, sequence, scriptSig) {
|
||||||
hashForSignature(inIndex, prevOutScript, hashType) {
|
typeforce(
|
||||||
typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments);
|
types.tuple(
|
||||||
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
|
types.Hash256bit,
|
||||||
if (inIndex >= this.ins.length)
|
types.UInt32,
|
||||||
return ONE;
|
types.maybe(types.UInt32),
|
||||||
// ignore OP_CODESEPARATOR
|
types.maybe(types.Buffer),
|
||||||
const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(x => {
|
),
|
||||||
return x !== script_1.OPS.OP_CODESEPARATOR;
|
arguments,
|
||||||
}));
|
);
|
||||||
const txTmp = this.clone();
|
if (types.Null(sequence)) {
|
||||||
// SIGHASH_NONE: ignore all outputs? (wildcard payee)
|
sequence = Transaction.DEFAULT_SEQUENCE;
|
||||||
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) {
|
// Add the input and return the input's index
|
||||||
typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments);
|
return (
|
||||||
let tbuffer = Buffer.from([]);
|
this.ins.push({
|
||||||
let toffset = 0;
|
hash,
|
||||||
function writeSlice(slice) {
|
index,
|
||||||
toffset += slice.copy(tbuffer, toffset);
|
script: scriptSig || EMPTY_SCRIPT,
|
||||||
}
|
sequence: sequence,
|
||||||
function writeUInt32(i) {
|
witness: EMPTY_WITNESS,
|
||||||
toffset = tbuffer.writeUInt32LE(i, toffset);
|
}) - 1
|
||||||
}
|
);
|
||||||
function writeUInt64(i) {
|
}
|
||||||
toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset);
|
addOutput(scriptPubKey, value) {
|
||||||
}
|
typeforce(types.tuple(types.Buffer, types.Satoshi), arguments);
|
||||||
function writeVarInt(i) {
|
// Add the output and return the output's index
|
||||||
varuint.encode(i, tbuffer, toffset);
|
return (
|
||||||
toffset += varuint.encode.bytes;
|
this.outs.push({
|
||||||
}
|
script: scriptPubKey,
|
||||||
function writeVarSlice(slice) {
|
value,
|
||||||
writeVarInt(slice.length);
|
}) - 1
|
||||||
writeSlice(slice);
|
);
|
||||||
}
|
}
|
||||||
let hashOutputs = ZERO;
|
hasWitnesses() {
|
||||||
let hashPrevouts = ZERO;
|
return this.ins.some(x => {
|
||||||
let hashSequence = ZERO;
|
return x.witness.length !== 0;
|
||||||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
|
});
|
||||||
tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
|
}
|
||||||
toffset = 0;
|
weight() {
|
||||||
this.ins.forEach(txIn => {
|
const base = this.__byteLength(false);
|
||||||
writeSlice(txIn.hash);
|
const total = this.__byteLength(true);
|
||||||
writeUInt32(txIn.index);
|
return base * 3 + total;
|
||||||
});
|
}
|
||||||
hashPrevouts = bcrypto.hash256(tbuffer);
|
virtualSize() {
|
||||||
}
|
return Math.ceil(this.weight() / 4);
|
||||||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
|
}
|
||||||
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
byteLength() {
|
||||||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
|
return this.__byteLength(true);
|
||||||
tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
|
}
|
||||||
toffset = 0;
|
clone() {
|
||||||
this.ins.forEach(txIn => {
|
const newTx = new Transaction();
|
||||||
writeUInt32(txIn.sequence);
|
newTx.version = this.version;
|
||||||
});
|
newTx.locktime = this.locktime;
|
||||||
hashSequence = bcrypto.hash256(tbuffer);
|
newTx.ins = this.ins.map(txIn => {
|
||||||
}
|
return {
|
||||||
if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
hash: txIn.hash,
|
||||||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
|
index: txIn.index,
|
||||||
const txOutsSize = this.outs.reduce((sum, output) => {
|
script: txIn.script,
|
||||||
return sum + 8 + varSliceSize(output.script);
|
sequence: txIn.sequence,
|
||||||
}, 0);
|
witness: txIn.witness,
|
||||||
tbuffer = Buffer.allocUnsafe(txOutsSize);
|
};
|
||||||
toffset = 0;
|
});
|
||||||
this.outs.forEach(out => {
|
newTx.outs = this.outs.map(txOut => {
|
||||||
writeUInt64(out.value);
|
return {
|
||||||
writeVarSlice(out.script);
|
script: txOut.script,
|
||||||
});
|
value: txOut.value,
|
||||||
hashOutputs = bcrypto.hash256(tbuffer);
|
};
|
||||||
}
|
});
|
||||||
else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE &&
|
return newTx;
|
||||||
inIndex < this.outs.length) {
|
}
|
||||||
const output = this.outs[inIndex];
|
/**
|
||||||
tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
|
* Hash transaction for signing a specific input.
|
||||||
toffset = 0;
|
*
|
||||||
writeUInt64(output.value);
|
* Bitcoin uses a different hash for each signed transaction input.
|
||||||
writeVarSlice(output.script);
|
* This method copies the transaction, makes the necessary changes based on the
|
||||||
hashOutputs = bcrypto.hash256(tbuffer);
|
* hashType, and then hashes the result.
|
||||||
}
|
* This hash can then be used to sign the provided transaction input.
|
||||||
tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript));
|
*/
|
||||||
toffset = 0;
|
hashForSignature(inIndex, prevOutScript, hashType) {
|
||||||
const input = this.ins[inIndex];
|
typeforce(
|
||||||
writeUInt32(this.version);
|
types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number),
|
||||||
writeSlice(hashPrevouts);
|
arguments,
|
||||||
writeSlice(hashSequence);
|
);
|
||||||
writeSlice(input.hash);
|
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
|
||||||
writeUInt32(input.index);
|
if (inIndex >= this.ins.length) return ONE;
|
||||||
writeVarSlice(prevOutScript);
|
// ignore OP_CODESEPARATOR
|
||||||
writeUInt64(value);
|
const ourScript = bscript.compile(
|
||||||
writeUInt32(input.sequence);
|
bscript.decompile(prevOutScript).filter(x => {
|
||||||
writeSlice(hashOutputs);
|
return x !== script_1.OPS.OP_CODESEPARATOR;
|
||||||
writeUInt32(this.locktime);
|
}),
|
||||||
writeUInt32(hashType);
|
);
|
||||||
return bcrypto.hash256(tbuffer);
|
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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
getHash(forWitness) {
|
// SIGHASH_ANYONECANPAY: ignore inputs entirely?
|
||||||
// wtxid for coinbase is always 32 bytes of 0x00
|
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
|
||||||
if (forWitness && this.isCoinbase())
|
txTmp.ins = [txTmp.ins[inIndex]];
|
||||||
return Buffer.alloc(32, 0);
|
txTmp.ins[0].script = ourScript;
|
||||||
return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness));
|
// SIGHASH_ALL: only ignore input scripts
|
||||||
|
} else {
|
||||||
|
// "blank" others input scripts
|
||||||
|
txTmp.ins.forEach(input => {
|
||||||
|
input.script = EMPTY_SCRIPT;
|
||||||
|
});
|
||||||
|
txTmp.ins[inIndex].script = ourScript;
|
||||||
}
|
}
|
||||||
getId() {
|
// serialize and hash
|
||||||
// transaction hash's are displayed in reverse order
|
const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4);
|
||||||
return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex');
|
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);
|
||||||
}
|
}
|
||||||
toBuffer(buffer, initialOffset) {
|
function writeUInt32(i) {
|
||||||
return this.__toBuffer(buffer, initialOffset, true);
|
toffset = tbuffer.writeUInt32LE(i, toffset);
|
||||||
}
|
}
|
||||||
toHex() {
|
function writeUInt64(i) {
|
||||||
return this.toBuffer(undefined, undefined).toString('hex');
|
toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset);
|
||||||
}
|
}
|
||||||
setInputScript(index, scriptSig) {
|
function writeVarInt(i) {
|
||||||
typeforce(types.tuple(types.Number, types.Buffer), arguments);
|
varuint.encode(i, tbuffer, toffset);
|
||||||
this.ins[index].script = scriptSig;
|
toffset += varuint.encode.bytes;
|
||||||
}
|
}
|
||||||
setWitness(index, witness) {
|
function writeVarSlice(slice) {
|
||||||
typeforce(types.tuple(types.Number, [types.Buffer]), arguments);
|
writeVarInt(slice.length);
|
||||||
this.ins[index].witness = witness;
|
writeSlice(slice);
|
||||||
}
|
}
|
||||||
__byteLength(_ALLOW_WITNESS) {
|
let hashOutputs = ZERO;
|
||||||
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
|
let hashPrevouts = ZERO;
|
||||||
return ((hasWitnesses ? 10 : 8) +
|
let hashSequence = ZERO;
|
||||||
varuint.encodingLength(this.ins.length) +
|
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
|
||||||
varuint.encodingLength(this.outs.length) +
|
tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
|
||||||
this.ins.reduce((sum, input) => {
|
toffset = 0;
|
||||||
return sum + 40 + varSliceSize(input.script);
|
this.ins.forEach(txIn => {
|
||||||
}, 0) +
|
writeSlice(txIn.hash);
|
||||||
this.outs.reduce((sum, output) => {
|
writeUInt32(txIn.index);
|
||||||
return sum + 8 + varSliceSize(output.script);
|
});
|
||||||
}, 0) +
|
hashPrevouts = bcrypto.hash256(tbuffer);
|
||||||
(hasWitnesses
|
|
||||||
? this.ins.reduce((sum, input) => {
|
|
||||||
return sum + vectorSize(input.witness);
|
|
||||||
}, 0)
|
|
||||||
: 0));
|
|
||||||
}
|
}
|
||||||
__toBuffer(buffer, initialOffset, _ALLOW_WITNESS) {
|
if (
|
||||||
if (!buffer)
|
!(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
|
||||||
buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS));
|
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
||||||
let offset = initialOffset || 0;
|
(hashType & 0x1f) !== Transaction.SIGHASH_NONE
|
||||||
function writeSlice(slice) {
|
) {
|
||||||
offset += slice.copy(buffer, offset);
|
tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
|
||||||
}
|
toffset = 0;
|
||||||
function writeUInt8(i) {
|
this.ins.forEach(txIn => {
|
||||||
offset = buffer.writeUInt8(i, offset);
|
writeUInt32(txIn.sequence);
|
||||||
}
|
});
|
||||||
function writeUInt32(i) {
|
hashSequence = bcrypto.hash256(tbuffer);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
28
src/types.js
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…
Add table
Reference in a new issue