From a7b4cbdd77f9a48a2a54eda2f20363aa45594321 Mon Sep 17 00:00:00 2001 From: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Wed, 4 Nov 2026 06:51:06 +0000 Subject: [PATCH] Preserve empty attributes when parsing composite keys CompositeKey.parseCompositeKey() split the key with String.split(delimiter, 0), which discards trailing empty strings. Empty trailing attributes were lost, so the parsed key no longer matched the original ledger key, and a composite key with an empty object type and no attributes failed with an ArrayIndexOutOfBoundsException. The Go and Node shims keep these attributes. Signed-off-by: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> --- .../fabric/shim/ledger/CompositeKey.java | 7 +++++-- .../fabric/shim/ledger/CompositeKeyTest.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/CompositeKey.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/CompositeKey.java index 4c88c2374..159669c23 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/CompositeKey.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/CompositeKey.java @@ -76,8 +76,11 @@ public static CompositeKey parseCompositeKey(final String compositeKey) { throw CompositeKeyFormatException.forInputString(compositeKey, compositeKey, 0); } // relying on the fact that NAMESPACE == DELIMETER - final String[] segments = compositeKey.split(DELIMITER, 0); - return new CompositeKey(segments[1], Arrays.stream(segments).skip(2).toArray(String[]::new)); + // keep empty segments so that empty attributes are preserved + final String[] segments = compositeKey.split(DELIMITER, -1); + // the final delimiter terminates the last attribute, so the empty segment after it is not an attribute + final int end = compositeKey.endsWith(DELIMITER) ? segments.length - 1 : segments.length; + return new CompositeKey(segments[1], Arrays.copyOfRange(segments, 2, Math.max(2, end))); } /** diff --git a/fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ledger/CompositeKeyTest.java b/fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ledger/CompositeKeyTest.java index 21293a01a..fd7679086 100644 --- a/fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ledger/CompositeKeyTest.java +++ b/fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ledger/CompositeKeyTest.java @@ -105,6 +105,22 @@ void testParseCompositeKey() { assertThat(key.toString(), is(equalTo("\u0000abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000"))); } + @Test + void testParseCompositeKeyWithEmptyAttributes() { + final CompositeKey key = CompositeKey.parseCompositeKey("\u0000abc\u0000\u0000def\u0000\u0000\u0000"); + assertThat(key.getObjectType(), is(equalTo("abc"))); + assertThat(key.getAttributes(), contains("", "def", "", "")); + assertThat(key.toString(), is(equalTo("\u0000abc\u0000\u0000def\u0000\u0000\u0000"))); + } + + @Test + void testParseCompositeKeyWithEmptyObjectType() { + final CompositeKey key = CompositeKey.parseCompositeKey("\u0000\u0000"); + assertThat(key.getObjectType(), is(equalTo(""))); + assertThat(key.getAttributes(), hasSize(0)); + assertThat(key.toString(), is(equalTo("\u0000\u0000"))); + } + @Test void testParseCompositeKeyInvalidObjectType() { assertThatThrownBy(() ->