github.com/iotexproject/iotex-core@v1.14.1-rc1/config/config_test.go (about) 1 // Copyright (c) 2024 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package config 7 8 import ( 9 "fmt" 10 "os" 11 "path/filepath" 12 "strings" 13 "testing" 14 15 "github.com/iotexproject/go-pkgs/crypto" 16 "github.com/pkg/errors" 17 "github.com/stretchr/testify/require" 18 19 "github.com/iotexproject/iotex-core/blockchain/genesis" 20 ) 21 22 const ( 23 overwritePath = "_overwritePath" 24 secretPath = "_secretPath" 25 subChainPath = "_subChainPath" 26 ) 27 28 var ( 29 _overwritePath string 30 _secretPath string 31 _subChainPath string 32 ) 33 34 func makePathAndWriteFile(cfgStr, flagForPath string) (err error) { 35 switch flagForPath { 36 case overwritePath: 37 _overwritePath = filepath.Join(os.TempDir(), "config.yaml") 38 err = os.WriteFile(_overwritePath, []byte(cfgStr), 0666) 39 case secretPath: 40 _secretPath = filepath.Join(os.TempDir(), "secret.yaml") 41 err = os.WriteFile(_secretPath, []byte(cfgStr), 0666) 42 case subChainPath: 43 _subChainPath = filepath.Join(os.TempDir(), "config.yaml") 44 err = os.WriteFile(_subChainPath, []byte(cfgStr), 0666) 45 } 46 return err 47 } 48 49 func resetPathValues(t *testing.T, flagForPath []string) { 50 for _, pathValue := range flagForPath { 51 switch pathValue { 52 case overwritePath: 53 err := os.Remove(_overwritePath) 54 _overwritePath = "" 55 require.NoError(t, err) 56 case secretPath: 57 err := os.Remove(_secretPath) 58 _secretPath = "" 59 require.NoError(t, err) 60 case subChainPath: 61 err := os.Remove(_subChainPath) 62 _subChainPath = "" 63 require.NoError(t, err) 64 } 65 } 66 } 67 68 func resetPathValuesWithLookupEnv(t *testing.T, oldEnv string, oldExist bool, flagForPath string) { 69 switch flagForPath { 70 case overwritePath: 71 err := os.Remove(_overwritePath) 72 require.NoError(t, err) 73 _overwritePath = "" 74 if oldExist { 75 err = os.Setenv("IOTEX_TEST_NODE_TYPE", oldEnv) 76 } else { 77 err = os.Unsetenv("IOTEX_TEST_NODE_TYPE") 78 } 79 require.NoError(t, err) 80 case subChainPath: 81 err := os.Remove(_subChainPath) 82 require.NoError(t, err) 83 _subChainPath = "" 84 if oldExist { 85 err = os.Setenv("IOTEX_TEST_NODE_TYPE", oldEnv) 86 } else { 87 err = os.Unsetenv("IOTEX_TEST_NODE_TYPE") 88 } 89 require.NoError(t, err) 90 } 91 } 92 93 func generateProducerPrivKey() (crypto.PrivateKey, string, error) { 94 sk, err := crypto.GenerateKey() 95 cfgStr := fmt.Sprintf(` 96 chain: 97 producerPrivKey: "%s" 98 `, 99 sk.HexString(), 100 ) 101 return sk, cfgStr, err 102 } 103 104 func TestStrs_String(t *testing.T) { 105 ss := strs{"test"} 106 str := "TEST" 107 require.Nil(t, ss.Set(str)) 108 } 109 110 func TestNewDefaultConfig(t *testing.T) { 111 cfg, err := New([]string{}, []string{}) 112 require.NoError(t, err) 113 genesis.SetGenesisTimestamp(cfg.Genesis.Timestamp) 114 require.Equal(t, cfg.Genesis.Timestamp, genesis.Timestamp()) 115 } 116 117 func TestNewConfigWithoutValidation(t *testing.T) { 118 cfg, err := New([]string{}, []string{}, DoNotValidate) 119 require.NoError(t, err) 120 require.NotNil(t, cfg) 121 exp := Default 122 exp.Network.MasterKey = cfg.Chain.ProducerPrivKey 123 require.Equal(t, exp, cfg) 124 } 125 126 func TestNewConfigWithWrongConfigPath(t *testing.T) { 127 cfg, err := New([]string{"wrong_path", ""}, []string{}) 128 require.Error(t, err) 129 require.Equal(t, Config{}, cfg) 130 if strings.Contains(err.Error(), 131 "open wrong_path: The system cannot find the file specified") == false { // for Windows 132 require.Contains(t, err.Error(), "open wrong_path: no such file or directory") 133 } 134 } 135 136 func TestNewConfigWithPlugins(t *testing.T) { 137 _plugins := strs{ 138 "gateway", 139 } 140 cfg, err := New([]string{}, _plugins) 141 142 require.Nil(t, cfg.Plugins[GatewayPlugin]) 143 require.NoError(t, err) 144 145 _plugins = strs{ 146 "trick", 147 } 148 149 cfg, err = New([]string{}, _plugins) 150 151 require.Equal(t, Config{}, cfg) 152 require.Error(t, err) 153 154 defer func() { 155 _plugins = nil 156 }() 157 } 158 159 func TestNewConfigWithOverride(t *testing.T) { 160 sk, cfgStr, err := generateProducerPrivKey() 161 require.NoError(t, err) 162 163 require.NoError(t, makePathAndWriteFile(cfgStr, "_overwritePath")) 164 165 defer resetPathValues(t, []string{"_overwritePath"}) 166 167 cfg, err := New([]string{_overwritePath, ""}, []string{}) 168 require.NoError(t, err) 169 require.NotNil(t, cfg) 170 require.Equal(t, sk.HexString(), cfg.Chain.ProducerPrivKey) 171 } 172 173 func TestNewConfigWithSecret(t *testing.T) { 174 sk, cfgStr, err := generateProducerPrivKey() 175 require.NoError(t, err) 176 177 require.NoError(t, makePathAndWriteFile(cfgStr, "_overwritePath")) 178 179 require.NoError(t, makePathAndWriteFile(cfgStr, "_secretPath")) 180 181 defer resetPathValues(t, []string{"_overwritePath", "_secretPath"}) 182 183 cfg, err := New([]string{_overwritePath, _secretPath}, []string{}) 184 require.NoError(t, err) 185 require.NotNil(t, cfg) 186 require.Equal(t, sk.HexString(), cfg.Chain.ProducerPrivKey) 187 } 188 189 func TestNewConfigWithLookupEnv(t *testing.T) { 190 oldEnv, oldExist := os.LookupEnv("IOTEX_TEST_NODE_TYPE") 191 192 _, cfgStr, err := generateProducerPrivKey() 193 require.NoError(t, err) 194 require.NoError(t, makePathAndWriteFile(cfgStr, "_overwritePath")) 195 196 defer resetPathValuesWithLookupEnv(t, oldEnv, oldExist, "_overwritePath") 197 198 cfg, err := New([]string{_overwritePath, ""}, []string{}) 199 require.NoError(t, err) 200 require.NotNil(t, cfg) 201 202 err = os.Unsetenv("IOTEX_TEST_NODE_TYPE") 203 require.NoError(t, err) 204 205 cfg, err = New([]string{_overwritePath, ""}, []string{}) 206 require.NoError(t, err) 207 require.NotNil(t, cfg) 208 } 209 210 func TestValidateDispatcher(t *testing.T) { 211 cfg := Default 212 require.NoError(t, ValidateDispatcher(cfg)) 213 cfg.Dispatcher.ActionChanSize = 0 214 err := ValidateDispatcher(cfg) 215 require.Error(t, err) 216 require.Equal(t, ErrInvalidCfg, errors.Cause(err)) 217 require.True( 218 t, 219 strings.Contains(err.Error(), "dispatcher chan size should be greater than 0"), 220 ) 221 cfg.Dispatcher.ActionChanSize = 100 222 cfg.Dispatcher.BlockChanSize = 0 223 err = ValidateDispatcher(cfg) 224 require.Error(t, err) 225 require.Equal(t, ErrInvalidCfg, errors.Cause(err)) 226 require.True( 227 t, 228 strings.Contains(err.Error(), "dispatcher chan size should be greater than 0"), 229 ) 230 cfg.Dispatcher.BlockChanSize = 100 231 cfg.Dispatcher.BlockSyncChanSize = 0 232 err = ValidateDispatcher(cfg) 233 require.Error(t, err) 234 require.Equal(t, ErrInvalidCfg, errors.Cause(err)) 235 require.True( 236 t, 237 strings.Contains(err.Error(), "dispatcher chan size should be greater than 0"), 238 ) 239 } 240 241 func TestValidateRollDPoS(t *testing.T) { 242 cfg := Default 243 cfg.Consensus.Scheme = RollDPoSScheme 244 245 cfg.Consensus.RollDPoS.FSM.EventChanSize = 0 246 err := ValidateRollDPoS(cfg) 247 require.Error(t, err) 248 require.Equal(t, ErrInvalidCfg, errors.Cause(err)) 249 require.True( 250 t, 251 strings.Contains(err.Error(), "roll-DPoS event chan size should be greater than 0"), 252 ) 253 } 254 255 func TestValidateArchiveMode(t *testing.T) { 256 cfg := Default 257 cfg.Chain.EnableArchiveMode = true 258 cfg.Chain.EnableTrielessStateDB = true 259 require.Error(t, ErrInvalidCfg, errors.Cause(ValidateArchiveMode(cfg))) 260 require.EqualError(t, ValidateArchiveMode(cfg), "Archive mode is incompatible with trieless state DB: invalid config value") 261 cfg.Chain.EnableArchiveMode = false 262 cfg.Chain.EnableTrielessStateDB = true 263 require.NoError(t, errors.Cause(ValidateArchiveMode(cfg))) 264 cfg.Chain.EnableArchiveMode = true 265 cfg.Chain.EnableTrielessStateDB = false 266 require.NoError(t, errors.Cause(ValidateArchiveMode(cfg))) 267 cfg.Chain.EnableArchiveMode = false 268 cfg.Chain.EnableTrielessStateDB = false 269 require.NoError(t, errors.Cause(ValidateArchiveMode(cfg))) 270 } 271 272 func TestValidateActPool(t *testing.T) { 273 cfg := Default 274 cfg.ActPool.MaxNumActsPerAcct = 0 275 err := ValidateActPool(cfg) 276 require.Error(t, err) 277 require.Equal(t, ErrInvalidCfg, errors.Cause(err)) 278 require.True( 279 t, 280 strings.Contains( 281 err.Error(), 282 "maximum number of actions per pool or per account cannot be zero or negative", 283 ), 284 ) 285 286 cfg.ActPool.MaxNumActsPerAcct = 100 287 cfg.ActPool.MaxNumActsPerPool = 0 288 err = ValidateActPool(cfg) 289 require.Error(t, err) 290 require.Equal(t, ErrInvalidCfg, errors.Cause(err)) 291 require.True( 292 t, 293 strings.Contains( 294 err.Error(), 295 "maximum number of actions per pool or per account cannot be zero or negative", 296 ), 297 ) 298 299 cfg.ActPool.MaxNumActsPerPool = 99 300 err = ValidateActPool(cfg) 301 require.Error(t, err) 302 require.Equal(t, ErrInvalidCfg, errors.Cause(err)) 303 require.True( 304 t, 305 strings.Contains( 306 err.Error(), 307 "maximum number of actions per pool cannot be less than maximum number of actions per account", 308 ), 309 ) 310 } 311 312 func TestValidateForkHeights(t *testing.T) { 313 r := require.New(t) 314 315 tests := []struct { 316 fork string 317 err error 318 errMsg string 319 }{ 320 { 321 "Pacific", ErrInvalidCfg, "Pacific is heigher than Aleutian", 322 }, 323 { 324 "Aleutian", ErrInvalidCfg, "Aleutian is heigher than Bering", 325 }, 326 { 327 "Bering", ErrInvalidCfg, "Bering is heigher than Cook", 328 }, 329 { 330 "Cook", ErrInvalidCfg, "Cook is heigher than Dardanelles", 331 }, 332 { 333 "Dardanelles", ErrInvalidCfg, "Dardanelles is heigher than Daytona", 334 }, 335 { 336 "Daytona", ErrInvalidCfg, "Daytona is heigher than Easter", 337 }, 338 { 339 "Easter", ErrInvalidCfg, "Easter is heigher than FairbankMigration", 340 }, 341 { 342 "FbkMigration", ErrInvalidCfg, "FairbankMigration is heigher than Fairbank", 343 }, 344 { 345 "Fairbank", ErrInvalidCfg, "Fairbank is heigher than Greenland", 346 }, 347 { 348 "Greenland", ErrInvalidCfg, "Greenland is heigher than Iceland", 349 }, 350 { 351 "Iceland", ErrInvalidCfg, "Iceland is heigher than Jutland", 352 }, 353 { 354 "Jutland", ErrInvalidCfg, "Jutland is heigher than Kamchatka", 355 }, 356 { 357 "Kamchatka", ErrInvalidCfg, "Kamchatka is heigher than LordHowe", 358 }, 359 { 360 "LordHowe", ErrInvalidCfg, "LordHowe is heigher than Midway", 361 }, 362 { 363 "Midway", ErrInvalidCfg, "Midway is heigher than Newfoundland", 364 }, 365 { 366 "Newfoundland", ErrInvalidCfg, "Newfoundland is heigher than Okhotsk", 367 }, 368 { 369 "Okhotsk", ErrInvalidCfg, "Okhotsk is heigher than Palau", 370 }, 371 { 372 "Palau", ErrInvalidCfg, "Palau is heigher than Quebec", 373 }, 374 { 375 "Quebec", ErrInvalidCfg, "Quebec is heigher than Redsea", 376 }, 377 { 378 "Redsea", ErrInvalidCfg, "Redsea is heigher than Sumatra", 379 }, 380 { 381 "Sumatra", ErrInvalidCfg, "Sumatra is heigher than Tsunami", 382 }, 383 { 384 "", nil, "", 385 }, 386 } 387 388 for _, v := range tests { 389 cfg := newTestCfg(v.fork) 390 err := ValidateForkHeights(cfg) 391 r.Equal(v.err, errors.Cause(err)) 392 if err != nil { 393 r.Contains(err.Error(), v.errMsg) 394 } 395 } 396 } 397 398 func newTestCfg(fork string) Config { 399 cfg := Default 400 switch fork { 401 case "Pacific": 402 cfg.Genesis.PacificBlockHeight = cfg.Genesis.AleutianBlockHeight + 1 403 case "Aleutian": 404 cfg.Genesis.AleutianBlockHeight = cfg.Genesis.BeringBlockHeight + 1 405 case "Bering": 406 cfg.Genesis.BeringBlockHeight = cfg.Genesis.CookBlockHeight + 1 407 case "Cook": 408 cfg.Genesis.CookBlockHeight = cfg.Genesis.DardanellesBlockHeight + 1 409 case "Dardanelles": 410 cfg.Genesis.DardanellesBlockHeight = cfg.Genesis.DaytonaBlockHeight + 1 411 case "Daytona": 412 cfg.Genesis.DaytonaBlockHeight = cfg.Genesis.EasterBlockHeight + 1 413 case "Easter": 414 cfg.Genesis.EasterBlockHeight = cfg.Genesis.FbkMigrationBlockHeight + 1 415 case "FbkMigration": 416 cfg.Genesis.FbkMigrationBlockHeight = cfg.Genesis.FairbankBlockHeight + 1 417 case "Fairbank": 418 cfg.Genesis.FairbankBlockHeight = cfg.Genesis.GreenlandBlockHeight + 1 419 case "Greenland": 420 cfg.Genesis.GreenlandBlockHeight = cfg.Genesis.IcelandBlockHeight + 1 421 case "Iceland": 422 cfg.Genesis.IcelandBlockHeight = cfg.Genesis.JutlandBlockHeight + 1 423 case "Jutland": 424 cfg.Genesis.JutlandBlockHeight = cfg.Genesis.KamchatkaBlockHeight + 1 425 case "Kamchatka": 426 cfg.Genesis.KamchatkaBlockHeight = cfg.Genesis.LordHoweBlockHeight + 1 427 case "LordHowe": 428 cfg.Genesis.LordHoweBlockHeight = cfg.Genesis.MidwayBlockHeight + 1 429 case "Midway": 430 cfg.Genesis.MidwayBlockHeight = cfg.Genesis.NewfoundlandBlockHeight + 1 431 case "Newfoundland": 432 cfg.Genesis.NewfoundlandBlockHeight = cfg.Genesis.OkhotskBlockHeight + 1 433 case "Okhotsk": 434 cfg.Genesis.OkhotskBlockHeight = cfg.Genesis.PalauBlockHeight + 1 435 case "Palau": 436 cfg.Genesis.PalauBlockHeight = cfg.Genesis.QuebecBlockHeight + 1 437 case "Quebec": 438 cfg.Genesis.QuebecBlockHeight = cfg.Genesis.RedseaBlockHeight + 1 439 case "Redsea": 440 cfg.Genesis.RedseaBlockHeight = cfg.Genesis.SumatraBlockHeight + 1 441 case "Sumatra": 442 cfg.Genesis.SumatraBlockHeight = cfg.Genesis.TsunamiBlockHeight + 1 443 } 444 return cfg 445 } 446 447 func TestNewSubConfigWithWrongConfigPath(t *testing.T) { 448 cfg, err := NewSub([]string{"", "wrong_path"}) 449 require.Error(t, err) 450 require.Equal(t, Config{}, cfg) 451 if strings.Contains(err.Error(), 452 "open wrong_path: The system cannot find the file specified") == false { // for Windows 453 require.Contains(t, err.Error(), "open wrong_path: no such file or directory") 454 } 455 } 456 457 func TestNewSubConfigWithSubChainPath(t *testing.T) { 458 sk, cfgStr, err := generateProducerPrivKey() 459 require.NoError(t, err) 460 require.NoError(t, makePathAndWriteFile(cfgStr, "_subChainPath")) 461 462 defer resetPathValues(t, []string{"_subChainPath"}) 463 cfg, err := NewSub([]string{"", _subChainPath}) 464 require.NoError(t, err) 465 require.NotNil(t, cfg) 466 require.Equal(t, sk.HexString(), cfg.Chain.ProducerPrivKey) 467 } 468 469 func TestNewSubConfigWithSecret(t *testing.T) { 470 sk, cfgStr, err := generateProducerPrivKey() 471 require.NoError(t, err) 472 require.NoError(t, makePathAndWriteFile(cfgStr, "_subChainPath")) 473 474 require.NoError(t, makePathAndWriteFile(cfgStr, "_secretPath")) 475 476 defer resetPathValues(t, []string{"_subChainPath", "_secretPath"}) 477 478 cfg, err := NewSub([]string{"", _subChainPath}) 479 require.NoError(t, err) 480 require.NotNil(t, cfg) 481 require.Equal(t, sk.HexString(), cfg.Chain.ProducerPrivKey) 482 } 483 484 func TestNewSubConfigWithLookupEnv(t *testing.T) { 485 oldEnv, oldExist := os.LookupEnv("IOTEX_TEST_NODE_TYPE") 486 487 _, cfgStr, err := generateProducerPrivKey() 488 require.NoError(t, err) 489 490 require.NoError(t, makePathAndWriteFile(cfgStr, "_subChainPath")) 491 492 defer resetPathValuesWithLookupEnv(t, oldEnv, oldExist, "_subChainPath") 493 494 cfg, err := NewSub([]string{"", _subChainPath}) 495 require.NoError(t, err) 496 require.NotNil(t, cfg) 497 498 err = os.Unsetenv("IOTEX_TEST_NODE_TYPE") 499 require.NoError(t, err) 500 501 cfg, err = NewSub([]string{"", _subChainPath}) 502 require.NoError(t, err) 503 require.NotNil(t, cfg) 504 }