mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Compare commits
20 Commits
bthomee/lo
...
mvadari/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
855f9141a3 | ||
|
|
712416e597 | ||
|
|
6edcc3547f | ||
|
|
a15bac0d34 | ||
|
|
ac68086bca | ||
|
|
ec0b68437b | ||
|
|
4906bd24a1 | ||
|
|
721d7b01a0 | ||
|
|
fb667a79b8 | ||
|
|
abba516f04 | ||
|
|
58e50d308f | ||
|
|
311b618068 | ||
|
|
3d11d3e10d | ||
|
|
02455f9bfc | ||
|
|
b2fb7382a8 | ||
|
|
64b53d6890 | ||
|
|
d8f11a9c17 | ||
|
|
ceeff478f4 | ||
|
|
a149cc944a | ||
|
|
c4c76e2aaf |
@@ -229,6 +229,19 @@ public:
|
||||
std::shared_ptr<Serializer const> const& txn,
|
||||
std::shared_ptr<Serializer const> const& metaData) override;
|
||||
|
||||
// Insert the transaction, and return the hash of the SHAMap leaf node
|
||||
// holding the transaction. The hash can be used to fetch the transaction
|
||||
// directly, instead of traversing the SHAMap
|
||||
// @param key transaction ID
|
||||
// @param txn transaction
|
||||
// @param metaData transaction metadata
|
||||
// @return hash of SHAMap leaf node that holds the transaction
|
||||
uint256
|
||||
rawTxInsertWithHash(
|
||||
uint256 const& key,
|
||||
std::shared_ptr<Serializer const> const& txn,
|
||||
std::shared_ptr<Serializer const> const& metaData);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -360,8 +361,13 @@ private:
|
||||
|
||||
TER
|
||||
consumeSeqProxy(SLE::pointer const& sleAccount);
|
||||
|
||||
TER
|
||||
payFee();
|
||||
|
||||
std::tuple<TER, XRPAmount, bool>
|
||||
processPersistentChanges(TER result, XRPAmount fee);
|
||||
|
||||
static NotTEC
|
||||
checkSingleSign(
|
||||
ReadView const& view,
|
||||
@@ -369,6 +375,7 @@ private:
|
||||
AccountID const& idAccount,
|
||||
std::shared_ptr<SLE const> sleAccount,
|
||||
beast::Journal const j);
|
||||
|
||||
static NotTEC
|
||||
checkMultiSign(
|
||||
ReadView const& view,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
@@ -30,6 +31,7 @@
|
||||
#include <xrpl/protocol/Seed.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/shamap/Family.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
#include <xrpl/shamap/SHAMapItem.h>
|
||||
@@ -41,6 +43,7 @@
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -490,14 +493,14 @@ void
|
||||
Ledger::rawErase(std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
if (!stateMap_.delItem(sle->key()))
|
||||
LogicError("Ledger::rawErase: key not found");
|
||||
Throw<std::logic_error>("Ledger::rawErase: key not found");
|
||||
}
|
||||
|
||||
void
|
||||
Ledger::rawErase(uint256 const& key)
|
||||
{
|
||||
if (!stateMap_.delItem(key))
|
||||
LogicError("Ledger::rawErase: key not found");
|
||||
Throw<std::logic_error>("Ledger::rawErase: key not found");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -507,7 +510,7 @@ Ledger::rawInsert(std::shared_ptr<SLE> const& sle)
|
||||
sle->add(ss);
|
||||
if (!stateMap_.addGiveItem(
|
||||
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
|
||||
LogicError("Ledger::rawInsert: key already exists");
|
||||
Throw<std::logic_error>("Ledger::rawInsert: key already exists");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -517,7 +520,7 @@ Ledger::rawReplace(std::shared_ptr<SLE> const& sle)
|
||||
sle->add(ss);
|
||||
if (!stateMap_.updateGiveItem(
|
||||
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
|
||||
LogicError("Ledger::rawReplace: key not found");
|
||||
Throw<std::logic_error>("Ledger::rawReplace: key not found");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -533,7 +536,27 @@ Ledger::rawTxInsert(
|
||||
s.addVL(txn->peekData());
|
||||
s.addVL(metaData->peekData());
|
||||
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, make_shamapitem(key, s.slice())))
|
||||
LogicError("duplicate_tx: " + to_string(key));
|
||||
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
|
||||
}
|
||||
|
||||
uint256
|
||||
Ledger::rawTxInsertWithHash(
|
||||
uint256 const& key,
|
||||
std::shared_ptr<Serializer const> const& txn,
|
||||
std::shared_ptr<Serializer const> const& metaData)
|
||||
{
|
||||
XRPL_ASSERT(metaData, "xrpl::Ledger::rawTxInsertWithHash : non-null metadata input");
|
||||
|
||||
// low-level - just add to table
|
||||
Serializer s(txn->getDataLength() + metaData->getDataLength() + 16);
|
||||
s.addVL(txn->peekData());
|
||||
s.addVL(metaData->peekData());
|
||||
auto item = make_shamapitem(key, s.slice());
|
||||
auto hash = sha512Half(HashPrefix::txNode, item->slice(), item->key());
|
||||
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, std::move(item)))
|
||||
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -44,9 +44,11 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -1119,6 +1121,116 @@ Transactor::trapTransaction(uint256 txHash) const
|
||||
JLOG(j_.debug()) << "Transaction trapped: " << txHash;
|
||||
}
|
||||
|
||||
std::tuple<TER, XRPAmount, bool>
|
||||
Transactor::processPersistentChanges(TER result, XRPAmount fee)
|
||||
{
|
||||
JLOG(j_.trace()) << "reapplying because of " << transToken(result);
|
||||
|
||||
// FIXME: This mechanism for doing work while returning a `tec` is
|
||||
// awkward and very limiting. A more general purpose approach
|
||||
// should be used, making it possible to do more useful work
|
||||
// when transactions fail with a `tec` code.
|
||||
|
||||
// Build a list of ledger entry types to collect, based on the
|
||||
// result code. Only deleted objects of these types will be
|
||||
// re-applied after the context is reset.
|
||||
std::unordered_set<LedgerEntryType> typesToCollect;
|
||||
if ((result == tecOVERSIZE) || (result == tecKILLED))
|
||||
typesToCollect.insert(ltOFFER);
|
||||
if (result == tecINCOMPLETE)
|
||||
{
|
||||
typesToCollect.insert(ltRIPPLE_STATE);
|
||||
typesToCollect.insert(ltMPTOKEN);
|
||||
}
|
||||
if (result == tecEXPIRED)
|
||||
{
|
||||
typesToCollect.insert(ltNFTOKEN_OFFER);
|
||||
typesToCollect.insert(ltCREDENTIAL);
|
||||
}
|
||||
|
||||
std::map<LedgerEntryType, std::vector<uint256>> deletedObjects;
|
||||
if (!typesToCollect.empty())
|
||||
{
|
||||
ctx_.visit([&typesToCollect, &deletedObjects](
|
||||
uint256 const& index,
|
||||
bool isDelete,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after) {
|
||||
if (isDelete)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
before && after,
|
||||
"xrpl::Transactor::processPersistentChanges : non-null "
|
||||
"SLE inputs");
|
||||
if (before && after)
|
||||
{
|
||||
auto const type = before->getType();
|
||||
if (typesToCollect.contains(type))
|
||||
{
|
||||
// For offers, only collect unfunded removals
|
||||
// (where TakerPays is unchanged)
|
||||
if (type == ltOFFER &&
|
||||
before->getFieldAmount(sfTakerPays) !=
|
||||
after->getFieldAmount(sfTakerPays))
|
||||
return;
|
||||
|
||||
deletedObjects[type].push_back(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Reset the context, potentially adjusting the fee.
|
||||
{
|
||||
auto const resetResult = reset(fee);
|
||||
if (!isTesSuccess(resetResult.first))
|
||||
result = resetResult.first;
|
||||
|
||||
fee = resetResult.second;
|
||||
}
|
||||
|
||||
// Re-apply the collected deletions, but only if the reset
|
||||
// succeeded (i.e. result is still a tec code).
|
||||
if (isTecClaim(result))
|
||||
{
|
||||
auto const viewJ = ctx_.registry.get().getJournal("View");
|
||||
for (auto const& [type, ids] : deletedObjects)
|
||||
{
|
||||
if (ids.empty())
|
||||
continue;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ltOFFER:
|
||||
removeUnfundedOffers(view(), ids, viewJ);
|
||||
break;
|
||||
case ltNFTOKEN_OFFER:
|
||||
removeExpiredNFTokenOffers(view(), ids, viewJ);
|
||||
break;
|
||||
case ltRIPPLE_STATE:
|
||||
removeDeletedTrustLines(view(), ids, viewJ);
|
||||
break;
|
||||
case ltMPTOKEN:
|
||||
removeDeletedMPTs(view(), ids, viewJ);
|
||||
break;
|
||||
case ltCREDENTIAL:
|
||||
removeExpiredCredentials(view(), ids, viewJ);
|
||||
break;
|
||||
// LCOV_EXCL_START
|
||||
default:
|
||||
UNREACHABLE(
|
||||
"xrpl::Transactor::processPersistentChanges() : "
|
||||
"unexpected type");
|
||||
break;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {result, fee, isTecClaim(result)};
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
Transactor::checkTransactionInvariants(TER result, XRPAmount fee)
|
||||
{
|
||||
@@ -1172,6 +1284,7 @@ Transactor::checkInvariants(TER result, XRPAmount fee)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ApplyResult
|
||||
Transactor::operator()()
|
||||
@@ -1240,108 +1353,7 @@ Transactor::operator()()
|
||||
(result == tecOVERSIZE) || (result == tecKILLED) || (result == tecINCOMPLETE) ||
|
||||
(result == tecEXPIRED) || (isTecClaimHardFail(result, view().flags())))
|
||||
{
|
||||
JLOG(j_.trace()) << "reapplying because of " << transToken(result);
|
||||
|
||||
// FIXME: This mechanism for doing work while returning a `tec` is
|
||||
// awkward and very limiting. A more general purpose approach
|
||||
// should be used, making it possible to do more useful work
|
||||
// when transactions fail with a `tec` code.
|
||||
std::vector<uint256> removedOffers;
|
||||
std::vector<uint256> removedTrustLines;
|
||||
std::vector<uint256> removedMPTs;
|
||||
std::vector<uint256> expiredNFTokenOffers;
|
||||
std::vector<uint256> expiredCredentials;
|
||||
|
||||
bool const doOffers = ((result == tecOVERSIZE) || (result == tecKILLED));
|
||||
bool const doLinesOrMPTs = (result == tecINCOMPLETE);
|
||||
bool const doNFTokenOffers = (result == tecEXPIRED);
|
||||
bool const doCredentials = (result == tecEXPIRED);
|
||||
if (doOffers || doLinesOrMPTs || doNFTokenOffers || doCredentials)
|
||||
{
|
||||
ctx_.visit([doOffers,
|
||||
&removedOffers,
|
||||
doLinesOrMPTs,
|
||||
&removedTrustLines,
|
||||
&removedMPTs,
|
||||
doNFTokenOffers,
|
||||
&expiredNFTokenOffers,
|
||||
doCredentials,
|
||||
&expiredCredentials](
|
||||
uint256 const& index,
|
||||
bool isDelete,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after) {
|
||||
if (isDelete)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
before && after,
|
||||
"xrpl::Transactor::operator()::visit : non-null SLE "
|
||||
"inputs");
|
||||
if (doOffers && before && after && (before->getType() == ltOFFER) &&
|
||||
(before->getFieldAmount(sfTakerPays) == after->getFieldAmount(sfTakerPays)))
|
||||
{
|
||||
// Removal of offer found or made unfunded
|
||||
removedOffers.push_back(index);
|
||||
}
|
||||
|
||||
if (doLinesOrMPTs && before && after)
|
||||
{
|
||||
// Removal of obsolete AMM trust line
|
||||
if (before->getType() == ltRIPPLE_STATE)
|
||||
{
|
||||
removedTrustLines.push_back(index);
|
||||
}
|
||||
else if (before->getType() == ltMPTOKEN)
|
||||
{
|
||||
removedMPTs.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (doNFTokenOffers && before && after &&
|
||||
(before->getType() == ltNFTOKEN_OFFER))
|
||||
expiredNFTokenOffers.push_back(index);
|
||||
|
||||
if (doCredentials && before && after && (before->getType() == ltCREDENTIAL))
|
||||
expiredCredentials.push_back(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Reset the context, potentially adjusting the fee.
|
||||
{
|
||||
auto const resetResult = reset(fee);
|
||||
if (!isTesSuccess(resetResult.first))
|
||||
result = resetResult.first;
|
||||
|
||||
fee = resetResult.second;
|
||||
}
|
||||
|
||||
// If necessary, remove any offers found unfunded during processing
|
||||
if ((result == tecOVERSIZE) || (result == tecKILLED))
|
||||
{
|
||||
removeUnfundedOffers(view(), removedOffers, ctx_.registry.get().getJournal("View"));
|
||||
}
|
||||
|
||||
if (result == tecEXPIRED)
|
||||
{
|
||||
removeExpiredNFTokenOffers(
|
||||
view(), expiredNFTokenOffers, ctx_.registry.get().getJournal("View"));
|
||||
}
|
||||
|
||||
if (result == tecINCOMPLETE)
|
||||
{
|
||||
removeDeletedTrustLines(
|
||||
view(), removedTrustLines, ctx_.registry.get().getJournal("View"));
|
||||
removeDeletedMPTs(view(), removedMPTs, ctx_.registry.get().getJournal("View"));
|
||||
}
|
||||
|
||||
if (result == tecEXPIRED)
|
||||
{
|
||||
removeExpiredCredentials(
|
||||
view(), expiredCredentials, ctx_.registry.get().getJournal("View"));
|
||||
}
|
||||
|
||||
applied = isTecClaim(result);
|
||||
std::tie(result, fee, applied) = processPersistentChanges(result, fee);
|
||||
}
|
||||
|
||||
if (applied)
|
||||
|
||||
Reference in New Issue
Block a user