Homework 2: Negative UNL Engineering
← Back to Consensus II: UNLs and Ledger Amendments
Objective
This homework provides hands-on experience with XRPL's Negative UNL (Universal Node List) mechanism. You will create test scenarios that disable and re-enable validators, observing how the network maintains consensus despite validator failures.
Format: Written report (PDF or Markdown) with test code, execution output, and analysis.
Background
The Negative UNL mechanism allows the network to temporarily disable poorly performing validators without manual intervention:
Key Concepts:
Flag Ledgers: Updates occur every 256 ledgers
ToDisable: Validators marked for disabling via
ttUNL_MODIFYtransactionsToReEnable: Disabled validators marked for re-enabling when recovered
Scoring: Validators scored based on validation participation using
validationFRESHNESSThresholds: Low/high watermarks determine disable/re-enable actions
Transaction Types:
ttUNL_MODIFY: Special transaction type for UNL modifications
Only one ToDisable per flag ledger
Only one ToReEnable per flag ledger
Transactions processed automatically at flag ledgers
Task
Create a comprehensive test function that simulates the complete validator lifecycle: disable, operation while disabled, and re-enable.
Requirements
Test File Setup
Navigate to
rippled/src/test/app/Open
LedgerMaster_test.cppor create new test fileLocate the
testNegativeUNL()function for reference (~line 100+)Understand the test helper functions
Phase 1: Initial Setup (15 minutes)
Create test function
testValidatorLifecycle()Create 5 validator public keys
Initialize genesis ledger with
featureNegativeUNLenabledCreate tracking structure:
hash_map<PublicKey, std::uint32_t> nUnlLedgerSeqAdd logging to track progress
Phase 2: Flag Ledger Progression (20 minutes)
Build ledgers until first flag ledger (sequence 256)
Verify
l->isFlagLedger()returns trueCall
l->updateNegativeUNL()on flag ledgerVerify initial state: empty nUNL, no ToDisable, no ToReEnable
Use
negUnlSizeTest()helper for verification
Phase 3: Validator Disabling (25 minutes)
Create
ttUNL_MODIFYtransaction for validator 0 (ToDisable)Create second disable transaction for validator 1 (should fail)
Create re-enable transaction for validator 0 (should fail - not in nUNL yet)
Apply transactions using
OpenViewVerify only first disable succeeds
Verify ToDisable field is set
Phase 4: Process Disable (20 minutes)
Progress 256 ledgers to next flag ledger
Verify ToDisable persists during progression
Call
updateNegativeUNL()on new flag ledgerVerify validator 0 is now in nUNL
Verify ToDisable field is cleared
Record validator 0's disable ledger sequence
Phase 5: Complex Scenarios (20 minutes)
Test disabling already-disabled validator (should fail)
Test disabling new validator 2 (should succeed)
Test re-enabling validator not in nUNL (should fail)
Test re-enabling same validator as ToDisable (should fail)
Test re-enabling validator 0 from nUNL (should succeed)
Verify final state has both ToDisable and ToReEnable set
Phase 6: Complete Lifecycle (15 minutes)
Progress to next flag ledger
Process both ToDisable and ToReEnable
Verify validator 2 added to nUNL
Verify validator 0 removed from nUNL
Verify tracking map is accurate
Implementation Guide
Test Function Skeleton:
Helper Functions Available:
Deliverable
A written report containing:
Complete Test Code:
Full
testValidatorLifecycle()function implementationComments explaining each phase
Proper use of helper functions
BEAST_EXPECT assertions at key points
Test Execution Output:
Console output from running your test
Show all 6 phases completing
Show success/failure of each transaction
Show nUNL state changes
Analysis Table:
Ledger SequenceEventnUNL SizeToDisableToReEnable256 (1st flag)
Initial
0
None
None
256
Disable TX
0
Validator 0
None
512 (2nd flag)
Process Disable
1
None
None
512
Complex TXs
1
Validator 2
Validator 0
768 (3rd flag)
Complete Lifecycle
1
None
None
Questions Answered:
Why can only one ToDisable transaction be processed per flag ledger?
Why can't a validator be disabled before reaching a flag ledger?
What happens to the ToDisable field after it's processed?
Can a validator be in both ToDisable and ToReEnable simultaneously?
How does the 256-ledger interval affect network responsiveness to validator failures?
What would happen if you tried to disable all validators?
Advanced Challenges (Optional)
Scoring System: Implement validator scoring based on
validationFRESHNESSAutomatic Disable: Trigger automatic disable when score falls below threshold
Multiple Cycles: Test disabling and re-enabling the same validator multiple times
Edge Cases: Test with only 2 validators, or with all but one disabled
Tips
Use
std::coutliberally to track progress through phasesThe
BEAST_EXPECT()macro is your test assertionFlag ledgers are at sequences 256, 512, 768, 1024, etc.
updateNegativeUNL()must be called on each flag ledgerOpenView allows transaction simulation before applying to ledger
Read the existing
testNegativeUNL()function for patternsBuild and run:
cmake --build . --target rippled --parallel 10 && ./rippled --unittest=LedgerMaster
Learning Goals
By completing this homework, you should be able to:
Write consensus tests using the jtx framework
Understand the Negative UNL transaction lifecycle
Observe automatic validator management mechanisms
Explain flag ledger processing
Predict nUNL state changes based on transactions
Debug consensus test failures
Verify complex multi-phase consensus scenarios
Reference Materials
Negative UNL Implementation:
rippled/src/xrpld/app/misc/NegativeUNLVote.cppTest Framework:
rippled/src/test/jtx/Ledger Interface:
rippled/src/xrpld/ledger/ReadView.h
Last updated

