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 = "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"
+ 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()