Skip to content

Commit

Permalink
feat: change claim primary/secondary slot logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Georgi Grigorov committed Oct 30, 2024
1 parent ae0bf55 commit 28a7dca
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 92 deletions.
147 changes: 93 additions & 54 deletions src/main/java/com/limechain/babe/Authorship.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.limechain.babe.predigest.PreDigestType;
import com.limechain.babe.state.EpochState;
import com.limechain.chain.lightsyncstate.BabeEpoch;
import com.limechain.storage.crypto.KeyStore;
import com.limechain.storage.crypto.KeyType;
import com.limechain.utils.ByteArrayUtils;
import com.limechain.utils.LittleEndianUtils;
import com.limechain.utils.math.BigRational;
Expand All @@ -18,22 +20,30 @@
import org.jetbrains.annotations.NotNull;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

//TODO: Add logs for successfully claiming primary/secondary slot
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Authorship {

//TODO: this method is not intended to be part of this class, the placement should be changed later
public static BabePreDigest claimSlot(final EpochState epochState) {
private static BabePreDigest claimSlot(EpochState epochState, KeyStore keyStore) {

var randomness = epochState.getCurrentEpochData().getRandomness();
var slotNumber = epochState.getCurrentSlotNumber();
var epochNumber = epochState.getCurrentEpochNumber();
var c = epochState.getCurrentEpochDescriptor().getConstant();
var authorities = epochState.getCurrentEpochData().getAuthorities();

var keys = extractKeyPairsForRegisteredAuthorities(authorities, keyStore);

BabePreDigest primarySlot = claimPrimarySlot(
epochState.getCurrentEpochData().getRandomness(),
epochState.getCurrentSlotNumber(),
epochState.getCurrentSlotNumber(), //TODO: get current epoch number
null, //TODO: get the keys somehow
epochState.getCurrentEpochData().getAuthorityIndex(),
epochState.getCurrentEpochDescriptor().getThreshold()
randomness,
slotNumber,
epochNumber,
authorities,
c,
keys
);

if (primarySlot != null) return primarySlot;
Expand All @@ -42,43 +52,50 @@ public static BabePreDigest claimSlot(final EpochState epochState) {
epochState.getCurrentEpochDescriptor().getAllowedSlots().equals(BabeEpoch.BabeAllowedSlots.PRIMARY_AND_SECONDARY_VRF_SLOTS);

return claimSecondarySlot(
epochState.getCurrentEpochData().getRandomness(),
epochState.getCurrentSlotNumber(),
epochState.getCurrentSlotNumber(), //TODO: get current epoch number
epochState.getCurrentEpochData().getAuthorities(),
null, //TODO: get the keys somehow
epochState.getCurrentEpochData().getAuthorityIndex(),
randomness,
slotNumber,
epochNumber,
authorities,
keys,
authorSecondaryVrfSlot
);
}

public static BabePreDigest claimPrimarySlot(final byte[] randomness,
final BigInteger slotNumber,
final BigInteger epochNumber,
final Schnorrkel.KeyPair keyPair,
final int authorityIndex,
final BigInteger threshold) {
final List<Authority> authorities,
final Pair<BigInteger, BigInteger> c,
final List<Pair<Integer, Schnorrkel.KeyPair>> keys) {

var transcript = makeTranscript(randomness, slotNumber, epochNumber);

Schnorrkel schnorrkel = Schnorrkel.getInstance();
VrfOutputAndProof vrfOutputAndProof = schnorrkel.vrfSign(keyPair, transcript);
byte[] vrfBytes = schnorrkel.makeBytes(keyPair, transcript, vrfOutputAndProof);
for (Pair<Integer, Schnorrkel.KeyPair> key : keys) {

if (vrfBytes.length != 16) {
throw new IllegalArgumentException("VRF byte array must be exactly 16 bytes long");
}
var index = key.getValue0();
var keyPair = key.getValue1();

var threshold = calculatePrimaryThreshold(c, authorities, index);

Schnorrkel schnorrkel = Schnorrkel.getInstance();
VrfOutputAndProof vrfOutputAndProof = schnorrkel.vrfSign(keyPair, transcript);
byte[] vrfBytes = schnorrkel.makeBytes(keyPair, transcript, vrfOutputAndProof);

if (vrfBytes.length != 16) {
throw new IllegalArgumentException("VRF byte array must be exactly 16 bytes long");
}

var isBelowThreshold = LittleEndianUtils.fromLittleEndianByteArray(vrfBytes).compareTo(threshold) < 0;
var isBelowThreshold = LittleEndianUtils.fromLittleEndianByteArray(vrfBytes).compareTo(threshold) < 0;

if (isBelowThreshold) {
return new BabePreDigest(
PreDigestType.BABE_PRIMARY,
authorityIndex,
slotNumber,
vrfOutputAndProof.getOutput(),
vrfOutputAndProof.getProof()
);
if (isBelowThreshold) {
return new BabePreDigest(
PreDigestType.BABE_PRIMARY,
index,
slotNumber,
vrfOutputAndProof.getOutput(),
vrfOutputAndProof.getProof()
);
}
}

return null;
Expand All @@ -88,37 +105,42 @@ public static BabePreDigest claimSecondarySlot(final byte[] randomness,
final BigInteger slotNumber,
final BigInteger epochNumber,
final List<Authority> authorities,
final Schnorrkel.KeyPair keyPair,
final int authorityIndex,
final List<Pair<Integer, Schnorrkel.KeyPair>> keys,
final boolean authorSecondaryVrfSlot) {

var secondarySlotAuthorIndex = getSecondarySlotAuthor(randomness, slotNumber, authorities);
if (secondarySlotAuthorIndex == null) {
return null;
}

//Not our turn to propose
if (secondarySlotAuthorIndex != authorityIndex) {
return null;
for (Pair<Integer, Schnorrkel.KeyPair> key : keys) {
var index = key.getValue0();
var keyPair = key.getValue1();

if (!secondarySlotAuthorIndex.equals(index)) {
return null;
}

if (authorSecondaryVrfSlot) {
return buildSecondaryVrfPreDigest(
randomness,
slotNumber,
epochNumber,
keyPair,
index
);
} else {
return new BabePreDigest(
PreDigestType.BABE_SECONDARY_PLAIN,
index,
slotNumber,
null,
null
);
}
}

if (authorSecondaryVrfSlot) {
return buildSecondaryVrfPreDigest(
randomness,
slotNumber,
epochNumber,
keyPair,
authorityIndex
);
} else {
return new BabePreDigest(
PreDigestType.BABE_SECONDARY_PLAIN,
authorityIndex,
slotNumber,
null,
null
);
}
return null;
}

private static BabePreDigest buildSecondaryVrfPreDigest(final byte[] randomness,
Expand Down Expand Up @@ -191,6 +213,23 @@ public static BigInteger calculatePrimaryThreshold(
return scaledNumer.divide(pRational.getDenominator());
}

private static List<Pair<Integer, Schnorrkel.KeyPair>> extractKeyPairsForRegisteredAuthorities(
List<Authority> authorities,
KeyStore keyStore) {

List<Pair<Integer, Schnorrkel.KeyPair>> keys = new ArrayList<>();
for (Authority authority : authorities) {
var privateKey = keyStore.get(KeyType.BABE, authority.getPublicKey());
if (privateKey != null) {
Schnorrkel.PublicKey publicKey = new Schnorrkel.PublicKey(authority.getPublicKey());
Schnorrkel.KeyPair keyPair = new Schnorrkel.KeyPair(publicKey, privateKey);
keys.add(new Pair<>(authorities.indexOf(authority), keyPair));
}
}

return keys;
}

private static double getBabeConstant(@NotNull Pair<BigInteger, BigInteger> constant,
@NotNull List<Authority> authorities,
int authorityIndex) {
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/com/limechain/babe/state/EpochData.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,4 @@
public class EpochData {
private List<Authority> authorities;
private byte[] randomness;
//TODO: this may be relocated to different place
private int authorityIndex;

public EpochData(List<Authority> authorities, byte[] randomness) {
this.authorities = authorities;
this.randomness = randomness;
}
}
24 changes: 0 additions & 24 deletions src/main/java/com/limechain/babe/state/EpochDescriptor.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.limechain.babe.state;

import com.limechain.babe.Authorship;
import com.limechain.chain.lightsyncstate.BabeEpoch;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.javatuples.Pair;

import java.math.BigInteger;
Expand All @@ -17,26 +15,4 @@
public class EpochDescriptor {
private Pair<BigInteger, BigInteger> constant;
private BabeEpoch.BabeAllowedSlots allowedSlots;
//TODO: this may be relocated to different place
@Setter
private BigInteger threshold;

public EpochDescriptor(Pair<BigInteger, BigInteger> constant, BabeEpoch.BabeAllowedSlots allowedSlots) {
this.constant = constant;
this.allowedSlots = allowedSlots;
}

public static EpochDescriptor build(Pair<BigInteger, BigInteger> constant,
BabeEpoch.BabeAllowedSlots allowedSlots,
EpochData epochData) {
var result = new EpochDescriptor(constant, allowedSlots);
result.setThreshold(
Authorship.calculatePrimaryThreshold(
constant,
epochData.getAuthorities(),
epochData.getAuthorityIndex()
)
);
return result;
}
}
8 changes: 1 addition & 7 deletions src/main/java/com/limechain/babe/state/EpochState.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ public void initialize(BabeApiConfiguration babeApiConfiguration) {
this.slotDuration = babeApiConfiguration.getSlotDuration();
this.epochLength = babeApiConfiguration.getEpochLength();
this.currentEpochData = new EpochData(babeApiConfiguration.getAuthorities(), babeApiConfiguration.getRandomness());
this.currentEpochDescriptor = EpochDescriptor.build(
babeApiConfiguration.getConstant(),
babeApiConfiguration.getAllowedSlots(),
currentEpochData
);
this.currentEpochDescriptor = new EpochDescriptor(babeApiConfiguration.getConstant(), babeApiConfiguration.getAllowedSlots());
}

public void updateNextEpochBlockConfig(byte[] message) {
Expand All @@ -57,6 +53,4 @@ public BigInteger getCurrentEpochNumber() {
//TODO: replace BigInteger.valueOf(1234) with genesis_slot
return BigIntegerUtils.divideAndRoundUp(BigInteger.valueOf(1234), epochLength);
}

//TODO: get auth index
}

0 comments on commit 28a7dca

Please sign in to comment.