sherlock-audit / 2024-05-aleo-judging

0 stars 0 forks source link

morbsel - Validators that are not in the committee can't unbond delegators that are pre bonded to them #26

Closed sherlock-admin4 closed 1 week ago

sherlock-admin4 commented 2 weeks ago

morbsel

Medium

Validators that are not in the committee can't unbond delegators that are pre bonded to them

Summary

Validators that are not in the committee can't unbond delegators that are pre bonded to them because the caller authorization in the unbond_public function will always be false due validators that aren't in the committee yet won't have a withdraw address set.

Vulnerability Detail

In the unbond_public function the caller is allowed to unbond for the staker when: r7: caller == staker's withdraw address. OR r11: caller == validator's withdraw address(r10) AND validator should have set the withdraw address(r8).

The problem lies in that the validator's withdraw address is only set when he calls the bond_validator function, has 10mil or more credits delegated to him and he is a new validator in the committee. That process is valid but it results in if the validator never was in the committee(like not having 10mil credits delegated to him) that he doesn't have a withdraw address set.

So the validator that isn't in the committee but has pre bonded stakers can't unbond them because in the unbond_public function: r7 will be false if the withdraw address of the staker isnt the validator that is calling. AND r11 will be false because the validator will never have set a withdraw address(r8).

Both conditions being false results in a revert:

// Either the caller is the staker's withdrawal address OR the validator's withdrawal address is set to the caller.
or r7 r11 into r12;
// Enforce the permission as `true`.
assert.eq r12 true;

Impact

The intended functionality that allows a validator to unbond delegators even when the validator is not in the committee does not work.

Code Snippet

Caller authorization in unbond_public function: https://github.com/sherlock-audit/2024-05-aleo/blob/55b2e4a02f27602a54c11f964f6f610fee6f4ab8/snarkVM/synthesizer/program/src/resources/credits.aleo#L448-L470

Setting withdraw address for new validators in bond_validator function: https://github.com/sherlock-audit/2024-05-aleo/blob/55b2e4a02f27602a54c11f964f6f610fee6f4ab8/snarkVM/synthesizer/program/src/resources/credits.aleo#L245-L251

Stated that a validator should be able to unbond delegators even when the validator is not in the committee:

// Check if the validator's withdrawal address has been set.
// Note: This contains check must use `withdraw` and **not** `committee` to ensure the validator
// can unbond delegators even when the validator is not in the committee. Of course, if the validator is
// in the committee, then the validator must be able to unbond its delegators.

at https://github.com/sherlock-audit/2024-05-aleo/blob/55b2e4a02f27602a54c11f964f6f610fee6f4ab8/snarkVM/synthesizer/program/src/resources/credits.aleo#L455-L458

and:

// Validators are permitted to fully unbond any of their delegators. When a validator unbonds a delegator,
// the entire bonded balance is unbonded, regardless of the amount of microcredits and may end up removing the validator
// from the committee if the total delegated balance falls below 10M credits.

at https://github.com/sherlock-audit/2024-05-aleo/blob/55b2e4a02f27602a54c11f964f6f610fee6f4ab8/snarkVM/synthesizer/program/src/resources/credits.aleo#L415-L417

Tool used

Manual Review

Recommendation

Possibilities:

  1. Adjust the authorization logic in the unbond_public function to allow the caller to unbond if they are the validator's address.
  2. Implement a set_withdraw_address function to allow validators/users to set/change a withdraw address

Duplicate of #20