Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: roster change objects #9908

Merged
merged 10 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public long getClassId() {
* @return the round when this address book was constructed, or {@link #UNKNOWN_ROUND} if the round is unknown or
* this address book was not constructed during a regular round
*/
public long getRound() {
public long getRound() { // TODO remove
edward-swirldslabs marked this conversation as resolved.
Show resolved Hide resolved
return round;
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.swirlds.platform.roster;

import com.swirlds.common.system.NodeId;
Expand All @@ -9,22 +25,17 @@
* Describes the difference between two rosters. Although it is possible to derive this information by comparing the
* {@link AddressBook} objects directly, this data is distilled and provided in this format for convenience.
*
* @param previousRoster the previous roster
* @param newRoster the new roster
* @param consensusWeightChanged whether the consensus weight changed
* @param membershipChanged whether the membership changed
* @param addedNodes the nodes that were added
* @param removedNodes the nodes that were removed
* @param modifiedNodes the nodes that were modified
* @param rostersAreIdentical whether the rosters are identical
* @param consensusWeightChanged whether the consensus weight changed
* @param membershipChanged whether the membership changed
*/
public record RosterDiff(
@NonNull AddressBook previousRoster,
@NonNull AddressBook newRoster,
@NonNull UpdatedRoster newRoster,
boolean rosterIsIdentical,
boolean consensusWeightChanged,
boolean membershipChanged,
cody-littley marked this conversation as resolved.
Show resolved Hide resolved
@NonNull List<NodeId> addedNodes,
@NonNull List<NodeId> removedNodes,
@NonNull List<AddressDiff> modifiedNodes,
boolean rostersAreIdentical,
boolean consensusWeightChanged,
boolean membershipChanged) {
}
@NonNull List<NodeId> modifiedNodes) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.swirlds.platform.roster;

import static com.swirlds.logging.legacy.LogMarker.EXCEPTION;

import com.swirlds.common.context.PlatformContext;
import com.swirlds.common.crypto.Cryptography;
import com.swirlds.common.system.NodeId;
import com.swirlds.common.system.address.Address;
import com.swirlds.common.system.address.AddressBook;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

// TODO unit test

/**
* Takes as input a sequence of {@link UpdatedRoster} objects and produces a sequence of {@link RosterDiff} objects.
*/
public class RosterDiffGenerator {

private static final Logger logger = LogManager.getLogger();
litt3 marked this conversation as resolved.
Show resolved Hide resolved

private AddressBook previousRoster;
private long previousEffectiveRound;

private final Cryptography cryptography;

/**
* Constructor.
*
* @param platformContext the platform context
*/
public RosterDiffGenerator(@NonNull final PlatformContext platformContext) {
cryptography = platformContext.getCryptography();
}

/**
* Given a new roster, generate a diff with respect to the previous roster.
*
* @param updatedRoster the new roster
* @return the diff, will return null for the very first roster added
*/
@Nullable
public RosterDiff generateDiff(@NonNull final UpdatedRoster updatedRoster) {
final AddressBook roster = updatedRoster.roster();
final long effectiveRound = updatedRoster.effectiveRound();

try {
if (previousRoster == null) {
// This is the first roster we've seen.
return null;
edward-swirldslabs marked this conversation as resolved.
Show resolved Hide resolved
}

if (previousEffectiveRound >= effectiveRound) {
logger.error(
EXCEPTION.getMarker(),
"Effective rounds should always increase over time. "
+ "Previous effective round: {}, new effective round: {}",
previousEffectiveRound,
effectiveRound);
}

if (roster.getHash() == null) {
// FUTURE WORK: when this is wired into the system, make sure this is thread safe
cryptography.digestSync(roster);
}
cody-littley marked this conversation as resolved.
Show resolved Hide resolved

return compareRosters(previousRoster, updatedRoster);
} finally {
previousRoster = roster;
previousEffectiveRound = effectiveRound;
}
}

/**
* Compare two rosters and generate a diff.
*
* @param previousRoster the previous roster
* @param updatedRoster describes the roster
* @return the difference between the new and the previous roster
*/
@NonNull
private static RosterDiff compareRosters(
@NonNull final AddressBook previousRoster, @NonNull final UpdatedRoster updatedRoster) {

final AddressBook roster = updatedRoster.roster();

// FUTURE WORK: make sure we've hashed the roster prior to this point in time
if (roster.getHash().equals(previousRoster.getHash())) {
// Simple case: the roster is identical to the previous one.
// Short circuit the diff generation for the sake of efficiency.
return new RosterDiff(updatedRoster, true, false, false, List.of(), List.of(), List.of());
}

final Set<NodeId> previousNodes = previousRoster.getNodeIdSet();
final Set<NodeId> currentNodes = roster.getNodeIdSet();

final List<NodeId> removedNodes = new ArrayList<>();
final List<NodeId> addedNodes = new ArrayList<>();
final List<NodeId> modifiedNodes = new ArrayList<>();
boolean consensusWeightChanged = false;
boolean membershipChanged = false;

// Find the nodes that have been removed.
for (final NodeId nodeId : previousNodes) {
if (!currentNodes.contains(nodeId)) {
removedNodes.add(nodeId);
membershipChanged = true;
consensusWeightChanged = true;
}
}

// Find the nodes that have been added or modified.
for (final NodeId nodeId : currentNodes) {
if (previousNodes.contains(nodeId)) {
final Address previousAddress = previousRoster.getAddress(nodeId);
final Address currentAddress = roster.getAddress(nodeId);
if (!previousAddress.equals(currentAddress)) {
modifiedNodes.add(nodeId);
}
if (previousAddress.getWeight() != currentAddress.getWeight()) {
consensusWeightChanged = true;
}
} else {
addedNodes.add(nodeId);
membershipChanged = true;
consensusWeightChanged = true;
}
}

return new RosterDiff(
updatedRoster,
false,
consensusWeightChanged,
membershipChanged,
addedNodes,
removedNodes,
modifiedNodes);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.swirlds.platform.roster;

import com.swirlds.common.system.address.AddressBook;
import edu.umd.cs.findbugs.annotations.NonNull;

/**
* Signals that a new roster has been selected. This happens once per round regardless of whether or not the new roster
* is any different that the prior roster.
*
* @param effectiveRound the round in which this roster becomes effective
* @param roster the new roster
*/
public record UpdatedRoster(long effectiveRound, @NonNull AddressBook roster) {}
Loading
Loading