Skip to content

Commit

Permalink
Merge branch 'dev' into #17-sync-substream
Browse files Browse the repository at this point in the history
  • Loading branch information
Boris Velkovski authored and Boris Velkovski committed Apr 12, 2023
2 parents bd91790 + 17d598a commit b11292b
Show file tree
Hide file tree
Showing 11 changed files with 12,228 additions and 4 deletions.
16 changes: 12 additions & 4 deletions src/main/java/com/limechain/network/Network.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.limechain.config.HostConfig;
import com.limechain.network.kad.KademliaService;
import com.limechain.network.protocol.sync.SyncService;
import com.limechain.network.substream.lightclient.LightMessagesService;
import io.ipfs.multihash.Multihash;
import io.libp2p.core.Host;
import io.libp2p.protocol.Ping;
Expand Down Expand Up @@ -33,7 +34,8 @@ public class Network {
private static final int HOST_PORT = 1001;
private static Network network;
public SyncService syncService;
public static KademliaService kademliaService;
public LightMessagesService lightMessagesService;
public KademliaService kademliaService;
private HostBuilder hostBuilder;
private Host host;

Expand All @@ -44,17 +46,23 @@ public class Network {
* Connects Kademlia to boot nodes
*
* @param chainService chain specification information containing boot nodes
* @param hostConfig host configuration containing current network
* @param hostConfig host configuration containing current network
*/
private Network(ChainService chainService, HostConfig hostConfig) {
boolean isLocalEnabled = hostConfig.getChain() == Chain.LOCAL;
hostBuilder = (new HostBuilder()).generateIdentity().listenLocalhost(HOST_PORT);
Multihash hostId = Multihash.deserialize(hostBuilder.getPeerId().getBytes());

kademliaService = new KademliaService("/dot/kad", hostId, isLocalEnabled);
hostBuilder.addProtocols(List.of(new Ping(), kademliaService.getDht()));

lightMessagesService = new LightMessagesService();
syncService = new SyncService();

hostBuilder.addProtocols(List.of(new Ping(), kademliaService.getDht(), syncService.getSyncMessages()));
hostBuilder.addProtocols(
List.of(new Ping(), kademliaService.getDht(),
lightMessagesService.getLightMessages(),
syncService.getSyncMessages()
));

host = hostBuilder.build();
kademliaService.setHost(host);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/limechain/network/kad/KademliaService.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public void findNewPeers() {

/**
* Makes a dns lookup and changes the address to an equal ip4 address
* Implementation is necessary due to a bug in jvm-libp2p that involves resolving dns addresses
* https://github.com/Peergos/nabu/issues/22#issuecomment-1495687079
*
* @param bootNode
* @return bootNode in ip4 format
Expand Down
121 changes: 121 additions & 0 deletions src/main/java/com/limechain/network/protobuf/LightClientMessage.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
syntax = "proto3";

package com.limechain.network.substream.lightclient.pb;
option java_package = "com.limechain.network.substream.lightclient.pb";

// Schema definition for light client messages.
// Copied from https://github.com/paritytech/substrate/blob/9b08105b8c7106d723c4f470304ad9e2868569d9/client/network/src/schema/light.v1.proto

// A pair of arbitrary bytes.
message Pair {
// The first element of the pair.
bytes fst = 1;
// The second element of the pair.
bytes snd = 2;
}

// Enumerate all possible light client request messages.
message Request {
oneof request {
RemoteCallRequest remote_call_request = 1;
RemoteReadRequest remote_read_request = 2;
RemoteHeaderRequest remote_header_request = 3;
RemoteReadChildRequest remote_read_child_request = 4;
RemoteChangesRequest remote_changes_request = 5;
}
}

// Enumerate all possible light client response messages.
message Response {
oneof response {
RemoteCallResponse remote_call_response = 1;
RemoteReadResponse remote_read_response = 2;
RemoteHeaderResponse remote_header_response = 3;
RemoteChangesResponse remote_changes_response = 4;
}
}

// Remote call request.
message RemoteCallRequest {
// Block at which to perform call.
bytes block = 2;
// Method name.
string method = 3;
// Call data.
bytes data = 4;
}

// Remote call response.
message RemoteCallResponse {
// Execution proof.
bytes proof = 2;
}

// Remote storage read request.
message RemoteReadRequest {
// Block at which to perform call.
bytes block = 2;
// Storage keys.
repeated bytes keys = 3;
}

// Remote read response.
message RemoteReadResponse {
// Read proof.
bytes proof = 2;
}

// Remote storage read child request.
message RemoteReadChildRequest {
// Block at which to perform call.
bytes block = 2;
// Child Storage key, this is relative
// to the child type storage location.
bytes storage_key = 3;
// Storage keys.
repeated bytes keys = 6;
}

// Remote header request.
message RemoteHeaderRequest {
// Block number to request header for.
bytes block = 2;
}

// Remote header response.
message RemoteHeaderResponse {
// Header. None if proof generation has failed (e.g. header is unknown).
bytes header = 2; // optional
// Header proof.
bytes proof = 3;
}

/// Remote changes request.
message RemoteChangesRequest {
// Hash of the first block of the range (including first) where changes are requested.
bytes first = 2;
// Hash of the last block of the range (including last) where changes are requested.
bytes last = 3;
// Hash of the first block for which the requester has the changes trie root. All other
// affected roots must be proved.
bytes min = 4;
// Hash of the last block that we can use when querying changes.
bytes max = 5;
// Storage child node key which changes are requested.
bytes storage_key = 6; // optional
// Storage key which changes are requested.
bytes key = 7;
}

// Remote changes response.
message RemoteChangesResponse {
// Proof has been generated using block with this number as a max block. Should be
// less than or equal to the RemoteChangesRequest::max block number.
bytes max = 2;
// Changes proof.
repeated bytes proof = 3;
// Changes tries roots missing on the requester' node.
repeated Pair roots = 4;
// Missing changes tries roots proof.
bytes roots_proof = 5;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.limechain.network.substream.lightclient;

import com.limechain.network.substream.lightclient.pb.LightClientMessage;
import io.libp2p.core.AddressBook;
import io.libp2p.core.Host;
import io.libp2p.core.PeerId;
import io.libp2p.core.multiformats.Multiaddr;
import io.libp2p.core.multistream.StrictProtocolBinding;
import lombok.extern.java.Log;

import java.util.concurrent.ExecutionException;
import java.util.logging.Level;

@Log
public class LightMessages extends StrictProtocolBinding<LightMessagesController> {
public LightMessages(LightMessagesProtocol protocol) {
super("/dot/light/2", protocol);

}

public LightClientMessage.Response remoteCallRequest(Host us, AddressBook addrs, PeerId peer,
String blockHash,
String method,
String data) {
LightMessagesController controller = dialPeer(us, peer, addrs);
try {
LightClientMessage.Response resp = controller.remoteCallRequest(blockHash, method, data).get();
log.log(Level.INFO, "Received response: " + resp.toString());
return resp;
} catch (ExecutionException | InterruptedException e) {
log.log(Level.SEVERE, "Error while sending remote call request: ", e);
throw new RuntimeException(e);
}
}

public LightClientMessage.Response remoteReadRequest(Host us, AddressBook addrs, PeerId peer,
String blockHash,
String[] keys) {
LightMessagesController controller = dialPeer(us, peer, addrs);
try {
LightClientMessage.Response resp = controller.remoteReadRequest(blockHash, keys).get();
log.log(Level.INFO, "Received response: " + resp.toString());
return resp;
} catch (ExecutionException | InterruptedException e) {
log.log(Level.SEVERE, "Error while sending remote call request: ", e);
throw new RuntimeException(e);
}
}

public LightClientMessage.Response remoteReadChildRequest(Host us, AddressBook addrs, PeerId peer,
String blockHash,
String childStorageKey,
String[] keys) {
LightMessagesController controller = dialPeer(us, peer, addrs);
try {
LightClientMessage.Response resp =
controller.remoteReadChildRequest(blockHash, childStorageKey, keys).get();
log.log(Level.INFO, "Received response: " + resp.toString());
return resp;
} catch (ExecutionException | InterruptedException e) {
log.log(Level.SEVERE, "Error while sending remote call request: ", e);
throw new RuntimeException(e);
}
}

private LightMessagesController dialPeer(Host us, PeerId peer, AddressBook addrs) {
Multiaddr[] addr = addrs.get(peer).join().toArray(new Multiaddr[0]);
if (addr.length == 0)
throw new IllegalStateException("No addresses known for peer " + peer);

return dial(us, peer, addr).getController().join();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.limechain.network.substream.lightclient;

import com.google.protobuf.ByteString;
import com.limechain.network.substream.lightclient.pb.LightClientMessage;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public interface LightMessagesController {
CompletableFuture<LightClientMessage.Response> send(LightClientMessage.Request msg);

default CompletableFuture<LightClientMessage.Response> remoteCallRequest(String blockHash,
String methodName,
String callData) {
return send(LightClientMessage.Request
.newBuilder()
.setRemoteCallRequest(
LightClientMessage.RemoteCallRequest
.newBuilder()
.setBlock(ByteString.copyFrom(blockHash.getBytes()))
.setMethod(methodName)
.setData(ByteString.copyFrom(callData.getBytes()))
.build()
)
.build());
}

default CompletableFuture<LightClientMessage.Response> remoteReadRequest(String blockHash,
String[] storageKeys) {

return send(LightClientMessage.Request
.newBuilder()
.setRemoteReadRequest(
LightClientMessage.RemoteReadRequest
.newBuilder()
.setBlock(ByteString.copyFrom(blockHash.getBytes()))
.addAllKeys(
Arrays.stream(storageKeys)
.map(s -> ByteString.copyFrom(s.getBytes()))
.collect(Collectors.toList())
)
.build()
)
.build());

}

default CompletableFuture<LightClientMessage.Response> remoteReadChildRequest(String blockHash,
String childStorageKey,
String[] keys) {
return send(LightClientMessage.Request
.newBuilder()
.setRemoteReadChildRequest(
LightClientMessage.RemoteReadChildRequest
.newBuilder()
.setBlock(ByteString.copyFrom(blockHash.getBytes()))
.setStorageKey(ByteString.copyFrom(childStorageKey.getBytes()))
.addAllKeys(
Arrays.stream(keys)
.map(s -> ByteString.copyFrom(s.getBytes()))
.collect(Collectors.toList())
)
.build()
)
.build());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.limechain.network.substream.lightclient;

import com.google.protobuf.ByteString;
import com.limechain.network.substream.lightclient.pb.LightClientMessage;
import io.libp2p.core.Stream;
import lombok.extern.java.Log;

import java.util.logging.Level;

@Log
public class LightMessagesEngine {

// TODO: Add logic for handling incoming each request type
public void receiveRequest(LightClientMessage.Request msg, Stream stream) {
var builder = LightClientMessage.Response.newBuilder();
if (msg.hasRemoteCallRequest()) {
log.log(Level.INFO, "Received: RemoteCallRequest");
builder.setRemoteCallResponse(
LightClientMessage.RemoteCallResponse.newBuilder()
.setProof(ByteString.copyFrom("0x0".getBytes()))
.build());
} else if (msg.hasRemoteReadRequest()) {
log.log(Level.INFO, "Received: RemoteReadRequest");
builder.setRemoteReadResponse(
LightClientMessage.RemoteReadResponse.newBuilder()
.setProof(ByteString.copyFrom("0x1".getBytes()))
.build());
} else if (msg.hasRemoteReadChildRequest()) {
log.log(Level.INFO, "Received: RemoteReadChildRequest");
builder.setRemoteReadResponse(
LightClientMessage.RemoteReadResponse.newBuilder()
.setProof(ByteString.copyFrom("0x2".getBytes()))
.build());
}
stream.writeAndFlush(builder.build());
}
}
Loading

0 comments on commit b11292b

Please sign in to comment.