mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-05 11:15:48 +00:00
Compare commits
1 Commits
sha-optimi
...
remit_squa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a15cfef3b |
@@ -455,6 +455,7 @@ target_sources (rippled PRIVATE
|
|||||||
src/ripple/app/tx/impl/GenesisMint.cpp
|
src/ripple/app/tx/impl/GenesisMint.cpp
|
||||||
src/ripple/app/tx/impl/Import.cpp
|
src/ripple/app/tx/impl/Import.cpp
|
||||||
src/ripple/app/tx/impl/Invoke.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/SetSignerList.cpp
|
||||||
src/ripple/app/tx/impl/SetTrust.cpp
|
src/ripple/app/tx/impl/SetTrust.cpp
|
||||||
src/ripple/app/tx/impl/SignerEntries.cpp
|
src/ripple/app/tx/impl/SignerEntries.cpp
|
||||||
@@ -740,6 +741,7 @@ if (tests)
|
|||||||
src/test/app/RCLCensorshipDetector_test.cpp
|
src/test/app/RCLCensorshipDetector_test.cpp
|
||||||
src/test/app/RCLValidations_test.cpp
|
src/test/app/RCLValidations_test.cpp
|
||||||
src/test/app/Regression_test.cpp
|
src/test/app/Regression_test.cpp
|
||||||
|
src/test/app/Remit_test.cpp
|
||||||
src/test/app/SHAMapStore_test.cpp
|
src/test/app/SHAMapStore_test.cpp
|
||||||
src/test/app/SetAuth_test.cpp
|
src/test/app/SetAuth_test.cpp
|
||||||
src/test/app/SetRegularKey_test.cpp
|
src/test/app/SetRegularKey_test.cpp
|
||||||
@@ -888,6 +890,7 @@ if (tests)
|
|||||||
src/test/jtx/impl/rate.cpp
|
src/test/jtx/impl/rate.cpp
|
||||||
src/test/jtx/impl/regkey.cpp
|
src/test/jtx/impl/regkey.cpp
|
||||||
src/test/jtx/impl/reward.cpp
|
src/test/jtx/impl/reward.cpp
|
||||||
|
src/test/jtx/impl/remit.cpp
|
||||||
src/test/jtx/impl/sendmax.cpp
|
src/test/jtx/impl/sendmax.cpp
|
||||||
src/test/jtx/impl/seq.cpp
|
src/test/jtx/impl/seq.cpp
|
||||||
src/test/jtx/impl/sig.cpp
|
src/test/jtx/impl/sig.cpp
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ echo "START BUILDING (HOST)"
|
|||||||
echo "Cleaning previously built binary"
|
echo "Cleaning previously built binary"
|
||||||
rm -f release-build/xahaud
|
rm -f release-build/xahaud
|
||||||
|
|
||||||
BUILD_CORES=$(echo "scale=0 ; `nproc` / 3" | bc)
|
BUILD_CORES=$(echo "scale=0 ; `nproc` / 1.337" | bc)
|
||||||
|
|
||||||
if [[ "$GITHUB_REPOSITORY" == "" ]]; then
|
if [[ "$GITHUB_REPOSITORY" == "" ]]; then
|
||||||
#Default
|
#Default
|
||||||
|
|||||||
@@ -597,7 +597,8 @@ ValidNewAccountRoot::finalize(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT) &&
|
if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT ||
|
||||||
|
tt == ttREMIT) &&
|
||||||
result == tesSUCCESS)
|
result == tesSUCCESS)
|
||||||
{
|
{
|
||||||
std::uint32_t const startingSeq{
|
std::uint32_t const startingSeq{
|
||||||
|
|||||||
501
src/ripple/app/tx/impl/Remit.cpp
Normal file
501
src/ripple/app/tx/impl/Remit.cpp
Normal file
@@ -0,0 +1,501 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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) || !view().rules().enabled(featureURIToken))
|
||||||
|
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
|
||||||
54
src/ripple/app/tx/impl/Remit.h
Normal file
54
src/ripple/app/tx/impl/Remit.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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,6 +577,14 @@ SetAccount::doApply()
|
|||||||
uFlagsOut |= lsfDisallowIncomingTrustline;
|
uFlagsOut |= lsfDisallowIncomingTrustline;
|
||||||
else if (uClearFlag == asfDisallowIncomingTrustline)
|
else if (uClearFlag == asfDisallowIncomingTrustline)
|
||||||
uFlagsOut &= ~lsfDisallowIncomingTrustline;
|
uFlagsOut &= ~lsfDisallowIncomingTrustline;
|
||||||
|
|
||||||
|
if (ctx_.view().rules().enabled(featureRemit))
|
||||||
|
{
|
||||||
|
if (uSetFlag == asfDisallowIncomingRemit)
|
||||||
|
uFlagsOut |= lsfDisallowIncomingRemit;
|
||||||
|
else if (uClearFlag == asfDisallowIncomingRemit)
|
||||||
|
uFlagsOut &= ~lsfDisallowIncomingRemit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uFlagsIn != uFlagsOut)
|
if (uFlagsIn != uFlagsOut)
|
||||||
|
|||||||
@@ -103,57 +103,7 @@ URIToken::preflight(PreflightContext const& ctx)
|
|||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!([](std::vector<uint8_t> const& u) -> bool {
|
if (!validateUTF8(uri))
|
||||||
// this code is from
|
|
||||||
// https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
|
|
||||||
uint8_t const* s = (uint8_t const*)u.data();
|
|
||||||
uint8_t const* end = s + u.size();
|
|
||||||
while (s < end)
|
|
||||||
{
|
|
||||||
if (*s < 0x80)
|
|
||||||
/* 0xxxxxxx */
|
|
||||||
s++;
|
|
||||||
else if ((s[0] & 0xe0) == 0xc0)
|
|
||||||
{
|
|
||||||
/* 110XXXXx 10xxxxxx */
|
|
||||||
if ((s[1] & 0xc0) != 0x80 ||
|
|
||||||
(s[0] & 0xfe) == 0xc0) /* overlong? */
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
s += 2;
|
|
||||||
}
|
|
||||||
else if ((s[0] & 0xf0) == 0xe0)
|
|
||||||
{
|
|
||||||
/* 1110XXXX 10Xxxxxx 10xxxxxx */
|
|
||||||
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
|
|
||||||
(s[0] == 0xe0 &&
|
|
||||||
(s[1] & 0xe0) == 0x80) || /* overlong? */
|
|
||||||
(s[0] == 0xed &&
|
|
||||||
(s[1] & 0xe0) == 0xa0) || /* surrogate? */
|
|
||||||
(s[0] == 0xef && s[1] == 0xbf &&
|
|
||||||
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
s += 3;
|
|
||||||
}
|
|
||||||
else if ((s[0] & 0xf8) == 0xf0)
|
|
||||||
{
|
|
||||||
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
|
|
||||||
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
|
|
||||||
(s[3] & 0xc0) != 0x80 ||
|
|
||||||
(s[0] == 0xf0 &&
|
|
||||||
(s[1] & 0xf0) == 0x80) || /* overlong? */
|
|
||||||
(s[0] == 0xf4 && s[1] > 0x8f) ||
|
|
||||||
s[0] > 0xf4) /* > U+10FFFF? */
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
s += 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})(uri))
|
|
||||||
{
|
{
|
||||||
JLOG(ctx.j.warn()) << "Malformed transaction. URI must be a "
|
JLOG(ctx.j.warn()) << "Malformed transaction. URI must be a "
|
||||||
"valid utf-8 string.";
|
"valid utf-8 string.";
|
||||||
|
|||||||
@@ -30,6 +30,56 @@ namespace ripple {
|
|||||||
class URIToken : public Transactor
|
class URIToken : public Transactor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
bool inline static validateUTF8(std::vector<uint8_t> const& u)
|
||||||
|
{
|
||||||
|
// this code is from
|
||||||
|
// https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
|
||||||
|
uint8_t const* s = (uint8_t const*)u.data();
|
||||||
|
uint8_t const* end = s + u.size();
|
||||||
|
while (s < end)
|
||||||
|
{
|
||||||
|
if (*s < 0x80)
|
||||||
|
/* 0xxxxxxx */
|
||||||
|
s++;
|
||||||
|
else if ((s[0] & 0xe0) == 0xc0)
|
||||||
|
{
|
||||||
|
/* 110XXXXx 10xxxxxx */
|
||||||
|
if ((s[1] & 0xc0) != 0x80 ||
|
||||||
|
(s[0] & 0xfe) == 0xc0) /* overlong? */
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
s += 2;
|
||||||
|
}
|
||||||
|
else if ((s[0] & 0xf0) == 0xe0)
|
||||||
|
{
|
||||||
|
/* 1110XXXX 10Xxxxxx 10xxxxxx */
|
||||||
|
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
|
||||||
|
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
|
||||||
|
(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
|
||||||
|
(s[0] == 0xef && s[1] == 0xbf &&
|
||||||
|
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
s += 3;
|
||||||
|
}
|
||||||
|
else if ((s[0] & 0xf8) == 0xf0)
|
||||||
|
{
|
||||||
|
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
|
||||||
|
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
|
||||||
|
(s[3] & 0xc0) != 0x80 ||
|
||||||
|
(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
|
||||||
|
(s[0] == 0xf4 && s[1] > 0x8f) ||
|
||||||
|
s[0] > 0xf4) /* > U+10FFFF? */
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
s += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||||
|
|
||||||
explicit URIToken(ApplyContext& ctx) : Transactor(ctx)
|
explicit URIToken(ApplyContext& ctx) : Transactor(ctx)
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ invoke_preflight(PreflightContext const& ctx)
|
|||||||
return invoke_preflight_helper<Import>(ctx);
|
return invoke_preflight_helper<Import>(ctx);
|
||||||
case ttINVOKE:
|
case ttINVOKE:
|
||||||
return invoke_preflight_helper<Invoke>(ctx);
|
return invoke_preflight_helper<Invoke>(ctx);
|
||||||
|
case ttREMIT:
|
||||||
|
return invoke_preflight_helper<Remit>(ctx);
|
||||||
case ttURITOKEN_MINT:
|
case ttURITOKEN_MINT:
|
||||||
case ttURITOKEN_BURN:
|
case ttURITOKEN_BURN:
|
||||||
case ttURITOKEN_BUY:
|
case ttURITOKEN_BUY:
|
||||||
@@ -283,6 +285,8 @@ invoke_preclaim(PreclaimContext const& ctx)
|
|||||||
return invoke_preclaim<Import>(ctx);
|
return invoke_preclaim<Import>(ctx);
|
||||||
case ttINVOKE:
|
case ttINVOKE:
|
||||||
return invoke_preclaim<Invoke>(ctx);
|
return invoke_preclaim<Invoke>(ctx);
|
||||||
|
case ttREMIT:
|
||||||
|
return invoke_preclaim<Remit>(ctx);
|
||||||
case ttURITOKEN_MINT:
|
case ttURITOKEN_MINT:
|
||||||
case ttURITOKEN_BURN:
|
case ttURITOKEN_BURN:
|
||||||
case ttURITOKEN_BUY:
|
case ttURITOKEN_BUY:
|
||||||
@@ -364,6 +368,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
return Import::calculateBaseFee(view, tx);
|
return Import::calculateBaseFee(view, tx);
|
||||||
case ttINVOKE:
|
case ttINVOKE:
|
||||||
return Invoke::calculateBaseFee(view, tx);
|
return Invoke::calculateBaseFee(view, tx);
|
||||||
|
case ttREMIT:
|
||||||
|
return Remit::calculateBaseFee(view, tx);
|
||||||
case ttURITOKEN_MINT:
|
case ttURITOKEN_MINT:
|
||||||
case ttURITOKEN_BURN:
|
case ttURITOKEN_BURN:
|
||||||
case ttURITOKEN_BUY:
|
case ttURITOKEN_BUY:
|
||||||
@@ -544,6 +550,10 @@ invoke_apply(ApplyContext& ctx)
|
|||||||
Invoke p(ctx);
|
Invoke p(ctx);
|
||||||
return p();
|
return p();
|
||||||
}
|
}
|
||||||
|
case ttREMIT: {
|
||||||
|
Remit p(ctx);
|
||||||
|
return p();
|
||||||
|
}
|
||||||
case ttURITOKEN_MINT:
|
case ttURITOKEN_MINT:
|
||||||
case ttURITOKEN_BURN:
|
case ttURITOKEN_BURN:
|
||||||
case ttURITOKEN_BUY:
|
case ttURITOKEN_BUY:
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <ripple/ledger/OpenView.h>
|
#include <ripple/ledger/OpenView.h>
|
||||||
#include <ripple/ledger/RawView.h>
|
#include <ripple/ledger/RawView.h>
|
||||||
#include <ripple/ledger/ReadView.h>
|
#include <ripple/ledger/ReadView.h>
|
||||||
|
#include <ripple/ledger/Sandbox.h>
|
||||||
#include <ripple/protocol/Feature.h>
|
#include <ripple/protocol/Feature.h>
|
||||||
#include <ripple/protocol/Protocol.h>
|
#include <ripple/protocol/Protocol.h>
|
||||||
#include <ripple/protocol/Rate.h>
|
#include <ripple/protocol/Rate.h>
|
||||||
@@ -388,6 +389,18 @@ rippleCredit(
|
|||||||
bool bCheckIssuer,
|
bool bCheckIssuer,
|
||||||
beast::Journal j);
|
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
|
[[nodiscard]] TER
|
||||||
accountSend(
|
accountSend(
|
||||||
ApplyView& view,
|
ApplyView& view,
|
||||||
@@ -627,12 +640,12 @@ trustTransferAllowed(
|
|||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_same<V, ReadView const>::value ||
|
std::is_same<V, ReadView const>::value ||
|
||||||
std::is_same<V, ApplyView>::value);
|
std::is_same<V, ApplyView>::value || std::is_same<V, Sandbox>::value);
|
||||||
|
|
||||||
typedef typename std::conditional<
|
typedef typename std::conditional<
|
||||||
std::is_same<V, ApplyView>::value,
|
std::is_same<V, ReadView const>::value,
|
||||||
std::shared_ptr<SLE>,
|
std::shared_ptr<SLE const>,
|
||||||
std::shared_ptr<SLE const>>::type SLEPtr;
|
std::shared_ptr<SLE>>::type SLEPtr;
|
||||||
|
|
||||||
if (isBadCurrency(issue.currency))
|
if (isBadCurrency(issue.currency))
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
|||||||
@@ -1150,7 +1150,7 @@ rippleCredit(
|
|||||||
// Send regardless of limits.
|
// Send regardless of limits.
|
||||||
// --> saAmount: Amount/currency/issuer to deliver to receiver.
|
// --> saAmount: Amount/currency/issuer to deliver to receiver.
|
||||||
// <-- saActual: Amount actually cost. Sender pays fees.
|
// <-- saActual: Amount actually cost. Sender pays fees.
|
||||||
static TER
|
TER
|
||||||
rippleSend(
|
rippleSend(
|
||||||
ApplyView& view,
|
ApplyView& view,
|
||||||
AccountID const& uSenderID,
|
AccountID const& uSenderID,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace detail {
|
|||||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||||
// the actual number of amendments. A LogicError on startup will verify this.
|
// the actual number of amendments. A LogicError on startup will verify this.
|
||||||
static constexpr std::size_t numFeatures = 66;
|
static constexpr std::size_t numFeatures = 67;
|
||||||
|
|
||||||
/** Amendments that this server supports and the default voting behavior.
|
/** Amendments that this server supports and the default voting behavior.
|
||||||
Whether they are enabled depends on the Rules defined in the validated
|
Whether they are enabled depends on the Rules defined in the validated
|
||||||
@@ -354,6 +354,7 @@ extern uint256 const featureImport;
|
|||||||
extern uint256 const featureXahauGenesis;
|
extern uint256 const featureXahauGenesis;
|
||||||
extern uint256 const featureHooksUpdate1;
|
extern uint256 const featureHooksUpdate1;
|
||||||
extern uint256 const fixURITokenV1;
|
extern uint256 const fixURITokenV1;
|
||||||
|
extern uint256 const featureRemit;
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ enum LedgerSpecificFlags {
|
|||||||
0x20000000, // True, reject new trustlines (only if no issued assets)
|
0x20000000, // True, reject new trustlines (only if no issued assets)
|
||||||
lsfURITokenIssuer =
|
lsfURITokenIssuer =
|
||||||
0x40000000, // True, has minted tokens in the past
|
0x40000000, // True, has minted tokens in the past
|
||||||
|
lsfDisallowIncomingRemit = // True, no remits allowed to this account
|
||||||
|
0x80000000,
|
||||||
|
|
||||||
// ltOFFER
|
// ltOFFER
|
||||||
lsfPassive = 0x00010000,
|
lsfPassive = 0x00010000,
|
||||||
|
|||||||
@@ -552,6 +552,7 @@ extern SF_ACCOUNT const sfEmitCallback;
|
|||||||
// account (uncommon)
|
// account (uncommon)
|
||||||
extern SF_ACCOUNT const sfHookAccount;
|
extern SF_ACCOUNT const sfHookAccount;
|
||||||
extern SF_ACCOUNT const sfNFTokenMinter;
|
extern SF_ACCOUNT const sfNFTokenMinter;
|
||||||
|
extern SF_ACCOUNT const sfInform;
|
||||||
|
|
||||||
// path set
|
// path set
|
||||||
extern SField const sfPaths;
|
extern SField const sfPaths;
|
||||||
@@ -562,6 +563,7 @@ extern SF_VECTOR256 const sfHashes;
|
|||||||
extern SF_VECTOR256 const sfAmendments;
|
extern SF_VECTOR256 const sfAmendments;
|
||||||
extern SF_VECTOR256 const sfNFTokenOffers;
|
extern SF_VECTOR256 const sfNFTokenOffers;
|
||||||
extern SF_VECTOR256 const sfHookNamespaces;
|
extern SF_VECTOR256 const sfHookNamespaces;
|
||||||
|
extern SF_VECTOR256 const sfURITokenIDs;
|
||||||
|
|
||||||
// inner object
|
// inner object
|
||||||
// OBJECT/1 is reserved for end of object
|
// OBJECT/1 is reserved for end of object
|
||||||
@@ -590,6 +592,8 @@ extern SField const sfHookGrant;
|
|||||||
extern SField const sfActiveValidator;
|
extern SField const sfActiveValidator;
|
||||||
extern SField const sfImportVLKey;
|
extern SField const sfImportVLKey;
|
||||||
extern SField const sfHookEmission;
|
extern SField const sfHookEmission;
|
||||||
|
extern SField const sfMintURIToken;
|
||||||
|
extern SField const sfAmountEntry;
|
||||||
|
|
||||||
// array of objects (common)
|
// array of objects (common)
|
||||||
// ARRAY/1 is reserved for end of array
|
// ARRAY/1 is reserved for end of array
|
||||||
@@ -617,6 +621,7 @@ extern SField const sfGenesisMints;
|
|||||||
extern SField const sfActiveValidators;
|
extern SField const sfActiveValidators;
|
||||||
extern SField const sfImportVLKeys;
|
extern SField const sfImportVLKeys;
|
||||||
extern SField const sfHookEmissions;
|
extern SField const sfHookEmissions;
|
||||||
|
extern SField const sfAmounts;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ constexpr std::uint32_t asfDisallowIncomingNFTokenOffer = 12;
|
|||||||
constexpr std::uint32_t asfDisallowIncomingCheck = 13;
|
constexpr std::uint32_t asfDisallowIncomingCheck = 13;
|
||||||
constexpr std::uint32_t asfDisallowIncomingPayChan = 14;
|
constexpr std::uint32_t asfDisallowIncomingPayChan = 14;
|
||||||
constexpr std::uint32_t asfDisallowIncomingTrustline = 15;
|
constexpr std::uint32_t asfDisallowIncomingTrustline = 15;
|
||||||
|
constexpr std::uint32_t asfDisallowIncomingRemit = 16;
|
||||||
|
|
||||||
// OfferCreate flags:
|
// OfferCreate flags:
|
||||||
constexpr std::uint32_t tfPassive = 0x00010000;
|
constexpr std::uint32_t tfPassive = 0x00010000;
|
||||||
|
|||||||
@@ -146,6 +146,10 @@ enum TxType : std::uint16_t
|
|||||||
ttURITOKEN_CREATE_SELL_OFFER = 48,
|
ttURITOKEN_CREATE_SELL_OFFER = 48,
|
||||||
ttURITOKEN_CANCEL_SELL_OFFER = 49,
|
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
|
/** 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,
|
* rewards/governance hooks, to print new XRP to be delivered directly to an array of destinations,
|
||||||
* according to reward schedule */
|
* according to reward schedule */
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::De
|
|||||||
REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes);
|
REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes);
|
||||||
REGISTER_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes);
|
REGISTER_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes);
|
||||||
REGISTER_FIX (fixURITokenV1, Supported::yes, VoteBehavior::DefaultNo);
|
REGISTER_FIX (fixURITokenV1, Supported::yes, VoteBehavior::DefaultNo);
|
||||||
|
REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo);
|
||||||
|
|
||||||
// The following amendments are obsolete, but must remain supported
|
// The following amendments are obsolete, but must remain supported
|
||||||
// because they could potentially get enabled.
|
// because they could potentially get enabled.
|
||||||
|
|||||||
@@ -141,6 +141,21 @@ InnerObjectFormats::InnerObjectFormats()
|
|||||||
{sfPublicKey, soeREQUIRED},
|
{sfPublicKey, soeREQUIRED},
|
||||||
{sfAccount, soeOPTIONAL},
|
{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&
|
InnerObjectFormats const&
|
||||||
|
|||||||
@@ -305,6 +305,7 @@ CONSTRUCT_TYPED_SFIELD(sfEmitCallback, "EmitCallback", ACCOUNT,
|
|||||||
|
|
||||||
// account (uncommon)
|
// account (uncommon)
|
||||||
CONSTRUCT_TYPED_SFIELD(sfHookAccount, "HookAccount", ACCOUNT, 16);
|
CONSTRUCT_TYPED_SFIELD(sfHookAccount, "HookAccount", ACCOUNT, 16);
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfInform, "Inform", ACCOUNT, 99);
|
||||||
|
|
||||||
// vector of 256-bit
|
// vector of 256-bit
|
||||||
CONSTRUCT_TYPED_SFIELD(sfIndexes, "Indexes", VECTOR256, 1, SField::sMD_Never);
|
CONSTRUCT_TYPED_SFIELD(sfIndexes, "Indexes", VECTOR256, 1, SField::sMD_Never);
|
||||||
@@ -312,6 +313,7 @@ CONSTRUCT_TYPED_SFIELD(sfHashes, "Hashes", VECTOR25
|
|||||||
CONSTRUCT_TYPED_SFIELD(sfAmendments, "Amendments", VECTOR256, 3);
|
CONSTRUCT_TYPED_SFIELD(sfAmendments, "Amendments", VECTOR256, 3);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfNFTokenOffers, "NFTokenOffers", VECTOR256, 4);
|
CONSTRUCT_TYPED_SFIELD(sfNFTokenOffers, "NFTokenOffers", VECTOR256, 4);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfHookNamespaces, "HookNamespaces", VECTOR256, 5);
|
CONSTRUCT_TYPED_SFIELD(sfHookNamespaces, "HookNamespaces", VECTOR256, 5);
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfURITokenIDs, "URITokenIDs", VECTOR256, 99);
|
||||||
|
|
||||||
// path set
|
// path set
|
||||||
CONSTRUCT_UNTYPED_SFIELD(sfPaths, "Paths", PATHSET, 1);
|
CONSTRUCT_UNTYPED_SFIELD(sfPaths, "Paths", PATHSET, 1);
|
||||||
@@ -346,6 +348,8 @@ CONSTRUCT_UNTYPED_SFIELD(sfGenesisMint, "GenesisMint", OBJECT,
|
|||||||
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidator, "ActiveValidator", OBJECT, 95);
|
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidator, "ActiveValidator", OBJECT, 95);
|
||||||
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKey, "ImportVLKey", OBJECT, 94);
|
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKey, "ImportVLKey", OBJECT, 94);
|
||||||
CONSTRUCT_UNTYPED_SFIELD(sfHookEmission, "HookEmission", OBJECT, 93);
|
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 of objects
|
||||||
// ARRAY/1 is reserved for end of array
|
// ARRAY/1 is reserved for end of array
|
||||||
@@ -370,6 +374,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY,
|
|||||||
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95);
|
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95);
|
||||||
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94);
|
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94);
|
||||||
CONSTRUCT_UNTYPED_SFIELD(sfHookEmissions, "HookEmissions", ARRAY, 93);
|
CONSTRUCT_UNTYPED_SFIELD(sfHookEmissions, "HookEmissions", ARRAY, 93);
|
||||||
|
CONSTRUCT_UNTYPED_SFIELD(sfAmounts, "Amounts", ARRAY, 92);
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,21 @@ TxFormats::TxFormats()
|
|||||||
},
|
},
|
||||||
commonFields);
|
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,
|
add(jss::EscrowCreate,
|
||||||
ttESCROW_CREATE,
|
ttESCROW_CREATE,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ JSS(Payment); // transaction type.
|
|||||||
JSS(PaymentChannelClaim); // transaction type.
|
JSS(PaymentChannelClaim); // transaction type.
|
||||||
JSS(PaymentChannelCreate); // transaction type.
|
JSS(PaymentChannelCreate); // transaction type.
|
||||||
JSS(PaymentChannelFund); // transaction type.
|
JSS(PaymentChannelFund); // transaction type.
|
||||||
|
JSS(Remit); // transaction type.
|
||||||
JSS(RippleState); // ledger type.
|
JSS(RippleState); // ledger type.
|
||||||
JSS(SLE_hit_rate); // out: GetCounts.
|
JSS(SLE_hit_rate); // out: GetCounts.
|
||||||
JSS(SetFee); // transaction type.
|
JSS(SetFee); // transaction type.
|
||||||
|
|||||||
@@ -422,11 +422,11 @@ doServerDefinitions(RPC::JsonContext& context)
|
|||||||
uint32_t curLgrSeq = context.ledgerMaster.getValidatedLedger()->info().seq;
|
uint32_t curLgrSeq = context.ledgerMaster.getValidatedLedger()->info().seq;
|
||||||
|
|
||||||
// static values used for cache
|
// static values used for cache
|
||||||
static uint32_t lastGenerated = 0; // last ledger seq it was generated
|
static thread_local uint32_t lastGenerated = 0; // last ledger seq it was generated
|
||||||
static Json::Value lastFeatures{
|
static thread_local Json::Value lastFeatures{
|
||||||
Json::objectValue}; // the actual features JSON last generated
|
Json::objectValue}; // the actual features JSON last generated
|
||||||
static uint256 lastFeatureHash; // the hash of the features JSON last time
|
static thread_local uint256 lastFeatureHash; // the hash of the features JSON last time
|
||||||
// it was generated
|
// it was generated
|
||||||
|
|
||||||
// if a flag ledger has passed since it was last generated, regenerate it,
|
// if a flag ledger has passed since it was last generated, regenerate it,
|
||||||
// update the cache above
|
// update the cache above
|
||||||
|
|||||||
1222
src/test/app/Remit_test.cpp
Normal file
1222
src/test/app/Remit_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -52,13 +52,20 @@ namespace test {
|
|||||||
* @param hasToReEnable if expect ToDisable in ledger
|
* @param hasToReEnable if expect ToDisable in ledger
|
||||||
* @return true if meet all three expectation
|
* @return true if meet all three expectation
|
||||||
*/
|
*/
|
||||||
bool
|
bool inline negUnlSizeTest(
|
||||||
negUnlSizeTest(
|
std::shared_ptr<ripple::Ledger const> const& l,
|
||||||
std::shared_ptr<Ledger const> const& l,
|
|
||||||
size_t size,
|
size_t size,
|
||||||
bool hasToDisable,
|
bool hasToDisable,
|
||||||
bool hasToReEnable);
|
bool hasToReEnable)
|
||||||
|
{
|
||||||
|
bool sameSize = l->negativeUNL().size() == size;
|
||||||
|
bool sameToDisable =
|
||||||
|
(l->validatorToDisable() != std::nullopt) == hasToDisable;
|
||||||
|
bool sameToReEnable =
|
||||||
|
(l->validatorToReEnable() != std::nullopt) == hasToReEnable;
|
||||||
|
|
||||||
|
return sameSize && sameToDisable && sameToReEnable;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Try to apply a ttUNL_MODIFY Tx, and test the apply result
|
* Try to apply a ttUNL_MODIFY Tx, and test the apply result
|
||||||
*
|
*
|
||||||
@@ -68,8 +75,18 @@ negUnlSizeTest(
|
|||||||
* @param pass if the Tx should be applied successfully
|
* @param pass if the Tx should be applied successfully
|
||||||
* @return true if meet the expectation of apply result
|
* @return true if meet the expectation of apply result
|
||||||
*/
|
*/
|
||||||
bool
|
bool inline applyAndTestResult(
|
||||||
applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass);
|
jtx::Env& env,
|
||||||
|
ripple::OpenView& view,
|
||||||
|
ripple::STTx const& tx,
|
||||||
|
bool pass)
|
||||||
|
{
|
||||||
|
auto res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal);
|
||||||
|
if (pass)
|
||||||
|
return res.first == tesSUCCESS;
|
||||||
|
else
|
||||||
|
return res.first == tefFAILURE || res.first == temDISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the content of negative UNL entries (public key and ledger sequence)
|
* Verify the content of negative UNL entries (public key and ledger sequence)
|
||||||
@@ -1912,31 +1929,6 @@ BEAST_DEFINE_TESTSUITE(NegativeUNLVoteFilterValidations, consensus, ripple);
|
|||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
bool
|
|
||||||
negUnlSizeTest(
|
|
||||||
std::shared_ptr<Ledger const> const& l,
|
|
||||||
size_t size,
|
|
||||||
bool hasToDisable,
|
|
||||||
bool hasToReEnable)
|
|
||||||
{
|
|
||||||
bool sameSize = l->negativeUNL().size() == size;
|
|
||||||
bool sameToDisable =
|
|
||||||
(l->validatorToDisable() != std::nullopt) == hasToDisable;
|
|
||||||
bool sameToReEnable =
|
|
||||||
(l->validatorToReEnable() != std::nullopt) == hasToReEnable;
|
|
||||||
|
|
||||||
return sameSize && sameToDisable && sameToReEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
|
|
||||||
{
|
|
||||||
auto res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal);
|
|
||||||
if (pass)
|
|
||||||
return res.first == tesSUCCESS;
|
|
||||||
else
|
|
||||||
return res.first == tefFAILURE || res.first == temDISABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VerifyPubKeyAndSeq(
|
VerifyPubKeyAndSeq(
|
||||||
|
|||||||
@@ -61,28 +61,57 @@ namespace test {
|
|||||||
// * @param hasToReEnable if expect ToDisable in ledger
|
// * @param hasToReEnable if expect ToDisable in ledger
|
||||||
// * @return true if meet all three expectation
|
// * @return true if meet all three expectation
|
||||||
// */
|
// */
|
||||||
inline bool
|
/*
|
||||||
|
bool
|
||||||
|
inline
|
||||||
negUnlSizeTest(
|
negUnlSizeTest(
|
||||||
std::shared_ptr<Ledger const> const& l,
|
std::shared_ptr<ripple::Ledger const> const& l,
|
||||||
size_t size,
|
size_t size,
|
||||||
bool hasToDisable,
|
bool hasToDisable,
|
||||||
bool hasToReEnable);
|
bool hasToReEnable)
|
||||||
|
{
|
||||||
|
bool sameSize = l->negativeUNL().size() == size;
|
||||||
|
bool sameToDisable =
|
||||||
|
(l->validatorToDisable() != std::nullopt) == hasToDisable;
|
||||||
|
bool sameToReEnable =
|
||||||
|
(l->validatorToReEnable() != std::nullopt) == hasToReEnable;
|
||||||
|
|
||||||
// /**
|
return sameSize && sameToDisable && sameToReEnable;
|
||||||
// * Try to apply a ttUNL_MODIFY Tx, and test the apply result
|
}
|
||||||
// *
|
*/
|
||||||
// * @param env the test environment
|
/**
|
||||||
// * @param view the OpenView of the ledger
|
* Try to apply a ttUNL_MODIFY Tx, and test the apply result
|
||||||
// * @param tx the ttUNL_MODIFY Tx
|
*
|
||||||
// * @param pass if the Tx should be applied successfully
|
* @param env the test environment
|
||||||
// * @return true if meet the expectation of apply result
|
* @param view the OpenView of the ledger
|
||||||
// */
|
* @param tx the ttUNL_MODIFY Tx
|
||||||
|
* @param pass if the Tx should be applied successfully
|
||||||
|
* @return true if meet the expectation of apply result
|
||||||
|
*/
|
||||||
|
/*
|
||||||
bool
|
bool
|
||||||
applyAndTestUNLRResult(
|
inline
|
||||||
jtx::Env& env,
|
applyAndTestResult(jtx::Env& env, ripple::OpenView& view, ripple::STTx const&
|
||||||
OpenView& view,
|
tx, bool pass)
|
||||||
STTx const& tx,
|
{
|
||||||
bool pass);
|
auto res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal);
|
||||||
|
if (pass)
|
||||||
|
return res.first == tesSUCCESS;
|
||||||
|
else
|
||||||
|
return res.first == tefFAILURE || res.first == temDISABLED;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
inline bool
|
||||||
|
applyAndTestUNLRResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
|
||||||
|
{
|
||||||
|
auto res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal);
|
||||||
|
if (pass)
|
||||||
|
return res.first == tesSUCCESS;
|
||||||
|
else
|
||||||
|
return res.first == tefFAILURE || res.first == temDISABLED ||
|
||||||
|
res.first == temMALFORMED ||
|
||||||
|
res.first == telIMPORT_VL_KEY_NOT_RECOGNISED;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the content of UNL Report entries (public key and ledger sequence)
|
* Verify the content of UNL Report entries (public key and ledger sequence)
|
||||||
@@ -1235,34 +1264,6 @@ BEAST_DEFINE_TESTSUITE(UNLReportVoteNewValidator, consensus, ripple);
|
|||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
inline bool
|
|
||||||
negUnlSizeTest(
|
|
||||||
std::shared_ptr<Ledger const> const& l,
|
|
||||||
size_t size,
|
|
||||||
bool hasToDisable,
|
|
||||||
bool hasToReEnable)
|
|
||||||
{
|
|
||||||
bool sameSize = l->negativeUNL().size() == size;
|
|
||||||
bool sameToDisable =
|
|
||||||
(l->validatorToDisable() != std::nullopt) == hasToDisable;
|
|
||||||
bool sameToReEnable =
|
|
||||||
(l->validatorToReEnable() != std::nullopt) == hasToReEnable;
|
|
||||||
|
|
||||||
return sameSize && sameToDisable && sameToReEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
applyAndTestUNLRResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
|
|
||||||
{
|
|
||||||
auto res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal);
|
|
||||||
if (pass)
|
|
||||||
return res.first == tesSUCCESS;
|
|
||||||
else
|
|
||||||
return res.first == tefFAILURE || res.first == temDISABLED ||
|
|
||||||
res.first == temMALFORMED ||
|
|
||||||
res.first == telIMPORT_VL_KEY_NOT_RECOGNISED;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VerifyUNLRPubKeyAndSeq(
|
VerifyUNLRPubKeyAndSeq(
|
||||||
std::shared_ptr<Ledger const> const& l,
|
std::shared_ptr<Ledger const> const& l,
|
||||||
@@ -1412,6 +1413,7 @@ createUNLRTx(
|
|||||||
return STTx(ttUNL_REPORT, fill);
|
return STTx(ttUNL_REPORT, fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
inline STTx
|
inline STTx
|
||||||
createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey)
|
createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey)
|
||||||
{
|
{
|
||||||
@@ -1422,35 +1424,24 @@ createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey)
|
|||||||
};
|
};
|
||||||
return STTx(ttUNL_MODIFY, fill);
|
return STTx(ttUNL_MODIFY, fill);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
inline std::size_t
|
inline std::size_t
|
||||||
countTx(std::shared_ptr<SHAMap> const& txSet)
|
countTx(std::shared_ptr<SHAMap> const& txSet)
|
||||||
{
|
{
|
||||||
/*uint64_t counter = 0;
|
|
||||||
if (txSet)
|
|
||||||
for (auto const& item : *txSet)
|
|
||||||
{
|
|
||||||
|
|
||||||
SerialIter sit(item.slice());
|
|
||||||
auto tx = std::make_shared<STTx
|
|
||||||
const>(SerialIter{sit.getSlice(sit.getVLDataLength())});
|
|
||||||
|
|
||||||
if (tx->getFieldU16(sfTransactionType) == ttUNL_MODIFY)
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
std::size_t count = 0;
|
std::size_t count = 0;
|
||||||
for (auto i = txSet->begin(); i != txSet->end(); ++i)
|
for (auto i = txSet->begin(); i != txSet->end(); ++i)
|
||||||
{
|
{
|
||||||
// RH TODO: why does the above parse??
|
|
||||||
auto raw = i->slice();
|
auto raw = i->slice();
|
||||||
if (raw[0] == 0x12U && raw[1] == 0 && raw[2] == 0x66U)
|
if (raw[0] == 0x12U && raw[1] == 0 && raw[2] == 0x66U)
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
inline bool
|
inline bool
|
||||||
applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
|
applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
|
||||||
{
|
{
|
||||||
@@ -1460,6 +1451,7 @@ applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
|
|||||||
else
|
else
|
||||||
return res.first == tefFAILURE || res.first == temDISABLED;
|
return res.first == tefFAILURE || res.first == temDISABLED;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
#include <test/jtx/require.h>
|
#include <test/jtx/require.h>
|
||||||
#include <test/jtx/requires.h>
|
#include <test/jtx/requires.h>
|
||||||
#include <test/jtx/reward.h>
|
#include <test/jtx/reward.h>
|
||||||
|
#include <test/jtx/remit.h>
|
||||||
#include <test/jtx/sendmax.h>
|
#include <test/jtx/sendmax.h>
|
||||||
#include <test/jtx/seq.h>
|
#include <test/jtx/seq.h>
|
||||||
#include <test/jtx/sig.h>
|
#include <test/jtx/sig.h>
|
||||||
|
|||||||
84
src/test/jtx/impl/remit.cpp
Normal file
84
src/test/jtx/impl/remit.cpp
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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
|
||||||
118
src/test/jtx/remit.h
Normal file
118
src/test/jtx/remit.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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