diff --git a/Cargo.lock b/Cargo.lock index 092f83eb..c98110de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -40,9 +40,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" dependencies = [ "axum-core", "bytes", @@ -122,15 +122,15 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" -version = "1.2.41" +version = "1.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" dependencies = [ "find-msvc-tools", "shlex", @@ -330,9 +330,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fixedbitset" @@ -449,12 +449,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -495,9 +494,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -516,9 +515,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "bytes", "futures-core", @@ -600,9 +599,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -779,9 +778,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -840,9 +839,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -980,9 +979,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1317c3bf3e7df961da95b0a56a172a02abead31276215a0497241a7624b487ce" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -1064,9 +1063,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.1" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64", "chrono", @@ -1074,7 +1073,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.0.5", + "schemars 1.1.0", "serde_core", "serde_json", "serde_with_macros", @@ -1083,9 +1082,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.1" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", "proc-macro2", @@ -1101,9 +1100,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -1132,9 +1131,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.107" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -1278,9 +1277,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -1289,18 +1288,18 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", ] [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "uuid" @@ -1330,9 +1329,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1341,25 +1340,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1367,22 +1352,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -1537,18 +1522,18 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 0b1ac00a..e3e902c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ prost-types = "0.14" prost-build = "0.14" rand = {version = "0.8", features = ["std_rng"]} uuid = {version = "1.16", features = ["serde"] } -serde_with = { version = "3", features = ["hex"] } +serde_with = { version = "3", features = ["hex", "base64"] } charon-crypto = { path = "crates/charon-crypto" } [workspace.lints.rust] diff --git a/crates/charon-cluster/src/definition.rs b/crates/charon-cluster/src/definition.rs index 69f60845..b4461007 100644 --- a/crates/charon-cluster/src/definition.rs +++ b/crates/charon-cluster/src/definition.rs @@ -1,24 +1,20 @@ -use crate::helpers::EthHex; +use crate::{ + helpers::EthHex, + operator::{Operator, OperatorV1X1, OperatorV1X2OrLater}, + version::versions::*, +}; use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use serde_with::{DisplayFromStr, PickFirst, serde_as}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_with::{ + DisplayFromStr, PickFirst, + base64::{Base64, Standard}, + serde_as, +}; use uuid::Uuid; -use crate::operator::Operator; - /// Definition defines an intended charon cluster configuration excluding -/// validators. Note the following struct tag meanings: -/// - json: json field name. Suffix 0xhex indicates bytes are formatted as 0x -/// prefixed hex strings. -/// - ssz: ssz equivalent. Either uint64 for numbers, BytesN for fixed length -/// bytes, ByteList[MaxN] for variable length strings, or -/// CompositeList[MaxN] for nested object arrays. -/// - config_hash: field ordering when calculating config hash. Some fields -/// are excluded indicated by `-`. -/// - definition_hash: field ordering when calculating definition hash. Some -/// fields are excluded indicated by `-`. -#[serde_as] -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +/// validators. +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Definition { /// UUID is a human-readable random unique identifier. Max 64 chars. pub uuid: Uuid, @@ -40,7 +36,6 @@ pub struct Definition { pub dkg_algorithm: String, /// ForkVersion defines the cluster's 4 byte beacon chain fork version /// (network/chain identifier). - #[serde_as(as = "EthHex")] pub fork_version: Vec, /// Operators define the charon nodes in the cluster and their operators. /// Max 256 operators. @@ -49,11 +44,9 @@ pub struct Definition { /// an operator. pub creator: Creator, /// ValidatorAddresses define addresses of each validator. - #[serde(rename = "validators")] pub validator_addresses: Vec, /// DepositAmounts specifies partial deposit amounts that sum up to at least /// 32ETH. - #[serde_as(as = "Vec>")] pub deposit_amounts: Vec, /// ConsensusProtocol is the consensus protocol name preferred by the /// cluster, e.g. "abft". @@ -65,18 +58,131 @@ pub struct Definition { pub compounding: bool, /// ConfigHash uniquely identifies a cluster definition excluding operator /// ENRs and signatures. - #[serde_as(as = "EthHex")] pub config_hash: Vec, /// DefinitionHash uniquely identifies a cluster definition including /// operator ENRs and signatures. - #[serde_as(as = "EthHex")] pub definition_hash: Vec, } +impl Serialize for Definition { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self.version.as_str() { + V1_0 | V1_1 => DefinitionV1x0or1::try_from(self.clone()) + .map_err(|e| serde::ser::Error::custom(format!("Conversion error: {:?}", e)))? + .serialize(serializer), + V1_2 | V1_3 => DefinitionV1x2or3::try_from(self.clone()) + .map_err(|e| serde::ser::Error::custom(format!("Conversion error: {:?}", e)))? + .serialize(serializer), + V1_4 => DefinitionV1x4::try_from(self.clone()) + .map_err(|e| serde::ser::Error::custom(format!("Conversion error: {:?}", e)))? + .serialize(serializer), + V1_5 | V1_6 | V1_7 => DefinitionV1x5to7::from(self.clone()).serialize(serializer), + V1_8 => DefinitionV1x8::from(self.clone()).serialize(serializer), + V1_9 => DefinitionV1x9::from(self.clone()).serialize(serializer), + V1_10 => DefinitionV1x10::from(self.clone()).serialize(serializer), + _ => Err(serde::ser::Error::custom(format!( + "Unsupported version: {}", + self.version + ))), + } + } +} + +impl<'de> Deserialize<'de> for Definition { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + + let value = serde_json::Value::deserialize(deserializer)?; + + let version = value + .get("version") + .and_then(|v| v.as_str()) + .ok_or_else(|| Error::custom("Missing 'version' field"))?; + + match version { + V1_0 | V1_1 => { + let definition: DefinitionV1x0or1 = + serde_json::from_value(value).map_err(Error::custom)?; + definition + .try_into() + .map_err(|e| Error::custom(format!("Conversion error: {:?}", e))) + } + V1_2 | V1_3 => { + let definition: DefinitionV1x2or3 = + serde_json::from_value(value).map_err(Error::custom)?; + definition + .try_into() + .map_err(|e| Error::custom(format!("Conversion error: {:?}", e))) + } + V1_4 => { + let definition: DefinitionV1x4 = + serde_json::from_value(value).map_err(Error::custom)?; + definition + .try_into() + .map_err(|e| Error::custom(format!("Conversion error: {:?}", e))) + } + V1_5 | V1_6 | V1_7 => { + let definition: DefinitionV1x5to7 = + serde_json::from_value(value).map_err(Error::custom)?; + Ok(definition.into()) + } + V1_8 => { + let definition: DefinitionV1x8 = + serde_json::from_value(value).map_err(Error::custom)?; + Ok(definition.into()) + } + V1_9 => { + let definition: DefinitionV1x9 = + serde_json::from_value(value).map_err(Error::custom)?; + Ok(definition.into()) + } + V1_10 => { + let definition: DefinitionV1x10 = + serde_json::from_value(value).map_err(Error::custom)?; + Ok(definition.into()) + } + _ => Err(Error::custom(format!("Unsupported version: {}", version))), + } + } +} + +/// DefinitionError is an error type for definition errors. +#[derive(Debug, thiserror::Error)] +pub enum DefinitionError { + /// InvalidValidatorAddresses is returned when multiple validator addresses + /// are found. + #[error("Multiple withdrawal or fee recipient addresses found")] + InvalidValidatorAddresses, +} + +impl Definition { + /// LegacyValidatorAddresses returns the legacy single withdrawal and single + /// fee recipient addresses or an error if multiple addresses are found. + pub fn legacy_validator_addresses(&self) -> Result { + let mut result_validator_addresses = ValidatorAddresses::default(); + + for (i, validator_addresses) in self.validator_addresses.iter().enumerate() { + if i == 0 { + result_validator_addresses = validator_addresses.clone(); + } else if validator_addresses != &result_validator_addresses { + return Err(DefinitionError::InvalidValidatorAddresses); + } + } + + Ok(result_validator_addresses) + } +} + /// Creator identifies the creator of a cluster definition. They may also be an /// operator. #[serde_as] -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Creator { /// The Ethereum address of the creator pub address: String, @@ -86,7 +192,7 @@ pub struct Creator { } /// ValidatorAddresses define addresses for a validator -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct ValidatorAddresses { /// The fee recipient address for the validator pub fee_recipient_address: String, @@ -94,12 +200,775 @@ pub struct ValidatorAddresses { pub withdrawal_address: String, } +/// DefinitionV1x0or1 is a cluster definition for version 1.0.0 or 1.1.0 +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DefinitionV1x0or1 { + /// Name is a human-readable cosmetic identifier. Max 256 chars. + pub name: String, + /// Operators define the charon nodes in the cluster and their operators. + /// Max 256 operators. + pub operators: Vec, + /// UUID is a human-readable random unique identifier. Max 64 chars. + pub uuid: Uuid, + /// Version is the schema version of this definition. Max 16 chars. + pub version: String, + /// Timestamp is the human-readable timestamp of this definition. Max 32 + /// chars. Note that this was added in v1.1.0, so may be empty for older + /// versions. + pub timestamp: DateTime, + /// NumValidators is the number of DVs to be created in the cluster lock + /// file. + pub num_validators: u64, + /// Threshold required for signature reconstruction. Defaults to safe value + /// for number of nodes/peers. + pub threshold: u64, + /// FeeRecipientAddress is the address of the fee recipient for the + /// validator. + pub fee_recipient_address: String, + /// WithdrawalAddress is the address of the withdrawal address for the + /// validator. + pub withdrawal_address: String, + /// DKGAlgorithm to use for key generation. Max 32 chars. + pub dkg_algorithm: String, + /// ForkVersion defines the cluster's 4 byte beacon chain fork version + /// (network/chain identifier). + #[serde_as(as = "EthHex")] + pub fork_version: Vec, + /// ConfigHash uniquely identifies a cluster definition excluding operator + /// ENRs and signatures. + #[serde_as(as = "Base64")] + pub config_hash: Vec, + /// DefinitionHash uniquely identifies a cluster definition including + /// operator ENRs and signatures. + #[serde_as(as = "Base64")] + pub definition_hash: Vec, +} + +impl TryFrom for DefinitionV1x0or1 { + type Error = DefinitionError; + + fn try_from(definition: Definition) -> Result { + let validator_addresses = definition.legacy_validator_addresses()?; + + Ok(Self { + name: definition.name, + operators: definition + .operators + .into_iter() + .map(OperatorV1X1::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + fee_recipient_address: validator_addresses.fee_recipient_address, + withdrawal_address: validator_addresses.withdrawal_address, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + }) + } +} + +impl TryFrom for Definition { + type Error = DefinitionError; + + fn try_from(definition: DefinitionV1x0or1) -> Result { + let validator_addresses = ValidatorAddresses { + fee_recipient_address: definition.fee_recipient_address, + withdrawal_address: definition.withdrawal_address, + }; + + let validator_addresses = + repeat_v_addresses(validator_addresses, definition.num_validators); + + Ok(Self { + name: definition.name, + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + operators: definition + .operators + .into_iter() + .map(Operator::from) + .collect(), + creator: Creator::default(), + validator_addresses, + deposit_amounts: Vec::new(), + consensus_protocol: String::new(), + target_gas_limit: 0, + compounding: false, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + }) + } +} + +/// DefinitionV1x2or3 is a cluster definition for version 1.2.0 or 1.3.0 +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DefinitionV1x2or3 { + /// Name is a human-readable cosmetic identifier. Max 256 chars. + pub name: String, + /// Operators define the charon nodes in the cluster and their operators. + /// Max 256 operators. + pub operators: Vec, + /// UUID is a human-readable random unique identifier. Max 64 chars. + pub uuid: Uuid, + /// Version is the schema version of this definition. Max 16 chars. + pub version: String, + /// Timestamp is the human-readable timestamp of this definition. Max 32 + /// chars. Note that this was added in v1.1.0, so may be empty for older + /// versions. + pub timestamp: DateTime, + /// NumValidators is the number of DVs to be created in the cluster lock + /// file. + pub num_validators: u64, + /// Threshold required for signature reconstruction. Defaults to safe value + /// for number of nodes/peers. + pub threshold: u64, + /// FeeRecipientAddress is the address of the fee recipient for the + /// validator. + pub fee_recipient_address: String, + /// WithdrawalAddress is the address of the withdrawal address for the + /// validator. + pub withdrawal_address: String, + /// DKGAlgorithm to use for key generation. Max 32 chars. + pub dkg_algorithm: String, + /// ForkVersion defines the cluster's 4 byte beacon chain fork version + /// (network/chain identifier). + #[serde_as(as = "EthHex")] + pub fork_version: Vec, + /// ConfigHash uniquely identifies a cluster definition excluding operator + /// ENRs and signatures. + #[serde_as(as = "EthHex")] + pub config_hash: Vec, + /// DefinitionHash uniquely identifies a cluster definition including + /// operator ENRs and signatures. + #[serde_as(as = "EthHex")] + pub definition_hash: Vec, +} + +impl TryFrom for DefinitionV1x2or3 { + type Error = DefinitionError; + + fn try_from(definition: Definition) -> Result { + let validator_addresses = definition.legacy_validator_addresses()?; + + Ok(Self { + name: definition.name, + operators: definition + .operators + .into_iter() + .map(OperatorV1X2OrLater::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + fee_recipient_address: validator_addresses.fee_recipient_address, + withdrawal_address: validator_addresses.withdrawal_address, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + }) + } +} + +impl TryFrom for Definition { + type Error = DefinitionError; + + fn try_from(definition: DefinitionV1x2or3) -> Result { + let validator_addresses = ValidatorAddresses { + fee_recipient_address: definition.fee_recipient_address, + withdrawal_address: definition.withdrawal_address, + }; + + let validator_addresses = + repeat_v_addresses(validator_addresses, definition.num_validators); + + Ok(Self { + name: definition.name, + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + operators: definition + .operators + .into_iter() + .map(Operator::from) + .collect(), + creator: Creator::default(), + validator_addresses, + deposit_amounts: Vec::new(), + consensus_protocol: String::new(), + target_gas_limit: 0, + compounding: false, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + }) + } +} + +/// DefinitionV1x4 is a cluster definition for version 1.4.0 +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DefinitionV1x4 { + /// Name is a human-readable cosmetic identifier. Max 256 chars. + pub name: String, + /// Creator identifies the creator of a cluster definition. They may also be + /// an operator. + pub creator: Creator, + /// Operators define the charon nodes in the cluster and their operators. + /// Max 256 operators. + pub operators: Vec, + /// UUID is a human-readable random unique identifier. Max 64 chars. + pub uuid: Uuid, + /// Version is the schema version of this definition. Max 16 chars. + pub version: String, + /// Timestamp is the human-readable timestamp of this definition. Max 32 + /// chars. Note that this was added in v1.1.0, so may be empty for older + /// versions. + pub timestamp: DateTime, + /// NumValidators is the number of DVs to be created in the cluster lock + /// file. + pub num_validators: u64, + /// Threshold required for signature reconstruction. Defaults to safe value + /// for number of nodes/peers. + pub threshold: u64, + /// FeeRecipientAddress is the address of the fee recipient for the + /// validator. + pub fee_recipient_address: String, + /// WithdrawalAddress is the address of the withdrawal address for the + /// validator. + pub withdrawal_address: String, + /// DKGAlgorithm to use for key generation. Max 32 chars. + pub dkg_algorithm: String, + /// ForkVersion defines the cluster's 4 byte beacon chain fork version + /// (network/chain identifier). + #[serde_as(as = "EthHex")] + pub fork_version: Vec, + /// ConfigHash uniquely identifies a cluster definition excluding operator + /// ENRs and signatures. + #[serde_as(as = "EthHex")] + pub config_hash: Vec, + /// DefinitionHash uniquely identifies a cluster definition including + /// operator ENRs and signatures. + #[serde_as(as = "EthHex")] + pub definition_hash: Vec, +} + +impl TryFrom for DefinitionV1x4 { + type Error = DefinitionError; + + fn try_from(definition: Definition) -> Result { + let validator_addresses = definition.legacy_validator_addresses()?; + + Ok(Self { + name: definition.name, + creator: definition.creator, + operators: definition + .operators + .into_iter() + .map(OperatorV1X2OrLater::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + fee_recipient_address: validator_addresses.fee_recipient_address, + withdrawal_address: validator_addresses.withdrawal_address, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + }) + } +} + +impl TryFrom for Definition { + type Error = DefinitionError; + + fn try_from(definition: DefinitionV1x4) -> Result { + let validator_addresses = ValidatorAddresses { + fee_recipient_address: definition.fee_recipient_address, + withdrawal_address: definition.withdrawal_address, + }; + + let validator_addresses = + repeat_v_addresses(validator_addresses, definition.num_validators); + + Ok(Self { + name: definition.name, + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + operators: definition + .operators + .into_iter() + .map(Operator::from) + .collect(), + creator: definition.creator, + validator_addresses, + deposit_amounts: Vec::new(), + consensus_protocol: String::new(), + target_gas_limit: 0, + compounding: false, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + }) + } +} + +/// DefinitionV1x5 is a cluster definition for version 1.5.0-1.7.0 +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DefinitionV1x5to7 { + /// Name is a human-readable cosmetic identifier. Max 256 chars. + pub name: String, + /// Creator identifies the creator of a cluster definition. They may also be + /// an operator. + pub creator: Creator, + /// Operators define the charon nodes in the cluster and their operators. + /// Max 256 operators. + pub operators: Vec, + /// UUID is a human-readable random unique identifier. Max 64 chars. + pub uuid: Uuid, + /// Version is the schema version of this definition. Max 16 chars. + pub version: String, + /// Timestamp is the human-readable timestamp of this definition. Max 32 + /// chars. Note that this was added in v1.1.0, so may be empty for older + /// versions. + pub timestamp: DateTime, + /// NumValidators is the number of DVs to be created in the cluster lock + /// file. + pub num_validators: u64, + /// Threshold required for signature reconstruction. Defaults to safe value + /// for number of nodes/peers. + pub threshold: u64, + /// ValidatorAddresses define addresses of each validator. + #[serde(rename = "validators")] + pub validator_addresses: Vec, + /// DKGAlgorithm to use for key generation. Max 32 chars. + pub dkg_algorithm: String, + /// ForkVersion defines the cluster's 4 byte beacon chain fork version + /// (network/chain identifier). + #[serde_as(as = "EthHex")] + pub fork_version: Vec, + /// ConfigHash uniquely identifies a cluster definition excluding operator + /// ENRs and signatures. + #[serde_as(as = "EthHex")] + pub config_hash: Vec, + /// DefinitionHash uniquely identifies a cluster definition including + /// operator ENRs and signatures. + #[serde_as(as = "EthHex")] + pub definition_hash: Vec, +} + +impl From for DefinitionV1x5to7 { + fn from(definition: Definition) -> Self { + Self { + name: definition.name, + creator: definition.creator, + operators: definition + .operators + .into_iter() + .map(OperatorV1X2OrLater::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + validator_addresses: definition.validator_addresses, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +impl From for Definition { + fn from(definition: DefinitionV1x5to7) -> Self { + Self { + name: definition.name, + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + operators: definition + .operators + .into_iter() + .map(Operator::from) + .collect(), + creator: definition.creator, + validator_addresses: definition.validator_addresses, + deposit_amounts: Vec::new(), + consensus_protocol: String::new(), + target_gas_limit: 0, + compounding: false, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +/// DefinitionV1x8 is a cluster definition for version 1.8.0 +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DefinitionV1x8 { + /// Name is a human-readable cosmetic identifier. Max 256 chars. + pub name: String, + /// Creator identifies the creator of a cluster definition. They may also be + /// an operator. + pub creator: Creator, + /// Operators define the charon nodes in the cluster and their operators. + /// Max 256 operators. + pub operators: Vec, + /// UUID is a human-readable random unique identifier. Max 64 chars. + pub uuid: Uuid, + /// Version is the schema version of this definition. Max 16 chars. + pub version: String, + /// Timestamp is the human-readable timestamp of this definition. Max 32 + /// chars. Note that this was added in v1.1.0, so may be empty for older + /// versions. + pub timestamp: DateTime, + /// NumValidators is the number of DVs to be created in the cluster lock + /// file. + pub num_validators: u64, + /// Threshold required for signature reconstruction. Defaults to safe value + /// for number of nodes/peers. + pub threshold: u64, + /// ValidatorAddresses define addresses of each validator. + #[serde(rename = "validators")] + pub validator_addresses: Vec, + /// DKGAlgorithm to use for key generation. Max 32 chars. + pub dkg_algorithm: String, + /// ForkVersion defines the cluster's 4 byte beacon chain fork version + /// (network/chain identifier). + #[serde_as(as = "EthHex")] + pub fork_version: Vec, + /// DepositAmounts specifies partial deposit amounts that sum up to at least + /// 32ETH. + #[serde_as(as = "Vec>")] + pub deposit_amounts: Vec, + /// ConfigHash uniquely identifies a cluster definition excluding operator + /// ENRs and signatures. + #[serde_as(as = "EthHex")] + pub config_hash: Vec, + /// DefinitionHash uniquely identifies a cluster definition including + /// operator ENRs and signatures. + #[serde_as(as = "EthHex")] + pub definition_hash: Vec, +} + +impl From for DefinitionV1x8 { + fn from(definition: Definition) -> Self { + Self { + name: definition.name, + creator: definition.creator, + operators: definition + .operators + .into_iter() + .map(OperatorV1X2OrLater::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + validator_addresses: definition.validator_addresses, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + deposit_amounts: definition.deposit_amounts, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +impl From for Definition { + fn from(definition: DefinitionV1x8) -> Self { + Self { + name: definition.name, + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + operators: definition + .operators + .into_iter() + .map(Operator::from) + .collect(), + creator: definition.creator, + validator_addresses: definition.validator_addresses, + deposit_amounts: definition.deposit_amounts, + consensus_protocol: String::new(), + target_gas_limit: 0, + compounding: false, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +/// DefinitionV1x9 is a cluster definition for version 1.9.0 +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DefinitionV1x9 { + /// Name is a human-readable cosmetic identifier. Max 256 chars. + pub name: String, + /// Creator identifies the creator of a cluster definition. They may also be + /// an operator. + pub creator: Creator, + /// Operators define the charon nodes in the cluster and their operators. + /// Max 256 operators. + pub operators: Vec, + /// UUID is a human-readable random unique identifier. Max 64 chars. + pub uuid: Uuid, + /// Version is the schema version of this definition. Max 16 chars. + pub version: String, + /// Timestamp is the human-readable timestamp of this definition. Max 32 + /// chars. Note that this was added in v1.1.0, so may be empty for older + /// versions. + pub timestamp: DateTime, + /// NumValidators is the number of DVs to be created in the cluster lock + /// file. + pub num_validators: u64, + /// Threshold required for signature reconstruction. Defaults to safe value + /// for number of nodes/peers. + pub threshold: u64, + /// ValidatorAddresses define addresses of each validator. + #[serde(rename = "validators")] + pub validator_addresses: Vec, + /// DKGAlgorithm to use for key generation. Max 32 chars. + pub dkg_algorithm: String, + /// ForkVersion defines the cluster's 4 byte beacon chain fork version + /// (network/chain identifier). + #[serde_as(as = "EthHex")] + pub fork_version: Vec, + /// DepositAmounts specifies partial deposit amounts that sum up to at least + /// 32ETH. + #[serde_as(as = "Vec>")] + pub deposit_amounts: Vec, + /// ConsensusProtocol is the consensus protocol name preferred by the + /// cluster, e.g. "abft". + pub consensus_protocol: String, + /// ConfigHash uniquely identifies a cluster definition excluding operator + /// ENRs and signatures. + #[serde_as(as = "EthHex")] + pub config_hash: Vec, + /// DefinitionHash uniquely identifies a cluster definition including + /// operator ENRs and signatures. + #[serde_as(as = "EthHex")] + pub definition_hash: Vec, +} + +impl From for DefinitionV1x9 { + fn from(definition: Definition) -> Self { + Self { + name: definition.name, + creator: definition.creator, + operators: definition + .operators + .into_iter() + .map(OperatorV1X2OrLater::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + validator_addresses: definition.validator_addresses, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + deposit_amounts: definition.deposit_amounts, + consensus_protocol: definition.consensus_protocol, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +impl From for Definition { + fn from(definition: DefinitionV1x9) -> Self { + Self { + name: definition.name, + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + operators: definition + .operators + .into_iter() + .map(Operator::from) + .collect(), + creator: definition.creator, + validator_addresses: definition.validator_addresses, + deposit_amounts: definition.deposit_amounts, + consensus_protocol: definition.consensus_protocol, + target_gas_limit: 0, + compounding: false, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +/// DefinitionV1x10 is a cluster definition for version 1.10.0 +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DefinitionV1x10 { + /// Name is a human-readable cosmetic identifier. Max 256 chars. + pub name: String, + /// Creator identifies the creator of a cluster definition. They may also be + /// an operator. + pub creator: Creator, + /// Operators define the charon nodes in the cluster and their operators. + /// Max 256 operators. + pub operators: Vec, + /// UUID is a human-readable random unique identifier. Max 64 chars. + pub uuid: Uuid, + /// Version is the schema version of this definition. Max 16 chars. + pub version: String, + /// Timestamp is the human-readable timestamp of this definition. Max 32 + /// chars. Note that this was added in v1.1.0, so may be empty for older + /// versions. + pub timestamp: DateTime, + /// NumValidators is the number of DVs to be created in the cluster lock + /// file. + pub num_validators: u64, + /// Threshold required for signature reconstruction. Defaults to safe value + /// for number of nodes/peers. + pub threshold: u64, + /// ValidatorAddresses define addresses of each validator. + #[serde(rename = "validators")] + pub validator_addresses: Vec, + /// DKGAlgorithm to use for key generation. Max 32 chars. + pub dkg_algorithm: String, + /// ForkVersion defines the cluster's 4 byte beacon chain fork version + /// (network/chain identifier). + #[serde_as(as = "EthHex")] + pub fork_version: Vec, + /// DepositAmounts specifies partial deposit amounts that sum up to at least + /// 32ETH. + #[serde_as(as = "Vec>")] + pub deposit_amounts: Vec, + /// ConsensusProtocol is the consensus protocol name preferred by the + /// cluster, e.g. "abft". + pub consensus_protocol: String, + /// TargetGasLimit is the target block gas limit for the cluster. + pub target_gas_limit: u64, + /// Compounding flag enables compounding rewards for validators by using + /// 0x02 withdrawal credentials. + pub compounding: bool, + /// ConfigHash uniquely identifies a cluster definition excluding operator + /// ENRs and signatures. + #[serde_as(as = "EthHex")] + pub config_hash: Vec, + /// DefinitionHash uniquely identifies a cluster definition including + /// operator ENRs and signatures. + #[serde_as(as = "EthHex")] + pub definition_hash: Vec, +} + +impl From for DefinitionV1x10 { + fn from(definition: Definition) -> Self { + Self { + name: definition.name, + creator: definition.creator, + operators: definition + .operators + .into_iter() + .map(OperatorV1X2OrLater::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + validator_addresses: definition.validator_addresses, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + deposit_amounts: definition.deposit_amounts, + consensus_protocol: definition.consensus_protocol, + target_gas_limit: definition.target_gas_limit, + compounding: definition.compounding, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +impl From for Definition { + fn from(definition: DefinitionV1x10) -> Self { + Self { + name: definition.name, + creator: definition.creator, + operators: definition + .operators + .into_iter() + .map(Operator::from) + .collect(), + uuid: definition.uuid, + version: definition.version, + timestamp: definition.timestamp, + num_validators: definition.num_validators, + threshold: definition.threshold, + dkg_algorithm: definition.dkg_algorithm, + fork_version: definition.fork_version, + validator_addresses: definition.validator_addresses, + deposit_amounts: definition.deposit_amounts, + consensus_protocol: definition.consensus_protocol, + target_gas_limit: definition.target_gas_limit, + compounding: definition.compounding, + config_hash: definition.config_hash, + definition_hash: definition.definition_hash, + } + } +} + +fn repeat_v_addresses(addr: ValidatorAddresses, num_validators: u64) -> Vec { + let mut validator_addresses = Vec::new(); + for _ in 0..num_validators { + validator_addresses.push(addr.clone()); + } + validator_addresses +} + #[cfg(test)] mod tests { use super::*; #[test] - fn test_cluster_definition_v1_10_0() { + fn test_cluster_definition_v1_10_0_fields() { let definition = serde_json::from_str::(include_str!( "testdata/cluster_definition_v1_10_0.json" )) @@ -202,4 +1071,112 @@ mod tests { .unwrap() ); } + + #[test] + fn test_cluster_definition_v1_0_0() { + let json_str = include_str!("testdata/cluster_definition_v1_0_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_1_0() { + let json_str = include_str!("testdata/cluster_definition_v1_1_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_2_0() { + let json_str = include_str!("testdata/cluster_definition_v1_2_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_3_0() { + let json_str = include_str!("testdata/cluster_definition_v1_3_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_4_0() { + let json_str = include_str!("testdata/cluster_definition_v1_4_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_5_0() { + let json_str = include_str!("testdata/cluster_definition_v1_5_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_6_0() { + let json_str = include_str!("testdata/cluster_definition_v1_6_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_7_0() { + let json_str = include_str!("testdata/cluster_definition_v1_7_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_8_0() { + let json_str = include_str!("testdata/cluster_definition_v1_8_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_9_0() { + let json_str = include_str!("testdata/cluster_definition_v1_9_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + #[test] + fn test_cluster_definition_v1_10_0() { + let json_str = include_str!("testdata/cluster_definition_v1_10_0.json"); + + let _ = serde_json::from_str::(json_str).unwrap(); + + let _ = serde_json::from_str::(json_str).unwrap(); + } + + // test incorrect version + #[test] + fn test_cluster_definition_incorrect_version() { + let json_str = include_str!("testdata/cluster_definition_incorrect_version.json"); + + let result = serde_json::from_str::(json_str); + assert!(result.is_err()); + } } diff --git a/crates/charon-cluster/src/examples/cluster-definition-005.json b/crates/charon-cluster/src/examples/cluster-definition-005.json index 61d89bbd..34a472b9 100644 --- a/crates/charon-cluster/src/examples/cluster-definition-005.json +++ b/crates/charon-cluster/src/examples/cluster-definition-005.json @@ -32,7 +32,11 @@ ], "uuid": "B6AE17B3-78F5-147B-2C37-2572B93437DF", "version": "v1.8.0", +<<<<<<< HEAD + "timestamp": "2023-05-18T15: 12: 46+02: 00", +======= "timestamp": "2023-05-18T15:12:46+02:00", +>>>>>>> origin/main "num_validators": 2, "threshold": 3, "validators": [ diff --git a/crates/charon-cluster/src/operator.rs b/crates/charon-cluster/src/operator.rs index 6115f7a4..cac33743 100644 --- a/crates/charon-cluster/src/operator.rs +++ b/crates/charon-cluster/src/operator.rs @@ -21,6 +21,7 @@ pub struct Operator { /// operatorJSONv1x1 is the json formatter of Operator for versions v1.0.0 and /// v1.1.0. +#[serde_as] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub struct OperatorV1X1 { @@ -31,9 +32,11 @@ pub struct OperatorV1X1 { /// The nonce of the operator (always 0) nonce: u64, /// The config signature of the operator - config_signature: Vec, + #[serde_as(as = "EthHex")] + pub config_signature: Vec, /// The ENR signature of the operator - enr_signature: Vec, + #[serde_as(as = "EthHex")] + pub enr_signature: Vec, } /// OperatorV1X2OrLater is the json formatter of Operator for versions v1.2.0 diff --git a/crates/charon-cluster/src/testdata/cluster_definition_incorrect_version.json b/crates/charon-cluster/src/testdata/cluster_definition_incorrect_version.json new file mode 100644 index 00000000..e081491e --- /dev/null +++ b/crates/charon-cluster/src/testdata/cluster_definition_incorrect_version.json @@ -0,0 +1,47 @@ +{ + "name": "test definition", + "creator": { + "address": "0x6325253fec738dd7a9e28bf921119c160f070244", + "config_signature": "0x0bf5059875921e668a5bdf2c7fc4844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364cc3dbd968b0f7172ed85794bb358b0c3b525da1786f9f1c" + }, + "operators": [ + { + "address": "0x094279db1944ebd7a19d0f7bbacbe0255aa5b7d4", + "enr": "enr://b0223beea5f4f74391f445d15afd4294040374f6924b98cbf8713f8d962d7c8d", + "config_signature": "0x019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d929333ff993933bea6f5b3af6de0374366c4719e43a1b067d89bc7f01f1f573981659a44ff17a4c1c", + "enr_signature": "0x15a3b539eb1e5849c6077dbb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8fa7311e4d7defa922daae7786667f7e936cd4f24ab1c" + }, + { + "address": "0xdf866baa56038367ad6145de1ee8f4a8b0993ebd", + "enr": "enr://e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4d", + "config_signature": "0xa6430105220d0b29688b734b8ea0f3ca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979e4d60f26686d9bf2fb26c901ff354cde1607ee294b1b", + "enr_signature": "0xf32b7c7822ba64f84ab43ca0c6e6b91c1fd3be8990434179d3af4491a369012db92d184fc39d1734ff5716428953bb6865fcf92b0c3a17c9028be9914eb7649c1c" + } + ], + "uuid": "0194FDC2-FA2F-4CC0-81D3-FF12045B73C8", + "version": "INCORRECT_VERSION", + "timestamp": "2022-07-19T18:19:58+02:00", + "num_validators": 2, + "threshold": 3, + "validators": [ + { + "fee_recipient_address": "0x52fdfc072182654f163f5f0f9a621d729566c74d", + "withdrawal_address": "0x81855ad8681d0d86d1e91e00167939cb6694d2c4" + }, + { + "fee_recipient_address": "0xeb9d18a44784045d87f3c67cf22746e995af5a25", + "withdrawal_address": "0x5fb90badb37c5821b6d95526a41a9504680b4e7c" + } + ], + "dkg_algorithm": "default", + "fork_version": "0x90000069", + "deposit_amounts": [ + "16000000000", + "16000000000" + ], + "consensus_protocol": "abft", + "target_gas_limit": 30000000, + "compounding": false, + "config_hash": "0x19f6e5753f05c9b662b54959fbe5b0c265d6f571ea414310b84c5fe2e0851f61", + "definition_hash": "0x59a8d3ffa9010f54965a11248e2835e716049d508f4f64bf43bd5a6ca56037c0" +} \ No newline at end of file diff --git a/crates/charon-cluster/src/version.rs b/crates/charon-cluster/src/version.rs index 8f83dae4..3c37ecf9 100644 --- a/crates/charon-cluster/src/version.rs +++ b/crates/charon-cluster/src/version.rs @@ -2,28 +2,32 @@ // Source License 1.1 /// List of supported cluster definition versions. -/// Version v1.10.0 (Default) -pub const V1_10: &str = "v1.10.0"; // Default -/// Version v1.9.0 -pub const V1_9: &str = "v1.9.0"; -/// Version v1.8.0 -pub const V1_8: &str = "v1.8.0"; -/// Version v1.7.0 -pub const V1_7: &str = "v1.7.0"; -/// Version v1.6.0 -pub const V1_6: &str = "v1.6.0"; -/// Version v1.5.0 -pub const V1_5: &str = "v1.5.0"; -/// Version v1.4.0 -pub const V1_4: &str = "v1.4.0"; -/// Version v1.3.0 -pub const V1_3: &str = "v1.3.0"; -/// Version v1.2.0 -pub const V1_2: &str = "v1.2.0"; -/// Version v1.1.0 -pub const V1_1: &str = "v1.1.0"; -/// Version v1.0.0 -pub const V1_0: &str = "v1.0.0"; +pub mod versions { + /// Version v1.10.0 (Default) + pub const V1_10: &str = "v1.10.0"; // Default + /// Version v1.9.0 + pub const V1_9: &str = "v1.9.0"; + /// Version v1.8.0 + pub const V1_8: &str = "v1.8.0"; + /// Version v1.7.0 + pub const V1_7: &str = "v1.7.0"; + /// Version v1.6.0 + pub const V1_6: &str = "v1.6.0"; + /// Version v1.5.0 + pub const V1_5: &str = "v1.5.0"; + /// Version v1.4.0 + pub const V1_4: &str = "v1.4.0"; + /// Version v1.3.0 + pub const V1_3: &str = "v1.3.0"; + /// Version v1.2.0 + pub const V1_2: &str = "v1.2.0"; + /// Version v1.1.0 + pub const V1_1: &str = "v1.1.0"; + /// Version v1.0.0 + pub const V1_0: &str = "v1.0.0"; +} + +pub use versions::*; /// The current version of the charon cluster definition format. pub const CURRENT_VERSION: &str = V1_10;