mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-22 14:50:54 +00:00
Remove temSTRING_TOO_LARGE, fix authorizedDomain, refactor Vault_test
This commit is contained in:
@@ -141,7 +141,6 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
|
||||
temBAD_TRANSFER_FEE,
|
||||
temSTRING_TOO_LARGE,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/fee.h>
|
||||
#include <test/jtx/mpt.h>
|
||||
#include <test/jtx/subcases.h>
|
||||
#include <test/jtx/utility.h>
|
||||
#include <test/jtx/vault.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -34,246 +33,348 @@ using namespace test::jtx;
|
||||
class Vault_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
testSequence(
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Vault& vault,
|
||||
PrettyAsset const& asset)
|
||||
{
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
BEAST_EXPECT(env.le(keylet));
|
||||
|
||||
{
|
||||
testcase("fail to deposit more than assets held");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(10000)});
|
||||
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("deposit non-zero amount");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(50)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("deposit non-zero amount again");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(50)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to delete non-empty vault");
|
||||
auto tx = vault.del({.owner = owner, .id = keylet.key});
|
||||
env(tx, ter(tecHAS_OBLIGATIONS));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to update because wrong owner");
|
||||
auto tx = vault.set({.owner = issuer, .id = keylet.key});
|
||||
env(tx, ter(tecNO_PERMISSION));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to update immutable flags");
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfFlags] = tfVaultPrivate;
|
||||
env(tx, ter(temINVALID_FLAG));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to set maximum lower than current amount");
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfAssetMaximum] = asset(50).number();
|
||||
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("set maximum higher than current amount");
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfAssetMaximum] = asset(200).number();
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to deposit more than maximum");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(200)});
|
||||
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to withdraw more than assets held");
|
||||
auto tx = vault.withdraw(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(1000)});
|
||||
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("deposit up to maximum");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
if (!asset.raw().native())
|
||||
{
|
||||
testcase("fail to clawback because wrong issuer");
|
||||
auto tx = vault.clawback(
|
||||
{.issuer = owner,
|
||||
.id = keylet.key,
|
||||
.holder = depositor,
|
||||
.amount = asset(50)});
|
||||
env(tx, ter(tecNO_PERMISSION));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("clawback");
|
||||
auto code =
|
||||
asset.raw().native() ? ter(tecNO_PERMISSION) : ter(tesSUCCESS);
|
||||
auto tx = vault.clawback(
|
||||
{.issuer = issuer,
|
||||
.id = keylet.key,
|
||||
.holder = depositor,
|
||||
.amount = asset(50)});
|
||||
env(tx, code);
|
||||
}
|
||||
|
||||
// TODO: redeem.
|
||||
|
||||
{
|
||||
testcase("withdraw non-zero assets");
|
||||
auto number = asset.raw().native() ? 200 : 150;
|
||||
auto tx = vault.withdraw(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(number)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to delete because wrong owner");
|
||||
auto tx = vault.del({.owner = issuer, .id = keylet.key});
|
||||
env(tx, ter(tecNO_PERMISSION));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("delete empty vault");
|
||||
auto tx = vault.del({.owner = owner, .id = keylet.key});
|
||||
env(tx);
|
||||
BEAST_EXPECT(!env.le(keylet));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(Sequences)
|
||||
testSequences()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
Account issuer{"issuer"};
|
||||
Account owner{"owner"};
|
||||
Account depositor{"depositor"};
|
||||
auto vault = env.vault();
|
||||
|
||||
env.fund(XRP(1000), issuer, owner, depositor);
|
||||
env.close();
|
||||
|
||||
SUBCASE("XRP")
|
||||
{
|
||||
PrettyAsset asset{xrpIssue(), 1'000'000};
|
||||
testSequence(env, issuer, owner, depositor, vault, asset);
|
||||
}
|
||||
|
||||
SUBCASE("IOU")
|
||||
{
|
||||
PrettyAsset asset = issuer["IOU"];
|
||||
env.trust(asset(1000), depositor);
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
auto const testSequence = [this](
|
||||
std::string const& prefix,
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Vault& vault,
|
||||
PrettyAsset const& asset) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
testSequence(env, issuer, owner, depositor, vault, asset);
|
||||
}
|
||||
BEAST_EXPECT(env.le(keylet));
|
||||
|
||||
SUBCASE("MPT")
|
||||
{
|
||||
MPTTester mptt{env, issuer, {.fund = false}};
|
||||
mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
|
||||
PrettyAsset asset = mptt.issuanceID();
|
||||
mptt.authorize({.account = depositor});
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
env.close();
|
||||
testSequence(env, issuer, owner, depositor, vault, asset);
|
||||
}
|
||||
{
|
||||
testcase(prefix + " fail to deposit more than assets held");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(10000)});
|
||||
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " deposit non-zero amount");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(50)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " deposit non-zero amount again");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(50)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " fail to delete non-empty vault");
|
||||
auto tx = vault.del({.owner = owner, .id = keylet.key});
|
||||
env(tx, ter(tecHAS_OBLIGATIONS));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " fail to update because wrong owner");
|
||||
auto tx = vault.set({.owner = issuer, .id = keylet.key});
|
||||
env(tx, ter(tecNO_PERMISSION));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " fail to update immutable flags");
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfFlags] = tfVaultPrivate;
|
||||
env(tx, ter(temINVALID_FLAG));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(
|
||||
prefix + " fail to set maximum lower than current amount");
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfAssetMaximum] = asset(50).number();
|
||||
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " set maximum higher than current amount");
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfAssetMaximum] = asset(200).number();
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " fail to deposit more than maximum");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(200)});
|
||||
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " fail to withdraw more than assets held");
|
||||
auto tx = vault.withdraw(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(1000)});
|
||||
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " deposit up to maximum");
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
if (!asset.raw().native())
|
||||
{
|
||||
testcase(prefix + " fail to clawback because wrong issuer");
|
||||
auto tx = vault.clawback(
|
||||
{.issuer = owner,
|
||||
.id = keylet.key,
|
||||
.holder = depositor,
|
||||
.amount = asset(50)});
|
||||
env(tx, ter(tecNO_PERMISSION));
|
||||
}
|
||||
|
||||
{
|
||||
testcase(prefix + " clawback");
|
||||
auto code = asset.raw().native() ? ter(tecNO_PERMISSION)
|
||||
: ter(tesSUCCESS);
|
||||
auto tx = vault.clawback(
|
||||
{.issuer = issuer,
|
||||
.id = keylet.key,
|
||||
.holder = depositor,
|
||||
.amount = asset(50)});
|
||||
env(tx, code);
|
||||
}
|
||||
|
||||
// TODO: redeem.
|
||||
|
||||
{
|
||||
testcase("withdraw non-zero assets");
|
||||
auto number = asset.raw().native() ? 200 : 150;
|
||||
auto tx = vault.withdraw(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(number)});
|
||||
env(tx);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("fail to delete because wrong owner");
|
||||
auto tx = vault.del({.owner = issuer, .id = keylet.key});
|
||||
env(tx, ter(tecNO_PERMISSION));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("delete empty vault");
|
||||
auto tx = vault.del({.owner = owner, .id = keylet.key});
|
||||
env(tx);
|
||||
BEAST_EXPECT(!env.le(keylet));
|
||||
}
|
||||
};
|
||||
|
||||
auto testCases =
|
||||
[this, &testSequence](
|
||||
std::string prefix,
|
||||
std::function<PrettyAsset(
|
||||
Env & env, Account const& issuer, Account const& depositor)>
|
||||
setup) {
|
||||
Env env{*this};
|
||||
Account issuer{"issuer"};
|
||||
Account owner{"owner"};
|
||||
Account depositor{"depositor"};
|
||||
auto vault = env.vault();
|
||||
env.fund(XRP(1000), issuer, owner, depositor);
|
||||
env.close();
|
||||
|
||||
PrettyAsset asset = setup(env, issuer, depositor);
|
||||
testSequence(
|
||||
prefix, env, issuer, owner, depositor, vault, asset);
|
||||
};
|
||||
|
||||
testCases(
|
||||
"XRP",
|
||||
[](Env& env, Account const& issuer, Account const& depositor)
|
||||
-> PrettyAsset { return {xrpIssue(), 1'000'000};
|
||||
});
|
||||
|
||||
testCases(
|
||||
"IOU",
|
||||
[](Env& env,
|
||||
Account const& issuer,
|
||||
Account const& depositor) -> Asset {
|
||||
PrettyAsset asset = issuer["IOU"];
|
||||
env.trust(asset(1000), depositor);
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
env.close();
|
||||
return asset;
|
||||
});
|
||||
|
||||
testCases(
|
||||
"MPT",
|
||||
[](Env& env,
|
||||
Account const& issuer,
|
||||
Account const& depositor) -> Asset {
|
||||
MPTTester mptt{env, issuer, {.fund = false}};
|
||||
mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
|
||||
PrettyAsset asset = mptt.issuanceID();
|
||||
mptt.authorize({.account = depositor});
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
env.close();
|
||||
return asset;
|
||||
});
|
||||
}
|
||||
|
||||
// Test for non-asset specific behaviors.
|
||||
TEST_CASE(CreateFailXRP)
|
||||
void
|
||||
testCreateFailXRP()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
Account issuer{"issuer"};
|
||||
Account owner{"owner"};
|
||||
Account depositor{"depositor"};
|
||||
env.fund(XRP(1000), issuer, owner, depositor);
|
||||
env.close();
|
||||
auto vault = env.vault();
|
||||
Asset asset = xrpIssue();
|
||||
|
||||
SUBCASE("nothing to delete")
|
||||
{
|
||||
auto testCase = [this](std::function<void(
|
||||
Env & env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault)> test) {
|
||||
Env env{*this};
|
||||
Account issuer{"issuer"};
|
||||
Account owner{"owner"};
|
||||
Account depositor{"depositor"};
|
||||
env.fund(XRP(1000), issuer, owner, depositor);
|
||||
env.close();
|
||||
auto vault = env.vault();
|
||||
Asset asset = xrpIssue();
|
||||
|
||||
test(env, issuer, owner, depositor, asset, vault);
|
||||
};
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
testcase("nothing to delete");
|
||||
auto tx = vault.del({.owner = issuer, .id = keylet::skip().key});
|
||||
env(tx, ter(tecOBJECT_NOT_FOUND));
|
||||
}
|
||||
});
|
||||
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
testcase("transaction is good");
|
||||
env(tx);
|
||||
});
|
||||
|
||||
SUBCASE("insufficient fee")
|
||||
{
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
testcase("insufficient fee");
|
||||
env(tx, fee(env.current()->fees().base), ter(telINSUF_FEE_P));
|
||||
}
|
||||
});
|
||||
|
||||
SUBCASE("insufficient reserve")
|
||||
{
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
testcase("insufficient reserve");
|
||||
// It is possible to construct a complicated mathematical
|
||||
// expression for this amount, but it is sadly not easy.
|
||||
env(pay(owner, issuer, XRP(775)));
|
||||
env.close();
|
||||
env(tx, ter(tecINSUFFICIENT_RESERVE));
|
||||
}
|
||||
});
|
||||
|
||||
SUBCASE("data too large")
|
||||
{
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
testcase("empty data");
|
||||
tx[sfData] = "";
|
||||
env(tx, ter(temMALFORMED));
|
||||
});
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
testcase("data too large");
|
||||
// A hexadecimal string of 257 bytes.
|
||||
tx[sfData] = std::string(514, 'A');
|
||||
env(tx, ter(temSTRING_TOO_LARGE));
|
||||
}
|
||||
env(tx, ter(temMALFORMED));
|
||||
});
|
||||
|
||||
SUBCASE("metadata too large")
|
||||
{
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
testcase("empty metadata");
|
||||
tx[sfMPTokenMetadata] = "";
|
||||
env(tx, ter(temMALFORMED));
|
||||
});
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
|
||||
testcase("metadata too large");
|
||||
// This metadata is for the share token.
|
||||
// A hexadecimal string of 1025 bytes.
|
||||
tx[sfMPTokenMetadata] = std::string(2050, 'B');
|
||||
env(tx, ter(temSTRING_TOO_LARGE));
|
||||
}
|
||||
env(tx, ter(temMALFORMED));
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE(CreateFailIOU)
|
||||
void
|
||||
testCreateFailIOU()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
@@ -287,16 +388,14 @@ class Vault_test : public beast::unit_test::suite
|
||||
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
|
||||
SUBCASE("global freeze")
|
||||
{
|
||||
env(fset(issuer, asfGlobalFreeze));
|
||||
env.close();
|
||||
env(tx, ter(tecFROZEN));
|
||||
env.close();
|
||||
}
|
||||
env(fset(issuer, asfGlobalFreeze));
|
||||
env.close();
|
||||
env(tx, ter(tecFROZEN));
|
||||
env.close();
|
||||
}
|
||||
|
||||
TEST_CASE(CreateFailMPT)
|
||||
void
|
||||
testCreateFailMPT()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
@@ -309,43 +408,67 @@ class Vault_test : public beast::unit_test::suite
|
||||
|
||||
MPTTester mptt{env, issuer, {.fund = false}};
|
||||
|
||||
SUBCASE("cannot transfer")
|
||||
{
|
||||
// Locked because that is the default flag.
|
||||
mptt.create();
|
||||
Asset asset = mptt.issuanceID();
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx, ter(tecLOCKED));
|
||||
}
|
||||
// Locked because that is the default flag.
|
||||
mptt.create();
|
||||
Asset asset = mptt.issuanceID();
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx, ter(tecLOCKED));
|
||||
}
|
||||
|
||||
TEST_CASE(WithMPT)
|
||||
void
|
||||
testWithMPT()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
Account issuer{"issuer"};
|
||||
Account owner{"owner"};
|
||||
Account depositor{"depositor"};
|
||||
env.fund(XRP(1000), issuer, owner, depositor);
|
||||
env.close();
|
||||
auto vault = env.vault();
|
||||
|
||||
MPTTester mptt{env, issuer, {.fund = false}};
|
||||
mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
|
||||
PrettyAsset asset = mptt.issuanceID();
|
||||
mptt.authorize({.account = depositor});
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
env.close();
|
||||
auto testCase = [this](std::function<void(
|
||||
Env & env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault,
|
||||
MPTTester& mptt)> test) {
|
||||
Env env{*this};
|
||||
Account issuer{"issuer"};
|
||||
Account owner{"owner"};
|
||||
Account depositor{"depositor"};
|
||||
env.fund(XRP(1000), issuer, owner, depositor);
|
||||
env.close();
|
||||
auto vault = env.vault();
|
||||
|
||||
SUBCASE("global lock")
|
||||
{
|
||||
MPTTester mptt{env, issuer, {.fund = false}};
|
||||
mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
|
||||
PrettyAsset asset = mptt.issuanceID();
|
||||
mptt.authorize({.account = depositor});
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
env.close();
|
||||
|
||||
test(env, issuer, owner, depositor, asset, vault, mptt);
|
||||
};
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault,
|
||||
MPTTester& mptt) {
|
||||
testcase("global lock");
|
||||
mptt.set({.account = issuer, .flags = tfMPTLock});
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx, ter(tecLOCKED));
|
||||
}
|
||||
});
|
||||
|
||||
SUBCASE("deposit non-zero amount")
|
||||
{
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault,
|
||||
MPTTester& mptt) {
|
||||
testcase("deposit non-zero amount");
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
@@ -365,18 +488,18 @@ class Vault_test : public beast::unit_test::suite
|
||||
Number outstandingShares = issuance->at(sfOutstandingAmount);
|
||||
BEAST_EXPECT(outstandingShares > 0);
|
||||
BEAST_EXPECT(outstandingShares == 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
EXECUTE(Sequences);
|
||||
EXECUTE(CreateFailXRP);
|
||||
EXECUTE(CreateFailIOU);
|
||||
EXECUTE(CreateFailMPT);
|
||||
EXECUTE(WithMPT);
|
||||
testSequences();
|
||||
testCreateFailXRP();
|
||||
testCreateFailIOU();
|
||||
testCreateFailMPT();
|
||||
testWithMPT();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
operator()(T v) const
|
||||
{
|
||||
STAmount amount{asset_, v * scale_};
|
||||
return {amount, "uhh"};
|
||||
return {amount, ""};
|
||||
}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 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 <test/jtx/subcases.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace subcases {
|
||||
|
||||
thread_local Subcase* Subcase::lastCreated;
|
||||
|
||||
Subcase::Subcase(Context& context, char const* name)
|
||||
: context_(context), name_(name)
|
||||
{
|
||||
lastCreated = this;
|
||||
}
|
||||
|
||||
Subcase::operator bool() const
|
||||
{
|
||||
auto& _ = context_;
|
||||
++_.level;
|
||||
if (_.level >= MAXIMUM_SUBCASE_DEPTH)
|
||||
throw std::logic_error("maximum subcase depth exceeded");
|
||||
if (_.entered < _.level && _.skip[_.level] == _.skipped)
|
||||
{
|
||||
_.entered = _.level;
|
||||
_.names[_.level] = name_;
|
||||
_.skipped = 0;
|
||||
return true;
|
||||
}
|
||||
++_.skipped;
|
||||
return false;
|
||||
}
|
||||
|
||||
Subcase::~Subcase()
|
||||
{
|
||||
auto& _ = context_;
|
||||
if (_.level == _.entered && _.skipped == 0)
|
||||
{
|
||||
// We are destroying the leaf subcase that executed on this pass.
|
||||
// Didn't have time to debug this. Cannot explain what is going wrong
|
||||
// with jtx. Just switch to a better test framework already.
|
||||
_.suite.pass();
|
||||
// We call `suite::testcase()` here, after the subcase is finished,
|
||||
// because only now do we know which subcase was the leaf,
|
||||
// and we only want to print one name line for each subcase.
|
||||
_.suite.testcase(_.name());
|
||||
// Let the runner know that a test executed,
|
||||
// even if `BEAST_EXPECT` was never called.
|
||||
_.suite.pass();
|
||||
}
|
||||
if (_.skipped == 0)
|
||||
{
|
||||
++_.skip[_.level];
|
||||
_.skip[_.level + 1] = 0;
|
||||
}
|
||||
--_.level;
|
||||
}
|
||||
|
||||
void
|
||||
execute(beast::unit_test::suite* suite, char const* name, Supercase supercase)
|
||||
{
|
||||
Context context{*suite};
|
||||
context.names[0] = name;
|
||||
do
|
||||
{
|
||||
context.lap();
|
||||
supercase(context);
|
||||
} while (context.skipped != 0);
|
||||
}
|
||||
|
||||
} // namespace subcases
|
||||
@@ -1,139 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TEST_JTX_SUBCASES_H_INCLUDED
|
||||
#define RIPPLE_TEST_JTX_SUBCASES_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
namespace subcases {
|
||||
|
||||
constexpr std::size_t MAXIMUM_SUBCASE_DEPTH = 10;
|
||||
|
||||
/**
|
||||
* This short library implements a pattern found in doctest and Catch:
|
||||
*
|
||||
* TEST_CASE(testName) {
|
||||
* // setup
|
||||
* SUBCASE("one") {
|
||||
* // actions and assertions
|
||||
* }
|
||||
* SUBCASE("two") {
|
||||
* // actions and assertions
|
||||
* }
|
||||
* SUBCASE("three") {
|
||||
* // actions and assertions
|
||||
* }
|
||||
* // assertions before teardown
|
||||
* }
|
||||
*
|
||||
* EXECUTE(testName);
|
||||
*
|
||||
* In short:
|
||||
*
|
||||
* - Top-level test cases are declared with `TEST_CASE(name)`.
|
||||
* The name must be a legal identifier.
|
||||
* It will become the name of a function.
|
||||
* - Subcases are declared with `SUBCASE("description")`.
|
||||
* Descriptions do not need to be unique.
|
||||
* - Test cases are executed with `EXECUTE(name)`,
|
||||
* where `name` is the one that was passed to `TEST_CASE`.
|
||||
* When executing a test case, it will loop,
|
||||
* executing exactly one leaf subcase in each pass,
|
||||
* until all subcases have executed.
|
||||
* The top-level test case is considered a subcase too.
|
||||
*
|
||||
* This lets test authors easily share common setup among multiple subcases.
|
||||
* Subcases can be nested up to `MAXIMUM_SUBCASE_DEPTH`.
|
||||
*/
|
||||
|
||||
struct Context
|
||||
{
|
||||
beast::unit_test::suite& suite;
|
||||
// The number of subcases to skip at each level to reach the next subcase.
|
||||
std::uint8_t skip[MAXIMUM_SUBCASE_DEPTH] = {0};
|
||||
// The subcase names at each level.
|
||||
char const* names[MAXIMUM_SUBCASE_DEPTH] = {""};
|
||||
// The current level.
|
||||
std::uint8_t level = 0;
|
||||
// The maximum depth at which we entered a subcase.
|
||||
std::uint8_t entered = 0;
|
||||
// The number of subcases we skipped on this or deeper levels
|
||||
// since entering a subcase.
|
||||
std::uint8_t skipped = 0;
|
||||
|
||||
std::string
|
||||
name() const
|
||||
{
|
||||
std::string n;
|
||||
for (auto i = 0; i <= level; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
n += " > ";
|
||||
}
|
||||
n += names[i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
lap()
|
||||
{
|
||||
level = 0;
|
||||
entered = 0;
|
||||
skipped = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Subcase
|
||||
{
|
||||
Context& context_;
|
||||
char const* name_;
|
||||
Subcase(Context& context, char const* name);
|
||||
~Subcase();
|
||||
/** Return true if we should enter this subcase. */
|
||||
operator bool() const;
|
||||
thread_local static Subcase* lastCreated;
|
||||
};
|
||||
|
||||
using Supercase = std::function<void(Context&)>;
|
||||
|
||||
void
|
||||
execute(beast::unit_test::suite* suite, char const* name, Supercase supercase);
|
||||
|
||||
} // namespace subcases
|
||||
|
||||
#define TEST_CASE(name) void name(subcases::Context& _09876)
|
||||
#define SUBCASE(name) if (subcases::Subcase sc##__COUNTER__{_09876, name})
|
||||
#define SKIP(name) if (false)
|
||||
#define EXECUTE(name) \
|
||||
subcases::execute(this, #name, [&](auto& ctx) { name(ctx); })
|
||||
// `AND_THEN` defines a subcase to contain all remaining subcases,
|
||||
// without having to indent them in a nested block.
|
||||
#define AND_THEN(name) \
|
||||
subcases::Subcase sc##__COUNTER__{_09876, name}; \
|
||||
if (!*subcases::Subcase::lastCreated) \
|
||||
return
|
||||
#define SECTION(name_) _09876.suite.testcase(_09876.name() + " > " + name_)
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <xrpld/app/misc/CredentialHelpers.h>
|
||||
#include <xrpld/ledger/View.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STVector256.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
@@ -193,18 +194,20 @@ authorizedDomain(
|
||||
uint256 domainID,
|
||||
AccountID const& subject)
|
||||
{
|
||||
auto const sle = view.read(keylet::permissionedDomain(domainID));
|
||||
if (!sle || !sle->isFieldPresent(sfAcceptedCredentials))
|
||||
auto const slePD = view.read(keylet::permissionedDomain(domainID));
|
||||
if (!slePD || !slePD->isFieldPresent(sfAcceptedCredentials))
|
||||
return tefINTERNAL;
|
||||
|
||||
for (auto const& h : sle->getFieldArray(sfAcceptedCredentials))
|
||||
for (auto const& h : slePD->getFieldArray(sfAcceptedCredentials))
|
||||
{
|
||||
if (!h.isFieldPresent(sfIssuer) || !h.isFieldPresent(sfCredentialType))
|
||||
return tefINTERNAL;
|
||||
|
||||
auto const issuer = h.getAccountID(sfIssuer);
|
||||
auto const type = makeSlice(h.getFieldVL(sfCredentialType));
|
||||
if (view.exists(keylet::credential(subject, issuer, type)))
|
||||
auto const sleCredential =
|
||||
view.read(keylet::credential(subject, issuer, type));
|
||||
if (sleCredential && sleCredential->getFlags() & lsfAccepted)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ VaultCreate::preflight(PreflightContext const& ctx)
|
||||
|
||||
if (auto const data = ctx.tx[~sfData])
|
||||
{
|
||||
if (data->length() > maxVaultDataLength)
|
||||
return temSTRING_TOO_LARGE;
|
||||
if (data->empty() || data->length() > maxVaultDataLength)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (auto const domain = ctx.tx[~sfDomainID])
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -57,8 +58,8 @@ VaultDeposit::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
if (auto const domain = vault->at(~sfVaultID))
|
||||
{
|
||||
if (!credentials::authorizedDomain(
|
||||
ctx.view, *domain, ctx.tx[sfAccount]))
|
||||
if (credentials::authorizedDomain(
|
||||
ctx.view, *domain, ctx.tx[sfAccount]) != tesSUCCESS)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ VaultSet::preflight(PreflightContext const& ctx)
|
||||
return temINVALID_FLAG;
|
||||
if (auto const data = ctx.tx[~sfData])
|
||||
{
|
||||
if (data->length() > maxVaultDataLength)
|
||||
return temSTRING_TOO_LARGE;
|
||||
if (data->empty() || data->length() > maxVaultDataLength)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const domain = ctx.tx[~sfDomainID];
|
||||
|
||||
Reference in New Issue
Block a user