Last active
April 27, 2023 16:05
-
-
Save alex-miller-0/f5e278e03b574c1e5f0e6e4d54cf1b49 to your computer and use it in GitHub Desktop.
Pre-sign validator exits as insurance against hardware failure for an ETH staker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# It may be a good idea to pre-sign exit messages so that if your staking setup | |
# encounters a hardware failure or goes down for some long period of time, you | |
# can safely exit and restrict penalties to only the few days in the exit queue. | |
# This script will sign (but NOT broadcast) exits for each of your validators | |
# and will write the result to a local file. | |
# NOTE: Be careful with this file. If someone obtains it, they can exit your | |
# validators for you (though they can't steal any money unless they have access | |
# to your withdrawal key). | |
# NOTE: This was designed for use with Lighthouse. It will need to be updated | |
# for use with other clients. | |
# | |
# USAGE: You can run this one of two ways: | |
# 1. Run without arguments to output a JSON file with signed exit messages | |
# from ALL known validators | |
# 2. Run with validator pubkey as command line argument to print a single | |
# signed exit message for that validator | |
# | |
from datetime import datetime, timezone | |
from math import trunc | |
import json | |
import os | |
import sys | |
import urllib.request | |
# Define the validator API location | |
VAL_API_URL = "http://127.0.0.1:" + (os.environ.get("VAL_API_PORT") or str(5062)) | |
# Make an HTTP request | |
def HTTP_REQ(url, data=None, headers={}): | |
if data is not None: | |
# POST | |
req = urllib.request.Request(url, data=data, headers=headers) | |
else: | |
req = urllib.request.Request(url, headers=headers) | |
with urllib.request.urlopen(req) as response: | |
data = json.loads(response.read().decode("utf-8")) | |
return data | |
# Get the current UTC timestamp using system clock | |
def get_current_utc_timestamp(): | |
dt = datetime.now(timezone.utc) | |
return trunc(dt.replace(tzinfo=timezone.utc).timestamp()) | |
# Get the authorization token so that we can make protected request | |
# to the validator API. This secret should be in a local file. | |
def get_val_api_token(): | |
token_loc = HTTP_REQ(VAL_API_URL + "/lighthouse/auth")["token_path"] | |
with open(token_loc) as f: | |
token = f.readline() | |
f.close() | |
return token | |
# Generate HTTP headers to send as authentication to the validator API | |
def get_val_api_headers(): | |
token = get_val_api_token() | |
return { "Authorization": "Basic " + token } | |
# Get a set of currently known validator public keys | |
def get_validator_pubkeys(): | |
headers = get_val_api_headers() | |
validators = HTTP_REQ(VAL_API_URL + "/lighthouse/validators", headers=headers)["data"] | |
val_pubkeys = [] | |
for val in validators: | |
val_pubkeys.append(val["voting_pubkey"]) | |
return val_pubkeys | |
# Generate a signed exit message | |
def sign_exit(pubkey): | |
headers = get_val_api_headers() | |
exit_msg = HTTP_REQ(VAL_API_URL + "/eth/v1/validator/" + pubkey + "/voluntary_exit", {}, headers) | |
return exit_msg | |
############ | |
# The script | |
############ | |
### OPTION 1: Sign exit for specific validator provided as command line arg ### | |
############################################################################### | |
# Check if the user provided a specific validator | |
if len(sys.argv) > 1: | |
print(json.dumps(sign_exit(sys.argv[1]), separators=(',', ':'))) | |
sys.exit(0) | |
### OPTION 2: Sign exits for all known validators ### | |
##################################################### | |
# First, get our validators | |
validators = get_validator_pubkeys() | |
# Get all sigs | |
signed_msgs = [] | |
for validator in validators: | |
signed_msgs.append(sign_exit(validator)) | |
# Write to file | |
fname = "signed-exits-" + str(get_current_utc_timestamp()) + ".json" | |
f = open(fname, "w+") | |
f.write(json.dumps(signed_msgs, separators=(',', ':')) | |
f.close() | |
print("Successfully wrote " + str(len(validators)) + " signed exit messages to: " + fname) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment