Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hanan/support safe #68

Merged
merged 12 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
RPC_HOLESKY=RPC_HOLESKY
RPC_MAINNET=RPC_MAINNET
RPC_GNOSIS=RPC_GNOSIS
RPC_SEPOLIA=RPC_SEPOLIA
89 changes: 89 additions & 0 deletions cluster-lock-with-safe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"cluster_definition": {
"name": "Rainy cluster",
"creator": {
"address": "0xEF43Ff87070247EC9D28B482D6474318e9DAFc98",
"config_signature": "0x0c4aaafcef722cf770b419d47a21571b3c73275185a1b719e5709168af6327eb2ee392134e660d6e4ea113abd184dd4f2bd8ee09a4ae33f434433f7575873b981c"
},
"operators": [
{
"address": "0xe5b709A14859EdF820347D78E587b1634B0ec771",
"enr": "enr:-HW4QGrhMVQGLtKv_KlubzcVZM9AUsSVgDPtq4k_0q344ZtWJ6tD4riMP5jfWXXdHIklAm_pHdQv0MbTE1DrXmuEM7yAgmlkgnY0iXNlY3AyNTZrMaECa_cfUuFYiuFQp_dUEEicCBdFWNfzm8QhrNhBwey8PBg",
"config_signature": "0xad813cd706d7469b560570ef13b0afdf36d0637e9bc5362ec358c218085d9b231b1377f0078e1ed9c2af99fddb3d7608c03bf8aee6edc931c73d7204a7fdee181b",
"enr_signature": "0x304c022e74c13bfd492219efc52fb133f728afa01fefa9fe1d2540ce7c23ca730dd8248b0ad76630dd120975085d77fba20c050620f1e9583590d003110dbca41c"
},
{
"address": "0x83b7CA5be230A50DEE511657CCFF262ba04D0353",
"enr": "enr:-HW4QNGbmr_0YhIeTUQfljlDGGCnR8RGhtWr_gjMI8a5BwigGur2qFgGZrqSumAvk4gX9ciP2Tu74uOdOZPw6nvTi7aAgmlkgnY0iXNlY3AyNTZrMaEC0nrshQsWtEOphaLrZVTzPFOz8EAJweGbhXDtSsXPiLY",
"config_signature": "0x41dfb02ff13775d53ad3f739151c44952ff98a3283d124b6f69782e7458150e92771e32f965803391818d23ad95c9c7bcba15e896c3a576d1a700a16d8cb29fe1b",
"enr_signature": "0x388460abfdeded56839e4ae1480ea48a70ac44a89963bda24152e4f879a9d22918c3e77733f2ccaed5c2745f503565d651a357c6a50634a597d61e16c384961e1c"
},
{
"address": "0x123C1cD7AeE10b525D09b848D4A62596958f7D9C",
"enr": "enr:-HW4QMpDdYHJR3TlnWsnuIKfbmg2aRMMcAFlhTuuZa0Grd3dMFC0AEPHvHM2cKboULC4RUl2Dz7-LpwL2EJRGhJzxcyAgmlkgnY0iXNlY3AyNTZrMaEC73iR5dcgO_bJ8tvsHLrFglwspGFhaAbiBtGAHAJPD9c",
"config_signature": "0x91d23aefc02263fdf0edd0dcfdf26eee01c4d673b97a91a099fc10dfc01f55fa690eccc809658ea2ed17383df355f80a62bd96df15e40a2e2f740189fa6537f61b",
"enr_signature": "0x1035b284df3b1d60434bfe2d139f1472ed29663e2af719779bb5da35537f202032ab0f7cdc540c724c8e80bedfba247809d741260201fbb5bcbfc7f0813334241b"
},
{
"address": "0x169212AE1A834C1E0efd8C827Abdbc2b5557B86C",
"enr": "enr:-HW4QPwheIq51G9r9k-6GYrhp9-y-hN2SSowqgQemvPyBu9cOKERqPeqy0JuZ0yKyWiji406eK8fFeHX5fKU-6OI3u6AgmlkgnY0iXNlY3AyNTZrMaECaARWVFt5KNkHPno5YYdFBUrkLU5EmOB3K0Ns9hp-s-s",
"config_signature": "0xf97cc2536afe96e43e07aac140254cea37b0345573b12476f8840e571095d72049aa9e03a099ef2965523f83648c4323cd16ec2ee3f5e6efac177da13bcf6eb21c",
"enr_signature": "0x422f471d54eb19fc52e076bb11fd5c8de895734ed111b66708ef805ae124f87b7519e841e59eccdb75c8328223efd8bb324f3fd7d3048f037419bb83e03caae41b"
}
],
"uuid": "3ac43657-b465-49a8-94aa-25b7f7979c2a",
"version": "v1.8.0",
"timestamp": "2025-01-08T15:18:28.598Z",
"num_validators": 1,
"threshold": 3,
"validators": [
{
"fee_recipient_address": "0xe5b709A14859EdF820347D78E587b1634B0ec771",
"withdrawal_address": "0xe5b709A14859EdF820347D78E587b1634B0ec771"
}
],
"dkg_algorithm": "default",
"fork_version": "0x01017000",
"deposit_amounts": [
"32000000000"
],
"config_hash": "0x1849d9434a5ce7a7d15e1e030a48740a307c80424608c6618386d78a2e597fb3",
"definition_hash": "0x85dabfcebf9bf850521cdb65ea6083c462d9ac57eb62d9a9eb47761ec10bc80e"
},
"distributed_validators": [
{
"distributed_public_key": "0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92",
"public_shares": [
"0x8d150f921a54fe258bb57f568288de87e392946124df65861afbe52d2a3be6a1fa09d19beb62f879b0f18b2e3b26f69f",
"0x8fa69ec7c31eeda66007bc3d49a3d5654dbe6a3c0890898ea5913ed263a0efc3f6744064dbde3ee0cc1d2f08db33b4d7",
"0xb862933b7b7adb33cfb3de1052f7dd9e45dfa30accda01dfc4d3c38716747504aade8f832fa5a256dcaf13f80dbbb38f",
"0xb5a15d86945c7ff38e0b09158a3264033e6dbaebf2ec9f3426988e6d24e38b3dbd6fe413d3253b308404bb691757fb2e"
],
"builder_registration": {
"message": {
"fee_recipient": "0xe5b709a14859edf820347d78e587b1634b0ec771",
"gas_limit": 30000000,
"timestamp": 1696000704,
"pubkey": "0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92"
},
"signature": "0xa5e4220e7d8e7fd4430590c70a27104473b15c8ea03b2d1571bc86bbbcb443e2b7467f75a7e77b5d9b1a10d5fbc335cf0d59c3b3bc1b6d036b2e22511abcf41373567a15185f011ce0129741f0ae0e9e49759f9c5c0ce62f2a99f30e8c7df996"
},
"partial_deposit_data": [
{
"pubkey": "0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92",
"withdrawal_credentials": "0x010000000000000000000000e5b709a14859edf820347d78e587b1634b0ec771",
"amount": "32000000000",
"signature": "0x90aff3cb35c577644cb5fe3036ae8aeb5ac6cc2ce2a33a4332e2e883dc48f8fb71165531862d3035a4b6e3a60215d661021672a59f9406e3bd04b5f88f4f77c5f54a9ff37167807010123cdcc230040e5a485b5eb36704fd276dbf541f7151a8"
}
]
}
],
"signature_aggregate": "0x86fdeae3db45215d5f24fc19645f8f95e91fffd1a1d0a87668d3f27724fa735b85fb15de120106b8463ff1d32823813e0f139ee7cb6d3b026c6b81cc96566819817742a13e24ac44fc6571385d5b0fdbaace7e50e2cfbcd692e36b1ca25436e4",
"lock_hash": "0xe03b0fdf6d6a7d38360b0ceac1f0380b051f710e80d0e166b855ef3148d46733",
"node_signatures": [
"0x564328bcad4cac498aa51b7e7e041cf264e9c65c95138df1fc00d4bf714bc38419c43b5a32662d5c8b747d646d6e77830b2bfd62fa8402e136147c244acdce0300",
"0xb0e984efd2874fc4bc97bb9c42dccbc723080cf29a20a3cf0b05867ebe87bc430c29a560936000439fcde610ce91452ced763586d3bcca24fbe4fe83bb39202601",
"0xf3633a8e59db2e8d253ffe1b284f9edab1c1c1aca6288c3a3b2e9cb4eb7e789e23366fc325274f13b7311b9faf53665c80b5cb1f93810713f7b094cd2bec1d9900",
"0x4067921a5257efe4ceb103f2129faaa7a502781157b3b54e5efca559c558c2b43b89f30962e87df88fbf62250049a31888fcd62735d54b7553e5dc75c3b6ae0901"
]
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@
"@chainsafe/discv5": "^0.5.1",
"@chainsafe/ssz": "^0.14.0",
"@metamask/eth-sig-util": "^7.0.1",
"@safe-global/protocol-kit": "4.1.1",
"@safe-global/safe-core-sdk-types": "5.1.0",
"@types/pdf-parse": "^1.1.4",
"ajv": "^8.12.0",
"cross-fetch": "^3.1.5",
"dotenv": "^16.4.7",
"elliptic": "^6.5.4",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-import": "^2.29.1",
Expand Down
10 changes: 10 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {
MAINNET_SPLITMAIN_BYTECODE,
} from './bytecodes';

import * as dotenv from 'dotenv';
dotenv.config();

export const CONFLICT_ERROR_MSG = 'Conflict';

export const EIP712_DOMAIN_NAME = 'Obol';
Expand Down Expand Up @@ -195,3 +198,10 @@ export const DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT = 1;
export const DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT = 0.1;

export const OBOL_SDK_EMAIL = '[email protected]';

export const PROVIDER_MAP: Record<number, string> = {
1: `${process.env.RPC_MAINNET}`, // Mainnet
17000: `${process.env.RPC_HOLESKY}`, // Holesky
11155111: `${process.env.RPC_SEPOLIA}`, // Sepolia
100: `${process.env.RPC_GNOSIS}`, // Gnosis
};
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
import { isContractAvailable } from './utils.js';
export * from './types.js';
export * from './services.js';
export * from './verification/signature-validator.js';

/**
* Obol sdk Client can be used for creating, managing and activating distributed validators.
Expand Down
12 changes: 10 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Provider } from 'ethers';
import { DefinitionFlow } from './constants';
import { ethers, type Provider } from 'ethers';
import { DefinitionFlow, PROVIDER_MAP } from './constants';
import { type ClusterDefinition } from './types';

export const hexWithout0x = (hex: string): string => {
Expand Down Expand Up @@ -75,3 +75,11 @@ export const isContractAvailable = async (
}
return !!code && code !== '0x' && code !== '0x0';
};

export const getProvider = (chainId: number): ethers.Provider => {
const rpcUrl = PROVIDER_MAP[chainId];
if (!rpcUrl) {
throw new Error(`No provider configured for chainId: ${chainId}`);
}
HananINouman marked this conversation as resolved.
Show resolved Hide resolved
return new ethers.JsonRpcProvider(rpcUrl);
};
71 changes: 40 additions & 31 deletions src/verification/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
hashClusterLockV1X7,
verifyDVV1X7,
} from './v1.7.0.js';
import { ethers } from 'ethers';
import {
DOMAIN_APPLICATION_BUILDER,
DOMAIN_DEPOSIT,
Expand All @@ -33,7 +32,6 @@ import {
signEnrPayload,
signOperatorConfigHashPayload,
} from '../constants.js';
import { SignTypedDataVersion, TypedDataUtils } from '@metamask/eth-sig-util';
import {
builderRegistrationMessageType,
depositMessageType,
Expand All @@ -48,6 +46,7 @@ import {
hashClusterLockV1X8,
verifyDVV1X8,
} from './v1.8.0.js';
import { validateAddressSignature } from './signature-validator.js';

// cluster-definition hash

Expand Down Expand Up @@ -132,101 +131,111 @@ export const clusterLockHash = (clusterLock: ClusterLock): string => {

// cluster-definition signatures verification

const getPOSTConfigHashSigner = (
const validatePOSTConfigHashSigner = async (
address: string,
signature: string,
configHash: string,
chainId: FORK_MAPPING,
): string => {
): Promise<boolean> => {
try {
const sig = ethers.Signature.from(signature);
const data = signCreatorConfigHashPayload(
{ creator_config_hash: configHash },
chainId,
);
const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);

return ethers.recoverAddress(digest, sig).toLowerCase();
return await validateAddressSignature({
address,
token: signature,
data,
chainId,
});
} catch (err) {
throw err;
}
};

const getPUTConfigHashSigner = (
const validatePUTConfigHashSigner = async (
address: string,
signature: string,
configHash: string,
chainId: number,
): string => {
): Promise<boolean> => {
try {
const sig = ethers.Signature.from(signature);
const data = signOperatorConfigHashPayload(
{ operator_config_hash: configHash },
chainId,
);
const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);

return ethers.recoverAddress(digest, sig).toLowerCase();
return await validateAddressSignature({
address,
token: signature,
data,
chainId,
});
} catch (err) {
throw err;
}
};

const getEnrSigner = (
const validateEnrSigner = async (
address: string,
signature: string,
payload: string,
chainId: number,
): string => {
): Promise<boolean> => {
HananINouman marked this conversation as resolved.
Show resolved Hide resolved
try {
const sig = ethers.Signature.from(signature);

const data = signEnrPayload({ enr: payload }, chainId);
const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);

return ethers.recoverAddress(digest, sig).toLowerCase();
return await validateAddressSignature({
address,
token: signature,
data,
chainId,
});
} catch (err) {
throw err;
}
};

const verifyDefinitionSignatures = (
const verifyDefinitionSignatures = async (
clusterDefinition: ClusterDefinition,
definitionType: DefinitionFlow,
): boolean => {
): Promise<boolean> => {
if (definitionType === DefinitionFlow.Charon) {
return true;
} else {
const configSigner = getPOSTConfigHashSigner(
const isPOSTConfigHashSignerValid = await validatePOSTConfigHashSigner(
clusterDefinition.creator.address,
clusterDefinition.creator.config_signature as string,
clusterDefinition.config_hash,
FORK_MAPPING[clusterDefinition.fork_version as keyof typeof FORK_MAPPING],
);

if (configSigner !== clusterDefinition.creator.address.toLowerCase()) {
if (!isPOSTConfigHashSignerValid) {
return false;
}
if (definitionType === DefinitionFlow.Solo) {
return true;
}
return clusterDefinition.operators.every(operator => {
const configSigner = getPUTConfigHashSigner(
return clusterDefinition.operators.every(async operator => {
const isPUTConfigHashSignerValid = await validatePUTConfigHashSigner(
operator.address,
operator.config_signature as string,
clusterDefinition.config_hash,
FORK_MAPPING[
clusterDefinition.fork_version as keyof typeof FORK_MAPPING
],
);

const enrSigner = getEnrSigner(
const isENRSignerValid = await validateEnrSigner(
operator.address,
operator.enr_signature as string,
operator.enr as string,
FORK_MAPPING[
clusterDefinition.fork_version as keyof typeof FORK_MAPPING
],
);

if (
configSigner !== operator.address.toLowerCase() ||
enrSigner !== operator.address.toLowerCase()
) {
if (!isPUTConfigHashSignerValid || !isENRSignerValid) {
return false;
}
return true;
Expand Down Expand Up @@ -444,7 +453,7 @@ export const isValidClusterLock = async (
if (definitionType == null) {
return false;
}
const isValidDefinitionData = verifyDefinitionSignatures(
const isValidDefinitionData = await verifyDefinitionSignatures(
clusterLock.cluster_definition,
definitionType,
);
Expand Down
Loading
Loading