Merge pull request #1343 from d-yokoi/prettier
replace standard with prettier and apply format
This commit is contained in:
commit
7b8d8a1c4f
51 changed files with 2573 additions and 2207 deletions
0
.prettierignore
Normal file
0
.prettierignore
Normal file
4
.prettierrc.json
Normal file
4
.prettierrc.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ node_js:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- node_js: "lts/*"
|
- node_js: "lts/*"
|
||||||
env: TEST_SUITE=standard
|
env: TEST_SUITE=format:ci
|
||||||
- node_js: "lts/*"
|
- node_js: "lts/*"
|
||||||
env: TEST_SUITE=coverage
|
env: TEST_SUITE=coverage
|
||||||
env:
|
env:
|
||||||
|
|
17
package.json
17
package.json
|
@ -19,16 +19,17 @@
|
||||||
"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:ci": "npm run prettier -- --check",
|
||||||
"integration": "npm run build && npm run nobuild:integration",
|
"integration": "npm run build && npm run nobuild:integration",
|
||||||
"nobuild:coverage-report": "nyc report --reporter=lcov",
|
"nobuild:coverage-report": "nyc report --reporter=lcov",
|
||||||
"nobuild:coverage-html": "nyc report --reporter=html",
|
"nobuild:coverage-html": "nyc report --reporter=html",
|
||||||
"nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha",
|
"nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha",
|
||||||
"nobuild:integration": "mocha --timeout 50000 test/integration/",
|
"nobuild:integration": "mocha --timeout 50000 test/integration/",
|
||||||
"nobuild:standard": "standard ts_src/**/*.ts",
|
|
||||||
"nobuild:unit": "mocha",
|
"nobuild:unit": "mocha",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
"standard": "npm run build && npm run nobuild:standard",
|
"prettier": "prettier ts_src/*.ts ts_src/**/*.ts --ignore-path ./.prettierignore",
|
||||||
"test": "npm run build && npm run nobuild:standard && npm run nobuild:coverage",
|
"test": "npm run build && npm run format:ci && npm run nobuild:coverage",
|
||||||
"unit": "npm run build && npm run nobuild:unit"
|
"unit": "npm run build && npm run nobuild:unit"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -68,16 +69,10 @@
|
||||||
"minimaldata": "^1.0.2",
|
"minimaldata": "^1.0.2",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"nyc": "^11.8.0",
|
"nyc": "^11.8.0",
|
||||||
|
"prettier": "^1.16.4",
|
||||||
"proxyquire": "^2.0.1",
|
"proxyquire": "^2.0.1",
|
||||||
"standard": "^11.0.1",
|
|
||||||
"typescript": "3.2.2",
|
"typescript": "3.2.2",
|
||||||
"typescript-eslint-parser": "^21.0.2"
|
"typescript-eslint-parser": "^21.0.2"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"standard": {
|
|
||||||
"parser": "typescript-eslint-parser",
|
|
||||||
"plugins": [
|
|
||||||
"typescript"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ function fromBech32(address) {
|
||||||
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;
|
||||||
|
@ -44,6 +44,7 @@ function toBech32(data, version, prefix) {
|
||||||
}
|
}
|
||||||
exports.toBech32 = toBech32;
|
exports.toBech32 = toBech32;
|
||||||
function fromOutputScript(output, network) {
|
function fromOutputScript(output, 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;
|
||||||
|
|
21
src/block.js
21
src/block.js
|
@ -10,22 +10,22 @@ const varuint = require('varuint-bitcoin');
|
||||||
const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions');
|
const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions');
|
||||||
const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block');
|
const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block');
|
||||||
function txesHaveWitnessCommit(transactions) {
|
function txesHaveWitnessCommit(transactions) {
|
||||||
return transactions instanceof Array &&
|
return (transactions instanceof Array &&
|
||||||
transactions[0] &&
|
transactions[0] &&
|
||||||
transactions[0].ins &&
|
transactions[0].ins &&
|
||||||
transactions[0].ins instanceof Array &&
|
transactions[0].ins instanceof Array &&
|
||||||
transactions[0].ins[0] &&
|
transactions[0].ins[0] &&
|
||||||
transactions[0].ins[0].witness &&
|
transactions[0].ins[0].witness &&
|
||||||
transactions[0].ins[0].witness instanceof Array &&
|
transactions[0].ins[0].witness instanceof Array &&
|
||||||
transactions[0].ins[0].witness.length > 0;
|
transactions[0].ins[0].witness.length > 0);
|
||||||
}
|
}
|
||||||
function anyTxHasWitness(transactions) {
|
function anyTxHasWitness(transactions) {
|
||||||
return transactions instanceof Array &&
|
return (transactions instanceof Array &&
|
||||||
transactions.some(tx => typeof tx === 'object' &&
|
transactions.some(tx => typeof tx === 'object' &&
|
||||||
tx.ins instanceof Array &&
|
tx.ins instanceof Array &&
|
||||||
tx.ins.some(input => typeof input === 'object' &&
|
tx.ins.some(input => typeof input === 'object' &&
|
||||||
input.witness instanceof Array &&
|
input.witness instanceof Array &&
|
||||||
input.witness.length > 0));
|
input.witness.length > 0)));
|
||||||
}
|
}
|
||||||
class Block {
|
class Block {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -116,9 +116,7 @@ class Block {
|
||||||
// There is no rule for the index of the output, so use filter to find it.
|
// 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
|
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
|
||||||
// If multiple commits are found, the output with highest index is assumed.
|
// If multiple commits are found, the output with highest index is assumed.
|
||||||
let witnessCommits = this.transactions[0].outs
|
let witnessCommits = this.transactions[0].outs.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))).map(out => out.script.slice(6, 38));
|
||||||
.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')))
|
|
||||||
.map(out => out.script.slice(6, 38));
|
|
||||||
if (witnessCommits.length === 0)
|
if (witnessCommits.length === 0)
|
||||||
return null;
|
return null;
|
||||||
// Use the commit with the highest output (should only be one though)
|
// Use the commit with the highest output (should only be one though)
|
||||||
|
@ -141,8 +139,9 @@ class Block {
|
||||||
byteLength(headersOnly) {
|
byteLength(headersOnly) {
|
||||||
if (headersOnly || !this.transactions)
|
if (headersOnly || !this.transactions)
|
||||||
return 80;
|
return 80;
|
||||||
return 80 + varuint.encodingLength(this.transactions.length) +
|
return (80 +
|
||||||
this.transactions.reduce((a, x) => a + x.byteLength(), 0);
|
varuint.encodingLength(this.transactions.length) +
|
||||||
|
this.transactions.reduce((a, x) => a + x.byteLength(), 0));
|
||||||
}
|
}
|
||||||
getHash() {
|
getHash() {
|
||||||
return bcrypto.hash256(this.toBuffer(true));
|
return bcrypto.hash256(this.toBuffer(true));
|
||||||
|
@ -197,8 +196,8 @@ class Block {
|
||||||
let hasWitnessCommit = this.hasWitnessCommit();
|
let hasWitnessCommit = this.hasWitnessCommit();
|
||||||
if (!hasWitnessCommit && this.hasWitness())
|
if (!hasWitnessCommit && this.hasWitness())
|
||||||
return false;
|
return false;
|
||||||
return this.__checkMerkleRoot() &&
|
return (this.__checkMerkleRoot() &&
|
||||||
(hasWitnessCommit ? this.__checkWitnessCommit() : true);
|
(hasWitnessCommit ? this.__checkWitnessCommit() : true));
|
||||||
}
|
}
|
||||||
checkMerkleRoot() {
|
checkMerkleRoot() {
|
||||||
console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' +
|
console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' +
|
||||||
|
|
|
@ -18,7 +18,7 @@ const types = {
|
||||||
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) {
|
||||||
|
|
|
@ -2,15 +2,21 @@
|
||||||
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) {
|
||||||
return createHash('rmd160').update(buffer).digest();
|
return createHash('rmd160')
|
||||||
|
.update(buffer)
|
||||||
|
.digest();
|
||||||
}
|
}
|
||||||
exports.ripemd160 = ripemd160;
|
exports.ripemd160 = ripemd160;
|
||||||
function sha1(buffer) {
|
function sha1(buffer) {
|
||||||
return createHash('sha1').update(buffer).digest();
|
return createHash('sha1')
|
||||||
|
.update(buffer)
|
||||||
|
.digest();
|
||||||
}
|
}
|
||||||
exports.sha1 = sha1;
|
exports.sha1 = sha1;
|
||||||
function sha256(buffer) {
|
function sha256(buffer) {
|
||||||
return createHash('sha256').update(buffer).digest();
|
return createHash('sha256')
|
||||||
|
.update(buffer)
|
||||||
|
.digest();
|
||||||
}
|
}
|
||||||
exports.sha256 = sha256;
|
exports.sha256 = sha256;
|
||||||
function hash160(buffer) {
|
function hash160(buffer) {
|
||||||
|
|
|
@ -8,13 +8,14 @@ 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) {
|
||||||
if (options === undefined)
|
if (options === undefined)
|
||||||
options = {};
|
options = {};
|
||||||
this.compressed = options.compressed === undefined ? true : options.compressed;
|
this.compressed =
|
||||||
|
options.compressed === undefined ? true : options.compressed;
|
||||||
this.network = options.network || NETWORKS.bitcoin;
|
this.network = options.network || NETWORKS.bitcoin;
|
||||||
this.__d = undefined;
|
this.__d = undefined;
|
||||||
this.__Q = undefined;
|
this.__Q = undefined;
|
||||||
|
@ -64,9 +65,11 @@ function fromWIF(string, network) {
|
||||||
const version = decoded.version;
|
const version = decoded.version;
|
||||||
// list of networks?
|
// list of networks?
|
||||||
if (types.Array(network)) {
|
if (types.Array(network)) {
|
||||||
network = network.filter(function (x) {
|
network = network
|
||||||
|
.filter(function (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)
|
||||||
|
@ -78,7 +81,7 @@ function fromWIF(string, network) {
|
||||||
}
|
}
|
||||||
return fromPrivateKey(decoded.privateKey, {
|
return fromPrivateKey(decoded.privateKey, {
|
||||||
compressed: decoded.compressed,
|
compressed: decoded.compressed,
|
||||||
network: network
|
network: network,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.fromWIF = fromWIF;
|
exports.fromWIF = fromWIF;
|
||||||
|
|
|
@ -5,31 +5,31 @@ exports.bitcoin = {
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,14 +14,13 @@ function stacksEqual(a, b) {
|
||||||
}
|
}
|
||||||
// output: OP_RETURN ...
|
// output: OP_RETURN ...
|
||||||
function p2data(a, opts) {
|
function p2data(a, opts) {
|
||||||
if (!a.data &&
|
if (!a.data && !a.output)
|
||||||
!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);
|
}, a);
|
||||||
const network = a.network || networks_1.bitcoin;
|
const network = a.network || networks_1.bitcoin;
|
||||||
const o = { network };
|
const o = { network };
|
||||||
|
|
|
@ -14,9 +14,9 @@ function prop(object, name, f) {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
value: value,
|
value: value,
|
||||||
writable: true
|
writable: true,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.prop = prop;
|
exports.prop = prop;
|
||||||
|
|
|
@ -24,9 +24,8 @@ function p2ms(a, opts) {
|
||||||
throw new TypeError('Not enough data');
|
throw new TypeError('Not enough data');
|
||||||
opts = Object.assign({ validate: true }, opts || {});
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
function isAcceptableSignature(x) {
|
function isAcceptableSignature(x) {
|
||||||
return bscript.isCanonicalScriptSignature(x) ||
|
return (bscript.isCanonicalScriptSignature(x) ||
|
||||||
(opts.allowIncomplete &&
|
(opts.allowIncomplete && x === OPS.OP_0) !== undefined);
|
||||||
(x === OPS.OP_0)) !== undefined; // eslint-disable-line
|
|
||||||
}
|
}
|
||||||
typef({
|
typef({
|
||||||
network: typef.maybe(typef.Object),
|
network: typef.maybe(typef.Object),
|
||||||
|
@ -35,7 +34,7 @@ function p2ms(a, opts) {
|
||||||
output: typef.maybe(typef.Buffer),
|
output: typef.maybe(typef.Buffer),
|
||||||
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)),
|
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)),
|
||||||
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
|
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
|
||||||
input: typef.maybe(typef.Buffer)
|
input: typef.maybe(typef.Buffer),
|
||||||
}, a);
|
}, a);
|
||||||
const network = a.network || networks_1.bitcoin;
|
const network = a.network || networks_1.bitcoin;
|
||||||
const o = { network };
|
const o = { network };
|
||||||
|
@ -46,8 +45,8 @@ function p2ms(a, opts) {
|
||||||
return;
|
return;
|
||||||
decoded = true;
|
decoded = true;
|
||||||
chunks = bscript.decompile(output);
|
chunks = bscript.decompile(output);
|
||||||
o.m = chunks[0] - OP_INT_BASE; // eslint-disable-line
|
o.m = chunks[0] - OP_INT_BASE;
|
||||||
o.n = chunks[chunks.length - 2] - OP_INT_BASE; // eslint-disable-line
|
o.n = chunks[chunks.length - 2] - OP_INT_BASE;
|
||||||
o.pubkeys = chunks.slice(1, -2);
|
o.pubkeys = chunks.slice(1, -2);
|
||||||
}
|
}
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function () {
|
||||||
|
@ -101,10 +100,7 @@ function p2ms(a, opts) {
|
||||||
throw new TypeError('Output is invalid');
|
throw new TypeError('Output is invalid');
|
||||||
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
|
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
|
||||||
throw new TypeError('Output is invalid');
|
throw new TypeError('Output is invalid');
|
||||||
if (o.m <= 0 || // eslint-disable-line
|
if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3)
|
||||||
o.n > 16 || // eslint-disable-line
|
|
||||||
o.m > o.n || // eslint-disable-line
|
|
||||||
o.n !== chunks.length - 3)
|
|
||||||
throw new TypeError('Output is invalid');
|
throw new TypeError('Output is invalid');
|
||||||
if (!o.pubkeys.every(x => ecc.isPoint(x)))
|
if (!o.pubkeys.every(x => ecc.isPoint(x)))
|
||||||
throw new TypeError('Output is invalid');
|
throw new TypeError('Output is invalid');
|
||||||
|
@ -131,7 +127,8 @@ function p2ms(a, opts) {
|
||||||
if (a.input) {
|
if (a.input) {
|
||||||
if (a.input[0] !== OPS.OP_0)
|
if (a.input[0] !== OPS.OP_0)
|
||||||
throw new TypeError('Input is invalid');
|
throw new TypeError('Input is invalid');
|
||||||
if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature))
|
if (o.signatures.length === 0 ||
|
||||||
|
!o.signatures.every(isAcceptableSignature))
|
||||||
throw new TypeError('Input has invalid signature(s)');
|
throw new TypeError('Input has invalid signature(s)');
|
||||||
if (a.signatures && !stacksEqual(a.signatures, o.signatures))
|
if (a.signatures && !stacksEqual(a.signatures, o.signatures))
|
||||||
throw new TypeError('Signature mismatch');
|
throw new TypeError('Signature mismatch');
|
||||||
|
|
|
@ -9,11 +9,7 @@ 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 &&
|
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
|
||||||
!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({
|
||||||
|
@ -21,18 +17,17 @@ function p2pk(a, opts) {
|
||||||
output: typef.maybe(typef.Buffer),
|
output: typef.maybe(typef.Buffer),
|
||||||
pubkey: typef.maybe(ecc.isPoint),
|
pubkey: typef.maybe(ecc.isPoint),
|
||||||
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
||||||
input: typef.maybe(typef.Buffer)
|
input: typef.maybe(typef.Buffer),
|
||||||
}, a);
|
}, a);
|
||||||
const _chunks = lazy.value(function () { return bscript.decompile(a.input); });
|
const _chunks = lazy.value(function () {
|
||||||
|
return bscript.decompile(a.input);
|
||||||
|
});
|
||||||
const network = a.network || networks_1.bitcoin;
|
const network = a.network || networks_1.bitcoin;
|
||||||
const o = { network };
|
const o = { network };
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function () {
|
||||||
if (!a.pubkey)
|
if (!a.pubkey)
|
||||||
return;
|
return;
|
||||||
return bscript.compile([
|
return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]);
|
||||||
a.pubkey,
|
|
||||||
OPS.OP_CHECKSIG
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
lazy.prop(o, 'pubkey', function () {
|
lazy.prop(o, 'pubkey', function () {
|
||||||
if (!a.output)
|
if (!a.output)
|
||||||
|
|
|
@ -11,11 +11,7 @@ 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 &&
|
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
|
||||||
!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({
|
||||||
|
@ -25,7 +21,7 @@ function p2pkh(a, opts) {
|
||||||
output: typef.maybe(typef.BufferN(25)),
|
output: typef.maybe(typef.BufferN(25)),
|
||||||
pubkey: typef.maybe(ecc.isPoint),
|
pubkey: typef.maybe(ecc.isPoint),
|
||||||
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
||||||
input: typef.maybe(typef.Buffer)
|
input: typef.maybe(typef.Buffer),
|
||||||
}, a);
|
}, a);
|
||||||
const _address = lazy.value(function () {
|
const _address = lazy.value(function () {
|
||||||
const payload = bs58check.decode(a.address);
|
const payload = bs58check.decode(a.address);
|
||||||
|
@ -33,7 +29,9 @@ function p2pkh(a, opts) {
|
||||||
const hash = payload.slice(1);
|
const hash = payload.slice(1);
|
||||||
return { version, hash };
|
return { version, hash };
|
||||||
});
|
});
|
||||||
const _chunks = lazy.value(function () { return bscript.decompile(a.input); });
|
const _chunks = lazy.value(function () {
|
||||||
|
return bscript.decompile(a.input);
|
||||||
|
});
|
||||||
const network = a.network || networks_1.bitcoin;
|
const network = a.network || networks_1.bitcoin;
|
||||||
const o = { network };
|
const o = { network };
|
||||||
lazy.prop(o, 'address', function () {
|
lazy.prop(o, 'address', function () {
|
||||||
|
@ -60,7 +58,7 @@ function p2pkh(a, opts) {
|
||||||
OPS.OP_HASH160,
|
OPS.OP_HASH160,
|
||||||
o.hash,
|
o.hash,
|
||||||
OPS.OP_EQUALVERIFY,
|
OPS.OP_EQUALVERIFY,
|
||||||
OPS.OP_CHECKSIG
|
OPS.OP_CHECKSIG,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
lazy.prop(o, 'pubkey', function () {
|
lazy.prop(o, 'pubkey', function () {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const networks_1 = require("../networks"); // eslint-disable-line
|
const networks_1 = require("../networks");
|
||||||
const bscript = require("../script");
|
const bscript = require("../script");
|
||||||
const bcrypto = require("../crypto");
|
const bcrypto = require("../crypto");
|
||||||
const lazy = require("./lazy");
|
const lazy = require("./lazy");
|
||||||
|
@ -18,11 +18,7 @@ function stacksEqual(a, b) {
|
||||||
// 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 &&
|
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input)
|
||||||
!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({
|
||||||
|
@ -34,10 +30,10 @@ function p2sh(a, opts) {
|
||||||
network: typef.maybe(typef.Object),
|
network: typef.maybe(typef.Object),
|
||||||
output: typef.maybe(typef.Buffer),
|
output: typef.maybe(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)),
|
||||||
}),
|
}),
|
||||||
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);
|
}, a);
|
||||||
let network = a.network;
|
let network = a.network;
|
||||||
if (!network) {
|
if (!network) {
|
||||||
|
@ -50,14 +46,16 @@ function p2sh(a, opts) {
|
||||||
const hash = payload.slice(1);
|
const hash = payload.slice(1);
|
||||||
return { version, hash };
|
return { version, hash };
|
||||||
});
|
});
|
||||||
const _chunks = lazy.value(function () { return bscript.decompile(a.input); });
|
const _chunks = lazy.value(function () {
|
||||||
|
return bscript.decompile(a.input);
|
||||||
|
});
|
||||||
const _redeem = lazy.value(function () {
|
const _redeem = lazy.value(function () {
|
||||||
const chunks = _chunks();
|
const chunks = _chunks();
|
||||||
return {
|
return {
|
||||||
network,
|
network,
|
||||||
output: chunks[chunks.length - 1],
|
output: chunks[chunks.length - 1],
|
||||||
input: bscript.compile(chunks.slice(0, -1)),
|
input: bscript.compile(chunks.slice(0, -1)),
|
||||||
witness: a.witness || []
|
witness: a.witness || [],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// output dependents
|
// output dependents
|
||||||
|
@ -81,11 +79,7 @@ function p2sh(a, opts) {
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function () {
|
||||||
if (!o.hash)
|
if (!o.hash)
|
||||||
return;
|
return;
|
||||||
return bscript.compile([
|
return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]);
|
||||||
OPS.OP_HASH160,
|
|
||||||
o.hash,
|
|
||||||
OPS.OP_EQUAL
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
// input dependents
|
// input dependents
|
||||||
lazy.prop(o, 'redeem', function () {
|
lazy.prop(o, 'redeem', function () {
|
||||||
|
@ -153,7 +147,7 @@ function p2sh(a, opts) {
|
||||||
if (hasInput && hasWitness)
|
if (hasInput && hasWitness)
|
||||||
throw new TypeError('Input and witness provided');
|
throw new TypeError('Input and witness provided');
|
||||||
if (hasInput) {
|
if (hasInput) {
|
||||||
const richunks = bscript.decompile(redeem.input);
|
const richunks = (bscript.decompile(redeem.input));
|
||||||
if (!bscript.isPushOnly(richunks))
|
if (!bscript.isPushOnly(richunks))
|
||||||
throw new TypeError('Non push-only scriptSig');
|
throw new TypeError('Non push-only scriptSig');
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,7 @@ 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 &&
|
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
|
||||||
!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({
|
||||||
|
@ -28,7 +24,7 @@ function p2wpkh(a, opts) {
|
||||||
output: typef.maybe(typef.BufferN(22)),
|
output: typef.maybe(typef.BufferN(22)),
|
||||||
pubkey: typef.maybe(ecc.isPoint),
|
pubkey: typef.maybe(ecc.isPoint),
|
||||||
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
||||||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
}, a);
|
}, a);
|
||||||
const _address = lazy.value(function () {
|
const _address = lazy.value(function () {
|
||||||
const result = bech32.decode(a.address);
|
const result = bech32.decode(a.address);
|
||||||
|
@ -37,7 +33,7 @@ function p2wpkh(a, opts) {
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
prefix: result.prefix,
|
prefix: result.prefix,
|
||||||
data: Buffer.from(data)
|
data: Buffer.from(data),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const network = a.network || networks_1.bitcoin;
|
const network = a.network || networks_1.bitcoin;
|
||||||
|
@ -60,10 +56,7 @@ function p2wpkh(a, opts) {
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function () {
|
||||||
if (!o.hash)
|
if (!o.hash)
|
||||||
return;
|
return;
|
||||||
return bscript.compile([
|
return bscript.compile([OPS.OP_0, o.hash]);
|
||||||
OPS.OP_0,
|
|
||||||
o.hash
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
lazy.prop(o, 'pubkey', function () {
|
lazy.prop(o, 'pubkey', function () {
|
||||||
if (a.pubkey)
|
if (a.pubkey)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const networks_1 = require("../networks"); // eslint-disable-line
|
const networks_1 = require("../networks");
|
||||||
const bscript = require("../script");
|
const bscript = require("../script");
|
||||||
const bcrypto = require("../crypto");
|
const bcrypto = require("../crypto");
|
||||||
const lazy = require("./lazy");
|
const lazy = require("./lazy");
|
||||||
|
@ -19,11 +19,7 @@ function stacksEqual(a, b) {
|
||||||
// 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 &&
|
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
|
||||||
!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({
|
||||||
|
@ -35,10 +31,10 @@ function p2wsh(a, opts) {
|
||||||
input: typef.maybe(typef.Buffer),
|
input: typef.maybe(typef.Buffer),
|
||||||
network: typef.maybe(typef.Object),
|
network: typef.maybe(typef.Object),
|
||||||
output: typef.maybe(typef.Buffer),
|
output: typef.maybe(typef.Buffer),
|
||||||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
}),
|
}),
|
||||||
input: typef.maybe(typef.BufferN(0)),
|
input: typef.maybe(typef.BufferN(0)),
|
||||||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
}, a);
|
}, a);
|
||||||
const _address = lazy.value(function () {
|
const _address = lazy.value(function () {
|
||||||
const result = bech32.decode(a.address);
|
const result = bech32.decode(a.address);
|
||||||
|
@ -47,10 +43,12 @@ function p2wsh(a, opts) {
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
prefix: result.prefix,
|
prefix: result.prefix,
|
||||||
data: Buffer.from(data)
|
data: Buffer.from(data),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input); });
|
const _rchunks = lazy.value(function () {
|
||||||
|
return bscript.decompile(a.redeem.input);
|
||||||
|
});
|
||||||
let network = a.network;
|
let network = a.network;
|
||||||
if (!network) {
|
if (!network) {
|
||||||
network = (a.redeem && a.redeem.network) || networks_1.bitcoin;
|
network = (a.redeem && a.redeem.network) || networks_1.bitcoin;
|
||||||
|
@ -74,10 +72,7 @@ function p2wsh(a, opts) {
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function () {
|
||||||
if (!o.hash)
|
if (!o.hash)
|
||||||
return;
|
return;
|
||||||
return bscript.compile([
|
return bscript.compile([OPS.OP_0, o.hash]);
|
||||||
OPS.OP_0,
|
|
||||||
o.hash
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
lazy.prop(o, 'redeem', function () {
|
lazy.prop(o, 'redeem', function () {
|
||||||
if (!a.witness)
|
if (!a.witness)
|
||||||
|
@ -85,7 +80,7 @@ function p2wsh(a, opts) {
|
||||||
return {
|
return {
|
||||||
output: a.witness[a.witness.length - 1],
|
output: a.witness[a.witness.length - 1],
|
||||||
input: EMPTY_BUFFER,
|
input: EMPTY_BUFFER,
|
||||||
witness: a.witness.slice(0, -1)
|
witness: a.witness.slice(0, -1),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
lazy.prop(o, 'input', function () {
|
lazy.prop(o, 'input', function () {
|
||||||
|
@ -165,11 +160,15 @@ function p2wsh(a, opts) {
|
||||||
}
|
}
|
||||||
if (a.redeem.input && !bscript.isPushOnly(_rchunks()))
|
if (a.redeem.input && !bscript.isPushOnly(_rchunks()))
|
||||||
throw new TypeError('Non push-only scriptSig');
|
throw new TypeError('Non push-only scriptSig');
|
||||||
if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness))
|
if (a.witness &&
|
||||||
|
a.redeem.witness &&
|
||||||
|
!stacksEqual(a.witness, a.redeem.witness))
|
||||||
throw new TypeError('Witness and redeem.witness mismatch');
|
throw new TypeError('Witness and redeem.witness mismatch');
|
||||||
}
|
}
|
||||||
if (a.witness) {
|
if (a.witness) {
|
||||||
if (a.redeem && a.redeem.output && !a.redeem.output.equals(a.witness[a.witness.length - 1]))
|
if (a.redeem &&
|
||||||
|
a.redeem.output &&
|
||||||
|
!a.redeem.output.equals(a.witness[a.witness.length - 1]))
|
||||||
throw new TypeError('Witness and redeem.output mismatch');
|
throw new TypeError('Witness and redeem.output mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,10 @@ 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 (types.Number(value) &&
|
||||||
((value === exports.OPS.OP_0) ||
|
(value === exports.OPS.OP_0 ||
|
||||||
(value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) ||
|
(value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) ||
|
||||||
(value === exports.OPS.OP_1NEGATE));
|
value === exports.OPS.OP_1NEGATE));
|
||||||
}
|
}
|
||||||
function isPushOnlyChunk(value) {
|
function isPushOnlyChunk(value) {
|
||||||
return types.Buffer(value) || isOPInt(value);
|
return types.Buffer(value) || isOPInt(value);
|
||||||
|
@ -96,7 +96,7 @@ function decompile(buffer) {
|
||||||
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)
|
if (d === null)
|
||||||
|
@ -129,7 +129,8 @@ function toASM(chunks) {
|
||||||
if (chunksIsBuffer(chunks)) {
|
if (chunksIsBuffer(chunks)) {
|
||||||
chunks = decompile(chunks);
|
chunks = decompile(chunks);
|
||||||
}
|
}
|
||||||
return chunks.map(function (chunk) {
|
return chunks
|
||||||
|
.map(function (chunk) {
|
||||||
// data?
|
// data?
|
||||||
if (singleChunkIsBuffer(chunk)) {
|
if (singleChunkIsBuffer(chunk)) {
|
||||||
const op = asMinimalOP(chunk);
|
const op = asMinimalOP(chunk);
|
||||||
|
@ -139,7 +140,8 @@ function toASM(chunks) {
|
||||||
}
|
}
|
||||||
// opcode!
|
// opcode!
|
||||||
return REVERSE_OPS[chunk];
|
return REVERSE_OPS[chunk];
|
||||||
}).join(' ');
|
})
|
||||||
|
.join(' ');
|
||||||
}
|
}
|
||||||
exports.toASM = toASM;
|
exports.toASM = toASM;
|
||||||
function fromASM(asm) {
|
function fromASM(asm) {
|
||||||
|
|
|
@ -19,8 +19,8 @@ function decode(buffer, maxLength, minimal) {
|
||||||
const a = buffer.readUInt32LE(0);
|
const a = buffer.readUInt32LE(0);
|
||||||
const b = buffer.readUInt8(4);
|
const b = buffer.readUInt8(4);
|
||||||
if (b & 0x80)
|
if (b & 0x80)
|
||||||
return -(((b & ~0x80) * 0x100000000) + a);
|
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;
|
||||||
|
@ -33,11 +33,16 @@ function decode(buffer, maxLength, minimal) {
|
||||||
}
|
}
|
||||||
exports.decode = decode;
|
exports.decode = decode;
|
||||||
function scriptNumSize(i) {
|
function scriptNumSize(i) {
|
||||||
return i > 0x7fffffff ? 5
|
return i > 0x7fffffff
|
||||||
: i > 0x7fffff ? 4
|
? 5
|
||||||
: i > 0x7fff ? 3
|
: i > 0x7fffff
|
||||||
: i > 0x7f ? 2
|
? 4
|
||||||
: i > 0x00 ? 1
|
: i > 0x7fff
|
||||||
|
? 3
|
||||||
|
: i > 0x7f
|
||||||
|
? 2
|
||||||
|
: i > 0x00
|
||||||
|
? 1
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
function encode(number) {
|
function encode(number) {
|
||||||
|
|
|
@ -34,14 +34,14 @@ function decode(buffer) {
|
||||||
const s = fromDER(decode.s);
|
const s = fromDER(decode.s);
|
||||||
return {
|
return {
|
||||||
signature: Buffer.concat([r, s], 64),
|
signature: Buffer.concat([r, s], 64),
|
||||||
hashType: hashType
|
hashType: hashType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
exports.decode = decode;
|
exports.decode = decode;
|
||||||
function encode(signature, hashType) {
|
function encode(signature, hashType) {
|
||||||
typeforce({
|
typeforce({
|
||||||
signature: types.BufferN(64),
|
signature: types.BufferN(64),
|
||||||
hashType: types.UInt8
|
hashType: types.UInt8,
|
||||||
}, { signature, hashType });
|
}, { signature, hashType });
|
||||||
const hashTypeMod = hashType & ~0x80;
|
const hashTypeMod = hashType & ~0x80;
|
||||||
if (hashTypeMod <= 0 || hashTypeMod >= 4)
|
if (hashTypeMod <= 0 || hashTypeMod >= 4)
|
||||||
|
@ -50,9 +50,6 @@ function encode(signature, hashType) {
|
||||||
hashTypeBuffer.writeUInt8(hashType, 0);
|
hashTypeBuffer.writeUInt8(hashType, 0);
|
||||||
const r = toDER(signature.slice(0, 32));
|
const r = toDER(signature.slice(0, 32));
|
||||||
const s = toDER(signature.slice(32, 64));
|
const s = toDER(signature.slice(32, 64));
|
||||||
return Buffer.concat([
|
return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]);
|
||||||
bip66.encode(r, s),
|
|
||||||
hashTypeBuffer
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
exports.encode = encode;
|
exports.encode = encode;
|
||||||
|
|
|
@ -5,10 +5,11 @@ 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 &&
|
return buffer.length > 1 && buffer[0] === OPS.OP_RETURN;
|
||||||
buffer[0] === OPS.OP_RETURN;
|
|
||||||
}
|
}
|
||||||
exports.check = check;
|
exports.check = check;
|
||||||
check.toJSON = function () { return 'null data output'; };
|
check.toJSON = function () {
|
||||||
|
return 'null data output';
|
||||||
|
};
|
||||||
const output = { check };
|
const output = { check };
|
||||||
exports.output = output;
|
exports.output = output;
|
||||||
|
|
|
@ -14,9 +14,10 @@ function varSliceSize(someScript) {
|
||||||
}
|
}
|
||||||
function vectorSize(someVector) {
|
function vectorSize(someVector) {
|
||||||
const length = someVector.length;
|
const length = someVector.length;
|
||||||
return varuint.encodingLength(length) + someVector.reduce((sum, witness) => {
|
return (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 = [];
|
||||||
|
@ -25,7 +26,7 @@ const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000
|
||||||
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;
|
||||||
|
@ -90,14 +91,14 @@ class Transaction {
|
||||||
index: readUInt32(),
|
index: readUInt32(),
|
||||||
script: readVarSlice(),
|
script: readVarSlice(),
|
||||||
sequence: readUInt32(),
|
sequence: readUInt32(),
|
||||||
witness: EMPTY_WITNESS
|
witness: EMPTY_WITNESS,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const voutLen = readVarInt();
|
const voutLen = readVarInt();
|
||||||
for (i = 0; i < voutLen; ++i) {
|
for (i = 0; i < voutLen; ++i) {
|
||||||
tx.outs.push({
|
tx.outs.push({
|
||||||
value: readUInt64(),
|
value: readUInt64(),
|
||||||
script: readVarSlice()
|
script: readVarSlice(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (hasWitnesses) {
|
if (hasWitnesses) {
|
||||||
|
@ -127,7 +128,7 @@ class Transaction {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
isCoinbase() {
|
isCoinbase() {
|
||||||
return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash);
|
return (this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash));
|
||||||
}
|
}
|
||||||
addInput(hash, index, sequence, scriptSig) {
|
addInput(hash, index, sequence, scriptSig) {
|
||||||
typeforce(types.tuple(types.Hash256bit, types.UInt32, types.maybe(types.UInt32), types.maybe(types.Buffer)), arguments);
|
typeforce(types.tuple(types.Hash256bit, types.UInt32, types.maybe(types.UInt32), types.maybe(types.Buffer)), arguments);
|
||||||
|
@ -140,7 +141,7 @@ class Transaction {
|
||||||
index: index,
|
index: index,
|
||||||
script: scriptSig || EMPTY_SCRIPT,
|
script: scriptSig || EMPTY_SCRIPT,
|
||||||
sequence: sequence,
|
sequence: sequence,
|
||||||
witness: EMPTY_WITNESS
|
witness: EMPTY_WITNESS,
|
||||||
}) - 1);
|
}) - 1);
|
||||||
}
|
}
|
||||||
addOutput(scriptPubKey, value) {
|
addOutput(scriptPubKey, value) {
|
||||||
|
@ -148,11 +149,11 @@ class Transaction {
|
||||||
// Add the output and return the output's index
|
// Add the output and return the output's index
|
||||||
return (this.outs.push({
|
return (this.outs.push({
|
||||||
script: scriptPubKey,
|
script: scriptPubKey,
|
||||||
value: value
|
value: value,
|
||||||
}) - 1);
|
}) - 1);
|
||||||
}
|
}
|
||||||
hasWitnesses() {
|
hasWitnesses() {
|
||||||
return this.ins.some((x) => {
|
return this.ins.some(x => {
|
||||||
return x.witness.length !== 0;
|
return x.witness.length !== 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -178,27 +179,29 @@ class Transaction {
|
||||||
this.outs.reduce((sum, output) => {
|
this.outs.reduce((sum, output) => {
|
||||||
return sum + 8 + varSliceSize(output.script);
|
return sum + 8 + varSliceSize(output.script);
|
||||||
}, 0) +
|
}, 0) +
|
||||||
(hasWitnesses ? this.ins.reduce((sum, input) => {
|
(hasWitnesses
|
||||||
return sum + vectorSize(input.witness);
|
? this.ins.reduce((sum, input) => {
|
||||||
}, 0) : 0));
|
return sum + vectorSize(input.witness);
|
||||||
|
}, 0)
|
||||||
|
: 0));
|
||||||
}
|
}
|
||||||
clone() {
|
clone() {
|
||||||
const newTx = new Transaction();
|
const newTx = new Transaction();
|
||||||
newTx.version = this.version;
|
newTx.version = this.version;
|
||||||
newTx.locktime = this.locktime;
|
newTx.locktime = this.locktime;
|
||||||
newTx.ins = this.ins.map((txIn) => {
|
newTx.ins = this.ins.map(txIn => {
|
||||||
return {
|
return {
|
||||||
hash: txIn.hash,
|
hash: txIn.hash,
|
||||||
index: txIn.index,
|
index: txIn.index,
|
||||||
script: txIn.script,
|
script: txIn.script,
|
||||||
sequence: txIn.sequence,
|
sequence: txIn.sequence,
|
||||||
witness: txIn.witness
|
witness: txIn.witness,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
newTx.outs = this.outs.map((txOut) => {
|
newTx.outs = this.outs.map(txOut => {
|
||||||
return {
|
return {
|
||||||
script: txOut.script,
|
script: txOut.script,
|
||||||
value: txOut.value
|
value: txOut.value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return newTx;
|
return newTx;
|
||||||
|
@ -217,7 +220,7 @@ class Transaction {
|
||||||
if (inIndex >= this.ins.length)
|
if (inIndex >= this.ins.length)
|
||||||
return ONE;
|
return ONE;
|
||||||
// ignore OP_CODESEPARATOR
|
// ignore OP_CODESEPARATOR
|
||||||
const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => {
|
const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(x => {
|
||||||
return x !== script_1.OPS.OP_CODESEPARATOR;
|
return x !== script_1.OPS.OP_CODESEPARATOR;
|
||||||
}));
|
}));
|
||||||
const txTmp = this.clone();
|
const txTmp = this.clone();
|
||||||
|
@ -257,7 +260,7 @@ class Transaction {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// "blank" others input scripts
|
// "blank" others input scripts
|
||||||
txTmp.ins.forEach((input) => {
|
txTmp.ins.forEach(input => {
|
||||||
input.script = EMPTY_SCRIPT;
|
input.script = EMPTY_SCRIPT;
|
||||||
});
|
});
|
||||||
txTmp.ins[inIndex].script = ourScript;
|
txTmp.ins[inIndex].script = ourScript;
|
||||||
|
@ -295,7 +298,7 @@ class Transaction {
|
||||||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
|
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
|
||||||
tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
|
tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
|
||||||
toffset = 0;
|
toffset = 0;
|
||||||
this.ins.forEach((txIn) => {
|
this.ins.forEach(txIn => {
|
||||||
writeSlice(txIn.hash);
|
writeSlice(txIn.hash);
|
||||||
writeUInt32(txIn.index);
|
writeUInt32(txIn.index);
|
||||||
});
|
});
|
||||||
|
@ -306,7 +309,7 @@ class Transaction {
|
||||||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
|
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
|
||||||
tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
|
tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
|
||||||
toffset = 0;
|
toffset = 0;
|
||||||
this.ins.forEach((txIn) => {
|
this.ins.forEach(txIn => {
|
||||||
writeUInt32(txIn.sequence);
|
writeUInt32(txIn.sequence);
|
||||||
});
|
});
|
||||||
hashSequence = bcrypto.hash256(tbuffer);
|
hashSequence = bcrypto.hash256(tbuffer);
|
||||||
|
@ -318,13 +321,14 @@ class Transaction {
|
||||||
}, 0);
|
}, 0);
|
||||||
tbuffer = Buffer.allocUnsafe(txOutsSize);
|
tbuffer = Buffer.allocUnsafe(txOutsSize);
|
||||||
toffset = 0;
|
toffset = 0;
|
||||||
this.outs.forEach((out) => {
|
this.outs.forEach(out => {
|
||||||
writeUInt64(out.value);
|
writeUInt64(out.value);
|
||||||
writeVarSlice(out.script);
|
writeVarSlice(out.script);
|
||||||
});
|
});
|
||||||
hashOutputs = bcrypto.hash256(tbuffer);
|
hashOutputs = bcrypto.hash256(tbuffer);
|
||||||
}
|
}
|
||||||
else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) {
|
else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE &&
|
||||||
|
inIndex < this.outs.length) {
|
||||||
const output = this.outs[inIndex];
|
const output = this.outs[inIndex];
|
||||||
tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
|
tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
|
||||||
toffset = 0;
|
toffset = 0;
|
||||||
|
@ -369,13 +373,13 @@ class Transaction {
|
||||||
offset += slice.copy(buffer, offset);
|
offset += slice.copy(buffer, offset);
|
||||||
}
|
}
|
||||||
function writeUInt8(i) {
|
function writeUInt8(i) {
|
||||||
offset = (buffer).writeUInt8(i, offset);
|
offset = buffer.writeUInt8(i, offset);
|
||||||
}
|
}
|
||||||
function writeUInt32(i) {
|
function writeUInt32(i) {
|
||||||
offset = (buffer).writeUInt32LE(i, offset);
|
offset = buffer.writeUInt32LE(i, offset);
|
||||||
}
|
}
|
||||||
function writeInt32(i) {
|
function writeInt32(i) {
|
||||||
offset = (buffer).writeInt32LE(i, offset);
|
offset = buffer.writeInt32LE(i, offset);
|
||||||
}
|
}
|
||||||
function writeUInt64(i) {
|
function writeUInt64(i) {
|
||||||
offset = bufferutils.writeUInt64LE(buffer, i, offset);
|
offset = bufferutils.writeUInt64LE(buffer, i, offset);
|
||||||
|
@ -399,14 +403,14 @@ class Transaction {
|
||||||
writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
|
writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
|
||||||
}
|
}
|
||||||
writeVarInt(this.ins.length);
|
writeVarInt(this.ins.length);
|
||||||
this.ins.forEach((txIn) => {
|
this.ins.forEach(txIn => {
|
||||||
writeSlice(txIn.hash);
|
writeSlice(txIn.hash);
|
||||||
writeUInt32(txIn.index);
|
writeUInt32(txIn.index);
|
||||||
writeVarSlice(txIn.script);
|
writeVarSlice(txIn.script);
|
||||||
writeUInt32(txIn.sequence);
|
writeUInt32(txIn.sequence);
|
||||||
});
|
});
|
||||||
writeVarInt(this.outs.length);
|
writeVarInt(this.outs.length);
|
||||||
this.outs.forEach((txOut) => {
|
this.outs.forEach(txOut => {
|
||||||
if (isOutput(txOut)) {
|
if (isOutput(txOut)) {
|
||||||
writeUInt64(txOut.value);
|
writeUInt64(txOut.value);
|
||||||
}
|
}
|
||||||
|
@ -416,7 +420,7 @@ class Transaction {
|
||||||
writeVarSlice(txOut.script);
|
writeVarSlice(txOut.script);
|
||||||
});
|
});
|
||||||
if (hasWitnesses) {
|
if (hasWitnesses) {
|
||||||
this.ins.forEach((input) => {
|
this.ins.forEach(input => {
|
||||||
writeVector(input.witness);
|
writeVector(input.witness);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class TransactionBuilder {
|
||||||
txb.__addInputUnsafe(txIn.hash, txIn.index, {
|
txb.__addInputUnsafe(txIn.hash, txIn.index, {
|
||||||
sequence: txIn.sequence,
|
sequence: txIn.sequence,
|
||||||
script: txIn.script,
|
script: txIn.script,
|
||||||
witness: txIn.witness
|
witness: txIn.witness,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// fix some things not possible through the public API
|
// fix some things not possible through the public API
|
||||||
|
@ -89,7 +89,7 @@ class TransactionBuilder {
|
||||||
return this.__addInputUnsafe(txHash, vout, {
|
return this.__addInputUnsafe(txHash, vout, {
|
||||||
sequence: sequence,
|
sequence: sequence,
|
||||||
prevOutScript: prevOutScript,
|
prevOutScript: prevOutScript,
|
||||||
value: value
|
value: value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
__addInputUnsafe(txHash, vout, options) {
|
__addInputUnsafe(txHash, vout, options) {
|
||||||
|
@ -194,7 +194,7 @@ class TransactionBuilder {
|
||||||
if (!canSign(input)) {
|
if (!canSign(input)) {
|
||||||
if (witnessValue !== undefined) {
|
if (witnessValue !== undefined) {
|
||||||
if (input.value !== undefined && input.value !== witnessValue)
|
if (input.value !== undefined && input.value !== witnessValue)
|
||||||
throw new Error('Input didn\'t match witnessValue');
|
throw new Error("Input didn't match witnessValue");
|
||||||
typeforce(types.Satoshi, witnessValue);
|
typeforce(types.Satoshi, witnessValue);
|
||||||
input.value = witnessValue;
|
input.value = witnessValue;
|
||||||
}
|
}
|
||||||
|
@ -251,18 +251,19 @@ class TransactionBuilder {
|
||||||
}
|
}
|
||||||
// if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs
|
// if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs
|
||||||
// .build() will fail, but .buildIncomplete() is OK
|
// .build() will fail, but .buildIncomplete() is OK
|
||||||
return (this.__tx.outs.length === 0) && this.__inputs.some((input) => {
|
return (this.__tx.outs.length === 0 &&
|
||||||
if (!input.signatures)
|
this.__inputs.some(input => {
|
||||||
return false;
|
if (!input.signatures)
|
||||||
return input.signatures.some((signature) => {
|
return false;
|
||||||
if (!signature)
|
return input.signatures.some(signature => {
|
||||||
return false; // no signature, no issue
|
if (!signature)
|
||||||
const hashType = signatureHashType(signature);
|
return false; // no signature, no issue
|
||||||
if (hashType & transaction_1.Transaction.SIGHASH_NONE)
|
const hashType = signatureHashType(signature);
|
||||||
return false; // SIGHASH_NONE doesn't care about outputs
|
if (hashType & transaction_1.Transaction.SIGHASH_NONE)
|
||||||
return true; // SIGHASH_* does care
|
return false; // SIGHASH_NONE doesn't care about outputs
|
||||||
});
|
return true; // SIGHASH_* does care
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
__canModifyOutputs() {
|
__canModifyOutputs() {
|
||||||
const nInputs = this.__tx.ins.length;
|
const nInputs = this.__tx.ins.length;
|
||||||
|
@ -313,21 +314,25 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) {
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SCRIPT_TYPES.P2WPKH: {
|
case SCRIPT_TYPES.P2WPKH: {
|
||||||
const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack });
|
const { output, pubkey, signature } = payments.p2wpkh({
|
||||||
|
witness: witnessStack,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
prevOutScript: output,
|
prevOutScript: output,
|
||||||
prevOutType: SCRIPT_TYPES.P2WPKH,
|
prevOutType: SCRIPT_TYPES.P2WPKH,
|
||||||
pubkeys: [pubkey],
|
pubkeys: [pubkey],
|
||||||
signatures: [signature]
|
signatures: [signature],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2PKH: {
|
case SCRIPT_TYPES.P2PKH: {
|
||||||
const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig });
|
const { output, pubkey, signature } = payments.p2pkh({
|
||||||
|
input: scriptSig,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
prevOutScript: output,
|
prevOutScript: output,
|
||||||
prevOutType: SCRIPT_TYPES.P2PKH,
|
prevOutType: SCRIPT_TYPES.P2PKH,
|
||||||
pubkeys: [pubkey],
|
pubkeys: [pubkey],
|
||||||
signatures: [signature]
|
signatures: [signature],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2PK: {
|
case SCRIPT_TYPES.P2PK: {
|
||||||
|
@ -335,26 +340,26 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) {
|
||||||
return {
|
return {
|
||||||
prevOutType: SCRIPT_TYPES.P2PK,
|
prevOutType: SCRIPT_TYPES.P2PK,
|
||||||
pubkeys: [undefined],
|
pubkeys: [undefined],
|
||||||
signatures: [signature]
|
signatures: [signature],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2MS: {
|
case SCRIPT_TYPES.P2MS: {
|
||||||
const { m, pubkeys, signatures } = payments.p2ms({
|
const { m, pubkeys, signatures } = payments.p2ms({
|
||||||
input: scriptSig,
|
input: scriptSig,
|
||||||
output: scriptPubKey
|
output: scriptPubKey,
|
||||||
}, { allowIncomplete: true });
|
}, { allowIncomplete: true });
|
||||||
return {
|
return {
|
||||||
prevOutType: SCRIPT_TYPES.P2MS,
|
prevOutType: SCRIPT_TYPES.P2MS,
|
||||||
pubkeys: pubkeys,
|
pubkeys: pubkeys,
|
||||||
signatures: signatures,
|
signatures: signatures,
|
||||||
maxSignatures: m
|
maxSignatures: m,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type === SCRIPT_TYPES.P2SH) {
|
if (type === SCRIPT_TYPES.P2SH) {
|
||||||
const { output, redeem } = payments.p2sh({
|
const { output, redeem } = payments.p2sh({
|
||||||
input: scriptSig,
|
input: scriptSig,
|
||||||
witness: witnessStack
|
witness: witnessStack,
|
||||||
});
|
});
|
||||||
const outputType = classify.output(redeem.output);
|
const outputType = classify.output(redeem.output);
|
||||||
const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output);
|
const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output);
|
||||||
|
@ -368,13 +373,13 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) {
|
||||||
witnessScript: expanded.witnessScript,
|
witnessScript: expanded.witnessScript,
|
||||||
witnessScriptType: expanded.witnessScriptType,
|
witnessScriptType: expanded.witnessScriptType,
|
||||||
pubkeys: expanded.pubkeys,
|
pubkeys: expanded.pubkeys,
|
||||||
signatures: expanded.signatures
|
signatures: expanded.signatures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (type === SCRIPT_TYPES.P2WSH) {
|
if (type === SCRIPT_TYPES.P2WSH) {
|
||||||
const { output, redeem } = payments.p2wsh({
|
const { output, redeem } = payments.p2wsh({
|
||||||
input: scriptSig,
|
input: scriptSig,
|
||||||
witness: witnessStack
|
witness: witnessStack,
|
||||||
});
|
});
|
||||||
const outputType = classify.output(redeem.output);
|
const outputType = classify.output(redeem.output);
|
||||||
let expanded;
|
let expanded;
|
||||||
|
@ -392,12 +397,12 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) {
|
||||||
witnessScript: redeem.output,
|
witnessScript: redeem.output,
|
||||||
witnessScriptType: expanded.prevOutType,
|
witnessScriptType: expanded.prevOutType,
|
||||||
pubkeys: expanded.pubkeys,
|
pubkeys: expanded.pubkeys,
|
||||||
signatures: expanded.signatures
|
signatures: expanded.signatures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
prevOutType: SCRIPT_TYPES.NONSTANDARD,
|
prevOutType: SCRIPT_TYPES.NONSTANDARD,
|
||||||
prevOutScript: scriptSig
|
prevOutScript: scriptSig,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// could be done in expandInput, but requires the original Transaction for hashForSignature
|
// could be done in expandInput, but requires the original Transaction for hashForSignature
|
||||||
|
@ -444,7 +449,7 @@ function expandOutput(script, ourPubKey) {
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
pubkeys: [ourPubKey],
|
pubkeys: [ourPubKey],
|
||||||
signatures: [undefined]
|
signatures: [undefined],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2WPKH: {
|
case SCRIPT_TYPES.P2WPKH: {
|
||||||
|
@ -458,7 +463,7 @@ function expandOutput(script, ourPubKey) {
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
pubkeys: [ourPubKey],
|
pubkeys: [ourPubKey],
|
||||||
signatures: [undefined]
|
signatures: [undefined],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2PK: {
|
case SCRIPT_TYPES.P2PK: {
|
||||||
|
@ -466,7 +471,7 @@ function expandOutput(script, ourPubKey) {
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
pubkeys: [p2pk.pubkey],
|
pubkeys: [p2pk.pubkey],
|
||||||
signatures: [undefined]
|
signatures: [undefined],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2MS: {
|
case SCRIPT_TYPES.P2MS: {
|
||||||
|
@ -475,7 +480,7 @@ function expandOutput(script, ourPubKey) {
|
||||||
type,
|
type,
|
||||||
pubkeys: p2ms.pubkeys,
|
pubkeys: p2ms.pubkeys,
|
||||||
signatures: p2ms.pubkeys.map(() => undefined),
|
signatures: p2ms.pubkeys.map(() => undefined),
|
||||||
maxSignatures: p2ms.m
|
maxSignatures: p2ms.m,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,7 +488,7 @@ function expandOutput(script, ourPubKey) {
|
||||||
}
|
}
|
||||||
function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
if (redeemScript && witnessScript) {
|
if (redeemScript && witnessScript) {
|
||||||
const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } });
|
const p2wsh = (payments.p2wsh({ redeem: { output: witnessScript } }));
|
||||||
const p2wshAlt = payments.p2wsh({ output: redeemScript });
|
const p2wshAlt = payments.p2wsh({ output: redeemScript });
|
||||||
const p2sh = payments.p2sh({ redeem: { output: redeemScript } });
|
const p2sh = payments.p2sh({ redeem: { output: redeemScript } });
|
||||||
const p2shAlt = payments.p2sh({ redeem: p2wsh });
|
const p2shAlt = payments.p2sh({ redeem: p2wsh });
|
||||||
|
@ -494,7 +499,10 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
throw new Error('Redeem script inconsistent with prevOutScript');
|
throw new Error('Redeem script inconsistent with prevOutScript');
|
||||||
const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
|
const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
|
||||||
if (!expanded.pubkeys)
|
if (!expanded.pubkeys)
|
||||||
throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')');
|
throw new Error(expanded.type +
|
||||||
|
' not supported as witnessScript (' +
|
||||||
|
bscript.toASM(witnessScript) +
|
||||||
|
')');
|
||||||
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
||||||
expanded.signatures = input.signatures;
|
expanded.signatures = input.signatures;
|
||||||
}
|
}
|
||||||
|
@ -513,7 +521,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
signType: expanded.type,
|
signType: expanded.type,
|
||||||
pubkeys: expanded.pubkeys,
|
pubkeys: expanded.pubkeys,
|
||||||
signatures: expanded.signatures,
|
signatures: expanded.signatures,
|
||||||
maxSignatures: expanded.maxSignatures
|
maxSignatures: expanded.maxSignatures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (redeemScript) {
|
if (redeemScript) {
|
||||||
|
@ -531,7 +539,10 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
}
|
}
|
||||||
const expanded = expandOutput(p2sh.redeem.output, ourPubKey);
|
const expanded = expandOutput(p2sh.redeem.output, ourPubKey);
|
||||||
if (!expanded.pubkeys)
|
if (!expanded.pubkeys)
|
||||||
throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')');
|
throw new Error(expanded.type +
|
||||||
|
' not supported as redeemScript (' +
|
||||||
|
bscript.toASM(redeemScript) +
|
||||||
|
')');
|
||||||
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
||||||
expanded.signatures = input.signatures;
|
expanded.signatures = input.signatures;
|
||||||
}
|
}
|
||||||
|
@ -549,7 +560,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
signType: expanded.type,
|
signType: expanded.type,
|
||||||
pubkeys: expanded.pubkeys,
|
pubkeys: expanded.pubkeys,
|
||||||
signatures: expanded.signatures,
|
signatures: expanded.signatures,
|
||||||
maxSignatures: expanded.maxSignatures
|
maxSignatures: expanded.maxSignatures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (witnessScript) {
|
if (witnessScript) {
|
||||||
|
@ -561,7 +572,10 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
}
|
}
|
||||||
const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
|
const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
|
||||||
if (!expanded.pubkeys)
|
if (!expanded.pubkeys)
|
||||||
throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')');
|
throw new Error(expanded.type +
|
||||||
|
' not supported as witnessScript (' +
|
||||||
|
bscript.toASM(witnessScript) +
|
||||||
|
')');
|
||||||
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
||||||
expanded.signatures = input.signatures;
|
expanded.signatures = input.signatures;
|
||||||
}
|
}
|
||||||
|
@ -578,7 +592,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
signType: expanded.type,
|
signType: expanded.type,
|
||||||
pubkeys: expanded.pubkeys,
|
pubkeys: expanded.pubkeys,
|
||||||
signatures: expanded.signatures,
|
signatures: expanded.signatures,
|
||||||
maxSignatures: expanded.maxSignatures
|
maxSignatures: expanded.maxSignatures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (input.prevOutType && input.prevOutScript) {
|
if (input.prevOutType && input.prevOutScript) {
|
||||||
|
@ -591,13 +605,16 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
throw new Error('PrevOutScript is missing');
|
throw new Error('PrevOutScript is missing');
|
||||||
const expanded = expandOutput(input.prevOutScript, ourPubKey);
|
const expanded = expandOutput(input.prevOutScript, ourPubKey);
|
||||||
if (!expanded.pubkeys)
|
if (!expanded.pubkeys)
|
||||||
throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')');
|
throw new Error(expanded.type +
|
||||||
|
' not supported (' +
|
||||||
|
bscript.toASM(input.prevOutScript) +
|
||||||
|
')');
|
||||||
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
||||||
expanded.signatures = input.signatures;
|
expanded.signatures = input.signatures;
|
||||||
}
|
}
|
||||||
let signScript = input.prevOutScript;
|
let signScript = input.prevOutScript;
|
||||||
if (expanded.type === SCRIPT_TYPES.P2WPKH) {
|
if (expanded.type === SCRIPT_TYPES.P2WPKH) {
|
||||||
signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output;
|
signScript = (payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
prevOutType: expanded.type,
|
prevOutType: expanded.type,
|
||||||
|
@ -607,7 +624,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
signType: expanded.type,
|
signType: expanded.type,
|
||||||
pubkeys: expanded.pubkeys,
|
pubkeys: expanded.pubkeys,
|
||||||
signatures: expanded.signatures,
|
signatures: expanded.signatures,
|
||||||
maxSignatures: expanded.maxSignatures
|
maxSignatures: expanded.maxSignatures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output;
|
const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output;
|
||||||
|
@ -618,7 +635,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
||||||
signScript: prevOutScript,
|
signScript: prevOutScript,
|
||||||
signType: SCRIPT_TYPES.P2PKH,
|
signType: SCRIPT_TYPES.P2PKH,
|
||||||
pubkeys: [ourPubKey],
|
pubkeys: [ourPubKey],
|
||||||
signatures: [undefined]
|
signatures: [undefined],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function build(type, input, allowIncomplete) {
|
function build(type, input, allowIncomplete) {
|
||||||
|
@ -656,7 +673,7 @@ function build(type, input, allowIncomplete) {
|
||||||
}
|
}
|
||||||
// if the transaction is not not complete (complete), or if signatures.length === m, validate
|
// if the transaction is not not complete (complete), or if signatures.length === m, validate
|
||||||
// otherwise, the number of OP_0's may be >= m, so don't validate (boo)
|
// otherwise, the number of OP_0's may be >= m, so don't validate (boo)
|
||||||
const validate = !allowIncomplete || (m === signatures.length);
|
const validate = !allowIncomplete || m === signatures.length;
|
||||||
return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate });
|
return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate });
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2SH: {
|
case SCRIPT_TYPES.P2SH: {
|
||||||
|
@ -667,8 +684,8 @@ function build(type, input, allowIncomplete) {
|
||||||
redeem: {
|
redeem: {
|
||||||
output: redeem.output || input.redeemScript,
|
output: redeem.output || input.redeemScript,
|
||||||
input: redeem.input,
|
input: redeem.input,
|
||||||
witness: redeem.witness
|
witness: redeem.witness,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case SCRIPT_TYPES.P2WSH: {
|
case SCRIPT_TYPES.P2WSH: {
|
||||||
|
@ -679,21 +696,20 @@ function build(type, input, allowIncomplete) {
|
||||||
redeem: {
|
redeem: {
|
||||||
output: input.witnessScript,
|
output: input.witnessScript,
|
||||||
input: redeem.input,
|
input: redeem.input,
|
||||||
witness: redeem.witness
|
witness: redeem.witness,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function canSign(input) {
|
function canSign(input) {
|
||||||
return input.signScript !== undefined &&
|
return (input.signScript !== undefined &&
|
||||||
input.signType !== undefined &&
|
input.signType !== undefined &&
|
||||||
input.pubkeys !== undefined &&
|
input.pubkeys !== undefined &&
|
||||||
input.signatures !== undefined &&
|
input.signatures !== undefined &&
|
||||||
input.signatures.length === input.pubkeys.length &&
|
input.signatures.length === input.pubkeys.length &&
|
||||||
input.pubkeys.length > 0 &&
|
input.pubkeys.length > 0 &&
|
||||||
(input.hasWitness === false ||
|
(input.hasWitness === false || input.value !== undefined));
|
||||||
input.value !== undefined);
|
|
||||||
}
|
}
|
||||||
function signatureHashType(buffer) {
|
function signatureHashType(buffer) {
|
||||||
return buffer.readUInt8(buffer.length - 1);
|
return buffer.readUInt8(buffer.length - 1);
|
||||||
|
|
|
@ -10,7 +10,9 @@ 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 = function () { return 'BIP32 derivation path'; };
|
BIP32Path.toJSON = function () {
|
||||||
|
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;
|
||||||
|
@ -23,11 +25,11 @@ 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);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable no-new */
|
|
||||||
|
|
||||||
const { describe, it, beforeEach } = require('mocha')
|
const { describe, it, beforeEach } = require('mocha')
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const proxyquire = require('proxyquire')
|
const proxyquire = require('proxyquire')
|
||||||
|
|
|
@ -1,101 +1,119 @@
|
||||||
import { Network } from './networks'
|
import { Network } from './networks';
|
||||||
import * as types from './types'
|
import * as types from './types';
|
||||||
import * as bscript from './script'
|
import * as bscript from './script';
|
||||||
import * as networks from './networks'
|
import * as networks from './networks';
|
||||||
import * as payments from './payments'
|
import * as payments from './payments';
|
||||||
|
|
||||||
const bech32 = require('bech32')
|
const bech32 = require('bech32');
|
||||||
const bs58check = require('bs58check')
|
const bs58check = require('bs58check');
|
||||||
const typeforce = require('typeforce')
|
const typeforce = require('typeforce');
|
||||||
|
|
||||||
export type Base58CheckResult = {
|
export type Base58CheckResult = {
|
||||||
hash: Buffer;
|
hash: Buffer;
|
||||||
version: number;
|
version: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Bech32Result = {
|
export type Bech32Result = {
|
||||||
version: number;
|
version: number;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
data: Buffer;
|
data: Buffer;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function fromBase58Check (address: string): Base58CheckResult {
|
export function fromBase58Check(address: string): Base58CheckResult {
|
||||||
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) throw new TypeError(address + ' is too short')
|
if (payload.length < 21) throw new TypeError(address + ' is too short');
|
||||||
if (payload.length > 21) throw new TypeError(address + ' is too long')
|
if (payload.length > 21) throw new TypeError(address + ' is too long');
|
||||||
|
|
||||||
const version = payload.readUInt8(0)
|
const version = payload.readUInt8(0);
|
||||||
const hash = payload.slice(1)
|
const hash = payload.slice(1);
|
||||||
|
|
||||||
return { version: version, hash: hash }
|
return { version: version, hash: hash };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromBech32 (address: string): Bech32Result {
|
export function fromBech32(address: string): Bech32Result {
|
||||||
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),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toBase58Check (hash: Buffer, version: number): string {
|
export function toBase58Check(hash: Buffer, version: number): string {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toBech32 (data: Buffer, version: number, prefix: string): string {
|
export function toBech32(
|
||||||
const words = bech32.toWords(data)
|
data: Buffer,
|
||||||
words.unshift(version)
|
version: number,
|
||||||
|
prefix: string,
|
||||||
|
): string {
|
||||||
|
const words = bech32.toWords(data);
|
||||||
|
words.unshift(version);
|
||||||
|
|
||||||
return bech32.encode(prefix, words)
|
return bech32.encode(prefix, words);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromOutputScript (output: Buffer, network: Network): string { //TODO: Network
|
export function fromOutputScript(output: Buffer, network: Network): string {
|
||||||
network = network || networks.bitcoin
|
//TODO: Network
|
||||||
|
network = network || networks.bitcoin;
|
||||||
|
|
||||||
try { return <string>payments.p2pkh({ output, network }).address } catch (e) {}
|
|
||||||
try { return <string>payments.p2sh({ output, network }).address } catch (e) {}
|
|
||||||
try { return <string>payments.p2wpkh({ output, network }).address } catch (e) {}
|
|
||||||
try { return <string>payments.p2wsh({ output, network }).address } catch (e) {}
|
|
||||||
|
|
||||||
throw new Error(bscript.toASM(output) + ' has no matching Address')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toOutputScript (address: string, network: Network): Buffer {
|
|
||||||
network = network || networks.bitcoin
|
|
||||||
|
|
||||||
let decodeBase58: Base58CheckResult | undefined = undefined
|
|
||||||
let decodeBech32: Bech32Result | undefined = undefined
|
|
||||||
try {
|
try {
|
||||||
decodeBase58 = fromBase58Check(address)
|
return <string>payments.p2pkh({ output, network }).address;
|
||||||
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
return <string>payments.p2sh({ output, network }).address;
|
||||||
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
return <string>payments.p2wpkh({ output, network }).address;
|
||||||
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
return <string>payments.p2wsh({ output, network }).address;
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
throw new Error(bscript.toASM(output) + ' has no matching Address');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toOutputScript(address: string, network: Network): Buffer {
|
||||||
|
network = network || networks.bitcoin;
|
||||||
|
|
||||||
|
let decodeBase58: Base58CheckResult | undefined = undefined;
|
||||||
|
let decodeBech32: Bech32Result | undefined = undefined;
|
||||||
|
try {
|
||||||
|
decodeBase58 = fromBase58Check(address);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (decodeBase58) {
|
if (decodeBase58) {
|
||||||
if (decodeBase58.version === network.pubKeyHash) return <Buffer>payments.p2pkh({ hash: decodeBase58.hash }).output
|
if (decodeBase58.version === network.pubKeyHash)
|
||||||
if (decodeBase58.version === network.scriptHash) return <Buffer>payments.p2sh({ hash: decodeBase58.hash }).output
|
return <Buffer>payments.p2pkh({ hash: decodeBase58.hash }).output;
|
||||||
|
if (decodeBase58.version === network.scriptHash)
|
||||||
|
return <Buffer>payments.p2sh({ hash: decodeBase58.hash }).output;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
decodeBech32 = fromBech32(address)
|
decodeBech32 = fromBech32(address);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (decodeBech32) {
|
if (decodeBech32) {
|
||||||
if (decodeBech32.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix')
|
if (decodeBech32.prefix !== network.bech32)
|
||||||
|
throw new Error(address + ' has an invalid prefix');
|
||||||
if (decodeBech32.version === 0) {
|
if (decodeBech32.version === 0) {
|
||||||
if (decodeBech32.data.length === 20) return <Buffer>payments.p2wpkh({ hash: decodeBech32.data }).output
|
if (decodeBech32.data.length === 20)
|
||||||
if (decodeBech32.data.length === 32) return <Buffer>payments.p2wsh({ hash: decodeBech32.data }).output
|
return <Buffer>payments.p2wpkh({ hash: decodeBech32.data }).output;
|
||||||
|
if (decodeBech32.data.length === 32)
|
||||||
|
return <Buffer>payments.p2wsh({ hash: decodeBech32.data }).output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(address + ' has no matching Script')
|
throw new Error(address + ' has no matching Script');
|
||||||
}
|
}
|
||||||
|
|
365
ts_src/block.ts
365
ts_src/block.ts
|
@ -1,17 +1,22 @@
|
||||||
import { Transaction } from './transaction'
|
import { Transaction } from './transaction';
|
||||||
import * as types from './types'
|
import * as types from './types';
|
||||||
import * as bcrypto from './crypto'
|
import * as bcrypto from './crypto';
|
||||||
import { reverseBuffer } from './bufferutils'
|
import { reverseBuffer } from './bufferutils';
|
||||||
|
|
||||||
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',
|
||||||
|
);
|
||||||
|
|
||||||
function txesHaveWitnessCommit (transactions: Array<Transaction>): boolean {
|
function txesHaveWitnessCommit(transactions: Array<Transaction>): boolean {
|
||||||
return transactions instanceof Array &&
|
return (
|
||||||
|
transactions instanceof Array &&
|
||||||
transactions[0] &&
|
transactions[0] &&
|
||||||
transactions[0].ins &&
|
transactions[0].ins &&
|
||||||
transactions[0].ins instanceof Array &&
|
transactions[0].ins instanceof Array &&
|
||||||
|
@ -19,255 +24,281 @@ function txesHaveWitnessCommit (transactions: Array<Transaction>): boolean {
|
||||||
transactions[0].ins[0].witness &&
|
transactions[0].ins[0].witness &&
|
||||||
transactions[0].ins[0].witness instanceof Array &&
|
transactions[0].ins[0].witness instanceof Array &&
|
||||||
transactions[0].ins[0].witness.length > 0
|
transactions[0].ins[0].witness.length > 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function anyTxHasWitness (transactions: Array<Transaction>): boolean {
|
function anyTxHasWitness(transactions: Array<Transaction>): boolean {
|
||||||
return transactions instanceof Array &&
|
return (
|
||||||
transactions.some(tx =>
|
transactions instanceof Array &&
|
||||||
typeof tx === 'object' &&
|
transactions.some(
|
||||||
tx.ins instanceof Array &&
|
tx =>
|
||||||
tx.ins.some(input =>
|
typeof tx === 'object' &&
|
||||||
typeof input === 'object' &&
|
tx.ins instanceof Array &&
|
||||||
input.witness instanceof Array &&
|
tx.ins.some(
|
||||||
input.witness.length > 0
|
input =>
|
||||||
)
|
typeof input === 'object' &&
|
||||||
|
input.witness instanceof Array &&
|
||||||
|
input.witness.length > 0,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Block {
|
export class Block {
|
||||||
version: number
|
version: number;
|
||||||
prevHash?: Buffer
|
prevHash?: Buffer;
|
||||||
merkleRoot?: Buffer
|
merkleRoot?: Buffer;
|
||||||
timestamp: number
|
timestamp: number;
|
||||||
witnessCommit?: Buffer
|
witnessCommit?: Buffer;
|
||||||
bits: number
|
bits: number;
|
||||||
nonce: number
|
nonce: number;
|
||||||
transactions?: Array<Transaction>
|
transactions?: Array<Transaction>;
|
||||||
|
|
||||||
constructor () {
|
constructor() {
|
||||||
this.version = 1
|
this.version = 1;
|
||||||
this.timestamp = 0
|
this.timestamp = 0;
|
||||||
this.bits = 0
|
this.bits = 0;
|
||||||
this.nonce = 0
|
this.nonce = 0;
|
||||||
this.prevHash = undefined
|
this.prevHash = undefined;
|
||||||
this.merkleRoot = undefined
|
this.merkleRoot = undefined;
|
||||||
this.witnessCommit = undefined
|
this.witnessCommit = undefined;
|
||||||
this.transactions = undefined
|
this.transactions = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBuffer (buffer: Buffer): Block {
|
static fromBuffer(buffer: Buffer): Block {
|
||||||
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)')
|
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
|
||||||
|
|
||||||
let offset: number = 0
|
let offset: number = 0;
|
||||||
const readSlice = (n: number): Buffer => {
|
const readSlice = (n: number): Buffer => {
|
||||||
offset += n
|
offset += n;
|
||||||
return buffer.slice(offset - n, offset)
|
return buffer.slice(offset - n, offset);
|
||||||
}
|
};
|
||||||
|
|
||||||
const readUInt32 = (): number => {
|
const readUInt32 = (): number => {
|
||||||
const i = buffer.readUInt32LE(offset)
|
const i = buffer.readUInt32LE(offset);
|
||||||
offset += 4
|
offset += 4;
|
||||||
return i
|
return i;
|
||||||
}
|
};
|
||||||
|
|
||||||
const readInt32 = (): number => {
|
const readInt32 = (): number => {
|
||||||
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) return block;
|
||||||
|
|
||||||
const readVarInt = (): number => {
|
const readVarInt = (): number => {
|
||||||
const vi = varuint.decode(buffer, offset)
|
const vi = varuint.decode(buffer, offset);
|
||||||
offset += varuint.decode.bytes
|
offset += varuint.decode.bytes;
|
||||||
return vi
|
return vi;
|
||||||
}
|
};
|
||||||
|
|
||||||
const readTransaction = (): any => {
|
const readTransaction = (): any => {
|
||||||
const tx = Transaction.fromBuffer(buffer.slice(offset), true)
|
const tx = Transaction.fromBuffer(buffer.slice(offset), true);
|
||||||
offset += tx.byteLength()
|
offset += tx.byteLength();
|
||||||
return tx
|
return tx;
|
||||||
}
|
};
|
||||||
|
|
||||||
const nTransactions = readVarInt()
|
const nTransactions = readVarInt();
|
||||||
block.transactions = []
|
block.transactions = [];
|
||||||
|
|
||||||
for (var i = 0; i < nTransactions; ++i) {
|
for (var i = 0; i < nTransactions; ++i) {
|
||||||
const tx = readTransaction()
|
const tx = readTransaction();
|
||||||
block.transactions.push(tx)
|
block.transactions.push(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let witnessCommit = block.getWitnessCommit()
|
let witnessCommit = block.getWitnessCommit();
|
||||||
// This Block contains a witness commit
|
// This Block contains a witness commit
|
||||||
if (witnessCommit) block.witnessCommit = witnessCommit
|
if (witnessCommit) block.witnessCommit = witnessCommit;
|
||||||
|
|
||||||
return block
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromHex (hex: string): Block {
|
static fromHex(hex: string): Block {
|
||||||
return Block.fromBuffer(Buffer.from(hex, 'hex'))
|
return Block.fromBuffer(Buffer.from(hex, 'hex'));
|
||||||
}
|
}
|
||||||
|
|
||||||
static calculateTarget (bits: number): Buffer {
|
static calculateTarget(bits: number): Buffer {
|
||||||
const exponent = ((bits & 0xff000000) >> 24) - 3
|
const exponent = ((bits & 0xff000000) >> 24) - 3;
|
||||||
const mantissa = bits & 0x007fffff
|
const mantissa = bits & 0x007fffff;
|
||||||
const target = Buffer.alloc(32, 0)
|
const target = Buffer.alloc(32, 0);
|
||||||
target.writeUIntBE(mantissa, 29 - exponent, 3)
|
target.writeUIntBE(mantissa, 29 - exponent, 3);
|
||||||
return target
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
static calculateMerkleRoot (transactions: Array<Transaction>, forWitness?: boolean): Buffer {
|
static calculateMerkleRoot(
|
||||||
typeforce([{ getHash: types.Function }], transactions)
|
transactions: Array<Transaction>,
|
||||||
if (transactions.length === 0) throw errorMerkleNoTxes
|
forWitness?: boolean,
|
||||||
if (forWitness && !txesHaveWitnessCommit(transactions)) throw errorWitnessNotSegwit
|
): Buffer {
|
||||||
|
typeforce([{ getHash: types.Function }], transactions);
|
||||||
|
if (transactions.length === 0) throw errorMerkleNoTxes;
|
||||||
|
if (forWitness && !txesHaveWitnessCommit(transactions))
|
||||||
|
throw errorWitnessNotSegwit;
|
||||||
|
|
||||||
const hashes = transactions.map(transaction => transaction.getHash(forWitness!))
|
const hashes = transactions.map(transaction =>
|
||||||
|
transaction.getHash(forWitness!),
|
||||||
|
);
|
||||||
|
|
||||||
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256)
|
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
|
||||||
|
|
||||||
return forWitness
|
return forWitness
|
||||||
? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]))
|
? bcrypto.hash256(
|
||||||
: rootHash
|
Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]),
|
||||||
|
)
|
||||||
|
: rootHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
getWitnessCommit (): Buffer | null {
|
getWitnessCommit(): Buffer | null {
|
||||||
if (!txesHaveWitnessCommit(this.transactions!)) return null
|
if (!txesHaveWitnessCommit(this.transactions!)) return null;
|
||||||
|
|
||||||
// The merkle root for the witness data is in an OP_RETURN output.
|
// 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.
|
// 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
|
// The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
|
||||||
// If multiple commits are found, the output with highest index is assumed.
|
// If multiple commits are found, the output with highest index is assumed.
|
||||||
let witnessCommits = this.transactions![0].outs
|
let witnessCommits = this.transactions![0].outs.filter(out =>
|
||||||
.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')))
|
out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
|
||||||
.map(out => out.script.slice(6, 38))
|
).map(out => out.script.slice(6, 38));
|
||||||
if (witnessCommits.length === 0) return null
|
if (witnessCommits.length === 0) return null;
|
||||||
// Use the commit with the highest output (should only be one though)
|
// Use the commit with the highest output (should only be one though)
|
||||||
let result = witnessCommits[witnessCommits.length - 1]
|
let result = witnessCommits[witnessCommits.length - 1];
|
||||||
|
|
||||||
if (!(result instanceof Buffer && result.length === 32)) return null
|
if (!(result instanceof Buffer && result.length === 32)) return null;
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasWitnessCommit (): boolean {
|
hasWitnessCommit(): boolean {
|
||||||
if (this.witnessCommit instanceof Buffer &&
|
if (
|
||||||
this.witnessCommit.length === 32) return true
|
this.witnessCommit instanceof Buffer &&
|
||||||
if (this.getWitnessCommit() !== null) return true
|
this.witnessCommit.length === 32
|
||||||
return false
|
)
|
||||||
|
return true;
|
||||||
|
if (this.getWitnessCommit() !== null) return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasWitness (): boolean {
|
hasWitness(): boolean {
|
||||||
return anyTxHasWitness(this.transactions!)
|
return anyTxHasWitness(this.transactions!);
|
||||||
}
|
}
|
||||||
|
|
||||||
byteLength (headersOnly: boolean): number {
|
byteLength(headersOnly: boolean): number {
|
||||||
if (headersOnly || !this.transactions) return 80
|
if (headersOnly || !this.transactions) return 80;
|
||||||
|
|
||||||
return 80 + varuint.encodingLength(this.transactions.length) +
|
return (
|
||||||
|
80 +
|
||||||
|
varuint.encodingLength(this.transactions.length) +
|
||||||
this.transactions.reduce((a, x) => a + x.byteLength(), 0)
|
this.transactions.reduce((a, x) => a + x.byteLength(), 0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHash (): Buffer {
|
getHash(): Buffer {
|
||||||
return bcrypto.hash256(this.toBuffer(true))
|
return bcrypto.hash256(this.toBuffer(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
getId (): string {
|
getId(): string {
|
||||||
return reverseBuffer(this.getHash()).toString('hex')
|
return reverseBuffer(this.getHash()).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
getUTCDate (): Date {
|
getUTCDate(): Date {
|
||||||
const date = new Date(0) // epoch
|
const date = new Date(0); // epoch
|
||||||
date.setUTCSeconds(this.timestamp)
|
date.setUTCSeconds(this.timestamp);
|
||||||
|
|
||||||
return date
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: buffer, offset compatibility
|
// TODO: buffer, offset compatibility
|
||||||
toBuffer (headersOnly: boolean): Buffer {
|
toBuffer(headersOnly: boolean): Buffer {
|
||||||
const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly))
|
const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
|
||||||
|
|
||||||
let offset: number = 0
|
let offset: number = 0;
|
||||||
const writeSlice = (slice: Buffer): void => {
|
const writeSlice = (slice: Buffer): void => {
|
||||||
slice.copy(buffer, offset)
|
slice.copy(buffer, offset);
|
||||||
offset += slice.length
|
offset += slice.length;
|
||||||
}
|
};
|
||||||
|
|
||||||
const writeInt32 = (i: number): void => {
|
const writeInt32 = (i: number): void => {
|
||||||
buffer.writeInt32LE(i, offset)
|
buffer.writeInt32LE(i, offset);
|
||||||
offset += 4
|
offset += 4;
|
||||||
}
|
};
|
||||||
const writeUInt32 = (i: number): void => {
|
const writeUInt32 = (i: number): void => {
|
||||||
buffer.writeUInt32LE(i, offset)
|
buffer.writeUInt32LE(i, offset);
|
||||||
offset += 4
|
offset += 4;
|
||||||
}
|
};
|
||||||
|
|
||||||
writeInt32(this.version)
|
writeInt32(this.version);
|
||||||
writeSlice(this.prevHash!)
|
writeSlice(this.prevHash!);
|
||||||
writeSlice(this.merkleRoot!)
|
writeSlice(this.merkleRoot!);
|
||||||
writeUInt32(this.timestamp)
|
writeUInt32(this.timestamp);
|
||||||
writeUInt32(this.bits)
|
writeUInt32(this.bits);
|
||||||
writeUInt32(this.nonce)
|
writeUInt32(this.nonce);
|
||||||
|
|
||||||
if (headersOnly || !this.transactions) return buffer
|
if (headersOnly || !this.transactions) return buffer;
|
||||||
|
|
||||||
varuint.encode(this.transactions.length, buffer, offset)
|
varuint.encode(this.transactions.length, buffer, offset);
|
||||||
offset += varuint.encode.bytes
|
offset += varuint.encode.bytes;
|
||||||
|
|
||||||
this.transactions.forEach(tx => {
|
this.transactions.forEach(tx => {
|
||||||
const txSize = tx.byteLength() // TODO: extract from toBuffer?
|
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
|
||||||
tx.toBuffer(buffer, offset)
|
tx.toBuffer(buffer, offset);
|
||||||
offset += txSize
|
offset += txSize;
|
||||||
})
|
});
|
||||||
|
|
||||||
return buffer
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex (headersOnly: boolean): string {
|
toHex(headersOnly: boolean): string {
|
||||||
return this.toBuffer(headersOnly).toString('hex')
|
return this.toBuffer(headersOnly).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTxRoots (): boolean {
|
checkTxRoots(): boolean {
|
||||||
// If the Block has segwit transactions but no witness commit,
|
// If the Block has segwit transactions but no witness commit,
|
||||||
// there's no way it can be valid, so fail the check.
|
// there's no way it can be valid, so fail the check.
|
||||||
let hasWitnessCommit = this.hasWitnessCommit()
|
let hasWitnessCommit = this.hasWitnessCommit();
|
||||||
if (!hasWitnessCommit && this.hasWitness()) return false
|
if (!hasWitnessCommit && this.hasWitness()) return false;
|
||||||
return this.__checkMerkleRoot() &&
|
return (
|
||||||
|
this.__checkMerkleRoot() &&
|
||||||
(hasWitnessCommit ? this.__checkWitnessCommit() : true)
|
(hasWitnessCommit ? this.__checkWitnessCommit() : true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkMerkleRoot (): boolean {
|
checkMerkleRoot(): boolean {
|
||||||
console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' +
|
console.warn(
|
||||||
'deprecated in v5. Please use checkTxRoots instead.')
|
'Deprecation Warning: Block method checkMerkleRoot will be ' +
|
||||||
return this.checkTxRoots()
|
'deprecated in v5. Please use checkTxRoots instead.',
|
||||||
|
);
|
||||||
|
return this.checkTxRoots();
|
||||||
}
|
}
|
||||||
|
|
||||||
__checkMerkleRoot (): boolean {
|
__checkMerkleRoot(): boolean {
|
||||||
if (!this.transactions) throw errorMerkleNoTxes
|
if (!this.transactions) throw errorMerkleNoTxes;
|
||||||
|
|
||||||
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions)
|
const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
|
||||||
return this.merkleRoot!.compare(actualMerkleRoot) === 0
|
return this.merkleRoot!.compare(actualMerkleRoot) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__checkWitnessCommit (): boolean {
|
__checkWitnessCommit(): boolean {
|
||||||
if (!this.transactions) throw errorMerkleNoTxes
|
if (!this.transactions) throw errorMerkleNoTxes;
|
||||||
if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit
|
if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit;
|
||||||
|
|
||||||
const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true)
|
const actualWitnessCommit = Block.calculateMerkleRoot(
|
||||||
return this.witnessCommit!.compare(actualWitnessCommit) === 0
|
this.transactions,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return this.witnessCommit!.compare(actualWitnessCommit) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkProofOfWork (): boolean {
|
checkProofOfWork(): boolean {
|
||||||
const hash: Buffer = reverseBuffer(this.getHash())
|
const hash: Buffer = reverseBuffer(this.getHash());
|
||||||
const target = Block.calculateTarget(this.bits)
|
const target = Block.calculateTarget(this.bits);
|
||||||
|
|
||||||
return hash.compare(target) <= 0
|
return hash.compare(target) <= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,44 @@
|
||||||
// https://github.com/feross/buffer/blob/master/index.js#L1127
|
// https://github.com/feross/buffer/blob/master/index.js#L1127
|
||||||
function verifuint (value: number, max: number): void {
|
function verifuint(value: number, max: number): void {
|
||||||
if (typeof value !== 'number') throw new Error('cannot write a non-number as a number')
|
if (typeof value !== 'number')
|
||||||
if (value < 0) throw new Error('specified a negative value for writing an unsigned value')
|
throw new Error('cannot write a non-number as a number');
|
||||||
if (value > max) throw new Error('RangeError: value out of range')
|
if (value < 0)
|
||||||
if (Math.floor(value) !== value) throw new Error('value has a fractional component')
|
throw new Error('specified a negative value for writing an unsigned value');
|
||||||
|
if (value > max) throw new Error('RangeError: value out of range');
|
||||||
|
if (Math.floor(value) !== value)
|
||||||
|
throw new Error('value has a fractional component');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readUInt64LE (buffer: Buffer, offset: number): number {
|
export function readUInt64LE(buffer: Buffer, offset: number): number {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writeUInt64LE (buffer: Buffer, value: number, offset: number): number {
|
export function writeUInt64LE(
|
||||||
verifuint(value, 0x001fffffffffffff)
|
buffer: Buffer,
|
||||||
|
value: number,
|
||||||
|
offset: number,
|
||||||
|
): number {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reverseBuffer (buffer: Buffer): Buffer {
|
export function reverseBuffer(buffer: Buffer): Buffer {
|
||||||
if (buffer.length < 1) return buffer
|
if (buffer.length < 1) 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +1,69 @@
|
||||||
import { decompile } from './script'
|
import { decompile } from './script';
|
||||||
import * as multisig from './templates/multisig'
|
import * as multisig from './templates/multisig';
|
||||||
import * as nullData from './templates/nulldata'
|
import * as nullData from './templates/nulldata';
|
||||||
import * as pubKey from './templates/pubkey'
|
import * as pubKey from './templates/pubkey';
|
||||||
import * as pubKeyHash from './templates/pubkeyhash'
|
import * as pubKeyHash from './templates/pubkeyhash';
|
||||||
import * as scriptHash from './templates/scripthash'
|
import * as scriptHash from './templates/scripthash';
|
||||||
import * as witnessPubKeyHash from './templates/witnesspubkeyhash'
|
import * as witnessPubKeyHash from './templates/witnesspubkeyhash';
|
||||||
import * as witnessScriptHash from './templates/witnessscripthash'
|
import * as witnessScriptHash from './templates/witnessscripthash';
|
||||||
import * as witnessCommitment from './templates/witnesscommitment'
|
import * as witnessCommitment from './templates/witnesscommitment';
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
P2MS: <string> 'multisig',
|
P2MS: <string>'multisig',
|
||||||
NONSTANDARD: <string> 'nonstandard',
|
NONSTANDARD: <string>'nonstandard',
|
||||||
NULLDATA: <string> 'nulldata',
|
NULLDATA: <string>'nulldata',
|
||||||
P2PK: <string> 'pubkey',
|
P2PK: <string>'pubkey',
|
||||||
P2PKH: <string> 'pubkeyhash',
|
P2PKH: <string>'pubkeyhash',
|
||||||
P2SH: <string> 'scripthash',
|
P2SH: <string>'scripthash',
|
||||||
P2WPKH: <string> 'witnesspubkeyhash',
|
P2WPKH: <string>'witnesspubkeyhash',
|
||||||
P2WSH: <string> 'witnessscripthash',
|
P2WSH: <string>'witnessscripthash',
|
||||||
WITNESS_COMMITMENT: <string> 'witnesscommitment'
|
WITNESS_COMMITMENT: <string>'witnesscommitment',
|
||||||
}
|
};
|
||||||
|
|
||||||
function classifyOutput (script: Buffer): string {
|
function classifyOutput(script: Buffer): string {
|
||||||
if (witnessPubKeyHash.output.check(script)) return types.P2WPKH
|
if (witnessPubKeyHash.output.check(script)) return types.P2WPKH;
|
||||||
if (witnessScriptHash.output.check(script)) return types.P2WSH
|
if (witnessScriptHash.output.check(script)) return types.P2WSH;
|
||||||
if (pubKeyHash.output.check(script)) return types.P2PKH
|
if (pubKeyHash.output.check(script)) return types.P2PKH;
|
||||||
if (scriptHash.output.check(script)) return types.P2SH
|
if (scriptHash.output.check(script)) return types.P2SH;
|
||||||
|
|
||||||
// XXX: optimization, below functions .decompile before use
|
// XXX: optimization, below functions .decompile before use
|
||||||
const chunks = decompile(script)
|
const chunks = decompile(script);
|
||||||
if (!chunks) throw new TypeError('Invalid script')
|
if (!chunks) throw new TypeError('Invalid script');
|
||||||
|
|
||||||
if (multisig.output.check(chunks)) return types.P2MS
|
if (multisig.output.check(chunks)) return types.P2MS;
|
||||||
if (pubKey.output.check(chunks)) return types.P2PK
|
if (pubKey.output.check(chunks)) return types.P2PK;
|
||||||
if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT
|
if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT;
|
||||||
if (nullData.output.check(chunks)) return types.NULLDATA
|
if (nullData.output.check(chunks)) return types.NULLDATA;
|
||||||
|
|
||||||
return types.NONSTANDARD
|
return types.NONSTANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
function classifyInput (script: Buffer, allowIncomplete: boolean): string {
|
function classifyInput(script: Buffer, allowIncomplete: boolean): string {
|
||||||
// XXX: optimization, below functions .decompile before use
|
// XXX: optimization, below functions .decompile before use
|
||||||
const chunks = decompile(script)
|
const chunks = decompile(script);
|
||||||
if (!chunks) throw new TypeError('Invalid script')
|
if (!chunks) throw new TypeError('Invalid script');
|
||||||
|
|
||||||
if (pubKeyHash.input.check(chunks)) return types.P2PKH
|
if (pubKeyHash.input.check(chunks)) return types.P2PKH;
|
||||||
if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH
|
if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH;
|
||||||
if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS
|
if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS;
|
||||||
if (pubKey.input.check(chunks)) return types.P2PK
|
if (pubKey.input.check(chunks)) return types.P2PK;
|
||||||
|
|
||||||
return types.NONSTANDARD
|
return types.NONSTANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
function classifyWitness (script: Array<Buffer>, allowIncomplete: boolean): string {
|
function classifyWitness(
|
||||||
|
script: Array<Buffer>,
|
||||||
|
allowIncomplete: boolean,
|
||||||
|
): string {
|
||||||
// XXX: optimization, below functions .decompile before use
|
// XXX: optimization, below functions .decompile before use
|
||||||
const chunks = decompile(script)
|
const chunks = decompile(script);
|
||||||
if (!chunks) throw new TypeError('Invalid script')
|
if (!chunks) throw new TypeError('Invalid script');
|
||||||
|
|
||||||
if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH
|
if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH;
|
||||||
if (witnessScriptHash.input.check(<Array<Buffer>>chunks, allowIncomplete)) return types.P2WSH
|
if (witnessScriptHash.input.check(<Array<Buffer>>chunks, allowIncomplete))
|
||||||
|
return types.P2WSH;
|
||||||
|
|
||||||
return types.NONSTANDARD
|
return types.NONSTANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -67,4 +71,4 @@ export {
|
||||||
classifyOutput as output,
|
classifyOutput as output,
|
||||||
classifyWitness as witness,
|
classifyWitness as witness,
|
||||||
types,
|
types,
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,21 +1,27 @@
|
||||||
const createHash = require('create-hash')
|
const createHash = require('create-hash');
|
||||||
|
|
||||||
export function ripemd160 (buffer: Buffer): Buffer {
|
export function ripemd160(buffer: Buffer): Buffer {
|
||||||
return createHash('rmd160').update(buffer).digest()
|
return createHash('rmd160')
|
||||||
|
.update(buffer)
|
||||||
|
.digest();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sha1 (buffer: Buffer): Buffer {
|
export function sha1(buffer: Buffer): Buffer {
|
||||||
return createHash('sha1').update(buffer).digest()
|
return createHash('sha1')
|
||||||
|
.update(buffer)
|
||||||
|
.digest();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sha256 (buffer: Buffer): Buffer {
|
export function sha256(buffer: Buffer): Buffer {
|
||||||
return createHash('sha256').update(buffer).digest()
|
return createHash('sha256')
|
||||||
|
.update(buffer)
|
||||||
|
.digest();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hash160 (buffer: Buffer): Buffer {
|
export function hash160(buffer: Buffer): Buffer {
|
||||||
return ripemd160(sha256(buffer))
|
return ripemd160(sha256(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hash256 (buffer: Buffer): Buffer {
|
export function hash256(buffer: Buffer): Buffer {
|
||||||
return sha256(sha256(buffer))
|
return sha256(sha256(buffer));
|
||||||
}
|
}
|
||||||
|
|
168
ts_src/ecpair.ts
168
ts_src/ecpair.ts
|
@ -1,130 +1,132 @@
|
||||||
import { Network } from './networks'
|
import { Network } from './networks';
|
||||||
import * as NETWORKS from './networks'
|
import * as NETWORKS from './networks';
|
||||||
import * as types from './types'
|
import * as types from './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(
|
||||||
compressed: types.maybe(types.Boolean),
|
typeforce.compile({
|
||||||
network: types.maybe(types.Network)
|
compressed: types.maybe(types.Boolean),
|
||||||
}))
|
network: types.maybe(types.Network),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
interface ECPairOptions {
|
interface ECPairOptions {
|
||||||
compressed?: boolean
|
compressed?: boolean;
|
||||||
network?: Network
|
network?: Network;
|
||||||
rng?(arg0: Buffer): Buffer
|
rng?(arg0: Buffer): Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ECPairInterface {
|
export interface ECPairInterface {
|
||||||
compressed: boolean
|
compressed: boolean;
|
||||||
network: Network
|
network: Network;
|
||||||
privateKey?: Buffer
|
privateKey?: Buffer;
|
||||||
publicKey?: Buffer
|
publicKey?: Buffer;
|
||||||
toWIF(): string
|
toWIF(): string;
|
||||||
sign(hash: Buffer): Buffer
|
sign(hash: Buffer): Buffer;
|
||||||
verify(hash: Buffer, signature: Buffer): Buffer
|
verify(hash: Buffer, signature: Buffer): Buffer;
|
||||||
getPublicKey?(): Buffer
|
getPublicKey?(): Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ECPair implements ECPairInterface {
|
class ECPair implements ECPairInterface {
|
||||||
compressed: boolean
|
compressed: boolean;
|
||||||
network: Network
|
network: Network;
|
||||||
private __d?: Buffer
|
private __d?: Buffer;
|
||||||
private __Q?: Buffer
|
private __Q?: Buffer;
|
||||||
|
|
||||||
constructor (d?: Buffer, Q?: Buffer, options?: ECPairOptions) {
|
constructor(d?: Buffer, Q?: Buffer, options?: ECPairOptions) {
|
||||||
if (options === undefined) options = {}
|
if (options === undefined) options = {};
|
||||||
this.compressed = options.compressed === undefined ? true : options.compressed
|
this.compressed =
|
||||||
this.network = options.network || NETWORKS.bitcoin
|
options.compressed === undefined ? true : options.compressed;
|
||||||
|
this.network = options.network || NETWORKS.bitcoin;
|
||||||
|
|
||||||
this.__d = undefined
|
this.__d = undefined;
|
||||||
this.__Q = undefined
|
this.__Q = undefined;
|
||||||
if (d !== undefined) this.__d = d
|
if (d !== undefined) this.__d = d;
|
||||||
if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed)
|
if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
get privateKey (): Buffer | undefined {
|
get privateKey(): Buffer | undefined {
|
||||||
return this.__d
|
return this.__d;
|
||||||
}
|
}
|
||||||
|
|
||||||
get publicKey (): Buffer | undefined {
|
get publicKey(): Buffer | undefined {
|
||||||
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed)
|
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed);
|
||||||
return this.__Q
|
return this.__Q;
|
||||||
}
|
}
|
||||||
|
|
||||||
toWIF (): string {
|
toWIF(): string {
|
||||||
if (!this.__d) throw new Error('Missing private key')
|
if (!this.__d) throw new Error('Missing private key');
|
||||||
return wif.encode(this.network.wif, this.__d, this.compressed)
|
return wif.encode(this.network.wif, this.__d, this.compressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
sign (hash: Buffer): Buffer {
|
sign(hash: Buffer): Buffer {
|
||||||
if (!this.__d) throw new Error('Missing private key')
|
if (!this.__d) throw new Error('Missing private key');
|
||||||
return ecc.sign(hash, this.__d)
|
return ecc.sign(hash, this.__d);
|
||||||
}
|
}
|
||||||
|
|
||||||
verify (hash: Buffer, signature: Buffer): Buffer {
|
verify(hash: Buffer, signature: Buffer): Buffer {
|
||||||
return ecc.verify(hash, this.publicKey, signature)
|
return ecc.verify(hash, this.publicKey, signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromPrivateKey (buffer: Buffer, options?: ECPairOptions): ECPair {
|
function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair {
|
||||||
typeforce(types.Buffer256bit, buffer)
|
typeforce(types.Buffer256bit, buffer);
|
||||||
if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)')
|
if (!ecc.isPrivate(buffer))
|
||||||
typeforce(isOptions, options)
|
throw new TypeError('Private key not in range [1, n)');
|
||||||
|
typeforce(isOptions, options);
|
||||||
|
|
||||||
return new ECPair(buffer, undefined, <ECPairOptions>options)
|
return new ECPair(buffer, undefined, <ECPairOptions>options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromPublicKey (buffer: Buffer, options?: ECPairOptions): ECPair {
|
function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair {
|
||||||
typeforce(ecc.isPoint, buffer)
|
typeforce(ecc.isPoint, buffer);
|
||||||
typeforce(isOptions, options)
|
typeforce(isOptions, options);
|
||||||
return new ECPair(undefined, buffer, <ECPairOptions>options)
|
return new ECPair(undefined, buffer, <ECPairOptions>options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromWIF (string: string, network?: Network | Array<Network>): ECPair {
|
function fromWIF(string: string, network?: Network | Array<Network>): ECPair {
|
||||||
const decoded = wif.decode(string)
|
const decoded = wif.decode(string);
|
||||||
const version = decoded.version
|
const version = decoded.version;
|
||||||
|
|
||||||
// list of networks?
|
// list of networks?
|
||||||
if (types.Array(network)) {
|
if (types.Array(network)) {
|
||||||
network = <Network>(<Array<Network>>network).filter(function (x: Network) {
|
network = <Network>(<Array<Network>>network)
|
||||||
return version === x.wif
|
.filter(function(x: Network) {
|
||||||
}).pop()
|
return version === x.wif;
|
||||||
|
})
|
||||||
|
.pop();
|
||||||
|
|
||||||
if (!network) throw new Error('Unknown network version')
|
if (!network) 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 {
|
} else {
|
||||||
network = network || NETWORKS.bitcoin
|
network = network || NETWORKS.bitcoin;
|
||||||
|
|
||||||
if (version !== (<Network>network).wif) throw new Error('Invalid network version')
|
if (version !== (<Network>network).wif)
|
||||||
|
throw new Error('Invalid network version');
|
||||||
}
|
}
|
||||||
|
|
||||||
return fromPrivateKey(decoded.privateKey, {
|
return fromPrivateKey(decoded.privateKey, {
|
||||||
compressed: decoded.compressed,
|
compressed: decoded.compressed,
|
||||||
network: <Network>network
|
network: <Network>network,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeRandom (options?: ECPairOptions): ECPair {
|
function makeRandom(options?: ECPairOptions): ECPair {
|
||||||
typeforce(isOptions, options)
|
typeforce(isOptions, options);
|
||||||
if (options === undefined) options = {}
|
if (options === undefined) 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF };
|
||||||
makeRandom,
|
|
||||||
fromPrivateKey,
|
|
||||||
fromPublicKey,
|
|
||||||
fromWIF
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,28 +1,20 @@
|
||||||
import * as bip32 from 'bip32'
|
import * as bip32 from 'bip32';
|
||||||
import * as ECPair from './ecpair'
|
import * as ECPair from './ecpair';
|
||||||
import * as address from './address'
|
import * as address from './address';
|
||||||
import * as crypto from './crypto'
|
import * as crypto from './crypto';
|
||||||
import * as networks from './networks'
|
import * as networks from './networks';
|
||||||
import * as payments from './payments'
|
import * as payments from './payments';
|
||||||
import * as script from './script'
|
import * as script from './script';
|
||||||
|
|
||||||
export {
|
export { ECPair, address, bip32, crypto, networks, payments, script };
|
||||||
ECPair,
|
|
||||||
address,
|
|
||||||
bip32,
|
|
||||||
crypto,
|
|
||||||
networks,
|
|
||||||
payments,
|
|
||||||
script,
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Block } from './block'
|
export { Block } from './block';
|
||||||
export { Transaction } from './transaction'
|
export { Transaction } from './transaction';
|
||||||
export { TransactionBuilder } from './transaction_builder'
|
export { TransactionBuilder } from './transaction_builder';
|
||||||
export { OPS as opcodes } from './script'
|
export { OPS as opcodes } from './script';
|
||||||
|
|
||||||
export { Payment, PaymentOpts } from './payments'
|
export { Payment, PaymentOpts } from './payments';
|
||||||
export { Input as TxInput, Output as TxOutput } from './transaction'
|
export { Input as TxInput, Output as TxOutput } from './transaction';
|
||||||
export { Network } from './networks'
|
export { Network } from './networks';
|
||||||
export { OpCode } from './script'
|
export { OpCode } from './script';
|
||||||
export { BIP32Interface } from 'bip32'
|
export { BIP32Interface } from 'bip32';
|
||||||
|
|
|
@ -7,43 +7,43 @@ export type Network = {
|
||||||
pubKeyHash: number;
|
pubKeyHash: number;
|
||||||
scriptHash: number;
|
scriptHash: number;
|
||||||
wif: number;
|
wif: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
type bip32 = {
|
type bip32 = {
|
||||||
public: number;
|
public: number;
|
||||||
private: number;
|
private: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const bitcoin: Network = {
|
export const bitcoin: Network = {
|
||||||
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,
|
||||||
}
|
};
|
||||||
export const regtest: Network = {
|
export const regtest: Network = {
|
||||||
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,
|
||||||
}
|
};
|
||||||
export const testnet: Network = {
|
export const testnet: Network = {
|
||||||
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,54 +1,59 @@
|
||||||
import { Payment, PaymentOpts } from './index' // eslint-disable-line
|
import { Payment, PaymentOpts } from './index';
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
import * as lazy from './lazy'
|
import * as lazy from './lazy';
|
||||||
import { bitcoin as BITCOIN_NETWORK } from '../networks'
|
import { bitcoin as BITCOIN_NETWORK } from '../networks';
|
||||||
const typef = require('typeforce')
|
const typef = require('typeforce');
|
||||||
const OPS = bscript.OPS
|
const OPS = bscript.OPS;
|
||||||
|
|
||||||
function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean {
|
function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean {
|
||||||
if (a.length !== b.length) return false
|
if (a.length !== b.length) return false;
|
||||||
|
|
||||||
return a.every(function (x, i) {
|
return a.every(function(x, i) {
|
||||||
return x.equals(b[i])
|
return x.equals(b[i]);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// output: OP_RETURN ...
|
// output: OP_RETURN ...
|
||||||
export function p2data (a: Payment, opts?: PaymentOpts): Payment {
|
export function p2data(a: Payment, opts?: PaymentOpts): Payment {
|
||||||
if (
|
if (!a.data && !a.output) throw new TypeError('Not enough data');
|
||||||
!a.data &&
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
!a.output
|
|
||||||
) throw new TypeError('Not enough data')
|
|
||||||
opts = Object.assign({ validate: true }, opts || {})
|
|
||||||
|
|
||||||
typef({
|
typef(
|
||||||
network: typef.maybe(typef.Object),
|
{
|
||||||
output: typef.maybe(typef.Buffer),
|
network: typef.maybe(typef.Object),
|
||||||
data: typef.maybe(typef.arrayOf(typef.Buffer))
|
output: typef.maybe(typef.Buffer),
|
||||||
}, a)
|
data: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
|
},
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
|
||||||
const network = a.network || BITCOIN_NETWORK
|
const network = a.network || BITCOIN_NETWORK;
|
||||||
const o = <Payment>{ network }
|
const o = <Payment>{ network };
|
||||||
|
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function() {
|
||||||
if (!a.data) return
|
if (!a.data) return;
|
||||||
return bscript.compile((<Array<Buffer | number>>[OPS.OP_RETURN]).concat(a.data))
|
return bscript.compile(
|
||||||
})
|
(<Array<Buffer | number>>[OPS.OP_RETURN]).concat(a.data),
|
||||||
lazy.prop(o, 'data', function () {
|
);
|
||||||
if (!a.output) return
|
});
|
||||||
return bscript.decompile(a.output)!.slice(1)
|
lazy.prop(o, 'data', function() {
|
||||||
})
|
if (!a.output) return;
|
||||||
|
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) throw new TypeError('Output is invalid')
|
if (chunks![0] !== OPS.OP_RETURN)
|
||||||
if (!chunks!.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid')
|
throw new TypeError('Output is invalid');
|
||||||
|
if (!chunks!.slice(1).every(typef.Buffer))
|
||||||
|
throw new TypeError('Output is invalid');
|
||||||
|
|
||||||
if (a.data && !stacksEqual(a.data, <Array<Buffer>>o.data)) throw new TypeError('Data mismatch')
|
if (a.data && !stacksEqual(a.data, <Array<Buffer>>o.data))
|
||||||
|
throw new TypeError('Data mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(o, a)
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
import { Network } from '../networks' // eslint-disable-line
|
import { Network } from '../networks';
|
||||||
import { p2data as embed } from './embed'
|
import { p2data as embed } from './embed';
|
||||||
import { p2ms } from './p2ms'
|
import { p2ms } from './p2ms';
|
||||||
import { p2pk } from './p2pk'
|
import { p2pk } from './p2pk';
|
||||||
import { p2pkh } from './p2pkh'
|
import { p2pkh } from './p2pkh';
|
||||||
import { p2sh } from './p2sh'
|
import { p2sh } from './p2sh';
|
||||||
import { p2wpkh } from './p2wpkh'
|
import { p2wpkh } from './p2wpkh';
|
||||||
import { p2wsh } from './p2wsh'
|
import { p2wsh } from './p2wsh';
|
||||||
|
|
||||||
export interface Payment {
|
export interface Payment {
|
||||||
network?: Network,
|
network?: Network;
|
||||||
output?: Buffer,
|
output?: Buffer;
|
||||||
data?: Array<Buffer>,
|
data?: Array<Buffer>;
|
||||||
m?: number,
|
m?: number;
|
||||||
n?: number,
|
n?: number;
|
||||||
pubkeys?: Array<Buffer>,
|
pubkeys?: Array<Buffer>;
|
||||||
input?: Buffer,
|
input?: Buffer;
|
||||||
signatures?: Array<Buffer>,
|
signatures?: Array<Buffer>;
|
||||||
pubkey?: Buffer,
|
pubkey?: Buffer;
|
||||||
signature?: Buffer,
|
signature?: Buffer;
|
||||||
address?: string,
|
address?: string;
|
||||||
hash?: Buffer,
|
hash?: Buffer;
|
||||||
redeem?: Payment,
|
redeem?: Payment;
|
||||||
witness?: Array<Buffer>,
|
witness?: Array<Buffer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaymentOpts {
|
export interface PaymentOpts {
|
||||||
validate?: boolean,
|
validate?: boolean;
|
||||||
allowIncomplete?: boolean,
|
allowIncomplete?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }
|
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh };
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// witness commitment
|
// witness commitment
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
export function prop (object: Object, name: string, f: ()=>any): void {
|
export function prop(object: Object, name: string, f: () => any): void {
|
||||||
Object.defineProperty(object, name, {
|
Object.defineProperty(object, name, {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: function () {
|
get: function() {
|
||||||
let value = f.call(this)
|
let value = f.call(this);
|
||||||
this[name] = value
|
this[name] = value;
|
||||||
return value
|
return value;
|
||||||
},
|
},
|
||||||
set: function (value) {
|
set: function(value) {
|
||||||
Object.defineProperty(this, name, {
|
Object.defineProperty(this, name, {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
value: value,
|
value: value,
|
||||||
writable: true
|
writable: true,
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function value <T> (f: ()=>T): ()=>T {
|
export function value<T>(f: () => T): () => T {
|
||||||
let value: T
|
let value: T;
|
||||||
return function (): T {
|
return function(): T {
|
||||||
if (value !== undefined) return value
|
if (value !== undefined) return value;
|
||||||
value = f()
|
value = f();
|
||||||
return value
|
return value;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,141 +1,160 @@
|
||||||
import { Payment, PaymentOpts } from './index' // eslint-disable-line
|
import { Payment, PaymentOpts } from './index';
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
import * as lazy from './lazy'
|
import * as lazy from './lazy';
|
||||||
import { bitcoin as BITCOIN_NETWORK } from '../networks'
|
import { bitcoin as BITCOIN_NETWORK } from '../networks';
|
||||||
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: Array<Buffer>, b: Array<Buffer>): boolean {
|
function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean {
|
||||||
if (a.length !== b.length) return false
|
if (a.length !== b.length) return false;
|
||||||
|
|
||||||
return a.every(function (x, i) {
|
return a.every(function(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
|
||||||
export function p2ms (a: Payment, opts?: PaymentOpts): Payment {
|
export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
|
||||||
if (
|
if (
|
||||||
!a.input &&
|
!a.input &&
|
||||||
!a.output &&
|
!a.output &&
|
||||||
!(a.pubkeys && a.m !== undefined) &&
|
!(a.pubkeys && a.m !== undefined) &&
|
||||||
!a.signatures
|
!a.signatures
|
||||||
) throw new TypeError('Not enough data')
|
)
|
||||||
opts = Object.assign({ validate: true }, opts || {})
|
throw new TypeError('Not enough data');
|
||||||
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
|
|
||||||
function isAcceptableSignature (x: Buffer | number) {
|
function isAcceptableSignature(x: Buffer | number) {
|
||||||
return bscript.isCanonicalScriptSignature(<Buffer>x) ||
|
return (
|
||||||
(opts!.allowIncomplete &&
|
bscript.isCanonicalScriptSignature(<Buffer>x) ||
|
||||||
(<number> x === OPS.OP_0)) !== undefined // eslint-disable-line
|
(opts!.allowIncomplete && <number>x === OPS.OP_0) !== undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
typef({
|
typef(
|
||||||
network: typef.maybe(typef.Object),
|
{
|
||||||
m: typef.maybe(typef.Number),
|
network: typef.maybe(typef.Object),
|
||||||
n: typef.maybe(typef.Number),
|
m: typef.maybe(typef.Number),
|
||||||
output: typef.maybe(typef.Buffer),
|
n: typef.maybe(typef.Number),
|
||||||
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)),
|
output: typef.maybe(typef.Buffer),
|
||||||
|
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)),
|
||||||
|
|
||||||
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
|
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
|
||||||
input: typef.maybe(typef.Buffer)
|
input: typef.maybe(typef.Buffer),
|
||||||
}, a)
|
},
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
|
||||||
const network = a.network || BITCOIN_NETWORK
|
const network = a.network || BITCOIN_NETWORK;
|
||||||
const o: Payment = { network }
|
const o: Payment = { network };
|
||||||
|
|
||||||
let chunks: Array<Buffer | number> = []
|
let chunks: Array<Buffer | number> = [];
|
||||||
let decoded = false
|
let decoded = false;
|
||||||
function decode (output: Buffer | Array<Buffer | number>): void {
|
function decode(output: Buffer | Array<Buffer | number>): void {
|
||||||
if (decoded) return
|
if (decoded) return;
|
||||||
decoded = true
|
decoded = true;
|
||||||
chunks = <Array<Buffer | number>>bscript.decompile(output)
|
chunks = <Array<Buffer | number>>bscript.decompile(output);
|
||||||
o.m = <number> chunks[0] - OP_INT_BASE // eslint-disable-line
|
o.m = <number>chunks[0] - OP_INT_BASE;
|
||||||
o.n = <number> chunks[chunks.length - 2] - OP_INT_BASE // eslint-disable-line
|
o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE;
|
||||||
o.pubkeys = <Array<Buffer>>chunks.slice(1, -2)
|
o.pubkeys = <Array<Buffer>>chunks.slice(1, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function() {
|
||||||
if (!a.m) return
|
if (!a.m) return;
|
||||||
if (!o.n) return
|
if (!o.n) return;
|
||||||
if (!a.pubkeys) return
|
if (!a.pubkeys) return;
|
||||||
return bscript.compile((<Array<Buffer | number>>[]).concat(
|
return bscript.compile(
|
||||||
OP_INT_BASE + a.m,
|
(<Array<Buffer | number>>[]).concat(
|
||||||
a.pubkeys,
|
OP_INT_BASE + a.m,
|
||||||
OP_INT_BASE + o.n,
|
a.pubkeys,
|
||||||
OPS.OP_CHECKMULTISIG
|
OP_INT_BASE + o.n,
|
||||||
))
|
OPS.OP_CHECKMULTISIG,
|
||||||
})
|
),
|
||||||
lazy.prop(o, 'm', function () {
|
);
|
||||||
if (!o.output) return
|
});
|
||||||
decode(o.output)
|
lazy.prop(o, 'm', function() {
|
||||||
return o.m
|
if (!o.output) return;
|
||||||
})
|
decode(o.output);
|
||||||
lazy.prop(o, 'n', function () {
|
return o.m;
|
||||||
if (!o.pubkeys) return
|
});
|
||||||
return o.pubkeys.length
|
lazy.prop(o, 'n', function() {
|
||||||
})
|
if (!o.pubkeys) return;
|
||||||
lazy.prop(o, 'pubkeys', function () {
|
return o.pubkeys.length;
|
||||||
if (!a.output) return
|
});
|
||||||
decode(a.output)
|
lazy.prop(o, 'pubkeys', function() {
|
||||||
return o.pubkeys
|
if (!a.output) return;
|
||||||
})
|
decode(a.output);
|
||||||
lazy.prop(o, 'signatures', function () {
|
return o.pubkeys;
|
||||||
if (!a.input) return
|
});
|
||||||
return bscript.decompile(a.input)!.slice(1)
|
lazy.prop(o, 'signatures', function() {
|
||||||
})
|
if (!a.input) return;
|
||||||
lazy.prop(o, 'input', function () {
|
return bscript.decompile(a.input)!.slice(1);
|
||||||
if (!a.signatures) return
|
});
|
||||||
return bscript.compile((<Array<Buffer | number>>[OPS.OP_0]).concat(a.signatures))
|
lazy.prop(o, 'input', function() {
|
||||||
})
|
if (!a.signatures) return;
|
||||||
lazy.prop(o, 'witness', function () {
|
return bscript.compile(
|
||||||
if (!o.input) return
|
(<Array<Buffer | number>>[OPS.OP_0]).concat(a.signatures),
|
||||||
return []
|
);
|
||||||
})
|
});
|
||||||
|
lazy.prop(o, 'witness', function() {
|
||||||
|
if (!o.input) return;
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
// extended validation
|
// extended validation
|
||||||
if (opts.validate) {
|
if (opts.validate) {
|
||||||
if (a.output) {
|
if (a.output) {
|
||||||
decode(a.output)
|
decode(a.output);
|
||||||
if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid')
|
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 (!typef.Number(chunks[chunks.length - 2]))
|
||||||
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid')
|
throw new TypeError('Output is invalid');
|
||||||
|
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
|
||||||
|
throw new TypeError('Output is invalid');
|
||||||
|
|
||||||
if (
|
if (o.m! <= 0 || o.n! > 16 || o.m! > o.n! || o.n !== chunks.length - 3)
|
||||||
o.m! <= 0 || // eslint-disable-line
|
throw new TypeError('Output is invalid');
|
||||||
o.n! > 16 || // eslint-disable-line
|
if (!o.pubkeys!.every(x => ecc.isPoint(x)))
|
||||||
o.m! > o.n! || // eslint-disable-line
|
throw new TypeError('Output is invalid');
|
||||||
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.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.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 && !stacksEqual(a.pubkeys, o.pubkeys!))
|
||||||
|
throw new TypeError('Pubkeys mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.pubkeys) {
|
if (a.pubkeys) {
|
||||||
if (a.n !== undefined && a.n !== a.pubkeys.length) throw new TypeError('Pubkey count mismatch')
|
if (a.n !== undefined && a.n !== a.pubkeys.length)
|
||||||
o.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 (o.n < o.m!) throw new TypeError('Pubkey count cannot be less than m');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.signatures) {
|
if (a.signatures) {
|
||||||
if (a.signatures.length < o.m!) throw new TypeError('Not enough signatures provided')
|
if (a.signatures.length < o.m!)
|
||||||
if (a.signatures.length > o.m!) throw new TypeError('Too many signatures provided')
|
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) {
|
||||||
if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid')
|
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 (
|
||||||
|
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.signatures && !stacksEqual(a.signatures, o.signatures!))
|
||||||
if (a.m !== undefined && a.m !== a.signatures!.length) throw new TypeError('Signature count mismatch')
|
throw new TypeError('Signature mismatch');
|
||||||
|
if (a.m !== undefined && a.m !== a.signatures!.length)
|
||||||
|
throw new TypeError('Signature count mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(o, a)
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +1,80 @@
|
||||||
import { Payment, PaymentOpts } from './index' // eslint-disable-line
|
import { Payment, PaymentOpts } from './index';
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
import * as lazy from './lazy'
|
import * as lazy from './lazy';
|
||||||
import { bitcoin as BITCOIN_NETWORK } from '../networks'
|
import { bitcoin as BITCOIN_NETWORK } from '../networks';
|
||||||
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
|
||||||
export function p2pk (a: Payment, opts?: PaymentOpts): Payment {
|
export function p2pk(a: Payment, opts?: PaymentOpts): Payment {
|
||||||
if (
|
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
|
||||||
!a.input &&
|
throw new TypeError('Not enough data');
|
||||||
!a.output &&
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
!a.pubkey &&
|
|
||||||
!a.input &&
|
|
||||||
!a.signature
|
|
||||||
) throw new TypeError('Not enough data')
|
|
||||||
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),
|
||||||
|
pubkey: typef.maybe(ecc.isPoint),
|
||||||
|
|
||||||
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
||||||
input: typef.maybe(typef.Buffer)
|
input: typef.maybe(typef.Buffer),
|
||||||
}, a)
|
},
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
|
||||||
const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) })
|
const _chunks = <() => Array<Buffer | number>>lazy.value(function() {
|
||||||
|
return bscript.decompile(a.input!);
|
||||||
|
});
|
||||||
|
|
||||||
const network = a.network || BITCOIN_NETWORK
|
const network = a.network || BITCOIN_NETWORK;
|
||||||
const o: Payment = { network }
|
const o: Payment = { network };
|
||||||
|
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function() {
|
||||||
if (!a.pubkey) return
|
if (!a.pubkey) return;
|
||||||
return bscript.compile([
|
return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]);
|
||||||
a.pubkey,
|
});
|
||||||
OPS.OP_CHECKSIG
|
lazy.prop(o, 'pubkey', function() {
|
||||||
])
|
if (!a.output) return;
|
||||||
})
|
return a.output.slice(1, -1);
|
||||||
lazy.prop(o, 'pubkey', function () {
|
});
|
||||||
if (!a.output) return
|
lazy.prop(o, 'signature', function() {
|
||||||
return a.output.slice(1, -1)
|
if (!a.input) return;
|
||||||
})
|
return <Buffer>_chunks()[0];
|
||||||
lazy.prop(o, 'signature', function () {
|
});
|
||||||
if (!a.input) return
|
lazy.prop(o, 'input', function() {
|
||||||
return <Buffer>_chunks()[0]
|
if (!a.signature) return;
|
||||||
})
|
return bscript.compile([a.signature]);
|
||||||
lazy.prop(o, 'input', function () {
|
});
|
||||||
if (!a.signature) return
|
lazy.prop(o, 'witness', function() {
|
||||||
return bscript.compile([a.signature])
|
if (!o.input) return;
|
||||||
})
|
return [];
|
||||||
lazy.prop(o, 'witness', function () {
|
});
|
||||||
if (!o.input) return
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
// extended validation
|
// extended validation
|
||||||
if (opts.validate) {
|
if (opts.validate) {
|
||||||
if (a.output) {
|
if (a.output) {
|
||||||
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) 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 pubkey is invalid')
|
throw new TypeError('Output is invalid');
|
||||||
if (a.pubkey && !a.pubkey.equals(o.pubkey!)) throw new TypeError('Pubkey mismatch')
|
if (!ecc.isPoint(o.pubkey))
|
||||||
|
throw new TypeError('Output pubkey is invalid');
|
||||||
|
if (a.pubkey && !a.pubkey.equals(o.pubkey!))
|
||||||
|
throw new TypeError('Pubkey mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.signature) {
|
if (a.signature) {
|
||||||
if (a.input && !a.input.equals(o.input!)) throw new TypeError('Signature mismatch')
|
if (a.input && !a.input.equals(o.input!))
|
||||||
|
throw new TypeError('Signature mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.input) {
|
if (a.input) {
|
||||||
if (_chunks().length !== 1) throw new TypeError('Input is invalid')
|
if (_chunks().length !== 1) throw new TypeError('Input is invalid');
|
||||||
if (!bscript.isCanonicalScriptSignature(o.signature!)) throw new TypeError('Input has invalid signature')
|
if (!bscript.isCanonicalScriptSignature(o.signature!))
|
||||||
|
throw new TypeError('Input has invalid signature');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(o, a)
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,101 +1,103 @@
|
||||||
import { Payment, PaymentOpts } from './index' // eslint-disable-line
|
import { Payment, PaymentOpts } from './index';
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
import * as bcrypto from '../crypto'
|
import * as bcrypto from '../crypto';
|
||||||
import * as lazy from './lazy'
|
import * as lazy from './lazy';
|
||||||
import { bitcoin as BITCOIN_NETWORK } from '../networks'
|
import { bitcoin as BITCOIN_NETWORK } from '../networks';
|
||||||
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');
|
||||||
|
|
||||||
const bs58check = require('bs58check')
|
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
|
||||||
export function p2pkh (a: Payment, opts?: PaymentOpts): Payment {
|
export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
|
||||||
if (
|
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
|
||||||
!a.address &&
|
throw new TypeError('Not enough data');
|
||||||
!a.hash &&
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
!a.output &&
|
|
||||||
!a.pubkey &&
|
|
||||||
!a.input
|
|
||||||
) throw new TypeError('Not enough data')
|
|
||||||
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)),
|
||||||
|
output: typef.maybe(typef.BufferN(25)),
|
||||||
|
|
||||||
pubkey: typef.maybe(ecc.isPoint),
|
pubkey: typef.maybe(ecc.isPoint),
|
||||||
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
signature: typef.maybe(bscript.isCanonicalScriptSignature),
|
||||||
input: typef.maybe(typef.Buffer)
|
input: typef.maybe(typef.Buffer),
|
||||||
}, a)
|
},
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
|
||||||
const _address = lazy.value(function () {
|
const _address = lazy.value(function() {
|
||||||
const payload = bs58check.decode(a.address)
|
const payload = bs58check.decode(a.address);
|
||||||
const version = payload.readUInt8(0)
|
const version = payload.readUInt8(0);
|
||||||
const hash = payload.slice(1)
|
const hash = payload.slice(1);
|
||||||
return { version, hash }
|
return { version, hash };
|
||||||
})
|
});
|
||||||
const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) })
|
const _chunks = <() => Array<Buffer | number>>lazy.value(function() {
|
||||||
|
return bscript.decompile(a.input!);
|
||||||
|
});
|
||||||
|
|
||||||
const network = a.network || BITCOIN_NETWORK
|
const network = a.network || BITCOIN_NETWORK;
|
||||||
const o: Payment = { network }
|
const o: Payment = { network };
|
||||||
|
|
||||||
lazy.prop(o, 'address', function () {
|
lazy.prop(o, 'address', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
|
|
||||||
const payload = Buffer.allocUnsafe(21)
|
const payload = Buffer.allocUnsafe(21);
|
||||||
payload.writeUInt8(network.pubKeyHash, 0)
|
payload.writeUInt8(network.pubKeyHash, 0);
|
||||||
o.hash.copy(payload, 1)
|
o.hash.copy(payload, 1);
|
||||||
return bs58check.encode(payload)
|
return bs58check.encode(payload);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'hash', function () {
|
lazy.prop(o, 'hash', function() {
|
||||||
if (a.output) return a.output.slice(3, 23)
|
if (a.output) return a.output.slice(3, 23);
|
||||||
if (a.address) return _address().hash
|
if (a.address) return _address().hash;
|
||||||
if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!)
|
if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
return bscript.compile([
|
return bscript.compile([
|
||||||
OPS.OP_DUP,
|
OPS.OP_DUP,
|
||||||
OPS.OP_HASH160,
|
OPS.OP_HASH160,
|
||||||
o.hash,
|
o.hash,
|
||||||
OPS.OP_EQUALVERIFY,
|
OPS.OP_EQUALVERIFY,
|
||||||
OPS.OP_CHECKSIG
|
OPS.OP_CHECKSIG,
|
||||||
])
|
]);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'pubkey', function () {
|
lazy.prop(o, 'pubkey', function() {
|
||||||
if (!a.input) return
|
if (!a.input) return;
|
||||||
return <Buffer>_chunks()[1]
|
return <Buffer>_chunks()[1];
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'signature', function () {
|
lazy.prop(o, 'signature', function() {
|
||||||
if (!a.input) return
|
if (!a.input) return;
|
||||||
return <Buffer>_chunks()[0]
|
return <Buffer>_chunks()[0];
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'input', function () {
|
lazy.prop(o, 'input', function() {
|
||||||
if (!a.pubkey) return
|
if (!a.pubkey) return;
|
||||||
if (!a.signature) return
|
if (!a.signature) return;
|
||||||
return bscript.compile([a.signature, a.pubkey])
|
return bscript.compile([a.signature, a.pubkey]);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'witness', function () {
|
lazy.prop(o, 'witness', function() {
|
||||||
if (!o.input) return
|
if (!o.input) return;
|
||||||
return []
|
return [];
|
||||||
})
|
});
|
||||||
|
|
||||||
// extended validation
|
// extended validation
|
||||||
if (opts.validate) {
|
if (opts.validate) {
|
||||||
let hash: Buffer = Buffer.from([])
|
let hash: Buffer = Buffer.from([]);
|
||||||
if (a.address) {
|
if (a.address) {
|
||||||
if (_address().version !== network.pubKeyHash) throw new TypeError('Invalid version or Network mismatch')
|
if (_address().version !== network.pubKeyHash)
|
||||||
if (_address().hash.length !== 20) throw new TypeError('Invalid address')
|
throw new TypeError('Invalid version or Network mismatch');
|
||||||
hash = _address().hash
|
if (_address().hash.length !== 20) throw new TypeError('Invalid address');
|
||||||
|
hash = _address().hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.hash) {
|
if (a.hash) {
|
||||||
if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(a.hash))
|
||||||
else hash = a.hash
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = a.hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.output) {
|
if (a.output) {
|
||||||
|
@ -105,32 +107,41 @@ export function p2pkh (a: Payment, opts?: PaymentOpts): Payment {
|
||||||
a.output[1] !== OPS.OP_HASH160 ||
|
a.output[1] !== OPS.OP_HASH160 ||
|
||||||
a.output[2] !== 0x14 ||
|
a.output[2] !== 0x14 ||
|
||||||
a.output[23] !== OPS.OP_EQUALVERIFY ||
|
a.output[23] !== OPS.OP_EQUALVERIFY ||
|
||||||
a.output[24] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid')
|
a.output[24] !== OPS.OP_CHECKSIG
|
||||||
|
)
|
||||||
|
throw new TypeError('Output is invalid');
|
||||||
|
|
||||||
const hash2 = a.output.slice(3, 23)
|
const hash2 = a.output.slice(3, 23);
|
||||||
if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(hash2))
|
||||||
else hash = hash2
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = hash2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.pubkey) {
|
if (a.pubkey) {
|
||||||
const pkh = bcrypto.hash160(a.pubkey)
|
const pkh = bcrypto.hash160(a.pubkey);
|
||||||
if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(pkh))
|
||||||
else hash = pkh
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = pkh;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.input) {
|
if (a.input) {
|
||||||
const chunks = _chunks()
|
const chunks = _chunks();
|
||||||
if (chunks.length !== 2) throw new TypeError('Input is invalid')
|
if (chunks.length !== 2) throw new TypeError('Input is invalid');
|
||||||
if (!bscript.isCanonicalScriptSignature(<Buffer>chunks[0])) throw new TypeError('Input has invalid signature')
|
if (!bscript.isCanonicalScriptSignature(<Buffer>chunks[0]))
|
||||||
if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey')
|
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(<Buffer>chunks[0])) throw new TypeError('Signature mismatch')
|
if (a.signature && !a.signature.equals(<Buffer>chunks[0]))
|
||||||
if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1])) throw new TypeError('Pubkey mismatch')
|
throw new TypeError('Signature mismatch');
|
||||||
|
if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1]))
|
||||||
|
throw new TypeError('Pubkey mismatch');
|
||||||
|
|
||||||
const pkh = bcrypto.hash160(<Buffer>chunks[1])
|
const pkh = bcrypto.hash160(<Buffer>chunks[1]);
|
||||||
if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(pkh))
|
||||||
|
throw new TypeError('Hash mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(o, a)
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,127 +1,127 @@
|
||||||
import { Payment, PaymentOpts } from './index' // eslint-disable-line
|
import { Payment, PaymentOpts } from './index';
|
||||||
import { bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line
|
import { bitcoin as BITCOIN_NETWORK } from '../networks';
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
import * as bcrypto from '../crypto'
|
import * as bcrypto from '../crypto';
|
||||||
import * as lazy from './lazy'
|
import * as lazy from './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: Array<Buffer>, b: Array<Buffer>): boolean {
|
function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean {
|
||||||
if (a.length !== b.length) return false
|
if (a.length !== b.length) return false;
|
||||||
|
|
||||||
return a.every(function (x, i) {
|
return a.every(function(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
|
||||||
export function p2sh (a: Payment, opts?: PaymentOpts): Payment {
|
export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
|
||||||
if (
|
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input)
|
||||||
!a.address &&
|
throw new TypeError('Not enough data');
|
||||||
!a.hash &&
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
!a.output &&
|
|
||||||
!a.redeem &&
|
|
||||||
!a.input
|
|
||||||
) throw new TypeError('Not enough data')
|
|
||||||
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),
|
||||||
output: typef.maybe(typef.Buffer),
|
|
||||||
input: typef.maybe(typef.Buffer),
|
|
||||||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
|
||||||
}),
|
|
||||||
input: typef.maybe(typef.Buffer),
|
|
||||||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
|
||||||
}, a)
|
|
||||||
|
|
||||||
let network = a.network
|
address: typef.maybe(typef.String),
|
||||||
|
hash: typef.maybe(typef.BufferN(20)),
|
||||||
|
output: typef.maybe(typef.BufferN(23)),
|
||||||
|
|
||||||
|
redeem: typef.maybe({
|
||||||
|
network: typef.maybe(typef.Object),
|
||||||
|
output: typef.maybe(typef.Buffer),
|
||||||
|
input: typef.maybe(typef.Buffer),
|
||||||
|
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
|
}),
|
||||||
|
input: typef.maybe(typef.Buffer),
|
||||||
|
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
|
},
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
|
||||||
|
let network = a.network;
|
||||||
if (!network) {
|
if (!network) {
|
||||||
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK
|
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const o: Payment = { network }
|
const o: Payment = { network };
|
||||||
|
|
||||||
const _address = lazy.value(function () {
|
const _address = lazy.value(function() {
|
||||||
const payload = bs58check.decode(a.address)
|
const payload = bs58check.decode(a.address);
|
||||||
const version = payload.readUInt8(0)
|
const version = payload.readUInt8(0);
|
||||||
const hash = payload.slice(1)
|
const hash = payload.slice(1);
|
||||||
return { version, hash }
|
return { version, hash };
|
||||||
})
|
});
|
||||||
const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) })
|
const _chunks = <() => Array<Buffer | number>>lazy.value(function() {
|
||||||
const _redeem = lazy.value(function (): Payment {
|
return bscript.decompile(a.input!);
|
||||||
const chunks = _chunks()
|
});
|
||||||
|
const _redeem = lazy.value(function(): Payment {
|
||||||
|
const chunks = _chunks();
|
||||||
return {
|
return {
|
||||||
network,
|
network,
|
||||||
output: <Buffer>chunks[chunks.length - 1],
|
output: <Buffer>chunks[chunks.length - 1],
|
||||||
input: bscript.compile(chunks.slice(0, -1)),
|
input: bscript.compile(chunks.slice(0, -1)),
|
||||||
witness: a.witness || []
|
witness: a.witness || [],
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
// output dependents
|
// output dependents
|
||||||
lazy.prop(o, 'address', function () {
|
lazy.prop(o, 'address', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
|
|
||||||
const payload = Buffer.allocUnsafe(21)
|
const payload = Buffer.allocUnsafe(21);
|
||||||
payload.writeUInt8(o.network!.scriptHash, 0)
|
payload.writeUInt8(o.network!.scriptHash, 0);
|
||||||
o.hash.copy(payload, 1)
|
o.hash.copy(payload, 1);
|
||||||
return bs58check.encode(payload)
|
return bs58check.encode(payload);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'hash', function () {
|
lazy.prop(o, 'hash', function() {
|
||||||
// in order of least effort
|
// in order of least effort
|
||||||
if (a.output) return a.output.slice(2, 22)
|
if (a.output) return a.output.slice(2, 22);
|
||||||
if (a.address) return _address().hash
|
if (a.address) return _address().hash;
|
||||||
if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output)
|
if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
return bscript.compile([
|
return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]);
|
||||||
OPS.OP_HASH160,
|
});
|
||||||
o.hash,
|
|
||||||
OPS.OP_EQUAL
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
// input dependents
|
// input dependents
|
||||||
lazy.prop(o, 'redeem', function () {
|
lazy.prop(o, 'redeem', function() {
|
||||||
if (!a.input) return
|
if (!a.input) return;
|
||||||
return _redeem()
|
return _redeem();
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'input', function () {
|
lazy.prop(o, 'input', function() {
|
||||||
if (!a.redeem || !a.redeem.input || !a.redeem.output) return
|
if (!a.redeem || !a.redeem.input || !a.redeem.output) return;
|
||||||
return bscript.compile((<Array<Buffer | number>>[]).concat(
|
return bscript.compile(
|
||||||
<Array<Buffer | number>>bscript.decompile(a.redeem.input),
|
(<Array<Buffer | number>>[]).concat(
|
||||||
a.redeem.output
|
<Array<Buffer | number>>bscript.decompile(a.redeem.input),
|
||||||
))
|
a.redeem.output,
|
||||||
})
|
),
|
||||||
lazy.prop(o, 'witness', function () {
|
);
|
||||||
if (o.redeem && o.redeem.witness) return o.redeem.witness
|
});
|
||||||
if (o.input) return []
|
lazy.prop(o, 'witness', function() {
|
||||||
})
|
if (o.redeem && o.redeem.witness) return o.redeem.witness;
|
||||||
|
if (o.input) return [];
|
||||||
|
});
|
||||||
|
|
||||||
if (opts.validate) {
|
if (opts.validate) {
|
||||||
let hash: Buffer = Buffer.from([])
|
let hash: Buffer = Buffer.from([]);
|
||||||
if (a.address) {
|
if (a.address) {
|
||||||
if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch')
|
if (_address().version !== network.scriptHash)
|
||||||
if (_address().hash.length !== 20) throw new TypeError('Invalid address')
|
throw new TypeError('Invalid version or Network mismatch');
|
||||||
hash = _address().hash
|
if (_address().hash.length !== 20) throw new TypeError('Invalid address');
|
||||||
|
hash = _address().hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.hash) {
|
if (a.hash) {
|
||||||
if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(a.hash))
|
||||||
else hash = a.hash
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = a.hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.output) {
|
if (a.output) {
|
||||||
|
@ -129,64 +129,79 @@ export function p2sh (a: Payment, opts?: PaymentOpts): Payment {
|
||||||
a.output.length !== 23 ||
|
a.output.length !== 23 ||
|
||||||
a.output[0] !== OPS.OP_HASH160 ||
|
a.output[0] !== OPS.OP_HASH160 ||
|
||||||
a.output[1] !== 0x14 ||
|
a.output[1] !== 0x14 ||
|
||||||
a.output[22] !== OPS.OP_EQUAL) throw new TypeError('Output is invalid')
|
a.output[22] !== OPS.OP_EQUAL
|
||||||
|
)
|
||||||
|
throw new TypeError('Output is invalid');
|
||||||
|
|
||||||
const hash2 = a.output.slice(2, 22)
|
const hash2 = a.output.slice(2, 22);
|
||||||
if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(hash2))
|
||||||
else hash = hash2
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = hash2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// inlined to prevent 'no-inner-declarations' failing
|
// inlined to prevent 'no-inner-declarations' failing
|
||||||
const checkRedeem = function (redeem: Payment): void {
|
const checkRedeem = function(redeem: Payment): void {
|
||||||
// is the redeem output empty/invalid?
|
// is the redeem output empty/invalid?
|
||||||
if (redeem.output) {
|
if (redeem.output) {
|
||||||
const decompile = bscript.decompile(redeem.output)
|
const decompile = bscript.decompile(redeem.output);
|
||||||
if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short')
|
if (!decompile || decompile.length < 1)
|
||||||
|
throw new TypeError('Redeem.output too short');
|
||||||
|
|
||||||
// match hash against other sources
|
// match hash against other sources
|
||||||
const hash2 = bcrypto.hash160(redeem.output)
|
const hash2 = bcrypto.hash160(redeem.output);
|
||||||
if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(hash2))
|
||||||
else hash = hash2
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = hash2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redeem.input) {
|
if (redeem.input) {
|
||||||
const hasInput = redeem.input.length > 0
|
const hasInput = redeem.input.length > 0;
|
||||||
const hasWitness = redeem.witness && redeem.witness.length > 0
|
const hasWitness = redeem.witness && redeem.witness.length > 0;
|
||||||
if (!hasInput && !hasWitness) throw new TypeError('Empty input')
|
if (!hasInput && !hasWitness) throw new TypeError('Empty input');
|
||||||
if (hasInput && hasWitness) throw new TypeError('Input and witness provided')
|
if (hasInput && hasWitness)
|
||||||
|
throw new TypeError('Input and witness provided');
|
||||||
if (hasInput) {
|
if (hasInput) {
|
||||||
const richunks = <Array<Buffer | number>>bscript.decompile(redeem.input)
|
const richunks = <Array<Buffer | number>>(
|
||||||
if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig')
|
bscript.decompile(redeem.input)
|
||||||
|
);
|
||||||
|
if (!bscript.isPushOnly(richunks))
|
||||||
|
throw new TypeError('Non push-only scriptSig');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (a.input) {
|
if (a.input) {
|
||||||
const chunks = _chunks()
|
const chunks = _chunks();
|
||||||
if (!chunks || chunks.length < 1) throw new TypeError('Input too short')
|
if (!chunks || chunks.length < 1) throw new TypeError('Input too short');
|
||||||
if (!Buffer.isBuffer(_redeem().output)) throw new TypeError('Input is invalid')
|
if (!Buffer.isBuffer(_redeem().output))
|
||||||
|
throw new TypeError('Input is invalid');
|
||||||
|
|
||||||
checkRedeem(_redeem())
|
checkRedeem(_redeem());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.redeem) {
|
if (a.redeem) {
|
||||||
if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch')
|
if (a.redeem.network && a.redeem.network !== network)
|
||||||
|
throw new TypeError('Network mismatch');
|
||||||
if (a.input) {
|
if (a.input) {
|
||||||
const redeem = _redeem()
|
const redeem = _redeem();
|
||||||
if (a.redeem.output && !a.redeem.output.equals(redeem.output!)) throw new TypeError('Redeem.output mismatch')
|
if (a.redeem.output && !a.redeem.output.equals(redeem.output!))
|
||||||
if (a.redeem.input && !a.redeem.input.equals(redeem.input!)) throw new TypeError('Redeem.input mismatch')
|
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)
|
checkRedeem(a.redeem);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.witness) {
|
if (a.witness) {
|
||||||
if (
|
if (
|
||||||
a.redeem &&
|
a.redeem &&
|
||||||
a.redeem.witness &&
|
a.redeem.witness &&
|
||||||
!stacksEqual(a.redeem.witness, a.witness)) throw new TypeError('Witness and redeem.witness mismatch')
|
!stacksEqual(a.redeem.witness, a.witness)
|
||||||
|
)
|
||||||
|
throw new TypeError('Witness and redeem.witness mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(o, a)
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,134 +1,142 @@
|
||||||
import { Payment, PaymentOpts } from './index' // eslint-disable-line
|
import { Payment, PaymentOpts } from './index';
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
import * as bcrypto from '../crypto'
|
import * as bcrypto from '../crypto';
|
||||||
import * as lazy from './lazy'
|
import * as lazy from './lazy';
|
||||||
import { bitcoin as BITCOIN_NETWORK } from '../networks'
|
import { bitcoin as BITCOIN_NETWORK } from '../networks';
|
||||||
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');
|
||||||
|
|
||||||
const bech32 = require('bech32')
|
const bech32 = require('bech32');
|
||||||
|
|
||||||
const EMPTY_BUFFER = Buffer.alloc(0)
|
const EMPTY_BUFFER = Buffer.alloc(0);
|
||||||
|
|
||||||
// witness: {signature} {pubKey}
|
// witness: {signature} {pubKey}
|
||||||
// input: <>
|
// input: <>
|
||||||
// output: OP_0 {pubKeyHash}
|
// output: OP_0 {pubKeyHash}
|
||||||
export function p2wpkh (a: Payment, opts?: PaymentOpts): Payment {
|
export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
|
||||||
if (
|
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
|
||||||
!a.address &&
|
throw new TypeError('Not enough data');
|
||||||
!a.hash &&
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
!a.output &&
|
|
||||||
!a.pubkey &&
|
|
||||||
!a.witness
|
|
||||||
) throw new TypeError('Not enough data')
|
|
||||||
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)),
|
||||||
|
},
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
|
||||||
const _address = lazy.value(function () {
|
const _address = lazy.value(function() {
|
||||||
const result = bech32.decode(a.address)
|
const result = bech32.decode(a.address);
|
||||||
const version = result.words.shift()
|
const version = result.words.shift();
|
||||||
const data = bech32.fromWords(result.words)
|
const data = bech32.fromWords(result.words);
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
prefix: result.prefix,
|
prefix: result.prefix,
|
||||||
data: Buffer.from(data)
|
data: Buffer.from(data),
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
const network = a.network || BITCOIN_NETWORK
|
const network = a.network || BITCOIN_NETWORK;
|
||||||
const o: Payment = { network }
|
const o: Payment = { network };
|
||||||
|
|
||||||
lazy.prop(o, 'address', function () {
|
lazy.prop(o, 'address', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
|
|
||||||
const words = bech32.toWords(o.hash)
|
const words = bech32.toWords(o.hash);
|
||||||
words.unshift(0x00)
|
words.unshift(0x00);
|
||||||
return bech32.encode(network.bech32, words)
|
return bech32.encode(network.bech32, words);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'hash', function () {
|
lazy.prop(o, 'hash', function() {
|
||||||
if (a.output) return a.output.slice(2, 22)
|
if (a.output) return a.output.slice(2, 22);
|
||||||
if (a.address) return _address().data
|
if (a.address) return _address().data;
|
||||||
if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!)
|
if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
return bscript.compile([
|
return bscript.compile([OPS.OP_0, o.hash]);
|
||||||
OPS.OP_0,
|
});
|
||||||
o.hash
|
lazy.prop(o, 'pubkey', function() {
|
||||||
])
|
if (a.pubkey) return a.pubkey;
|
||||||
})
|
if (!a.witness) return;
|
||||||
lazy.prop(o, 'pubkey', function () {
|
return a.witness[1];
|
||||||
if (a.pubkey) return a.pubkey
|
});
|
||||||
if (!a.witness) return
|
lazy.prop(o, 'signature', function() {
|
||||||
return a.witness[1]
|
if (!a.witness) return;
|
||||||
})
|
return a.witness[0];
|
||||||
lazy.prop(o, 'signature', function () {
|
});
|
||||||
if (!a.witness) return
|
lazy.prop(o, 'input', function() {
|
||||||
return a.witness[0]
|
if (!o.witness) return;
|
||||||
})
|
return EMPTY_BUFFER;
|
||||||
lazy.prop(o, 'input', function () {
|
});
|
||||||
if (!o.witness) return
|
lazy.prop(o, 'witness', function() {
|
||||||
return EMPTY_BUFFER
|
if (!a.pubkey) return;
|
||||||
})
|
if (!a.signature) return;
|
||||||
lazy.prop(o, 'witness', function () {
|
return [a.signature, a.pubkey];
|
||||||
if (!a.pubkey) return
|
});
|
||||||
if (!a.signature) return
|
|
||||||
return [a.signature, a.pubkey]
|
|
||||||
})
|
|
||||||
|
|
||||||
// extended validation
|
// extended validation
|
||||||
if (opts.validate) {
|
if (opts.validate) {
|
||||||
let hash: Buffer = Buffer.from([])
|
let hash: Buffer = Buffer.from([]);
|
||||||
if (a.address) {
|
if (a.address) {
|
||||||
if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch')
|
if (network && network.bech32 !== _address().prefix)
|
||||||
if (_address().version !== 0x00) throw new TypeError('Invalid address version')
|
throw new TypeError('Invalid prefix or Network mismatch');
|
||||||
if (_address().data.length !== 20) throw new TypeError('Invalid address data')
|
if (_address().version !== 0x00)
|
||||||
hash = _address().data
|
throw new TypeError('Invalid address version');
|
||||||
|
if (_address().data.length !== 20)
|
||||||
|
throw new TypeError('Invalid address data');
|
||||||
|
hash = _address().data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.hash) {
|
if (a.hash) {
|
||||||
if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(a.hash))
|
||||||
else hash = a.hash
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = a.hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.output) {
|
if (a.output) {
|
||||||
if (
|
if (
|
||||||
a.output.length !== 22 ||
|
a.output.length !== 22 ||
|
||||||
a.output[0] !== OPS.OP_0 ||
|
a.output[0] !== OPS.OP_0 ||
|
||||||
a.output[1] !== 0x14) throw new TypeError('Output is invalid')
|
a.output[1] !== 0x14
|
||||||
if (hash.length > 0 && !hash.equals(a.output.slice(2))) throw new TypeError('Hash mismatch')
|
)
|
||||||
else hash = a.output.slice(2)
|
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) {
|
if (a.pubkey) {
|
||||||
const pkh = bcrypto.hash160(a.pubkey)
|
const pkh = bcrypto.hash160(a.pubkey);
|
||||||
if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(pkh))
|
||||||
else hash = pkh
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = pkh;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.witness) {
|
if (a.witness) {
|
||||||
if (a.witness.length !== 2) throw new TypeError('Witness is invalid')
|
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 (!bscript.isCanonicalScriptSignature(a.witness[0]))
|
||||||
if (!ecc.isPoint(a.witness[1])) throw new TypeError('Witness has invalid pubkey')
|
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.signature && !a.signature.equals(a.witness[0]))
|
||||||
if (a.pubkey && !a.pubkey.equals(a.witness[1])) throw new TypeError('Pubkey mismatch')
|
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])
|
const pkh = bcrypto.hash160(a.witness[1]);
|
||||||
if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(pkh))
|
||||||
|
throw new TypeError('Hash mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(o, a)
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,103 +1,100 @@
|
||||||
import { Payment, PaymentOpts } from './index' // eslint-disable-line
|
import { Payment, PaymentOpts } from './index';
|
||||||
import { bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line
|
import { bitcoin as BITCOIN_NETWORK } from '../networks';
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
import * as bcrypto from '../crypto'
|
import * as bcrypto from '../crypto';
|
||||||
import * as lazy from './lazy'
|
import * as lazy from './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: Array<Buffer>, b: Array<Buffer>): boolean {
|
function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean {
|
||||||
if (a.length !== b.length) return false
|
if (a.length !== b.length) return false;
|
||||||
|
|
||||||
return a.every(function (x, i) {
|
return a.every(function(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)}
|
||||||
export function p2wsh (a: Payment, opts?: PaymentOpts): Payment {
|
export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
|
||||||
if (
|
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
|
||||||
!a.address &&
|
throw new TypeError('Not enough data');
|
||||||
!a.hash &&
|
opts = Object.assign({ validate: true }, opts || {});
|
||||||
!a.output &&
|
|
||||||
!a.redeem &&
|
|
||||||
!a.witness
|
|
||||||
) throw new TypeError('Not enough data')
|
|
||||||
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),
|
||||||
output: typef.maybe(typef.Buffer),
|
|
||||||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
|
||||||
}),
|
|
||||||
input: typef.maybe(typef.BufferN(0)),
|
|
||||||
witness: typef.maybe(typef.arrayOf(typef.Buffer))
|
|
||||||
}, a)
|
|
||||||
|
|
||||||
const _address = lazy.value(function () {
|
address: typef.maybe(typef.String),
|
||||||
const result = bech32.decode(a.address)
|
hash: typef.maybe(typef.BufferN(32)),
|
||||||
const version = result.words.shift()
|
output: typef.maybe(typef.BufferN(34)),
|
||||||
const data = bech32.fromWords(result.words)
|
|
||||||
|
redeem: typef.maybe({
|
||||||
|
input: typef.maybe(typef.Buffer),
|
||||||
|
network: typef.maybe(typef.Object),
|
||||||
|
output: typef.maybe(typef.Buffer),
|
||||||
|
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
|
}),
|
||||||
|
input: typef.maybe(typef.BufferN(0)),
|
||||||
|
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
||||||
|
},
|
||||||
|
a,
|
||||||
|
);
|
||||||
|
|
||||||
|
const _address = lazy.value(function() {
|
||||||
|
const result = bech32.decode(a.address);
|
||||||
|
const version = result.words.shift();
|
||||||
|
const data = bech32.fromWords(result.words);
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
prefix: result.prefix,
|
prefix: result.prefix,
|
||||||
data: Buffer.from(data)
|
data: Buffer.from(data),
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
const _rchunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.redeem!.input!) })
|
const _rchunks = <() => Array<Buffer | number>>lazy.value(function() {
|
||||||
|
return bscript.decompile(a.redeem!.input!);
|
||||||
|
});
|
||||||
|
|
||||||
let network = a.network
|
let network = a.network;
|
||||||
if (!network) {
|
if (!network) {
|
||||||
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK
|
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const o: Payment = { network }
|
const o: Payment = { network };
|
||||||
|
|
||||||
lazy.prop(o, 'address', function () {
|
lazy.prop(o, 'address', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
const words = bech32.toWords(o.hash)
|
const words = bech32.toWords(o.hash);
|
||||||
words.unshift(0x00)
|
words.unshift(0x00);
|
||||||
return bech32.encode(network!.bech32, words)
|
return bech32.encode(network!.bech32, words);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'hash', function () {
|
lazy.prop(o, 'hash', function() {
|
||||||
if (a.output) return a.output.slice(2)
|
if (a.output) return a.output.slice(2);
|
||||||
if (a.address) return _address().data
|
if (a.address) return _address().data;
|
||||||
if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output)
|
if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output);
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'output', function () {
|
lazy.prop(o, 'output', function() {
|
||||||
if (!o.hash) return
|
if (!o.hash) return;
|
||||||
return bscript.compile([
|
return bscript.compile([OPS.OP_0, o.hash]);
|
||||||
OPS.OP_0,
|
});
|
||||||
o.hash
|
lazy.prop(o, 'redeem', function() {
|
||||||
])
|
if (!a.witness) return;
|
||||||
})
|
|
||||||
lazy.prop(o, 'redeem', function () {
|
|
||||||
if (!a.witness) return
|
|
||||||
return {
|
return {
|
||||||
output: a.witness[a.witness.length - 1],
|
output: a.witness[a.witness.length - 1],
|
||||||
input: EMPTY_BUFFER,
|
input: EMPTY_BUFFER,
|
||||||
witness: a.witness.slice(0, -1)
|
witness: a.witness.slice(0, -1),
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'input', function () {
|
lazy.prop(o, 'input', function() {
|
||||||
if (!o.witness) return
|
if (!o.witness) return;
|
||||||
return EMPTY_BUFFER
|
return EMPTY_BUFFER;
|
||||||
})
|
});
|
||||||
lazy.prop(o, 'witness', function () {
|
lazy.prop(o, 'witness', function() {
|
||||||
// transform redeem input to witness stack?
|
// transform redeem input to witness stack?
|
||||||
if (
|
if (
|
||||||
a.redeem &&
|
a.redeem &&
|
||||||
|
@ -106,47 +103,55 @@ export function p2wsh (a: Payment, opts?: PaymentOpts): Payment {
|
||||||
a.redeem.output &&
|
a.redeem.output &&
|
||||||
a.redeem.output.length > 0
|
a.redeem.output.length > 0
|
||||||
) {
|
) {
|
||||||
const stack = bscript.toStack(_rchunks())
|
const stack = bscript.toStack(_rchunks());
|
||||||
|
|
||||||
// assign, and blank the existing input
|
// assign, and blank the existing input
|
||||||
o.redeem = Object.assign({ witness: stack }, a.redeem)
|
o.redeem = Object.assign({ witness: stack }, a.redeem);
|
||||||
o.redeem.input = EMPTY_BUFFER
|
o.redeem.input = EMPTY_BUFFER;
|
||||||
return (<Array<Buffer>>[]).concat(stack, a.redeem.output)
|
return (<Array<Buffer>>[]).concat(stack, a.redeem.output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!a.redeem) return
|
if (!a.redeem) return;
|
||||||
if (!a.redeem.output) return
|
if (!a.redeem.output) return;
|
||||||
if (!a.redeem.witness) return
|
if (!a.redeem.witness) return;
|
||||||
return (<Array<Buffer>>[]).concat(a.redeem.witness, a.redeem.output)
|
return (<Array<Buffer>>[]).concat(a.redeem.witness, a.redeem.output);
|
||||||
})
|
});
|
||||||
|
|
||||||
// extended validation
|
// extended validation
|
||||||
if (opts.validate) {
|
if (opts.validate) {
|
||||||
let hash: Buffer = Buffer.from([])
|
let hash: Buffer = Buffer.from([]);
|
||||||
if (a.address) {
|
if (a.address) {
|
||||||
if (_address().prefix !== network.bech32) throw new TypeError('Invalid prefix or Network mismatch')
|
if (_address().prefix !== network.bech32)
|
||||||
if (_address().version !== 0x00) throw new TypeError('Invalid address version')
|
throw new TypeError('Invalid prefix or Network mismatch');
|
||||||
if (_address().data.length !== 32) throw new TypeError('Invalid address data')
|
if (_address().version !== 0x00)
|
||||||
hash = _address().data
|
throw new TypeError('Invalid address version');
|
||||||
|
if (_address().data.length !== 32)
|
||||||
|
throw new TypeError('Invalid address data');
|
||||||
|
hash = _address().data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.hash) {
|
if (a.hash) {
|
||||||
if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(a.hash))
|
||||||
else hash = a.hash
|
throw new TypeError('Hash mismatch');
|
||||||
|
else hash = a.hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.output) {
|
if (a.output) {
|
||||||
if (
|
if (
|
||||||
a.output.length !== 34 ||
|
a.output.length !== 34 ||
|
||||||
a.output[0] !== OPS.OP_0 ||
|
a.output[0] !== OPS.OP_0 ||
|
||||||
a.output[1] !== 0x20) throw new TypeError('Output is invalid')
|
a.output[1] !== 0x20
|
||||||
const hash2 = a.output.slice(2)
|
)
|
||||||
if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch')
|
throw new TypeError('Output is invalid');
|
||||||
else hash = hash2
|
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) {
|
||||||
if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch')
|
if (a.redeem.network && a.redeem.network !== network)
|
||||||
|
throw new TypeError('Network mismatch');
|
||||||
|
|
||||||
// is there two redeem sources?
|
// is there two redeem sources?
|
||||||
if (
|
if (
|
||||||
|
@ -154,26 +159,40 @@ export function p2wsh (a: Payment, opts?: PaymentOpts): Payment {
|
||||||
a.redeem.input.length > 0 &&
|
a.redeem.input.length > 0 &&
|
||||||
a.redeem.witness &&
|
a.redeem.witness &&
|
||||||
a.redeem.witness.length > 0
|
a.redeem.witness.length > 0
|
||||||
) throw new TypeError('Ambiguous witness source')
|
)
|
||||||
|
throw new TypeError('Ambiguous witness source');
|
||||||
|
|
||||||
// is the redeem output non-empty?
|
// is the redeem output non-empty?
|
||||||
if (a.redeem.output) {
|
if (a.redeem.output) {
|
||||||
if (bscript.decompile(a.redeem.output)!.length === 0) throw new TypeError('Redeem.output is invalid')
|
if (bscript.decompile(a.redeem.output)!.length === 0)
|
||||||
|
throw new TypeError('Redeem.output is invalid');
|
||||||
|
|
||||||
// match hash against other sources
|
// match hash against other sources
|
||||||
const hash2 = bcrypto.sha256(a.redeem.output)
|
const hash2 = bcrypto.sha256(a.redeem.output);
|
||||||
if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch')
|
if (hash.length > 0 && !hash.equals(hash2))
|
||||||
else hash = 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.redeem.input && !bscript.isPushOnly(_rchunks()))
|
||||||
if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness)) throw new TypeError('Witness and redeem.witness mismatch')
|
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.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')
|
if (
|
||||||
|
a.redeem &&
|
||||||
|
a.redeem.output &&
|
||||||
|
!a.redeem.output.equals(a.witness[a.witness.length - 1])
|
||||||
|
)
|
||||||
|
throw new TypeError('Witness and redeem.output mismatch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(o, a)
|
return Object.assign(o, a);
|
||||||
}
|
}
|
||||||
|
|
248
ts_src/script.ts
248
ts_src/script.ts
|
@ -1,206 +1,218 @@
|
||||||
import * as types from './types'
|
import * as types from './types';
|
||||||
import * as scriptNumber from './script_number'
|
import * as scriptNumber from './script_number';
|
||||||
import * as scriptSignature from './script_signature'
|
import * as scriptSignature from './script_signature';
|
||||||
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');
|
||||||
const typeforce = require('typeforce')
|
const typeforce = require('typeforce');
|
||||||
|
|
||||||
export type OpCode = number
|
export type OpCode = number;
|
||||||
export const OPS = <{[index:string]: OpCode}> require('bitcoin-ops')
|
export const OPS = <{ [index: string]: OpCode }>require('bitcoin-ops');
|
||||||
|
|
||||||
const REVERSE_OPS = <{[index:number]: string}> require('bitcoin-ops/map')
|
const REVERSE_OPS = <{ [index: number]: string }>require('bitcoin-ops/map');
|
||||||
const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
|
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
|
||||||
|
|
||||||
function isOPInt (value:number): boolean {
|
function isOPInt(value: number): boolean {
|
||||||
return types.Number(value) &&
|
return (
|
||||||
((value === OPS.OP_0) ||
|
types.Number(value) &&
|
||||||
(value >= OPS.OP_1 && value <= OPS.OP_16) ||
|
(value === OPS.OP_0 ||
|
||||||
(value === OPS.OP_1NEGATE))
|
(value >= OPS.OP_1 && value <= OPS.OP_16) ||
|
||||||
|
value === OPS.OP_1NEGATE)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPushOnlyChunk (value: number | Buffer): boolean {
|
function isPushOnlyChunk(value: number | Buffer): boolean {
|
||||||
return types.Buffer(value) || isOPInt(<number>value)
|
return types.Buffer(value) || isOPInt(<number>value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPushOnly (value: Array<number | Buffer>) {
|
export function isPushOnly(value: Array<number | Buffer>) {
|
||||||
return types.Array(value) && value.every(isPushOnlyChunk)
|
return types.Array(value) && value.every(isPushOnlyChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
function asMinimalOP (buffer: Buffer): number | void {
|
function asMinimalOP(buffer: Buffer): number | void {
|
||||||
if (buffer.length === 0) return OPS.OP_0
|
if (buffer.length === 0) return OPS.OP_0;
|
||||||
if (buffer.length !== 1) return
|
if (buffer.length !== 1) return;
|
||||||
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0]
|
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
|
||||||
if (buffer[0] === 0x81) return OPS.OP_1NEGATE
|
if (buffer[0] === 0x81) return OPS.OP_1NEGATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
function chunksIsBuffer(buf: Buffer | Array<number | Buffer>): buf is Buffer {
|
function chunksIsBuffer(buf: Buffer | Array<number | Buffer>): buf is Buffer {
|
||||||
return Buffer.isBuffer(buf)
|
return Buffer.isBuffer(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
function chunksIsArray(buf: Buffer | Array<number | Buffer>): buf is Array<number | Buffer> {
|
function chunksIsArray(
|
||||||
return types.Array(buf)
|
buf: Buffer | Array<number | Buffer>,
|
||||||
|
): buf is Array<number | Buffer> {
|
||||||
|
return types.Array(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
function singleChunkIsBuffer(buf: number | Buffer): buf is Buffer {
|
function singleChunkIsBuffer(buf: number | Buffer): buf is Buffer {
|
||||||
return Buffer.isBuffer(buf)
|
return Buffer.isBuffer(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compile (chunks: Buffer | Array<number | Buffer>): Buffer {
|
export function compile(chunks: Buffer | Array<number | Buffer>): Buffer {
|
||||||
// TODO: remove me
|
// TODO: remove me
|
||||||
if (chunksIsBuffer(chunks)) return chunks
|
if (chunksIsBuffer(chunks)) return chunks;
|
||||||
|
|
||||||
typeforce(types.Array, chunks)
|
typeforce(types.Array, chunks);
|
||||||
|
|
||||||
const bufferSize = chunks.reduce(function (accum: number, chunk) {
|
const bufferSize = chunks.reduce(function(accum: number, 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 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return accum + pushdata.encodingLength(chunk.length) + chunk.length
|
return accum + pushdata.encodingLength(chunk.length) + chunk.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// opcode
|
// opcode
|
||||||
return accum + 1
|
return accum + 1;
|
||||||
}, 0.0)
|
}, 0.0);
|
||||||
|
|
||||||
const buffer = Buffer.allocUnsafe(bufferSize)
|
const buffer = Buffer.allocUnsafe(bufferSize);
|
||||||
let offset = 0
|
let offset = 0;
|
||||||
|
|
||||||
chunks.forEach(function (chunk) {
|
chunks.forEach(function(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
|
||||||
const opcode = asMinimalOP(chunk)
|
const opcode = asMinimalOP(chunk);
|
||||||
if (opcode !== undefined) {
|
if (opcode !== undefined) {
|
||||||
buffer.writeUInt8(opcode, offset)
|
buffer.writeUInt8(opcode, offset);
|
||||||
offset += 1
|
offset += 1;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += pushdata.encode(buffer, chunk.length, offset)
|
offset += pushdata.encode(buffer, chunk.length, offset);
|
||||||
chunk.copy(buffer, offset)
|
chunk.copy(buffer, offset);
|
||||||
offset += chunk.length
|
offset += chunk.length;
|
||||||
|
|
||||||
// opcode
|
// opcode
|
||||||
} else {
|
} else {
|
||||||
buffer.writeUInt8(chunk, offset)
|
buffer.writeUInt8(chunk, offset);
|
||||||
offset += 1
|
offset += 1;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
if (offset !== buffer.length) throw new Error('Could not decode chunks')
|
if (offset !== buffer.length) throw new Error('Could not decode chunks');
|
||||||
return buffer
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decompile (buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> | null {
|
export function decompile(
|
||||||
|
buffer: Buffer | Array<number | Buffer>,
|
||||||
|
): Array<number | Buffer> | null {
|
||||||
// TODO: remove me
|
// TODO: remove me
|
||||||
if (chunksIsArray(buffer)) return buffer
|
if (chunksIsArray(buffer)) return buffer;
|
||||||
|
|
||||||
typeforce(types.Buffer, buffer)
|
typeforce(types.Buffer, buffer);
|
||||||
|
|
||||||
const chunks: Array<number | Buffer> = []
|
const chunks: Array<number | Buffer> = [];
|
||||||
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 > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) {
|
if (opcode > OPS.OP_0 && opcode <= 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) return null;
|
||||||
i += d.size
|
i += d.size;
|
||||||
|
|
||||||
// attempt to read too much data?
|
// attempt to read too much data?
|
||||||
if (i + d.number > buffer.length) return null
|
if (i + d.number > buffer.length) return null;
|
||||||
|
|
||||||
const data = buffer.slice(i, i + d.number)
|
const data = buffer.slice(i, i + d.number);
|
||||||
i += d.number
|
i += d.number;
|
||||||
|
|
||||||
// decompile minimally
|
// decompile minimally
|
||||||
const op = asMinimalOP(data)
|
const op = asMinimalOP(data);
|
||||||
if (op !== undefined) {
|
if (op !== undefined) {
|
||||||
chunks.push(op)
|
chunks.push(op);
|
||||||
} else {
|
} else {
|
||||||
chunks.push(data)
|
chunks.push(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// opcode
|
// opcode
|
||||||
} else {
|
} else {
|
||||||
chunks.push(opcode)
|
chunks.push(opcode);
|
||||||
|
|
||||||
i += 1
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toASM(chunks: Buffer | Array<number | Buffer>): string {
|
||||||
|
if (chunksIsBuffer(chunks)) {
|
||||||
|
chunks = <Array<number | Buffer>>decompile(chunks);
|
||||||
|
}
|
||||||
|
|
||||||
return chunks
|
return chunks
|
||||||
|
.map(function(chunk) {
|
||||||
|
// data?
|
||||||
|
if (singleChunkIsBuffer(chunk)) {
|
||||||
|
const op = asMinimalOP(chunk);
|
||||||
|
if (op === undefined) return chunk.toString('hex');
|
||||||
|
chunk = <number>op;
|
||||||
|
}
|
||||||
|
|
||||||
|
// opcode!
|
||||||
|
return REVERSE_OPS[chunk];
|
||||||
|
})
|
||||||
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toASM (chunks: Buffer | Array<number | Buffer>): string {
|
export function fromASM(asm: string): Buffer {
|
||||||
if (chunksIsBuffer(chunks)) {
|
typeforce(types.String, asm);
|
||||||
chunks = <Array<number | Buffer>>decompile(chunks)
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunks.map(function (chunk) {
|
return compile(
|
||||||
// data?
|
asm.split(' ').map(function(chunkStr) {
|
||||||
if (singleChunkIsBuffer(chunk)) {
|
// opcode?
|
||||||
const op = asMinimalOP(chunk)
|
if (OPS[chunkStr] !== undefined) return OPS[chunkStr];
|
||||||
if (op === undefined) return chunk.toString('hex')
|
typeforce(types.Hex, chunkStr);
|
||||||
chunk = <number>op
|
|
||||||
}
|
|
||||||
|
|
||||||
// opcode!
|
// data!
|
||||||
return REVERSE_OPS[chunk]
|
return Buffer.from(chunkStr, 'hex');
|
||||||
}).join(' ')
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromASM (asm: string): Buffer {
|
export function toStack(
|
||||||
typeforce(types.String, asm)
|
chunks: Buffer | Array<number | Buffer>,
|
||||||
|
): Array<Buffer> {
|
||||||
|
chunks = <Array<number | Buffer>>decompile(chunks);
|
||||||
|
typeforce(isPushOnly, chunks);
|
||||||
|
|
||||||
return compile(asm.split(' ').map(function (chunkStr) {
|
return chunks.map(function(op) {
|
||||||
// opcode?
|
if (singleChunkIsBuffer(op)) return op;
|
||||||
if (OPS[chunkStr] !== undefined) return OPS[chunkStr]
|
if (op === OPS.OP_0) return Buffer.allocUnsafe(0);
|
||||||
typeforce(types.Hex, chunkStr)
|
|
||||||
|
|
||||||
// data!
|
return scriptNumber.encode(op - OP_INT_BASE);
|
||||||
return Buffer.from(chunkStr, 'hex')
|
});
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toStack (chunks: Buffer | Array<number | Buffer>): Array<Buffer> {
|
export function isCanonicalPubKey(buffer: Buffer): boolean {
|
||||||
chunks = <Array<number | Buffer>>decompile(chunks)
|
return ecc.isPoint(buffer);
|
||||||
typeforce(isPushOnly, chunks)
|
|
||||||
|
|
||||||
return chunks.map(function (op) {
|
|
||||||
if (singleChunkIsBuffer(op)) return op
|
|
||||||
if (op === OPS.OP_0) return Buffer.allocUnsafe(0)
|
|
||||||
|
|
||||||
return scriptNumber.encode(op - OP_INT_BASE)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCanonicalPubKey (buffer: Buffer): boolean {
|
export function isDefinedHashType(hashType: number): boolean {
|
||||||
return ecc.isPoint(buffer)
|
const hashTypeMod = hashType & ~0x80;
|
||||||
}
|
|
||||||
|
|
||||||
export function isDefinedHashType (hashType: number): boolean {
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCanonicalScriptSignature (buffer: Buffer): boolean {
|
export function isCanonicalScriptSignature(buffer: Buffer): boolean {
|
||||||
if (!Buffer.isBuffer(buffer)) return false
|
if (!Buffer.isBuffer(buffer)) return false;
|
||||||
if (!isDefinedHashType(buffer[buffer.length - 1])) return false
|
if (!isDefinedHashType(buffer[buffer.length - 1])) return false;
|
||||||
|
|
||||||
return bip66.check(buffer.slice(0, -1))
|
return bip66.check(buffer.slice(0, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const number = scriptNumber
|
export const number = scriptNumber;
|
||||||
export const signature = scriptSignature
|
export const signature = scriptSignature;
|
||||||
|
|
|
@ -1,62 +1,71 @@
|
||||||
|
export function decode(
|
||||||
|
buffer: Buffer,
|
||||||
|
maxLength?: number,
|
||||||
|
minimal?: boolean,
|
||||||
|
): number {
|
||||||
|
maxLength = maxLength || 4;
|
||||||
|
minimal = minimal === undefined ? true : minimal;
|
||||||
|
|
||||||
|
const length = buffer.length;
|
||||||
export function decode (buffer: Buffer, maxLength?: number, minimal?: boolean): number {
|
if (length === 0) return 0;
|
||||||
maxLength = maxLength || 4
|
if (length > maxLength) throw new TypeError('Script number overflow');
|
||||||
minimal = minimal === undefined ? true : minimal
|
|
||||||
|
|
||||||
const length = buffer.length
|
|
||||||
if (length === 0) return 0
|
|
||||||
if (length > maxLength) throw new TypeError('Script number overflow')
|
|
||||||
if (minimal) {
|
if (minimal) {
|
||||||
if ((buffer[length - 1] & 0x7f) === 0) {
|
if ((buffer[length - 1] & 0x7f) === 0) {
|
||||||
if (length <= 1 || (buffer[length - 2] & 0x80) === 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
|
// 40-bit
|
||||||
if (length === 5) {
|
if (length === 5) {
|
||||||
const a = buffer.readUInt32LE(0)
|
const a = buffer.readUInt32LE(0);
|
||||||
const b = buffer.readUInt8(4)
|
const b = buffer.readUInt8(4);
|
||||||
|
|
||||||
if (b & 0x80) 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 (var i = 0; i < length; ++i) {
|
for (var i = 0; i < length; ++i) {
|
||||||
result |= buffer[i] << (8 * i)
|
result |= buffer[i] << (8 * i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer[length - 1] & 0x80) return -(result & ~(0x80 << (8 * (length - 1))))
|
if (buffer[length - 1] & 0x80)
|
||||||
return result
|
return -(result & ~(0x80 << (8 * (length - 1))));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function scriptNumSize (i: number): number {
|
function scriptNumSize(i: number): number {
|
||||||
return i > 0x7fffffff ? 5
|
return i > 0x7fffffff
|
||||||
: i > 0x7fffff ? 4
|
? 5
|
||||||
: i > 0x7fff ? 3
|
: i > 0x7fffff
|
||||||
: i > 0x7f ? 2
|
? 4
|
||||||
: i > 0x00 ? 1
|
: i > 0x7fff
|
||||||
: 0
|
? 3
|
||||||
|
: i > 0x7f
|
||||||
|
? 2
|
||||||
|
: i > 0x00
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encode (number: number): Buffer {
|
export function encode(number: number): Buffer {
|
||||||
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 (var i = 0; i < size; ++i) {
|
for (var 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,66 @@
|
||||||
import * as types from './types'
|
import * as types from './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: Buffer): Buffer {
|
function toDER(x: Buffer): Buffer {
|
||||||
let i = 0
|
let i = 0;
|
||||||
while (x[i] === 0) ++i
|
while (x[i] === 0) ++i;
|
||||||
if (i === x.length) return ZERO
|
if (i === x.length) return ZERO;
|
||||||
x = x.slice(i)
|
x = x.slice(i);
|
||||||
if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length)
|
if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length);
|
||||||
return x
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromDER (x: Buffer): Buffer {
|
function fromDER(x: Buffer): Buffer {
|
||||||
if (x[0] === 0x00) x = x.slice(1)
|
if (x[0] === 0x00) 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScriptSignature {
|
interface ScriptSignature {
|
||||||
signature: Buffer
|
signature: Buffer;
|
||||||
hashType: number
|
hashType: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
export function decode (buffer: Buffer): ScriptSignature {
|
export function decode(buffer: Buffer): ScriptSignature {
|
||||||
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) throw new Error('Invalid hashType ' + hashType)
|
if (hashTypeMod <= 0 || hashTypeMod >= 4)
|
||||||
|
throw new Error('Invalid hashType ' + hashType);
|
||||||
|
|
||||||
const decode = bip66.decode(buffer.slice(0, -1))
|
const decode = bip66.decode(buffer.slice(0, -1));
|
||||||
const r = fromDER(decode.r)
|
const r = fromDER(decode.r);
|
||||||
const s = fromDER(decode.s)
|
const s = fromDER(decode.s);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
signature: Buffer.concat([r, s], 64),
|
signature: Buffer.concat([r, s], 64),
|
||||||
hashType: hashType
|
hashType: hashType,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encode (signature: Buffer, hashType: number): Buffer {
|
export function encode(signature: Buffer, hashType: number): Buffer {
|
||||||
typeforce({
|
typeforce(
|
||||||
signature: types.BufferN(64),
|
{
|
||||||
hashType: types.UInt8
|
signature: types.BufferN(64),
|
||||||
}, { signature, hashType })
|
hashType: types.UInt8,
|
||||||
|
},
|
||||||
|
{ signature, hashType },
|
||||||
|
);
|
||||||
|
|
||||||
const hashTypeMod = hashType & ~0x80
|
const hashTypeMod = hashType & ~0x80;
|
||||||
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)
|
if (hashTypeMod <= 0 || hashTypeMod >= 4)
|
||||||
|
throw new Error('Invalid hashType ' + hashType);
|
||||||
|
|
||||||
const hashTypeBuffer = Buffer.allocUnsafe(1)
|
const hashTypeBuffer = Buffer.allocUnsafe(1);
|
||||||
hashTypeBuffer.writeUInt8(hashType, 0)
|
hashTypeBuffer.writeUInt8(hashType, 0);
|
||||||
|
|
||||||
const r = toDER(signature.slice(0, 32))
|
const r = toDER(signature.slice(0, 32));
|
||||||
const s = toDER(signature.slice(32, 64))
|
const s = toDER(signature.slice(32, 64));
|
||||||
|
|
||||||
return Buffer.concat([
|
return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]);
|
||||||
bip66.encode(r, s),
|
|
||||||
hashTypeBuffer
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
// OP_RETURN {data}
|
// OP_RETURN {data}
|
||||||
import * as bscript from '../script'
|
import * as bscript from '../script';
|
||||||
const OPS = bscript.OPS
|
const OPS = bscript.OPS;
|
||||||
|
|
||||||
export function check (script: Buffer | Array<number | Buffer>): boolean {
|
export function check(script: Buffer | Array<number | Buffer>): boolean {
|
||||||
const buffer = bscript.compile(script)
|
const buffer = bscript.compile(script);
|
||||||
|
|
||||||
return buffer.length > 1 &&
|
return buffer.length > 1 && buffer[0] === OPS.OP_RETURN;
|
||||||
buffer[0] === OPS.OP_RETURN
|
|
||||||
}
|
}
|
||||||
check.toJSON = function () { return 'null data output' }
|
check.toJSON = function() {
|
||||||
|
return 'null data output';
|
||||||
|
};
|
||||||
|
|
||||||
const output = { check }
|
const output = { check };
|
||||||
|
|
||||||
export {
|
export { output };
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,282 +1,311 @@
|
||||||
import * as bcrypto from './crypto'
|
import * as bcrypto from './crypto';
|
||||||
import * as bscript from './script'
|
import * as bscript from './script';
|
||||||
import * as types from './types'
|
import * as types from './types';
|
||||||
import * as bufferutils from './bufferutils'
|
import * as bufferutils from './bufferutils';
|
||||||
import { reverseBuffer } from './bufferutils'
|
import { reverseBuffer } from './bufferutils';
|
||||||
import { OPS as opcodes } from './script'
|
import { OPS as opcodes } from './script';
|
||||||
|
|
||||||
const typeforce = require('typeforce')
|
const typeforce = require('typeforce');
|
||||||
const varuint = require('varuint-bitcoin')
|
const varuint = require('varuint-bitcoin');
|
||||||
|
|
||||||
function varSliceSize (someScript: Buffer): number {
|
function varSliceSize(someScript: Buffer): number {
|
||||||
const length = someScript.length
|
const length = someScript.length;
|
||||||
|
|
||||||
return varuint.encodingLength(length) + length
|
return varuint.encodingLength(length) + length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function vectorSize (someVector: Array<Buffer>): number {
|
function vectorSize(someVector: Array<Buffer>): number {
|
||||||
const length = someVector.length
|
const length = someVector.length;
|
||||||
|
|
||||||
return varuint.encodingLength(length) + someVector.reduce((sum, witness) => {
|
return (
|
||||||
return sum + varSliceSize(witness)
|
varuint.encodingLength(length) +
|
||||||
}, 0)
|
someVector.reduce((sum, witness) => {
|
||||||
|
return sum + varSliceSize(witness);
|
||||||
|
}, 0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0)
|
const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0);
|
||||||
const EMPTY_WITNESS: Array<Buffer> = []
|
const EMPTY_WITNESS: Array<Buffer> = [];
|
||||||
const ZERO: Buffer = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
|
const ZERO: Buffer = Buffer.from(
|
||||||
const ONE: Buffer = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex')
|
'hex',
|
||||||
|
);
|
||||||
|
const ONE: Buffer = Buffer.from(
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000001',
|
||||||
|
'hex',
|
||||||
|
);
|
||||||
|
const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex');
|
||||||
const BLANK_OUTPUT: BlankOutput = {
|
const BLANK_OUTPUT: BlankOutput = {
|
||||||
script: EMPTY_SCRIPT,
|
script: EMPTY_SCRIPT,
|
||||||
valueBuffer: VALUE_UINT64_MAX
|
valueBuffer: VALUE_UINT64_MAX,
|
||||||
}
|
};
|
||||||
|
|
||||||
function isOutput(out: Output | BlankOutput): out is Output {
|
function isOutput(out: Output | BlankOutput): out is Output {
|
||||||
return (<Output>out).value !== undefined
|
return (<Output>out).value !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BlankOutput = {
|
export type BlankOutput = {
|
||||||
script: Buffer
|
script: Buffer;
|
||||||
valueBuffer: Buffer
|
valueBuffer: Buffer;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Output = {
|
export type Output = {
|
||||||
script: Buffer
|
script: Buffer;
|
||||||
value: number
|
value: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Input = {
|
export type Input = {
|
||||||
hash: Buffer
|
hash: Buffer;
|
||||||
index: number
|
index: number;
|
||||||
script: Buffer
|
script: Buffer;
|
||||||
sequence: number
|
sequence: number;
|
||||||
witness: Array<Buffer>
|
witness: Array<Buffer>;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class Transaction {
|
export class Transaction {
|
||||||
version: number
|
version: number;
|
||||||
locktime: number
|
locktime: number;
|
||||||
ins: Array<Input>
|
ins: Array<Input>;
|
||||||
outs: Array<Output | BlankOutput>
|
outs: Array<Output | BlankOutput>;
|
||||||
|
|
||||||
static readonly DEFAULT_SEQUENCE = 0xffffffff
|
static readonly DEFAULT_SEQUENCE = 0xffffffff;
|
||||||
static readonly SIGHASH_ALL = 0x01
|
static readonly SIGHASH_ALL = 0x01;
|
||||||
static readonly SIGHASH_NONE = 0x02
|
static readonly SIGHASH_NONE = 0x02;
|
||||||
static readonly SIGHASH_SINGLE = 0x03
|
static readonly SIGHASH_SINGLE = 0x03;
|
||||||
static readonly SIGHASH_ANYONECANPAY = 0x80
|
static readonly SIGHASH_ANYONECANPAY = 0x80;
|
||||||
static readonly ADVANCED_TRANSACTION_MARKER = 0x00
|
static readonly ADVANCED_TRANSACTION_MARKER = 0x00;
|
||||||
static readonly ADVANCED_TRANSACTION_FLAG = 0x01
|
static readonly ADVANCED_TRANSACTION_FLAG = 0x01;
|
||||||
|
|
||||||
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: Buffer, __noStrict?: boolean): Transaction {
|
static fromBuffer(buffer: Buffer, __noStrict?: boolean): Transaction {
|
||||||
let offset: number = 0
|
let offset: number = 0;
|
||||||
|
|
||||||
function readSlice (n: number): Buffer {
|
function readSlice(n: number): Buffer {
|
||||||
offset += n
|
offset += n;
|
||||||
return buffer.slice(offset - n, offset)
|
return buffer.slice(offset - n, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readUInt32 (): number {
|
function readUInt32(): number {
|
||||||
const i = buffer.readUInt32LE(offset)
|
const i = buffer.readUInt32LE(offset);
|
||||||
offset += 4
|
offset += 4;
|
||||||
return i
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readInt32 (): number {
|
function readInt32(): number {
|
||||||
const i = buffer.readInt32LE(offset)
|
const i = buffer.readInt32LE(offset);
|
||||||
offset += 4
|
offset += 4;
|
||||||
return i
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readUInt64 (): number {
|
function readUInt64(): number {
|
||||||
const i = bufferutils.readUInt64LE(buffer, offset)
|
const i = bufferutils.readUInt64LE(buffer, offset);
|
||||||
offset += 8
|
offset += 8;
|
||||||
return i
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readVarInt (): number {
|
function readVarInt(): number {
|
||||||
const vi = varuint.decode(buffer, offset)
|
const vi = varuint.decode(buffer, offset);
|
||||||
offset += varuint.decode.bytes
|
offset += varuint.decode.bytes;
|
||||||
return vi
|
return vi;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readVarSlice (): Buffer {
|
function readVarSlice(): Buffer {
|
||||||
return readSlice(readVarInt())
|
return readSlice(readVarInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
function readVector (): Array<Buffer> {
|
function readVector(): Array<Buffer> {
|
||||||
const count = readVarInt()
|
const count = readVarInt();
|
||||||
const vector: Array<Buffer> = []
|
const vector: Array<Buffer> = [];
|
||||||
for (var i = 0; i < count; i++) vector.push(readVarSlice())
|
for (var i = 0; i < count; i++) vector.push(readVarSlice());
|
||||||
return vector
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tx = new Transaction()
|
const tx = new Transaction();
|
||||||
tx.version = readInt32()
|
tx.version = readInt32();
|
||||||
|
|
||||||
const marker = buffer.readUInt8(offset)
|
const marker = buffer.readUInt8(offset);
|
||||||
const flag = buffer.readUInt8(offset + 1)
|
const flag = buffer.readUInt8(offset + 1);
|
||||||
|
|
||||||
let hasWitnesses = false
|
let hasWitnesses = false;
|
||||||
if (marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
|
if (
|
||||||
flag === Transaction.ADVANCED_TRANSACTION_FLAG) {
|
marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
|
||||||
offset += 2
|
flag === Transaction.ADVANCED_TRANSACTION_FLAG
|
||||||
hasWitnesses = true
|
) {
|
||||||
|
offset += 2;
|
||||||
|
hasWitnesses = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vinLen = readVarInt()
|
const vinLen = readVarInt();
|
||||||
for (var i = 0; i < vinLen; ++i) {
|
for (var i = 0; i < vinLen; ++i) {
|
||||||
tx.ins.push({
|
tx.ins.push({
|
||||||
hash: readSlice(32),
|
hash: readSlice(32),
|
||||||
index: readUInt32(),
|
index: readUInt32(),
|
||||||
script: readVarSlice(),
|
script: readVarSlice(),
|
||||||
sequence: readUInt32(),
|
sequence: readUInt32(),
|
||||||
witness: EMPTY_WITNESS
|
witness: EMPTY_WITNESS,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const voutLen = readVarInt()
|
const voutLen = readVarInt();
|
||||||
for (i = 0; i < voutLen; ++i) {
|
for (i = 0; i < voutLen; ++i) {
|
||||||
tx.outs.push({
|
tx.outs.push({
|
||||||
value: readUInt64(),
|
value: readUInt64(),
|
||||||
script: readVarSlice()
|
script: readVarSlice(),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasWitnesses) {
|
if (hasWitnesses) {
|
||||||
for (i = 0; i < vinLen; ++i) {
|
for (i = 0; i < vinLen; ++i) {
|
||||||
tx.ins[i].witness = readVector()
|
tx.ins[i].witness = readVector();
|
||||||
}
|
}
|
||||||
|
|
||||||
// was this pointless?
|
// was this pointless?
|
||||||
if (!tx.hasWitnesses()) throw new Error('Transaction has superfluous witness data')
|
if (!tx.hasWitnesses())
|
||||||
|
throw new Error('Transaction has superfluous witness data');
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.locktime = readUInt32()
|
tx.locktime = readUInt32();
|
||||||
|
|
||||||
if (__noStrict) return tx
|
if (__noStrict) return tx;
|
||||||
if (offset !== buffer.length) throw new Error('Transaction has unexpected data')
|
if (offset !== buffer.length)
|
||||||
|
throw new Error('Transaction has unexpected data');
|
||||||
|
|
||||||
return tx
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromHex (hex: string): Transaction {
|
static fromHex(hex: string): Transaction {
|
||||||
return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false)
|
return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static isCoinbaseHash (buffer: Buffer): boolean {
|
static isCoinbaseHash(buffer: Buffer): boolean {
|
||||||
typeforce(types.Hash256bit, buffer)
|
typeforce(types.Hash256bit, buffer);
|
||||||
for (var i = 0; i < 32; ++i) {
|
for (var i = 0; i < 32; ++i) {
|
||||||
if (buffer[i] !== 0) return false
|
if (buffer[i] !== 0) return false;
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
isCoinbase (): boolean {
|
isCoinbase(): boolean {
|
||||||
return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
|
return (
|
||||||
|
this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addInput (hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number {
|
addInput(
|
||||||
typeforce(types.tuple(
|
hash: Buffer,
|
||||||
types.Hash256bit,
|
index: number,
|
||||||
types.UInt32,
|
sequence?: number,
|
||||||
types.maybe(types.UInt32),
|
scriptSig?: Buffer,
|
||||||
types.maybe(types.Buffer)
|
): number {
|
||||||
), arguments)
|
typeforce(
|
||||||
|
types.tuple(
|
||||||
|
types.Hash256bit,
|
||||||
|
types.UInt32,
|
||||||
|
types.maybe(types.UInt32),
|
||||||
|
types.maybe(types.Buffer),
|
||||||
|
),
|
||||||
|
arguments,
|
||||||
|
);
|
||||||
|
|
||||||
if (types.Null(sequence)) {
|
if (types.Null(sequence)) {
|
||||||
sequence = Transaction.DEFAULT_SEQUENCE
|
sequence = Transaction.DEFAULT_SEQUENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the input and return the input's index
|
// Add the input and return the input's index
|
||||||
return (this.ins.push({
|
return (
|
||||||
hash: hash,
|
this.ins.push({
|
||||||
index: index,
|
hash: hash,
|
||||||
script: scriptSig || EMPTY_SCRIPT,
|
index: index,
|
||||||
sequence: <number>sequence,
|
script: scriptSig || EMPTY_SCRIPT,
|
||||||
witness: EMPTY_WITNESS
|
sequence: <number>sequence,
|
||||||
}) - 1)
|
witness: EMPTY_WITNESS,
|
||||||
|
}) - 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addOutput (scriptPubKey: Buffer, value: number): number {
|
addOutput(scriptPubKey: Buffer, value: number): number {
|
||||||
typeforce(types.tuple(types.Buffer, types.Satoshi), arguments)
|
typeforce(types.tuple(types.Buffer, types.Satoshi), arguments);
|
||||||
|
|
||||||
// Add the output and return the output's index
|
// Add the output and return the output's index
|
||||||
return (this.outs.push({
|
return (
|
||||||
script: scriptPubKey,
|
this.outs.push({
|
||||||
value: value
|
script: scriptPubKey,
|
||||||
}) - 1)
|
value: value,
|
||||||
|
}) - 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasWitnesses (): boolean {
|
hasWitnesses(): boolean {
|
||||||
return this.ins.some((x) => {
|
return this.ins.some(x => {
|
||||||
return x.witness.length !== 0
|
return x.witness.length !== 0;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
weight (): number {
|
weight(): number {
|
||||||
const base = this.__byteLength(false)
|
const base = this.__byteLength(false);
|
||||||
const total = this.__byteLength(true)
|
const total = this.__byteLength(true);
|
||||||
return base * 3 + total
|
return base * 3 + total;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtualSize (): number {
|
virtualSize(): number {
|
||||||
return Math.ceil(this.weight() / 4)
|
return Math.ceil(this.weight() / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
byteLength (): number {
|
byteLength(): number {
|
||||||
return this.__byteLength(true)
|
return this.__byteLength(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private __byteLength (__allowWitness: boolean): number {
|
private __byteLength(__allowWitness: boolean): number {
|
||||||
const hasWitnesses = __allowWitness && this.hasWitnesses()
|
const hasWitnesses = __allowWitness && this.hasWitnesses();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(hasWitnesses ? 10 : 8) +
|
(hasWitnesses ? 10 : 8) +
|
||||||
varuint.encodingLength(this.ins.length) +
|
varuint.encodingLength(this.ins.length) +
|
||||||
varuint.encodingLength(this.outs.length) +
|
varuint.encodingLength(this.outs.length) +
|
||||||
this.ins.reduce((sum, input) => {
|
this.ins.reduce((sum, input) => {
|
||||||
return sum + 40 + varSliceSize(input.script)
|
return sum + 40 + varSliceSize(input.script);
|
||||||
}, 0) +
|
}, 0) +
|
||||||
this.outs.reduce((sum, output) => {
|
this.outs.reduce((sum, output) => {
|
||||||
return sum + 8 + varSliceSize(output.script)
|
return sum + 8 + varSliceSize(output.script);
|
||||||
}, 0) +
|
}, 0) +
|
||||||
(hasWitnesses ? this.ins.reduce((sum, input) => {
|
(hasWitnesses
|
||||||
return sum + vectorSize(input.witness)
|
? this.ins.reduce((sum, input) => {
|
||||||
}, 0) : 0)
|
return sum + vectorSize(input.witness);
|
||||||
)
|
}, 0)
|
||||||
|
: 0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
clone (): Transaction {
|
clone(): Transaction {
|
||||||
const newTx = new Transaction()
|
const newTx = new Transaction();
|
||||||
newTx.version = this.version
|
newTx.version = this.version;
|
||||||
newTx.locktime = this.locktime
|
newTx.locktime = this.locktime;
|
||||||
|
|
||||||
newTx.ins = this.ins.map((txIn) => {
|
newTx.ins = this.ins.map(txIn => {
|
||||||
return {
|
return {
|
||||||
hash: txIn.hash,
|
hash: txIn.hash,
|
||||||
index: txIn.index,
|
index: txIn.index,
|
||||||
script: txIn.script,
|
script: txIn.script,
|
||||||
sequence: txIn.sequence,
|
sequence: txIn.sequence,
|
||||||
witness: txIn.witness
|
witness: txIn.witness,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
newTx.outs = this.outs.map((txOut) => {
|
newTx.outs = this.outs.map(txOut => {
|
||||||
return {
|
return {
|
||||||
script: txOut.script,
|
script: txOut.script,
|
||||||
value: (<Output>txOut).value
|
value: (<Output>txOut).value,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
return newTx
|
return newTx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,284 +316,313 @@ export class Transaction {
|
||||||
* hashType, and then hashes the result.
|
* hashType, and then hashes the result.
|
||||||
* This hash can then be used to sign the provided transaction input.
|
* This hash can then be used to sign the provided transaction input.
|
||||||
*/
|
*/
|
||||||
hashForSignature (inIndex: number, prevOutScript: Buffer, hashType: number): Buffer {
|
hashForSignature(
|
||||||
typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments)
|
inIndex: number,
|
||||||
|
prevOutScript: Buffer,
|
||||||
|
hashType: number,
|
||||||
|
): Buffer {
|
||||||
|
typeforce(
|
||||||
|
types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number),
|
||||||
|
arguments,
|
||||||
|
);
|
||||||
|
|
||||||
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
|
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
|
||||||
if (inIndex >= this.ins.length) return ONE
|
if (inIndex >= this.ins.length) return ONE;
|
||||||
|
|
||||||
// ignore OP_CODESEPARATOR
|
// ignore OP_CODESEPARATOR
|
||||||
const ourScript = bscript.compile(bscript.decompile(prevOutScript)!.filter((x) => {
|
const ourScript = bscript.compile(
|
||||||
return x !== opcodes.OP_CODESEPARATOR
|
bscript.decompile(prevOutScript)!.filter(x => {
|
||||||
}))
|
return x !== opcodes.OP_CODESEPARATOR;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const txTmp = this.clone()
|
const txTmp = this.clone();
|
||||||
|
|
||||||
// SIGHASH_NONE: ignore all outputs? (wildcard payee)
|
// SIGHASH_NONE: ignore all outputs? (wildcard payee)
|
||||||
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
|
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
|
||||||
txTmp.outs = []
|
txTmp.outs = [];
|
||||||
|
|
||||||
// ignore sequence numbers (except at inIndex)
|
// ignore sequence numbers (except at inIndex)
|
||||||
txTmp.ins.forEach((input, i) => {
|
txTmp.ins.forEach((input, i) => {
|
||||||
if (i === inIndex) return
|
if (i === inIndex) return;
|
||||||
|
|
||||||
input.sequence = 0
|
input.sequence = 0;
|
||||||
})
|
});
|
||||||
|
|
||||||
// SIGHASH_SINGLE: ignore all outputs, except at the same index?
|
// SIGHASH_SINGLE: ignore all outputs, except at the same index?
|
||||||
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
|
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
|
||||||
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
|
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
|
||||||
if (inIndex >= this.outs.length) return ONE
|
if (inIndex >= this.outs.length) return ONE;
|
||||||
|
|
||||||
// truncate outputs after
|
// truncate outputs after
|
||||||
txTmp.outs.length = inIndex + 1
|
txTmp.outs.length = inIndex + 1;
|
||||||
|
|
||||||
// "blank" outputs before
|
// "blank" outputs before
|
||||||
for (var i = 0; i < inIndex; i++) {
|
for (var i = 0; i < inIndex; i++) {
|
||||||
txTmp.outs[i] = BLANK_OUTPUT
|
txTmp.outs[i] = BLANK_OUTPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore sequence numbers (except at inIndex)
|
// ignore sequence numbers (except at inIndex)
|
||||||
txTmp.ins.forEach((input, y) => {
|
txTmp.ins.forEach((input, y) => {
|
||||||
if (y === inIndex) return
|
if (y === inIndex) return;
|
||||||
|
|
||||||
input.sequence = 0
|
input.sequence = 0;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// SIGHASH_ANYONECANPAY: ignore inputs entirely?
|
// SIGHASH_ANYONECANPAY: ignore inputs entirely?
|
||||||
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
|
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
|
||||||
txTmp.ins = [txTmp.ins[inIndex]]
|
txTmp.ins = [txTmp.ins[inIndex]];
|
||||||
txTmp.ins[0].script = ourScript
|
txTmp.ins[0].script = ourScript;
|
||||||
|
|
||||||
// SIGHASH_ALL: only ignore input scripts
|
// SIGHASH_ALL: only ignore input scripts
|
||||||
} else {
|
} else {
|
||||||
// "blank" others input scripts
|
// "blank" others input scripts
|
||||||
txTmp.ins.forEach((input) => {
|
txTmp.ins.forEach(input => {
|
||||||
input.script = EMPTY_SCRIPT
|
input.script = EMPTY_SCRIPT;
|
||||||
})
|
});
|
||||||
txTmp.ins[inIndex].script = ourScript
|
txTmp.ins[inIndex].script = ourScript;
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialize and hash
|
// serialize and hash
|
||||||
const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4)
|
const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4);
|
||||||
buffer.writeInt32LE(hashType, buffer.length - 4)
|
buffer.writeInt32LE(hashType, buffer.length - 4);
|
||||||
txTmp.__toBuffer(buffer, 0, false)
|
txTmp.__toBuffer(buffer, 0, false);
|
||||||
|
|
||||||
return bcrypto.hash256(buffer)
|
return bcrypto.hash256(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
hashForWitnessV0 (inIndex: number, prevOutScript: Buffer, value: number, hashType: number): Buffer {
|
hashForWitnessV0(
|
||||||
typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments)
|
inIndex: number,
|
||||||
|
prevOutScript: Buffer,
|
||||||
|
value: number,
|
||||||
|
hashType: number,
|
||||||
|
): Buffer {
|
||||||
|
typeforce(
|
||||||
|
types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32),
|
||||||
|
arguments,
|
||||||
|
);
|
||||||
|
|
||||||
let tbuffer: Buffer = Buffer.from([])
|
let tbuffer: Buffer = Buffer.from([]);
|
||||||
let toffset: number = 0
|
let toffset: number = 0;
|
||||||
|
|
||||||
function writeSlice (slice: Buffer): void {
|
function writeSlice(slice: Buffer): void {
|
||||||
toffset += slice.copy(tbuffer, toffset)
|
toffset += slice.copy(tbuffer, toffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeUInt32 (i: number): void {
|
function writeUInt32(i: number): void {
|
||||||
toffset = tbuffer.writeUInt32LE(i, toffset)
|
toffset = tbuffer.writeUInt32LE(i, toffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeUInt64 (i: number): void {
|
function writeUInt64(i: number): void {
|
||||||
toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset)
|
toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeVarInt (i: number): void {
|
function writeVarInt(i: number): void {
|
||||||
varuint.encode(i, tbuffer, toffset)
|
varuint.encode(i, tbuffer, toffset);
|
||||||
toffset += varuint.encode.bytes
|
toffset += varuint.encode.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeVarSlice (slice: Buffer): void {
|
function writeVarSlice(slice: Buffer): void {
|
||||||
writeVarInt(slice.length)
|
writeVarInt(slice.length);
|
||||||
writeSlice(slice)
|
writeSlice(slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
let hashOutputs = ZERO
|
let hashOutputs = ZERO;
|
||||||
let hashPrevouts = ZERO
|
let hashPrevouts = ZERO;
|
||||||
let hashSequence = ZERO
|
let hashSequence = ZERO;
|
||||||
|
|
||||||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
|
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
|
||||||
tbuffer = Buffer.allocUnsafe(36 * this.ins.length)
|
tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
|
||||||
toffset = 0
|
toffset = 0;
|
||||||
|
|
||||||
this.ins.forEach((txIn) => {
|
this.ins.forEach(txIn => {
|
||||||
writeSlice(txIn.hash)
|
writeSlice(txIn.hash);
|
||||||
writeUInt32(txIn.index)
|
writeUInt32(txIn.index);
|
||||||
})
|
});
|
||||||
|
|
||||||
hashPrevouts = bcrypto.hash256(tbuffer)
|
hashPrevouts = bcrypto.hash256(tbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
|
if (
|
||||||
|
!(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
|
||||||
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
||||||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
|
(hashType & 0x1f) !== Transaction.SIGHASH_NONE
|
||||||
tbuffer = Buffer.allocUnsafe(4 * this.ins.length)
|
) {
|
||||||
toffset = 0
|
tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
|
||||||
|
toffset = 0;
|
||||||
|
|
||||||
this.ins.forEach((txIn) => {
|
this.ins.forEach(txIn => {
|
||||||
writeUInt32(txIn.sequence)
|
writeUInt32(txIn.sequence);
|
||||||
})
|
});
|
||||||
|
|
||||||
hashSequence = bcrypto.hash256(tbuffer)
|
hashSequence = bcrypto.hash256(tbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
if (
|
||||||
(hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
|
(hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
||||||
|
(hashType & 0x1f) !== Transaction.SIGHASH_NONE
|
||||||
|
) {
|
||||||
const txOutsSize = this.outs.reduce((sum, output) => {
|
const txOutsSize = this.outs.reduce((sum, output) => {
|
||||||
return sum + 8 + varSliceSize(output.script)
|
return sum + 8 + varSliceSize(output.script);
|
||||||
}, 0)
|
}, 0);
|
||||||
|
|
||||||
tbuffer = Buffer.allocUnsafe(txOutsSize)
|
tbuffer = Buffer.allocUnsafe(txOutsSize);
|
||||||
toffset = 0
|
toffset = 0;
|
||||||
|
|
||||||
this.outs.forEach((out) => {
|
this.outs.forEach(out => {
|
||||||
writeUInt64((<Output>out).value)
|
writeUInt64((<Output>out).value);
|
||||||
writeVarSlice(out.script)
|
writeVarSlice(out.script);
|
||||||
})
|
});
|
||||||
|
|
||||||
hashOutputs = bcrypto.hash256(tbuffer)
|
hashOutputs = bcrypto.hash256(tbuffer);
|
||||||
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) {
|
} else if (
|
||||||
const output = this.outs[inIndex]
|
(hashType & 0x1f) === Transaction.SIGHASH_SINGLE &&
|
||||||
|
inIndex < this.outs.length
|
||||||
|
) {
|
||||||
|
const output = this.outs[inIndex];
|
||||||
|
|
||||||
tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script))
|
tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
|
||||||
toffset = 0
|
toffset = 0;
|
||||||
writeUInt64((<Output>output).value)
|
writeUInt64((<Output>output).value);
|
||||||
writeVarSlice(output.script)
|
writeVarSlice(output.script);
|
||||||
|
|
||||||
hashOutputs = bcrypto.hash256(tbuffer)
|
hashOutputs = bcrypto.hash256(tbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript))
|
tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript));
|
||||||
toffset = 0
|
toffset = 0;
|
||||||
|
|
||||||
const input = this.ins[inIndex]
|
const input = this.ins[inIndex];
|
||||||
writeUInt32(this.version)
|
writeUInt32(this.version);
|
||||||
writeSlice(hashPrevouts)
|
writeSlice(hashPrevouts);
|
||||||
writeSlice(hashSequence)
|
writeSlice(hashSequence);
|
||||||
writeSlice(input.hash)
|
writeSlice(input.hash);
|
||||||
writeUInt32(input.index)
|
writeUInt32(input.index);
|
||||||
writeVarSlice(prevOutScript)
|
writeVarSlice(prevOutScript);
|
||||||
writeUInt64(value)
|
writeUInt64(value);
|
||||||
writeUInt32(input.sequence)
|
writeUInt32(input.sequence);
|
||||||
writeSlice(hashOutputs)
|
writeSlice(hashOutputs);
|
||||||
writeUInt32(this.locktime)
|
writeUInt32(this.locktime);
|
||||||
writeUInt32(hashType)
|
writeUInt32(hashType);
|
||||||
return bcrypto.hash256(tbuffer)
|
return bcrypto.hash256(tbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHash (forWitness?: boolean): Buffer {
|
getHash(forWitness?: boolean): Buffer {
|
||||||
// wtxid for coinbase is always 32 bytes of 0x00
|
// wtxid for coinbase is always 32 bytes of 0x00
|
||||||
if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0)
|
if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0);
|
||||||
return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness))
|
return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness));
|
||||||
}
|
}
|
||||||
|
|
||||||
getId (): string {
|
getId(): string {
|
||||||
// transaction hash's are displayed in reverse order
|
// transaction hash's are displayed in reverse order
|
||||||
return reverseBuffer(this.getHash(false)).toString('hex')
|
return reverseBuffer(this.getHash(false)).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
toBuffer (buffer?: Buffer, initialOffset?: number): Buffer {
|
toBuffer(buffer?: Buffer, initialOffset?: number): Buffer {
|
||||||
return this.__toBuffer(buffer, initialOffset, true)
|
return this.__toBuffer(buffer, initialOffset, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private __toBuffer (buffer?: Buffer, initialOffset?: number, __allowWitness?: boolean): Buffer {
|
private __toBuffer(
|
||||||
if (!buffer) buffer = <Buffer> Buffer.allocUnsafe(this.__byteLength(__allowWitness!))
|
buffer?: Buffer,
|
||||||
|
initialOffset?: number,
|
||||||
|
__allowWitness?: boolean,
|
||||||
|
): Buffer {
|
||||||
|
if (!buffer)
|
||||||
|
buffer = <Buffer>Buffer.allocUnsafe(this.__byteLength(__allowWitness!));
|
||||||
|
|
||||||
let offset = initialOffset || 0
|
let offset = initialOffset || 0;
|
||||||
|
|
||||||
function writeSlice (slice: Buffer): void {
|
function writeSlice(slice: Buffer): void {
|
||||||
offset += slice.copy(buffer!, offset)
|
offset += slice.copy(buffer!, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeUInt8 (i: number) {
|
function writeUInt8(i: number) {
|
||||||
offset = (buffer!).writeUInt8(i, offset)
|
offset = buffer!.writeUInt8(i, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeUInt32 (i: number) {
|
function writeUInt32(i: number) {
|
||||||
offset = (buffer!).writeUInt32LE(i, offset)
|
offset = buffer!.writeUInt32LE(i, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeInt32 (i: number) {
|
function writeInt32(i: number) {
|
||||||
offset = (buffer!).writeInt32LE(i, offset)
|
offset = buffer!.writeInt32LE(i, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeUInt64 (i: number) {
|
function writeUInt64(i: number) {
|
||||||
offset = bufferutils.writeUInt64LE(buffer!, i, offset)
|
offset = bufferutils.writeUInt64LE(buffer!, i, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeVarInt (i: number) {
|
function writeVarInt(i: number) {
|
||||||
varuint.encode(i, buffer, offset)
|
varuint.encode(i, buffer, offset);
|
||||||
offset += varuint.encode.bytes
|
offset += varuint.encode.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeVarSlice (slice: Buffer) {
|
function writeVarSlice(slice: Buffer) {
|
||||||
writeVarInt(slice.length)
|
writeVarInt(slice.length);
|
||||||
writeSlice(slice)
|
writeSlice(slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeVector (vector: Array<Buffer>) {
|
function writeVector(vector: Array<Buffer>) {
|
||||||
writeVarInt(vector.length)
|
writeVarInt(vector.length);
|
||||||
vector.forEach(writeVarSlice)
|
vector.forEach(writeVarSlice);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeInt32(this.version)
|
writeInt32(this.version);
|
||||||
|
|
||||||
const hasWitnesses = __allowWitness && this.hasWitnesses()
|
const hasWitnesses = __allowWitness && this.hasWitnesses();
|
||||||
|
|
||||||
if (hasWitnesses) {
|
if (hasWitnesses) {
|
||||||
writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER)
|
writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
|
||||||
writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG)
|
writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeVarInt(this.ins.length)
|
writeVarInt(this.ins.length);
|
||||||
|
|
||||||
this.ins.forEach((txIn) => {
|
this.ins.forEach(txIn => {
|
||||||
writeSlice(txIn.hash)
|
writeSlice(txIn.hash);
|
||||||
writeUInt32(txIn.index)
|
writeUInt32(txIn.index);
|
||||||
writeVarSlice(txIn.script)
|
writeVarSlice(txIn.script);
|
||||||
writeUInt32(txIn.sequence)
|
writeUInt32(txIn.sequence);
|
||||||
})
|
});
|
||||||
|
|
||||||
writeVarInt(this.outs.length)
|
writeVarInt(this.outs.length);
|
||||||
this.outs.forEach((txOut) => {
|
this.outs.forEach(txOut => {
|
||||||
if (isOutput(txOut)) {
|
if (isOutput(txOut)) {
|
||||||
writeUInt64(txOut.value)
|
writeUInt64(txOut.value);
|
||||||
} else {
|
} else {
|
||||||
writeSlice(txOut.valueBuffer)
|
writeSlice(txOut.valueBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeVarSlice(txOut.script)
|
writeVarSlice(txOut.script);
|
||||||
})
|
});
|
||||||
|
|
||||||
if (hasWitnesses) {
|
if (hasWitnesses) {
|
||||||
this.ins.forEach((input) => {
|
this.ins.forEach(input => {
|
||||||
writeVector(input.witness)
|
writeVector(input.witness);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
writeUInt32(this.locktime)
|
writeUInt32(this.locktime);
|
||||||
|
|
||||||
// avoid slicing unless necessary
|
// avoid slicing unless necessary
|
||||||
if (initialOffset !== undefined) return buffer.slice(initialOffset, offset)
|
if (initialOffset !== undefined) return buffer.slice(initialOffset, offset);
|
||||||
return buffer
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex () {
|
toHex() {
|
||||||
return this.toBuffer(undefined, undefined).toString('hex')
|
return this.toBuffer(undefined, undefined).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
setInputScript (index: number, scriptSig: Buffer) {
|
setInputScript(index: number, scriptSig: Buffer) {
|
||||||
typeforce(types.tuple(types.Number, types.Buffer), arguments)
|
typeforce(types.tuple(types.Number, types.Buffer), arguments);
|
||||||
|
|
||||||
this.ins[index].script = scriptSig
|
this.ins[index].script = scriptSig;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWitness (index: number, witness: Array<Buffer>) {
|
setWitness(index: number, witness: Array<Buffer>) {
|
||||||
typeforce(types.tuple(types.Number, [types.Buffer]), arguments)
|
typeforce(types.tuple(types.Number, [types.Buffer]), arguments);
|
||||||
|
|
||||||
this.ins[index].witness = witness
|
this.ins[index].witness = witness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,49 +1,51 @@
|
||||||
const typeforce = require('typeforce')
|
const typeforce = require('typeforce');
|
||||||
|
|
||||||
const UINT31_MAX: number = Math.pow(2, 31) - 1
|
const UINT31_MAX: number = Math.pow(2, 31) - 1;
|
||||||
export function UInt31 (value: number): boolean {
|
export function UInt31(value: number): boolean {
|
||||||
return typeforce.UInt32(value) && value <= UINT31_MAX
|
return typeforce.UInt32(value) && value <= UINT31_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BIP32Path (value: string): boolean {
|
export function BIP32Path(value: string): boolean {
|
||||||
return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/)
|
return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/);
|
||||||
}
|
}
|
||||||
BIP32Path.toJSON = function () { return 'BIP32 derivation path' }
|
BIP32Path.toJSON = function() {
|
||||||
|
return 'BIP32 derivation path';
|
||||||
|
};
|
||||||
|
|
||||||
const SATOSHI_MAX: number = 21 * 1e14
|
const SATOSHI_MAX: number = 21 * 1e14;
|
||||||
export function Satoshi (value: number): boolean {
|
export function Satoshi(value: number): boolean {
|
||||||
return typeforce.UInt53(value) && value <= SATOSHI_MAX
|
return typeforce.UInt53(value) && value <= SATOSHI_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// external dependent types
|
// external dependent types
|
||||||
export const ECPoint = typeforce.quacksLike('Point')
|
export const ECPoint = typeforce.quacksLike('Point');
|
||||||
|
|
||||||
// exposed, external API
|
// exposed, external API
|
||||||
export const Network = typeforce.compile({
|
export const 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,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const Buffer256bit = typeforce.BufferN(32)
|
export const Buffer256bit = typeforce.BufferN(32);
|
||||||
export const Hash160bit = typeforce.BufferN(20)
|
export const Hash160bit = typeforce.BufferN(20);
|
||||||
export const Hash256bit = typeforce.BufferN(32)
|
export const Hash256bit = typeforce.BufferN(32);
|
||||||
export const Number = typeforce.Number
|
export const Number = typeforce.Number;
|
||||||
export const Array = typeforce.Array
|
export const Array = typeforce.Array;
|
||||||
export const Boolean = typeforce.Boolean
|
export const Boolean = typeforce.Boolean;
|
||||||
export const String = typeforce.String
|
export const String = typeforce.String;
|
||||||
export const Buffer = typeforce.Buffer
|
export const Buffer = typeforce.Buffer;
|
||||||
export const Hex = typeforce.Hex
|
export const Hex = typeforce.Hex;
|
||||||
export const maybe = typeforce.maybe
|
export const maybe = typeforce.maybe;
|
||||||
export const tuple = typeforce.tuple
|
export const tuple = typeforce.tuple;
|
||||||
export const UInt8 = typeforce.UInt8
|
export const UInt8 = typeforce.UInt8;
|
||||||
export const UInt32 = typeforce.UInt32
|
export const UInt32 = typeforce.UInt32;
|
||||||
export const Function = typeforce.Function
|
export const Function = typeforce.Function;
|
||||||
export const BufferN = typeforce.BufferN
|
export const BufferN = typeforce.BufferN;
|
||||||
export const Null = typeforce.Null
|
export const Null = typeforce.Null;
|
||||||
export const oneOf = typeforce.oneOf
|
export const oneOf = typeforce.oneOf;
|
||||||
|
|
2
types/index.d.ts
vendored
2
types/index.d.ts
vendored
|
@ -5,7 +5,7 @@ import * as crypto from './crypto';
|
||||||
import * as networks from './networks';
|
import * as networks from './networks';
|
||||||
import * as payments from './payments';
|
import * as payments from './payments';
|
||||||
import * as script from './script';
|
import * as script from './script';
|
||||||
export { ECPair, address, bip32, crypto, networks, payments, script, };
|
export { ECPair, address, bip32, crypto, networks, payments, script };
|
||||||
export { Block } from './block';
|
export { Block } from './block';
|
||||||
export { Transaction } from './transaction';
|
export { Transaction } from './transaction';
|
||||||
export { TransactionBuilder } from './transaction_builder';
|
export { TransactionBuilder } from './transaction_builder';
|
||||||
|
|
Loading…
Reference in a new issue