sui_genesis_builder/
validator_info.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use anyhow::bail;
5use fastcrypto::traits::ToFromBytes;
6use serde::{Deserialize, Serialize};
7use serde_with::serde_as;
8use sui_types::base_types::SuiAddress;
9use sui_types::crypto::{
10    AuthorityPublicKey, AuthorityPublicKeyBytes, AuthoritySignature, NetworkPublicKey,
11    verify_proof_of_possession,
12};
13use sui_types::multiaddr::Multiaddr;
14
15const MAX_VALIDATOR_METADATA_LENGTH: usize = 256;
16
17/// Publicly known information about a validator
18#[serde_as]
19#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
20#[serde(rename_all = "kebab-case")]
21pub struct ValidatorInfo {
22    pub name: String,
23    pub account_address: SuiAddress,
24    pub protocol_key: AuthorityPublicKeyBytes,
25    pub worker_key: NetworkPublicKey,
26    pub network_key: NetworkPublicKey,
27    pub gas_price: u64,
28    pub commission_rate: u64,
29    pub network_address: Multiaddr,
30    pub p2p_address: Multiaddr,
31    pub narwhal_primary_address: Multiaddr,
32    pub narwhal_worker_address: Multiaddr,
33    pub description: String,
34    pub image_url: String,
35    pub project_url: String,
36}
37
38impl ValidatorInfo {
39    pub fn name(&self) -> &str {
40        &self.name
41    }
42
43    pub fn sui_address(&self) -> SuiAddress {
44        self.account_address
45    }
46
47    pub fn protocol_key(&self) -> AuthorityPublicKeyBytes {
48        self.protocol_key
49    }
50
51    pub fn worker_key(&self) -> &NetworkPublicKey {
52        &self.worker_key
53    }
54
55    pub fn network_key(&self) -> &NetworkPublicKey {
56        &self.network_key
57    }
58
59    pub fn gas_price(&self) -> u64 {
60        self.gas_price
61    }
62
63    pub fn commission_rate(&self) -> u64 {
64        self.commission_rate
65    }
66
67    pub fn network_address(&self) -> &Multiaddr {
68        &self.network_address
69    }
70
71    pub fn narwhal_primary_address(&self) -> &Multiaddr {
72        &self.narwhal_primary_address
73    }
74
75    pub fn narwhal_worker_address(&self) -> &Multiaddr {
76        &self.narwhal_worker_address
77    }
78
79    pub fn p2p_address(&self) -> &Multiaddr {
80        &self.p2p_address
81    }
82}
83
84#[serde_as]
85#[derive(Clone, Debug, Serialize, Deserialize)]
86pub struct GenesisValidatorInfo {
87    pub info: ValidatorInfo,
88    pub proof_of_possession: AuthoritySignature,
89}
90
91impl GenesisValidatorInfo {
92    pub fn validate(&self) -> anyhow::Result<(), anyhow::Error> {
93        if !self.info.name.is_ascii() {
94            bail!("name must be ascii");
95        }
96        if self.info.name.len() > MAX_VALIDATOR_METADATA_LENGTH {
97            bail!("name must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
98        }
99
100        if !self.info.description.is_ascii() {
101            bail!("description must be ascii");
102        }
103        if self.info.description.len() > MAX_VALIDATOR_METADATA_LENGTH {
104            bail!("description must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
105        }
106
107        if self.info.image_url.len() > MAX_VALIDATOR_METADATA_LENGTH {
108            bail!("image url must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
109        }
110
111        if self.info.project_url.len() > MAX_VALIDATOR_METADATA_LENGTH {
112            bail!("project url must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
113        }
114
115        if !self.info.network_address.to_string().is_ascii() {
116            bail!("network address must be ascii");
117        }
118        if self.info.network_address.len() > MAX_VALIDATOR_METADATA_LENGTH {
119            bail!("network address must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
120        }
121
122        if !self.info.p2p_address.to_string().is_ascii() {
123            bail!("p2p address must be ascii");
124        }
125        if self.info.p2p_address.len() > MAX_VALIDATOR_METADATA_LENGTH {
126            bail!("p2p address must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
127        }
128
129        if !self.info.narwhal_primary_address.to_string().is_ascii() {
130            bail!("primary address must be ascii");
131        }
132        if self.info.narwhal_primary_address.len() > MAX_VALIDATOR_METADATA_LENGTH {
133            bail!("primary address must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
134        }
135
136        if !self.info.narwhal_worker_address.to_string().is_ascii() {
137            bail!("worker address must be ascii");
138        }
139        if self.info.narwhal_worker_address.len() > MAX_VALIDATOR_METADATA_LENGTH {
140            bail!("worker address must be <= {MAX_VALIDATOR_METADATA_LENGTH} bytes long");
141        }
142
143        if let Err(e) = self.info.p2p_address.to_anemo_address() {
144            bail!("p2p address must be valid anemo address: {e}");
145        }
146        if let Err(e) = self.info.narwhal_primary_address.to_anemo_address() {
147            bail!("primary address must be valid anemo address: {e}");
148        }
149        if let Err(e) = self.info.narwhal_worker_address.to_anemo_address() {
150            bail!("worker address must be valid anemo address: {e}");
151        }
152
153        if self.info.commission_rate > 10000 {
154            bail!("commissions rate must be lower than 100%");
155        }
156
157        let protocol_pubkey = AuthorityPublicKey::from_bytes(self.info.protocol_key.as_ref())?;
158        if let Err(e) = verify_proof_of_possession(
159            &self.proof_of_possession,
160            &protocol_pubkey,
161            self.info.account_address,
162        ) {
163            bail!("proof of possession is incorrect: {e}");
164        }
165
166        Ok(())
167    }
168}
169
170impl From<GenesisValidatorInfo> for GenesisValidatorMetadata {
171    fn from(
172        GenesisValidatorInfo {
173            info,
174            proof_of_possession,
175        }: GenesisValidatorInfo,
176    ) -> Self {
177        Self {
178            name: info.name,
179            description: info.description,
180            image_url: info.image_url,
181            project_url: info.project_url,
182            sui_address: info.account_address,
183            gas_price: info.gas_price,
184            commission_rate: info.commission_rate,
185            protocol_public_key: info.protocol_key.as_bytes().to_vec(),
186            proof_of_possession: proof_of_possession.as_ref().to_vec(),
187            network_public_key: info.network_key.as_bytes().to_vec(),
188            worker_public_key: info.worker_key.as_bytes().to_vec(),
189            network_address: info.network_address,
190            p2p_address: info.p2p_address,
191            primary_address: info.narwhal_primary_address,
192            worker_address: info.narwhal_worker_address,
193        }
194    }
195}
196
197#[derive(Clone, Debug, Serialize, Deserialize)]
198#[serde(rename_all = "kebab-case")]
199pub struct GenesisValidatorMetadata {
200    pub name: String,
201    pub description: String,
202    pub image_url: String,
203    pub project_url: String,
204
205    pub sui_address: SuiAddress,
206
207    pub gas_price: u64,
208    pub commission_rate: u64,
209
210    pub protocol_public_key: Vec<u8>, //AuthorityPublicKeyBytes,
211    pub proof_of_possession: Vec<u8>, // AuthoritySignature,
212
213    pub network_public_key: Vec<u8>, // NetworkPublicKey,
214    pub worker_public_key: Vec<u8>,  // NetworkPublicKey,
215
216    pub network_address: Multiaddr,
217    pub p2p_address: Multiaddr,
218    pub primary_address: Multiaddr,
219    pub worker_address: Multiaddr,
220}