stacks_core/crypto/
hash160.rs1use 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")]
17pub 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#[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
50pub 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}