mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'ximinez/lending-XLS-66-ongoing' into vlntb/RIPD-4257-fix-tecFROZEN
This commit is contained in:
9
.github/actions/build-deps/action.yml
vendored
9
.github/actions/build-deps/action.yml
vendored
@@ -4,9 +4,6 @@ description: "Install Conan dependencies, optionally forcing a rebuild of all de
|
||||
# Note that actions do not support 'type' and all inputs are strings, see
|
||||
# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs.
|
||||
inputs:
|
||||
build_dir:
|
||||
description: "The directory where to build."
|
||||
required: true
|
||||
build_type:
|
||||
description: 'The build type to use ("Debug", "Release").'
|
||||
required: true
|
||||
@@ -28,17 +25,13 @@ runs:
|
||||
- name: Install Conan dependencies
|
||||
shell: bash
|
||||
env:
|
||||
BUILD_DIR: ${{ inputs.build_dir }}
|
||||
BUILD_NPROC: ${{ inputs.build_nproc }}
|
||||
BUILD_OPTION: ${{ inputs.force_build == 'true' && '*' || 'missing' }}
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
|
||||
run: |
|
||||
echo 'Installing dependencies.'
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
cd "${BUILD_DIR}"
|
||||
conan install \
|
||||
--output-folder . \
|
||||
--build="${BUILD_OPTION}" \
|
||||
--options:host='&:tests=True' \
|
||||
--options:host='&:xrpld=True' \
|
||||
@@ -46,4 +39,4 @@ runs:
|
||||
--conf:all tools.build:jobs=${BUILD_NPROC} \
|
||||
--conf:all tools.build:verbosity="${LOG_VERBOSITY}" \
|
||||
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \
|
||||
..
|
||||
.
|
||||
|
||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -22,7 +22,7 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
BUILD_DIR: .build
|
||||
BUILD_DIR: build
|
||||
NPROC_SUBTRACT: 2
|
||||
|
||||
jobs:
|
||||
|
||||
29
.github/workflows/reusable-build-test-config.yml
vendored
29
.github/workflows/reusable-build-test-config.yml
vendored
@@ -3,11 +3,6 @@ name: Build and test configuration
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_dir:
|
||||
description: "The directory where to build."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
build_only:
|
||||
description: 'Whether to only build or to build and test the code ("true", "false").'
|
||||
required: true
|
||||
@@ -59,6 +54,11 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
# Conan installs the generators in the build/generators directory, see the
|
||||
# layout() method in conanfile.py. We then run CMake from the build directory.
|
||||
BUILD_DIR: build
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: ${{ inputs.config_name }}
|
||||
@@ -96,7 +96,6 @@ jobs:
|
||||
- name: Build dependencies
|
||||
uses: ./.github/actions/build-deps
|
||||
with:
|
||||
build_dir: ${{ inputs.build_dir }}
|
||||
build_nproc: ${{ steps.nproc.outputs.nproc }}
|
||||
build_type: ${{ inputs.build_type }}
|
||||
# Set the verbosity to "quiet" for Windows to avoid an excessive
|
||||
@@ -104,7 +103,7 @@ jobs:
|
||||
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
||||
|
||||
- name: Configure CMake
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
CMAKE_ARGS: ${{ inputs.cmake_args }}
|
||||
@@ -117,7 +116,7 @@ jobs:
|
||||
..
|
||||
|
||||
- name: Build the binary
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -132,8 +131,6 @@ jobs:
|
||||
- name: Upload the binary (Linux)
|
||||
if: ${{ github.repository_owner == 'XRPLF' && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
env:
|
||||
BUILD_DIR: ${{ inputs.build_dir }}
|
||||
with:
|
||||
name: xrpld-${{ inputs.config_name }}
|
||||
path: ${{ env.BUILD_DIR }}/xrpld
|
||||
@@ -142,7 +139,7 @@ jobs:
|
||||
|
||||
- name: Check linking (Linux)
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
run: |
|
||||
ldd ./xrpld
|
||||
if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then
|
||||
@@ -154,13 +151,13 @@ jobs:
|
||||
|
||||
- name: Verify presence of instrumentation (Linux)
|
||||
if: ${{ runner.os == 'Linux' && env.ENABLED_VOIDSTAR == 'true' }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
run: |
|
||||
./xrpld --version | grep libvoidstar
|
||||
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
# Windows locks some of the build files while running tests, and parallel jobs can collide
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -173,7 +170,7 @@ jobs:
|
||||
|
||||
- name: Run the embedded tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', inputs.build_dir, inputs.build_type) || inputs.build_dir }}
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
run: |
|
||||
@@ -189,7 +186,7 @@ jobs:
|
||||
|
||||
- name: Prepare coverage report
|
||||
if: ${{ !inputs.build_only && env.ENABLED_COVERAGE == 'true' }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -207,7 +204,7 @@ jobs:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
fail_ci_if_error: true
|
||||
files: ${{ inputs.build_dir }}/coverage.xml
|
||||
files: ${{ env.BUILD_DIR }}/coverage.xml
|
||||
plugins: noop
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
verbose: true
|
||||
|
||||
6
.github/workflows/reusable-build-test.yml
vendored
6
.github/workflows/reusable-build-test.yml
vendored
@@ -8,11 +8,6 @@ name: Build and test
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_dir:
|
||||
description: "The directory where to build."
|
||||
required: false
|
||||
type: string
|
||||
default: ".build"
|
||||
os:
|
||||
description: 'The operating system to use for the build ("linux", "macos", "windows").'
|
||||
required: true
|
||||
@@ -46,7 +41,6 @@ jobs:
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
max-parallel: 10
|
||||
with:
|
||||
build_dir: ${{ inputs.build_dir }}
|
||||
build_only: ${{ matrix.build_only }}
|
||||
build_type: ${{ matrix.build_type }}
|
||||
cmake_args: ${{ matrix.cmake_args }}
|
||||
|
||||
1
.github/workflows/upload-conan-deps.yml
vendored
1
.github/workflows/upload-conan-deps.yml
vendored
@@ -92,7 +92,6 @@ jobs:
|
||||
- name: Build dependencies
|
||||
uses: ./.github/actions/build-deps
|
||||
with:
|
||||
build_dir: .build
|
||||
build_nproc: ${{ steps.nproc.outputs.nproc }}
|
||||
build_type: ${{ matrix.build_type }}
|
||||
force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }}
|
||||
|
||||
@@ -541,7 +541,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
|
||||
{sfStartDate, soeREQUIRED},
|
||||
{sfPaymentInterval, soeREQUIRED},
|
||||
{sfGracePeriod, soeDEFAULT},
|
||||
{sfPreviousPaymentDate, soeDEFAULT},
|
||||
{sfPreviousPaymentDueDate, soeDEFAULT},
|
||||
{sfNextPaymentDueDate, soeDEFAULT},
|
||||
// The loan object tracks these values:
|
||||
//
|
||||
|
||||
@@ -102,7 +102,7 @@ TYPED_SFIELD(sfMutableFlags, UINT32, 53)
|
||||
TYPED_SFIELD(sfStartDate, UINT32, 54)
|
||||
TYPED_SFIELD(sfPaymentInterval, UINT32, 55)
|
||||
TYPED_SFIELD(sfGracePeriod, UINT32, 56)
|
||||
TYPED_SFIELD(sfPreviousPaymentDate, UINT32, 57)
|
||||
TYPED_SFIELD(sfPreviousPaymentDueDate, UINT32, 57)
|
||||
TYPED_SFIELD(sfNextPaymentDueDate, UINT32, 58)
|
||||
TYPED_SFIELD(sfPaymentRemaining, UINT32, 59)
|
||||
TYPED_SFIELD(sfPaymentTotal, UINT32, 60)
|
||||
|
||||
@@ -372,7 +372,7 @@ protected:
|
||||
if (auto loan = env.le(loanKeylet); env.test.BEAST_EXPECT(loan))
|
||||
{
|
||||
env.test.BEAST_EXPECT(
|
||||
loan->at(sfPreviousPaymentDate) == previousPaymentDate);
|
||||
loan->at(sfPreviousPaymentDueDate) == previousPaymentDate);
|
||||
env.test.BEAST_EXPECT(
|
||||
loan->at(sfPaymentRemaining) == paymentRemaining);
|
||||
env.test.BEAST_EXPECT(
|
||||
@@ -507,7 +507,7 @@ protected:
|
||||
if (auto loan = env.le(loanKeylet); BEAST_EXPECT(loan))
|
||||
{
|
||||
return LoanState{
|
||||
.previousPaymentDate = loan->at(sfPreviousPaymentDate),
|
||||
.previousPaymentDate = loan->at(sfPreviousPaymentDueDate),
|
||||
.startDate = tp{d{loan->at(sfStartDate)}},
|
||||
.nextPaymentDate = loan->at(sfNextPaymentDueDate),
|
||||
.paymentRemaining = loan->at(sfPaymentRemaining),
|
||||
@@ -1454,7 +1454,7 @@ protected:
|
||||
BEAST_EXPECT(
|
||||
loan->at(sfPaymentInterval) == *loanParams.payInterval);
|
||||
BEAST_EXPECT(loan->at(sfGracePeriod) == *loanParams.gracePd);
|
||||
BEAST_EXPECT(loan->at(sfPreviousPaymentDate) == 0);
|
||||
BEAST_EXPECT(loan->at(sfPreviousPaymentDueDate) == 0);
|
||||
BEAST_EXPECT(
|
||||
loan->at(sfNextPaymentDueDate) ==
|
||||
startDate + *loanParams.payInterval);
|
||||
@@ -3597,13 +3597,39 @@ protected:
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
testcase("Vault maximum value exceeded");
|
||||
testcase("Vault at maximum value");
|
||||
env(set(issuer, broker.brokerID, principalRequest),
|
||||
counterparty(lender),
|
||||
interestRate(TenthBips32(10'000)),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
fee(env.current()->fees().base * 5),
|
||||
ter(tecLIMIT_EXCEEDED));
|
||||
ter(tecLIMIT_EXCEEDED),
|
||||
THISLINE);
|
||||
},
|
||||
nullptr);
|
||||
|
||||
testCase(
|
||||
[&, this](Env& env, BrokerInfo const& broker, auto&) {
|
||||
using namespace loan;
|
||||
Number const principalRequest = broker.asset(1'000).value();
|
||||
Vault vault{env};
|
||||
auto tx = vault.set({.owner = lender, .id = broker.vaultID});
|
||||
tx[sfAssetsMaximum] =
|
||||
BrokerParameters::defaults().vaultDeposit +
|
||||
broker.asset(1).number();
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
testcase("Vault maximum value exceeded");
|
||||
env(set(issuer, broker.brokerID, principalRequest),
|
||||
counterparty(lender),
|
||||
interestRate(TenthBips32(100'000)),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
fee(env.current()->fees().base * 5),
|
||||
paymentTotal(2),
|
||||
paymentInterval(3600 * 24),
|
||||
ter(tecLIMIT_EXCEEDED),
|
||||
THISLINE);
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
@@ -3841,7 +3867,7 @@ protected:
|
||||
BEAST_EXPECT(loan[sfPaymentInterval] == 60);
|
||||
BEAST_EXPECT(loan[sfPeriodicPayment] == "1000000000");
|
||||
BEAST_EXPECT(loan[sfPaymentRemaining] == 1);
|
||||
BEAST_EXPECT(!loan.isMember(sfPreviousPaymentDate));
|
||||
BEAST_EXPECT(!loan.isMember(sfPreviousPaymentDueDate));
|
||||
BEAST_EXPECT(loan[sfPrincipalOutstanding] == "1000000000");
|
||||
BEAST_EXPECT(loan[sfTotalValueOutstanding] == "1000000000");
|
||||
BEAST_EXPECT(!loan.isMember(sfLoanScale));
|
||||
@@ -6048,7 +6074,7 @@ protected:
|
||||
{
|
||||
// --- PoC Summary ----------------------------------------------------
|
||||
// Scenario: Borrower makes one periodic payment early (before next due)
|
||||
// so doPayment sets sfPreviousPaymentDate to the (future)
|
||||
// so doPayment sets sfPreviousPaymentDueDate to the (future)
|
||||
// sfNextPaymentDueDate and advances sfNextPaymentDueDate by one
|
||||
// interval. Borrower then immediately performs a full-payment
|
||||
// (tfLoanFullPayment). Why it matters: Full-payment interest accrual
|
||||
|
||||
@@ -1669,7 +1669,7 @@ loanMakePayment(
|
||||
|
||||
Number const periodicPayment = loan->at(sfPeriodicPayment);
|
||||
|
||||
auto prevPaymentDateProxy = loan->at(sfPreviousPaymentDate);
|
||||
auto prevPaymentDateProxy = loan->at(sfPreviousPaymentDueDate);
|
||||
std::uint32_t const startDate = loan->at(sfStartDate);
|
||||
|
||||
std::uint32_t const paymentInterval = loan->at(sfPaymentInterval);
|
||||
|
||||
@@ -374,7 +374,8 @@ LoanManage::unimpairLoan(
|
||||
loanSle->clearFlag(lsfLoanImpaired);
|
||||
auto const paymentInterval = loanSle->at(sfPaymentInterval);
|
||||
auto const normalPaymentDueDate =
|
||||
std::max(loanSle->at(sfPreviousPaymentDate), loanSle->at(sfStartDate)) +
|
||||
std::max(
|
||||
loanSle->at(sfPreviousPaymentDueDate), loanSle->at(sfStartDate)) +
|
||||
paymentInterval;
|
||||
if (!hasExpired(view, normalPaymentDueDate))
|
||||
{
|
||||
|
||||
@@ -282,6 +282,15 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
if (!vault)
|
||||
// Should be impossible
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
if (vault->at(sfAssetsMaximum) != 0 &&
|
||||
vault->at(sfAssetsTotal) >= vault->at(sfAssetsMaximum))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Vault at maximum assets limit. Can't add another loan.";
|
||||
return tecLIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
Asset const asset = vault->at(sfAsset);
|
||||
|
||||
auto const vaultPseudo = vault->at(sfAccount);
|
||||
@@ -411,8 +420,12 @@ LoanSet::doApply()
|
||||
principalRequested,
|
||||
properties.loanState.managementFeeDue);
|
||||
|
||||
if (vaultSle->at(sfAssetsMaximum) != 0 &&
|
||||
vaultTotalProxy + state.interestDue > vaultSle->at(sfAssetsMaximum))
|
||||
auto const vaultMaximum = *vaultSle->at(sfAssetsMaximum);
|
||||
XRPL_ASSERT_PARTS(
|
||||
vaultMaximum == 0 || vaultMaximum > *vaultTotalProxy,
|
||||
"ripple::LoanSet::doApply",
|
||||
"Vault is below maximum limit");
|
||||
if (vaultMaximum != 0 && state.interestDue > vaultMaximum - vaultTotalProxy)
|
||||
{
|
||||
JLOG(j_.warn()) << "Loan would exceed the maximum assets of the vault";
|
||||
return tecLIMIT_EXCEEDED;
|
||||
@@ -600,7 +613,7 @@ LoanSet::doApply()
|
||||
loan->at(sfTotalValueOutstanding) = properties.loanState.valueOutstanding;
|
||||
loan->at(sfManagementFeeOutstanding) =
|
||||
properties.loanState.managementFeeDue;
|
||||
loan->at(sfPreviousPaymentDate) = 0;
|
||||
loan->at(sfPreviousPaymentDueDate) = 0;
|
||||
loan->at(sfNextPaymentDueDate) = startDate + paymentInterval;
|
||||
loan->at(sfPaymentRemaining) = paymentTotal;
|
||||
view.insert(loan);
|
||||
|
||||
Reference in New Issue
Block a user