ceremonyclient/node/main.go
2024-03-29 02:20:35 -05:00

599 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build !js && !wasm
package main
import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"io/fs"
"log"
"os"
"os/signal"
"path/filepath"
"runtime"
"runtime/pprof"
"strconv"
"syscall"
"time"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
"source.quilibrium.com/quilibrium/monorepo/node/utils"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pbnjay/memory"
"github.com/pkg/errors"
"source.quilibrium.com/quilibrium/monorepo/node/app"
"source.quilibrium.com/quilibrium/monorepo/node/config"
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/kzg"
"source.quilibrium.com/quilibrium/monorepo/node/rpc"
)
var (
configDirectory = flag.String(
"config",
filepath.Join(".", ".config"),
"the configuration directory",
)
balance = flag.Bool(
"balance",
false,
"print the node's confirmed token balance to stdout and exit",
)
dbConsole = flag.Bool(
"db-console",
false,
"starts the node in database console mode",
)
importPrivKey = flag.String(
"import-priv-key",
"",
"creates a new config using a specific key from the phase one ceremony",
)
peerId = flag.Bool(
"peer-id",
false,
"print the peer id to stdout from the config and exit",
)
cpuprofile = flag.String(
"cpuprofile",
"",
"write cpu profile to file",
)
memprofile = flag.String(
"memprofile",
"",
"write memory profile after 20m to this file",
)
nodeInfo = flag.Bool(
"node-info",
false,
"print node related information",
)
debug = flag.Bool(
"debug",
false,
"sets log output to debug (verbose)",
)
)
func main() {
flag.Parse()
if *memprofile != "" {
go func() {
for {
time.Sleep(5 * time.Minute)
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
pprof.WriteHeapProfile(f)
f.Close()
}
}()
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
if *balance {
config, err := config.LoadConfig(*configDirectory, "")
if err != nil {
panic(err)
}
printBalance(config)
return
}
if *peerId {
config, err := config.LoadConfig(*configDirectory, "")
if err != nil {
panic(err)
}
printPeerID(config.P2P)
return
}
if *importPrivKey != "" {
config, err := config.LoadConfig(*configDirectory, *importPrivKey)
if err != nil {
panic(err)
}
printPeerID(config.P2P)
fmt.Println("Import completed, you are ready for the launch.")
return
}
if *nodeInfo {
config, err := config.LoadConfig(*configDirectory, "")
if err != nil {
panic(err)
}
printNodeInfo(config)
return
}
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
if !*dbConsole {
printLogo()
printVersion()
fmt.Println(" ")
}
nodeConfig, err := config.LoadConfig(*configDirectory, "")
if err != nil {
panic(err)
}
clearIfTestData(*configDirectory, nodeConfig)
if *dbConsole {
console, err := app.NewDBConsole(nodeConfig)
if err != nil {
panic(err)
}
console.Run()
return
}
fmt.Println("Loading ceremony state and starting node...")
kzg.Init()
report := RunSelfTestIfNeeded(*configDirectory, nodeConfig)
var node *app.Node
if *debug {
node, err = app.NewDebugNode(nodeConfig, report)
} else {
node, err = app.NewNode(nodeConfig, report)
}
if err != nil {
panic(err)
}
repair(*configDirectory, node)
if nodeConfig.ListenGRPCMultiaddr != "" {
srv, err := rpc.NewRPCServer(
nodeConfig.ListenGRPCMultiaddr,
nodeConfig.ListenRestMultiaddr,
node.GetLogger(),
node.GetClockStore(),
node.GetKeyManager(),
node.GetPubSub(),
node.GetMasterClock(),
node.GetExecutionEngines(),
)
if err != nil {
panic(err)
}
go func() {
err := srv.Start()
if err != nil {
panic(err)
}
}()
}
node.Start()
<-done
node.Stop()
}
// Reintroduce at a later date
func RunCompaction(clockStore *store.PebbleClockStore) {
intrinsicFilter := append(
p2p.GetBloomFilter(application.CEREMONY_ADDRESS, 256, 3),
p2p.GetBloomFilterIndices(application.CEREMONY_ADDRESS, 65536, 24)...,
)
fmt.Println("running compaction")
if err := clockStore.Compact(
intrinsicFilter,
); err != nil {
if errors.Is(err, store.ErrNotFound) {
fmt.Println("missing compaction data, skipping for now", zap.Error(err))
} else {
panic(err)
}
}
fmt.Println("compaction complete")
}
func RunSelfTestIfNeeded(
configDir string,
nodeConfig *config.Config,
) *protobufs.SelfTestReport {
logger, _ := zap.NewProduction()
cores := runtime.GOMAXPROCS(0)
memory := memory.TotalMemory()
d, err := os.Stat(filepath.Join(configDir, "store"))
if d == nil {
err := os.Mkdir(filepath.Join(configDir, "store"), 0755)
if err != nil {
panic(err)
}
}
f, err := os.Stat(filepath.Join(configDir, "SELF_TEST"))
if f != nil {
if f.Size() != 0 {
report := &protobufs.SelfTestReport{}
selfTestBytes, err := os.ReadFile(filepath.Join(configDir, "SELF_TEST"))
if err != nil {
panic(err)
}
err = proto.Unmarshal(selfTestBytes, report)
if err != nil {
panic(err)
}
if report.Cores == uint32(cores) &&
binary.BigEndian.Uint64(report.Memory) == memory {
return report
}
}
logger.Info("no self-test report found, generating")
}
report := &protobufs.SelfTestReport{}
difficulty := nodeConfig.Engine.Difficulty
if difficulty == 0 {
difficulty = 10000
}
report.Difficulty = difficulty
frameProver := qcrypto.NewWesolowskiFrameProver(logger)
logger.Info("generating difficulty metric")
start := time.Now().UnixMilli()
_, err = frameProver.ProveMasterClockFrame(
&protobufs.ClockFrame{
Filter: []byte{0x00},
FrameNumber: 0,
Timestamp: 0,
Difficulty: difficulty,
ParentSelector: []byte{0x00},
Input: make([]byte, 516),
Output: make([]byte, 516),
},
0,
difficulty,
)
if err != nil {
panic(err)
}
end := time.Now().UnixMilli()
report.DifficultyMetric = end - start
logger.Info("generating entropy for commit/proof sizes")
p16bytes := make([]byte, 1024)
p128bytes := make([]byte, 8192)
p1024bytes := make([]byte, 65536)
p65536bytes := make([]byte, 4194304)
rand.Read(p16bytes)
rand.Read(p128bytes)
rand.Read(p1024bytes)
rand.Read(p65536bytes)
kzgProver := kzg.DefaultKZGProver()
p16, _ := kzgProver.BytesToPolynomial(p16bytes)
p128, _ := kzgProver.BytesToPolynomial(p128bytes)
p1024, _ := kzgProver.BytesToPolynomial(p1024bytes)
p65536, _ := kzgProver.BytesToPolynomial(p65536bytes)
logger.Info("generating 16 degree commitment metric")
start = time.Now().UnixMilli()
c16, err := kzgProver.Commit(p16)
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Commit_16Metric = end - start
logger.Info("generating 128 degree commitment metric")
start = time.Now().UnixMilli()
c128, err := kzgProver.Commit(p128)
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Commit_128Metric = end - start
logger.Info("generating 1024 degree commitment metric")
start = time.Now().UnixMilli()
c1024, err := kzgProver.Commit(p1024)
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Commit_1024Metric = end - start
logger.Info("generating 65536 degree commitment metric")
start = time.Now().UnixMilli()
c65536, err := kzgProver.Commit(p65536)
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Commit_65536Metric = end - start
logger.Info("generating 16 degree proof metric")
start = time.Now().UnixMilli()
_, err = kzgProver.Prove(p16, c16, p16[0])
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Proof_16Metric = end - start
logger.Info("generating 128 degree proof metric")
start = time.Now().UnixMilli()
_, err = kzgProver.Prove(p128, c128, p128[0])
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Proof_128Metric = end - start
logger.Info("generating 1024 degree proof metric")
start = time.Now().UnixMilli()
_, err = kzgProver.Prove(p1024, c1024, p1024[0])
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Proof_1024Metric = end - start
logger.Info("generating 65536 degree proof metric")
start = time.Now().UnixMilli()
_, err = kzgProver.Prove(p65536, c65536, p65536[0])
if err != nil {
panic(err)
}
end = time.Now().UnixMilli()
report.Proof_65536Metric = end - start
report.Cores = uint32(cores)
report.Memory = binary.BigEndian.AppendUint64([]byte{}, memory)
disk := utils.GetDiskSpace(nodeConfig.DB.Path)
report.Storage = binary.BigEndian.AppendUint64([]byte{}, disk)
logger.Info("writing report")
// tag: dusk capabilities report in v1.5
reportBytes, err := proto.Marshal(report)
if err != nil {
panic(err)
}
err = os.WriteFile(
filepath.Join(configDir, "SELF_TEST"),
reportBytes,
fs.FileMode(0600),
)
if err != nil {
panic(err)
}
return report
}
func clearIfTestData(configDir string, nodeConfig *config.Config) {
_, err := os.Stat(filepath.Join(configDir, "RELEASE_VERSION"))
if os.IsNotExist(err) {
fmt.Println("Clearing test data...")
err := os.RemoveAll(nodeConfig.DB.Path)
if err != nil {
panic(err)
}
versionFile, err := os.OpenFile(
filepath.Join(configDir, "RELEASE_VERSION"),
os.O_CREATE|os.O_RDWR,
fs.FileMode(0600),
)
if err != nil {
panic(err)
}
_, err = versionFile.Write([]byte{0x01, 0x00, 0x00})
if err != nil {
panic(err)
}
err = versionFile.Close()
if err != nil {
panic(err)
}
}
}
func repair(configDir string, node *app.Node) {
_, err := os.Stat(filepath.Join(configDir, "REPAIR"))
if os.IsNotExist(err) {
node.RunRepair()
repairFile, err := os.OpenFile(
filepath.Join(configDir, "REPAIR"),
os.O_CREATE|os.O_RDWR,
fs.FileMode(0600),
)
if err != nil {
panic(err)
}
_, err = repairFile.Write([]byte{0x00, 0x00, 0x01})
if err != nil {
panic(err)
}
err = repairFile.Close()
if err != nil {
panic(err)
}
}
}
func printBalance(config *config.Config) {
if config.ListenGRPCMultiaddr == "" {
_, _ = fmt.Fprintf(os.Stderr, "gRPC Not Enabled, Please Configure\n")
os.Exit(1)
}
conn, err := app.ConnectToNode(config)
if err != nil {
panic(err)
}
defer conn.Close()
client := protobufs.NewNodeServiceClient(conn)
balance, err := app.FetchTokenBalance(client)
if err != nil {
panic(err)
}
fmt.Println("Owned balance:", balance.Owned, "QUIL")
fmt.Println("Unconfirmed balance:", balance.UnconfirmedOwned, "QUIL")
}
func printPeerID(p2pConfig *config.P2PConfig) {
peerPrivKey, err := hex.DecodeString(p2pConfig.PeerPrivKey)
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
privKey, err := crypto.UnmarshalEd448PrivateKey(peerPrivKey)
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
pub := privKey.GetPublic()
id, err := peer.IDFromPublicKey(pub)
if err != nil {
panic(errors.Wrap(err, "error getting peer id"))
}
fmt.Println("Peer ID: " + id.String())
}
func printNodeInfo(cfg *config.Config) {
if cfg.ListenGRPCMultiaddr == "" {
_, _ = fmt.Fprintf(os.Stderr, "gRPC Not Enabled, Please Configure\n")
os.Exit(1)
}
printPeerID(cfg.P2P)
conn, err := app.ConnectToNode(cfg)
if err != nil {
panic(err)
}
defer conn.Close()
client := protobufs.NewNodeServiceClient(conn)
nodeInfo, err := app.FetchNodeInfo(client)
if err != nil {
panic(err)
}
fmt.Println("Version: " + config.FormatVersion(nodeInfo.Version))
fmt.Println("Max Frame: " + strconv.FormatUint(nodeInfo.GetMaxFrame(), 10))
fmt.Println("Peer Score: " + strconv.FormatUint(nodeInfo.GetPeerScore(), 10))
printBalance(cfg)
}
func printLogo() {
fmt.Println(" %#########")
fmt.Println(" #############################")
fmt.Println(" ########################################&")
fmt.Println(" ###############################################")
fmt.Println(" &#####################% %######################")
fmt.Println(" ################# #################")
fmt.Println(" ############### ###############")
fmt.Println(" ############# ##############")
fmt.Println(" ############# ############&")
fmt.Println(" ############ ############")
fmt.Println(" ########### ########## &###########")
fmt.Println(" ########### ############## ###########")
fmt.Println(" ########### ############## ##########&")
fmt.Println(" ########## ############## ##########")
fmt.Println("%########## ########## ##########")
fmt.Println("########## ##########")
fmt.Println("########## &#########")
fmt.Println("########## ####### ####### ##########")
fmt.Println("%######### &######################### ##########")
fmt.Println(" ########## ##############% ############## &##########")
fmt.Println(" ' ' &############## ############### ##########")
fmt.Println(" ' ' ############### ##############% ###########")
fmt.Println(" ' '. ########## ############### ########")
fmt.Println(" '. . ##### ##############% ####")
fmt.Println(" ' '. ###############")
fmt.Println(" '. '.. ##############%")
fmt.Println(" '. '-. ###############")
fmt.Println(" '-. ''-.. .. ##############%")
fmt.Println(" '-. ''---............----' '. ###############")
fmt.Println(" '-.. '. ############")
fmt.Println(" ''-.. ..' ########")
fmt.Println(" ''---.. ...---'' ##")
fmt.Println(" ''----------''")
}
func printVersion() {
fmt.Println(" ")
fmt.Println(" Quilibrium Node - v" + config.GetVersionString() + " Sunset")
}