diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index c3fd51bc7..26b9c08b6 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -456,6 +456,7 @@ target_sources (rippled PRIVATE src/ripple/app/tx/impl/Import.cpp src/ripple/app/tx/impl/Invoke.cpp src/ripple/app/tx/impl/Remit.cpp + src/ripple/app/tx/impl/SetRemarks.cpp src/ripple/app/tx/impl/SetSignerList.cpp src/ripple/app/tx/impl/SetTrust.cpp src/ripple/app/tx/impl/SignerEntries.cpp diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 0b18e6a8d..b26620d85 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -17,6 +17,10 @@ #include #include #include +#include +#include +#include + using namespace ripple; diff --git a/src/ripple/app/tx/impl/SetRemarks.cpp b/src/ripple/app/tx/impl/SetRemarks.cpp index 5d9aabb43..ef33476a6 100644 --- a/src/ripple/app/tx/impl/SetRemarks.cpp +++ b/src/ripple/app/tx/impl/SetRemarks.cpp @@ -59,9 +59,18 @@ SetRemarks::preflight(PreflightContext const& ctx) return temMALFORMED; } - for (auto const& remark : remarks) { + if (remark.getFName() != sfRemark) + { + JLOG(j.warn()) << "Remarks: contained non-sfRemark field."; + return temMALFORMED; + } + + // will be checked by template system, extra check for security + if (!remark.isFieldPresent(sfRemarkName)) + return temMALFORMED; + Blob const& name = remark.getFieldVL(sfRemarkName); if (already_seen.find(name) != already_seen.end()) { @@ -204,14 +213,14 @@ SetRemarks::preclaim(PreclaimContext const& ctx) if (!sleO) return tecNO_TARGET; - std::optional issuer = getRemarksIssuer(sleO); + std::optional issuer = getRemarksIssuer(sleO); if (!issuer || *issuer != id) return tecNO_PERMISSION; // sanity check the remarks merge between txn and obj - auto const& remarksTxn = tx.getFieldArray(sfRemarks); + auto const& remarksTxn = ctx.tx.getFieldArray(sfRemarks); std::map> keys; if (sleO->isFieldPresent(sfRemarks)) @@ -220,9 +229,10 @@ SetRemarks::preclaim(PreclaimContext const& ctx) // map the remark name to its value and whether it's immutable for (auto const& remark : remarksObj) - keys.emplace_back(remark.getFieldVL(sfRemarkName), - {remark.getFieldVL(sfRemarkValue), - remark.isFieldPresent(sfFlags) && remark.getFieldU32(sfFlags) & tfImmutable}); + keys.emplace( + std::make_pair(remark.getFieldVL(sfRemarkName), + std::make_pair(remark.getFieldVL(sfRemarkValue), + remark.isFieldPresent(sfFlags) && remark.getFieldU32(sfFlags) & tfImmutable))); } int64_t count = keys.size(); @@ -258,7 +268,7 @@ SetRemarks::preclaim(PreclaimContext const& ctx) if (immutable) { - JLOG(j.warn()) + JLOG(ctx.j.warn()) << "Remarks: attempt to mutate an immutable remark."; return tecIMMUTABLE; } @@ -267,7 +277,7 @@ SetRemarks::preclaim(PreclaimContext const& ctx) { if (--count < 0) { - JLOG(j.warn()) + JLOG(ctx.j.warn()) << "Remarks: insane remarks accounting."; return tecCLAIM; } @@ -276,7 +286,7 @@ SetRemarks::preclaim(PreclaimContext const& ctx) if (count > 32) { - JLOG(j.warn()) + JLOG(ctx.j.warn()) << "Remarks: an object may have at most 32 remarks."; return tecTOO_MANY_REMARKS; } @@ -287,6 +297,8 @@ SetRemarks::preclaim(PreclaimContext const& ctx) TER SetRemarks::doApply() { + auto j = ctx_.journal; + auto const sle = view().peek(keylet::account(account_)); if (!sle) return tefINTERNAL; @@ -296,9 +308,9 @@ SetRemarks::doApply() if (!sleO) return tecNO_TARGET; - std::optional issuer = getRemarksIssuer(sleO); + std::optional issuer = getRemarksIssuer(sleO); - if (!issuer || *issuer != id) + if (!issuer || *issuer != account_) return tecNO_PERMISSION; auto const& remarksTxn = ctx_.tx.getFieldArray(sfRemarks); @@ -338,7 +350,7 @@ SetRemarks::doApply() if (remarksMap.find(name) == remarksMap.end()) { - remarksMap[name] = {name, {*val, setImmutable}}; + remarksMap[name] = std::make_pair(*val, setImmutable); continue; } @@ -364,22 +376,30 @@ SetRemarks::doApply() if (remarksMap[k].second & tfImmutable) remark.setFieldU32(sfFlags, lsfImmutable); - newRemarks.push_back(std::map(remark)); + newRemarks.push_back(std::move(remark)); } if (newRemarks.size() > 32) return tecINTERNAL; if (newRemarks.empty() && sleO->isFieldPresent(sfRemarks)) - sleO.makeFieldAbsent(sfRemarks); + sleO->makeFieldAbsent(sfRemarks); else - sleO.setFieldArray(sfRemarks, std::move(newRemarks)); + sleO->setFieldArray(sfRemarks, std::move(newRemarks)); - view.update(sleO); + view().update(sleO); return tesSUCCESS; } -// RH TODO: transaction fee needs to charge for remarks, in particular because they are not ownercounted. +XRPAmount +calculateBaseFee(ReadView const& view, STTx const& tx) +{ + // RH TODO: transaction fee needs to charge for remarks, in particular because they are not ownercounted. + auto fee = Transactor::calculateBaseFee(view, tx); + return fee; +} + + } // namespace ripple diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp index 4658f3348..a7ebe246f 100644 --- a/src/ripple/app/tx/impl/applySteps.cpp +++ b/src/ripple/app/tx/impl/applySteps.cpp @@ -168,7 +168,7 @@ invoke_preflight(PreflightContext const& ctx) case ttREMIT: return invoke_preflight_helper(ctx); case ttREMARKS_SET: - return invoke_preflight_helper(ctx); + return invoke_preflight_helper(ctx); case ttURITOKEN_MINT: case ttURITOKEN_BURN: case ttURITOKEN_BUY: @@ -291,7 +291,7 @@ invoke_preclaim(PreclaimContext const& ctx) case ttREMIT: return invoke_preclaim(ctx); case ttREMARKS_SET: - return invoke_preclaim(ctx); + return invoke_preclaim(ctx); case ttURITOKEN_MINT: case ttURITOKEN_BURN: case ttURITOKEN_BUY: @@ -376,7 +376,7 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx) case ttREMIT: return Remit::calculateBaseFee(view, tx); case ttREMARKS_SET: - return Remarks::calculateBaseFee(view, tx); + return SetRemarks::calculateBaseFee(view, tx); case ttURITOKEN_MINT: case ttURITOKEN_BURN: case ttURITOKEN_BUY: @@ -561,7 +561,7 @@ invoke_apply(ApplyContext& ctx) return p(); } case ttREMARKS_SET: { - Remarks p(ctx); + SetRemarks p(ctx); return p(); } case ttURITOKEN_MINT: diff --git a/src/ripple/protocol/LedgerFormats.h b/src/ripple/protocol/LedgerFormats.h index 6134a8f33..56321df7a 100644 --- a/src/ripple/protocol/LedgerFormats.h +++ b/src/ripple/protocol/LedgerFormats.h @@ -315,6 +315,9 @@ enum LedgerSpecificFlags { // ltURI_TOKEN lsfBurnable = 0x00000001, // True, issuer can burn the token + + // remarks + lsfImmutable = 1, }; //------------------------------------------------------------------------------ diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index 5a59df960..a5cdd4153 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -538,6 +538,8 @@ extern SF_VL const sfHookReturnString; extern SF_VL const sfHookParameterName; extern SF_VL const sfHookParameterValue; extern SF_VL const sfBlob; +extern SF_VL const sfRemarkName; +extern SF_VL const sfRemarkValue; // account extern SF_ACCOUNT const sfAccount; @@ -595,6 +597,7 @@ extern SField const sfImportVLKey; extern SField const sfHookEmission; extern SField const sfMintURIToken; extern SField const sfAmountEntry; +extern SField const sfRemark; // array of objects (common) // ARRAY/1 is reserved for end of array @@ -623,6 +626,7 @@ extern SField const sfActiveValidators; extern SField const sfImportVLKeys; extern SField const sfHookEmissions; extern SField const sfAmounts; +extern SField const sfRemarks; //------------------------------------------------------------------------------ diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index 31cf8385b..659aa6232 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -121,6 +121,7 @@ JSS(Remit); // transaction type. JSS(RippleState); // ledger type. JSS(SLE_hit_rate); // out: GetCounts. JSS(SetFee); // transaction type. +JSS(SetRemarks); // transaction type JSS(UNLModify); // transaction type. JSS(UNLReport); // transaction type. JSS(SettleDelay); // in: TransactionSign