partner_chains_plutus_data/
bridge.rs

1//! Plutus data types used by the token bridge
2
3use crate::*;
4use cardano_serialization_lib::{PlutusData, traits::NoneOrEmpty};
5use sidechain_domain::byte_string::ByteString;
6
7/// Datum containing token transfer data
8#[derive(Clone, Debug, PartialEq)]
9pub enum TokenTransferDatum {
10	/// Version 1
11	V1(TokenTransferDatumV1),
12}
13
14/// Datum containing token transfer data, version 1
15#[derive(Clone, Debug, PartialEq)]
16pub enum TokenTransferDatumV1 {
17	/// User-initiated transfer sent to a specific receiver address
18	UserTransfer {
19		/// Receiving address on the Partner Chain
20		receiver: ByteString,
21	},
22	/// Reserve transfer
23	ReserveTransfer,
24}
25
26impl From<TokenTransferDatumV1> for PlutusData {
27	fn from(datum: TokenTransferDatumV1) -> Self {
28		VersionedGenericDatum {
29			version: 1,
30			datum: PlutusData::new_empty_constr_plutus_data(&0u64.into()),
31			appendix: {
32				match datum {
33					TokenTransferDatumV1::UserTransfer { receiver } => {
34						PlutusData::new_single_value_constr_plutus_data(
35							&0u64.into(),
36							&PlutusData::new_bytes(receiver.0),
37						)
38					},
39					TokenTransferDatumV1::ReserveTransfer => {
40						PlutusData::new_empty_constr_plutus_data(&1u64.into())
41					},
42				}
43			},
44		}
45		.into()
46	}
47}
48
49impl From<TokenTransferDatum> for PlutusData {
50	fn from(datum: TokenTransferDatum) -> Self {
51		match datum {
52			TokenTransferDatum::V1(datum) => datum.into(),
53		}
54	}
55}
56
57impl TryFrom<PlutusData> for TokenTransferDatum {
58	type Error = DataDecodingError;
59	fn try_from(data: PlutusData) -> Result<Self, Self::Error> {
60		Self::decode(&data)
61	}
62}
63
64impl VersionedDatum for TokenTransferDatum {
65	fn decode(data: &PlutusData) -> crate::DecodingResult<Self> {
66		match plutus_data_version_and_payload(data) {
67			None => Err(decoding_error_and_log(data, "TokenTransferDatum", "unversioned datum")),
68			Some(VersionedGenericDatum { appendix, version: 1, .. }) => {
69				decode_v1_token_transfer_datum(&appendix).ok_or_else(|| {
70					decoding_error_and_log(&appendix, "TokenTransferDatum", "malformed appendix")
71				})
72			},
73			Some(_) => Err(decoding_error_and_log(data, "TokenTransferDatum", "invalid version")),
74		}
75	}
76}
77
78fn decode_v1_token_transfer_datum(appendix: &PlutusData) -> Option<TokenTransferDatum> {
79	let constr = appendix.as_constr_plutus_data()?;
80	let alternative = u64::from(constr.alternative());
81	let data = constr.data();
82
83	match alternative {
84		0 if data.len() == 1 => {
85			let receiver = data.get(0).as_bytes()?.into();
86			Some(TokenTransferDatum::V1(TokenTransferDatumV1::UserTransfer { receiver }))
87		},
88		1 if data.is_none_or_empty() => {
89			Some(TokenTransferDatum::V1(TokenTransferDatumV1::ReserveTransfer))
90		},
91		_ => None,
92	}
93}
94
95#[cfg(test)]
96mod tests {
97	use super::*;
98	use crate::test_helpers::test_plutus_data;
99
100	fn reserve_transfer_data() -> PlutusData {
101		test_plutus_data!({
102			"list": [
103				{ "constructor": 0, "fields": [] },
104				{ "constructor": 1, "fields": [] },
105				{ "int":1 },
106
107			]
108		})
109	}
110
111	fn user_transfer_data(addr: &[u8]) -> PlutusData {
112		test_plutus_data!({
113					"list": [
114						{ "constructor": 0, "fields": [] },
115						{ "constructor": 0, "fields": [{ "bytes": hex::encode(addr) }] },
116						{ "int":1 },
117
118					]
119		})
120	}
121
122	mod decode {
123		use super::*;
124		use hex_literal::hex;
125		use pretty_assertions::assert_eq;
126
127		#[test]
128		fn user_transfer_v1() {
129			let datum = TokenTransferDatum::decode(&user_transfer_data(&hex!("abcd")))
130				.expect("Should decode successfully");
131			assert_eq!(
132				datum,
133				TokenTransferDatum::V1(TokenTransferDatumV1::UserTransfer {
134					receiver: ByteString(hex!("abcd").into())
135				})
136			)
137		}
138
139		#[test]
140		fn reserve_transfer_v1() {
141			let datum = TokenTransferDatum::decode(&reserve_transfer_data())
142				.expect("Should decode successfully");
143			assert_eq!(datum, TokenTransferDatum::V1(TokenTransferDatumV1::ReserveTransfer))
144		}
145	}
146
147	mod encode {
148		use super::*;
149		use hex_literal::hex;
150		use pretty_assertions::assert_eq;
151
152		#[test]
153		fn user_transfer_v1() {
154			let data: PlutusData = TokenTransferDatum::V1(TokenTransferDatumV1::UserTransfer {
155				receiver: ByteString::from_hex_unsafe("abcd"),
156			})
157			.into();
158
159			assert_eq!(data, user_transfer_data(&hex!("abcd")))
160		}
161
162		#[test]
163		fn reserve_transfer_v1() {
164			let data: PlutusData =
165				TokenTransferDatum::V1(TokenTransferDatumV1::ReserveTransfer).into();
166
167			assert_eq!(data, reserve_transfer_data())
168		}
169	}
170}