Skip to content

Commit

Permalink
Merge pull request #686 from LimeChain/401-scheduled-and-forced-autho…
Browse files Browse the repository at this point in the history
…rity-set-changes

Scheduled and Forced Authority Set Change Handling
Grigorov-Georgi authored Jan 16, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 72c928b + 48bfb8a commit e76171c
Showing 19 changed files with 200 additions and 173 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/limechain/babe/Authorship.java
Original file line number Diff line number Diff line change
@@ -194,7 +194,7 @@ public static Integer getSecondarySlotAuthor(byte[] randomness,
var authorityIndex = rand.mod(authoritiesCount);

if (authorityIndex.compareTo(authoritiesCount) < 0) {
return authorityIndex.intValue();
return authorityIndex.intValueExact();
}

return null;
72 changes: 67 additions & 5 deletions src/main/java/com/limechain/grandpa/state/RoundState.java
Original file line number Diff line number Diff line change
@@ -4,26 +4,34 @@
import com.limechain.chain.lightsyncstate.LightSyncState;
import com.limechain.network.protocol.grandpa.messages.catchup.res.SignedVote;
import com.limechain.network.protocol.grandpa.messages.commit.Vote;
import com.limechain.network.protocol.grandpa.messages.consensus.GrandpaConsensusMessage;
import com.limechain.storage.DBConstants;
import com.limechain.storage.KVRepository;
import com.limechain.storage.StateUtil;
import com.limechain.sync.warpsync.dto.AuthoritySetChange;
import com.limechain.sync.warpsync.dto.ForcedAuthoritySetChange;
import com.limechain.sync.warpsync.dto.ScheduledAuthoritySetChange;
import io.libp2p.core.crypto.PubKey;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.java.Log;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;

/**
* Represents the state information for the current round and authorities that are needed
* for block finalization with GRANDPA.
* Note: Intended for use only when the host is configured as an Authoring Node.
*/
@Log
@Getter
@Setter //TODO: remove it when initialize() method is implemented
@RequiredArgsConstructor
@@ -36,6 +44,8 @@ public class RoundState {
private BigInteger setId;
private BigInteger roundNumber;

private final PriorityQueue<AuthoritySetChange> authoritySetChanges = new PriorityQueue<>(AuthoritySetChange.getComparator());

//TODO: This may not be the best place for those maps
private Map<PubKey, Vote> precommits = new ConcurrentHashMap<>();
private Map<PubKey, Vote> prevotes = new ConcurrentHashMap<>();
@@ -127,17 +137,69 @@ public void persistState() {
savePrevotes();
}

public BigInteger incrementSetId() {
public void startNewSet(List<Authority> authorities) {
this.setId = setId.add(BigInteger.ONE);
return setId;
}
this.roundNumber = BigInteger.ZERO;
this.authorities = authorities;

public void resetRound() {
this.roundNumber = BigInteger.ONE;
log.log(Level.INFO, "Successfully transitioned to authority set id: " + setId);
}

public void setLightSyncState(LightSyncState initState) {
this.setId = initState.getGrandpaAuthoritySet().getSetId();
this.authorities = Arrays.asList(initState.getGrandpaAuthoritySet().getCurrentAuthorities());
}

/**
* Apply scheduled or forced authority set changes from the queue if present
*
* @param blockNumber required to determine if it's time to apply the change
*/
public boolean handleAuthoritySetChange(BigInteger blockNumber) {
AuthoritySetChange changeSetData = authoritySetChanges.peek();

boolean updated = false;
while (changeSetData != null) {

if (changeSetData.getApplicationBlockNumber().compareTo(blockNumber) > 0) {
break;
}

startNewSet(changeSetData.getAuthorities());
authoritySetChanges.poll();
updated = true;

changeSetData = authoritySetChanges.peek();
}

return updated;
}

public void handleGrandpaConsensusMessage(GrandpaConsensusMessage consensusMessage, BigInteger currentBlockNumber) {
switch (consensusMessage.getFormat()) {
case GRANDPA_SCHEDULED_CHANGE -> authoritySetChanges.add(new ScheduledAuthoritySetChange(
consensusMessage.getAuthorities(),
consensusMessage.getDelay(),
currentBlockNumber
));
case GRANDPA_FORCED_CHANGE -> authoritySetChanges.add(new ForcedAuthoritySetChange(
consensusMessage.getAuthorities(),
consensusMessage.getDelay(),
consensusMessage.getAdditionalOffset(),
currentBlockNumber
));
//TODO: Implement later
case GRANDPA_ON_DISABLED -> {
log.log(Level.SEVERE, "'ON DISABLED' grandpa message not implemented");
}
case GRANDPA_PAUSE -> {
log.log(Level.SEVERE, "'PAUSE' grandpa message not implemented");
}
case GRANDPA_RESUME -> {
log.log(Level.SEVERE, "'RESUME' grandpa message not implemented");
}
}

log.fine(String.format("Updated grandpa set config: %s", consensusMessage.getFormat().toString()));
}
}
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ private void write(ScaleCodecWriter writer, BlockHeader blockHeader, boolean sea
writer.writeUint256(blockHeader.getParentHash().getBytes());
// NOTE: Usage of BlockNumberWriter is intentionally omitted here,
// since we want this to be a compact int, not a var size int
writer.writeCompact(blockHeader.getBlockNumber().intValue());
writer.writeCompact(blockHeader.getBlockNumber().intValueExact());
writer.writeUint256(blockHeader.getStateRoot().getBytes());
writer.writeUint256(blockHeader.getExtrinsicsRoot().getBytes());

Original file line number Diff line number Diff line change
@@ -8,9 +8,10 @@

@Data
public class GrandpaConsensusMessage {
private BigInteger delayStartBlockNumber;
private GrandpaConsensusMessageFormat format;
private List<Authority> authorities;
private BigInteger disabledAuthority;
private long delay;
private GrandpaConsensusMessageFormat format;
private BigInteger delay;
// this is denoted as 'm' in the polkadot spec
private BigInteger additionalOffset;
}
Original file line number Diff line number Diff line change
@@ -14,27 +14,34 @@ public class GrandpaConsensusMessageReader implements ScaleReader<GrandpaConsens

@Override
public GrandpaConsensusMessage read(ScaleCodecReader reader) {

GrandpaConsensusMessage grandpaConsensusMessage = new GrandpaConsensusMessage();
GrandpaConsensusMessageFormat format = GrandpaConsensusMessageFormat.fromFormat(reader.readByte());
grandpaConsensusMessage.setFormat(format);

switch (format) {
case GRANDPA_SCHEDULED_CHANGE -> {
List<Authority> authorities = reader.read(new ListReader<>(new AuthorityReader()));
long delay = reader.readUint32();
BigInteger delay = BigInteger.valueOf(reader.readUint32());

grandpaConsensusMessage.setAuthorities(authorities);
grandpaConsensusMessage.setDelay(delay);
}
case GRANDPA_FORCED_CHANGE -> {
long delayStartBlockNumber = reader.readUint32();
BigInteger additionalOffset = BigInteger.valueOf(reader.readUint32());
List<Authority> authorities = reader.read(new ListReader<>(new AuthorityReader()));
long delay = reader.readUint32();
grandpaConsensusMessage.setDelayStartBlockNumber(BigInteger.valueOf(delayStartBlockNumber));
BigInteger delay = BigInteger.valueOf(reader.readUint32());

grandpaConsensusMessage.setAuthorities(authorities);
grandpaConsensusMessage.setDelay(delay);
grandpaConsensusMessage.setAdditionalOffset(additionalOffset);
}
case GRANDPA_ON_DISABLED -> grandpaConsensusMessage.setDisabledAuthority(new UInt64Reader().read(reader));
case GRANDPA_PAUSE, GRANDPA_RESUME -> grandpaConsensusMessage.setDelay(reader.readUint32());
case GRANDPA_PAUSE, GRANDPA_RESUME -> grandpaConsensusMessage.setDelay(
BigInteger.valueOf(reader.readUint32())
);
}

return grandpaConsensusMessage;
}
}
Original file line number Diff line number Diff line change
@@ -22,9 +22,9 @@ public void write(ScaleCodecWriter wrt, BigInteger value) throws IOException {
throw new IllegalArgumentException("Negative values are not supported: " + value);
}
if (blockNumSize > 0) {
wrt.directWrite(value.and(BigInteger.valueOf(255L)).intValue());
wrt.directWrite(value.and(BigInteger.valueOf(255L)).intValueExact());
for (int i = 1; i < blockNumSize; i++) {
wrt.directWrite(value.shiftRight(8 * i).and(BigInteger.valueOf(255L)).intValue());
wrt.directWrite(value.shiftRight(8 * i).and(BigInteger.valueOf(255L)).intValueExact());
}
}
}
12 changes: 11 additions & 1 deletion src/main/java/com/limechain/storage/block/BlockHandler.java
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

import com.limechain.babe.BlockProductionVerifier;
import com.limechain.babe.state.EpochState;
import com.limechain.grandpa.state.RoundState;
import com.limechain.network.PeerMessageCoordinator;
import com.limechain.network.PeerRequester;
import com.limechain.network.protocol.message.ProtocolMessageBuilder;
@@ -27,6 +28,7 @@ public class BlockHandler {

private final BlockState blockState;
private final EpochState epochState;
private final RoundState roundState;

private final PeerRequester requester;
private final PeerMessageCoordinator messageCoordinator;
@@ -40,7 +42,9 @@ public BlockHandler(EpochState epochState,
PeerRequester requester,
RuntimeBuilder builder,
TransactionProcessor transactionProcessor,
PeerMessageCoordinator messageCoordinator) {
PeerMessageCoordinator messageCoordinator,
RoundState roundState) {

this.epochState = epochState;
this.requester = requester;
this.messageCoordinator = messageCoordinator;
@@ -49,6 +53,7 @@ public BlockHandler(EpochState epochState,
this.verifier = new BlockProductionVerifier();
blockState = BlockState.getInstance();
asyncExecutor = AsyncExecutor.withPoolSize(10);
this.roundState = roundState;
}

public synchronized void handleBlockHeader(Instant arrivalTime, BlockHeader header, PeerId excluding) {
@@ -116,6 +121,11 @@ private void handleBlock(Block block, Instant arrivalTime) {
log.fine(String.format("Updated epoch block config: %s", cm.getFormat().toString()));
});

DigestHelper.getGrandpaConsensusMessage(header.getDigest())
.ifPresent(cm -> roundState.handleGrandpaConsensusMessage(cm, header.getBlockNumber()));

roundState.handleAuthoritySetChange(header.getBlockNumber());

asyncExecutor.executeAndForget(() -> transactionProcessor.maintainTransactionPool(block));
}
}
4 changes: 2 additions & 2 deletions src/main/java/com/limechain/storage/block/BlockState.java
Original file line number Diff line number Diff line change
@@ -582,9 +582,9 @@ public List<Hash256> retrieveRangeFromDatabase(final Hash256 startHash, final Bl

BigInteger blocksInRange = endHeader.getBlockNumber()
.subtract(startHeader.getBlockNumber()).add(BigInteger.ONE);
List<Hash256> hashes = new ArrayList<>(blocksInRange.intValue());
List<Hash256> hashes = new ArrayList<>(blocksInRange.intValueExact());

int lastPosition = blocksInRange.intValue() - 1;
int lastPosition = blocksInRange.intValueExact() - 1;

hashes.add(0, startHash);
hashes.add(lastPosition, endHeader.getHash());
Original file line number Diff line number Diff line change
@@ -115,7 +115,7 @@ public void start() {

int startNumber = syncState.getLastFinalizedBlockNumber()
.add(BigInteger.ONE)
.intValue();
.intValueExact();

int blocksToFetch = 100;
List<Block> receivedBlocks = requester.requestBlocks(BlockRequestField.ALL, startNumber, blocksToFetch).join();
Loading

0 comments on commit e76171c

Please sign in to comment.