mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
Merge ElementsProject/elements#1046: PSET: change sanity checks for blinding
d1a1d19113test: add decodepsbt and analyzepsbt tests for blinding proofs (Andrew Poelstra)03b835c887PSET: output warnings in decodepsbt and analyzepsbt about blinding status (Andrew Poelstra)694ec795e4PSET: fix asset proof generation and verification (Andrew Poelstra)c6f801d4cePSET: encapsulate blind proof checks into one method (Andrew Poelstra)35cfda73b9PSET: do not assume in GetUnsignedTx that explicit amounts/assets are available (Andrew Poelstra)c88eb96e74pset: check that we can get the blinding factors from any IsMine outputs before signing (Andrew Poelstra)9c55d0a175pset: 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: ACKd1a1d19113Tree-SHA512: 472ee0fe285308e803cbc3f5885e513ec91d1b3f18a2973772cd5a88d29fa4dba9441d9b3bd58847564e43d9a7478bbc75af62d9326c727709b5ff2dc646714a
This commit is contained in:
commit
4c7dc0620a
10 changed files with 305 additions and 53 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
19
src/psbt.cpp
19
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 != 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);
|
||||
|
|
|
|||
22
src/psbt.h
22
src/psbt.h
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -716,6 +716,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()
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue