tezos_smart_rollup_encoding/
public_key_hash.rs

1// SPDX-FileCopyrightText: 2022-2023 TriliTech <contact@trili.tech>
2//
3// SPDX-License-Identifier: MIT
4
5//! Hash of Layer1 contract ids.
6
7use std::fmt::Display;
8use tezos_data_encoding::enc::BinWriter;
9use tezos_data_encoding::encoding::HasEncoding;
10use tezos_data_encoding::nom::NomReader;
11
12use crypto::base58::{FromBase58Check, FromBase58CheckError};
13use crypto::hash::{
14    ContractTz1Hash, ContractTz2Hash, ContractTz3Hash, Hash, HashTrait, HashType,
15};
16
17/// Hash of Layer1 contract ids.
18#[derive(
19    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, HasEncoding, BinWriter, NomReader,
20)]
21pub enum PublicKeyHash {
22    /// Tz1-contract
23    Ed25519(ContractTz1Hash),
24    /// Tz2-contract
25    Secp256k1(ContractTz2Hash),
26    /// Tz3-contract
27    P256(ContractTz3Hash),
28}
29
30impl Display for PublicKeyHash {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Self::Ed25519(tz1) => write!(f, "{}", tz1),
34            Self::Secp256k1(tz2) => write!(f, "{}", tz2),
35            Self::P256(tz3) => write!(f, "{}", tz3),
36        }
37    }
38}
39
40impl PublicKeyHash {
41    /// Conversion from base58-encoding string (with prefix).
42    pub fn from_b58check(data: &str) -> Result<Self, FromBase58CheckError> {
43        let bytes = data.from_base58check()?;
44        match bytes {
45            _ if bytes.starts_with(HashType::ContractTz1Hash.base58check_prefix()) => Ok(
46                PublicKeyHash::Ed25519(ContractTz1Hash::from_b58check(data)?),
47            ),
48            _ if bytes.starts_with(HashType::ContractTz2Hash.base58check_prefix()) => Ok(
49                PublicKeyHash::Secp256k1(ContractTz2Hash::from_b58check(data)?),
50            ),
51            _ if bytes.starts_with(HashType::ContractTz3Hash.base58check_prefix()) => {
52                Ok(PublicKeyHash::P256(ContractTz3Hash::from_b58check(data)?))
53            }
54            _ => Err(FromBase58CheckError::InvalidBase58),
55        }
56    }
57
58    /// Conversion to base58-encoding string (with prefix).
59    pub fn to_b58check(&self) -> String {
60        match self {
61            Self::Ed25519(tz1) => tz1.to_b58check(),
62            Self::Secp256k1(tz2) => tz2.to_b58check(),
63            Self::P256(tz3) => tz3.to_b58check(),
64        }
65    }
66}
67
68impl From<PublicKeyHash> for Hash {
69    fn from(pkh: PublicKeyHash) -> Self {
70        match pkh {
71            PublicKeyHash::Ed25519(tz1) => tz1.into(),
72            PublicKeyHash::Secp256k1(tz2) => tz2.into(),
73            PublicKeyHash::P256(tz3) => tz3.into(),
74        }
75    }
76}
77
78impl TryFrom<&str> for PublicKeyHash {
79    type Error = FromBase58CheckError;
80
81    fn try_from(value: &str) -> Result<Self, Self::Error> {
82        Self::from_b58check(value)
83    }
84}
85
86#[cfg(test)]
87mod test {
88    use super::*;
89
90    #[test]
91    fn tz1_b58check() {
92        let tz1 = "tz1RjtZUVeLhADFHDL8UwDZA6vjWWhojpu5w";
93
94        let pkh = PublicKeyHash::from_b58check(tz1);
95
96        assert!(matches!(pkh, Ok(PublicKeyHash::Ed25519(_))));
97
98        let tz1_from_pkh = pkh.unwrap().to_b58check();
99
100        assert_eq!(tz1, &tz1_from_pkh);
101    }
102
103    #[test]
104    fn tz2_b58check() {
105        let tz2 = "tz2VGBaXuS6rnaa5hpC92qkgadRJKdEbeGwc";
106
107        let pkh = PublicKeyHash::from_b58check(tz2);
108
109        assert!(matches!(pkh, Ok(PublicKeyHash::Secp256k1(_))));
110
111        let tz2_from_pkh = pkh.unwrap().to_b58check();
112
113        assert_eq!(tz2, &tz2_from_pkh);
114    }
115
116    #[test]
117    fn tz3_b58check() {
118        let tz3 = "tz3WEJYwJ6pPwVbSL8FrSoAXRmFHHZTuEnMA";
119
120        let pkh = PublicKeyHash::from_b58check(tz3);
121
122        assert!(matches!(pkh, Ok(PublicKeyHash::P256(_))));
123
124        let tz3_from_pkh = pkh.unwrap().to_b58check();
125
126        assert_eq!(tz3, &tz3_from_pkh);
127    }
128
129    #[test]
130    fn tz1_encoding() {
131        let tz1 = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx";
132
133        let pkh = PublicKeyHash::from_b58check(tz1).expect("expected valid tz1 hash");
134
135        let mut bin = Vec::new();
136        pkh.bin_write(&mut bin).expect("serialization should work");
137
138        let deserde_pkh = NomReader::nom_read(bin.as_slice())
139            .expect("deserialization should work")
140            .1;
141
142        // Check tag encoding
143        assert_eq!(0_u8, bin[0]);
144        assert_eq!(pkh, deserde_pkh);
145    }
146
147    #[test]
148    fn tz2_encoding() {
149        let tz2 = "tz2KZPgf2rshxNUBXFcTaCemik1LH1v9qz3F";
150
151        let pkh = PublicKeyHash::from_b58check(tz2).expect("expected valid tz2 hash");
152
153        let mut bin = Vec::new();
154        pkh.bin_write(&mut bin).expect("serialization should work");
155
156        let deserde_pkh = NomReader::nom_read(bin.as_slice())
157            .expect("deserialization should work")
158            .1;
159
160        // Check tag encoding
161        assert_eq!(1_u8, bin[0]);
162        assert_eq!(pkh, deserde_pkh);
163    }
164
165    #[test]
166    fn tz3_encoding() {
167        let tz3 = "tz3fTJbAxj1LQCEKDKmYLWKP6e5vNC9vwvyo";
168
169        let pkh = PublicKeyHash::from_b58check(tz3).expect("expected valid tz3 hash");
170
171        let mut bin = Vec::new();
172        pkh.bin_write(&mut bin).expect("serialization should work");
173
174        let deserde_pkh = NomReader::nom_read(bin.as_slice())
175            .expect("deserialization should work")
176            .1;
177
178        // Check tag encoding
179        assert_eq!(2_u8, bin[0]);
180        assert_eq!(pkh, deserde_pkh);
181    }
182}