From 858c9b6c6f37cf1db849b93472670989a71d4d21 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Thu, 19 Dec 2024 16:38:42 +0000 Subject: [PATCH 1/7] feat: added error condition around number values that are too big --- .../encoding/coders/BigNumberCoder.test.ts | 40 +++++++++++++++++++ .../src/encoding/coders/BigNumberCoder.ts | 7 ++++ 2 files changed, 47 insertions(+) diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts index 2cd1632c752..35198060bf5 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts @@ -23,6 +23,35 @@ describe('BigNumberCoder', () => { expect(actual).toStrictEqual(expected); }); + it('should encode a u64 [max safe integer]', () => { + const coder = new BigNumberCoder('u64'); + const value: number = Number.MAX_SAFE_INTEGER; + const expected = new Uint8Array([0, 31, 255, 255, 255, 255, 255, 255]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should encode a u64 [very big number - as string]', () => { + const coder = new BigNumberCoder('u64'); + const value: string = (Number.MAX_SAFE_INTEGER + 1).toString(); + const expected = new Uint8Array([0, 32, 0, 0, 0, 0, 0, 0]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should throw an error when encoding [number more than max safe integer]', () => { + const coder = new BigNumberCoder('u64'); + const value: number = Number.MAX_SAFE_INTEGER + 1; + + expect(() => coder.encode(value)).toThrow( + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - number value is too large.') + ); + }); + it('should decode a u64 number', () => { const coder = new BigNumberCoder('u64'); const expectedValue = 0; @@ -34,6 +63,17 @@ describe('BigNumberCoder', () => { expect(actualLength).toBe(expectedLength); }); + it('should decode a u64 [very big number]', () => { + const coder = new BigNumberCoder('u64'); + const data = new Uint8Array([1, 15, 174, 231, 121, 200, 89, 80]); + const expectedValue = bn('76472027892439376'); + + const [actualValue, actualLength] = coder.decode(data, 0); + + expect(actualValue).toEqualBn(expectedValue); + expect(actualLength).toEqual(8); + }); + it('should encode u8 max number', () => { const coder = new BigNumberCoder('u64'); const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 255]); diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts index 08e8c58eb5c..ff0162a8727 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts @@ -20,6 +20,13 @@ export class BigNumberCoder extends Coder { encode(value: BNInput): Uint8Array { let bytes; + if (typeof value === 'number' && value > Number.MAX_SAFE_INTEGER) { + throw new FuelError( + ErrorCode.ENCODE_ERROR, + `Invalid ${this.type} type - number value is too large.` + ); + } + try { bytes = toBytes(value, this.encodedLength); } catch (error) { From 2a4d6cf81f74884dd9024c4e4530b71ba0d95ede Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Thu, 19 Dec 2024 16:40:29 +0000 Subject: [PATCH 2/7] chore: changeset --- .changeset/orange-moles-leave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/orange-moles-leave.md diff --git a/.changeset/orange-moles-leave.md b/.changeset/orange-moles-leave.md new file mode 100644 index 00000000000..ffb08873802 --- /dev/null +++ b/.changeset/orange-moles-leave.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/abi-coder": patch +--- + +fix: handling very large numbers gracefully From a0f983f7b90b7c53d06899288c64ccd2541fe570 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Fri, 20 Dec 2024 17:28:55 +0000 Subject: [PATCH 3/7] chore: add comment + altered test --- .../src/encoding/coders/BigNumberCoder.test.ts | 15 ++++++++++++--- .../src/encoding/coders/BigNumberCoder.ts | 3 +++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts index 35198060bf5..dc43eaa6a19 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts @@ -33,10 +33,19 @@ describe('BigNumberCoder', () => { expect(data).toEqual(expected); }); + it('should throw an error when encoding [number more than max safe integer]', () => { + const coder = new BigNumberCoder('u64'); + const value: number = Number.MAX_SAFE_INTEGER + 1; + + expect(() => coder.encode(value)).toThrow( + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - number value is too large.') + ); + }); + it('should encode a u64 [very big number - as string]', () => { const coder = new BigNumberCoder('u64'); - const value: string = (Number.MAX_SAFE_INTEGER + 1).toString(); - const expected = new Uint8Array([0, 32, 0, 0, 0, 0, 0, 0]); + const value: string = '76472027892439376'; + const expected = new Uint8Array([1, 15, 174, 231, 121, 200, 89, 80]); const data = coder.encode(value); @@ -45,7 +54,7 @@ describe('BigNumberCoder', () => { it('should throw an error when encoding [number more than max safe integer]', () => { const coder = new BigNumberCoder('u64'); - const value: number = Number.MAX_SAFE_INTEGER + 1; + const value: number = 76472027892439376; expect(() => coder.encode(value)).toThrow( new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - number value is too large.') diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts index ff0162a8727..e9a68d5e783 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts @@ -20,6 +20,9 @@ export class BigNumberCoder extends Coder { encode(value: BNInput): Uint8Array { let bytes; + // We throw an error if the value is a number and it's more than the max safe integer + // This is because we can experience some odd behavior with integers more than the max safe integer + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER#description if (typeof value === 'number' && value > Number.MAX_SAFE_INTEGER) { throw new FuelError( ErrorCode.ENCODE_ERROR, From b68944cadc18df5eed0abfceac5545c3f5850160 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Mon, 23 Dec 2024 09:53:05 +0000 Subject: [PATCH 4/7] Update .changeset/orange-moles-leave.md --- .changeset/orange-moles-leave.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/orange-moles-leave.md b/.changeset/orange-moles-leave.md index ffb08873802..b749b60962f 100644 --- a/.changeset/orange-moles-leave.md +++ b/.changeset/orange-moles-leave.md @@ -2,4 +2,4 @@ "@fuel-ts/abi-coder": patch --- -fix: handling very large numbers gracefully +fix: validation and handling of unsafe integers From 8faf42db844a4cf4e709efc1e716589cde472efc Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Mon, 30 Dec 2024 11:05:01 +0000 Subject: [PATCH 5/7] Update packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts Co-authored-by: Daniel Bate --- packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts index dc43eaa6a19..d12449cdcc7 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts @@ -57,7 +57,7 @@ describe('BigNumberCoder', () => { const value: number = 76472027892439376; expect(() => coder.encode(value)).toThrow( - new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - number value is too large.') + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - too large. Number can only safely handle up to 53 bits'). ); }); From eb24fc14c6266760414820969a0ee3c1dea641c1 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Mon, 30 Dec 2024 11:15:43 +0000 Subject: [PATCH 6/7] chore: improving error message --- .../encoding/coders/BigNumberCoder.test.ts | 20 +++++++++++++------ .../src/encoding/coders/BigNumberCoder.ts | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts index d12449cdcc7..38ee726f084 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts @@ -33,12 +33,16 @@ describe('BigNumberCoder', () => { expect(data).toEqual(expected); }); - it('should throw an error when encoding [number more than max safe integer]', () => { + it('should throw an error when encoding [number more than max safe integer]', async () => { const coder = new BigNumberCoder('u64'); const value: number = Number.MAX_SAFE_INTEGER + 1; - expect(() => coder.encode(value)).toThrow( - new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - number value is too large.') + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + ErrorCode.ENCODE_ERROR, + 'Invalid u64 type - too large. Number can only safely handle up to 53 bits.' + ) ); }); @@ -52,12 +56,16 @@ describe('BigNumberCoder', () => { expect(data).toEqual(expected); }); - it('should throw an error when encoding [number more than max safe integer]', () => { + it('should throw an error when encoding [number more than max safe integer]', async () => { const coder = new BigNumberCoder('u64'); const value: number = 76472027892439376; - expect(() => coder.encode(value)).toThrow( - new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 type - too large. Number can only safely handle up to 53 bits'). + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + ErrorCode.ENCODE_ERROR, + 'Invalid u64 type - too large. Number can only safely handle up to 53 bits.' + ) ); }); diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts index e9a68d5e783..b421bec9ab8 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.ts @@ -26,7 +26,7 @@ export class BigNumberCoder extends Coder { if (typeof value === 'number' && value > Number.MAX_SAFE_INTEGER) { throw new FuelError( ErrorCode.ENCODE_ERROR, - `Invalid ${this.type} type - number value is too large.` + `Invalid ${this.type} type - number value is too large. Number can only safely handle up to 53 bits.` ); } From c1eed364bf6eb1feac713ef8937df5779ce8c6af Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Mon, 30 Dec 2024 13:02:57 +0000 Subject: [PATCH 7/7] chore: fix error messages --- packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts index 38ee726f084..ff0718c1ce8 100644 --- a/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts +++ b/packages/abi-coder/src/encoding/coders/BigNumberCoder.test.ts @@ -41,7 +41,7 @@ describe('BigNumberCoder', () => { () => coder.encode(value), new FuelError( ErrorCode.ENCODE_ERROR, - 'Invalid u64 type - too large. Number can only safely handle up to 53 bits.' + 'Invalid u64 type - number value is too large. Number can only safely handle up to 53 bits.' ) ); }); @@ -64,7 +64,7 @@ describe('BigNumberCoder', () => { () => coder.encode(value), new FuelError( ErrorCode.ENCODE_ERROR, - 'Invalid u64 type - too large. Number can only safely handle up to 53 bits.' + 'Invalid u64 type - number value is too large. Number can only safely handle up to 53 bits.' ) ); });