Skip to content

Commit

Permalink
Simplify genesis and view reconfiguration
Browse files Browse the repository at this point in the history
remove unnecessary signatures n' such.
  • Loading branch information
Hellblazer committed Apr 14, 2024
1 parent 17da122 commit 77d99b5
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 301 deletions.
35 changes: 18 additions & 17 deletions choam/src/main/java/com/salesforce/apollo/choam/CHOAM.java
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public static Checkpoint checkpoint(DigestAlgorithm algo, File state, int segmen
return cp;
}

public static Block genesis(Digest id, Map<Member, Join> joins, HashedBlock head, Context<Member> context,
public static Block genesis(Digest id, Map<Digest, Join> joins, HashedBlock head, Context<Member> context,
HashedBlock lastViewChange, Parameters params, HashedBlock lastCheckpoint,
Iterable<Transaction> initialization) {
var reconfigure = reconfigure(id, joins, context, params.checkpointBlockDelta());
Expand All @@ -202,26 +202,23 @@ public static Digest hashOf(Transaction transaction, DigestAlgorithm digestAlgor

public static String print(Join join, DigestAlgorithm da) {
return "J[view: " + Digest.from(join.getMember().getVm().getView()) + " member: " + ViewContext.print(
join.getMember(), da) + "certifications: " + join.getEndorsementsList()
.stream()
.map(c -> ViewContext.print(c, da))
.toList() + "]";
join.getMember(), da) + "]";
}

public static Reconfigure reconfigure(Digest nextViewId, Map<Member, Join> joins, Context<Member> context,
public static Reconfigure reconfigure(Digest nextViewId, Map<Digest, Join> joins, Context<Member> context,
int checkpointTarget) {
var builder = Reconfigure.newBuilder().setCheckpointTarget(checkpointTarget).setId(nextViewId.toDigeste());

// Canonical labeling of the view members for Ethereal
var remapped = rosterMap(context, joins.keySet());

remapped.keySet().stream().sorted().map(remapped::get).forEach(m -> builder.addJoins(joins.get(m)));
remapped.keySet().stream().sorted().map(remapped::get).forEach(m -> builder.addJoins(joins.get(m.getId())));

var reconfigure = builder.build();
return reconfigure;
}

public static Block reconfigure(Digest nextViewId, Map<Member, Join> joins, HashedBlock head,
public static Block reconfigure(Digest nextViewId, Map<Digest, Join> joins, HashedBlock head,
Context<Member> context, HashedBlock lastViewChange, Parameters params,
HashedBlock lastCheckpoint) {
final Block lvc = lastViewChange.block;
Expand All @@ -237,10 +234,8 @@ public static Block reconfigure(Digest nextViewId, Map<Member, Join> joins, Hash
.build();
}

public static Map<Digest, Member> rosterMap(Context<Member> baseContext, Collection<Member> members) {

// Canonical labeling of the view members for Ethereal
return members.stream().collect(Collectors.toMap(Member::getId, m -> m));
public static Map<Digest, Member> rosterMap(Context<Member> baseContext, Collection<Digest> members) {
return members.stream().collect(Collectors.toMap(m -> m, m -> baseContext.getMember(m)));
}

public static List<Transaction> toGenesisData(List<? extends Message> initializationData) {
Expand Down Expand Up @@ -497,11 +492,17 @@ public Block checkpoint() {
}

@Override
public Block genesis(Map<Member, Join> joining, Digest nextViewId, HashedBlock previous) {
public Block genesis(Map<Digest, Join> joining, Digest nextViewId, HashedBlock previous) {
final HashedCertifiedBlock cp = checkpoint.get();
final HashedCertifiedBlock v = view.get();
var g = CHOAM.genesis(nextViewId, joining, previous, params.context(), v, params, cp,
params.genesisData().apply(joining));
params.genesisData()
.apply(joining.keySet()
.stream()
.map(m -> params.context().getMember(m))
.filter(m -> m != null)
.collect(
Collectors.toMap(m -> m, m -> joining.get(m.getId())))));
log.info("Create genesis: {} on: {}", nextViewId, params.member().getId());
return g;
}
Expand Down Expand Up @@ -548,7 +549,7 @@ public void publish(Digest hash, CertifiedBlock cb) {
}

@Override
public Block reconfigure(Map<Member, Join> joining, Digest nextViewId, HashedBlock previous,
public Block reconfigure(Map<Digest, Join> joining, Digest nextViewId, HashedBlock previous,
HashedBlock checkpoint) {
final HashedCertifiedBlock v = view.get();
var block = CHOAM.reconfigure(nextViewId, joining, previous, pendingView().get(), v, params,
Expand Down Expand Up @@ -1008,7 +1009,7 @@ private void synchronizedProcess(CertifiedBlock certifiedBlock) {
public interface BlockProducer {
Block checkpoint();

Block genesis(Map<Member, Join> joining, Digest nextViewId, HashedBlock previous);
Block genesis(Map<Digest, Join> joining, Digest nextViewId, HashedBlock previous);

void onFailure();

Expand All @@ -1018,7 +1019,7 @@ public interface BlockProducer {

void publish(Digest hash, CertifiedBlock cb);

Block reconfigure(Map<Member, Join> joining, Digest nextViewId, HashedBlock previous, HashedBlock checkpoint);
Block reconfigure(Map<Digest, Join> joining, Digest nextViewId, HashedBlock previous, HashedBlock checkpoint);
}

@FunctionalInterface
Expand Down
111 changes: 21 additions & 90 deletions choam/src/main/java/com/salesforce/apollo/choam/GenesisAssembly.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@
* @author hal.hildebrand
*/
public class GenesisAssembly implements Genesis {
private static final Logger log = LoggerFactory.getLogger(GenesisAssembly.class);
private static final Logger log = LoggerFactory.getLogger(GenesisAssembly.class);
private final Ethereal controller;
private final ChRbcGossip coordinator;
private final SignedViewMember genesisMember;
private final Map<Digest, Member> nextAssembly;
private final Map<Digest, Proposed> proposals = new ConcurrentHashMap<>();
private final AtomicBoolean published = new AtomicBoolean();
private final Map<Member, Join> slate = new ConcurrentHashMap<>();
private final AtomicBoolean started = new AtomicBoolean();
private final AtomicBoolean published = new AtomicBoolean();
private final Map<Digest, Join> slate = new ConcurrentHashMap<>();
private final AtomicBoolean started = new AtomicBoolean();
private final Transitions transitions;
private final ViewContext view;
private final Map<Member, Validate> witnesses = new ConcurrentHashMap<>();
private final Map<Member, Validate> witnesses = new ConcurrentHashMap<>();
private final OneShot ds;
private final List<Validate> pendingValidations = new ArrayList<>();
private volatile Thread blockingThread;
private volatile HashedBlock reconfiguration;

Expand Down Expand Up @@ -104,13 +104,9 @@ public GenesisAssembly(ViewContext vc, CommonCommunications<Terminal, ?> comms,

@Override
public void certify() {
proposals.values()
.stream()
.filter(p -> p.certifications.size() == nextAssembly.size())
.forEach(p -> slate.put(p.member(), joinOf(p)));
if (slate.size() != nextAssembly.size()) {
log.info("Not certifying genesis for: {} slate incomplete: {} on: {}", view.context().getId(),
slate.keySet().stream().map(m -> m.getId()).toList(), params().member().getId());
slate.keySet().stream().sorted().toList(), params().member().getId());
return;
}
assert slate.size() == nextAssembly.size() : "Expected: %s members, slate: %s".formatted(nextAssembly.size(),
Expand All @@ -120,8 +116,10 @@ public void certify() {
params().digestAlgorithm())));
var validate = view.generateValidation(reconfiguration);
log.debug("Certifying genesis block: {} for: {} slate: {} on: {}", reconfiguration.hash, view.context().getId(),
slate.keySet().stream().map(m -> m.getId()).toList(), params().member().getId());
slate.keySet().stream().sorted().toList(), params().member().getId());
ds.setValue(validate.toByteString());
witnesses.put(params().member(), validate);
pendingValidations.forEach(v -> certify(v));
}

@Override
Expand All @@ -139,15 +137,8 @@ public void certify(List<ByteString> preblock, boolean last) {
@Override
public void gather() {
log.info("Gathering next assembly on: {}", params().member().getId());
var certification = view.generateValidation(genesisMember).getWitness();
var join = Join.newBuilder()
.setMember(genesisMember)
.addEndorsements(certification)
.setKerl(params().kerl().get())
.build();
var proposed = new Proposed(join, params().member());
proposed.certifications.put(params().member(), certification);
proposals.put(params().member().getId(), proposed);
var join = Join.newBuilder().setMember(genesisMember).setKerl(params().kerl().get()).build();
slate.put(params().member().getId(), join);

ds.setValue(join.toByteString());
coordinator.start(params().producer().gossipDuration());
Expand All @@ -172,19 +163,6 @@ public void gather(List<ByteString> preblock, boolean last) {
.forEach(this::join);
}

@Override
public void nominate() {
var validations = Validations.newBuilder();
proposals.values()
.stream()
.filter(p -> !p.member.equals(params().member()))
.map(p -> view.generateValidation(p.join.getMember()))
.forEach(validations::addValidations);
ds.setValue(validations.build().toByteString());
log.info("Nominations of: {} validations: {} on: {}", params().context().getId(),
validations.getValidationsCount(), params().member().getId());
}

@Override
public void nominations(List<ByteString> preblock, boolean last) {
preblock.stream()
Expand All @@ -198,8 +176,7 @@ public void nominations(List<ByteString> preblock, boolean last) {
})
.filter(Objects::nonNull)
.flatMap(vs -> vs.getValidationsList().stream())
.filter(v -> !v.equals(Validate.getDefaultInstance()))
.forEach(this::validate);
.filter(v -> !v.equals(Validate.getDefaultInstance()));
}

@Override
Expand Down Expand Up @@ -258,6 +235,9 @@ public void stop() {
}

private void certify(Validate v) {
if (reconfiguration == null) {
pendingValidations.add(v);
}
log.trace("Validating reconfiguration block: {} height: {} on: {}", reconfiguration.hash,
reconfiguration.height(), params().member().getId());
if (!view.validate(reconfiguration, v)) {
Expand Down Expand Up @@ -326,64 +306,15 @@ private void join(Join join) {
ViewContext.print(svm, params().digestAlgorithm()), params().member().getId());
return;
}
if (log.isTraceEnabled()) {
log.trace("Valid view member: {} on: {}", ViewContext.print(svm, params().digestAlgorithm()),
params().member().getId());
}
var proposed = proposals.computeIfAbsent(mid, k -> new Proposed(join, m));
if (join.getEndorsementsList().size() == 1) {
proposed.certifications.computeIfAbsent(m, k -> join.getEndorsements(0));
if (slate.putIfAbsent(m.getId(), join) == null) {
if (log.isTraceEnabled()) {
log.trace("Add view member: {} to slate on: {}", ViewContext.print(svm, params().digestAlgorithm()),
params().member().getId());
}
}
}

private Join joinOf(Proposed candidate) {
final List<Certification> witnesses = candidate.certifications.values()
.stream()
.sorted(
Comparator.comparing(c -> new Digest(c.getId())))
.collect(Collectors.toList());
return Join.newBuilder(candidate.join).clearEndorsements().addAllEndorsements(witnesses).build();
}

private Parameters params() {
return view.params();
}

private void validate(Validate v) {
final var cid = Digest.from(v.getWitness().getId());
var certifier = view.context().getMember(cid);
if (certifier == null) {
log.warn("Unknown certifier: {} on: {}", cid, params().member().getId());
return; // do not have the join yet
}
final var vid = Digest.from(v.getHash());
final var member = nextAssembly.get(vid);
if (member == null) {
return;
}
var proposed = proposals.get(vid);
if (proposed == null) {
log.warn("Invalid certification, unknown view join: {} on: {}", vid, params().member().getId());
return; // do not have the join yet
}
if (!view.validate(proposed.join.getMember(), v)) {
log.warn("Invalid certification for view join: {} from: {} on: {}", vid,
Digest.from(v.getWitness().getId()), params().member().getId());
return;
}
var prev = proposed.certifications.put(certifier, v.getWitness());
if (prev == null) {
log.debug("New validation of view member: {} using certifier: {} witnesses: {} on: {}", member.getId(),
certifier.getId(), proposed.certifications.values().size(), params().member().getId());
} else {
log.debug("Redundant validation of view member: {} hash: {} using certifier: {} on: {}", member.getId(),
vid, certifier.getId(), params().member().getId());
}
}

private record Proposed(Join join, Member member, Map<Member, Certification> certifications) {
public Proposed(Join join, Member member) {
this(join, member, new HashMap<>());
}
}
}
Loading

0 comments on commit 77d99b5

Please sign in to comment.