Add discouraged unsafe nonsegwit signing

This commit is contained in:
junderw 2020-04-28 18:52:43 +09:00
parent 7d09fe5dcb
commit 0c52803ba1
No known key found for this signature in database
GPG key ID: B256185D3A971908
2 changed files with 68 additions and 3 deletions

View file

@ -69,6 +69,14 @@ class Psbt {
__NON_WITNESS_UTXO_BUF_CACHE: [],
__TX_IN_CACHE: {},
__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,
};
if (this.data.inputs.length === 0) this.setVersion(2);
// Make data hidden when enumerating
@ -313,6 +321,7 @@ class Psbt {
inputIndex,
Object.assign({}, input, { sighashType: sig.hashType }),
this.__CACHE,
true,
)
: { hash: hashCache, script: scriptCache };
sighashCache = sig.hashType;
@ -513,12 +522,15 @@ class Psbt {
});
}
toBuffer() {
checkCache(this.__CACHE);
return this.data.toBuffer();
}
toHex() {
checkCache(this.__CACHE);
return this.data.toHex();
}
toBase64() {
checkCache(this.__CACHE);
return this.data.toBase64();
}
updateGlobal(updateData) {
@ -626,6 +638,11 @@ function canFinalize(input, script, scriptType) {
return false;
}
}
function checkCache(cache) {
if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
throw new Error('Not BIP174 compliant, can not export');
}
}
function hasSigs(neededSigs, partialSig, pubkeys) {
if (!partialSig) return false;
let sigs;
@ -857,6 +874,7 @@ function getHashAndSighashType(
inputIndex,
input,
cache,
false,
sighashTypes,
);
checkScriptForPubkey(pubkey, script, 'sign');
@ -865,7 +883,7 @@ function getHashAndSighashType(
sighashType,
};
}
function getHashForSig(inputIndex, input, cache, sighashTypes) {
function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
const unsignedTx = cache.__TX;
const sighashType =
input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
@ -925,11 +943,24 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) {
);
} else {
// non-segwit
if (input.nonWitnessUtxo === undefined)
if (
input.nonWitnessUtxo === undefined &&
cache.__UNSAFE_SIGN_NONSEGWIT === false
)
throw new Error(
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
`${meaningfulScript.toString('hex')}`,
);
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,

View file

@ -115,6 +115,14 @@ export class Psbt {
__NON_WITNESS_UTXO_BUF_CACHE: [],
__TX_IN_CACHE: {},
__TX: (this.data.globalMap.unsignedTx as PsbtTransaction).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,
};
if (this.data.inputs.length === 0) this.setVersion(2);
@ -386,6 +394,7 @@ export class Psbt {
inputIndex,
Object.assign({}, input, { sighashType: sig.hashType }),
this.__CACHE,
true,
)
: { hash: hashCache!, script: scriptCache! };
sighashCache = sig.hashType;
@ -619,14 +628,17 @@ export class Psbt {
}
toBuffer(): Buffer {
checkCache(this.__CACHE);
return this.data.toBuffer();
}
toHex(): string {
checkCache(this.__CACHE);
return this.data.toHex();
}
toBase64(): string {
checkCache(this.__CACHE);
return this.data.toBase64();
}
@ -681,6 +693,7 @@ interface PsbtCache {
__FEE_RATE?: number;
__FEE?: number;
__EXTRACTED_TX?: Transaction;
__UNSAFE_SIGN_NONSEGWIT: boolean;
}
interface PsbtOptsOptional {
@ -825,6 +838,12 @@ function canFinalize(
}
}
function checkCache(cache: PsbtCache): void {
if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
throw new Error('Not BIP174 compliant, can not export');
}
}
function hasSigs(
neededSigs: number,
partialSig?: any[],
@ -1130,6 +1149,7 @@ function getHashAndSighashType(
inputIndex,
input,
cache,
false,
sighashTypes,
);
checkScriptForPubkey(pubkey, script, 'sign');
@ -1143,6 +1163,7 @@ function getHashForSig(
inputIndex: number,
input: PsbtInput,
cache: PsbtCache,
forValidate: boolean,
sighashTypes?: number[],
): {
script: Buffer;
@ -1213,11 +1234,24 @@ function getHashForSig(
);
} else {
// non-segwit
if (input.nonWitnessUtxo === undefined)
if (
input.nonWitnessUtxo === undefined &&
cache.__UNSAFE_SIGN_NONSEGWIT === false
)
throw new Error(
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
`${meaningfulScript.toString('hex')}`,
);
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,