diff --git a/CHANGELOG.md b/CHANGELOG.md index 971ba2e..eb10ec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # 0.18.0 - unreleased - Add `WebTransport` instance for `Multiaddr`. See [PR 70]. + - Disable all features of `multihash`. See [PR 77]. + - Mark `Protocol` as `#[non_exhaustive]`. See [PR 82]. - Rename `Protocol::WebRTC` to `Protocol::WebRTCDirect`. @@ -9,10 +11,14 @@ Remove deprecated support for `/webrtc` in favor of the existing `/webrtc-direct` string representation. **Note that this is a breaking change.** +- Make `/p2p` typesafe, i.e. have `Protocol::P2p` contain a `PeerId` instead of a `Multihash`. + See [PR 83]. + [multiformats/multiaddr discussion]: https://github.com/multiformats/multiaddr/pull/150#issuecomment-1468791586 [PR 70]: https://github.com/multiformats/rust-multiaddr/pull/70 [PR 77]: https://github.com/multiformats/rust-multiaddr/pull/77 [PR 82]: https://github.com/multiformats/rust-multiaddr/pull/82 +[PR 83]: https://github.com/multiformats/rust-multiaddr/pull/83 # 0.17.1 diff --git a/Cargo.toml b/Cargo.toml index 7ed04f8..cace133 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,12 +18,13 @@ arrayref = "0.3" byteorder = "1.3.1" data-encoding = "2.1" multibase = "0.9.1" -multihash = { version = "0.18", default-features = false, features = ["std"] } +multihash = "0.19" percent-encoding = "2.1.0" serde = "1.0.70" static_assertions = "1.1" unsigned-varint = "0.7" url = { version = "2.1.0", optional = true, default-features = false } +libp2p-identity = { version = "0.2.0", features = ["peerid"] } [dev-dependencies] bincode = "1" diff --git a/src/lib.rs b/src/lib.rs index e79e385..92a31fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ -///! Implementation of [multiaddr](https://github.com/multiformats/multiaddr) in Rust. +//! Implementation of [multiaddr](https://github.com/multiformats/multiaddr) in Rust. + pub use multihash; mod errors; diff --git a/src/protocol.rs b/src/protocol.rs index 53abb66..90732c1 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -3,7 +3,7 @@ use crate::{Error, Result}; use arrayref::array_ref; use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; use data_encoding::BASE32; -use multihash::MultihashGeneric; +use libp2p_identity::PeerId; use std::{ borrow::Cow, convert::From, @@ -57,7 +57,7 @@ const WSS_WITH_PATH: u32 = 4780; // Note: not standard /// The `64` defines the allocation size for the digest within the `Multihash`. /// This allows us to use hashes such as SHA512. /// In case protocols like `/certhash` ever support hashes larger than that, we will need to update this size here (which will be a breaking change!). -type Multihash = MultihashGeneric<64>; +type Multihash = multihash::Multihash<64>; const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS .add(b'%') @@ -99,7 +99,7 @@ pub enum Protocol<'a> { Memory(u64), Onion(Cow<'a, [u8; 10]>, u16), Onion3(Onion3Addr<'a>), - P2p(Multihash), + P2p(PeerId), P2pCircuit, Quic, QuicV1, @@ -179,7 +179,9 @@ impl<'a> Protocol<'a> { "p2p" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; let decoded = multibase::Base::Base58Btc.decode(s)?; - Ok(Protocol::P2p(Multihash::from_bytes(&decoded)?)) + let peer_id = + PeerId::from_bytes(&decoded).map_err(|e| Error::ParsingError(Box::new(e)))?; + Ok(Protocol::P2p(peer_id)) } "http" => Ok(Protocol::Http), "https" => Ok(Protocol::Https), @@ -324,7 +326,12 @@ impl<'a> Protocol<'a> { P2P => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; - Ok((Protocol::P2p(Multihash::from_bytes(data)?), rest)) + Ok(( + Protocol::P2p( + PeerId::from_bytes(data).map_err(|e| Error::ParsingError(Box::new(e)))?, + ), + rest, + )) } P2P_CIRCUIT => Ok((Protocol::P2pCircuit, input)), QUIC => Ok((Protocol::Quic, input)), @@ -436,9 +443,9 @@ impl<'a> Protocol<'a> { w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } - Protocol::P2p(multihash) => { + Protocol::P2p(peer_id) => { w.write_all(encode::u32(P2P, &mut buf))?; - let bytes = multihash.to_bytes(); + let bytes = peer_id.to_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(&bytes)? } diff --git a/tests/lib.rs b/tests/lib.rs index 70227f8..071a4ce 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,6 +1,7 @@ use data_encoding::HEXUPPER; +use libp2p_identity::PeerId; use multiaddr::*; -use multihash::MultihashGeneric; +use multihash::Multihash; use quickcheck::{Arbitrary, Gen, QuickCheck}; use std::{ borrow::Cow, @@ -122,7 +123,7 @@ impl Arbitrary for Proto { .unwrap(); Proto(Onion3((a, std::cmp::max(1, u16::arbitrary(g))).into())) } - 17 => Proto(P2p(Mh::arbitrary(g).0)), + 17 => Proto(P2p(PId::arbitrary(g).0)), 18 => Proto(P2pCircuit), 19 => Proto(Quic), 20 => Proto(QuicV1), @@ -143,13 +144,24 @@ impl Arbitrary for Proto { } #[derive(Clone, Debug)] -struct Mh(MultihashGeneric<64>); +struct Mh(Multihash<64>); impl Arbitrary for Mh { fn arbitrary(g: &mut Gen) -> Self { let mut hash: [u8; 32] = [0; 32]; hash.fill_with(|| u8::arbitrary(g)); - Mh(MultihashGeneric::wrap(0x0, &hash).expect("The digest size is never too large")) + Mh(Multihash::wrap(0x0, &hash).expect("The digest size is never too large")) + } +} + +#[derive(Clone, Debug)] +struct PId(PeerId); + +impl Arbitrary for PId { + fn arbitrary(g: &mut Gen) -> Self { + let mh = Mh::arbitrary(g); + + PId(PeerId::from_multihash(mh.0).expect("identity multihash works if digest size < 64")) } } @@ -177,8 +189,8 @@ fn ma_valid(source: &str, target: &str, protocols: Vec>) { ); } -fn multihash(s: &str) -> MultihashGeneric<64> { - MultihashGeneric::from_bytes(&multibase::Base::Base58Btc.decode(s).unwrap()).unwrap() +fn peer_id(s: &str) -> PeerId { + s.parse().unwrap() } #[test] @@ -231,7 +243,7 @@ fn construct_success() { ma_valid( "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", - vec![P2p(multihash( + vec![P2p(peer_id( "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", ))], ); @@ -253,7 +265,7 @@ fn construct_success() { "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", vec![ - P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), + P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234), ], ); @@ -277,30 +289,30 @@ fn construct_success() { "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", vec![ Ip4(local), - P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), + P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), ], ); ma_valid("/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", - vec![Ip4(local), P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234)]); + vec![Ip4(local), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234)]); // /unix/a/b/c/d/e, // /unix/stdio, // /ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f, // /ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "29200108A07AC542013AC986FFFE317095061F40DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", - vec![Ip6(addr6), Tcp(8000), Ws("/".into()), P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")) + vec![Ip6(addr6), Tcp(8000), Ws("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")) ]); ma_valid("/p2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "9302047F000001062382DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", - vec![P2pWebRtcStar, Ip4(local), Tcp(9090), Ws("/".into()), P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")) + vec![P2pWebRtcStar, Ip4(local), Tcp(9090), Ws("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")) ]); ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/wss/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "29200108A07AC542013AC986FFFE317095061F40DE03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", - vec![Ip6(addr6), Tcp(8000), Wss("/".into()), P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); + vec![Ip6(addr6), Tcp(8000), Wss("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); ma_valid("/ip4/127.0.0.1/tcp/9090/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "047F000001062382A202A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", - vec![Ip4(local), Tcp(9090), P2pCircuit, P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); + vec![Ip4(local), Tcp(9090), P2pCircuit, P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); ma_valid( "/onion/aaimaq4ygg2iegci:80", @@ -332,7 +344,7 @@ fn construct_success() { ma_valid( "/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "3819736A632D312E626F6F7473747261702E6C69627032702E696F0604D2A50322122006B3608AA000274049EB28AD8E793A26FF6FAB281A7D3BD77CD18EB745DFAABB", - vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io")), Tcp(1234), P2p(multihash("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"))] + vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io")), Tcp(1234), P2p(peer_id("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"))] ); ma_valid( "/ip4/127.0.0.1/tcp/127/ws", @@ -371,7 +383,7 @@ fn construct_success() { Ip4(local), Udp(1234), WebRTCDirect, - Certhash(MultihashGeneric::from_bytes(&decoded).unwrap()), + Certhash(Multihash::from_bytes(&decoded).unwrap()), ], ); @@ -390,7 +402,7 @@ fn construct_success() { Ip4(local), Udp(1234), WebTransport, - Certhash(MultihashGeneric::from_bytes(&decoded).unwrap()), + Certhash(Multihash::from_bytes(&decoded).unwrap()), ], ); }