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  }