diff --git a/CHANGELOG.md b/CHANGELOG.md index 1284a19..edf550a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 3.4.0 - Stellar Address Support: Add support for stellar Contract address. +- Fix incorrect CBOR encoding of large negative integers. ## 3.3.0 diff --git a/lib/cbor/cbor.dart b/lib/cbor/cbor.dart index 6dee719..edfd53a 100644 --- a/lib/cbor/cbor.dart +++ b/lib/cbor/cbor.dart @@ -5,3 +5,5 @@ library cbor; export 'core/cbor.dart'; export 'types/types.dart'; + +export 'exception/exception.dart'; diff --git a/lib/cbor/core/cbor.dart b/lib/cbor/core/cbor.dart index b38c8d9..4c9c91a 100644 --- a/lib/cbor/core/cbor.dart +++ b/lib/cbor/core/cbor.dart @@ -1,7 +1,7 @@ +import 'package:blockchain_utils/cbor/exception/exception.dart'; import 'package:blockchain_utils/utils/utils.dart'; import 'package:blockchain_utils/cbor/types/types.dart'; import 'package:blockchain_utils/cbor/utils/cbor_utils.dart'; -import 'package:blockchain_utils/exception/exception.dart'; /// An abstract class representing a CBOR (Concise Binary Object Representation) object. /// CBOR objects can hold various data types and optional tags, providing a flexible way @@ -49,12 +49,15 @@ abstract class CborObject { } else if (value is List>) { return CborDynamicBytesValue(value); } else if (value is Map) { - return CborMapValue.fixedLength(value); + return CborMapValue.fixedLength({ + for (final i in value.entries) + CborObject.fromDynamic(i.key): CborObject.fromDynamic(i.value) + }); } else if (value is List) { return CborListValue.fixedLength( value.map((e) => CborObject.fromDynamic(e)).toList()); } - throw UnimplementedError("does not supported"); + throw const CborException("does not supported"); } } @@ -73,7 +76,7 @@ abstract class CborNumeric implements CborObject { } else if (val is CborSafeIntValue) { return val.value; } - throw const ArgumentException("invalid cbornumeric"); + throw const CborException("invalid cbornumeric"); } /// Convert the CborNumeric object to an integer. diff --git a/lib/cbor/exception/exception.dart b/lib/cbor/exception/exception.dart new file mode 100644 index 0000000..c6f804c --- /dev/null +++ b/lib/cbor/exception/exception.dart @@ -0,0 +1,6 @@ +import 'package:blockchain_utils/exception/exceptions.dart'; + +class CborException extends BlockchainUtilsException { + const CborException(String message, {Map? details}) + : super(message, details: details); +} diff --git a/lib/cbor/types/bytes.dart b/lib/cbor/types/bytes.dart index 72b9e76..2e8a0d1 100644 --- a/lib/cbor/types/bytes.dart +++ b/lib/cbor/types/bytes.dart @@ -10,8 +10,6 @@ class CborBytesValue implements CborObject { /// It accepts the bytes value. CborBytesValue(List value) : value = value.asImmutableBytes; - /// The value as a List. - @override final List value; /// Encode the value into CBOR bytes @@ -51,6 +49,8 @@ class CborDynamicBytesValue implements CborObject { @override final List> value; + // @override + // List> get value => _value; /// Encode the value into CBOR bytes @override @@ -82,7 +82,7 @@ class CborDynamicBytesValue implements CborObject { operator ==(other) { if (other is! CborDynamicBytesValue) return false; - return value == other.value; + return CompareUtils.iterableIsEqual(value, other.value); } /// ovveride hash code diff --git a/lib/cbor/types/cbor_tag.dart b/lib/cbor/types/cbor_tag.dart index 3789e5a..1eb3e6c 100644 --- a/lib/cbor/types/cbor_tag.dart +++ b/lib/cbor/types/cbor_tag.dart @@ -22,7 +22,7 @@ class CborTagValue implements CborObject { List encode() { final bytes = CborBytesTracker(); bytes.pushTags(tags); - final obj = CborObject.fromDynamic(value).encode(); + final obj = CborObject.fromDynamic(_value).encode(); bytes.pushBytes(obj); return bytes.toBytes(); } diff --git a/lib/cbor/types/decimal.dart b/lib/cbor/types/decimal.dart index 0d3eef4..70ff400 100644 --- a/lib/cbor/types/decimal.dart +++ b/lib/cbor/types/decimal.dart @@ -13,9 +13,7 @@ class CborDecimalFracValue implements CborObject { /// Create a CborBigFloatValue from two CborNumeric values representing the exponent and mantissa. factory CborDecimalFracValue.fromCborNumeric( - CborNumeric exponent, - CborNumeric mantissa, - ) { + CborNumeric exponent, CborNumeric mantissa) { return CborDecimalFracValue(CborNumeric.getCborNumericValue(exponent), CborNumeric.getCborNumericValue(mantissa)); } diff --git a/lib/cbor/types/int.dart b/lib/cbor/types/int.dart index 8f2d26d..ff6ae98 100644 --- a/lib/cbor/types/int.dart +++ b/lib/cbor/types/int.dart @@ -1,4 +1,5 @@ import 'package:blockchain_utils/cbor/core/cbor.dart'; +import 'package:blockchain_utils/cbor/exception/exception.dart'; import 'package:blockchain_utils/cbor/utils/dynamic_bytes.dart'; import 'package:blockchain_utils/cbor/core/tags.dart'; import 'package:blockchain_utils/utils/utils.dart'; @@ -20,10 +21,13 @@ class CborIntValue implements CborNumeric { List encode() { final bytes = CborBytesTracker(); if (value.bitLength > 31 && value.isNegative) { - final value = (~BigInt.parse(this.value.toString())).toInt(); - bytes.pushInt(MajorTags.negInt, value); + final value = (~BigInt.parse(this.value.toString())); + if (!value.isValidInt) { + throw CborException("Value is to large for encoding as CborInteger", + details: {"value": this.value.toString()}); + } + bytes.pushInt(MajorTags.negInt, value.toInt()); } else { - // print("is here lower!"); bytes.pushInt(value.isNegative ? MajorTags.negInt : MajorTags.posInt, value.isNegative ? ~value : value); } diff --git a/lib/cbor/types/list.dart b/lib/cbor/types/list.dart index f43157e..9f8ab92 100644 --- a/lib/cbor/types/list.dart +++ b/lib/cbor/types/list.dart @@ -1,4 +1,3 @@ -import 'package:blockchain_utils/helper/helper.dart'; import 'package:blockchain_utils/utils/utils.dart'; import 'package:blockchain_utils/cbor/utils/dynamic_bytes.dart'; import 'package:blockchain_utils/cbor/core/tags.dart'; @@ -9,18 +8,13 @@ class CborListValue implements CborObject { /// Constructor for creating a CborListValue instance with the provided parameters. /// It accepts the List of all cbor encodable value. /// - CborListValue.fixedLength(List value) - : value = value.immutable, - _isFixedLength = true; + CborListValue.fixedLength(this.value) : _isFixedLength = true; /// Constructor for creating a CborListValue instance with the provided parameters. /// It accepts the List of all cbor encodable value. /// this method encode values with indefinite tag. - CborListValue.dynamicLength(List value) - : value = value.immutable, - _isFixedLength = false; + CborListValue.dynamicLength(this.value) : _isFixedLength = false; - /// value as List @override final List value; diff --git a/lib/cbor/types/map.dart b/lib/cbor/types/map.dart index 19aa0db..2a60389 100644 --- a/lib/cbor/types/map.dart +++ b/lib/cbor/types/map.dart @@ -1,4 +1,3 @@ -import 'package:blockchain_utils/helper/helper.dart'; import 'package:blockchain_utils/utils/utils.dart'; import 'package:blockchain_utils/cbor/utils/dynamic_bytes.dart'; import 'package:blockchain_utils/cbor/core/tags.dart'; @@ -8,16 +7,12 @@ import 'package:blockchain_utils/cbor/core/cbor.dart'; class CborMapValue implements CborObject { /// Constructor for creating a CborMapValue instance with the provided parameters. /// It accepts the Map with all cbor encodable key and value. - CborMapValue.fixedLength(Map value) - : value = value.immutable, - _isFixedLength = true; + CborMapValue.fixedLength(this.value) : _isFixedLength = true; /// Constructor for creating a CborMapValue instance with the provided parameters. /// It accepts the Map with all cbor encodable key and value. /// this method encode values with indefinite tag. - CborMapValue.dynamicLength(Map value) - : value = value.immutable, - _isFixedLength = false; + CborMapValue.dynamicLength(this.value) : _isFixedLength = false; /// value as Map @override diff --git a/lib/cbor/types/set.dart b/lib/cbor/types/set.dart index f11655b..18fbf5b 100644 --- a/lib/cbor/types/set.dart +++ b/lib/cbor/types/set.dart @@ -7,7 +7,7 @@ import 'package:blockchain_utils/cbor/core/cbor.dart'; class CborSetValue implements CborObject { /// Constructor for creating a CborSetValue instance with the provided parameters. /// It accepts a set of all encodable cbor object. - CborSetValue(Set value) : value = Set.unmodifiable(value); + CborSetValue(this.value); /// value as set @override diff --git a/lib/cbor/types/string.dart b/lib/cbor/types/string.dart index b022d96..d94f4d8 100644 --- a/lib/cbor/types/string.dart +++ b/lib/cbor/types/string.dart @@ -61,7 +61,6 @@ class CborIndefiniteStringValue extends CborString { /// It accepts a List value. CborIndefiniteStringValue(List value) : value = value.immutable; - /// value as List @override final List value; diff --git a/lib/cbor/utils/cbor_utils.dart b/lib/cbor/utils/cbor_utils.dart index 2afaa1d..97acb7f 100644 --- a/lib/cbor/utils/cbor_utils.dart +++ b/lib/cbor/utils/cbor_utils.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; import 'package:blockchain_utils/cbor/core/cbor.dart'; +import 'package:blockchain_utils/cbor/exception/exception.dart'; import 'package:blockchain_utils/cbor/types/types.dart'; import 'package:blockchain_utils/cbor/utils/float_utils.dart'; import 'package:blockchain_utils/cbor/core/tags.dart'; -import 'package:blockchain_utils/exception/exception.dart'; import 'package:blockchain_utils/utils/utils.dart'; class CborUtils { @@ -23,7 +23,7 @@ class CborUtils { // Split the string into the date and offset parts final parts = dateTimeString.split('+'); if (parts.length != 2) { - throw MessageException("Invalid format: $dateTimeString"); + throw CborException("Invalid RFC3339 format: $dateTimeString"); } final datePart = DateTime.parse(parts[0]); return datePart; @@ -66,11 +66,11 @@ class CborUtils { } return _decodeArray(cborBytes, i, info, tags); default: - throw ArgumentException( + throw CborException( "invalid or unsuported cbor tag major: $majorTag "); } } - throw const ArgumentException("invalid or unsuported cbor tag"); + throw const CborException("invalid or unsuported cbor tag"); } static Tuple, int> _parsBytes(int info, List cborBytes) { @@ -96,7 +96,7 @@ class CborUtils { } return Tuple(decode, len + 1); } else { - throw ArgumentException('Invalid additional info for int: $info'); + throw CborException('Invalid additional info for int: $info'); } } @@ -254,7 +254,7 @@ class CborUtils { List objects, List tags) { objects = objects.whereType().toList(); if (objects.length != 2) { - throw const MessageException("invalid bigFloat array length"); + throw const CborException("invalid bigFloat array length"); } if (BytesUtils.bytesEqual(tags, CborTags.decimalFrac)) { tags.clear(); @@ -313,7 +313,7 @@ class CborUtils { offset = offset + 8; break; default: - throw const MessageException("Invalid simpleOrFloatTags"); + throw const CborException("Invalid simpleOrFloatTags"); } if (BytesUtils.bytesEqual(tags, CborTags.dateEpoch)) { final dt = DateTime.fromMillisecondsSinceEpoch((val * 1000).round()); diff --git a/lib/cbor/utils/float_utils.dart b/lib/cbor/utils/float_utils.dart index d9d496e..79756e3 100644 --- a/lib/cbor/utils/float_utils.dart +++ b/lib/cbor/utils/float_utils.dart @@ -17,7 +17,7 @@ import 'dart:math' as math; import 'dart:typed_data'; import 'package:blockchain_utils/cbor/core/tags.dart'; -import 'package:blockchain_utils/exception/exception.dart'; +import 'package:blockchain_utils/cbor/exception/exception.dart'; import 'package:blockchain_utils/utils/utils.dart'; // Enum representing different floating-point formats and their characteristics. @@ -60,7 +60,7 @@ class FloatLength { if (index >= 0 && index < values.length) { return values[index]; } - throw MessageException('Index out of bounds', details: {"input": index}); + throw CborException('Index out of bounds', details: {"input": index}); } } @@ -242,13 +242,13 @@ class FloatUtils { switch (decodFloatType) { case FloatLength.bytes16: if (!isLessThan16) { - throw const ArgumentException("overflow bytes"); + throw const CborException("overflow bytes"); } bytes = _encodeFloat16(endianness); break; case FloatLength.bytes32: if (!isLessThan32) { - throw const ArgumentException("overflow bytes"); + throw const CborException("overflow bytes"); } bytes = _encodeFloat32(endianness); break; @@ -262,7 +262,7 @@ class FloatUtils { /// Decode a 16-bit floating-point value from a byte array and return it as a double. static double floatFromBytes16(List bytes) { if (bytes.length != 2) { - throw const ArgumentException( + throw const CborException( 'Input byte array must be exactly 2 bytes long for Float16'); } diff --git a/lib/utils/compare/compare.dart b/lib/utils/compare/compare.dart index 67d1a3d..59a02f8 100644 --- a/lib/utils/compare/compare.dart +++ b/lib/utils/compare/compare.dart @@ -21,7 +21,15 @@ class CompareUtils { /// Compare the individual byte values in 'a' and 'b'. for (int index = 0; index < a.length; index += 1) { - if (a.elementAt(index) != b.elementAt(index)) { + final valueA = a.elementAt(index); + final valueB = b.elementAt(index); + if (valueA is Map && valueB is Map) { + // Recursively compare maps + if (!mapIsEqual(valueA, valueB)) return false; + } else if (valueA is Iterable && valueB is Iterable) { + // Recursively compare iterables + if (!iterableIsEqual(valueA, valueB)) return false; + } else if (valueA != valueB) { return false; } } @@ -29,4 +37,33 @@ class CompareUtils { /// If no differences were found, the lists are equal. return true; } + + static bool mapIsEqual(Map? a, Map? b) { + // Handle null comparison + if (a == null) return b == null; + if (b == null || a.length != b.length) return false; + + // Check if 'a' and 'b' are identical objects + if (identical(a, b)) return true; + + // Compare the entries in 'a' and 'b' + for (final key in a.keys) { + if (!b.containsKey(key)) return false; + + final valueA = a[key]; + final valueB = b[key]; + + if (valueA is Map && valueB is Map) { + // Recursively compare maps + if (!mapIsEqual(valueA, valueB)) return false; + } else if (valueA is Iterable && valueB is Iterable) { + // Recursively compare iterables + if (!iterableIsEqual(valueA, valueB)) return false; + } else if (valueA != valueB) { + return false; + } + } + + return true; + } }