Compare commits

..

63 Commits

Author SHA1 Message Date
Nicholas Dudfield
7694602d10 Close F0 nominal classifier proof gap 2026-08-25 12:01:08 +07:00
Nicholas Dudfield
f3e9d2b6a7 Advance JSHooks provider to F0 2026-08-25 11:44:57 +07:00
Nicholas Dudfield
ff7de7207a ci(hooks): skip Docker builds on jshookz again
Restore the pre-2b493ed skip: do not run Build using Docker on jshookz
pushes/PRs, and skip the build job when the PR head is jshookz.
2026-08-24 16:32:09 +07:00
Nicholas Dudfield
1b3722c012 style: x-format-changed since origin/dev
clang-format 18 on HookArtifact/JSHooks tests (CI clang-format job),
ruff on conanfile.py, cmake-format on CMakeLists.txt.
2026-08-24 16:24:28 +07:00
Nicholas Dudfield
e2f87c0b03 ci(hooks): run JSHookz e2e on jshookz push only
Drop the pull_request trigger for now. Push and PR used different refs,
so concurrency did not cancel the duplicate self-hosted Env job.
2026-08-24 14:46:53 +07:00
Nicholas Dudfield
6d6f011b5b fix(hooks): fail closed on sealed Receipt-A lock tables
Generator now rejects declaration/surface hashes, the 22 typed exports,
the 13 typed imports including module, and the four object limits unless
they match the immutable Receipt-A expectation. Coordinated JSON and
JSON+CMake mutations go red in the Python suite, which is also a CMake
target for the focused consumer gate.
2026-08-24 13:34:31 +07:00
Nicholas Dudfield
8e7a7f48f9 ci(hooks): feed JSHookz e2e from xahaud-pinned
Point JSHOOKZ_REF at the dedicated 78de1c4 convenience branch. Tracked
manifest/native locks remain the authority; no expected-commit check.
2026-08-24 13:27:14 +07:00
Nicholas Dudfield
07962562f6 test(hooks): project typed exports and fail-closed lock mutations
Project all 22 typed QuickJS provider export rows (kind/name/params/results
plus the memory row) and wasm_stack_bytes=131072 from the sealed 78de1c4
lock. Cross-compare JSON/CMake/native identities in the generator and add
durable mutation reds for provider bytes/size/SHA, manifest SHA, runtime
profile, declaration/surface, bytecode/native ABI, import/export
signatures, memory shape, and each serialized-object limit.
2026-08-24 13:13:02 +07:00
Nicholas Dudfield
50615166b1 chore(hooks): pin QuickJS provider 78de1c4 (Receipt B)
Consume the sealed lock at 1101461 bytes / SHA-256 40f9ac02… / runtime
60950ca8… / bytecode ABI 75ea54f3…. Project the four serialized-object
limits, 22 exports, memory min 6 / max 512, and javascript-surface
hashes. Native STObject/STArray fixture covers Amount.issue.currency,
repeated identity, and canonical JSON/bytes. Fuel ratchets step with
the larger provider; guest ABI is unchanged.
2026-08-24 12:54:02 +07:00
Nicholas Dudfield
f14e9236d2 fix(hooks): bill current-profile invocation fuel without incomplete type
SetHook cannot dereference the opaque QuickJS runtime handle. Use
currentQuickJSRuntimeProfile().invocationFuel for the CREATE fee
ceiling. Keep this out of the Receipt B provider pin.
2026-08-24 12:38:23 +07:00
Nicholas Dudfield
2b493ed881 fix(hooks): harden QuickJS consumer before next provider pin
Empty embed is fatal at daemon start. QuickJS CREATE bills the
invocation-fuel ceiling. TestEnv keeps the log rewriter on a shared
rewrite table. Wasmtime i32 results are kind-checked. Drop unused
pre-GuestMemory HOOK_SETUP/DECLARE/DEFINE copies from Macro.h. Docker
no longer skips jshookz; e2e also runs on pull_request.
2026-08-24 12:24:44 +07:00
Nicholas Dudfield
b9d0434e96 chore(hooks): pin provider after xahau-types and CFUNC rewrite
Seal lock to 6d05e793 (941714). Bytecode ABI unchanged. Four
HookInstructionCount ratchets step by one with the new wasm.
2026-08-18 12:34:39 +07:00
Nicholas Dudfield
3d5844e0ed ci(hooks): install pinned Wizer 10 in JSHookz e2e
Copy the provider workflow's wizer-v10.0.0 linux tarball install.
Without it, build provider dies after cmake with "wizer not found".
Also clang-format 18.1.8 the TestEnv / JSHooks files CI rejected.
2026-08-17 20:20:33 +07:00
Nicholas Dudfield
ad85d170c6 ci(hooks): check out jshookz main instead of a SHA
The sealed lock is the pin. CI builds whatever is on jshookz main
and fails if it does not reproduce the checked-in manifests. The
snapshot importer no longer rewrites JSHOOKZ_REF.
2026-08-17 20:10:08 +07:00
Nicholas Dudfield
32baf652ee chore(hooks): pin Wizered QuickJS provider snapshot
Seal the runtime-profile lock to jshookz 5d00e66 (wasm 6cbe9383,
941701, memory min 6). Bytecode ABI is unchanged. Default provider
build Wizers from jshookz_provider.unwizered.wasm.
2026-08-17 20:06:49 +07:00
Nicholas Dudfield
7bf8cd75e5 build(hooks): project provider values at build time
Replace configure-time header/embed generation with a static
QuickJSProviderProfile.h and one generated QuickJSProviderValues.cpp.
CMake only EXISTS-checks the three lock files. A refresh target plus
fingerprint stamp sees wasm appear or change without reconfigure.

The snapshot importer accepts python/jshookz or packages/jshookz.
2026-08-17 19:17:26 +07:00
Nicholas Dudfield
e910177838 chore(hooks): update JSHookz provider snapshot
Pin the sealed runtime-profile lock to local product 220bfdf
(wasm 9a0ff771, 852299). Bytecode ABI is unchanged; runtime
profile is now f606dec6. The e2e workflow finds python/jshookz
after the producer flatten. The wasm stays gitignored.

CI JSHOOKZ_REF is unpublished until that product commit is on
origin.
2026-08-17 17:33:57 +07:00
Nicholas Dudfield
d1cd4b22f0 fix(logs): give TestEnv a null-check rewriter
setTransform is installed in the TestEnv constructor and cleared
in the destructor. The empty path is a null check; SuiteJournal
writes the original string when nothing is installed.
2026-08-17 15:49:42 +07:00
Nicholas Dudfield
277bad787d test(hooks): add TestEnv, HooksTrace, and external hook-test wiring
TestEnv names accounts, prefixes phases, and honors XAHAU_TEST_LOG
(TESTENV_LOGGING alias). Logs::applyTransform copies the rewriter
under the existing mutex so suite journals can rewrite r-addresses
without racing setTransform. Hook host traces move off the View
journal onto a HooksTrace partition.

HOOKS_TEST_DIR / HOOKS_SOURCE_DIR pull producer-side *_test.cpp
into the rippled test binary. Coverage is not wired.
2026-08-17 15:14:01 +07:00
Nicholas Dudfield
dce17e2fa6 test(hooks): move JSHooks fixtures onto current v1 guest
Refresh the sealed provider pin to local product a48fb8a (wasm
95c40c7b, 852299). Guest sources now use rollback.onFail, total
hook.account(), STBlob.fromHex, CallbackInfo, and
//@jshookz-allow-malformed for undeployable modules. Regenerated
the bytecode header and fuel ratchets from a local JSHooks run
(17 cases, 343 tests). BEAST_EXPECT_EQ prints both sides.

CI JSHOOKZ_REF is that unpublished SHA; do not push until it is
on origin.
2026-08-17 15:13:15 +07:00
Nicholas Dudfield
e6403f1596 docs(hooks): launch contract names the daemon wiring as built 2026-08-15 18:46:50 +07:00
Nicholas Dudfield
ee0b677c2e chore(hooks): match embed generator file mode to its sibling script 2026-08-15 18:44:29 +07:00
Nicholas Dudfield
a4a43578de test(hooks): cost seam rejects failed validation; assert every stage ran
measureQuickJSSessionCostForTests now treats a validate call that did
not return callable flags (1 or 3) as an error instead of sampling it,
and the measuring case asserts each stage's minimum wall time is
non-zero, so a skipped stage or a rejecting session turns the case red
without pinning any number (grok, b6b57e3ec review).
2026-08-15 18:38:41 +07:00
Nicholas Dudfield
b6b57e3ec3 test(hooks): measure per-session quickjs provider cost
ENABLE_TESTS seam measureQuickJSSessionCostForTests runs N cold sessions
exactly as validation builds them (create → initialize → arm invocation
fuel → validate) and reports wall time per stage (sum and min) plus the
initialization and invocation fuel consumed; ProviderSession gains
initializationFuelConsumed beside invocationFuelConsumed. JSHooks_test
logs the figures (issue 0001 step 1: measure before changing provider
bytes) and asserts only the shape.

on this mac, release, live producer bundle: ~200us per cold session
(create 34us, initialize 157us, validate 6us), init fuel 1800041 of
5000000, invocation fuel 49832 of 50000000.
2026-08-15 18:12:45 +07:00
Nicholas Dudfield
41400f1cc6 build(hooks): never let a stale empty provider embed survive a bundle change
a stub written by a bundle-less configure is newer than a wasm copied in
afterwards, so ninja could skip the embed and the daemon would warn and
stay up. when the wasm is present, configure now drops an existing stub
(recognised by its provenance line) so the custom command must run; the
absent case is a configure-time warning instead of a status line. found by
grok's review of 77a1c2d65; verified by writing a stub, reconfiguring, and
watching the embed regenerate with the real bytes.
2026-08-15 18:09:32 +07:00
Nicholas Dudfield
77a1c2d65d feat(hooks): embed sealed quickjs provider and register it at daemon startup
cmake/EmbedQuickJSProvider.py projects the bundle's provider wasm into a
generated translation unit (one escaped string literal; ~0.7s to compile)
defining hook::embeddedQuickJSProvider(), re-verifying sha-256 and size
against the manifest at build time; a bundle without the binary embeds an
empty span. QuickJSProvider.cmake wires the custom command (or the absent
stub) and RippledCore adds the generated source to rippled.

the daemon path in Main.cpp launches the background registration before
Application::setup, awaits it after setup and before start, and treats a
refused launch or a failed registration as fatal, per the registry
contract: a node that cannot register its sealed provider must not stay
up. an empty embed logs a warning and continues. jtx::Env does not pass
through Main, so unit tests keep an unregistered current profile.

standalone probe on this machine: registered 1031425 bytes, awaited
135 ms after launch, before "Application starting".
2026-08-15 17:53:17 +07:00
Nicholas Dudfield
e4f4b28e0f fix(hooks): harden background provider registration per grok review
one static RegistryState holds mutex, runtimes and pending together so
teardown no longer depends on which accessor a process touches first
(error()-first then launch destroyed the registry before the pending
join). the pending entry is reserved under the lock with a promise-backed
future and the compile thread is started outside it and detached, so no
path can wait on the compile while holding the registry mutex and nothing
joins at exit; the worker catches and retains exceptions instead of
settling with an empty error.

launch now returns the refusal reason: mismatched profile against a
registered or launched identity, or a terminally failed earlier launch
(its retained error); same-profile relaunch stays a no-op.
quickJSRuntimeRegistrationError becomes awaitQuickJSRuntimeRegistration,
which is nullopt only when the identity is registered with this profile
and otherwise says why not (retained failure, different profile, or
never launched). header states the contract: launch returns before any
find/await, await before shutdown, failure is fatal at startup.

first tests for the lane on distinct runtimeProfile identities: launch
with concurrent finds agreeing on one handle, same/mismatched relaunch,
and a corrupted launch retaining its error and refusing relaunch.
2026-08-15 17:39:19 +07:00
Nicholas Dudfield
9870e5df67 feat(hooks): background quickjs provider compilation at startup
launchQuickJSRuntimeRegistration compiles/registers a provider on a
startup thread (std::async; the shared state joins on teardown so a
half-registered compile cannot be abandoned). findQuickJSRuntime waits
for a pending registration of the same identity before answering, so
background compilation is invisible to resolution except as startup
latency; failed identities retain their error via
quickJSRuntimeRegistrationError. in-memory only: no serialization, no
cache, nothing consensus-visible — the input is still the sha-verified
provider bytes (ai-docs issue 0001, ruled simplest step)
2026-08-15 17:13:25 +07:00
Nicholas Dudfield
dfc2a8ed83 chore(hooks): update JSHookz provider snapshot 2026-08-14 15:52:10 +07:00
Nicholas Dudfield
9de7ba734d chore(hooks): automate QuickJS provider snapshots 2026-08-14 15:50:45 +07:00
Nicholas Dudfield
359c3e7990 ci(hooks): update QuickJS policy test filter 2026-08-13 21:18:47 +07:00
Nicholas Dudfield
650b2d6e1c refactor(hooks): remove duplicate QuickJS catalogue 2026-08-13 20:18:52 +07:00
Nicholas Dudfield
94c61cc626 refactor(hooks): remove unused import categories 2026-08-13 19:58:10 +07:00
Nicholas Dudfield
b7881302e3 refactor(hooks): share host bodies with QuickJS 2026-08-13 19:46:03 +07:00
Nicholas Dudfield
4e87ccd924 chore(hooks): mark generated integration artifacts 2026-08-13 16:19:50 +07:00
Nicholas Dudfield
df86bebd5f test(hooks): focus proof on the QuickJS boundary 2026-08-13 16:19:36 +07:00
Nicholas Dudfield
670e77d4c0 refactor(hooks): simplify QuickJS guest memory 2026-08-13 16:18:53 +07:00
Nicholas Dudfield
ba54d7fd77 refactor(hooks): restore legacy WasmEdge execution 2026-08-13 15:50:18 +07:00
Nicholas Dudfield
78929910ee refactor(hooks): freeze QuickJS runtime policy 2026-08-13 14:50:05 +07:00
Nicholas Dudfield
3436c241e2 refactor(hooks): restore legacy host wrappers to applyHook 2026-08-13 10:42:31 +07:00
Nicholas Dudfield
398f6f01d7 ci: wait for both coverage reports 2026-08-13 09:11:11 +07:00
Nicholas Dudfield
aeea7a8251 ci(hooks): cover the shared host seam 2026-08-13 07:54:44 +07:00
Nicholas Dudfield
33510213d3 fix(hooks): reject null provider allocations 2026-08-13 07:54:38 +07:00
Nicholas Dudfield
bb136e616a fix(hooks): make admission and teardown deterministic 2026-08-13 07:31:19 +07:00
Nicholas Dudfield
e4ccc443c1 fix(ci): preserve Git checkout metadata 2026-08-13 07:13:35 +07:00
Nicholas Dudfield
0b38795617 ci(hooks): reuse Xahau build caches 2026-08-13 07:07:36 +07:00
Nicholas Dudfield
f3ed814dcf test(ci): report provider-backed coverage 2026-08-12 19:06:50 +07:00
Nicholas Dudfield
7c66f94a90 test(ci): exercise QuickJS provider in coverage 2026-08-12 18:40:48 +07:00
Nicholas Dudfield
240f33d398 fix(ci): refresh Wasmtime recipe 2026-08-12 18:21:24 +07:00
Nicholas Dudfield
a868a46848 fix(build): normalize Wasmtime package compiler 2026-08-12 18:14:01 +07:00
Nicholas Dudfield
b86112bca4 ci(hooks): defer HBB release packaging 2026-08-12 17:59:18 +07:00
Nicholas Dudfield
92832aac10 fix(ci): install server build dependencies 2026-08-12 17:51:39 +07:00
Nicholas Dudfield
8e7def3739 fix(hooks): keep hook macros in app layer 2026-08-12 17:51:32 +07:00
Nicholas Dudfield
552d1cf1e0 style(hooks): apply clang-format 2026-08-12 17:32:23 +07:00
Nicholas Dudfield
f35d8591d8 ci(hooks): prove jshookz integration 2026-08-12 17:31:24 +07:00
Nicholas Dudfield
124235d92e build(hooks): consume jshookz provider bundle 2026-08-12 16:42:23 +07:00
Nicholas Dudfield
56a456a7ff test(hooks): verify dual-runtime catalogue execution 2026-08-12 11:15:35 +07:00
Nicholas Dudfield
70814197f5 feat(hooks): expose raw Hook catalogue through Wasmtime 2026-08-12 11:13:45 +07:00
Nicholas Dudfield
d5334234fc build(hooks): pin Wasmtime provider dependencies 2026-08-12 11:13:23 +07:00
Nicholas Dudfield
d406e336a4 Factor shared Hook host operation seam 2026-08-11 16:10:11 +07:00
Nicholas Dudfield
4c58f4223c Route Hook installation through canonical artifacts 2026-08-11 15:30:22 +07:00
Nicholas Dudfield
2d48d21cc6 Refactor QuickJS Hook provider execution boundary 2026-08-11 13:01:46 +07:00
Nicholas Dudfield
7dd63aaac9 Add QuickJS Hook transaction integration 2026-08-11 12:24:41 +07:00
1405 changed files with 50255 additions and 58995 deletions

View File

@@ -1,21 +1,5 @@
---
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
---
Language: Cpp
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
@@ -34,41 +18,51 @@ AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: false
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 80
CommentPragmas: "^ IWYU pragma:"
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [Q_FOREACH, BOOST_FOREACH]
IncludeBlocks: Regroup
ForEachMacros: [ Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: "^<(test)/"
Priority: 0
- Regex: "^<(xrpld)/"
Priority: 1
- Regex: "^<(xrpl)/"
Priority: 2
- Regex: "^<(boost)/"
Priority: 3
- Regex: "^.*/"
Priority: 4
- Regex: '^.*\.h'
Priority: 5
- Regex: ".*"
Priority: 6
IncludeIsMainRegex: "$"
- Regex: '^<(test)/'
Priority: 0
- Regex: '^<(xrpld)/'
Priority: 1
- Regex: '^<(xrpl)/'
Priority: 2
- Regex: '^<(boost)/'
Priority: 3
- Regex: '.*'
Priority: 4
IncludeIsMainRegex: '$'
IndentCaseLabels: true
IndentFunctionDeclarationAfterType: false
IndentRequiresClause: true
IndentWidth: 4
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
@@ -79,25 +73,19 @@ PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: true
ReflowComments: true
RequiresClausePosition: OwnLine
SortIncludes: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
QualifierAlignment: Right
---
Language: Proto
BasedOnStyle: Google
ColumnLimit: 0
IndentWidth: 2
Standard: Cpp11
TabWidth: 8
UseTab: Never

View File

@@ -1,5 +1,8 @@
codecov:
require_ci_to_pass: true
# This integration branch uploads the normal suite and JSHookz E2E coverage.
notify:
after_n_builds: 2
comment:
behavior: default
@@ -27,12 +30,11 @@ github_checks:
parsers:
cobertura:
partials_as_hits: true
handle_missing_conditions: true
handle_missing_conditions : true
slack_app: false
ignore:
- "src/test/"
- "src/tests/"
- "include/xrpl/beast/test/"
- "include/xrpl/beast/unit_test/"

View File

@@ -23,9 +23,3 @@ e61880699997398f5a746e6c4034edc7632661f5
d25b5dcd568bb96c18e347d55fac10fe901a1bfb
# Reformat code with clang-format-18
02749feea88ce61c1f7eeb2d61a57d8ecf07ab11
# chore: Run prettier on all files (#5657) (rippled)
97f0747e103f13e26e45b731731059b32f7679ac
# chore: Reverts formatting changes to external files, adds formatting changes to proto files (#5711) (rippled)
b13370ac0d207217354f1fc1c29aef87769fb8a1
# chore: Fix file formatting (#5718) (rippled)
896b8c3b54a22b0497cb0d1ce95e1095f9a227ce

6
.gitattributes vendored
View File

@@ -11,3 +11,9 @@ LICENSE binary
*.vcxproj text eol=crlf
*.props text eol=crlf
*.filters text eol=crlf
# JSHookz projections checked for freshness by the integration workflow.
external/quickjs-provider/jshookz_provider.manifest.cmake linguist-generated=true
external/quickjs-provider/jshookz_provider.manifest.json linguist-generated=true
external/quickjs-provider/jshookz_provider.native-abi.json linguist-generated=true
src/test/app/JSHooks_test_hooks.h linguist-generated=true

View File

@@ -2,35 +2,30 @@
name: Bug Report
about: Create a report to help us improve rippled
title: "[Title with short description] (Version: [rippled version])"
labels: ""
assignees: ""
---
labels: ''
assignees: ''
---
<!-- Please search existing issues to avoid creating duplicates.-->
## Issue Description
<!--Provide a summary for your issue/bug.-->
## Steps to Reproduce
<!--List in detail the exact steps to reproduce the unexpected behavior of the software.-->
## Expected Result
<!--Explain in detail what behavior you expected to happen.-->
## Actual Result
<!--Explain in detail what behavior actually happened.-->
## Environment
<!--Please describe your environment setup (such as Ubuntu 18.04 with Boost 1.70).-->
<!-- If you are using a formal release, please use the version returned by './rippled --version' as the version number-->
<!-- If you are working off of develop, please add the git hash via 'git rev-parse HEAD'-->
## Supporting Files
<!--If you have supporting files such as a log, feel free to post a link here using Github Gist.-->
<!--Consider adding configuration files with private information removed via Github Gist. -->

View File

@@ -3,23 +3,19 @@ name: Feature Request
about: Suggest a new feature for the rippled project
title: "[Title with short description] (Version: [rippled version])"
labels: Feature Request
assignees: ""
---
assignees: ''
---
<!-- Please search existing issues to avoid creating duplicates.-->
## Summary
<!-- Provide a summary to the feature request-->
## Motivation
<!-- Why do we need this feature?-->
## Solution
<!-- What is the solution?-->
## Paths Not Taken
<!-- What other alternatives have been considered?-->

View File

@@ -33,7 +33,7 @@ inputs:
required: false
default: ''
compiler-id:
description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. gcc-13-libstdcxx)'
description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. clang-14-libstdcxx-gcc11, gcc-13-libstdcxx)'
required: false
default: ''
cache_version:
@@ -155,7 +155,7 @@ runs:
fi
# Configure C++ standard library if specified
# libstdcxx used for clang-16 to work around missing lexicographical_compare_three_way in libc++
# libstdcxx used for clang-14/16 to work around missing lexicographical_compare_three_way in libc++
# libcxx can be used with clang-17+ which has full C++20 support
# Note: -stdlib flag is Clang-specific, GCC always uses libstdc++
CMAKE_CXX_FLAGS=""
@@ -171,7 +171,7 @@ runs:
# Configure GCC toolchain for Clang if specified
if [ -n "${{ inputs.clang_gcc_toolchain }}" ] && [[ "${{ inputs.cxx }}" == clang* ]]; then
# Extract Clang version from compiler executable name (e.g., clang++-16 -> 16)
# Extract Clang version from compiler executable name (e.g., clang++-14 -> 14)
clang_version=$(echo "${{ inputs.cxx }}" | grep -oE '[0-9]+$')
# Clang 16+ supports --gcc-install-dir (precise path specification)

View File

@@ -145,6 +145,9 @@ runs:
# Export wasmedge if not already exported
conan list wasmedge/0.11.2@xahaud/stable 2>/dev/null | (grep -q "not found" && exit 1 || exit 0) || \
conan export external/wasmedge --version 0.11.2 --user xahaud --channel stable
# Keep the cached recipe in sync with this checkout; package binaries remain cached.
conan export external/wasmtime --version 47.0.3 --user xahaud --channel stable
- name: Install dependencies
shell: bash
env:
@@ -159,4 +162,6 @@ runs:
--output-folder . \
--build missing \
--settings build_type=${{ inputs.configuration }} \
--options '&:xrpld=True' \
--options '&:tests=True' \
..

View File

@@ -33,6 +33,7 @@ jobs:
fetch-depth: 2 # Only get the last 2 commits, to avoid fetching all history
build:
if: github.head_ref != 'jshookz'
runs-on: [self-hosted, xahaud-build]
needs: [checkout]
defaults:

View File

@@ -82,6 +82,9 @@ jobs:
command -v clang-format || (echo "Error: clang-format not found" && exit 1)
echo "All tools verified successfully"
- name: Verify independent Wasmtime host catalogue fixture
run: src/test/app/jshooks/raw-catalogue/generate.py --check
# Execute build script to regenerate xahau.h
- name: Run build_xahau_h.sh
run: |

View File

@@ -8,15 +8,6 @@ jobs:
env:
CLANG_VERSION: 18
steps:
# For jobs running in containers, $GITHUB_WORKSPACE and ${{ github.workspace }} might not be the
# same directory. The actions/checkout step is *supposed* to checkout into $GITHUB_WORKSPACE and
# then add it to safe.directory (see instructions at https://github.com/actions/checkout)
# but that's apparently not happening for some container images. We can't be sure what is actually
# happening, so let's pre-emptively add both directories to safe.directory. There's a
# Github issue opened in 2022 and not resolved in 2025 https://github.com/actions/runner/issues/2058 ¯\_(ツ)_/¯
- run: |
git config --global --add safe.directory $GITHUB_WORKSPACE
git config --global --add safe.directory ${{ github.workspace }}
- uses: actions/checkout@v4
- name: Install clang-format
run: |
@@ -29,7 +20,7 @@ jobs:
sudo apt-get update
sudo apt-get install clang-format-${CLANG_VERSION}
- name: Format first-party sources
run: find include src tests -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format-${CLANG_VERSION} -i {} +
run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format-${CLANG_VERSION} -i {} +
- name: Check for differences
id: assert
run: |

236
.github/workflows/jshookz-e2e.yml vendored Normal file
View File

@@ -0,0 +1,236 @@
# End-to-end proof for the external jshookz toolchain and real Xahau Env.
name: JSHookz end to end
on:
push:
branches: [jshookz]
# PRs targeting jshookz/dev currently duplicate the branch-push e2e
# (different github.ref, so concurrency does not cancel). Re-enable later.
workflow_dispatch:
permissions:
contents: read
id-token: write
concurrency:
group: jshookz-e2e-${{ github.ref }}
cancel-in-progress: true
env:
JSHOOKZ_REF: xahaud-pinned
HOOKZ_REF: 846f5f0f80861985dceb41ad6f02ff914f8a0c3d
XAHAUD_SCRIPTS_REF: e41a712c68a613d95a8911cbf89711db2d95aa3b
WASI_SDK_VERSION: "32"
WASI_SDK_SHA256: 55fc523ebfbc98f69d1034fcfcb83d1ff5610cd9ab7eceef6cd097a30ba4ef93
BINARYEN_VERSION: "128"
BINARYEN_SHA256: 4ce79586d1c4762502eebe9a1db071fa5e446ef8897f2f766eb1cce5ec6dee9e
WIZER_VERSION: "10.0.0"
WIZER_SHA256: 1e9dfaa2f6c436ca7acfdbb82c166f5b8a0062068f12916a554e82c97ca8d371
WASI_SDK_PATH: ${{ github.workspace }}/.tools/wasi-sdk
HOOKZ_WASI_SDK: ${{ github.workspace }}/.tools/wasi-sdk
BINARYEN_HOME: ${{ github.workspace }}/.tools/binaryen
JSHOOKZ_PROVIDER_BUNDLE: ${{ github.workspace }}/.deps/jshookz/build/xahau-provider
XAHAU_QJS_PROVIDER_WASM: ${{ github.workspace }}/.deps/jshookz/build/xahau-provider/jshookz_provider.wasm
XAHAU_REQUIRE_QJS_PROVIDER_TESTS: "1"
CC: gcc-13
CXX: g++-13
CCACHE_DIR: /github/home/.ccache-cache
CMAKE_BUILD_PARALLEL_LEVEL: "2"
jobs:
env-proof:
runs-on: [self-hosted, generic, 20.04]
timeout-minutes: 120
container:
image: ubuntu:24.04
volumes:
- /home/runner/.conan-cache:/.conan-cache
- /home/runner/.ccache-cache:/github/home/.ccache-cache
defaults:
run:
shell: bash
steps:
- name: Install checkout prerequisite
run: |
apt-get update
apt-get install -y git
- name: Check out Xahau integration
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Check out public jshookz
uses: actions/checkout@v4
with:
repository: sublimator/jshookz
ref: ${{ env.JSHOOKZ_REF }}
path: .deps/jshookz
persist-credentials: false
- name: Check out public hookz
uses: actions/checkout@v4
with:
repository: sublimator/hookz
ref: ${{ env.HOOKZ_REF }}
path: .deps/hookz
persist-credentials: false
- name: Check out public xahaud-scripts
uses: actions/checkout@v4
with:
repository: sublimator/xahaud-scripts
ref: ${{ env.XAHAUD_SCRIPTS_REF }}
path: .deps/xahaud-scripts
persist-credentials: false
- uses: astral-sh/setup-uv@v7
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install native and TypeScript tools
run: |
apt-get install -y \
build-essential ccache clang-format-18 cmake curl g++-13 gcc-13 \
libsqlite3-dev ninja-build pkg-config xz-utils
ln -sf /usr/bin/clang-format-18 /usr/local/bin/clang-format
ln -sf /usr/bin/gcov-13 /usr/local/bin/gcov
npm install --global typescript@6.0.3
uv python install 3.13
uv tool install --python 3.13 conan==2.21.0
uv tool install --python 3.13 'gcovr>=7,<9'
- name: Install wasi-sdk and Binaryen
run: |
mkdir -p .tools
curl --fail --location --retry 3 \
--output /tmp/wasi-sdk.tar.gz \
"https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.tar.gz"
echo "$WASI_SDK_SHA256 /tmp/wasi-sdk.tar.gz" | sha256sum --check
tar -xzf /tmp/wasi-sdk.tar.gz -C .tools
mv ".tools/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux" "$WASI_SDK_PATH"
curl --fail --location --retry 3 \
--output /tmp/binaryen.tar.gz \
"https://github.com/WebAssembly/binaryen/releases/download/version_${BINARYEN_VERSION}/binaryen-version_${BINARYEN_VERSION}-x86_64-linux.tar.gz"
echo "$BINARYEN_SHA256 /tmp/binaryen.tar.gz" | sha256sum --check
tar -xzf /tmp/binaryen.tar.gz -C .tools
mv ".tools/binaryen-version_${BINARYEN_VERSION}" "$BINARYEN_HOME"
echo "$BINARYEN_HOME/bin" >> "$GITHUB_PATH"
- name: Install Wizer
run: |
curl --fail --location --retry 3 \
--output /tmp/wizer.tar.xz \
"https://github.com/bytecodealliance/wizer/releases/download/v${WIZER_VERSION}/wizer-v${WIZER_VERSION}-x86_64-linux.tar.xz"
echo "$WIZER_SHA256 /tmp/wizer.tar.xz" | sha256sum --check
tar -xJf /tmp/wizer.tar.xz -C .tools
echo "$GITHUB_WORKSPACE/.tools/wizer-v${WIZER_VERSION}-x86_64-linux" >> "$GITHUB_PATH"
- name: Install the Hook toolchain
run: |
if [ -f .deps/jshookz/python/jshookz/pyproject.toml ]; then
jshookz_project=.deps/jshookz/python/jshookz
else
jshookz_project=.deps/jshookz/packages/jshookz
fi
uv sync --project "$jshookz_project" \
--locked --group dev --python 3.13
uv tool install --python 3.13 --force --from .deps/hookz hookz
uv tool install --python 3.13 --force \
--from .deps/xahaud-scripts xahaud-scripts
jshookz_cmd="$jshookz_project/.venv/bin/jshookz"
echo "JSHOOKZ_CMD=$jshookz_cmd" >> "$GITHUB_ENV"
echo "JSHOOKZ_HOOK_COMPILER=$jshookz_cmd compile-hook" >> "$GITHUB_ENV"
"$jshookz_cmd" info
hookz --version
x-run-tests --help >/dev/null
- name: Build and verify the sealed provider
run: |
"$JSHOOKZ_CMD" build provider
test -s "$XAHAU_QJS_PROVIDER_WASM"
cmp external/quickjs-provider/jshookz_provider.manifest.json \
"$JSHOOKZ_PROVIDER_BUNDLE/jshookz_provider.manifest.json"
cmp external/quickjs-provider/jshookz_provider.manifest.cmake \
"$JSHOOKZ_PROVIDER_BUNDLE/jshookz_provider.manifest.cmake"
cmp external/quickjs-provider/jshookz_provider.native-abi.json \
"$JSHOOKZ_PROVIDER_BUNDLE/jshookz_provider.native-abi.json"
cp "$XAHAU_QJS_PROVIDER_WASM" \
external/quickjs-provider/jshookz_provider.wasm
- name: Prove generator-lock mutation reds
run: python3 cmake/test_generate_quickjs_provider_bundle.py
- name: Configure Conan for Xahau
run: |
mkdir -p \
/.conan-cache/conan2 \
/.conan-cache/conan2_download \
/.conan-cache/conan2_sources
mkdir -p "$HOME/.conan2"
printf '%s\n' \
'core.cache:storage_path=/.conan-cache/conan2' \
'core.download:download_cache=/.conan-cache/conan2_download' \
'core.sources:download_cache=/.conan-cache/conan2_sources' \
> "$HOME/.conan2/global.conf"
mkdir -p "$HOME/.conan2/profiles"
cat > "$HOME/.conan2/profiles/default" <<'EOF'
[settings]
arch=x86_64
build_type=Debug
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=13
os=Linux
[buildenv]
CC=/usr/bin/gcc-13
CXX=/usr/bin/g++-13
[conf]
tools.build:compiler_executables={"c": "/usr/bin/gcc-13", "cpp": "/usr/bin/g++-13"}
[options]
&:xrpld=True
&:tests=True
date/*:use_system_tz_db=True
EOF
conan remote add --force xrplf https://conan.ripplex.io
conan profile show
conan export external/snappy --version 1.1.10 \
--user xahaud --channel stable
conan export external/soci --version 4.0.3 \
--user xahaud --channel stable
conan export external/wasmedge --version 0.11.2 \
--user xahaud --channel stable
conan export external/wasmtime --version 47.0.3 \
--user xahaud --channel stable
- name: Build Xahau and run the authoritative Env suite
run: |
x-run-tests \
--compile-hooks src/test/app/JSHooks_test.cpp \
--build-type Coverage \
--build-dir build-jshookz-e2e \
--reconfigure-build \
--ccache \
-j 2 \
-- \
ripple.app.JSHooks,ripple.app.QuickJSHostPolicy,ripple.app.HookGuestMemory,ripple.app.HookAPI,ripple.app.HookWasmtimeHostCatalogue,ripple.app.HookArtifact
- name: Upload JSHookz coverage
uses: codecov/codecov-action@v5
with:
files: build-jshookz-e2e/coverage/coverage.json
fail_ci_if_error: true
disable_search: true
verbose: true
plugins: noop
use_oidc: true
- name: Verify the checked-in fixture header is current
run: git diff --exit-code -- src/test/app/JSHooks_test_hooks.h

View File

@@ -122,6 +122,4 @@ jobs:
- name: Test
run: |
cd ${{ env.build_dir }}
./rippled --unittest --unittest-jobs $(nproc)
ctest -j $(nproc) --output-on-failure
${{ env.build_dir }}/rippled --unittest --unittest-jobs $(nproc)

View File

@@ -82,6 +82,17 @@ jobs:
"configuration": "Debug",
"job_type": "coverage"
},
{
"compiler_id": "clang-14-libstdcxx-gcc11",
"compiler": "clang",
"cc": "clang-14",
"cxx": "clang++-14",
"compiler_version": 14,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 11,
"configuration": "Debug",
"job_type": "build"
},
{
"compiler_id": "clang-16-libstdcxx-gcc13",
"compiler": "clang",
@@ -121,7 +132,7 @@ jobs:
minimal_matrix = [
full_matrix[1], # gcc-13 (middle-ground gcc)
full_matrix[2], # gcc-13 coverage
full_matrix[3] # clang-16 (mature, stable clang)
full_matrix[3] # clang-14 (mature, stable clang)
]
# Determine which matrix to use based on the target branch
@@ -422,9 +433,7 @@ jobs:
run: |
# Ensure the binary exists before trying to run
if [ -f "${{ env.build_dir }}/rippled" ]; then
cd ${{ env.build_dir }}
./rippled --unittest --unittest-jobs $(nproc)
ctest -j $(nproc) --output-on-failure
${{ env.build_dir }}/rippled --unittest --unittest-jobs $(nproc)
else
echo "Error: rippled executable not found in ${{ env.build_dir }}"
exit 1

View File

@@ -1,38 +1,6 @@
# To run pre-commit hooks, first install pre-commit:
# - `pip install pre-commit==${PRE_COMMIT_VERSION}`
#
# Then, run the following command to install the git hook scripts:
# - `pre-commit install`
# You can run all configured hooks against all files with:
# - `pre-commit run --all-files`
# To manually run a specific hook, use:
# - `pre-commit run <hook_id> --all-files`
# To run the hooks against only the staged files, use:
# - `pre-commit run`
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: mixed-line-ending
- id: check-merge-conflict
args: [--assume-in-merge]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 7d85583be209cb547946c82fbe51f4bc5dd1d017 # frozen: v18.1.8
hooks:
- id: clang-format
args: [--style=file]
"types_or": [c++, c, proto]
- repo: https://github.com/rbubley/mirrors-prettier
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
hooks:
- id: prettier
exclude: |
(?x)^(
external/.*|
Builds/scripts/levelization/results/.*\.txt
)$
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v18.1.3
hooks:
- id: clang-format

View File

@@ -1 +0,0 @@
external

View File

@@ -83,17 +83,9 @@ The [commandline](https://xrpl.org/docs/references/http-websocket-apis/api-conve
The `network_id` field was added in the `server_info` response in version 1.5.0 (2019), but it is not returned in [reporting mode](https://xrpl.org/rippled-server-modes.html#reporting-mode). However, use of reporting mode is now discouraged, in favor of using [Clio](https://github.com/XRPLF/clio) instead.
## XRP Ledger server version 2.5.0
As of 2025-04-04, version 2.5.0 is in development. You can use a pre-release version by building from source or [using the `nightly` package](https://xrpl.org/docs/infrastructure/installation/install-rippled-on-ubuntu).
### Additions and bugfixes in 2.5.0
- `channel_authorize`: If `signing_support` is not enabled in the config, the RPC is disabled.
## XRP Ledger server version 2.4.0
[Version 2.4.0](https://github.com/XRPLF/rippled/releases/tag/2.4.0) was released on March 4, 2025.
As of 2025-01-28, version 2.4.0 is in development. You can use a pre-release version by building from source or [using the `nightly` package](https://xrpl.org/docs/infrastructure/installation/install-rippled-on-ubuntu).
### Additions and bugfixes in 2.4.0

138
BUILD.md
View File

@@ -35,7 +35,7 @@ See [System Requirements](https://xrpl.org/system-requirements.html).
Building rippled generally requires git, Python, Conan, CMake, and a C++ compiler. Some guidance on setting up such a [C++ development environment can be found here](./docs/build/environment.md).
- [Python 3.7](https://www.python.org/downloads/)
- [Python 3.7](https://www.python.org/downloads/) (configure also uses it to project the QuickJS provider)
- [Conan 2.x](https://conan.io/downloads)
- [CMake 3.16](https://cmake.org/download/)
@@ -84,9 +84,9 @@ If you are unfamiliar with Conan, then please read [this crash course](./docs/bu
You'll need at least one Conan profile:
```
conan profile detect --force
```
```
conan profile detect --force
```
Update the compiler settings:
@@ -182,6 +182,16 @@ which allows you to statically link it with GCC, if you want.
conan export external/snappy --version 1.1.10 --user xahaud --channel stable
```
Export our [Conan recipe for RocksDB](./external/rocksdb).
It does not override paths to dependencies when building with Visual Studio.
```
# Conan 1.x
conan export external/rocksdb rocksdb/6.29.5@
# Conan 2.x
conan export --version 6.29.5 external/rocksdb
```
Export our [Conan recipe for SOCI](./external/soci).
It patches their CMake to correctly import its dependencies.
@@ -195,6 +205,17 @@ It patches their CMake to correctly import its dependencies.
conan export external/wasmedge --version 0.11.2 --user xahaud --channel stable
```
Export our [Conan recipe for NuDB](./external/nudb).
It fixes some source files to add missing `#include`s.
```
# Conan 1.x
conan export external/nudb nudb/2.0.8@
# Conan 2.x
conan export --version 2.0.8 external/nudb
```
### Build and Test
1. Create a build directory and move into it.
@@ -215,60 +236,60 @@ It patches their CMake to correctly import its dependencies.
2. Use conan to generate CMake files for every configuration you want to build:
```
conan install .. --output-folder . --build missing --settings build_type=Release
conan install .. --output-folder . --build missing --settings build_type=Debug
```
```
conan install .. --output-folder . --build missing --settings build_type=Release
conan install .. --output-folder . --build missing --settings build_type=Debug
```
To build Debug, in the next step, be sure to set `-DCMAKE_BUILD_TYPE=Debug`
To build Debug, in the next step, be sure to set `-DCMAKE_BUILD_TYPE=Debug`
For a single-configuration generator, e.g. `Unix Makefiles` or `Ninja`,
you only need to run this command once.
For a multi-configuration generator, e.g. `Visual Studio`, you may want to
run it more than once.
For a single-configuration generator, e.g. `Unix Makefiles` or `Ninja`,
you only need to run this command once.
For a multi-configuration generator, e.g. `Visual Studio`, you may want to
run it more than once.
Each of these commands should also have a different `build_type` setting.
A second command with the same `build_type` setting will overwrite the files
generated by the first. You can pass the build type on the command line with
`--settings build_type=$BUILD_TYPE` or in the profile itself,
under the section `[settings]` with the key `build_type`.
Each of these commands should also have a different `build_type` setting.
A second command with the same `build_type` setting will overwrite the files
generated by the first. You can pass the build type on the command line with
`--settings build_type=$BUILD_TYPE` or in the profile itself,
under the section `[settings]` with the key `build_type`.
If you are using a Microsoft Visual C++ compiler,
then you will need to ensure consistency between the `build_type` setting
and the `compiler.runtime` setting.
If you are using a Microsoft Visual C++ compiler,
then you will need to ensure consistency between the `build_type` setting
and the `compiler.runtime` setting.
When `build_type` is `Release`, `compiler.runtime` should be `MT`.
When `build_type` is `Release`, `compiler.runtime` should be `MT`.
When `build_type` is `Debug`, `compiler.runtime` should be `MTd`.
When `build_type` is `Debug`, `compiler.runtime` should be `MTd`.
```
conan install .. --output-folder . --build missing --settings build_type=Release --settings compiler.runtime=MT
conan install .. --output-folder . --build missing --settings build_type=Debug --settings compiler.runtime=MTd
```
```
conan install .. --output-folder . --build missing --settings build_type=Release --settings compiler.runtime=MT
conan install .. --output-folder . --build missing --settings build_type=Debug --settings compiler.runtime=MTd
```
3. Configure CMake and pass the toolchain file generated by Conan, located at
`$OUTPUT_FOLDER/build/generators/conan_toolchain.cmake`.
Single-config generators:
Single-config generators:
Pass the CMake variable [`CMAKE_BUILD_TYPE`][build_type]
and make sure it matches the one of the `build_type` settings
you chose in the previous step.
Pass the CMake variable [`CMAKE_BUILD_TYPE`][build_type]
and make sure it matches the one of the `build_type` settings
you chose in the previous step.
For example, to build Debug, in the next command, replace "Release" with "Debug"
For example, to build Debug, in the next command, replace "Release" with "Debug"
```
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release -Dxrpld=ON -Dtests=ON ..
```
```
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release -Dxrpld=ON -Dtests=ON ..
```
Multi-config generators:
Multi-config generators:
```
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -Dxrpld=ON -Dtests=ON ..
```
```
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -Dxrpld=ON -Dtests=ON ..
```
**Note:** You can pass build options for `xahaud` in this step.
**Note:** You can pass build options for `xahaud` in this step.
4. Build `xahaud`.
@@ -280,7 +301,7 @@ It patches their CMake to correctly import its dependencies.
Single-config generators:
```
cmake --build . -j $(nproc)
cmake --build .
```
Multi-config generators:
@@ -308,6 +329,7 @@ It patches their CMake to correctly import its dependencies.
The location of `xahaud` in your build directory depends on your CMake
generator. Pass `--help` to see the rest of the command line options.
## Coverage report
The coverage report is intended for developers using compilers GCC
@@ -347,7 +369,7 @@ variable in `cmake`. The specific command line used to run the `gcovr` tool will
displayed if the `CODE_COVERAGE_VERBOSE` variable is set.
By default, the code coverage tool runs parallel unit tests with `--unittest-jobs`
set to the number of available CPU cores. This may cause spurious test
set to the number of available CPU cores. This may cause spurious test
errors on Apple. Developers can override the number of unit test jobs with
the `coverage_test_parallelism` variable in `cmake`.
@@ -366,24 +388,24 @@ stored inside the build directory, as either of:
- file named `coverage.`_extension_ , with a suitable extension for the report format, or
- directory named `coverage`, with the `index.html` and other files inside, for the `html-details` or `html-nested` report formats.
## Options
| Option | Default Value | Description |
| ---------- | ------------- | -------------------------------------------------------------------------- |
| `assert` | OFF | Enable assertions. |
| `coverage` | OFF | Prepare the coverage report. |
| `san` | N/A | Enable a sanitizer with Clang. Choices are `thread` and `address`. |
| `tests` | OFF | Build tests. |
| `unity` | OFF | Configure a unity build. |
| `xrpld` | OFF | Build the xrpld (`rippled`) application, and not just the libxrpl library. |
| `werr` | OFF | Treat compilation warnings as errors |
| `wextra` | OFF | Enable additional compilation warnings |
| Option | Default Value | Description |
| --- | ---| ---|
| `assert` | OFF | Enable assertions.
| `coverage` | OFF | Prepare the coverage report. |
| `san` | N/A | Enable a sanitizer with Clang. Choices are `thread` and `address`. |
| `tests` | OFF | Build tests. |
| `unity` | ON | Configure a unity build. |
| `xrpld` | OFF | Build the xrpld (`rippled`) application, and not just the libxrpl library. |
[Unity builds][5] may be faster for the first build
(at the cost of much more memory) since they concatenate sources into fewer
translation units. Non-unity builds may be faster for incremental builds,
and can be helpful for detecting `#include` omissions.
## Troubleshooting
@@ -453,13 +475,13 @@ If you want to experiment with a new package, follow these steps:
1. Search for the package on [Conan Center](https://conan.io/center/).
2. Modify [`conanfile.py`](./conanfile.py):
- Add a version of the package to the `requires` property.
- Change any default options for the package by adding them to the
`default_options` property (with syntax `'$package:$option': $value`).
- Add a version of the package to the `requires` property.
- Change any default options for the package by adding them to the
`default_options` property (with syntax `'$package:$option': $value`).
3. Modify [`CMakeLists.txt`](./CMakeLists.txt):
- Add a call to `find_package($package REQUIRED)`.
- Link a library from the package to the target `ripple_libs`
(search for the existing call to `target_link_libraries(ripple_libs INTERFACE ...)`).
- Add a call to `find_package($package REQUIRED)`.
- Link a library from the package to the target `ripple_libs`
(search for the existing call to `target_link_libraries(ripple_libs INTERFACE ...)`).
4. Start coding! Don't forget to include whatever headers you need from the package.

View File

@@ -25,28 +25,28 @@ more dependencies listed later.
**tl;dr:** The modules listed first are more independent than the modules
listed later.
| Level / Tier | Module(s) |
| ------------ | -------------------------------------------------------------------------------------------------------- |
| 01 | ripple/beast ripple/unity |
| 02 | ripple/basics |
| 03 | ripple/json ripple/crypto |
| 04 | ripple/protocol |
| 05 | ripple/core ripple/conditions ripple/consensus ripple/resource ripple/server |
| 06 | ripple/peerfinder ripple/ledger ripple/nodestore ripple/net |
| 07 | ripple/shamap ripple/overlay |
| 08 | ripple/app |
| 09 | ripple/rpc |
| 10 | ripple/perflog |
| 11 | test/jtx test/beast test/csf |
| 12 | test/unit_test |
| 13 | test/crypto test/conditions test/json test/resource test/shamap test/peerfinder test/basics test/overlay |
| 14 | test |
| 15 | test/net test/protocol test/ledger test/consensus test/core test/server test/nodestore |
| 16 | test/rpc test/app |
| Level / Tier | Module(s) |
|--------------|-----------------------------------------------|
| 01 | ripple/beast ripple/unity
| 02 | ripple/basics
| 03 | ripple/json ripple/crypto
| 04 | ripple/protocol
| 05 | ripple/core ripple/conditions ripple/consensus ripple/resource ripple/server
| 06 | ripple/peerfinder ripple/ledger ripple/nodestore ripple/net
| 07 | ripple/shamap ripple/overlay
| 08 | ripple/app
| 09 | ripple/rpc
| 10 | ripple/perflog
| 11 | test/jtx test/beast test/csf
| 12 | test/unit_test
| 13 | test/crypto test/conditions test/json test/resource test/shamap test/peerfinder test/basics test/overlay
| 14 | test
| 15 | test/net test/protocol test/ledger test/consensus test/core test/server test/nodestore
| 16 | test/rpc test/app
(Note that `test` levelization is _much_ less important and _much_ less
(Note that `test` levelization is *much* less important and *much* less
strictly enforced than `ripple` levelization, other than the requirement
that `test` code should _never_ be included in `ripple` code.)
that `test` code should *never* be included in `ripple` code.)
## Validation
@@ -59,48 +59,48 @@ the rippled source. The only caveat is that it runs much slower
under Windows than in Linux. It hasn't yet been tested under MacOS.
It generates many files of [results](results):
- `rawincludes.txt`: The raw dump of the `#includes`
- `paths.txt`: A second dump grouping the source module
* `rawincludes.txt`: The raw dump of the `#includes`
* `paths.txt`: A second dump grouping the source module
to the destination module, deduped, and with frequency counts.
- `includes/`: A directory where each file represents a module and
* `includes/`: A directory where each file represents a module and
contains a list of modules and counts that the module _includes_.
- `includedby/`: Similar to `includes/`, but the other way around. Each
* `includedby/`: Similar to `includes/`, but the other way around. Each
file represents a module and contains a list of modules and counts
that _include_ the module.
- [`loops.txt`](results/loops.txt): A list of direct loops detected
* [`loops.txt`](results/loops.txt): A list of direct loops detected
between modules as they actually exist, as opposed to how they are
desired as described above. In a perfect repo, this file will be
empty.
This file is committed to the repo, and is used by the [levelization
Github workflow](../../.github/workflows/levelization.yml) to validate
that nothing changed.
- [`ordering.txt`](results/ordering.txt): A list showing relationships
* [`ordering.txt`](results/ordering.txt): A list showing relationships
between modules where there are no loops as they actually exist, as
opposed to how they are desired as described above.
This file is committed to the repo, and is used by the [levelization
Github workflow](../../.github/workflows/levelization.yml) to validate
that nothing changed.
- [`levelization.yml`](../../.github/workflows/levelization.yml)
* [`levelization.yml`](../../.github/workflows/levelization.yml)
Github Actions workflow to test that levelization loops haven't
changed. Unfortunately, if changes are detected, it can't tell if
changed. Unfortunately, if changes are detected, it can't tell if
they are improvements or not, so if you have resolved any issues or
done anything else to improve levelization, run `levelization.py`,
and commit the updated results.
The `loops.txt` and `ordering.txt` files relate the modules
The `loops.txt` and `ordering.txt` files relate the modules
using comparison signs, which indicate the number of times each
module is included in the other.
- `A > B` means that A should probably be at a higher level than B,
* `A > B` means that A should probably be at a higher level than B,
because B is included in A significantly more than A is included in B.
These results can be included in both `loops.txt` and `ordering.txt`.
Because `ordering.txt`only includes relationships where B is not
included in A at all, it will only include these types of results.
- `A ~= B` means that A and B are included in each other a different
* `A ~= B` means that A and B are included in each other a different
number of times, but the values are so close that the script can't
definitively say that one should be above the other. These results
will only be included in `loops.txt`.
- `A == B` means that A and B include each other the same number of
* `A == B` means that A and B include each other the same number of
times, so the script has no clue which should be higher. These results
will only be included in `loops.txt`.
@@ -110,5 +110,5 @@ get those details locally.
1. Run `levelization.py`
2. Grep the modules in `paths.txt`.
- For example, if a cycle is found `A ~= B`, simply `grep -w
A Builds/levelization/results/paths.txt | grep -w B`
* For example, if a cycle is found `A ~= B`, simply `grep -w
A Builds/levelization/results/paths.txt | grep -w B`

View File

@@ -16,14 +16,20 @@ Loop: xrpl.protocol xrpld.app
Loop: xrpld.app xrpld.core
xrpld.app > xrpld.core
Loop: xrpld.app xrpld.ledger
xrpld.app > xrpld.ledger
Loop: xrpld.app xrpld.net
xrpld.app > xrpld.net
Loop: xrpld.app xrpld.nodestore
xrpld.app > xrpld.nodestore
Loop: xrpld.app xrpld.overlay
xrpld.overlay > xrpld.app
xrpld.overlay ~= xrpld.app
Loop: xrpld.app xrpld.peerfinder
xrpld.peerfinder ~= xrpld.app
xrpld.app > xrpld.peerfinder
Loop: xrpld.app xrpld.rpc
xrpld.rpc > xrpld.app
@@ -31,9 +37,15 @@ Loop: xrpld.app xrpld.rpc
Loop: xrpld.app xrpld.shamap
xrpld.app > xrpld.shamap
Loop: xrpld.core xrpld.net
xrpld.net > xrpld.core
Loop: xrpld.core xrpld.perflog
xrpld.perflog == xrpld.core
Loop: xrpld.net xrpld.rpc
xrpld.rpc > xrpld.net
Loop: xrpld.overlay xrpld.rpc
xrpld.rpc ~= xrpld.overlay

View File

@@ -2,18 +2,11 @@ libxrpl.basics > xrpl.basics
libxrpl.crypto > xrpl.basics
libxrpl.json > xrpl.basics
libxrpl.json > xrpl.json
libxrpl.ledger > xrpl.basics
libxrpl.ledger > xrpl.json
libxrpl.ledger > xrpl.ledger
libxrpl.ledger > xrpl.protocol
libxrpl.net > xrpl.basics
libxrpl.net > xrpl.net
libxrpl.protocol > xrpl.basics
libxrpl.protocol > xrpl.hook
libxrpl.protocol > xrpl.json
libxrpl.protocol > xrpl.protocol
libxrpl.resource > xrpl.basics
libxrpl.resource > xrpl.json
libxrpl.resource > xrpl.resource
libxrpl.server > xrpl.basics
libxrpl.server > xrpl.json
@@ -24,12 +17,12 @@ test.app > test.unit_test
test.app > xrpl.basics
test.app > xrpld.app
test.app > xrpld.core
test.app > xrpld.ledger
test.app > xrpld.nodestore
test.app > xrpld.overlay
test.app > xrpld.rpc
test.app > xrpl.hook
test.app > xrpl.json
test.app > xrpl.ledger
test.app > xrpl.protocol
test.app > xrpl.resource
test.basics > test.jtx
@@ -49,8 +42,7 @@ test.consensus > xrpl.basics
test.consensus > xrpld.app
test.consensus > xrpld.consensus
test.consensus > xrpld.core
test.consensus > xrpl.json
test.consensus > xrpl.ledger
test.consensus > xrpld.ledger
test.consensus > xrpl.protocol
test.core > test.jtx
test.core > test.toplevel
@@ -68,12 +60,13 @@ test.json > test.jtx
test.json > xrpl.json
test.jtx > xrpl.basics
test.jtx > xrpld.app
test.jtx > xrpld.consensus
test.jtx > xrpld.core
test.jtx > xrpld.ledger
test.jtx > xrpld.net
test.jtx > xrpld.rpc
test.jtx > xrpl.hook
test.jtx > xrpl.json
test.jtx > xrpl.ledger
test.jtx > xrpl.net
test.jtx > xrpl.protocol
test.jtx > xrpl.resource
test.jtx > xrpl.server
@@ -82,7 +75,7 @@ test.ledger > test.toplevel
test.ledger > xrpl.basics
test.ledger > xrpld.app
test.ledger > xrpld.core
test.ledger > xrpl.ledger
test.ledger > xrpld.ledger
test.ledger > xrpl.protocol
test.nodestore > test.jtx
test.nodestore > test.toplevel
@@ -123,6 +116,7 @@ test.rpc > test.toplevel
test.rpc > xrpl.basics
test.rpc > xrpld.app
test.rpc > xrpld.core
test.rpc > xrpld.net
test.rpc > xrpld.overlay
test.rpc > xrpld.rpc
test.rpc > xrpld.shamap
@@ -147,15 +141,9 @@ test.shamap > xrpl.protocol
test.toplevel > test.csf
test.toplevel > xrpl.json
test.unit_test > xrpl.basics
tests.libxrpl > xrpl.basics
tests.libxrpl > xrpl.json
tests.libxrpl > xrpl.net
xrpl.hook > xrpl.basics
xrpl.hook > xrpl.protocol
xrpl.json > xrpl.basics
xrpl.ledger > xrpl.basics
xrpl.ledger > xrpl.protocol
xrpl.net > xrpl.basics
xrpl.protocol > xrpl.basics
xrpl.protocol > xrpl.json
xrpl.resource > xrpl.basics
@@ -171,8 +159,6 @@ xrpld.app > xrpld.consensus
xrpld.app > xrpld.perflog
xrpld.app > xrpl.hook
xrpld.app > xrpl.json
xrpld.app > xrpl.ledger
xrpld.app > xrpl.net
xrpld.app > xrpl.resource
xrpld.conditions > xrpl.basics
xrpld.conditions > xrpl.protocol
@@ -181,8 +167,15 @@ xrpld.consensus > xrpl.json
xrpld.consensus > xrpl.protocol
xrpld.core > xrpl.basics
xrpld.core > xrpl.json
xrpld.core > xrpl.net
xrpld.core > xrpl.protocol
xrpld.ledger > xrpl.basics
xrpld.ledger > xrpld.core
xrpld.ledger > xrpl.json
xrpld.ledger > xrpl.protocol
xrpld.net > xrpl.basics
xrpld.net > xrpl.json
xrpld.net > xrpl.protocol
xrpld.net > xrpl.resource
xrpld.nodestore > xrpl.basics
xrpld.nodestore > xrpld.core
xrpld.nodestore > xrpld.unity
@@ -201,13 +194,13 @@ xrpld.peerfinder > xrpld.core
xrpld.peerfinder > xrpl.protocol
xrpld.perflog > xrpl.basics
xrpld.perflog > xrpl.json
xrpld.perflog > xrpl.protocol
xrpld.rpc > xrpl.basics
xrpld.rpc > xrpld.core
xrpld.rpc > xrpld.ledger
xrpld.rpc > xrpld.nodestore
xrpld.rpc > xrpld.shamap
xrpld.rpc > xrpl.json
xrpld.rpc > xrpl.ledger
xrpld.rpc > xrpl.net
xrpld.rpc > xrpl.protocol
xrpld.rpc > xrpl.resource
xrpld.rpc > xrpl.server

View File

@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.16)
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -18,53 +18,52 @@ if(POLICY CMP0144)
cmake_policy(SET CMP0144 NEW)
endif()
project (xrpl)
project(xrpl)
set(Boost_NO_BOOST_CMAKE ON)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
# GCC-specific fixes
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Clang-specific fixes
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
elseif(MSVC)
# MSVC-specific fixes
add_compile_options(/wd4068) # Ignore unknown pragmas
endif()
# make GIT_COMMIT_HASH define available to all sources
find_package(Git)
if(Git_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
if(gch)
set(GIT_COMMIT_HASH "${gch}")
message(STATUS gch: ${GIT_COMMIT_HASH})
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git
rev-parse HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE gch)
if(gch)
set(GIT_COMMIT_HASH "${gch}")
message(STATUS gch: ${GIT_COMMIT_HASH})
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse --abbrev-ref HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
if(gb)
set(GIT_BRANCH "${gb}")
message(STATUS gb: ${GIT_BRANCH})
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
endif()
endif() #git
execute_process(
COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git
rev-parse --abbrev-ref HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE gb)
if(gb)
set(GIT_BRANCH "${gb}")
message(STATUS gb: ${GIT_BRANCH})
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
endif()
endif() # git
# make SOURCE_ROOT_PATH define available for logging
set(SOURCE_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/")
add_definitions(-DSOURCE_ROOT_PATH="${SOURCE_ROOT_PATH}")
# BEAST_ENHANCED_LOGGING - adds file:line numbers and formatting to logs
# Automatically enabled for Debug builds via generator expression
# Can be explicitly controlled with -DBEAST_ENHANCED_LOGGING=ON/OFF
option(BEAST_ENHANCED_LOGGING "Include file and line numbers in log messages (auto: Debug=ON, Release=OFF)" OFF)
# Automatically enabled for Debug builds via generator expression Can be
# explicitly controlled with -DBEAST_ENHANCED_LOGGING=ON/OFF
option(
BEAST_ENHANCED_LOGGING
"Include file and line numbers in log messages (auto: Debug=ON, Release=OFF)"
OFF)
message(STATUS "BEAST_ENHANCED_LOGGING option: ${BEAST_ENHANCED_LOGGING}")
if(thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options(
-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
-DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options("-stdlib=libc++")
add_link_options("-stdlib=libc++")
endif()
@@ -74,55 +73,58 @@ option(USE_CONAN "Use Conan package manager for dependencies" OFF)
if(CMAKE_TOOLCHAIN_FILE)
# Check if the toolchain file path contains "conan_toolchain"
if(CMAKE_TOOLCHAIN_FILE MATCHES "conan_toolchain")
set(USE_CONAN ON CACHE BOOL "Using Conan detected from toolchain file" FORCE)
set(USE_CONAN
ON
CACHE BOOL "Using Conan detected from toolchain file" FORCE)
message(STATUS "Conan toolchain detected: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Building with Conan dependencies")
endif()
endif()
include (CheckCXXCompilerFlag)
include (FetchContent)
include (ExternalProject)
include (CMakeFuncs) # must come *after* ExternalProject b/c it overrides one function in EP
if (target)
message (FATAL_ERROR "The target option has been removed - use native cmake options to control build")
endif ()
include(CheckCXXCompilerFlag)
include(FetchContent)
include(ExternalProject)
include(CMakeFuncs) # must come *after* ExternalProject b/c it overrides one
# function in EP
if(target)
message(
FATAL_ERROR
"The target option has been removed - use native cmake options to control build"
)
endif()
include(RippledSanity)
include(RippledVersion)
include(RippledSettings)
# this check has to remain in the top-level cmake
# because of the early return statement
if (packages_only)
if (NOT TARGET rpm)
message (FATAL_ERROR "packages_only requested, but targets were not created - is docker installed?")
# this check has to remain in the top-level cmake because of the early return
# statement
if(packages_only)
if(NOT TARGET rpm)
message(
FATAL_ERROR
"packages_only requested, but targets were not created - is docker installed?"
)
endif()
return ()
endif ()
return()
endif()
include(RippledCompiler)
include(RippledInterface)
include(deps/Boost)
find_package(OpenSSL 1.1.1 REQUIRED)
set_target_properties(OpenSSL::SSL PROPERTIES
INTERFACE_COMPILE_DEFINITIONS OPENSSL_NO_SSL2
)
set_target_properties(OpenSSL::SSL PROPERTIES INTERFACE_COMPILE_DEFINITIONS
OPENSSL_NO_SSL2)
set(SECP256K1_INSTALL TRUE)
set(SECP256K1_BUILD_BENCHMARK FALSE)
set(SECP256K1_BUILD_TESTS FALSE)
set(SECP256K1_BUILD_EXHAUSTIVE_TESTS FALSE)
set(SECP256K1_BUILD_CTIME_TESTS FALSE)
set(SECP256K1_BUILD_EXAMPLES FALSE)
add_subdirectory(external/secp256k1)
add_library(secp256k1::secp256k1 ALIAS secp256k1)
add_subdirectory(external/ed25519-donna)
add_subdirectory(external/antithesis-sdk)
find_package(gRPC REQUIRED)
find_package(lz4 REQUIRED)
# Target names with :: are not allowed in a generator expression.
# We need to pull the include directories and imported location properties
# from separate targets.
# Target names with :: are not allowed in a generator expression. We need to
# pull the include directories and imported location properties from separate
# targets.
find_package(LibArchive REQUIRED)
find_package(SOCI REQUIRED)
find_package(SQLite3 REQUIRED)
@@ -130,9 +132,9 @@ find_package(SQLite3 REQUIRED)
option(rocksdb "Enable RocksDB" ON)
if(rocksdb)
find_package(RocksDB REQUIRED)
set_target_properties(RocksDB::rocksdb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS RIPPLE_ROCKSDB_AVAILABLE=1
)
set_target_properties(
RocksDB::rocksdb PROPERTIES INTERFACE_COMPILE_DEFINITIONS
RIPPLE_ROCKSDB_AVAILABLE=1)
target_link_libraries(ripple_libs INTERFACE RocksDB::rocksdb)
endif()
@@ -141,8 +143,12 @@ find_package(date REQUIRED)
find_package(xxHash REQUIRED)
find_package(magic_enum REQUIRED)
include(deps/WasmEdge)
if(TARGET nudb::core)
include(deps/WasmEdge)
if(xrpld)
include(deps/Wasmtime)
include(QuickJSProvider)
endif()
if(TARGET nudb::core)
set(nudb nudb::core)
elseif(TARGET NuDB::nudb)
set(nudb NuDB::nudb)
@@ -151,16 +157,16 @@ else()
endif()
target_link_libraries(ripple_libs INTERFACE ${nudb})
target_link_libraries(ripple_libs INTERFACE
ed25519::ed25519
LibArchive::LibArchive
lz4::lz4
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1
soci::soci
SQLite::SQLite3
)
target_link_libraries(
ripple_libs
INTERFACE ed25519::ed25519
LibArchive::LibArchive
lz4::lz4
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1
soci::soci
SQLite::SQLite3)
if(coverage)
include(RippledCov)
@@ -171,8 +177,3 @@ include(RippledCore)
include(RippledInstall)
include(RippledDocs)
include(RippledValidatorKeys)
if(tests)
include(CTest)
add_subdirectory(src/tests/libxrpl)
endif()

View File

@@ -7,6 +7,7 @@ We assume you are familiar with the general practice of [making
contributions on GitHub][1]. This file includes only special
instructions specific to this project.
## Before you start
In general, contributions should be developed in your personal
@@ -27,6 +28,7 @@ your verifying key. Please set up [signature verification][signing].
[signing]:
https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification
## Major contributions
If your contribution is a major feature or breaking change, then you
@@ -43,6 +45,7 @@ responsibility of the XLS author to update the draft to match the final
implementation when its corresponding pull request is merged, unless the
author delegates that responsibility to others.
## Before making a pull request
Changes that alter transaction processing must be guarded by an
@@ -72,17 +75,16 @@ Changes should be usually squashed down into a single commit.
Some larger or more complicated change sets make more sense,
and are easier to review if organized into multiple logical commits.
Either way, all commits should fit the following criteria:
- Changes should be presented in a single commit or a logical
* Changes should be presented in a single commit or a logical
sequence of commits.
Specifically, chronological commits that simply
reflect the history of how the author implemented
the change, "warts and all", are not useful to
reviewers.
- Every commit should have a [good message](#good-commit-messages).
* Every commit should have a [good message](#good-commit-messages).
to explain a specific aspects of the change.
- Every commit should be signed.
- Every commit should be well-formed (builds successfully,
* Every commit should be signed.
* Every commit should be well-formed (builds successfully,
unit tests passing), as this helps to resolve merge
conflicts, and makes it easier to use `git bisect`
to find bugs.
@@ -94,14 +96,13 @@ Refer to
for general rules on writing a good commit message.
tl;dr
> 1. Separate subject from body with a blank line.
> 2. Limit the subject line to 50 characters.
> - [...]shoot for 50 characters, but consider 72 the hard limit.
> * [...]shoot for 50 characters, but consider 72 the hard limit.
> 3. Capitalize the subject line.
> 4. Do not end the subject line with a period.
> 5. Use the imperative mood in the subject line.
> - A properly formed Git commit subject line should always be able
> * A properly formed Git commit subject line should always be able
> to complete the following sentence: "If applied, this commit will
> _your subject line here_".
> 6. Wrap the body at 72 characters.
@@ -109,17 +110,16 @@ tl;dr
In addition to those guidelines, please add one of the following
prefixes to the subject line if appropriate.
- `fix:` - The primary purpose is to fix an existing bug.
- `perf:` - The primary purpose is performance improvements.
- `refactor:` - The changes refactor code without affecting
* `fix:` - The primary purpose is to fix an existing bug.
* `perf:` - The primary purpose is performance improvements.
* `refactor:` - The changes refactor code without affecting
functionality.
- `test:` - The changes _only_ affect unit tests.
- `docs:` - The changes _only_ affect documentation. This can
* `test:` - The changes _only_ affect unit tests.
* `docs:` - The changes _only_ affect documentation. This can
include code comments in addition to `.md` files like this one.
- `build:` - The changes _only_ affect the build process,
* `build:` - The changes _only_ affect the build process,
including CMake and/or Conan settings.
- `chore:` - Other tasks that don't affect the binary, but don't fit
* `chore:` - Other tasks that don't affect the binary, but don't fit
any of the other cases. e.g. formatting, git settings, updating
Github Actions jobs.
@@ -169,11 +169,11 @@ meets a few criteria:
2. All CI checks must be complete and passed. (One-off failures may
be acceptable if they are related to a known issue.)
3. The PR must have a [good commit message](#good-commit-messages).
- If the PR started with a good commit message, and it doesn't
* If the PR started with a good commit message, and it doesn't
need to be updated, the author can indicate that in a comment.
- Any contributor, preferably the author, can leave a comment
* Any contributor, preferably the author, can leave a comment
suggesting a commit message.
- If the author squashes and rebases the code in preparation for
* If the author squashes and rebases the code in preparation for
merge, they should also ensure the commit message(s) are updated
as well.
4. The PR branch must be up to date with the base branch (usually
@@ -320,6 +320,7 @@ This is a non-exhaustive list of recommended style guidelines. These are
not always strictly enforced and serve as a way to keep the codebase
coherent rather than a set of _thou shalt not_ commandments.
## Formatting
All code must conform to `clang-format` version 10,
@@ -348,7 +349,6 @@ To download the patch file:
5. Commit and push.
You can install a pre-commit hook to automatically run `clang-format` before every commit:
```
pip3 install pre-commit
pre-commit install
@@ -379,51 +379,49 @@ locations, where the reporting of contract violations on the Antithesis
platform is either not possible or not useful.
For this reason:
- The locations where `assert` or `assert(false)` contracts should continue to be used:
- `constexpr` functions
- unit tests i.e. files under `src/test`
- unit tests-related modules (files under `beast/test` and `beast/unit_test`)
- Outside of the listed locations, do not use `assert`; use `XRPL_ASSERT` instead,
* The locations where `assert` or `assert(false)` contracts should continue to be used:
* `constexpr` functions
* unit tests i.e. files under `src/test`
* unit tests-related modules (files under `beast/test` and `beast/unit_test`)
* Outside of the listed locations, do not use `assert`; use `XRPL_ASSERT` instead,
giving it unique name, with the short description of the contract.
- Outside of the listed locations, do not use `assert(false)`; use
* Outside of the listed locations, do not use `assert(false)`; use
`UNREACHABLE` instead, giving it unique name, with the description of the
condition being violated
- The contract name should start with a full name (including scope) of the
function, optionally a named lambda, followed by a colon `:` and a brief
* The contract name should start with a full name (including scope) of the
function, optionally a named lambda, followed by a colon ` : ` and a brief
(typically at most five words) description. `UNREACHABLE` contracts
can use slightly longer descriptions. If there are multiple overloads of the
function, use common sense to balance both brevity and unambiguity of the
function name. NOTE: the purpose of name is to provide stable means of
unique identification of every contract; for this reason try to avoid elements
which can change in some obvious refactors or when reinforcing the condition.
- Contract description typically (except for `UNREACHABLE`) should describe the
* Contract description typically (except for `UNREACHABLE`) should describe the
_expected_ condition, as in "I assert that _expected_ is true".
- Contract description for `UNREACHABLE` should describe the _unexpected_
* Contract description for `UNREACHABLE` should describe the _unexpected_
situation which caused the line to have been reached.
- Example good name for an
* Example good name for an
`UNREACHABLE` macro `"Json::operator==(Value, Value) : invalid type"`; example
good name for an `XRPL_ASSERT` macro `"Json::Value::asCString : valid type"`.
- Example **bad** name
* Example **bad** name
`"RFC1751::insert(char* s, int x, int start, int length) : length is greater than or equal zero"`
(missing namespace, unnecessary full function signature, description too verbose).
Good name: `"ripple::RFC1751::insert : minimum length"`.
- In **few** well-justified cases a non-standard name can be used, in which case a
* In **few** well-justified cases a non-standard name can be used, in which case a
comment should be placed to explain the rationale (example in `contract.cpp`)
- Do **not** rename a contract without a good reason (e.g. the name no longer
* Do **not** rename a contract without a good reason (e.g. the name no longer
reflects the location or the condition being checked)
- Do not use `std::unreachable`
- Do not put contracts where they can be violated by an external condition
* Do not use `std::unreachable`
* Do not put contracts where they can be violated by an external condition
(e.g. timing, data payload before mandatory validation etc.) as this creates
bogus bug reports (and causes crashes of Debug builds)
## Unit Tests
To execute all unit tests:
`rippled --unittest --unittest-jobs=<number of cores>`
```rippled --unittest --unittest-jobs=<number of cores>```
(Note: Using multiple cores on a Mac M1 can cause spurious test failures. The
(Note: Using multiple cores on a Mac M1 can cause spurious test failures. The
cause is still under investigation. If you observe this problem, try specifying fewer jobs.)
To run a specific set of test suites:
@@ -431,11 +429,10 @@ To run a specific set of test suites:
```
rippled --unittest TestSuiteName
```
Note: In this example, all tests with prefix `TestSuiteName` will be run, so if
`TestSuiteName1` and `TestSuiteName2` both exist, then both tests will run.
Alternatively, if the unit test name finds an exact match, it will stop
doing partial matches, i.e. if a unit test with a title of `TestSuiteName`
`TestSuiteName1` and `TestSuiteName2` both exist, then both tests will run.
Alternatively, if the unit test name finds an exact match, it will stop
doing partial matches, i.e. if a unit test with a title of `TestSuiteName`
exists, then no other unit test will be executed, apart from `TestSuiteName`.
## Avoid
@@ -451,6 +448,7 @@ exists, then no other unit test will be executed, apart from `TestSuiteName`.
explanatory comments.
8. Importing new libraries unless there is a very good reason to do so.
## Seek to
9. Extend functionality of existing code rather than creating new code.
@@ -465,12 +463,14 @@ exists, then no other unit test will be executed, apart from `TestSuiteName`.
14. Provide as many comments as you feel that a competent programmer
would need to understand what your code does.
# Maintainers
Maintainers are ecosystem participants with elevated access to the repository.
They are able to push new code, make decisions on when a release should be
made, etc.
## Adding and removing
New maintainers can be proposed by two existing maintainers, subject to a vote
@@ -485,6 +485,7 @@ A minimum of 60% agreement and 50% participation are required.
The XRP Ledger Foundation will have the ability, for cause, to remove an
existing maintainer without a vote.
## Current Maintainers
* [Richard Holland](https://github.com/RichardAH) (XRPL Labs + INFTF)

View File

@@ -1,4 +1,4 @@
ISC License
ISC License
Copyright (c) 2011, Arthur Britto, David Schwartz, Jed McCaleb, Vinnie Falco, Bob Way, Eric Lombrozo, Nikolaos D. Bougalis, Howard Hinnant.
Copyright (c) 2012-2020, the XRP Ledger developers.
@@ -15,3 +15,4 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -5,11 +5,9 @@
[Xahau](https://xahau.network/) is a decentralized cryptographic ledger that builds upon the robust foundation of the XRP Ledger. It inherits the XRP Ledger's Byzantine Fault Tolerant consensus algorithm and enhances it with additional features and functionalities. Developers and users familiar with the XRP Ledger will find that most documentation and tutorials available on [xrpl.org](https://xrpl.org) are relevant and applicable to Xahau, including those related to running validators and managing validator keys. For Xahau specific documentation you can visit our [documentation](https://xahau.network/)
## XAH
XAH is the public, counterparty-free asset native to Xahau and functions primarily as network gas. Transactions submitted to the Xahau network must supply an appropriate amount of XAH, to be burnt by the network as a fee, in order to be successfully included in a validated ledger. In addition, XAH also acts as a bridge currency within the Xahau DEX. XAH is traded on the open-market and is available for anyone to access. Xahau was created in 2023 with a supply of 600 million units of XAH.
## xahaud
The server software that powers Xahau is called `xahaud` and is available in this repository under the permissive [ISC open-source license](LICENSE.md). The `xahaud` server software is written primarily in C++ and runs on a variety of platforms. The `xahaud` server software can run in several modes depending on its configuration.
### Build from Source

4716
RELEASENOTES.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
For more details on operating the Xahau server securely, please visit https://docs.xahau.network/infrastructure/building-xahau.
# Security Policy
## Supported Versions

218
bin/physical.sh Executable file
View File

@@ -0,0 +1,218 @@
#!/bin/bash
set -o errexit
marker_base=f62f74da10c5936c64bd16cd509a8b68f1464e41
marker_commit=${1:-${marker_base}}
if [ $(git merge-base ${marker_commit} ${marker_base}) != ${marker_base} ]; then
echo "first marker commit not an ancestor: ${marker_commit}"
exit 1
fi
if [ $(git merge-base ${marker_commit} HEAD) != $(git rev-parse --verify ${marker_commit}) ]; then
echo "given marker commit not an ancestor: ${marker_commit}"
exit 1
fi
if [ -e Builds/CMake ]; then
echo move CMake
git mv Builds/CMake cmake
git add --update .
git commit -m 'Move CMake directory' --author 'Pretty Printer <cpp@ripple.com>'
fi
if [ -e src/ripple ]; then
echo move protocol buffers
mkdir -p include/xrpl
if [ -e src/ripple/proto ]; then
git mv src/ripple/proto include/xrpl
fi
extract_list() {
git show ${marker_commit}:Builds/CMake/RippledCore.cmake | \
awk "/END ${1}/ { p = 0 } p && /src\/ripple/; /BEGIN ${1}/ { p = 1 }" | \
sed -e 's#src/ripple/##' -e 's#[^a-z]\+$##'
}
move_files() {
oldroot="$1"; shift
newroot="$1"; shift
detail="$1"; shift
files=("$@")
for file in ${files[@]}; do
if [ ! -e ${oldroot}/${file} ]; then
continue
fi
dir=$(dirname ${file})
if [ $(basename ${dir}) == 'details' ]; then
dir=$(dirname ${dir})
fi
if [ $(basename ${dir}) == 'impl' ]; then
dir="$(dirname ${dir})/${detail}"
fi
mkdir -p ${newroot}/${dir}
git mv ${oldroot}/${file} ${newroot}/${dir}
done
}
echo move libxrpl headers
files=$(extract_list 'LIBXRPL HEADERS')
files+=(
basics/SlabAllocator.h
beast/asio/io_latency_probe.h
beast/container/aged_container.h
beast/container/aged_container_utility.h
beast/container/aged_map.h
beast/container/aged_multimap.h
beast/container/aged_multiset.h
beast/container/aged_set.h
beast/container/aged_unordered_map.h
beast/container/aged_unordered_multimap.h
beast/container/aged_unordered_multiset.h
beast/container/aged_unordered_set.h
beast/container/detail/aged_associative_container.h
beast/container/detail/aged_container_iterator.h
beast/container/detail/aged_ordered_container.h
beast/container/detail/aged_unordered_container.h
beast/container/detail/empty_base_optimization.h
beast/core/LockFreeStack.h
beast/insight/Collector.h
beast/insight/Counter.h
beast/insight/CounterImpl.h
beast/insight/Event.h
beast/insight/EventImpl.h
beast/insight/Gauge.h
beast/insight/GaugeImpl.h
beast/insight/Group.h
beast/insight/Groups.h
beast/insight/Hook.h
beast/insight/HookImpl.h
beast/insight/Insight.h
beast/insight/Meter.h
beast/insight/MeterImpl.h
beast/insight/NullCollector.h
beast/insight/StatsDCollector.h
beast/test/fail_counter.h
beast/test/fail_stream.h
beast/test/pipe_stream.h
beast/test/sig_wait.h
beast/test/string_iostream.h
beast/test/string_istream.h
beast/test/string_ostream.h
beast/test/test_allocator.h
beast/test/yield_to.h
beast/utility/hash_pair.h
beast/utility/maybe_const.h
beast/utility/temp_dir.h
# included by only json/impl/json_assert.h
json/json_errors.h
protocol/PayChan.h
protocol/RippleLedgerHash.h
protocol/messages.h
protocol/st.h
)
files+=(
basics/README.md
crypto/README.md
json/README.md
protocol/README.md
resource/README.md
)
move_files src/ripple include/xrpl detail ${files[@]}
echo move libxrpl sources
files=$(extract_list 'LIBXRPL SOURCES')
move_files src/ripple src/libxrpl "" ${files[@]}
echo check leftovers
dirs=$(cd include/xrpl; ls -d */)
dirs=$(cd src/ripple; ls -d ${dirs} 2>/dev/null || true)
files="$(cd src/ripple; find ${dirs} -type f)"
if [ -n "${files}" ]; then
echo "leftover files:"
echo ${files}
exit
fi
echo remove empty directories
empty_dirs="$(cd src/ripple; find ${dirs} -depth -type d)"
for dir in ${empty_dirs[@]}; do
if [ -e ${dir} ]; then
rmdir ${dir}
fi
done
echo move xrpld sources
files=$(
extract_list 'XRPLD SOURCES'
cd src/ripple
find * -regex '.*\.\(h\|ipp\|md\|pu\|uml\|png\)'
)
move_files src/ripple src/xrpld detail ${files[@]}
files="$(cd src/ripple; find . -type f)"
if [ -n "${files}" ]; then
echo "leftover files:"
echo ${files}
exit
fi
fi
rm -rf src/ripple
echo rename .hpp to .h
find include src -name '*.hpp' -exec bash -c 'f="{}"; git mv "${f}" "${f%hpp}h"' \;
echo move PerfLog.h
if [ -e include/xrpl/basics/PerfLog.h ]; then
git mv include/xrpl/basics/PerfLog.h src/xrpld/perflog
fi
# Make sure all protobuf includes have the correct prefix.
protobuf_replace='s:^#include\s*["<].*org/xrpl\([^">]\+\)[">]:#include <xrpl/proto/org/xrpl\1>:'
# Make sure first-party includes use angle brackets and .h extension.
ripple_replace='s:include\s*["<]ripple/\(.*\)\.h\(pp\)\?[">]:include <ripple/\1.h>:'
beast_replace='s:include\s*<beast/:include <xrpl/beast/:'
# Rename impl directories to detail.
impl_rename='s:\(<xrpl.*\)/impl\(/details\)\?/:\1/detail/:'
echo rewrite includes in libxrpl
find include/xrpl src/libxrpl -type f -exec sed -i \
-e "${protobuf_replace}" \
-e "${ripple_replace}" \
-e "${beast_replace}" \
-e 's:^#include <ripple/:#include <xrpl/:' \
-e "${impl_rename}" \
{} +
echo rewrite includes in xrpld
# # https://www.baeldung.com/linux/join-multiple-lines
libxrpl_dirs="$(cd include/xrpl; ls -d1 */ | sed 's:/$::')"
# libxrpl_dirs='a\nb\nc\n'
readarray -t libxrpl_dirs <<< "${libxrpl_dirs}"
# libxrpl_dirs=(a b c)
libxrpl_dirs=$(printf -v txt '%s\\|' "${libxrpl_dirs[@]}"; echo "${txt%\\|}")
# libxrpl_dirs='a\|b\|c'
find src/xrpld src/test -type f -exec sed -i \
-e "${protobuf_replace}" \
-e "${ripple_replace}" \
-e "${beast_replace}" \
-e "s:^#include <ripple/basics/PerfLog.h>:#include <xrpld/perflog/PerfLog.h>:" \
-e "s:^#include <ripple/\(${libxrpl_dirs}\)/:#include <xrpl/\1/:" \
-e 's:^#include <ripple/:#include <xrpld/:' \
-e "${impl_rename}" \
{} +
git commit -m 'Rearrange sources' --author 'Pretty Printer <cpp@ripple.com>'
find include src -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format-10 -i {} +
git add --update .
git commit -m 'Rewrite includes' --author 'Pretty Printer <cpp@ripple.com>'
./Builds/levelization/levelization.sh
git add --update .
git commit -m 'Recompute loops' --author 'Pretty Printer <cpp@ripple.com>'

148
bin/update-jshookz-snapshot Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""Build and import one committed jshookz provider snapshot into Xahau."""
from __future__ import annotations
import argparse
import json
import os
import shlex
import shutil
import subprocess
from pathlib import Path
SNAPSHOT_FILES = (
"jshookz_provider.manifest.cmake",
"jshookz_provider.manifest.json",
"jshookz_provider.native-abi.json",
"jshookz_provider.wasm",
)
API_SNAPSHOT_FILES = {
"python/jshookz/src/jshookz/types/api-artifacts.json": "api-artifacts.json",
"python/jshookz/src/jshookz/types/hooks-api.d.ts": "hooks-api.d.ts",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts":
"xahau-quickjs-v1.d.ts",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json":
"xahau-quickjs-v1.surface.json",
}
def run(*args: str, cwd: Path, capture: bool = False) -> str:
completed = subprocess.run(
args,
cwd=cwd,
check=True,
text=True,
stdout=subprocess.PIPE if capture else None,
)
return completed.stdout.strip() if capture else ""
def main() -> int:
parser = argparse.ArgumentParser(
description=(
"Build a committed jshookz checkout and copy its sealed "
"provider lock into Xahau. CI tracks jshookz main; the lock "
"is what must match."
)
)
parser.add_argument("jshookz_checkout", type=Path)
parser.add_argument(
"--no-build",
action="store_true",
help="import an already-built build/xahau-provider bundle",
)
args = parser.parse_args()
xahau_root = Path(__file__).resolve().parents[1]
jshookz_root = args.jshookz_checkout.expanduser().resolve()
cli = next(
(
candidate
for candidate in (
jshookz_root / "python/jshookz/.venv/bin/jshookz",
jshookz_root / "packages/jshookz/.venv/bin/jshookz",
)
if candidate.is_file()
),
None,
)
if cli is None:
parser.error(
"missing jshookz CLI under python/jshookz/.venv or "
"packages/jshookz/.venv"
)
bundle = jshookz_root / "build/xahau-provider"
destination = xahau_root / "external/quickjs-provider"
dirty = run(
"git",
"status",
"--porcelain",
"--untracked-files=no",
cwd=jshookz_root,
capture=True,
)
if dirty:
parser.error(
"jshookz checkout has uncommitted tracked changes; commit them "
"before creating a pinned snapshot"
)
commit = run("git", "rev-parse", "HEAD", cwd=jshookz_root, capture=True)
if not args.no_build:
run(str(cli), "build", "provider", cwd=jshookz_root)
missing = [name for name in SNAPSHOT_FILES if not (bundle / name).is_file()]
missing.extend(
source for source in API_SNAPSHOT_FILES if not (jshookz_root / source).is_file()
)
if missing:
parser.error(f"incomplete provider bundle: {', '.join(missing)}")
for name in SNAPSHOT_FILES:
shutil.copy2(bundle / name, destination / name)
for source, name in API_SNAPSHOT_FILES.items():
shutil.copy2(jshookz_root / source, destination / name)
manifest = json.loads((bundle / "jshookz_provider.manifest.json").read_text())
wasi_sdk = os.environ.get("HOOKZ_WASI_SDK")
if not wasi_sdk:
try:
wasi_sdk = str(
Path(
run(
"mise",
"where",
"wasi-sdk",
cwd=jshookz_root,
capture=True,
)
)
/ "wasi-sdk"
)
except (FileNotFoundError, subprocess.CalledProcessError):
wasi_sdk = "/path/to/wasi-sdk"
print(f"jshookz commit: {commit}")
print(f"provider sha256: {manifest['provider']['sha256']}")
print(f"bytecode ABI: {manifest['bytecode_abi_id']}")
print(f"runtime profile: {manifest['runtime_profile_id']}")
print("snapshot imported; regenerate fixtures and run the focused suite with:")
print(
" env "
f"JSHOOKZ_HOOK_COMPILER={shlex.quote(f'{cli} compile-hook')} "
f"HOOKZ_WASI_SDK={shlex.quote(wasi_sdk)} "
f"XAHAU_QJS_PROVIDER_WASM={shlex.quote(str(bundle / 'jshookz_provider.wasm'))} "
"XAHAU_REQUIRE_QJS_PROVIDER_TESTS=1 "
"x-run-tests --compile-hooks src/test/app/JSHooks_test.cpp "
"--build-dir build --ccache -- "
"ripple.app.JSHooks,ripple.app.QuickJSHostPolicy,"
"ripple.app.HookGuestMemory,ripple.app.HookAPI"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -111,9 +111,9 @@ fi
cd ..;
mv src/libxrpl/net/RegisterSSLCerts.cpp.old src/libxrpl/net/RegisterSSLCerts.cpp;
mv src/xrpld/net/detail/RegisterSSLCerts.cpp.old src/xrpld/net/detail/RegisterSSLCerts.cpp;
mv cmake/deps/WasmEdge.old cmake/deps/WasmEdge.cmake;
rm include/xrpl/net/certs;
rm src/certs/certbundle.h;
git checkout src/libxrpl/protocol/BuildInfo.cpp;
echo "END INSIDE CONTAINER - CORE"

14
build-full.sh Executable file → Normal file
View File

@@ -17,10 +17,10 @@ umask 0000
####
cd /io
mkdir -p include/xrpl/net/certs
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o include/xrpl/net/certs/certbundle.h
if [ "$(grep certbundle.h src/libxrpl/net/RegisterSSLCerts.cpp | wc -l)" -eq "0" ]; then
cp src/libxrpl/net/RegisterSSLCerts.cpp src/libxrpl/net/RegisterSSLCerts.cpp.old
mkdir -p src/certs
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o src/certs/certbundle.h
if [ "$(grep certbundle.h src/xrpld/net/detail/RegisterSSLCerts.cpp | wc -l)" -eq "0" ]; then
cp src/xrpld/net/detail/RegisterSSLCerts.cpp src/xrpld/net/detail/RegisterSSLCerts.cpp.old
perl -i -pe "s/^{/{
#ifdef EMBEDDED_CA_BUNDLE
BIO *cbio = BIO_new_mem_buf(ca_bundle.data(), ca_bundle.size());
@@ -60,11 +60,11 @@ if [ "$(grep certbundle.h src/libxrpl/net/RegisterSSLCerts.cpp | wc -l)" -eq "0"
BIO_free(cbio);
}
}
#endif/g" src/libxrpl/net/RegisterSSLCerts.cpp &&
sed -i "s/#include <xrpl\/net\/RegisterSSLCerts.h>/\0\n#include <xrpl\/net\/certs\/certbundle.h>/g" src/libxrpl/net/RegisterSSLCerts.cpp
#endif/g" src/xrpld/net/detail/RegisterSSLCerts.cpp &&
sed -i "s/#include <xrpld\/net\/RegisterSSLCerts.h>/\0\n#include <certs\/certbundle.h>/g" src/xrpld/net/detail/RegisterSSLCerts.cpp
fi
# Environment setup moved to Dockerfile in release-builder.sh
source /opt/rh/gcc-toolset-${GCC_TOOLSET_VERSION}/enable
source /opt/rh/gcc-toolset-11/enable
export PATH=/usr/local/bin:$PATH
export CC='/usr/lib64/ccache/gcc' &&
export CXX='/usr/lib64/ccache/g++' &&

View File

@@ -394,8 +394,8 @@
# true - enables compression
# false - disables compression [default].
#
# The rippled server can save bandwidth by compressing its peer-to-peer communications,
# at a cost of greater CPU usage. If you enable link compression,
# The rippled server can save bandwidth by compressing its peer-to-peer communications,
# at a cost of greater CPU usage. If you enable link compression,
# the server automatically compresses communications with peer servers
# that also have link compression enabled.
# https://xrpl.org/enable-link-compression.html
@@ -1070,7 +1070,7 @@
# that xahaud is still in sync with the network,
# and that the validated ledger is less than
# 'age_threshold_seconds' old. If not, then continue
# sleeping for this number of seconds and
# sleeping for this number of seconds and
# checking until healthy.
# Default is 5.
#
@@ -1172,7 +1172,7 @@
# page_size Valid values: integer (MUST be power of 2 between 512 and 65536)
# The default is 4096 bytes. This setting determines
# the size of a page in the transaction.db file.
# See https://www.sqlite.org/pragma.html#pragma_page_size
# See https://www.sqlite.org/pragma.html#pragma_page_size
# for more details about the available options.
#
# journal_size_limit Valid values: integer

View File

@@ -1,3 +1,21 @@
macro(group_sources_in source_dir curdir)
file(GLOB children RELATIVE ${source_dir}/${curdir}
${source_dir}/${curdir}/*)
foreach (child ${children})
if (IS_DIRECTORY ${source_dir}/${curdir}/${child})
group_sources_in(${source_dir} ${curdir}/${child})
else()
string(REPLACE "/" "\\" groupname ${curdir})
source_group(${groupname} FILES
${source_dir}/${curdir}/${child})
endif()
endforeach()
endmacro()
macro(group_sources curdir)
group_sources_in(${PROJECT_SOURCE_DIR} ${curdir})
endmacro()
macro (exclude_from_default target_)
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_ALL ON)
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)

View File

@@ -98,17 +98,6 @@
# 2024-04-03, Bronek Kozicki
# - add support for output formats: jacoco, clover, lcov
#
# 2025-05-12, Jingchen Wu
# - add -fprofile-update=atomic to ensure atomic profile generation
#
# 2025-08-28, Bronek Kozicki
# - fix "At least one COMMAND must be given" CMake warning from policy CMP0175
#
# 2025-09-03, Jingchen Wu
# - remove the unused function append_coverage_compiler_flags and append_coverage_compiler_flags_to_target
# - add a new function add_code_coverage_to_target
# - remove some unused code
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
@@ -117,8 +106,10 @@
# using a CMake option() to enable it just optionally):
# include(CodeCoverage)
#
# 3. Append necessary compiler flags and linker flags for all supported source files:
# add_code_coverage_to_target(<target> <PRIVATE|PUBLIC|INTERFACE>)
# 3. Append necessary compiler flags for all supported source files:
# append_coverage_compiler_flags()
# Or for specific target:
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
#
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
#
@@ -207,69 +198,55 @@ endforeach()
set(COVERAGE_COMPILER_FLAGS "-g --coverage"
CACHE INTERNAL "")
set(COVERAGE_CXX_COMPILER_FLAGS "")
set(COVERAGE_C_COMPILER_FLAGS "")
set(COVERAGE_CXX_LINKER_FLAGS "")
set(COVERAGE_C_LINKER_FLAGS "")
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
include(CheckCCompilerFlag)
include(CheckLinkerFlag)
set(COVERAGE_CXX_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS})
set(COVERAGE_C_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS})
set(COVERAGE_CXX_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS})
set(COVERAGE_C_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS})
check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path)
if(HAVE_cxx_fprofile_abs_path)
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-abs-path")
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
include(CheckCCompilerFlag)
check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path)
if(HAVE_c_fprofile_abs_path)
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-abs-path")
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
check_linker_flag(CXX -fprofile-abs-path HAVE_cxx_linker_fprofile_abs_path)
if(HAVE_cxx_linker_fprofile_abs_path)
set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-abs-path")
endif()
check_linker_flag(C -fprofile-abs-path HAVE_c_linker_fprofile_abs_path)
if(HAVE_c_linker_fprofile_abs_path)
set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-abs-path")
endif()
check_cxx_compiler_flag(-fprofile-update=atomic HAVE_cxx_fprofile_update)
if(HAVE_cxx_fprofile_update)
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-update=atomic")
endif()
check_c_compiler_flag(-fprofile-update=atomic HAVE_c_fprofile_update)
if(HAVE_c_fprofile_update)
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-update=atomic")
endif()
check_linker_flag(CXX -fprofile-update=atomic HAVE_cxx_linker_fprofile_update)
if(HAVE_cxx_linker_fprofile_update)
set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-update=atomic")
endif()
check_linker_flag(C -fprofile-update=atomic HAVE_c_linker_fprofile_update)
if(HAVE_c_linker_fprofile_update)
set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-update=atomic")
endif()
endif()
set(CMAKE_Fortran_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
FORCE )
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_Fortran_FLAGS_COVERAGE
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
@@ -455,24 +432,23 @@ function(setup_target_for_coverage_gcovr)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND echo
COMMAND ;
COMMENT "Code coverage report saved in ${GCOVR_OUTPUT_FILE} formatted as ${Coverage_FORMAT}"
)
endfunction() # setup_target_for_coverage_gcovr
function(add_code_coverage_to_target name scope)
separate_arguments(COVERAGE_CXX_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_COMPILER_FLAGS}")
separate_arguments(COVERAGE_C_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_C_COMPILER_FLAGS}")
separate_arguments(COVERAGE_CXX_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_LINKER_FLAGS}")
separate_arguments(COVERAGE_C_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_C_LINKER_FLAGS}")
function(append_coverage_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # append_coverage_compiler_flags
# Add compiler options to the target
target_compile_options(${name} ${scope}
$<$<COMPILE_LANGUAGE:CXX>:${COVERAGE_CXX_COMPILER_FLAGS}>
$<$<COMPILE_LANGUAGE:C>:${COVERAGE_C_COMPILER_FLAGS}>)
target_link_libraries (${name} ${scope}
$<$<LINK_LANGUAGE:CXX>:${COVERAGE_CXX_LINKER_FLAGS} gcov>
$<$<LINK_LANGUAGE:C>:${COVERAGE_C_LINKER_FLAGS} gcov>
)
endfunction() # add_code_coverage_to_target
# Setup coverage for specific library
function(append_coverage_compiler_flags_to_target name)
separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
target_compile_options(${name} PRIVATE ${_flag_list})
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
target_link_libraries(${name} PRIVATE gcov)
endif()
endfunction()

View File

@@ -0,0 +1,928 @@
#!/usr/bin/env python3
"""Project the sealed QuickJS bundle into one C++ translation unit."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import sys
from pathlib import Path
from typing import Any
CHUNK = 64
CMAKE_SET = re.compile(r'^set\(XAHAU_QUICKJS_([A-Z0-9_]+) "([^"]*)"\)\s*$')
WASM_VALTYPE = {"i32", "i64"}
WASM_BYTE = {"i32": "0x7f", "i64": "0x7e"}
WASM_STACK_BYTES = 131072
WASM_PAGE_BYTES = 65536
PROVIDER_MEMORY_MINIMUM_PAGES = 7
PROVIDER_MEMORY_MAXIMUM_PAGES = 512
PROVIDER_MEMORY_MAX_BYTES = PROVIDER_MEMORY_MAXIMUM_PAGES * WASM_PAGE_BYTES
SEALED_MANIFEST_SHA256 = (
"1673b27d21063957cece9095cceb7e2dbfa53b6f49a5b7f824a486d7d90b2391"
)
SEALED_PROVIDER_SHA256 = (
"f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df"
)
SEALED_PROVIDER_SIZE = 1116638
SEALED_NATIVE_ABI_SHA256 = (
"328ec938dcad875f3bdf25b8d779dd77f3571589818ca9271cb98c64c7018f99"
)
SEALED_BYTECODE_ABI_ID = (
"75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e"
)
SEALED_RUNTIME_PROFILE_ID = (
"29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25"
)
SEALED_WASMTIME_VERSION = "47.0.3"
SEALED_HOOK_API_VERSION = 1
SEALED_HOST_ADAPTER_POLICY = "xahau-raw-hook-host-v1"
SEALED_BROAD_DECLARATION_SHA256 = (
"649079b831bc68eff0f440644649a505addafb08a3f5174e4031c8de6aedffa0"
)
SEALED_EXACT_V1_DECLARATION_SHA256 = (
"20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da"
)
SEALED_SURFACE_SHA256 = (
"96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac"
)
SEALED_API_ARTIFACT_MANIFEST_SHA256 = (
"95babf622bf5edff76fdcbd9746bad8f1f8b275a3d4bacdc320ff4deae543eba"
)
API_ARTIFACT_SCHEMA = "jshookz.api-artifacts.v1"
SEALED_API_ARTIFACTS = {
"python/jshookz/src/jshookz/types/hooks-api.d.ts": (
"broad_declaration",
"hooks-api.d.ts",
SEALED_BROAD_DECLARATION_SHA256,
),
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts": (
"exact_v1_declaration",
"xahau-quickjs-v1.d.ts",
SEALED_EXACT_V1_DECLARATION_SHA256,
),
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json": (
"selected_surface",
"xahau-quickjs-v1.surface.json",
SEALED_SURFACE_SHA256,
),
}
SEALED_LIMITS = {
"host_work_base_per_call": 1,
"host_work_budget": 1000000,
"host_work_meter": "base-plus-addressed-byte-v1",
"host_work_per_addressed_byte": 1,
"quickjs_heap_bytes": 16777216,
"quickjs_stack_bytes": 65536,
"serialized_object_max_bytes": 1048576,
"serialized_object_max_fields": 32768,
"serialized_object_max_scopes": 32769,
"serialized_object_max_depth": 10,
"wasmtime_fuel_per_initialization": 5000000,
"wasmtime_fuel_per_invocation": 50000000,
}
SEALED_HOST_WORK_ADDRESSED_LENGTH_INDICES = {
"accept": [1],
"emit": [1, 3],
"hook_account": [1],
"ledger_last_hash": [1],
"prepare": [1, 3],
"rollback": [1],
"state": [1, 3],
"state_set": [1, 3],
"trace": [1, 3],
}
SEALED_ENGINE_CONFIGURATION = {
"consume_fuel": True,
"cranelift_nan_canonicalization": True,
"wasi": False,
"wasm_memory64": False,
"wasm_multi_memory": False,
"wasm_relaxed_simd": False,
"wasm_tail_call": False,
"wasm_threads": False,
}
IMPORT_KEYS = frozenset({"module", "name", "params", "results"})
FUNCTION_EXPORT_KEYS = frozenset({"kind", "name", "params", "results"})
MEMORY_EXPORT_KEYS = frozenset(
{"kind", "name", "minimum_pages", "maximum_pages", "memory64", "shared"}
)
SURFACE_KEYS = frozenset(
{"declaration", "declaration_sha256", "manifest", "schema", "sha256"}
)
NESTED_SURFACE_KEYS = frozenset({"declaration", "manifest", "schema"})
def _import_row(name: str, params: list[str], results: list[str]) -> dict[str, Any]:
return {"module": "env", "name": name, "params": params, "results": results}
def _function_export(name: str, params: list[str], results: list[str]) -> dict[str, Any]:
return {"kind": "function", "name": name, "params": params, "results": results}
SEALED_IMPORTS = [
_import_row("accept", ["i32", "i32", "i64"], ["i64"]),
_import_row("emit", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("etxn_reserve", ["i32"], ["i64"]),
_import_row("hook_account", ["i32", "i32"], ["i64"]),
_import_row("ledger_last_hash", ["i32", "i32"], ["i64"]),
_import_row("ledger_last_time", [], ["i64"]),
_import_row("ledger_seq", [], ["i64"]),
_import_row("otxn_type", [], ["i64"]),
_import_row("prepare", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("rollback", ["i32", "i32", "i64"], ["i64"]),
_import_row("state", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("state_set", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("trace", ["i32", "i32", "i32", "i32", "i32"], ["i64"]),
]
SEALED_EXPORTS = [
_function_export("_initialize", [], []),
_function_export("free", ["i32"], []),
_function_export("malloc", ["i32"], ["i32"]),
{
"kind": "memory",
"maximum_pages": 512,
"memory64": False,
"minimum_pages": 7,
"name": "memory",
"shared": False,
},
_function_export("qjs_cbak", ["i32", "i32", "i32"], ["i32"]),
_function_export("qjs_compile", ["i32", "i32"], ["i32"]),
_function_export("qjs_compile_module", ["i32", "i32"], ["i32"]),
_function_export("qjs_destroy", [], []),
_function_export("qjs_enable_coverage", ["i32"], []),
_function_export("qjs_eval", ["i32", "i32"], ["i32"]),
_function_export("qjs_eval_bytecode", ["i32", "i32"], ["i32"]),
_function_export("qjs_eval_module", ["i32", "i32"], ["i32"]),
_function_export("qjs_get_bytecode_len", [], ["i32"]),
_function_export("qjs_get_bytecode_ptr", [], ["i32"]),
_function_export("qjs_get_result_len", [], ["i32"]),
_function_export("qjs_get_result_ptr", [], ["i32"]),
_function_export("qjs_hook", ["i32", "i32", "i32"], ["i32"]),
_function_export("qjs_init", [], []),
_function_export("qjs_set_max_stack_size", ["i32"], []),
_function_export("qjs_set_memory_limit", ["i32"], []),
_function_export("qjs_set_seed", ["i32"], []),
_function_export("qjs_validate_hook_module", ["i32", "i32"], ["i32"]),
]
class LockError(ValueError):
"""A sealed QuickJS lock identity failed a fail-closed check."""
def quote(value: str) -> str:
if not isinstance(value, str) or any(c in value for c in "\n\r"):
raise LockError(f"invalid QuickJS policy string: {value!r}")
return json.dumps(value)
def parse_cmake(path: Path) -> dict[str, str]:
values: dict[str, str] = {}
for line in path.read_text().splitlines():
match = CMAKE_SET.match(line)
if match:
values[match.group(1)] = match.group(2)
return values
def hex_bytes(value: str) -> str:
if len(value) != 64 or any(c not in "0123456789abcdefABCDEF" for c in value):
raise LockError(f"QuickJS identity must be 32 hexadecimal bytes: {value!r}")
return ", ".join(f"0x{value[i:i + 2]}" for i in range(0, 64, 2))
def require(cmake: dict[str, str], key: str) -> str:
if key not in cmake:
raise LockError(f"QuickJS cmake projection missing {key}")
return cmake[key]
def require_int(cmake: dict[str, str], key: str) -> int:
return int(require(cmake, key))
def require_hex(value: object, label: str) -> str:
text = str(value).lower()
if len(text) != 64 or any(c not in "0123456789abcdef" for c in text):
raise LockError(f"{label} is not a SHA-256 hex digest: {value!r}")
return text
def require_sealed(actual: object, expected: object, label: str) -> None:
if actual != expected:
raise LockError(
f"{label} disagrees with the sealed F0 table: "
f"{actual!r} != {expected!r}"
)
def validate_api_artifacts(bundle: Path) -> dict[str, str]:
manifest_path = bundle / "api-artifacts.json"
manifest = json.loads(manifest_path.read_text())
if not isinstance(manifest, dict) or set(manifest) != {"artifacts", "schema"}:
raise LockError("API artifact manifest has extra or missing keys")
if manifest.get("schema") != API_ARTIFACT_SCHEMA:
raise LockError("API artifact manifest schema disagrees with the sealed F0 table")
artifacts = manifest.get("artifacts")
if not isinstance(artifacts, dict) or set(artifacts) != set(SEALED_API_ARTIFACTS):
raise LockError("API artifact manifest file set disagrees with the sealed F0 table")
identities: dict[str, str] = {}
for source_path, artifact in SEALED_API_ARTIFACTS.items():
identity_name, local_name, sealed_digest = artifact
actual_digest = hashlib.sha256((bundle / local_name).read_bytes()).hexdigest()
declared_digest = require_hex(
artifacts.get(source_path), f"API artifact {source_path} SHA-256"
)
if actual_digest != declared_digest:
raise LockError(
f"API artifact {source_path} disagrees with its manifest digest"
)
require_sealed(actual_digest, sealed_digest, f"API artifact {source_path}")
identities[identity_name] = actual_digest
actual_manifest = hashlib.sha256(manifest_path.read_bytes()).hexdigest()
require_sealed(
actual_manifest,
SEALED_API_ARTIFACT_MANIFEST_SHA256,
"API artifact manifest SHA-256",
)
identities["api_artifact_manifest"] = actual_manifest
return identities
def cpp_bool(value: object) -> str:
if value is True:
return "true"
if value is False:
return "false"
raise LockError(f"expected JSON boolean, got {value!r}")
def join_types(values: object, label: str) -> str:
if not isinstance(values, list) or not all(isinstance(item, str) for item in values):
raise LockError(f"{label} is not a list of wasm value types: {values!r}")
for item in values:
if item not in WASM_VALTYPE:
raise LockError(f"{label} has unsupported wasm value type {item!r}")
return ",".join(values)
def wasm_signature(params: object, results: object, label: str) -> list[str]:
join_types(params, f"{label} params")
join_types(results, f"{label} results")
assert isinstance(params, list) and isinstance(results, list)
return [WASM_BYTE[item] for item in results] + [WASM_BYTE[item] for item in params]
def embed_body(data: bytes | None) -> str:
if data is None:
return (
"std::span<std::uint8_t const>\n"
"embeddedQuickJSProvider()\n"
"{\n"
" return {};\n"
"}\n"
)
lines = []
for offset in range(0, len(data), CHUNK):
chunk = data[offset : offset + CHUNK]
lines.append(' "' + "".join(f"\\x{b:02x}" for b in chunk) + '"')
return (
"namespace {\n"
"\n"
"constexpr char const sealedProvider[] =\n"
+ "\n".join(lines)
+ ";\n"
"\n"
"static_assert(\n"
f" sizeof(sealedProvider) - 1 == {len(data)},\n"
' "embedded provider size disagrees with its manifest");\n'
"\n"
"} // namespace\n"
"\n"
"std::span<std::uint8_t const>\n"
"embeddedQuickJSProvider()\n"
"{\n"
" return {\n"
" reinterpret_cast<std::uint8_t const*>(sealedProvider),\n"
" sizeof(sealedProvider) - 1};\n"
"}\n"
)
def format_export_row(item: dict[str, Any]) -> str:
kind = item["kind"]
name = item["name"]
if kind == "memory":
fields = (
quote(kind),
quote(name),
quote(""),
quote(""),
f"{int(item['minimum_pages'])}U",
f"{int(item['maximum_pages'])}U",
cpp_bool(item["memory64"]),
cpp_bool(item["shared"]),
)
elif kind == "function":
fields = (
quote(kind),
quote(name),
quote(join_types(item["params"], f"export {name} params")),
quote(join_types(item["results"], f"export {name} results")),
"0U",
"0U",
"false",
"false",
)
else:
raise LockError(f"unsupported provider export kind: {kind!r}")
return "{" + ", ".join(fields) + "}"
def require_typed_exports(exports: object, label: str) -> list[dict[str, Any]]:
if not isinstance(exports, list) or not all(isinstance(item, dict) for item in exports):
raise LockError(f"{label} must be a list of typed export rows")
names: list[str] = []
memory_rows: list[dict[str, Any]] = []
for item in exports:
kind = item.get("kind")
name = item.get("name")
if not isinstance(kind, str) or not isinstance(name, str) or not name:
raise LockError(f"{label} row is missing kind/name: {item!r}")
if kind == "function":
if set(item) != FUNCTION_EXPORT_KEYS:
raise LockError(f"{label} function {name} has extra or missing keys")
join_types(item.get("params"), f"{label} {name} params")
join_types(item.get("results"), f"{label} {name} results")
elif kind == "memory":
if set(item) != MEMORY_EXPORT_KEYS:
raise LockError(f"{label} memory row has extra or missing keys")
for key in ("minimum_pages", "maximum_pages"):
if not isinstance(item.get(key), int):
raise LockError(f"{label} memory row missing integer {key}")
if item.get("memory64") is not False or item.get("shared") is not False:
raise LockError(
f"{label} memory row is not memory64=false / shared=false"
)
memory_rows.append(item)
else:
raise LockError(f"{label} has unsupported export kind {kind!r}")
names.append(name)
if len(names) != len(set(names)):
raise LockError(f"{label} contains duplicate export names")
if names != sorted(names):
raise LockError(f"{label} must be normalized by export name")
if len(memory_rows) != 1 or memory_rows[0].get("name") != "memory":
raise LockError(f"{label} must export exactly one memory named memory")
return exports
def require_typed_imports(imports: object, label: str) -> list[dict[str, Any]]:
if not isinstance(imports, list) or not all(isinstance(item, dict) for item in imports):
raise LockError(f"{label} must be a list of typed import rows")
names: list[str] = []
for item in imports:
module = item.get("module")
name = item.get("name")
if not isinstance(module, str) or not isinstance(name, str) or not name:
raise LockError(f"{label} row is missing module/name: {item!r}")
if set(item) != IMPORT_KEYS:
raise LockError(f"{label} {name} has extra or missing keys")
wasm_signature(item.get("params"), item.get("results"), f"{label} {name}")
names.append(name)
if len(names) != len(set(names)):
raise LockError(f"{label} contains duplicate import names")
return imports
def require_build(build: object, label: str) -> dict[str, Any]:
if not isinstance(build, dict):
raise LockError(f"{label} is missing build metadata")
stack = build.get("wasm_stack_bytes")
memory = build.get("wasm_memory_max_bytes")
if stack != WASM_STACK_BYTES:
raise LockError(
f"{label} wasm_stack_bytes is {stack!r}, expected {WASM_STACK_BYTES}"
)
if memory != PROVIDER_MEMORY_MAX_BYTES:
raise LockError(
f"{label} wasm_memory_max_bytes is {memory!r}, "
f"expected {PROVIDER_MEMORY_MAX_BYTES}"
)
return build
def cross_compare_int(cmake: dict[str, str], key: str, actual: object, label: str) -> int:
expected = require_int(cmake, key)
if actual != expected:
raise LockError(
f"{label} disagrees between JSON ({actual!r}) and CMake {key} ({expected!r})"
)
return expected
def cross_compare_str(cmake: dict[str, str], key: str, actual: object, label: str) -> str:
expected = require(cmake, key)
if str(actual) != expected:
raise LockError(
f"{label} disagrees between JSON ({actual!r}) and CMake {key} ({expected!r})"
)
return expected
def validate_lock(
profile: dict[str, Any],
native: dict[str, Any],
cmake: dict[str, str],
wasmtime_version: str,
api_identities: dict[str, str],
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], dict[str, Any], str, str, int]:
schema = require(cmake, "MANIFEST_SCHEMA")
if schema != "xahau.quickjs.runtime-profile-lock.v1" or profile.get("schema") != schema:
raise LockError(f"unsupported QuickJS provider manifest schema: {schema}")
expected_sha = require_hex(require(cmake, "PROVIDER_SHA256"), "CMake provider SHA-256")
json_sha = require_hex(profile.get("provider", {}).get("sha256"), "JSON provider SHA-256")
if json_sha != expected_sha:
raise LockError("provider sha256 disagrees between JSON lock and cmake")
require_sealed(expected_sha, SEALED_PROVIDER_SHA256, "provider SHA-256")
expected_size = require_int(cmake, "PROVIDER_SIZE")
json_size = profile.get("provider", {}).get("size")
if json_size != expected_size:
raise LockError("provider size disagrees between JSON lock and cmake")
require_sealed(expected_size, SEALED_PROVIDER_SIZE, "provider size")
bytecode = require_hex(require(cmake, "BYTECODE_ABI_ID"), "CMake bytecode ABI")
if require_hex(profile.get("bytecode_abi_id"), "JSON bytecode ABI") != bytecode:
raise LockError("bytecode ABI disagrees between JSON lock and cmake")
require_sealed(bytecode, SEALED_BYTECODE_ABI_ID, "bytecode ABI")
runtime_profile = require_hex(
require(cmake, "RUNTIME_PROFILE_ID"), "CMake runtime-profile ID"
)
if require_hex(profile.get("runtime_profile_id"), "JSON runtime-profile ID") != runtime_profile:
raise LockError("runtime-profile ID disagrees between JSON lock and cmake")
require_sealed(
runtime_profile, SEALED_RUNTIME_PROFILE_ID, "runtime-profile ID"
)
pinned_wasmtime = require(cmake, "WASMTIME_VERSION")
engine = profile.get("source", {}).get("engine", {})
engine_version = engine.get("version")
if wasmtime_version != pinned_wasmtime or engine_version != pinned_wasmtime:
raise LockError(
f"QuickJS provider requires Wasmtime {pinned_wasmtime}, "
f"but CMake resolved {wasmtime_version} and JSON has {engine_version!r}"
)
require_sealed(pinned_wasmtime, SEALED_WASMTIME_VERSION, "Wasmtime version")
require_sealed(
engine.get("configuration"),
SEALED_ENGINE_CONFIGURATION,
"Wasmtime engine configuration",
)
limits = profile.get("source", {}).get("limits")
if not isinstance(limits, dict):
raise LockError("JSON runtime-profile source is missing limits")
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_BYTES", limits.get("serialized_object_max_bytes"),
"serialized_object_max_bytes",
)
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_FIELDS", limits.get("serialized_object_max_fields"),
"serialized_object_max_fields",
)
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_SCOPES", limits.get("serialized_object_max_scopes"),
"serialized_object_max_scopes",
)
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_DEPTH", limits.get("serialized_object_max_depth"),
"serialized_object_max_depth",
)
for key, expected in SEALED_LIMITS.items():
require_sealed(limits.get(key), expected, key)
require_sealed(
limits.get("host_work_addressed_length_indices"),
SEALED_HOST_WORK_ADDRESSED_LENGTH_INDICES,
"host_work_addressed_length_indices",
)
cross_compare_int(cmake, "HEAP_BYTES", limits.get("quickjs_heap_bytes"), "quickjs_heap_bytes")
cross_compare_int(cmake, "STACK_BYTES", limits.get("quickjs_stack_bytes"), "quickjs_stack_bytes")
cross_compare_int(
cmake, "INITIALIZATION_FUEL", limits.get("wasmtime_fuel_per_initialization"),
"initialization fuel",
)
cross_compare_int(
cmake, "INVOCATION_FUEL", limits.get("wasmtime_fuel_per_invocation"),
"invocation fuel",
)
cross_compare_int(cmake, "HOST_WORK_BUDGET", limits.get("host_work_budget"), "host_work_budget")
cross_compare_int(
cmake, "HOST_WORK_BASE_PER_CALL", limits.get("host_work_base_per_call"),
"host_work_base_per_call",
)
cross_compare_int(
cmake, "HOST_WORK_PER_ADDRESSED_BYTE", limits.get("host_work_per_addressed_byte"),
"host_work_per_addressed_byte",
)
cross_compare_str(cmake, "HOST_WORK_METER", limits.get("host_work_meter"), "host_work_meter")
host_adapter_policy = cross_compare_str(
cmake, "HOST_ADAPTER_POLICY",
profile.get("source", {}).get("execution", {}).get("host_adapter_policy"),
"host_adapter_policy",
)
require_sealed(
host_adapter_policy, SEALED_HOST_ADAPTER_POLICY, "host_adapter_policy"
)
hook_api_version = cross_compare_int(
cmake, "HOOK_API_VERSION",
profile.get("source", {}).get("artifact", {}).get("hook_api_version"),
"hook_api_version",
)
require_sealed(hook_api_version, SEALED_HOOK_API_VERSION, "hook_api_version")
provider = profile.get("provider")
source_provider = profile.get("source", {}).get("provider")
if not isinstance(provider, dict) or not isinstance(source_provider, dict):
raise LockError("JSON lock is missing provider or source.provider")
require_build(provider.get("build"), "provider.build")
require_build(source_provider.get("build"), "source.provider.build")
require_sealed(
source_provider.get("forbidden_import_modules"),
["wasi_snapshot_preview1"],
"forbidden provider import modules",
)
expected_import_count = require_int(cmake, "PROVIDER_IMPORT_COUNT")
provider_imports = require_typed_imports(provider.get("imports"), "provider.imports")
source_imports = require_typed_imports(
source_provider.get("imports"), "source.provider.imports"
)
native_imports = native.get("selected")
if not isinstance(native_imports, list):
raise LockError("native ABI snapshot is missing selected imports")
if provider_imports != source_imports:
raise LockError("provider import signatures disagree between JSON copies")
if provider_imports != SEALED_IMPORTS:
raise LockError(
"provider import signatures disagree with the sealed F0 table"
)
if (
len(provider_imports) != expected_import_count
or len(native_imports) != expected_import_count
):
raise LockError("QuickJS provider/native ABI import counts differ")
provider_names = [item["name"] for item in provider_imports]
native_names = [item["name"] for item in native_imports]
if len(set(provider_names)) != expected_import_count or set(provider_names) != set(
native_names
):
raise LockError("QuickJS provider/native ABI import sets differ")
native_by_name = {item["name"]: item for item in native_imports}
for item in provider_imports:
native_item = native_by_name[item["name"]]
expected_sig = wasm_signature(
item["params"], item["results"], f"import {item['name']}"
)
if native_item.get("wasm_signature") != expected_sig:
raise LockError(
f"import {item['name']} wasm signature disagrees between JSON and native ABI"
)
expected_export_count = require_int(cmake, "PROVIDER_EXPORT_COUNT")
provider_exports = require_typed_exports(provider.get("exports"), "provider.exports")
allowed_exports = require_typed_exports(
source_provider.get("allowed_exports"), "source.provider.allowed_exports"
)
if len(provider_exports) != expected_export_count:
raise LockError("QuickJS provider export count disagrees with its CMake lock")
if provider_exports != allowed_exports:
raise LockError("provider export signatures disagree between JSON copies")
memory = next(item for item in provider_exports if item["kind"] == "memory")
if (
int(memory["minimum_pages"]) != PROVIDER_MEMORY_MINIMUM_PAGES
or int(memory["maximum_pages"]) != PROVIDER_MEMORY_MAXIMUM_PAGES
or memory["memory64"] is not False
or memory["shared"] is not False
):
raise LockError(
"QuickJS provider memory shape is not min 7 / max 512 / "
"memory64=false / shared=false"
)
if provider_exports != SEALED_EXPORTS:
raise LockError(
"provider export signatures disagree with the sealed F0 table"
)
surface = profile.get("javascript_surface")
nested_surface = profile.get("source", {}).get("javascript_surface")
if not isinstance(surface, dict) or not isinstance(nested_surface, dict):
raise LockError("JSON lock is missing javascript_surface")
if set(surface) != SURFACE_KEYS or set(nested_surface) != NESTED_SURFACE_KEYS:
raise LockError("javascript_surface has extra or missing keys")
declaration_sha = require_hex(
surface.get("declaration_sha256"), "javascript surface declaration SHA-256"
)
surface_sha = require_hex(surface.get("sha256"), "javascript surface SHA-256")
require_sealed(
declaration_sha,
api_identities["exact_v1_declaration"],
"exact-v1 declaration SHA-256",
)
require_sealed(
surface_sha, api_identities["selected_surface"], "selected surface SHA-256"
)
for key in ("declaration", "manifest", "schema"):
if surface.get(key) != nested_surface.get(key):
raise LockError(f"javascript_surface.{key} disagrees between JSON copies")
return (
provider_imports,
provider_exports,
native_imports,
declaration_sha,
surface_sha,
expected_size,
)
def render_source(
*,
provenance: str,
cmake: dict[str, str],
native: dict[str, Any],
provider_imports: list[dict[str, Any]],
provider_exports: list[dict[str, Any]],
native_imports: list[dict[str, Any]],
declaration_sha: str,
surface_sha: str,
broad_declaration_sha: str,
api_artifact_manifest_sha: str,
manifest_sha: str,
expected_sha: str,
expected_size: int,
wasm_bytes: bytes | None,
actual_native: str,
) -> str:
provider_names = [item["name"] for item in provider_imports]
provider_rows = ",\n ".join(
"{"
+ ", ".join(
(
quote(item["module"]),
quote(item["name"]),
quote(join_types(item["params"], f"import {item['name']} params")),
quote(join_types(item["results"], f"import {item['name']} results")),
)
)
+ "}"
for item in provider_imports
)
native_rows = ",\n ".join(
"{"
+ ", ".join(
(
quote(item["name"]),
quote(item["return_type"]),
quote(",".join(item["param_types"])),
quote(item["amendment"] or ""),
)
)
+ "}"
for item in native_imports
)
export_rows = ",\n ".join(format_export_row(item) for item in provider_exports)
source = native["source"]
memory = next(item for item in provider_exports if item["kind"] == "memory")
return f"""// Generated by cmake/GenerateQuickJSProviderBundle.py; do not edit.
// Source: {provenance}
#include <xrpld/app/hook/QuickJSHookRuntime.h>
#include <xrpld/app/hook/detail/QuickJSProviderProfile.h>
#include <array>
#include <cstdint>
#include <span>
#include <string_view>
namespace hook::artifact::generated {{
std::array<std::uint8_t, 32> const providerSHA256 = {{
{hex_bytes(expected_sha)}}};
std::array<std::uint8_t, 32> const bytecodeABI = {{
{hex_bytes(require(cmake, "BYTECODE_ABI_ID"))}}};
std::array<std::uint8_t, 32> const runtimeProfile = {{
{hex_bytes(require(cmake, "RUNTIME_PROFILE_ID"))}}};
std::string_view const providerManifestSHA256 = {quote(manifest_sha)};
std::size_t const providerSize = {expected_size};
std::uint16_t const hookApiVersion = {require(cmake, "HOOK_API_VERSION")};
std::uint64_t const initializationFuel = {require(cmake, "INITIALIZATION_FUEL")}ULL;
std::uint64_t const invocationFuel = {require(cmake, "INVOCATION_FUEL")}ULL;
std::string_view const hostWorkMeter = {quote(require(cmake, "HOST_WORK_METER"))};
std::uint64_t const hostWorkBudget = {require(cmake, "HOST_WORK_BUDGET")}ULL;
std::uint64_t const hostWorkBasePerCall = {require(cmake, "HOST_WORK_BASE_PER_CALL")}ULL;
std::uint64_t const hostWorkPerAddressedByte =
{require(cmake, "HOST_WORK_PER_ADDRESSED_BYTE")}ULL;
std::string_view const hostAdapterPolicy = {quote(require(cmake, "HOST_ADAPTER_POLICY"))};
std::uint32_t const heapBytes = {require(cmake, "HEAP_BYTES")}U;
std::uint32_t const stackBytes = {require(cmake, "STACK_BYTES")}U;
std::uint32_t const wasmStackBytes = {WASM_STACK_BYTES}U;
std::uint32_t const serializedObjectMaxBytes =
{require(cmake, "SERIALIZED_OBJECT_MAX_BYTES")}U;
std::uint32_t const serializedObjectMaxFields =
{require(cmake, "SERIALIZED_OBJECT_MAX_FIELDS")}U;
std::uint32_t const serializedObjectMaxScopes =
{require(cmake, "SERIALIZED_OBJECT_MAX_SCOPES")}U;
std::uint32_t const serializedObjectMaxDepth =
{require(cmake, "SERIALIZED_OBJECT_MAX_DEPTH")}U;
std::uint32_t const providerMemoryMinimumPages = {int(memory["minimum_pages"])}U;
std::uint32_t const providerMemoryMaximumPages = {int(memory["maximum_pages"])}U;
bool const providerMemory64 = false;
bool const providerMemoryShared = false;
std::string_view const javascriptBroadDeclarationSHA256 =
{quote(broad_declaration_sha)};
std::string_view const javascriptExactV1DeclarationSHA256 =
{quote(declaration_sha)};
std::string_view const javascriptSurfaceSHA256 = {quote(surface_sha)};
std::string_view const javascriptAPIArtifactManifestSHA256 =
{quote(api_artifact_manifest_sha)};
namespace {{
std::string_view const providerImportNames[] = {{
{", ".join(quote(name) for name in provider_names)}}};
std::string_view const providerExportNames[] = {{
{", ".join(quote(item["name"]) for item in provider_exports)}}};
ProviderImportSignature const providerImportSignatureData[] = {{
{provider_rows}}};
ProviderExportSignature const providerExportSignatureData[] = {{
{export_rows}}};
NativeImportSignature const nativeImportSignatureData[] = {{
{native_rows}}};
}} // namespace
std::span<std::string_view const> const providerImports{{providerImportNames}};
std::span<std::string_view const> const providerExports{{providerExportNames}};
std::span<ProviderImportSignature const> const providerImportSignatures{{
providerImportSignatureData}};
std::span<ProviderExportSignature const> const providerExportSignatures{{
providerExportSignatureData}};
std::span<NativeImportSignature const> const nativeImportSignatures{{
nativeImportSignatureData}};
std::string_view const nativeABISourceRepository = {quote(source["repository"])};
std::string_view const nativeABISourceCommit = {quote(source["commit"])};
std::string_view const nativeABISourcePath = {quote(source["path"])};
std::string_view const nativeABISHA256 = {quote(actual_native)};
std::size_t const nativeABICatalogueCount = {source["macro_function_count"]};
}} // namespace hook::artifact::generated
namespace hook {{
{embed_body(wasm_bytes)}
}} // namespace hook
"""
def project_bundle(bundle: Path, wasmtime_version: str, output: Path) -> int:
profile_path = bundle / "jshookz_provider.manifest.json"
cmake_path = bundle / "jshookz_provider.manifest.cmake"
native_path = bundle / "jshookz_provider.native-abi.json"
api_manifest_path = bundle / "api-artifacts.json"
api_paths = tuple(
bundle / local_name
for _, local_name, _ in SEALED_API_ARTIFACTS.values()
)
for path in (profile_path, cmake_path, native_path, api_manifest_path, *api_paths):
if not path.is_file():
print(f"missing sealed QuickJS bundle file: {path}", file=sys.stderr)
return 1
cmake = parse_cmake(cmake_path)
actual_manifest = hashlib.sha256(profile_path.read_bytes()).hexdigest()
if actual_manifest != require(cmake, "MANIFEST_SHA256"):
print(
"QuickJS provider JSON manifest does not match its CMake projection",
file=sys.stderr,
)
return 1
actual_native = hashlib.sha256(native_path.read_bytes()).hexdigest()
if actual_native != require(cmake, "NATIVE_ABI_SHA256"):
print(
"QuickJS native ABI snapshot does not match its sealed digest",
file=sys.stderr,
)
return 1
try:
profile = json.loads(profile_path.read_text())
native = json.loads(native_path.read_text())
api_identities = validate_api_artifacts(bundle)
(
provider_imports,
provider_exports,
native_imports,
declaration_sha,
surface_sha,
expected_size,
) = validate_lock(
profile, native, cmake, wasmtime_version, api_identities
)
require_sealed(
actual_manifest, SEALED_MANIFEST_SHA256, "provider manifest SHA-256"
)
require_sealed(
actual_native, SEALED_NATIVE_ABI_SHA256, "native ABI SHA-256"
)
except LockError as error:
print(str(error), file=sys.stderr)
return 1
wasm_name = require(cmake, "PROVIDER_FILE")
wasm_path = bundle / wasm_name
expected_sha = require(cmake, "PROVIDER_SHA256").lower()
fingerprint = hashlib.sha256()
for path in (
profile_path,
cmake_path,
native_path,
api_manifest_path,
*api_paths,
Path(__file__),
):
fingerprint.update(path.read_bytes())
fingerprint.update(wasmtime_version.encode())
wasm_bytes: bytes | None = None
if wasm_path.is_file():
wasm_bytes = wasm_path.read_bytes()
digest = hashlib.sha256(wasm_bytes).hexdigest()
if digest != expected_sha or len(wasm_bytes) != expected_size:
print(
"QuickJS provider WASM does not match its generated manifest",
file=sys.stderr,
)
return 1
fingerprint.update(b"wasm")
fingerprint.update(bytes.fromhex(digest))
provenance = f"{wasm_path} sha256={digest} size={len(wasm_bytes)}"
else:
fingerprint.update(b"absent")
provenance = "no provider binary was present in the bundle directory"
stamp_path = output.with_name(output.name + ".stamp")
stamp = fingerprint.hexdigest()
if output.is_file() and stamp_path.is_file() and stamp_path.read_text() == stamp:
return 0
if wasm_bytes is None:
print(
f"No QuickJS provider WASM at {wasm_path}; nothing is embedded "
"and the daemon will refuse to start",
file=sys.stderr,
)
text = render_source(
provenance=provenance,
cmake=cmake,
native=native,
provider_imports=provider_imports,
provider_exports=provider_exports,
native_imports=native_imports,
declaration_sha=declaration_sha,
surface_sha=surface_sha,
broad_declaration_sha=api_identities["broad_declaration"],
api_artifact_manifest_sha=api_identities["api_artifact_manifest"],
manifest_sha=actual_manifest,
expected_sha=expected_sha,
expected_size=expected_size,
wasm_bytes=wasm_bytes,
actual_native=actual_native,
)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(text)
stamp_path.write_text(stamp)
return 0
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--bundle-dir", type=Path, required=True)
parser.add_argument("--wasmtime-version", required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
try:
return project_bundle(args.bundle_dir, args.wasmtime_version, args.output)
except LockError as error:
print(str(error), file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,90 @@
set(XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/external/quickjs-provider"
CACHE PATH
"Directory containing the sealed QuickJS provider WASM and generated manifests")
get_filename_component(XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}"
ABSOLUTE
BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(XAHAU_QUICKJS_PROVIDER_MANIFEST
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/jshookz_provider.manifest.cmake")
set(XAHAU_QUICKJS_PROVIDER_PROFILE_LOCK
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/jshookz_provider.manifest.json")
set(XAHAU_QUICKJS_NATIVE_ABI
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/jshookz_provider.native-abi.json")
set(XAHAU_QUICKJS_API_ARTIFACT_MANIFEST
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/api-artifacts.json")
set(XAHAU_QUICKJS_BROAD_DECLARATION
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/hooks-api.d.ts")
set(XAHAU_QUICKJS_EXACT_V1_DECLARATION
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/xahau-quickjs-v1.d.ts")
set(XAHAU_QUICKJS_SELECTED_SURFACE
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/xahau-quickjs-v1.surface.json")
foreach(_bundle_file
"${XAHAU_QUICKJS_PROVIDER_MANIFEST}"
"${XAHAU_QUICKJS_PROVIDER_PROFILE_LOCK}"
"${XAHAU_QUICKJS_NATIVE_ABI}"
"${XAHAU_QUICKJS_API_ARTIFACT_MANIFEST}"
"${XAHAU_QUICKJS_BROAD_DECLARATION}"
"${XAHAU_QUICKJS_EXACT_V1_DECLARATION}"
"${XAHAU_QUICKJS_SELECTED_SURFACE}")
if(NOT EXISTS "${_bundle_file}")
message(FATAL_ERROR
"Missing sealed QuickJS provider bundle file: ${_bundle_file}")
endif()
endforeach()
if(NOT DEFINED wasmtime_VERSION_STRING OR wasmtime_VERSION_STRING STREQUAL "")
message(FATAL_ERROR
"QuickJS provider requires an exact Wasmtime version, but the resolved "
"package did not publish one")
endif()
find_program(XAHAU_QUICKJS_PYTHON NAMES python3 python)
if(NOT XAHAU_QUICKJS_PYTHON)
message(FATAL_ERROR
"Python is required to project the sealed QuickJS provider bundle")
endif()
set(XAHAU_QUICKJS_GENERATED_INCLUDE_DIR
"${CMAKE_CURRENT_BINARY_DIR}/generated")
set(XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE
"${XAHAU_QUICKJS_GENERATED_INCLUDE_DIR}/xrpld/app/hook/detail/QuickJSProviderValues.cpp")
set(XAHAU_QUICKJS_BUNDLE_SCRIPT
"${CMAKE_CURRENT_LIST_DIR}/GenerateQuickJSProviderBundle.py")
# The gitignored wasm is not a CMake depend (ninja cannot name a missing
# file). Refresh on every rippled build; the script no-ops when inputs
# including wasm existence/digest are unchanged.
add_custom_target(xahau_quickjs_refresh_provider
COMMAND
"${XAHAU_QUICKJS_PYTHON}"
"${XAHAU_QUICKJS_BUNDLE_SCRIPT}"
--bundle-dir "${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}"
--wasmtime-version "${wasmtime_VERSION_STRING}"
--output "${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}"
BYPRODUCTS "${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}"
DEPENDS
"${XAHAU_QUICKJS_PROVIDER_PROFILE_LOCK}"
"${XAHAU_QUICKJS_PROVIDER_MANIFEST}"
"${XAHAU_QUICKJS_NATIVE_ABI}"
"${XAHAU_QUICKJS_API_ARTIFACT_MANIFEST}"
"${XAHAU_QUICKJS_BROAD_DECLARATION}"
"${XAHAU_QUICKJS_EXACT_V1_DECLARATION}"
"${XAHAU_QUICKJS_SELECTED_SURFACE}"
"${XAHAU_QUICKJS_BUNDLE_SCRIPT}"
COMMENT "Projecting sealed QuickJS provider values"
VERBATIM)
message(STATUS
"QuickJS provider values: ${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}")
add_custom_target(xahau_quickjs_lock_mutations
COMMAND
"${XAHAU_QUICKJS_PYTHON}"
"${CMAKE_CURRENT_LIST_DIR}/test_generate_quickjs_provider_bundle.py"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Proving sealed QuickJS lock mutation reds"
VERBATIM)

View File

@@ -12,7 +12,7 @@ if (static OR MSVC)
else ()
set (Boost_USE_STATIC_RUNTIME OFF)
endif ()
find_dependency (Boost
find_dependency (Boost 1.70
COMPONENTS
chrono
container
@@ -52,3 +52,5 @@ if (TARGET ZLIB::ZLIB)
set_target_properties(OpenSSL::Crypto PROPERTIES
INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
endif ()
include ("${CMAKE_CURRENT_LIST_DIR}/RippleTargets.cmake")

View File

@@ -93,16 +93,27 @@ if (MSVC)
-errorreport:none
-machine:X64)
else ()
# HACK : because these need to come first, before any warning demotion
string (APPEND CMAKE_CXX_FLAGS " -Wall -Wdeprecated")
if (wextra)
string (APPEND CMAKE_CXX_FLAGS " -Wextra -Wno-unused-parameter")
endif ()
# not MSVC
target_compile_options (common
INTERFACE
-Wall
-Wdeprecated
$<$<BOOL:${is_clang}>:-Wno-deprecated-declarations>
$<$<BOOL:${wextra}>:-Wextra -Wno-unused-parameter>
$<$<BOOL:${werr}>:-Werror>
-fstack-protector
$<$<COMPILE_LANGUAGE:CXX>:
-frtti
-Wnon-virtual-dtor
>
-Wno-sign-compare
-Wno-unused-but-set-variable
-Wno-char-subscripts
-Wno-format
-Wno-unused-local-typedefs
$<$<BOOL:${is_gcc}>:
-Wno-unused-but-set-variable
-Wno-deprecated
>
$<$<NOT:$<CONFIG:Debug>>:-fno-strict-aliasing>
# tweak gcc optimization for debug
$<$<AND:$<BOOL:${is_gcc}>,$<CONFIG:Debug>>:-O0>

View File

@@ -85,7 +85,10 @@ include(target_link_modules)
# Level 01
add_module(xrpl beast)
target_link_libraries(xrpl.libxrpl.beast PUBLIC xrpl.imports.main)
target_link_libraries(xrpl.libxrpl.beast PUBLIC
xrpl.imports.main
xrpl.libpb
)
# Level 02
add_module(xrpl basics)
@@ -113,24 +116,9 @@ target_link_libraries(xrpl.libxrpl.protocol PUBLIC
add_module(xrpl resource)
target_link_libraries(xrpl.libxrpl.resource PUBLIC xrpl.libxrpl.protocol)
# Level 06
add_module(xrpl net)
target_link_libraries(xrpl.libxrpl.net PUBLIC
xrpl.libxrpl.basics
xrpl.libxrpl.json
xrpl.libxrpl.protocol
xrpl.libxrpl.resource
)
add_module(xrpl server)
target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol)
add_module(xrpl ledger)
target_link_libraries(xrpl.libxrpl.ledger PUBLIC
xrpl.libxrpl.basics
xrpl.libxrpl.json
xrpl.libxrpl.protocol
)
add_library(xrpl.libxrpl)
set_target_properties(xrpl.libxrpl PROPERTIES OUTPUT_NAME xrpl)
@@ -161,8 +149,6 @@ target_link_modules(xrpl PUBLIC
protocol
resource
server
net
ledger
)
# All headers in libxrpl are in modules.
@@ -178,9 +164,6 @@ if(xrpld)
add_executable(rippled)
if(tests)
target_compile_definitions(rippled PUBLIC ENABLE_TESTS)
target_compile_definitions(rippled PRIVATE
UNIT_TEST_REFERENCE_FEE=${UNIT_TEST_REFERENCE_FEE}
)
endif()
target_include_directories(rippled
PRIVATE
@@ -191,18 +174,106 @@ if(xrpld)
"${CMAKE_CURRENT_SOURCE_DIR}/src/xrpld/*.cpp"
)
target_sources(rippled PRIVATE ${sources})
add_dependencies(rippled xahau_quickjs_refresh_provider)
target_sources(rippled PRIVATE "${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}")
if(tests)
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/test/*.cpp"
)
if(HOOKS_TEST_ONLY OR DEFINED ENV{HOOKS_TEST_ONLY})
list(FILTER sources EXCLUDE REGEX "_test\\.cpp$")
message(STATUS "HOOKS_TEST_ONLY: excluded *_test.cpp from src/test/")
endif()
target_sources(rippled PRIVATE ${sources})
# External Env tests live next to the producer. Point at a directory of
# *_test.cpp files (typically jshookz/env-tests). hookz build-test-hooks
# must be on PATH. Coverage is not wired here.
if(NOT HOOKS_TEST_DIR AND DEFINED ENV{HOOKS_TEST_DIR})
set(HOOKS_TEST_DIR $ENV{HOOKS_TEST_DIR})
endif()
if(NOT HOOKS_SOURCE_DIR AND DEFINED ENV{HOOKS_SOURCE_DIR})
set(HOOKS_SOURCE_DIR $ENV{HOOKS_SOURCE_DIR})
endif()
if(HOOKS_TEST_DIR AND EXISTS "${HOOKS_TEST_DIR}")
file(GLOB EXTERNAL_HOOK_TESTS CONFIGURE_DEPENDS
"${HOOKS_TEST_DIR}/*_test.cpp"
)
if(EXTERNAL_HOOK_TESTS)
set(_hooks_extra_args "")
set(_hooks_source_deps "")
if(HOOKS_SOURCE_DIR)
foreach(_dir ${HOOKS_SOURCE_DIR})
list(APPEND _hooks_extra_args "--hooks-c-dir" "${_dir}")
string(REGEX REPLACE "^[^=]+=" "" _hook_dir "${_dir}")
if(EXISTS "${_hook_dir}")
file(GLOB_RECURSE _hook_dir_deps CONFIGURE_DEPENDS
"${_hook_dir}/*.c"
"${_hook_dir}/*.h"
"${_hook_dir}/*.ts"
"${_hook_dir}/*.js"
)
if(HOOKS_TEST_DIR)
list(FILTER _hook_dir_deps EXCLUDE REGEX "^${HOOKS_TEST_DIR}/")
endif()
list(APPEND _hooks_source_deps ${_hook_dir_deps})
endif()
endforeach()
list(REMOVE_DUPLICATES _hooks_source_deps)
endif()
set(_hooks_always_run OFF)
if(HOOKS_FORCE_RECOMPILE OR DEFINED ENV{HOOKS_FORCE_RECOMPILE})
list(APPEND _hooks_extra_args "--force-write" "--no-cache")
set(_hooks_always_run ON)
message(STATUS "Hook force recompile enabled (cache bypassed)")
endif()
foreach(_test_file ${EXTERNAL_HOOK_TESTS})
get_filename_component(_stem ${_test_file} NAME_WE)
set(_hooks_header "${HOOKS_TEST_DIR}/${_stem}_hooks.h")
if(_hooks_always_run)
add_custom_target(compile_hooks_${_stem} ALL
COMMAND hookz build-test-hooks "${_test_file}" ${_hooks_extra_args}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Compiling hooks for ${_stem} (forced)"
VERBATIM
)
list(APPEND EXTERNAL_HOOK_TARGETS compile_hooks_${_stem})
else()
add_custom_command(
OUTPUT "${_hooks_header}"
COMMAND hookz build-test-hooks "${_test_file}" ${_hooks_extra_args}
DEPENDS "${_test_file}" ${_hooks_source_deps}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Compiling hooks for ${_stem}"
VERBATIM
)
list(APPEND EXTERNAL_HOOK_HEADERS "${_hooks_header}")
endif()
endforeach()
if(_hooks_always_run)
foreach(_tgt ${EXTERNAL_HOOK_TARGETS})
add_dependencies(rippled ${_tgt})
endforeach()
else()
add_custom_target(compile_external_hooks DEPENDS ${EXTERNAL_HOOK_HEADERS})
add_dependencies(rippled compile_external_hooks)
endif()
target_sources(rippled PRIVATE ${EXTERNAL_HOOK_TESTS})
set_property(
SOURCE ${EXTERNAL_HOOK_TESTS}
APPEND PROPERTY INCLUDE_DIRECTORIES "${HOOKS_TEST_DIR}"
)
message(STATUS "Including external hook tests from: ${HOOKS_TEST_DIR}")
endif()
endif()
endif()
target_link_libraries(rippled
Ripple::boost
Ripple::opts
Ripple::libs
wasmtime::wasmtime
xrpl.libxrpl
# Workaround for a Conan 1.x bug that prevents static linking of libstdc++
# when a dependency (snappy) modifies system_libs. See the comment in

View File

@@ -33,8 +33,6 @@ setup_target_for_coverage_gcovr(
FORMAT ${coverage_format}
EXECUTABLE rippled
EXECUTABLE_ARGS --unittest$<$<BOOL:${coverage_test}>:=${coverage_test}> --unittest-jobs ${coverage_test_parallelism} --quiet --unittest-log
EXCLUDE "src/test" "src/tests" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
EXCLUDE "src/test" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
DEPENDENCIES rippled
)
add_code_coverage_to_target(opts INTERFACE)

View File

@@ -19,9 +19,7 @@ install (
xrpl.libxrpl.json
xrpl.libxrpl.protocol
xrpl.libxrpl.resource
xrpl.libxrpl.ledger
xrpl.libxrpl.server
xrpl.libxrpl.net
xrpl.libxrpl
antithesis-sdk-cpp
EXPORT RippleExports

View File

@@ -28,11 +28,15 @@ target_compile_options (opts
$<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized>
$<$<BOOL:${perf}>:-fno-omit-frame-pointer>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
target_link_libraries (opts
INTERFACE
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)

View File

@@ -1,8 +1,23 @@
#[===================================================================[
sanity checks
convenience variables and sanity checks
#]===================================================================]
get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
include(ProcessorCount)
if (NOT ep_procs)
ProcessorCount(ep_procs)
if (ep_procs GREATER 1)
# never use more than half of cores for EP builds
math (EXPR ep_procs "${ep_procs} / 2")
message (STATUS "Using ${ep_procs} cores for ExternalProject builds.")
endif ()
endif ()
get_property (is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (is_multiconfig STREQUAL "NOTFOUND")
if (${CMAKE_GENERATOR} STREQUAL "Xcode" OR ${CMAKE_GENERATOR} MATCHES "^Visual Studio")
set (is_multiconfig TRUE)
endif ()
endif ()
set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
if (NOT is_multiconfig)
@@ -16,22 +31,42 @@ if (NOT is_multiconfig)
endif ()
endif ()
get_directory_property(has_parent PARENT_DIRECTORY)
if (has_parent)
set (is_root_project OFF)
else ()
set (is_root_project ON)
endif ()
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") # both Clang and AppleClang
set (is_clang TRUE)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
message (FATAL_ERROR "This project requires clang 16 or later")
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0)
message (FATAL_ERROR "This project requires clang 8 or later")
endif ()
# TODO min AppleClang version check ?
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set (is_gcc TRUE)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
message (FATAL_ERROR "This project requires GCC 12 or later")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0)
message (FATAL_ERROR "This project requires GCC 8 or later")
endif ()
endif ()
if (CMAKE_GENERATOR STREQUAL "Xcode")
set (is_xcode TRUE)
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set (is_linux TRUE)
else ()
set (is_linux FALSE)
endif ()
if ("$ENV{CI}" STREQUAL "true" OR "$ENV{CONTINUOUS_INTEGRATION}" STREQUAL "true")
set (is_ci TRUE)
else ()
set (is_ci FALSE)
endif ()
# check for in-source build and fail
if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message (FATAL_ERROR "Builds (in-source) are not allowed in "

View File

@@ -1,54 +1,28 @@
#[===================================================================[
declare options and variables
declare user options/settings
#]===================================================================]
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set (is_linux TRUE)
else()
set(is_linux FALSE)
endif()
include(ProcessorCount)
if("$ENV{CI}" STREQUAL "true" OR "$ENV{CONTINUOUS_INTEGRATION}" STREQUAL "true")
set(is_ci TRUE)
else()
set(is_ci FALSE)
endif()
get_directory_property(has_parent PARENT_DIRECTORY)
if(has_parent)
set(is_root_project OFF)
else()
set(is_root_project ON)
endif()
ProcessorCount(PROCESSOR_COUNT)
option(assert "Enables asserts, even in release builds" OFF)
option(xrpld "Build xrpld" ON)
option(tests "Build tests" ON)
if(tests)
# This setting allows making a separate workflow to test fees other than default 10
if(NOT UNIT_TEST_REFERENCE_FEE)
set(UNIT_TEST_REFERENCE_FEE "10" CACHE STRING "")
endif()
endif()
option(unity "Creates a build using UNITY support in cmake." OFF)
option(unity "Creates a build using UNITY support in cmake. This is the default" ON)
if(unity)
if(NOT is_ci)
set(CMAKE_UNITY_BUILD_BATCH_SIZE 15 CACHE STRING "")
endif()
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
endif()
if(is_clang AND is_linux)
option(voidstar "Enable Antithesis instrumentation." OFF)
endif()
if(is_gcc OR is_clang)
include(ProcessorCount)
ProcessorCount(PROCESSOR_COUNT)
option(coverage "Generates coverage info." OFF)
option(profile "Add profiling flags" OFF)
set(coverage_test_parallelism "${PROCESSOR_COUNT}" CACHE STRING
@@ -68,7 +42,6 @@ else()
set(coverage OFF CACHE BOOL "gcc/clang only" FORCE)
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
endif()
if(is_linux)
option(BUILD_SHARED_LIBS "build shared ripple libraries" OFF)
option(static "link protobuf, openssl, libc++, and boost statically" ON)
@@ -85,13 +58,11 @@ else()
set(use_gold OFF CACHE BOOL "gold linker, linux only" FORCE)
set(use_mold OFF CACHE BOOL "mold linker, linux only" FORCE)
endif()
if(is_clang)
option(use_lld "enables detection of lld linker" ON)
else()
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)
endif()
option(jemalloc "Enables jemalloc for heap profiling" OFF)
option(werr "treat warnings as errors" OFF)
option(local_protobuf
@@ -125,6 +96,16 @@ if(san)
message(FATAL_ERROR "${san} sanitizer does not seem to be supported by your compiler")
endif()
endif()
set(container_label "" CACHE STRING "tag to use for package building containers")
option(packages_only
"ONLY generate package building targets. This is special use-case and almost \
certainly not what you want. Use with caution as you won't be able to build \
any compiled targets locally." OFF)
option(have_package_container
"Sometimes you already have the tagged container you want to use for package \
building and you don't want docker to rebuild it. This flag will detach the \
dependency of the package build from the container build. It's an advanced \
use case and most likely you should not be touching this flag." OFF)
# the remaining options are obscure and rarely used
option(beast_no_unit_test_inline
@@ -138,13 +119,15 @@ option(boost_show_deprecated
"Allow boost to fail on deprecated usage. Only useful if you're trying\
to find deprecated calls."
OFF)
option(beast_hashers
"Use local implementations for sha/ripemd hashes (experimental, not recommended)"
OFF)
if(WIN32)
option(beast_disable_autolink "Disables autolinking of system libraries on WIN32" OFF)
else()
set(beast_disable_autolink OFF CACHE BOOL "WIN32 only" FORCE)
endif()
if(coverage)
message(STATUS "coverage build requested - forcing Debug build")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "build type" FORCE)

View File

@@ -2,6 +2,7 @@ find_package(Boost 1.86 REQUIRED
COMPONENTS
chrono
container
context
coroutine
date_time
filesystem
@@ -24,7 +25,7 @@ endif()
target_link_libraries(ripple_boost
INTERFACE
Boost::headers
Boost::boost
Boost::chrono
Boost::container
Boost::coroutine

View File

@@ -0,0 +1 @@
find_package(wasmtime REQUIRED)

View File

@@ -0,0 +1,756 @@
#!/usr/bin/env python3
"""Fail-closed generator/checker mutation reds for the sealed QuickJS lock."""
from __future__ import annotations
import hashlib
import json
import re
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = Path(__file__).with_name("GenerateQuickJSProviderBundle.py")
BUNDLE = ROOT / "external" / "quickjs-provider"
WASM_VERSION = "47.0.3"
CMAKE_SET = re.compile(r'^set\(XAHAU_QUICKJS_([A-Z0-9_]+) "([^"]*)"\)\s*$')
PIN = {
"provider_sha256": "f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df",
"provider_size": "1116638",
"manifest_sha256": "1673b27d21063957cece9095cceb7e2dbfa53b6f49a5b7f824a486d7d90b2391",
"runtime_profile_id": "29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25",
"broad_declaration_sha256": "649079b831bc68eff0f440644649a505addafb08a3f5174e4031c8de6aedffa0",
"exact_v1_declaration_sha256": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da",
"surface_sha256": "96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac",
"api_artifact_manifest_sha256": "95babf622bf5edff76fdcbd9746bad8f1f8b275a3d4bacdc320ff4deae543eba",
"bytecode_abi": "75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e",
"native_abi": "328ec938dcad875f3bdf25b8d779dd77f3571589818ca9271cb98c64c7018f99",
"wasm_stack_bytes": "131072",
}
RECEIPT_B = {
"provider_sha256": "40f9ac0203afa9296196c627bc94e669ad789322ddc1e4ab58ba0cf6d32a5e21",
"provider_size": "1101461",
"runtime_profile_id": "60950ca8b6fe4dd2a35559367051998bc04f75e8aa41934dd4caad0de5a1c3ba",
"declaration_sha256": "56b4b2974b8a63a550721abd60350e392990762e76666f050b1a7c810e584957",
"surface_sha256": "b112346b95da74d04930ba86bd597e56a428e41e11581804ccc49907ae206eb0",
}
TYPED_IMPORT_ROWS = (
'{"env", "accept", "i32,i32,i64", "i64"}',
'{"env", "emit", "i32,i32,i32,i32", "i64"}',
'{"env", "etxn_reserve", "i32", "i64"}',
'{"env", "hook_account", "i32,i32", "i64"}',
'{"env", "ledger_last_hash", "i32,i32", "i64"}',
'{"env", "ledger_last_time", "", "i64"}',
'{"env", "ledger_seq", "", "i64"}',
'{"env", "otxn_type", "", "i64"}',
'{"env", "prepare", "i32,i32,i32,i32", "i64"}',
'{"env", "rollback", "i32,i32,i64", "i64"}',
'{"env", "state", "i32,i32,i32,i32", "i64"}',
'{"env", "state_set", "i32,i32,i32,i32", "i64"}',
'{"env", "trace", "i32,i32,i32,i32,i32", "i64"}',
)
TYPED_EXPORT_ROWS = (
'{"function", "_initialize", "", "", 0U, 0U, false, false}',
'{"function", "free", "i32", "", 0U, 0U, false, false}',
'{"function", "malloc", "i32", "i32", 0U, 0U, false, false}',
'{"memory", "memory", "", "", 7U, 512U, false, false}',
'{"function", "qjs_cbak", "i32,i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_compile", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_compile_module", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_destroy", "", "", 0U, 0U, false, false}',
'{"function", "qjs_enable_coverage", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_eval", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_eval_bytecode", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_eval_module", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_bytecode_len", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_bytecode_ptr", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_result_len", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_result_ptr", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_hook", "i32,i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_init", "", "", 0U, 0U, false, false}',
'{"function", "qjs_set_max_stack_size", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_set_memory_limit", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_set_seed", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_validate_hook_module", "i32,i32", "i32", 0U, 0U, false, false}',
)
def json_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.manifest.json"
def cmake_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.manifest.cmake"
def native_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.native-abi.json"
def wasm_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.wasm"
def api_manifest_path(bundle: Path) -> Path:
return bundle / "api-artifacts.json"
def broad_declaration_path(bundle: Path) -> Path:
return bundle / "hooks-api.d.ts"
def exact_v1_declaration_path(bundle: Path) -> Path:
return bundle / "xahau-quickjs-v1.d.ts"
def surface_path(bundle: Path) -> Path:
return bundle / "xahau-quickjs-v1.surface.json"
def load_json(bundle: Path) -> dict:
return json.loads(json_path(bundle).read_text())
def write_json(bundle: Path, data: dict) -> None:
json_path(bundle).write_text(json.dumps(data, indent=2) + "\n")
def set_cmake(bundle: Path, key: str, value: str) -> None:
path = cmake_path(bundle)
lines = []
replaced = False
for line in path.read_text().splitlines():
match = CMAKE_SET.match(line)
if match and match.group(1) == key:
lines.append(f'set(XAHAU_QUICKJS_{key} "{value}")')
replaced = True
else:
lines.append(line)
if not replaced:
raise AssertionError(f"CMake lock is missing {key}")
path.write_text("\n".join(lines) + "\n")
def rehash_manifest(bundle: Path) -> None:
digest = hashlib.sha256(json_path(bundle).read_bytes()).hexdigest()
set_cmake(bundle, "MANIFEST_SHA256", digest)
def rehash_native(bundle: Path) -> None:
digest = hashlib.sha256(native_path(bundle).read_bytes()).hexdigest()
set_cmake(bundle, "NATIVE_ABI_SHA256", digest)
def load_api_manifest(bundle: Path) -> dict:
return json.loads(api_manifest_path(bundle).read_text())
def write_api_manifest(bundle: Path, data: dict) -> None:
api_manifest_path(bundle).write_text(json.dumps(data, indent=2) + "\n")
def clone_bundle(root: Path) -> Path:
dest = root / "bundle"
shutil.copytree(BUNDLE, dest, ignore=shutil.ignore_patterns("README.md"))
return dest
def generate(
bundle: Path, work: Path, wasmtime_version: str = WASM_VERSION
) -> tuple[int, str, str]:
output = work / "QuickJSProviderValues.cpp"
completed = subprocess.run(
[
sys.executable,
str(SCRIPT),
"--bundle-dir",
str(bundle),
"--wasmtime-version",
wasmtime_version,
"--output",
str(output),
],
check=False,
capture_output=True,
text=True,
)
cpp = output.read_text() if output.is_file() else ""
return completed.returncode, completed.stderr, cpp
def hex_bytes(value: str) -> str:
return ", ".join(f"0x{value[i:i + 2]}" for i in range(0, 64, 2))
def pin_holds(returncode: int, cpp: str) -> bool:
if returncode != 0 or not cpp:
return False
required = [
hex_bytes(PIN["provider_sha256"]),
f"std::size_t const providerSize = {PIN['provider_size']};",
f'"{PIN["manifest_sha256"]}"',
hex_bytes(PIN["runtime_profile_id"]),
f'"{PIN["broad_declaration_sha256"]}"',
f'"{PIN["exact_v1_declaration_sha256"]}"',
f'"{PIN["surface_sha256"]}"',
f'"{PIN["api_artifact_manifest_sha256"]}"',
hex_bytes(PIN["bytecode_abi"]),
f'"{PIN["native_abi"]}"',
f"std::uint32_t const wasmStackBytes = {PIN['wasm_stack_bytes']}U;",
"std::uint32_t const serializedObjectMaxBytes =\n 1048576U;",
"std::uint32_t const serializedObjectMaxFields =\n 32768U;",
"std::uint32_t const serializedObjectMaxScopes =\n 32769U;",
"std::uint32_t const serializedObjectMaxDepth =\n 10U;",
"std::uint32_t const providerMemoryMinimumPages = 7U;",
"std::uint32_t const providerMemoryMaximumPages = 512U;",
"constexpr char const sealedProvider[] =",
f"sizeof(sealedProvider) - 1 == {PIN['provider_size']}",
]
if any(item not in cpp for item in required):
return False
return all(row in cpp for row in (*TYPED_IMPORT_ROWS, *TYPED_EXPORT_ROWS))
class GenerateQuickJSProviderBundleTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
missing = [
path.name
for path in (
json_path(BUNDLE),
cmake_path(BUNDLE),
native_path(BUNDLE),
wasm_path(BUNDLE),
api_manifest_path(BUNDLE),
broad_declaration_path(BUNDLE),
exact_v1_declaration_path(BUNDLE),
surface_path(BUNDLE),
SCRIPT,
)
if not path.is_file()
]
if missing:
raise AssertionError(
"sealed QuickJS bundle is incomplete: " + ", ".join(missing)
)
def mutate(
self, mutator, wasmtime_version: str = WASM_VERSION
) -> tuple[int, str, str]:
with tempfile.TemporaryDirectory() as tmp:
work = Path(tmp)
bundle = clone_bundle(work)
mutator(bundle)
return generate(bundle, work / "out", wasmtime_version)
def assert_generator_red(
self, mutator, fragment: str, wasmtime_version: str = WASM_VERSION
) -> None:
returncode, stderr, cpp = self.mutate(mutator, wasmtime_version)
self.assertNotEqual(returncode, 0, stderr)
self.assertIn(fragment, stderr)
self.assertFalse(pin_holds(returncode, cpp), stderr)
def test_unmutated_lock_projects_pin_and_typed_exports(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
work = Path(tmp)
returncode, stderr, cpp = generate(clone_bundle(work), work / "out")
self.assertEqual(returncode, 0, stderr)
self.assertTrue(pin_holds(returncode, cpp), stderr)
self.assertEqual(cpp.count("ProviderExportSignature const"), 2)
def test_provider_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
path = wasm_path(bundle)
data = bytearray(path.read_bytes())
data[-1] ^= 0xFF
path.write_bytes(data)
self.assert_generator_red(mutate, "WASM does not match")
def test_provider_size_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "PROVIDER_SIZE", "1116637")
self.assert_generator_red(mutate, "provider size disagrees")
def test_coordinated_provider_size_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = 1116637
data = load_json(bundle)
data["provider"]["size"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "PROVIDER_SIZE", str(value))
self.assert_generator_red(mutate, "provider size disagrees with the sealed F0")
def test_provider_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(
bundle,
"PROVIDER_SHA256",
"00" + PIN["provider_sha256"][2:],
)
self.assert_generator_red(mutate, "provider sha256 disagrees")
def test_coordinated_provider_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = "00" + PIN["provider_sha256"][2:]
data = load_json(bundle)
data["provider"]["sha256"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "PROVIDER_SHA256", value)
self.assert_generator_red(mutate, "provider SHA-256 disagrees with the sealed F0")
def test_manifest_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(
bundle,
"MANIFEST_SHA256",
"00" + PIN["manifest_sha256"][2:],
)
self.assert_generator_red(mutate, "JSON manifest does not match")
def test_coordinated_manifest_identity_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["diagnostic_note"] = "not F0"
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "provider manifest SHA-256")
def test_runtime_profile_id_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(
bundle,
"RUNTIME_PROFILE_ID",
"00" + PIN["runtime_profile_id"][2:],
)
self.assert_generator_red(mutate, "runtime-profile ID disagrees")
def test_coordinated_runtime_profile_id_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = "00" + PIN["runtime_profile_id"][2:]
data = load_json(bundle)
data["runtime_profile_id"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "RUNTIME_PROFILE_ID", value)
self.assert_generator_red(mutate, "runtime-profile ID disagrees with the sealed F0")
def _coordinated_api_artifact_mutation(
self, source_path: str, local_path, fragment: str
) -> None:
def mutate(bundle: Path) -> None:
path = local_path(bundle)
path.write_bytes(path.read_bytes() + b"\n")
manifest = load_api_manifest(bundle)
manifest["artifacts"][source_path] = hashlib.sha256(
path.read_bytes()
).hexdigest()
write_api_manifest(bundle, manifest)
self.assert_generator_red(mutate, fragment)
def test_broad_declaration_identity_mutation(self) -> None:
self._coordinated_api_artifact_mutation(
"python/jshookz/src/jshookz/types/hooks-api.d.ts",
broad_declaration_path,
"hooks-api.d.ts disagrees with the sealed F0 table",
)
def test_exact_v1_declaration_identity_mutation(self) -> None:
self._coordinated_api_artifact_mutation(
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
exact_v1_declaration_path,
"xahau-quickjs-v1.d.ts disagrees with the sealed F0 table",
)
def test_selected_surface_identity_mutation(self) -> None:
self._coordinated_api_artifact_mutation(
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json",
surface_path,
"xahau-quickjs-v1.surface.json disagrees with the sealed F0 table",
)
def test_api_artifact_manifest_identity_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_api_manifest(bundle)
api_manifest_path(bundle).write_text(json.dumps(data, indent=4) + "\n")
self.assert_generator_red(mutate, "API artifact manifest SHA-256")
def test_declaration_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["javascript_surface"]["declaration_sha256"] = (
"00" + PIN["exact_v1_declaration_sha256"][2:]
)
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "declaration SHA-256 disagrees")
def test_surface_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["javascript_surface"]["sha256"] = "00" + PIN["surface_sha256"][2:]
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "surface SHA-256 disagrees")
def test_bytecode_abi_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "BYTECODE_ABI_ID", "00" + PIN["bytecode_abi"][2:])
self.assert_generator_red(mutate, "bytecode ABI disagrees")
def test_coordinated_bytecode_abi_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = "00" + PIN["bytecode_abi"][2:]
data = load_json(bundle)
data["bytecode_abi_id"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "BYTECODE_ABI_ID", value)
self.assert_generator_red(mutate, "bytecode ABI disagrees with the sealed F0")
def test_native_abi_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "NATIVE_ABI_SHA256", "00" + PIN["native_abi"][2:])
self.assert_generator_red(mutate, "native ABI snapshot does not match")
def test_coordinated_native_abi_identity_mutation(self) -> None:
def mutate(bundle: Path) -> None:
path = native_path(bundle)
path.write_bytes(path.read_bytes() + b"\n")
rehash_native(bundle)
self.assert_generator_red(mutate, "native ABI SHA-256")
def test_import_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["imports"][0]["params"].append("i32")
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "import signatures disagree")
def test_export_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for item in data["provider"]["exports"]:
if item["name"] == "qjs_hook":
item["params"].append("i32")
break
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "export signatures disagree")
def test_coordinated_export_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["name"] == "qjs_hook":
item["params"].append("i32")
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "sealed F0 table")
def test_coordinated_wasi_import_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["imports"],
data["source"]["provider"]["imports"],
):
for item in collection:
if item["name"] == "accept":
item["module"] = "wasi_snapshot_preview1"
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "sealed F0 table")
def test_coordinated_import_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["imports"],
data["source"]["provider"]["imports"],
):
for item in collection:
if item["name"] == "accept":
item["params"].append("i32")
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "sealed F0 table")
def test_export_extra_key_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["name"] == "qjs_init":
item["note"] = "extra"
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "extra or missing keys")
def _memory_shape_mutation(self, key: str, value) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["kind"] == "memory":
item[key] = value
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "memory")
def test_memory_minimum_shape_mutation(self) -> None:
self._memory_shape_mutation("minimum_pages", 6)
def test_memory_maximum_shape_mutation(self) -> None:
self._memory_shape_mutation("maximum_pages", 511)
def test_memory64_shape_mutation(self) -> None:
self._memory_shape_mutation("memory64", True)
def test_shared_memory_shape_mutation(self) -> None:
self._memory_shape_mutation("shared", True)
def test_serialized_object_max_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_BYTES", "1048577")
self.assert_generator_red(mutate, "serialized_object_max_bytes")
def test_serialized_object_max_fields_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_FIELDS", "32769")
self.assert_generator_red(mutate, "serialized_object_max_fields")
def test_serialized_object_max_scopes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_SCOPES", "32770")
self.assert_generator_red(mutate, "serialized_object_max_scopes")
def test_serialized_object_max_depth_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_DEPTH", "11")
self.assert_generator_red(mutate, "serialized_object_max_depth")
def _coordinated_limit(self, json_key: str, cmake_key: str, value: int) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["limits"][json_key] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, cmake_key, str(value))
self.assert_generator_red(mutate, "sealed F0 table")
def test_coordinated_serialized_object_max_bytes_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_bytes", "SERIALIZED_OBJECT_MAX_BYTES", 1048578
)
def test_coordinated_serialized_object_max_fields_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_fields", "SERIALIZED_OBJECT_MAX_FIELDS", 1
)
def test_coordinated_serialized_object_max_scopes_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_scopes", "SERIALIZED_OBJECT_MAX_SCOPES", 1
)
def test_coordinated_serialized_object_max_depth_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_depth", "SERIALIZED_OBJECT_MAX_DEPTH", 1
)
def test_coordinated_heap_limit_mutation(self) -> None:
self._coordinated_limit("quickjs_heap_bytes", "HEAP_BYTES", 16777215)
def test_coordinated_stack_limit_mutation(self) -> None:
self._coordinated_limit("quickjs_stack_bytes", "STACK_BYTES", 65535)
def test_coordinated_initialization_fuel_mutation(self) -> None:
self._coordinated_limit(
"wasmtime_fuel_per_initialization", "INITIALIZATION_FUEL", 4999999
)
def test_coordinated_invocation_fuel_mutation(self) -> None:
self._coordinated_limit(
"wasmtime_fuel_per_invocation", "INVOCATION_FUEL", 49999999
)
def test_coordinated_host_work_budget_mutation(self) -> None:
self._coordinated_limit("host_work_budget", "HOST_WORK_BUDGET", 999999)
def test_coordinated_host_work_base_mutation(self) -> None:
self._coordinated_limit(
"host_work_base_per_call", "HOST_WORK_BASE_PER_CALL", 2
)
def test_coordinated_host_work_per_byte_mutation(self) -> None:
self._coordinated_limit(
"host_work_per_addressed_byte", "HOST_WORK_PER_ADDRESSED_BYTE", 2
)
def test_coordinated_host_work_meter_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["limits"]["host_work_meter"] = "other-meter"
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "HOST_WORK_METER", "other-meter")
self.assert_generator_red(mutate, "host_work_meter")
def test_host_work_address_mapping_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["limits"]["host_work_addressed_length_indices"][
"accept"
] = [0]
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "host_work_addressed_length_indices")
def test_coordinated_host_adapter_policy_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["execution"]["host_adapter_policy"] = "other-policy"
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "HOST_ADAPTER_POLICY", "other-policy")
self.assert_generator_red(mutate, "host_adapter_policy")
def test_coordinated_hook_api_version_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["artifact"]["hook_api_version"] = 2
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "HOOK_API_VERSION", "2")
self.assert_generator_red(mutate, "hook_api_version")
def test_wasmtime_engine_configuration_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["engine"]["configuration"]["wasi"] = True
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "Wasmtime engine configuration")
def test_coordinated_wasmtime_version_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["engine"]["version"] = "48.0.0"
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "WASMTIME_VERSION", "48.0.0")
self.assert_generator_red(
mutate, "Wasmtime version disagrees with the sealed F0", "48.0.0"
)
def test_provider_memory_max_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["build"]["wasm_memory_max_bytes"] = 33554431
data["source"]["provider"]["build"][
"wasm_memory_max_bytes"
] = 33554431
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "wasm_memory_max_bytes")
def test_wasm_stack_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["build"]["wasm_stack_bytes"] = 65536
data["source"]["provider"]["build"]["wasm_stack_bytes"] = 65536
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "wasm_stack_bytes")
def test_stale_receipt_b_lock_control(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["sha256"] = RECEIPT_B["provider_sha256"]
data["provider"]["size"] = int(RECEIPT_B["provider_size"])
data["runtime_profile_id"] = RECEIPT_B["runtime_profile_id"]
data["javascript_surface"]["declaration_sha256"] = RECEIPT_B[
"declaration_sha256"
]
data["javascript_surface"]["sha256"] = RECEIPT_B["surface_sha256"]
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["kind"] == "memory":
item["minimum_pages"] = 6
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "PROVIDER_SHA256", RECEIPT_B["provider_sha256"])
set_cmake(bundle, "PROVIDER_SIZE", RECEIPT_B["provider_size"])
set_cmake(
bundle, "RUNTIME_PROFILE_ID", RECEIPT_B["runtime_profile_id"]
)
self.assert_generator_red(
mutate, "provider SHA-256 disagrees with the sealed F0"
)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,41 +0,0 @@
include(isolate_headers)
function(xrpl_add_test name)
set(target ${PROJECT_NAME}.test.${name})
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
)
add_executable(${target} EXCLUDE_FROM_ALL ${ARGN} ${sources})
isolate_headers(
${target}
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/tests/${name}"
PRIVATE
)
# Make sure the test isn't optimized away in unity builds
set_target_properties(${target} PROPERTIES
UNITY_BUILD_MODE GROUP
UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
add_test(NAME ${target} COMMAND ${target})
set_tests_properties(
${target} PROPERTIES
FIXTURES_REQUIRED ${target}_fixture
)
add_test(
NAME ${target}.build
COMMAND
${CMAKE_COMMAND}
--build ${CMAKE_BINARY_DIR}
--config $<CONFIG>
--target ${target}
)
set_tests_properties(${target}.build PROPERTIES
FIXTURES_SETUP ${target}_fixture
)
endfunction()

View File

@@ -1,37 +0,0 @@
{% set os = detect_api.detect_os() %}
{% set arch = detect_api.detect_arch() %}
{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %}
{% set compiler_version = version %}
{% if os == "Linux" %}
{% set compiler_version = detect_api.default_compiler_version(compiler, version) %}
{% endif %}
[settings]
os={{ os }}
arch={{ arch }}
build_type=Debug
compiler={{compiler}}
compiler.version={{ compiler_version }}
compiler.cppstd=20
{% if os == "Windows" %}
compiler.runtime=static
{% else %}
compiler.libcxx={{detect_api.detect_libcxx(compiler, version, compiler_exe)}}
{% endif %}
[conf]
{% if compiler == "clang" and compiler_version >= 19 %}
grpc/1.50.1:tools.build:cxxflags+=['-Wno-missing-template-arg-list-after-template-kw']
{% endif %}
{% if compiler == "apple-clang" and compiler_version >= 17 %}
grpc/1.50.1:tools.build:cxxflags+=['-Wno-missing-template-arg-list-after-template-kw']
{% endif %}
{% if compiler == "clang" and compiler_version == 16 %}
tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS']
{% endif %}
{% if compiler == "gcc" and compiler_version < 13 %}
tools.build:cxxflags+=['-Wno-restrict']
{% endif %}
[tool_requires]
!cmake/*: cmake/[>=3 <4]

View File

@@ -1,165 +1,163 @@
from conan import ConanFile, __version__ as conan_version
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
import re
class Xrpl(ConanFile):
name = 'xrpl'
license = 'ISC'
author = 'John Freeman <jfreeman@ripple.com>'
url = 'https://github.com/xrplf/rippled'
description = 'The XRP Ledger'
settings = 'os', 'compiler', 'build_type', 'arch'
class Xrpl(ConanFile):
name = "xrpl"
license = "ISC"
author = "John Freeman <jfreeman@ripple.com>"
url = "https://github.com/xrplf/rippled"
description = "The XRP Ledger"
settings = "os", "compiler", "build_type", "arch"
options = {
'assertions': [True, False],
'coverage': [True, False],
'fPIC': [True, False],
'jemalloc': [True, False],
'rocksdb': [True, False],
'shared': [True, False],
'static': [True, False],
'tests': [True, False],
'unity': [True, False],
'xrpld': [True, False],
'with_wasmedge': [True, False],
'tool_requires_b2': [True, False],
"assertions": [True, False],
"coverage": [True, False],
"fPIC": [True, False],
"jemalloc": [True, False],
"rocksdb": [True, False],
"shared": [True, False],
"static": [True, False],
"tests": [True, False],
"unity": [True, False],
"xrpld": [True, False],
"with_wasmedge": [True, False],
"tool_requires_b2": [True, False],
}
requires = [
'grpc/1.50.1',
'libarchive/3.8.1',
'magic_enum/0.9.5',
'nudb/2.0.9',
'openssl/3.6.0',
'soci/4.0.3@xahaud/stable',
'zlib/1.3.1',
]
test_requires = [
'doctest/2.4.11',
"date/3.0.3",
"grpc/1.50.1",
"libarchive/3.7.6",
"magic_enum/0.9.5",
"nudb/2.0.8",
"openssl/3.6.0",
"soci/4.0.3@xahaud/stable",
"xxhash/0.8.2",
"zlib/1.3.1",
]
tool_requires = [
'protobuf/3.21.12',
"protobuf/3.21.12",
]
default_options = {
'assertions': False,
'coverage': False,
'fPIC': True,
'jemalloc': False,
'rocksdb': True,
'shared': False,
'static': True,
'tests': False,
'unity': False,
'xrpld': False,
'with_wasmedge': True,
'tool_requires_b2': False,
'date/*:header_only': False,
'grpc/*:shared': False,
'grpc/*:secure': True,
'libarchive/*:shared': False,
'libarchive/*:with_acl': False,
'libarchive/*:with_bzip2': False,
'libarchive/*:with_cng': False,
'libarchive/*:with_expat': False,
'libarchive/*:with_iconv': False,
'libarchive/*:with_libxml2': False,
'libarchive/*:with_lz4': True,
'libarchive/*:with_lzma': False,
'libarchive/*:with_lzo': False,
'libarchive/*:with_nettle': False,
'libarchive/*:with_openssl': False,
'libarchive/*:with_pcreposix': False,
'libarchive/*:with_xattr': False,
'libarchive/*:with_zlib': False,
'lz4/*:shared': False,
'openssl/*:shared': False,
'protobuf/*:shared': False,
'protobuf/*:with_zlib': True,
'rocksdb/*:enable_sse': False,
'rocksdb/*:lite': False,
'rocksdb/*:shared': False,
'rocksdb/*:use_rtti': True,
'rocksdb/*:with_jemalloc': False,
'rocksdb/*:with_lz4': True,
'rocksdb/*:with_snappy': True,
'snappy/*:shared': False,
'soci/*:shared': False,
'soci/*:with_sqlite3': True,
'soci/*:with_boost': True,
'xxhash/*:shared': False,
"assertions": False,
"coverage": False,
"fPIC": True,
"jemalloc": False,
"rocksdb": True,
"shared": False,
"static": True,
"tests": False,
"unity": False,
"xrpld": False,
"with_wasmedge": True,
"tool_requires_b2": False,
"date/*:header_only": False,
"grpc/*:shared": False,
"grpc/*:secure": True,
"libarchive/*:shared": False,
"libarchive/*:with_acl": False,
"libarchive/*:with_bzip2": False,
"libarchive/*:with_cng": False,
"libarchive/*:with_expat": False,
"libarchive/*:with_iconv": False,
"libarchive/*:with_libxml2": False,
"libarchive/*:with_lz4": True,
"libarchive/*:with_lzma": False,
"libarchive/*:with_lzo": False,
"libarchive/*:with_nettle": False,
"libarchive/*:with_openssl": False,
"libarchive/*:with_pcreposix": False,
"libarchive/*:with_xattr": False,
"libarchive/*:with_zlib": False,
"lz4/*:shared": False,
"openssl/*:shared": False,
"protobuf/*:shared": False,
"protobuf/*:with_zlib": True,
"rocksdb/*:enable_sse": False,
"rocksdb/*:lite": False,
"rocksdb/*:shared": False,
"rocksdb/*:use_rtti": True,
"rocksdb/*:with_jemalloc": False,
"rocksdb/*:with_lz4": True,
"rocksdb/*:with_snappy": True,
"snappy/*:shared": False,
"soci/*:shared": False,
"soci/*:with_sqlite3": True,
"soci/*:with_boost": True,
"wasmtime/*:shared": False,
"xxhash/*:shared": False,
}
def set_version(self):
if self.version is None:
path = f'{self.recipe_folder}/src/libxrpl/protocol/BuildInfo.cpp'
regex = r'versionString\s?=\s?\"(.*)\"'
with open(path, encoding='utf-8') as file:
matches = (re.search(regex, line) for line in file)
match = next(m for m in matches if m)
self.version = match.group(1)
path = f"{self.recipe_folder}/src/libxrpl/protocol/BuildInfo.cpp"
regex = r"versionString\s?=\s?\"(.*)\""
with open(path, "r") as file:
matches = (re.search(regex, line) for line in file)
match = next(m for m in matches if m)
self.version = match.group(1)
def build_requirements(self):
# These provide build tools (protoc, grpc plugins) that run during build
self.tool_requires('grpc/1.50.1')
self.tool_requires("grpc/1.50.1")
# Explicitly require b2 (e.g. for building from source for glibc compatibility)
if self.options.tool_requires_b2:
self.tool_requires('b2/5.3.2')
self.tool_requires("b2/5.3.2")
def configure(self):
if self.settings.compiler == 'apple-clang':
self.options['boost/*'].visibility = 'global'
if self.settings.compiler == "apple-clang":
self.options["boost/*"].visibility = "global"
def requirements(self):
# Conan 2 requires transitive headers to be specified
transitive_headers_opt = {'transitive_headers': True} if conan_version.split('.')[0] == '2' else {}
# Force sqlite3 version to avoid conflicts with soci
self.requires('sqlite3/3.49.1', override=True)
# Force our custom snappy build to avoid Conan CMakeDeps stdc++ heuristic bug
self.requires('snappy/1.1.10@xahaud/stable', override=True)
self.requires("sqlite3/3.47.0", override=True)
# Force our custom snappy build for all dependencies
self.requires("snappy/1.1.10@xahaud/stable", override=True)
# Force boost version for all dependencies to avoid conflicts
self.requires('boost/1.86.0', force=True, **transitive_headers_opt)
self.requires('date/3.0.4', **transitive_headers_opt)
self.requires('lz4/1.10.0', force=True)
self.requires("boost/1.86.0", override=True)
self.requires("lz4/1.10.0", force=True)
if self.options.with_wasmedge:
self.requires('wasmedge/0.11.2@xahaud/stable', **transitive_headers_opt)
self.requires("wasmedge/0.11.2@xahaud/stable")
if self.options.xrpld:
self.requires("wasmtime/47.0.3@xahaud/stable")
if self.options.jemalloc:
self.requires('jemalloc/5.3.0', **transitive_headers_opt)
self.requires("jemalloc/5.3.0")
if self.options.rocksdb:
self.requires('rocksdb/10.0.1')
self.requires('xxhash/0.8.3', **transitive_headers_opt)
self.requires("rocksdb/6.29.5")
exports_sources = (
'CMakeLists.txt',
'cfg/*',
'cmake/*',
'external/*',
'include/*',
'src/*',
"CMakeLists.txt",
"bin/getRippledInfo",
"cfg/*",
"cmake/*",
"external/*",
"include/*",
"src/*",
)
def layout(self):
cmake_layout(self)
# Fix this setting to follow the default introduced in Conan 1.48
# to align with our build instructions.
self.folders.generators = 'build/generators'
self.folders.generators = "build/generators"
generators = "CMakeDeps"
generators = 'CMakeDeps'
def generate(self):
tc = CMakeToolchain(self)
tc.variables['tests'] = self.options.tests
tc.variables['assert'] = self.options.assertions
tc.variables['coverage'] = self.options.coverage
tc.variables['jemalloc'] = self.options.jemalloc
tc.variables['rocksdb'] = self.options.rocksdb
tc.variables['BUILD_SHARED_LIBS'] = self.options.shared
tc.variables['static'] = self.options.static
tc.variables['unity'] = self.options.unity
tc.variables['xrpld'] = self.options.xrpld
tc.variables["tests"] = self.options.tests
tc.variables["assert"] = self.options.assertions
tc.variables["coverage"] = self.options.coverage
tc.variables["jemalloc"] = self.options.jemalloc
tc.variables["rocksdb"] = self.options.rocksdb
tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
tc.variables["static"] = self.options.static
tc.variables["unity"] = self.options.unity
tc.variables["xrpld"] = self.options.xrpld
tc.generate()
def build(self):
@@ -174,39 +172,29 @@ class Xrpl(ConanFile):
cmake.install()
def package_info(self):
libxrpl = self.cpp_info.components['libxrpl']
libxrpl = self.cpp_info.components["libxrpl"]
libxrpl.libs = [
'xrpl',
'xrpl.libpb',
'ed25519',
'secp256k1',
"xrpl",
"xrpl.libpb",
"ed25519",
"secp256k1",
]
# TODO: Fix the protobufs to include each other relative to
# `include/`, not `include/ripple/proto/`.
libxrpl.includedirs = ['include', 'include/ripple/proto']
libxrpl.includedirs = ["include", "include/ripple/proto"]
libxrpl.requires = [
'boost::headers',
'boost::chrono',
'boost::container',
'boost::coroutine',
'boost::date_time',
'boost::filesystem',
'boost::json',
'boost::program_options',
'boost::regex',
'boost::system',
'boost::thread',
'date::date',
'grpc::grpc++',
'libarchive::libarchive',
'lz4::lz4',
'nudb::nudb',
'openssl::crypto',
'protobuf::libprotobuf',
'soci::soci',
'sqlite3::sqlite',
'xxhash::xxhash',
'zlib::zlib',
"boost::boost",
"date::date",
"grpc::grpc++",
"libarchive::libarchive",
"lz4::lz4",
"nudb::nudb",
"openssl::crypto",
"protobuf::libprotobuf",
"soci::soci",
"sqlite3::sqlite",
"xxhash::xxhash",
"zlib::zlib",
]
if self.options.rocksdb:
libxrpl.requires.append('rocksdb::librocksdb')
libxrpl.requires.append("rocksdb::librocksdb")

View File

@@ -5,6 +5,7 @@ platforms: Linux, macOS, or Windows.
[BUILD.md]: ../../BUILD.md
## Linux
Package ecosystems vary across Linux distributions,
@@ -22,7 +23,7 @@ direction.
```
apt update
apt install --yes curl git libssl-dev pipx python3.10-dev python3-pip make g++-11 libprotobuf-dev protobuf-compiler
apt install --yes curl git libssl-dev python3.10-dev python3-pip make g++-11 libprotobuf-dev protobuf-compiler
curl --location --remote-name \
"https://github.com/Kitware/CMake/releases/download/v3.25.1/cmake-3.25.1.tar.gz"
@@ -34,8 +35,7 @@ make --jobs $(nproc)
make install
cd ..
pipx install 'conan<2'
pipx ensurepath
pip3 install 'conan<2'
```
[1]: https://github.com/thejohnfreeman/rippled-docker/blob/master/ubuntu-22.04/install.sh

View File

@@ -9,7 +9,7 @@ project(
LANGUAGES CXX
)
find_package(xrpl CONFIG REQUIRED)
find_package(xrpl REQUIRED)
add_executable(example)
target_sources(example PRIVATE src/example.cpp)

View File

@@ -0,0 +1,59 @@
from conan import ConanFile, conan_version
from conan.tools.cmake import CMake, cmake_layout
class Example(ConanFile):
def set_name(self):
if self.name is None:
self.name = 'example'
def set_version(self):
if self.version is None:
self.version = '0.1.0'
license = 'ISC'
author = 'John Freeman <jfreeman08@gmail.com>'
settings = 'os', 'compiler', 'build_type', 'arch'
options = {'shared': [True, False], 'fPIC': [True, False]}
default_options = {
'shared': False,
'fPIC': True,
'xrpl:xrpld': False,
}
requires = ['xrpl/2.2.0-rc1@jfreeman/nodestore']
generators = ['CMakeDeps', 'CMakeToolchain']
exports_sources = [
'CMakeLists.txt',
'cmake/*',
'external/*',
'include/*',
'src/*',
]
# For out-of-source build.
# https://docs.conan.io/en/latest/reference/build_helpers/cmake.html#configure
no_copy_source = True
def layout(self):
cmake_layout(self)
def config_options(self):
if self.settings.os == 'Windows':
del self.options.fPIC
def build(self):
cmake = CMake(self)
cmake.configure(variables={'BUILD_TESTING': 'NO'})
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
path = f'{self.package_folder}/share/{self.name}/cpp_info.py'
with open(path, 'r') as file:
exec(file.read(), {}, {'self': self.cpp_info})

View File

@@ -1,10 +1,8 @@
#include <xrpl/protocol/BuildInfo.h>
#include <cstdio>
int
main(int argc, char const** argv)
{
#include <xrpl/protocol/BuildInfo.h>
int main(int argc, char const** argv) {
std::printf("%s\n", ripple::BuildInfo::getVersionString().c_str());
return 0;
}

13
external/README.md vendored
View File

@@ -2,8 +2,11 @@ The subdirectories in this directory contain either copies or Conan recipes
of external libraries used by rippled.
The Conan recipes include patches we have not yet pushed upstream.
| Folder | Upstream | Description |
| :--------------- | :------------------------------------------------------------- | :------------------------------------------------------------------------------------------- |
| `antithesis-sdk` | [Project](https://github.com/antithesishq/antithesis-sdk-cpp/) | [Antithesis](https://antithesis.com/docs/using_antithesis/sdk/cpp/overview.html) SDK for C++ |
| `ed25519-donna` | [Project](https://github.com/floodyberry/ed25519-donna) | [Ed25519](http://ed25519.cr.yp.to/) digital signatures |
| `secp256k1` | [Project](https://github.com/bitcoin-core/secp256k1) | ECDSA digital signatures using the **secp256k1** curve |
| Folder | Upstream | Description |
|:----------------|:---------------------------------------------|:------------|
| `antithesis-sdk`| [Project](https://github.com/antithesishq/antithesis-sdk-cpp/) | [Antithesis](https://antithesis.com/docs/using_antithesis/sdk/cpp/overview.html) SDK for C++ |
| `ed25519-donna` | [Project](https://github.com/floodyberry/ed25519-donna) | [Ed25519](http://ed25519.cr.yp.to/) digital signatures |
| `rocksdb` | [Recipe](https://github.com/conan-io/conan-center-index/tree/master/recipes/rocksdb) | Fast key/value database. (Supports rotational disks better than NuDB.) |
| `secp256k1` | [Project](https://github.com/bitcoin-core/secp256k1) | ECDSA digital signatures using the **secp256k1** curve |
| `snappy` | [Recipe](https://github.com/conan-io/conan-center-index/tree/master/recipes/snappy) | "Snappy" lossless compression algorithm. |
| `soci` | [Recipe](https://github.com/conan-io/conan-center-index/tree/master/recipes/soci) | Abstraction layer for database access. |

View File

@@ -1,3 +1,3 @@
---
DisableFormat: true
SortIncludes: Never
SortIncludes: false

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.18)
cmake_minimum_required(VERSION 3.25)
# Note, version set explicitly by rippled project
project(antithesis-sdk-cpp VERSION 0.4.4 LANGUAGES CXX)

View File

@@ -1,9 +1,8 @@
# Antithesis C++ SDK
This library provides methods for C++ programs to configure the [Antithesis](https://antithesis.com) platform. It contains three kinds of functionality:
- Assertion macros that allow you to define test properties about your software or workload.
- Randomness functions for requesting both structured and unstructured randomness from the Antithesis platform.
- Lifecycle functions that inform the Antithesis environment that particular test phases or milestones have been reached.
* Assertion macros that allow you to define test properties about your software or workload.
* Randomness functions for requesting both structured and unstructured randomness from the Antithesis platform.
* Lifecycle functions that inform the Antithesis environment that particular test phases or milestones have been reached.
For general usage guidance see the [Antithesis C++ SDK Documentation](https://antithesis.com/docs/using_antithesis/sdk/cpp/overview/)

View File

@@ -17,9 +17,6 @@ add_library(ed25519 STATIC
)
add_library(ed25519::ed25519 ALIAS ed25519)
target_link_libraries(ed25519 PUBLIC OpenSSL::SSL)
if(NOT MSVC)
target_compile_options(ed25519 PRIVATE -Wno-implicit-fallthrough)
endif()
include(GNUInstallDirs)

10
external/nudb/conandata.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
sources:
"2.0.8":
url: "https://github.com/CPPAlliance/NuDB/archive/2.0.8.tar.gz"
sha256: "9b71903d8ba111cd893ab064b9a8b6ac4124ed8bd6b4f67250205bc43c7f13a8"
patches:
"2.0.8":
- patch_file: "patches/2.0.8-0001-add-include-stdexcept-for-msvc.patch"
patch_description: "Fix build for MSVC by including stdexcept"
patch_type: "portability"
patch_source: "https://github.com/cppalliance/NuDB/pull/100/files"

72
external/nudb/conanfile.py vendored Normal file
View File

@@ -0,0 +1,72 @@
import os
from conan import ConanFile
from conan.tools.build import check_min_cppstd
from conan.tools.files import apply_conandata_patches, copy, export_conandata_patches, get
from conan.tools.layout import basic_layout
required_conan_version = ">=1.52.0"
class NudbConan(ConanFile):
name = "nudb"
description = "A fast key/value insert-only database for SSD drives in C++11"
license = "BSL-1.0"
url = "https://github.com/conan-io/conan-center-index"
homepage = "https://github.com/CPPAlliance/NuDB"
topics = ("header-only", "KVS", "insert-only")
package_type = "header-library"
settings = "os", "arch", "compiler", "build_type"
no_copy_source = True
@property
def _min_cppstd(self):
return 11
def export_sources(self):
export_conandata_patches(self)
def layout(self):
basic_layout(self, src_folder="src")
def requirements(self):
self.requires("boost/1.83.0")
def package_id(self):
self.info.clear()
def validate(self):
if self.settings.compiler.cppstd:
check_min_cppstd(self, self._min_cppstd)
def source(self):
get(self, **self.conan_data["sources"][self.version], strip_root=True)
def build(self):
apply_conandata_patches(self)
def package(self):
copy(self, "LICENSE*",
dst=os.path.join(self.package_folder, "licenses"),
src=self.source_folder)
copy(self, "*",
dst=os.path.join(self.package_folder, "include"),
src=os.path.join(self.source_folder, "include"))
def package_info(self):
self.cpp_info.bindirs = []
self.cpp_info.libdirs = []
self.cpp_info.set_property("cmake_target_name", "NuDB")
self.cpp_info.set_property("cmake_target_aliases", ["NuDB::nudb"])
self.cpp_info.set_property("cmake_find_mode", "both")
self.cpp_info.components["core"].set_property("cmake_target_name", "nudb")
self.cpp_info.components["core"].names["cmake_find_package"] = "nudb"
self.cpp_info.components["core"].names["cmake_find_package_multi"] = "nudb"
self.cpp_info.components["core"].requires = ["boost::thread", "boost::system"]
# TODO: to remove in conan v2 once cmake_find_package_* generators removed
self.cpp_info.names["cmake_find_package"] = "NuDB"
self.cpp_info.names["cmake_find_package_multi"] = "NuDB"

View File

@@ -0,0 +1,24 @@
diff --git a/include/nudb/detail/stream.hpp b/include/nudb/detail/stream.hpp
index 6c07bf1..e0ce8ed 100644
--- a/include/nudb/detail/stream.hpp
+++ b/include/nudb/detail/stream.hpp
@@ -14,6 +14,7 @@
#include <cstdint>
#include <cstring>
#include <memory>
+#include <stdexcept>
namespace nudb {
namespace detail {
diff --git a/include/nudb/impl/context.ipp b/include/nudb/impl/context.ipp
index beb7058..ffde0b3 100644
--- a/include/nudb/impl/context.ipp
+++ b/include/nudb/impl/context.ipp
@@ -9,6 +9,7 @@
#define NUDB_IMPL_CONTEXT_IPP
#include <nudb/detail/store_base.hpp>
+#include <stdexcept>
namespace nudb {

48
external/quickjs-provider/README.md vendored Normal file
View File

@@ -0,0 +1,48 @@
# QuickJS provider manifest
These files are generated or sealed together by a jshookz source checkout:
```sh
# Current layout (cpp/provider, python/jshookz). Older checkouts used
# packages/jshookz; bin/update-jshookz-snapshot accepts either CLI path.
uv sync --project python/jshookz --locked --group dev
python/jshookz/.venv/bin/jshookz build provider
```
Update this Xahau branch from a clean, committed jshookz checkout with one
command:
```sh
bin/update-jshookz-snapshot /path/to/jshookz
```
That builds and imports the sealed lock files plus the broad/exact-v1
declarations, selected-surface manifest, and API-artifact manifest. CI checks out jshookz
`main` and fails if `build provider` does not reproduce that lock. The
importer then prints a self-contained `x-run-tests` command that
regenerates the embedded Hook fixtures and verifies the cut.
`jshookz_provider.manifest.json` is the verified runtime-profile lock.
`jshookz_provider.manifest.cmake` is its minimal CMake projection. Xahau checks
the JSON hash before generating the C++ profile constants, and the runtime
checks any supplied provider WASM against the projected size and SHA-256.
`api-artifacts.json` closes the three tracked API artifacts; Xahau verifies its
own digest and every named artifact before projecting their identities. These
files add no parallel CMake lock-value fields.
The provider binary is not vendored in this integration slice. Tests inject
the exact built artifact. Configure needs python3 to project these lock files.
A daemon whose embed is empty will not start.
For a local producer-to-consumer build, point Xahau straight at the emitted
bundle instead of copying it:
```sh
cmake -S . -B build \
-DXAHAU_QUICKJS_PROVIDER_BUNDLE_DIR=/path/to/jshookz/build/xahau-provider
```
A build-time generator writes `QuickJSProviderValues.cpp` from those
locks and, if present, the gitignored wasm. Configure only checks that
the three lock files exist. Changing the bundle rebuilds that one
translation unit; hash, Wasmtime, and optional-wasm checks run then.

View File

@@ -0,0 +1,8 @@
{
"artifacts": {
"python/jshookz/src/jshookz/types/hooks-api.d.ts": "649079b831bc68eff0f440644649a505addafb08a3f5174e4031c8de6aedffa0",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json": "96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac"
},
"schema": "jshookz.api-artifacts.v1"
}

3209
external/quickjs-provider/hooks-api.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
# Generated by jshookz build provider; do not edit.
set(XAHAU_QUICKJS_MANIFEST_SCHEMA "xahau.quickjs.runtime-profile-lock.v1")
set(XAHAU_QUICKJS_MANIFEST_SHA256 "1673b27d21063957cece9095cceb7e2dbfa53b6f49a5b7f824a486d7d90b2391")
set(XAHAU_QUICKJS_PROVIDER_FILE "jshookz_provider.wasm")
set(XAHAU_QUICKJS_PROVIDER_SHA256 "f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df")
set(XAHAU_QUICKJS_PROVIDER_SIZE "1116638")
set(XAHAU_QUICKJS_PROVIDER_IMPORT_COUNT "13")
set(XAHAU_QUICKJS_PROVIDER_EXPORT_COUNT "22")
set(XAHAU_QUICKJS_NATIVE_ABI_FILE "jshookz_provider.native-abi.json")
set(XAHAU_QUICKJS_NATIVE_ABI_SHA256 "328ec938dcad875f3bdf25b8d779dd77f3571589818ca9271cb98c64c7018f99")
set(XAHAU_QUICKJS_BYTECODE_ABI_ID "75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e")
set(XAHAU_QUICKJS_RUNTIME_PROFILE_ID "29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25")
set(XAHAU_QUICKJS_HOOK_API_VERSION "1")
set(XAHAU_QUICKJS_WASMTIME_VERSION "47.0.3")
set(XAHAU_QUICKJS_INITIALIZATION_FUEL "5000000")
set(XAHAU_QUICKJS_INVOCATION_FUEL "50000000")
set(XAHAU_QUICKJS_HOST_WORK_METER "base-plus-addressed-byte-v1")
set(XAHAU_QUICKJS_HOST_WORK_BUDGET "1000000")
set(XAHAU_QUICKJS_HOST_WORK_BASE_PER_CALL "1")
set(XAHAU_QUICKJS_HOST_WORK_PER_ADDRESSED_BYTE "1")
set(XAHAU_QUICKJS_HOST_ADAPTER_POLICY "xahau-raw-hook-host-v1")
set(XAHAU_QUICKJS_HEAP_BYTES "16777216")
set(XAHAU_QUICKJS_STACK_BYTES "65536")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_BYTES "1048576")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_FIELDS "32768")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_SCOPES "32769")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_DEPTH "10")

View File

@@ -0,0 +1,854 @@
{
"bytecode_abi_id": "75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e",
"javascript_surface": {
"declaration": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
"declaration_sha256": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da",
"manifest": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json",
"schema": "jshookz.javascript-surface.v1",
"sha256": "96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac"
},
"provider": {
"build": {
"wasm_memory_max_bytes": 33554432,
"wasm_stack_bytes": 131072
},
"exports": [
{
"kind": "function",
"name": "_initialize",
"params": [],
"results": []
},
{
"kind": "function",
"name": "free",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "malloc",
"params": [
"i32"
],
"results": [
"i32"
]
},
{
"kind": "memory",
"maximum_pages": 512,
"memory64": false,
"minimum_pages": 7,
"name": "memory",
"shared": false
},
{
"kind": "function",
"name": "qjs_cbak",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_destroy",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_enable_coverage",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_eval",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_bytecode",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_hook",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_init",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_set_max_stack_size",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_memory_limit",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_seed",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_validate_hook_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
}
],
"imports": [
{
"module": "env",
"name": "accept",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "emit",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "etxn_reserve",
"params": [
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "hook_account",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_hash",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_time",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_seq",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "otxn_type",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "prepare",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "rollback",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state_set",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "trace",
"params": [
"i32",
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
}
],
"sha256": "f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df",
"size": 1116638
},
"runtime_profile_id": "29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25",
"schema": "xahau.quickjs.runtime-profile-lock.v1",
"source": {
"artifact": {
"envelope_version": 1,
"hook_api_version": 1,
"kind": "quickjs-bytecode"
},
"bytecode_abi": {
"entry_exports": {
"callback": "optional-callable",
"main": "required-callable"
},
"quickjs_bytecode_version": 5,
"quickjs_version": "2025-09-13",
"read_flags": [
"JS_READ_OBJ_BYTECODE"
],
"root": "es-module",
"write_flags": [
"JS_WRITE_OBJ_BYTECODE"
]
},
"engine": {
"configuration": {
"consume_fuel": true,
"cranelift_nan_canonicalization": true,
"wasi": false,
"wasm_memory64": false,
"wasm_multi_memory": false,
"wasm_relaxed_simd": false,
"wasm_tail_call": false,
"wasm_threads": false
},
"implementation": "wasmtime-native-c-api",
"module_lifecycle": "compile-once-fresh-store-and-instance-per-invocation",
"version": "47.0.3"
},
"execution": {
"host_adapter_policy": "xahau-raw-hook-host-v1",
"host_work_exhaustion": "uncatchable-wasmtime-trap-to-wasm-error",
"initialization": "finite-cold-qjs-init-budget-before-invocation-fuel-reset",
"ordinary_host_status": "returned-as-i64-never-trapped",
"pending_jobs_after_entry": "not-drained",
"result_failure_policy": "rollback.onFail-preserves-coded-failure;rollback.requirePresent-requires-success-and-non-nullish-value;rollback.requireTruthy-requires-success-and-truthy-value;accept.unlessPresent-accepts-on-failure-or-nullish-value;accept.unlessTruthy-accepts-on-failure-or-falsy-value;guards-use-explicit-message-and-optional-code-default-zero",
"sfHookInstructionCount": "initial-wasmtime-invocation-fuel-minus-remaining-fuel",
"terminals": "record-hook-result-and-uncatchable-wasmtime-trap"
},
"javascript_surface": {
"declaration": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
"manifest": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json",
"schema": "jshookz.javascript-surface.v1"
},
"limits": {
"host_work_addressed_length_indices": {
"accept": [
1
],
"emit": [
1,
3
],
"hook_account": [
1
],
"ledger_last_hash": [
1
],
"prepare": [
1,
3
],
"rollback": [
1
],
"state": [
1,
3
],
"state_set": [
1,
3
],
"trace": [
1,
3
]
},
"host_work_base_per_call": 1,
"host_work_budget": 1000000,
"host_work_meter": "base-plus-addressed-byte-v1",
"host_work_per_addressed_byte": 1,
"quickjs_heap_bytes": 16777216,
"quickjs_stack_bytes": 65536,
"serialized_object_max_bytes": 1048576,
"serialized_object_max_depth": 10,
"serialized_object_max_fields": 32768,
"serialized_object_max_scopes": 32769,
"wasmtime_fuel_per_initialization": 5000000,
"wasmtime_fuel_per_invocation": 50000000
},
"name": "xahau-quickjs-v1",
"provider": {
"allowed_exports": [
{
"kind": "function",
"name": "_initialize",
"params": [],
"results": []
},
{
"kind": "function",
"name": "free",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "malloc",
"params": [
"i32"
],
"results": [
"i32"
]
},
{
"kind": "memory",
"maximum_pages": 512,
"memory64": false,
"minimum_pages": 7,
"name": "memory",
"shared": false
},
{
"kind": "function",
"name": "qjs_cbak",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_destroy",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_enable_coverage",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_eval",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_bytecode",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_hook",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_init",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_set_max_stack_size",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_memory_limit",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_seed",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_validate_hook_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
}
],
"build": {
"c_flags": [
"-fwrapv",
"-funsigned-char",
"-mllvm",
"-wasm-enable-sjlj"
],
"cxx_flags": [
"-fno-exceptions",
"-fno-rtti",
"-mllvm",
"-wasm-enable-sjlj"
],
"optimization": "O2",
"target": "wasm32-wasi",
"wasi_sdk": "32",
"wasm_memory_max_bytes": 33554432,
"wasm_stack_bytes": 131072
},
"forbidden_import_modules": [
"wasi_snapshot_preview1"
],
"imports": [
{
"module": "env",
"name": "accept",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "emit",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "etxn_reserve",
"params": [
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "hook_account",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_hash",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_time",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_seq",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "otxn_type",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "prepare",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "rollback",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state_set",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "trace",
"params": [
"i32",
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
}
],
"javascript": {
"atomics": false,
"date_now": "previous-ledger-close-ripple-epoch-to-unix-milliseconds",
"dynamic_eval": "enabled-and-metered",
"finalization_registry": false,
"math_random": "absent-use-ledger-random-api",
"number": "enabled",
"promise": "required-for-module-evaluation-no-post-entry-job-drain",
"shared_array_buffer": false,
"timezone": "UTC",
"weak_ref": false
}
},
"schema": "xahau.quickjs.runtime-profile-source.v1"
}
}

View File

@@ -0,0 +1 @@
{"selected":[{"amendment":null,"name":"accept","param_types":["uint32_t","uint32_t","int64_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7e"]},{"amendment":null,"name":"rollback","param_types":["uint32_t","uint32_t","int64_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7e"]},{"amendment":null,"name":"ledger_seq","param_types":[],"return_type":"int64_t","wasm_signature":["0x7e"]},{"amendment":null,"name":"ledger_last_time","param_types":[],"return_type":"int64_t","wasm_signature":["0x7e"]},{"amendment":null,"name":"ledger_last_hash","param_types":["uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f"]},{"amendment":null,"name":"otxn_type","param_types":[],"return_type":"int64_t","wasm_signature":["0x7e"]},{"amendment":null,"name":"hook_account","param_types":["uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f"]},{"amendment":null,"name":"trace","param_types":["uint32_t","uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f","0x7f"]},{"amendment":null,"name":"state","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]},{"amendment":null,"name":"state_set","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]},{"amendment":"featureHooksUpdate2","name":"prepare","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]},{"amendment":null,"name":"etxn_reserve","param_types":["uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f"]},{"amendment":null,"name":"emit","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]}],"source":{"commit":"bb244ef7729503a0317bcff0f8fdaa93ca5cb7d2","macro_function_count":75,"path":"include/xrpl/hook/hook_api.macro","repository":"https://github.com/Xahau/xahaud"}}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,581 @@
{
"byte_policies": {
"AccountID.from": {
"0": "bytesLike"
},
"AccountID.fromHex": {
"0": "hexString"
},
"Hash256.from": {
"0": "bytesLike"
},
"Hash256.fromHex": {
"0": "hexString"
},
"STBlob.equals": {
"0": "bytesLikeOrSTBlob"
},
"STBlob.from": {
"0": "bytesLike"
},
"STBlob.fromHex": {
"0": "hexString"
},
"accept": {
"0": "lifecycleMessage"
},
"emit.prepare": {
"0": "bytesLikeOrSTBlob"
},
"emit.tx": {
"0": "bytesLikeOrSTBlob"
},
"rollback": {
"0": "lifecycleMessage"
},
"state.get": {
"0": "stateKeyLike"
},
"state.set": {
"0": "stateKeyLike",
"1": "stateValueLike"
}
},
"declaration": {
"path": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
"sha256": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da"
},
"globals": {
"AccountID": "object",
"Amount": "object",
"Currency": "object",
"Field": "object",
"Hash": "object",
"Hash128": "object",
"Hash160": "object",
"Hash192": "object",
"Hash256": "object",
"IOUAmount": "object",
"Issue": "object",
"MPTAmount": "object",
"NativeAmount": "object",
"Path": "object",
"PathHop": "object",
"PathSet": "object",
"Result": "object",
"STArray": "object",
"STBlob": "object",
"STObject": "object",
"SerializedField": "object",
"UInt": "object",
"UInt16": "object",
"UInt32": "object",
"UInt64": "object",
"UInt8": "object",
"Vector256": "object",
"VoidResult": "object",
"XChainBridge": "object",
"XFLDecimal": "object",
"accept": "function",
"emit": "object",
"hook": "object",
"ledger": "object",
"otxn": "object",
"rollback": "function",
"state": "object",
"trace": "function",
"util": "object"
},
"namespaces": {
"accept": {
"unlessPresent": "function",
"unlessTruthy": "function",
"when": "function"
},
"emit": {
"prepare": "function",
"reserve": "function",
"tx": "function"
},
"hook": {
"account": "function"
},
"ledger": {
"lastHash": "value",
"lastTime": "value",
"sequence": "value"
},
"otxn": {
"type": "function"
},
"rollback": {
"onAllFail": "function",
"onAnyFail": "function",
"onFail": "function",
"requirePresent": "function",
"requireTruthy": "function",
"when": "function"
},
"state": {
"get": "function",
"set": "function"
},
"util": {
"decodeObject": "function",
"safeDecodeObject": "function",
"validateObject": "function"
}
},
"prototypes": {
"AccountID": {
"members": {
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "AccountID.from(new Uint8Array(20))"
},
"Amount": {
"members": {
"asIOU": "function",
"asMPT": "function",
"asNative": "function",
"byteLength": "value",
"compare": "function",
"currency": "value",
"drops": "value",
"equals": "function",
"isIOU": "function",
"isMPT": "function",
"isNative": "function",
"issue": "value",
"issuer": "value",
"kind": "value",
"mptIssuanceId": "value",
"toBytes": "function",
"toString": "function",
"toXFL": "function",
"value": "value"
},
"own": [
"asIOU",
"asMPT",
"asNative",
"byteLength",
"compare",
"currency",
"drops",
"equals",
"isIOU",
"isMPT",
"isNative",
"issue",
"issuer",
"kind",
"mptIssuanceId",
"toBytes",
"toString",
"toXFL",
"value"
],
"probe": "util.decodeObject(STBlob.fromHex('61D4838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8')).Amount"
},
"Currency": {
"members": {
"byteLength": "value",
"equals": "function",
"isNative": "value",
"toBytes": "function",
"toHex": "function",
"toString": "function"
},
"own": [
"byteLength",
"equals",
"isNative",
"toBytes",
"toHex",
"toString"
],
"probe": "util.decodeObject(new Uint8Array()).withField('BaseAsset', new Uint8Array(20)).BaseAsset"
},
"Hash128": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "util.decodeObject(new Uint8Array()).withField('EmailHash', new Uint8Array(16)).EmailHash"
},
"Hash160": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "util.decodeObject(new Uint8Array()).withField('TakerPaysCurrency', new Uint8Array(20)).TakerPaysCurrency"
},
"Hash192": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "util.decodeObject(new Uint8Array()).withField('MPTokenIssuanceID', new Uint8Array(24)).MPTokenIssuanceID"
},
"Hash256": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "Hash256.from(new Uint8Array(32))"
},
"Issue": {
"members": {
"currency": "value",
"equals": "function",
"issuer": "value",
"kind": "value",
"mptIssuanceId": "value",
"toBytes": "function"
},
"own": [
"currency",
"equals",
"issuer",
"kind",
"mptIssuanceId",
"toBytes"
],
"probe": "util.decodeObject(new Uint8Array()).withField('LockingChainIssue', new Uint8Array(20)).LockingChainIssue"
},
"Path": {
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value"
},
"own": [
"[Symbol.iterator]",
"at",
"length"
],
"probe": "util.decodeObject(STBlob.fromHex('011201B5F762798A53D543A014CAF8B297CFF8F2F937E800')).Paths.at(0)"
},
"PathHop": {
"members": {
"account": "value",
"currency": "value",
"issuer": "value"
},
"own": [
"account",
"currency",
"issuer"
],
"probe": "util.decodeObject(STBlob.fromHex('011201B5F762798A53D543A014CAF8B297CFF8F2F937E800')).Paths.at(0).at(0)"
},
"PathSet": {
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value",
"toBytes": "function"
},
"own": [
"[Symbol.iterator]",
"at",
"length",
"toBytes"
],
"probe": "util.decodeObject(STBlob.fromHex('011201B5F762798A53D543A014CAF8B297CFF8F2F937E800')).Paths"
},
"Result": {
"frozen": true,
"members": {
"okMapOr": "function",
"okOr": "function",
"okOrHandle": "function"
},
"own": [
"okMapOr",
"okOr",
"okOrHandle"
],
"probe": "UInt64.from(0n)"
},
"STArray": {
"exotic": {
"array": false,
"enumerable": [
"0"
],
"extensible": false,
"own": [
"0",
"length"
]
},
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value",
"toJSON": "function"
},
"own": [
"[Symbol.iterator]",
"at",
"toJSON"
],
"probe": "util.decodeObject(new Uint8Array([0xF9, 0xEA, 0x22, 0, 0, 0, 1, 0xE1, 0xF1])).Memos"
},
"STBlob": {
"members": {
"byteAt": "function",
"byteLength": "value",
"equals": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteAt",
"byteLength",
"equals",
"toBytes",
"toHex"
],
"probe": "STBlob.from(new Uint8Array(0))"
},
"STObject": {
"exotic": {
"array": false,
"enumerable": [
"Flags"
],
"extensible": false,
"own": [
"Flags"
]
},
"members": {
"fieldBytes": "function",
"get": "function",
"has": "function",
"toBytes": "function",
"toJSON": "function",
"withField": "function",
"withoutField": "function"
},
"own": [
"fieldBytes",
"get",
"has",
"toBytes",
"toJSON",
"withField",
"withoutField"
],
"probe": "util.decodeObject(new Uint8Array([0x22, 0, 0, 0, 9]))"
},
"UInt": {
"members": {
"add": "function",
"bits": "value",
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"saturatingAdd": "function",
"saturatingSubtract": "function",
"subtract": "function",
"toBigInt": "function",
"toNumber": "function",
"toString": "function",
"wrappingAdd": "function",
"wrappingMultiply": "function",
"wrappingSubtract": "function"
},
"own": [
"add",
"bits",
"byteLength",
"compare",
"equals",
"isZero",
"saturatingAdd",
"saturatingSubtract",
"subtract",
"toBigInt",
"toNumber",
"toString",
"wrappingAdd",
"wrappingMultiply",
"wrappingSubtract"
],
"probe": "UInt8.zero"
},
"Vector256": {
"exotic": {
"array": false,
"enumerable": [
"0"
],
"extensible": false,
"own": [
"0",
"length"
]
},
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value",
"toBytes": "function"
},
"own": [
"[Symbol.iterator]",
"at",
"toBytes"
],
"probe": "util.decodeObject(STBlob.fromHex('011320000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F')).Indexes"
},
"VoidResult": {
"frozen": true,
"members": {
"moot": "function",
"okMapOr": "function",
"okOr": "function",
"okOrHandle": "function"
},
"own": [
"moot"
],
"probe": "state.set('surface-probe', new Uint8Array(0))"
},
"XChainBridge": {
"members": {
"IssuingChainDoor": "value",
"IssuingChainIssue": "value",
"LockingChainDoor": "value",
"LockingChainIssue": "value",
"equals": "function",
"toBytes": "function"
},
"own": [
"IssuingChainDoor",
"IssuingChainIssue",
"LockingChainDoor",
"LockingChainIssue",
"equals",
"toBytes"
],
"probe": "util.decodeObject(STBlob.fromHex('011914B5F762798A53D543A014CAF8B297CFF8F2F937E8000000000000000000000000000000000000000014B5F762798A53D543A014CAF8B297CFF8F2F937E80000000000000000000000000000000000000000')).XChainBridge"
},
"XFLDecimal": {
"members": {
"isNegative": "function",
"isZero": "function"
},
"own": [
"isNegative",
"isZero"
],
"probe": "util.decodeObject(STBlob.fromHex('61D4838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8')).Amount.toXFL()"
}
},
"schema": "jshookz.javascript-surface.v1",
"statics": {
"AccountID": {
"from": "function",
"fromHex": "function",
"one": "value",
"zero": "value"
},
"Hash256": {
"from": "function",
"fromHex": "function",
"zero": "value"
},
"STBlob": {
"from": "function",
"fromHex": "function"
},
"UInt16": {
"from": "function",
"max": "value",
"mulDiv": "function",
"zero": "value"
},
"UInt32": {
"from": "function",
"max": "value",
"mulDiv": "function",
"zero": "value"
},
"UInt64": {
"from": "function",
"max": "value",
"mulDiv": "function",
"mulDivXfl": "function",
"zero": "value"
},
"UInt8": {
"from": "function",
"max": "value",
"mulDiv": "function",
"zero": "value"
}
}
}

27
external/rocksdb/conandata.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
sources:
"6.29.5":
url: "https://github.com/facebook/rocksdb/archive/refs/tags/v6.29.5.tar.gz"
sha256: "ddbf84791f0980c0bbce3902feb93a2c7006f6f53bfd798926143e31d4d756f0"
"6.27.3":
url: "https://github.com/facebook/rocksdb/archive/refs/tags/v6.27.3.tar.gz"
sha256: "ee29901749b9132692b26f0a6c1d693f47d1a9ed8e3771e60556afe80282bf58"
"6.20.3":
url: "https://github.com/facebook/rocksdb/archive/refs/tags/v6.20.3.tar.gz"
sha256: "c6502c7aae641b7e20fafa6c2b92273d935d2b7b2707135ebd9a67b092169dca"
"8.8.1":
url: "https://github.com/facebook/rocksdb/archive/refs/tags/v8.8.1.tar.gz"
sha256: "056c7e21ad8ae36b026ac3b94b9d6e0fcc60e1d937fc80330921e4181be5c36e"
patches:
"6.29.5":
- patch_file: "patches/6.29.5-0001-add-include-cstdint-for-gcc-13.patch"
patch_description: "Fix build with gcc 13 by including cstdint"
patch_type: "portability"
patch_source: "https://github.com/facebook/rocksdb/pull/11118"
- patch_file: "patches/6.29.5-0002-exclude-thirdparty.patch"
patch_description: "Do not include thirdparty.inc"
patch_type: "portability"
"6.27.3":
- patch_file: "patches/6.27.3-0001-add-include-cstdint-for-gcc-13.patch"
patch_description: "Fix build with gcc 13 by including cstdint"
patch_type: "portability"
patch_source: "https://github.com/facebook/rocksdb/pull/11118"

233
external/rocksdb/conanfile.py vendored Normal file
View File

@@ -0,0 +1,233 @@
import os
import glob
import shutil
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.build import check_min_cppstd
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.files import apply_conandata_patches, collect_libs, copy, export_conandata_patches, get, rm, rmdir
from conan.tools.microsoft import check_min_vs, is_msvc, is_msvc_static_runtime
from conan.tools.scm import Version
required_conan_version = ">=1.53.0"
class RocksDBConan(ConanFile):
name = "rocksdb"
homepage = "https://github.com/facebook/rocksdb"
license = ("GPL-2.0-only", "Apache-2.0")
url = "https://github.com/conan-io/conan-center-index"
description = "A library that provides an embeddable, persistent key-value store for fast storage"
topics = ("database", "leveldb", "facebook", "key-value")
package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {
"shared": [True, False],
"fPIC": [True, False],
"lite": [True, False],
"with_gflags": [True, False],
"with_snappy": [True, False],
"with_lz4": [True, False],
"with_zlib": [True, False],
"with_zstd": [True, False],
"with_tbb": [True, False],
"with_jemalloc": [True, False],
"enable_sse": [False, "sse42", "avx2"],
"use_rtti": [True, False],
}
default_options = {
"shared": False,
"fPIC": True,
"lite": False,
"with_snappy": False,
"with_lz4": False,
"with_zlib": False,
"with_zstd": False,
"with_gflags": False,
"with_tbb": False,
"with_jemalloc": False,
"enable_sse": False,
"use_rtti": False,
}
@property
def _min_cppstd(self):
return "11" if Version(self.version) < "8.8.1" else "17"
@property
def _compilers_minimum_version(self):
return {} if self._min_cppstd == "11" else {
"apple-clang": "10",
"clang": "7",
"gcc": "7",
"msvc": "191",
"Visual Studio": "15",
}
def export_sources(self):
export_conandata_patches(self)
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
if self.settings.arch != "x86_64":
del self.options.with_tbb
if self.settings.build_type == "Debug":
self.options.use_rtti = True # Rtti are used in asserts for debug mode...
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
def layout(self):
cmake_layout(self, src_folder="src")
def requirements(self):
if self.options.with_gflags:
self.requires("gflags/2.2.2")
if self.options.with_snappy:
self.requires("snappy/1.1.10")
if self.options.with_lz4:
self.requires("lz4/1.10.0")
if self.options.with_zlib:
self.requires("zlib/[>=1.2.11 <2]")
if self.options.with_zstd:
self.requires("zstd/1.5.6")
if self.options.get_safe("with_tbb"):
self.requires("onetbb/2021.12.0")
if self.options.with_jemalloc:
self.requires("jemalloc/5.3.0")
def validate(self):
if self.settings.compiler.get_safe("cppstd"):
check_min_cppstd(self, self._min_cppstd)
minimum_version = self._compilers_minimum_version.get(str(self.settings.compiler), False)
if minimum_version and Version(self.settings.compiler.version) < minimum_version:
raise ConanInvalidConfiguration(
f"{self.ref} requires C++{self._min_cppstd}, which your compiler does not support."
)
if self.settings.arch not in ["x86_64", "ppc64le", "ppc64", "mips64", "armv8"]:
raise ConanInvalidConfiguration("Rocksdb requires 64 bits")
check_min_vs(self, "191")
if self.version == "6.20.3" and \
self.settings.os == "Linux" and \
self.settings.compiler == "gcc" and \
Version(self.settings.compiler.version) < "5":
raise ConanInvalidConfiguration("Rocksdb 6.20.3 is not compilable with gcc <5.") # See https://github.com/facebook/rocksdb/issues/3522
def source(self):
get(self, **self.conan_data["sources"][self.version], strip_root=True)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["FAIL_ON_WARNINGS"] = False
tc.variables["WITH_TESTS"] = False
tc.variables["WITH_TOOLS"] = False
tc.variables["WITH_CORE_TOOLS"] = False
tc.variables["WITH_BENCHMARK_TOOLS"] = False
tc.variables["WITH_FOLLY_DISTRIBUTED_MUTEX"] = False
if is_msvc(self):
tc.variables["WITH_MD_LIBRARY"] = not is_msvc_static_runtime(self)
tc.variables["ROCKSDB_INSTALL_ON_WINDOWS"] = self.settings.os == "Windows"
tc.variables["ROCKSDB_LITE"] = self.options.lite
tc.variables["WITH_GFLAGS"] = self.options.with_gflags
tc.variables["WITH_SNAPPY"] = self.options.with_snappy
tc.variables["WITH_LZ4"] = self.options.with_lz4
tc.variables["WITH_ZLIB"] = self.options.with_zlib
tc.variables["WITH_ZSTD"] = self.options.with_zstd
tc.variables["WITH_TBB"] = self.options.get_safe("with_tbb", False)
tc.variables["WITH_JEMALLOC"] = self.options.with_jemalloc
tc.variables["ROCKSDB_BUILD_SHARED"] = self.options.shared
tc.variables["ROCKSDB_LIBRARY_EXPORTS"] = self.settings.os == "Windows" and self.options.shared
tc.variables["ROCKSDB_DLL" ] = self.settings.os == "Windows" and self.options.shared
tc.variables["USE_RTTI"] = self.options.use_rtti
if not bool(self.options.enable_sse):
tc.variables["PORTABLE"] = True
tc.variables["FORCE_SSE42"] = False
elif self.options.enable_sse == "sse42":
tc.variables["PORTABLE"] = True
tc.variables["FORCE_SSE42"] = True
elif self.options.enable_sse == "avx2":
tc.variables["PORTABLE"] = False
tc.variables["FORCE_SSE42"] = False
# not available yet in CCI
tc.variables["WITH_NUMA"] = False
tc.generate()
deps = CMakeDeps(self)
if self.options.with_jemalloc:
deps.set_property("jemalloc", "cmake_file_name", "JeMalloc")
deps.set_property("jemalloc", "cmake_target_name", "JeMalloc::JeMalloc")
deps.generate()
def build(self):
apply_conandata_patches(self)
cmake = CMake(self)
cmake.configure()
cmake.build()
def _remove_static_libraries(self):
rm(self, "rocksdb.lib", os.path.join(self.package_folder, "lib"))
for lib in glob.glob(os.path.join(self.package_folder, "lib", "*.a")):
if not lib.endswith(".dll.a"):
os.remove(lib)
def _remove_cpp_headers(self):
for path in glob.glob(os.path.join(self.package_folder, "include", "rocksdb", "*")):
if path != os.path.join(self.package_folder, "include", "rocksdb", "c.h"):
if os.path.isfile(path):
os.remove(path)
else:
shutil.rmtree(path)
def package(self):
copy(self, "COPYING", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
copy(self, "LICENSE*", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
cmake = CMake(self)
cmake.install()
if self.options.shared:
self._remove_static_libraries()
self._remove_cpp_headers() # Force stable ABI for shared libraries
rmdir(self, os.path.join(self.package_folder, "lib", "cmake"))
rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig"))
def package_info(self):
cmake_target = "rocksdb-shared" if self.options.shared else "rocksdb"
self.cpp_info.set_property("cmake_file_name", "RocksDB")
self.cpp_info.set_property("cmake_target_name", f"RocksDB::{cmake_target}")
# TODO: back to global scope in conan v2 once cmake_find_package* generators removed
self.cpp_info.components["librocksdb"].libs = collect_libs(self)
if self.settings.os == "Windows":
self.cpp_info.components["librocksdb"].system_libs = ["shlwapi", "rpcrt4"]
if self.options.shared:
self.cpp_info.components["librocksdb"].defines = ["ROCKSDB_DLL"]
elif self.settings.os in ["Linux", "FreeBSD"]:
self.cpp_info.components["librocksdb"].system_libs = ["pthread", "m"]
if self.options.lite:
self.cpp_info.components["librocksdb"].defines.append("ROCKSDB_LITE")
# TODO: to remove in conan v2 once cmake_find_package* generators removed
self.cpp_info.names["cmake_find_package"] = "RocksDB"
self.cpp_info.names["cmake_find_package_multi"] = "RocksDB"
self.cpp_info.components["librocksdb"].names["cmake_find_package"] = cmake_target
self.cpp_info.components["librocksdb"].names["cmake_find_package_multi"] = cmake_target
self.cpp_info.components["librocksdb"].set_property("cmake_target_name", f"RocksDB::{cmake_target}")
if self.options.with_gflags:
self.cpp_info.components["librocksdb"].requires.append("gflags::gflags")
if self.options.with_snappy:
self.cpp_info.components["librocksdb"].requires.append("snappy::snappy")
if self.options.with_lz4:
self.cpp_info.components["librocksdb"].requires.append("lz4::lz4")
if self.options.with_zlib:
self.cpp_info.components["librocksdb"].requires.append("zlib::zlib")
if self.options.with_zstd:
self.cpp_info.components["librocksdb"].requires.append("zstd::zstd")
if self.options.get_safe("with_tbb"):
self.cpp_info.components["librocksdb"].requires.append("onetbb::onetbb")
if self.options.with_jemalloc:
self.cpp_info.components["librocksdb"].requires.append("jemalloc::jemalloc")

View File

@@ -0,0 +1,30 @@
--- a/include/rocksdb/utilities/checkpoint.h
+++ b/include/rocksdb/utilities/checkpoint.h
@@ -8,6 +8,7 @@
#pragma once
#ifndef ROCKSDB_LITE
+#include <cstdint>
#include <string>
#include <vector>
#include "rocksdb/status.h"
--- a/table/block_based/data_block_hash_index.h
+++ b/table/block_based/data_block_hash_index.h
@@ -5,6 +5,7 @@
#pragma once
+#include <cstdint>
#include <string>
#include <vector>
--- a/util/string_util.h
+++ b/util/string_util.h
@@ -6,6 +6,7 @@
#pragma once
+#include <cstdint>
#include <sstream>
#include <string>
#include <unordered_map>

View File

@@ -0,0 +1,16 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ec59d4491..35577c998 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -101 +100,0 @@ if(MSVC)
- option(WITH_GFLAGS "build with GFlags" OFF)
@@ -103,2 +102,2 @@ if(MSVC)
- include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
-else()
+endif()
+
@@ -117 +116 @@ else()
- if(MINGW)
+ if(MINGW OR MSVC)
@@ -183 +181,0 @@ else()
-endif()

View File

@@ -541,7 +541,7 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
/** Verify an ECDSA signature.
*
* Returns: 1: correct signature
* 0: incorrect or unparsable signature
* 0: incorrect or unparseable signature
* Args: ctx: pointer to a context object
* In: sig: the signature being verified.
* msghash32: the 32-byte message hash being verified.

27
external/wasmtime/conandata.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
sources:
47.0.3:
Linux:
armv8:
sha256: c1ef99bb4ef0151282f339e22a67f8229d9ab336f9a17387e65f140b79c45be1
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-linux-c-api.tar.xz
x86_64:
sha256: aaa3621f2a3d8393696702897f8f78a1cc504437d500701496d560125aefd732
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-linux-c-api.tar.xz
Macos:
armv8:
sha256: 1854c8f03a764c89afe77fa88d9092ab89a368e527cd27a12959b1d91152324e
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-macos-c-api.tar.xz
x86_64:
sha256: 627622087b77b92c163e826ec6ebf834a70d78735828043edf7ace263f8a9e62
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-macos-c-api.tar.xz
MinGW:
x86_64:
sha256: 355a0c7b49b92bb011e74a4047cc1186271aeb8c8cfcc4360248caaf3c4d5f38
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-mingw-c-api.zip
Windows:
armv8:
sha256: 57107b14b28ac2152e903501fe1722007b90fc11ccd63ad6d62ed574c5dd90db
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-windows-c-api.zip
x86_64:
sha256: 952dfbc864295eccc89caa92bc303686017ca4ea205a1f21878809f374bb77c5
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-windows-c-api.zip

162
external/wasmtime/conanfile.py vendored Normal file
View File

@@ -0,0 +1,162 @@
import os
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.files import copy, get
from conan.tools.microsoft import is_msvc
required_conan_version = ">=2.0"
class WasmtimeConan(ConanFile):
name = "wasmtime"
description = "Standalone WebAssembly runtime using Cranelift"
license = "Apache-2.0 WITH LLVM-exception"
url = "https://github.com/Xahau/xahaud"
homepage = "https://github.com/bytecodealliance/wasmtime"
topics = ("webassembly", "wasm")
package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {"shared": [True, False]}
default_options = {"shared": False}
no_copy_source = True
@property
def _sources_os_key(self):
if is_msvc(self):
return "Windows"
if self.settings.os == "Windows" and self.settings.compiler == "gcc":
return "MinGW"
return str(self.settings.os)
def configure(self):
self.settings.rm_safe("compiler.libcxx")
self.settings.rm_safe("compiler.cppstd")
def package_id(self):
# Official release archives contain a prebuilt C ABI. Compiler patch
# versions do not change the packaged bytes.
if self.info.settings.compiler == "clang":
self.info.settings.compiler = "gcc"
del self.info.settings.compiler.version
def validate(self):
try:
self.conan_data["sources"][self.version][self._sources_os_key][
str(self.settings.arch)
]
except KeyError as error:
raise ConanInvalidConfiguration(
"No official Wasmtime C-API archive for "
f"{self.settings.os}/{self.settings.arch}"
) from error
def build(self):
get(
self,
**self.conan_data["sources"][self.version][self._sources_os_key][
str(self.settings.arch)
],
destination=self.build_folder,
strip_root=True,
)
def package(self):
copy(
self,
"*.h",
dst=os.path.join(self.package_folder, "include"),
src=os.path.join(self.build_folder, "include"),
)
copy(
self,
"*.hh",
dst=os.path.join(self.package_folder, "include"),
src=os.path.join(self.build_folder, "include"),
)
source_lib = os.path.join(self.build_folder, "lib")
package_lib = os.path.join(self.package_folder, "lib")
package_bin = os.path.join(self.package_folder, "bin")
if self.options.shared:
copy(
self,
"wasmtime.dll.lib",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"wasmtime.dll",
dst=package_bin,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.dll.a",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.so*",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.dylib",
dst=package_lib,
src=source_lib,
keep_path=False,
)
else:
copy(
self,
"wasmtime.lib",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.a",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"LICENSE",
dst=os.path.join(self.package_folder, "licenses"),
src=self.build_folder,
)
def package_info(self):
if self.options.shared:
self.cpp_info.libs = [
"wasmtime.dll" if self.settings.os == "Windows" else "wasmtime"
]
else:
if self.settings.os == "Windows":
self.cpp_info.defines = ["WASM_API_EXTERN=", "WASI_API_EXTERN="]
self.cpp_info.libs = ["wasmtime"]
if self.settings.os == "Windows":
self.cpp_info.system_libs = [
"ws2_32",
"bcrypt",
"advapi32",
"userenv",
"ntdll",
"shell32",
"ole32",
]
elif self.settings.os in ("Linux", "FreeBSD"):
self.cpp_info.system_libs = ["pthread", "dl", "m", "rt"]

View File

@@ -100,14 +100,14 @@ update_hook_array() {
echo -e "${BLUE}==> Updating ${hook_name}...${NC}"
# Check if hook already exists
if grep -q "static std::vector<uint8_t> const ${hook_name} = {" "${XAHAU_H}"; then
if grep -q "static const std::vector<uint8_t> ${hook_name} = {" "${XAHAU_H}"; then
echo -e "${YELLOW} Replacing existing ${hook_name}${NC}"
# Use awk to replace the array content
awk -v hook="${hook_name}" -v hex="${hex_array}" '
BEGIN { in_array=0 }
{
if ($0 ~ "static std::vector<uint8_t> const " hook " = {") {
if ($0 ~ "static const std::vector<uint8_t> " hook " = {") {
print $0
print hex
in_array=1
@@ -133,7 +133,7 @@ update_hook_array() {
{
if ($0 ~ /#endif.*XAHAU_GENESIS_HOOKS/) {
print ""
print "static std::vector<uint8_t> const " hook " = {"
print "static const std::vector<uint8_t> " hook " = {"
print hex
print "};"
print ""
@@ -178,7 +178,7 @@ echo -e "${GREEN} Formatting completed${NC}"
echo -e "${BLUE}==> Verifying changes...${NC}"
for hook_entry in "${HOOK_FILES[@]}"; do
hook_name="${hook_entry%%:*}"
if grep -q "static std::vector<uint8_t> const ${hook_name} = {" "${XAHAU_H}"; then
if grep -q "static const std::vector<uint8_t> ${hook_name} = {" "${XAHAU_H}"; then
echo -e "${GREEN}${hook_name} found in xahau.h${NC}"
else
echo -e "${RED}${hook_name} NOT found in xahau.h${NC}" >&2

View File

@@ -45,8 +45,8 @@
#include "error.h"
#include "extern.h"
#include "macro.h"
#include "sfcodes.h"
#include "macro.h"
#include "tts.h"
#include "ls_flags.h"

View File

@@ -25,7 +25,6 @@ enum ltACCOUNT_ROOT {
enum ltOFFER {
lsfPassive = 0x00010000,
lsfSell = 0x00020000,
lsfHybrid = 0x00040000,
};
enum ltRIPPLE_STATE {
lsfLowReserve = 0x00010000,
@@ -65,14 +64,6 @@ enum ltMPTOKEN_ISSUANCE {
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
lsmfMPTCanMutateCanLock = 0x00000002,
lsmfMPTCanMutateRequireAuth = 0x00000004,
lsmfMPTCanMutateCanEscrow = 0x00000008,
lsmfMPTCanMutateCanTrade = 0x00000010,
lsmfMPTCanMutateCanTransfer = 0x00000020,
lsmfMPTCanMutateCanClawback = 0x00000040,
lsmfMPTCanMutateMetadata = 0x00010000,
lsmfMPTCanMutateTransferFee = 0x00020000,
};
enum ltMPTOKEN {
lsfMPTAuthorized = 0x00000002,
@@ -80,8 +71,5 @@ enum ltMPTOKEN {
enum ltCREDENTIAL {
lsfAccepted = 0x00010000,
};
enum ltVAULT {
lsfVaultPrivate = 0x00010000,
};
#endif // HOOKLSFLAGS_INCLUDED

View File

@@ -9,7 +9,6 @@
#define sfUNLModifyDisabling ((16U << 16U) + 17U)
#define sfHookResult ((16U << 16U) + 18U)
#define sfWasLockingChainSend ((16U << 16U) + 19U)
#define sfWithdrawalPolicy ((16U << 16U) + 20U)
#define sfLedgerEntryType ((1U << 16U) + 1U)
#define sfTransactionType ((1U << 16U) + 2U)
#define sfSignerWeight ((1U << 16U) + 3U)
@@ -73,8 +72,6 @@
#define sfLockCount ((2U << 16U) + 49U)
#define sfFirstNFTokenSequence ((2U << 16U) + 50U)
#define sfOracleDocumentID ((2U << 16U) + 51U)
#define sfPermissionValue ((2U << 16U) + 52U)
#define sfMutableFlags ((2U << 16U) + 53U)
#define sfStartTime ((2U << 16U) + 93U)
#define sfRepeatCount ((2U << 16U) + 94U)
#define sfDelaySeconds ((2U << 16U) + 95U)
@@ -118,7 +115,6 @@
#define sfTakerGetsCurrency ((17U << 16U) + 3U)
#define sfTakerGetsIssuer ((17U << 16U) + 4U)
#define sfMPTokenIssuanceID ((21U << 16U) + 1U)
#define sfShareMPTID ((21U << 16U) + 2U)
#define sfLedgerHash ((5U << 16U) + 1U)
#define sfParentHash ((5U << 16U) + 2U)
#define sfTransactionHash ((5U << 16U) + 3U)
@@ -156,8 +152,6 @@
#define sfEscrowID ((5U << 16U) + 35U)
#define sfURITokenID ((5U << 16U) + 36U)
#define sfDomainID ((5U << 16U) + 37U)
#define sfVaultID ((5U << 16U) + 38U)
#define sfParentBatchID ((5U << 16U) + 39U)
#define sfHookOnOutgoing ((5U << 16U) + 93U)
#define sfHookOnIncoming ((5U << 16U) + 94U)
#define sfCron ((5U << 16U) + 95U)
@@ -166,10 +160,6 @@
#define sfGovernanceMarks ((5U << 16U) + 98U)
#define sfGovernanceFlags ((5U << 16U) + 99U)
#define sfNumber ((9U << 16U) + 1U)
#define sfAssetsAvailable ((9U << 16U) + 2U)
#define sfAssetsMaximum ((9U << 16U) + 3U)
#define sfAssetsTotal ((9U << 16U) + 4U)
#define sfLossUnrealized ((9U << 16U) + 5U)
#define sfAmount ((6U << 16U) + 1U)
#define sfBalance ((6U << 16U) + 2U)
#define sfLimitAmount ((6U << 16U) + 3U)
@@ -244,7 +234,6 @@
#define sfNFTokenMinter ((8U << 16U) + 9U)
#define sfEmitCallback ((8U << 16U) + 10U)
#define sfHolder ((8U << 16U) + 11U)
#define sfDelegate ((8U << 16U) + 12U)
#define sfHookAccount ((8U << 16U) + 16U)
#define sfOtherChainSource ((8U << 16U) + 18U)
#define sfOtherChainDestination ((8U << 16U) + 19U)
@@ -283,7 +272,6 @@
#define sfNFToken ((14U << 16U) + 12U)
#define sfEmitDetails ((14U << 16U) + 13U)
#define sfHook ((14U << 16U) + 14U)
#define sfPermission ((14U << 16U) + 15U)
#define sfSigner ((14U << 16U) + 16U)
#define sfMajority ((14U << 16U) + 18U)
#define sfDisabledValidator ((14U << 16U) + 19U)
@@ -300,9 +288,6 @@
#define sfXChainCreateAccountAttestationCollectionElement ((14U << 16U) + 31U)
#define sfPriceData ((14U << 16U) + 32U)
#define sfCredential ((14U << 16U) + 33U)
#define sfRawTransaction ((14U << 16U) + 34U)
#define sfBatchSigner ((14U << 16U) + 35U)
#define sfBook ((14U << 16U) + 36U)
#define sfAmountEntry ((14U << 16U) + 91U)
#define sfMintURIToken ((14U << 16U) + 92U)
#define sfHookEmission ((14U << 16U) + 93U)
@@ -322,7 +307,6 @@
#define sfNFTokens ((15U << 16U) + 10U)
#define sfHooks ((15U << 16U) + 11U)
#define sfVoteSlots ((15U << 16U) + 12U)
#define sfAdditionalBooks ((15U << 16U) + 13U)
#define sfMajorities ((15U << 16U) + 16U)
#define sfDisabledValidators ((15U << 16U) + 17U)
#define sfHookExecutions ((15U << 16U) + 18U)
@@ -335,9 +319,6 @@
#define sfAuthorizeCredentials ((15U << 16U) + 26U)
#define sfUnauthorizeCredentials ((15U << 16U) + 27U)
#define sfAcceptedCredentials ((15U << 16U) + 28U)
#define sfPermissions ((15U << 16U) + 29U)
#define sfRawTransactions ((15U << 16U) + 30U)
#define sfBatchSigners ((15U << 16U) + 31U)
#define sfAmounts ((15U << 16U) + 92U)
#define sfHookEmissions ((15U << 16U) + 93U)
#define sfImportVLKeys ((15U << 16U) + 94U)

View File

@@ -61,14 +61,6 @@
#define ttNFTOKEN_MODIFY 70
#define ttPERMISSIONED_DOMAIN_SET 71
#define ttPERMISSIONED_DOMAIN_DELETE 72
#define ttDELEGATE_SET 73
#define ttVAULT_CREATE 74
#define ttVAULT_SET 75
#define ttVAULT_DELETE 76
#define ttVAULT_DEPOSIT 77
#define ttVAULT_WITHDRAW 78
#define ttVAULT_CLAWBACK 79
#define ttBATCH 80
#define ttCRON 92
#define ttCRON_SET 93
#define ttREMARKS_SET 94

View File

@@ -4,7 +4,6 @@
enum UniversalFlags : uint32_t {
tfFullyCanonicalSig = 0x80000000,
tfInnerBatchTxn = 0x40000000,
};
enum AccountSetFlags : uint32_t {
@@ -41,7 +40,6 @@ enum OfferCreateFlags : uint32_t {
tfImmediateOrCancel = 0x00020000,
tfFillOrKill = 0x00040000,
tfSell = 0x00080000,
tfHybrid = 0x00100000,
};
enum PaymentFlags : uint32_t {
@@ -117,10 +115,3 @@ enum AMMClawbackFlags : uint32_t {
enum BridgeModifyFlags : uint32_t {
tfClearAccountCreateAmount = 0x00010000,
};
enum BatchFlags : uint32_t {
tfAllOrNothing = 0x00010000,
tfOnlyOne = 0x00020000,
tfUntilFailure = 0x00040000,
tfIndependent = 0x00080000,
};

View File

@@ -27,6 +27,7 @@
#include <algorithm>
#include <optional>
#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -367,7 +368,7 @@ get(Section const& section,
}
inline std::string
get(Section const& section, std::string const& name, char const* defaultValue)
get(Section const& section, std::string const& name, const char* defaultValue)
{
try
{

View File

@@ -22,10 +22,10 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <cstdint>
#include <cstring>
#include <memory>
#include <utility>
namespace ripple {

View File

@@ -21,11 +21,9 @@
#define RIPPLED_COMPRESSIONALGORITHMS_H_INCLUDED
#include <xrpl/basics/contract.h>
#include <lz4.h>
#include <algorithm>
#include <cstdint>
#include <lz4.h>
#include <stdexcept>
#include <vector>
@@ -55,7 +53,7 @@ lz4Compress(void const* in, std::size_t inSize, BufferFactory&& bf)
auto compressed = bf(outCapacity);
auto compressedSize = LZ4_compress_default(
reinterpret_cast<char const*>(in),
reinterpret_cast<const char*>(in),
reinterpret_cast<char*>(compressed),
inSize,
outCapacity);
@@ -89,7 +87,7 @@ lz4Decompress(
Throw<std::runtime_error>("lz4Decompress: integer overflow (output)");
if (LZ4_decompress_safe(
reinterpret_cast<char const*>(in),
reinterpret_cast<const char*>(in),
reinterpret_cast<char*>(decompressed),
inSize,
decompressedSize) != decompressedSize)

View File

@@ -21,7 +21,6 @@
#define RIPPLE_BASICS_COUNTEDOBJECT_H_INCLUDED
#include <xrpl/beast/type_name.h>
#include <atomic>
#include <string>
#include <utility>

View File

@@ -24,7 +24,9 @@
#include <boost/outcome.hpp>
#include <concepts>
#include <stdexcept>
#include <type_traits>
namespace ripple {
@@ -93,7 +95,7 @@ public:
{
}
constexpr E const&
constexpr const E&
value() const&
{
return val_;
@@ -111,7 +113,7 @@ public:
return std::move(val_);
}
constexpr E const&&
constexpr const E&&
value() const&&
{
return std::move(val_);

View File

@@ -1,515 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_BASICS_INTRUSIVEPOINTER_H_INCLUDED
#define RIPPLE_BASICS_INTRUSIVEPOINTER_H_INCLUDED
#include <concepts>
#include <cstdint>
#include <type_traits>
#include <utility>
namespace ripple {
//------------------------------------------------------------------------------
/** Tag to create an intrusive pointer from another intrusive pointer by using a
static cast. This is useful to create an intrusive pointer to a derived
class from an intrusive pointer to a base class.
*/
struct StaticCastTagSharedIntrusive
{
};
/** Tag to create an intrusive pointer from another intrusive pointer by using a
dynamic cast. This is useful to create an intrusive pointer to a derived
class from an intrusive pointer to a base class. If the cast fails an empty
(null) intrusive pointer is created.
*/
struct DynamicCastTagSharedIntrusive
{
};
/** When creating or adopting a raw pointer, controls whether the strong count
is incremented or not. Use this tag to increment the strong count.
*/
struct SharedIntrusiveAdoptIncrementStrongTag
{
};
/** When creating or adopting a raw pointer, controls whether the strong count
is incremented or not. Use this tag to leave the strong count unchanged.
*/
struct SharedIntrusiveAdoptNoIncrementTag
{
};
//------------------------------------------------------------------------------
//
template <class T>
concept CAdoptTag = std::is_same_v<T, SharedIntrusiveAdoptIncrementStrongTag> ||
std::is_same_v<T, SharedIntrusiveAdoptNoIncrementTag>;
//------------------------------------------------------------------------------
/** A shared intrusive pointer class that supports weak pointers.
This is meant to be used for SHAMapInnerNodes, but may be useful for other
cases. Since the reference counts are stored on the pointee, the pointee is
not destroyed until both the strong _and_ weak pointer counts go to zero.
When the strong pointer count goes to zero, the "partialDestructor" is
called. This can be used to destroy as much of the object as possible while
still retaining the reference counts. For example, for SHAMapInnerNodes the
children may be reset in that function. Note that std::shared_poiner WILL
run the destructor when the strong count reaches zero, but may not free the
memory used by the object until the weak count reaches zero. In rippled, we
typically allocate shared pointers with the `make_shared` function. When
that is used, the memory is not reclaimed until the weak count reaches zero.
*/
template <class T>
class SharedIntrusive
{
public:
SharedIntrusive() = default;
template <CAdoptTag TAdoptTag>
SharedIntrusive(T* p, TAdoptTag) noexcept;
SharedIntrusive(SharedIntrusive const& rhs);
template <class TT>
// TODO: convertible_to isn't quite right. That include a static castable.
// Find the right concept.
requires std::convertible_to<TT*, T*>
SharedIntrusive(SharedIntrusive<TT> const& rhs);
SharedIntrusive(SharedIntrusive&& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedIntrusive(SharedIntrusive<TT>&& rhs);
SharedIntrusive&
operator=(SharedIntrusive const& rhs);
bool
operator!=(std::nullptr_t) const;
bool
operator==(std::nullptr_t) const;
template <class TT>
requires std::convertible_to<TT*, T*>
SharedIntrusive&
operator=(SharedIntrusive<TT> const& rhs);
SharedIntrusive&
operator=(SharedIntrusive&& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedIntrusive&
operator=(SharedIntrusive<TT>&& rhs);
/** Adopt the raw pointer. The strong reference may or may not be
incremented, depending on the TAdoptTag
*/
template <CAdoptTag TAdoptTag = SharedIntrusiveAdoptIncrementStrongTag>
void
adopt(T* p);
~SharedIntrusive();
/** Create a new SharedIntrusive by statically casting the pointer
controlled by the rhs param.
*/
template <class TT>
SharedIntrusive(
StaticCastTagSharedIntrusive,
SharedIntrusive<TT> const& rhs);
/** Create a new SharedIntrusive by statically casting the pointer
controlled by the rhs param.
*/
template <class TT>
SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive<TT>&& rhs);
/** Create a new SharedIntrusive by dynamically casting the pointer
controlled by the rhs param.
*/
template <class TT>
SharedIntrusive(
DynamicCastTagSharedIntrusive,
SharedIntrusive<TT> const& rhs);
/** Create a new SharedIntrusive by dynamically casting the pointer
controlled by the rhs param.
*/
template <class TT>
SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT>&& rhs);
T&
operator*() const noexcept;
T*
operator->() const noexcept;
explicit
operator bool() const noexcept;
/** Set the pointer to null, decrement the strong count, and run the
appropriate release action.
*/
void
reset();
/** Get the raw pointer */
T*
get() const;
/** Return the strong count */
std::size_t
use_count() const;
template <class TT, class... Args>
friend SharedIntrusive<TT>
make_SharedIntrusive(Args&&... args);
template <class TT>
friend class SharedIntrusive;
template <class TT>
friend class SharedWeakUnion;
template <class TT>
friend class WeakIntrusive;
private:
/** Return the raw pointer held by this object. */
T*
unsafeGetRawPtr() const;
/** Exchange the current raw pointer held by this object with the given
pointer. Decrement the strong count of the raw pointer previously held
by this object and run the appropriate release action.
*/
void
unsafeReleaseAndStore(T* next);
/** Set the raw pointer directly. This is wrapped in a function so the class
can support both atomic and non-atomic pointers in a future patch.
*/
void
unsafeSetRawPtr(T* p);
/** Exchange the raw pointer directly.
This sets the raw pointer to the given value and returns the previous
value. This is wrapped in a function so the class can support both
atomic and non-atomic pointers in a future patch.
*/
T*
unsafeExchange(T* p);
/** pointer to the type with an intrusive count */
T* ptr_{nullptr};
};
//------------------------------------------------------------------------------
/** A weak intrusive pointer class for the SharedIntrusive pointer class.
Note that this weak pointer class asks differently from normal weak pointer
classes. When the strong pointer count goes to zero, the "partialDestructor"
is called. See the comment on SharedIntrusive for a fuller explanation.
*/
template <class T>
class WeakIntrusive
{
public:
WeakIntrusive() = default;
WeakIntrusive(WeakIntrusive const& rhs);
WeakIntrusive(WeakIntrusive&& rhs);
WeakIntrusive(SharedIntrusive<T> const& rhs);
// There is no move constructor from a strong intrusive ptr because
// moving would be move expensive than copying in this case (the strong
// ref would need to be decremented)
WeakIntrusive(SharedIntrusive<T> const&& rhs) = delete;
// Since there are no current use cases for copy assignment in
// WeakIntrusive, we delete this operator to simplify the implementation. If
// a need arises in the future, we can reintroduce it with proper
// consideration."
WeakIntrusive&
operator=(WeakIntrusive const&) = delete;
template <class TT>
requires std::convertible_to<TT*, T*>
WeakIntrusive&
operator=(SharedIntrusive<TT> const& rhs);
/** Adopt the raw pointer and increment the weak count. */
void
adopt(T* ptr);
~WeakIntrusive();
/** Get a strong pointer from the weak pointer, if possible. This will
only return a seated pointer if the strong count on the raw pointer
is non-zero before locking.
*/
SharedIntrusive<T>
lock() const;
/** Return true if the strong count is zero. */
bool
expired() const;
/** Set the pointer to null and decrement the weak count.
Note: This may run the destructor if the strong count is zero.
*/
void
reset();
private:
T* ptr_ = nullptr;
/** Decrement the weak count. This does _not_ set the raw pointer to
null.
Note: This may run the destructor if the strong count is zero.
*/
void
unsafeReleaseNoStore();
};
//------------------------------------------------------------------------------
/** A combination of a strong and a weak intrusive pointer stored in the
space of a single pointer.
This class is similar to a `std::variant<SharedIntrusive,WeakIntrusive>`
with some optimizations. In particular, it uses a low-order bit to
determine if the raw pointer represents a strong pointer or a weak
pointer. It can also be quickly switched between its strong pointer and
weak pointer representations. This class is useful for storing intrusive
pointers in tagged caches.
*/
template <class T>
class SharedWeakUnion
{
// Tagged pointer. Low bit determines if this is a strong or a weak
// pointer. The low bit must be masked to zero when converting back to a
// pointer. If the low bit is '1', this is a weak pointer.
static_assert(
alignof(T) >= 2,
"Bad alignment: Combo pointer requires low bit to be zero");
public:
SharedWeakUnion() = default;
SharedWeakUnion(SharedWeakUnion const& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakUnion(SharedIntrusive<TT> const& rhs);
SharedWeakUnion(SharedWeakUnion&& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakUnion(SharedIntrusive<TT>&& rhs);
SharedWeakUnion&
operator=(SharedWeakUnion const& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakUnion&
operator=(SharedIntrusive<TT> const& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakUnion&
operator=(SharedIntrusive<TT>&& rhs);
~SharedWeakUnion();
/** Return a strong pointer if this is already a strong pointer (i.e.
don't lock the weak pointer. Use the `lock` method if that's what's
needed)
*/
SharedIntrusive<T>
getStrong() const;
/** Return true if this is a strong pointer and the strong pointer is
seated.
*/
explicit
operator bool() const noexcept;
/** Set the pointer to null, decrement the appropriate ref count, and
run the appropriate release action.
*/
void
reset();
/** If this is a strong pointer, return the raw pointer. Otherwise
return null.
*/
T*
get() const;
/** If this is a strong pointer, return the strong count. Otherwise
* return 0
*/
std::size_t
use_count() const;
/** Return true if there is a non-zero strong count. */
bool
expired() const;
/** If this is a strong pointer, return the strong pointer. Otherwise
attempt to lock the weak pointer.
*/
SharedIntrusive<T>
lock() const;
/** Return true is this represents a strong pointer. */
bool
isStrong() const;
/** Return true is this represents a weak pointer. */
bool
isWeak() const;
/** If this is a weak pointer, attempt to convert it to a strong
pointer.
@return true if successfully converted to a strong pointer (or was
already a strong pointer). Otherwise false.
*/
bool
convertToStrong();
/** If this is a strong pointer, attempt to convert it to a weak
pointer.
@return false if the pointer is null. Otherwise return true.
*/
bool
convertToWeak();
private:
// Tagged pointer. Low bit determines if this is a strong or a weak
// pointer. The low bit must be masked to zero when converting back to a
// pointer. If the low bit is '1', this is a weak pointer.
std::uintptr_t tp_{0};
static constexpr std::uintptr_t tagMask = 1;
static constexpr std::uintptr_t ptrMask = ~tagMask;
private:
/** Return the raw pointer held by this object.
*/
T*
unsafeGetRawPtr() const;
enum class RefStrength { strong, weak };
/** Set the raw pointer and tag bit directly.
*/
void
unsafeSetRawPtr(T* p, RefStrength rs);
/** Set the raw pointer and tag bit to all zeros (strong null pointer).
*/
void unsafeSetRawPtr(std::nullptr_t);
/** Decrement the appropriate ref count, and run the appropriate release
action. Note: this does _not_ set the raw pointer to null.
*/
void
unsafeReleaseNoStore();
};
//------------------------------------------------------------------------------
/** Create a shared intrusive pointer.
Note: unlike std::shared_ptr, where there is an advantage of allocating
the pointer and control block together, there is no benefit for intrusive
pointers.
*/
template <class TT, class... Args>
SharedIntrusive<TT>
make_SharedIntrusive(Args&&... args)
{
auto p = new TT(std::forward<Args>(args)...);
static_assert(
noexcept(SharedIntrusive<TT>(
std::declval<TT*>(),
std::declval<SharedIntrusiveAdoptNoIncrementTag>())),
"SharedIntrusive constructor should not throw or this can leak "
"memory");
return SharedIntrusive<TT>(p, SharedIntrusiveAdoptNoIncrementTag{});
}
//------------------------------------------------------------------------------
namespace intr_ptr {
template <class T>
using SharedPtr = SharedIntrusive<T>;
template <class T>
using WeakPtr = WeakIntrusive<T>;
template <class T>
using SharedWeakUnionPtr = SharedWeakUnion<T>;
template <class T, class... A>
SharedPtr<T>
make_shared(A&&... args)
{
return make_SharedIntrusive<T>(std::forward<A>(args)...);
}
template <class T, class TT>
SharedPtr<T>
static_pointer_cast(TT const& v)
{
return SharedPtr<T>(StaticCastTagSharedIntrusive{}, v);
}
template <class T, class TT>
SharedPtr<T>
dynamic_pointer_cast(TT const& v)
{
return SharedPtr<T>(DynamicCastTagSharedIntrusive{}, v);
}
} // namespace intr_ptr
} // namespace ripple
#endif

View File

@@ -1,742 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_BASICS_INTRUSIVEPOINTER_IPP_INCLUDED
#define RIPPLE_BASICS_INTRUSIVEPOINTER_IPP_INCLUDED
#include <xrpl/basics/IntrusivePointer.h>
#include <xrpl/basics/IntrusiveRefCounts.h>
#include <utility>
namespace ripple {
template <class T>
template <CAdoptTag TAdoptTag>
SharedIntrusive<T>::SharedIntrusive(T* p, TAdoptTag) noexcept : ptr_{p}
{
if constexpr (std::is_same_v<
TAdoptTag,
SharedIntrusiveAdoptIncrementStrongTag>)
{
if (p)
p->addStrongRef();
}
}
template <class T>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive const& rhs)
: ptr_{[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}()}
{
}
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}()}
{
}
template <class T>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive&& rhs)
: ptr_{rhs.unsafeExchange(nullptr)}
{
}
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive<TT>&& rhs)
: ptr_{rhs.unsafeExchange(nullptr)}
{
}
template <class T>
SharedIntrusive<T>&
SharedIntrusive<T>::operator=(SharedIntrusive const& rhs)
{
if (this == &rhs)
return *this;
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
unsafeReleaseAndStore(p);
return *this;
}
template <class T>
template <class TT>
// clang-format off
requires std::convertible_to<TT*, T*>
// clang-format on
SharedIntrusive<T>&
SharedIntrusive<T>::operator=(SharedIntrusive<TT> const& rhs)
{
if constexpr (std::is_same_v<T, TT>)
{
// This case should never be hit. The operator above will run instead.
// (The normal operator= is needed or it will be marked `deleted`)
if (this == &rhs)
return *this;
}
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
unsafeReleaseAndStore(p);
return *this;
}
template <class T>
SharedIntrusive<T>&
SharedIntrusive<T>::operator=(SharedIntrusive&& rhs)
{
if (this == &rhs)
return *this;
unsafeReleaseAndStore(rhs.unsafeExchange(nullptr));
return *this;
}
template <class T>
template <class TT>
// clang-format off
requires std::convertible_to<TT*, T*>
// clang-format on
SharedIntrusive<T>&
SharedIntrusive<T>::operator=(SharedIntrusive<TT>&& rhs)
{
static_assert(
!std::is_same_v<T, TT>,
"This overload should not be instantiated for T == TT");
unsafeReleaseAndStore(rhs.unsafeExchange(nullptr));
return *this;
}
template <class T>
bool
SharedIntrusive<T>::operator!=(std::nullptr_t) const
{
return this->get() != nullptr;
}
template <class T>
bool
SharedIntrusive<T>::operator==(std::nullptr_t) const
{
return this->get() == nullptr;
}
template <class T>
template <CAdoptTag TAdoptTag>
void
SharedIntrusive<T>::adopt(T* p)
{
if constexpr (std::is_same_v<
TAdoptTag,
SharedIntrusiveAdoptIncrementStrongTag>)
{
if (p)
p->addStrongRef();
}
unsafeReleaseAndStore(p);
}
template <class T>
SharedIntrusive<T>::~SharedIntrusive()
{
unsafeReleaseAndStore(nullptr);
};
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(
StaticCastTagSharedIntrusive,
SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = static_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}()}
{
}
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(
StaticCastTagSharedIntrusive,
SharedIntrusive<TT>&& rhs)
: ptr_{static_cast<T*>(rhs.unsafeExchange(nullptr))}
{
}
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(
DynamicCastTagSharedIntrusive,
SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = dynamic_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}()}
{
}
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(
DynamicCastTagSharedIntrusive,
SharedIntrusive<TT>&& rhs)
{
// This can be simplified without the `exchange`, but the `exchange` is kept
// in anticipation of supporting atomic operations.
auto toSet = rhs.unsafeExchange(nullptr);
if (toSet)
{
ptr_ = dynamic_cast<T*>(toSet);
if (!ptr_)
// need to set the pointer back or will leak
rhs.unsafeExchange(toSet);
}
}
template <class T>
T&
SharedIntrusive<T>::operator*() const noexcept
{
return *unsafeGetRawPtr();
}
template <class T>
T*
SharedIntrusive<T>::operator->() const noexcept
{
return unsafeGetRawPtr();
}
template <class T>
SharedIntrusive<T>::operator bool() const noexcept
{
return bool(unsafeGetRawPtr());
}
template <class T>
void
SharedIntrusive<T>::reset()
{
unsafeReleaseAndStore(nullptr);
}
template <class T>
T*
SharedIntrusive<T>::get() const
{
return unsafeGetRawPtr();
}
template <class T>
std::size_t
SharedIntrusive<T>::use_count() const
{
if (auto p = unsafeGetRawPtr())
return p->use_count();
return 0;
}
template <class T>
T*
SharedIntrusive<T>::unsafeGetRawPtr() const
{
return ptr_;
}
template <class T>
void
SharedIntrusive<T>::unsafeSetRawPtr(T* p)
{
ptr_ = p;
}
template <class T>
T*
SharedIntrusive<T>::unsafeExchange(T* p)
{
return std::exchange(ptr_, p);
}
template <class T>
void
SharedIntrusive<T>::unsafeReleaseAndStore(T* next)
{
auto prev = unsafeExchange(next);
if (!prev)
return;
using enum ReleaseStrongRefAction;
auto action = prev->releaseStrongRef();
switch (action)
{
case noop:
break;
case destroy:
delete prev;
break;
case partialDestroy:
prev->partialDestructor();
partialDestructorFinished(&prev);
// prev is null and may no longer be used
break;
}
}
//------------------------------------------------------------------------------
template <class T>
WeakIntrusive<T>::WeakIntrusive(WeakIntrusive const& rhs) : ptr_{rhs.ptr_}
{
if (ptr_)
ptr_->addWeakRef();
}
template <class T>
WeakIntrusive<T>::WeakIntrusive(WeakIntrusive&& rhs) : ptr_{rhs.ptr_}
{
rhs.ptr_ = nullptr;
}
template <class T>
WeakIntrusive<T>::WeakIntrusive(SharedIntrusive<T> const& rhs)
: ptr_{rhs.unsafeGetRawPtr()}
{
if (ptr_)
ptr_->addWeakRef();
}
template <class T>
template <class TT>
// clang-format off
requires std::convertible_to<TT*, T*>
// clang-format on
WeakIntrusive<T>&
WeakIntrusive<T>::operator=(SharedIntrusive<TT> const& rhs)
{
unsafeReleaseNoStore();
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addWeakRef();
return *this;
}
template <class T>
void
WeakIntrusive<T>::adopt(T* ptr)
{
unsafeReleaseNoStore();
if (ptr)
ptr->addWeakRef();
ptr_ = ptr;
}
template <class T>
WeakIntrusive<T>::~WeakIntrusive()
{
unsafeReleaseNoStore();
}
template <class T>
SharedIntrusive<T>
WeakIntrusive<T>::lock() const
{
if (ptr_ && ptr_->checkoutStrongRefFromWeak())
{
return SharedIntrusive<T>{ptr_, SharedIntrusiveAdoptNoIncrementTag{}};
}
return {};
}
template <class T>
bool
WeakIntrusive<T>::expired() const
{
return (!ptr_ || ptr_->expired());
}
template <class T>
void
WeakIntrusive<T>::reset()
{
unsafeReleaseNoStore();
ptr_ = nullptr;
}
template <class T>
void
WeakIntrusive<T>::unsafeReleaseNoStore()
{
if (!ptr_)
return;
using enum ReleaseWeakRefAction;
auto action = ptr_->releaseWeakRef();
switch (action)
{
case noop:
break;
case destroy:
delete ptr_;
break;
}
}
//------------------------------------------------------------------------------
template <class T>
SharedWeakUnion<T>::SharedWeakUnion(SharedWeakUnion const& rhs) : tp_{rhs.tp_}
{
auto p = rhs.unsafeGetRawPtr();
if (!p)
return;
if (rhs.isStrong())
p->addStrongRef();
else
p->addWeakRef();
}
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakUnion<T>::SharedWeakUnion(SharedIntrusive<TT> const& rhs)
{
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
unsafeSetRawPtr(p, RefStrength::strong);
}
template <class T>
SharedWeakUnion<T>::SharedWeakUnion(SharedWeakUnion&& rhs) : tp_{rhs.tp_}
{
rhs.unsafeSetRawPtr(nullptr);
}
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakUnion<T>::SharedWeakUnion(SharedIntrusive<TT>&& rhs)
{
auto p = rhs.unsafeGetRawPtr();
if (p)
unsafeSetRawPtr(p, RefStrength::strong);
rhs.unsafeSetRawPtr(nullptr);
}
template <class T>
SharedWeakUnion<T>&
SharedWeakUnion<T>::operator=(SharedWeakUnion const& rhs)
{
if (this == &rhs)
return *this;
unsafeReleaseNoStore();
if (auto p = rhs.unsafeGetRawPtr())
{
if (rhs.isStrong())
{
p->addStrongRef();
unsafeSetRawPtr(p, RefStrength::strong);
}
else
{
p->addWeakRef();
unsafeSetRawPtr(p, RefStrength::weak);
}
}
else
{
unsafeSetRawPtr(nullptr);
}
return *this;
}
template <class T>
template <class TT>
// clang-format off
requires std::convertible_to<TT*, T*>
// clang-format on
SharedWeakUnion<T>&
SharedWeakUnion<T>::operator=(SharedIntrusive<TT> const& rhs)
{
unsafeReleaseNoStore();
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
unsafeSetRawPtr(p, RefStrength::strong);
return *this;
}
template <class T>
template <class TT>
// clang-format off
requires std::convertible_to<TT*, T*>
// clang-format on
SharedWeakUnion<T>&
SharedWeakUnion<T>::operator=(SharedIntrusive<TT>&& rhs)
{
unsafeReleaseNoStore();
unsafeSetRawPtr(rhs.unsafeGetRawPtr(), RefStrength::strong);
rhs.unsafeSetRawPtr(nullptr);
return *this;
}
template <class T>
SharedWeakUnion<T>::~SharedWeakUnion()
{
unsafeReleaseNoStore();
};
// Return a strong pointer if this is already a strong pointer (i.e. don't
// lock the weak pointer. Use the `lock` method if that's what's needed)
template <class T>
SharedIntrusive<T>
SharedWeakUnion<T>::getStrong() const
{
SharedIntrusive<T> result;
auto p = unsafeGetRawPtr();
if (p && isStrong())
{
result.template adopt<SharedIntrusiveAdoptIncrementStrongTag>(p);
}
return result;
}
template <class T>
SharedWeakUnion<T>::operator bool() const noexcept
{
return bool(get());
}
template <class T>
void
SharedWeakUnion<T>::reset()
{
unsafeReleaseNoStore();
unsafeSetRawPtr(nullptr);
}
template <class T>
T*
SharedWeakUnion<T>::get() const
{
return isStrong() ? unsafeGetRawPtr() : nullptr;
}
template <class T>
std::size_t
SharedWeakUnion<T>::use_count() const
{
if (auto p = get())
return p->use_count();
return 0;
}
template <class T>
bool
SharedWeakUnion<T>::expired() const
{
auto p = unsafeGetRawPtr();
return (!p || p->expired());
}
template <class T>
SharedIntrusive<T>
SharedWeakUnion<T>::lock() const
{
SharedIntrusive<T> result;
auto p = unsafeGetRawPtr();
if (!p)
return result;
if (isStrong())
{
result.template adopt<SharedIntrusiveAdoptIncrementStrongTag>(p);
return result;
}
if (p->checkoutStrongRefFromWeak())
{
result.template adopt<SharedIntrusiveAdoptNoIncrementTag>(p);
return result;
}
return result;
}
template <class T>
bool
SharedWeakUnion<T>::isStrong() const
{
return !(tp_ & tagMask);
}
template <class T>
bool
SharedWeakUnion<T>::isWeak() const
{
return tp_ & tagMask;
}
template <class T>
bool
SharedWeakUnion<T>::convertToStrong()
{
if (isStrong())
return true;
auto p = unsafeGetRawPtr();
if (p && p->checkoutStrongRefFromWeak())
{
[[maybe_unused]] auto action = p->releaseWeakRef();
XRPL_ASSERT(
(action == ReleaseWeakRefAction::noop),
"ripple::SharedWeakUnion::convertToStrong : "
"action is noop");
unsafeSetRawPtr(p, RefStrength::strong);
return true;
}
return false;
}
template <class T>
bool
SharedWeakUnion<T>::convertToWeak()
{
if (isWeak())
return true;
auto p = unsafeGetRawPtr();
if (!p)
return false;
using enum ReleaseStrongRefAction;
auto action = p->addWeakReleaseStrongRef();
switch (action)
{
case noop:
break;
case destroy:
// We just added a weak ref. How could we destroy?
// LCOV_EXCL_START
UNREACHABLE(
"ripple::SharedWeakUnion::convertToWeak : destroying freshly "
"added ref");
delete p;
unsafeSetRawPtr(nullptr);
return true; // Should never happen
// LCOV_EXCL_STOP
case partialDestroy:
// This is a weird case. We just converted the last strong
// pointer to a weak pointer.
p->partialDestructor();
partialDestructorFinished(&p);
// p is null and may no longer be used
break;
}
unsafeSetRawPtr(p, RefStrength::weak);
return true;
}
template <class T>
T*
SharedWeakUnion<T>::unsafeGetRawPtr() const
{
return reinterpret_cast<T*>(tp_ & ptrMask);
}
template <class T>
void
SharedWeakUnion<T>::unsafeSetRawPtr(T* p, RefStrength rs)
{
tp_ = reinterpret_cast<std::uintptr_t>(p);
if (tp_ && rs == RefStrength::weak)
tp_ |= tagMask;
}
template <class T>
void
SharedWeakUnion<T>::unsafeSetRawPtr(std::nullptr_t)
{
tp_ = 0;
}
template <class T>
void
SharedWeakUnion<T>::unsafeReleaseNoStore()
{
auto p = unsafeGetRawPtr();
if (!p)
return;
if (isStrong())
{
using enum ReleaseStrongRefAction;
auto strongAction = p->releaseStrongRef();
switch (strongAction)
{
case noop:
break;
case destroy:
delete p;
break;
case partialDestroy:
p->partialDestructor();
partialDestructorFinished(&p);
// p is null and may no longer be used
break;
}
}
else
{
using enum ReleaseWeakRefAction;
auto weakAction = p->releaseWeakRef();
switch (weakAction)
{
case noop:
break;
case destroy:
delete p;
break;
}
}
}
} // namespace ripple
#endif

View File

@@ -1,502 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_BASICS_INTRUSIVEREFCOUNTS_H_INCLUDED
#define RIPPLE_BASICS_INTRUSIVEREFCOUNTS_H_INCLUDED
#include <xrpl/beast/utility/instrumentation.h>
#include <atomic>
#include <cstdint>
namespace ripple {
/** Action to perform when releasing a strong pointer.
noop: Do nothing. For example, a `noop` action will occur when a count is
decremented to a non-zero value.
partialDestroy: Run the `partialDestructor`. This action will happen when a
strong count is decremented to zero and the weak count is non-zero.
destroy: Run the destructor. This action will occur when either the strong
count or weak count is decremented and the other count is also zero.
*/
enum class ReleaseStrongRefAction { noop, partialDestroy, destroy };
/** Action to perform when releasing a weak pointer.
noop: Do nothing. For example, a `noop` action will occur when a count is
decremented to a non-zero value.
destroy: Run the destructor. This action will occur when either the strong
count or weak count is decremented and the other count is also zero.
*/
enum class ReleaseWeakRefAction { noop, destroy };
/** Implement the strong count, weak count, and bit flags for an intrusive
pointer.
A class can satisfy the requirements of a ripple::IntrusivePointer by
inheriting from this class.
*/
struct IntrusiveRefCounts
{
virtual ~IntrusiveRefCounts() noexcept;
// This must be `noexcept` or the make_SharedIntrusive function could leak
// memory.
void
addStrongRef() const noexcept;
void
addWeakRef() const noexcept;
ReleaseStrongRefAction
releaseStrongRef() const;
// Same as:
// {
// addWeakRef();
// return releaseStrongRef;
// }
// done as one atomic operation
ReleaseStrongRefAction
addWeakReleaseStrongRef() const;
ReleaseWeakRefAction
releaseWeakRef() const;
// Returns true is able to checkout a strong ref. False otherwise
bool
checkoutStrongRefFromWeak() const noexcept;
bool
expired() const noexcept;
std::size_t
use_count() const noexcept;
// This function MUST be called after a partial destructor finishes running.
// Calling this function may cause other threads to delete the object
// pointed to by `o`, so `o` should never be used after calling this
// function. The parameter will be set to a `nullptr` after calling this
// function to emphasize that it should not be used.
// Note: This is intentionally NOT called at the end of `partialDestructor`.
// The reason for this is if new classes are written to support this smart
// pointer class, they need to write their own `partialDestructor` function
// and ensure `partialDestructorFinished` is called at the end. Putting this
// call inside the smart pointer class itself is expected to be less error
// prone.
// Note: The "two-star" programming is intentional. It emphasizes that `o`
// may be deleted and the unergonomic API is meant to signal the special
// nature of this function call to callers.
// Note: This is a template to support incompletely defined classes.
template <class T>
friend void
partialDestructorFinished(T** o);
private:
// TODO: We may need to use a uint64_t for both counts. This will reduce the
// memory savings. We need to audit the code to make sure 16 bit counts are
// enough for strong pointers and 14 bit counts are enough for weak
// pointers. Use type aliases to make it easy to switch types.
using CountType = std::uint16_t;
static constexpr size_t StrongCountNumBits = sizeof(CountType) * 8;
static constexpr size_t WeakCountNumBits = StrongCountNumBits - 2;
using FieldType = std::uint32_t;
static constexpr size_t FieldTypeBits = sizeof(FieldType) * 8;
static constexpr FieldType one = 1;
/** `refCounts` consists of four fields that are treated atomically:
1. Strong count. This is a count of the number of shared pointers that
hold a reference to this object. When the strong counts goes to zero,
if the weak count is zero, the destructor is run. If the weak count is
non-zero when the strong count goes to zero then the partialDestructor
is run.
2. Weak count. This is a count of the number of weak pointer that hold
a reference to this object. When the weak count goes to zero and the
strong count is also zero, then the destructor is run.
3. Partial destroy started bit. This bit is set if the
`partialDestructor` function has been started (or is about to be
started). This is used to prevent the destructor from running
concurrently with the partial destructor. This can easily happen when
the last strong pointer release its reference in one thread and starts
the partialDestructor, while in another thread the last weak pointer
goes out of scope and starts the destructor while the partialDestructor
is still running. Both a start and finished bit is needed to handle a
corner-case where the last strong pointer goes out of scope, then then
last `weakPointer` goes out of scope, but this happens before the
`partialDestructor` bit is set. It would be possible to use a single
bit if it could also be set atomically when the strong count goes to
zero and the weak count is non-zero, but that would add complexity (and
likely slow down common cases as well).
4. Partial destroy finished bit. This bit is set when the
`partialDestructor` has finished running. See (3) above for more
information.
*/
mutable std::atomic<FieldType> refCounts{strongDelta};
/** Amount to change the strong count when adding or releasing a reference
Note: The strong count is stored in the low `StrongCountNumBits` bits
of refCounts
*/
static constexpr FieldType strongDelta = 1;
/** Amount to change the weak count when adding or releasing a reference
Note: The weak count is stored in the high `WeakCountNumBits` bits of
refCounts
*/
static constexpr FieldType weakDelta = (one << StrongCountNumBits);
/** Flag that is set when the partialDestroy function has started running
(or is about to start running).
See description of the `refCounts` field for a fuller description of
this field.
*/
static constexpr FieldType partialDestroyStartedMask =
(one << (FieldTypeBits - 1));
/** Flag that is set when the partialDestroy function has finished running
See description of the `refCounts` field for a fuller description of
this field.
*/
static constexpr FieldType partialDestroyFinishedMask =
(one << (FieldTypeBits - 2));
/** Mask that will zero out all the `count` bits and leave the tag bits
unchanged.
*/
static constexpr FieldType tagMask =
partialDestroyStartedMask | partialDestroyFinishedMask;
/** Mask that will zero out the `tag` bits and leave the count bits
unchanged.
*/
static constexpr FieldType valueMask = ~tagMask;
/** Mask that will zero out everything except the strong count.
*/
static constexpr FieldType strongMask =
((one << StrongCountNumBits) - 1) & valueMask;
/** Mask that will zero out everything except the weak count.
*/
static constexpr FieldType weakMask =
(((one << WeakCountNumBits) - 1) << StrongCountNumBits) & valueMask;
/** Unpack the count and tag fields from the packed atomic integer form. */
struct RefCountPair
{
CountType strong;
CountType weak;
/** The `partialDestroyStartedBit` is set to on when the partial
destroy function is started. It is not a boolean; it is a uint32
with all bits zero with the possible exception of the
`partialDestroyStartedMask` bit. This is done so it can be directly
masked into the `combinedValue`.
*/
FieldType partialDestroyStartedBit{0};
/** The `partialDestroyFinishedBit` is set to on when the partial
destroy function has finished.
*/
FieldType partialDestroyFinishedBit{0};
RefCountPair(FieldType v) noexcept;
RefCountPair(CountType s, CountType w) noexcept;
/** Convert back to the packed integer form. */
FieldType
combinedValue() const noexcept;
static constexpr CountType maxStrongValue =
static_cast<CountType>((one << StrongCountNumBits) - 1);
static constexpr CountType maxWeakValue =
static_cast<CountType>((one << WeakCountNumBits) - 1);
/** Put an extra margin to detect when running up against limits.
This is only used in debug code, and is useful if we reduce the
number of bits in the strong and weak counts (to 16 and 14 bits).
*/
static constexpr CountType checkStrongMaxValue = maxStrongValue - 32;
static constexpr CountType checkWeakMaxValue = maxWeakValue - 32;
};
};
inline void
IntrusiveRefCounts::addStrongRef() const noexcept
{
refCounts.fetch_add(strongDelta, std::memory_order_acq_rel);
}
inline void
IntrusiveRefCounts::addWeakRef() const noexcept
{
refCounts.fetch_add(weakDelta, std::memory_order_acq_rel);
}
inline ReleaseStrongRefAction
IntrusiveRefCounts::releaseStrongRef() const
{
// Subtract `strongDelta` from refCounts. If this releases the last strong
// ref, set the `partialDestroyStarted` bit. It is important that the ref
// count and the `partialDestroyStartedBit` are changed atomically (hence
// the loop and `compare_exchange` op). If this didn't need to be done
// atomically, the loop could be replaced with a `fetch_sub` and a
// conditional `fetch_or`. This loop will almost always run once.
using enum ReleaseStrongRefAction;
auto prevIntVal = refCounts.load(std::memory_order_acquire);
while (1)
{
RefCountPair const prevVal{prevIntVal};
XRPL_ASSERT(
(prevVal.strong >= strongDelta),
"ripple::IntrusiveRefCounts::releaseStrongRef : previous ref "
"higher than new");
auto nextIntVal = prevIntVal - strongDelta;
ReleaseStrongRefAction action = noop;
if (prevVal.strong == 1)
{
if (prevVal.weak == 0)
{
action = destroy;
}
else
{
nextIntVal |= partialDestroyStartedMask;
action = partialDestroy;
}
}
if (refCounts.compare_exchange_weak(
prevIntVal, nextIntVal, std::memory_order_acq_rel))
{
// Can't be in partial destroy because only decrementing the strong
// count to zero can start a partial destroy, and that can't happen
// twice.
XRPL_ASSERT(
(action == noop) || !(prevIntVal & partialDestroyStartedMask),
"ripple::IntrusiveRefCounts::releaseStrongRef : not in partial "
"destroy");
return action;
}
}
}
inline ReleaseStrongRefAction
IntrusiveRefCounts::addWeakReleaseStrongRef() const
{
using enum ReleaseStrongRefAction;
static_assert(weakDelta > strongDelta);
auto constexpr delta = weakDelta - strongDelta;
auto prevIntVal = refCounts.load(std::memory_order_acquire);
// This loop will almost always run once. The loop is needed to atomically
// change the counts and flags (the count could be atomically changed, but
// the flags depend on the current value of the counts).
//
// Note: If this becomes a perf bottleneck, the `partialDestoryStartedMask`
// may be able to be set non-atomically. But it is easier to reason about
// the code if the flag is set atomically.
while (1)
{
RefCountPair const prevVal{prevIntVal};
// Converted the last strong pointer to a weak pointer.
//
// Can't be in partial destroy because only decrementing the
// strong count to zero can start a partial destroy, and that
// can't happen twice.
XRPL_ASSERT(
(!prevVal.partialDestroyStartedBit),
"ripple::IntrusiveRefCounts::addWeakReleaseStrongRef : not in "
"partial destroy");
auto nextIntVal = prevIntVal + delta;
ReleaseStrongRefAction action = noop;
if (prevVal.strong == 1)
{
if (prevVal.weak == 0)
{
action = noop;
}
else
{
nextIntVal |= partialDestroyStartedMask;
action = partialDestroy;
}
}
if (refCounts.compare_exchange_weak(
prevIntVal, nextIntVal, std::memory_order_acq_rel))
{
XRPL_ASSERT(
(!(prevIntVal & partialDestroyStartedMask)),
"ripple::IntrusiveRefCounts::addWeakReleaseStrongRef : not "
"started partial destroy");
return action;
}
}
}
inline ReleaseWeakRefAction
IntrusiveRefCounts::releaseWeakRef() const
{
auto prevIntVal = refCounts.fetch_sub(weakDelta, std::memory_order_acq_rel);
RefCountPair prev = prevIntVal;
if (prev.weak == 1 && prev.strong == 0)
{
if (!prev.partialDestroyStartedBit)
{
// This case should only be hit if the partialDestroyStartedBit is
// set non-atomically (and even then very rarely). The code is kept
// in case we need to set the flag non-atomically for perf reasons.
refCounts.wait(prevIntVal, std::memory_order_acquire);
prevIntVal = refCounts.load(std::memory_order_acquire);
prev = RefCountPair{prevIntVal};
}
if (!prev.partialDestroyFinishedBit)
{
// partial destroy MUST finish before running a full destroy (when
// using weak pointers)
refCounts.wait(prevIntVal - weakDelta, std::memory_order_acquire);
}
return ReleaseWeakRefAction::destroy;
}
return ReleaseWeakRefAction::noop;
}
inline bool
IntrusiveRefCounts::checkoutStrongRefFromWeak() const noexcept
{
auto curValue = RefCountPair{1, 1}.combinedValue();
auto desiredValue = RefCountPair{2, 1}.combinedValue();
while (!refCounts.compare_exchange_weak(
curValue, desiredValue, std::memory_order_acq_rel))
{
RefCountPair const prev{curValue};
if (!prev.strong)
return false;
desiredValue = curValue + strongDelta;
}
return true;
}
inline bool
IntrusiveRefCounts::expired() const noexcept
{
RefCountPair const val = refCounts.load(std::memory_order_acquire);
return val.strong == 0;
}
inline std::size_t
IntrusiveRefCounts::use_count() const noexcept
{
RefCountPair const val = refCounts.load(std::memory_order_acquire);
return val.strong;
}
inline IntrusiveRefCounts::~IntrusiveRefCounts() noexcept
{
#ifndef NDEBUG
auto v = refCounts.load(std::memory_order_acquire);
XRPL_ASSERT(
(!(v & valueMask)),
"ripple::IntrusiveRefCounts::~IntrusiveRefCounts : count must be zero");
auto t = v & tagMask;
XRPL_ASSERT(
(!t || t == tagMask),
"ripple::IntrusiveRefCounts::~IntrusiveRefCounts : valid tag");
#endif
}
//------------------------------------------------------------------------------
inline IntrusiveRefCounts::RefCountPair::RefCountPair(
IntrusiveRefCounts::FieldType v) noexcept
: strong{static_cast<CountType>(v & strongMask)}
, weak{static_cast<CountType>((v & weakMask) >> StrongCountNumBits)}
, partialDestroyStartedBit{v & partialDestroyStartedMask}
, partialDestroyFinishedBit{v & partialDestroyFinishedMask}
{
XRPL_ASSERT(
(strong < checkStrongMaxValue && weak < checkWeakMaxValue),
"ripple::IntrusiveRefCounts::RefCountPair(FieldType) : inputs inside "
"range");
}
inline IntrusiveRefCounts::RefCountPair::RefCountPair(
IntrusiveRefCounts::CountType s,
IntrusiveRefCounts::CountType w) noexcept
: strong{s}, weak{w}
{
XRPL_ASSERT(
(strong < checkStrongMaxValue && weak < checkWeakMaxValue),
"ripple::IntrusiveRefCounts::RefCountPair(CountType, CountType) : "
"inputs inside range");
}
inline IntrusiveRefCounts::FieldType
IntrusiveRefCounts::RefCountPair::combinedValue() const noexcept
{
XRPL_ASSERT(
(strong < checkStrongMaxValue && weak < checkWeakMaxValue),
"ripple::IntrusiveRefCounts::RefCountPair::combinedValue : inputs "
"inside range");
return (static_cast<IntrusiveRefCounts::FieldType>(weak)
<< IntrusiveRefCounts::StrongCountNumBits) |
static_cast<IntrusiveRefCounts::FieldType>(strong) |
partialDestroyStartedBit | partialDestroyFinishedBit;
}
template <class T>
inline void
partialDestructorFinished(T** o)
{
T& self = **o;
IntrusiveRefCounts::RefCountPair p =
self.refCounts.fetch_or(IntrusiveRefCounts::partialDestroyFinishedMask);
XRPL_ASSERT(
(!p.partialDestroyFinishedBit && p.partialDestroyStartedBit &&
!p.strong),
"ripple::partialDestructorFinished : not a weak ref");
if (!p.weak)
{
// There was a weak count before the partial destructor ran (or we would
// have run the full destructor) and now there isn't a weak count. Some
// thread is waiting to run the destructor.
self.refCounts.notify_one();
}
// Set the pointer to null to emphasize that the object shouldn't be used
// after calling this function as it may be destroyed in another thread.
*o = nullptr;
}
//------------------------------------------------------------------------------
} // namespace ripple
#endif

View File

@@ -21,7 +21,6 @@
#define RIPPLE_BASICS_LOCALVALUE_H_INCLUDED
#include <boost/thread/tss.hpp>
#include <memory>
#include <unordered_map>

View File

@@ -22,11 +22,10 @@
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/beast/utility/Journal.h>
#include <boost/beast/core/string.hpp>
#include <boost/filesystem.hpp>
#include <fstream>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -167,6 +166,8 @@ private:
beast::severities::Severity thresh_;
File file_;
bool silent_ = false;
using Transform = std::function<std::string(std::string const&)>;
Transform transform_;
public:
Logs(beast::severities::Severity level);
@@ -205,6 +206,28 @@ public:
std::string const& text,
bool console);
/** Rewrite test log lines. Not synchronized.
*
* Test-only. Installed once for the life of Logs. Empty is a null check.
*/
void
setTransform(Transform fn)
{
transform_ = std::move(fn);
}
bool
hasTransform() const
{
return static_cast<bool>(transform_);
}
std::string
applyTransform(std::string const& text) const
{
return transform_ ? transform_(text) : text;
}
std::string
rotate();

View File

@@ -4,34 +4,37 @@ Utility functions and classes.
ripple/basic should contain no dependencies on other modules.
# Choosing a rippled container.
- `std::vector`
- For ordered containers with most insertions or erases at the end.
Choosing a rippled container.
=============================
- `std::deque`
- For ordered containers with most insertions or erases at the start or end.
* `std::vector`
* For ordered containers with most insertions or erases at the end.
- `std::list`
- For ordered containers with inserts and erases to the middle.
- For containers with iterators stable over insert and erase.
- Generally slower and bigger than `std::vector` or `std::deque` except for
* `std::deque`
* For ordered containers with most insertions or erases at the start or end.
* `std::list`
* For ordered containers with inserts and erases to the middle.
* For containers with iterators stable over insert and erase.
* Generally slower and bigger than `std::vector` or `std::deque` except for
those cases.
- `std::set`
- For sorted containers.
* `std::set`
* For sorted containers.
- `ripple::hash_set`
- Where inserts and contains need to be O(1).
- For "small" sets, `std::set` might be faster and smaller.
* `ripple::hash_set`
* Where inserts and contains need to be O(1).
* For "small" sets, `std::set` might be faster and smaller.
- `ripple::hardened_hash_set`
- For data sets where the key could be manipulated by an attacker
in an attempt to mount an algorithmic complexity attack: see
* `ripple::hardened_hash_set`
* For data sets where the key could be manipulated by an attacker
in an attempt to mount an algorithmic complexity attack: see
http://en.wikipedia.org/wiki/Algorithmic_complexity_attack
The following container is deprecated
- `std::unordered_set`
- Use `ripple::hash_set` instead, which uses a better hashing algorithm.
- Or use `ripple::hardened_hash_set` to prevent algorithmic complexity attacks.
* `std::unordered_set`
* Use `ripple::hash_set` instead, which uses a better hashing algorithm.
* Or use `ripple::hardened_hash_set` to prevent algorithmic complexity attacks.

View File

@@ -20,11 +20,11 @@
#ifndef RIPPLE_BASICS_RESOLVER_H_INCLUDED
#define RIPPLE_BASICS_RESOLVER_H_INCLUDED
#include <xrpl/beast/net/IPEndpoint.h>
#include <functional>
#include <vector>
#include <xrpl/beast/net/IPEndpoint.h>
namespace ripple {
class Resolver

View File

@@ -22,7 +22,6 @@
#include <xrpl/basics/Resolver.h>
#include <xrpl/beast/utility/Journal.h>
#include <boost/asio/io_service.hpp>
namespace ripple {

View File

@@ -1,135 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_BASICS_SHAREDWEAKCACHEPOINTER_H_INCLUDED
#define RIPPLE_BASICS_SHAREDWEAKCACHEPOINTER_H_INCLUDED
#include <memory>
#include <variant>
namespace ripple {
/** A combination of a std::shared_ptr and a std::weak_pointer.
This class is a wrapper to a `std::variant<std::shared_ptr,std::weak_ptr>`
This class is useful for storing intrusive pointers in tagged caches using less
memory than storing both pointers directly.
*/
template <class T>
class SharedWeakCachePointer
{
public:
SharedWeakCachePointer() = default;
SharedWeakCachePointer(SharedWeakCachePointer const& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer(std::shared_ptr<TT> const& rhs);
SharedWeakCachePointer(SharedWeakCachePointer&& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer(std::shared_ptr<TT>&& rhs);
SharedWeakCachePointer&
operator=(SharedWeakCachePointer const& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer&
operator=(std::shared_ptr<TT> const& rhs);
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer&
operator=(std::shared_ptr<TT>&& rhs);
~SharedWeakCachePointer();
/** Return a strong pointer if this is already a strong pointer (i.e. don't
lock the weak pointer. Use the `lock` method if that's what's needed)
*/
std::shared_ptr<T> const&
getStrong() const;
/** Return true if this is a strong pointer and the strong pointer is
seated.
*/
explicit
operator bool() const noexcept;
/** Set the pointer to null, decrement the appropriate ref count, and run
the appropriate release action.
*/
void
reset();
/** If this is a strong pointer, return the raw pointer. Otherwise return
null.
*/
T*
get() const;
/** If this is a strong pointer, return the strong count. Otherwise return 0
*/
std::size_t
use_count() const;
/** Return true if there is a non-zero strong count. */
bool
expired() const;
/** If this is a strong pointer, return the strong pointer. Otherwise
attempt to lock the weak pointer.
*/
std::shared_ptr<T>
lock() const;
/** Return true is this represents a strong pointer. */
bool
isStrong() const;
/** Return true is this represents a weak pointer. */
bool
isWeak() const;
/** If this is a weak pointer, attempt to convert it to a strong pointer.
@return true if successfully converted to a strong pointer (or was
already a strong pointer). Otherwise false.
*/
bool
convertToStrong();
/** If this is a strong pointer, attempt to convert it to a weak pointer.
@return false if the pointer is null. Otherwise return true.
*/
bool
convertToWeak();
private:
std::variant<std::shared_ptr<T>, std::weak_ptr<T>> combo_;
};
} // namespace ripple
#endif

View File

@@ -1,192 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_BASICS_SHAREDWEAKCACHEPOINTER_IPP_INCLUDED
#define RIPPLE_BASICS_SHAREDWEAKCACHEPOINTER_IPP_INCLUDED
#include <xrpl/basics/SharedWeakCachePointer.h>
namespace ripple {
template <class T>
SharedWeakCachePointer<T>::SharedWeakCachePointer(
SharedWeakCachePointer const& rhs) = default;
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer<T>::SharedWeakCachePointer(
std::shared_ptr<TT> const& rhs)
: combo_{rhs}
{
}
template <class T>
SharedWeakCachePointer<T>::SharedWeakCachePointer(
SharedWeakCachePointer&& rhs) = default;
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer<T>::SharedWeakCachePointer(std::shared_ptr<TT>&& rhs)
: combo_{std::move(rhs)}
{
}
template <class T>
SharedWeakCachePointer<T>&
SharedWeakCachePointer<T>::operator=(SharedWeakCachePointer const& rhs) =
default;
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer<T>&
SharedWeakCachePointer<T>::operator=(std::shared_ptr<TT> const& rhs)
{
combo_ = rhs;
return *this;
}
template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedWeakCachePointer<T>&
SharedWeakCachePointer<T>::operator=(std::shared_ptr<TT>&& rhs)
{
combo_ = std::move(rhs);
return *this;
}
template <class T>
SharedWeakCachePointer<T>::~SharedWeakCachePointer() = default;
// Return a strong pointer if this is already a strong pointer (i.e. don't
// lock the weak pointer. Use the `lock` method if that's what's needed)
template <class T>
std::shared_ptr<T> const&
SharedWeakCachePointer<T>::getStrong() const
{
static std::shared_ptr<T> const empty;
if (auto p = std::get_if<std::shared_ptr<T>>(&combo_))
return *p;
return empty;
}
template <class T>
SharedWeakCachePointer<T>::operator bool() const noexcept
{
return !!std::get_if<std::shared_ptr<T>>(&combo_);
}
template <class T>
void
SharedWeakCachePointer<T>::reset()
{
combo_ = std::shared_ptr<T>{};
}
template <class T>
T*
SharedWeakCachePointer<T>::get() const
{
return std::get_if<std::shared_ptr<T>>(&combo_).get();
}
template <class T>
std::size_t
SharedWeakCachePointer<T>::use_count() const
{
if (auto p = std::get_if<std::shared_ptr<T>>(&combo_))
return p->use_count();
return 0;
}
template <class T>
bool
SharedWeakCachePointer<T>::expired() const
{
if (auto p = std::get_if<std::weak_ptr<T>>(&combo_))
return p->expired();
return !std::get_if<std::shared_ptr<T>>(&combo_);
}
template <class T>
std::shared_ptr<T>
SharedWeakCachePointer<T>::lock() const
{
if (auto p = std::get_if<std::shared_ptr<T>>(&combo_))
return *p;
if (auto p = std::get_if<std::weak_ptr<T>>(&combo_))
return p->lock();
return {};
}
template <class T>
bool
SharedWeakCachePointer<T>::isStrong() const
{
if (auto p = std::get_if<std::shared_ptr<T>>(&combo_))
return !!p->get();
return false;
}
template <class T>
bool
SharedWeakCachePointer<T>::isWeak() const
{
return !isStrong();
}
template <class T>
bool
SharedWeakCachePointer<T>::convertToStrong()
{
if (isStrong())
return true;
if (auto p = std::get_if<std::weak_ptr<T>>(&combo_))
{
if (auto s = p->lock())
{
combo_ = std::move(s);
return true;
}
}
return false;
}
template <class T>
bool
SharedWeakCachePointer<T>::convertToWeak()
{
if (isWeak())
return true;
if (auto p = std::get_if<std::shared_ptr<T>>(&combo_))
{
combo_ = std::weak_ptr<T>(*p);
return true;
}
return false;
}
} // namespace ripple
#endif

View File

@@ -176,7 +176,7 @@ public:
@param count the number of items the slab allocator can allocate; note
that a count of 0 is valid and means that the allocator
is, effectively, disabled. This can be very useful in some
contexts (e.g. when minimal memory usage is needed) and
contexts (e.g. when mimimal memory usage is needed) and
allows for graceful failure.
*/
constexpr explicit SlabAllocator(

View File

@@ -23,7 +23,6 @@
#include <xrpl/basics/contract.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <algorithm>
#include <array>
#include <cstdint>

Some files were not shown because too many files have changed in this diff Show More