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

[Merged by Bors] - implement genesis config #3600

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
48 changes: 35 additions & 13 deletions cmd/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ import (
"github.com/spacemeshos/go-spacemesh/txs"
)

const edKeyFileName = "key.bin"
const (
edKeyFileName = "key.bin"
genesisFileName = "genesis.json"
)

// Logger names.
const (
Expand Down Expand Up @@ -115,19 +118,19 @@ var Cmd = &cobra.Command{
)
starter := func() error {
if err := app.Initialize(); err != nil {
return fmt.Errorf("init node: %w", err)
return err
}
// This blocks until the context is finished or until an error is produced
if err := app.Start(); err != nil {
return fmt.Errorf("start node: %w", err)
return err
}

return nil
}
err = starter()
app.Cleanup()
if err != nil {
log.With().Fatal("Failed to run the node. See logs for details.", log.Err(err))
log.With().Fatal(err.Error())
}
},
}
Expand Down Expand Up @@ -296,6 +299,31 @@ func (app *App) introduction() {

// Initialize sets up an exit signal, logging and checks the clock, returns error if clock is not in sync.
func (app *App) Initialize() (err error) {
// ensure all data folders exist
err = filesystem.ExistOrCreate(app.Config.DataDir())
Copy link
Contributor

Choose a reason for hiding this comment

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

for better usability maybe we should just go ahead to create the directory if it doesn't exist.

Copy link
Contributor Author

@dshulyak dshulyak Sep 29, 2022

Choose a reason for hiding this comment

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

but this function is creating a directory exactly in such case. did i miss your point?

Copy link
Contributor

Choose a reason for hiding this comment

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

lol. my bad.

the error msg is "ensure folders exist" which misled me and prolly the operator. but i guess it'll print filesystem error to give the operator more hint.

if err != nil {
return fmt.Errorf("ensure folders exist: %w", err)
}

gpath := filepath.Join(app.Config.DataDir(), genesisFileName)
var existing config.GenesisConfig
if err := existing.LoadFromFile(gpath); err != nil {
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to load genesis config at %s", gpath)
}
if err := app.Config.Genesis.Validate(); err != nil {
return err
}
if err := app.Config.Genesis.WriteToFile(gpath); err != nil {
return fmt.Errorf("failed to write genesis config to %s: %w", gpath, err)
}
} else {
diff := existing.Diff(app.Config.Genesis)
if len(diff) > 0 {
return fmt.Errorf("genesis config was updated after initializing a node, if you know that update is required delete config at %s.\ndiff:\n%s", gpath, diff)
}
}

// tortoise wait zdist layers for hare to timeout for a layer. once hare timeout, tortoise will
// vote against all blocks in that layer. so it's important to make sure zdist takes longer than
// hare's max time duration to run consensus for a layer
Expand All @@ -315,12 +343,6 @@ func (app *App) Initialize() (err error) {
// override default config in timesync since timesync is using TimeConfigValues
timeCfg.TimeConfigValues = app.Config.TIME

// ensure all data folders exist
err = filesystem.ExistOrCreate(app.Config.DataDir())
if err != nil {
return fmt.Errorf("ensure folders exist: %w", err)
}

// exit gracefully - e.g. with app Cleanup on sig abort (ctrl-c)
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
Expand Down Expand Up @@ -466,7 +488,7 @@ func (app *App) initServices(ctx context.Context,
}
}

goldenATXID := types.ATXID(types.HexToHash32(app.Config.GoldenATXID))
goldenATXID := types.ATXID(app.Config.Genesis.GenesisID().ToHash32())
if goldenATXID == *types.EmptyATXID {
return errors.New("invalid golden atx id")
}
Expand Down Expand Up @@ -1036,9 +1058,9 @@ func (app *App) Start() error {
/* Initialize all protocol services */

dbStorepath := app.Config.DataDir()
gTime, err := time.Parse(time.RFC3339, app.Config.GenesisTime)
gTime, err := time.Parse(time.RFC3339, app.Config.Genesis.GenesisTime)
if err != nil {
return fmt.Errorf("cannot parse genesis time %s: %d", app.Config.GenesisTime, err)
return fmt.Errorf("cannot parse genesis time %s: %d", app.Config.Genesis.GenesisTime, err)
}
ld := time.Duration(app.Config.LayerDurationSec) * time.Second
clock := timesync.NewClock(timesync.RealClock{}, ld, gTime, lg.WithName("clock"))
Expand Down
93 changes: 88 additions & 5 deletions cmd/node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,8 +709,8 @@ func TestSpacemeshApp_TransactionService(t *testing.T) {
app.Config.SyncInterval = 1000000
app.Config.LayerDurationSec = 2

app.Config.GenesisTime = time.Now().Add(20 * time.Second).Format(time.RFC3339)
app.Config.Genesis = &apiConfig.GenesisConfig{
app.Config.Genesis = &config.GenesisConfig{
GenesisTime: time.Now().Add(20 * time.Second).Format(time.RFC3339),
Accounts: map[string]uint64{
address.String(): 100_000_000,
},
Expand Down Expand Up @@ -914,6 +914,90 @@ func TestConfig_GenesisAccounts(t *testing.T) {
})
}

type nonFatalLogger struct {
log.Log
record []string
}

func TestGenesisConfig(t *testing.T) {
t.Run("config is written to a file", func(t *testing.T) {
app := New()
app.Config = &config.Config{
BaseConfig: config.BaseConfig{
DataDirParent: t.TempDir(),
},
Genesis: &config.GenesisConfig{
ExtraData: "test",
GenesisTime: time.Now().Format(time.RFC3339),
},
}
require.ErrorContains(t, app.Initialize(), "tortoise")
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe you can start from DefaultTestConfig() and modify the genesis config and data dir so you don't hit the tortoise param error. see TestInitialize_BadTortoiseParams

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks, slipped from my mind

var existing config.GenesisConfig
require.NoError(t, existing.LoadFromFile(filepath.Join(app.Config.DataDir(), genesisFileName)))
require.Empty(t, existing.Diff(app.Config.Genesis))
})
t.Run("no error if no diff", func(t *testing.T) {
app := New()
app.Config = &config.Config{
BaseConfig: config.BaseConfig{
DataDirParent: t.TempDir(),
},
Genesis: &config.GenesisConfig{
ExtraData: "test",
GenesisTime: time.Now().Format(time.RFC3339),
},
}

require.ErrorContains(t, app.Initialize(), "tortoise")
require.ErrorContains(t, app.Initialize(), "tortoise")
Copy link
Contributor

Choose a reason for hiding this comment

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

double-whammy :P

})
t.Run("fatal error on a diff", func(t *testing.T) {
app := New()
app.Config = &config.Config{
BaseConfig: config.BaseConfig{
DataDirParent: t.TempDir(),
},
Genesis: &config.GenesisConfig{
ExtraData: "test",
GenesisTime: time.Now().Format(time.RFC3339),
},
}

require.ErrorContains(t, app.Initialize(), "tortoise")
app.Config.Genesis.ExtraData = "changed"
err := app.Initialize()
require.ErrorContains(t, err, "genesis config")
})
t.Run("not valid time", func(t *testing.T) {
app := New()
app.Config = &config.Config{
BaseConfig: config.BaseConfig{
DataDirParent: t.TempDir(),
},
Genesis: &config.GenesisConfig{
ExtraData: "test",
GenesisTime: time.Now().Format(time.RFC1123),
},
}

require.ErrorContains(t, app.Initialize(), "time.RFC3339")
})
t.Run("long extra data", func(t *testing.T) {
app := New()
app.Config = &config.Config{
BaseConfig: config.BaseConfig{
DataDirParent: t.TempDir(),
},
Genesis: &config.GenesisConfig{
ExtraData: string(make([]byte, 256)),
GenesisTime: time.Now().Format(time.RFC1123),
},
}

require.ErrorContains(t, app.Initialize(), "extra-data")
})
}

func getTestDefaultConfig() *config.Config {
cfg, err := LoadConfigFromFile()
if err != nil {
Expand Down Expand Up @@ -959,11 +1043,10 @@ func getTestDefaultConfig() *config.Config {
cfg.FETCH.MaxRetriesForPeer = 5
cfg.FETCH.BatchSize = 5
cfg.FETCH.BatchTimeout = 5
cfg.GoldenATXID = "0x5678"

cfg.Beacon = beacon.NodeSimUnitTestConfig()

cfg.Genesis = apiConfig.DefaultTestGenesisConfig()
cfg.Genesis = config.DefaultTestGenesisConfig()

types.SetLayersPerEpoch(cfg.LayersPerEpoch)

Expand All @@ -977,7 +1060,7 @@ func initSingleInstance(lg log.Log, cfg config.Config, i int, genesisTime string
) (*App, error) {
smApp := New(WithLog(lg))
smApp.Config = &cfg
smApp.Config.GenesisTime = genesisTime
smApp.Config.Genesis.GenesisTime = genesisTime

coinbaseAddressBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(coinbaseAddressBytes, uint32(i+1))
Expand Down
6 changes: 2 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,14 @@ func AddCommands(cmd *cobra.Command) {
config.OracleServerWorldID, "The worldid to use with the oracle server (temporary) ")
cmd.PersistentFlags().StringVar(&config.PoETServer, "poet-server",
config.PoETServer, "The poet server url. (temporary) ")
cmd.PersistentFlags().StringVar(&config.GenesisTime, "genesis-time",
config.GenesisTime, "Time of the genesis layer in 2019-13-02T17:02:00+00:00 format")
cmd.PersistentFlags().StringVar(&config.Genesis.GenesisTime, "genesis-time",
config.Genesis.GenesisTime, "Time of the genesis layer in 2019-13-02T17:02:00+00:00 format")
cmd.PersistentFlags().IntVar(&config.LayerDurationSec, "layer-duration-sec",
config.LayerDurationSec, "Duration between layers in seconds")
cmd.PersistentFlags().IntVar(&config.LayerAvgSize, "layer-average-size",
config.LayerAvgSize, "Layer Avg size")
cmd.PersistentFlags().BoolVar(&config.PprofHTTPServer, "pprof-server",
config.PprofHTTPServer, "enable http pprof server")
cmd.PersistentFlags().StringVar(&config.GoldenATXID, "golden-atx",
config.GoldenATXID, "golden ATX hash")
cmd.PersistentFlags().Uint64Var(&config.TickSize, "tick-size", config.TickSize, "number of poet leaves in a single tick")
cmd.PersistentFlags().StringVar(&config.PublishEventsURL, "events-url",
config.PublishEventsURL, "publish events to this url; if no url specified no events will be published")
Expand Down
38 changes: 16 additions & 22 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"math"
"path/filepath"
"time"

"github.com/spf13/viper"

Expand Down Expand Up @@ -38,21 +37,21 @@ var (
// Config defines the top level configuration for a spacemesh node.
type Config struct {
BaseConfig `mapstructure:"main"`
Address *types.Config `mapstructure:"address"`
Genesis *apiConfig.GenesisConfig `mapstructure:"genesis"`
Tortoise tortoise.Config `mapstructure:"tortoise"`
P2P p2p.Config `mapstructure:"p2p"`
API apiConfig.Config `mapstructure:"api"`
HARE hareConfig.Config `mapstructure:"hare"`
HareEligibility eligConfig.Config `mapstructure:"hare-eligibility"`
Beacon beacon.Config `mapstructure:"beacon"`
TIME timeConfig.TimeConfig `mapstructure:"time"`
VM vm.Config `mapstructure:"vm"`
POST activation.PostConfig `mapstructure:"post"`
POET activation.PoetConfig `mapstructure:"poet"`
SMESHING SmeshingConfig `mapstructure:"smeshing"`
LOGGING LoggerConfig `mapstructure:"logging"`
FETCH fetch.Config `mapstructure:"fetch"`
Address *types.Config `mapstructure:"address"`
Genesis *GenesisConfig `mapstructure:"genesis"`
Tortoise tortoise.Config `mapstructure:"tortoise"`
P2P p2p.Config `mapstructure:"p2p"`
API apiConfig.Config `mapstructure:"api"`
HARE hareConfig.Config `mapstructure:"hare"`
HareEligibility eligConfig.Config `mapstructure:"hare-eligibility"`
Beacon beacon.Config `mapstructure:"beacon"`
TIME timeConfig.TimeConfig `mapstructure:"time"`
VM vm.Config `mapstructure:"vm"`
POST activation.PostConfig `mapstructure:"post"`
POET activation.PoetConfig `mapstructure:"poet"`
SMESHING SmeshingConfig `mapstructure:"smeshing"`
LOGGING LoggerConfig `mapstructure:"logging"`
FETCH fetch.Config `mapstructure:"fetch"`
}

// DataDir returns the absolute path to use for the node's data. This is the tilde-expanded path given in the config
Expand All @@ -79,7 +78,6 @@ type BaseConfig struct {
OracleServer string `mapstructure:"oracle_server"`
OracleServerWorldID int `mapstructure:"oracle_server_worldid"`

GenesisTime string `mapstructure:"genesis-time"`
LayerDurationSec int `mapstructure:"layer-duration-sec"`
LayerAvgSize int `mapstructure:"layer-average-size"`
LayersPerEpoch uint32 `mapstructure:"layers-per-epoch"`
Expand All @@ -88,8 +86,6 @@ type BaseConfig struct {

PprofHTTPServer bool `mapstructure:"pprof-server"`

GoldenATXID string `mapstructure:"golden-atx"`

GenesisActiveSet int `mapstructure:"genesis-active-size"` // the active set size for genesis

SyncRequestTimeout int `mapstructure:"sync-request-timeout"` // ms the timeout for direct request in the sync
Expand Down Expand Up @@ -118,7 +114,7 @@ func DefaultConfig() Config {
return Config{
Address: types.DefaultAddressConfig(),
BaseConfig: defaultBaseConfig(),
Genesis: apiConfig.DefaultGenesisConfig(),
Genesis: DefaultGenesisConfig(),
Tortoise: tortoise.DefaultConfig(),
P2P: p2p.DefaultConfig(),
API: apiConfig.DefaultConfig(),
Expand Down Expand Up @@ -158,11 +154,9 @@ func defaultBaseConfig() BaseConfig {
ProfilerName: "gp-spacemesh",
OracleServer: "http://localhost:3030",
OracleServerWorldID: 0,
GenesisTime: time.Now().Format(time.RFC3339),
LayerDurationSec: 30,
LayersPerEpoch: 3,
PoETServer: "127.0.0.1",
GoldenATXID: "0x5678", // TODO: Change the value
SyncRequestTimeout: 2000,
SyncInterval: 10,
TxsPerProposal: 100,
Expand Down
Loading