Skip to content

Commit

Permalink
Merge branch 'develop' into feature/upgrade_codecov_action
Browse files Browse the repository at this point in the history
  • Loading branch information
zrayn authored Jul 2, 2024
2 parents 46f217f + e1534a3 commit 1c3b662
Show file tree
Hide file tree
Showing 15 changed files with 367 additions and 200 deletions.
3 changes: 2 additions & 1 deletion include/xrpl/protocol/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,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 = 76;
static constexpr std::size_t numFeatures = 77;

/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
Expand Down Expand Up @@ -369,6 +369,7 @@ extern uint256 const fixAMMv1_1;
extern uint256 const featureNFTokenMintOffer;
extern uint256 const fixReducedOffersV2;
extern uint256 const fixEnforceNFTokenTrustline;
extern uint256 const fixInnerObjTemplate2;

} // namespace ripple

Expand Down
3 changes: 1 addition & 2 deletions include/xrpl/protocol/STObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
namespace ripple {

class STArray;
class Rules;

inline void
throwFieldNotFound(SField const& field)
Expand Down Expand Up @@ -105,7 +104,7 @@ class STObject : public STBase, public CountedObject<STObject>
explicit STObject(SField const& name);

static STObject
makeInnerObject(SField const& name, Rules const& rules);
makeInnerObject(SField const& name);

iterator
begin() const;
Expand Down
1 change: 1 addition & 0 deletions src/libxrpl/protocol/Feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ REGISTER_FIX (fixAMMv1_1, Supported::yes, VoteBehavior::De
REGISTER_FEATURE(NFTokenMintOffer, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixReducedOffersV2, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixEnforceNFTokenTrustline, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixInnerObjTemplate2, Supported::yes, VoteBehavior::DefaultNo);

// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.
Expand Down
13 changes: 11 additions & 2 deletions src/libxrpl/protocol/STObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,19 @@ STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(
}

STObject
STObject::makeInnerObject(SField const& name, Rules const& rules)
STObject::makeInnerObject(SField const& name)
{
STObject obj{name};
if (rules.enabled(fixInnerObjTemplate))

// The if is complicated because inner object templates were added in
// two phases:
// 1. If there are no available Rules, then always apply the template.
// 2. fixInnerObjTemplate added templates to two AMM inner objects.
// 3. fixInnerObjTemplate2 added templates to all remaining inner objects.
std::optional<Rules> const& rules = getCurrentTransactionRules();
bool const isAMMObj = name == sfAuctionSlot || name == sfVoteEntry;
if (!rules || (rules->enabled(fixInnerObjTemplate) && isAMMObj) ||
(rules->enabled(fixInnerObjTemplate2) && !isAMMObj))
{
if (SOTemplate const* elements =
InnerObjectFormats::getInstance().findSOTemplateBySField(name))
Expand Down
10 changes: 6 additions & 4 deletions src/libxrpl/protocol/XChainAttestations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ AttestationClaim::AttestationClaim(Json::Value const& v)
STObject
AttestationClaim::toSTObject() const
{
STObject o{sfXChainClaimAttestationCollectionElement};
STObject o =
STObject::makeInnerObject(sfXChainClaimAttestationCollectionElement);
addHelper(o);
o[sfXChainClaimID] = claimID;
if (dst)
Expand Down Expand Up @@ -345,7 +346,8 @@ AttestationCreateAccount::AttestationCreateAccount(
STObject
AttestationCreateAccount::toSTObject() const
{
STObject o{sfXChainCreateAccountAttestationCollectionElement};
STObject o = STObject::makeInnerObject(
sfXChainCreateAccountAttestationCollectionElement);
addHelper(o);

o[sfXChainAccountCreateCount] = createCount;
Expand Down Expand Up @@ -497,7 +499,7 @@ XChainClaimAttestation::XChainClaimAttestation(
STObject
XChainClaimAttestation::toSTObject() const
{
STObject o{sfXChainClaimProofSig};
STObject o = STObject::makeInnerObject(sfXChainClaimProofSig);
o[sfAttestationSignerAccount] =
STAccount{sfAttestationSignerAccount, keyAccount};
o[sfPublicKey] = publicKey;
Expand Down Expand Up @@ -609,7 +611,7 @@ XChainCreateAccountAttestation::XChainCreateAccountAttestation(
STObject
XChainCreateAccountAttestation::toSTObject() const
{
STObject o{sfXChainCreateAccountProofSig};
STObject o = STObject::makeInnerObject(sfXChainCreateAccountProofSig);

o[sfAttestationSignerAccount] =
STAccount{sfAttestationSignerAccount, keyAccount};
Expand Down
125 changes: 125 additions & 0 deletions src/test/rpc/AccountObjects_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <test/jtx/xchain_bridge.h>
#include <xrpld/app/tx/detail/NFTokenMint.h>
#include <xrpl/json/json_reader.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/to_string.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/nft.h>

#include <boost/utility/string_ref.hpp>

Expand Down Expand Up @@ -1032,13 +1034,136 @@ class AccountObjects_test : public beast::unit_test::suite
BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::hashes), 0));
}

void
testNFTsMarker()
{
// there's some bug found in account_nfts method that it did not
// return invalid params when providing unassociated nft marker.
// this test tests both situations when providing valid nft marker
// and unassociated nft marker.
testcase("NFTsMarker");

using namespace jtx;
Env env(*this);

Account const bob{"bob"};
env.fund(XRP(10000), bob);

static constexpr unsigned nftsSize = 10;
for (unsigned i = 0; i < nftsSize; i++)
{
env(token::mint(bob, 0));
}

env.close();

// save the NFTokenIDs to use later
std::vector<Json::Value> tokenIDs;
{
Json::Value params;
params[jss::account] = bob.human();
params[jss::ledger_index] = "validated";
Json::Value const resp =
env.rpc("json", "account_nfts", to_string(params));
Json::Value const& nfts = resp[jss::result][jss::account_nfts];
for (Json::Value const& nft : nfts)
tokenIDs.push_back(nft["NFTokenID"]);
}

// this lambda function is used to check if the account_nfts method
// returns the correct token information. lastIndex is used to query the
// last marker.
auto compareNFTs = [&tokenIDs, &env, &bob](
unsigned const limit, unsigned const lastIndex) {
Json::Value params;
params[jss::account] = bob.human();
params[jss::limit] = limit;
params[jss::marker] = tokenIDs[lastIndex];
params[jss::ledger_index] = "validated";
Json::Value const resp =
env.rpc("json", "account_nfts", to_string(params));

if (resp[jss::result].isMember(jss::error))
return false;

Json::Value const& nfts = resp[jss::result][jss::account_nfts];
unsigned const nftsCount = tokenIDs.size() - lastIndex - 1 < limit
? tokenIDs.size() - lastIndex - 1
: limit;

if (nfts.size() != nftsCount)
return false;

for (unsigned i = 0; i < nftsCount; i++)
{
if (nfts[i]["NFTokenID"] != tokenIDs[lastIndex + 1 + i])
return false;
}

return true;
};

// test a valid marker which is equal to the third tokenID
BEAST_EXPECT(compareNFTs(4, 2));

// test a valid marker which is equal to the 8th tokenID
BEAST_EXPECT(compareNFTs(4, 7));

// lambda that holds common code for invalid cases.
auto testInvalidMarker = [&env, &bob](
auto marker, char const* errorMessage) {
Json::Value params;
params[jss::account] = bob.human();
params[jss::limit] = 4;
params[jss::ledger_index] = jss::validated;
params[jss::marker] = marker;
Json::Value const resp =
env.rpc("json", "account_nfts", to_string(params));
return resp[jss::result][jss::error_message] == errorMessage;
};

// test an invalid marker that is not a string
BEAST_EXPECT(
testInvalidMarker(17, "Invalid field \'marker\', not string."));

// test an invalid marker that has a non-hex character
BEAST_EXPECT(testInvalidMarker(
"00000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B900000000000000000G",
"Invalid field \'marker\'."));

// this lambda function is used to create some fake marker using given
// taxon and sequence because we want to test some unassociated markers
// later
auto createFakeNFTMarker = [](AccountID const& issuer,
std::uint32_t taxon,
std::uint32_t tokenSeq,
std::uint16_t flags = 0,
std::uint16_t fee = 0) {
// the marker has the exact same format as an NFTokenID
return to_string(NFTokenMint::createNFTokenID(
flags, fee, issuer, nft::toTaxon(taxon), tokenSeq));
};

// test an unassociated marker which does not exist in the NFTokenIDs
BEAST_EXPECT(testInvalidMarker(
createFakeNFTMarker(bob.id(), 0x000000000, 0x00000000),
"Invalid field \'marker\'."));

// test an unassociated marker which exceeds the maximum value of the
// existing NFTokenID
BEAST_EXPECT(testInvalidMarker(
createFakeNFTMarker(bob.id(), 0xFFFFFFFF, 0xFFFFFFFF),
"Invalid field \'marker\'."));
}

void
run() override
{
testErrors();
testUnsteppedThenStepped();
testUnsteppedThenSteppedWithNFTs();
testObjectTypes();
testNFTsMarker();
}
};

Expand Down
Loading

0 comments on commit 1c3b662

Please sign in to comment.