diff --git a/src/ajv.ts b/src/ajv.ts index 693b83e..d8d9268 100644 --- a/src/ajv.ts +++ b/src/ajv.ts @@ -1,10 +1,31 @@ import Ajv, { type ErrorObject } from 'ajv' +import { ether_to_gwei } from './constants'; -export function validatePayload ( + +function validDpositAmounts(data: boolean, deposits: string[]) { + let sum = 0; + for (let i = 0; i < deposits.length; i++) { + const amount = parseInt(deposits[i]); + if (amount % ether_to_gwei !== 0 || amount > 32 * ether_to_gwei) { + return false + } + sum += amount; + } + if (sum !== 32 * ether_to_gwei) { return false } { + return true + } +} + +export function validatePayload( data: any, schema: any, ): ErrorObject[] | undefined | null | boolean { const ajv = new Ajv() + ajv.addKeyword({ + keyword: 'validDpositAmounts', + validate: validDpositAmounts, + errors: true, + }); const validate = ajv.compile(schema) const isValid = validate(data) if (!isValid) { diff --git a/src/schema.ts b/src/schema.ts index d431816..8467a39 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -53,6 +53,14 @@ export const definitionSchema = { required: ['fee_recipient_address', 'withdrawal_address'], }, }, + deposit_amounts: { + type: 'array', + items: { + type: 'string', + pattern: "^[0-9]+$", + }, + validDpositAmounts:true + }, }, - required: ['name', 'operators', 'validators'], + required: ['name','operators', 'validators'], } diff --git a/src/verification/common.ts b/src/verification/common.ts index b903267..9f015db 100644 --- a/src/verification/common.ts +++ b/src/verification/common.ts @@ -320,9 +320,10 @@ export const signingRoot = ( const verifyLockData = async (clusterLock: ClusterLock): Promise => { - const ec = new elliptic.ec('secp256k1') - const validators = clusterLock.distributed_validators - const nodeSignatures = clusterLock.node_signatures + const ec = new elliptic.ec('secp256k1'); + const validators = clusterLock.distributed_validators; + const nodeSignatures = clusterLock.node_signatures; + const depositDomain = computeDomain( fromHexString(DOMAIN_DEPOSIT), clusterLock.cluster_definition.fork_version, @@ -344,7 +345,7 @@ const verifyLockData = async (clusterLock: ClusterLock): Promise => { const validatorPublicShares = validator.public_shares; const distributedPublicKey = validator.distributed_public_key; - + //Needed in signature_aggregate verification for (const element of validatorPublicShares) { pubShares.push(fromHexString(element)) } diff --git a/test/methods.test.ts b/test/methods.test.ts index 9834d8d..ba38392 100644 --- a/test/methods.test.ts +++ b/test/methods.test.ts @@ -1,6 +1,6 @@ import { ethers } from 'ethers' import { Client, validateClusterLock } from '../src/index' -import { clusterConfigV1X8, clusterLockV1X7, clusterLockV1X8 } from './fixtures.js' +import { clusterConfigV1X7, clusterConfigV1X8, clusterLockV1X7, clusterLockV1X8 } from './fixtures.js' import { SDK_VERSION } from '../src/constants' import { Base } from '../src/base' import { validatePayload } from '../src/ajv' @@ -70,6 +70,36 @@ describe('Cluster Client', () => { } }) + test('createClusterDefinition should accept a configuration without deposit_amounts ', async () => { + clientInstance['request'] = jest + .fn() + .mockReturnValue(Promise.resolve({ config_hash: mockConfigHash })) + + const configHash = await clientInstance.createClusterDefinition({ + ...clusterConfigV1X7, + }) + + expect(configHash).toEqual(mockConfigHash) + }) + + test('createClusterDefinition should throw on not valid deposit_amounts ', async () => { + clientInstance['request'] = jest + .fn() + .mockReturnValue(Promise.resolve({ config_hash: mockConfigHash })) + try { + await clientInstance.createClusterDefinition({ + ...clusterConfigV1X7, + deposit_amounts: ["34000000"] + }) + + } catch (error: any) { + console.log(error.message) + expect(error.message).toEqual( + "Schema compilation errors', must pass \"validDpositAmounts\" keyword validation", + ) + } + }) + test('validatePayload should throw an error on empty schema', async () => { try { validatePayload({ ...clusterConfigV1X8, operators: [] }, '')