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

Add peer id inlining for small public keys #1237

Merged
merged 4 commits into from
Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions core/src/peer_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ use quick_error::quick_error;
use multihash;
use std::{convert::TryFrom, fmt, str::FromStr};

/// Public keys with byte-lengths smaller than `MAX_INLINE_KEY_LENGTH` will be
/// automatically used as the peer id using an identity multihash.
const MAX_INLINE_KEY_LENGTH: usize = 42;

/// Identifier of a peer of the network.
///
/// The data is a multihash of the public key of the peer.
Expand Down Expand Up @@ -52,8 +56,23 @@ impl PeerId {
#[inline]
pub fn from_public_key(key: PublicKey) -> PeerId {
let key_enc = key.into_protobuf_encoding();
let multihash = multihash::encode(multihash::Hash::SHA2256, &key_enc)
.expect("sha2-256 is always supported");

// Note: the correct behaviour, according to the libp2p specifications, is the
// commented-out code, which consists it transmitting small keys un-hashed. However, this
// version and all previous versions of rust-libp2p always hash the key. Starting from
// version 0.13, rust-libp2p accepts both hashed and non-hashed keys as input
// (see `from_bytes`). Starting from version 0.14, rust-libp2p will switch to not hashing
// the key (a.k.a. the correct behaviour).
// In other words, rust-libp2p 0.13 is compatible with all versions of rust-libp2p.
// Rust-libp2p 0.12 and below is **NOT** compatible with rust-libp2p 0.14 and above.
/*let hash_algorithm = if key_enc.len() <= MAX_INLINE_KEY_LENGTH {
multihash::Hash::Identity
} else {
multihash::Hash::SHA2256
};*/
let hash_algorithm = multihash::Hash::SHA2256;
let multihash = multihash::encode(hash_algorithm, &key_enc)
.expect("identity and sha2-256 are always supported by known public key types");
PeerId { multihash }
}

Expand All @@ -63,12 +82,14 @@ impl PeerId {
pub fn from_bytes(data: Vec<u8>) -> Result<PeerId, Vec<u8>> {
match multihash::Multihash::from_bytes(data) {
Ok(multihash) => {
if multihash.algorithm() == multihash::Hash::SHA2256 {
if multihash.algorithm() == multihash::Hash::SHA2256
|| multihash.algorithm() == multihash::Hash::Identity
{
Ok(PeerId { multihash })
} else {
Err(multihash.into_bytes())
}
},
}
Err(err) => Err(err.data),
}
}
Expand Down Expand Up @@ -131,7 +152,8 @@ impl PeerId {
let enc = public_key.clone().into_protobuf_encoding();
match multihash::encode(alg, &enc) {
Ok(h) => Some(h == self.multihash),
Err(multihash::EncodeError::UnsupportedType) => None
Err(multihash::EncodeError::UnsupportedType) => None,
Err(multihash::EncodeError::UnsupportedInputLength) => None,
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions misc/multihash/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ use std::{error, fmt};
pub enum EncodeError {
/// The requested hash algorithm isn't supported by this library.
UnsupportedType,
/// The input length is too large for the hash algorithm.
UnsupportedInputLength,
}

impl fmt::Display for EncodeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
EncodeError::UnsupportedType => write!(f, "This type is not supported yet"),
EncodeError::UnsupportedInputLength => write!(
f,
"The length of the input for the given hash is not yet supported"
),
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions misc/multihash/src/hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
/// Not all hash types are supported by this library.
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
pub enum Hash {
/// Identity (Raw binary )
Identity,
/// SHA-1 (20-byte hash size)
SHA1,
/// SHA-256 (32-byte hash size)
Expand Down Expand Up @@ -39,6 +41,7 @@ impl Hash {
/// Get the corresponding hash code.
pub fn code(&self) -> u16 {
match self {
Hash::Identity => 0x00,
Hash::SHA1 => 0x11,
Hash::SHA2256 => 0x12,
Hash::SHA2512 => 0x13,
Expand All @@ -60,6 +63,7 @@ impl Hash {
/// Get the hash length in bytes.
pub fn size(&self) -> u8 {
match self {
Hash::Identity => 42,
Hash::SHA1 => 20,
Hash::SHA2256 => 32,
Hash::SHA2512 => 64,
Expand All @@ -81,6 +85,7 @@ impl Hash {
/// Returns the algorithm corresponding to a code, or `None` if no algorithm is matching.
pub fn from_code(code: u16) -> Option<Hash> {
Some(match code {
0x00 => Hash::Identity,
0x11 => Hash::SHA1,
0x12 => Hash::SHA2256,
0x13 => Hash::SHA2512,
Expand Down
77 changes: 57 additions & 20 deletions misc/multihash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,46 @@ macro_rules! match_encoder {
/// ```
///
pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
let (offset, mut output) = encode_hash(hash);
match_encoder!(hash for (input, &mut output[offset ..]) {
SHA1 => sha1::Sha1,
SHA2256 => sha2::Sha256,
SHA2512 => sha2::Sha512,
SHA3224 => sha3::Sha3_224,
SHA3256 => sha3::Sha3_256,
SHA3384 => sha3::Sha3_384,
SHA3512 => sha3::Sha3_512,
Keccak224 => sha3::Keccak224,
Keccak256 => sha3::Keccak256,
Keccak384 => sha3::Keccak384,
Keccak512 => sha3::Keccak512,
Blake2b512 => blake2::Blake2b,
Blake2s256 => blake2::Blake2s,
});

Ok(Multihash { bytes: output.freeze() })
// Custom length encoding for the identity multihash
if let Hash::Identity = hash {
if u64::from(std::u32::MAX) < as_u64(input.len()) {
return Err(EncodeError::UnsupportedInputLength);
}
let mut buf = encode::u16_buffer();
let code = encode::u16(hash.code(), &mut buf);
let mut len_buf = encode::u32_buffer();
let size = encode::u32(input.len() as u32, &mut len_buf);

let total_len = code.len() + size.len() + input.len();

let mut output = BytesMut::with_capacity(total_len);
output.put_slice(code);
output.put_slice(size);
output.put_slice(input);
Ok(Multihash {
bytes: output.freeze(),
})
} else {
let (offset, mut output) = encode_hash(hash);
match_encoder!(hash for (input, &mut output[offset ..]) {
SHA1 => sha1::Sha1,
SHA2256 => sha2::Sha256,
SHA2512 => sha2::Sha512,
SHA3224 => sha3::Sha3_224,
SHA3256 => sha3::Sha3_256,
SHA3384 => sha3::Sha3_384,
SHA3512 => sha3::Sha3_512,
Keccak224 => sha3::Keccak224,
Keccak256 => sha3::Keccak256,
Keccak384 => sha3::Keccak384,
Keccak512 => sha3::Keccak512,
Blake2b512 => blake2::Blake2b,
Blake2s256 => blake2::Blake2s,
});
Ok(Multihash {
bytes: output.freeze(),
})
}
}

// Encode the given [`Hash`] value and ensure the returned [`BytesMut`]
Expand Down Expand Up @@ -180,15 +202,25 @@ impl<'a> MultihashRef<'a> {
let (code, bytes) = decode::u16(&input).map_err(|_| DecodeError::BadInputLength)?;

let alg = Hash::from_code(code).ok_or(DecodeError::UnknownCode)?;

// handle the identity case
if alg == Hash::Identity {
let (hash_len, bytes) = decode::u32(&bytes).map_err(|_| DecodeError::BadInputLength)?;
if as_u64(bytes.len()) != u64::from(hash_len) {
return Err(DecodeError::BadInputLength);
}
return Ok(MultihashRef { bytes: input });
}

let hash_len = usize::from(alg.size());

// Length of input after hash code should be exactly hash_len + 1
if bytes.len() != hash_len + 1 {
return Err(DecodeError::BadInputLength)
return Err(DecodeError::BadInputLength);
}

if usize::from(bytes[0]) != hash_len {
return Err(DecodeError::BadInputLength)
return Err(DecodeError::BadInputLength);
}

Ok(MultihashRef { bytes: input })
Expand Down Expand Up @@ -231,6 +263,11 @@ impl<'a> PartialEq<Multihash> for MultihashRef<'a> {
}
}

#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
fn as_u64(a: usize) -> u64 {
a as u64
}

/// Convert bytes to a hex representation
pub fn to_hex(bytes: &[u8]) -> String {
let mut hex = String::with_capacity(bytes.len() * 2);
Expand Down