Amendment Architecture

← Back to Building Amendments: Lifecycle and Core Protocol Impact


Introduction

The amendment system is one of the most sophisticated architectural components of rippled. It allows the XRPL network to evolve in a decentralized manner by introducing new features and bug fixes while maintaining network consistency. Unlike traditional blockchain systems that require coordinated hard forks, XRPL uses a distributed consensus mechanism to activate protocol changes.

In this section, we will explore the internal architecture of the amendment system: the fundamental components, data structures, and integration with the consensus engine. We will see how rippled manages the list of amendments, tracks their state, and coordinates their activation across the network.


Amendment System Overview

The amendment system in XRPL is based on several key principles:

Distributed consensus: Amendments can only be activated if more than 80% of the UNL validators vote in their favor for a minimum period of two weeks. This ensures that no single entity can impose changes on the network.

Stability period: The two-week period after the 80% threshold is exceeded allows node operators to prepare for upcoming changes. This critical window provides the necessary time to update software, test integrations, and coordinate.

Automatic activation: Once consensus is reached and the stability period has elapsed, the amendment is automatically activated via an EnableAmendment pseudo-transaction injected into the ledger by the network itself.

Network protection: If a node does not support an activated amendment, it automatically enters "amendment blocked" mode and stops participating in consensus until it is updated. This protects network integrity by preventing an outdated node from applying incorrect rules.


AmendmentTable: The Central Manager

AmendmentTable Interface

The AmendmentTable class is the public interface of the amendment system. It is defined in src/xrpld/app/misc/AmendmentTable.h and provides essential methods for:

Amendment management:

  • find(uint256 const& amendment): Search for an amendment by its hash

  • enable(uint256 const& amendment): Activate an amendment locally

  • isEnabled(uint256 const& amendment): Check if an amendment is enabled

  • isSupported(uint256 const& amendment): Check if the node supports an amendment

Voting and validation:

  • doVoting(NetClock::time_point closeTime, ...): Execute voting logic for a consensus round

  • doValidation(std::set<uint256> const& enabledAmendments): Determine which amendments to announce in validations

  • getDesired(): Return all amendments that the node wishes to see enabled

Ledger synchronization:

  • doValidatedLedger(std::shared_ptr<ReadView const> const& ledger): Synchronize amendment state after ledger validation

  • hasUnsupportedEnabled(): Check if unsupported amendments are enabled

  • firstUnsupportedExpected(): Return when the first unsupported amendment is expected to activate

Monitoring and inspection:

  • getJson(): Generate a JSON representation of all amendment states

  • trustChanged(hash_set<PublicKey> const& added, ...): Notify changes in the trusted validator list

AmendmentTableImpl: The Implementation

The concrete implementation AmendmentTableImpl (in src/xrpld/app/misc/detail/AmendmentTable.cpp) manages the complete internal state of the amendment system.

Key data structures:

Thread safety: All operations on amendment state are protected by a mutex. The amendment system can be queried from multiple threads simultaneously (network thread, consensus thread, RPC thread), so synchronization is critical.


AmendmentState: The State of an Amendment

Each amendment in the system is represented by an AmendmentState structure that captures its complete state:

AmendmentVote: Vote Types

The AmendmentVote type represents the local node's position:

  • up: The node votes to activate the amendment

  • down: The node votes against the amendment (or has no opinion)

  • obsolete: The amendment is marked as obsolete and should no longer be voted on

Enabled vs Supported

It is crucial to understand the distinction between these two states:

enabled: The amendment is activated on the network (present in the ledger in the sfAmendments field). All nodes must apply the rules of this amendment, whether they support it or not.

supported: The local node has the necessary code to apply the rules of this amendment. If a node does not support an enabled amendment, it will block (amendment blocked).


Amendment Registry: features.macro

All known amendments are defined in the macro file include/xrpl/protocol/detail/features.macro. This file uses preprocessor macros to automatically generate the necessary code.

Declaration Format

Amendment Categories

Supported Amendments: Amendments for which the node has implementation code. They are listed in features.macro with Supported::yes.

Obsolete Amendments: Historical amendments that are no longer relevant but must remain in the code in case they are activated. Marked with VoteBehavior::Obsolete.

Retired Amendments: Amendments that have been active for at least two years, whose pre-amendment code has been removed, and whose identifiers are deprecated. Marked with XRPL_RETIRE.

Registration Process

To register a new amendment in the system:

  1. Add the entry in features.macro with the appropriate macro (XRPL_FEATURE or XRPL_FIX)

  2. Increment numFeatures in include/xrpl/protocol/Feature.h

  3. The amendment will be automatically included in the feature registry and available for voting


FeatureInfo: Amendment Metadata

The FeatureInfo structure encapsulates amendment metadata:

Amendment Hash Calculation

The hash of an amendment is calculated by taking the SHA-512Half of the amendment name. For example:

This hash is used as a unique identifier throughout the system: in validations, in EnableAmendment transactions, and in the ledger's sfAmendments field.


Integration with Consensus

The amendment system is tightly integrated with XRPL's consensus engine. This integration occurs at several levels:

Validation Phase

When a validator creates a validation for a ledger, it includes the list of amendments it supports and wishes to see activated. This list is generated by doValidation() and included in the validation message broadcast to the network.

Vote Collection

During each consensus round, the validations received from other validators are analyzed to extract their amendment votes. These votes are aggregated into the TrustedVotes structure which maintains:

  • The total number of trusted validations received

  • The number of votes for each specific amendment

  • A timeout mechanism to expire old votes

Threshold Calculation

The system calculates whether an amendment has exceeded the required threshold (more than 80% of UNL validators) using the AmendmentSet class which:

  1. Aggregates all collected votes

  2. Calculates the threshold based on the number of trusted validators

  3. Determines which amendments pass the threshold

  4. Generates the necessary actions (tfGotMajority, tfLostMajority, or activation)

Application to Ledger

Amendment decisions (got majority, lost majority, or enable) are applied to the ledger via pseudo-transactions that modify the sfMajorities and sfAmendments fields of the global ledger object.


Database Persistence

The amendment system persists critical data in the node's database.

FeatureVotes Table

The FeatureVotes table stores the local node's votes:

Fields:

  • Hash: The amendment hash (in hexadecimal)

  • Name: The human-readable name of the amendment

  • Vote: The vote value (0=down, 1=up, 2=obsolete)

  • VoteTime: Timestamp of the vote (for history)

Persistence Operations

persistVote(): Records the current vote of an amendment in the database. This function is called when an administrator changes the vote via RPC or when the node initializes its votes at startup.

readAmendments(): Reads previously recorded votes from the database at node startup. Uses a SQL window function to retrieve only the most recent vote for each amendment.


Special Cases and Edge Cases

Low Number of Validators

If the network has very few UNL validators, the threshold calculation ensures that at least 1 vote is required. The threshold is calculated as:

This avoids situations where a single validator could activate an amendment in a test network with a single validator.

Network Partition

In case of network partition, amendments may not reach the required threshold if validators are divided. The normal consensus process handles these cases by not activating the amendment until consensus is restored.

Standalone Mode

In standalone mode (a single node without network), the node can still execute voting logic, but since there are no other validators, no amendment will ever be activated by consensus. Amendments can be manually enabled via admin commands for testing.


Source Code References

The amendment system components are distributed across multiple files:

Interface and implementation:

  • src/xrpld/app/misc/AmendmentTable.h - Public interface

  • src/xrpld/app/misc/detail/AmendmentTable.cpp - Complete implementation

Feature registry:

  • include/xrpl/protocol/detail/features.macro - Definitions of all amendments

  • include/xrpl/protocol/Feature.h - Declarations and constants

  • src/libxrpl/protocol/Feature.cpp - Code generated from macros

Persistence:

  • src/xrpld/app/rdb/detail/Wallet.cpp - Functions voteAmendment() and readAmendments()

  • src/xrpld/app/rdb/Wallet.h - Declarations of persistence functions

Consensus integration:

  • src/xrpld/app/consensus/RCLConsensus.cpp - Integration with consensus

  • src/xrpld/consensus/Consensus.h - Generic consensus engine

Last updated