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

[VAULTS][POC] a very raw bug-ridden proof-of-concept for PredepositGuardian #932

Draft
wants to merge 8 commits into
base: vault-guardian
Choose a base branch
from

Conversation

failingtwice
Copy link
Contributor

THIS IS A POC, NOT READY FOR PRODUCTION, PRACTICALLY PSEUDOCODE

Comment on lines 71 to 93
function withdrawAsVaultOwner(address stakingVault, bytes[] calldata validatorPubkeys) external {
if (msg.sender != StakingVault(payable(stakingVault)).owner()) revert MustBeVaultOwner();

for (uint256 i = 0; i < validatorPubkeys.length; i++) {
bytes32 validatorPubkeyHash = keccak256(validatorPubkeys[i]);

if (validatorStatuses[validatorPubkeyHash] != ValidatorStatus.RESOLVED) {
revert MustBeResolvedValidatorPubkey();
}

if (validatorStatuses[validatorPubkeyHash] == ValidatorStatus.WITHDRAWN) {
revert ValidatorAlreadyWithdrawn();
}

if (wcRecords[validatorPubkeyHash] == StakingVault(payable(stakingVault)).withdrawalCredentials()) {
revert ValidatorWithdrawalCredentialsMatchVaultWithdrawalCredentials();
}

msg.sender.call{value: 1 ether}("");

validatorStatuses[validatorPubkeyHash] = ValidatorStatus.WITHDRAWN;
}
}
Comment on lines 71 to 93
function withdrawAsVaultOwner(address stakingVault, bytes[] calldata validatorPubkeys) external {
if (msg.sender != StakingVault(payable(stakingVault)).owner()) revert MustBeVaultOwner();

for (uint256 i = 0; i < validatorPubkeys.length; i++) {
bytes32 validatorPubkeyHash = keccak256(validatorPubkeys[i]);

if (validatorStatuses[validatorPubkeyHash] != ValidatorStatus.RESOLVED) {
revert MustBeResolvedValidatorPubkey();
}

if (validatorStatuses[validatorPubkeyHash] == ValidatorStatus.WITHDRAWN) {
revert ValidatorAlreadyWithdrawn();
}

if (wcRecords[validatorPubkeyHash] == StakingVault(payable(stakingVault)).withdrawalCredentials()) {
revert ValidatorWithdrawalCredentialsMatchVaultWithdrawalCredentials();
}

msg.sender.call{value: 1 ether}("");

validatorStatuses[validatorPubkeyHash] = ValidatorStatus.WITHDRAWN;
}
}

Check warning

Code scanning / Slither

Unchecked low-level calls Medium

Comment on lines 95 to 115
function withdrawAsNodeOperator(bytes[] calldata validatorPubkeys) external {
for (uint256 i = 0; i < validatorPubkeys.length; i++) {
bytes32 validatorPubkeyHash = keccak256(validatorPubkeys[i]);

if (validatorStatuses[validatorPubkeyHash] != ValidatorStatus.RESOLVED) {
revert MustBeResolvedValidatorPubkey();
}

if (validatorStatuses[validatorPubkeyHash] == ValidatorStatus.WITHDRAWN) {
revert ValidatorAlreadyWithdrawn();
}

if (nodeOperatorToValidators[msg.sender] != validatorPubkeyHash) {
revert ValidatorMustBelongToSender();
}

msg.sender.call{value: 1 ether}("");

validatorStatuses[validatorPubkeyHash] = ValidatorStatus.WITHDRAWN;
}
}
Comment on lines 95 to 115
function withdrawAsNodeOperator(bytes[] calldata validatorPubkeys) external {
for (uint256 i = 0; i < validatorPubkeys.length; i++) {
bytes32 validatorPubkeyHash = keccak256(validatorPubkeys[i]);

if (validatorStatuses[validatorPubkeyHash] != ValidatorStatus.RESOLVED) {
revert MustBeResolvedValidatorPubkey();
}

if (validatorStatuses[validatorPubkeyHash] == ValidatorStatus.WITHDRAWN) {
revert ValidatorAlreadyWithdrawn();
}

if (nodeOperatorToValidators[msg.sender] != validatorPubkeyHash) {
revert ValidatorMustBelongToSender();
}

msg.sender.call{value: 1 ether}("");

validatorStatuses[validatorPubkeyHash] = ValidatorStatus.WITHDRAWN;
}
}

Check warning

Code scanning / Slither

Unchecked low-level calls Medium

@tamtamchik tamtamchik added solidity issues/tasks related to smart contract code vaults labels Jan 29, 2025
Copy link
Contributor

@TheDZhon TheDZhon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

bytes32 _expectedGlobalDepositRoot,
bytes calldata _signature
) external {
function depositToBeaconChain(Deposit[] calldata _deposits) external {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strawman sanity checks would be needed for the input data

@@ -68,6 +68,7 @@ contract StakingVault is IStakingVault, OwnableUpgradeable {
uint128 locked;
int128 inOutDelta;
address nodeOperator;
// depositGuardian becomes the depositor, instead of just guardian, perhaps a renaming is needed 🌚
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make depositGuardian immutable


// TODO: think about naming. It's not a deposit guardian, it's the depositor itself
// TODO: minor UX improvement: perhaps there's way to reuse predeposits for a different validator without withdrawing
contract PredepositGuardian {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
contract PredepositGuardian {
contract PredepositGuarantee {

Comment on lines 14 to 15
mapping(bytes32 validatorId => bool isPreDeposited) public validatorPredeposits;
mapping(bytes32 validatorId => bytes32 withdrawalCredentials) public validatorWithdrawalCredentials;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mapping(bytes32 validatorId => bool isPreDeposited) public validatorPredeposits;
mapping(bytes32 validatorId => bytes32 withdrawalCredentials) public validatorWithdrawalCredentials;
mapping(address nodeOperator => uint256) public nodeOperatorGuarantee;
mapping(bytes validatorPubkey => bytes32 withdrawalCredentials) public validatorWithdrawalCredentials;

mapping(bytes32 validatorId => bytes32 withdrawalCredentials) public validatorWithdrawalCredentials;

// Question: predeposit is permissionless, i.e. the msg.sender doesn't have to be the node operator,
// however, the deposit will still revert if it wasn't signed with the validator private key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't get this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function can't be permissionless

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • let's include accounting and 'outsourcing' for funding


// called by the staking vault owner if the predeposited validator has a different withdrawal credentials than the vault's withdrawal credentials,
// i.e. node operator was malicious
function withdrawDisprovenPredeposits(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what should be done here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's implement optionality in Dashboard (either fund stVault or withdraw to the outer address)

// set flag to false to prevent double withdrawal
validatorPredeposits[validatorId] = false;

(bool success, ) = _recipient.call{value: 1 ether}("");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ maybe prevent direct calls to my stVault here (to not lose funds)

for (uint256 i = 0; i < validatorsLength; i++) {
bytes32 validatorId = _validatorIds[i];

if (msg.sender != _stakingVault.nodeOperator()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why inside for loop?

stakingVault.depositToBeaconChain(deposits);
}

function proveValidatorWithdrawalCredentials(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should combine prove + deposit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
solidity issues/tasks related to smart contract code vaults
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants