Browse Source

Fix setup for typescript

master
junderw 5 years ago
parent
commit
00bb116a33
No known key found for this signature in database GPG Key ID: A9273B5AD3E47B45
  1. 3
      .gitignore
  2. 12
      .travis.yml
  3. 2
      LICENSE
  4. 29
      jest.json
  5. 4473
      package-lock.json
  6. 41
      package.json
  7. 4
      src/index.d.ts
  8. 299
      src/index.js
  9. 0
      test/index.js
  10. 10
      ts_src/index.spec.ts
  11. 158
      ts_src/index.ts
  12. 39
      tsconfig.json
  13. 10
      tslint.json

3
.gitignore

@ -1,4 +1,3 @@
node_modules
.nyc_output
coverage
.idea
package-lock.json

12
.travis.yml

@ -1,19 +1,15 @@
sudo: false
language: node_js
node_js:
- "lts/*"
- "9"
- "10"
- "13"
- "lts/*"
matrix:
include:
- node_js: "lts/*"
env: TEST_SUITE=format:ci
- node_js: "lts/*"
env: TEST_SUITE=gitdiff:ci
- node_js: "lts/*"
env: TEST_SUITE=lint
- node_js: "lts/*"
env: TEST_SUITE=coverage
env:
- TEST_SUITE=unit
script: npm run-script $TEST_SUITE
- TEST_SUITE=test
script: npm run $TEST_SUITE

2
LICENSE

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2011-2018 bitcoinjs-lib contributors
Copyright (c) 2020 bitcoinjs-lib contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

29
jest.json

@ -0,0 +1,29 @@
{
"moduleFileExtensions": [
"ts",
"js",
"json"
],
"transform": {
"^.+\\.ts$": "ts-jest"
},
"testRegex": "/ts_src/.*\\.spec\\.ts$",
"testURL": "http://localhost/",
"coverageThreshold": {
"global": {
"statements": 0,
"branches": 0,
"functions": 0,
"lines": 0
}
},
"collectCoverageFrom": [
"ts_src/**/*.ts",
"!**/node_modules/**"
],
"coverageReporters": [
"lcov",
"text"
],
"verbose": true
}

4473
package-lock.json

File diff suppressed because it is too large

41
package.json

@ -10,44 +10,41 @@
"btcpayserver"
],
"main": "./src/index.js",
"types": "./types/index.d.ts",
"engines": {
"node": ">=6.0.0"
},
"types": "./src/index.d.ts",
"scripts": {
"build": "tsc -p ./tsconfig.json",
"coverage-report": "npm run build && npm run nobuild:coverage-report",
"coverage": "npm run build && npm run nobuild:coverage",
"build": "npm run clean && tsc -p tsconfig.json && npm run formatjs",
"clean": "rm -rf src",
"coverage": "npm run unit -- --coverage",
"format": "npm run prettier -- --write",
"formatjs": "npm run prettierjs -- --write > /dev/null 2>&1",
"format:ci": "npm run prettier -- --check",
"gitdiff:ci": "npm run build && git diff --exit-code",
"gitdiff": "git diff --exit-code",
"gitdiff:ci": "npm run build && npm run gitdiff",
"lint": "tslint -p tsconfig.json -c tslint.json",
"nobuild:coverage-report": "nyc report --reporter=lcov",
"nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 npm run nobuild:unit",
"nobuild:unit": "tape test/*.js",
"prettier": "prettier 'ts-src/**/*.ts' --ignore-path ./.prettierignore",
"test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage",
"unit": "npm run build && npm run nobuild:unit"
"prepublishOnly": "npm run test && npm run gitdiff",
"prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore",
"prettierjs": "prettier 'src/**/*.js' --ignore-path ./.prettierignore",
"test": "npm run build && npm run format:ci && npm run lint && npm run unit",
"unit": "jest --config=jest.json --runInBand"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Kukks/payjoin-client-js.git"
},
"files": [
"src",
"types"
"src"
],
"dependencies": {
"@types/node": "^13.13.0",
"bip174": "^1.0.1",
"bitcoinjs-lib": "^5.1.7"
},
"devDependencies": {
"nyc": "^15.0.0",
"prettier": "1.16.4",
"tape": "^4.13.2",
"@types/jest": "^25.2.1",
"@types/node": "^13.13.0",
"jest": "^25.3.0",
"prettier": "^2.0.4",
"ts-jest": "^25.3.1",
"tslint": "^6.1.1",
"typescript": "3.8.3"
"typescript": "^3.8.3"
},
"author": "Andrew Camilleri (Kukks)",
"license": "MIT",

4
types/index.d.ts → src/index.d.ts

@ -1,5 +1,5 @@
import { Psbt } from 'bitcoinjs-lib';
declare type Nullable<T> = T | null;
export declare function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall: (psbt: Psbt) => Promise<Nullable<Psbt>>): Promise<null | undefined>;
export declare function requestPayjoin(psbt: Psbt, payjoinEndpoint: string): Promise<null | undefined>;
export declare function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall: (psbt: Psbt) => Promise<Nullable<Psbt>>): Promise<void>;
export declare function requestPayjoin(psbt: Psbt, payjoinEndpoint: string): Promise<void>;
export {};

299
src/index.js

@ -1,88 +1,231 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
const payments_1 = require("bitcoinjs-lib/types/payments");
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const bitcoinjs_lib_1 = require('bitcoinjs-lib');
const bitcoinjs_lib_2 = require('bitcoinjs-lib');
async function requestPayjoinWithCustomRemoteCall(psbt, remoteCall) {
const clonedPsbt = psbt.clone();
clonedPsbt.finalizeAllInputs();
// We make sure we don't send unnecessary information to the receiver
for (let index = 0; index < clonedPsbt.inputCount; index++) {
clonedPsbt.clearFinalizedInput(index);
}
clonedPsbt.data.outputs.forEach(output => {
delete output.bip32Derivation;
const clonedPsbt = psbt.clone();
clonedPsbt.finalizeAllInputs();
// We make sure we don't send unnecessary information to the receiver
for (let index = 0; index < clonedPsbt.inputCount; index++) {
clonedPsbt.clearFinalizedInput(index);
}
clonedPsbt.data.outputs.forEach((output) => {
delete output.bip32Derivation;
});
delete clonedPsbt.data.globalMap.globalXpub;
const payjoinPsbt = await remoteCall(clonedPsbt);
if (!payjoinPsbt) throw new Error("We did not get the receiver's PSBT");
// no inputs were added?
if (clonedPsbt.inputCount <= payjoinPsbt.inputCount) {
throw new Error(
"There were less inputs than before in the receiver's PSBT",
);
}
if (
payjoinPsbt.data.globalMap.globalXpub &&
payjoinPsbt.data.globalMap.globalXpub.length > 0
) {
throw new Error(
"GlobalXPubs should not be included in the receiver's PSBT",
);
}
if (
hasKeypathInformationSet(payjoinPsbt.data.outputs) ||
hasKeypathInformationSet(payjoinPsbt.data.inputs)
) {
throw new Error(
"Keypath information should not be included in the receiver's PSBT",
);
}
const sanityResult = checkSanity(payjoinPsbt);
if (Object.keys(sanityResult).length > 0) {
throw new Error(
`Receiver's PSBT is insane: ${JSON.stringify(sanityResult)}`,
);
}
// We make sure we don't sign what should not be signed
for (let index = 0; index < payjoinPsbt.inputCount; index++) {
// check if input is Finalized
if (isFinalized(payjoinPsbt.data.inputs[index]))
payjoinPsbt.clearFinalizedInput(index);
}
for (let index = 0; index < payjoinPsbt.data.outputs.length; index++) {
const output = payjoinPsbt.data.outputs[index];
const outputLegacy = getGlobalTransaction(payjoinPsbt).outs[index];
// Make sure only our output has any information
delete output.bip32Derivation;
psbt.data.outputs.forEach((originalOutput) => {
// update the payjoin outputs
if (
outputLegacy.script.equals(
// TODO: what if output is P2SH or P2WSH or anything other than P2WPKH?
// Can we assume output will contain redeemScript and witnessScript?
// If so, we could decompile scriptPubkey, RS, and WS, and search for
// the pubkey and its hash160.
bitcoinjs_lib_2.payments.p2wpkh({
pubkey: originalOutput.bip32Derivation[0].pubkey,
}).output,
)
)
payjoinPsbt.updateOutput(index, originalOutput);
});
delete clonedPsbt.data.globalMap.globalXpub;
const payjoinPsbt = await remoteCall(clonedPsbt);
if (!payjoinPsbt)
return null;
// no inputs were added?
if (clonedPsbt.inputCount <= payjoinPsbt.inputCount) {
return null;
}
// TODO: check payjoinPsbt.version == psbt.version
// TODO: check payjoinPsbt.locktime == psbt.locktime
// TODO: check payjoinPsbt.inputs where input belongs to us, that it is not finalized
// TODO: check payjoinPsbt.inputs where input belongs to us, that it is was included in psbt.inputs
// TODO: check payjoinPsbt.inputs where input belongs to us, that its sequence has not changed from that of psbt.inputs
// TODO: check payjoinPsbt.inputs where input is new, that it is finalized
// TODO: check payjoinPsbt.inputs where input is new, that it is the same type as all other inputs from psbt.inputs (all==P2WPKH || all = P2SH-P2WPKH)
// TODO: check psbt.inputs that payjoinPsbt.inputs contains them all
// TODO: check payjoinPsbt.inputs > psbt.inputs
// TODO: check that if spend amount of payjoinPsbt > spend amount of psbt:
// TODO: * check if the difference is due to adjusting fee to increase transaction size
}
exports.requestPayjoinWithCustomRemoteCall = requestPayjoinWithCustomRemoteCall;
async function requestPayjoin(psbt, payjoinEndpoint) {
return requestPayjoinWithCustomRemoteCall(psbt, (psbt1) =>
doRequest(psbt1, payjoinEndpoint),
);
}
exports.requestPayjoin = requestPayjoin;
function checkSanity(psbt) {
const result = {};
psbt.data.inputs.forEach((value, index) => {
const sanityResult = checkInputSanity(
value,
getGlobalTransaction(psbt).ins[index],
);
if (sanityResult.length > 0) {
result[index] = sanityResult;
}
});
return result;
}
function checkInputSanity(input, txInput) {
const errors = [];
if (isFinalized(input)) {
if (input.partialSig && input.partialSig.length > 0) {
errors.push('Input finalized, but partial sigs are not empty');
}
if (input.bip32Derivation && input.bip32Derivation.length > 0) {
errors.push('Input finalized, but hd keypaths are not empty');
}
if (input.sighashType) {
errors.push('Input finalized, but sighash type is not null');
}
if (input.redeemScript) {
errors.push('Input finalized, but redeem script is not null');
}
if (input.witnessScript) {
errors.push('Input finalized, but witness script is not null');
}
// We make sure we don't sign things what should not be signed
for (let index = 0; index < payjoinPsbt.inputCount; index++) {
// Is Finalized
if (payjoinPsbt.data.inputs[index].finalScriptSig !== undefined ||
payjoinPsbt.data.inputs[index].finalScriptWitness !== undefined)
payjoinPsbt.clearFinalizedInput(index);
}
if (input.witnessUtxo && input.nonWitnessUtxo) {
errors.push('witness utxo and non witness utxo simultaneously present');
}
if (input.witnessScript && !input.witnessUtxo) {
errors.push('witness script present but no witness utxo');
}
if (!input.finalScriptWitness && !input.witnessUtxo) {
errors.push('final witness script present but no witness utxo');
}
if (input.nonWitnessUtxo) {
// TODO: get hash
const prevOutTxId = input.nonWitnessUtxo;
let validOutpoint = true;
if (txInput.hash !== prevOutTxId) {
errors.push(
'non_witness_utxo does not match the transaction id referenced by the global transaction sign',
);
validOutpoint = false;
}
for (let index = 0; index < payjoinPsbt.data.outputs.length; index++) {
const output = payjoinPsbt.data.outputs[index];
// TODO: bitcoinjs-lib to expose outputs to Psbt class
// instead of using private (JS has no private) attributes
// @ts-ignore
if (txInput.index >= input.nonWitnessUtxo.Outputs.length) {
errors.push(
'Global transaction referencing an out of bound output in non_witness_utxo',
);
validOutpoint = false;
}
if (input.redeemScript && validOutpoint) {
if (
// @ts-ignore
input.redeemScript.Hash.ScriptPubKey !==
// @ts-ignore
input.nonWitnessUtxo.Outputs[txInput.index].ScriptPubKey
)
errors.push(
'The redeem_script is not coherent with the scriptPubKey of the non_witness_utxo',
);
}
}
if (input.witnessUtxo) {
if (input.redeemScript) {
if (
// @ts-ignore
input.redeemScript.Hash.ScriptPubKey !== input.witnessUtxo.ScriptPubKey
)
errors.push(
'The redeem_script is not coherent with the scriptPubKey of the witness_utxo',
);
if (
input.witnessScript &&
input.redeemScript &&
// @ts-ignore
const outputLegacy = payjoinPsbt.__CACHE.__TX.outs[index];
// Make sure only the only our output have any information
delete output.bip32Derivation;
psbt.data.outputs.forEach(originalOutput => {
// update the payjoin outputs
if (outputLegacy.script.equals(
// TODO: what if output is P2SH or P2WSH or anything other than P2WPKH?
// Can we assume output will contain redeemScript and witnessScript?
// If so, we could decompile scriptPubkey, RS, and WS, and search for
// the pubkey and its hash160.
payments_1.p2wpkh({
pubkey: originalOutput.bip32Derivation.pubkey,
}).output))
payjoinPsbt.updateOutput(index, originalOutput);
});
PayToWitScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(
input.redeemScript,
) !== input.witnessScript.WitHash
)
errors.push(
'witnessScript with witness UTXO does not match the redeemScript',
);
}
// TODO: check payjoinPsbt.version == psbt.version
// TODO: check payjoinPsbt.locktime == psbt.locktime
// TODO: check payjoinPsbt.inputs where input belongs to us, that it is not finalized
// TODO: check payjoinPsbt.inputs where input belongs to us, that it is was included in psbt.inputs
// TODO: check payjoinPsbt.inputs where input belongs to us, that its sequence has not changed from that of psbt.inputs
// TODO: check payjoinPsbt.inputs where input is new, that it is finalized
// TODO: check payjoinPsbt.inputs where input is new, that it is the same type as all other inputs from psbt.inputs (all==P2WPKH || all = P2SH-P2WPKH)
// TODO: check psbt.inputs that payjoinPsbt.inputs contains them all
// TODO: check payjoinPsbt.inputs > psbt.inputs
// TODO: check that if spend amount of payjoinPsbt > spend amount of psbt:
// TODO: * check if the difference is due to adjusting fee to increase transaction size
}
// figure out how to port this lofic
// if (input.witnessUtxo.ScriptPubKey is Script s)
// {
//
// if (!s.IsScriptType(ScriptType.P2SH) && !s.IsScriptType(ScriptType.Witness))
// errors.push('A Witness UTXO is provided for a non-witness input');
// if (s.IsScriptType(ScriptType.P2SH) && redeem_script is Script r && !r.IsScriptType(ScriptType.Witness))
// errors.push('A Witness UTXO is provided for a non-witness input');
// }
return errors;
}
exports.requestPayjoinWithCustomRemoteCall = requestPayjoinWithCustomRemoteCall;
function requestPayjoin(psbt, payjoinEndpoint) {
return requestPayjoinWithCustomRemoteCall(psbt, psbt1 => doRequest(psbt1, payjoinEndpoint));
function hasKeypathInformationSet(items) {
return (
items.filter(
(value) => !!value.bip32Derivation && value.bip32Derivation.length > 0,
).length > 0
);
}
function isFinalized(input) {
return (
input.finalScriptSig !== undefined || input.finalScriptWitness !== undefined
);
}
function getGlobalTransaction(psbt) {
// TODO: bitcoinjs-lib to expose outputs to Psbt class
// instead of using private (JS has no private) attributes
// @ts-ignore
return psbt.__CACHE.__TX;
}
exports.requestPayjoin = requestPayjoin;
function doRequest(psbt, payjoinEndpoint) {
return new Promise((resolve, reject) => {
if (!psbt) {
reject();
}
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4)
return;
if (xhr.status >= 200 && xhr.status < 300) {
resolve(bitcoinjs_lib_1.Psbt.fromHex(xhr.responseText));
}
else {
reject(xhr.responseText);
}
};
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.open('POST', payjoinEndpoint);
xhr.send(psbt.toHex());
});
return new Promise((resolve, reject) => {
if (!psbt) {
reject();
}
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status >= 200 && xhr.status < 300) {
resolve(bitcoinjs_lib_1.Psbt.fromHex(xhr.responseText));
} else {
reject(xhr.responseText);
}
};
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.open('POST', payjoinEndpoint);
xhr.send(psbt.toHex());
});
}

0
test/index.js

10
ts_src/index.spec.ts

@ -0,0 +1,10 @@
import { requestPayjoin, requestPayjoinWithCustomRemoteCall } from './index';
describe('requestPayjoin', () => {
it('should exist', () => {
expect(requestPayjoin).toBeDefined();
expect(typeof requestPayjoin).toBe('function');
expect(requestPayjoinWithCustomRemoteCall).toBeDefined();
expect(typeof requestPayjoinWithCustomRemoteCall).toBe('function');
});
});

158
ts-src/index.ts → ts_src/index.ts

@ -1,11 +1,24 @@
import { Psbt, Transaction } from 'bitcoinjs-lib';
import { p2wpkh } from 'bitcoinjs-lib/types/payments';
import { Bip32Derivation, GlobalXpub, PsbtInput } from 'bip174/src/lib/interfaces';
import { Input } from 'bitcoinjs-lib/types/transaction';
import { payments } from 'bitcoinjs-lib';
import {
Bip32Derivation,
GlobalXpub,
PsbtInput,
} from 'bip174/src/lib/interfaces';
type Nullable<T> = T | null;
interface TxInput {
hash: Buffer;
index: number;
script: Buffer;
sequence: number;
witness: Buffer[];
}
export async function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall: (psbt: Psbt) => Promise<Nullable<Psbt>>) {
export async function requestPayjoinWithCustomRemoteCall(
psbt: Psbt,
remoteCall: (psbt: Psbt) => Promise<Nullable<Psbt>>,
): Promise<void> {
const clonedPsbt = psbt.clone();
clonedPsbt.finalizeAllInputs();
@ -13,29 +26,43 @@ export async function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall:
for (let index = 0; index < clonedPsbt.inputCount; index++) {
clonedPsbt.clearFinalizedInput(index);
}
clonedPsbt.data.outputs.forEach(output => {
clonedPsbt.data.outputs.forEach((output): void => {
delete output.bip32Derivation;
});
delete clonedPsbt.data.globalMap.globalXpub;
const payjoinPsbt = await remoteCall(clonedPsbt);
if (!payjoinPsbt) throw new Error('We did not get the receiver\'s PSBT');
if (!payjoinPsbt) throw new Error("We did not get the receiver's PSBT");
// no inputs were added?
if (clonedPsbt.inputCount <= payjoinPsbt.inputCount) {
throw new Error('There were less inputs than before in the receiver\'s PSBT');
throw new Error(
"There were less inputs than before in the receiver's PSBT",
);
}
if (payjoinPsbt.data.globalMap.globalXpub && (payjoinPsbt.data.globalMap.globalXpub as GlobalXpub[]).length > 0) {
throw new Error('GlobalXPubs should not be included in the receiver\'s PSBT');
if (
payjoinPsbt.data.globalMap.globalXpub &&
(payjoinPsbt.data.globalMap.globalXpub as GlobalXpub[]).length > 0
) {
throw new Error(
"GlobalXPubs should not be included in the receiver's PSBT",
);
}
if (hasKeypathInformationSet(payjoinPsbt.data.outputs) || hasKeypathInformationSet(payjoinPsbt.data.inputs)) {
throw new Error(('Keypath information should not be included in the receiver\'s PSBT');
if (
hasKeypathInformationSet(payjoinPsbt.data.outputs) ||
hasKeypathInformationSet(payjoinPsbt.data.inputs)
) {
throw new Error(
"Keypath information should not be included in the receiver's PSBT",
);
}
const sanityResult = checkSanity(payjoinPsbt);
if(Object.keys(sanityResult).length > 0){
throw new Error(`Receiver's PSBT is insane: ${JSON.stringify(sanityResult)}`);
if (Object.keys(sanityResult).length > 0) {
throw new Error(
`Receiver's PSBT is insane: ${JSON.stringify(sanityResult)}`,
);
}
// We make sure we don't sign what should not be signed
@ -50,7 +77,7 @@ export async function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall:
const outputLegacy = getGlobalTransaction(payjoinPsbt).outs[index];
// Make sure only our output has any information
delete output.bip32Derivation;
psbt.data.outputs.forEach(originalOutput => {
psbt.data.outputs.forEach((originalOutput): void => {
// update the payjoin outputs
if (
outputLegacy.script.equals(
@ -58,9 +85,9 @@ export async function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall:
// Can we assume output will contain redeemScript and witnessScript?
// If so, we could decompile scriptPubkey, RS, and WS, and search for
// the pubkey and its hash160.
p2wpkh({
pubkey: originalOutput.bip32Derivation.pubkey,
}).output,
payments.p2wpkh({
pubkey: originalOutput.bip32Derivation![0].pubkey,
}).output!,
)
)
payjoinPsbt.updateOutput(index, originalOutput);
@ -79,14 +106,23 @@ export async function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall:
// TODO: * check if the difference is due to adjusting fee to increase transaction size
}
export function requestPayjoin(psbt: Psbt, payjoinEndpoint: string) {
return requestPayjoinWithCustomRemoteCall(psbt, psbt1 => doRequest(psbt1, payjoinEndpoint));
export async function requestPayjoin(
psbt: Psbt,
payjoinEndpoint: string,
): Promise<void> {
return requestPayjoinWithCustomRemoteCall(
psbt,
(psbt1): Promise<Nullable<Psbt>> => doRequest(psbt1, payjoinEndpoint),
);
}
function checkSanity(psbt: Psbt): { [index: number]: string[] } {
const result: { [index: number]: string[] } = {};
psbt.data.inputs.forEach((value, index) => {
const sanityResult = checkInputSanity(value, getGlobalTransaction(psbt).ins[index]);
psbt.data.inputs.forEach((value, index): void => {
const sanityResult = checkInputSanity(
value,
getGlobalTransaction(psbt).ins[index],
);
if (sanityResult.length > 0) {
result[index] = sanityResult;
}
@ -94,7 +130,7 @@ function checkSanity(psbt: Psbt): { [index: number]: string[] } {
return result;
}
function checkInputSanity(input: PsbtInput, txInput: Input): string[] {
function checkInputSanity(input: PsbtInput, txInput: TxInput): string[] {
const errors: string[] = [];
if (isFinalized(input)) {
if (input.partialSig && input.partialSig.length > 0) {
@ -126,36 +162,61 @@ function checkInputSanity(input: PsbtInput, txInput: Input): string[] {
}
if (input.nonWitnessUtxo) {
//TODO: get hash
// TODO: get hash
const prevOutTxId = input.nonWitnessUtxo;
let validOutpoint = true;
if (txInput.hash != prevOutTxId) {
errors.push('non_witness_utxo does not match the transaction id referenced by the global transaction sign');
if (txInput.hash !== prevOutTxId) {
errors.push(
'non_witness_utxo does not match the transaction id referenced by the global transaction sign',
);
validOutpoint = false;
}
// @ts-ignore
if (txInput.index >= input.nonWitnessUtxo.Outputs.length) {
errors.push('Global transaction referencing an out of bound output in non_witness_utxo');
errors.push(
'Global transaction referencing an out of bound output in non_witness_utxo',
);
validOutpoint = false;
}
if (input.redeemScript && validOutpoint) {
if (input.redeemScript.Hash.ScriptPubKey != input.nonWitnessUtxo.Outputs[txInput.index].ScriptPubKey)
errors.push('The redeem_script is not coherent with the scriptPubKey of the non_witness_utxo');
if (
// @ts-ignore
input.redeemScript.Hash.ScriptPubKey !==
// @ts-ignore
input.nonWitnessUtxo.Outputs[txInput.index].ScriptPubKey
)
errors.push(
'The redeem_script is not coherent with the scriptPubKey of the non_witness_utxo',
);
}
}
if (input.witnessUtxo) {
if (input.redeemScript) {
if (input.redeemScript.Hash.ScriptPubKey != input.witnessUtxo.ScriptPubKey)
errors.push('The redeem_script is not coherent with the scriptPubKey of the witness_utxo');
if (input.witnessScript &&
if (
// @ts-ignore
input.redeemScript.Hash.ScriptPubKey !== input.witnessUtxo.ScriptPubKey
)
errors.push(
'The redeem_script is not coherent with the scriptPubKey of the witness_utxo',
);
if (
input.witnessScript &&
input.redeemScript &&
PayToWitScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(input.redeemScript) != input.witnessScript.WitHash)
errors.push('witnessScript with witness UTXO does not match the redeemScript');
// @ts-ignore
PayToWitScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(
input.redeemScript,
// @ts-ignore
) !== input.witnessScript.WitHash
)
errors.push(
'witnessScript with witness UTXO does not match the redeemScript',
);
}
}
//figure out how to port this lofic
// figure out how to port this lofic
// if (input.witnessUtxo.ScriptPubKey is Script s)
// {
//
@ -165,18 +226,24 @@ function checkInputSanity(input: PsbtInput, txInput: Input): string[] {
// errors.push('A Witness UTXO is provided for a non-witness input');
// }
return errors;
}
function hasKeypathInformationSet(items: { bip32Derivation?: Bip32Derivation[] }[]): boolean {
return items.filter(value => value.bip32Derivation && value.bip32Derivation.length > 0).length > 0;
function hasKeypathInformationSet(
items: { bip32Derivation?: Bip32Derivation[] }[],
): boolean {
return (
items.filter(
(value): boolean =>
!!value.bip32Derivation && value.bip32Derivation.length > 0,
).length > 0
);
}
function isFinalized(input: PsbtInput) {
return input.finalScriptSig !== undefined ||
input.finalScriptWitness !== undefined;
function isFinalized(input: PsbtInput): boolean {
return (
input.finalScriptSig !== undefined || input.finalScriptWitness !== undefined
);
}
function getGlobalTransaction(psbt: Psbt): Transaction {
@ -186,14 +253,17 @@ function getGlobalTransaction(psbt: Psbt): Transaction {
return psbt.__CACHE.__TX;
}
function doRequest(psbt: Psbt, payjoinEndpoint: string): Promise<Nullable<Psbt>> {
return new Promise<Nullable<Psbt>>((resolve, reject) => {
function doRequest(
psbt: Psbt,
payjoinEndpoint: string,
): Promise<Nullable<Psbt>> {
return new Promise<Nullable<Psbt>>((resolve, reject): void => {
if (!psbt) {
reject();
}
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
xhr.onreadystatechange = (): void => {
if (xhr.readyState !== 4) return;
if (xhr.status >= 200 && xhr.status < 300) {
resolve(Psbt.fromHex(xhr.responseText));

39
tsconfig.json

@ -1,31 +1,32 @@
{
"compilerOptions": {
"allowJs": false,
"alwaysStrict": true,
"declaration": true,
"declarationDir": "./types",
"esModuleInterop": false,
"lib": [
"es2017",
"dom"
],
"target": "es2017",
"module": "commonjs",
"noImplicitAny": true,
"noImplicitThis": true,
"outDir": "./src",
"rootDir": "./ts-src",
"declaration": true,
"rootDir": "./ts_src",
"types": [
"node",
"jest"
],
"allowJs": false,
"strict": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"target": "es2017",
"types": [
"node"
]
"noImplicitThis": true,
"alwaysStrict": true,
"esModuleInterop": false,
"resolveJsonModule": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true
},
"include": [
"ts-src/*.ts"
"ts_src/**/*.ts",
"ts_src/**/*.json"
],
"exclude": [
"**/*.spec.ts",

10
tslint.json

@ -16,12 +16,18 @@
"no-bitwise": false,
"no-console": false,
"no-empty": [true, "allow-empty-catch"],
"no-implicit-dependencies": true,
"no-implicit-dependencies": false,
"no-return-await": true,
"no-var-requires": false,
"no-unused-expression": false,
"object-literal-sort-keys": false,
"quotemark": [true, "single"],
"quotemark": [true, "single", "avoid-escape"],
"typedef": [
true,
"call-signature",
"arrow-call-signature",
"property-declaration"
],
"variable-name": [
true,
"ban-keywords",

Loading…
Cancel
Save