diff --git a/cfg/rippled-example.cfg b/cfg/rippled-example.cfg index 8bffc150c1..04d1bfb4f7 100644 --- a/cfg/rippled-example.cfg +++ b/cfg/rippled-example.cfg @@ -1249,6 +1249,39 @@ # Example: # owner_reserve = 2000000 # 2 XRP # +# extension_compute_limit = +# +# The extension compute limit is the maximum amount of gas that can be +# consumed by a single transaction. The gas limit is used to prevent +# transactions from consuming too many resources. +# +# If this parameter is unspecified, rippled will use an internal +# default. Don't change this without understanding the consequences. +# +# Example: +# extension_compute_limit = 1000000 # 1 million gas +# +# extension_size_limit = +# +# The extension size limit is the maximum size of a WASM extension in +# bytes. The size limit is used to prevent extensions from consuming +# too many resources. +# +# If this parameter is unspecified, rippled will use an internal +# default. Don't change this without understanding the consequences. +# +# Example: +# extension_size_limit = 100000 # 100 kb +# +# gas_price = +# +# The gas price is the conversion between WASM gas and its price in drops. +# +# If this parameter is unspecified, rippled will use an internal +# default. Don't change this without understanding the consequences. +# +# Example: +# gas_price = 1000000 # 1 drop per gas #------------------------------------------------------------------------------- # # 9. Misc Settings diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 4393f1a1d9..1a1ecacb36 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -24,6 +24,8 @@ namespace ripple { +constexpr std::uint32_t MICRO_DROPS_PER_DROP{1'000'000}; + /** Reflects the fee settings for a particular ledger. The fees are always the same for any transactions applied @@ -34,6 +36,10 @@ struct Fees XRPAmount base{0}; // Reference tx cost (drops) XRPAmount reserve{0}; // Reserve base (drops) XRPAmount increment{0}; // Reserve increment (drops) + std::uint32_t extensionComputeLimit{ + 0}; // Extension compute limit (instructions) + std::uint32_t extensionSizeLimit{0}; // Extension size limit (bytes) + std::uint32_t gasPrice{0}; // price of WASM gas (micro-drops) explicit Fees() = default; Fees(Fees const&) = default; diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 9dc40dc8e5..c826c147f6 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -32,6 +32,7 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FEATURE(SmartEscrow, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (IncludeKeyletFields, Supported::no, VoteBehavior::DefaultNo) XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index f76188095e..bc78786d02 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -320,6 +320,11 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({ {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + // Smart Escrow fields + {sfExtensionComputeLimit, soeOPTIONAL}, + {sfExtensionSizeLimit, soeOPTIONAL}, + {sfGasPrice, soeOPTIONAL}, + {sfPreviousTxnID, soeOPTIONAL}, {sfPreviousTxnLgrSeq, soeOPTIONAL}, })) diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 10fe015dac..138276b514 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -115,6 +115,9 @@ TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50) TYPED_SFIELD(sfOracleDocumentID, UINT32, 51) TYPED_SFIELD(sfPermissionValue, UINT32, 52) TYPED_SFIELD(sfMutableFlags, UINT32, 53) +TYPED_SFIELD(sfExtensionComputeLimit, UINT32, 54) +TYPED_SFIELD(sfExtensionSizeLimit, UINT32, 55) +TYPED_SFIELD(sfGasPrice, UINT32, 56) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 3ea4a3bbec..f6e4f792b8 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -978,6 +978,10 @@ TRANSACTION(ttFEE, 101, SetFee, {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + // Smart Escrow fields + {sfExtensionComputeLimit, soeOPTIONAL}, + {sfExtensionSizeLimit, soeOPTIONAL}, + {sfGasPrice, soeOPTIONAL}, })) /** This system-generated transaction type is used to update the network's negative UNL diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 8609aedaef..7e62409da9 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -272,6 +272,9 @@ JSS(expected_date_UTC); // out: any (warnings) JSS(expected_ledger_size); // out: TxQ JSS(expiration); // out: AccountOffers, AccountChannels, // ValidatorList, amm_info +JSS(extension_compute); // out: NetworkOps +JSS(extension_size); // out: NetworkOps +JSS(gas_price); // out: NetworkOps JSS(fail_hard); // in: Sign, Submit JSS(failed); // out: InboundLedger JSS(feature); // in: Feature @@ -722,11 +725,11 @@ JSS(write_load); // out: GetCounts #pragma push_macro("LEDGER_ENTRY_DUPLICATE") #undef LEDGER_ENTRY_DUPLICATE -#define LEDGER_ENTRY(tag, value, name, rpcName, ...) \ - JSS(name); \ +#define LEDGER_ENTRY(tag, value, name, rpcName, fields) \ + JSS(name); \ JSS(rpcName); -#define LEDGER_ENTRY_DUPLICATE(tag, value, name, rpcName, ...) JSS(rpcName); +#define LEDGER_ENTRY_DUPLICATE(tag, value, name, rpcName, fields) JSS(rpcName); #include diff --git a/src/libxrpl/protocol/STValidation.cpp b/src/libxrpl/protocol/STValidation.cpp index 51246a1830..3a9d16445e 100644 --- a/src/libxrpl/protocol/STValidation.cpp +++ b/src/libxrpl/protocol/STValidation.cpp @@ -78,6 +78,10 @@ STValidation::validationFormat() {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + // featureSmartEscrow + {sfExtensionComputeLimit, soeOPTIONAL}, + {sfExtensionSizeLimit, soeOPTIONAL}, + {sfGasPrice, soeOPTIONAL}, }; // clang-format on diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index ba3d379219..6960ce7fa9 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -29,6 +29,7 @@ class FeeVote_test : public beast::unit_test::suite void testSetup() { + testcase("FeeVote setup"); FeeSetup const defaultSetup; { // defaults @@ -37,36 +38,62 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT( + setup.extension_compute_limit == + defaultSetup.extension_compute_limit); + BEAST_EXPECT( + setup.extension_size_limit == + defaultSetup.extension_size_limit); + BEAST_EXPECT(setup.gas_price == defaultSetup.gas_price); } { Section config; config.append( {"reference_fee = 50", "account_reserve = 1234567", - "owner_reserve = 1234"}); + "owner_reserve = 1234", + "extension_compute_limit = 100", + "extension_size_limit = 200", + " gas_price = 300"}); auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == 50); BEAST_EXPECT(setup.account_reserve == 1234567); BEAST_EXPECT(setup.owner_reserve == 1234); + BEAST_EXPECT(setup.extension_compute_limit == 100); + BEAST_EXPECT(setup.extension_size_limit == 200); + BEAST_EXPECT(setup.gas_price == 300); } { Section config; config.append( {"reference_fee = blah", "account_reserve = yada", - "owner_reserve = foo"}); + "owner_reserve = foo", + "extension_compute_limit = bar", + "extension_size_limit = baz", + "gas_price = qux"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT( + setup.extension_compute_limit == + defaultSetup.extension_compute_limit); + BEAST_EXPECT( + setup.extension_size_limit == + defaultSetup.extension_size_limit); + BEAST_EXPECT(setup.gas_price == defaultSetup.gas_price); } { Section config; config.append( {"reference_fee = -50", "account_reserve = -1234567", - "owner_reserve = -1234"}); + "owner_reserve = -1234", + "extension_compute_limit = -100", + "extension_size_limit = -200", + "gas_price = -300"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); @@ -74,6 +101,12 @@ class FeeVote_test : public beast::unit_test::suite setup.account_reserve == static_cast(-1234567)); BEAST_EXPECT( setup.owner_reserve == static_cast(-1234)); + BEAST_EXPECT( + setup.extension_compute_limit == + static_cast(-100)); + BEAST_EXPECT( + setup.extension_size_limit == static_cast(-200)); + BEAST_EXPECT(setup.gas_price == static_cast(-300)); } { auto const big64 = std::to_string( @@ -84,12 +117,22 @@ class FeeVote_test : public beast::unit_test::suite config.append( {"reference_fee = " + big64, "account_reserve = " + big64, - "owner_reserve = " + big64}); + "owner_reserve = " + big64, + "extension_compute_limit = " + big64, + "extension_size_limit = " + big64, + "gas_price = " + big64}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT( + setup.extension_compute_limit == + defaultSetup.extension_compute_limit); + BEAST_EXPECT( + setup.extension_size_limit == + defaultSetup.extension_size_limit); + BEAST_EXPECT(setup.gas_price == defaultSetup.gas_price); } } diff --git a/src/test/app/PseudoTx_test.cpp b/src/test/app/PseudoTx_test.cpp index 53adf795c2..4b7edc79d9 100644 --- a/src/test/app/PseudoTx_test.cpp +++ b/src/test/app/PseudoTx_test.cpp @@ -50,6 +50,12 @@ struct PseudoTx_test : public beast::unit_test::suite obj[sfReserveIncrement] = 0; obj[sfReferenceFeeUnits] = 0; } + if (rules.enabled(featureSmartEscrow)) + { + obj[sfExtensionComputeLimit] = 0; + obj[sfExtensionSizeLimit] = 0; + obj[sfGasPrice] = 0; + } })); res.emplace_back(STTx(ttAMENDMENT, [&](auto& obj) { @@ -118,7 +124,9 @@ struct PseudoTx_test : public beast::unit_test::suite FeatureBitset const all{testable_amendments()}; FeatureBitset const xrpFees{featureXRPFees}; + testPrevented(all - featureXRPFees - featureSmartEscrow); testPrevented(all - featureXRPFees); + testPrevented(all - featureSmartEscrow); testPrevented(all); testAllowed(); } diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 9ed02fa532..ad5f0ca33a 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -532,6 +532,22 @@ public: if (jv.isMember(jss::reserve_inc) != isFlagLedger) return false; + if (env.closed()->rules().enabled(featureSmartEscrow)) + { + if (jv.isMember(jss::extension_compute) != isFlagLedger) + return false; + + if (jv.isMember(jss::extension_size) != isFlagLedger) + return false; + } + else + { + if (jv.isMember(jss::extension_compute)) + return false; + + if (jv.isMember(jss::extension_size)) + return false; + } return true; }; @@ -1596,7 +1612,8 @@ public: testTransactions_APIv1(); testTransactions_APIv2(); testManifests(); - testValidations(all - xrpFees); + testValidations(all - featureXRPFees - featureSmartEscrow); + testValidations(all - featureSmartEscrow); testValidations(all); testSubErrors(true); testSubErrors(false); diff --git a/src/xrpld/app/ledger/Ledger.cpp b/src/xrpld/app/ledger/Ledger.cpp index 6de4f2cbde..f3a5c42566 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/xrpld/app/ledger/Ledger.cpp @@ -221,6 +221,15 @@ Ledger::Ledger( sle->at(sfReserveIncrement) = *f; sle->at(sfReferenceFeeUnits) = Config::FEE_UNITS_DEPRECATED; } + if (std::find( + amendments.begin(), amendments.end(), featureSmartEscrow) != + amendments.end()) + { + sle->at(sfExtensionComputeLimit) = + config.FEES.extension_compute_limit; + sle->at(sfExtensionSizeLimit) = config.FEES.extension_size_limit; + sle->at(sfGasPrice) = config.FEES.gas_price; + } rawInsert(sle); } @@ -612,6 +621,7 @@ Ledger::setup() { bool oldFees = false; bool newFees = false; + bool extensionFees = false; { auto const baseFee = sle->at(~sfBaseFee); auto const reserveBase = sle->at(~sfReserveBase); @@ -629,6 +639,7 @@ Ledger::setup() auto const reserveBaseXRP = sle->at(~sfReserveBaseDrops); auto const reserveIncrementXRP = sle->at(~sfReserveIncrementDrops); + auto assign = [&ret]( XRPAmount& dest, std::optional const& src) { @@ -645,12 +656,35 @@ Ledger::setup() assign(fees_.increment, reserveIncrementXRP); newFees = baseFeeXRP || reserveBaseXRP || reserveIncrementXRP; } + { + auto const extensionComputeLimit = + sle->at(~sfExtensionComputeLimit); + auto const extensionSizeLimit = sle->at(~sfExtensionSizeLimit); + auto const gasPrice = sle->at(~sfGasPrice); + + auto assign = [](std::uint32_t& dest, + std::optional const& src) { + if (src) + { + dest = src.value(); + } + }; + assign(fees_.extensionComputeLimit, extensionComputeLimit); + assign(fees_.extensionSizeLimit, extensionSizeLimit); + assign(fees_.gasPrice, gasPrice); + extensionFees = + extensionComputeLimit || extensionSizeLimit || gasPrice; + } if (oldFees && newFees) // Should be all of one or the other, but not both ret = false; if (!rules_.enabled(featureXRPFees) && newFees) // Can't populate the new fees before the amendment is enabled ret = false; + if (!rules_.enabled(featureSmartEscrow) && extensionFees) + // Can't populate the extension fees before the amendment is + // enabled + ret = false; } } catch (SHAMapMissingNode const&) @@ -669,15 +703,20 @@ Ledger::setup() void Ledger::defaultFees(Config const& config) { - XRPL_ASSERT( - fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0, - "ripple::Ledger::defaultFees : zero fees"); + assert( + fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0 && + fees_.extensionComputeLimit == 0 && fees_.extensionSizeLimit == 0 && + fees_.gasPrice == 0); if (fees_.base == 0) fees_.base = config.FEES.reference_fee; if (fees_.reserve == 0) fees_.reserve = config.FEES.account_reserve; - if (fees_.increment == 0) - fees_.increment = config.FEES.owner_reserve; + if (fees_.extensionComputeLimit == 0) + fees_.extensionComputeLimit = config.FEES.extension_compute_limit; + if (fees_.extensionSizeLimit == 0) + fees_.extensionSizeLimit = config.FEES.extension_size_limit; + if (fees_.gasPrice == 0) + fees_.gasPrice = config.FEES.gas_price; } std::shared_ptr diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index 85b5791d67..ba8b1d5d5c 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -28,10 +28,10 @@ namespace ripple { namespace detail { +template class VotableValue { private: - using value_type = XRPAmount; value_type const current_; // The current setting value_type const target_; // The setting we want std::map voteMap_; @@ -66,8 +66,9 @@ public: getVotes() const; }; -auto -VotableValue::getVotes() const -> std::pair +template +std::pair +VotableValue::getVotes() const { value_type ourVote = current_; int weight = 0; @@ -190,6 +191,33 @@ FeeVoteImpl::doValidation( "reserve increment", sfReserveIncrement); } + if (rules.enabled(featureSmartEscrow)) + { + auto vote = [&v, this]( + auto const current, + std::uint32_t target, + char const* name, + auto const& sfield) { + if (current != target) + { + JLOG(journal_.info()) + << "Voting for " << name << " of " << target; + + v[sfield] = target; + } + }; + vote( + lastFees.extensionComputeLimit, + target_.extension_compute_limit, + "extension compute limit", + sfExtensionComputeLimit); + vote( + lastFees.extensionSizeLimit, + target_.extension_size_limit, + "extension size limit", + sfExtensionSizeLimit); + vote(lastFees.gasPrice, target_.gas_price, "gas price", sfGasPrice); + } } void @@ -212,11 +240,22 @@ FeeVoteImpl::doVoting( detail::VotableValue incReserveVote( lastClosedLedger->fees().increment, target_.owner_reserve); + detail::VotableValue extensionComputeVote( + lastClosedLedger->fees().extensionComputeLimit, + target_.extension_compute_limit); + + detail::VotableValue extensionSizeVote( + lastClosedLedger->fees().extensionSizeLimit, + target_.extension_size_limit); + + detail::VotableValue gasPriceVote( + lastClosedLedger->fees().gasPrice, target_.gas_price); + auto const& rules = lastClosedLedger->rules(); if (rules.enabled(featureXRPFees)) { auto doVote = [](std::shared_ptr const& val, - detail::VotableValue& value, + detail::VotableValue& value, SF_AMOUNT const& xrpField) { if (auto const field = ~val->at(~xrpField); field && field->native()) @@ -245,7 +284,7 @@ FeeVoteImpl::doVoting( else { auto doVote = [](std::shared_ptr const& val, - detail::VotableValue& value, + detail::VotableValue& value, auto const& valueField) { if (auto const field = val->at(~valueField)) { @@ -276,6 +315,30 @@ FeeVoteImpl::doVoting( doVote(val, incReserveVote, sfReserveIncrement); } } + if (rules.enabled(featureSmartEscrow)) + { + auto doVote = [](std::shared_ptr const& val, + detail::VotableValue& value, + SF_UINT32 const& extensionField) { + if (auto const field = ~val->at(~extensionField); field) + { + value.addVote(field.value()); + } + else + { + value.noVote(); + } + }; + + for (auto const& val : set) + { + if (!val->isTrusted()) + continue; + doVote(val, extensionComputeVote, sfExtensionComputeLimit); + doVote(val, extensionSizeVote, sfExtensionSizeLimit); + doVote(val, gasPriceVote, sfGasPrice); + } + } // choose our positions // TODO: Use structured binding once LLVM 16 is the minimum supported @@ -284,11 +347,15 @@ FeeVoteImpl::doVoting( auto const baseFee = baseFeeVote.getVotes(); auto const baseReserve = baseReserveVote.getVotes(); auto const incReserve = incReserveVote.getVotes(); + auto const extensionCompute = extensionComputeVote.getVotes(); + auto const extensionSize = extensionSizeVote.getVotes(); + auto const gasPrice = gasPriceVote.getVotes(); auto const seq = lastClosedLedger->info().seq + 1; // add transactions to our position - if (baseFee.second || baseReserve.second || incReserve.second) + if (baseFee.second || baseReserve.second || incReserve.second || + extensionCompute.second || extensionSize.second || gasPrice.second) { JLOG(journal_.warn()) << "We are voting for a fee change: " << baseFee.first << "/" @@ -316,6 +383,12 @@ FeeVoteImpl::doVoting( incReserveVote.current()); obj[sfReferenceFeeUnits] = Config::FEE_UNITS_DEPRECATED; } + if (rules.enabled(featureSmartEscrow)) + { + obj[sfExtensionComputeLimit] = extensionCompute.first; + obj[sfExtensionSizeLimit] = extensionSize.first; + obj[sfGasPrice] = gasPrice.first; + } }); uint256 txID = feeTx.getTransactionID(); diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index b9069442f8..2443da10d2 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -1452,11 +1452,6 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) for (auto& t : transactions) mTransactions.push_back(std::move(t)); } - if (mTransactions.empty()) - { - JLOG(m_journal.debug()) << "No transaction to process!"; - return; - } doTransactionSyncBatch(lock, [&](std::unique_lock const&) { XRPL_ASSERT( @@ -2482,6 +2477,18 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) reserveIncXRP && reserveIncXRP->native()) jvObj[jss::reserve_inc] = reserveIncXRP->xrp().jsonClipped(); + if (auto const extensionComputeLimit = + ~val->at(~sfExtensionComputeLimit); + extensionComputeLimit) + jvObj[jss::extension_compute] = *extensionComputeLimit; + + if (auto const extensionSizeLimit = ~val->at(~sfExtensionSizeLimit); + extensionSizeLimit) + jvObj[jss::extension_size] = *extensionSizeLimit; + + if (auto const gasPrice = ~val->at(~sfGasPrice); gasPrice) + jvObj[jss::gas_price] = *gasPrice; + // NOTE Use MultiApiJson to publish two slightly different JSON objects // for consumers supporting different API versions MultiApiJson multiObj{jvObj}; @@ -2931,12 +2938,22 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::seq] = Json::UInt(lpClosed->info().seq); l[jss::hash] = to_string(lpClosed->info().hash); + bool const smartEscrowEnabled = + m_ledgerMaster.getValidatedLedger()->rules().enabled( + featureSmartEscrow); if (!human) { l[jss::base_fee] = baseFee.jsonClipped(); l[jss::reserve_base] = lpClosed->fees().accountReserve(0).jsonClipped(); l[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); + if (smartEscrowEnabled) + { + l[jss::extension_compute] = + lpClosed->fees().extensionComputeLimit; + l[jss::extension_size] = lpClosed->fees().extensionSizeLimit; + l[jss::gas_price] = lpClosed->fees().gasPrice; + } l[jss::close_time] = Json::Value::UInt( lpClosed->info().closeTime.time_since_epoch().count()); } @@ -2946,6 +2963,13 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::reserve_base_xrp] = lpClosed->fees().accountReserve(0).decimalXRP(); l[jss::reserve_inc_xrp] = lpClosed->fees().increment.decimalXRP(); + if (smartEscrowEnabled) + { + l[jss::extension_compute] = + lpClosed->fees().extensionComputeLimit; + l[jss::extension_size] = lpClosed->fees().extensionSizeLimit; + l[jss::gas_price] = lpClosed->fees().gasPrice; + } if (auto const closeOffset = app_.timeKeeper().closeOffset(); std::abs(closeOffset.count()) >= 60) @@ -3138,6 +3162,14 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) lpAccepted->fees().accountReserve(0).jsonClipped(); jvObj[jss::reserve_inc] = lpAccepted->fees().increment.jsonClipped(); + if (lpAccepted->rules().enabled(featureSmartEscrow)) + { + jvObj[jss::extension_compute] = + lpAccepted->fees().extensionComputeLimit; + jvObj[jss::extension_size] = + lpAccepted->fees().extensionSizeLimit; + jvObj[jss::gas_price] = lpAccepted->fees().gasPrice; + } jvObj[jss::txn_count] = Json::UInt(alpAccepted->size()); @@ -3508,8 +3540,8 @@ NetworkOPsImp::pubAccountTransaction( } JLOG(m_journal.trace()) - << "pubAccountTransaction: " - << "proposed=" << iProposed << ", accepted=" << iAccepted; + << "pubAccountTransaction: " << "proposed=" << iProposed + << ", accepted=" << iAccepted; if (!notify.empty() || !accountHistoryNotify.empty()) { @@ -4183,13 +4215,20 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) jvResult[jss::ledger_hash] = to_string(lpClosed->info().hash); jvResult[jss::ledger_time] = Json::Value::UInt( lpClosed->info().closeTime.time_since_epoch().count()); + jvResult[jss::network_id] = app_.config().NETWORK_ID; if (!lpClosed->rules().enabled(featureXRPFees)) jvResult[jss::fee_ref] = Config::FEE_UNITS_DEPRECATED; jvResult[jss::fee_base] = lpClosed->fees().base.jsonClipped(); jvResult[jss::reserve_base] = lpClosed->fees().accountReserve(0).jsonClipped(); jvResult[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); - jvResult[jss::network_id] = app_.config().NETWORK_ID; + if (lpClosed->rules().enabled(featureSmartEscrow)) + { + jvResult[jss::extension_compute] = + lpClosed->fees().extensionComputeLimit; + jvResult[jss::extension_size] = lpClosed->fees().extensionSizeLimit; + jvResult[jss::gas_price] = lpClosed->fees().gasPrice; + } } if ((mMode >= OperatingMode::SYNCING) && !isNeedNetworkLedger()) diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index de30ed5f62..7b1f649c56 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -130,6 +130,20 @@ Change::preclaim(PreclaimContext const& ctx) ctx.tx.isFieldPresent(sfReserveIncrementDrops)) return temDISABLED; } + if (ctx.view.rules().enabled(featureSmartEscrow)) + { + if (!ctx.tx.isFieldPresent(sfExtensionComputeLimit) || + !ctx.tx.isFieldPresent(sfExtensionSizeLimit) || + !ctx.tx.isFieldPresent(sfGasPrice)) + return temMALFORMED; + } + else + { + if (ctx.tx.isFieldPresent(sfExtensionComputeLimit) || + ctx.tx.isFieldPresent(sfExtensionSizeLimit) || + ctx.tx.isFieldPresent(sfGasPrice)) + return temDISABLED; + } return tesSUCCESS; case ttAMENDMENT: case ttUNL_MODIFY: @@ -377,6 +391,12 @@ Change::applyFee() set(feeObject, ctx_.tx, sfReserveBase); set(feeObject, ctx_.tx, sfReserveIncrement); } + if (view().rules().enabled(featureSmartEscrow)) + { + set(feeObject, ctx_.tx, sfExtensionComputeLimit); + set(feeObject, ctx_.tx, sfExtensionSizeLimit); + set(feeObject, ctx_.tx, sfGasPrice); + } view().update(feeObject); diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index a58867958b..7a4b087403 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -73,6 +73,15 @@ struct FeeSetup /** The per-owned item reserve requirement in drops. */ XRPAmount owner_reserve{2 * DROPS_PER_XRP}; + /** The compute limit for Feature Extensions. */ + std::uint32_t extension_compute_limit{1'000'000}; + + /** The WASM size limit for Feature Extensions. */ + std::uint32_t extension_size_limit{100'000}; + + /** The price of 1 WASM gas, in micro-drops. */ + std::uint32_t gas_price{1'000'000}; + /* (Remember to update the example cfg files when changing any of these * values.) */ }; diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index 95147e23d5..994865732e 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -1141,6 +1141,12 @@ setup_FeeVote(Section const& section) setup.account_reserve = temp; if (set(temp, "owner_reserve", section)) setup.owner_reserve = temp; + if (set(temp, "extension_compute_limit", section)) + setup.extension_compute_limit = temp; + if (set(temp, "extension_size_limit", section)) + setup.extension_size_limit = temp; + if (set(temp, "gas_price", section)) + setup.gas_price = temp; } return setup; }