stacks_core/crypto/
hash160.rs

1use ripemd::{Digest, Ripemd160};
2use serde::{Deserialize, Serialize};
3
4use super::sha256::Sha256Hasher;
5use crate::{
6	crypto::{Hasher, Hashing, Hex},
7	StacksError, StacksResult,
8};
9
10pub(crate) const HASH160_LENGTH: usize = 20;
11
12#[derive(
13	Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord,
14)]
15#[serde(try_from = "Hex")]
16#[serde(into = "Hex")]
17/// Hash160 hash type
18pub struct Hash160Hashing([u8; HASH160_LENGTH]);
19
20impl Hashing<HASH160_LENGTH> for Hash160Hashing {
21	fn hash(data: &[u8]) -> Self {
22		Self(Ripemd160::digest(Sha256Hasher::new(data)).into())
23	}
24
25	fn as_bytes(&self) -> &[u8] {
26		&self.0
27	}
28
29	fn from_bytes(bytes: &[u8]) -> StacksResult<Self> {
30		Ok(Self(bytes.try_into()?))
31	}
32}
33
34// From conversion is fallible for this type
35#[allow(clippy::from_over_into)]
36impl Into<Hex> for Hash160Hashing {
37	fn into(self) -> Hex {
38		Hex(hex::encode(self.as_bytes()))
39	}
40}
41
42impl TryFrom<Hex> for Hash160Hashing {
43	type Error = StacksError;
44
45	fn try_from(value: Hex) -> Result<Self, Self::Error> {
46		Self::from_bytes(&hex::decode(value.0)?)
47	}
48}
49
50/// Hash160 hasher type
51pub type Hash160Hasher = Hasher<Hash160Hashing, HASH160_LENGTH>;
52
53#[cfg(test)]
54mod tests {
55	use super::*;
56
57	#[test]
58	fn should_sha256_hash_correctly() {
59		let plaintext = "Hello world";
60		let expected_hash_hex = "f5e95668dadf6fdef8521f7e1aa8a5e650c9f849";
61
62		assert_eq!(
63			hex::encode(Hash160Hasher::hash(plaintext.as_bytes())),
64			expected_hash_hex
65		);
66	}
67}