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 }