code.vegaprotocol.io/vega@v0.79.0/cmd/vega/commands/verify/genesis.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package verify 17 18 import ( 19 "bytes" 20 "encoding/json" 21 22 "code.vegaprotocol.io/vega/core/events" 23 "code.vegaprotocol.io/vega/core/netparams" 24 vgjson "code.vegaprotocol.io/vega/libs/json" 25 "code.vegaprotocol.io/vega/libs/num" 26 "code.vegaprotocol.io/vega/logging" 27 ) 28 29 // These types are copies of the ones in the engines that read the genesis file appstate 30 // but the double-book keeping allows us to know when a change to the genesis state occurs. 31 type validator struct { 32 ID string `json:"id"` 33 VegaPubKey string `json:"vega_pub_key"` 34 VegaPubKeyIndex uint32 `json:"vega_pub_key_index"` 35 EthereumAddress string `json:"ethereum_address"` 36 TMPubKey string `json:"tm_pub_key"` 37 InfoURL string `json:"info_url"` 38 Country string `json:"country"` 39 Name string `json:"name"` 40 AvatarURL string `json:"avatar_url"` 41 FromEpoch uint64 `json:"from_epoch"` 42 SubmitterAddress string `json:"submitter_address"` 43 } 44 45 type asset struct { 46 Name string 47 Symbol string 48 Decimals uint64 49 Quantum string `json:"quantum"` 50 Source *struct { 51 BuiltInAsset *struct { 52 MaxFaucetAmountMint string `json:"max_faucet_amount_mint"` 53 } `json:"builtin_asset,omitempty"` 54 ERC20 *struct { 55 ContractAddress string `json:"contract_address"` 56 LifetimeLimit string `json:"lifetime_limit"` 57 WithdrawThreshold string `json:"withdraw_threshold"` 58 ChainID string `json:"chain_id"` 59 } `json:"erc20,omitempty"` 60 } 61 } 62 63 type appState struct { 64 Network *struct { 65 ReplayAttackThreshold *int `json:"replay_attack_threshold"` 66 } `json:"network"` 67 NetworkParameters map[string]string `json:"network_parameters"` 68 Validators map[string]validator `json:"validators"` 69 Assets map[string]asset `json:"assets"` 70 NetworkParametersCPOverwrite []string `json:"network_parameters_checkpoint_overwrite"` 71 NetworkLimits *json.RawMessage `json:"network_limits"` 72 Checkpoint *json.RawMessage `json:"checkpoint"` 73 } 74 75 type GenesisCmd struct{} 76 77 func (opts *GenesisCmd) Execute(params []string) error { 78 return verifier(params, verifyGenesis) 79 } 80 81 type noopBroker struct{} 82 83 func (n noopBroker) Send(e events.Event) {} 84 85 func (noopBroker) SendBatch(e []events.Event) {} 86 87 func verifyAssets(r *reporter, assets map[string]asset) { 88 if assets == nil { 89 return // this is fine 90 } 91 92 for k, v := range assets { 93 if n, failed := num.UintFromString(v.Quantum, 10); failed || n.IsNegative() || n.IsZero() { 94 r.Err("app_state.assets[%s].quantum not a valid positive number: %s", k, v.Quantum) 95 } 96 97 switch { 98 case v.Source == nil: 99 r.Err("app_state.assets[%s].source is missing", k) 100 case v.Source.BuiltInAsset != nil && v.Source.ERC20 != nil: 101 r.Err("app_state.assets[%s].source cannot be both builtin or ERC20", k) 102 case v.Source.BuiltInAsset != nil: 103 if _, failed := num.UintFromString(v.Source.BuiltInAsset.MaxFaucetAmountMint, 10); failed { 104 r.Err("app_state.assets[%s].source.builtin_asset.max_faucet_amount_mint is not a valid number: %s", 105 k, v.Source.BuiltInAsset.MaxFaucetAmountMint) 106 } 107 case v.Source.ERC20 != nil: 108 if !isValidParty(k) { 109 r.Err("app_state.assets contains an non valid asset id, `%v`", k) 110 } 111 if len(v.Source.ERC20.ContractAddress) <= 0 { 112 r.Err("app_state.assets[%s] contains an empty contract address", k) 113 } else if !isValidEthereumAddress(v.Source.ERC20.ContractAddress) { 114 r.Err("app_state.assets[%s] contains an invalid ethereum contract address %s", k, v.Source.ERC20.ContractAddress) 115 } 116 default: 117 r.Err("app_state.assets[%s].source must be either builtin or ERC20", k) 118 } 119 } 120 } 121 122 func verifyValidators(r *reporter, validators map[string]validator) { 123 if validators == nil || len(validators) <= 0 { 124 r.Warn("app_state.validators is missing or empty") 125 return 126 } 127 128 for key, v := range validators { 129 switch { 130 case len(key) <= 0: 131 r.Err("app_state.validators contains an empty key") 132 case !isValidCometBFTKey(key): 133 r.Err("app_state.validators contains an invalid CometBFT public key, `%v`", key) 134 case key != v.TMPubKey: 135 r.Err("app_state.validator[%v] hash mismatched CometBFT public key, `%v`", key, v.TMPubKey) 136 } 137 138 if !isValidParty(v.ID) { 139 r.Err("app_state.validators[%v] has an invalid id, `%v`", key, v.ID) 140 } 141 142 if !isValidParty(v.VegaPubKey) { 143 r.Err("app_state.validators[%v] has an invalid vega public key, `%v`", key, v.VegaPubKey) 144 } 145 146 if v.VegaPubKeyIndex == 0 { 147 r.Err("app_state.validators[%v] has an invalid vega public key index, `%v`", key, v.VegaPubKeyIndex) 148 } 149 150 if !isValidEthereumAddress(v.EthereumAddress) { 151 r.Err("app_state.validators[%v] has an invalid ethereum address, `%v`", key, v.EthereumAddress) 152 } 153 } 154 } 155 156 func verifyNetworkParameters(r *reporter, nps map[string]string, overwriteParameters []string) { 157 if nps == nil { 158 r.Err("app_state.network_parameters is missing") 159 return 160 } 161 162 log := logging.NewTestLogger() 163 164 netp := netparams.New( 165 log, 166 netparams.NewDefaultConfig(), 167 noopBroker{}, 168 ) 169 170 // check for no missing keys 171 for k := range netparams.AllKeys { 172 if _, ok := nps[k]; !ok { 173 val, _ := netp.Get(k) 174 r.Warn("app_state.network_parameters missing parameter `%v`, default value will be used `%v`", k, val) 175 } 176 } 177 178 // check for no unknown keys or invalid values 179 for k, v := range nps { 180 if _, ok := netparams.AllKeys[k]; !ok { 181 r.Err("appstate.network_parameters unknown parameter `%v`", k) 182 continue 183 } 184 185 if _, ok := netparams.Deprecated[k]; ok { 186 r.Err("appstate.network_parameters deprecated parameter `%v`", k) 187 continue 188 } 189 190 err := netp.Validate(k, v) 191 if err != nil { 192 r.Err("appstate.network_parameters invalid parameter `%v`, %v", k, err) 193 } 194 } 195 196 // check overwrite parameters are real 197 for _, k := range overwriteParameters { 198 if _, ok := netparams.AllKeys[k]; !ok { 199 r.Err("appstate.network_parameters_checkpoint_overwrite unknown parameter `%v`", k) 200 continue 201 } 202 } 203 } 204 205 func verifyGenesis(r *reporter, bs []byte) string { 206 // Unmarshal to get appstate 207 g := struct { 208 AppState json.RawMessage `json:"app_state"` 209 ConsensusParams struct { 210 Block struct { 211 TimeIotaMs string `json:"time_iota_ms"` 212 } `json:"block"` 213 } `json:"consensus_params"` 214 }{} 215 216 if err := json.Unmarshal(bs, &g); err != nil { 217 r.Err("unable to unmarshal genesis file, %v", err) 218 return "" 219 } 220 221 if g.ConsensusParams.Block.TimeIotaMs != "1" { 222 r.Err("consensus_params.block.time_iota_ms must be 1") 223 } 224 225 appstate := &appState{} 226 d := json.NewDecoder(bytes.NewBuffer(g.AppState)) 227 d.DisallowUnknownFields() // This allows us to fail if an appstate field is found which we don't know about 228 229 if err := d.Decode(appstate); err != nil { 230 r.Err("unable to unmarshal app_state in genesis file, %v", err) 231 return "" 232 } 233 234 if appstate.NetworkLimits == nil { 235 r.Warn("app_state.network_limits are missing, default values will be used") 236 } 237 238 switch { 239 case appstate.Network == nil: 240 r.Err("app_state.network is missing") 241 case appstate.Network.ReplayAttackThreshold == nil: 242 r.Err("app_state.network.replay_attach_threshold is missing") 243 case *appstate.Network.ReplayAttackThreshold < 0: 244 r.Err("app_state.network.replace_attach_threshold can't be < 0") 245 } 246 247 verifyNetworkParameters(r, appstate.NetworkParameters, appstate.NetworkParametersCPOverwrite) 248 verifyValidators(r, appstate.Validators) 249 verifyAssets(r, appstate.Assets) 250 251 out, _ := vgjson.Prettify(g) 252 return string(out) 253 }