diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/DebugDataDumper.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/DebugDataDumper.java index 6790a91999f..c160c936c03 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/DebugDataDumper.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/DebugDataDumper.java @@ -18,6 +18,10 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.sql.Date; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; @@ -54,11 +58,11 @@ public DebugDataDumper(final Path directory, final boolean enabled) { } public void saveGossipMessageDecodingError( - final String topic, final String arrivalTimestamp, final Bytes originalMessage) { + final String topic, final Optional arrivalTimestamp, final Bytes originalMessage) { if (!enabled) { return; } - final String fileName = String.format("%s.ssz", arrivalTimestamp); + final String fileName = String.format("%s.ssz", formatTimestamp(arrivalTimestamp)); final Path topicPath = Path.of(GOSSIP_MESSAGES_DIR).resolve(DECODING_ERROR_SUB_DIR).resolve(topic); saveBytesToFile( @@ -66,11 +70,11 @@ public void saveGossipMessageDecodingError( } public void saveGossipRejectedMessageToFile( - final String topic, final String arrivalTimestamp, final Bytes decodedMessage) { + final String topic, final Optional arrivalTimestamp, final Bytes decodedMessage) { if (!enabled) { return; } - final String fileName = String.format("%s.ssz", arrivalTimestamp); + final String fileName = String.format("%s.ssz", formatTimestamp(arrivalTimestamp)); final Path topicPath = Path.of(GOSSIP_MESSAGES_DIR).resolve(REJECTED_SUB_DIR).resolve(topic); saveBytesToFile("rejected gossip message", topicPath.resolve(fileName), decodedMessage); } @@ -135,6 +139,17 @@ private void createDirectory(final Path path, final String directoryName, final } } + @VisibleForTesting + protected static String formatTimestamp(final Optional arrivalTimestamp) { + if (arrivalTimestamp.isEmpty()) { + return "unknown"; + } + + final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS"); + Date date = new Date(arrivalTimestamp.get().longValue()); + return df.format(date); + } + @VisibleForTesting protected boolean isEnabled() { return enabled; diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/noop/NoOpDebugDataDumper.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/noop/NoOpDebugDataDumper.java index cdd21d711d2..32c506997e4 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/noop/NoOpDebugDataDumper.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/noop/NoOpDebugDataDumper.java @@ -14,6 +14,7 @@ package tech.pegasys.teku.statetransition.util.noop; import java.nio.file.Path; +import java.util.Optional; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.infrastructure.unsigned.UInt64; @@ -27,11 +28,11 @@ public NoOpDebugDataDumper() { @Override public void saveGossipMessageDecodingError( - final String topic, final String arrivalTimestamp, final Bytes originalMessage) {} + final String topic, final Optional arrivalTimestamp, final Bytes originalMessage) {} @Override public void saveGossipRejectedMessageToFile( - final String topic, final String arrivalTimestamp, final Bytes decodedMessage) {} + final String topic, final Optional arrivalTimestamp, final Bytes decodedMessage) {} @Override public void saveInvalidBlockToFile( diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/DebugDataDumperTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/DebugDataDumperTest.java index 32001f66407..3fd3bcad651 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/DebugDataDumperTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/DebugDataDumperTest.java @@ -22,12 +22,14 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.util.Optional; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; import tech.pegasys.teku.infrastructure.time.StubTimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.util.DataStructureUtil; @@ -41,11 +43,12 @@ class DebugDataDumperTest { void saveGossipMessageDecodingError_shouldSaveToFile(@TempDir Path tempDir) { final DebugDataDumper manager = new DebugDataDumper(tempDir, true); final Bytes messageBytes = dataStructureUtil.stateBuilderPhase0().build().sszSerialize(); - final String arrivalTimestamp = timeProvider.getTimeInMillis().toString(); + final Optional arrivalTimestamp = Optional.of(timeProvider.getTimeInMillis()); final String topic = "test_topic"; manager.saveGossipMessageDecodingError("test_topic", arrivalTimestamp, messageBytes); - final String fileName = String.format("%s.ssz", arrivalTimestamp); + final String fileName = + String.format("%s.ssz", DebugDataDumper.formatTimestamp(arrivalTimestamp)); final Path expectedFile = tempDir .resolve("gossip_messages") @@ -59,11 +62,12 @@ void saveGossipMessageDecodingError_shouldSaveToFile(@TempDir Path tempDir) { void saveGossipMessageDecodingError_shouldNotSaveToFileWhenDisabled(@TempDir Path tempDir) { final DebugDataDumper manager = new DebugDataDumper(tempDir, false); final Bytes messageBytes = dataStructureUtil.stateBuilderPhase0().build().sszSerialize(); - final String arrivalTimestamp = timeProvider.getTimeInMillis().toString(); + final Optional arrivalTimestamp = Optional.of(timeProvider.getTimeInMillis()); manager.saveGossipMessageDecodingError("test_topic", arrivalTimestamp, messageBytes); assertThat(manager.isEnabled()).isFalse(); - final String fileName = String.format("%s.ssz", arrivalTimestamp); + final String fileName = + String.format("%s.ssz", DebugDataDumper.formatTimestamp(arrivalTimestamp)); final Path expectedFile = tempDir.resolve("gossip_messages").resolve("decoding_error").resolve(fileName); checkFileNotExist(expectedFile); @@ -73,11 +77,12 @@ void saveGossipMessageDecodingError_shouldNotSaveToFileWhenDisabled(@TempDir Pat void saveGossipRejectedMessageToFile_shouldSaveToFile(@TempDir Path tempDir) { final DebugDataDumper manager = new DebugDataDumper(tempDir, true); final Bytes messageBytes = dataStructureUtil.stateBuilderPhase0().build().sszSerialize(); - final String arrivalTimestamp = timeProvider.getTimeInMillis().toString(); + final Optional arrivalTimestamp = Optional.of(timeProvider.getTimeInMillis()); final String topic = "test_topic"; manager.saveGossipRejectedMessageToFile("test_topic", arrivalTimestamp, messageBytes); - final String fileName = String.format("%s.ssz", arrivalTimestamp); + final String fileName = + String.format("%s.ssz", DebugDataDumper.formatTimestamp(arrivalTimestamp)); final Path expectedFile = tempDir.resolve("gossip_messages").resolve("rejected").resolve(topic).resolve(fileName); checkBytesSavedToFile(expectedFile, messageBytes); @@ -87,7 +92,7 @@ void saveGossipRejectedMessageToFile_shouldSaveToFile(@TempDir Path tempDir) { void saveGossipRejectedMessageToFile_shouldNotSaveToFileWhenDisabled(@TempDir Path tempDir) { final DebugDataDumper manager = new DebugDataDumper(tempDir, false); final Bytes messageBytes = dataStructureUtil.stateBuilderPhase0().build().sszSerialize(); - final String arrivalTimestamp = timeProvider.getTimeInMillis().toString(); + final Optional arrivalTimestamp = Optional.of(timeProvider.getTimeInMillis()); manager.saveGossipRejectedMessageToFile("test_topic", arrivalTimestamp, messageBytes); assertThat(manager.isEnabled()).isFalse(); @@ -150,6 +155,19 @@ void constructionOfDirectories_shouldDisableWhenFailedToCreate(@TempDir Path tem assertThat(manager.isEnabled()).isFalse(); } + @Test + void formatTimestamp_shouldFormatDate() { + final String formattedTimestamp = + DebugDataDumper.formatTimestamp(Optional.of(dataStructureUtil.randomUInt64())); + assertThat(formattedTimestamp).isEqualTo("147882977-02-28T19:22:42.956"); + } + + @Test + void formatTimestamp_shouldReturnConsistentUnknown() { + final String formattedTimestamp = DebugDataDumper.formatTimestamp(Optional.empty()); + assertThat(formattedTimestamp).isEqualTo("unknown"); + } + private void checkBytesSavedToFile(final Path path, final Bytes expectedBytes) { try { final Bytes bytes = Bytes.wrap(Files.readAllBytes(path)); diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/topichandlers/Eth2TopicHandler.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/topichandlers/Eth2TopicHandler.java index d6d7c0aab10..3e57420f403 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/topichandlers/Eth2TopicHandler.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/topichandlers/Eth2TopicHandler.java @@ -137,7 +137,7 @@ private void processMessage( case REJECT: debugDataDumper.saveGossipRejectedMessageToFile( getTopic(), - message.getArrivalTimestamp().map(UInt64::toString).orElse("unknown"), + message.getArrivalTimestamp(), message.getDecodedMessage().getDecodedMessage().orElse(Bytes.EMPTY)); P2P_LOG.onGossipRejected( getTopic(), @@ -171,9 +171,7 @@ protected ValidationResult handleMessageProcessingError( final ValidationResult response; if (ExceptionUtil.hasCause(err, DecodingException.class)) { debugDataDumper.saveGossipMessageDecodingError( - getTopic(), - message.getArrivalTimestamp().map(UInt64::toString).orElse("unknown"), - message.getOriginalMessage()); + getTopic(), message.getArrivalTimestamp(), message.getOriginalMessage()); P2P_LOG.onGossipMessageDecodingError(getTopic(), message.getOriginalMessage(), err); response = ValidationResult.Invalid; } else if (ExceptionUtil.hasCause(err, RejectedExecutionException.class)) {