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  }