Compare commits

..

27 Commits

Author SHA1 Message Date
Mayukha Vadari
ec01e14e6c Merge branch 'develop' into copilot/fix-5adea215-d850-4ab8-a595-b04e63e948a6 2025-10-06 11:57:21 -04:00
Bronek Kozicki
5d79bfc531 Remove bogus coverage warning (#5838) 2025-10-02 11:54:09 +01:00
Ed Hennis
51ef35ab55 fix: Transaction sig checking functions do not get a full context (#5829)
Fixes a (currently harmless) bug introduced by PR #5594
2025-10-01 20:58:43 +00:00
Valentin Balaschenko
330a3215bc fix: FD/handle guarding + exponential backoff (#5823) 2025-10-01 12:57:33 +01:00
Mayukha Vadari
3bb3f1b31d Merge branch 'develop' into copilot/fix-5adea215-d850-4ab8-a595-b04e63e948a6 2025-09-23 15:04:11 -04:00
copilot-swe-agent[bot]
4063ccaa09 refactor: replace individual synthetic field functions with insertAllSyntheticInJson helper in simulate RPC
Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
2025-09-11 14:27:29 +00:00
Mayukha Vadari
bb7042388a Merge branch 'develop' into copilot/fix-5adea215-d850-4ab8-a595-b04e63e948a6 2025-09-11 10:20:58 -04:00
Mayukha Vadari
ee3cba2fd9 pre-commit fixes 2025-09-10 11:48:35 -04:00
Mayukha Vadari
539074bdcf [Claude] fix tests 2025-09-10 11:48:35 -04:00
Mayukha Vadari
7e2dcbd221 remove folder structure file 2025-09-10 11:48:35 -04:00
Mayukha Vadari
e79416bc06 remove copilot-setup-steps 2025-09-10 11:48:35 -04:00
Mayukha Vadari
45fdb1b108 [Claude] replace with a helper function 2025-09-10 11:48:35 -04:00
Mayukha Vadari
de84c82d0e [Claude] simplify tests 2025-09-10 11:48:35 -04:00
Mayukha Vadari
376bca2866 [Claude] get tests working 2025-09-10 11:48:34 -04:00
Mayukha Vadari
649fd105f8 [Claude] add folder structure document 2025-09-10 11:48:34 -04:00
copilot-swe-agent[bot]
c491adc46b Fix NFT test failures by handling API version metadata field differences
Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
2025-09-10 11:48:34 -04:00
Mayukha Vadari
ac16c83a9f fix push so it doesn't run twice 2025-09-10 11:48:34 -04:00
Mayukha Vadari
f6d15f3d16 try again 2025-09-10 11:48:34 -04:00
Mayukha Vadari
a8d5d995e1 actual setup steps 2025-09-10 11:48:34 -04:00
Mayukha Vadari
3d8c1a3e58 oops 2025-09-10 11:48:34 -04:00
Mayukha Vadari
2f7cbc4f57 messed up the file 2025-09-10 11:48:34 -04:00
Mayukha Vadari
2e4b30dab5 try adding copilot-setup-steps 2025-09-10 11:48:34 -04:00
copilot-swe-agent[bot]
14ff58aaaa Fix NFT synthetic field insertion for different API versions
Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
2025-09-10 11:48:34 -04:00
copilot-swe-agent[bot]
72bb19c1a3 Fix clang-format violations
Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
2025-09-10 11:48:34 -04:00
copilot-swe-agent[bot]
e26065176b Update NFToken test helpers to include ledger and account_tx RPC checks
Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
2025-09-10 11:48:34 -04:00
copilot-swe-agent[bot]
ed7167c116 Fix missing nftoken_id in ledger RPC response
Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
2025-09-10 11:48:34 -04:00
copilot-swe-agent[bot]
2ccd1082f5 Initial plan 2025-09-10 11:48:34 -04:00
13 changed files with 408 additions and 138 deletions

View File

@@ -31,13 +31,13 @@ namespace ripple {
namespace RPC {
/**
Adds common synthetic fields to transaction-related JSON responses
Adds common synthetic fields to transaction metadata JSON
@{
*/
void
insertNFTSyntheticInJson(
Json::Value&,
Json::Value& metadata,
std::shared_ptr<STTx const> const&,
TxMeta const&);
/** @} */

View File

@@ -3126,7 +3126,7 @@ rippleUnlockEscrowMPT(
{ // LCOV_EXCL_START
JLOG(j.error())
<< "rippleUnlockEscrowMPT: MPToken not found for " << receiver;
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
return tecOBJECT_NOT_FOUND;
} // LCOV_EXCL_STOP
auto current = sle->getFieldU64(sfMPTAmount);

View File

@@ -32,12 +32,12 @@ namespace RPC {
void
insertNFTSyntheticInJson(
Json::Value& response,
Json::Value& metadata,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta)
{
insertNFTokenID(response[jss::meta], transaction, transactionMeta);
insertNFTokenOfferID(response[jss::meta], transaction, transactionMeta);
insertNFTokenID(metadata, transaction, transactionMeta);
insertNFTokenOfferID(metadata, transaction, transactionMeta);
}
} // namespace RPC

View File

@@ -6894,41 +6894,118 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
env.close();
Json::Value const meta =
env.rpc("tx", txHash)[jss::result][jss::meta];
// Expect nftokens_id field
if (!BEAST_EXPECT(meta.isMember(jss::nftoken_id)))
// Test 1: Check tx RPC response
Json::Value const txResult = env.rpc("tx", txHash)[jss::result];
Json::Value const& txMeta = txResult[jss::meta];
// Expect nftoken_id field
if (!BEAST_EXPECT(txMeta.isMember(jss::nftoken_id)))
return;
// Check the value of NFT ID in the meta with the
// actual value
// Check the value of NFT ID matches
uint256 nftID;
BEAST_EXPECT(nftID.parseHex(meta[jss::nftoken_id].asString()));
BEAST_EXPECT(nftID.parseHex(txMeta[jss::nftoken_id].asString()));
BEAST_EXPECT(nftID == actualNftID);
// Get ledger sequence from tx response
auto const ledgerSeq = txResult[jss::ledger_index].asUInt();
// Test 2: Check ledger RPC response with expanded transactions
Json::Value ledgerParams;
ledgerParams[jss::ledger_index] = ledgerSeq;
ledgerParams[jss::transactions] = true;
ledgerParams[jss::expand] = true;
auto const ledgerResult =
env.rpc("json", "ledger", to_string(ledgerParams));
auto const& tx =
ledgerResult[jss::result][jss::ledger][jss::transactions][0u];
// Verify transaction hash matches
BEAST_EXPECT(tx[jss::hash].asString() == txHash);
// Check synthetic fields in ledger response (this tests our
// LedgerToJson.cpp fix)
Json::Value const* meta = nullptr;
if (tx.isMember(jss::meta))
meta = &tx[jss::meta];
else if (tx.isMember(jss::metaData))
meta = &tx[jss::metaData];
if (BEAST_EXPECT(meta != nullptr))
{
BEAST_EXPECT(meta->isMember(jss::nftoken_id));
if (meta->isMember(jss::nftoken_id))
{
uint256 ledgerNftId;
BEAST_EXPECT(ledgerNftId.parseHex(
(*meta)[jss::nftoken_id].asString()));
BEAST_EXPECT(ledgerNftId == actualNftID);
}
}
// Test 3: Check account_tx RPC response
Json::Value accountTxParams;
accountTxParams[jss::account] = alice.human();
accountTxParams[jss::limit] = 1;
auto const accountTxResult =
env.rpc("json", "account_tx", to_string(accountTxParams));
auto const& accountTx =
accountTxResult[jss::result][jss::transactions][0u];
// Check if the latest transaction is ours (it should be, but
// account_tx can be ordering-dependent)
bool isOurTransaction = (accountTx[jss::hash].asString() == txHash);
// Only check synthetic fields if this is our transaction
if (isOurTransaction)
{
// Check synthetic fields in account_tx response
Json::Value const* accountMeta = nullptr;
if (accountTx.isMember(jss::meta))
accountMeta = &accountTx[jss::meta];
else if (accountTx.isMember(jss::metaData))
accountMeta = &accountTx[jss::metaData];
if (BEAST_EXPECT(accountMeta != nullptr))
{
BEAST_EXPECT(accountMeta->isMember(jss::nftoken_id));
if (accountMeta->isMember(jss::nftoken_id))
{
uint256 accountNftId;
BEAST_EXPECT(accountNftId.parseHex(
(*accountMeta)[jss::nftoken_id].asString()));
BEAST_EXPECT(accountNftId == actualNftID);
}
}
}
};
// Verify `nftoken_ids` value equals to the NFTokenIDs that were
// changed in the most recent NFTokenCancelOffer transaction
auto verifyNFTokenIDsInCancelOffer =
[&](std::vector<uint256> actualNftIDs) {
auto verifyNFTokenIDsInCancelOffer = [&](std::vector<uint256>
actualNftIDs) {
// Get the hash for the most recent transaction.
std::string const txHash{
env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
env.close();
Json::Value const meta =
env.rpc("tx", txHash)[jss::result][jss::meta];
// Test 1: Check tx RPC response
Json::Value const txResult = env.rpc("tx", txHash)[jss::result];
Json::Value const& txMeta = txResult[jss::meta];
// Expect nftokens_ids field and verify the values
if (!BEAST_EXPECT(meta.isMember(jss::nftoken_ids)))
if (!BEAST_EXPECT(txMeta.isMember(jss::nftoken_ids)))
return;
// Convert NFT IDs from Json::Value to uint256
std::vector<uint256> metaIDs;
std::transform(
meta[jss::nftoken_ids].begin(),
meta[jss::nftoken_ids].end(),
txMeta[jss::nftoken_ids].begin(),
txMeta[jss::nftoken_ids].end(),
std::back_inserter(metaIDs),
[this](Json::Value id) {
uint256 nftID;
@@ -6947,6 +7024,104 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
// actual values
for (size_t i = 0; i < metaIDs.size(); ++i)
BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
// Get ledger sequence from tx response
auto const ledgerSeq = txResult[jss::ledger_index].asUInt();
// Test 2: Check ledger RPC response with expanded transactions
Json::Value ledgerParams;
ledgerParams[jss::ledger_index] = ledgerSeq;
ledgerParams[jss::transactions] = true;
ledgerParams[jss::expand] = true;
auto const ledgerResult =
env.rpc("json", "ledger", to_string(ledgerParams));
auto const& tx =
ledgerResult[jss::result][jss::ledger][jss::transactions][0u];
// Verify transaction hash matches
BEAST_EXPECT(tx[jss::hash].asString() == txHash);
// Check synthetic fields in ledger response
Json::Value const* meta = nullptr;
if (tx.isMember(jss::meta))
meta = &tx[jss::meta];
else if (tx.isMember(jss::metaData))
meta = &tx[jss::metaData];
if (BEAST_EXPECT(meta != nullptr))
{
BEAST_EXPECT(meta->isMember(jss::nftoken_ids));
if (meta->isMember(jss::nftoken_ids))
{
// Convert and verify NFT IDs in ledger response
std::vector<uint256> ledgerMetaIDs;
std::transform(
(*meta)[jss::nftoken_ids].begin(),
(*meta)[jss::nftoken_ids].end(),
std::back_inserter(ledgerMetaIDs),
[this](Json::Value id) {
uint256 nftID;
BEAST_EXPECT(nftID.parseHex(id.asString()));
return nftID;
});
std::sort(ledgerMetaIDs.begin(), ledgerMetaIDs.end());
BEAST_EXPECT(ledgerMetaIDs.size() == actualNftIDs.size());
for (size_t i = 0; i < ledgerMetaIDs.size(); ++i)
BEAST_EXPECT(ledgerMetaIDs[i] == actualNftIDs[i]);
}
}
// Test 3: Check account_tx RPC response
Json::Value accountTxParams;
accountTxParams[jss::account] = alice.human();
accountTxParams[jss::limit] = 1;
auto const accountTxResult =
env.rpc("json", "account_tx", to_string(accountTxParams));
auto const& accountTx =
accountTxResult[jss::result][jss::transactions][0u];
// Check if the latest transaction is ours (it should be, but
// account_tx can be ordering-dependent)
bool isOurTransaction = (accountTx[jss::hash].asString() == txHash);
// Only check synthetic fields if this is our transaction
if (isOurTransaction)
{
// Check synthetic fields in account_tx response
Json::Value const* accountMeta = nullptr;
if (accountTx.isMember(jss::meta))
accountMeta = &accountTx[jss::meta];
else if (accountTx.isMember(jss::metaData))
accountMeta = &accountTx[jss::metaData];
if (BEAST_EXPECT(accountMeta != nullptr))
{
BEAST_EXPECT(accountMeta->isMember(jss::nftoken_ids));
if (accountMeta->isMember(jss::nftoken_ids))
{
// Convert and verify NFT IDs in account_tx response
std::vector<uint256> accountMetaIDs;
std::transform(
(*accountMeta)[jss::nftoken_ids].begin(),
(*accountMeta)[jss::nftoken_ids].end(),
std::back_inserter(accountMetaIDs),
[this](Json::Value id) {
uint256 nftID;
BEAST_EXPECT(nftID.parseHex(id.asString()));
return nftID;
});
std::sort(accountMetaIDs.begin(), accountMetaIDs.end());
BEAST_EXPECT(
accountMetaIDs.size() == actualNftIDs.size());
for (size_t i = 0; i < accountMetaIDs.size(); ++i)
BEAST_EXPECT(accountMetaIDs[i] == actualNftIDs[i]);
}
}
}
};
// Verify `offer_id` value equals to the offerID that was

View File

@@ -22,11 +22,11 @@
#include <xrpld/app/misc/DeliverMax.h>
#include <xrpld/app/misc/TxQ.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/ApiVersion.h>
#include <xrpl/protocol/NFTSyntheticSerializer.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
@@ -150,19 +150,12 @@ fillJsonTx(
{
txJson[jss::meta] = stMeta->getJson(JsonOptions::none);
// If applicable, insert delivered amount
if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
RPC::insertDeliveredAmount(
// Insert all synthetic fields
RPC::insertAllSyntheticInJson(
txJson[jss::meta],
fill.ledger,
txn,
{txn->getTransactionID(), fill.ledger.seq(), *stMeta});
// If applicable, insert mpt issuance id
RPC::insertMPTokenIssuanceID(
txJson[jss::meta],
txn,
{txn->getTransactionID(), fill.ledger.seq(), *stMeta});
}
if (!fill.ledger.open())
@@ -187,19 +180,12 @@ fillJsonTx(
{
txJson[jss::metaData] = stMeta->getJson(JsonOptions::none);
// If applicable, insert delivered amount
if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
RPC::insertDeliveredAmount(
// Insert all synthetic fields
RPC::insertAllSyntheticInJson(
txJson[jss::metaData],
fill.ledger,
txn,
{txn->getTransactionID(), fill.ledger.seq(), *stMeta});
// If applicable, insert mpt issuance id
RPC::insertMPTokenIssuanceID(
txJson[jss::metaData],
txn,
{txn->getTransactionID(), fill.ledger.seq(), *stMeta});
}
}

View File

@@ -49,9 +49,8 @@
#include <xrpld/perflog/PerfLog.h>
#include <xrpld/rpc/BookChanges.h>
#include <xrpld/rpc/CTID.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/ServerHandler.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/basics/UptimeClock.h>
#include <xrpl/basics/mulDiv.h>
@@ -3274,11 +3273,8 @@ NetworkOPsImp::transJson(
if (meta)
{
jvObj[jss::meta] = meta->get().getJson(JsonOptions::none);
RPC::insertDeliveredAmount(
RPC::insertAllSyntheticInJson(
jvObj[jss::meta], *ledger, transaction, meta->get());
RPC::insertNFTSyntheticInJson(jvObj, transaction, meta->get());
RPC::insertMPTokenIssuanceID(
jvObj[jss::meta], transaction, meta->get());
}
// add CTID where the needed data for it exists

View File

@@ -660,14 +660,15 @@ Transactor::apply()
NotTEC
Transactor::checkSign(
PreclaimContext const& ctx,
ReadView const& view,
ApplyFlags flags,
AccountID const& idAccount,
STObject const& sigObject)
STObject const& sigObject,
beast::Journal const j)
{
auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey);
// Ignore signature check on batch inner transactions
if (sigObject.isFlag(tfInnerBatchTxn) &&
ctx.view.rules().enabled(featureBatch))
if (sigObject.isFlag(tfInnerBatchTxn) && view.rules().enabled(featureBatch))
{
// Defensive Check: These values are also checked in Batch::preflight
if (sigObject.isFieldPresent(sfTxnSignature) || !pkSigner.empty() ||
@@ -678,7 +679,7 @@ Transactor::checkSign(
return tesSUCCESS;
}
if ((ctx.flags & tapDRY_RUN) && pkSigner.empty() &&
if ((flags & tapDRY_RUN) && pkSigner.empty() &&
!sigObject.isFieldPresent(sfSigners))
{
// simulate: skip signature validation when neither SigningPubKey nor
@@ -688,9 +689,9 @@ Transactor::checkSign(
// If the pk is empty and not simulate or simulate and signers,
// then we must be multi-signing.
if (ctx.tx.isFieldPresent(sfSigners))
if (sigObject.isFieldPresent(sfSigners))
{
return checkMultiSign(ctx, idAccount, sigObject);
return checkMultiSign(view, flags, idAccount, sigObject, j);
}
// Check Single Sign
@@ -699,7 +700,7 @@ Transactor::checkSign(
if (!publicKeyType(makeSlice(pkSigner)))
{
JLOG(ctx.j.trace()) << "checkSign: signing public key type is unknown";
JLOG(j.trace()) << "checkSign: signing public key type is unknown";
return tefBAD_AUTH; // FIXME: should be better error!
}
@@ -707,11 +708,11 @@ Transactor::checkSign(
auto const idSigner = pkSigner.empty()
? idAccount
: calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const sleAccount = ctx.view.read(keylet::account(idAccount));
auto const sleAccount = view.read(keylet::account(idAccount));
if (!sleAccount)
return terNO_ACCOUNT;
return checkSingleSign(ctx, idSigner, idAccount, sleAccount);
return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
}
NotTEC
@@ -720,7 +721,7 @@ Transactor::checkSign(PreclaimContext const& ctx)
auto const idAccount = ctx.tx.isFieldPresent(sfDelegate)
? ctx.tx.getAccountID(sfDelegate)
: ctx.tx.getAccountID(sfAccount);
return checkSign(ctx, idAccount, ctx.tx);
return checkSign(ctx.view, ctx.flags, idAccount, ctx.tx, ctx.j);
}
NotTEC
@@ -735,7 +736,8 @@ Transactor::checkBatchSign(PreclaimContext const& ctx)
Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey);
if (pkSigner.empty())
{
if (ret = checkMultiSign(ctx, idAccount, signer);
if (ret = checkMultiSign(
ctx.view, ctx.flags, idAccount, signer, ctx.j);
!isTesSuccess(ret))
return ret;
}
@@ -759,7 +761,8 @@ Transactor::checkBatchSign(PreclaimContext const& ctx)
return tesSUCCESS;
}
if (ret = checkSingleSign(ctx, idSigner, idAccount, sleAccount);
if (ret = checkSingleSign(
ctx.view, idSigner, idAccount, sleAccount, ctx.j);
!isTesSuccess(ret))
return ret;
}
@@ -769,14 +772,15 @@ Transactor::checkBatchSign(PreclaimContext const& ctx)
NotTEC
Transactor::checkSingleSign(
PreclaimContext const& ctx,
ReadView const& view,
AccountID const& idSigner,
AccountID const& idAccount,
std::shared_ptr<SLE const> sleAccount)
std::shared_ptr<SLE const> sleAccount,
beast::Journal const j)
{
bool const isMasterDisabled = sleAccount->isFlag(lsfDisableMaster);
if (ctx.view.rules().enabled(fixMasterKeyAsRegularKey))
if (view.rules().enabled(fixMasterKeyAsRegularKey))
{
// Signed with regular key.
if ((*sleAccount)[~sfRegularKey] == idSigner)
@@ -813,16 +817,14 @@ Transactor::checkSingleSign(
else if (sleAccount->isFieldPresent(sfRegularKey))
{
// Signing key does not match master or regular key.
JLOG(ctx.j.trace())
<< "checkSingleSign: Not authorized to use account.";
JLOG(j.trace()) << "checkSingleSign: Not authorized to use account.";
return tefBAD_AUTH;
}
else
{
// No regular key on account and signing key does not match master key.
// FIXME: Why differentiate this case from tefBAD_AUTH?
JLOG(ctx.j.trace())
<< "checkSingleSign: Not authorized to use account.";
JLOG(j.trace()) << "checkSingleSign: Not authorized to use account.";
return tefBAD_AUTH_MASTER;
}
@@ -831,17 +833,19 @@ Transactor::checkSingleSign(
NotTEC
Transactor::checkMultiSign(
PreclaimContext const& ctx,
ReadView const& view,
ApplyFlags flags,
AccountID const& id,
STObject const& sigObject)
STObject const& sigObject,
beast::Journal const j)
{
// Get id's SignerList and Quorum.
std::shared_ptr<STLedgerEntry const> sleAccountSigners =
ctx.view.read(keylet::signers(id));
view.read(keylet::signers(id));
// If the signer list doesn't exist the account is not multi-signing.
if (!sleAccountSigners)
{
JLOG(ctx.j.trace())
JLOG(j.trace())
<< "applyTransaction: Invalid: Not a multi-signing account.";
return tefNOT_MULTI_SIGNING;
}
@@ -856,7 +860,7 @@ Transactor::checkMultiSign(
"ripple::Transactor::checkMultiSign : signer list ID is 0");
auto accountSigners =
SignerEntries::deserialize(*sleAccountSigners, ctx.j, "ledger");
SignerEntries::deserialize(*sleAccountSigners, j, "ledger");
if (!accountSigners)
return accountSigners.error();
@@ -880,7 +884,7 @@ Transactor::checkMultiSign(
{
if (++iter == accountSigners->end())
{
JLOG(ctx.j.trace())
JLOG(j.trace())
<< "applyTransaction: Invalid SigningAccount.Account.";
return tefBAD_SIGNATURE;
}
@@ -888,7 +892,7 @@ Transactor::checkMultiSign(
if (iter->account != txSignerAcctID)
{
// The SigningAccount is not in the SignerEntries.
JLOG(ctx.j.trace())
JLOG(j.trace())
<< "applyTransaction: Invalid SigningAccount.Account.";
return tefBAD_SIGNATURE;
}
@@ -902,13 +906,13 @@ Transactor::checkMultiSign(
// STTx::checkMultiSign
if (!spk.empty() && !publicKeyType(makeSlice(spk)))
{
JLOG(ctx.j.trace())
JLOG(j.trace())
<< "checkMultiSign: signing public key type is unknown";
return tefBAD_SIGNATURE;
}
XRPL_ASSERT(
(ctx.flags & tapDRY_RUN) || !spk.empty(),
(flags & tapDRY_RUN) || !spk.empty(),
"ripple::Transactor::checkMultiSign : non-empty signer or "
"simulation");
AccountID const signingAcctIDFromPubKey = spk.empty()
@@ -940,8 +944,7 @@ Transactor::checkMultiSign(
// In any of these cases we need to know whether the account is in
// the ledger. Determine that now.
auto const sleTxSignerRoot =
ctx.view.read(keylet::account(txSignerAcctID));
auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID));
if (signingAcctIDFromPubKey == txSignerAcctID)
{
@@ -954,7 +957,7 @@ Transactor::checkMultiSign(
if (signerAccountFlags & lsfDisableMaster)
{
JLOG(ctx.j.trace())
JLOG(j.trace())
<< "applyTransaction: Signer:Account lsfDisableMaster.";
return tefMASTER_DISABLED;
}
@@ -966,21 +969,21 @@ Transactor::checkMultiSign(
// Public key must hash to the account's regular key.
if (!sleTxSignerRoot)
{
JLOG(ctx.j.trace()) << "applyTransaction: Non-phantom signer "
JLOG(j.trace()) << "applyTransaction: Non-phantom signer "
"lacks account root.";
return tefBAD_SIGNATURE;
}
if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
{
JLOG(ctx.j.trace())
JLOG(j.trace())
<< "applyTransaction: Account lacks RegularKey.";
return tefBAD_SIGNATURE;
}
if (signingAcctIDFromPubKey !=
sleTxSignerRoot->getAccountID(sfRegularKey))
{
JLOG(ctx.j.trace())
JLOG(j.trace())
<< "applyTransaction: Account doesn't match RegularKey.";
return tefBAD_SIGNATURE;
}
@@ -992,8 +995,7 @@ Transactor::checkMultiSign(
// Cannot perform transaction if quorum is not met.
if (weightSum < sleAccountSigners->getFieldU32(sfSignerQuorum))
{
JLOG(ctx.j.trace())
<< "applyTransaction: Signers failed to meet quorum.";
JLOG(j.trace()) << "applyTransaction: Signers failed to meet quorum.";
return tefBAD_QUORUM;
}

View File

@@ -283,9 +283,11 @@ protected:
static NotTEC
checkSign(
PreclaimContext const& ctx,
AccountID const& id,
STObject const& sigObject);
ReadView const& view,
ApplyFlags flags,
AccountID const& idAccount,
STObject const& sigObject,
beast::Journal const j);
// Base class always returns true
static bool
@@ -323,15 +325,18 @@ private:
payFee();
static NotTEC
checkSingleSign(
PreclaimContext const& ctx,
ReadView const& view,
AccountID const& idSigner,
AccountID const& idAccount,
std::shared_ptr<SLE const> sleAccount);
std::shared_ptr<SLE const> sleAccount,
beast::Journal const j);
static NotTEC
checkMultiSign(
PreclaimContext const& ctx,
ReadView const& view,
ApplyFlags flags,
AccountID const& id,
STObject const& sigObject);
STObject const& sigObject,
beast::Journal const j);
void trapTransaction(uint256) const;

View File

@@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/NFTSyntheticSerializer.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
namespace RPC {
void
insertAllSyntheticInJson(
Json::Value& metadata,
ReadView const& ledger,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta)
{
insertDeliveredAmount(metadata, ledger, transaction, transactionMeta);
insertNFTSyntheticInJson(metadata, transaction, transactionMeta);
insertMPTokenIssuanceID(metadata, transaction, transactionMeta);
}
void
insertAllSyntheticInJson(
Json::Value& metadata,
JsonContext const& context,
std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta)
{
insertDeliveredAmount(metadata, context, transaction, transactionMeta);
insertNFTSyntheticInJson(metadata, transaction, transactionMeta);
insertMPTokenIssuanceID(metadata, transaction, transactionMeta);
}
} // namespace RPC
} // namespace ripple

View File

@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_RPC_DETAIL_SYNTHETICFIELDS_H_INCLUDED
#define RIPPLE_RPC_DETAIL_SYNTHETICFIELDS_H_INCLUDED
#include <xrpl/json/json_forwards.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TxMeta.h>
#include <memory>
namespace ripple {
class ReadView;
namespace RPC {
struct JsonContext;
/**
Adds all synthetic fields to transaction metadata JSON.
This includes delivered amount, NFT synthetic fields, and MPToken issuance
ID.
@{
*/
void
insertAllSyntheticInJson(
Json::Value& metadata,
ReadView const&,
std::shared_ptr<STTx const> const&,
TxMeta const&);
void
insertAllSyntheticInJson(
Json::Value& metadata,
JsonContext const&,
std::shared_ptr<STTx const> const&,
TxMeta const&);
/** @} */
} // namespace RPC
} // namespace ripple
#endif

View File

@@ -23,9 +23,8 @@
#include <xrpld/app/misc/Transaction.h>
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/ReadView.h>
@@ -346,11 +345,8 @@ populateJsonResponse(
{
jvObj[jss::meta] =
txnMeta->getJson(JsonOptions::include_date);
insertDeliveredAmount(
jvObj[jss::meta], context, txn, *txnMeta);
RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
RPC::insertMPTokenIssuanceID(
jvObj[jss::meta], sttx, *txnMeta);
RPC::insertAllSyntheticInJson(
jvObj[jss::meta], context, sttx, *txnMeta);
}
else
UNREACHABLE(

View File

@@ -27,6 +27,7 @@
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/GRPCHandlers.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpld/rpc/detail/TransactionSign.h>
#include <xrpl/protocol/ErrorCodes.h>
@@ -275,17 +276,11 @@ simulateTxn(RPC::JsonContext& context, std::shared_ptr<Transaction> transaction)
else
{
jvResult[jss::meta] = result.metadata->getJson(JsonOptions::none);
RPC::insertDeliveredAmount(
RPC::insertAllSyntheticInJson(
jvResult[jss::meta],
view,
transaction->getSTransaction(),
*result.metadata);
RPC::insertNFTSyntheticInJson(
jvResult, transaction->getSTransaction(), *result.metadata);
RPC::insertMPTokenIssuanceID(
jvResult[jss::meta],
transaction->getSTransaction(),
*result.metadata);
}
}

View File

@@ -25,10 +25,9 @@
#include <xrpld/app/rdb/RelationalDatabase.h>
#include <xrpld/rpc/CTID.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/GRPCHandlers.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/basics/ToString.h>
#include <xrpl/protocol/ErrorCodes.h>
@@ -268,10 +267,8 @@ populateJsonResponse(
if (meta)
{
response[jss::meta] = meta->getJson(JsonOptions::none);
insertDeliveredAmount(
response[jss::meta], context, result.txn, *meta);
RPC::insertNFTSyntheticInJson(response, sttx, *meta);
RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
RPC::insertAllSyntheticInJson(
response[jss::meta], context, sttx, *meta);
}
}
response[jss::validated] = result.validated;