From b14894b8e112e43e695301c8b38b9dbd05d0d272 Mon Sep 17 00:00:00 2001 From: ljianghedera <55505519+ljianghedera@users.noreply.github.com> Date: Wed, 26 May 2021 00:41:09 -0500 Subject: [PATCH] Use Instant to replace RichInstant usage whenever possible (#1462) * Replace RichInstant with Instant in MerkleNetworkContext, hence reduce the conversion between these two types. * Add MerkleNetworkContext.deserialize() null-safety and avoid transient ISS (#1464) * Add null-safety and avoid transient ISS from MerkleNetworkContext fast-copy sharing FeeMultiplierSource congestionLevelStarts Signed-off-by: tinker-michaelj * Fix unit test Signed-off-by: tinker-michaelj * 01399 m yacli valide ip and port address book update (#1454) * add checks to validate port number in service endpoints when uploading an addressBook using yahcli Signed-off-by: anighanta * add test addressbook for bad port in endpoint Signed-off-by: anighanta * updated gitignore to not track script files from yahcli/assests Signed-off-by: anighanta * screened launch script Signed-off-by: anighanta * Resolve SonarCloud Minor Bugs (#1449) * fix few minor sonarcloud bugs Signed-off-by: Neeharika-Sompalli * Additional fixes Signed-off-by: Neeharika-Sompalli * fix code smell Signed-off-by: Neeharika-Sompalli * move duplicated code to a method Signed-off-by: Neeharika-Sompalli * fix other minor bugs Signed-off-by: Neeharika-Sompalli * fix other issues Signed-off-by: Neeharika-Sompalli * remove unnecessary toString() calls Signed-off-by: Neeharika-Sompalli * add braces Signed-off-by: Neeharika-Sompalli * remove a few casts Signed-off-by: Neeharika-Sompalli * convert getCommonTransactionBodyBytes and legacyReceiptStorageSecs to return long and remove casts Signed-off-by: Neeharika-Sompalli * fix other bugs created Signed-off-by: Neeharika-Sompalli * Address review comments and fix alignment in SmartContractFeeBuilder Signed-off-by: Neeharika-Sompalli * address few review comments Signed-off-by: Neeharika-Sompalli * fix few other review points Signed-off-by: Neeharika-Sompalli * fix typo Signed-off-by: Neeharika-Sompalli * fix review comments, add UnitTest for MerkleDiskFs Signed-off-by: Neeharika-Sompalli * Add unit tests for FreezeHandler Signed-off-by: Neeharika-Sompalli * fix random failing tests because of MockedStatic Signed-off-by: Neeharika-Sompalli * add more tests for codecov Signed-off-by: Neeharika-Sompalli * fix all failing tests, yet to add more tests for code coverage Signed-off-by: Neeharika-Sompalli * remove main method in SignatureVerifier and unused methods as they are not used anywhere Signed-off-by: Neeharika-Sompalli * Address other review comments Signed-off-by: Neeharika-Sompalli * Fix blocker and critical sonar bugs. (#1446) Signed-off-by: ljianghedera * Fix the json map issue. Signed-off-by: ljianghedera * Rename and changes per review comments. Signed-off-by: ljianghedera * Cleanup and more coverage. Signed-off-by: ljianghedera Co-authored-by: Michael Tinker Co-authored-by: Anirudh Ghanta <53790698+anighanta@users.noreply.github.com> Co-authored-by: Neeha <52669918+Neeharika-Sompalli@users.noreply.github.com> --- .../fees/TxnRateFeeMultiplierSource.java | 8 +- .../state/merkle/MerkleNetworkContext.java | 68 ++++---- .../services/context/ServicesContextTest.java | 4 +- .../fees/TxnRateFeeMultiplierSourceTest.java | 22 ++- .../merkle/MerkleNetworkContextTest.java | 157 ++++++++++++++---- 5 files changed, 181 insertions(+), 78 deletions(-) diff --git a/hedera-node/src/main/java/com/hedera/services/fees/TxnRateFeeMultiplierSource.java b/hedera-node/src/main/java/com/hedera/services/fees/TxnRateFeeMultiplierSource.java index 72aa3f3bf311..5a73c564a8b7 100644 --- a/hedera-node/src/main/java/com/hedera/services/fees/TxnRateFeeMultiplierSource.java +++ b/hedera-node/src/main/java/com/hedera/services/fees/TxnRateFeeMultiplierSource.java @@ -155,12 +155,16 @@ public void updateMultiplier(Instant consensusNow) { @Override public void resetCongestionLevelStarts(Instant[] savedStartTimes) { - congestionLevelStarts = savedStartTimes; + congestionLevelStarts = savedStartTimes.clone(); } @Override public Instant[] congestionLevelStarts() { - return congestionLevelStarts; + /* If the Platform is serializing a fast-copy of the MerkleNetworkContext, + and that copy references this object's congestionLevelStarts, we will get + a (transient) ISS if the congestion level changes mid-serialization on one + node but not others. */ + return congestionLevelStarts.clone(); } private boolean ensureConfigUpToDate() { diff --git a/hedera-node/src/main/java/com/hedera/services/state/merkle/MerkleNetworkContext.java b/hedera-node/src/main/java/com/hedera/services/state/merkle/MerkleNetworkContext.java index 22e625823dcc..dc1486720226 100644 --- a/hedera-node/src/main/java/com/hedera/services/state/merkle/MerkleNetworkContext.java +++ b/hedera-node/src/main/java/com/hedera/services/state/merkle/MerkleNetworkContext.java @@ -23,7 +23,6 @@ import com.hedera.services.fees.FeeMultiplierSource; import com.hedera.services.state.serdes.DomainSerdes; import com.hedera.services.state.submerkle.ExchangeRates; -import com.hedera.services.state.submerkle.RichInstant; import com.hedera.services.state.submerkle.SequenceNumber; import com.hedera.services.throttles.DeterministicThrottle; import com.hedera.services.throttling.FunctionalityThrottling; @@ -36,7 +35,6 @@ import java.io.IOException; import java.time.Instant; import java.util.List; -import java.util.Optional; import java.util.function.Supplier; import static com.hedera.services.state.submerkle.RichInstant.fromJava; @@ -52,19 +50,19 @@ public class MerkleNetworkContext extends AbstractMerkleLeaf { static final int RELEASE_0140_VERSION = 3; static final int MERKLE_VERSION = RELEASE_0140_VERSION; static final long RUNTIME_CONSTRUCTABLE_ID = 0x8d4aa0f0a968a9f3L; - static final RichInstant[] NO_CONGESTION_STARTS = new RichInstant[0]; + static final Instant[] NO_CONGESTION_STARTS = new Instant[0]; static final DeterministicThrottle.UsageSnapshot[] NO_SNAPSHOTS = new DeterministicThrottle.UsageSnapshot[0]; - public static final RichInstant UNKNOWN_CONSENSUS_TIME = null; + public static final Instant UNKNOWN_CONSENSUS_TIME = null; static DomainSerdes serdes = new DomainSerdes(); static Supplier ratesSupplier = ExchangeRates::new; static Supplier seqNoSupplier = SequenceNumber::new; private int stateVersion = UNRECORDED_STATE_VERSION; - private RichInstant[] congestionLevelStarts = NO_CONGESTION_STARTS; + private Instant[] congestionLevelStarts = NO_CONGESTION_STARTS; private ExchangeRates midnightRates; - private RichInstant consensusTimeOfLastHandledTxn; + private Instant consensusTimeOfLastHandledTxn = UNKNOWN_CONSENSUS_TIME; private SequenceNumber seqNo; private long lastScannedEntity; private long entitiesScannedThisSecond = 0L; @@ -76,7 +74,7 @@ public MerkleNetworkContext() { } public MerkleNetworkContext( - RichInstant consensusTimeOfLastHandledTxn, + Instant consensusTimeOfLastHandledTxn, SequenceNumber seqNo, long lastScannedEntity, ExchangeRates midnightRates @@ -88,12 +86,12 @@ public MerkleNetworkContext( } public MerkleNetworkContext( - RichInstant consensusTimeOfLastHandledTxn, + Instant consensusTimeOfLastHandledTxn, SequenceNumber seqNo, long lastScannedEntity, ExchangeRates midnightRates, DeterministicThrottle.UsageSnapshot[] usageSnapshots, - RichInstant[] congestionStartPeriods, + Instant[] congestionStartPeriods, int stateVersion, long entitiesScannedThisSecond, long entitiesTouchedThisSecond @@ -137,31 +135,22 @@ public void resetWithSavedSnapshots(FunctionalityThrottling throttling) { } public void updateCongestionStartsFrom(FeeMultiplierSource feeMultiplierSource) { - var congestionStarts = feeMultiplierSource.congestionLevelStarts(); - if (congestionStarts.length == 0) { + final var congestionStarts = feeMultiplierSource.congestionLevelStarts(); + if(null == congestionStarts ) { congestionLevelStarts = NO_CONGESTION_STARTS; } else { - congestionLevelStarts = new RichInstant[congestionStarts.length]; - for (int i = 0; i < congestionStarts.length; i++) { - congestionLevelStarts[i] = RichInstant.fromJava(congestionStarts[i]); - } + congestionLevelStarts = congestionStarts; } } public void updateWithSavedCongestionStarts(FeeMultiplierSource feeMultiplierSource) { if (congestionLevelStarts.length > 0) { - Instant[] congestionStarts = new Instant[congestionLevelStarts.length]; - for (int i = 0; i < congestionLevelStarts.length; i++) { - if (congestionLevelStarts[i] != null) { - congestionStarts[i] = congestionLevelStarts[i].toJava(); - } - } - feeMultiplierSource.resetCongestionLevelStarts(congestionStarts); + feeMultiplierSource.resetCongestionLevelStarts(congestionLevelStarts); } } public void setConsensusTimeOfLastHandledTxn(Instant consensusTimeOfLastHandledTxn) { - this.consensusTimeOfLastHandledTxn = fromJava(consensusTimeOfLastHandledTxn); + this.consensusTimeOfLastHandledTxn = consensusTimeOfLastHandledTxn; } public MerkleNetworkContext copy() { @@ -180,7 +169,9 @@ public MerkleNetworkContext copy() { @Override public void deserialize(SerializableDataInputStream in, int version) throws IOException { - consensusTimeOfLastHandledTxn = serdes.readNullableInstant(in); + final var lastHandleTime = serdes.readNullableInstant(in); + consensusTimeOfLastHandledTxn = (lastHandleTime == null) ? null : lastHandleTime.toJava(); + seqNo = seqNoSupplier.get(); seqNo.deserialize(in); midnightRates = in.readSerializable(true, ratesSupplier); @@ -193,16 +184,16 @@ public void deserialize(SerializableDataInputStream in, int version) throws IOEx var used = in.readLong(); var lastUsed = serdes.readNullableInstant(in); usageSnapshots[i] = new DeterministicThrottle.UsageSnapshot( - used, - Optional.ofNullable(lastUsed).map(RichInstant::toJava).orElse(null)); + used, (lastUsed == null) ? null : lastUsed.toJava()); } } int numCongestionStarts = in.readInt(); if (numCongestionStarts > 0) { - congestionLevelStarts = new RichInstant[numCongestionStarts]; + congestionLevelStarts = new Instant[numCongestionStarts]; for (int i = 0; i < numCongestionStarts; i++) { - congestionLevelStarts[i] = serdes.readNullableInstant(in); + final var levelStart = serdes.readNullableInstant(in); + congestionLevelStarts[i] = (levelStart == null) ? null : levelStart.toJava(); } } } @@ -216,7 +207,7 @@ public void deserialize(SerializableDataInputStream in, int version) throws IOEx @Override public void serialize(SerializableDataOutputStream out) throws IOException { - serdes.writeNullableInstant(consensusTimeOfLastHandledTxn, out); + serdes.writeNullableInstant(fromJava(consensusTimeOfLastHandledTxn), out); seqNo.serialize(out); out.writeSerializable(midnightRates, true); int n = usageSnapshots.length; @@ -228,7 +219,7 @@ public void serialize(SerializableDataOutputStream out) throws IOException { n = congestionLevelStarts.length; out.writeInt(n); for (var congestionStart : congestionLevelStarts) { - serdes.writeNullableInstant(congestionStart, out); + serdes.writeNullableInstant(fromJava(congestionStart), out); } out.writeLong(lastScannedEntity); out.writeLong(entitiesScannedThisSecond); @@ -237,7 +228,7 @@ public void serialize(SerializableDataOutputStream out) throws IOException { } public Instant consensusTimeOfLastHandledTxn() { - return Optional.ofNullable(consensusTimeOfLastHandledTxn).map(RichInstant::toJava).orElse(null); + return consensusTimeOfLastHandledTxn; } public SequenceNumber seqNo() { @@ -279,7 +270,7 @@ public String toString() { for (var snapshot : usageSnapshots) { sb.append("\n ").append(snapshot.used()) .append(" used (last decision time ") - .append(reprOf(fromJava(snapshot.lastDecisionTime()))).append(")"); + .append(reprOf(snapshot.lastDecisionTime())).append(")"); } sb.append("\n Congestion level start times are ::"); for (var start : congestionLevelStarts) { @@ -325,22 +316,19 @@ private void resetUnconditionally( } } - private String reprOf(RichInstant consensusTime) { - return Optional.ofNullable(consensusTime) - .map(RichInstant::toJava) - .map(Object::toString) - .orElse(""); + private String reprOf(Instant consensusTime) { + return consensusTime == null ? "" : consensusTime.toString(); } - void setCongestionLevelStarts(RichInstant[] congestionLevelStarts) { + void setCongestionLevelStarts(Instant[] congestionLevelStarts) { this.congestionLevelStarts = congestionLevelStarts; } - RichInstant[] getCongestionLevelStarts() { + Instant[] getCongestionLevelStarts() { return congestionLevelStarts; } - RichInstant getConsensusTimeOfLastHandledTxn() { + Instant getConsensusTimeOfLastHandledTxn() { return consensusTimeOfLastHandledTxn; } diff --git a/hedera-node/src/test/java/com/hedera/services/context/ServicesContextTest.java b/hedera-node/src/test/java/com/hedera/services/context/ServicesContextTest.java index 1bb91ccce13c..ec75511a19a0 100644 --- a/hedera-node/src/test/java/com/hedera/services/context/ServicesContextTest.java +++ b/hedera-node/src/test/java/com/hedera/services/context/ServicesContextTest.java @@ -179,7 +179,7 @@ public class ServicesContextTest { private final NodeId nodeId = new NodeId(false, id); private static final String recordStreamDir = "somePath/recordStream"; - RichInstant consensusTimeOfLastHandledTxn = RichInstant.fromJava(Instant.now()); + Instant consensusTimeOfLastHandledTxn = Instant.now(); Platform platform; SequenceNumber seqNo; ExchangeRates midnightRates; @@ -290,7 +290,7 @@ void delegatesPrimitivesToState() { inOrder.verify(state).addressBook(); assertEquals(seqNo, actualSeqNo); assertEquals(midnightRates, actualMidnightRates); - assertEquals(consensusTimeOfLastHandledTxn.toJava(), actualLastHandleTime); + assertEquals(consensusTimeOfLastHandledTxn, actualLastHandleTime); inOrder.verify(state).topics(); inOrder.verify(state).storage(); inOrder.verify(state).accounts(); diff --git a/hedera-node/src/test/java/com/hedera/services/fees/TxnRateFeeMultiplierSourceTest.java b/hedera-node/src/test/java/com/hedera/services/fees/TxnRateFeeMultiplierSourceTest.java index 897dbdf36056..e0ea44cd5e73 100644 --- a/hedera-node/src/test/java/com/hedera/services/fees/TxnRateFeeMultiplierSourceTest.java +++ b/hedera-node/src/test/java/com/hedera/services/fees/TxnRateFeeMultiplierSourceTest.java @@ -45,6 +45,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.BDDMockito.given; @@ -69,13 +70,32 @@ void setUp() { subject = new TxnRateFeeMultiplierSource(mockProps, throttling); } + @Test + void makesDefensiveCopyOfCongestionStarts() { + // setup: + final var someInstants = new Instant[] { + Instant.ofEpochSecond(1_234_567L), + Instant.ofEpochSecond(2_234_567L), + }; + + // given: + subject.resetCongestionLevelStarts(someInstants); + + // when: + final var equalNotSameInstants = subject.congestionLevelStarts(); + + // then: + assertEquals(List.of(someInstants), List.of(equalNotSameInstants)); + assertNotSame(someInstants, equalNotSameInstants); + } + @Test void updatesCongestionStarts() { // when: subject.resetCongestionLevelStarts(congestionStarts); // then: - Assertions.assertSame(congestionStarts, subject.congestionLevelStarts()); + Assertions.assertEquals(List.of(congestionStarts), List.of(subject.congestionLevelStarts())); } /* MockGlobalDynamicProps has 2 secs for minCongestionPeriod */ diff --git a/hedera-node/src/test/java/com/hedera/services/state/merkle/MerkleNetworkContextTest.java b/hedera-node/src/test/java/com/hedera/services/state/merkle/MerkleNetworkContextTest.java index 04d62defe671..2b9cea17e163 100644 --- a/hedera-node/src/test/java/com/hedera/services/state/merkle/MerkleNetworkContextTest.java +++ b/hedera-node/src/test/java/com/hedera/services/state/merkle/MerkleNetworkContextTest.java @@ -23,7 +23,6 @@ import com.hedera.services.fees.FeeMultiplierSource; import com.hedera.services.state.serdes.DomainSerdes; import com.hedera.services.state.submerkle.ExchangeRates; -import com.hedera.services.state.submerkle.RichInstant; import com.hedera.services.state.submerkle.SequenceNumber; import com.hedera.services.throttles.DeterministicThrottle; import com.hedera.services.throttling.FunctionalityThrottling; @@ -43,13 +42,13 @@ import java.io.IOException; import java.time.Instant; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Supplier; import static com.hedera.services.state.merkle.MerkleNetworkContext.NO_CONGESTION_STARTS; import static com.hedera.services.state.merkle.MerkleNetworkContext.NO_SNAPSHOTS; +import static com.hedera.services.state.submerkle.RichInstant.fromJava; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -63,6 +62,8 @@ import static org.mockito.BDDMockito.inOrder; import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.verify; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.times; @ExtendWith(LogCaptureExtension.class) @@ -71,7 +72,7 @@ class MerkleNetworkContextTest { private long lastScannedEntity = 1000L; private long entitiesTouchedThisSecond = 123L; private long entitiesScannedThisSecond = 123_456L; - private RichInstant consensusTimeOfLastHandledTxn; + private Instant consensusTimeOfLastHandledTxn; private SequenceNumber seqNo; private SequenceNumber seqNoCopy; private ExchangeRates midnightRateSet; @@ -92,12 +93,11 @@ class MerkleNetworkContextTest { @BeforeEach void setup() { congestionStarts = new Instant[] { - Instant.ofEpochSecond(1_234_567L, 54321), - Instant.ofEpochSecond(1_234_789L, 12345) + Instant.ofEpochSecond(1_234_567L, 54321L), + Instant.ofEpochSecond(1_234_789L, 12345L) }; - consensusTimeOfLastHandledTxn = RichInstant.fromJava( - Instant.ofEpochSecond(1_234_567L, 54321)); + consensusTimeOfLastHandledTxn = Instant.ofEpochSecond(1_234_567L, 54321L); seqNo = mock(SequenceNumber.class); given(seqNo.current()).willReturn(1234L); @@ -109,12 +109,12 @@ void setup() { midnightRateSetCopy = midnightRateSet.copy(); usageSnapshots = new DeterministicThrottle.UsageSnapshot[] { new DeterministicThrottle.UsageSnapshot( - 123L, consensusTimeOfLastHandledTxn.toJava()), + 123L, consensusTimeOfLastHandledTxn), new DeterministicThrottle.UsageSnapshot( - 456L, consensusTimeOfLastHandledTxn.toJava().plusSeconds(1L)) + 456L, consensusTimeOfLastHandledTxn.plusSeconds(1L)) }; - serdes = mock(DomainSerdes.class); + serdes = mock(DomainSerdes.class, RETURNS_DEEP_STUBS); MerkleNetworkContext.serdes = serdes; subject = new MerkleNetworkContext( @@ -123,7 +123,7 @@ void setup() { lastScannedEntity, midnightRateSet, usageSnapshots, - richCongestionStarts(), + congestionStarts(), stateVersion, entitiesScannedThisSecond, entitiesTouchedThisSecond); @@ -203,6 +203,21 @@ void toStringRendersAsExpected() { " 1970-01-15T06:56:07.000054321Z\n" + " 1970-01-15T06:59:49.000012345Z"; + var noStateVersionAndNulConsensusHandledTxn = "The network context (state version ) is,\n" + + " Consensus time of last handled transaction :: \n" + + " Midnight rate set :: 1ℏ <-> 14¢ til 1234567 | 1ℏ <-> 15¢ til 2345678\n" + + " Next entity number :: 1234\n" + + " Last scanned entity :: 1000\n" + + " Entities scanned last consensus second :: 123456\n" + + " Entities touched last consensus second :: 123\n" + + " Throttle usage snapshots are ::\n" + + " 100 used (last decision time 1970-01-01T00:00:01.000000100Z)\n" + + " 200 used (last decision time 1970-01-01T00:00:02.000000200Z)\n" + + " 300 used (last decision time 1970-01-01T00:00:03.000000300Z)\n" + + " Congestion level start times are ::\n" + + " 1970-01-15T06:56:07.000054321Z\n" + + " 1970-01-15T06:59:49.000012345Z"; + // then: assertEquals(desiredWithStateVersion, subject.toString()); @@ -210,6 +225,12 @@ void toStringRendersAsExpected() { subject.setStateVersion(MerkleNetworkContext.UNRECORDED_STATE_VERSION); // then: assertEquals(desiredWithoutStateVersion, subject.toString()); + + // and when: + subject.setConsensusTimeOfLastHandledTxn(null); + // then: + assertEquals(noStateVersionAndNulConsensusHandledTxn, subject.toString()); + } @Test @@ -251,13 +272,27 @@ void updatesEmptyLevelStartsAsExpected() { // setup: feeMultiplierSource = mock(FeeMultiplierSource.class); - given(feeMultiplierSource.congestionLevelStarts()).willReturn(new Instant[0]); + given(feeMultiplierSource.congestionLevelStarts()).willReturn(NO_CONGESTION_STARTS); + + // when: + subject.updateCongestionStartsFrom(feeMultiplierSource); + + // then: + assertEquals(NO_CONGESTION_STARTS, subject.getCongestionLevelStarts()); + } + + @Test + void updatesNullCongestionLevelStartsAsExpected() { + // setup: + feeMultiplierSource = mock(FeeMultiplierSource.class); + + given(feeMultiplierSource.congestionLevelStarts()).willReturn(null); // when: subject.updateCongestionStartsFrom(feeMultiplierSource); // then: - assertSame(NO_CONGESTION_STARTS, subject.getCongestionLevelStarts()); + assertEquals(NO_CONGESTION_STARTS, subject.getCongestionLevelStarts()); } @Test @@ -298,7 +333,7 @@ void updatesCongestionStartsAsExpected() { subject.updateCongestionStartsFrom(feeMultiplierSource); // then: - assertArrayEquals(richCongestionStarts(), subject.getCongestionLevelStarts()); + assertArrayEquals(congestionStarts(), subject.getCongestionLevelStarts()); } @Test @@ -421,7 +456,7 @@ void deserializeWorksForPre0130() throws IOException { MerkleNetworkContext.seqNoSupplier = () -> seqNo; InOrder inOrder = inOrder(in, seqNo); - given(serdes.readNullableInstant(in)).willReturn(consensusTimeOfLastHandledTxn); + given(serdes.readNullableInstant(in).toJava()).willReturn(consensusTimeOfLastHandledTxn); // when: subject.deserialize(in, MerkleNetworkContext.PRE_RELEASE_0130_VERSION); @@ -429,7 +464,7 @@ void deserializeWorksForPre0130() throws IOException { // then: assertEquals(consensusTimeOfLastHandledTxn, subject.getConsensusTimeOfLastHandledTxn()); assertSame(usageSnapshots, subject.usageSnapshots()); - assertArrayEquals(richCongestionStarts(), subject.getCongestionLevelStarts()); + assertArrayEquals(congestionStarts(), subject.getCongestionLevelStarts()); // and: inOrder.verify(seqNo).deserialize(in); inOrder.verify(in).readSerializable(booleanThat(Boolean.TRUE::equals), any(Supplier.class)); @@ -451,12 +486,12 @@ void deserializeWorksFor0130() throws IOException { given(in.readLong()) .willReturn(usageSnapshots[0].used()) .willReturn(usageSnapshots[1].used()); - given(serdes.readNullableInstant(in)) + given(serdes.readNullableInstant(in).toJava()) .willReturn(consensusTimeOfLastHandledTxn) - .willReturn(RichInstant.fromJava(usageSnapshots[0].lastDecisionTime())) - .willReturn(RichInstant.fromJava(usageSnapshots[1].lastDecisionTime())) - .willReturn(RichInstant.fromJava(congestionStarts[0])) - .willReturn(RichInstant.fromJava(congestionStarts[1])); + .willReturn(usageSnapshots[0].lastDecisionTime()) + .willReturn(usageSnapshots[1].lastDecisionTime()) + .willReturn(congestionStarts[0]) + .willReturn(congestionStarts[1]); // when: subject.deserialize(in, MerkleNetworkContext.RELEASE_0130_VERSION); @@ -464,7 +499,7 @@ void deserializeWorksFor0130() throws IOException { // then: assertEquals(consensusTimeOfLastHandledTxn, subject.getConsensusTimeOfLastHandledTxn()); assertArrayEquals(usageSnapshots, subject.usageSnapshots()); - assertArrayEquals(richCongestionStarts(), subject.getCongestionLevelStarts()); + assertArrayEquals(congestionStarts(), subject.getCongestionLevelStarts()); // and: inOrder.verify(seqNo).deserialize(in); inOrder.verify(in).readSerializable(booleanThat(Boolean.TRUE::equals), any(Supplier.class)); @@ -493,11 +528,11 @@ void deserializeWorksFor0140() throws IOException { .willReturn(entitiesScannedThisSecond) .willReturn(entitiesTouchedThisSecond); given(serdes.readNullableInstant(in)) - .willReturn(consensusTimeOfLastHandledTxn) - .willReturn(RichInstant.fromJava(usageSnapshots[0].lastDecisionTime())) - .willReturn(RichInstant.fromJava(usageSnapshots[1].lastDecisionTime())) - .willReturn(RichInstant.fromJava(congestionStarts[0])) - .willReturn(RichInstant.fromJava(congestionStarts[1])); + .willReturn(fromJava(consensusTimeOfLastHandledTxn)) + .willReturn(fromJava(usageSnapshots[0].lastDecisionTime())) + .willReturn(fromJava(usageSnapshots[1].lastDecisionTime())) + .willReturn(fromJava(congestionStarts[0])) + .willReturn(fromJava(congestionStarts[1])); // when: subject.deserialize(in, MerkleNetworkContext.RELEASE_0140_VERSION); @@ -507,7 +542,49 @@ void deserializeWorksFor0140() throws IOException { assertEquals(entitiesScannedThisSecond, subject.getEntitiesScannedThisSecond()); assertEquals(entitiesTouchedThisSecond, subject.getEntitiesTouchedThisSecond()); assertArrayEquals(usageSnapshots, subject.usageSnapshots()); - assertArrayEquals(richCongestionStarts(), subject.getCongestionLevelStarts()); + assertArrayEquals(congestionStarts(), subject.getCongestionLevelStarts()); + // and: + inOrder.verify(seqNo).deserialize(in); + inOrder.verify(in).readSerializable(booleanThat(Boolean.TRUE::equals), any(Supplier.class)); + // and: + assertEquals(lastScannedEntity, subject.lastScannedEntity()); + assertEquals(stateVersion, subject.getStateVersion()); + } + + @Test + void deserializeWorksForNullInstants() throws IOException { + // setup: + var in = mock(SerializableDataInputStream.class); + MerkleNetworkContext.ratesSupplier = () -> midnightRateSet; + MerkleNetworkContext.seqNoSupplier = () -> seqNo; + InOrder inOrder = inOrder(in, seqNo); + + subject = new MerkleNetworkContext(); + + given(in.readInt()) + .willReturn(usageSnapshots.length) + .willReturn(congestionStarts.length) + .willReturn(stateVersion); + given(in.readLong()) + .willReturn(usageSnapshots[0].used()) + .willReturn(usageSnapshots[1].used()) + .willReturn(lastScannedEntity) + .willReturn(entitiesScannedThisSecond) + .willReturn(entitiesTouchedThisSecond); + given(serdes.readNullableInstant(in)).willReturn(null); + + // when: + subject.deserialize(in, MerkleNetworkContext.RELEASE_0140_VERSION); + + // then: + Assertions.assertNull(subject.getConsensusTimeOfLastHandledTxn()); + assertEquals(entitiesScannedThisSecond, subject.getEntitiesScannedThisSecond()); + assertEquals(entitiesTouchedThisSecond, subject.getEntitiesTouchedThisSecond()); + assertArrayEquals(new DeterministicThrottle.UsageSnapshot[] { + new DeterministicThrottle.UsageSnapshot(usageSnapshots[0].used(), null), + new DeterministicThrottle.UsageSnapshot(usageSnapshots[1].used(), null) + }, subject.usageSnapshots()); + assertArrayEquals(new Instant[] { null, null }, subject.getCongestionLevelStarts()); // and: inOrder.verify(seqNo).deserialize(in); inOrder.verify(in).readSerializable(booleanThat(Boolean.TRUE::equals), any(Supplier.class)); @@ -533,19 +610,19 @@ void serializeWorks() throws IOException { subject.serialize(out); // expect: - inOrder.verify(serdes).writeNullableInstant(consensusTimeOfLastHandledTxn, out); + inOrder.verify(serdes).writeNullableInstant(fromJava(consensusTimeOfLastHandledTxn), out); inOrder.verify(seqNo).serialize(out); inOrder.verify(out).writeSerializable(midnightRateSet, true); // and: inOrder.verify(out).writeInt(3); for (int i = 0; i < 3; i++) { inOrder.verify(out).writeLong(used[i]); - inOrder.verify(serdes).writeNullableInstant(RichInstant.fromJava(lastUseds[i]), out); + inOrder.verify(serdes).writeNullableInstant(fromJava(lastUseds[i]), out); } // and: inOrder.verify(out).writeInt(2); for (int i = 0; i < 2; i++) { - inOrder.verify(serdes).writeNullableInstant(richCongestionStarts()[i], out); + inOrder.verify(serdes).writeNullableInstant(fromJava(congestionStarts()[i]), out); } inOrder.verify(out).writeLong(lastScannedEntity); inOrder.verify(out).writeLong(entitiesScannedThisSecond); @@ -553,6 +630,20 @@ void serializeWorks() throws IOException { inOrder.verify(out).writeInt(stateVersion); } + @Test + void canSerializeNullCongestionLevelStarts() { + // setup: + var out = mock(SerializableDataOutputStream.class); + feeMultiplierSource = mock(FeeMultiplierSource.class); + + given(feeMultiplierSource.congestionLevelStarts()).willReturn(new Instant[] { null, null }); + + // when: + subject.updateCongestionStartsFrom(feeMultiplierSource); + // and: + Assertions.assertDoesNotThrow(() -> subject.serialize(out)); + } + @Test void sanityChecks() { assertEquals(MerkleNetworkContext.MERKLE_VERSION, subject.getVersion()); @@ -592,7 +683,7 @@ private List snapshots() { return cur; } - private RichInstant[] richCongestionStarts() { - return Arrays.stream(congestionStarts).map(RichInstant::fromJava).toArray(RichInstant[]::new); + private Instant[] congestionStarts() { + return congestionStarts; } }