bitcoinjs-lib
Advanced tools
Comparing version 6.0.2 to 6.1.0-rc.0
{ | ||
"name": "bitcoinjs-lib", | ||
"version": "6.0.2", | ||
"version": "6.1.0-rc.0", | ||
"description": "Client-side Bitcoin JavaScript library", | ||
@@ -53,3 +53,3 @@ "main": "./src/index.js", | ||
"bech32": "^2.0.0", | ||
"bip174": "^2.0.1", | ||
"bip174": "^2.1.0", | ||
"bs58check": "^2.1.2", | ||
@@ -77,3 +77,2 @@ "create-hash": "^1.1.0", | ||
"bip68": "^1.0.3", | ||
"bn.js": "^4.11.8", | ||
"bs58": "^4.0.0", | ||
@@ -91,3 +90,3 @@ "dhttp": "^3.0.0", | ||
"rimraf": "^2.6.3", | ||
"tiny-secp256k1": "^2.1.2", | ||
"tiny-secp256k1": "^2.2.0", | ||
"ts-node": "^8.3.0", | ||
@@ -94,0 +93,0 @@ "tslint": "^6.1.3", |
@@ -68,5 +68,5 @@ # BitcoinJS (bitcoinjs-lib) | ||
Finally, **adhere to best practice**. | ||
We are not an authorative source of best practice, but, at the very least: | ||
We are not an authoritative source of best practice, but, at the very least: | ||
* [Don't re-use addresses](https://en.bitcoin.it/wiki/Address_reuse). | ||
* [Don't reuse addresses](https://en.bitcoin.it/wiki/Address_reuse). | ||
* Don't share BIP32 extended public keys ('xpubs'). [They are a liability](https://bitcoin.stackexchange.com/questions/56916/derivation-of-parent-private-key-from-non-hardened-child), and it only takes 1 misplaced private key (or a buggy implementation!) and you are vulnerable to **catastrophic fund loss**. | ||
@@ -73,0 +73,0 @@ * [Don't use `Math.random`](https://security.stackexchange.com/questions/181580/why-is-math-random-not-designed-to-be-cryptographically-secure) - in any way - don't. |
@@ -7,10 +7,9 @@ 'use strict'; | ||
const bscript = require('./script'); | ||
const types = require('./types'); | ||
const types_1 = require('./types'); | ||
const bech32_1 = require('bech32'); | ||
const bs58check = require('bs58check'); | ||
const { typeforce } = types; | ||
const FUTURE_SEGWIT_MAX_SIZE = 40; | ||
const FUTURE_SEGWIT_MIN_SIZE = 2; | ||
const FUTURE_SEGWIT_MAX_VERSION = 16; | ||
const FUTURE_SEGWIT_MIN_VERSION = 1; | ||
const FUTURE_SEGWIT_MIN_VERSION = 2; | ||
const FUTURE_SEGWIT_VERSION_DIFF = 0x50; | ||
@@ -73,3 +72,6 @@ const FUTURE_SEGWIT_VERSION_WARNING = | ||
function toBase58Check(hash, version) { | ||
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); | ||
(0, types_1.typeforce)( | ||
(0, types_1.tuple)(types_1.Hash160bit, types_1.UInt8), | ||
arguments, | ||
); | ||
const payload = Buffer.allocUnsafe(21); | ||
@@ -105,2 +107,5 @@ payload.writeUInt8(version, 0); | ||
try { | ||
return payments.p2tr({ output, network }).address; | ||
} catch (e) {} | ||
try { | ||
return _toFutureSegwitAddress(output, network); | ||
@@ -135,2 +140,5 @@ } catch (e) {} | ||
return payments.p2wsh({ hash: decodeBech32.data }).output; | ||
} else if (decodeBech32.version === 1) { | ||
if (decodeBech32.data.length === 32) | ||
return payments.p2tr({ pubkey: decodeBech32.data }).output; | ||
} else if ( | ||
@@ -137,0 +145,0 @@ decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION && |
@@ -15,1 +15,2 @@ import * as address from './address'; | ||
export { Input as TxInput, Output as TxOutput } from './transaction'; | ||
export { initEccLib } from './ecc_lib'; |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.address = void 0; | ||
exports.initEccLib = exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.address = void 0; | ||
const address = require('./address'); | ||
@@ -42,1 +42,8 @@ exports.address = address; | ||
}); | ||
var ecc_lib_1 = require('./ecc_lib'); | ||
Object.defineProperty(exports, 'initEccLib', { | ||
enumerable: true, | ||
get: function() { | ||
return ecc_lib_1.initEccLib; | ||
}, | ||
}); |
@@ -120,2 +120,3 @@ 'use strict'; | ||
OP_NOP10: 185, | ||
OP_CHECKSIGADD: 186, | ||
OP_PUBKEYHASH: 253, | ||
@@ -122,0 +123,0 @@ OP_PUBKEY: 254, |
/// <reference types="node" /> | ||
import { Network } from '../networks'; | ||
import { Taptree } from '../types'; | ||
import { p2data as embed } from './embed'; | ||
@@ -10,2 +11,3 @@ import { p2ms } from './p2ms'; | ||
import { p2wsh } from './p2wsh'; | ||
import { p2tr } from './p2tr'; | ||
export interface Payment { | ||
@@ -21,2 +23,3 @@ name?: string; | ||
signatures?: Buffer[]; | ||
internalPubkey?: Buffer; | ||
pubkey?: Buffer; | ||
@@ -27,2 +30,4 @@ signature?: Buffer; | ||
redeem?: Payment; | ||
redeemVersion?: number; | ||
scriptTree?: Taptree; | ||
witness?: Buffer[]; | ||
@@ -39,2 +44,2 @@ } | ||
export declare type StackFunction = () => Stack; | ||
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; | ||
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr }; |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
exports.p2wsh = exports.p2wpkh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.p2ms = exports.embed = void 0; | ||
exports.p2tr = exports.p2wsh = exports.p2wpkh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.p2ms = exports.embed = void 0; | ||
const embed_1 = require('./embed'); | ||
@@ -53,3 +53,10 @@ Object.defineProperty(exports, 'embed', { | ||
}); | ||
const p2tr_1 = require('./p2tr'); | ||
Object.defineProperty(exports, 'p2tr', { | ||
enumerable: true, | ||
get: function() { | ||
return p2tr_1.p2tr; | ||
}, | ||
}); | ||
// TODO | ||
// witness commitment |
@@ -83,3 +83,6 @@ /// <reference types="node" /> | ||
finalizeAllInputs(): this; | ||
finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this; | ||
finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc | FinalTaprootScriptsFunc): this; | ||
finalizeTaprootInput(inputIndex: number, tapLeafHashToFinalize?: Buffer, finalScriptsFunc?: FinalTaprootScriptsFunc): this; | ||
private _finalizeInput; | ||
private _finalizeTaprootInput; | ||
getInputType(inputIndex: number): AllScriptType; | ||
@@ -92,2 +95,4 @@ inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean; | ||
validateSignaturesOfInput(inputIndex: number, validator: ValidateSigFunction, pubkey?: Buffer): boolean; | ||
private _validateSignaturesOfInput; | ||
private validateSignaturesOfTaprootInput; | ||
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; | ||
@@ -100,3 +105,10 @@ signAllInputsHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; | ||
signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; | ||
signTaprootInput(inputIndex: number, keyPair: Signer, tapLeafHashToSign?: Buffer, sighashTypes?: number[]): this; | ||
private _signInput; | ||
private _signTaprootInput; | ||
signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; | ||
signTaprootInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, tapLeafHash?: Buffer, sighashTypes?: number[]): Promise<void>; | ||
private _signInputAsync; | ||
private _signTaprootInputAsync; | ||
private checkTaprootHashesForSig; | ||
toBuffer(): Buffer; | ||
@@ -161,2 +173,3 @@ toHex(): string; | ||
sign(hash: Buffer, lowR?: boolean): Buffer; | ||
signSchnorr?(hash: Buffer): Buffer; | ||
getPublicKey?(): Buffer; | ||
@@ -168,2 +181,3 @@ } | ||
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; | ||
signSchnorr?(hash: Buffer): Promise<Buffer>; | ||
getPublicKey?(): Buffer; | ||
@@ -186,3 +200,8 @@ } | ||
}; | ||
declare type FinalTaprootScriptsFunc = (inputIndex: number, // Which input is it? | ||
input: PsbtInput, // The PSBT input contents | ||
tapLeafHashToFinalize?: Buffer) => { | ||
finalScriptWitness: Buffer | undefined; | ||
}; | ||
declare type AllScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard' | 'p2sh-witnesspubkeyhash' | 'p2sh-pubkeyhash' | 'p2sh-multisig' | 'p2sh-pubkey' | 'p2sh-nonstandard' | 'p2wsh-pubkeyhash' | 'p2wsh-multisig' | 'p2wsh-pubkey' | 'p2wsh-nonstandard' | 'p2sh-p2wsh-pubkeyhash' | 'p2sh-p2wsh-multisig' | 'p2sh-p2wsh-pubkey' | 'p2sh-p2wsh-nonstandard'; | ||
export {}; |
594
src/psbt.js
@@ -9,7 +9,9 @@ 'use strict'; | ||
const bufferutils_1 = require('./bufferutils'); | ||
const crypto_1 = require('./crypto'); | ||
const networks_1 = require('./networks'); | ||
const payments = require('./payments'); | ||
const bip341_1 = require('./payments/bip341'); | ||
const bscript = require('./script'); | ||
const transaction_1 = require('./transaction'); | ||
const bip371_1 = require('./psbt/bip371'); | ||
const psbtutils_1 = require('./psbt/psbtutils'); | ||
/** | ||
@@ -202,2 +204,3 @@ * These are the default arguments for a Psbt instance. | ||
} | ||
(0, bip371_1.checkTaprootInputFields)(inputData, inputData, 'addInput'); | ||
checkInputsForPartialSig(this.data.inputs, 'addInput'); | ||
@@ -242,2 +245,3 @@ if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript); | ||
} | ||
(0, bip371_1.checkTaprootOutputFields)(outputData, outputData, 'addOutput'); | ||
const c = this.__CACHE; | ||
@@ -277,4 +281,29 @@ this.data.addOutput(outputData); | ||
} | ||
finalizeInput(inputIndex, finalScriptsFunc = getFinalScripts) { | ||
finalizeInput(inputIndex, finalScriptsFunc) { | ||
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); | ||
if ((0, bip371_1.isTaprootInput)(input)) | ||
return this._finalizeTaprootInput( | ||
inputIndex, | ||
input, | ||
undefined, | ||
finalScriptsFunc, | ||
); | ||
return this._finalizeInput(inputIndex, input, finalScriptsFunc); | ||
} | ||
finalizeTaprootInput( | ||
inputIndex, | ||
tapLeafHashToFinalize, | ||
finalScriptsFunc = bip371_1.tapScriptFinalizer, | ||
) { | ||
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); | ||
if ((0, bip371_1.isTaprootInput)(input)) | ||
return this._finalizeTaprootInput( | ||
inputIndex, | ||
input, | ||
tapLeafHashToFinalize, | ||
finalScriptsFunc, | ||
); | ||
throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`); | ||
} | ||
_finalizeInput(inputIndex, input, finalScriptsFunc = getFinalScripts) { | ||
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( | ||
@@ -303,2 +332,33 @@ inputIndex, | ||
} | ||
_finalizeTaprootInput( | ||
inputIndex, | ||
input, | ||
tapLeafHashToFinalize, | ||
finalScriptsFunc = bip371_1.tapScriptFinalizer, | ||
) { | ||
if (!input.witnessUtxo) | ||
throw new Error( | ||
`Cannot finalize input #${inputIndex}. Missing withness utxo.`, | ||
); | ||
// Check key spend first. Increased privacy and reduced block space. | ||
if (input.tapKeySig) { | ||
const payment = payments.p2tr({ | ||
output: input.witnessUtxo.script, | ||
signature: input.tapKeySig, | ||
}); | ||
const finalScriptWitness = (0, psbtutils_1.witnessStackToScriptWitness)( | ||
payment.witness, | ||
); | ||
this.data.updateInput(inputIndex, { finalScriptWitness }); | ||
} else { | ||
const { finalScriptWitness } = finalScriptsFunc( | ||
inputIndex, | ||
input, | ||
tapLeafHashToFinalize, | ||
); | ||
this.data.updateInput(inputIndex, { finalScriptWitness }); | ||
} | ||
this.data.clearFinalizedInput(inputIndex); | ||
return this; | ||
} | ||
getInputType(inputIndex) { | ||
@@ -350,2 +410,12 @@ const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); | ||
const input = this.data.inputs[inputIndex]; | ||
if ((0, bip371_1.isTaprootInput)(input)) | ||
return this.validateSignaturesOfTaprootInput( | ||
inputIndex, | ||
validator, | ||
pubkey, | ||
); | ||
return this._validateSignaturesOfInput(inputIndex, validator, pubkey); | ||
} | ||
_validateSignaturesOfInput(inputIndex, validator, pubkey) { | ||
const input = this.data.inputs[inputIndex]; | ||
const partialSig = (input || {}).partialSig; | ||
@@ -383,2 +453,50 @@ if (!input || !partialSig || partialSig.length < 1) | ||
} | ||
validateSignaturesOfTaprootInput(inputIndex, validator, pubkey) { | ||
const input = this.data.inputs[inputIndex]; | ||
const tapKeySig = (input || {}).tapKeySig; | ||
const tapScriptSig = (input || {}).tapScriptSig; | ||
if (!input && !tapKeySig && !(tapScriptSig && !tapScriptSig.length)) | ||
throw new Error('No signatures to validate'); | ||
if (typeof validator !== 'function') | ||
throw new Error('Need validator function to validate signatures'); | ||
pubkey = pubkey && (0, bip371_1.toXOnly)(pubkey); | ||
const allHashses = pubkey | ||
? getTaprootHashesForSig( | ||
inputIndex, | ||
input, | ||
this.data.inputs, | ||
pubkey, | ||
this.__CACHE, | ||
) | ||
: getAllTaprootHashesForSig( | ||
inputIndex, | ||
input, | ||
this.data.inputs, | ||
this.__CACHE, | ||
); | ||
if (!allHashses.length) throw new Error('No signatures for this pubkey'); | ||
const tapKeyHash = allHashses.find(h => !!h.leafHash); | ||
if (tapKeySig && tapKeyHash) { | ||
const isValidTapkeySig = validator( | ||
tapKeyHash.pubkey, | ||
tapKeyHash.hash, | ||
tapKeySig, | ||
); | ||
if (!isValidTapkeySig) return false; | ||
} | ||
if (tapScriptSig) { | ||
for (const tapSig of tapScriptSig) { | ||
const tapSigHash = allHashses.find(h => tapSig.pubkey.equals(h.pubkey)); | ||
if (tapSigHash) { | ||
const isValidTapScriptSig = validator( | ||
tapSig.pubkey, | ||
tapSigHash.hash, | ||
tapSig.signature, | ||
); | ||
if (!isValidTapScriptSig) return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
signAllInputsHD( | ||
@@ -467,6 +585,3 @@ hdKeyPair, | ||
} | ||
signAllInputs( | ||
keyPair, | ||
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], | ||
) { | ||
signAllInputs(keyPair, sighashTypes) { | ||
if (!keyPair || !keyPair.publicKey) | ||
@@ -491,6 +606,3 @@ throw new Error('Need Signer to sign input'); | ||
} | ||
signAllInputsAsync( | ||
keyPair, | ||
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], | ||
) { | ||
signAllInputsAsync(keyPair, sighashTypes) { | ||
return new Promise((resolve, reject) => { | ||
@@ -524,3 +636,32 @@ if (!keyPair || !keyPair.publicKey) | ||
} | ||
signInput( | ||
signInput(inputIndex, keyPair, sighashTypes) { | ||
if (!keyPair || !keyPair.publicKey) | ||
throw new Error('Need Signer to sign input'); | ||
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); | ||
if ((0, bip371_1.isTaprootInput)(input)) { | ||
return this._signTaprootInput( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
undefined, | ||
sighashTypes, | ||
); | ||
} | ||
return this._signInput(inputIndex, keyPair, sighashTypes); | ||
} | ||
signTaprootInput(inputIndex, keyPair, tapLeafHashToSign, sighashTypes) { | ||
if (!keyPair || !keyPair.publicKey) | ||
throw new Error('Need Signer to sign input'); | ||
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); | ||
if ((0, bip371_1.isTaprootInput)(input)) | ||
return this._signTaprootInput( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
tapLeafHashToSign, | ||
sighashTypes, | ||
); | ||
throw new Error(`Input #${inputIndex} is not of type Taproot.`); | ||
} | ||
_signInput( | ||
inputIndex, | ||
@@ -530,4 +671,2 @@ keyPair, | ||
) { | ||
if (!keyPair || !keyPair.publicKey) | ||
throw new Error('Need Signer to sign input'); | ||
const { hash, sighashType } = getHashAndSighashType( | ||
@@ -549,28 +688,178 @@ this.data.inputs, | ||
} | ||
signInputAsync( | ||
_signTaprootInput( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], | ||
tapLeafHashToSign, | ||
allowedSighashTypes = [transaction_1.Transaction.SIGHASH_DEFAULT], | ||
) { | ||
const hashesForSig = this.checkTaprootHashesForSig( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
tapLeafHashToSign, | ||
allowedSighashTypes, | ||
); | ||
const tapKeySig = hashesForSig | ||
.filter(h => !h.leafHash) | ||
.map(h => | ||
(0, bip371_1.serializeTaprootSignature)( | ||
keyPair.signSchnorr(h.hash), | ||
input.sighashType, | ||
), | ||
)[0]; | ||
const tapScriptSig = hashesForSig | ||
.filter(h => !!h.leafHash) | ||
.map(h => ({ | ||
pubkey: (0, bip371_1.toXOnly)(keyPair.publicKey), | ||
signature: (0, bip371_1.serializeTaprootSignature)( | ||
keyPair.signSchnorr(h.hash), | ||
input.sighashType, | ||
), | ||
leafHash: h.leafHash, | ||
})); | ||
if (tapKeySig) { | ||
this.data.updateInput(inputIndex, { tapKeySig }); | ||
} | ||
if (tapScriptSig.length) { | ||
this.data.updateInput(inputIndex, { tapScriptSig }); | ||
} | ||
return this; | ||
} | ||
signInputAsync(inputIndex, keyPair, sighashTypes) { | ||
return Promise.resolve().then(() => { | ||
if (!keyPair || !keyPair.publicKey) | ||
throw new Error('Need Signer to sign input'); | ||
const { hash, sighashType } = getHashAndSighashType( | ||
this.data.inputs, | ||
inputIndex, | ||
keyPair.publicKey, | ||
this.__CACHE, | ||
sighashTypes, | ||
); | ||
return Promise.resolve(keyPair.sign(hash)).then(signature => { | ||
const partialSig = [ | ||
{ | ||
pubkey: keyPair.publicKey, | ||
signature: bscript.signature.encode(signature, sighashType), | ||
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); | ||
if ((0, bip371_1.isTaprootInput)(input)) | ||
return this._signTaprootInputAsync( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
undefined, | ||
sighashTypes, | ||
); | ||
return this._signInputAsync(inputIndex, keyPair, sighashTypes); | ||
}); | ||
} | ||
signTaprootInputAsync(inputIndex, keyPair, tapLeafHash, sighashTypes) { | ||
return Promise.resolve().then(() => { | ||
if (!keyPair || !keyPair.publicKey) | ||
throw new Error('Need Signer to sign input'); | ||
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); | ||
if ((0, bip371_1.isTaprootInput)(input)) | ||
return this._signTaprootInputAsync( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
tapLeafHash, | ||
sighashTypes, | ||
); | ||
throw new Error(`Input #${inputIndex} is not of type Taproot.`); | ||
}); | ||
} | ||
_signInputAsync( | ||
inputIndex, | ||
keyPair, | ||
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], | ||
) { | ||
const { hash, sighashType } = getHashAndSighashType( | ||
this.data.inputs, | ||
inputIndex, | ||
keyPair.publicKey, | ||
this.__CACHE, | ||
sighashTypes, | ||
); | ||
return Promise.resolve(keyPair.sign(hash)).then(signature => { | ||
const partialSig = [ | ||
{ | ||
pubkey: keyPair.publicKey, | ||
signature: bscript.signature.encode(signature, sighashType), | ||
}, | ||
]; | ||
this.data.updateInput(inputIndex, { partialSig }); | ||
}); | ||
} | ||
async _signTaprootInputAsync( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
tapLeafHash, | ||
sighashTypes = [transaction_1.Transaction.SIGHASH_DEFAULT], | ||
) { | ||
const hashesForSig = this.checkTaprootHashesForSig( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
tapLeafHash, | ||
sighashTypes, | ||
); | ||
const signaturePromises = []; | ||
const tapKeyHash = hashesForSig.filter(h => !h.leafHash)[0]; | ||
if (tapKeyHash) { | ||
const tapKeySigPromise = Promise.resolve( | ||
keyPair.signSchnorr(tapKeyHash.hash), | ||
).then(sig => { | ||
return { | ||
tapKeySig: (0, bip371_1.serializeTaprootSignature)( | ||
sig, | ||
input.sighashType, | ||
), | ||
}; | ||
}); | ||
signaturePromises.push(tapKeySigPromise); | ||
} | ||
const tapScriptHashes = hashesForSig.filter(h => !!h.leafHash); | ||
if (tapScriptHashes.length) { | ||
const tapScriptSigPromises = tapScriptHashes.map(tsh => { | ||
return Promise.resolve(keyPair.signSchnorr(tsh.hash)).then( | ||
signature => { | ||
const tapScriptSig = [ | ||
{ | ||
pubkey: (0, bip371_1.toXOnly)(keyPair.publicKey), | ||
signature: (0, bip371_1.serializeTaprootSignature)( | ||
signature, | ||
input.sighashType, | ||
), | ||
leafHash: tsh.leafHash, | ||
}, | ||
]; | ||
return { tapScriptSig }; | ||
}, | ||
]; | ||
this.data.updateInput(inputIndex, { partialSig }); | ||
); | ||
}); | ||
signaturePromises.push(...tapScriptSigPromises); | ||
} | ||
return Promise.all(signaturePromises).then(results => { | ||
results.forEach(v => this.data.updateInput(inputIndex, v)); | ||
}); | ||
} | ||
checkTaprootHashesForSig( | ||
inputIndex, | ||
input, | ||
keyPair, | ||
tapLeafHashToSign, | ||
allowedSighashTypes, | ||
) { | ||
if (typeof keyPair.signSchnorr !== 'function') | ||
throw new Error( | ||
`Need Schnorr Signer to sign taproot input #${inputIndex}.`, | ||
); | ||
const hashesForSig = getTaprootHashesForSig( | ||
inputIndex, | ||
input, | ||
this.data.inputs, | ||
keyPair.publicKey, | ||
this.__CACHE, | ||
tapLeafHashToSign, | ||
allowedSighashTypes, | ||
); | ||
if (!hashesForSig || !hashesForSig.length) | ||
throw new Error( | ||
`Can not sign for input #${inputIndex} with the key ${keyPair.publicKey.toString( | ||
'hex', | ||
)}`, | ||
); | ||
return hashesForSig; | ||
} | ||
toBuffer() { | ||
@@ -594,2 +883,7 @@ checkCache(this.__CACHE); | ||
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript); | ||
(0, bip371_1.checkTaprootInputFields)( | ||
this.data.inputs[inputIndex], | ||
updateData, | ||
'updateInput', | ||
); | ||
this.data.updateInput(inputIndex, updateData); | ||
@@ -606,2 +900,8 @@ if (updateData.nonWitnessUtxo) { | ||
updateOutput(outputIndex, updateData) { | ||
const outputData = this.data.outputs[outputIndex]; | ||
(0, bip371_1.checkTaprootOutputFields)( | ||
outputData, | ||
updateData, | ||
'updateOutput', | ||
); | ||
this.data.updateOutput(outputIndex, updateData); | ||
@@ -720,18 +1020,2 @@ return this; | ||
} | ||
function isPaymentFactory(payment) { | ||
return script => { | ||
try { | ||
payment({ output: script }); | ||
return true; | ||
} catch (err) { | ||
return false; | ||
} | ||
}; | ||
} | ||
const isP2MS = isPaymentFactory(payments.p2ms); | ||
const isP2PK = isPaymentFactory(payments.p2pk); | ||
const isP2PKH = isPaymentFactory(payments.p2pkh); | ||
const isP2WPKH = isPaymentFactory(payments.p2wpkh); | ||
const isP2WSHScript = isPaymentFactory(payments.p2wsh); | ||
const isP2SHScript = isPaymentFactory(payments.p2sh); | ||
function bip32DerivationIsMine(root) { | ||
@@ -770,33 +1054,7 @@ return d => { | ||
inputs.forEach(input => { | ||
let throws = false; | ||
let pSigs = []; | ||
if ((input.partialSig || []).length === 0) { | ||
if (!input.finalScriptSig && !input.finalScriptWitness) return; | ||
pSigs = getPsigsFromInputFinalScripts(input); | ||
} else { | ||
pSigs = input.partialSig; | ||
} | ||
pSigs.forEach(pSig => { | ||
const { hashType } = bscript.signature.decode(pSig.signature); | ||
const whitelist = []; | ||
const isAnyoneCanPay = | ||
hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY; | ||
if (isAnyoneCanPay) whitelist.push('addInput'); | ||
const hashMod = hashType & 0x1f; | ||
switch (hashMod) { | ||
case transaction_1.Transaction.SIGHASH_ALL: | ||
break; | ||
case transaction_1.Transaction.SIGHASH_SINGLE: | ||
case transaction_1.Transaction.SIGHASH_NONE: | ||
whitelist.push('addOutput'); | ||
whitelist.push('setInputSequence'); | ||
break; | ||
} | ||
if (whitelist.indexOf(action) === -1) { | ||
throws = true; | ||
} | ||
}); | ||
if (throws) { | ||
const throws = (0, bip371_1.isTaprootInput)(input) | ||
? (0, bip371_1.checkTaprootInputForSigs)(input, action) | ||
: (0, psbtutils_1.checkInputForSig)(input, action); | ||
if (throws) | ||
throw new Error('Can not modify transaction, signatures exist.'); | ||
} | ||
}); | ||
@@ -815,3 +1073,3 @@ } | ||
function checkScriptForPubkey(pubkey, script, action) { | ||
if (!pubkeyInScript(pubkey, script)) { | ||
if (!(0, psbtutils_1.pubkeyInScript)(pubkey, script)) { | ||
throw new Error( | ||
@@ -910,5 +1168,9 @@ `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, | ||
if (p2wsh) { | ||
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness); | ||
finalScriptWitness = (0, psbtutils_1.witnessStackToScriptWitness)( | ||
p2wsh.witness, | ||
); | ||
} else { | ||
finalScriptWitness = witnessStackToScriptWitness(payment.witness); | ||
finalScriptWitness = (0, psbtutils_1.witnessStackToScriptWitness)( | ||
payment.witness, | ||
); | ||
} | ||
@@ -955,9 +1217,3 @@ if (p2sh) { | ||
input.sighashType || transaction_1.Transaction.SIGHASH_ALL; | ||
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) { | ||
const str = sighashTypeToString(sighashType); | ||
throw new Error( | ||
`Sighash type is not allowed. Retry the sign method passing the ` + | ||
`sighashTypes array of whitelisted types. Sighash type: ${str}`, | ||
); | ||
} | ||
checkSighashTypeAllowed(sighashType, sighashTypes); | ||
let hash; | ||
@@ -1000,3 +1256,3 @@ let prevout; | ||
); | ||
} else if (isP2WPKH(meaningfulScript)) { | ||
} else if ((0, psbtutils_1.isP2WPKH)(meaningfulScript)) { | ||
// P2WPKH uses the P2PKH template for prevoutScript when signing | ||
@@ -1043,2 +1299,85 @@ const signingScript = payments.p2pkh({ hash: meaningfulScript.slice(2) }) | ||
} | ||
function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) { | ||
const allPublicKeys = []; | ||
if (input.tapInternalKey) { | ||
const outputKey = (0, bip371_1.tweakInternalPubKey)(inputIndex, input); | ||
allPublicKeys.push(outputKey); | ||
} | ||
if (input.tapScriptSig) { | ||
const tapScriptPubkeys = input.tapScriptSig.map(tss => tss.pubkey); | ||
allPublicKeys.push(...tapScriptPubkeys); | ||
} | ||
const allHashes = allPublicKeys.map(pubicKey => | ||
getTaprootHashesForSig(inputIndex, input, inputs, pubicKey, cache), | ||
); | ||
return allHashes.flat(); | ||
} | ||
function getTaprootHashesForSig( | ||
inputIndex, | ||
input, | ||
inputs, | ||
pubkey, | ||
cache, | ||
tapLeafHashToSign, | ||
allowedSighashTypes, | ||
) { | ||
const unsignedTx = cache.__TX; | ||
const sighashType = | ||
input.sighashType || transaction_1.Transaction.SIGHASH_DEFAULT; | ||
checkSighashTypeAllowed(sighashType, allowedSighashTypes); | ||
const prevOuts = inputs.map((i, index) => | ||
getScriptAndAmountFromUtxo(index, i, cache), | ||
); | ||
const signingScripts = prevOuts.map(o => o.script); | ||
const values = prevOuts.map(o => o.value); | ||
const hashes = []; | ||
if (input.tapInternalKey && !tapLeafHashToSign) { | ||
const outputKey = (0, bip371_1.tweakInternalPubKey)(inputIndex, input); | ||
if ((0, bip371_1.toXOnly)(pubkey).equals(outputKey)) { | ||
const tapKeyHash = unsignedTx.hashForWitnessV1( | ||
inputIndex, | ||
signingScripts, | ||
values, | ||
sighashType, | ||
); | ||
hashes.push({ pubkey, hash: tapKeyHash }); | ||
} | ||
} | ||
const tapLeafHashes = (input.tapLeafScript || []) | ||
.filter(tapLeaf => (0, psbtutils_1.pubkeyInScript)(pubkey, tapLeaf.script)) | ||
.map(tapLeaf => { | ||
const hash = (0, bip341_1.tapleafHash)({ | ||
output: tapLeaf.script, | ||
version: tapLeaf.leafVersion, | ||
}); | ||
return Object.assign({ hash }, tapLeaf); | ||
}) | ||
.filter( | ||
tapLeaf => !tapLeafHashToSign || tapLeafHashToSign.equals(tapLeaf.hash), | ||
) | ||
.map(tapLeaf => { | ||
const tapScriptHash = unsignedTx.hashForWitnessV1( | ||
inputIndex, | ||
signingScripts, | ||
values, | ||
transaction_1.Transaction.SIGHASH_DEFAULT, | ||
tapLeaf.hash, | ||
); | ||
return { | ||
pubkey, | ||
hash: tapScriptHash, | ||
leafHash: tapLeaf.hash, | ||
}; | ||
}); | ||
return hashes.concat(tapLeafHashes); | ||
} | ||
function checkSighashTypeAllowed(sighashType, sighashTypes) { | ||
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) { | ||
const str = sighashTypeToString(sighashType); | ||
throw new Error( | ||
`Sighash type is not allowed. Retry the sign method passing the ` + | ||
`sighashTypes array of whitelisted types. Sighash type: ${str}`, | ||
); | ||
} | ||
} | ||
function getPayment(script, scriptType, partialSig) { | ||
@@ -1077,16 +1416,2 @@ let payment; | ||
} | ||
function getPsigsFromInputFinalScripts(input) { | ||
const scriptItems = !input.finalScriptSig | ||
? [] | ||
: bscript.decompile(input.finalScriptSig) || []; | ||
const witnessItems = !input.finalScriptWitness | ||
? [] | ||
: bscript.decompile(input.finalScriptWitness) || []; | ||
return scriptItems | ||
.concat(witnessItems) | ||
.filter(item => { | ||
return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item); | ||
}) | ||
.map(sig => ({ signature: sig })); | ||
} | ||
function getScriptFromInput(inputIndex, input, cache) { | ||
@@ -1119,3 +1444,3 @@ const unsignedTx = cache.__TX; | ||
} | ||
if (input.witnessScript || isP2WPKH(res.script)) { | ||
if (input.witnessScript || (0, psbtutils_1.isP2WPKH)(res.script)) { | ||
res.isSegwit = true; | ||
@@ -1210,24 +1535,2 @@ } | ||
} | ||
function witnessStackToScriptWitness(witness) { | ||
let buffer = Buffer.allocUnsafe(0); | ||
function writeSlice(slice) { | ||
buffer = Buffer.concat([buffer, Buffer.from(slice)]); | ||
} | ||
function writeVarInt(i) { | ||
const currentLen = buffer.length; | ||
const varintLen = varuint.encodingLength(i); | ||
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); | ||
varuint.encode(i, buffer, currentLen); | ||
} | ||
function writeVarSlice(slice) { | ||
writeVarInt(slice.length); | ||
writeSlice(slice); | ||
} | ||
function writeVector(vector) { | ||
writeVarInt(vector.length); | ||
vector.forEach(writeVarSlice); | ||
} | ||
writeVector(witness); | ||
return buffer; | ||
} | ||
function addNonWitnessTxCache(cache, input, inputIndex) { | ||
@@ -1295,4 +1598,11 @@ cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo; | ||
function getScriptFromUtxo(inputIndex, input, cache) { | ||
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache); | ||
return script; | ||
} | ||
function getScriptAndAmountFromUtxo(inputIndex, input, cache) { | ||
if (input.witnessUtxo !== undefined) { | ||
return input.witnessUtxo.script; | ||
return { | ||
script: input.witnessUtxo.script, | ||
value: input.witnessUtxo.value, | ||
}; | ||
} else if (input.nonWitnessUtxo !== undefined) { | ||
@@ -1304,3 +1614,4 @@ const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( | ||
); | ||
return nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script; | ||
const o = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index]; | ||
return { script: o.script, value: o.value }; | ||
} else { | ||
@@ -1319,3 +1630,3 @@ throw new Error("Can't find pubkey in input without Utxo data"); | ||
); | ||
return pubkeyInScript(pubkey, meaningfulScript); | ||
return (0, psbtutils_1.pubkeyInScript)(pubkey, meaningfulScript); | ||
} | ||
@@ -1331,3 +1642,3 @@ function pubkeyInOutput(pubkey, output, outputIndex, cache) { | ||
); | ||
return pubkeyInScript(pubkey, meaningfulScript); | ||
return (0, psbtutils_1.pubkeyInScript)(pubkey, meaningfulScript); | ||
} | ||
@@ -1380,5 +1691,6 @@ function redeemFromFinalScriptSig(finalScript) { | ||
) { | ||
const isP2SH = isP2SHScript(script); | ||
const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript); | ||
const isP2WSH = isP2WSHScript(script); | ||
const isP2SH = (0, psbtutils_1.isP2SHScript)(script); | ||
const isP2SHP2WSH = | ||
isP2SH && redeemScript && (0, psbtutils_1.isP2WSHScript)(redeemScript); | ||
const isP2WSH = (0, psbtutils_1.isP2WSHScript)(script); | ||
if (isP2SH && redeemScript === undefined) | ||
@@ -1418,20 +1730,14 @@ throw new Error('scriptPubkey is P2SH but redeemScript missing'); | ||
function checkInvalidP2WSH(script) { | ||
if (isP2WPKH(script) || isP2SHScript(script)) { | ||
if ( | ||
(0, psbtutils_1.isP2WPKH)(script) || | ||
(0, psbtutils_1.isP2SHScript)(script) | ||
) { | ||
throw new Error('P2WPKH or P2SH can not be contained within P2WSH'); | ||
} | ||
} | ||
function pubkeyInScript(pubkey, script) { | ||
const pubkeyHash = (0, crypto_1.hash160)(pubkey); | ||
const decompiled = bscript.decompile(script); | ||
if (decompiled === null) throw new Error('Unknown script error'); | ||
return decompiled.some(element => { | ||
if (typeof element === 'number') return false; | ||
return element.equals(pubkey) || element.equals(pubkeyHash); | ||
}); | ||
} | ||
function classifyScript(script) { | ||
if (isP2WPKH(script)) return 'witnesspubkeyhash'; | ||
if (isP2PKH(script)) return 'pubkeyhash'; | ||
if (isP2MS(script)) return 'multisig'; | ||
if (isP2PK(script)) return 'pubkey'; | ||
if ((0, psbtutils_1.isP2WPKH)(script)) return 'witnesspubkeyhash'; | ||
if ((0, psbtutils_1.isP2PKH)(script)) return 'pubkeyhash'; | ||
if ((0, psbtutils_1.isP2MS)(script)) return 'multisig'; | ||
if ((0, psbtutils_1.isP2PK)(script)) return 'pubkey'; | ||
return 'nonstandard'; | ||
@@ -1438,0 +1744,0 @@ } |
@@ -13,2 +13,25 @@ /// <reference types="node" /> | ||
export declare const Network: any; | ||
export interface XOnlyPointAddTweakResult { | ||
parity: 1 | 0; | ||
xOnlyPubkey: Uint8Array; | ||
} | ||
export interface Tapleaf { | ||
output: Buffer; | ||
version?: number; | ||
} | ||
export declare const TAPLEAF_VERSION_MASK = 254; | ||
export declare function isTapleaf(o: any): o is Tapleaf; | ||
/** | ||
* Binary tree repsenting script path spends for a Taproot input. | ||
* Each node is either a single Tapleaf, or a pair of Tapleaf | Taptree. | ||
* The tree has no balancing requirements. | ||
*/ | ||
export declare type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf; | ||
export declare function isTaptree(scriptTree: any): scriptTree is Taptree; | ||
export interface TinySecp256k1Interface { | ||
isXOnlyPoint(p: Uint8Array): boolean; | ||
xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null; | ||
privateAdd(d: Uint8Array, tweak: Uint8Array): Uint8Array | null; | ||
privateNegate(d: Uint8Array): Uint8Array; | ||
} | ||
export declare const Buffer256bit: any; | ||
@@ -15,0 +38,0 @@ export declare const Hash160bit: any; |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
exports.oneOf = exports.Null = exports.BufferN = exports.Function = exports.UInt32 = exports.UInt8 = exports.tuple = exports.maybe = exports.Hex = exports.Buffer = exports.String = exports.Boolean = exports.Array = exports.Number = exports.Hash256bit = exports.Hash160bit = exports.Buffer256bit = exports.Network = exports.ECPoint = exports.Satoshi = exports.Signer = exports.BIP32Path = exports.UInt31 = exports.isPoint = exports.typeforce = void 0; | ||
exports.oneOf = exports.Null = exports.BufferN = exports.Function = exports.UInt32 = exports.UInt8 = exports.tuple = exports.maybe = exports.Hex = exports.Buffer = exports.String = exports.Boolean = exports.Array = exports.Number = exports.Hash256bit = exports.Hash160bit = exports.Buffer256bit = exports.isTaptree = exports.isTapleaf = exports.TAPLEAF_VERSION_MASK = exports.Network = exports.ECPoint = exports.Satoshi = exports.Signer = exports.BIP32Path = exports.UInt31 = exports.isPoint = exports.typeforce = void 0; | ||
const buffer_1 = require('buffer'); | ||
@@ -71,2 +71,17 @@ exports.typeforce = require('typeforce'); | ||
}); | ||
exports.TAPLEAF_VERSION_MASK = 0xfe; | ||
function isTapleaf(o) { | ||
if (!o || !('output' in o)) return false; | ||
if (!buffer_1.Buffer.isBuffer(o.output)) return false; | ||
if (o.version !== undefined) | ||
return (o.version & exports.TAPLEAF_VERSION_MASK) === o.version; | ||
return true; | ||
} | ||
exports.isTapleaf = isTapleaf; | ||
function isTaptree(scriptTree) { | ||
if (!(0, exports.Array)(scriptTree)) return isTapleaf(scriptTree); | ||
if (scriptTree.length !== 2) return false; | ||
return scriptTree.every(t => isTaptree(t)); | ||
} | ||
exports.isTaptree = isTaptree; | ||
exports.Buffer256bit = exports.typeforce.BufferN(32); | ||
@@ -73,0 +88,0 @@ exports.Hash160bit = exports.typeforce.BufferN(20); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
233087
30
63
6305
2
Updatedbip174@^2.1.0