Compare commits

..

13 Commits

Author SHA1 Message Date
Denis Angell
9df80a90d0 fix weak tsh 2024-01-19 15:42:35 +01:00
RichardAH
f21d3e1e97 Merge pull request #260 from Xahau/emit_guard
Fix: EmittedTxn Reliability
2024-01-19 14:05:06 +01:00
Denis Angell
858055c811 clang-format 2024-01-19 11:07:22 +01:00
Denis Angell
5d66f17574 add test 2024-01-19 11:05:45 +01:00
Denis Angell
00328cb782 Merge branch 'dev' into emit_guard 2024-01-19 10:24:34 +01:00
Richard Holland
fdf7ea4174 dbg inject clang + permission check 2024-01-17 18:15:17 +00:00
Richard Holland
7877ed9704 debug txn injector 2024-01-17 18:07:05 +00:00
Richard Holland
17ccec9ac5 Add additional checks for emitted txns 2024-01-17 15:39:02 +00:00
RichardAH
de522ac4ae Merge pull request #255 from Xahau/candidate
Candidate/release/sync
2023-12-29 22:18:04 +01:00
RichardAH
74c83a9271 Merge branch 'release' into candidate 2023-12-29 21:56:05 +01:00
Wietse Wind
66ee96d456 Build on release after all 2023-12-29 15:43:33 +01:00
Wietse Wind
b476aea55b Do not auto build on release 2023-12-29 15:39:48 +01:00
Denis Angell
1b9373e220 add array xpop path 2023-10-30 15:53:48 +01:00
35 changed files with 380 additions and 2095 deletions

View File

@@ -455,7 +455,6 @@ target_sources (rippled PRIVATE
src/ripple/app/tx/impl/GenesisMint.cpp
src/ripple/app/tx/impl/Import.cpp
src/ripple/app/tx/impl/Invoke.cpp
src/ripple/app/tx/impl/Remit.cpp
src/ripple/app/tx/impl/SetSignerList.cpp
src/ripple/app/tx/impl/SetTrust.cpp
src/ripple/app/tx/impl/SignerEntries.cpp
@@ -741,7 +740,6 @@ if (tests)
src/test/app/RCLCensorshipDetector_test.cpp
src/test/app/RCLValidations_test.cpp
src/test/app/Regression_test.cpp
src/test/app/Remit_test.cpp
src/test/app/SHAMapStore_test.cpp
src/test/app/SetAuth_test.cpp
src/test/app/SetRegularKey_test.cpp
@@ -891,7 +889,6 @@ if (tests)
src/test/jtx/impl/rate.cpp
src/test/jtx/impl/regkey.cpp
src/test/jtx/impl/reward.cpp
src/test/jtx/impl/remit.cpp
src/test/jtx/impl/sendmax.cpp
src/test/jtx/impl/seq.cpp
src/test/jtx/impl/sig.cpp

View File

@@ -30,6 +30,7 @@
#include <boost/circular_buffer.hpp>
#include <boost/intrusive/set.hpp>
#include <optional>
#include <vector>
namespace ripple {
@@ -56,7 +57,14 @@ class Config;
*/
class TxQ
{
private:
std::mutex debugTxInjectMutex;
std::vector<STTx> debugTxInjectQueue;
public:
void
debugTxInject(STTx const& txn);
/// Fee level for single-signed reference transaction.
static constexpr FeeLevel64 baseLevel{256};

View File

@@ -93,6 +93,13 @@ increase(FeeLevel64 level, std::uint32_t increasePercent)
//////////////////////////////////////////////////////////////////////////
void
TxQ::debugTxInject(STTx const& txn)
{
const std::lock_guard<std::mutex> _(debugTxInjectMutex);
debugTxInjectQueue.push_back(txn);
}
std::size_t
TxQ::FeeMetrics::update(
Application& app,
@@ -1444,6 +1451,32 @@ TxQ::accept(Application& app, OpenView& view)
auto const metricsSnapshot = feeMetrics_.getSnapshot();
// try to inject any debug txns waiting in the debug queue
{
std::unique_lock<std::mutex> trylock(
TxQ::debugTxInjectMutex, std::try_to_lock);
if (trylock.owns_lock() && !debugTxInjectQueue.empty())
{
// pop everything
for (STTx const& txn : debugTxInjectQueue)
{
auto txnHash = txn.getTransactionID();
app.getHashRouter().setFlags(txnHash, SF_EMITTED | SF_PRIVATE2);
auto const& emitted =
const_cast<ripple::STTx&>(txn).downcast<STObject>();
auto s = std::make_shared<ripple::Serializer>();
emitted.add(*s);
view.rawTxInsert(txnHash, std::move(s), nullptr);
ledgerChanged = true;
}
debugTxInjectQueue.clear();
}
}
// Inject emitted transactions if any
if (view.rules().enabled(featureHooks))
do

View File

@@ -597,8 +597,7 @@ ValidNewAccountRoot::finalize(
return false;
}
if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT ||
tt == ttREMIT) &&
if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT) &&
result == tesSUCCESS)
{
std::uint32_t const startingSeq{

View File

@@ -1,502 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/app/tx/impl/Remit.h>
#include <ripple/app/tx/impl/URIToken.h>
#include <ripple/basics/Log.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
namespace ripple {
TxConsequences
Remit::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
Remit::preflight(PreflightContext const& ctx)
{
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
if (ctx.tx.getFlags() & tfUniversalMask)
{
// There are no flags (other than universal).
JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set.";
return temINVALID_FLAG;
}
AccountID const dstID = ctx.tx.getAccountID(sfDestination);
AccountID const srcID = ctx.tx.getAccountID(sfAccount);
if (dstID == srcID)
{
JLOG(ctx.j.warn()) << "Malformed transaction: Remit to self.";
return temREDUNDANT;
}
// sanity check amounts
if (ctx.tx.isFieldPresent(sfAmounts))
{
std::map<Currency, std::set<AccountID>> already;
bool nativeAlready = false;
STArray const& sEntries(ctx.tx.getFieldArray(sfAmounts));
for (STObject const& sEntry : sEntries)
{
// Validate the AmountEntry.
if (sEntry.getFName() != sfAmountEntry)
{
JLOG(ctx.j.warn()) << "Malformed: Expected AmountEntry.";
return temMALFORMED;
}
STAmount const amount = sEntry.getFieldAmount(sfAmount);
if (!isLegalNet(amount) || amount.signum() <= 0)
{
JLOG(ctx.j.warn()) << "Malformed transaction: bad amount: "
<< amount.getFullText();
return temBAD_AMOUNT;
}
if (isBadCurrency(amount.getCurrency()))
{
JLOG(ctx.j.warn()) << "Malformed transaction: Bad currency.";
return temBAD_CURRENCY;
}
if (isXRP(amount))
{
if (nativeAlready)
{
JLOG(ctx.j.warn()) << "Malformed transaction: Native "
"Currency appears more than once.";
return temMALFORMED;
}
nativeAlready = true;
continue;
}
auto found = already.find(amount.getCurrency());
if (found == already.end())
{
already.emplace(
amount.getCurrency(),
std::set<AccountID>{amount.getIssuer()});
continue;
}
if (found->second.find(amount.getIssuer()) != found->second.end())
{
JLOG(ctx.j.warn()) << "Malformed transaction: Issued Currency "
"appears more than once.";
return temMALFORMED;
}
found->second.emplace(amount.getIssuer());
}
}
// sanity check minturitoken
if (ctx.tx.isFieldPresent(sfMintURIToken))
{
STObject const& mint = const_cast<ripple::STTx&>(ctx.tx)
.getField(sfMintURIToken)
.downcast<STObject>();
// RH TODO: iterate mint fields detect any that shouldnt be there
Blob const uri = mint.getFieldVL(sfURI);
if (uri.size() < 1 || uri.size() > 256)
{
JLOG(ctx.j.warn())
<< "Malformed transaction: URI was too short/long.";
return temMALFORMED;
}
if (!URIToken::validateUTF8(mint.getFieldVL(sfURI)))
{
JLOG(ctx.j.warn())
<< "Malformed transaction: Invalid UTF8 inside MintURIToken.";
return temMALFORMED;
}
if (mint.isFieldPresent(sfFlags))
{
if (mint.getFieldU32(sfFlags) & tfURITokenMintMask)
return temINVALID_FLAG;
}
}
// check uritokenids for duplicates
if (ctx.tx.isFieldPresent(sfURITokenIDs))
{
STVector256 ids = ctx.tx.getFieldV256(sfURITokenIDs);
std::sort(ids.begin(), ids.end());
if (std::adjacent_find(ids.begin(), ids.end()) != ids.end())
{
JLOG(ctx.j.warn())
<< "Malformed transaction: Duplicate URITokenID.";
return temMALFORMED;
}
}
return preflight2(ctx);
}
TER
Remit::doApply()
{
if (!view().rules().enabled(featureRemit))
return temDISABLED;
Sandbox sb(&ctx_.view());
beast::Journal const& j = ctx_.journal;
auto const srcAccID = ctx_.tx[sfAccount];
auto sleSrcAcc = sb.peek(keylet::account(srcAccID));
if (!sleSrcAcc)
return terNO_ACCOUNT;
XRPAmount const accountReserve{sb.fees().accountReserve(0)};
XRPAmount const objectReserve{sb.fees().accountReserve(1) - accountReserve};
// amount of native tokens we will transfer to cover reserves for the
// tls/acc/uritokens created, and native tokens listed in amounts
XRPAmount nativeRemit{0};
AccountID const dstAccID{ctx_.tx[sfDestination]};
auto sleDstAcc = sb.peek(keylet::account(dstAccID));
auto const flags = !sleDstAcc ? 0 : sleDstAcc->getFlags();
// Check if the destination has disallowed incoming
if (sb.rules().enabled(featureDisallowIncoming) &&
(flags & lsfDisallowIncomingRemit))
return tecNO_PERMISSION;
// the destination may require a dest tag
if ((flags & lsfRequireDestTag) &&
!ctx_.tx.isFieldPresent(sfDestinationTag))
{
JLOG(j.warn())
<< "Remit: DestinationTag required for this destination.";
return tecDST_TAG_NEEDED;
}
// if the destination doesn't exist, create it.
bool const createDst = !sleDstAcc;
if (createDst)
{
// sender will pay the reserve
nativeRemit += accountReserve;
// Create the account.
std::uint32_t const seqno{
sb.rules().enabled(featureXahauGenesis)
? sb.info().parentCloseTime.time_since_epoch().count()
: sb.rules().enabled(featureDeletableAccounts) ? sb.seq() : 1};
sleDstAcc = std::make_shared<SLE>(keylet::account(dstAccID));
sleDstAcc->setAccountID(sfAccount, dstAccID);
sleDstAcc->setFieldU32(sfSequence, seqno);
sleDstAcc->setFieldU32(sfOwnerCount, 0);
if (sb.exists(keylet::fees()) &&
sb.rules().enabled(featureXahauGenesis))
{
auto sleFees = sb.peek(keylet::fees());
uint64_t accIdx = sleFees->isFieldPresent(sfAccountCount)
? sleFees->getFieldU64(sfAccountCount)
: 0;
sleDstAcc->setFieldU64(sfAccountIndex, accIdx);
sleFees->setFieldU64(sfAccountCount, accIdx + 1);
sb.update(sleFees);
}
// we'll fix this up at the end
sleDstAcc->setFieldAmount(sfBalance, STAmount{XRPAmount{0}});
sb.insert(sleDstAcc);
}
// if theres a minted uritoken the sender pays for that
if (ctx_.tx.isFieldPresent(sfMintURIToken))
{
nativeRemit += objectReserve;
STObject const& mint = const_cast<ripple::STTx&>(ctx_.tx)
.getField(sfMintURIToken)
.downcast<STObject>();
Blob const& mintURI = mint.getFieldVL(sfURI);
std::optional<uint256> mintDigest;
if (mint.isFieldPresent(sfDigest))
mintDigest = mint.getFieldH256(sfDigest);
Keylet kl = keylet::uritoken(srcAccID, mintURI);
// check that it doesn't already exist
if (sb.exists(kl))
{
JLOG(j.trace()) << "Remit: tried to creat duplicate URIToken. Tx: "
<< ctx_.tx.getTransactionID();
return tecDUPLICATE;
}
auto sleMint = std::make_shared<SLE>(kl);
sleMint->setAccountID(sfOwner, dstAccID);
sleMint->setAccountID(sfIssuer, srcAccID);
sleMint->setFieldVL(sfURI, mintURI);
if (mint.isFieldPresent(sfDigest))
sleMint->setFieldH256(sfDigest, mint.getFieldH256(sfDigest));
sleMint->setFieldU32(
sfFlags,
mint.isFieldPresent(sfFlags) ? mint.getFieldU32(sfFlags) : 0);
auto const page = view().dirInsert(
keylet::ownerDir(dstAccID), kl, describeOwnerDir(dstAccID));
JLOG(j_.trace()) << "Adding URIToken to owner directory "
<< to_string(kl.key) << ": "
<< (page ? "success" : "failure");
if (!page)
return tecDIR_FULL;
sleMint->setFieldU64(sfOwnerNode, *page);
sb.insert(sleMint);
// ensure there is a deletion blocker against the issuer now
sleSrcAcc->setFieldU32(
sfFlags, sleSrcAcc->getFlags() | lsfURITokenIssuer);
adjustOwnerCount(sb, sleDstAcc, 1, j);
}
// iterate uritokens
if (ctx_.tx.isFieldPresent(sfURITokenIDs))
{
STVector256 ids = ctx_.tx.getFieldV256(sfURITokenIDs);
for (uint256 const klRaw : ids)
{
Keylet kl = keylet::unchecked(klRaw);
auto sleU = sb.peek(kl);
// does it exist
if (!sleU)
{
JLOG(j.warn()) << "Remit: one or more uritokens did not exist "
"on the source account.";
return tecUNFUNDED_PAYMENT;
}
// is it a uritoken?
if (sleU->getFieldU16(sfLedgerEntryType) != ltURI_TOKEN)
{
JLOG(j.warn()) << "Remit: one or more supplied URITokenIDs was "
"not actually a uritoken.";
return tecNO_ENTRY;
}
// is it our uritoken?
if (sleU->getAccountID(sfOwner) != srcAccID)
{
JLOG(j.warn()) << "Remit: one or more supplied URITokenIDs was "
"not owned by sender.";
return tecNO_PERMISSION;
}
// erase current sale offers, if any
if (sleU->isFieldPresent(sfAmount))
sleU->makeFieldAbsent(sfAmount);
if (sleU->isFieldPresent(sfDestination))
sleU->makeFieldAbsent(sfDestination);
// pay the reserve
nativeRemit += objectReserve;
// remove from sender dir
{
auto const page = (*sleU)[sfOwnerNode];
if (!sb.dirRemove(
keylet::ownerDir(srcAccID), page, kl.key, true))
{
JLOG(j.fatal())
<< "Could not remove URIToken from owner directory";
return tefBAD_LEDGER;
}
adjustOwnerCount(sb, sleSrcAcc, -1, j);
}
// add to dest dir
{
auto const page = sb.dirInsert(
keylet::ownerDir(dstAccID), kl, describeOwnerDir(dstAccID));
JLOG(j_.trace()) << "Adding URIToken to owner directory "
<< to_string(kl.key) << ": "
<< (page ? "success" : "failure");
if (!page)
return tecDIR_FULL;
sleU->setFieldU64(sfOwnerNode, *page);
adjustOwnerCount(sb, sleDstAcc, 1, j);
}
// change the owner
sleU->setAccountID(sfOwner, dstAccID);
sb.update(sleU);
}
}
// iterate trustlines
if (ctx_.tx.isFieldPresent(sfAmounts))
{
// process trustline remits
STArray const& sEntries(ctx_.tx.getFieldArray(sfAmounts));
for (STObject const& sEntry : sEntries)
{
STAmount const amount = sEntry.getFieldAmount(sfAmount);
if (isXRP(amount))
{
// since we have to pay for all the created objects including
// possibly the account itself this is paid right at the end,
// and only if there is balance enough to cover.
nativeRemit += amount.xrp();
continue;
}
AccountID const issuerAccID = amount.getIssuer();
// check permissions
if (issuerAccID == srcAccID || issuerAccID == dstAccID)
{
// no permission check needed when the issuer sends out or a
// subscriber sends back RH TODO: move this condition into
// trustTransferAllowed, guarded by an amendment
}
else if (TER canXfer = trustTransferAllowed(
sb,
std::vector<AccountID>{srcAccID, dstAccID},
amount.issue(),
j);
canXfer != tesSUCCESS)
return canXfer;
// compute the amount the source will need to send
// in remit the sender pays all transfer fees, so that
// the destination can always be assured they got the exact amount
// specified. therefore we need to compute the amount + transfer fee
auto const srcAmt =
issuerAccID != srcAccID && issuerAccID != dstAccID
? multiply(amount, transferRate(sb, issuerAccID))
: amount;
auto const dstAmt = amount;
STAmount availableFunds{
accountFunds(sb, srcAccID, srcAmt, fhZERO_IF_FROZEN, j)};
if (availableFunds < srcAmt)
return tecUNFUNDED_PAYMENT;
// if the target trustline doesn't exist we need to create it and
// pay its reserve
if (!sb.exists(keylet::line(
issuerAccID == dstAccID ? srcAccID : dstAccID,
issuerAccID,
amount.getCurrency())))
nativeRemit += objectReserve;
// action the transfer
STAmount sentAmt;
if (TER result =
rippleSend(sb, srcAccID, dstAccID, dstAmt, sentAmt, j);
result != tesSUCCESS)
return result;
if (sentAmt != srcAmt)
return tecINTERNAL;
}
}
if (nativeRemit < beast::zero)
return tecINTERNAL;
if (nativeRemit > beast::zero)
{
// ensure the account can cover the native remit
if (mSourceBalance < nativeRemit)
return tecUNFUNDED_PAYMENT;
// subtract the balance from the sender
{
STAmount bal = mSourceBalance;
bal -= nativeRemit;
if (bal < beast::zero || bal > mSourceBalance)
return tecINTERNAL;
sleSrcAcc->setFieldAmount(sfBalance, bal);
}
// add the balance to the destination
{
STAmount bal = sleDstAcc->getFieldAmount(sfBalance);
STAmount prior = bal;
bal += nativeRemit;
if (bal < beast::zero || bal < prior)
return tecINTERNAL;
sleDstAcc->setFieldAmount(sfBalance, bal);
}
}
// apply
sb.update(sleSrcAcc);
sb.update(sleDstAcc);
sb.apply(ctx_.rawView());
return tesSUCCESS;
}
XRPAmount
Remit::calculateBaseFee(ReadView const& view, STTx const& tx)
{
XRPAmount extraFee{0};
if (tx.isFieldPresent(sfBlob))
extraFee +=
XRPAmount{static_cast<XRPAmount>(tx.getFieldVL(sfBlob).size())};
// RH TODO: add fees
return Transactor::calculateBaseFee(view, tx) + extraFee;
}
} // namespace ripple

View File

@@ -1,54 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TX_SIMPLE_PAYMENT_H_INCLUDED
#define RIPPLE_TX_SIMPLE_PAYMENT_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/protocol/Indexes.h>
namespace ripple {
class Remit : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
explicit Remit(ApplyContext& ctx) : Transactor(ctx)
{
}
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
TER
doApply() override;
};
} // namespace ripple
#endif

View File

@@ -577,14 +577,6 @@ SetAccount::doApply()
uFlagsOut |= lsfDisallowIncomingTrustline;
else if (uClearFlag == asfDisallowIncomingTrustline)
uFlagsOut &= ~lsfDisallowIncomingTrustline;
if (ctx_.view().rules().enabled(featureRemit))
{
if (uSetFlag == asfDisallowIncomingRemit)
uFlagsOut |= lsfDisallowIncomingRemit;
else if (uClearFlag == asfDisallowIncomingRemit)
uFlagsOut &= ~lsfDisallowIncomingRemit;
}
}
if (uFlagsIn != uFlagsOut)

View File

@@ -648,6 +648,43 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
if (ctx.view.txExists(ctx.tx.getTransactionID()))
return tefALREADY;
if (hook::isEmittedTxn(ctx.tx) && ctx.view.rules().enabled(featureHooks) &&
ctx.view.rules().enabled(fixXahauV2))
{
// check if the emitted txn exists on ledger and is in the emission
// directory if not that's a re-apply so discard
auto const kl = keylet::emittedTxn(ctx.tx.getTransactionID());
auto const sleE = ctx.view.read(kl);
if (!sleE)
return tefNONDIR_EMIT;
// lookup the page
uint64_t const page = sleE->getFieldU64(sfOwnerNode);
auto node = ctx.view.read(keylet::page(keylet::emittedDir(), page));
if (!node)
{
JLOG(ctx.j.warn())
<< "applyTransaction: orphaned emitted txn detected. keylet="
<< to_string(kl.key);
// RH TODO: work out how to safely delete the object
return tefNONDIR_EMIT;
}
auto entries = node->getFieldV256(sfIndexes);
auto it = std::find(entries.begin(), entries.end(), kl.key);
if (entries.end() == it)
{
JLOG(ctx.j.warn()) << "applyTransaction: orphaned emitted txn "
"detected (2). keylet="
<< to_string(kl.key);
// RH TODO: work out how to safely delete the object
return tefNONDIR_EMIT;
}
}
return tesSUCCESS;
}
@@ -1437,14 +1474,15 @@ 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);
// 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)
@@ -1698,6 +1736,8 @@ 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 +1766,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 +2009,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

@@ -164,8 +164,6 @@ invoke_preflight(PreflightContext const& ctx)
return invoke_preflight_helper<Import>(ctx);
case ttINVOKE:
return invoke_preflight_helper<Invoke>(ctx);
case ttREMIT:
return invoke_preflight_helper<Remit>(ctx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -285,8 +283,6 @@ invoke_preclaim(PreclaimContext const& ctx)
return invoke_preclaim<Import>(ctx);
case ttINVOKE:
return invoke_preclaim<Invoke>(ctx);
case ttREMIT:
return invoke_preclaim<Remit>(ctx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -368,8 +364,6 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
return Import::calculateBaseFee(view, tx);
case ttINVOKE:
return Invoke::calculateBaseFee(view, tx);
case ttREMIT:
return Remit::calculateBaseFee(view, tx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -549,10 +543,6 @@ invoke_apply(ApplyContext& ctx)
Invoke p(ctx);
return p();
}
case ttREMIT: {
Remit p(ctx);
return p();
}
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:

View File

@@ -389,18 +389,6 @@ rippleCredit(
bool bCheckIssuer,
beast::Journal j);
// Send regardless of limits.
// --> saAmount: Amount/currency/issuer to deliver to receiver.
// <-- saActual: Amount actually cost. Sender pays fees.
TER
rippleSend(
ApplyView& view,
AccountID const& uSenderID,
AccountID const& uReceiverID,
STAmount const& saAmount,
STAmount& saActual,
beast::Journal j);
[[nodiscard]] TER
accountSend(
ApplyView& view,
@@ -644,9 +632,9 @@ trustTransferAllowed(
std::is_same<V, ApplyView>::value || std::is_same<V, Sandbox>::value);
typedef typename std::conditional<
std::is_same<V, ReadView const>::value,
std::shared_ptr<SLE const>,
std::shared_ptr<SLE>>::type SLEPtr;
std::is_same<V, ApplyView>::value,
std::shared_ptr<SLE>,
std::shared_ptr<SLE const>>::type SLEPtr;
if (isBadCurrency(issue.currency))
return tecNO_PERMISSION;

View File

@@ -1150,7 +1150,7 @@ rippleCredit(
// Send regardless of limits.
// --> saAmount: Amount/currency/issuer to deliver to receiver.
// <-- saActual: Amount actually cost. Sender pays fees.
TER
static TER
rippleSend(
ApplyView& view,
AccountID const& uSenderID,

View File

@@ -354,7 +354,7 @@ extern uint256 const featureImport;
extern uint256 const featureXahauGenesis;
extern uint256 const featureHooksUpdate1;
extern uint256 const fixXahauV1;
extern uint256 const featureRemit;
extern uint256 const fixXahauV2;
} // namespace ripple

View File

@@ -285,8 +285,6 @@ enum LedgerSpecificFlags {
0x20000000, // True, reject new trustlines (only if no issued assets)
lsfURITokenIssuer =
0x40000000, // True, has minted tokens in the past
lsfDisallowIncomingRemit = // True, no remits allowed to this account
0x80000000,
// ltOFFER
lsfPassive = 0x00010000,

View File

@@ -552,7 +552,6 @@ extern SF_ACCOUNT const sfEmitCallback;
// account (uncommon)
extern SF_ACCOUNT const sfHookAccount;
extern SF_ACCOUNT const sfNFTokenMinter;
extern SF_ACCOUNT const sfInform;
// path set
extern SField const sfPaths;
@@ -563,7 +562,6 @@ extern SF_VECTOR256 const sfHashes;
extern SF_VECTOR256 const sfAmendments;
extern SF_VECTOR256 const sfNFTokenOffers;
extern SF_VECTOR256 const sfHookNamespaces;
extern SF_VECTOR256 const sfURITokenIDs;
// inner object
// OBJECT/1 is reserved for end of object
@@ -592,8 +590,6 @@ extern SField const sfHookGrant;
extern SField const sfActiveValidator;
extern SField const sfImportVLKey;
extern SField const sfHookEmission;
extern SField const sfMintURIToken;
extern SField const sfAmountEntry;
// array of objects (common)
// ARRAY/1 is reserved for end of array
@@ -621,7 +617,6 @@ extern SField const sfGenesisMints;
extern SField const sfActiveValidators;
extern SField const sfImportVLKeys;
extern SField const sfHookEmissions;
extern SField const sfAmounts;
//------------------------------------------------------------------------------

View File

@@ -183,6 +183,7 @@ enum TEFcodes : TERUnderlyingType {
tefNFTOKEN_IS_NOT_TRANSFERABLE,
tefPAST_IMPORT_SEQ,
tefPAST_IMPORT_VL_SEQ,
tefNONDIR_EMIT,
};
//------------------------------------------------------------------------------

View File

@@ -86,7 +86,6 @@ constexpr std::uint32_t asfDisallowIncomingNFTokenOffer = 12;
constexpr std::uint32_t asfDisallowIncomingCheck = 13;
constexpr std::uint32_t asfDisallowIncomingPayChan = 14;
constexpr std::uint32_t asfDisallowIncomingTrustline = 15;
constexpr std::uint32_t asfDisallowIncomingRemit = 16;
// OfferCreate flags:
constexpr std::uint32_t tfPassive = 0x00010000;

View File

@@ -146,10 +146,6 @@ enum TxType : std::uint16_t
ttURITOKEN_CREATE_SELL_OFFER = 48,
ttURITOKEN_CANCEL_SELL_OFFER = 49,
/* A payment transactor that delivers only the exact amounts specified, creating accounts and TLs as needed
* that the sender pays for. */
ttREMIT = 95,
/** This transaction can only be used by the genesis account, which is controlled exclusively by
* rewards/governance hooks, to print new XRP to be delivered directly to an array of destinations,
* according to reward schedule */

View File

@@ -460,7 +460,7 @@ REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::De
REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixXahauV1, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixXahauV2, Supported::yes, VoteBehavior::DefaultNo);
// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.

View File

@@ -141,21 +141,6 @@ InnerObjectFormats::InnerObjectFormats()
{sfPublicKey, soeREQUIRED},
{sfAccount, soeOPTIONAL},
});
add(sfAmountEntry.jsonName.c_str(),
sfAmountEntry.getCode(),
{
{sfAmount, soeREQUIRED},
{sfFlags, soeOPTIONAL},
});
add(sfMintURIToken.jsonName.c_str(),
sfMintURIToken.getCode(),
{
{sfURI, soeREQUIRED},
{sfDigest, soeOPTIONAL},
{sfFlags, soeOPTIONAL},
});
}
InnerObjectFormats const&

View File

@@ -305,7 +305,6 @@ CONSTRUCT_TYPED_SFIELD(sfEmitCallback, "EmitCallback", ACCOUNT,
// account (uncommon)
CONSTRUCT_TYPED_SFIELD(sfHookAccount, "HookAccount", ACCOUNT, 16);
CONSTRUCT_TYPED_SFIELD(sfInform, "Inform", ACCOUNT, 99);
// vector of 256-bit
CONSTRUCT_TYPED_SFIELD(sfIndexes, "Indexes", VECTOR256, 1, SField::sMD_Never);
@@ -313,7 +312,6 @@ CONSTRUCT_TYPED_SFIELD(sfHashes, "Hashes", VECTOR25
CONSTRUCT_TYPED_SFIELD(sfAmendments, "Amendments", VECTOR256, 3);
CONSTRUCT_TYPED_SFIELD(sfNFTokenOffers, "NFTokenOffers", VECTOR256, 4);
CONSTRUCT_TYPED_SFIELD(sfHookNamespaces, "HookNamespaces", VECTOR256, 5);
CONSTRUCT_TYPED_SFIELD(sfURITokenIDs, "URITokenIDs", VECTOR256, 99);
// path set
CONSTRUCT_UNTYPED_SFIELD(sfPaths, "Paths", PATHSET, 1);
@@ -348,8 +346,6 @@ CONSTRUCT_UNTYPED_SFIELD(sfGenesisMint, "GenesisMint", OBJECT,
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidator, "ActiveValidator", OBJECT, 95);
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKey, "ImportVLKey", OBJECT, 94);
CONSTRUCT_UNTYPED_SFIELD(sfHookEmission, "HookEmission", OBJECT, 93);
CONSTRUCT_UNTYPED_SFIELD(sfMintURIToken, "MintURIToken", OBJECT, 92);
CONSTRUCT_UNTYPED_SFIELD(sfAmountEntry, "AmountEntry", OBJECT, 91);
// array of objects
// ARRAY/1 is reserved for end of array
@@ -374,7 +370,6 @@ CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY,
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95);
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94);
CONSTRUCT_UNTYPED_SFIELD(sfHookEmissions, "HookEmissions", ARRAY, 93);
CONSTRUCT_UNTYPED_SFIELD(sfAmounts, "Amounts", ARRAY, 92);
// clang-format on

View File

@@ -115,6 +115,7 @@ transResults()
MAKE_ERROR(tefTOO_BIG, "Transaction affects too many items."),
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
MAKE_ERROR(tefNONDIR_EMIT, "An emitted txn was injected into the ledger without a corresponding directory entry."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),

View File

@@ -116,21 +116,6 @@ TxFormats::TxFormats()
},
commonFields);
add(jss::Remit,
ttREMIT,
{
{sfDestination, soeREQUIRED},
{sfAmounts, soeOPTIONAL},
{sfURITokenIDs, soeOPTIONAL},
{sfMintURIToken, soeOPTIONAL},
{sfInvoiceID, soeOPTIONAL},
{sfDestinationTag, soeOPTIONAL},
{sfTicketSequence, soeOPTIONAL},
{sfBlob, soeOPTIONAL},
{sfInform, soeOPTIONAL},
},
commonFields);
add(jss::EscrowCreate,
ttESCROW_CREATE,
{

View File

@@ -117,7 +117,6 @@ JSS(Payment); // transaction type.
JSS(PaymentChannelClaim); // transaction type.
JSS(PaymentChannelCreate); // transaction type.
JSS(PaymentChannelFund); // transaction type.
JSS(Remit); // transaction type.
JSS(RippleState); // ledger type.
JSS(SLE_hit_rate); // out: GetCounts.
JSS(SetFee); // transaction type.
@@ -354,11 +353,12 @@ JSS(ident); // in: AccountCurrencies, AccountInfo,
// OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry, DownloadShard
// out: STLedgerEntry,
// LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(in_queue);
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry, DownloadShard
// out: STLedgerEntry,
// LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us);
JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid

View File

@@ -141,6 +141,8 @@ doCrawlShards(RPC::JsonContext&);
Json::Value
doStop(RPC::JsonContext&);
Json::Value
doInject(RPC::JsonContext&);
Json::Value
doSubmit(RPC::JsonContext&);
Json::Value
doSubmitMultiSigned(RPC::JsonContext&);

View File

@@ -422,7 +422,6 @@ doServerDefinitions(RPC::JsonContext& context)
uint32_t curLgrSeq = context.ledgerMaster.getValidatedLedger()->info().seq;
// static values used for cache
static thread_local uint32_t lastGenerated =
0; // last ledger seq it was generated
static thread_local Json::Value lastFeatures{
@@ -430,6 +429,7 @@ doServerDefinitions(RPC::JsonContext& context)
static thread_local uint256
lastFeatureHash; // the hash of the features JSON last time
// it was generated
// if a flag ledger has passed since it was last generated, regenerate it,
// update the cache above
if (curLgrSeq > ((lastGenerated >> 8) + 1) << 8 || lastGenerated == 0)

View File

@@ -20,6 +20,7 @@
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/tx/apply.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
@@ -39,6 +40,51 @@ getFailHard(RPC::JsonContext const& context)
context.params["fail_hard"].asBool());
}
// {
// tx_blob: serialized tx
// }
// Only for debug use!!!!
Json::Value
doInject(RPC::JsonContext& context)
{
if (context.role != Role::ADMIN)
return RPC::make_error(
rpcNOT_SUPPORTED, "Signing is not supported by this server.");
if (context.role != Role::ADMIN)
return rpcError(rpcNO_PERMISSION);
Json::Value jvResult;
auto ret = strUnHex(context.params[jss::tx_blob].asString());
if (!ret || !ret->size())
return rpcError(rpcINVALID_PARAMS);
SerialIter sitTrans(makeSlice(*ret));
std::shared_ptr<STTx const> stpTrans;
try
{
stpTrans = std::make_shared<STTx const>(std::ref(sitTrans));
}
catch (std::exception& e)
{
jvResult[jss::error] = "invalidTransaction";
jvResult[jss::error_exception] = e.what();
jvResult[jss::in_queue] = false;
return jvResult;
}
context.app.getTxQ().debugTxInject(*stpTrans);
jvResult[jss::tx_json] = stpTrans->getJson(JsonOptions::none);
jvResult[jss::in_queue] = true;
return jvResult;
}
// {
// tx_json: <object>,
// secret: <secret>

View File

@@ -141,6 +141,7 @@ Handler const handlerArray[]{
{"ripple_path_find", byRef(&doRipplePathFind), Role::USER, NO_CONDITION},
{"sign", byRef(&doSign), Role::USER, NO_CONDITION},
{"sign_for", byRef(&doSignFor), Role::USER, NO_CONDITION},
{"inject", byRef(&doInject), Role::USER, NEEDS_CURRENT_LEDGER},
{"submit", byRef(&doSubmit), Role::USER, NEEDS_CURRENT_LEDGER},
{"submit_multisigned",
byRef(&doSubmitMultiSigned),

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,9 @@
*/
//==============================================================================
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/tx/apply.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/PayChan.h>
#include <ripple/protocol/jss.h>
@@ -4331,7 +4334,220 @@ private:
}
void
testWithFeats(FeatureBitset features)
testEmittedTxnReliability(FeatureBitset features)
{
testcase("emitted txn reliability");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{
*this,
network::makeNetworkConfig(21337, "10", "1000000", "200000"),
features};
auto const account = Account("alice");
auto const dest = Account("bob");
env.fund(XRP(1000), account, dest);
env.close();
auto setHook = [](test::jtx::Account const& account) {
std::string const createCodeHex =
"0061736D0100000001350860057F7F7F7F7F017E60017F017E60047F7F7F7F"
"017E60037F7F7E017E60027F7F017E60037F7F7F017E60027F7F017F600001"
"7E02CD010D03656E76057472616365000003656E760C6574786E5F72657365"
"727665000103656E760A7574696C5F6163636964000203656E760974726163"
"655F6E756D000303656E760C686F6F6B5F6163636F756E74000403656E760A"
"6F74786E5F6669656C64000503656E7608726F6C6C6261636B000303656E76"
"025F67000603656E7606616363657074000303656E760A6C65646765725F73"
"6571000703656E760C6574786E5F64657461696C73000403656E760D657478"
"6E5F6665655F62617365000403656E7604656D697400020303020101050301"
"0002062B077F0141C08C040B7F004180080B7F0041B40C0B7F004180080B7F"
"0041C08C040B7F0041000B7F0041010B070F02046362616B000D04686F6F6B"
"000E0AE1930002AC800001017F230041106B220124002001200036020C41F6"
"0B411A41B70A4119410010001A200141106A240042000BAE930002017F017C"
"230041B0056B22012400200120003602AC0541E40B411141840A4110410010"
"001A410110011A200120014190056A411441940A4123100237038805418C08"
"410320012903880510031A200141F0046A411410041A2001200141D0046A41"
"144181802010053E02CC0441E409411120013402CC0410031A20012802CC04"
"411448044041910C4123420110061A0B200141003602C804200141013602C8"
"04200141003602C4040340418180B48178411510071A4100210020012802C8"
"04047F20012802C4044114480520000B4101710440200120012802C4042001"
"41F0046A6A2D000020012802C404200141D0046A6A2D0000463602C8042001"
"20012802C40441016A3602C4040C010B0B20012802C804450440419B08411D"
"420210081A0B200120014190046A413041818018100537038804200142C084"
"3D370380040240200129038804420852044041D00A41CE0041D40841CD0041"
"0010001A0C010B419F0B41C40041A10941C300410010001A200120012D0090"
"04410776047E427E052001310097042001310090044291A2C480B001834238"
"862001310091044230867C2001310092044228867C2001310093044220867C"
"2001310094044218867C2001310095044210867C2001310096044208867C7C"
"0B3703F803419008410A20012903F80310031A20012903F80342A08D065504"
"402001027E20012903F803B94400000040E17A843FA2220299440000000000"
"00E0436304402002B00C010B428080808080808080807F0B370380040B0B41"
"F609410D20012903800410031A2001200141E0016A3602DC01200120012903"
"80043703B801200141003602B401200141003602B001200110093E02AC0120"
"0141C0016A411410041A200141003A00AB0120012802DC0141123A00002001"
"2802DC0120012D00AB014108763A000120012802DC0120012D00AB013A0002"
"200120012802DC0141036A3602DC0120014180808080783602A40120014102"
"3A00A30120012802DC0120012D00A301410F7141206A3A000020012802DC01"
"20012802A4014118763A000120012802DC0120012802A4014110763A000220"
"012802DC0120012802A4014108763A000320012802DC0120012802A4013A00"
"04200120012802DC0141056A3602DC01200120012802B00136029C01200141"
"033A009B0120012802DC0120012D009B01410F7141206A3A000020012802DC"
"01200128029C014118763A000120012802DC01200128029C014110763A0002"
"20012802DC01200128029C014108763A000320012802DC01200128029C013A"
"0004200120012802DC0141056A3602DC012001410036029401200141043A00"
"930120012802DC0120012D009301410F7141206A3A000020012802DC012001"
"280294014118763A000120012802DC012001280294014110763A0002200128"
"02DC012001280294014108763A000320012802DC012001280294013A000420"
"0120012802DC0141056A3602DC01200120012802B40136028C012001410E3A"
"008B0120012802DC0120012D008B01410F7141206A3A000020012802DC0120"
"0128028C014118763A000120012802DC01200128028C014110763A00022001"
"2802DC01200128028C014108763A000320012802DC01200128028C013A0004"
"200120012802DC0141056A3602DC01200120012802AC0141016A3602840120"
"01411A3A00830120012802DC0141203A000020012802DC0120012D0083013A"
"000120012802DC012001280284014118763A000220012802DC012001280284"
"014110763A000320012802DC012001280284014108763A000420012802DC01"
"2001280284013A0005200120012802DC0141066A3602DC01200120012802AC"
"0141056A36027C2001411B3A007B20012802DC0141203A000020012802DC01"
"20012D007B3A000120012802DC01200128027C4118763A000220012802DC01"
"200128027C4110763A000320012802DC01200128027C4108763A0004200128"
"02DC01200128027C3A0005200120012802DC0141066A3602DC01200141013A"
"007A200120012903B80137037020012802DC0120012D007A410F7141E0006A"
"3A000020012802DC012001290370423888423F8342407D3C000120012802DC"
"01200129037042308842FF01833C000220012802DC01200129037042288842"
"FF01833C000320012802DC01200129037042208842FF01833C000420012802"
"DC01200129037042188842FF01833C000520012802DC012001290370421088"
"42FF01833C000620012802DC01200129037042088842FF01833C0007200128"
"02DC01200129037042FF01833C0008200120012802DC0141096A3602DC0120"
"0120012802DC0136026C200141083A006B2001420037036020012802DC0120"
"012D006B410F7141E0006A3A000020012802DC012001290360423888423F83"
"42407D3C000120012802DC01200129036042308842FF01833C000220012802"
"DC01200129036042288842FF01833C000320012802DC012001290360422088"
"42FF01833C000420012802DC01200129036042188842FF01833C0005200128"
"02DC01200129036042108842FF01833C000620012802DC0120012903604208"
"8842FF01833C000720012802DC01200129036042FF01833C00082001200128"
"02DC0141096A3602DC0120012802DC0141F3003A000020012802DC0141213A"
"000120012802DC01420037030220012802DC01420037030A20012802DC0142"
"0037031220012802DC014200370319200120012802DC0141236A3602DC0120"
"0141013A005F20012802DC0120012D005F4180016A3A000020012802DC0141"
"143A000120012802DC0120012903C00137030220012802DC0120012903C801"
"37030A20012802DC0120012802D001360212200120012802DC0141166A3602"
"DC01200141033A005E20012802DC0120012D005E4180016A3A000020012802"
"DC0141143A000120012802DC0120012903900537030220012802DC01200129"
"03980537030A20012802DC0120012802A005360212200120012802DC014116"
"6A3602DC01200120012802DC01418E02100A3703502001200141E0016A418E"
"02100B370348200141083A004720012001290348370338200128026C20012D"
"0047410F7141E0006A3A0000200128026C2001290338423888423F8342407D"
"3C0001200128026C200129033842308842FF01833C0002200128026C200129"
"033842288842FF01833C0003200128026C200129033842208842FF01833C00"
"04200128026C200129033842188842FF01833C0005200128026C2001290338"
"42108842FF01833C0006200128026C200129033842088842FF01833C000720"
"0128026C200129033842FF01833C00082001200128026C41096A36026C2001"
"200141106A4120200141E0016A418E02100C370308418008410B2001290308"
"10031A41B808411C420010081A200141B0056A240042000B0BBB0401004180"
"080BB304656D69745F726573756C7400726574006F74786E5F64726F707300"
"436172626F6E3A20496E636F6D696E67207472616E73616374696F6E004361"
"72626F6E3A20456D6974746564207472616E73616374696F6E00436172626F"
"6E3A204E6F6E2D787270207472616E73616374696F6E206465746563746564"
"2C2073656E64696E672064656661756C7420313030302064726F707320746F"
"207266436172626F6E00436172626F6E3A20585250207472616E7361637469"
"6F6E2064657465637465642C20636F6D707574696E6720312520746F207365"
"6E6420746F207266436172626F6E006163636F756E745F6669656C645F6C65"
"6E0064726F70735F746F5F73656E6400436172626F6E3A2073746172746564"
"0072504D68375069396374363939695A5554576179744A556F48634A376367"
"797A694B00436172626F6E3A2063616C6C6261636B2063616C6C65642E0022"
"436172626F6E3A204E6F6E2D787270207472616E73616374696F6E20646574"
"65637465642C2073656E64696E672064656661756C7420313030302064726F"
"707320746F207266436172626F6E220022436172626F6E3A20585250207472"
"616E73616374696F6E2064657465637465642C20636F6D707574696E672031"
"2520746F2073656E6420746F207266436172626F6E220022436172626F6E3A"
"2073746172746564220022436172626F6E3A2063616C6C6261636B2063616C"
"6C65642E2200436172626F6E3A2073664163636F756E74206669656C64206D"
"697373696E67212121";
Json::Value jhv = hso(createCodeHex);
jhv[jss::HookOn] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffbfff"
"ff";
Json::Value jv = ripple::test::jtx::hook(account, {{jhv}}, 0);
return jv;
};
env(setHook(account), HSFEE);
env.close();
// ttINVOKE
env(invoke::invoke(account), fee(XRP(1)), ter(tesSUCCESS));
env.close();
Blob txBlob;
auto meta = env.meta();
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
for (auto const& node : meta->getFieldArray(sfAffectedNodes))
{
SField const& metaType = node.getFName();
uint16_t nodeType = node.getFieldU16(sfLedgerEntryType);
if (metaType == sfCreatedNode && nodeType == ltEMITTED_TXN)
{
auto const& nf = const_cast<ripple::STObject&>(node)
.getField(sfNewFields)
.downcast<STObject>();
auto const& et = const_cast<ripple::STObject&>(nf)
.getField(sfEmittedTxn)
.downcast<STObject>();
txBlob = et.getSerializer().getData();
break;
}
}
env.close();
auto const preDest = env.balance(dest);
bool const withFix = env.current()->rules().enabled(fixXahauV2);
bool didApply;
TER terRes;
env.app().openLedger().modify([&](OpenView& view, beast::Journal j) {
auto const tx =
std::make_unique<STTx>(Slice{txBlob.data(), txBlob.size()});
std::tie(terRes, didApply) =
ripple::apply(env.app(), view, *tx, tapNONE, env.journal);
bool const applyResult = withFix ? false : true;
if (withFix)
{
BEAST_EXPECT(terRes == tefNONDIR_EMIT);
}
else
{
BEAST_EXPECT(terRes == tesSUCCESS);
}
BEAST_EXPECT(didApply == applyResult);
return didApply;
});
env.close();
auto const postDest = env.balance(dest);
auto const postValue = withFix ? XRP(0) : XRP(1);
BEAST_EXPECT(postDest == preDest + postValue);
for (size_t i = 0; i < 4; i++)
{
Json::Value params1;
params1[jss::tx_blob] = strHex(Slice{txBlob.data(), txBlob.size()});
auto const jrr1 = env.rpc("json", "inject", to_string(params1));
env.close();
}
auto const postDest1 = env.balance(dest);
auto const postValue1 = withFix ? XRP(0) : XRP(2);
BEAST_EXPECT(postDest1 == postDest + postValue1);
}
void
testTSH(FeatureBitset features)
{
testAccountSetTSH(features);
testAccountDeleteTSH(features);
@@ -4366,14 +4582,22 @@ private:
testURITokenCreateSellOfferTSH(features);
}
void
testEmittedTxn(FeatureBitset features)
{
testEmittedTxnReliability(features);
}
public:
void
run() override
{
using namespace test::jtx;
auto const sa = supported_amendments();
testWithFeats(sa - fixXahauV1);
testWithFeats(sa);
testTSH(sa - fixXahauV1);
testTSH(sa);
testEmittedTxn(sa - fixXahauV2);
testEmittedTxn(sa);
}
};

View File

@@ -60,7 +60,7 @@ namespace test {
// * @param pass if the Tx should be applied successfully
// * @return true if meet the expectation of apply result
// */
inline bool
bool
applyAndTestUNLRResult(
jtx::Env& env,
OpenView& view,
@@ -1324,4 +1324,4 @@ createUNLRTx(
}
} // namespace test
} // namespace ripple
} // namespace ripple

View File

@@ -57,7 +57,6 @@
#include <test/jtx/quality.h>
#include <test/jtx/rate.h>
#include <test/jtx/regkey.h>
#include <test/jtx/remit.h>
#include <test/jtx/require.h>
#include <test/jtx/requires.h>
#include <test/jtx/reward.h>

View File

@@ -17,7 +17,6 @@
*/
//==============================================================================
#include <ripple/protocol/jss.h>
#include <test/jtx/Env.h>
#include <test/jtx/acctdelete.h>

View File

@@ -1,85 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 XRPL Labs
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/protocol/jss.h>
#include <test/jtx/remit.h>
namespace ripple {
namespace test {
namespace jtx {
namespace remit {
Json::Value
remit(jtx::Account const& account, jtx::Account const& dest)
{
using namespace jtx;
Json::Value jv;
jv[jss::TransactionType] = jss::Remit;
jv[jss::Account] = account.human();
jv[jss::Destination] = dest.human();
return jv;
}
void
amts::operator()(Env& env, JTx& jt) const
{
auto& ja = jt.jv[sfAmounts.getJsonName()];
for (std::size_t i = 0; i < amts_.size(); ++i)
{
ja[i][sfAmountEntry.jsonName] = Json::Value{};
ja[i][sfAmountEntry.jsonName][jss::Amount] =
amts_[i].getJson(JsonOptions::none);
}
jt.jv[sfAmounts.jsonName] = ja;
}
void
blob::operator()(Env& env, JTx& jt) const
{
jt.jv[sfBlob.jsonName] = blob_;
}
void
inform::operator()(Env& env, JTx& jt) const
{
jt.jv[sfInform.jsonName] = inform_.human();
}
void
token_ids::operator()(Env& env, JTx& jt) const
{
for (std::size_t i = 0; i < token_ids_.size(); ++i)
{
jt.jv[sfURITokenIDs.jsonName] = Json::arrayValue;
jt.jv[sfURITokenIDs.jsonName][i] = token_ids_[i];
}
}
void
uri::operator()(Env& env, JTx& jt) const
{
jt.jv[sfMintURIToken.jsonName] = Json::Value{};
jt.jv[sfMintURIToken.jsonName][sfURI.jsonName] = strHex(uri_);
;
}
} // namespace remit
} // namespace jtx
} // namespace test
} // namespace ripple

View File

@@ -1,119 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 XRPL Labs
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TEST_JTX_REMIT_H_INCLUDED
#define RIPPLE_TEST_JTX_REMIT_H_INCLUDED
#include <ripple/protocol/STAmount.h>
#include <test/jtx/Account.h>
#include <test/jtx/Env.h>
namespace ripple {
namespace test {
namespace jtx {
namespace remit {
Json::Value
remit(jtx::Account const& account, jtx::Account const& dest);
/** Sets the optional Amount on a JTx. */
class amts
{
private:
std::vector<STAmount> amts_;
public:
explicit amts(std::vector<STAmount> const& amts) : amts_(amts)
{
}
void
operator()(Env&, JTx& jtx) const;
};
/** Set the optional "Blob" on a JTx */
class blob
{
private:
std::string blob_;
public:
explicit blob(std::string const& blob) : blob_(blob)
{
}
void
operator()(Env&, JTx& jtx) const;
};
/** Sets the optional "Inform" on a JTx. */
class inform
{
private:
jtx::Account inform_;
public:
explicit inform(jtx::Account const& inform) : inform_(inform)
{
}
void
operator()(Env&, JTx& jtx) const;
};
/** Sets the optional "URITokenIDs" on a JTx. */
class token_ids
{
private:
std::vector<std::string> token_ids_;
public:
explicit token_ids(std::vector<std::string> const& token_ids)
: token_ids_(token_ids)
{
}
void
operator()(Env&, JTx& jtx) const;
};
/** Set the optional "sfMintURIToken" on a JTx */
class uri
{
private:
std::string uri_;
public:
explicit uri(std::string const& uri) : uri_(uri)
{
}
void
operator()(Env&, JTx& jtx) const;
};
} // namespace remit
} // namespace jtx
} // namespace test
} // namespace ripple
#endif // RIPPLE_TEST_JTX_REMIT_H_INCLUDED