Skip to content
Permalink
Browse files

Version 556. Improved client initialization.

  • Loading branch information
n-y-z-o committed Dec 2, 2019
1 parent 2b4602a commit 5b8eec59c27d71717c9c489d552fb52aa805f369
@@ -380,8 +380,9 @@ public static Message response(Message message) {
// Many actions are taken inside this block as a result of messages. Therefore, we only want to continue if
// the message is valid. The timestamp check protects against various replay attacks.
if (message != null && message.isValid() &&
message.getTimestamp() >= System.currentTimeMillis() - Message.replayProtectionInterval &&
message.getTimestamp() <= System.currentTimeMillis() + Message.replayProtectionInterval) {
((message.getTimestamp() >= System.currentTimeMillis() - Message.replayProtectionInterval &&
message.getTimestamp() <= System.currentTimeMillis() + Message.replayProtectionInterval) ||
message.getType() == MessageType.TimestampRequest27)) {

Verifier.registerMessage();

@@ -224,8 +224,10 @@ public void run() {

if (messageCallback != null) {
if (response != null && response.isValid() &&
response.getTimestamp() >= System.currentTimeMillis() - replayProtectionInterval &&
response.getTimestamp() <= System.currentTimeMillis() + replayProtectionInterval) {
((response.getTimestamp() >= System.currentTimeMillis() - replayProtectionInterval &&
response.getTimestamp() <= System.currentTimeMillis() +
replayProtectionInterval) ||
response.getType() == MessageType.TimestampResponse28)) {
MessageQueue.add(messageCallback, response);
} else {
MessageQueue.add(messageCallback, null);
@@ -2,7 +2,7 @@

public class Version {

private static final int version = 554;
private static final int version = 556;

public static int getVersion() {

@@ -26,14 +26,20 @@ public static void main(String[] args) {
Version.getVersion())));

// Start the data manager. This collects the data necessary for the client to run properly.
ClientDataManager.start();
boolean startedDataManager = ClientDataManager.start();

// If the preference is set, start the web listener.
if (PreferencesUtil.getBoolean(WebListener.startWebListenerKey, false)) {
WebListener.start();
}
// If the data manager started properly, continue. Otherwise, display an error message.
if (startedDataManager) {
// If the preference is set, start the web listener.
if (PreferencesUtil.getBoolean(WebListener.startWebListenerKey, false)) {
WebListener.start();
}

runCommandLoop();
runCommandLoop();
} else {
System.out.println(ConsoleColor.Red + "data manager did not start; exiting" + ConsoleColor.reset);
UpdateUtil.terminate();
}
}

private static void runCommandLoop() {
@@ -7,9 +7,9 @@
import co.nyzo.verifier.util.UpdateUtil;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

public class ClientDataManager {
@@ -19,7 +19,7 @@
private static final long minimumReinitializationInterval = 1000L * 60L * 10L; // 10 minutes
private static final long reinitializationThreshold = 1000L * 60L * 10L; // 10 minutes; about 86 blocks

public static void start() {
public static boolean start() {

System.out.println("starting client data manager");

@@ -34,58 +34,129 @@ public static void start() {
Message.fetchTcp(entryPoint.getHost(), entryPoint.getPort(), new Message(MessageType.Ping200, null), null);
}

// Load the mesh from trusted entry points first, as this is needed for fetching other data.
loadMesh(trustedEntryPoints);
// Check this system's clock against the trusted entry points.
long timeOffset = medianTimestampOffset(trustedEntryPoints);
boolean started;
if (Math.abs(timeOffset) > Message.replayProtectionInterval) {
started = false;
System.out.println(String.format("%scalculated time offset is %.1f; not starting data manager%s",
ConsoleColor.Red.background(), timeOffset / 1000.0, ConsoleColor.reset));
System.out.println(ConsoleColor.Red.background() + "please check your system's clock" + ConsoleColor.reset);
} else {
started = true;

if (Math.abs(timeOffset) > Message.replayProtectionInterval * 3 / 5) {
System.out.println(String.format("%scalculated time offset is %.1f; messaging problems may occur%s",
ConsoleColor.Yellow.background(), timeOffset / 1000.0, ConsoleColor.reset));
}

// Load the Genesis block. This will be a democratic process among the trusted entry points to fetch, then it
// will be stored for future runs of the client.
loadGenesisBlock(trustedEntryPoints);
// Load the mesh from trusted entry points first, as this is needed for fetching other data.
loadMesh(trustedEntryPoints);

// Load the consensus frozen edge. This is the same process used by the verifier.
ChainInitializationManager.initializeFrozenEdge(trustedEntryPoints);
// Load the Genesis block. This will be a democratic process among the trusted entry points to fetch, then it
// will be stored for future runs of the client.
loadGenesisBlock(trustedEntryPoints);

new Thread(new Runnable() {
@Override
public void run() {
// Load the consensus frozen edge. This is the same process used by the verifier.
ChainInitializationManager.initializeFrozenEdge(trustedEntryPoints);

long lastMeshUpdateTimestamp = 0L;
long lastBlockUpdateTimestamp = 0L;
long lastReinitializationTimestamp = System.currentTimeMillis();
new Thread(new Runnable() {
@Override
public void run() {

while (!UpdateUtil.shouldTerminate()) {
long lastMeshUpdateTimestamp = 0L;
long lastBlockUpdateTimestamp = 0L;
long lastReinitializationTimestamp = System.currentTimeMillis();

// Update the mesh.
if (lastMeshUpdateTimestamp < System.currentTimeMillis() - meshUpdateInterval) {
lastMeshUpdateTimestamp = System.currentTimeMillis();
updateMesh();
}
while (!UpdateUtil.shouldTerminate()) {

// Try to fetch a new block.
if (lastBlockUpdateTimestamp < System.currentTimeMillis() - blockUpdateInterval) {
lastBlockUpdateTimestamp = System.currentTimeMillis();
requestBlockWithVotes();
}
// Update the mesh.
if (lastMeshUpdateTimestamp < System.currentTimeMillis() - meshUpdateInterval) {
lastMeshUpdateTimestamp = System.currentTimeMillis();
updateMesh();
}

// Reinitialize the frozen edge, if necessary. Both checks must be performed to avoid continuously
// reinitializing if the blockchain is stalled.
long timeSinceVerification = System.currentTimeMillis() -
BlockManager.getFrozenEdge().getVerificationTimestamp();
long timeSinceReinitialization = System.currentTimeMillis() - lastReinitializationTimestamp;
if (timeSinceVerification > reinitializationThreshold &&
timeSinceReinitialization > minimumReinitializationInterval) {
System.out.println(ConsoleColor.Yellow.background() + "reinitializing frozen edge, height=" +
BlockManager.getFrozenEdgeHeight() + ConsoleColor.reset);
ChainInitializationManager.initializeFrozenEdge(trustedEntryPoints);
System.out.println(ConsoleColor.Yellow.background() + "reinitialized frozen edge, height=" +
BlockManager.getFrozenEdgeHeight() + ConsoleColor.reset);
lastReinitializationTimestamp = System.currentTimeMillis();
}
// Try to fetch a new block.
if (lastBlockUpdateTimestamp < System.currentTimeMillis() - blockUpdateInterval) {
lastBlockUpdateTimestamp = System.currentTimeMillis();
requestBlockWithVotes();
}

// Sleep for a short time to keep the loop from running too tightly.
ThreadUtil.sleep(300L);
// Reinitialize the frozen edge, if necessary. Both checks must be performed to avoid continuously
// reinitializing if the blockchain is stalled.
long timeSinceVerification = System.currentTimeMillis() -
BlockManager.getFrozenEdge().getVerificationTimestamp();
long timeSinceReinitialization = System.currentTimeMillis() - lastReinitializationTimestamp;
if (timeSinceVerification > reinitializationThreshold &&
timeSinceReinitialization > minimumReinitializationInterval) {
System.out.println(ConsoleColor.Yellow.background() + "reinitializing frozen edge, height=" +
BlockManager.getFrozenEdgeHeight() + ConsoleColor.reset);
ChainInitializationManager.initializeFrozenEdge(trustedEntryPoints);
System.out.println(ConsoleColor.Yellow.background() + "reinitialized frozen edge, height=" +
BlockManager.getFrozenEdgeHeight() + ConsoleColor.reset);
lastReinitializationTimestamp = System.currentTimeMillis();
}

// Sleep for a short time to keep the loop from running too tightly.
ThreadUtil.sleep(300L);
}
}
}).start();
}

return started;
}

private static long medianTimestampOffset(List<TrustedEntryPoint> trustedEntryPoints) {

// Send timestamp requests to each trusted entry point.
List<Long> offsets = new CopyOnWriteArrayList<>();
AtomicInteger numberOfResponsesReceived = new AtomicInteger(0);
for (TrustedEntryPoint trustedEntryPoint : trustedEntryPoints) {
Message requestMessage = new Message(MessageType.TimestampRequest27, null);
Message.fetchTcp(trustedEntryPoint.getHost(), trustedEntryPoint.getPort(), requestMessage,
new MessageCallback() {
@Override
public void responseReceived(Message responseMessage) {
if (responseMessage.getContent() instanceof TimestampResponse) {
// A response timestamp before the outgoing timestamp is a negative offset, a response
// timestamp after the current timestamp is a positive offset, and a response timestamp
// between the outgoing and current timestamp is an offset of 0.
TimestampResponse response = (TimestampResponse) responseMessage.getContent();
long currentTimestamp = System.currentTimeMillis();
if (response.getTimestamp() < requestMessage.getTimestamp()) {
offsets.add(response.getTimestamp() - requestMessage.getTimestamp());
} else if (response.getTimestamp() > currentTimestamp) {
offsets.add(response.getTimestamp() - currentTimestamp);
} else {
offsets.add(0L);
}
}
numberOfResponsesReceived.incrementAndGet();
}
});
}

// Wait up to 2 seconds, or until all responses have been received.
for (int i = 0; i < 10 && numberOfResponsesReceived.get() < trustedEntryPoints.size(); i++) {
ThreadUtil.sleep(200L);
}

// Return the median offset.
long offset;
if (offsets.isEmpty()) {
offset = 0;
} else {
List<Long> offsetsCopy = new ArrayList<>(offsets);
Collections.sort(offsetsCopy);
if (offsetsCopy.size() % 2 == 0) {
offset = (offsetsCopy.get(offsetsCopy.size() / 2 - 1) + offsetsCopy.get(offsetsCopy.size() / 2)) / 2;
} else {
offset = offsetsCopy.get(offsetsCopy.size() / 2);
}
}).start();
}

return offset;
}

private static void loadMesh(List<TrustedEntryPoint> trustedEntryPoints) {
@@ -126,7 +197,7 @@ private static void loadGenesisBlock(List<TrustedEntryPoint> trustedEntryPoints)
// the Genesis block. This will also allow the client to work well with other Nyzo-based blockchains simply
// by changing the trusted entry points.
Block genesisBlock = BlockManager.frozenBlockForHeight(0L);
if (genesisBlock == null) {
while (genesisBlock == null) {

AtomicInteger numberOfResponsesPending = new AtomicInteger(trustedEntryPoints.size());

@@ -180,12 +251,17 @@ public void responseReceived(Message message) {
}
}

// If a hash was found, save the Genesis block.
// If a hash was found, save the Genesis block. Otherwise, sleep 5 seconds before the next iteration.
if (winningHash != null) {
genesisBlock = blocks.get(winningHash);

BalanceList balanceList = BalanceListManager.balanceListForBlock(genesisBlock);
BlockManager.freezeBlock(genesisBlock, genesisBlock.getPreviousBlockHash(), balanceList, null);
} else {
// This sleep is not strictly necessary, but adding a small delay to problematic, unexpected situations
// often allows the situation to resolve itself more easily. Without this sleep, this would still not be
// a tight loop, due to the 3-second wait above.
ThreadUtil.sleep(5000L);
}
}
}
@@ -23,7 +23,7 @@ public static void initializeFrozenEdge(List<ManagedVerifier> verifiers) {
boolean completedInitialization = false;
while (!completedInitialization) {
Set<BootstrapResponseV2> bootstrapResponses = fetchBootstrapResponses(verifiers);
LogUtil.println("forzen edge initialization: got " + bootstrapResponses.size() + " bootstrap responses");
LogUtil.println("frozen edge initialization: got " + bootstrapResponses.size() + " bootstrap responses");
completedInitialization = processBootstrapResponses(bootstrapResponses, verifiers);

// If initialization was not completed, sleep for 5 seconds to prevent looping too tightly and hammering

0 comments on commit 5b8eec5

Please sign in to comment.
You can’t perform that action at this time.