stacks_core/
utils.rs

1use std::io;
2
3use strum::FromRepr;
4
5use crate::{
6	address::{AddressVersion, StacksAddress},
7	codec::Codec,
8	contract_name::ContractName,
9};
10
11#[derive(PartialEq, Eq, Debug, Clone)]
12/// Standard principal data type
13pub struct StandardPrincipalData(pub AddressVersion, pub StacksAddress);
14
15impl StandardPrincipalData {
16	/// Create a new standard principal data type from the provided address
17	/// version and stacks address
18	pub fn new(version: AddressVersion, address: StacksAddress) -> Self {
19		Self(version, address)
20	}
21}
22
23impl Codec for StandardPrincipalData {
24	fn codec_serialize<W: io::Write>(&self, dest: &mut W) -> io::Result<()> {
25		self.1.codec_serialize(dest)
26	}
27
28	fn codec_deserialize<R: io::Read>(data: &mut R) -> io::Result<Self>
29	where
30		Self: Sized,
31	{
32		let addr = StacksAddress::codec_deserialize(data)?;
33
34		Ok(Self(addr.version(), addr))
35	}
36}
37
38impl From<StacksAddress> for StandardPrincipalData {
39	fn from(address: StacksAddress) -> Self {
40		Self(address.version(), address)
41	}
42}
43
44#[derive(PartialEq, Eq, Debug, Clone)]
45/// Principal Data type
46pub enum PrincipalData {
47	/// Standard principal data type
48	Standard(StandardPrincipalData),
49	/// Contract principal data type
50	Contract(StandardPrincipalData, ContractName),
51}
52
53#[repr(u8)]
54#[derive(FromRepr, Debug, Clone, Copy)]
55enum PrincipalTypeByte {
56	Standard = 0x05,
57	Contract = 0x06,
58}
59
60impl Codec for PrincipalData {
61	fn codec_serialize<W: io::Write>(&self, dest: &mut W) -> io::Result<()> {
62		match self {
63			Self::Standard(data) => {
64				dest.write_all(&[PrincipalTypeByte::Standard as u8])?;
65				data.codec_serialize(dest)
66			}
67			Self::Contract(data, contract_name) => {
68				dest.write_all(&[PrincipalTypeByte::Contract as u8])?;
69				data.codec_serialize(dest)?;
70				contract_name.codec_serialize(dest)
71			}
72		}
73	}
74
75	fn codec_deserialize<R: io::Read>(data: &mut R) -> io::Result<Self>
76	where
77		Self: Sized,
78	{
79		let mut type_buffer = [0u8; 1];
80		data.read_exact(&mut type_buffer)?;
81
82		let principal_type = PrincipalTypeByte::from_repr(type_buffer[0])
83			.ok_or_else(|| {
84				io::Error::new(
85					io::ErrorKind::InvalidData,
86					format!("Invalid principal type: {}", type_buffer[0]),
87				)
88			})?;
89
90		match principal_type {
91			PrincipalTypeByte::Standard => {
92				let standard_data =
93					StandardPrincipalData::codec_deserialize(data)?;
94
95				Ok(Self::Standard(standard_data))
96			}
97			PrincipalTypeByte::Contract => {
98				let standard_data =
99					StandardPrincipalData::codec_deserialize(data)?;
100				let contract_name = ContractName::codec_deserialize(data)?;
101
102				Ok(Self::Contract(standard_data, contract_name))
103			}
104		}
105	}
106}
107
108impl From<StacksAddress> for PrincipalData {
109	fn from(address: StacksAddress) -> Self {
110		Self::Standard(StandardPrincipalData::from(address))
111	}
112}
113
114#[cfg(test)]
115mod tests {
116	use super::*;
117	use crate::crypto::hash160::Hash160Hasher;
118
119	#[test]
120	fn should_serialize_standard_principal_data() {
121		let addr = StacksAddress::new(
122			AddressVersion::TestnetSingleSig,
123			Hash160Hasher::default(),
124		);
125		let data = PrincipalData::Standard(StandardPrincipalData(
126			addr.version(),
127			addr.clone(),
128		));
129
130		let mut expected_bytes = vec![];
131
132		expected_bytes.push(PrincipalTypeByte::Standard as u8);
133		expected_bytes.push(addr.version() as u8);
134		expected_bytes.extend(addr.hash().as_ref());
135
136		let serialized = data.serialize_to_vec();
137
138		assert_eq!(serialized, expected_bytes);
139	}
140
141	#[test]
142	fn should_deserialize_standard_principal_data() {
143		let addr = StacksAddress::new(
144			AddressVersion::TestnetSingleSig,
145			Hash160Hasher::default(),
146		);
147		let expected_principal_data = PrincipalData::Standard(
148			StandardPrincipalData(addr.version(), addr.clone()),
149		);
150
151		let mut expected_bytes = vec![];
152
153		expected_bytes.push(PrincipalTypeByte::Standard as u8);
154		expected_bytes.push(addr.version() as u8);
155		expected_bytes.extend(addr.hash().as_ref());
156
157		let serialized = expected_principal_data.serialize_to_vec();
158		let deserialized =
159			PrincipalData::deserialize(&mut &serialized[..]).unwrap();
160
161		assert_eq!(deserialized, expected_principal_data);
162	}
163
164	#[test]
165	fn should_serialize_contract_principal_data() {
166		let addr = StacksAddress::new(
167			AddressVersion::TestnetSingleSig,
168			Hash160Hasher::default(),
169		);
170		let contract = ContractName::new("helloworld").unwrap();
171		let data = PrincipalData::Contract(
172			StandardPrincipalData(addr.version(), addr.clone()),
173			contract.clone(),
174		);
175
176		let mut expected_bytes = vec![];
177
178		expected_bytes.push(PrincipalTypeByte::Contract as u8);
179		expected_bytes.push(addr.version() as u8);
180		expected_bytes.extend(addr.hash().as_ref());
181		expected_bytes.push(contract.len() as u8);
182		expected_bytes.extend(contract.as_bytes());
183
184		let serialized = data.serialize_to_vec();
185
186		assert_eq!(serialized, expected_bytes);
187	}
188
189	#[test]
190	fn should_deserialize_contract_principal_data() {
191		let addr = StacksAddress::new(
192			AddressVersion::TestnetSingleSig,
193			Hash160Hasher::default(),
194		);
195		let contract = ContractName::new("helloworld").unwrap();
196		let expected_principal_data = PrincipalData::Contract(
197			StandardPrincipalData(addr.version(), addr.clone()),
198			contract.clone(),
199		);
200
201		let mut expected_bytes = vec![];
202
203		expected_bytes.push(PrincipalTypeByte::Contract as u8);
204		expected_bytes.push(addr.version() as u8);
205		expected_bytes.extend(addr.hash().as_ref());
206		expected_bytes.push(contract.len() as u8);
207		expected_bytes.extend(contract.as_bytes());
208
209		let serialized = expected_principal_data.serialize_to_vec();
210		let deserialized =
211			PrincipalData::deserialize(&mut &serialized[..]).unwrap();
212
213		assert_eq!(deserialized, expected_principal_data);
214	}
215}