Skip to content

Commit

Permalink
chore: working warp sync
Browse files Browse the repository at this point in the history
  • Loading branch information
ablax committed Aug 28, 2024
1 parent b9e826b commit e831a69
Show file tree
Hide file tree
Showing 11 changed files with 569 additions and 79 deletions.
10 changes: 8 additions & 2 deletions src/main/java/com/limechain/chain/lightsyncstate/Authority.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package com.limechain.chain.lightsyncstate;

import com.limechain.teavm.annotation.Reflectable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.Serializable;
import java.math.BigInteger;

@Getter
@Setter
@Reflectable
@AllArgsConstructor
@NoArgsConstructor
public class Authority implements Serializable {
private final byte[] publicKey;
private final BigInteger weight;
private byte[] publicKey;
private BigInteger weight;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public static LightSyncState decode(Map<String, String> lightSyncStateMap) {
lightSyncState.grandpaAuthoritySet = new AuthoritySetReader()
.read(new ScaleCodecReader(StringUtils.hexToBytes(grandpaAuthoritySet)));

System.out.println(lightSyncState);
return lightSyncState;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import com.limechain.network.protocol.blockannounce.scale.BlockHeaderScaleWriter;
import com.limechain.polkaj.Hash256;
import com.limechain.utils.HashUtils;
import com.limechain.utils.StringUtils;
import com.limechain.utils.scale.ScaleUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.teavm.jso.core.JSString;

import java.math.BigInteger;
import java.util.Arrays;
Expand Down Expand Up @@ -37,6 +40,7 @@ public String toString() {

public Hash256 getHash() {
byte[] scaleEncoded = ScaleUtils.Encode.encode(BlockHeaderScaleWriter.getInstance(), this);
return null;//new Hash256(HashUtils.hashWithBlake2b(scaleEncoded));
JSString jsString = HashUtils.hashWithBlake2b(StringUtils.toHex(scaleEncoded));
return new Hash256(StringUtils.hexToBytes(jsString.stringValue()));
}
}
1 change: 1 addition & 0 deletions src/main/java/com/limechain/storage/DBConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class DBConstants {
public static final String AUTHORITY_SET = "ss::authoritySet";
public static final String LATEST_ROUND = "ss::latestRound";
public static final String SET_ID = "ss::setId";
public static final String STATE_ROOT = "ss::stateRoot";

//</editor-fold>
}
7 changes: 7 additions & 0 deletions src/main/java/com/limechain/storage/block/SyncState.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class SyncState {
private final BigInteger startingBlock;
private final Hash256 genesisBlockHash;
private Hash256 lastFinalizedBlockHash;
private Hash256 stateRoot;
@Setter
private Authority[] authoritySet;
private BigInteger latestRound;
Expand All @@ -41,6 +42,8 @@ private void loadState() {
DBConstants.LAST_FINALIZED_BLOCK_NUMBER, BigInteger.class).orElse(BigInteger.ZERO);
this.lastFinalizedBlockHash = new Hash256(LocalStorage.find(
DBConstants.LAST_FINALIZED_BLOCK_HASH, byte[].class).orElse(genesisBlockHash.getBytes()));
byte[] stateRootBytes = LocalStorage.find(DBConstants.STATE_ROOT, byte[].class).orElse(null);
this.stateRoot = stateRootBytes != null ? new Hash256(stateRootBytes) : null;
this.authoritySet = LocalStorage.find(DBConstants.AUTHORITY_SET, Authority[].class).orElse(new Authority[0]);
this.latestRound = LocalStorage.find(DBConstants.LATEST_ROUND, BigInteger.class).orElse(BigInteger.ONE);
this.setId = LocalStorage.find(DBConstants.SET_ID, BigInteger.class).orElse(BigInteger.ZERO);
Expand All @@ -52,17 +55,21 @@ public void persistState() {
LocalStorage.save(DBConstants.AUTHORITY_SET, authoritySet);
LocalStorage.save(DBConstants.LATEST_ROUND, latestRound);
LocalStorage.save(DBConstants.SET_ID, setId);
LocalStorage.save(DBConstants.STATE_ROOT, stateRoot.getBytes());
}

public void finalizeHeader(BlockHeader header) {
this.lastFinalizedBlockNumber = header.getBlockNumber();
this.lastFinalizedBlockHash = header.getHash();
this.stateRoot = header.getStateRoot();
}

public void finalizedCommitMessage(CommitMessage commitMessage) {
try {
this.lastFinalizedBlockHash = commitMessage.getVote().getBlockHash();
this.lastFinalizedBlockNumber = commitMessage.getVote().getBlockNumber();
this.setId = commitMessage.getSetId();
this.latestRound = commitMessage.getRoundNumber();
} catch (HeaderNotFoundException ignored) {
log.fine("Received commit message for a block that is not in the block store");
}
Expand Down
96 changes: 43 additions & 53 deletions src/main/java/com/limechain/sync/JustificationVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.limechain.chain.lightsyncstate.Authority;
import com.limechain.network.protocol.warp.dto.Precommit;
import com.limechain.polkaj.Hash256;
import com.limechain.polkaj.Hash512;
import com.limechain.rpc.server.AppBean;
import com.limechain.storage.block.SyncState;
import com.limechain.utils.LittleEndianUtils;
Expand All @@ -18,6 +17,14 @@
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -60,9 +67,11 @@ public static boolean verify(Precommit[] precommits, BigInteger round) {

byte[] data = getDataToVerify(precommit, authoritiesSetId, round);

boolean isValid =
verifySignature(precommit.getAuthorityPublicKey().toString(), precommit.getSignature().toString(),
data);
boolean isValid = verifySignature(
StringUtils.toHex(precommit.getAuthorityPublicKey().getBytes()),
StringUtils.toHex(precommit.getSignature().getBytes()),
StringUtils.toHex(data));

if (!isValid) {
log.log(Level.WARNING, "Failed to verify signature");
return false;
Expand All @@ -77,6 +86,32 @@ public static boolean verify(Precommit[] precommits, BigInteger round) {
return true;
}

private static boolean verifySignature(String publicKeyHex, String signatureHex,
String messageHex) {
JSPromise<JSBoolean> verifyAsync = verifyAsync(publicKeyHex, signatureHex, messageHex);
Object lock = new Object();
AtomicBoolean valid = new AtomicBoolean(false);

verifyAsync.then((isValid) -> {
synchronized (lock) {
valid.set(isValid.booleanValue());
lock.notify();
}
return null;
});

boolean isValid;
synchronized (lock) {
try {
lock.wait();
isValid = valid.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return isValid;
}

private static byte[] getDataToVerify(Precommit precommit, BigInteger authoritiesSetId, BigInteger round) {
// 1 reserved byte for data type
// 32 reserved for target hash
Expand Down Expand Up @@ -106,53 +141,8 @@ private static byte[] getDataToVerify(Precommit precommit, BigInteger authoritie
return data;
}

public static boolean verifySignature(String publicKeyHex, String signatureHex, byte[] data) {
String message = StringUtils.toHex(data);
AtomicBoolean verifier = new AtomicBoolean(false);
Object lock = new Object();

verifySignature(publicKeyHex, signatureHex, message).then(isValid -> {
synchronized (lock) {
verifier.set(isValid.booleanValue());
lock.notify();
}
return null;
});

synchronized (lock) {
try {
lock.wait();

boolean result = verifier.get();
if (!result) {
log.log(Level.WARNING, "Invalid signature");
}
return result;
} catch (InterruptedException e) {
log.log(Level.WARNING, "Interrupted while waiting for signature verification");
return false;
}
}
}

@JSBody(params = {"publicKeyHex", "signatureHex", "messageHex"},
script = "return (async () => {" +
" const publicKeyBytes = new Uint8Array([...publicKeyHex.matchAll(/../g)].map(m => parseInt(m[0], 16)));" +
" const signatureBytes = new Uint8Array([...signatureHex.matchAll(/../g)].map(m => parseInt(m[0], 16)));" +
" const publicKey = await crypto.subtle.importKey(" +
" 'raw'," + " publicKeyBytes," +
" {" + " name: 'NODE-ED25519'," +
" namedCurve: 'ed25519'" + " }," +
" true," + " ['verify']" + " );" +
" const messageBytes = new Uint8Array([...messageHex.matchAll(/../g)].map(m => parseInt(m[0], 16)));;" +
" const isValid = await crypto.subtle.verify(" +
" {" + " name: 'NODE-ED25519'" +
" }," + " publicKey," +
" signatureBytes," +
" messageBytes" + " );" +
" return isValid;" +

"})()")
public static native JSPromise<JSBoolean> verifySignature(String publicKeyHex, String signatureHex,
String messageHex);
@JSBody(params = {"publicKeyHex", "signatureHex",
"messageHex"}, script = "return verifyAsync(signatureHex, messageHex, publicKeyHex);")
public static native JSPromise<JSBoolean> verifyAsync(String publicKeyHex, String signatureHex,
String messageHex);
}
16 changes: 9 additions & 7 deletions src/main/java/com/limechain/sync/warpsync/WarpSyncMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,19 @@ public boolean isSyncing() {
}

public void start() {
if (this.chainService.getChainSpec().getLightSyncState() != null) {
LightSyncState initState = LightSyncState.decode(this.chainService.getChainSpec().getLightSyncState());
if (this.syncState.getLastFinalizedBlockNumber()
.compareTo(initState.getFinalizedBlockHeader().getBlockNumber()) < 0) {
this.syncState.setLightSyncState(initState);
}
LightSyncState initState = LightSyncState.decode(this.chainService.getChainSpec().getLightSyncState());

if (this.syncState.getLastFinalizedBlockNumber()
.compareTo(initState.getFinalizedBlockHeader().getBlockNumber()) < 0) {
this.syncState.setLightSyncState(initState);
}
System.out.println(this.syncState.getLastFinalizedBlockHash());
System.out.println(this.syncState.getLastFinalizedBlockNumber());

final Hash256 initStateHash = this.syncState.getLastFinalizedBlockHash();

// Always start with requesting fragments
log.log(Level.INFO, "Requesting fragments... " + initStateHash.toString());
log.log(Level.INFO, "Requesting fragments... " + initStateHash);
this.networkService.updateCurrentSelectedPeerWithNextBootnode();
this.warpSyncAction = new RequestFragmentsAction(initStateHash);

Expand Down
23 changes: 8 additions & 15 deletions src/main/java/com/limechain/utils/HashUtils.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
package com.limechain.utils;

import com.limechain.polkaj.Hash256;
import lombok.experimental.UtilityClass;
import org.teavm.jso.JSBody;
import org.teavm.jso.core.JSString;

@UtilityClass
public class HashUtils {
// public static final int HASH256_HASH_LENGTH = Hash256.SIZE_BYTES * Byte.SIZE;
//
// /**
// * Conducts a 256-bit Blake2b hash.
// * @param input the data to be hashed.
// * @return byte array containing the 256-bit hash result.
// */
// public static byte[] hashWithBlake2b(byte[] input) {
// Blake2bDigest digest = new Blake2bDigest(HASH256_HASH_LENGTH);
// digest.reset();
// digest.update(input, 0, input.length);
// byte[] hash = new byte[digest.getDigestSize()];
// digest.doFinal(hash, 0);
// return hash;
// }

@JSBody(params = {"inputHex"}, script = "{" +
"let bytes = new Uint8Array([...inputHex.matchAll(/../g)].map(m => parseInt(m[0], 16)));" +
"return Blake2b.hash(bytes,undefined,32);" + "}")
public static native JSString hashWithBlake2b(String inputHex);
}
2 changes: 2 additions & 0 deletions src/main/webapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
<script src="https://unpkg.com/it-pipe/dist/index.min.js"></script>
<script src="https://unpkg.com/it-pb-stream/dist/index.min.js"></script>
<script src=""></script>
<script type="text/javascript" charset="utf-8" src="js/blake2b.js"></script>
<script type="text/javascript" charset="utf-8" src="js/ed25519.js"></script>
<script type="text/javascript" charset="utf-8" src="js/fruzhin.js"></script>
<script type="text/javascript" charset="utf-8" src="js/http.js"></script>
</head>
Expand Down
Loading

0 comments on commit e831a69

Please sign in to comment.