Merge ElementsProject/elements#1046: PSET: change sanity checks for blinding

d1a1d19113 test: add decodepsbt and analyzepsbt tests for blinding proofs (Andrew Poelstra)
03b835c887 PSET: output warnings in decodepsbt and analyzepsbt about blinding status (Andrew Poelstra)
694ec795e4 PSET: fix asset proof generation and verification (Andrew Poelstra)
c6f801d4ce PSET: encapsulate blind proof checks into one method (Andrew Poelstra)
35cfda73b9 PSET: do not assume in GetUnsignedTx that explicit amounts/assets are available (Andrew Poelstra)
c88eb96e74 pset: check that we can get the blinding factors from any IsMine outputs before signing (Andrew Poelstra)
9c55d0a175 pset: only check asset/amount proofs in case both explicit+blinded values are provided (Andrew Poelstra)

Pull request description:

  After conversation with @stepansnigirev in #1026 I realize we need to adjust our sanity checks on asset proofs. In particular,

  * Rather than enforcing that all confidential values have corresponding explicit values with proofs, we should only check that **if** both confidential and explicit values are present, they are connected by a proof.
  * Further, the wallet should check before signing that any `IsMine` scripts have confidential values that we are actually able to spend (i.e. rewinding works).

  This PR implements these two things, and also extends the `analyzepsbt` and `decodepsbt` RPC calls to provide more information if the amount/asset proofs are missing or invalid. It adds tests for these and also demonstrates that `combinepsbt` does not really work without explicit data present.

  `walletprocesspsbt`, when blinding, will still produce these proofs and there is currently no way to disable this behavior.

ACKs for top commit:
  achow101:
    ACK d1a1d19113

Tree-SHA512: 472ee0fe285308e803cbc3f5885e513ec91d1b3f18a2973772cd5a88d29fa4dba9441d9b3bd58847564e43d9a7478bbc75af62d9326c727709b5ff2dc646714a
This commit is contained in:
Andrew Poelstra 2021-09-22 02:01:33 +00:00
commit 4c7dc0620a
No known key found for this signature in database
GPG key ID: C588D63CE41B97C1
10 changed files with 305 additions and 53 deletions

View file

@ -94,7 +94,7 @@ The currently defined elements per-input proprietary types are as folows:
| None
| No key data
| <tt><33 byte commitment></tt>
| The 33 byte Value Commitment. If provided, <tt>PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF</tt> must be provided too.
| The 33 byte Value Commitment. If provided, either <tt>PSBT_ELEMENTS_IN_ISSUANCE_VALUE</tt> must be removed, or <tt>PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF</tt> 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
| <tt><33 byte commitment></tt>
| The 33 byte commitment to the inflation keys output value in this issuance. If provided, <tt>PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF</tt> must be provided too.
| The 33 byte commitment to the inflation keys output value in this issuance. If provided, either <tt>PSBT_ELEMENTS_IN_ISSUANCE_INFLATION_KEYS</tt> must be removed or <tt>PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF</tt> 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
| <tt><33 byte commitment></tt>
| The 33 byte Value Commitment for this output. If provided, <tt>PSBT_ELEMENTS_OUT_BLIND_VALUE_PROOF</tt> must be provided too.
| The 33 byte Value Commitment for this output. If provided, either <tt>PSBT_OUT_VALUE</tt> must be removed or <tt>PSBT_ELEMENTS_OUT_BLIND_VALUE_PROOF</tt> 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
| <tt><33 byte commitment></tt>
| The 33 byte Asset Commitment for this output. If provided, <tt>PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF</tt> must be provided too.
| The 33 byte Asset Commitment for this output. If provided, either <tt>PSBT_ELEMENTS_OUT_ASSET</tt> must be removed or <tt>PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF</tt> must be provided too.
|
| 0
| 2

View file

@ -66,19 +66,23 @@ bool CreateAssetSurjectionProof(std::vector<unsigned char>& output_proof, const
return true;
}
bool VerifyBlindAssetProof(const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset)
static bool VerifyBlindAssetProof(const uint256& asset, const std::vector<unsigned char>& 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<unsigned char>& rangeproof, const uint256
}
// Create an explicit value rangeproof which proves that the commitment commits to an explicit value
bool CreateBlindValueProof(std::vector<unsigned char>& rangeproof, const uint256& value_blinder, const CAmount amount, const secp256k1_pedersen_commitment& value_commit, const secp256k1_generator& gen)
static bool CreateBlindValueProof(std::vector<unsigned char>& 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<unsigned char>& rangeproof, const uint256
return res == 1;
}
bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector<unsigned char>& 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<unsigned char>& 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<unsigned char>& 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 != 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<uint32_t, st
// Create explicit asset surjection proof
std::vector<unsigned char> 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;
}

View file

@ -16,6 +16,7 @@
#include <secp256k1_surjectionproof.h>
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<unsigned char>& output_proof, const std::vector<secp256k1_fixed_asset_tag>& fixed_input_tags, const std::vector<secp256k1_generator>& ephemeral_input_tags, const std::vector<uint256>& 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<unsigned char>& proof, const CConfidentialAsset& conf_asset);
uint256 GenerateRangeproofECDHKey(CPubKey& ephemeral_pubkey, const CPubKey blinding_pubkey);
bool CreateValueRangeProof(std::vector<unsigned char>& 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<unsigned char>& 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<unsigned char>& 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<uint32_t, std::tuple<CAmount, CAsset, uint256, uint256>> our_input_data, std::map<uint32_t, std::pair<CKey, CKey>> our_issuances_to_blind);
BlindProofResult VerifyBlindProofs(const PSBTOutput& o);
#endif //BITCOIN_BLINDPSBT_H

View file

@ -87,9 +87,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;

View file

@ -6,6 +6,7 @@
#define BITCOIN_NODE_PSBT_H
#include <psbt.h>
#include <blindpsbt.h>
/**
* Holds an analysis of one input from a PSBT
@ -21,6 +22,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)
*/
@ -29,6 +38,7 @@ struct PSBTAnalysis {
Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
Optional<CAmount> fee; //!< Amount of fee being paid by the transaction
std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
std::vector<PSBTOutputAnalysis> 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

View file

@ -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 != 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);

View file

@ -926,18 +926,6 @@ struct PSBTInput
if (prev_out == nullopt) {
throw std::ios_base::failure("Previous output's index is required in PSBTv2");
}
if (!m_issuance_value_commitment.IsNull() && m_issuance_value == 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 == 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");
}
@ -1319,21 +1307,15 @@ struct PSBTOutput
// Make sure required PSBTv2 fields are present
if (m_psbt_version >= 2) {
if (amount == nullopt) {
if (amount == nullopt && m_value_commitment.IsNull()) {
throw std::ios_base::failure("Output amount is required in PSBTv2");
}
if (script == 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 == nullopt) {
throw std::ios_base::failure("Output is blinded but does not have a blinder index");
}

View file

@ -1227,6 +1227,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"},
@ -1673,6 +1674,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);
@ -2316,6 +2338,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 + "/kB. 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"},
@ -2376,6 +2406,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 != nullopt) {
result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
}

View file

@ -2829,21 +2829,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.get(), 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
}
}
}
}
@ -2853,7 +2883,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());

View file

@ -716,6 +716,87 @@ class PSBTTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
def pset_confidential_proofs(self):
BLINDED = "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/ewcpqRz4eW8zMzXFO/ZlNvAomxweIBD8YyywTguhBMI0BdLs2VeS5mc5e1oR0R27YAUccH/ARwc2V0CkMBAAGJm91DfvVBUOaEFZ0uH1RbT2cgI9MN9k1lE1hlWc2AtALpMJ17khkivt8F7dgCAVdBvcHFaw138ZsVfiD7g480AAEEAAEDCADh9QUAAAAAB/wEcHNldAIgIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIH/ARwc2V0CAQAAAAAAA=="
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()
@ -876,5 +957,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()