Skip to content

Commit

Permalink
Remove temporary peer ID compatibility.
Browse files Browse the repository at this point in the history
This removes the temporary compatibility mode between
peer IDs using identity hashing and sha256, thus being
the last step in the context of libp2p#555.
  • Loading branch information
Roman S. Borschel committed Jun 15, 2020
1 parent e8bf34f commit 3147a41
Showing 1 changed file with 22 additions and 109 deletions.
131 changes: 22 additions & 109 deletions core/src/peer_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
use crate::PublicKey;
use bs58;
use thiserror::Error;
use multihash::{self, Code, Sha2_256};
use multihash::{self, Code, Multihash};
use rand::Rng;
use std::{convert::TryFrom, borrow::Borrow, fmt, hash, str::FromStr};

Expand All @@ -35,12 +35,7 @@ const MAX_INLINE_KEY_LENGTH: usize = 42;
// TODO: maybe keep things in decoded version?
#[derive(Clone, Eq)]
pub struct PeerId {
multihash: multihash::Multihash,
/// A (temporary) "canonical" multihash if `multihash` is of type
/// multihash::Hash::Identity, so that `Borrow<[u8]>` semantics
/// can be given, i.e. a view of a byte representation whose
/// equality is consistent with `PartialEq`.
canonical: Option<multihash::Multihash>,
multihash: Multihash,
}

impl fmt::Debug for PeerId {
Expand All @@ -62,57 +57,35 @@ impl PeerId {
pub fn from_public_key(key: PublicKey) -> PeerId {
let key_enc = key.into_protobuf_encoding();

// Note: before 0.12, this was incorrectly implemented and `SHA2256` was always used.
// Starting from version 0.13, rust-libp2p accepts both hashed and non-hashed keys as
// input (see `from_bytes`). Starting from version 0.16 rust-libp2p will compare
// `PeerId`s of different hashes equal, which makes it possible to connect through
// secio or noise to nodes with an identity hash. Starting from version 0.17, rust-libp2p
// will switch to not hashing the key (i.e. the correct behaviour).
// In other words, rust-libp2p 0.16 is compatible with all versions of rust-libp2p.
// Rust-libp2p 0.12 and below is **NOT** compatible with rust-libp2p 0.17 and above.
let (hash_algorithm, canonical_algorithm) = if key_enc.len() <= MAX_INLINE_KEY_LENGTH {
(Code::Identity, Some(Code::Sha2_256))
let hash_algorithm = if key_enc.len() <= MAX_INLINE_KEY_LENGTH {
Code::Identity
} else {
(Code::Sha2_256, None)
Code::Sha2_256
};

let canonical = canonical_algorithm.map(|alg|
alg.digest(&key_enc));

let multihash = hash_algorithm.digest(&key_enc);

PeerId { multihash, canonical }
PeerId { multihash }
}

/// Checks whether `data` is a valid `PeerId`. If so, returns the `PeerId`. If not, returns
/// back the data as an error.
pub fn from_bytes(data: Vec<u8>) -> Result<PeerId, Vec<u8>> {
match multihash::Multihash::from_bytes(data) {
Ok(multihash) => {
if multihash.algorithm() == multihash::Code::Sha2_256 {
Ok(PeerId { multihash, canonical: None })
}
else if multihash.algorithm() == multihash::Code::Identity {
let canonical = Sha2_256::digest(&multihash.digest());
Ok(PeerId { multihash, canonical: Some(canonical) })
} else {
Err(multihash.into_bytes())
}
}
match Multihash::from_bytes(data) {
Ok(multihash) => match multihash.algorithm() {
Code::Sha2_256 | Code::Identity => Ok(PeerId { multihash }),
_ => Err(multihash.into_bytes())
},
Err(err) => Err(err.data),
}
}

/// Turns a `Multihash` into a `PeerId`. If the multihash doesn't use the correct algorithm,
/// returns back the data as an error.
pub fn from_multihash(data: multihash::Multihash) -> Result<PeerId, multihash::Multihash> {
if data.algorithm() == multihash::Code::Sha2_256 {
Ok(PeerId { multihash: data, canonical: None })
} else if data.algorithm() == multihash::Code::Identity {
let canonical = Sha2_256::digest(data.digest());
Ok(PeerId { multihash: data, canonical: Some(canonical) })
} else {
Err(data)
pub fn from_multihash(data: Multihash) -> Result<PeerId, Multihash> {
match data.algorithm() {
Code::Sha2_256 | Code::Identity => Ok(PeerId { multihash: data }),
_ => Err(data)
}
}

Expand All @@ -122,8 +95,7 @@ impl PeerId {
pub fn random() -> PeerId {
let peer_id = rand::thread_rng().gen::<[u8; 32]>();
PeerId {
multihash: multihash::wrap(multihash::Code::Sha2_256, &peer_id),
canonical: None,
multihash: multihash::wrap(Code::Identity, &peer_id),
}
}

Expand All @@ -147,7 +119,7 @@ impl PeerId {

/// Returns a base-58 encoded string of this `PeerId`.
pub fn to_base58(&self) -> String {
bs58::encode(self.as_bytes()).into_string()
bs58::encode(self.borrow() as &[u8]).into_string()
}

/// Checks whether the public key passed as parameter matches the public key of this `PeerId`.
Expand All @@ -172,7 +144,6 @@ impl hash::Hash for PeerId {
}

impl From<PublicKey> for PeerId {
#[inline]
fn from(key: PublicKey) -> PeerId {
PeerId::from_public_key(key)
}
Expand All @@ -186,10 +157,10 @@ impl TryFrom<Vec<u8>> for PeerId {
}
}

impl TryFrom<multihash::Multihash> for PeerId {
type Error = multihash::Multihash;
impl TryFrom<Multihash> for PeerId {
type Error = Multihash;

fn try_from(value: multihash::Multihash) -> Result<Self, Self::Error> {
fn try_from(value: Multihash) -> Result<Self, Self::Error> {
PeerId::from_multihash(value)
}
}
Expand All @@ -204,7 +175,7 @@ impl PartialEq<PeerId> for PeerId {

impl Borrow<[u8]> for PeerId {
fn borrow(&self) -> &[u8] {
self.canonical.as_ref().map_or(self.multihash.as_bytes(), |c| c.as_bytes())
self.multihash.as_bytes()
}
}

Expand All @@ -217,7 +188,7 @@ impl AsRef<[u8]> for PeerId {
}
}

impl From<PeerId> for multihash::Multihash {
impl From<PeerId> for Multihash {
fn from(peer_id: PeerId) -> Self {
peer_id.multihash
}
Expand Down Expand Up @@ -274,62 +245,4 @@ mod tests {
assert_eq!(peer_id, PeerId::from_bytes(peer_id.clone().into_bytes()).unwrap());
}
}

#[test]
fn peer_id_identity_equal_to_sha2256() {
let random_bytes = (0..64).map(|_| rand::random::<u8>()).collect::<Vec<u8>>();
let mh1 = multihash::Sha2_256::digest(&random_bytes);
let mh2 = multihash::Identity::digest(&random_bytes);
let peer_id1 = PeerId::try_from(mh1).unwrap();
let peer_id2 = PeerId::try_from(mh2).unwrap();
assert_eq!(peer_id1, peer_id2);
assert_eq!(peer_id2, peer_id1);
}

#[test]
fn peer_id_identity_hashes_equal_to_sha2256() {
let random_bytes = (0..64).map(|_| rand::random::<u8>()).collect::<Vec<u8>>();
let mh1 = multihash::Sha2_256::digest(&random_bytes);
let mh2 = multihash::Identity::digest(&random_bytes);
let peer_id1 = PeerId::try_from(mh1).unwrap();
let peer_id2 = PeerId::try_from(mh2).unwrap();

let mut hasher1 = fnv::FnvHasher::with_key(0);
hash::Hash::hash(&peer_id1, &mut hasher1);
let mut hasher2 = fnv::FnvHasher::with_key(0);
hash::Hash::hash(&peer_id2, &mut hasher2);

assert_eq!(hasher1.finish(), hasher2.finish());
}

#[test]
fn peer_id_equal_across_algorithms() {
use multihash::Code;
use quickcheck::{Arbitrary, Gen};

#[derive(Debug, Clone, PartialEq, Eq)]
struct HashAlgo(Code);

impl Arbitrary for HashAlgo {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
match g.next_u32() % 4 { // make Hash::Identity more likely
0 => HashAlgo(Code::Sha2_256),
_ => HashAlgo(Code::Identity)
}
}
}

fn property(data: Vec<u8>, algo1: HashAlgo, algo2: HashAlgo) -> bool {
let a = PeerId::try_from(algo1.0.digest(&data)).unwrap();
let b = PeerId::try_from(algo2.0.digest(&data)).unwrap();

if algo1 == algo2 || algo1.0 == Code::Identity || algo2.0 == Code::Identity {
a == b
} else {
a != b
}
}

quickcheck::quickcheck(property as fn(Vec<u8>, HashAlgo, HashAlgo) -> bool)
}
}

0 comments on commit 3147a41

Please sign in to comment.