From cde67ff0e44b63946907eb8f5261eaad7a4e0c6a Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Wed, 23 Jul 2025 17:54:18 +0300 Subject: [PATCH 1/9] draft implementation Signed-off-by: Nikita Lebedev --- .../platform/state/virtual_map_state.proto | 326 ++++++++++++++++++ .../app/state/merkle/disk/OnDiskTest.java | 14 +- .../roster/WritableRosterStoreTest.java | 2 +- .../WritablePlatformStateStoreTest.java | 4 +- .../com/swirlds/state/merkle/StateUtils.java | 11 + .../swirlds/state/merkle/VirtualMapState.java | 43 ++- .../state/merkle/disk/OnDiskQueueHelper.java | 45 ++- .../merkle/disk/OnDiskReadableKVState.java | 26 +- .../merkle/disk/OnDiskReadableQueueState.java | 15 +- .../disk/OnDiskReadableSingletonState.java | 13 +- .../merkle/disk/OnDiskSingletonHelper.java | 51 ++- .../merkle/disk/OnDiskWritableKVState.java | 43 ++- .../merkle/disk/OnDiskWritableQueueState.java | 41 +-- .../disk/OnDiskWritableSingletonState.java | 31 +- .../src/main/java/module-info.java | 2 +- .../merkle/disk/OnDiskReadableStateTest.java | 25 +- .../merkle/disk/OnDiskWritableStateTest.java | 25 +- 17 files changed, 531 insertions(+), 186 deletions(-) diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto index a984d927cc83..918281a93b05 100644 --- a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto +++ b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto @@ -54,6 +54,21 @@ import "services/state/tss/tss_encryption_keys.proto"; import "services/state/tss/tss_message_map_key.proto"; import "services/state/tss/tss_vote_map_key.proto"; +// TODO: docs +message VirtualLeaf { + VirtualMapKey key = 2; + + VirtualMapValue value = 3; + +} + +message QueueState { + int64 head = 1; + + int64 tail = 2; + +} + /** * This message defines the type of keys used in the Virtual Map State. * Note that `_I_` plays a role of a reliable separator between the service name @@ -270,6 +285,317 @@ message VirtualMapKey { } } +// TODO: add docs +message VirtualMapValue { + oneof value{ + /** + * A state identifier for the next entity Identifier. + */ + proto.EntityNumber EntityIdService_I_ENTITY_ID = 1; + + /** + * A state identifier for the Accounts state. + */ + proto.Account TokenService_I_ACCOUNTS = 2; + + /** + * A state identifier for account aliases. + */ + proto.AccountID TokenService_I_ALIASES = 3; + + /** + * A state identifier for contract storage slots. + */ + proto.SlotValue ContractService_I_STORAGE = 4; + + /** + * A state identifier for contract bytecode. + */ + proto.Bytecode ContractService_I_BYTECODE = 5; + + /** + * A state identifier for Hedera File Service (HFS). + */ + proto.File FileService_I_FILES = 6; + + /** + * A state identifier for Hedera Token Service (HTS). + */ + proto.Token TokenService_I_TOKENS = 7; + + /** + * A state identifier for non-fungible/unique tokens. + */ + proto.Nft TokenService_I_NFTS = 8; + + /** + * A state identifier for token relationships. + */ + proto.TokenRelation TokenService_I_TOKEN_RELS = 9; + + /** + * A state identifier for network staking information. + */ + proto.StakingNodeInfo TokenService_I_STAKING_INFOS = 10; + + /** + * A state identifier for network staking rewards. + */ + proto.NetworkStakingRewards TokenService_I_STAKING_NETWORK_REWARDS = 11; + + /** + * A state identifier for throttle usage. + */ + proto.ThrottleUsageSnapshots CongestionThrottleService_I_THROTTLE_USAGE_SNAPSHOTS = 12; + + /** + * A state identifier for network congestion start times. + */ + proto.CongestionLevelStarts CongestionThrottleService_I_CONGESTION_LEVEL_STARTS = 13; + + /** + * A state identifier for scheduled transactions. + */ + proto.Schedule ScheduleService_I_SCHEDULES_BY_ID = 14; + + /** + * A state identifier for scheduled transaction expiration. + */ + proto.ScheduleList ScheduleService_I_SCHEDULES_BY_EXPIRY_SEC = 15; + + /** + * A state identifier for scheduled transaction deduplication. + */ + proto.ScheduleList ScheduleService_I_SCHEDULES_BY_EQUALITY = 16; + + /** + * A state identifier for conversion rate updates. + */ + proto.ExchangeRateSet FeeService_I_MIDNIGHT_RATES = 17; + + /** + * A state identifier for the network running hash(es). + */ + proto.RunningHashes BlockRecordService_I_RUNNING_HASHES = 18; + + /** + * A state identifier for network block information. + */ + proto.BlockInfo BlockRecordService_I_BLOCKS = 19; + + /** + * A state identifier for address book nodes. + */ + com.hedera.hapi.node.state.addressbook.Node AddressBookService_I_NODES = 20; + + /** + * A state identifier for the Topics state. + */ + proto.Topic ConsensusService_I_TOPICS = 21; + + /** + * A state identifier for the hash of the next "upgrade" file. + */ + proto.ProtoBytes FreezeService_I_UPGRADE_FILE_HASH = 22; + + /** + * A state identifier for the next network freeze time. Singleton state. + */ + proto.Timestamp FreezeService_I_FREEZE_TIME = 23; + + /** + * A state identifier for the block stream status. Singleton state. + */ + com.hedera.hapi.node.state.blockstream.BlockStreamInfo BlockStreamService_I_BLOCK_STREAM_INFO = 24; + + /** + * A state identifier for pending airdrops. + */ + proto.AccountPendingAirdrop TokenService_I_PENDING_AIRDROPS = 25; + + /** + * A state identifier for the platform state. Singleton state. + */ + PlatformState PlatformStateService_I_PLATFORM_STATE = 26; + + /** + * A state identifier for the roster state. Singleton state. + */ + com.hedera.hapi.node.state.roster.RosterState RosterService_I_ROSTER_STATE = 27; + + /** + * A state identifier for the rosters. + */ + com.hedera.hapi.node.state.roster.Roster RosterService_I_ROSTERS = 28; + + /** + * A state identifier for counts of transactions scheduled and + * processed in a second. + */ + proto.ScheduledCounts ScheduleService_I_SCHEDULED_COUNTS = 29; + + /** + * A state identifier for scheduled transaction deduplication. + */ + proto.ScheduleID ScheduleService_I_SCHEDULE_ID_BY_EQUALITY = 30; + + /** + * A state identifier for TSS messages. + */ + com.hedera.hapi.services.auxiliary.tss.TssMessageTransactionBody TssBaseService_I_TSS_MESSAGES = 31; + + /** + * A state identifier for TSS votes. + */ + com.hedera.hapi.services.auxiliary.tss.TssVoteTransactionBody TssBaseService_I_TSS_VOTES = 32; + + /** + * A state identifier for the ordering of scheduled transactions. + */ + proto.ScheduleID ScheduleService_I_SCHEDULED_ORDERS = 33; + + /** + * A state identifier for scheduled throttle usage snapshots. + */ + proto.ThrottleUsageSnapshots ScheduleService_I_SCHEDULED_USAGES = 34; + + /** + * A state identifier for the TSS encryption keys. + */ + com.hedera.hapi.node.state.tss.TssEncryptionKeys TssBaseService_I_TSS_ENCRYPTION_KEY = 35; + + /** + * A state identifier for hinTS key sets. + */ + com.hedera.hapi.node.state.hints.HintsKeySet HintsService_I_HINTS_KEY_SETS = 37; + + /** + * A state identifier for the active hinTS construction. Singleton state. + */ + com.hedera.hapi.node.state.hints.HintsConstruction HintsService_I_ACTIVE_HINTS_CONSTRUCTION = 38; + + /** + * A state identifier for the next hinTS construction. Singleton state. + */ + com.hedera.hapi.node.state.hints.HintsConstruction HintsService_I_NEXT_HINTS_CONSTRUCTION = 39; + + /** + * A state identifier for hinTS preprocessing output votes. + */ + com.hedera.hapi.node.state.hints.PreprocessingVote HintsService_I_PREPROCESSING_VOTES = 40; + + /** + * A state identifier for the entity counts. Singleton state. + */ + com.hedera.hapi.node.state.entity.EntityCounts EntityIdService_I_ENTITY_COUNTS = 41; + + /** + * A state identifier for the ledger id. Singleton state. + */ + proto.ProtoBytes HistoryService_I_LEDGER_ID = 42; + + /** + * A state identifier for history proof key sets. + */ + com.hedera.hapi.node.state.history.ProofKeySet HistoryService_I_PROOF_KEY_SETS = 43; + + /** + * A state identifier for the active proof construction. Singleton state. + */ + com.hedera.hapi.node.state.history.HistoryProofConstruction HistoryService_I_ACTIVE_PROOF_CONSTRUCTION = 44; + + /** + * A state identifier for the next proof construction. Singleton state. + */ + com.hedera.hapi.node.state.history.HistoryProofConstruction HistoryService_I_NEXT_PROOF_CONSTRUCTION = 45; + + /** + * A state identifier for signatures on roster transition histories. + */ + com.hedera.hapi.node.state.history.RecordedHistorySignature HistoryService_I_HISTORY_SIGNATURES = 46; + + /** + * A state identifier for votes on history proofs. + */ + com.hedera.hapi.node.state.history.HistoryProofVote HistoryService_I_PROOF_VOTES = 47; + + /** + * A state identifier for the CRS state. Singleton state. + */ + com.hedera.hapi.node.state.hints.CRSState HintsService_I_CRS_STATE = 48; + + /** + * A state identifier for the CRS publications. + */ + com.hedera.hapi.services.auxiliary.hints.CrsPublicationTransactionBody HintsService_I_CRS_PUBLICATIONS = 49; + + /** + * A state identifier for the node rewards. Singleton state. + */ + proto.NodeRewards TokenService_I_NODE_REWARDS = 50; + + /** + * Metadata of the round receipts queue. + */ + // TODO: double check + proto.TransactionReceiptEntries RecordCacheTransactionReceiptQueue = 126; + + /** + * Metadata of the `150` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_150 = 10001; + + /** + * Metadata of the`151` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_151 = 10002; + + /** + * Metadata of the `152` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_152 = 10003; + + /** + * Metadata of the `153` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_153 = 10004; + + /** + * Metadata of the`154` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_154 = 10005; + + /** + * Metadata of the `155` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_155 = 10006; + + /** + * Metadata of the `156` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_156 = 10007; + + /** + * Metadata of the `157` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_157 = 10008; + + /** + * Metadata of the `158` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_158 = 10009; + + /** + * Metadata of the `159` upgrade file data queue. + */ + proto.ProtoBytes FileService_I_UPGRADE_DATA_159 = 10010; + + //// + QueueState queue_state = 11111; + + } +} + /** * An enumeration of the singleton state types. */ diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java index c7bf0e9a1d91..3fef3bdd166d 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java @@ -101,8 +101,7 @@ VirtualMap copyHashAndFlush(VirtualMap map) { @Test void populateTheMapAndFlushToDiskAndReadBack() throws IOException { // Populate the data set and flush it all to disk - final var ws = new OnDiskWritableKVState<>( - TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, Account.PROTOBUF, virtualMap); + final var ws = new OnDiskWritableKVState<>(TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, virtualMap); for (int i = 0; i < 10; i++) { final var id = AccountID.newBuilder().accountNum(i).build(); final var acct = Account.newBuilder() @@ -135,8 +134,8 @@ void populateTheMapAndFlushToDiskAndReadBack() throws IOException { // read it back now as our map and validate the data come back fine virtualMap = parseTree(serializedBytes, snapshotDir); - final var rs = new OnDiskReadableKVState<>( - TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, Account.PROTOBUF, virtualMap); + final var rs = new OnDiskReadableKVState( + TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, virtualMap); for (int i = 0; i < 10; i++) { final var id = AccountID.newBuilder().accountNum(i).build(); final var acct = rs.get(id); @@ -149,8 +148,7 @@ void populateTheMapAndFlushToDiskAndReadBack() throws IOException { @Test void populateFlushToDisk() { - final var ws = new OnDiskWritableKVState<>( - TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, Account.PROTOBUF, virtualMap); + final var ws = new OnDiskWritableKVState<>(TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, virtualMap); for (int i = 1; i < 10; i++) { final var id = AccountID.newBuilder().accountNum(i).build(); final var acct = Account.newBuilder() @@ -163,8 +161,8 @@ void populateFlushToDisk() { ws.commit(); virtualMap = copyHashAndFlush(virtualMap); - final var rs = new OnDiskReadableKVState<>( - TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, Account.PROTOBUF, virtualMap); + final var rs = new OnDiskReadableKVState( + TokenService.NAME, ACCOUNTS_KEY, AccountID.PROTOBUF, virtualMap); for (int i = 1; i < 10; i++) { final var id = AccountID.newBuilder().accountNum(i).build(); final var acct = rs.get(id); diff --git a/platform-sdk/consensus-utility/src/test/java/org/hiero/consensus/roster/WritableRosterStoreTest.java b/platform-sdk/consensus-utility/src/test/java/org/hiero/consensus/roster/WritableRosterStoreTest.java index b5afb0f43bcd..0423e5365ee0 100644 --- a/platform-sdk/consensus-utility/src/test/java/org/hiero/consensus/roster/WritableRosterStoreTest.java +++ b/platform-sdk/consensus-utility/src/test/java/org/hiero/consensus/roster/WritableRosterStoreTest.java @@ -57,7 +57,7 @@ void setUp() { .thenReturn(rosters); when(writableStates.getSingleton(WritableRosterStore.ROSTER_STATES_KEY)) .thenReturn(new OnDiskWritableSingletonState<>( - RosterStateId.NAME, WritableRosterStore.ROSTER_STATES_KEY, RosterState.PROTOBUF, virtualMap)); + RosterStateId.NAME, WritableRosterStore.ROSTER_STATES_KEY, virtualMap)); readableRosterStore = new ReadableRosterStoreImpl(writableStates); writableRosterStore = new WritableRosterStore(writableStates); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java index 3d18fa2a6f8c..7b49e78324de 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java @@ -52,8 +52,8 @@ void setUp() { PlatformState.PROTOBUF); when(writableStates.getSingleton(PLATFORM_STATE_KEY)) - .thenReturn(new OnDiskWritableSingletonState<>( - PlatformStateService.NAME, PLATFORM_STATE_KEY, PlatformState.PROTOBUF, virtualMap)); + .thenReturn( + new OnDiskWritableSingletonState<>(PlatformStateService.NAME, PLATFORM_STATE_KEY, virtualMap)); store = new WritablePlatformStateStore(writableStates); } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java index 62eb91dedcd2..0ecc658fac7f 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java @@ -8,6 +8,7 @@ import com.hedera.hapi.platform.state.SingletonType; import com.hedera.hapi.platform.state.VirtualMapKey; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.Codec; import com.hedera.pbj.runtime.OneOf; import com.hedera.pbj.runtime.ParseException; @@ -503,4 +504,14 @@ public static byte[] createVirtualMapKeyBytesForKV( return byteBuffer.array(); } + + public static VirtualMapValue getVirtualMapValue( + @NonNull final String serviceName, @NonNull final String stateKey, final V value) { + return new VirtualMapValue(new OneOf<>( + VirtualMapValue.ValueOneOfType.fromProtobufOrdinal(getValidatedStateId(serviceName, stateKey)), value)); + } + + public static VirtualMapValue getVirtualMapValueQueueState(final V value) { + return new VirtualMapValue(new OneOf<>(VirtualMapValue.ValueOneOfType.QUEUE_STATE, value)); + } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java index 46d7e506dfa3..1b84da45828e 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java @@ -4,6 +4,7 @@ import static com.swirlds.state.StateChangeListener.StateType.MAP; import static com.swirlds.state.StateChangeListener.StateType.QUEUE; import static com.swirlds.state.StateChangeListener.StateType.SINGLETON; +import static com.swirlds.state.merkle.disk.OnDiskQueueHelper.convertFromProto; import static java.util.Objects.requireNonNull; import com.hedera.pbj.runtime.Codec; @@ -581,11 +582,6 @@ static String extractStateKey(@NonNull final StateMetadata md) { static Codec extractKeyCodec(@NonNull final StateMetadata md) { return md.stateDefinition().keyCodec(); } - - @NonNull - static Codec extractValueCodec(@NonNull final StateMetadata md) { - return md.stateDefinition().valueCodec(); - } } /** @@ -606,22 +602,19 @@ public final class MerkleReadableStates extends MerkleStates { @Override @NonNull protected ReadableKVState createReadableKVState(@NonNull final StateMetadata md) { - return new OnDiskReadableKVState<>( - md.serviceName(), extractStateKey(md), extractKeyCodec(md), extractValueCodec(md), virtualMap); + return new OnDiskReadableKVState<>(md.serviceName(), extractStateKey(md), extractKeyCodec(md), virtualMap); } @Override @NonNull protected ReadableSingletonState createReadableSingletonState(@NonNull final StateMetadata md) { - return new OnDiskReadableSingletonState<>( - md.serviceName(), extractStateKey(md), extractValueCodec(md), virtualMap); + return new OnDiskReadableSingletonState<>(md.serviceName(), extractStateKey(md), virtualMap); } @NonNull @Override protected ReadableQueueState createReadableQueueState(@NonNull StateMetadata md) { - return new OnDiskReadableQueueState( - md.serviceName(), extractStateKey(md), extractValueCodec(md), virtualMap); + return new OnDiskReadableQueueState(md.serviceName(), extractStateKey(md), virtualMap); } } @@ -684,8 +677,8 @@ public WritableQueueState getQueue(@NonNull String stateKey) { @Override @NonNull protected WritableKVState createReadableKVState(@NonNull final StateMetadata md) { - final var state = new OnDiskWritableKVState<>( - md.serviceName(), extractStateKey(md), extractKeyCodec(md), extractValueCodec(md), virtualMap); + final var state = + new OnDiskWritableKVState<>(md.serviceName(), extractStateKey(md), extractKeyCodec(md), virtualMap); listeners.forEach(listener -> { if (listener.stateTypes().contains(MAP)) { registerKVListener(serviceName, state, listener); @@ -697,8 +690,7 @@ public WritableQueueState getQueue(@NonNull String stateKey) { @Override @NonNull protected WritableSingletonState createReadableSingletonState(@NonNull final StateMetadata md) { - final var state = new OnDiskWritableSingletonState<>( - md.serviceName(), extractStateKey(md), extractValueCodec(md), virtualMap); + final var state = new OnDiskWritableSingletonState<>(md.serviceName(), extractStateKey(md), virtualMap); listeners.forEach(listener -> { if (listener.stateTypes().contains(SINGLETON)) { registerSingletonListener(serviceName, state, listener); @@ -710,8 +702,7 @@ protected WritableSingletonState createReadableSingletonState(@NonNull final @NonNull @Override protected WritableQueueState createReadableQueueState(@NonNull final StateMetadata md) { - final var state = new OnDiskWritableQueueState<>( - md.serviceName(), extractStateKey(md), extractValueCodec(md), virtualMap); + final var state = new OnDiskWritableQueueState<>(md.serviceName(), extractStateKey(md), virtualMap); listeners.forEach(listener -> { if (listener.stateTypes().contains(QUEUE)) { registerQueueListener(serviceName, state, listener); @@ -860,12 +851,18 @@ public String getInfoJson() { final Bytes keyBytes = StateUtils.getVirtualMapKeyForSingleton(serviceName, stateKey); final VirtualLeafBytes leafBytes = recordAccessor.findLeafRecord(keyBytes); if (leafBytes != null) { - final QueueState queueState = new QueueState(leafBytes.valueBytes()); - final JSONObject queueJson = new JSONObject(); - queueJson.put("head", queueState.getHead()); - queueJson.put("tail", queueState.getTail()); - queueJson.put("path", leafBytes.path()); - queues.put(StateUtils.computeLabel(serviceName, stateKey), queueJson); + try { + final com.hedera.hapi.platform.state.QueueState queueStateProto = + com.hedera.hapi.platform.state.QueueState.PROTOBUF.parse(leafBytes.valueBytes()); + final QueueState queueState = convertFromProto(queueStateProto); + final JSONObject queueJson = new JSONObject(); + queueJson.put("head", queueState.getHead()); + queueJson.put("tail", queueState.getTail()); + queueJson.put("path", leafBytes.path()); + queues.put(StateUtils.computeLabel(serviceName, stateKey), queueJson); + } catch (ParseException e) { + throw new RuntimeException(e); + } } } }); diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java index c39aececfe6a..50c664faa9a1 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java @@ -2,13 +2,16 @@ package com.swirlds.state.merkle.disk; import static com.swirlds.state.merkle.StateUtils.computeLabel; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapValueQueueState; import static com.swirlds.state.merkle.logging.StateLogger.logQueueIterate; import static java.util.Objects.requireNonNull; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.Codec; +import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.state.merkle.StateUtils; import com.swirlds.state.merkle.queue.QueueState; -import com.swirlds.state.merkle.queue.QueueStateCodec; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ConcurrentModificationException; @@ -62,12 +65,6 @@ public final class OnDiskQueueHelper { @NonNull private final VirtualMap virtualMap; - /** - * The codec for the elements of the queue. - */ - @NonNull - private final Codec valueCodec; - /** * An empty iterator used as a placeholder when no elements are available. */ @@ -79,17 +76,12 @@ public final class OnDiskQueueHelper { * @param serviceName The name of the service that owns the queue's state. * @param stateKey The unique key for identifying the queue's state. * @param virtualMap The storage mechanism for the queue's data. - * @param valueCodec The codec for the elements of the queue. */ public OnDiskQueueHelper( - @NonNull final String serviceName, - @NonNull final String stateKey, - @NonNull final VirtualMap virtualMap, - @NonNull final Codec valueCodec) { + @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final VirtualMap virtualMap) { this.serviceName = requireNonNull(serviceName); this.stateKey = requireNonNull(stateKey); this.virtualMap = requireNonNull(virtualMap); - this.valueCodec = requireNonNull(valueCodec); } /** @@ -120,7 +112,9 @@ public Iterator iterateOnDataSource() { */ @NonNull public E getFromStore(final long index) { - final var value = virtualMap.get(StateUtils.getVirtualMapKeyForQueue(serviceName, stateKey, index), valueCodec); + final VirtualMapValue virtualMapValue = virtualMap.get( + StateUtils.getVirtualMapKeyForQueue(serviceName, stateKey, index), VirtualMapValue.PROTOBUF); + final E value = virtualMapValue != null ? virtualMapValue.value().as() : null; if (value == null) { throw new IllegalStateException("Can't find queue element at index " + index + " in the store"); } @@ -133,8 +127,11 @@ public E getFromStore(final long index) { * @return The current state of the queue. */ public QueueState getState() { - final QueueState state = virtualMap.get( - StateUtils.getVirtualMapKeyForSingleton(serviceName, stateKey), QueueStateCodec.INSTANCE); + final VirtualMapValue virtualMapValue = + virtualMap.get(getVirtualMapKeyForSingleton(serviceName, stateKey), VirtualMapValue.PROTOBUF); + final QueueState state = virtualMapValue != null + ? convertFromProto(virtualMapValue.value().as()) + : null; if (state == null) { return null; } @@ -148,7 +145,21 @@ public QueueState getState() { * @param state The new state to set for the queue. */ public void updateState(@NonNull final QueueState state) { - virtualMap.put(StateUtils.getVirtualMapKeyForSingleton(serviceName, stateKey), state, QueueStateCodec.INSTANCE); + final Bytes keyBytes = getVirtualMapKeyForSingleton(serviceName, stateKey); + + final VirtualMapValue virtualMapValue = getVirtualMapValueQueueState(convertToProto(state)); + virtualMap.put(keyBytes, virtualMapValue, VirtualMapValue.PROTOBUF); + } + + public static com.hedera.hapi.platform.state.QueueState convertToProto(QueueState state) { + return com.hedera.hapi.platform.state.QueueState.newBuilder() + .head(state.getHead()) + .tail(state.getTail()) + .build(); + } + + public static QueueState convertFromProto(com.hedera.hapi.platform.state.QueueState state) { + return new QueueState(state.head(), state.tail()); } /** diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java index 074b01b0a083..5d769f4d7ff7 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java @@ -8,6 +8,7 @@ import static com.swirlds.state.merkle.logging.StateLogger.logMapIterate; import static java.util.Objects.requireNonNull; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.Codec; import com.swirlds.state.merkle.StateUtils; import com.swirlds.state.spi.ReadableKVState; @@ -32,37 +33,34 @@ public final class OnDiskReadableKVState extends ReadableKVStateBase @NonNull private final Codec keyCodec; - @NonNull - private final Codec valueCodec; - /** * Create a new instance * - * @param serviceName the service name - * @param stateKey the state key - * @param keyCodec the codec for the key - * @param valueCodec the codec for the value - * @param virtualMap the backing merkle data structure to use + * @param serviceName the service name + * @param stateKey the state key + * @param keyCodec the codec for the key + * @param virtualMap the backing merkle data structure to use */ public OnDiskReadableKVState( @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final Codec keyCodec, - @NonNull final Codec valueCodec, @NonNull final VirtualMap virtualMap) { super(serviceName, stateKey); this.keyCodec = requireNonNull(keyCodec); - this.valueCodec = requireNonNull(valueCodec); this.virtualMap = requireNonNull(virtualMap); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override protected V readFromDataSource(@NonNull K key) { - final var value = virtualMap.get(getVirtualMapKeyForKv(serviceName, stateKey, key), valueCodec); + final VirtualMapValue virtualMapValue = + virtualMap.get(getVirtualMapKeyForKv(serviceName, stateKey, key), VirtualMapValue.PROTOBUF); // Log to transaction state log, what was read - logMapGet(computeLabel(serviceName, stateKey), key, value); - return value; + logMapGet(computeLabel(serviceName, stateKey), key, virtualMapValue); + return virtualMapValue != null ? virtualMapValue.value().as() : null; } /** {@inheritDoc} */ diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java index 03af79f63847..a29a0087be82 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java @@ -4,7 +4,6 @@ import static com.swirlds.state.merkle.StateUtils.computeLabel; import static com.swirlds.state.merkle.logging.StateLogger.logQueuePeek; -import com.hedera.pbj.runtime.Codec; import com.swirlds.state.merkle.queue.QueueState; import com.swirlds.state.spi.ReadableQueueState; import com.swirlds.state.spi.ReadableQueueStateBase; @@ -28,18 +27,14 @@ public class OnDiskReadableQueueState extends ReadableQueueStateBase { /** * Create a new instance * - * @param serviceName the service name - * @param stateKey the state key - * @param valueCodec the codec for the value - * @param virtualMap the backing merkle data structure to use + * @param serviceName the service name + * @param stateKey the state key + * @param virtualMap the backing merkle data structure to use */ public OnDiskReadableQueueState( - @NonNull final String serviceName, - @NonNull final String stateKey, - @NonNull final Codec valueCodec, - @NonNull final VirtualMap virtualMap) { + @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final VirtualMap virtualMap) { super(serviceName, stateKey); - this.onDiskQueueHelper = new OnDiskQueueHelper<>(serviceName, stateKey, virtualMap, valueCodec); + this.onDiskQueueHelper = new OnDiskQueueHelper<>(serviceName, stateKey, virtualMap); } @Nullable diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableSingletonState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableSingletonState.java index 154d865140ff..ddfceda12783 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableSingletonState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableSingletonState.java @@ -5,7 +5,6 @@ import static com.swirlds.state.merkle.logging.StateLogger.logSingletonRead; import static java.util.Objects.requireNonNull; -import com.hedera.pbj.runtime.Codec; import com.swirlds.state.spi.ReadableSingletonState; import com.swirlds.state.spi.ReadableSingletonStateBase; import com.swirlds.virtualmap.VirtualMap; @@ -23,25 +22,17 @@ public class OnDiskReadableSingletonState extends ReadableSingletonStateBase< @NonNull private final VirtualMap virtualMap; - @NonNull - private final Codec valueCodec; - /** * Create a new instance * * @param serviceName the service name * @param stateKey the state key - * @param valueCodec the codec for the value * @param virtualMap the backing merkle data structure to use */ public OnDiskReadableSingletonState( - @NonNull final String serviceName, - @NonNull final String stateKey, - @NonNull final Codec valueCodec, - @NonNull final VirtualMap virtualMap) { + @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final VirtualMap virtualMap) { super(serviceName, stateKey); this.virtualMap = requireNonNull(virtualMap); - this.valueCodec = requireNonNull(valueCodec); } /** @@ -49,7 +40,7 @@ public OnDiskReadableSingletonState( */ @Override protected T readFromDataSource() { - final var value = OnDiskSingletonHelper.getFromStore(serviceName, stateKey, virtualMap, valueCodec); + final T value = OnDiskSingletonHelper.getFromStore(serviceName, stateKey, virtualMap); // Log to transaction state log, what was read logSingletonRead(computeLabel(serviceName, stateKey), value); return value; diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java index 0bb5da81e4da..c684158455b1 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java @@ -1,8 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 package com.swirlds.state.merkle.disk; -import com.hedera.pbj.runtime.Codec; -import com.swirlds.state.merkle.StateUtils; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; + +import com.hedera.hapi.platform.state.VirtualMapValue; +import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; @@ -11,10 +13,8 @@ * *

This class was created to extract repetitive code from * {@code OnDiskWritableQueueState} and {@code OnDiskReadableQueueState}. - * - * @param the type of element stored in the on-disk singleton */ -public final class OnDiskSingletonHelper { +public final class OnDiskSingletonHelper { private OnDiskSingletonHelper() {} @@ -24,22 +24,41 @@ private OnDiskSingletonHelper() {} * @param serviceName the name of the service that owns the singleton's state * @param stateKey the unique key for identifying the singleton's state * @param virtualMap the storage mechanism for the singleton's data - * @param valueCodec the codec for the elements of the singleton * @param the type of element stored in the on-disk singleton * @return the singleton object */ public static T getFromStore( - @NonNull final String serviceName, - @NonNull final String stateKey, - @NonNull final VirtualMap virtualMap, - @NonNull final Codec valueCodec) { - final var key = StateUtils.getVirtualMapKeyForSingleton(serviceName, stateKey); - var value = virtualMap.get(key, valueCodec); - - if (value == null && virtualMap.containsKey(key)) { - value = valueCodec.getDefaultInstance(); + @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final VirtualMap virtualMap) { + + final Bytes key = getVirtualMapKeyForSingleton(serviceName, stateKey); + final VirtualMapValue virtualMapValue = virtualMap.get(key, VirtualMapValue.PROTOBUF); + + // It may be possible, but I doubt it, need to debug + if (virtualMapValue == null && virtualMap.containsKey(key)) { + // return valueCodec.getDefaultInstance(); + + // Need to check this scenario, but I doubt that it will work + // because of possible issue w/ default instance + return VirtualMapValue.PROTOBUF.getDefaultInstance().value().as(); + } + + if (virtualMapValue == null) { + return null; + } + + final var mapValue = virtualMapValue.value(); + // This is more possible scenario, but still need to debug + if (mapValue == null && virtualMap.containsKey(key)) { + // Need to check this scenario, but I doubt that it will work + // because of possible issue w/ default instance + return VirtualMapValue.PROTOBUF.getDefaultInstance().value().as(); + + // I think this is what's needed as we're returning original value, + // so we need to have a codec here + // return valueCodec.getDefaultInstance(); } - return value; + // Suppress NPE or do something with it? + return mapValue.as(); } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java index 4c29f1a1f582..a434533b2fea 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java @@ -3,6 +3,7 @@ import static com.swirlds.state.merkle.StateUtils.computeLabel; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForKv; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapValue; import static com.swirlds.state.merkle.logging.StateLogger.logMapGet; import static com.swirlds.state.merkle.logging.StateLogger.logMapGetSize; import static com.swirlds.state.merkle.logging.StateLogger.logMapIterate; @@ -10,6 +11,7 @@ import static com.swirlds.state.merkle.logging.StateLogger.logMapRemove; import static java.util.Objects.requireNonNull; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.Codec; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.state.merkle.StateUtils; @@ -35,37 +37,34 @@ public final class OnDiskWritableKVState extends WritableKVStateBase @NonNull private final Codec keyCodec; - @NonNull - private final Codec valueCodec; - /** * Create a new instance * - * @param serviceName the service name - * @param stateKey the state key - * @param keyCodec the codec for the key - * @param valueCodec the codec for the value - * @param virtualMap the backing merkle data structure to use + * @param serviceName the service name + * @param stateKey the state key + * @param keyCodec the codec for the key + * @param virtualMap the backing merkle data structure to use */ public OnDiskWritableKVState( @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final Codec keyCodec, - @NonNull final Codec valueCodec, @NonNull final VirtualMap virtualMap) { super(serviceName, stateKey); this.keyCodec = requireNonNull(keyCodec); - this.valueCodec = requireNonNull(valueCodec); this.virtualMap = requireNonNull(virtualMap); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override protected V readFromDataSource(@NonNull K key) { - final var value = virtualMap.get(getVirtualMapKeyForKv(serviceName, stateKey, key), valueCodec); + final VirtualMapValue virtualMapValue = + virtualMap.get(getVirtualMapKeyForKv(serviceName, stateKey, key), VirtualMapValue.PROTOBUF); // Log to transaction state log, what was read - logMapGet(computeLabel(serviceName, stateKey), key, value); - return value; + logMapGet(computeLabel(serviceName, stateKey), key, virtualMapValue); + return virtualMapValue != null ? virtualMapValue.value().as() : null; } /** {@inheritDoc} */ @@ -80,9 +79,12 @@ protected Iterator iterateFromDataSource() { /** {@inheritDoc} */ @Override protected void putIntoDataSource(@NonNull K key, @NonNull V value) { - final Bytes kb = keyCodec.toBytes(key); - assert kb != null; - virtualMap.put(getVirtualMapKeyForKv(serviceName, stateKey, key), value, valueCodec); + assert keyCodec.toBytes(key) != null; + + final Bytes keyBytes = getVirtualMapKeyForKv(serviceName, stateKey, key); + final VirtualMapValue virtualMapValue = getVirtualMapValue(serviceName, stateKey, value); + + virtualMap.put(keyBytes, virtualMapValue, VirtualMapValue.PROTOBUF); // Log to transaction state log, what was put logMapPut(computeLabel(serviceName, stateKey), key, value); } @@ -90,9 +92,12 @@ protected void putIntoDataSource(@NonNull K key, @NonNull V value) { /** {@inheritDoc} */ @Override protected void removeFromDataSource(@NonNull K key) { - final var removed = virtualMap.remove(getVirtualMapKeyForKv(serviceName, stateKey, key), valueCodec); + final VirtualMapValue virtualMapValue = + virtualMap.remove(getVirtualMapKeyForKv(serviceName, stateKey, key), VirtualMapValue.PROTOBUF); + final var removedValue = + virtualMapValue != null ? virtualMapValue.value().as() : null; // Log to transaction state log, what was removed - logMapRemove(computeLabel(serviceName, stateKey), key, removed); + logMapRemove(computeLabel(serviceName, stateKey), key, removedValue); } /** {@inheritDoc} */ diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java index f3133e0b4e28..068c79688853 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java @@ -2,12 +2,14 @@ package com.swirlds.state.merkle.disk; import static com.swirlds.state.merkle.StateUtils.computeLabel; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForQueue; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapValue; import static com.swirlds.state.merkle.logging.StateLogger.logQueueAdd; import static com.swirlds.state.merkle.logging.StateLogger.logQueueRemove; import static java.util.Objects.requireNonNull; -import com.hedera.pbj.runtime.Codec; -import com.swirlds.state.merkle.StateUtils; +import com.hedera.hapi.platform.state.VirtualMapValue; +import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.state.merkle.queue.QueueState; import com.swirlds.state.spi.WritableQueueStateBase; import com.swirlds.virtualmap.VirtualMap; @@ -26,29 +28,21 @@ public class OnDiskWritableQueueState extends WritableQueueStateBase { @NonNull private final VirtualMap virtualMap; - @NonNull - private final Codec valueCodec; - @NonNull private final OnDiskQueueHelper onDiskQueueHelper; /** * Create a new instance * - * @param serviceName the service name - * @param stateKey the state key - * @param valueCodec the codec for the value - * @param virtualMap the backing merkle data structure to use + * @param serviceName the service name + * @param stateKey the state key + * @param virtualMap the backing merkle data structure to use */ public OnDiskWritableQueueState( - @NonNull final String serviceName, - @NonNull final String stateKey, - @NonNull final Codec valueCodec, - @NonNull final VirtualMap virtualMap) { + @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final VirtualMap virtualMap) { super(serviceName, stateKey); this.virtualMap = requireNonNull(virtualMap); - this.valueCodec = requireNonNull(valueCodec); - this.onDiskQueueHelper = new OnDiskQueueHelper<>(serviceName, stateKey, virtualMap, valueCodec); + this.onDiskQueueHelper = new OnDiskQueueHelper<>(serviceName, stateKey, virtualMap); } /** {@inheritDoc} */ @@ -59,10 +53,11 @@ protected void addToDataSource(@NonNull E element) { // Adding to this Queue State first time - initialize QueueState. state = new QueueState(); } - virtualMap.put( - StateUtils.getVirtualMapKeyForQueue(serviceName, stateKey, state.getTailAndIncrement()), - element, - valueCodec); + + final Bytes keyBytes = getVirtualMapKeyForQueue(serviceName, stateKey, state.getTailAndIncrement()); + final VirtualMapValue virtualMapValue = getVirtualMapValue(serviceName, stateKey, element); + + virtualMap.put(keyBytes, virtualMapValue, VirtualMapValue.PROTOBUF); onDiskQueueHelper.updateState(state); // Log to transaction state log, what was added logQueueAdd(computeLabel(serviceName, stateKey), element); @@ -73,10 +68,12 @@ protected void addToDataSource(@NonNull E element) { protected void removeFromDataSource() { final QueueState state = requireNonNull(onDiskQueueHelper.getState()); if (!state.isEmpty()) { - final var removedValue = virtualMap.remove( - StateUtils.getVirtualMapKeyForQueue(serviceName, stateKey, state.getHeadAndIncrement()), - valueCodec); + final VirtualMapValue virtualMapValue = virtualMap.remove( + getVirtualMapKeyForQueue(serviceName, stateKey, state.getHeadAndIncrement()), + VirtualMapValue.PROTOBUF); onDiskQueueHelper.updateState(state); + final var removedValue = + virtualMapValue != null ? virtualMapValue.value().as() : null; // Log to transaction state log, what was removed logQueueRemove(computeLabel(serviceName, stateKey), removedValue); } else { diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableSingletonState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableSingletonState.java index 7d53d46c37ed..dffe3ddbad8b 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableSingletonState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableSingletonState.java @@ -2,13 +2,15 @@ package com.swirlds.state.merkle.disk; import static com.swirlds.state.merkle.StateUtils.computeLabel; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapValue; import static com.swirlds.state.merkle.logging.StateLogger.logSingletonRead; import static com.swirlds.state.merkle.logging.StateLogger.logSingletonRemove; import static com.swirlds.state.merkle.logging.StateLogger.logSingletonWrite; import static java.util.Objects.requireNonNull; -import com.hedera.pbj.runtime.Codec; -import com.swirlds.state.merkle.StateUtils; +import com.hedera.hapi.platform.state.VirtualMapValue; +import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.state.spi.WritableSingletonState; import com.swirlds.state.spi.WritableSingletonStateBase; import com.swirlds.virtualmap.VirtualMap; @@ -26,31 +28,23 @@ public class OnDiskWritableSingletonState extends WritableSingletonStateBase< @NonNull private final VirtualMap virtualMap; - @NonNull - private final Codec valueCodec; - /** * Create a new instance * * @param serviceName the service name * @param stateKey the state key - * @param valueCodec the codec for the value * @param virtualMap the backing merkle data structure to use */ public OnDiskWritableSingletonState( - @NonNull final String serviceName, - @NonNull final String stateKey, - @NonNull final Codec valueCodec, - @NonNull final VirtualMap virtualMap) { + @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final VirtualMap virtualMap) { super(serviceName, stateKey); - this.valueCodec = requireNonNull(valueCodec); this.virtualMap = requireNonNull(virtualMap); } /** {@inheritDoc} */ @Override protected T readFromDataSource() { - final var value = OnDiskSingletonHelper.getFromStore(serviceName, stateKey, virtualMap, valueCodec); + final T value = OnDiskSingletonHelper.getFromStore(serviceName, stateKey, virtualMap); // Log to transaction state log, what was read logSingletonRead(computeLabel(serviceName, stateKey), value); return value; @@ -59,7 +53,10 @@ protected T readFromDataSource() { /** {@inheritDoc} */ @Override protected void putIntoDataSource(@NonNull T value) { - virtualMap.put(StateUtils.getVirtualMapKeyForSingleton(serviceName, stateKey), value, valueCodec); + final Bytes keyBytes = getVirtualMapKeyForSingleton(serviceName, stateKey); + final VirtualMapValue virtualMapValue = getVirtualMapValue(serviceName, stateKey, value); + + virtualMap.put(keyBytes, virtualMapValue, VirtualMapValue.PROTOBUF); // Log to transaction state log, what was put logSingletonWrite(computeLabel(serviceName, stateKey), value); } @@ -67,9 +64,11 @@ protected void putIntoDataSource(@NonNull T value) { /** {@inheritDoc} */ @Override protected void removeFromDataSource() { - final var removed = - virtualMap.remove(StateUtils.getVirtualMapKeyForSingleton(serviceName, stateKey), valueCodec); + final VirtualMapValue virtualMapValue = + virtualMap.remove(getVirtualMapKeyForSingleton(serviceName, stateKey), VirtualMapValue.PROTOBUF); + final var removedValue = + virtualMapValue != null ? virtualMapValue.value().as() : null; // Log to transaction state log, what was removed - logSingletonRemove(computeLabel(serviceName, stateKey), removed); + logSingletonRemove(computeLabel(serviceName, stateKey), removedValue); } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/module-info.java b/platform-sdk/swirlds-state-impl/src/main/java/module-info.java index 3b8ce9455db9..13ff799be231 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/module-info.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/module-info.java @@ -10,6 +10,7 @@ opens com.swirlds.state.merkle.disk to com.hedera.node.app; + requires transitive com.hedera.node.hapi; requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.base; requires transitive com.swirlds.common; @@ -20,7 +21,6 @@ requires transitive com.swirlds.virtualmap; requires transitive org.hiero.base.crypto; requires transitive org.hiero.base.utility; - requires com.hedera.node.hapi; requires com.swirlds.fcqueue; requires com.swirlds.logging; requires com.swirlds.merkledb; diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java index 8a9b59dfe12a..50d877899c2a 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java @@ -39,40 +39,39 @@ final class ConstructorTest { @Test @DisplayName("You must specify the serviceName") void nullServiceNameThrows() { - assertThatThrownBy(() -> new OnDiskReadableKVState<>( - null, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, fruitVirtualMap)) + assertThatThrownBy(() -> new OnDiskReadableKVState<>(null, FRUIT_STATE_KEY, STRING_CODEC, fruitVirtualMap)) .isInstanceOf(NullPointerException.class); } @Test @DisplayName("You must specify the stateKey") void nullStateKeyThrows() { - assertThatThrownBy(() -> new OnDiskReadableKVState<>( - FRUIT_SERVICE_NAME, null, STRING_CODEC, STRING_CODEC, fruitVirtualMap)) + assertThatThrownBy( + () -> new OnDiskReadableKVState<>(FRUIT_SERVICE_NAME, null, STRING_CODEC, fruitVirtualMap)) .isInstanceOf(NullPointerException.class); } @Test @DisplayName("You must specify the virtual map") void nullVirtualMapThrows() { - assertThatThrownBy(() -> new OnDiskReadableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, null)) + assertThatThrownBy( + () -> new OnDiskReadableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, null)) .isInstanceOf(NullPointerException.class); } @Test @DisplayName("The serviceName matches that supplied") void serviceName() { - final var state = new OnDiskReadableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, fruitVirtualMap); + final var state = + new OnDiskReadableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, fruitVirtualMap); assertThat(state.getServiceName()).isEqualTo(FRUIT_SERVICE_NAME); } @Test @DisplayName("The stateKey matches that supplied") void stateKey() { - final var state = new OnDiskReadableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, fruitVirtualMap); + final var state = + new OnDiskReadableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, fruitVirtualMap); assertThat(state.getStateKey()).isEqualTo(FRUIT_STATE_KEY); } } @@ -85,7 +84,7 @@ final class QueryTest { @BeforeEach void setUp() { state = new OnDiskReadableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC, fruitVirtualMap); + FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, fruitVirtualMap); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, A_KEY, APPLE); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, B_KEY, BANANA); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, C_KEY, CHERRY); @@ -107,8 +106,8 @@ void get() { @Test @DisplayName("The method warm() calls the appropriate method on the virtual map") void warm(@Mock VirtualMap virtualMapMock) { - final var state = new OnDiskReadableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC, virtualMapMock); + final var state = + new OnDiskReadableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, virtualMapMock); state.warm(A_KEY); verify(virtualMapMock).warm(StateUtils.getVirtualMapKeyForKv(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, A_KEY)); } diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java index 0df12721549a..1cfff6708e93 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java @@ -34,8 +34,7 @@ void setUp() { @DisplayName("You must specify the serviceName") void nullServiceNameThrows() { //noinspection DataFlowIssue - assertThatThrownBy(() -> new OnDiskWritableKVState<>( - null, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, fruitVirtualMap)) + assertThatThrownBy(() -> new OnDiskWritableKVState<>(null, FRUIT_STATE_KEY, STRING_CODEC, fruitVirtualMap)) .isInstanceOf(NullPointerException.class); } @@ -43,8 +42,8 @@ void nullServiceNameThrows() { @DisplayName("You must specify the stateKey") void nullStateKeyThrows() { //noinspection DataFlowIssue - assertThatThrownBy(() -> new OnDiskWritableKVState<>( - FRUIT_SERVICE_NAME, null, STRING_CODEC, STRING_CODEC, fruitVirtualMap)) + assertThatThrownBy( + () -> new OnDiskWritableKVState<>(FRUIT_SERVICE_NAME, null, STRING_CODEC, fruitVirtualMap)) .isInstanceOf(NullPointerException.class); } @@ -52,24 +51,24 @@ void nullStateKeyThrows() { @DisplayName("You must specify the virtual map") void nullMerkleMapThrows() { //noinspection DataFlowIssue - assertThatThrownBy(() -> new OnDiskWritableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, null)) + assertThatThrownBy( + () -> new OnDiskWritableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, null)) .isInstanceOf(NullPointerException.class); } @Test @DisplayName("The serviceName matches that supplied by the metadata") void serviceName() { - final var state = new OnDiskWritableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, fruitVirtualMap); + final var state = + new OnDiskWritableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, fruitVirtualMap); assertThat(state.getServiceName()).isEqualTo(FRUIT_SERVICE_NAME); } @Test @DisplayName("The stateKey matches that supplied by the metadata") void stateKey() { - final var state = new OnDiskWritableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC, fruitVirtualMap); + final var state = + new OnDiskWritableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, STRING_CODEC, fruitVirtualMap); assertThat(state.getStateKey()).isEqualTo(FRUIT_STATE_KEY); } @@ -97,7 +96,7 @@ final class QueryTest { void setUp() { setupFruitVirtualMap(); state = new OnDiskWritableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC, fruitVirtualMap); + FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, fruitVirtualMap); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, A_KEY, APPLE); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, B_KEY, BANANA); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, C_KEY, CHERRY); @@ -145,7 +144,7 @@ final class MutationTest { void setUp() { setupFruitVirtualMap(); state = new OnDiskWritableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC, fruitVirtualMap); + FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, fruitVirtualMap); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, A_KEY, APPLE); add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, B_KEY, BANANA); } @@ -268,7 +267,7 @@ void smorgasbord() throws ParseException { fruitVirtualMap = fruitVirtualMap.copy(); oldVirtualMap.release(); state = new OnDiskWritableKVState<>( - FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC, fruitVirtualMap); + FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, fruitVirtualMap); assertThat(state.get(A_KEY)).isEqualTo(APPLE); state.remove(B_KEY); assertThat(state.get(C_KEY)).isEqualTo(CHERRY); From 9f9216c757041ff3bec4d4ffc49cd3ada05e958d Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Thu, 24 Jul 2025 18:22:07 +0300 Subject: [PATCH 2/9] address review comments Signed-off-by: Nikita Lebedev --- .../platform/state/virtual_map_state.proto | 50 ++++-- .../swirlds/state/merkle/MerkleStateRoot.java | 9 +- .../com/swirlds/state/merkle/StateUtils.java | 21 ++- .../swirlds/state/merkle/VirtualMapState.java | 24 +-- .../state/merkle/disk/OnDiskQueueHelper.java | 35 ++--- .../merkle/disk/OnDiskReadableKVState.java | 5 +- .../merkle/disk/OnDiskReadableQueueState.java | 4 +- .../merkle/disk/OnDiskSingletonHelper.java | 30 +--- .../merkle/disk/OnDiskWritableKVState.java | 5 +- .../merkle/disk/OnDiskWritableQueueState.java | 25 ++- .../state/merkle/queue/QueueState.java | 148 ------------------ .../state/merkle/queue/QueueStateCodec.java | 91 ----------- 12 files changed, 116 insertions(+), 331 deletions(-) delete mode 100644 platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueState.java delete mode 100644 platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueStateCodec.java diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto index 918281a93b05..a6cd45bc1fce 100644 --- a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto +++ b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto @@ -1,12 +1,3 @@ -/** - * # VirtualMapKey - * Messages that define the type of keys used in the Virtual Map State. - * - * The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - * "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this - * document are to be interpreted as described in [RFC2119](https://www.ietf.org/rfc/rfc2119) - * and clarified in [RFC8174](https://www.ietf.org/rfc/rfc8174). - */ syntax = "proto3"; package com.hedera.hapi.platform.state; @@ -54,18 +45,39 @@ import "services/state/tss/tss_encryption_keys.proto"; import "services/state/tss/tss_message_map_key.proto"; import "services/state/tss/tss_vote_map_key.proto"; -// TODO: docs +/** + * This message defines the Virtual Leaf. + */ message VirtualLeaf { + /** + * Virtual Key.
+ * The key used to identify and locate this virtual map entry. + */ VirtualMapKey key = 2; + /** + * Virtual Value.
+ * The actual data content stored for this virtual map entry. + */ VirtualMapValue value = 3; } +/** + * This message defines the queue state used in the Virtual Map State. + */ message QueueState { - int64 head = 1; + /** + * A head of a queue.
+ * Points to the index of the front of a queue. + */ + uint64 head = 1; - int64 tail = 2; + /** + * A tail of a queue.
+ * Points to the index where of the back of a queue. + */ + uint64 tail = 2; } @@ -285,7 +297,12 @@ message VirtualMapKey { } } -// TODO: add docs +/** + * This message defines the type of values used in the Virtual Map State. + * Note that `_I_` plays a role of a reliable separator between the service name + * and the state name, which is will be present in all types of code generation results as just underscores + * may be deleted and create unexpected collisions. + */ message VirtualMapValue { oneof value{ /** @@ -537,7 +554,6 @@ message VirtualMapValue { /** * Metadata of the round receipts queue. */ - // TODO: double check proto.TransactionReceiptEntries RecordCacheTransactionReceiptQueue = 126; /** @@ -590,8 +606,10 @@ message VirtualMapValue { */ proto.ProtoBytes FileService_I_UPGRADE_DATA_159 = 10010; - //// - QueueState queue_state = 11111; + /** + * Metadata of the queue state. + */ + QueueState queue_state = 11111; // pick the number } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java index 6423cd0e15cf..fdf0afe1702a 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java @@ -9,11 +9,14 @@ import static com.swirlds.state.lifecycle.StateMetadata.computeLabel; import static com.swirlds.state.merkle.StateUtils.createVirtualMapKeyBytesForKV; import static com.swirlds.state.merkle.StateUtils.decomposeLabel; +import static com.swirlds.state.merkle.StateUtils.getQueueStateVirtualMapValue; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForQueue; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; import static com.swirlds.state.merkle.VirtualMapState.VM_LABEL; import static java.util.Objects.requireNonNull; +import com.hedera.hapi.platform.state.QueueState; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.base.time.Time; import com.swirlds.base.utility.Pair; @@ -45,8 +48,6 @@ import com.swirlds.state.merkle.queue.BackedReadableQueueState; import com.swirlds.state.merkle.queue.BackedWritableQueueState; import com.swirlds.state.merkle.queue.QueueNode; -import com.swirlds.state.merkle.queue.QueueState; -import com.swirlds.state.merkle.queue.QueueStateCodec; import com.swirlds.state.merkle.singleton.BackedReadableSingletonState; import com.swirlds.state.merkle.singleton.BackedWritableSingletonState; import com.swirlds.state.merkle.singleton.SingletonNode; @@ -1158,8 +1159,8 @@ private void migrateQueueStates( .get() .put( getVirtualMapKeyForSingleton(serviceName, stateKey), - queueState, - QueueStateCodec.INSTANCE); + getQueueStateVirtualMapValue(queueState), + VirtualMapValue.PROTOBUF); long migrationTimeMs = System.currentTimeMillis() - migrationStartTime; logger.info( diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java index 0ecc658fac7f..2965eed2d704 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java @@ -6,6 +6,7 @@ import static com.hedera.pbj.runtime.ProtoParserTools.TAG_FIELD_OFFSET; import static com.hedera.pbj.runtime.ProtoWriterTools.sizeOfVarInt32; +import com.hedera.hapi.platform.state.QueueState; import com.hedera.hapi.platform.state.SingletonType; import com.hedera.hapi.platform.state.VirtualMapKey; import com.hedera.hapi.platform.state.VirtualMapValue; @@ -505,13 +506,29 @@ public static byte[] createVirtualMapKeyBytesForKV( return byteBuffer.array(); } + /** + * Creates an instance of {@link VirtualMapValue} which is stored in a {@link com.swirlds.virtualmap.VirtualMap}. + * + * @param the type of the value + * @param serviceName the service name + * @param stateKey the state key + * @param value the value object + * @return a {@link VirtualMapValue} for a {@link com.swirlds.virtualmap.VirtualMap} + * @throws IllegalArgumentException if the derived state ID is not within the range [0..65535] + */ public static VirtualMapValue getVirtualMapValue( @NonNull final String serviceName, @NonNull final String stateKey, final V value) { return new VirtualMapValue(new OneOf<>( VirtualMapValue.ValueOneOfType.fromProtobufOrdinal(getValidatedStateId(serviceName, stateKey)), value)); } - public static VirtualMapValue getVirtualMapValueQueueState(final V value) { - return new VirtualMapValue(new OneOf<>(VirtualMapValue.ValueOneOfType.QUEUE_STATE, value)); + /** + * Creates an instance of {@link VirtualMapValue} for a {@link com.hedera.hapi.platform.state.QueueState} which is stored in a {@link com.swirlds.virtualmap.VirtualMap}. + * + * @param queueState the value object + * @return a {@link VirtualMapValue} for {@link com.hedera.hapi.platform.state.QueueState} in a {@link com.swirlds.virtualmap.VirtualMap} + */ + public static VirtualMapValue getQueueStateVirtualMapValue(@NonNull final QueueState queueState) { + return new VirtualMapValue(new OneOf<>(VirtualMapValue.ValueOneOfType.QUEUE_STATE, queueState)); } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java index 1b84da45828e..81d669d242b5 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java @@ -4,9 +4,10 @@ import static com.swirlds.state.StateChangeListener.StateType.MAP; import static com.swirlds.state.StateChangeListener.StateType.QUEUE; import static com.swirlds.state.StateChangeListener.StateType.SINGLETON; -import static com.swirlds.state.merkle.disk.OnDiskQueueHelper.convertFromProto; import static java.util.Objects.requireNonNull; +import com.hedera.hapi.platform.state.QueueState; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.Codec; import com.hedera.pbj.runtime.ParseException; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -31,7 +32,6 @@ import com.swirlds.state.merkle.disk.OnDiskWritableKVState; import com.swirlds.state.merkle.disk.OnDiskWritableQueueState; import com.swirlds.state.merkle.disk.OnDiskWritableSingletonState; -import com.swirlds.state.merkle.queue.QueueState; import com.swirlds.state.spi.CommittableWritableStates; import com.swirlds.state.spi.EmptyReadableStates; import com.swirlds.state.spi.KVChangeListener; @@ -839,8 +839,14 @@ public String getInfoJson() { singletonJson.put("hash", hash); singletonJson.put("path", leafBytes.path()); try { - singletonJson.put( - "value", stateDefinition.valueCodec().parse(leafBytes.valueBytes())); + final VirtualMapValue virtualMapValue = + VirtualMapValue.PROTOBUF.parse(leafBytes.valueBytes()); + final var typedSingletonValue = stateDefinition + .valueCodec() + .getDefaultInstance() + .getClass() + .cast(virtualMapValue.value().as()); + singletonJson.put("value", typedSingletonValue); } catch (ParseException e) { singletonJson.put("value", "ParseException: " + e.getMessage()); } @@ -852,12 +858,12 @@ public String getInfoJson() { final VirtualLeafBytes leafBytes = recordAccessor.findLeafRecord(keyBytes); if (leafBytes != null) { try { - final com.hedera.hapi.platform.state.QueueState queueStateProto = - com.hedera.hapi.platform.state.QueueState.PROTOBUF.parse(leafBytes.valueBytes()); - final QueueState queueState = convertFromProto(queueStateProto); + final VirtualMapValue virtualMapValue = + VirtualMapValue.PROTOBUF.parse(leafBytes.valueBytes()); + final QueueState queueState = virtualMapValue.queueState(); final JSONObject queueJson = new JSONObject(); - queueJson.put("head", queueState.getHead()); - queueJson.put("tail", queueState.getTail()); + queueJson.put("head", queueState.head()); + queueJson.put("tail", queueState.tail()); queueJson.put("path", leafBytes.path()); queues.put(StateUtils.computeLabel(serviceName, stateKey), queueJson); } catch (ParseException e) { diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java index 50c664faa9a1..1287063fcf61 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java @@ -2,16 +2,16 @@ package com.swirlds.state.merkle.disk; import static com.swirlds.state.merkle.StateUtils.computeLabel; +import static com.swirlds.state.merkle.StateUtils.getQueueStateVirtualMapValue; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; -import static com.swirlds.state.merkle.StateUtils.getVirtualMapValueQueueState; import static com.swirlds.state.merkle.logging.StateLogger.logQueueIterate; import static java.util.Objects.requireNonNull; +import com.hedera.hapi.platform.state.QueueState; import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.Codec; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.state.merkle.StateUtils; -import com.swirlds.state.merkle.queue.QueueState; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ConcurrentModificationException; @@ -95,9 +95,9 @@ public Iterator iterateOnDataSource() { if (state == null) { return EMPTY_ITERATOR; } else { - final QueueIterator it = new QueueIterator(state.getHead(), state.getTail()); + final QueueIterator it = new QueueIterator(state.head(), state.tail()); // Log to transaction state log, what was iterated - logQueueIterate(computeLabel(serviceName, stateKey), state.getTail() - state.getHead(), it); + logQueueIterate(computeLabel(serviceName, stateKey), state.tail() - state.head(), it); it.reset(); return it; } @@ -129,14 +129,14 @@ public E getFromStore(final long index) { public QueueState getState() { final VirtualMapValue virtualMapValue = virtualMap.get(getVirtualMapKeyForSingleton(serviceName, stateKey), VirtualMapValue.PROTOBUF); - final QueueState state = virtualMapValue != null - ? convertFromProto(virtualMapValue.value().as()) - : null; + final QueueState state = + virtualMapValue != null ? virtualMapValue.value().as() : null; if (state == null) { return null; } + // TODO: check it this can be removed now // FUTURE WORK: optimize performance here, see https://github.com/hiero-ledger/hiero-consensus-node/issues/19670 - return new QueueState(state.getHead(), state.getTail()); + return state; // new QueueState(state.head(), state.tail()); } /** @@ -147,19 +147,18 @@ public QueueState getState() { public void updateState(@NonNull final QueueState state) { final Bytes keyBytes = getVirtualMapKeyForSingleton(serviceName, stateKey); - final VirtualMapValue virtualMapValue = getVirtualMapValueQueueState(convertToProto(state)); + final VirtualMapValue virtualMapValue = getQueueStateVirtualMapValue(state); virtualMap.put(keyBytes, virtualMapValue, VirtualMapValue.PROTOBUF); } - public static com.hedera.hapi.platform.state.QueueState convertToProto(QueueState state) { - return com.hedera.hapi.platform.state.QueueState.newBuilder() - .head(state.getHead()) - .tail(state.getTail()) - .build(); - } - - public static QueueState convertFromProto(com.hedera.hapi.platform.state.QueueState state) { - return new QueueState(state.head(), state.tail()); + /** + * Checks if a queue is empty. + * + * @param state the queue state to check + * @return {@code true} if the queue is empty, {@code false} otherwise + */ + public static boolean isEmpty(@NonNull final QueueState state) { + return state.head() == state.tail(); } /** diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java index 5d769f4d7ff7..198b568c1815 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableKVState.java @@ -58,9 +58,10 @@ public OnDiskReadableKVState( protected V readFromDataSource(@NonNull K key) { final VirtualMapValue virtualMapValue = virtualMap.get(getVirtualMapKeyForKv(serviceName, stateKey, key), VirtualMapValue.PROTOBUF); + final V value = virtualMapValue != null ? virtualMapValue.value().as() : null; // Log to transaction state log, what was read - logMapGet(computeLabel(serviceName, stateKey), key, virtualMapValue); - return virtualMapValue != null ? virtualMapValue.value().as() : null; + logMapGet(computeLabel(serviceName, stateKey), key, value); + return value; } /** {@inheritDoc} */ diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java index a29a0087be82..524548096c59 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskReadableQueueState.java @@ -4,7 +4,7 @@ import static com.swirlds.state.merkle.StateUtils.computeLabel; import static com.swirlds.state.merkle.logging.StateLogger.logQueuePeek; -import com.swirlds.state.merkle.queue.QueueState; +import com.hedera.hapi.platform.state.QueueState; import com.swirlds.state.spi.ReadableQueueState; import com.swirlds.state.spi.ReadableQueueStateBase; import com.swirlds.virtualmap.VirtualMap; @@ -42,7 +42,7 @@ public OnDiskReadableQueueState( protected E peekOnDataSource() { final QueueState state = onDiskQueueHelper.getState(); Objects.requireNonNull(state); - final E value = state.isEmpty() ? null : onDiskQueueHelper.getFromStore(state.getHead()); + final E value = OnDiskQueueHelper.isEmpty(state) ? null : onDiskQueueHelper.getFromStore(state.head()); // Log to transaction state log, what was peeked logQueuePeek(computeLabel(serviceName, stateKey), value); return value; diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java index c684158455b1..1a7eb37c6ae8 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskSingletonHelper.java @@ -29,36 +29,8 @@ private OnDiskSingletonHelper() {} */ public static T getFromStore( @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final VirtualMap virtualMap) { - final Bytes key = getVirtualMapKeyForSingleton(serviceName, stateKey); final VirtualMapValue virtualMapValue = virtualMap.get(key, VirtualMapValue.PROTOBUF); - - // It may be possible, but I doubt it, need to debug - if (virtualMapValue == null && virtualMap.containsKey(key)) { - // return valueCodec.getDefaultInstance(); - - // Need to check this scenario, but I doubt that it will work - // because of possible issue w/ default instance - return VirtualMapValue.PROTOBUF.getDefaultInstance().value().as(); - } - - if (virtualMapValue == null) { - return null; - } - - final var mapValue = virtualMapValue.value(); - // This is more possible scenario, but still need to debug - if (mapValue == null && virtualMap.containsKey(key)) { - // Need to check this scenario, but I doubt that it will work - // because of possible issue w/ default instance - return VirtualMapValue.PROTOBUF.getDefaultInstance().value().as(); - - // I think this is what's needed as we're returning original value, - // so we need to have a codec here - // return valueCodec.getDefaultInstance(); - } - - // Suppress NPE or do something with it? - return mapValue.as(); + return virtualMapValue != null ? virtualMapValue.value().as() : null; } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java index a434533b2fea..02f283302838 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableKVState.java @@ -62,9 +62,10 @@ public OnDiskWritableKVState( protected V readFromDataSource(@NonNull K key) { final VirtualMapValue virtualMapValue = virtualMap.get(getVirtualMapKeyForKv(serviceName, stateKey, key), VirtualMapValue.PROTOBUF); + final V value = virtualMapValue != null ? virtualMapValue.value().as() : null; // Log to transaction state log, what was read - logMapGet(computeLabel(serviceName, stateKey), key, virtualMapValue); - return virtualMapValue != null ? virtualMapValue.value().as() : null; + logMapGet(computeLabel(serviceName, stateKey), key, value); + return value; } /** {@inheritDoc} */ diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java index 068c79688853..8c009f61f05e 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java @@ -8,9 +8,9 @@ import static com.swirlds.state.merkle.logging.StateLogger.logQueueRemove; import static java.util.Objects.requireNonNull; +import com.hedera.hapi.platform.state.QueueState; import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.state.merkle.queue.QueueState; import com.swirlds.state.spi.WritableQueueStateBase; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; @@ -51,14 +51,19 @@ protected void addToDataSource(@NonNull E element) { QueueState state = onDiskQueueHelper.getState(); if (state == null) { // Adding to this Queue State first time - initialize QueueState. - state = new QueueState(); + state = QueueState.newBuilder().head(1).tail(1).build(); } - final Bytes keyBytes = getVirtualMapKeyForQueue(serviceName, stateKey, state.getTailAndIncrement()); + final Bytes keyBytes = getVirtualMapKeyForQueue(serviceName, stateKey, state.tail()); final VirtualMapValue virtualMapValue = getVirtualMapValue(serviceName, stateKey, element); virtualMap.put(keyBytes, virtualMapValue, VirtualMapValue.PROTOBUF); - onDiskQueueHelper.updateState(state); + + // increment tail and update state + final QueueState stateIncrementedTail = + state.copyBuilder().tail(state.tail() + 1).build(); + onDiskQueueHelper.updateState(stateIncrementedTail); + // Log to transaction state log, what was added logQueueAdd(computeLabel(serviceName, stateKey), element); } @@ -67,11 +72,15 @@ protected void addToDataSource(@NonNull E element) { @Override protected void removeFromDataSource() { final QueueState state = requireNonNull(onDiskQueueHelper.getState()); - if (!state.isEmpty()) { + if (!OnDiskQueueHelper.isEmpty(state)) { final VirtualMapValue virtualMapValue = virtualMap.remove( - getVirtualMapKeyForQueue(serviceName, stateKey, state.getHeadAndIncrement()), - VirtualMapValue.PROTOBUF); - onDiskQueueHelper.updateState(state); + getVirtualMapKeyForQueue(serviceName, stateKey, state.head()), VirtualMapValue.PROTOBUF); + + // increment head and update state + final QueueState stateIncrementedHead = + state.copyBuilder().head(state.head() + 1).build(); + onDiskQueueHelper.updateState(stateIncrementedHead); + final var removedValue = virtualMapValue != null ? virtualMapValue.value().as() : null; // Log to transaction state log, what was removed diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueState.java deleted file mode 100644 index 0785a276abfc..000000000000 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueState.java +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package com.swirlds.state.merkle.queue; - -import com.hedera.pbj.runtime.io.ReadableSequentialData; -import com.hedera.pbj.runtime.io.WritableSequentialData; -import com.hedera.pbj.runtime.io.buffer.Bytes; -import edu.umd.cs.findbugs.annotations.NonNull; - -/** - * Represents the state of a queue, tracking its head and tail indices. - * - *

Why is it needed?

- * This class encapsulates the metadata required for managing a queue, such as its head and tail - * indices. It provides utilities for serialization, size computation, and determining if the queue - * is empty. By abstracting this information, the queue's state can be stored, retrieved, and updated - * effectively, making it integral to on-disk queue implementations. - * - *

What does it do?

- * This class: - *
    - *
  • Tracks the head (next index to retrieve) and tail (next index to add) of the queue.
  • - *
  • Determines queue size and whether it is empty.
  • - *
  • Handles serialization and deserialization of the queue state for persistence.
  • - *
- * - *

Where is it used?

- * This class is used in queue implementations that rely on disk-backed storage, such as in - * conjunction with the {@link com.swirlds.state.merkle.disk.OnDiskQueueHelper}, to persist the state - * of the queue and manage its metadata. - */ -public class QueueState { - - /** - * Queue head index. The index at which the next element is retrieved from the queue. - * If {@code head == tail}, the queue is empty. - */ - private long head = 1; - - /** - * Queue tail index. The index at which the next element is added to the queue. - * The size of the queue is calculated as {@code tail - head}. - */ - private long tail = 1; - - /** - * Creates a new queue state with default head and tail indices. - */ - public QueueState() {} - - /** - * Create a new {@link QueueState} from the given bytes. - * - * @param bytes The bytes to read. Cannot be null. - */ - public QueueState(@NonNull final Bytes bytes) { - this(bytes.toReadableSequentialData()); - } - - /** - * Creates a queue state by reading its metadata from the given input stream. - * - * @param in the input from which the head and tail indices are read - */ - public QueueState(final ReadableSequentialData in) { - this.head = in.readLong(); - this.tail = in.readLong(); - } - - /** - * Creates a queue state with the specified head and tail indices. - * - * @param head the initial head index - * @param tail the initial tail index - */ - public QueueState(long head, long tail) { - this.head = head; - this.tail = tail; - } - - /** - * Writes the state (head and tail indices) to the given output stream. - * - * @param out the output to which the state is written - */ - public void writeTo(final WritableSequentialData out) { - out.writeLong(head); - out.writeLong(tail); - } - - /** - * Returns the size of the serialized state in bytes. - * - * @return the size in bytes required to serialize the queue state (16 bytes) - */ - public int getSizeInBytes() { - return 2 * Long.BYTES; - } - - /** - * Gets the head index. - * - * @return the current head index - */ - public long getHead() { - return head; - } - - /** - * Gets the head index and increments it by one. This operation is not thread-safe. - * - * @return the head index before incrementing - */ - public long getHeadAndIncrement() { - return head++; - } - - /** - * Gets the tail index. - * - * @return the current tail index - */ - public long getTail() { - return tail; - } - - /** - * Gets the tail index and increments it by one. This operation is not thread-safe. - * - * @return the tail index before incrementing - */ - public long getTailAndIncrement() { - return tail++; - } - - /** - * Determines if the queue represented by this state is empty. - * - * @return {@code true} if the queue is empty, otherwise {@code false} - */ - public boolean isEmpty() { - return head == tail; - } - - @Override - public String toString() { - return "QueueState{" + "head=" + head + ", tail=" + tail + '}'; - } -} diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueStateCodec.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueStateCodec.java deleted file mode 100644 index 83cc16a023a0..000000000000 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/queue/QueueStateCodec.java +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package com.swirlds.state.merkle.queue; - -import com.hedera.pbj.runtime.Codec; -import com.hedera.pbj.runtime.ParseException; -import com.hedera.pbj.runtime.io.ReadableSequentialData; -import com.hedera.pbj.runtime.io.WritableSequentialData; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.IOException; - -/** - * A codec for encoding and decoding the {@link QueueState} used in on-disk queue implementations. - * - *

Where is it used?

- * This class is used within on-disk queue implementations to encode and decode queue states as they - * are written to or read from persistent storage. - * For example: The {@link com.swirlds.state.merkle.disk.OnDiskQueueHelper} uses it to persist and retrieve - * the {@link QueueState} for defining boundaries and metadata of a queue. - */ -public class QueueStateCodec implements Codec { - - /** - * Singleton instance of {@code QueueCodec}. - */ - public static final Codec INSTANCE = new QueueStateCodec(); - - /** - * {@inheritDoc} - * - *

Parses the input to create a {@link QueueState}. - */ - @NonNull - @Override - public QueueState parse( - @NonNull ReadableSequentialData input, boolean strictMode, boolean parseUnknownFields, int maxDepth) - throws ParseException { - return new QueueState(input); - } - - /** - * {@inheritDoc} - * - *

Writes the {@link QueueState} to the output stream. - */ - @Override - public void write(@NonNull QueueState item, @NonNull WritableSequentialData output) throws IOException { - item.writeTo(output); - } - - /** - * {@inheritDoc} - * - *

Measures the size (in bytes) of a serialized {@link QueueState} by computing - * the difference in stream positions before and after parsing. - */ - @Override - public int measure(@NonNull ReadableSequentialData input) throws ParseException { - final var start = input.position(); - parse(input); - final var end = input.position(); - return (int) (end - start); - } - - /** - * {@inheritDoc} - */ - @Override - public int measureRecord(QueueState item) { - return item.getSizeInBytes(); - } - - /** - * {@inheritDoc} - * - *

Compares the given {@link QueueState} with its serialized binary representation. - */ - @Override - public boolean fastEquals(@NonNull QueueState item, @NonNull ReadableSequentialData input) throws ParseException { - return item.equals(parse(input)); - } - - /** - * {@inheritDoc} - * - * @return {@link QueueState} with default head and tail indices. - */ - @Override - public QueueState getDefaultInstance() { - return new QueueState(); - } -} From a8f817be8bf551b05ca50cb66cd0e4554f50be8a Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Thu, 24 Jul 2025 18:22:16 +0300 Subject: [PATCH 3/9] fix tests Signed-off-by: Nikita Lebedev --- .../merkle/MerkleSchemaRegistryTest.java | 27 ++-- .../app/state/merkle/SerializationTest.java | 21 +-- .../app/state/merkle/StateMetadataTest.java | 4 +- .../handle/DispatchHandleContextTest.java | 4 +- .../handle/stack/SavepointStackImplTest.java | 6 +- .../state/ReadableQueueStateBaseTest.java | 7 +- .../state/WritableQueueStateBaseTest.java | 39 ++--- .../state/merkle/singleton/ValueLeafTest.java | 3 +- .../WritablePlatformStateStoreTest.java | 11 +- .../test/fixtures/state/MerkleTestBase.java | 50 +++--- .../swirlds-state-api/build.gradle.kts | 1 - .../state/spi/ReadableKVStateBaseTest.java | 10 +- .../state/spi/ReadableSingletonStateTest.java | 5 +- .../state/spi/WrappedReadableKVStateTest.java | 4 +- .../state/spi/WrappedWritableKVStateTest.java | 6 +- .../spi/WrappedWritableSingletonTest.java | 7 +- .../state/spi/WritableKVStateBaseTest.java | 68 +++++---- .../spi/WritableSingletonStateBaseTest.java | 18 ++- .../state/test/fixtures/StateTestBase.java | 142 +++++++++--------- .../src/testFixtures/java/module-info.java | 1 + .../state/merkle/MerkleStateRootTest.java | 22 +-- .../state/merkle/VirtualMapStateTest.java | 24 +-- .../merkle/disk/OnDiskReadableStateTest.java | 6 +- .../merkle/disk/OnDiskWritableStateTest.java | 20 +-- .../memory/InMemoryReadableStateTest.java | 11 +- .../memory/InMemoryWritableStateTest.java | 27 ++-- .../state/merkle/singleton/ValueLeafTest.java | 3 +- .../test/fixtures/merkle/MerkleTestBase.java | 115 ++++++++++---- 28 files changed, 376 insertions(+), 286 deletions(-) diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java index 6a658134b988..b1db5197280e 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java @@ -445,7 +445,7 @@ public void migrate(@NonNull final MigrationContext ctx) { assertThat(ctx).isNotNull(); assertThat(ctx.previousVersion()).isNull(); assertThat(ctx.newStates().size()).isEqualTo(1); - final WritableKVState fruit = + final WritableKVState fruit = ctx.newStates().get(FRUIT_STATE_KEY); fruit.put(A_KEY, APPLE); fruit.put(B_KEY, BANANA); @@ -476,7 +476,7 @@ public void migrate(@NonNull final MigrationContext ctx) { // and nothing new assertThat(previousStates.isEmpty()).isFalse(); assertThat(previousStates.contains(FRUIT_STATE_KEY)).isTrue(); - final ReadableKVState oldFruit = previousStates.get(FRUIT_STATE_KEY); + final ReadableKVState oldFruit = previousStates.get(FRUIT_STATE_KEY); assertThat(oldFruit.keys()).toIterable().hasSize(3); assertThat(oldFruit.get(A_KEY)).isEqualTo(APPLE); assertThat(oldFruit.get(B_KEY)).isEqualTo(BANANA); @@ -489,18 +489,18 @@ public void migrate(@NonNull final MigrationContext ctx) { assertThat(newStates.contains(COUNTRY_STATE_KEY)).isTrue(); // Add in the new animals - final WritableKVState animals = newStates.get(ANIMAL_STATE_KEY); + final WritableKVState animals = newStates.get(ANIMAL_STATE_KEY); animals.put(A_KEY, AARDVARK); animals.put(B_KEY, BEAR); // Remove, update, and add fruit - final WritableKVState fruit = newStates.get(FRUIT_STATE_KEY); + final WritableKVState fruit = newStates.get(FRUIT_STATE_KEY); fruit.remove(A_KEY); fruit.put(B_KEY, BLACKBERRY); fruit.put(E_KEY, EGGPLANT); // Initialize the COUNTRY to be BRAZIL - final WritableSingletonState country = newStates.getSingleton(COUNTRY_STATE_KEY); + final WritableSingletonState country = newStates.getSingleton(COUNTRY_STATE_KEY); country.put(BRAZIL); // And the old states shouldn't have a COUNTRY_STATE_KEY @@ -532,12 +532,12 @@ public void migrate(@NonNull MigrationContext ctx) { // Verify that everything in v2 is still here assertThat(previousStates.stateKeys()) .containsExactlyInAnyOrder(FRUIT_STATE_KEY, ANIMAL_STATE_KEY, COUNTRY_STATE_KEY); - final ReadableKVState oldFruit = previousStates.get(FRUIT_STATE_KEY); + final ReadableKVState oldFruit = previousStates.get(FRUIT_STATE_KEY); assertThat(oldFruit.keys()).toIterable().containsExactlyInAnyOrder(B_KEY, C_KEY, E_KEY); assertThat(oldFruit.get(B_KEY)).isEqualTo(BLACKBERRY); assertThat(oldFruit.get(C_KEY)).isEqualTo(CHERRY); assertThat(oldFruit.get(E_KEY)).isEqualTo(EGGPLANT); - final ReadableKVState oldAnimals = previousStates.get(ANIMAL_STATE_KEY); + final ReadableKVState oldAnimals = previousStates.get(ANIMAL_STATE_KEY); assertThat(oldAnimals.get(A_KEY)).isEqualTo(AARDVARK); assertThat(oldAnimals.get(B_KEY)).isEqualTo(BEAR); @@ -547,12 +547,13 @@ public void migrate(@NonNull MigrationContext ctx) { assertThat(newStates.contains(ANIMAL_STATE_KEY)).isTrue(); // Add in a new animal - final WritableKVState animals = newStates.get(ANIMAL_STATE_KEY); + final WritableKVState animals = newStates.get(ANIMAL_STATE_KEY); animals.put(C_KEY, CUTTLEFISH); // And I should still see the COUNTRY_STATE_KEY in the previousStates, // but not in the newStates - final ReadableSingletonState country = previousStates.getSingleton(COUNTRY_STATE_KEY); + final ReadableSingletonState country = + previousStates.getSingleton(COUNTRY_STATE_KEY); assertThat(country.get()).isEqualTo(BRAZIL); assertThat(newStates.contains(COUNTRY_STATE_KEY)).isFalse(); @@ -614,15 +615,15 @@ void upgradeAndAddAState() { final var readableStates = merkleTree.getReadableStates(FIRST_SERVICE); assertThat(readableStates.size()).isEqualTo(3); - final ReadableKVState fruitV2 = readableStates.get(FRUIT_STATE_KEY); + final ReadableKVState fruitV2 = readableStates.get(FRUIT_STATE_KEY); assertThat(fruitV2.keys()).toIterable().containsExactlyInAnyOrder(B_KEY, C_KEY, E_KEY); assertThat(fruitV2.get(B_KEY)).isEqualTo(BLACKBERRY); - final ReadableKVState animalV2 = readableStates.get(ANIMAL_STATE_KEY); + final ReadableKVState animalV2 = readableStates.get(ANIMAL_STATE_KEY); assertThat(animalV2.get(A_KEY)).isEqualTo(AARDVARK); assertThat(animalV2.get(B_KEY)).isEqualTo(BEAR); - final ReadableSingletonState countryV2 = readableStates.getSingleton(COUNTRY_STATE_KEY); + final ReadableSingletonState countryV2 = readableStates.getSingleton(COUNTRY_STATE_KEY); assertThat(countryV2.get()).isEqualTo(BRAZIL); } @@ -661,7 +662,7 @@ void upgradeWithARemoveStep() { .isInstanceOf(IllegalArgumentException.class); // And this should be updated - final ReadableKVState animalV2 = readableStates.get(ANIMAL_STATE_KEY); + final ReadableKVState animalV2 = readableStates.get(ANIMAL_STATE_KEY); assertThat(animalV2.get(A_KEY)).isEqualTo(AARDVARK); assertThat(animalV2.get(B_KEY)).isEqualTo(BEAR); assertThat(animalV2.get(C_KEY)).isEqualTo(CUTTLEFISH); diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java index 78ce4277eef3..176396235a4f 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java @@ -105,7 +105,7 @@ public Set statesToCreate() { @Override public void migrate(@NonNull final MigrationContext ctx) { final var newStates = ctx.newStates(); - final WritableKVState fruit = newStates.get(FRUIT_STATE_KEY); + final WritableKVState fruit = newStates.get(FRUIT_STATE_KEY); fruit.put(A_KEY, APPLE); fruit.put(B_KEY, BANANA); fruit.put(C_KEY, CHERRY); @@ -114,8 +114,9 @@ public void migrate(@NonNull final MigrationContext ctx) { fruit.put(F_KEY, FIG); fruit.put(G_KEY, GRAPE); - final OnDiskWritableKVState animals = (OnDiskWritableKVState) - (OnDiskWritableKVState) newStates.get(ANIMAL_STATE_KEY); + final OnDiskWritableKVState animals = + (OnDiskWritableKVState) + (OnDiskWritableKVState) newStates.get(ANIMAL_STATE_KEY); animals.put(A_KEY, AARDVARK); animals.put(B_KEY, BEAR); animals.put(C_KEY, CUTTLEFISH); @@ -125,10 +126,10 @@ public void migrate(@NonNull final MigrationContext ctx) { animals.put(G_KEY, GOOSE); animals.commit(); - final WritableSingletonState country = newStates.getSingleton(COUNTRY_STATE_KEY); + final WritableSingletonState country = newStates.getSingleton(COUNTRY_STATE_KEY); country.put(CHAD); - final WritableQueueState steam = newStates.getQueue(STEAM_STATE_KEY); + final WritableQueueState steam = newStates.getQueue(STEAM_STATE_KEY); steam.add(ART); steam.add(BIOLOGY); steam.add(CHEMISTRY); @@ -331,7 +332,7 @@ private MerkleNodeState createMerkleHederaState(Schema schemaV1) { private static void populateVmCache(State loadedTree) { final var states = loadedTree.getWritableStates(FIRST_SERVICE); - final WritableKVState animalState = states.get(ANIMAL_STATE_KEY); + final WritableKVState animalState = states.get(ANIMAL_STATE_KEY); assertThat(animalState.get(A_KEY)).isEqualTo(AARDVARK); assertThat(animalState.get(B_KEY)).isEqualTo(BEAR); assertThat(animalState.get(C_KEY)).isEqualTo(CUTTLEFISH); @@ -343,7 +344,7 @@ private static void populateVmCache(State loadedTree) { private static void assertTree(State loadedTree) { final var states = loadedTree.getReadableStates(FIRST_SERVICE); - final ReadableKVState fruitState = states.get(FRUIT_STATE_KEY); + final ReadableKVState fruitState = states.get(FRUIT_STATE_KEY); assertThat(fruitState.get(A_KEY)).isEqualTo(APPLE); assertThat(fruitState.get(B_KEY)).isEqualTo(BANANA); assertThat(fruitState.get(C_KEY)).isEqualTo(CHERRY); @@ -352,7 +353,7 @@ private static void assertTree(State loadedTree) { assertThat(fruitState.get(F_KEY)).isEqualTo(FIG); assertThat(fruitState.get(G_KEY)).isEqualTo(GRAPE); - final ReadableKVState animalState = states.get(ANIMAL_STATE_KEY); + final ReadableKVState animalState = states.get(ANIMAL_STATE_KEY); assertThat(animalState.get(A_KEY)).isEqualTo(AARDVARK); assertThat(animalState.get(B_KEY)).isEqualTo(BEAR); assertThat(animalState.get(C_KEY)).isEqualTo(CUTTLEFISH); @@ -361,10 +362,10 @@ private static void assertTree(State loadedTree) { assertThat(animalState.get(F_KEY)).isEqualTo(FOX); assertThat(animalState.get(G_KEY)).isEqualTo(GOOSE); - final ReadableSingletonState countryState = states.getSingleton(COUNTRY_STATE_KEY); + final ReadableSingletonState countryState = states.getSingleton(COUNTRY_STATE_KEY); assertThat(countryState.get()).isEqualTo(CHAD); - final ReadableQueueState steamState = states.getQueue(STEAM_STATE_KEY); + final ReadableQueueState steamState = states.getQueue(STEAM_STATE_KEY); assertThat(steamState.iterator()) .toIterable() .containsExactly(ART, BIOLOGY, CHEMISTRY, DISCIPLINE, ECOLOGY, FIELDS, GEOMETRY); diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java index 0212f51049f8..c58234089e24 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java @@ -19,7 +19,7 @@ class StateMetadataTest extends MerkleTestBase { private Schema schema; - private StateDefinition def; + private StateDefinition def; @BeforeEach void setUp() { @@ -108,7 +108,7 @@ void onDiskValueSerializerClassId() { assertThat(expected).isEqualTo(md.onDiskValueSerializerClassId()); } - private long computeClassId(StateMetadata md, String suffix) { + private long computeClassId(StateMetadata md, String suffix) { return StateMetadata.computeClassId( md.serviceName(), md.stateDefinition().stateKey(), md.schema().getVersion(), suffix); } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchHandleContextTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchHandleContextTest.java index 5d9bdf9962a6..9be894cdc69e 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchHandleContextTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchHandleContextTest.java @@ -569,7 +569,7 @@ void failsAsExpectedWithoutAvailableApi() { final class DispatcherTest { private static final Predicate VERIFIER_CALLBACK = key -> true; private static final String FOOD_SERVICE = "FOOD_SERVICE"; - private static final Map BASE_DATA = Map.of( + private static final Map BASE_DATA = Map.of( A_KEY, APPLE, B_KEY, BANANA, C_KEY, CHERRY, @@ -690,7 +690,7 @@ void testDispatchPrecedingWithNonEmptyStackDoesntFail() { @Test void testDispatchPrecedingWithChangedDataDoesntFail() { final var context = createContext(txBody, HandleContext.TransactionCategory.USER); - final Map newData = new HashMap<>(BASE_DATA); + final Map newData = new HashMap<>(BASE_DATA); newData.put(B_KEY, BLUEBERRY); assertThatNoException() diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/stack/SavepointStackImplTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/stack/SavepointStackImplTest.java index f7fa7dff732f..ec96eea55047 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/stack/SavepointStackImplTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/stack/SavepointStackImplTest.java @@ -51,7 +51,7 @@ class SavepointStackImplTest extends StateTestBase { AccountID.newBuilder().accountNum(666L).build(); private static final Timestamp VALID_START = new Timestamp(1_234_567L, 890); - private final Map BASE_DATA = Map.of( + private final Map BASE_DATA = Map.of( A_KEY, APPLE, B_KEY, BANANA, C_KEY, CHERRY, @@ -763,11 +763,11 @@ void testReuseAfterCommitFullStack() { } } - private static Condition content(Map expected) { + private static Condition content(Map expected) { return new Condition<>(contentCheck(expected), "state " + expected); } - private static Predicate contentCheck(Map expected) { + private static Predicate contentCheck(Map expected) { return readableStates -> { final var actual = readableStates.get(FRUIT_STATE_KEY); if (expected.size() != actual.size()) { diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/ReadableQueueStateBaseTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/ReadableQueueStateBaseTest.java index a08cb4f64bb1..e568dbcf6a98 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/ReadableQueueStateBaseTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/ReadableQueueStateBaseTest.java @@ -3,13 +3,14 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.swirlds.state.test.fixtures.ListReadableQueueState; import com.swirlds.state.test.fixtures.ListWritableQueueState; import com.swirlds.state.test.fixtures.StateTestBase; import java.util.ArrayList; import org.junit.jupiter.api.Test; -class ReadableQueueStateBaseTest extends StateTestBase { +class ReadableQueueStateBaseTest extends StateTestBase { @Test void stateKey() { final var subject = @@ -34,14 +35,14 @@ void peekIsNullWhenEmpty() { void peekDoesNotRemove() { // Given a non-empty queue final var subject = readableSTEAMState(); - final var startingElements = new ArrayList(); + final var startingElements = new ArrayList(); subject.iterator().forEachRemaining(startingElements::add); // When we peek subject.peek(); // None of the queue elements are removed - final var endingElements = new ArrayList(); + final var endingElements = new ArrayList(); subject.iterator().forEachRemaining(endingElements::add); assertThat(startingElements).containsExactlyElementsOf(endingElements); } diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/WritableQueueStateBaseTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/WritableQueueStateBaseTest.java index 0a815381171f..cea2a763989b 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/WritableQueueStateBaseTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/WritableQueueStateBaseTest.java @@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.inOrder; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.swirlds.state.spi.QueueChangeListener; import com.swirlds.state.test.fixtures.ListWritableQueueState; import java.util.ConcurrentModificationException; @@ -20,7 +21,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -final class WritableQueueStateBaseTest extends ReadableQueueStateBaseTest { +final class WritableQueueStateBaseTest extends ReadableQueueStateBaseTest { @Nested class AddTests { @@ -38,7 +39,7 @@ void addDoesNotChangeDataSource() { @Test void iteratorIsInvalidAfterAdd() { final var subject = writableSTEAMState(); - final var element = "Hydrology"; + final var element = toProtoBytes("Hydrology"); final var iterator = subject.iterator(); assertThat(iterator.hasNext()).isTrue(); @@ -50,20 +51,20 @@ void iteratorIsInvalidAfterAdd() { @Test void iterateAfterAddGivesNewElements() { final var subject = writableSTEAMState(); - final var element = "Hydrology"; + final var element = toProtoBytes("Hydrology"); subject.add(element); assertThat(subject.iterator()) .toIterable() - .containsExactly(ART, BIOLOGY, CHEMISTRY, DISCIPLINE, ECOLOGY, FIELDS, GEOMETRY, "Hydrology"); + .containsExactly(ART, BIOLOGY, CHEMISTRY, DISCIPLINE, ECOLOGY, FIELDS, GEOMETRY, element); } @Test void addOnEmptyIsVisibleWithPeek() { final var subject = ListWritableQueueState.builder("FAKE_NAME", "FAKE_KEY").build(); - final var element = "Hydrology"; + final var element = toProtoBytes("Hydrology"); subject.add(element); @@ -110,7 +111,7 @@ void peekAfterRemoveIfFails() { @Test void peekAfterAdd() { final var subject = writableSTEAMState(); - subject.add("Hydrology"); + subject.add(toProtoBytes("Hydrology")); assertThat(subject.peek()).isSameAs(ART); } @@ -118,8 +119,9 @@ void peekAfterAdd() { void peekAfterAddOnEmptyList() { final var subject = ListWritableQueueState.builder("FAKE_NAME", "FAKE_KEY").build(); - subject.add("Hydrology"); - assertThat(subject.peek()).isSameAs("Hydrology"); + final var value = toProtoBytes("Hydrology"); + subject.add(value); + assertThat(subject.peek()).isSameAs(value); } } @@ -169,7 +171,8 @@ void pollAfterRemoveIfFails() { @Test void pollAfterAdd() { final var subject = writableSTEAMState(); - subject.add("Hydrology"); + final var value = toProtoBytes("Hydrology"); + subject.add(value); assertThat(subject.poll()).isSameAs(ART); assertThat(subject.poll()).isSameAs(BIOLOGY); assertThat(subject.poll()).isSameAs(CHEMISTRY); @@ -177,7 +180,7 @@ void pollAfterAdd() { assertThat(subject.poll()).isSameAs(ECOLOGY); assertThat(subject.poll()).isSameAs(FIELDS); assertThat(subject.poll()).isSameAs(GEOMETRY); - assertThat(subject.poll()).isSameAs("Hydrology"); + assertThat(subject.poll()).isSameAs(value); assertThat(subject.poll()).isNull(); } @@ -285,7 +288,7 @@ void commitTwice() { @Test void commitAfterAddOnNonEmptyList() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); backingList.add(ART); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); @@ -297,7 +300,7 @@ void commitAfterAddOnNonEmptyList() { @Test void commitAfterRemoving() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); backingList.add(ART); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); @@ -310,7 +313,7 @@ void commitAfterRemoving() { @Test void commitAfterRemovingAllAddedElementsDoesNotThrow() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); subject.add(ART); subject.add(BIOLOGY); @@ -321,7 +324,7 @@ void commitAfterRemovingAllAddedElementsDoesNotThrow() { @Test void commitAfterRemovingSomeAddedElementsOnlyIncludesAdded() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); subject.add(ART); subject.add(BIOLOGY); @@ -336,7 +339,7 @@ void commitAfterRemovingSomeAddedElementsOnlyIncludesAdded() { @Test void commitResetsIndex() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); subject.add(ART); subject.add(BIOLOGY); @@ -353,7 +356,7 @@ void commitResetsIndex() { @Test void commitAfterPeekingAndAddingStillAddsEverything() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); backingList.add(ART); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); subject.peek(); @@ -375,7 +378,7 @@ void resetOnEmptyList() { @Test void resetBeforeCommit() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); subject.add(ART); @@ -387,7 +390,7 @@ void resetBeforeCommit() { @Test void resetAfterCommit() { - final var backingList = new LinkedList(); + final var backingList = new LinkedList(); final var subject = new ListWritableQueueState<>(STEAM_SERVICE_NAME, STEAM_STATE_KEY, backingList); subject.add(ART); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/merkle/singleton/ValueLeafTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/merkle/singleton/ValueLeafTest.java index 688ca59c00dc..47b94f2977b3 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/merkle/singleton/ValueLeafTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/merkle/singleton/ValueLeafTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.swirlds.state.merkle.singleton.ValueLeaf; import com.swirlds.state.test.fixtures.merkle.MerkleTestBase; import org.junit.jupiter.api.Test; @@ -11,7 +12,7 @@ class ValueLeafTest extends MerkleTestBase { @Test void setValue() { setupSingletonCountry(); - final var leaf = new ValueLeaf<>(singletonClassId(COUNTRY_STATE_KEY), STRING_CODEC, DENMARK); + final var leaf = new ValueLeaf<>(singletonClassId(COUNTRY_STATE_KEY), ProtoBytes.PROTOBUF, DENMARK); assertThat(leaf.getValue()).isEqualTo(DENMARK); leaf.setValue(FRANCE); assertThat(leaf.getValue()).isEqualTo(FRANCE); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java index 7b49e78324de..5f860e84f0f1 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/service/WritablePlatformStateStoreTest.java @@ -11,6 +11,8 @@ import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.platform.state.PlatformState; +import com.hedera.hapi.platform.state.VirtualMapValue; +import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.test.fixtures.Randotron; import com.swirlds.merkledb.test.fixtures.MerkleDbTestUtils; import com.swirlds.platform.test.fixtures.virtualmap.VirtualMapUtils; @@ -46,10 +48,11 @@ void setUp() { "vm-" + WritablePlatformStateStoreTest.class.getSimpleName() + java.util.UUID.randomUUID(); virtualMap = VirtualMapUtils.createVirtualMap(virtualMapLabel, 1); - virtualMap.put( - StateUtils.getVirtualMapKeyForSingleton(PlatformStateService.NAME, PLATFORM_STATE_KEY), - toPbjPlatformState(randomPlatformState(randotron)), - PlatformState.PROTOBUF); + final Bytes key = StateUtils.getVirtualMapKeyForSingleton(PlatformStateService.NAME, PLATFORM_STATE_KEY); + final VirtualMapValue value = StateUtils.getVirtualMapValue( + PlatformStateService.NAME, PLATFORM_STATE_KEY, toPbjPlatformState(randomPlatformState(randotron))); + + virtualMap.put(key, value, VirtualMapValue.PROTOBUF); when(writableStates.getSingleton(PLATFORM_STATE_KEY)) .thenReturn( diff --git a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/MerkleTestBase.java b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/MerkleTestBase.java index 12abfdd9532c..be000aa425f0 100644 --- a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/MerkleTestBase.java +++ b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/MerkleTestBase.java @@ -42,12 +42,12 @@ public class MerkleTestBase extends com.swirlds.state.test.fixtures.merkle.Merkl protected SemanticVersion v1 = SemanticVersion.newBuilder().major(1).build(); - protected StateMetadata fruitMetadata; - protected StateMetadata fruitVirtualMetadata; - protected StateMetadata animalMetadata; - protected StateMetadata spaceMetadata; - protected StateMetadata steamMetadata; - protected StateMetadata countryMetadata; + protected StateMetadata fruitMetadata; + protected StateMetadata fruitVirtualMetadata; + protected StateMetadata animalMetadata; + protected StateMetadata spaceMetadata; + protected StateMetadata steamMetadata; + protected StateMetadata countryMetadata; /** Sets up the "Fruit" merkle map, label, and metadata. */ @Override @@ -56,7 +56,7 @@ protected void setupFruitMerkleMap() { fruitMetadata = new StateMetadata<>( FIRST_SERVICE, new TestSchema(1), - StateDefinition.inMemory(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC)); + StateDefinition.inMemory(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, ProtoBytes.PROTOBUF)); } /** Sets up the "Fruit" virtual map, label, and metadata. */ @@ -66,7 +66,7 @@ protected void setupFruitVirtualMap() { fruitVirtualMetadata = new StateMetadata<>( FIRST_SERVICE, new TestSchema(1), - StateDefinition.onDisk(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC, 100)); + StateDefinition.onDisk(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, ProtoBytes.PROTOBUF, 100)); } /** Sets up the "Animal" merkle map, label, and metadata. */ @@ -76,7 +76,7 @@ protected void setupAnimalMerkleMap() { animalMetadata = new StateMetadata<>( FIRST_SERVICE, new TestSchema(1), - StateDefinition.inMemory(ANIMAL_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC)); + StateDefinition.inMemory(ANIMAL_STATE_KEY, ProtoBytes.PROTOBUF, ProtoBytes.PROTOBUF)); } /** Sets up the "Space" merkle map, label, and metadata. */ @@ -86,21 +86,21 @@ protected void setupSpaceMerkleMap() { spaceMetadata = new StateMetadata<>( SECOND_SERVICE, new TestSchema(1), - StateDefinition.inMemory(SPACE_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC)); + StateDefinition.inMemory(SPACE_STATE_KEY, ProtoBytes.PROTOBUF, ProtoBytes.PROTOBUF)); } @Override protected void setupSingletonCountry() { super.setupSingletonCountry(); countryMetadata = new StateMetadata<>( - FIRST_SERVICE, new TestSchema(1), StateDefinition.singleton(COUNTRY_STATE_KEY, STRING_CODEC)); + FIRST_SERVICE, new TestSchema(1), StateDefinition.singleton(COUNTRY_STATE_KEY, ProtoBytes.PROTOBUF)); } @Override protected void setupSteamQueue() { super.setupSteamQueue(); steamMetadata = new StateMetadata<>( - FIRST_SERVICE, new TestSchema(1), StateDefinition.queue(STEAM_STATE_KEY, STRING_CODEC)); + FIRST_SERVICE, new TestSchema(1), StateDefinition.queue(STEAM_STATE_KEY, ProtoBytes.PROTOBUF)); } /** @@ -125,32 +125,22 @@ protected MerkleNode getNodeForLabel(MerkleStateRoot stateRoot, String label) { /** A convenience method for adding a k/v state to a merkle map */ protected void addKvState( - MerkleMap, InMemoryValue> map, - StateMetadata md, + MerkleMap, InMemoryValue> map, + StateMetadata md, ProtoBytes key, - String value) { + ProtoBytes value) { final var def = md.stateDefinition(); super.addKvState(map, md.inMemoryValueClassId(), def.keyCodec(), def.valueCodec(), key, value); } /** A convenience method for adding a singleton state to a virtual map */ - protected void addSingletonState(VirtualMap map, StateMetadata md, String value) { - super.addSingletonState( - map, - md.serviceName(), - md.stateDefinition().stateKey(), - md.stateDefinition().valueCodec(), - value); + protected void addSingletonState(VirtualMap map, StateMetadata md, ProtoBytes value) { + super.addSingletonState(map, md.serviceName(), md.stateDefinition().stateKey(), value); } /** A convenience method for adding a k/v state to a virtual map */ - protected void addKvState(VirtualMap map, StateMetadata md, ProtoBytes key, String value) { - super.addKvState( - map, - md.serviceName(), - md.stateDefinition().stateKey(), - md.stateDefinition().valueCodec(), - key, - value); + protected void addKvState( + VirtualMap map, StateMetadata md, ProtoBytes key, ProtoBytes value) { + super.addKvState(map, md.serviceName(), md.stateDefinition().stateKey(), key, value); } } diff --git a/platform-sdk/swirlds-state-api/build.gradle.kts b/platform-sdk/swirlds-state-api/build.gradle.kts index 05b0310c5213..2554b2b32e13 100644 --- a/platform-sdk/swirlds-state-api/build.gradle.kts +++ b/platform-sdk/swirlds-state-api/build.gradle.kts @@ -12,7 +12,6 @@ testModuleInfo { requires("org.mockito") requires("org.mockito.junit.jupiter") requires("com.swirlds.state.api.test.fixtures") - requires("org.mockito") runtimeOnly("com.swirlds.config.api") runtimeOnly("com.swirlds.config.impl") } diff --git a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableKVStateBaseTest.java b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableKVStateBaseTest.java index c31b307e8c96..04dd7e7ae0ca 100644 --- a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableKVStateBaseTest.java +++ b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableKVStateBaseTest.java @@ -20,8 +20,8 @@ * ReadableKVStateBase#reset()}) will be covered by other tests in addition to this one. */ public class ReadableKVStateBaseTest extends StateTestBase { - private ReadableKVStateBase state; - protected Map backingMap; + private ReadableKVStateBase state; + protected Map backingMap; @BeforeEach void setUp() { @@ -29,8 +29,8 @@ void setUp() { this.state = createFruitState(this.backingMap); } - protected Map createBackingMap() { - final var map = new HashMap(); + protected Map createBackingMap() { + final var map = new HashMap(); map.put(A_KEY, APPLE); map.put(B_KEY, BANANA); map.put(C_KEY, CHERRY); @@ -41,7 +41,7 @@ protected Map createBackingMap() { return map; } - protected ReadableKVStateBase createFruitState(Map backingMap) { + protected ReadableKVStateBase createFruitState(Map backingMap) { return new MapReadableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, backingMap); } diff --git a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableSingletonStateTest.java b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableSingletonStateTest.java index 154e85987c5f..208a0fd0a922 100644 --- a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableSingletonStateTest.java +++ b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/ReadableSingletonStateTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.swirlds.state.test.fixtures.FunctionReadableSingletonState; import com.swirlds.state.test.fixtures.StateTestBase; import java.util.concurrent.atomic.AtomicReference; @@ -11,9 +12,9 @@ public class ReadableSingletonStateTest extends StateTestBase { - protected AtomicReference backingStore = new AtomicReference<>(AUSTRALIA); + protected AtomicReference backingStore = new AtomicReference<>(AUSTRALIA); - ReadableSingletonStateBase createState() { + ReadableSingletonStateBase createState() { return new FunctionReadableSingletonState<>(COUNTRY_STATE_KEY, COUNTRY_SERVICE_NAME, backingStore::get); } diff --git a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedReadableKVStateTest.java b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedReadableKVStateTest.java index c4ebd6e4ffef..d8333bfb5ce3 100644 --- a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedReadableKVStateTest.java +++ b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedReadableKVStateTest.java @@ -17,12 +17,12 @@ */ class WrappedReadableKVStateTest extends StateTestBase { @Mock - private ReadableKVState delegate; + private ReadableKVState delegate; @Mock private Iterator keys; - private WrappedReadableKVState state; + private WrappedReadableKVState state; @BeforeEach void setUp() { diff --git a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableKVStateTest.java b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableKVStateTest.java index 8b8d5d815c6e..edb653b538d5 100644 --- a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableKVStateTest.java +++ b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableKVStateTest.java @@ -16,12 +16,12 @@ * This test verifies the behavior of {@link WrappedWritableKVState}. */ class WrappedWritableKVStateTest extends StateTestBase { - private WritableKVStateBase delegate; - private WrappedWritableKVState state; + private WritableKVStateBase delegate; + private WrappedWritableKVState state; @BeforeEach public void setUp() { - final var map = new HashMap(); + final var map = new HashMap(); map.put(A_KEY, APPLE); map.put(B_KEY, BANANA); this.delegate = new MapWritableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, map); diff --git a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableSingletonTest.java b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableSingletonTest.java index 08304f9aa198..294afe1819b8 100644 --- a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableSingletonTest.java +++ b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WrappedWritableSingletonTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.swirlds.state.test.fixtures.FunctionWritableSingletonState; import com.swirlds.state.test.fixtures.StateTestBase; import java.util.concurrent.atomic.AtomicReference; @@ -13,11 +14,11 @@ * This test verifies behavior specific to a {@link WrappedWritableKVState}. */ class WrappedWritableSingletonTest extends StateTestBase { - private WritableSingletonState delegate; + private WritableSingletonState delegate; - protected AtomicReference backingStore = new AtomicReference<>(AUSTRALIA); + protected AtomicReference backingStore = new AtomicReference<>(AUSTRALIA); - private WritableSingletonStateBase createState() { + private WritableSingletonStateBase createState() { delegate = new FunctionWritableSingletonState<>( COUNTRY_STATE_KEY, COUNTRY_SERVICE_NAME, backingStore::get, backingStore::set); return new WrappedWritableSingletonState<>(delegate); diff --git a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableKVStateBaseTest.java b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableKVStateBaseTest.java index 8153e5eab021..3a3bfe453768 100644 --- a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableKVStateBaseTest.java +++ b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableKVStateBaseTest.java @@ -40,11 +40,11 @@ */ public class WritableKVStateBaseTest extends ReadableKVStateBaseTest { private static final String NUM_ITERATIONS_ARG = "WritableKVStateBaseTest.DeterministicUpdates.numIterations"; - private WritableKVStateBase state; + private WritableKVStateBase state; @Override - protected Map createBackingMap() { - final var map = new HashMap(); + protected Map createBackingMap() { + final var map = new HashMap(); this.backingMap = Mockito.spy(map); backingMap.put(A_KEY, APPLE); backingMap.put(B_KEY, BANANA); @@ -60,7 +60,8 @@ protected Map createBackingMap() { return backingMap; } - protected WritableKVStateBase createFruitState(@NonNull final Map map) { + protected WritableKVStateBase createFruitState( + @NonNull final Map map) { this.state = Mockito.spy(new MapWritableKVState<>(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, map)); return state; } @@ -70,10 +71,10 @@ protected WritableKVStateBase createFruitState(@NonNull fina @ExtendWith(MockitoExtension.class) final class WithRegisteredListeners { @Mock - private KVChangeListener firstListener; + private KVChangeListener firstListener; @Mock - private KVChangeListener secondListener; + private KVChangeListener secondListener; @BeforeEach void setUp() { @@ -87,28 +88,33 @@ void allAreNotifiedOfPut() { final var inOrder = inOrder(firstListener, secondListener); ProtoBytes hKey = toProtoBytes("H"); ProtoBytes iKey = toProtoBytes("I"); - state.put(hKey, "Honeydew"); - state.put(iKey, "Indian Fig"); + ProtoBytes hValue = toProtoBytes("Honeydew"); + ProtoBytes iValue = toProtoBytes("Indian Fig"); + state.put(hKey, hValue); + state.put(iKey, iValue); state.commit(); - inOrder.verify(firstListener).mapUpdateChange(hKey, "Honeydew"); - inOrder.verify(secondListener).mapUpdateChange(hKey, "Honeydew"); - inOrder.verify(firstListener).mapUpdateChange(iKey, "Indian Fig"); - inOrder.verify(secondListener).mapUpdateChange(iKey, "Indian Fig"); + inOrder.verify(firstListener).mapUpdateChange(hKey, hValue); + inOrder.verify(secondListener).mapUpdateChange(hKey, hValue); + inOrder.verify(firstListener).mapUpdateChange(iKey, iValue); + inOrder.verify(secondListener).mapUpdateChange(iKey, iValue); } @Test @DisplayName("all listeners are notified of updates in order") void allAreNotifiedOfUpdates() { final var inOrder = inOrder(firstListener, secondListener); - state.put(B_KEY, "Blackberry"); - state.put(C_KEY, "Cantaloupe"); + ProtoBytes bValue = toProtoBytes("Blackberry"); + ProtoBytes cValue = toProtoBytes("Cantaloupe"); + + state.put(B_KEY, bValue); + state.put(C_KEY, cValue); state.commit(); - inOrder.verify(firstListener).mapUpdateChange(B_KEY, "Blackberry"); - inOrder.verify(secondListener).mapUpdateChange(B_KEY, "Blackberry"); - inOrder.verify(firstListener).mapUpdateChange(C_KEY, "Cantaloupe"); - inOrder.verify(secondListener).mapUpdateChange(C_KEY, "Cantaloupe"); + inOrder.verify(firstListener).mapUpdateChange(B_KEY, bValue); + inOrder.verify(secondListener).mapUpdateChange(B_KEY, bValue); + inOrder.verify(firstListener).mapUpdateChange(C_KEY, cValue); + inOrder.verify(secondListener).mapUpdateChange(C_KEY, cValue); } @Test @@ -154,7 +160,7 @@ void putAndIncrementCount() { // Commit should cause the value to be added state.commit(); - verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), Mockito.anyString()); + verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).putIntoDataSource(C_KEY, CHERRY); verify(state, Mockito.never()).removeFromDataSource(anyProtoBytes()); @@ -183,7 +189,7 @@ void putExisting() { // Commit should cause the value to be updated state.commit(); - verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), Mockito.anyString()); + verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).putIntoDataSource(A_KEY, ACAI); verify(state, Mockito.never()).removeFromDataSource(anyProtoBytes()); @@ -209,7 +215,7 @@ void putAfterGet() { // Commit should cause the value to be updated state.commit(); - verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).putIntoDataSource(B_KEY, BLACKBERRY); verify(state, Mockito.never()).removeFromDataSource(anyProtoBytes()); } @@ -232,7 +238,7 @@ void putTwice() { // Commit should cause the value to be updated to the latest value state.commit(); - verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).putIntoDataSource(B_KEY, BLUEBERRY); verify(state, Mockito.never()).removeFromDataSource(anyProtoBytes()); @@ -261,7 +267,7 @@ void putAfterRemove() { // Commit should cause the value to be updated to the latest value state.commit(); - verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.times(1)).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).putIntoDataSource(B_KEY, BLACKBERRY); verify(state, Mockito.never()).removeFromDataSource(anyProtoBytes()); @@ -270,10 +276,6 @@ void putAfterRemove() { } } - private static ProtoBytes anyProtoBytes() { - return any(ProtoBytes.class); - } - @Nested @DisplayName("remove") final class RemoveTest { @@ -308,7 +310,7 @@ void removeUnknown() { // Commit should cause the value to be removed (even though it doesn't actually exist in // the backend) state.commit(); - verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(C_KEY); @@ -343,7 +345,7 @@ void removeKnown() { // Commit should cause the value to be removed state.commit(); - verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(A_KEY); @@ -379,7 +381,7 @@ void removeTwice() { // Commit should cause the value to be removed state.commit(); - verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(B_KEY); @@ -412,7 +414,7 @@ void removeAfterGet() { // Commit should cause the value to be removed but not "put" state.commit(); - verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(A_KEY); } @@ -442,7 +444,7 @@ void removeAfterGetUnknown() { // Commit should cause the value to be removed but not "put" state.commit(); - verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(C_KEY); } @@ -475,7 +477,7 @@ void removeAfterPut() { // Commit should cause the value to be removed but not "put" state.commit(); - verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyString()); + verify(state, Mockito.never()).putIntoDataSource(anyProtoBytes(), anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(anyProtoBytes()); verify(state, Mockito.times(1)).removeFromDataSource(A_KEY); diff --git a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableSingletonStateBaseTest.java b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableSingletonStateBaseTest.java index a4897267219b..9d4d28d62401 100644 --- a/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableSingletonStateBaseTest.java +++ b/platform-sdk/swirlds-state-api/src/test/java/com/swirlds/state/spi/WritableSingletonStateBaseTest.java @@ -6,6 +6,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.inOrder; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.swirlds.state.test.fixtures.FunctionWritableSingletonState; import java.time.Duration; import java.util.SplittableRandom; @@ -25,7 +26,7 @@ public class WritableSingletonStateBaseTest extends ReadableSingletonStateTest { @Override - protected WritableSingletonStateBase createState() { + protected WritableSingletonStateBase createState() { return new FunctionWritableSingletonState<>( COUNTRY_STATE_KEY, COUNTRY_SERVICE_NAME, backingStore::get, backingStore::set); } @@ -92,7 +93,7 @@ void lastPutIsAlwaysCommittedValue() { final var nextValue = new AtomicInteger(); final var putFuture = runAsync(() -> { while (!done.get()) { - final var value = values.get(nextValue.getAndIncrement() % NUM_UNIQUE_STRINGS); + final var value = toProtoBytes(values.get(nextValue.getAndIncrement() % NUM_UNIQUE_STRINGS)); state.put(value); state.commit(); // Confirm that concurrent get() calls don't corrupt the committed modification @@ -236,12 +237,12 @@ void dirtyRead() { @ExtendWith(MockitoExtension.class) final class WithRegisteredListeners { @Mock - private SingletonChangeListener firstListener; + private SingletonChangeListener firstListener; @Mock - private SingletonChangeListener secondListener; + private SingletonChangeListener secondListener; - private final WritableSingletonStateBase subject = createState(); + private final WritableSingletonStateBase subject = createState(); @BeforeEach void setUp() { @@ -254,11 +255,12 @@ void setUp() { void allAreNotifiedOfPopsAndAddsInOrder() { final var inOrder = inOrder(firstListener, secondListener); - subject.put("Dragonfruit"); + ProtoBytes dValue = toProtoBytes("Dragonfruit"); + subject.put(dValue); subject.commit(); - inOrder.verify(firstListener).singletonUpdateChange("Dragonfruit"); - inOrder.verify(secondListener).singletonUpdateChange("Dragonfruit"); + inOrder.verify(firstListener).singletonUpdateChange(toProtoBytes("Dragonfruit")); + inOrder.verify(secondListener).singletonUpdateChange(toProtoBytes("Dragonfruit")); } } } diff --git a/platform-sdk/swirlds-state-api/src/testFixtures/java/com/swirlds/state/test/fixtures/StateTestBase.java b/platform-sdk/swirlds-state-api/src/testFixtures/java/com/swirlds/state/test/fixtures/StateTestBase.java index 73dc5bb4099b..051bfaa79e00 100644 --- a/platform-sdk/swirlds-state-api/src/testFixtures/java/com/swirlds/state/test/fixtures/StateTestBase.java +++ b/platform-sdk/swirlds-state-api/src/testFixtures/java/com/swirlds/state/test/fixtures/StateTestBase.java @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 package com.swirlds.state.test.fixtures; +import static org.mockito.ArgumentMatchers.any; + import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -49,60 +51,56 @@ public class StateTestBase extends TestBase { protected static final ProtoBytes F_KEY = toProtoBytes("F"); protected static final ProtoBytes G_KEY = toProtoBytes("G"); - protected static ProtoBytes toProtoBytes(final String value) { - return new ProtoBytes(Bytes.wrap(value)); - } - - protected static final String APPLE = "Apple"; - protected static final String ACAI = "Acai"; - protected static final String BANANA = "Banana"; - protected static final String BLACKBERRY = "BlackBerry"; - protected static final String BLUEBERRY = "BlueBerry"; - protected static final String CHERRY = "Cherry"; - protected static final String CRANBERRY = "Cranberry"; - protected static final String DATE = "Date"; - protected static final String DRAGONFRUIT = "DragonFruit"; - protected static final String EGGPLANT = "Eggplant"; - protected static final String ELDERBERRY = "ElderBerry"; - protected static final String FIG = "Fig"; - protected static final String FEIJOA = "Feijoa"; - protected static final String GRAPE = "Grape"; - - protected static final String AARDVARK = "Aardvark"; - protected static final String BEAR = "Bear"; - protected static final String CUTTLEFISH = "Cuttlefish"; - protected static final String DOG = "Dog"; - protected static final String EMU = "Emu"; - protected static final String FOX = "Fox"; - protected static final String GOOSE = "Goose"; - - protected static final String ASTRONAUT = "Astronaut"; - protected static final String BLASTOFF = "Blastoff"; - protected static final String COMET = "Comet"; - protected static final String DRACO = "Draco"; - protected static final String EXOPLANET = "Exoplanet"; - protected static final String FORCE = "Force"; - protected static final String GRAVITY = "Gravity"; - - protected static final String ART = "Art"; - protected static final String BIOLOGY = "Biology"; - protected static final String CHEMISTRY = "Chemistry"; - protected static final String DISCIPLINE = "Discipline"; - protected static final String ECOLOGY = "Ecology"; - protected static final String FIELDS = "Fields"; - protected static final String GEOMETRY = "Geometry"; - - protected static final String AUSTRALIA = "Australia"; - protected static final String BRAZIL = "Brazil"; - protected static final String CHAD = "Chad"; - protected static final String DENMARK = "Denmark"; - protected static final String ESTONIA = "Estonia"; - protected static final String FRANCE = "France"; - protected static final String GHANA = "Ghana"; + protected static final ProtoBytes APPLE = toProtoBytes("Apple"); + protected static final ProtoBytes ACAI = toProtoBytes("Acai"); + protected static final ProtoBytes BANANA = toProtoBytes("Banana"); + protected static final ProtoBytes BLACKBERRY = toProtoBytes("BlackBerry"); + protected static final ProtoBytes BLUEBERRY = toProtoBytes("BlueBerry"); + protected static final ProtoBytes CHERRY = toProtoBytes("Cherry"); + protected static final ProtoBytes CRANBERRY = toProtoBytes("Cranberry"); + protected static final ProtoBytes DATE = toProtoBytes("Date"); + protected static final ProtoBytes DRAGONFRUIT = toProtoBytes("DragonFruit"); + protected static final ProtoBytes EGGPLANT = toProtoBytes("Eggplant"); + protected static final ProtoBytes ELDERBERRY = toProtoBytes("ElderBerry"); + protected static final ProtoBytes FIG = toProtoBytes("Fig"); + protected static final ProtoBytes FEIJOA = toProtoBytes("Feijoa"); + protected static final ProtoBytes GRAPE = toProtoBytes("Grape"); + + protected static final ProtoBytes AARDVARK = toProtoBytes("Aardvark"); + protected static final ProtoBytes BEAR = toProtoBytes("Bear"); + protected static final ProtoBytes CUTTLEFISH = toProtoBytes("Cuttlefish"); + protected static final ProtoBytes DOG = toProtoBytes("Dog"); + protected static final ProtoBytes EMU = toProtoBytes("Emu"); + protected static final ProtoBytes FOX = toProtoBytes("Fox"); + protected static final ProtoBytes GOOSE = toProtoBytes("Goose"); + + protected static final ProtoBytes ASTRONAUT = toProtoBytes("Astronaut"); + protected static final ProtoBytes BLASTOFF = toProtoBytes("Blastoff"); + protected static final ProtoBytes COMET = toProtoBytes("Comet"); + protected static final ProtoBytes DRACO = toProtoBytes("Draco"); + protected static final ProtoBytes EXOPLANET = toProtoBytes("Exoplanet"); + protected static final ProtoBytes FORCE = toProtoBytes("Force"); + protected static final ProtoBytes GRAVITY = toProtoBytes("Gravity"); + + protected static final ProtoBytes ART = toProtoBytes("Art"); + protected static final ProtoBytes BIOLOGY = toProtoBytes("Biology"); + protected static final ProtoBytes CHEMISTRY = toProtoBytes("Chemistry"); + protected static final ProtoBytes DISCIPLINE = toProtoBytes("Discipline"); + protected static final ProtoBytes ECOLOGY = toProtoBytes("Ecology"); + protected static final ProtoBytes FIELDS = toProtoBytes("Fields"); + protected static final ProtoBytes GEOMETRY = toProtoBytes("Geometry"); + + protected static final ProtoBytes AUSTRALIA = toProtoBytes("Australia"); + protected static final ProtoBytes BRAZIL = toProtoBytes("Brazil"); + protected static final ProtoBytes CHAD = toProtoBytes("Chad"); + protected static final ProtoBytes DENMARK = toProtoBytes("Denmark"); + protected static final ProtoBytes ESTONIA = toProtoBytes("Estonia"); + protected static final ProtoBytes FRANCE = toProtoBytes("France"); + protected static final ProtoBytes GHANA = toProtoBytes("Ghana"); @NonNull - protected MapReadableKVState readableFruitState() { - return MapReadableKVState.builder(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY) + protected MapReadableKVState readableFruitState() { + return MapReadableKVState.builder(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY) .value(A_KEY, APPLE) .value(B_KEY, BANANA) .value(C_KEY, CHERRY) @@ -114,8 +112,8 @@ protected MapReadableKVState readableFruitState() { } @NonNull - protected MapWritableKVState writableFruitState() { - return MapWritableKVState.builder(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY) + protected MapWritableKVState writableFruitState() { + return MapWritableKVState.builder(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY) .value(A_KEY, APPLE) .value(B_KEY, BANANA) .value(C_KEY, CHERRY) @@ -127,8 +125,8 @@ protected MapWritableKVState writableFruitState() { } @NonNull - protected MapReadableKVState readableAnimalState() { - return MapReadableKVState.builder(ANIMAL_SERVICE_NAME, ANIMAL_STATE_KEY) + protected MapReadableKVState readableAnimalState() { + return MapReadableKVState.builder(ANIMAL_SERVICE_NAME, ANIMAL_STATE_KEY) .value(A_KEY, AARDVARK) .value(B_KEY, BEAR) .value(C_KEY, CUTTLEFISH) @@ -140,8 +138,8 @@ protected MapReadableKVState readableAnimalState() { } @NonNull - protected MapWritableKVState writableAnimalState() { - return MapWritableKVState.builder(ANIMAL_SERVICE_NAME, ANIMAL_STATE_KEY) + protected MapWritableKVState writableAnimalState() { + return MapWritableKVState.builder(ANIMAL_SERVICE_NAME, ANIMAL_STATE_KEY) .value(A_KEY, AARDVARK) .value(B_KEY, BEAR) .value(C_KEY, CUTTLEFISH) @@ -153,20 +151,20 @@ protected MapWritableKVState writableAnimalState() { } @NonNull - protected ReadableSingletonState readableSpaceState() { + protected ReadableSingletonState readableSpaceState() { return new FunctionReadableSingletonState<>(SPACE_SERVICE_NAME, SPACE_STATE_KEY, () -> ASTRONAUT); } @NonNull - protected WritableSingletonState writableSpaceState() { - final AtomicReference backingValue = new AtomicReference<>(ASTRONAUT); + protected WritableSingletonState writableSpaceState() { + final AtomicReference backingValue = new AtomicReference<>(ASTRONAUT); return new FunctionWritableSingletonState<>( SPACE_SERVICE_NAME, SPACE_STATE_KEY, backingValue::get, backingValue::set); } @NonNull - protected ListReadableQueueState readableSTEAMState() { - return ListReadableQueueState.builder(STEAM_STATE_KEY, STEAM_SERVICE_NAME) + protected ListReadableQueueState readableSTEAMState() { + return ListReadableQueueState.builder(STEAM_STATE_KEY, STEAM_SERVICE_NAME) .value(ART) .value(BIOLOGY) .value(CHEMISTRY) @@ -178,8 +176,8 @@ protected ListReadableQueueState readableSTEAMState() { } @NonNull - protected ListWritableQueueState writableSTEAMState() { - return ListWritableQueueState.builder(STEAM_SERVICE_NAME, STEAM_STATE_KEY) + protected ListWritableQueueState writableSTEAMState() { + return ListWritableQueueState.builder(STEAM_SERVICE_NAME, STEAM_STATE_KEY) .value(ART) .value(BIOLOGY) .value(CHEMISTRY) @@ -191,13 +189,13 @@ protected ListWritableQueueState writableSTEAMState() { } @NonNull - protected ReadableSingletonState readableCountryState() { + protected ReadableSingletonState readableCountryState() { return new FunctionReadableSingletonState<>(COUNTRY_SERVICE_NAME, COUNTRY_STATE_KEY, () -> AUSTRALIA); } @NonNull - protected WritableSingletonState writableCountryState() { - final AtomicReference backingValue = new AtomicReference<>(AUSTRALIA); + protected WritableSingletonState writableCountryState() { + final AtomicReference backingValue = new AtomicReference<>(AUSTRALIA); return new FunctionWritableSingletonState<>( COUNTRY_SERVICE_NAME, COUNTRY_STATE_KEY, backingValue::get, backingValue::set); } @@ -206,4 +204,12 @@ protected WritableSingletonState writableCountryState() { protected SemanticVersion version(int major, int minor, int patch) { return new SemanticVersion(major, minor, patch, null, null); } + + protected static ProtoBytes anyProtoBytes() { + return any(ProtoBytes.class); + } + + protected static ProtoBytes toProtoBytes(final String value) { + return new ProtoBytes(Bytes.wrap(value)); + } } diff --git a/platform-sdk/swirlds-state-api/src/testFixtures/java/module-info.java b/platform-sdk/swirlds-state-api/src/testFixtures/java/module-info.java index c2c6bcd43099..955b305aee1a 100644 --- a/platform-sdk/swirlds-state-api/src/testFixtures/java/module-info.java +++ b/platform-sdk/swirlds-state-api/src/testFixtures/java/module-info.java @@ -4,6 +4,7 @@ requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.state.api; requires transitive org.junit.jupiter.params; + requires org.mockito; requires static transitive com.github.spotbugs.annotations; exports com.swirlds.state.test.fixtures; diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/MerkleStateRootTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/MerkleStateRootTest.java index 96bd5f0ea07f..9cdb9ce3d000 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/MerkleStateRootTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/MerkleStateRootTest.java @@ -232,7 +232,7 @@ void addingServiceTwiceWithDifferentMetadata() { final var fruitMetadata2 = new StateMetadata<>( FIRST_SERVICE, new TestSchema(1), - StateDefinition.inMemory(FRUIT_STATE_KEY, STRING_CODEC, LONG_CODEC)); + StateDefinition.inMemory(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, LONG_CODEC)); stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> fruitMerkleMap); stateRoot.putServiceStateIfAbsent(fruitMetadata2, () -> fruitMerkleMap); @@ -326,7 +326,7 @@ void remove() { final var md = new StateMetadata<>( serviceName, new TestSchema(1), - StateDefinition.inMemory(FRUIT_STATE_KEY, STRING_CODEC, STRING_CODEC)); + StateDefinition.inMemory(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, ProtoBytes.PROTOBUF)); final var node = createMerkleMap(label); map.put(serviceName, node); @@ -489,16 +489,16 @@ void knownServiceNameUsingReadableStates() { assertThat(states.isEmpty()).isFalse(); assertThat(states.size()).isEqualTo(4); // animal and fruit and country and steam - final ReadableKVState fruitState = states.get(FRUIT_STATE_KEY); + final ReadableKVState fruitState = states.get(FRUIT_STATE_KEY); assertFruitState(fruitState); - final ReadableKVState animalState = states.get(ANIMAL_STATE_KEY); + final ReadableKVState animalState = states.get(ANIMAL_STATE_KEY); assertAnimalState(animalState); - final ReadableSingletonState countryState = states.getSingleton(COUNTRY_STATE_KEY); + final ReadableSingletonState countryState = states.getSingleton(COUNTRY_STATE_KEY); assertCountryState(countryState); - final ReadableQueueState steamState = states.getQueue(STEAM_STATE_KEY); + final ReadableQueueState steamState = states.getQueue(STEAM_STATE_KEY); assertSteamState(steamState); // And the states we got back CANNOT be cast to WritableState @@ -527,7 +527,7 @@ void knownServiceNameUsingReadableStates() { .isInstanceOf(ClassCastException.class); } - private static void assertFruitState(ReadableKVState fruitState) { + private static void assertFruitState(ReadableKVState fruitState) { assertThat(fruitState).isNotNull(); assertThat(fruitState.get(A_KEY)).isSameAs(APPLE); assertThat(fruitState.get(B_KEY)).isSameAs(BANANA); @@ -538,7 +538,7 @@ private static void assertFruitState(ReadableKVState fruitSt assertThat(fruitState.get(G_KEY)).isNull(); } - private void assertAnimalState(ReadableKVState animalState) { + private void assertAnimalState(ReadableKVState animalState) { assertThat(animalState).isNotNull(); assertThat(animalState.get(A_KEY)).isNull(); assertThat(animalState.get(B_KEY)).isNull(); @@ -549,12 +549,12 @@ private void assertAnimalState(ReadableKVState animalState) assertThat(animalState.get(G_KEY)).isNull(); } - private void assertCountryState(ReadableSingletonState countryState) { + private void assertCountryState(ReadableSingletonState countryState) { assertThat(countryState.getStateKey()).isEqualTo(COUNTRY_STATE_KEY); assertThat(countryState.get()).isEqualTo(GHANA); } - private void assertSteamState(ReadableQueueState steamState) { + private void assertSteamState(ReadableQueueState steamState) { assertThat(steamState.getStateKey()).isEqualTo(STEAM_STATE_KEY); assertThat(steamState.peek()).isEqualTo(ART); } @@ -691,7 +691,7 @@ void knownServiceNameUsingWritableStates() { assertThat(states.isEmpty()).isFalse(); assertThat(states.size()).isEqualTo(4); - final WritableKVState fruitStates = states.get(FRUIT_STATE_KEY); + final WritableKVState fruitStates = states.get(FRUIT_STATE_KEY); assertThat(fruitStates).isNotNull(); final var animalStates = states.get(ANIMAL_STATE_KEY); diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/VirtualMapStateTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/VirtualMapStateTest.java index df94e5eacc1e..c47c2374f4c0 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/VirtualMapStateTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/VirtualMapStateTest.java @@ -162,7 +162,7 @@ void addingServiceTwiceWithDifferentMetadata() { final var fruitMetadata2 = new StateMetadata<>( StateTestBase.FIRST_SERVICE, new TestSchema(1), - StateDefinition.inMemory(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, STRING_CODEC)); + StateDefinition.inMemory(FRUIT_STATE_KEY, ProtoBytes.PROTOBUF, ProtoBytes.PROTOBUF)); virtualMapState.initializeState(fruitMetadata); virtualMapState.initializeState(fruitMetadata2); @@ -394,16 +394,16 @@ void knownServiceNameUsingReadableStates() { assertThat(states.isEmpty()).isFalse(); assertThat(states.size()).isEqualTo(4); // animal and fruit and country and steam - final ReadableKVState fruitState = states.get(FRUIT_STATE_KEY); + final ReadableKVState fruitState = states.get(FRUIT_STATE_KEY); assertFruitState(fruitState); - final ReadableKVState animalState = states.get(ANIMAL_STATE_KEY); + final ReadableKVState animalState = states.get(ANIMAL_STATE_KEY); assertAnimalState(animalState); - final ReadableSingletonState countryState = states.getSingleton(COUNTRY_STATE_KEY); + final ReadableSingletonState countryState = states.getSingleton(COUNTRY_STATE_KEY); assertCountryState(countryState); - final ReadableQueueState steamState = states.getQueue(STEAM_STATE_KEY); + final ReadableQueueState steamState = states.getQueue(STEAM_STATE_KEY); assertSteamState(steamState); // And the states we got back CANNOT be cast to WritableState @@ -461,10 +461,12 @@ void testGetInfoJson() { + "{\"First-Service.STEAM\":{\"head\":1,\"path\":14,\"tail\":3}}," + "\"VirtualMapMetadata\":{\"firstLeafPath\":8,\"lastLeafPath\":16}," + "\"Singletons\":" - + "{\"First-Service.COUNTRY\":{\"path\":10,\"value\":\"Ghana\"}}}"); + + "{\"First-Service.COUNTRY\":{\"path\":10,\"value\":\"" + + GHANA + + "\"}}}"); } - private static void assertFruitState(ReadableKVState fruitState) { + private static void assertFruitState(ReadableKVState fruitState) { assertThat(fruitState).isNotNull(); assertThat(fruitState.get(A_KEY)).isSameAs(APPLE); assertThat(fruitState.get(B_KEY)).isSameAs(BANANA); @@ -475,7 +477,7 @@ private static void assertFruitState(ReadableKVState fruitSt assertThat(fruitState.get(G_KEY)).isNull(); } - private void assertAnimalState(ReadableKVState animalState) { + private void assertAnimalState(ReadableKVState animalState) { assertThat(animalState).isNotNull(); assertThat(animalState.get(A_KEY)).isNull(); assertThat(animalState.get(B_KEY)).isNull(); @@ -486,12 +488,12 @@ private void assertAnimalState(ReadableKVState animalState) assertThat(animalState.get(G_KEY)).isNull(); } - private void assertCountryState(ReadableSingletonState countryState) { + private void assertCountryState(ReadableSingletonState countryState) { assertThat(countryState.getStateKey()).isEqualTo(COUNTRY_STATE_KEY); assertThat(countryState.get()).isEqualTo(GHANA); } - private void assertSteamState(ReadableQueueState steamState) { + private void assertSteamState(ReadableQueueState steamState) { assertThat(steamState.getStateKey()).isEqualTo(STEAM_STATE_KEY); assertThat(steamState.peek()).isEqualTo(ART); } @@ -622,7 +624,7 @@ void knownServiceNameUsingWritableStates() { assertThat(states.isEmpty()).isFalse(); assertThat(states.size()).isEqualTo(4); - final WritableKVState fruitStates = states.get(FRUIT_STATE_KEY); + final WritableKVState fruitStates = states.get(FRUIT_STATE_KEY); assertThat(fruitStates).isNotNull(); final var animalStates = states.get(ANIMAL_STATE_KEY); diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java index 50d877899c2a..a56023215a71 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskReadableStateTest.java @@ -28,8 +28,8 @@ void setUp() { setupFruitVirtualMap(); } - private void add(String serviceName, String stateKey, ProtoBytes key, String value) { - addKvState(fruitVirtualMap, serviceName, stateKey, STRING_CODEC, key, value); + private void add(String serviceName, String stateKey, ProtoBytes key, ProtoBytes value) { + addKvState(fruitVirtualMap, serviceName, stateKey, key, value); } @Nested @@ -79,7 +79,7 @@ void stateKey() { @Nested @DisplayName("Query Tests") final class QueryTest { - private OnDiskReadableKVState state; + private OnDiskReadableKVState state; @BeforeEach void setUp() { diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java index 1cfff6708e93..90845aac368c 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/disk/OnDiskWritableStateTest.java @@ -5,6 +5,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import com.hedera.hapi.node.state.primitives.ProtoBytes; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.ParseException; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.merkledb.test.fixtures.MerkleDbTestUtils; @@ -83,14 +84,14 @@ void tearDown() throws IOException { } } - private void add(String serviceName, String stateKey, ProtoBytes key, String value) { - addKvState(fruitVirtualMap, serviceName, stateKey, STRING_CODEC, key, value); + private void add(String serviceName, String stateKey, ProtoBytes key, ProtoBytes value) { + addKvState(fruitVirtualMap, serviceName, stateKey, key, value); } @Nested @DisplayName("Query Tests") final class QueryTest { - private OnDiskWritableKVState state; + private OnDiskWritableKVState state; @BeforeEach void setUp() { @@ -117,9 +118,9 @@ void get() { @Test @DisplayName("Iteration includes both mutations and committed state") void iterateIncludesMutations() { - add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, A_KEY, "Apple"); - add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, B_KEY, "Banana"); - state.put(C_KEY, "Cherry"); + add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, A_KEY, APPLE); + add(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, B_KEY, BANANA); + state.put(C_KEY, toProtoBytes("Cherry")); final var actual = StreamSupport.stream(Spliterators.spliterator(state.keys(), 3, 0), false) .toList(); assertThat(actual).containsExactlyInAnyOrder(A_KEY, B_KEY, C_KEY); @@ -138,7 +139,7 @@ void tearDown() throws IOException { @Nested @DisplayName("Mutation Tests") final class MutationTest { - private OnDiskWritableKVState state; + private OnDiskWritableKVState state; @BeforeEach void setUp() { @@ -154,9 +155,10 @@ boolean merkleMapContainsKey(ProtoBytes key) { return fruitVirtualMap.containsKey(keyBytes); } - String readValueFromMerkleMap(ProtoBytes key) { + ProtoBytes readValueFromMerkleMap(ProtoBytes key) { final Bytes keyBytes = StateUtils.getVirtualMapKeyForKv(FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, key); - return fruitVirtualMap.get(keyBytes, STRING_CODEC); + final VirtualMapValue virtualMapValue = fruitVirtualMap.get(keyBytes, VirtualMapValue.PROTOBUF); + return virtualMapValue != null ? virtualMapValue.value().as() : null; } @Test diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryReadableStateTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryReadableStateTest.java index 553f464b261c..771372f346b7 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryReadableStateTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryReadableStateTest.java @@ -61,15 +61,20 @@ void stateKey() { } } - private void add(ProtoBytes key, String value) { + private void add(ProtoBytes key, ProtoBytes value) { addKvState( - fruitMerkleMap, inMemoryValueClassId(FRUIT_STATE_KEY), ProtoBytes.PROTOBUF, STRING_CODEC, key, value); + fruitMerkleMap, + inMemoryValueClassId(FRUIT_STATE_KEY), + ProtoBytes.PROTOBUF, + ProtoBytes.PROTOBUF, + key, + value); } @Nested @DisplayName("Query Tests") final class QueryTest { - private InMemoryReadableKVState state; + private InMemoryReadableKVState state; @BeforeEach void setUp() { diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryWritableStateTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryWritableStateTest.java index 9afde07d4c0f..0cf821d2af44 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryWritableStateTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/memory/InMemoryWritableStateTest.java @@ -32,7 +32,7 @@ void nullServiceNameThrows() { FRUIT_STATE_KEY, inMemoryValueClassId(FRUIT_STATE_KEY), ProtoBytes.PROTOBUF, - STRING_CODEC, + ProtoBytes.PROTOBUF, fruitMerkleMap)) .isInstanceOf(NullPointerException.class); } @@ -46,7 +46,7 @@ void nullStateKeyThrows() { null, inMemoryValueClassId(FRUIT_STATE_KEY), ProtoBytes.PROTOBUF, - STRING_CODEC, + ProtoBytes.PROTOBUF, fruitMerkleMap)) .isInstanceOf(NullPointerException.class); } @@ -59,8 +59,8 @@ void nullMerkleMapThrows() { FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, inMemoryValueClassId(FRUIT_STATE_KEY), - STRING_CODEC, - STRING_CODEC, + ProtoBytes.PROTOBUF, + ProtoBytes.PROTOBUF, null)) .isInstanceOf(NullPointerException.class); } @@ -73,25 +73,30 @@ void stateKey() { } } - private InMemoryWritableKVState createState() { + private InMemoryWritableKVState createState() { return new InMemoryWritableKVState<>( FRUIT_SERVICE_NAME, FRUIT_STATE_KEY, inMemoryValueClassId(FRUIT_STATE_KEY), ProtoBytes.PROTOBUF, - STRING_CODEC, + ProtoBytes.PROTOBUF, fruitMerkleMap); } - private void add(ProtoBytes key, String value) { + private void add(ProtoBytes key, ProtoBytes value) { addKvState( - fruitMerkleMap, inMemoryValueClassId(FRUIT_STATE_KEY), ProtoBytes.PROTOBUF, STRING_CODEC, key, value); + fruitMerkleMap, + inMemoryValueClassId(FRUIT_STATE_KEY), + ProtoBytes.PROTOBUF, + ProtoBytes.PROTOBUF, + key, + value); } @Nested @DisplayName("Query Tests") final class QueryTest { - private InMemoryWritableKVState state; + private InMemoryWritableKVState state; @BeforeEach void setUp() { @@ -124,7 +129,7 @@ void iterate() { @Nested @DisplayName("Mutation Tests") final class MutationTest { - private InMemoryWritableKVState state; + private InMemoryWritableKVState state; @BeforeEach void setUp() { @@ -138,7 +143,7 @@ boolean merkleMapContainsKey(ProtoBytes key) { return fruitMerkleMap.containsKey(new InMemoryKey<>(key)); } - String readValueFromMerkleMap(ProtoBytes key) { + ProtoBytes readValueFromMerkleMap(ProtoBytes key) { final var val = fruitMerkleMap.get(new InMemoryKey<>(key)); return val == null ? null : val.getValue(); } diff --git a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/singleton/ValueLeafTest.java b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/singleton/ValueLeafTest.java index dcf6f16bbbd4..9bfc22d23c97 100644 --- a/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/singleton/ValueLeafTest.java +++ b/platform-sdk/swirlds-state-impl/src/test/java/com/swirlds/state/merkle/singleton/ValueLeafTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.swirlds.state.test.fixtures.merkle.MerkleTestBase; import org.junit.jupiter.api.Test; @@ -11,7 +12,7 @@ class ValueLeafTest extends MerkleTestBase { @Test void setValue() { setupSingletonCountry(); - final var leaf = new ValueLeaf<>(singletonClassId(COUNTRY_STATE_KEY), STRING_CODEC, DENMARK); + final var leaf = new ValueLeaf<>(singletonClassId(COUNTRY_STATE_KEY), ProtoBytes.PROTOBUF, DENMARK); assertThat(leaf.getValue()).isEqualTo(DENMARK); leaf.setValue(FRANCE); assertThat(leaf.getValue()).isEqualTo(FRANCE); diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java index a90a0be384c8..084e4f34abf5 100644 --- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java +++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java @@ -11,7 +11,9 @@ import static com.swirlds.state.lifecycle.StateMetadata.computeClassId; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForKv; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapValue; import static com.swirlds.virtualmap.constructable.ConstructableUtils.registerVirtualMapConstructables; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -20,6 +22,7 @@ import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.hedera.hapi.platform.state.SingletonType; import com.hedera.hapi.platform.state.VirtualMapKey; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.pbj.runtime.Codec; import com.hedera.pbj.runtime.OneOf; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -57,6 +60,7 @@ import java.nio.file.Path; import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; import org.hiero.base.constructable.ClassConstructorPair; import org.hiero.base.constructable.ConstructableRegistry; @@ -138,7 +142,7 @@ public class MerkleTestBase extends StateTestBase { // The "FRUIT" Map is part of FIRST_SERVICE protected String fruitLabel; - protected MerkleMap, InMemoryValue> fruitMerkleMap; + protected MerkleMap, InMemoryValue> fruitMerkleMap; // An alternative "FRUIT" Map that is also part of FIRST_SERVICE, but based on VirtualMap protected String fruitVirtualLabel; @@ -146,19 +150,19 @@ public class MerkleTestBase extends StateTestBase { // The "ANIMAL" map is part of FIRST_SERVICE protected String animalLabel; - protected MerkleMap, InMemoryValue> animalMerkleMap; + protected MerkleMap, InMemoryValue> animalMerkleMap; // The "SPACE" map is part of SECOND_SERVICE and uses the long-based keys protected String spaceLabel; - protected MerkleMap, InMemoryValue> spaceMerkleMap; + protected MerkleMap, InMemoryValue> spaceMerkleMap; // The "STEAM" queue is part of FIRST_SERVICE protected String steamLabel; - protected QueueNode steamQueue; + protected QueueNode steamQueue; // The "COUNTRY" singleton is part of FIRST_SERVICE protected String countryLabel; - protected SingletonNode countrySingleton; + protected SingletonNode countrySingleton; /** * This static mock instance will override calls to the static methods in StateUtils @@ -179,10 +183,14 @@ public class MerkleTestBase extends StateTestBase { */ @BeforeAll static void init() { + AtomicBoolean bypassStateIdFor = new AtomicBoolean(false); stateUtilsMock = mockStatic(StateUtils.class, CALLS_REAL_METHODS); stateUtilsMock .when(() -> StateUtils.stateIdFor(anyString(), anyString())) .thenAnswer(invocation -> { + if (bypassStateIdFor.get()) { + return invocation.callRealMethod(); + } try { // First, try calling the real method. return invocation.callRealMethod(); @@ -209,16 +217,17 @@ static void init() { } }); stateUtilsMock - .when(() -> StateUtils.getVirtualMapKeyForKv(anyString(), anyString(), anyString())) + .when(() -> StateUtils.getVirtualMapKeyForKv(anyString(), anyString(), any())) .thenAnswer(invocation -> { try { + bypassStateIdFor.set(true); // First, try calling the real method. return invocation.callRealMethod(); } catch (Exception e) { // The real method couldn't find a valid mapping. final String serviceName = invocation.getArgument(0); final String stateKey = invocation.getArgument(1); - final String keyObject = invocation.getArgument(2); + final ProtoBytes keyObject = invocation.getArgument(2); // We have to map "made up" states to existing ones to keep the compatibility with the protocol // The following states are chosen because they have generic `ProtoBytes` as their key type @@ -230,6 +239,52 @@ static void init() { // Neither the real method nor any test mappings applied. return 65000; } + } finally { + bypassStateIdFor.set(false); + } + }); + stateUtilsMock + .when(() -> StateUtils.getVirtualMapValue(anyString(), anyString(), any())) + .thenAnswer(invocation -> { + try { + bypassStateIdFor.set(true); + // First, try calling the real method. + return invocation.callRealMethod(); + } catch (Exception e) { + // The real method couldn't find a valid mapping. + final String serviceName = invocation.getArgument(0); + final String stateKey = invocation.getArgument(1); + final ProtoBytes valueObject = invocation.getArgument(2); + + // We have to map "made up" states to existing ones to keep the compatibility with the protocol + // The following states are chosen because they have generic `ProtoBytes` as their value type + if (FRUIT_SERVICE_NAME.equals(serviceName) || FRUIT_STATE_KEY.equals(stateKey)) { + return createVirtualMapValue( + VirtualMapValue.ValueOneOfType.FREEZESERVICE_I_UPGRADE_FILE_HASH, valueObject); + } else if (ANIMAL_SERVICE_NAME.equals(serviceName) || ANIMAL_STATE_KEY.equals(stateKey)) { + return createVirtualMapValue( + VirtualMapValue.ValueOneOfType.HISTORYSERVICE_I_LEDGER_ID, valueObject); + } else if (SPACE_SERVICE_NAME.equals(serviceName) || SPACE_STATE_KEY.equals(stateKey)) { + return createVirtualMapValue( + com.hedera.hapi.platform.state.VirtualMapValue.ValueOneOfType + .FILESERVICE_I_UPGRADE_DATA_150, + valueObject); + } else if (STEAM_SERVICE_NAME.equals(serviceName) || STEAM_STATE_KEY.equals(stateKey)) { + return createVirtualMapValue( + com.hedera.hapi.platform.state.VirtualMapValue.ValueOneOfType + .FILESERVICE_I_UPGRADE_DATA_151, + valueObject); + } else if (COUNTRY_SERVICE_NAME.equals(serviceName) || COUNTRY_STATE_KEY.equals(stateKey)) { + return createVirtualMapValue( + com.hedera.hapi.platform.state.VirtualMapValue.ValueOneOfType + .FILESERVICE_I_UPGRADE_DATA_152, + valueObject); + } else { + // Neither the real method nor any test mappings applied. + return 65000; + } + } finally { + bypassStateIdFor.set(false); } }); stateUtilsMock @@ -237,6 +292,7 @@ static void init() { .thenAnswer(invocation -> { try { // First, try calling the real method. + bypassStateIdFor.set(true); return invocation.callRealMethod(); } catch (Exception e) { // The real method couldn't find a valid mapping. @@ -252,12 +308,15 @@ static void init() { // Neither the real method nor any test mappings applied. return 65000; } + } finally { + bypassStateIdFor.set(false); } }); stateUtilsMock .when(() -> StateUtils.getVirtualMapKeyForSingleton(anyString(), anyString())) .thenAnswer(invocation -> { try { + bypassStateIdFor.set(true); // First, try calling the real method. return invocation.callRealMethod(); } catch (Exception e) { @@ -277,13 +336,14 @@ static void init() { // Neither the real method nor any test mappings applied. return 65000; } + } finally { + bypassStateIdFor.set(false); } }); } - private static Bytes createVirtualMapKeyForKv(VirtualMapKey.KeyOneOfType type, String keyObject) { - return VirtualMapKey.PROTOBUF.toBytes( - new VirtualMapKey(new OneOf<>(type, new ProtoBytes(Bytes.wrap(keyObject))))); + private static Bytes createVirtualMapKeyForKv(VirtualMapKey.KeyOneOfType type, ProtoBytes keyObject) { + return VirtualMapKey.PROTOBUF.toBytes(new VirtualMapKey(new OneOf<>(type, keyObject))); } private static Bytes createVirtualMapKeyForSingleton(SingletonType type) { @@ -294,6 +354,10 @@ private static Bytes createVirtualMapKeyForQueue(VirtualMapKey.KeyOneOfType type return VirtualMapKey.PROTOBUF.toBytes(new VirtualMapKey(new OneOf<>(type, index))); } + private static VirtualMapValue createVirtualMapValue(VirtualMapValue.ValueOneOfType type, ProtoBytes valueObject) { + return new VirtualMapValue(new OneOf<>(type, valueObject)); + } + /** Sets up the "Fruit" merkle map, label, and metadata. */ protected void setupFruitMerkleMap() { fruitLabel = StateMetadata.computeLabel(FIRST_SERVICE, FRUIT_STATE_KEY); @@ -336,7 +400,7 @@ protected void setupSingletonCountry() { FIRST_SERVICE, COUNTRY_STATE_KEY, computeClassId(FIRST_SERVICE, COUNTRY_STATE_KEY, TEST_VERSION, SINGLETON_CLASS_ID_SUFFIX), - STRING_CODEC, + ProtoBytes.PROTOBUF, AUSTRALIA); } @@ -347,7 +411,7 @@ protected void setupSteamQueue() { STEAM_STATE_KEY, computeClassId(FIRST_SERVICE, STEAM_STATE_KEY, TEST_VERSION, QUEUE_NODE_CLASS_ID_SUFFIX), computeClassId(FIRST_SERVICE, STEAM_STATE_KEY, TEST_VERSION, SINGLETON_CLASS_ID_SUFFIX), - STRING_CODEC); + ProtoBytes.PROTOBUF); } /** Sets up the {@link #registry}, ready to be used for serialization tests */ @@ -395,31 +459,30 @@ protected VirtualMap createVirtualMap(String label) { /** A convenience method for adding a k/v pair to a merkle map */ protected void addKvState( - MerkleMap, InMemoryValue> map, + MerkleMap, InMemoryValue> map, long inMemoryValueClassId, Codec keyCodec, - Codec valueCodec, + Codec valueCodec, ProtoBytes key, - String value) { + ProtoBytes value) { final var k = new InMemoryKey<>(key); map.put(k, new InMemoryValue<>(inMemoryValueClassId, keyCodec, valueCodec, k, value)); } /** A convenience method for adding a singleton state to a virtual map */ - protected void addSingletonState( - VirtualMap map, String serviceName, String stateKey, Codec valueCodec, String value) { - map.put(getVirtualMapKeyForSingleton(serviceName, stateKey), value, valueCodec); + protected void addSingletonState(VirtualMap map, String serviceName, String stateKey, ProtoBytes value) { + map.put( + getVirtualMapKeyForSingleton(serviceName, stateKey), + getVirtualMapValue(serviceName, stateKey, value), + VirtualMapValue.PROTOBUF); } /** A convenience method for adding a k/v state to a virtual map */ - protected void addKvState( - VirtualMap map, - String serviceName, - String stateKey, - Codec valueCodec, - ProtoBytes key, - String value) { - map.put(getVirtualMapKeyForKv(serviceName, stateKey, key), value, valueCodec); + protected void addKvState(VirtualMap map, String serviceName, String stateKey, ProtoBytes key, ProtoBytes value) { + map.put( + getVirtualMapKeyForKv(serviceName, stateKey, key), + getVirtualMapValue(serviceName, stateKey, value), + VirtualMapValue.PROTOBUF); } /** A convenience method used to serialize a merkle tree */ From e87454533649a72b3271a7d2c3c55cb152a024fc Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Thu, 24 Jul 2025 20:22:46 +0300 Subject: [PATCH 4/9] fix tests Signed-off-by: Nikita Lebedev --- .../swirlds/state/merkle/MerkleStateRoot.java | 23 +++++--- .../com/swirlds/state/merkle/StateUtils.java | 56 +++++++++---------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java index fdf0afe1702a..4f48fc71e79b 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java @@ -7,11 +7,12 @@ import static com.swirlds.state.StateChangeListener.StateType.QUEUE; import static com.swirlds.state.StateChangeListener.StateType.SINGLETON; import static com.swirlds.state.lifecycle.StateMetadata.computeLabel; -import static com.swirlds.state.merkle.StateUtils.createVirtualMapKeyBytesForKV; +import static com.swirlds.state.merkle.StateUtils.createVirtualMapBytesForKV; import static com.swirlds.state.merkle.StateUtils.decomposeLabel; import static com.swirlds.state.merkle.StateUtils.getQueueStateVirtualMapValue; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForQueue; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapValue; import static com.swirlds.state.merkle.VirtualMapState.VM_LABEL; import static java.util.Objects.requireNonNull; @@ -1068,10 +1069,12 @@ private void migrateSingletonStates( logger.info(STARTUP.getMarker(), "\nMigrating {}...", singletonStateLabel); long migrationStartTime = System.currentTimeMillis(); - final var codec = originalStore.getCodec(); final var value = Objects.requireNonNull(originalStore.getValue(), "Null value is not expected here"); - virtualMap.put(getVirtualMapKeyForSingleton(serviceName, stateKey), value, codec); + + final Bytes key = getVirtualMapKeyForSingleton(serviceName, stateKey); + final VirtualMapValue virtualMapValue = getVirtualMapValue(serviceName, stateKey, value); + virtualMap.put(key, virtualMapValue, VirtualMapValue.PROTOBUF); long migrationTimeMs = System.currentTimeMillis() - migrationStartTime; logger.info( @@ -1141,7 +1144,6 @@ private void migrateQueueStates( long tail = 1; for (ValueLeaf leaf : originalStore) { - final var codec = leaf.getCodec(); final var value = Objects.requireNonNull(leaf.getValue(), "Null value is not expected here"); VirtualMap currentMap = virtualMapRef.get(); @@ -1151,7 +1153,10 @@ private void migrateQueueStates( older.release(); virtualMapRef.set(currentMap); } - virtualMapRef.get().put(getVirtualMapKeyForQueue(serviceName, stateKey, tail++), value, codec); + + final Bytes key = getVirtualMapKeyForQueue(serviceName, stateKey, tail++); + final VirtualMapValue virtualMapValue = getVirtualMapValue(serviceName, stateKey, value); + virtualMapRef.get().put(key, virtualMapValue, VirtualMapValue.PROTOBUF); } final var queueState = new QueueState(head, tail); @@ -1239,9 +1244,11 @@ private void migrateKVStates( older.release(); virtualMapRef.set(currentMap); } - final var keyBytes = Bytes.wrap(createVirtualMapKeyBytesForKV( + final Bytes keyBytes = Bytes.wrap(createVirtualMapBytesForKV( serviceName, stateKey, pair.key().toByteArray())); - virtualMapRef.get().putBytes(keyBytes, pair.value()); + final Bytes valueBytes = Bytes.wrap(createVirtualMapBytesForKV( + serviceName, stateKey, pair.value().toByteArray())); + virtualMapRef.get().putBytes(keyBytes, valueBytes); }; try { @@ -1305,7 +1312,7 @@ private static void validateKVStateMigrated( while (merkleNodeMerkleIterator.hasNext()) { MerkleNode next = merkleNodeMerkleIterator.next(); if (next instanceof VirtualLeafNode virtualLeafNode) { - final var keyBytes = Bytes.wrap(createVirtualMapKeyBytesForKV( + final var keyBytes = Bytes.wrap(createVirtualMapBytesForKV( serviceName, stateKey, virtualLeafNode.getKey().toByteArray())); assert virtualMap.containsKey(keyBytes); } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java index 2965eed2d704..c0af9c665c02 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java @@ -478,34 +478,6 @@ public static Bytes getVirtualMapKeyForKv( VirtualMapKey.KeyOneOfType.fromProtobufOrdinal(getValidatedStateId(serviceName, stateKey)), key))); } - /** - * Creates Protocol Buffer encoded byte array for a VirtualMapKey field. - * Follows protobuf encoding format: tag (field number + wire type), length, and value. - * - * @param serviceName the service name - * @param stateKey the state key - * @param keyObjectBytes the serialized key object - * @return Properly encoded Protocol Buffer byte array - * @throws IllegalArgumentException if the derived state ID is not within the range [0..65535] - */ - public static byte[] createVirtualMapKeyBytesForKV( - @NonNull final String serviceName, @NonNull final String stateKey, byte[] keyObjectBytes) { - final int stateId = getValidatedStateId(serviceName, stateKey); - // This matches the Protocol Buffer tag format: (field_number << TAG_TYPE_BITS) | wire_type - int tag = (stateId << TAG_FIELD_OFFSET) | WIRE_TYPE_DELIMITED.ordinal(); - - ByteBuffer byteBuffer = ByteBuffer.allocate(sizeOfVarInt32(tag) - + sizeOfVarInt32(keyObjectBytes.length) /* length */ - + keyObjectBytes.length /* key bytes */); - BufferedData bufferedData = BufferedData.wrap(byteBuffer); - - bufferedData.writeVarInt(tag, false); - bufferedData.writeVarInt(keyObjectBytes.length, false); - bufferedData.writeBytes(keyObjectBytes); - - return byteBuffer.array(); - } - /** * Creates an instance of {@link VirtualMapValue} which is stored in a {@link com.swirlds.virtualmap.VirtualMap}. * @@ -531,4 +503,32 @@ public static VirtualMapValue getVirtualMapValue( public static VirtualMapValue getQueueStateVirtualMapValue(@NonNull final QueueState queueState) { return new VirtualMapValue(new OneOf<>(VirtualMapValue.ValueOneOfType.QUEUE_STATE, queueState)); } + + /** + * Creates Protocol Buffer encoded byte array for either a {@link VirtualMapKey} or a {@link VirtualMapValue} field. + * Follows protobuf encoding format: tag (field number + wire type), length, and value. + * + * @param serviceName the service name + * @param stateKey the state key + * @param objectBytes the serialized key or value object + * @return Properly encoded Protocol Buffer byte array + * @throws IllegalArgumentException if the derived state ID is not within the range [0..65535] + */ + public static byte[] createVirtualMapBytesForKV( + @NonNull final String serviceName, @NonNull final String stateKey, byte[] objectBytes) { + final int stateId = getValidatedStateId(serviceName, stateKey); + // This matches the Protocol Buffer tag format: (field_number << TAG_TYPE_BITS) | wire_type + int tag = (stateId << TAG_FIELD_OFFSET) | WIRE_TYPE_DELIMITED.ordinal(); + + ByteBuffer byteBuffer = ByteBuffer.allocate(sizeOfVarInt32(tag) + + sizeOfVarInt32(objectBytes.length) /* length */ + + objectBytes.length /* key bytes */); + BufferedData bufferedData = BufferedData.wrap(byteBuffer); + + bufferedData.writeVarInt(tag, false); + bufferedData.writeVarInt(objectBytes.length, false); + bufferedData.writeBytes(objectBytes); + + return byteBuffer.array(); + } } From 10cba7880e5659bed693c1ecb4ae063862185d68 Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Thu, 24 Jul 2025 20:25:25 +0300 Subject: [PATCH 5/9] address review comments Signed-off-by: Nikita Lebedev --- .../com/swirlds/state/merkle/disk/OnDiskQueueHelper.java | 9 +-------- .../state/merkle/disk/OnDiskWritableQueueState.java | 2 +- .../state/test/fixtures/merkle/MerkleTestBase.java | 1 + 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java index 1287063fcf61..59d3beb52105 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskQueueHelper.java @@ -129,14 +129,7 @@ public E getFromStore(final long index) { public QueueState getState() { final VirtualMapValue virtualMapValue = virtualMap.get(getVirtualMapKeyForSingleton(serviceName, stateKey), VirtualMapValue.PROTOBUF); - final QueueState state = - virtualMapValue != null ? virtualMapValue.value().as() : null; - if (state == null) { - return null; - } - // TODO: check it this can be removed now - // FUTURE WORK: optimize performance here, see https://github.com/hiero-ledger/hiero-consensus-node/issues/19670 - return state; // new QueueState(state.head(), state.tail()); + return virtualMapValue != null ? virtualMapValue.value().as() : null; } /** diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java index 8c009f61f05e..604a84b0f6c1 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java @@ -51,7 +51,7 @@ protected void addToDataSource(@NonNull E element) { QueueState state = onDiskQueueHelper.getState(); if (state == null) { // Adding to this Queue State first time - initialize QueueState. - state = QueueState.newBuilder().head(1).tail(1).build(); + state = new QueueState(1, 1); } final Bytes keyBytes = getVirtualMapKeyForQueue(serviceName, stateKey, state.tail()); diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java index 084e4f34abf5..e4968bb9d785 100644 --- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java +++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleTestBase.java @@ -183,6 +183,7 @@ public class MerkleTestBase extends StateTestBase { */ @BeforeAll static void init() { + // Static flag to bypass stateIdFor mock during calls from other mocked methods below AtomicBoolean bypassStateIdFor = new AtomicBoolean(false); stateUtilsMock = mockStatic(StateUtils.class, CALLS_REAL_METHODS); stateUtilsMock From 2f32fda5d93b8193467c7b646a3811b910ad723a Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Fri, 25 Jul 2025 15:51:50 +0300 Subject: [PATCH 6/9] Address review comments Signed-off-by: Nikita Lebedev --- .../platform/state/virtual_map_state.proto | 38 ++++++++++++++----- .../swirlds/state/merkle/MerkleStateRoot.java | 11 ++---- .../com/swirlds/state/merkle/StateUtils.java | 18 ++++----- .../merkle/disk/OnDiskWritableQueueState.java | 11 +----- 4 files changed, 43 insertions(+), 35 deletions(-) diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto index a6cd45bc1fce..5b33595c8e8e 100644 --- a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto +++ b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto @@ -1,3 +1,12 @@ +/** + * # Virtual Map State + * Messages that define the type of keys and values used in the Virtual Map State. + * + * The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + * "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + * document are to be interpreted as described in [RFC2119](https://www.ietf.org/rfc/rfc2119) + * and clarified in [RFC8174](https://www.ietf.org/rfc/rfc8174). + */ syntax = "proto3"; package com.hedera.hapi.platform.state; @@ -64,18 +73,22 @@ message VirtualLeaf { } /** - * This message defines the queue state used in the Virtual Map State. + * This message defines the internal state of a queue state. + * Note that queue state is not a state of type Queue (versus Singleton or KV). + * This state contains two pointers (longs), which indicate at what position a new element is put to the queue (tail) + * or removed from the queue (head). These aren't indices in any array or whatsoever, they are just numbers, + * which are increased on every operation, used to maintain elements order in the queue. */ message QueueState { /** * A head of a queue.
- * Points to the index of the front of a queue. + * Indicate at what position a new element is removed from the queue. */ uint64 head = 1; /** * A tail of a queue.
- * Points to the index where of the back of a queue. + * Indicate at what position a new element is put to the queue. */ uint64 tail = 2; @@ -84,7 +97,7 @@ message QueueState { /** * This message defines the type of keys used in the Virtual Map State. * Note that `_I_` plays a role of a reliable separator between the service name - * and the state name, which is will be present in all types of code generation results as just underscores + * and the state name, which will be present in all types of code generation results as just underscores * may be deleted and create unexpected collisions. */ message VirtualMapKey { @@ -300,7 +313,7 @@ message VirtualMapKey { /** * This message defines the type of values used in the Virtual Map State. * Note that `_I_` plays a role of a reliable separator between the service name - * and the state name, which is will be present in all types of code generation results as just underscores + * and the state name, which will be present in all types of code generation results as just underscores * may be deleted and create unexpected collisions. */ message VirtualMapValue { @@ -556,6 +569,16 @@ message VirtualMapValue { */ proto.TransactionReceiptEntries RecordCacheTransactionReceiptQueue = 126; + /** + * Metadata of the queue state.
+ * Reasons for the field number: + *

    + *
  • Intermediate state types are expected to be rare (field numbers 8000-9999 are allocated for that purpose).
  • + *
  • QueueState represents internal head/tail pointers of a queue, not a standalone Queue type (as with Singleton or KV).
  • + *
+ */ + QueueState queue_state = 8001; + /** * Metadata of the `150` upgrade file data queue. */ @@ -606,11 +629,6 @@ message VirtualMapValue { */ proto.ProtoBytes FileService_I_UPGRADE_DATA_159 = 10010; - /** - * Metadata of the queue state. - */ - QueueState queue_state = 11111; // pick the number - } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java index 4f48fc71e79b..a340bc147b0e 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/MerkleStateRoot.java @@ -7,11 +7,11 @@ import static com.swirlds.state.StateChangeListener.StateType.QUEUE; import static com.swirlds.state.StateChangeListener.StateType.SINGLETON; import static com.swirlds.state.lifecycle.StateMetadata.computeLabel; -import static com.swirlds.state.merkle.StateUtils.createVirtualMapBytesForKV; import static com.swirlds.state.merkle.StateUtils.decomposeLabel; import static com.swirlds.state.merkle.StateUtils.getQueueStateVirtualMapValue; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForQueue; import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyForSingleton; +import static com.swirlds.state.merkle.StateUtils.getVirtualMapKeyValueBytes; import static com.swirlds.state.merkle.StateUtils.getVirtualMapValue; import static com.swirlds.state.merkle.VirtualMapState.VM_LABEL; import static java.util.Objects.requireNonNull; @@ -1244,10 +1244,8 @@ private void migrateKVStates( older.release(); virtualMapRef.set(currentMap); } - final Bytes keyBytes = Bytes.wrap(createVirtualMapBytesForKV( - serviceName, stateKey, pair.key().toByteArray())); - final Bytes valueBytes = Bytes.wrap(createVirtualMapBytesForKV( - serviceName, stateKey, pair.value().toByteArray())); + final Bytes keyBytes = getVirtualMapKeyValueBytes(serviceName, stateKey, pair.key()); + final Bytes valueBytes = getVirtualMapKeyValueBytes(serviceName, stateKey, pair.value()); virtualMapRef.get().putBytes(keyBytes, valueBytes); }; @@ -1312,8 +1310,7 @@ private static void validateKVStateMigrated( while (merkleNodeMerkleIterator.hasNext()) { MerkleNode next = merkleNodeMerkleIterator.next(); if (next instanceof VirtualLeafNode virtualLeafNode) { - final var keyBytes = Bytes.wrap(createVirtualMapBytesForKV( - serviceName, stateKey, virtualLeafNode.getKey().toByteArray())); + final var keyBytes = getVirtualMapKeyValueBytes(serviceName, stateKey, virtualLeafNode.getKey()); assert virtualMap.containsKey(keyBytes); } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java index c0af9c665c02..ed371e1f7735 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java @@ -479,7 +479,7 @@ public static Bytes getVirtualMapKeyForKv( } /** - * Creates an instance of {@link VirtualMapValue} which is stored in a {@link com.swirlds.virtualmap.VirtualMap}. + * Creates an instance of {@link VirtualMapValue} which is stored in a state. * * @param the type of the value * @param serviceName the service name @@ -495,7 +495,7 @@ public static VirtualMapValue getVirtualMapValue( } /** - * Creates an instance of {@link VirtualMapValue} for a {@link com.hedera.hapi.platform.state.QueueState} which is stored in a {@link com.swirlds.virtualmap.VirtualMap}. + * Creates an instance of {@link VirtualMapValue} for a {@link com.hedera.hapi.platform.state.QueueState} which is stored in a state. * * @param queueState the value object * @return a {@link VirtualMapValue} for {@link com.hedera.hapi.platform.state.QueueState} in a {@link com.swirlds.virtualmap.VirtualMap} @@ -514,21 +514,21 @@ public static VirtualMapValue getQueueStateVirtualMapValue(@NonNull final QueueS * @return Properly encoded Protocol Buffer byte array * @throws IllegalArgumentException if the derived state ID is not within the range [0..65535] */ - public static byte[] createVirtualMapBytesForKV( - @NonNull final String serviceName, @NonNull final String stateKey, byte[] objectBytes) { + public static Bytes getVirtualMapKeyValueBytes( + @NonNull final String serviceName, @NonNull final String stateKey, @NonNull final Bytes objectBytes) { final int stateId = getValidatedStateId(serviceName, stateKey); // This matches the Protocol Buffer tag format: (field_number << TAG_TYPE_BITS) | wire_type int tag = (stateId << TAG_FIELD_OFFSET) | WIRE_TYPE_DELIMITED.ordinal(); + int length = Math.toIntExact(objectBytes.length()); - ByteBuffer byteBuffer = ByteBuffer.allocate(sizeOfVarInt32(tag) - + sizeOfVarInt32(objectBytes.length) /* length */ - + objectBytes.length /* key bytes */); + ByteBuffer byteBuffer = + ByteBuffer.allocate(sizeOfVarInt32(tag) + sizeOfVarInt32(length) /* length */ + length /* key bytes */); BufferedData bufferedData = BufferedData.wrap(byteBuffer); bufferedData.writeVarInt(tag, false); - bufferedData.writeVarInt(objectBytes.length, false); + bufferedData.writeVarInt(length, false); bufferedData.writeBytes(objectBytes); - return byteBuffer.array(); + return Bytes.wrap(byteBuffer.array()); } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java index 604a84b0f6c1..8b40621e9531 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskWritableQueueState.java @@ -58,11 +58,8 @@ protected void addToDataSource(@NonNull E element) { final VirtualMapValue virtualMapValue = getVirtualMapValue(serviceName, stateKey, element); virtualMap.put(keyBytes, virtualMapValue, VirtualMapValue.PROTOBUF); - // increment tail and update state - final QueueState stateIncrementedTail = - state.copyBuilder().tail(state.tail() + 1).build(); - onDiskQueueHelper.updateState(stateIncrementedTail); + onDiskQueueHelper.updateState(new QueueState(state.head(), state.tail() + 1)); // Log to transaction state log, what was added logQueueAdd(computeLabel(serviceName, stateKey), element); @@ -75,12 +72,8 @@ protected void removeFromDataSource() { if (!OnDiskQueueHelper.isEmpty(state)) { final VirtualMapValue virtualMapValue = virtualMap.remove( getVirtualMapKeyForQueue(serviceName, stateKey, state.head()), VirtualMapValue.PROTOBUF); - // increment head and update state - final QueueState stateIncrementedHead = - state.copyBuilder().head(state.head() + 1).build(); - onDiskQueueHelper.updateState(stateIncrementedHead); - + onDiskQueueHelper.updateState(new QueueState(state.head() + 1, state.tail())); final var removedValue = virtualMapValue != null ? virtualMapValue.value().as() : null; // Log to transaction state log, what was removed From d3160e6bb66e9a1be11bd9108ee4a54a719bfcec Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Tue, 29 Jul 2025 14:51:35 +0300 Subject: [PATCH 7/9] additional doc comment in virtual_map_state.proto Signed-off-by: Nikita Lebedev --- .../proto/platform/state/virtual_map_state.proto | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto index 5b33595c8e8e..613a1d470beb 100644 --- a/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto +++ b/hapi/hedera-protobuf-java-api/src/main/proto/platform/state/virtual_map_state.proto @@ -314,7 +314,17 @@ message VirtualMapKey { * This message defines the type of values used in the Virtual Map State. * Note that `_I_` plays a role of a reliable separator between the service name * and the state name, which will be present in all types of code generation results as just underscores - * may be deleted and create unexpected collisions. + * may be deleted and create unexpected collisions.
+ * ID ranges for VirtualMapValue types: + *
    + *
  1. 0–7999: Core state entries. Reserved for all platform states + * (accounts, tokens, schedules, consensus topics, etc.).
  2. + *
  3. 8000–9999: Auxiliary/helper entries. Reserved for internal or support + * messages that are not themselves states (e.g. `QueueState`).
  4. + *
  5. 10000 and above: File/blob entries. Reserved for file service or any + * large-blob content stored via the virtual map (for example, file + * contents, etc.).
  6. + *
*/ message VirtualMapValue { oneof value{ From 28585a23b19ca7a2f8ab6a0081b3bf424754e6ac Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Tue, 29 Jul 2025 15:20:52 +0300 Subject: [PATCH 8/9] update state validator Signed-off-by: Nikita Lebedev --- .../validators/servicesstate/AccountValidator.java | 14 +++++++++----- .../servicesstate/TokenRelationsIntegrity.java | 12 ++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java index 88b5547a5bb0..e204709cff15 100644 --- a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java +++ b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java @@ -5,9 +5,11 @@ import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyStateId; import static com.swirlds.state.merkle.StateUtils.stateIdFor; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.state.token.Account; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.node.app.ids.EntityIdService; import com.hedera.node.app.ids.ReadableEntityIdStoreImpl; import com.hedera.node.app.service.token.impl.TokenServiceImpl; @@ -26,7 +28,6 @@ import com.swirlds.state.spi.ReadableKVState; import com.swirlds.virtualmap.VirtualMap; import com.swirlds.virtualmap.VirtualMapMigration; -import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -47,7 +48,7 @@ public class AccountValidator { final long TOTAL_tHBAR_SUPPLY = 5_000_000_000_000_000_000L; @Test - void validate(DeserializedSignedState deserializedState, Report report) throws IOException, InterruptedException { + void validate(DeserializedSignedState deserializedState, Report report) throws InterruptedException { final MerkleNodeState merkleNodeState = deserializedState.reservedSignedState().get().getState(); @@ -72,10 +73,13 @@ void validate(DeserializedSignedState deserializedState, Report report) throws I InterruptableConsumer> handler = pair -> { final Bytes keyBytes = pair.left(); - final int readStateId = extractVirtualMapKeyStateId(keyBytes); - if (readStateId == targetStateId) { + final Bytes valueBytes = pair.right(); + final int readKeyStateId = extractVirtualMapKeyStateId(keyBytes); + final int readValueStateId = extractVirtualMapKeyStateId(valueBytes); + if ((readKeyStateId == targetStateId) && (readValueStateId == targetStateId)) { try { - final Account account = Account.PROTOBUF.parse(pair.right()); + final VirtualMapValue virtualMapValue = VirtualMapValue.PROTOBUF.parse(valueBytes); + final Account account = virtualMapValue.value().as(); final long tinybarBalance = account.tinybarBalance(); assertTrue(tinybarBalance >= 0); totalBalance.addAndGet(tinybarBalance); diff --git a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java index 5d12ced7b8f7..fc9d4940aaa5 100644 --- a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java +++ b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.statevalidation.validators.servicesstate; -import static com.hedera.pbj.runtime.ProtoParserTools.readNextFieldNumber; import static com.hedera.statevalidation.validators.ParallelProcessingUtil.VALIDATOR_FORK_JOIN_POOL; +import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyStateId; import static com.swirlds.state.merkle.StateUtils.stateIdFor; import static org.junit.jupiter.api.Assertions.*; @@ -13,6 +13,7 @@ import com.hedera.hapi.node.state.token.Token; import com.hedera.hapi.node.state.token.TokenRelation; import com.hedera.hapi.platform.state.VirtualMapKey; +import com.hedera.hapi.platform.state.VirtualMapValue; import com.hedera.node.app.ids.EntityIdService; import com.hedera.node.app.ids.ReadableEntityIdStoreImpl; import com.hedera.node.app.service.token.impl.TokenServiceImpl; @@ -77,8 +78,10 @@ void validate(DeserializedSignedState deserializedState, Report report) throws I InterruptableConsumer> handler = pair -> { final Bytes keyBytes = pair.left(); - final int readStateId = readNextFieldNumber(keyBytes.toReadableSequentialData()); - if (readStateId == targetStateId) { + final Bytes valueBytes = pair.right(); + final int readKeyStateId = extractVirtualMapKeyStateId(keyBytes); + final int readValueStateId = extractVirtualMapKeyStateId(valueBytes); + if ((readKeyStateId == targetStateId) && (readValueStateId == targetStateId)) { try { final VirtualMapKey parse = VirtualMapKey.PROTOBUF.parse(keyBytes); @@ -86,7 +89,8 @@ void validate(DeserializedSignedState deserializedState, Report report) throws I final AccountID accountId1 = entityIDPair.accountId(); final TokenID tokenId1 = entityIDPair.tokenId(); - final TokenRelation tokenRelation = TokenRelation.PROTOBUF.parse(pair.right()); + final VirtualMapValue virtualMapValue = VirtualMapValue.PROTOBUF.parse(valueBytes); + final TokenRelation tokenRelation = virtualMapValue.value().as(); final AccountID accountId2 = tokenRelation.accountId(); final TokenID tokenId2 = tokenRelation.tokenId(); From 0c3b701fac99e9432dfaaba984664c96b9121bcc Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Tue, 29 Jul 2025 15:28:19 +0300 Subject: [PATCH 9/9] minor refactor Signed-off-by: Nikita Lebedev --- .../servicesstate/AccountValidator.java | 6 +++--- .../servicesstate/TokenRelationsIntegrity.java | 6 +++--- .../com/swirlds/state/merkle/StateUtils.java | 16 ++++++++-------- .../state/merkle/disk/OnDiskIterator.java | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java index e204709cff15..e240433fbe36 100644 --- a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java +++ b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/AccountValidator.java @@ -2,7 +2,7 @@ package com.hedera.statevalidation.validators.servicesstate; import static com.hedera.statevalidation.validators.ParallelProcessingUtil.VALIDATOR_FORK_JOIN_POOL; -import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyStateId; +import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyValueStateId; import static com.swirlds.state.merkle.StateUtils.stateIdFor; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -74,8 +74,8 @@ void validate(DeserializedSignedState deserializedState, Report report) throws I InterruptableConsumer> handler = pair -> { final Bytes keyBytes = pair.left(); final Bytes valueBytes = pair.right(); - final int readKeyStateId = extractVirtualMapKeyStateId(keyBytes); - final int readValueStateId = extractVirtualMapKeyStateId(valueBytes); + final int readKeyStateId = extractVirtualMapKeyValueStateId(keyBytes); + final int readValueStateId = extractVirtualMapKeyValueStateId(valueBytes); if ((readKeyStateId == targetStateId) && (readValueStateId == targetStateId)) { try { final VirtualMapValue virtualMapValue = VirtualMapValue.PROTOBUF.parse(valueBytes); diff --git a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java index fc9d4940aaa5..e57f61853972 100644 --- a/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java +++ b/hedera-state-validator/src/main/java/com/hedera/statevalidation/validators/servicesstate/TokenRelationsIntegrity.java @@ -2,7 +2,7 @@ package com.hedera.statevalidation.validators.servicesstate; import static com.hedera.statevalidation.validators.ParallelProcessingUtil.VALIDATOR_FORK_JOIN_POOL; -import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyStateId; +import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyValueStateId; import static com.swirlds.state.merkle.StateUtils.stateIdFor; import static org.junit.jupiter.api.Assertions.*; @@ -79,8 +79,8 @@ void validate(DeserializedSignedState deserializedState, Report report) throws I InterruptableConsumer> handler = pair -> { final Bytes keyBytes = pair.left(); final Bytes valueBytes = pair.right(); - final int readKeyStateId = extractVirtualMapKeyStateId(keyBytes); - final int readValueStateId = extractVirtualMapKeyStateId(valueBytes); + final int readKeyStateId = extractVirtualMapKeyValueStateId(keyBytes); + final int readValueStateId = extractVirtualMapKeyValueStateId(valueBytes); if ((readKeyStateId == targetStateId) && (readValueStateId == targetStateId)) { try { final VirtualMapKey parse = VirtualMapKey.PROTOBUF.parse(keyBytes); diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java index 8a022504a3de..b1e06cb7aeae 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateUtils.java @@ -534,20 +534,20 @@ public static Bytes getVirtualMapKeyValueBytes( } /** - * Extracts the state ID from a serialized {@link com.hedera.hapi.platform.state.VirtualMapKey}. + * Extracts the state ID from a serialized {@link VirtualMapKey} or {@link VirtualMapValue}. *

- * This method reads the next protobuf field number (the one-of field number) from the key's + * This method reads the next protobuf field number (the one-of field number) from the key's or value's * sequential data, which corresponds to the embedded state ID. *

* - * @param keyBytes the serialized {@link VirtualMapKey} bytes + * @param objectBytes the serialized {@link VirtualMapKey} or {@link VirtualMapValue} bytes * @return the extracted state ID - * @throws NullPointerException if {@code keyBytes} is null + * @throws NullPointerException if {@code objectBytes} is null */ - public static int extractVirtualMapKeyStateId(@NonNull final Bytes keyBytes) { - Objects.requireNonNull(keyBytes, "keyBytes must not be null"); - // Rely on the fact that VirtualMapKey has a single OneOf field, + public static int extractVirtualMapKeyValueStateId(@NonNull final Bytes objectBytes) { + Objects.requireNonNull(objectBytes, "objectBytes must not be null"); + // Rely on the fact that VirtualMapKey and VirtualMapValue has a single OneOf field, // so the next field number is the state ID. - return readNextFieldNumber(keyBytes.toReadableSequentialData()); + return readNextFieldNumber(objectBytes.toReadableSequentialData()); } } diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskIterator.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskIterator.java index 8079ccefdd32..17f8ee7070bd 100644 --- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskIterator.java +++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/disk/OnDiskIterator.java @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package com.swirlds.state.merkle.disk; -import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyStateId; +import static com.swirlds.state.merkle.StateUtils.extractVirtualMapKeyValueStateId; import static java.util.Objects.requireNonNull; import com.hedera.hapi.platform.state.VirtualMapKey; @@ -36,7 +36,7 @@ public boolean hasNext() { final MerkleNode merkleNode = itr.next(); if (merkleNode instanceof VirtualLeafNode leaf) { final Bytes k = leaf.getKey(); - final int nextNextStateId = extractVirtualMapKeyStateId(k); + final int nextNextStateId = extractVirtualMapKeyValueStateId(k); if (stateId == nextNextStateId) { try { final VirtualMapKey parse = VirtualMapKey.PROTOBUF.parse(k);