mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-26 14:59:16 +02:00
Merge #16821: Fix bug where duplicate PSBT keys are accepted
9743432034Fix 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: ACK9743432034instagibbs: code review ACK9743432034Tree-SHA512: 34f4b34c8e6561c6a6ab745cdd319f6687eac6f7cecc735c94035eeca8c5157e17a27f2ae853dbaa6634fcd5a8f4e1c6cc13d1ebd7e563459665d72bb147cc1e
This commit is contained in:
29
src/psbt.h
29
src/psbt.h
@@ -126,6 +126,9 @@ struct PSBTInput
|
||||
|
||||
template <typename Stream>
|
||||
inline void Unserialize(Stream& s) {
|
||||
// Used for duplicate key detection
|
||||
std::set<std::vector<unsigned char>> key_lookup;
|
||||
|
||||
// Read loop
|
||||
bool found_sep = false;
|
||||
while(!s.empty()) {
|
||||
@@ -147,7 +150,7 @@ struct PSBTInput
|
||||
switch(type) {
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
|
||||
@@ -158,7 +161,7 @@ struct PSBTInput
|
||||
break;
|
||||
}
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Witness utxo key is more than one byte type");
|
||||
@@ -189,7 +192,7 @@ struct PSBTInput
|
||||
break;
|
||||
}
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Sighash type key is more than one byte type");
|
||||
@@ -198,7 +201,7 @@ struct PSBTInput
|
||||
break;
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Input redeemScript key is more than one byte type");
|
||||
@@ -208,7 +211,7 @@ struct PSBTInput
|
||||
}
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Input witnessScript key is more than one byte type");
|
||||
@@ -223,7 +226,7 @@ struct PSBTInput
|
||||
}
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Final scriptSig key is more than one byte type");
|
||||
@@ -233,7 +236,7 @@ struct PSBTInput
|
||||
}
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
|
||||
@@ -309,6 +312,9 @@ struct PSBTOutput
|
||||
|
||||
template <typename Stream>
|
||||
inline void Unserialize(Stream& s) {
|
||||
// Used for duplicate key detection
|
||||
std::set<std::vector<unsigned char>> key_lookup;
|
||||
|
||||
// Read loop
|
||||
bool found_sep = false;
|
||||
while(!s.empty()) {
|
||||
@@ -330,7 +336,7 @@ struct PSBTOutput
|
||||
switch(type) {
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Output redeemScript key is more than one byte type");
|
||||
@@ -340,7 +346,7 @@ struct PSBTOutput
|
||||
}
|
||||
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");
|
||||
} else if (key.size() != 1) {
|
||||
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");
|
||||
}
|
||||
|
||||
// Used for duplicate key detection
|
||||
std::set<std::vector<unsigned char>> key_lookup;
|
||||
|
||||
// Read global data
|
||||
bool found_sep = false;
|
||||
while(!s.empty()) {
|
||||
@@ -469,7 +478,7 @@ struct PartiallySignedTransaction
|
||||
switch(type) {
|
||||
case PSBT_GLOBAL_UNSIGNED_TX:
|
||||
{
|
||||
if (tx) {
|
||||
if (!key_lookup.emplace(key).second) {
|
||||
throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
|
||||
} else if (key.size() != 1) {
|
||||
throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
|
||||
|
||||
Reference in New Issue
Block a user