Skip to content

Commit

Permalink
bootstrap optimization for context majority
Browse files Browse the repository at this point in the history
  • Loading branch information
Hellblazer committed Jan 15, 2024
1 parent b827f0c commit d0e37f7
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public Void answer(InvocationOnMock invocation) {
var invitation = gorgoneionClient.apply(Duration.ofSeconds(2_000));
assertNotNull(invitation);
assertNotEquals(Validations.getDefaultInstance(), invitation);
assertTrue(invitation.getValidationsCount() >= context.majority());
assertTrue(invitation.getValidationsCount() >= context.majority(true));

assertTrue(countdown.await(1, TimeUnit.SECONDS));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ private SignedNonce generateNonce(KERL_ application) {
var successors = context.totalCount() == 1 ? Collections.singletonList(member)
: Context.uniqueSuccessors(context, digestOf(ident,
parameters.digestAlgorithm()));
final var majority = context.totalCount() == 1 && bootstrap ? 1 : context.majority();
final var majority = context.majority(true);
final var redirecting = new SliceIterator<>("Nonce Endorsement", member, successors, endorsementComm);
Set<MemberSignature> endorsements = Collections.newSetFromMap(new ConcurrentHashMap<>());
var generated = new CompletableFuture<SignedNonce>();
Expand All @@ -171,8 +171,10 @@ private SignedNonce generateNonce(KERL_ application) {
return link.endorse(nonce, parameters.registrationTimeout());
}, (futureSailor, link, m) -> completeEndorsement(futureSailor, m, endorsements), () -> {
if (endorsements.size() < majority) {
generated.completeExceptionally(new StatusRuntimeException(
Status.ABORTED.withDescription("Cannot gather required nonce endorsements")));
generated.completeExceptionally(new StatusRuntimeException(Status.ABORTED.withDescription(
"Cannot gather required nonce endorsements: %s required: %s on: %s".formatted(endorsements.size(),
majority,
member.getId()))));
} else {
generated.complete(SignedNonce.newBuilder()
.addSignatures(MemberSignature.newBuilder()
Expand All @@ -193,7 +195,7 @@ private SignedNonce generateNonce(KERL_ application) {
Thread.currentThread().interrupt();
return null;
} catch (ExecutionException e) {
throw new RuntimeException(e);
throw new RuntimeException(e.getCause());
}
}

Expand All @@ -219,7 +221,7 @@ private void notarize(Credentials credentials, Validations validations) {

var successors = Context.uniqueSuccessors(context,
digestOf(identifier.toIdent(), parameters.digestAlgorithm()));
final var majority = context.totalCount() == 1 && bootstrap ? 0 : context.majority();
final var majority = context.majority(true);
SliceIterator<Endorsement> redirecting = new SliceIterator<>("Enrollment", member, successors, endorsementComm);
var completed = new HashSet<Member>();
redirecting.iterate((link, m) -> {
Expand All @@ -246,7 +248,7 @@ private Validations register(Credentials request) {

var successors = Context.uniqueSuccessors(context,
digestOf(identifier.toIdent(), parameters.digestAlgorithm()));
final var majority = context.totalCount() == 1 && bootstrap ? 0 : context.majority();
final var majority = context.majority(true);
final var redirecting = new SliceIterator<>("Credential verification", member, successors, endorsementComm);
var verifications = new HashSet<Validation_>();
redirecting.iterate((link, m) -> {
Expand Down Expand Up @@ -461,7 +463,7 @@ private boolean validate(Notarization request, Identifier identifier, KERL_ kerl
}
}
// If there is only one active member in our context, it's us.
var majority = context.totalCount() == 1 && bootstrap ? 1 : context.majority();
var majority = context.majority(true);
if (count < majority) {
log.warn("Invalid notarization, no majority: {} required: {} for: {} from: {} on: {}", count,
majority, identifier, from, member.getId());
Expand Down Expand Up @@ -520,7 +522,7 @@ private boolean validateCredentials(Credentials credentials, Digest from) {
count++;
}

var majority = context.totalCount() == 1 && bootstrap ? 1 : context.majority();
var majority = context.majority(true);
if (count < majority) {
log.warn("Invalid credential nonce, no majority signature: {} required >= {} from: {} on: {}", count,
majority, from, member.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
public interface Context<T extends Member> {

double DEFAULT_EPSILON = 0.99999;
static final String RING_HASH_TEMPLATE = "%s-%s-%s";
String RING_HASH_TEMPLATE = "%s-%s-%s";

static Digest hashFor(Digest ctxId, int ring, Digest d) {
return d.prefix(ctxId, ring);
Expand Down Expand Up @@ -279,7 +279,17 @@ static List<Member> uniqueSuccessors(final Context<Member> context, Digest diges
/**
* Answer the majority cardinality of the context, based on the current ring count
*/
int majority();
default int majority() {
return majority(false);
}

/**
* Answer the majority cardinality of the context, based on the current ring count
*
* @param bootstrapped - if true, calculate correct majority for bootstrapping cases where totalCount < true
* majority
*/
int majority(boolean bootstrapped);

/**
* Answer the total member count (offline + active) tracked by this context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,9 @@ public boolean equals(Object obj) {
return false;
Context<?> other = (Context<?>) obj;
if (id == null) {
if (other.getId() != null)
return false;
} else if (!id.equals(other.getId()))
return false;
return true;
return other.getId() == null;
} else
return id.equals(other.getId());
}

@Override
Expand Down Expand Up @@ -341,6 +339,16 @@ public int majority() {
return getRingCount() - toleranceLevel();
}

@Override
public int majority(boolean bootstrapped) {
var majority = getRingCount() - toleranceLevel();
if (bootstrapped && totalCount() < 4) {
return Math.round(majority / 2);
} else {
return majority;
}
}

@Override
public int memberCount() {
return members.size();
Expand Down Expand Up @@ -701,9 +709,7 @@ public String toString() {

private void rebalance(int ringCount, ContextImpl<M> contextImpl) {
final var newHashes = new Digest[ringCount];
for (int i = 0; i < Math.min(ringCount, hashes.length); i++) {
newHashes[i] = hashes[i];
}
System.arraycopy(hashes, 0, newHashes, 0, Math.min(ringCount, hashes.length));
for (int i = Math.min(ringCount, hashes.length); i < newHashes.length; i++) {
newHashes[i] = contextImpl.hashFor(member.getId(), i);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.codahale.metrics.MetricRegistry;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.salesforce.apollo.test.proto.ByteMessage;
import com.salesforce.apollo.archipelago.LocalServer;
import com.salesforce.apollo.archipelago.Router;
import com.salesforce.apollo.archipelago.ServerConnectionCache;
Expand All @@ -27,6 +26,7 @@
import com.salesforce.apollo.stereotomy.StereotomyImpl;
import com.salesforce.apollo.stereotomy.mem.MemKERL;
import com.salesforce.apollo.stereotomy.mem.MemKeyStore;
import com.salesforce.apollo.test.proto.ByteMessage;
import com.salesforce.apollo.utils.Entropy;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -112,12 +112,13 @@ public void broadcast() throws Exception {
view.registerHandler(receiver);
receivers.put(view.getMember(), receiver);
}
System.out.println();
int rounds = LARGE_TESTS ? 100 : 5;
for (int r = 0; r < rounds; r++) {
CountDownLatch latch = new CountDownLatch(messengers.size());
round.set(latch);
var rnd = r;
System.out.print("\nround: %s ".formatted(r));
System.out.printf("\nround: %s ", r);
messengers.stream().forEach(view -> {
byte[] rand = new byte[32];
Entropy.nextSecureBytes(rand);
Expand Down
12 changes: 6 additions & 6 deletions thoth/src/main/java/com/salesforce/apollo/thoth/KerlDHT.java
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ private <T> void completeIt(CompletableFuture<T> result, HashMultiset<T> gathere
.max(Ordering.natural().onResultOf(Multiset.Entry::getCount))
.orElse(null);
if (max != null) {
if (max.getCount() >= context.majority()) {
if (max.getCount() >= context.majority(true)) {
try {
result.complete(max.getElement());
} catch (Throwable t) {
Expand All @@ -891,15 +891,15 @@ private <T> void completeIt(CompletableFuture<T> result, HashMultiset<T> gathere
}
}
result.completeExceptionally(new CompletionException(
"Unable to achieve majority, max: " + (max == null ? 0 : max.getCount()) + " required: " + context.majority()
+ " on: " + member.getId()));
"Unable to achieve majority, max: " + (max == null ? 0 : max.getCount()) + " required: " + context.majority(
true) + " on: " + member.getId()));
}

private boolean failedMajority(CompletableFuture<?> result, int maxAgree) {
log.error("Unable to achieve majority read, max: {} required: {} on: {}", maxAgree, context.majority(),
log.error("Unable to achieve majority read, max: {} required: {} on: {}", maxAgree, context.majority(true),
member.getId());
return result.completeExceptionally(new CompletionException(
"Unable to achieve majority read, max: " + maxAgree + " required: " + context.majority() + " on: "
"Unable to achieve majority read, max: " + maxAgree + " required: " + context.majority(true) + " on: "
+ member.getId()));
}

Expand Down Expand Up @@ -976,7 +976,7 @@ private <T> boolean read(CompletableFuture<T> result, HashMultiset<T> gathered,
if (max != null) {
tally.set(max.getCount());
// If there is only one active member in our context, it's us.
final var majority = tally.get() >= (context.activeCount() == 1 ? 1 : context.majority());
final var majority = tally.get() >= context.majority(true);
if (majority) {
result.complete(max.getElement());
log.debug("Majority: {} achieved: {}: {} on: {}", max.getCount(), action, identifier, member.getId());
Expand Down
4 changes: 2 additions & 2 deletions thoth/src/main/java/com/salesforce/apollo/thoth/Maat.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ record validator(EstablishmentEvent validating, JohnHancock signature) {
r.validating.getIdentifier());
}
}
var validated = verified >= context.majority();
var validated = verified >= context.majority(true);

log.trace("Validated: {} valid: {} out of: {} required: {} for: {} ", validated, verified, mapped.size(),
ctx.majority(), event.getCoordinates());
ctx.majority(true), event.getCoordinates());
return validated;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,14 @@ public void before() throws Exception {
.mapToObj(i -> stereotomy.newIdentifier())
.collect(Collectors.toMap(controlled -> new ControlledIdentifierMember(controlled),
controlled -> controlled));
context = Context.<Member>newBuilder().setpByz(PBYZ).setCardinality(getCardinality()).build();
context = Context.newBuilder().setpByz(PBYZ).setCardinality(getCardinality()).build();
ConcurrentSkipListMap<Digest, Member> serverMembers = new ConcurrentSkipListMap<>();
identities.keySet().forEach(member -> instantiate(member, context, serverMembers));

System.out.println();
System.out.println();
System.out.println(
String.format("Cardinality: %s, Prob Byz: %s, Rings: %s Majority: %s", getCardinality(), PBYZ,
context.getRingCount(), context.majority()));
System.out.printf("Cardinality: %s, Prob Byz: %s, Rings: %s Majority: %s%n", getCardinality(), PBYZ,
context.getRingCount(), context.majority(true));
System.out.println();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public void smokin() throws Exception {
final var invitation = gorgoneionClient.apply(Duration.ofSeconds(120));
assertNotNull(invitation);
assertNotEquals(Validations.getDefaultInstance(), invitation);
assertTrue(invitation.getValidationsCount() >= context.majority());
assertTrue(invitation.getValidationsCount() >= context.majority(true));
// Verify client KERL published
Utils.waitForCondition(30_000, 1000, () -> testKerl.getKeyEvent(client.getEvent().getCoordinates()) != null);
var keyS = testKerl.getKeyEvent(client.getEvent().getCoordinates());
Expand Down

0 comments on commit d0e37f7

Please sign in to comment.