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 }