mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 22:30:57 +00:00
feat: Update to keylet::sponsorship and sfRemainingOwnerCount (#7578)
This commit is contained in:
@@ -153,7 +153,7 @@ signers(AccountID const& account) noexcept;
|
||||
|
||||
/** A Sponsorship */
|
||||
Keylet
|
||||
sponsor(AccountID const& sponsor, AccountID const& sponsee) noexcept;
|
||||
sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept;
|
||||
|
||||
/** A Check */
|
||||
/** @{ */
|
||||
|
||||
@@ -622,7 +622,7 @@ LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({
|
||||
{sfSponsee, SoeRequired},
|
||||
{sfFeeAmount, SoeOptional},
|
||||
{sfMaxFee, SoeOptional},
|
||||
{sfReserveCount, SoeDefault},
|
||||
{sfRemainingOwnerCount, SoeDefault},
|
||||
{sfOwnerNode, SoeRequired},
|
||||
{sfSponseeNode, SoeRequired},
|
||||
}))
|
||||
|
||||
@@ -116,7 +116,7 @@ TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 68) // 1/10 basis points (bi
|
||||
TYPED_SFIELD(sfSponsoredOwnerCount, UINT32, 69)
|
||||
TYPED_SFIELD(sfSponsoringOwnerCount, UINT32, 70)
|
||||
TYPED_SFIELD(sfSponsoringAccountCount, UINT32, 71)
|
||||
TYPED_SFIELD(sfReserveCount, UINT32, 72)
|
||||
TYPED_SFIELD(sfRemainingOwnerCount, UINT32, 72)
|
||||
TYPED_SFIELD(sfSponsorFlags, UINT32, 73)
|
||||
|
||||
// 64-bit integers (common)
|
||||
|
||||
@@ -1103,7 +1103,7 @@ TRANSACTION(ttSPONSORSHIP_SET, 86, SponsorshipSet,
|
||||
{sfSponsee, SoeOptional},
|
||||
{sfFeeAmount, SoeOptional},
|
||||
{sfMaxFee, SoeOptional},
|
||||
{sfReserveCount, SoeOptional},
|
||||
{sfRemainingOwnerCount, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This system-generated transaction type is used to update the status of the various amendments.
|
||||
|
||||
@@ -138,27 +138,27 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfReserveCount (SoeDefault)
|
||||
* @brief Get sfRemainingOwnerCount (SoeDefault)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getReserveCount() const
|
||||
getRemainingOwnerCount() const
|
||||
{
|
||||
if (hasReserveCount())
|
||||
return this->sle_->at(sfReserveCount);
|
||||
if (hasRemainingOwnerCount())
|
||||
return this->sle_->at(sfRemainingOwnerCount);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfReserveCount is present.
|
||||
* @brief Check if sfRemainingOwnerCount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasReserveCount() const
|
||||
hasRemainingOwnerCount() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfReserveCount);
|
||||
return this->sle_->isFieldPresent(sfRemainingOwnerCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,13 +297,13 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfReserveCount (SoeDefault)
|
||||
* @brief Set sfRemainingOwnerCount (SoeDefault)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SponsorshipBuilder&
|
||||
setReserveCount(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
setRemainingOwnerCount(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfReserveCount] = value;
|
||||
object_[sfRemainingOwnerCount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,29 +152,29 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfReserveCount (SoeOptional)
|
||||
* @brief Get sfRemainingOwnerCount (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getReserveCount() const
|
||||
getRemainingOwnerCount() const
|
||||
{
|
||||
if (hasReserveCount())
|
||||
if (hasRemainingOwnerCount())
|
||||
{
|
||||
return this->tx_->at(sfReserveCount);
|
||||
return this->tx_->at(sfRemainingOwnerCount);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfReserveCount is present.
|
||||
* @brief Check if sfRemainingOwnerCount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasReserveCount() const
|
||||
hasRemainingOwnerCount() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfReserveCount);
|
||||
return this->tx_->isFieldPresent(sfRemainingOwnerCount);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -263,13 +263,13 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfReserveCount (SoeOptional)
|
||||
* @brief Set sfRemainingOwnerCount (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SponsorshipSetBuilder&
|
||||
setReserveCount(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
setRemainingOwnerCount(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfReserveCount] = value;
|
||||
object_[sfRemainingOwnerCount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -274,13 +274,14 @@ adjustOwnerCount(
|
||||
adjustOwnerCountHlp(view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j);
|
||||
adjustOwnerCountHlp(view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j);
|
||||
|
||||
auto sponsorObjSle = view.peek(keylet::sponsor(sponsorID, accountID));
|
||||
auto sponsorObjSle = view.peek(keylet::sponsorship(sponsorID, accountID));
|
||||
if (sponsorObjSle && adjustment > 0)
|
||||
{
|
||||
// update the pre-funded ReserveCount on Sponsorship ledger object
|
||||
// Reserve count moves opposite to adjustment: +adjustment => consume reserve (-),
|
||||
// update the pre-funded RemainingOwnerCount on Sponsorship ledger object
|
||||
// Remaining owner count moves opposite to adjustment:
|
||||
// +adjustment => consume reserve (-),
|
||||
adjustOwnerCountHlp(
|
||||
view, sponsorObjSle, sfReserveCount, sponsorID, -adjustment, j, false);
|
||||
view, sponsorObjSle, sfRemainingOwnerCount, sponsorID, -adjustment, j, false);
|
||||
}
|
||||
}
|
||||
adjustOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j);
|
||||
@@ -345,7 +346,8 @@ checkInsufficientReserve(
|
||||
auto const isCoSigning = isSponsorReserveCoSigning(tx);
|
||||
|
||||
auto const sle = view.read(
|
||||
keylet::sponsor(sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount)));
|
||||
keylet::sponsorship(
|
||||
sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount)));
|
||||
|
||||
// prefunded sponsor should have a sponsorship entry
|
||||
if (!isCoSigning && !sle)
|
||||
@@ -353,7 +355,7 @@ checkInsufficientReserve(
|
||||
|
||||
if (sle)
|
||||
{
|
||||
auto const ownerCountAllowed = sle->getFieldU32(sfReserveCount);
|
||||
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
|
||||
if (ownerCountAllowed < ownerCountDelta)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ signers(AccountID const& account) noexcept
|
||||
}
|
||||
|
||||
Keylet
|
||||
sponsor(AccountID const& sponsor, AccountID const& sponsee) noexcept
|
||||
sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept
|
||||
{
|
||||
return {ltSPONSORSHIP, indexHash(LedgerNameSpace::Sponsorship, sponsor, sponsee)};
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const sponsorshipSle =
|
||||
view.read(keylet::sponsor(tx.getAccountID(sfSponsor), tx.getAccountID(sfAccount)));
|
||||
view.read(keylet::sponsorship(tx.getAccountID(sfSponsor), tx.getAccountID(sfAccount)));
|
||||
|
||||
// sponsorship object missing for pre-funded tx
|
||||
if (!sponsorshipSle)
|
||||
@@ -1322,7 +1322,7 @@ Transactor::getFeePayer(ReadView const& view, STTx const& tx)
|
||||
auto const sponsorAccountID = tx.getAccountID(sfSponsor);
|
||||
auto const sponseeAccountID = tx.getAccountID(sfAccount);
|
||||
auto const hasSponsorSignature = tx.isFieldPresent(sfSponsorSignature);
|
||||
auto const sponsorshipKeylet = keylet::sponsor(sponsorAccountID, sponseeAccountID);
|
||||
auto const sponsorshipKeylet = keylet::sponsorship(sponsorAccountID, sponseeAccountID);
|
||||
|
||||
// if pre-funded sponsorship exists, prefer it
|
||||
if (hasSponsorSignature && !view.exists(sponsorshipKeylet))
|
||||
|
||||
@@ -67,7 +67,7 @@ SponsorshipSet::preflight(PreflightContext const& ctx)
|
||||
return temINVALID_FLAG;
|
||||
|
||||
// can not include these fields when deleting
|
||||
if (ctx.tx.isFieldPresent(sfFeeAmount) || ctx.tx.isFieldPresent(sfReserveCount) ||
|
||||
if (ctx.tx.isFieldPresent(sfFeeAmount) || ctx.tx.isFieldPresent(sfRemainingOwnerCount) ||
|
||||
ctx.tx.isFieldPresent(sfMaxFee))
|
||||
return temMALFORMED;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx)
|
||||
auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) ||
|
||||
((txFlags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee)) !=
|
||||
0u);
|
||||
auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) ||
|
||||
auto const sponsoringReserve = tx.isFieldPresent(sfRemainingOwnerCount) ||
|
||||
((txFlags &
|
||||
(tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)) != 0u);
|
||||
|
||||
@@ -170,7 +170,8 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// check if object exists
|
||||
auto const sponsorObjSle = ctx.view.read(keylet::sponsor(sponsorAccountID, sponseeAccountID));
|
||||
auto const sponsorObjSle =
|
||||
ctx.view.read(keylet::sponsorship(sponsorAccountID, sponseeAccountID));
|
||||
|
||||
if (ctx.tx.isFlag(tfDeleteObject) && !sponsorObjSle)
|
||||
return tecNO_ENTRY;
|
||||
@@ -235,7 +236,7 @@ SponsorshipSet::doApply()
|
||||
if (!ctx_.view().exists(keylet::account(sponseeAccountID)))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sponsorKeylet = keylet::sponsor(sponsorAccountID, sponseeAccountID);
|
||||
auto const sponsorKeylet = keylet::sponsorship(sponsorAccountID, sponseeAccountID);
|
||||
auto const sponsorObjSle = ctx_.view().peek(sponsorKeylet);
|
||||
|
||||
if (ctx_.tx.isFlag(tfDeleteObject))
|
||||
@@ -249,7 +250,7 @@ SponsorshipSet::doApply()
|
||||
|
||||
auto const feeAmount = ctx_.tx[~sfFeeAmount];
|
||||
auto const maxFee = ctx_.tx[~sfMaxFee];
|
||||
auto const reserveCount = ctx_.tx[~sfReserveCount];
|
||||
auto const reserveCount = ctx_.tx[~sfRemainingOwnerCount];
|
||||
|
||||
auto reserveSponsorAccSle = getTxReserveSponsor(view(), ctx_.tx);
|
||||
if (!reserveSponsorAccSle)
|
||||
@@ -286,7 +287,7 @@ SponsorshipSet::doApply()
|
||||
if (maxFee && *maxFee > XRPAmount(0))
|
||||
(*newSle)[sfMaxFee] = *maxFee;
|
||||
if (reserveCount && *reserveCount > 0)
|
||||
(*newSle)[sfReserveCount] = *reserveCount;
|
||||
(*newSle)[sfRemainingOwnerCount] = *reserveCount;
|
||||
|
||||
auto flags = 0;
|
||||
if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee))
|
||||
@@ -367,7 +368,7 @@ SponsorshipSet::doApply()
|
||||
}
|
||||
|
||||
if (reserveCount)
|
||||
sponsorObjSle->at(sfReserveCount) = *reserveCount;
|
||||
sponsorObjSle->at(sfRemainingOwnerCount) = *reserveCount;
|
||||
|
||||
// update Flags
|
||||
auto flags = sponsorObjSle->getFieldU32(sfFlags);
|
||||
|
||||
@@ -385,12 +385,13 @@ reduceReserveCount(
|
||||
if (delta > 0)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sponsorKeylet = keylet::sponsor(sponsor, account);
|
||||
auto const sponsorKeylet = keylet::sponsorship(sponsor, account);
|
||||
auto const sponsorSle = view.peek(sponsorKeylet);
|
||||
if (!sponsorSle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const afterReserveCount = applyCountDelta(sponsorSle->getFieldU32(sfReserveCount), delta);
|
||||
auto const afterReserveCount =
|
||||
applyCountDelta(sponsorSle->getFieldU32(sfRemainingOwnerCount), delta);
|
||||
if (!afterReserveCount)
|
||||
{
|
||||
// already checked in preclaim()
|
||||
@@ -398,7 +399,7 @@ reduceReserveCount(
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
sponsorSle->at(sfReserveCount) = *afterReserveCount;
|
||||
sponsorSle->at(sfRemainingOwnerCount) = *afterReserveCount;
|
||||
view.update(sponsorSle);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -322,8 +322,9 @@ public:
|
||||
env.close();
|
||||
|
||||
// Increasing feeAmount to reach insufficient reserve
|
||||
auto const currentFeeAmount =
|
||||
env.le(keylet::sponsor(sponsor.id(), alice.id()))->getFieldAmount(sfFeeAmount).xrp();
|
||||
auto const currentFeeAmount = env.le(keylet::sponsorship(sponsor.id(), alice.id()))
|
||||
->getFieldAmount(sfFeeAmount)
|
||||
.xrp();
|
||||
adjustAccountXRPBalance(env, sponsor, XRP(310));
|
||||
env(sponsor::set_fee(sponsor, 0, currentFeeAmount + XRP(309)),
|
||||
sponsor::SponseeAcc(alice),
|
||||
@@ -541,9 +542,9 @@ public:
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
auto sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
auto sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->at(sfReserveCount) == 100);
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100);
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100));
|
||||
BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1));
|
||||
BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForFee));
|
||||
@@ -557,9 +558,9 @@ public:
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->at(sfReserveCount) == 50);
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 50);
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(50));
|
||||
BEAST_EXPECT(sle->at(sfMaxFee) == XRP(0.5));
|
||||
BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - sle->at(sfFeeAmount) - XRP(2));
|
||||
@@ -571,9 +572,9 @@ public:
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->at(sfReserveCount) == 200);
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 200);
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(200));
|
||||
BEAST_EXPECT(sle->at(sfMaxFee) == XRP(2));
|
||||
BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - sle->at(sfFeeAmount) - XRP(3));
|
||||
@@ -597,7 +598,7 @@ public:
|
||||
// delete from sponsee
|
||||
env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS));
|
||||
env.close();
|
||||
BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice)));
|
||||
BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice)));
|
||||
|
||||
// create sponsorship with zero value
|
||||
env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)),
|
||||
@@ -605,9 +606,9 @@ public:
|
||||
Fee(XRP(1)));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfReserveCount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfMaxFee));
|
||||
// verify flags from previous sponsorship are not carried over
|
||||
@@ -620,9 +621,9 @@ public:
|
||||
Fee(XRP(1)));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->at(sfReserveCount) == 100);
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100);
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100));
|
||||
BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1));
|
||||
|
||||
@@ -632,9 +633,9 @@ public:
|
||||
Fee(XRP(1)));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfReserveCount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfMaxFee));
|
||||
}
|
||||
@@ -716,17 +717,17 @@ public:
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
auto sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
auto sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->at(sfReserveCount) == 99);
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 99);
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99));
|
||||
|
||||
env(did::del(alice), Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsor(sponsor, alice));
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->at(sfReserveCount) == 99); // not paybacked
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 99); // not paybacked
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99));
|
||||
}
|
||||
|
||||
@@ -1199,8 +1200,8 @@ public:
|
||||
auto checkSle = env.le(keylet::unchecked(checkId));
|
||||
BEAST_EXPECT(checkSle->isFieldPresent(sfSponsor));
|
||||
BEAST_EXPECT(checkSle->getAccountID(sfSponsor) == sponsor1.id());
|
||||
auto sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice));
|
||||
BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 99);
|
||||
auto sponsor1Sle = env.le(keylet::sponsorship(sponsor1, alice));
|
||||
BEAST_EXPECT(sponsor1Sle->getFieldU32(sfRemainingOwnerCount) == 99);
|
||||
|
||||
// transfer sponsor
|
||||
env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::SponseeAcc(alice));
|
||||
@@ -1222,10 +1223,10 @@ public:
|
||||
checkSle = env.le(keylet::unchecked(checkId));
|
||||
BEAST_EXPECT(checkSle->isFieldPresent(sfSponsor));
|
||||
BEAST_EXPECT(checkSle->getAccountID(sfSponsor) == sponsor2.id());
|
||||
sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice));
|
||||
BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 99);
|
||||
auto sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice));
|
||||
BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 99);
|
||||
sponsor1Sle = env.le(keylet::sponsorship(sponsor1, alice));
|
||||
BEAST_EXPECT(sponsor1Sle->getFieldU32(sfRemainingOwnerCount) == 99);
|
||||
auto sponsor2Sle = env.le(keylet::sponsorship(sponsor2, alice));
|
||||
BEAST_EXPECT(sponsor2Sle->getFieldU32(sfRemainingOwnerCount) == 99);
|
||||
|
||||
// dissolve sponsor
|
||||
adjustAccountXRPBalance(env, alice, reserve(env, 1));
|
||||
@@ -1245,8 +1246,8 @@ public:
|
||||
!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount));
|
||||
checkSle = env.le(keylet::unchecked(checkId));
|
||||
BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsor));
|
||||
sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice));
|
||||
BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 99);
|
||||
sponsor2Sle = env.le(keylet::sponsorship(sponsor2, alice));
|
||||
BEAST_EXPECT(sponsor2Sle->getFieldU32(sfRemainingOwnerCount) == 99);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1319,7 +1320,8 @@ public:
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
BEAST_EXPECT(
|
||||
env.le(keylet::sponsor(sponsor, alice))->getFieldU32(sfReserveCount) == 100);
|
||||
env.le(keylet::sponsorship(sponsor, alice))->getFieldU32(sfRemainingOwnerCount) ==
|
||||
100);
|
||||
|
||||
// not the owner of the object
|
||||
env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), Ter(tecNO_PERMISSION));
|
||||
@@ -1333,7 +1335,8 @@ public:
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(
|
||||
env.le(keylet::sponsor(sponsor, alice))->getFieldU32(sfReserveCount) == 100);
|
||||
env.le(keylet::sponsorship(sponsor, alice))->getFieldU32(sfRemainingOwnerCount) ==
|
||||
100);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1552,7 +1555,9 @@ public:
|
||||
env.close();
|
||||
|
||||
auto const sponsorFeeBalance = [&](Account const& sponsor, Account const& sponsee) {
|
||||
return env.le(keylet::sponsor(sponsor, sponsee))->getFieldAmount(sfFeeAmount).xrp();
|
||||
return env.le(keylet::sponsorship(sponsor, sponsee))
|
||||
->getFieldAmount(sfFeeAmount)
|
||||
.xrp();
|
||||
};
|
||||
|
||||
{
|
||||
@@ -1632,7 +1637,7 @@ public:
|
||||
BEAST_EXPECT(env.balance(bob) == bobBalance + XRP(100));
|
||||
BEAST_EXPECT(env.balance(sponsor) == sponsorBalance);
|
||||
BEAST_EXPECT(
|
||||
!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
!env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
}
|
||||
|
||||
// reset FeeAmount and MaxFee
|
||||
@@ -1689,14 +1694,16 @@ public:
|
||||
env(sponsor::set_fee(sponsor, 0, XRP(10)), sponsor::SponseeAcc(alice));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(
|
||||
env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
auto sponsorAvailableFee = sponsorFeeBalance(sponsor, alice);
|
||||
env(check::cancel(alice, uint256(1)),
|
||||
Fee(sponsorAvailableFee),
|
||||
sponsor::As(sponsor, spfSponsorFee),
|
||||
Ter(tecNO_ENTRY));
|
||||
env.close();
|
||||
BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(
|
||||
!env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1730,7 +1737,7 @@ public:
|
||||
BEAST_EXPECT(result.applied);
|
||||
|
||||
// Only MaxFee (10 drops) must be deducted, not the full 1000 drops.
|
||||
auto const sle = overlay.read(keylet::sponsor(carol.id(), alice.id()));
|
||||
auto const sle = overlay.read(keylet::sponsorship(carol.id(), alice.id()));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == drops(990)); // 1000 - MaxFee(10)
|
||||
@@ -1757,7 +1764,8 @@ public:
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(10));
|
||||
env.le(keylet::sponsorship(sponsor, alice))->getFieldAmount(sfFeeAmount) ==
|
||||
XRP(10));
|
||||
|
||||
// clear flag
|
||||
env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)),
|
||||
@@ -1765,7 +1773,7 @@ public:
|
||||
env.close();
|
||||
|
||||
// Payment is re-applied
|
||||
BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
}
|
||||
|
||||
// RequireSignForFee: co-signing should succeed
|
||||
@@ -1798,7 +1806,7 @@ public:
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(9));
|
||||
env.le(keylet::sponsorship(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(9));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1974,7 +1982,8 @@ public:
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(
|
||||
env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(10));
|
||||
env.le(keylet::sponsorship(sponsor, alice))->getFieldAmount(sfFeeAmount) ==
|
||||
XRP(10));
|
||||
|
||||
// clear flag
|
||||
env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)),
|
||||
@@ -1983,7 +1992,7 @@ public:
|
||||
|
||||
// CheckCreate is re-applied
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2091,7 +2100,7 @@ public:
|
||||
else
|
||||
{
|
||||
// cleanup previous sponsorship
|
||||
if (env.le(keylet::sponsor(sponsor, sponsee)))
|
||||
if (env.le(keylet::sponsorship(sponsor, sponsee)))
|
||||
{
|
||||
env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee));
|
||||
env.close();
|
||||
@@ -5468,7 +5477,7 @@ public:
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
auto const keylet = keylet::sponsor(sponsor, alice);
|
||||
auto const keylet = keylet::sponsorship(sponsor, alice);
|
||||
BEAST_EXPECT(env.le(keylet));
|
||||
// sponsor pays its own reserve here, so there is no SponsoredOwnerCount.
|
||||
BEAST_EXPECT(ownerCount(env, sponsor) == 1);
|
||||
@@ -5518,7 +5527,7 @@ public:
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
auto const keylet = keylet::sponsor(sponsor, alice);
|
||||
auto const keylet = keylet::sponsorship(sponsor, alice);
|
||||
auto const obj = env.le(keylet);
|
||||
BEAST_EXPECT(obj);
|
||||
BEAST_EXPECT(
|
||||
@@ -5885,10 +5894,10 @@ public:
|
||||
BEAST_EXPECT(env.balance(alice) == XRP(1000));
|
||||
BEAST_EXPECT(env.balance(sponsor) == XRP(900));
|
||||
|
||||
auto const sponsorshipSle = env.le(keylet::sponsor(sponsor, alice));
|
||||
auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sponsorshipSle);
|
||||
BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100 - 1));
|
||||
BEAST_EXPECT(sponsorshipSle->at(sfReserveCount) == 100);
|
||||
BEAST_EXPECT(sponsorshipSle->at(sfRemainingOwnerCount) == 100);
|
||||
}
|
||||
//
|
||||
// Inner transaction
|
||||
@@ -5991,10 +6000,10 @@ public:
|
||||
BEAST_EXPECT(env.balance(sponsor) == XRP(900));
|
||||
|
||||
// reserve count is decreased
|
||||
auto const sponsorshipSle = env.le(keylet::sponsor(sponsor, alice));
|
||||
auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sponsorshipSle);
|
||||
BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100));
|
||||
BEAST_EXPECT(sponsorshipSle->at(sfReserveCount) == 99);
|
||||
BEAST_EXPECT(sponsorshipSle->at(sfRemainingOwnerCount) == 99);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ set(jtx::Account const& account,
|
||||
jv[jss::Account] = account.human();
|
||||
jv[sfFlags.jsonName] = flags;
|
||||
if (reserveCount)
|
||||
jv[sfReserveCount.jsonName] = *reserveCount;
|
||||
jv[sfRemainingOwnerCount.jsonName] = *reserveCount;
|
||||
if (feeAmount)
|
||||
jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::Values::None);
|
||||
if (maxFee)
|
||||
@@ -61,7 +61,7 @@ set_reserve(jtx::Account const& account, uint32_t flags, uint32_t reserveCount)
|
||||
jv[jss::TransactionType] = jss::SponsorshipSet;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[sfFlags.jsonName] = flags;
|
||||
jv[sfReserveCount.jsonName] = reserveCount;
|
||||
jv[sfRemainingOwnerCount.jsonName] = reserveCount;
|
||||
return jv;
|
||||
}
|
||||
|
||||
|
||||
@@ -950,7 +950,7 @@ public:
|
||||
BEAST_EXPECT(sponsorship[sfSponsee.jsonName] == gw.human());
|
||||
BEAST_EXPECT(
|
||||
sponsorship[sfFlags.jsonName].asUInt() == tfSponsorshipSetRequireSignForFee);
|
||||
BEAST_EXPECT(sponsorship[sfReserveCount.jsonName].asUInt() == 200);
|
||||
BEAST_EXPECT(sponsorship[sfRemainingOwnerCount.jsonName].asUInt() == 200);
|
||||
BEAST_EXPECT(sponsorship[sfFeeAmount.jsonName].asUInt() == 100000000);
|
||||
BEAST_EXPECT(sponsorship[sfMaxFee.jsonName].asUInt() == 10);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ TEST(SponsorshipTests, BuilderSettersRoundTrip)
|
||||
auto const sponseeValue = canonical_ACCOUNT();
|
||||
auto const feeAmountValue = canonical_AMOUNT();
|
||||
auto const maxFeeValue = canonical_AMOUNT();
|
||||
auto const reserveCountValue = canonical_UINT32();
|
||||
auto const remainingOwnerCountValue = canonical_UINT32();
|
||||
auto const ownerNodeValue = canonical_UINT64();
|
||||
auto const sponseeNodeValue = canonical_UINT64();
|
||||
|
||||
@@ -41,7 +41,7 @@ TEST(SponsorshipTests, BuilderSettersRoundTrip)
|
||||
|
||||
builder.setFeeAmount(feeAmountValue);
|
||||
builder.setMaxFee(maxFeeValue);
|
||||
builder.setReserveCount(reserveCountValue);
|
||||
builder.setRemainingOwnerCount(remainingOwnerCountValue);
|
||||
|
||||
builder.setLedgerIndex(index);
|
||||
builder.setFlags(0x1u);
|
||||
@@ -105,11 +105,11 @@ TEST(SponsorshipTests, BuilderSettersRoundTrip)
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = reserveCountValue;
|
||||
auto const actualOpt = entry.getReserveCount();
|
||||
auto const& expected = remainingOwnerCountValue;
|
||||
auto const actualOpt = entry.getRemainingOwnerCount();
|
||||
ASSERT_TRUE(actualOpt.has_value());
|
||||
expectEqualField(expected, *actualOpt, "sfReserveCount");
|
||||
EXPECT_TRUE(entry.hasReserveCount());
|
||||
expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount");
|
||||
EXPECT_TRUE(entry.hasRemainingOwnerCount());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(entry.hasLedgerIndex());
|
||||
@@ -131,7 +131,7 @@ TEST(SponsorshipTests, BuilderFromSleRoundTrip)
|
||||
auto const sponseeValue = canonical_ACCOUNT();
|
||||
auto const feeAmountValue = canonical_AMOUNT();
|
||||
auto const maxFeeValue = canonical_AMOUNT();
|
||||
auto const reserveCountValue = canonical_UINT32();
|
||||
auto const remainingOwnerCountValue = canonical_UINT32();
|
||||
auto const ownerNodeValue = canonical_UINT64();
|
||||
auto const sponseeNodeValue = canonical_UINT64();
|
||||
|
||||
@@ -143,7 +143,7 @@ TEST(SponsorshipTests, BuilderFromSleRoundTrip)
|
||||
sle->at(sfSponsee) = sponseeValue;
|
||||
sle->at(sfFeeAmount) = feeAmountValue;
|
||||
sle->at(sfMaxFee) = maxFeeValue;
|
||||
sle->at(sfReserveCount) = reserveCountValue;
|
||||
sle->at(sfRemainingOwnerCount) = remainingOwnerCountValue;
|
||||
sle->at(sfOwnerNode) = ownerNodeValue;
|
||||
sle->at(sfSponseeNode) = sponseeNodeValue;
|
||||
|
||||
@@ -243,16 +243,16 @@ TEST(SponsorshipTests, BuilderFromSleRoundTrip)
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = reserveCountValue;
|
||||
auto const& expected = remainingOwnerCountValue;
|
||||
|
||||
auto const fromSleOpt = entryFromSle.getReserveCount();
|
||||
auto const fromBuilderOpt = entryFromBuilder.getReserveCount();
|
||||
auto const fromSleOpt = entryFromSle.getRemainingOwnerCount();
|
||||
auto const fromBuilderOpt = entryFromBuilder.getRemainingOwnerCount();
|
||||
|
||||
ASSERT_TRUE(fromSleOpt.has_value());
|
||||
ASSERT_TRUE(fromBuilderOpt.has_value());
|
||||
|
||||
expectEqualField(expected, *fromSleOpt, "sfReserveCount");
|
||||
expectEqualField(expected, *fromBuilderOpt, "sfReserveCount");
|
||||
expectEqualField(expected, *fromSleOpt, "sfRemainingOwnerCount");
|
||||
expectEqualField(expected, *fromBuilderOpt, "sfRemainingOwnerCount");
|
||||
}
|
||||
|
||||
EXPECT_EQ(entryFromSle.getKey(), index);
|
||||
@@ -323,7 +323,7 @@ TEST(SponsorshipTests, OptionalFieldsReturnNullopt)
|
||||
EXPECT_FALSE(entry.getFeeAmount().has_value());
|
||||
EXPECT_FALSE(entry.hasMaxFee());
|
||||
EXPECT_FALSE(entry.getMaxFee().has_value());
|
||||
EXPECT_FALSE(entry.hasReserveCount());
|
||||
EXPECT_FALSE(entry.getReserveCount().has_value());
|
||||
EXPECT_FALSE(entry.hasRemainingOwnerCount());
|
||||
EXPECT_FALSE(entry.getRemainingOwnerCount().has_value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip)
|
||||
auto const sponseeValue = canonical_ACCOUNT();
|
||||
auto const feeAmountValue = canonical_AMOUNT();
|
||||
auto const maxFeeValue = canonical_AMOUNT();
|
||||
auto const reserveCountValue = canonical_UINT32();
|
||||
auto const remainingOwnerCountValue = canonical_UINT32();
|
||||
|
||||
SponsorshipSetBuilder builder{
|
||||
accountValue,
|
||||
@@ -46,7 +46,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip)
|
||||
builder.setSponsee(sponseeValue);
|
||||
builder.setFeeAmount(feeAmountValue);
|
||||
builder.setMaxFee(maxFeeValue);
|
||||
builder.setReserveCount(reserveCountValue);
|
||||
builder.setRemainingOwnerCount(remainingOwnerCountValue);
|
||||
|
||||
auto tx = builder.build(publicKey, secretKey);
|
||||
|
||||
@@ -97,11 +97,11 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip)
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = reserveCountValue;
|
||||
auto const actualOpt = tx.getReserveCount();
|
||||
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveCount should be present";
|
||||
expectEqualField(expected, *actualOpt, "sfReserveCount");
|
||||
EXPECT_TRUE(tx.hasReserveCount());
|
||||
auto const& expected = remainingOwnerCountValue;
|
||||
auto const actualOpt = tx.getRemainingOwnerCount();
|
||||
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCount should be present";
|
||||
expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount");
|
||||
EXPECT_TRUE(tx.hasRemainingOwnerCount());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -124,7 +124,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip)
|
||||
auto const sponseeValue = canonical_ACCOUNT();
|
||||
auto const feeAmountValue = canonical_AMOUNT();
|
||||
auto const maxFeeValue = canonical_AMOUNT();
|
||||
auto const reserveCountValue = canonical_UINT32();
|
||||
auto const remainingOwnerCountValue = canonical_UINT32();
|
||||
|
||||
// Build an initial transaction
|
||||
SponsorshipSetBuilder initialBuilder{
|
||||
@@ -137,7 +137,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip)
|
||||
initialBuilder.setSponsee(sponseeValue);
|
||||
initialBuilder.setFeeAmount(feeAmountValue);
|
||||
initialBuilder.setMaxFee(maxFeeValue);
|
||||
initialBuilder.setReserveCount(reserveCountValue);
|
||||
initialBuilder.setRemainingOwnerCount(remainingOwnerCountValue);
|
||||
|
||||
auto initialTx = initialBuilder.build(publicKey, secretKey);
|
||||
|
||||
@@ -185,10 +185,10 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip)
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = reserveCountValue;
|
||||
auto const actualOpt = rebuiltTx.getReserveCount();
|
||||
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveCount should be present";
|
||||
expectEqualField(expected, *actualOpt, "sfReserveCount");
|
||||
auto const& expected = remainingOwnerCountValue;
|
||||
auto const actualOpt = rebuiltTx.getRemainingOwnerCount();
|
||||
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCount should be present";
|
||||
expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -254,8 +254,8 @@ TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt)
|
||||
EXPECT_FALSE(tx.getFeeAmount().has_value());
|
||||
EXPECT_FALSE(tx.hasMaxFee());
|
||||
EXPECT_FALSE(tx.getMaxFee().has_value());
|
||||
EXPECT_FALSE(tx.hasReserveCount());
|
||||
EXPECT_FALSE(tx.getReserveCount().has_value());
|
||||
EXPECT_FALSE(tx.hasRemainingOwnerCount());
|
||||
EXPECT_FALSE(tx.getRemainingOwnerCount().has_value());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -785,7 +785,7 @@ parseSponsorship(
|
||||
if (!sponseeAccountID)
|
||||
return std::unexpected(sponseeAccountID.error());
|
||||
|
||||
return keylet::sponsor(*sponsorAccountID, *sponseeAccountID).key;
|
||||
return keylet::sponsorship(*sponsorAccountID, *sponseeAccountID).key;
|
||||
}
|
||||
|
||||
static std::expected<uint256, json::Value>
|
||||
|
||||
Reference in New Issue
Block a user