Skip to content

Commit

Permalink
Amendment: Fix Xahau v1 (XRPLF#231)
Browse files Browse the repository at this point in the history
* FXV1: Meta Amount (XRPLF#225)

* FXV1: Optional Offer Sequence (XRPLF#224)

* FXV1: Patch Hooks OwnerDir (XRPLF#236)

* FXV1:  Fix `Import` Quorum (XRPLF#235)

* FXV1: Namespace Limit (XRPLF#220)

* FXV1: allow duplicate entries in genesis mint transactor (XRPLF#239)

* FXV1: Fix URIToken (XRPLF#243)

* lite fixes for tsh issues (XRPLF#244)

Co-authored-by: RichardAH <[email protected]>
  • Loading branch information
dangell7 and RichardAH authored Dec 21, 2023
1 parent 64a07e5 commit 475b6f7
Show file tree
Hide file tree
Showing 37 changed files with 3,141 additions and 2,299 deletions.
2 changes: 2 additions & 0 deletions hook/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@
#define INVALID_KEY -41
#define NOT_A_STRING -42
#define MEM_OVERLAP -43
#define TOO_MANY_STATE_MODIFICATIONS -44
#define TOO_MANY_NAMESPACES -45
#define HOOK_ERROR_CODES
#endif //HOOK_ERROR_CODES
7 changes: 7 additions & 0 deletions src/ripple/app/hook/Enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ maxHookChainLength(void)
return 10;
}

inline uint32_t
maxNamespaces(void)
{
return 256;
}

enum TSHFlags : uint8_t {
tshNONE = 0b000,
tshROLLBACK = 0b001,
Expand Down Expand Up @@ -313,6 +319,7 @@ enum hook_return_code : int64_t {
MEM_OVERLAP = -43, // one or more specified buffers are the same memory
TOO_MANY_STATE_MODIFICATIONS = -44, // more than 5000 modified state
// entires in the combined hook chains
TOO_MANY_NAMESPACES = -45
};

enum ExitType : uint8_t {
Expand Down
3 changes: 2 additions & 1 deletion src/ripple/app/hook/applyHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ isEmittedTxn(ripple::STTx const& tx);
// only upon tesSuccess for the otxn.
class HookStateMap : public std::map<
ripple::AccountID, // account that owns the state
std::pair<
std::tuple<
int64_t, // remaining available ownercount
int64_t, // total namespace count
std::map<
ripple::uint256, // namespace
std::map<
Expand Down
151 changes: 125 additions & 26 deletions src/ripple/app/hook/impl/applyHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
return rv.read(keylet::nftoffer(*id));
};

bool const fixV1 = rv.rules().enabled(fixXahauV1);

switch (tt)
{
case ttIMPORT: {
Expand All @@ -97,19 +99,36 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
// the burner is the issuer and not the owner of the token

if (issuer == owner)
{
// pass, already a TSH
}
else if (*otxnAcc == owner)
break;
// pass, already a TSH

// new logic
if (fixV1)
{
// the owner burns their token, and the issuer is a weak TSH
ADD_TSH(issuer, canRollback);
}
else
{
if (*otxnAcc == owner && rv.exists(keylet::account(issuer)))
ADD_TSH(issuer, false);
// the issuer burns the owner's token, and the owner is a weak
// TSH
ADD_TSH(owner, canRollback);
else if (rv.exists(keylet::account(owner)))
ADD_TSH(owner, false);

break;
}

// old logic
{
if (*otxnAcc == owner)
{
// the owner burns their token, and the issuer is a weak TSH
ADD_TSH(issuer, true);
}
else
{
// the issuer burns the owner's token, and the owner is a
// weak TSH
ADD_TSH(owner, true);
}
}

break;
Expand Down Expand Up @@ -300,19 +319,59 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)

case ttESCROW_CANCEL:
case ttESCROW_FINISH: {
if (!tx.isFieldPresent(sfOwner) ||
!tx.isFieldPresent(sfOfferSequence))
return {};
// new logic
if (fixV1)
{
if (!tx.isFieldPresent(sfOwner))
return {};

auto escrow = rv.read(keylet::escrow(
tx.getAccountID(sfOwner), tx.getFieldU32(sfOfferSequence)));
AccountID const owner = tx.getAccountID(sfOwner);

if (!escrow)
return {};
bool const hasSeq = tx.isFieldPresent(sfOfferSequence);
bool const hasID = tx.isFieldPresent(sfEscrowID);
if (!hasSeq && !hasID)
return {};

ADD_TSH(escrow->getAccountID(sfAccount), true);
ADD_TSH(escrow->getAccountID(sfDestination), canRollback);
break;
Keylet kl = hasSeq
? keylet::escrow(owner, tx.getFieldU32(sfOfferSequence))
: Keylet(ltESCROW, tx.getFieldH256(sfEscrowID));

auto escrow = rv.read(kl);

if (!escrow ||
escrow->getFieldU16(sfLedgerEntryType) != ltESCROW)
return {};

// this should always be the same as owner, but defensively...
AccountID const src = escrow->getAccountID(sfAccount);
AccountID const dst = escrow->getAccountID(sfDestination);

// the source account is a strong transacitonal stakeholder for
// fin and can
ADD_TSH(src, true);

// the dest acc is a strong tsh for fin and weak for can
if (src != dst)
ADD_TSH(dst, tt == ttESCROW_FINISH);

break;
}
// old logic
{
if (!tx.isFieldPresent(sfOwner) ||
!tx.isFieldPresent(sfOfferSequence))
return {};

auto escrow = rv.read(keylet::escrow(
tx.getAccountID(sfOwner), tx.getFieldU32(sfOfferSequence)));

if (!escrow)
return {};

ADD_TSH(escrow->getAccountID(sfAccount), true);
ADD_TSH(escrow->getAccountID(sfDestination), canRollback);
break;
}
}

case ttPAYCHAN_FUND:
Expand Down Expand Up @@ -1287,7 +1346,7 @@ lookup_state_cache(
if (stateMap.find(acc) == stateMap.end())
return std::nullopt;

auto& stateMapAcc = stateMap[acc].second;
auto& stateMapAcc = std::get<2>(stateMap[acc]);
if (stateMapAcc.find(ns) == stateMapAcc.end())
return std::nullopt;

Expand All @@ -1312,18 +1371,22 @@ set_state_cache(
bool modified)
{
auto& stateMap = hookCtx.result.stateMap;
auto& view = hookCtx.applyCtx.view();

if (modified && stateMap.modified_entry_count >= max_state_modifications)
return TOO_MANY_STATE_MODIFICATIONS;

bool const createNamespace = view.rules().enabled(fixXahauV1) &&
!view.exists(keylet::hookStateDir(acc, ns));

if (stateMap.find(acc) == stateMap.end())
{
// if this is the first time this account has been interacted with
// we will compute how many available reserve positions there are
auto const& fees = hookCtx.applyCtx.view().fees();

auto const accSLE =
hookCtx.applyCtx.view().read(ripple::keylet::account(acc));
auto const accSLE = view.read(ripple::keylet::account(acc));

if (!accSLE)
return DOESNT_EXIST;

Expand All @@ -1342,15 +1405,32 @@ set_state_cache(
if (availableForReserves < 1 && modified)
return RESERVE_INSUFFICIENT;

int64_t namespaceCount = accSLE->isFieldPresent(sfHookNamespaces)
? accSLE->getFieldV256(sfHookNamespaces).size()
: 0;

if (createNamespace)
{
// overflow should never ever happen but check anyway
if (namespaceCount + 1 < namespaceCount)
return INTERNAL_ERROR;

if (++namespaceCount > hook::maxNamespaces())
return TOO_MANY_NAMESPACES;
}

stateMap.modified_entry_count++;

stateMap[acc] = {
availableForReserves - 1, {{ns, {{key, {modified, data}}}}}};
availableForReserves - 1,
namespaceCount,
{{ns, {{key, {modified, data}}}}}};
return 1;
}

auto& stateMapAcc = stateMap[acc].second;
auto& availableForReserves = stateMap[acc].first;
auto& availableForReserves = std::get<0>(stateMap[acc]);
auto& namespaceCount = std::get<1>(stateMap[acc]);
auto& stateMapAcc = std::get<2>(stateMap[acc]);
bool const canReserveNew = availableForReserves > 0;

if (stateMapAcc.find(ns) == stateMapAcc.end())
Expand All @@ -1360,6 +1440,18 @@ set_state_cache(
if (!canReserveNew)
return RESERVE_INSUFFICIENT;

if (createNamespace)
{
// overflow should never ever happen but check anyway
if (namespaceCount + 1 < namespaceCount)
return INTERNAL_ERROR;

if (namespaceCount + 1 > hook::maxNamespaces())
return TOO_MANY_NAMESPACES;

namespaceCount++;
}

availableForReserves--;
stateMap.modified_entry_count++;
}
Expand Down Expand Up @@ -1488,6 +1580,13 @@ DEFINE_HOOK_FUNCTION(
auto const key = make_state_key(
std::string_view{(const char*)(memory + kread_ptr), (size_t)kread_len});

if (view.rules().enabled(fixXahauV1))
{
auto const sleAccount = view.peek(hookCtx.result.accountKeylet);
if (!sleAccount)
return tefINTERNAL;
}

if (!key)
return INTERNAL_ERROR;

Expand Down Expand Up @@ -1610,7 +1709,7 @@ hook::finalizeHookState(
for (const auto& accEntry : stateMap)
{
const auto& acc = accEntry.first;
for (const auto& nsEntry : accEntry.second.second)
for (const auto& nsEntry : std::get<2>(accEntry.second))
{
const auto& ns = nsEntry.first;
for (const auto& cacheEntry : nsEntry.second)
Expand Down
11 changes: 6 additions & 5 deletions src/ripple/app/misc/impl/TxQ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2030,19 +2030,20 @@ TxQ::doRPC(Application& app, std::optional<XRPAmount> hookFeeUnits) const
levels[jss::median_level] = to_string(metrics.medFeeLevel);
levels[jss::open_ledger_level] = to_string(metrics.openLedgerFeeLevel);

auto const txFee = XRPAmount{hookFeeUnits->drops()};
auto const baseFee = view->fees().base;
auto const baseFee =
hookFeeUnits ? XRPAmount{hookFeeUnits->drops()} : view->fees().base;
// If the base fee is 0 drops, but escalation has kicked in, treat the
// base fee as if it is 1 drop, which makes the rest of the math
// work.
auto const effectiveBaseFee = [&txFee, &metrics]() {
if (!txFee && metrics.openLedgerFeeLevel != metrics.referenceFeeLevel)
auto const effectiveBaseFee = [&baseFee, &metrics]() {
if (!baseFee && metrics.openLedgerFeeLevel != metrics.referenceFeeLevel)
return XRPAmount{1};
return txFee;
return baseFee;
}();

auto& drops = ret[jss::drops] = Json::Value();

drops[jss::base_fee_no_hooks] = to_string(view->fees().base);
drops[jss::base_fee] = to_string(baseFee);
drops[jss::median_fee] = to_string(toDrops(metrics.medFeeLevel, baseFee));
drops[jss::minimum_fee] = to_string(toDrops(
Expand Down
Loading

0 comments on commit 475b6f7

Please sign in to comment.