2020-10-15 22:31:52 +00:00
|
|
|
package accounts
|
2020-08-11 23:15:06 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
2022-06-07 15:19:12 +00:00
|
|
|
"context"
|
2020-08-11 23:15:06 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/logrusorgru/aurora"
|
|
|
|
"github.com/pkg/errors"
|
2022-08-16 12:20:13 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/v3/io/file"
|
|
|
|
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
|
2020-08-11 23:15:06 +00:00
|
|
|
)
|
|
|
|
|
2020-09-17 01:34:42 +00:00
|
|
|
var (
|
|
|
|
au = aurora.NewAurora(true)
|
|
|
|
)
|
|
|
|
|
2020-08-11 23:15:06 +00:00
|
|
|
const (
|
2022-06-07 15:19:12 +00:00
|
|
|
allAccountsText = "All accounts"
|
|
|
|
// ArchiveFilename specifies the name of the backup. Exported for tests.
|
|
|
|
ArchiveFilename = "backup.zip"
|
2020-08-11 23:15:06 +00:00
|
|
|
)
|
|
|
|
|
2022-06-21 15:26:56 +00:00
|
|
|
// Backup allows users to select validator accounts from their wallet
|
2020-08-11 23:15:06 +00:00
|
|
|
// and export them as a backup.zip file containing the keys as EIP-2335 compliant
|
2021-06-26 19:00:33 +00:00
|
|
|
// keystore.json files, which are compatible with importing in other Ethereum consensus clients.
|
2022-06-07 15:19:12 +00:00
|
|
|
func (acm *AccountsCLIManager) Backup(ctx context.Context) error {
|
|
|
|
keystoresToBackup, err := acm.keymanager.ExtractKeystores(ctx, acm.filteredPubKeys, acm.backupsPassword)
|
2022-03-10 17:19:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not extract keys from keymanager")
|
2020-08-11 23:15:06 +00:00
|
|
|
}
|
2022-06-07 15:19:12 +00:00
|
|
|
return zipKeystoresToOutputDir(keystoresToBackup, acm.backupsDir)
|
2020-08-11 23:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Zips a list of keystore into respective EIP-2335 keystore.json files and
|
|
|
|
// writes their zipped format into the specified output directory.
|
2020-10-15 22:31:52 +00:00
|
|
|
func zipKeystoresToOutputDir(keystoresToBackup []*keymanager.Keystore, outputDir string) error {
|
2020-08-11 23:15:06 +00:00
|
|
|
if len(keystoresToBackup) == 0 {
|
|
|
|
return errors.New("nothing to backup")
|
|
|
|
}
|
2021-09-17 21:55:24 +00:00
|
|
|
if err := file.MkdirAll(outputDir); err != nil {
|
2020-08-11 23:15:06 +00:00
|
|
|
return errors.Wrapf(err, "could not create directory at path: %s", outputDir)
|
|
|
|
}
|
|
|
|
// Marshal and zip all keystore files together and write the zip file
|
|
|
|
// to the specified output directory.
|
2022-06-07 15:19:12 +00:00
|
|
|
archivePath := filepath.Join(outputDir, ArchiveFilename)
|
2021-09-17 21:55:24 +00:00
|
|
|
if file.FileExists(archivePath) {
|
2020-08-11 23:15:06 +00:00
|
|
|
return errors.Errorf("Zip file already exists in directory: %s", archivePath)
|
|
|
|
}
|
|
|
|
// We create a new file to store our backup.zip.
|
2022-01-21 16:52:31 +00:00
|
|
|
zipfile, err := os.Create(filepath.Clean(archivePath))
|
2020-08-11 23:15:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "could not create zip file with path: %s", archivePath)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err := zipfile.Close(); err != nil {
|
|
|
|
log.WithError(err).Error("Could not close zipfile")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
// Using this zip file, we create a new zip writer which we write
|
|
|
|
// files to directly from our marshaled keystores.
|
|
|
|
writer := zip.NewWriter(zipfile)
|
|
|
|
defer func() {
|
|
|
|
// We close the zip writer when done.
|
|
|
|
if err := writer.Close(); err != nil {
|
|
|
|
log.WithError(err).Error("Could not close zip file after writing")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
for i, k := range keystoresToBackup {
|
|
|
|
encodedFile, err := json.MarshalIndent(k, "", "\t")
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not marshal keystore to JSON file")
|
|
|
|
}
|
|
|
|
f, err := writer.Create(fmt.Sprintf("keystore-%d.json", i))
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not write keystore file to zip")
|
|
|
|
}
|
|
|
|
if _, err = f.Write(encodedFile); err != nil {
|
|
|
|
return errors.Wrap(err, "could not write keystore file contents")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.WithField(
|
|
|
|
"backup-path", archivePath,
|
|
|
|
).Infof("Successfully backed up %d accounts", len(keystoresToBackup))
|
|
|
|
return nil
|
|
|
|
}
|