From 5c2e169d27525bb502e821ea6459f2c772311a81 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Tue, 7 Nov 2023 12:19:20 +0000 Subject: [PATCH] CORE-18180 Correct `StateRef.toString` (#1327) The index in `StateRef.toString` was being formatted which added commas to the output. This created an invalid string representation of a `StateRef`, causing errors if we ever called `StateRef.parse` on the output of `StateRef.toString`. Rely on normal string concatenation instead of `MessageFormat.format` to resolve this. --- .../net/corda/v5/ledger/utxo/StateRef.java | 10 ++--- .../v5/ledger/utxo/StateRefParseTest.java | 40 ++++++++++++++++--- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/ledger/ledger-utxo/src/main/java/net/corda/v5/ledger/utxo/StateRef.java b/ledger/ledger-utxo/src/main/java/net/corda/v5/ledger/utxo/StateRef.java index e3a94ff57d..6132454d12 100644 --- a/ledger/ledger-utxo/src/main/java/net/corda/v5/ledger/utxo/StateRef.java +++ b/ledger/ledger-utxo/src/main/java/net/corda/v5/ledger/utxo/StateRef.java @@ -6,7 +6,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.text.MessageFormat; import java.util.Objects; /** @@ -72,7 +71,8 @@ public static StateRef parse(@NotNull final String value, DigestService digestSe final int lastIndexOfDelimiter = value.lastIndexOf(DELIMITER); if (lastIndexOfDelimiter == -1) { throw new IllegalArgumentException( - MessageFormat.format("Failed to parse a StateRef from the specified value. At least one delimiter ({0}) is expected in value: {1}.", DELIMITER, value) + "Failed to parse a StateRef from the specified value. At least one delimiter (" + DELIMITER + ") " + + "is expected in value: " + value + "." ); } @@ -84,12 +84,12 @@ public static StateRef parse(@NotNull final String value, DigestService digestSe return new StateRef(transactionId, index); } catch (NumberFormatException numberFormatException) { throw new IllegalArgumentException( - MessageFormat.format("Failed to parse a StateRef from the specified value. The index is malformed: {0}.", value), + "Failed to parse a StateRef from the specified value. The index is malformed: " + value + ".", numberFormatException ); } catch (IllegalArgumentException illegalArgumentException) { throw new IllegalArgumentException( - MessageFormat.format("Failed to parse a StateRef from the specified value. The transaction ID is malformed: {0}.", value), + "Failed to parse a StateRef from the specified value. The transaction ID is malformed: " + value + ".", illegalArgumentException ); } @@ -126,6 +126,6 @@ public int hashCode() { */ @Override public String toString() { - return MessageFormat.format("{0}{1}{2}", transactionId, DELIMITER, index); + return transactionId + DELIMITER + index; } } diff --git a/ledger/ledger-utxo/src/test/java/net/corda/v5/ledger/utxo/StateRefParseTest.java b/ledger/ledger-utxo/src/test/java/net/corda/v5/ledger/utxo/StateRefParseTest.java index 91219d60b7..ef5272b191 100644 --- a/ledger/ledger-utxo/src/test/java/net/corda/v5/ledger/utxo/StateRefParseTest.java +++ b/ledger/ledger-utxo/src/test/java/net/corda/v5/ledger/utxo/StateRefParseTest.java @@ -2,9 +2,14 @@ import net.corda.v5.application.crypto.DigestService; import net.corda.v5.crypto.SecureHash; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; @@ -26,21 +31,21 @@ void parseValidValue() { final StateRef stateRef = new StateRef(secureHash, Integer.parseUnsignedInt(value.substring(lastIndexOfDelimiter + 1))); - Assertions.assertEquals(StateRef.parse(value, digestService).getTransactionId().toString(), stateRef.getTransactionId().toString()); + assertEquals(StateRef.parse(value, digestService).getTransactionId().toString(), stateRef.getTransactionId().toString()); } @Test void parseMalformedWithZeroDelimiter() { final String value = "XXX"; final String errorMessage = assertThrows(IllegalArgumentException.class, () -> StateRef.parse(value, digestService)).getMessage(); - Assertions.assertEquals(String.format("Failed to parse a StateRef from the specified value. At least one delimiter (%s) is expected in value: %s.", DELIMITER, value), errorMessage); + assertEquals(String.format("Failed to parse a StateRef from the specified value. At least one delimiter (%s) is expected in value: %s.", DELIMITER, value), errorMessage); } @Test void parseMalformedIndex() { final String value = ":asdf:a"; final String errorMessage = assertThrows(IllegalArgumentException.class, () -> StateRef.parse(value, digestService)).getMessage(); - Assertions.assertEquals(String.format("Failed to parse a StateRef from the specified value. The index is malformed: %s.", value), errorMessage); + assertEquals(String.format("Failed to parse a StateRef from the specified value. The index is malformed: %s.", value), errorMessage); } @Test @@ -57,6 +62,31 @@ void parseMalformedTransactionId() { final String errorMessage = assertThrows(IllegalArgumentException.class, () -> StateRef.parse(value, digestService)).getMessage(); - Assertions.assertEquals(String.format("Failed to parse a StateRef from the specified value. The transaction ID is malformed: %s.", value), errorMessage); + assertEquals(String.format("Failed to parse a StateRef from the specified value. The transaction ID is malformed: %s.", value), errorMessage); + } + + @ParameterizedTest(name = "Parse large state ref index and reparse into state ref {0}") + @MethodSource("stateRefIndexes") + void parseLargeValueAndReparse(int index) { + final String value = "SHA-256D:ED87C7285E1E34BF5E46302086F76317ACE9B17AEF7BD086EE09A5ACBD17CEA4:" + index; + final int lastIndexOfDelimiter = value.lastIndexOf(DELIMITER); + final String subStringBeforeDelimiter = value.substring(0, lastIndexOfDelimiter); + final SecureHash secureHash = mock(SecureHash.class); + + doReturn(secureHash).when(digestService).parseSecureHash(subStringBeforeDelimiter); + doReturn(subStringBeforeDelimiter).when(secureHash).toString(); + + final StateRef stateRef = StateRef.parse(value, digestService); + + assertEquals(stateRef, StateRef.parse(stateRef.toString(), digestService)); + } + + public static Stream stateRefIndexes() { + return Stream.of( + Arguments.of(1000), + Arguments.of(1001), + Arguments.of(99999999), + Arguments.of(Integer.MAX_VALUE) + ); } }