mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 14:20:56 +00:00
fix: Charge kFeeHeavyBurdenRpc in doChannelVerify
This commit is contained in:
@@ -13,13 +13,18 @@
|
||||
#include <test/jtx/ticket.h>
|
||||
#include <test/jtx/txflags.h>
|
||||
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/handlers/Handlers.h>
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Zero.h> // IWYU pragma: keep
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/Job.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
@@ -39,6 +44,9 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/resource/Charge.h>
|
||||
#include <xrpl/resource/Consumer.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@@ -48,6 +56,7 @@
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -495,7 +504,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// Owner closes, will close after settleDelay
|
||||
env(claim(alice, chan), Txflags(tfClose));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
env.close(settleTimepoint - settleDelay / 2);
|
||||
env.close(settleTimepoint - (settleDelay / 2));
|
||||
{
|
||||
// receiver can still claim
|
||||
auto const chanBal = channelBalance(*env.current(), chan);
|
||||
@@ -1587,6 +1596,146 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testChannelVerifyLoadType(FeatureBitset features)
|
||||
{
|
||||
testcase("channel_verify sets kFEE_HEAVY_BURDEN_RPC load type");
|
||||
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chanStr = to_string(channel(alice, bob, env.seq(alice)));
|
||||
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
// Step 1: get a valid signature from channel_authorize
|
||||
auto const authResult = env.rpc("channel_authorize", "alice", chanStr, "1000");
|
||||
auto const sig = authResult[jss::result][jss::signature].asString();
|
||||
BEAST_EXPECT(!sig.empty());
|
||||
auto const pkHex = strHex(pk.slice());
|
||||
|
||||
// Step 2: build RPC::JsonContext directly so we can inspect loadType
|
||||
auto& app = env.app();
|
||||
Resource::Charge loadType = Resource::kFeeReferenceRpc;
|
||||
Resource::Consumer c;
|
||||
RPC::JsonContext context{
|
||||
{.j = env.journal,
|
||||
.app = app,
|
||||
.loadType = loadType,
|
||||
.netOps = app.getOPs(),
|
||||
.ledgerMaster = app.getLedgerMaster(),
|
||||
.consumer = c,
|
||||
.role = Role::USER,
|
||||
.coro = {},
|
||||
.infoSub = {},
|
||||
.apiVersion = RPC::kApiVersionIfUnspecified},
|
||||
{},
|
||||
{}};
|
||||
json::Value params;
|
||||
params[jss::public_key] = pkHex;
|
||||
params[jss::channel_id] = chanStr;
|
||||
params[jss::amount] = "1000";
|
||||
params[jss::signature] = sig;
|
||||
context.params = std::move(params);
|
||||
|
||||
// Confirm default before calling handler
|
||||
BEAST_EXPECT(context.loadType == Resource::kFeeReferenceRpc);
|
||||
json::Value result;
|
||||
Gate g;
|
||||
app.getJobQueue().postCoro(JtClient, "RPC-Client", [&](auto const& coro) {
|
||||
context.coro = coro;
|
||||
result = doChannelVerify(context);
|
||||
g.signal();
|
||||
});
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
// Signature must verify correctly
|
||||
BEAST_EXPECT(result[jss::signature_verified].asBool());
|
||||
// KEY ASSERTION: loadType must be kFEE_HEAVY_BURDEN_RPC after the fix
|
||||
// Before fix: this will FAIL because loadType stays kFEE_REFERENCE_RPC (20)
|
||||
// After fix: this will PASS because loadType is kFEE_HEAVY_BURDEN_RPC (3000)
|
||||
BEAST_EXPECT(context.loadType == Resource::kFeeHeavyBurdenRpc);
|
||||
// Confirm the charge is 150x heavier than the current (broken) default
|
||||
BEAST_EXPECT(context.loadType.cost() == Resource::kFeeHeavyBurdenRpc.cost()); // 3000
|
||||
BEAST_EXPECT(context.loadType.cost() != Resource::kFeeReferenceRpc.cost()); // not 20
|
||||
}
|
||||
|
||||
void
|
||||
testChannelAuthorizeLoadType(FeatureBitset features)
|
||||
{
|
||||
testcase("channel_authorize sets kFEE_HEAVY_BURDEN_RPC load type");
|
||||
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
Env env{*this, features};
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const chanStr = to_string(channel(alice, bob, env.seq(alice)));
|
||||
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
auto& app = env.app();
|
||||
Resource::Charge loadType = Resource::kFeeReferenceRpc;
|
||||
Resource::Consumer c;
|
||||
RPC::JsonContext context{
|
||||
{.j = env.journal,
|
||||
.app = app,
|
||||
.loadType = loadType,
|
||||
.netOps = app.getOPs(),
|
||||
.ledgerMaster = app.getLedgerMaster(),
|
||||
.consumer = c,
|
||||
.role = Role::ADMIN, // channel_authorize requires ADMIN or canSign()
|
||||
.coro = {},
|
||||
.infoSub = {},
|
||||
.apiVersion = RPC::kApiVersionIfUnspecified},
|
||||
{},
|
||||
{}};
|
||||
json::Value params;
|
||||
params[jss::channel_id] = chanStr;
|
||||
params[jss::amount] = "1000";
|
||||
params[jss::secret] = alice.name(); // use account name as seed
|
||||
context.params = std::move(params);
|
||||
|
||||
// Confirm default before calling handler
|
||||
BEAST_EXPECT(context.loadType == Resource::kFeeReferenceRpc);
|
||||
json::Value result;
|
||||
Gate g;
|
||||
app.getJobQueue().postCoro(JtClient, "RPC-Client", [&](auto const& coro) {
|
||||
context.coro = coro;
|
||||
result = doChannelAuthorize(context);
|
||||
g.signal();
|
||||
});
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
// Must return a valid signature
|
||||
BEAST_EXPECT(result.isMember(jss::signature));
|
||||
BEAST_EXPECT(!result[jss::signature].asString().empty());
|
||||
// KEY ASSERTION: loadType must be kFEE_HEAVY_BURDEN_RPC after the fix
|
||||
// Before fix: FAILS — stays at kFEE_REFERENCE_RPC (charge=20)
|
||||
// After fix: PASSES — set to kFEE_HEAVY_BURDEN_RPC (charge=3000)
|
||||
BEAST_EXPECT(context.loadType == Resource::kFeeHeavyBurdenRpc);
|
||||
}
|
||||
|
||||
void
|
||||
testMalformedPK(FeatureBitset features)
|
||||
{
|
||||
@@ -1983,6 +2132,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
testMetaAndOwnership(features);
|
||||
testAccountDelete(features);
|
||||
testUsingTickets(features);
|
||||
testChannelVerifyLoadType(features);
|
||||
testChannelAuthorizeLoadType(features);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol/tokens.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
@@ -36,6 +37,8 @@ doChannelVerify(RPC::JsonContext& context)
|
||||
return RPC::missingFieldError(p);
|
||||
}
|
||||
|
||||
context.loadType = Resource::kFeeHeavyBurdenRpc;
|
||||
|
||||
std::optional<PublicKey> pk;
|
||||
{
|
||||
std::string const strPk = params[jss::public_key].asString();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
@@ -37,6 +38,8 @@ doChannelAuthorize(RPC::JsonContext& context)
|
||||
return RPC::makeError(RpcNotSupported, "Signing is not supported by this server.");
|
||||
}
|
||||
|
||||
context.loadType = Resource::kFeeHeavyBurdenRpc;
|
||||
|
||||
auto const& params(context.params);
|
||||
for (auto const& p : {jss::channel_id, jss::amount})
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user