From aeaa14167d9b5359db1d0b1ce70a8c8b4799e4d7 Mon Sep 17 00:00:00 2001 From: Hellblazer Date: Sun, 2 Jun 2024 14:26:20 -0700 Subject: [PATCH] clean up, slice iterator traversal now copy on write array, cleaner Bootstrapper --- .../apollo/choam/support/Bootstrapper.java | 62 +++--- .../apollo/cryptography/HexBloom.java | 188 +++++------------- .../salesforce/apollo/ring/SliceIterator.java | 8 +- 3 files changed, 87 insertions(+), 171 deletions(-) diff --git a/choam/src/main/java/com/salesforce/apollo/choam/support/Bootstrapper.java b/choam/src/main/java/com/salesforce/apollo/choam/support/Bootstrapper.java index 8c59d987d..35155358c 100644 --- a/choam/src/main/java/com/salesforce/apollo/choam/support/Bootstrapper.java +++ b/choam/src/main/java/com/salesforce/apollo/choam/support/Bootstrapper.java @@ -90,10 +90,13 @@ private void anchor(AtomicReference start, ULong end) { } final var randomCut = params.digestAlgorithm().random(); log.trace("Anchoring from: {} to: {} cut: {} on: {}", start.get(), end, randomCut, params.member().getId()); - new RingIterator<>(params.gossipDuration(), params.context(), params.member(), comms, true, scheduler).iterate( - randomCut, (link, ring) -> anchor(link, start, end), - (tally, futureSailor, destination) -> completeAnchor(futureSailor, start, end, destination), - t -> scheduleAnchorCompletion(start, end)); + var iterator = new RingIterator(params.gossipDuration(), params.context(), params.member(), + comms, true, scheduler); + iterator.ignoreSelf(); + iterator.noDuplicates(); + iterator.iterate(randomCut, (link, ring) -> anchor(link, start, end), + (tally, futureSailor, destination) -> completeAnchor(futureSailor, start, end, destination), + t -> scheduleAnchorCompletion(start, end)); } private Blocks anchor(Terminal link, AtomicReference start, ULong end) { @@ -122,6 +125,7 @@ private void checkpointCompletion(int threshold, Initial mostRecent) { assert !checkpointView.height() .equals(Unsigned.ulong(0)) : "Should not attempt when bootstrapping from genesis"; var crown = HexBloom.from(checkpoint.block.getCheckpoint().getCrown()); + assert !crown.crowns().isEmpty() : "Crowns should not be empty"; log.info("Assembling from checkpoint: {}:{} crown: {} last cp: {} on: {}", checkpoint.height(), checkpoint.hash, crown.compactWrapped(), Digest.from(checkpoint.block.getHeader().getLastCheckpointHash()), params.member().getId()); @@ -190,10 +194,13 @@ private boolean completeAnchor(Optional futureSailor, AtomicReference
    start, ULong end) { - new RingIterator<>(params.gossipDuration(), params.context(), params.member(), scheduler, comms).iterate( - params.digestAlgorithm().random(), (link, ring) -> completeViewChain(link, start, end), - (tally, result, destination) -> completeViewChain(result, start, end, destination), - t -> scheduleViewChainCompletion(start, end)); + var iterator = new RingIterator(params.gossipDuration(), params.context(), params.member(), + scheduler, comms); + iterator.noDuplicates(); + iterator.ignoreSelf(); + iterator.iterate(params.digestAlgorithm().random(), (link, ring) -> completeViewChain(link, start, end), + (tally, result, destination) -> completeViewChain(result, start, end, destination), + t -> scheduleViewChainCompletion(start, end)); } private boolean completeViewChain(Optional futureSailor, AtomicReference start, ULong end, @@ -255,33 +262,15 @@ private void computeGenesis(Map votes) { // If restoring from known genesis... .filter(e -> genesis == null || genesis.hash.equals(e.getKey())) .filter(e -> { - if (e.getValue().hasGenesis()) { - if (lastCheckpoint != null - && lastCheckpoint.compareTo(ULong.valueOf(0)) > 0) { - log.trace( - "Rejecting genesis from: {} last checkpoint: {} > 0 on: {}", - e.getKey(), lastCheckpoint, params.member().getId()); - return false; - } - log.trace("Accepting genesis from: {} on: {}", e.getKey(), - params.member().getId()); - return true; - } - if (!e.getValue().hasCheckpoint()) { - log.trace( - "Rejecting: {} has no checkpoint. last checkpoint from: {} > 0 on: {}", - e.getKey(), lastCheckpoint, params.member().getId()); + if (lastCheckpoint != null + && lastCheckpoint.compareTo(ULong.valueOf(0)) > 0) { + log.trace("Rejecting genesis from: {} last checkpoint: {} > 0 on: {}", + e.getKey(), lastCheckpoint, params.member().getId()); return false; } - - ULong checkpointViewHeight = HashedBlock.height( - e.getValue().getCheckpointView().getBlock()); - ULong recordedCheckpointViewHeight = ULong.valueOf( - e.getValue().getCheckpoint().getBlock().getHeader().getLastReconfig()); - // checkpoint's view should match - log.trace("Accepting checkpoint from: {} on: {}", e.getKey(), + log.trace("Accepting genesis from: {} on: {}", e.getKey(), params.member().getId()); - return checkpointViewHeight.equals(recordedCheckpointViewHeight); + return true; }) .peek(e -> tally.add(new HashedCertifiedBlock(params.digestAlgorithm(), e.getValue().getGenesis()))) @@ -354,15 +343,15 @@ private void computeGenesis(Map votes) { checkpointView == null ? genesis.hash : checkpoint.hash, params.member().getId()); sync.complete(new SynchronizedState(genesis, checkpointView, checkpoint, checkpointState)); } else { - log.error("Failed synchronizing to {} from: {} last view: {} on: {}", genesis.hash, + log.error("Failed synchronizing to: {} from: {} last view: {} on: {}", genesis.hash, checkpoint == null ? genesis.hash : checkpoint.hash, - checkpointView == null ? genesis.hash : checkpoint.hash, t); + checkpointView == null ? genesis.hash : checkpoint.hash, params.member().getId(), t); sync.completeExceptionally(t); } }).exceptionally(t -> { - log.error("Failed synchronizing to {} from: {} last view: {} on: {}", genesis.hash, + log.error("Failed synchronizing to: {} from: {} last view: {} on: {}", genesis.hash, checkpoint == null ? genesis.hash : checkpoint.hash, - checkpointView == null ? genesis.hash : checkpoint.hash, t); + checkpointView == null ? genesis.hash : checkpoint.hash, params.member().getId(), t); sync.completeExceptionally(t); return null; }); @@ -389,6 +378,7 @@ private void sample() { var iterator = new RingIterator<>(params.gossipDuration(), params.context(), params.member(), comms, true, scheduler); iterator.allowDuplicates(); + iterator.ignoreSelf(); iterator.iterate(randomCut, (link, _) -> synchronize(s, link), (_, futureSailor, destination) -> synchronize(futureSailor, votes, destination), t -> computeGenesis(votes)); diff --git a/cryptography/src/main/java/com/salesforce/apollo/cryptography/HexBloom.java b/cryptography/src/main/java/com/salesforce/apollo/cryptography/HexBloom.java index c2480c1fe..f6f29f885 100644 --- a/cryptography/src/main/java/com/salesforce/apollo/cryptography/HexBloom.java +++ b/cryptography/src/main/java/com/salesforce/apollo/cryptography/HexBloom.java @@ -27,7 +27,6 @@ */ public class HexBloom { - public static final double DEFAULT_FPR = 0.0001; public static final long DEFAULT_SEED = Primes.PRIMES[666]; private static final Function IDENTITY = d -> d; private static final int MINIMUM_BFF_CARD = 100; @@ -41,29 +40,30 @@ public HexBloom(Digest initial, int count) { var hashes = hashes(count); crowns = new Digest[count]; cardinality = 0; - membership = new BloomFilter.DigestBloomFilter(0x666, MINIMUM_BFF_CARD, DEFAULT_FPR); + var cardinality = Math.max(MINIMUM_BFF_CARD, count); + membership = new BloomFilter.DigestBloomFilter(0x666, MINIMUM_BFF_CARD, 1.0 / (double) cardinality); for (int i = 0; i < crowns.length; i++) { crowns[i] = hashes.get(i).apply(initial); } } public HexBloom(Digest initial, List> hashes) { - assert hashes.size() > 0; + assert !hashes.isEmpty(); crowns = new Digest[hashes.size()]; cardinality = 0; - membership = new BloomFilter.DigestBloomFilter(0x666, MINIMUM_BFF_CARD, DEFAULT_FPR); + membership = new BloomFilter.DigestBloomFilter(0x666, MINIMUM_BFF_CARD, 1.0 / (double) MINIMUM_BFF_CARD); for (int i = 0; i < crowns.length; i++) { crowns[i] = hashes.get(i).apply(initial); } } public HexBloom(HexBloome hb) { - this(hb.getCardinality(), hb.getCrownsList().stream().map(d -> Digest.from(d)).toList(), + this(hb.getCardinality(), hb.getCrownsList().stream().map(Digest::from).toList(), BloomFilter.from(hb.getMembership())); } public HexBloom(int cardinality, List crowns, BloomFilter membership) { - assert crowns.size() > 0; + assert !crowns.isEmpty(); this.crowns = new Digest[crowns.size()]; for (int i = 0; i < crowns.size(); i++) { this.crowns[i] = crowns.get(i); @@ -90,7 +90,7 @@ public HexBloom(Digest initial) { * @return the HexBloom built according to spec */ public static HexBloom construct(int count, Stream digests, Digest initialCrown, int crowns) { - return construct(count, digests, Collections.emptyList(), hashes(crowns), initialCrown, DEFAULT_FPR); + return construct(count, digests, Collections.emptyList(), hashes(crowns), initialCrown); } /** @@ -105,7 +105,7 @@ public static HexBloom construct(int count, Stream digests, Digest initi */ public static HexBloom construct(int currentCount, Stream currentMembership, List added, Digest initialCrown, int count) { - return construct(currentCount, currentMembership, added, hashes(count), initialCrown, DEFAULT_FPR); + return construct(currentCount, currentMembership, added, hashes(count), initialCrown); } /** @@ -120,7 +120,7 @@ public static HexBloom construct(int currentCount, Stream currentMembers */ public static HexBloom construct(int currentCount, Stream currentMembership, List added, List crowns, List removed) { - return construct(currentCount, currentMembership, added, crowns, removed, hashes(crowns.size()), DEFAULT_FPR); + return construct(currentCount, currentMembership, added, crowns, removed, hashes(crowns.size())); } /** @@ -132,33 +132,32 @@ public static HexBloom construct(int currentCount, Stream currentMembers * @param crowns - the current crown state corresponding to the currentMembership * @param removed - digests removed that are present in the currentMembership list * @param hashes - the list of functions for computing the hash of a digest for a given crown - * @param fpr - desired false positive rate for membership bloomfilter * @return the HexBloom representing the new state */ public static HexBloom construct(int currentCount, Stream currentMembership, List added, - List crowns, List removed, List> hashes, - double fpr) { - assert crowns.size() > 0; + List crowns, List removed, List> hashes) { + assert !crowns.isEmpty(); if (hashes.size() != crowns.size()) { throw new IllegalArgumentException( "Size of supplied hash functions: " + hashes.size() + " must equal the # of crowns: " + crowns.size()); } var cardinality = currentCount + added.size() - removed.size(); - var membership = new BloomFilter.DigestBloomFilter(DEFAULT_SEED, Math.max(MINIMUM_BFF_CARD, cardinality), fpr); - var crwns = crowns.stream().map(d -> new AtomicReference<>(d)).toList(); + var n = Math.max(MINIMUM_BFF_CARD, cardinality); + var membership = new BloomFilter.DigestBloomFilter(DEFAULT_SEED, n, 1.0 / (double) n); + var crwns = crowns.stream().map(AtomicReference::new).toList(); added.forEach(d -> { for (int i = 0; i < crwns.size(); i++) { - crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), (a, b) -> a.xor(b)); + crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), Digest::xor); } membership.add(d); }); removed.forEach(d -> { for (int i = 0; i < crwns.size(); i++) { - crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), (a, b) -> a.xor(b)); + crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), Digest::xor); } }); - currentMembership.forEach(d -> membership.add(d)); - return new HexBloom(cardinality, crwns.stream().map(ad -> ad.get()).toList(), membership); + currentMembership.forEach(membership::add); + return new HexBloom(cardinality, crwns.stream().map(AtomicReference::get).toList(), membership); } /** @@ -169,81 +168,32 @@ public static HexBloom construct(int currentCount, Stream currentMembers * @param added - the added member digests * @param hashes - the supplied crown hash functions * @param initialCrown - the initial value of the crowns - * @param fpr - the false positive rate for the membership bloom filter * @return the HexBloom built according to spec */ public static HexBloom construct(int currentCount, Stream currentMembership, List added, - List> hashes, Digest initialCrown, double fpr) { - assert hashes.size() > 0; + List> hashes, Digest initialCrown) { + assert !hashes.isEmpty(); var cardinality = currentCount + added.size(); - var membership = new BloomFilter.DigestBloomFilter(DEFAULT_SEED, Math.max(MINIMUM_BFF_CARD, cardinality), fpr); + var n = Math.max(MINIMUM_BFF_CARD, cardinality); + var membership = new BloomFilter.DigestBloomFilter(DEFAULT_SEED, n, 1.0 / (double) n); - var crwns = IntStream.range(0, hashes.size()) - .mapToObj(i -> hashes.get(i).apply(initialCrown)) - .map(d -> new AtomicReference<>(d)) - .toList(); + var crwns = hashes.stream().map(hash -> hash.apply(initialCrown)).map(AtomicReference::new).toList(); currentMembership.forEach(d -> { for (int i = 0; i < crwns.size(); i++) { - crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), (a, b) -> a.xor(b)); + crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), Digest::xor); } membership.add(d); }); added.forEach(d -> { for (int i = 0; i < crwns.size(); i++) { - crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), (a, b) -> a.xor(b)); + crwns.get(i).accumulateAndGet(hashes.get(i).apply(d), Digest::xor); } membership.add(d); }); return new HexBloom(cardinality, crwns.stream().map(ad -> ad.get()).toList(), membership); } - /** - * Construct a HexBloom with a membership bloomfilter using the default false positive rate and the default hash - * transforms for each crown - * - * @param currentMembership - list of digests that correspond to the supplied crowns - * @param added - digests added that are not present in the currentMembership list - * @param crowns - the current crown state corresponding to the currentMembership - * @param removed - digests removed that are present in the currentMembership list - * @return the HexBloom representing the new state - */ - public static HexBloom construct(List currentMembership, List added, List crowns, - List removed) { - return construct(currentMembership, added, crowns, removed, hashes(crowns.size()), DEFAULT_FPR); - } - - /** - * Construct a HexBloom with a membership bloomfilter using the default false positive rate - * - * @param currentMembership - list of digests that correspond to the supplied crowns - * @param added - digests added that are not present in the currentMembership list - * @param crowns - the current crown state corresponding to the currentMembership - * @param removed - digests removed that are present in the currentMembership list - * @param hashes - the list of functions for computing the hash of a digest for a given crown - * @return the HexBloom representing the new state - */ - public static HexBloom construct(List currentMembership, List added, List crowns, - List removed, List> hashes) { - return construct(currentMembership, added, crowns, removed, hashes, DEFAULT_FPR); - } - - /** - * Construct a HexBloom. - * - * @param currentMembership - list of digests that correspond to the supplied crowns - * @param added - digests added that are not present in the currentMembership list - * @param crowns - the current crown state corresponding to the currentMembership - * @param removed - digests removed that are present in the currentMembership list - * @param hashes - the list of functions for computing the hash of a digest for a given crown - * @param fpr - desired false positive rate for membership bloomfilter - * @return the HexBloom representing the new state - */ - public static HexBloom construct(List currentMembership, List added, List crowns, - List removed, List> hashes, double fpr) { - return construct(currentMembership.size(), currentMembership.stream(), added, crowns, removed, hashes, fpr); - } - public static HexBloom from(HexBloome hb) { assert !HexBloome.getDefaultInstance().equals(hb); return new HexBloom(hb); @@ -252,7 +202,6 @@ public static HexBloom from(HexBloome hb) { /** * Answer the default hash for a crown positiion * - * @param index * @return the hash transform */ public static Function hash(int index) { @@ -261,19 +210,13 @@ public static Function hash(int index) { /** * Answer the default hash transforms for the number of crowns - * - * @param crowns - * @return */ public static List> hashes(int crowns) { - return IntStream.range(0, crowns).mapToObj(i -> hash(i)).toList(); + return IntStream.range(0, crowns).mapToObj(HexBloom::hash).toList(); } /** * Answer the default wrapping hash for a crown positiion - * - * @param index - * @return the wrapping hash transform */ public static Function hashWrap(int index) { return d -> d.prefix(index); @@ -281,12 +224,9 @@ public static Function hashWrap(int index) { /** * Answer the default wrapping hash transforms for the number of crowns - * - * @param crowns - * @return */ public static List> hashWraps(int crowns) { - return IntStream.range(0, crowns).mapToObj(i -> hashWrap(i)).toList(); + return IntStream.range(0, crowns).mapToObj(HexBloom::hashWrap).toList(); } public HexBloom add(Digest d, List> hashes) { @@ -312,7 +252,7 @@ public Digest compact() { if (crowns.length == 1) { return crowns[0]; } - return Arrays.asList(crowns).stream().reduce(crowns[0].getAlgorithm().getOrigin(), (a, b) -> a.xor(b)); + return Arrays.stream(crowns).reduce(crowns[0].getAlgorithm().getOrigin(), Digest::xor); } /** @@ -334,7 +274,7 @@ public Digest compactWrapped(List> hashes) { .mapToObj(i -> hashes.get(i).apply(crowns[i])) .toList() .stream() - .reduce(crowns[0].getAlgorithm().getOrigin(), (a, b) -> a.xor(b)); + .reduce(crowns[0].getAlgorithm().getOrigin(), Digest::xor); } public boolean contains(Digest digest) { @@ -370,9 +310,6 @@ public HexBloome toHexBloome() { /** * Answer the serialized receiver, with crowns hashed using the supplied hashes functions - * - * @param hashes - * @return */ public HexBloome toHexBloome(List> hashes) { if (hashes.size() != crowns.length) { @@ -388,8 +325,6 @@ public HexBloome toHexBloome(List> hashes) { /** * Answer the serialized receiver, using no transformation on the crowns - * - * @return */ public HexBloome toIdentityHexBloome() { return toHexBloome(IntStream.range(0, crowns.length).mapToObj(i -> IDENTITY).toList()); @@ -401,8 +336,8 @@ public String toString() { } /** - * Validate that the supplied members matches the receiver's crowns. All members must be included in the membership - * bloomfilter, all calculated crowns must match and the cardinality must match. + * Validate that the supplied members match the receiver's crowns. All members must be included in the membership + * bloomfilter, all calculated crowns must match, and the cardinality must match. * * @param members - list of member digests * @return true if validated @@ -412,8 +347,8 @@ public boolean validate(List members) { } /** - * Validate that the supplied members matches the receiver's crowns. All members must be included in the membership - * bloomfilter, all calculated crowns must match and the cardinality must match. + * Validate that the supplied members match the receiver's crowns. All members must be included in the membership + * bloomfilter, all calculated crowns must match, and the cardinality must match. * * @param members - lsit of member digests * @param hashes - hash functions for computing crowns @@ -424,8 +359,8 @@ public boolean validate(List members, List> has } /** - * Validate that the supplied members matches the receiver's crowns. All members must be included in the membership - * bloomfilter, all calculated crowns must match and the cardinality must match. + * Validate that the supplied members match the receiver's crowns. All members must be included in the membership + * bloomfilter, all calculated crowns must match, and the cardinality must match. * * @param members - stream of member digests * @return true if validated @@ -435,8 +370,8 @@ public boolean validate(Stream members) { } /** - * Validate that the supplied members matches the receiver's crowns. All members must be included in the membership - * bloomfilter, all calculated crowns must match and the cardinality must match. + * Validate that the supplied members match the receiver's crowns. All members must be included in the membership + * bloomfilter, all calculated crowns must match, and the cardinality must match. * * @param members - stream of member digests * @param hashes - hash functions for computing crowns @@ -448,12 +383,12 @@ public boolean validate(Stream members, List> h "Size of supplied hash functions: " + hashes.size() + " must equal the # of crowns: " + crowns.length); } var count = new AtomicInteger(); - var calculated = IntStream.range(0, crowns.length) - .mapToObj(i -> new AtomicReference(crowns[i].getAlgorithm().getOrigin())) - .toList(); + var calculated = Arrays.stream(crowns) + .map(crown -> new AtomicReference(crown.getAlgorithm().getOrigin())) + .toList(); members.forEach(d -> { for (int i = 0; i < crowns.length; i++) { - calculated.get(i).accumulateAndGet(hashes.get(i).apply(d), (a, b) -> a.xor(b)); + calculated.get(i).accumulateAndGet(hashes.get(i).apply(d), Digest::xor); } count.incrementAndGet(); }); @@ -526,30 +461,20 @@ public static class Accumulator { protected final List> hashes; protected int currentCount = 0; - public Accumulator(int cardinality, int crowns, Digest initial, double fpr) { - this(cardinality, hashes(crowns), initial, fpr); - } - - public Accumulator(int cardinality, List> crownHashes, Digest initial, double fpr) { + public Accumulator(int cardinality, List> crownHashes, Digest initial) { if (cardinality < 0) { throw new IllegalArgumentException(("Cardinality must be >= 0")); } if (crownHashes == null || crownHashes.isEmpty()) { throw new IllegalArgumentException("Crown hashes must not be null or empty"); } - if (fpr <= 0) { - throw new IllegalArgumentException("False positive rate must be > 0"); - } this.cardinality = cardinality; this.hashes = crownHashes; - accumulators = IntStream.range(0, hashes.size()) - .mapToObj(i -> hashes.get(i).apply(initial)) - .map(d -> new AtomicReference<>(d)) - .toList(); + accumulators = hashes.stream().map(hash -> hash.apply(initial)).map(AtomicReference::new).toList(); } public Accumulator(int cardinality, int crowns, Digest initial) { - this(cardinality, crowns, initial, DEFAULT_FPR); + this(cardinality, hashes(crowns), initial); } public void add(Digest digest) { @@ -558,7 +483,7 @@ public void add(Digest digest) { } currentCount++; for (int i = 0; i < accumulators.size(); i++) { - accumulators.get(i).accumulateAndGet(hashes.get(i).apply(digest), (a, b) -> a.xor(b)); + accumulators.get(i).accumulateAndGet(hashes.get(i).apply(digest), Digest::xor); } } @@ -571,12 +496,12 @@ public Digest compactWrapped(List> hashes) { "Size of supplied hash functions: " + hashes.size() + " must equal the # of crowns: " + accumulators.size()); } - var algorithm = accumulators.get(0).get().getAlgorithm(); + var algorithm = accumulators.getFirst().get().getAlgorithm(); return IntStream.range(0, accumulators.size()) .mapToObj(i -> hashes.get(i).apply(accumulators.get(i).get())) .toList() .stream() - .reduce(algorithm.getOrigin(), (a, b) -> a.xor(b)); + .reduce(algorithm.getOrigin(), Digest::xor); } /** @@ -587,7 +512,7 @@ public Digest compactWrapped() { } public List crowns() { - return accumulators.stream().map(ar -> ar.get()).toList(); + return accumulators.stream().map(AtomicReference::get).toList(); } public List wrappedCrowns() { @@ -604,17 +529,14 @@ public List wrappedCrowns(List> wrapingHash) { public static class HexAccumulator extends Accumulator { private final BloomFilter membership; - public HexAccumulator(int cardinality, int crowns, Digest initial, double fpr) { - this(cardinality, hashes(crowns), initial, fpr); - } - - public HexAccumulator(int cardinality, List> crownHashes, Digest initial, double fpr) { - super(cardinality, crownHashes, initial, fpr); - membership = new BloomFilter.DigestBloomFilter(DEFAULT_SEED, Math.max(MINIMUM_BFF_CARD, cardinality), fpr); + public HexAccumulator(int cardinality, int crowns, Digest initial) { + this(cardinality, hashes(crowns), initial); } - public HexAccumulator(int cardinality, int crowns, Digest initial) { - this(cardinality, crowns, initial, DEFAULT_FPR); + public HexAccumulator(int cardinality, List> crownHashes, Digest initial) { + super(cardinality, crownHashes, initial); + var n = Math.max(MINIMUM_BFF_CARD, cardinality); + membership = new BloomFilter.DigestBloomFilter(DEFAULT_SEED, n, 1.0 / (double) n); } @Override @@ -625,7 +547,7 @@ public void add(Digest digest) { public HexBloom build() { assert currentCount == cardinality : "Did not add all members, missing: " + (cardinality - currentCount); - return new HexBloom(cardinality, accumulators.stream().map(ar -> ar.get()).toList(), membership); + return new HexBloom(cardinality, accumulators.stream().map(AtomicReference::get).toList(), membership); } } } diff --git a/memberships/src/main/java/com/salesforce/apollo/ring/SliceIterator.java b/memberships/src/main/java/com/salesforce/apollo/ring/SliceIterator.java index 15fc27209..4b255669c 100644 --- a/memberships/src/main/java/com/salesforce/apollo/ring/SliceIterator.java +++ b/memberships/src/main/java/com/salesforce/apollo/ring/SliceIterator.java @@ -18,7 +18,11 @@ import java.io.IOException; import java.time.Duration; -import java.util.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -49,7 +53,7 @@ public SliceIterator(String label, SigningMember member, Collection(s); + this.slice = new CopyOnWriteArrayList<>(s); this.comm = comm; this.scheduler = scheduler; Entropy.secureShuffle(this.slice);