featureDisallowIncoming: Opt-out of incoming Checks, PayChans, NFTokenOffers and Trustlines (#4336)

featureDisallowIncoming is a new amendment that would allow users to opt-out of incoming Checks, Payment Channels, NFTokenOffers, and trust lines. This commit includes tests.

Adds four new AccountSet Flags:
1. asfDisallowIncomingNFTOffer
2. asfDisallowIncomingCheck
3. asfDisallowIncomingPayChan
4. asfDisallowIncomingTrustline
This commit is contained in:
RichardAH
2022-12-20 02:35:35 +01:00
committed by GitHub
parent c3a9f3dbf3
commit d8a84e9530
14 changed files with 644 additions and 99 deletions

View File

@@ -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

View File

@@ -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);
}
};

View File

@@ -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,
@@ -175,12 +177,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testSimple()
testSimple(FeatureBitset features)
{
testcase("simple");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto USDA = alice["USD"];
@@ -350,7 +352,91 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testCancelAfter()
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)
{
testcase("cancel after");
using namespace jtx;
@@ -360,7 +446,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);
Env env{*this, features};
env.fund(XRP(10000), alice, bob);
auto const pk = alice.pk();
auto const settleDelay = 100s;
@@ -392,7 +478,7 @@ struct PayChan_test : public beast::unit_test::suite
}
{
// Third party can close after cancel after
Env env(*this);
Env env{*this, features};
env.fund(XRP(10000), alice, bob, carol);
auto const pk = alice.pk();
auto const settleDelay = 100s;
@@ -415,12 +501,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testExpiration()
testExpiration(FeatureBitset features)
{
testcase("expiration");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
@@ -481,12 +567,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testSettleDelay()
testSettleDelay(FeatureBitset features)
{
testcase("settle delay");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
@@ -541,12 +627,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testCloseDry()
testCloseDry(FeatureBitset features)
{
testcase("close dry");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
@@ -575,13 +661,13 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testDefaultAmount()
testDefaultAmount(FeatureBitset features)
{
// auth amount defaults to balance if not present
testcase("default amount");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
@@ -630,7 +716,7 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testDisallowXRP()
testDisallowXRP(FeatureBitset features)
{
// auth amount defaults to balance if not present
testcase("Disallow XRP");
@@ -641,7 +727,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));
@@ -652,7 +738,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);
Env env{*this, features};
env.fund(XRP(10000), alice, bob);
env(fset(bob, asfDisallowXRP));
auto const chan = channel(alice, bob, env.seq(alice));
@@ -663,7 +749,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()));
@@ -677,7 +763,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);
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()));
@@ -690,14 +776,14 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testDstTag()
testDstTag(FeatureBitset features)
{
// auth amount defaults to balance if not present
testcase("Dst Tag");
using namespace jtx;
using namespace std::literals::chrono_literals;
// Create a channel where dst disallows XRP
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
@@ -720,7 +806,7 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testDepositAuth()
testDepositAuth(FeatureBitset features)
{
testcase("Deposit Authorization");
using namespace jtx;
@@ -731,7 +817,7 @@ struct PayChan_test : public beast::unit_test::suite
auto const carol = Account("carol");
auto USDA = alice["USD"];
{
Env env(*this);
Env env{*this, features};
env.fund(XRP(10000), alice, bob, carol);
env(fset(bob, asfDepositAuth));
@@ -844,13 +930,13 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testMultiple()
testMultiple(FeatureBitset features)
{
// auth amount defaults to balance if not present
testcase("Multiple channels to the same account");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
@@ -867,13 +953,13 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testAccountChannelsRPC()
testAccountChannelsRPC(FeatureBitset features)
{
testcase("AccountChannels RPC");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const charlie = Account("charlie", KeyType::ed25519);
@@ -922,7 +1008,7 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testAccountChannelsRPCMarkers()
testAccountChannelsRPCMarkers(FeatureBitset features)
{
testcase("Account channels RPC markers");
@@ -941,7 +1027,7 @@ struct PayChan_test : public beast::unit_test::suite
return r;
}();
Env env(*this);
Env env{*this, features};
env.fund(XRP(10000), alice);
for (auto const& a : bobs)
{
@@ -1038,7 +1124,7 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testAccountChannelsRPCSenderOnly()
testAccountChannelsRPCSenderOnly(FeatureBitset features)
{
// Check that the account_channels command only returns channels owned
// by the account
@@ -1049,7 +1135,7 @@ struct PayChan_test : public beast::unit_test::suite
auto const alice = Account("alice");
auto const bob = Account("bob");
Env env(*this);
Env env{*this, features};
env.fund(XRP(10000), alice, bob);
// Create a channel from alice to bob and from bob to alice
@@ -1075,12 +1161,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testAuthVerifyRPC()
testAuthVerifyRPC(FeatureBitset features)
{
testcase("PayChan Auth/Verify RPC");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const charlie = Account("charlie", KeyType::ed25519);
@@ -1415,12 +1501,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testOptionalFields()
testOptionalFields(FeatureBitset features)
{
testcase("Optional Fields");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
@@ -1466,12 +1552,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testMalformedPK()
testMalformedPK(FeatureBitset features)
{
testcase("malformed pk");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto USDA = alice["USD"];
@@ -1536,7 +1622,7 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testMetaAndOwnership()
testMetaAndOwnership(FeatureBitset features)
{
testcase("Metadata & Ownership");
@@ -1565,8 +1651,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();
@@ -1587,7 +1672,7 @@ struct PayChan_test : public beast::unit_test::suite
{
// Test with adding the paychan to the recipient's owner directory
Env env(*this);
Env env{*this, features};
env.fund(XRP(10000), alice, bob);
env(create(alice, bob, XRP(1000), settleDelay, pk));
env.close();
@@ -1609,8 +1694,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));
@@ -1644,7 +1728,7 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testAccountDelete()
testAccountDelete(FeatureBitset features)
{
testcase("Account Delete");
using namespace test::jtx;
@@ -1678,8 +1762,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();
@@ -1771,8 +1855,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;
@@ -1878,12 +1961,12 @@ struct PayChan_test : public beast::unit_test::suite
}
void
testUsingTickets()
testUsingTickets(FeatureBitset features)
{
testcase("using tickets");
using namespace jtx;
using namespace std::literals::chrono_literals;
Env env(*this);
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto USDA = alice["USD"];
@@ -2039,28 +2122,39 @@ struct PayChan_test : public beast::unit_test::suite
BEAST_EXPECT(env.seq(bob) == bobSeq);
}
void
testWithFeats(FeatureBitset features)
{
testSimple(features);
testDisallowIncoming(features);
testCancelAfter(features);
testSettleDelay(features);
testExpiration(features);
testCloseDry(features);
testDefaultAmount(features);
testDisallowXRP(features);
testDstTag(features);
testDepositAuth(features);
testMultiple(features);
testAccountChannelsRPC(features);
testAccountChannelsRPCMarkers(features);
testAccountChannelsRPCSenderOnly(features);
testAuthVerifyRPC(features);
testOptionalFields(features);
testMalformedPK(features);
testMetaAndOwnership(features);
testAccountDelete(features);
testUsingTickets(features);
}
public:
void
run() override
{
testSimple();
testCancelAfter();
testSettleDelay();
testExpiration();
testCloseDry();
testDefaultAmount();
testDisallowXRP();
testDstTag();
testDepositAuth();
testMultiple();
testAccountChannelsRPC();
testAccountChannelsRPCMarkers();
testAccountChannelsRPCSenderOnly();
testAuthVerifyRPC();
testOptionalFields();
testMalformedPK();
testMetaAndOwnership();
testAccountDelete();
testUsingTickets();
using namespace test::jtx;
FeatureBitset const all{supported_amendments()};
testWithFeats(all - disallowIncoming);
testWithFeats(all);
}
};

View File

@@ -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);