github.com/kotalco/kotal@v0.3.0/apis/ethereum/v1alpha1/genesis_validation.go (about)

     1  package v1alpha1
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"reflect"
     7  	"sort"
     8  	"strings"
     9  
    10  	"k8s.io/apimachinery/pkg/util/validation/field"
    11  )
    12  
    13  var (
    14  	// ChainByID is public chains indexed by ID
    15  	ChainByID = map[uint]string{
    16  		1:        MainNetwork,
    17  		3:        RopstenNetwork,
    18  		4:        RinkebyNetwork,
    19  		5:        GoerliNetwork,
    20  		6:        KottiNetwork,
    21  		61:       ClassicNetwork,
    22  		63:       MordorNetwork,
    23  		2018:     DevNetwork,
    24  		11155111: SepoliaNetwork,
    25  	}
    26  )
    27  
    28  // EnabledConsensusConfigs returns enabled consensus configs
    29  func (g *Genesis) EnabledConsensusConfigs() []string {
    30  	configs := map[string]bool{
    31  		"ethash": g.Ethash != nil,
    32  		"clique": g.Clique != nil,
    33  		"ibft2":  g.IBFT2 != nil,
    34  	}
    35  
    36  	enabledConfigs := []string{}
    37  
    38  	for consensus, enabled := range configs {
    39  		if enabled {
    40  			enabledConfigs = append(enabledConfigs, consensus)
    41  		}
    42  	}
    43  
    44  	return enabledConfigs
    45  }
    46  
    47  // ReservedAccountIsUsed returns true if reserved account is used
    48  // reserved accounts are accounts from 0x00...01 to 0x00...ff (1 to 256)
    49  // reserved accounts are used for precompiles
    50  func (g *Genesis) ReservedAccountIsUsed() (bool, string) {
    51  	// space of reserved addresses
    52  	space := new(big.Int)
    53  	space.SetInt64(256)
    54  
    55  	for _, account := range g.Accounts {
    56  		address := string(account.Address)
    57  		i := new(big.Int)
    58  		i.SetString(address[2:], 16)
    59  		// address must be outside (with greater int value) reserved space
    60  		if i.Cmp(space) != 1 {
    61  			return true, address
    62  		}
    63  	}
    64  	return false, ""
    65  }
    66  
    67  // validate validates network genesis block spec
    68  func (g *Genesis) validate() field.ErrorList {
    69  
    70  	var allErrors field.ErrorList
    71  
    72  	// validate accounts from 0x00...01 to 0x00...ff are reserved
    73  	if used, address := g.ReservedAccountIsUsed(); used {
    74  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("accounts"), address, "reserved account is used")
    75  		allErrors = append(allErrors, err)
    76  	}
    77  
    78  	// validate consensus config (ethash, clique, ibft2) is not missing
    79  	// validate only one consensus configuration can be set
    80  	// TODO: update this validation after suporting new consensus algorithm
    81  	configs := g.EnabledConsensusConfigs()
    82  	if len(configs) == 0 {
    83  		err := field.Invalid(field.NewPath("spec").Child("genesis"), "", "consensus configuration (ethash, clique, or ibft2) is missing")
    84  		allErrors = append(allErrors, err)
    85  	} else if len(configs) > 1 {
    86  		sort.Strings(configs)
    87  		err := field.Invalid(field.NewPath("spec").Child("genesis"), "", fmt.Sprintf("multiple consensus configurations (%s) are enabled", strings.Join(configs, ", ")))
    88  		allErrors = append(allErrors, err)
    89  	}
    90  
    91  	// don't use existing network chain id
    92  	if chain := ChainByID[g.ChainID]; chain != "" {
    93  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("chainId"), fmt.Sprintf("%d", g.ChainID), fmt.Sprintf("can't use chain id of %s network to avoid tx replay", chain))
    94  		allErrors = append(allErrors, err)
    95  	}
    96  
    97  	// validate forks order
    98  	allErrors = append(allErrors, g.ValidateForksOrder()...)
    99  	return allErrors
   100  }
   101  
   102  // ValidateForksOrder validates that forks are in correct order
   103  func (g *Genesis) ValidateForksOrder() field.ErrorList {
   104  	var orderErrors field.ErrorList
   105  	forks := g.Forks
   106  
   107  	forkNames := []string{
   108  		"homestead",
   109  		"eip150",
   110  		"eip155",
   111  		"eip155",
   112  		"byzantium",
   113  		"constantinople",
   114  		"petersburg",
   115  		"istanbul",
   116  		"muirglacier",
   117  		"berlin",
   118  		"london",
   119  		"arrowglacier",
   120  	}
   121  
   122  	// milestones at the correct order
   123  	milestones := []uint{
   124  		forks.Homestead,
   125  		forks.EIP150,
   126  		forks.EIP155,
   127  		forks.EIP155,
   128  		forks.Byzantium,
   129  		forks.Constantinople,
   130  		forks.Petersburg,
   131  		forks.Istanbul,
   132  		forks.MuirGlacier,
   133  		forks.Berlin,
   134  		forks.London,
   135  		forks.ArrowGlacier,
   136  	}
   137  
   138  	for i := 1; i < len(milestones); i++ {
   139  		if milestones[i] < milestones[i-1] {
   140  			path := field.NewPath("spec").Child("genesis").Child("forks").Child(forkNames[i])
   141  			msg := fmt.Sprintf("Fork %s can't be activated (at block %d) before fork %s (at block %d)", forkNames[i], milestones[i], forkNames[i-1], milestones[i-1])
   142  			orderErrors = append(orderErrors, field.Invalid(path, fmt.Sprintf("%d", milestones[i]), msg))
   143  		}
   144  	}
   145  
   146  	return orderErrors
   147  
   148  }
   149  
   150  // ValidateCreate validates genesis block during node creation
   151  func (g *Genesis) ValidateCreate() field.ErrorList {
   152  	var allErrors field.ErrorList
   153  
   154  	allErrors = append(allErrors, g.validate()...)
   155  
   156  	return allErrors
   157  }
   158  
   159  func (g *Genesis) ValidateUpdate(oldGenesis *Genesis) field.ErrorList {
   160  	var allErrors field.ErrorList
   161  
   162  	if g.Coinbase != oldGenesis.Coinbase {
   163  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("coinbase"), g.Coinbase, "field is immutable")
   164  		allErrors = append(allErrors, err)
   165  	}
   166  
   167  	if g.Difficulty != oldGenesis.Difficulty {
   168  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("difficulty"), g.Difficulty, "field is immutable")
   169  		allErrors = append(allErrors, err)
   170  	}
   171  
   172  	if g.MixHash != oldGenesis.MixHash {
   173  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("mixHash"), g.MixHash, "field is immutable")
   174  		allErrors = append(allErrors, err)
   175  	}
   176  
   177  	if g.GasLimit != oldGenesis.GasLimit {
   178  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("gasLimit"), g.GasLimit, "field is immutable")
   179  		allErrors = append(allErrors, err)
   180  	}
   181  
   182  	if g.Nonce != oldGenesis.Nonce {
   183  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("nonce"), g.Nonce, "field is immutable")
   184  		allErrors = append(allErrors, err)
   185  	}
   186  
   187  	if g.Timestamp != oldGenesis.Timestamp {
   188  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("timestamp"), g.Timestamp, "field is immutable")
   189  		allErrors = append(allErrors, err)
   190  	}
   191  
   192  	if !reflect.DeepEqual(g.Accounts, oldGenesis.Accounts) {
   193  		err := field.Invalid(field.NewPath("spec").Child("genesis").Child("accounts"), "", "field is immutable")
   194  		allErrors = append(allErrors, err)
   195  	}
   196  
   197  	allErrors = append(allErrors, g.validate()...)
   198  
   199  	return allErrors
   200  }