26
26
import com .hedera .node .app .service .mono .state .merkle .MerkleStakingInfo ;
27
27
import com .hedera .node .app .service .mono .state .migration .StateChildIndices ;
28
28
import com .hedera .node .app .service .mono .utils .EntityNum ;
29
- import com .hedera .node .app .service .token .ReadableStakingInfoStore ;
30
29
import com .hedera .node .app .service .token .TokenService ;
31
- import com .hedera .node .app .spi .state .WritableKVState ;
32
- import com .hedera .node .app .spi .state .WritableKVStateBase ;
33
30
import com .hedera .node .app .state .merkle .HederaLifecycles ;
34
31
import com .hedera .node .app .state .merkle .MerkleHederaState ;
35
- import com .hedera .node .app .workflows .dispatcher .ReadableStoreFactory ;
32
+ import com .hedera .node .app .state .merkle .disk .OnDiskKey ;
33
+ import com .hedera .node .app .state .merkle .disk .OnDiskValue ;
36
34
import com .swirlds .common .context .PlatformContext ;
37
35
import com .swirlds .common .platform .NodeId ;
36
+ import com .swirlds .common .threading .manager .AdHocThreadManager ;
38
37
import com .swirlds .config .api .Configuration ;
39
38
import com .swirlds .merkle .map .MerkleMap ;
40
39
import com .swirlds .platform .state .PlatformState ;
44
43
import com .swirlds .platform .system .SoftwareVersion ;
45
44
import com .swirlds .platform .system .address .AddressBook ;
46
45
import com .swirlds .platform .system .events .Event ;
46
+ import com .swirlds .virtualmap .VirtualMap ;
47
+ import com .swirlds .virtualmap .VirtualMapMigration ;
47
48
import edu .umd .cs .findbugs .annotations .NonNull ;
48
49
import edu .umd .cs .findbugs .annotations .Nullable ;
49
50
import java .util .Arrays ;
50
- import java .util .Objects ;
51
51
import java .util .Set ;
52
+ import java .util .function .BiConsumer ;
52
53
import org .apache .logging .log4j .LogManager ;
53
54
import org .apache .logging .log4j .Logger ;
54
55
@@ -60,10 +61,42 @@ public class HederaLifecyclesImpl implements HederaLifecycles {
60
61
private static final long LEDGER_TOTAL_TINY_BAR_FLOAT = 5000000000000000000L ;
61
62
private static final int NUM_REWARD_HISTORY_STORED_PERIODS = 365 ;
62
63
64
+ private static final BiConsumer <
65
+ VirtualMap <OnDiskKey <EntityNumber >, OnDiskValue <StakingNodeInfo >>,
66
+ BiConsumer <EntityNumber , StakingNodeInfo >>
67
+ WEIGHT_UPDATE_VISITOR = (map , visitor ) -> {
68
+ try {
69
+ VirtualMapMigration .extractVirtualMapData (
70
+ AdHocThreadManager .getStaticThreadManager (),
71
+ map ,
72
+ pair -> visitor .accept (
73
+ pair .key ().getKey (), pair .value ().getValue ()),
74
+ 1 );
75
+ } catch (InterruptedException e ) {
76
+ logger .error ("Interrupted while updating weights" , e );
77
+ throw new IllegalStateException (e );
78
+ }
79
+ };
80
+
63
81
private final Hedera hedera ;
82
+ private final BiConsumer <
83
+ VirtualMap <OnDiskKey <EntityNumber >, OnDiskValue <StakingNodeInfo >>,
84
+ BiConsumer <EntityNumber , StakingNodeInfo >>
85
+ weightUpdateVisitor ;
64
86
65
87
public HederaLifecyclesImpl (@ NonNull final Hedera hedera ) {
66
- this .hedera = Objects .requireNonNull (hedera );
88
+ this (hedera , WEIGHT_UPDATE_VISITOR );
89
+ }
90
+
91
+ public HederaLifecyclesImpl (
92
+ @ NonNull final Hedera hedera ,
93
+ @ NonNull
94
+ final BiConsumer <
95
+ VirtualMap <OnDiskKey <EntityNumber >, OnDiskValue <StakingNodeInfo >>,
96
+ BiConsumer <EntityNumber , StakingNodeInfo >>
97
+ weightUpdateVisitor ) {
98
+ this .hedera = requireNonNull (hedera );
99
+ this .weightUpdateVisitor = requireNonNull (weightUpdateVisitor );
67
100
}
68
101
69
102
@ Override
@@ -92,56 +125,40 @@ public void onUpdateWeight(
92
125
@ NonNull final MerkleHederaState state ,
93
126
@ NonNull final AddressBook configAddressBook ,
94
127
@ NonNull final PlatformContext context ) {
95
- final var tokenServiceState = state .getWritableStates ( TokenService . NAME ) ;
128
+ final var isMonoState = state .getChild ( StateChildIndices . STAKING_INFO ) instanceof MerkleMap ;
96
129
// Get all nodeIds added in the config.txt
97
- Set <NodeId > configNodeIds = configAddressBook .getNodeIdSet ();
98
- final var configAddressBookSize = configNodeIds .size ();
99
- if (!tokenServiceState .isEmpty ()) {
100
- final var stakingInfoState = tokenServiceState .<EntityNumber , StakingNodeInfo >get (STAKING_INFO_KEY );
101
- final var readableStoreFactory = new ReadableStoreFactory (state );
102
- final var stakingInfoStore = readableStoreFactory .getStore (ReadableStakingInfoStore .class );
103
- final var allNodeIds = stakingInfoStore .getAll ();
104
- for (final var nodeId : allNodeIds ) {
105
- final var stakingInfo = requireNonNull (stakingInfoStore .get (nodeId ));
106
- NodeId id = new NodeId (nodeId );
107
- // set weight for the nodes that exist in state and remove from
108
- // nodes given in config.txt. This is needed to recognize newly added nodes
109
- // It is possible that some nodes are deleted in configAddressBook compared to state
110
- // We will set those node sas deleted in EndOfStakingPeriodCalculator
111
- if (configNodeIds .contains (id )) {
112
- configAddressBook .updateWeight (id , stakingInfo .weight ());
113
- configNodeIds .remove (id );
114
- } else {
115
- // We need to validate and mark any node that are removed during upgrade as deleted
116
- // and also set the weight to 0
117
- stakingInfoState .put (
118
- EntityNumber .newBuilder ().number (id .id ()).build (),
119
- stakingInfo .copyBuilder ().deleted (true ).weight (0 ).build ());
120
- logger .info (
121
- "Node {} is deleted from configAddressBook during upgrade "
122
- + "and is marked deleted in state" ,
123
- id );
124
- }
130
+ final var nodeIdsLeftToUpdate = configAddressBook .getNodeIdSet ();
131
+ if (!isMonoState ) {
132
+ final var stakingInfoIndex = state .findNodeIndex (TokenService .NAME , STAKING_INFO_KEY );
133
+ if (stakingInfoIndex == -1 ) {
134
+ logger .warn ("Staking info not found in state, skipping weight update" );
135
+ return ;
125
136
}
126
- // for any newly added nodes that doesn't exist in state, weight should be set to 0
127
- // irrespective of the weight provided in config.txt
128
- if (!configNodeIds .isEmpty ()) {
129
- configNodeIds .forEach (nodeId -> {
130
- configAddressBook .updateWeight (nodeId , 0 );
137
+ @ SuppressWarnings ("unchecked" )
138
+ final var stakingInfoVMap = (VirtualMap <OnDiskKey <EntityNumber >, OnDiskValue <StakingNodeInfo >>)
139
+ state .getChild (stakingInfoIndex );
140
+ // Since it is much easier to modify the in-state staking info after schemas
141
+ // are registered with MerkleHederaState, we do that work later in the token
142
+ // service schema's restart() hook. Here we only update the address book weights
143
+ // based on the staking info in the state.
144
+ weightUpdateVisitor .accept (stakingInfoVMap , (node , info ) -> {
145
+ final var nodeId = new NodeId (node .number ());
146
+ // If present in the address book, remove this node id from the
147
+ // set of node ids left to update and update its weight
148
+ if (nodeIdsLeftToUpdate .remove (nodeId )) {
149
+ configAddressBook .updateWeight (nodeId , info .weight ());
150
+ } else {
131
151
logger .info (
132
- "Node {} is newly added in configAddressBook during upgrade "
133
- + "with weight 0 in configAddressBook" ,
152
+ "Node {} is no longer in address book; restart() will ensure its staking info is marked deleted" ,
134
153
nodeId );
135
- });
136
- // update the state with new nodes and set weight to 0
137
- addAdditionalNodesToState (
138
- stakingInfoState , configNodeIds , context .getConfiguration (), configAddressBookSize );
139
- }
140
-
141
- if (stakingInfoState .isModified ()) {
142
- ((WritableKVStateBase ) stakingInfoState ).commit ();
143
- }
154
+ }
155
+ });
156
+ nodeIdsLeftToUpdate .forEach (nodeId -> {
157
+ configAddressBook .updateWeight (nodeId , 0 );
158
+ logger .info ("Found new node {}; restart() will add its staking info" , nodeId );
159
+ });
144
160
} else {
161
+ final var configAddressBookSize = nodeIdsLeftToUpdate .size ();
145
162
// When doing the first upgrade from 0.48 to 0.49, we will call updateWeight before BBM migration.
146
163
// In this case, we need to update the weight in the stakingInfo map from mono service state.
147
164
logger .info ("Token service state is empty, so we are migrating from mono to mod-service with "
@@ -150,9 +167,9 @@ public void onUpdateWeight(
150
167
(MerkleMap <EntityNum , MerkleStakingInfo >) state .getChild (StateChildIndices .STAKING_INFO ));
151
168
stakingInfosMap .forEachNode ((nodeNum , stakingInfo ) -> {
152
169
final NodeId nodeId = new NodeId (nodeNum .longValue ());
153
- if (configNodeIds .contains (nodeId )) {
170
+ if (nodeIdsLeftToUpdate .contains (nodeId )) {
154
171
configAddressBook .updateWeight (nodeId , stakingInfo .getWeight ());
155
- configNodeIds .remove (nodeId );
172
+ nodeIdsLeftToUpdate .remove (nodeId );
156
173
} else {
157
174
final var newStakingInfo = stakingInfosMap .getForModify (nodeNum );
158
175
newStakingInfo .setWeight (0 );
@@ -164,8 +181,8 @@ public void onUpdateWeight(
164
181
});
165
182
// for any newly added nodes that doesn't exist in state, weight should be set to 0
166
183
// irrespective of the weight provided in config.txt
167
- if (!configNodeIds .isEmpty ()) {
168
- configNodeIds .forEach (nodeId -> {
184
+ if (!nodeIdsLeftToUpdate .isEmpty ()) {
185
+ nodeIdsLeftToUpdate .forEach (nodeId -> {
169
186
configAddressBook .updateWeight (nodeId , 0 );
170
187
logger .info (
171
188
"Node {} is newly added in configAddressBook during upgrade "
@@ -174,13 +191,14 @@ public void onUpdateWeight(
174
191
});
175
192
// update the state with new nodes and set weight to 0
176
193
addAdditionalNodesToState (
177
- stakingInfosMap , configNodeIds , context .getConfiguration (), configAddressBookSize );
194
+ stakingInfosMap , nodeIdsLeftToUpdate , context .getConfiguration (), configAddressBookSize );
178
195
}
179
196
}
180
197
}
181
198
182
199
/**
183
200
* Add additional nodes to state with weight 0 and update all nodes maxStake to maxStakePerNode
201
+ *
184
202
* @param stakingInfoMap The state to update
185
203
* @param newNodeIds The new node ids to add
186
204
* @param config The configuration
@@ -220,49 +238,6 @@ private void addAdditionalNodesToState(
220
238
});
221
239
}
222
240
223
- /**
224
- * Add additional nodes to state with weight 0 and update all nodes maxStake to maxStakePerNode
225
- * @param stakingToState The state to update
226
- * @param newNodeIds The new node ids to add
227
- * @param config The configuration
228
- * @param configAddressBookSize The size of the address book
229
- */
230
- private void addAdditionalNodesToState (
231
- @ NonNull final WritableKVState <EntityNumber , StakingNodeInfo > stakingToState ,
232
- @ NonNull final Set <NodeId > newNodeIds ,
233
- @ NonNull final Configuration config ,
234
- final int configAddressBookSize ) {
235
- // Since PlatformContext configuration is not available here,
236
- // we are using the default values here. (FUTURE) Need to see how to use config here
237
- // for ledger.totalTinyBarFloat and staking.rewardHistory.numStoredPeriods
238
- final long maxStakePerNode = LEDGER_TOTAL_TINY_BAR_FLOAT / configAddressBookSize ;
239
-
240
- // Add new nodes with weight 0
241
- for (final var nodeId : newNodeIds ) {
242
- final var rewardSumHistory = new Long [NUM_REWARD_HISTORY_STORED_PERIODS + 1 ];
243
- Arrays .fill (rewardSumHistory , 0L );
244
-
245
- final var newNodeStakingInfo = StakingNodeInfo .newBuilder ()
246
- .nodeNumber (nodeId .id ())
247
- .maxStake (maxStakePerNode )
248
- .minStake (0L )
249
- .rewardSumHistory (Arrays .asList (rewardSumHistory ))
250
- .weight (0 )
251
- .build ();
252
- stakingToState .put (new EntityNumber (nodeId .id ()), newNodeStakingInfo );
253
- logger .info ("Node {} is added in state with weight 0 and maxStakePerNode {} " , nodeId , maxStakePerNode );
254
- }
255
- // Update all nodes maxStake to maxStakePerNode
256
- stakingToState .keys ().forEachRemaining (key -> {
257
- final var stakingInfo = stakingToState .get (key );
258
- final var copy = requireNonNull (stakingInfo )
259
- .copyBuilder ()
260
- .maxStake (maxStakePerNode )
261
- .build ();
262
- stakingToState .put (key , copy );
263
- });
264
- }
265
-
266
241
@ Override
267
242
public void onNewRecoveredState (@ NonNull final MerkleHederaState recoveredState ) {
268
243
hedera .onNewRecoveredState (recoveredState );
0 commit comments