mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Add support for DomainID in MPTokenIssuance transactions (#5509)
This change adds support for `DomainID` to existing transactions `MPTokenIssuanceCreate` and `MPTokenIssuanceSet`. In #5224 `DomainID` was added as an access control mechanism for `SingleAssetVault`. The actual implementation of this feature lies in `MPToken` and `MPTokenIssuance`, hence it makes sense to enable the use of `DomainID` also in `MPTokenIssuanceCreate` and `MPTokenIssuanceSet`, following same rules as in Vault: * `MPTokenIssuanceCreate` and `MPTokenIssuanceSet` can only set `DomainID` if flag `MPTRequireAuth` is set. * `MPTokenIssuanceCreate` requires that `DomainID` be a non-zero, uint256 number. * `MPTokenIssuanceSet` allows `DomainID` to be zero (or empty) in which case it will remove `DomainID` from the `MPTokenIssuance` object. The change is amendment-gated by `SingleAssetVault`. This is a non-breaking change because `SingleAssetVault` amendment is `Supported::no`, i.e. at this moment considered a work in progress, which cannot be enabled on the network.
This commit is contained in:
@@ -18,10 +18,16 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/credentials.h>
|
||||
#include <test/jtx/permissioned_domains.h>
|
||||
#include <test/jtx/trust.h>
|
||||
#include <test/jtx/xchain_bridge.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -61,6 +67,48 @@ class MPToken_test : public beast::unit_test::suite
|
||||
.metadata = "test",
|
||||
.err = temMALFORMED});
|
||||
|
||||
if (!features[featureSingleAssetVault])
|
||||
{
|
||||
// tries to set DomainID when SAV is disabled
|
||||
mptAlice.create(
|
||||
{.maxAmt = 100,
|
||||
.assetScale = 0,
|
||||
.metadata = "test",
|
||||
.flags = tfMPTRequireAuth,
|
||||
.domainID = uint256(42),
|
||||
.err = temDISABLED});
|
||||
}
|
||||
else if (!features[featurePermissionedDomains])
|
||||
{
|
||||
// tries to set DomainID when PD is disabled
|
||||
mptAlice.create(
|
||||
{.maxAmt = 100,
|
||||
.assetScale = 0,
|
||||
.metadata = "test",
|
||||
.flags = tfMPTRequireAuth,
|
||||
.domainID = uint256(42),
|
||||
.err = temDISABLED});
|
||||
}
|
||||
else
|
||||
{
|
||||
// tries to set DomainID when RequireAuth is not set
|
||||
mptAlice.create(
|
||||
{.maxAmt = 100,
|
||||
.assetScale = 0,
|
||||
.metadata = "test",
|
||||
.domainID = uint256(42),
|
||||
.err = temMALFORMED});
|
||||
|
||||
// tries to set zero DomainID
|
||||
mptAlice.create(
|
||||
{.maxAmt = 100,
|
||||
.assetScale = 0,
|
||||
.metadata = "test",
|
||||
.flags = tfMPTRequireAuth,
|
||||
.domainID = beast::zero,
|
||||
.err = temMALFORMED});
|
||||
}
|
||||
|
||||
// tries to set a txfee greater than max
|
||||
mptAlice.create(
|
||||
{.maxAmt = 100,
|
||||
@@ -140,6 +188,48 @@ class MPToken_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(
|
||||
result[sfMaximumAmount.getJsonName()] == "9223372036854775807");
|
||||
}
|
||||
|
||||
if (features[featureSingleAssetVault])
|
||||
{
|
||||
// Add permissioned domain
|
||||
Account const credIssuer1{"credIssuer1"};
|
||||
std::string const credType = "credential";
|
||||
|
||||
pdomain::Credentials const credentials1{
|
||||
{.issuer = credIssuer1, .credType = credType}};
|
||||
|
||||
{
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(1000), credIssuer1);
|
||||
|
||||
env(pdomain::setTx(credIssuer1, credentials1));
|
||||
auto const domainId1 = [&]() {
|
||||
auto tx = env.tx()->getJson(JsonOptions::none);
|
||||
return pdomain::getNewDomain(env.meta());
|
||||
}();
|
||||
|
||||
MPTTester mptAlice(env, alice);
|
||||
mptAlice.create({
|
||||
.maxAmt = maxMPTokenAmount, // 9'223'372'036'854'775'807
|
||||
.assetScale = 1,
|
||||
.transferFee = 10,
|
||||
.metadata = "123",
|
||||
.ownerCount = 1,
|
||||
.flags = tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow |
|
||||
tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback,
|
||||
.domainID = domainId1,
|
||||
});
|
||||
|
||||
// Get the hash for the most recent transaction.
|
||||
std::string const txHash{
|
||||
env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
|
||||
|
||||
Json::Value const result = env.rpc("tx", txHash)[jss::result];
|
||||
BEAST_EXPECT(
|
||||
result[sfMaximumAmount.getJsonName()] ==
|
||||
"9223372036854775807");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -499,6 +589,59 @@ class MPToken_test : public beast::unit_test::suite
|
||||
.flags = 0x00000008,
|
||||
.err = temINVALID_FLAG});
|
||||
|
||||
if (!features[featureSingleAssetVault])
|
||||
{
|
||||
// test invalid flags - nothing is being changed
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.flags = 0x00000000,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
.flags = 0x00000000,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
// cannot set DomainID since SAV is not enabled
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.domainID = uint256(42),
|
||||
.err = temDISABLED});
|
||||
}
|
||||
else
|
||||
{
|
||||
// test invalid flags - nothing is being changed
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.flags = 0x00000000,
|
||||
.err = temMALFORMED});
|
||||
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
.flags = 0x00000000,
|
||||
.err = temMALFORMED});
|
||||
|
||||
if (!features[featurePermissionedDomains])
|
||||
{
|
||||
// cannot set DomainID since PD is not enabled
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.domainID = uint256(42),
|
||||
.err = temDISABLED});
|
||||
}
|
||||
else
|
||||
{
|
||||
// cannot set DomainID since Holder is set
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
.domainID = uint256(42),
|
||||
.err = temMALFORMED});
|
||||
}
|
||||
}
|
||||
|
||||
// set both lock and unlock flags at the same time will fail
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
@@ -582,6 +725,53 @@ class MPToken_test : public beast::unit_test::suite
|
||||
mptAlice.set(
|
||||
{.holder = cindy, .flags = tfMPTLock, .err = tecNO_DST});
|
||||
}
|
||||
|
||||
if (features[featureSingleAssetVault] &&
|
||||
features[featurePermissionedDomains])
|
||||
{
|
||||
// Add permissioned domain
|
||||
Account const credIssuer1{"credIssuer1"};
|
||||
std::string const credType = "credential";
|
||||
|
||||
pdomain::Credentials const credentials1{
|
||||
{.issuer = credIssuer1, .credType = credType}};
|
||||
|
||||
{
|
||||
Env env{*this, features};
|
||||
|
||||
MPTTester mptAlice(env, alice);
|
||||
mptAlice.create({});
|
||||
|
||||
// Trying to set DomainID on a public MPTokenIssuance
|
||||
mptAlice.set(
|
||||
{.domainID = uint256(42), .err = tecNO_PERMISSION});
|
||||
|
||||
mptAlice.set(
|
||||
{.domainID = beast::zero, .err = tecNO_PERMISSION});
|
||||
}
|
||||
|
||||
{
|
||||
Env env{*this, features};
|
||||
|
||||
MPTTester mptAlice(env, alice);
|
||||
mptAlice.create({.flags = tfMPTRequireAuth});
|
||||
|
||||
// Trying to set non-existing DomainID
|
||||
mptAlice.set(
|
||||
{.domainID = uint256(42), .err = tecOBJECT_NOT_FOUND});
|
||||
|
||||
// Trying to lock but locking is disabled
|
||||
mptAlice.set(
|
||||
{.flags = tfMPTUnlock,
|
||||
.domainID = uint256(42),
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
mptAlice.set(
|
||||
{.flags = tfMPTUnlock,
|
||||
.domainID = beast::zero,
|
||||
.err = tecNO_PERMISSION});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -590,71 +780,136 @@ class MPToken_test : public beast::unit_test::suite
|
||||
testcase("Enabled set transaction");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
// Test locking and unlocking
|
||||
Env env{*this, features};
|
||||
Account const alice("alice"); // issuer
|
||||
Account const bob("bob"); // holder
|
||||
|
||||
MPTTester mptAlice(env, alice, {.holders = {bob}});
|
||||
|
||||
// create a mptokenissuance with locking
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock});
|
||||
|
||||
mptAlice.authorize({.account = bob, .holderCount = 1});
|
||||
|
||||
// locks bob's mptoken
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
|
||||
// trying to lock bob's mptoken again will still succeed
|
||||
// but no changes to the objects
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
|
||||
// alice locks the mptissuance
|
||||
mptAlice.set({.account = alice, .flags = tfMPTLock});
|
||||
|
||||
// alice tries to lock up both mptissuance and mptoken again
|
||||
// it will not change the flags and both will remain locked.
|
||||
mptAlice.set({.account = alice, .flags = tfMPTLock});
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
|
||||
// alice unlocks bob's mptoken
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTUnlock});
|
||||
|
||||
// locks up bob's mptoken again
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
if (!features[featureSingleAssetVault])
|
||||
{
|
||||
// Delete bobs' mptoken even though it is locked
|
||||
mptAlice.authorize({.account = bob, .flags = tfMPTUnauthorize});
|
||||
// Test locking and unlocking
|
||||
Env env{*this, features};
|
||||
|
||||
MPTTester mptAlice(env, alice, {.holders = {bob}});
|
||||
|
||||
// create a mptokenissuance with locking
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock});
|
||||
|
||||
mptAlice.authorize({.account = bob, .holderCount = 1});
|
||||
|
||||
// locks bob's mptoken
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
|
||||
// trying to lock bob's mptoken again will still succeed
|
||||
// but no changes to the objects
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
|
||||
// alice locks the mptissuance
|
||||
mptAlice.set({.account = alice, .flags = tfMPTLock});
|
||||
|
||||
// alice tries to lock up both mptissuance and mptoken again
|
||||
// it will not change the flags and both will remain locked.
|
||||
mptAlice.set({.account = alice, .flags = tfMPTLock});
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
|
||||
// alice unlocks bob's mptoken
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
.flags = tfMPTUnlock,
|
||||
.err = tecOBJECT_NOT_FOUND});
|
||||
{.account = alice, .holder = bob, .flags = tfMPTUnlock});
|
||||
|
||||
return;
|
||||
// locks up bob's mptoken again
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
if (!features[featureSingleAssetVault])
|
||||
{
|
||||
// Delete bobs' mptoken even though it is locked
|
||||
mptAlice.authorize({.account = bob, .flags = tfMPTUnauthorize});
|
||||
|
||||
mptAlice.set(
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
.flags = tfMPTUnlock,
|
||||
.err = tecOBJECT_NOT_FOUND});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Cannot delete locked MPToken
|
||||
mptAlice.authorize(
|
||||
{.account = bob,
|
||||
.flags = tfMPTUnauthorize,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
// alice unlocks mptissuance
|
||||
mptAlice.set({.account = alice, .flags = tfMPTUnlock});
|
||||
|
||||
// alice unlocks bob's mptoken
|
||||
mptAlice.set(
|
||||
{.account = alice, .holder = bob, .flags = tfMPTUnlock});
|
||||
|
||||
// alice unlocks mptissuance and bob's mptoken again despite that
|
||||
// they are already unlocked. Make sure this will not change the
|
||||
// flags
|
||||
mptAlice.set(
|
||||
{.account = alice, .holder = bob, .flags = tfMPTUnlock});
|
||||
mptAlice.set({.account = alice, .flags = tfMPTUnlock});
|
||||
}
|
||||
|
||||
// Cannot delete locked MPToken
|
||||
mptAlice.authorize(
|
||||
{.account = bob,
|
||||
.flags = tfMPTUnauthorize,
|
||||
.err = tecNO_PERMISSION});
|
||||
if (features[featureSingleAssetVault])
|
||||
{
|
||||
// Add permissioned domain
|
||||
std::string const credType = "credential";
|
||||
|
||||
// alice unlocks mptissuance
|
||||
mptAlice.set({.account = alice, .flags = tfMPTUnlock});
|
||||
// Test setting and resetting domain ID
|
||||
Env env{*this, features};
|
||||
|
||||
// alice unlocks bob's mptoken
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTUnlock});
|
||||
auto const domainId1 = [&]() {
|
||||
Account const credIssuer1{"credIssuer1"};
|
||||
env.fund(XRP(1000), credIssuer1);
|
||||
|
||||
// alice unlocks mptissuance and bob's mptoken again despite that
|
||||
// they are already unlocked. Make sure this will not change the
|
||||
// flags
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTUnlock});
|
||||
mptAlice.set({.account = alice, .flags = tfMPTUnlock});
|
||||
pdomain::Credentials const credentials1{
|
||||
{.issuer = credIssuer1, .credType = credType}};
|
||||
|
||||
env(pdomain::setTx(credIssuer1, credentials1));
|
||||
return [&]() {
|
||||
auto tx = env.tx()->getJson(JsonOptions::none);
|
||||
return pdomain::getNewDomain(env.meta());
|
||||
}();
|
||||
}();
|
||||
|
||||
auto const domainId2 = [&]() {
|
||||
Account const credIssuer2{"credIssuer2"};
|
||||
env.fund(XRP(1000), credIssuer2);
|
||||
|
||||
pdomain::Credentials const credentials2{
|
||||
{.issuer = credIssuer2, .credType = credType}};
|
||||
|
||||
env(pdomain::setTx(credIssuer2, credentials2));
|
||||
return [&]() {
|
||||
auto tx = env.tx()->getJson(JsonOptions::none);
|
||||
return pdomain::getNewDomain(env.meta());
|
||||
}();
|
||||
}();
|
||||
|
||||
MPTTester mptAlice(env, alice, {.holders = {bob}});
|
||||
|
||||
// create a mptokenissuance with auth.
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth});
|
||||
BEAST_EXPECT(mptAlice.checkDomainID(std::nullopt));
|
||||
|
||||
// reset "domain not set" to "domain not set", i.e. no change
|
||||
mptAlice.set({.domainID = beast::zero});
|
||||
BEAST_EXPECT(mptAlice.checkDomainID(std::nullopt));
|
||||
|
||||
// reset "domain not set" to domain1
|
||||
mptAlice.set({.domainID = domainId1});
|
||||
BEAST_EXPECT(mptAlice.checkDomainID(domainId1));
|
||||
|
||||
// reset domain1 to domain2
|
||||
mptAlice.set({.domainID = domainId2});
|
||||
BEAST_EXPECT(mptAlice.checkDomainID(domainId2));
|
||||
|
||||
// reset domain to "domain not set"
|
||||
mptAlice.set({.domainID = beast::zero});
|
||||
BEAST_EXPECT(mptAlice.checkDomainID(std::nullopt));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -889,6 +1144,200 @@ class MPToken_test : public beast::unit_test::suite
|
||||
mptAlice.pay(bob, alice, 100, tecNO_AUTH);
|
||||
}
|
||||
|
||||
if (features[featureSingleAssetVault] &&
|
||||
features[featurePermissionedDomains])
|
||||
{
|
||||
// If RequireAuth is enabled and domain is a match, payment succeeds
|
||||
{
|
||||
Env env{*this, features};
|
||||
std::string const credType = "credential";
|
||||
Account const credIssuer1{"credIssuer1"};
|
||||
env.fund(XRP(1000), credIssuer1, bob);
|
||||
|
||||
auto const domainId1 = [&]() {
|
||||
pdomain::Credentials const credentials1{
|
||||
{.issuer = credIssuer1, .credType = credType}};
|
||||
|
||||
env(pdomain::setTx(credIssuer1, credentials1));
|
||||
return [&]() {
|
||||
auto tx = env.tx()->getJson(JsonOptions::none);
|
||||
return pdomain::getNewDomain(env.meta());
|
||||
}();
|
||||
}();
|
||||
// bob is authorized via domain
|
||||
env(credentials::create(bob, credIssuer1, credType));
|
||||
env(credentials::accept(bob, credIssuer1, credType));
|
||||
env.close();
|
||||
|
||||
MPTTester mptAlice(env, alice, {});
|
||||
env.close();
|
||||
|
||||
mptAlice.create({
|
||||
.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTRequireAuth | tfMPTCanTransfer,
|
||||
.domainID = domainId1,
|
||||
});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
env.close();
|
||||
|
||||
// bob is authorized via domain
|
||||
mptAlice.pay(alice, bob, 100);
|
||||
mptAlice.set({.domainID = beast::zero});
|
||||
|
||||
// bob is no longer authorized
|
||||
mptAlice.pay(alice, bob, 100, tecNO_AUTH);
|
||||
}
|
||||
|
||||
{
|
||||
Env env{*this, features};
|
||||
std::string const credType = "credential";
|
||||
Account const credIssuer1{"credIssuer1"};
|
||||
env.fund(XRP(1000), credIssuer1, bob);
|
||||
|
||||
auto const domainId1 = [&]() {
|
||||
pdomain::Credentials const credentials1{
|
||||
{.issuer = credIssuer1, .credType = credType}};
|
||||
|
||||
env(pdomain::setTx(credIssuer1, credentials1));
|
||||
return [&]() {
|
||||
auto tx = env.tx()->getJson(JsonOptions::none);
|
||||
return pdomain::getNewDomain(env.meta());
|
||||
}();
|
||||
}();
|
||||
// bob is authorized via domain
|
||||
env(credentials::create(bob, credIssuer1, credType));
|
||||
env(credentials::accept(bob, credIssuer1, credType));
|
||||
env.close();
|
||||
|
||||
MPTTester mptAlice(env, alice, {});
|
||||
env.close();
|
||||
|
||||
mptAlice.create({
|
||||
.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTRequireAuth | tfMPTCanTransfer,
|
||||
.domainID = domainId1,
|
||||
});
|
||||
|
||||
// bob creates an empty MPToken
|
||||
mptAlice.authorize({.account = bob});
|
||||
|
||||
// alice authorizes bob to hold funds
|
||||
mptAlice.authorize({.account = alice, .holder = bob});
|
||||
|
||||
// alice sends 100 MPT to bob
|
||||
mptAlice.pay(alice, bob, 100);
|
||||
|
||||
// alice UNAUTHORIZES bob
|
||||
mptAlice.authorize(
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
.flags = tfMPTUnauthorize});
|
||||
|
||||
// bob is still authorized, via domain
|
||||
mptAlice.pay(bob, alice, 10);
|
||||
|
||||
mptAlice.set({.domainID = beast::zero});
|
||||
|
||||
// bob fails to send back to alice because he is no longer
|
||||
// authorize to move his funds!
|
||||
mptAlice.pay(bob, alice, 10, tecNO_AUTH);
|
||||
}
|
||||
|
||||
{
|
||||
Env env{*this, features};
|
||||
std::string const credType = "credential";
|
||||
// credIssuer1 is the owner of domainId1 and a credential issuer
|
||||
Account const credIssuer1{"credIssuer1"};
|
||||
// credIssuer2 is the owner of domainId2 and a credential issuer
|
||||
// Note, domainId2 also lists credentials issued by credIssuer1
|
||||
Account const credIssuer2{"credIssuer2"};
|
||||
env.fund(XRP(1000), credIssuer1, credIssuer2, bob, carol);
|
||||
|
||||
auto const domainId1 = [&]() {
|
||||
pdomain::Credentials const credentials{
|
||||
{.issuer = credIssuer1, .credType = credType}};
|
||||
|
||||
env(pdomain::setTx(credIssuer1, credentials));
|
||||
return [&]() {
|
||||
auto tx = env.tx()->getJson(JsonOptions::none);
|
||||
return pdomain::getNewDomain(env.meta());
|
||||
}();
|
||||
}();
|
||||
|
||||
auto const domainId2 = [&]() {
|
||||
pdomain::Credentials const credentials{
|
||||
{.issuer = credIssuer1, .credType = credType},
|
||||
{.issuer = credIssuer2, .credType = credType}};
|
||||
|
||||
env(pdomain::setTx(credIssuer2, credentials));
|
||||
return [&]() {
|
||||
auto tx = env.tx()->getJson(JsonOptions::none);
|
||||
return pdomain::getNewDomain(env.meta());
|
||||
}();
|
||||
}();
|
||||
|
||||
// bob is authorized via credIssuer1 which is recognized by both
|
||||
// domainId1 and domainId2
|
||||
env(credentials::create(bob, credIssuer1, credType));
|
||||
env(credentials::accept(bob, credIssuer1, credType));
|
||||
env.close();
|
||||
|
||||
// carol is authorized via credIssuer2, only recognized by
|
||||
// domainId2
|
||||
env(credentials::create(carol, credIssuer2, credType));
|
||||
env(credentials::accept(carol, credIssuer2, credType));
|
||||
env.close();
|
||||
|
||||
MPTTester mptAlice(env, alice, {});
|
||||
env.close();
|
||||
|
||||
mptAlice.create({
|
||||
.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTRequireAuth | tfMPTCanTransfer,
|
||||
.domainID = domainId1,
|
||||
});
|
||||
|
||||
// bob and carol create an empty MPToken
|
||||
mptAlice.authorize({.account = bob});
|
||||
mptAlice.authorize({.account = carol});
|
||||
env.close();
|
||||
|
||||
// alice sends 50 MPT to bob but cannot send to carol
|
||||
mptAlice.pay(alice, bob, 50);
|
||||
mptAlice.pay(alice, carol, 50, tecNO_AUTH);
|
||||
env.close();
|
||||
|
||||
// bob cannot send to carol because they are not on the same
|
||||
// domain (since credIssuer2 is not recognized by domainId1)
|
||||
mptAlice.pay(bob, carol, 10, tecNO_AUTH);
|
||||
env.close();
|
||||
|
||||
// alice updates domainID to domainId2 which recognizes both
|
||||
// credIssuer1 and credIssuer2
|
||||
mptAlice.set({.domainID = domainId2});
|
||||
// alice can now send to carol
|
||||
mptAlice.pay(alice, carol, 10);
|
||||
env.close();
|
||||
|
||||
// bob can now send to carol because both are in the same
|
||||
// domain
|
||||
mptAlice.pay(bob, carol, 10);
|
||||
env.close();
|
||||
|
||||
// bob loses his authorization and can no longer send MPT
|
||||
env(credentials::deleteCred(
|
||||
credIssuer1, bob, credIssuer1, credType));
|
||||
env.close();
|
||||
|
||||
mptAlice.pay(bob, carol, 10, tecNO_AUTH);
|
||||
mptAlice.pay(bob, alice, 10, tecNO_AUTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-issuer cannot send to each other if MPTCanTransfer isn't set
|
||||
{
|
||||
Env env(*this, features);
|
||||
@@ -1340,10 +1789,8 @@ class MPToken_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testDepositPreauth()
|
||||
testDepositPreauth(FeatureBitset features)
|
||||
{
|
||||
testcase("DepositPreauth");
|
||||
|
||||
using namespace test::jtx;
|
||||
Account const alice("alice"); // issuer
|
||||
Account const bob("bob"); // holder
|
||||
@@ -1352,8 +1799,11 @@ class MPToken_test : public beast::unit_test::suite
|
||||
|
||||
char const credType[] = "abcde";
|
||||
|
||||
if (features[featureCredentials])
|
||||
{
|
||||
Env env(*this);
|
||||
testcase("DepositPreauth");
|
||||
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(50000), diana, dpIssuer);
|
||||
env.close();
|
||||
@@ -2297,6 +2747,8 @@ public:
|
||||
|
||||
// MPTokenIssuanceCreate
|
||||
testCreateValidation(all - featureSingleAssetVault);
|
||||
testCreateValidation(
|
||||
(all | featureSingleAssetVault) - featurePermissionedDomains);
|
||||
testCreateValidation(all | featureSingleAssetVault);
|
||||
testCreateEnabled(all - featureSingleAssetVault);
|
||||
testCreateEnabled(all | featureSingleAssetVault);
|
||||
@@ -2314,7 +2766,11 @@ public:
|
||||
testAuthorizeEnabled(all | featureSingleAssetVault);
|
||||
|
||||
// MPTokenIssuanceSet
|
||||
testSetValidation(all);
|
||||
testSetValidation(all - featureSingleAssetVault);
|
||||
testSetValidation(
|
||||
(all | featureSingleAssetVault) - featurePermissionedDomains);
|
||||
testSetValidation(all | featureSingleAssetVault);
|
||||
|
||||
testSetEnabled(all - featureSingleAssetVault);
|
||||
testSetEnabled(all | featureSingleAssetVault);
|
||||
|
||||
@@ -2323,8 +2779,9 @@ public:
|
||||
testClawback(all);
|
||||
|
||||
// Test Direct Payment
|
||||
testPayment(all);
|
||||
testDepositPreauth();
|
||||
testPayment(all | featureSingleAssetVault);
|
||||
testDepositPreauth(all);
|
||||
testDepositPreauth(all - featureCredentials);
|
||||
|
||||
// Test MPT Amount is invalid in Tx, which don't support MPT
|
||||
testMPTInvalidInTx(all);
|
||||
|
||||
Reference in New Issue
Block a user