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#[derive(Debug, PartialEq, strum::Display)]
143#[cfg_attr(
144 feature = "serde-conversion",
145 derive(serde::Serialize, serde::Deserialize)
146)]
147pub enum StateTransitionProofOutcome {
148 ExecutionProved(StateTransitionProofResult),
151 AffectedState(StateTransitionProofResult),
156}
157
158impl StateTransitionProofOutcome {
159 pub fn result(&self) -> &StateTransitionProofResult {
161 match self {
162 Self::ExecutionProved(result) | Self::AffectedState(result) => result,
163 }
164 }
165
166 pub fn into_result(self) -> StateTransitionProofResult {
168 match self {
169 Self::ExecutionProved(result) | Self::AffectedState(result) => result,
170 }
171 }
172
173 pub fn is_execution_proved(&self) -> bool {
175 matches!(self, Self::ExecutionProved(_))
176 }
177}
178
179#[cfg(feature = "json-conversion")]
187mod json_safe_address_info_map {
188 use super::{AddressNonce, Credits, PlatformAddress};
189 use serde::de::Deserializer;
190 use serde::ser::{SerializeMap, Serializer};
191 use serde::{Deserialize, Serialize};
192 use std::collections::BTreeMap;
193
194 type AddressInfoMap = BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>>;
198
199 #[derive(Serialize, Deserialize)]
200 struct Entry(
201 AddressNonce,
202 #[serde(with = "crate::serialization::json_safe_u64")] Credits,
203 );
204
205 pub fn serialize<S: Serializer>(
206 map: &AddressInfoMap,
207 serializer: S,
208 ) -> Result<S::Ok, S::Error> {
209 let mut s = serializer.serialize_map(Some(map.len()))?;
210 for (k, v) in map {
211 let wrapped = v.map(|(nonce, credits)| Entry(nonce, credits));
212 s.serialize_entry(k, &wrapped)?;
213 }
214 s.end()
215 }
216
217 pub fn deserialize<'de, D: Deserializer<'de>>(
218 deserializer: D,
219 ) -> Result<AddressInfoMap, D::Error> {
220 let raw: BTreeMap<PlatformAddress, Option<Entry>> = BTreeMap::deserialize(deserializer)?;
221 Ok(raw
222 .into_iter()
223 .map(|(k, v)| (k, v.map(|Entry(nonce, credits)| (nonce, credits))))
224 .collect())
225 }
226}
227
228#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
230impl crate::serialization::JsonConvertible for StateTransitionProofResult {}
231
232#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
233impl crate::serialization::ValueConvertible for StateTransitionProofResult {}
234
235#[cfg(all(
236 test,
237 feature = "json-conversion",
238 feature = "value-conversion",
239 feature = "serde-conversion"
240))]
241mod json_convertible_tests {
242 use super::*;
243 use platform_value::{Identifier, Value};
244 use serde_json::json;
245
246 fn fixture() -> StateTransitionProofResult {
250 StateTransitionProofResult::VerifiedTokenBalance(
251 Identifier::new([0xab; 32]),
252 123_456_789u64,
253 )
254 }
255
256 #[test]
257 fn json_round_trip_with_full_wire_shape() {
258 use crate::serialization::JsonConvertible;
259 let original = fixture();
260 let json = original.to_json().expect("to_json");
261 assert_eq!(
267 json,
268 json!({
269 "VerifiedTokenBalance": [
270 "CZ8YUVdk7znjrUmnb5n7kgySk9yRAsQDYmyCxzfSky9t",
271 123_456_789u64,
272 ],
273 })
274 );
275 let recovered = StateTransitionProofResult::from_json(json).expect("from_json");
276 assert_eq!(original, recovered);
277 }
278
279 #[test]
280 fn value_round_trip_with_full_wire_shape() {
281 use crate::serialization::ValueConvertible;
282 let original = fixture();
283 let value = original.to_object().expect("to_object");
284 let expected = Value::Map(vec![(
291 Value::Text("VerifiedTokenBalance".to_string()),
292 Value::Array(vec![Value::Identifier([0xab; 32]), Value::U64(123_456_789)]),
293 )]);
294 assert_eq!(value, expected);
295 let recovered = StateTransitionProofResult::from_object(value).expect("from_object");
296 assert_eq!(original, recovered);
297 }
298
299 #[test]
300 fn verified_token_balance_large_amount_serializes_as_string() {
301 use crate::serialization::JsonConvertible;
302 let original = StateTransitionProofResult::VerifiedTokenBalance(
305 Identifier::new([0xab; 32]),
306 9_007_199_254_740_993, );
308 let json = original.to_json().expect("to_json");
309 assert_eq!(json["VerifiedTokenBalance"][1], json!("9007199254740993"));
310 let recovered = StateTransitionProofResult::from_json(json).expect("from_json");
311 assert_eq!(original, recovered);
312 }
313
314 #[test]
315 fn verified_address_infos_large_credits_serialize_as_string() {
316 use crate::serialization::{JsonConvertible, ValueConvertible};
317 use std::collections::BTreeMap;
318 let big_credits: Credits = 9_007_199_254_740_993; let mut infos: BTreeMap<PlatformAddress, Option<(AddressNonce, Credits)>> = BTreeMap::new();
323 infos.insert(PlatformAddress::P2pkh([0x11; 20]), Some((7, big_credits)));
324 infos.insert(PlatformAddress::P2sh([0x22; 20]), None);
325 let original = StateTransitionProofResult::VerifiedAddressInfos(infos);
326
327 let json = original.to_json().expect("to_json");
329 let map = json["VerifiedAddressInfos"].as_object().expect("object");
330 let non_null = map
331 .values()
332 .find(|v| !v.is_null())
333 .expect("one populated entry");
334 assert_eq!(non_null[0], json!(7));
335 assert_eq!(non_null[1], json!("9007199254740993"));
336 assert!(
337 map.values().any(|v| v.is_null()),
338 "the None entry must survive"
339 );
340 let recovered = StateTransitionProofResult::from_json(json).expect("from_json");
341 assert_eq!(original, recovered);
342
343 let value = original.to_object().expect("to_object");
345 let recovered = StateTransitionProofResult::from_object(value).expect("from_object");
346 assert_eq!(original, recovered);
347 }
348}