mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-21 19:18:22 +00:00
Compare commits
54
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66c6f21c60 | ||
|
|
3d526d456e | ||
|
|
f05c9f7913 | ||
|
|
c2a4bc3aa1 | ||
|
|
718185e3b2 | ||
|
|
1d7783bb86 | ||
|
|
9fd2c552f5 | ||
|
|
70b8fd301b | ||
|
|
e6564f553d | ||
|
|
ed96e60ce3 | ||
|
|
2ebd745a1b | ||
|
|
cfcbe45b60 | ||
|
|
81e06a9ba8 | ||
|
|
b3141d91fa | ||
|
|
1b1cd4c260 | ||
|
|
a482d6aced | ||
|
|
299627298f | ||
|
|
640798696b | ||
|
|
47512afad5 | ||
|
|
ff3708a757 | ||
|
|
391a1e442c | ||
|
|
9e77212900 | ||
|
|
93f5a0e217 | ||
|
|
71367f361c | ||
|
|
931d21b2a7 | ||
|
|
c99feb82e7 | ||
|
|
7b53a5e0c5 | ||
|
|
4a02518497 | ||
|
|
0b6c3630cc | ||
|
|
4d04ba5be5 | ||
|
|
ed53557d41 | ||
|
|
e4d10393f3 | ||
|
|
fcc7f57e82 | ||
|
|
a25229f154 | ||
|
|
eb6eaf2532 | ||
|
|
ff987fc7c6 | ||
|
|
d21137c4c1 | ||
|
|
ac7db2e621 | ||
|
|
42005a8080 | ||
|
|
1f7b1b3a78 | ||
|
|
80b90544c5 | ||
|
|
00b9a8cd67 | ||
|
|
3be49f814a | ||
|
|
1674fabe81 | ||
|
|
6dfa47ce7a | ||
|
|
bef095be65 | ||
|
|
8e5d774c36 | ||
|
|
fb8fb30f6c | ||
|
|
a553001125 | ||
|
|
57782e84ee | ||
|
|
9d5076c8a9 | ||
|
|
1af379e09f | ||
|
|
1ced0875ae | ||
|
|
53e6d7580a |
No files matched your search
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"image_tag": "sha-1798262",
|
||||
"image_tag": "sha-ed96e60",
|
||||
"configs": {
|
||||
"ubuntu": [
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@ permissions:
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-1798262
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-ed96e60
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
|
||||
@@ -41,7 +41,7 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-1798262
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-ed96e60
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
needs: [determine-files]
|
||||
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-1798262"
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-ed96e60"
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
@@ -28,7 +28,7 @@ permissions:
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-1798262
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-ed96e60
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-1798262
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-ed96e60
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-1798262
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-ed96e60
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
@@ -40,7 +40,7 @@ defaults:
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-1798262
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-ed96e60
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
||||
|
||||
@@ -36,6 +36,10 @@ Version 3.5.0 is not yet released.
|
||||
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||
|
||||
### Bugfixes in 3.5.0
|
||||
|
||||
- `feature`: The admin-only `vetoed` field now returns `invalidParams` unless its value is a boolean. [#7583](https://github.com/XRPLF/rippled/pull/7583)
|
||||
|
||||
## XRP Ledger server version 3.4.0
|
||||
|
||||
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
|
||||
|
||||
@@ -26,13 +26,7 @@ if [ "$(uname -s)" != "Linux" ]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# TODO: Add jq to the Nix packages and use it here instead of Python.
|
||||
folders="$(python3 -c '
|
||||
import json, sys
|
||||
for node in json.load(open(sys.argv[1]))["graph"]["nodes"].values():
|
||||
if node["context"] == "build" and node.get("package_folder"):
|
||||
print(node["package_folder"])
|
||||
' "$1" | sort -u)"
|
||||
folders="$(jq -r '.graph.nodes[] | select(.context == "build" and .package_folder) | .package_folder' "$1" | sort -u)"
|
||||
|
||||
checked=0
|
||||
failed=0
|
||||
|
||||
+17
-6
@@ -20,9 +20,11 @@
|
||||
# development setups, but not in the macOS CI environment. They are checked
|
||||
# everywhere except when running in CI on macOS.
|
||||
#
|
||||
# Tools that Nix also exposes under a version-suffixed name (`clang-tidy-22`,
|
||||
# `g++-15`, ...) are probed under both names: a suffixed name can break while
|
||||
# the plain one still works (see mkVersionedToolLinks in nix/packages.nix).
|
||||
# Tools that Nix also exposes under a version-suffixed name
|
||||
# (`clang-tidy-<v>`, `g++-<v>`, ...) are probed under both names:
|
||||
# a suffixed name can break while the plain one still works
|
||||
# (see mkVersionedToolLinks in nix/packages.nix).
|
||||
# The suffix is the major version of the plain `clang` / `gcc` on PATH.
|
||||
#
|
||||
# Tools scoped to a single dev shell rather than to commonPackages are checked
|
||||
# only in that shell, keyed off XRPL_DEVSHELL.
|
||||
@@ -34,9 +36,16 @@
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# Version suffixes of the Nix tool links, tracking nix/packages.nix.
|
||||
gcc_version=15
|
||||
llvm_version=22
|
||||
# major_version <compiler>
|
||||
# Major version of a compiler on PATH, or "unknown" when it isn't there.
|
||||
major_version() {
|
||||
local version
|
||||
version="$("$1" -dumpversion 2>/dev/null)" || version=""
|
||||
version="${version%%.*}"
|
||||
printf '%s' "${version:-unknown}"
|
||||
}
|
||||
|
||||
llvm_version="$(major_version clang)"
|
||||
|
||||
missing=()
|
||||
checked=0
|
||||
@@ -112,6 +121,7 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
check ClangBuildAnalyzer
|
||||
check curl
|
||||
check file
|
||||
check jq
|
||||
check less
|
||||
check make
|
||||
# net-tools netstat reports "net-tools X.Y"; macOS ships BSD netstat with no
|
||||
@@ -180,6 +190,7 @@ fi
|
||||
if [ "${os}" = "linux" ]; then
|
||||
echo
|
||||
echo "GCC toolchain:"
|
||||
gcc_version="$(major_version gcc)"
|
||||
check gcc
|
||||
check "gcc-${gcc_version}"
|
||||
check g++
|
||||
|
||||
@@ -26,8 +26,6 @@ import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
CLANG_TIDY_VERSION = 22
|
||||
|
||||
# Extensions run-clang-tidy can analyse: `.cpp` translation units and, thanks to
|
||||
# the `verify_headers` build option, `.h`/`.hpp` headers (each has its own
|
||||
# compile_commands.json entry). `.ipp` fragments have no entry and are skipped.
|
||||
@@ -39,8 +37,21 @@ TIDY_EXTENSIONS = {".cpp", ".h", ".hpp"}
|
||||
FILEPATH_RE = re.compile(r"^(\s*(?:-\s+)?FilePath:\s*)'((?:[^']|'')*)'\s*$")
|
||||
|
||||
|
||||
def find_tool(name: str) -> str | None:
|
||||
for candidate in (f"{name}-{CLANG_TIDY_VERSION}", name):
|
||||
def clang_tidy_major() -> str | None:
|
||||
"""Major version of the `clang-tidy` on PATH, which run-clang-tidy invokes."""
|
||||
if not (clang_tidy := shutil.which("clang-tidy")):
|
||||
return None
|
||||
output = subprocess.run(
|
||||
[clang_tidy, "--version"], capture_output=True, text=True
|
||||
).stdout
|
||||
m = re.search(r"LLVM version (\d+)", output)
|
||||
return m.group(1) if m else None
|
||||
|
||||
|
||||
def find_tool(name: str, version: str | None) -> str | None:
|
||||
"""Prefer `<name>-<version>`, so a host tool of another version can't win."""
|
||||
candidates = ([f"{name}-{version}"] if version else []) + [name]
|
||||
for candidate in candidates:
|
||||
if path := shutil.which(candidate):
|
||||
return path
|
||||
return None
|
||||
@@ -103,8 +114,9 @@ def main():
|
||||
if not files:
|
||||
return 0
|
||||
|
||||
run_clang_tidy = find_tool("run-clang-tidy")
|
||||
clang_apply_replacements = find_tool("clang-apply-replacements")
|
||||
version = clang_tidy_major()
|
||||
run_clang_tidy = find_tool("run-clang-tidy", version)
|
||||
clang_apply_replacements = find_tool("clang-apply-replacements", version)
|
||||
missing = [
|
||||
name
|
||||
for name, path in (
|
||||
@@ -114,9 +126,10 @@ def main():
|
||||
if not path
|
||||
]
|
||||
if missing:
|
||||
tried = f" (tried the '-{version}' suffix too)" if version else ""
|
||||
print(
|
||||
f"clang-tidy check failed: TIDY is enabled but {' and '.join(missing)} "
|
||||
f"was not found in PATH (tried the '-{CLANG_TIDY_VERSION}' suffix too).",
|
||||
f"was not found in PATH{tried}.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
@@ -292,6 +292,14 @@ if(xrpld)
|
||||
)
|
||||
target_sources(xrpld PRIVATE ${sources})
|
||||
|
||||
rpcspec_generate_instantiations(
|
||||
OUT_VAR rpcspec_instantiations
|
||||
VALUE_TYPE "::json::Value"
|
||||
VIEW_HEADER "xrpld/rpc/detail/JsonObjectView.hpp"
|
||||
HANDLERS ledger
|
||||
)
|
||||
target_sources(xrpld PRIVATE ${rpcspec_instantiations})
|
||||
|
||||
if(tests)
|
||||
file(
|
||||
GLOB_RECURSE sources
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"requires": [
|
||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
|
||||
"xrpl-rpc-spec/0.1.19#870b2d3abcfbbf13b61c2d3c69495060%1790348286.187549",
|
||||
"xrpl-rpc-spec/0.1.20#6daa13eeb4e6c92b82ea6d78866e1da8%1790720024.368048",
|
||||
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
|
||||
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
|
||||
|
||||
+9
-1
@@ -3,5 +3,13 @@
|
||||
core:non_interactive=True
|
||||
core.download:parallel={{ os.cpu_count() }}
|
||||
core.upload:parallel={{ os.cpu_count() }}
|
||||
tools.files.download:retry=5
|
||||
# Fall back to Conan Center's source backups when a recipe's upstream URL is down
|
||||
# (e.g. the GNU FTP mirrors), see
|
||||
# https://github.com/conan-io/conan-center-index/issues/28147#issuecomment-3183544772
|
||||
# The backups are only tried once every upstream URL has used up its retries,
|
||||
# so keep the retries low.
|
||||
core.sources:download_urls=["origin", "https://c3i.jfrog.io/artifactory/conan-center-backup-sources"]
|
||||
tools.files.download:retry=1
|
||||
tools.files.download:retry_wait=10
|
||||
# Fail fast on unreachable hosts (default connect timeout is 30s), keep the 60s read timeout.
|
||||
core.net.http:timeout=(5, 60)
|
||||
@@ -29,9 +29,9 @@ os.version={{ min_macos_version }}
|
||||
[conf]
|
||||
{# The Boost recipe builds with b2, which doesn't use Conan's toolchain files. #}
|
||||
{# Instead it hand-rolls the compiler for user-config.jam, #}
|
||||
{# and its fallback probes a version-suffixed binary (e.g. `g++-15`) before plain `g++`. #}
|
||||
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-15` suffix), #}
|
||||
{# so on a host that also has a system `g++-15` the probe escapes Nix #}
|
||||
{# and its fallback probes a version-suffixed binary (e.g. `g++-<major>`) before plain `g++`. #}
|
||||
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-<major>` suffix), #}
|
||||
{# so on a host that also has a system `g++-<major>` the probe escapes Nix #}
|
||||
{# and picks the system compiler, which is mismatched with the Nix libraries #}
|
||||
{# and breaks the build (e.g. Boost.Stacktrace link checks fail). #}
|
||||
{# Pinning the executables here short-circuits that probe so Boost (and the rest of the toolchain) #}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ class Xrpl(ConanFile):
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.3",
|
||||
"soci/4.0.3",
|
||||
"xrpl-rpc-spec/0.1.19",
|
||||
"xrpl-rpc-spec/0.1.20",
|
||||
"zlib/1.3.2",
|
||||
]
|
||||
|
||||
|
||||
Vendored
+9
-7
@@ -10,14 +10,15 @@ This document explains how to set one up.
|
||||
support it — see [compiler support for C++23][cpp23-support].
|
||||
The versions currently tested in CI are:
|
||||
|
||||
| Compiler | Version |
|
||||
| ----------- | ------------------ |
|
||||
| GCC | 15.2 |
|
||||
| Clang | 22 |
|
||||
| Apple Clang | 21 |
|
||||
| MSVC | Visual Studio 2026 |
|
||||
| Compiler | Version |
|
||||
| ----------- | ------------------------------- |
|
||||
| GCC | `gccVersion` in [packages.nix] |
|
||||
| Clang | `llvmVersion` in [packages.nix] |
|
||||
| Apple Clang | 21 |
|
||||
| MSVC | Visual Studio 2026 |
|
||||
|
||||
LLVM tools (`clang-tidy` and `clang-format`) are also pinned to version 22.
|
||||
LLVM tools (`clang-tidy` and `clang-format`)
|
||||
come from the same LLVM release as Clang.
|
||||
|
||||
### Older compilers
|
||||
|
||||
@@ -156,3 +157,4 @@ version out of the box — run it via `run-clang-tidy`. No separate installation
|
||||
is needed.
|
||||
|
||||
[cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23
|
||||
[packages.nix]: ../../nix/packages.nix
|
||||
@@ -17,7 +17,7 @@
|
||||
namespace xrpl {
|
||||
|
||||
template <typename Key>
|
||||
static std::size_t
|
||||
std::size_t
|
||||
extract(Key const& key)
|
||||
{
|
||||
return key;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace beast::unit_test {
|
||||
namespace detail {
|
||||
|
||||
template <class String>
|
||||
static std::string
|
||||
std::string
|
||||
makeReason(String const& reason, char const* file, int line)
|
||||
{
|
||||
std::string s(reason);
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -25,6 +31,52 @@ public:
|
||||
: Base(keylet::amendments(), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of amendments this entry reports as enabled.
|
||||
*
|
||||
* @return the set of enabled amendments.
|
||||
*/
|
||||
[[nodiscard]] std::set<UInt256>
|
||||
enabledAmendments() const
|
||||
{
|
||||
std::set<UInt256> amendments;
|
||||
|
||||
if (this->exists() && (*this)->isFieldPresent(sfAmendments))
|
||||
{
|
||||
auto const& v = (*this)->getFieldV256(sfAmendments);
|
||||
amendments.insert_range(v);
|
||||
}
|
||||
|
||||
return amendments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of amendments that have achieved majority, to the time
|
||||
* majority was reached.
|
||||
*
|
||||
* @return a map of amendment to the time majority was reached.
|
||||
*/
|
||||
[[nodiscard]] std::map<UInt256, NetClock::time_point>
|
||||
majorityAmendments() const
|
||||
{
|
||||
std::map<UInt256, NetClock::time_point> ret;
|
||||
|
||||
if (this->exists() && (*this)->isFieldPresent(sfMajorities))
|
||||
{
|
||||
using TimePoint = NetClock::time_point;
|
||||
using Duration = TimePoint::duration;
|
||||
|
||||
auto const majorities = (*this)->getFieldArray(sfMajorities);
|
||||
|
||||
for (auto const& m : majorities)
|
||||
{
|
||||
ret[m.getFieldH256(sfAmendment)] = TimePoint(Duration(m.getFieldU32(sfCloseTime)));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
using AmendmentsEntryR = AmendmentsEntry<ReadView>;
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -27,6 +29,291 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Validate that @p account may lock @p amount of a token for later delivery
|
||||
* to @p dest.
|
||||
*
|
||||
* The lock-side counterpart of escrowUnlockPreclaimHelper: every issuer
|
||||
* control (locking opt-in, authorization, freeze/lock, transferability,
|
||||
* spendable balance) that gates locking token value lives here, so any
|
||||
* transactor that locks funds applies the same rules. The signature is
|
||||
* view-based rather than PreclaimContext-based so it can also run from
|
||||
* doApply.
|
||||
*/
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowLockPreclaimHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
auto const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
STAmount const balance = (*sleRippleState)[sfBalance];
|
||||
|
||||
// If balance is positive, issuer must have higher address than account
|
||||
if (balance > beast::kZero && issuer < account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If balance is negative, issuer must have lower address than account
|
||||
if (balance < beast::kZero && issuer > account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(view, issue, account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, issue, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecFROZEN
|
||||
if (isFrozen(view, account, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
// If the issuer has frozen the destination, return tecFROZEN
|
||||
if (isFrozen(view, dest, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
STAmount const spendableAmount =
|
||||
accountHolds(view, account, issue.currency, issuer, FreezeHandling::IgnoreFreeze, j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the amount is not addable to the balance, return tecPRECISION_LOSS
|
||||
if (!canAdd(spendableAmount, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
|
||||
if (!sleIssuance->isFlag(lsfMPTCanEscrow))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the issuer is not the same as the issuer of the mpt, return
|
||||
// tecNO_PERMISSION
|
||||
if (sleIssuance->getAccountID(sfIssuer) != issuer)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account does not have the mpt, return tecOBJECT_NOT_FOUND
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, account)))
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
if (auto const ter = requireAuth(view, mptIssue, dest, AuthType::WeakAuth); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the issuer has frozen the destination, return tecLOCKED
|
||||
if (isFrozen(view, dest, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the mpt cannot be transferred, return tecNO_AUTH
|
||||
if (auto const ter = canTransfer(view, mptIssue, account, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
view,
|
||||
account,
|
||||
amount.get<MPTIssue>(),
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
AuthHandling::IgnoreAuth,
|
||||
j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowLockApplyHelper(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockApplyHelper<Issue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter =
|
||||
directSendNoFee(view, sender, issuer, amount, !amount.holds<MPTIssue>(), journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockApplyHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter = lockEscrowMPT(view, sender, amount, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowUnlockPreclaimHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze = true);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowUnlockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, amount.get<Issue>(), account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has deep frozen the destination, return tecFROZEN
|
||||
if (checkFreeze &&
|
||||
isDeepFrozen(view, account, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowUnlockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (checkFreeze && isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowUnlockApplyHelper(
|
||||
@@ -55,9 +342,6 @@ escrowUnlockApplyHelper<Issue>(
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
|
||||
@@ -67,6 +351,10 @@ escrowUnlockApplyHelper<Issue>(
|
||||
if (receiverIssuer)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
|
||||
if (!ctx.view.exists(trustLineKey) && createAsset)
|
||||
{
|
||||
// Can the account cover the trust line's reserve?
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Enumeration of ledger shortcuts for specifying which ledger to use.
|
||||
*
|
||||
* These shortcuts provide a convenient way to reference commonly used ledgers
|
||||
* without needing to specify their exact hash or sequence number.
|
||||
*/
|
||||
enum class LedgerShortcut {
|
||||
/**
|
||||
* The current working ledger (open, not yet closed)
|
||||
*/
|
||||
Current,
|
||||
|
||||
/**
|
||||
* The most recently closed ledger (may not be validated)
|
||||
*/
|
||||
Closed,
|
||||
|
||||
/**
|
||||
* The most recently validated ledger
|
||||
*/
|
||||
Validated
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -15,6 +15,7 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FEATURE(DefragDirectories, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(Cosign, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocolV1_2, Supported::No, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
#include <xrpl/protocol/LedgerShortcut.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/TxSearched.h>
|
||||
@@ -105,21 +104,6 @@ public:
|
||||
using TxnMetaLedgerType = std::tuple<Blob, Blob, std::uint32_t>;
|
||||
using MetaTxsList = std::vector<TxnMetaLedgerType>;
|
||||
|
||||
using LedgerSequence = uint32_t;
|
||||
using LedgerHash = UInt256;
|
||||
using LedgerSpecifier = std::variant<LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash>;
|
||||
|
||||
struct AccountTxArgs
|
||||
{
|
||||
AccountID account;
|
||||
std::optional<LedgerSpecifier> ledger;
|
||||
bool binary = false;
|
||||
bool forward = false;
|
||||
uint32_t limit = 0;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::optional<DelegateFilter> delegate;
|
||||
};
|
||||
|
||||
struct AccountTxResult
|
||||
{
|
||||
std::variant<AccountTxs, MetaTxsList> transactions;
|
||||
|
||||
+34
-31
@@ -5,8 +5,8 @@ Core build tools:
|
||||
cmake version 4.4.3
|
||||
/nix/store/q85csxf4s4shx89zif097h1ql4ax1rrz-cmake-4.4.3/bin/cmake
|
||||
✅ conan
|
||||
Conan version 2.32.0
|
||||
/nix/store/921jqsgbilixmr3xchj95si0jl8pi13z-conan-2.32.0/bin/conan
|
||||
Conan version 2.33.0
|
||||
/nix/store/rp0zf22rx3iwnhxp4d53ncirn4lzg68j-conan-2.33.0/bin/conan
|
||||
✅ git
|
||||
git version 2.55.0
|
||||
/nix/store/gw7c7m0dwca5lg4152x9lpmxpj171bzw-git-2.55.0/bin/git
|
||||
@@ -19,17 +19,17 @@ Development tooling:
|
||||
ccache version 4.13.6
|
||||
/nix/store/ydr5nlzxp1djb5256y0zzqa87vz55gsk-ccache-4.13.6/bin/ccache
|
||||
✅ clang
|
||||
clang version 22.1.8
|
||||
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang
|
||||
✅ clang-22
|
||||
clang version 22.1.8
|
||||
/nix/store/w826sqb98nnym6kzackg829664iqdlxj-clang-22/bin/clang-22
|
||||
clang version 23.1.0
|
||||
/nix/store/2c16z6akvv65znb8330sl5qpklaf3194-clang-wrapper-23.1.0/bin/clang
|
||||
✅ clang-23
|
||||
clang version 23.1.0
|
||||
/nix/store/vfd0q2395vyn0bsjsh1b4068icy1x9ph-clang-23/bin/clang-23
|
||||
✅ clang++
|
||||
clang version 22.1.8
|
||||
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang++
|
||||
✅ clang++-22
|
||||
clang version 22.1.8
|
||||
/nix/store/wnwyw3yfqrhpxij65bl3pa1x65q2zg3m-clang++-22/bin/clang++-22
|
||||
clang version 23.1.0
|
||||
/nix/store/2c16z6akvv65znb8330sl5qpklaf3194-clang-wrapper-23.1.0/bin/clang++
|
||||
✅ clang++-23
|
||||
clang version 23.1.0
|
||||
/nix/store/0i6kfnif2v4sqyv5fzfg8a9n09rw9lkp-clang++-23/bin/clang++-23
|
||||
✅ ClangBuildAnalyzer
|
||||
ClangBuildAnalyzer 1.6.0
|
||||
/nix/store/c7jjnw29ra78jdlzjww2m374izhy05n8-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
|
||||
@@ -39,6 +39,9 @@ Development tooling:
|
||||
✅ file
|
||||
file-5.48
|
||||
/nix/store/y44vpziwbzfm8020dzlqqbybyyy7lb6g-file-5.48/bin/file
|
||||
✅ jq
|
||||
jq-1.8.2
|
||||
/nix/store/wm7v59a2lhj1w2k8jz53spcs1iizm9lv-jq-1.8.2-bin/bin/jq
|
||||
✅ less
|
||||
less 710 (PCRE2 regular expressions)
|
||||
/nix/store/3yn3dx5fwg5i0inqqzwnxq7f6lwhpjqa-less-710/bin/less
|
||||
@@ -64,23 +67,23 @@ Development tooling:
|
||||
Zip 3.0
|
||||
/nix/store/dvawr36npnad4xa6dxi2ss0fw44aa82r-zip-3.0/bin/zip
|
||||
✅ clang-apply-replacements
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-apply-replacements
|
||||
✅ clang-apply-replacements-22
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/w4bz034k4l0w719rrmbwnqcgyjbswy81-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||
clang-apply-replacements version 23.1.0
|
||||
/nix/store/mva6ngy45m9in3m2lsfccj1y0z6n1f5s-clang-tools-23.1.0/bin/clang-apply-replacements
|
||||
✅ clang-apply-replacements-23
|
||||
clang-apply-replacements version 23.1.0
|
||||
/nix/store/872k02mqr21hxwcpfwaqkxljfj60aak0-clang-apply-replacements-23/bin/clang-apply-replacements-23
|
||||
✅ clang-format
|
||||
clang-format version 22.1.8
|
||||
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-format
|
||||
✅ clang-format-22
|
||||
clang-format version 22.1.8
|
||||
/nix/store/w7z346l0r8y36b6lc5i7jzj3w0581l2y-clang-format-22/bin/clang-format-22
|
||||
clang-format version 23.1.0
|
||||
/nix/store/mva6ngy45m9in3m2lsfccj1y0z6n1f5s-clang-tools-23.1.0/bin/clang-format
|
||||
✅ clang-format-23
|
||||
clang-format version 23.1.0
|
||||
/nix/store/wf30x6lcya44n2y1fagd6q7x7p4gyl6f-clang-format-23/bin/clang-format-23
|
||||
✅ clang-tidy
|
||||
LLVM version 22.1.8
|
||||
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-tidy
|
||||
✅ clang-tidy-22
|
||||
LLVM version 22.1.8
|
||||
/nix/store/0lv25qd3ddgjm3ndn70q61jfr6krlkl7-clang-tidy-22/bin/clang-tidy-22
|
||||
LLVM version 23.1.0
|
||||
/nix/store/mva6ngy45m9in3m2lsfccj1y0z6n1f5s-clang-tools-23.1.0/bin/clang-tidy
|
||||
✅ clang-tidy-23
|
||||
LLVM version 23.1.0
|
||||
/nix/store/kgx00aass66g02p1s4gqqf65kq39m4ci-clang-tidy-23/bin/clang-tidy-23
|
||||
✅ dot
|
||||
dot - graphviz version 15.1.1 (0)
|
||||
/nix/store/mc99a6bpbk2waym2cnw6ifmxli4ndb4x-graphviz-15.1.1/bin/dot
|
||||
@@ -107,10 +110,10 @@ Development tooling:
|
||||
/nix/store/ivh54xypg9qvd3ha9lis0i8p4nkf8n7a-pre-commit-4.6.2/bin/pre-commit
|
||||
✅ run-clang-tidy
|
||||
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
||||
/nix/store/3xss1mm3mh1hzcmg182na6ki1p5rnxf1-run-clang-tidy/bin/run-clang-tidy
|
||||
✅ run-clang-tidy-22
|
||||
/nix/store/zjbzr154xan5xg9pmjiv0zkwklyb94wv-run-clang-tidy/bin/run-clang-tidy
|
||||
✅ run-clang-tidy-23
|
||||
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
||||
/nix/store/w68r9z07hcq4fwbfyq3yvc7qxm8aqfbl-run-clang-tidy-22/bin/run-clang-tidy-22
|
||||
/nix/store/svhfrpgx2j581brsqmkpav9b286v22yy-run-clang-tidy-23/bin/run-clang-tidy-23
|
||||
|
||||
Rust toolchain:
|
||||
✅ cargo
|
||||
@@ -143,4 +146,4 @@ Rust toolchain:
|
||||
|
||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||
|
||||
✅ All 45 checked tools are present and runnable.
|
||||
✅ All 46 checked tools are present and runnable.
|
||||
@@ -39,6 +39,9 @@ Development tooling:
|
||||
✅ file
|
||||
file-5.48
|
||||
/nix/store/g0fbnccaan8q0rxlw43xnbyb90i94vlc-file-5.48/bin/file
|
||||
✅ jq
|
||||
jq-1.8.2
|
||||
/nix/store/m3y7hqmy20nd1ps6qy8410a326255bf0-jq-1.8.2-bin/bin/jq
|
||||
✅ less
|
||||
less 710 (PCRE2 regular expressions)
|
||||
/nix/store/zi4d0awnc6crz18s177bv9y2yz9al3lq-less-710/bin/less
|
||||
@@ -65,22 +68,22 @@ Development tooling:
|
||||
/nix/store/5ypvkry3qra47dylc9kfk36r1q9vha80-zip-3.0/bin/zip
|
||||
✅ clang-apply-replacements
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/dykdkqyz48al2mllwjpwgdxyxgkwhk9x-clang-tools-22.1.8/bin/clang-apply-replacements
|
||||
/nix/store/37jhp4j3y339zmkx09qbr3fpfy3q0kl0-clang-tools-22.1.8/bin/clang-apply-replacements
|
||||
✅ clang-apply-replacements-22
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/m0aqc6dbryn1mvr0m2zsxxgpmahcvp6v-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||
/nix/store/qg0b99zn522gl3vz9hx9ni9ianb11d1r-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||
✅ clang-format
|
||||
clang-format version 22.1.8
|
||||
/nix/store/dykdkqyz48al2mllwjpwgdxyxgkwhk9x-clang-tools-22.1.8/bin/clang-format
|
||||
/nix/store/37jhp4j3y339zmkx09qbr3fpfy3q0kl0-clang-tools-22.1.8/bin/clang-format
|
||||
✅ clang-format-22
|
||||
clang-format version 22.1.8
|
||||
/nix/store/grpzqmx46d3xr3ssrz4ibkl9xr7rvjzj-clang-format-22/bin/clang-format-22
|
||||
/nix/store/m1al4bygpn7i13q1kalavybwh9ldg6rz-clang-format-22/bin/clang-format-22
|
||||
✅ clang-tidy
|
||||
LLVM version 22.1.8
|
||||
/nix/store/dykdkqyz48al2mllwjpwgdxyxgkwhk9x-clang-tools-22.1.8/bin/clang-tidy
|
||||
/nix/store/37jhp4j3y339zmkx09qbr3fpfy3q0kl0-clang-tools-22.1.8/bin/clang-tidy
|
||||
✅ clang-tidy-22
|
||||
LLVM version 22.1.8
|
||||
/nix/store/v9g20lw1q1qq9ljwvh5k580hgd9hmsv8-clang-tidy-22/bin/clang-tidy-22
|
||||
/nix/store/h51xrqf8xgx4zil2rm1ydp909858y4xm-clang-tidy-22/bin/clang-tidy-22
|
||||
✅ dot
|
||||
dot - graphviz version 15.1.1 (0)
|
||||
/nix/store/f7r47nc0d13xa4c2xaaxa6dp3pzm9sp1-graphviz-15.1.1/bin/dot
|
||||
@@ -171,4 +174,4 @@ Mold:
|
||||
|
||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||
|
||||
✅ All 53 checked tools are present and runnable.
|
||||
✅ All 54 checked tools are present and runnable.
|
||||
@@ -39,6 +39,9 @@ Development tooling:
|
||||
✅ file
|
||||
file-5.48
|
||||
/nix/store/66sd1x1hls0yijibn00iz08jh4fnas9z-file-5.48/bin/file
|
||||
✅ jq
|
||||
jq-1.8.2
|
||||
/nix/store/kr5py0dcqc9f3l9148qd1axh0z77lld3-jq-1.8.2-bin/bin/jq
|
||||
✅ less
|
||||
less 710 (PCRE2 regular expressions)
|
||||
/nix/store/n6z9dv3yjlgvfx1nmrx6qzyv8vzim1l2-less-710/bin/less
|
||||
@@ -65,22 +68,22 @@ Development tooling:
|
||||
/nix/store/lfwhq17wfcmjy823fldhlhxj1hjchdzc-zip-3.0/bin/zip
|
||||
✅ clang-apply-replacements
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/iv4dxwnzvl0lqj8jfyibwf5ijb8hjw2i-clang-tools-22.1.8/bin/clang-apply-replacements
|
||||
/nix/store/6jr2pbz9mzxznf47lw1w6ss1arqsakw6-clang-tools-22.1.8/bin/clang-apply-replacements
|
||||
✅ clang-apply-replacements-22
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/7gq05wr155k0pm1b2k73pya3l35hgifl-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||
/nix/store/mgcaw2063w2d4s61fz023bd0pf3vq5y7-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||
✅ clang-format
|
||||
clang-format version 22.1.8
|
||||
/nix/store/iv4dxwnzvl0lqj8jfyibwf5ijb8hjw2i-clang-tools-22.1.8/bin/clang-format
|
||||
/nix/store/6jr2pbz9mzxznf47lw1w6ss1arqsakw6-clang-tools-22.1.8/bin/clang-format
|
||||
✅ clang-format-22
|
||||
clang-format version 22.1.8
|
||||
/nix/store/54pnici9imw8ff0j5m7nig7dwaxrnlhv-clang-format-22/bin/clang-format-22
|
||||
/nix/store/2iwvl9784yrjfjhfac46z5ihwmbaavk7-clang-format-22/bin/clang-format-22
|
||||
✅ clang-tidy
|
||||
LLVM version 22.1.8
|
||||
/nix/store/iv4dxwnzvl0lqj8jfyibwf5ijb8hjw2i-clang-tools-22.1.8/bin/clang-tidy
|
||||
/nix/store/6jr2pbz9mzxznf47lw1w6ss1arqsakw6-clang-tools-22.1.8/bin/clang-tidy
|
||||
✅ clang-tidy-22
|
||||
LLVM version 22.1.8
|
||||
/nix/store/0mmdxscn11irlmm7jvp1c0a32712s026-clang-tidy-22/bin/clang-tidy-22
|
||||
/nix/store/2b7hn8hyp02yfarh8c40c4jsjkm789l0-clang-tidy-22/bin/clang-tidy-22
|
||||
✅ dot
|
||||
dot - graphviz version 15.1.1 (0)
|
||||
/nix/store/vz4zb8d1rfigzzdn3i0hd4kff28vk02l-graphviz-15.1.1/bin/dot
|
||||
@@ -171,4 +174,4 @@ Mold:
|
||||
|
||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||
|
||||
✅ All 53 checked tools are present and runnable.
|
||||
✅ All 54 checked tools are present and runnable.
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (import ./packages.nix { inherit pkgs; }) commonPackages;
|
||||
inherit (import ./packages.nix { inherit pkgs customGlibc; }) commonPackages;
|
||||
|
||||
# Each forces something absent on the other platform, so both stay lazy.
|
||||
linux = import ./linux.nix { inherit pkgs customGlibc; };
|
||||
|
||||
+14
-12
@@ -31,6 +31,9 @@ let
|
||||
};
|
||||
customGccGcov = if pkgs.stdenv.hostPlatform.isLinux then linux.gcov else plainGcov;
|
||||
|
||||
# commonPackages whose clang tools parse with the custom toolchain's headers.
|
||||
customCommonPackages = (import ./packages.nix { inherit pkgs customGlibc; }).commonPackages;
|
||||
|
||||
# Whole directory: init.sh locates the profiles relative to itself.
|
||||
conanDir = ../conan;
|
||||
|
||||
@@ -86,11 +89,11 @@ let
|
||||
version ? null,
|
||||
versionedTools ? [ ],
|
||||
extraPackages ? [ ],
|
||||
warningHook ? "",
|
||||
# Opt out of PatchNixBinary.cmake retargeting binaries to the system
|
||||
# loader. The plain toolchain links a newer glibc, so it must not be
|
||||
# patched; the custom toolchain patches by default.
|
||||
noPatchNixBinary ? false,
|
||||
# The stock nixpkgs toolchain: warn that it doesn't match CI, keep the
|
||||
# clang tools off the custom toolchain, and opt out of PatchNixBinary.cmake
|
||||
# retargeting binaries to the system loader (the plain toolchain links a
|
||||
# newer glibc, so it must not be patched).
|
||||
plain ? false,
|
||||
}:
|
||||
let
|
||||
compilerVersionHook =
|
||||
@@ -110,7 +113,8 @@ let
|
||||
in
|
||||
(pkgs.mkShell.override { inherit stdenv; }) (
|
||||
{
|
||||
packages = commonPackages ++ versionedLinks ++ extraPackages;
|
||||
packages =
|
||||
(if plain then commonPackages else customCommonPackages) ++ versionedLinks ++ extraPackages;
|
||||
# Marks a managed dev shell, so the build (XrplSanity.cmake) can tell an
|
||||
# intentional Nix toolchain from one leaked into a bare shell.
|
||||
XRPL_DEVSHELL = shellName;
|
||||
@@ -119,10 +123,10 @@ let
|
||||
${compilerVersionHook}
|
||||
${darwinLibresolvHook}
|
||||
${conanHook}
|
||||
${warningHook}
|
||||
${pkgs.lib.optionalString plain plainWarningHook}
|
||||
'';
|
||||
}
|
||||
// pkgs.lib.optionalAttrs noPatchNixBinary { XRPLD_NO_PATCH_NIX_BINARY = "1"; }
|
||||
// pkgs.lib.optionalAttrs plain { XRPLD_NO_PATCH_NIX_BINARY = "1"; }
|
||||
);
|
||||
in
|
||||
rec {
|
||||
@@ -180,8 +184,7 @@ rec {
|
||||
version = gccVersion;
|
||||
versionedTools = gccVersionedTools;
|
||||
extraPackages = [ plainGcov ];
|
||||
warningHook = plainWarningHook;
|
||||
noPatchNixBinary = true;
|
||||
plain = true;
|
||||
};
|
||||
|
||||
clang-plain = makeShell {
|
||||
@@ -190,7 +193,6 @@ rec {
|
||||
compilerName = "clang";
|
||||
version = llvmVersion;
|
||||
versionedTools = clangVersionedTools;
|
||||
warningHook = plainWarningHook;
|
||||
noPatchNixBinary = true;
|
||||
plain = true;
|
||||
};
|
||||
}
|
||||
@@ -146,6 +146,7 @@ in
|
||||
})
|
||||
];
|
||||
|
||||
clang = customClang;
|
||||
gccStdenv = customStdenv;
|
||||
clangStdenv = pkgs.stdenvAdapters.overrideCC pkgs.stdenv customClang;
|
||||
gcov = customGcov;
|
||||
|
||||
+38
-7
@@ -1,14 +1,28 @@
|
||||
{ pkgs }:
|
||||
{
|
||||
pkgs,
|
||||
# With the custom glibc, the clang tools (clang-tidy, ...) parse code with the
|
||||
# Linux custom toolchain's headers, i.e. the same glibc and libstdc++ as the
|
||||
# build. Without it, they use the nixpkgs default compiler's.
|
||||
customGlibc ? null,
|
||||
}:
|
||||
let
|
||||
# Compiler versions used across the dev shell and the CI environment.
|
||||
# Docs link here and scripts read the version off the tools;
|
||||
# only the clang-format rev in .pre-commit-config.yaml
|
||||
# has to be bumped alongside llvmVersion.
|
||||
gccVersion = 15;
|
||||
llvmVersion = 22;
|
||||
llvmVersion = 23;
|
||||
|
||||
gccPackage = pkgs."gcc${toString gccVersion}";
|
||||
llvmPackages = pkgs."llvmPackages_${toString llvmVersion}";
|
||||
|
||||
# Bound explicitly so it tracks llvmPackages above, not the `with pkgs` default.
|
||||
clangTools = llvmPackages.clang-tools;
|
||||
# isLinux first: darwin must not evaluate the custom glibc.
|
||||
clangTools = llvmPackages.clang-tools.override (
|
||||
pkgs.lib.optionalAttrs (pkgs.stdenv.hostPlatform.isLinux && customGlibc != null) {
|
||||
inherit (import ./linux.nix { inherit pkgs customGlibc; }) clang;
|
||||
}
|
||||
);
|
||||
|
||||
# In LLVM 22, run-clang-tidy.py moved from share/clang/ to bin/, so nixpkgs
|
||||
# clang-tools no longer links it. Wrap it manually.
|
||||
@@ -16,6 +30,21 @@ let
|
||||
exec ${pkgs.python3}/bin/python3 ${llvmPackages.clang-unwrapped}/bin/run-clang-tidy "$@"
|
||||
'';
|
||||
|
||||
# Conan 2.33 is the first release whose settings.yml accepts clang 23.
|
||||
# TODO: drop once nixpkgs ships it.
|
||||
conan = pkgs.conan.overridePythonAttrs (old: {
|
||||
version = "2.33.0";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "conan-io";
|
||||
repo = "conan";
|
||||
tag = "2.33.0";
|
||||
hash = "sha256-FDJjesqvPiAUAEjeqQt088Vnet/KXHbUyEPytP9MyG4=";
|
||||
};
|
||||
dependencies = old.dependencies ++ [ pkgs.python3Packages.truststore ];
|
||||
# The upstream test suite takes long and needs network access.
|
||||
doCheck = false;
|
||||
});
|
||||
|
||||
rust = import ./rust.nix { inherit pkgs; };
|
||||
|
||||
# Nix wraps its toolchain so that binaries are exposed only under unsuffixed
|
||||
@@ -25,10 +54,11 @@ let
|
||||
# - Conan's Boost recipe looks up `g++-<major>` before plain `g++`.
|
||||
# - bin/pre-commit/clang_tidy_check.py looks up `run-clang-tidy-<v>` and
|
||||
# `clang-apply-replacements-<v>` before the unsuffixed names.
|
||||
# On a host that also has the matching system binary (e.g. Ubuntu's
|
||||
# `/usr/bin/g++-15` or `clang-tidy-22`) the probe escapes Nix and mixes a
|
||||
# system tool into the Nix environment. Generate version-suffixed symlinks
|
||||
# next to a package's tools so those probes resolve to the Nix ones.
|
||||
# On a host that also has the matching system binary
|
||||
# (e.g. Ubuntu's `/usr/bin/g++-<v>` or `clang-tidy-<v>`),
|
||||
# the probe escapes Nix and mixes a system tool into the Nix environment.
|
||||
# Generate version-suffixed symlinks next to a package's tools
|
||||
# so those probes resolve to the Nix ones.
|
||||
#
|
||||
# Compiler links must point at whichever compiler is active in a given
|
||||
# environment (the plain stdenv compiler in the dev shell, the custom-glibc
|
||||
@@ -112,6 +142,7 @@ in
|
||||
gnumake
|
||||
gnupg # needed for signing commits & codecov/codecov-action
|
||||
graphviz
|
||||
jq
|
||||
less # needed for git diff
|
||||
mold
|
||||
nettools # provides netstat, used to debug failures in CI
|
||||
|
||||
@@ -26,6 +26,14 @@ namespace xrpl {
|
||||
|
||||
namespace directory {
|
||||
|
||||
struct Gap
|
||||
{
|
||||
uint64_t const page;
|
||||
SLE::pointer node;
|
||||
uint64_t const nextPage;
|
||||
SLE::pointer next;
|
||||
};
|
||||
|
||||
std::uint64_t
|
||||
createRoot(
|
||||
ApplyView& view,
|
||||
@@ -126,7 +134,9 @@ insertPage(
|
||||
if (page == 0)
|
||||
return std::nullopt;
|
||||
if (!view.rules().enabled(fixDirectoryLimit) && page >= kDirNodeMaxPages) // Old pages limit
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// We are about to create a new node; we'll link it to
|
||||
// the chain first:
|
||||
@@ -147,12 +157,8 @@ insertPage(
|
||||
// Save some space by not specifying the value 0 since it's the default.
|
||||
if (page != 1)
|
||||
node->setFieldU64(sfIndexPrevious, page - 1);
|
||||
XRPL_ASSERT_PARTS(!nextPage, "xrpl::directory::insertPage", "nextPage has default value");
|
||||
/* Reserved for future use when directory pages may be inserted in
|
||||
* between two other pages instead of only at the end of the chain.
|
||||
if (nextPage)
|
||||
node->setFieldU64(sfIndexNext, nextPage);
|
||||
*/
|
||||
describe(node);
|
||||
view.insert(node);
|
||||
|
||||
@@ -168,7 +174,7 @@ ApplyView::dirAdd(
|
||||
UInt256 const& key,
|
||||
std::function<void(SLE::Ref)> const& describe)
|
||||
{
|
||||
auto root = peek(directory);
|
||||
auto const root = peek(directory);
|
||||
|
||||
if (!root)
|
||||
{
|
||||
@@ -178,6 +184,43 @@ ApplyView::dirAdd(
|
||||
|
||||
auto [page, node, indexes] = directory::findPreviousPage(*this, directory, root);
|
||||
|
||||
if (rules().enabled(featureDefragDirectories))
|
||||
{
|
||||
// If there are more nodes than just the root, and there's no space in
|
||||
// the last one, walk backwards to find one with space, or to find one
|
||||
// missing.
|
||||
std::optional<directory::Gap> gapPages;
|
||||
while (page && indexes.size() >= kDIR_NODE_MAX_PAGES)
|
||||
{
|
||||
// Find a page with space, or a gap in pages.
|
||||
auto [prevPage, prevNode, prevIndexes] =
|
||||
directory::findPreviousPage(*this, directory, node);
|
||||
if (!gapPages && prevPage != page - 1)
|
||||
gapPages.emplace(prevPage, prevNode, page, node);
|
||||
page = prevPage;
|
||||
node = prevNode;
|
||||
indexes = prevIndexes;
|
||||
}
|
||||
// We looped through all the pages back to the root.
|
||||
if (!page)
|
||||
{
|
||||
// If we found a gap, use it.
|
||||
if (gapPages)
|
||||
{
|
||||
return directory::insertPage(
|
||||
*this,
|
||||
gapPages->page,
|
||||
gapPages->node,
|
||||
gapPages->nextPage,
|
||||
gapPages->next,
|
||||
key,
|
||||
directory,
|
||||
describe);
|
||||
}
|
||||
std::tie(page, node, indexes) = directory::findPreviousPage(*this, directory, root);
|
||||
}
|
||||
}
|
||||
|
||||
// If there's space, we use it:
|
||||
if (indexes.size() < kDirNodeMaxEntries)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <xrpl/ledger/LedgerTiming.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
|
||||
#include <xrpl/ledger/entries/NegativeUNLEntry.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
@@ -640,7 +641,7 @@ HashSet<PublicKey>
|
||||
Ledger::negativeUNL() const
|
||||
{
|
||||
HashSet<PublicKey> negUnl;
|
||||
if (auto sle = read(keylet::negativeUNL()); sle && sle->isFieldPresent(sfDisabledValidators))
|
||||
if (auto const sle = NegativeUNLEntryR(*this); sle && sle->isFieldPresent(sfDisabledValidators))
|
||||
{
|
||||
auto const& nUnlData = sle->getFieldArray(sfDisabledValidators);
|
||||
for (auto const& n : nUnlData)
|
||||
@@ -664,7 +665,7 @@ Ledger::negativeUNL() const
|
||||
std::optional<PublicKey>
|
||||
Ledger::validatorToDisable() const
|
||||
{
|
||||
if (auto sle = read(keylet::negativeUNL()); sle && sle->isFieldPresent(sfValidatorToDisable))
|
||||
if (auto const sle = NegativeUNLEntryR(*this); sle && sle->isFieldPresent(sfValidatorToDisable))
|
||||
{
|
||||
auto d = sle->getFieldVL(sfValidatorToDisable);
|
||||
auto s = makeSlice(d);
|
||||
@@ -678,7 +679,8 @@ Ledger::validatorToDisable() const
|
||||
std::optional<PublicKey>
|
||||
Ledger::validatorToReEnable() const
|
||||
{
|
||||
if (auto sle = read(keylet::negativeUNL()); sle && sle->isFieldPresent(sfValidatorToReEnable))
|
||||
if (auto const sle = NegativeUNLEntryR(*this);
|
||||
sle && sle->isFieldPresent(sfValidatorToReEnable))
|
||||
{
|
||||
auto d = sle->getFieldVL(sfValidatorToReEnable);
|
||||
auto s = makeSlice(d);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/hash/uhash.h>
|
||||
#include <xrpl/ledger/detail/ReadViewFwdRange.h>
|
||||
#include <xrpl/ledger/entries/AmendmentsEntry.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
@@ -72,9 +73,8 @@ makeRulesGivenLedger(
|
||||
std::optional const digest = ledger.digest(k.key);
|
||||
if (digest)
|
||||
{
|
||||
auto const sle = ledger.read(k);
|
||||
if (sle)
|
||||
return Rules(presets, digest, sle->getFieldV256(sfAmendments));
|
||||
if (AmendmentsEntryR const entry(k, ledger); entry)
|
||||
return Rules(presets, digest, entry->getFieldV256(sfAmendments));
|
||||
}
|
||||
return Rules(presets);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AmendmentsEntry.h>
|
||||
#include <xrpl/ledger/entries/LedgerHashesEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
@@ -309,40 +310,13 @@ areCompatible(
|
||||
std::set<UInt256>
|
||||
getEnabledAmendments(ReadView const& view)
|
||||
{
|
||||
std::set<UInt256> amendments;
|
||||
|
||||
if (auto const sle = view.read(keylet::amendments()))
|
||||
{
|
||||
if (sle->isFieldPresent(sfAmendments))
|
||||
{
|
||||
auto const& v = sle->getFieldV256(sfAmendments);
|
||||
amendments.insert(v.begin(), v.end());
|
||||
}
|
||||
}
|
||||
|
||||
return amendments;
|
||||
return AmendmentsEntryR(view).enabledAmendments();
|
||||
}
|
||||
|
||||
MajorityAmendmentsT
|
||||
getMajorityAmendments(ReadView const& view)
|
||||
{
|
||||
MajorityAmendmentsT ret;
|
||||
|
||||
if (auto const sle = view.read(keylet::amendments()))
|
||||
{
|
||||
if (sle->isFieldPresent(sfMajorities))
|
||||
{
|
||||
using Tp = NetClock::time_point;
|
||||
using D = Tp::duration;
|
||||
|
||||
auto const majorities = sle->getFieldArray(sfMajorities);
|
||||
|
||||
for (auto const& m : majorities)
|
||||
ret[m.getFieldH256(sfAmendment)] = Tp(D(m.getFieldU32(sfCloseTime)));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return AmendmentsEntryR(view).majorityAmendments();
|
||||
}
|
||||
|
||||
std::optional<UInt256>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/MPTAmount.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
@@ -100,28 +99,6 @@ AMMLiquidity<TIn, TOut>::generateFibSeqOffer(TAmounts<TIn, TOut> const& balances
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
constexpr T
|
||||
maxAmount()
|
||||
{
|
||||
if constexpr (std::is_same_v<T, XRPAmount>)
|
||||
{
|
||||
return XRPAmount(STAmount::kMaxNative);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, IOUAmount>)
|
||||
{
|
||||
return IOUAmount(STAmount::kMaxValue / 2, STAmount::kMaxOffset);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, STAmount>)
|
||||
{
|
||||
return STAmount(STAmount::kMaxValue / 2, STAmount::kMaxOffset);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, MPTAmount>)
|
||||
{
|
||||
return MPTAmount(kMaxMpTokenAmount);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
maxOut(T const& out, Asset const& asset)
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/CheckEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -30,7 +32,7 @@ CheckCancel::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
CheckCancel::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID]));
|
||||
CheckEntryR const sleCheck(ctx.tx[sfCheckID], ctx.view);
|
||||
if (!sleCheck)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Check does not exist.";
|
||||
@@ -59,7 +61,7 @@ CheckCancel::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
CheckCancel::doApply()
|
||||
{
|
||||
auto const sleCheck = view().peek(keylet::check(ctx_.tx[sfCheckID]));
|
||||
CheckEntryW sleCheck(ctx_.tx[sfCheckID], view(), j_);
|
||||
if (!sleCheck)
|
||||
{
|
||||
// Error should have been caught in preclaim.
|
||||
@@ -96,10 +98,10 @@ CheckCancel::doApply()
|
||||
}
|
||||
|
||||
// If we succeeded, update the check owner's reserve.
|
||||
decreaseOwnerCountForObject(view(), srcId, sleCheck, 1, viewJ);
|
||||
decreaseOwnerCountForObject(view(), srcId, sleCheck.mutableRawSle(), 1, viewJ);
|
||||
|
||||
// Remove check from ledger.
|
||||
view().erase(sleCheck);
|
||||
sleCheck.erase();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/CheckEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -87,7 +88,7 @@ CheckCash::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID]));
|
||||
CheckEntryR const sleCheck(ctx.tx[sfCheckID], ctx.view);
|
||||
if (!sleCheck)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Check does not exist.";
|
||||
@@ -296,8 +297,9 @@ CheckCash::doApply()
|
||||
// directly on a View.
|
||||
PaymentSandbox psb(&ctx_.view());
|
||||
|
||||
auto sleCheck = psb.peek(keylet::check(ctx_.tx[sfCheckID]));
|
||||
if (!sleCheck)
|
||||
std::optional<CheckEntryW> sleCheck;
|
||||
sleCheck.emplace(ctx_.tx[sfCheckID], psb, j_);
|
||||
if (!*sleCheck)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Precheck did not verify check's existence.";
|
||||
@@ -305,7 +307,7 @@ CheckCash::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
AccountID const srcId{sleCheck->getAccountID(sfAccount)};
|
||||
AccountID const srcId{(*sleCheck)->getAccountID(sfAccount)};
|
||||
if (!psb.exists(keylet::account(srcId)) || !psb.exists(keylet::account(accountID_)))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -314,7 +316,7 @@ CheckCash::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sponsorCheckSle = getLedgerEntryReserveSponsor(psb, sleCheck);
|
||||
auto const sponsorCheckSle = getLedgerEntryReserveSponsor(psb, sleCheck->rawSle());
|
||||
|
||||
// Preclaim already checked that source has at least the requested
|
||||
// funds.
|
||||
@@ -331,7 +333,7 @@ CheckCash::doApply()
|
||||
|
||||
if (srcId != accountID_)
|
||||
{
|
||||
STAmount const sendMax = sleCheck->at(sfSendMax);
|
||||
STAmount const sendMax = (*sleCheck)->at(sfSendMax);
|
||||
|
||||
// Flow() doesn't do XRP to XRP transfers.
|
||||
if (sendMax.native())
|
||||
@@ -569,7 +571,7 @@ CheckCash::doApply()
|
||||
true, // owner pays transfer fee
|
||||
OfferCrossing::No,
|
||||
std::nullopt,
|
||||
sleCheck->getFieldAmount(sfSendMax),
|
||||
(*sleCheck)->getFieldAmount(sfSendMax),
|
||||
std::nullopt, // check does not support domain
|
||||
viewJ);
|
||||
|
||||
@@ -594,7 +596,7 @@ CheckCash::doApply()
|
||||
// for DeliverMin.
|
||||
ctx_.deliver(result.actualAmountOut);
|
||||
|
||||
sleCheck = psb.peek(keylet::check(ctx_.tx[sfCheckID]));
|
||||
sleCheck.emplace(ctx_.tx[sfCheckID], psb, j_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +604,10 @@ CheckCash::doApply()
|
||||
// check link from destination directory.
|
||||
if (srcId != accountID_ &&
|
||||
!psb.dirRemove(
|
||||
keylet::ownerDir(accountID_), sleCheck->at(sfDestinationNode), sleCheck->key(), true))
|
||||
keylet::ownerDir(accountID_),
|
||||
(*sleCheck)->at(sfDestinationNode),
|
||||
(*sleCheck)->key(),
|
||||
true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete check from destination.";
|
||||
@@ -611,7 +616,8 @@ CheckCash::doApply()
|
||||
}
|
||||
|
||||
// Remove check from check owner's directory.
|
||||
if (!psb.dirRemove(keylet::ownerDir(srcId), sleCheck->at(sfOwnerNode), sleCheck->key(), true))
|
||||
if (!psb.dirRemove(
|
||||
keylet::ownerDir(srcId), (*sleCheck)->at(sfOwnerNode), (*sleCheck)->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete check from owner.";
|
||||
@@ -620,10 +626,10 @@ CheckCash::doApply()
|
||||
}
|
||||
|
||||
// If we succeeded, update the check owner's reserve.
|
||||
decreaseOwnerCountForObject(psb, srcId, sleCheck, 1, viewJ);
|
||||
decreaseOwnerCountForObject(psb, srcId, sleCheck->mutableRawSle(), 1, viewJ);
|
||||
|
||||
// Remove check from ledger.
|
||||
psb.erase(sleCheck);
|
||||
sleCheck->erase();
|
||||
|
||||
psb.apply(ctx_.rawView());
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/CheckEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
@@ -25,7 +26,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -201,8 +201,9 @@ CheckCreate::doApply()
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// Check sequence. For more explanation see comments in SeqProxy.h.
|
||||
auto const seq = ctx_.tx.getSeqProxy();
|
||||
Keylet const checkKeylet = keylet::check(accountID_, seq);
|
||||
auto sleCheck = std::make_shared<SLE>(checkKeylet);
|
||||
CheckEntryW sleCheck(accountID_, seq, view(), j_);
|
||||
Keylet const checkKeylet = sleCheck.keylet();
|
||||
sleCheck.newSLE();
|
||||
|
||||
sleCheck->setAccountID(sfAccount, accountID_);
|
||||
AccountID const dstAccountId = ctx_.tx[sfDestination];
|
||||
@@ -218,7 +219,7 @@ CheckCreate::doApply()
|
||||
if (auto const expiry = ctx_.tx[~sfExpiration])
|
||||
sleCheck->setFieldU32(sfExpiration, *expiry);
|
||||
|
||||
view().insert(sleCheck);
|
||||
sleCheck.insert();
|
||||
|
||||
auto viewJ = ctx_.registry.get().getJournal("View");
|
||||
// If it's not a self-send (and it shouldn't be), add Check to the
|
||||
@@ -252,7 +253,7 @@ CheckCreate::doApply()
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, viewJ);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck.mutableRawSle());
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,10 @@
|
||||
#include <xrpl/ledger/entries/EscrowEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
@@ -35,60 +30,6 @@ EscrowCancel::preflight(PreflightContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
static TER
|
||||
escrowCancelPreclaimHelper(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& account,
|
||||
STAmount const& amount);
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowCancelPreclaimHelper<Issue>(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& account,
|
||||
STAmount const& amount)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecINTERNAL
|
||||
if (issuer == account)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(ctx.view, amount.get<Issue>(), account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowCancelPreclaimHelper<MPTIssue>(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& account,
|
||||
STAmount const& amount)
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecINTERNAL
|
||||
if (issuer == account)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
EscrowCancel::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
@@ -106,7 +47,7 @@ EscrowCancel::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowCancelPreclaimHelper<T>(ctx, account, amount);
|
||||
return escrowUnlockPreclaimHelper<T>(ctx.view, account, amount, false);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include <xrpl/ledger/entries/EscrowEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -19,6 +18,7 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTAmount.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
@@ -185,162 +185,6 @@ EscrowCreate::preflight(PreflightContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
static TER
|
||||
escrowCreatePreclaimHelper(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount);
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowCreatePreclaimHelper<Issue>(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = ctx.view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
STAmount const balance = (*sleRippleState)[sfBalance];
|
||||
|
||||
// If balance is positive, issuer must have higher address than account
|
||||
if (balance > beast::kZero && issuer < account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If balance is negative, issuer must have lower address than account
|
||||
if (balance < beast::kZero && issuer > account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(ctx.view, issue, account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(ctx.view, issue, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecFROZEN
|
||||
if (isFrozen(ctx.view, account, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
// If the issuer has frozen the destination, return tecFROZEN
|
||||
if (isFrozen(ctx.view, dest, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
ctx.view, account, issue.currency, issuer, FreezeHandling::IgnoreFreeze, ctx.j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the amount is not addable to the balance, return tecPRECISION_LOSS
|
||||
if (!canAdd(spendableAmount, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowCreatePreclaimHelper<MPTIssue>(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount)
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
|
||||
if (!sleIssuance->isFlag(lsfMPTCanEscrow))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the issuer is not the same as the issuer of the mpt, return
|
||||
// tecNO_PERMISSION
|
||||
if (sleIssuance->getAccountID(sfIssuer) != issuer)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account does not have the mpt, return tecOBJECT_NOT_FOUND
|
||||
if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, account)))
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (isFrozen(ctx.view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the issuer has frozen the destination, return tecLOCKED
|
||||
if (isFrozen(ctx.view, dest, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the mpt cannot be transferred, return tecNO_AUTH
|
||||
if (auto const ter = canTransfer(ctx.view, mptIssue, account, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
ctx.view,
|
||||
account,
|
||||
amount.get<MPTIssue>(),
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
AuthHandling::IgnoreAuth,
|
||||
ctx.j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
EscrowCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
@@ -366,7 +210,7 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowCreatePreclaimHelper<T>(ctx, account, dest, amount);
|
||||
return escrowLockPreclaimHelper<T>(ctx.view, account, dest, amount, ctx.j);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
@@ -375,54 +219,6 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
static TER
|
||||
escrowLockApplyHelper(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowLockApplyHelper<Issue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter =
|
||||
directSendNoFee(view, sender, issuer, amount, !amount.holds<MPTIssue>(), journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowLockApplyHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter = lockEscrowMPT(view, sender, amount, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
EscrowCreate::doApply()
|
||||
{
|
||||
|
||||
@@ -13,15 +13,10 @@
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
@@ -137,68 +132,6 @@ EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
static TER
|
||||
escrowFinishPreclaimHelper(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount);
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowFinishPreclaimHelper<Issue>(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == dest)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(ctx.view, amount.get<Issue>(), dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has deep frozen the destination, return tecFROZEN
|
||||
if (isDeepFrozen(ctx.view, dest, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowFinishPreclaimHelper<MPTIssue>(
|
||||
PreclaimContext const& ctx,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the dest, return tesSUCCESS
|
||||
if (issuer == dest)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the destination, return tecLOCKED
|
||||
if (isFrozen(ctx.view, dest, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
EscrowFinish::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
@@ -223,7 +156,7 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
|
||||
return escrowUnlockPreclaimHelper<T>(ctx.view, dest, amount);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/AmendmentTable.h>
|
||||
#include <xrpl/ledger/entries/AmendmentsEntry.h>
|
||||
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
|
||||
#include <xrpl/ledger/entries/NegativeUNLEntry.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -26,7 +27,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -169,14 +169,12 @@ Change::applyAmendment()
|
||||
{
|
||||
UInt256 const amendment(ctx_.tx.getFieldH256(sfAmendment));
|
||||
|
||||
auto const k = keylet::amendments();
|
||||
|
||||
SLE::pointer amendmentObject = view().peek(k);
|
||||
AmendmentsEntryW amendmentObject(view(), j_);
|
||||
|
||||
if (!amendmentObject)
|
||||
{
|
||||
amendmentObject = std::make_shared<SLE>(k);
|
||||
view().insert(amendmentObject);
|
||||
amendmentObject.newSLE();
|
||||
amendmentObject.insert();
|
||||
}
|
||||
|
||||
STVector256 amendments = amendmentObject->getFieldV256(sfAmendments);
|
||||
@@ -253,7 +251,7 @@ Change::applyAmendment()
|
||||
amendmentObject->setFieldArray(sfMajorities, newMajorities);
|
||||
}
|
||||
|
||||
view().update(amendmentObject);
|
||||
amendmentObject.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -331,12 +329,11 @@ Change::applyUNLModify()
|
||||
JLOG(j_.info()) << "N-UNL: applyUNLModify, " << (disabling ? "ToDisable" : "ToReEnable")
|
||||
<< " seq=" << seq << " validator data:" << strHex(validator);
|
||||
|
||||
auto const k = keylet::negativeUNL();
|
||||
SLE::pointer negUnlObject = view().peek(k);
|
||||
NegativeUNLEntryW negUnlObject(view(), j_);
|
||||
if (!negUnlObject)
|
||||
{
|
||||
negUnlObject = std::make_shared<SLE>(k);
|
||||
view().insert(negUnlObject);
|
||||
negUnlObject.newSLE();
|
||||
negUnlObject.insert();
|
||||
}
|
||||
|
||||
bool const found = [&] {
|
||||
@@ -409,7 +406,7 @@ Change::applyUNLModify()
|
||||
negUnlObject->setFieldVL(sfValidatorToReEnable, validator);
|
||||
}
|
||||
|
||||
view().update(negUnlObject);
|
||||
negUnlObject.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <test/jtx/offer.h>
|
||||
#include <test/jtx/paths.h>
|
||||
#include <test/jtx/pay.h>
|
||||
#include <test/jtx/paychan.h>
|
||||
#include <test/jtx/rate.h>
|
||||
#include <test/jtx/sendmax.h>
|
||||
#include <test/jtx/seq.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <test/jtx/offer.h>
|
||||
#include <test/jtx/paths.h>
|
||||
#include <test/jtx/pay.h>
|
||||
#include <test/jtx/paychan.h>
|
||||
#include <test/jtx/rate.h>
|
||||
#include <test/jtx/sendmax.h>
|
||||
#include <test/jtx/seq.h>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/TestHelpers.h>
|
||||
#include <test/jtx/amount.h>
|
||||
#include <test/jtx/balance.h> // IWYU pragma: keep
|
||||
#include <test/jtx/escrow.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/Dir.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -46,52 +45,6 @@ namespace xrpl::test {
|
||||
|
||||
struct EscrowToken_test : public beast::unit_test::Suite
|
||||
{
|
||||
static uint64_t
|
||||
mptEscrowed(jtx::Env const& env, jtx::Account const& account, jtx::MPT const& mpt)
|
||||
{
|
||||
auto const sle = env.le(keylet::mptoken(mpt.mpt(), account));
|
||||
if (sle && sle->isFieldPresent(sfLockedAmount))
|
||||
return (*sle)[sfLockedAmount];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
issuerMPTEscrowed(jtx::Env const& env, jtx::MPT const& mpt)
|
||||
{
|
||||
auto const sle = env.le(keylet::mptokenIssuance(mpt.mpt()));
|
||||
if (sle && sle->isFieldPresent(sfLockedAmount))
|
||||
return (*sle)[sfLockedAmount];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static jtx::PrettyAmount
|
||||
issuerBalance(jtx::Env& env, jtx::Account const& account, Issue const& issue)
|
||||
{
|
||||
json::Value params;
|
||||
params[jss::account] = account.human();
|
||||
auto jrr = env.rpc("json", "gateway_balances", to_string(params));
|
||||
auto const result = jrr[jss::result];
|
||||
auto const obligations = result[jss::obligations][to_string(issue.currency)];
|
||||
if (obligations.isNull())
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
STAmount const amount = amountFromString(issue, obligations.asString());
|
||||
return {amount, account.name()};
|
||||
}
|
||||
|
||||
static jtx::PrettyAmount
|
||||
issuerEscrowed(jtx::Env& env, jtx::Account const& account, Issue const& issue)
|
||||
{
|
||||
json::Value params;
|
||||
params[jss::account] = account.human();
|
||||
auto jrr = env.rpc("json", "gateway_balances", to_string(params));
|
||||
auto const result = jrr[jss::result];
|
||||
auto const locked = result[jss::locked][to_string(issue.currency)];
|
||||
if (locked.isNull())
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
STAmount const amount = amountFromString(issue, locked.asString());
|
||||
return {amount, account.name()};
|
||||
}
|
||||
|
||||
void
|
||||
testIOUEnablement(FeatureBitset features)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <test/jtx/deposit.h>
|
||||
#include <test/jtx/fee.h>
|
||||
#include <test/jtx/flags.h>
|
||||
#include <test/jtx/paychan.h>
|
||||
#include <test/jtx/ter.h>
|
||||
#include <test/jtx/ticket.h>
|
||||
#include <test/jtx/txflags.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/handlers/Handlers.h>
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
@@ -35,13 +35,10 @@
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PayChan.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -77,27 +74,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
return {k.key, view.read(k)};
|
||||
}
|
||||
|
||||
static Buffer
|
||||
signClaimAuth(
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk,
|
||||
UInt256 const& channel,
|
||||
STAmount const& authAmt)
|
||||
{
|
||||
Serializer msg;
|
||||
serializePayChanAuthorization(msg, channel, authAmt.xrp());
|
||||
return sign(pk, sk, msg.slice());
|
||||
}
|
||||
|
||||
static STAmount
|
||||
channelAmount(ReadView const& view, UInt256 const& chan)
|
||||
{
|
||||
auto const slep = view.read({ltPAYCHAN, chan});
|
||||
if (!slep)
|
||||
return XRPAmount{-1};
|
||||
return (*slep)[sfAmount];
|
||||
}
|
||||
|
||||
static std::optional<std::int64_t>
|
||||
channelExpiration(ReadView const& view, UInt256 const& chan)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <test/jtx/offer.h>
|
||||
#include <test/jtx/paths.h>
|
||||
#include <test/jtx/pay.h>
|
||||
#include <test/jtx/paychan.h>
|
||||
#include <test/jtx/sendmax.h>
|
||||
#include <test/jtx/seq.h>
|
||||
#include <test/jtx/sig.h>
|
||||
|
||||
+15
-61
@@ -19,8 +19,8 @@
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/PathAsset.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
@@ -367,6 +367,20 @@ checkArraySize(json::Value const& val, unsigned int size);
|
||||
std::uint32_t
|
||||
ownerCount(test::jtx::Env const& env, test::jtx::Account const& account);
|
||||
|
||||
/* Token (IOU/MPT) Locking */
|
||||
/******************************************************************************/
|
||||
uint64_t
|
||||
mptEscrowed(jtx::Env const& env, jtx::Account const& account, jtx::MPT const& mpt);
|
||||
|
||||
uint64_t
|
||||
issuerMPTEscrowed(jtx::Env const& env, jtx::MPT const& mpt);
|
||||
|
||||
jtx::PrettyAmount
|
||||
issuerBalance(jtx::Env& env, jtx::Account const& account, Issue const& issue);
|
||||
|
||||
jtx::PrettyAmount
|
||||
issuerEscrowed(jtx::Env& env, jtx::Account const& account, Issue const& issue);
|
||||
|
||||
// Helper function that returns the sponsored owner count on an account.
|
||||
std::uint32_t
|
||||
sponsoredOwnerCount(test::jtx::Env const& env, test::jtx::Account const& account);
|
||||
@@ -583,66 +597,6 @@ accountBalance(Env& env, Account const& acct);
|
||||
[[nodiscard]] bool
|
||||
expectLedgerEntryRoot(Env& env, Account const& acct, STAmount const& expectedValue);
|
||||
|
||||
/* Payment Channel */
|
||||
/******************************************************************************/
|
||||
namespace paychan {
|
||||
|
||||
json::Value
|
||||
create(
|
||||
AccountID const& account,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
NetClock::duration const& settleDelay,
|
||||
PublicKey const& pk,
|
||||
std::optional<NetClock::time_point> const& cancelAfter = std::nullopt,
|
||||
std::optional<std::uint32_t> const& dstTag = std::nullopt);
|
||||
|
||||
inline json::Value
|
||||
create(
|
||||
Account const& account,
|
||||
Account const& to,
|
||||
STAmount const& amount,
|
||||
NetClock::duration const& settleDelay,
|
||||
PublicKey const& pk,
|
||||
std::optional<NetClock::time_point> const& cancelAfter = std::nullopt,
|
||||
std::optional<std::uint32_t> const& dstTag = std::nullopt)
|
||||
{
|
||||
return create(account.id(), to.id(), amount, settleDelay, pk, cancelAfter, dstTag);
|
||||
}
|
||||
|
||||
json::Value
|
||||
fund(
|
||||
AccountID const& account,
|
||||
UInt256 const& channel,
|
||||
STAmount const& amount,
|
||||
std::optional<NetClock::time_point> const& expiration = std::nullopt);
|
||||
|
||||
json::Value
|
||||
claim(
|
||||
AccountID const& account,
|
||||
UInt256 const& channel,
|
||||
std::optional<STAmount> const& balance = std::nullopt,
|
||||
std::optional<STAmount> const& amount = std::nullopt,
|
||||
std::optional<Slice> const& signature = std::nullopt,
|
||||
std::optional<PublicKey> const& pk = std::nullopt);
|
||||
|
||||
UInt256
|
||||
channel(AccountID const& account, AccountID const& dst, std::uint32_t seqProxyValue);
|
||||
|
||||
inline UInt256
|
||||
channel(Account const& account, Account const& dst, std::uint32_t seqProxyValue)
|
||||
{
|
||||
return channel(account.id(), dst.id(), seqProxyValue);
|
||||
}
|
||||
|
||||
STAmount
|
||||
channelBalance(ReadView const& view, UInt256 const& chan);
|
||||
|
||||
bool
|
||||
channelExists(ReadView const& view, UInt256 const& chan);
|
||||
|
||||
} // namespace paychan
|
||||
|
||||
/* Crossing Limits */
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
@@ -16,18 +16,15 @@
|
||||
#include <xrpld/rpc/Role.h>
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/Job.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
@@ -37,13 +34,11 @@
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/PathAsset.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/STPathSet.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -53,7 +48,6 @@
|
||||
#include <xrpl/tx/paths/detail/Steps.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -95,6 +89,54 @@ ownerCount(Env const& env, Account const& account)
|
||||
return env.ownerCount(account);
|
||||
}
|
||||
|
||||
/* Token (IOU/MPT) Locking */
|
||||
/******************************************************************************/
|
||||
uint64_t
|
||||
mptEscrowed(jtx::Env const& env, jtx::Account const& account, jtx::MPT const& mpt)
|
||||
{
|
||||
auto const sle = env.le(keylet::mptoken(mpt.mpt(), account));
|
||||
if (sle && sle->isFieldPresent(sfLockedAmount))
|
||||
return (*sle)[sfLockedAmount];
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
issuerMPTEscrowed(jtx::Env const& env, jtx::MPT const& mpt)
|
||||
{
|
||||
auto const sle = env.le(keylet::mptokenIssuance(mpt.mpt()));
|
||||
if (sle && sle->isFieldPresent(sfLockedAmount))
|
||||
return (*sle)[sfLockedAmount];
|
||||
return 0;
|
||||
}
|
||||
|
||||
jtx::PrettyAmount
|
||||
issuerBalance(jtx::Env& env, jtx::Account const& account, Issue const& issue)
|
||||
{
|
||||
json::Value params;
|
||||
params[jss::account] = account.human();
|
||||
auto jrr = env.rpc("json", "gateway_balances", to_string(params));
|
||||
auto const result = jrr[jss::result];
|
||||
auto const obligations = result[jss::obligations][to_string(issue.currency)];
|
||||
if (obligations.isNull())
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
STAmount const amount = amountFromString(issue, obligations.asString());
|
||||
return {amount, account.name()};
|
||||
}
|
||||
|
||||
jtx::PrettyAmount
|
||||
issuerEscrowed(jtx::Env& env, jtx::Account const& account, Issue const& issue)
|
||||
{
|
||||
json::Value params;
|
||||
params[jss::account] = account.human();
|
||||
auto jrr = env.rpc("json", "gateway_balances", to_string(params));
|
||||
auto const result = jrr[jss::result];
|
||||
auto const locked = result[jss::locked][to_string(issue.currency)];
|
||||
if (locked.isNull())
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
STAmount const amount = amountFromString(issue, locked.asString());
|
||||
return {amount, account.name()};
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
sponsoredOwnerCount(Env const& env, Account const& account)
|
||||
{
|
||||
@@ -500,101 +542,6 @@ expectLedgerEntryRoot(Env& env, Account const& acct, STAmount const& expectedVal
|
||||
return accountBalance(env, acct) == to_string(expectedValue.xrp());
|
||||
}
|
||||
|
||||
/* Payment Channel */
|
||||
/******************************************************************************/
|
||||
namespace paychan {
|
||||
|
||||
json::Value
|
||||
create(
|
||||
AccountID const& account,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
NetClock::duration const& settleDelay,
|
||||
PublicKey const& pk,
|
||||
std::optional<NetClock::time_point> const& cancelAfter,
|
||||
std::optional<std::uint32_t> const& dstTag)
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelCreate;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[jss::Destination] = to_string(to);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::Values::None);
|
||||
jv[jss::SettleDelay] = settleDelay.count();
|
||||
jv[sfPublicKey.fieldName] = strHex(pk.slice());
|
||||
if (cancelAfter)
|
||||
jv[sfCancelAfter.fieldName] = cancelAfter->time_since_epoch().count();
|
||||
if (dstTag)
|
||||
jv[sfDestinationTag.fieldName] = *dstTag;
|
||||
return jv;
|
||||
}
|
||||
|
||||
json::Value
|
||||
fund(
|
||||
AccountID const& account,
|
||||
UInt256 const& channel,
|
||||
STAmount const& amount,
|
||||
std::optional<NetClock::time_point> const& expiration)
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelFund;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[sfChannel.fieldName] = to_string(channel);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::Values::None);
|
||||
if (expiration)
|
||||
jv[sfExpiration.fieldName] = expiration->time_since_epoch().count();
|
||||
return jv;
|
||||
}
|
||||
|
||||
json::Value
|
||||
claim(
|
||||
AccountID const& account,
|
||||
UInt256 const& channel,
|
||||
std::optional<STAmount> const& balance,
|
||||
std::optional<STAmount> const& amount,
|
||||
std::optional<Slice> const& signature,
|
||||
std::optional<PublicKey> const& pk)
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelClaim;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv["Channel"] = to_string(channel);
|
||||
if (amount)
|
||||
jv[jss::Amount] = amount->getJson(JsonOptions::Values::None);
|
||||
if (balance)
|
||||
jv["Balance"] = balance->getJson(JsonOptions::Values::None);
|
||||
if (signature)
|
||||
jv["Signature"] = strHex(*signature);
|
||||
if (pk)
|
||||
jv["PublicKey"] = strHex(pk->slice());
|
||||
return jv;
|
||||
}
|
||||
|
||||
UInt256
|
||||
channel(AccountID const& account, AccountID const& dst, std::uint32_t seqProxyValue)
|
||||
{
|
||||
auto const seqProxy = SeqProxy::rawSequence(seqProxyValue);
|
||||
auto const k = keylet::payChannel(account, dst, seqProxy);
|
||||
return k.key;
|
||||
}
|
||||
|
||||
STAmount
|
||||
channelBalance(ReadView const& view, UInt256 const& chan)
|
||||
{
|
||||
auto const slep = view.read({ltPAYCHAN, chan});
|
||||
if (!slep)
|
||||
return XRPAmount{-1};
|
||||
return (*slep)[sfBalance];
|
||||
}
|
||||
|
||||
bool
|
||||
channelExists(ReadView const& view, UInt256 const& chan)
|
||||
{
|
||||
auto const slep = view.read({ltPAYCHAN, chan});
|
||||
return bool(slep);
|
||||
}
|
||||
|
||||
} // namespace paychan
|
||||
|
||||
/* Crossing Limits */
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <test/jtx/paychan.h>
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PayChan.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
/**
|
||||
* Paychan operations.
|
||||
*/
|
||||
namespace xrpl::test::jtx::paychan {
|
||||
|
||||
json::Value
|
||||
create(
|
||||
AccountID const& account,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
NetClock::duration const& settleDelay,
|
||||
PublicKey const& pk,
|
||||
std::optional<NetClock::time_point> const& cancelAfter,
|
||||
std::optional<std::uint32_t> const& dstTag)
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelCreate;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[jss::Destination] = to_string(to);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::Values::None);
|
||||
jv[jss::SettleDelay] = settleDelay.count();
|
||||
jv[sfPublicKey.fieldName] = strHex(pk.slice());
|
||||
if (cancelAfter)
|
||||
jv[sfCancelAfter.fieldName] = cancelAfter->time_since_epoch().count();
|
||||
if (dstTag)
|
||||
jv[sfDestinationTag.fieldName] = *dstTag;
|
||||
return jv;
|
||||
}
|
||||
|
||||
json::Value
|
||||
fund(
|
||||
AccountID const& account,
|
||||
uint256 const& channel,
|
||||
STAmount const& amount,
|
||||
std::optional<NetClock::time_point> const& expiration)
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelFund;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[sfChannel.fieldName] = to_string(channel);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::Values::None);
|
||||
if (expiration)
|
||||
jv[sfExpiration.fieldName] = expiration->time_since_epoch().count();
|
||||
return jv;
|
||||
}
|
||||
|
||||
json::Value
|
||||
claim(
|
||||
AccountID const& account,
|
||||
uint256 const& channel,
|
||||
std::optional<STAmount> const& balance,
|
||||
std::optional<STAmount> const& amount,
|
||||
std::optional<Slice> const& signature,
|
||||
std::optional<PublicKey> const& pk)
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelClaim;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv["Channel"] = to_string(channel);
|
||||
if (amount)
|
||||
jv[jss::Amount] = amount->getJson(JsonOptions::Values::None);
|
||||
if (balance)
|
||||
jv["Balance"] = balance->getJson(JsonOptions::Values::None);
|
||||
if (signature)
|
||||
jv["Signature"] = strHex(*signature);
|
||||
if (pk)
|
||||
jv["PublicKey"] = strHex(pk->slice());
|
||||
return jv;
|
||||
}
|
||||
|
||||
uint256
|
||||
channel(AccountID const& account, AccountID const& dst, std::uint32_t seqProxyValue)
|
||||
{
|
||||
auto const seqProxy = SeqProxy::rawSequence(seqProxyValue);
|
||||
auto const k = keylet::payChannel(account, dst, seqProxy);
|
||||
return k.key;
|
||||
}
|
||||
|
||||
STAmount
|
||||
channelBalance(ReadView const& view, uint256 const& chan)
|
||||
{
|
||||
auto const slep = view.read({ltPAYCHAN, chan});
|
||||
if (!slep)
|
||||
return XRPAmount{-1};
|
||||
return (*slep)[sfBalance];
|
||||
}
|
||||
|
||||
STAmount
|
||||
channelAmount(ReadView const& view, uint256 const& chan)
|
||||
{
|
||||
auto const slep = view.read({ltPAYCHAN, chan});
|
||||
if (!slep)
|
||||
return XRPAmount{-1};
|
||||
return (*slep)[sfAmount];
|
||||
}
|
||||
|
||||
bool
|
||||
channelExists(ReadView const& view, uint256 const& chan)
|
||||
{
|
||||
auto const slep = view.read({ltPAYCHAN, chan});
|
||||
return bool(slep);
|
||||
}
|
||||
|
||||
Buffer
|
||||
signClaimAuth(
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk,
|
||||
uint256 const& channel,
|
||||
STAmount const& authAmt)
|
||||
{
|
||||
Serializer msg;
|
||||
serializePayChanAuthorization(msg, channel, authAmt.xrp());
|
||||
return sign(pk, sk, msg.slice());
|
||||
}
|
||||
|
||||
} // namespace xrpl::test::jtx::paychan
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <test/jtx/Account.h>
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
/**
|
||||
* Paychan operations.
|
||||
*/
|
||||
namespace xrpl::test::jtx::paychan {
|
||||
|
||||
json::Value
|
||||
create(
|
||||
AccountID const& account,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
NetClock::duration const& settleDelay,
|
||||
PublicKey const& pk,
|
||||
std::optional<NetClock::time_point> const& cancelAfter = std::nullopt,
|
||||
std::optional<std::uint32_t> const& dstTag = std::nullopt);
|
||||
|
||||
inline json::Value
|
||||
create(
|
||||
Account const& account,
|
||||
Account const& to,
|
||||
STAmount const& amount,
|
||||
NetClock::duration const& settleDelay,
|
||||
PublicKey const& pk,
|
||||
std::optional<NetClock::time_point> const& cancelAfter = std::nullopt,
|
||||
std::optional<std::uint32_t> const& dstTag = std::nullopt)
|
||||
{
|
||||
return create(account.id(), to.id(), amount, settleDelay, pk, cancelAfter, dstTag);
|
||||
}
|
||||
|
||||
json::Value
|
||||
fund(
|
||||
AccountID const& account,
|
||||
uint256 const& channel,
|
||||
STAmount const& amount,
|
||||
std::optional<NetClock::time_point> const& expiration = std::nullopt);
|
||||
|
||||
json::Value
|
||||
claim(
|
||||
AccountID const& account,
|
||||
uint256 const& channel,
|
||||
std::optional<STAmount> const& balance = std::nullopt,
|
||||
std::optional<STAmount> const& amount = std::nullopt,
|
||||
std::optional<Slice> const& signature = std::nullopt,
|
||||
std::optional<PublicKey> const& pk = std::nullopt);
|
||||
|
||||
uint256
|
||||
channel(AccountID const& account, AccountID const& dst, std::uint32_t seqProxyValue);
|
||||
|
||||
inline uint256
|
||||
channel(Account const& account, Account const& dst, std::uint32_t seqProxyValue)
|
||||
{
|
||||
return channel(account.id(), dst.id(), seqProxyValue);
|
||||
}
|
||||
|
||||
STAmount
|
||||
channelBalance(ReadView const& view, uint256 const& chan);
|
||||
|
||||
STAmount
|
||||
channelAmount(ReadView const& view, uint256 const& chan);
|
||||
|
||||
bool
|
||||
channelExists(ReadView const& view, uint256 const& chan);
|
||||
|
||||
Buffer
|
||||
signClaimAuth(
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk,
|
||||
uint256 const& channel,
|
||||
STAmount const& authAmt);
|
||||
|
||||
} // namespace xrpl::test::jtx::paychan
|
||||
@@ -319,6 +319,22 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotFound));
|
||||
}
|
||||
|
||||
{
|
||||
json::Value p{jParams};
|
||||
|
||||
p[jss::ledger_index] = "validated";
|
||||
BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p))));
|
||||
|
||||
p[jss::ledger_index] = "closed";
|
||||
BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p))));
|
||||
|
||||
p[jss::ledger_index] = "current";
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotValidated));
|
||||
|
||||
p[jss::ledger_index] = "";
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotValidated));
|
||||
}
|
||||
|
||||
// Ledger Hash
|
||||
{
|
||||
json::Value p{jParams};
|
||||
|
||||
@@ -506,7 +506,18 @@ class Feature_test : public beast::unit_test::Suite
|
||||
feature = *(jrr.begin());
|
||||
BEAST_EXPECTS(feature[jss::name] == kFeatureName, "name");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed");
|
||||
auto testInvalidVetoed = [&](auto const& vetoed) {
|
||||
json::Value params;
|
||||
params[jss::feature] = kFeatureName;
|
||||
params[jss::vetoed] = vetoed;
|
||||
|
||||
auto const result = env.rpc("json", "feature", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
};
|
||||
|
||||
testInvalidVetoed("false");
|
||||
testInvalidVetoed(json::Value(json::ValueType::Null));
|
||||
// anything other than accept or reject is an error
|
||||
jrr = env.rpc("feature", kFeatureName, "maybe");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
|
||||
@@ -103,6 +103,19 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jrr[jss::ledger][jss::ledger_index] == std::to_string(env.current()->header().seq));
|
||||
BEAST_EXPECT(jrr[jss::ledger_current_index] == env.current()->header().seq);
|
||||
}
|
||||
|
||||
{
|
||||
auto const jrr = env.rpc("json", "ledger", "{}")[jss::result];
|
||||
BEAST_EXPECT(!jrr.isMember(jss::ledger));
|
||||
BEAST_EXPECT(jrr[jss::closed][jss::ledger][jss::closed] == true);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::closed][jss::ledger][jss::ledger_index] ==
|
||||
std::to_string(env.closed()->header().seq));
|
||||
BEAST_EXPECT(jrr[jss::open][jss::ledger][jss::closed] == false);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::open][jss::ledger][jss::ledger_index] ==
|
||||
std::to_string(env.current()->header().seq));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <xrpl/ledger/entries/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
|
||||
#include <xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -55,11 +56,11 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// The entry classes have no consumers yet, and an un-instantiated class
|
||||
// template is barely type-checked. Instantiate every one explicitly so the
|
||||
// compiler actually checks them. Keep this block even once real call sites
|
||||
// exist: it is what catches a new ledger entry type being added without its
|
||||
// wrapper class, or the wrapper class existing but never actually being used.
|
||||
// An un-instantiated class template is barely type-checked. Name every entry
|
||||
// class in a static_assert below so its class definition is instantiated and
|
||||
// type-checked; the per-type suites check the member bodies. Keep this block
|
||||
// even once real call sites exist: it is what catches a new ledger entry type
|
||||
// being added without its wrapper class.
|
||||
//
|
||||
// Driving this off ledger_entries.macro keeps it exhaustive by construction:
|
||||
// adding a ledger entry type without adding its entry class stops compiling
|
||||
@@ -76,12 +77,22 @@ template class SLEBase<ApplyView>;
|
||||
#pragma push_macro("LEDGER_ENTRY")
|
||||
#undef LEDGER_ENTRY
|
||||
|
||||
// No explicit instantiation here, neither a definition nor a declaration.
|
||||
// A definition would instantiate an entry a second time once its member
|
||||
// bodies live in its own .cpp (MSVC rejects that with C4661 under /WX), and a
|
||||
// declaration (`extern template`) stops clang from emitting the inline
|
||||
// constructors of the header-only entries at -O0, which leaves them undefined
|
||||
// at link time in the debug builds. Naming the class in a static_assert is
|
||||
// enough to instantiate its definition, which is all this exhaustiveness
|
||||
// check needs; the per-type suites exercise the members.
|
||||
#define LEDGER_ENTRY(tag, value, name, ...) \
|
||||
template class name##Entry<ReadView>; \
|
||||
template class name##Entry<ApplyView>; \
|
||||
static_assert( \
|
||||
name##Entry<ReadView>::kEntryType == tag && name##Entry<ApplyView>::kEntryType == tag, \
|
||||
#name "Entry must be bound to " #tag);
|
||||
#name "Entry must be bound to " #tag); \
|
||||
static_assert( \
|
||||
std::is_constructible_v<name##Entry<ReadView>, Keylet const&, ReadView const&> && \
|
||||
std::is_constructible_v<name##Entry<ApplyView>, Keylet const&, ApplyView&>, \
|
||||
#name "Entry must be constructible from a Keylet and a view");
|
||||
|
||||
#include <xrpl/protocol/detail/ledger_entries.macro>
|
||||
|
||||
|
||||
@@ -311,6 +311,20 @@ TEST(SpecBridge, shared_spec_warns_on_a_deprecated_field)
|
||||
EXPECT_EQ(warnings[0].code, ::rpc::WarningCode::WarnRpcDeprecated);
|
||||
}
|
||||
|
||||
TEST(SpecBridge, inject_spec_error_uses_default_message)
|
||||
{
|
||||
json::Value out{json::ValueType::Object};
|
||||
rpc::injectSpecError(out, ::rpc::Status{RpcLgrNotFound});
|
||||
EXPECT_EQ(out, rpc::makeError(RpcLgrNotFound));
|
||||
}
|
||||
|
||||
TEST(SpecBridge, inject_spec_error_uses_custom_message)
|
||||
{
|
||||
json::Value out{json::ValueType::Object};
|
||||
rpc::injectSpecError(out, ::rpc::Status{RpcInvalidParams, "Invalid field 'ledger'."});
|
||||
EXPECT_EQ(out, rpc::makeError(RpcInvalidParams, "Invalid field 'ledger'."));
|
||||
}
|
||||
|
||||
TEST(SpecBridge, inject_spec_warnings)
|
||||
{
|
||||
std::string const kDeprecatedBase{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpld/rpc/MethodNames.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
#include <xrpld/rpc/handlers/Handlers.h>
|
||||
#include <xrpld/rpc/handlers/ledger/Ledger.h>
|
||||
#include <xrpld/rpc/handlers/server_info/Version.h>
|
||||
@@ -12,10 +13,16 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/Types.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <expected>
|
||||
#include <iterator>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
@@ -49,7 +56,34 @@ byRef(JsonContext& context, json::Value& result)
|
||||
return Status();
|
||||
}
|
||||
|
||||
template <typename HandlerImpl>
|
||||
concept SpecBasedHandler = std::derived_from<HandlerImpl, HandlerFor<typename HandlerImpl::Input>>;
|
||||
|
||||
template <typename HandlerImpl>
|
||||
concept InputlessHandler = requires(HandlerImpl& handler) { handler.process(); };
|
||||
|
||||
template <class HandlerImpl, class Output>
|
||||
Status
|
||||
respond(
|
||||
HandlerImpl const& handler,
|
||||
json::Value& object,
|
||||
std::expected<Output, ::rpc::Status> const& output,
|
||||
::rpc::spec::Warnings const& warnings)
|
||||
{
|
||||
if (not output.has_value())
|
||||
{
|
||||
injectSpecError(object, output.error());
|
||||
return std::get<ErrorCodeI>(output.error().code);
|
||||
}
|
||||
|
||||
handler.writeResult(object, *output);
|
||||
injectSpecWarnings(object, warnings);
|
||||
|
||||
return Status::kOK;
|
||||
}
|
||||
|
||||
template <class HandlerImpl>
|
||||
requires SpecBasedHandler<HandlerImpl> or InputlessHandler<HandlerImpl>
|
||||
Status
|
||||
handle(JsonContext& context, json::Value& object)
|
||||
{
|
||||
@@ -57,18 +91,28 @@ handle(JsonContext& context, json::Value& object)
|
||||
context.apiVersion >= HandlerImpl::minApiVer &&
|
||||
context.apiVersion <= HandlerImpl::maxApiVer,
|
||||
"xrpl::rpc::handle : valid API version");
|
||||
HandlerImpl handler(context);
|
||||
|
||||
auto status = handler.check();
|
||||
if (status)
|
||||
if constexpr (SpecBasedHandler<HandlerImpl>)
|
||||
{
|
||||
status.inject(object);
|
||||
auto const input = HandlerImpl::parseInput(context.params, context.apiVersion);
|
||||
if (not input.has_value())
|
||||
{
|
||||
injectSpecError(object, input.error());
|
||||
return std::get<ErrorCodeI>(input.error().code);
|
||||
}
|
||||
|
||||
HandlerImpl handler(context);
|
||||
auto const output = handler.process(*input);
|
||||
auto const warnings = output.has_value()
|
||||
? HandlerImpl::spec(context.apiVersion).check(context.params)
|
||||
: ::rpc::spec::Warnings{};
|
||||
return respond(handler, object, output, warnings);
|
||||
}
|
||||
else
|
||||
{
|
||||
handler.writeResult(object);
|
||||
HandlerImpl handler(context);
|
||||
return respond(handler, object, handler.process(), /* warnings= */ {});
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
template <typename HandlerImpl>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/LedgerShortcut.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/RippleLedgerHash.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -25,15 +24,28 @@
|
||||
#include <org/xrpl/rpc/v1/get_ledger_data.pb.h> // IWYU pragma: keep
|
||||
#include <org/xrpl/rpc/v1/get_ledger_entry.pb.h> // IWYU pragma: keep
|
||||
#include <org/xrpl/rpc/v1/ledger.pb.h>
|
||||
#include <rpcspec/Ledger.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename... Ts>
|
||||
struct Overload : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
|
||||
constexpr Overload(Ts&&... ts) : Ts(std::forward<Ts>(ts))...
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
isValidatedOld(LedgerMaster& ledgerMaster, bool standalone)
|
||||
{
|
||||
@@ -68,13 +80,13 @@ ledgerFromIndex(
|
||||
auto const index = indexValue.asString();
|
||||
|
||||
if (index == "current" || index.empty())
|
||||
return getLedger(ledger, LedgerShortcut::Current, context);
|
||||
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Current, context);
|
||||
|
||||
if (index == "validated")
|
||||
return getLedger(ledger, LedgerShortcut::Validated, context);
|
||||
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Validated, context);
|
||||
|
||||
if (index == "closed")
|
||||
return getLedger(ledger, LedgerShortcut::Closed, context);
|
||||
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Closed, context);
|
||||
|
||||
std::uint32_t iVal = 0;
|
||||
if (!beast::lexicalCastChecked(iVal, index))
|
||||
@@ -146,7 +158,7 @@ ledgerFromRequest(T& ledger, JsonContext const& context)
|
||||
}
|
||||
|
||||
// nothing specified, `index` has a default setting
|
||||
return getLedger(ledger, LedgerShortcut::Current, context);
|
||||
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Current, context);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -204,17 +216,17 @@ ledgerFromSpecifier(
|
||||
auto const shortcut = specifier.shortcut();
|
||||
if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
|
||||
{
|
||||
return getLedger(ledger, LedgerShortcut::Validated, context);
|
||||
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Validated, context);
|
||||
}
|
||||
|
||||
if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT ||
|
||||
shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED)
|
||||
{
|
||||
return getLedger(ledger, LedgerShortcut::Current, context);
|
||||
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Current, context);
|
||||
}
|
||||
if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
|
||||
{
|
||||
return getLedger(ledger, LedgerShortcut::Closed, context);
|
||||
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Closed, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,7 +275,7 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context)
|
||||
|
||||
template <class T>
|
||||
Status
|
||||
getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
|
||||
getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context)
|
||||
{
|
||||
if (isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
|
||||
{
|
||||
@@ -272,7 +284,7 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
|
||||
return {RpcNotSynced, "notSynced"};
|
||||
}
|
||||
|
||||
if (shortcut == LedgerShortcut::Validated)
|
||||
if (shortcut == ::rpc::spec::LedgerShortcut::Validated)
|
||||
{
|
||||
ledger = context.ledgerMaster.getValidatedLedger();
|
||||
if (ledger == nullptr)
|
||||
@@ -286,12 +298,12 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shortcut == LedgerShortcut::Current)
|
||||
if (shortcut == ::rpc::spec::LedgerShortcut::Current)
|
||||
{
|
||||
ledger = context.ledgerMaster.getCurrentLedger();
|
||||
XRPL_ASSERT(ledger->open(), "xrpl::rpc::getLedger : current is open");
|
||||
}
|
||||
else if (shortcut == LedgerShortcut::Closed)
|
||||
else if (shortcut == ::rpc::spec::LedgerShortcut::Closed)
|
||||
{
|
||||
ledger = context.ledgerMaster.getClosedLedger();
|
||||
XRPL_ASSERT(!ledger->open(), "xrpl::rpc::getLedger : closed is not open");
|
||||
@@ -321,16 +333,33 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
|
||||
return Status::kOK;
|
||||
}
|
||||
|
||||
// Explicit instantiation of above three functions
|
||||
template <class T>
|
||||
Status
|
||||
getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context)
|
||||
{
|
||||
return std::visit(
|
||||
Overload{
|
||||
[&](std::monostate) {
|
||||
return getLedger(ledger, ::rpc::spec::kDefaultLedgerShortcut, context);
|
||||
},
|
||||
[&](auto const& value) { return getLedger(ledger, value, context); },
|
||||
},
|
||||
specifier.value);
|
||||
}
|
||||
|
||||
// Explicit instantiation of above four functions
|
||||
template Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context const&);
|
||||
|
||||
template Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, LedgerShortcut shortcut, Context const&);
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerShortcut shortcut, Context const&);
|
||||
|
||||
template Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, UInt256 const&, Context const&);
|
||||
|
||||
template Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerSpecifier const&, Context const&);
|
||||
|
||||
// explicit instantiation of ledgerFromSpecifier
|
||||
template Status
|
||||
ledgerFromSpecifier<>(
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/Ledger.h>
|
||||
#include <xrpl/protocol/LedgerShortcut.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
#include <org/xrpl/rpc/v1/ledger.pb.h>
|
||||
#include <rpcspec/Ledger.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
@@ -76,7 +76,22 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context);
|
||||
*/
|
||||
template <class T>
|
||||
Status
|
||||
getLedger(T& ledger, LedgerShortcut shortcut, Context const& context);
|
||||
getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the ledger a spec-parsed request selects.
|
||||
*
|
||||
* An unspecified selection means the current ledger.
|
||||
*
|
||||
* @tparam T Type of the ledger pointer to be filled.
|
||||
* @param ledger Reference to the ledger pointer to be filled.
|
||||
* @param specifier The ledger the request selects.
|
||||
* @param context The RPC context.
|
||||
* @return Status indicating success or failure of the operation.
|
||||
*/
|
||||
template <class T>
|
||||
Status
|
||||
getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context);
|
||||
|
||||
/**
|
||||
* @brief Looks up a ledger from a request and returns a json::Value with either
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/rpc/detail/JsonObjectView.hpp>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/HandlerFor.hpp>
|
||||
#include <rpcspec/Types.hpp>
|
||||
|
||||
#include <map>
|
||||
@@ -12,6 +16,22 @@
|
||||
|
||||
namespace xrpl::rpc {
|
||||
|
||||
template <typename InputT>
|
||||
using HandlerFor = ::rpc::spec::HandlerFor<InputT, json::Value>;
|
||||
|
||||
inline void
|
||||
injectSpecError(json::Value& object, ::rpc::Status const& status)
|
||||
{
|
||||
if (auto const code = std::get<ErrorCodeI>(status.code); status.message.empty())
|
||||
{
|
||||
injectError(code, object);
|
||||
}
|
||||
else
|
||||
{
|
||||
injectError(code, status.message, object);
|
||||
}
|
||||
}
|
||||
|
||||
// Warnings are grouped by code into one entry each: the code's standard message followed by
|
||||
// every per-field detail, space separated.
|
||||
//
|
||||
|
||||
@@ -20,13 +20,14 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/LedgerShortcut.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/RippleLedgerHash.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/rdb/RelationalDatabase.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
|
||||
#include <rpcspec/Ledger.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
@@ -82,9 +83,24 @@ parseDelegateFilter(json::Value const& delegateNode)
|
||||
using TxnsData = RelationalDatabase::AccountTxs;
|
||||
using TxnsDataBinary = RelationalDatabase::MetaTxsList;
|
||||
using TxnDataBinary = RelationalDatabase::TxnMetaLedgerType;
|
||||
using AccountTxArgs = RelationalDatabase::AccountTxArgs;
|
||||
using AccountTxResult = RelationalDatabase::AccountTxResult;
|
||||
using LedgerSpecifier = RelationalDatabase::LedgerSpecifier;
|
||||
|
||||
// Moved here from RelationalDatabase, where nothing but this handler used them, so that
|
||||
// libxrpl does not depend on the spec for LedgerShortcut. Temporary: the spec's
|
||||
// account_tx Input replaces both once account_tx is migrated to the spec.
|
||||
using LedgerSpecifier =
|
||||
std::variant<LedgerRange, ::rpc::spec::LedgerShortcut, std::uint32_t, uint256>;
|
||||
|
||||
struct AccountTxArgs
|
||||
{
|
||||
AccountID account;
|
||||
std::optional<LedgerSpecifier> ledger;
|
||||
bool binary = false;
|
||||
bool forward = false;
|
||||
uint32_t limit = 0;
|
||||
std::optional<RelationalDatabase::AccountTxMarker> marker;
|
||||
std::optional<DelegateFilter> delegate;
|
||||
};
|
||||
|
||||
// parses args into a ledger specifier, or returns a Json object on error
|
||||
std::variant<std::optional<LedgerSpecifier>, json::Value>
|
||||
@@ -148,15 +164,15 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params)
|
||||
|
||||
if (ledgerStr == "current" || ledgerStr.empty())
|
||||
{
|
||||
ledger = LedgerShortcut::Current;
|
||||
ledger = ::rpc::spec::LedgerShortcut::Current;
|
||||
}
|
||||
else if (ledgerStr == "closed")
|
||||
{
|
||||
ledger = LedgerShortcut::Closed;
|
||||
ledger = ::rpc::spec::LedgerShortcut::Closed;
|
||||
}
|
||||
else if (ledgerStr == "validated")
|
||||
{
|
||||
ledger = LedgerShortcut::Validated;
|
||||
ledger = ::rpc::spec::LedgerShortcut::Validated;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
@@ -19,11 +20,11 @@
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
#include <xrpl/server/LoadFeeTrack.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
|
||||
#include <grpcpp/support/status.h>
|
||||
#include <org/xrpl/rpc/v1/get_ledger.pb.h>
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
@@ -40,126 +41,83 @@ LedgerHandler::LedgerHandler(JsonContext& context) : context_(context)
|
||||
{
|
||||
}
|
||||
|
||||
Status
|
||||
LedgerHandler::check()
|
||||
std::expected<LedgerHandler::Output, ::rpc::Status>
|
||||
LedgerHandler::process(Input const& input)
|
||||
{
|
||||
auto const& params = context_.params;
|
||||
Output output;
|
||||
output.options = (input.full ? static_cast<int>(LedgerFill::Options::Full) : 0) |
|
||||
(input.expand ? static_cast<int>(LedgerFill::Options::Expand) : 0) |
|
||||
(input.transactions ? static_cast<int>(LedgerFill::Options::DumpTxrp) : 0) |
|
||||
(input.accounts ? static_cast<int>(LedgerFill::Options::DumpState) : 0) |
|
||||
(input.binary ? static_cast<int>(LedgerFill::Options::Binary) : 0) |
|
||||
(input.ownerFunds ? static_cast<int>(LedgerFill::Options::OwnerFunds) : 0) |
|
||||
(input.queue ? static_cast<int>(LedgerFill::Options::DumpQueue) : 0);
|
||||
|
||||
auto getBool = [&](json::StaticString const& field) -> std::expected<bool, Status> {
|
||||
if (!params.isMember(field))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!params[field].isBool())
|
||||
{
|
||||
return std::unexpected(RpcInvalidParams);
|
||||
}
|
||||
if (input.ledger.isUnspecified())
|
||||
return output;
|
||||
|
||||
return params[field].asBool();
|
||||
};
|
||||
if (auto const status = getLedger(output.ledger, input.ledger, context_.get()))
|
||||
return std::unexpected{::rpc::Status{status.toErrorCode(), status.message()}};
|
||||
|
||||
auto const full = getBool(jss::full);
|
||||
auto const transactions = getBool(jss::transactions);
|
||||
auto const accounts = getBool(jss::accounts);
|
||||
auto const expand = getBool(jss::expand);
|
||||
auto const binary = getBool(jss::binary);
|
||||
auto const ownerFunds = getBool(jss::owner_funds);
|
||||
auto const queue = getBool(jss::queue);
|
||||
|
||||
if (!full.has_value())
|
||||
return full.error();
|
||||
if (!transactions.has_value())
|
||||
return transactions.error();
|
||||
if (!accounts.has_value())
|
||||
return accounts.error();
|
||||
if (!expand.has_value())
|
||||
return expand.error();
|
||||
if (!binary.has_value())
|
||||
return binary.error();
|
||||
if (!ownerFunds.has_value())
|
||||
return ownerFunds.error();
|
||||
if (!queue.has_value())
|
||||
return queue.error();
|
||||
|
||||
options_ = (*full ? static_cast<int>(LedgerFill::Options::Full) : 0) |
|
||||
(*expand ? static_cast<int>(LedgerFill::Options::Expand) : 0) |
|
||||
(*transactions ? static_cast<int>(LedgerFill::Options::DumpTxrp) : 0) |
|
||||
(*accounts ? static_cast<int>(LedgerFill::Options::DumpState) : 0) |
|
||||
(*binary ? static_cast<int>(LedgerFill::Options::Binary) : 0) |
|
||||
(*ownerFunds ? static_cast<int>(LedgerFill::Options::OwnerFunds) : 0) |
|
||||
(*queue ? static_cast<int>(LedgerFill::Options::DumpQueue) : 0);
|
||||
|
||||
bool const needsLedger = params.isMember(jss::ledger) || params.isMember(jss::ledger_hash) ||
|
||||
params.isMember(jss::ledger_index);
|
||||
if (!needsLedger)
|
||||
return Status::kOK;
|
||||
if (auto s = lookupLedger(ledger_, context_, result_))
|
||||
return s;
|
||||
|
||||
if (*full || *accounts)
|
||||
if (input.full || input.accounts)
|
||||
{
|
||||
// Until some sane way to get full ledgers has been implemented,
|
||||
// disallow retrieving all state nodes.
|
||||
if (!isUnlimited(context_.role))
|
||||
return RpcNoPermission;
|
||||
if (!isUnlimited(context_.get().role))
|
||||
return std::unexpected{::rpc::Status{RpcNoPermission}};
|
||||
|
||||
if (context_.app.getFeeTrack().isLoadedLocal() && !isUnlimited(context_.role))
|
||||
{
|
||||
return RpcTooBusy;
|
||||
}
|
||||
context_.loadType = binary ? resource::kFeeMediumBurdenRpc : resource::kFeeHeavyBurdenRpc;
|
||||
context_.get().loadType =
|
||||
input.binary ? resource::kFeeMediumBurdenRpc : resource::kFeeHeavyBurdenRpc;
|
||||
}
|
||||
|
||||
if (*queue)
|
||||
if (input.queue)
|
||||
{
|
||||
if (!ledger_ || !ledger_->open())
|
||||
if (!output.ledger || !output.ledger->open())
|
||||
{
|
||||
// It doesn't make sense to request the queue
|
||||
// with a non-existent or closed/validated ledger.
|
||||
return RpcInvalidParams;
|
||||
return std::unexpected{::rpc::Status{RpcInvalidParams}};
|
||||
}
|
||||
|
||||
queueTxs_ = context_.app.getTxQ().getTxs();
|
||||
output.queueTxs = context_.get().app.getTxQ().getTxs();
|
||||
}
|
||||
|
||||
return Status::kOK;
|
||||
return output;
|
||||
}
|
||||
|
||||
void
|
||||
LedgerHandler::writeResult(json::Value& value)
|
||||
LedgerHandler::writeResult(json::Value& value, Output const& output) const
|
||||
{
|
||||
if (ledger_)
|
||||
if (output.ledger)
|
||||
{
|
||||
copyFrom(value, result_);
|
||||
addJson(value, {*ledger_, &context_, options_, queueTxs_});
|
||||
auto const& header = output.ledger->header();
|
||||
|
||||
if (output.ledger->open())
|
||||
{
|
||||
value[jss::ledger_current_index] = header.seq;
|
||||
}
|
||||
else
|
||||
{
|
||||
value[jss::ledger_hash] = to_string(header.hash);
|
||||
value[jss::ledger_index] = header.seq;
|
||||
}
|
||||
|
||||
value[jss::validated] = context_.get().ledgerMaster.isValidated(*output.ledger);
|
||||
|
||||
addJson(value, {*output.ledger, &context_.get(), output.options, output.queueTxs});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& master = context_.app.getLedgerMaster();
|
||||
auto& master = context_.get().app.getLedgerMaster();
|
||||
{
|
||||
auto& closed = value[jss::closed] = json::ValueType::Object;
|
||||
addJson(closed, {*master.getClosedLedger(), &context_, 0});
|
||||
addJson(closed, {*master.getClosedLedger(), &context_.get(), 0});
|
||||
}
|
||||
{
|
||||
auto& open = value[jss::open] = json::ValueType::Object;
|
||||
addJson(open, {*master.getCurrentLedger(), &context_, 0});
|
||||
addJson(open, {*master.getCurrentLedger(), &context_.get(), 0});
|
||||
}
|
||||
}
|
||||
|
||||
json::Value warnings{json::ValueType::Array};
|
||||
if (context_.params.isMember(jss::type))
|
||||
{
|
||||
json::Value& w = warnings.append(json::ValueType::Object);
|
||||
w[jss::id] = WarnRpcFieldsDeprecated;
|
||||
w[jss::message] =
|
||||
"Some fields from your request are deprecated. Please check the "
|
||||
"documentation at "
|
||||
"https://xrpl.org/docs/references/http-websocket-apis/ "
|
||||
"and update your request. Field `type` is deprecated.";
|
||||
}
|
||||
|
||||
if (warnings.size() != 0u)
|
||||
value[jss::warnings] = std::move(warnings);
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
@@ -5,13 +5,18 @@
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/MethodNames.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Handler.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/handlers/ledger/Types.hpp>
|
||||
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -26,16 +31,23 @@ struct JsonContext;
|
||||
// full: true | false // optional, defaults to false.
|
||||
// }
|
||||
|
||||
class LedgerHandler
|
||||
class LedgerHandler : public HandlerFor<::rpc::spec::handlers::ledger::Input>
|
||||
{
|
||||
public:
|
||||
struct Output
|
||||
{
|
||||
std::shared_ptr<ReadView const> ledger;
|
||||
std::vector<TxQ::TxDetails> queueTxs;
|
||||
int options = 0;
|
||||
};
|
||||
|
||||
explicit LedgerHandler(JsonContext&);
|
||||
|
||||
Status
|
||||
check();
|
||||
[[nodiscard]] std::expected<Output, ::rpc::Status>
|
||||
process(Input const& input);
|
||||
|
||||
void
|
||||
writeResult(json::Value&);
|
||||
writeResult(json::Value& value, Output const& output) const;
|
||||
|
||||
// NOLINTBEGIN(readability-identifier-naming)
|
||||
static constexpr std::string_view name = method::kLedger;
|
||||
@@ -50,11 +62,7 @@ public:
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
|
||||
private:
|
||||
JsonContext& context_;
|
||||
std::shared_ptr<ReadView const> ledger_;
|
||||
std::vector<TxQ::TxDetails> queueTxs_;
|
||||
json::Value result_;
|
||||
int options_ = 0;
|
||||
std::reference_wrapper<JsonContext> context_;
|
||||
};
|
||||
|
||||
} // namespace xrpl::rpc
|
||||
@@ -64,6 +64,11 @@ doFeature(rpc::JsonContext& context)
|
||||
if (!isAdmin)
|
||||
return rpcError(RpcNoPermission);
|
||||
|
||||
if (!context.params[jss::vetoed].isBool())
|
||||
{
|
||||
return rpcError(RpcInvalidParams);
|
||||
}
|
||||
|
||||
if (context.params[jss::vetoed].asBool())
|
||||
{
|
||||
table.veto(feature);
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/MethodNames.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Handler.h>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
@@ -17,21 +21,29 @@ namespace xrpl::rpc {
|
||||
class VersionHandler
|
||||
{
|
||||
public:
|
||||
explicit VersionHandler(JsonContext& c)
|
||||
: apiVersion_(c.apiVersion), betaEnabled_(c.app.config().betaRpcApi)
|
||||
struct Output
|
||||
{
|
||||
std::uint32_t apiVersion;
|
||||
bool betaEnabled;
|
||||
};
|
||||
|
||||
explicit VersionHandler(JsonContext& context) : context_(context)
|
||||
{
|
||||
}
|
||||
|
||||
static Status
|
||||
check()
|
||||
[[nodiscard]] std::expected<Output, ::rpc::Status>
|
||||
process() const
|
||||
{
|
||||
return Status::kOK;
|
||||
return Output{
|
||||
.apiVersion = context_.get().apiVersion,
|
||||
.betaEnabled = context_.get().app.config().betaRpcApi,
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
writeResult(json::Value& obj) const
|
||||
static void
|
||||
writeResult(json::Value& obj, Output const& output)
|
||||
{
|
||||
setVersion(obj, apiVersion_, betaEnabled_);
|
||||
setVersion(obj, output.apiVersion, output.betaEnabled);
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(readability-identifier-naming)
|
||||
@@ -47,8 +59,7 @@ public:
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
|
||||
private:
|
||||
unsigned int apiVersion_;
|
||||
bool betaEnabled_;
|
||||
std::reference_wrapper<JsonContext> context_;
|
||||
};
|
||||
|
||||
} // namespace xrpl::rpc
|
||||
Reference in new issue
Block a user