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

persistent validator settings in validator client #12354

Merged
merged 43 commits into from
May 16, 2023

Conversation

james-prysm
Copy link
Contributor

@james-prysm james-prysm commented May 3, 2023

What type of PR is this?

Feature

What does this PR do? Why is it needed?

Currently validator settings from proposer-settings is never persisted in the validator client. Instead, fee recipients and validator registrations are persisted on the beacon node.

Downsides to the current approach.

  • settings are not persisted on restart if using MEV Boost if KeymanagerAPIs are used to populate the settings. This is because the validator registrations will expire after 3 epochs if we stop receiving them.
  • validator settings updated through keymanager API are not immediately active, instead will be scheduled to update every epoch

With the new approach

  • validator settings are saved to db in a bucket
  • bucket will be overridden if restarted with the proposer settings flag, but will update if restarted with default-fee-recipient or enable builder flags.
  • Keymanager APIs will save to bucket

Side Effects
Our gaslimit API was not working in the way that was intended, as it is only used for MEV-related items it will now return an error if those settings are not active or if no proposer settings exist in the first place.

Flag/DB/API combination effects
ps = proposer settings, vc = validator client

  • no ps in db + vc started with suggested fee recipient flag (+ enable builder flag)= new ps saved to db
  • no ps in db + vc started with proposer-settings-file or proposer-settings-url = new ps saved to db
  • no ps in db + vc started without flags + set fee recipient via keymanager api = new ps saved to db
  • ps in db + vc started without flags = validator starts with proposer settings from db
  • ps in db + vc started with suggested fee recipient flag (+ enabled builder flag) = update default settings in ps in db
  • ps in db + vc started with proposer-settings-file or proposer-settings-url = new ps saved to db
  • ps in db + vc started (with or without flags, see results above) + set fee recipient via keymanager api = update key's values in existing ps in db

Future things to consider

  • migrate persistent settings from beacon node to validator client
  • delete validator registration db in favor of cache ( currently behind a flag)

@james-prysm james-prysm self-assigned this May 3, 2023
@james-prysm james-prysm added API Api related tasks Builder PR or issue that supports builder related work labels May 3, 2023
@james-prysm james-prysm requested review from prestonvanloon and nisdas and removed request for potuz May 8, 2023 16:27
if err := proto.Unmarshal(b, to); err != nil {
return errors.Wrap(err, "failed to unmarshal proposer settings")
}
settings, err := validatorServiceConfig.ToSettings(to)
Copy link
Member

Choose a reason for hiding this comment

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

why can't we just modify the proto file directly here ? Or is it just much simpler to update settings ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can change this, it's just options was passed as the internal struct representation so i need to convert it into a proto type anyways. I can change this to converting the options to a proto type to update instead or just update the values of the proto if it's preferred.

err := s.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket(proposerSettingsBucket)
b := bkt.Get(proposerSettingsKey)
if len(b) == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

will we always have a proposer settings object saved in the db ? is that enforced at startup

Copy link
Contributor Author

Choose a reason for hiding this comment

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

validator/db/kv/db.go will start with the bucket, and the check to see if it exists will trigger whether to update or to save a new copy

@@ -60,7 +62,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"google.golang.org/protobuf/encoding/protojson"
"gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/util/yaml"
Copy link
Member

Choose a reason for hiding this comment

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

any reason to change this to a different package ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the k8s package will unmarshal yamls despsite only having json: in the struct, in our case the proto. since we manually wrote the json object that parses before I added yaml: settings but since i'm using the proto now it doesn't auto generate with it. the k8s library is more flexible in this case

pubkeyB := bytesutil.ToBytes48(decodedKey)
vpSettings.ProposeConfig[pubkeyB] = o
}
if psExists {
Copy link
Member

Choose a reason for hiding this comment

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

if no settings are saved don't you want to save them ? trying to understand the purpose of psExists here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right so if the default settings are being updated witth proposer-settings-file,--suggested-fee-recipient,--enable-builder it will update the existing data, whereas if we are restarting with the proposer settings populating specific settings for validators it should just reset the whole thing, otherwise there's no way to clear the set values. We could also introduce a flag to do this if we want...

if there is something that exists we save a new copy.

Copy link
Member

Choose a reason for hiding this comment

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

So proposer settings will only exist in the db, if its a fresh run with the proposer-settings file ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

proposer settings will load from db and not be overwritten if starting without proposer-settings-file. I'll write down all the cases in the description of the PR and transfer it to some docs page.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the description with all the different cases to make this more clear.

FeeRecipient: common.HexToAddress(optionPayload.FeeRecipient),
},
}
if optionPayload.Builder != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

What if there is no fee recipient but there is a builder option. Is it fine to continue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if there's no fee recipient it skips it , see on line 18


// UnmarshalJSON custom unmarshal function for json
func (u *Uint64) UnmarshalJSON(bs []byte) error {
str := string(bs) // Parse plain numbers directly.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should do some length checks before the lines below

Copy link
Contributor Author

Choose a reason for hiding this comment

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

adding more tests.

@@ -77,3 +123,81 @@ type ProposerOption struct {
FeeRecipientConfig *FeeRecipientConfig
BuilderConfig *BuilderConfig
}

// Clone creates a deep copy of the proposer settings
Copy link
Member

Choose a reason for hiding this comment

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

Are these clone behaviors tested somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's tested in the db tests but I'm working on adding very specific unit tests right now for clone.

@@ -188,10 +188,21 @@ func (v *ValidatorService) Start() {
return
}

// checks db if proposer settings exist if none is provided.
if v.proposerSettings == nil {
settings, err := v.db.ProposerSettings(v.ctx)
Copy link
Member

Choose a reason for hiding this comment

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

what happens if err is not nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's going to start without db settings, will start with the flag settings, and if those are not provided it will fall back to the beacon node's settings

}

// ProposerSettingsExists returns true or false if the settings exist or not
func (s *Store) ProposerSettingsExists(ctx context.Context) (bool, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Unit test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image
mostly covered by the node_test.go will have other tests to cover related.


// UpdateProposerSettingsForPubkey updates the existing settings for an internal representation of the proposers settings file at a particular public key
func (s *Store) UpdateProposerSettingsForPubkey(ctx context.Context, pubkey [fieldparams.BLSPubkeyLength]byte, options *validatorServiceConfig.ProposerOption) error {
_, span := trace.StartSpan(ctx, "validator.db.UpdateProposerSettingsDefault")
Copy link
Member

Choose a reason for hiding this comment

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

Wrong span name

}
b, err := hexutil.Decode(key)
if err != nil {
return nil, err
Copy link
Contributor

Choose a reason for hiding this comment

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

We should add info about what the bad key was that we were trying to decode here

Copy link
Contributor

Choose a reason for hiding this comment

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

errorf perhaps and %s

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@james-prysm james-prysm merged commit d907cae into develop May 16, 2023
@james-prysm james-prysm deleted the db-proposer-settings branch May 16, 2023 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API Api related tasks Builder PR or issue that supports builder related work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants