Skip to content

Commit 0c52803

Browse files
committed
Add discouraged unsafe nonsegwit signing
1 parent 7d09fe5 commit 0c52803

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/psbt.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ class Psbt {
6969
__NON_WITNESS_UTXO_BUF_CACHE: [],
7070
__TX_IN_CACHE: {},
7171
__TX: this.data.globalMap.unsignedTx.tx,
72+
// Old TransactionBuilder behavior was to not confirm input values
73+
// before signing. Even though we highly encourage people to get
74+
// the full parent transaction to verify values, the ability to
75+
// sign non-segwit inputs without the full transaction was often
76+
// requested. So the only way to activate is to use @ts-ignore.
77+
// We will disable exporting the Psbt when unsafe sign is active.
78+
// because it is not BIP174 compliant.
79+
__UNSAFE_SIGN_NONSEGWIT: false,
7280
};
7381
if (this.data.inputs.length === 0) this.setVersion(2);
7482
// Make data hidden when enumerating
@@ -313,6 +321,7 @@ class Psbt {
313321
inputIndex,
314322
Object.assign({}, input, { sighashType: sig.hashType }),
315323
this.__CACHE,
324+
true,
316325
)
317326
: { hash: hashCache, script: scriptCache };
318327
sighashCache = sig.hashType;
@@ -513,12 +522,15 @@ class Psbt {
513522
});
514523
}
515524
toBuffer() {
525+
checkCache(this.__CACHE);
516526
return this.data.toBuffer();
517527
}
518528
toHex() {
529+
checkCache(this.__CACHE);
519530
return this.data.toHex();
520531
}
521532
toBase64() {
533+
checkCache(this.__CACHE);
522534
return this.data.toBase64();
523535
}
524536
updateGlobal(updateData) {
@@ -626,6 +638,11 @@ function canFinalize(input, script, scriptType) {
626638
return false;
627639
}
628640
}
641+
function checkCache(cache) {
642+
if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
643+
throw new Error('Not BIP174 compliant, can not export');
644+
}
645+
}
629646
function hasSigs(neededSigs, partialSig, pubkeys) {
630647
if (!partialSig) return false;
631648
let sigs;
@@ -857,6 +874,7 @@ function getHashAndSighashType(
857874
inputIndex,
858875
input,
859876
cache,
877+
false,
860878
sighashTypes,
861879
);
862880
checkScriptForPubkey(pubkey, script, 'sign');
@@ -865,7 +883,7 @@ function getHashAndSighashType(
865883
sighashType,
866884
};
867885
}
868-
function getHashForSig(inputIndex, input, cache, sighashTypes) {
886+
function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
869887
const unsignedTx = cache.__TX;
870888
const sighashType =
871889
input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
@@ -925,11 +943,24 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) {
925943
);
926944
} else {
927945
// non-segwit
928-
if (input.nonWitnessUtxo === undefined)
946+
if (
947+
input.nonWitnessUtxo === undefined &&
948+
cache.__UNSAFE_SIGN_NONSEGWIT === false
949+
)
929950
throw new Error(
930951
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
931952
`${meaningfulScript.toString('hex')}`,
932953
);
954+
if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false)
955+
console.warn(
956+
'Warning: Signing non-segwit inputs without the full parent transaction ' +
957+
'means there is a chance that a miner could feed you incorrect information ' +
958+
'to trick you into paying large fees. This behavior is the same as the old ' +
959+
'TransactionBuilder class when signing non-segwit scripts. You are not ' +
960+
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
961+
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
962+
'*********************',
963+
);
933964
hash = unsignedTx.hashForSignature(
934965
inputIndex,
935966
meaningfulScript,

ts_src/psbt.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,14 @@ export class Psbt {
115115
__NON_WITNESS_UTXO_BUF_CACHE: [],
116116
__TX_IN_CACHE: {},
117117
__TX: (this.data.globalMap.unsignedTx as PsbtTransaction).tx,
118+
// Old TransactionBuilder behavior was to not confirm input values
119+
// before signing. Even though we highly encourage people to get
120+
// the full parent transaction to verify values, the ability to
121+
// sign non-segwit inputs without the full transaction was often
122+
// requested. So the only way to activate is to use @ts-ignore.
123+
// We will disable exporting the Psbt when unsafe sign is active.
124+
// because it is not BIP174 compliant.
125+
__UNSAFE_SIGN_NONSEGWIT: false,
118126
};
119127
if (this.data.inputs.length === 0) this.setVersion(2);
120128

@@ -386,6 +394,7 @@ export class Psbt {
386394
inputIndex,
387395
Object.assign({}, input, { sighashType: sig.hashType }),
388396
this.__CACHE,
397+
true,
389398
)
390399
: { hash: hashCache!, script: scriptCache! };
391400
sighashCache = sig.hashType;
@@ -619,14 +628,17 @@ export class Psbt {
619628
}
620629

621630
toBuffer(): Buffer {
631+
checkCache(this.__CACHE);
622632
return this.data.toBuffer();
623633
}
624634

625635
toHex(): string {
636+
checkCache(this.__CACHE);
626637
return this.data.toHex();
627638
}
628639

629640
toBase64(): string {
641+
checkCache(this.__CACHE);
630642
return this.data.toBase64();
631643
}
632644

@@ -681,6 +693,7 @@ interface PsbtCache {
681693
__FEE_RATE?: number;
682694
__FEE?: number;
683695
__EXTRACTED_TX?: Transaction;
696+
__UNSAFE_SIGN_NONSEGWIT: boolean;
684697
}
685698

686699
interface PsbtOptsOptional {
@@ -825,6 +838,12 @@ function canFinalize(
825838
}
826839
}
827840

841+
function checkCache(cache: PsbtCache): void {
842+
if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
843+
throw new Error('Not BIP174 compliant, can not export');
844+
}
845+
}
846+
828847
function hasSigs(
829848
neededSigs: number,
830849
partialSig?: any[],
@@ -1130,6 +1149,7 @@ function getHashAndSighashType(
11301149
inputIndex,
11311150
input,
11321151
cache,
1152+
false,
11331153
sighashTypes,
11341154
);
11351155
checkScriptForPubkey(pubkey, script, 'sign');
@@ -1143,6 +1163,7 @@ function getHashForSig(
11431163
inputIndex: number,
11441164
input: PsbtInput,
11451165
cache: PsbtCache,
1166+
forValidate: boolean,
11461167
sighashTypes?: number[],
11471168
): {
11481169
script: Buffer;
@@ -1213,11 +1234,24 @@ function getHashForSig(
12131234
);
12141235
} else {
12151236
// non-segwit
1216-
if (input.nonWitnessUtxo === undefined)
1237+
if (
1238+
input.nonWitnessUtxo === undefined &&
1239+
cache.__UNSAFE_SIGN_NONSEGWIT === false
1240+
)
12171241
throw new Error(
12181242
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
12191243
`${meaningfulScript.toString('hex')}`,
12201244
);
1245+
if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false)
1246+
console.warn(
1247+
'Warning: Signing non-segwit inputs without the full parent transaction ' +
1248+
'means there is a chance that a miner could feed you incorrect information ' +
1249+
'to trick you into paying large fees. This behavior is the same as the old ' +
1250+
'TransactionBuilder class when signing non-segwit scripts. You are not ' +
1251+
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
1252+
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
1253+
'*********************',
1254+
);
12211255
hash = unsignedTx.hashForSignature(
12221256
inputIndex,
12231257
meaningfulScript,

0 commit comments

Comments
 (0)