Compare commits

..

8 Commits

Author SHA1 Message Date
Denis Angell
b926e2e2f6 update tsh 2023-12-18 13:29:20 +01:00
Denis Angell
2876ee146d add nftoken 2023-12-15 14:34:52 +01:00
Denis Angell
94fe1f7b29 clang-format 2023-12-15 14:27:10 +01:00
Denis Angell
2d93c50bcc refactor tsh 2023-12-15 14:24:23 +01:00
Denis Angell
a9f7bc81c3 Merge branch 'fixXahauV1' into fxv1-tsh 2023-12-15 12:30:21 +01:00
Denis Angell
10ae534300 clang-format 2023-12-15 12:28:25 +01:00
Denis Angell
3b353e146f fixup for offer sequence 2023-12-12 16:50:41 +01:00
Denis Angell
7295577cdd fxv1-tsh 2023-12-12 16:17:52 +01:00
10 changed files with 541 additions and 899 deletions

View File

@@ -45,43 +45,6 @@ public:
};
using namespace ripple;
static const std::map<uint16_t, uint8_t> TSHAllowances = {
{ttPAYMENT, tshROLLBACK},
{ttESCROW_CREATE, tshROLLBACK},
{ttESCROW_FINISH, tshROLLBACK},
{ttACCOUNT_SET, tshNONE},
{ttESCROW_CANCEL, tshCOLLECT},
{ttREGULAR_KEY_SET, tshROLLBACK},
{ttOFFER_CREATE, tshCOLLECT},
{ttOFFER_CANCEL, tshNONE},
{ttTICKET_CREATE, tshNONE},
{ttSIGNER_LIST_SET, tshROLLBACK},
{ttPAYCHAN_CREATE, tshROLLBACK},
{ttPAYCHAN_FUND, tshCOLLECT},
{ttPAYCHAN_CLAIM, tshCOLLECT},
{ttCHECK_CREATE, tshROLLBACK},
{ttCHECK_CASH, tshROLLBACK},
{ttCHECK_CANCEL, tshCOLLECT},
{ttDEPOSIT_PREAUTH, tshROLLBACK},
{ttTRUST_SET, tshCOLLECT},
{ttACCOUNT_DELETE, tshROLLBACK},
{ttHOOK_SET, tshNONE},
{ttNFTOKEN_MINT, tshROLLBACK},
{ttNFTOKEN_BURN, tshCOLLECT},
{ttNFTOKEN_CREATE_OFFER, tshROLLBACK},
{ttNFTOKEN_CANCEL_OFFER, tshCOLLECT},
{ttNFTOKEN_ACCEPT_OFFER, tshROLLBACK},
{ttCLAIM_REWARD, tshROLLBACK},
{ttINVOKE, tshROLLBACK},
{ttURITOKEN_MINT, tshNONE},
{ttURITOKEN_BURN, tshROLLBACK},
{ttURITOKEN_BUY, tshROLLBACK},
{ttURITOKEN_CREATE_SELL_OFFER, tshROLLBACK},
{ttURITOKEN_CANCEL_SELL_OFFER, tshNONE},
{ttIMPORT, tshROLLBACK},
{ttGENESIS_MINT, tshCOLLECT},
};
std::vector<std::pair<AccountID, bool>>
getTransactionalStakeHolders(STTx const& tx, ReadView const& rv);
} // namespace hook

View File

@@ -21,6 +21,8 @@
using namespace ripple;
namespace hook {
using namespace ripple;
std::vector<std::pair<AccountID, bool>>
getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
{
@@ -38,19 +40,10 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
uint16_t tt = tx.getFieldU16(sfTransactionType);
uint8_t tsh = tshNONE;
if (auto const& found = hook::TSHAllowances.find(tt);
found != hook::TSHAllowances.end())
tsh = found->second;
else
return {};
std::map<AccountID, std::pair<int, bool>> tshEntries;
int upto = 0;
bool canRollback = tsh & tshROLLBACK;
auto const ADD_TSH = [&otxnAcc, &tshEntries, &upto](
const AccountID& acc_r, bool rb) {
if (acc_r != *otxnAcc)
@@ -71,13 +64,14 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
return rv.read(keylet::nftoffer(*id));
};
bool const fixV1 = rv.rules().enabled(fixXahauV1);
bool const tshSTRONG = true; // tshROLLBACK
bool const tshWEAK = false; // tshCOLLECT
switch (tt)
{
case ttIMPORT: {
if (tx.isFieldPresent(sfIssuer))
ADD_TSH(tx.getAccountID(sfIssuer), canRollback);
ADD_TSH(tx.getAccountID(sfIssuer), tshSTRONG);
break;
}
@@ -99,36 +93,19 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
// the burner is the issuer and not the owner of the token
if (issuer == owner)
break;
// pass, already a TSH
// new logic
if (fixV1)
{
// pass, already a TSH
}
else if (*otxnAcc == owner)
{
// the owner burns their token, and the issuer is a weak TSH
if (*otxnAcc == owner && rv.exists(keylet::account(issuer)))
ADD_TSH(issuer, false);
ADD_TSH(issuer, tshWEAK);
}
else
{
// the issuer burns the owner's token, and the owner is a weak
// TSH
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);
}
ADD_TSH(owner, tshWEAK);
}
break;
@@ -145,16 +122,16 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
auto const owner = ut->getAccountID(sfOwner);
if (owner != tx.getAccountID(sfAccount))
if (owner != *otxnAcc)
{
// current owner is a strong TSH
ADD_TSH(owner, canRollback);
// current owner is a TSH
ADD_TSH(owner, tshSTRONG);
}
// issuer is also a strong TSH if the burnable flag is set
// issuer is also a TSH if the burnable flag is set
auto const issuer = ut->getAccountID(sfIssuer);
if (issuer != owner)
ADD_TSH(issuer, ut->getFlags() & lsfBurnable);
ADD_TSH(issuer, (ut->getFlags() & lsfBurnable) ? tshSTRONG : tshWEAK);
break;
}
@@ -173,22 +150,30 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
// issuer is a strong TSH if the burnable flag is set
if (issuer != owner)
ADD_TSH(issuer, ut->getFlags() & lsfBurnable);
ADD_TSH(issuer, (ut->getFlags() & lsfBurnable) ? tshSTRONG : tshWEAK);
// destination is a strong tsh
if (tx.isFieldPresent(sfDestination))
ADD_TSH(tx.getAccountID(sfDestination), canRollback);
ADD_TSH(tx.getAccountID(sfDestination), tshSTRONG);
break;
}
// NFT
case ttNFTOKEN_MINT:
case ttCLAIM_REWARD: {
if (tx.isFieldPresent(sfIssuer))
ADD_TSH(tx.getAccountID(sfIssuer), canRollback);
case ttURITOKEN_CANCEL_SELL_OFFER: {
Keylet const id{ltURI_TOKEN, tx.getFieldH256(sfURITokenID)};
if (!rv.exists(id))
return {};
auto const ut = rv.read(id);
if (!ut || ut->getFieldU16(sfLedgerEntryType) != ltURI_TOKEN)
return {};
// destination is weak tsh
if (ut->isFieldPresent(sfDestination))
ADD_TSH(ut->getAccountID(sfDestination), tshWEAK);
break;
};
}
case ttNFTOKEN_BURN:
case ttNFTOKEN_CREATE_OFFER: {
@@ -209,7 +194,7 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
ADD_TSH(issuer, issuerCanRollback);
if (hasOwner)
ADD_TSH(owner, canRollback);
ADD_TSH(owner, tshWEAK);
break;
}
@@ -228,16 +213,16 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
if (bo)
{
ADD_TSH(bo->getAccountID(sfOwner), canRollback);
ADD_TSH(bo->getAccountID(sfOwner), tshSTRONG);
if (bo->isFieldPresent(sfDestination))
ADD_TSH(bo->getAccountID(sfDestination), canRollback);
ADD_TSH(bo->getAccountID(sfDestination), tshSTRONG);
}
if (so)
{
ADD_TSH(so->getAccountID(sfOwner), canRollback);
ADD_TSH(so->getAccountID(sfOwner), tshSTRONG);
if (so->isFieldPresent(sfDestination))
ADD_TSH(so->getAccountID(sfDestination), canRollback);
ADD_TSH(so->getAccountID(sfDestination), tshSTRONG);
}
break;
@@ -253,10 +238,10 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
auto const offer = getNFTOffer(offerID, rv);
if (offer)
{
ADD_TSH(offer->getAccountID(sfOwner), canRollback);
ADD_TSH(offer->getAccountID(sfOwner), tshWEAK);
if (offer->isFieldPresent(sfDestination))
ADD_TSH(
offer->getAccountID(sfDestination), canRollback);
offer->getAccountID(sfDestination), tshWEAK);
// issuer can't stop people canceling their offers, but can
// get weak executions
@@ -268,9 +253,14 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
break;
}
case ttNFTOKEN_MINT:
case ttCLAIM_REWARD: {
if (tx.isFieldPresent(sfIssuer))
ADD_TSH(tx.getAccountID(sfIssuer), tshSTRONG);
break;
};
// self transactions
case ttURITOKEN_MINT:
case ttURITOKEN_CANCEL_SELL_OFFER:
case ttACCOUNT_SET:
case ttOFFER_CANCEL:
case ttTICKET_CREATE:
@@ -283,18 +273,19 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
case ttREGULAR_KEY_SET: {
if (!tx.isFieldPresent(sfRegularKey))
return {};
ADD_TSH(tx.getAccountID(sfRegularKey), canRollback);
ADD_TSH(tx.getAccountID(sfRegularKey), tshSTRONG);
break;
}
case ttDEPOSIT_PREAUTH: {
if (!tx.isFieldPresent(sfAuthorize))
return {};
ADD_TSH(tx.getAccountID(sfAuthorize), canRollback);
ADD_TSH(tx.getAccountID(sfAuthorize), tshSTRONG);
break;
}
// simple two party transactions
case ttURITOKEN_MINT:
case ttPAYMENT:
case ttESCROW_CREATE:
case ttCHECK_CREATE:
@@ -302,7 +293,7 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
case ttPAYCHAN_CREATE:
case ttINVOKE: {
if (destAcc)
ADD_TSH(*destAcc, canRollback);
ADD_TSH(*destAcc, tshSTRONG);
break;
}
@@ -313,65 +304,33 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
auto const& lim = tx.getFieldAmount(sfLimitAmount);
AccountID const& issuer = lim.getIssuer();
ADD_TSH(issuer, canRollback);
ADD_TSH(issuer, tshWEAK);
break;
}
case ttESCROW_CANCEL:
case ttESCROW_FINISH: {
// new logic
if (fixV1)
{
if (!tx.isFieldPresent(sfOwner))
return {};
if (!tx.isFieldPresent(sfOwner))
return {};
AccountID const owner = tx.getAccountID(sfOwner);
if (!tx.isFieldPresent(sfOfferSequence) &&
!tx.isFieldPresent(sfEscrowID))
return {};
bool const hasSeq = tx.isFieldPresent(sfOfferSequence);
bool const hasID = tx.isFieldPresent(sfEscrowID);
if (!hasSeq && !hasID)
return {};
std::optional<uint256> escrowID = tx[~sfEscrowID];
std::optional<std::uint32_t> offerSeq = tx[~sfOfferSequence];
Keylet kl = hasSeq
? keylet::escrow(owner, tx.getFieldU32(sfOfferSequence))
: Keylet(ltESCROW, tx.getFieldH256(sfEscrowID));
Keylet k = escrowID
? Keylet(ltESCROW, *escrowID)
: keylet::escrow(tx.getAccountID(sfOwner), *offerSeq);
auto escrow = rv.read(kl);
auto escrow = rv.read(k);
if (!escrow)
return {};
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;
}
ADD_TSH(escrow->getAccountID(sfAccount), tshWEAK);
ADD_TSH(escrow->getAccountID(sfDestination), tshWEAK);
break;
}
case ttPAYCHAN_FUND:
@@ -383,8 +342,8 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
if (!chan)
return {};
ADD_TSH(chan->getAccountID(sfAccount), true);
ADD_TSH(chan->getAccountID(sfDestination), canRollback);
ADD_TSH(chan->getAccountID(sfAccount), tshWEAK);
ADD_TSH(chan->getAccountID(sfDestination), tshWEAK);
break;
}
@@ -397,8 +356,8 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
if (!check)
return {};
ADD_TSH(check->getAccountID(sfAccount), true);
ADD_TSH(check->getAccountID(sfDestination), canRollback);
ADD_TSH(check->getAccountID(sfAccount), tshWEAK);
ADD_TSH(check->getAccountID(sfDestination), tshWEAK);
break;
}
@@ -408,7 +367,7 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
STArray const& signerEntries = tx.getFieldArray(sfSignerEntries);
for (auto const& entryObj : signerEntries)
if (entryObj.isFieldPresent(sfAccount))
ADD_TSH(entryObj.getAccountID(sfAccount), canRollback);
ADD_TSH(entryObj.getAccountID(sfAccount), tshSTRONG);
break;
}
@@ -420,7 +379,7 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
{
if (mint.isFieldPresent(sfDestination))
{
ADD_TSH(mint.getAccountID(sfDestination), canRollback);
ADD_TSH(mint.getAccountID(sfDestination), tshWEAK);
}
}
}

View File

@@ -74,8 +74,6 @@ GenesisMint::preflight(PreflightContext const& ctx)
return temMALFORMED;
}
bool const allowDuplicates = ctx.rules.enabled(fixXahauV1);
std::unordered_set<AccountID> alreadySeen;
for (auto const& dest : dests)
{
@@ -139,9 +137,6 @@ GenesisMint::preflight(PreflightContext const& ctx)
return temMALFORMED;
}
if (allowDuplicates)
continue;
if (alreadySeen.find(accid) != alreadySeen.end())
{
JLOG(ctx.j.warn()) << "GenesisMint: duplicate in destinations.";
@@ -176,114 +171,7 @@ GenesisMint::doApply()
{
auto const& dests = ctx_.tx.getFieldArray(sfGenesisMints);
if (!view().rules().enabled(fixXahauV1))
{
STAmount dropsAdded{0};
for (auto const& dest : dests)
{
auto const amt = dest[~sfAmount];
if (amt && !isXRP(*amt))
{
JLOG(ctx_.journal.warn()) << "GenesisMint: Non-xrp amount.";
return tecINTERNAL;
}
auto const flags = dest[~sfGovernanceFlags];
auto const marks = dest[~sfGovernanceMarks];
auto const id = dest.getAccountID(sfDestination);
auto const k = keylet::account(id);
auto sle = view().peek(k);
bool const created = !sle;
if (created)
{
// Create the account.
std::uint32_t const seqno{
view().info().parentCloseTime.time_since_epoch().count()};
sle = std::make_shared<SLE>(k);
sle->setAccountID(sfAccount, id);
sle->setFieldU32(sfSequence, seqno);
if (amt)
{
sle->setFieldAmount(sfBalance, *amt);
dropsAdded += *amt;
}
else // give them 2 XRP if the account didn't exist, same as
// ttIMPORT
{
XRPAmount const initialXrp =
Import::computeStartingBonus(ctx_.view());
sle->setFieldAmount(sfBalance, initialXrp);
dropsAdded += initialXrp;
}
}
else if (amt)
{
// Credit the account
STAmount startBal = sle->getFieldAmount(sfBalance);
STAmount finalBal = startBal + *amt;
if (finalBal <= startBal)
{
JLOG(ctx_.journal.warn())
<< "GenesisMint: cannot credit " << dest
<< " due to balance overflow";
return tecINTERNAL;
}
sle->setFieldAmount(sfBalance, finalBal);
dropsAdded += *amt;
}
// set flags and marks as applicable
if (flags)
sle->setFieldH256(sfGovernanceFlags, *flags);
if (marks)
sle->setFieldH256(sfGovernanceMarks, *marks);
if (created)
view().insert(sle);
else
view().update(sle);
}
// update ledger header
if (dropsAdded < beast::zero ||
dropsAdded.xrp() + view().info().drops < view().info().drops)
{
JLOG(ctx_.journal.warn()) << "GenesisMint: dropsAdded overflowed\n";
return tecINTERNAL;
}
if (dropsAdded > beast::zero)
ctx_.rawView().rawDestroyXRP(-dropsAdded.xrp());
return tesSUCCESS;
}
// RH NOTE:
// As of fixXahauV1, duplicate accounts are allowed
// so we first do a summation loop then an actioning loop
// if amendment is not active then there's exactly one entry per
std::map<
AccountID, // account to credit
std::tuple<
XRPAmount, // amount to credit
std::optional<uint256>, // gov flags
std::optional<uint256>>> // gov marks
mints;
STAmount dropsAdded{0};
// sum loop
for (auto const& dest : dests)
{
auto const amt = dest[~sfAmount];
@@ -294,54 +182,13 @@ GenesisMint::doApply()
return tecINTERNAL;
}
XRPAmount toCredit = amt ? amt->xrp() : XRPAmount{0};
auto const flags = dest[~sfGovernanceFlags];
auto const marks = dest[~sfGovernanceMarks];
auto const id = dest.getAccountID(sfDestination);
auto const k = keylet::account(id);
bool const firstOccurance = mints.find(id) == mints.end();
dropsAdded += toCredit;
// if flags / marks appear more than once we just take the first
// appearance
if (firstOccurance)
mints[id] = {toCredit, flags, marks};
else
{
// duplicated entries accumulate in the map
auto& [accTotal, f, m] = mints[id];
// detect overflow
if (accTotal + toCredit < accTotal)
{
JLOG(ctx_.journal.warn()) << "GenesisMint: cannot credit " << id
<< " due to balance overflow";
return tecINTERNAL;
}
accTotal += toCredit;
}
}
// check for ledger header overflow
if (dropsAdded < beast::zero ||
dropsAdded.xrp() + view().info().drops < view().info().drops)
{
JLOG(ctx_.journal.warn()) << "GenesisMint: dropsAdded overflowed\n";
return tecINTERNAL;
}
// action loop
for (auto const& [id, values] : mints)
{
auto const& [amt, flags, marks] = values;
auto const k = keylet::account(id);
auto sle = view().peek(k);
bool const created = !sle;
if (created)
@@ -355,21 +202,34 @@ GenesisMint::doApply()
sle->setFieldU32(sfSequence, seqno);
sle->setFieldAmount(sfBalance, amt);
if (amt)
{
sle->setFieldAmount(sfBalance, *amt);
dropsAdded += *amt;
}
else // give them 2 XRP if the account didn't exist, same as
// ttIMPORT
{
XRPAmount const initialXrp =
Import::computeStartingBonus(ctx_.view());
sle->setFieldAmount(sfBalance, initialXrp);
dropsAdded += initialXrp;
}
}
else if (amt > beast::zero)
else if (amt)
{
// Credit the account
STAmount startBal = sle->getFieldAmount(sfBalance);
STAmount finalBal = startBal + amt;
STAmount finalBal = startBal + *amt;
if (finalBal <= startBal)
{
JLOG(ctx_.journal.warn()) << "GenesisMint: cannot credit " << id
<< " due to balance overflow";
JLOG(ctx_.journal.warn()) << "GenesisMint: cannot credit "
<< dest << " due to balance overflow";
return tecINTERNAL;
}
sle->setFieldAmount(sfBalance, finalBal);
dropsAdded += *amt;
}
// set flags and marks as applicable
@@ -385,6 +245,14 @@ GenesisMint::doApply()
view().update(sle);
}
// update ledger header
if (dropsAdded < beast::zero ||
dropsAdded.xrp() + view().info().drops < view().info().drops)
{
JLOG(ctx_.journal.warn()) << "GenesisMint: dropsAdded overflowed\n";
return tecINTERNAL;
}
if (dropsAdded > beast::zero)
ctx_.rawView().rawDestroyXRP(-dropsAdded.xrp());

View File

@@ -1437,15 +1437,13 @@ Transactor::addWeakTSHFromSandbox(detail::ApplyViewBase const& pv)
TER
Transactor::doTSH(
bool strong, // only strong iff true, only weak iff false
std::vector<std::pair<AccountID, bool>> tsh,
hook::HookStateMap& stateMap,
std::vector<hook::HookResult>& results,
std::shared_ptr<STObject const> const& provisionalMeta)
{
auto& view = ctx_.view();
std::vector<std::pair<AccountID, bool>> tsh =
hook::getTransactionalStakeHolders(ctx_.tx, view);
// add the extra TSH marked out by the specific transactor (if applicable)
if (!strong)
for (auto& weakTsh : additionalWeakTSH_)
@@ -1463,22 +1461,37 @@ Transactor::doTSH(
// obviously we will never execute OTXN account
// as a TSH because they already had first execution
if (tshAccountID == account_)
{
JLOG(j_.trace()) << "doTSH: tshAccountID == account_";
continue;
}
if (alreadyProcessed.find(tshAccountID) != alreadyProcessed.end())
{
JLOG(j_.trace()) << "doTSH: alreadyProcessed.find(tshAccountID) != "
"alreadyProcessed.end()";
continue;
}
alreadyProcessed.emplace(tshAccountID);
// only process the relevant ones
if ((!canRollback && strong) || (canRollback && !strong))
{
JLOG(j_.trace()) << "doTSH: (!canRollback && strong) || "
"(canRollback && !strong)";
continue;
}
auto klTshHook = keylet::hook(tshAccountID);
auto tshHook = view.read(klTshHook);
if (!(tshHook && tshHook->isFieldPresent(sfHooks)))
{
JLOG(j_.trace())
<< "doTSH: !(tshHook && tshHook->isFieldPresent(sfHooks))";
continue;
}
// scoping here allows tshAcc to leave scope before
// hook execution, which is probably safer
@@ -1486,7 +1499,10 @@ Transactor::doTSH(
// check if the TSH exists and/or has any hooks
auto tshAcc = view.peek(keylet::account(tshAccountID));
if (!tshAcc)
{
JLOG(j_.trace()) << "doTSH: !tshAcc";
continue;
}
// compute and deduct fees for the TSH if applicable
XRPAmount tshFeeDrops =
@@ -1494,7 +1510,10 @@ Transactor::doTSH(
// no hooks to execute, skip tsh
if (tshFeeDrops == 0)
{
JLOG(j_.trace()) << "doTSH: tshFeeDrops == 0";
continue;
}
assert(tshFeeDrops >= beast::zero);
@@ -1698,6 +1717,9 @@ Transactor::operator()()
// application to the ledger
std::map<AccountID, std::set<uint256>> aawMap;
std::vector<std::pair<AccountID, bool>> tsh =
hook::getTransactionalStakeHolders(ctx_.tx, ctx_.view());
// Pre-application (Strong TSH) Hooks are executed here
// These TSH have the right to rollback.
// Weak TSH and callback are executed post-application.
@@ -1726,7 +1748,7 @@ Transactor::operator()()
// (who have the right to rollback the txn), any weak TSH will be
// executed after doApply has been successful (callback as well)
result = doTSH(true, stateMap, hookResults, {});
result = doTSH(true, tsh, stateMap, hookResults, {});
}
// write state if all chains executed successfully
@@ -1969,7 +1991,7 @@ Transactor::operator()()
hook::HookStateMap stateMap;
std::vector<hook::HookResult> weakResults;
doTSH(false, stateMap, weakResults, proMeta);
doTSH(false, tsh, stateMap, weakResults, proMeta);
// execute any hooks that nominated for 'again as weak'
for (auto const& [accID, hookHashes] : aawMap)

View File

@@ -188,6 +188,7 @@ protected:
TER
doTSH(
bool strong, // only do strong TSH iff true, otheriwse only weak
std::vector<std::pair<AccountID, bool>> tsh,
hook::HookStateMap& stateMap,
std::vector<hook::HookResult>& result,
std::shared_ptr<STObject const> const& provisionalMeta);

View File

@@ -103,7 +103,57 @@ URIToken::preflight(PreflightContext const& ctx)
return temMALFORMED;
}
if (!validateUTF8(uri))
if (!([](std::vector<uint8_t> const& u) -> bool {
// this code is from
// https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
uint8_t const* s = (uint8_t const*)u.data();
uint8_t const* end = s + u.size();
while (s < end)
{
if (*s < 0x80)
/* 0xxxxxxx */
s++;
else if ((s[0] & 0xe0) == 0xc0)
{
/* 110XXXXx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[0] & 0xfe) == 0xc0) /* overlong? */
return false;
else
s += 2;
}
else if ((s[0] & 0xf0) == 0xe0)
{
/* 1110XXXX 10Xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
(s[0] == 0xe0 &&
(s[1] & 0xe0) == 0x80) || /* overlong? */
(s[0] == 0xed &&
(s[1] & 0xe0) == 0xa0) || /* surrogate? */
(s[0] == 0xef && s[1] == 0xbf &&
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
return false;
else
s += 3;
}
else if ((s[0] & 0xf8) == 0xf0)
{
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
(s[3] & 0xc0) != 0x80 ||
(s[0] == 0xf0 &&
(s[1] & 0xf0) == 0x80) || /* overlong? */
(s[0] == 0xf4 && s[1] > 0x8f) ||
s[0] > 0xf4) /* > U+10FFFF? */
return false;
else
s += 4;
}
else
return false;
}
return true;
})(uri))
{
JLOG(ctx.j.warn()) << "Malformed transaction. URI must be a "
"valid utf-8 string.";

View File

@@ -30,56 +30,6 @@ namespace ripple {
class URIToken : public Transactor
{
public:
bool inline static validateUTF8(std::vector<uint8_t> const& u)
{
// this code is from
// https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
uint8_t const* s = (uint8_t const*)u.data();
uint8_t const* end = s + u.size();
while (s < end)
{
if (*s < 0x80)
/* 0xxxxxxx */
s++;
else if ((s[0] & 0xe0) == 0xc0)
{
/* 110XXXXx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[0] & 0xfe) == 0xc0) /* overlong? */
return false;
else
s += 2;
}
else if ((s[0] & 0xf0) == 0xe0)
{
/* 1110XXXX 10Xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
(s[0] == 0xef && s[1] == 0xbf &&
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
return false;
else
s += 3;
}
else if ((s[0] & 0xf8) == 0xf0)
{
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
(s[3] & 0xc0) != 0x80 ||
(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
(s[0] == 0xf4 && s[1] > 0x8f) ||
s[0] > 0xf4) /* > U+10FFFF? */
return false;
else
s += 4;
}
else
return false;
}
return true;
}
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit URIToken(ApplyContext& ctx) : Transactor(ctx)

View File

@@ -24,34 +24,6 @@ namespace ripple {
namespace test {
struct GenesisMint_test : public beast::unit_test::suite
{
void
validateEmittedTxn(jtx::Env& env, std::string result, uint64_t lineno)
{
// get the emitted txn id
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const emissions = meta[sfHookEmissions.jsonName];
auto const emission = emissions[0u][sfHookEmission.jsonName];
auto const txId = emission[sfEmittedTxnID.jsonName];
env.close();
// verify emitted result
Json::Value params1;
params1[jss::transaction] = txId;
auto const jrr1 = env.rpc("json", "tx", to_string(params1));
auto const meta1 = jrr1[jss::result][jss::meta];
BEAST_EXPECT(meta1[sfTransactionResult.jsonName] == result);
if (meta1[sfTransactionResult.jsonName] != result)
{
std::cout << "validateEmittedTxn failed " << lineno
<< " expected: " << result
<< " result: " << meta1[sfTransactionResult.jsonName]
<< "\n";
}
}
void
testDisabled(FeatureBitset features)
{
@@ -134,18 +106,10 @@ struct GenesisMint_test : public beast::unit_test::suite
using namespace std::literals::chrono_literals;
Env env{*this, envconfig(), features, nullptr};
// Env env{*this, envconfig(), features, nullptr,
// // beast::severities::kWarning
// beast::severities::kTrace
// };
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const invoker = Account("invoker");
auto const gw = Account("gateway");
auto const USD = gw["USD"];
auto const edward = Account("edward");
env.fund(XRP(10000), alice, bob, invoker, edward);
env.close();
env.fund(XRP(10000), alice, bob, invoker);
// burn down the total ledger coins so that genesis mints don't mint
// above 100B tripping invariant
@@ -179,9 +143,7 @@ struct GenesisMint_test : public beast::unit_test::suite
ter(tesSUCCESS));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
{
auto acc = env.le(keylet::account(bob.id()));
@@ -220,24 +182,21 @@ struct GenesisMint_test : public beast::unit_test::suite
ter(tesSUCCESS));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
env.close();
{
auto acc = env.le(keylet::account(carol.id()));
BEAST_EXPECT(
acc->getFieldAmount(sfBalance).xrp().drops() == 67890000000ULL);
BEAST_EXPECT(acc->getFieldU32(sfSequence) == 60);
BEAST_EXPECT(acc->getFieldU32(sfSequence) == 50);
}
{
auto acc = env.le(keylet::account(david.id()));
BEAST_EXPECT(
acc->getFieldAmount(sfBalance).xrp().drops() == 12345000000ULL);
BEAST_EXPECT(acc->getFieldU32(sfSequence) == 60);
BEAST_EXPECT(acc->getFieldU32(sfSequence) == 50);
}
// lots of entries
@@ -262,9 +221,7 @@ struct GenesisMint_test : public beast::unit_test::suite
fee(XRP(1)));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
for (auto const& [acc, amt, _, __] : mints)
{
@@ -277,9 +234,7 @@ struct GenesisMint_test : public beast::unit_test::suite
fee(XRP(1)));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
for (auto const& [acc, amt, _, __] : mints)
{
@@ -312,6 +267,9 @@ struct GenesisMint_test : public beast::unit_test::suite
// invalid amount should cause emit fail which should cause hook
// rollback
auto const gw = Account("gateway");
auto const USD = gw["USD"];
auto const edward = Account("edward");
env(invoke::invoke(
invoker,
env.master,
@@ -320,24 +278,13 @@ struct GenesisMint_test : public beast::unit_test::suite
fee(XRP(1)),
ter(tecHOOK_REJECTED));
// zero xrp is not allowed
{
env(invoke::invoke(
invoker,
env.master,
genesis::makeBlob(
{{edward.id(), XRP(0), std::nullopt, std::nullopt}})),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// validate emitted txn
auto const txResult = env.current()->rules().enabled(fixXahauV1)
? "tesSUCCESS"
: "tecINTERNAL";
validateEmittedTxn(env, txResult, __LINE__);
}
// zero xrp is allowed
env(invoke::invoke(
invoker,
env.master,
genesis::makeBlob(
{{edward.id(), XRP(0), std::nullopt, std::nullopt}})),
fee(XRP(1)));
// missing an amount
env(invoke::invoke(
@@ -379,9 +326,7 @@ struct GenesisMint_test : public beast::unit_test::suite
fee(XRP(1)));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
}
// check that alice has the right balance, and Governance Flags set
@@ -405,9 +350,7 @@ struct GenesisMint_test : public beast::unit_test::suite
fee(XRP(1)));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
}
// check that bob has the right balance, and Governance Marks set
@@ -432,9 +375,7 @@ struct GenesisMint_test : public beast::unit_test::suite
fee(XRP(1)));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
}
// check
@@ -461,12 +402,9 @@ struct GenesisMint_test : public beast::unit_test::suite
XRP(0).value(),
std::nullopt,
std::nullopt}})),
fee(XRP(1)),
ter(tesSUCCESS));
fee(XRP(1)));
env.close();
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
}
// check
@@ -534,9 +472,7 @@ struct GenesisMint_test : public beast::unit_test::suite
std::nullopt}})),
fee(XRP(1)));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
}
// check
@@ -561,19 +497,11 @@ struct GenesisMint_test : public beast::unit_test::suite
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tesSUCCESS", __LINE__);
env.close();
}
auto const amtResult = env.current()->rules().enabled(fixXahauV1)
? 30000000ULL
: 10000000ULL;
// try to include the same destination twice
{
auto const txResult = env.current()->rules().enabled(fixXahauV1)
? ter(tesSUCCESS)
: ter(tecHOOK_REJECTED);
env(invoke::invoke(
invoker,
env.master,
@@ -588,7 +516,7 @@ struct GenesisMint_test : public beast::unit_test::suite
std::nullopt},
})),
fee(XRP(1)),
txResult);
ter(tecHOOK_REJECTED));
env.close();
env.close();
}
@@ -598,7 +526,7 @@ struct GenesisMint_test : public beast::unit_test::suite
auto const le = env.le(keylet::account(greg.id()));
BEAST_EXPECT(
!!le &&
le->getFieldAmount(sfBalance).xrp().drops() == amtResult);
le->getFieldAmount(sfBalance).xrp().drops() == 10000000ULL);
}
// trip the supply cap invariant
@@ -615,15 +543,13 @@ struct GenesisMint_test : public beast::unit_test::suite
})),
fee(XRP(1)));
env.close();
// validate emitted txn
validateEmittedTxn(env, "tecINVARIANT_FAILED", __LINE__);
env.close();
// check balance wasn't changed
auto const le = env.le(keylet::account(greg.id()));
BEAST_EXPECT(
!!le &&
le->getFieldAmount(sfBalance).xrp().drops() == amtResult);
le->getFieldAmount(sfBalance).xrp().drops() == 10000000ULL);
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(
@@ -676,27 +602,92 @@ struct GenesisMint_test : public beast::unit_test::suite
le->getFieldAmount(sfBalance).xrp().drops() == 10000000000ULL);
}
public:
void
testWithFeats(FeatureBitset features)
testGenesisMintTSH(FeatureBitset features)
{
testDisabled(features);
testGenesisEmit(features);
testGenesisNonEmit(features);
testNonGenesisEmit(features);
testNonGenesisNonEmit(features);
testcase("GenesisMint TSH");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env{*this, envconfig(), features, nullptr};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const invoker = Account("invoker");
env.fund(XRP(10000), alice, bob, invoker);
// set tsh collect on bob
env(fset(bob, asfTshCollect));
// burn down the total ledger coins so that genesis mints don't mint
// above 100B tripping invariant
env(burn(env.master), fee(XRP(10'000'000ULL)));
env.close();
// set the test hook
env(genesis::setMintHook(env.master), fee(XRP(10)));
env.close();
// set the accept hook
env(genesis::setAcceptHook(bob), fee(XRP(10)));
env.close();
// test a mint
{
env(invoke::invoke(
invoker,
env.master,
genesis::makeBlob({
{bob.id(),
XRP(123).value(),
std::nullopt,
std::nullopt},
})),
fee(XRP(10)),
ter(tesSUCCESS));
env.close();
// get the emitted txn id
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const emissions = meta[sfHookEmissions.jsonName];
auto const emission = emissions[0u][sfHookEmission.jsonName];
auto const txId = emission[sfEmittedTxnID.jsonName];
// trigger the emitted txn
env.close();
// verify tsh hook triggered
Json::Value params1;
params1[jss::transaction] = txId;
auto const jrr1 = env.rpc("json", "tx", to_string(params1));
auto const meta1 = jrr1[jss::result][jss::meta];
auto const executions = meta1[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
}
}
public:
void
run() override
{
using namespace test::jtx;
auto const sa = supported_amendments();
testWithFeats(sa);
testWithFeats(sa - fixXahauV1);
testDisabled(sa);
testGenesisEmit(sa);
testGenesisNonEmit(sa);
testNonGenesisEmit(sa);
testNonGenesisNonEmit(sa);
testGenesisMintTSH(sa);
}
};
BEAST_DEFINE_TESTSUITE(GenesisMint, app, ripple);
} // namespace test
} // namespace ripple
} // namespace ripple

View File

@@ -223,11 +223,11 @@ private:
}
// Check
// | otxn | tsh | cancel | create | cash |
// | A | A | S | S | N |
// | A | D | N | S | N |
// | D | D | S | N | S |
// | D | A | S | N | S |
// | otxn | tsh | cancel | cash | create |
// | A | A | S | N | S |
// | A | D | W | N | S |
// | D | D | S | S | N |
// | D | A | W | W | N |
static uint256
getCheckIndex(AccountID const& account, std::uint32_t uSequence)
{
@@ -285,7 +285,7 @@ private:
// otxn: account
// tsh destination
// w/s: none
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -323,7 +323,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: dest
@@ -369,7 +371,7 @@ private:
// otxn: dest
// tsh account
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -381,13 +383,17 @@ private:
env.fund(XRP(1000), account, dest);
env.close();
// set tsh collect
env(fset(account, asfTshCollect));
env.close();
// create check
uint256 const checkId{getCheckIndex(account, env.seq(account))};
env(check::create(account, dest, XRP(100)), ter(tesSUCCESS));
env.close();
// set tsh hook
env(hook(account, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(account, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -405,7 +411,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -462,7 +468,7 @@ private:
// otxn: dest
// tsh account
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -504,7 +510,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -782,7 +788,7 @@ private:
// | A | A | S | S | S |
// | A | D | W | S | W |
// | D | D | S | N | S |
// | D | A | S | N | S |
// | D | A | W | N | S |
static uint256
getEscrowIndex(AccountID const& account, std::uint32_t uSequence)
@@ -898,7 +904,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: dest
@@ -953,7 +961,7 @@ private:
// otxn: dest
// tsh account
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -1002,7 +1010,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -1129,11 +1137,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
// auto const execution = executions[0u][sfHookExecution.jsonName];
// BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
// BEAST_EXPECT(execution[sfHookReturnString.jsonName] ==
// "00000001");
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: dest
@@ -1249,7 +1255,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -1407,7 +1415,7 @@ private:
// otxn: account
// tsh dest
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -1420,8 +1428,8 @@ private:
env.close();
// set tsh collect
// env(fset(dest, asfTshCollect));
// env.close();
env(fset(dest, asfTshCollect));
env.close();
// create escrow
auto const seq1 = env.seq(account);
@@ -1433,7 +1441,7 @@ private:
env.close();
// set tsh hook
env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(dest, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -1453,7 +1461,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: dest
@@ -1505,7 +1513,7 @@ private:
// otxn: dest
// tsh account
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -1517,6 +1525,10 @@ private:
env.fund(XRP(1000), account, dest);
env.close();
// set tsh collect
env(fset(account, asfTshCollect));
env.close();
// create escrow
auto const seq1 = env.seq(account);
NetClock::time_point const finishTime = env.now() + 1s;
@@ -1527,7 +1539,7 @@ private:
env.close();
// set tsh hook
env(hook(account, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(account, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -1547,7 +1559,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -1569,6 +1581,11 @@ private:
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
// Env env{*this, envconfig(), features, nullptr,
// // beast::severities::kWarning
// beast::severities::kTrace
// };
auto const account = Account("alice");
auto const dest = Account("bob");
env.fund(XRP(1000), account, dest);
@@ -1667,7 +1684,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: dest
@@ -1777,7 +1796,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -2187,7 +2208,7 @@ private:
// Offer
// | otxn | tsh | cancel | create |
// | A | A | S | S |
// | A | C | N | N |
// | A | C | N | W |
void
testOfferCancelTSH(FeatureBitset features)
@@ -2309,13 +2330,15 @@ private:
auto const gw = Account{"gateway"};
auto const USD = gw["USD"];
env.fund(XRP(1000), account, cross, gw);
env.trust(USD(10000), cross);
env(pay(gw, cross, USD(1000)));
env.close();
// set tsh collect
env(fset(cross, asfTshCollect));
// gw create offer
env(offer(gw, USD(1000), XRP(1000)));
// cross create offer
env(offer(cross, XRP(1000), USD(1000)));
env.close();
// set tsh hook
@@ -2337,7 +2360,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -2482,7 +2507,7 @@ private:
// | A | A | S | S | S |
// | A | D | W | S | W |
// | D | D | S | N | N |
// | D | A | S | N | N |
// | D | A | W | N | N |
static uint256
channel(
@@ -2548,6 +2573,7 @@ private:
// claim paychannel
env(paychan::claim(account, chan, reqBal, authAmt),
txflags(tfClose),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -2601,6 +2627,7 @@ private:
// claim paychannel
env(paychan::claim(account, chan, reqBal, authAmt),
txflags(tfClose),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -2654,6 +2681,7 @@ private:
signClaimAuth(account.pk(), account.sk(), chan, authAmt);
env(paychan::claim(
dest, chan, reqBal, authAmt, Slice(sig), account.pk()),
txflags(tfClose),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -2672,7 +2700,7 @@ private:
// otxn: dest
// tsh account
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -2684,6 +2712,9 @@ private:
env.fund(XRP(1000), account, dest);
env.close();
// set tsh collect
env(fset(account, asfTshCollect));
// create paychannel
auto const pk = account.pk();
auto const settleDelay = 100s;
@@ -2693,7 +2724,7 @@ private:
env.close();
// set tsh hook
env(hook(account, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(account, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -2707,6 +2738,7 @@ private:
signClaimAuth(account.pk(), account.sk(), chan, authAmt);
env(paychan::claim(
dest, chan, reqBal, authAmt, Slice(sig), account.pk()),
txflags(tfClose),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -2720,7 +2752,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
}
@@ -3020,7 +3052,7 @@ private:
// otxn: account
// tsh dest
// w/s: weak
// w/s: strong
{
test::jtx::Env env{
*this,
@@ -3295,19 +3327,18 @@ private:
// | otxn | tfBurnable | tsh | mint | burn | buy | sell | cancel
// | O | false | O | N | S | S | S | S
// | O | false | I | N | W | W | W | N
// | O | false | B | N | N | N | S | N
// | O | true | B | N | N | N | S | N
// | O | false | B | N | N | N | S | W
// | O | true | B | N | N | N | S | W
// | O | true | O | N | S | S | S | S
// | O | true | I | N | W | S | S | N
// | I | false | O | N | N | N | N | N
// | I | false | I | S | N | N | N | N
// | I | false | B | N | N | N | N | N
// | I | false | B | S | N | N | N | N
// | I | true | O | N | W | N | N | N
// | I | true | I | S | S | N | N | N
// | I | true | B | N | N | N | N | N
// | I | true | B | S | N | N | N | N
// | B | true | O | N | N | ? | N | N
// | B | true | B | N | N | ? | N | N
void
testURITokenBurnTSH(FeatureBitset features)
{
@@ -3373,7 +3404,7 @@ private:
// otxn: owner
// flag: not burnable
// tsh issuer
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -3389,6 +3420,9 @@ private:
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
env(fset(issuer, asfTshCollect));
env.close();
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
@@ -3403,7 +3437,7 @@ private:
env.close();
// set tsh hook
env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(issuer, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -3421,7 +3455,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: owner
@@ -3482,7 +3516,7 @@ private:
// otxn: owner
// flag: burnable
// tsh issuer
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -3498,6 +3532,9 @@ private:
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
env(fset(issuer, asfTshCollect));
env.close();
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
@@ -3513,7 +3550,7 @@ private:
env.close();
// set tsh hook
env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(issuer, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -3531,13 +3568,13 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: issuer
// flag: burnable
// tsh owner
// w/s: strong
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -3553,6 +3590,9 @@ private:
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
env(fset(owner, asfTshCollect));
env.close();
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
@@ -3568,7 +3608,7 @@ private:
env.close();
// set tsh hook
env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(owner, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -3586,7 +3626,7 @@ private:
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: issuer
@@ -3653,213 +3693,8 @@ private:
using namespace test::jtx;
using namespace std::literals;
// otxn: owner
// flag: not burnable
// tsh owner
// w/s: strong
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
env.fund(XRP(1000), issuer, owner);
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
ter(tesSUCCESS));
env.close();
// set tsh hook
env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// verify tsh hook triggered
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
}
// otxn: owner
// flag: not burnable
// tsh issuer
// w/s: strong
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
env.fund(XRP(1000), issuer, owner);
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
ter(tesSUCCESS));
env.close();
// set tsh hook
env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// verify tsh hook triggered
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
}
// otxn: owner
// flag: burnable
// tsh owner
// w/s: strong
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
env.fund(XRP(1000), issuer, owner);
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
txflags(tfBurnable),
ter(tesSUCCESS));
env.close();
// set tsh hook
env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// verify tsh hook triggered
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
}
// otxn: owner
// flag: burnable
// tsh issuer
// w/s: strong
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
env.fund(XRP(1000), issuer, owner);
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
txflags(tfBurnable),
ter(tesSUCCESS));
env.close();
// set tsh hook
env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// verify tsh hook triggered
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
}
// otxn: buyer
// flag: not burnable
// tsh owner
// w/s: strong
{
@@ -3925,8 +3760,9 @@ private:
}
// otxn: buyer
// tsh buyer
// w/s: strong
// flag: not burnable
// tsh issuer
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -3943,6 +3779,9 @@ private:
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
env(fset(issuer, asfTshCollect));
env.close();
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
@@ -3965,7 +3804,141 @@ private:
env.close();
// set tsh hook
env(hook(buyer, {{hso(TshHook, overrideFlag)}}, 0),
env(hook(issuer, {{hso(TshHook, collectFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(buyer, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// verify tsh hook triggered
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: buyer
// flag: burnable
// tsh owner
// w/s: strong
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
auto const buyer = Account("carol");
env.fund(XRP(1000), issuer, owner, buyer);
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
txflags(tfBurnable),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// sell uritoken
env(uritoken::sell(owner, hexid),
uritoken::dest(buyer),
uritoken::amt(XRP(1)),
ter(tesSUCCESS));
env.close();
// set tsh hook
env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(buyer, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// verify tsh hook triggered
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
}
// otxn: buyer
// flag: burnable
// tsh issuer
// w/s: strong
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
auto const buyer = Account("carol");
env.fund(XRP(1000), issuer, owner, buyer);
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// mint uritoken
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
txflags(tfBurnable),
ter(tesSUCCESS));
env.close();
// buy uritoken
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
// sell uritoken
env(uritoken::sell(owner, hexid),
uritoken::dest(buyer),
uritoken::amt(XRP(1)),
ter(tesSUCCESS));
env.close();
// set tsh hook
env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -4063,7 +4036,7 @@ private:
// otxn: owner
// flag: not burnable
// tsh buyer
// w/s: none
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -4120,13 +4093,15 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: owner
// flag: burnable
// tsh buyer
// w/s: none
// w/s: weak
{
test::jtx::Env env{
*this,
@@ -4184,7 +4159,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001");
}
// otxn: owner
@@ -4675,7 +4652,7 @@ private:
// otxn: issuer
// flag: not burnable
// tsh buyer
// w/s: none
// w/s: strong
{
test::jtx::Env env{
*this,
@@ -4687,15 +4664,12 @@ private:
env.fund(XRP(1000), issuer, buyer);
env.close();
env(fset(buyer, asfTshCollect));
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// set tsh hook
env(hook(buyer, {{hso(TshHook, collectFlag)}}, 0),
env(hook(buyer, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -4715,7 +4689,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
}
// otxn: issuer
@@ -4767,7 +4743,7 @@ private:
// otxn: issuer
// flag: burnable
// tsh buyer
// w/s: none
// w/s: strong
{
test::jtx::Env env{
*this,
@@ -4779,15 +4755,12 @@ private:
env.fund(XRP(1000), issuer, buyer);
env.close();
env(fset(buyer, asfTshCollect));
env.close();
std::string const uri(2, '?');
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
// set tsh hook
env(hook(buyer, {{hso(TshHook, collectFlag)}}, 0),
env(hook(buyer, {{hso(TshHook, overrideFlag)}}, 0),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
@@ -4808,7 +4781,9 @@ private:
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
auto const executions = meta[sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 0);
auto const execution = executions[0u][sfHookExecution.jsonName];
BEAST_EXPECT(execution[sfHookResult.jsonName] == 3);
BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000");
}
}

View File

@@ -4245,142 +4245,6 @@ struct XahauGenesis_test : public beast::unit_test::suite
env, user, preLedger, preLedger + 1, postUser, preTime));
}
void
testValidL1WithUNLReport(FeatureBitset features)
{
using namespace jtx;
using namespace std::chrono_literals;
testcase("test claim reward valid for L1 with unl report");
for (bool const withXahauV1 : {true, false})
{
FeatureBitset _features = features - featureXahauGenesis;
auto const amend = withXahauV1 ? _features : _features - fixXahauV1;
Env env{*this, envconfig(), amend};
double const rateDrops = 0.00333333333 * 1'000'000;
STAmount const feesXRP = XRP(1);
// setup governance
Account const alice = Account("alice");
Account const bob = Account("bob");
Account const carol = Account("carol");
Account const david = Account("david");
Account const edward = Account("edward");
env.fund(XRP(10000), alice, bob, carol, david, edward);
env.close();
std::vector<AccountID> initial_members_ids{
alice.id(), bob.id(), carol.id(), david.id(), edward.id()};
setupGov(env, initial_members_ids);
// update reward delay
{
// this will be the new reward delay
// 100
std::vector<uint8_t> vote_data{
0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U};
updateTopic(
env, alice, bob, carol, david, edward, 'R', 'D', vote_data);
}
// setup unl report
std::vector<std::string> const _ivlKeys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC1",
};
std::vector<PublicKey> ivlKeys;
for (auto const& strPk : _ivlKeys)
{
auto pkHex = strUnHex(strPk);
ivlKeys.emplace_back(makeSlice(*pkHex));
}
std::vector<std::string> const _vlKeys = {
"0388935426E0D08083314842EDFBB2D517BD47699F9A4527318A8E10468C97"
"C05"
"2",
"02691AC5AE1C4C333AE5DF8A93BDC495F0EEBFC6DB0DA7EB6EF808F3AFC006"
"E3F"
"E",
"028949021029D5CC87E78BCF053AFEC0CAFD15108EC119EAAFEC466F5C0954"
"07B"
"F",
"027BAEF0CB02EA8B95F50DF4BC16C740B17B50C85F3757AA06A5DB6ADE0ED9"
"210"
"6",
"0318E0D644F3D2911D7B7E1B0B17684E7E625A6C36AECCE851BD16A4AD628B"
"213"
"6"};
std::vector<PublicKey> vlKeys;
for (auto const& strPk : _vlKeys)
{
auto pkHex = strUnHex(strPk);
vlKeys.emplace_back(makeSlice(*pkHex));
}
// activate unl report
activateUNLReport(env, ivlKeys, vlKeys);
// validate unl report
BEAST_EXPECT(hasUNLReport(env) == true);
// opt in claim reward
env(claimReward(alice, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// close ledgers
validateTime(lastClose(env), 90);
for (int i = 0; i < 5; ++i)
{
env.close(10s);
}
validateTime(lastClose(env), 180);
// define pre close values
STAmount const preAlice = env.balance(alice);
STAmount const preBob = env.balance(bob);
STAmount const preCarol = env.balance(carol);
STAmount const preDavid = env.balance(david);
STAmount const preEdward = env.balance(edward);
NetClock::time_point const preTime = lastClose(env);
std::uint32_t const preLedger = env.current()->seq();
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
// claim reward
auto const txResult =
withXahauV1 ? ter(tesSUCCESS) : ter(tecHOOK_REJECTED);
env(claimReward(alice, env.master), fee(feesXRP), txResult);
env.close();
// trigger emitted txn
env.close();
// calculate rewards
STAmount const netReward =
rewardUserAmount(*acctSle, preLedger, rateDrops);
STAmount const l1Reward =
withXahauV1 ? rewardL1Amount(netReward, 20) : STAmount(0);
// validate govern rewards
BEAST_EXPECT(env.balance(bob) == preBob + l1Reward);
BEAST_EXPECT(env.balance(carol) == preCarol + l1Reward);
BEAST_EXPECT(env.balance(david) == preDavid + l1Reward);
BEAST_EXPECT(env.balance(edward) == preEdward + l1Reward);
// validate account fields
STAmount const postAlice = preAlice + netReward + l1Reward;
bool const boolResult = withXahauV1 ? true : false;
BEAST_EXPECT(
expectAccountFields(
env, alice, preLedger, preLedger + 1, postAlice, preTime) ==
boolResult);
}
}
void
testOptInOptOut(FeatureBitset features)
{
@@ -5009,7 +4873,6 @@ struct XahauGenesis_test : public beast::unit_test::suite
testInvalidBeforeTime(features);
testValidWithoutUNLReport(features);
testValidWithUNLReport(features);
testValidL1WithUNLReport(features);
testOptInOptOut(features);
testValidLowBalance(features);
testValidElapsed1(features);