Skip to content

Commit 91953bd

Browse files
feat: Keep self node account id in memory (#22487)
Signed-off-by: Zhivko Kelchev <[email protected]> Signed-off-by: Derek Riley <[email protected]> Co-authored-by: Derek Riley <[email protected]>
1 parent a2c46e5 commit 91953bd

File tree

15 files changed

+289
-155
lines changed

15 files changed

+289
-155
lines changed

hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import com.hedera.node.app.info.StateNetworkInfo;
6464
import com.hedera.node.app.metrics.StoreMetricsServiceImpl;
6565
import com.hedera.node.app.records.BlockRecordService;
66+
import com.hedera.node.app.records.impl.producers.formats.SelfNodeAccountIdManagerImpl;
6667
import com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl;
6768
import com.hedera.node.app.service.consensus.impl.ConsensusServiceImpl;
6869
import com.hedera.node.app.service.contract.impl.ContractServiceImpl;
@@ -1252,6 +1253,7 @@ private void initializeDagger(@NonNull final State state, @NonNull final InitTri
12521253
platform.getSelfId().id(), state, currentRoster, configProvider, () -> requireNonNull(
12531254
genesisNetworkSupplier)
12541255
.get());
1256+
final var selfNodeAccountIdManager = new SelfNodeAccountIdManagerImpl(configProvider, networkInfo, state);
12551257
hintsService.initCurrentRoster(currentRoster);
12561258
final var blockHashSigner = blockHashSignerFactory.apply(hintsService, historyService, configProvider);
12571259
// Fully qualified so as to not confuse javadoc
@@ -1279,6 +1281,7 @@ private void initializeDagger(@NonNull final State state, @NonNull final InitTri
12791281
.migrationStateChanges(migrationStateChanges != null ? migrationStateChanges : new ArrayList<>())
12801282
.initialStateHash(initialStateHash)
12811283
.networkInfo(networkInfo)
1284+
.selfNodeAccountIdManager(selfNodeAccountIdManager)
12821285
.startupNetworks(startupNetworks)
12831286
.hintsService(hintsService)
12841287
.historyService(historyService)

hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaInjectionComponent.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.hedera.node.app.spi.info.NodeInfo;
4444
import com.hedera.node.app.spi.migrate.StartupNetworks;
4545
import com.hedera.node.app.spi.records.RecordCache;
46+
import com.hedera.node.app.spi.records.SelfNodeAccountIdManager;
4647
import com.hedera.node.app.spi.throttle.ScheduleThrottle;
4748
import com.hedera.node.app.state.HederaStateInjectionModule;
4849
import com.hedera.node.app.state.WorkingStateAccessor;
@@ -160,6 +161,8 @@ public interface HederaInjectionComponent {
160161

161162
QuiescenceController quiescenceController();
162163

164+
SelfNodeAccountIdManager selfNodeAccountIdManager();
165+
163166
@Component.Builder
164167
interface Builder {
165168
@BindsInstance
@@ -246,6 +249,9 @@ interface Builder {
246249
@BindsInstance
247250
Builder appContext(AppContext appContext);
248251

252+
@BindsInstance
253+
Builder selfNodeAccountIdManager(SelfNodeAccountIdManager selfNodeAccountIdManager);
254+
249255
HederaInjectionComponent build();
250256
}
251257
}

hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/BlockRecordWriterFactoryImpl.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.hedera.node.app.records.impl.producers.BlockRecordWriter;
77
import com.hedera.node.app.records.impl.producers.BlockRecordWriterFactory;
88
import com.hedera.node.app.records.impl.producers.formats.v6.BlockRecordWriterV6;
9+
import com.hedera.node.app.spi.records.SelfNodeAccountIdManager;
910
import com.hedera.node.config.ConfigProvider;
1011
import com.hedera.node.config.data.BlockRecordStreamConfig;
1112
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -22,7 +23,7 @@ public class BlockRecordWriterFactoryImpl implements BlockRecordWriterFactory {
2223
private final ConfigProvider configProvider;
2324
private final Signer signer;
2425
private final FileSystem fileSystem;
25-
private final SelfNodeAccountIdManagerImpl selfNodeAccountIdManager;
26+
private final SelfNodeAccountIdManager selfNodeAccountIdManager;
2627

2728
/**
2829
*
@@ -35,7 +36,7 @@ public BlockRecordWriterFactoryImpl(
3536
@NonNull final ConfigProvider configProvider,
3637
@NonNull final Signer signer,
3738
@NonNull final FileSystem fileSystem,
38-
@NonNull final SelfNodeAccountIdManagerImpl selfNodeAccountIdManager) {
39+
@NonNull final SelfNodeAccountIdManager selfNodeAccountIdManager) {
3940
this.configProvider = requireNonNull(configProvider);
4041
this.fileSystem = requireNonNull(fileSystem);
4142
this.signer = requireNonNull(signer);
Lines changed: 68 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,105 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package com.hedera.node.app.records.impl.producers.formats;
33

4-
import static com.swirlds.common.io.utility.FileUtils.getAbsolutePath;
4+
import static java.util.Objects.requireNonNull;
55

66
import com.hedera.hapi.node.base.AccountID;
7+
import com.hedera.hapi.node.base.NodeAddressBook;
8+
import com.hedera.node.app.service.file.ReadableFileStore;
79
import com.hedera.node.app.spi.info.NetworkInfo;
810
import com.hedera.node.app.spi.info.NodeInfo;
911
import com.hedera.node.app.spi.records.SelfNodeAccountIdManager;
12+
import com.hedera.node.app.store.ReadableStoreFactory;
13+
import com.hedera.node.app.util.FileUtilities;
1014
import com.hedera.node.config.ConfigProvider;
11-
import com.hedera.node.config.data.NodesConfig;
15+
import com.hedera.node.config.data.FilesConfig;
16+
import com.hedera.pbj.runtime.ParseException;
17+
import com.hedera.pbj.runtime.io.buffer.Bytes;
18+
import com.swirlds.state.State;
1219
import edu.umd.cs.findbugs.annotations.NonNull;
13-
import java.io.IOException;
14-
import java.nio.file.Files;
15-
import java.nio.file.Path;
16-
import javax.inject.Inject;
20+
import java.util.concurrent.atomic.AtomicReference;
1721
import javax.inject.Singleton;
1822
import org.apache.logging.log4j.LogManager;
1923
import org.apache.logging.log4j.Logger;
2024

2125
/**
22-
* Manages the persistent storage and retrieval of the self node's account ID.
23-
* <p>
24-
* The account ID is stored in a file named {@code node_account_id.txt} on disk to ensure it is preserved
25-
* across restarts and upgrades. On first access, if the file does not exist (e.g., for new nodes),
26-
* it is created using the current node's account ID from the "NodeInfo selfInfo". Subsequent accesses
27-
* will return the value from the file.
26+
* Manages retrieval of the self node's account ID from state (File 0.0.102, node details).
2827
*/
2928
@Singleton
3029
public class SelfNodeAccountIdManagerImpl implements SelfNodeAccountIdManager {
3130
private static final Logger logger = LogManager.getLogger(SelfNodeAccountIdManagerImpl.class);
32-
31+
private final ConfigProvider configProvider;
3332
private final NodeInfo nodeInfo;
34-
private final Path filePath;
33+
private final State state;
34+
35+
private final AtomicReference<AccountID> accountId = new AtomicReference<>();
3536

36-
@Inject
37-
public SelfNodeAccountIdManagerImpl(@NonNull ConfigProvider configProvider, @NonNull NetworkInfo networkInfo) {
38-
this.nodeInfo = networkInfo.selfNodeInfo();
39-
final NodesConfig nodesConfig = configProvider.getConfiguration().getConfigData(NodesConfig.class);
40-
this.filePath = getAbsolutePath(nodesConfig.nodeGeneratedDir()).resolve(nodesConfig.nodeAccountIdFile());
37+
/**
38+
* Primary constructor used in production: reads self node account ID from state node details file.
39+
*/
40+
public SelfNodeAccountIdManagerImpl(
41+
@NonNull final ConfigProvider configProvider,
42+
@NonNull final NetworkInfo networkInfo,
43+
@NonNull final State state) {
44+
this.configProvider = requireNonNull(configProvider);
45+
this.nodeInfo = requireNonNull(networkInfo).selfNodeInfo();
46+
this.state = requireNonNull(state);
4147
}
4248

4349
/**
4450
* Retrieves the self node's account ID.
45-
* <p>
46-
* If the backing file {@code node_account_id.txt} exists, returns the value stored in the file.
47-
* If the file is missing (such as on a new node), creates the file using the current
48-
* node account ID from "NodeInfo selfInfo" and returns that value.
49-
*
5051
* @return the self node's account ID
5152
*/
5253
public AccountID getSelfNodeAccountId() {
53-
try {
54-
// if the file don't exist, create one
55-
if (!filePath.toFile().exists()) {
56-
writeAccountIdFile(nodeInfo.accountId());
57-
}
58-
59-
final var accountIdString = Files.readString(filePath);
60-
String[] parts = accountIdString.split("[.]");
61-
return AccountID.newBuilder()
62-
.shardNum(Long.parseLong(parts[0]))
63-
.realmNum(Long.parseLong(parts[1]))
64-
.accountNum(Long.parseLong(parts[2]))
65-
.build();
66-
67-
} catch (IOException e) {
68-
logger.error("Failed to read node account id from {}", filePath, e);
69-
return nodeInfo.accountId();
70-
} catch (NumberFormatException | IndexOutOfBoundsException e) {
71-
logger.error("Failed to parse account id from {}", filePath, e);
72-
return nodeInfo.accountId();
54+
if (accountId.get() == null) {
55+
initSelfNodeAccountId();
7356
}
57+
58+
return accountId.get();
7459
}
7560

7661
/**
77-
* Creates or updates the file {@code node_account_id.txt} containing the self node's account ID.
78-
* <p>
79-
* This method writes the provided {@link AccountID} to the persistent file,
80-
* overwriting any existing value.
81-
*
82-
* @param accountId the new account ID to persist
62+
* Sets the self node's account ID in-memory.
63+
* @param accountId the new account ID
8364
*/
84-
public void setSelfNodeAccountId(final AccountID accountId) {
85-
try {
86-
writeAccountIdFile(accountId);
87-
} catch (IOException e) {
88-
logger.error("Failed to write node account id to {}", filePath, e);
89-
}
65+
public void setSelfNodeAccountId(@NonNull final AccountID accountId) {
66+
this.accountId.set(requireNonNull(accountId));
9067
}
9168

92-
private void writeAccountIdFile(AccountID accountId) throws IOException {
93-
Files.createDirectories(filePath.getParent());
94-
String content = accountId.shardNum() + "." + accountId.realmNum() + "." + accountId.accountNum();
95-
Files.writeString(filePath, content);
96-
logger.info("Wrote node account id file at {}", filePath);
69+
private void initSelfNodeAccountId() {
70+
if (state == null) {
71+
accountId.set(nodeInfo.accountId());
72+
return;
73+
}
74+
final var config = configProvider.getConfiguration();
75+
final var filesConfig = config.getConfigData(FilesConfig.class);
76+
try {
77+
final var nodeDetailsId = FileUtilities.createFileID(filesConfig.nodeDetails(), config);
78+
final var fileStore = new ReadableStoreFactory(state).getStore(ReadableFileStore.class);
79+
final var nodeDetailsFile = fileStore.getFileLeaf(nodeDetailsId);
80+
final Bytes bytes = (nodeDetailsFile == null) ? Bytes.EMPTY : nodeDetailsFile.contents();
81+
if (bytes.length() == 0) {
82+
logger.info(
83+
"Node details file ({}) missing or empty; falling back to self NodeInfo",
84+
filesConfig.nodeDetails());
85+
accountId.set(nodeInfo.accountId());
86+
return;
87+
}
88+
final var book = NodeAddressBook.PROTOBUF.parse(bytes);
89+
final var selfNodeId = nodeInfo.nodeId();
90+
final var maybeEntry = book.nodeAddress().stream()
91+
.filter(addr -> addr.nodeId() == selfNodeId)
92+
.findFirst();
93+
if (maybeEntry.isPresent() && maybeEntry.get().hasNodeAccountId()) {
94+
accountId.set(maybeEntry.get().nodeAccountIdOrThrow());
95+
} else {
96+
logger.warn("Self node id {} not found in node details; using NodeInfo account id", selfNodeId);
97+
accountId.set(nodeInfo.accountId());
98+
}
99+
} catch (ParseException e) {
100+
logger.warn(
101+
"Failed to parse node details (file {}); using NodeInfo account id", filesConfig.nodeDetails(), e);
102+
accountId.set(nodeInfo.accountId());
103+
}
97104
}
98105
}
Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package com.hedera.node.app.state;
33

4-
import com.hedera.node.app.records.impl.producers.formats.SelfNodeAccountIdManagerImpl;
5-
import com.hedera.node.app.spi.info.NetworkInfo;
64
import com.hedera.node.app.spi.records.RecordCache;
7-
import com.hedera.node.app.spi.records.SelfNodeAccountIdManager;
85
import com.hedera.node.app.state.recordcache.DeduplicationCacheImpl;
96
import com.hedera.node.app.state.recordcache.RecordCacheImpl;
10-
import com.hedera.node.config.ConfigProvider;
117
import dagger.Binds;
128
import dagger.Module;
139
import dagger.Provides;
14-
import edu.umd.cs.findbugs.annotations.NonNull;
1510
import javax.inject.Singleton;
1611

1712
@Module
@@ -30,11 +25,4 @@ public interface HederaStateInjectionModule {
3025
static WorkingStateAccessor provideWorkingStateAccessor() {
3126
return new WorkingStateAccessor();
3227
}
33-
34-
@Provides
35-
@Singleton
36-
static SelfNodeAccountIdManager selfNodeAccountIdManager(
37-
@NonNull final ConfigProvider configProvider, @NonNull final NetworkInfo networkInfo) {
38-
return new SelfNodeAccountIdManagerImpl(configProvider, networkInfo);
39-
}
4028
}

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/SystemTransactions.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import com.hedera.node.app.spi.info.NetworkInfo;
5959
import com.hedera.node.app.spi.info.NodeInfo;
6060
import com.hedera.node.app.spi.migrate.StartupNetworks;
61+
import com.hedera.node.app.spi.records.SelfNodeAccountIdManager;
6162
import com.hedera.node.app.spi.workflows.SystemContext;
6263
import com.hedera.node.app.state.HederaRecordCache;
6364
import com.hedera.node.app.state.recordcache.LegacyListRecordSource;
@@ -151,6 +152,7 @@ public class SystemTransactions {
151152
private final StartupNetworks startupNetworks;
152153
private final StakePeriodChanges stakePeriodChanges;
153154
private final ImmediateStateChangeListener immediateStateChangeListener;
155+
private final SelfNodeAccountIdManager selfNodeAccountIdManager;
154156

155157
private int nextDispatchNonce = 1;
156158

@@ -172,7 +174,8 @@ public SystemTransactions(
172174
@NonNull final HederaRecordCache recordCache,
173175
@NonNull final StartupNetworks startupNetworks,
174176
@NonNull final StakePeriodChanges stakePeriodChanges,
175-
@NonNull final ImmediateStateChangeListener immediateStateChangeListener) {
177+
@NonNull final ImmediateStateChangeListener immediateStateChangeListener,
178+
@NonNull final SelfNodeAccountIdManager selfNodeAccountIdManager) {
176179
this.initTrigger = requireNonNull(initTrigger);
177180
this.fileService = requireNonNull(fileService);
178181
this.parentTxnFactory = requireNonNull(parentTxnFactory);
@@ -191,6 +194,7 @@ public SystemTransactions(
191194
this.startupNetworks = requireNonNull(startupNetworks);
192195
this.stakePeriodChanges = requireNonNull(stakePeriodChanges);
193196
this.immediateStateChangeListener = requireNonNull(immediateStateChangeListener);
197+
this.selfNodeAccountIdManager = requireNonNull(selfNodeAccountIdManager);
194198
}
195199

196200
/**
@@ -363,6 +367,7 @@ public void doPostUpgradeSetup(@NonNull final Instant now, @NonNull final State
363367
final var nodeStore = new ReadableStoreFactory(state).getStore(ReadableNodeStore.class);
364368
fileService.updateAddressBookAndNodeDetailsAfterFreeze(systemContext, nodeStore);
365369
}
370+
selfNodeAccountIdManager.setSelfNodeAccountId(networkInfo.selfNodeInfo().accountId());
366371

367372
// And then we update the system files for fees schedules, throttles, override properties, and override
368373
// permissions from any upgrade files that are present in the configured directory

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/ExecutorComponent.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.hedera.node.app.service.util.impl.UtilServiceImpl;
1717
import com.hedera.node.app.services.ServicesInjectionModule;
1818
import com.hedera.node.app.spi.AppContext;
19+
import com.hedera.node.app.spi.records.SelfNodeAccountIdManager;
1920
import com.hedera.node.app.spi.throttle.ScheduleThrottle;
2021
import com.hedera.node.app.state.HederaStateInjectionModule;
2122
import com.hedera.node.app.throttle.ThrottleServiceManager;
@@ -95,6 +96,9 @@ interface Builder {
9596
@BindsInstance
9697
Builder appContext(AppContext appContext);
9798

99+
@BindsInstance
100+
Builder selfNodeAccountIdManager(SelfNodeAccountIdManager selfNodeAccountIdManager);
101+
98102
ExecutorComponent build();
99103
}
100104

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/TransactionExecutors.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.hedera.node.app.history.impl.HistoryLibraryImpl;
1515
import com.hedera.node.app.history.impl.HistoryServiceImpl;
1616
import com.hedera.node.app.info.NodeInfoImpl;
17+
import com.hedera.node.app.records.impl.producers.formats.SelfNodeAccountIdManagerImpl;
1718
import com.hedera.node.app.service.consensus.impl.ConsensusServiceImpl;
1819
import com.hedera.node.app.service.contract.impl.ContractServiceImpl;
1920
import com.hedera.node.app.service.entityid.EntityIdFactory;
@@ -28,6 +29,7 @@
2829
import com.hedera.node.app.state.recordcache.LegacyListRecordSource;
2930
import com.hedera.node.app.throttle.AppThrottleFactory;
3031
import com.hedera.node.app.throttle.ThrottleAccumulator;
32+
import com.hedera.node.app.workflows.standalone.impl.StandaloneNetworkInfo;
3133
import com.hedera.node.config.data.BlockStreamConfig;
3234
import com.hedera.node.config.data.HederaConfig;
3335
import com.hedera.node.config.types.StreamMode;
@@ -295,6 +297,8 @@ private ExecutorComponent newExecutorComponent(
295297
bootstrapConfig.getConfigData(BlockStreamConfig.class).blockPeriod());
296298
final var historyService = new HistoryServiceImpl(
297299
NO_OP_METRICS, ForkJoinPool.commonPool(), appContext, new HistoryLibraryImpl(), bootstrapConfig);
300+
final var standaloneNetworkInfo = new StandaloneNetworkInfo(configProvider);
301+
standaloneNetworkInfo.initFrom(state);
298302
final var component = DaggerExecutorComponent.builder()
299303
.appContext(appContext)
300304
.configProviderImpl(configProvider)
@@ -310,6 +314,8 @@ private ExecutorComponent newExecutorComponent(
310314
.historyService(historyService)
311315
.metrics(NO_OP_METRICS)
312316
.throttleFactory(appContext.throttleFactory())
317+
.selfNodeAccountIdManager(
318+
new SelfNodeAccountIdManagerImpl(configProvider, standaloneNetworkInfo, state))
313319
.build();
314320
componentRef.set(component);
315321
return component;

0 commit comments

Comments
 (0)