Skip to content

Commit 572066c

Browse files
author
Adrian Nagy
committed
feat: GCP support
1 parent e67b9c9 commit 572066c

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

cli/src/commands/node/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ pub struct Node {
156156
pub archive_gcp_storage: bool,
157157

158158
/// Enable AWS precomputed storage.
159-
///
159+
///
160160
/// This requires the following environment variables to be set:
161161
/// - AWS_ACCESS_KEY_ID
162162
/// - AWS_SECRET_ACCESS_KEY

node/common/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ mio = { version = "1.0.2", features = ["os-poll", "net"] }
4444
reqwest = { version = "0.12.8", features = ["blocking", "json"] }
4545
aws-config = { version = "1.1.7", features = ["behavior-version-latest"] }
4646
aws-sdk-s3 = "1.73.0"
47+
google-cloud-storage = "0.24.0"
48+
google-cloud-auth = "0.17.2"
49+
4750

4851
[features]
4952
p2p-webrtc = ["node/p2p-webrtc"]

node/common/src/service/archive.rs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use bitflags::bitflags;
2+
use gcs::http::objects::upload as gcs_upload;
3+
use google_cloud_auth::credentials::CredentialsFile as GcpCredentialsFile;
4+
use google_cloud_storage as gcs;
25
use mina_p2p_messages::v2::{self};
36
use node::core::{channels::mpsc, thread};
47
use node::ledger::write::BlockApplyResult;
@@ -84,7 +87,21 @@ impl ArchiveStorageOptions {
8487
// }
8588
}
8689

87-
// TODO(adonagy): Add GCP precomputed storage validation
90+
if self.uses_gcp_precomputed_storage() {
91+
if env::var("GCP_CREDENTIALS_JSON").is_err() {
92+
return Err(
93+
"GCP_CREDENTIALS_JSON is required when GCP_PRECOMPUTED_STORAGE is enabled"
94+
.to_string(),
95+
);
96+
}
97+
98+
if env::var("GCP_BUCKET_NAME").is_err() {
99+
return Err(
100+
"GCP_BUCKET_NAME is required when GCP_PRECOMPUTED_STORAGE is enabled"
101+
.to_string(),
102+
);
103+
}
104+
}
88105

89106
Ok(())
90107
}
@@ -112,7 +129,7 @@ pub struct ArchiveService {
112129

113130
struct ArchiveServiceClients {
114131
aws_client: Option<ArchiveAWSClient>,
115-
gcp_client: Option<()>,
132+
gcp_client: Option<ArchiveGCPClient>,
116133
local_path: Option<String>,
117134
}
118135

@@ -122,6 +139,11 @@ struct ArchiveAWSClient {
122139
bucket_path: String,
123140
}
124141

142+
struct ArchiveGCPClient {
143+
client: gcs::client::Client,
144+
bucket_name: String,
145+
}
146+
125147
impl ArchiveServiceClients {
126148
async fn new(options: &ArchiveStorageOptions, work_dir: String) -> Self {
127149
let aws_client = if options.uses_aws_precomputed_storage() {
@@ -137,6 +159,23 @@ impl ArchiveServiceClients {
137159
None
138160
};
139161

162+
let gcp_client = if options.uses_gcp_precomputed_storage() {
163+
let cred_file = env::var("GCP_CREDENTIALS_JSON").unwrap_or_default();
164+
let bucket_name = env::var("GCP_BUCKET_NAME").unwrap_or_default();
165+
let credentials = GcpCredentialsFile::new_from_file(cred_file).await.unwrap();
166+
let config = gcs::client::ClientConfig::default()
167+
.with_credentials(credentials)
168+
.await
169+
.unwrap();
170+
let client = gcs::client::Client::new(config);
171+
Some(ArchiveGCPClient {
172+
client,
173+
bucket_name,
174+
})
175+
} else {
176+
None
177+
};
178+
140179
let local_path = if options.uses_local_precomputed_storage() {
141180
let env_path = env::var("OPENMINA_LOCAL_PRECOMPUTED_STORAGE_PATH");
142181
let default = format!("{}/archive-precomputed", work_dir);
@@ -147,7 +186,7 @@ impl ArchiveServiceClients {
147186

148187
Self {
149188
aws_client,
150-
gcp_client: None,
189+
gcp_client,
151190
local_path,
152191
}
153192
}
@@ -156,7 +195,7 @@ impl ArchiveServiceClients {
156195
self.aws_client.as_ref()
157196
}
158197

159-
pub fn gcp_client(&self) -> Option<&()> {
198+
pub fn gcp_client(&self) -> Option<&ArchiveGCPClient> {
160199
self.gcp_client.as_ref()
161200
}
162201

@@ -268,7 +307,34 @@ impl ArchiveService {
268307
}
269308

270309
if options.uses_gcp_precomputed_storage() {
271-
// TODO(adonagy): Implement GCP precomputed storage
310+
if let Some(client) = clients.gcp_client() {
311+
// TODO(adonagy): Serialize just once?
312+
let json = serde_json::to_string(&precomputed_block).unwrap();
313+
let upload_type =
314+
gcs_upload::UploadType::Simple(gcs_upload::Media::new(key.clone()));
315+
let uploaded = client
316+
.client
317+
.upload_object(
318+
&gcs_upload::UploadObjectRequest {
319+
bucket: client.bucket_name.clone(),
320+
..Default::default()
321+
},
322+
json.as_bytes().to_vec(),
323+
&upload_type,
324+
)
325+
.await;
326+
327+
if let Err(e) = uploaded {
328+
node::core::warn!(
329+
summary = "Failed to upload precomputed block to GCP",
330+
error = e.to_string()
331+
);
332+
} else {
333+
node::core::warn!(
334+
summary = "Successfully uploaded precomputed block to GCP"
335+
);
336+
}
337+
}
272338
}
273339

274340
if options.uses_aws_precomputed_storage() {

0 commit comments

Comments
 (0)