diff --git a/doc/pset.mediawiki b/doc/pset.mediawiki index fc6e011cb1..bc04590e06 100644 --- a/doc/pset.mediawiki +++ b/doc/pset.mediawiki @@ -94,7 +94,7 @@ The currently defined elements per-input proprietary types are as folows: | None | No key data | <33 byte commitment> -| The 33 byte Value Commitment. If provided, PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF must be provided too. +| The 33 byte Value Commitment. If provided, either PSBT_ELEMENTS_IN_ISSUANCE_VALUE must be removed, or PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF must be provided too. | | 0 | 2 @@ -194,7 +194,7 @@ The currently defined elements per-input proprietary types are as folows: | None | No key data | <33 byte commitment> -| The 33 byte commitment to the inflation keys output value in this issuance. If provided, PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF must be provided too. +| The 33 byte commitment to the inflation keys output value in this issuance. If provided, either PSBT_ELEMENTS_IN_ISSUANCE_INFLATION_KEYS must be removed or PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF must be provided too. | | 0 | 2 @@ -268,7 +268,7 @@ The currently defined elements per-output proprietary types are as follows: | None | No key data | <33 byte commitment> -| The 33 byte Value Commitment for this output. If provided, PSBT_ELEMENTS_OUT_BLIND_VALUE_PROOF must be provided too. +| The 33 byte Value Commitment for this output. If provided, either PSBT_OUT_VALUE must be removed or PSBT_ELEMENTS_OUT_BLIND_VALUE_PROOF must be provided too. | | 0 | 2 @@ -288,7 +288,7 @@ The currently defined elements per-output proprietary types are as follows: | None | No key data | <33 byte commitment> -| The 33 byte Asset Commitment for this output. If provided, PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF must be provided too. +| The 33 byte Asset Commitment for this output. If provided, either PSBT_ELEMENTS_OUT_ASSET must be removed or PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF must be provided too. | | 0 | 2 diff --git a/src/blindpsbt.cpp b/src/blindpsbt.cpp index 914f12e0a0..84abf24a22 100644 --- a/src/blindpsbt.cpp +++ b/src/blindpsbt.cpp @@ -66,19 +66,23 @@ bool CreateAssetSurjectionProof(std::vector& output_proof, const return true; } -bool VerifyBlindAssetProof(const std::vector& proof, const CConfidentialAsset& conf_asset) +static bool VerifyBlindAssetProof(const uint256& asset, const std::vector& proof, const CConfidentialAsset& conf_asset) { secp256k1_surjectionproof surj_proof; if (secp256k1_surjectionproof_parse(secp256k1_blind_context, &surj_proof, proof.data(), proof.size()) == 0) { return false; } - secp256k1_generator gen; - if (secp256k1_generator_parse(secp256k1_blind_context, &gen, conf_asset.vchCommitment.data()) == 0) { + secp256k1_generator blinded_asset_gen; + if (secp256k1_generator_parse(secp256k1_blind_context, &blinded_asset_gen, conf_asset.vchCommitment.data()) == 0) { + return false; + } + secp256k1_generator asset_gen; + if (secp256k1_generator_generate(secp256k1_blind_context, &asset_gen, asset.data()) == 0) { return false; } - return secp256k1_surjectionproof_verify(secp256k1_blind_context, &surj_proof, &gen, 1, &gen) == 0; + return secp256k1_surjectionproof_verify(secp256k1_blind_context, &surj_proof, &asset_gen, 1, &blinded_asset_gen); } uint256 GenerateRangeproofECDHKey(CPubKey& ephemeral_pubkey, const CPubKey blinding_pubkey) @@ -116,7 +120,7 @@ bool CreateValueRangeProof(std::vector& rangeproof, const uint256 } // Create an explicit value rangeproof which proves that the commitment commits to an explicit value -bool CreateBlindValueProof(std::vector& rangeproof, const uint256& value_blinder, const CAmount amount, const secp256k1_pedersen_commitment& value_commit, const secp256k1_generator& gen) +static bool CreateBlindValueProof(std::vector& rangeproof, const uint256& value_blinder, const CAmount amount, const secp256k1_pedersen_commitment& value_commit, const secp256k1_generator& gen) { // Prep rangeproof size_t rangeproof_len = 5134; @@ -132,7 +136,44 @@ bool CreateBlindValueProof(std::vector& rangeproof, const uint256 return res == 1; } -bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector& proof, const CConfidentialAsset& conf_asset) +// Create an explicit value rangeproof which proves that the commitment commits to an explicit value +static bool CreateBlindAssetProof(std::vector& assetproof, const CAsset& asset, const CConfidentialAsset& asset_commit, const uint256& asset_blinder) +{ + const unsigned char zero32[32] = {0}; + secp256k1_surjectionproof proof; + size_t input_index; + secp256k1_generator asset_gen; + secp256k1_generator blinded_asset_gen; + secp256k1_fixed_asset_tag fixed_tag; + memcpy(&fixed_tag, asset.begin(), 32); + + if (!secp256k1_generator_generate(secp256k1_blind_context, &asset_gen, asset.begin())) { + return false; + } + if (secp256k1_generator_parse(secp256k1_blind_context, &blinded_asset_gen, asset_commit.vchCommitment.data()) == 0) { + return false; + } + + if (!secp256k1_surjectionproof_initialize(secp256k1_blind_context, &proof, &input_index, &fixed_tag, 1, 1, &fixed_tag, 1, zero32)) { + return false; + } + assert(input_index == 0); + + if (!secp256k1_surjectionproof_generate(secp256k1_blind_context, &proof, &asset_gen, 1, &blinded_asset_gen, 0, zero32, asset_blinder.data())) { + return false; + } + if (!secp256k1_surjectionproof_verify(secp256k1_blind_context, &proof, &asset_gen, 1, &blinded_asset_gen)) { + return false; + } + + size_t output_len = secp256k1_surjectionproof_serialized_size(secp256k1_blind_context, &proof); + assetproof.resize(output_len); + secp256k1_surjectionproof_serialize(secp256k1_blind_context, &assetproof[0], &output_len, &proof); + assert(output_len == assetproof.size()); + return true; +} + +static bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector& proof, const CConfidentialAsset& conf_asset) { secp256k1_pedersen_commitment value_commit; if (secp256k1_pedersen_commitment_parse(secp256k1_blind_context, &value_commit, conf_value.vchCommitment.data()) == 0) { @@ -152,6 +193,33 @@ bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, return min_value == (uint64_t)value; } +BlindProofResult VerifyBlindProofs(const PSBTOutput& o) { + // No blinding, no problem + if (!o.IsBlinded()) { + return BlindProofResult::OK; + } else if (!o.IsFullyBlinded()) { + return BlindProofResult::NOT_FULLY_BLINDED; + } + + if (o.amount != std::nullopt) { + if (o.m_blind_value_proof.empty()) { + return BlindProofResult::MISSING_VALUE_PROOF; + } else if (!VerifyBlindValueProof(*o.amount, o.m_value_commitment, o.m_blind_value_proof, o.m_asset_commitment)) { + return BlindProofResult::INVALID_VALUE_PROOF; + } + } + + if (!o.m_asset.IsNull()) { + if (o.m_blind_asset_proof.empty()) { + return BlindProofResult::MISSING_ASSET_PROOF; + } else if (!VerifyBlindAssetProof(o.m_asset, o.m_blind_asset_proof, o.m_asset_commitment)) { + return BlindProofResult::INVALID_ASSET_PROOF; + } + } + + return BlindProofResult::OK; +} + void CreateAssetCommitment(CConfidentialAsset& conf_asset, secp256k1_generator& asset_gen, const CAsset& asset, const uint256& asset_blinder) { conf_asset.vchCommitment.resize(CConfidentialAsset::nCommittedSize); @@ -471,7 +539,7 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map blind_asset_proof; - if (!CreateAssetSurjectionProof(blind_asset_proof, fixed_input_tags, ephemeral_input_tags, input_asset_blinders, asset_blinder, asset_generator, asset, /* num_targets */ 1)) { + if (!CreateBlindAssetProof(blind_asset_proof, asset, asset_commitment, asset_blinder)) { return BlindingStatus::ASP_UNABLE; } diff --git a/src/blindpsbt.h b/src/blindpsbt.h index 2917334d91..51cc09dff5 100644 --- a/src/blindpsbt.h +++ b/src/blindpsbt.h @@ -16,6 +16,7 @@ #include struct PartiallySignedTransaction; +struct PSBTOutput; enum class BlindingStatus { @@ -29,16 +30,23 @@ enum class BlindingStatus NO_BLIND_OUTPUTS, }; +enum class BlindProofResult { + OK, + NOT_FULLY_BLINDED, + MISSING_VALUE_PROOF, + MISSING_ASSET_PROOF, + INVALID_VALUE_PROOF, + INVALID_ASSET_PROOF, +}; + std::string GetBlindingStatusError(const BlindingStatus& status); bool CreateAssetSurjectionProof(std::vector& output_proof, const std::vector& fixed_input_tags, const std::vector& ephemeral_input_tags, const std::vector& input_asset_blinders, const uint256& output_asset_blinder, const secp256k1_generator& output_asset_tag, const CAsset& asset, size_t num_targets = MAX_SURJECTION_TARGETS); -bool VerifyBlindAssetProof(const std::vector& proof, const CConfidentialAsset& conf_asset); uint256 GenerateRangeproofECDHKey(CPubKey& ephemeral_pubkey, const CPubKey blinding_pubkey); bool CreateValueRangeProof(std::vector& rangeproof, const uint256& value_blinder, const uint256& nonce, const CAmount amount, const CScript& scriptPubKey, const secp256k1_pedersen_commitment& value_commit, const secp256k1_generator& gen, const CAsset& asset, const uint256& asset_blinder); -bool CreateBlindValueProof(std::vector& rangeproof, const uint256& value_blinder, const CAmount amount, const secp256k1_pedersen_commitment& value_commit, const secp256k1_generator& gen); -bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector& proof, const CConfidentialAsset& conf_asset); void CreateAssetCommitment(CConfidentialAsset& conf_asset, secp256k1_generator& asset_gen, const CAsset& asset, const uint256& asset_blinder); void CreateValueCommitment(CConfidentialValue& conf_value, secp256k1_pedersen_commitment& value_commit, const uint256& value_blinder, const secp256k1_generator& asset_gen, const CAmount amount); BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map> our_input_data, std::map> our_issuances_to_blind); +BlindProofResult VerifyBlindProofs(const PSBTOutput& o); #endif //BITCOIN_BLINDPSBT_H diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp index 84dad1b823..b265ea91ab 100644 --- a/src/node/psbt.cpp +++ b/src/node/psbt.cpp @@ -89,9 +89,15 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) } } - for (const PSBTOutput& output : psbtx.outputs) { + result.outputs.resize(psbtx.outputs.size()); + for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) { + const PSBTOutput& output = psbtx.outputs[i]; + PSBTOutputAnalysis& output_analysis = result.outputs[i]; CTxOut txout = output.GetTxOut(); - if (output.IsBlinded()) { + + output_analysis.is_blind = output.IsBlinded(); + output_analysis.proof_result = VerifyBlindProofs(output); + if (output_analysis.is_blind) { has_blinded_outputs = true; if (!output.IsFullyBlinded()) { result.next = PSBTRole::BLINDER; diff --git a/src/node/psbt.h b/src/node/psbt.h index ae0c99af24..ebd1697df0 100644 --- a/src/node/psbt.h +++ b/src/node/psbt.h @@ -6,6 +6,7 @@ #define BITCOIN_NODE_PSBT_H #include +#include #include @@ -23,6 +24,14 @@ struct PSBTInputAnalysis { uint256 missing_witness_script; //!< SHA256 of witness script, if missing }; +/** + * Holds an analysis of one output from a PSBT + */ +struct PSBTOutputAnalysis { + bool is_blind; //!< Whether the output should be blinded (has a set ECDH pubkey) + BlindProofResult proof_result; //!< Result of checking the explicit-confidential-matching proof +}; + /** * Holds the results of AnalyzePSBT (miscellaneous information about a PSBT) */ @@ -31,6 +40,7 @@ struct PSBTAnalysis { std::optional estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction std::optional fee; //!< Amount of fee being paid by the transaction std::vector inputs; //!< More information about the individual inputs of the transaction + std::vector outputs; //!< More information about the individual outputs of the transaction PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next std::string error; //!< Error message diff --git a/src/psbt.cpp b/src/psbt.cpp index db181d934d..58f832ebb3 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -135,15 +135,20 @@ CMutableTransaction PartiallySignedTransaction::GetUnsignedTx(bool force_unblind CTxOut txout; CTxOutWitness txoutwit; txout.scriptPubKey = *output.script; - if (output.IsFullyBlinded() && !force_unblinded) { - txout.nValue = output.m_value_commitment; - txout.nAsset = output.m_asset_commitment; - txout.nNonce.vchCommitment.insert(txout.nNonce.vchCommitment.end(), output.m_ecdh_pubkey.begin(), output.m_ecdh_pubkey.end()); - txoutwit.vchRangeproof = output.m_value_rangeproof; - txoutwit.vchSurjectionproof = output.m_asset_surjection_proof; - } else { + if (output.m_value_commitment.IsNull() || (output.amount != std::nullopt && force_unblinded)) { txout.nValue.SetToAmount(*output.amount); + } else { + txout.nValue = output.m_value_commitment; + txoutwit.vchRangeproof = output.m_value_rangeproof; + } + if (output.m_asset_commitment.IsNull() || (!output.m_asset.IsNull() && force_unblinded)) { txout.nAsset.SetToAsset(CAsset(output.m_asset)); + } else { + txout.nAsset = output.m_asset_commitment; + txoutwit.vchSurjectionproof = output.m_asset_surjection_proof; + } + if (output.m_ecdh_pubkey.IsValid() && !force_unblinded) { + txout.nNonce.vchCommitment.insert(txout.nNonce.vchCommitment.end(), output.m_ecdh_pubkey.begin(), output.m_ecdh_pubkey.end()); } mtx.vout.push_back(txout); mtx.witness.vtxoutwit.push_back(txoutwit); diff --git a/src/psbt.h b/src/psbt.h index 94dc39f4fc..19d0823da4 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -927,18 +927,6 @@ struct PSBTInput if (prev_out == std::nullopt) { throw std::ios_base::failure("Previous output's index is required in PSBTv2"); } - if (!m_issuance_value_commitment.IsNull() && m_issuance_value == std::nullopt) { - throw std::ios_base::failure("Explicit issuance value must be provided if its commitment is provided too"); - } - if (!m_issuance_value_commitment.IsNull() && m_blind_issuance_value_proof.empty()) { - throw std::ios_base::failure("Blind issuance value proof must be provided if its commitment is provided too"); - } - if (!m_issuance_inflation_keys_commitment.IsNull() && m_issuance_inflation_keys_amount == std::nullopt) { - throw std::ios_base::failure("Explicit issuance inflation keys amount must be provided if its commitment is provided too"); - } - if (!m_issuance_inflation_keys_commitment.IsNull() && m_blind_issuance_inflation_keys_proof.empty()) { - throw std::ios_base::failure("Blind issuance inflation keys value proof must be provided if its commitment is provided too"); - } if (!m_issuance_value_commitment.IsNull() && m_issuance_rangeproof.empty()) { throw std::ios_base::failure("Issuance value commitment provided without value rangeproof"); } @@ -1320,21 +1308,15 @@ struct PSBTOutput // Make sure required PSBTv2 fields are present if (m_psbt_version >= 2) { - if (amount == std::nullopt) { + if (amount == std::nullopt && m_value_commitment.IsNull()) { throw std::ios_base::failure("Output amount is required in PSBTv2"); } if (script == std::nullopt) { throw std::ios_base::failure("Output script is required in PSBTv2"); } - if (m_asset.IsNull()) { + if (m_asset.IsNull() && m_asset_commitment.IsNull()) { throw std::ios_base::failure("Output asset is required in PSET"); } - if (!m_value_commitment.IsNull() && m_blind_value_proof.empty()) { - throw std::ios_base::failure("Blind value proof must be provided if value commitment is provided"); - } - if (!m_asset_commitment.IsNull() && m_blind_asset_proof.empty()) { - throw std::ios_base::failure("Blind asset proof must be provided if asset commitment is provided"); - } if (m_blinding_pubkey.IsValid() && m_blinder_index == std::nullopt) { throw std::ios_base::failure("Output is blinded but does not have a blinder index"); } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index d8f52b632e..bf5e25b2d3 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1268,6 +1268,7 @@ static RPCHelpMan decodepsbt() {RPCResult::Type::STR_HEX, "blinding_pubkey", "The blinding pubkey for the output"}, {RPCResult::Type::STR_HEX, "blind_value_proof", "Explicit value rangeproof that proves the value commitment matches the value"}, {RPCResult::Type::STR_HEX, "blind_asset_proof", "Assert surjection proof that proves the assert commitment matches the asset"}, + {RPCResult::Type::STR, "status", "information about how the output has been blinded, if available"}, {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, @@ -1714,6 +1715,27 @@ static RPCHelpMan decodepsbt() out.pushKV("blind_asset_proof", HexStr(output.m_blind_asset_proof)); } + switch (VerifyBlindProofs(output)) { + case BlindProofResult::OK: + // all good + break; + case BlindProofResult::NOT_FULLY_BLINDED: + out.pushKV("status", "needs blinding"); + break; + case BlindProofResult::MISSING_VALUE_PROOF: + out.pushKV("status", "WARNING: has confidential and explicit values but no proof connecting them"); + break; + case BlindProofResult::MISSING_ASSET_PROOF: + out.pushKV("status", "WARNING: has confidential and explicit assets but no proof connecting them"); + break; + case BlindProofResult::INVALID_VALUE_PROOF: + out.pushKV("status", "ERROR: has invalid value proof, the value may be a lie!"); + break; + case BlindProofResult::INVALID_ASSET_PROOF: + out.pushKV("status", "ERROR: has invalid asset proof, the asset may be a lie!"); + break; + } + // Proprietary if (!output.m_proprietary.empty()) { UniValue proprietary(UniValue::VARR); @@ -2362,6 +2384,14 @@ static RPCHelpMan analyzepsbt() {RPCResult::Type::STR, "next", /* optional */ true, "Role of the next person that this input needs to go to"}, }}, }}, + {RPCResult::Type::ARR, "outputs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "blind", "whether the output should be blinded"}, + {RPCResult::Type::STR, "status", "to what extent the output has been blinded"}, + }}, + }}, {RPCResult::Type::NUM, "estimated_vsize", /* optional */ true, "Estimated vsize of the final signed transaction"}, {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /* optional */ true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kvB. Shown only if all UTXO slots in the PSBT have been filled"}, {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled"}, @@ -2422,6 +2452,36 @@ static RPCHelpMan analyzepsbt() } if (!inputs_result.empty()) result.pushKV("inputs", inputs_result); + UniValue outputs_result(UniValue::VARR); + for (const auto& output : psbta.outputs) { + UniValue output_univ(UniValue::VOBJ); + + output_univ.pushKV("blind", output.is_blind); + switch (output.proof_result) { + case BlindProofResult::OK: + output_univ.pushKV("status", "done"); + break; + case BlindProofResult::NOT_FULLY_BLINDED: + output_univ.pushKV("status", "unblinded"); + break; + case BlindProofResult::MISSING_VALUE_PROOF: + output_univ.pushKV("status", "WARNING: has confidential and explicit values but no proof connecting them"); + break; + case BlindProofResult::MISSING_ASSET_PROOF: + output_univ.pushKV("status", "WARNING: has confidential and explicit assets but no proof connecting them"); + break; + case BlindProofResult::INVALID_VALUE_PROOF: + output_univ.pushKV("status", "ERROR: has invalid value proof, the value may be a lie!"); + break; + case BlindProofResult::INVALID_ASSET_PROOF: + output_univ.pushKV("status", "ERROR: has invalid asset proof, the asset may be a lie!"); + break; + } + + outputs_result.push_back(output_univ); + } + if (!outputs_result.empty()) result.pushKV("outputs", outputs_result); + if (psbta.estimated_vsize != std::nullopt) { result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d38e489171..5cf548ab3d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1933,21 +1933,51 @@ BlindingStatus CWallet::WalletBlindPSBT(PartiallySignedTransaction& psbtx) const } TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool imbalance_ok, bool bip32derivs, size_t* n_signed) const { + LOCK(cs_wallet); + // If we're signing, check that the transaction is not still in need of blinding // Also check that the amount and asset proofs are valid if (sign) { for (const PSBTOutput& o : psbtx.outputs) { if (o.IsBlinded()) { - if (!o.IsFullyBlinded()) { - return TransactionError::BLINDING_REQUIRED; + switch (VerifyBlindProofs(o)) { + case BlindProofResult::OK: + break; + case BlindProofResult::NOT_FULLY_BLINDED: + return TransactionError::BLINDING_REQUIRED; + case BlindProofResult::INVALID_VALUE_PROOF: + case BlindProofResult::MISSING_VALUE_PROOF: + return TransactionError::INVALID_VALUE_PROOF; + case BlindProofResult::INVALID_ASSET_PROOF: + case BlindProofResult::MISSING_ASSET_PROOF: + return TransactionError::INVALID_ASSET_PROOF; } - assert(!o.m_blind_value_proof.empty()); - assert(!o.m_blind_asset_proof.empty()); - if (!VerifyBlindValueProof(*o.amount, o.m_value_commitment, o.m_blind_value_proof, o.m_asset_commitment)) { - return TransactionError::INVALID_VALUE_PROOF; - } - if (!VerifyBlindAssetProof(o.m_blind_asset_proof, o.m_asset_commitment)) { - return TransactionError::INVALID_ASSET_PROOF; + + if (o.script && IsMine(*o.script)) { + CKey blinding_key; + if ((blinding_key = GetBlindingKey(&*o.script)).IsValid()) { + CAmount value; + uint256 value_factor; + CAsset asset; + uint256 asset_factor; + + CConfidentialNonce nonce; + nonce.vchCommitment.insert(nonce.vchCommitment.end(), o.m_ecdh_pubkey.begin(), o.m_ecdh_pubkey.end()); + if (!UnblindConfidentialPair(blinding_key, o.m_value_commitment, o.m_asset_commitment, nonce, *o.script, o.m_value_rangeproof, value, value_factor, asset, asset_factor)) { + // These assertions are cryptographically impossible to trigger, as we + // checked the proofs above, and then `UnblindConfidentialPair` checks + // the extracted value/asset against the commitments. + if (o.amount) { + assert(*o.amount == value); + } + if (!o.m_asset.IsNull()) { + assert(CAsset(o.m_asset) == asset); + } + return TransactionError::INVALID_ASSET_PROOF; // FIXME + } + } else { + return TransactionError::INVALID_ASSET_PROOF; // FIXME + } } } } @@ -1957,7 +1987,6 @@ TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& comp *n_signed = 0; } - LOCK(cs_wallet); CMutableTransaction tx = psbtx.GetUnsignedTx(); tx.witness.vtxoutwit.resize(tx.vout.size()); diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 42f7566804..37fafca627 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -735,6 +735,87 @@ class PSBTTest(BitcoinTestFramework): self.nodes[0].generate(1) self.sync_all() + def pset_confidential_proofs(self): + BLINDED = "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" + NO_VALUE_PROOF = "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" + BAD_VALUE_PROOF = "cHNldP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAP1UAQIAAAAAASopobdl5W15RSedscp/8bxEXKuKIMOZw+JTqgD8qJEKBAAAAAD9////Awrye7Xu4kI5VnpTDeGaq8sYdXP3qdzYaHrLDRzaC8y51ggl1U8hJxSo+8GcTzHv926wsqTTkOrdBnJo8qcLwLQauQKktt71EJU7HTH5HsgG4kJV/tC32F992/WgieIPRkUkmxYAFPrs/iioimRS5hoJKl/hua83d7rwC1uuuLvfuQh38wHS+0Vg2ecXzypsUabYofOFaGSrICByCKvjgTF6TdHNp2el7Cwi+94dy4qMDrEh/25Aqnc+5qABAqWPEY9ZNCz7m64pANrr04bVgPxaWCr7LvvWGH5FLzvRFgAU96wAzcLFRah7B8gq17sVY9Uso18BIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIBAAAAAAAAKfQAAAAAAAABAXoK8nu17uJCOVZ6Uw3hmqvLGHVz96nc2Gh6yw0c2gvMudYIJdVPIScUqPvBnE8x7/dusLKk05Dq3QZyaPKnC8C0GrkCpLbe9RCVOx0x+R7IBuJCVf7Qt9hffdv1oIniD0ZFJJsWABT67P4oqIpkUuYaCSpf4bmvN3e68CIGA3pgD7iheh1WkyCWvviXQBa9KOJk6JBeYxEpPuxiRBOvEElHIxkAAACAAQAAgAMAAIABDiB25bQww62kp1L1uQVb7MxEVoem8kCzSmM5DW09I9V6DQEPBAAAAAABEAT/////B/wEcHNldA79CwFgAgAHY7+9IRzxAXWemL7C9M7CBAqQoSrXRoxI5/YnMLV6nV/GBMEhmvoDFJcNzRXI/LrIRMLZFvNrP5IupN8OZ+4q+++aJTnuYCZIDR1pssb0JHA0z2UXkEYdHv26qoW26RbLf2LNh29yVIOHG3jqqc7+L7F4UELZmjlEs6R1sulqQ0ePCUUgAsqURkdnNKtl0nORiyLN/9JfqGGTC30WhsdXifWRmqOfkWil0Va1bDYumMU7zJdW/go83ODuZ5VZVWFsBLFSn9HxF1SaFCGt197qo8dr+vhPZwb72k13A72D+5Lx7UKoYqamRJsoAZdUZ/oVd9GRlPbAmRPV7iOxmPYf+t9AQiEd0Z4AIgICuujF5+Lk/uCeX9+RWtJ8ioG51rogGduwt+iY1tZFtjUQSUcjGQAAAIAAAACACwAAgAEEFgAUg+8ATSQ8VvNg+WJAuweXm6kXlFkH/ARwc2V0ASEIBoHxCnQKKMcpdKYCHdu36jzQ0zSc49oGuDQl7Nvus3gBAwgAv3xIGAkAAAf8BHBzZXQDIQsuVWSYT/UkUbq/hYsdWuoo3ARSy5K7e//36h8QhjdKRAf8BHBzZXQCICMPT11LfG+oRYBu5PZ3E0WeG2no5g/O4uSUDHoNXeGyB/wEcHNldAT9CwFgAgAACRhIfL75AJaUOCJ2q+YnbnYTFqluECvtDoJFGcrYvu5VsxPdASJNduFIJRBglnPdW73QRjqt+r3KlxBQ3XUWTce6is6cGED9eySEVJwBXz4Mt8SjqM2GsyUfqC+Ey3+APGgh54MYLt+HHKmt6ibcvE1DDU/UGpVo+I3cY/kgKJzrWMG6y/jDm/CHcF49L8EBtYC7iSrBhwzmDk7DmiViiQFCTUDfIqilX/piqS9ZlO4JNydA5kmLqXkj/xtR2hKt57wknqqvM7/car1S4Do8VljtG9lCzvSOBtBvijSwpFY1KaVFjpj0UZI9XJQ2eEbMrqC0qygNBi1f+ULyZFccNSGpXaZnrZAH/ARwc2V0BUMBAAECnwdoJ4rVnGgLT0He5GaLEhDnGqCKcH0nlTi1T53tBYMI8InonQGT61IAjoLcRxOqzMLgEC3KXg7yW8x6d6VmB/wEcHNldAYhAwxmNPa94Vg9u/nZBWC/8IYTgnp85V5TMOEFWTTAcF2pB/wEcHNldAchAitGVbG/bZNcV2ifjimuh04FOwRlxNrNPva66U6/RiHFB/wEcHNldAgEAAAAAAf8BHBzZXQJSSAAAAkYSHy/AIN6lvAUJ1o6ZQK5i/ewcpqSz4eW8zMzXFO/ZlNvAomxweIBD8YyywTguhBMI0BdLs2VeS5mc5e1oR0R27YAUccH/ARwc2V0CkMBAAGJm91DfvVBUOaEFZ0uH1RbT2cgI9MN9k1lE1hlWc2AtALpMJ17khkivt8F7dgCAVdBvcHFaw138ZsVfiD7g480AAEEAAEDCADh9QUAAAAAB/wEcHNldAIgIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIH/ARwc2V0CAQAAAAAAA==" + NO_ASSET_PROOF = "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" + BAD_ASSET_PROOF = "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" + ONLY_BLIND = "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" + + ## Check warnings for PSETs + for output in self.nodes[0].decodepsbt(BLINDED)["outputs"]: + assert "status" not in output + for output in self.nodes[0].analyzepsbt(BLINDED)["outputs"]: + assert_equal (output["status"], "done") + + stats = [output.get("status") for output in self.nodes[0].decodepsbt(NO_VALUE_PROOF)["outputs"]] + assert_equal(stats, [ + "WARNING: has confidential and explicit values but no proof connecting them", + None, + ]) + stats = [output for output in self.nodes[0].analyzepsbt(NO_VALUE_PROOF)["outputs"]] + assert_equal(stats, [ + {"blind": True, "status": "WARNING: has confidential and explicit values but no proof connecting them" }, + {"blind": False, "status": "done" }, + ]) + + stats = [output.get("status") for output in self.nodes[0].decodepsbt(BAD_VALUE_PROOF)["outputs"]] + assert_equal(stats, [ + "ERROR: has invalid value proof, the value may be a lie!", + None, + ]) + stats = [output for output in self.nodes[0].analyzepsbt(BAD_VALUE_PROOF)["outputs"]] + assert_equal(stats, [ + {"blind": True, "status": "ERROR: has invalid value proof, the value may be a lie!" }, + {"blind": False, "status": "done" }, + ]) + + stats = [output.get("status") for output in self.nodes[0].decodepsbt(NO_ASSET_PROOF)["outputs"]] + assert_equal(stats, [ + "WARNING: has confidential and explicit assets but no proof connecting them", + None, + ]) + stats = [output for output in self.nodes[0].analyzepsbt(NO_ASSET_PROOF)["outputs"]] + assert_equal(stats, [ + {"blind": True, "status": "WARNING: has confidential and explicit assets but no proof connecting them" }, + {"blind": False, "status": "done" }, + ]) + + stats = [output.get("status") for output in self.nodes[0].decodepsbt(BAD_ASSET_PROOF)["outputs"]] + assert_equal(stats, [ + "ERROR: has invalid asset proof, the asset may be a lie!", + None, + ]) + stats = [output for output in self.nodes[0].analyzepsbt(BAD_ASSET_PROOF)["outputs"]] + assert_equal(stats, [ + {"blind": True, "status": "ERROR: has invalid asset proof, the asset may be a lie!" }, + {"blind": False, "status": "done" }, + ]) + + stats = [output.get("status") for output in self.nodes[0].decodepsbt(ONLY_BLIND)["outputs"]] + assert_equal(stats, [None, None]) + stats = [output for output in self.nodes[0].analyzepsbt(ONLY_BLIND)["outputs"]] + assert_equal(stats, [ + {"blind": True, "status": "done" }, + {"blind": False, "status": "done" }, + ]) + + # Check that we can combine these in any combination, as they all have explicit data + assert_equal (self.nodes[0].combinepsbt([BLINDED, BLINDED]), BLINDED) + for pset1 in [ BLINDED, NO_VALUE_PROOF, BAD_VALUE_PROOF, NO_ASSET_PROOF, BAD_ASSET_PROOF ]: + for pset2 in [ BLINDED, NO_VALUE_PROOF, BAD_VALUE_PROOF, NO_ASSET_PROOF, BAD_ASSET_PROOF ]: + combo = self.nodes[0].combinepsbt([pset1, pset2]) + if combo != pset1: + assert_equal (combo, BLINDED) + + # On the other hand, none of these can be combined with the "only confidential, no + # explicit values" version + assert_equal (self.nodes[0].combinepsbt([ONLY_BLIND, ONLY_BLIND]), ONLY_BLIND) + for pset in [ BLINDED, NO_VALUE_PROOF, BAD_VALUE_PROOF, NO_ASSET_PROOF, BAD_ASSET_PROOF ]: + assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [pset, ONLY_BLIND]) + assert_raises_rpc_error(-8, "PSBTs not compatible (different transactions)", self.nodes[0].combinepsbt, [ONLY_BLIND, pset]) + def run_test(self): self.nodes[0].generate(200) self.sync_all() @@ -896,5 +977,8 @@ class PSBTTest(BitcoinTestFramework): assert_raises_rpc_error(-25, 'Inputs missing or spent', self.nodes[0].walletprocesspsbt, "cHNldP8BAgQCAAAAAQMEAAAAAAEEAQEBBQEDAfsEAgAAAAABAKICAAAAAAHH6k+xEgicvmA3NdivY741Mkb1NOcXWr0NNl6hrR/WbgAAAEAA/////wIBIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIBAAAAAlQLx/QAFgAUTwXL7rzz4++YOM52QVixAcDETlwBIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIBAAAAAAAAHAwAAAAAAAABDiBzQYOL5jKoCOgksiRTvw0zfNZ+6QwsBsCZRoqc3PHoygEPBAIAAAABEAT9////ACICAt/pWo4sGJOHmHcQ8znTQCNWAZbCdkdGx3JaRfNNtbr6EAm9XegAAACAAQAAgEgAAIABBBYAFCuDv44MRC5Qj+VetbjoeiSUS5p3AQMIzLoIvwEAAAAH/ARwc2V0AiAjD09dS3xvqEWAbuT2dxNFnhtp6OYPzuLklAx6DV3hsgf8BHBzZXQIBAAAAAAAAQQWABSNJKzjaUb3uOxixsvh1GGE3fW7zQEDCAD5ApUAAAAAB/wEcHNldAIgIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIH/ARwc2V0CAQAAAAAAAEEAAEDCCgUAAAAAAAAB/wEcHNldAIgIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIH/ARwc2V0CAQAAAAAAA==") + self.log.info("Try decoding and combining transactions in various states of blindedness") + self.pset_confidential_proofs() + if __name__ == '__main__': PSBTTest().main()