diff --git a/src/main/java/com/limechain/grandpa/round/CompletedStage.java b/src/main/java/com/limechain/grandpa/round/CompletedStage.java
index 114f2efe..2eefd5a5 100644
--- a/src/main/java/com/limechain/grandpa/round/CompletedStage.java
+++ b/src/main/java/com/limechain/grandpa/round/CompletedStage.java
@@ -8,7 +8,7 @@ public class CompletedStage implements StageState {
@Override
public void start(GrandpaRound round) {
round.setOnFinalizeHandler(null);
- round.getOnStageTimerHandler().shutdown();
+ round.clearOnStageTimerHandler();
end(round);
}
diff --git a/src/main/java/com/limechain/grandpa/round/GrandpaRound.java b/src/main/java/com/limechain/grandpa/round/GrandpaRound.java
index 41b327af..5057056a 100644
--- a/src/main/java/com/limechain/grandpa/round/GrandpaRound.java
+++ b/src/main/java/com/limechain/grandpa/round/GrandpaRound.java
@@ -103,6 +103,12 @@ public class GrandpaRound {
@Nullable
private Vote preCommitChoice;
+ /**
+ * Updated when the Grandpa Ghost is calculated.
+ * Default value is false.
+ */
+ private boolean isCompletable;
+
private Map preVotes = new ConcurrentHashMap<>();
private Map preCommits = new ConcurrentHashMap<>();
private Vote primaryVote;
@@ -248,7 +254,7 @@ private boolean isFinalizable() {
*
* @return if the current round is completable
*/
- private boolean isCompletable() {
+ public boolean checkIfCompletable() {
Map votes = getDirectVotes(SubRound.PRE_COMMIT);
long votesCount = votes.values().stream()
@@ -350,7 +356,7 @@ private BlockHeader findBestFinalCandidate() {
*
* @return GRANDPA GHOST block as a vote
*/
- private BlockHeader findGrandpaGhost() {
+ public BlockHeader findGrandpaGhost() {
if (roundNumber.equals(BigInteger.ZERO)) {
return lastFinalizedBlock;
@@ -362,10 +368,11 @@ private BlockHeader findGrandpaGhost() {
throw new GhostExecutionException("GHOST not found");
}
- BlockHeader grandpaGhost = selectBlockWithMostVotes(blocks, getPrevBestFinalCandidate());
- this.grandpaGhost = grandpaGhost;
+ BlockHeader result = selectBlockWithMostVotes(blocks, getPrevBestFinalCandidate());
+ this.grandpaGhost = result;
+ this.isCompletable = checkIfCompletable();
- return grandpaGhost;
+ return result;
}
/**
@@ -650,4 +657,11 @@ public void broadcastCommitMessage() {
peerMessageCoordinator.sendCommitMessageToPeers(commitMessage);
}
+
+ public void clearOnStageTimerHandler() {
+ if (onStageTimerHandler != null && onStageTimerHandler.isShutdown()) {
+ onStageTimerHandler.shutdown();
+ onStageTimerHandler = null;
+ }
+ }
}
diff --git a/src/main/java/com/limechain/grandpa/round/PreCommitStage.java b/src/main/java/com/limechain/grandpa/round/PreCommitStage.java
index 84ea33d8..f2bf6083 100644
--- a/src/main/java/com/limechain/grandpa/round/PreCommitStage.java
+++ b/src/main/java/com/limechain/grandpa/round/PreCommitStage.java
@@ -1,13 +1,51 @@
package com.limechain.grandpa.round;
+import com.limechain.exception.grandpa.GrandpaGenericException;
+import com.limechain.grandpa.vote.SubRound;
+import com.limechain.grandpa.vote.Vote;
+import lombok.extern.java.Log;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+@Log
public class PreCommitStage implements StageState {
@Override
public void start(GrandpaRound round) {
+ log.fine(String.format("Round %d started pre-commit stage.", round.getRoundNumber()));
+
+ if (round.isCompletable()) {
+ end(round);
+ return;
+ }
+
+ long timeElapsed = System.currentTimeMillis() - round.getStartTime().toEpochMilli();
+ long timeRemaining = (4 * GrandpaRound.DURATION) - timeElapsed;
+
+ round.setOnStageTimerHandler(Executors.newScheduledThreadPool(1));
+ round.getOnStageTimerHandler().schedule(() -> {
+ log.fine(String.format("Round %d timer triggered.", round.getRoundNumber()));
+ end(round);
+ }, timeRemaining, TimeUnit.MILLISECONDS);
}
@Override
public void end(GrandpaRound round) {
- }
-}
+ round.clearOnStageTimerHandler();
+
+ try {
+
+ Vote grandpaGhost = Vote.fromBlockHeader(round.getGrandpaGhost());
+ log.fine(String.format("Round %d ended pre-commit stage.", round.getRoundNumber()));
+
+ round.broadcastVoteMessage(grandpaGhost, SubRound.PRE_COMMIT);
+ round.setState(new FinalizeStage());
+ round.getState().start(round);
+
+ } catch (GrandpaGenericException e) {
+ log.fine(String.format("Round %d cannot end now: %s", round.getRoundNumber(), e.getMessage()));
+ }
+ }
+}
\ No newline at end of file