Skip to content

Commit

Permalink
First approach
Browse files Browse the repository at this point in the history
  • Loading branch information
dappnodedev committed Sep 13, 2024
1 parent 8da9ce3 commit 34a1ebf
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 6 deletions.
2 changes: 1 addition & 1 deletion shutter/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ RUN apt-get update && \
ENV SHUTTER_GNOSIS_SM_BLOCKTIME=10 \
SHUTTER_GNOSIS_GENESIS_KEYPER=0x440Dc6F164e9241F04d282215ceF2780cd0B755e \
SHUTTER_GNOSIS_MAXTXPOINTERAGE=5 \
SHUTTER_DATABASEURL=postgres://[email protected]${NETWORK}.dappnode:5432/keyper \
SHUTTER_DATABASE_URL=postgres://[email protected]${NETWORK}.dappnode:5432/keyper \
SHUTTER_SHUTTERMINT_SHUTTERMINTURL=http://localhost:26657 \
CHAIN_LISTEN_PORT=26657 \
SHUTTER_BIN=/rolling-shutter \
Expand Down
7 changes: 7 additions & 0 deletions shutter/go-dappnode-shutter/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module go-dappnode-shutter

go 1.22.2

require github.com/joho/godotenv v1.5.1

require github.com/pelletier/go-toml/v2 v2.2.3 // indirect
6 changes: 6 additions & 0 deletions shutter/go-dappnode-shutter/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
73 changes: 73 additions & 0 deletions shutter/go-dappnode-shutter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"flag"
"fmt"
"go-dappnode-shutter/settings"
"log"
"os"

"github.com/joho/godotenv"
)

func main() {
// Define flags for the template, config, and output paths
templateFilePath := flag.String("template", "", "Path to the template file where settings will be included")
configFilePath := flag.String("config", "", "Path to the config file where the settings will be read")
outputFilePath := flag.String("output", "", "Path where the modified settings will be saved")

// Parse the flags
flag.Parse()

// Load environment variables from the .env file
err := godotenv.Load(os.Getenv("ASSETS_DIR") + "/variables.env")
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}

// Check for additional arguments, e.g., keyper or chain
if len(flag.Args()) < 1 {
fmt.Println("Error: missing argument. Use 'include-keyper-settings' or 'include-chain-settings'.")
os.Exit(1)
}

// Read the argument passed to the program
argument := flag.Arg(0)

// Call appropriate function based on the command
switch argument {
case "include-keyper-settings":
// Ensure template, config, and output paths are provided
if *templateFilePath == "" || *configFilePath == "" || *outputFilePath == "" {
fmt.Println("Error: --template, --config, and --output flags must be provided for keyper settings.")
flag.Usage()
os.Exit(1)
}

// Call the function to configure keyper
err := settings.AddSettingsToKeyper(*templateFilePath, *configFilePath, *outputFilePath)
if err != nil {
log.Fatalf("Failed to configure keyper: %v", err)
}

case "include-chain-settings":
// Ensure config and output paths are provided
if *configFilePath == "" || *outputFilePath == "" {
fmt.Println("Error: --config and --output flags must be provided for chain settings.")
flag.Usage()
os.Exit(1)
}

// Call the function to configure chain
err := settings.AddSettingsToChain(*configFilePath, *outputFilePath)
if err != nil {
log.Fatalf("Failed to configure chain: %v", err)
}

default:
fmt.Println("Invalid argument. Use 'include-keyper-settings' or 'include-chain-settings'.")
os.Exit(1)
}

fmt.Println("Configuration completed successfully!")
}
105 changes: 105 additions & 0 deletions shutter/go-dappnode-shutter/settings/chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package settings

import (
"fmt"
"os"

"github.com/pelletier/go-toml/v2"
)

type ChainConfig struct {
PrivateKey string
InstanceID string
}

/**
* This function should:
* - Read all the values from the template configuration file
* - Read the values defined in ChainConfig from the chain configuration file
* - Read the environment variables
* - Copy all the values from the template configuration file to the output configuration file
* - Modify the values using the chain configuration file and environment variables (these have more priority)
* - Save the modified configuration file
* - Return an error if any
*/
func AddSettingsToChain(templateFilePath, outputFilePath string) error {

var chainConfig ChainConfig

chainConfigPath := os.Getenv("SHUTTER_CHAIN_CONFIG_FILE")

// Check that the chain configuration file exists
if _, err := os.Stat(chainConfigPath); os.IsNotExist(err) {
fmt.Println("Chain configuration file does not exist:", chainConfigPath)
os.Exit(1)
}


chainConfigFile, err := os.ReadFile(chainConfigPath)
if err != nil {
fmt.Println("Error reading TOML file:", err)
os.Exit(1)
}

err = toml.Unmarshal(chainConfigFile, &chainConfig)
if err != nil {
fmt.Println("Error unmarshalling TOML file:", err)
os.Exit(1)
}

// Read the template configuration file
templateFile, err := os.ReadFile(templateFilePath)
if err != nil {
return fmt.Errorf("error reading template TOML file: %v", err)
}

// Create a map to hold the template configuration
var templateConfig map[string]interface{}
err = toml.Unmarshal(templateFile, &templateConfig)
if err != nil {
return fmt.Errorf("error unmarshalling template TOML file: %v", err)
}

// Modify the template configuration based on ChainConfig and environment variables
// ChainConfig values take priority over the template values
applyChainConfig(&templateConfig, chainConfig)

// Apply environment variables, which have even higher priority than the ChainConfig
applyEnvOverrides(&templateConfig)

// Marshal the modified configuration to TOML format
modifiedConfig, err := toml.Marshal(templateConfig)
if err != nil {
return fmt.Errorf("error marshalling modified config to TOML: %v", err)
}

// Write the modified configuration to the output file
err = os.WriteFile(outputFilePath, modifiedConfig, 0644)
if err != nil {
return fmt.Errorf("error writing modified TOML file: %v", err)
}

fmt.Println("TOML file modified successfully and saved to", outputFilePath)
return nil
}

// applyChainConfig modifies the template configuration based on the values from ChainConfig
func applyChainConfig(templateConfig *map[string]interface{}, chainConfig ChainConfig) {
if chainConfig.PrivateKey != "" {
(*templateConfig)["PrivateKey"] = chainConfig.PrivateKey
}
if chainConfig.InstanceID != "" {
(*templateConfig)["InstanceID"] = chainConfig.InstanceID
}
}

// applyEnvOverrides applies environment variables to the template configuration, giving them the highest priority
func applyEnvOverrides(templateConfig *map[string]interface{}) {
// Example: Check for environment variables and override values if they exist
if privateKey := os.Getenv("PRIVATE_KEY"); privateKey != "" {
(*templateConfig)["PrivateKey"] = privateKey
}
if instanceID := os.Getenv("INSTANCE_ID"); instanceID != "" {
(*templateConfig)["InstanceID"] = instanceID
}
}
152 changes: 152 additions & 0 deletions shutter/go-dappnode-shutter/settings/keyper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package settings

import (
"fmt"
"os"
"reflect"
"strconv"
"strings"

"github.com/pelletier/go-toml/v2"
)

type KeyperConfig struct {
InstanceID int `env:"_ASSETS_INSTANCE_ID"`
MaxNumKeysPerMessage int `env:"_ASSETS_MAX_NUM_KEYS_PER_MESSAGE"`
EncryptedGasLimit int `env:"_ASSETS_ENCRYPTED_GAS_LIMIT"`
GenesisSlotTimestamp int `env:"_ASSETS_GENESIS_SLOT_TIMESTAMP"`
SyncStartBlockNumber int `env:"_ASSETS_SYNC_START_BLOCK_NUMBER"`
KeyperSetManager string `env:"_ASSETS_KEYPER_SET_MANAGER"`
KeyBroadcastContract string `env:"_ASSETS_KEY_BROADCAST_CONTRACT"`
Sequencer string `env:"_ASSETS_SEQUENCER"`
ValidatorRegistry string `env:"_ASSETS_VALIDATOR_REGISTRY"`
DiscoveryNamespace string `env:"_ASSETS_DISCOVERY_NAME_PREFIX"`
CustomBootstrapAddresses []string `env:"_ASSETS_CUSTOM_BOOTSTRAP_ADDRESSES"`
DKGPhaseLength int `env:"_ASSETS_DKG_PHASE_LENGTH"`
DKGStartBlockDelta int `env:"_ASSETS_DKG_START_BLOCK_DELTA"`

DatabaseURL string `env:"SHUTTER_DATABASE_URL"`
BeaconAPIURL string `env:"SHUTTER_BEACONAPIURL"`
ContractsURL string `env:"SHUTTER_GNOSIS_NODE_CONTRACTSURL"`
MaxTxPointerAge int `env:"SHUTTER_GNOSIS_MAXTXPOINTERAGE"`
DeploymentDir string `env:"SHUTTER_DEPLOYMENT_DIR"` // Unused, but you can still add an env if needed
EthereumURL string `env:"SHUTTER_GNOSIS_NODE_ETHEREUMURL"`
ShuttermintURL string `env:"SHUTTER_SHUTTERMINT_SHUTTERMINTURL"`
ListenAddresses string `env:"SHUTTER_P2P_LISTENADDRESSES"`
AdvertiseAddresses string `env:"SHUTTER_P2P_ADVERTISEADDRESSES"`
ValidatorPublicKey string `env:"VALIDATOR_PUBLIC_KEY"`
Enabled bool `env:"SHUTTER_ENABLED"`
}

// AddSettingsToKeyper modifies the keyper settings by combining the template, config, and environment variables.
func AddSettingsToKeyper(templateFilePath, configFilePath, outputFilePath string) error {
var keyperConfig KeyperConfig

fmt.Println("Adding user settings to keyper...")

// Read the keyper config file
configFile, err := os.ReadFile(configFilePath)
if err != nil {
return fmt.Errorf("error reading chain config TOML file: %v", err)
}

// Unmarshal the chain config TOML into the chainConfig struct
err = toml.Unmarshal(configFile, &keyperConfig)
if err != nil {
return fmt.Errorf("error unmarshalling chain config TOML file: %v", err)
}

// Read the template file
templateFile, err := os.ReadFile(templateFilePath)
if err != nil {
return fmt.Errorf("error reading template TOML file: %v", err)
}

// Create a map to hold the template configuration
var templateConfig map[string]interface{}
err = toml.Unmarshal(templateFile, &templateConfig)
if err != nil {
return fmt.Errorf("error unmarshalling template TOML file: %v", err)
}

// Modify the template configuration based on ChainConfig and environment variables
applyKeyperConfig(&templateConfig, keyperConfig)
applyEnvOverrides(&templateConfig)

// Marshal the modified configuration to TOML format
modifiedConfig, err := toml.Marshal(templateConfig)
if err != nil {
return fmt.Errorf("error marshalling modified config to TOML: %v", err)
}

// Write the modified configuration to the output file
err = os.WriteFile(outputFilePath, modifiedConfig, 0644)
if err != nil {
return fmt.Errorf("error writing modified TOML file: %v", err)
}

fmt.Println("Keyper TOML file modified successfully and saved to", outputFilePath)
return nil
}

// applyKeyperConfig modifies the template based on environment variables, config, and default template values
func applyKeyperConfig(templateConfig *map[string]interface{}, keyperConfig KeyperConfig) {
v := reflect.ValueOf(keyperConfig)
t := reflect.TypeOf(keyperConfig)

for i := 0; i < v.NumField(); i++ {
fieldValue := v.Field(i)
fieldType := t.Field(i)

// Get the environment variable tag
envVar := fieldType.Tag.Get("env")

// Get the corresponding field value based on the priority (env > config > template)
fieldName := fieldType.Name

var finalValue interface{}

// 1. Check environment variable
envValue := os.Getenv(envVar)
if envValue != "" {
finalValue = parseEnvValue(envValue, fieldType.Type)
} else if !isZeroValue(fieldValue.Interface()) {
// 2. Use config value if it exists and is non-zero
finalValue = fieldValue.Interface()
} else {
// 3. Fallback to template value (do nothing as it's already in template)
finalValue = (*templateConfig)[fieldName]
}

setTemplateField(templateConfig, fieldName, finalValue)
}
}

// Helper function to update template configuration
func setTemplateField(templateConfig *map[string]interface{}, key string, value interface{}) {
if !isZeroValue(value) {
(*templateConfig)[key] = value
}
}

// Parse environment variable value into the correct type
func parseEnvValue(value string, valueType reflect.Type) interface{} {
switch valueType.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
intValue, _ := strconv.ParseInt(value, 10, 64)
return intValue
case reflect.Bool:
boolValue, _ := strconv.ParseBool(value)
return boolValue
case reflect.Slice:
// Assume comma-separated values for slice
return strings.Split(value, ",")
default:
return value
}
}

// Check if a value is the zero value of its type
func isZeroValue(value interface{}) bool {
return reflect.DeepEqual(value, reflect.Zero(reflect.TypeOf(value)).Interface())
}
4 changes: 2 additions & 2 deletions shutter/scripts/configure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ generate_keyper_config() {

# Check if the configuration file already exists
if [ -f "$KEYPER_GENERATED_CONFIG_FILE" ]; then
echo "[INFO | configure] Configuration file already exists. Skipping generation..."
return
echo "[INFO | configure] Configuration file already exists. Removing it..."
rm "$KEYPER_GENERATED_CONFIG_FILE"
fi

echo "[INFO | configure] Generating configuration files..."
Expand Down
2 changes: 1 addition & 1 deletion shutter/scripts/configure_keyper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ sed -i "/^DKGPhaseLength/c\DKGPhaseLength = ${_ASSETS_DKG_PHASE_LENGTH}" "$KEYPE
sed -i "/^DKGStartBlockDelta/c\DKGStartBlockDelta = ${_ASSETS_DKG_START_BLOCK_DELTA}" "$KEYPER_CONFIG_FILE"

# Dynamic values (regenerated on each start)
sed -i "/^DatabaseURL/c\DatabaseURL = \"${SHUTTER_DATABASEURL}\"" "$KEYPER_CONFIG_FILE"
sed -i "/^DatabaseURL/c\DatabaseURL = \"${SHUTTER_DATABASE_URL}\"" "$KEYPER_CONFIG_FILE"
sed -i "/^BeaconAPIURL/c\BeaconAPIURL = \"${SHUTTER_BEACONAPIURL}\"" "$KEYPER_CONFIG_FILE"
sed -i "/^ContractsURL/c\ContractsURL = \"${SHUTTER_GNOSIS_NODE_CONTRACTSURL}\"" "$KEYPER_CONFIG_FILE"
sed -i "/^MaxTxPointerAge/c\MaxTxPointerAge = ${SHUTTER_GNOSIS_MAXTXPOINTERAGE}" "$KEYPER_CONFIG_FILE"
Expand Down
Loading

0 comments on commit 34a1ebf

Please sign in to comment.