From b08db30b3a7f35e87eba979e24e825b686a224e6 Mon Sep 17 00:00:00 2001 From: Lukasz Gasior Date: Tue, 28 Apr 2026 09:25:13 +0200 Subject: [PATCH 1/4] Add keep_previous_substate_values flag --- .../radixdlt/environment/DatabaseConfig.java | 5 +++-- .../helper/NodeRustEnvironmentBuilder.java | 2 +- .../src/core_api/conversions/receipt.rs | 4 ++-- core-rust/state-manager/src/receipt.rs | 17 +++++++++++++++++ core-rust/state-manager/src/store/rocks_db.rs | 13 ++++++++++++- core-rust/state-manager/src/store/traits.rs | 2 ++ .../main/java/com/radixdlt/RadixNodeModule.java | 4 +++- .../rev2/NodeRustEnvironmentHelper.java | 2 +- .../DeterministicEngineStateApiTestBase.java | 2 +- .../core/LtsAccountDepositBehaviourTest.java | 6 +++--- .../api/core/LtsAccountResourceBalanceTest.java | 2 +- .../api/core/LtsTransactionOutcomesTest.java | 4 ++-- .../api/core/TransactionPreviewTest.java | 2 +- .../api/core/TransactionPreviewV2Test.java | 2 +- .../api/core/regression/StateHistoryTest.java | 2 +- .../radixdlt/rev2/REv2StateComputerTest.java | 2 +- .../java/com/radixdlt/rev2/RustMempoolTest.java | 2 +- .../java/com/radixdlt/rev2/StateTreeGcTest.java | 2 +- 18 files changed, 54 insertions(+), 21 deletions(-) diff --git a/core-rust-bridge/src/main/java/com/radixdlt/environment/DatabaseConfig.java b/core-rust-bridge/src/main/java/com/radixdlt/environment/DatabaseConfig.java index 9fe5bd65a3..43cb46d4a6 100644 --- a/core-rust-bridge/src/main/java/com/radixdlt/environment/DatabaseConfig.java +++ b/core-rust-bridge/src/main/java/com/radixdlt/environment/DatabaseConfig.java @@ -72,7 +72,8 @@ public record DatabaseConfig( boolean enableLocalTransactionExecutionIndex, boolean enableAccountChangeIndex, boolean enableHistoricalSubstateValues, - boolean enableEntityListingIndices) { + boolean enableEntityListingIndices, + boolean keepPreviousSubstateValues) { public static void registerCodec(CodecMap codecMap) { codecMap.register( DatabaseConfig.class, @@ -81,6 +82,6 @@ public static void registerCodec(CodecMap codecMap) { public static DatabaseConfig forTesting() { // Many test assert on transaction execution details, so we keep this one on by default: - return new DatabaseConfig(true, false, false, false); + return new DatabaseConfig(true, false, false, false, true); } } diff --git a/core-rust-bridge/src/test/java/com/radixdlt/helper/NodeRustEnvironmentBuilder.java b/core-rust-bridge/src/test/java/com/radixdlt/helper/NodeRustEnvironmentBuilder.java index ad6d7afe36..ec8e878538 100644 --- a/core-rust-bridge/src/test/java/com/radixdlt/helper/NodeRustEnvironmentBuilder.java +++ b/core-rust-bridge/src/test/java/com/radixdlt/helper/NodeRustEnvironmentBuilder.java @@ -90,7 +90,7 @@ public static NodeRustEnvironment createNodeRustEnvironment(String dbPath) { new RustMempoolConfig(mempoolMaxTotalTransactionsSize, mempoolMaxTransactionCount)), Option.none(), stateManagerDbConfig, - new DatabaseConfig(false, false, false, false), + new DatabaseConfig(false, false, false, false, true), LoggingConfig.getDefault(), StateTreeGcConfig.forTesting(), LedgerProofsGcConfig.forTesting(), diff --git a/core-rust/core-api-server/src/core_api/conversions/receipt.rs b/core-rust/core-api-server/src/core_api/conversions/receipt.rs index 88b3db07b1..5d9a27d0b8 100644 --- a/core-rust/core-api-server/src/core_api/conversions/receipt.rs +++ b/core-rust/core-api-server/src/core_api/conversions/receipt.rs @@ -379,7 +379,7 @@ pub fn to_api_state_updates( &typed_substate_key, new, )?), - previous_value: if context.substate_options.include_previous { + previous_value: if context.substate_options.include_previous && !previous.is_empty() { Some(Box::new(to_api_substate_value( context, &state_mapping_lookups, @@ -395,7 +395,7 @@ pub fn to_api_state_updates( SubstateChangeAction::Delete { previous } => { deleted_substates.push(models::DeletedSubstate { substate_id, - previous_value: if context.substate_options.include_previous { + previous_value: if context.substate_options.include_previous && !previous.is_empty() { Some(Box::new(to_api_substate_value( context, &state_mapping_lookups, diff --git a/core-rust/state-manager/src/receipt.rs b/core-rust/state-manager/src/receipt.rs index 4f07acf97f..74b12f1444 100644 --- a/core-rust/state-manager/src/receipt.rs +++ b/core-rust/state-manager/src/receipt.rs @@ -594,6 +594,23 @@ impl BySubstate { }) } + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + self.by_node_id + .iter_mut() + .flat_map(|(node_id, by_partition_num)| { + by_partition_num + .iter_mut() + .flat_map(move |(partition_num, by_substate_key)| { + by_substate_key.iter_mut().map(move |(substate_key, element)| { + ( + SubstateReference(*node_id, *partition_num, substate_key.clone()), + element, + ) + }) + }) + }) + } + pub fn iter_node_ids(&self) -> impl Iterator + '_ { self.by_node_id.keys() } diff --git a/core-rust/state-manager/src/store/rocks_db.rs b/core-rust/state-manager/src/store/rocks_db.rs index fa0fdab4ab..c2e24042da 100644 --- a/core-rust/state-manager/src/store/rocks_db.rs +++ b/core-rust/state-manager/src/store/rocks_db.rs @@ -221,6 +221,7 @@ impl ActualStateManagerDatabase { enable_account_change_index: false, enable_historical_substate_values: false, enable_entity_listing_indices: false, + keep_previous_substate_values: true, }, rocks: DirectRocks { db }, } @@ -256,6 +257,7 @@ impl ActualStateManagerDatabase { enable_account_change_index: false, enable_historical_substate_values: false, enable_entity_listing_indices: false, + keep_previous_substate_values: true, }, rocks: DirectRocks { db }, } @@ -698,9 +700,18 @@ impl StateManagerDatabase { db_context .cf(CommittedTransactionIdentifiersCf) .put(&state_version, &identifiers); + + let mut on_ledger_receipt = receipt.on_ledger; + if !self.config.keep_previous_substate_values { + for (_, action) in on_ledger_receipt.state_changes.substate_level_changes.iter_mut() { + if let SubstateChangeAction::Update { previous, .. } = action { + previous.clear(); + } + } + } db_context .cf(TransactionReceiptsCf) - .put(&state_version, &receipt.on_ledger); + .put(&state_version, &on_ledger_receipt); for nullification in &receipt.local_execution.nullifications { let Nullification::Intent { intent_hash, .. } = nullification; diff --git a/core-rust/state-manager/src/store/traits.rs b/core-rust/state-manager/src/store/traits.rs index 064a1c4e8d..45371d2428 100644 --- a/core-rust/state-manager/src/store/traits.rs +++ b/core-rust/state-manager/src/store/traits.rs @@ -91,6 +91,7 @@ pub struct DatabaseConfig { pub enable_account_change_index: bool, pub enable_historical_substate_values: bool, pub enable_entity_listing_indices: bool, + pub keep_previous_substate_values: bool, } impl Default for DatabaseConfig { @@ -100,6 +101,7 @@ impl Default for DatabaseConfig { enable_account_change_index: true, enable_historical_substate_values: false, enable_entity_listing_indices: true, + keep_previous_substate_values: true, } } } diff --git a/core/src/main/java/com/radixdlt/RadixNodeModule.java b/core/src/main/java/com/radixdlt/RadixNodeModule.java index 15c7b974ce..fcd895c296 100644 --- a/core/src/main/java/com/radixdlt/RadixNodeModule.java +++ b/core/src/main/java/com/radixdlt/RadixNodeModule.java @@ -288,12 +288,14 @@ protected void configure() { var enableHistoricalSubstateValues = properties.get("db.historical_substate_values.enable", false); var enableEntityListingIndices = properties.get("db.entity_listing_indices.enable", false); + var keepPreviousSubstateValues = properties.get("db.keep_previous_substate_values", true); var databaseConfig = new DatabaseConfig( enableLocalTransactionExecutionIndex, enableAccountChangeIndex, enableHistoricalSubstateValues, - enableEntityListingIndices); + enableEntityListingIndices, + keepPreviousSubstateValues); var vertexMaxTransactionCount = properties.get( diff --git a/core/src/test-core/java/com/radixdlt/rev2/NodeRustEnvironmentHelper.java b/core/src/test-core/java/com/radixdlt/rev2/NodeRustEnvironmentHelper.java index 11f06a0a13..44eac9d6a1 100644 --- a/core/src/test-core/java/com/radixdlt/rev2/NodeRustEnvironmentHelper.java +++ b/core/src/test-core/java/com/radixdlt/rev2/NodeRustEnvironmentHelper.java @@ -85,7 +85,7 @@ public static NodeRustEnvironment createNodeRustEnvironment(String dbPath) { new RustMempoolConfig(mempoolMaxTotalTransactionsSize, mempoolMaxTransactionCount)), Option.none(), stateManagerDbConfig, - new DatabaseConfig(false, false, false, false), + new DatabaseConfig(false, false, false, false, true), LoggingConfig.getDefault(), StateTreeGcConfig.forTesting(), LedgerProofsGcConfig.forTesting(), diff --git a/core/src/test/java/com/radixdlt/api/DeterministicEngineStateApiTestBase.java b/core/src/test/java/com/radixdlt/api/DeterministicEngineStateApiTestBase.java index e1cc8d0d87..de70c2b965 100644 --- a/core/src/test/java/com/radixdlt/api/DeterministicEngineStateApiTestBase.java +++ b/core/src/test/java/com/radixdlt/api/DeterministicEngineStateApiTestBase.java @@ -120,7 +120,7 @@ protected StateComputerConfig.REv2StateComputerConfig defaultConfig() { 1, Decimal.ONE, GenesisConsensusManagerConfig.Builder.testDefaults().epochExactRoundCount(1000000))) - .withDatabaseConfig(new DatabaseConfig(true, false, true, true)) + .withDatabaseConfig(new DatabaseConfig(true, false, true, true, true)) .withStateTreeGcConfig( new StateTreeGcConfig(UInt32.fromNonNegativeInt(1), UInt64.fromNonNegativeLong(20))); } diff --git a/core/src/test/java/com/radixdlt/api/core/LtsAccountDepositBehaviourTest.java b/core/src/test/java/com/radixdlt/api/core/LtsAccountDepositBehaviourTest.java index 1b2ba35b99..f4489503ac 100644 --- a/core/src/test/java/com/radixdlt/api/core/LtsAccountDepositBehaviourTest.java +++ b/core/src/test/java/com/radixdlt/api/core/LtsAccountDepositBehaviourTest.java @@ -84,7 +84,7 @@ public final class LtsAccountDepositBehaviourTest extends DeterministicCoreApiTe @Test public void account_with_default_config_allows_all_deposits() throws Exception { final var config = - defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false)); + defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false, true)); try (final var test = buildRunningServerTest(config)) { test.suppressUnusedWarning(); @@ -160,7 +160,7 @@ public void account_with_default_config_allows_all_deposits() throws Exception { @Test public void account_with_reject_default_rule_disallows_all_deposits() throws Exception { final var config = - defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false)); + defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false, true)); try (final var test = buildRunningServerTest(config)) { test.suppressUnusedWarning(); @@ -216,7 +216,7 @@ public void account_with_reject_default_rule_disallows_all_deposits() throws Exc @Test public void configured_resource_preference_and_depositor_badge_is_returned() throws Exception { final var config = - defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false)); + defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false, true)); try (final var test = buildRunningServerTest(config)) { test.suppressUnusedWarning(); diff --git a/core/src/test/java/com/radixdlt/api/core/LtsAccountResourceBalanceTest.java b/core/src/test/java/com/radixdlt/api/core/LtsAccountResourceBalanceTest.java index 774a0e6aa1..88211a07d2 100644 --- a/core/src/test/java/com/radixdlt/api/core/LtsAccountResourceBalanceTest.java +++ b/core/src/test/java/com/radixdlt/api/core/LtsAccountResourceBalanceTest.java @@ -80,7 +80,7 @@ public final class LtsAccountResourceBalanceTest extends DeterministicCoreApiTes @Test public void test_lts_account_xrd_balance() throws Exception { final var config = - defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false)); + defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false, true)); try (final var test = buildRunningServerTest(config)) { test.suppressUnusedWarning(); diff --git a/core/src/test/java/com/radixdlt/api/core/LtsTransactionOutcomesTest.java b/core/src/test/java/com/radixdlt/api/core/LtsTransactionOutcomesTest.java index d29e9bce02..997e680cba 100644 --- a/core/src/test/java/com/radixdlt/api/core/LtsTransactionOutcomesTest.java +++ b/core/src/test/java/com/radixdlt/api/core/LtsTransactionOutcomesTest.java @@ -87,7 +87,7 @@ public class LtsTransactionOutcomesTest extends DeterministicCoreApiTestBase { @Test public void test_non_fungible_entity_changes() throws Exception { final var config = - defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false)); + defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false, true)); try (final var test = buildRunningServerTest(config)) { test.suppressUnusedWarning(); @@ -269,7 +269,7 @@ private LtsCommittedTransactionOutcome getSingleCommittedTransactionOutcome( @Test public void test_multiple_transactions_have_correct_outcomes() throws Exception { final var config = - defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false)); + defaultConfig().withDatabaseConfig(new DatabaseConfig(true, true, false, false, true)); try (final var test = buildRunningServerTest(config)) { test.suppressUnusedWarning(); diff --git a/core/src/test/java/com/radixdlt/api/core/TransactionPreviewTest.java b/core/src/test/java/com/radixdlt/api/core/TransactionPreviewTest.java index d9934af569..ff5f059f0f 100644 --- a/core/src/test/java/com/radixdlt/api/core/TransactionPreviewTest.java +++ b/core/src/test/java/com/radixdlt/api/core/TransactionPreviewTest.java @@ -459,7 +459,7 @@ private static double getVaultBalance(TransactionReceipt receipt, String vaultAd private DeterministicTest buildTest(boolean stateHistoryEnabled, long historyLength) { return buildRunningServerTest( defaultConfig() - .withDatabaseConfig(new DatabaseConfig(true, false, stateHistoryEnabled, false)) + .withDatabaseConfig(new DatabaseConfig(true, false, stateHistoryEnabled, false, true)) .withStateTreeGcConfig( new StateTreeGcConfig( UInt32.fromNonNegativeInt(1), UInt64.fromNonNegativeLong(historyLength)))); diff --git a/core/src/test/java/com/radixdlt/api/core/TransactionPreviewV2Test.java b/core/src/test/java/com/radixdlt/api/core/TransactionPreviewV2Test.java index ee66c00bea..b8bcade7d9 100644 --- a/core/src/test/java/com/radixdlt/api/core/TransactionPreviewV2Test.java +++ b/core/src/test/java/com/radixdlt/api/core/TransactionPreviewV2Test.java @@ -83,7 +83,7 @@ public class TransactionPreviewV2Test extends DeterministicCoreApiTestBase { private DeterministicTest buildTest(boolean stateHistoryEnabled, long historyLength) { return buildRunningServerTest( defaultConfig() - .withDatabaseConfig(new DatabaseConfig(true, false, stateHistoryEnabled, false)) + .withDatabaseConfig(new DatabaseConfig(true, false, stateHistoryEnabled, false, true)) .withStateTreeGcConfig( new StateTreeGcConfig( UInt32.fromNonNegativeInt(1), UInt64.fromNonNegativeLong(historyLength)))); diff --git a/core/src/test/java/com/radixdlt/api/core/regression/StateHistoryTest.java b/core/src/test/java/com/radixdlt/api/core/regression/StateHistoryTest.java index c579f8d1ba..a6f084e003 100644 --- a/core/src/test/java/com/radixdlt/api/core/regression/StateHistoryTest.java +++ b/core/src/test/java/com/radixdlt/api/core/regression/StateHistoryTest.java @@ -128,7 +128,7 @@ public void state_history_supports_substate_deletes() throws Exception { private DeterministicTest buildTest(boolean stateHistoryEnabled, long historyLength) { return buildRunningServerTest( defaultConfig() - .withDatabaseConfig(new DatabaseConfig(true, false, stateHistoryEnabled, false)) + .withDatabaseConfig(new DatabaseConfig(true, false, stateHistoryEnabled, false, true)) .withStateTreeGcConfig( new StateTreeGcConfig( UInt32.fromNonNegativeInt(1), UInt64.fromNonNegativeLong(historyLength)))); diff --git a/core/src/test/java/com/radixdlt/rev2/REv2StateComputerTest.java b/core/src/test/java/com/radixdlt/rev2/REv2StateComputerTest.java index e6bc5f8209..b5ac6804dc 100644 --- a/core/src/test/java/com/radixdlt/rev2/REv2StateComputerTest.java +++ b/core/src/test/java/com/radixdlt/rev2/REv2StateComputerTest.java @@ -133,7 +133,7 @@ private Injector createInjector() { REv2StateManagerModule.createForTesting( genesisProvider, ProposalLimitsConfig.testDefaults(), - new DatabaseConfig(false, false, false, false), + new DatabaseConfig(false, false, false, false, true), Option.none(), false, StateTreeGcConfig.forTesting(), diff --git a/core/src/test/java/com/radixdlt/rev2/RustMempoolTest.java b/core/src/test/java/com/radixdlt/rev2/RustMempoolTest.java index 5cb8fd576d..0c0185ba46 100644 --- a/core/src/test/java/com/radixdlt/rev2/RustMempoolTest.java +++ b/core/src/test/java/com/radixdlt/rev2/RustMempoolTest.java @@ -307,7 +307,7 @@ private NodeRustEnvironment createNodeRustEnvironment() throws IOException { new RustMempoolConfig(mempoolMaxTotalTransactionsSize, mempoolMaxTransactionCount)), Option.none(), stateManagerDbConfig, - new DatabaseConfig(false, false, false, false), + new DatabaseConfig(false, false, false, false, true), LoggingConfig.getDefault(), StateTreeGcConfig.forTesting(), LedgerProofsGcConfig.forTesting(), diff --git a/core/src/test/java/com/radixdlt/rev2/StateTreeGcTest.java b/core/src/test/java/com/radixdlt/rev2/StateTreeGcTest.java index a1efd8bcf8..65026405ec 100644 --- a/core/src/test/java/com/radixdlt/rev2/StateTreeGcTest.java +++ b/core/src/test/java/com/radixdlt/rev2/StateTreeGcTest.java @@ -101,7 +101,7 @@ private DeterministicTest createTest( var genesis = GenesisBuilder.createTestGenesisWithNumValidators( 1, Decimal.ONE, GenesisConsensusManagerConfig.Builder.testWithRoundsPerEpoch(100)); - var databaseConfig = new DatabaseConfig(false, false, storeHistoricalSubstates, false); + var databaseConfig = new DatabaseConfig(false, false, storeHistoricalSubstates, false, true); var proposerConfig = REV2ProposerConfig.noUserTransactions(); var stateTreeGcConfig = new StateTreeGcConfig( From c6356fba31bf8282e516a7f481139837ccd6da97 Mon Sep 17 00:00:00 2001 From: Lukasz Gasior Date: Tue, 28 Apr 2026 09:51:45 +0200 Subject: [PATCH 2/4] Apply spotless --- .../src/core_api/conversions/receipt.rs | 8 ++++++-- .../handlers/lts/transaction_status.rs | 14 +++++++------- .../core_api/handlers/transaction_status.rs | 14 +++++++------- .../src/engine_state_api/readers.rs | 12 ++++++------ core-rust/node-common/src/locks.rs | 2 +- core-rust/node-common/src/store/rocks_db.rs | 10 +++++----- core-rust/node-common/src/utils.rs | 8 ++------ core-rust/p2p/src/rocks_db.rs | 4 +++- .../src/accumulator_tree/slice_merger.rs | 2 +- .../src/accumulator_tree/tree_builder.rs | 4 ++-- .../src/protocol/protocol_config.rs | 3 +-- .../definitions/custom_definition.rs | 2 +- .../protocol_content_overrides.rs | 2 +- .../protocol_updates/protocol_updaters.rs | 6 +++--- core-rust/state-manager/src/receipt.rs | 18 ++++++++++++------ core-rust/state-manager/src/staging/cache.rs | 7 ++----- .../src/staging/epoch_handling.rs | 2 +- .../src/store/historical_state.rs | 4 ++-- core-rust/state-manager/src/store/rocks_db.rs | 15 +++++++++------ .../src/transaction/executable_logic.rs | 4 ++-- 20 files changed, 74 insertions(+), 67 deletions(-) diff --git a/core-rust/core-api-server/src/core_api/conversions/receipt.rs b/core-rust/core-api-server/src/core_api/conversions/receipt.rs index 5d9a27d0b8..2db9ba9222 100644 --- a/core-rust/core-api-server/src/core_api/conversions/receipt.rs +++ b/core-rust/core-api-server/src/core_api/conversions/receipt.rs @@ -379,7 +379,9 @@ pub fn to_api_state_updates( &typed_substate_key, new, )?), - previous_value: if context.substate_options.include_previous && !previous.is_empty() { + previous_value: if context.substate_options.include_previous + && !previous.is_empty() + { Some(Box::new(to_api_substate_value( context, &state_mapping_lookups, @@ -395,7 +397,9 @@ pub fn to_api_state_updates( SubstateChangeAction::Delete { previous } => { deleted_substates.push(models::DeletedSubstate { substate_id, - previous_value: if context.substate_options.include_previous && !previous.is_empty() { + previous_value: if context.substate_options.include_previous + && !previous.is_empty() + { Some(Box::new(to_api_substate_value( context, &state_mapping_lookups, diff --git a/core-rust/core-api-server/src/core_api/handlers/lts/transaction_status.rs b/core-rust/core-api-server/src/core_api/handlers/lts/transaction_status.rs index 5f6de047ec..c1cf0b8d19 100644 --- a/core-rust/core-api-server/src/core_api/handlers/lts/transaction_status.rs +++ b/core-rust/core-api-server/src/core_api/handlers/lts/transaction_status.rs @@ -36,13 +36,13 @@ pub(crate) async fn handle_lts_transaction_status( .filter_map(|p| p.1.intent_invalid_from_epoch) .next(); - let intent_is_permanently_rejected = invalid_from_epoch.map_or(false, |invalid_from_epoch| { - current_epoch >= invalid_from_epoch - }) || known_pending_payloads.iter().any(|p| { - p.1.earliest_permanent_rejection - .as_ref() - .map_or(false, |r| r.marks_permanent_rejection_for_intent()) - }); + let intent_is_permanently_rejected = invalid_from_epoch + .is_some_and(|invalid_from_epoch| current_epoch >= invalid_from_epoch) + || known_pending_payloads.iter().any(|p| { + p.1.earliest_permanent_rejection + .as_ref() + .is_some_and(|r| r.marks_permanent_rejection_for_intent()) + }); if let Some(txn_state_version) = txn_state_version_opt { let hashes = database diff --git a/core-rust/core-api-server/src/core_api/handlers/transaction_status.rs b/core-rust/core-api-server/src/core_api/handlers/transaction_status.rs index 23d991fcc6..5a30e0fdcc 100644 --- a/core-rust/core-api-server/src/core_api/handlers/transaction_status.rs +++ b/core-rust/core-api-server/src/core_api/handlers/transaction_status.rs @@ -36,13 +36,13 @@ pub(crate) async fn handle_transaction_status( .filter_map(|p| p.1.intent_invalid_from_epoch) .next(); - let intent_is_permanently_rejected = invalid_from_epoch.map_or(false, |invalid_from_epoch| { - current_epoch >= invalid_from_epoch - }) || known_pending_payloads.iter().any(|p| { - p.1.earliest_permanent_rejection - .as_ref() - .map_or(false, |r| r.marks_permanent_rejection_for_intent()) - }); + let intent_is_permanently_rejected = invalid_from_epoch + .is_some_and(|invalid_from_epoch| current_epoch >= invalid_from_epoch) + || known_pending_payloads.iter().any(|p| { + p.1.earliest_permanent_rejection + .as_ref() + .is_some_and(|r| r.marks_permanent_rejection_for_intent()) + }); if let Some(txn_state_version) = txn_state_version_opt { let hashes = database diff --git a/core-rust/engine-state-api-server/src/engine_state_api/readers.rs b/core-rust/engine-state-api-server/src/engine_state_api/readers.rs index 2d1e41379b..e6803a7b9e 100644 --- a/core-rust/engine-state-api-server/src/engine_state_api/readers.rs +++ b/core-rust/engine-state-api-server/src/engine_state_api/readers.rs @@ -667,7 +667,7 @@ pub struct BlueprintFieldMeta { impl BlueprintFieldMeta { /// Post-processes and returns the [`Self::transience_default_value_bytes`] (see the note there). - pub fn transience(&self) -> Option { + pub fn transience(&self) -> Option> { self.transience_default_value_bytes .as_ref() .map(|default_value_bytes| FieldTransienceMeta { @@ -1212,7 +1212,7 @@ impl<'s, S: SubstateDatabase> EngineStateDataLoader<'s, S> { module_id: ModuleId, collection_meta: &'s ObjectCollectionMeta, from_key: Option<&RawCollectionKey>, - ) -> Result + '_, EngineStateBrowsingError> { + ) -> Result> + '_, EngineStateBrowsingError> { // From performance PoV, there is no way to iterate over keys without iterating over values // too. The cost of the (discarded) `SborData` wrapper construction is negligible, hence: Ok(self @@ -1230,7 +1230,7 @@ impl<'s, S: SubstateDatabase> EngineStateDataLoader<'s, S> { collection_meta: &'s ObjectCollectionMeta, from_key: Option<&RawCollectionKey>, ) -> Result< - impl Iterator)> + '_, + impl Iterator, SborData<'s>)> + '_, EngineStateBrowsingError, > { let collection_index = collection_meta.index.number; @@ -1262,7 +1262,7 @@ impl<'s, S: SubstateDatabase> EngineStateDataLoader<'s, S> { node_id: &NodeId, kv_store_meta: &'s KeyValueStoreMeta, from_key: Option<&MapKey>, - ) -> Result + '_, EngineStateBrowsingError> { + ) -> Result> + '_, EngineStateBrowsingError> { Ok(self .reader .key_value_store_iter(node_id, from_key) @@ -1280,7 +1280,7 @@ impl<'s, S: SubstateDatabase> EngineStateDataLoader<'s, S> { pub fn load_schema( &self, reference: &SchemaReference, - ) -> Result { + ) -> Result, EngineStateBrowsingError> { let versioned_schema = self .reader .get_schema(&reference.node_id, &reference.schema_hash) @@ -1305,7 +1305,7 @@ impl<'s, S: SubstateDatabase> EngineStateDataLoader<'s, S> { fn to_object_collection_key( substate_key: SubstateKey, collection_meta: &ObjectCollectionMeta, - ) -> SborCollectionKey { + ) -> SborCollectionKey<'_> { match (&collection_meta.kind, substate_key) { (ObjectCollectionKind::KeyValueStore, SubstateKey::Map(key)) => { SborCollectionKey::KeyValueStore(SborData::new( diff --git a/core-rust/node-common/src/locks.rs b/core-rust/node-common/src/locks.rs index 026bc162bd..deebf8381d 100644 --- a/core-rust/node-common/src/locks.rs +++ b/core-rust/node-common/src/locks.rs @@ -309,7 +309,7 @@ impl<'s, D: Snapshottable<'s>> DbLock { /// do not leave database inconsistent. // TODO(future enhancement): also provide something like `lock_snapshot_unlock()`, which would // not need the "writes are atomic+consistent" assumption? - pub fn snapshot(&'s self) -> impl Deref + '_ { + pub fn snapshot(&'s self) -> impl Deref + 's { LockGuard::new( || OwnedDeref { owned: self.database.snapshot(), diff --git a/core-rust/node-common/src/store/rocks_db.rs b/core-rust/node-common/src/store/rocks_db.rs index b27d1b362c..8699c9a1b9 100644 --- a/core-rust/node-common/src/store/rocks_db.rs +++ b/core-rust/node-common/src/store/rocks_db.rs @@ -99,7 +99,7 @@ pub trait ReadableRocks { &self, cf: &impl AsColumnFamilyRef, key: impl AsRef<[u8]>, - ) -> Option; + ) -> Option>; /// Gets multiple values by keys. /// @@ -121,7 +121,7 @@ pub trait WriteableRocks: ReadableRocks { fn write(&self, batch: WriteBatch); /// Returns a snapshot of the current state. - fn snapshot(&self) -> SnapshotRocks; + fn snapshot(&self) -> SnapshotRocks<'_>; } /// A [`ReadableRocks`] instance opened as secondary instance. @@ -162,7 +162,7 @@ impl ReadableRocks for DirectRocks { &self, cf: &impl AsColumnFamilyRef, key: impl AsRef<[u8]>, - ) -> Option { + ) -> Option> { self.db.get_pinned_cf(cf, key).expect("DB get by key") } @@ -183,7 +183,7 @@ impl WriteableRocks for DirectRocks { self.db.write(batch).expect("DB write batch"); } - fn snapshot(&self) -> SnapshotRocks { + fn snapshot(&self) -> SnapshotRocks<'_> { SnapshotRocks { db: &self.db, snapshot: self.db.snapshot(), @@ -248,7 +248,7 @@ impl<'db> ReadableRocks for SnapshotRocks<'db> { &self, cf: &impl AsColumnFamilyRef, key: impl AsRef<[u8]>, - ) -> Option { + ) -> Option> { self.snapshot .get_pinned_cf(cf, key) .expect("snapshot DB get by key") diff --git a/core-rust/node-common/src/utils.rs b/core-rust/node-common/src/utils.rs index f8d5206110..7aa6be3aec 100644 --- a/core-rust/node-common/src/utils.rs +++ b/core-rust/node-common/src/utils.rs @@ -65,18 +65,14 @@ use crate::prelude::*; /// An implementation helper for a runtime-safe "capture a single expected value" functionality. +#[derive(Default)] pub enum CaptureSupport { + #[default] NotExpecting, Expecting, Captured(T), } -impl Default for CaptureSupport { - fn default() -> Self { - Self::NotExpecting - } -} - impl CaptureSupport { /// Configures this instance to expect a single [`Self::capture_value()`] call. pub fn expect_capture(&mut self) { diff --git a/core-rust/p2p/src/rocks_db.rs b/core-rust/p2p/src/rocks_db.rs index 4b54f2d27c..e91b19a7f0 100644 --- a/core-rust/p2p/src/rocks_db.rs +++ b/core-rust/p2p/src/rocks_db.rs @@ -127,7 +127,9 @@ fn new_rocks_db(root_path: PathBuf, column_families: &[&str]) -> DB { DB::open_cf_descriptors(&db_opts, root_path.as_path(), column_families).unwrap() } -fn open_rw_context(db: &R) -> TypedDbContext> { +fn open_rw_context( + db: &R, +) -> TypedDbContext<'_, R, BufferedWriteSupport<'_, R>> { TypedDbContext::new(db, BufferedWriteSupport::new(db)) } diff --git a/core-rust/state-manager/src/accumulator_tree/slice_merger.rs b/core-rust/state-manager/src/accumulator_tree/slice_merger.rs index 165dff9b00..c4abcb060e 100644 --- a/core-rust/state-manager/src/accumulator_tree/slice_merger.rs +++ b/core-rust/state-manager/src/accumulator_tree/slice_merger.rs @@ -109,7 +109,7 @@ impl AccuTreeSliceMerger { break; } let mut appended_nodes = appended_level.unwrap().nodes; - merged_to = (merged_to + 1) / 2; + merged_to = merged_to.div_ceil(2); appended_from /= 2; merged_nodes.truncate(merged_nodes.len() + appended_from - merged_to); merged_nodes.append(&mut appended_nodes); diff --git a/core-rust/state-manager/src/accumulator_tree/tree_builder.rs b/core-rust/state-manager/src/accumulator_tree/tree_builder.rs index 7088ac4882..0c6cf87b5e 100644 --- a/core-rust/state-manager/src/accumulator_tree/tree_builder.rs +++ b/core-rust/state-manager/src/accumulator_tree/tree_builder.rs @@ -124,7 +124,7 @@ impl<'s, S: AccuTreeStore, M: Merklizable> AccuTree<'s, S, M> { let target_height = usize::BITS - (target_length - 1).leading_zeros(); for _ in 0..target_height { let previous_slice_level = previous_slice_levels.next(); - let left_sibling_cache = if from % 2 == 0 { + let left_sibling_cache = if from.is_multiple_of(2) { None } else { let previous_slice_level = previous_slice_level.unwrap(); @@ -138,7 +138,7 @@ impl<'s, S: AccuTreeStore, M: Merklizable> AccuTree<'s, S, M> { TreeSliceLevel::new(left_sibling_cache, higher_level_nodes), ); - let to = (from + lower_level_access.slice_level.nodes.len() + 1) / 2; + let to = (from + lower_level_access.slice_level.nodes.len()).div_ceil(2); from /= 2; higher_level_nodes = (from..to) .map(|level_index| level_index * 2) diff --git a/core-rust/state-manager/src/protocol/protocol_config.rs b/core-rust/state-manager/src/protocol/protocol_config.rs index 282300995a..c0b820c8e0 100644 --- a/core-rust/state-manager/src/protocol/protocol_config.rs +++ b/core-rust/state-manager/src/protocol/protocol_config.rs @@ -259,8 +259,7 @@ impl ProtocolVersionName { pub fn padded_len_16_version_name_for_readiness_signal(&self) -> String { self.validate() .expect("Must be valid before extracting readiness signal name"); - std::iter::repeat('0') - .take(16 - self.0.len()) + std::iter::repeat_n('0', 16 - self.0.len()) .chain(self.0.chars()) .collect() } diff --git a/core-rust/state-manager/src/protocol/protocol_updates/definitions/custom_definition.rs b/core-rust/state-manager/src/protocol/protocol_updates/definitions/custom_definition.rs index 79d615ba78..d6a769fd3c 100644 --- a/core-rust/state-manager/src/protocol/protocol_updates/definitions/custom_definition.rs +++ b/core-rust/state-manager/src/protocol/protocol_updates/definitions/custom_definition.rs @@ -77,7 +77,7 @@ impl NodeProtocolUpdateGenerator for ArbitraryNodeBatchGenerator { false } - fn batch_groups(&self) -> Vec> { + fn batch_groups(&self) -> Vec + '_>> { let mut batch_group = NodeFixedBatchGroupGenerator::named(Self::BATCH_GROUP_DESCRIPTOR); for (index, batch) in self.batches.iter().enumerate() { batch_group = batch_group.add_batch(format!("batch-{index:02}"), |_| batch.clone()) diff --git a/core-rust/state-manager/src/protocol/protocol_updates/protocol_content_overrides.rs b/core-rust/state-manager/src/protocol/protocol_updates/protocol_content_overrides.rs index 72d38d02ef..f23830666c 100644 --- a/core-rust/state-manager/src/protocol/protocol_updates/protocol_content_overrides.rs +++ b/core-rust/state-manager/src/protocol/protocol_updates/protocol_content_overrides.rs @@ -124,7 +124,7 @@ impl RawProtocolUpdateContentOverrides { Default::default() } - pub fn iter(&self) -> hash_map::Iter> { + pub fn iter(&self) -> hash_map::Iter<'_, ProtocolVersionName, Vec> { self.0.iter() } diff --git a/core-rust/state-manager/src/protocol/protocol_updates/protocol_updaters.rs b/core-rust/state-manager/src/protocol/protocol_updates/protocol_updaters.rs index fa0acd30e6..49585f0b8d 100644 --- a/core-rust/state-manager/src/protocol/protocol_updates/protocol_updaters.rs +++ b/core-rust/state-manager/src/protocol/protocol_updates/protocol_updaters.rs @@ -36,7 +36,7 @@ pub trait NodeProtocolUpdateGenerator { /// Return the list of batch groups for the protocol update. /// /// Each should be a fixed, conceptual step in the update process. - fn batch_groups(&self) -> Vec>; + fn batch_groups(&self) -> Vec + '_>>; } /// Each batch group is a logical grouping of batches. @@ -107,7 +107,7 @@ impl NodeProtocolUpdateGenerator for WrappedProtocolUpdateGenerator { .insert_status_tracking_flash_transactions() } - fn batch_groups(&self) -> Vec> { + fn batch_groups(&self) -> Vec + '_>> { self.engine_generator .batch_groups() .into_iter() @@ -236,7 +236,7 @@ impl NodeProtocolUpdateGenerator }) } - fn batch_groups(&self) -> Vec> { + fn batch_groups(&self) -> Vec + '_>> { let mut batch_groups = self.base_batch_generator.batch_groups(); if !self.scenario_names.is_empty() { batch_groups.insert( diff --git a/core-rust/state-manager/src/receipt.rs b/core-rust/state-manager/src/receipt.rs index 74b12f1444..f8755f82b7 100644 --- a/core-rust/state-manager/src/receipt.rs +++ b/core-rust/state-manager/src/receipt.rs @@ -601,12 +601,18 @@ impl BySubstate { by_partition_num .iter_mut() .flat_map(move |(partition_num, by_substate_key)| { - by_substate_key.iter_mut().map(move |(substate_key, element)| { - ( - SubstateReference(*node_id, *partition_num, substate_key.clone()), - element, - ) - }) + by_substate_key + .iter_mut() + .map(move |(substate_key, element)| { + ( + SubstateReference( + *node_id, + *partition_num, + substate_key.clone(), + ), + element, + ) + }) }) }) } diff --git a/core-rust/state-manager/src/staging/cache.rs b/core-rust/state-manager/src/staging/cache.rs index cbc46e530c..5a1f2c6a3d 100644 --- a/core-rust/state-manager/src/staging/cache.rs +++ b/core-rust/state-manager/src/staging/cache.rs @@ -106,13 +106,10 @@ impl ExecutionCacheManager { let execution_cache = self.execution_cache.lock(); let mut transaction_root = parent_transaction_root; for transaction in transactions { - transaction_root = match execution_cache.get_cached_transaction_root( + transaction_root = execution_cache.get_cached_transaction_root( transaction_root, &transaction.ledger_transaction_hash(), - ) { - Some(cached) => cached, - None => return None, - } + )? } Some(*transaction_root) } diff --git a/core-rust/state-manager/src/staging/epoch_handling.rs b/core-rust/state-manager/src/staging/epoch_handling.rs index b2b1477337..41fd6912e4 100644 --- a/core-rust/state-manager/src/staging/epoch_handling.rs +++ b/core-rust/state-manager/src/staging/epoch_handling.rs @@ -96,7 +96,7 @@ impl EpochAwareAccuTreeFactory { &'s self, previous_epoch_root: N, store: &'s mut S, - ) -> EpochAccuTreeBuilder { + ) -> EpochAccuTreeBuilder<'s, S, N> { EpochAccuTreeBuilder::new( store, self.epoch_start_state_version, diff --git a/core-rust/state-manager/src/store/historical_state.rs b/core-rust/state-manager/src/store/historical_state.rs index e4172f8d66..eec95a1bbe 100644 --- a/core-rust/state-manager/src/store/historical_state.rs +++ b/core-rust/state-manager/src/store/historical_state.rs @@ -264,10 +264,10 @@ impl<'s, R: ReadableRocks + 's, DS: Deref>> return Err(StateHistoryError::StateVersionInFuture { current_version }); } - return Ok(Self::Historical(StateTreeBasedSubstateDatabase::new( + Ok(Self::Historical(StateTreeBasedSubstateDatabase::new( database, requested_version, - ))); + ))) } /// Returns the summary of the ledger's state at which this store is scoped. diff --git a/core-rust/state-manager/src/store/rocks_db.rs b/core-rust/state-manager/src/store/rocks_db.rs index c2e24042da..3baf187edd 100644 --- a/core-rust/state-manager/src/store/rocks_db.rs +++ b/core-rust/state-manager/src/store/rocks_db.rs @@ -270,14 +270,14 @@ impl ActualStateManagerDatabase { impl StateManagerDatabase { /// Starts a read-only interaction with the DB through per-CF type-safe APIs. - fn open_read_context(&self) -> TypedDbContext { + fn open_read_context(&self) -> TypedDbContext<'_, R, NoWriteSupport> { TypedDbContext::new(&self.rocks, NoWriteSupport) } } impl StateManagerDatabase { /// Starts a read/buffered-write interaction with the DB through per-CF type-safe APIs. - fn open_rw_context(&self) -> TypedDbContext> { + fn open_rw_context(&self) -> TypedDbContext<'_, R, BufferedWriteSupport<'_, R>> { TypedDbContext::new(&self.rocks, BufferedWriteSupport::new(&self.rocks)) } } @@ -703,7 +703,11 @@ impl StateManagerDatabase { let mut on_ledger_receipt = receipt.on_ledger; if !self.config.keep_previous_substate_values { - for (_, action) in on_ledger_receipt.state_changes.substate_level_changes.iter_mut() { + for (_, action) in on_ledger_receipt + .state_changes + .substate_level_changes + .iter_mut() + { if let SubstateChangeAction::Update { previous, .. } = action { previous.clear(); } @@ -1638,9 +1642,8 @@ impl RestoreDecember2023LostSubstates for StateManagerDatabas // Substates were deleted on the transition to epoch 51817 so no need to restore // substates if the current epoch has not reached this epoch yet. - self.get_latest_epoch_proof().map_or(false, |p| { - p.ledger_header.next_epoch.unwrap().epoch.number() >= 51817 - }) + self.get_latest_epoch_proof() + .is_some_and(|p| p.ledger_header.next_epoch.unwrap().epoch.number() >= 51817) } else { // For other networks, we can calculate the "problem" epoch from theoretical principles: let (Some(first_proof), Some(latest_epoch_proof)) = diff --git a/core-rust/state-manager/src/transaction/executable_logic.rs b/core-rust/state-manager/src/transaction/executable_logic.rs index 18c18b1843..161283ae22 100644 --- a/core-rust/state-manager/src/transaction/executable_logic.rs +++ b/core-rust/state-manager/src/transaction/executable_logic.rs @@ -103,7 +103,7 @@ impl ExecutionConfigurator { transaction_hashes: &LedgerTransactionHashes, ledger_executable: &'a LedgerExecutable, description: impl ToString, - ) -> ConfiguredExecutable { + ) -> ConfiguredExecutable<'a> { match ledger_executable { LedgerExecutable::GenesisFlash => ConfiguredExecutable::SystemFlash { state_updates: create_system_bootstrap_flash_state_updates(), @@ -157,7 +157,7 @@ impl ExecutionConfigurator { executable: &'a ExecutableTransaction, config_type: ConfigType, description: String, - ) -> ConfiguredExecutable { + ) -> ConfiguredExecutable<'a> { ConfiguredExecutable::Transaction { executable, vm_modules: &self.vm_modules, From e85f812dbd943fb97029a322cd2f9a0d02ef0cab Mon Sep 17 00:00:00 2001 From: Lukasz Gasior Date: Tue, 28 Apr 2026 10:32:41 +0200 Subject: [PATCH 3/4] Add env variable --- docker/config/default.config.envsubst | 1 + testnet-node/radix-node.env | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docker/config/default.config.envsubst b/docker/config/default.config.envsubst index 6764636567..8ce6813466 100644 --- a/docker/config/default.config.envsubst +++ b/docker/config/default.config.envsubst @@ -17,6 +17,7 @@ db.local_transaction_execution_index.enable=$RADIXDLT_DB_LOCAL_TRANSACTION_EXECU db.account_change_index.enable=$RADIXDLT_DB_ACCOUNT_CHANGE_INDEX_ENABLE db.entity_listing_indices.enable=$RADIXDLT_ENTITY_LISTING_INDICES_ENABLE db.historical_substate_values.enable=$RADIXDLT_DB_HISTORICAL_SUBSTATE_VALUES_ENABLE +db.keep_previous_substate_values=$RADIXDLT_DB_KEEP_PREVIOUS_SUBSTATE_VALUES db.checkpoints_path=${RADIXDLT_DB_CHECKPOINTS_PATH} mempool.max_transaction_count=${RADIXDLT_MEMPOOL_MAX_TRANSACTION_COUNT} diff --git a/testnet-node/radix-node.env b/testnet-node/radix-node.env index f94024cd5c..5fdee38244 100644 --- a/testnet-node/radix-node.env +++ b/testnet-node/radix-node.env @@ -13,3 +13,5 @@ RADIXDLT_NETWORK_SEEDS_REMOTE=radix://node_tdx_2_1qv89yg0la2jt429vqp8sxtpg95hj63 # Enabling Engine State API's features: RADIXDLT_ENTITY_LISTING_INDICES_ENABLE=true RADIXDLT_DB_HISTORICAL_SUBSTATE_VALUES_ENABLE=true + +RADIXDLT_DB_KEEP_PREVIOUS_SUBSTATE_VALUES=false \ No newline at end of file From a15974472ace3d0646f58250fc55108c55c1c932 Mon Sep 17 00:00:00 2001 From: Lukasz Gasior Date: Tue, 28 Apr 2026 11:58:56 +0200 Subject: [PATCH 4/4] Update libssl version in Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ad56e523ba..d2240686a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,7 @@ ENV VERSION_LAST_TAG=$VERSION_LAST_TAG RUN apt-get update \ && apt-get install -y --no-install-recommends \ docker.io=20.10.24+dfsg1-1+deb12u1+b3 \ - libssl-dev=3.0.18-1~deb12u2 \ + libssl-dev=3.0.19-1~deb12u2 \ pkg-config=1.8.1-1 \ unzip=6.0-28 \ wget=${WGET_VERSION} \ @@ -131,7 +131,7 @@ RUN apt-get update \ g++-x86-64-linux-gnu \ libc6-dev-arm64-cross=2.36-8cross1 \ libclang-dev=1:14.0-55.7~deb12u1 \ - libssl-dev=3.0.18-1~deb12u2 \ + libssl-dev=3.0.19-1~deb12u2 \ pkg-config=1.8.1-1 \ && rm -rf /var/lib/apt/lists/*