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