From aaa273da52970c8910afa2786db9810cd44ebbde Mon Sep 17 00:00:00 2001 From: Chris Gianelloni Date: Thu, 23 May 2024 18:53:09 -0400 Subject: [PATCH] feat: hard-code shelley genesis values per network (#190) Signed-off-by: Chris Gianelloni --- env.go | 49 +--------------- internal/config/config.go | 58 ++++++++++++++++--- main.go | 14 ++--- node.go | 114 +------------------------------------- 4 files changed, 60 insertions(+), 175 deletions(-) diff --git a/env.go b/env.go index 3e0f7d7..381b773 100644 --- a/env.go +++ b/env.go @@ -22,8 +22,6 @@ import ( "net/http" "time" - "github.com/blinklabs-io/gouroboros/protocol/localstatequery" - "github.com/blinklabs-io/nview/internal/config" ) @@ -67,28 +65,8 @@ func getNodeMetrics(ctx context.Context) ([]byte, int, error) { return respBodyBytes, resp.StatusCode, nil } -// Calculate current KES period from tip ref -// -//nolint:unused -func getCurrentKESPeriod(g *localstatequery.GenesisConfigResult) uint64 { - return getSlotTipRef(g) / uint64(g.SlotsPerKESPeriod) -} - -// Calculate epoch from current second -// -//nolint:unused -func getEpoch() uint64 { - cfg := config.GetConfig() - currentTimeSec := uint64(time.Now().Unix() - 1) - byronEndTime := cfg.Node.ByronGenesis.StartTime + ((uint64(cfg.Node.ShelleyTransEpoch) * cfg.Node.ByronGenesis.EpochLength * cfg.Node.ByronGenesis.SlotLength) / 1000) - result := uint64( - cfg.Node.ShelleyTransEpoch, - ) + ((currentTimeSec - byronEndTime) / cfg.Node.ByronGenesis.EpochLength / cfg.Node.ByronGenesis.SlotLength) - return uint64(result) -} - // Calculate slot number -func getSlotTipRef(g *localstatequery.GenesisConfigResult) uint64 { +func getSlotTipRef() uint64 { cfg := config.GetConfig() currentTimeSec := uint64(time.Now().Unix() - 1) byronSlots := uint64( @@ -98,20 +76,11 @@ func getSlotTipRef(g *localstatequery.GenesisConfigResult) uint64 { if currentTimeSec < byronEndTime { return ((currentTimeSec - cfg.Node.ByronGenesis.StartTime) * 1000) / cfg.Node.ByronGenesis.SlotLength } - return byronSlots + ((currentTimeSec - byronEndTime) / uint64(g.SlotLength/1000000)) -} - -// Calculate expected interval between blocks -func slotInterval(g *localstatequery.GenesisConfigResult) uint64 { - // g.SlotLength is nanoseconds - // 0.05 is g.ActiveSlotsCoeff resolved - // 0.5 is decentralisation (removed in babbage... so use default) - result := (float64(g.SlotLength/1000000) / 0.05 / 0.5) + 0.5 - return uint64(result) + return byronSlots + ((currentTimeSec - byronEndTime) / uint64(cfg.Node.ShelleyGenesis.SlotLength/1000)) } // Time is in seconds -func timeLeft(t uint64) string { +func timeFromSeconds(t uint64) string { d := t / 60 / 60 / 24 h := math.Mod(float64(t/60/60), 24) m := math.Mod(float64(t/60), 60) @@ -122,15 +91,3 @@ func timeLeft(t uint64) string { } return fmt.Sprintf("%s%02d:%02d:%02d", result, int(h), int(m), int(s)) } - -//nolint:unused -func timeUntilNextEpoch() uint64 { - cfg := config.GetConfig() - currentTimeSec := uint64(time.Now().Unix() - 1) - ste := uint64(cfg.Node.ShelleyTransEpoch) - bgel := cfg.Node.ByronGenesis.EpochLength - bgsl := cfg.Node.ByronGenesis.SlotLength - byronLength := (ste * bgel * bgsl) / 1000 - result := byronLength + ((getEpoch() + 1 - ste) * bgel * bgsl) - currentTimeSec + cfg.Node.ByronGenesis.StartTime - return uint64(result) -} diff --git a/internal/config/config.go b/internal/config/config.go index da3398b..2458748 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -37,14 +37,15 @@ type AppConfig struct { } type NodeConfig struct { - ByronGenesis ByronGenesisConfig `yaml:"byron"` - Binary string `yaml:"binary" envconfig:"CARDANO_NODE_BINARY"` - Network string `yaml:"network" envconfig:"CARDANO_NETWORK"` - SocketPath string `yaml:"socketPath" envconfig:"CARDANO_NODE_SOCKET_PATH"` - NetworkMagic uint32 `yaml:"networkMagic" envconfig:"CARDANO_NODE_NETWORK_MAGIC"` - Port uint32 `yaml:"port" envconfig:"CARDANO_PORT"` - ShelleyTransEpoch int32 `yaml:"shellyTransEpoch" envconfig:"SHELLEY_TRANS_EPOCH"` - BlockProducer bool `yaml:"blockProducer" envconfig:"CARDANO_BLOCK_PRODUCER"` + ByronGenesis ByronGenesisConfig `yaml:"byron"` + Binary string `yaml:"binary" envconfig:"CARDANO_NODE_BINARY"` + Network string `yaml:"network" envconfig:"CARDANO_NETWORK"` + SocketPath string `yaml:"socketPath" envconfig:"CARDANO_NODE_SOCKET_PATH"` + NetworkMagic uint32 `yaml:"networkMagic" envconfig:"CARDANO_NODE_NETWORK_MAGIC"` + Port uint32 `yaml:"port" envconfig:"CARDANO_PORT"` + ShelleyGenesis ShelleyGenesisConfig `yaml:"shelley"` + ShelleyTransEpoch int32 `yaml:"shellyTransEpoch" envconfig:"SHELLEY_TRANS_EPOCH"` + BlockProducer bool `yaml:"blockProducer" envconfig:"CARDANO_BLOCK_PRODUCER"` } type PrometheusConfig struct { @@ -61,6 +62,12 @@ type ByronGenesisConfig struct { SlotLength uint64 `yaml:"slotLength" envconfig:"BYRON_SLOT_LENGTH"` } +type ShelleyGenesisConfig struct { + EpochLength uint64 `yaml:"epochLength" envconfig:"SHELLEY_EPOCH_LENGTH"` + SlotLength uint64 `yaml:"slotLength" envconfig:"SHELLEY_SLOT_LENGTH"` + SlotsPerKESPeriod uint64 `yaml:"slotsPerKESPeriod" envconfig:"SHELLEY_SLOTS_PER_KES_PERIOD"` +} + // Singleton config instance with default values var globalConfig = &Config{ App: AppConfig{ @@ -111,6 +118,10 @@ func LoadConfig(configFile string) (*Config, error) { if err := globalConfig.populateByronGenesis(); err != nil { return nil, err } + // Populate ShelleyGenesis from named networks + if err := globalConfig.populateShelleyGenesis(); err != nil { + return nil, err + } // Populate ShelleyTransEpoch from named networks if err := globalConfig.populateShelleyTransEpoch(); err != nil { return nil, err @@ -218,3 +229,34 @@ func (c *Config) populateByronGenesis() error { c.Node.ByronGenesis.EpochLength = (10 * c.Node.ByronGenesis.K) return nil } + +// Populates ShelleyGenesisConfig from named networks +func (c *Config) populateShelleyGenesis() error { + if c.Node.ShelleyGenesis.EpochLength != 0 { + return nil + } + // Our slot length is always 1000 in supported networks + c.Node.ShelleyGenesis.SlotLength = 1000 + // Our slots per KES period is always 129600 in supported networks + c.Node.ShelleyGenesis.SlotsPerKESPeriod = 129600 + // Our epoch length is 432000, except sanchonet/preview + c.Node.ShelleyGenesis.EpochLength = 432000 + if c.App.Network != "" { + switch c.App.Network { + case "sancho": + c.Node.ShelleyGenesis.EpochLength = 86400 + case "preview": + c.Node.ShelleyGenesis.EpochLength = 86400 + } + } else if c.Node.Network != "" { + switch c.Node.Network { + case "sancho": + c.Node.ShelleyGenesis.EpochLength = 86400 + case "preview": + c.Node.ShelleyGenesis.EpochLength = 86400 + } + } else { + return fmt.Errorf("unable to populate shelley genesis config") + } + return nil +} diff --git a/main.go b/main.go index f7ac94f..c3dbc8b 100644 --- a/main.go +++ b/main.go @@ -131,8 +131,6 @@ func main() { os.Exit(1) } - // Set the genesisConfig - genesisConfig = getGenesisConfig(cfg) // Determine if we're P2P p2p = getP2P(ctx, processMetrics) // Set role @@ -490,11 +488,11 @@ var epochItemsLast = 0 func getEpochProgress() float32 { cfg := config.GetConfig() var epochProgress float32 - if promMetrics == nil || genesisConfig == nil { + if promMetrics == nil { epochProgress = float32(0.0) } else if promMetrics.EpochNum >= uint64(cfg.Node.ShelleyTransEpoch) { epochProgress = float32( - (float32(promMetrics.SlotInEpoch) / float32(genesisConfig.EpochLength)) * 100, + (float32(promMetrics.SlotInEpoch) / float32(cfg.Node.ShelleyGenesis.EpochLength)) * 100, ) } else { epochProgress = float32( @@ -509,8 +507,6 @@ func getEpochText(ctx context.Context) string { epochProgress := getEpochProgress() epochProgress1dec := fmt.Sprintf("%.1f", epochProgress) - // TODO: set this calculation - // epochTimeLeft := timeLeft(timeUntilNextEpoch()) sb.WriteString( fmt.Sprintf( @@ -566,7 +562,7 @@ func getChainText(ctx context.Context) string { len(strconv.FormatUint(promMetrics.MempoolTx, 10)) - len(strconv.FormatUint(mempoolTxKBytes, 10))) - tipRef := getSlotTipRef(genesisConfig) + tipRef := getSlotTipRef() tipDiff := (tipRef - promMetrics.SlotNum) // Row 1 @@ -594,7 +590,7 @@ func getChainText(ctx context.Context) string { )+"s[green]", "starting", )) - } else if tipDiff <= slotInterval(genesisConfig) { + } else if tipDiff <= 20 { sb.WriteString(fmt.Sprintf( " Tip (diff) : [white]%-"+strconv.Itoa(9)+"s[green]", fmt.Sprintf("%s 😀", strconv.FormatUint(tipDiff, 10)), @@ -889,7 +885,7 @@ func getNodeText(ctx context.Context) string { sb.WriteString(fmt.Sprintln()) } sb.WriteString(fmt.Sprintf(" [green]Uptime : [white]%s\n", - timeLeft(uptimes), + timeFromSeconds(uptimes), )) return fmt.Sprint(sb.String()) } diff --git a/node.go b/node.go index 83383ca..dd93031 100644 --- a/node.go +++ b/node.go @@ -17,23 +17,17 @@ package main import ( "context" "encoding/json" - "fmt" - "net" "os" "strings" - ouroboros "github.com/blinklabs-io/gouroboros" - "github.com/blinklabs-io/gouroboros/protocol/chainsync" - "github.com/blinklabs-io/gouroboros/protocol/localstatequery" "github.com/shirou/gopsutil/v3/process" "github.com/blinklabs-io/nview/internal/config" ) var ( - genesisConfig *localstatequery.GenesisConfigResult - p2p bool = true - role string = "Relay" + p2p bool = true + role string = "Relay" ) func setRole() { @@ -83,107 +77,3 @@ func getP2P(ctx context.Context, processMetrics *process.Process) bool { } return p2p } - -func buildLocalStateQueryConfig() localstatequery.Config { - return localstatequery.NewConfig() -} - -//nolint:unused -func buildChainSyncConfig() chainsync.Config { - return chainsync.NewConfig() -} - -func createClientConnection(cfg *config.Config) net.Conn { - var err error - var conn net.Conn - var dialProto string - var dialAddress string - if cfg.Node.SocketPath != "" { - dialProto = "unix" - dialAddress = cfg.Node.SocketPath - } else { - return conn - } - - conn, err = net.Dial(dialProto, dialAddress) - if err != nil { - fmt.Printf("ERROR: %s\n", err) - os.Exit(1) - } - return conn -} - -// Get Genesis Config from a running node using Ouroboros NtC -func getGenesisConfig(cfg *config.Config) *localstatequery.GenesisConfigResult { - var result *localstatequery.GenesisConfigResult - // Get a connection and setup our error channels - conn := createClientConnection(cfg) - if conn == nil { - return result - } - errorChan := make(chan error) - go func() { - for { - err := <-errorChan - fmt.Printf("ERROR: %s\n", err) - os.Exit(1) - } - }() - // Configure our Ouroboros connection - oConn, err := ouroboros.NewConnection( - ouroboros.WithConnection(conn), - ouroboros.WithNetworkMagic(uint32(cfg.Node.NetworkMagic)), - ouroboros.WithErrorChan(errorChan), - ouroboros.WithNodeToNode(false), - ouroboros.WithKeepAlive(false), - ouroboros.WithLocalStateQueryConfig(buildLocalStateQueryConfig()), - ) - if err != nil { - return result - } - // Query our client - oConn.LocalStateQuery().Client.Start() - result, err = oConn.LocalStateQuery().Client.GetGenesisConfig() - if err != nil { - return result - } - return result -} - -// Get remote tip -// -//nolint:unused -func getRemoteTip(cfg *config.Config, address string) *chainsync.Tip { - var result *chainsync.Tip - // Get a connection and setup our error channels - conn := createRemoteClientConnection(address) - if conn == nil { - return result - } - errorChan := make(chan error) - go func() { - for { - err := <-errorChan - fmt.Printf("ERROR: %s\n", err) - os.Exit(1) - } - }() - oConn, err := ouroboros.NewConnection( - ouroboros.WithConnection(conn), - ouroboros.WithNetworkMagic(uint32(cfg.Node.NetworkMagic)), - ouroboros.WithErrorChan(errorChan), - ouroboros.WithNodeToNode(true), - ouroboros.WithKeepAlive(false), - ouroboros.WithChainSyncConfig(buildChainSyncConfig()), - ) - if err != nil { - return result - } - // Query our client - oConn.ChainSync().Client.Start() - result, err = oConn.ChainSync().Client.GetCurrentTip() - if err != nil { - return result - } - return result -}