Don't allow block generation unless system clock is accurate.

Controller performs NTP check on startup (and every 5 minutes)
which determines whether block generation is allowed.

System Tray tooltip updated to reflect generating status.
Plus new translations.

Improved GuiTests.

BlockGenerator fetches forging accounts first, and sleeps
if none configured, which is less work than processing peer lists.
This commit is contained in:
catbref 2019-07-25 12:42:08 +01:00
parent 73e53120a9
commit 671dc5995a
5 changed files with 54 additions and 19 deletions

View File

@ -79,6 +79,15 @@ public class BlockGenerator extends Thread {
return;
}
// If Controller says we can't generate, then don't...
if (!Controller.getInstance().isGenerationAllowed())
continue;
List<ForgingAccountData> forgingAccountsData = repository.getAccountRepository().getForgingAccounts();
// No forging accounts?
if (forgingAccountsData.isEmpty())
continue;
List<Peer> peers = Network.getInstance().getUniqueHandshakedPeers();
BlockData lastBlockData = blockRepository.getLastBlock();
@ -109,11 +118,6 @@ public class BlockGenerator extends Thread {
}
// Do we need to build any potential new blocks?
List<ForgingAccountData> forgingAccountsData = repository.getAccountRepository().getForgingAccounts();
// No forging accounts?
if (forgingAccountsData.isEmpty())
continue;
List<PrivateKeyAccount> forgingAccounts = forgingAccountsData.stream().map(accountData -> new PrivateKeyAccount(repository, accountData.getSeed())).collect(Collectors.toList());
// Discard accounts we have blocks for

View File

@ -91,7 +91,7 @@ public class Controller extends Thread {
private static final String repositoryUrlTemplate = "jdbc:hsqldb:file:%s/blockchain;create=true;hsqldb.full_log_replay=true";
private static final long ARBITRARY_REQUEST_TIMEOUT = 5 * 1000; // ms
private static final long REPOSITORY_BACKUP_PERIOD = 123 * 60 * 1000; // ms
private static final long NTP_NAG_PERIOD = 5 * 60 * 1000; // ms
private static final long NTP_CHECK_PERIOD = 5 * 60 * 1000; // ms
private static final long MAX_NTP_OFFSET = 500; // ms
private static volatile boolean isStopping = false;
@ -103,7 +103,9 @@ public class Controller extends Thread {
private final long buildTimestamp; // seconds
private long repositoryBackupTimestamp = startTime + REPOSITORY_BACKUP_PERIOD;
private long ntpNagTimestamp = startTime + NTP_NAG_PERIOD;
private long ntpCheckTimestamp = startTime; // ms
/** Whether BlockGenerator is allowed to generate blocks. Mostly determined by system clock accuracy. */
private boolean isGenerationAllowed = false;
/** Signature of peer's latest block when we tried to sync but peer had inferior chain. */
private byte[] inferiorChainPeerBlockSignature = null;
@ -202,6 +204,10 @@ public class Controller extends Thread {
return this.blockchainLock;
}
public boolean isGenerationAllowed() {
return this.isGenerationAllowed;
}
// Entry point
public static void main(String args[]) {
@ -321,9 +327,13 @@ public class Controller extends Thread {
}
// Potentially nag end-user about NTP
if (System.currentTimeMillis() >= ntpNagTimestamp) {
ntpNagTimestamp += NTP_NAG_PERIOD;
ntpNag();
if (System.currentTimeMillis() >= ntpCheckTimestamp) {
ntpCheckTimestamp += NTP_CHECK_PERIOD;
Boolean isClockAccurate = ntpCheck();
if (isClockAccurate != null) {
isGenerationAllowed = isClockAccurate;
updateSysTray();
}
}
// Prune stuck/slow/old peers
@ -422,8 +432,12 @@ public class Controller extends Thread {
}
}
/** Nag if we detect system clock is too far from internet time. */
private void ntpNag() {
/**
* Nag if we detect system clock is too far from internet time.
*
* @return <tt>true</tt> if clock is accurate, <tt>false</tt> if inaccurate, <tt>null</tt> if we don't know.
*/
private Boolean ntpCheck() {
// Fetch mean offset from internet time (ms).
Long meanOffset = NTP.getOffset();
@ -459,13 +473,20 @@ public class Controller extends Thread {
}
// If offset is good and ntp is active then we're good
if (Math.abs(meanOffset) < MAX_NTP_OFFSET && isNtpActive == true)
return;
if (meanOffset != null && Math.abs(meanOffset) < MAX_NTP_OFFSET && isNtpActive == true)
return true;
// Time to nag
String caption = Translator.INSTANCE.translate("SysTray", "NTP_NAG_CAPTION");
String text = Translator.INSTANCE.translate("SysTray", isWindows ? "NTP_NAG_TEXT_WINDOWS" : "NTP_NAG_TEXT_UNIX");
SysTray.getInstance().showMessage(caption, text, MessageType.WARNING);
if (meanOffset == null)
// We don't know if we're inaccurate
return null;
// Return whether we're accurate (disregarding whether NTP service is active)
return Math.abs(meanOffset) < MAX_NTP_OFFSET;
}
public void updateSysTray() {
@ -475,7 +496,9 @@ public class Controller extends Thread {
String connectionsText = Translator.INSTANCE.translate("SysTray", numberOfPeers != 1 ? "CONNECTIONS" : "CONNECTION");
String heightText = Translator.INSTANCE.translate("SysTray", "BLOCK_HEIGHT");
String tooltip = String.format("qora-core - %d %s - %s %d", numberOfPeers, connectionsText, heightText, height);
String generatingText = Translator.INSTANCE.translate("SysTray", isGenerationAllowed ? "GENERATING_ENABLED" : "GENERATING_DISABLED");
String tooltip = String.format("%s - %d %s - %s %d", generatingText, numberOfPeers, connectionsText, heightText, height);
SysTray.getInstance().setToolTipText(tooltip);
}

View File

@ -5,12 +5,16 @@ BLOCK_HEIGHT = height
CHECK_TIME_ACCURACY = Check time accuracy
CONNECTION = block
CONNECTION = connection
CONNECTIONS = connections
EXIT = Exit
GENERATING_DISABLED = NOT minting
GENERATING_ENABLED = \u2714 Minting
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = Computer's clock is inaccurate!

View File

@ -5,10 +5,16 @@ BLOCK_HEIGHT = \u5757\u9AD8\u5EA6
CHECK_TIME_ACCURACY = \u68C0\u67E5\u65F6\u95F4\u51C6\u786E\u6027
CONNECTION = \u4E2A\u8FDE\u63A5
CONNECTIONS = \u4E2A\u8FDE\u63A5
EXIT = \u9000\u51FA\u8F6F\u4EF6
GENERATING_DISABLED = \u6CA1\u6709\u94F8\u5E01
GENERATING_ENABLED = \u2714 \u94F8\u5E01
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = \u7535\u8111\u7684\u65F6\u949F\u4E0D\u51C6\u786E\uFF01

View File

@ -19,9 +19,7 @@ public class GuiTests {
public void testSysTray() throws InterruptedException {
SysTray.getInstance();
while(true) {
Thread.sleep(2000L);
}
Thread.sleep(10_000L);
}
}