github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/common/config.go (about)

     1  package common
     2  
     3  import (
     4  	"encoding/json"
     5  	"flag"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"runtime/debug"
    10  	"strings"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	"github.com/piotrnar/gocoin"
    16  	"github.com/piotrnar/gocoin/lib/btc"
    17  	"github.com/piotrnar/gocoin/lib/others/sys"
    18  	"github.com/piotrnar/gocoin/lib/utxo"
    19  )
    20  
    21  const LastTrustedBTCBlock = "00000000000000000001fcf207ce30e9172433f815bf4ca0e90ecd0601286a20" // #817490
    22  const LastTrustedTN3Block = "0000000000000f56395d3d0e54515ae310541b6c8e5a4311d05edbaed567211f" // #2536700
    23  
    24  var (
    25  	ConfigFile string = "gocoin.conf"
    26  
    27  	FLAG struct { // Command line only options
    28  		Rescan        bool
    29  		VolatileUTXO  bool
    30  		UndoBlocks    uint
    31  		TrustAll      bool
    32  		UnbanAllPeers bool
    33  		NoWallet      bool
    34  		Log           bool
    35  		SaveConfig    bool
    36  	}
    37  
    38  	CFG struct { // Options that can come from either command line or common file
    39  		Testnet          bool
    40  		ConnectOnly      string
    41  		Datadir          string
    42  		TextUI_Enabled   bool
    43  		UserAgent        string
    44  		LastTrustedBlock string
    45  
    46  		WebUI struct {
    47  			Interface   string
    48  			AllowedIP   string // comma separated
    49  			ShowBlocks  uint32
    50  			AddrListLen uint32 // size of address list in MakeTx tab popups
    51  			Title       string
    52  			PayCmdName  string
    53  			ServerMode  bool
    54  			SSLPort     uint16
    55  		}
    56  		RPC struct {
    57  			Enabled  bool
    58  			Username string
    59  			Password string
    60  			TCPPort  uint32
    61  		}
    62  		Net struct {
    63  			ListenTCP      bool
    64  			TCPPort        uint16
    65  			BindToIF       string
    66  			MaxOutCons     uint32
    67  			MaxInCons      uint32
    68  			MaxUpKBps      uint
    69  			MaxDownKBps    uint
    70  			MaxBlockAtOnce uint32
    71  			ExternalIP     string
    72  		}
    73  		TXPool struct {
    74  			Enabled        bool // Global on/off swicth
    75  			AllowMemInputs bool
    76  			FeePerByte     float64
    77  			MaxTxSize      uint32
    78  			MaxSizeMB      uint
    79  			MaxRejectMB    uint
    80  			MaxRejectCnt   uint
    81  			SaveOnDisk     bool
    82  			Debug          bool
    83  		}
    84  		TXRoute struct {
    85  			Enabled    bool // Global on/off swicth
    86  			FeePerByte float64
    87  			MaxTxSize  uint32
    88  			MemInputs  bool
    89  		}
    90  		Memory struct {
    91  			GCPercTrshold        int
    92  			UseGoHeap            bool // Use Go Heap and Garbage Collector for UTXO records
    93  			MaxCachedBlks        uint32
    94  			FreeAtStart          bool // Free all possible memory after initial loading of block chain
    95  			CacheOnDisk          bool
    96  			MaxSyncCacheMB       uint32 // When syncing chain, prebuffer up to this MB of bocks data
    97  			MaxDataFileMB        uint   // 0 for unlimited size
    98  			DataFilesKeep        uint32 // 0 for all
    99  			OldDataBackup        bool   // move old dat files to "oldat/" folder (instead of removing them)
   100  			PurgeUnspendableUTXO bool
   101  			CompressBlockDB      bool
   102  		}
   103  		AllBalances struct {
   104  			MinValue  uint64 // Do not keep balance records for values lower than this
   105  			UseMapCnt int
   106  			AutoLoad  bool
   107  		}
   108  		Stat struct {
   109  			HashrateHrs uint
   110  			MiningHrs   uint
   111  			FeesBlks    uint
   112  			BSizeBlks   uint
   113  			NoCounters  bool
   114  		}
   115  		DropPeers struct {
   116  			DropEachMinutes uint // zero for never
   117  			BlckExpireHours uint // zero for never
   118  			PingPeriodSec   uint // zero to not ping
   119  		}
   120  		UTXOSave struct {
   121  			SecondsToTake   uint   // zero for as fast as possible, 600 for do it in 10 minutes
   122  			BlocksToHold    uint32 // zero for immediatelly, one for every other block...
   123  			CompressRecords bool
   124  		}
   125  	}
   126  
   127  	mutex_cfg sync.Mutex
   128  )
   129  
   130  type oneAllowedAddr struct {
   131  	Addr, Mask uint32
   132  }
   133  
   134  var WebUIAllowed []oneAllowedAddr
   135  
   136  func InitConfig() {
   137  	var new_config_file bool
   138  
   139  	// Fill in default values
   140  	CFG.Net.ListenTCP = true
   141  	CFG.Net.MaxOutCons = 9
   142  	CFG.Net.MaxInCons = 10
   143  	CFG.Net.MaxBlockAtOnce = 3
   144  	CFG.Net.BindToIF = "0.0.0.0"
   145  
   146  	CFG.TextUI_Enabled = true
   147  
   148  	CFG.WebUI.Interface = "127.0.0.1:8833"
   149  	CFG.WebUI.AllowedIP = "127.0.0.1"
   150  	CFG.WebUI.ShowBlocks = 144
   151  	CFG.WebUI.AddrListLen = 15
   152  	CFG.WebUI.Title = "Gocoin"
   153  	CFG.WebUI.PayCmdName = "pay_cmd.txt"
   154  
   155  	CFG.RPC.Username = "gocoinrpc"
   156  	CFG.RPC.Password = "gocoinpwd"
   157  
   158  	CFG.TXPool.Enabled = true
   159  	CFG.TXPool.AllowMemInputs = true
   160  	CFG.TXPool.FeePerByte = 1.0
   161  	CFG.TXPool.MaxTxSize = 100e3
   162  	CFG.TXPool.MaxSizeMB = 300
   163  	CFG.TXPool.MaxRejectMB = 25
   164  	CFG.TXPool.MaxRejectCnt = 5000
   165  	CFG.TXPool.SaveOnDisk = true
   166  
   167  	CFG.TXRoute.Enabled = true
   168  	CFG.TXRoute.FeePerByte = 0.0
   169  	CFG.TXRoute.MaxTxSize = 100e3
   170  	CFG.TXRoute.MemInputs = true
   171  
   172  	CFG.Memory.GCPercTrshold = 30 // 30% (To save mem)
   173  	CFG.Memory.MaxCachedBlks = 200
   174  	CFG.Memory.CacheOnDisk = true
   175  	CFG.Memory.MaxSyncCacheMB = 500
   176  	CFG.Memory.MaxDataFileMB = 1000 // max 1GB per single data file
   177  	CFG.Memory.CompressBlockDB = true
   178  
   179  	CFG.Stat.HashrateHrs = 12
   180  	CFG.Stat.MiningHrs = 24
   181  	CFG.Stat.FeesBlks = 4 * 6 /*last 4 hours*/
   182  	CFG.Stat.BSizeBlks = 1008 /*one week*/
   183  
   184  	CFG.AllBalances.MinValue = 1e5 // 0.001 BTC
   185  	CFG.AllBalances.UseMapCnt = 5000
   186  	CFG.AllBalances.AutoLoad = true
   187  
   188  	CFG.DropPeers.DropEachMinutes = 5  // minutes
   189  	CFG.DropPeers.BlckExpireHours = 24 // hours
   190  	CFG.DropPeers.PingPeriodSec = 15   // seconds
   191  
   192  	CFG.UTXOSave.SecondsToTake = 300
   193  	CFG.UTXOSave.BlocksToHold = 6
   194  
   195  	if cfgfn := os.Getenv("GOCOIN_CLIENT_CONFIG"); cfgfn != "" {
   196  		ConfigFile = cfgfn
   197  	}
   198  	// pre-parse command line: look for -cfg <fname> or -h
   199  	for i := 1; i < len(os.Args); i++ {
   200  		if os.Args[i] == "-cfg" || os.Args[i] == "--cfg" {
   201  			if i+1 >= len(os.Args) {
   202  				println("Missing the file name for", os.Args[i], "argument")
   203  				os.Exit(1)
   204  			}
   205  			ConfigFile = os.Args[i+1]
   206  			break
   207  		}
   208  		if strings.HasPrefix(os.Args[i], "-cfg=") || strings.HasPrefix(os.Args[i], "--cfg=") {
   209  			ss := strings.SplitN(os.Args[i], "=", 2)
   210  			ConfigFile = ss[1]
   211  		}
   212  	}
   213  
   214  	cfgfilecontent, e := ioutil.ReadFile(ConfigFile)
   215  	if e == nil && len(cfgfilecontent) > 0 {
   216  		e = json.Unmarshal(cfgfilecontent, &CFG)
   217  		if e != nil {
   218  			println("Error in", ConfigFile, e.Error())
   219  			os.Exit(1)
   220  		}
   221  		fmt.Println("Using config file", ConfigFile)
   222  	} else {
   223  		new_config_file = true
   224  		fmt.Println("New config file", ConfigFile)
   225  	}
   226  
   227  	var _cfg_fn string
   228  	flag.StringVar(&_cfg_fn, "cfg", ConfigFile, "Specify name of the config file")
   229  	flag.BoolVar(&FLAG.Rescan, "r", false, "Rebuild UTXO database (fixes 'Unknown input TxID' errors)")
   230  	flag.BoolVar(&FLAG.VolatileUTXO, "v", false, "Use UTXO database in volatile mode (speeds up rebuilding)")
   231  	flag.BoolVar(&CFG.Testnet, "t", CFG.Testnet, "Use Testnet3")
   232  	flag.StringVar(&CFG.ConnectOnly, "c", CFG.ConnectOnly, "Connect only to this host and nowhere else")
   233  	flag.BoolVar(&CFG.Net.ListenTCP, "l", CFG.Net.ListenTCP, "Listen for incoming TCP connections (on default port)")
   234  	flag.StringVar(&CFG.Datadir, "d", CFG.Datadir, "Specify Gocoin's database root folder")
   235  	flag.UintVar(&CFG.Net.MaxUpKBps, "ul", CFG.Net.MaxUpKBps, "Upload limit in KB/s (0 for no limit)")
   236  	flag.UintVar(&CFG.Net.MaxDownKBps, "dl", CFG.Net.MaxDownKBps, "Download limit in KB/s (0 for no limit)")
   237  	flag.StringVar(&CFG.WebUI.Interface, "webui", CFG.WebUI.Interface, "Serve WebUI from the given interface")
   238  	flag.BoolVar(&CFG.TXRoute.Enabled, "txp", CFG.TXPool.Enabled, "Enable Memory Pool")
   239  	flag.BoolVar(&CFG.TXRoute.Enabled, "txr", CFG.TXRoute.Enabled, "Enable Transaction Routing")
   240  	flag.BoolVar(&CFG.TextUI_Enabled, "textui", CFG.TextUI_Enabled, "Enable processing TextUI commands (from stdin)")
   241  	flag.UintVar(&FLAG.UndoBlocks, "undo", 0, "Undo UTXO with this many blocks and exit")
   242  	flag.BoolVar(&FLAG.TrustAll, "trust", FLAG.TrustAll, "Trust all scripts inside new blocks (for fast syncig)")
   243  	flag.BoolVar(&FLAG.UnbanAllPeers, "unban", FLAG.UnbanAllPeers, "Un-ban all peers in databse, before starting")
   244  	flag.BoolVar(&FLAG.NoWallet, "nowallet", FLAG.NoWallet, "Do not automatically enable the wallet functionality (lower memory usage and faster block processing)")
   245  	flag.BoolVar(&FLAG.Log, "log", FLAG.Log, "Store some runtime information in the log files")
   246  	flag.BoolVar(&FLAG.SaveConfig, "sc", FLAG.SaveConfig, "Save "+ConfigFile+" file and exit (use to create default config file)")
   247  
   248  	if CFG.Datadir == "" {
   249  		CFG.Datadir = sys.BitcoinHome() + "gocoin"
   250  	}
   251  
   252  	if flag.Lookup("h") != nil {
   253  		flag.PrintDefaults()
   254  		os.Exit(0)
   255  	}
   256  	flag.Parse()
   257  
   258  	// swap LastTrustedBlock if it's now from the other chain
   259  	if CFG.Testnet {
   260  		if new_config_file || CFG.LastTrustedBlock == LastTrustedBTCBlock {
   261  			CFG.LastTrustedBlock = LastTrustedTN3Block
   262  		}
   263  	} else {
   264  		if new_config_file || CFG.LastTrustedBlock == LastTrustedTN3Block {
   265  			CFG.LastTrustedBlock = LastTrustedBTCBlock
   266  		}
   267  	}
   268  
   269  	ApplyBalMinVal()
   270  
   271  	if !FLAG.NoWallet {
   272  		if FLAG.UndoBlocks != 0 {
   273  			FLAG.NoWallet = true // this will prevent loading of balances, thus speeding up the process
   274  		} else {
   275  			FLAG.NoWallet = !CFG.AllBalances.AutoLoad
   276  		}
   277  	}
   278  
   279  	if new_config_file {
   280  		// Create default config file
   281  		// Set to purge unspndable UTXO records, for lower system memory usage
   282  		CFG.Memory.PurgeUnspendableUTXO = true
   283  		SaveConfig()
   284  		println("Stored default configuration in", ConfigFile)
   285  	}
   286  
   287  	if CFG.Memory.UseGoHeap {
   288  		fmt.Println("Using native Go heap with Garbage Collector for UTXO records")
   289  	} else {
   290  		fmt.Println("Using modernc.org/memory package to skip GC for UTXO records ")
   291  		utxo.Memory_Malloc = func(le int) (res []byte) {
   292  			MemMutex.Lock()
   293  			res, _ = Memory.Malloc(le)
   294  			MemMutex.Unlock()
   295  			return
   296  		}
   297  		utxo.Memory_Free = func(ptr []byte) {
   298  			MemMutex.Lock()
   299  			Memory.Free(ptr)
   300  			MemMutex.Unlock()
   301  		}
   302  	}
   303  
   304  	if CFG.Memory.DataFilesKeep == 0 {
   305  		Services |= btc.SERVICE_NETWORK
   306  	}
   307  
   308  	Reset()
   309  }
   310  
   311  func DataSubdir() string {
   312  	if CFG.Testnet {
   313  		return "tstnet"
   314  	} else {
   315  		return "btcnet"
   316  	}
   317  }
   318  
   319  func SaveConfig() bool {
   320  	dat, _ := json.MarshalIndent(&CFG, "", "    ")
   321  	if dat == nil {
   322  		return false
   323  	}
   324  	ioutil.WriteFile(ConfigFile, dat, 0660)
   325  	return true
   326  
   327  }
   328  
   329  // make sure to call it with locked mutex_cfg
   330  func Reset() {
   331  	SetUploadLimit(uint64(CFG.Net.MaxUpKBps) << 10)
   332  	SetDownloadLimit(uint64(CFG.Net.MaxDownKBps) << 10)
   333  	debug.SetGCPercent(CFG.Memory.GCPercTrshold)
   334  	if AllBalMinVal() != CFG.AllBalances.MinValue {
   335  		fmt.Println("In order to apply the new value of AllBalMinVal, restart the node or do 'wallet off' and 'wallet on'")
   336  	}
   337  	DropSlowestEvery = time.Duration(CFG.DropPeers.DropEachMinutes) * time.Minute
   338  	BlockExpireEvery = time.Duration(CFG.DropPeers.BlckExpireHours) * time.Hour
   339  	PingPeerEvery = time.Duration(CFG.DropPeers.PingPeriodSec) * time.Second
   340  
   341  	atomic.StoreUint64(&maxMempoolSizeBytes, uint64(CFG.TXPool.MaxSizeMB)*1e6)
   342  	atomic.StoreUint64(&maxRejectedSizeBytes, uint64(CFG.TXPool.MaxRejectMB)*1e6)
   343  	atomic.StoreUint64(&minFeePerKB, uint64(CFG.TXPool.FeePerByte*1000))
   344  	atomic.StoreUint64(&minminFeePerKB, MinFeePerKB())
   345  
   346  	if CFG.Memory.MaxSyncCacheMB < 100 {
   347  		CFG.Memory.MaxSyncCacheMB = 100
   348  	}
   349  	SyncMaxCacheBytes.Store(int(CFG.Memory.MaxSyncCacheMB) << 20)
   350  
   351  	if CFG.Stat.NoCounters {
   352  		if !NoCounters.Get() {
   353  			// switching counters off - reset the data
   354  			NoCounters.Set()
   355  			CounterMutex.Lock()
   356  			Counter = make(map[string]uint64)
   357  			CounterMutex.Unlock()
   358  		}
   359  	} else {
   360  		NoCounters.Clr()
   361  	}
   362  
   363  	ips := strings.Split(CFG.WebUI.AllowedIP, ",")
   364  	WebUIAllowed = nil
   365  	for i := range ips {
   366  		oaa := str2oaa(ips[i])
   367  		if oaa != nil {
   368  			WebUIAllowed = append(WebUIAllowed, *oaa)
   369  		} else {
   370  			println("ERROR: Incorrect AllowedIP:", ips[i])
   371  		}
   372  	}
   373  	if len(WebUIAllowed) == 0 {
   374  		println("WARNING: No IP is currently allowed at WebUI")
   375  	}
   376  	ListenTCP = CFG.Net.ListenTCP
   377  
   378  	utxo.UTXO_WRITING_TIME_TARGET = time.Second * time.Duration(CFG.UTXOSave.SecondsToTake)
   379  	utxo.UTXO_SKIP_SAVE_BLOCKS = CFG.UTXOSave.BlocksToHold
   380  	utxo.UTXO_PURGE_UNSPENDABLE = CFG.Memory.PurgeUnspendableUTXO
   381  
   382  	if CFG.UserAgent != "" {
   383  		UserAgent = CFG.UserAgent
   384  	} else {
   385  		UserAgent = "/Gocoin:" + gocoin.Version + "/"
   386  	}
   387  
   388  	if CFG.Memory.MaxDataFileMB != 0 && CFG.Memory.MaxDataFileMB < 8 {
   389  		CFG.Memory.MaxDataFileMB = 8
   390  	}
   391  
   392  	if CFG.Net.BindToIF == "" {
   393  		CFG.Net.BindToIF = "0.0.0.0"
   394  	}
   395  
   396  	MkTempBlocksDir()
   397  
   398  	ReloadMiners()
   399  
   400  	ApplyLastTrustedBlock()
   401  }
   402  
   403  func MkTempBlocksDir() {
   404  	// no point doing it before GocoinHomeDir is set in host_init()
   405  	if CFG.Memory.CacheOnDisk && GocoinHomeDir != "" {
   406  		os.Mkdir(TempBlocksDir(), 0700)
   407  	}
   408  }
   409  
   410  func RPCPort() (res uint32) {
   411  	mutex_cfg.Lock()
   412  	defer mutex_cfg.Unlock()
   413  
   414  	if CFG.RPC.TCPPort != 0 {
   415  		res = CFG.RPC.TCPPort
   416  		return
   417  	}
   418  	if CFG.Testnet {
   419  		res = 18332
   420  	} else {
   421  		res = 8332
   422  	}
   423  	return
   424  }
   425  
   426  func DefaultTcpPort() (res uint16) {
   427  	mutex_cfg.Lock()
   428  	defer mutex_cfg.Unlock()
   429  
   430  	if CFG.Net.TCPPort != 0 {
   431  		res = CFG.Net.TCPPort
   432  		return
   433  	}
   434  	if CFG.Testnet {
   435  		res = 18333
   436  	} else {
   437  		res = 8333
   438  	}
   439  	return
   440  }
   441  
   442  // str2oaa converts an IP range to addr/mask
   443  func str2oaa(ip string) (res *oneAllowedAddr) {
   444  	var a, b, c, d, x uint32
   445  	n, _ := fmt.Sscanf(ip, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &x)
   446  	if n < 4 {
   447  		return
   448  	}
   449  	if (a|b|c|d) > 255 || n == 5 && x > 32 {
   450  		return
   451  	}
   452  	res = new(oneAllowedAddr)
   453  	res.Addr = (a << 24) | (b << 16) | (c << 8) | d
   454  	if n == 4 || x == 32 {
   455  		res.Mask = 0xffffffff
   456  	} else {
   457  		res.Mask = uint32((uint64(1)<<(32-x))-1) ^ 0xffffffff
   458  	}
   459  	res.Addr &= res.Mask
   460  	//fmt.Printf(" %s -> %08x / %08x\n", ip, res.Addr, res.Mask)
   461  	return
   462  }
   463  
   464  func LockCfg() {
   465  	mutex_cfg.Lock()
   466  }
   467  
   468  func UnlockCfg() {
   469  	mutex_cfg.Unlock()
   470  }
   471  
   472  func CloseBlockChain() {
   473  	if BlockChain != nil {
   474  		fmt.Println("Closing BlockChain")
   475  		BlockChain.Close()
   476  		//BlockChain = nil
   477  	}
   478  }
   479  
   480  func GetDuration(addr *time.Duration) (res time.Duration) {
   481  	mutex_cfg.Lock()
   482  	res = *addr
   483  	mutex_cfg.Unlock()
   484  	return
   485  }
   486  
   487  func GetUint64(addr *uint64) (res uint64) {
   488  	mutex_cfg.Lock()
   489  	res = *addr
   490  	mutex_cfg.Unlock()
   491  	return
   492  }
   493  
   494  func GetUint32(addr *uint32) (res uint32) {
   495  	mutex_cfg.Lock()
   496  	res = *addr
   497  	mutex_cfg.Unlock()
   498  	return
   499  }
   500  
   501  func SetUint32(addr *uint32, val uint32) {
   502  	mutex_cfg.Lock()
   503  	*addr = val
   504  	mutex_cfg.Unlock()
   505  }
   506  
   507  func GetBool(addr *bool) (res bool) {
   508  	mutex_cfg.Lock()
   509  	res = *addr
   510  	mutex_cfg.Unlock()
   511  	return
   512  }
   513  
   514  func SetBool(addr *bool, val bool) {
   515  	mutex_cfg.Lock()
   516  	*addr = val
   517  	mutex_cfg.Unlock()
   518  }
   519  
   520  func AllBalMinVal() uint64 {
   521  	return atomic.LoadUint64(&allBalMinVal)
   522  }
   523  
   524  func ApplyBalMinVal() {
   525  	atomic.StoreUint64(&allBalMinVal, CFG.AllBalances.MinValue)
   526  }
   527  
   528  func MinFeePerKB() uint64 {
   529  	return atomic.LoadUint64(&minFeePerKB)
   530  }
   531  
   532  func SetMinFeePerKB(val uint64) bool {
   533  	minmin := atomic.LoadUint64(&minminFeePerKB)
   534  	if val < minmin {
   535  		val = minmin
   536  	}
   537  	if val == MinFeePerKB() {
   538  		return false
   539  	}
   540  	atomic.StoreUint64(&minFeePerKB, val)
   541  	return true
   542  }
   543  
   544  func RouteMinFeePerKB() uint64 {
   545  	return atomic.LoadUint64(&routeMinFeePerKB)
   546  }
   547  
   548  func IsListenTCP() (res bool) {
   549  	mutex_cfg.Lock()
   550  	res = CFG.ConnectOnly == "" && ListenTCP
   551  	mutex_cfg.Unlock()
   552  	return
   553  }
   554  
   555  func MaxMempoolSize() uint64 {
   556  	return atomic.LoadUint64(&maxMempoolSizeBytes)
   557  }
   558  
   559  func RejectedTxsLimits() (size uint64, cnt int) {
   560  	mutex_cfg.Lock()
   561  	size = maxRejectedSizeBytes
   562  	cnt = int(CFG.TXPool.MaxRejectCnt)
   563  	mutex_cfg.Unlock()
   564  	return
   565  }
   566  
   567  func TempBlocksDir() string {
   568  	return GocoinHomeDir + "tmpblk" + string(os.PathSeparator)
   569  }
   570  
   571  func GetExternalIp() (res string) {
   572  	mutex_cfg.Lock()
   573  	res = CFG.Net.ExternalIP
   574  	mutex_cfg.Unlock()
   575  	return
   576  }