Skip to content

Commit

Permalink
Add light client messaging protocol (#57)
Browse files Browse the repository at this point in the history
* Add nabu jar

* Add initial Kademlia bootstrap logic

* Replace existing Nabu jar with Nabu jar that has dependencies built into it

* Add network module

* Add network module

* Update nabu

* Refactor

* Remove finished TOTO

* Refactor long line

* Remove unused import

* Refactor time period to constant

* Update cli logic

* Add comma separator

* Rename constant

* Remove unused variable

* Refactor network module

* Change bootnode

* Remove westend-local

* Refactor DB

* Add loading local genesis only from json

* Add javadocs

* Remove unused import

* Refactor

* Add light client messaging protocol

* Remove unnecessary dependency

* Add base warp sync structure

* Remove unused annotations

* Fix build

* Resolve formatting issue

* Remove unrelated files

---------

Co-authored-by: Boris Velkovski <[email protected]>
  • Loading branch information
vikinatora and Boris Velkovski authored Apr 6, 2023
1 parent 9bba258 commit 17d598a
Show file tree
Hide file tree
Showing 10 changed files with 12,224 additions and 3 deletions.
13 changes: 10 additions & 3 deletions src/main/java/com/limechain/network/Network.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.limechain.chain.ChainService;
import com.limechain.config.HostConfig;
import com.limechain.network.kad.KademliaService;
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 @@ -31,7 +32,8 @@ public class Network {
private static final int TEN_SECONDS_IN_MS = 10000;
private static final int HOST_PORT = 1001;
private static Network network;
public static KademliaService kademliaService;
public LightMessagesService lightMessagesService;
public KademliaService kademliaService;
private HostBuilder hostBuilder;
private Host host;

Expand All @@ -42,14 +44,19 @@ 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();

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

host = hostBuilder.build();
kademliaService.setHost(host);
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 17d598a

Please sign in to comment.