---
# System prepended metadata

title: Voluntary Exit (research)
tags: [exit, ' research']

---

---
tags: exit, research
---

# Voluntary Exit (research) 

## CLI


````
bazel run //validator:validator -- accounts voluntary-exit --help

NAME:
   validator accounts voluntary-exit - 

USAGE:
   validator accounts voluntary-exit [command options] [arguments...]

DESCRIPTION:
   Performs a voluntary exit on selected accounts

OPTIONS:
   --wallet-dir value             Path to a wallet directory on-disk for Prysm validator accounts (default: "/home/mezin/.eth2validators/prysm-wallet-v2")
   --wallet-password-file value   Path to a plain-text, .txt file containing your wallet password
   --account-password-file value  Path to a plain-text, .txt file containing a password for a validator account
   --public-keys value            Comma-separated list of public key hex strings to specify on which validator accounts to perform a voluntary exit
   --beacon-rpc-provider value    Beacon node RPC provider endpoint (default: "127.0.0.1:4000")
   --grpc-max-msg-size value      Integer to define max recieve message call size (default: 15900000)
   --tls-cert value               Certificate for secure gRPC. Pass this and the tls-key flag in order to use gRPC securely.
   --grpc-headers value           A comma separated list of key value pairs to pass as gRPC headers for all gRPC calls. Example: --grpc-headers=key=value
   --grpc-retries value           Number of attempts to retry gRPC requests (default: 5)
   --grpc-retry-delay value       The amount of time between gRPC retry requests. (default: 1s)
   --exit-all                     Exit all validators. This will still require the staker to confirm a userprompt for the action (default: false)
   --mainnet                      Run on Ethereum Beacon Chain Main Net. This is the default and can be omitted. (default: true)
   --pyrmont                      This defines the flag through which we can run on the Pyrmont Multiclient Testnet (default: false)
   --prater                       Run Prysm configured for the Prater test network (default: false)
   --accept-terms-of-use          Accept Terms and Conditions (for non-interactive environments) (default: false)
   --help, -h                     show help (default: false)
````


````
bazel run //validator:validator -- accounts voluntary-exit --wallet-dir=/home/mezin/go/src/tesseract/.data/node-0/wallet-dir-memo --wallet-password-file=/home/mezin/go/src/tesseract/.data/node-0/password.txt

[2023-01-31 20:31:57]  WARN flags: Running on Ethereum Consensus Mainnet
[Selected account] 0 | previously-distinct-sparrow | 0x8d633e8189d3
Done with selections
Are you sure you want to perform a voluntary exit on 1 account? (0x8d633e8189d3) Y/N:
y
===============IMPORTANT===============
Withdrawing funds is not possible in Phase 0 of the system. Please navigate to the following website and make sure you understand the current implications of a voluntary exit before making the final decision:
https://docs.prylabs.network/docs/wallet/exiting-a-validator/#withdrawal-delay-warning
If you still want to continue with the voluntary exit, please input a phrase found at the end of the page from the above URL:
Exit my validator
[2023-01-31 21:11:35]  WARN validator: You are using an insecure gRPC connection. If you are running your beacon node and validator on the same machines, you can ignore this message. If you want to know how to enable secure connections, see: https://docs.prylabs.network/docs/prysm-usage/secure-grpc
[2023-01-31 21:11:35]  WARN accounts: Could not perform voluntary exit for account 0x8c2fc2bdf1a5: failed to propose voluntary exit: rpc error: code = InvalidArgument desc = validator has not been active long enough to exit: 4 of 256 epochs. Validator will be eligible for exit at epoch 256
[2023-01-31 21:11:35]  INFO accounts: No successful voluntary exits
````

### success
И после перенастройки конфига успешно отработало

````go
ShardCommitteePeriod    types.Epoch `yaml:"SHARD_COMMITTEE_PERIOD" spec:"true"`              
// ShardCommitteePeriod is the minimum amount of 
// epochs a validator must participate before exiting.
````

````
/home/mezin/go/src/tesseract/config/params
mainnet_config.go
	ShardCommitteePeriod:             256,
````

````
[2023-02-01 14:41:36]  WARN flags: Running on Ethereum Consensus Mainnet
[Selected account] 0 | sincerely-loving-chigger | 0x8c2fc2bdf1a5
[Selected account] 0 | sincerely-loving-chigger | 0x8c2fc2bdf1a5
Done with selections
Are you sure you want to perform a voluntary exit on 1 account? (0x8c2fc2bdf1a5) Y/N:
y
===============IMPORTANT===============
Withdrawing funds is not possible in Phase 0 of the system. Please navigate to the following website and make sure you understand the current implications of a voluntary exit before making the final decision:
https://docs.prylabs.network/docs/wallet/exiting-a-validator/#withdrawal-delay-warning
If you still want to continue with the voluntary exit, please input a phrase found at the end of the page from the above URL:
Exit my validator
[2023-02-01 14:58:50]  WARN validator: You are using an insecure gRPC connection. If you are running your beacon node and validator on the same machines, you can ignore this message. If you want to know how to enable secure connections, see: https://docs.prylabs.network/docs/prysm-usage/secure-grpc
[2023-02-01 14:58:50]  INFO accounts: Voluntary exit was successful for the accounts listed. URLs where you can track each validator's exit:
https://beaconcha.in/validator/8c2fc2bdf1a5b519abf28c1297982d7f22c6a3506cb28ebcc4f9ffc3091348aa5260f2db34a4b68f79a99c476b3ad038
 publicKeys=0x8c2fc2bdf1a5
````

До запуска процедуры:
````json
{
  "index": "90",
  "balance": "32000019182209",
  "status": "active_ongoing",
  "validator": {
    "pubkey": "0x8c2fc2bdf1a5b519abf28c1297982d7f22c6a3506cb28ebcc4f9ffc3091348aa5260f2db34a4b68f79a99c476b3ad038",
    "withdrawal_credentials": "0x010000000000000000000000e43bb1b64fc7068d313d24d01d8ccca785b22c72",
    "effective_balance": "32000000000000",
    "slashed": false,
    "activation_eligibility_epoch": "0",
    "activation_epoch": "0",
    "exit_epoch": "18446744073709551615",
    "withdrawable_epoch": "18446744073709551615"
  }
}
````

и после:
````json
{
  "data": {
    "index": "90",
    "balance": "32000546143994",
    "status": "active_exiting",
    "validator": {
      "pubkey": "0x8c2fc2bdf1a5b519abf28c1297982d7f22c6a3506cb28ebcc4f9ffc3091348aa5260f2db34a4b68f79a99c476b3ad038",
      "withdrawal_credentials": "0x010000000000000000000000e43bb1b64fc7068d313d24d01d8ccca785b22c72",
      "effective_balance": "32000000000000",
      "slashed": false,
      "activation_eligibility_epoch": "0",
      "activation_epoch": "0",
      "exit_epoch": "26",
      "^^^^^^^^^^": "^^",
      "withdrawable_epoch": "282",
      "^^^^^^^^^^^^^^^^^^": "^^^"
    }
  },
  "execution_optimistic": true
},

````

после 26 эпохи:
````json
{
  "data": {
    "index": "90",
    "balance": "32000626027307",
    "status": "exited_unslashed",
    "validator": {
      "pubkey": "0x8c2fc2bdf1a5b519abf28c1297982d7f22c6a3506cb28ebcc4f9ffc3091348aa5260f2db34a4b68f79a99c476b3ad038",
      "withdrawal_credentials": "0x010000000000000000000000e43bb1b64fc7068d313d24d01d8ccca785b22c72",
      "effective_balance": "32000000000000",
      "slashed": false,
      "activation_eligibility_epoch": "0",
      "activation_epoch": "0",
      "exit_epoch": "26",
      "withdrawable_epoch": "282"
    }
  },
  "execution_optimistic": true
},
````








## init exit (stacks)

### AccountsHandlerServer
````go
tesseract/proto/prysm/v1alpha1/validator-client
web_api.pb.gw.go
RegisterAccountsHandlerServer
		resp, md, err := local_request_Accounts_VoluntaryExit_0(rctx, inboundMarshaler, server, req, pathParams)

/home/mezin/go/src/tesseract/proto/prysm/v1alpha1/validator-client
web_api.pb.gw.go
local_request_Accounts_VoluntaryExit_0
	msg, err := server.VoluntaryExit(ctx, &protoReq)

/home/mezin/go/src/tesseract/validator/rpc
accounts.go
VoluntaryExit
	rawExitedKeys, _, err := accounts.PerformVoluntaryExit(ctx, cfg)
...

/home/mezin/go/src/tesseract/validator/rpc
accounts.go
VoluntaryExit
	rawExitedKeys, _, err := accounts.PerformVoluntaryExit(ctx, cfg)
````

### AccountsServer
````go
/home/mezin/go/src/tesseract/proto/prysm/v1alpha1/validator-client
web_api.pb.go
RegisterAccountsServer
	s.RegisterService(&_Accounts_serviceDesc, srv)
    
var _Accounts_serviceDesc = grpc.ServiceDesc{
	ServiceName: "ethereum.validator.accounts.v2.Accounts",
	Methods: []grpc.MethodDesc{
		{
			MethodName: "ListAccounts",
			Handler:    _Accounts_ListAccounts_Handler,
		},
		{
			MethodName: "BackupAccounts",
			Handler:    _Accounts_BackupAccounts_Handler,
		},
		{
			MethodName: "DeleteAccounts",
			Handler:    _Accounts_DeleteAccounts_Handler,
		},
		{
			MethodName: "VoluntaryExit",
			Handler:    _Accounts_VoluntaryExit_Handler,
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
		},
	},
}


/home/mezin/go/src/tesseract/proto/prysm/v1alpha1/validator-client
web_api.pb.go
_Accounts_VoluntaryExit_Handler
		return srv.(AccountsServer).VoluntaryExit(ctx, in)
		return srv.(AccountsServer).VoluntaryExit(ctx, req.(*VoluntaryExitRequest))

/home/mezin/go/src/tesseract/validator/rpc
accounts.go
VoluntaryExit
	rawExitedKeys, _, err := accounts.PerformVoluntaryExit(ctx, cfg)

````

### ExitAccountsCli
````go
/home/mezin/go/src/tesseract/validator/accounts
accounts_exit.go
ExitAccountsCli
	rawExitedKeys, trimmedExitedKeys, err := PerformVoluntaryExit(cliCtx.Context, cfg)

````




## PerformVoluntaryExit


````go
// PerformVoluntaryExit uses gRPC clients to submit a voluntary exit message to a beacon node.
func PerformVoluntaryExit(
	ctx context.Context, cfg PerformExitCfg,
) (rawExitedKeys [][]byte, formattedExitedKeys []string, err error) {
	for i, key := range cfg.RawPubKeys {
		if err := client.ProposeExit(ctx, cfg.ValidatorClient, cfg.NodeClient, cfg.Keymanager.Sign, key); err != nil { ... }
	}
    ...
}


// ProposeExit performs a voluntary exit on a validator.
// The exit is signed by the validator before being sent to the beacon node for broadcasting.
func ProposeExit(
	ctx context.Context,
	validatorClient ethpb.BeaconNodeValidatorClient,
	nodeClient ethpb.NodeClient,
	signer signingFunc,
	pubKey []byte,
) error {


	indexResponse, err := validatorClient.ValidatorIndex(ctx, &ethpb.ValidatorIndexRequest{PublicKey: pubKey})
	...
	genesisResponse, err := nodeClient.GetGenesis(ctx, &emptypb.Empty{})
	...
	totalSecondsPassed := prysmTime.Now().Unix() - genesisResponse.GenesisTime.Seconds
	currentEpoch := types.Epoch(uint64(totalSecondsPassed) / uint64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot)))

	exit := &ethpb.VoluntaryExit{Epoch: currentEpoch, ValidatorIndex: indexResponse.Index}
	sig, err := signVoluntaryExit(ctx, validatorClient, signer, pubKey, exit)
	...
	signedExit := &ethpb.SignedVoluntaryExit{Exit: exit, Signature: sig}
	exitResp, err := validatorClient.ProposeExit(ctx, signedExit)
	...
	return nil
}


````



## Epoch handling

````go
InitiateValidatorExit
/home/mezin/go/src/tesseract/beacon-chain/core/blocks
exit.go
ProcessVoluntaryExits
		beaconState, err = v.InitiateValidatorExit(ctx, beaconState, exit.Exit.ValidatorIndex)
                                    ^^^^^^^^^^^^^^^^^^^^^^^
/home/mezin/go/src/tesseract/beacon-chain/core/epoch
epoch_processing.go
ProcessRegistryUpdates
			state, err = validators.InitiateValidatorExit(ctx, state, types.ValidatorIndex(idx))
/home/mezin/go/src/tesseract/beacon-chain/core/validators
validator.go
SlashValidator
	s, err := InitiateValidatorExit(ctx, s, slashedIdx)
````


### InitiateValidatorExit

````go
...
//	  # Set validator exit epoch and withdrawable epoch
//	  validator.exit_epoch = exit_queue_epoch
//	  validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx types.ValidatorIndex) (state.BeaconState, error) {
        ...
	var exitEpochs []types.Epoch
	err = s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
		if val.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
			exitEpochs = append(exitEpochs, val.ExitEpoch())
		}
		return nil
	})
	...
	exitEpochs = append(exitEpochs, helpers.ActivationExitEpoch(time.CurrentEpoch(s)))

	// Obtain the exit queue epoch as the maximum number in the exit epochs array.
	exitQueueEpoch := types.Epoch(0)
	for _, i := range exitEpochs {
		if exitQueueEpoch < i {
			exitQueueEpoch = i
		}
	}

	// We use the exit queue churn to determine if we have passed a churn limit.
	exitQueueChurn := uint64(0)
	err = s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
		if val.ExitEpoch() == exitQueueEpoch {
			exitQueueChurn++
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	activeValidatorCount, err := helpers.ActiveValidatorCount(ctx, s, time.CurrentEpoch(s))
	if err != nil {
		return nil, errors.Wrap(err, "could not get active validator count")
	}
	churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
	if err != nil {
		return nil, errors.Wrap(err, "could not get churn limit")
	}

	if exitQueueChurn >= churn {
		exitQueueEpoch++
	}
	validator.ExitEpoch = exitQueueEpoch
	validator.WithdrawableEpoch = exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
	if err := s.UpdateValidatorAtIndex(idx, validator); err != nil {
		return nil, err
	}
	return s, nil
}
````


