2023-08-21 03:50:38 +00:00
|
|
|
|
package config
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/aes"
|
|
|
|
|
"crypto/cipher"
|
|
|
|
|
"crypto/rand"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/fs"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2024-06-08 11:32:45 +00:00
|
|
|
|
"strings"
|
2023-08-21 03:50:38 +00:00
|
|
|
|
|
|
|
|
|
"github.com/cloudflare/circl/sign/ed448"
|
|
|
|
|
"github.com/libp2p/go-libp2p/core/crypto"
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Config struct {
|
2023-10-09 04:52:19 +00:00
|
|
|
|
Key *KeyConfig `yaml:"key"`
|
|
|
|
|
P2P *P2PConfig `yaml:"p2p"`
|
|
|
|
|
Engine *EngineConfig `yaml:"engine"`
|
|
|
|
|
DB *DBConfig `yaml:"db"`
|
|
|
|
|
ListenGRPCMultiaddr string `yaml:"listenGrpcMultiaddr"`
|
|
|
|
|
ListenRestMultiaddr string `yaml:"listenRESTMultiaddr"`
|
|
|
|
|
LogFile string `yaml:"logFile"`
|
2023-08-21 03:50:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewConfig(configPath string) (*Config, error) {
|
|
|
|
|
file, err := os.Open(configPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
d := yaml.NewDecoder(file)
|
|
|
|
|
config := &Config{}
|
|
|
|
|
|
|
|
|
|
if err := d.Decode(&config); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return config, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-23 01:38:20 +00:00
|
|
|
|
var BootstrapPeers = []string{
|
2024-06-08 11:32:45 +00:00
|
|
|
|
"/dns/bootstrap.quilibrium.com/udp/8336/quic-v1/p2p/Qme3g6rJWuz8HVXxpDb7aV2hiFq8bZJNqxMmwzmASzfq1M",
|
|
|
|
|
"/dns/quaalude.quilibrium.com/udp/8336/quic-v1/p2p/QmYruNcruYNgyTKeUqJSxjbuTFYWYDw2r5df9YKMGWCRKA",
|
|
|
|
|
"/dns/quecifer.quilibrium.com/udp/8336/quic-v1/p2p/QmdWF9bGTH5mwJXkxrG859HA5r34MxXtMSTuEikSMDSESv",
|
|
|
|
|
"/dns/quantum.quilibrium.com/udp/8336/quic-v1/p2p/QmbmVeKnSWK9HHAQHSS714XU3gx77TrS356JmHmKFj7q7M",
|
|
|
|
|
"/dns/quidditas.quilibrium.com/udp/8336/quic-v1/p2p/QmR1rF5E9zAob9FZyMF7uTUM27D7GYtX9RaiSsNY9UP72J",
|
|
|
|
|
"/dns/quillon.quilibrium.com/udp/8336/quic-v1/p2p/QmWgHv6z3tyimW4JvrvYRgsJEimgV7J2xbE7QEpFNPvAnB",
|
|
|
|
|
"/dns/quidditch.quilibrium.com/udp/8336/quic-v1/p2p/QmbZEGuinaCndj4XLb6fteZmjmP3C1Tsgijmc5BGuUk8Ma",
|
|
|
|
|
"/dns/quagmire.quilibrium.com/udp/8336/quic-v1/p2p/QmaQ9KAaKtqXhYSQ5ARQNnn8B8474cWGvvD6PgJ4gAtMrx",
|
|
|
|
|
"/ip4/204.186.74.46/udp/8316/quic-v1/p2p/QmeqBjm3iX7sdTieyto1gys5ruQrQNPKfaTGcVQQWJPYDV",
|
|
|
|
|
"/ip4/185.143.102.84/udp/8336/quic-v1/p2p/Qmce68gLLq9eMdwCcmd1ptfoC2nVoe861LF1cjdVHC2DwK",
|
|
|
|
|
"/ip4/65.109.17.13/udp/8336/quic-v1/p2p/Qmc35n99eojSvW3PkbfBczJoSX92WmnnKh3Fg114ok3oo4",
|
|
|
|
|
"/ip4/65.108.194.84/udp/8336/quic-v1/p2p/QmP8C7g9ZRiWzhqN2AgFu5onS6HwHzR6Vv1TCHxAhnCSnq",
|
|
|
|
|
"/dns/quil.dfcnodes.eu/udp/8336/quic-v1/p2p/QmQaFmbYVrKSwoen5UQdaqyDq4QhXfSSLDVnYpYD4SF9tX",
|
2024-07-19 02:57:41 +00:00
|
|
|
|
"/ip4/15.204.100.222/udp/8336/quic-v1/p2p/Qmef3Z3RvGg49ZpDPcf2shWtJNgPJNpXrowjUcfz23YQ3V",
|
|
|
|
|
"/ip4/69.197.174.35/udp/8336/quic-v1/p2p/QmeprCaZKiymofPJgnp2ANR3F4pRus9PHHaxnJDh1Jwr1p",
|
|
|
|
|
"/ip4/70.36.102.32/udp/8336/quic-v1/p2p/QmYriGRXCUiwFodqSoS4GgEcD7UVyxXPeCgQKmYne3iLSF",
|
2024-07-17 19:45:16 +00:00
|
|
|
|
"/ip4/204.12.220.2/udp/8336/quic-v1/p2p/QmRw5Tw4p5v2vLPvVSAkQEiRPQGnWk9HM4xiSvgxF82CCw",
|
2024-07-19 02:52:43 +00:00
|
|
|
|
"/ip4/209.159.149.14/udp/8336/quic-v1/p2p/Qmcq4Lmw45tbodvdRWZ8iGgy3rUcR3dikHTj1fBXP8VJqv",
|
2024-07-20 00:23:24 +00:00
|
|
|
|
"/ip4/148.251.9.90/udp/8336/quic-v1/p2p/QmRpKmQ1W83s6moBFpG6D6nrttkqdQSbdCJpvfxDVGcs38",
|
2024-07-20 00:21:36 +00:00
|
|
|
|
"/ip4/35.232.113.144/udp/8336/quic-v1/p2p/QmWxkBc7a17ZsLHhszLyTvKsoHMKvKae2XwfQXymiU66md",
|
|
|
|
|
"/ip4/34.87.85.78/udp/8336/quic-v1/p2p/QmTGguT5XhtvZZwTLnNQTN8Bg9eUm1THWEneXXHGhMDPrz",
|
|
|
|
|
"/ip4/34.81.199.27/udp/8336/quic-v1/p2p/QmTMMKpzCKJCwrnUzNu6tNj4P1nL7hVqz251245wsVpGNg",
|
|
|
|
|
"/ip4/34.143.255.235/udp/8336/quic-v1/p2p/QmeifsP6Kvq8A3yabQs6CBg7prSpDSqdee8P2BDQm9EpP8",
|
|
|
|
|
"/ip4/34.34.125.238/udp/8336/quic-v1/p2p/QmZdSyBJLm9UiDaPZ4XDkgRGXUwPcHJCmKoH6fS9Qjyko4",
|
|
|
|
|
"/ip4/34.80.245.52/udp/8336/quic-v1/p2p/QmNmbqobt82Vre5JxUGVNGEWn2HsztQQ1xfeg6mx7X5u3f",
|
2024-07-16 22:42:56 +00:00
|
|
|
|
"/dns/bravo-1.qcommander.sh/udp/8336/quic-v1/p2p/QmWFK1gVuhEqZdr8phTo3QbyLwjYmyivx31Zubqt7oR4XB",
|
2024-07-20 00:15:26 +00:00
|
|
|
|
"/ip4/138.201.203.208/udp/8336/quic-v1/p2p/QmbNhSTd4Y64ZCbV2gAXYR4ZFDdfRBMfrgWsNg99JHxsJo",
|
2024-04-23 01:38:20 +00:00
|
|
|
|
// purged peers (keep your node online to return to this list)
|
2024-06-08 11:32:45 +00:00
|
|
|
|
// "/ip4/204.186.74.47/udp/8317/quic-v1/p2p/Qmd233pLUDvcDW3ama27usfbG1HxKNh1V9dmWVW1SXp1pd",
|
|
|
|
|
// "/ip4/186.233.184.181/udp/8336/quic-v1/p2p/QmW6QDvKuYqJYYMP5tMZSp12X3nexywK28tZNgqtqNpEDL",
|
|
|
|
|
// "/dns/quil.zanshindojo.org/udp/8336/quic-v1/p2p/QmXbbmtS5D12rEc4HWiHWr6e83SCE4jeThPP4VJpAQPvXq",
|
|
|
|
|
// "/ip4/144.76.104.93/udp/8336/quic-v1/p2p/QmZejZ8DBGQ6foX9recW73GA6TqL6hCMX9ETWWW1Fb8xtx",
|
|
|
|
|
// "/ip4/207.246.81.38/udp/8336/quic-v1/p2p/QmPBYgDy7snHon7PAn8nv1shApQBQz1iHb2sBBS8QSgQwW",
|
|
|
|
|
// "/dns/abyssia.fr/udp/8336/quic-v1/p2p/QmS7C1UhN8nvzLJgFFf1uspMRrXjJqThHNN6AyEXp6oVUB",
|
|
|
|
|
// "/ip4/51.15.18.247/udp/8336/quic-v1/p2p/QmYVaHXdFmHFeTa6oPixgjMVag6Ex7gLjE559ejJddwqzu",
|
2024-04-23 01:38:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-21 03:50:38 +00:00
|
|
|
|
func LoadConfig(configPath string, proverKey string) (*Config, error) {
|
|
|
|
|
info, err := os.Stat(configPath)
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
fmt.Println("Creating config directory " + configPath)
|
|
|
|
|
if err = os.Mkdir(configPath, fs.FileMode(0700)); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !info.IsDir() {
|
|
|
|
|
panic(configPath + " is not a directory")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file, err := os.Open(filepath.Join(configPath, "config.yml"))
|
|
|
|
|
saveDefaults := false
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
|
|
|
saveDefaults = true
|
|
|
|
|
} else {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 02:43:35 +00:00
|
|
|
|
genesisSeed := "5768656e20696e2074686520436f75727365206f6620746563686e6f6c6f676963616c206576656e74732c206974206265636f6d6573206e656365737361727920666f72206f6e65206e6574776f726b0a746f20646973736f6c766520746865202070726f746f636f6c2062616e647320776869636820206861766520636f6e6e6563746564207468656d20207769746820616e6f746865722c20616e6420746f0a617373756d6520616d6f6e672074686520706f77657273206f6620746865207765622c202074686520736570617261746520616e6420657175616c2073746174696f6e20746f207768696368207468650a4c617773206f6620546563686e6f6c6f677920616e6420206f6620546563686e6f6c6f6779277320537465776172647320656e7469746c65207468656d2c20206120646563656e7420726573706563740a746f20746865206f70696e696f6e73206f6620416c6c20726571756972657320207468617420746865792073686f756c64206465636c617265207468652063617573657320776869636820696d70656c0a7468656d20746f207468652073657061726174696f6e2e0a0a576520686f6c642074686573652074727574687320746f2062652073656c662d65766964656e742c207468617420616c6c20636f676e697469766520656e7469746965732061726520637265617465640a657175616c2c2074686174202074686579206172652020656e646f7765642062792020746865206f7264657220206f66206c696665202077697468206365727461696e2020756e616c69656e61626c650a5269676874732c2020746861742020616d6f6e6720207468657365202061726520204c6966652c20204c6962657274792c2020507269766163792020616e64202050726f70657274792e2020546861740a686973746f726963616c6c7920746f20207365637572652020746865736520207269676874732c202050726f746f636f6c7320206861766520206265656e2020696e737469747574656420616d6f6e670a656e7469746965732c2020617320476f7665726e6d656e74732068617665206265656e2020696e737469747574656420616d6f6e67204d656e2c2020686176696e6720646572697665642074686569720a6a75737420706f776572732066726f6d2074686520636f6e73656e74206f662074686520676f7665726e65642c2054686174207768656e6576657220616e7920466f726d206f662050726f746f636f6c0a6265636f6d6573206465737472756374697665206f6620746865736520656e64732c20697420697320746865205269676874206f6620416c6c20746f20616c746572206f7220746f2061626f6c6973680a69742c20616e6420746f20696e73746974757465206e65772050726f746f636f6c732c206c6179696e672069747320666f756e646174696f6e206f6e2073756368207072696e6369706c657320616e640a6f7267616e697a696e672069747320706f7765727320696e207375636820666f726d2c2020617320746f207468656d207368616c6c207365656d206d6f7374206c696b656c7920746f206566666563740a74686569722053616665747920616e642048617070696e6573732e2050727564656e63652c2020696e646565642c202077696c6c206469637461746520746861742050726f746f636f6c73206c6f6e670a65737461626c6973686564202073686f756c6420206e6f742020626520206368616e6765642020666f7220206c696768742020616e6420207472616e7369656e7420206361757365733b202020616e640a6163636f7264696e676c7920616c6c20657870657269656e6365206861746820736865776e2c2074686174206d616e6b696e6420617265206d6f726520646973706f73656420746f207375666665722c0a7768696c65206576696c73206172652073756666657261626c652c207468616e20746f20207269676874207468656d73656c7665732062792061626f6c697368696e672074686520666f726d7320746f0a776869636820746865792020617265206163637573746f6d65642e2020427574207768656e202061206c6f6e6720747261696e20206f66206162757365732020616e642075737572706174696f6e732c0a7075727375696e672020696e7661726961626c7920207468652073616d6520204f626a656374206576696e6365732020612064657369676e2020746f2072656475636520207468656d2020756e6465720a6162736f6c7574652020446573706f7469736d2c20206974206973202074686569722072696768742c202069742069732020746865697220647574792c2020746f207468726f77206f666620737563680a50726f746f636f6c732c2020616e6420746f2070726f76696465206e6577204775617264732020666f72207468656972206675747572652073656375726974792e20205375636820686173206265656e0a746865202070617469656e742020737566666572616e636520206f66202074686520207765623b2020616e6420207375636820697320206e6f772074686520206e6563657373697479202077686963680a636f6e73747261696e73207468656d20746f20616c74657220746865697220666f726d65722053797374656d73206f66204e6574776f726b696e672e202054686520686973746f7279206f66207468650a70726573656e7420496e7465726e6574206973206120686973746f727920206f6620726570656174656420696e6a757269657320616e642075737572706174696f6e732c2020
|
|
|
|
|
|
2023-08-21 03:50:38 +00:00
|
|
|
|
config := &Config{
|
|
|
|
|
DB: &DBConfig{
|
|
|
|
|
Path: configPath + "/store",
|
|
|
|
|
},
|
|
|
|
|
Key: &KeyConfig{
|
|
|
|
|
KeyStore: KeyManagerTypeFile,
|
|
|
|
|
KeyStoreFile: &KeyStoreFileConfig{
|
|
|
|
|
Path: filepath.Join(configPath, "keys.yml"),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
P2P: &P2PConfig{
|
2024-06-08 11:32:45 +00:00
|
|
|
|
ListenMultiaddr: "/ip4/0.0.0.0/udp/8336/quic-v1",
|
2024-04-23 01:38:20 +00:00
|
|
|
|
BootstrapPeers: BootstrapPeers,
|
2023-09-25 02:43:35 +00:00
|
|
|
|
PeerPrivKey: "",
|
2024-05-25 05:22:50 +00:00
|
|
|
|
Network: 0,
|
2023-08-21 03:50:38 +00:00
|
|
|
|
},
|
|
|
|
|
Engine: &EngineConfig{
|
|
|
|
|
ProvingKeyId: "default-proving-key",
|
|
|
|
|
Filter: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
2023-09-25 02:43:35 +00:00
|
|
|
|
GenesisSeed: genesisSeed,
|
2023-08-21 03:50:38 +00:00
|
|
|
|
MaxFrames: -1,
|
|
|
|
|
PendingCommitWorkers: 4,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if saveDefaults {
|
|
|
|
|
fmt.Println("Generating default config...")
|
|
|
|
|
fmt.Println("Generating random host key...")
|
|
|
|
|
privkey, _, err := crypto.GenerateEd448Key(rand.Reader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hostKey, err := privkey.Raw()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config.P2P.PeerPrivKey = hex.EncodeToString(hostKey)
|
|
|
|
|
|
|
|
|
|
fmt.Println("Generating keystore key...")
|
|
|
|
|
keystoreKey := make([]byte, 32)
|
|
|
|
|
if _, err := rand.Read(keystoreKey); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config.Key.KeyStoreFile.EncryptionKey = hex.EncodeToString(keystoreKey)
|
|
|
|
|
|
2024-02-19 06:29:52 +00:00
|
|
|
|
if multiAddr := os.Getenv("DEFAULT_LISTEN_GRPC_MULTIADDR"); multiAddr != "" {
|
|
|
|
|
config.ListenGRPCMultiaddr = multiAddr
|
|
|
|
|
config.ListenRestMultiaddr = os.Getenv("DEFAULT_LISTEN_REST_MULTIADDR")
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-29 06:02:49 +00:00
|
|
|
|
if multiAddr := os.Getenv("DEFAULT_STATS_MULTIADDR"); multiAddr != "" {
|
|
|
|
|
config.Engine.StatsMultiaddr = multiAddr
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-21 03:50:38 +00:00
|
|
|
|
fmt.Println("Saving config...")
|
|
|
|
|
if err = SaveConfig(configPath, config); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyfile, err := os.OpenFile(
|
|
|
|
|
filepath.Join(configPath, "keys.yml"),
|
|
|
|
|
os.O_CREATE|os.O_RDWR,
|
2024-02-29 06:02:49 +00:00
|
|
|
|
fs.FileMode(0600),
|
2023-08-21 03:50:38 +00:00
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if proverKey != "" {
|
|
|
|
|
provingKey, err := hex.DecodeString(proverKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iv := [12]byte{}
|
|
|
|
|
rand.Read(iv[:])
|
|
|
|
|
aesCipher, err := aes.NewCipher(keystoreKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not construct cipher")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gcm, err := cipher.NewGCM(aesCipher)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not construct block")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ciphertext := gcm.Seal(nil, iv[:], provingKey, nil)
|
|
|
|
|
ciphertext = append(append([]byte{}, iv[:]...), ciphertext...)
|
|
|
|
|
|
|
|
|
|
provingPubKey := ed448.PrivateKey(provingKey).Public().(ed448.PublicKey)
|
|
|
|
|
|
|
|
|
|
keyfile.Write([]byte(
|
|
|
|
|
"default-proving-key:\n id: default-proving-key\n" +
|
|
|
|
|
" type: 0\n privateKey: " + hex.EncodeToString(ciphertext) + "\n" +
|
|
|
|
|
" publicKey: " + hex.EncodeToString(provingPubKey) + "\n"))
|
|
|
|
|
} else {
|
|
|
|
|
keyfile.Write([]byte("null:\n"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyfile.Close()
|
|
|
|
|
|
|
|
|
|
if file, err = os.Open(
|
|
|
|
|
filepath.Join(configPath, "config.yml"),
|
|
|
|
|
); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
d := yaml.NewDecoder(file)
|
|
|
|
|
if err := d.Decode(&config); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 02:43:35 +00:00
|
|
|
|
if config.Engine.GenesisSeed == "00" {
|
|
|
|
|
config.Engine.GenesisSeed = genesisSeed
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 11:32:45 +00:00
|
|
|
|
// upgrade quic string to quic-v1
|
|
|
|
|
if strings.HasSuffix(config.P2P.ListenMultiaddr, "/quic") {
|
|
|
|
|
config.P2P.ListenMultiaddr += "-v1"
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 08:08:44 +00:00
|
|
|
|
// Slight trick here to get people on the latest bootstrap list –
|
|
|
|
|
// if it's empty, always use latest, if it has the Q bootstrap node, always
|
|
|
|
|
// use latest.
|
|
|
|
|
if len(config.P2P.BootstrapPeers) == 0 ||
|
2024-04-23 01:38:20 +00:00
|
|
|
|
config.P2P.BootstrapPeers[0][:30] == "/dns/bootstrap.quilibrium.com/" {
|
|
|
|
|
config.P2P.BootstrapPeers = BootstrapPeers
|
2024-06-08 11:32:45 +00:00
|
|
|
|
} else {
|
|
|
|
|
peers := make([]string, len(config.P2P.BootstrapPeers))
|
|
|
|
|
for i, p := range config.P2P.BootstrapPeers {
|
|
|
|
|
// upgrade quic strings to quic-v1
|
|
|
|
|
peers[i] = strings.Replace(p, "/quic/", "/quic-v1/", 1)
|
|
|
|
|
}
|
|
|
|
|
config.P2P.BootstrapPeers = peers
|
2023-09-25 02:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-21 03:50:38 +00:00
|
|
|
|
return config, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SaveConfig(configPath string, config *Config) error {
|
|
|
|
|
file, err := os.OpenFile(
|
|
|
|
|
filepath.Join(configPath, "config.yml"),
|
|
|
|
|
os.O_CREATE|os.O_RDWR,
|
|
|
|
|
os.FileMode(0600),
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
d := yaml.NewEncoder(file)
|
|
|
|
|
|
|
|
|
|
if err := d.Encode(config); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|