2025-07-15 14:59:32 +03:00
|
|
|
package cfg
|
2025-07-08 04:28:42 +03:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/hex"
|
2025-07-12 12:07:50 +03:00
|
|
|
"encoding/json"
|
2025-07-08 04:28:42 +03:00
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
2025-07-15 14:59:32 +03:00
|
|
|
"io"
|
2025-07-08 04:28:42 +03:00
|
|
|
"lbc/yggdrasil"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strconv"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
|
cfg "github.com/tendermint/tendermint/config"
|
|
|
|
|
"github.com/tendermint/tendermint/p2p"
|
|
|
|
|
"github.com/tendermint/tendermint/privval"
|
|
|
|
|
tmTypes "github.com/tendermint/tendermint/types"
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-15 14:59:32 +03:00
|
|
|
type Config = cfg.Config
|
|
|
|
|
|
2025-07-08 04:28:42 +03:00
|
|
|
var (
|
|
|
|
|
yggListenPort = 4224
|
|
|
|
|
yggKeyPath = flag.String("ygg-key", "./config/yggdrasil.key", "Path to Yggdrasil key file")
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-15 14:59:32 +03:00
|
|
|
func copyFile(src, dst string) error {
|
|
|
|
|
in, err := os.Open(src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer in.Close()
|
|
|
|
|
|
|
|
|
|
if err = os.MkdirAll(filepath.Dir(dst), 0o700); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out, err := os.Create(dst)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
_ = out.Sync()
|
|
|
|
|
_ = out.Close()
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
_, err = io.Copy(out, in)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func LoadViperConfig(path string) (*viper.Viper, error) {
|
|
|
|
|
v := viper.New()
|
|
|
|
|
v.SetConfigFile(path)
|
|
|
|
|
err := v.ReadInConfig()
|
|
|
|
|
return v, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 21:01:30 +03:00
|
|
|
func InitTendermintFiles(config *cfg.Config, isGenesis bool, chainName string) error {
|
2025-07-08 04:28:42 +03:00
|
|
|
if err := os.MkdirAll(filepath.Dir(config.PrivValidatorKeyFile()), 0700); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Join(config.RootDir, "data"), 0700); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pv := privval.GenFilePV(
|
|
|
|
|
config.PrivValidatorKeyFile(),
|
|
|
|
|
config.PrivValidatorStateFile(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if _, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key, err := pv.GetPubKey()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 21:01:30 +03:00
|
|
|
pv.Save()
|
|
|
|
|
|
|
|
|
|
if isGenesis {
|
|
|
|
|
// Genesis
|
|
|
|
|
genDoc := &tmTypes.GenesisDoc{
|
|
|
|
|
ChainID: chainName,
|
|
|
|
|
GenesisTime: time.Now(),
|
|
|
|
|
ConsensusParams: tmTypes.DefaultConsensusParams(),
|
|
|
|
|
Validators: []tmTypes.GenesisValidator{
|
|
|
|
|
{
|
|
|
|
|
Address: key.Address(),
|
|
|
|
|
PubKey: key,
|
|
|
|
|
Power: 10,
|
|
|
|
|
Name: config.Moniker,
|
|
|
|
|
},
|
2025-07-08 04:28:42 +03:00
|
|
|
},
|
2025-07-12 21:01:30 +03:00
|
|
|
AppHash: []byte{},
|
|
|
|
|
}
|
2025-07-08 04:28:42 +03:00
|
|
|
|
2025-07-12 21:01:30 +03:00
|
|
|
return genDoc.SaveAs(config.GenesisFile())
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2025-07-08 04:28:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeYggdrasilKey(path string) {
|
|
|
|
|
bytes := yggdrasil.GeneratePrivateKey()
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hexKey := hex.EncodeToString(bytes[:])
|
|
|
|
|
os.WriteFile(path, []byte(hexKey), 0600)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 12:07:50 +03:00
|
|
|
func WriteConfig(config *cfg.Config, configPath *string, nodeInfo p2p.NodeInfo) *viper.Viper {
|
2025-07-08 04:28:42 +03:00
|
|
|
writeYggdrasilKey(*yggKeyPath)
|
|
|
|
|
pubkey, err := yggdrasil.GetPublicKey(*yggKeyPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "yggdrasil key not found: %v\n", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
domain := hex.EncodeToString(pubkey) + ".pk.ygg"
|
|
|
|
|
fmt.Printf("Yggdrasil node domain: %s\n", domain)
|
|
|
|
|
|
|
|
|
|
v := viper.New()
|
|
|
|
|
v.SetConfigFile(*configPath)
|
|
|
|
|
v.SetConfigType("toml")
|
|
|
|
|
|
|
|
|
|
v.Set("moniker", config.Moniker)
|
|
|
|
|
v.Set("db_backend", config.DBBackend)
|
|
|
|
|
v.Set("db_dir", config.DBDir())
|
|
|
|
|
v.Set("log_level", config.LogLevel)
|
|
|
|
|
v.Set("log_format", config.LogFormat)
|
|
|
|
|
v.Set("genesis_file", config.GenesisFile())
|
|
|
|
|
v.Set("node_key_file", config.NodeKeyFile())
|
|
|
|
|
v.Set("abci", config.ABCI)
|
|
|
|
|
v.Set("filter_peers", config.FilterPeers)
|
|
|
|
|
|
|
|
|
|
v.Set("priv_validator", map[string]any{
|
|
|
|
|
"key_file": config.PrivValidatorKeyFile(),
|
|
|
|
|
"state_file": config.PrivValidatorStateFile(),
|
|
|
|
|
"laddr": config.PrivValidatorListenAddr,
|
|
|
|
|
"client_certificate_file": "",
|
|
|
|
|
"client_key_file": "",
|
|
|
|
|
"root_ca_file": "",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
v.Set("yggdrasil", map[string]any{
|
|
|
|
|
"admin_listen": "none",
|
|
|
|
|
"peers": "auto",
|
|
|
|
|
"allowed_public_keys": []string{},
|
|
|
|
|
"private_key_file": *yggKeyPath,
|
|
|
|
|
})
|
|
|
|
|
|
2025-07-12 21:01:30 +03:00
|
|
|
if a := ReadP2Peers(*configPath); a == "" {
|
|
|
|
|
nodeId := nodeInfo.ID()
|
|
|
|
|
myPeer := yggdrasil.GetYggdrasilAddress(v)
|
|
|
|
|
config.P2P.PersistentPeers = string(nodeId) + "@ygg://[" + myPeer + "]:" + strconv.Itoa(yggListenPort)
|
|
|
|
|
} else {
|
|
|
|
|
config.P2P.PersistentPeers = a
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-08 04:28:42 +03:00
|
|
|
v.Set("p2p", map[string]interface{}{
|
|
|
|
|
"use_legacy": false,
|
|
|
|
|
"queue_type": "priority",
|
2025-07-12 12:07:50 +03:00
|
|
|
"laddr": strconv.Itoa(yggListenPort) + ":127.0.0.1:8000",
|
2025-07-08 04:28:42 +03:00
|
|
|
"external_address": "", // will be set automatically by Tendermint if needed
|
|
|
|
|
"upnp": false,
|
|
|
|
|
"bootstrap_peers": "",
|
|
|
|
|
"persistent_peers": config.P2P.PersistentPeers,
|
|
|
|
|
"addr_book_file": "config/addrbook.json",
|
2025-07-12 21:01:30 +03:00
|
|
|
"addr_book_strict": false,
|
2025-07-08 04:28:42 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
err = v.WriteConfigAs(*configPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "error writing config: %v\n", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 12:07:50 +03:00
|
|
|
return v
|
2025-07-08 04:28:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ReadConfig(configFile string) (*cfg.Config, error) {
|
|
|
|
|
config := cfg.DefaultConfig()
|
|
|
|
|
config.RootDir = filepath.Dir(filepath.Dir(configFile))
|
|
|
|
|
viper.SetConfigFile(configFile)
|
|
|
|
|
viper.SetConfigType("toml")
|
|
|
|
|
|
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("viper read config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := viper.Unmarshal(config); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("viper unmarshal: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := config.ValidateBasic(); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("config invalid: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return config, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func DefaultConfig() *cfg.Config {
|
|
|
|
|
return cfg.DefaultConfig()
|
|
|
|
|
}
|
2025-07-12 12:07:50 +03:00
|
|
|
|
2025-07-12 21:01:30 +03:00
|
|
|
func ReadP2Peers(configFile string) string {
|
|
|
|
|
var genesis map[string]any
|
|
|
|
|
genesisJson, err := os.ReadFile(filepath.Join(filepath.Dir(configFile), "genesis.json"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
_ = json.Unmarshal(genesisJson, &genesis)
|
|
|
|
|
p2peers, ok := genesis["p2peers"].(string)
|
|
|
|
|
if ok && p2peers != "" {
|
|
|
|
|
return p2peers
|
|
|
|
|
} else {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 13:30:41 +03:00
|
|
|
func UpdateGenesisJson(nodeInfo p2p.NodeInfo, v *viper.Viper, defaultConfigDirectoryPath string) {
|
2025-07-12 21:01:30 +03:00
|
|
|
genesisJsonPath := filepath.Join(defaultConfigDirectoryPath, "genesis.json")
|
|
|
|
|
file, err := os.ReadFile(genesisJsonPath)
|
2025-07-12 12:07:50 +03:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dat map[string]any
|
|
|
|
|
if err := json.Unmarshal(file, &dat); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 21:01:30 +03:00
|
|
|
myPeer := yggdrasil.GetYggdrasilAddress(v)
|
2025-07-12 12:07:50 +03:00
|
|
|
|
|
|
|
|
p2peers := fmt.Sprintf("%s@ygg://[%s]:%d", nodeInfo.ID(), myPeer, yggListenPort)
|
|
|
|
|
dat["p2peers"] = p2peers
|
|
|
|
|
|
|
|
|
|
out, err := json.MarshalIndent(dat, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2025-07-12 21:01:30 +03:00
|
|
|
if err := os.WriteFile(genesisJsonPath, out, 0o644); err != nil {
|
2025-07-12 12:07:50 +03:00
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-15 14:59:32 +03:00
|
|
|
|
|
|
|
|
func InitGenesis(chainName, defaultConfigPath string) (*cfg.Config, *viper.Viper) {
|
|
|
|
|
config := cfg.DefaultConfig()
|
|
|
|
|
config.RootDir = filepath.Dir(filepath.Dir(defaultConfigPath))
|
|
|
|
|
|
|
|
|
|
nodeinfo := p2p.DefaultNodeInfo{}
|
|
|
|
|
viper := WriteConfig(config, &defaultConfigPath, nodeinfo)
|
|
|
|
|
if err := InitTendermintFiles(config, true, chainName); err != nil {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "Failed to init files: %v\n", err)
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return config, viper
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func InitJoiner(chainName, defaultConfigPath, path string) error {
|
|
|
|
|
config := cfg.DefaultConfig()
|
|
|
|
|
config.RootDir = filepath.Dir(filepath.Dir(defaultConfigPath))
|
|
|
|
|
|
|
|
|
|
if err := copyFile(path, config.GenesisFile()); err != nil {
|
|
|
|
|
fmt.Fprintln(os.Stderr, "не удалось скопировать genesis.json:", err)
|
|
|
|
|
os.Exit(3)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nodeinfo := p2p.DefaultNodeInfo{}
|
|
|
|
|
WriteConfig(config, &defaultConfigPath, nodeinfo)
|
|
|
|
|
//viper := cfg.WriteConfig(config, &defaultConfigPath, nodeinfo)
|
|
|
|
|
if err := InitTendermintFiles(config, false, chainName); err != nil {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "Failed to init files: %v\n", err)
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//node, db, err := buildNode()
|
|
|
|
|
//if err != nil {
|
|
|
|
|
// panic(err)
|
|
|
|
|
//}
|
|
|
|
|
//defer db.Close()
|
|
|
|
|
|
|
|
|
|
//cfg.WriteConfig(config, &defaultConfigPath, node.NodeInfo())
|
|
|
|
|
|
|
|
|
|
//if err := os.MkdirAll(filepath.Join(config.RootDir, "data"), 0o700); err != nil {
|
|
|
|
|
// fmt.Fprintln(os.Stderr, "не удалось создать директорию data")
|
|
|
|
|
// os.Exit(1)
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
if _, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()); err != nil {
|
|
|
|
|
return fmt.Errorf("ошибка генерации node_key.json")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pv := privval.GenFilePV(
|
|
|
|
|
config.PrivValidatorKeyFile(),
|
|
|
|
|
config.PrivValidatorStateFile(),
|
|
|
|
|
)
|
|
|
|
|
pv.Save()
|
|
|
|
|
return nil
|
|
|
|
|
}
|