Skip to content

Commit

Permalink
DepositPreauth MPT support
Browse files Browse the repository at this point in the history
  • Loading branch information
oleks-rip committed Nov 6, 2024
1 parent e34a1c8 commit 82605a8
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 4 deletions.
152 changes: 152 additions & 0 deletions src/test/app/MPToken_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,157 @@ class MPToken_test : public beast::unit_test::suite
}
}

void
testDepositPreauth()
{
testcase("DepositPreauth");

using namespace test::jtx;
Account const alice("alice"); // issuer
Account const bob("bob"); // holder
Account const diana("diana");
Account const dpIssuer("dpIssuer"); // holder

const char credType[] = "abcde";

{
Env env(*this);

env.fund(XRP(50000), diana, dpIssuer);
env.close();

MPTTester mptAlice(env, alice, {.holders = {bob}});
mptAlice.create(
{.ownerCount = 1,
.holderCount = 0,
.flags = tfMPTRequireAuth | tfMPTCanTransfer});

env(pay(diana, bob, XRP(500)));
env.close();

// bob creates an empty MPToken
mptAlice.authorize({.account = bob});
// alice authorizes bob to hold funds
mptAlice.authorize({.account = alice, .holder = bob});

// Bob require preauthorization
env(fset(bob, asfDepositAuth));
env.close();

// alice try to send 100 MPT to bob, not authorized
mptAlice.pay(alice, bob, 100, tecNO_PERMISSION);
env.close();

// Bob authorize alice
env(deposit::auth(bob, alice));
env.close();

// alice sends 100 MPT to bob
mptAlice.pay(alice, bob, 100);
env.close();

// Create credentials
env(credentials::create(alice, dpIssuer, credType));
env.close();
env(credentials::accept(alice, dpIssuer, credType));
env.close();
auto const jv =
credentials::ledgerEntry(env, alice, dpIssuer, credType);
std::string const credIdx = jv[jss::result][jss::index].asString();

// alice sends 100 MPT to bob with credentials which aren't required
mptAlice.pay(alice, bob, 100, tesSUCCESS, {{credIdx}});
env.close();

// Bob revoke authorization
env(deposit::unauth(bob, alice));
env.close();

// alice try to send 100 MPT to bob, not authorized
mptAlice.pay(alice, bob, 100, tecNO_PERMISSION);
env.close();

// alice sends 100 MPT to bob with credentials, not authorized
mptAlice.pay(alice, bob, 100, tecNO_PERMISSION, {{credIdx}});
env.close();

// Bob authorize credentials
env(deposit::authCredentials(bob, {{dpIssuer, credType}}));
env.close();

// alice try to send 100 MPT to bob, not authorized
mptAlice.pay(alice, bob, 100, tecNO_PERMISSION);
env.close();

// alice sends 100 MPT to bob with credentials
mptAlice.pay(alice, bob, 100, tesSUCCESS, {{credIdx}});
env.close();
}

testcase("DepositPreauth disabled featureCredentials");
{
Env env(*this, supported_amendments() - featureCredentials);

std::string const credIdx =
"D007AE4B6E1274B4AF872588267B810C2F82716726351D1C7D38D3E5499FC6"
"E2";

env.fund(XRP(50000), diana, dpIssuer);
env.close();

MPTTester mptAlice(env, alice, {.holders = {bob}});
mptAlice.create(
{.ownerCount = 1,
.holderCount = 0,
.flags = tfMPTRequireAuth | tfMPTCanTransfer});

env(pay(diana, bob, XRP(500)));
env.close();

// bob creates an empty MPToken
mptAlice.authorize({.account = bob});
// alice authorizes bob to hold funds
mptAlice.authorize({.account = alice, .holder = bob});

// Bob require preauthorization
env(fset(bob, asfDepositAuth));
env.close();

// alice try to send 100 MPT to bob, authorization is not checked
mptAlice.pay(alice, bob, 100);
env.close();

// alice try to send 100 MPT to bob with credentials, amendment
// disabled
mptAlice.pay(alice, bob, 100, temDISABLED, {{credIdx}});
env.close();

// Bob authorize alice
env(deposit::auth(bob, alice));
env.close();

// alice sends 100 MPT to bob, authorization is not checked
mptAlice.pay(alice, bob, 100);
env.close();

// alice sends 100 MPT to bob with credentials, amendment disabled
mptAlice.pay(alice, bob, 100, temDISABLED, {{credIdx}});
env.close();

// Bob revoke authorization
env(deposit::unauth(bob, alice));
env.close();

// alice try to send 100 MPT to bob, authorization is not checked
mptAlice.pay(alice, bob, 100);
env.close();

// alice sends 100 MPT to bob with credentials, amendment disabled
mptAlice.pay(alice, bob, 100, temDISABLED, {{credIdx}});
env.close();
}
}

void
testMPTInvalidInTx(FeatureBitset features)
{
Expand Down Expand Up @@ -1979,6 +2130,7 @@ class MPToken_test : public beast::unit_test::suite

// Test Direct Payment
testPayment(all);
testDepositPreauth();

// Test MPT Amount is invalid in Tx, which don't support MPT
testMPTInvalidInTx(all);
Expand Down
13 changes: 11 additions & 2 deletions src/test/jtx/impl/mpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,23 @@ MPTTester::pay(
Account const& src,
Account const& dest,
std::int64_t amount,
std::optional<TER> err)
std::optional<TER> err,
std::optional<std::vector<std::string>> credentials)
{
if (!id_)
Throw<std::runtime_error>("MPT has not been created");
auto const srcAmt = getBalance(src);
auto const destAmt = getBalance(dest);
auto const outstnAmt = getBalance(issuer_);
env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)));

if (credentials)
env_(
jtx::pay(src, dest, mpt(amount)),
ter(err.value_or(tesSUCCESS)),
credentials::ids(*credentials));
else
env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)));

if (env_.ter() != tesSUCCESS)
amount = 0;
if (close_)
Expand Down
3 changes: 2 additions & 1 deletion src/test/jtx/mpt.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ class MPTTester
pay(Account const& src,
Account const& dest,
std::int64_t amount,
std::optional<TER> err = std::nullopt);
std::optional<TER> err = std::nullopt,
std::optional<std::vector<std::string>> credentials = std::nullopt);

void
claw(
Expand Down
8 changes: 8 additions & 0 deletions src/xrpld/app/tx/detail/Payment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,14 @@ Payment::doApply()
ter != tesSUCCESS)
return ter;

if (view().rules().enabled(featureCredentials))
{
if (auto err = credentials::verify(
ctx_, account_, dstAccountID, reqDepositAuth);
!isTesSuccess(err))
return err;
}

auto const& issuer = mptIssue.getIssuer();

// Transfer rate
Expand Down
2 changes: 1 addition & 1 deletion src/xrpld/rpc/handlers/LedgerEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ doLedgerEntry(RPC::JsonContext& context)
}
else
{
auto const& ac(dp[jss::authorize_credentials]);
auto const& ac(dp[jss::authorized_credentials]);
STArray const arr = parseAuthorizeCredentials(ac);

if (arr.empty() || (arr.size() > maxCredentialsArraySize))
Expand Down

0 comments on commit 82605a8

Please sign in to comment.