1use crate::address_funds::PlatformAddress;
2use crate::asset_lock::StoredAssetLockInfo;
3use crate::balances::credits::TokenAmount;
4use crate::data_contract::group::GroupSumPower;
5use crate::data_contract::DataContract;
6use crate::document::Document;
7use crate::fee::Credits;
8use crate::group::group_action_status::GroupActionStatus;
9use crate::identity::{Identity, PartialIdentity};
10use crate::prelude::AddressNonce;
11use crate::tokens::info::IdentityTokenInfo;
12use crate::tokens::status::TokenStatus;
13use crate::tokens::token_pricing_schedule::TokenPricingSchedule;
14use crate::voting::votes::Vote;
15use platform_value::Identifier;
16use std::collections::BTreeMap;
17
18#[derive(Debug, PartialEq, strum::Display, derive_more::TryInto)]
19#[cfg_attr(
20 feature = "serde-conversion",
21 derive(serde::Serialize, serde::Deserialize)
22)]
23pub enum StateTransitionProofResult {
24 VerifiedDataContract(DataContract),
25 VerifiedIdentity(Identity),
26 VerifiedTokenBalanceAbsence(Identifier),
27 VerifiedTokenBalance(
33 Identifier,
34 #[cfg_attr(
35 feature = "json-conversion",
36 serde(with = "crate::serialization::json_safe_u64")
37 )]
38 TokenAmount,
39 ),
40 VerifiedTokenIdentityInfo(Identifier, IdentityTokenInfo),
41 VerifiedTokenPricingSchedule(Identifier, Option<TokenPricingSchedule>),
42 VerifiedTokenStatus(TokenStatus),
43 VerifiedTokenIdentitiesBalances(
44 #[cfg_attr(
45 feature = "json-conversion",
46 serde(
47 with = "crate::serialization::json::safe_integer_map::json_safe_identifier_u64_map"
48 )
49 )]
50 BTreeMap<Identifier, TokenAmount>,
51 ),
52 VerifiedPartialIdentity(PartialIdentity),
53 VerifiedBalanceTransfer(PartialIdentity, PartialIdentity), VerifiedDocuments(BTreeMap<Identifier, Option<Document>>),
55 VerifiedTokenActionWithDocument(Document),
56 VerifiedTokenGroupActionWithDocument(GroupSumPower, Option<Document>),
57 VerifiedTokenGroupActionWithTokenBalance(
58 GroupSumPower,
59 GroupActionStatus,
60 #[cfg_attr(
61 feature = "json-conversion",
62 serde(with = "crate::serialization::json_safe_option_u64")
63 )]
64 Option<TokenAmount>,
65 ),
66 VerifiedTokenGroupActionWithTokenIdentityInfo(
67 GroupSumPower,
68 GroupActionStatus,
69 Option<IdentityTokenInfo>,
70 ),
71 VerifiedTokenGroupActionWithTokenPricingSchedule(
72 GroupSumPower,
73 GroupActionStatus,
74 Option<TokenPricingSchedule>,
75 ),
76 VerifiedMasternodeVote(Vote),
77 VerifiedNextDistribution(Vote),
78 VerifiedAddressInfos(
79 #[cfg_attr(
80 feature = "json-conversion",
81 serde(with = "json_safe_address_info_map")
82 )]
83 BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>>,
84 ),
85 VerifiedIdentityFullWithAddressInfos(
86 Identity,
87 #[cfg_attr(
88 feature = "json-conversion",
89 serde(with = "json_safe_address_info_map")
90 )]
91 BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>>,
92 ),
93 VerifiedIdentityWithAddressInfos(
94 PartialIdentity,
95 #[cfg_attr(
96 feature = "json-conversion",
97 serde(with = "json_safe_address_info_map")
98 )]
99 BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>>,
100 ),
101 VerifiedAssetLockConsumed(StoredAssetLockInfo),
102 VerifiedShieldedNullifiers(Vec<(Vec<u8>, bool)>),
103 VerifiedShieldedNullifiersWithAddressInfos(
104 Vec<(Vec<u8>, bool)>,
105 #[cfg_attr(
106 feature = "json-conversion",
107 serde(with = "json_safe_address_info_map")
108 )]
109 BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>>,
110 ),
111 VerifiedShieldedNullifiersWithWithdrawalDocument(
112 Vec<(Vec<u8>, bool)>,
113 BTreeMap<Identifier, Option<Document>>,
114 ),
115 VerifiedAssetLockConsumedWithAddressInfos(
123 StoredAssetLockInfo,
124 BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>>,
125 ),
126 VerifiedIdentityWithShieldedNullifiers(Identity, Vec<(Vec<u8>, bool)>),
131}
132
133#[cfg(feature = "json-conversion")]
141mod json_safe_address_info_map {
142 use super::{AddressNonce, Credits, PlatformAddress};
143 use serde::de::Deserializer;
144 use serde::ser::{SerializeMap, Serializer};
145 use serde::{Deserialize, Serialize};
146 use std::collections::BTreeMap;
147
148 type AddressInfoMap = BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>>;
152
153 #[derive(Serialize, Deserialize)]
154 struct Entry(
155 AddressNonce,
156 #[serde(with = "crate::serialization::json_safe_u64")] Credits,
157 );
158
159 pub fn serialize<S: Serializer>(
160 map: &AddressInfoMap,
161 serializer: S,
162 ) -> Result<S::Ok, S::Error> {
163 let mut s = serializer.serialize_map(Some(map.len()))?;
164 for (k, v) in map {
165 let wrapped = v.map(|(nonce, credits)| Entry(nonce, credits));
166 s.serialize_entry(k, &wrapped)?;
167 }
168 s.end()
169 }
170
171 pub fn deserialize<'de, D: Deserializer<'de>>(
172 deserializer: D,
173 ) -> Result<AddressInfoMap, D::Error> {
174 let raw: BTreeMap<PlatformAddress, Option<Entry>> = BTreeMap::deserialize(deserializer)?;
175 Ok(raw
176 .into_iter()
177 .map(|(k, v)| (k, v.map(|Entry(nonce, credits)| (nonce, credits))))
178 .collect())
179 }
180}
181
182#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
184impl crate::serialization::JsonConvertible for StateTransitionProofResult {}
185
186#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
187impl crate::serialization::ValueConvertible for StateTransitionProofResult {}
188
189#[cfg(all(
190 test,
191 feature = "json-conversion",
192 feature = "value-conversion",
193 feature = "serde-conversion"
194))]
195mod json_convertible_tests {
196 use super::*;
197 use platform_value::{Identifier, Value};
198 use serde_json::json;
199
200 fn fixture() -> StateTransitionProofResult {
204 StateTransitionProofResult::VerifiedTokenBalance(
205 Identifier::new([0xab; 32]),
206 123_456_789u64,
207 )
208 }
209
210 #[test]
211 fn json_round_trip_with_full_wire_shape() {
212 use crate::serialization::JsonConvertible;
213 let original = fixture();
214 let json = original.to_json().expect("to_json");
215 assert_eq!(
221 json,
222 json!({
223 "VerifiedTokenBalance": [
224 "CZ8YUVdk7znjrUmnb5n7kgySk9yRAsQDYmyCxzfSky9t",
225 123_456_789u64,
226 ],
227 })
228 );
229 let recovered = StateTransitionProofResult::from_json(json).expect("from_json");
230 assert_eq!(original, recovered);
231 }
232
233 #[test]
234 fn value_round_trip_with_full_wire_shape() {
235 use crate::serialization::ValueConvertible;
236 let original = fixture();
237 let value = original.to_object().expect("to_object");
238 let expected = Value::Map(vec![(
245 Value::Text("VerifiedTokenBalance".to_string()),
246 Value::Array(vec![Value::Identifier([0xab; 32]), Value::U64(123_456_789)]),
247 )]);
248 assert_eq!(value, expected);
249 let recovered = StateTransitionProofResult::from_object(value).expect("from_object");
250 assert_eq!(original, recovered);
251 }
252
253 #[test]
254 fn verified_token_balance_large_amount_serializes_as_string() {
255 use crate::serialization::JsonConvertible;
256 let original = StateTransitionProofResult::VerifiedTokenBalance(
259 Identifier::new([0xab; 32]),
260 9_007_199_254_740_993, );
262 let json = original.to_json().expect("to_json");
263 assert_eq!(json["VerifiedTokenBalance"][1], json!("9007199254740993"));
264 let recovered = StateTransitionProofResult::from_json(json).expect("from_json");
265 assert_eq!(original, recovered);
266 }
267
268 #[test]
269 fn verified_address_infos_large_credits_serialize_as_string() {
270 use crate::serialization::{JsonConvertible, ValueConvertible};
271 use std::collections::BTreeMap;
272 let big_credits: Credits = 9_007_199_254_740_993; let mut infos: BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>> = BTreeMap::new();
277 infos.insert(PlatformAddress::P2pkh([0x11; 20]), Some((7, big_credits)));
278 infos.insert(PlatformAddress::P2sh([0x22; 20]), None);
279 let original = StateTransitionProofResult::VerifiedAddressInfos(infos);
280
281 let json = original.to_json().expect("to_json");
283 let map = json["VerifiedAddressInfos"].as_object().expect("object");
284 let non_null = map
285 .values()
286 .find(|v| !v.is_null())
287 .expect("one populated entry");
288 assert_eq!(non_null[0], json!(7));
289 assert_eq!(non_null[1], json!("9007199254740993"));
290 assert!(
291 map.values().any(|v| v.is_null()),
292 "the None entry must survive"
293 );
294 let recovered = StateTransitionProofResult::from_json(json).expect("from_json");
295 assert_eq!(original, recovered);
296
297 let value = original.to_object().expect("to_object");
299 let recovered = StateTransitionProofResult::from_object(value).expect("from_object");
300 assert_eq!(original, recovered);
301 }
302}