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