Files
rippled/.github/scripts/levelization
Pratik Mankawde bec9e1c8a9 fix(telemetry): resolve the node identity before the Application is built
resolveNodePublicKey() returned std::nullopt in three real cases: a first boot
with no wallet database, a standalone run (its wallet is a private temporary
database), and --newnodeid. Telemetry's resources are built during
ApplicationImp's member-init list and are immutable, so on those runs the node
reported an empty service.instance.id and no xrpl.node.id for the whole run,
while setup() minted a key moments later and patched only the tracer.

Replace it with resolveNodeIdentity(), which always returns a keypair: derived
from a configured seed, else read from an existing wallet database, else
minted. Main.cpp passes that pair to makeApplication(), ApplicationImp stores
it in nodeIdentity_ -- now declared before telemetry_ and no longer an optional,
because it is always set -- and builds the telemetry resource from it.

setup() calls getNodeIdentity(), which now persists rather than mints: it
stores the resolved pair when the wallet holds no identity, adopts the stored
one when it does, and clears first for --newnodeid. The write stays in setup()
because that is where the database exists; a standalone run has no persistent
wallet to write to, which is why the pair has to be decided before
construction rather than read back afterwards. Wallet gains storeNodeIdentity()
for that write, and getNodeIdentity(session) now uses it instead of repeating
the insert.

The three-argument makeApplication() mints a keypair, so jtx::Env and any other
test Application behave as a standalone run always did.

Also fold the three hand-rolled "meter from a NoopMeterProvider" copies into
telemetry::noopMeter(): the base-pointer call and the kMeterVersion argument are
both easy to get wrong alone, and the meter identity has to match the one the
histogram views select on.

The new gtest covers the wallet half: store-then-read, store not replacing an
existing identity, clear-then-store, and that the mint path persists. It adds
the tests.libxrpl > xrpl.rdb levelization edge, regenerated here.
2026-09-14 20:34:31 +01:00
..

Levelization

Levelization is the term used to describe efforts to prevent xrpld from having or creating cyclic dependencies.

xrpld code is organized into directories under src/xrpld, src/libxrpl (and src/test) representing modules. The modules are intended to be organized into "tiers" or "levels" such that a module from one level can only include code from lower levels. Additionally, a module in one level should never include code in an impl or detail folder of any level other than its own.

The codebase is split into two main areas:

  • libxrpl (src/libxrpl, include/xrpl): Reusable library modules with public interfaces
  • xrpld (src/xrpld): Application-specific implementation code

Unfortunately, over time, enforcement of levelization has been inconsistent, so the current state of the code doesn't necessarily reflect these rules. Whenever possible, developers should refactor any levelization violations they find (by moving files or individual classes). At the very least, don't make things worse.

The table below summarizes the desired division of modules, based on the current state of the xrpld code. The levels are numbered from the bottom up with the lower level, lower numbered, more independent modules listed first, and the higher level, higher numbered modules with more dependencies listed later.

tl;dr: The modules listed first are more independent than the modules listed later.

libxrpl Modules (Reusable Libraries)

Level / Tier Module(s)
01 xrpl/beast
02 xrpl/basics
03 xrpl/json xrpl/crypto
04 xrpl/protocol
05 xrpl/core xrpl/resource xrpl/server
06 xrpl/ledger xrpl/nodestore xrpl/net
07 xrpl/shamap xrpl/consensus

xrpld Modules (Application Implementation)

Level / Tier Module(s)
05 xrpld/conditions
06 xrpld/core xrpld/peerfinder
07 xrpld/shamap xrpld/overlay
08 xrpld/app
09 xrpld/rpc
10 xrpld/perflog

Test Modules

Level / Tier Module(s)
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 strictly enforced than xrpl/xrpld levelization, other than the requirement that test code should never be included in xrpl or xrpld code.)

Validation

The levelization script takes no parameters, reads no environment variables, and can be run from any directory, as long as it is in the expected location in the xrpld repo. It can be run at any time from within a checked out repo, and will do an analysis of all the #includes in the xrpld 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:

  • rawincludes.txt: The raw dump of the #includes
  • paths.txt: A second dump grouping the source module to the destination module, de-duped, and with frequency counts.
  • includes/: A directory where each file represents a module and contains a list of modules and counts that the module includes.
  • included_by/: 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: 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 to validate that nothing changed.
  • 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 to validate that nothing changed.
  • levelization.yml Github Actions workflow to test that levelization loops haven't 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 generate.py, and commit the updated results.

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, 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.txtonly 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 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 times, so the script has no clue which should be higher. These results will only be included in loops.txt.

The committed files hide the detailed values intentionally, to prevent false alarms and merging issues, and because it's easy to get those details locally.

  1. Run generate.py
  2. Grep the modules in paths.txt.
    • For example, if a cycle is found A ~= B, simply grep -w A .github/scripts/levelization/results/paths.txt | grep -w B