pallas_primitives/alonzo/
json.rs

1use std::ops::Deref;
2
3use serde_json::json;
4
5use crate::ToCanonicalJson;
6
7impl<A> super::Constr<A> {
8    pub fn constructor_value(&self) -> Option<u64> {
9        match self.tag {
10            121..=127 => Some(self.tag - 121),
11            1280..=1400 => Some(self.tag - 1280 + 7),
12            102 => self.any_constructor,
13            _ => None,
14        }
15    }
16}
17
18// infered from https://github.com/input-output-hk/cardano-node/blob/c1efb2f97134c0607c982246a36e3da7266ac194/cardano-api/src/Cardano/Api/ScriptData.hs#L254
19impl ToCanonicalJson for super::PlutusData {
20    fn to_json(&self) -> serde_json::Value {
21        match self {
22            super::PlutusData::Constr(x) => {
23                let fields: Vec<_> = x.fields.iter().map(|i| i.to_json()).collect();
24                json!({ "constructor": x.constructor_value(), "fields": fields })
25            }
26            super::PlutusData::Map(x) => {
27                let map: Vec<_> = x
28                    .iter()
29                    .map(|(k, v)| json!({ "k": k.to_json(), "v": v.to_json() }))
30                    .collect();
31                json!({ "map": map })
32            }
33            super::PlutusData::BigInt(int) => match int {
34                super::BigInt::Int(n) => match i64::try_from(*n.deref()) {
35                    Ok(x) => json!({ "int": x }),
36                    Err(_) => {
37                        json!({ "bignint": hex::encode(i128::from(*n.deref()).to_be_bytes()) })
38                    }
39                },
40                // WARNING / TODO: the CDDL shows a bignum variants of arbitrary length expressed as
41                // bytes, but I can't find the corresponding mapping to JSON in the
42                // Haskell implementation. Not sure what I'm missing. For the time
43                // being, I'll invent a new JSON expression that uses hex strings as
44                // a way to express the values.
45                super::BigInt::BigUInt(x) => json!({ "biguint": hex::encode(x.as_slice())}),
46                super::BigInt::BigNInt(x) => json!({ "bignint": hex::encode(x.as_slice())}),
47            },
48            super::PlutusData::BoundedBytes(x) => json!({ "bytes": hex::encode(x.as_slice())}),
49            super::PlutusData::Array(x) => {
50                let list: Vec<_> = x.iter().map(|i| i.to_json()).collect();
51                json!({ "list": list })
52            }
53        }
54    }
55}
56
57impl ToCanonicalJson for super::NativeScript {
58    fn to_json(&self) -> serde_json::Value {
59        match self {
60            super::NativeScript::ScriptPubkey(x) => {
61                json!({ "keyHash": x.to_string(), "type": "sig"})
62            }
63            super::NativeScript::ScriptAll(x) => {
64                let scripts: Vec<_> = x.iter().map(|i| i.to_json()).collect();
65                json!({ "type": "all", "scripts": scripts})
66            }
67            super::NativeScript::ScriptAny(x) => {
68                let scripts: Vec<_> = x.iter().map(|i| i.to_json()).collect();
69                json!({ "type": "any", "scripts": scripts})
70            }
71            super::NativeScript::ScriptNOfK(n, k) => {
72                let scripts: Vec<_> = k.iter().map(|i| i.to_json()).collect();
73                json!({ "type": "atLeast", "required": n, "scripts" : scripts })
74            }
75            super::NativeScript::InvalidBefore(slot) => json!({ "type": "after", "slot": slot }),
76            super::NativeScript::InvalidHereafter(slot) => json!({"type": "before", "slot": slot }),
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use pallas_codec::minicbor;
84
85    use crate::{alonzo::Block, ToCanonicalJson};
86
87    type BlockWrapper = (u16, Block);
88
89    #[test]
90    fn test_datums_serialize_as_expected() {
91        let test_blocks = [(
92            include_str!("../../../test_data/alonzo9.block"),
93            include_str!("../../../test_data/alonzo9.datums"),
94        )];
95
96        for (idx, (block_str, jsonl_str)) in test_blocks.iter().enumerate() {
97            println!("decoding json block {}", idx + 1);
98
99            let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
100
101            let (_, block): BlockWrapper = minicbor::decode(&bytes[..])
102                .unwrap_or_else(|_| panic!("error decoding cbor for file {idx}"));
103
104            let mut datums = jsonl_str.lines();
105
106            for ws in block.transaction_witness_sets.iter() {
107                if let Some(pds) = &ws.plutus_data {
108                    for pd in pds.iter() {
109                        let expected: serde_json::Value =
110                            serde_json::from_str(datums.next().unwrap()).unwrap();
111                        let current = pd.to_json();
112                        assert_eq!(current, expected);
113                    }
114                }
115            }
116        }
117    }
118
119    #[test]
120    fn test_native_scripts_serialize_as_expected() {
121        let test_blocks = [(
122            include_str!("../../../test_data/alonzo9.block"),
123            include_str!("../../../test_data/alonzo9.native"),
124        )];
125
126        for (idx, (block_str, jsonl_str)) in test_blocks.iter().enumerate() {
127            println!("decoding json block {}", idx + 1);
128
129            let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
130
131            let (_, block): BlockWrapper = minicbor::decode(&bytes[..])
132                .unwrap_or_else(|_| panic!("error decoding cbor for file {idx}"));
133
134            let mut scripts = jsonl_str.lines();
135
136            for ws in block.transaction_witness_sets.iter() {
137                if let Some(nss) = &ws.native_script {
138                    for ns in nss.iter() {
139                        let expected: serde_json::Value =
140                            serde_json::from_str(scripts.next().unwrap()).unwrap();
141                        let current = ns.to_json();
142                        assert_eq!(current, expected);
143                    }
144                }
145            }
146        }
147    }
148}