mirror of
https://github.com/Xahau/xahaud.git
synced 2026-01-25 00:55:17 +00:00
Compare commits
13 Commits
remit_old
...
fix-weak-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9df80a90d0 | ||
|
|
f21d3e1e97 | ||
|
|
858055c811 | ||
|
|
5d66f17574 | ||
|
|
00328cb782 | ||
|
|
fdf7ea4174 | ||
|
|
7877ed9704 | ||
|
|
17ccec9ac5 | ||
|
|
de522ac4ae | ||
|
|
74c83a9271 | ||
|
|
66ee96d456 | ||
|
|
b476aea55b | ||
|
|
1b9373e220 |
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ enum TEFcodes : TERUnderlyingType {
|
||||
tefNFTOKEN_IS_NOT_TRANSFERABLE,
|
||||
tefPAST_IMPORT_SEQ,
|
||||
tefPAST_IMPORT_VL_SEQ,
|
||||
tefNONDIR_EMIT,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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&
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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,
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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&);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/acctdelete.h>
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user