Skip to content

Commit 666312e

Browse files
authored
Merge pull request #2257 from CosmWasm/test-messagepack-encodings
Add messagepack encoding tests and docs for integer types
2 parents 0580b99 + 2834870 commit 666312e

File tree

2 files changed

+270
-2
lines changed

2 files changed

+270
-2
lines changed

packages/std/src/math/mod.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,39 @@ pub use uint64::Uint64;
3333
macro_rules! impl_int_serde {
3434
($ty:ty) => {
3535
impl ::serde::Serialize for $ty {
36-
/// Serializes as an integer string using base 10
36+
/// Serializes as an integer string using base 10.
37+
///
38+
/// We consistently serialize all `UintXXX` and `IntYYY` types as strings in JSON
39+
/// to ensure the best possible compatibility with clients. E.g. JavaScript and jq
40+
/// only support up to ~53bit numbers without losing precision, making it hard to use
41+
/// serialized `u64`s on other systems than Rust or Go. `Uint64`/`Int64` ensure the full
42+
/// 64 bit range is supported. For larger integers, the use of strings is pretty much the
43+
/// only reasonable way to store them in JSON.
44+
///
45+
/// For binary encodings (notably MessagePack) strings are used too. The reason is that
46+
/// in MessagePack integers are limited to 64 bit and we strive for consistent encoding
47+
/// within the `UintXXX`/`IntYYY` family. Also for small to mid sized values, decimal strings
48+
/// are often more compact than a fixed-length binary encoding.
49+
///
50+
/// ## Examples
51+
///
52+
/// Serialize to JSON:
53+
///
54+
/// ```
55+
/// # use cosmwasm_std::{to_json_vec, Uint64};
56+
/// let value = Uint64::new(17);
57+
/// let serialized = to_json_vec(&value).unwrap();
58+
/// assert_eq!(serialized, b"\"17\"");
59+
/// ```
60+
///
61+
/// Serialize to MessagePack:
62+
///
63+
/// ```
64+
/// # use cosmwasm_std::{to_msgpack_vec, Uint64};
65+
/// let value = Uint64::new(17);
66+
/// let serialized = to_msgpack_vec(&value).unwrap();
67+
/// assert_eq!(serialized, [0b10100000 ^ 2, b'1', b'7']); // string of lengths 2 with value "17"
68+
/// ```
3769
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3870
where
3971
S: ::serde::ser::Serializer,
@@ -43,7 +75,10 @@ macro_rules! impl_int_serde {
4375
}
4476

4577
impl<'de> ::serde::Deserialize<'de> for $ty {
46-
/// Deserialized from an integer string using base 10
78+
/// Deserializes from an integer string using base 10.
79+
///
80+
/// See the [`Serialize` documentation](#method.serialize) for a few more words
81+
/// on the encoding of the `UintXXX`/`IntYYY` family.
4782
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4883
where
4984
D: ::serde::de::Deserializer<'de>,

packages/std/src/msgpack.rs

+233
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ where
3535
#[cfg(test)]
3636
mod tests {
3737
use super::*;
38+
use crate::{Int128, Int256, Int512, Int64, Uint128, Uint256, Uint512, Uint64};
3839
use serde::Deserialize;
3940

4041
#[derive(Serialize, Deserialize, Debug, PartialEq)]
@@ -224,4 +225,236 @@ mod tests {
224225
}
225226
);
226227
}
228+
229+
#[test]
230+
fn msgpack_serialization_for_boolean_types() {
231+
// "Bool format family stores false or true in 1 byte."
232+
let serialized = to_msgpack_vec(&false).unwrap();
233+
assert_eq!(serialized, [0xc2]);
234+
let serialized = to_msgpack_vec(&true).unwrap();
235+
assert_eq!(serialized, [0xc3]);
236+
}
237+
238+
#[test]
239+
fn msgpack_serialization_for_integer_types() {
240+
// primitive integers up to 64bit
241+
// similar to VARINT in protobuf or number in JSON, the encoding does not contain integer size
242+
{
243+
// "positive fixint stores 7-bit positive integer"
244+
let serialized = to_msgpack_vec(&0u8).unwrap();
245+
assert_eq!(serialized, [0]);
246+
let serialized = to_msgpack_vec(&0u16).unwrap();
247+
assert_eq!(serialized, [0]);
248+
let serialized = to_msgpack_vec(&0u32).unwrap();
249+
assert_eq!(serialized, [0]);
250+
let serialized = to_msgpack_vec(&0u64).unwrap();
251+
assert_eq!(serialized, [0]);
252+
let serialized = to_msgpack_vec(&0i64).unwrap();
253+
assert_eq!(serialized, [0]);
254+
let serialized = to_msgpack_vec(&7u8).unwrap();
255+
assert_eq!(serialized, [7]);
256+
let serialized = to_msgpack_vec(&7u16).unwrap();
257+
assert_eq!(serialized, [7]);
258+
let serialized = to_msgpack_vec(&7u32).unwrap();
259+
assert_eq!(serialized, [7]);
260+
let serialized = to_msgpack_vec(&7u64).unwrap();
261+
assert_eq!(serialized, [7]);
262+
let serialized = to_msgpack_vec(&127u32).unwrap();
263+
assert_eq!(serialized, [127]);
264+
265+
// "negative fixint stores 5-bit negative integer"
266+
let serialized = to_msgpack_vec(&-1i32).unwrap();
267+
assert_eq!(serialized, [255]);
268+
let serialized = to_msgpack_vec(&-1i64).unwrap();
269+
assert_eq!(serialized, [255]);
270+
let serialized = to_msgpack_vec(&-10i64).unwrap();
271+
assert_eq!(serialized, [246]);
272+
let serialized = to_msgpack_vec(&-24i64).unwrap();
273+
assert_eq!(serialized, [232]);
274+
275+
// "uint 8 stores a 8-bit unsigned integer"
276+
let serialized = to_msgpack_vec(&128u32).unwrap();
277+
assert_eq!(serialized, [0xcc, 128]);
278+
let serialized = to_msgpack_vec(&237u32).unwrap();
279+
assert_eq!(serialized, [0xcc, 237]);
280+
281+
// "uint 16 stores a 16-bit big-endian unsigned integer"
282+
let serialized = to_msgpack_vec(&1000u32).unwrap();
283+
assert_eq!(serialized, [0xcd, 3, 232]);
284+
285+
// "uint 32 stores a 32-bit big-endian unsigned integer"
286+
let serialized = to_msgpack_vec(&u32::MAX).unwrap();
287+
assert_eq!(serialized, [0xce, 255, 255, 255, 255]);
288+
289+
// "uint 64 stores a 64-bit big-endian unsigned integer"
290+
let serialized = to_msgpack_vec(&575747839886u64).unwrap();
291+
assert_eq!(serialized, [0xcf, 0, 0, 0, 134, 13, 62, 215, 142]);
292+
let serialized = to_msgpack_vec(&u64::MAX).unwrap();
293+
assert_eq!(serialized, [0xcf, 255, 255, 255, 255, 255, 255, 255, 255]);
294+
295+
// "int 8 stores a 8-bit signed integer"
296+
let serialized = to_msgpack_vec(&i8::MIN).unwrap();
297+
assert_eq!(serialized, [0xd0, 128]);
298+
let serialized = to_msgpack_vec(&-111i8).unwrap();
299+
assert_eq!(serialized, [0xd0, 145]);
300+
301+
// "int 16 stores a 16-bit big-endian signed integer"
302+
let serialized = to_msgpack_vec(&i16::MIN).unwrap();
303+
assert_eq!(serialized, [0xd1, 128, 0]);
304+
305+
// "int 32 stores a 32-bit big-endian signed integer"
306+
let serialized = to_msgpack_vec(&i32::MIN).unwrap();
307+
assert_eq!(serialized, [0xd2, 128, 0, 0, 0]);
308+
309+
// "int 64 stores a 64-bit big-endian signed integer"
310+
let serialized = to_msgpack_vec(&i64::MIN).unwrap();
311+
assert_eq!(serialized, [0xd3, 128, 0, 0, 0, 0, 0, 0, 0]);
312+
}
313+
314+
// u128/i128
315+
// cannot be serialized as integers in messagepack due to the limitation
316+
// "a value of an Integer object is limited from -(2^63) upto (2^64)-1"
317+
{
318+
// encoded as 16 bytes big endian
319+
// i.e. takes 18 bytes of storage
320+
assert_eq!(
321+
to_msgpack_vec(&0u128).unwrap(),
322+
[0xc4, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
323+
);
324+
assert_eq!(
325+
to_msgpack_vec(&1u128).unwrap(),
326+
[0xc4, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
327+
);
328+
assert_eq!(
329+
to_msgpack_vec(&17u128).unwrap(),
330+
[0xc4, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17]
331+
);
332+
assert_eq!(
333+
to_msgpack_vec(&u128::MAX).unwrap(),
334+
[
335+
0xc4, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
336+
255, 255
337+
]
338+
);
339+
340+
assert_eq!(
341+
to_msgpack_vec(&0i128).unwrap(),
342+
[0xc4, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
343+
);
344+
assert_eq!(
345+
to_msgpack_vec(&1i128).unwrap(),
346+
[0xc4, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
347+
);
348+
assert_eq!(
349+
to_msgpack_vec(&17i128).unwrap(),
350+
[0xc4, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17]
351+
);
352+
assert_eq!(
353+
to_msgpack_vec(&-1i128).unwrap(),
354+
[
355+
0xc4, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
356+
255, 255
357+
]
358+
);
359+
assert_eq!(
360+
to_msgpack_vec(&i128::MIN).unwrap(),
361+
[0xc4, 16, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
362+
);
363+
assert_eq!(
364+
to_msgpack_vec(&i128::MAX).unwrap(),
365+
[
366+
0xc4, 16, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
367+
255, 255
368+
]
369+
);
370+
}
371+
372+
// Uint64/Uint128/Uint256/Uint512
373+
{
374+
let s = to_msgpack_vec(&Uint64::zero()).unwrap();
375+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
376+
let s = to_msgpack_vec(&Uint128::zero()).unwrap();
377+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
378+
let s = to_msgpack_vec(&Uint256::zero()).unwrap();
379+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
380+
let s = to_msgpack_vec(&Uint512::zero()).unwrap();
381+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
382+
383+
let s = to_msgpack_vec(&Uint64::one()).unwrap();
384+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
385+
let s = to_msgpack_vec(&Uint128::one()).unwrap();
386+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
387+
let s = to_msgpack_vec(&Uint256::one()).unwrap();
388+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
389+
let s = to_msgpack_vec(&Uint512::one()).unwrap();
390+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
391+
392+
let s = to_msgpack_vec(&Uint64::MAX).unwrap();
393+
assert_eq!(
394+
s,
395+
[
396+
0b10100000 ^ 20,
397+
b'1',
398+
b'8',
399+
b'4',
400+
b'4',
401+
b'6',
402+
b'7',
403+
b'4',
404+
b'4',
405+
b'0',
406+
b'7',
407+
b'3',
408+
b'7',
409+
b'0',
410+
b'9',
411+
b'5',
412+
b'5',
413+
b'1',
414+
b'6',
415+
b'1',
416+
b'5'
417+
]
418+
); // string of lengths 1 with value "1"
419+
}
420+
421+
// Int64/Int128/Int256/Int512
422+
{
423+
let s = to_msgpack_vec(&Int64::zero()).unwrap();
424+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
425+
let s = to_msgpack_vec(&Int128::zero()).unwrap();
426+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
427+
let s = to_msgpack_vec(&Int256::zero()).unwrap();
428+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
429+
let s = to_msgpack_vec(&Int512::zero()).unwrap();
430+
assert_eq!(s, [0b10100000 ^ 1, b'0']); // string of lengths 1 with value "0"
431+
432+
let s = to_msgpack_vec(&Int64::one()).unwrap();
433+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
434+
let s = to_msgpack_vec(&Int128::one()).unwrap();
435+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
436+
let s = to_msgpack_vec(&Int256::one()).unwrap();
437+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
438+
let s = to_msgpack_vec(&Int512::one()).unwrap();
439+
assert_eq!(s, [0b10100000 ^ 1, b'1']); // string of lengths 1 with value "1"
440+
441+
let s = to_msgpack_vec(&Int64::from(15i32)).unwrap();
442+
assert_eq!(s, [0b10100000 ^ 2, b'1', b'5']); // string of lengths 2 with value "15"
443+
let s = to_msgpack_vec(&Int128::from(15i32)).unwrap();
444+
assert_eq!(s, [0b10100000 ^ 2, b'1', b'5']); // string of lengths 2 with value "15"
445+
let s = to_msgpack_vec(&Int256::from(15i32)).unwrap();
446+
assert_eq!(s, [0b10100000 ^ 2, b'1', b'5']); // string of lengths 2 with value "15"
447+
let s = to_msgpack_vec(&Int512::from(15i32)).unwrap();
448+
assert_eq!(s, [0b10100000 ^ 2, b'1', b'5']); // string of lengths 2 with value "15"
449+
450+
let s = to_msgpack_vec(&Int64::from(-1i64)).unwrap();
451+
assert_eq!(s, [0b10100000 ^ 2, b'-', b'1']); // string of lengths 2 with value "-1"
452+
let s = to_msgpack_vec(&Int128::from(-1i64)).unwrap();
453+
assert_eq!(s, [0b10100000 ^ 2, b'-', b'1']); // string of lengths 2 with value "-1"
454+
let s = to_msgpack_vec(&Int256::from(-1i64)).unwrap();
455+
assert_eq!(s, [0b10100000 ^ 2, b'-', b'1']); // string of lengths 2 with value "-1"
456+
let s = to_msgpack_vec(&Int512::from(-1i64)).unwrap();
457+
assert_eq!(s, [0b10100000 ^ 2, b'-', b'1']); // string of lengths 2 with value "-1"
458+
}
459+
}
227460
}

0 commit comments

Comments
 (0)