Skip to content
4 changes: 3 additions & 1 deletion src/blindpsbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,13 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
if (secp256k1_generator_generate(secp256k1_blind_context, &ephemeral_input_tags.back(), asset.GetAsset().begin()) != 1) {
return BlindingStatus::INVALID_ASSET;
}
} else {
} else if (asset.IsCommitment()) {
// Parse the asset commitment as a generator (because it is)
if (secp256k1_generator_parse(secp256k1_blind_context, &ephemeral_input_tags.back(), asset.vchCommitment.data()) != 1) {
return BlindingStatus::INVALID_ASSET_COMMITMENT;
}
} else {
return BlindingStatus::INVALID_ASSET; // Missing asset
}

fixed_input_tags.emplace_back();
Expand Down
44 changes: 25 additions & 19 deletions src/psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ template<typename Stream, typename... X>
void UnserializeFromVector(Stream& s, X&... args)
{
size_t expected_size = ReadCompactSize(s);
if (!expected_size) {
return; /* Zero size = no data to read */
}
Comment thread
jgriffiths marked this conversation as resolved.
size_t remaining_before = s.size();
UnserializeMany(s, args...);
size_t remaining_after = s.size();
Expand Down Expand Up @@ -343,15 +346,15 @@ struct PSBTInput
}

// Elements proprietary fields are only allowed with v2
// Issuance value
if (!m_issuance_value_commitment.IsNull()) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ISSUANCE_VALUE_COMMITMENT));
SerializeToVector(s, m_issuance_value_commitment);
}
// Issuance value + commitment
if (m_issuance_value != std::nullopt) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ISSUANCE_VALUE));
SerializeToVector(s, *m_issuance_value);
}
if (!m_issuance_value_commitment.IsNull()) {
SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ISSUANCE_VALUE_COMMITMENT));
SerializeToVector(s, m_issuance_value_commitment);
}

// Issuance rangeproof
if (!m_issuance_rangeproof.empty()) {
Expand Down Expand Up @@ -995,8 +998,12 @@ struct PSBTOutput
SerializeHDKeypaths(s, hd_keypaths, CompactSizeWriter(PSBT_OUT_BIP32_DERIVATION));

if (m_psbt_version >= 2) {
// Write spk
if (script != std::nullopt) {
// Write amount and spk
if (amount != std::nullopt) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_AMOUNT));
SerializeToVector(s, *amount);
}
if (script.has_value()) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_SCRIPT));
s << *script;
}
Expand All @@ -1007,20 +1014,16 @@ struct PSBTOutput
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_OUT_VALUE_COMMITMENT));
SerializeToVector(s, m_value_commitment);
}
if (amount != std::nullopt) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_AMOUNT));
SerializeToVector(s, *amount);
}

// Asset
if (!m_asset_commitment.IsNull()) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_OUT_ASSET_COMMITMENT));
SerializeToVector(s, m_asset_commitment);
}
// Asset + commitment
if (!m_asset.IsNull()) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_OUT_ASSET));
SerializeToVector(s, m_asset);
}
if (!m_asset_commitment.IsNull()) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_OUT_ASSET_COMMITMENT));
SerializeToVector(s, m_asset_commitment);
}

// Value rangeproof
if (!m_value_rangeproof.empty()) {
Expand Down Expand Up @@ -1052,13 +1055,13 @@ struct PSBTOutput
SerializeToVector(s, *m_blinder_index);
}

// BLind value proof
// Blind value proof
if (!m_blind_value_proof.empty()) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_OUT_BLIND_VALUE_PROOF));
s << m_blind_value_proof;
}

// BLind asset proof
// Blind asset proof
if (!m_blind_asset_proof.empty()) {
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_OUT_BLIND_ASSET_PROOF));
s << m_blind_asset_proof;
Expand Down Expand Up @@ -1423,7 +1426,7 @@ struct PartiallySignedTransaction
// Scalar offsets
for (const uint256& scalar : m_scalar_offsets) {
SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_GLOBAL_SCALAR), scalar);
SerializeToVector(s, std::vector<unsigned char>());
s << PSBT_SEPARATOR; /* Zero length data value */
Comment thread
apoelstra marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -1659,6 +1662,9 @@ struct PartiallySignedTransaction
m_scalar_offsets.insert(scalar);
break;
}
default:
known = false;
break;
}
}

Expand Down
40 changes: 40 additions & 0 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2237,6 +2237,45 @@ static RPCHelpMan utxoupdatepsbt()
};
}

static RPCHelpMan parsepsbt()
{
return RPCHelpMan{"parsepsbt",
"\nparse and print a PSBT.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
},
RPCResult {
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
{RPCResult::Type::BOOL, "canonical", "Whether the input PSBT matches the output PSBT"}
}
},
RPCExamples {
HelpExampleCli("parsepsbt", "\"psbt\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
RPCTypeCheck(request.params, {UniValue::VSTR}, true);

// Unserialize the PSBT
PartiallySignedTransaction psbtx;
std::string error;
if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("PSBT decode failed %s", error));
}

// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
const std::string encoded = EncodeBase64(ssTx);
UniValue result(UniValue::VOBJ);
result.pushKV("psbt", encoded);
result.pushKV("canonical", encoded == request.params[0].get_str());
return result;
},
};
}
#if 0
static RPCHelpMan joinpsbts()
{
Expand Down Expand Up @@ -3207,6 +3246,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", &createpsbt, },
{ "rawtransactions", &converttopsbt, },
{ "rawtransactions", &utxoupdatepsbt, },
{ "rawtransactions", &parsepsbt, },
#if 0
{ "rawtransactions", &joinpsbts, },
#endif
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getnewblockhex",
"getpakinfo",
"getsidechaininfo",
"parsepsbt",
"rawblindrawtransaction",
"rawissueasset",
"rawreissueasset",
Expand Down
Loading