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