mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-16 03:21:22 +00:00
Remove temporary peer ID compatibility mode. (#1608)
* Remove temporary peer ID compatibility. This removes the temporary compatibility mode between peer IDs using identity hashing and sha256, thus being the last step in the context of https://github.com/libp2p/rust-libp2p/issues/555. * Check digest length in PeerId::from_multihash for Identity hash. * Update core/src/peer_id.rs Co-authored-by: Toralf Wittner <tw@dtex.org> * Update core changelog. * Update core changelog. Co-authored-by: Toralf Wittner <tw@dtex.org>
This commit is contained in:
@ -1,3 +1,18 @@
|
|||||||
|
# 0.22.0 [unreleased]
|
||||||
|
|
||||||
|
- Remove `PeerId` compatibility mode for "identity" and SHA2 hashes.
|
||||||
|
Historically, before 0.12, `PeerId`s were incorrectly always hashed with SHA2.
|
||||||
|
Starting from version 0.13, rust-libp2p accepted both hashed and non-hashed keys as
|
||||||
|
input. Starting from version 0.16 rust-libp2p compared `PeerId`s of "identity" and
|
||||||
|
SHA2 hashes equal, which made it possible to connect through secio or noise to nodes
|
||||||
|
with an identity hash for the same peer ID. Starting from version 0.17, rust-libp2p
|
||||||
|
switched to not hashing the key (i.e. the correct behaviour) while retaining
|
||||||
|
equality between peer IDs using the "identity" hash and SHA2. Finally, with
|
||||||
|
this release, that will no longer be the case and it is assumed that peer IDs
|
||||||
|
whose length is less or equal to 42 bytes always use the "identity" hash so
|
||||||
|
two peer IDs are equal if and only if they use the same hash algorithm and
|
||||||
|
have the same hash digest. [PR 1608](https://github.com/libp2p/rust-libp2p/pull/1608).
|
||||||
|
|
||||||
# 0.21.0 [2020-08-18]
|
# 0.21.0 [2020-08-18]
|
||||||
|
|
||||||
- Remove duplicates when performing address translation
|
- Remove duplicates when performing address translation
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
use crate::PublicKey;
|
use crate::PublicKey;
|
||||||
use bs58;
|
use bs58;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use multihash::{self, Code, Sha2_256};
|
use multihash::{self, Code, Multihash};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::{convert::TryFrom, borrow::Borrow, fmt, hash, str::FromStr, cmp};
|
use std::{convert::TryFrom, borrow::Borrow, fmt, hash, str::FromStr, cmp};
|
||||||
|
|
||||||
@ -35,12 +35,7 @@ const MAX_INLINE_KEY_LENGTH: usize = 42;
|
|||||||
// TODO: maybe keep things in decoded version?
|
// TODO: maybe keep things in decoded version?
|
||||||
#[derive(Clone, Eq)]
|
#[derive(Clone, Eq)]
|
||||||
pub struct PeerId {
|
pub struct PeerId {
|
||||||
multihash: multihash::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>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for PeerId {
|
impl fmt::Debug for PeerId {
|
||||||
@ -77,57 +72,37 @@ impl PeerId {
|
|||||||
pub fn from_public_key(key: PublicKey) -> PeerId {
|
pub fn from_public_key(key: PublicKey) -> PeerId {
|
||||||
let key_enc = key.into_protobuf_encoding();
|
let key_enc = key.into_protobuf_encoding();
|
||||||
|
|
||||||
// Note: before 0.12, this was incorrectly implemented and `SHA2256` was always used.
|
let hash_algorithm = if key_enc.len() <= MAX_INLINE_KEY_LENGTH {
|
||||||
// Starting from version 0.13, rust-libp2p accepts both hashed and non-hashed keys as
|
Code::Identity
|
||||||
// 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))
|
|
||||||
} else {
|
} 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);
|
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
|
/// Checks whether `data` is a valid `PeerId`. If so, returns the `PeerId`. If not, returns
|
||||||
/// back the data as an error.
|
/// back the data as an error.
|
||||||
pub fn from_bytes(data: Vec<u8>) -> Result<PeerId, Vec<u8>> {
|
pub fn from_bytes(data: Vec<u8>) -> Result<PeerId, Vec<u8>> {
|
||||||
match multihash::Multihash::from_bytes(data) {
|
match Multihash::from_bytes(data) {
|
||||||
Ok(multihash) => {
|
Ok(multihash) => PeerId::from_multihash(multihash).map_err(Multihash::into_bytes),
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => Err(err.data),
|
Err(err) => Err(err.data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns a `Multihash` into a `PeerId`. If the multihash doesn't use the correct algorithm,
|
/// Tries to turn a `Multihash` into a `PeerId`.
|
||||||
/// returns back the data as an error.
|
///
|
||||||
pub fn from_multihash(data: multihash::Multihash) -> Result<PeerId, multihash::Multihash> {
|
/// If the multihash does not use a valid hashing algorithm for peer IDs,
|
||||||
if data.algorithm() == multihash::Code::Sha2_256 {
|
/// or the hash value does not satisfy the constraints for a hashed
|
||||||
Ok(PeerId { multihash: data, canonical: None })
|
/// peer ID, it is returned as an `Err`.
|
||||||
} else if data.algorithm() == multihash::Code::Identity {
|
pub fn from_multihash(multihash: Multihash) -> Result<PeerId, Multihash> {
|
||||||
let canonical = Sha2_256::digest(data.digest());
|
match multihash.algorithm() {
|
||||||
Ok(PeerId { multihash: data, canonical: Some(canonical) })
|
Code::Sha2_256 => Ok(PeerId { multihash }),
|
||||||
} else {
|
Code::Identity if multihash.digest().len() <= MAX_INLINE_KEY_LENGTH
|
||||||
Err(data)
|
=> Ok(PeerId { multihash }),
|
||||||
|
_ => Err(multihash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +112,7 @@ impl PeerId {
|
|||||||
pub fn random() -> PeerId {
|
pub fn random() -> PeerId {
|
||||||
let peer_id = rand::thread_rng().gen::<[u8; 32]>();
|
let peer_id = rand::thread_rng().gen::<[u8; 32]>();
|
||||||
PeerId {
|
PeerId {
|
||||||
multihash: multihash::wrap(multihash::Code::Sha2_256, &peer_id),
|
multihash: multihash::wrap(Code::Identity, &peer_id),
|
||||||
canonical: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +136,7 @@ impl PeerId {
|
|||||||
|
|
||||||
/// Returns a base-58 encoded string of this `PeerId`.
|
/// Returns a base-58 encoded string of this `PeerId`.
|
||||||
pub fn to_base58(&self) -> String {
|
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`.
|
/// Checks whether the public key passed as parameter matches the public key of this `PeerId`.
|
||||||
@ -187,7 +161,6 @@ impl hash::Hash for PeerId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<PublicKey> for PeerId {
|
impl From<PublicKey> for PeerId {
|
||||||
#[inline]
|
|
||||||
fn from(key: PublicKey) -> PeerId {
|
fn from(key: PublicKey) -> PeerId {
|
||||||
PeerId::from_public_key(key)
|
PeerId::from_public_key(key)
|
||||||
}
|
}
|
||||||
@ -201,10 +174,10 @@ impl TryFrom<Vec<u8>> for PeerId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<multihash::Multihash> for PeerId {
|
impl TryFrom<Multihash> for PeerId {
|
||||||
type Error = multihash::Multihash;
|
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)
|
PeerId::from_multihash(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,7 +192,7 @@ impl PartialEq<PeerId> for PeerId {
|
|||||||
|
|
||||||
impl Borrow<[u8]> for PeerId {
|
impl Borrow<[u8]> for PeerId {
|
||||||
fn borrow(&self) -> &[u8] {
|
fn borrow(&self) -> &[u8] {
|
||||||
self.canonical.as_ref().map_or(self.multihash.as_bytes(), |c| c.as_bytes())
|
self.multihash.as_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +205,7 @@ impl AsRef<[u8]> for PeerId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PeerId> for multihash::Multihash {
|
impl From<PeerId> for Multihash {
|
||||||
fn from(peer_id: PeerId) -> Self {
|
fn from(peer_id: PeerId) -> Self {
|
||||||
peer_id.multihash
|
peer_id.multihash
|
||||||
}
|
}
|
||||||
@ -259,7 +232,6 @@ impl FromStr for PeerId {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{PeerId, identity};
|
use crate::{PeerId, identity};
|
||||||
use std::{convert::TryFrom as _, hash::{self, Hasher as _}};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn peer_id_is_public_key() {
|
fn peer_id_is_public_key() {
|
||||||
@ -289,62 +261,4 @@ mod tests {
|
|||||||
assert_eq!(peer_id, PeerId::from_bytes(peer_id.clone().into_bytes()).unwrap());
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user