Compare commits

..

24 Commits

Author SHA1 Message Date
tequ
17f8ae3ffa Merge branch 'dev' into cli-definitions-output 2026-04-29 11:16:16 +09:00
tequ
8cd8d05b77 Merge remote-tracking branch 'upstream/dev' into cli-definitions-output 2026-04-29 10:46:53 +09:00
tequ
ea835da1f4 Merge branch 'dev' into cli-definitions-output 2026-04-24 14:24:24 +09:00
Alloy Networks
cd00ed72d8 change build instructions url 2026-04-24 11:12:28 +10:00
tequ
05a3e04f2d Fix BEAST_ENHANCED_LOGGING not working and restore original behavior 2026-04-24 11:11:40 +10:00
tequ
66f7294120 Test: hint build_test_hooks.sh when hook wasm is empty in hso() 2026-04-24 11:10:46 +10:00
Nicholas Dudfield
7f6ac75617 Revert "chore: use improved levelization script with threading and argparse"
This reverts commit 5c1d7d9ae9.
2026-04-24 11:09:19 +10:00
Nicholas Dudfield
4150f0383c chore: use improved levelization script with threading and argparse 2026-04-24 11:09:19 +10:00
Nicholas Dudfield
25123b370a chore: replace levelization shell script with python
Backport of XRPLF/rippled#6325. The python version runs ~80x faster.
2026-04-24 11:09:19 +10:00
tequ
f90ed41802 enable ccache direct_mode 2026-04-24 11:06:51 +10:00
tequ
8c4c158d3a output ccache configuration in release-builder 2026-04-24 11:06:51 +10:00
tequ
2d2951875d fix: typo SignersListSet 2026-04-24 11:05:20 +10:00
tequ
9bfca63574 Update util_keylet fee test 2026-04-24 11:00:31 +10:00
tequ
1ba444ae7f Updated tests to align with the changes merged into the dev branch. 2026-04-24 11:00:31 +10:00
tequ
f96d9b6e51 Add tests for Hooks fee 2026-04-24 11:00:31 +10:00
tequ
4bfd1966da actions/upload-artifact@v7 2026-03-09 23:30:35 +09:00
tequ
0e0a46b2af ci: remove conditional check for GCC 13 in server definitions export 2026-03-09 18:42:49 +09:00
tequ
47a12fe33b ci: export server definitions JSON artifact from workflow 2026-03-09 18:30:19 +09:00
tequ
4403c4f427 Add --definitions CLI flag to output static server definitions without starting server 2026-03-09 18:16:40 +09:00
tequ
8cfee6c8a3 Merge fixAMMClawbackRounding amendment into featureAMMClawback amendment 2026-02-25 19:07:45 +10:00
yinyiqian1
8673599d2b fixAMMClawbackRounding: adjust last holder's LPToken balance (#5513)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance. This was fixed for `AMMWithdraw` in `fixAMMv1_1` by adjusting the LPTokenBalance to be the same as the trustline balance. Since `AMMClawback` is also performing a withdrawal, we need to adjust LPTokenBalance as well in `AMMClawback.`

This change includes:
1. Refactored `verifyAndAdjustLPTokenBalance` function in `AMMUtils`, which both`AMMWithdraw` and `AMMClawback` call to adjust LPTokenBalance.
2. Added the unit test `testLastHolderLPTokenBalance` to test the scenario.
3. Modify the existing unit tests for `fixAMMClawbackRounding`.
2026-02-25 19:07:45 +10:00
tequ
ec65e622aa Merge fixAMMv1_3 amendment into featureAMM amendment 2026-02-25 16:20:43 +10:00
Gregory Tsipenyuk
65837f49e1 fix: Add AMMv1_3 amendment (#5203)
* Add AMM bid/create/deposit/swap/withdraw/vote invariants:
  - Deposit, Withdrawal invariants: `sqrt(asset1Balance * asset2Balance) >= LPTokens`.
  - Bid: `sqrt(asset1Balance * asset2Balance) > LPTokens` and the pool balances don't change.
  - Create: `sqrt(asset1Balance * assetBalance2) == LPTokens`.
  - Swap: `asset1BalanceAfter * asset2BalanceAfter >= asset1BalanceBefore * asset2BalanceBefore`
     and `LPTokens` don't change.
  - Vote: `LPTokens` and pool balances don't change.
  - All AMM and swap transactions: amounts and tokens are greater than zero, except on withdrawal if all tokens
    are withdrawn.
* Add AMM deposit and withdraw rounding to ensure AMM invariant:
  - On deposit, tokens out are rounded downward and deposit amount is rounded upward.
  - On withdrawal, tokens in are rounded upward and withdrawal amount is rounded downward.
* Add Order Book Offer invariant to verify consumed amounts. Consumed amounts are less than the offer.
* Fix Bid validation. `AuthAccount` can't have duplicate accounts or the submitter account.
2026-02-25 16:20:43 +10:00
RichardAH
e5b21f026e Merge pull request #692 from Xahau/sync-2.4.0-rebased
Sync: Ripple(d) 2.4.0
2026-02-24 16:07:09 +10:00
30 changed files with 1537 additions and 593 deletions

View File

@@ -20,7 +20,7 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install clang-format-${CLANG_VERSION} sudo apt-get install clang-format-${CLANG_VERSION}
- name: Format first-party sources - name: Format first-party sources
run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format-${CLANG_VERSION} -i {} + run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -not -path "src/magic/magic_enum.h" -exec clang-format-${CLANG_VERSION} -i {} +
- name: Check for differences - name: Check for differences
id: assert id: assert
run: | run: |

View File

@@ -18,10 +18,6 @@ jobs:
generator: bash ./hook/generate_sfcodes.sh generator: bash ./hook/generate_sfcodes.sh
- target: hook/tts.h - target: hook/tts.h
generator: ./hook/generate_tts.sh generator: ./hook/generate_tts.sh
- target: hook/ls_flags.h
generator: ./hook/generate_lsflags.sh
- target: hook/tx_flags.h
generator: ./hook/generate_txflags.sh
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
env: env:
CLANG_VERSION: 18 CLANG_VERSION: 18

View File

@@ -464,3 +464,16 @@ jobs:
verbose: true verbose: true
plugins: noop plugins: noop
use_oidc: true use_oidc: true
- name: Export server definitions
if: matrix.job_type == 'build' && matrix.compiler_id == 'gcc-13-libstdcxx'
run: |
${{ env.build_dir }}/rippled --definitions | python3 -m json.tool > server_definitions.json
- name: Upload server definitions
if: matrix.job_type == 'build' && matrix.compiler_id == 'gcc-13-libstdcxx'
uses: actions/upload-artifact@v7
with:
name: server-definitions
path: server_definitions.json
archive: false

View File

@@ -122,7 +122,6 @@ endif()
find_package(nudb REQUIRED) find_package(nudb REQUIRED)
find_package(date REQUIRED) find_package(date REQUIRED)
find_package(xxHash REQUIRED) find_package(xxHash REQUIRED)
find_package(magic_enum REQUIRED)
include(deps/WasmEdge) include(deps/WasmEdge)
if(TARGET nudb::core) if(TARGET nudb::core)

View File

@@ -380,7 +380,6 @@ function(setup_target_for_coverage_gcovr)
${GCOVR_PATH} ${GCOVR_PATH}
--gcov-executable ${GCOV_TOOL} --gcov-executable ${GCOV_TOOL}
--gcov-ignore-parse-errors=negative_hits.warn_once_per_file --gcov-ignore-parse-errors=negative_hits.warn_once_per_file
--gcov-ignore-parse-errors=suspicious_hits.warn_once_per_file
-r ${BASEDIR} -r ${BASEDIR}
${GCOVR_ADDITIONAL_ARGS} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} ${GCOVR_EXCLUDE_ARGS}

View File

@@ -54,7 +54,6 @@ add_library(xrpl.imports.main INTERFACE)
target_link_libraries(xrpl.imports.main target_link_libraries(xrpl.imports.main
INTERFACE INTERFACE
LibArchive::LibArchive LibArchive::LibArchive
magic_enum::magic_enum
OpenSSL::Crypto OpenSSL::Crypto
Ripple::boost Ripple::boost
wasmedge::wasmedge wasmedge::wasmedge

View File

@@ -29,7 +29,6 @@ class Xrpl(ConanFile):
'date/3.0.3', 'date/3.0.3',
'grpc/1.50.1', 'grpc/1.50.1',
'libarchive/3.7.6', 'libarchive/3.7.6',
'magic_enum/0.9.5',
'nudb/2.0.8', 'nudb/2.0.8',
'openssl/3.6.0', 'openssl/3.6.0',
'soci/4.0.3@xahaud/stable', 'soci/4.0.3@xahaud/stable',

View File

@@ -1,82 +0,0 @@
#!/bin/bash
set -eu
SCRIPT_DIR=$(dirname "$0")
SCRIPT_DIR=$(cd "$SCRIPT_DIR" && pwd)
RIPPLED_ROOT="$SCRIPT_DIR/../include/xrpl"
LEDGER_FORMATS="$RIPPLED_ROOT/protocol/LedgerFormats.h"
echo '// Generated using generate_lsflags.sh'
echo ''
echo '#ifndef HOOKLSFLAGS_INCLUDED'
echo '#define HOOKLSFLAGS_INCLUDED 1'
echo ''
awk '
function ltrim(s) { sub(/^[[:space:]]+/, "", s); return s }
function rtrim(s) { sub(/[[:space:]]+$/, "", s); return s }
function trim(s) { return rtrim(ltrim(s)) }
function flush_group() {
if (entry_count > 0 && group != "") {
printf "enum %s {\n", group
for (i = 1; i <= entry_count; i++) {
printf " %s,\n", entries[i]
}
printf "};\n"
}
delete entries
entry_count = 0
}
/enum LedgerSpecificFlags \{/ { inside = 1; next }
inside && /^\};/ { inside = 0; flush_group(); next }
!inside { next }
# Group header comments: // ltFOO or // remarks
/^[[:space:]]*\/\/[[:space:]]*(lt[A-Z_]+|remarks)[[:space:]]*$/ {
flush_group()
line = $0
sub(/.*\/\/[[:space:]]*/, "", line)
group = trim(line)
next
}
# Skip pure comment lines (not group headers)
/^[[:space:]]*\/\// { next }
# Skip blank lines
/^[[:space:]]*$/ { next }
# Accumulate flag lines (handle multi-line values)
{
line = $0
# Strip inline comments
sub(/\/\/.*/, "", line)
line = trim(line)
if (line == "") next
if (pending != "") {
pending = pending " " line
} else {
pending = line
}
# If line ends with comma, the entry is complete
if (pending ~ /,$/) {
# Remove trailing comma
sub(/,$/, "", pending)
entries[++entry_count] = pending
pending = ""
}
}
BEGIN {
inside = 0
group = ""
pending = ""
entry_count = 0
}
' "$LEDGER_FORMATS"
echo ''
echo '#endif // HOOKLSFLAGS_INCLUDED'

View File

@@ -1,25 +0,0 @@
#!/bin/bash
set -eu
SCRIPT_DIR=$(dirname "$0")
SCRIPT_DIR=$(cd "$SCRIPT_DIR" && pwd)
RIPPLED_ROOT="$SCRIPT_DIR/../include/xrpl"
TX_FLAGS="$RIPPLED_ROOT/protocol/TxFlags.h"
echo '// Generated using generate_txflags.sh'
echo '#include "ls_flags.h"'
echo '#include <stdint.h>'
echo ''
cat "$TX_FLAGS" |
awk '
/^[[:space:]]*enum / {
if (count > 0) print ""
inside = 1
count++
}
inside {
print
if (/};/) inside = 0
}
'

View File

@@ -49,7 +49,4 @@
#include "macro.h" #include "macro.h"
#include "tts.h" #include "tts.h"
#include "ls_flags.h"
#include "tx_flags.h"
#endif #endif

View File

@@ -1,75 +0,0 @@
// Generated using generate_lsflags.sh
#ifndef HOOKLSFLAGS_INCLUDED
#define HOOKLSFLAGS_INCLUDED 1
enum ltACCOUNT_ROOT {
lsfPasswordSpent = 0x00010000,
lsfRequireDestTag = 0x00020000,
lsfRequireAuth = 0x00040000,
lsfDisallowXRP = 0x00080000,
lsfDisableMaster = 0x00100000,
lsfNoFreeze = 0x00200000,
lsfGlobalFreeze = 0x00400000,
lsfDefaultRipple = 0x00800000,
lsfDepositAuth = 0x01000000,
lsfTshCollect = 0x02000000,
lsfDisallowIncomingNFTokenOffer = 0x04000000,
lsfDisallowIncomingCheck = 0x08000000,
lsfDisallowIncomingPayChan = 0x10000000,
lsfDisallowIncomingTrustline = 0x20000000,
lsfURITokenIssuer = 0x40000000,
lsfDisallowIncomingRemit = 0x80000000,
lsfAllowTrustLineClawback = 0x00001000,
};
enum ltOFFER {
lsfPassive = 0x00010000,
lsfSell = 0x00020000,
};
enum ltRIPPLE_STATE {
lsfLowReserve = 0x00010000,
lsfHighReserve = 0x00020000,
lsfLowAuth = 0x00040000,
lsfHighAuth = 0x00080000,
lsfLowNoRipple = 0x00100000,
lsfHighNoRipple = 0x00200000,
lsfLowFreeze = 0x00400000,
lsfHighFreeze = 0x00800000,
lsfLowDeepFreeze = 0x02000000,
lsfHighDeepFreeze = 0x04000000,
lsfAMMNode = 0x01000000,
};
enum ltSIGNER_LIST {
lsfOneOwnerCount = 0x00010000,
};
enum ltDIR_NODE {
lsfNFTokenBuyOffers = 0x00000001,
lsfNFTokenSellOffers = 0x00000002,
lsfEmittedDir = 0x00000004,
};
enum ltNFTOKEN_OFFER {
lsfSellNFToken = 0x00000001,
};
enum ltURI_TOKEN {
lsfBurnable = 0x00000001,
};
enum remarks {
lsfImmutable = 1,
};
enum ltMPTOKEN_ISSUANCE {
lsfMPTLocked = 0x00000001,
lsfMPTCanLock = 0x00000002,
lsfMPTRequireAuth = 0x00000004,
lsfMPTCanEscrow = 0x00000008,
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
};
enum ltMPTOKEN {
lsfMPTAuthorized = 0x00000002,
};
enum ltCREDENTIAL {
lsfAccepted = 0x00010000,
};
#endif // HOOKLSFLAGS_INCLUDED

View File

@@ -274,6 +274,7 @@
#define sfDisabledValidator ((14U << 16U) + 19U) #define sfDisabledValidator ((14U << 16U) + 19U)
#define sfEmittedTxn ((14U << 16U) + 20U) #define sfEmittedTxn ((14U << 16U) + 20U)
#define sfHookExecution ((14U << 16U) + 21U) #define sfHookExecution ((14U << 16U) + 21U)
#define sfHookDefinition ((14U << 16U) + 22U)
#define sfHookParameter ((14U << 16U) + 23U) #define sfHookParameter ((14U << 16U) + 23U)
#define sfHookGrant ((14U << 16U) + 24U) #define sfHookGrant ((14U << 16U) + 24U)
#define sfVoteEntry ((14U << 16U) + 25U) #define sfVoteEntry ((14U << 16U) + 25U)

View File

@@ -1,117 +0,0 @@
// Generated using generate_txflags.sh
#include "ls_flags.h"
#include <stdint.h>
enum UniversalFlags : uint32_t {
tfFullyCanonicalSig = 0x80000000,
};
enum AccountSetFlags : uint32_t {
tfRequireDestTag = 0x00010000,
tfOptionalDestTag = 0x00020000,
tfRequireAuth = 0x00040000,
tfOptionalAuth = 0x00080000,
tfDisallowXRP = 0x00100000,
tfAllowXRP = 0x00200000,
};
enum AccountFlags : uint32_t {
asfRequireDest = 1,
asfRequireAuth = 2,
asfDisallowXRP = 3,
asfDisableMaster = 4,
asfAccountTxnID = 5,
asfNoFreeze = 6,
asfGlobalFreeze = 7,
asfDefaultRipple = 8,
asfDepositAuth = 9,
asfAuthorizedNFTokenMinter = 10,
asfTshCollect = 11,
asfDisallowIncomingNFTokenOffer = 12,
asfDisallowIncomingCheck = 13,
asfDisallowIncomingPayChan = 14,
asfDisallowIncomingTrustline = 15,
asfDisallowIncomingRemit = 16,
asfAllowTrustLineClawback = 17,
};
enum OfferCreateFlags : uint32_t {
tfPassive = 0x00010000,
tfImmediateOrCancel = 0x00020000,
tfFillOrKill = 0x00040000,
tfSell = 0x00080000,
};
enum PaymentFlags : uint32_t {
tfNoRippleDirect = 0x00010000,
tfPartialPayment = 0x00020000,
tfLimitQuality = 0x00040000,
};
enum TrustSetFlags : uint32_t {
tfSetfAuth = 0x00010000,
tfSetNoRipple = 0x00020000,
tfClearNoRipple = 0x00040000,
tfSetFreeze = 0x00100000,
tfClearFreeze = 0x00200000,
tfSetDeepFreeze = 0x00400000,
tfClearDeepFreeze = 0x00800000
};
enum EnableAmendmentFlags : uint32_t {
tfGotMajority = 0x00010000,
tfLostMajority = 0x00020000,
tfTestSuite = 0x80000000,
};
enum PaymentChannelClaimFlags : uint32_t {
tfRenew = 0x00010000,
tfClose = 0x00020000,
};
enum NFTokenMintFlags : uint32_t {
tfBurnable = 0x00000001,
tfOnlyXRP = 0x00000002,
tfTrustLine = 0x00000004,
tfTransferable = 0x00000008,
tfMutable = 0x00000010,
tfStrongTSH = 0x00008000,
};
enum MPTokenIssuanceCreateFlags : uint32_t {
tfMPTCanLock = lsfMPTCanLock,
tfMPTRequireAuth = lsfMPTRequireAuth,
tfMPTCanEscrow = lsfMPTCanEscrow,
tfMPTCanTrade = lsfMPTCanTrade,
tfMPTCanTransfer = lsfMPTCanTransfer,
tfMPTCanClawback = lsfMPTCanClawback,
};
enum MPTokenAuthorizeFlags : uint32_t {
tfMPTUnauthorize = 0x00000001,
};
enum MPTokenIssuanceSetFlags : uint32_t {
tfMPTLock = 0x00000001,
tfMPTUnlock = 0x00000002,
};
enum NFTokenCreateOfferFlags : uint32_t {
tfSellNFToken = 0x00000001,
};
enum ClaimRewardFlags : uint32_t {
tfOptOut = 0x00000001,
};
enum CronSetFlags : uint32_t {
tfCronUnset = 0x00000001,
};
enum AMMClawbackFlags : uint32_t {
tfClawTwoAssets = 0x00000001,
};
enum BridgeModifyFlags : uint32_t {
tfClearAccountCreateAmount = 0x00010000,
};

View File

@@ -80,7 +80,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how // Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this. // the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 115; static constexpr std::size_t numFeatures = 114;
/** Amendments that this server supports and the default voting behavior. /** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated Whether they are enabled depends on the Rules defined in the validated

View File

@@ -132,7 +132,7 @@ constexpr std::uint32_t tfTrustSetMask =
tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze); tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze);
// EnableAmendment flags: // EnableAmendment flags:
enum EnableAmendmentFlags : uint32_t { enum EnableAmendmentFlags : std::uint32_t {
tfGotMajority = 0x00010000, tfGotMajority = 0x00010000,
tfLostMajority = 0x00020000, tfLostMajority = 0x00020000,
tfTestSuite = 0x80000000, tfTestSuite = 0x80000000,

View File

@@ -31,7 +31,6 @@
// If you add an amendment here, then do not forget to increment `numFeatures` // If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h. // in include/xrpl/protocol/Feature.h.
XRPL_FIX (IOULockedBalanceInvariant, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ImportIssuer, Supported::yes, VoteBehavior::DefaultYes) XRPL_FIX (ImportIssuer, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HookAPISerializedType240, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(HookAPISerializedType240, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo) XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo)

View File

@@ -367,7 +367,7 @@ UNTYPED_SFIELD(sfMajority, OBJECT, 18)
UNTYPED_SFIELD(sfDisabledValidator, OBJECT, 19) UNTYPED_SFIELD(sfDisabledValidator, OBJECT, 19)
UNTYPED_SFIELD(sfEmittedTxn, OBJECT, 20) UNTYPED_SFIELD(sfEmittedTxn, OBJECT, 20)
UNTYPED_SFIELD(sfHookExecution, OBJECT, 21) UNTYPED_SFIELD(sfHookExecution, OBJECT, 21)
// 22 unused UNTYPED_SFIELD(sfHookDefinition, OBJECT, 22)
UNTYPED_SFIELD(sfHookParameter, OBJECT, 23) UNTYPED_SFIELD(sfHookParameter, OBJECT, 23)
UNTYPED_SFIELD(sfHookGrant, OBJECT, 24) UNTYPED_SFIELD(sfHookGrant, OBJECT, 24)
UNTYPED_SFIELD(sfVoteEntry, OBJECT, 25) UNTYPED_SFIELD(sfVoteEntry, OBJECT, 25)

View File

@@ -87,6 +87,19 @@ InnerObjectFormats::InnerObjectFormats()
{sfEmittedTxnID, soeREQUIRED}, {sfEmittedTxnID, soeREQUIRED},
{sfEmitNonce, soeOPTIONAL}}); {sfEmitNonce, soeOPTIONAL}});
add(sfHookDefinition.jsonName,
sfHookDefinition.getCode(),
{{sfCreateCode, soeREQUIRED},
{sfHookNamespace, soeREQUIRED},
{sfHookParameters, soeREQUIRED},
{sfHookOn, soeOPTIONAL},
{sfHookOnIncoming, soeOPTIONAL},
{sfHookOnOutgoing, soeOPTIONAL},
{sfHookCanEmit, soeOPTIONAL},
{sfHookApiVersion, soeREQUIRED},
{sfFlags, soeREQUIRED},
{sfFee, soeREQUIRED}});
add(sfHook.jsonName, add(sfHook.jsonName,
sfHook.getCode(), sfHook.getCode(),
{{sfHookHash, soeOPTIONAL}, {{sfHookHash, soeOPTIONAL},

1474
src/magic/magic_enum.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -20,8 +20,6 @@
#include <test/jtx.h> #include <test/jtx.h>
#include <test/jtx/envconfig.h> #include <test/jtx/envconfig.h>
#include <xrpld/app/paths/AccountCurrencies.h> #include <xrpld/app/paths/AccountCurrencies.h>
#include <xrpld/app/paths/Pathfinder.h>
#include <xrpld/app/paths/RippleLineCache.h>
#include <xrpld/core/JobQueue.h> #include <xrpld/core/JobQueue.h>
#include <xrpld/rpc/Context.h> #include <xrpld/rpc/Context.h>
#include <xrpld/rpc/RPCHandler.h> #include <xrpld/rpc/RPCHandler.h>
@@ -575,164 +573,6 @@ public:
BEAST_EXPECT(equal(sa, Account("alice")["USD"](5))); BEAST_EXPECT(equal(sa, Account("alice")["USD"](5)));
} }
Json::Value
six_path_append_request_result()
{
using namespace jtx;
Env env = pathTestEnv();
Account A1{"A1"};
Account A2{"A2"};
Account G1{"G1"};
Account G2{"G2"};
Account M1{"M1"};
Account M2{"M2"};
Account M3{"M3"};
Account M4{"M4"};
Account M5{"M5"};
Account M6{"M6"};
Account MM{"MM"};
env.fund(XRP(1000), A1, A2, G1, G2, M1, M2, M3, M4, M5, M6, MM);
env.close();
env.trust(G1["HKD"](2000), A1);
env.trust(G2["HKD"](2000), A2);
env.trust(G1["HKD"](100000), M1, M2, M3, M4, M5, M6, MM);
env.trust(G2["HKD"](100000), M1, M2, M3, M4, M5, M6, MM);
env.close();
env(pay(G1, A1, G1["HKD"](1000)));
env(pay(G1, M1, G1["HKD"](10)));
env(pay(G1, M2, G1["HKD"](10)));
env(pay(G1, M3, G1["HKD"](10)));
env(pay(G1, M4, G1["HKD"](10)));
env(pay(G1, M5, G1["HKD"](10)));
env(pay(G1, M6, G1["HKD"](10)));
env(pay(G1, MM, G1["HKD"](1000)));
env(pay(G2, M1, G2["HKD"](10)));
env(pay(G2, M2, G2["HKD"](10)));
env(pay(G2, M3, G2["HKD"](10)));
env(pay(G2, M4, G2["HKD"](10)));
env(pay(G2, M5, G2["HKD"](10)));
env(pay(G2, M6, G2["HKD"](10)));
env(pay(G2, MM, G2["HKD"](1000)));
env.close();
env(offer(MM, G1["HKD"](1000), G2["HKD"](100)));
env.close();
return find_paths_request(
env, A1, A2, A2["HKD"](60), std::nullopt, G1["HKD"].currency);
}
void
pathfind_paths_computed_never_exceeds_six()
{
testcase("pathfind paths_computed never exceeds six");
auto const result = six_path_append_request_result();
BEAST_EXPECT(result.isMember(jss::alternatives));
if (!result.isMember(jss::alternatives))
return;
BEAST_EXPECT(result[jss::alternatives].isArray());
if (!result[jss::alternatives].isArray())
return;
bool sawPathsComputed = false;
for (auto const& alt : result[jss::alternatives])
{
if (!alt.isMember(jss::paths_computed))
continue;
sawPathsComputed = true;
BEAST_EXPECT(alt[jss::paths_computed].isArray());
if (alt[jss::paths_computed].isArray())
BEAST_EXPECT(alt[jss::paths_computed].size() <= 6);
}
BEAST_EXPECT(sawPathsComputed);
}
void
pathfind_can_return_six_paths_with_append()
{
testcase("pathfind can return six paths with append");
using namespace jtx;
Env env = pathTestEnv();
Account A1{"A1"};
Account A2{"A2"};
Account G1{"G1"};
Account G2{"G2"};
Account M1{"M1"};
Account M2{"M2"};
Account M3{"M3"};
Account M4{"M4"};
Account M5{"M5"};
Account M6{"M6"};
Account MM{"MM"};
env.fund(XRP(1000), A1, A2, G1, G2, M1, M2, M3, M4, M5, M6, MM);
env.close();
env.trust(G1["HKD"](2000), A1);
env.trust(G2["HKD"](2000), A2);
env.trust(G1["HKD"](100000), M1, M2, M3, M4, M5, M6, MM);
env.trust(G2["HKD"](100000), M1, M2, M3, M4, M5, M6, MM);
env.close();
env(pay(G1, A1, G1["HKD"](1000)));
env(pay(G1, M1, G1["HKD"](10)));
env(pay(G1, M2, G1["HKD"](10)));
env(pay(G1, M3, G1["HKD"](10)));
env(pay(G1, M4, G1["HKD"](10)));
env(pay(G1, M5, G1["HKD"](10)));
env(pay(G1, M6, G1["HKD"](10)));
env(pay(G1, MM, G1["HKD"](1000)));
env(pay(G2, M1, G2["HKD"](10)));
env(pay(G2, M2, G2["HKD"](10)));
env(pay(G2, M3, G2["HKD"](10)));
env(pay(G2, M4, G2["HKD"](10)));
env(pay(G2, M5, G2["HKD"](10)));
env(pay(G2, M6, G2["HKD"](10)));
env(pay(G2, MM, G2["HKD"](1000)));
env.close();
env(offer(MM, G1["HKD"](1000), G2["HKD"](100)));
env.close();
auto cache = std::make_shared<RippleLineCache>(
env.current(), env.app().journal("RippleLineCache"));
Pathfinder pf(
cache,
A1.id(),
A2.id(),
G1["HKD"].currency,
std::nullopt,
A2["HKD"](60),
std::nullopt,
env.app());
BEAST_EXPECT(pf.findPaths(7));
pf.computePathRanks(5);
STPath fullLiquidityPath;
auto bestPaths =
pf.getBestPaths(5, fullLiquidityPath, STPathSet{}, A1.id());
BEAST_EXPECT(bestPaths.size() == 5);
BEAST_EXPECT(!fullLiquidityPath.empty());
if (!fullLiquidityPath.empty())
bestPaths.push_back(fullLiquidityPath);
BEAST_EXPECT(bestPaths.size() == 6);
}
void void
issues_path_negative_issue() issues_path_negative_issue()
{ {
@@ -1541,8 +1381,6 @@ public:
path_find_04(); path_find_04();
path_find_05(); path_find_05();
path_find_06(); path_find_06();
pathfind_paths_computed_never_exceeds_six();
pathfind_can_return_six_paths_with_append();
} }
}; };

View File

@@ -28,6 +28,7 @@
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <regex>
namespace ripple { namespace ripple {
@@ -136,9 +137,7 @@ class Invariants_test : public beast::unit_test::suite
if (sink.messages().str().find(m) == std::string::npos) if (sink.messages().str().find(m) == std::string::npos)
{ {
// uncomment if you want to log the invariant failure // uncomment if you want to log the invariant failure
// message // message log << " --> " << m << std::endl;
// log << sink.messages().str() << std::endl;
// log << " --> " << m << std::endl;
fail(); fail();
} }
} }
@@ -1234,42 +1233,6 @@ class Invariants_test : public beast::unit_test::suite
{tecINVARIANT_FAILED, tecINVARIANT_FAILED}); {tecINVARIANT_FAILED, tecINVARIANT_FAILED});
} }
void
testLockedBalance()
{
using namespace test::jtx;
testcase << "ValidLockedBalance";
doInvariantCheck(
{{"Invariant failed: IOU locked balance is greater than balance"}},
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
IOU const USD{A2["USD"]};
auto const sle =
std::make_shared<SLE>(keylet::line(A1, A2, USD.currency));
sle->setFieldAmount(sfHighLimit, A1["USD"](100));
sle->setFieldAmount(sfLowLimit, A2["USD"](100));
sle->setFieldAmount(sfBalance, USD(100));
sle->setFieldAmount(sfLockedBalance, USD(101));
ac.view().insert(sle);
return true;
});
doInvariantCheck(
{{"Invariant failed: IOU locked balance is greater than balance"}},
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
IOU const USD{A2["USD"]};
auto const sle =
std::make_shared<SLE>(keylet::line(A1, A2, USD.currency));
sle->setFieldAmount(sfHighLimit, A1["USD"](100));
sle->setFieldAmount(sfLowLimit, A2["USD"](100));
sle->setFieldAmount(sfBalance, USD(-100));
sle->setFieldAmount(sfLockedBalance, USD(-101));
ac.view().insert(sle);
return true;
});
}
public: public:
void void
run() override run() override
@@ -1288,7 +1251,6 @@ public:
testValidNewAccountRoot(); testValidNewAccountRoot();
testNFTokenPageInvariants(); testNFTokenPageInvariants();
testPermissionedDomainInvariants(); testPermissionedDomainInvariants();
testLockedBalance();
} }
}; };

View File

@@ -69,6 +69,7 @@ class Hooks_test : public beast::unit_test::suite
sfHookAccount, sfHookAccount,
sfEmittedTxn, sfEmittedTxn,
sfHook, sfHook,
sfHookDefinition,
sfHookParameter, sfHookParameter,
sfHookGrant, sfHookGrant,
sfEmitDetails, sfEmitDetails,

View File

@@ -24,6 +24,7 @@
#include <xrpld/core/TimeKeeper.h> #include <xrpld/core/TimeKeeper.h>
#include <xrpld/net/RPCCall.h> #include <xrpld/net/RPCCall.h>
#include <xrpld/rpc/RPCHandler.h> #include <xrpld/rpc/RPCHandler.h>
#include <xrpld/rpc/handlers/Handlers.h>
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/basics/StringUtilities.h> #include <xrpl/basics/StringUtilities.h>
#include <xrpl/basics/contract.h> #include <xrpl/basics/contract.h>
@@ -387,7 +388,8 @@ run(int argc, char** argv)
po::value<std::string>(), po::value<std::string>(),
"Specify the range of present ledgers for testing purposes. Min and " "Specify the range of present ledgers for testing purposes. Min and "
"max values are comma separated.")( "max values are comma separated.")(
"version", "Display the build version."); "version", "Display the build version.")(
"definitions", "Output server definitions as JSON and exit.");
po::options_description data("Ledger/Data Options"); po::options_description data("Ledger/Data Options");
data.add_options()("import", importText.c_str())( data.add_options()("import", importText.c_str())(
@@ -529,6 +531,13 @@ run(int argc, char** argv)
return 0; return 0;
} }
if (vm.count("definitions"))
{
auto defs = getStaticServerDefinitions();
std::cout << Json::FastWriter().write(defs);
return 0;
}
#ifndef ENABLE_TESTS #ifndef ENABLE_TESTS
if (vm.count("unittest") || vm.count("unittest-child")) if (vm.count("unittest") || vm.count("unittest-child"))
{ {

View File

@@ -170,7 +170,7 @@ private:
std::chrono::steady_clock::time_point quick_reply_; std::chrono::steady_clock::time_point quick_reply_;
std::chrono::steady_clock::time_point full_reply_; std::chrono::steady_clock::time_point full_reply_;
static unsigned int const max_paths_ = 5; static unsigned int const max_paths_ = 4;
}; };
} // namespace ripple } // namespace ripple

View File

@@ -1324,13 +1324,11 @@ Pathfinder::initPathTable()
fillPaths( fillPaths(
pt_nonXRP_to_XRP, pt_nonXRP_to_XRP,
{{1, "sxd"}, // gateway buys XRP {{1, "sxd"}, // gateway buys XRP
{2, "saxd"}, // source -> gateway -> book(XRP) -> dest {2, "saxd"}, // source -> gateway -> book(XRP) -> dest
{5, "sabxd"}, // source -> gateway -> book -> book(XRP) -> dest
{6, "saaxd"}, {6, "saaxd"},
{7, "sbxd"}, {7, "sbxd"},
{8, {8, "sabxd"},
"sabbxd"}, // source -> gateway -> book -> book -> book(XRP) -> dest
{9, "sabaxd"}}); {9, "sabaxd"}});
// non-XRP to non-XRP (same currency) // non-XRP to non-XRP (same currency)

View File

@@ -1982,44 +1982,4 @@ ValidAMM::finalize(
return true; return true;
} }
void
ValidLockedBalance::visitEntry(
bool,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
{
if (after && after->getType() == ltRIPPLE_STATE &&
after->isFieldPresent(sfLockedBalance))
{
iouIOULockedBalanceAfter_ = (*after)[sfLockedBalance];
iouIOUBalanceAfter_ = (*after)[sfBalance];
}
}
bool
ValidLockedBalance::finalize(
STTx const& tx,
TER const result,
XRPAmount const,
ReadView const& view,
beast::Journal const& j)
{
if (!view.rules().enabled(fixIOULockedBalanceInvariant))
return true;
if (iouIOULockedBalanceAfter_)
{
if ((!iouIOULockedBalanceAfter_->negative() &&
*iouIOULockedBalanceAfter_ > *iouIOUBalanceAfter_) ||
(iouIOULockedBalanceAfter_->negative() &&
*iouIOULockedBalanceAfter_ < *iouIOUBalanceAfter_))
{
JLOG(j.fatal()) << "Invariant failed: IOU locked balance is "
"greater than balance";
return false;
}
}
return true;
}
} // namespace ripple } // namespace ripple

View File

@@ -680,30 +680,6 @@ private:
beast::Journal const&) const; beast::Journal const&) const;
}; };
class ValidLockedBalance
{
std::optional<STAmount> iouIOULockedBalanceAfter_;
std::optional<STAmount> iouIOUBalanceAfter_;
public:
ValidLockedBalance()
{
}
void
visitEntry(
bool,
std::shared_ptr<SLE const> const&,
std::shared_ptr<SLE const> const&);
bool
finalize(
STTx const&,
TER const,
XRPAmount const,
ReadView const&,
beast::Journal const&);
};
// additional invariant checks can be declared above and then added to this // additional invariant checks can be declared above and then added to this
// tuple // tuple
using InvariantChecks = std::tuple< using InvariantChecks = std::tuple<
@@ -724,8 +700,7 @@ using InvariantChecks = std::tuple<
ValidClawback, ValidClawback,
ValidMPTIssuance, ValidMPTIssuance,
ValidPermissionedDomain, ValidPermissionedDomain,
ValidAMM, ValidAMM>;
ValidLockedBalance>;
/** /**
* @brief get a tuple of all invariant checks * @brief get a tuple of all invariant checks

View File

@@ -425,7 +425,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
JLOG(ctx.j.trace()) JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::NAMESPACE_MISSING << ")[" << "HookSet(" << hook::log::NAMESPACE_MISSING << ")["
<< HS_ACC() << HS_ACC()
<< "]: Malformed transaction: SetHook ltHookDefinition " << "]: Malformed transaction: SetHook sfHookDefinition "
"must contain sfHookNamespace."; "must contain sfHookNamespace.";
return false; return false;
} }
@@ -2001,7 +2001,7 @@ SetHook::setHook()
// sfHookHash, sfHookNamespace, sfHookOn, sfHookOnOutgoing, // sfHookHash, sfHookNamespace, sfHookOn, sfHookOnOutgoing,
// sfHookOnIncoming, sfHookCanEmit sfHookApiVersion, sfFlags: free // sfHookOnIncoming, sfHookCanEmit sfHookApiVersion, sfFlags: free
// ltHookDefinition is not reserved because it is an unowned object, // sfHookDefinition is not reserved because it is an unowned object,
// rather the uploader is billed via fee according to the following: // rather the uploader is billed via fee according to the following:
// sfCreateCode: 5000 drops per byte // sfCreateCode: 5000 drops per byte
// sfHookParameters: 5000 drops per byte // sfHookParameters: 5000 drops per byte

View File

@@ -129,6 +129,8 @@ doRipplePathFind(RPC::JsonContext&);
Json::Value Json::Value
doServerDefinitions(RPC::JsonContext&); doServerDefinitions(RPC::JsonContext&);
Json::Value Json::Value
getStaticServerDefinitions();
Json::Value
doServerInfo(RPC::JsonContext&); // for humans doServerInfo(RPC::JsonContext&); // for humans
Json::Value Json::Value
doServerState(RPC::JsonContext&); // for machines doServerState(RPC::JsonContext&); // for machines

View File

@@ -32,7 +32,7 @@
#include <xrpl/protocol/digest.h> #include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h> #include <xrpl/protocol/jss.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <magic_enum.hpp> #include <magic/magic_enum.h>
#include <sstream> #include <sstream>
#define MAGIC_ENUM(x, _min, _max) \ #define MAGIC_ENUM(x, _min, _max) \
@@ -523,6 +523,15 @@ public:
} }
}; };
Json::Value
getStaticServerDefinitions()
{
static const Definitions defs{};
Json::Value ret = defs();
ret[jss::hash] = to_string(defs.getHash());
return ret;
}
Json::Value Json::Value
doServerDefinitions(RPC::JsonContext& context) doServerDefinitions(RPC::JsonContext& context)
{ {