Skip to content

Commit

Permalink
Merge pull request #579 from LimeChain/547-block-production-lottery
Browse files Browse the repository at this point in the history
Block Production Lottery - Primary Slot
  • Loading branch information
Grigorov-Georgi authored Oct 29, 2024
2 parents 0f0432c + 11b4dd0 commit 4f6f679
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 3 deletions.
Binary file removed libs/polkaj-schnorrkel-0.5.2-SNAPSHOT.jar
Binary file not shown.
Binary file added libs/polkaj-schnorrkel-0.5.3-SNAPSHOT.jar
Binary file not shown.
40 changes: 40 additions & 0 deletions src/main/java/com/limechain/babe/Authorship.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.limechain.babe;

import com.limechain.utils.LittleEndianUtils;
import com.limechain.utils.math.BigRational;
import com.limechain.chain.lightsyncstate.Authority;
import io.emeraldpay.polkaj.merlin.TranscriptData;
import io.emeraldpay.polkaj.schnorrkel.Schnorrkel;
import io.emeraldpay.polkaj.schnorrkel.VrfOutputAndProof;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.javatuples.Pair;
Expand All @@ -13,6 +17,34 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Authorship {

//TODO: Replace return type with PreDigest
public static Object claimPrimarySlot(final byte[] randomness,
final long slotNumber,
final long epochNumber,
final Schnorrkel.KeyPair keyPair,
final int authorityIndex,
final BigInteger threshold) {

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

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;

if (isBelowThreshold) {
//TODO: Return PreDigest
return null;
}

return null;
}

// threshold = 2^128 * (1 - (1 - c) ^ (authority_weight / sum(authorities_weights)))
public static BigInteger calculatePrimaryThreshold(
@NotNull final Pair<BigInteger, BigInteger> constant,
Expand Down Expand Up @@ -66,4 +98,12 @@ private static double getBabeConstant(@NotNull Pair<BigInteger, BigInteger> cons

return c;
}

private static TranscriptData makeTranscript(byte[] randomness, Long slotNumber, Long epochNumber) {
var transcript = new TranscriptData("BABE".getBytes());
transcript.appendMessage("slot number", LittleEndianUtils.longToLittleEndianBytes(slotNumber));
transcript.appendMessage("current epoch", LittleEndianUtils.longToLittleEndianBytes(epochNumber));
transcript.appendMessage("chain randomness", randomness);
return transcript;
}
}
34 changes: 32 additions & 2 deletions src/main/java/com/limechain/utils/LittleEndianUtils.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.limechain.utils;

import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

Expand Down Expand Up @@ -31,13 +33,14 @@ public static byte[] convertBytes(byte[] byteArray) {
* If the input array is shorter than the specified length, it will be padded with zeros.
*
* @param byteArray The byte array to be converted to fixed length little-endian.
* @param length The desired length of the resulting byte array.
* @param length The desired length of the resulting byte array.
* @return A new byte array with the little-endian representation of the input, padded with zeros if necessary.
*/
public static byte[] bytesToFixedLength(byte[] byteArray, int length) {
byte[] littleEndian = new byte[length];
int smallestLength = Math.min(byteArray.length, littleEndian.length);

for (int i = 0; i < byteArray.length; i++) {
for (int i = 0; i < smallestLength; i++) {
littleEndian[i] = byteArray[byteArray.length - 1 - i];
}

Expand All @@ -57,4 +60,31 @@ public static byte[] intTo32LEBytes(int number) {
byte byte4 = (byte) (number >>> 24);
return new byte[]{byte1, byte2, byte3, byte4};
}

/**
* Converts a long value into a little-endian byte array.
*
* @param value the long value to convert to a byte array
* @return a byte array representing the long value in little-endian order
*/
public static byte[] longToLittleEndianBytes(long value) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putLong(value);
return buffer.array();
}

/**
* Converts a little-endian byte array into a BigInteger.
*
* @param bytes the byte array to convert
* @return a BigInteger representation of the input byte array
*/
public static BigInteger fromLittleEndianByteArray(@NotNull byte[] bytes) {
byte[] reversed = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) {
reversed[i] = bytes[bytes.length - 1 - i];
}
return new BigInteger(1, reversed);
}
}
1 change: 0 additions & 1 deletion src/main/java/com/limechain/utils/Sr25519Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,4 @@ public static boolean verifySignature(final VerifySignature signature) {
return false;
}
}

}
92 changes: 92 additions & 0 deletions src/test/java/com/limechain/utils/LittleEndianUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.limechain.utils;

import org.junit.jupiter.api.Test;

import java.math.BigInteger;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;

class LittleEndianUtilsTest {
@Test
void testBytesToFixedLength() {
byte[] bigEndianArray = {0x01, 0x02, 0x03, 0x04};
int targetLength = 4;

byte[] expectedLittleEndianArray = {0x04, 0x03, 0x02, 0x01};
byte[] actualLittleEndianArray = LittleEndianUtils.bytesToFixedLength(bigEndianArray, targetLength);
assertArrayEquals(expectedLittleEndianArray, actualLittleEndianArray);
}

@Test
void testBytesToFixedLengthWithPadding() {
byte[] bigEndianArray = {0x0A, 0x0B, 0x0C};
int targetLength = 5;

byte[] expectedLittleEndianArray = {0x0C, 0x0B, 0x0A, 0x00, 0x00};
byte[] actualLittleEndianArray = LittleEndianUtils.bytesToFixedLength(bigEndianArray, targetLength);
assertArrayEquals(expectedLittleEndianArray, actualLittleEndianArray);
}

@Test
void testBytesToFixedLengthWithTruncation() {
byte[] bigEndianArray = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
int targetLength = 4;

byte[] expectedLittleEndianArray = {0x60, 0x50, 0x40, 0x30};
byte[] actualLittleEndianArray = LittleEndianUtils.bytesToFixedLength(bigEndianArray, targetLength);
assertArrayEquals(expectedLittleEndianArray, actualLittleEndianArray);
}

@Test
void testLongToLittleEndianBytesWithMaxLongValue() {
long value = Long.MAX_VALUE;
byte[] expected = new byte[]{-1, -1, -1, -1, -1, -1, -1, 127}; // Expected little-endian bytes for Long.MAX_VALUE
byte[] result = LittleEndianUtils.longToLittleEndianBytes(value);
assertArrayEquals(expected, result);
}

@Test
void testLongToLittleEndianBytesWithMinLongValue() {
long value = Long.MIN_VALUE;
byte[] expected = new byte[]{0, 0, 0, 0, 0, 0, 0, -128}; // Expected little-endian bytes for Long.MIN_VALUE
byte[] result = LittleEndianUtils.longToLittleEndianBytes(value);
assertArrayEquals(expected, result);
}

@Test
void testLongToLittleEndianBytesWithZero() {
long value = 0L;
byte[] expected = new byte[]{0, 0, 0, 0, 0, 0, 0, 0}; // Expected little-endian bytes for 0
byte[] result = LittleEndianUtils.longToLittleEndianBytes(value);
assertArrayEquals(expected, result);
}

@Test
void testFromLittleEndianByteArray() {
byte[] bytes = {1, 0, 0, 0};
BigInteger expected = BigInteger.ONE;
BigInteger result = LittleEndianUtils.fromLittleEndianByteArray(bytes);
assertEquals(expected, result);
}

@Test
void testFromLittleEndianByteArrayWithRandomNumber() {
// Tested with rust build-in 'u128::from_le_bytes' function
// let byte_arr: [i8 ; 16] = [-124, -72, -92, 56, 112, -102, 29, 107, -111, -17, 73, -30, -49, -30, 58, 111];
// let byte_arr = byte_arr.map(|byte| byte as u8);
// let result = u128::from_le_bytes(byte_arr))
byte[] bytes = {-124, -72, -92, 56, 112, -102, 29, 107, -111, -17, 73, -30, -49, -30, 58, 111};
String expected = "147850061044753742757552691558406731908";
BigInteger result = LittleEndianUtils.fromLittleEndianByteArray(bytes);
assertEquals(expected, result.toString());
}

@Test
void testFromLittleEndianByteArrayWithEmptyArray() {
byte[] bytes = {};
BigInteger expected = BigInteger.ZERO;
BigInteger result = LittleEndianUtils.fromLittleEndianByteArray(bytes);
assertEquals(expected, result);
}
}

0 comments on commit 4f6f679

Please sign in to comment.