1use std::str::FromStr;
3
4use anyhow::anyhow;
5use cardano_serialization_lib as csl;
6
7#[cfg(feature = "lbf")]
8use lbr_prelude::json::{self, Error, Json};
9use num_bigint::BigInt;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13use crate as plutus_ledger_api;
14use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA};
15use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL};
16use crate::plutus_data::{
17 parse_constr, parse_fixed_len_constr_fields, IsPlutusData, PlutusData, PlutusDataError,
18};
19use crate::v1::crypto::Ed25519PubKeyHash;
20use crate::v1::script::ValidatorHash;
21
22#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
32#[is_plutus_data_derive_strategy = "Constr"]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[cfg_attr(feature = "lbf", derive(Json))]
35pub struct Address {
36 pub credential: Credential,
37 pub staking_credential: Option<StakingCredential>,
38}
39
40impl Address {
41 pub fn with_extra_info(&self, network_tag: u8) -> AddressWithExtraInfo {
42 AddressWithExtraInfo {
43 address: self,
44 network_tag,
45 }
46 }
47}
48
49impl FromStr for Address {
50 type Err = anyhow::Error;
51
52 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
53 let csl_addr = csl::Address::from_bech32(s)
54 .map_err(|err| anyhow!("Couldn't parse bech32 address: {}", err))?;
55 csl_addr
56 .try_to_pla()
57 .map_err(|err| anyhow!("Couldn't convert address: {}", err))
58 }
59}
60
61impl TryFromCSL<csl::Address> for Address {
62 fn try_from_csl(value: &csl::Address) -> Result<Self, TryFromCSLError> {
63 if let Some(addr) = csl::BaseAddress::from_address(value) {
64 Ok(Address {
65 credential: Credential::from_csl(&addr.payment_cred()),
66 staking_credential: Some(StakingCredential::from_csl(&addr.stake_cred())),
67 })
68 } else if let Some(addr) = csl::PointerAddress::from_address(value) {
69 Ok(Address {
70 credential: Credential::from_csl(&addr.payment_cred()),
71 staking_credential: Some(StakingCredential::from_csl(&addr.stake_pointer())),
72 })
73 } else if let Some(addr) = csl::EnterpriseAddress::from_address(value) {
74 Ok(Address {
75 credential: Credential::from_csl(&addr.payment_cred()),
76 staking_credential: None,
77 })
78 } else {
79 Err(TryFromCSLError::ImpossibleConversion(format!(
80 "Unable to represent address {:?}",
81 value
82 )))
83 }
84 }
85}
86
87#[derive(Clone, Debug)]
88pub struct AddressWithExtraInfo<'a> {
91 pub address: &'a Address,
92 pub network_tag: u8,
93}
94
95impl TryFromPLA<AddressWithExtraInfo<'_>> for csl::Address {
96 fn try_from_pla(val: &AddressWithExtraInfo<'_>) -> Result<Self, TryFromPLAError> {
97 let payment = val.address.credential.try_to_csl()?;
98
99 Ok(match val.address.staking_credential {
100 None => csl::EnterpriseAddress::new(val.network_tag, &payment).to_address(),
101 Some(ref sc) => match sc {
102 StakingCredential::Hash(c) => {
103 csl::BaseAddress::new(val.network_tag, &payment, &c.try_to_csl()?).to_address()
104 }
105 StakingCredential::Pointer(ptr) => {
106 csl::PointerAddress::new(val.network_tag, &payment, &ptr.try_to_csl()?)
107 .to_address()
108 }
109 },
110 })
111 }
112}
113
114impl std::fmt::Display for AddressWithExtraInfo<'_> {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 let bech32_addr: Option<String> = self
118 .try_to_csl()
119 .ok()
120 .and_then(|csl_addr: csl::Address| csl_addr.to_bech32(None).ok());
121 match bech32_addr {
122 Some(addr) => write!(f, "{}", addr),
123 None => write!(f, "INVALID ADDRESS {:?}", self),
124 }
125 }
126}
127
128#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
134#[is_plutus_data_derive_strategy = "Constr"]
135#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
136pub enum Credential {
137 PubKey(Ed25519PubKeyHash),
138 Script(ValidatorHash),
139}
140
141#[cfg(feature = "lbf")]
142impl Json for Credential {
143 fn to_json(&self) -> serde_json::Value {
144 match self {
145 Credential::PubKey(pkh) => {
146 json::json_constructor("PubKeyCredential", vec![pkh.to_json()])
147 }
148 Credential::Script(val_hash) => {
149 json::json_constructor("ScriptCredential", vec![val_hash.to_json()])
150 }
151 }
152 }
153
154 fn from_json(value: &serde_json::Value) -> Result<Self, Error> {
155 json::case_json_constructor(
156 "Plutus.V1.Credential",
157 vec![
158 (
159 "PubKeyCredential",
160 Box::new(|ctor_fields| match &ctor_fields[..] {
161 [pkh] => Ok(Credential::PubKey(Json::from_json(pkh)?)),
162 _ => Err(Error::UnexpectedArrayLength {
163 wanted: 1,
164 got: ctor_fields.len(),
165 parser: "Plutus.V1.Credential".to_owned(),
166 }),
167 }),
168 ),
169 (
170 "ScriptCredential",
171 Box::new(|ctor_fields| match &ctor_fields[..] {
172 [val_hash] => Ok(Credential::Script(Json::from_json(val_hash)?)),
173 _ => Err(Error::UnexpectedArrayLength {
174 wanted: 1,
175 got: ctor_fields.len(),
176 parser: "Plutus.V1.Credential".to_owned(),
177 }),
178 }),
179 ),
180 ],
181 value,
182 )
183 }
184}
185
186impl FromCSL<csl::Credential> for Credential {
187 fn from_csl(value: &csl::Credential) -> Self {
188 match value.kind() {
189 csl::CredKind::Key => {
190 Credential::PubKey(Ed25519PubKeyHash::from_csl(&value.to_keyhash().unwrap()))
191 }
192 csl::CredKind::Script => {
193 Credential::Script(ValidatorHash::from_csl(&value.to_scripthash().unwrap()))
194 }
195 }
196 }
197}
198
199impl TryFromPLA<Credential> for csl::Credential {
200 fn try_from_pla(val: &Credential) -> Result<Self, TryFromPLAError> {
201 match val {
202 Credential::PubKey(pkh) => Ok(csl::Credential::from_keyhash(&pkh.try_to_csl()?)),
203 Credential::Script(sh) => Ok(csl::Credential::from_scripthash(&sh.0.try_to_csl()?)),
204 }
205 }
206}
207
208#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
214#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
215pub enum StakingCredential {
216 Hash(Credential),
217 Pointer(ChainPointer),
218}
219
220impl IsPlutusData for StakingCredential {
222 fn to_plutus_data(&self) -> PlutusData {
223 match self {
224 StakingCredential::Hash(credential) => {
225 PlutusData::Constr(BigInt::from(0), vec![credential.to_plutus_data()])
226 }
227 StakingCredential::Pointer(ChainPointer {
228 slot_number,
229 transaction_index,
230 certificate_index,
231 }) => PlutusData::Constr(
232 BigInt::from(1),
233 vec![
234 slot_number.to_plutus_data(),
235 transaction_index.to_plutus_data(),
236 certificate_index.to_plutus_data(),
237 ],
238 ),
239 }
240 }
241
242 fn from_plutus_data(data: &PlutusData) -> Result<Self, PlutusDataError> {
243 let (tag, fields) = parse_constr(data)?;
244 match tag {
245 0 => {
246 let [field] = parse_fixed_len_constr_fields::<1>(fields)?;
247 Ok(Self::Hash(Credential::from_plutus_data(field)?))
248 }
249 1 => {
250 let [field_0, field_1, field_2] = parse_fixed_len_constr_fields::<3>(fields)?;
251 Ok(Self::Pointer(ChainPointer {
252 slot_number: Slot::from_plutus_data(field_0)?,
253 transaction_index: TransactionIndex::from_plutus_data(field_1)?,
254 certificate_index: CertificateIndex::from_plutus_data(field_2)?,
255 }))
256 }
257 _ => Err(PlutusDataError::UnexpectedPlutusInvariant {
258 wanted: "Constr with tag 0 or 1".to_owned(),
259 got: tag.to_string(),
260 }),
261 }
262 }
263}
264
265#[cfg(feature = "lbf")]
266impl Json for StakingCredential {
267 fn to_json(&self) -> serde_json::Value {
268 match self {
269 StakingCredential::Hash(pkh) => {
270 json::json_constructor("StakingHash", vec![pkh.to_json()])
271 }
272 StakingCredential::Pointer(val_hash) => {
273 json::json_constructor("StakingPtr", vec![val_hash.to_json()])
274 }
275 }
276 }
277
278 fn from_json(value: &serde_json::Value) -> Result<Self, Error> {
279 json::case_json_constructor(
280 "Plutus.V1.StakingCredential",
281 vec![
282 (
283 "StakingHash",
284 Box::new(|ctor_fields| match &ctor_fields[..] {
285 [pkh] => Ok(StakingCredential::Hash(Json::from_json(pkh)?)),
286 _ => Err(Error::UnexpectedArrayLength {
287 wanted: 1,
288 got: ctor_fields.len(),
289 parser: "Plutus.V1.StakingCredential".to_owned(),
290 }),
291 }),
292 ),
293 (
294 "StakingPtr",
295 Box::new(|ctor_fields| match &ctor_fields[..] {
296 [val_hash] => Ok(StakingCredential::Pointer(Json::from_json(val_hash)?)),
297 _ => Err(Error::UnexpectedArrayLength {
298 wanted: 1,
299 got: ctor_fields.len(),
300 parser: "Plutus.V1.StakingCredential".to_owned(),
301 }),
302 }),
303 ),
304 ],
305 value,
306 )
307 }
308}
309
310impl FromCSL<csl::Credential> for StakingCredential {
311 fn from_csl(value: &csl::Credential) -> Self {
312 StakingCredential::Hash(Credential::from_csl(value))
313 }
314}
315
316impl TryFromPLA<StakingCredential> for csl::Credential {
317 fn try_from_pla(val: &StakingCredential) -> Result<Self, TryFromPLAError> {
318 match val {
319 StakingCredential::Hash(c) => c.try_to_csl(),
320 StakingCredential::Pointer(_) => Err(TryFromPLAError::ImpossibleConversion(
321 "cannot represent chain pointer".into(),
322 )),
323 }
324 }
325}
326
327impl FromCSL<csl::Pointer> for StakingCredential {
328 fn from_csl(value: &csl::Pointer) -> Self {
329 StakingCredential::Pointer(ChainPointer::from_csl(value))
330 }
331}
332
333#[derive(Clone, Debug)]
334pub struct RewardAddressWithExtraInfo<'a> {
335 pub staking_credential: &'a StakingCredential,
336 pub network_tag: u8,
337}
338
339impl TryFromPLA<RewardAddressWithExtraInfo<'_>> for csl::RewardAddress {
340 fn try_from_pla(val: &RewardAddressWithExtraInfo<'_>) -> Result<Self, TryFromPLAError> {
341 Ok(csl::RewardAddress::new(
342 val.network_tag,
343 &val.staking_credential.try_to_csl()?,
344 ))
345 }
346}
347
348#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
358#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
359#[cfg_attr(feature = "lbf", derive(Json))]
360pub struct ChainPointer {
361 pub slot_number: Slot,
362 pub transaction_index: TransactionIndex,
363 pub certificate_index: CertificateIndex,
364}
365
366impl FromCSL<csl::Pointer> for ChainPointer {
367 fn from_csl(value: &csl::Pointer) -> Self {
368 ChainPointer {
369 slot_number: Slot::from_csl(&value.slot_bignum()),
370 transaction_index: TransactionIndex::from_csl(&value.tx_index_bignum()),
371 certificate_index: CertificateIndex::from_csl(&value.cert_index_bignum()),
372 }
373 }
374}
375
376impl TryFromPLA<ChainPointer> for csl::Pointer {
377 fn try_from_pla(val: &ChainPointer) -> Result<Self, TryFromPLAError> {
378 Ok(csl::Pointer::new_pointer(
379 &val.slot_number.try_to_csl()?,
380 &val.transaction_index.try_to_csl()?,
381 &val.certificate_index.try_to_csl()?,
382 ))
383 }
384}
385
386#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
392#[is_plutus_data_derive_strategy = "Newtype"]
393#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
394#[cfg_attr(feature = "lbf", derive(Json))]
395pub struct Slot(pub BigInt);
396
397impl FromCSL<csl::BigNum> for Slot {
398 fn from_csl(value: &csl::BigNum) -> Self {
399 Slot(BigInt::from_csl(value))
400 }
401}
402
403impl TryFromPLA<Slot> for csl::BigNum {
404 fn try_from_pla(val: &Slot) -> Result<Self, TryFromPLAError> {
405 val.0.try_to_csl()
406 }
407}
408
409#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
415#[is_plutus_data_derive_strategy = "Newtype"]
416#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
417#[cfg_attr(feature = "lbf", derive(Json))]
418pub struct CertificateIndex(pub BigInt);
419
420impl FromCSL<csl::BigNum> for CertificateIndex {
421 fn from_csl(value: &csl::BigNum) -> Self {
422 CertificateIndex(BigInt::from_csl(value))
423 }
424}
425
426impl TryFromPLA<CertificateIndex> for csl::BigNum {
427 fn try_from_pla(val: &CertificateIndex) -> Result<Self, TryFromPLAError> {
428 val.0.try_to_csl()
429 }
430}
431
432#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
439#[is_plutus_data_derive_strategy = "Newtype"]
440#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
441#[cfg_attr(feature = "lbf", derive(Json))]
442pub struct TransactionIndex(pub BigInt);
443
444impl FromCSL<csl::BigNum> for TransactionIndex {
445 fn from_csl(value: &csl::BigNum) -> Self {
446 TransactionIndex(BigInt::from_csl(value))
447 }
448}
449
450impl TryFromPLA<TransactionIndex> for csl::BigNum {
451 fn try_from_pla(val: &TransactionIndex) -> Result<Self, TryFromPLAError> {
452 val.0.try_to_csl()
453 }
454}