diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 94f753d8f3..43b21b5a85 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -24,13 +24,17 @@ struct FeeSettingsFields std::optional baseFeeDrops = std::nullopt; std::optional reserveBaseDrops = std::nullopt; std::optional reserveIncrementDrops = std::nullopt; + std::optional extensionComputeLimit = std::nullopt; + std::optional extensionSizeLimit = std::nullopt; + std::optional gasPrice = std::nullopt; }; STTx createFeeTx( Rules const& rules, std::uint32_t seq, - FeeSettingsFields const& fields) + FeeSettingsFields const& fields, + bool forceAllFields = false) { auto fill = [&](auto& obj) { obj.setAccountID(sfAccount, AccountID()); @@ -64,6 +68,17 @@ createFeeTx( sfReferenceFeeUnits, fields.referenceFeeUnits ? *fields.referenceFeeUnits : 0); } + if (rules.enabled(featureSmartEscrow) || forceAllFields) + { + obj.setFieldU32( + sfExtensionComputeLimit, + fields.extensionComputeLimit ? *fields.extensionComputeLimit + : 0); + obj.setFieldU32( + sfExtensionSizeLimit, + fields.extensionSizeLimit ? *fields.extensionSizeLimit : 0); + obj.setFieldU32(sfGasPrice, fields.gasPrice ? *fields.gasPrice : 0); + } }; return STTx(ttFEE, fill); } @@ -112,6 +127,12 @@ createInvalidFeeTx( obj.setFieldU32(sfReserveIncrement, 50000); obj.setFieldU32(sfReferenceFeeUnits, 10); } + if (rules.enabled(featureSmartEscrow)) + { + obj.setFieldU32(sfExtensionComputeLimit, 100 + uniqueValue); + obj.setFieldU32(sfExtensionSizeLimit, 200 + uniqueValue); + obj.setFieldU32(sfGasPrice, 300 + uniqueValue); + } } // If missingRequiredFields is true, we don't add the required fields // (default behavior) @@ -119,12 +140,12 @@ createInvalidFeeTx( return STTx(ttFEE, fill); } -bool +TER applyFeeAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx) { auto const res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal); - return res.ter == tesSUCCESS; + return res.ter; } bool @@ -180,6 +201,25 @@ verifyFeeObject( if (!checkEquality(sfReferenceFeeUnits, expected.referenceFeeUnits)) return false; } + if (rules.enabled(featureSmartEscrow)) + { + if (!checkEquality( + sfExtensionComputeLimit, + expected.extensionComputeLimit.value_or(0))) + return false; + if (!checkEquality( + sfExtensionSizeLimit, expected.extensionSizeLimit.value_or(0))) + return false; + if (!checkEquality(sfGasPrice, expected.gasPrice.value_or(0))) + return false; + } + else + { + if (feeObject->isFieldPresent(sfExtensionComputeLimit) || + feeObject->isFieldPresent(sfExtensionSizeLimit) || + feeObject->isFieldPresent(sfGasPrice)) + return false; + } return true; } @@ -202,6 +242,7 @@ class FeeVote_test : public beast::unit_test::suite void testSetup() { + testcase("FeeVote setup"); FeeSetup const defaultSetup; { // defaults @@ -210,36 +251,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); @@ -247,6 +314,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( @@ -257,12 +330,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); } } @@ -273,7 +356,10 @@ class FeeVote_test : public beast::unit_test::suite // Test with XRPFees disabled (legacy format) { - jtx::Env env(*this, jtx::testable_amendments() - featureXRPFees); + jtx::Env env( + *this, + jtx::testable_amendments() - featureXRPFees - + featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -294,7 +380,8 @@ class FeeVote_test : public beast::unit_test::suite auto feeTx = createFeeTx(ledger->rules(), ledger->seq(), fields); OpenView accum(ledger.get()); - BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx)); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, accum, feeTx))); accum.apply(*ledger); // Verify fee object was created/updated correctly @@ -303,7 +390,8 @@ class FeeVote_test : public beast::unit_test::suite // Test with XRPFees enabled (new format) { - jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); + jtx::Env env( + *this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -322,12 +410,76 @@ class FeeVote_test : public beast::unit_test::suite auto feeTx = createFeeTx(ledger->rules(), ledger->seq(), fields); OpenView accum(ledger.get()); - BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx)); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, accum, feeTx))); accum.apply(*ledger); // Verify fee object was created/updated correctly BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields)); } + + // Test with both XRPFees and SmartEscrow enabled + { + jtx::Env env(*this, jtx::testable_amendments()); + auto ledger = std::make_shared( + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); + + // Create the next ledger to apply transaction to + ledger = std::make_shared( + *ledger, env.app().timeKeeper().closeTime()); + + FeeSettingsFields fields{ + .baseFeeDrops = XRPAmount{10}, + .reserveBaseDrops = XRPAmount{200000}, + .reserveIncrementDrops = XRPAmount{50000}, + .extensionComputeLimit = 100, + .extensionSizeLimit = 200, + .gasPrice = 300}; + // Test successful fee transaction with new fields + auto feeTx = createFeeTx(ledger->rules(), ledger->seq(), fields); + + OpenView accum(ledger.get()); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, accum, feeTx))); + accum.apply(*ledger); + + // Verify fee object was created/updated correctly + BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields)); + } + + // Test that the Smart Escrow fields are rejected if the + // feature is disabled + { + jtx::Env env( + *this, jtx::testable_amendments() - featureSmartEscrow); + auto ledger = std::make_shared( + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); + + // Create the next ledger to apply transaction to + ledger = std::make_shared( + *ledger, env.app().timeKeeper().closeTime()); + + FeeSettingsFields fields{ + .baseFeeDrops = XRPAmount{10}, + .reserveBaseDrops = XRPAmount{200000}, + .reserveIncrementDrops = XRPAmount{50000}, + .extensionComputeLimit = 100, + .extensionSizeLimit = 200, + .gasPrice = 300}; + // Test successful fee transaction with new fields + auto feeTx = + createFeeTx(ledger->rules(), ledger->seq(), fields, true); + + OpenView accum(ledger.get()); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, feeTx))); + } } void @@ -336,7 +488,10 @@ class FeeVote_test : public beast::unit_test::suite testcase("Fee Transaction Validation"); { - jtx::Env env(*this, jtx::testable_amendments() - featureXRPFees); + jtx::Env env( + *this, + jtx::testable_amendments() - featureXRPFees - + featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -351,16 +506,19 @@ class FeeVote_test : public beast::unit_test::suite auto invalidTx = createInvalidFeeTx( ledger->rules(), ledger->seq(), true, false, 1); OpenView accum(ledger.get()); - BEAST_EXPECT(!applyFeeAndTestResult(env, accum, invalidTx)); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, invalidTx))); // Test transaction with new format fields when XRPFees is disabled auto disallowedTx = createInvalidFeeTx( ledger->rules(), ledger->seq(), false, true, 2); - BEAST_EXPECT(!applyFeeAndTestResult(env, accum, disallowedTx)); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, disallowedTx))); } { - jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); + jtx::Env env( + *this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -375,12 +533,43 @@ class FeeVote_test : public beast::unit_test::suite auto invalidTx = createInvalidFeeTx( ledger->rules(), ledger->seq(), true, false, 3); OpenView accum(ledger.get()); - BEAST_EXPECT(!applyFeeAndTestResult(env, accum, invalidTx)); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, invalidTx))); // Test transaction with legacy fields when XRPFees is enabled auto disallowedTx = createInvalidFeeTx( ledger->rules(), ledger->seq(), false, true, 4); - BEAST_EXPECT(!applyFeeAndTestResult(env, accum, disallowedTx)); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, disallowedTx))); + } + + { + jtx::Env env( + *this, + jtx::testable_amendments() | featureXRPFees | + featureSmartEscrow); + auto ledger = std::make_shared( + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); + + // Create the next ledger to apply transaction to + ledger = std::make_shared( + *ledger, env.app().timeKeeper().closeTime()); + + // Test transaction with missing required new fields + auto invalidTx = createInvalidFeeTx( + ledger->rules(), ledger->seq(), true, false, 5); + OpenView accum(ledger.get()); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, invalidTx))); + + // Test transaction with legacy fields when XRPFees is enabled + auto disallowedTx = createInvalidFeeTx( + ledger->rules(), ledger->seq(), false, true, 6); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, disallowedTx))); } } @@ -389,7 +578,7 @@ class FeeVote_test : public beast::unit_test::suite { testcase("Pseudo Transaction Properties"); - jtx::Env env(*this, jtx::testable_amendments()); + jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -419,7 +608,8 @@ class FeeVote_test : public beast::unit_test::suite // But can be applied to a closed ledger { OpenView closedAccum(ledger.get()); - BEAST_EXPECT(applyFeeAndTestResult(env, closedAccum, feeTx)); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, closedAccum, feeTx))); } } @@ -428,7 +618,7 @@ class FeeVote_test : public beast::unit_test::suite { testcase("Multiple Fee Updates"); - jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); + jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -446,7 +636,8 @@ class FeeVote_test : public beast::unit_test::suite { OpenView accum(ledger.get()); - BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx1)); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, accum, feeTx1))); accum.apply(*ledger); } @@ -464,7 +655,8 @@ class FeeVote_test : public beast::unit_test::suite { OpenView accum(ledger.get()); - BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx2)); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, accum, feeTx2))); accum.apply(*ledger); } @@ -477,7 +669,7 @@ class FeeVote_test : public beast::unit_test::suite { testcase("Wrong Ledger Sequence"); - jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); + jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -500,7 +692,7 @@ class FeeVote_test : public beast::unit_test::suite // The transaction should still succeed as long as other fields are // valid // The ledger sequence field is only used for informational purposes - BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx)); + BEAST_EXPECT(isTesSuccess(applyFeeAndTestResult(env, accum, feeTx))); } void @@ -508,7 +700,7 @@ class FeeVote_test : public beast::unit_test::suite { testcase("Partial Field Updates"); - jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); + jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -526,7 +718,8 @@ class FeeVote_test : public beast::unit_test::suite { OpenView accum(ledger.get()); - BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx1)); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, accum, feeTx1))); accum.apply(*ledger); } @@ -543,7 +736,8 @@ class FeeVote_test : public beast::unit_test::suite { OpenView accum(ledger.get()); - BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx2)); + BEAST_EXPECT( + isTesSuccess(applyFeeAndTestResult(env, accum, feeTx2))); accum.apply(*ledger); } @@ -556,7 +750,7 @@ class FeeVote_test : public beast::unit_test::suite { testcase("Single Invalid Transaction"); - jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); + jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( create_genesis, env.app().config(), @@ -579,7 +773,8 @@ class FeeVote_test : public beast::unit_test::suite }); OpenView accum(ledger.get()); - BEAST_EXPECT(!applyFeeAndTestResult(env, accum, invalidTx)); + BEAST_EXPECT( + !isTesSuccess(applyFeeAndTestResult(env, accum, invalidTx))); } void @@ -596,7 +791,7 @@ class FeeVote_test : public beast::unit_test::suite // Test with XRPFees enabled { - Env env(*this, testable_amendments() | featureXRPFees); + Env env(*this, testable_amendments() - featureSmartEscrow); auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); auto ledger = std::make_shared( @@ -631,7 +826,9 @@ class FeeVote_test : public beast::unit_test::suite // Test with XRPFees disabled (legacy format) { - Env env(*this, testable_amendments() - featureXRPFees); + Env env( + *this, + testable_amendments() - featureXRPFees - featureSmartEscrow); auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); auto ledger = std::make_shared( @@ -674,7 +871,7 @@ class FeeVote_test : public beast::unit_test::suite setup.account_reserve = 1234567; setup.owner_reserve = 7654321; - Env env(*this, testable_amendments() | featureXRPFees); + Env env(*this, testable_amendments() - featureSmartEscrow); // establish what the current fees are BEAST_EXPECT( @@ -764,6 +961,128 @@ class FeeVote_test : public beast::unit_test::suite XRPAmount{setup.owner_reserve}); } + void + testDoVotingSmartEscrow() + { + testcase("doVoting with Smart Escrow"); + + using namespace jtx; + + FeeSetup setup; + setup.reference_fee = 42; + setup.account_reserve = 1234567; + setup.owner_reserve = 7654321; + setup.extension_compute_limit = 100; + setup.extension_size_limit = 200; + setup.gas_price = 300; + + Env env( + *this, testable_amendments() | featureXRPFees | featureSmartEscrow); + + // establish what the current fees are + BEAST_EXPECT( + env.current()->fees().base == XRPAmount{UNIT_TEST_REFERENCE_FEE}); + BEAST_EXPECT(env.current()->fees().reserve == XRPAmount{200'000'000}); + BEAST_EXPECT(env.current()->fees().increment == XRPAmount{50'000'000}); + BEAST_EXPECT(env.current()->fees().extensionComputeLimit == 0); + BEAST_EXPECT(env.current()->fees().extensionSizeLimit == 0); + BEAST_EXPECT(env.current()->fees().gasPrice == 0); + + auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); + auto ledger = std::make_shared( + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); + + // doVoting requires a flag ledger (every 256th ledger) + // We need to create a ledger at sequence 256 to make it a flag + // ledger + for (int i = 0; i < 256 - 1; ++i) + { + ledger = std::make_shared( + *ledger, env.app().timeKeeper().closeTime()); + } + BEAST_EXPECT(ledger->isFlagLedger()); + + // Create some mock validations with fee votes + std::vector> validations; + + for (int i = 0; i < 5; i++) + { + auto sec = randomSecretKey(); + auto pub = derivePublicKey(KeyType::secp256k1, sec); + + auto val = std::make_shared( + env.app().timeKeeper().now(), + pub, + sec, + calcNodeID(pub), + [&](STValidation& v) { + v.setFieldU32(sfLedgerSequence, ledger->seq()); + // Vote for different fees than current + v.setFieldAmount( + sfBaseFeeDrops, XRPAmount{setup.reference_fee}); + v.setFieldAmount( + sfReserveBaseDrops, XRPAmount{setup.account_reserve}); + v.setFieldAmount( + sfReserveIncrementDrops, + XRPAmount{setup.owner_reserve}); + v.setFieldU32( + sfExtensionComputeLimit, setup.extension_compute_limit); + v.setFieldU32( + sfExtensionSizeLimit, setup.extension_size_limit); + v.setFieldU32(sfGasPrice, setup.gas_price); + }); + if (i % 2) + val->setTrusted(); + validations.push_back(val); + } + + auto txSet = std::make_shared( + SHAMapType::TRANSACTION, env.app().getNodeFamily()); + + // This should not throw since we have a flag ledger + feeVote->doVoting(ledger, validations, txSet); + + auto const txs = getTxs(txSet); + BEAST_EXPECT(txs.size() == 1); + auto const& feeTx = txs[0]; + + BEAST_EXPECT(feeTx.getTxnType() == ttFEE); + + BEAST_EXPECT(feeTx.getAccountID(sfAccount) == AccountID()); + BEAST_EXPECT(feeTx.getFieldU32(sfLedgerSequence) == ledger->seq() + 1); + + BEAST_EXPECT(feeTx.isFieldPresent(sfBaseFeeDrops)); + BEAST_EXPECT(feeTx.isFieldPresent(sfReserveBaseDrops)); + BEAST_EXPECT(feeTx.isFieldPresent(sfReserveIncrementDrops)); + + // The legacy fields should NOT be present + BEAST_EXPECT(!feeTx.isFieldPresent(sfBaseFee)); + BEAST_EXPECT(!feeTx.isFieldPresent(sfReserveBase)); + BEAST_EXPECT(!feeTx.isFieldPresent(sfReserveIncrement)); + BEAST_EXPECT(!feeTx.isFieldPresent(sfReferenceFeeUnits)); + + // Check the values + BEAST_EXPECT( + feeTx.getFieldAmount(sfBaseFeeDrops) == + XRPAmount{setup.reference_fee}); + BEAST_EXPECT( + feeTx.getFieldAmount(sfReserveBaseDrops) == + XRPAmount{setup.account_reserve}); + BEAST_EXPECT( + feeTx.getFieldAmount(sfReserveIncrementDrops) == + XRPAmount{setup.owner_reserve}); + BEAST_EXPECT( + feeTx.getFieldU32(sfExtensionComputeLimit) == + setup.extension_compute_limit); + BEAST_EXPECT( + feeTx.getFieldU32(sfExtensionSizeLimit) == + setup.extension_size_limit); + BEAST_EXPECT(feeTx.getFieldU32(sfGasPrice) == setup.gas_price); + } + void run() override { @@ -777,6 +1096,7 @@ class FeeVote_test : public beast::unit_test::suite testSingleInvalidTransaction(); testDoValidation(); testDoVoting(); + testDoVotingSmartEscrow(); } }; diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index f116150681..40493c6f31 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -9,10 +9,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_; @@ -47,8 +47,9 @@ public: getVotes() const; }; -auto -VotableValue::getVotes() const -> std::pair +template +std::pair +VotableValue::getVotes() const { value_type ourVote = current_; int weight = 0; @@ -171,6 +172,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 @@ -193,11 +221,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()) @@ -226,7 +265,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)) { @@ -257,6 +296,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 @@ -265,11 +328,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 << "/" @@ -297,6 +364,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/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index 43db9ae13c..1a1fad0873 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -105,6 +105,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: @@ -269,6 +283,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);