mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-28 17:40:10 +00:00
Merge branch 'develop-nonconan' into icv2-super
This commit is contained in:
@@ -909,6 +909,7 @@ if (tests)
|
||||
src/test/protocol/InnerObjectFormats_test.cpp
|
||||
src/test/protocol/Issue_test.cpp
|
||||
src/test/protocol/Hooks_test.cpp
|
||||
src/test/protocol/Memo_test.cpp
|
||||
src/test/protocol/PublicKey_test.cpp
|
||||
src/test/protocol/Quality_test.cpp
|
||||
src/test/protocol/STAccount_test.cpp
|
||||
|
||||
@@ -90,8 +90,14 @@ CreateCheck::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_DST;
|
||||
}
|
||||
|
||||
if ((sleDst->getFlags() & lsfRequireDestTag) &&
|
||||
!ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
auto const flags = sleDst->getFlags();
|
||||
|
||||
// Check if the destination has disallowed incoming checks
|
||||
if (ctx.view.rules().enabled(featureDisallowIncoming) &&
|
||||
(flags & lsfDisallowIncomingCheck))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if ((flags & lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
|
||||
@@ -165,11 +165,42 @@ NFTokenCreateOffer::preclaim(PreclaimContext const& ctx)
|
||||
return tecUNFUNDED_OFFER;
|
||||
}
|
||||
|
||||
// If a destination is specified, the destination must already be in
|
||||
// the ledger.
|
||||
if (auto const destination = ctx.tx[~sfDestination];
|
||||
destination && !ctx.view.exists(keylet::account(*destination)))
|
||||
return tecNO_DST;
|
||||
if (auto const destination = ctx.tx[~sfDestination])
|
||||
{
|
||||
// If a destination is specified, the destination must already be in
|
||||
// the ledger.
|
||||
auto const sleDst = ctx.view.read(keylet::account(*destination));
|
||||
|
||||
if (!sleDst)
|
||||
return tecNO_DST;
|
||||
|
||||
// check if the destination has disallowed incoming offers
|
||||
if (ctx.view.rules().enabled(featureDisallowIncoming))
|
||||
{
|
||||
// flag cannot be set unless amendment is enabled but
|
||||
// out of an abundance of caution check anyway
|
||||
|
||||
if (sleDst->getFlags() & lsfDisallowIncomingNFTOffer)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const owner = ctx.tx[~sfOwner])
|
||||
{
|
||||
// Check if the owner (buy offer) has disallowed incoming offers
|
||||
if (ctx.view.rules().enabled(featureDisallowIncoming))
|
||||
{
|
||||
auto const sleOwner = ctx.view.read(keylet::account(*owner));
|
||||
|
||||
// defensively check
|
||||
// it should not be possible to specify owner that doesn't exist
|
||||
if (!sleOwner)
|
||||
return tecNO_TARGET;
|
||||
|
||||
if (sleOwner->getFlags() & lsfDisallowIncomingNFTOffer)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -538,6 +538,30 @@ SetAccount::doApply()
|
||||
sle->makeFieldAbsent(sfNFTokenMinter);
|
||||
}
|
||||
|
||||
// Set or clear flags for disallowing various incoming instruments
|
||||
if (ctx_.view().rules().enabled(featureDisallowIncoming))
|
||||
{
|
||||
if (uSetFlag == asfDisallowIncomingNFTOffer)
|
||||
uFlagsOut |= lsfDisallowIncomingNFTOffer;
|
||||
else if (uClearFlag == asfDisallowIncomingNFTOffer)
|
||||
uFlagsOut &= ~lsfDisallowIncomingNFTOffer;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -128,6 +128,20 @@ SetTrust::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// If the destination has opted to disallow incoming trustlines
|
||||
// then honour that flag
|
||||
if (ctx.view.rules().enabled(featureDisallowIncoming))
|
||||
{
|
||||
auto const sleDst = ctx.view.read(keylet::account(uDstAccountID));
|
||||
|
||||
if (!sleDst)
|
||||
return tecNO_DST;
|
||||
|
||||
auto const dstFlags = sleDst->getFlags();
|
||||
if (dstFlags & lsfDisallowIncomingTrustline)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -1464,16 +1464,7 @@ struct RPCCallImp
|
||||
// callbackFuncP.
|
||||
|
||||
// Receive reply
|
||||
if (iStatus == 401)
|
||||
Throw<std::runtime_error>(
|
||||
"incorrect rpcuser or rpcpassword (authorization failed)");
|
||||
else if (
|
||||
(iStatus >= 400) && (iStatus != 400) && (iStatus != 404) &&
|
||||
(iStatus != 500)) // ?
|
||||
Throw<std::runtime_error>(
|
||||
std::string("server returned HTTP error ") +
|
||||
std::to_string(iStatus));
|
||||
else if (strData.empty())
|
||||
if (strData.empty())
|
||||
Throw<std::runtime_error>("no response from server");
|
||||
|
||||
// Parse reply
|
||||
|
||||
@@ -163,12 +163,15 @@ enum warning_code_i {
|
||||
|
||||
namespace RPC {
|
||||
|
||||
/** Maps an rpc error code to its token and default message. */
|
||||
/** Maps an rpc error code to its token, default message, and HTTP status. */
|
||||
struct ErrorInfo
|
||||
{
|
||||
// Default ctor needed to produce an empty std::array during constexpr eval.
|
||||
constexpr ErrorInfo()
|
||||
: code(rpcUNKNOWN), token("unknown"), message("An unknown error code.")
|
||||
: code(rpcUNKNOWN)
|
||||
, token("unknown")
|
||||
, message("An unknown error code.")
|
||||
, http_status(200)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -176,13 +179,26 @@ struct ErrorInfo
|
||||
error_code_i code_,
|
||||
char const* token_,
|
||||
char const* message_)
|
||||
: code(code_), token(token_), message(message_)
|
||||
: code(code_), token(token_), message(message_), http_status(200)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ErrorInfo(
|
||||
error_code_i code_,
|
||||
char const* token_,
|
||||
char const* message_,
|
||||
int http_status_)
|
||||
: code(code_)
|
||||
, token(token_)
|
||||
, message(message_)
|
||||
, http_status(http_status_)
|
||||
{
|
||||
}
|
||||
|
||||
error_code_i code;
|
||||
Json::StaticString token;
|
||||
Json::StaticString message;
|
||||
int http_status;
|
||||
};
|
||||
|
||||
/** Returns an ErrorInfo that reflects the error code. */
|
||||
@@ -332,6 +348,10 @@ not_validator_error()
|
||||
bool
|
||||
contains_error(Json::Value const& json);
|
||||
|
||||
/** Returns http status that corresponds to the error code. */
|
||||
int
|
||||
error_code_http_status(error_code_i code);
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
/** Returns a single string with the contents of an RPC error. */
|
||||
|
||||
@@ -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 = 54;
|
||||
static constexpr std::size_t numFeatures = 55;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -340,6 +340,7 @@ extern uint256 const featureNonFungibleTokensV1_1;
|
||||
extern uint256 const fixTrustLinesToSelf;
|
||||
extern uint256 const fixRemoveNFTokenAutoTrustLine;
|
||||
extern uint256 const featureImmediateOfferKilled;
|
||||
extern uint256 const featureDisallowIncoming;
|
||||
extern uint256 const featurePaychanAndEscrowForTokens;
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -232,6 +232,17 @@ enum LedgerSpecificFlags {
|
||||
lsfDefaultRipple =
|
||||
0x00800000, // True, trust lines allow rippling by default
|
||||
lsfDepositAuth = 0x01000000, // True, all deposits require authorization
|
||||
/* // reserved for Hooks amendment
|
||||
lsfTshCollect = 0x02000000, // True, allow TSH collect-calls to acc hooks
|
||||
*/
|
||||
lsfDisallowIncomingNFTOffer =
|
||||
0x04000000, // True, reject new incoming NFT offers
|
||||
lsfDisallowIncomingCheck =
|
||||
0x08000000, // True, reject new checks
|
||||
lsfDisallowIncomingPayChan =
|
||||
0x10000000, // True, reject new paychans
|
||||
lsfDisallowIncomingTrustline =
|
||||
0x20000000, // True, reject new trustlines (only if no issued assets)
|
||||
|
||||
// ltOFFER
|
||||
lsfPassive = 0x00010000,
|
||||
|
||||
@@ -79,6 +79,13 @@ constexpr std::uint32_t asfGlobalFreeze = 7;
|
||||
constexpr std::uint32_t asfDefaultRipple = 8;
|
||||
constexpr std::uint32_t asfDepositAuth = 9;
|
||||
constexpr std::uint32_t asfAuthorizedNFTokenMinter = 10;
|
||||
/* // reserved for Hooks amendment
|
||||
constexpr std::uint32_t asfTshCollect = 11;
|
||||
*/
|
||||
constexpr std::uint32_t asfDisallowIncomingNFTOffer = 12;
|
||||
constexpr std::uint32_t asfDisallowIncomingCheck = 13;
|
||||
constexpr std::uint32_t asfDisallowIncomingPayChan = 14;
|
||||
constexpr std::uint32_t asfDisallowIncomingTrustline = 15;
|
||||
|
||||
// OfferCreate flags:
|
||||
constexpr std::uint32_t tfPassive = 0x00010000;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -26,105 +27,96 @@ namespace RPC {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// clang-format off
|
||||
// Unordered array of ErrorInfos, so we don't have to maintain the list
|
||||
// ordering by hand.
|
||||
//
|
||||
// This array will be omitted from the object file; only the sorted version
|
||||
// will remain in the object file. But the string literals will remain.
|
||||
constexpr static ErrorInfo unorderedErrorInfos[]{
|
||||
{rpcACT_MALFORMED, "actMalformed", "Account malformed."},
|
||||
{rpcACT_NOT_FOUND, "actNotFound", "Account not found."},
|
||||
{rpcALREADY_MULTISIG, "alreadyMultisig", "Already multisigned."},
|
||||
{rpcALREADY_SINGLE_SIG, "alreadySingleSig", "Already single-signed."},
|
||||
{rpcAMENDMENT_BLOCKED, "amendmentBlocked", "Amendment blocked, need upgrade."},
|
||||
{rpcEXPIRED_VALIDATOR_LIST, "unlBlocked", "Validator list expired."},
|
||||
{rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range."},
|
||||
{rpcBAD_KEY_TYPE, "badKeyType", "Bad key type."},
|
||||
{rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid."},
|
||||
{rpcBAD_ISSUER, "badIssuer", "Issuer account malformed."},
|
||||
{rpcBAD_MARKET, "badMarket", "No such market."},
|
||||
{rpcBAD_SECRET, "badSecret", "Secret does not match account."},
|
||||
{rpcBAD_SEED, "badSeed", "Disallowed seed."},
|
||||
{rpcBAD_SYNTAX, "badSyntax", "Syntax error."},
|
||||
{rpcCHANNEL_MALFORMED, "channelMalformed", "Payment channel is malformed."},
|
||||
{rpcCHANNEL_AMT_MALFORMED, "channelAmtMalformed", "Payment channel amount is malformed."},
|
||||
{rpcCOMMAND_MISSING, "commandMissing", "Missing command entry."},
|
||||
{rpcDB_DESERIALIZATION, "dbDeserialization", "Database deserialization error."},
|
||||
{rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed."},
|
||||
{rpcDST_ACT_MISSING, "dstActMissing", "Destination account not provided."},
|
||||
{rpcDST_ACT_NOT_FOUND, "dstActNotFound", "Destination account not found."},
|
||||
{rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed."},
|
||||
{rpcDST_AMT_MISSING, "dstAmtMissing", "Destination amount/currency/issuer is missing."},
|
||||
{rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed."},
|
||||
{rpcEXCESSIVE_LGR_RANGE, "excessiveLgrRange", "Ledger range exceeds 1000."},
|
||||
{rpcFORBIDDEN, "forbidden", "Bad credentials."},
|
||||
{rpcFAILED_TO_FORWARD, "failedToForward", "Failed to forward request to p2p node"},
|
||||
{rpcHIGH_FEE, "highFee", "Current transaction fee exceeds your limit."},
|
||||
{rpcINTERNAL, "internal", "Internal error."},
|
||||
{rpcINVALID_LGR_RANGE, "invalidLgrRange", "Ledger range is invalid."},
|
||||
{rpcINVALID_PARAMS, "invalidParams", "Invalid parameters."},
|
||||
{rpcJSON_RPC, "json_rpc", "JSON-RPC transport error."},
|
||||
{rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid."},
|
||||
{rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed."},
|
||||
{rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found."},
|
||||
{rpcLGR_NOT_VALIDATED, "lgrNotValidated", "Ledger not validated."},
|
||||
{rpcMASTER_DISABLED, "masterDisabled", "Master key is disabled."},
|
||||
{rpcNOT_ENABLED, "notEnabled", "Not enabled in configuration."},
|
||||
{rpcNOT_IMPL, "notImpl", "Not implemented."},
|
||||
{rpcNOT_READY, "notReady", "Not ready to handle this request."},
|
||||
{rpcNOT_SUPPORTED, "notSupported", "Operation not supported."},
|
||||
{rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable."},
|
||||
{rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable."},
|
||||
{rpcNOT_SYNCED, "notSynced", "Not synced to the network."},
|
||||
{rpcNO_EVENTS, "noEvents", "Current transport does not support events."},
|
||||
{rpcNO_NETWORK, "noNetwork", "Not synced to the network."},
|
||||
{rpcNO_PERMISSION, "noPermission", "You don't have permission for this command."},
|
||||
{rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress."},
|
||||
{rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed."},
|
||||
{rpcREPORTING_UNSUPPORTED, "reportingUnsupported", "Requested operation not supported by reporting mode server"},
|
||||
{rpcSIGNING_MALFORMED, "signingMalformed", "Signing of transaction is malformed."},
|
||||
{rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server."},
|
||||
{rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed."},
|
||||
{rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided."},
|
||||
{rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found."},
|
||||
{rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed."},
|
||||
{rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed."},
|
||||
{rpcSTREAM_MALFORMED, "malformedStream", "Stream malformed."},
|
||||
{rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now."},
|
||||
{rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found."},
|
||||
{rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method."},
|
||||
{rpcSENDMAX_MALFORMED, "sendMaxMalformed", "SendMax amount malformed."},
|
||||
{rpcOBJECT_NOT_FOUND, "objectNotFound", "The requested object was not found."}};
|
||||
// clang-format on
|
||||
|
||||
// C++ does not allow you to return an array from a function. You must
|
||||
// return an object which may in turn contain an array. The following
|
||||
// struct is simply defined so the enclosed array can be returned from a
|
||||
// constexpr function.
|
||||
//
|
||||
// In C++17 this struct can be replaced by a std::array. But in C++14
|
||||
// the constexpr methods of a std::array are not sufficient to perform the
|
||||
// necessary work at compile time.
|
||||
template <int N>
|
||||
struct ErrorInfoArray
|
||||
{
|
||||
// Visual Studio doesn't treat a templated aggregate as an aggregate.
|
||||
// So, for Visual Studio, we define a constexpr default constructor.
|
||||
constexpr ErrorInfoArray() : infos{}
|
||||
{
|
||||
}
|
||||
// There's a certain amount of tension in determining the correct HTTP
|
||||
// status to associate with a given RPC error. Initially all RPC errors
|
||||
// returned 200 (OK). And that's the default behavior if no HTTP status code
|
||||
// is specified below.
|
||||
//
|
||||
// The codes currently selected target the load balancer fail-over use case.
|
||||
// If a query fails on one node but is likely to have a positive outcome
|
||||
// on a different node, then the failure should return a 4xx/5xx range
|
||||
// status code.
|
||||
|
||||
ErrorInfo infos[N];
|
||||
};
|
||||
// clang-format off
|
||||
constexpr static ErrorInfo unorderedErrorInfos[]{
|
||||
{rpcACT_MALFORMED, "actMalformed", "Account malformed."},
|
||||
{rpcACT_NOT_FOUND, "actNotFound", "Account not found."},
|
||||
{rpcALREADY_MULTISIG, "alreadyMultisig", "Already multisigned."},
|
||||
{rpcALREADY_SINGLE_SIG, "alreadySingleSig", "Already single-signed."},
|
||||
{rpcAMENDMENT_BLOCKED, "amendmentBlocked", "Amendment blocked, need upgrade.", 503},
|
||||
{rpcEXPIRED_VALIDATOR_LIST, "unlBlocked", "Validator list expired.", 503},
|
||||
{rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range.", 400},
|
||||
{rpcBAD_KEY_TYPE, "badKeyType", "Bad key type.", 400},
|
||||
{rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid.", 500},
|
||||
{rpcBAD_ISSUER, "badIssuer", "Issuer account malformed.", 400},
|
||||
{rpcBAD_MARKET, "badMarket", "No such market.", 404},
|
||||
{rpcBAD_SECRET, "badSecret", "Secret does not match account.", 403},
|
||||
{rpcBAD_SEED, "badSeed", "Disallowed seed.", 403},
|
||||
{rpcBAD_SYNTAX, "badSyntax", "Syntax error.", 400},
|
||||
{rpcCHANNEL_MALFORMED, "channelMalformed", "Payment channel is malformed.", 400},
|
||||
{rpcCHANNEL_AMT_MALFORMED, "channelAmtMalformed", "Payment channel amount is malformed.", 400},
|
||||
{rpcCOMMAND_MISSING, "commandMissing", "Missing command entry.", 400},
|
||||
{rpcDB_DESERIALIZATION, "dbDeserialization", "Database deserialization error.", 502},
|
||||
{rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed.", 400},
|
||||
{rpcDST_ACT_MISSING, "dstActMissing", "Destination account not provided.", 400},
|
||||
{rpcDST_ACT_NOT_FOUND, "dstActNotFound", "Destination account not found.", 404},
|
||||
{rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed.", 400},
|
||||
{rpcDST_AMT_MISSING, "dstAmtMissing", "Destination amount/currency/issuer is missing.", 400},
|
||||
{rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed.", 400},
|
||||
{rpcEXCESSIVE_LGR_RANGE, "excessiveLgrRange", "Ledger range exceeds 1000.", 400},
|
||||
{rpcFORBIDDEN, "forbidden", "Bad credentials.", 403},
|
||||
{rpcFAILED_TO_FORWARD, "failedToForward", "Failed to forward request to p2p node", 503},
|
||||
{rpcHIGH_FEE, "highFee", "Current transaction fee exceeds your limit.", 402},
|
||||
{rpcINTERNAL, "internal", "Internal error.", 500},
|
||||
{rpcINVALID_LGR_RANGE, "invalidLgrRange", "Ledger range is invalid.", 400},
|
||||
{rpcINVALID_PARAMS, "invalidParams", "Invalid parameters.", 400},
|
||||
{rpcJSON_RPC, "json_rpc", "JSON-RPC transport error.", 500},
|
||||
{rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid.", 400},
|
||||
{rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed.", 400},
|
||||
{rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found.", 404},
|
||||
{rpcLGR_NOT_VALIDATED, "lgrNotValidated", "Ledger not validated.", 202},
|
||||
{rpcMASTER_DISABLED, "masterDisabled", "Master key is disabled.", 403},
|
||||
{rpcNOT_ENABLED, "notEnabled", "Not enabled in configuration.", 501},
|
||||
{rpcNOT_IMPL, "notImpl", "Not implemented.", 501},
|
||||
{rpcNOT_READY, "notReady", "Not ready to handle this request.", 503},
|
||||
{rpcNOT_SUPPORTED, "notSupported", "Operation not supported.", 501},
|
||||
{rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable.", 503},
|
||||
{rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable.", 503},
|
||||
{rpcNOT_SYNCED, "notSynced", "Not synced to the network.", 503},
|
||||
{rpcNO_EVENTS, "noEvents", "Current transport does not support events.", 405},
|
||||
{rpcNO_NETWORK, "noNetwork", "Not synced to the network.", 503},
|
||||
{rpcNO_PERMISSION, "noPermission", "You don't have permission for this command.", 401},
|
||||
{rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress.", 404},
|
||||
{rpcOBJECT_NOT_FOUND, "objectNotFound", "The requested object was not found.", 404},
|
||||
{rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed.", 400},
|
||||
{rpcREPORTING_UNSUPPORTED, "reportingUnsupported", "Requested operation not supported by reporting mode server", 405},
|
||||
{rpcSENDMAX_MALFORMED, "sendMaxMalformed", "SendMax amount malformed.", 400},
|
||||
{rpcSIGNING_MALFORMED, "signingMalformed", "Signing of transaction is malformed.", 400},
|
||||
{rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server.", 429},
|
||||
{rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed.", 400},
|
||||
{rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided.", 400},
|
||||
{rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found.", 404},
|
||||
{rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed.", 400},
|
||||
{rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed.", 400},
|
||||
{rpcSTREAM_MALFORMED, "malformedStream", "Stream malformed.", 400},
|
||||
{rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now.", 503},
|
||||
{rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found.", 404},
|
||||
{rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method.", 405}};
|
||||
// clang-format on
|
||||
|
||||
// Sort and validate unorderedErrorInfos at compile time. Should be
|
||||
// converted to consteval when get to C++20.
|
||||
template <int M, int N>
|
||||
constexpr auto
|
||||
sortErrorInfos(ErrorInfo const (&unordered)[N]) -> ErrorInfoArray<M>
|
||||
sortErrorInfos(ErrorInfo const (&unordered)[N]) -> std::array<ErrorInfo, M>
|
||||
{
|
||||
ErrorInfoArray<M> ret;
|
||||
std::array<ErrorInfo, M> ret = {};
|
||||
|
||||
for (ErrorInfo const& info : unordered)
|
||||
{
|
||||
@@ -135,12 +127,10 @@ sortErrorInfos(ErrorInfo const (&unordered)[N]) -> ErrorInfoArray<M>
|
||||
static_assert(rpcSUCCESS == 0, "Unexpected error_code_i layout.");
|
||||
int const index{info.code - 1};
|
||||
|
||||
if (ret.infos[index].code != rpcUNKNOWN)
|
||||
if (ret[index].code != rpcUNKNOWN)
|
||||
throw(std::invalid_argument("Duplicate error_code_i in list"));
|
||||
|
||||
ret.infos[index].code = info.code;
|
||||
ret.infos[index].token = info.token;
|
||||
ret.infos[index].message = info.message;
|
||||
ret[index] = info;
|
||||
}
|
||||
|
||||
// Verify that all entries are filled in starting with 1 and proceeding
|
||||
@@ -150,7 +140,7 @@ sortErrorInfos(ErrorInfo const (&unordered)[N]) -> ErrorInfoArray<M>
|
||||
// rpcUNKNOWN. But other than that all entries should match their index.
|
||||
int codeCount{0};
|
||||
int expect{rpcBAD_SYNTAX - 1};
|
||||
for (ErrorInfo const& info : ret.infos)
|
||||
for (ErrorInfo const& info : ret)
|
||||
{
|
||||
++expect;
|
||||
if (info.code == rpcUNKNOWN)
|
||||
@@ -181,7 +171,7 @@ get_error_info(error_code_i code)
|
||||
{
|
||||
if (code <= rpcSUCCESS || code > rpcLAST)
|
||||
return detail::unknownError;
|
||||
return detail::sortedErrorInfos.infos[code - 1];
|
||||
return detail::sortedErrorInfos[code - 1];
|
||||
}
|
||||
|
||||
Json::Value
|
||||
@@ -208,6 +198,12 @@ contains_error(Json::Value const& json)
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
error_code_http_status(error_code_i code)
|
||||
{
|
||||
return get_error_info(code).http_status;
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
std::string
|
||||
|
||||
@@ -450,6 +450,7 @@ REGISTER_FEATURE(NonFungibleTokensV1_1, Supported::yes, DefaultVote::no)
|
||||
REGISTER_FIX (fixTrustLinesToSelf, Supported::yes, DefaultVote::no);
|
||||
REGISTER_FIX (fixRemoveNFTokenAutoTrustLine, Supported::yes, DefaultVote::yes);
|
||||
REGISTER_FEATURE(ImmediateOfferKilled, Supported::yes, DefaultVote::no);
|
||||
REGISTER_FEATURE(DisallowIncoming, Supported::yes, DefaultVote::no);
|
||||
REGISTER_FEATURE(PaychanAndEscrowForTokens, Supported::yes, DefaultVote::no);
|
||||
|
||||
// The following amendments have been active for at least two years. Their
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/net/RPCErr.h>
|
||||
#include <ripple/overlay/Overlay.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/resource/ResourceManager.h>
|
||||
#include <ripple/rpc/RPCHandler.h>
|
||||
@@ -970,6 +971,29 @@ ServerHandlerImp::processRequest(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're returning an error_code, use that to determine the HTTP status.
|
||||
int const httpStatus = [&reply]() {
|
||||
// This feature is enabled with ripplerpc version 3.0 and above.
|
||||
// Before ripplerpc version 3.0 always return 200.
|
||||
if (reply.isMember(jss::ripplerpc) &&
|
||||
reply[jss::ripplerpc].isString() &&
|
||||
reply[jss::ripplerpc].asString() >= "3.0")
|
||||
{
|
||||
// If there's an error_code, use that to determine the HTTP Status.
|
||||
if (reply.isMember(jss::error) &&
|
||||
reply[jss::error].isMember(jss::error_code) &&
|
||||
reply[jss::error][jss::error_code].isInt())
|
||||
{
|
||||
int const errCode = reply[jss::error][jss::error_code].asInt();
|
||||
return RPC::error_code_http_status(
|
||||
static_cast<error_code_i>(errCode));
|
||||
}
|
||||
}
|
||||
// Return OK.
|
||||
return 200;
|
||||
}();
|
||||
|
||||
auto response = to_string(reply);
|
||||
|
||||
rpc_time_.notify(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
@@ -988,7 +1012,7 @@ ServerHandlerImp::processRequest(
|
||||
stream << "Reply: " << response.substr(0, maxSize);
|
||||
}
|
||||
|
||||
HTTPReply(200, response, output, rpcJ);
|
||||
HTTPReply(httpStatus, response, output, rpcJ);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -61,7 +61,7 @@ HTTPReply(
|
||||
{
|
||||
JLOG(j.trace()) << "HTTP Reply " << nStatus << " " << content;
|
||||
|
||||
if (nStatus == 401)
|
||||
if (content.empty() && nStatus == 401)
|
||||
{
|
||||
output("HTTP/1.0 401 Authorization Required\r\n");
|
||||
output(getHTTPHeaderTimestamp());
|
||||
@@ -100,18 +100,33 @@ HTTPReply(
|
||||
case 200:
|
||||
output("HTTP/1.1 200 OK\r\n");
|
||||
break;
|
||||
case 202:
|
||||
output("HTTP/1.1 202 Accepted\r\n");
|
||||
break;
|
||||
case 400:
|
||||
output("HTTP/1.1 400 Bad Request\r\n");
|
||||
break;
|
||||
case 401:
|
||||
output("HTTP/1.1 401 Authorization Required\r\n");
|
||||
break;
|
||||
case 403:
|
||||
output("HTTP/1.1 403 Forbidden\r\n");
|
||||
break;
|
||||
case 404:
|
||||
output("HTTP/1.1 404 Not Found\r\n");
|
||||
break;
|
||||
case 405:
|
||||
output("HTTP/1.1 405 Method Not Allowed\r\n");
|
||||
break;
|
||||
case 429:
|
||||
output("HTTP/1.1 429 Too Many Requests\r\n");
|
||||
break;
|
||||
case 500:
|
||||
output("HTTP/1.1 500 Internal Server Error\r\n");
|
||||
break;
|
||||
case 501:
|
||||
output("HTTP/1.1 501 Not Implemented\r\n");
|
||||
break;
|
||||
case 503:
|
||||
output("HTTP/1.1 503 Server is overloaded\r\n");
|
||||
break;
|
||||
|
||||
@@ -85,6 +85,8 @@ public:
|
||||
|
||||
class Check_test : public beast::unit_test::suite
|
||||
{
|
||||
FeatureBitset const disallowIncoming{featureDisallowIncoming};
|
||||
|
||||
static uint256
|
||||
getCheckIndex(AccountID const& account, std::uint32_t uSequence)
|
||||
{
|
||||
@@ -293,6 +295,100 @@ class Check_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(checksOnAccount(env, bob).size() == bobCount + 7);
|
||||
}
|
||||
|
||||
void
|
||||
testCreateDisallowIncoming(FeatureBitset features)
|
||||
{
|
||||
testcase("Create valid with disallow incoming");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
// test flag doesn't set unless amendment enabled
|
||||
{
|
||||
Env env{*this, features - disallowIncoming};
|
||||
Account const alice{"alice"};
|
||||
env.fund(XRP(10000), alice);
|
||||
env(fset(alice, asfDisallowIncomingCheck));
|
||||
env.close();
|
||||
auto const sle = env.le(alice);
|
||||
uint32_t flags = sle->getFlags();
|
||||
BEAST_EXPECT(!(flags & lsfDisallowIncomingCheck));
|
||||
}
|
||||
|
||||
Account const gw{"gateway"};
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
IOU const USD{gw["USD"]};
|
||||
|
||||
Env env{*this, features | disallowIncoming};
|
||||
|
||||
STAmount const startBalance{XRP(1000).value()};
|
||||
env.fund(startBalance, gw, alice, bob);
|
||||
|
||||
/*
|
||||
* Attempt to create two checks from `from` to `to` and
|
||||
* require they both result in error/success code `expected`
|
||||
*/
|
||||
auto writeTwoChecksDI = [&env, &USD, this](
|
||||
Account const& from,
|
||||
Account const& to,
|
||||
TER expected) {
|
||||
std::uint32_t const fromOwnerCount{ownerCount(env, from)};
|
||||
std::uint32_t const toOwnerCount{ownerCount(env, to)};
|
||||
|
||||
std::size_t const fromCkCount{checksOnAccount(env, from).size()};
|
||||
std::size_t const toCkCount{checksOnAccount(env, to).size()};
|
||||
|
||||
env(check::create(from, to, XRP(2000)), ter(expected));
|
||||
env.close();
|
||||
|
||||
env(check::create(from, to, USD(50)), ter(expected));
|
||||
env.close();
|
||||
|
||||
if (expected == tesSUCCESS)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
checksOnAccount(env, from).size() == fromCkCount + 2);
|
||||
BEAST_EXPECT(checksOnAccount(env, to).size() == toCkCount + 2);
|
||||
|
||||
env.require(owners(from, fromOwnerCount + 2));
|
||||
env.require(
|
||||
owners(to, to == from ? fromOwnerCount + 2 : toOwnerCount));
|
||||
return;
|
||||
}
|
||||
|
||||
BEAST_EXPECT(checksOnAccount(env, from).size() == fromCkCount);
|
||||
BEAST_EXPECT(checksOnAccount(env, to).size() == toCkCount);
|
||||
|
||||
env.require(owners(from, fromOwnerCount));
|
||||
env.require(owners(to, to == from ? fromOwnerCount : toOwnerCount));
|
||||
};
|
||||
|
||||
// enable the DisallowIncoming flag on both bob and alice
|
||||
env(fset(bob, asfDisallowIncomingCheck));
|
||||
env(fset(alice, asfDisallowIncomingCheck));
|
||||
env.close();
|
||||
|
||||
// both alice and bob can't receive checks
|
||||
writeTwoChecksDI(alice, bob, tecNO_PERMISSION);
|
||||
writeTwoChecksDI(gw, alice, tecNO_PERMISSION);
|
||||
|
||||
// remove the flag from alice but not from bob
|
||||
env(fclear(alice, asfDisallowIncomingCheck));
|
||||
env.close();
|
||||
|
||||
// now bob can send alice a cheque but not visa-versa
|
||||
writeTwoChecksDI(bob, alice, tesSUCCESS);
|
||||
writeTwoChecksDI(alice, bob, tecNO_PERMISSION);
|
||||
|
||||
// remove bob's flag too
|
||||
env(fclear(bob, asfDisallowIncomingCheck));
|
||||
env.close();
|
||||
|
||||
// now they can send checks freely
|
||||
writeTwoChecksDI(bob, alice, tesSUCCESS);
|
||||
writeTwoChecksDI(alice, bob, tesSUCCESS);
|
||||
}
|
||||
|
||||
void
|
||||
testCreateInvalid(FeatureBitset features)
|
||||
{
|
||||
@@ -2602,6 +2698,7 @@ class Check_test : public beast::unit_test::suite
|
||||
{
|
||||
testEnabled(features);
|
||||
testCreateValid(features);
|
||||
testCreateDisallowIncoming(features);
|
||||
testCreateInvalid(features);
|
||||
testCashXRP(features);
|
||||
testCashIOU(features);
|
||||
@@ -2621,6 +2718,7 @@ public:
|
||||
using namespace test::jtx;
|
||||
auto const sa = supported_amendments();
|
||||
testWithFeats(sa - featureCheckCashMakesTrustLine);
|
||||
testWithFeats(sa - disallowIncoming);
|
||||
testWithFeats(sa);
|
||||
|
||||
testTrustLineCreation(sa); // Test with featureCheckCashMakesTrustLine
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace ripple {
|
||||
|
||||
class NFToken_test : public beast::unit_test::suite
|
||||
{
|
||||
FeatureBitset const disallowIncoming{featureDisallowIncoming};
|
||||
|
||||
// Helper function that returns the owner count of an account root.
|
||||
static std::uint32_t
|
||||
ownerCount(test::jtx::Env const& env, test::jtx::Account const& acct)
|
||||
@@ -2975,6 +2977,135 @@ class NFToken_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCreateOfferDestinationDisallowIncoming(FeatureBitset features)
|
||||
{
|
||||
testcase("Create offer destination disallow incoming");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
// test flag doesn't set unless amendment enabled
|
||||
{
|
||||
Env env{*this, features - disallowIncoming};
|
||||
Account const alice{"alice"};
|
||||
env.fund(XRP(10000), alice);
|
||||
env(fset(alice, asfDisallowIncomingNFTOffer));
|
||||
env.close();
|
||||
auto const sle = env.le(alice);
|
||||
uint32_t flags = sle->getFlags();
|
||||
BEAST_EXPECT(!(flags & lsfDisallowIncomingNFTOffer));
|
||||
}
|
||||
|
||||
Env env{*this, features | disallowIncoming};
|
||||
|
||||
Account const issuer{"issuer"};
|
||||
Account const minter{"minter"};
|
||||
Account const buyer{"buyer"};
|
||||
Account const alice{"alice"};
|
||||
|
||||
env.fund(XRP(1000), issuer, minter, buyer, alice);
|
||||
|
||||
env(token::setMinter(issuer, minter));
|
||||
env.close();
|
||||
|
||||
uint256 const nftokenID =
|
||||
token::getNextID(env, issuer, 0, tfTransferable);
|
||||
env(token::mint(minter, 0),
|
||||
token::issuer(issuer),
|
||||
txflags(tfTransferable));
|
||||
env.close();
|
||||
|
||||
// enable flag
|
||||
env(fset(buyer, asfDisallowIncomingNFTOffer));
|
||||
env.close();
|
||||
|
||||
// a sell offer from the minter to the buyer should be rejected
|
||||
{
|
||||
env(token::createOffer(minter, nftokenID, drops(1)),
|
||||
token::destination(buyer),
|
||||
txflags(tfSellNFToken),
|
||||
ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
|
||||
// disable the flag
|
||||
env(fclear(buyer, asfDisallowIncomingNFTOffer));
|
||||
env.close();
|
||||
|
||||
// create offer (allowed now) then cancel
|
||||
{
|
||||
uint256 const offerIndex =
|
||||
keylet::nftoffer(minter, env.seq(minter)).key;
|
||||
|
||||
env(token::createOffer(minter, nftokenID, drops(1)),
|
||||
token::destination(buyer),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
|
||||
env(token::cancelOffer(minter, {offerIndex}));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// create offer, enable flag, then cancel
|
||||
{
|
||||
uint256 const offerIndex =
|
||||
keylet::nftoffer(minter, env.seq(minter)).key;
|
||||
|
||||
env(token::createOffer(minter, nftokenID, drops(1)),
|
||||
token::destination(buyer),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
|
||||
env(fset(buyer, asfDisallowIncomingNFTOffer));
|
||||
env.close();
|
||||
|
||||
env(token::cancelOffer(minter, {offerIndex}));
|
||||
env.close();
|
||||
|
||||
env(fclear(buyer, asfDisallowIncomingNFTOffer));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// create offer then transfer
|
||||
{
|
||||
uint256 const offerIndex =
|
||||
keylet::nftoffer(minter, env.seq(minter)).key;
|
||||
|
||||
env(token::createOffer(minter, nftokenID, drops(1)),
|
||||
token::destination(buyer),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
|
||||
env(token::acceptSellOffer(buyer, offerIndex));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// buyer now owns the token
|
||||
|
||||
// enable flag again
|
||||
env(fset(buyer, asfDisallowIncomingNFTOffer));
|
||||
env.close();
|
||||
|
||||
// a random offer to buy the token
|
||||
{
|
||||
env(token::createOffer(alice, nftokenID, drops(1)),
|
||||
token::owner(buyer),
|
||||
ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// minter offer to buy the token
|
||||
{
|
||||
env(token::createOffer(minter, nftokenID, drops(1)),
|
||||
token::owner(buyer),
|
||||
ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCreateOfferExpiration(FeatureBitset features)
|
||||
{
|
||||
@@ -4929,6 +5060,7 @@ class NFToken_test : public beast::unit_test::suite
|
||||
testMintTaxon(features);
|
||||
testMintURI(features);
|
||||
testCreateOfferDestination(features);
|
||||
testCreateOfferDestinationDisallowIncoming(features);
|
||||
testCreateOfferExpiration(features);
|
||||
testCancelOffers(features);
|
||||
testCancelTooManyOffers(features);
|
||||
@@ -4949,6 +5081,7 @@ public:
|
||||
FeatureBitset const fixNFTDir{fixNFTokenDirV1};
|
||||
|
||||
testWithFeats(all - fixNFTDir);
|
||||
testWithFeats(all - disallowIncoming);
|
||||
testWithFeats(all);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace ripple {
|
||||
namespace test {
|
||||
struct PayChan_test : public beast::unit_test::suite
|
||||
{
|
||||
FeatureBitset const disallowIncoming{featureDisallowIncoming};
|
||||
|
||||
static uint256
|
||||
channel(
|
||||
jtx::Account const& account,
|
||||
@@ -221,7 +223,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("simple");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -382,6 +384,90 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testDisallowIncoming(FeatureBitset features)
|
||||
{
|
||||
testcase("Disallow Incoming Flag");
|
||||
using namespace jtx;
|
||||
|
||||
// test flag doesn't set unless amendment enabled
|
||||
{
|
||||
Env env{*this, features - disallowIncoming};
|
||||
Account const alice{"alice"};
|
||||
env.fund(XRP(10000), alice);
|
||||
env(fset(alice, asfDisallowIncomingPayChan));
|
||||
env.close();
|
||||
auto const sle = env.le(alice);
|
||||
uint32_t flags = sle->getFlags();
|
||||
BEAST_EXPECT(!(flags & lsfDisallowIncomingPayChan));
|
||||
}
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env{*this, features | disallowIncoming};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const cho = Account("cho");
|
||||
env.fund(XRP(10000), alice, bob, cho);
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
|
||||
// set flag on bob only
|
||||
env(fset(bob, asfDisallowIncomingPayChan));
|
||||
env.close();
|
||||
|
||||
// channel creation from alice to bob is disallowed
|
||||
{
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk),
|
||||
ter(tecNO_PERMISSION));
|
||||
BEAST_EXPECT(!channelExists(*env.current(), chan));
|
||||
}
|
||||
|
||||
// set flag on alice also
|
||||
env(fset(alice, asfDisallowIncomingPayChan));
|
||||
env.close();
|
||||
|
||||
// channel creation from bob to alice is now disallowed
|
||||
{
|
||||
auto const chan = channel(bob, alice, env.seq(bob));
|
||||
env(create(bob, alice, XRP(1000), settleDelay, pk),
|
||||
ter(tecNO_PERMISSION));
|
||||
BEAST_EXPECT(!channelExists(*env.current(), chan));
|
||||
}
|
||||
|
||||
// remove flag from bob
|
||||
env(fclear(bob, asfDisallowIncomingPayChan));
|
||||
env.close();
|
||||
|
||||
// now the channel between alice and bob can exist
|
||||
{
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk),
|
||||
ter(tesSUCCESS));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
}
|
||||
|
||||
// a channel from cho to alice isn't allowed
|
||||
{
|
||||
auto const chan = channel(cho, alice, env.seq(cho));
|
||||
env(create(cho, alice, XRP(1000), settleDelay, pk),
|
||||
ter(tecNO_PERMISSION));
|
||||
BEAST_EXPECT(!channelExists(*env.current(), chan));
|
||||
}
|
||||
|
||||
// remove flag from alice
|
||||
env(fclear(alice, asfDisallowIncomingPayChan));
|
||||
env.close();
|
||||
|
||||
// now a channel from cho to alice is allowed
|
||||
{
|
||||
auto const chan = channel(cho, alice, env.seq(cho));
|
||||
env(create(cho, alice, XRP(1000), settleDelay, pk),
|
||||
ter(tesSUCCESS));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCancelAfter(FeatureBitset features)
|
||||
{
|
||||
@@ -393,7 +479,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
auto const carol = Account("carol");
|
||||
{
|
||||
// If dst claims after cancel after, channel closes
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
@@ -425,7 +511,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// Third party can close after cancel after
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob, carol);
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
@@ -453,7 +539,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("expiration");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
@@ -519,7 +605,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("settle delay");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -580,7 +666,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("close dry");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -615,7 +701,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("default amount");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -675,7 +761,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
auto const bob = Account("bob");
|
||||
{
|
||||
// Create a channel where dst disallows XRP
|
||||
Env env(*this, supported_amendments() - featureDepositAuth);
|
||||
Env env(*this, features - featureDepositAuth);
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env(fset(bob, asfDisallowXRP));
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
@@ -686,7 +772,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
{
|
||||
// Create a channel where dst disallows XRP. Ignore that flag,
|
||||
// since it's just advisory.
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env(fset(bob, asfDisallowXRP));
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
@@ -697,7 +783,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
{
|
||||
// Claim to a channel where dst disallows XRP
|
||||
// (channel is created before disallow xrp is set)
|
||||
Env env(*this, supported_amendments() - featureDepositAuth);
|
||||
Env env(*this, features - featureDepositAuth);
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
|
||||
@@ -711,7 +797,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
// Claim to a channel where dst disallows XRP (channel is
|
||||
// created before disallow xrp is set). Ignore that flag
|
||||
// since it is just advisory.
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
|
||||
@@ -731,7 +817,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
// Create a channel where dst disallows XRP
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -764,7 +850,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
{
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob, carol);
|
||||
|
||||
env(fset(bob, asfDepositAuth));
|
||||
@@ -883,7 +969,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("Multiple channels to the same account");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -906,7 +992,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const charlie = Account("charlie", KeyType::ed25519);
|
||||
@@ -974,7 +1060,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
return r;
|
||||
}();
|
||||
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice);
|
||||
for (auto const& a : bobs)
|
||||
{
|
||||
@@ -1082,7 +1168,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
|
||||
// Create a channel from alice to bob and from bob to alice
|
||||
@@ -1113,7 +1199,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("PayChan Auth/Verify RPC");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const charlie = Account("charlie", KeyType::ed25519);
|
||||
@@ -1465,7 +1551,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("Optional Fields");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
@@ -1516,7 +1602,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("malformed pk");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -1609,8 +1695,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
{
|
||||
// Test without adding the paychan to the recipient's owner
|
||||
// directory
|
||||
Env env(
|
||||
*this, supported_amendments() - fixPayChanRecipientOwnerDir);
|
||||
Env env(*this, features - fixPayChanRecipientOwnerDir);
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
@@ -1631,7 +1716,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
// Test with adding the paychan to the recipient's owner directory
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
@@ -1653,8 +1738,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
{
|
||||
// Test removing paychans created before adding to the recipient's
|
||||
// owner directory
|
||||
Env env(
|
||||
*this, supported_amendments() - fixPayChanRecipientOwnerDir);
|
||||
Env env(*this, features - fixPayChanRecipientOwnerDir);
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
// create the channel before the amendment activates
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
@@ -1722,8 +1806,8 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
for (bool const withOwnerDirFix : {false, true})
|
||||
{
|
||||
auto const amd = withOwnerDirFix
|
||||
? supported_amendments()
|
||||
: supported_amendments() - fixPayChanRecipientOwnerDir;
|
||||
? features
|
||||
: features - fixPayChanRecipientOwnerDir;
|
||||
Env env{*this, amd};
|
||||
env.fund(XRP(10000), alice, bob, carol);
|
||||
env.close();
|
||||
@@ -1815,8 +1899,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
// test resurrected account
|
||||
Env env{
|
||||
*this, supported_amendments() - fixPayChanRecipientOwnerDir};
|
||||
Env env{*this, features - fixPayChanRecipientOwnerDir};
|
||||
env.fund(XRP(10000), alice, bob, carol);
|
||||
env.close();
|
||||
auto const feeDrops = env.current()->fees().base;
|
||||
@@ -1927,7 +2010,7 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testcase("using tickets");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
Env env(*this, features);
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
@@ -5011,6 +5094,7 @@ public:
|
||||
{
|
||||
using namespace test::jtx;
|
||||
FeatureBitset const all{supported_amendments()};
|
||||
testWithFeats(all - disallowIncoming);
|
||||
testWithFeats(all);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,9 +26,14 @@ namespace test {
|
||||
|
||||
class SetTrust_test : public beast::unit_test::suite
|
||||
{
|
||||
FeatureBitset const disallowIncoming{featureDisallowIncoming};
|
||||
|
||||
public:
|
||||
void
|
||||
testFreeTrustlines(bool thirdLineCreatesLE, bool createOnHighAcct)
|
||||
testFreeTrustlines(
|
||||
FeatureBitset features,
|
||||
bool thirdLineCreatesLE,
|
||||
bool createOnHighAcct)
|
||||
{
|
||||
if (thirdLineCreatesLE)
|
||||
testcase("Allow two free trustlines");
|
||||
@@ -36,7 +41,7 @@ public:
|
||||
testcase("Dynamic reserve for trustline");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features);
|
||||
|
||||
auto const gwA = Account{"gwA"};
|
||||
auto const gwB = Account{"gwB"};
|
||||
@@ -107,14 +112,14 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testTicketSetTrust()
|
||||
testTicketSetTrust(FeatureBitset features)
|
||||
{
|
||||
testcase("SetTrust using a ticket");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
// Verify that TrustSet transactions can use tickets.
|
||||
Env env{*this};
|
||||
Env env{*this, features};
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const alice = Account{"alice"};
|
||||
auto const USD = gw["USD"];
|
||||
@@ -152,12 +157,12 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testMalformedTransaction()
|
||||
testMalformedTransaction(FeatureBitset features)
|
||||
{
|
||||
testcase("SetTrust checks for malformed transactions");
|
||||
|
||||
using namespace jtx;
|
||||
Env env{*this};
|
||||
Env env{*this, features};
|
||||
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const alice = Account{"alice"};
|
||||
@@ -199,14 +204,17 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testModifyQualityOfTrustline(bool createQuality, bool createOnHighAcct)
|
||||
testModifyQualityOfTrustline(
|
||||
FeatureBitset features,
|
||||
bool createQuality,
|
||||
bool createOnHighAcct)
|
||||
{
|
||||
testcase << "SetTrust " << (createQuality ? "creates" : "removes")
|
||||
<< " quality of trustline for "
|
||||
<< (createOnHighAcct ? "high" : "low") << " account";
|
||||
|
||||
using namespace jtx;
|
||||
Env env{*this};
|
||||
Env env{*this, features};
|
||||
|
||||
auto const alice = Account{"alice"};
|
||||
auto const bob = Account{"bob"};
|
||||
@@ -249,20 +257,119 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
testDisallowIncoming(FeatureBitset features)
|
||||
{
|
||||
testFreeTrustlines(true, false);
|
||||
testFreeTrustlines(false, true);
|
||||
testFreeTrustlines(false, true);
|
||||
testcase("Create trustline with disallow incoming");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
// test flag doesn't set unless amendment enabled
|
||||
{
|
||||
Env env{*this, features - disallowIncoming};
|
||||
Account const alice{"alice"};
|
||||
env.fund(XRP(10000), alice);
|
||||
env(fset(alice, asfDisallowIncomingTrustline));
|
||||
env.close();
|
||||
auto const sle = env.le(alice);
|
||||
uint32_t flags = sle->getFlags();
|
||||
BEAST_EXPECT(!(flags & lsfDisallowIncomingTrustline));
|
||||
}
|
||||
|
||||
Env env{*this, features | disallowIncoming};
|
||||
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const alice = Account{"alice"};
|
||||
auto const bob = Account{"bob"};
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
env.fund(XRP(10000), gw, alice, bob);
|
||||
env.close();
|
||||
|
||||
// Set flag on gateway
|
||||
env(fset(gw, asfDisallowIncomingTrustline));
|
||||
env.close();
|
||||
|
||||
// Create a trustline which will fail
|
||||
env(trust(alice, USD(1000)), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
|
||||
// Unset the flag
|
||||
env(fclear(gw, asfDisallowIncomingTrustline));
|
||||
env.close();
|
||||
|
||||
// Create a trustline which will now succeed
|
||||
env(trust(alice, USD(1000)));
|
||||
env.close();
|
||||
|
||||
// Now the payment succeeds.
|
||||
env(pay(gw, alice, USD(200)));
|
||||
env.close();
|
||||
|
||||
// Set flag on gateway again
|
||||
env(fset(gw, asfDisallowIncomingTrustline));
|
||||
env.close();
|
||||
|
||||
// Destroy the balance by sending it back
|
||||
env(pay(gw, alice, USD(200)));
|
||||
env.close();
|
||||
|
||||
// The trustline still exists in default state
|
||||
// So a further payment should work
|
||||
env(pay(gw, alice, USD(200)));
|
||||
env.close();
|
||||
|
||||
// Also set the flag on bob
|
||||
env(fset(bob, asfDisallowIncomingTrustline));
|
||||
env.close();
|
||||
|
||||
// But now bob can't open a trustline because he didn't already have one
|
||||
env(trust(bob, USD(1000)), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
|
||||
// The gateway also can't open this trustline because bob has the flag
|
||||
// set
|
||||
env(trust(gw, bob["USD"](1000)), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
|
||||
// Unset the flag only on the gateway
|
||||
env(fclear(gw, asfDisallowIncomingTrustline));
|
||||
env.close();
|
||||
|
||||
// Now bob can open a trustline
|
||||
env(trust(bob, USD(1000)));
|
||||
env.close();
|
||||
|
||||
// And the gateway can send bob a balance
|
||||
env(pay(gw, bob, USD(200)));
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testWithFeats(FeatureBitset features)
|
||||
{
|
||||
testFreeTrustlines(features, true, false);
|
||||
testFreeTrustlines(features, false, true);
|
||||
testFreeTrustlines(features, false, true);
|
||||
// true, true case doesn't matter since creating a trustline ledger
|
||||
// entry requires reserve from the creator
|
||||
// independent of hi/low account ids for endpoints
|
||||
testTicketSetTrust();
|
||||
testMalformedTransaction();
|
||||
testModifyQualityOfTrustline(false, false);
|
||||
testModifyQualityOfTrustline(false, true);
|
||||
testModifyQualityOfTrustline(true, false);
|
||||
testModifyQualityOfTrustline(true, true);
|
||||
testTicketSetTrust(features);
|
||||
testMalformedTransaction(features);
|
||||
testModifyQualityOfTrustline(features, false, false);
|
||||
testModifyQualityOfTrustline(features, false, true);
|
||||
testModifyQualityOfTrustline(features, true, false);
|
||||
testModifyQualityOfTrustline(features, true, true);
|
||||
testDisallowIncoming(features);
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace test::jtx;
|
||||
auto const sa = supported_amendments();
|
||||
testWithFeats(sa - disallowIncoming);
|
||||
testWithFeats(sa);
|
||||
}
|
||||
};
|
||||
BEAST_DEFINE_TESTSUITE(SetTrust, app, ripple);
|
||||
|
||||
123
src/test/protocol/Memo_test.cpp
Normal file
123
src/test/protocol/Memo_test.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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/basics/strHex.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Memo_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testMemos()
|
||||
{
|
||||
testcase("Test memos");
|
||||
|
||||
using namespace test::jtx;
|
||||
Account alice{"alice"};
|
||||
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), alice);
|
||||
env.close();
|
||||
|
||||
// Lambda that returns a valid JTx with a memo that we can hack up.
|
||||
// This is the basis for building tests of invalid states.
|
||||
auto makeJtxWithMemo = [&env, &alice]() {
|
||||
JTx example = noop(alice);
|
||||
memo const exampleMemo{"tic", "tac", "toe"};
|
||||
exampleMemo(env, example);
|
||||
return example;
|
||||
};
|
||||
|
||||
// A valid memo.
|
||||
env(makeJtxWithMemo());
|
||||
env.close();
|
||||
|
||||
{
|
||||
// Make sure that too big a memo is flagged as invalid.
|
||||
JTx memoSize = makeJtxWithMemo();
|
||||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoData.jsonName] = std::string(2020, '0');
|
||||
env(memoSize, ter(temINVALID));
|
||||
|
||||
// This memo is just barely small enough.
|
||||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoData.jsonName] = std::string(2018, '1');
|
||||
env(memoSize);
|
||||
}
|
||||
{
|
||||
// Put a non-Memo in the Memos array.
|
||||
JTx memoNonMemo = noop(alice);
|
||||
auto& jv = memoNonMemo.jv;
|
||||
auto& ma = jv[sfMemos.jsonName];
|
||||
auto& mi = ma[ma.size()];
|
||||
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos
|
||||
m[sfMemoData.jsonName] = "3030303030";
|
||||
|
||||
env(memoNonMemo, ter(temINVALID));
|
||||
}
|
||||
{
|
||||
// Put an invalid field in a Memo object.
|
||||
JTx memoExtra = makeJtxWithMemo();
|
||||
memoExtra
|
||||
.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] =
|
||||
13;
|
||||
env(memoExtra, ter(temINVALID));
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoType field.
|
||||
JTx memoBadChar = makeJtxWithMemo();
|
||||
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoType.jsonName] =
|
||||
strHex(std::string_view("ONE<INFINITY"));
|
||||
env(memoBadChar, ter(temINVALID));
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoData field.
|
||||
// That's okay.
|
||||
JTx memoLegitChar = makeJtxWithMemo();
|
||||
memoLegitChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoData.jsonName] =
|
||||
strHex(std::string_view("ONE<INFINITY"));
|
||||
env(memoLegitChar);
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoFormat.
|
||||
JTx memoBadChar = makeJtxWithMemo();
|
||||
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoFormat.jsonName] =
|
||||
strHex(std::string_view("NoBraces{}InURL"));
|
||||
env(memoBadChar, ter(temINVALID));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testMemos();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Memo, ripple_data, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
// elsewhere.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flag == asfAuthorizedNFTokenMinter)
|
||||
{
|
||||
// The asfAuthorizedNFTokenMinter flag requires the
|
||||
@@ -82,8 +83,18 @@ public:
|
||||
// the transaction. It is tested elsewhere.
|
||||
continue;
|
||||
}
|
||||
else if (
|
||||
std::find(goodFlags.begin(), goodFlags.end(), flag) !=
|
||||
|
||||
if (flag == asfDisallowIncomingCheck ||
|
||||
flag == asfDisallowIncomingPayChan ||
|
||||
flag == asfDisallowIncomingNFTOffer ||
|
||||
flag == asfDisallowIncomingTrustline)
|
||||
{
|
||||
// These flags are part of the DisallowIncoming amendment
|
||||
// and are tested elsewhere
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::find(goodFlags.begin(), goodFlags.end(), flag) !=
|
||||
goodFlags.end())
|
||||
{
|
||||
// Good flag
|
||||
|
||||
@@ -1675,7 +1675,7 @@ class LedgerRPC_test : public beast::unit_test::suite
|
||||
void
|
||||
testLedgerAccountsOption()
|
||||
{
|
||||
testcase("Ledger Request, Accounts Option");
|
||||
testcase("Ledger Request, Accounts Hashes");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
|
||||
Reference in New Issue
Block a user