Skip to content

Commit 78a37fc

Browse files
committed
fix: use the validStart null flag in TransactionId.compareTo
The validStart null-handling branch returned based on thisAccountIdIsNull instead of thisStartIsNull. Since that branch is only reachable when both ids agree on accountId nullness, the comparison returned the same sign in both directions, violating the Comparable antisymmetry and transitivity contracts. Fixes #2740
1 parent f371599 commit 78a37fc

2 files changed

Lines changed: 19 additions & 1 deletion

File tree

sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ public int compareTo(TransactionId o) {
529529
var thisStartIsNull = (validStart == null);
530530
var otherStartIsNull = (o.validStart == null);
531531
if (thisStartIsNull != otherStartIsNull) {
532-
return thisAccountIdIsNull ? -1 : 1;
532+
return thisStartIsNull ? -1 : 1;
533533
}
534534
if (!thisStartIsNull) {
535535
return validStart.compareTo(o.validStart);

sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionIdTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,22 @@ void equalsHashCodeContractWithNonce() {
186186
txnId1.equals(txnId2) && txnId1.hashCode() != txnId2.hashCode(),
187187
"equals/hashCode contract violation: equal objects must have same hashCode");
188188
}
189+
190+
@Test
191+
void compareToIsAntisymmetricWhenOnlyOneValidStartIsNull() {
192+
var accountId = AccountId.fromString("0.0.23847");
193+
var withValidStart = new TransactionId(accountId, Instant.ofEpochSecond(1588539964));
194+
var withoutValidStart = new TransactionId(accountId, null);
195+
196+
// A null validStart must sort before a non-null one no matter which side it is on.
197+
assertThat(withoutValidStart.compareTo(withValidStart)).isEqualTo(-1);
198+
assertThat(withValidStart.compareTo(withoutValidStart)).isEqualTo(1);
199+
200+
// Same requirement when neither transaction id carries an account id.
201+
var noAccountWithStart = new TransactionId(null, Instant.ofEpochSecond(1588539964));
202+
var noAccountWithoutStart = new TransactionId(null, null);
203+
204+
assertThat(noAccountWithoutStart.compareTo(noAccountWithStart)).isEqualTo(-1);
205+
assertThat(noAccountWithStart.compareTo(noAccountWithoutStart)).isEqualTo(1);
206+
}
189207
}

0 commit comments

Comments
 (0)