Socket
Socket
Sign inDemoInstall

bitcoinjs-lib

Package Overview
Dependencies
Maintainers
3
Versions
88
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bitcoinjs-lib - npm Package Compare versions

Comparing version 5.1.10 to 5.2.0

7

CHANGELOG.md

@@ -0,1 +1,8 @@

# 5.2.0
__changed__
- Updated PSBT to allow for witnessUtxo and nonWitnessUtxo simultaneously (Re: segwit psbt bug) (#1563)
__added__
- PSBT methods `getInputType`, `inputHasPubkey`, `inputHasHDKey`, `outputHasPubkey`, `outputHasHDKey` (#1563)
# 5.1.10

@@ -2,0 +9,0 @@ __fixed__

4

package.json
{
"name": "bitcoinjs-lib",
"version": "5.1.10",
"version": "5.2.0",
"description": "Client-side Bitcoin JavaScript library",

@@ -53,3 +53,3 @@ "main": "./src/index.js",

"bech32": "^1.1.2",
"bip174": "^1.0.1",
"bip174": "^2.0.1",
"bip32": "^2.0.4",

@@ -56,0 +56,0 @@ "bip66": "^1.1.0",

@@ -45,5 +45,5 @@ 'use strict';

function cloneBuffer(buffer) {
const clone = Buffer.alloc(buffer.length);
const clone = Buffer.allocUnsafe(buffer.length);
buffer.copy(clone);
return buffer;
return clone;
}

@@ -50,0 +50,0 @@ exports.cloneBuffer = cloneBuffer;

@@ -72,2 +72,10 @@ 'use strict';

__TX: this.data.globalMap.unsignedTx.tx,
// Old TransactionBuilder behavior was to not confirm input values
// before signing. Even though we highly encourage people to get
// the full parent transaction to verify values, the ability to
// sign non-segwit inputs without the full transaction was often
// requested. So the only way to activate is to use @ts-ignore.
// We will disable exporting the Psbt when unsafe sign is active.
// because it is not BIP174 compliant.
__UNSAFE_SIGN_NONSEGWIT: false,
};

@@ -191,2 +199,3 @@ if (this.data.inputs.length === 0) this.setVersion(2);

checkInputsForPartialSig(this.data.inputs, 'addInput');
if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
const c = this.__CACHE;

@@ -288,2 +297,39 @@ this.data.addInput(inputData);

}
getInputType(inputIndex) {
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
const script = getScriptFromUtxo(inputIndex, input, this.__CACHE);
const result = getMeaningfulScript(
script,
inputIndex,
'input',
input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig),
input.witnessScript ||
redeemFromFinalWitnessScript(input.finalScriptWitness),
);
const type = result.type === 'raw' ? '' : result.type + '-';
const mainType = classifyScript(result.meaningfulScript);
return type + mainType;
}
inputHasPubkey(inputIndex, pubkey) {
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
}
inputHasHDKey(inputIndex, root) {
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
const derivationIsMine = bip32DerivationIsMine(root);
return (
!!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine)
);
}
outputHasPubkey(outputIndex, pubkey) {
const output = utils_1.checkForOutput(this.data.outputs, outputIndex);
return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
}
outputHasHDKey(outputIndex, root) {
const output = utils_1.checkForOutput(this.data.outputs, outputIndex);
const derivationIsMine = bip32DerivationIsMine(root);
return (
!!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine)
);
}
validateSignaturesOfAllInputs() {

@@ -317,2 +363,3 @@ utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one

this.__CACHE,
true,
)

@@ -517,8 +564,11 @@ : { hash: hashCache, script: scriptCache };

toBuffer() {
checkCache(this.__CACHE);
return this.data.toBuffer();
}
toHex() {
checkCache(this.__CACHE);
return this.data.toHex();
}
toBase64() {
checkCache(this.__CACHE);
return this.data.toBase64();

@@ -531,2 +581,3 @@ }

updateInput(inputIndex, updateData) {
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
this.data.updateInput(inputIndex, updateData);

@@ -632,2 +683,7 @@ if (updateData.nonWitnessUtxo) {

}
function checkCache(cache) {
if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
throw new Error('Not BIP174 compliant, can not export');
}
}
function hasSigs(neededSigs, partialSig, pubkeys) {

@@ -668,2 +724,10 @@ if (!partialSig) return false;

const isP2WSHScript = isPaymentFactory(payments.p2wsh);
const isP2SHScript = isPaymentFactory(payments.p2sh);
function bip32DerivationIsMine(root) {
return d => {
if (!d.masterFingerprint.equals(root.fingerprint)) return false;
if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false;
return true;
};
}
function check32Bit(num) {

@@ -739,10 +803,3 @@ if (

function checkScriptForPubkey(pubkey, script, action) {
const pubkeyHash = crypto_1.hash160(pubkey);
const decompiled = bscript.decompile(script);
if (decompiled === null) throw new Error('Unknown script error');
const hasKey = decompiled.some(element => {
if (typeof element === 'number') return false;
return element.equals(pubkey) || element.equals(pubkeyHash);
});
if (!hasKey) {
if (!pubkeyInScript(pubkey, script)) {
throw new Error(

@@ -779,3 +836,3 @@ `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,

function scriptCheckerFactory(payment, paymentScriptName) {
return (inputIndex, scriptPubKey, redeemScript) => {
return (inputIndex, scriptPubKey, redeemScript, ioType) => {
const redeemScriptOutput = payment({

@@ -786,3 +843,3 @@ redeem: { output: redeemScript },

throw new Error(
`${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
`${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`,
);

@@ -874,2 +931,3 @@ }

cache,
false,
sighashTypes,

@@ -883,3 +941,3 @@ );

}
function getHashForSig(inputIndex, input, cache, sighashTypes) {
function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
const unsignedTx = cache.__TX;

@@ -896,3 +954,3 @@ const sighashType =

let hash;
let script;
let prevout;
if (input.nonWitnessUtxo) {

@@ -913,79 +971,60 @@ const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(

const prevoutIndex = unsignedTx.ins[inputIndex].index;
const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
if (input.redeemScript) {
// If a redeemScript is provided, the scriptPubKey must be for that redeemScript
checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
script = input.redeemScript;
} else {
script = prevout.script;
}
if (isP2WSHScript(script)) {
if (!input.witnessScript)
throw new Error('Segwit input needs witnessScript if not P2WPKH');
checkWitnessScript(inputIndex, script, input.witnessScript);
hash = unsignedTx.hashForWitnessV0(
inputIndex,
input.witnessScript,
prevout.value,
sighashType,
);
script = input.witnessScript;
} else if (isP2WPKH(script)) {
// P2WPKH uses the P2PKH template for prevoutScript when signing
const signingScript = payments.p2pkh({ hash: script.slice(2) }).output;
hash = unsignedTx.hashForWitnessV0(
inputIndex,
signingScript,
prevout.value,
sighashType,
);
} else {
hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
}
prevout = nonWitnessUtxoTx.outs[prevoutIndex];
} else if (input.witnessUtxo) {
let _script; // so we don't shadow the `let script` above
if (input.redeemScript) {
// If a redeemScript is provided, the scriptPubKey must be for that redeemScript
checkRedeemScript(
inputIndex,
input.witnessUtxo.script,
input.redeemScript,
);
_script = input.redeemScript;
} else {
_script = input.witnessUtxo.script;
}
if (isP2WPKH(_script)) {
// P2WPKH uses the P2PKH template for prevoutScript when signing
const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output;
hash = unsignedTx.hashForWitnessV0(
inputIndex,
signingScript,
input.witnessUtxo.value,
sighashType,
);
script = _script;
} else if (isP2WSHScript(_script)) {
if (!input.witnessScript)
throw new Error('Segwit input needs witnessScript if not P2WPKH');
checkWitnessScript(inputIndex, _script, input.witnessScript);
hash = unsignedTx.hashForWitnessV0(
inputIndex,
input.witnessScript,
input.witnessUtxo.value,
sighashType,
);
// want to make sure the script we return is the actual meaningful script
script = input.witnessScript;
} else {
prevout = input.witnessUtxo;
} else {
throw new Error('Need a Utxo input item for signing');
}
const { meaningfulScript, type } = getMeaningfulScript(
prevout.script,
inputIndex,
'input',
input.redeemScript,
input.witnessScript,
);
if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
hash = unsignedTx.hashForWitnessV0(
inputIndex,
meaningfulScript,
prevout.value,
sighashType,
);
} else if (isP2WPKH(meaningfulScript)) {
// P2WPKH uses the P2PKH template for prevoutScript when signing
const signingScript = payments.p2pkh({ hash: meaningfulScript.slice(2) })
.output;
hash = unsignedTx.hashForWitnessV0(
inputIndex,
signingScript,
prevout.value,
sighashType,
);
} else {
// non-segwit
if (
input.nonWitnessUtxo === undefined &&
cache.__UNSAFE_SIGN_NONSEGWIT === false
)
throw new Error(
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
`${_script.toString('hex')}`,
`${meaningfulScript.toString('hex')}`,
);
}
} else {
throw new Error('Need a Utxo input item for signing');
if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false)
console.warn(
'Warning: Signing non-segwit inputs without the full parent transaction ' +
'means there is a chance that a miner could feed you incorrect information ' +
'to trick you into paying large fees. This behavior is the same as the old ' +
'TransactionBuilder class when signing non-segwit scripts. You are not ' +
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
'*********************',
);
hash = unsignedTx.hashForSignature(
inputIndex,
meaningfulScript,
sighashType,
);
}
return {
script,
script: meaningfulScript,
sighashType,

@@ -1242,2 +1281,125 @@ hash,

}
function getScriptFromUtxo(inputIndex, input, cache) {
if (input.witnessUtxo !== undefined) {
return input.witnessUtxo.script;
} else if (input.nonWitnessUtxo !== undefined) {
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
cache,
input,
inputIndex,
);
return nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script;
} else {
throw new Error("Can't find pubkey in input without Utxo data");
}
}
function pubkeyInInput(pubkey, input, inputIndex, cache) {
const script = getScriptFromUtxo(inputIndex, input, cache);
const { meaningfulScript } = getMeaningfulScript(
script,
inputIndex,
'input',
input.redeemScript,
input.witnessScript,
);
return pubkeyInScript(pubkey, meaningfulScript);
}
function pubkeyInOutput(pubkey, output, outputIndex, cache) {
const script = cache.__TX.outs[outputIndex].script;
const { meaningfulScript } = getMeaningfulScript(
script,
outputIndex,
'output',
output.redeemScript,
output.witnessScript,
);
return pubkeyInScript(pubkey, meaningfulScript);
}
function redeemFromFinalScriptSig(finalScript) {
if (!finalScript) return;
const decomp = bscript.decompile(finalScript);
if (!decomp) return;
const lastItem = decomp[decomp.length - 1];
if (
!Buffer.isBuffer(lastItem) ||
isPubkeyLike(lastItem) ||
isSigLike(lastItem)
)
return;
const sDecomp = bscript.decompile(lastItem);
if (!sDecomp) return;
return lastItem;
}
function redeemFromFinalWitnessScript(finalScript) {
if (!finalScript) return;
const decomp = scriptWitnessToWitnessStack(finalScript);
const lastItem = decomp[decomp.length - 1];
if (isPubkeyLike(lastItem)) return;
const sDecomp = bscript.decompile(lastItem);
if (!sDecomp) return;
return lastItem;
}
function isPubkeyLike(buf) {
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
}
function isSigLike(buf) {
return bscript.isCanonicalScriptSignature(buf);
}
function getMeaningfulScript(
script,
index,
ioType,
redeemScript,
witnessScript,
) {
const isP2SH = isP2SHScript(script);
const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript);
const isP2WSH = isP2WSHScript(script);
if (isP2SH && redeemScript === undefined)
throw new Error('scriptPubkey is P2SH but redeemScript missing');
if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
throw new Error(
'scriptPubkey or redeemScript is P2WSH but witnessScript missing',
);
let meaningfulScript;
if (isP2SHP2WSH) {
meaningfulScript = witnessScript;
checkRedeemScript(index, script, redeemScript, ioType);
checkWitnessScript(index, redeemScript, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript);
} else if (isP2WSH) {
meaningfulScript = witnessScript;
checkWitnessScript(index, script, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript);
} else if (isP2SH) {
meaningfulScript = redeemScript;
checkRedeemScript(index, script, redeemScript, ioType);
} else {
meaningfulScript = script;
}
return {
meaningfulScript,
type: isP2SHP2WSH
? 'p2sh-p2wsh'
: isP2SH
? 'p2sh'
: isP2WSH
? 'p2wsh'
: 'raw',
};
}
function checkInvalidP2WSH(script) {
if (isP2WPKH(script) || isP2SHScript(script)) {
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
}
}
function pubkeyInScript(pubkey, script) {
const pubkeyHash = 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) {

@@ -1244,0 +1406,0 @@ if (isP2WPKH(script)) return 'witnesspubkeyhash';

@@ -10,3 +10,3 @@ import * as bip32 from 'bip32';

export { Block } from './block';
export { Psbt } from './psbt';
export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt';
export { OPS as opcodes } from './script';

@@ -13,0 +13,0 @@ export { Transaction } from './transaction';

@@ -6,2 +6,8 @@ import { Psbt as PsbtBase } from 'bip174';

import { Transaction } from './transaction';
export interface PsbtTxInput extends TransactionInput {
hash: Buffer;
}
export interface PsbtTxOutput extends TransactionOutput {
address: string | undefined;
}
/**

@@ -50,4 +56,4 @@ * Psbt class can parse and generate a PSBT binary based off of the BIP174.

locktime: number;
readonly txInputs: TransactionInput[];
readonly txOutputs: TransactionOutput[];
readonly txInputs: PsbtTxInput[];
readonly txOutputs: PsbtTxOutput[];
combine(...those: Psbt[]): this;

@@ -68,2 +74,7 @@ clone(): Psbt;

finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this;
getInputType(inputIndex: number): AllScriptType;
inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean;
inputHasHDKey(inputIndex: number, root: HDSigner): boolean;
outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean;
outputHasHDKey(outputIndex: number, root: HDSigner): boolean;
validateSignaturesOfAllInputs(): boolean;

@@ -149,2 +160,3 @@ validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean;

};
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 {};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc