Skip to content

Commit

Permalink
feat: hard-code shelley genesis values per network (#190)
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Gianelloni <[email protected]>
  • Loading branch information
wolf31o2 authored May 23, 2024
1 parent 0c57cbe commit aaa273d
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 175 deletions.
49 changes: 3 additions & 46 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import (
"net/http"
"time"

"github.com/blinklabs-io/gouroboros/protocol/localstatequery"

"github.com/blinklabs-io/nview/internal/config"
)

Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand All @@ -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)
}
58 changes: 50 additions & 8 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
14 changes: 5 additions & 9 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -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())
}
Expand Down
114 changes: 2 additions & 112 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
}

0 comments on commit aaa273d

Please sign in to comment.