mirror of
https://github.com/Xahau/xahaud.git
synced 2026-09-26 07:10:15 +00:00
Compare commits
8 Commits
litetshfix
...
fxv1-tsh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b926e2e2f6 | ||
|
|
2876ee146d | ||
|
|
94fe1f7b29 | ||
|
|
2d93c50bcc | ||
|
|
a9f7bc81c3 | ||
|
|
10ae534300 | ||
|
|
3b353e146f | ||
|
|
7295577cdd |
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.";
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user