Skip to content

Commit c535318

Browse files
committed
fix(PDG): don't allow proving without activation by default
1 parent 4b96094 commit c535318

File tree

3 files changed

+40
-24
lines changed

3 files changed

+40
-24
lines changed

contracts/0.8.25/vaults/LazyOracle.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ contract LazyOracle is ILazyOracle, AccessControlEnumerableUpgradeable {
249249
return batch;
250250
}
251251

252+
/**
253+
* @notice batch method to mass check the validator stages in PredepositGuarantee contract
254+
* @param _pubkeys the array of validator's pubkeys to check
255+
*/
252256
function batchValidatorStages(
253257
bytes[] calldata _pubkeys
254258
) external view returns (IPredepositGuarantee.ValidatorStage[] memory batch) {

contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
199199
}
200200

201201
/**
202-
* @notice returns the current amount of ether that is predeposited to a given vault
202+
* @notice returns the number of validators in PREDEPOSITED and PROVEN states but not ACTIVATED yet
203203
* @param _vault staking vault address
204-
* @return amount of ether in wei
204+
* @return the number of validators yet-to-be-activated
205205
*/
206206
function pendingActivations(IStakingVault _vault) external view returns (uint256) {
207207
return _storage().pendingActivations[address(_vault)];
@@ -420,12 +420,13 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
420420
}
421421

422422
/**
423-
* @notice permissionless method to prove correct Withdrawal Credentials for the validator
423+
* @notice permissionless method to prove correct Withdrawal Credentials and activate validator if possible
424424
* @param _witness object containing validator pubkey, Merkle proof and timestamp for Beacon Block root child block
425425
* @dev will revert if proof is invalid or misformed or validator is not predeposited
426-
* @dev transition PREDEPOSITED => PROVEN
426+
* @dev transition PREDEPOSITED => PROVEN [=> ACTIVATED]
427+
* @dev if activation is impossible, it can be done later by calling activateValidator() explicitly
427428
*/
428-
function proveWC(ValidatorWitness calldata _witness) external whenResumed {
429+
function proveWCAndActivate(ValidatorWitness calldata _witness) external whenResumed {
429430
ValidatorStatus storage validator = _storage().validatorStatus[_witness.pubkey];
430431

431432
if (validator.stage != ValidatorStage.PREDEPOSITED) {
@@ -434,9 +435,19 @@ contract PredepositGuarantee is IPredepositGuarantee, CLProofVerifier, PausableU
434435

435436
IStakingVault stakingVault = validator.stakingVault;
436437
bytes32 withdrawalCredentials = _checkVaultWC(stakingVault);
438+
address nodeOperator = validator.nodeOperator;
439+
440+
_proveWC(_witness, stakingVault, withdrawalCredentials, nodeOperator);
437441

438-
validator.stage = ValidatorStage.PROVEN;
439-
_proveWC(_witness, stakingVault, withdrawalCredentials, validator.nodeOperator);
442+
// activate validator if possible
443+
if (stakingVault.depositor() == address(this) && stakingVault.stagedBalance() >= ACTIVATION_DEPOSIT_AMOUNT) {
444+
_activateValidator(_witness.pubkey, 0, stakingVault, withdrawalCredentials, nodeOperator);
445+
validator.stage = ValidatorStage.ACTIVATED;
446+
} else {
447+
// only if validator is disconnected
448+
// because on connection we check depositor and staged balance
449+
validator.stage = ValidatorStage.PROVEN;
450+
}
440451
}
441452

442453
/**

test/0.8.25/vaults/predepositGuarantee/predepositGuarantee.test.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -851,27 +851,29 @@ describe("PredepositGuarantee.sol", () => {
851851
};
852852

853853
// stage NONE
854-
await expect(pdg.proveWC(witness))
854+
await expect(pdg.proveWCAndActivate(witness))
855855
.to.be.revertedWithCustomError(pdg, "ValidatorNotPreDeposited")
856856
.withArgs(validator.container.pubkey, 0n);
857857

858858
// stage PREDEPOSITED
859859
const { deposit, depositY } = await generatePredeposit(validator);
860860
await pdg.predeposit(stakingVault, [deposit], [depositY]);
861861

862-
const proveTx = await pdg.proveWC(witness);
862+
const proveTx = await pdg.proveWCAndActivate(witness);
863863
await expect(proveTx)
864864
.to.emit(pdg, "BalanceUnlocked")
865865
.withArgs(vaultOperator.address, balance, 0)
866866
.to.emit(pdg, "ValidatorProven")
867+
.withArgs(validator.container.pubkey, vaultOperator.address, await stakingVault.getAddress(), wc)
868+
.to.emit(pdg, "ValidatorActivated")
867869
.withArgs(validator.container.pubkey, vaultOperator.address, await stakingVault.getAddress(), wc);
868870

869-
expect((await pdg.validatorStatus(validator.container.pubkey)).stage).to.equal(2n);
871+
expect((await pdg.validatorStatus(validator.container.pubkey)).stage).to.equal(3n); // 3n is ACTIVATED
870872

871-
// stage PROVEN
872-
await expect(pdg.proveWC(witness))
873+
// stage ACTIVATED
874+
await expect(pdg.proveWCAndActivate(witness))
873875
.to.be.revertedWithCustomError(pdg, "ValidatorNotPreDeposited")
874-
.withArgs(validator.container.pubkey, 2n);
876+
.withArgs(validator.container.pubkey, 3n); // 3n is ACTIVATED
875877
});
876878

877879
it("allows NO to proveValidatorWC", async () => {
@@ -918,16 +920,18 @@ describe("PredepositGuarantee.sol", () => {
918920
proposerIndex: beaconBlockHeader.proposerIndex,
919921
};
920922

921-
const proveValidatorWCTX = pdg.connect(vaultOwner).proveWC(witness);
923+
const proveValidatorWCTX = pdg.connect(vaultOwner).proveWCAndActivate(witness);
922924

923925
await expect(proveValidatorWCTX)
924926
.to.emit(pdg, "BalanceUnlocked")
925927
.withArgs(vaultOperator, ether("1"), ether("0"))
926928
.to.emit(pdg, "ValidatorProven")
929+
.withArgs(validator.container.pubkey, vaultOperator, stakingVault, vaultWC)
930+
.to.emit(pdg, "ValidatorActivated")
927931
.withArgs(validator.container.pubkey, vaultOperator, stakingVault, vaultWC);
928932

929933
const validatorStatus = await pdg.validatorStatus(validator.container.pubkey);
930-
expect(validatorStatus.stage).to.equal(2n);
934+
expect(validatorStatus.stage).to.equal(3n); // 3n is ACTIVATED
931935
expect(validatorStatus.stakingVault).to.equal(stakingVault);
932936
expect(validatorStatus.nodeOperator).to.equal(vaultOperator);
933937
});
@@ -1017,35 +1021,32 @@ describe("PredepositGuarantee.sol", () => {
10171021
const sameNoProof = [...sameNoValidatorProof.proof, ...sameNoStateProof, ...beaconProof];
10181022

10191023
// prove
1020-
await pdg.proveWC({
1024+
await pdg.proveWCAndActivate({
10211025
proof: mainProof,
10221026
pubkey: mainValidator.container.pubkey,
10231027
validatorIndex: mainValidatorIndex,
10241028
childBlockTimestamp: childBlockTimestamp,
10251029
slot: beaconHeader.slot,
10261030
proposerIndex: beaconHeader.proposerIndex,
10271031
});
1028-
await pdg.activateValidator(mainValidator.container.pubkey);
10291032

1030-
await pdg.proveWC({
1033+
await pdg.proveWCAndActivate({
10311034
proof: sideProof,
10321035
pubkey: sideValidator.container.pubkey,
10331036
validatorIndex: sideValidatorIndex,
10341037
childBlockTimestamp: childBlockTimestamp,
10351038
slot: beaconHeader.slot,
10361039
proposerIndex: beaconHeader.proposerIndex,
10371040
});
1038-
await pdg.activateValidator(sideValidator.container.pubkey);
10391041

1040-
await pdg.proveWC({
1042+
await pdg.proveWCAndActivate({
10411043
proof: sameNoProof,
10421044
pubkey: sameNOValidator.container.pubkey,
10431045
validatorIndex: sameNoValidatorIndex,
10441046
childBlockTimestamp: childBlockTimestamp,
10451047
slot: beaconHeader.slot,
10461048
proposerIndex: beaconHeader.proposerIndex,
10471049
});
1048-
await pdg.activateValidator(sameNOValidator.container.pubkey);
10491050

10501051
expect((await pdg.validatorStatus(mainValidator.container.pubkey)).stage).to.equal(3n); // 3n is ACTIVATED
10511052
expect((await pdg.validatorStatus(sideValidator.container.pubkey)).stage).to.equal(3n); // 3n is ACTIVATED
@@ -1233,10 +1234,10 @@ describe("PredepositGuarantee.sol", () => {
12331234
).to.revertedWithCustomError(pdg, "WithdrawalCredentialsMatch");
12341235

12351236
// proving
1236-
await pdg.proveWC(validNotPredepostedValidatorWitness);
1237+
await pdg.proveWCAndActivate(validNotPredepostedValidatorWitness);
12371238
await expect(pdg.connect(vaultOperator).proveInvalidValidatorWC(validNotPredepostedValidatorWitness, validWC))
12381239
.to.revertedWithCustomError(pdg, "ValidatorNotPreDeposited")
1239-
.withArgs(validNotPredepostedValidator.container.pubkey, 2n);
1240+
.withArgs(validNotPredepostedValidator.container.pubkey, 3n); // 3n is ACTIVATED
12401241
});
12411242

12421243
it("reverts when trying to prove valid validator", async () => {
@@ -1413,7 +1414,7 @@ describe("PredepositGuarantee.sol", () => {
14131414
};
14141415

14151416
await expect(pdg.predeposit(stakingVault, [], [])).to.revertedWithCustomError(pdg, "ResumedExpected");
1416-
await expect(pdg.proveWC(witness)).to.revertedWithCustomError(pdg, "ResumedExpected");
1417+
await expect(pdg.proveWCAndActivate(witness)).to.revertedWithCustomError(pdg, "ResumedExpected");
14171418
await expect(pdg.activateValidator(witness.pubkey)).to.revertedWithCustomError(pdg, "ResumedExpected");
14181419
await expect(pdg.topUpExistingValidators([])).to.revertedWithCustomError(pdg, "ResumedExpected");
14191420
await expect(pdg.proveWCActivateAndTopUpValidators([], [])).to.revertedWithCustomError(pdg, "ResumedExpected");

0 commit comments

Comments
 (0)