Skip to content

Commit b1de33d

Browse files
committed
Merge #16821: Fix bug where duplicate PSBT keys are accepted
9743432 Fix bug where duplicate PSBT keys are accepted (John L. Jegutanis) Pull request description: As per the BIP 174 spec a PSBT key cannot be duplicated, however the current code accepts key duplication. The PSBT key/value entries can be duplicated when the value is `empty()` or `IsNull()` for `CScript` or `CTxOut` respectively and if those key/value entries are serialized before the non-empty ones. For example, the following PSBT, included in the test vectors, contains a duplicate field: ``` // magic 70736274ff // global tx //// key 0100 //// value 2a02000000000140420f000000000017a9146e91b72d5593e7d4391e2ff44e91e985c31641f08700000000 //// separator 00 // no inputs // outputs //// key PSBT_OUT_WITNESSSCRIPT 0101 //// value (empty script) 00 //// key PSBT_OUT_WITNESSSCRIPT (same as the above) 0101 //// value (an OP_RETURN script) 016a //// separator 00 ``` ACKs for top commit: achow101: ACK 9743432 instagibbs: code review ACK bitcoin/bitcoin@9743432 Tree-SHA512: 34f4b34c8e6561c6a6ab745cdd319f6687eac6f7cecc735c94035eeca8c5157e17a27f2ae853dbaa6634fcd5a8f4e1c6cc13d1ebd7e563459665d72bb147cc1e
2 parents 97e0581 + 9743432 commit b1de33d

File tree

2 files changed

+28
-11
lines changed

2 files changed

+28
-11
lines changed

src/psbt.h

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ struct PSBTInput
126126

127127
template <typename Stream>
128128
inline void Unserialize(Stream& s) {
129+
// Used for duplicate key detection
130+
std::set<std::vector<unsigned char>> key_lookup;
131+
129132
// Read loop
130133
bool found_sep = false;
131134
while(!s.empty()) {
@@ -147,7 +150,7 @@ struct PSBTInput
147150
switch(type) {
148151
case PSBT_IN_NON_WITNESS_UTXO:
149152
{
150-
if (non_witness_utxo) {
153+
if (!key_lookup.emplace(key).second) {
151154
throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
152155
} else if (key.size() != 1) {
153156
throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
@@ -158,7 +161,7 @@ struct PSBTInput
158161
break;
159162
}
160163
case PSBT_IN_WITNESS_UTXO:
161-
if (!witness_utxo.IsNull()) {
164+
if (!key_lookup.emplace(key).second) {
162165
throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
163166
} else if (key.size() != 1) {
164167
throw std::ios_base::failure("Witness utxo key is more than one byte type");
@@ -189,7 +192,7 @@ struct PSBTInput
189192
break;
190193
}
191194
case PSBT_IN_SIGHASH:
192-
if (sighash_type > 0) {
195+
if (!key_lookup.emplace(key).second) {
193196
throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
194197
} else if (key.size() != 1) {
195198
throw std::ios_base::failure("Sighash type key is more than one byte type");
@@ -198,7 +201,7 @@ struct PSBTInput
198201
break;
199202
case PSBT_IN_REDEEMSCRIPT:
200203
{
201-
if (!redeem_script.empty()) {
204+
if (!key_lookup.emplace(key).second) {
202205
throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
203206
} else if (key.size() != 1) {
204207
throw std::ios_base::failure("Input redeemScript key is more than one byte type");
@@ -208,7 +211,7 @@ struct PSBTInput
208211
}
209212
case PSBT_IN_WITNESSSCRIPT:
210213
{
211-
if (!witness_script.empty()) {
214+
if (!key_lookup.emplace(key).second) {
212215
throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
213216
} else if (key.size() != 1) {
214217
throw std::ios_base::failure("Input witnessScript key is more than one byte type");
@@ -223,7 +226,7 @@ struct PSBTInput
223226
}
224227
case PSBT_IN_SCRIPTSIG:
225228
{
226-
if (!final_script_sig.empty()) {
229+
if (!key_lookup.emplace(key).second) {
227230
throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
228231
} else if (key.size() != 1) {
229232
throw std::ios_base::failure("Final scriptSig key is more than one byte type");
@@ -233,7 +236,7 @@ struct PSBTInput
233236
}
234237
case PSBT_IN_SCRIPTWITNESS:
235238
{
236-
if (!final_script_witness.IsNull()) {
239+
if (!key_lookup.emplace(key).second) {
237240
throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
238241
} else if (key.size() != 1) {
239242
throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
@@ -309,6 +312,9 @@ struct PSBTOutput
309312

310313
template <typename Stream>
311314
inline void Unserialize(Stream& s) {
315+
// Used for duplicate key detection
316+
std::set<std::vector<unsigned char>> key_lookup;
317+
312318
// Read loop
313319
bool found_sep = false;
314320
while(!s.empty()) {
@@ -330,7 +336,7 @@ struct PSBTOutput
330336
switch(type) {
331337
case PSBT_OUT_REDEEMSCRIPT:
332338
{
333-
if (!redeem_script.empty()) {
339+
if (!key_lookup.emplace(key).second) {
334340
throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
335341
} else if (key.size() != 1) {
336342
throw std::ios_base::failure("Output redeemScript key is more than one byte type");
@@ -340,7 +346,7 @@ struct PSBTOutput
340346
}
341347
case PSBT_OUT_WITNESSSCRIPT:
342348
{
343-
if (!witness_script.empty()) {
349+
if (!key_lookup.emplace(key).second) {
344350
throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
345351
} else if (key.size() != 1) {
346352
throw std::ios_base::failure("Output witnessScript key is more than one byte type");
@@ -448,6 +454,9 @@ struct PartiallySignedTransaction
448454
throw std::ios_base::failure("Invalid PSBT magic bytes");
449455
}
450456

457+
// Used for duplicate key detection
458+
std::set<std::vector<unsigned char>> key_lookup;
459+
451460
// Read global data
452461
bool found_sep = false;
453462
while(!s.empty()) {
@@ -469,7 +478,7 @@ struct PartiallySignedTransaction
469478
switch(type) {
470479
case PSBT_GLOBAL_UNSIGNED_TX:
471480
{
472-
if (tx) {
481+
if (!key_lookup.emplace(key).second) {
473482
throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
474483
} else if (key.size() != 1) {
475484
throw std::ios_base::failure("Global unsigned tx key is more than one byte type");

test/functional/data/rpc_psbt.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@
1818
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
1919
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
2020
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
21-
"cHNidP8BAHMCAAAAAbiWoY6pOQepFsEGhUPXaulX9rvye2NH+NrdlAHg+WgpAQAAAAD/////AkBLTAAAAAAAF6kUqWwXCcLM5BN2zoNqMNT5qMlIi7+HQEtMAAAAAAAXqRSVF/in2XNxAlN1OSxkyp0z+Wtg2YcAAAAAAAEBIBNssgAAAAAAF6kUamsvautR8hRlMRY6OKNTx03DK96HAQcXFgAUo8u1LWpHprjt/uENAwBpGZD0UH0BCGsCRzBEAiAONfH3DYiw67ZbylrsxCF/XXpVwyWBRgofyRbPslzvwgIgIKCsWw5sHSIPh1icNvcVLZLHWj6NA7Dk+4Os2pOnMbQBIQPGStfYHPtyhpV7zIWtn0Q4GXv5gK1zy/tnJ+cBXu4iiwABABYAFMwmJQEz+HDpBEEabxJ5PogPsqZRAAEAFgAUyCrGc3h3FYCmiIspbv2pSTKZ5jU"
21+
"cHNidP8BAHMCAAAAAbiWoY6pOQepFsEGhUPXaulX9rvye2NH+NrdlAHg+WgpAQAAAAD/////AkBLTAAAAAAAF6kUqWwXCcLM5BN2zoNqMNT5qMlIi7+HQEtMAAAAAAAXqRSVF/in2XNxAlN1OSxkyp0z+Wtg2YcAAAAAAAEBIBNssgAAAAAAF6kUamsvautR8hRlMRY6OKNTx03DK96HAQcXFgAUo8u1LWpHprjt/uENAwBpGZD0UH0BCGsCRzBEAiAONfH3DYiw67ZbylrsxCF/XXpVwyWBRgofyRbPslzvwgIgIKCsWw5sHSIPh1icNvcVLZLHWj6NA7Dk+4Os2pOnMbQBIQPGStfYHPtyhpV7zIWtn0Q4GXv5gK1zy/tnJ+cBXu4iiwABABYAFMwmJQEz+HDpBEEabxJ5PogPsqZRAAEAFgAUyCrGc3h3FYCmiIspbv2pSTKZ5jU",
22+
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQEAAQEBagA=",
23+
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQAAAQABagA=",
24+
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJ//////////8AAQEJAADK/gAAAAAAAA==",
25+
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQMErd7f7gEDBAEAAAAA",
26+
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA=",
27+
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA=",
28+
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA=",
29+
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA="
2230
],
2331
"valid" : [
2432
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",

0 commit comments

Comments
 (0)