mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01:00
Fix bug where duplicate PSBT keys are accepted
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 ```
This commit is contained in:
parent
febc31c641
commit
9743432034
29
src/psbt.h
29
src/psbt.h
@ -126,6 +126,9 @@ struct PSBTInput
|
|||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
inline void Unserialize(Stream& s) {
|
inline void Unserialize(Stream& s) {
|
||||||
|
// Used for duplicate key detection
|
||||||
|
std::set<std::vector<unsigned char>> key_lookup;
|
||||||
|
|
||||||
// Read loop
|
// Read loop
|
||||||
bool found_sep = false;
|
bool found_sep = false;
|
||||||
while(!s.empty()) {
|
while(!s.empty()) {
|
||||||
@ -147,7 +150,7 @@ struct PSBTInput
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case PSBT_IN_NON_WITNESS_UTXO:
|
case PSBT_IN_NON_WITNESS_UTXO:
|
||||||
{
|
{
|
||||||
if (non_witness_utxo) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
|
throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
|
throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
|
||||||
@ -158,7 +161,7 @@ struct PSBTInput
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PSBT_IN_WITNESS_UTXO:
|
case PSBT_IN_WITNESS_UTXO:
|
||||||
if (!witness_utxo.IsNull()) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
|
throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Witness utxo key is more than one byte type");
|
throw std::ios_base::failure("Witness utxo key is more than one byte type");
|
||||||
@ -189,7 +192,7 @@ struct PSBTInput
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PSBT_IN_SIGHASH:
|
case PSBT_IN_SIGHASH:
|
||||||
if (sighash_type > 0) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
|
throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Sighash type key is more than one byte type");
|
throw std::ios_base::failure("Sighash type key is more than one byte type");
|
||||||
@ -198,7 +201,7 @@ struct PSBTInput
|
|||||||
break;
|
break;
|
||||||
case PSBT_IN_REDEEMSCRIPT:
|
case PSBT_IN_REDEEMSCRIPT:
|
||||||
{
|
{
|
||||||
if (!redeem_script.empty()) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
|
throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Input redeemScript key is more than one byte type");
|
throw std::ios_base::failure("Input redeemScript key is more than one byte type");
|
||||||
@ -208,7 +211,7 @@ struct PSBTInput
|
|||||||
}
|
}
|
||||||
case PSBT_IN_WITNESSSCRIPT:
|
case PSBT_IN_WITNESSSCRIPT:
|
||||||
{
|
{
|
||||||
if (!witness_script.empty()) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
|
throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Input witnessScript key is more than one byte type");
|
throw std::ios_base::failure("Input witnessScript key is more than one byte type");
|
||||||
@ -223,7 +226,7 @@ struct PSBTInput
|
|||||||
}
|
}
|
||||||
case PSBT_IN_SCRIPTSIG:
|
case PSBT_IN_SCRIPTSIG:
|
||||||
{
|
{
|
||||||
if (!final_script_sig.empty()) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
|
throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Final scriptSig key is more than one byte type");
|
throw std::ios_base::failure("Final scriptSig key is more than one byte type");
|
||||||
@ -233,7 +236,7 @@ struct PSBTInput
|
|||||||
}
|
}
|
||||||
case PSBT_IN_SCRIPTWITNESS:
|
case PSBT_IN_SCRIPTWITNESS:
|
||||||
{
|
{
|
||||||
if (!final_script_witness.IsNull()) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
|
throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
|
throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
|
||||||
@ -309,6 +312,9 @@ struct PSBTOutput
|
|||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
inline void Unserialize(Stream& s) {
|
inline void Unserialize(Stream& s) {
|
||||||
|
// Used for duplicate key detection
|
||||||
|
std::set<std::vector<unsigned char>> key_lookup;
|
||||||
|
|
||||||
// Read loop
|
// Read loop
|
||||||
bool found_sep = false;
|
bool found_sep = false;
|
||||||
while(!s.empty()) {
|
while(!s.empty()) {
|
||||||
@ -330,7 +336,7 @@ struct PSBTOutput
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case PSBT_OUT_REDEEMSCRIPT:
|
case PSBT_OUT_REDEEMSCRIPT:
|
||||||
{
|
{
|
||||||
if (!redeem_script.empty()) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
|
throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Output redeemScript key is more than one byte type");
|
throw std::ios_base::failure("Output redeemScript key is more than one byte type");
|
||||||
@ -340,7 +346,7 @@ struct PSBTOutput
|
|||||||
}
|
}
|
||||||
case PSBT_OUT_WITNESSSCRIPT:
|
case PSBT_OUT_WITNESSSCRIPT:
|
||||||
{
|
{
|
||||||
if (!witness_script.empty()) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
|
throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Output witnessScript key is more than one byte type");
|
throw std::ios_base::failure("Output witnessScript key is more than one byte type");
|
||||||
@ -448,6 +454,9 @@ struct PartiallySignedTransaction
|
|||||||
throw std::ios_base::failure("Invalid PSBT magic bytes");
|
throw std::ios_base::failure("Invalid PSBT magic bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for duplicate key detection
|
||||||
|
std::set<std::vector<unsigned char>> key_lookup;
|
||||||
|
|
||||||
// Read global data
|
// Read global data
|
||||||
bool found_sep = false;
|
bool found_sep = false;
|
||||||
while(!s.empty()) {
|
while(!s.empty()) {
|
||||||
@ -469,7 +478,7 @@ struct PartiallySignedTransaction
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case PSBT_GLOBAL_UNSIGNED_TX:
|
case PSBT_GLOBAL_UNSIGNED_TX:
|
||||||
{
|
{
|
||||||
if (tx) {
|
if (!key_lookup.emplace(key).second) {
|
||||||
throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
|
throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
|
||||||
} else if (key.size() != 1) {
|
} else if (key.size() != 1) {
|
||||||
throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
|
throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
|
||||||
|
@ -18,7 +18,15 @@
|
|||||||
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
|
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
|
||||||
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
|
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
|
||||||
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
|
"cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
|
||||||
"cHNidP8BAHMCAAAAAbiWoY6pOQepFsEGhUPXaulX9rvye2NH+NrdlAHg+WgpAQAAAAD/////AkBLTAAAAAAAF6kUqWwXCcLM5BN2zoNqMNT5qMlIi7+HQEtMAAAAAAAXqRSVF/in2XNxAlN1OSxkyp0z+Wtg2YcAAAAAAAEBIBNssgAAAAAAF6kUamsvautR8hRlMRY6OKNTx03DK96HAQcXFgAUo8u1LWpHprjt/uENAwBpGZD0UH0BCGsCRzBEAiAONfH3DYiw67ZbylrsxCF/XXpVwyWBRgofyRbPslzvwgIgIKCsWw5sHSIPh1icNvcVLZLHWj6NA7Dk+4Os2pOnMbQBIQPGStfYHPtyhpV7zIWtn0Q4GXv5gK1zy/tnJ+cBXu4iiwABABYAFMwmJQEz+HDpBEEabxJ5PogPsqZRAAEAFgAUyCrGc3h3FYCmiIspbv2pSTKZ5jU"
|
"cHNidP8BAHMCAAAAAbiWoY6pOQepFsEGhUPXaulX9rvye2NH+NrdlAHg+WgpAQAAAAD/////AkBLTAAAAAAAF6kUqWwXCcLM5BN2zoNqMNT5qMlIi7+HQEtMAAAAAAAXqRSVF/in2XNxAlN1OSxkyp0z+Wtg2YcAAAAAAAEBIBNssgAAAAAAF6kUamsvautR8hRlMRY6OKNTx03DK96HAQcXFgAUo8u1LWpHprjt/uENAwBpGZD0UH0BCGsCRzBEAiAONfH3DYiw67ZbylrsxCF/XXpVwyWBRgofyRbPslzvwgIgIKCsWw5sHSIPh1icNvcVLZLHWj6NA7Dk+4Os2pOnMbQBIQPGStfYHPtyhpV7zIWtn0Q4GXv5gK1zy/tnJ+cBXu4iiwABABYAFMwmJQEz+HDpBEEabxJ5PogPsqZRAAEAFgAUyCrGc3h3FYCmiIspbv2pSTKZ5jU",
|
||||||
|
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQEAAQEBagA=",
|
||||||
|
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQAAAQABagA=",
|
||||||
|
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJ//////////8AAQEJAADK/gAAAAAAAA==",
|
||||||
|
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQMErd7f7gEDBAEAAAAA",
|
||||||
|
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA=",
|
||||||
|
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA=",
|
||||||
|
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA=",
|
||||||
|
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA="
|
||||||
],
|
],
|
||||||
"valid" : [
|
"valid" : [
|
||||||
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
|
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
|
||||||
|
Loading…
Reference in New Issue
Block a user