Skip to content

Commit

Permalink
Ability to dial a remote address and exchange identity
Browse files Browse the repository at this point in the history
  • Loading branch information
caolan committed Sep 23, 2024
1 parent ac0462f commit 74781e1
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 3 deletions.
9 changes: 9 additions & 0 deletions mutiny/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type MutinyRequest = {
};
export type MutinyRequestBody = {type: "LocalPeerId"}
| {type: "Peers"}
| {type: "DialAddress", address: string}
| {type: "AppAnnouncements"}
| {type: "GetLastPort", app_uuid: string}
| {type: "SetLastPort", app_uuid: string, port: number}
Expand Down Expand Up @@ -290,6 +291,14 @@ export class MutinyClient {
return response.peers;
}

async dialAddress(address: string): Promise<void> {
const response = await this.requestOne({
type: "DialAddress",
address,
});
assert(response.type === 'Success');
}

private _subscribe<R>(request: MutinyRequest): AsyncIterableIterator<R> {
const waiting = this.waiting;
let promise: Promise<MutinyResponseBody> = this.queueRequest(request);
Expand Down
15 changes: 15 additions & 0 deletions mutiny/src/commands/dial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defaultSocketPath } from "../client.ts";
import { parseArgs } from "@std/cli/parse-args";
import { help } from "./help.ts";
import { MutinyClient } from "../client.ts";

export default async function (args: ReturnType<typeof parseArgs>) {
if (args._.length < 1 || args.help) {
help('dial');
}
const socket_path = args.s || args.socket || defaultSocketPath();
const address = "" + args._[0];

const client = new MutinyClient({socket_path});
await client.dialAddress(address);
}
16 changes: 16 additions & 0 deletions mutiny/src/commands/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ export function listCommands() {
console.error("Commands:");
console.error(" serve Serve an application");
console.error(" info Show info about mutinyd");
console.error(" dial Connects to a known multi-address");
}

export function help(command?: string) {
switch (command) {
case "serve": {
console.error("Usage: mutiny serve [OPTIONS] LABEL PATH");
console.error("");
console.error("Arguments:");
console.error(" LABEL Label of app instance");
console.error(" PATH Path to static assets");
console.error("");
console.error("Options:");
console.error(" -s, --socket <SOCKET> Unix socket to bind to");
console.error(" --help Show this message");
Expand All @@ -22,6 +27,17 @@ export function help(command?: string) {
console.error(" --help Show this message");
break;
}
case "dial": {
console.error("Usage: mutiny dial [OPTIONS] ADDRESS");
console.error("");
console.error("Arguments:");
console.error(" ADDRESS libp2p multi-address to connect to");
console.error("");
console.error("Options:");
console.error(" -s, --socket <SOCKET> Unix socket to bind to");
console.error(" --help Show this message");
break;
}
default: {
console.error("Usage: mutiny COMMAND");
console.error("");
Expand Down
5 changes: 5 additions & 0 deletions mutiny/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { help, listCommands } from "./commands/help.ts";
import { parseArgs } from "@std/cli/parse-args";
import serve from "./commands/serve.ts";
import info from "./commands/info.ts";
import dial from "./commands/dial.ts";

if (import.meta.main) {
const args = parseArgs(Deno.args);
Expand All @@ -19,6 +20,10 @@ if (import.meta.main) {
info(args);
break;
}
case "dial": {
dial(args);
break;
}
default: {
console.error(`Unknown command: ${command}`);
console.error("");
Expand Down
25 changes: 25 additions & 0 deletions mutinyd/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mutinyd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"

[dependencies]
tokio = { version = "1.37.0", features = ["full"] }
libp2p = { version = "0.53.2", features = ["tcp", "quic", "tls", "dns", "noise", "yamux", "websocket", "ping", "macros", "tokio", "gossipsub", "mdns", "request-response", "cbor"] }
libp2p = { version = "0.53.2", features = ["tcp", "quic", "tls", "dns", "noise", "yamux", "websocket", "ping", "macros", "tokio", "gossipsub", "identify", "mdns", "request-response", "cbor"] }
serde = { version = "1.0.203", features = ["derive"] }
uuid = { version = "1.8.0", features = ["v4"] }
rusqlite = "0.31.0"
Expand Down
3 changes: 3 additions & 0 deletions mutinyd/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub enum RequestBody {
},
LocalPeerId,
Peers,
DialAddress {
address: String,
},
Announce {
peer: String,
app_uuid: String,
Expand Down
34 changes: 34 additions & 0 deletions mutinyd/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,30 @@ impl Server {
println!("Connection closed: {address}");
}
},
SwarmEvent::Dialing {..} => {
println!("Dialing...");
},
SwarmEvent::OutgoingConnectionError { error, .. } => {
println!("Outgoing connection error: {error}");
},
SwarmEvent::Behaviour(swarm::MutinyBehaviourEvent::Identify(ev)) => match ev {
// Identification information of the local node has been sent to a peer in response to an identification request.
libp2p::identify::Event::Sent { peer_id, .. } => {
println!("Sent identify info to {peer_id:?}")
},
// Identification information has been received from a peer.
libp2p::identify::Event::Received { info, .. } => {
println!("Received identify info {info:?}")
},
// Identification information of the local node has been actively pushed to a peer.
libp2p::identify::Event::Pushed { peer_id, .. } => {
println!("Pushed identify info to {peer_id:?}")
},
// Error while attempting to identify the remote.
libp2p::identify::Event::Error { peer_id, .. } => {
println!("Error identifying remote {peer_id:?}")
},
},
_ => {}
};
Ok(())
Expand Down Expand Up @@ -362,6 +386,16 @@ impl Server {
}
let _ = request.response.send(ResponseBody::Peers {peers}).await;
},
RequestBody::DialAddress {address} => {
let remote = address.parse::<Multiaddr>()?;
self.swarm.dial(remote)?;
// TODO: use connection id to track success or not of
// Indentify by checking for outgoing connection error,
// connection established, and identify sent/received events.
// TODO: use identify::Behaviour::push() to actively push
// identity info to peer after this dial request.
let _ = request.response.send(ResponseBody::Success).await;
},
RequestBody::AppAnnouncements => {
let tx = self.store.transaction()?;
let _ = request.response.send(ResponseBody::AppAnnouncements {
Expand Down
9 changes: 7 additions & 2 deletions mutinyd/src/swarm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use libp2p::{identity::Keypair, mdns, request_response::{self, ProtocolSupport}, swarm::{NetworkBehaviour, StreamProtocol}};
use libp2p::{identity::Keypair, identify, mdns, request_response::{self, ProtocolSupport}, swarm::{NetworkBehaviour, StreamProtocol}};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use std::error::Error;
Expand All @@ -25,6 +25,7 @@ pub enum Response {
#[derive(NetworkBehaviour)]
pub struct MutinyBehaviour {
pub request_response: request_response::cbor::Behaviour<Request, Response>,
pub identify: identify::Behaviour,
pub mdns: mdns::tokio::Behaviour,
}

Expand All @@ -47,11 +48,15 @@ pub async fn start(keypair: Keypair) -> Result<
[(StreamProtocol::new("/mutiny-request-response-protocol"), ProtocolSupport::Full)],
libp2p::request_response::Config::default(),
);
let identify = identify::Behaviour::new(identify::Config::new(
String::from("mutiny/1.0.0"),
key.public(),
));
// Find peers on local network using multicast DNS
let mdns = libp2p::mdns::tokio::Behaviour::new(
libp2p::mdns::Config::default(), key.public().to_peer_id()
)?;
Ok(MutinyBehaviour { request_response, mdns })
Ok(MutinyBehaviour { request_response, identify, mdns })
})?
.with_swarm_config(
|c| c.with_idle_connection_timeout(Duration::from_secs(60))
Expand Down

0 comments on commit 74781e1

Please sign in to comment.