-
Notifications
You must be signed in to change notification settings - Fork 18
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
Add gossipable trait + relevant logic #45
base: main
Are you sure you want to change the base?
Conversation
@Sanghren, thanks for the contribution! I took a quick look and I think things look pretty good. I'll leave a couple of comments next week! |
core/network/examples/peer_gossip.rs
Outdated
let n = socket.read(&mut buf).await.unwrap(); | ||
|
||
// Empty, wait 5 sec before next attempt | ||
if n == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When socket.read returns 0, that means the client has disconnected. You will forever get 0, so this is an infinite loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if the client disconnects, the read
call will error and the program will panic on that unwrap
. If wouldn't infinite loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not true. Example program: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d809a48e2983d4fe94833852a8a28961
Client drops the tcp_stream
on line 35, and waits for the server to finish up before exiting. While it's in this state, read() repeatedly returns Ok(0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool! Awesome job if some of those examples work!
I'm happy to keep reviewing your code and give you some Rust-specific pointers, but as @exdx mentioned, it might be tough getting this merged in as the P2P stuff isn't stable yet.
Right now, there's quite a bit of code that could panic in spots where you wouldn't want code panicking.
If you're just looking to learn, I'm happy to help as much as I can, but if it's contributions that you want, it might be a better idea to take a look at something smaller. In either case, I'm here to help!
@@ -12,25 +12,42 @@ readme = "README.md" | |||
|
|||
[dependencies] | |||
avalanche-types = { path = "../../crates/avalanche-types", features = ["message"] } | |||
async-trait = { version = "0.1.73", features = [] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔, usually libraries re-export async-trait
such that the trait definition remains consistent with the trait implementation.
I know I'll get my answer below, but are we defining traits with the macro or as we implementing traits with the macro? Or both?
[dev-dependencies] | ||
env_logger = "0.10.0" | ||
mockall = "0.11.4" | ||
proptest = "1.2.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
random-manager = "0.0.5" | ||
tokio = { version = "1.32.0", features = ["full"] } | ||
testing_logger = "0.1.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔, not sure we need this as it looks like it's unmaintained. I guess I'll see where it's used.
core/network/examples/peer_gossip.rs
Outdated
use std::error::Error; | ||
use std::hash::Hash; | ||
use tokio::sync::Mutex; | ||
use std::sync::Arc; | ||
use std::time::Duration; | ||
use async_trait::async_trait; | ||
use tokio::net::{TcpListener, TcpStream}; | ||
use tokio::io::{AsyncReadExt, AsyncWriteExt}; | ||
use network::p2p::gossip::gossip::{Config, Gossiper}; | ||
use tokio::sync::mpsc::{channel}; | ||
use avalanche_types::ids::Id; | ||
use network::p2p::client::{AppResponseCallback, Client}; | ||
use network::p2p::gossip::{Gossipable, Set}; | ||
use network::p2p::gossip::handler::{Handler, HandlerConfig, new_handler}; | ||
use network::p2p::handler::Handler as TraitHandler; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer merged imports as it's easier to keep them consistent when using automatic imports from tools like rust-analyzer
.
use std::error::Error; | |
use std::hash::Hash; | |
use tokio::sync::Mutex; | |
use std::sync::Arc; | |
use std::time::Duration; | |
use async_trait::async_trait; | |
use tokio::net::{TcpListener, TcpStream}; | |
use tokio::io::{AsyncReadExt, AsyncWriteExt}; | |
use network::p2p::gossip::gossip::{Config, Gossiper}; | |
use tokio::sync::mpsc::{channel}; | |
use avalanche_types::ids::Id; | |
use network::p2p::client::{AppResponseCallback, Client}; | |
use network::p2p::gossip::{Gossipable, Set}; | |
use network::p2p::gossip::handler::{Handler, HandlerConfig, new_handler}; | |
use network::p2p::handler::Handler as TraitHandler; | |
use async_trait::async_trait; | |
use avalanche_types::ids::Id; | |
use network::p2p::{ | |
client::{AppResponseCallback, Client}, | |
gossip::{ | |
gossip::{Config, Gossiper}, | |
handler::{new_handler, Handler, HandlerConfig}, | |
Gossipable, Set, | |
}, | |
handler::Handler as TraitHandler, | |
}; | |
use std::{error::Error, hash::Hash, sync::Arc, time::Duration}; | |
use tokio::{ | |
io::{AsyncReadExt, AsyncWriteExt}, | |
net::{TcpListener, TcpStream}, | |
sync::{mpsc::channel, Mutex}, | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I overlooked this, should check how you can configure this in CLion .
core/network/examples/peer_gossip.rs
Outdated
#[allow(unused_variables)] | ||
impl Client for TestClient { | ||
async fn app_gossip(&mut self, request_bytes: Vec<u8>) { | ||
unimplemented!() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, todo!
is a little shorter, and they pretty much do the same thing.
}); | ||
|
||
let mut guard = self.client.try_lock().expect("Failed to acquire a lock on client"); | ||
guard.app_request_any(msg_bytes.clone(), on_response).await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to clone msg_bytes
here.
guard.app_request_any(msg_bytes.clone(), on_response).await; | |
guard.app_request_any(msg_bytes, on_response).await; |
core/network/src/p2p/client.rs
Outdated
use std::sync::Arc; | ||
use async_trait::async_trait; | ||
|
||
pub type AppResponseCallback = Arc<dyn Fn(Vec<u8>) + Send + Sync>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you have to wrap the callback in an Arc
?
} | ||
|
||
#[async_trait] | ||
#[allow(unused_variables)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shouldn't really ever have this on a trait. Just use the _
prefix where necessary.
let mut response_size = 0_usize; | ||
let mut gossip_bytes: Vec<Vec<u8>> = Vec::new(); | ||
let guard = self.set.try_lock().expect("Lock failed on set"); | ||
guard.iterate(&|gossipable| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guard.iterate(&|gossipable| { | |
guard.iterate(|gossipable| { |
set: Arc<Mutex<S>>, | ||
client: Arc<Client>, | ||
stop_rx: Receiver<()>, | ||
phantom: PhantomData<T>, // Had to use this to please the compiler about T not being used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick lesson on when to use associated types vs. generics (in case you don't already know this):
A generic type represents many types, right? So, would it make sense to have many different trait implementations, one for each concrete type? OR... is there a single type that should be associated with this particular trait implementation?
Say the Iterator
trait did not exist, and we wanted to create an Iterator
trait and implement it for Vec<T>
(not actually how this works, but I think the example will be clear). Would we want to have a different implementation for a Vec<String>
vs. a Vec<u8>
? Definitely not! Here's where we want an associated type.
A good rule of thumb is to actually start with an associated type and see if you can get it to work the way you want; it will become clear very quickly if you actually need to make your trait generic.
Thanks for all the feedbacks ! My goal here is more to learn something (never used rust for this kind of work, so definitely lot of things I don't know) than to have something merged in. Am gonna aim for something working, would be a nice achievement ;) |
@Sanghren, hit the "Re-request review" button when you're ready for another round! I can't promise I'll get to it right away, but I will get there when I get a chance! |
Thank you so much to take time to review this, helping me a lot and learning tons of stuff. |
Hey @Sanghren, do you still intend to work on this PR in the near future? Our team can hop in and try to finish the great work you've started. |
eyh o/ Well I submitted some stuff and was waiting for a review (kinda lost track of this tbh). Please do, not sure my code is worth it though, I am learning here. |
Summary
This pull request is an initial attempt to address the issue: #29
Changes
Added traits for Gossipable and Set.
Implemented logic for Gossiper and Handler (first dab at it).
Build rust source from the sdk.proto
Dummy Client implementation (real implementation out of scope of this PR, imho)
Basic Handler implemenation.
ToDos
[ ] More tests
[ ] Logic review (feel like i can improve stuff)
Questions
Would love to have feedback on this. First dive into avalanche-rs so am not really familiar with it.