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: Better error reporting when features are disabled #2972

Merged
merged 39 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f83ccea
Return `snow::Error` as source
thomaseizinger Oct 3, 2022
d64c216
Use `thiserror` macro to generate error
thomaseizinger Oct 3, 2022
9218c5d
Move `NoiseError` to `lib.rs`
thomaseizinger Oct 3, 2022
330e3ff
Be more specific about error case for bad signatures
thomaseizinger Oct 3, 2022
992706b
Be more specific about error case for unexpected key in authentication
thomaseizinger Oct 3, 2022
84ca840
Use `?` for better indentation
thomaseizinger Oct 3, 2022
b9ffed9
Even more use of `?`
thomaseizinger Oct 3, 2022
ae8b833
Use combinators on ALL the things
thomaseizinger Oct 3, 2022
e1c80e3
Be more specific about error case when validating key length
thomaseizinger Oct 3, 2022
3c15f9f
Include source of decoding error
thomaseizinger Oct 3, 2022
a937f16
Add changelog entry
thomaseizinger Oct 3, 2022
2531ab5
Slightly revise coding style of `ipfs-kad` example
thomaseizinger Oct 3, 2022
59ba1dd
Be more specific about what is not supported
thomaseizinger Oct 3, 2022
d789d5b
Don't include sources in Display impl
thomaseizinger Oct 3, 2022
097cf5e
Print source chain on `DialError`
thomaseizinger Oct 3, 2022
0826287
Format code
thomaseizinger Oct 4, 2022
5c5281f
Fix bad parenthesis
thomaseizinger Oct 4, 2022
76cdbbe
Fix clippy error
thomaseizinger Oct 4, 2022
c81f730
Introduce dedicated constructors for `DecodingError`
thomaseizinger Oct 5, 2022
18c5c02
Merge branch 'master' into 2971-better-error-message
mxinden Oct 5, 2022
fd870f8
Merge branch 'master' into 2971-better-error-message
mxinden Oct 5, 2022
682b363
Merge branch 'master' into 2971-better-error-message
mxinden Oct 9, 2022
f6017db
Merge branch 'master' into 2971-better-error-message
thomaseizinger Oct 11, 2022
1a49349
Merge branch 'master' into 2971-better-error-message
thomaseizinger Oct 14, 2022
7fc3c61
Merge branch 'master' into 2971-better-error-message
thomaseizinger Oct 19, 2022
db129cb
Bump version accordingly
thomaseizinger Oct 19, 2022
8a75a12
Merge branch 'master' into 2971-better-error-message
thomaseizinger Oct 24, 2022
513b920
Increase `libp2p-core` version to 0.37.1
thomaseizinger Oct 24, 2022
2fad2ac
Merge branch 'master' into 2971-better-error-message
mxinden Nov 2, 2022
3763ddf
Merge branch 'master' into 2971-better-error-message
thomaseizinger Nov 4, 2022
daaccd3
Merge branch 'master' into 2971-better-error-message
thomaseizinger Nov 15, 2022
b04b367
Fix bad boolean logic
thomaseizinger Nov 15, 2022
d8426bf
Merge branch 'master' into 2971-better-error-message
thomaseizinger Nov 17, 2022
2c348c6
Merge branch 'master' into 2971-better-error-message
thomaseizinger Nov 22, 2022
cedd5a1
Remove unnecessary feature activation
thomaseizinger Nov 22, 2022
47e3772
Revert "Remove unnecessary feature activation"
thomaseizinger Nov 22, 2022
7a7522b
Fix semver problem
thomaseizinger Nov 22, 2022
60d03f2
MOAR CONDITIONALS
thomaseizinger Nov 22, 2022
a549775
Fmt
thomaseizinger Nov 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
#[cfg(any(not(feature = "rsa"), target_arch = "wasm32"))]
keys_proto::KeyType::Rsa => {
log::debug!("support for RSA was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
Err(DecodingError::new("RSA keys are unsupported"))
thomaseizinger marked this conversation as resolved.
Show resolved Hide resolved
}
#[cfg(feature = "secp256k1")]
keys_proto::KeyType::Secp256k1 => {
Expand All @@ -332,7 +332,7 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
#[cfg(not(feature = "secp256k1"))]
keys_proto::KeyType::Secp256k1 => {
log::debug!("support for secp256k1 was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
Err(DecodingError::new("secp256k1 keys are unsupported"))
}
#[cfg(feature = "ecdsa")]
keys_proto::KeyType::Ecdsa => {
Expand All @@ -341,7 +341,7 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
#[cfg(not(feature = "ecdsa"))]
keys_proto::KeyType::Ecdsa => {
log::debug!("support for ECDSA was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
Err(DecodingError::new("ECDSA keys are unsupported"))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/src/upgrade/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UpgradeError::Select(e) => write!(f, "select error: {}", e),
UpgradeError::Apply(e) => write!(f, "upgrade apply error: {}", e),
UpgradeError::Select(_) => write!(f, "Multistream select failed"),
UpgradeError::Apply(_) => write!(f, "Handshake failed"),
}
}
}
Expand Down
79 changes: 37 additions & 42 deletions examples/ipfs-kad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,15 @@
//! You can pass as parameter a base58 peer ID to search for. If you don't pass any parameter, a
//! peer ID will be generated randomly.

use async_std::task;
use futures::StreamExt;
use libp2p::kad::record::store::MemoryStore;
use libp2p::kad::{GetClosestPeersError, Kademlia, KademliaConfig, KademliaEvent, QueryResult};
use libp2p::{
development_transport, identity,
swarm::{Swarm, SwarmEvent},
Multiaddr, PeerId,
PeerId,
};
use std::{env, error::Error, str::FromStr, time::Duration};
use std::{env, error::Error, time::Duration};

const BOOTNODES: [&str; 4] = [
"QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
Expand Down Expand Up @@ -63,58 +62,54 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Add the bootnodes to the local routing table. `libp2p-dns` built
// into the `transport` resolves the `dnsaddr` when Kademlia tries
// to dial these nodes.
let bootaddr = Multiaddr::from_str("/dnsaddr/bootstrap.libp2p.io")?;
for peer in &BOOTNODES {
behaviour.add_address(&PeerId::from_str(peer)?, bootaddr.clone());
behaviour.add_address(&peer.parse()?, "/dnsaddr/bootstrap.libp2p.io".parse()?);
}

Swarm::new(transport, behaviour, local_peer_id)
};

// Order Kademlia to search for a peer.
let to_search: PeerId = if let Some(peer_id) = env::args().nth(1) {
peer_id.parse()?
} else {
identity::Keypair::generate_ed25519().public().into()
};
let to_search = env::args()
.nth(1)
.map(|p| p.parse())
.transpose()?
.unwrap_or_else(PeerId::random);

println!("Searching for the closest peers to {:?}", to_search);
println!("Searching for the closest peers to {}", to_search);
swarm.behaviour_mut().get_closest_peers(to_search);

// Kick it off!
task::block_on(async move {
loop {
let event = swarm.select_next_some().await;
if let SwarmEvent::Behaviour(KademliaEvent::OutboundQueryCompleted {
result: QueryResult::GetClosestPeers(result),
..
}) = event
{
match result {
Ok(ok) => {
if !ok.peers.is_empty() {
println!("Query finished with closest peers: {:#?}", ok.peers)
} else {
// The example is considered failed as there
// should always be at least 1 reachable peer.
println!("Query finished with no closest peers.")
}
loop {
let event = swarm.select_next_some().await;
if let SwarmEvent::Behaviour(KademliaEvent::OutboundQueryCompleted {
result: QueryResult::GetClosestPeers(result),
..
}) = event
{
match result {
Ok(ok) => {
if !ok.peers.is_empty() {
println!("Query finished with closest peers: {:#?}", ok.peers)
} else {
// The example is considered failed as there
// should always be at least 1 reachable peer.
println!("Query finished with no closest peers.")
}
Err(GetClosestPeersError::Timeout { peers, .. }) => {
if !peers.is_empty() {
println!("Query timed out with closest peers: {:#?}", peers)
} else {
// The example is considered failed as there
// should always be at least 1 reachable peer.
println!("Query timed out with no closest peers.");
}
}
Err(GetClosestPeersError::Timeout { peers, .. }) => {
if !peers.is_empty() {
println!("Query timed out with closest peers: {:#?}", peers)
} else {
// The example is considered failed as there
// should always be at least 1 reachable peer.
println!("Query timed out with no closest peers.");
}
};
}
};

break;
}
break;
}
}

Ok(())
})
Ok(())
}
60 changes: 55 additions & 5 deletions swarm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1480,17 +1480,45 @@ impl fmt::Display for DialError {
f,
"Dial error: Pending connection attempt has been aborted."
),
DialError::InvalidPeerId(multihash) => write!(f, "Dial error: multihash {:?} is not a PeerId", multihash),
DialError::WrongPeerId { obtained, endpoint} => write!(f, "Dial error: Unexpected peer ID {} at {:?}.", obtained, endpoint),
DialError::InvalidPeerId(multihash) => {
write!(f, "Dial error: multihash {:?} is not a PeerId", multihash)
}
DialError::WrongPeerId { obtained, endpoint } => write!(
f,
"Dial error: Unexpected peer ID {} at {:?}.",
obtained, endpoint
),
DialError::ConnectionIo(e) => write!(
f,
"Dial error: An I/O error occurred on the connection: {:?}.", e
"Dial error: An I/O error occurred on the connection: {:?}.",
e
),
DialError::Transport(e) => write!(f, "An error occurred while negotiating the transport protocol(s) on a connection: {:?}.", e),
DialError::Transport(errors) => {
write!(f, "Failed to negotiate transport protocol(s): [")?;

for (addr, error) in errors {
write!(f, "({addr}")?;
print_error_chain(f, error)?;
write!(f, ")")?;
}
write!(f, "]")?;

Ok(())
}
}
}
}

fn print_error_chain(f: &mut fmt::Formatter<'_>, e: &dyn error::Error) -> fmt::Result {
write!(f, ": {e}")?;

if let Some(source) = e.source() {
print_error_chain(f, source)?;
}

Ok(())
}

impl error::Error for DialError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Expand Down Expand Up @@ -1620,10 +1648,13 @@ mod tests {
use libp2p::core::{identity, multiaddr, transport, upgrade};
use libp2p::plaintext;
use libp2p::yamux;
use libp2p_core::either::EitherError;
use libp2p_core::multiaddr::multiaddr;
use libp2p_core::transport::memory::MemoryTransportError;
use libp2p_core::transport::TransportEvent;
use libp2p_core::Endpoint;
use libp2p_core::{Endpoint, UpgradeError};
use quickcheck::*;
use void::Void;

// Test execution state.
// Connection => Disconnecting => Connecting.
Expand Down Expand Up @@ -2502,4 +2533,23 @@ mod tests {
e => panic!("Unexpected swarm event {:?}.", e),
}
}

#[test]
fn dial_error_prints_sources() {
// This constitutes a fairly typical error for chained transports.
let error = DialError::Transport(vec![(
"/ip4/127.0.0.1/tcp/80".parse().unwrap(),
TransportError::Other(io::Error::new(
io::ErrorKind::Other,
EitherError::<_, Void>::A(EitherError::<Void, _>::B(UpgradeError::Apply(
MemoryTransportError::Unreachable,
))),
)),
)]);

let string = format!("{error}");

// Unfortunately, we have some "empty" errors that lead to multiple colons without text but that is the best we can do.
thomaseizinger marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!("Failed to negotiate transport protocol(s): [(/ip4/127.0.0.1/tcp/80: : Handshake failed: No listener on the given port.)]", string)
}
}
2 changes: 2 additions & 0 deletions transports/noise/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
- Introduce `NoiseAuthenticated::xx` constructor, assuming a X25519 DH key exchange. An XX key exchange and X25519 keys
are the most common way of using noise in libp2p and thus deserve a convenience constructor. See [PR 2887].
- Add `NoiseConfig::with_prologue` which allows users to set the noise prologue of the handshake. See [PR 2903].
- Introduce more variants to `NoiseError` to better differentiate between failure cases during authentication. See [PR XXXX].

[PR 2887]: https://github.com/libp2p/rust-libp2p/pull/2887
[PR 2903]: https://github.com/libp2p/rust-libp2p/pull/2903
[PR XXXX]: https://github.com/libp2p/rust-libp2p/pull/XXXX

# 0.39.0

Expand Down
1 change: 1 addition & 0 deletions transports/noise/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ prost = "0.11"
rand = "0.8.3"
sha2 = "0.10.0"
static_assertions = "1"
thiserror = "1.0.37"
x25519-dalek = "1.1.0"
zeroize = "1"

Expand Down
92 changes: 0 additions & 92 deletions transports/noise/src/error.rs

This file was deleted.

37 changes: 16 additions & 21 deletions transports/noise/src/io/framed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,28 +94,23 @@ impl<T> NoiseFramed<T, snow::HandshakeState> {
where
C: Protocol<C> + AsRef<[u8]>,
{
let dh_remote_pubkey = match self.session.get_remote_static() {
None => None,
Some(k) => match C::public_from_bytes(k) {
Err(e) => return Err(e),
Ok(dh_pk) => Some(dh_pk),
},
let dh_remote_pubkey = self
.session
.get_remote_static()
.map(C::public_from_bytes)
.transpose()?;

let io = NoiseFramed {
session: self.session.into_transport_mode()?,
io: self.io,
read_state: ReadState::Ready,
write_state: WriteState::Ready,
read_buffer: self.read_buffer,
write_buffer: self.write_buffer,
decrypt_buffer: self.decrypt_buffer,
};
match self.session.into_transport_mode() {
Err(e) => Err(e.into()),
Ok(s) => {
let io = NoiseFramed {
session: s,
io: self.io,
read_state: ReadState::Ready,
write_state: WriteState::Ready,
read_buffer: self.read_buffer,
write_buffer: self.write_buffer,
decrypt_buffer: self.decrypt_buffer,
};
Ok((dh_remote_pubkey, NoiseOutput::new(io)))
}
}

Ok((dh_remote_pubkey, NoiseOutput::new(io)))
}
}

Expand Down