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); + } }