mirror of
				https://github.com/Xahau/xahaud.git
				synced 2025-11-04 10:45:50 +00:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1454c1e1a2 | ||
| 
						 | 
					1c335cde2b | ||
| 
						 | 
					228cbeca84 | ||
| 
						 | 
					a816fb862e | ||
| 
						 | 
					9d2efb1498 | ||
| 
						 | 
					32b73c071f | ||
| 
						 | 
					827dc07965 | ||
| 
						 | 
					a13e3425dc | ||
| 
						 | 
					ff6293c955 | ||
| 
						 | 
					215b7e8260 | ||
| 
						 | 
					226574665b | ||
| 
						 | 
					9f650f4258 | ||
| 
						 | 
					ee33c97950 | ||
| 
						 | 
					9d389b9cf0 | ||
| 
						 | 
					43d90e4905 | ||
| 
						 | 
					6854055f70 | ||
| 
						 | 
					bba4281f52 | ||
| 
						 | 
					af952a945f | ||
| 
						 | 
					16553b3778 | ||
| 
						 | 
					5168b2684a | ||
| 
						 | 
					489a47241d | 
@@ -455,6 +455,7 @@ 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
 | 
			
		||||
@@ -740,6 +741,7 @@ 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
 | 
			
		||||
@@ -889,6 +891,7 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -597,7 +597,8 @@ ValidNewAccountRoot::finalize(
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT) &&
 | 
			
		||||
    if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT ||
 | 
			
		||||
         tt == ttREMIT) &&
 | 
			
		||||
        result == tesSUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
        std::uint32_t const startingSeq{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										502
									
								
								src/ripple/app/tx/impl/Remit.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								src/ripple/app/tx/impl/Remit.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,502 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										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;
 | 
			
		||||
        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)
 | 
			
		||||
 
 | 
			
		||||
@@ -164,6 +164,8 @@ 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:
 | 
			
		||||
@@ -283,6 +285,8 @@ 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:
 | 
			
		||||
@@ -364,6 +368,8 @@ 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:
 | 
			
		||||
@@ -543,6 +549,10 @@ 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,6 +389,18 @@ 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,
 | 
			
		||||
@@ -632,9 +644,9 @@ trustTransferAllowed(
 | 
			
		||||
        std::is_same<V, ApplyView>::value || std::is_same<V, Sandbox>::value);
 | 
			
		||||
 | 
			
		||||
    typedef typename std::conditional<
 | 
			
		||||
        std::is_same<V, ApplyView>::value,
 | 
			
		||||
        std::shared_ptr<SLE>,
 | 
			
		||||
        std::shared_ptr<SLE const>>::type SLEPtr;
 | 
			
		||||
        std::is_same<V, ReadView const>::value,
 | 
			
		||||
        std::shared_ptr<SLE const>,
 | 
			
		||||
        std::shared_ptr<SLE>>::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.
 | 
			
		||||
static TER
 | 
			
		||||
TER
 | 
			
		||||
rippleSend(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& uSenderID,
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,7 @@ namespace detail {
 | 
			
		||||
// 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
 | 
			
		||||
// 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.
 | 
			
		||||
   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 featureHooksUpdate1;
 | 
			
		||||
extern uint256 const fixXahauV1;
 | 
			
		||||
extern uint256 const featureRemit;
 | 
			
		||||
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -285,6 +285,8 @@ 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,6 +552,7 @@ 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;
 | 
			
		||||
@@ -562,6 +563,7 @@ 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
 | 
			
		||||
@@ -590,6 +592,8 @@ 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
 | 
			
		||||
@@ -617,6 +621,7 @@ extern SField const sfGenesisMints;
 | 
			
		||||
extern SField const sfActiveValidators;
 | 
			
		||||
extern SField const sfImportVLKeys;
 | 
			
		||||
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 asfDisallowIncomingPayChan         = 14;
 | 
			
		||||
constexpr std::uint32_t asfDisallowIncomingTrustline       = 15;
 | 
			
		||||
constexpr std::uint32_t asfDisallowIncomingRemit           = 16;
 | 
			
		||||
 | 
			
		||||
// OfferCreate flags:
 | 
			
		||||
constexpr std::uint32_t tfPassive                          = 0x00010000;
 | 
			
		||||
 
 | 
			
		||||
@@ -146,6 +146,10 @@ 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);
 | 
			
		||||
 | 
			
		||||
// The following amendments are obsolete, but must remain supported
 | 
			
		||||
// because they could potentially get enabled.
 | 
			
		||||
 
 | 
			
		||||
@@ -141,6 +141,21 @@ 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,6 +305,7 @@ 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);
 | 
			
		||||
@@ -312,6 +313,7 @@ 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);
 | 
			
		||||
@@ -346,6 +348,8 @@ 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
 | 
			
		||||
@@ -370,6 +374,7 @@ 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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -116,6 +116,21 @@ 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,6 +117,7 @@ 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.
 | 
			
		||||
 
 | 
			
		||||
@@ -422,6 +422,7 @@ 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{
 | 
			
		||||
@@ -429,7 +430,6 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1229
									
								
								src/test/app/Remit_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1229
									
								
								src/test/app/Remit_test.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -60,7 +60,7 @@ namespace test {
 | 
			
		||||
//  * @param pass if the Tx should be applied successfully
 | 
			
		||||
//  * @return true if meet the expectation of apply result
 | 
			
		||||
//  */
 | 
			
		||||
bool
 | 
			
		||||
inline bool
 | 
			
		||||
applyAndTestUNLRResult(
 | 
			
		||||
    jtx::Env& env,
 | 
			
		||||
    OpenView& view,
 | 
			
		||||
@@ -1324,4 +1324,4 @@ createUNLRTx(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace test
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,7 @@
 | 
			
		||||
#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,6 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <ripple/protocol/jss.h>
 | 
			
		||||
#include <test/jtx/Env.h>
 | 
			
		||||
#include <test/jtx/acctdelete.h>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								src/test/jtx/impl/remit.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/test/jtx/impl/remit.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										119
									
								
								src/test/jtx/remit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/test/jtx/remit.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    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