Skip to content

Commit b4935a5

Browse files
authored
Merge pull request #1157 from achow101/explicit-pset-fields
pset: Add input explicit value, assets, and proofs, and issuance blinding flag
2 parents da236e8 + 9ecbf38 commit b4935a5

File tree

5 files changed

+222
-1
lines changed

5 files changed

+222
-1
lines changed

doc/pset.mediawiki

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,56 @@ The currently defined elements per-input proprietary types are as follows:
255255
|
256256
| 0
257257
| 2
258+
|-
259+
| Explicit Value
260+
| <tt>PSBT_ELEMENTS_IN_EXPLICIT_VALUE = 0x11</tt>
261+
| None
262+
| No key data
263+
| <tt><64-bit little endian int value></tt>
264+
| 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.
265+
|
266+
| 0
267+
| 2
268+
|-
269+
| Explicit Value Proof
270+
| <tt>PSBT_ELEMENTS_IN_VALUE_PROOF = 0x12</tt>
271+
| None
272+
| No key data
273+
| <tt><rangeproof></tt>
274+
| 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.
275+
|
276+
| 0
277+
| 2
278+
|-
279+
| Explicit Asset
280+
| <tt>PSBT_ELEMENTS_IN_EXPLICIT_ASSET = 0x13</tt>
281+
| None
282+
| No key data
283+
| <tt><32 byte asset tag></tt>
284+
| 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.
285+
|
286+
| 0
287+
| 2
288+
|-
289+
| Explicit Asset Proof
290+
| <tt>PSBT_ELEMENTS_IN_ASSET_PROOF = 0x14</tt>
291+
| None
292+
| No key data
293+
| <tt><proof></tt>
294+
| 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.
295+
|
296+
| 0
297+
| 2
298+
|-
299+
| Blinded Issuance Flag
300+
| <tt>PSBT_ELEMENTS_IN_BLINDED_ISSUANCE = 0x15</tt>
301+
| None
302+
| No key data
303+
| <tt><1 byte boolean></tt>
304+
| 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.
305+
|
306+
| 0
307+
| 2
258308
|}
259309

260310
The currently defined elements per-output proprietary types are as follows:

src/blindpsbt.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,35 @@ BlindProofResult VerifyBlindProofs(const PSBTOutput& o) {
220220
return BlindProofResult::OK;
221221
}
222222

223+
BlindProofResult VerifyBlindProofs(const PSBTInput& i) {
224+
CTxOut utxo;
225+
if (!i.GetUTXO(utxo)) {
226+
return BlindProofResult::OK;
227+
}
228+
229+
if (i.m_explicit_value != std::nullopt) {
230+
if (i.m_value_proof.empty()) {
231+
return BlindProofResult::MISSING_VALUE_PROOF;
232+
} else if (!utxo.nValue.IsCommitment()) {
233+
return BlindProofResult::NOT_FULLY_BLINDED;
234+
} else if (!VerifyBlindValueProof(*i.m_explicit_value, utxo.nValue, i.m_value_proof, utxo.nAsset)) {
235+
return BlindProofResult::INVALID_VALUE_PROOF;
236+
}
237+
}
238+
239+
if (!i.m_explicit_asset.IsNull()) {
240+
if (i.m_asset_proof.empty()) {
241+
return BlindProofResult::MISSING_ASSET_PROOF;
242+
} else if (!utxo.nAsset.IsCommitment()) {
243+
return BlindProofResult::NOT_FULLY_BLINDED;
244+
} else if (!VerifyBlindAssetProof(i.m_explicit_asset, i.m_asset_proof, utxo.nAsset)) {
245+
return BlindProofResult::INVALID_ASSET_PROOF;
246+
}
247+
}
248+
249+
return BlindProofResult::OK;
250+
}
251+
223252
void CreateAssetCommitment(CConfidentialAsset& conf_asset, secp256k1_generator& asset_gen, const CAsset& asset, const uint256& asset_blinder)
224253
{
225254
conf_asset.vchCommitment.resize(CConfidentialAsset::nCommittedSize);
@@ -386,7 +415,8 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
386415
}
387416

388417
// Handle issuances
389-
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()) {
418+
if ((!input.m_blinded_issuance.has_value() || input.m_blinded_issuance.value()) &&
419+
(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())) {
390420
CAsset issuance_asset;
391421
CAsset reissuance_asset;
392422

src/blindpsbt.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
struct PartiallySignedTransaction;
1919
struct PSBTOutput;
20+
struct PSBTInput;
2021

2122
enum class BlindingStatus
2223
{
@@ -52,5 +53,6 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
5253
bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset);
5354
bool VerifyBlindAssetProof(const uint256& asset, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset);
5455
BlindProofResult VerifyBlindProofs(const PSBTOutput& o);
56+
BlindProofResult VerifyBlindProofs(const PSBTInput& i);
5557

5658
#endif //BITCOIN_BLINDPSBT_H

src/psbt.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_ASSET_ENTROPY = 0x0d;
7373
static constexpr uint8_t PSBT_ELEMENTS_IN_UTXO_RANGEPROOF = 0x0e;
7474
static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF = 0x0f;
7575
static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF = 0x10;
76+
static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_VALUE = 0x11;
77+
static constexpr uint8_t PSBT_ELEMENTS_IN_VALUE_PROOF = 0x12;
78+
static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_ASSET = 0x13;
79+
static constexpr uint8_t PSBT_ELEMENTS_IN_ASSET_PROOF = 0x14;
80+
static constexpr uint8_t PSBT_ELEMENTS_IN_BLINDED_ISSUANCE = 0x15;
7681

7782
// Output types
7883
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
@@ -248,6 +253,7 @@ struct PSBTInput
248253
uint256 m_issuance_asset_entropy;
249254
std::vector<unsigned char> m_blind_issuance_value_proof;
250255
std::vector<unsigned char> m_blind_issuance_inflation_keys_proof;
256+
std::optional<bool> m_blinded_issuance;
251257

252258
// Peg-in
253259
std::variant<std::monostate, Sidechain::Bitcoin::CTransactionRef, CTransactionRef> m_peg_in_tx;
@@ -259,6 +265,10 @@ struct PSBTInput
259265

260266
// Auxiliary elements stuff
261267
std::vector<unsigned char> m_utxo_rangeproof;
268+
std::optional<CAmount> m_explicit_value;
269+
std::vector<unsigned char> m_value_proof;
270+
uint256 m_explicit_asset;
271+
std::vector<unsigned char> m_asset_proof;
262272

263273
bool IsNull() const;
264274
void FillSignatureData(SignatureData& sigdata) const;
@@ -473,6 +483,31 @@ struct PSBTInput
473483
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF));
474484
s << m_blind_issuance_inflation_keys_proof;
475485
}
486+
487+
// Explicit value and its proof
488+
if (m_explicit_value.has_value()) {
489+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_EXPLICIT_VALUE));
490+
SerializeToVector(s, m_explicit_value.value());
491+
}
492+
if (!m_value_proof.empty()) {
493+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_VALUE_PROOF));
494+
s << m_value_proof;
495+
}
496+
497+
// Explicit asset and its proof
498+
if (!m_explicit_asset.IsNull()) {
499+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_EXPLICIT_ASSET));
500+
SerializeToVector(s, m_explicit_asset);
501+
}
502+
if (!m_asset_proof.empty()) {
503+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ASSET_PROOF));
504+
s << m_asset_proof;
505+
}
506+
507+
if (m_blinded_issuance.has_value()) {
508+
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_BLINDED_ISSUANCE));
509+
SerializeToVector(s, *m_blinded_issuance);
510+
}
476511
}
477512

478513
// Write proprietary things
@@ -886,6 +921,60 @@ struct PSBTInput
886921
s >> m_blind_issuance_inflation_keys_proof;
887922
break;
888923
}
924+
case PSBT_ELEMENTS_IN_EXPLICIT_VALUE:
925+
{
926+
if (!key_lookup.emplace(key).second) {
927+
throw std::ios_base::failure("Duplicate Key, explicit value is already provided");
928+
} else if (key.size() != 1) {
929+
throw std::ios_base::failure("Input explicit value is more than one byte type");
930+
}
931+
CAmount v;
932+
UnserializeFromVector(s, v);
933+
m_explicit_value = v;
934+
break;
935+
}
936+
case PSBT_ELEMENTS_IN_VALUE_PROOF:
937+
{
938+
if (!key_lookup.emplace(key).second) {
939+
throw std::ios_base::failure("Duplicate Key, explicit value proof is already provided");
940+
} else if (key.size() != 1) {
941+
throw std::ios_base::failure("Input explicit value proof is more than one byte type");
942+
}
943+
s >> m_value_proof;
944+
break;
945+
}
946+
case PSBT_ELEMENTS_IN_EXPLICIT_ASSET:
947+
{
948+
if (!key_lookup.emplace(key).second) {
949+
throw std::ios_base::failure("Duplicate Key, explicit asset is already provided");
950+
} else if (key.size() != 1) {
951+
throw std::ios_base::failure("Input explicit asset is more than one byte type");
952+
}
953+
UnserializeFromVector(s, m_explicit_asset);
954+
break;
955+
}
956+
case PSBT_ELEMENTS_IN_ASSET_PROOF:
957+
{
958+
if (!key_lookup.emplace(key).second) {
959+
throw std::ios_base::failure("Duplicate Key, explicit asset proof is already provided");
960+
} else if (key.size() != 1) {
961+
throw std::ios_base::failure("Input explicit asset proof is more than one byte type");
962+
}
963+
s >> m_value_proof;
964+
break;
965+
}
966+
case PSBT_ELEMENTS_IN_BLINDED_ISSUANCE:
967+
{
968+
if (!key_lookup.emplace(key).second) {
969+
throw std::ios_base::failure("Duplicate Key, issuance needs blinded flag is already provided");
970+
} else if (key.size() != 1) {
971+
throw std::ios_base::failure("Input issuance needs blinded flag is more than one byte type");
972+
}
973+
bool b;
974+
UnserializeFromVector(s, b);
975+
m_blinded_issuance = b;
976+
break;
977+
}
889978
default:
890979
{
891980
known = false;
@@ -936,6 +1025,12 @@ struct PSBTInput
9361025
if (!m_issuance_inflation_keys_commitment.IsNull() && m_issuance_inflation_keys_rangeproof.empty()) {
9371026
throw std::ios_base::failure("Issuance inflation keys commitment provided without inflation keys rangeproof");
9381027
}
1028+
if ((m_explicit_value.has_value() || !m_value_proof.empty()) && (!m_explicit_value.has_value() || m_value_proof.empty())) {
1029+
throw std::ios_base::failure("Input explicit value and value proof must be provided together");
1030+
}
1031+
if ((!m_explicit_asset.IsNull() || !m_asset_proof.empty()) && (!m_explicit_asset.IsNull() || m_asset_proof.empty())) {
1032+
throw std::ios_base::failure("Input explicit asset and asset proof must be provided together");
1033+
}
9391034
}
9401035
}
9411036

src/rpc/rawtransaction.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,11 @@ static RPCHelpMan decodepsbt()
12141214
{RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"},
12151215
}},
12161216
{RPCResult::Type::STR_HEX, "utxo_rangeproof", "The rangeproof for the UTXO"},
1217+
{RPCResult::Type::NUM, "explicit_value", /*optional=*/true, "The explicit value for this input"},
1218+
{RPCResult::Type::STR_HEX, "value_proof", /*optional=*/true, "The explicit value proof for this input"},
1219+
{RPCResult::Type::STR_HEX, "explicit_asset", /*optional=*/true, "The explicit asset for this input"},
1220+
{RPCResult::Type::STR_HEX, "asset_proof", /*optional=*/true, "The explicit asset proof for this input"},
1221+
{RPCResult::Type::BOOL, "blinded_issuance", /*optional=*/true, "Whether the issuance should be blinded prior to signing"},
12171222
{RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
12181223
{
12191224
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
@@ -1596,6 +1601,45 @@ static RPCHelpMan decodepsbt()
15961601
in.pushKV("utxo_rangeproof", HexStr(input.m_utxo_rangeproof));
15971602
}
15981603

1604+
if (input.m_explicit_value.has_value()) {
1605+
in.pushKV("explicit_value", ValueFromAmount(*input.m_explicit_value));
1606+
}
1607+
if (!input.m_value_proof.empty()) {
1608+
in.pushKV("value_proof", HexStr(input.m_value_proof));
1609+
}
1610+
if (!input.m_explicit_asset.IsNull()) {
1611+
in.pushKV("explicit_asset", input.m_explicit_asset.GetHex());
1612+
}
1613+
if (!input.m_asset_proof.empty()) {
1614+
in.pushKV("asset_proof", HexStr(input.m_asset_proof));
1615+
}
1616+
1617+
if (input.m_blinded_issuance.has_value()) {
1618+
in.pushKV("blinded_issuance", *input.m_blinded_issuance);
1619+
}
1620+
1621+
switch (VerifyBlindProofs(input)) {
1622+
case BlindProofResult::OK:
1623+
// all good
1624+
break;
1625+
case BlindProofResult::NOT_FULLY_BLINDED:
1626+
in.pushKV("status", "ERROR: Proofs provided for unblinded input");
1627+
break;
1628+
case BlindProofResult::MISSING_VALUE_PROOF:
1629+
in.pushKV("status", "WARNING: has confidential and explicit values but no proof connecting them");
1630+
break;
1631+
case BlindProofResult::MISSING_ASSET_PROOF:
1632+
in.pushKV("status", "WARNING: has confidential and explicit assets but no proof connecting them");
1633+
break;
1634+
case BlindProofResult::INVALID_VALUE_PROOF:
1635+
in.pushKV("status", "ERROR: has invalid value proof, the value may be a lie!");
1636+
break;
1637+
case BlindProofResult::INVALID_ASSET_PROOF:
1638+
in.pushKV("status", "ERROR: has invalid asset proof, the asset may be a lie!");
1639+
break;
1640+
}
1641+
1642+
15991643
// Proprietary
16001644
if (!input.m_proprietary.empty()) {
16011645
UniValue proprietary(UniValue::VARR);

0 commit comments

Comments
 (0)