Skip to content

Commit

Permalink
Merge pull request #600 from LimeChain/593-retrieve-genesis-slot
Browse files Browse the repository at this point in the history
Implement Genesis Slot Number Retrieval
  • Loading branch information
Grigorov-Georgi authored Nov 8, 2024
2 parents 5963d69 + 527bf18 commit 9649bcf
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 49 deletions.
22 changes: 19 additions & 3 deletions src/main/java/com/limechain/babe/state/EpochState.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,29 @@
@Getter
@Component
public class EpochState {

private boolean isInitialized = false;

private long disabledAuthority;
private BigInteger slotDuration;
private BigInteger epochLength;
private BigInteger genesisSlotNumber;

private EpochData currentEpochData;
private EpochDescriptor currentEpochDescriptor;

private EpochData nextEpochData;
private EpochDescriptor nextEpochDescriptor;
private long disabledAuthority;
private BigInteger genesisSlotNumber;

public void initialize(BabeApiConfiguration babeApiConfiguration) {
this.slotDuration = babeApiConfiguration.getSlotDuration();
this.epochLength = babeApiConfiguration.getEpochLength();
this.currentEpochData = new EpochData(babeApiConfiguration.getAuthorities(), babeApiConfiguration.getRandomness());
this.currentEpochDescriptor = new EpochDescriptor(babeApiConfiguration.getConstant(), babeApiConfiguration.getAllowedSlots());
this.isInitialized = true;
}

//TODO: This will be fixed in https://github.com/LimeChain/Fruzhin/issues/593
//TODO: Call this form the BlockImporter class when it it created!
public void updateNextEpochBlockConfig(byte[] message) {
BabeConsensusMessage babeConsensusMessage = ScaleUtils.Decode.decode(message, new BabeConsensusMessageReader());
switch (babeConsensusMessage.getFormat()) {
Expand All @@ -44,6 +50,16 @@ public void updateNextEpochBlockConfig(byte[] message) {
}
}

public void setGenesisSlotNumber(BigInteger retrievedGenesisSlotNumber) {
if (retrievedGenesisSlotNumber != null) {
this.genesisSlotNumber = retrievedGenesisSlotNumber;
} else {
// If retrieved genesis slot number is null, then there are no executed
// blocks on the chain and current slot number should be the genesis
this.genesisSlotNumber = getCurrentSlotNumber();
}
}

public BigInteger getCurrentSlotNumber() {
return BigInteger.valueOf(Instant.now().toEpochMilli()).divide(slotDuration);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ private void handleBlockAnnounce(byte[] msg, PeerId peerId) {
if (BlockState.getInstance().isInitialized()) {
BlockState.getInstance().addBlockToBlockTree(announce.getHeader());
}
//TODO: This will be fixed in https://github.com/LimeChain/Fruzhin/issues/593
// digestHelper.handleHeaderDigests(announce.getHeader().getDigest());
}

public void writeHandshakeToStream(Stream stream, PeerId peerId) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/limechain/rpc/config/CommonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ public WarpSyncMachine warpSyncMachine(Network network, ChainService chainServic
}

@Bean
public FullSyncMachine fullSyncMachine(Network network, SyncState syncState) {
return new FullSyncMachine(network, syncState);
public FullSyncMachine fullSyncMachine(HostConfig hostConfig, Network network, SyncState syncState) {
return new FullSyncMachine(hostConfig, network, syncState);
}

@Bean
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/limechain/runtime/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.limechain.runtime.version.scale.RuntimeVersionReader;
import com.limechain.sync.fullsync.inherents.InherentData;
import com.limechain.sync.fullsync.inherents.scale.InherentDataWriter;
import com.limechain.trie.structure.nibble.Nibbles;
import com.limechain.utils.LittleEndianUtils;
import com.limechain.utils.scale.ScaleUtils;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
Expand All @@ -20,6 +22,8 @@
import org.wasmer.Instance;
import org.wasmer.Module;

import java.math.BigInteger;
import java.util.Optional;
import java.util.logging.Level;

@Log
Expand Down Expand Up @@ -65,6 +69,15 @@ private byte[] callInner(RuntimeEndpoint function, RuntimePointerSize parameterP
return context.getSharedMemory().readData(responsePtrSize);
}

private Optional<byte[]> findStorageValue(Nibbles key) {
return this.context.trieAccessor.findStorageValue(key);
}

public BigInteger getGenesisSlotNumber() {
var optGenesisSlotBytes = this.findStorageValue(RuntimeStorageKey.GENESIS_SLOT.getNibbles());
return optGenesisSlotBytes.map(LittleEndianUtils::fromLittleEndianByteArray).orElse(null);
}

/**
* @return the {@link BabeApiConfiguration}.
*/
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/limechain/runtime/RuntimeStorageKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.limechain.runtime;

import com.limechain.trie.structure.nibble.Nibbles;
import lombok.AllArgsConstructor;
import lombok.Getter;

// All available keys can be found here: https://www.shawntabrizi.com/substrate-known-keys/
// Keys are encoded using 'xxHash' algorithm, and we use directly the encoded value
@Getter
@AllArgsConstructor
public enum RuntimeStorageKey {
GENESIS_SLOT("0x1cb6f36e027abb2091cfb5110ab5087f678711d15ebbceba5cd0cea158e6675a");

private final String encodedKey;

public Nibbles getNibbles() {
return Nibbles.fromHexString(this.getEncodedKey());
}
}
19 changes: 15 additions & 4 deletions src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.limechain.sync.fullsync;

import com.google.protobuf.ByteString;
import com.limechain.config.HostConfig;
import com.limechain.exception.storage.BlockNodeNotFoundException;
import com.limechain.exception.sync.BlockExecutionException;
import com.limechain.network.Network;
import com.limechain.network.protocol.blockannounce.NodeRole;
import com.limechain.network.protocol.sync.BlockRequestDto;
import com.limechain.network.protocol.sync.pb.SyncMessage;
import com.limechain.network.protocol.warp.dto.Block;
Expand All @@ -15,7 +17,6 @@
import com.limechain.network.protocol.warp.scale.reader.BlockHeaderReader;
import com.limechain.rpc.server.AppBean;
import com.limechain.runtime.Runtime;
import com.limechain.babe.api.BabeApiConfiguration;
import com.limechain.babe.state.EpochState;
import com.limechain.runtime.builder.RuntimeBuilder;
import com.limechain.runtime.version.StateVersion;
Expand Down Expand Up @@ -51,6 +52,8 @@
@Getter
@Log
public class FullSyncMachine {

private final HostConfig hostConfig;
private final Network networkService;
private final SyncState syncState;
private final BlockState blockState = BlockState.getInstance();
Expand All @@ -59,7 +62,8 @@ public class FullSyncMachine {
private final EpochState epochState = AppBean.getBean(EpochState.class);
private Runtime runtime = null;

public FullSyncMachine(final Network networkService, final SyncState syncState) {
public FullSyncMachine(HostConfig hostConfig, Network networkService, SyncState syncState) {
this.hostConfig = hostConfig;
this.networkService = networkService;
this.syncState = syncState;
}
Expand All @@ -81,8 +85,6 @@ public void start() {

runtime = buildRuntimeFromState(trieAccessor);
StateVersion runtimeStateVersion = runtime.getVersion().getStateVersion();
BabeApiConfiguration babeApiConfiguration = runtime.callBabeApiConfiguration();
epochState.initialize(babeApiConfiguration);
trieAccessor.setCurrentStateVersion(runtimeStateVersion);

byte[] calculatedMerkleRoot = trieAccessor.getMerkleRoot(runtimeStateVersion);
Expand All @@ -108,9 +110,18 @@ public void start() {
receivedBlocks = requestBlocks(startNumber, blocksToFetch);
}

if (NodeRole.AUTHORING.equals(hostConfig.getNodeRole())) {
initializeEpochState();
}

blockState.setFullSyncFinished(true);
}

private void initializeEpochState() {
epochState.initialize(runtime.callBabeApiConfiguration());
epochState.setGenesisSlotNumber(runtime.getGenesisSlotNumber());
}

private TrieStructure<NodeData> loadStateAtBlockFromPeer(Hash256 lastFinalizedBlockHash) {
log.info("Loading state at block from peer");
Map<ByteString, ByteString> kvps = makeStateRequest(lastFinalizedBlockHash);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.limechain.network.protocol.warp;

import com.limechain.babe.state.EpochState;
import com.limechain.babe.consensus.BabeConsensusMessageFormat;
import com.limechain.babe.predigest.PreDigestType;
import com.limechain.network.protocol.warp.dto.BlockHeader;
import com.limechain.network.protocol.warp.dto.ConsensusEngine;
import com.limechain.network.protocol.warp.dto.DigestType;
Expand All @@ -10,49 +11,108 @@
import io.emeraldpay.polkaj.schnorrkel.SchnorrkelException;
import io.emeraldpay.polkaj.types.Hash256;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigInteger;
import java.security.SecureRandom;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(MockitoExtension.class)
public class DigestHelperTest {

@InjectMocks
private DigestHelper digestHelper;

@Mock
private EpochState epochState;

//TODO: This will be fixed in https://github.com/LimeChain/Fruzhin/issues/593
// @Test
// void handleHeaderDigests_WithConsensusAndPreRuntimeDigests_ShouldProcessBothCorrectly() {
// byte[] consensusMessage = new byte[]{1, 2, 3};
// byte[] preRuntimeMessage = StringUtils.hexToBytes("0x03b7010000286f301100000000424dc7bc71c9b0f3a15b6aba389471fff57e154dbf3dc046f47513a38375ce4cfe421df446b2b7a0f5b1b2e69c810a6ba36787c0301e6636df3e45688116e005363dfe9922b3b7cc7b80e2fdab8b0b98dcfb4b96cc470fb35f753debda40aa0e");
//
// HeaderDigest consensusDigest = mock(HeaderDigest.class);
// HeaderDigest preRuntimeDigest = mock(HeaderDigest.class);
//
// when(consensusDigest.getType()).thenReturn(DigestType.CONSENSUS_MESSAGE);
// when(consensusDigest.getId()).thenReturn(ConsensusEngine.BABE);
// when(consensusDigest.getMessage()).thenReturn(consensusMessage);
//
// when(preRuntimeDigest.getType()).thenReturn(DigestType.PRE_RUNTIME);
// when(preRuntimeDigest.getId()).thenReturn(ConsensusEngine.BABE);
// when(preRuntimeDigest.getMessage()).thenReturn(preRuntimeMessage);
//
// HeaderDigest[] headerDigests = {consensusDigest, preRuntimeDigest};
//
// digestHelper.handleHeaderDigests(headerDigests);
//
// verify(epochState).updateNextEpochBlockConfig(consensusMessage);
// }
class DigestHelperTest {

@Test
void getBabeConsensusMessageTest() {
HeaderDigest consensusDigest = new HeaderDigest();
consensusDigest.setId(ConsensusEngine.BABE);
consensusDigest.setType(DigestType.CONSENSUS_MESSAGE);

// Create a consensus message with type DISABLED_AUTHORITY(2) and value equal to 0
var message = new byte[33];
message[0] = 2;
consensusDigest.setMessage(message);

HeaderDigest[] headerDigests = new HeaderDigest[] {consensusDigest};
var optResult = DigestHelper.getBabeConsensusMessage(headerDigests);

assertTrue(optResult.isPresent());

var result = optResult.get();
assertEquals(BabeConsensusMessageFormat.DISABLED_AUTHORITY, result.getFormat());
assertEquals(0, result.getDisabledAuthority());
assertNull(result.getNextEpochData());
assertNull(result.getNextEpochDescriptor());
}

@Test
void getBabeConsensusMessageWithoutSuchDigestInHeadersTest() {
var optResult = DigestHelper.getBabeConsensusMessage(new HeaderDigest[0]);
assertTrue(optResult.isEmpty());
}

@Test
void getBabePreRuntimeDigestForPrimarySlotTest() {
HeaderDigest consensusDigest = new HeaderDigest();
consensusDigest.setId(ConsensusEngine.BABE);
consensusDigest.setType(DigestType.PRE_RUNTIME);

// Create a PreRuntimeDigest with:
// Type -> BABE_PRIMARY(1)
// Authority index -> 0
// Slot number -> 0
// VRF_OUTPUT -> byte array with 32 zeros
// VRF_PROOF -> byte array with 64 zeros
var message = new byte[193];
message[0] = 1;
consensusDigest.setMessage(message);

HeaderDigest[] headerDigests = new HeaderDigest[] {consensusDigest};
var optResult = DigestHelper.getBabePreRuntimeDigest(headerDigests);

assertTrue(optResult.isPresent());

var result = optResult.get();
assertEquals(PreDigestType.BABE_PRIMARY, result.getType());
assertEquals(0, result.getAuthorityIndex());
assertEquals(BigInteger.ZERO, result.getSlotNumber());
assertArrayEquals(new byte[32], result.getVrfOutput());
assertArrayEquals(new byte[64], result.getVrfProof());
}

@Test
void getBabePreRuntimeDigestForSecondaryPlainSlotTest() {
HeaderDigest consensusDigest = new HeaderDigest();
consensusDigest.setId(ConsensusEngine.BABE);
consensusDigest.setType(DigestType.PRE_RUNTIME);

// Create a PreRuntimeDigest with:
// Type -> BABE_SECONDARY_PLAIN(2)
// Authority index -> 0
// Slot number -> 0
var message = new byte[97];
message[0] = 2;
consensusDigest.setMessage(message);

HeaderDigest[] headerDigests = new HeaderDigest[] {consensusDigest};
var optResult = DigestHelper.getBabePreRuntimeDigest(headerDigests);

assertTrue(optResult.isPresent());

var result = optResult.get();
assertEquals(PreDigestType.BABE_SECONDARY_PLAIN, result.getType());
assertEquals(0, result.getAuthorityIndex());
assertEquals(BigInteger.ZERO, result.getSlotNumber());
assertNull(result.getVrfOutput());
assertNull(result.getVrfProof());
}

@Test
void getBabePreRuntimeDigestWithoutSuchDigestInHeadersTest() {
var optResult = DigestHelper.getBabePreRuntimeDigest(new HeaderDigest[0]);
assertTrue(optResult.isEmpty());
}

@Test
void testBuildSealHeaderDigest() throws SchnorrkelException {
Expand All @@ -72,7 +132,7 @@ void testBuildSealHeaderDigest() throws SchnorrkelException {

Schnorrkel.KeyPair keyPair = Schnorrkel.getInstance().generateKeyPair(new SecureRandom());

HeaderDigest sealHeaderDigest = digestHelper.buildSealHeaderDigest(blockHeader, keyPair);
HeaderDigest sealHeaderDigest = DigestHelper.buildSealHeaderDigest(blockHeader, keyPair);

assertNotNull(sealHeaderDigest);
assertEquals(DigestType.SEAL, sealHeaderDigest.getType());
Expand Down

0 comments on commit 9649bcf

Please sign in to comment.