Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement Pairing API #3

Open
wants to merge 81 commits into
base: kdf
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
0e8b8c3
implement rpc methods
borngraced Aug 30, 2024
900f72f
implement pairing-uri generation from pairing
borngraced Aug 30, 2024
3096073
save dev state
borngraced Aug 30, 2024
9679ee4
save dev state
borngraced Aug 30, 2024
9f73ef1
implement pairing unit test
borngraced Sep 1, 2024
3ab024c
scope mutex guard operation
borngraced Sep 1, 2024
dedf579
move rpc related to rpc crate and some refactorings
borngraced Sep 2, 2024
84febe9
git add files
borngraced Sep 2, 2024
0fe5eff
use async mutex
borngraced Sep 2, 2024
b13def3
minor changes
borngraced Sep 2, 2024
1d540af
minor changes and add doc comment
borngraced Sep 3, 2024
7079def
add more unit tests
borngraced Sep 3, 2024
43057cc
minor changes
borngraced Sep 3, 2024
548a821
cargo clippy
borngraced Sep 3, 2024
c3d8f4e
improve pairing client implementation
borngraced Sep 4, 2024
b4f8b43
use expected message_id for wallet response
borngraced Sep 4, 2024
ab51d8e
print connectino uri
borngraced Sep 4, 2024
656d945
implement pair method
borngraced Sep 4, 2024
1bfb7e2
cargo fmt
borngraced Sep 4, 2024
36e015b
commit uri_parser.rs
borngraced Sep 4, 2024
d0d5118
create common lib
borngraced Sep 5, 2024
8c0c215
commit pairing, uri mod
borngraced Sep 5, 2024
2395848
minor changes
borngraced Sep 6, 2024
e9107b7
cargo clippy fmt
borngraced Sep 6, 2024
f4db2a3
improve pairing code
borngraced Sep 6, 2024
528219f
impl session params
borngraced Sep 6, 2024
ddaf123
commit session files
borngraced Sep 6, 2024
0926bcc
save dev state
borngraced Sep 9, 2024
b955bbb
save dev state
borngraced Sep 9, 2024
3f82d7b
commit wc_common
borngraced Sep 9, 2024
f57b631
refactoring request and response params
borngraced Sep 11, 2024
6757cda
update expiry derivation
borngraced Sep 11, 2024
a85bc6a
minor changes
borngraced Sep 16, 2024
1f1044a
minor changes and fix failing tests
borngraced Sep 16, 2024
76bdbad
delegate pairing topic subscription to client
borngraced Sep 16, 2024
c8f06e0
minor changes
borngraced Sep 18, 2024
8efb1da
fix review notes
borngraced Sep 18, 2024
37c929d
fix cargo clippy
borngraced Sep 19, 2024
71acc1a
use idiomatic code for sym_key gen
borngraced Sep 20, 2024
98211ed
use macro for default impl in PairingClient struct
borngraced Sep 20, 2024
f7bde84
don't append methods to url if empty
borngraced Sep 23, 2024
6740313
Merge branch 'kdf' of github.com:KomodoPlatform/WalletConnectRust int…
borngraced Sep 23, 2024
2af7e1a
fix generate url fn
borngraced Sep 23, 2024
3bfcc41
fix connection uri generator fn
borngraced Sep 26, 2024
c02d999
use Default to initialize PairingClient and minor fix
borngraced Oct 1, 2024
14e9726
fix review notes
borngraced Oct 9, 2024
052ea1d
fix clippy
borngraced Oct 10, 2024
17b1e31
fix disconnect bug
borngraced Oct 10, 2024
974907e
minor changes
borngraced Oct 10, 2024
9836e93
fix pairing delete
borngraced Oct 10, 2024
8175331
use rustls for tokio wss
borngraced Oct 10, 2024
61add9a
remove tokio-rustls dep
borngraced Oct 10, 2024
3ea808b
add sessionProperties to SettleSettle structure
borngraced Oct 11, 2024
91cb115
abstract timestamp check into a function
borngraced Oct 16, 2024
423c67c
revert mod import deletion
borngraced Oct 16, 2024
fd8c7be
extend Params enums with Arbitrary response variant and fix review notes
borngraced Oct 24, 2024
994db68
fix sym_key len check
borngraced Oct 24, 2024
174d386
fix review notes and add caip validation for settle namespaces
borngraced Oct 27, 2024
a271959
export connection_event_loop
borngraced Oct 31, 2024
5c974ad
handle pairing SymKey exporting properly/securely
borngraced Nov 4, 2024
f766404
fix pairing test
borngraced Nov 4, 2024
b427ff0
cargo fmt!!
borngraced Nov 4, 2024
2372fa5
refactor Client new_unmanged and update rand crate
borngraced Nov 6, 2024
90d4008
add doc to PairingClient struct and explicity take Topic as arg
borngraced Nov 8, 2024
f9d9952
handle possible out of bounds error
borngraced Nov 8, 2024
b8040e1
rename expiry, reuse sym_key
borngraced Nov 9, 2024
a7e7339
use DashMap for perfomance
borngraced Nov 9, 2024
cdf38a5
use try_fill_bytes
borngraced Nov 9, 2024
4e09437
debug ws socket dirty close
borngraced Nov 11, 2024
d643b29
fix doc test
borngraced Nov 11, 2024
4a78c4b
debug connection closed
borngraced Nov 12, 2024
95da428
debug ws send
borngraced Nov 12, 2024
9b78f48
debug
borngraced Nov 12, 2024
edd96c9
remove debug log
borngraced Nov 12, 2024
d208b54
nits
borngraced Nov 12, 2024
aab0f30
reuse activate pairing fn
borngraced Nov 19, 2024
eb384d6
fix review notes
borngraced Nov 27, 2024
848d100
don't build example features
borngraced Nov 27, 2024
897cf78
replace dashmap with RwLock
borngraced Nov 28, 2024
99674a0
fix cargo clippy
borngraced Nov 28, 2024
0bd862f
debug ci
borngraced Nov 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

.idea
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,26 @@ authors = ["WalletConnect Team"]
license = "Apache-2.0"

[workspace]
members = ["blockchain_api", "relay_client", "relay_rpc"]
members = [
"blockchain_api",
"wc_common",
"pairing_api",
"relay_client",
"relay_rpc",
]

[features]
default = ["full"]
full = ["client", "rpc", "http"]
client = ["dep:relay_client"]
http = ["relay_client/http"]
rpc = ["dep:relay_rpc"]
pairing_api = ["dep:pairing_api"]

[dependencies]
relay_client = { path = "./relay_client", optional = true }
relay_rpc = { path = "./relay_rpc", optional = true }
pairing_api = { path = "./pairing_api", optional = true }

[dev-dependencies]
anyhow = "1"
Expand Down
41 changes: 41 additions & 0 deletions pairing_api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[package]
name = "pairing_api"
version = "0.1.0"
edition = "2021"

[dependencies]
chrono = { version = "0.4", default-features = false, features = [
"std",
"clock",
] }
anyhow = "1.0.86"
hex = "0.4.2"
lazy_static = "1.4"
paste = "1.0.15"
rand = "0.8"
regex = "1.7"
relay_client = { path = "../relay_client" }
relay_rpc = { path = "../relay_rpc" }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] }
structopt = { version = "0.3", default-features = false }
shamardy marked this conversation as resolved.
Show resolved Hide resolved
thiserror = "1.0"
url = "2.3"
wc_common = { path = "../wc_common" }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.22", features = [
"rt",
"rt-multi-thread",
"sync",
"macros",
"time",
"signal",
] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { version = "1.22", features = ["sync", "macros"] }


[[example]]
name = "pairing"
216 changes: 216 additions & 0 deletions pairing_api/examples/pairing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use {
pairing_api::{PairingClient, PairingClientError},
relay_client::{
error::ClientError,
websocket::{Client, CloseFrame, ConnectionHandler, PublishedMessage},
ConnectionOptions,
},
relay_rpc::{
auth::{ed25519_dalek::SigningKey, AuthToken},
domain::Topic,
rpc::{params::ResponseParamsSuccess, Params, Payload},
},
std::{sync::Arc, time::Duration},
structopt::StructOpt,
tokio::{
signal,
spawn,
sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
},
wc_common::decode_and_decrypt_type0,
};

#[derive(StructOpt)]
struct Args {
/// Specify WebSocket address.
#[structopt(short, long, default_value = "wss://relay.walletconnect.com")]
address: String,

/// Specify WalletConnect project ID.
#[structopt(short, long, default_value = "1979a8326eb123238e633655924f0a78")]
project_id: String,
}

struct Handler {
name: &'static str,
sender: UnboundedSender<PublishedMessage>,
}

fn create_conn_opts(address: &str, project_id: &str) -> ConnectionOptions {
let key = SigningKey::generate(&mut rand::thread_rng());

let auth = AuthToken::new("http://127.0.0.1:8000")
.aud(address)
.ttl(Duration::from_secs(60 * 60))
.as_jwt(&key)
.unwrap();

ConnectionOptions::new(project_id, auth).with_address(address)
}

impl Handler {
fn new(name: &'static str, sender: UnboundedSender<PublishedMessage>) -> Self {
Self { name, sender }
}
}

impl ConnectionHandler for Handler {
fn connected(&mut self) {
println!("[{}] connection open", self.name);
}

fn disconnected(&mut self, frame: Option<CloseFrame<'static>>) {
println!("[{}] connection closed: frame={frame:?}", self.name);
}

fn message_received(&mut self, message: PublishedMessage) {
println!(
"[{}] inbound message: topic={} message={}",
self.name, message.topic, message.message
);

if let Err(err) = self.sender.send(message.clone()) {
println!("error {err:?} while sending {message:?}");
};
}

fn inbound_error(&mut self, error: ClientError) {
println!("[{}] inbound error: {error}", self.name);
}

fn outbound_error(&mut self, error: ClientError) {
println!("[{}] outbound error: {error}", self.name);
}
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::from_args();
let (sender, receiver) = unbounded_channel();
let client1 = Arc::new(Client::new(Handler::new("client1", sender)));
client1
.connect(&create_conn_opts(&args.address, &args.project_id))
.await?;

let pairing_client = Arc::new(PairingClient::new());
// Create Pairing.
// let topic = create_pairing(&pairing_client).await;
// Pair
let topic = pair_from_uri(&pairing_client, &client1).await;
// Subscribe to the pairing topic
println!("\nSubscribing to topic: {}", topic);
client1
.subscribe(topic.clone())
.await
.map_err(PairingClientError::SubscriptionError)?;
borngraced marked this conversation as resolved.
Show resolved Hide resolved
println!("\nSuccessfully subscribed to topic: {:?}", topic);

let key = pairing_client.sym_key(topic.as_ref()).await.unwrap();
let receiver_handle = spawn(spawn_published_message_recv_loop(
client1,
pairing_client,
receiver,
key,
));

// Keep the main task running
tokio::select! {
_ = signal::ctrl_c() => {
println!("Received Ctrl+C, shutting down");
}
_ = receiver_handle => {
println!("Receiver loop ended");
}
};

Ok(())
}

async fn spawn_published_message_recv_loop(
client: Arc<Client>,
pairing_client: Arc<PairingClient>,
mut recv: UnboundedReceiver<PublishedMessage>,
key: String,
) {
while let Some(msg) = recv.recv().await {
let topic = msg.topic.to_string();
let key = hex::decode(key.clone()).unwrap();
let message = decode_and_decrypt_type0(msg.message.as_bytes(), &key).unwrap();
println!("\nInbound message payload={message}");

let response = serde_json::from_str::<Payload>(&message).unwrap();
match response {
Payload::Request(request) => match request.params {
Params::PairingDelete(_) => {
// send a success response back to wc.
let delete_request = ResponseParamsSuccess::PairingDelete(true);
pairing_client
.publish_response(&topic, delete_request, request.id, &client)
.await
.unwrap();
// send a request to delete pairing from store.
pairing_client.delete(&topic).await;
}
Params::PairingExtend(data) => {
let extend_request = ResponseParamsSuccess::PairingExtend(true);
// send a success response back to wc.
pairing_client
.publish_response(&topic, extend_request, request.id, &client)
.await
.unwrap();
// send a request to update pairing expiry in store.
pairing_client.update_expiry(&topic, data.expiry).await;
}
Params::PairingPing(_) => {
let ping_request = ResponseParamsSuccess::PairingPing(true);
// send a success response back to wc.
pairing_client
.publish_response(&topic, ping_request, request.id, &client)
.await
.unwrap();
}
_ => unimplemented!(),
},
Payload::Response(value) => {
println!("Response: {value:?}");
}
}
}
}

/// For a session Controller to pair with a session
async fn pair_from_uri(pairing_client: &PairingClient, client: &Client) -> Topic {
let topic = pairing_client
.pair(
"wc:
borngraced marked this conversation as resolved.
Show resolved Hide resolved
b99c41b1219a6c3131f2960e64cc015900b6880b49470e43bf14e9e520bd922d@2?
expiryTimestamp=1725467415&relay-protocol=irn&
symKey=4a7cccd69a33ac0a3debfbee49e8ff0e65edbdc2031ba600e37880f73eb5b638",
true,
)
.await
.unwrap();
client.subscribe(topic.clone()).await.unwrap();
topic
}

/// For a Session Proposer to create a pairing connection url.
#[allow(unused)]
async fn create_pairing(pairing_client: &PairingClient) -> Topic {
let metadata = relay_rpc::rpc::params::Metadata {
description: "A decentralized application that enables secure
communication and transactions."
.to_string(),
url: "https://127.0.0.1:3000".to_string(),
icons: vec![
"https://example-dapp.com/icon1.png".to_string(),
"https://example-dapp.com/icon2.png".to_string(),
],
name: "Example DApp".to_string(),
};

let (topic, uri) = pairing_client.create(metadata, None).await.unwrap();
println!("pairing_uri: {uri}");

topic
}
4 changes: 4 additions & 0 deletions pairing_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod pairing;
mod uri;

pub use {pairing::*, uri::Methods};
Loading
Loading