Update hashing and support ticket (#6444)

This commit is contained in:
yinyiqian1
2026-02-27 11:50:22 -05:00
committed by GitHub
parent c2f8b91397
commit c52d317810
8 changed files with 50 additions and 67 deletions

View File

@@ -50,15 +50,15 @@ incrementConfidentialVersion(STObject& mptoken)
/**
* @brief Adds common fields to a serializer for ZKP context hash generation.
*
* Serializes the transaction type, account, sequence number, and issuance ID
* Serializes the transaction type, account, issuance ID and sequence/ticket number
* into the provided serializer. These fields form the base of all context
* hashes used in zero-knowledge proofs.
*
* @param s The serializer to append fields to.
* @param txType The transaction type identifier.
* @param account The account ID of the transaction sender.
* @param sequence The transaction sequence number.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
*/
void
addCommonZKPFields(
@@ -75,8 +75,8 @@ addCommonZKPFields(
* this specific send transaction, preventing proof reuse across transactions.
*
* @param account The sender's account ID.
* @param sequence The transaction sequence number.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
* @param destination The destination account ID.
* @param version The sender's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
@@ -84,8 +84,8 @@ addCommonZKPFields(
uint256
getSendContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version);
@@ -96,18 +96,16 @@ getSendContextHash(
* specific clawback transaction.
*
* @param account The issuer's account ID.
* @param sequence The transaction sequence number.
* @param issuanceID The MPToken Issuance ID.
* @param amount The amount being clawed back.
* @param sequence The transaction sequence number or ticket number.
* @param holder The holder's account ID being clawed back from.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getClawbackContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount,
std::uint32_t sequence,
AccountID const& holder);
/**
@@ -117,17 +115,12 @@ getClawbackContextHash(
* registration) to this specific convert transaction.
*
* @param account The holder's account ID.
* @param sequence The transaction sequence number.
* @param issuanceID The MPToken Issuance ID.
* @param amount The amount being converted to confidential.
* @param sequence The transaction sequence number or a ticket number.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount);
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence);
/**
* @brief Generates the context hash for ConfidentialMPTConvertBack transactions.
@@ -136,18 +129,16 @@ getConvertContextHash(
* this specific convert-back transaction.
*
* @param account The holder's account ID.
* @param sequence The transaction sequence number.
* @param issuanceID The MPToken Issuance ID.
* @param amount The amount being converted back to public.
* @param sequence The transaction sequence number or a ticket number.
* @param version The holder's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertBackContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount,
std::uint32_t sequence,
std::uint32_t version);
/**

View File

@@ -12,26 +12,28 @@ addCommonZKPFields(
Serializer& s,
std::uint16_t txType,
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID)
uint192 const& issuanceID,
std::uint32_t sequence)
{
// TxCommonHash = hash(TxType || Account || IssuanceID || SequenceOrTicket)
s.add16(txType);
s.addBitString(account);
s.add32(sequence);
s.addBitString(issuanceID);
s.add32(sequence);
}
uint256
getSendContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version)
{
Serializer s;
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_SEND, account, sequence, issuanceID);
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_SEND, account, issuanceID, sequence);
// TxSpecific = identity || freshness
s.addBitString(destination);
s.addInteger(version);
@@ -41,27 +43,29 @@ getSendContextHash(
uint256
getClawbackContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount,
std::uint32_t sequence,
AccountID const& holder)
{
Serializer s;
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_CLAWBACK, account, sequence, issuanceID);
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_CLAWBACK, account, issuanceID, sequence);
s.add64(amount);
// TxSpecific = identity || freshness
s.addBitString(holder);
s.addInteger(0);
return s.getSHA512Half();
}
uint256
getConvertContextHash(AccountID const& account, std::uint32_t sequence, uint192 const& issuanceID, std::uint64_t amount)
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence)
{
Serializer s;
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_CONVERT, account, sequence, issuanceID);
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_CONVERT, account, issuanceID, sequence);
s.add64(amount);
// TxSpecific = identity || freshness
s.addBitString(account);
s.addInteger(0);
return s.getSHA512Half();
}
@@ -69,15 +73,15 @@ getConvertContextHash(AccountID const& account, std::uint32_t sequence, uint192
uint256
getConvertBackContextHash(
AccountID const& account,
std::uint32_t sequence,
uint192 const& issuanceID,
std::uint64_t amount,
std::uint32_t sequence,
std::uint32_t version)
{
Serializer s;
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_CONVERT_BACK, account, sequence, issuanceID);
addCommonZKPFields(s, ttCONFIDENTIAL_MPT_CONVERT_BACK, account, issuanceID, sequence);
s.add64(amount);
// TxSpecific = identity || freshness
s.addBitString(account);
s.addInteger(version);
return s.getSHA512Half();

View File

@@ -2068,8 +2068,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
Buffer const convertBlindingFactor = generateBlindingFactor();
auto const convertHolderCiphertext = mptAlice.encryptAmount(bob, maxMPTokenAmount, convertBlindingFactor);
auto const convertIssuerCiphertext = mptAlice.encryptAmount(alice, maxMPTokenAmount, convertBlindingFactor);
auto const convertContextHash =
getConvertContextHash(bob.id(), env.seq(bob), mptAlice.issuanceID(), maxMPTokenAmount);
auto const convertContextHash = getConvertContextHash(bob.id(), mptAlice.issuanceID(), env.seq(bob));
auto const schnorrProof = mptAlice.getSchnorrProof(bob, convertContextHash);
BEAST_EXPECT(schnorrProof.has_value());
@@ -2121,7 +2120,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// Generate the proof using known spending balance value
auto const version = mptAlice.getMPTokenVersion(bob);
uint256 const convertBackContextHash =
getConvertBackContextHash(bob.id(), env.seq(bob), mptAlice.issuanceID(), convertBackAmt, version);
getConvertBackContextHash(bob.id(), mptAlice.issuanceID(), env.seq(bob), version);
Buffer const proof = mptAlice.getConvertBackProof(
bob,
@@ -3408,8 +3407,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// The proof uses PC(1, rho) but the transaction submits PC(balance, rho).
// Verification fails because the proof doesn't match the submitted commitment.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const badPedersenCommitment = mptAlice.getPedersenCommitment(1, pcBlindingFactor);
Buffer const proof = mptAlice.getConvertBackProof(
bob,
@@ -3437,8 +3435,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// The pedersen commitment PC = balance*G + rho*H requires the same rho
// used in proof generation. Using a different rho breaks the linkage.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const proof = mptAlice.getConvertBackProof(
bob,
@@ -3466,8 +3463,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// The proof claims balance=1 but the encrypted spending balance contains
// the actual balance. Verification fails because the values don't match.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const proof = mptAlice.getConvertBackProof(
bob,
@@ -3496,8 +3492,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// different pedersen commitment. Verification fails because the
// submitted commitment doesn't match what the proof was generated for.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const badPedersenCommitment = mptAlice.getPedersenCommitment(1, pcBlindingFactor);
Buffer const proof = mptAlice.getConvertBackProof(
bob,
@@ -3526,8 +3521,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// sequence, issuanceID, amount, version). Using a different context hash
// makes the proof invalid for this transaction, preventing replay attacks.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
uint256 const badContextHash{1};
Buffer const pedersenProof = mptAlice.getBalanceLinkageProof(
bob,
@@ -3560,8 +3554,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// Test 6: Correct proof to verify the test setup is valid.
// All parameters are correct, so the transaction should succeed.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const proof = mptAlice.getConvertBackProof(
bob,
@@ -3675,8 +3668,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// commitment was created with (balance - amount). The verifier computes
// PC_rem = PC - amount*G and checks if the bulletproof matches, which fails.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const bulletproof = mptAlice.getBulletproof(
{1}, // wrong remaining balance
@@ -3701,8 +3693,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// commitment PC = (balance - amount)*G + rho*H. Using a different rho
// creates a commitment mismatch and verification fails.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const bulletproof = mptAlice.getBulletproof(
{*spendingBalance - amt},
@@ -3727,8 +3718,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// sequence, issuanceID, amount, version). Using a different context hash
// makes the proof invalid for this transaction, preventing replay attacks.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
uint256 const badContextHash{1};
Buffer const bulletproof = mptAlice.getBulletproof(
@@ -3752,8 +3742,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
// Test 4: Correct proof to verify the test setup is valid.
// All parameters are correct, so the transaction should succeed.
{
uint256 const contextHash =
getConvertBackContextHash(bob, env.seq(bob), mptAlice.issuanceID(), amt, version);
uint256 const contextHash = getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version);
Buffer const proof = mptAlice.getConvertBackProof(
bob,

View File

@@ -1057,7 +1057,7 @@ MPTTester::convert(MPTConvert const& arg)
// if fillSchnorrProof is explicitly set, follow its value;
// otherwise, default to generating the proof only if holder pub key is
// present.
auto const contextHash = getConvertContextHash(arg.account->id(), env_.seq(*arg.account), *id_, *arg.amt);
auto const contextHash = getConvertContextHash(arg.account->id(), *id_, env_.seq(*arg.account));
auto const proof = getSchnorrProof(*arg.account, contextHash);
if (proof)
@@ -1257,7 +1257,7 @@ MPTTester::send(MPTConfidentialSend const& arg)
{
auto const version = getMPTokenVersion(*arg.account);
auto const ctxHash =
getSendContextHash(arg.account->id(), env_.seq(*arg.account), *id_, arg.dest->id(), version);
getSendContextHash(arg.account->id(), *id_, env_.seq(*arg.account), arg.dest->id(), version);
auto const nRecipients = getConfidentialRecipientCount(auditorAmt.has_value());
std::vector<ConfidentialRecipient> recipients;
@@ -1427,7 +1427,7 @@ MPTTester::confidentialClaw(MPTConfidentialClawback const& arg)
else
{
std::uint32_t const seq = env_.seq(account);
uint256 const contextHash = getClawbackContextHash(account.id(), seq, *id_, *arg.amt, arg.holder->id());
uint256 const contextHash = getClawbackContextHash(account.id(), *id_, seq, arg.holder->id());
auto const privKey = getPrivKey(account);
if (!privKey || privKey->size() != ecPrivKeyLength)
@@ -1707,8 +1707,7 @@ MPTTester::convertBack(MPTConvertBack const& arg)
// if the caller generated ciphertexts themselves, they should also
// generate the proof themselves from the blinding factor
uint256 const contextHash =
getConvertBackContextHash(arg.account->id(), env_.seq(*arg.account), *id_, *arg.amt, version);
uint256 const contextHash = getConvertBackContextHash(arg.account->id(), *id_, env_.seq(*arg.account), version);
auto const prevEncryptedSpendingBalance = getEncryptedBalance(*arg.account, HOLDER_ENCRYPTED_SPENDING);
Buffer proof;

View File

@@ -83,7 +83,7 @@ ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx)
if (amount > (*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0))
return tecINSUFFICIENT_FUNDS;
auto const contextHash = getClawbackContextHash(account, ctx.tx[sfSequence], mptIssuanceID, amount, holder);
auto const contextHash = getClawbackContextHash(account, mptIssuanceID, ctx.tx.getSeqProxy().value(), holder);
// Verify the revealed confidential amount by the issuer matches the exact
// confidential balance of the holder.

View File

@@ -120,7 +120,7 @@ ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx)
{
holderPubKey = ctx.tx[sfHolderElGamalPublicKey];
auto const contextHash = getConvertContextHash(account, ctx.tx[sfSequence], issuanceID, amount);
auto const contextHash = getConvertContextHash(account, issuanceID, ctx.tx.getSeqProxy().value());
// when register new pk, verify through schnorr proof
if (!isTesSuccess(verifySchnorrProof(holderPubKey, ctx.tx[sfZKProof], contextHash)))

View File

@@ -69,7 +69,7 @@ verifyProofs(STTx const& tx, std::shared_ptr<SLE const> const& issuance, std::sh
auto const holderPubKey = (*mptoken)[sfHolderElGamalPublicKey];
auto const contextHash = getConvertBackContextHash(
account, tx[sfSequence], mptIssuanceID, amount, (*mptoken)[~sfConfidentialBalanceVersion].value_or(0));
account, mptIssuanceID, tx.getSeqProxy().value(), (*mptoken)[~sfConfidentialBalanceVersion].value_or(0));
// Prepare Auditor Info
std::optional<ConfidentialRecipient> auditor;

View File

@@ -132,8 +132,8 @@ verifySendProofs(
// Prepare the context hash
auto const contextHash = getSendContextHash(
ctx.tx[sfAccount],
ctx.tx[sfSequence],
ctx.tx[sfMPTokenIssuanceID],
ctx.tx.getSeqProxy().value(),
ctx.tx[sfDestination],
(*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));