Skip to main content

dpp/asset_lock/
mod.rs

1use crate::asset_lock::reduced_asset_lock_value::AssetLockValue;
2
3pub mod reduced_asset_lock_value;
4
5pub type PastAssetLockStateTransitionHashes = Vec<Vec<u8>>;
6
7/// An enumeration of the possible states when querying platform to get the stored state of an outpoint
8/// representing if the asset lock was already used or not.
9#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
10#[serde(tag = "$type")]
11pub enum StoredAssetLockInfo {
12    /// The asset lock was fully consumed in the past
13    FullyConsumed,
14    /// The asset lock was partially consumed, and we stored the asset lock value in the state
15    PartiallyConsumed(AssetLockValue),
16    /// The asset lock is not yet known to Platform
17    NotPresent,
18}
19
20#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
21impl crate::serialization::JsonConvertible for StoredAssetLockInfo {}
22
23#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
24impl crate::serialization::ValueConvertible for StoredAssetLockInfo {}
25
26#[cfg(all(
27    test,
28    feature = "json-conversion",
29    feature = "value-conversion",
30    feature = "serde-conversion"
31))]
32mod json_convertible_tests {
33    use super::*;
34    use platform_value::{platform_value, Bytes32, Value};
35    use platform_version::version::PlatformVersion;
36    use serde_json::json;
37
38    fn partially_consumed_fixture() -> StoredAssetLockInfo {
39        let asset_lock_value = AssetLockValue::new(
40            1_000_000,
41            vec![0xaa, 0xbb, 0xcc, 0xdd],
42            500_000,
43            vec![Bytes32::new([0x42; 32])],
44            PlatformVersion::latest(),
45        )
46        .expect("fixture");
47        StoredAssetLockInfo::PartiallyConsumed(asset_lock_value)
48    }
49
50    // `StoredAssetLockInfo` is internally tagged (`#[serde(tag = "$type")]`):
51    // unit variants serialize as `{"$type": "FullyConsumed"}`, and the
52    // `PartiallyConsumed` newtype variant flattens its inner `AssetLockValue`
53    // (itself `tag = "$formatVersion"`) up to the same wire level, giving
54    // `{"$type": "PartiallyConsumed", "$formatVersion": "0", ...}`. The `$`
55    // prefix follows the codebase rule: the discriminator is `$`-prefixed when
56    // other `$`-fields (here `$formatVersion`) share the same wire level.
57
58    #[test]
59    fn json_round_trip_fully_consumed() {
60        use crate::serialization::JsonConvertible;
61        let original = StoredAssetLockInfo::FullyConsumed;
62        let json = original.to_json().expect("to_json");
63        assert_eq!(json, json!({ "$type": "FullyConsumed" }));
64        let recovered = StoredAssetLockInfo::from_json(json).expect("from_json");
65        assert_eq!(original, recovered);
66    }
67
68    #[test]
69    fn json_round_trip_not_present() {
70        use crate::serialization::JsonConvertible;
71        let original = StoredAssetLockInfo::NotPresent;
72        let json = original.to_json().expect("to_json");
73        assert_eq!(json, json!({ "$type": "NotPresent" }));
74        let recovered = StoredAssetLockInfo::from_json(json).expect("from_json");
75        assert_eq!(original, recovered);
76    }
77
78    #[test]
79    fn json_round_trip_partially_consumed() {
80        use crate::serialization::JsonConvertible;
81        let original = partially_consumed_fixture();
82        let json = original.to_json().expect("to_json");
83        // Internal `$type` tag sits flat alongside the inner `AssetLockValue`'s
84        // own `$formatVersion` tag (`tag = "$formatVersion"`). `Bytes32` is
85        // base64 in JSON HR, and `tx_out_script` (`Vec<u8>`) is base64 too via
86        // `#[json_safe_fields]`'s `serde_bytes_var` annotation.
87        assert_eq!(
88            json,
89            json!({
90                "$type": "PartiallyConsumed",
91                "$formatVersion": "0",
92                "initial_credit_value": 1_000_000,
93                "tx_out_script": "qrvM3Q==",
94                "remaining_credit_value": 500_000,
95                "used_tags": ["QkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkI="],
96            })
97        );
98        let recovered = StoredAssetLockInfo::from_json(json).expect("from_json");
99        assert_eq!(original, recovered);
100    }
101
102    #[test]
103    fn value_round_trip_fully_consumed() {
104        use crate::serialization::ValueConvertible;
105        let original = StoredAssetLockInfo::FullyConsumed;
106        let value = original.to_object().expect("to_object");
107        assert_eq!(value, platform_value!({ "$type": "FullyConsumed" }));
108        let recovered = StoredAssetLockInfo::from_object(value).expect("from_object");
109        assert_eq!(original, recovered);
110    }
111
112    #[test]
113    fn value_round_trip_not_present() {
114        use crate::serialization::ValueConvertible;
115        let original = StoredAssetLockInfo::NotPresent;
116        let value = original.to_object().expect("to_object");
117        assert_eq!(value, platform_value!({ "$type": "NotPresent" }));
118        let recovered = StoredAssetLockInfo::from_object(value).expect("from_object");
119        assert_eq!(original, recovered);
120    }
121
122    #[test]
123    fn value_round_trip_partially_consumed() {
124        use crate::serialization::ValueConvertible;
125        let original = partially_consumed_fixture();
126        let value = original.to_object().expect("to_object");
127        // `Credits` (u64) → `Value::U64`. `tx_out_script` (`Vec<u8>`) →
128        // `Value::Bytes` via `#[json_safe_fields]`. `used_tags` →
129        // `Array(Vec<Value::Bytes32>)`.
130        assert_eq!(
131            value,
132            platform_value!({
133                "$type": "PartiallyConsumed",
134                "$formatVersion": "0",
135                "initial_credit_value": 1_000_000u64,
136                "tx_out_script": Value::Bytes(vec![0xaa, 0xbb, 0xcc, 0xdd]),
137                "remaining_credit_value": 500_000u64,
138                "used_tags": [Value::Bytes32([0x42; 32])],
139            })
140        );
141        let recovered = StoredAssetLockInfo::from_object(value).expect("from_object");
142        assert_eq!(original, recovered);
143    }
144}