Skip to content

Implement CAP-77 - ability to freeze ledger keys via network configuration#5162

Open
dmkozh wants to merge 5 commits intostellar:masterfrom
dmkozh:cap77
Open

Implement CAP-77 - ability to freeze ledger keys via network configuration#5162
dmkozh wants to merge 5 commits intostellar:masterfrom
dmkozh:cap77

Conversation

@dmkozh
Copy link
Contributor

@dmkozh dmkozh commented Mar 4, 2026

Description

Implement CAP-77 - ability to freeze ledger keys via network configuration.

This implements all of the CAP features:

  • Add configuration setting and upgrade mechanism for freezing/unfreezing the keys
  • Add configuration setting and upgrade mechanism for allowing transactions to bypass the freezing mechanism
  • Reject transactions for which we know that they access a frozen key at validation time (but only if they're not in the bypass transaction list)
  • Reject transactions for which we can only tell that they access a frozen key at apply time
  • Remove DEX offers that would result in a frozen entry modification

This is a big change, but the bulk of it is tests (as we have a lot of operations to cover) and updates to the interface of every operation. The actual freeze logic is quite straightforward and short, so I'm not sure if it's worth to break this down.

Checklist

  • Reviewed the contributing 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

@dmkozh dmkozh force-pushed the cap77 branch 4 times, most recently from 39f36a6 to 005a309 Compare March 4, 2026 20:47
@dmkozh dmkozh marked this pull request as ready for review March 5, 2026 00:04
Copilot AI review requested due to automatic review settings March 5, 2026 00:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements CAP-77 — the ability to freeze ledger keys via network configuration in stellar-core. It adds configuration settings and upgrade mechanisms for freezing/unfreezing keys and bypass transactions, rejects transactions that access frozen keys at validation and apply time, and removes DEX offers that reference frozen entries.

Changes:

  • Adds frozen ledger key and freeze bypass TX config settings with delta-based upgrade mechanisms, validation, and caching in SorobanNetworkConfig.
  • Extends every operation frame with doesAccessFrozenKey() checks and adds new doApply overloads that accept sorobanConfig for operations needing apply-time frozen key checks (DEX, liquidity pool, claimable balance).
  • Adds comprehensive tests in FrozenLedgerKeysTests.cpp and updates existing test infrastructure to handle the new txFROZEN_KEY_ACCESSED result code.

Reviewed changes

Copilot reviewed 80 out of 80 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/ledger/NetworkConfig.h/.cpp Adds frozen key/bypass TX storage, loading, creation, validation, and query methods
src/transactions/OperationFrame.h/.cpp Adds accessesFrozenKey() and virtual doesAccessFrozenKey() to base operation class; adds doApply overload with sorobanConfig
src/transactions/TransactionFrame.h/.cpp Adds accessesFrozenKey(), threads envelopeContentsHash through validation/apply, loads sorobanConfig for all txs at V20+
src/transactions/FeeBumpTransactionFrame.cpp Adds frozen fee bump source account check and passes contents hash to inner tx
src/transactions/TransactionUtils.h/.cpp Adds offerAccessesFrozenKey() helper
src/transactions/OfferExchange.h/.cpp Adds eSkipFrozen filter result with offer removal and liability release
src/transactions/ManageOfferOpFrameBase.h/.cpp Adds frozen key checks for offer operations at validation and apply time
src/transactions/PathPaymentOpFrameBase.h/.cpp Adds frozen key checks for path payments and passes sorobanConfig to convert
src/transactions/PaymentOpFrame.h/.cpp Adds frozen key checks for payment destination/source trustlines
src/transactions/{various}OpFrame.h/.cpp Adds doesAccessFrozenKey() implementations for all remaining operations
src/transactions/LiquidityPool{Deposit,Withdraw}OpFrame.h/.cpp Adds apply-time frozen trustline checks for LP operations
src/transactions/ClaimClaimableBalanceOpFrame.h/.cpp Adds apply-time frozen key check for claiming balances
src/transactions/test/FrozenLedgerKeysTests.cpp Comprehensive tests for frozen key config, validation, apply-time checks, DEX behavior
src/transactions/test/InvokeHostFunctionTests.cpp Skips delta config settings in upgrade tests
src/herder/Upgrades.cpp Applies frozen key delta upgrades and creates v26 ledger entries
src/simulation/TxGenerator.h/.cpp, LoadGenerator.cpp Supports frozen key deltas in upgrade config generation
src/test/TxTests.cpp, TestExceptions.h/.cpp Adds txFROZEN_KEY_ACCESSED and new operation result codes
src/testdata/*.json Updated test ledger close meta snapshots
Builds/VisualStudio/* Build system updates for new test file and preprocessor defs
Comments suppressed due to low confidence (5)

src/transactions/PaymentOpFrame.cpp:1

  • When the asset is native, doesAccessFrozenKey only checks the destination account and immediately returns, skipping the source account check. But the source account's frozen state also matters for native payments (the source account entry holds the native balance). The source account is checked at the transaction level in TransactionFrame::accessesFrozenKey, so this is not a full bug, but the early return means that for native payments the destination account is checked but nothing else at the operation level — which means if the destination account is not frozen, the function returns false without checking whether the source account's entry is frozen for the native balance. Since the tx-level check covers the source account key, this is technically safe but inconsistent with other operations and with offerAccessesFrozenKey which explicitly checks the account key for native. Consider also checking the destination account key when the asset is non-native (for the destination's account entry receiving native in a different path), or documenting why the early return is correct.
    src/transactions/SetOptionsOpFrame.cpp:1
  • There is an extra closing brace at line 336. The original file ends the stellar namespace with a } that was already present. The added function body closes with } at line 335, but then line 336 has another } which appears to be a duplicate namespace-closing brace, resulting in a mismatched or extra brace.
    src/transactions/InflationOpFrame.cpp:1
  • Same issue as in SetOptionsOpFrame.cpp — there is an extra closing brace at line 155. The function body closes at line 154, and then line 155 adds a second } that would result in a mismatched brace with the namespace.
    src/transactions/test/FrozenLedgerKeysTests.cpp:1
  • Minor grammar issue: 'adds and remove' should be 'adds and removes'.
    src/transactions/test/FrozenLedgerKeysTests.cpp:1
  • The variable names buyerSellingPre and buyerBuyingPre are confusing because buyerSellingPre loads the balance for makerBuying (which is what the buyer is selling/spending), and buyerBuyingPre loads for makerSelling (which is what the buyer receives). The naming convention is reversed from the perspective of the buyer's assets. Consider renaming to buyerSpendAssetPre/buyerReceiveAssetPre or adding a comment to clarify the naming convention.

LedgerKey frozenKeysLk(LedgerEntryType::CONFIG_SETTING);
frozenKeysLk.configSetting().configSettingID =
CONFIG_SETTING_FROZEN_LEDGER_KEYS;
auto& frozenKeysVec = ltx.load(frozenKeysLk)
Copy link
Contributor

@sisuresh sisuresh Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this technically works because the reference is pointing to the entry in LedgerTxn mEntry, but the LedgerTxnEntry returned by current() goes out of scope after this line. We shouldn't do this. This comment applies below as well. Relevant slack discussion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed.

return false;
}
}
catch (...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we catch xdr_runtime_error or std::exception and log the exception along with the key that caused the issue? We can keep the catch (...) as a fallback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants