Skip to main content

dpp/state_transition/state_transitions/shielded/shield_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::shield_transition::v0::ShieldTransitionV0;
10use crate::state_transition::shield_transition::v0::ShieldTransitionV0Signable;
11use crate::state_transition::StateTransitionFieldTypes;
12
13pub type ShieldTransitionLatest = ShieldTransitionV0;
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.shield_state_transition"
57)]
58pub enum ShieldTransition {
59    #[cfg_attr(feature = "serde-conversion", serde(rename = "0"))]
60    V0(ShieldTransitionV0),
61}
62
63impl OptionallyAssetLockProved for ShieldTransition {}
64
65impl StateTransitionFieldTypes for ShieldTransition {
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::address_funds::{AddressFundsFeeStrategyStep, AddressWitness, PlatformAddress};
88    use crate::shielded::SerializedAction;
89    use crate::state_transition::shield_transition::v0::ShieldTransitionV0;
90    use platform_value::{platform_value, BinaryData, Bytes32};
91    use serde_json::json;
92    use std::collections::BTreeMap;
93
94    fn fixture_action() -> SerializedAction {
95        SerializedAction {
96            nullifier: [0x11; 32],
97            rk: [0x22; 32],
98            cmx: [0x33; 32],
99            encrypted_note: vec![0x44; 216],
100            cv_net: [0x55; 32],
101            spend_auth_sig: [0x66; 64],
102        }
103    }
104
105    pub(crate) fn fixture() -> ShieldTransition {
106        let mut inputs = BTreeMap::new();
107        inputs.insert(PlatformAddress::P2pkh([0xa1; 20]), (3u32, 500_000u64));
108        ShieldTransition::V0(ShieldTransitionV0 {
109            inputs,
110            actions: vec![fixture_action()],
111            amount: 250_000,
112            anchor: [0x77; 32],
113            proof: vec![0x88; 192],
114            binding_signature: [0x99; 64],
115            fee_strategy: vec![AddressFundsFeeStrategyStep::DeductFromInput(0)],
116            user_fee_increase: 5,
117            input_witnesses: vec![AddressWitness::P2pkh {
118                signature: BinaryData::new(vec![0xaa; 65]),
119            }],
120        })
121    }
122
123    #[test]
124    fn json_round_trip_with_full_wire_shape() {
125        use crate::serialization::JsonConvertible;
126        let original = fixture();
127        let json = original.to_json().expect("to_json");
128        // Sized-int fields whose JSON wire encoding loses size info:
129        // `inputs[].nonce` (u32 AddressNonce), `inputs[].amount` (u64),
130        // `amount` (u64), `feeStrategy[].index` (u16),
131        // `userFeeIncrease` (u16). PlatformAddress → hex string in HR / 21 bytes
132        // non-HR; AddressWitness uses externally-tagged `{type, signature}`.
133        // BTreeMap<PlatformAddress, (u32, u64)> serializes as array of
134        // `{address, nonce, amount}` objects, NOT a JSON map. The value-path
135        // assertion locks all sized variants via explicit suffixes.
136        assert_eq!(
137            json,
138            json!({
139                "$formatVersion": "0",
140                "inputs": [{
141                    "address": "00a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
142                    "nonce": 3,
143                    "amount": 500_000,
144                }],
145                "actions": [{
146                    "nullifier": "ERERERERERERERERERERERERERERERERERERERERERE=",
147                    "rk": "IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI=",
148                    "cmx": "MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM=",
149                    "encryptedNote": "RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE",
150                    "cvNet": "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=",
151                    "spendAuthSig": "ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZg==",
152                }],
153                "amount": 250_000,
154                "anchor": "d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3c=",
155                "proof": "iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI",
156                "bindingSignature": "mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmQ==",
157                "feeStrategy": [{"$type": "deductFromInput", "index": 0}],
158                "userFeeIncrease": 5,
159                "inputWitnesses": [{
160                    "$type": "p2pkh",
161                    "signature": "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=",
162                }],
163            })
164        );
165        let recovered = ShieldTransition::from_json(json).expect("from_json");
166        assert_eq!(original, recovered);
167    }
168
169    #[test]
170    fn value_round_trip_with_full_wire_shape() {
171        use crate::serialization::ValueConvertible;
172        let original = fixture();
173        let value = original.to_object().expect("to_object");
174        // Explicit suffixes lock in sized variants: `inputs[].nonce` u32
175        // (AddressNonce), `inputs[].amount` u64, `amount` u64,
176        // `feeStrategy[].index` u16, `userFeeIncrease` u16.
177        // PlatformAddress non-HR → 21-byte `Value::Bytes` (P2pkh type byte 0x00).
178        let mut address_bytes = vec![0x00];
179        address_bytes.extend(vec![0xa1; 20]);
180        assert_eq!(
181            value,
182            platform_value!({
183                "$formatVersion": "0",
184                "inputs": [{
185                    "address": platform_value::Value::Bytes(address_bytes),
186                    "nonce": 3u32,
187                    "amount": 500_000u64,
188                }],
189                "actions": [{
190                    "nullifier": Bytes32::new([0x11; 32]),
191                    "rk": Bytes32::new([0x22; 32]),
192                    "cmx": Bytes32::new([0x33; 32]),
193                    "encryptedNote": platform_value::Value::Bytes(vec![0x44; 216]),
194                    "cvNet": Bytes32::new([0x55; 32]),
195                    "spendAuthSig": platform_value::Value::Bytes(vec![0x66; 64]),
196                }],
197                "amount": 250_000u64,
198                "anchor": Bytes32::new([0x77; 32]),
199                "proof": platform_value::Value::Bytes(vec![0x88; 192]),
200                "bindingSignature": platform_value::Value::Bytes(vec![0x99; 64]),
201                "feeStrategy": [{"$type": "deductFromInput", "index": 0u16}],
202                "userFeeIncrease": 5u16,
203                "inputWitnesses": [{
204                    "$type": "p2pkh",
205                    "signature": BinaryData::new(vec![0xaa; 65]),
206                }],
207            })
208        );
209        let recovered = ShieldTransition::from_object(value).expect("from_object");
210        assert_eq!(original, recovered);
211    }
212}