github.com/MetalBlockchain/metalgo@v1.11.9/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/MetalBlockchain/metalgo/ids" 21 "github.com/MetalBlockchain/metalgo/utils/constants" 22 "github.com/MetalBlockchain/metalgo/utils/hashing" 23 "github.com/MetalBlockchain/metalgo/utils/perms" 24 "github.com/MetalBlockchain/metalgo/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 "tahoe": { 51 networkID: 5, 52 config: &TahoeConfig, 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 := TahoeConfig 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 "testnet": { 188 networkID: constants.TahoeID, 189 customConfig: customGenesisConfigJSON, 190 expectedErr: errOverridesStandardNetworkConfig, 191 }, 192 "testnet (with custom specified)": { 193 networkID: constants.TahoeID, 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: "18dc0a7ab9b257b86ee65870649a0dabbb76c03d0ca9f5c44a43ce0d0a7f20c6", 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 "tahoe": { 270 networkID: constants.TahoeID, 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: "18dc0a7ab9b257b86ee65870649a0dabbb76c03d0ca9f5c44a43ce0d0a7f20c6", 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.TahoeID: 318 genBytes, err = json.Marshal(&TahoeConfig) 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 networkID uint32 347 expectedID string 348 }{ 349 { 350 networkID: constants.MainnetID, 351 expectedID: "2QWBywoaRDBk7Z1YmnGm4W2Mid5CUqBHGWdiHg45uRTJTQ8TDD", 352 }, 353 { 354 networkID: constants.TahoeID, 355 expectedID: "tZW9eSc1k8bMxQusjYSVucoJQhuRaWimezf5feGQFxiPgk85q", 356 }, 357 { 358 networkID: constants.LocalID, 359 expectedID: "LSxVrtM25cDTLmMcjeZRXBYCrof6sAKyGh2SUfN7SezxsN62x", 360 }, 361 } 362 for _, test := range tests { 363 t.Run(constants.NetworkIDToNetworkName[test.networkID], func(t *testing.T) { 364 require := require.New(t) 365 366 config := GetConfig(test.networkID) 367 genesisBytes, _, err := FromConfig(config) 368 require.NoError(err) 369 370 var genesisID ids.ID = hashing.ComputeHash256Array(genesisBytes) 371 require.Equal(test.expectedID, genesisID.String()) 372 }) 373 } 374 } 375 376 func TestVMGenesis(t *testing.T) { 377 type vmTest struct { 378 vmID ids.ID 379 expectedID string 380 } 381 tests := []struct { 382 networkID uint32 383 vmTest []vmTest 384 }{ 385 { 386 networkID: constants.MainnetID, 387 vmTest: []vmTest{ 388 { 389 vmID: constants.AVMID, 390 expectedID: "UQg9hfKuviMwwkR16hE8nHyrmG6f5tax5seEoqUSiBmsTghXE", 391 }, 392 { 393 vmID: constants.EVMID, 394 expectedID: "28fJD1hMz2PSRJKJt7YT41urTPR37rUNUcdeJ8daoiwP6DGnAR", 395 }, 396 }, 397 }, 398 { 399 networkID: constants.TahoeID, 400 vmTest: []vmTest{ 401 { 402 vmID: constants.AVMID, 403 expectedID: "N8BzztcRDHj6nNcGLbdimm6FSwE34rSVSgxhcV18TAaYSa4Q8", 404 }, 405 { 406 vmID: constants.EVMID, 407 expectedID: "t34kbq3fgdNaurCHn4aJpayuE46vh5AozKPZZG6MrjE2F7XP6", 408 }, 409 }, 410 }, 411 { 412 networkID: constants.LocalID, 413 vmTest: []vmTest{ 414 { 415 vmID: constants.AVMID, 416 expectedID: "2EwQEpAoKi3b8GgAhKGprkGiJYFduE9bbbEANMWioSWXsb6ZQz", 417 }, 418 { 419 vmID: constants.EVMID, 420 expectedID: "2CA6j5zYzasynPsFeNoqWkmTCt3VScMvXUZHbfDJ8k3oGzAPtU", 421 }, 422 }, 423 }, 424 } 425 426 for _, test := range tests { 427 for _, vmTest := range test.vmTest { 428 name := fmt.Sprintf("%s-%s", 429 constants.NetworkIDToNetworkName[test.networkID], 430 vmTest.vmID, 431 ) 432 t.Run(name, func(t *testing.T) { 433 require := require.New(t) 434 435 config := GetConfig(test.networkID) 436 genesisBytes, _, err := FromConfig(config) 437 require.NoError(err) 438 439 genesisTx, err := VMGenesis(genesisBytes, vmTest.vmID) 440 require.NoError(err) 441 442 require.Equal( 443 vmTest.expectedID, 444 genesisTx.ID().String(), 445 "%s genesisID with networkID %d mismatch", 446 vmTest.vmID, 447 test.networkID, 448 ) 449 }) 450 } 451 } 452 } 453 454 func TestAVAXAssetID(t *testing.T) { 455 tests := []struct { 456 networkID uint32 457 expectedID string 458 }{ 459 { 460 networkID: constants.MainnetID, 461 expectedID: "2pjq58dnYTfrUJvvnC1uHDBP87DyP2oJj9uTmt3vdJg9Nhr9d4", 462 }, 463 { 464 networkID: constants.TahoeID, 465 expectedID: "2QpCJwPk3nzi1VqJEuaFA44WM2UUzraBXQyH6jMGLTLQhqoe4n", 466 }, 467 { 468 networkID: constants.LocalID, 469 expectedID: "ui6eSQwLELPt3zZsreVkdSFhimBdAyXgADd7CGfQ58NeDNzCS", 470 }, 471 } 472 473 for _, test := range tests { 474 t.Run(constants.NetworkIDToNetworkName[test.networkID], func(t *testing.T) { 475 require := require.New(t) 476 477 config := GetConfig(test.networkID) 478 _, avaxAssetID, err := FromConfig(config) 479 require.NoError(err) 480 481 require.Equal( 482 test.expectedID, 483 avaxAssetID.String(), 484 "AVAX assetID with networkID %d mismatch", 485 test.networkID, 486 ) 487 }) 488 } 489 }