mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-11 17:32:34 +00:00
Compare commits
3 Commits
ripple/was
...
legleux/ar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b81a9ae88 | ||
|
|
24ef026fda | ||
|
|
7a96d1bd3c |
124
.github/scripts/strategy-matrix/generate.py
vendored
124
.github/scripts/strategy-matrix/generate.py
vendored
@@ -50,123 +50,13 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
|
||||
# Only generate a subset of configurations in PRs.
|
||||
if not all:
|
||||
# Debian:
|
||||
# - Bookworm using GCC 13: Release on linux/amd64, set the reference
|
||||
# fee to 500.
|
||||
# - Bookworm using GCC 15: Debug on linux/amd64, enable code
|
||||
# coverage (which will be done below).
|
||||
# - Bookworm using Clang 16: Debug on linux/arm64, enable voidstar.
|
||||
# - Bookworm using Clang 17: Release on linux/amd64, set the
|
||||
# reference fee to 1000.
|
||||
# - Bookworm using Clang 20: Debug on linux/amd64.
|
||||
if os["distro_name"] == "debian":
|
||||
skip = True
|
||||
if os["distro_version"] == "bookworm":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13"
|
||||
and build_type == "Release"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}"
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/arm64"
|
||||
):
|
||||
cmake_args = f"-Dvoidstar=ON {cmake_args}"
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-17"
|
||||
and build_type == "Release"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}"
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if skip:
|
||||
continue
|
||||
|
||||
# RHEL:
|
||||
# - 9 using GCC 12: Debug on linux/amd64.
|
||||
# - 10 using Clang: Release on linux/amd64.
|
||||
if os["distro_name"] == "rhel":
|
||||
skip = True
|
||||
if os["distro_version"] == "9":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
elif os["distro_version"] == "10":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-any"
|
||||
and build_type == "Release"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if skip:
|
||||
continue
|
||||
|
||||
# Ubuntu:
|
||||
# - Jammy using GCC 12: Debug on linux/arm64.
|
||||
# - Noble using GCC 14: Release on linux/amd64.
|
||||
# - Noble using Clang 18: Debug on linux/amd64.
|
||||
# - Noble using Clang 19: Release on linux/arm64.
|
||||
if os["distro_name"] == "ubuntu":
|
||||
skip = True
|
||||
if os["distro_version"] == "jammy":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/arm64"
|
||||
):
|
||||
skip = False
|
||||
elif os["distro_version"] == "noble":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14"
|
||||
and build_type == "Release"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-18"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-19"
|
||||
and build_type == "Release"
|
||||
and architecture["platform"] == "linux/arm64"
|
||||
):
|
||||
skip = False
|
||||
if skip:
|
||||
continue
|
||||
|
||||
# MacOS:
|
||||
# - Debug on macos/arm64.
|
||||
if os["distro_name"] == "macos" and not (
|
||||
build_type == "Debug" and architecture["platform"] == "macos/arm64"
|
||||
):
|
||||
continue
|
||||
|
||||
# Windows:
|
||||
# - Release on windows/amd64.
|
||||
if os["distro_name"] == "windows" and not (
|
||||
build_type == "Release" and architecture["platform"] == "windows/amd64"
|
||||
# TEMPORARY: Only build ubuntu-jammy-gcc-12-debug on amd64
|
||||
if not (
|
||||
os["distro_name"] == "ubuntu"
|
||||
and os["distro_version"] == "jammy"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
continue
|
||||
|
||||
|
||||
27
.github/workflows/on-pr.yml
vendored
27
.github/workflows/on-pr.yml
vendored
@@ -6,6 +6,9 @@
|
||||
name: PR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- legleux/art_upload
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
@@ -91,20 +94,21 @@ jobs:
|
||||
DRAFT: ${{ github.event.pull_request.draft }}
|
||||
READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }}
|
||||
MERGE: ${{ github.event_name == 'merge_group' }}
|
||||
PUSH: ${{ github.event_name == 'push' }}
|
||||
run: |
|
||||
echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >> "${GITHUB_OUTPUT}"
|
||||
echo "go=${{ env.PUSH == 'true' || (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >> "${GITHUB_OUTPUT}"
|
||||
cat "${GITHUB_OUTPUT}"
|
||||
outputs:
|
||||
go: ${{ steps.go.outputs.go == 'true' }}
|
||||
|
||||
check-levelization:
|
||||
needs: should-run
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
if: false
|
||||
uses: ./.github/workflows/reusable-check-levelization.yml
|
||||
|
||||
check-rename:
|
||||
needs: should-run
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
if: false
|
||||
uses: ./.github/workflows/reusable-check-rename.yml
|
||||
|
||||
build-test:
|
||||
@@ -114,7 +118,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [linux, macos, windows]
|
||||
os: [linux]
|
||||
with:
|
||||
# Enable ccache only for events targeting the XRPLF repository, since
|
||||
# other accounts will not have access to our remote cache storage.
|
||||
@@ -123,6 +127,18 @@ jobs:
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
upload-artifactory:
|
||||
needs:
|
||||
- should-run
|
||||
- build-test
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
uses: ./.github/workflows/reusable-upload-artifactory.yml
|
||||
with:
|
||||
artifactory_repo: generic-local
|
||||
secrets:
|
||||
ARTIFACTORY_HOST: ${{ secrets.ARTIFACTORY_HOST }}
|
||||
ARTIFACTORY_TOKEN: ${{ secrets.ARTIFACTORY_TOKEN }}
|
||||
|
||||
upload-recipe:
|
||||
needs:
|
||||
- should-run
|
||||
@@ -154,9 +170,8 @@ jobs:
|
||||
passed:
|
||||
if: failure() || cancelled()
|
||||
needs:
|
||||
- check-levelization
|
||||
- check-rename
|
||||
- build-test
|
||||
- upload-artifactory
|
||||
- upload-recipe
|
||||
- notify-clio
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
1
.github/workflows/pre-commit.yml
vendored
1
.github/workflows/pre-commit.yml
vendored
@@ -11,6 +11,7 @@ on:
|
||||
jobs:
|
||||
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
|
||||
run-hooks:
|
||||
if: false
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@320be44621ca2a080f05aeb15817c44b84518108
|
||||
with:
|
||||
runs_on: ubuntu-latest
|
||||
|
||||
81
.github/workflows/reusable-upload-artifactory.yml
vendored
Normal file
81
.github/workflows/reusable-upload-artifactory.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Upload artifacts to Artifactory
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifact_pattern:
|
||||
description: "Glob pattern to match GitHub artifacts to download."
|
||||
required: false
|
||||
type: string
|
||||
default: "xrpld-*"
|
||||
|
||||
artifactory_repo:
|
||||
description: "Artifactory repository name."
|
||||
required: false
|
||||
type: string
|
||||
default: "xrpl-dev-local"
|
||||
|
||||
artifact_prefix:
|
||||
description: "Top-level directory in Artifactory under which artifacts are stored."
|
||||
required: false
|
||||
type: string
|
||||
default: "xrpld"
|
||||
|
||||
secrets:
|
||||
ARTIFACTORY_HOST:
|
||||
description: "Artifactory base URL (e.g. https://xrpl.jfrog.io/artifactory)."
|
||||
required: true
|
||||
ARTIFACTORY_TOKEN:
|
||||
description: "Artifactory API token."
|
||||
required: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Generate version
|
||||
id: version
|
||||
uses: ./.github/actions/generate-version
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
pattern: ${{ inputs.artifact_pattern }}
|
||||
path: artifacts
|
||||
|
||||
- name: List downloaded artifacts
|
||||
run: ls -lR artifacts
|
||||
|
||||
- name: Upload to Artifactory
|
||||
env:
|
||||
ARTIFACTORY_HOST: ${{ secrets.ARTIFACTORY_HOST }}
|
||||
ARTIFACTORY_TOKEN: ${{ secrets.ARTIFACTORY_TOKEN }}
|
||||
REPO: ${{ inputs.artifactory_repo }}
|
||||
PREFIX: ${{ inputs.artifact_prefix }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
for config_dir in artifacts/*/; do
|
||||
config_name="$(basename "${config_dir}")"
|
||||
for file in "${config_dir}"*; do
|
||||
[ -f "${file}" ] || continue
|
||||
filename="$(basename "${file}")"
|
||||
url="${ARTIFACTORY_HOST}/${REPO}/${PREFIX}/${VERSION}/${config_name}/${filename}"
|
||||
echo "Uploading ${file} -> ${url}"
|
||||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-T "${file}" \
|
||||
-H "Authorization: Bearer ${ARTIFACTORY_TOKEN}" \
|
||||
"${url}")
|
||||
if [[ "${status}" != 2* ]]; then
|
||||
echo "::error::Upload failed for ${file} with HTTP status ${status}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Upload succeeded (HTTP ${status})"
|
||||
done
|
||||
done
|
||||
@@ -103,7 +103,6 @@ find_package(OpenSSL REQUIRED)
|
||||
find_package(secp256k1 REQUIRED)
|
||||
find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(wasmi REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -45,7 +45,6 @@ target_link_libraries(
|
||||
Xrpl::opts
|
||||
Xrpl::syslibs
|
||||
secp256k1::secp256k1
|
||||
wasmi::wasmi
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"requires": [
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
|
||||
"wasmi/1.0.6#407c9db14601a8af1c7dd3b388f3e4cd%1768164779.349",
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
|
||||
@@ -35,7 +35,6 @@ class Xrpl(ConanFile):
|
||||
"openssl/3.5.5",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"wasmi/1.0.6",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
|
||||
@@ -216,7 +215,6 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
|
||||
@@ -287,7 +287,6 @@ words:
|
||||
- venv
|
||||
- vfalco
|
||||
- vinnie
|
||||
- wasmi
|
||||
- wextra
|
||||
- wptr
|
||||
- writeme
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
|
||||
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -36,12 +36,6 @@ class Invariants_test : public beast::unit_test::suite
|
||||
// changes that will cause the check to fail.
|
||||
using Precheck = std::function<bool(test::jtx::Account const& a, test::jtx::Account const& b, ApplyContext& ac)>;
|
||||
|
||||
static FeatureBitset
|
||||
defaultAmendments()
|
||||
{
|
||||
return xrpl::test::jtx::testable_amendments() | featureInvariantsV1_1 | featureSingleAssetVault;
|
||||
}
|
||||
|
||||
/** Run a specific test case to put the ledger into a state that will be
|
||||
* detected by an invariant. Simulates the actions of a transaction that
|
||||
* would violate an invariant.
|
||||
@@ -68,23 +62,10 @@ class Invariants_test : public beast::unit_test::suite
|
||||
std::initializer_list<TER> ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED},
|
||||
Preclose const& preclose = {},
|
||||
TxAccount setTxAccount = TxAccount::None)
|
||||
{
|
||||
return doInvariantCheck(
|
||||
test::jtx::Env(*this, defaultAmendments()), expect_logs, precheck, fee, tx, ters, preclose, setTxAccount);
|
||||
}
|
||||
|
||||
void
|
||||
doInvariantCheck(
|
||||
test::jtx::Env&& env,
|
||||
std::vector<std::string> const& expect_logs,
|
||||
Precheck const& precheck,
|
||||
XRPAmount fee = XRPAmount{},
|
||||
STTx tx = STTx{ttACCOUNT_SET, [](STObject&) {}},
|
||||
std::initializer_list<TER> ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED},
|
||||
Preclose const& preclose = {},
|
||||
TxAccount setTxAccount = TxAccount::None)
|
||||
{
|
||||
using namespace test::jtx;
|
||||
FeatureBitset amendments = testable_amendments() | featureInvariantsV1_1 | featureSingleAssetVault;
|
||||
Env env{*this, amendments};
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
@@ -93,28 +74,11 @@ class Invariants_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(preclose(A1, A2, env));
|
||||
env.close();
|
||||
|
||||
if (setTxAccount != TxAccount::None)
|
||||
tx.setAccountID(sfAccount, setTxAccount == TxAccount::A1 ? A1.id() : A2.id());
|
||||
|
||||
return doInvariantCheck(std::move(env), A1, A2, expect_logs, precheck, fee, tx, ters);
|
||||
}
|
||||
|
||||
void
|
||||
doInvariantCheck(
|
||||
test::jtx::Env&& env,
|
||||
test::jtx::Account const& A1,
|
||||
test::jtx::Account const& A2,
|
||||
std::vector<std::string> const& expect_logs,
|
||||
Precheck const& precheck,
|
||||
XRPAmount fee = XRPAmount{},
|
||||
STTx tx = STTx{ttACCOUNT_SET, [](STObject&) {}},
|
||||
std::initializer_list<TER> ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED})
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
OpenView ov{*env.current()};
|
||||
test::StreamSink sink{beast::severities::kWarning};
|
||||
beast::Journal jlog{sink};
|
||||
if (setTxAccount != TxAccount::None)
|
||||
tx.setAccountID(sfAccount, setTxAccount == TxAccount::A1 ? A1.id() : A2.id());
|
||||
ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog};
|
||||
|
||||
BEAST_EXPECT(precheck(A1, A2, ac));
|
||||
@@ -127,21 +91,19 @@ class Invariants_test : public beast::unit_test::suite
|
||||
for (TER const& terExpect : ters)
|
||||
{
|
||||
terActual = ac.checkInvariants(terActual, fee);
|
||||
BEAST_EXPECTS(terExpect == terActual, std::to_string(TERtoInt(terActual)));
|
||||
BEAST_EXPECT(terExpect == terActual);
|
||||
auto const messages = sink.messages().str();
|
||||
|
||||
if (terActual != tesSUCCESS)
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
messages.starts_with("Invariant failed:") ||
|
||||
messages.starts_with("Transaction caused an exception"),
|
||||
messages);
|
||||
}
|
||||
|
||||
BEAST_EXPECT(
|
||||
messages.starts_with("Invariant failed:") || messages.starts_with("Transaction caused an exception"));
|
||||
// std::cerr << messages << '\n';
|
||||
for (auto const& m : expect_logs)
|
||||
{
|
||||
BEAST_EXPECTS(messages.find(m) != std::string::npos, m);
|
||||
if (messages.find(m) == std::string::npos)
|
||||
{
|
||||
// uncomment if you want to log the invariant failure
|
||||
// std::cerr << " --> " << m << std::endl;
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1157,80 +1119,86 @@ class Invariants_test : public beast::unit_test::suite
|
||||
});
|
||||
}
|
||||
|
||||
static std::shared_ptr<SLE>
|
||||
void
|
||||
createPermissionedDomain(
|
||||
ApplyContext& ac,
|
||||
std::shared_ptr<SLE>& sle,
|
||||
test::jtx::Account const& A1,
|
||||
test::jtx::Account const& A2,
|
||||
std::uint32_t numCreds = 2,
|
||||
std::uint32_t seq = 10)
|
||||
test::jtx::Account const& A2)
|
||||
{
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), seq);
|
||||
auto sle = std::make_shared<SLE>(pdKeylet);
|
||||
|
||||
sle->setAccountID(sfOwner, A1);
|
||||
sle->setFieldU32(sfSequence, seq);
|
||||
sle->setFieldU32(sfSequence, 10);
|
||||
|
||||
if (numCreds)
|
||||
STArray credentials(sfAcceptedCredentials, 2);
|
||||
for (std::size_t n = 0; n < 2; ++n)
|
||||
{
|
||||
// This array is sorted naturally, but if you willing to change this
|
||||
// behavior don't forget to use credentials::makeSorted
|
||||
STArray credentials(sfAcceptedCredentials, numCreds);
|
||||
for (std::size_t n = 0; n < numCreds; ++n)
|
||||
{
|
||||
auto cred = STObject::makeInnerObject(sfCredential);
|
||||
cred.setAccountID(sfIssuer, A2);
|
||||
auto credType = "cred_type" + std::to_string(n);
|
||||
cred.setFieldVL(sfCredentialType, Slice(credType.c_str(), credType.size()));
|
||||
credentials.push_back(std::move(cred));
|
||||
}
|
||||
sle->setFieldArray(sfAcceptedCredentials, credentials);
|
||||
auto cred = STObject::makeInnerObject(sfCredential);
|
||||
cred.setAccountID(sfIssuer, A2);
|
||||
auto credType = "cred_type" + std::to_string(n);
|
||||
cred.setFieldVL(sfCredentialType, Slice(credType.c_str(), credType.size()));
|
||||
credentials.push_back(std::move(cred));
|
||||
}
|
||||
|
||||
sle->setFieldArray(sfAcceptedCredentials, credentials);
|
||||
ac.view().insert(sle);
|
||||
return sle;
|
||||
};
|
||||
|
||||
void
|
||||
testPermissionedDomainInvariants(FeatureBitset features)
|
||||
testPermissionedDomainInvariants()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
bool const fixPDEnabled = features[fixPermissionedDomainInvariant];
|
||||
std::initializer_list<TER> badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED};
|
||||
std::initializer_list<TER> failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED};
|
||||
|
||||
testcase << "PermissionedDomain" + std::string(fixPDEnabled ? " fix" : "");
|
||||
|
||||
testcase << "PermissionedDomain";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain with no rules."}},
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
return createPermissionedDomain(ac, A1, A2, 0).get();
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
slePd->setAccountID(sfOwner, A1);
|
||||
slePd->setFieldU32(sfSequence, 10);
|
||||
|
||||
ac.view().insert(slePd);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
testcase << "PermissionedDomain 2";
|
||||
|
||||
auto constexpr tooBig = maxPermissionedDomainCredentialsArraySize + 1;
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain bad credentials size " + std::to_string(tooBig)}},
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
return !!createPermissionedDomain(ac, A1, A2, tooBig);
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
slePd->setAccountID(sfOwner, A1);
|
||||
slePd->setFieldU32(sfSequence, 10);
|
||||
|
||||
STArray credentials(sfAcceptedCredentials, tooBig);
|
||||
for (std::size_t n = 0; n < tooBig; ++n)
|
||||
{
|
||||
auto cred = STObject::makeInnerObject(sfCredential);
|
||||
cred.setAccountID(sfIssuer, A2);
|
||||
auto credType = std::string("cred_type") + std::to_string(n);
|
||||
cred.setFieldVL(sfCredentialType, Slice(credType.c_str(), credType.size()));
|
||||
credentials.push_back(std::move(cred));
|
||||
}
|
||||
slePd->setFieldArray(sfAcceptedCredentials, credentials);
|
||||
ac.view().insert(slePd);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
testcase << "PermissionedDomain 3";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain credentials aren't sorted"}},
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
auto slePd = createPermissionedDomain(ac, A1, A2, 0);
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
slePd->setAccountID(sfOwner, A1);
|
||||
slePd->setFieldU32(sfSequence, 10);
|
||||
|
||||
STArray credentials(sfAcceptedCredentials, 2);
|
||||
for (std::size_t n = 0; n < 2; ++n)
|
||||
@@ -1242,19 +1210,21 @@ class Invariants_test : public beast::unit_test::suite
|
||||
credentials.push_back(std::move(cred));
|
||||
}
|
||||
slePd->setFieldArray(sfAcceptedCredentials, credentials);
|
||||
ac.view().update(slePd);
|
||||
ac.view().insert(slePd);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
testcase << "PermissionedDomain 4";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain credentials aren't unique"}},
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
auto slePd = createPermissionedDomain(ac, A1, A2, 0);
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
slePd->setAccountID(sfOwner, A1);
|
||||
slePd->setFieldU32(sfSequence, 10);
|
||||
|
||||
STArray credentials(sfAcceptedCredentials, 2);
|
||||
for (std::size_t n = 0; n < 2; ++n)
|
||||
@@ -1265,20 +1235,22 @@ class Invariants_test : public beast::unit_test::suite
|
||||
credentials.push_back(std::move(cred));
|
||||
}
|
||||
slePd->setFieldArray(sfAcceptedCredentials, credentials);
|
||||
ac.view().update(slePd);
|
||||
ac.view().insert(slePd);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
testcase << "PermissionedDomain Set 1";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain with no rules."}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
|
||||
// create PD
|
||||
auto slePd = createPermissionedDomain(ac, A1, A2);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
// update PD with empty rules
|
||||
{
|
||||
@@ -1290,16 +1262,18 @@ class Invariants_test : public beast::unit_test::suite
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
testcase << "PermissionedDomain Set 2";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain bad credentials size " + std::to_string(tooBig)}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
|
||||
// create PD
|
||||
auto slePd = createPermissionedDomain(ac, A1, A2);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
// update PD
|
||||
{
|
||||
@@ -1321,16 +1295,18 @@ class Invariants_test : public beast::unit_test::suite
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
testcase << "PermissionedDomain Set 3";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain credentials aren't sorted"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
|
||||
// create PD
|
||||
auto slePd = createPermissionedDomain(ac, A1, A2);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
// update PD
|
||||
{
|
||||
@@ -1351,16 +1327,18 @@ class Invariants_test : public beast::unit_test::suite
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
testcase << "PermissionedDomain Set 4";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"permissioned domain credentials aren't unique"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
|
||||
// create PD
|
||||
auto slePd = createPermissionedDomain(ac, A1, A2);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
// update PD
|
||||
{
|
||||
@@ -1379,155 +1357,8 @@ class Invariants_test : public beast::unit_test::suite
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : badTers);
|
||||
|
||||
std::initializer_list<TER> goodTers = {tesSUCCESS, tesSUCCESS};
|
||||
|
||||
std::vector<std::string> badMoreThan1{{"transaction affected more than 1 permissioned domain entry."}};
|
||||
std::vector<std::string> emptyV;
|
||||
std::vector<std::string> badNoDomains{{"no domain objects affected by"}};
|
||||
std::vector<std::string> badNotDeleted{{"domain object modified, but not deleted by "}};
|
||||
std::vector<std::string> badDeleted{{"domain object deleted by"}};
|
||||
std::vector<std::string> badTx{{"domain object(s) affected by an unauthorized transaction."}};
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain set 2 domains ";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
fixPDEnabled ? badMoreThan1 : emptyV,
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, A1, A2);
|
||||
createPermissionedDomain(ac, A1, A2, 2, 11);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain del 2 domains";
|
||||
|
||||
Env env1(*this, features);
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
env1.fund(XRP(1000), A1, A2);
|
||||
env1.close();
|
||||
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
[[maybe_unused]] auto [seq2, pd2] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
env1.close();
|
||||
|
||||
doInvariantCheck(
|
||||
std::move(env1),
|
||||
A1,
|
||||
A2,
|
||||
fixPDEnabled ? badMoreThan1 : emptyV,
|
||||
[&pd1, &pd2](Account const&, Account const&, ApplyContext& ac) {
|
||||
auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1});
|
||||
auto sle2 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd2});
|
||||
ac.view().erase(sle1);
|
||||
ac.view().erase(sle2);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain set 0 domains ";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
fixPDEnabled ? badNoDomains : emptyV,
|
||||
[](Account const&, Account const&, ApplyContext&) { return true; },
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? badTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain del 0 domains";
|
||||
|
||||
Env env1(*this, features);
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
env1.fund(XRP(1000), A1, A2);
|
||||
env1.close();
|
||||
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
[[maybe_unused]] auto [seq2, pd2] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
env1.close();
|
||||
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
A1,
|
||||
A2,
|
||||
fixPDEnabled ? badNoDomains : emptyV,
|
||||
[](Account const&, Account const&, ApplyContext&) { return true; },
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixPDEnabled ? badTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain set, delete domain";
|
||||
|
||||
Env env1(*this, features);
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
env1.fund(XRP(1000), A1, A2);
|
||||
env1.close();
|
||||
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
env1.close();
|
||||
|
||||
doInvariantCheck(
|
||||
std::move(env1),
|
||||
A1,
|
||||
A2,
|
||||
fixPDEnabled ? badDeleted : emptyV,
|
||||
[&pd1](Account const&, Account const&, ApplyContext& ac) {
|
||||
auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1});
|
||||
ac.view().erase(sle1);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain del, create domain ";
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
fixPDEnabled ? badNotDeleted : emptyV,
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, A1, A2);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixPDEnabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain invalid tx";
|
||||
|
||||
doInvariantCheck(
|
||||
fixPDEnabled ? badTx : emptyV,
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, A1, A2);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPAYMENT, [](STObject&) {}},
|
||||
failTers);
|
||||
}
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1647,43 +1478,13 @@ class Invariants_test : public beast::unit_test::suite
|
||||
});
|
||||
}
|
||||
|
||||
static std::pair<std::uint32_t, uint256>
|
||||
createPermissionedDomainEnv(
|
||||
test::jtx::Env& env,
|
||||
test::jtx::Account const& A1,
|
||||
test::jtx::Account const& A2,
|
||||
std::uint32_t numCreds = 2)
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
pdomain::Credentials credentials;
|
||||
|
||||
for (std::size_t n = 0; n < numCreds; ++n)
|
||||
{
|
||||
auto credType = "cred_type" + std::to_string(n);
|
||||
credentials.push_back({A2, credType});
|
||||
}
|
||||
|
||||
std::uint32_t const seq = env.seq(A1);
|
||||
env(pdomain::setTx(A1, credentials));
|
||||
uint256 key = pdomain::getNewDomain(env.meta());
|
||||
|
||||
// std::cout << "PD, acc: " << A1.id() << ", seq: " << seq << ", k: " <<
|
||||
// key << std::endl;
|
||||
return {seq, key};
|
||||
}
|
||||
|
||||
void
|
||||
testPermissionedDEX(FeatureBitset features)
|
||||
testPermissionedDEX()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
bool const fixPDEnabled = features[fixPermissionedDomainInvariant];
|
||||
|
||||
testcase << "PermissionedDEX" + std::string(fixPDEnabled ? " fix" : "");
|
||||
testcase << "PermissionedDEX";
|
||||
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"domain doesn't exist"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
Keylet const offerKey = keylet::offer(A1.id(), 10);
|
||||
@@ -1710,9 +1511,12 @@ class Invariants_test : public beast::unit_test::suite
|
||||
|
||||
// missing domain ID in offer object
|
||||
doInvariantCheck(
|
||||
Env(*this, features),
|
||||
{{"hybrid offer is malformed"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
@@ -1727,154 +1531,116 @@ class Invariants_test : public beast::unit_test::suite
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
STTx{ttOFFER_CREATE, [&](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
// more than one entry in sfAdditionalBooks
|
||||
{
|
||||
Env env1(*this, features);
|
||||
doInvariantCheck(
|
||||
{{"hybrid offer is malformed"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
env1.fund(XRP(1000), A1, A2);
|
||||
env1.close();
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
sleOffer->setFlag(lsfHybrid);
|
||||
sleOffer->setFieldH256(sfDomainID, pdKeylet.key);
|
||||
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
env1.close();
|
||||
|
||||
doInvariantCheck(
|
||||
std::move(env1),
|
||||
A1,
|
||||
A2,
|
||||
{{"hybrid offer is malformed"}},
|
||||
[&pd1](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
sleOffer->setFlag(lsfHybrid);
|
||||
sleOffer->setFieldH256(sfDomainID, pd1);
|
||||
|
||||
STArray bookArr;
|
||||
bookArr.push_back(STObject::makeInnerObject(sfBook));
|
||||
bookArr.push_back(STObject::makeInnerObject(sfBook));
|
||||
sleOffer->setFieldArray(sfAdditionalBooks, bookArr);
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
}
|
||||
STArray bookArr;
|
||||
bookArr.push_back(STObject::makeInnerObject(sfBook));
|
||||
bookArr.push_back(STObject::makeInnerObject(sfBook));
|
||||
sleOffer->setFieldArray(sfAdditionalBooks, bookArr);
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
// hybrid offer missing sfAdditionalBooks
|
||||
{
|
||||
Env env1(*this, features);
|
||||
doInvariantCheck(
|
||||
{{"hybrid offer is malformed"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
env1.fund(XRP(1000), A1, A2);
|
||||
env1.close();
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
sleOffer->setFlag(lsfHybrid);
|
||||
sleOffer->setFieldH256(sfDomainID, pdKeylet.key);
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
env1.close();
|
||||
doInvariantCheck(
|
||||
{{"transaction consumed wrong domains"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
doInvariantCheck(
|
||||
std::move(env1),
|
||||
A1,
|
||||
A2,
|
||||
{{"hybrid offer is malformed"}},
|
||||
[&pd1](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
sleOffer->setFlag(lsfHybrid);
|
||||
sleOffer->setFieldH256(sfDomainID, pd1);
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
}
|
||||
Keylet const badDomainKeylet = keylet::permissionedDomain(A1.id(), 20);
|
||||
auto sleBadPd = std::make_shared<SLE>(badDomainKeylet);
|
||||
createPermissionedDomain(ac, sleBadPd, A1, A2);
|
||||
|
||||
{
|
||||
Env env1(*this, features);
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
sleOffer->setFieldH256(sfDomainID, pdKeylet.key);
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{
|
||||
ttOFFER_CREATE,
|
||||
[&](STObject& tx) {
|
||||
Account const A1{"A1"};
|
||||
Keylet const badDomainKey = keylet::permissionedDomain(A1.id(), 20);
|
||||
tx.setFieldH256(sfDomainID, badDomainKey.key);
|
||||
tx.setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
tx.setFieldAmount(sfTakerGets, XRP(1));
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
env1.fund(XRP(1000), A1, A2);
|
||||
env1.close();
|
||||
doInvariantCheck(
|
||||
{{"domain transaction affected regular offers"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
createPermissionedDomain(ac, slePd, A1, A2);
|
||||
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
[[maybe_unused]] auto [seq2, pd2] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
env1.close();
|
||||
|
||||
doInvariantCheck(
|
||||
std::move(env1),
|
||||
A1,
|
||||
A2,
|
||||
{{"transaction consumed wrong domains"}},
|
||||
[&pd1](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
sleOffer->setFieldH256(sfDomainID, pd1);
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{
|
||||
ttOFFER_CREATE,
|
||||
[&pd2, &A1](STObject& tx) {
|
||||
tx.setFieldH256(sfDomainID, pd2);
|
||||
tx.setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
tx.setFieldAmount(sfTakerGets, XRP(1));
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
}
|
||||
|
||||
{
|
||||
Env env1(*this, features);
|
||||
|
||||
Account const A1{"A1"};
|
||||
Account const A2{"A2"};
|
||||
env1.fund(XRP(1000), A1, A2);
|
||||
env1.close();
|
||||
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2);
|
||||
env1.close();
|
||||
|
||||
doInvariantCheck(
|
||||
std::move(env1),
|
||||
A1,
|
||||
A2,
|
||||
{{"domain transaction affected regular offers"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{
|
||||
ttOFFER_CREATE,
|
||||
[&](STObject& tx) {
|
||||
Account const A1{"A1"};
|
||||
tx.setFieldH256(sfDomainID, pd1);
|
||||
tx.setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
tx.setFieldAmount(sfTakerGets, XRP(1));
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
}
|
||||
Keylet const offerKey = keylet::offer(A2.id(), 10);
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
sleOffer->setAccountID(sfAccount, A2);
|
||||
sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
sleOffer->setFieldAmount(sfTakerGets, XRP(1));
|
||||
ac.view().insert(sleOffer);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{
|
||||
ttOFFER_CREATE,
|
||||
[&](STObject& tx) {
|
||||
Account const A1{"A1"};
|
||||
Keylet const domainKey = keylet::permissionedDomain(A1.id(), 10);
|
||||
tx.setFieldH256(sfDomainID, domainKey.key);
|
||||
tx.setFieldAmount(sfTakerPays, A1["USD"](10));
|
||||
tx.setFieldAmount(sfTakerGets, XRP(1));
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
}
|
||||
|
||||
Keylet
|
||||
@@ -3724,10 +3490,8 @@ public:
|
||||
testNoZeroEscrow();
|
||||
testValidNewAccountRoot();
|
||||
testNFTokenPageInvariants();
|
||||
testPermissionedDomainInvariants(defaultAmendments() | fixPermissionedDomainInvariant);
|
||||
testPermissionedDomainInvariants(defaultAmendments() - fixPermissionedDomainInvariant);
|
||||
testPermissionedDEX(defaultAmendments() | fixPermissionedDomainInvariant);
|
||||
testPermissionedDEX(defaultAmendments() - fixPermissionedDomainInvariant);
|
||||
testPermissionedDomainInvariants();
|
||||
testPermissionedDEX();
|
||||
testNoModifiedUnmodifiableFields();
|
||||
testValidPseudoAccounts();
|
||||
testValidLoanBroker();
|
||||
|
||||
@@ -38,17 +38,13 @@ class PermissionedDomains_test : public beast::unit_test::suite
|
||||
testable_amendments() //
|
||||
| featurePermissionedDomains | featureCredentials};
|
||||
|
||||
FeatureBitset withFix_{
|
||||
testable_amendments() //
|
||||
| featurePermissionedDomains | featureCredentials};
|
||||
|
||||
// Verify that each tx type can execute if the feature is enabled.
|
||||
void
|
||||
testEnabled(FeatureBitset features)
|
||||
testEnabled()
|
||||
{
|
||||
testcase("Enabled");
|
||||
Account const alice("alice");
|
||||
Env env(*this, features);
|
||||
Env env(*this, withFeature_);
|
||||
env.fund(XRP(1000), alice);
|
||||
pdomain::Credentials credentials{{alice, "first credential"}};
|
||||
env(pdomain::setTx(alice, credentials));
|
||||
@@ -241,10 +237,10 @@ class PermissionedDomains_test : public beast::unit_test::suite
|
||||
|
||||
// Test PermissionedDomainSet
|
||||
void
|
||||
testSet(FeatureBitset features)
|
||||
testSet()
|
||||
{
|
||||
testcase("Set");
|
||||
Env env(*this, features);
|
||||
Env env(*this, withFeature_);
|
||||
env.set_parse_failure_expected(true);
|
||||
|
||||
int const accNum = 12;
|
||||
@@ -399,10 +395,10 @@ class PermissionedDomains_test : public beast::unit_test::suite
|
||||
|
||||
// Test PermissionedDomainDelete
|
||||
void
|
||||
testDelete(FeatureBitset features)
|
||||
testDelete()
|
||||
{
|
||||
testcase("Delete");
|
||||
Env env(*this, features);
|
||||
Env env(*this, withFeature_);
|
||||
Account const alice("alice");
|
||||
|
||||
env.fund(XRP(1000), alice);
|
||||
@@ -452,14 +448,14 @@ class PermissionedDomains_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testAccountReserve(FeatureBitset features)
|
||||
testAccountReserve()
|
||||
{
|
||||
// Verify that the reserve behaves as expected for creating.
|
||||
testcase("Account Reserve");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env(*this, features);
|
||||
Env env(*this, withFeature_);
|
||||
Account const alice("alice");
|
||||
|
||||
// Fund alice enough to exist, but not enough to meet
|
||||
@@ -504,16 +500,12 @@ public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testEnabled(withFeature_);
|
||||
testEnabled(withFix_);
|
||||
testEnabled();
|
||||
testCredentialsDisabled();
|
||||
testDisabled();
|
||||
testSet(withFeature_);
|
||||
testSet(withFix_);
|
||||
testDelete(withFeature_);
|
||||
testDelete(withFix_);
|
||||
testAccountReserve(withFeature_);
|
||||
testAccountReserve(withFix_);
|
||||
testSet();
|
||||
testDelete();
|
||||
testAccountReserve();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@ namespace xrpl {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
#define TEST_EXPECT(cond) env.test.expect(cond, __FILE__, __LINE__)
|
||||
#define TEST_EXPECTS(cond, reason) \
|
||||
((cond) ? (env.test.pass(), true) : (env.test.fail((reason), __FILE__, __LINE__), false))
|
||||
|
||||
void
|
||||
doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, Issue const& issue)
|
||||
{
|
||||
@@ -16,13 +12,11 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value,
|
||||
auto const sle = env.le(keylet::account(account));
|
||||
if (none)
|
||||
{
|
||||
TEST_EXPECT(!sle);
|
||||
env.test.expect(!sle);
|
||||
}
|
||||
else if (TEST_EXPECT(sle))
|
||||
else if (env.test.expect(sle))
|
||||
{
|
||||
TEST_EXPECTS(
|
||||
sle->getFieldAmount(sfBalance) == value,
|
||||
sle->getFieldAmount(sfBalance).getText() + " / " + value.getText());
|
||||
env.test.expect(sle->getFieldAmount(sfBalance) == value);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -30,15 +24,15 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value,
|
||||
auto const sle = env.le(keylet::line(account, issue));
|
||||
if (none)
|
||||
{
|
||||
TEST_EXPECT(!sle);
|
||||
env.test.expect(!sle);
|
||||
}
|
||||
else if (TEST_EXPECT(sle))
|
||||
else if (env.test.expect(sle))
|
||||
{
|
||||
auto amount = sle->getFieldAmount(sfBalance);
|
||||
amount.setIssuer(issue.account);
|
||||
if (account > issue.account)
|
||||
amount.negate();
|
||||
TEST_EXPECTS(amount == value, amount.getText());
|
||||
env.test.expect(amount == value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,12 +43,12 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value,
|
||||
auto const sle = env.le(keylet::mptoken(mptIssue.getMptID(), account));
|
||||
if (none)
|
||||
{
|
||||
TEST_EXPECT(!sle);
|
||||
env.test.expect(!sle);
|
||||
}
|
||||
else if (TEST_EXPECT(sle))
|
||||
else if (env.test.expect(sle))
|
||||
{
|
||||
STAmount const amount{mptIssue, sle->getFieldU64(sfMPTAmount)};
|
||||
TEST_EXPECT(amount == value);
|
||||
env.test.expect(amount == value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1507,7 +1507,7 @@ ValidMPTIssuance::finalize(
|
||||
|
||||
void
|
||||
ValidPermissionedDomain::visitEntry(
|
||||
bool isDel,
|
||||
bool,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after)
|
||||
{
|
||||
@@ -1516,29 +1516,39 @@ ValidPermissionedDomain::visitEntry(
|
||||
if (after && after->getType() != ltPERMISSIONED_DOMAIN)
|
||||
return;
|
||||
|
||||
auto check = [isDel](std::vector<SleStatus>& sleStatus, std::shared_ptr<SLE const> const& sle) {
|
||||
auto check = [](SleStatus& sleStatus, std::shared_ptr<SLE const> const& sle) {
|
||||
auto const& credentials = sle->getFieldArray(sfAcceptedCredentials);
|
||||
sleStatus.credentialsSize_ = credentials.size();
|
||||
auto const sorted = credentials::makeSorted(credentials);
|
||||
|
||||
SleStatus ss{credentials.size(), false, !sorted.empty(), isDel};
|
||||
sleStatus.isUnique_ = !sorted.empty();
|
||||
|
||||
// If array have duplicates then all the other checks are invalid
|
||||
if (ss.isUnique_)
|
||||
sleStatus.isSorted_ = false;
|
||||
|
||||
if (sleStatus.isUnique_)
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (auto const& cred : sorted)
|
||||
{
|
||||
auto const& credTx = credentials[i++];
|
||||
ss.isSorted_ = (cred.first == credTx[sfIssuer]) && (cred.second == credTx[sfCredentialType]);
|
||||
if (!ss.isSorted_)
|
||||
sleStatus.isSorted_ = (cred.first == credTx[sfIssuer]) && (cred.second == credTx[sfCredentialType]);
|
||||
if (!sleStatus.isSorted_)
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleStatus.emplace_back(std::move(ss));
|
||||
};
|
||||
|
||||
if (before)
|
||||
{
|
||||
sleStatus_[0] = SleStatus();
|
||||
check(*sleStatus_[0], after);
|
||||
}
|
||||
|
||||
if (after)
|
||||
check(sleStatus_, after);
|
||||
{
|
||||
sleStatus_[1] = SleStatus();
|
||||
check(*sleStatus_[1], after);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1549,6 +1559,9 @@ ValidPermissionedDomain::finalize(
|
||||
ReadView const& view,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || result != tesSUCCESS)
|
||||
return true;
|
||||
|
||||
auto check = [](SleStatus const& sleStatus, beast::Journal const& j) {
|
||||
if (!sleStatus.credentialsSize_)
|
||||
{
|
||||
@@ -1582,76 +1595,7 @@ ValidPermissionedDomain::finalize(
|
||||
return true;
|
||||
};
|
||||
|
||||
if (view.rules().enabled(fixPermissionedDomainInvariant))
|
||||
{
|
||||
// No permissioned domains should be affected if the transaction failed
|
||||
if (result != tesSUCCESS)
|
||||
// If nothing changed, all is good. If there were changes, that's
|
||||
// bad.
|
||||
return sleStatus_.empty();
|
||||
|
||||
if (sleStatus_.size() > 1)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: transaction affected more "
|
||||
"than 1 permissioned domain entry.";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (tx.getTxnType())
|
||||
{
|
||||
case ttPERMISSIONED_DOMAIN_SET: {
|
||||
if (sleStatus_.empty())
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: no domain objects affected by "
|
||||
"PermissionedDomainSet";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const& sleStatus = sleStatus_[0];
|
||||
if (sleStatus.isDelete_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: domain object "
|
||||
"deleted by PermissionedDomainSet";
|
||||
return false;
|
||||
}
|
||||
return check(sleStatus, j);
|
||||
}
|
||||
case ttPERMISSIONED_DOMAIN_DELETE: {
|
||||
if (sleStatus_.empty())
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: no domain objects affected by "
|
||||
"PermissionedDomainDelete";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sleStatus_[0].isDelete_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: domain object "
|
||||
"modified, but not deleted by "
|
||||
"PermissionedDomainDelete";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
if (!sleStatus_.empty())
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: " << sleStatus_.size()
|
||||
<< " domain object(s) affected by an "
|
||||
"unauthorized transaction. "
|
||||
<< tx.getTxnType();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || result != tesSUCCESS || sleStatus_.empty())
|
||||
return true;
|
||||
return check(sleStatus_[0], j);
|
||||
}
|
||||
return (sleStatus_[0] ? check(*sleStatus_[0], j) : true) && (sleStatus_[1] ? check(*sleStatus_[1], j) : true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -449,11 +449,9 @@ class ValidPermissionedDomain
|
||||
struct SleStatus
|
||||
{
|
||||
std::size_t credentialsSize_{0};
|
||||
bool isSorted_ = false;
|
||||
bool isUnique_ = false;
|
||||
bool isDelete_ = false;
|
||||
bool isSorted_ = false, isUnique_ = false;
|
||||
};
|
||||
std::vector<SleStatus> sleStatus_;
|
||||
std::optional<SleStatus> sleStatus_[2];
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user