4d17fb3af6
## Issue Addressed Attempt to fix #3812 ## Proposed Changes Move web3signer binary download script out of build script to avoid downloading unless necessary. If this works, it should also reduce the build time for all jobs that runs compilation.
706 lines
26 KiB
Rust
706 lines
26 KiB
Rust
//! NOTE: These tests will fail without a java runtime environment (such as openjdk) installed and
|
|
//! available on `$PATH`.
|
|
//!
|
|
//! This crate provides a series of integration tests between the Lighthouse `ValidatorStore` and
|
|
//! Web3Signer by Consensys.
|
|
//!
|
|
//! These tests aim to ensure that:
|
|
//!
|
|
//! - Lighthouse can issue valid requests to Web3Signer.
|
|
//! - The signatures generated by Web3Signer are identical to those which Lighthouse generates.
|
|
//!
|
|
//! There is a `download_binary` function in the `get_web3signer` module which obtains the latest version of Web3Signer and makes
|
|
//! it available via the `TEMP_DIR`.
|
|
#![cfg(all(test, unix, not(debug_assertions)))]
|
|
|
|
mod get_web3signer;
|
|
|
|
mod tests {
|
|
use crate::get_web3signer::download_binary;
|
|
use account_utils::validator_definitions::{
|
|
SigningDefinition, ValidatorDefinition, ValidatorDefinitions, Web3SignerDefinition,
|
|
};
|
|
use eth2_keystore::KeystoreBuilder;
|
|
use eth2_network_config::Eth2NetworkConfig;
|
|
use lazy_static::lazy_static;
|
|
use parking_lot::Mutex;
|
|
use reqwest::Client;
|
|
use serde::Serialize;
|
|
use slot_clock::{SlotClock, TestingSlotClock};
|
|
use std::env;
|
|
use std::fmt::Debug;
|
|
use std::fs::{self, File};
|
|
use std::future::Future;
|
|
use std::path::PathBuf;
|
|
use std::process::{Child, Command, Stdio};
|
|
use std::sync::Arc;
|
|
use std::time::{Duration, Instant};
|
|
use task_executor::TaskExecutor;
|
|
use tempfile::{tempdir, TempDir};
|
|
use tokio::sync::OnceCell;
|
|
use tokio::time::sleep;
|
|
use types::*;
|
|
use url::Url;
|
|
use validator_client::{
|
|
initialized_validators::{
|
|
load_pem_certificate, load_pkcs12_identity, InitializedValidators,
|
|
},
|
|
validator_store::ValidatorStore,
|
|
SlashingDatabase, SLASHING_PROTECTION_FILENAME,
|
|
};
|
|
|
|
/// If the we are unable to reach the Web3Signer HTTP API within this time out then we will
|
|
/// assume it failed to start.
|
|
const UPCHECK_TIMEOUT: Duration = Duration::from_secs(20);
|
|
|
|
/// Set to `false` to send the Web3Signer logs to the console during tests. Logs are useful when
|
|
/// debugging.
|
|
const SUPPRESS_WEB3SIGNER_LOGS: bool = true;
|
|
|
|
lazy_static! {
|
|
static ref TEMP_DIR: Arc<Mutex<TempDir>> = Arc::new(Mutex::new(
|
|
tempdir().expect("Failed to create temporary directory")
|
|
));
|
|
static ref GET_WEB3SIGNER_BIN: OnceCell<()> = OnceCell::new();
|
|
}
|
|
|
|
type E = MainnetEthSpec;
|
|
|
|
/// This marker trait is implemented for objects that we wish to compare to ensure Web3Signer
|
|
/// and Lighthouse agree on signatures.
|
|
///
|
|
/// The purpose of this trait is to prevent accidentally comparing useless values like `()`.
|
|
trait SignedObject: PartialEq + Debug {}
|
|
|
|
impl SignedObject for Signature {}
|
|
impl SignedObject for Attestation<E> {}
|
|
impl SignedObject for SignedBeaconBlock<E> {}
|
|
impl SignedObject for SignedAggregateAndProof<E> {}
|
|
impl SignedObject for SelectionProof {}
|
|
impl SignedObject for SyncSelectionProof {}
|
|
impl SignedObject for SyncCommitteeMessage {}
|
|
impl SignedObject for SignedContributionAndProof<E> {}
|
|
impl SignedObject for SignedValidatorRegistrationData {}
|
|
|
|
/// A file format used by Web3Signer to discover and unlock keystores.
|
|
#[derive(Serialize)]
|
|
struct Web3SignerKeyConfig {
|
|
#[serde(rename = "type")]
|
|
config_type: String,
|
|
#[serde(rename = "keyType")]
|
|
key_type: String,
|
|
#[serde(rename = "keystoreFile")]
|
|
keystore_file: String,
|
|
#[serde(rename = "keystorePasswordFile")]
|
|
keystore_password_file: String,
|
|
}
|
|
|
|
const KEYSTORE_PASSWORD: &str = "hi mum";
|
|
const WEB3SIGNER_LISTEN_ADDRESS: &str = "127.0.0.1";
|
|
|
|
/// A deterministic, arbitrary keypair.
|
|
fn testing_keypair() -> Keypair {
|
|
// Just an arbitrary secret key.
|
|
let sk = SecretKey::deserialize(&[
|
|
85, 40, 245, 17, 84, 193, 234, 155, 24, 234, 181, 58, 171, 193, 209, 164, 120, 147, 10,
|
|
174, 189, 228, 119, 48, 181, 19, 117, 223, 2, 240, 7, 108,
|
|
])
|
|
.unwrap();
|
|
let pk = sk.public_key();
|
|
Keypair::from_components(pk, sk)
|
|
}
|
|
|
|
/// The location of the Web3Signer binary generated by the build script.
|
|
fn web3signer_binary() -> PathBuf {
|
|
TEMP_DIR
|
|
.lock()
|
|
.path()
|
|
.to_path_buf()
|
|
.join("web3signer")
|
|
.join("bin")
|
|
.join("web3signer")
|
|
}
|
|
|
|
/// The location of a directory where we keep some files for testing TLS.
|
|
fn tls_dir() -> PathBuf {
|
|
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("tls")
|
|
}
|
|
|
|
fn root_certificate_path() -> PathBuf {
|
|
tls_dir().join("lighthouse").join("web3signer.pem")
|
|
}
|
|
|
|
fn client_identity_path() -> PathBuf {
|
|
tls_dir().join("lighthouse").join("key.p12")
|
|
}
|
|
|
|
fn client_identity_password() -> String {
|
|
fs::read_to_string(tls_dir().join("lighthouse").join("password.txt"))
|
|
.unwrap()
|
|
.trim()
|
|
.to_string()
|
|
}
|
|
|
|
/// A testing rig which holds a live Web3Signer process.
|
|
struct Web3SignerRig {
|
|
keypair: Keypair,
|
|
_keystore_dir: TempDir,
|
|
keystore_path: PathBuf,
|
|
web3signer_child: Child,
|
|
http_client: Client,
|
|
url: Url,
|
|
}
|
|
|
|
impl Drop for Web3SignerRig {
|
|
fn drop(&mut self) {
|
|
self.web3signer_child.kill().unwrap();
|
|
}
|
|
}
|
|
|
|
impl Web3SignerRig {
|
|
pub async fn new(network: &str, listen_address: &str, listen_port: u16) -> Self {
|
|
GET_WEB3SIGNER_BIN
|
|
.get_or_init(|| async {
|
|
// Read a Github API token from the environment. This is intended to prevent rate-limits on CI.
|
|
// We use a name that is unlikely to accidentally collide with anything the user has configured.
|
|
let github_token = env::var("LIGHTHOUSE_GITHUB_TOKEN");
|
|
download_binary(
|
|
TEMP_DIR.lock().path().to_path_buf(),
|
|
github_token.as_deref().unwrap_or(""),
|
|
)
|
|
.await;
|
|
})
|
|
.await;
|
|
|
|
let keystore_dir = TempDir::new().unwrap();
|
|
let keypair = testing_keypair();
|
|
let keystore =
|
|
KeystoreBuilder::new(&keypair, KEYSTORE_PASSWORD.as_bytes(), "".to_string())
|
|
.unwrap()
|
|
.build()
|
|
.unwrap();
|
|
let keystore_filename = "keystore.json";
|
|
let keystore_path = keystore_dir.path().join(keystore_filename);
|
|
let keystore_file = File::create(&keystore_path).unwrap();
|
|
keystore.to_json_writer(&keystore_file).unwrap();
|
|
|
|
let keystore_password_filename = "password.txt";
|
|
let keystore_password_path = keystore_dir.path().join(keystore_password_filename);
|
|
fs::write(&keystore_password_path, KEYSTORE_PASSWORD.as_bytes()).unwrap();
|
|
|
|
let key_config = Web3SignerKeyConfig {
|
|
config_type: "file-keystore".to_string(),
|
|
key_type: "BLS".to_string(),
|
|
keystore_file: keystore_filename.to_string(),
|
|
keystore_password_file: keystore_password_filename.to_string(),
|
|
};
|
|
let key_config_file =
|
|
File::create(&keystore_dir.path().join("key-config.yaml")).unwrap();
|
|
serde_yaml::to_writer(key_config_file, &key_config).unwrap();
|
|
|
|
let tls_keystore_file = tls_dir().join("web3signer").join("key.p12");
|
|
let tls_keystore_password_file = tls_dir().join("web3signer").join("password.txt");
|
|
let tls_known_clients_file = tls_dir().join("web3signer").join("known_clients.txt");
|
|
|
|
let stdio = || {
|
|
if SUPPRESS_WEB3SIGNER_LOGS {
|
|
Stdio::null()
|
|
} else {
|
|
Stdio::inherit()
|
|
}
|
|
};
|
|
|
|
let web3signer_child = Command::new(web3signer_binary())
|
|
.arg(format!(
|
|
"--key-store-path={}",
|
|
keystore_dir.path().to_str().unwrap()
|
|
))
|
|
.arg(format!("--http-listen-host={}", listen_address))
|
|
.arg(format!("--http-listen-port={}", listen_port))
|
|
.arg(format!(
|
|
"--tls-known-clients-file={}",
|
|
tls_known_clients_file.to_str().unwrap()
|
|
))
|
|
.arg(format!(
|
|
"--tls-keystore-file={}",
|
|
tls_keystore_file.to_str().unwrap()
|
|
))
|
|
.arg(format!(
|
|
"--tls-keystore-password-file={}",
|
|
tls_keystore_password_file.to_str().unwrap()
|
|
))
|
|
.arg("eth2")
|
|
.arg(format!("--network={}", network))
|
|
.arg("--slashing-protection-enabled=false")
|
|
.stdout(stdio())
|
|
.stderr(stdio())
|
|
.spawn()
|
|
.unwrap();
|
|
|
|
let url = Url::parse(&format!("https://{}:{}", listen_address, listen_port)).unwrap();
|
|
|
|
let certificate = load_pem_certificate(root_certificate_path()).unwrap();
|
|
let identity =
|
|
load_pkcs12_identity(client_identity_path(), &client_identity_password()).unwrap();
|
|
let http_client = Client::builder()
|
|
.add_root_certificate(certificate)
|
|
.identity(identity)
|
|
.build()
|
|
.unwrap();
|
|
|
|
let s = Self {
|
|
keypair,
|
|
_keystore_dir: keystore_dir,
|
|
keystore_path,
|
|
web3signer_child,
|
|
http_client,
|
|
url,
|
|
};
|
|
|
|
s.wait_until_up(UPCHECK_TIMEOUT).await;
|
|
|
|
s
|
|
}
|
|
|
|
pub async fn wait_until_up(&self, timeout: Duration) {
|
|
let start = Instant::now();
|
|
loop {
|
|
if self.upcheck().await.is_ok() {
|
|
return;
|
|
} else if Instant::now().duration_since(start) > timeout {
|
|
panic!("upcheck failed with timeout {:?}", timeout)
|
|
} else {
|
|
sleep(Duration::from_secs(1)).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn upcheck(&self) -> Result<(), ()> {
|
|
let url = self.url.join("upcheck").unwrap();
|
|
self.http_client
|
|
.get(url)
|
|
.send()
|
|
.await
|
|
.map_err(|_| ())?
|
|
.error_for_status()
|
|
.map(|_| ())
|
|
.map_err(|_| ())
|
|
}
|
|
}
|
|
|
|
/// A testing rig which holds a `ValidatorStore`.
|
|
struct ValidatorStoreRig {
|
|
validator_store: Arc<ValidatorStore<TestingSlotClock, E>>,
|
|
_validator_dir: TempDir,
|
|
runtime: Arc<tokio::runtime::Runtime>,
|
|
_runtime_shutdown: exit_future::Signal,
|
|
}
|
|
|
|
impl ValidatorStoreRig {
|
|
pub async fn new(validator_definitions: Vec<ValidatorDefinition>, spec: ChainSpec) -> Self {
|
|
let log = environment::null_logger().unwrap();
|
|
let validator_dir = TempDir::new().unwrap();
|
|
|
|
let validator_definitions = ValidatorDefinitions::from(validator_definitions);
|
|
let initialized_validators = InitializedValidators::from_definitions(
|
|
validator_definitions,
|
|
validator_dir.path().into(),
|
|
log.clone(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let voting_pubkeys: Vec<_> = initialized_validators.iter_voting_pubkeys().collect();
|
|
|
|
let runtime = Arc::new(
|
|
tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap(),
|
|
);
|
|
let (runtime_shutdown, exit) = exit_future::signal();
|
|
let (shutdown_tx, _) = futures::channel::mpsc::channel(1);
|
|
let executor =
|
|
TaskExecutor::new(Arc::downgrade(&runtime), exit, log.clone(), shutdown_tx);
|
|
|
|
let slashing_db_path = validator_dir.path().join(SLASHING_PROTECTION_FILENAME);
|
|
let slashing_protection = SlashingDatabase::open_or_create(&slashing_db_path).unwrap();
|
|
slashing_protection
|
|
.register_validators(voting_pubkeys.iter().copied())
|
|
.unwrap();
|
|
|
|
let slot_clock =
|
|
TestingSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(1));
|
|
let config = validator_client::Config::default();
|
|
|
|
let validator_store = ValidatorStore::<_, E>::new(
|
|
initialized_validators,
|
|
slashing_protection,
|
|
Hash256::repeat_byte(42),
|
|
spec,
|
|
None,
|
|
slot_clock,
|
|
&config,
|
|
executor,
|
|
log.clone(),
|
|
);
|
|
|
|
Self {
|
|
validator_store: Arc::new(validator_store),
|
|
_validator_dir: validator_dir,
|
|
runtime,
|
|
_runtime_shutdown: runtime_shutdown,
|
|
}
|
|
}
|
|
|
|
pub fn shutdown(self) {
|
|
Arc::try_unwrap(self.runtime).unwrap().shutdown_background()
|
|
}
|
|
}
|
|
|
|
/// A testing rig which holds multiple `ValidatorStore` rigs and one `Web3Signer` rig.
|
|
///
|
|
/// The intent of this rig is to allow testing a `ValidatorStore` using `Web3Signer` against
|
|
/// another `ValidatorStore` using a local keystore and ensure that both `ValidatorStore`s
|
|
/// behave identically.
|
|
struct TestingRig {
|
|
_signer_rig: Web3SignerRig,
|
|
validator_rigs: Vec<ValidatorStoreRig>,
|
|
validator_pubkey: PublicKeyBytes,
|
|
}
|
|
|
|
impl Drop for TestingRig {
|
|
fn drop(&mut self) {
|
|
for rig in std::mem::take(&mut self.validator_rigs) {
|
|
rig.shutdown();
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TestingRig {
|
|
pub async fn new(network: &str, spec: ChainSpec, listen_port: u16) -> Self {
|
|
let signer_rig =
|
|
Web3SignerRig::new(network, WEB3SIGNER_LISTEN_ADDRESS, listen_port).await;
|
|
let validator_pubkey = signer_rig.keypair.pk.clone();
|
|
|
|
let local_signer_validator_store = {
|
|
let validator_definition = ValidatorDefinition {
|
|
enabled: true,
|
|
voting_public_key: validator_pubkey.clone(),
|
|
graffiti: None,
|
|
suggested_fee_recipient: None,
|
|
gas_limit: None,
|
|
builder_proposals: None,
|
|
description: String::default(),
|
|
signing_definition: SigningDefinition::LocalKeystore {
|
|
voting_keystore_path: signer_rig.keystore_path.clone(),
|
|
voting_keystore_password_path: None,
|
|
voting_keystore_password: Some(KEYSTORE_PASSWORD.to_string().into()),
|
|
},
|
|
};
|
|
ValidatorStoreRig::new(vec![validator_definition], spec.clone()).await
|
|
};
|
|
|
|
let remote_signer_validator_store = {
|
|
let validator_definition = ValidatorDefinition {
|
|
enabled: true,
|
|
voting_public_key: validator_pubkey.clone(),
|
|
graffiti: None,
|
|
suggested_fee_recipient: None,
|
|
gas_limit: None,
|
|
builder_proposals: None,
|
|
description: String::default(),
|
|
signing_definition: SigningDefinition::Web3Signer(Web3SignerDefinition {
|
|
url: signer_rig.url.to_string(),
|
|
root_certificate_path: Some(root_certificate_path()),
|
|
request_timeout_ms: None,
|
|
client_identity_path: Some(client_identity_path()),
|
|
client_identity_password: Some(client_identity_password()),
|
|
}),
|
|
};
|
|
ValidatorStoreRig::new(vec![validator_definition], spec).await
|
|
};
|
|
|
|
Self {
|
|
_signer_rig: signer_rig,
|
|
validator_rigs: vec![local_signer_validator_store, remote_signer_validator_store],
|
|
validator_pubkey: PublicKeyBytes::from(&validator_pubkey),
|
|
}
|
|
}
|
|
|
|
/// Run the `generate_sig` function across all validator stores on `self` and assert that
|
|
/// they all return the same value.
|
|
pub async fn assert_signatures_match<F, R, S>(
|
|
self,
|
|
case_name: &str,
|
|
generate_sig: F,
|
|
) -> Self
|
|
where
|
|
F: Fn(PublicKeyBytes, Arc<ValidatorStore<TestingSlotClock, E>>) -> R,
|
|
R: Future<Output = S>,
|
|
// We use the `SignedObject` trait to white-list objects for comparison. This avoids
|
|
// accidentally comparing something meaningless like a `()`.
|
|
S: SignedObject,
|
|
{
|
|
let mut prev_signature = None;
|
|
for (i, validator_rig) in self.validator_rigs.iter().enumerate() {
|
|
let signature =
|
|
generate_sig(self.validator_pubkey, validator_rig.validator_store.clone())
|
|
.await;
|
|
|
|
if let Some(prev_signature) = &prev_signature {
|
|
assert_eq!(
|
|
prev_signature, &signature,
|
|
"signature mismatch at index {} for case {}",
|
|
i, case_name
|
|
);
|
|
}
|
|
|
|
prev_signature = Some(signature)
|
|
}
|
|
assert!(prev_signature.is_some(), "sanity check");
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Get a generic, arbitrary attestation for signing.
|
|
fn get_attestation() -> Attestation<E> {
|
|
Attestation {
|
|
aggregation_bits: BitList::with_capacity(1).unwrap(),
|
|
data: AttestationData {
|
|
slot: <_>::default(),
|
|
index: <_>::default(),
|
|
beacon_block_root: <_>::default(),
|
|
source: Checkpoint {
|
|
epoch: <_>::default(),
|
|
root: <_>::default(),
|
|
},
|
|
target: Checkpoint {
|
|
epoch: <_>::default(),
|
|
root: <_>::default(),
|
|
},
|
|
},
|
|
signature: AggregateSignature::empty(),
|
|
}
|
|
}
|
|
|
|
fn get_validator_registration(pubkey: PublicKeyBytes) -> ValidatorRegistrationData {
|
|
let fee_recipient = Address::repeat_byte(42);
|
|
ValidatorRegistrationData {
|
|
fee_recipient,
|
|
gas_limit: 30_000_000,
|
|
timestamp: 100,
|
|
pubkey,
|
|
}
|
|
}
|
|
|
|
/// Test all the "base" (phase 0) types.
|
|
async fn test_base_types(network: &str, listen_port: u16) {
|
|
let network_config = Eth2NetworkConfig::constant(network).unwrap().unwrap();
|
|
let spec = &network_config.chain_spec::<E>().unwrap();
|
|
|
|
TestingRig::new(network, spec.clone(), listen_port)
|
|
.await
|
|
.assert_signatures_match("randao_reveal", |pubkey, validator_store| async move {
|
|
validator_store
|
|
.randao_reveal(pubkey, Epoch::new(0))
|
|
.await
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.assert_signatures_match("beacon_block_base", |pubkey, validator_store| async move {
|
|
let block = BeaconBlock::Base(BeaconBlockBase::empty(spec));
|
|
let block_slot = block.slot();
|
|
validator_store
|
|
.sign_block(pubkey, block, block_slot)
|
|
.await
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.assert_signatures_match("attestation", |pubkey, validator_store| async move {
|
|
let mut attestation = get_attestation();
|
|
validator_store
|
|
.sign_attestation(pubkey, 0, &mut attestation, Epoch::new(0))
|
|
.await
|
|
.unwrap();
|
|
attestation
|
|
})
|
|
.await
|
|
.assert_signatures_match("signed_aggregate", |pubkey, validator_store| async move {
|
|
let attestation = get_attestation();
|
|
validator_store
|
|
.produce_signed_aggregate_and_proof(
|
|
pubkey,
|
|
0,
|
|
attestation,
|
|
SelectionProof::from(Signature::empty()),
|
|
)
|
|
.await
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.assert_signatures_match("selection_proof", |pubkey, validator_store| async move {
|
|
validator_store
|
|
.produce_selection_proof(pubkey, Slot::new(0))
|
|
.await
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.assert_signatures_match(
|
|
"validator_registration",
|
|
|pubkey, validator_store| async move {
|
|
let val_reg_data = get_validator_registration(pubkey);
|
|
validator_store
|
|
.sign_validator_registration_data(val_reg_data)
|
|
.await
|
|
.unwrap()
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
/// Test all the Altair types.
|
|
async fn test_altair_types(network: &str, listen_port: u16) {
|
|
let network_config = Eth2NetworkConfig::constant(network).unwrap().unwrap();
|
|
let spec = &network_config.chain_spec::<E>().unwrap();
|
|
let altair_fork_slot = spec
|
|
.altair_fork_epoch
|
|
.unwrap()
|
|
.start_slot(E::slots_per_epoch());
|
|
|
|
TestingRig::new(network, spec.clone(), listen_port)
|
|
.await
|
|
.assert_signatures_match(
|
|
"beacon_block_altair",
|
|
|pubkey, validator_store| async move {
|
|
let mut altair_block = BeaconBlockAltair::empty(spec);
|
|
altair_block.slot = altair_fork_slot;
|
|
validator_store
|
|
.sign_block(pubkey, BeaconBlock::Altair(altair_block), altair_fork_slot)
|
|
.await
|
|
.unwrap()
|
|
},
|
|
)
|
|
.await
|
|
.assert_signatures_match(
|
|
"sync_selection_proof",
|
|
|pubkey, validator_store| async move {
|
|
validator_store
|
|
.produce_sync_selection_proof(
|
|
&pubkey,
|
|
altair_fork_slot,
|
|
SyncSubnetId::from(0),
|
|
)
|
|
.await
|
|
.unwrap()
|
|
},
|
|
)
|
|
.await
|
|
.assert_signatures_match(
|
|
"sync_committee_signature",
|
|
|pubkey, validator_store| async move {
|
|
validator_store
|
|
.produce_sync_committee_signature(
|
|
altair_fork_slot,
|
|
Hash256::zero(),
|
|
0,
|
|
&pubkey,
|
|
)
|
|
.await
|
|
.unwrap()
|
|
},
|
|
)
|
|
.await
|
|
.assert_signatures_match(
|
|
"signed_contribution_and_proof",
|
|
|pubkey, validator_store| async move {
|
|
let contribution = SyncCommitteeContribution {
|
|
slot: altair_fork_slot,
|
|
beacon_block_root: <_>::default(),
|
|
subcommittee_index: <_>::default(),
|
|
aggregation_bits: <_>::default(),
|
|
signature: AggregateSignature::empty(),
|
|
};
|
|
validator_store
|
|
.produce_signed_contribution_and_proof(
|
|
0,
|
|
pubkey,
|
|
contribution,
|
|
SyncSelectionProof::from(Signature::empty()),
|
|
)
|
|
.await
|
|
.unwrap()
|
|
},
|
|
)
|
|
.await
|
|
.assert_signatures_match(
|
|
"validator_registration",
|
|
|pubkey, validator_store| async move {
|
|
let val_reg_data = get_validator_registration(pubkey);
|
|
validator_store
|
|
.sign_validator_registration_data(val_reg_data)
|
|
.await
|
|
.unwrap()
|
|
},
|
|
)
|
|
.await;
|
|
}
|
|
|
|
/// Test all the Merge types.
|
|
async fn test_merge_types(network: &str, listen_port: u16) {
|
|
let network_config = Eth2NetworkConfig::constant(network).unwrap().unwrap();
|
|
let spec = &network_config.chain_spec::<E>().unwrap();
|
|
let merge_fork_slot = spec
|
|
.bellatrix_fork_epoch
|
|
.unwrap()
|
|
.start_slot(E::slots_per_epoch());
|
|
|
|
TestingRig::new(network, spec.clone(), listen_port)
|
|
.await
|
|
.assert_signatures_match("beacon_block_merge", |pubkey, validator_store| async move {
|
|
let mut merge_block = BeaconBlockMerge::empty(spec);
|
|
merge_block.slot = merge_fork_slot;
|
|
validator_store
|
|
.sign_block(pubkey, BeaconBlock::Merge(merge_block), merge_fork_slot)
|
|
.await
|
|
.unwrap()
|
|
})
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn mainnet_base_types() {
|
|
test_base_types("mainnet", 4242).await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn mainnet_altair_types() {
|
|
test_altair_types("mainnet", 4243).await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn prater_base_types() {
|
|
test_base_types("prater", 4246).await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn prater_altair_types() {
|
|
test_altair_types("prater", 4247).await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn sepolia_base_types() {
|
|
test_base_types("sepolia", 4250).await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn sepolia_altair_types() {
|
|
test_altair_types("sepolia", 4251).await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn sepolia_merge_types() {
|
|
test_merge_types("sepolia", 4252).await
|
|
}
|
|
}
|