diff --git a/Cargo.lock b/Cargo.lock index b4375ddac..907c336fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3164,6 +3164,28 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "google-cloud-auth" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357160f51a60ec3e32169ad687f4abe0ee1e90c73b449aa5d11256c4f1cf2ff6" +dependencies = [ + "async-trait", + "base64 0.21.7", + "google-cloud-metadata 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "google-cloud-token 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "home", + "jsonwebtoken", + "reqwest 0.12.7", + "serde", + "serde_json", + "thiserror", + "time", + "tokio", + "tracing", + "urlencoding", +] + [[package]] name = "google-cloud-gax" version = "0.18.0" @@ -3179,16 +3201,43 @@ dependencies = [ "tracing", ] +[[package]] +name = "google-cloud-gax" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c929076122a1839455cfe6c030278f10a400dd4dacc11d2ca46c20c47dc05996" +dependencies = [ + "google-cloud-token 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "http 1.1.0", + "thiserror", + "tokio", + "tokio-retry2", + "tonic 0.12.3", + "tower 0.4.13", + "tracing", +] + [[package]] name = "google-cloud-googleapis" version = "0.14.0" source = "git+https://github.com/yoshidan/google-cloud-rust.git?tag=v20240627#8f387a13309707c493d2e581961ee5b0f20631fc" dependencies = [ "prost 0.12.6", - "prost-types", + "prost-types 0.12.6", "tonic 0.11.0", ] +[[package]] +name = "google-cloud-googleapis" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae8ab26ef7c7c3f7dfb9cc3982293d031d8e78c85d00ddfb704b5c35aeff7c8" +dependencies = [ + "prost 0.13.3", + "prost-types 0.13.3", + "tonic 0.12.3", +] + [[package]] name = "google-cloud-kms" version = "0.3.0" @@ -3198,11 +3247,28 @@ dependencies = [ "ethers-core", "ethers-signers", "google-cloud-auth 0.16.0 (git+https://github.com/yoshidan/google-cloud-rust.git?tag=v20240627)", - "google-cloud-gax", - "google-cloud-googleapis", + "google-cloud-gax 0.18.0", + "google-cloud-googleapis 0.14.0", "google-cloud-token 0.1.2 (git+https://github.com/yoshidan/google-cloud-rust.git?tag=v20240627)", "k256 0.13.4", - "prost-types", + "prost-types 0.12.6", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "google-cloud-kms" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed3ae94026ce6ee13f22ad0005cf19ea0de543b78b06583f267e23cb7839d1b" +dependencies = [ + "google-cloud-auth 0.17.1", + "google-cloud-gax 0.19.1", + "google-cloud-googleapis 0.15.0", + "google-cloud-token 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "prost-types 0.13.3", "serde", "serde_json", "thiserror", @@ -3263,6 +3329,39 @@ dependencies = [ "url", ] +[[package]] +name = "google-cloud-storage" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7347a3d65cd64db51e5b4aebf0c68c484042948c6d53f856f58269bc9816360" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "base64 0.21.7", + "bytes", + "futures-util", + "google-cloud-auth 0.17.1", + "google-cloud-metadata 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "google-cloud-token 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hex", + "once_cell", + "percent-encoding", + "pkcs8 0.10.2", + "regex", + "reqwest 0.12.7", + "reqwest-middleware", + "ring", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", + "time", + "tokio", + "tracing", + "url", +] + [[package]] name = "google-cloud-token" version = "0.1.2" @@ -5856,7 +5955,7 @@ dependencies = [ "petgraph", "prettyplease", "prost 0.12.6", - "prost-types", + "prost-types 0.12.6", "regex", "syn 2.0.77", "tempfile", @@ -5899,7 +5998,7 @@ dependencies = [ "miette", "once_cell", "prost 0.12.6", - "prost-types", + "prost-types 0.12.6", "serde", "serde-value", ] @@ -5913,6 +6012,15 @@ dependencies = [ "prost 0.12.6", ] +[[package]] +name = "prost-types" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" +dependencies = [ + "prost 0.13.3", +] + [[package]] name = "protox" version = "0.5.1" @@ -5923,7 +6031,7 @@ dependencies = [ "miette", "prost 0.12.6", "prost-reflect", - "prost-types", + "prost-types 0.12.6", "protox-parse", "thiserror", ] @@ -5936,7 +6044,7 @@ checksum = "7b4581f441c58863525a3e6bec7b8de98188cf75239a56c725a3e7288450a33f" dependencies = [ "logos", "miette", - "prost-types", + "prost-types 0.12.6", "thiserror", ] @@ -8571,6 +8679,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-retry2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903934dba1c4c2f2e9cb460ef10b5695e0b0ecad3bf9ee7c8675e540c5e8b2d1" +dependencies = [ + "pin-project", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -8717,6 +8835,7 @@ dependencies = [ "axum 0.7.6", "base64 0.22.1", "bytes", + "flate2", "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", @@ -8727,13 +8846,16 @@ dependencies = [ "percent-encoding", "pin-project", "prost 0.13.3", + "rustls-pemfile 2.1.3", "socket2", "tokio", + "tokio-rustls 0.26.0", "tokio-stream", "tower 0.4.13", "tower-layer", "tower-service", "tracing", + "webpki-roots", ] [[package]] @@ -10599,7 +10721,12 @@ version = "0.1.0" dependencies = [ "anyhow", "envy", + "google-cloud-kms 0.5.1", + "google-cloud-storage 0.22.1", + "hex", + "rustls 0.23.13", "serde", + "tokio", "zksync_basic_types", "zksync_config", "zksync_system_constants", @@ -10662,8 +10789,8 @@ version = "0.1.0" dependencies = [ "async-trait", "ethers-signers", - "google-cloud-gax", - "google-cloud-kms", + "google-cloud-gax 0.18.0", + "google-cloud-kms 0.3.0", "hex", "rlp", "thiserror", @@ -11321,7 +11448,7 @@ dependencies = [ "bincode", "flate2", "google-cloud-auth 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "google-cloud-storage", + "google-cloud-storage 0.20.0", "http 1.1.0", "prost 0.12.6", "rand 0.8.5", diff --git a/core/lib/config/src/configs/da_client/avail.rs b/core/lib/config/src/configs/da_client/avail.rs index 590dc5fef..f75937735 100644 --- a/core/lib/config/src/configs/da_client/avail.rs +++ b/core/lib/config/src/configs/da_client/avail.rs @@ -13,4 +13,5 @@ pub struct AvailConfig { #[derive(Clone, Debug, PartialEq)] pub struct AvailSecrets { pub seed_phrase: Option, + pub private_key: Option, } diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 814901851..c472fc204 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -950,6 +950,7 @@ impl Distribution for EncodeDist { fn sample(&self, rng: &mut R) -> configs::secrets::DataAvailabilitySecrets { configs::secrets::DataAvailabilitySecrets::Avail(configs::da_client::avail::AvailSecrets { seed_phrase: Some(SeedPhrase(Secret::new(self.sample(rng)))), + private_key: None, }) } } diff --git a/core/lib/env_config/Cargo.toml b/core/lib/env_config/Cargo.toml index 31ffb8223..0e4554244 100644 --- a/core/lib/env_config/Cargo.toml +++ b/core/lib/env_config/Cargo.toml @@ -18,5 +18,11 @@ anyhow.workspace = true serde.workspace = true envy.workspace = true +google-cloud-kms = "0.5.1" +google-cloud-storage = "0.22.1" +hex = "0.4.3" +tokio = { workspace = true, features = ["rt"] } +rustls = "0.23" + [dev-dependencies] zksync_system_constants.workspace = true diff --git a/core/lib/env_config/src/da_client.rs b/core/lib/env_config/src/da_client.rs index 0fc3ad216..3b704450f 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -8,7 +8,7 @@ use zksync_config::configs::{ secrets::DataAvailabilitySecrets, }; -use crate::{envy_load, FromEnv}; +use crate::{envy_load, gcloud_encrypted_seed::retrieve_seed_from_gcloud, FromEnv}; impl FromEnv for DAClientConfig { fn from_env() -> anyhow::Result { @@ -30,11 +30,31 @@ impl FromEnv for DataAvailabilitySecrets { let client_tag = std::env::var("DA_CLIENT")?; let secrets = match client_tag.as_str() { AVAIL_CLIENT_CONFIG_NAME => { + let from_gcs = if let Some(secrets_from_gcs_tag) = env::var("DA_SECRETS_FROM_GCS").ok() { + secrets_from_gcs_tag == "true" + } else { + false + }; + + let _seed = match from_gcs { + true => { + let gcs_bucket_name = std::env::var("DA_SECRETS_GCS_BUCKET_NAME") + .ok() + .expect("Failed to get DA client secrets from GCS bucket"); + let decrypt_key_name = std::env::var("DA_SECRETS_KMS_DECRYPT_KEY_NAME") + .ok() + .expect("Failed to get DA client secrets KMS decrypt key"); + + Some(retrieve_seed_from_gcloud(decrypt_key_name, gcs_bucket_name)) + }, + false => None, + }; + let seed_phrase = env::var("DA_SECRETS_SEED_PHRASE") .ok() .map(|s| s.parse()) .transpose()?; - Self::Avail(AvailSecrets { seed_phrase }) + Self::Avail(AvailSecrets { seed_phrase: seed_phrase, private_key: _seed}) } _ => anyhow::bail!("Unknown DA client name: {}", client_tag), }; diff --git a/core/lib/env_config/src/gcloud_encrypted_seed.rs b/core/lib/env_config/src/gcloud_encrypted_seed.rs new file mode 100644 index 000000000..fc073e3cc --- /dev/null +++ b/core/lib/env_config/src/gcloud_encrypted_seed.rs @@ -0,0 +1,58 @@ +use google_cloud_kms::{ + client::{Client as kms_client, ClientConfig as kms_config}, + grpc::kms::v1::DecryptRequest, +}; +use google_cloud_storage::{ + client::{Client as storage_client, ClientConfig as storage_config}, + http::objects::{download::Range, get::GetObjectRequest}, +}; +use hex; +use tokio; + +pub fn retrieve_seed_from_gcloud(decrypt_key: String, bucket_name: String)-> String { + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + let rt = tokio::runtime::Runtime::new().unwrap(); + + // Download encryped seed for google cloud storage + let encrypted_seed_from_gcs = rt.block_on(download_seed_from_gcs(bucket_name)); + // Decrypt seed with kms key + let decrypted_seed = rt.block_on(decrypt_seed_with_kms(&encrypted_seed_from_gcs, decrypt_key)); + + decrypted_seed +} + +async fn decrypt_seed_with_kms(encrypted_seed: &[u8], decrypt_key: String) -> String { + let config = kms_config::default().with_auth().await.unwrap(); + let client = kms_client::new(config).await.unwrap(); + + let request = DecryptRequest { + name: decrypt_key, + ciphertext: encrypted_seed.to_vec(), + additional_authenticated_data: vec![], + ciphertext_crc32c: None, + additional_authenticated_data_crc32c: None, + }; + let decrypted_seed = client + .decrypt(request, None) + .await + .expect("Failed to decrypt seed"); + hex::encode(decrypted_seed.plaintext) +} +// Downloads the seed from the specified GCS bucket. +async fn download_seed_from_gcs(bucket_name: String) -> Vec { + let config = storage_config::default().with_auth().await.unwrap(); + let client = storage_client::new(config); + + // Download the file + client + .download_object( + &GetObjectRequest { + bucket: bucket_name, + object: "seed.bin".to_string(), + ..Default::default() + }, + &Range::default(), + ) + .await + .unwrap() +} \ No newline at end of file diff --git a/core/lib/env_config/src/lib.rs b/core/lib/env_config/src/lib.rs index 8f7a35d76..084355e46 100644 --- a/core/lib/env_config/src/lib.rs +++ b/core/lib/env_config/src/lib.rs @@ -34,6 +34,7 @@ mod vm_runner; mod wallets; mod da_client; +mod gcloud_encrypted_seed; pub trait FromEnv: Sized { fn from_env() -> anyhow::Result; diff --git a/core/lib/protobuf_config/src/secrets.rs b/core/lib/protobuf_config/src/secrets.rs index 587351480..4d57b6a82 100644 --- a/core/lib/protobuf_config/src/secrets.rs +++ b/core/lib/protobuf_config/src/secrets.rs @@ -110,6 +110,7 @@ impl ProtoRepr for proto::DataAvailabilitySecrets { ) .unwrap(), ), + private_key: None, }), }; @@ -133,7 +134,7 @@ impl ProtoRepr for proto::DataAvailabilitySecrets { None }; - Some(DaSecrets::Avail(AvailSecret { seed_phrase })) + Some(DaSecrets::Avail(AvailSecret { seed_phrase: seed_phrase }),) } }; diff --git a/core/node/da_clients/src/avail/client.rs b/core/node/da_clients/src/avail/client.rs index 7718691bf..d898a1b36 100644 --- a/core/node/da_clients/src/avail/client.rs +++ b/core/node/da_clients/src/avail/client.rs @@ -10,6 +10,7 @@ use zksync_da_client::{ }; use crate::avail::sdk::RawAvailClient; +use hex::FromHex; /// An implementation of the `DataAvailabilityClient` trait that interacts with the Avail network. #[derive(Debug, Clone)] @@ -20,10 +21,23 @@ pub struct AvailClient { impl AvailClient { pub async fn new(config: AvailConfig, secrets: AvailSecrets) -> anyhow::Result { - let seed_phrase = secrets + let sdk_client = if let Some(_pk) = &secrets.private_key { + let bytes = Vec::from_hex(_pk).map_err(|e| anyhow::anyhow!("hex string convert failed: {}", e.to_string()))?; + if bytes.len() != 32 { + return Err(anyhow::anyhow!("Hex string must represent exactly 32 bytes.")); + } + let mut array = [0u8; 32]; + array.copy_from_slice(&bytes); + + RawAvailClient::new_with_gcs_seed(config.app_id, array).await? + } else { + let seed_phrase = secrets .seed_phrase .ok_or_else(|| anyhow::anyhow!("seed phrase"))?; - let sdk_client = RawAvailClient::new(config.app_id, seed_phrase.0.expose_secret()).await?; + RawAvailClient::new(config.app_id, seed_phrase.0.expose_secret()).await? + }; + + tracing::info!("Created AvailClient!!!"); Ok(Self { config, diff --git a/core/node/da_clients/src/avail/sdk.rs b/core/node/da_clients/src/avail/sdk.rs index 002422109..fc92c6328 100644 --- a/core/node/da_clients/src/avail/sdk.rs +++ b/core/node/da_clients/src/avail/sdk.rs @@ -11,7 +11,7 @@ use parity_scale_codec::{Compact, Decode, Encode}; use scale_encode::EncodeAsFields; use subxt_signer::{ bip39::Mnemonic, - sr25519::{Keypair, Signature}, + sr25519::{Keypair, Seed, Signature}, }; use crate::avail::client::to_non_retriable_da_error; @@ -47,6 +47,11 @@ impl RawAvailClient { Ok(Self { app_id, keypair }) } + pub(crate) async fn new_with_gcs_seed(app_id: u32, seed: Seed) -> anyhow::Result { + let keypair = Keypair::from_seed(seed)?; + Ok(Self { app_id, keypair }) + } + /// Returns a hex-encoded extrinsic pub(crate) async fn build_extrinsic( &self,