Files
rippled/src/ripple/app/tx/impl/SetAccount.cpp
Kenny Lei ecd49e1535 Rename 'NFT' to 'NFToken' in DisallowIncoming flags (#4442)
* Follow-up to #4336
* NFToken is the naming convention in the codebase (rather than NFT)
* Rename `lsfDisallowIncomingNFTOffer` to `lsfDisallowIncomingNFTokenOffer`
* Rename `asfDisallowIncomingNFTOffer` to `asfDisallowIncomingNFTokenOffer`
2023-03-02 13:01:50 -08:00

572 lines
16 KiB
C++

//------------------------------------------------------------------------------
/*
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/SetAccount.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/Quality.h>
#include <ripple/protocol/st.h>
namespace ripple {
TxConsequences
SetAccount::makeTxConsequences(PreflightContext const& ctx)
{
// The SetAccount may be a blocker, but only if it sets or clears
// specific account flags.
auto getTxConsequencesCategory = [](STTx const& tx) {
if (std::uint32_t const uTxFlags = tx.getFlags();
uTxFlags & (tfRequireAuth | tfOptionalAuth))
return TxConsequences::blocker;
if (auto const uSetFlag = tx[~sfSetFlag]; uSetFlag &&
(*uSetFlag == asfRequireAuth || *uSetFlag == asfDisableMaster ||
*uSetFlag == asfAccountTxnID))
return TxConsequences::blocker;
if (auto const uClearFlag = tx[~sfClearFlag]; uClearFlag &&
(*uClearFlag == asfRequireAuth || *uClearFlag == asfDisableMaster ||
*uClearFlag == asfAccountTxnID))
return TxConsequences::blocker;
return TxConsequences::normal;
};
return TxConsequences{ctx.tx, getTxConsequencesCategory(ctx.tx)};
}
NotTEC
SetAccount::preflight(PreflightContext const& ctx)
{
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
auto& tx = ctx.tx;
auto& j = ctx.j;
std::uint32_t const uTxFlags = tx.getFlags();
if (uTxFlags & tfAccountSetMask)
{
JLOG(j.trace()) << "Malformed transaction: Invalid flags set.";
return temINVALID_FLAG;
}
std::uint32_t const uSetFlag = tx.getFieldU32(sfSetFlag);
std::uint32_t const uClearFlag = tx.getFieldU32(sfClearFlag);
if ((uSetFlag != 0) && (uSetFlag == uClearFlag))
{
JLOG(j.trace()) << "Malformed transaction: Set and clear same flag.";
return temINVALID_FLAG;
}
//
// RequireAuth
//
bool bSetRequireAuth =
(uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth);
bool bClearRequireAuth =
(uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth);
if (bSetRequireAuth && bClearRequireAuth)
{
JLOG(j.trace()) << "Malformed transaction: Contradictory flags set.";
return temINVALID_FLAG;
}
//
// RequireDestTag
//
bool bSetRequireDest =
(uTxFlags & tfRequireDestTag) || (uSetFlag == asfRequireDest);
bool bClearRequireDest =
(uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest);
if (bSetRequireDest && bClearRequireDest)
{
JLOG(j.trace()) << "Malformed transaction: Contradictory flags set.";
return temINVALID_FLAG;
}
//
// DisallowXRP
//
bool bSetDisallowXRP =
(uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP);
bool bClearDisallowXRP =
(uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP);
if (bSetDisallowXRP && bClearDisallowXRP)
{
JLOG(j.trace()) << "Malformed transaction: Contradictory flags set.";
return temINVALID_FLAG;
}
// TransferRate
if (tx.isFieldPresent(sfTransferRate))
{
std::uint32_t uRate = tx.getFieldU32(sfTransferRate);
if (uRate && (uRate < QUALITY_ONE))
{
JLOG(j.trace())
<< "Malformed transaction: Transfer rate too small.";
return temBAD_TRANSFER_RATE;
}
if (uRate > 2 * QUALITY_ONE)
{
JLOG(j.trace())
<< "Malformed transaction: Transfer rate too large.";
return temBAD_TRANSFER_RATE;
}
}
// TickSize
if (tx.isFieldPresent(sfTickSize))
{
auto uTickSize = tx[sfTickSize];
if (uTickSize &&
((uTickSize < Quality::minTickSize) ||
(uTickSize > Quality::maxTickSize)))
{
JLOG(j.trace()) << "Malformed transaction: Bad tick size.";
return temBAD_TICK_SIZE;
}
}
if (auto const mk = tx[~sfMessageKey])
{
if (mk->size() && !publicKeyType({mk->data(), mk->size()}))
{
JLOG(j.trace()) << "Invalid message key specified.";
return telBAD_PUBLIC_KEY;
}
}
if (auto const domain = tx[~sfDomain];
domain && domain->size() > maxDomainLength)
{
JLOG(j.trace()) << "domain too long";
return telBAD_DOMAIN;
}
if (ctx.rules.enabled(featureNonFungibleTokensV1))
{
// Configure authorized minting account:
if (uSetFlag == asfAuthorizedNFTokenMinter &&
!tx.isFieldPresent(sfNFTokenMinter))
return temMALFORMED;
if (uClearFlag == asfAuthorizedNFTokenMinter &&
tx.isFieldPresent(sfNFTokenMinter))
return temMALFORMED;
}
return preflight2(ctx);
}
TER
SetAccount::preclaim(PreclaimContext const& ctx)
{
auto const id = ctx.tx[sfAccount];
std::uint32_t const uTxFlags = ctx.tx.getFlags();
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
return terNO_ACCOUNT;
std::uint32_t const uFlagsIn = sle->getFieldU32(sfFlags);
std::uint32_t const uSetFlag = ctx.tx.getFieldU32(sfSetFlag);
// legacy AccountSet flags
bool bSetRequireAuth =
(uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth);
//
// RequireAuth
//
if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth))
{
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
{
JLOG(ctx.j.trace()) << "Retry: Owner directory not empty.";
return (ctx.flags & tapRETRY) ? TER{terOWNERS} : TER{tecOWNERS};
}
}
return tesSUCCESS;
}
TER
SetAccount::doApply()
{
auto const sle = view().peek(keylet::account(account_));
if (!sle)
return tefINTERNAL;
std::uint32_t const uFlagsIn = sle->getFieldU32(sfFlags);
std::uint32_t uFlagsOut = uFlagsIn;
STTx const& tx{ctx_.tx};
std::uint32_t const uSetFlag{tx.getFieldU32(sfSetFlag)};
std::uint32_t const uClearFlag{tx.getFieldU32(sfClearFlag)};
// legacy AccountSet flags
std::uint32_t const uTxFlags{tx.getFlags()};
bool const bSetRequireDest{
(uTxFlags & tfRequireDestTag) || (uSetFlag == asfRequireDest)};
bool const bClearRequireDest{
(uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest)};
bool const bSetRequireAuth{
(uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth)};
bool const bClearRequireAuth{
(uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth)};
bool const bSetDisallowXRP{
(uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP)};
bool const bClearDisallowXRP{
(uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP)};
bool const sigWithMaster{[&tx, &acct = account_]() {
auto const spk = tx.getSigningPubKey();
if (publicKeyType(makeSlice(spk)))
{
PublicKey const signingPubKey(makeSlice(spk));
if (calcAccountID(signingPubKey) == acct)
return true;
}
return false;
}()};
//
// RequireAuth
//
if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth))
{
JLOG(j_.trace()) << "Set RequireAuth.";
uFlagsOut |= lsfRequireAuth;
}
if (bClearRequireAuth && (uFlagsIn & lsfRequireAuth))
{
JLOG(j_.trace()) << "Clear RequireAuth.";
uFlagsOut &= ~lsfRequireAuth;
}
//
// RequireDestTag
//
if (bSetRequireDest && !(uFlagsIn & lsfRequireDestTag))
{
JLOG(j_.trace()) << "Set lsfRequireDestTag.";
uFlagsOut |= lsfRequireDestTag;
}
if (bClearRequireDest && (uFlagsIn & lsfRequireDestTag))
{
JLOG(j_.trace()) << "Clear lsfRequireDestTag.";
uFlagsOut &= ~lsfRequireDestTag;
}
//
// DisallowXRP
//
if (bSetDisallowXRP && !(uFlagsIn & lsfDisallowXRP))
{
JLOG(j_.trace()) << "Set lsfDisallowXRP.";
uFlagsOut |= lsfDisallowXRP;
}
if (bClearDisallowXRP && (uFlagsIn & lsfDisallowXRP))
{
JLOG(j_.trace()) << "Clear lsfDisallowXRP.";
uFlagsOut &= ~lsfDisallowXRP;
}
//
// DisableMaster
//
if ((uSetFlag == asfDisableMaster) && !(uFlagsIn & lsfDisableMaster))
{
if (!sigWithMaster)
{
JLOG(j_.trace()) << "Must use master key to disable master key.";
return tecNEED_MASTER_KEY;
}
if ((!sle->isFieldPresent(sfRegularKey)) &&
(!view().peek(keylet::signers(account_))))
{
// Account has no regular key or multi-signer signer list.
return tecNO_ALTERNATIVE_KEY;
}
JLOG(j_.trace()) << "Set lsfDisableMaster.";
uFlagsOut |= lsfDisableMaster;
}
if ((uClearFlag == asfDisableMaster) && (uFlagsIn & lsfDisableMaster))
{
JLOG(j_.trace()) << "Clear lsfDisableMaster.";
uFlagsOut &= ~lsfDisableMaster;
}
//
// DefaultRipple
//
if (uSetFlag == asfDefaultRipple)
{
JLOG(j_.trace()) << "Set lsfDefaultRipple.";
uFlagsOut |= lsfDefaultRipple;
}
else if (uClearFlag == asfDefaultRipple)
{
JLOG(j_.trace()) << "Clear lsfDefaultRipple.";
uFlagsOut &= ~lsfDefaultRipple;
}
//
// NoFreeze
//
if (uSetFlag == asfNoFreeze)
{
if (!sigWithMaster && !(uFlagsIn & lsfDisableMaster))
{
JLOG(j_.trace()) << "Must use master key to set NoFreeze.";
return tecNEED_MASTER_KEY;
}
JLOG(j_.trace()) << "Set NoFreeze flag";
uFlagsOut |= lsfNoFreeze;
}
// Anyone may set global freeze
if (uSetFlag == asfGlobalFreeze)
{
JLOG(j_.trace()) << "Set GlobalFreeze flag";
uFlagsOut |= lsfGlobalFreeze;
}
// If you have set NoFreeze, you may not clear GlobalFreeze
// This prevents those who have set NoFreeze from using
// GlobalFreeze strategically.
if ((uSetFlag != asfGlobalFreeze) && (uClearFlag == asfGlobalFreeze) &&
((uFlagsOut & lsfNoFreeze) == 0))
{
JLOG(j_.trace()) << "Clear GlobalFreeze flag";
uFlagsOut &= ~lsfGlobalFreeze;
}
//
// Track transaction IDs signed by this account in its root
//
if ((uSetFlag == asfAccountTxnID) && !sle->isFieldPresent(sfAccountTxnID))
{
JLOG(j_.trace()) << "Set AccountTxnID.";
sle->makeFieldPresent(sfAccountTxnID);
}
if ((uClearFlag == asfAccountTxnID) && sle->isFieldPresent(sfAccountTxnID))
{
JLOG(j_.trace()) << "Clear AccountTxnID.";
sle->makeFieldAbsent(sfAccountTxnID);
}
//
// DepositAuth
//
if (view().rules().enabled(featureDepositAuth))
{
if (uSetFlag == asfDepositAuth)
{
JLOG(j_.trace()) << "Set lsfDepositAuth.";
uFlagsOut |= lsfDepositAuth;
}
else if (uClearFlag == asfDepositAuth)
{
JLOG(j_.trace()) << "Clear lsfDepositAuth.";
uFlagsOut &= ~lsfDepositAuth;
}
}
//
// EmailHash
//
if (tx.isFieldPresent(sfEmailHash))
{
uint128 const uHash = tx.getFieldH128(sfEmailHash);
if (!uHash)
{
JLOG(j_.trace()) << "unset email hash";
sle->makeFieldAbsent(sfEmailHash);
}
else
{
JLOG(j_.trace()) << "set email hash";
sle->setFieldH128(sfEmailHash, uHash);
}
}
//
// WalletLocator
//
if (tx.isFieldPresent(sfWalletLocator))
{
uint256 const uHash = tx.getFieldH256(sfWalletLocator);
if (!uHash)
{
JLOG(j_.trace()) << "unset wallet locator";
sle->makeFieldAbsent(sfWalletLocator);
}
else
{
JLOG(j_.trace()) << "set wallet locator";
sle->setFieldH256(sfWalletLocator, uHash);
}
}
//
// MessageKey
//
if (tx.isFieldPresent(sfMessageKey))
{
Blob const messageKey = tx.getFieldVL(sfMessageKey);
if (messageKey.empty())
{
JLOG(j_.debug()) << "set message key";
sle->makeFieldAbsent(sfMessageKey);
}
else
{
JLOG(j_.debug()) << "set message key";
sle->setFieldVL(sfMessageKey, messageKey);
}
}
//
// Domain
//
if (tx.isFieldPresent(sfDomain))
{
Blob const domain = tx.getFieldVL(sfDomain);
if (domain.empty())
{
JLOG(j_.trace()) << "unset domain";
sle->makeFieldAbsent(sfDomain);
}
else
{
JLOG(j_.trace()) << "set domain";
sle->setFieldVL(sfDomain, domain);
}
}
//
// TransferRate
//
if (tx.isFieldPresent(sfTransferRate))
{
std::uint32_t uRate = tx.getFieldU32(sfTransferRate);
if (uRate == 0 || uRate == QUALITY_ONE)
{
JLOG(j_.trace()) << "unset transfer rate";
sle->makeFieldAbsent(sfTransferRate);
}
else
{
JLOG(j_.trace()) << "set transfer rate";
sle->setFieldU32(sfTransferRate, uRate);
}
}
//
// TickSize
//
if (tx.isFieldPresent(sfTickSize))
{
auto uTickSize = tx[sfTickSize];
if ((uTickSize == 0) || (uTickSize == Quality::maxTickSize))
{
JLOG(j_.trace()) << "unset tick size";
sle->makeFieldAbsent(sfTickSize);
}
else
{
JLOG(j_.trace()) << "set tick size";
sle->setFieldU8(sfTickSize, uTickSize);
}
}
// Configure authorized minting account:
if (ctx_.view().rules().enabled(featureNonFungibleTokensV1))
{
if (uSetFlag == asfAuthorizedNFTokenMinter)
sle->setAccountID(sfNFTokenMinter, ctx_.tx[sfNFTokenMinter]);
if (uClearFlag == asfAuthorizedNFTokenMinter &&
sle->isFieldPresent(sfNFTokenMinter))
sle->makeFieldAbsent(sfNFTokenMinter);
}
// Set or clear flags for disallowing various incoming instruments
if (ctx_.view().rules().enabled(featureDisallowIncoming))
{
if (uSetFlag == asfDisallowIncomingNFTokenOffer)
uFlagsOut |= lsfDisallowIncomingNFTokenOffer;
else if (uClearFlag == asfDisallowIncomingNFTokenOffer)
uFlagsOut &= ~lsfDisallowIncomingNFTokenOffer;
if (uSetFlag == asfDisallowIncomingCheck)
uFlagsOut |= lsfDisallowIncomingCheck;
else if (uClearFlag == asfDisallowIncomingCheck)
uFlagsOut &= ~lsfDisallowIncomingCheck;
if (uSetFlag == asfDisallowIncomingPayChan)
uFlagsOut |= lsfDisallowIncomingPayChan;
else if (uClearFlag == asfDisallowIncomingPayChan)
uFlagsOut &= ~lsfDisallowIncomingPayChan;
if (uSetFlag == asfDisallowIncomingTrustline)
uFlagsOut |= lsfDisallowIncomingTrustline;
else if (uClearFlag == asfDisallowIncomingTrustline)
uFlagsOut &= ~lsfDisallowIncomingTrustline;
}
if (uFlagsIn != uFlagsOut)
sle->setFieldU32(sfFlags, uFlagsOut);
return tesSUCCESS;
}
} // namespace ripple