diff --git a/src/app/routes/create-cluster/distribute-offline.tsx b/src/app/routes/create-cluster/distribute-offline.tsx index b4524c450..88a9f8b4a 100644 --- a/src/app/routes/create-cluster/distribute-offline.tsx +++ b/src/app/routes/create-cluster/distribute-offline.tsx @@ -42,12 +42,15 @@ export const DistributeOffline: FC = () => { const health = useOperatorsDKGHealth(operators.data ?? [], { enabled: isNew, }); - - const hasUnhealthyOperators = - isNew && - health.data?.some( - ({ isHealthy, isMismatchId }) => !isHealthy || isMismatchId, - ); + const hasOutdatedOperator = health.data?.some(({ isOutdated }) => isOutdated); + const isAllOperatorsAreOutdated = + health.data?.every(({ isOutdated }) => isOutdated) || false; + const hasUnhealthyOperators = health.data?.some( + ({ isHealthy, isMismatchId }) => !isHealthy || isMismatchId, + ); + const hasIssuedOperator = hasOutdatedOperator + ? hasUnhealthyOperators || !isAllOperatorsAreOutdated + : hasUnhealthyOperators; return ( @@ -73,7 +76,7 @@ export const DistributeOffline: FC = () => { isLoading={health.isLoading} /> - {selectedOption === "new" && hasUnhealthyOperators && ( + {isNew && hasIssuedOperator && ( <> { )} )} - {selectedOption === "new" && - !hasUnhealthyOperators && - health.isSuccess && ( - - )} + {selectedOption === "new" && !hasIssuedOperator && health.isSuccess && ( + + )} {selectedOption === "existing" && ( )} diff --git a/src/app/routes/protected-cluster-route.tsx b/src/app/routes/protected-cluster-route.tsx index bae08acd2..8f27adc85 100644 --- a/src/app/routes/protected-cluster-route.tsx +++ b/src/app/routes/protected-cluster-route.tsx @@ -18,11 +18,11 @@ export const ProtectedClusterRoute: FC> = ({ const location = useLocation(); const currentPath = location.pathname; const [enabled] = useLocalStorage("reshareFlowEnabled", false); - if (isUndefined(clusterHash)) return ; if (cluster.isLoading) return ; if (cluster.isError || !cluster.data) return ; - if (isFrom("/clusters/:clusterHash")) resetState(); + if (isFrom("/clusters/:clusterHash") && currentPath.includes("reshare")) + resetState(); if (!enabled && currentPath.includes("reshare")) { return ; } diff --git a/src/app/routes/reshare-dkg/ceremony-section.tsx b/src/app/routes/reshare-dkg/ceremony-section.tsx index d1fe6f84f..d903cb093 100644 --- a/src/app/routes/reshare-dkg/ceremony-section.tsx +++ b/src/app/routes/reshare-dkg/ceremony-section.tsx @@ -6,10 +6,7 @@ import { LuCheck, LuCopy } from "react-icons/lu"; import CeremonySummary from "@/app/routes/create-cluster/ceremony-summary.tsx"; import { useQuery } from "@tanstack/react-query"; import { stringifyBigints } from "@/lib/utils/bigint.ts"; -import { - DKG_VERSIONS, - generateSSVKeysDockerCMD, -} from "@/lib/utils/keyshares.ts"; +import { generateSSVKeysDockerCMD } from "@/lib/utils/keyshares.ts"; import type { Address } from "viem"; import { useAccount } from "@/hooks/account/use-account.ts"; import { useSSVAccount } from "@/hooks/use-ssv-account.ts"; @@ -17,7 +14,6 @@ import { useBulkActionContext } from "@/guard/bulk-action-guard.tsx"; import { useReshareDkg } from "@/hooks/use-reshare-dkg.ts"; import { useCopyToClipboard } from "react-use"; import { CompletedBadge } from "@/components/ui/completed-badge.tsx"; -import { useOperatorsDKGHealth } from "@/hooks/operator/use-operator-dkg-health.ts"; const VALIDATOR_COUNT_THRESHOLD = 20; @@ -45,16 +41,6 @@ const CeremonySection = ({ const context = useBulkActionContext(); const reshareContext = useReshareDkg(); const [copyState, copy] = useCopyToClipboard(); - const health = useOperatorsDKGHealth( - context.dkgReshareState.newOperators.length - ? context.dkgReshareState.newOperators - : context.dkgReshareState.operators, - ); - const version = health.data?.every( - ({ isHealthy, isOutdated }) => isHealthy && isOutdated, - ) - ? DKG_VERSIONS.OLD - : DKG_VERSIONS.NEW; const cmd = useQuery({ queryKey: stringifyBigints([ @@ -85,7 +71,6 @@ const CeremonySection = ({ signatures, os: context.dkgReshareState.selectedOs, proofsString, - version, }), ); }, diff --git a/src/app/routes/reshare-dkg/reshare.tsx b/src/app/routes/reshare-dkg/reshare.tsx index 7c0f291ed..bf94fd9ef 100644 --- a/src/app/routes/reshare-dkg/reshare.tsx +++ b/src/app/routes/reshare-dkg/reshare.tsx @@ -23,13 +23,10 @@ const Reshare = () => { ? health.data?.every( ({ isHealthy, isEthClientConnected, isOutdated, isMismatchId }) => isHealthy && isEthClientConnected && !isOutdated && !isMismatchId, - ) || - health.data?.every( - ({ isHealthy, isOutdated, isMismatchId }) => - isHealthy && isOutdated && !isMismatchId, ) : health.data?.every( - ({ isHealthy, isMismatchId }) => isHealthy && !isMismatchId, + ({ isHealthy, isMismatchId, isOutdated }) => + isHealthy && !isMismatchId && !isOutdated, ); if (health.isLoading) { diff --git a/src/components/offline-generation/docker-instructions.tsx b/src/components/offline-generation/docker-instructions.tsx index 2a56fd410..8882b94e9 100644 --- a/src/components/offline-generation/docker-instructions.tsx +++ b/src/components/offline-generation/docker-instructions.tsx @@ -2,7 +2,7 @@ import { type FC } from "react"; import { Button } from "@/components/ui/button"; import { Text } from "@/components/ui/text"; import { Spinner } from "@/components/ui/spinner"; -import { generateSSVKeysDockerCMD } from "@/lib/utils/keyshares"; +import { DKG_VERSIONS, generateSSVKeysDockerCMD } from "@/lib/utils/keyshares"; import type { Operator } from "@/types/api"; import { LuCheck, LuCopy } from "react-icons/lu"; import { useRegisterValidatorContext } from "@/guard/register-validator-guard"; @@ -29,6 +29,7 @@ import { useNavigate } from "react-router"; interface DockerInstructionsProps { operators: Operator[]; + isOutdatedOperators: boolean; } const schema = z.object({ @@ -45,6 +46,7 @@ const schema = z.object({ export const DockerInstructions: FC = ({ operators, + isOutdatedOperators, }) => { const navigate = useNavigate(); @@ -71,7 +73,7 @@ export const DockerInstructions: FC = ({ isAddress(withdrawalAddress) && validators > 0 && hasConfirmed; - + const cliVersion = isOutdatedOperators ? DKG_VERSIONS.OLD : DKG_VERSIONS.NEW; const cmd = useQuery({ queryKey: stringifyBigints([ "docker-cmd", @@ -79,6 +81,7 @@ export const DockerInstructions: FC = ({ account.address, validators, withdrawalAddress, + cliVersion, dkgCeremonyState.selectedOs, account.chainId, ]), @@ -90,6 +93,7 @@ export const DockerInstructions: FC = ({ account: account.address!, withdrawalAddress: withdrawalAddress as Address, chainId: account.chainId, + version: cliVersion, validatorsCount: validators, os: dkgCeremonyState.selectedOs, }), diff --git a/src/components/offline-generation/unhealthy-operators-list.tsx b/src/components/offline-generation/unhealthy-operators-list.tsx index fc238dae4..832a5a9ed 100644 --- a/src/components/offline-generation/unhealthy-operators-list.tsx +++ b/src/components/offline-generation/unhealthy-operators-list.tsx @@ -25,13 +25,13 @@ const getBadgeInfo = ( text: "ID/IP Mismatch", }; } + if (healthData.isOutdated) { + return { + variant: "warning" as BadgeVariants["variant"], + text: "Dkg Outdated", + }; + } if (isReshareFlow) { - if (healthData.isOutdated) { - return { - variant: "warning" as BadgeVariants["variant"], - text: "Dkg Outdated", - }; - } if (!healthData.isEthClientConnected) { return { variant: "error" as BadgeVariants["variant"], diff --git a/src/hooks/operator/use-reshare-signature-payload.ts b/src/hooks/operator/use-reshare-signature-payload.ts index 4cf621c8c..fee4f8490 100644 --- a/src/hooks/operator/use-reshare-signature-payload.ts +++ b/src/hooks/operator/use-reshare-signature-payload.ts @@ -25,20 +25,25 @@ export const useReshareSignaturePayload = ({ const nonce = await getOwnerNonce(ownerAddress); const chainId = FORKS[getChainId(config)]; const payload = (proofsQuery.data?.validators || []).map( - ({ publicKey, proofs }) => ({ - messageData: { + ({ publicKey, proofs }, index: number) => { + const messageData: MessageData = { publicKey, oldOperators: context.dkgReshareState.operators, chainId, withdrawalCredentials: withdrawAddress, ownerAddress, - nonce, + nonce: nonce + index, amount: DEFAULT_AMOUNT, - } as MessageData, - proofs: proofs, - }), + }; + if (context.dkgReshareState.newOperators.length) { + messageData.newOperators = context.dkgReshareState.newOperators; + } + return { + messageData, + proofs, + }; + }, ); - return sign.signMessageAsync({ message: getSignaturePayload(payload), }); diff --git a/src/hooks/use-validate-proofs.ts b/src/hooks/use-validate-proofs.ts index 01ce7115e..9c112e971 100644 --- a/src/hooks/use-validate-proofs.ts +++ b/src/hooks/use-validate-proofs.ts @@ -122,7 +122,14 @@ export const useValidateProofs = (files: File[]) => { ); } state.dkgReshareState.selectedValidatorsCount = validators.length; - + if ( + json.every((proof: Proof | Proof[]) => Array.isArray(proof)) && + json.length > validators.length + ) { + throw new Error( + "proofs.json must only contain validators that are registered.", + ); + } return { proofs: json, validators, diff --git a/src/lib/utils/dkg.ts b/src/lib/utils/dkg.ts index cfe7f8449..ba290be15 100644 --- a/src/lib/utils/dkg.ts +++ b/src/lib/utils/dkg.ts @@ -118,8 +118,8 @@ const serializedResignMessage = (message: { const serializedReshareMessage = (message: { messageData: MessageData; proofs: ProofType[]; -}) => - ReshareMessageType.serialize({ +}) => { + return ReshareMessageType.serialize({ Reshare: { ValidatorPubKey: parseHexToBuffer(message.messageData.publicKey), OldOperators: message.messageData.oldOperators.map( @@ -134,8 +134,8 @@ const serializedReshareMessage = (message: { PubKey: Buffer.from(op.public_key), }), ), - OldT: message.messageData.oldOperators.length % 3, - NewT: (message.messageData.newOperators || []).length % 3, + OldT: Number(message.messageData.oldOperators.length % 3), + NewT: Number((message.messageData.newOperators || []).length % 3), Fork: parseHexToBuffer(message.messageData.chainId).slice(0, 4), WithdrawalCredentials: parseHexToBuffer( message.messageData.withdrawalCredentials, @@ -154,6 +154,7 @@ const serializedReshareMessage = (message: { Signature: parseHexToBuffer(proof.signature), })), }); +}; export const getSignaturePayload = ( data: { diff --git a/src/lib/utils/keyshares.ts b/src/lib/utils/keyshares.ts index 155b0d540..9f1a2dea6 100644 --- a/src/lib/utils/keyshares.ts +++ b/src/lib/utils/keyshares.ts @@ -132,7 +132,7 @@ export const generateSSVKeysDockerCMD = ({ chainId = getChainId(config), validatorsCount = 1, os = getOSName(), - version = DKG_VERSIONS.OLD, + version = DKG_VERSIONS.NEW, newOperators, signatures, proofsString, @@ -166,7 +166,7 @@ export const generateSSVKeysDockerCMD = ({ .map((op) => op.id) .join(",")}` : "" - } --withdrawAddress ${withdrawalAddress} --owner ${account} --nonce ${nonce} --network ${chainName} ${proofsString ? `--proofsString ${proofsString}` : "--proofsFilePath /data/proofs.json"} --operatorsInfo ${newOperators ? getOperatorsData([...operators, ...newOperators]) : getOperatorsData(operators)} --signatures ${signatures} --outputPath /output --logLevel info --logFormat json --logLevelFormat capitalColor --logFilePath /data/debug.log --tlsInsecure`; + } --withdrawAddress ${withdrawalAddress} --owner ${account} --nonce ${nonce} --network ${chainName} ${proofsString ? `--proofsString '${proofsString}'` : "--proofsFilePath ./data/proofs.json"} --operatorsInfo ${newOperators ? getOperatorsData([...operators, ...newOperators]) : getOperatorsData(operators)} --signatures ${signatures} --outputPath ./output --logLevel info --logFormat json --logLevelFormat capitalColor --logFilePath ./data/debug.log --tlsInsecure`; } - return `docker pull bloxstaking/ssv-dkg:v2.1.0 && docker run --rm -v ${dynamicFullPath}:/data -it "bloxstaking/ssv-dkg:v${version}" init --owner ${account} --nonce ${nonce} --withdrawAddress ${withdrawalAddress} --operatorIDs ${operatorIds} --operatorsInfo ${getOperatorsData(sortedOperators)} --network ${chainName} --validators ${validatorsCount} --logFilePath /data/debug.log --outputPath /data`; + return `docker pull bloxstaking/ssv-dkg:v${version} && docker run --rm -v ${dynamicFullPath}:/data -it "bloxstaking/ssv-dkg:v${version}" init --owner ${account} --nonce ${nonce} --withdrawAddress ${withdrawalAddress} --operatorIDs ${operatorIds} --operatorsInfo ${getOperatorsData(sortedOperators)} --network ${chainName} --validators ${validatorsCount} ${version === DKG_VERSIONS.OLD ? "--logFilePath /data/debug.log --outputPath /data" : "--logFilePath ./data/debug.log --outputPath ./data --tlsInsecure"}}`; };