Skip to content

Commit

Permalink
Update StakingInfo.weight midnight UTC (#6231)
Browse files Browse the repository at this point in the history
Signed-off-by: Neeharika-Sompalli <neeharika.sompalli@swirldslabs.com>
Signed-off-by: Neeha <52669918+Neeharika-Sompalli@users.noreply.github.com>
Co-authored-by: Matt Hess <matt.hess@swirldslabs.com>
  • Loading branch information
Neeharika-Sompalli and mhess-swl authored Apr 27, 2023
1 parent 8b5d854 commit edb8da9
Show file tree
Hide file tree
Showing 32 changed files with 154 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ private PropertyNames() {
public static final String STAKING_REQUIRE_MIN_STAKE_TO_REWARD = "staking.requireMinStakeToReward";
public static final String STAKING_REWARD_RATE = "staking.rewardRate";
public static final String STAKING_START_THRESH = "staking.startThreshold";
public static final String STAKING_SUM_OF_CONSENSUS_WEIGHTS = "staking.sumOfConsensusWeights";
public static final String TOKENS_MAX_AGGREGATE_RELS = "tokens.maxAggregateRels";
public static final String TOKENS_STORE_RELS_ON_DISK = "tokens.storeRelsOnDisk";
public static final String TOKENS_MAX_NUM = "tokens.maxNumber";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import com.hedera.node.app.spi.state.StateDefinition;
import com.hedera.node.app.spi.state.WritableKVState;
import com.hedera.node.app.spi.state.WritableSingletonState;
import com.swirlds.common.exceptions.MutabilityException;
import com.swirlds.base.state.MutabilityException;
import com.swirlds.common.merkle.MerkleNode;
import com.swirlds.common.system.Round;
import com.swirlds.common.system.SwirldDualState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,4 @@ accounts.blocklist.enabled=true
accounts.blocklist.resource=evm-addresses-blocklist.csv
virtualdatasource.jasperdbToMerkledb=true
contracts.precompile.unsupportedCustomFeeReceiverDebits=
staking.sumOfConsensusWeights=500
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
import com.hedera.node.app.service.mono.utils.MiscUtils;
import com.hedera.node.app.spi.config.PropertyNames;
import com.hederahashgraph.api.proto.java.AccountID;
import com.swirlds.common.context.PlatformContext;
import com.swirlds.common.crypto.CryptographyHolder;
import com.swirlds.common.crypto.DigestType;
import com.swirlds.common.crypto.ImmutableHash;
Expand Down Expand Up @@ -283,6 +284,15 @@ public void preHandle(final Event event) {
metadata.app().eventExpansion().expandAllSigs(event, this);
}

@Override
public AddressBook updateWeight(@NonNull AddressBook configAddressBook, @NonNull PlatformContext context) {
throwIfImmutable();
stakingInfo()
.forEach((nodeNum, stakingInfo) ->
configAddressBook.updateWeight(nodeNum.longValue(), stakingInfo.getWeight()));
return configAddressBook;
}

private ServicesApp deserializedInit(
final Platform platform,
final SwirldDualState dualState,
Expand Down Expand Up @@ -565,7 +575,6 @@ void createGenesisChildren(
setChild(StateChildIndices.SPECIAL_FILES, new MerkleSpecialFiles());
setChild(StateChildIndices.SCHEDULE_TXS, new MerkleScheduledTransactions());
setChild(StateChildIndices.RECORD_STREAM_RUNNING_HASH, genesisRunningHashLeaf());
// setChild(StateChildIndices.ADDRESS_BOOK, addressBook);
setChild(StateChildIndices.CONTRACT_STORAGE, virtualMapFactory.newVirtualizedIterableStorage());
setChild(
StateChildIndices.STAKING_INFO,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_REWARD_RATE;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_STARTUP_HELPER_RECOMPUTE;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_START_THRESH;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_SUM_OF_CONSENSUS_WEIGHTS;
import static com.hedera.node.app.spi.config.PropertyNames.STATS_CONS_THROTTLES_TO_SAMPLE;
import static com.hedera.node.app.spi.config.PropertyNames.STATS_ENTITY_UTILS_GAUGE_UPDATE_INTERVAL_MS;
import static com.hedera.node.app.spi.config.PropertyNames.STATS_EXECUTION_TIMES_TO_TRACK;
Expand Down Expand Up @@ -427,7 +428,8 @@ public String getRawValue(final String name) {
STAKING_PERIOD_MINS,
STAKING_REWARD_HISTORY_NUM_STORED_PERIODS,
STAKING_STARTUP_HELPER_RECOMPUTE,
WORKFLOWS_ENABLED);
WORKFLOWS_ENABLED,
STAKING_SUM_OF_CONSENSUS_WEIGHTS);

static final Set<String> GLOBAL_DYNAMIC_PROPS = Set.of(
ACCOUNTS_MAX_NUM,
Expand Down Expand Up @@ -728,6 +730,7 @@ public static Function<String, Object> transformFor(final String prop) {
entry(STAKING_REQUIRE_MIN_STAKE_TO_REWARD, AS_BOOLEAN),
entry(STAKING_REWARD_RATE, AS_LONG),
entry(STAKING_START_THRESH, AS_LONG),
entry(STAKING_SUM_OF_CONSENSUS_WEIGHTS, AS_INT),
entry(TOKENS_MAX_AGGREGATE_RELS, AS_LONG),
entry(TOKENS_STORE_RELS_ON_DISK, AS_BOOLEAN),
entry(TOKENS_MAX_NUM, AS_LONG),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_REQUIRE_MIN_STAKE_TO_REWARD;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_REWARD_RATE;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_START_THRESH;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_SUM_OF_CONSENSUS_WEIGHTS;
import static com.hedera.node.app.spi.config.PropertyNames.TOKENS_AUTO_CREATIONS_ENABLED;
import static com.hedera.node.app.spi.config.PropertyNames.TOKENS_MAX_AGGREGATE_RELS;
import static com.hedera.node.app.spi.config.PropertyNames.TOKENS_MAX_CUSTOM_FEES_ALLOWED;
Expand Down Expand Up @@ -294,6 +295,7 @@ public class GlobalDynamicProperties implements EvmProperties {
private int maxAutoAssociations;
private Set<Address> contractsWithSpecialHapiSigsAccess;
private LegacyContractIdActivations legacyContractIdActivations;
private int sumOfConsensusWeights;

@Inject
public GlobalDynamicProperties(final HederaNumbers hederaNums, @CompositeProps final PropertySource properties) {
Expand Down Expand Up @@ -444,6 +446,11 @@ public void reload() {
contractsWithSpecialHapiSigsAccess = properties.getEvmAddresses(CONTRACTS_WITH_SPECIAL_HAPI_SIGS_ACCESS);
maxNumWithHapiSigsAccess = properties.getLongProperty(CONTRACTS_MAX_NUM_WITH_HAPI_SIGS_ACCESS);
maxAutoAssociations = properties.getIntProperty(LEDGER_MAX_AUTO_ASSOCIATIONS);
sumOfConsensusWeights = properties.getIntProperty(STAKING_SUM_OF_CONSENSUS_WEIGHTS);
}

public int sumOfConsensusWeights() {
return sumOfConsensusWeights;
}

public int maxTokensPerAccount() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,27 +158,33 @@ public void updateNodes(final Instant consensusTime) {

// Update node stake infos for the record with the updated consensus weights.
// The weights are updated based on the updated stake of the node.
for (int i = 0; i < nodeStakingInfosBuilder.size(); i++) {
final var builder = nodeStakingInfosBuilder.get(i);
for (final NodeStake.Builder builder : nodeStakingInfosBuilder) {
final var sumOfConsensusWeights = dynamicProperties.sumOfConsensusWeights();
final var nodeNum = builder.getNodeId();
final var stakingInfo = curStakingInfos.getForModify(EntityNum.fromLong(nodeNum));
// If the total stake(rewarded + non-rewarded) of a node is less than minStake, stakingInfo's stake field
// represents 0, as per calculation done in reviewElectionsAndRecomputeStakes.
// Similarly, the total stake(rewarded + non-rewarded) of the node is greater than maxStake,
// stakingInfo's stake field is set to maxStake.So, there is no need to clamp the stake value here. Sum of
// all stakes can be used to calculate the weight.
final var updatedWeight = calculateWeightFromStake(stakingInfo.getStake(), newTotalStakedStart);
final var updatedWeight =
calculateWeightFromStake(stakingInfo.getStake(), newTotalStakedStart, sumOfConsensusWeights);
final var oldWeight = stakingInfo.getWeight();
stakingInfo.setWeight(updatedWeight);
log.info("Node {} weight is updated. Old weight {}, updated weight {}", nodeNum, oldWeight, updatedWeight);

// Scale the consensus weight range [0, 500] to [minStake, trueMaxStakeOfAllNodes] range and export
// Scale the consensus weight range [0, sumOfConsensusWeights] to [minStake, trueMaxStakeOfAllNodes] range
// and export
// to mirror node. We need to consider the true maxStakeOfAllNodes instead of maxStake because
// for a one node network, whose stake < maxStake, we assign a weight of 500 to the node.
// for a one node network, whose stake < maxStake, we assign a weight of sumOfConsensusWeights to the node.
// When we scale it back to stake, we need to give back the real stake of the node
// instead of maxStake set on the node.
final var scaledWeightToStake = scaleUpWeightToStake(
updatedWeight, stakingInfo.getMinStake(), maxStakeOfAllNodes, newTotalStakedStart);
updatedWeight,
stakingInfo.getMinStake(),
maxStakeOfAllNodes,
newTotalStakedStart,
sumOfConsensusWeights);
builder.setStake(scaledWeightToStake);
nodeStakingInfos.add(builder.build());
}
Expand All @@ -203,17 +209,19 @@ public void updateNodes(final Instant consensusTime) {
}

/**
* Scales up the weight of the node to the range [minStake, maxSTakeOfAllNodes] from the consensus weight range [0, 500].
* Scales up the weight of the node to the range [minStake, maxStakeOfAllNodes] from the consensus weight range [0, sumOfConsensusWeights].
* @param weight weight of the node
* @param minStake min stake of the node
* @param maxStakeOfAllNodes real max stake of all nodes computed by taking max(stakeOfNode1, stakeOfNode2, ...)
* @param newMinStake min stake of the node
* @param newMaxStake real max stake of all nodes computed by taking max(stakeOfNode1, stakeOfNode2, ...)
* @return scaled weight of the node
*/
long scaleUpWeightToStake(
final int weight, final long minStake, final long maxStakeOfAllNodes, final long totalStakeOfAllNodes) {
final int weight,
final long newMinStake,
final long newMaxStake,
final long totalStakeOfAllNodes,
final int sumOfConsensusWeights) {
final var zeroStake = 0;
final var newMin = minStake;
final var newMax = maxStakeOfAllNodes;
// If zero stake return zero
if (weight == zeroStake) {
return zeroStake;
Expand All @@ -222,37 +230,38 @@ long scaleUpWeightToStake(
if (totalStakeOfAllNodes == 0) {
return 0;
}
final var oldMinWeight = 1;
final var oldMinWeight = 1L;
// Since we are calculating weights based on the real stake values, we need to consider
// the real max Stake and not theoretical max stake of nodes.
// Otherwise, on a one node network with a stake < maxStake where we assign a weight of 500,
// Otherwise, on a one node network with a stake < maxStake where we assign a weight of
// sumOfConsensusWeights,
// the scaled stake value will be greater than its real stake.
final var oldMaxWeight = BigInteger.valueOf(maxStakeOfAllNodes)
.multiply(BigInteger.valueOf(500))
final var oldMaxWeight = BigInteger.valueOf(newMaxStake)
.multiply(BigInteger.valueOf(sumOfConsensusWeights))
.divide(BigInteger.valueOf(totalStakeOfAllNodes))
.longValue();
// Otherwise compute the interpolation of the weight in the range [minStake, maxStake]
return BigInteger.valueOf(newMax)
.subtract(BigInteger.valueOf(newMin))
return BigInteger.valueOf(newMaxStake)
.subtract(BigInteger.valueOf(newMinStake))
.multiply(BigInteger.valueOf(weight - oldMinWeight))
.divide(BigInteger.valueOf(oldMaxWeight - oldMinWeight))
.add(BigInteger.valueOf(newMin))
.add(BigInteger.valueOf(newMinStake))
.longValue();
}
}

/**
* Calculates consensus weight of the node. The network normalizes the weights of nodes above minStake so that the
* total sum of weight is approximately 500. The stake field in {@code MerkleStakingInfo} is already clamped to
* [minStake, maxStake].
* total sum of weight is approximately as described by {@code GlobalDynamicProperties#sumOfConsensusWeights}.
* The stake field in {@code MerkleStakingInfo} is already clamped to [minStake, maxStake].
* If stake is less than minStake the weight of a node A will be 0. If stake is greater than minStake, the weight of a node A
* will be computed so that every node above minStake has weight at least 1; but any node that has staked at least 1
* out of every 250 whole hbars staked will have weight >= 2.
* @param stake the stake of current node, includes stake rewarded and non-rewarded
* @param totalStakeOfAllNodes the total stake of all nodes at the start of new period
* @return calculated consensus weight of the node
*/
int calculateWeightFromStake(long stake, long totalStakeOfAllNodes) {
int calculateWeightFromStake(final long stake, final long totalStakeOfAllNodes, final int sumOfConsensusWeights) {
// if node's total stake is less than minStake, MerkleStakingInfo stake will be zero as per calculation
// in reviewElectionsAndRecomputeStakes and weight will be zero.
if (stake == 0) {
Expand All @@ -265,7 +274,7 @@ int calculateWeightFromStake(long stake, long totalStakeOfAllNodes) {
return 0;
}
final var weight = BigInteger.valueOf(stake)
.multiply(BigInteger.valueOf(500))
.multiply(BigInteger.valueOf(sumOfConsensusWeights))
.divide(BigInteger.valueOf(totalStakeOfAllNodes))
.longValue();
return (int) Math.max(weight, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
import com.hedera.node.app.service.mono.state.virtual.ContractKey;
import com.hedera.node.app.service.mono.state.virtual.KeyPackingUtils;
import com.hedera.node.app.service.mono.utils.EntityNum;
import com.swirlds.common.exceptions.MutabilityException;
import com.swirlds.base.state.MutabilityException;
import com.swirlds.common.io.streams.SerializableDataInputStream;
import com.swirlds.common.io.streams.SerializableDataOutputStream;
import com.swirlds.common.merkle.MerkleLeaf;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
import com.google.common.base.MoreObjects;
import com.hedera.node.app.service.mono.context.properties.BootstrapProperties;
import com.hedera.node.app.service.mono.utils.EntityNum;
import com.swirlds.base.state.MutabilityException;
import com.swirlds.common.crypto.DigestType;
import com.swirlds.common.crypto.Hash;
import com.swirlds.common.exceptions.MutabilityException;
import com.swirlds.common.io.streams.SerializableDataInputStream;
import com.swirlds.common.io.streams.SerializableDataOutputStream;
import com.swirlds.common.merkle.MerkleLeaf;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.hedera.node.app.service.mono.state.virtual;

import com.swirlds.common.exceptions.MutabilityException;
import com.swirlds.base.state.MutabilityException;
import com.swirlds.common.io.streams.SerializableDataInputStream;
import com.swirlds.common.io.streams.SerializableDataOutputStream;
import com.swirlds.virtualmap.VirtualValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,4 @@ tokens.autoCreations.isEnabled=true
virtualdatasource.jasperdbToMerkledb=false
accounts.blocklist.enabled=false
accounts.blocklist.resource=
staking.sumOfConsensusWeights=500
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import com.hedera.node.app.service.mono.state.merkle.MerkleNetworkContext;
import com.hedera.node.app.service.mono.state.merkle.MerkleScheduledTransactions;
import com.hedera.node.app.service.mono.state.merkle.MerkleSpecialFiles;
import com.hedera.node.app.service.mono.state.merkle.MerkleStakingInfo;
import com.hedera.node.app.service.mono.state.migration.MapMigrationToDisk;
import com.hedera.node.app.service.mono.state.migration.StakingInfoMapBuilder;
import com.hedera.node.app.service.mono.state.migration.StateChildIndices;
Expand All @@ -79,12 +80,12 @@
import com.hedera.test.utils.IdUtils;
import com.hedera.test.utils.ResponsibleVMapUser;
import com.hederahashgraph.api.proto.java.SemanticVersion;
import com.swirlds.base.state.MutabilityException;
import com.swirlds.common.context.PlatformContext;
import com.swirlds.common.crypto.CryptographyHolder;
import com.swirlds.common.crypto.Hash;
import com.swirlds.common.crypto.RunningHash;
import com.swirlds.common.crypto.engine.CryptoEngine;
import com.swirlds.common.exceptions.MutabilityException;
import com.swirlds.common.system.InitTrigger;
import com.swirlds.common.system.NodeId;
import com.swirlds.common.system.Platform;
Expand Down Expand Up @@ -868,6 +869,33 @@ void testUniqueTokensWhenMerkleMap() {
assertSame(mmap, subject.uniqueTokens().merkleMap());
}

@Test
void updatesAddressBookWithZeroWeightOnGenesisStart() {
final MerkleMap<EntityNum, MerkleStakingInfo> stakingMap = subject.getChild(StateChildIndices.STAKING_INFO);
assertEquals(1, stakingMap.size());
assertEquals(0, stakingMap.get(EntityNum.fromLong(0L)).getWeight());

subject.updateWeight(addressBook, platform.getContext());
verify(addressBook).updateWeight(0, 0);
}

@Test
void updatesAddressBookWithNonZeroWeightsOnGenesisStart() {
final MerkleMap<EntityNum, MerkleStakingInfo> stakingMap = subject.getChild(StateChildIndices.STAKING_INFO);
assertEquals(1, stakingMap.size());
assertEquals(0, stakingMap.get(EntityNum.fromLong(0L)).getWeight());

stakingMap.forEach((k, v) -> {
v.setStake(1000L);
v.setWeight(500);
});
assertEquals(1000L, stakingMap.get(EntityNum.fromLong(0L)).getStake());
subject.setChild(StateChildIndices.STAKING_INFO, stakingMap);

subject.updateWeight(addressBook, platform.getContext());
verify(addressBook).updateWeight(0, 500);
}

private static ServicesApp createApp(final Platform platform) {
return DaggerServicesApp.builder()
.initTrigger(InitTrigger.GENESIS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_REWARD_RATE;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_STARTUP_HELPER_RECOMPUTE;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_START_THRESH;
import static com.hedera.node.app.spi.config.PropertyNames.STAKING_SUM_OF_CONSENSUS_WEIGHTS;
import static com.hedera.node.app.spi.config.PropertyNames.STATS_CONS_THROTTLES_TO_SAMPLE;
import static com.hedera.node.app.spi.config.PropertyNames.STATS_ENTITY_UTILS_GAUGE_UPDATE_INTERVAL_MS;
import static com.hedera.node.app.spi.config.PropertyNames.STATS_EXECUTION_TIMES_TO_TRACK;
Expand Down Expand Up @@ -535,7 +536,8 @@ class BootstrapPropertiesTest {
entry(WORKFLOWS_ENABLED, Set.of()),
entry(VIRTUALDATASOURCE_JASPERDB_TO_MERKLEDB, true),
entry(ACCOUNTS_BLOCKLIST_ENABLED, true),
entry(ACCOUNTS_BLOCKLIST_RESOURCE, "evm-addresses-blocklist.csv"));
entry(ACCOUNTS_BLOCKLIST_RESOURCE, "evm-addresses-blocklist.csv"),
entry(STAKING_SUM_OF_CONSENSUS_WEIGHTS, 500));

@Test
void containsProperty() {
Expand Down
Loading

0 comments on commit edb8da9

Please sign in to comment.