Kukks
5 years ago
commit
cb18b1ca51
15 changed files with 2360 additions and 0 deletions
@ -0,0 +1,4 @@ |
|||
node_modules |
|||
.nyc_output |
|||
coverage |
|||
.idea |
@ -0,0 +1,4 @@ |
|||
{ |
|||
"singleQuote": true, |
|||
"trailingComma": "all" |
|||
} |
@ -0,0 +1,19 @@ |
|||
sudo: false |
|||
language: node_js |
|||
node_js: |
|||
- "lts/*" |
|||
- "9" |
|||
- "10" |
|||
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 |
@ -0,0 +1,3 @@ |
|||
# Refer to Bitcoinjs-lib |
|||
|
|||
[Please refer to bitcoinjs-lib CONTRIBUTING for a guide on how to contribute by clicking here.](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/CONTRIBUTING.md) |
@ -0,0 +1,21 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2011-2018 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 |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
@ -0,0 +1,35 @@ |
|||
# bip32 |
|||
[![Build Status](https://travis-ci.org/bitcoinjs/bip32.png?branch=master)](https://travis-ci.org/bitcoinjs/bip32) |
|||
|
|||
[![NPM](https://img.shields.io/npm/v/bip32.svg)](https://www.npmjs.org/package/bip32) |
|||
|
|||
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) |
|||
|
|||
A [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) compatible library written in TypeScript with transpiled JavaScript committed to git. |
|||
|
|||
|
|||
## Example |
|||
|
|||
TypeScript |
|||
|
|||
``` typescript |
|||
import * as bip32 from 'bip32'; |
|||
import { BIP32Interface } from 'bip32'; |
|||
let node: BIP32Interface = bip32.fromBase58('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'); |
|||
|
|||
let child: BIP32Interface = node.derivePath('m/0/0'); |
|||
// ... |
|||
``` |
|||
|
|||
NodeJS |
|||
|
|||
``` javascript |
|||
let bip32 = require('bip32') |
|||
let node = bip32.fromBase58('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi') |
|||
|
|||
let child = node.derivePath('m/0/0') |
|||
// ... |
|||
``` |
|||
|
|||
## LICENSE [MIT](LICENSE) |
|||
A derivation (and extraction for modularity) of the `HDWallet`/`HDNode` written and tested by [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) contributors since 2014. |
File diff suppressed because it is too large
@ -0,0 +1,56 @@ |
|||
{ |
|||
"name": "payjoin-client-js", |
|||
"version": "1.0.0", |
|||
"description": "A BTCPay Payjoin compatible client", |
|||
"keywords": [ |
|||
"bitcoinjs", |
|||
"bitcoin", |
|||
"bip79", |
|||
"payjoin", |
|||
"btcpayserver" |
|||
], |
|||
"main": "./src/index.js", |
|||
"types": "./types/index.d.ts", |
|||
"engines": { |
|||
"node": ">=6.0.0" |
|||
}, |
|||
"scripts": { |
|||
"build": "tsc -p ./tsconfig.json", |
|||
"coverage-report": "npm run build && npm run nobuild:coverage-report", |
|||
"coverage": "npm run build && npm run nobuild:coverage", |
|||
"format": "npm run prettier -- --write", |
|||
"format:ci": "npm run prettier -- --check", |
|||
"gitdiff:ci": "npm run build && git diff --exit-code", |
|||
"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" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/Kukks/payjoin-client-js.git" |
|||
}, |
|||
"files": [ |
|||
"src", |
|||
"types" |
|||
], |
|||
"dependencies": { |
|||
"@types/node": "^13.13.0", |
|||
"bitcoinjs-lib": "^5.1.7" |
|||
}, |
|||
"devDependencies": { |
|||
"nyc": "^15.0.0", |
|||
"prettier": "1.16.4", |
|||
"tape": "^4.13.2", |
|||
"tslint": "^6.1.1", |
|||
"typescript": "3.8.3" |
|||
}, |
|||
"author": "Andrew Camilleri (Kukks)", |
|||
"license": "MIT", |
|||
"bugs": { |
|||
"url": "https://github.com/Kukks/payjoin-client-js/issues" |
|||
} |
|||
} |
@ -0,0 +1,88 @@ |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const bitcoinjs_lib_1 = require("bitcoinjs-lib"); |
|||
const payments_1 = require("bitcoinjs-lib/types/payments"); |
|||
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; |
|||
}); |
|||
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; |
|||
} |
|||
// 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); |
|||
} |
|||
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
|
|||
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); |
|||
}); |
|||
} |
|||
// 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; |
|||
function requestPayjoin(psbt, payjoinEndpoint) { |
|||
return requestPayjoinWithCustomRemoteCall(psbt, psbt1 => doRequest(psbt1, payjoinEndpoint)); |
|||
} |
|||
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()); |
|||
}); |
|||
} |
@ -0,0 +1,99 @@ |
|||
import { Psbt, Transaction } from 'bitcoinjs-lib'; |
|||
import { p2wpkh } from 'bitcoinjs-lib/types/payments'; |
|||
|
|||
type Nullable<T> = T | null; |
|||
|
|||
export async function requestPayjoinWithCustomRemoteCall(psbt: Psbt, remoteCall: (psbt: Psbt) => Promise<Nullable<Psbt>>) { |
|||
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) return null; |
|||
// no inputs were added?
|
|||
if (clonedPsbt.inputCount <= payjoinPsbt.inputCount) { |
|||
return 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); |
|||
} |
|||
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 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.
|
|||
p2wpkh({ |
|||
pubkey: originalOutput.bip32Derivation.pubkey, |
|||
}).output, |
|||
) |
|||
) |
|||
payjoinPsbt.updateOutput(index, originalOutput); |
|||
}); |
|||
} |
|||
// 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
|
|||
} |
|||
|
|||
export function requestPayjoin(psbt: Psbt, payjoinEndpoint: string) { |
|||
return requestPayjoinWithCustomRemoteCall(psbt, psbt1 => doRequest(psbt1, payjoinEndpoint)); |
|||
} |
|||
|
|||
function getGlobalTransaction(psbt: Psbt): Transaction{ |
|||
// TODO: bitcoinjs-lib to expose outputs to Psbt class
|
|||
// instead of using private (JS has no private) attributes
|
|||
// @ts-ignore
|
|||
return psbt.__CACHE.__TX; |
|||
} |
|||
|
|||
function doRequest(psbt: Psbt, payjoinEndpoint: string): Promise<Nullable<Psbt>> { |
|||
return new Promise<Nullable<Psbt>>((resolve, reject) => { |
|||
if (!psbt) { |
|||
reject(); |
|||
} |
|||
|
|||
const xhr = new XMLHttpRequest(); |
|||
xhr.onreadystatechange = () => { |
|||
if (xhr.readyState !== 4) return; |
|||
if (xhr.status >= 200 && xhr.status < 300) { |
|||
resolve(Psbt.fromHex(xhr.responseText)); |
|||
} else { |
|||
reject(xhr.responseText); |
|||
} |
|||
}; |
|||
xhr.setRequestHeader('Content-Type', 'text/plain'); |
|||
xhr.open('POST', payjoinEndpoint); |
|||
xhr.send(psbt.toHex()); |
|||
}); |
|||
} |
@ -0,0 +1,34 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"allowJs": false, |
|||
"alwaysStrict": true, |
|||
"declaration": true, |
|||
"declarationDir": "./types", |
|||
"esModuleInterop": false, |
|||
"lib": [ |
|||
"es2017", |
|||
"dom" |
|||
], |
|||
"module": "commonjs", |
|||
"noImplicitAny": true, |
|||
"noImplicitThis": true, |
|||
"outDir": "./src", |
|||
"rootDir": "./ts-src", |
|||
"strict": true, |
|||
"strictBindCallApply": true, |
|||
"strictFunctionTypes": true, |
|||
"strictNullChecks": true, |
|||
"strictPropertyInitialization": true, |
|||
"target": "es2017", |
|||
"types": [ |
|||
"node" |
|||
] |
|||
}, |
|||
"include": [ |
|||
"ts-src/*.ts" |
|||
], |
|||
"exclude": [ |
|||
"**/*.spec.ts", |
|||
"node_modules/**/*" |
|||
] |
|||
} |
@ -0,0 +1,34 @@ |
|||
{ |
|||
"defaultSeverity": "error", |
|||
"extends": ["tslint:recommended"], |
|||
"rules": { |
|||
"arrow-parens": [true, "ban-single-arg-parens"], |
|||
"curly": false, |
|||
"indent": [ |
|||
true, |
|||
"spaces", |
|||
2 |
|||
], |
|||
"interface-name": [false], |
|||
"match-default-export-name": true, |
|||
"max-classes-per-file": [false], |
|||
"member-access": [true, "no-public"], |
|||
"no-bitwise": false, |
|||
"no-console": false, |
|||
"no-empty": [true, "allow-empty-catch"], |
|||
"no-implicit-dependencies": true, |
|||
"no-return-await": true, |
|||
"no-var-requires": false, |
|||
"no-unused-expression": false, |
|||
"object-literal-sort-keys": false, |
|||
"quotemark": [true, "single"], |
|||
"variable-name": [ |
|||
true, |
|||
"ban-keywords", |
|||
"check-format", |
|||
"allow-leading-underscore", |
|||
"allow-pascal-case" |
|||
] |
|||
}, |
|||
"rulesDirectory": [] |
|||
} |
@ -0,0 +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 {}; |
Loading…
Reference in new issue