Skip to content

Commit 133f5bc

Browse files
authored
Bring back the Rust XDR parsing check. (stellar#5396)
# Description This check has been removed in commit `4287224758c47dc6cdc0c14930754c898b886e51`, however it's still necessary for most of the Core builds that support 2 protocol version. This change reverts the removal commit and does a bit of necessary refactoring to support the new build. # Checklist - [ ] Reviewed the [contributing](https://github.com/stellar/stellar-core/blob/master/CONTRIBUTING.md#submitting-changes) document - [ ] Rebased on top of master (no merge commits) - [ ] Ran `clang-format` v8.0.0 (via `make format` or the Visual Studio extension) - [ ] Compiles - [ ] Ran all tests - [ ] If change impacts performance, include supporting evidence per the [performance document](https://github.com/stellar/stellar-core/blob/master/performance-eval/performance-eval.md)
2 parents ac260d7 + da32407 commit 133f5bc

8 files changed

Lines changed: 199 additions & 21 deletions

src/test/TestUtils.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "rust/RustBridge.h"
1010
#include "simulation/LoadGenerator.h"
1111
#include "simulation/Simulation.h"
12+
#include "test/Catch2.h"
1213
#include "test/TxTests.h"
1314
#include "test/test.h"
1415
#include "transactions/test/SorobanTxTestUtils.h"
@@ -632,4 +633,21 @@ generateTransactions(Application& app, std::filesystem::path const& outputFile,
632633
LOG_INFO(DEFAULT_LOG, "Generated {} transactions in {}", numTransactions,
633634
outputFile);
634635
}
636+
637+
bool
638+
isSorobanProtocolLinked(Config const& cfg, ProtocolVersion protocolVersion)
639+
{
640+
auto sorobanProtocolCfg = cfg;
641+
sorobanProtocolCfg.USE_CONFIG_FOR_GENESIS = true;
642+
sorobanProtocolCfg.TESTING_UPGRADE_LEDGER_PROTOCOL_VERSION =
643+
static_cast<uint32_t>(protocolVersion);
644+
auto res =
645+
testutil::isTestApplicationProtocolVersionSupported(sorobanProtocolCfg);
646+
if (!res)
647+
{
648+
SUCCEED("Skipping historical Soroban protocol test: requested "
649+
"protocol is not linked in this build");
650+
}
651+
return res;
652+
}
635653
}

src/test/TestUtils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,6 @@ void generateTransactions(Application& app,
146146
std::filesystem::path const& outputFile,
147147
uint32_t numTransactions, uint32_t accounts,
148148
uint32_t offset);
149+
bool isSorobanProtocolLinked(Config const& cfg,
150+
ProtocolVersion protocolVersion);
149151
}

src/transactions/FeeBumpTransactionFrame.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,9 @@ FeeBumpTransactionFrame::checkValidImpl(
277277
DiagnosticEventManager& diagnosticEvents, bool isOverlayValidation,
278278
std::optional<uint32_t> validationLedgerSeq) const
279279
{
280-
if (!xdr::check_xdr_depth(mEnvelope, 500) || !XDRProvidesValidFee())
280+
auto ledgerVersion = ledgerView.getLedgerHeader().current().ledgerVersion;
281+
if (!validateXDRForProtocol(ledgerVersion, app.getConfig(), mEnvelope) ||
282+
!XDRProvidesValidFee())
281283
{
282284
return FeeBumpMutableTransactionResult::createTxError(txMALFORMED);
283285
}
@@ -292,7 +294,6 @@ FeeBumpTransactionFrame::checkValidImpl(
292294
auto txResult = FeeBumpMutableTransactionResult::createSuccess(
293295
*mInnerTx, feeCharged, 0);
294296

295-
auto ledgerVersion = ledgerView.getLedgerHeader().current().ledgerVersion;
296297
SignatureChecker signatureChecker{ledgerVersion, getContentsHash(),
297298
mEnvelope.feeBump().signatures,
298299
isOverlayValidation};

src/transactions/TransactionFrame.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,11 +1934,13 @@ TransactionFrame::checkValidImpl(
19341934
DiagnosticEventManager& diagnosticEvents, bool isOverlayValidation,
19351935
std::optional<uint32_t> validationLedgerSeq) const
19361936
{
1937+
auto const& header = ledgerView.getLedgerHeader().current();
19371938
// Subtle: this check has to happen in `checkValid` and not
19381939
// `checkValidWithOptionallyChargedFee` in order to not validate the
19391940
// envelope XDR twice for the fee bump transactions (they use
19401941
// `checkValidWithOptionallyChargedFee` for the inner tx).
1941-
if (!xdr::check_xdr_depth(mEnvelope, 500))
1942+
if (!validateXDRForProtocol(header.ledgerVersion, app.getConfig(),
1943+
mEnvelope))
19421944
{
19431945
return MutableTransactionResult::createTxError(txMALFORMED);
19441946
}
@@ -1952,9 +1954,8 @@ TransactionFrame::checkValidImpl(
19521954
// aren't the fees that would end up being applied. However, this is
19531955
// what Core used to return for a while, and some users may rely on
19541956
// this, so we maintain this logic for the time being.
1955-
int64_t minBaseFee = ledgerView.getLedgerHeader().current().baseFee;
1956-
auto feeCharged =
1957-
getFee(ledgerView.getLedgerHeader().current(), minBaseFee, false);
1957+
int64_t minBaseFee = header.baseFee;
1958+
auto feeCharged = getFee(header, minBaseFee, false);
19581959
auto txResult = MutableTransactionResult::createSuccess(*this, feeCharged);
19591960
checkValidWithOptionallyChargedFee(
19601961
app, ledgerView, current, true, lowerBoundCloseTimeOffset,

src/transactions/TransactionUtils.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,39 @@ hasMuxedAccount(TransactionEnvelope const& e)
13451345
return c.mHasMuxedAccount;
13461346
}
13471347

1348+
bool
1349+
validateXDRForProtocol(uint32_t currProtocol, Config const& cfg,
1350+
TransactionEnvelope const& envelope)
1351+
{
1352+
uint32_t rustDepthLimit = 1000;
1353+
// Rust XDR parser may measure depth to be deeper than depth measured by
1354+
// the C++ parser. Before p28 we didn't do the Rust parsing check at all
1355+
// and instead relied on the C++ check with a reduced depth limit to
1356+
// approximate for that fact.
1357+
// Starting with p28 build we do the Rust parsing check unconditionally both
1358+
// to ensure that XDR can be parsed with the parser for the given protocol
1359+
// version, and to ensure that the depth limit is not exceeded. However, in
1360+
// order to avoid divergence with p27 builds, we still do a C++ depth check
1361+
// and relax the Rust depth limit (so that it's a basically no-op) in order
1362+
// to ensure identical behavior in both builds.
1363+
// Note, that the actual 50'000 depth won't be reachable in practice, so we
1364+
// don't need to worry about the stack overflow in the Rust parser.
1365+
if (protocolVersionIsBefore(currProtocol, ProtocolVersion::V_28))
1366+
{
1367+
if (!xdr::check_xdr_depth(envelope, 500))
1368+
{
1369+
return false;
1370+
}
1371+
rustDepthLimit = 50'000;
1372+
}
1373+
1374+
uint32_t maxProtocol = cfg.CURRENT_LEDGER_PROTOCOL_VERSION;
1375+
auto cxxBuf = CxxBuf{
1376+
std::make_unique<std::vector<uint8_t>>(xdr::xdr_to_opaque(envelope))};
1377+
return rust_bridge::can_parse_transaction(maxProtocol, currProtocol, cxxBuf,
1378+
rustDepthLimit);
1379+
}
1380+
13481381
uint64_t
13491382
getUpperBoundCloseTimeOffset(Application& app, uint64_t lastCloseTime)
13501383
{

src/transactions/TransactionUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,13 @@ bool accountFlagMaskCheckIsValid(uint32_t flag, uint32_t ledgerVersion);
264264

265265
bool hasMuxedAccount(TransactionEnvelope const& e);
266266

267+
// Checks if the transaction XDR is parseable for the provided protocol version.
268+
// The 'parse-ability' check ensures that the XDR is not too deeply nested, and
269+
// that a Soroban host for the provided protocol version would be able to parse
270+
// the XDR.
271+
bool validateXDRForProtocol(uint32_t currProtocol, Config const& cfg,
272+
TransactionEnvelope const& envelope);
273+
267274
uint64_t getUpperBoundCloseTimeOffset(Application& app, uint64_t lastCloseTime);
268275

269276
bool hasAccountEntryExtV2(AccountEntry const& ae);

src/transactions/test/InvokeHostFunctionTests.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,6 @@ getParallelSorobanTestProtocolVersion()
8989
return testLedgerProtocolVersion;
9090
}
9191

92-
bool
93-
isSorobanProtocolLinked(Config const& cfg, ProtocolVersion protocolVersion)
94-
{
95-
auto sorobanProtocolCfg = cfg;
96-
sorobanProtocolCfg.USE_CONFIG_FOR_GENESIS = true;
97-
sorobanProtocolCfg.TESTING_UPGRADE_LEDGER_PROTOCOL_VERSION =
98-
static_cast<uint32_t>(protocolVersion);
99-
return testutil::isTestApplicationProtocolVersionSupported(
100-
sorobanProtocolCfg);
101-
}
102-
10392
void
10493
overrideNetworkSettingsToMin(Application& app)
10594
{
@@ -7222,8 +7211,6 @@ TEST_CASE("Module cache", "[tx][soroban]")
72227211
cfg.USE_CONFIG_FOR_GENESIS = false;
72237212
if (!isSorobanProtocolLinked(cfg, SOROBAN_PROTOCOL_VERSION))
72247213
{
7225-
SUCCEED("Skipping historical Soroban protocol test: requested "
7226-
"protocol is not linked in this build");
72277214
return;
72287215
}
72297216

@@ -7283,8 +7270,6 @@ TEST_CASE("Vm instantiation tightening", "[tx][soroban]")
72837270
cfg.USE_CONFIG_FOR_GENESIS = false;
72847271
if (!isSorobanProtocolLinked(cfg, SOROBAN_PROTOCOL_VERSION))
72857272
{
7286-
SUCCEED("Skipping historical Soroban protocol test: requested "
7287-
"protocol is not linked in this build");
72887273
return;
72897274
}
72907275

src/transactions/test/TxEnvelopeTests.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,3 +3049,134 @@ TEST_CASE_VERSIONS("soroban transaction validation", "[tx][envelope][soroban]")
30493049
}
30503050
});
30513051
}
3052+
3053+
TEST_CASE("XDR protocol 22 compatibility validation", "[tx][envelope]")
3054+
{
3055+
if (!isSorobanProtocolLinked(getTestConfig(), ProtocolVersion::V_21))
3056+
{
3057+
return;
3058+
}
3059+
auto validateTx = [](ProtocolVersion protocolVersion) {
3060+
VirtualClock clock;
3061+
auto cfg = getTestConfig();
3062+
cfg.TESTING_UPGRADE_LEDGER_PROTOCOL_VERSION =
3063+
static_cast<uint32_t>(protocolVersion);
3064+
auto app = createTestApplication(clock, cfg);
3065+
auto root = app->getRoot();
3066+
Operation op;
3067+
op.body.type(INVOKE_HOST_FUNCTION);
3068+
op.body.invokeHostFunctionOp().hostFunction.type(
3069+
HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2);
3070+
3071+
auto tx =
3072+
sorobanTransactionFrameFromOps(app->getNetworkID(), *root, {op}, {},
3073+
SorobanResources(), 1000, 1'000'000);
3074+
CheckValidLedgerViewWrapper ledgerView(*app);
3075+
return tx->checkValid(app->getAppConnector(), ledgerView, 0, 0, 0);
3076+
};
3077+
SECTION("not valid in protocol 21")
3078+
{
3079+
auto res = validateTx(ProtocolVersion::V_21);
3080+
REQUIRE(res->getResultCode() == txMALFORMED);
3081+
}
3082+
SECTION("valid in protocol 22")
3083+
{
3084+
auto res = validateTx(ProtocolVersion::V_22);
3085+
REQUIRE(res->isSuccess());
3086+
}
3087+
}
3088+
3089+
TEST_CASE("XDR protocol 23 compatibility validation", "[tx][envelope]")
3090+
{
3091+
if (!isSorobanProtocolLinked(getTestConfig(), ProtocolVersion::V_22))
3092+
{
3093+
return;
3094+
}
3095+
auto runTest = [](ProtocolVersion protocolVersion, bool expectSuccess) {
3096+
VirtualClock clock;
3097+
auto cfg = getTestConfig();
3098+
cfg.TESTING_UPGRADE_LEDGER_PROTOCOL_VERSION =
3099+
static_cast<uint32_t>(protocolVersion);
3100+
auto app = createTestApplication(clock, cfg);
3101+
auto root = app->getRoot();
3102+
Operation op;
3103+
op.body.type(INVOKE_HOST_FUNCTION);
3104+
op.body.invokeHostFunctionOp().hostFunction.type(
3105+
HOST_FUNCTION_TYPE_INVOKE_CONTRACT);
3106+
3107+
CheckValidLedgerViewWrapper ledgerView(*app);
3108+
SECTION("muxed account ScAddress in function args")
3109+
{
3110+
auto& val = op.body.invokeHostFunctionOp()
3111+
.hostFunction.invokeContract()
3112+
.args.emplace_back();
3113+
val.type(SCV_ADDRESS);
3114+
val.address().type(SC_ADDRESS_TYPE_MUXED_ACCOUNT);
3115+
val.address().muxedAccount().id = 123;
3116+
auto tx = sorobanTransactionFrameFromOps(
3117+
app->getNetworkID(), *root, {op}, {}, SorobanResources(), 1000,
3118+
1'000'000);
3119+
3120+
auto res =
3121+
tx->checkValid(app->getAppConnector(), ledgerView, 0, 0, 0);
3122+
REQUIRE(res->isSuccess() == expectSuccess);
3123+
if (!expectSuccess)
3124+
{
3125+
REQUIRE(res->getResultCode() == txMALFORMED);
3126+
}
3127+
}
3128+
SECTION("claimable balance ScAddress in auth")
3129+
{
3130+
auto& authEntry =
3131+
op.body.invokeHostFunctionOp().auth.emplace_back();
3132+
authEntry.rootInvocation.function.type(
3133+
SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN);
3134+
auto& address =
3135+
authEntry.rootInvocation.function.contractFn().contractAddress;
3136+
address.type(SC_ADDRESS_TYPE_CLAIMABLE_BALANCE);
3137+
address.claimableBalanceId().v0()[0] = 1;
3138+
auto tx = sorobanTransactionFrameFromOps(
3139+
app->getNetworkID(), *root, {op}, {}, SorobanResources(), 1000,
3140+
1'000'000);
3141+
auto res =
3142+
tx->checkValid(app->getAppConnector(), ledgerView, 0, 0, 0);
3143+
REQUIRE(res->isSuccess() == expectSuccess);
3144+
if (!expectSuccess)
3145+
{
3146+
REQUIRE(res->getResultCode() == txMALFORMED);
3147+
}
3148+
}
3149+
SECTION("liquidity pool ScAddress in footprint")
3150+
{
3151+
Operation ttlOp;
3152+
ttlOp.body.type(EXTEND_FOOTPRINT_TTL);
3153+
3154+
SorobanResources resources;
3155+
auto& key = resources.footprint.readOnly.emplace_back();
3156+
key.type(CONTRACT_DATA);
3157+
auto& address = key.contractData().contract;
3158+
3159+
address.type(SC_ADDRESS_TYPE_LIQUIDITY_POOL);
3160+
address.liquidityPoolId()[1] = 10;
3161+
auto tx = sorobanTransactionFrameFromOps(app->getNetworkID(), *root,
3162+
{ttlOp}, {}, resources,
3163+
1000, 1'000'000);
3164+
auto res =
3165+
tx->checkValid(app->getAppConnector(), ledgerView, 0, 0, 0);
3166+
REQUIRE(res->isSuccess() == expectSuccess);
3167+
if (!expectSuccess)
3168+
{
3169+
REQUIRE(res->getResultCode() == txMALFORMED);
3170+
}
3171+
}
3172+
};
3173+
3174+
SECTION("not valid in protocol 22")
3175+
{
3176+
runTest(ProtocolVersion::V_22, false);
3177+
}
3178+
SECTION("valid in protocol 23")
3179+
{
3180+
runTest(ProtocolVersion::V_23, true);
3181+
}
3182+
}

0 commit comments

Comments
 (0)