state_headless.go raw
1 // +build headless
2
3 package state
4
5 import (
6 "fmt"
7 "math/rand"
8 "runtime"
9 "strings"
10 "sync"
11 "time"
12
13 "github.com/p9c/p9/pkg/chaincfg"
14 "github.com/p9c/p9/pkg/chainclient"
15 "github.com/p9c/p9/pkg/ctrl"
16 "github.com/p9c/p9/pod/config"
17 "github.com/p9c/p9/pod/podcfgs"
18
19 "github.com/p9c/p9/pkg/qu"
20
21 "go.uber.org/atomic"
22
23 "github.com/p9c/p9/cmd/node/active"
24 "github.com/p9c/p9/pkg/chainrpc"
25 "github.com/p9c/p9/pkg/util/lang"
26 )
27
28 type _dtype int
29
30 var _d _dtype
31
32 // State stores all the common state data used in pod
33 type State struct {
34 sync.Mutex
35 WaitGroup sync.WaitGroup
36 KillAll qu.C
37 Config *config.Config
38 // ConfigMap
39 ConfigMap map[string]interface{}
40 // StateCfg is a reference to the main node state configuration struct
41 StateCfg *active.Config
42 // ActiveNet is the active net parameters
43 ActiveNet *chaincfg.Params
44 // Language libraries
45 Language *lang.Lexicon
46 // // DataDir is the default data dir
47 // DataDir string
48 // Node is the run state of the node
49 Node atomic.Bool
50 // NodeReady is closed when it is ready then always returns
51 NodeReady qu.C
52 // NodeKill is the killswitch for the Node
53 NodeKill qu.C
54 // Wallet is the run state of the wallet
55 Wallet atomic.Bool
56 // WalletKill is the killswitch for the Wallet
57 WalletKill qu.C
58 // RPCServer is needed to directly query data
59 RPCServer *chainrpc.Server
60 // NodeChan relays the chain RPC server to the main
61 NodeChan chan *chainrpc.Server
62 // // WalletServer is needed to query the wallet
63 // WalletServer *wallet.Wallet
64 // ChainClientReady signals when the chain client is ready
65 ChainClientReady qu.C
66 // ChainClient is the wallet's chain RPC client
67 ChainClient *chainclient.RPCClient
68 // RealNode is the main node
69 RealNode *chainrpc.Node
70 // Hashrate is the current total hashrate from kopach workers taking work from this node
71 Hashrate atomic.Uint64
72 // Controller is the state of the controller
73 Controller *ctrl.State
74 // OtherNodesCounter is the count of nodes connected automatically on the LAN
75 OtherNodesCounter atomic.Int32
76 // IsGUI indicates if we have the possibility of terminal input
77 IsGUI bool
78 waitChangers []string
79 waitCounter int
80 Syncing *atomic.Bool
81 }
82
83 func (cx *State) WaitAdd() {
84 cx.WaitGroup.Add(1)
85 _, file, line, _ := runtime.Caller(1)
86 record := fmt.Sprintf("+ %s:%d", file, line)
87 cx.waitChangers = append(cx.waitChangers, record)
88 cx.waitCounter++
89 D.Ln("added to waitgroup", record, cx.waitCounter)
90 D.Ln(cx.PrintWaitChangers())
91 }
92
93 func (cx *State) WaitDone() {
94 _, file, line, _ := runtime.Caller(1)
95 record := fmt.Sprintf("- %s:%d", file, line)
96 cx.waitChangers = append(cx.waitChangers, record)
97 cx.waitCounter--
98 D.Ln("removed from waitgroup", record, cx.waitCounter)
99 D.Ln(cx.PrintWaitChangers())
100 qu.PrintChanState()
101 cx.WaitGroup.Done()
102 }
103
104 func (cx *State) WaitWait() {
105 D.Ln(cx.PrintWaitChangers())
106 cx.WaitGroup.Wait()
107 }
108
109 func (cx *State) PrintWaitChangers() string {
110 o := "Calls that change context waitgroup values:\n"
111 for i := range cx.waitChangers {
112 o += strings.Repeat(" ", 48)
113 o += cx.waitChangers[i] + "\n"
114 }
115 o += strings.Repeat(" ", 48)
116 o += "current total:"
117 o += fmt.Sprint(cx.waitCounter)
118 return o
119 }
120
121 // GetNewContext returns a fresh new context
122 func GetNewContext() (s *State, e error) {
123 config := podcfgs.GetDefaultConfig()
124 if e = config.Initialize(); E.Chk(e) {
125 return
126 }
127 chainClientReady := qu.T()
128 rand.Seed(time.Now().UnixNano())
129 rand.Seed(rand.Int63())
130 s = &State{
131 ChainClientReady: chainClientReady,
132 KillAll: qu.T(),
133 App: cli.NewApp(),
134 Config: config,
135 ConfigMap: config.Map,
136 StateCfg: new(active.Config),
137 // Language: lang.ExportLanguage(appLang),
138 // DataDir: appdata.Dir(appName, false),
139 NodeChan: make(chan *chainrpc.Server),
140 Syncing: atomic.NewBool(true),
141 }
142 return
143 }
144
145 func GetContext(cx *State) *chainrpc.Context {
146 return &chainrpc.Context{
147 Config: cx.Config, StateCfg: cx.StateCfg, ActiveNet: cx.ActiveNet,
148 Hashrate: cx.Hashrate,
149 }
150 }
151
152 func (cx *State) IsCurrent() (is bool) {
153 rn := cx.RealNode
154 cc := rn.ConnectedCount()
155 othernodes := cx.OtherNodesCounter.Load()
156 if !*cx.Config.LAN {
157 cc -= othernodes
158 }
159 D.Ln(cc, "nodes connected")
160 connected := cc > 0
161 is = rn.Chain.IsCurrent() &&
162 rn.SyncManager.IsCurrent() &&
163 connected &&
164 rn.Chain.BestChain.Height() >= rn.HighestKnown.Load() || *cx.Config.Solo
165 D.Ln(
166 "is current:", is, "-", rn.Chain.IsCurrent(), rn.SyncManager.IsCurrent(),
167 *cx.Config.Solo, "connected", rn.HighestKnown.Load(), rn.Chain.BestChain.Height(),
168 othernodes,
169 )
170 return is
171 }
172