From 9754d4d70f8eebc0ca7f218f7713228fc425553f Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 28 Feb 2024 07:31:31 +0700 Subject: [PATCH 1/7] Reduce visibility of methods. Move package scope method up. --- .../components/cathash/HandleFactory.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java index fcb19fa693..456512a006 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java @@ -22,10 +22,19 @@ @Slf4j public class HandleFactory { - public Handle calculateHandle(byte[] data) { + Handle calculateHandle(byte[] data) { return new Handle(calculateHandleValue(data)); } + static byte[] bucketValues(long handle) { + int buckets = getSize(handle); + byte[] values = new byte[buckets]; + for (int i = 0; i < buckets; i++) { + values[buckets - i - 1] = getNibbleAt(handle, i); + } + return values; + } + /** * Encodes an array of bytes (data) into a single long value, which serves as a compact, unique identifier * (or "handle") for a set of values. Each byte in the array represents a "nibble" (a 4-bit value), and the function @@ -34,7 +43,7 @@ public Handle calculateHandle(byte[] data) { * @param data The distributed hash over the buckets * @return val The composite handle value, which encodes both the sequence of nibbles and the length of the data array */ - static long calculateHandleValue(byte[] data) { + private static long calculateHandleValue(byte[] data) { // Check if the input array exceeds the maximum length of 14 bytes. // This limit ensures that the data can be encoded into a 64-bit long value without overflow. // Since 8 bits are reserved for length encoding only 56 bits can be used (14 * 4) @@ -71,7 +80,7 @@ static long calculateHandleValue(byte[] data) { return val; } - static byte getNibbleAt(long value, int index) { + private static byte getNibbleAt(long value, int index) { if (index < 0 || index > 15) { throw new IllegalArgumentException(String.format("index @%d", index)); } @@ -82,16 +91,7 @@ static byte getNibbleAt(long value, int index) { return (byte) (maskedValue >> index * 4); } - static int getSize(long value) { + private static int getSize(long value) { return getNibbleAt(value, 14); } - - public static byte[] bucketValues(long handle) { - int buckets = getSize(handle); - byte[] values = new byte[buckets]; - for (int i = 0; i < buckets; i++) { - values[buckets - i - 1] = getNibbleAt(handle, i); - } - return values; - } } From f1870ec863b9033ea05113d52d8c4c49f58626f0 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 28 Feb 2024 07:52:21 +0700 Subject: [PATCH 2/7] Remove Handle and HandleFactory. Use an integer array instead of encoding the data into a long which added limitations and made the code more complicate. --- .../desktop/components/cathash/CatHash.java | 13 +-- .../components/cathash/Configuration.java | 13 +++ .../desktop/components/cathash/Handle.java | 50 ---------- .../components/cathash/HandleFactory.java | 97 ------------------- .../cathash/VariableSizeHashing.java | 13 +++ .../CreateProfileController.java | 4 +- 6 files changed, 32 insertions(+), 158 deletions(-) delete mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Handle.java delete mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java index 41513e253e..f3458bd635 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java @@ -30,7 +30,6 @@ @Slf4j public class CatHash { private static final int MAX_CACHE_SIZE = 10000; - private static final HandleFactory HANDLE_FACTORY = new HandleFactory(); private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); public static Image getImage(byte[] pubKeyHash) { @@ -48,20 +47,16 @@ private static Image getImage(ByteArray pubKeyHash, boolean useCache) { BigInteger bigInteger = new BigInteger(pubKeyHash.getBytes()); Configuration configuration = new Configuration(); VariableSizeHashing hashing = new VariableSizeHashing(configuration.getBucketSizes()); - byte[] data = hashing.createBuckets(bigInteger); - Handle handle = HANDLE_FACTORY.calculateHandle(data); - Image image = imageForHandle(handle, configuration); + int[] integerBuckets = hashing.createIntegerBuckets(bigInteger); + Image image = imageFromIntegerBuckets(integerBuckets, configuration); if (useCache && CACHE.size() < MAX_CACHE_SIZE) { CACHE.put(pubKeyHash, image); } return image; } - private static Image imageForHandle(Handle handle, Configuration configuration) { - long ts = System.currentTimeMillis(); - byte[] bucketValues = handle.bucketValues(); - String[] paths = configuration.convertToFacetParts(bucketValues); - log.debug("Generated paths for CatHash image in {} ms", System.currentTimeMillis() - ts); // typically <1ms + private static Image imageFromIntegerBuckets(int[] integerBuckets, Configuration configuration) { + String[] paths = configuration.integerBucketsToPaths(integerBuckets); return ImageUtil.composeImage(paths, configuration.width(), configuration.height()); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java index 44b37be2de..10afde8a0c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java @@ -56,6 +56,19 @@ public class Configuration { }; } + public String[] integerBucketsToPaths(int[] integerBuckets) { + if (integerBuckets.length != BUCKET_COUNT) { + throw new IllegalArgumentException(); + } + + String[] paths = new String[FACET_COUNT]; + for (int facet = 0; facet < FACET_COUNT; facet++) { + int bucketValue = integerBuckets[facet]; + paths[facet] = generatePath(FACET_PATH_TEMPLATES[facet], bucketValue); + } + return paths; + } + public String[] convertToFacetParts(byte[] bucketValues) { if (bucketValues.length != BUCKET_COUNT) { throw new IllegalArgumentException(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Handle.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Handle.java deleted file mode 100644 index 7c057457b4..0000000000 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Handle.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.components.cathash; - -public final class Handle { - private final long value; - - Handle(long v) { - this.value = v; - } - - @Override - protected Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - @Override - public boolean equals(Object o) { - return super.equals(o); - } - - @Override - public String toString() { - return String.format("%016X", value); - } - - @Override - public int hashCode() { - return (int) value; - } - - public byte[] bucketValues() { - return HandleFactory.bucketValues(this.value); - } -} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java deleted file mode 100644 index 456512a006..0000000000 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/HandleFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.components.cathash; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class HandleFactory { - - Handle calculateHandle(byte[] data) { - return new Handle(calculateHandleValue(data)); - } - - static byte[] bucketValues(long handle) { - int buckets = getSize(handle); - byte[] values = new byte[buckets]; - for (int i = 0; i < buckets; i++) { - values[buckets - i - 1] = getNibbleAt(handle, i); - } - return values; - } - - /** - * Encodes an array of bytes (data) into a single long value, which serves as a compact, unique identifier - * (or "handle") for a set of values. Each byte in the array represents a "nibble" (a 4-bit value), and the function - * ensures that these values, along with the length of the data, are packed into the returned long. - * - * @param data The distributed hash over the buckets - * @return val The composite handle value, which encodes both the sequence of nibbles and the length of the data array - */ - private static long calculateHandleValue(byte[] data) { - // Check if the input array exceeds the maximum length of 14 bytes. - // This limit ensures that the data can be encoded into a 64-bit long value without overflow. - // Since 8 bits are reserved for length encoding only 56 bits can be used (14 * 4) - // This means that the maximum number of buckets that we can have is 14 - if (data.length > 14) { - throw new IllegalArgumentException(); - } - - long val = 0; - for (int i = 0; i < data.length; i++) { - int nibble = data[i]; - - // Validate that the current nibble does not exceed the maximum value of 15 (0xF), ensuring it's a - // valid 4-bit value. - // Each nibble uses 4 bits, therefore we can only encode 2^4 (0..15) possibilities - // (i.e. max size per bucket is 15, which represent 16 images) - if (nibble > 15) { // 0xf - throw new IllegalArgumentException(String.format("nibble to large @%d: %02X", i, nibble)); - } - - // Shift the current handle value 4 bits to the left to make room for the new nibble. This operation - // progressively builds up the handle value from its constituent nibbles. - val <<= 4; - - // Incorporate the current nibble into the lowest 4 bits of the handle value. - val |= nibble; - } - - // After processing all nibbles, encode the length of the data array into the handle value. - // This is achieved by shifting the length leftward by (14 * 4) bits (56 bits), which positions the length - // information in the upper 8 bits of the 64-bit long value. This ensures the length can be retrieved from the - // handle and also contributes to the uniqueness of the handle. - val |= ((long) data.length) << (14 * 4); - return val; - } - - private static byte getNibbleAt(long value, int index) { - if (index < 0 || index > 15) { - throw new IllegalArgumentException(String.format("index @%d", index)); - } - - long mask = (long) 0xf << (index * 4); - long maskedValue = (value & mask); - - return (byte) (maskedValue >> index * 4); - } - - private static int getSize(long value) { - return getNibbleAt(value, 14); - } -} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/VariableSizeHashing.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/VariableSizeHashing.java index e117b036b9..77bd03c477 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/VariableSizeHashing.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/VariableSizeHashing.java @@ -67,4 +67,17 @@ public byte[] createBuckets(BigInteger hash) { return ret; } + + public int[] createIntegerBuckets(BigInteger input) { + int currentBucket = 0; + int[] result = new int[bucketSizes.length]; + while (currentBucket < bucketSizes.length) { + BigInteger[] divisorReminder = input.divideAndRemainder(BigInteger.valueOf(bucketSizes[currentBucket])); + input = divisorReminder[0]; + long reminder = divisorReminder[1].longValue(); + result[currentBucket] = (int) Math.abs(reminder % bucketSizes[currentBucket]); + currentBucket++; + } + return result; + } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java index 29b91ccd3e..8ab20d28c6 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java @@ -22,8 +22,8 @@ import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; import bisq.desktop.common.view.Navigation; -import bisq.desktop.components.overlay.Popup; import bisq.desktop.components.cathash.CatHash; +import bisq.desktop.components.overlay.Popup; import bisq.desktop.overlay.OverlayController; import bisq.i18n.Res; import bisq.identity.IdentityService; @@ -154,7 +154,7 @@ private CompletableFuture createProofOfWork(byte[] pubKeyHash) { .thenApply(proofOfWork -> { long powDuration = System.currentTimeMillis() - ts; log.info("Proof of work creation completed after {} ms", powDuration); - createSimulatedDelay(powDuration); + //createSimulatedDelay(powDuration); UIThread.run(() -> { model.setProofOfWork(Optional.of(proofOfWork)); String nym = NymIdGenerator.fromHash(pubKeyHash); From c77eb234f41ee90666e9bfffc20383987c776314 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 28 Feb 2024 07:54:04 +0700 Subject: [PATCH 3/7] Replace #ITEM# with NUM --- .../components/cathash/Configuration.java | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java index 10afde8a0c..63066c72dc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java @@ -44,15 +44,15 @@ public class Configuration { static { String postFix = ".png"; FACET_PATH_TEMPLATES = new String[]{ - ROOT + "bg0/#ITEM#" + postFix, - ROOT + "bg1/#ITEM#" + postFix, - ROOT + "ears0/#ITEM#" + postFix, - ROOT + "ears1/#ITEM#" + postFix, - ROOT + "face0/#ITEM#" + postFix, - ROOT + "face1/#ITEM#" + postFix, - ROOT + "eyes0/#ITEM#" + postFix, - ROOT + "nose0/#ITEM#" + postFix, - ROOT + "whiskers0/#ITEM#" + postFix, + ROOT + "bg0/NUM" + postFix, + ROOT + "bg1/NUM" + postFix, + ROOT + "ears0/NUM" + postFix, + ROOT + "ears1/NUM" + postFix, + ROOT + "face0/NUM" + postFix, + ROOT + "face1/NUM" + postFix, + ROOT + "eyes0/NUM" + postFix, + ROOT + "nose0/NUM" + postFix, + ROOT + "whiskers0/NUM" + postFix, }; } @@ -69,21 +69,8 @@ public String[] integerBucketsToPaths(int[] integerBuckets) { return paths; } - public String[] convertToFacetParts(byte[] bucketValues) { - if (bucketValues.length != BUCKET_COUNT) { - throw new IllegalArgumentException(); - } - - String[] paths = new String[FACET_COUNT]; - for (int facet = 0; facet < FACET_COUNT; facet++) { - int bucketValue = bucketValues[facet]; - paths[facet] = generatePath(FACET_PATH_TEMPLATES[facet], bucketValue); - } - return paths; - } - private String generatePath(String facetPathTemplate, int bucketValue) { - return facetPathTemplate.replaceAll("#ITEM#", String.format("%02d", bucketValue)); + return facetPathTemplate.replaceAll("NUM", String.format("%02d", bucketValue)); } public byte[] getBucketSizes() { From f3ad8004cb0b83542c8d00ccfbb7f172cd7682c0 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 28 Feb 2024 07:58:20 +0700 Subject: [PATCH 4/7] Reduce visibility of methods. Move package scope method up. Use SIZE instead of width()/height() methods --- .../bisq/desktop/components/cathash/CatHash.java | 3 ++- .../components/cathash/Configuration.java | 16 ++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java index f3458bd635..46e09544ad 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java @@ -29,6 +29,7 @@ // Number of combinations: 3 * 15 * 15 * 15 * 15 * 15 * 15 = 34171875 (2 ^ 25) @Slf4j public class CatHash { + private static final int SIZE = 300; private static final int MAX_CACHE_SIZE = 10000; private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); @@ -57,6 +58,6 @@ private static Image getImage(ByteArray pubKeyHash, boolean useCache) { private static Image imageFromIntegerBuckets(int[] integerBuckets, Configuration configuration) { String[] paths = configuration.integerBucketsToPaths(integerBuckets); - return ImageUtil.composeImage(paths, configuration.width(), configuration.height()); + return ImageUtil.composeImage(paths, SIZE, SIZE); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java index 63066c72dc..734c227459 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java @@ -56,7 +56,7 @@ public class Configuration { }; } - public String[] integerBucketsToPaths(int[] integerBuckets) { + String[] integerBucketsToPaths(int[] integerBuckets) { if (integerBuckets.length != BUCKET_COUNT) { throw new IllegalArgumentException(); } @@ -69,19 +69,11 @@ public String[] integerBucketsToPaths(int[] integerBuckets) { return paths; } - private String generatePath(String facetPathTemplate, int bucketValue) { - return facetPathTemplate.replaceAll("NUM", String.format("%02d", bucketValue)); - } - - public byte[] getBucketSizes() { + byte[] getBucketSizes() { return BUCKET_SIZES; } - public int width() { - return 300; - } - - public int height() { - return 300; + private String generatePath(String facetPathTemplate, int bucketValue) { + return facetPathTemplate.replaceAll("NUM", String.format("%02d", bucketValue)); } } From 76d06198a191c654dd902a41a34847b557a39b47 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 28 Feb 2024 08:20:05 +0700 Subject: [PATCH 5/7] Remove VariableSizeHashing Add BucketGenerator Increase *_COUNT values by 1 (was wrong before to use max index) --- ...eSizeHashing.java => BucketGenerator.java} | 62 +++++++------------ .../desktop/components/cathash/CatHash.java | 14 +++-- .../components/cathash/Configuration.java | 49 +++++++-------- 3 files changed, 51 insertions(+), 74 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/{VariableSizeHashing.java => BucketGenerator.java} (50%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/VariableSizeHashing.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketGenerator.java similarity index 50% rename from apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/VariableSizeHashing.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketGenerator.java index 77bd03c477..525a6da5f2 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/VariableSizeHashing.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketGenerator.java @@ -18,28 +18,22 @@ package bisq.desktop.components.cathash; import java.math.BigInteger; -import java.util.UUID; -/** - * "Hash" a big integer (expected: hash or uuid) into buckets. The goal is to deterministically - * "repack" the randomness of the hash into the bucket. - *

- * Each bucket is defined by a maximum value. The implementation guarantees that the values in bucket n is in the - * range 0..(bucketSize[n]-1). - */ -public class VariableSizeHashing { - private final byte[] bucketSizes; - - public VariableSizeHashing(byte[] bucketSizes) { - this.bucketSizes = bucketSizes; - } - - static BigInteger uuidToBigInteger(UUID uuid) { - return BigInteger.valueOf(uuid.getMostSignificantBits()).shiftLeft(64).add(BigInteger.valueOf(uuid.getLeastSignificantBits())); - } +public class BucketGenerator { + static String[] integerBucketsToPaths(int[] integerBuckets, + int bucketCount, + int facetCount, + String[] facetPathTemplates) { + if (integerBuckets.length != bucketCount) { + throw new IllegalArgumentException(); + } - public byte[] createBuckets(UUID uuid) { - return createBuckets(uuidToBigInteger(uuid)); + String[] paths = new String[facetCount]; + for (int facet = 0; facet < facetCount; facet++) { + int bucketValue = integerBuckets[facet]; + paths[facet] = generatePath(facetPathTemplates[facet], bucketValue); + } + return paths; } /** @@ -47,28 +41,10 @@ public byte[] createBuckets(UUID uuid) { *

* Assumption: the value of hash is (much) larger than `16^bucketSizes.length` and uniformly distributed (random) * - * @param hash Any BigInteger that is to be split up in buckets according to the bucket configuration #bucketSizes. + * @param input Any BigInteger that is to be split up in buckets according to the bucket configuration #bucketSizes. * @return buckets The distributed hash */ - public byte[] createBuckets(BigInteger hash) { - int currentBucket = 0; - byte[] ret = new byte[this.bucketSizes.length]; - - while (currentBucket < this.bucketSizes.length) { - BigInteger[] divisorReminder = hash.divideAndRemainder(BigInteger.valueOf(bucketSizes[currentBucket])); - - hash = divisorReminder[0]; - long reminder = divisorReminder[1].longValue(); - - ret[currentBucket] = (byte) Math.abs(reminder % bucketSizes[currentBucket]); - - currentBucket += 1; - } - - return ret; - } - - public int[] createIntegerBuckets(BigInteger input) { + static int[] createBuckets(BigInteger input, int[] bucketSizes) { int currentBucket = 0; int[] result = new int[bucketSizes.length]; while (currentBucket < bucketSizes.length) { @@ -80,4 +56,8 @@ public int[] createIntegerBuckets(BigInteger input) { } return result; } -} + + private static String generatePath(String facetPathTemplate, int bucketValue) { + return facetPathTemplate.replaceAll("NUM", String.format("%02d", bucketValue)); + } +} \ No newline at end of file diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java index 46e09544ad..5b2be5a5c4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java @@ -46,18 +46,20 @@ private static Image getImage(ByteArray pubKeyHash, boolean useCache) { return CACHE.get(pubKeyHash); } BigInteger bigInteger = new BigInteger(pubKeyHash.getBytes()); - Configuration configuration = new Configuration(); - VariableSizeHashing hashing = new VariableSizeHashing(configuration.getBucketSizes()); - int[] integerBuckets = hashing.createIntegerBuckets(bigInteger); - Image image = imageFromIntegerBuckets(integerBuckets, configuration); + int[] buckets = BucketGenerator.createBuckets(bigInteger, Configuration.getBucketSizes()); + Image image = imageFromBuckets(buckets); if (useCache && CACHE.size() < MAX_CACHE_SIZE) { CACHE.put(pubKeyHash, image); } return image; } - private static Image imageFromIntegerBuckets(int[] integerBuckets, Configuration configuration) { - String[] paths = configuration.integerBucketsToPaths(integerBuckets); + + private static Image imageFromBuckets(int[] integerBuckets) { + String[] paths = BucketGenerator.integerBucketsToPaths(integerBuckets, + Configuration.getBucketCount(), + Configuration.getFacetCount(), + Configuration.getFacetPathTemplates()); return ImageUtil.composeImage(paths, SIZE, SIZE); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java index 734c227459..abac55e6fe 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java @@ -21,25 +21,25 @@ @Slf4j public class Configuration { - private final static String ROOT = ""; + private static final String ROOT = ""; - private static final int BG0_COUNT = 15; - private static final int BG1_COUNT = 15; - private static final int EARS0_COUNT = 15; - private static final int EARS1_COUNT = 2; - private static final int FACE0_COUNT = 15; - private static final int FACE1_COUNT = 8; - private static final int EYES0_COUNT = 15; - private static final int NOSE0_COUNT = 5; - private static final int WHISKERS0_COUNT = 6; + private static final int BG0_COUNT = 16; + private static final int BG1_COUNT = 16; + private static final int EARS0_COUNT = 16; + private static final int EARS1_COUNT = 3; + private static final int FACE0_COUNT = 16; + private static final int FACE1_COUNT = 9; + private static final int EYES0_COUNT = 16; + private static final int NOSE0_COUNT = 6; + private static final int WHISKERS0_COUNT = 7; - private final static int BUCKET_COUNT = 9; - private final static int FACET_COUNT = 9; + private static final int BUCKET_COUNT = 9; + private static final int FACET_COUNT = 9; - private final static byte[] BUCKET_SIZES = new byte[]{BG0_COUNT, BG1_COUNT, EARS0_COUNT, EARS1_COUNT, FACE0_COUNT, + private static final int[] BUCKET_SIZES = new int[]{BG0_COUNT, BG1_COUNT, EARS0_COUNT, EARS1_COUNT, FACE0_COUNT, FACE1_COUNT, EYES0_COUNT, NOSE0_COUNT, WHISKERS0_COUNT}; - private final static String[] FACET_PATH_TEMPLATES; + private static final String[] FACET_PATH_TEMPLATES; static { String postFix = ".png"; @@ -56,24 +56,19 @@ public class Configuration { }; } - String[] integerBucketsToPaths(int[] integerBuckets) { - if (integerBuckets.length != BUCKET_COUNT) { - throw new IllegalArgumentException(); - } + static int getBucketCount() { + return BUCKET_COUNT; + } - String[] paths = new String[FACET_COUNT]; - for (int facet = 0; facet < FACET_COUNT; facet++) { - int bucketValue = integerBuckets[facet]; - paths[facet] = generatePath(FACET_PATH_TEMPLATES[facet], bucketValue); - } - return paths; + static int getFacetCount() { + return FACET_COUNT; } - byte[] getBucketSizes() { + static int[] getBucketSizes() { return BUCKET_SIZES; } - private String generatePath(String facetPathTemplate, int bucketValue) { - return facetPathTemplate.replaceAll("NUM", String.format("%02d", bucketValue)); + static String[] getFacetPathTemplates() { + return FACET_PATH_TEMPLATES; } } From 49cb0f083586c343e079adbf2c3f8c4249038565 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 28 Feb 2024 09:06:02 +0700 Subject: [PATCH 6/7] Cleanups, Renamings --- .../{Configuration.java => BucketConfig.java} | 51 +++++++-------- .../components/cathash/BucketEncoder.java | 53 ++++++++++++++++ .../components/cathash/BucketGenerator.java | 63 ------------------- .../desktop/components/cathash/CatHash.java | 17 ++--- .../main/java/bisq/common/util/MathUtils.java | 4 ++ 5 files changed, 87 insertions(+), 101 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/{Configuration.java => BucketConfig.java} (60%) create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketEncoder.java delete mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketGenerator.java diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketConfig.java similarity index 60% rename from apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketConfig.java index abac55e6fe..a63f008649 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/Configuration.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketConfig.java @@ -17,11 +17,12 @@ package bisq.desktop.components.cathash; +import bisq.common.util.MathUtils; import lombok.extern.slf4j.Slf4j; @Slf4j -public class Configuration { - private static final String ROOT = ""; +public class BucketConfig { + static final String DIGIT = "#"; private static final int BG0_COUNT = 16; private static final int BG1_COUNT = 16; @@ -33,42 +34,42 @@ public class Configuration { private static final int NOSE0_COUNT = 6; private static final int WHISKERS0_COUNT = 7; - private static final int BUCKET_COUNT = 9; - private static final int FACET_COUNT = 9; - private static final int[] BUCKET_SIZES = new int[]{BG0_COUNT, BG1_COUNT, EARS0_COUNT, EARS1_COUNT, FACE0_COUNT, FACE1_COUNT, EYES0_COUNT, NOSE0_COUNT, WHISKERS0_COUNT}; - private static final String[] FACET_PATH_TEMPLATES; + private static final String[] PATH_TEMPLATES; static { String postFix = ".png"; - FACET_PATH_TEMPLATES = new String[]{ - ROOT + "bg0/NUM" + postFix, - ROOT + "bg1/NUM" + postFix, - ROOT + "ears0/NUM" + postFix, - ROOT + "ears1/NUM" + postFix, - ROOT + "face0/NUM" + postFix, - ROOT + "face1/NUM" + postFix, - ROOT + "eyes0/NUM" + postFix, - ROOT + "nose0/NUM" + postFix, - ROOT + "whiskers0/NUM" + postFix, + PATH_TEMPLATES = new String[]{ + "bg0/" + DIGIT + postFix, + "bg1/" + DIGIT + postFix, + "ears0/" + DIGIT + postFix, + "ears1/" + DIGIT + postFix, + "face0/" + DIGIT + postFix, + "face1/" + DIGIT + postFix, + "eyes0/" + DIGIT + postFix, + "nose0/" + DIGIT + postFix, + "whiskers0/" + DIGIT + postFix }; - } - - static int getBucketCount() { - return BUCKET_COUNT; - } - static int getFacetCount() { - return FACET_COUNT; + long numCombinations = getNumCombinations(); + log.info("Number of combinations: 2^{} = {}", MathUtils.getLog2(numCombinations), numCombinations); } static int[] getBucketSizes() { return BUCKET_SIZES; } - static String[] getFacetPathTemplates() { - return FACET_PATH_TEMPLATES; + static String[] getPathTemplates() { + return PATH_TEMPLATES; + } + + static long getNumCombinations() { + long result = 1; + for (int bucketSize : BUCKET_SIZES) { + result *= bucketSize; + } + return result; } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketEncoder.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketEncoder.java new file mode 100644 index 0000000000..d6288e546e --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketEncoder.java @@ -0,0 +1,53 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.components.cathash; + +import java.math.BigInteger; + +public class BucketEncoder { + /** + * @param input A BigInteger input that is to be split up deterministically in buckets according to the bucketSizes array. + * @return buckets + */ + static int[] encode(BigInteger input, int[] bucketSizes) { + int currentBucket = 0; + int[] result = new int[bucketSizes.length]; + while (currentBucket < bucketSizes.length) { + int bucketSize = bucketSizes[currentBucket]; + BigInteger[] divisorReminder = input.divideAndRemainder(BigInteger.valueOf(bucketSize)); + input = divisorReminder[0]; + long reminder = divisorReminder[1].longValue(); + result[currentBucket] = (int) Math.abs(reminder % bucketSize); + currentBucket++; + } + return result; + } + + static String[] toPaths(int[] buckets, String[] pathTemplates) { + String[] paths = new String[buckets.length]; + for (int facet = 0; facet < buckets.length; facet++) { + int bucketValue = buckets[facet]; + paths[facet] = generatePath(pathTemplates[facet], bucketValue); + } + return paths; + } + + private static String generatePath(String pathTemplate, int index) { + return pathTemplate.replaceAll(BucketConfig.DIGIT, String.format("%02d", index)); + } +} \ No newline at end of file diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketGenerator.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketGenerator.java deleted file mode 100644 index 525a6da5f2..0000000000 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/BucketGenerator.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.components.cathash; - -import java.math.BigInteger; - -public class BucketGenerator { - static String[] integerBucketsToPaths(int[] integerBuckets, - int bucketCount, - int facetCount, - String[] facetPathTemplates) { - if (integerBuckets.length != bucketCount) { - throw new IllegalArgumentException(); - } - - String[] paths = new String[facetCount]; - for (int facet = 0; facet < facetCount; facet++) { - int bucketValue = integerBuckets[facet]; - paths[facet] = generatePath(facetPathTemplates[facet], bucketValue); - } - return paths; - } - - /** - * Takes the hash value and distributes it over the buckets. - *

- * Assumption: the value of hash is (much) larger than `16^bucketSizes.length` and uniformly distributed (random) - * - * @param input Any BigInteger that is to be split up in buckets according to the bucket configuration #bucketSizes. - * @return buckets The distributed hash - */ - static int[] createBuckets(BigInteger input, int[] bucketSizes) { - int currentBucket = 0; - int[] result = new int[bucketSizes.length]; - while (currentBucket < bucketSizes.length) { - BigInteger[] divisorReminder = input.divideAndRemainder(BigInteger.valueOf(bucketSizes[currentBucket])); - input = divisorReminder[0]; - long reminder = divisorReminder[1].longValue(); - result[currentBucket] = (int) Math.abs(reminder % bucketSizes[currentBucket]); - currentBucket++; - } - return result; - } - - private static String generatePath(String facetPathTemplate, int bucketValue) { - return facetPathTemplate.replaceAll("NUM", String.format("%02d", bucketValue)); - } -} \ No newline at end of file diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java index 5b2be5a5c4..ceb0849b1a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/cathash/CatHash.java @@ -26,7 +26,6 @@ import java.util.concurrent.ConcurrentHashMap; // Derived from https://github.com/neuhalje/android-robohash -// Number of combinations: 3 * 15 * 15 * 15 * 15 * 15 * 15 = 34171875 (2 ^ 25) @Slf4j public class CatHash { private static final int SIZE = 300; @@ -45,21 +44,13 @@ private static Image getImage(ByteArray pubKeyHash, boolean useCache) { if (useCache && CACHE.containsKey(pubKeyHash)) { return CACHE.get(pubKeyHash); } - BigInteger bigInteger = new BigInteger(pubKeyHash.getBytes()); - int[] buckets = BucketGenerator.createBuckets(bigInteger, Configuration.getBucketSizes()); - Image image = imageFromBuckets(buckets); + BigInteger input = new BigInteger(pubKeyHash.getBytes()); + int[] buckets = BucketEncoder.encode(input, BucketConfig.getBucketSizes()); + String[] paths = BucketEncoder.toPaths(buckets, BucketConfig.getPathTemplates()); + Image image = ImageUtil.composeImage(paths, SIZE, SIZE); if (useCache && CACHE.size() < MAX_CACHE_SIZE) { CACHE.put(pubKeyHash, image); } return image; } - - - private static Image imageFromBuckets(int[] integerBuckets) { - String[] paths = BucketGenerator.integerBucketsToPaths(integerBuckets, - Configuration.getBucketCount(), - Configuration.getFacetCount(), - Configuration.getFacetPathTemplates()); - return ImageUtil.composeImage(paths, SIZE, SIZE); - } } diff --git a/common/src/main/java/bisq/common/util/MathUtils.java b/common/src/main/java/bisq/common/util/MathUtils.java index da046d4177..16428cf4ec 100644 --- a/common/src/main/java/bisq/common/util/MathUtils.java +++ b/common/src/main/java/bisq/common/util/MathUtils.java @@ -108,4 +108,8 @@ public static long bounded(long lowerBound, long upperBound, long value) { "lowerBound must not be larger than upperBound"); return Math.min(Math.max(value, lowerBound), upperBound); } + + public static double getLog2(long value) { + return Math.log(value) / Math.log(2); + } } From 18dc6ee78dbdbfb600426c92a8c6373c86a35537 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 28 Feb 2024 19:18:54 +0700 Subject: [PATCH 7/7] Remove comment for dev testing --- .../onboarding/create_profile/CreateProfileController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java index 8ab20d28c6..27b3ab1ac1 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/overlay/onboarding/create_profile/CreateProfileController.java @@ -154,7 +154,7 @@ private CompletableFuture createProofOfWork(byte[] pubKeyHash) { .thenApply(proofOfWork -> { long powDuration = System.currentTimeMillis() - ts; log.info("Proof of work creation completed after {} ms", powDuration); - //createSimulatedDelay(powDuration); + createSimulatedDelay(powDuration); UIThread.run(() -> { model.setProofOfWork(Optional.of(proofOfWork)); String nym = NymIdGenerator.fromHash(pubKeyHash);