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

[CLI][Localnet][Bug] - [localnet_client_debug] Error 137 - Issue #607 #608

Merged
merged 18 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from 12 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
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ develop_start: ## Run all of the make commands necessary to develop on the proje
make go_clean_deps && \
make mockgen && \
make generate_rpc_openapi && \
make refresh_debug_keybase && \
make build

.PHONY: develop_test
Expand Down Expand Up @@ -218,6 +219,10 @@ docker_loki_install: docker_check ## Installs the loki docker driver
docker_loki_check:
if [ `docker plugin ls | grep loki: | wc -l` -eq 0 ]; then make docker_loki_install; fi

.PHONY: refresh_debug_keybase
refresh_debug_keybase: ## Refreshes the debug keybase with the latest keys from the private_keys.yaml file if necessary
go run build/debug_keybase/main.go ./build/localnet/manifests/private-keys.yaml ./build/debug_keybase

.PHONY: clean_mocks
clean_mocks: ## Use `clean_mocks` to delete mocks before recreating them. Also useful to cleanup code that was generated from a different branch
$(eval modules_dir = "shared/modules")
Expand Down
120 changes: 27 additions & 93 deletions app/client/keybase/debug/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,17 @@ package debug

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"

"github.com/korovkin/limiter"
"github.com/dgraph-io/badger/v3"
"github.com/pokt-network/pocket/app/client/keybase"
"github.com/pokt-network/pocket/build"
"github.com/pokt-network/pocket/logger"
cryptoPocket "github.com/pokt-network/pocket/shared/crypto"
"gopkg.in/yaml.v2"
)

const (
// NOTE: This is the number of validators in the private-keys.yaml manifest file
numValidators = 999
debugKeybaseSuffix = "/.pocket/keys"

// Increasing this number is linearly proportional to the amount of RAM required for the debug client to start and import
// pre-generated keys into the keybase. Beware that might cause OOM and process can exit with 137 status code.
// 4 threads takes 350-400MiB from a few tests which sounds acceptable.
debugKeybaseImportConcurrencyLimit = 4
)
const debugKeybaseSuffix = "/.pocket/keys"

var (
// TODO: Allow users to override this value via `datadir` flag or env var or config file
Expand All @@ -45,81 +36,17 @@ func init() {
}

func initializeDebugKeybase() error {
var (
validatorKeysPairMap map[string]string
err error
)

validatorKeysPairMap, err = parseValidatorPrivateKeysFromEmbeddedYaml()

if err != nil {
return err
}

// Create/Open the keybase at `$HOME/.pocket/keys`
kb, err := keybase.NewKeybase(debugKeybasePath)
if err != nil {
return err
}
db := kb.GetBadgerDB()

// Add the keys if the keybase contains less than 999
curAddr, _, err := kb.GetAll()
if err != nil {
if err := restoreBadgerDB(build.DebugKeybaseBackup, db); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

This logic is much cleaner 10/10 🚀

return err
}

// Add validator addresses if not present
if len(curAddr) < numValidators {
logger.Global.Debug().Msgf(fmt.Sprintf("Debug keybase initializing... Adding %d validator keys to the keybase", numValidators-len(curAddr)))

// Use writebatch to speed up bulk insert
wb := db.NewWriteBatch()

// Create a channel to receive errors from goroutines
errCh := make(chan error, numValidators)

limit := limiter.NewConcurrencyLimiter(debugKeybaseImportConcurrencyLimit)

for _, privHexString := range validatorKeysPairMap {
limit.Execute(func() {
// Import the keys into the keybase with no passphrase or hint as these are for debug purposes
keyPair, err := cryptoPocket.CreateNewKeyFromString(privHexString, "", "")
if err != nil {
errCh <- err
return
}

// Use key address as key in DB
addrKey := keyPair.GetAddressBytes()

// Encode KeyPair into []byte for value
keypairBz, err := keyPair.Marshal()
if err != nil {
errCh <- err
return
}
if err := wb.Set(addrKey, keypairBz); err != nil {
errCh <- err
return
}
})
}

limit.WaitAndClose()

// Check if any goroutines returned an error
select {
case err := <-errCh:
return err
default:
}

if err := wb.Flush(); err != nil {
return err
}
}

// Close DB connection
if err := kb.Stop(); err != nil {
return err
Expand All @@ -128,24 +55,31 @@ func initializeDebugKeybase() error {
return nil
}

// parseValidatorPrivateKeysFromEmbeddedYaml fetches the validator private keys from the embedded build/localnet/manifests/private-keys.yaml manifest file.
func parseValidatorPrivateKeysFromEmbeddedYaml() (map[string]string, error) {
func restoreBadgerDB(backupData []byte, db *badger.DB) error {
logger.Global.Debug().Msgf(fmt.Sprintf("Debug keybase initializing... Restoring from the embedded backup file..."))

// Parse the YAML file and load into the config struct
var config struct {
ApiVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
MetaData map[string]string `yaml:"metadata"`
Type string `yaml:"type"`
StringData map[string]string `yaml:"stringData"`
// Create a temporary directory to store the backup data
tempDir, err := ioutil.TempDir("", "badgerdb-restore")
if err != nil {
return err
}
if err := yaml.Unmarshal(build.PrivateKeysFile, &config); err != nil {
return nil, err
defer os.RemoveAll(tempDir)

// Write the backup data to a file in the temporary directory
backupFilePath := filepath.Join(tempDir, "backup")
if err := ioutil.WriteFile(backupFilePath, backupData, 0644); err != nil {
return err
}
validatorKeysMap := make(map[string]string)

for id, privHexString := range config.StringData {
validatorKeysMap[id] = privHexString
backupFile, err := os.Open(backupFilePath)
if err != nil {
return err
}
return validatorKeysMap, nil
defer backupFile.Close()

if err := db.Load(backupFile, 4); err != nil {
return err
}

return nil
}
4 changes: 4 additions & 0 deletions app/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.4] - 2023-03-23

- Updated debug keystore initialization to use an embedded backup instead of the yaml file that has to be rehydrated every time.

## [0.0.0.3] - 2023-03-20

- Adds message routing type field labels to debug CLI actions
Expand Down
10 changes: 5 additions & 5 deletions build/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ package build

import _ "embed"

// PrivateKeysFile is the pre-generated manifest file for LocalNet debugging
// DebugKeybaseBackup is a backup of the pre-loaded debug keybase sourced from the manifest file for LocalNet debugging
//
//go:embed localnet/manifests/private-keys.yaml
var PrivateKeysFile []byte
//go:embed debug_keybase/debug_keybase.bak
var DebugKeybaseBackup []byte

func init() {
if len(PrivateKeysFile) == 0 {
panic("PrivateKeysFile is empty")
if len(DebugKeybaseBackup) == 0 {
panic("DebugKeybaseBackup is empty")
}
}
1 change: 1 addition & 0 deletions build/debug_keybase/cedffe1d784ca397062169b565abc056.md5
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file is used to check if the keybase dump is in sync with the YAML file. Its name is the MD5 hash of the private_keys.yaml
Binary file added build/debug_keybase/debug_keybase.bak
Binary file not shown.
Loading