github.com/ava-labs/avalanchego@v1.11.11/genesis/genesis_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package genesis 5 6 import ( 7 "encoding/base64" 8 "encoding/hex" 9 "encoding/json" 10 "fmt" 11 "os" 12 "path/filepath" 13 "testing" 14 "time" 15 16 "github.com/stretchr/testify/require" 17 18 _ "embed" 19 20 "github.com/ava-labs/avalanchego/ids" 21 "github.com/ava-labs/avalanchego/utils/constants" 22 "github.com/ava-labs/avalanchego/utils/hashing" 23 "github.com/ava-labs/avalanchego/utils/perms" 24 "github.com/ava-labs/avalanchego/vms/platformvm/genesis" 25 ) 26 27 var ( 28 //go:embed genesis_test.json 29 customGenesisConfigJSON []byte 30 invalidGenesisConfigJSON = []byte(`{ 31 "networkID": 9999}}}} 32 }`) 33 34 genesisStakingCfg = &StakingConfig{ 35 MaxStakeDuration: 365 * 24 * time.Hour, 36 } 37 ) 38 39 func TestValidateConfig(t *testing.T) { 40 tests := map[string]struct { 41 networkID uint32 42 config *Config 43 expectedErr error 44 }{ 45 "mainnet": { 46 networkID: 1, 47 config: &MainnetConfig, 48 expectedErr: nil, 49 }, 50 "fuji": { 51 networkID: 5, 52 config: &FujiConfig, 53 expectedErr: nil, 54 }, 55 "local": { 56 networkID: 12345, 57 config: &LocalConfig, 58 expectedErr: nil, 59 }, 60 "mainnet (networkID mismatch)": { 61 networkID: 2, 62 config: &MainnetConfig, 63 expectedErr: errConflictingNetworkIDs, 64 }, 65 "invalid start time": { 66 networkID: 12345, 67 config: func() *Config { 68 thisConfig := LocalConfig 69 thisConfig.StartTime = 999999999999999 70 return &thisConfig 71 }(), 72 expectedErr: errFutureStartTime, 73 }, 74 "no initial supply": { 75 networkID: 12345, 76 config: func() *Config { 77 thisConfig := LocalConfig 78 thisConfig.Allocations = []Allocation{} 79 return &thisConfig 80 }(), 81 expectedErr: errNoSupply, 82 }, 83 "no initial stakers": { 84 networkID: 12345, 85 config: func() *Config { 86 thisConfig := LocalConfig 87 thisConfig.InitialStakers = []Staker{} 88 return &thisConfig 89 }(), 90 expectedErr: errNoStakers, 91 }, 92 "invalid initial stake duration": { 93 networkID: 12345, 94 config: func() *Config { 95 thisConfig := LocalConfig 96 thisConfig.InitialStakeDuration = 0 97 return &thisConfig 98 }(), 99 expectedErr: errNoStakeDuration, 100 }, 101 "too large initial stake duration": { 102 networkID: 12345, 103 config: func() *Config { 104 thisConfig := LocalConfig 105 thisConfig.InitialStakeDuration = uint64(genesisStakingCfg.MaxStakeDuration+time.Second) / uint64(time.Second) 106 return &thisConfig 107 }(), 108 expectedErr: errStakeDurationTooHigh, 109 }, 110 "invalid stake offset": { 111 networkID: 12345, 112 config: func() *Config { 113 thisConfig := LocalConfig 114 thisConfig.InitialStakeDurationOffset = 100000000 115 return &thisConfig 116 }(), 117 expectedErr: errInitialStakeDurationTooLow, 118 }, 119 "empty initial staked funds": { 120 networkID: 12345, 121 config: func() *Config { 122 thisConfig := LocalConfig 123 thisConfig.InitialStakedFunds = []ids.ShortID(nil) 124 return &thisConfig 125 }(), 126 expectedErr: errNoInitiallyStakedFunds, 127 }, 128 "duplicate initial staked funds": { 129 networkID: 12345, 130 config: func() *Config { 131 thisConfig := LocalConfig 132 thisConfig.InitialStakedFunds = append(thisConfig.InitialStakedFunds, thisConfig.InitialStakedFunds[0]) 133 return &thisConfig 134 }(), 135 expectedErr: errDuplicateInitiallyStakedAddress, 136 }, 137 "initial staked funds not in allocations": { 138 networkID: 5, 139 config: func() *Config { 140 thisConfig := FujiConfig 141 thisConfig.InitialStakedFunds = append(thisConfig.InitialStakedFunds, LocalConfig.InitialStakedFunds[0]) 142 return &thisConfig 143 }(), 144 expectedErr: errNoAllocationToStake, 145 }, 146 "empty C-Chain genesis": { 147 networkID: 12345, 148 config: func() *Config { 149 thisConfig := LocalConfig 150 thisConfig.CChainGenesis = "" 151 return &thisConfig 152 }(), 153 expectedErr: errNoCChainGenesis, 154 }, 155 "empty message": { 156 networkID: 12345, 157 config: func() *Config { 158 thisConfig := LocalConfig 159 thisConfig.Message = "" 160 return &thisConfig 161 }(), 162 expectedErr: nil, 163 }, 164 } 165 166 for name, test := range tests { 167 t.Run(name, func(t *testing.T) { 168 err := validateConfig(test.networkID, test.config, genesisStakingCfg) 169 require.ErrorIs(t, err, test.expectedErr) 170 }) 171 } 172 } 173 174 func TestGenesisFromFile(t *testing.T) { 175 tests := map[string]struct { 176 networkID uint32 177 customConfig []byte 178 missingFilepath string 179 expectedErr error 180 expectedHash string 181 }{ 182 "mainnet": { 183 networkID: constants.MainnetID, 184 customConfig: customGenesisConfigJSON, 185 expectedErr: errOverridesStandardNetworkConfig, 186 }, 187 "fuji": { 188 networkID: constants.FujiID, 189 customConfig: customGenesisConfigJSON, 190 expectedErr: errOverridesStandardNetworkConfig, 191 }, 192 "fuji (with custom specified)": { 193 networkID: constants.FujiID, 194 customConfig: localGenesisConfigJSON, // won't load 195 expectedErr: errOverridesStandardNetworkConfig, 196 }, 197 "local": { 198 networkID: constants.LocalID, 199 customConfig: customGenesisConfigJSON, 200 expectedErr: errOverridesStandardNetworkConfig, 201 }, 202 "local (with custom specified)": { 203 networkID: constants.LocalID, 204 customConfig: customGenesisConfigJSON, 205 expectedErr: errOverridesStandardNetworkConfig, 206 }, 207 "custom": { 208 networkID: 9999, 209 customConfig: customGenesisConfigJSON, 210 expectedErr: nil, 211 expectedHash: "a1d1838586db85fe94ab1143560c3356df9ba2445794b796bba050be89f4fcb4", 212 }, 213 "custom (networkID mismatch)": { 214 networkID: 9999, 215 customConfig: localGenesisConfigJSON, 216 expectedErr: errConflictingNetworkIDs, 217 }, 218 "custom (invalid format)": { 219 networkID: 9999, 220 customConfig: invalidGenesisConfigJSON, 221 expectedErr: errInvalidGenesisJSON, 222 }, 223 "custom (missing filepath)": { 224 networkID: 9999, 225 missingFilepath: "missing.json", 226 expectedErr: os.ErrNotExist, 227 }, 228 } 229 230 for name, test := range tests { 231 t.Run(name, func(t *testing.T) { 232 require := require.New(t) 233 234 // test loading of genesis from file 235 var customFile string 236 if len(test.customConfig) > 0 { 237 customFile = filepath.Join(t.TempDir(), "config.json") 238 require.NoError(perms.WriteFile(customFile, test.customConfig, perms.ReadWrite)) 239 } 240 241 if len(test.missingFilepath) > 0 { 242 customFile = test.missingFilepath 243 } 244 245 genesisBytes, _, err := FromFile(test.networkID, customFile, genesisStakingCfg) 246 require.ErrorIs(err, test.expectedErr) 247 if test.expectedErr == nil { 248 genesisHash := hex.EncodeToString(hashing.ComputeHash256(genesisBytes)) 249 require.Equal(test.expectedHash, genesisHash, "genesis hash mismatch") 250 251 _, err = genesis.Parse(genesisBytes) 252 require.NoError(err) 253 } 254 }) 255 } 256 } 257 258 func TestGenesisFromFlag(t *testing.T) { 259 tests := map[string]struct { 260 networkID uint32 261 customConfig []byte 262 expectedErr error 263 expectedHash string 264 }{ 265 "mainnet": { 266 networkID: constants.MainnetID, 267 expectedErr: errOverridesStandardNetworkConfig, 268 }, 269 "fuji": { 270 networkID: constants.FujiID, 271 expectedErr: errOverridesStandardNetworkConfig, 272 }, 273 "local": { 274 networkID: constants.LocalID, 275 expectedErr: errOverridesStandardNetworkConfig, 276 }, 277 "local (with custom specified)": { 278 networkID: constants.LocalID, 279 customConfig: customGenesisConfigJSON, 280 expectedErr: errOverridesStandardNetworkConfig, 281 }, 282 "custom": { 283 networkID: 9999, 284 customConfig: customGenesisConfigJSON, 285 expectedErr: nil, 286 expectedHash: "a1d1838586db85fe94ab1143560c3356df9ba2445794b796bba050be89f4fcb4", 287 }, 288 "custom (networkID mismatch)": { 289 networkID: 9999, 290 customConfig: localGenesisConfigJSON, 291 expectedErr: errConflictingNetworkIDs, 292 }, 293 "custom (invalid format)": { 294 networkID: 9999, 295 customConfig: invalidGenesisConfigJSON, 296 expectedErr: errInvalidGenesisJSON, 297 }, 298 "custom (missing content)": { 299 networkID: 9999, 300 expectedErr: errInvalidGenesisJSON, 301 }, 302 } 303 304 for name, test := range tests { 305 t.Run(name, func(t *testing.T) { 306 require := require.New(t) 307 308 // test loading of genesis content from flag/env-var 309 var genBytes []byte 310 if len(test.customConfig) == 0 { 311 // try loading a default config 312 var err error 313 switch test.networkID { 314 case constants.MainnetID: 315 genBytes, err = json.Marshal(&MainnetConfig) 316 require.NoError(err) 317 case constants.TestnetID: 318 genBytes, err = json.Marshal(&FujiConfig) 319 require.NoError(err) 320 case constants.LocalID: 321 genBytes, err = json.Marshal(&LocalConfig) 322 require.NoError(err) 323 default: 324 genBytes = make([]byte, 0) 325 } 326 } else { 327 genBytes = test.customConfig 328 } 329 content := base64.StdEncoding.EncodeToString(genBytes) 330 331 genesisBytes, _, err := FromFlag(test.networkID, content, genesisStakingCfg) 332 require.ErrorIs(err, test.expectedErr) 333 if test.expectedErr == nil { 334 genesisHash := hex.EncodeToString(hashing.ComputeHash256(genesisBytes)) 335 require.Equal(test.expectedHash, genesisHash, "genesis hash mismatch") 336 337 _, err = genesis.Parse(genesisBytes) 338 require.NoError(err) 339 } 340 }) 341 } 342 } 343 344 func TestGenesis(t *testing.T) { 345 tests := []struct { 346 config *Config 347 expectedID string 348 }{ 349 { 350 config: &MainnetConfig, 351 expectedID: "UUvXi6j7QhVvgpbKM89MP5HdrxKm9CaJeHc187TsDNf8nZdLk", 352 }, 353 { 354 config: &FujiConfig, 355 expectedID: "MSj6o9TpezwsQx4Tv7SHqpVvCbJ8of1ikjsqPZ1bKRjc9zBy3", 356 }, 357 { 358 config: &unmodifiedLocalConfig, 359 expectedID: "23DnViuN2kgePiBN4JxZXh1VrfXca2rwUp6XrKgNGdj3TSQjiN", 360 }, 361 } 362 for _, test := range tests { 363 t.Run(constants.NetworkIDToNetworkName[test.config.NetworkID], func(t *testing.T) { 364 require := require.New(t) 365 366 genesisBytes, _, err := FromConfig(test.config) 367 require.NoError(err) 368 369 var genesisID ids.ID = hashing.ComputeHash256Array(genesisBytes) 370 require.Equal(test.expectedID, genesisID.String()) 371 }) 372 } 373 } 374 375 func TestVMGenesis(t *testing.T) { 376 type vmTest struct { 377 vmID ids.ID 378 expectedID string 379 } 380 tests := []struct { 381 networkID uint32 382 vmTest []vmTest 383 }{ 384 { 385 networkID: constants.MainnetID, 386 vmTest: []vmTest{ 387 { 388 vmID: constants.AVMID, 389 expectedID: "2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM", 390 }, 391 { 392 vmID: constants.EVMID, 393 expectedID: "2q9e4r6Mu3U68nU1fYjgbR6JvwrRx36CohpAX5UQxse55x1Q5", 394 }, 395 }, 396 }, 397 { 398 networkID: constants.FujiID, 399 vmTest: []vmTest{ 400 { 401 vmID: constants.AVMID, 402 expectedID: "2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm", 403 }, 404 { 405 vmID: constants.EVMID, 406 expectedID: "yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp", 407 }, 408 }, 409 }, 410 { 411 networkID: constants.LocalID, 412 vmTest: []vmTest{ 413 { 414 vmID: constants.AVMID, 415 expectedID: "2eNy1mUFdmaxXNj1eQHUe7Np4gju9sJsEtWQ4MX3ToiNKuADed", 416 }, 417 { 418 vmID: constants.EVMID, 419 expectedID: "2CA6j5zYzasynPsFeNoqWkmTCt3VScMvXUZHbfDJ8k3oGzAPtU", 420 }, 421 }, 422 }, 423 } 424 425 for _, test := range tests { 426 for _, vmTest := range test.vmTest { 427 name := fmt.Sprintf("%s-%s", 428 constants.NetworkIDToNetworkName[test.networkID], 429 vmTest.vmID, 430 ) 431 t.Run(name, func(t *testing.T) { 432 require := require.New(t) 433 434 config := GetConfig(test.networkID) 435 genesisBytes, _, err := FromConfig(config) 436 require.NoError(err) 437 438 genesisTx, err := VMGenesis(genesisBytes, vmTest.vmID) 439 require.NoError(err) 440 441 require.Equal( 442 vmTest.expectedID, 443 genesisTx.ID().String(), 444 "%s genesisID with networkID %d mismatch", 445 vmTest.vmID, 446 test.networkID, 447 ) 448 }) 449 } 450 } 451 } 452 453 func TestAVAXAssetID(t *testing.T) { 454 tests := []struct { 455 networkID uint32 456 expectedID string 457 }{ 458 { 459 networkID: constants.MainnetID, 460 expectedID: "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", 461 }, 462 { 463 networkID: constants.FujiID, 464 expectedID: "U8iRqJoiJm8xZHAacmvYyZVwqQx6uDNtQeP3CQ6fcgQk3JqnK", 465 }, 466 { 467 networkID: constants.LocalID, 468 expectedID: "2fombhL7aGPwj3KH4bfrmJwW6PVnMobf9Y2fn9GwxiAAJyFDbe", 469 }, 470 } 471 472 for _, test := range tests { 473 t.Run(constants.NetworkIDToNetworkName[test.networkID], func(t *testing.T) { 474 require := require.New(t) 475 476 config := GetConfig(test.networkID) 477 _, avaxAssetID, err := FromConfig(config) 478 require.NoError(err) 479 480 require.Equal( 481 test.expectedID, 482 avaxAssetID.String(), 483 "AVAX assetID with networkID %d mismatch", 484 test.networkID, 485 ) 486 }) 487 } 488 }