Skip to content

Commit

Permalink
improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
olegshmuelov committed Dec 27, 2023
1 parent c1b0f0b commit 2de450b
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 136 deletions.
15 changes: 10 additions & 5 deletions e2e/cmd/ssv-e2e/beacon_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/attestantio/go-eth2-client/http"
"os"
"time"

"github.com/attestantio/go-eth2-client/http"

eth2client "github.com/attestantio/go-eth2-client"
"github.com/attestantio/go-eth2-client/auto"
"github.com/attestantio/go-eth2-client/spec/phase0"
Expand All @@ -30,6 +31,10 @@ type BeaconProxyCmd struct {
BasePort int ` env:"BASE_PORT" help:"Base port for the gateways." default:"6631"`
}

type BeaconProxyJSON struct {
Validators map[phase0.ValidatorIndex]string `json:"beacon_proxy"`
}

func GetValidators(ctx context.Context, beaconURL string, idxs []phase0.ValidatorIndex) (map[phase0.ValidatorIndex]*v1.Validator, error) {
// todo: maybe create the client on top and pass down to all components
client, err := auto.New(
Expand Down Expand Up @@ -79,17 +84,17 @@ func (cmd *BeaconProxyCmd) Run(logger *zap.Logger, globals Globals) error {
}
logger.Info("Beacon client status OK")

var validators map[phase0.ValidatorIndex]string // dx => tests
contents, err := os.ReadFile(globals.ValidatorsFile)
if err != nil {
return fmt.Errorf("failed to read file contents: %s, %w", globals.ValidatorsFile, err)
}
err = json.Unmarshal(contents, &validators)
if err != nil {

var beaconProxyJSON BeaconProxyJSON // dx => tests
if err = json.Unmarshal(contents, &beaconProxyJSON); err != nil {
return fmt.Errorf("error parsing json file: %s, %w", globals.ValidatorsFile, err)
}

validatorsData, err := GetValidators(ctx, cmd.BeaconNodeUrl, maps.Keys(validators))
validatorsData, err := GetValidators(ctx, cmd.BeaconNodeUrl, maps.Keys(beaconProxyJSON.Validators))
if err != nil {
return fmt.Errorf("failed to get validators data from beacon node err:%v", err)
}
Expand Down
50 changes: 29 additions & 21 deletions e2e/cmd/ssv-e2e/logs_catcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package main

import (
"context"
"encoding/json"
"fmt"
"os"

"go.uber.org/zap"

Expand All @@ -11,15 +13,18 @@ import (
)

type LogsCatcherCmd struct {
Mode string `required:"" env:"Mode" help:"Mode of the logs catcher. Can be Slashing or BlsVerification"`
Leader int `env:"Leader" help:"Leader to run the bls verification on"`
Mode string `required:"" env:"Mode" help:"Mode of the logs catcher. Can be Slashing or BlsVerification"`
}

const (
SlashingMode = "Slashing"
BlsVerificationMode = "BlsVerification"
)

type BlsVerificationJSON struct {
CorruptedShares []*logs_catcher.CorruptedShare `json:"bls_verification"`
}

func (cmd *LogsCatcherCmd) Run(logger *zap.Logger, globals Globals) error {
// TODO: where do we stop?
ctx := context.Background()
Expand Down Expand Up @@ -48,27 +53,15 @@ func (cmd *LogsCatcherCmd) Run(logger *zap.Logger, globals Globals) error {
case BlsVerificationMode:
logger.Info("Running BlsVerification mode")

var corruptedShare logs_catcher.CorruptedShare
switch cmd.Leader {
case 1:
corruptedShare = logs_catcher.CorruptedShare{
OperatorID: 2,
ValidatorPubKey: "8c5801d7a18e27fae47dfdd99c0ac67fbc6a5a56bb1fc52d0309626d805861e04eaaf67948c18ad50c96d63e44328ab0",
ValidatorIndex: fmt.Sprintf("v%d", 1476356),
}

case 2:
corruptedShare = logs_catcher.CorruptedShare{
OperatorID: 2,
ValidatorPubKey: "a238aa8e3bd1890ac5def81e1a693a7658da491ac087d92cee870ab4d42998a184957321d70cbd42f9d38982dd9a928c",
ValidatorIndex: fmt.Sprintf("v%d", 1476357),
}
default:
return fmt.Errorf("invalid leader: %d", cmd.Leader)
corruptedShares, err := UnmarshalBlsVerificationJSON(globals.ValidatorsFile)
if err != nil {
return fmt.Errorf("failed to unmarshal bls verification json: %w", err)
}

if err = logs_catcher.VerifyBLSSignature(ctx, logger, cli, corruptedShare); err != nil {
return err
for _, corruptedShare := range corruptedShares {
if err = logs_catcher.VerifyBLSSignature(ctx, logger, cli, corruptedShare); err != nil {
return fmt.Errorf("failed to verify BLS signature for validator index %d: %w", corruptedShare.ValidatorIndex, err)
}
}

default:
Expand All @@ -77,3 +70,18 @@ func (cmd *LogsCatcherCmd) Run(logger *zap.Logger, globals Globals) error {

return nil
}

// UnmarshalBlsVerificationJSON reads the JSON file and unmarshals it into []*CorruptedShare.
func UnmarshalBlsVerificationJSON(filePath string) ([]*logs_catcher.CorruptedShare, error) {
contents, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("error reading json file for BLS verification: %s, %w", filePath, err)
}

var blsVerificationJSON BlsVerificationJSON
if err = json.Unmarshal(contents, &blsVerificationJSON); err != nil {
return nil, fmt.Errorf("error parsing json file for BLS verification: %s, %w", filePath, err)
}

return blsVerificationJSON.CorruptedShares, nil
}
1 change: 1 addition & 0 deletions e2e/cmd/ssv-e2e/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

type Globals struct {
NetworkName string `env:"NETWORK" default:"holesky-e2e" help:"Network config name"`
LogLevel string `env:"LOG_LEVEL" enum:"debug,info,warn,error" default:"debug" help:"Log level."`
LogFormat string `env:"LOG_FORMAT" enum:"console,json" default:"console" help:"Log format."`
ValidatorsFile string `env:"VALIDATORS_FILE" default:"./validators.json" help:"Path to the validators.json file." type:"path"`
Expand Down
176 changes: 116 additions & 60 deletions e2e/cmd/ssv-e2e/share_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,64 @@ import (
"encoding/base64"
"encoding/hex"
"fmt"
"os"

"github.com/bloxapp/ssv-spec/types"
"github.com/bloxapp/ssv/ekm"
"github.com/bloxapp/ssv/networkconfig"
operatorstorage "github.com/bloxapp/ssv/operator/storage"
"github.com/bloxapp/ssv/storage/basedb"
"github.com/bloxapp/ssv/storage/kv"
"github.com/bloxapp/ssv/utils/blskeygen"
"github.com/bloxapp/ssv/utils/rsaencryption"
"github.com/herumi/bls-eth-go-binary/bls"
"go.uber.org/zap"
"gopkg.in/yaml.v3"

"github.com/bloxapp/ssv/e2e/logs_catcher"
)

type ShareUpdateCmd struct {
NetworkName string `required:"" env:"NETWORK" env-description:"Network config name"`
DBPath string `required:"" env:"DB_PATH" help:"Path to the DB folder"`
OperatorPrivateKey string `required:"" env:"OPERATOR_KEY" env-description:"Operator private key"`
ValidatorPubKey string `required:"" env:"VALIDATOR_PUB_KEY" env-description:"Validator public key"`
OperatorPrivateKey string `yaml:"OperatorPrivateKey"`
}

const (
// secret key to be used for updated share
skLeader1 = "3548db63ab5701878daf25fa877638dc7809778815b9d9ecd5369da33ca9e64f"
skLeader2 = "66dd37ae71b35c81022cdde98370e881cff896b689fa9136917f45afce43fd3b"
vpkLeader1 = "8c5801d7a18e27fae47dfdd99c0ac67fbc6a5a56bb1fc52d0309626d805861e04eaaf67948c18ad50c96d63e44328ab0" // leader 1
vpkLeader2 = "a238aa8e3bd1890ac5def81e1a693a7658da491ac087d92cee870ab4d42998a184957321d70cbd42f9d38982dd9a928c" // leader 2
dbPathFormat = "/ssv-node-%d-data/db"
shareYAMLPath = "/tconfig/share%d.yaml"
)

func (cmd *ShareUpdateCmd) Run(logger *zap.Logger, globals Globals) error {
// Setup DB
db, err := kv.New(logger, basedb.Options{
Path: cmd.DBPath,
})
networkConfig, err := networkconfig.GetNetworkConfigByName(globals.NetworkName)
if err != nil {
return fmt.Errorf("failed to get network config: %w", err)
}

corruptedShares, err := UnmarshalBlsVerificationJSON(globals.ValidatorsFile)
if err != nil {
return fmt.Errorf("failed to open db: %w", err)
return fmt.Errorf("failed to unmarshal bls verification json: %w", err)
}

operatorSharesMap := buildOperatorCorruptedSharesMap(corruptedShares)

for operatorID, operatorCorruptedShares := range operatorSharesMap {
// Read OperatorPrivateKey from the YAML file
operatorPrivateKey, err := readOperatorPrivateKeyFromFile(fmt.Sprintf(shareYAMLPath, operatorID))
if err != nil {
return err
}

if err := Process(logger, networkConfig, operatorPrivateKey, operatorID, operatorCorruptedShares); err != nil {
return fmt.Errorf("failed to process operator %d: %w", operatorID, err)
}
}

return nil
}

func Process(logger *zap.Logger, networkConfig networkconfig.NetworkConfig, operatorPrivateKey string, operatorID types.OperatorID, operatorCorruptedShares []*logs_catcher.CorruptedShare) error {
dbPath := fmt.Sprintf(dbPathFormat, operatorID)
db, err := openDB(logger, dbPath)
if err != nil {
return err
}
defer db.Close()

Expand All @@ -46,7 +71,7 @@ func (cmd *ShareUpdateCmd) Run(logger *zap.Logger, globals Globals) error {
return fmt.Errorf("failed to create node storage: %w", err)
}

opSK, err := base64.StdEncoding.DecodeString(cmd.OperatorPrivateKey)
opSK, err := base64.StdEncoding.DecodeString(operatorPrivateKey)
if err != nil {
return err
}
Expand All @@ -67,66 +92,97 @@ func (cmd *ShareUpdateCmd) Run(logger *zap.Logger, globals Globals) error {
if !found {
return fmt.Errorf("operator data not found")
}
if operatorData.ID != operatorID {
return fmt.Errorf("operator ID mismatch")
}

logger.Info("operator data found", zap.Any("operator ID", operatorData.ID))

keyBytes := x509.MarshalPKCS1PrivateKey(rsaPriv)
hashedKey, _ := rsaencryption.HashRsaKey(keyBytes)

networkConfig, err := networkconfig.GetNetworkConfigByName(cmd.NetworkName)
if err != nil {
return fmt.Errorf("failed to get network config: %w", err)
}
keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, networkConfig, false, hashedKey)
if err != nil {
return fmt.Errorf("failed to create key manager: %w", err)
}

pkBytes, err := hex.DecodeString(cmd.ValidatorPubKey)
if err != nil {
return fmt.Errorf("failed to decode validator public key: %w", err)
}

validatorShare := nodeStorage.Shares().Get(nil, pkBytes)
if validatorShare == nil {
return fmt.Errorf(fmt.Sprintf("validator share not found for %s", cmd.ValidatorPubKey))
}
for i, op := range validatorShare.Committee {
if op.OperatorID == operatorData.ID {
var sk string
switch cmd.ValidatorPubKey {
case vpkLeader1:
sk = skLeader1
case vpkLeader2:
sk = skLeader2
default:
return fmt.Errorf("invalid validator public key")
}
for _, corruptedShare := range operatorCorruptedShares {
pkBytes, err := hex.DecodeString(corruptedShare.ValidatorPubKey)
if err != nil {
return fmt.Errorf("failed to decode validator public key: %w", err)
}

blsSK := &bls.SecretKey{}
if err = blsSK.SetHexString(sk); err != nil {
return fmt.Errorf("failed to set secret key: %w", err)
}
validatorShare := nodeStorage.Shares().Get(nil, pkBytes)
if validatorShare == nil {
return fmt.Errorf(fmt.Sprintf("validator share not found for %s", corruptedShare.ValidatorPubKey))
}
if validatorShare.Metadata.BeaconMetadata.Index != corruptedShare.ValidatorIndex {
return fmt.Errorf("validator index mismatch for validator %s", corruptedShare.ValidatorPubKey)
}

if err = keyManager.AddShare(blsSK); err != nil {
return fmt.Errorf("failed to add share: %w", err)
var operatorFound bool
for i, op := range validatorShare.Committee {
if op.OperatorID == operatorData.ID {
operatorFound = true

blsSK, blsPK := blskeygen.GenBLSKeyPair()
if err = keyManager.AddShare(blsSK); err != nil {
return fmt.Errorf("failed to add share: %w", err)
}

preChangePK := validatorShare.SharePubKey
validatorShare.SharePubKey = blsPK.Serialize()
validatorShare.Share.Committee[i].PubKey = validatorShare.SharePubKey
if err = nodeStorage.Shares().Save(nil, validatorShare); err != nil {
return fmt.Errorf("failed to save share: %w", err)
}

logger.Info("validator share was updated successfully",
zap.String("validator pub key", hex.EncodeToString(validatorShare.ValidatorPubKey)),
zap.String("BEFORE: share pub key", hex.EncodeToString(preChangePK)),
zap.String("AFTER: share pub key", hex.EncodeToString(validatorShare.SharePubKey)),
)
}
}
if !operatorFound {
return fmt.Errorf("operator %d not found in corrupted share", operatorData.ID)
}
}

preChangePK := validatorShare.SharePubKey
validatorShare.SharePubKey = blsSK.GetPublicKey().Serialize()
validatorShare.Share.Committee[i].PubKey = validatorShare.SharePubKey
if err = nodeStorage.Shares().Save(nil, validatorShare); err != nil {
return fmt.Errorf("failed to save share: %w", err)
}
return nil
}

logger.Info("validator share was updated successfully",
zap.String("validator pub key", hex.EncodeToString(validatorShare.ValidatorPubKey)),
zap.String("BEFORE: share pub key", hex.EncodeToString(preChangePK)),
zap.String("AFTER: share pub key", hex.EncodeToString(validatorShare.SharePubKey)),
)
return nil
}
func openDB(logger *zap.Logger, dbPath string) (*kv.BadgerDB, error) {
db, err := kv.New(logger, basedb.Options{Path: dbPath})
if err != nil {
return nil, fmt.Errorf("failed to open db: %w", err)
}
return db, nil
}

func readOperatorPrivateKeyFromFile(filePath string) (string, error) {
var config ShareUpdateCmd

data, err := os.ReadFile(filePath)
if err != nil {
return "", fmt.Errorf("failed to read file: %s, error: %w", filePath, err)
}

if err = yaml.Unmarshal(data, &config); err != nil {
return "", fmt.Errorf("failed to unmarshal YAML: %w", err)
}

return config.OperatorPrivateKey, nil
}

// buildOperatorCorruptedSharesMap takes a slice of CorruptedShare and returns a map
// where each key is an OperatorID and the value is a slice of CorruptedShares associated with that OperatorID.
func buildOperatorCorruptedSharesMap(corruptedShares []*logs_catcher.CorruptedShare) map[types.OperatorID][]*logs_catcher.CorruptedShare {
operatorSharesMap := make(map[types.OperatorID][]*logs_catcher.CorruptedShare)

for _, share := range corruptedShares {
operatorSharesMap[share.OperatorID] = append(operatorSharesMap[share.OperatorID], share)
}

return fmt.Errorf("operator not found in validator share")
return operatorSharesMap
}
11 changes: 9 additions & 2 deletions e2e/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ services:
networks:
- blox-docker
volumes:
- ${PWD}/validators.json:/app/validators.json
- /var/run/docker.sock:/var/run/docker.sock

share_update:
Expand All @@ -51,10 +52,16 @@ services:
dockerfile: Dockerfile
image: share_update:latest
command: share-update
environment:
- NETWORK=holesky-e2e
volumes:
- ${PWD}/validators.json:/app/validators.json
- ssv-node-1-data:/ssv-node-1-data
- ssv-node-2-data:/ssv-node-2-data
- ssv-node-3-data:/ssv-node-3-data
- ssv-node-4-data:/ssv-node-4-data
- ${PWD}/config/share1.yaml:/tconfig/share1.yaml
- ${PWD}/config/share2.yaml:/tconfig/share2.yaml
- ${PWD}/config/share3.yaml:/tconfig/share3.yaml
- ${PWD}/config/share4.yaml:/tconfig/share4.yaml
networks:
- blox-docker

Expand Down
Loading

0 comments on commit 2de450b

Please sign in to comment.