Skip to content

Commit

Permalink
Fix validation uint int sizes (#6434)
Browse files Browse the repository at this point in the history
* add ABI test cases with single numeric parameter that have a size that is not a power of 2

* fail-fast by throwing an error if during conversion of JSON shceme to Zod 'check' function is undefined (due to unsupported type)

* make sure validators are defined for all valid sized of numeric types

* prettify

* updated changelog

* include all newly generated single param test cases in validator test suite

* add schemaError and unit test

* update errors

* format

* test converage

* address feedback

---------

Co-authored-by: Alex <alex.luu@mail.utoronto.ca>
Co-authored-by: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
  • Loading branch information
3 people committed Oct 17, 2023
1 parent 226b3ba commit 09f4c8b
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 12 deletions.
4 changes: 3 additions & 1 deletion packages/web3-errors/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,6 @@ Documentation:

- Dependencies updated

## [Unreleased]
## [Unreleased]

- Added new SchemaFormatError (#6434)
4 changes: 4 additions & 0 deletions packages/web3-errors/src/error_codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,14 @@ export const ERR_INVALID_NIBBLE_WIDTH = 1014;
// Validation error codes
export const ERR_VALIDATION = 1100;


// Core error codes
export const ERR_CORE_HARDFORK_MISMATCH = 1101;
export const ERR_CORE_CHAIN_MISMATCH = 1102;

// Schema error codes
export const ERR_SCHEMA_FORMAT = 1200;

// RPC error codes (EIP-1474)
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md
export const ERR_RPC_INVALID_JSON = -32700;
Expand Down
32 changes: 32 additions & 0 deletions packages/web3-errors/src/errors/schema_errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { ERR_SCHEMA_FORMAT } from '../error_codes.js';
import { BaseWeb3Error } from '../web3_error_base.js';

export class SchemaFormatError extends BaseWeb3Error {
public code = ERR_SCHEMA_FORMAT;

public constructor(public type: string) {
super(`Format for the type ${type} is unsupported`);
}

public toJSON() {
return { ...super.toJSON(), type: this.type };
}

}
1 change: 1 addition & 0 deletions packages/web3-errors/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ export * from './errors/response_errors.js';
export * from './errors/core_errors.js';
export * from './errors/rpc_errors.js';
export * from './errors/rpc_error_messages.js';
export * from './errors/schema_errors.js';
10 changes: 10 additions & 0 deletions packages/web3-errors/test/unit/__snapshots__/errors.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,16 @@ Object {
}
`;

exports[`errors SchemaFormatError should have valid json structure 1`] = `
Object {
"code": 1200,
"innerError": undefined,
"message": "Format for the type unsupported is unsupported",
"name": "SchemaFormatError",
"type": "unsupported",
}
`;

exports[`errors TransactionError should have valid json structure 1`] = `
Object {
"code": 400,
Expand Down
11 changes: 11 additions & 0 deletions packages/web3-errors/test/unit/errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import * as signatureErrors from '../../src/errors/signature_errors';
import * as transactionErrors from '../../src/errors/transaction_errors';
import * as utilsErrors from '../../src/errors/utils_errors';
import * as responseErrors from '../../src/errors/response_errors';
import * as schemaErrors from '../../src/errors/schema_errors';

import { ConvertValueToString } from '../fixtures/errors';
import { BaseWeb3Error } from '../../src/web3_error_base';
Expand All @@ -50,6 +51,7 @@ describe('errors', () => {
...signatureErrors,
...transactionErrors,
...utilsErrors,
...schemaErrors,
})) {
if (ErrorClass === transactionErrors.InvalidPropertiesForTransactionTypeError) break;
// To disable error for the abstract class
Expand Down Expand Up @@ -379,4 +381,13 @@ describe('errors', () => {
).toMatchSnapshot();
});
});

describe('SchemaFormatError', () => {
it('should have valid json structure', () => {
expect(
new schemaErrors.SchemaFormatError("unsupported"
).toJSON(),
).toMatchSnapshot();
});
});
});
2 changes: 2 additions & 0 deletions packages/web3-validator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,5 @@ Documentation:

- Multi-dimensional arrays are now handled properly when parsing ABIs (#6435)
- Fix issue with default config with babel (and React): "TypeError: Cannot convert a BigInt value to a number #6187" (#6506)
- Validator will now properly handle all valid numeric type sizes: intN / uintN where 8 <= N <= 256 and N % 8 == 0 (#6434)
- Will now throw SchemaFormatError when unsupported format is passed to `convertToZod` method (#6434)
3 changes: 1 addition & 2 deletions packages/web3-validator/src/formats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ const formats: { [key: string]: (data: unknown) => boolean } = {
string: (data: unknown) => isString(data as ValidInputTypes),
};
// generate formats for all numbers types
for (let i = 3; i <= 8; i += 1) {
const bitSize = 2 ** i;
for (let bitSize = 8; bitSize <= 256; bitSize += 8) {
formats[`int${bitSize}`] = data => isInt(data as ValidInputTypes, { bitSize });
formats[`uint${bitSize}`] = data => isUInt(data as ValidInputTypes, { bitSize });
}
Expand Down
5 changes: 5 additions & 0 deletions packages/web3-validator/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { SchemaFormatError } from 'web3-errors';
import { Web3ValidationErrorObject } from 'web3-types';

import { z, ZodType, ZodIssue, ZodIssueCode, ZodTypeAny } from 'zod';
Expand Down Expand Up @@ -67,6 +68,10 @@ const convertToZod = (schema: JsonSchema): ZodType => {
}

if (schema?.format) {
if (!formats[schema.format]) {
throw new SchemaFormatError(schema.format);
}

return z.any().refine(formats[schema.format], (value: unknown) => ({
params: { value, format: schema.format },
}));
Expand Down
2 changes: 1 addition & 1 deletion packages/web3-validator/test/config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
},
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
},
verbose: false,
collectCoverage: false,
coverageReporters: ['json'],
Expand Down
35 changes: 34 additions & 1 deletion packages/web3-validator/test/fixtures/abi_to_json_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type AbiToJsonSchemaCase = {
data: Record<string, unknown> | Array<unknown>;
};
};
export const abiToJsonSchemaCases: AbiToJsonSchemaCase[] = [
const abiToJsonSchemaCases: AbiToJsonSchemaCase[] = [
{
title: 'single param uint',
abi: {
Expand Down Expand Up @@ -1659,3 +1659,36 @@ export const abiToJsonSchemaCases: AbiToJsonSchemaCase[] = [
},
},
];

function generateSingleParamNumericCase(type: string, bitSize: number) {
return {
title: `single param ${type}${bitSize}`,
abi: {
fullSchema: [{ name: 'a', type: `${type}${bitSize}` }],
shortSchema: [`${type}${bitSize}`],
data: [12],
},
json: {
fullSchema: {
type: 'array',
items: [{ $id: 'a', format: `${type}${bitSize}`, required: true }],
minItems: 1,
maxItems: 1,
},
shortSchema: {
type: 'array',
items: [{ $id: '/0/0', format: `${type}${bitSize}`, required: true }],
minItems: 1,
maxItems: 1,
},
data: [12],
},
};
}

for (let i = 256; i >= 8; i -= 8) {
abiToJsonSchemaCases.unshift(generateSingleParamNumericCase('int', i));
abiToJsonSchemaCases.unshift(generateSingleParamNumericCase('uint', i));
}

export { abiToJsonSchemaCases };
2 changes: 1 addition & 1 deletion packages/web3-validator/test/unit/load.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { Web3Validator } from '../../src/web3_validator';
import { Json, JsonSchema, ValidationSchemaInput } from '../..';
import { Json, JsonSchema, ValidationSchemaInput } from '../../src/types';

const abi = [
{ indexed: true, internalType: 'address', name: 'from', type: 'address' },
Expand Down
31 changes: 25 additions & 6 deletions packages/web3-validator/test/unit/web3_validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { SchemaFormatError } from 'web3-errors'
import { abiToJsonSchemaCases } from '../fixtures/abi_to_json_schema';
import { Web3Validator } from '../../src/web3_validator';
import { Web3ValidatorError } from '../../src/errors';
Expand Down Expand Up @@ -101,14 +102,32 @@ describe('web3-validator', () => {
),
).toBeUndefined();
});

it('should throw due to unsupported format', () => {
expect(() => {
validator.validateJSONSchema(
{
type: 'array',
items: [{ $id: 'a', format: 'unsupportedFormat', required: true }],
minItems: 1,
maxItems: 1,
},
['0x2df0879f1ee2b2b1f2448c64c089c29e3ad7ccc5'],
);
}).toThrow(SchemaFormatError);
});
});
describe('validateJsonSchema', () => {
it.each(abiToJsonSchemaCases.slice(0, 5))('should pass for valid data', abi => {
const jsonSchema = abi.json;
expect(
validator.validateJSONSchema(jsonSchema.fullSchema, jsonSchema.data),
).toBeUndefined();
});
// only single param test cases
it.each(abiToJsonSchemaCases.slice(0, 69))(
`$title - should pass for valid data`,
abi => {
const jsonSchema = abi.json;
expect(
validator.validateJSONSchema(jsonSchema.fullSchema, jsonSchema.data),
).toBeUndefined();
},
);

it('should throw', () => {
expect(() => {
Expand Down

0 comments on commit 09f4c8b

Please sign in to comment.