Skip to content

Commit

Permalink
feat: Add initial support for Comparable in protobuf-defined state ty…
Browse files Browse the repository at this point in the history
…pes.

* Changed the protobufs branch to use the commit hash with updated proto definitions.
   * This is part of moving away from branches to commit hashes and tags for long-term stability.
* Fixed "pretty print" test in smart contract impl that depends on internal data type names in PBJ.
* Modified KeyComparator to make use of new compareTo methods and explicitly "normalize" results.
* Fixed new protobuf clone task to not prepend `origin` unnecessarily, preventing use of commit hash.

Signed-off-by: Joseph Sinclair <[email protected]>
  • Loading branch information
jsync-swirlds committed Feb 9, 2024
1 parent 98d1e45 commit 4a71d3b
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 310 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ abstract class GitClone : DefaultTask() {
}
exec.exec {
workingDir = localClone.asFile
commandLine("git", "reset", "--hard", "origin/${branchOrTag.get()}", "-q")
commandLine("git", "reset", "--hard", "${branchOrTag.get()}", "-q")
}
}
}
}
2 changes: 1 addition & 1 deletion hedera-node/hapi/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ description = "Hedera API"

// Add downloaded HAPI repo protobuf files into build directory and add to sources to build them
tasks.cloneHederaProtobufs {
branchOrTag = "add-pbj-types-for-state"
branchOrTag = "add-comparable-to-state-messages"
// As long as the 'branchOrTag' above is not stable, run always:
outputs.upToDateWhen { false }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,38 @@
public class KeyComparator implements Comparator<Key> {
@Override
public int compare(final Key first, final Key second) {
if (first == second) return 0;
else if (first == null) return -1;
else if (second == null) return 1;
// use a temporary so we can "normalize" results at the end.
int result;
if (first == second) result = 0;
else if (first == null) result = -1;
else if (second == null) result = 1;
// Note, record defines equals, but it uses reference equality for reference type members.
// We must not use reference equality here, so we cannot use that.
else if (first.key() == null) return second.key() == null ? 0 : -1;
else if (second.key() == null) return 1;

final KeyOneOfType firstKeyType = first.key().kind();
final KeyOneOfType secondKeyType = second.key().kind();
if (firstKeyType != secondKeyType) return compareCrossType(firstKeyType, secondKeyType);
else if (first.key() == null) result = second.key() == null ? 0 : -1;
else if (second.key() == null) result = 1;
else {
// both keys are the same type; so compare the details.
return switch (firstKeyType) {
case UNSET -> 0; // both unset compares equal.
case CONTRACT_ID -> compareContractId(first, second);
case DELEGATABLE_CONTRACT_ID -> compareDelegateable(first, second);
case ED25519 -> compareEdwards(first, second);
case ECDSA_SECP256K1 -> compareSecp256k(first, second);
case THRESHOLD_KEY -> compareThreshold(first, second);
case KEY_LIST -> compareKeyList(first, second);
final KeyOneOfType firstKeyType = first.key().kind();
final KeyOneOfType secondKeyType = second.key().kind();
if (firstKeyType != secondKeyType)
result = firstKeyType.compareTo(secondKeyType);
else {
// both keys are the same type; so compare the details.
result = switch (firstKeyType) {
case UNSET -> 0; // both unset compares equal.
case CONTRACT_ID -> compareContractId(first, second);
case DELEGATABLE_CONTRACT_ID -> compareDelegateable(first, second);
case ED25519 -> compareEdwards(first, second);
case ECDSA_SECP256K1 -> compareSecp256k(first, second);
case THRESHOLD_KEY -> compareThreshold(first, second);
case KEY_LIST -> compareKeyList(first, second);
// The next two are not currently supported key types.
case RSA_3072 -> compareRsa(first, second);
case ECDSA_384 -> compareEcdsa(first, second);
};
case RSA_3072 -> compareRsa(first, second);
case ECDSA_384 -> compareEcdsa(first, second);
};
}
}
// Use Integer.compare to "normalize" result as exactly -1, 0, or 1
return Integer.compare(result, 0);
}

private int compareContractId(final Key first, final Key second) {
Expand All @@ -72,7 +78,7 @@ private int compareContractId(final Key first, final Key second) {
if (lhs == rhs) return 0;
else if (lhs == null) return -1;
else if (rhs == null) return 1;
else return compareId(lhs, rhs);
else return lhs.compareTo(rhs);
}

private int compareDelegateable(final Key first, final Key second) {
Expand All @@ -81,34 +87,7 @@ private int compareDelegateable(final Key first, final Key second) {
if (lhs == rhs) return 0;
else if (lhs == null) return -1;
else if (rhs == null) return 1;
else return compareId(lhs, rhs);
}

private int compareId(final ContractID leftId, final ContractID rightId) {
final long realmOne = leftId.realmNum();
final long realmTwo = rightId.realmNum();
final long shardOne = leftId.shardNum();
final long shardTwo = rightId.shardNum();
final Bytes evmOne = leftId.evmAddress();
final Bytes evmTwo = rightId.evmAddress();
final Long leftNum = leftId.contractNum();
final Long rightNum = rightId.contractNum();
// default -1 so contractNum sorts "before" evm address
final long firstId = leftNum != null ? leftNum.longValue() : -1L;
final long secondId = rightNum != null ? rightNum.longValue() : -1L;
if (realmOne == realmTwo) {
if (shardOne == shardTwo) {
if (firstId == secondId) {
return compareBytes(evmOne, evmTwo);
} else {
return Long.compare(firstId, secondId);
}
} else {
return Long.compare(shardOne, shardTwo);
}
} else {
return Long.compare(realmOne, realmTwo);
}
else return lhs.compareTo(rhs);
}

private int compareEdwards(final Key first, final Key second) {
Expand Down Expand Up @@ -184,32 +163,6 @@ private int compareBytes(final Bytes lhs, final Bytes rhs) {
if (lhs == rhs) return 0;
else if (lhs == null) return -1;
else if (rhs == null) return 1;

final long leftLength = lhs.length();
final long rightLength = rhs.length();
if (leftLength != rightLength) return leftLength > rightLength ? -1 : 1;
else {
// left and right length are equal.
for (long offset = 0L; offset < leftLength; offset++) {
final byte left = lhs.getByte(offset);
final byte right = rhs.getByte(offset);
if (left != right) return Byte.compareUnsigned(left, right) > 0 ? 1 : -1;
}
}
// nothing differed, so these are equal.
return 0;
}

/**
* Compare two keys of different types for sort ordering.
* The "natural" order, in this case, is just the type order in the proto file.
* @param firstKeyType The OneOfType for the first (left hand) key.
* @param secondKeyType The OneOfType for the second (right hand) key.
* @return a value greater than, equal to, or less than 0 indicating if the first key type is
* "greater than" (sorted after), the same as, or "less than" (sorted before),
* the second key type.
*/
private int compareCrossType(final KeyOneOfType firstKeyType, final KeyOneOfType secondKeyType) {
return Integer.compare(firstKeyType.protoOrdinal(), secondKeyType.protoOrdinal());
else return lhs.compareTo(rhs);
}
}
Loading

0 comments on commit 4a71d3b

Please sign in to comment.