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 { namespace RPC {
/** /**
Adds common synthetic fields to transaction-related JSON responses Adds common synthetic fields to transaction metadata JSON
@{ @{
*/ */
void void
insertNFTSyntheticInJson( insertNFTSyntheticInJson(
Json::Value&, Json::Value& metadata,
std::shared_ptr<STTx const> const&, std::shared_ptr<STTx const> const&,
TxMeta const&); TxMeta const&);
/** @} */ /** @} */

View File

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

View File

@@ -32,12 +32,12 @@ namespace RPC {
void void
insertNFTSyntheticInJson( insertNFTSyntheticInJson(
Json::Value& response, Json::Value& metadata,
std::shared_ptr<STTx const> const& transaction, std::shared_ptr<STTx const> const& transaction,
TxMeta const& transactionMeta) TxMeta const& transactionMeta)
{ {
insertNFTokenID(response[jss::meta], transaction, transactionMeta); insertNFTokenID(metadata, transaction, transactionMeta);
insertNFTokenOfferID(response[jss::meta], transaction, transactionMeta); insertNFTokenOfferID(metadata, transaction, transactionMeta);
} }
} // namespace RPC } // 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.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
env.close(); env.close();
Json::Value const meta =
env.rpc("tx", txHash)[jss::result][jss::meta];
// Expect nftokens_id field // Test 1: Check tx RPC response
if (!BEAST_EXPECT(meta.isMember(jss::nftoken_id))) 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; return;
// Check the value of NFT ID in the meta with the // Check the value of NFT ID matches
// actual value
uint256 nftID; uint256 nftID;
BEAST_EXPECT(nftID.parseHex(meta[jss::nftoken_id].asString())); BEAST_EXPECT(nftID.parseHex(txMeta[jss::nftoken_id].asString()));
BEAST_EXPECT(nftID == actualNftID); 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 // Verify `nftoken_ids` value equals to the NFTokenIDs that were
// changed in the most recent NFTokenCancelOffer transaction // changed in the most recent NFTokenCancelOffer transaction
auto verifyNFTokenIDsInCancelOffer = auto verifyNFTokenIDsInCancelOffer = [&](std::vector<uint256>
[&](std::vector<uint256> actualNftIDs) { actualNftIDs) {
// Get the hash for the most recent transaction. // Get the hash for the most recent transaction.
std::string const txHash{ std::string const txHash{
env.tx()->getJson(JsonOptions::none)[jss::hash].asString()}; env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
env.close(); 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 // 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; return;
// Convert NFT IDs from Json::Value to uint256 // Convert NFT IDs from Json::Value to uint256
std::vector<uint256> metaIDs; std::vector<uint256> metaIDs;
std::transform( std::transform(
meta[jss::nftoken_ids].begin(), txMeta[jss::nftoken_ids].begin(),
meta[jss::nftoken_ids].end(), txMeta[jss::nftoken_ids].end(),
std::back_inserter(metaIDs), std::back_inserter(metaIDs),
[this](Json::Value id) { [this](Json::Value id) {
uint256 nftID; uint256 nftID;
@@ -6947,6 +7024,104 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
// actual values // actual values
for (size_t i = 0; i < metaIDs.size(); ++i) for (size_t i = 0; i < metaIDs.size(); ++i)
BEAST_EXPECT(metaIDs[i] == actualNftIDs[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 // 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/DeliverMax.h>
#include <xrpld/app/misc/TxQ.h> #include <xrpld/app/misc/TxQ.h>
#include <xrpld/rpc/Context.h> #include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h> #include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/ApiVersion.h> #include <xrpl/protocol/ApiVersion.h>
#include <xrpl/protocol/NFTSyntheticSerializer.h>
#include <xrpl/protocol/jss.h> #include <xrpl/protocol/jss.h>
namespace ripple { namespace ripple {
@@ -150,19 +150,12 @@ fillJsonTx(
{ {
txJson[jss::meta] = stMeta->getJson(JsonOptions::none); txJson[jss::meta] = stMeta->getJson(JsonOptions::none);
// If applicable, insert delivered amount // Insert all synthetic fields
if (txnType == ttPAYMENT || txnType == ttCHECK_CASH) RPC::insertAllSyntheticInJson(
RPC::insertDeliveredAmount(
txJson[jss::meta], txJson[jss::meta],
fill.ledger, fill.ledger,
txn, txn,
{txn->getTransactionID(), fill.ledger.seq(), *stMeta}); {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()) if (!fill.ledger.open())
@@ -187,19 +180,12 @@ fillJsonTx(
{ {
txJson[jss::metaData] = stMeta->getJson(JsonOptions::none); txJson[jss::metaData] = stMeta->getJson(JsonOptions::none);
// If applicable, insert delivered amount // Insert all synthetic fields
if (txnType == ttPAYMENT || txnType == ttCHECK_CASH) RPC::insertAllSyntheticInJson(
RPC::insertDeliveredAmount(
txJson[jss::metaData], txJson[jss::metaData],
fill.ledger, fill.ledger,
txn, txn,
{txn->getTransactionID(), fill.ledger.seq(), *stMeta}); {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/perflog/PerfLog.h>
#include <xrpld/rpc/BookChanges.h> #include <xrpld/rpc/BookChanges.h>
#include <xrpld/rpc/CTID.h> #include <xrpld/rpc/CTID.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/ServerHandler.h> #include <xrpld/rpc/ServerHandler.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/basics/UptimeClock.h> #include <xrpl/basics/UptimeClock.h>
#include <xrpl/basics/mulDiv.h> #include <xrpl/basics/mulDiv.h>
@@ -3274,11 +3273,8 @@ NetworkOPsImp::transJson(
if (meta) if (meta)
{ {
jvObj[jss::meta] = meta->get().getJson(JsonOptions::none); jvObj[jss::meta] = meta->get().getJson(JsonOptions::none);
RPC::insertDeliveredAmount( RPC::insertAllSyntheticInJson(
jvObj[jss::meta], *ledger, transaction, meta->get()); 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 // add CTID where the needed data for it exists

View File

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

View File

@@ -283,9 +283,11 @@ protected:
static NotTEC static NotTEC
checkSign( checkSign(
PreclaimContext const& ctx, ReadView const& view,
AccountID const& id, ApplyFlags flags,
STObject const& sigObject); AccountID const& idAccount,
STObject const& sigObject,
beast::Journal const j);
// Base class always returns true // Base class always returns true
static bool static bool
@@ -323,15 +325,18 @@ private:
payFee(); payFee();
static NotTEC static NotTEC
checkSingleSign( checkSingleSign(
PreclaimContext const& ctx, ReadView const& view,
AccountID const& idSigner, AccountID const& idSigner,
AccountID const& idAccount, AccountID const& idAccount,
std::shared_ptr<SLE const> sleAccount); std::shared_ptr<SLE const> sleAccount,
beast::Journal const j);
static NotTEC static NotTEC
checkMultiSign( checkMultiSign(
PreclaimContext const& ctx, ReadView const& view,
ApplyFlags flags,
AccountID const& id, AccountID const& id,
STObject const& sigObject); STObject const& sigObject,
beast::Journal const j);
void trapTransaction(uint256) const; 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/misc/Transaction.h>
#include <xrpld/app/rdb/backend/SQLiteDatabase.h> #include <xrpld/app/rdb/backend/SQLiteDatabase.h>
#include <xrpld/rpc/Context.h> #include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/Role.h> #include <xrpld/rpc/Role.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/json/json_value.h> #include <xrpl/json/json_value.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
@@ -346,11 +345,8 @@ populateJsonResponse(
{ {
jvObj[jss::meta] = jvObj[jss::meta] =
txnMeta->getJson(JsonOptions::include_date); txnMeta->getJson(JsonOptions::include_date);
insertDeliveredAmount( RPC::insertAllSyntheticInJson(
jvObj[jss::meta], context, txn, *txnMeta); jvObj[jss::meta], context, sttx, *txnMeta);
RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
RPC::insertMPTokenIssuanceID(
jvObj[jss::meta], sttx, *txnMeta);
} }
else else
UNREACHABLE( UNREACHABLE(

View File

@@ -27,6 +27,7 @@
#include <xrpld/rpc/DeliveredAmount.h> #include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/GRPCHandlers.h> #include <xrpld/rpc/GRPCHandlers.h>
#include <xrpld/rpc/MPTokenIssuanceID.h> #include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpld/rpc/detail/TransactionSign.h> #include <xrpld/rpc/detail/TransactionSign.h>
#include <xrpl/protocol/ErrorCodes.h> #include <xrpl/protocol/ErrorCodes.h>
@@ -275,17 +276,11 @@ simulateTxn(RPC::JsonContext& context, std::shared_ptr<Transaction> transaction)
else else
{ {
jvResult[jss::meta] = result.metadata->getJson(JsonOptions::none); jvResult[jss::meta] = result.metadata->getJson(JsonOptions::none);
RPC::insertDeliveredAmount( RPC::insertAllSyntheticInJson(
jvResult[jss::meta], jvResult[jss::meta],
view, view,
transaction->getSTransaction(), transaction->getSTransaction(),
*result.metadata); *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/app/rdb/RelationalDatabase.h>
#include <xrpld/rpc/CTID.h> #include <xrpld/rpc/CTID.h>
#include <xrpld/rpc/Context.h> #include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/GRPCHandlers.h> #include <xrpld/rpc/GRPCHandlers.h>
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/detail/RPCHelpers.h> #include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/basics/ToString.h> #include <xrpl/basics/ToString.h>
#include <xrpl/protocol/ErrorCodes.h> #include <xrpl/protocol/ErrorCodes.h>
@@ -268,10 +267,8 @@ populateJsonResponse(
if (meta) if (meta)
{ {
response[jss::meta] = meta->getJson(JsonOptions::none); response[jss::meta] = meta->getJson(JsonOptions::none);
insertDeliveredAmount( RPC::insertAllSyntheticInJson(
response[jss::meta], context, result.txn, *meta); response[jss::meta], context, sttx, *meta);
RPC::insertNFTSyntheticInJson(response, sttx, *meta);
RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
} }
} }
response[jss::validated] = result.validated; response[jss::validated] = result.validated;