Skip to content

Commit 4f1d875

Browse files
authored
Merge pull request #392 from Emurgo/nint-fix
Fix: nint deserialization below i64::MIN
2 parents da86e74 + 6f5fcd3 commit 4f1d875

File tree

1 file changed

+62
-20
lines changed

1 file changed

+62
-20
lines changed

rust/src/utils.rs

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ impl Deserialize for Value {
479479
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
480480
pub struct Int(pub (crate) i128);
481481

482+
to_from_bytes!(Int);
483+
482484
#[wasm_bindgen]
483485
impl Int {
484486
pub fn new(x: &BigNum) -> Self {
@@ -573,13 +575,30 @@ impl Deserialize for Int {
573575
(|| -> Result<_, DeserializeError> {
574576
match raw.cbor_type()? {
575577
cbor_event::Type::UnsignedInteger => Ok(Self(raw.unsigned_integer()? as i128)),
576-
cbor_event::Type::NegativeInteger => Ok(Self(raw.negative_integer()? as i128)),
578+
cbor_event::Type::NegativeInteger => Ok(Self(read_nint(raw)?)),
577579
_ => Err(DeserializeFailure::NoVariantMatched.into()),
578580
}
579581
})().map_err(|e| e.annotate("Int"))
580582
}
581583
}
582584

585+
/// TODO: this function can be removed in case `cbor_event` library ever gets a fix on their side
586+
/// See https://github.com/Emurgo/cardano-serialization-lib/pull/392
587+
fn read_nint<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result <i128, DeserializeError> {
588+
let found = raw.cbor_type()?;
589+
if found != cbor_event::Type::NegativeInteger {
590+
return Err(cbor_event::Error::Expected(cbor_event::Type::NegativeInteger, found).into());
591+
}
592+
let (len, len_sz) = raw.cbor_len()?;
593+
match len {
594+
cbor_event::Len::Indefinite => Err(cbor_event::Error::IndefiniteLenNotSupported(cbor_event::Type::NegativeInteger).into()),
595+
cbor_event::Len::Len(v) => {
596+
raw.advance(1 + len_sz)?;
597+
Ok(-(v as i128) - 1)
598+
}
599+
}
600+
}
601+
583602
const BOUNDED_BYTES_CHUNK_SIZE: usize = 64;
584603

585604
pub (crate) fn write_bounded_bytes<'se, W: Write>(serializer: &'se mut Serializer<W>, bytes: &[u8]) -> cbor_event::Result<&'se mut Serializer<W>> {
@@ -706,22 +725,28 @@ impl cbor_event::se::Serialize for BigInt {
706725
num_bigint::Sign::Minus => serializer.write_negative_integer(-(*u64_digits.first().unwrap() as i128) as i64),
707726
},
708727
_ => {
728+
// Small edge case: nint's minimum is -18446744073709551616 but in this bigint lib
729+
// that takes 2 u64 bytes so we put that as a special case here:
730+
if sign == num_bigint::Sign::Minus && u64_digits == vec![0, 1] {
731+
serializer.write_negative_integer(-18446744073709551616i128 as i64)
732+
} else {
709733
let (sign, bytes) = self.0.to_bytes_be();
710-
match sign {
711-
// positive bigint
712-
num_bigint::Sign::Plus |
713-
num_bigint::Sign::NoSign => {
714-
serializer.write_tag(2u64)?;
715-
write_bounded_bytes(serializer, &bytes)
716-
},
717-
// negative bigint
718-
num_bigint::Sign::Minus => {
719-
serializer.write_tag(3u64)?;
720-
use std::ops::Neg;
721-
// CBOR RFC defines this as the bytes of -n -1
722-
let adjusted = self.0.clone().neg().checked_sub(&num_bigint::BigInt::from(1u32)).unwrap().to_biguint().unwrap();
723-
write_bounded_bytes(serializer, &adjusted.to_bytes_be())
724-
},
734+
match sign {
735+
// positive bigint
736+
num_bigint::Sign::Plus |
737+
num_bigint::Sign::NoSign => {
738+
serializer.write_tag(2u64)?;
739+
write_bounded_bytes(serializer, &bytes)
740+
},
741+
// negative bigint
742+
num_bigint::Sign::Minus => {
743+
serializer.write_tag(3u64)?;
744+
use std::ops::Neg;
745+
// CBOR RFC defines this as the bytes of -n -1
746+
let adjusted = self.0.clone().neg().checked_sub(&num_bigint::BigInt::from(1u32)).unwrap().to_biguint().unwrap();
747+
write_bounded_bytes(serializer, &adjusted.to_bytes_be())
748+
},
749+
}
725750
}
726751
},
727752
}
@@ -753,7 +778,7 @@ impl Deserialize for BigInt {
753778
// uint
754779
CBORType::UnsignedInteger => Ok(Self(num_bigint::BigInt::from(raw.unsigned_integer()?))),
755780
// nint
756-
CBORType::NegativeInteger => Ok(Self(num_bigint::BigInt::from(raw.negative_integer()?))),
781+
CBORType::NegativeInteger => Ok(Self(num_bigint::BigInt::from(read_nint(raw)?))),
757782
_ => return Err(DeserializeFailure::NoVariantMatched.into()),
758783
}
759784
})().map_err(|e| e.annotate("BigInt"))
@@ -2085,9 +2110,8 @@ mod tests {
20852110
assert_eq!(hex::decode("c249010000000000000000").unwrap(), BigInt::from_str("18446744073709551616").unwrap().to_bytes());
20862111
// uint
20872112
assert_eq!(hex::decode("1b000000e8d4a51000").unwrap(), BigInt::from_str("1000000000000").unwrap().to_bytes());
2088-
// nint
2089-
// we can't use this due to cbor_event actually not supporting the full NINT spectrum as it uses an i64 for some reason...
2090-
//assert_eq!(hex::decode("3bffffffffffffffff").unwrap(), BigInt::from_str("-18446744073709551616").unwrap().to_bytes());
2113+
// nint (lowest possible - used to be unsupported but works now)
2114+
assert_eq!(hex::decode("3bffffffffffffffff").unwrap(), BigInt::from_str("-18446744073709551616").unwrap().to_bytes());
20912115
// this one fits in an i64 though
20922116
assert_eq!(hex::decode("3903e7").unwrap(), BigInt::from_str("-1000").unwrap().to_bytes());
20932117

@@ -2299,4 +2323,22 @@ mod tests {
22992323
assert_eq!(Int::new_i32(42).as_i32_or_fail().unwrap(), 42);
23002324
assert_eq!(Int::new_i32(-42).as_i32_or_fail().unwrap(), -42);
23012325
}
2326+
2327+
#[test]
2328+
fn int_full_range() {
2329+
// cbor_event's nint API worked via i64 but we now have a workaround for it
2330+
// so these tests are here to make sure that workaround works.
2331+
2332+
// first nint below of i64::MIN
2333+
let bytes_x = vec![0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
2334+
let x = Int::from_bytes(bytes_x.clone()).unwrap();
2335+
assert_eq!(x.to_str(), "-9223372036854775809");
2336+
assert_eq!(bytes_x, x.to_bytes());
2337+
2338+
// smallest possible nint which is -u64::MAX - 1
2339+
let bytes_y = vec![0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
2340+
let y = Int::from_bytes(bytes_y.clone()).unwrap();
2341+
assert_eq!(y.to_str(), "-18446744073709551616");
2342+
assert_eq!(bytes_y, y.to_bytes());
2343+
}
23022344
}

0 commit comments

Comments
 (0)