1use std::fmt;
4
5use serde::de::{DeserializeOwned, MapAccess, SeqAccess, Visitor};
6use serde::{Deserialize, Serialize};
7use serde_json::{Map, Value};
8
9use crate::diagnostic::{Diagnostic, DiagnosticCategory};
10use crate::limits::{CANONICAL_CODEC_LIMITS, CodecLimits};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
14#[serde(transparent)]
15pub struct FormatVersion(u16);
16
17impl FormatVersion {
18 pub const V1: Self = Self(1);
20 pub const fn from_raw(value: u16) -> Self {
22 Self(value)
23 }
24 pub const fn get(self) -> u16 {
26 self.0
27 }
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
32#[serde(transparent)]
33pub struct CodecVersion(u16);
34
35impl CodecVersion {
36 pub const V1: Self = Self(1);
38 pub const fn from_raw(value: u16) -> Self {
40 Self(value)
41 }
42 pub const fn get(self) -> u16 {
44 self.0
45 }
46}
47
48pub fn ensure_format_version(
50 actual: FormatVersion,
51 supported: FormatVersion,
52) -> Result<(), Diagnostic> {
53 if actual == supported {
54 Ok(())
55 } else {
56 Err(Diagnostic::stable(
57 DiagnosticCategory::InvalidContract,
58 "unsupported_format_version",
59 "contract format version is not supported",
60 )
61 .with_detail("actual", i64::from(actual.get()))
62 .with_detail("supported", i64::from(supported.get())))
63 }
64}
65
66pub fn ensure_codec_version(
68 actual: CodecVersion,
69 supported: CodecVersion,
70) -> Result<(), Diagnostic> {
71 if actual == supported {
72 Ok(())
73 } else {
74 Err(Diagnostic::stable(
75 DiagnosticCategory::InvalidContract,
76 "unsupported_codec_version",
77 "canonical codec version is not supported",
78 )
79 .with_detail("actual", i64::from(actual.get()))
80 .with_detail("supported", i64::from(supported.get())))
81 }
82}
83
84pub fn to_canonical_json<T: Serialize>(value: &T) -> Result<Vec<u8>, Diagnostic> {
86 to_canonical_json_with_limits(value, CANONICAL_CODEC_LIMITS)
87}
88
89pub fn to_canonical_json_with_limits<T: Serialize>(
91 value: &T,
92 limits: CodecLimits,
93) -> Result<Vec<u8>, Diagnostic> {
94 let mut value = serde_json::to_value(value).map_err(|_| {
95 Diagnostic::stable(
96 DiagnosticCategory::InvalidContract,
97 "canonical_json_encode_failed",
98 "value cannot be represented as canonical JSON",
99 )
100 })?;
101 normalize_numbers(&mut value).map_err(|()| {
102 Diagnostic::stable(
103 DiagnosticCategory::InvalidContract,
104 "canonical_json_encode_failed",
105 "value contains a number outside the canonical JSON domain",
106 )
107 })?;
108 sort_object_keys(&mut value);
109 inspect(&value, 1, limits)?;
110 let bytes = serde_json::to_vec(&value).map_err(|_| {
111 Diagnostic::stable(
112 DiagnosticCategory::InvalidContract,
113 "canonical_json_encode_failed",
114 "value cannot be encoded as canonical JSON",
115 )
116 })?;
117 ensure_bytes(bytes.len(), limits)?;
118 Ok(bytes)
119}
120
121pub fn from_canonical_json<T>(bytes: &[u8]) -> Result<T, Diagnostic>
123where
124 T: DeserializeOwned + Serialize,
125{
126 from_canonical_json_with_limits(bytes, CANONICAL_CODEC_LIMITS)
127}
128
129pub fn from_canonical_json_with_limits<T>(
131 bytes: &[u8],
132 limits: CodecLimits,
133) -> Result<T, Diagnostic>
134where
135 T: DeserializeOwned + Serialize,
136{
137 ensure_bytes(bytes.len(), limits)?;
138 let mut value = match serde_json::from_slice::<UniqueValue>(bytes) {
139 Ok(value) => value.0,
140 Err(error) if error.to_string().contains(DUPLICATE_KEY_MARKER) => {
141 return Err(Diagnostic::stable(
142 DiagnosticCategory::InvalidContract,
143 "duplicate_canonical_json_key",
144 "canonical JSON objects cannot contain duplicate keys",
145 ));
146 }
147 Err(_) => {
148 return Err(Diagnostic::stable(
149 DiagnosticCategory::InvalidContract,
150 "malformed_canonical_json",
151 "input is not valid canonical JSON",
152 ));
153 }
154 };
155 inspect(&value, 1, limits)?;
156 normalize_numbers(&mut value).map_err(|()| {
157 Diagnostic::stable(
158 DiagnosticCategory::InvalidContract,
159 "malformed_canonical_json",
160 "input is not valid canonical JSON",
161 )
162 })?;
163 sort_object_keys(&mut value);
164 let canonical = serde_json::to_vec(&value).map_err(|_| {
165 Diagnostic::stable(
166 DiagnosticCategory::InvalidContract,
167 "canonical_json_encode_failed",
168 "decoded JSON cannot be re-encoded",
169 )
170 })?;
171 if canonical != bytes {
172 return Err(Diagnostic::stable(
173 DiagnosticCategory::InvalidContract,
174 "non_canonical_json",
175 "input is valid JSON but not the canonical encoding",
176 )
177 .with_detail("actual_bytes", count(bytes.len()))
178 .with_detail("canonical_bytes", count(canonical.len())));
179 }
180 serde_json::from_value(value).map_err(|_| {
181 Diagnostic::stable(
182 DiagnosticCategory::InvalidContract,
183 "invalid_canonical_value",
184 "canonical JSON does not satisfy the requested contract type",
185 )
186 })
187}
188
189const DUPLICATE_KEY_MARKER: &str = "duplicate canonical JSON object key";
190const ARBITRARY_PRECISION_NUMBER_KEY: &str = "$serde_json::private::Number";
191
192struct UniqueValue(Value);
196
197impl<'de> Deserialize<'de> for UniqueValue {
198 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
199 where
200 D: serde::Deserializer<'de>,
201 {
202 deserializer.deserialize_any(UniqueValueVisitor)
203 }
204}
205
206struct UniqueValueVisitor;
207
208impl<'de> Visitor<'de> for UniqueValueVisitor {
209 type Value = UniqueValue;
210
211 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
212 formatter.write_str("a JSON value with unique object keys")
213 }
214
215 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
216 Ok(UniqueValue(Value::Bool(value)))
217 }
218
219 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
220 Ok(UniqueValue(Value::Number(value.into())))
221 }
222
223 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
224 Ok(UniqueValue(Value::Number(value.into())))
225 }
226
227 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
228 where
229 E: serde::de::Error,
230 {
231 serde_json::Number::from_f64(value)
232 .map(Value::Number)
233 .map(UniqueValue)
234 .ok_or_else(|| E::custom("non-finite JSON number"))
235 }
236
237 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
238 where
239 E: serde::de::Error,
240 {
241 self.visit_string(value.to_owned())
242 }
243
244 fn visit_string<E>(self, value: String) -> Result<Self::Value, E> {
245 Ok(UniqueValue(Value::String(value)))
246 }
247
248 fn visit_none<E>(self) -> Result<Self::Value, E> {
249 Ok(UniqueValue(Value::Null))
250 }
251
252 fn visit_unit<E>(self) -> Result<Self::Value, E> {
253 Ok(UniqueValue(Value::Null))
254 }
255
256 fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
257 where
258 A: SeqAccess<'de>,
259 {
260 let mut values = Vec::with_capacity(sequence.size_hint().unwrap_or(0));
261 while let Some(value) = sequence.next_element::<UniqueValue>()? {
262 values.push(value.0);
263 }
264 Ok(UniqueValue(Value::Array(values)))
265 }
266
267 fn visit_map<A>(self, mut object: A) -> Result<Self::Value, A::Error>
268 where
269 A: MapAccess<'de>,
270 {
271 let Some(first_key) = object.next_key::<String>()? else {
272 return Ok(UniqueValue(Value::Object(Map::new())));
273 };
274 if first_key == ARBITRARY_PRECISION_NUMBER_KEY {
275 let spelling = object.next_value::<String>()?;
276 if object.next_key::<String>()?.is_some() {
277 return Err(serde::de::Error::custom(DUPLICATE_KEY_MARKER));
278 }
279 let number = spelling
280 .parse::<serde_json::Number>()
281 .map_err(serde::de::Error::custom)?;
282 return Ok(UniqueValue(Value::Number(number)));
283 }
284
285 let mut values = Map::new();
286 let first_value = object.next_value::<UniqueValue>()?.0;
287 values.insert(first_key, first_value);
288 while let Some(key) = object.next_key::<String>()? {
289 if values.contains_key(&key) {
290 return Err(serde::de::Error::custom(DUPLICATE_KEY_MARKER));
291 }
292 values.insert(key, object.next_value::<UniqueValue>()?.0);
293 }
294 Ok(UniqueValue(Value::Object(values)))
295 }
296}
297
298fn sort_object_keys(value: &mut Value) {
306 match value {
307 Value::Array(values) => {
308 for value in values {
309 sort_object_keys(value);
310 }
311 }
312 Value::Object(values) => {
313 for value in values.values_mut() {
314 sort_object_keys(value);
315 }
316 let mut entries = std::mem::take(values).into_iter().collect::<Vec<_>>();
317 entries.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
318 values.extend(entries);
319 }
320 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {}
321 }
322}
323
324fn normalize_numbers(value: &mut Value) -> Result<(), ()> {
329 match value {
330 Value::Array(values) => {
331 for value in values {
332 normalize_numbers(value)?;
333 }
334 }
335 Value::Object(values) => {
336 for value in values.values_mut() {
337 normalize_numbers(value)?;
338 }
339 }
340 Value::Number(number) => {
341 let normalized = if let Some(value) = number.as_i64() {
342 value.into()
343 } else if let Some(value) = number.as_u64() {
344 value.into()
345 } else if let Some(value) = number.as_f64() {
346 serde_json::Number::from_f64(value).ok_or(())?
347 } else {
348 return Err(());
349 };
350 *number = normalized;
351 }
352 Value::Null | Value::Bool(_) | Value::String(_) => {}
353 }
354 Ok(())
355}
356
357fn inspect(value: &Value, depth: usize, limits: CodecLimits) -> Result<(), Diagnostic> {
358 if depth > limits.max_depth {
359 return Err(Diagnostic::stable(
360 DiagnosticCategory::ResourceLimit,
361 "canonical_json_too_deep",
362 "canonical JSON exceeds the nesting-depth ceiling",
363 )
364 .with_detail("maximum_depth", count(limits.max_depth)));
365 }
366 match value {
367 Value::String(value) => ensure_string(value.len(), limits),
368 Value::Array(values) => {
369 ensure_collection(values.len(), limits)?;
370 for value in values {
371 inspect(value, depth + 1, limits)?;
372 }
373 Ok(())
374 }
375 Value::Object(values) => {
376 ensure_collection(values.len(), limits)?;
377 for (key, value) in values {
378 ensure_string(key.len(), limits)?;
379 inspect(value, depth + 1, limits)?;
380 }
381 Ok(())
382 }
383 Value::Null | Value::Bool(_) | Value::Number(_) => Ok(()),
384 }
385}
386
387fn ensure_bytes(actual: usize, limits: CodecLimits) -> Result<(), Diagnostic> {
388 if actual <= limits.max_bytes {
389 Ok(())
390 } else {
391 Err(Diagnostic::stable(
392 DiagnosticCategory::ResourceLimit,
393 "canonical_json_too_large",
394 "canonical JSON exceeds the byte ceiling",
395 )
396 .with_detail("actual_bytes", count(actual))
397 .with_detail("maximum_bytes", count(limits.max_bytes)))
398 }
399}
400fn ensure_collection(actual: usize, limits: CodecLimits) -> Result<(), Diagnostic> {
401 if actual <= limits.max_collection_len {
402 Ok(())
403 } else {
404 Err(Diagnostic::stable(
405 DiagnosticCategory::ResourceLimit,
406 "canonical_collection_too_large",
407 "canonical JSON collection exceeds its member ceiling",
408 )
409 .with_detail("actual_items", count(actual))
410 .with_detail("maximum_items", count(limits.max_collection_len)))
411 }
412}
413fn ensure_string(actual: usize, limits: CodecLimits) -> Result<(), Diagnostic> {
414 if actual <= limits.max_string_bytes {
415 Ok(())
416 } else {
417 Err(Diagnostic::stable(
418 DiagnosticCategory::ResourceLimit,
419 "canonical_string_too_large",
420 "canonical JSON string exceeds its byte ceiling",
421 )
422 .with_detail("actual_bytes", count(actual))
423 .with_detail("maximum_bytes", count(limits.max_string_bytes)))
424 }
425}
426fn count(value: usize) -> i64 {
427 i64::try_from(value).unwrap_or(i64::MAX)
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433 use crate::value::CanonicalValue;
434 use serde::{Deserialize, Serialize};
435 use serde_json::Value;
436
437 #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
438 struct OutOfOrderObject {
439 zeta: u8,
440 alpha: OutOfOrderNested,
441 }
442
443 #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
444 struct OutOfOrderNested {
445 zeta: u8,
446 alpha: u8,
447 }
448
449 #[test]
450 fn canonical_object_order_is_independent_of_the_serde_json_map_backend() {
451 let value = OutOfOrderObject {
452 zeta: 3,
453 alpha: OutOfOrderNested { zeta: 2, alpha: 1 },
454 };
455 let canonical = br#"{"alpha":{"alpha":1,"zeta":2},"zeta":3}"#;
456 assert_eq!(to_canonical_json(&value).unwrap(), canonical);
457 assert_eq!(
458 from_canonical_json::<OutOfOrderObject>(canonical).unwrap(),
459 value
460 );
461
462 let insertion_order = br#"{"zeta":3,"alpha":{"zeta":2,"alpha":1}}"#;
463 assert_eq!(
464 from_canonical_json::<OutOfOrderObject>(insertion_order)
465 .unwrap_err()
466 .code()
467 .as_str(),
468 "non_canonical_json"
469 );
470 }
471
472 #[test]
473 fn canonical_decoder_distinguishes_malformed_and_noncanonical_input() {
474 assert_eq!(
475 from_canonical_json::<CanonicalValue>(b"{")
476 .unwrap_err()
477 .code()
478 .as_str(),
479 "malformed_canonical_json"
480 );
481 let spaced = br#"{ "kind":"long","value":"1"}"#;
482 assert_eq!(
483 from_canonical_json::<CanonicalValue>(spaced)
484 .unwrap_err()
485 .code()
486 .as_str(),
487 "non_canonical_json"
488 );
489
490 for noncanonical in [b"1e0" as &[u8], b"1E+0", b"-0"] {
491 for error in [
492 from_canonical_json::<FormatVersion>(noncanonical).unwrap_err(),
493 from_canonical_json::<Value>(noncanonical).unwrap_err(),
494 ] {
495 assert_eq!(error.code().as_str(), "non_canonical_json");
496 }
497 }
498 assert_eq!(
499 from_canonical_json::<Value>(b"01")
500 .unwrap_err()
501 .code()
502 .as_str(),
503 "malformed_canonical_json"
504 );
505 }
506
507 #[test]
508 fn canonical_decoder_rejects_duplicate_keys_at_every_depth() {
509 for duplicate in [
510 br#"{"kind":"long","kind":"long","value":"1"}"# as &[u8],
511 br#"{"outer":{"value":1,"value":2}}"#,
512 ] {
513 assert_eq!(
514 from_canonical_json::<Value>(duplicate)
515 .unwrap_err()
516 .code()
517 .as_str(),
518 "duplicate_canonical_json_key"
519 );
520 }
521 }
522
523 #[test]
524 fn canonical_numbers_are_independent_of_the_serde_json_number_backend() {
525 for canonical in [
526 b"0" as &[u8],
527 b"-1",
528 b"-9223372036854775808",
529 b"18446744073709551615",
530 b"1.0",
531 b"0.0",
532 b"-0.0",
533 b"5e-324",
534 ] {
535 let value = from_canonical_json::<Value>(canonical).unwrap();
536 assert_eq!(to_canonical_json(&value).unwrap(), canonical);
537 }
538
539 for noncanonical in [
540 b"-9223372036854775809" as &[u8],
541 b"18446744073709551616",
542 b"100000000000000000000000000000000000000000000000000",
543 b"4.9406564584124654e-324",
544 ] {
545 assert_eq!(
546 from_canonical_json::<Value>(noncanonical)
547 .unwrap_err()
548 .code()
549 .as_str(),
550 "non_canonical_json"
551 );
552 }
553
554 assert_eq!(to_canonical_json(&1.0_f64).unwrap(), b"1.0");
555 assert_eq!(to_canonical_json(&f64::from_bits(1)).unwrap(), b"5e-324");
556 }
557
558 #[test]
559 fn exact_limits_accept_boundary_and_reject_next_value() {
560 let value = CanonicalValue::String(crate::value::CanonicalString::new("abc").unwrap());
561 let bytes = to_canonical_json(&value).unwrap();
562 let mut limits = CodecLimits::CANONICAL;
563 limits.max_bytes = bytes.len();
564 assert!(from_canonical_json_with_limits::<CanonicalValue>(&bytes, limits).is_ok());
565 limits.max_bytes -= 1;
566 assert_eq!(
567 from_canonical_json_with_limits::<CanonicalValue>(&bytes, limits)
568 .unwrap_err()
569 .code()
570 .as_str(),
571 "canonical_json_too_large"
572 );
573 }
574
575 #[test]
576 fn required_versions_fail_closed() {
577 assert!(serde_json::from_str::<FormatVersion>(r#""1""#).is_err());
578 assert!(serde_json::from_str::<CodecVersion>(r#""1""#).is_err());
579 assert!(ensure_format_version(FormatVersion::V1, FormatVersion::V1).is_ok());
580 assert!(ensure_codec_version(CodecVersion::V1, CodecVersion::V1).is_ok());
581
582 assert_eq!(
583 ensure_format_version(FormatVersion::from_raw(2), FormatVersion::V1)
584 .unwrap_err()
585 .code()
586 .as_str(),
587 "unsupported_format_version",
588 );
589 assert_eq!(
590 ensure_codec_version(CodecVersion::from_raw(2), CodecVersion::V1)
591 .unwrap_err()
592 .code()
593 .as_str(),
594 "unsupported_codec_version",
595 );
596 }
597
598 #[test]
599 fn structural_limits_reject_depth_members_strings_and_keys() {
600 let base = CodecLimits {
601 max_bytes: 128,
602 max_depth: 8,
603 max_collection_len: 8,
604 max_string_bytes: 8,
605 };
606
607 let depth = CodecLimits {
608 max_depth: 2,
609 ..base
610 };
611 assert_eq!(
612 from_canonical_json_with_limits::<Value>(b"[[0]]", depth)
613 .unwrap_err()
614 .code()
615 .as_str(),
616 "canonical_json_too_deep",
617 );
618
619 let members = CodecLimits {
620 max_collection_len: 1,
621 ..base
622 };
623 assert_eq!(
624 from_canonical_json_with_limits::<Value>(b"[0,1]", members)
625 .unwrap_err()
626 .code()
627 .as_str(),
628 "canonical_collection_too_large",
629 );
630
631 let strings = CodecLimits {
632 max_string_bytes: 3,
633 ..base
634 };
635 for bytes in [br#""abcd""# as &[u8], br#"{"abcd":0}"# as &[u8]] {
636 assert_eq!(
637 from_canonical_json_with_limits::<Value>(bytes, strings)
638 .unwrap_err()
639 .code()
640 .as_str(),
641 "canonical_string_too_large",
642 );
643 }
644 }
645}