Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Gnosis chain deposit support #27

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cli/existing_mnemonic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub fn subcommand<'a, 'b>() -> App<'a, 'b> {
.long("chain")
.required(true)
.takes_value(true)
.possible_values(&["goerli", "prater", "mainnet", "minimal"])
.possible_values(&crate::networks::SUPPORTED_NETWORKS)
.help(
r#"The name of Ethereum PoS chain you are
targeting. Use "mainnet" if you are
Expand Down
2 changes: 1 addition & 1 deletion src/cli/new_mnemonic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub fn subcommand<'a, 'b>() -> App<'a, 'b> {
.long("chain")
.required(true)
.takes_value(true)
.possible_values(&["goerli", "prater", "mainnet", "minimal"])
.possible_values(&crate::networks::SUPPORTED_NETWORKS)
.help(
r#"The name of Ethereum PoS chain you are
targeting. Use "mainnet" if you are
Expand Down
84 changes: 59 additions & 25 deletions src/deposit.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use eth2_keystore::keypair_from_secret;
use eth2_network_config::Eth2NetworkConfig;
use std::path::Path;
use types::{ChainSpec, Config, DepositData, Hash256, MainnetEthSpec, MinimalEthSpec, Signature};
use types::{ChainSpec, Config, DepositData, Hash256, MinimalEthSpec, Signature};

use crate::key_material::VotingKeyMaterial;
use crate::{key_material::VotingKeyMaterial, networks::NetworkSpec};

#[derive(Debug, Eq, PartialEq)]
pub enum DepositError {
Expand All @@ -23,7 +22,7 @@ pub(crate) fn keystore_to_deposit(
// withdrawal credentials
withdrawal_credentials: &[u8],
deposit_amount_gwei: u64,
network: String,
network: NetworkSpec,
chain_spec_file: Option<String>,
) -> Result<(DepositData, ChainSpec), DepositError> {
// Validate data input
Expand All @@ -41,22 +40,14 @@ pub(crate) fn keystore_to_deposit(
));
};

let network_str = network.as_str();
let spec;

if ["goerli", "prater", "mainnet"].contains(&network_str) {
spec = Eth2NetworkConfig::constant(network_str)
.unwrap()
.unwrap()
.chain_spec::<MainnetEthSpec>()
.unwrap();
} else if network_str == "minimal" {
let spec = if !network.is_public() {
// Loads custom configuration for private net
if chain_spec_file.is_none() {
return Err(DepositError::NoCustomConfig(
"Custom config for minimal network must be provided".to_string(),
));
}
spec = match Config::from_file(Path::new(chain_spec_file.unwrap().as_str())) {
match Config::from_file(Path::new(chain_spec_file.unwrap().as_str())) {
Ok(cfg) => cfg
.apply_to_chain_spec::<MinimalEthSpec>(&ChainSpec::minimal())
.unwrap(),
Expand All @@ -68,10 +59,8 @@ pub(crate) fn keystore_to_deposit(
}
}
} else {
return Err(DepositError::InvalidNetworkName(
"Unknown network name passed".to_string(),
));
}
network.into()
};

let credentials_hash = Hash256::from_slice(withdrawal_credentials);

Expand Down Expand Up @@ -105,7 +94,9 @@ mod test {
use types::PublicKey;

use super::keystore_to_deposit;
use crate::{key_material::VotingKeyMaterial, utils::get_withdrawal_credentials};
use crate::{
key_material::VotingKeyMaterial, networks::NetworkSpec, utils::get_withdrawal_credentials,
};
use std::{path::PathBuf, str::FromStr};
use test_log::test;

Expand All @@ -132,7 +123,7 @@ mod test {
&key_material,
withdrawal_creds.as_slice(),
32_000_000_000,
"mainnet".to_string(),
NetworkSpec::Mainnet,
None,
)
.unwrap();
Expand Down Expand Up @@ -172,7 +163,7 @@ mod test {
&key_material,
withdrawal_creds.as_slice(),
32_000_000_000,
"mainnet".to_string(),
NetworkSpec::Mainnet,
None,
)
.unwrap();
Expand Down Expand Up @@ -212,7 +203,7 @@ mod test {
&key_material,
&withdrawal_creds,
32_000_000_000,
"mainnet".to_string(),
NetworkSpec::Mainnet,
None,
)
.unwrap();
Expand Down Expand Up @@ -259,7 +250,7 @@ mod test {
&key_material,
&withdrawal_creds.as_slice(),
32_000_000_000,
"goerli".to_string(),
NetworkSpec::Goerli,
None,
)
.unwrap();
Expand Down Expand Up @@ -294,7 +285,7 @@ mod test {
&key_material,
&withdrawal_creds.as_slice(),
32_000_000_000,
"minimal".to_string(),
NetworkSpec::Minimal,
Some(manifest.to_str().unwrap().to_string()),
)
.unwrap();
Expand All @@ -311,4 +302,47 @@ mod test {
deposit_data.signature.to_string().as_str().strip_prefix("0x").unwrap()
);
}

#[test]
fn test_deposit_gnosis() {
let keystore = Keystore::from_json_str(KEYSTORE).unwrap();
let keypair = keystore.decrypt_keypair(PASSWORD).unwrap();
let key_material = VotingKeyMaterial {
keystore: Some(keystore.clone()),
keypair,
voting_secret: PlainText::from(
keystore
.decrypt_keypair(PASSWORD)
.unwrap()
.sk
.serialize()
.as_bytes()
.to_vec(),
),
withdrawal_keypair: None,
};
let withdrawal_creds = hex::decode(WITHDRAWAL_CREDENTIALS_ETH1).unwrap();
let (deposit_data, _) = keystore_to_deposit(
&key_material,
&withdrawal_creds.as_slice(),
32_000_000_000,
NetworkSpec::Gnosis,
None,
)
.unwrap();

// Signature asserted here is generated with
// https://github.com/gnosischain/validator-data-generator

// python ./staking_deposit/deposit.py existing-mnemonic --eth1_withdrawal_address=0x010000000000000000000000000000000000000001

// Please enter your mnemonic separated by spaces (" "): entire habit bottom mention spoil clown finger wheat motion fox axis mechanic country make garment bar blind stadium sugar water scissors canyon often ketchup
// Enter the index (key number) you wish to start generating more keys from. For example, if you've generated 4 keys in the past, you'd enter 4 here. [0]: 0
// Please choose how many new validators you wish to run: 1
// Please choose the (mainnet or testnet) network/chain name ['mainnet', 'ropsten', 'goerli', 'kiln', 'sepolia', 'gnosis', 'chiado']: [gnosis]: gnosis
assert_eq!(
"97cb6902975fc7cdcd685006ca972a22799aece787b9665af9e0b501e70a4db100cf280c99f1b20ea49c953b526b0e760b4a6d6a8d1092a6afbad3efdab8269384f2034c4fd97ebd8fc2101467b2fe6aeadcf5fcfaa11952be8d3939a55b10f7",
deposit_data.signature.to_string().as_str().strip_prefix("0x").unwrap()
);
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod bls_to_execution_change;
pub mod cli;
pub(crate) mod deposit;
pub(crate) mod key_material;
pub mod networks;
pub(crate) mod seed;
pub(crate) mod utils;
pub mod validators;
Expand Down
77 changes: 77 additions & 0 deletions src/networks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use eth2_network_config::Eth2NetworkConfig;
use types::{ChainSpec, GnosisEthSpec, MainnetEthSpec};

use crate::DepositError;

/// String representation for all supported networks
pub static SUPPORTED_NETWORKS: [&str; 5] = ["goerli", "prater", "mainnet", "gnosis", "minimal"];

#[derive(Clone)]
pub enum NetworkSpec {
Mainnet,
Goerli,
Gnosis,
Minimal,
}

/// Collects network spec from user or library inputs
impl TryFrom<String> for NetworkSpec {
type Error = DepositError;

fn try_from(value: String) -> Result<Self, Self::Error> {
let network = match value.as_str() {
"mainnet" => NetworkSpec::Mainnet,
"goerli" => NetworkSpec::Goerli,
"prater" => NetworkSpec::Goerli,
"gnosis" => NetworkSpec::Gnosis,
"minimal" => NetworkSpec::Minimal,
_ => {
log::info!("Invalid network name passed: {value}");
return Err(DepositError::InvalidNetworkName(
"Unknown network name passed".to_string(),
));
}
};
Ok(network)
}
}

/// Convert to a string that is recognizeable by Lighthouse's
/// Eth2NetworkConfig::constant
impl From<NetworkSpec> for &'static str {
fn from(value: NetworkSpec) -> &'static str {
match value {
NetworkSpec::Mainnet => "mainnet",
NetworkSpec::Goerli => "goerli",
NetworkSpec::Gnosis => "gnosis",
NetworkSpec::Minimal => "minimal",
}
}
}

/// Convert to Lighthouse internal spec repr.
/// Works only for public networks, private net needs
/// to load file.
impl From<NetworkSpec> for ChainSpec {
fn from(value: NetworkSpec) -> ChainSpec {
match value {
NetworkSpec::Gnosis => Eth2NetworkConfig::constant(value.into())
.unwrap()
.unwrap()
.chain_spec::<GnosisEthSpec>()
.unwrap(),
_ => Eth2NetworkConfig::constant(value.into())
.unwrap()
.unwrap()
.chain_spec::<MainnetEthSpec>()
.unwrap(),
}
}
}

impl NetworkSpec {
/// Find out if the config for public network, or private net one
pub fn is_public(&self) -> bool {
!matches!(self, NetworkSpec::Minimal)
}
}
7 changes: 5 additions & 2 deletions src/validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::str::FromStr;

use crate::deposit::{keystore_to_deposit, DepositError};
use crate::key_material::{seed_to_key_material, VotingKeyMaterial};
use crate::networks::NetworkSpec;
use crate::seed::get_eth2_seed;
use crate::utils::get_withdrawal_credentials;
use bip39::{Mnemonic, Seed as Bip39Seed};
Expand Down Expand Up @@ -243,6 +244,8 @@ impl Validators {
let mut private_keys: Vec<String> = vec![];
let mut deposit_data: Vec<DepositExport> = vec![];

let network_spec = NetworkSpec::try_from(network)?;

for key_with_store in self.key_material.iter() {
if let Some(ks) = key_with_store.keystore.clone() {
keystores.push(ks);
Expand All @@ -260,7 +263,7 @@ impl Validators {
&(*key_with_store).clone(),
withdrawal_credentials.as_ref(),
deposit_amount_gwei,
network.clone(),
network_spec.clone(),
chain_spec_file.clone(),
)?;

Expand All @@ -278,7 +281,7 @@ impl Validators {
deposit_message_root: hex::encode(deposit.as_deposit_message().tree_hash_root()),
deposit_data_root: hex::encode(deposit.tree_hash_root()),
fork_version: hex::encode(chain_spec.genesis_fork_version),
network_name: network.clone(),
network_name: Into::<&'static str>::into(network_spec.clone()).to_string(),
deposit_cli_version: deposit_cli_version.clone(),
})
}
Expand Down