Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/questfever_fix-web3signer-same-size-reload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Fixed

- Reload Web3Signer validator public keys when the configured key file changes without changing size.
37 changes: 14 additions & 23 deletions validator/keymanager/remote-web3signer/keymanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,9 @@ func (km *Keymanager) refreshRemoteKeysFromFileChanges(ctx context.Context, mark
log.WithError(err).Error("Could not close file watcher")
}
}()
initialFileInfo, err := os.Stat(km.keyFilePath)
if err != nil {
if _, err := os.Stat(km.keyFilePath); err != nil {
return errors.Wrap(err, "could not stat remote signer public key file")
}
initialFileSize := initialFileInfo.Size()
if err := watcher.Add(km.keyFilePath); err != nil {
return errors.Wrap(err, "could not add file to file watcher")
}
Expand Down Expand Up @@ -352,29 +350,22 @@ func (km *Keymanager) refreshRemoteKeysFromFileChanges(ctx context.Context, mark
if e.Has(fsnotify.Remove) {
return errors.New("remote signer key file was removed")
}
currentFileInfo, err := os.Stat(km.keyFilePath)
fileKeys, _, err := km.readKeyFile()
if err != nil {
return errors.New("could not read key file")
}
// prioritize file keys over flag keys
if len(fileKeys) == 0 {
log.Warnln("Remote signer key file no longer has keys, defaulting to flag provided keys")
fileKeys = slices.Collect(maps.Values(km.flagLoadedKeysMap))
}
currentKeys, err := km.FetchValidatingPublicKeys(ctx)
if err != nil {
return errors.Wrap(err, "could not stat remote signer public key file")
return errors.Wrap(err, "could not fetch current keys")
}
if currentFileInfo.Size() != initialFileSize {
if !slices.Equal(currentKeys, fileKeys) {
log.Info("Remote signer key file updated")
fileKeys, _, err := km.readKeyFile()
if err != nil {
return errors.New("could not read key file")
}
// prioritize file keys over flag keys
if len(fileKeys) == 0 {
log.Warnln("Remote signer key file no longer has keys, defaulting to flag provided keys")
fileKeys = slices.Collect(maps.Values(km.flagLoadedKeysMap))
}
currentKeys, err := km.FetchValidatingPublicKeys(ctx)
if err != nil {
return errors.Wrap(err, "could not fetch current keys")
}
if !slices.Equal(currentKeys, fileKeys) {
km.updatePublicKeys(fileKeys)
}
initialFileSize = currentFileInfo.Size()
km.updatePublicKeys(fileKeys)
}
case err, ok := <-watcher.Errors:
if !ok { // Channel was closed (i.e. Watcher.Close() was called).
Expand Down
47 changes: 47 additions & 0 deletions validator/keymanager/remote-web3signer/keymanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,53 @@ func TestNewKeyManager_ChangingFileCreated(t *testing.T) {
}
}

func TestNewKeyManager_ReloadsSameSizeKeyFileUpdate(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context())
defer cancel()

const keyA = "0x800077e04f8d7496099b3d30ac5430aea64873a45e5bcfe004d2095babcbf55e21138ff0d5691abc29da190aa32755c6"
const keyB = "0x8000a9a6d3f5e22d783eefaadbcf0298146adb5d95b04db910a0d4e16976b30229d0b1e7b9cda6c7e0bfa11f72efe055"
keyFilePath := filepath.Join(t.TempDir(), "keyfile.txt")
require.NoError(t, file.WriteFile(keyFilePath, []byte(keyA+"\n")))

root, err := hexutil.Decode("0x270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69")
require.NoError(t, err)
km, err := NewKeymanager(ctx, &SetupConfig{
BaseEndpoint: "http://example.com",
GenesisValidatorsRoot: root,
KeyFilePath: keyFilePath,
})
require.NoError(t, err)

before, err := os.Stat(keyFilePath)
require.NoError(t, err)
pubKeysChan := make(chan [][fieldparams.BLSPubkeyLength]byte, 1)
sub := km.SubscribeAccountChanges(pubKeysChan)
defer sub.Unsubscribe()

// Do not truncate the file: overwrite it in place so its size never changes.
f, err := os.OpenFile(keyFilePath, os.O_WRONLY, 0600)
require.NoError(t, err)
_, err = f.WriteString(keyB + "\n")
require.NoError(t, err)
require.NoError(t, f.Sync())
require.NoError(t, f.Close())

after, err := os.Stat(keyFilePath)
require.NoError(t, err)
require.Equal(t, before.Size(), after.Size())

timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case updatedKeys := <-pubKeysChan:
require.Equal(t, 1, len(updatedKeys))
require.Equal(t, keyB, hexutil.Encode(updatedKeys[0][:]))
case <-timer.C:
t.Fatal("timed out waiting for the same-size key file update")
}
}

func TestNewKeyManager_FileAndFlagsWithDifferentKeys(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
Expand Down