mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-13 18:11:22 +00:00
Add peer id inlining for small public keys (#1237)
* Add peer id inlining for small public keys * Apply @twittner suggestions * Restore hashing
This commit is contained in:
@ -24,6 +24,10 @@ use quick_error::quick_error;
|
|||||||
use multihash;
|
use multihash;
|
||||||
use std::{convert::TryFrom, fmt, str::FromStr};
|
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.
|
/// Identifier of a peer of the network.
|
||||||
///
|
///
|
||||||
/// The data is a multihash of the public key of the peer.
|
/// The data is a multihash of the public key of the peer.
|
||||||
@ -52,8 +56,23 @@ impl PeerId {
|
|||||||
#[inline]
|
#[inline]
|
||||||
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();
|
||||||
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 }
|
PeerId { multihash }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,12 +82,14 @@ impl PeerId {
|
|||||||
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::Multihash::from_bytes(data) {
|
||||||
Ok(multihash) => {
|
Ok(multihash) => {
|
||||||
if multihash.algorithm() == multihash::Hash::SHA2256 {
|
if multihash.algorithm() == multihash::Hash::SHA2256
|
||||||
|
|| multihash.algorithm() == multihash::Hash::Identity
|
||||||
|
{
|
||||||
Ok(PeerId { multihash })
|
Ok(PeerId { multihash })
|
||||||
} else {
|
} else {
|
||||||
Err(multihash.into_bytes())
|
Err(multihash.into_bytes())
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(err) => Err(err.data),
|
Err(err) => Err(err.data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +152,8 @@ impl PeerId {
|
|||||||
let enc = public_key.clone().into_protobuf_encoding();
|
let enc = public_key.clone().into_protobuf_encoding();
|
||||||
match multihash::encode(alg, &enc) {
|
match multihash::encode(alg, &enc) {
|
||||||
Ok(h) => Some(h == self.multihash),
|
Ok(h) => Some(h == self.multihash),
|
||||||
Err(multihash::EncodeError::UnsupportedType) => None
|
Err(multihash::EncodeError::UnsupportedType) => None,
|
||||||
|
Err(multihash::EncodeError::UnsupportedInputLength) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ use std::{error, fmt};
|
|||||||
pub enum EncodeError {
|
pub enum EncodeError {
|
||||||
/// The requested hash algorithm isn't supported by this library.
|
/// The requested hash algorithm isn't supported by this library.
|
||||||
UnsupportedType,
|
UnsupportedType,
|
||||||
|
/// The input length is too large for the hash algorithm.
|
||||||
|
UnsupportedInputLength,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for EncodeError {
|
impl fmt::Display for EncodeError {
|
||||||
@ -12,6 +14,10 @@ impl fmt::Display for EncodeError {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
EncodeError::UnsupportedType => write!(f, "This type is not supported yet"),
|
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"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
/// Not all hash types are supported by this library.
|
/// Not all hash types are supported by this library.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
|
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
|
||||||
pub enum Hash {
|
pub enum Hash {
|
||||||
|
/// Identity (Raw binary )
|
||||||
|
Identity,
|
||||||
/// SHA-1 (20-byte hash size)
|
/// SHA-1 (20-byte hash size)
|
||||||
SHA1,
|
SHA1,
|
||||||
/// SHA-256 (32-byte hash size)
|
/// SHA-256 (32-byte hash size)
|
||||||
@ -39,6 +41,7 @@ impl Hash {
|
|||||||
/// Get the corresponding hash code.
|
/// Get the corresponding hash code.
|
||||||
pub fn code(&self) -> u16 {
|
pub fn code(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
|
Hash::Identity => 0x00,
|
||||||
Hash::SHA1 => 0x11,
|
Hash::SHA1 => 0x11,
|
||||||
Hash::SHA2256 => 0x12,
|
Hash::SHA2256 => 0x12,
|
||||||
Hash::SHA2512 => 0x13,
|
Hash::SHA2512 => 0x13,
|
||||||
@ -60,6 +63,7 @@ impl Hash {
|
|||||||
/// Get the hash length in bytes.
|
/// Get the hash length in bytes.
|
||||||
pub fn size(&self) -> u8 {
|
pub fn size(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
|
Hash::Identity => 42,
|
||||||
Hash::SHA1 => 20,
|
Hash::SHA1 => 20,
|
||||||
Hash::SHA2256 => 32,
|
Hash::SHA2256 => 32,
|
||||||
Hash::SHA2512 => 64,
|
Hash::SHA2512 => 64,
|
||||||
@ -81,6 +85,7 @@ impl Hash {
|
|||||||
/// Returns the algorithm corresponding to a code, or `None` if no algorithm is matching.
|
/// Returns the algorithm corresponding to a code, or `None` if no algorithm is matching.
|
||||||
pub fn from_code(code: u16) -> Option<Hash> {
|
pub fn from_code(code: u16) -> Option<Hash> {
|
||||||
Some(match code {
|
Some(match code {
|
||||||
|
0x00 => Hash::Identity,
|
||||||
0x11 => Hash::SHA1,
|
0x11 => Hash::SHA1,
|
||||||
0x12 => Hash::SHA2256,
|
0x12 => Hash::SHA2256,
|
||||||
0x13 => Hash::SHA2512,
|
0x13 => Hash::SHA2512,
|
||||||
|
@ -57,24 +57,46 @@ macro_rules! match_encoder {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
|
pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
|
||||||
let (offset, mut output) = encode_hash(hash);
|
// Custom length encoding for the identity multihash
|
||||||
match_encoder!(hash for (input, &mut output[offset ..]) {
|
if let Hash::Identity = hash {
|
||||||
SHA1 => sha1::Sha1,
|
if u64::from(std::u32::MAX) < as_u64(input.len()) {
|
||||||
SHA2256 => sha2::Sha256,
|
return Err(EncodeError::UnsupportedInputLength);
|
||||||
SHA2512 => sha2::Sha512,
|
}
|
||||||
SHA3224 => sha3::Sha3_224,
|
let mut buf = encode::u16_buffer();
|
||||||
SHA3256 => sha3::Sha3_256,
|
let code = encode::u16(hash.code(), &mut buf);
|
||||||
SHA3384 => sha3::Sha3_384,
|
let mut len_buf = encode::u32_buffer();
|
||||||
SHA3512 => sha3::Sha3_512,
|
let size = encode::u32(input.len() as u32, &mut len_buf);
|
||||||
Keccak224 => sha3::Keccak224,
|
|
||||||
Keccak256 => sha3::Keccak256,
|
|
||||||
Keccak384 => sha3::Keccak384,
|
|
||||||
Keccak512 => sha3::Keccak512,
|
|
||||||
Blake2b512 => blake2::Blake2b,
|
|
||||||
Blake2s256 => blake2::Blake2s,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Multihash { bytes: output.freeze() })
|
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`]
|
// Encode the given [`Hash`] value and ensure the returned [`BytesMut`]
|
||||||
@ -180,15 +202,25 @@ impl<'a> MultihashRef<'a> {
|
|||||||
let (code, bytes) = decode::u16(&input).map_err(|_| DecodeError::BadInputLength)?;
|
let (code, bytes) = decode::u16(&input).map_err(|_| DecodeError::BadInputLength)?;
|
||||||
|
|
||||||
let alg = Hash::from_code(code).ok_or(DecodeError::UnknownCode)?;
|
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());
|
let hash_len = usize::from(alg.size());
|
||||||
|
|
||||||
// Length of input after hash code should be exactly hash_len + 1
|
// Length of input after hash code should be exactly hash_len + 1
|
||||||
if bytes.len() != hash_len + 1 {
|
if bytes.len() != hash_len + 1 {
|
||||||
return Err(DecodeError::BadInputLength)
|
return Err(DecodeError::BadInputLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
if usize::from(bytes[0]) != hash_len {
|
if usize::from(bytes[0]) != hash_len {
|
||||||
return Err(DecodeError::BadInputLength)
|
return Err(DecodeError::BadInputLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(MultihashRef { bytes: input })
|
Ok(MultihashRef { bytes: input })
|
||||||
@ -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
|
/// Convert bytes to a hex representation
|
||||||
pub fn to_hex(bytes: &[u8]) -> String {
|
pub fn to_hex(bytes: &[u8]) -> String {
|
||||||
let mut hex = String::with_capacity(bytes.len() * 2);
|
let mut hex = String::with_capacity(bytes.len() * 2);
|
||||||
|
Reference in New Issue
Block a user