Merge pull request #1157 from achow101/explicit-pset-fields

pset: Add input explicit value, assets, and proofs, and issuance blinding flag
This commit is contained in:
Pablo Greco 2022-09-06 16:00:45 -03:00 committed by GitHub
commit b4935a51c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 222 additions and 1 deletions

View file

@ -255,6 +255,56 @@ The currently defined elements per-input proprietary types are as follows:
|
| 0
| 2
|-
| Explicit Value
| <tt>PSBT_ELEMENTS_IN_EXPLICIT_VALUE = 0x11</tt>
| None
| No key data
| <tt><64-bit little endian int value></tt>
| The explicit value for the input being spent. If provided, <tt>PSBT_ELEMENTS_IN_VALUE_PROOF</tt> must be provided too. Must not be provided if the input's value in the UTXO is already explicit.
|
| 0
| 2
|-
| Explicit Value Proof
| <tt>PSBT_ELEMENTS_IN_VALUE_PROOF = 0x12</tt>
| None
| No key data
| <tt><rangeproof></tt>
| An explicit value rangeproof that proves that the value commitment in this input's UTXO matches the explicit value in <tt>PSBT_ELEMENTS_IN_EXPLICIT_VALUE</tt>. If provided, <tt>PSBT_ELEMENTS_IN_EXPLICIT_VALUE</tt> must be provided too.
|
| 0
| 2
|-
| Explicit Asset
| <tt>PSBT_ELEMENTS_IN_EXPLICIT_ASSET = 0x13</tt>
| None
| No key data
| <tt><32 byte asset tag></tt>
| The explicit asset for the input being spent. If provided, <tt>PSBT_ELEMENTS_IN_ASSET_PROOF</tt> must be provided too. Must not be provided if the input's asset in the UTXO is already explicit.
|
| 0
| 2
|-
| Explicit Asset Proof
| <tt>PSBT_ELEMENTS_IN_ASSET_PROOF = 0x14</tt>
| None
| No key data
| <tt><proof></tt>
| An asset surjection proof with this input's asset as the only asset in the input set in order to prove that the asset commitment in the UTXO matches the explicit asset in <tt>PSBT_ELEMENTS_IN_EXPLICIT_ASSET</tt>. If provided, <tt>PSBT_ELEMENTS_IN_EXPLICIT_ASSET</tt> must be provided too.
|
| 0
| 2
|-
| Blinded Issuance Flag
| <tt>PSBT_ELEMENTS_IN_BLINDED_ISSUANCE = 0x15</tt>
| None
| No key data
| <tt><1 byte boolean></tt>
| A boolean flag. <tt>0x00</tt> indicates the issuance should not be blinded, <tt>0x01</tt> indicates it should be. If not specified, assumed to be <tt>0x01</tt>. Note that this does not indicate actual blinding status, but rather the expected blinding status prior to signing.
|
| 0
| 2
|}
The currently defined elements per-output proprietary types are as follows:

View file

@ -220,6 +220,35 @@ BlindProofResult VerifyBlindProofs(const PSBTOutput& o) {
return BlindProofResult::OK;
}
BlindProofResult VerifyBlindProofs(const PSBTInput& i) {
CTxOut utxo;
if (!i.GetUTXO(utxo)) {
return BlindProofResult::OK;
}
if (i.m_explicit_value != std::nullopt) {
if (i.m_value_proof.empty()) {
return BlindProofResult::MISSING_VALUE_PROOF;
} else if (!utxo.nValue.IsCommitment()) {
return BlindProofResult::NOT_FULLY_BLINDED;
} else if (!VerifyBlindValueProof(*i.m_explicit_value, utxo.nValue, i.m_value_proof, utxo.nAsset)) {
return BlindProofResult::INVALID_VALUE_PROOF;
}
}
if (!i.m_explicit_asset.IsNull()) {
if (i.m_asset_proof.empty()) {
return BlindProofResult::MISSING_ASSET_PROOF;
} else if (!utxo.nAsset.IsCommitment()) {
return BlindProofResult::NOT_FULLY_BLINDED;
} else if (!VerifyBlindAssetProof(i.m_explicit_asset, i.m_asset_proof, utxo.nAsset)) {
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);
@ -386,7 +415,8 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
}
// Handle issuances
if (input.m_issuance_value != std::nullopt || input.m_issuance_value_commitment.IsCommitment() || input.m_issuance_inflation_keys_amount != std::nullopt || input.m_issuance_inflation_keys_commitment.IsCommitment()) {
if ((!input.m_blinded_issuance.has_value() || input.m_blinded_issuance.value()) &&
(input.m_issuance_value != std::nullopt || input.m_issuance_value_commitment.IsCommitment() || input.m_issuance_inflation_keys_amount != std::nullopt || input.m_issuance_inflation_keys_commitment.IsCommitment())) {
CAsset issuance_asset;
CAsset reissuance_asset;

View file

@ -17,6 +17,7 @@
struct PartiallySignedTransaction;
struct PSBTOutput;
struct PSBTInput;
enum class BlindingStatus
{
@ -52,5 +53,6 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset);
bool VerifyBlindAssetProof(const uint256& asset, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset);
BlindProofResult VerifyBlindProofs(const PSBTOutput& o);
BlindProofResult VerifyBlindProofs(const PSBTInput& i);
#endif //BITCOIN_BLINDPSBT_H

View file

@ -73,6 +73,11 @@ static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_ASSET_ENTROPY = 0x0d;
static constexpr uint8_t PSBT_ELEMENTS_IN_UTXO_RANGEPROOF = 0x0e;
static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF = 0x0f;
static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF = 0x10;
static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_VALUE = 0x11;
static constexpr uint8_t PSBT_ELEMENTS_IN_VALUE_PROOF = 0x12;
static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_ASSET = 0x13;
static constexpr uint8_t PSBT_ELEMENTS_IN_ASSET_PROOF = 0x14;
static constexpr uint8_t PSBT_ELEMENTS_IN_BLINDED_ISSUANCE = 0x15;
// Output types
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
@ -248,6 +253,7 @@ struct PSBTInput
uint256 m_issuance_asset_entropy;
std::vector<unsigned char> m_blind_issuance_value_proof;
std::vector<unsigned char> m_blind_issuance_inflation_keys_proof;
std::optional<bool> m_blinded_issuance;
// Peg-in
std::variant<std::monostate, Sidechain::Bitcoin::CTransactionRef, CTransactionRef> m_peg_in_tx;
@ -259,6 +265,10 @@ struct PSBTInput
// Auxiliary elements stuff
std::vector<unsigned char> m_utxo_rangeproof;
std::optional<CAmount> m_explicit_value;
std::vector<unsigned char> m_value_proof;
uint256 m_explicit_asset;
std::vector<unsigned char> m_asset_proof;
bool IsNull() const;
void FillSignatureData(SignatureData& sigdata) const;
@ -473,6 +483,31 @@ struct PSBTInput
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF));
s << m_blind_issuance_inflation_keys_proof;
}
// Explicit value and its proof
if (m_explicit_value.has_value()) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_EXPLICIT_VALUE));
SerializeToVector(s, m_explicit_value.value());
}
if (!m_value_proof.empty()) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_VALUE_PROOF));
s << m_value_proof;
}
// Explicit asset and its proof
if (!m_explicit_asset.IsNull()) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_EXPLICIT_ASSET));
SerializeToVector(s, m_explicit_asset);
}
if (!m_asset_proof.empty()) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ASSET_PROOF));
s << m_asset_proof;
}
if (m_blinded_issuance.has_value()) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_BLINDED_ISSUANCE));
SerializeToVector(s, *m_blinded_issuance);
}
}
// Write proprietary things
@ -886,6 +921,60 @@ struct PSBTInput
s >> m_blind_issuance_inflation_keys_proof;
break;
}
case PSBT_ELEMENTS_IN_EXPLICIT_VALUE:
{
if (!key_lookup.emplace(key).second) {
throw std::ios_base::failure("Duplicate Key, explicit value is already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input explicit value is more than one byte type");
}
CAmount v;
UnserializeFromVector(s, v);
m_explicit_value = v;
break;
}
case PSBT_ELEMENTS_IN_VALUE_PROOF:
{
if (!key_lookup.emplace(key).second) {
throw std::ios_base::failure("Duplicate Key, explicit value proof is already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input explicit value proof is more than one byte type");
}
s >> m_value_proof;
break;
}
case PSBT_ELEMENTS_IN_EXPLICIT_ASSET:
{
if (!key_lookup.emplace(key).second) {
throw std::ios_base::failure("Duplicate Key, explicit asset is already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input explicit asset is more than one byte type");
}
UnserializeFromVector(s, m_explicit_asset);
break;
}
case PSBT_ELEMENTS_IN_ASSET_PROOF:
{
if (!key_lookup.emplace(key).second) {
throw std::ios_base::failure("Duplicate Key, explicit asset proof is already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input explicit asset proof is more than one byte type");
}
s >> m_value_proof;
break;
}
case PSBT_ELEMENTS_IN_BLINDED_ISSUANCE:
{
if (!key_lookup.emplace(key).second) {
throw std::ios_base::failure("Duplicate Key, issuance needs blinded flag is already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input issuance needs blinded flag is more than one byte type");
}
bool b;
UnserializeFromVector(s, b);
m_blinded_issuance = b;
break;
}
default:
{
known = false;
@ -936,6 +1025,12 @@ struct PSBTInput
if (!m_issuance_inflation_keys_commitment.IsNull() && m_issuance_inflation_keys_rangeproof.empty()) {
throw std::ios_base::failure("Issuance inflation keys commitment provided without inflation keys rangeproof");
}
if ((m_explicit_value.has_value() || !m_value_proof.empty()) && (!m_explicit_value.has_value() || m_value_proof.empty())) {
throw std::ios_base::failure("Input explicit value and value proof must be provided together");
}
if ((!m_explicit_asset.IsNull() || !m_asset_proof.empty()) && (!m_explicit_asset.IsNull() || m_asset_proof.empty())) {
throw std::ios_base::failure("Input explicit asset and asset proof must be provided together");
}
}
}

View file

@ -1214,6 +1214,11 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"},
}},
{RPCResult::Type::STR_HEX, "utxo_rangeproof", "The rangeproof for the UTXO"},
{RPCResult::Type::NUM, "explicit_value", /*optional=*/true, "The explicit value for this input"},
{RPCResult::Type::STR_HEX, "value_proof", /*optional=*/true, "The explicit value proof for this input"},
{RPCResult::Type::STR_HEX, "explicit_asset", /*optional=*/true, "The explicit asset for this input"},
{RPCResult::Type::STR_HEX, "asset_proof", /*optional=*/true, "The explicit asset proof for this input"},
{RPCResult::Type::BOOL, "blinded_issuance", /*optional=*/true, "Whether the issuance should be blinded prior to signing"},
{RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
@ -1596,6 +1601,45 @@ static RPCHelpMan decodepsbt()
in.pushKV("utxo_rangeproof", HexStr(input.m_utxo_rangeproof));
}
if (input.m_explicit_value.has_value()) {
in.pushKV("explicit_value", ValueFromAmount(*input.m_explicit_value));
}
if (!input.m_value_proof.empty()) {
in.pushKV("value_proof", HexStr(input.m_value_proof));
}
if (!input.m_explicit_asset.IsNull()) {
in.pushKV("explicit_asset", input.m_explicit_asset.GetHex());
}
if (!input.m_asset_proof.empty()) {
in.pushKV("asset_proof", HexStr(input.m_asset_proof));
}
if (input.m_blinded_issuance.has_value()) {
in.pushKV("blinded_issuance", *input.m_blinded_issuance);
}
switch (VerifyBlindProofs(input)) {
case BlindProofResult::OK:
// all good
break;
case BlindProofResult::NOT_FULLY_BLINDED:
in.pushKV("status", "ERROR: Proofs provided for unblinded input");
break;
case BlindProofResult::MISSING_VALUE_PROOF:
in.pushKV("status", "WARNING: has confidential and explicit values but no proof connecting them");
break;
case BlindProofResult::MISSING_ASSET_PROOF:
in.pushKV("status", "WARNING: has confidential and explicit assets but no proof connecting them");
break;
case BlindProofResult::INVALID_VALUE_PROOF:
in.pushKV("status", "ERROR: has invalid value proof, the value may be a lie!");
break;
case BlindProofResult::INVALID_ASSET_PROOF:
in.pushKV("status", "ERROR: has invalid asset proof, the asset may be a lie!");
break;
}
// Proprietary
if (!input.m_proprietary.empty()) {
UniValue proprietary(UniValue::VARR);