-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat: Add code generator for transactions and ledger entries #6443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
a1q123456
wants to merge
2
commits into
develop
Choose a base branch
from
a1q123456/add-code-generator
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,995
−2
Draft
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| #[===================================================================[ | ||
| Protocol Autogen - Code generation for protocol wrapper classes | ||
| #]===================================================================] | ||
|
|
||
| # Function to set up code generation for protocol_autogen module | ||
| # This runs at configure time to generate C++ wrapper classes from macro files | ||
| function (setup_protocol_autogen) | ||
| # Directory paths | ||
| set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol/detail") | ||
| set(AUTOGEN_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol_autogen") | ||
| set(SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts") | ||
|
|
||
| # Input macro files | ||
| set(TRANSACTIONS_MACRO "${MACRO_DIR}/transactions.macro") | ||
| set(LEDGER_ENTRIES_MACRO "${MACRO_DIR}/ledger_entries.macro") | ||
| set(SFIELDS_MACRO "${MACRO_DIR}/sfields.macro") | ||
|
|
||
| # Python scripts | ||
| set(GENERATE_TX_SCRIPT "${SCRIPTS_DIR}/generate_tx_classes.py") | ||
| set(GENERATE_LEDGER_SCRIPT "${SCRIPTS_DIR}/generate_ledger_classes.py") | ||
| set(REQUIREMENTS_FILE "${SCRIPTS_DIR}/requirements.txt") | ||
|
|
||
| # Create output directories | ||
| file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/transactions") | ||
| file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/ledger_objects") | ||
|
|
||
| # Find Python3 - check if already found by Conan or find it ourselves | ||
| if (NOT Python3_EXECUTABLE) | ||
| find_package(Python3 COMPONENTS Interpreter QUIET) | ||
| endif () | ||
|
|
||
| if (NOT Python3_EXECUTABLE) | ||
| # Try finding python3 executable directly | ||
| find_program(Python3_EXECUTABLE NAMES python3 python) | ||
| endif () | ||
|
|
||
| if (NOT Python3_EXECUTABLE) | ||
| message(FATAL_ERROR "Python3 not found. Code generation cannot proceed.") | ||
| return() | ||
| endif () | ||
|
|
||
| message(STATUS "Using Python3 for code generation: ${Python3_EXECUTABLE}") | ||
|
|
||
| # Set up Python virtual environment for code generation | ||
| set(VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv") | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good to have: may be worth making it a build option |
||
|
|
||
| # Determine the Python executable path in the venv | ||
| if (WIN32) | ||
| set(VENV_PYTHON "${VENV_DIR}/Scripts/python.exe") | ||
| set(VENV_PIP "${VENV_DIR}/Scripts/pip.exe") | ||
| else () | ||
| set(VENV_PYTHON "${VENV_DIR}/bin/python") | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if it builds if there's no network connection |
||
| set(VENV_PIP "${VENV_DIR}/bin/pip") | ||
| endif () | ||
|
|
||
| # Check if venv needs to be created or updated | ||
| set(VENV_NEEDS_UPDATE FALSE) | ||
| if (NOT EXISTS "${VENV_PYTHON}") | ||
| set(VENV_NEEDS_UPDATE TRUE) | ||
| message(STATUS "Creating Python virtual environment for code generation...") | ||
| elseif ("${REQUIREMENTS_FILE}" IS_NEWER_THAN "${VENV_DIR}/.requirements_installed") | ||
| set(VENV_NEEDS_UPDATE TRUE) | ||
| message(STATUS "Updating Python virtual environment (requirements changed)...") | ||
| endif () | ||
|
|
||
| # Create/update virtual environment if needed | ||
| if (VENV_NEEDS_UPDATE) | ||
| message(STATUS "Setting up Python virtual environment at ${VENV_DIR}") | ||
| execute_process(COMMAND ${Python3_EXECUTABLE} -m venv "${VENV_DIR}" | ||
| RESULT_VARIABLE VENV_RESULT ERROR_VARIABLE VENV_ERROR) | ||
| if (NOT VENV_RESULT EQUAL 0) | ||
| message(FATAL_ERROR "Failed to create virtual environment: ${VENV_ERROR}") | ||
| endif () | ||
|
|
||
| message(STATUS "Installing Python dependencies...") | ||
| execute_process(COMMAND ${VENV_PIP} install --upgrade pip RESULT_VARIABLE PIP_UPGRADE_RESULT | ||
| OUTPUT_QUIET ERROR_VARIABLE PIP_UPGRADE_ERROR) | ||
| if (NOT PIP_UPGRADE_RESULT EQUAL 0) | ||
| message(WARNING "Failed to upgrade pip: ${PIP_UPGRADE_ERROR}") | ||
| endif () | ||
|
|
||
| execute_process(COMMAND ${VENV_PIP} install -r "${REQUIREMENTS_FILE}" | ||
| RESULT_VARIABLE PIP_INSTALL_RESULT ERROR_VARIABLE PIP_INSTALL_ERROR) | ||
| if (NOT PIP_INSTALL_RESULT EQUAL 0) | ||
| message(FATAL_ERROR "Failed to install Python dependencies: ${PIP_INSTALL_ERROR}") | ||
| endif () | ||
|
|
||
| # Mark requirements as installed | ||
| file(TOUCH "${VENV_DIR}/.requirements_installed") | ||
| message(STATUS "Python virtual environment ready") | ||
| endif () | ||
|
|
||
| # Generate transaction classes at configure time | ||
| message(STATUS "Generating transaction classes from transactions.macro...") | ||
| execute_process(COMMAND ${VENV_PYTHON} "${GENERATE_TX_SCRIPT}" "${TRANSACTIONS_MACRO}" | ||
| --header-dir "${AUTOGEN_HEADER_DIR}/transactions" --sfields-macro | ||
| "${SFIELDS_MACRO}" | ||
| WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" | ||
| RESULT_VARIABLE TX_GEN_RESULT | ||
| OUTPUT_VARIABLE TX_GEN_OUTPUT | ||
| ERROR_VARIABLE TX_GEN_ERROR) | ||
| if (NOT TX_GEN_RESULT EQUAL 0) | ||
| message(FATAL_ERROR "Failed to generate transaction classes:\n${TX_GEN_ERROR}") | ||
| else () | ||
| message(STATUS "Transaction classes generated successfully") | ||
| endif () | ||
|
|
||
| # Generate ledger entry classes at configure time | ||
| message(STATUS "Generating ledger entry classes from ledger_entries.macro...") | ||
| execute_process(COMMAND ${VENV_PYTHON} "${GENERATE_LEDGER_SCRIPT}" "${LEDGER_ENTRIES_MACRO}" | ||
| --header-dir "${AUTOGEN_HEADER_DIR}/ledger_objects" --sfields-macro | ||
| "${SFIELDS_MACRO}" | ||
| WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" | ||
| RESULT_VARIABLE LEDGER_GEN_RESULT | ||
| OUTPUT_VARIABLE LEDGER_GEN_OUTPUT | ||
| ERROR_VARIABLE LEDGER_GEN_ERROR) | ||
| if (NOT LEDGER_GEN_RESULT EQUAL 0) | ||
| message(FATAL_ERROR "Failed to generate ledger entry classes:\n${LEDGER_GEN_ERROR}") | ||
| else () | ||
| message(STATUS "Ledger entry classes generated successfully") | ||
| endif () | ||
|
|
||
| # Add the generated header directory to the module's include path | ||
| target_include_directories( | ||
| xrpl.libxrpl.protocol_autogen PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> | ||
| $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) | ||
|
|
||
| # Install generated headers | ||
| install(DIRECTORY "${AUTOGEN_HEADER_DIR}/" | ||
| DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/xrpl/protocol_autogen" FILES_MATCHING | ||
| PATTERN "*.h") | ||
| endfunction () | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| #pragma once | ||
|
|
||
| #include <xrpl/protocol/LedgerFormats.h> | ||
| #include <xrpl/protocol/SField.h> | ||
| #include <xrpl/protocol/STLedgerEntry.h> | ||
|
|
||
| #include <optional> | ||
| #include <string> | ||
|
|
||
| namespace xrpl::ledger_entries { | ||
|
|
||
| /** | ||
| * @brief Base class for type-safe ledger entry wrappers. | ||
| * | ||
| * This class provides common functionality for all ledger entry types, | ||
| * including access to common fields (sfLedgerIndex, sfLedgerEntryType, sfFlags). | ||
| * | ||
| * This is an immutable wrapper around SLE (Serialized Ledger Entry). | ||
| * Use the corresponding Builder classes to construct new ledger entries. | ||
| */ | ||
| class LedgerEntryBase | ||
| { | ||
| public: | ||
| /** | ||
| * @brief Construct a ledger entry wrapper from an existing SLE object. | ||
| * @param sle The underlying serialized ledger entry to wrap | ||
| */ | ||
| explicit LedgerEntryBase(SLE const& sle) : sle_(sle) | ||
| { | ||
| } | ||
|
|
||
| /** | ||
| * @brief Get the ledger entry type. | ||
| * @return The type of this ledger entry | ||
| */ | ||
| [[nodiscard]] | ||
| LedgerEntryType | ||
| getType() const | ||
| { | ||
| return sle_.getType(); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Get the key (index) of this ledger entry. | ||
| * | ||
| * The key uniquely identifies this ledger entry in the ledger state. | ||
| * @return A constant reference to the 256-bit key | ||
| */ | ||
| [[nodiscard]] | ||
| uint256 const& | ||
| getKey() const | ||
| { | ||
| return sle_.key(); | ||
| } | ||
|
|
||
| // Common field getters (from LedgerFormats.cpp commonFields) | ||
|
|
||
| /** | ||
| * @brief Get the ledger index (sfLedgerIndex). | ||
| * | ||
| * This field is OPTIONAL and represents the index of the ledger entry. | ||
| * @return The ledger index if present, std::nullopt otherwise | ||
| */ | ||
| [[nodiscard]] | ||
| std::optional<uint256> | ||
| getLedgerIndex() const | ||
| { | ||
| if (sle_.isFieldPresent(sfLedgerIndex)) | ||
| { | ||
| return sle_.at(sfLedgerIndex); | ||
| } | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Check if the ledger entry has a ledger index. | ||
| * @return true if sfLedgerIndex is present, false otherwise | ||
| */ | ||
| [[nodiscard]] | ||
| bool | ||
| hasLedgerIndex() const | ||
| { | ||
| return sle_.isFieldPresent(sfLedgerIndex); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Get the ledger entry type field (sfLedgerEntryType). | ||
| * | ||
| * This field is REQUIRED for all ledger entries and indicates the type | ||
| * of the ledger entry (e.g., AccountRoot, RippleState, Offer, etc.). | ||
| * @return The ledger entry type as a 16-bit unsigned integer | ||
| */ | ||
| [[nodiscard]] | ||
| uint16_t | ||
| getLedgerEntryType() const | ||
| { | ||
| return sle_.at(sfLedgerEntryType); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Get the flags field (sfFlags). | ||
| * | ||
| * This field is REQUIRED for all ledger entries and contains | ||
| * type-specific flags that modify the behavior of the ledger entry. | ||
| * @return The flags value as a 32-bit unsigned integer | ||
| */ | ||
| [[nodiscard]] | ||
| std::uint32_t | ||
| getFlags() const | ||
| { | ||
| return sle_.at(sfFlags); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Get the underlying SLE object. | ||
| * | ||
| * Provides direct access to the wrapped serialized ledger entry object | ||
| * for cases where the type-safe accessors are insufficient. | ||
| * @return A constant reference to the underlying SLE object | ||
| */ | ||
| [[nodiscard]] | ||
| SLE const& | ||
| getSle() const | ||
| { | ||
| return sle_; | ||
| } | ||
|
|
||
| protected: | ||
| /** @brief The underlying serialized ledger entry being wrapped. */ | ||
| SLE const& sle_; | ||
| }; | ||
|
|
||
| } // namespace xrpl::ledger_entries |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #pragma once | ||
|
|
||
| #include <xrpl/json/json_value.h> | ||
| #include <xrpl/protocol/SField.h> | ||
| #include <xrpl/protocol/STLedgerEntry.h> | ||
| #include <xrpl/protocol/STObject.h> | ||
|
|
||
| namespace xrpl::ledger_entries { | ||
|
|
||
| /** | ||
| * Base class for all ledger entry builders. | ||
| * Provides common field setters that are available for all ledger entry types. | ||
| */ | ||
| template <typename Derived> | ||
| class LedgerEntryBuilderBase | ||
| { | ||
| public: | ||
| /** | ||
| * Set the ledger index. | ||
| * @param value Ledger index | ||
| * @return Reference to the derived builder for method chaining. | ||
| */ | ||
| Derived& | ||
| setLedgerIndex(uint256 const& value) | ||
| { | ||
| object_[sfLedgerIndex] = value; | ||
| return static_cast<Derived&>(*this); | ||
| } | ||
|
|
||
| /** | ||
| * Set the flags. | ||
| * @param value Flags value | ||
| * @return Reference to the derived builder for method chaining. | ||
| */ | ||
| Derived& | ||
| setFlags(uint32_t value) | ||
| { | ||
| object_.setFieldU32(sfFlags, value); | ||
| return static_cast<Derived&>(*this); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Factory method to create a new instance of the derived builder. | ||
| * | ||
| * Creates a default-constructed builder instance. It is recommended to use | ||
| * this factory method instead of directly constructing the derived type to | ||
| * avoid creating unnecessary temporary objects. | ||
| * @return A new instance of the derived builder type | ||
| */ | ||
| static Derived | ||
| create() | ||
| { | ||
| return Derived{}; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Factory method to create an instance of the derived builder from an existing SLE. | ||
| * | ||
| * Creates a builder instance initialized with data from an existing serialized | ||
| * ledger entry. It is recommended to use this factory method instead of directly | ||
| * constructing the derived type to avoid creating unnecessary temporary objects. | ||
| * @param sle The existing serialized ledger entry to initialize from | ||
| * @return A new instance of the derived builder type initialized with the SLE data | ||
| */ | ||
| static Derived | ||
| create(SLE const& sle) | ||
| { | ||
| return Derived{sle}; | ||
| } | ||
|
|
||
| protected: | ||
| STObject object_{sfLedgerEntry}; | ||
| }; | ||
|
|
||
| } // namespace xrpl::ledger_entries |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be a good idea to check those files in