Compare commits

..

7 Commits

Author SHA1 Message Date
Denis Angell
b418b416b0 add claude 2026-02-06 19:31:07 +01:00
Ayaz Salikhov
2305bc98a4 chore: Remove CODEOWNERS (#6337) 2026-02-06 11:39:23 -05:00
Bart
677758b1cc perf: Remove unnecessary caches (#5439)
This change removes the cache in `DatabaseNodeImp` and simplifies the caching logic in `SHAMapStoreImp`. As NuDB and RocksDB internally already use caches, additional caches in the code are not very valuable or may even be unnecessary, as also confirmed during preliminary performance analyses.
2026-02-06 09:42:35 -05:00
Bart
25d7c2c4ec chore: Restore unity builds (#6328)
In certain cases, such as when modifying headers used by many compilation units, performing a unity build is slower than when performing a regular build with `ccache` enabled. There is also a benefit to a unity build in that it can detect things such as macro redefinitions within the group of files that are compiled together as a unit. This change therefore restores the ability to perform unity builds. However, instead of running every configuration with and without unity enabled, it is now only enabled for a single configuration to maintain lower computational use.

As part of restoring the code, it became clear that currently two configurations have coverage enabled, since the check doesn't focus specifically on Debian Bookworm so it also applies to Debian Trixie. This has been fixed too in this change.
2026-02-06 14:12:45 +00:00
Bart
0a626d95f4 refactor: Update secp256k1 to 0.7.1 (#6331)
The latest secp256k1 release, 0.7.1, contains bug fixes that we may benefit from, see https://github.com/bitcoin-core/secp256k1/blob/master/CHANGELOG.md.
2026-02-05 16:45:57 +00:00
Niq Dudfield
6006c281e2 fix: Increment sequence when accepting new manifests (#6059)
The `ManifestCache::applyManifest` function was returning early without incrementing `seq_`. `OverlayImpl `uses this sequence to identify/invalidate a cached `TMManifests` message, which is exchanged with peers on connection. Depending on network size, startup sequencing, and topology, this can cause syncing issues. This change therefore increments `seq_` when a new manifest is accepted.
2026-02-05 10:40:27 -05:00
Vito Tumas
e79673cf40 fix typo in LendingHelpers unit-test (#6215) 2026-02-05 10:23:44 +00:00
39 changed files with 822 additions and 703 deletions

54
.claude/CLAUDE.md Normal file
View File

@@ -0,0 +1,54 @@
# Workflow Orchestration
## 1. Plan Mode Default
- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
- If something goes sideways, STOP and re-plan immediately - don't keep pushing
- Use plan mode for verification steps, not just building
- Write detailed specs upfront to reduce ambiguity
## 2. Subagent Strategy
- Use subagents liberally to keep main context window clean
- Offload research, exploration, and parallel analysis to subagents
- For complex problems, throw compute at it via parallelizing
- One task per subagent for focused execution
## 3. Self-Improvement Loop
- After ANY correction from the user: update `tasks/lessons.md` with the pattern
- Write rules for yourself that prevent the same mistake
- Ruthlessly iterate on these rules until mistake drops
- Review lessons at session start for relevant project
## 4. Verification Before Done
- Never mark a task complete without proving it works
- Diff behavior between main and your changes when relevant
- Ask yourself: "Would a staff engineer approve this?"
- Run tests, check logs, demonstrate correctness
## 5. Demand Elegance (Balanced)
- For non-trivial changes: pause and ask "is there a more elegant way?"
- If a fix feels hacky: "Know now, do later, implement the elegant solution"
- Skip this for simple, obvious fixes - don't over-engineer
- Challenge your own first solution
## 6. Autonomous Bug Fixing
- When given a bug report: just fix it. Don't ask for hand-holding
- Point at logs, errors, failing tests - then resolve them
- Zero context switching required from the user
- Go fix failing CI tests without being told how
# Task Management
1. **Plan First**: Write plan to `tasks/todo.md` with checkable items
2. **Verify Plans**: Check in before starting implementation
3. **Track Progress**: Mark items complete as you go
4. **Explain Changes**: High-level summary at each step
5. **Document Results**: Add review section to `tasks/todo.md`
6. **Capture Lessons**: Update `tasks/lessons.md` after corrections
# Core Principles
**Simplicity First**: Make every change as simple as possible. Impact minimal code.
**No Boilerplate**: Skip documentation. No README/docs bloat. Test only if requested.
**Minimal Impact**: Changes should only touch what's necessary. Avoid introducing bugs.

530
.claude/skills/amendment.md Normal file
View File

@@ -0,0 +1,530 @@
# XRPL Amendment Creation Skill
This skill guides you through creating XRPL amendments, whether for brand new features or fixes/extensions to existing functionality.
## Amendment Types
There are two main types of amendments:
1. **New Feature Amendment** (`feature{Name}`) - Entirely new functionality
2. **Fix/Extension Amendment** (`fix{Name}`) - Modifications to existing functionality
## Step 1: Determine Amendment Type
Ask the user:
- Is this a **brand new feature** (new transaction type, ledger entry, or capability)?
- Or is this a **fix or extension** to existing functionality?
---
## For NEW FEATURE Amendments
### Checklist:
#### 1. Feature Definition in features.macro
**ONLY FILE TO EDIT:** `include/xrpl/protocol/detail/features.macro`
- [ ] Add to TOP of features.macro (reverse chronological order):
```
XRPL_FEATURE(YourFeatureName, Supported::no, VoteBehavior::DefaultNo)
```
- [ ] This creates the variable `featureYourFeatureName` automatically
- [ ] Follow naming convention: Use the feature name WITHOUT the "feature" prefix
- [ ] Examples: `Batch``featureBatch`, `LendingProtocol``featureLendingProtocol`
#### 2. Support Status
- [ ] Start with `Supported::no` during development
- [ ] Change to `Supported::yes` when ready for network voting
- [ ] Use `VoteBehavior::DefaultNo` (validators must explicitly vote for it)
#### 3. Code Implementation
- [ ] Implement new functionality (transaction type, ledger entry, etc.)
- [ ] Add feature gate check in preflight:
```cpp
if (!env.current()->rules().enabled(feature{Name}))
{
return temDISABLED;
}
```
#### 4. Disable Route Handling
- [ ] Ensure transaction returns `temDISABLED` when amendment is disabled
- [ ] Implement early rejection in preflight/preclaim phase
- [ ] Add appropriate error messages
#### 5. Test Implementation
Create comprehensive test suite with this structure:
```cpp
class {FeatureName}_test : public beast::unit_test::suite
{
public:
void testEnable(FeatureBitset features)
{
testcase("enabled");
// Test with feature DISABLED
{
auto const amendNoFeature = features - feature{Name};
Env env{*this, amendNoFeature};
env(transaction, ter(temDISABLED));
}
// Test with feature ENABLED
{
Env env{*this, features};
env(transaction, ter(tesSUCCESS));
// Validate new functionality works
}
}
void testPreflight(FeatureBitset features)
{
testcase("preflight");
// Test malformed transaction validation
}
void testPreclaim(FeatureBitset features)
{
testcase("preclaim");
// Test signature and claim phase validation
}
void testWithFeats(FeatureBitset features)
{
testEnable(features);
testPreflight(features);
testPreclaim(features);
// Add feature-specific tests
}
void run() override
{
using namespace test::jtx;
auto const all = supported_amendments();
testWithFeats(all);
}
};
```
#### 6. Test Coverage Requirements
- [ ] Test amendment DISABLED state (returns `temDISABLED`)
- [ ] Test amendment ENABLED state (returns `tesSUCCESS`)
- [ ] Test malformed transactions
- [ ] Test signature validation
- [ ] Test edge cases specific to feature
- [ ] Test amendment transition behavior
#### 7. Documentation
- [ ] Create specification document (XLS_{NAME}.md)
- [ ] Document new transaction types, ledger entries, or capabilities
- [ ] Create test plan document
- [ ] Document expected behavior when enabled/disabled
---
## For FIX/EXTENSION Amendments
### Checklist:
#### 1. Fix Definition in features.macro
**ONLY FILE TO EDIT:** `include/xrpl/protocol/detail/features.macro`
- [ ] Add to TOP of features.macro (reverse chronological order):
```
XRPL_FIX(YourFixName, Supported::no, VoteBehavior::DefaultNo)
```
- [ ] This creates the variable `fixYourFixName` automatically
- [ ] Follow naming convention: Use the fix name WITHOUT the "fix" prefix (it's added automatically)
- [ ] Examples: `TokenEscrowV1``fixTokenEscrowV1`, `DirectoryLimit``fixDirectoryLimit`
- [ ] Start with `Supported::no` during development, change to `Supported::yes` when ready
#### 2. Backward Compatibility Implementation
**Critical**: Use enable() with if/else to preserve existing functionality
```cpp
// Check if fix is enabled
bool const fix{Name} = env.current()->rules().enabled(fix{Name});
// Conditional logic based on amendment state
if (fix{Name})
{
// NEW behavior with fix applied
// This is the corrected/improved logic
}
else
{
// OLD behavior (legacy path)
// Preserve original functionality for backward compatibility
}
```
**Alternative pattern with ternary operator:**
```cpp
auto& viewToUse = sb.rules().enabled(fix{Name}) ? sb : legacyView;
```
#### 3. Multiple Fix Versions Pattern
For iterative fixes, use version checking:
```cpp
bool const fixV1 = rv.rules().enabled(fixXahauV1);
bool const fixV2 = rv.rules().enabled(fixXahauV2);
switch (transactionType)
{
case TYPE_1:
if (fixV1) {
// Behavior with V1 fix
} else {
// Legacy behavior
}
break;
case TYPE_2:
if (fixV2) {
// Behavior with V2 fix
} else if (fixV1) {
// Behavior with only V1
} else {
// Legacy behavior
}
break;
}
```
#### 4. Test Both Paths
Always test BOTH enabled and disabled states:
```cpp
void testFix(FeatureBitset features)
{
testcase("fix behavior");
for (bool withFix : {false, true})
{
auto const amend = withFix ? features : features - fix{Name};
Env env{*this, amend};
// Setup test scenario
env.fund(XRP(1000), alice);
env.close();
if (!withFix)
{
// Test OLD behavior (before fix)
env(operation, ter(expectedErrorWithoutFix));
// Verify old behavior is preserved
}
else
{
// Test NEW behavior (after fix)
env(operation, ter(expectedErrorWithFix));
// Verify fix works correctly
}
}
}
```
#### 5. Security Fix Pattern
For security-critical fixes (like fixBatchInnerSigs):
```cpp
// Test vulnerability exists WITHOUT fix
{
auto const amendNoFix = features - fix{Name};
Env env{*this, amendNoFix};
// Demonstrate vulnerability
// Expected: Validity::Valid (WRONG - vulnerable!)
BEAST_EXPECT(result == Validity::Valid);
}
// Test vulnerability is FIXED WITH amendment
{
Env env{*this, features};
// Demonstrate fix
// Expected: Validity::SigBad (CORRECT - protected!)
BEAST_EXPECT(result == Validity::SigBad);
}
```
#### 6. Test Coverage Requirements
- [ ] Test fix DISABLED (legacy behavior preserved)
- [ ] Test fix ENABLED (new behavior applied)
- [ ] Test amendment transition
- [ ] For security fixes: demonstrate vulnerability without fix
- [ ] For security fixes: demonstrate protection with fix
- [ ] Test edge cases that triggered the fix
- [ ] Test combinations with other amendments
#### 7. Documentation
- [ ] Document what was broken/suboptimal
- [ ] Document the fix applied
- [ ] Document backward compatibility behavior
- [ ] Create test summary showing both paths
---
## Best Practices for All Amendments
### 1. Naming Conventions
- New features: `feature{DescriptiveName}` (e.g., `featureBatch`, `featureHooks`)
- Fixes: `fix{IssueDescription}` (e.g., `fixBatchInnerSigs`, `fixNSDelete`)
- Use CamelCase without underscores
### 2. Feature Flag Checking
```cpp
// At the point where behavior diverges:
bool const amendmentEnabled = env.current()->rules().enabled(feature{Name});
// Or in view/rules context:
if (!rv.rules().enabled(feature{Name}))
return {}; // or legacy behavior
```
### 3. Error Codes
- New features when disabled: `temDISABLED`
- Fixes may return different validation errors based on state
- Document all error code changes
### 4. Test Structure Template
```cpp
class Amendment_test : public beast::unit_test::suite
{
public:
// Core tests
void testEnable(FeatureBitset features); // Enable/disable states
void testPreflight(FeatureBitset features); // Validation
void testPreclaim(FeatureBitset features); // Claim phase
// Feature-specific tests
void test{SpecificScenario1}(FeatureBitset features);
void test{SpecificScenario2}(FeatureBitset features);
// Master orchestrator
void testWithFeats(FeatureBitset features)
{
testEnable(features);
testPreflight(features);
testPreclaim(features);
test{SpecificScenario1}(features);
test{SpecificScenario2}(features);
}
void run() override
{
using namespace test::jtx;
auto const all = supported_amendments();
testWithFeats(all);
}
};
BEAST_DEFINE_TESTSUITE(Amendment, app, ripple);
```
### 5. Documentation Files
Create these files:
- **Specification**: `XLS_{FEATURE_NAME}.md` - Technical specification
- **Test Plan**: `{FEATURE}_COMPREHENSIVE_TEST_PLAN.md` - Test strategy
- **Test Summary**: `{FEATURE}_TEST_IMPLEMENTATION_SUMMARY.md` - Test results
- **Review Findings**: `{FEATURE}_REVIEW_FINDINGS.md` (if applicable)
### 6. Amendment Transition Testing
Test the moment an amendment activates:
```cpp
void testAmendmentTransition(FeatureBitset features)
{
testcase("amendment transition");
// Start with amendment disabled
auto const amendNoFeature = features - feature{Name};
Env env{*this, amendNoFeature};
// Perform operations in disabled state
env(operation1, ter(temDISABLED));
// Enable amendment mid-test (if testing mechanism supports it)
// Verify state transitions correctly
// Perform operations in enabled state
env(operation2, ter(tesSUCCESS));
}
```
### 7. Cache Behavior
If amendment affects caching (like fixBatchInnerSigs):
- [ ] Test cache behavior without fix
- [ ] Test cache behavior with fix
- [ ] Document cache invalidation requirements
### 8. Multi-Amendment Combinations
Test interactions with other amendments:
```cpp
void testMultipleAmendments(FeatureBitset features)
{
// Test all combinations
for (bool withFeature1 : {false, true})
for (bool withFeature2 : {false, true})
{
auto amend = features;
if (!withFeature1) amend -= feature1;
if (!withFeature2) amend -= feature2;
Env env{*this, amend};
// Test interaction behavior
}
}
```
### 9. Performance Considerations
- [ ] Minimize runtime checks (cache `rules().enabled()` result if used multiple times)
- [ ] Avoid nested feature checks where possible
- [ ] Document performance impact
### 10. Code Review Checklist
- [ ] Both enabled/disabled paths are tested
- [ ] Backward compatibility is preserved (for fixes)
- [ ] Error codes are appropriate
- [ ] Documentation is complete
- [ ] Security implications are considered
- [ ] Cache behavior is correct
- [ ] Edge cases are covered
---
## Common Patterns Reference
### Pattern: New Transaction Type
```cpp
// In transactor code:
TER doApply() override
{
if (!ctx_.view().rules().enabled(feature{Name}))
return temDISABLED;
// New transaction logic here
return tesSUCCESS;
}
```
### Pattern: New Ledger Entry Type
```cpp
// In ledger entry creation:
if (!view.rules().enabled(feature{Name}))
return temDISABLED;
auto const sle = std::make_shared<SLE>(ltNEW_TYPE, keylet);
view.insert(sle);
```
### Pattern: Behavioral Fix
```cpp
// At decision point:
bool const useFix = view.rules().enabled(fix{Name});
if (useFix)
{
// Corrected behavior
return performCorrectValidation();
}
else
{
// Legacy behavior (preserved for compatibility)
return performLegacyValidation();
}
```
### Pattern: View Selection
```cpp
// Select which view to use based on amendment:
auto& applyView = sb.rules().enabled(feature{Name}) ? newView : legacyView;
```
---
## Example Workflows
### Workflow 1: Creating a New Feature Amendment
1. User requests: "Add a new ClaimReward transaction type"
2. Skill asks: "What should the amendment be called? (e.g., BalanceRewards - without 'feature' prefix)"
3. Add to features.macro:
```
XRPL_FEATURE(BalanceRewards, Supported::no, VoteBehavior::DefaultNo)
```
4. Implement transaction with `temDISABLED` gate using `featureBalanceRewards`
5. Create test suite with testEnable, testPreflight, testPreclaim
6. Run tests with amendment enabled and disabled
7. When ready, update to `Supported::yes` in features.macro
8. Create specification document
9. Review checklist
### Workflow 2: Creating a Fix Amendment
1. User requests: "Fix the signature validation bug in batch transactions"
2. Skill asks: "What should the fix be called? (e.g., BatchInnerSigs - without 'fix' prefix)"
3. Add to features.macro:
```
XRPL_FIX(BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo)
```
4. Implement fix with if/else using `fixBatchInnerSigs` to preserve old behavior
5. Create test demonstrating vulnerability without fix
6. Create test showing fix works when enabled
7. When ready, update to `Supported::yes` in features.macro
8. Document both code paths
9. Review checklist
---
## Quick Reference: File Locations
- **Amendment definitions (ONLY place to add)**: `include/xrpl/protocol/detail/features.macro`
- **Feature.h (auto-generated, DO NOT EDIT)**: `include/xrpl/protocol/Feature.h`
- **Feature.cpp (auto-generated, DO NOT EDIT)**: `src/libxrpl/protocol/Feature.cpp`
- **Test files**: `src/test/app/` or `src/test/protocol/`
- **Specifications**: Project root (e.g., `XLS_SMART_CONTRACTS.md`)
- **Test plans**: Project root (e.g., `BATCH_COMPREHENSIVE_TEST_PLAN.md`)
## How the Macro System Works
The amendment system uses C preprocessor macros:
1. **features.macro** - Single source of truth (ONLY file you edit):
```
XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX(TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo)
```
2. **Feature.h** - Auto-generated declarations from macro:
```cpp
extern uint256 const featureBatch;
extern uint256 const fixTokenEscrowV1;
```
3. **Feature.cpp** - Auto-generated registrations from macro:
```cpp
uint256 const featureBatch = registerFeature("Batch", ...);
uint256 const fixTokenEscrowV1 = registerFeature("fixTokenEscrowV1", ...);
```
**DO NOT** modify Feature.h or Feature.cpp directly - they process features.macro automatically.
---
## When to Use This Skill
Invoke this skill when:
- Creating a new XRPL amendment
- Adding a new transaction type or ledger entry
- Fixing existing XRPL functionality
- Need guidance on amendment best practices
- Setting up amendment tests
- Reviewing amendment implementation
The skill will guide you through the appropriate workflow based on amendment type.

8
.github/CODEOWNERS vendored
View File

@@ -1,8 +0,0 @@
# Allow anyone to review any change by default.
*
# Require the rpc-reviewers team to review changes to the rpc code.
include/xrpl/protocol/ @xrplf/rpc-reviewers
src/libxrpl/protocol/ @xrplf/rpc-reviewers
src/xrpld/rpc/ @xrplf/rpc-reviewers
src/xrpld/app/misc/ @xrplf/rpc-reviewers

View File

@@ -196,11 +196,22 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
# Enable code coverage for Debian Bookworm using GCC 15 in Debug on
# linux/amd64
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
f"{os['distro_name']}-{os['distro_version']}" == "debian-bookworm"
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}"
cmake_args = f"{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
# Enable unity build for Ubuntu Jammy using GCC 12 in Debug on
# linux/amd64.
if (
f"{os['distro_name']}-{os['distro_version']}" == "ubuntu-jammy"
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"{cmake_args} -Dunity=ON"
# Generate a unique name for the configuration, e.g. macos-arm64-debug
# or debian-bookworm-gcc-12-amd64-release.
@@ -217,6 +228,8 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
config_name += f"-{build_type.lower()}"
if "-Dcoverage=ON" in cmake_args:
config_name += "-coverage"
if "-Dunity=ON" in cmake_args:
config_name += "-unity"
# Add the configuration to the list, with the most unique fields first,
# so that they are easier to identify in the GitHub Actions UI, as long

2
.gitignore vendored
View File

@@ -69,5 +69,3 @@ DerivedData
# AI tools.
/.augment
/.claude
/CLAUDE.md

View File

@@ -575,10 +575,16 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
| `assert` | OFF | Enable assertions. |
| `coverage` | OFF | Prepare the coverage report. |
| `tests` | OFF | Build tests. |
| `unity` | OFF | Configure a unity build. |
| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. |
| `werr` | OFF | Treat compilation warnings as errors |
| `wextra` | OFF | Enable additional compilation warnings |
[Unity builds][5] may be faster for the first build (at the cost of much more
memory) since they concatenate sources into fewer translation units. Non-unity
builds may be faster for incremental builds, and can be helpful for detecting
`#include` omissions.
## Troubleshooting
### Conan
@@ -645,6 +651,7 @@ If you want to experiment with a new package, follow these steps:
[1]: https://github.com/conan-io/conan-center-index/issues/13168
[2]: https://en.cppreference.com/w/cpp/compiler_support/20
[3]: https://docs.conan.io/en/latest/getting_started.html
[5]: https://en.wikipedia.org/wiki/Unity_build
[6]: https://github.com/boostorg/beast/issues/2648
[7]: https://github.com/boostorg/beast/issues/2661
[gcovr]: https://gcovr.com/en/stable/getting-started.html

View File

@@ -940,23 +940,7 @@
#
# path Location to store the database
#
# Optional keys
#
# cache_size Size of cache for database records. Default is 16384.
# Setting this value to 0 will use the default value.
#
# cache_age Length of time in minutes to keep database records
# cached. Default is 5 minutes. Setting this value to
# 0 will use the default value.
#
# Note: if neither cache_size nor cache_age is
# specified, the cache for database records will not
# be created. If only one of cache_size or cache_age
# is specified, the cache will be created using the
# default value for the unspecified parameter.
#
# Note: the cache will not be created if online_delete
# is specified.
# Optional keys for NuDB and RocksDB:
#
# fast_load Boolean. If set, load the last persisted ledger
# from disk upon process start before syncing to
@@ -964,8 +948,6 @@
# if sufficient IOPS capacity is available.
# Default 0.
#
# Optional keys for NuDB or RocksDB:
#
# earliest_seq The default is 32570 to match the XRP ledger
# network's earliest allowed sequence. Alternate
# networks may set this value. Minimum value of 1.

View File

@@ -4,7 +4,12 @@
include(target_protobuf_sources)
# Protocol buffers cannot participate in a unity build,
# because all the generated sources
# define a bunch of `static const` variables with the same names,
# so we just build them as a separate library.
add_library(xrpl.libpb)
set_target_properties(xrpl.libpb PROPERTIES UNITY_BUILD OFF)
target_protobuf_sources(xrpl.libpb xrpl/proto LANGUAGE cpp IMPORT_DIRS include/xrpl/proto
PROTOS include/xrpl/proto/xrpl.proto)

View File

@@ -30,6 +30,14 @@ if (tests)
endif ()
endif ()
option(unity "Creates a build using UNITY support in cmake." OFF)
if (unity)
if (NOT is_ci)
set(CMAKE_UNITY_BUILD_BATCH_SIZE 15 CACHE STRING "")
endif ()
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
endif ()
if (is_clang AND is_linux)
option(voidstar "Enable Antithesis instrumentation." OFF)
endif ()

View File

@@ -6,7 +6,7 @@
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
"secp256k1/0.7.0#0fda78daa3b864deb8a2fbc083398356%1770226294.524",
"secp256k1/0.7.1#3a61e95e220062ef32c48d019e9c81f7%1770306721.686",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",

View File

@@ -23,6 +23,7 @@ class Xrpl(ConanFile):
"shared": [True, False],
"static": [True, False],
"tests": [True, False],
"unity": [True, False],
"xrpld": [True, False],
}
@@ -32,7 +33,7 @@ class Xrpl(ConanFile):
"libarchive/3.8.1",
"nudb/2.0.9",
"openssl/3.5.5",
"secp256k1/0.7.0",
"secp256k1/0.7.1",
"soci/4.0.3",
"zlib/1.3.1",
]
@@ -54,6 +55,7 @@ class Xrpl(ConanFile):
"shared": False,
"static": True,
"tests": False,
"unity": False,
"xrpld": False,
"date/*:header_only": True,
"ed25519/*:shared": False,
@@ -166,6 +168,7 @@ class Xrpl(ConanFile):
tc.variables["rocksdb"] = self.options.rocksdb
tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
tc.variables["static"] = self.options.static
tc.variables["unity"] = self.options.unity
tc.variables["xrpld"] = self.options.xrpld
tc.generate()

View File

@@ -1,138 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2024 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_BASICS_CANPROCESS_H_INCLUDED
#define RIPPLE_BASICS_CANPROCESS_H_INCLUDED
#include <functional>
#include <mutex>
#include <set>
/** RAII class to check if an Item is already being processed on another thread,
* as indicated by it's presence in a Collection.
*
* If the Item is not in the Collection, it will be added under lock in the
* ctor, and removed under lock in the dtor. The object will be considered
* "usable" and evaluate to `true`.
*
* If the Item is in the Collection, no changes will be made to the collection,
* and the CanProcess object will be considered "unusable".
*
* It's up to the caller to decide what "usable" and "unusable" mean. (e.g.
* Process or skip a block of code, or set a flag.)
*
* The current use is to avoid lock contention that would be involved in
* processing something associated with the Item.
*
* Examples:
*
* void IncomingLedgers::acquireAsync(LedgerHash const& hash, ...)
* {
* if (CanProcess check{acquiresMutex_, pendingAcquires_, hash})
* {
* acquire(hash, ...);
* }
* }
*
* bool
* NetworkOPsImp::recvValidation(
* std::shared_ptr<STValidation> const& val,
* std::string const& source)
* {
* CanProcess check(
* validationsMutex_, pendingValidations_, val->getLedgerHash());
* BypassAccept bypassAccept =
* check ? BypassAccept::no : BypassAccept::yes;
* handleNewValidation(app_, val, source, bypassAccept, m_journal);
* }
*
*/
class CanProcess
{
public:
template <class Mutex, class Collection, class Item>
CanProcess(Mutex& mtx, Collection& collection, Item const& item) : cleanup_(insert(mtx, collection, item))
{
}
~CanProcess()
{
if (cleanup_)
cleanup_();
}
CanProcess(CanProcess const&) = delete;
CanProcess&
operator=(CanProcess const&) = delete;
explicit
operator bool() const
{
return static_cast<bool>(cleanup_);
}
private:
template <bool useIterator, class Mutex, class Collection, class Item>
std::function<void()>
doInsert(Mutex& mtx, Collection& collection, Item const& item)
{
std::unique_lock<Mutex> lock(mtx);
// TODO: Use structured binding once LLVM 16 is the minimum supported
// version. See also: https://github.com/llvm/llvm-project/issues/48582
// https://github.com/llvm/llvm-project/commit/127bf44385424891eb04cff8e52d3f157fc2cb7c
auto const insertResult = collection.insert(item);
auto const it = insertResult.first;
if (!insertResult.second)
return {};
if constexpr (useIterator)
return [&, it]() {
std::unique_lock<Mutex> lock(mtx);
collection.erase(it);
};
else
return [&]() {
std::unique_lock<Mutex> lock(mtx);
collection.erase(item);
};
}
// Generic insert() function doesn't use iterators because they may get
// invalidated
template <class Mutex, class Collection, class Item>
std::function<void()>
insert(Mutex& mtx, Collection& collection, Item const& item)
{
return doInsert<false>(mtx, collection, item);
}
// Specialize insert() for std::set, which does not invalidate iterators for
// insert and erase
template <class Mutex, class Item>
std::function<void()>
insert(Mutex& mtx, std::set<Item>& collection, Item const& item)
{
return doInsert<true>(mtx, collection, item);
}
// If set, then the item is "usable"
std::function<void()> cleanup_;
};
#endif

View File

@@ -133,10 +133,6 @@ public:
std::uint32_t ledgerSeq,
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback);
/** Remove expired entries from the positive and negative caches. */
virtual void
sweep() = 0;
/** Gather statistics pertaining to read and write activities.
*
* @param obj Json object reference into which to place counters.

View File

@@ -23,32 +23,6 @@ public:
beast::Journal j)
: Database(scheduler, readThreads, config, j), backend_(std::move(backend))
{
std::optional<int> cacheSize, cacheAge;
if (config.exists("cache_size"))
{
cacheSize = get<int>(config, "cache_size");
if (cacheSize.value() < 0)
{
Throw<std::runtime_error>("Specified negative value for cache_size");
}
}
if (config.exists("cache_age"))
{
cacheAge = get<int>(config, "cache_age");
if (cacheAge.value() < 0)
{
Throw<std::runtime_error>("Specified negative value for cache_age");
}
}
if (cacheSize != 0 || cacheAge != 0)
{
cache_ = std::make_shared<TaggedCache<uint256, NodeObject>>(
"DatabaseNodeImp", cacheSize.value_or(0), std::chrono::minutes(cacheAge.value_or(0)), stopwatch(), j);
}
XRPL_ASSERT(
backend_,
"xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null "
@@ -103,13 +77,7 @@ public:
std::uint32_t ledgerSeq,
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback) override;
void
sweep() override;
private:
// Cache for database objects. This cache is not always initialized. Check
// for null before using.
std::shared_ptr<TaggedCache<uint256, NodeObject>> cache_;
// Persistent key/value storage
std::shared_ptr<Backend> backend_;

View File

@@ -55,9 +55,6 @@ public:
void
sync() override;
void
sweep() override;
private:
std::shared_ptr<Backend> writableBackend_;
std::shared_ptr<Backend> archiveBackend_;

View File

@@ -35,8 +35,6 @@ struct LedgerHeader
// If validated is false, it means "not yet validated."
// Once validated is true, it will never be set false at a later time.
// NOTE: If you are accessing this directly, you are probably doing it
// wrong. Use LedgerMaster::isValidated().
// VFALCO TODO Make this not mutable
bool mutable validated = false;
bool accepted = false;

View File

@@ -85,8 +85,7 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec,
// There is a very unpleasant interaction between <wincrypt> and
// openssl x509 types (namely the former has macros that stomp
// on the latter), these undefs allow this TU to be safely used in
// unity builds without messing up subsequent TUs. Although we
// no longer use unity builds, leaving the undefs here does no harm.
// unity builds without messing up subsequent TUs.
#if BOOST_OS_WINDOWS
#undef X509_NAME
#undef X509_EXTENSIONS

View File

@@ -10,11 +10,6 @@ DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, st
auto obj = NodeObject::createObject(type, std::move(data), hash);
backend_->store(obj);
if (cache_)
{
// After the store, replace a negative cache entry if there is one
cache_->canonicalize(hash, obj, [](std::shared_ptr<NodeObject> const& n) { return n->getType() == hotDUMMY; });
}
}
void
@@ -23,77 +18,36 @@ DatabaseNodeImp::asyncFetch(
std::uint32_t ledgerSeq,
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback)
{
if (cache_)
{
std::shared_ptr<NodeObject> obj = cache_->fetch(hash);
if (obj)
{
callback(obj->getType() == hotDUMMY ? nullptr : obj);
return;
}
}
Database::asyncFetch(hash, ledgerSeq, std::move(callback));
}
void
DatabaseNodeImp::sweep()
{
if (cache_)
cache_->sweep();
}
std::shared_ptr<NodeObject>
DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
{
std::shared_ptr<NodeObject> nodeObject = cache_ ? cache_->fetch(hash) : nullptr;
std::shared_ptr<NodeObject> nodeObject = nullptr;
Status status;
if (!nodeObject)
try
{
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record not " << (cache_ ? "cached" : "found");
Status status;
try
{
status = backend_->fetch(hash.data(), &nodeObject);
}
catch (std::exception const& e)
{
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
Rethrow();
}
switch (status)
{
case ok:
if (cache_)
{
if (nodeObject)
cache_->canonicalize_replace_client(hash, nodeObject);
else
{
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
cache_->canonicalize_replace_client(hash, notFound);
if (notFound->getType() != hotDUMMY)
nodeObject = notFound;
}
}
break;
case notFound:
break;
case dataCorrupt:
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
break;
default:
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
break;
}
status = backend_->fetch(hash.data(), &nodeObject);
}
else
catch (std::exception const& e)
{
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record found in cache";
if (nodeObject->getType() == hotDUMMY)
nodeObject.reset();
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
Rethrow();
}
switch (status)
{
case ok:
case notFound:
break;
case dataCorrupt:
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
break;
default:
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
break;
}
if (nodeObject)
@@ -105,66 +59,36 @@ DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport
std::vector<std::shared_ptr<NodeObject>>
DatabaseNodeImp::fetchBatch(std::vector<uint256> const& hashes)
{
std::vector<std::shared_ptr<NodeObject>> results{hashes.size()};
using namespace std::chrono;
auto const before = steady_clock::now();
std::unordered_map<uint256 const*, size_t> indexMap;
std::vector<uint256 const*> cacheMisses;
uint64_t hits = 0;
uint64_t fetches = 0;
std::vector<uint256 const*> batch{};
batch.reserve(hashes.size());
for (size_t i = 0; i < hashes.size(); ++i)
{
auto const& hash = hashes[i];
// See if the object already exists in the cache
auto nObj = cache_ ? cache_->fetch(hash) : nullptr;
++fetches;
if (!nObj)
{
// Try the database
indexMap[&hash] = i;
cacheMisses.push_back(&hash);
}
else
{
results[i] = nObj->getType() == hotDUMMY ? nullptr : nObj;
// It was in the cache.
++hits;
}
batch.push_back(&hash);
}
JLOG(j_.debug()) << "fetchBatch - cache hits = " << (hashes.size() - cacheMisses.size())
<< " - cache misses = " << cacheMisses.size();
auto dbResults = backend_->fetchBatch(cacheMisses).first;
for (size_t i = 0; i < dbResults.size(); ++i)
// Get the node objects that match the hashes from the backend. To protect
// against the backends returning fewer or more results than expected, the
// container is resized to the number of hashes.
auto results = backend_->fetchBatch(batch).first;
XRPL_ASSERT(
results.size() == hashes.size() || results.empty(),
"number of output objects either matches number of input hashes or is empty");
results.resize(hashes.size());
for (size_t i = 0; i < results.size(); ++i)
{
auto nObj = std::move(dbResults[i]);
size_t index = indexMap[cacheMisses[i]];
auto const& hash = hashes[index];
if (nObj)
{
// Ensure all threads get the same object
if (cache_)
cache_->canonicalize_replace_client(hash, nObj);
}
else
if (!results[i])
{
JLOG(j_.error()) << "fetchBatch - "
<< "record not found in db or cache. hash = " << strHex(hash);
if (cache_)
{
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
cache_->canonicalize_replace_client(hash, notFound);
if (notFound->getType() != hotDUMMY)
nObj = std::move(notFound);
}
<< "record not found in db. hash = " << strHex(hashes[i]);
}
results[index] = std::move(nObj);
}
auto fetchDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - before).count();
updateFetchMetrics(fetches, hits, fetchDurationUs);
updateFetchMetrics(hashes.size(), 0, fetchDurationUs);
return results;
}

View File

@@ -93,12 +93,6 @@ DatabaseRotatingImp::store(NodeObjectType type, Blob&& data, uint256 const& hash
storeStats(1, nObj->getData().size());
}
void
DatabaseRotatingImp::sweep()
{
// nothing to do
}
std::shared_ptr<NodeObject>
DatabaseRotatingImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
{

View File

@@ -81,12 +81,7 @@ public:
}
virtual void
acquireAsync(
JobType type,
std::string const& name,
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) override
acquireAsync(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override
{
}

View File

@@ -592,20 +592,18 @@ class LendingHelpers_test : public beast::unit_test::suite
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
Number const overpaymentAmount{50};
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
auto const overpaymentComponents = computeOverpaymentComponents(
asset, loanScale, overpaymentAmount, TenthBips32(0), TenthBips32(0), managementFeeRate);
auto const loanProperites = computeLoanProperties(
auto const loanProperties = computeLoanProperties(
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
Number const periodicPayment = loanProperites.periodicPayment;
auto const ret = tryOverpayment(
asset,
loanScale,
overpaymentComponents,
loanProperites.loanState,
periodicPayment,
loanProperties.loanState,
loanProperties.periodicPayment,
periodicRate,
paymentsRemaining,
managementFeeRate,
@@ -636,20 +634,20 @@ class LendingHelpers_test : public beast::unit_test::suite
// =========== VALIDATE STATE CHANGES ===========
BEAST_EXPECTS(
loanProperites.loanState.interestDue - newState.interestDue == 0,
loanProperties.loanState.interestDue - newState.interestDue == 0,
" interest change mismatch: expected 0, got " +
to_string(loanProperites.loanState.interestDue - newState.interestDue));
to_string(loanProperties.loanState.interestDue - newState.interestDue));
BEAST_EXPECTS(
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
" management fee change mismatch: expected 0, got " +
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
BEAST_EXPECTS(
actualPaymentParts.principalPaid ==
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
" principalPaid mismatch: expected " +
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(actualPaymentParts.principalPaid));
}
@@ -672,7 +670,7 @@ class LendingHelpers_test : public beast::unit_test::suite
std::uint32_t const paymentsRemaining = 10;
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
auto const overpaymentComponents = computeOverpaymentComponents(
asset,
loanScale,
Number{50, 0},
@@ -680,17 +678,15 @@ class LendingHelpers_test : public beast::unit_test::suite
TenthBips32(10'000), // 10% overpayment fee
managementFeeRate);
auto const loanProperites = computeLoanProperties(
auto const loanProperties = computeLoanProperties(
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
Number const periodicPayment = loanProperites.periodicPayment;
auto const ret = tryOverpayment(
asset,
loanScale,
overpaymentComponents,
loanProperites.loanState,
periodicPayment,
loanProperties.loanState,
loanProperties.periodicPayment,
periodicRate,
paymentsRemaining,
managementFeeRate,
@@ -721,21 +717,21 @@ class LendingHelpers_test : public beast::unit_test::suite
// =========== VALIDATE STATE CHANGES ===========
// With no Loan interest, interest outstanding should not change
BEAST_EXPECTS(
loanProperites.loanState.interestDue - newState.interestDue == 0,
loanProperties.loanState.interestDue - newState.interestDue == 0,
" interest change mismatch: expected 0, got " +
to_string(loanProperites.loanState.interestDue - newState.interestDue));
to_string(loanProperties.loanState.interestDue - newState.interestDue));
// With no Loan management fee, management fee due should not change
BEAST_EXPECTS(
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
" management fee change mismatch: expected 0, got " +
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
BEAST_EXPECTS(
actualPaymentParts.principalPaid ==
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
" principalPaid mismatch: expected " +
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(actualPaymentParts.principalPaid));
}
@@ -758,7 +754,7 @@ class LendingHelpers_test : public beast::unit_test::suite
std::uint32_t const paymentsRemaining = 10;
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
auto const overpaymentComponents = computeOverpaymentComponents(
asset,
loanScale,
Number{50, 0},
@@ -766,17 +762,15 @@ class LendingHelpers_test : public beast::unit_test::suite
TenthBips32(0), // 0% overpayment fee
managementFeeRate);
auto const loanProperites = computeLoanProperties(
auto const loanProperties = computeLoanProperties(
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
Number const periodicPayment = loanProperites.periodicPayment;
auto const ret = tryOverpayment(
asset,
loanScale,
overpaymentComponents,
loanProperites.loanState,
periodicPayment,
loanProperties.loanState,
loanProperties.periodicPayment,
periodicRate,
paymentsRemaining,
managementFeeRate,
@@ -812,22 +806,22 @@ class LendingHelpers_test : public beast::unit_test::suite
// =========== VALIDATE STATE CHANGES ===========
BEAST_EXPECTS(
actualPaymentParts.principalPaid ==
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
" principalPaid mismatch: expected " +
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(actualPaymentParts.principalPaid));
BEAST_EXPECTS(
actualPaymentParts.valueChange == newState.interestDue - loanProperites.loanState.interestDue,
actualPaymentParts.valueChange == newState.interestDue - loanProperties.loanState.interestDue,
" valueChange mismatch: expected " +
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
to_string(actualPaymentParts.valueChange));
// With no Loan management fee, management fee due should not change
BEAST_EXPECTS(
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
" management fee change mismatch: expected 0, got " +
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
}
void
@@ -849,7 +843,7 @@ class LendingHelpers_test : public beast::unit_test::suite
std::uint32_t const paymentsRemaining = 10;
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
auto const overpaymentComponents = computeOverpaymentComponents(
asset,
loanScale,
Number{50, 0},
@@ -857,17 +851,15 @@ class LendingHelpers_test : public beast::unit_test::suite
TenthBips32(0), // 0% overpayment fee
managementFeeRate);
auto const loanProperites = computeLoanProperties(
auto const loanProperties = computeLoanProperties(
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
Number const periodicPayment = loanProperites.periodicPayment;
auto const ret = tryOverpayment(
asset,
loanScale,
overpaymentComponents,
loanProperites.loanState,
periodicPayment,
loanProperties.loanState,
loanProperties.periodicPayment,
periodicRate,
paymentsRemaining,
managementFeeRate,
@@ -904,26 +896,26 @@ class LendingHelpers_test : public beast::unit_test::suite
// =========== VALIDATE STATE CHANGES ===========
BEAST_EXPECTS(
actualPaymentParts.principalPaid ==
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
" principalPaid mismatch: expected " +
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(actualPaymentParts.principalPaid));
// The change in interest is equal to the value change sans the
// overpayment interest
BEAST_EXPECTS(
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
newState.interestDue - loanProperites.loanState.interestDue,
newState.interestDue - loanProperties.loanState.interestDue,
" valueChange mismatch: expected " +
to_string(
newState.interestDue - loanProperites.loanState.interestDue + actualPaymentParts.interestPaid) +
newState.interestDue - loanProperties.loanState.interestDue + actualPaymentParts.interestPaid) +
", got " + to_string(actualPaymentParts.valueChange));
// With no Loan management fee, management fee due should not change
BEAST_EXPECTS(
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
" management fee change mismatch: expected 0, got " +
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
}
void
@@ -947,7 +939,7 @@ class LendingHelpers_test : public beast::unit_test::suite
std::uint32_t const paymentsRemaining = 10;
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
auto const overpaymentComponents = computeOverpaymentComponents(
asset,
loanScale,
Number{50, 0},
@@ -955,17 +947,15 @@ class LendingHelpers_test : public beast::unit_test::suite
TenthBips32(0), // 0% overpayment fee
managementFeeRate);
auto const loanProperites = computeLoanProperties(
auto const loanProperties = computeLoanProperties(
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
Number const periodicPayment = loanProperites.periodicPayment;
auto const ret = tryOverpayment(
asset,
loanScale,
overpaymentComponents,
loanProperites.loanState,
periodicPayment,
loanProperties.loanState,
loanProperties.periodicPayment,
periodicRate,
paymentsRemaining,
managementFeeRate,
@@ -1004,23 +994,23 @@ class LendingHelpers_test : public beast::unit_test::suite
// =========== VALIDATE STATE CHANGES ===========
BEAST_EXPECTS(
actualPaymentParts.principalPaid ==
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
" principalPaid mismatch: expected " +
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(actualPaymentParts.principalPaid));
// Note that the management fee value change is not captured, as this
// value is not needed to correctly update the Vault state.
BEAST_EXPECTS(
(newState.managementFeeDue - loanProperites.loanState.managementFeeDue == Number{-20592, -5}),
(newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-20592, -5}),
" management fee change mismatch: expected " + to_string(Number{-20592, -5}) + ", got " +
to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
BEAST_EXPECTS(
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
newState.interestDue - loanProperites.loanState.interestDue,
newState.interestDue - loanProperties.loanState.interestDue,
" valueChange mismatch: expected " +
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
}
@@ -1043,7 +1033,7 @@ class LendingHelpers_test : public beast::unit_test::suite
std::uint32_t const paymentsRemaining = 10;
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
auto const overpaymentComponents = computeOverpaymentComponents(
asset,
loanScale,
Number{50, 0},
@@ -1051,17 +1041,15 @@ class LendingHelpers_test : public beast::unit_test::suite
TenthBips32(10'000), // 10% overpayment fee
managementFeeRate);
auto const loanProperites = computeLoanProperties(
auto const loanProperties = computeLoanProperties(
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
Number const periodicPayment = loanProperites.periodicPayment;
auto const ret = tryOverpayment(
asset,
loanScale,
overpaymentComponents,
loanProperites.loanState,
periodicPayment,
loanProperties.loanState,
loanProperties.periodicPayment,
periodicRate,
paymentsRemaining,
managementFeeRate,
@@ -1101,23 +1089,23 @@ class LendingHelpers_test : public beast::unit_test::suite
BEAST_EXPECTS(
actualPaymentParts.principalPaid ==
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
" principalPaid mismatch: expected " +
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
to_string(actualPaymentParts.principalPaid));
// Note that the management fee value change is not captured, as this
// value is not needed to correctly update the Vault state.
BEAST_EXPECTS(
(newState.managementFeeDue - loanProperites.loanState.managementFeeDue == Number{-18304, -5}),
(newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-18304, -5}),
" management fee change mismatch: expected " + to_string(Number{-18304, -5}) + ", got " +
to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
BEAST_EXPECTS(
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
newState.interestDue - loanProperites.loanState.interestDue,
newState.interestDue - loanProperties.loanState.interestDue,
" valueChange mismatch: expected " +
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
}

View File

@@ -827,8 +827,13 @@ public:
// applyManifest should accept new manifests with
// higher sequence numbers
auto const seq0 = cache.sequence();
BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::accepted);
BEAST_EXPECT(cache.sequence() > seq0);
auto const seq1 = cache.sequence();
BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
BEAST_EXPECT(cache.sequence() == seq1);
BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::accepted);
BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);

View File

@@ -490,19 +490,8 @@ public:
Env env(*this, envconfig(onlineDelete));
/////////////////////////////////////////////////////////////
// Create the backend. Normally, SHAMapStoreImp handles all these
// details
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
// Provide default values:
if (!nscfg.exists("cache_size"))
nscfg.set(
"cache_size", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
if (!nscfg.exists("cache_age"))
nscfg.set(
"cache_age", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
// Create NodeStore with two backends to allow online deletion of data.
// Normally, SHAMapStoreImp handles all these details.
NodeStoreScheduler scheduler(env.app().getJobQueue());
std::string const writableDb = "write";
@@ -510,9 +499,8 @@ public:
auto writableBackend = makeBackendRotating(env, scheduler, writableDb);
auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
// Create NodeStore with two backends to allow online deletion of
// data
constexpr int readThreads = 4;
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
scheduler,
readThreads,

View File

@@ -1,157 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/basics/CanProcess.h>
#include <xrpl/beast/unit_test.h>
#include <memory>
namespace ripple {
namespace test {
struct CanProcess_test : beast::unit_test::suite
{
template <class Mutex, class Collection, class Item>
void
test(std::string const& name, Mutex& mtx, Collection& collection, std::vector<Item> const& items)
{
testcase(name);
if (!BEAST_EXPECT(!items.empty()))
return;
if (!BEAST_EXPECT(collection.empty()))
return;
// CanProcess objects can't be copied or moved. To make that easier,
// store shared_ptrs
std::vector<std::shared_ptr<CanProcess>> trackers;
// Fill up the vector with two CanProcess for each Item. The first
// inserts the item into the collection and is "good". The second does
// not and is "bad".
for (int i = 0; i < items.size(); ++i)
{
{
auto const& good = trackers.emplace_back(std::make_shared<CanProcess>(mtx, collection, items[i]));
BEAST_EXPECT(*good);
}
BEAST_EXPECT(trackers.size() == (2 * i) + 1);
BEAST_EXPECT(collection.size() == i + 1);
{
auto const& bad = trackers.emplace_back(std::make_shared<CanProcess>(mtx, collection, items[i]));
BEAST_EXPECT(!*bad);
}
BEAST_EXPECT(trackers.size() == 2 * (i + 1));
BEAST_EXPECT(collection.size() == i + 1);
}
BEAST_EXPECT(collection.size() == items.size());
// Now remove the items from the vector<CanProcess> two at a time, and
// try to get another CanProcess for that item.
for (int i = 0; i < items.size(); ++i)
{
// Remove the "bad" one in the second position
// This will have no effect on the collection
{
auto const iter = trackers.begin() + 1;
BEAST_EXPECT(!**iter);
trackers.erase(iter);
}
BEAST_EXPECT(trackers.size() == (2 * items.size()) - 1);
BEAST_EXPECT(collection.size() == items.size());
{
// Append a new "bad" one
auto const& bad = trackers.emplace_back(std::make_shared<CanProcess>(mtx, collection, items[i]));
BEAST_EXPECT(!*bad);
}
BEAST_EXPECT(trackers.size() == 2 * items.size());
BEAST_EXPECT(collection.size() == items.size());
// Remove the "good" one from the front
{
auto const iter = trackers.begin();
BEAST_EXPECT(**iter);
trackers.erase(iter);
}
BEAST_EXPECT(trackers.size() == (2 * items.size()) - 1);
BEAST_EXPECT(collection.size() == items.size() - 1);
{
// Append a new "good" one
auto const& good = trackers.emplace_back(std::make_shared<CanProcess>(mtx, collection, items[i]));
BEAST_EXPECT(*good);
}
BEAST_EXPECT(trackers.size() == 2 * items.size());
BEAST_EXPECT(collection.size() == items.size());
}
// Now remove them all two at a time
for (int i = items.size() - 1; i >= 0; --i)
{
// Remove the "bad" one from the front
{
auto const iter = trackers.begin();
BEAST_EXPECT(!**iter);
trackers.erase(iter);
}
BEAST_EXPECT(trackers.size() == (2 * i) + 1);
BEAST_EXPECT(collection.size() == i + 1);
// Remove the "good" one now in front
{
auto const iter = trackers.begin();
BEAST_EXPECT(**iter);
trackers.erase(iter);
}
BEAST_EXPECT(trackers.size() == 2 * i);
BEAST_EXPECT(collection.size() == i);
}
BEAST_EXPECT(trackers.empty());
BEAST_EXPECT(collection.empty());
}
void
run() override
{
{
std::mutex m;
std::set<int> collection;
std::vector<int> const items{1, 2, 3, 4, 5};
test("set of int", m, collection, items);
}
{
std::mutex m;
std::set<std::string> collection;
std::vector<std::string> const items{"one", "two", "three", "four", "five"};
test("set of string", m, collection, items);
}
{
std::mutex m;
std::unordered_set<char> collection;
std::vector<char> const items{'1', '2', '3', '4', '5'};
test("unorderd_set of char", m, collection, items);
}
{
std::mutex m;
std::unordered_set<std::uint64_t> collection;
std::vector<std::uint64_t> const items{100u, 1000u, 150u, 4u, 0u};
test("unordered_set of uint64_t", m, collection, items);
}
}
};
BEAST_DEFINE_TESTSUITE(CanProcess, ripple_basics, ripple);
} // namespace test
} // namespace ripple

View File

@@ -100,7 +100,10 @@ RCLConsensus::Adaptor::acquireLedger(LedgerHash const& hash)
// Tell the ledger acquire system that we need the consensus ledger
acquiringLedger_ = hash;
app_.getInboundLedgers().acquireAsync(jtADVANCE, "GetConsL1", hash, 0, InboundLedger::Reason::CONSENSUS);
app_.getJobQueue().addJob(jtADVANCE, "GetConsL1", [id = hash, &app = app_, this]() {
JLOG(j_.debug()) << "JOB advanceLedger getConsensusLedger1 started";
app.getInboundLedgers().acquireAsync(id, 0, InboundLedger::Reason::CONSENSUS);
});
}
return std::nullopt;
}
@@ -915,7 +918,7 @@ void
RCLConsensus::Adaptor::updateOperatingMode(std::size_t const positions) const
{
if (!positions && app_.getOPs().isFull())
app_.getOPs().setMode(OperatingMode::CONNECTED, "updateOperatingMode: no positions");
app_.getOPs().setMode(OperatingMode::CONNECTED);
}
void

View File

@@ -109,7 +109,12 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash)
{
JLOG(j_.warn()) << "Need validated ledger for preferred ledger analysis " << hash;
app_.getInboundLedgers().acquireAsync(jtADVANCE, "GetConsL2", hash, 0, InboundLedger::Reason::CONSENSUS);
Application* pApp = &app_;
app_.getJobQueue().addJob(jtADVANCE, "GetConsL2", [pApp, hash, this]() {
JLOG(j_.debug()) << "JOB advanceLedger getConsensusLedger2 started";
pApp->getInboundLedgers().acquireAsync(hash, 0, InboundLedger::Reason::CONSENSUS);
});
return std::nullopt;
}

View File

@@ -26,12 +26,7 @@ public:
// Queue. TODO review whether all callers of acquire() can use this
// instead. Inbound ledger acquisition is asynchronous anyway.
virtual void
acquireAsync(
JobType type,
std::string const& name,
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) = 0;
acquireAsync(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) = 0;
virtual std::shared_ptr<InboundLedger>
find(LedgerHash const& hash) = 0;

View File

@@ -344,14 +344,7 @@ InboundLedger::onTimer(bool wasProgress, ScopedLockType&)
if (!wasProgress)
{
if (checkLocal())
{
// Done. Something else (probably consensus) built the ledger
// locally while waiting for data (or possibly before requesting)
XRPL_ASSERT(isDone(), "ripple::InboundLedger::onTimer : done");
JLOG(journal_.info()) << "Finished while waiting " << hash_;
return;
}
checkLocal();
mByHash = true;

View File

@@ -3,9 +3,9 @@
#include <xrpld/app/main/Application.h>
#include <xrpld/app/misc/NetworkOPs.h>
#include <xrpl/basics/CanProcess.h>
#include <xrpl/basics/DecayingSample.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/scope.h>
#include <xrpl/beast/container/aged_map.h>
#include <xrpl/core/JobQueue.h>
#include <xrpl/core/PerfLog.h>
@@ -58,15 +58,12 @@ public:
(reason != InboundLedger::Reason::CONSENSUS))
return {};
std::stringstream ss;
bool isNew = true;
std::shared_ptr<InboundLedger> inbound;
{
ScopedLockType sl(mLock);
if (stopping_)
{
JLOG(j_.debug()) << "Abort(stopping): " << ss.str();
return {};
}
@@ -85,60 +82,46 @@ public:
++mCounter;
}
}
ss << " IsNew: " << (isNew ? "true" : "false");
if (inbound->isFailed())
{
JLOG(j_.debug()) << "Abort(failed): " << ss.str();
return {};
}
if (!isNew)
inbound->update(seq);
if (!inbound->isComplete())
{
JLOG(j_.debug()) << "InProgress: " << ss.str();
return {};
}
JLOG(j_.debug()) << "Complete: " << ss.str();
return inbound->getLedger();
};
using namespace std::chrono_literals;
return perf::measureDurationAndLog(doAcquire, "InboundLedgersImp::acquire", 500ms, j_);
std::shared_ptr<Ledger const> ledger =
perf::measureDurationAndLog(doAcquire, "InboundLedgersImp::acquire", 500ms, j_);
return ledger;
}
void
acquireAsync(
JobType type,
std::string const& name,
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) override
acquireAsync(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override
{
if (auto check = std::make_shared<CanProcess const>(acquiresMutex_, pendingAcquires_, hash); *check)
std::unique_lock lock(acquiresMutex_);
try
{
app_.getJobQueue().addJob(type, name, [check, name, hash, seq, reason, this]() {
JLOG(j_.debug()) << "JOB acquireAsync " << name << " started ";
try
{
acquire(hash, seq, reason);
}
catch (std::exception const& e)
{
JLOG(j_.warn()) << "Exception thrown for acquiring new "
"inbound ledger "
<< hash << ": " << e.what();
}
catch (...)
{
JLOG(j_.warn()) << "Unknown exception thrown for acquiring new "
"inbound ledger "
<< hash;
}
});
if (pendingAcquires_.contains(hash))
return;
pendingAcquires_.insert(hash);
scope_unlock unlock(lock);
acquire(hash, seq, reason);
}
catch (std::exception const& e)
{
JLOG(j_.warn()) << "Exception thrown for acquiring new inbound ledger " << hash << ": " << e.what();
}
catch (...)
{
JLOG(j_.warn()) << "Unknown exception thrown for acquiring new inbound ledger " << hash;
}
pendingAcquires_.erase(hash);
}
std::shared_ptr<InboundLedger>

View File

@@ -890,8 +890,8 @@ LedgerMaster::checkAccept(std::shared_ptr<Ledger const> const& ledger)
return;
}
JLOG(m_journal.info()) << "Advancing accepted ledger to " << ledger->header().seq << " ("
<< to_short_string(ledger->header().hash) << ") with >= " << minVal << " validations";
JLOG(m_journal.info()) << "Advancing accepted ledger to " << ledger->header().seq << " with >= " << minVal
<< " validations";
ledger->setValidated();
ledger->setFull();

View File

@@ -13,8 +13,7 @@ TimeoutCounter::TimeoutCounter(
QueueJobParameter&& jobParameter,
beast::Journal journal)
: app_(app)
, sink_(journal, to_short_string(hash) + " ")
, journal_(sink_)
, journal_(journal)
, hash_(hash)
, timeouts_(0)
, complete_(false)
@@ -34,7 +33,6 @@ TimeoutCounter::setTimer(ScopedLockType& sl)
{
if (isDone())
return;
JLOG(journal_.debug()) << "Setting timer for " << timerInterval_.count() << "ms";
timer_.expires_after(timerInterval_);
timer_.async_wait([wptr = pmDowncast()](boost::system::error_code const& ec) {
if (ec == boost::asio::error::operation_aborted)
@@ -42,9 +40,6 @@ TimeoutCounter::setTimer(ScopedLockType& sl)
if (auto ptr = wptr.lock())
{
JLOG(ptr->journal_.debug()) << "timer: ec: " << ec
<< " (operation_aborted: " << boost::asio::error::operation_aborted << " - "
<< (ec == boost::asio::error::operation_aborted ? "aborted" : "other") << ")";
ScopedLockType sl(ptr->mtx_);
ptr->queueJob(sl);
}

View File

@@ -3,7 +3,6 @@
#include <xrpld/app/main/Application.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/WrappedSink.h>
#include <xrpl/core/Job.h>
#include <boost/asio/basic_waitable_timer.hpp>
@@ -104,7 +103,6 @@ protected:
// Used in this class for access to boost::asio::io_context and
// xrpl::Overlay. Used in subtypes for the kitchen sink.
Application& app_;
beast::WrappedSink sink_;
beast::Journal journal_;
mutable std::recursive_mutex mtx_;

View File

@@ -908,10 +908,6 @@ public:
JLOG(m_journal.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize
<< "; size after: " << masterTxCache.size();
}
{
// Does not appear to have an associated cache.
getNodeStore().sweep();
}
{
std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize();

View File

@@ -199,7 +199,7 @@ public:
/** Add a suppression peer and get message's relay status.
* Return pair:
* element 1: true if the key is added.
* element 1: true if the peer is added.
* element 2: optional is seated to the relay time point or
* is unseated if has not relayed yet. */
std::pair<bool, std::optional<Stopwatch::time_point>>

View File

@@ -33,10 +33,10 @@
#include <xrpld/rpc/MPTokenIssuanceID.h>
#include <xrpld/rpc/ServerHandler.h>
#include <xrpl/basics/CanProcess.h>
#include <xrpl/basics/UptimeClock.h>
#include <xrpl/basics/mulDiv.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/basics/scope.h>
#include <xrpl/beast/utility/rngfill.h>
#include <xrpl/core/PerfLog.h>
#include <xrpl/crypto/RFC1751.h>
@@ -378,7 +378,7 @@ public:
isFull() override;
void
setMode(OperatingMode om, char const* reason) override;
setMode(OperatingMode om) override;
bool
isBlocked() override;
@@ -809,7 +809,7 @@ NetworkOPsImp::strOperatingMode(bool const admin /* = false */) const
inline void
NetworkOPsImp::setStandAlone()
{
setMode(OperatingMode::FULL, "setStandAlone");
setMode(OperatingMode::FULL);
}
inline void
@@ -945,7 +945,7 @@ NetworkOPsImp::processHeartbeatTimer()
{
if (mMode != OperatingMode::DISCONNECTED)
{
setMode(OperatingMode::DISCONNECTED, "Heartbeat: insufficient peers");
setMode(OperatingMode::DISCONNECTED);
std::stringstream ss;
ss << "Node count (" << numPeers << ") has fallen "
<< "below required minimum (" << minPeerCount_ << ").";
@@ -969,7 +969,7 @@ NetworkOPsImp::processHeartbeatTimer()
if (mMode == OperatingMode::DISCONNECTED)
{
setMode(OperatingMode::CONNECTED, "Heartbeat: sufficient peers");
setMode(OperatingMode::CONNECTED);
JLOG(m_journal.info()) << "Node count (" << numPeers << ") is sufficient.";
CLOG(clog.ss()) << "setting mode to CONNECTED based on " << numPeers << " peers. ";
}
@@ -979,9 +979,9 @@ NetworkOPsImp::processHeartbeatTimer()
auto origMode = mMode.load();
CLOG(clog.ss()) << "mode: " << strOperatingMode(origMode, true);
if (mMode == OperatingMode::SYNCING)
setMode(OperatingMode::SYNCING, "Heartbeat: check syncing");
setMode(OperatingMode::SYNCING);
else if (mMode == OperatingMode::CONNECTED)
setMode(OperatingMode::CONNECTED, "Heartbeat: check connected");
setMode(OperatingMode::CONNECTED);
auto newMode = mMode.load();
if (origMode != newMode)
{
@@ -1650,7 +1650,7 @@ void
NetworkOPsImp::setAmendmentBlocked()
{
amendmentBlocked_ = true;
setMode(OperatingMode::CONNECTED, "setAmendmentBlocked");
setMode(OperatingMode::CONNECTED);
}
inline bool
@@ -1681,7 +1681,7 @@ void
NetworkOPsImp::setUNLBlocked()
{
unlBlocked_ = true;
setMode(OperatingMode::CONNECTED, "setUNLBlocked");
setMode(OperatingMode::CONNECTED);
}
inline void
@@ -1776,7 +1776,7 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint
if ((mMode == OperatingMode::TRACKING) || (mMode == OperatingMode::FULL))
{
setMode(OperatingMode::CONNECTED, "check LCL: not on consensus ledger");
setMode(OperatingMode::CONNECTED);
}
if (consensus)
@@ -1856,8 +1856,8 @@ NetworkOPsImp::beginConsensus(uint256 const& networkClosed, std::unique_ptr<std:
// this shouldn't happen unless we jump ledgers
if (mMode == OperatingMode::FULL)
{
JLOG(m_journal.warn()) << "beginConsensus Don't have LCL, going to tracking";
setMode(OperatingMode::TRACKING, "beginConsensus: No LCL");
JLOG(m_journal.warn()) << "Don't have LCL, going to tracking";
setMode(OperatingMode::TRACKING);
CLOG(clog) << "beginConsensus Don't have LCL, going to tracking. ";
}
@@ -1981,7 +1981,7 @@ NetworkOPsImp::endConsensus(std::unique_ptr<std::stringstream> const& clog)
// validations we have for LCL. If the ledger is good enough, go to
// TRACKING - TODO
if (!needNetworkLedger_)
setMode(OperatingMode::TRACKING, "endConsensus: check tracking");
setMode(OperatingMode::TRACKING);
}
if (((mMode == OperatingMode::CONNECTED) || (mMode == OperatingMode::TRACKING)) && !ledgerChange)
@@ -1992,7 +1992,7 @@ NetworkOPsImp::endConsensus(std::unique_ptr<std::stringstream> const& clog)
auto current = m_ledgerMaster.getCurrentLedger();
if (app_.timeKeeper().now() < (current->header().parentCloseTime + 2 * current->header().closeTimeResolution))
{
setMode(OperatingMode::FULL, "endConsensus: check full");
setMode(OperatingMode::FULL);
}
}
@@ -2004,7 +2004,7 @@ NetworkOPsImp::consensusViewChange()
{
if ((mMode == OperatingMode::FULL) || (mMode == OperatingMode::TRACKING))
{
setMode(OperatingMode::CONNECTED, "consensusViewChange");
setMode(OperatingMode::CONNECTED);
}
}
@@ -2302,7 +2302,7 @@ NetworkOPsImp::pubPeerStatus(std::function<Json::Value(void)> const& func)
}
void
NetworkOPsImp::setMode(OperatingMode om, char const* reason)
NetworkOPsImp::setMode(OperatingMode om)
{
using namespace std::chrono_literals;
if (om == OperatingMode::CONNECTED)
@@ -2322,12 +2322,11 @@ NetworkOPsImp::setMode(OperatingMode om, char const* reason)
if (mMode == om)
return;
auto const sink = om < mMode ? m_journal.warn() : m_journal.info();
mMode = om;
accounting_.mode(om);
JLOG(sink) << "STATE->" << strOperatingMode() << " - " << reason;
JLOG(m_journal.info()) << "STATE->" << strOperatingMode();
pubServer();
}
@@ -2336,23 +2335,31 @@ NetworkOPsImp::recvValidation(std::shared_ptr<STValidation> const& val, std::str
{
JLOG(m_journal.trace()) << "recvValidation " << val->getLedgerHash() << " from " << source;
std::unique_lock lock(validationsMutex_);
BypassAccept bypassAccept = BypassAccept::no;
try
{
CanProcess const check(validationsMutex_, pendingValidations_, val->getLedgerHash());
try
{
BypassAccept bypassAccept = check ? BypassAccept::no : BypassAccept::yes;
handleNewValidation(app_, val, source, bypassAccept, m_journal);
}
catch (std::exception const& e)
{
JLOG(m_journal.warn()) << "Exception thrown for handling new validation " << val->getLedgerHash() << ": "
<< e.what();
}
catch (...)
{
JLOG(m_journal.warn()) << "Unknown exception thrown for handling new validation " << val->getLedgerHash();
}
if (pendingValidations_.contains(val->getLedgerHash()))
bypassAccept = BypassAccept::yes;
else
pendingValidations_.insert(val->getLedgerHash());
scope_unlock unlock(lock);
handleNewValidation(app_, val, source, bypassAccept, m_journal);
}
catch (std::exception const& e)
{
JLOG(m_journal.warn()) << "Exception thrown for handling new validation " << val->getLedgerHash() << ": "
<< e.what();
}
catch (...)
{
JLOG(m_journal.warn()) << "Unknown exception thrown for handling new validation " << val->getLedgerHash();
}
if (bypassAccept == BypassAccept::no)
{
pendingValidations_.erase(val->getLedgerHash());
}
lock.unlock();
pubValidation(val);

View File

@@ -179,7 +179,7 @@ public:
virtual bool
isFull() = 0;
virtual void
setMode(OperatingMode om, char const* reason) = 0;
setMode(OperatingMode om) = 0;
virtual bool
isBlocked() = 0;
virtual bool

View File

@@ -130,14 +130,6 @@ std::unique_ptr<NodeStore::Database>
SHAMapStoreImp::makeNodeStore(int readThreads)
{
auto nscfg = app_.config().section(ConfigSection::nodeDatabase());
// Provide default values:
if (!nscfg.exists("cache_size"))
nscfg.set("cache_size", std::to_string(app_.config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
if (!nscfg.exists("cache_age"))
nscfg.set("cache_age", std::to_string(app_.config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
std::unique_ptr<NodeStore::Database> db;
if (deleteInterval_)
@@ -226,8 +218,6 @@ SHAMapStoreImp::run()
LedgerIndex lastRotated = state_db_.getState().lastRotated;
netOPs_ = &app_.getOPs();
ledgerMaster_ = &app_.getLedgerMaster();
fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache());
treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache());
if (advisoryDelete_)
canDelete_ = state_db_.getCanDelete();
@@ -490,16 +480,19 @@ void
SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
{
ledgerMaster_->clearLedgerCachePrior(validatedSeq);
fullBelowCache_->clear();
// Also clear the FullBelowCache so its generation counter is bumped.
// This prevents stale "full below" markers from persisting across
// backend rotation/online deletion and interfering with SHAMap sync.
app_.getNodeFamily().getFullBelowCache()->clear();
}
void
SHAMapStoreImp::freshenCaches()
{
if (freshenCache(*treeNodeCache_))
return;
if (freshenCache(app_.getMasterTransaction().getCache()))
if (freshenCache(*app_.getNodeFamily().getTreeNodeCache()))
return;
freshenCache(app_.getMasterTransaction().getCache());
}
void

View File

@@ -93,8 +93,6 @@ private:
// as of run() or before
NetworkOPs* netOPs_ = nullptr;
LedgerMaster* ledgerMaster_ = nullptr;
FullBelowCache* fullBelowCache_ = nullptr;
TreeNodeCache* treeNodeCache_ = nullptr;
static constexpr auto nodeStoreName_ = "NodeStore";

View File

@@ -459,6 +459,10 @@ ManifestCache::applyManifest(Manifest m)
auto masterKey = m.masterKey;
map_.emplace(std::move(masterKey), std::move(m));
// Something has changed. Keep track of it.
seq_++;
return ManifestDisposition::accepted;
}