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