From abc9cb39589988b17cbfb3ea324da049b60ade5b Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 5 Apr 2022 08:07:19 +0100 Subject: [PATCH] Compute the nonce for the 'next' timestamp before the 'current' timestamp. Nodes use each 30 minute period to compute the nonce for the next 30 minute period, so this should be prioritized. Once calculated, the 'current' timestamp is attempted if there is enough time. Doing it in this order avoids falling behind and then struggling to catch up. We will need to think about how to handle node restarts, since otherwise an auto update could cause a gap in online accounts due to all nodes computing the 'next' timestamp before the 'current' one. --- .../controller/OnlineAccountsManager.java | 83 ++++++++++++------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/qortal/controller/OnlineAccountsManager.java b/src/main/java/org/qortal/controller/OnlineAccountsManager.java index f6e87aff..a2a359fa 100644 --- a/src/main/java/org/qortal/controller/OnlineAccountsManager.java +++ b/src/main/java/org/qortal/controller/OnlineAccountsManager.java @@ -351,13 +351,17 @@ public class OnlineAccountsManager extends Thread { return; } - // 'current' timestamp + // 'next' timestamp (prioritize this as it's the most important) + final long nextOnlineAccountsTimestamp = toOnlineAccountTimestamp(now) + getOnlineTimestampModulus(); + boolean success = computeOurAccountsForTimestamp(mintingAccounts, nextOnlineAccountsTimestamp); + if (!success) { + // We didn't compute the required nonce value(s), and so can't proceed until they have been retried + return; + } + + // 'current' timestamp (if there's enough time after successfully computing the 'next' timestamps) final long onlineAccountsTimestamp = toOnlineAccountTimestamp(now); computeOurAccountsForTimestamp(mintingAccounts, onlineAccountsTimestamp); - - // 'next' timestamp // TODO -// final long nextOnlineAccountsTimestamp = toOnlineAccountTimestamp(now) + getOnlineTimestampModulus(); -// computeOurAccountsForTimestamp(mintingAccounts, nextOnlineAccountsTimestamp); } /** @@ -365,7 +369,7 @@ public class OnlineAccountsManager extends Thread { * @param mintingAccounts - the online accounts * @param onlineAccountsTimestamp - the online accounts timestamp */ - private void computeOurAccountsForTimestamp(List mintingAccounts, long onlineAccountsTimestamp) { + private boolean computeOurAccountsForTimestamp(List mintingAccounts, long onlineAccountsTimestamp) { try (final Repository repository = RepositoryManager.getRepository()) { boolean hasInfoChanged = false; @@ -409,7 +413,7 @@ public class OnlineAccountsManager extends Thread { BlockData recentBlockData = repository.getBlockRepository().fromHeight(referenceHeight); if (recentBlockData == null || recentBlockData.getSignature() == null) { LOGGER.info("Unable to compute online accounts without having a recent block"); - return; + return false; } byte[] reducedRecentBlockSignature = Arrays.copyOfRange(recentBlockData.getSignature(), 0, 8); @@ -422,8 +426,20 @@ public class OnlineAccountsManager extends Thread { continue MINTING_ACCOUNTS; } - Integer nonce = this.computeMemoryPoW(mempowBytes, publicKey); - if (nonce == null) { + Integer nonce; + if (isMemoryPoWActive()) { + try { + nonce = this.computeMemoryPoW(mempowBytes, publicKey, onlineAccountsTimestamp); + if (nonce == null) { + // A nonce is required + return false; + } + } catch (TimeoutException e) { + LOGGER.info(String.format("Timed out computing nonce for account %.8s", Base58.encode(publicKey))); + return false; + } + } + else { // Send zero if we haven't computed a nonce due to feature trigger timestamp nonce = 0; } @@ -432,15 +448,20 @@ public class OnlineAccountsManager extends Thread { OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey, Arrays.asList(nonce), reducedRecentBlockSignature); - this.onlineAccounts.add(ourOnlineAccountData); + // Make sure to verify before adding + if (verifyMemoryPoW(ourOnlineAccountData)) { + this.onlineAccounts.add(ourOnlineAccountData); - LOGGER.trace(() -> String.format("Added our online account %s with timestamp %d", mintingAccount.getAddress(), onlineAccountsTimestamp)); - ourOnlineAccounts.add(ourOnlineAccountData); - hasInfoChanged = true; + LOGGER.trace(() -> String.format("Added our online account %s with timestamp %d", mintingAccount.getAddress(), onlineAccountsTimestamp)); + ourOnlineAccounts.add(ourOnlineAccountData); + hasInfoChanged = true; + } } - if (!hasInfoChanged) - return; + if (!hasInfoChanged) { + // Nothing to do + return true; + } Message messageV2 = new OnlineAccountsV2Message(ourOnlineAccounts); Message messageV3 = new OnlineAccountsV3Message(ourOnlineAccounts); @@ -450,9 +471,11 @@ public class OnlineAccountsManager extends Thread { ); LOGGER.trace(() -> String.format("Broadcasted %d online account%s with timestamp %d", ourOnlineAccounts.size(), (ourOnlineAccounts.size() != 1 ? "s" : ""), onlineAccountsTimestamp)); + return true; } catch (DataException e) { LOGGER.error(String.format("Repository issue while computing online accounts"), e); + return false; } } @@ -467,35 +490,29 @@ public class OnlineAccountsManager extends Thread { return outputStream.toByteArray(); } - private Integer computeMemoryPoW(byte[] bytes, byte[] publicKey) { - Long startTime = NTP.getTime(); - if (startTime < BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp() || Settings.getInstance().isOnlineAccountsMemPoWEnabled()) { + private Integer computeMemoryPoW(byte[] bytes, byte[] publicKey, long onlineAccountsTimestamp) throws TimeoutException { + if (!isMemoryPoWActive()) { LOGGER.info("Mempow start timestamp not yet reached, and onlineAccountsMemPoWEnabled not enabled in settings"); return null; } - LOGGER.info(String.format("Computing nonce for account %.8s...", Base58.encode(publicKey))); + LOGGER.info(String.format("Computing nonce for account %.8s and timestamp %d...", Base58.encode(publicKey), onlineAccountsTimestamp)); // Calculate the time until the next online timestamp and use it as a timeout when computing the nonce + Long startTime = NTP.getTime(); final long nextOnlineAccountsTimestamp = toOnlineAccountTimestamp(startTime) + getOnlineTimestampModulus(); long timeUntilNextTimestamp = nextOnlineAccountsTimestamp - startTime; - Integer nonce; - try { - nonce = MemoryPoW.compute2(bytes, POW_BUFFER_SIZE, POW_DIFFICULTY, timeUntilNextTimestamp); - } catch (TimeoutException e) { - LOGGER.info(String.format("Timed out computing nonce for account %.8s", Base58.encode(publicKey))); - return null; - } + Integer nonce = MemoryPoW.compute2(bytes, POW_BUFFER_SIZE, POW_DIFFICULTY, timeUntilNextTimestamp); double totalSeconds = (NTP.getTime() - startTime) / 1000.0f; int minutes = (int) ((totalSeconds % 3600) / 60); int seconds = (int) (totalSeconds % 60); double hashRate = nonce / totalSeconds; - LOGGER.info(String.format("Computed nonce for account %.8s: %d. Buffer size: %d. Difficulty: %d. " + - "Time taken: %02d:%02d. Hashrate: %f", Base58.encode(publicKey), nonce, - POW_BUFFER_SIZE, POW_DIFFICULTY, minutes, seconds, hashRate)); + LOGGER.info(String.format("Computed nonce for timestamp %d and account %.8s: %d. Buffer size: %d. Difficulty: %d. " + + "Time taken: %02d:%02d. Hashrate: %f", onlineAccountsTimestamp, Base58.encode(publicKey), + nonce, POW_BUFFER_SIZE, POW_DIFFICULTY, minutes, seconds, hashRate)); return nonce; } @@ -572,6 +589,14 @@ public class OnlineAccountsManager extends Thread { } } + private boolean isMemoryPoWActive() { + Long now = NTP.getTime(); + if (now < BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp() || Settings.getInstance().isOnlineAccountsMemPoWEnabled()) { + return false; + } + return true; + } + // Network handlers