Skip to main content

dpp/state_transition/state_transitions/shielded/unshield_transition/
mod.rs

1pub mod accessors;
2pub mod methods;
3mod state_transition_estimated_fee_validation;
4mod state_transition_like;
5mod state_transition_validation;
6pub mod v0;
7mod version;
8
9use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
10use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0Signable;
11use crate::state_transition::StateTransitionFieldTypes;
12
13pub type UnshieldTransitionLatest = UnshieldTransitionV0;
14
15use crate::identity::state_transition::OptionallyAssetLockProved;
16#[cfg(feature = "json-conversion")]
17use crate::serialization::JsonConvertible;
18#[cfg(feature = "value-conversion")]
19use crate::serialization::ValueConvertible;
20use crate::ProtocolError;
21use bincode::{Decode, Encode};
22use derive_more::From;
23use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize, PlatformSignable};
24use platform_versioning::PlatformVersioned;
25#[cfg(feature = "serde-conversion")]
26use serde::{Deserialize, Serialize};
27
28#[derive(
29    Debug,
30    Clone,
31    Encode,
32    Decode,
33    PlatformDeserialize,
34    PlatformSerialize,
35    PlatformSignable,
36    PlatformVersioned,
37    From,
38    PartialEq,
39)]
40#[cfg_attr(
41    feature = "serde-conversion",
42    derive(Serialize, Deserialize),
43    serde(tag = "$formatVersion")
44)]
45#[cfg_attr(
46    all(feature = "json-conversion", feature = "serde-conversion"),
47    derive(JsonConvertible)
48)]
49#[cfg_attr(feature = "value-conversion", derive(ValueConvertible))]
50#[platform_serialize(unversioned)] //versioned directly, no need to use platform_version
51#[platform_version_path_bounds(
52    "dpp.state_transition_serialization_versions.unshield_state_transition"
53)]
54pub enum UnshieldTransition {
55    #[cfg_attr(feature = "serde-conversion", serde(rename = "0"))]
56    V0(UnshieldTransitionV0),
57}
58
59impl OptionallyAssetLockProved for UnshieldTransition {}
60
61impl StateTransitionFieldTypes for UnshieldTransition {
62    fn signature_property_paths() -> Vec<&'static str> {
63        vec![]
64    }
65
66    fn identifiers_property_paths() -> Vec<&'static str> {
67        vec![]
68    }
69
70    fn binary_property_paths() -> Vec<&'static str> {
71        vec![]
72    }
73}
74
75#[cfg(all(
76    test,
77    feature = "json-conversion",
78    feature = "value-conversion",
79    feature = "serde-conversion"
80))]
81pub(crate) mod json_convertible_tests {
82    use super::*;
83    use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
84    use platform_value::{platform_value, Bytes32};
85    use serde_json::json;
86
87    fn fixture_action() -> crate::shielded::SerializedAction {
88        crate::shielded::SerializedAction {
89            nullifier: [0x11; 32],
90            rk: [0x22; 32],
91            cmx: [0x33; 32],
92            encrypted_note: vec![0x44; 216],
93            cv_net: [0x55; 32],
94            spend_auth_sig: [0x66; 64],
95        }
96    }
97
98    pub(crate) fn fixture() -> UnshieldTransition {
99        UnshieldTransition::V0(UnshieldTransitionV0 {
100            output_address: crate::address_funds::PlatformAddress::P2pkh([0xa1; 20]),
101            actions: vec![fixture_action()],
102            unshielding_amount: 250_000,
103            anchor: [0x77; 32],
104            proof: vec![0x88; 192],
105            binding_signature: [0x99; 64],
106        })
107    }
108
109    #[test]
110    fn json_round_trip_with_full_wire_shape() {
111        use crate::serialization::JsonConvertible;
112        let original = fixture();
113        let json = original.to_json().expect("to_json");
114        // Sized-int field whose JSON wire encoding loses size info:
115        // `unshieldingAmount` (u64). `outputAddress` is a `PlatformAddress` which
116        // serializes as hex string in HR (1 byte type + 20 byte hash) and bytes
117        // non-HR. SerializedAction byte fields: 32-byte arrays are base64 (HR);
118        // value-path uses `Value::Bytes32`.
119        assert_eq!(
120            json,
121            json!({
122                "$formatVersion": "0",
123                "outputAddress": "00a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
124                "actions": [{
125                    "nullifier": "ERERERERERERERERERERERERERERERERERERERERERE=",
126                    "rk": "IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI=",
127                    "cmx": "MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM=",
128                    "encryptedNote": "RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE",
129                    "cvNet": "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=",
130                    "spendAuthSig": "ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZg==",
131                }],
132                "unshieldingAmount": 250_000,
133                "anchor": "d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3c=",
134                "proof": "iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI",
135                "bindingSignature": "mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmQ==",
136            })
137        );
138        let recovered = UnshieldTransition::from_json(json).expect("from_json");
139        assert_eq!(original, recovered);
140    }
141
142    #[test]
143    fn value_round_trip_with_full_wire_shape() {
144        use crate::serialization::ValueConvertible;
145        let original = fixture();
146        let value = original.to_object().expect("to_object");
147        // Explicit suffix locks `unshieldingAmount` as u64. `PlatformAddress` on the
148        // non-HR path serializes as raw bytes (21 = 1 type + 20 hash); for P2pkh the
149        // type byte is 0x00. Fixed-size 32-byte fields → `Value::Bytes32`.
150        let mut output_address_bytes = vec![0x00];
151        output_address_bytes.extend(vec![0xa1; 20]);
152        assert_eq!(
153            value,
154            platform_value!({
155                "$formatVersion": "0",
156                "outputAddress": platform_value::Value::Bytes(output_address_bytes),
157                "actions": [{
158                    "nullifier": Bytes32::new([0x11; 32]),
159                    "rk": Bytes32::new([0x22; 32]),
160                    "cmx": Bytes32::new([0x33; 32]),
161                    "encryptedNote": platform_value::Value::Bytes(vec![0x44; 216]),
162                    "cvNet": Bytes32::new([0x55; 32]),
163                    "spendAuthSig": platform_value::Value::Bytes(vec![0x66; 64]),
164                }],
165                "unshieldingAmount": 250_000u64,
166                "anchor": Bytes32::new([0x77; 32]),
167                "proof": platform_value::Value::Bytes(vec![0x88; 192]),
168                "bindingSignature": platform_value::Value::Bytes(vec![0x99; 64]),
169            })
170        );
171        let recovered = UnshieldTransition::from_object(value).expect("from_object");
172        assert_eq!(original, recovered);
173    }
174}