Rippled Audit

Build System

rippled uses CMake to build binaries from source. While we will quickly go over how CMake works and how build tasks are launched, it is suggested to read up on external CMake documentation for more details

CMake works by reading the CMakeLists.txt in the top level project directory, which contains commands defining the various build targets used by the application. It is here the various source code modules are listed for inclusion in the binary and commands are defined to perform the actual compilation process. A developer will typically checkout the rippled source code from github via git, optionally making modifications, before issuing the cmake command to process the CMakeLists.txt file and convert the sources to binary. CMake is also able to be run from various IDEs including VisualStudio which the rippled repository ships the necessary configuration for.

At the top of CMakeLists.txt we see

1
2
3
4
5
6
7
cmake_minimum_required (VERSION 3.9.0)
set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Builds/CMake ${CMAKE_MODULE_PATH})
include (CMakeFuncs)
include (CheckCXXCompilerFlag)
include (ExternalProject)
...
project (rippled)

This sets the base CMake version rippled depends on, includes the Builds/CMake directory in the include path and loads various base modules to define base commands, check dependencies, and load external dependencies. At the end of this block we see project (rippled) starting the actual project definition

The next section performs basic sanity checks and sets various vars depending on environment settings after which the version being build is read from Builds/CMake/RippleConfig.cmake

76
77
78
79
80
81
file (STRINGS src/ripple/protocol/impl/BuildInfo.cpp BUILD_INFO)
foreach (line_ ${BUILD_INFO})
  if (line_ MATCHES "versionString[ ]*=[ ]*\"(.+)\"")
    set (rippled_version ${CMAKE_MATCH_1})
  endif ()
endforeach ()

The next block contains the definition of options which may be set by the user to alter the behaviour of the compilation process when invoking CMake. After this we see the start of project wide compilation settings, where flags, paths, and other variables are configured based on the project layout and user-specified options (via the command line, environment, etc). Here we see calls to the add_library, link_library, and other macros which include and configure various rippled submodules in the compiled binaries

192
193
194
195
196
197
198
add_library (common INTERFACE)
add_library (Ripple::common ALIAS common)
# add a single global dependency on this interface lib
link_libraries (Ripple::common)
set_target_properties (common
PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON)
target_compile_features (common INTERFACE cxx_std_14)

We see platform specific settings after this including calls to target_compile_options and target_link_libraries to set various compiler flags and linker dependencies on the project.

From here it's rinse-and-repeat as we define other rippled subcomponents:

Next is the Boost configuration, Boost is one of the largest independent C++ libraries, consistent of many different but interoperable libraries used to satisfy a plethora of use cases in C++ applications. The next few sections define the configuration options used by rippled's boost integration.

453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
if ((NOT DEFINED BOOST_ROOT) AND (DEFINED ENV{BOOST_ROOT}))
  set (BOOST_ROOT $ENV{BOOST_ROOT})
endif ()
file (TO_CMAKE_PATH "${BOOST_ROOT}" BOOST_ROOT)
...
if (static)
  set (Boost_USE_STATIC_LIBS ON)
endif ()
set (Boost_USE_MULTITHREADED ON)
if (static AND NOT APPLE)
  set (Boost_USE_STATIC_RUNTIME ON)
else ()
  set (Boost_USE_STATIC_RUNTIME OFF)
endif ()
find_package (Boost 1.67 REQUIRED
  COMPONENTS
    chrono
    context
    coroutine
    date_time
    filesystem
    program_options
    regex
    serialization
    system
    thread)

add_library (ripple_boost INTERFACE)
add_library (Ripple::boost ALIAS ripple_boost)
...
target_link_libraries (ripple_boost
  INTERFACE
    Boost::boost
    Boost::chrono
    Boost::coroutine
    Boost::date_time
    Boost::filesystem
    Boost::program_options
    Boost::regex
    Boost::serialization
    Boost::system
Boost::thread)

After Boost, we see a similarly purposed section setting the options for OpenSSL configuration. OpenSSL is a widely used and standardized toolkit for cryptographic applications

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
if ((NOT DEFINED OPENSSL_ROOT) AND (DEFINED ENV{OPENSSL_ROOT}))
  set (OPENSSL_ROOT $ENV{OPENSSL_ROOT})
endif ()
file (TO_CMAKE_PATH "${OPENSSL_ROOT}" OPENSSL_ROOT)

if (static)
  set (OPENSSL_USE_STATIC_LIBS ON)
endif ()
set (OPENSSL_MSVC_STATIC_RT ON)
set (_ssl_min_ver 1.0.2)
...
find_package (OpenSSL ${_ssl_min_ver} REQUIRED)
target_link_libraries (ripple_libs
  INTERFACE
    OpenSSL::SSL
OpenSSL::Crypto)

After OpenSSL, we see configuration for various libraries shipped as vendored components in the rippled codebase. These include:

See the Directory Structure section of this document for more information on these libraries, each of which is built as static object in the compilation process, resulting in the binary logic being included directly in the rippled executable (as opposed to other non-vendored dependencies which are not shipped with the rippled binary and thus must be installed and present on the system which rippled is to be executed on)

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
add_library (secp256k1 STATIC
  src/secp256k1/src/secp256k1.c)

add_library (ed25519-donna STATIC
  src/ed25519-donna/ed25519.c)

add_library (lz4 STATIC
  src/lz4/lib/lz4.c
  src/lz4/lib/lz4hc.c
  src/lz4/lib/lz4frame.c
  src/lz4/lib/xxhash.c)

add_library (sqlite3 STATIC
  src/sqlite/sqlite/sqlite3.c)

add_library (soci STATIC
  src/ripple/unity/soci.cpp
  ${CMAKE_BINARY_DIR}/sqlite3/include/sqlite3.h)

add_library (snappy STATIC
  src/snappy/snappy/snappy.cc
  src/snappy/snappy/snappy-sinksource.cc
  src/snappy/snappy/snappy-stubs-internal.cc)

add_library (rocksdb STATIC
  src/ripple/unity/rocksdb.cpp)

add_library (nudb INTERFACE)

nudb being a header-only library is included as part of the build as an interface and thus is configured slightly different than the other static dependencies. Nestled inbetween the lz4 and sqlite3 static library configuration blocks is configuration for the libarchive dependency (not-vendored / dynamically loaded at runtime).

Next we see the start of the ripple-specific logic configuration, specifically the protobuf config which incorporates the vendored protobuf library along with the rippled interface definition. protobuf is used to serialize data in a language and platform agnostic way.

After protobuf, the XRPL Core module is configured for use by client libraries needing access to utilities and other constructs used by the internal rippled subsystems.

1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
add_library (xrpl_core
...
)
add_library (Ripple::xrpl_core ALIAS xrpl_core)
target_include_directories (xrpl_core
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/ripple>
    # this one is for beast/legacy files:
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/beast/extras>
    $<INSTALL_INTERFACE:include>)
target_compile_options (xrpl_core
  PUBLIC
    $<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized>)
target_link_libraries (xrpl_core
  PUBLIC
    OpenSSL::Crypto
    NIH::secp256k1
    NIH::ed25519-donna
    Ripple::syslibs
    Ripple::boost
    Ripple::opts)

After the xrpl_core library definition we finally get to the main header installation, facilitating the inclusion of headers files defining public data structures and methods in the build output directory

1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
install (
  FILES
    src/ripple/basics/Blob.h
    ...
  DESTINATION include/ripple/basics)
install (
  FILES
    src/ripple/crypto/GenerateDeterministicKey.h
    ...
  DESTINATION include/ripple/crypto)
install (
  FILES
    src/ripple/crypto/impl/ec_key.h
    ...
  DESTINATION include/ripple/crypto/impl)
install (
  FILES
    src/ripple/json/JsonPropertyStream.h
    ...
  DESTINATION include/ripple/json)
install (
  FILES
    src/ripple/json/impl/json_assert.h
  DESTINATION include/ripple/json/impl)
install (
  FILES
    src/ripple/protocol/AccountID.h
    ...
  DESTINATION include/ripple/protocol)
install (
  FILES
    src/ripple/protocol/impl/STVar.h
    ...
  DESTINATION include/ripple/protocol/impl)

This is followed by the main rippled executable defnition and listing of source components:

1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
add_executable (rippled src/ripple/app/main/Application.h)
target_sources (rippled PRIVATE
... ~650 lines of source files !!!
)
target_link_libraries (rippled
  Ripple::opts
  Ripple::libs
  Ripple::boost
  Ripple::xrpl_core)
exclude_if_included (rippled)

These are the instructions to build the actual rippled command which runs the XRP service on nodes on the network. This executable is the principle target of the build process, incorporating the core and ansillary rippled logic to run the ledger.

Towards the end of CMakeLists.txt we see the directives which actually install the built binaries in the various output directories specified by the build system:

2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
install (
  TARGETS
    secp256k1
    ed25519-donna
    common
    opts
    ripple_syslibs
    ripple_boost
    xrpl_core
  EXPORT RippleExports
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include)
install (EXPORT RippleExports
  FILE RippleTargets.cmake
  NAMESPACE Ripple::
  DESTINATION lib/cmake/ripple)
include (CMakePackageConfigHelpers)
write_basic_package_version_file (
  RippleConfigVersion.cmake
  VERSION ${rippled_version}
  COMPATIBILITY SameMajorVersion)

if (is_root_project)
  install (TARGETS rippled RUNTIME DESTINATION bin)
  set_target_properties(rippled PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON)
  install (
    FILES
      ${CMAKE_CURRENT_SOURCE_DIR}/Builds/CMake/RippleConfig.cmake
      ${CMAKE_CURRENT_BINARY_DIR}/RippleConfigVersion.cmake
DESTINATION lib/cmake/ripple)
  ...
endif ()

Finally we see build instructions for CMake Multi-Configuration Generators and Doxygen based documentation.

2110
2111
2112
2113
2114
2115
2116
2117
2118
find_package (Doxygen)
if (TARGET Doxygen::doxygen)
  set (doc_srcs docs/source.dox)
  file (GLOB_RECURSE other_docs docs/*.md)
  list (APPEND doc_srcs "${other_docs}")
  ...
else ()
  message (STATUS "doxygen executable not found -- skipping docs target")
endif ()

Building

When the cmake command is run in the rippled project directory, it will automatically locate and load the CMakeLists.txt file, processing it to deduce the necessary operations needed to run to build the sources as described and output the binaries, headers, and documentation. Assuming that the invoker has all the necessary dependencies installed on their system (see build instructions on the ripple.com website) CMake will automatically compile all the necessary logic to build the libraries and executable described above and install them to the specified directories. The rippled command will then be available to run via the local filesystem.