Skip to content

Commit 26ed848

Browse files
authored
Merge pull request #1077 from openmina/feat/archive-redundancy
Archive redundancy with precomputed blocks storages
2 parents 27ba379 + 495bb0c commit 26ed848

File tree

31 files changed

+2269
-664
lines changed

31 files changed

+2269
-664
lines changed

Cargo.lock

Lines changed: 868 additions & 62 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/src/commands/node/mod.rs

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use node::p2p::identity::SecretKey;
1717
use node::service::Recorder;
1818
use node::SnarkerStrategy;
1919

20-
use openmina_node_native::{tracing, NodeBuilder};
20+
use openmina_node_native::{archive::config::ArchiveStorageOptions, tracing, NodeBuilder};
2121

2222
/// Openmina node
2323
#[derive(Debug, clap::Args)]
@@ -138,9 +138,39 @@ pub struct Node {
138138
#[arg(short = 'c', long, env)]
139139
pub config: Option<PathBuf>,
140140

141-
/// Enable archive mode (seding blocks to the archive process).
141+
/// Enable local precomputed storage.
142+
///
143+
/// This option requires the following environment variables to be set:
144+
/// - OPENMINA_ARCHIVE_LOCAL_STORAGE_PATH (otherwise the path to the working directory will be used)
145+
#[arg(long, env)]
146+
pub archive_local_storage: bool,
147+
148+
/// Enable archiver process.
149+
///
150+
/// This requires the following environment variables to be set:
151+
/// - OPENMINA_ARCHIVE_ADDRESS
152+
#[arg(long, env)]
153+
pub archive_archiver_process: bool,
154+
155+
/// Enable GCP precomputed storage.
156+
///
157+
/// This requires the following environment variables to be set:
158+
/// - GCP_CREDENTIALS_JSON
159+
/// - GCP_BUCKET_NAME
160+
///
142161
#[arg(long, env)]
143-
pub archive_address: Option<Url>,
162+
pub archive_gcp_storage: bool,
163+
164+
/// Enable AWS precomputed storage.
165+
///
166+
/// This requires the following environment variables to be set:
167+
/// - AWS_ACCESS_KEY_ID
168+
/// - AWS_SECRET_ACCESS_KEY
169+
/// - AWS_SESSION_TOKEN
170+
/// - AWS_DEFAULT_REGION
171+
/// - OPENMINA_AWS_BUCKET_NAME
172+
#[arg(long, env)]
173+
pub archive_aws_storage: bool,
144174
}
145175

146176
impl Node {
@@ -272,23 +302,44 @@ impl Node {
272302
}
273303
}
274304

275-
if let Some(address) = self.archive_address {
276-
openmina_core::IS_ARCHIVE
277-
.set(true)
278-
.expect("IS_ARCHIVE already set");
305+
let archive_storage_options = ArchiveStorageOptions::from_iter(
306+
[
307+
(
308+
self.archive_local_storage,
309+
ArchiveStorageOptions::LOCAL_PRECOMPUTED_STORAGE,
310+
),
311+
(
312+
self.archive_archiver_process,
313+
ArchiveStorageOptions::ARCHIVER_PROCESS,
314+
),
315+
(
316+
self.archive_gcp_storage,
317+
ArchiveStorageOptions::GCP_PRECOMPUTED_STORAGE,
318+
),
319+
(
320+
self.archive_aws_storage,
321+
ArchiveStorageOptions::AWS_PRECOMPUTED_STORAGE,
322+
),
323+
]
324+
.iter()
325+
.filter(|(enabled, _)| *enabled)
326+
.map(|(_, option)| option.clone()),
327+
);
328+
329+
if archive_storage_options.is_enabled() {
279330
node::core::info!(
280331
summary = "Archive mode enabled",
281-
address = address.to_string()
332+
local_storage = archive_storage_options.uses_local_precomputed_storage(),
333+
archiver_process = archive_storage_options.uses_archiver_process(),
334+
gcp_storage = archive_storage_options.uses_gcp_precomputed_storage(),
335+
aws_storage = archive_storage_options.uses_aws_precomputed_storage(),
282336
);
283-
// Convert URL to SocketAddr
284-
let socket_addrs = address.socket_addrs(|| None).expect("Invalid URL");
285337

286-
let socket_addr = socket_addrs.first().expect("No socket address found");
287-
node_builder.archive(*socket_addr);
288-
} else {
289-
openmina_core::IS_ARCHIVE
290-
.set(false)
291-
.expect("IS_ARCHIVE already set");
338+
archive_storage_options
339+
.validate_env_vars()
340+
.map_err(|e| anyhow::anyhow!(e))?;
341+
342+
node_builder.archive(archive_storage_options, work_dir.clone());
292343
}
293344

294345
if let Some(sec_key) = self.run_snarker {

core/src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ pub mod transaction;
2424
pub mod consensus;
2525

2626
mod substate;
27-
use std::sync::OnceLock;
2827

2928
pub use substate::{Substate, SubstateAccess, SubstateResult};
3029

@@ -37,9 +36,6 @@ pub use chain_id::*;
3736
pub mod encrypted_key;
3837
pub use encrypted_key::*;
3938

40-
// FIXME(#1043): refactor
41-
pub static IS_ARCHIVE: OnceLock<bool> = OnceLock::new();
42-
4339
mod work_dir {
4440
use once_cell::sync::OnceCell;
4541
use std::path::PathBuf;

docker-compose.archive.devnet.compare.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,11 @@ services:
137137
node-openmina:
138138
image: adrnagy/openmina:archive-test
139139
container_name: node-openmina
140+
environment:
141+
OPENMINA_ARCHIVE_ADDRESS: http://archive-openmina:3087
140142
command: >
141143
node
142-
--archive-address http://archive-openmina:3087
144+
--archive-archiver-process
143145
ports:
144146
- "3000:3000"
145147
depends_on:

docker-compose.archive.devnet.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,11 @@ services:
6363
node-openmina:
6464
image: openmina/openmina:latest
6565
container_name: node-openmina
66+
environment:
67+
- OPENMINA_ARCHIVE_ADDRESS=http://archive-openmina:3086
6668
command: >
6769
node
68-
--archive-address http://archive-openmina:3086
70+
--archive-archiver-process
6971
ports:
7072
- "127.0.0.1:3000:3000"
7173
depends_on:

docs/archive-node-guide.md

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,61 @@
22

33
This guide is intended for setting up archive nodes on **Mina Devnet** only. Do not use this guide for Mina Mainnet
44

5+
## Archive Mode Configuration
6+
7+
We start archive mode in openmina by setting one of the following flags along with their associated environment variables:
8+
9+
### Archiver Process (`--archive-archiver-process`)
10+
11+
Stores blocks in a database by receiving them directly from the openmina node
12+
13+
**Required Environment Variables**:
14+
- `OPENMINA_ARCHIVE_ADDRESS`: Network address for the archiver service
15+
16+
### Local Storage (`--archive-local-storage`)
17+
18+
Stores blocks in the local filesystem
19+
20+
**Required Environment Variables**:
21+
- (None)
22+
23+
**Optional Environment Variables**:
24+
- `OPENMINA_ARCHIVE_LOCAL_STORAGE_PATH`: Custom path for block storage (default: ~/.openmina/archive-precomputed)
25+
26+
### GCP Storage (`--archive-gcp-storage`)
27+
28+
Uploads blocks to a Google Cloud Platform bucket
29+
30+
**Required Environment Variables**:
31+
- `GCP_CREDENTIALS_JSON`: Service account credentials JSON
32+
- `GCP_BUCKET_NAME`: Target storage bucket name
33+
34+
### AWS Storage (`--archive-aws-storage`)
35+
36+
Uploads blocks to an AWS S3 bucket
37+
38+
**Required Environment Variables**:
39+
- `AWS_ACCESS_KEY_ID`: IAM user access key
40+
- `AWS_SECRET_ACCESS_KEY`: IAM user secret key
41+
- `AWS_DEFAULT_REGION`: AWS region name
42+
- `AWS_SESSION_TOKEN`: Temporary session token for temporary credentials
43+
- `OPENMINA_AWS_BUCKET_NAME`: Target S3 bucket name
44+
45+
## Redundancy
46+
47+
The archive mode is designed to be redundant. We can combine the flags to have multiple options running simultaneously.
48+
549
## Prerequisites
650

751
Ensure Docker and Docker Compose are installed on your system - [Docker Installation Guide](./docker-installation.md)
852

9-
## Docker compose setup
53+
## Docker compose setup (with archiver process)
54+
55+
The compose file sets up a PG database, the archiver process and the openmina node. The archiver process is responsible for storing the blocks in the database by receiving the blocks from the openmina node.
1056

11-
The compose file sets up a PG database, the archiver process and the openmina node. The archiver process is responsible for storing the blocks in the database by receiving the blocks from the openmina node. We start the archive mode in openmina by setting the `--archive-mode` flag to the address fo archiver process. See [docker-compose.archive.devnet.yml](../docker-compose.archive.devnet.yml) for more details.
57+
See [docker-compose.archive.devnet.yml](../docker-compose.archive.devnet.yml) for more details.
1258

13-
## Starting the setup
59+
### Starting the setup
1460

1561
```bash
1662
docker compose -f docker-compose.archive.devnet.yml up -d

ledger/src/base.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,12 @@ impl LedgerIntf for Mask {
300300
/// public API of this module.
301301
/// This should *NOT* be used to create a ledger for other purposes.
302302
fn create_masked(&self) -> Self {
303-
let mask = Mask::new_unattached(self.depth() as usize);
303+
let mut mask = Mask::new_unattached(self.depth() as usize);
304+
305+
if self.has_token_owners() {
306+
mask.set_token_owners();
307+
}
308+
304309
// We don't register the mask here. This is only used in transaction logic,
305310
// where we don't want to unregister. Transaction logic is also
306311
// synchronous, so we don't need to worry that our mask will be reparented.

ledger/src/database/database.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ impl Database<V2> {
6161
Self::create_with_dir(depth, None)
6262
}
6363

64+
pub fn create_with_token_owners(depth: u8) -> Self {
65+
let mut db = Self::create_with_dir(depth, None);
66+
db.set_token_owners();
67+
db
68+
}
69+
70+
pub fn set_token_owners(&mut self) {
71+
self.with(|this| this.set_token_owners());
72+
}
73+
74+
pub fn unset_token_owners(&mut self) {
75+
self.with(|this| this.unset_token_owners());
76+
}
77+
6478
pub fn root_hash(&mut self) -> Fp {
6579
self.with(|this| this.root_hash())
6680
}
@@ -105,6 +119,10 @@ impl Database<V2> {
105119
self.with(|this| this.transfert_hashes(hashes))
106120
}
107121

122+
pub fn has_token_owners(&self) -> bool {
123+
self.with(|this| this.has_token_owners())
124+
}
125+
108126
#[cfg(test)]
109127
pub fn test_matrix(&self) -> HashesMatrix {
110128
self.with(|this| this.hashes_matrix.clone())

ledger/src/database/database_impl.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use std::{
66

77
use mina_hasher::Fp;
88
use mina_signer::CompressedPubKey;
9-
use openmina_core::IS_ARCHIVE;
109

1110
use crate::{
1211
next_uuid, Account, AccountId, AccountIndex, AccountLegacy, Address, AddressIterator,
@@ -279,6 +278,10 @@ impl DatabaseImpl<V2> {
279278
pub fn transfert_hashes(&mut self, hashes: HashesMatrix) {
280279
self.hashes_matrix.transfert_hashes(hashes)
281280
}
281+
282+
pub fn has_token_owners(&self) -> bool {
283+
self.token_owners.is_some()
284+
}
282285
}
283286

284287
impl DatabaseImpl<V1> {
@@ -311,11 +314,12 @@ impl DatabaseImpl<V1> {
311314
}
312315

313316
impl DatabaseImpl<V2> {
317+
const NACCOUNTS: usize = 10_000;
318+
const NTOKENS: usize = 10;
319+
314320
pub fn create_with_dir(depth: u8, dir_name: Option<PathBuf>) -> Self {
315321
assert!((1..0xfe).contains(&depth));
316322

317-
const NACCOUNTS: usize = 10_000;
318-
319323
let uuid = next_uuid();
320324

321325
let path = match dir_name {
@@ -339,19 +343,13 @@ impl DatabaseImpl<V2> {
339343

340344
// std::fs::create_dir_all(&path).ok();
341345

342-
let token_owners = if IS_ARCHIVE.get().cloned().unwrap_or_default() {
343-
Some(HashMap::with_capacity(NACCOUNTS))
344-
} else {
345-
None
346-
};
347-
348346
Self {
349347
depth,
350-
accounts: Vec::with_capacity(NACCOUNTS),
348+
accounts: Vec::with_capacity(Self::NACCOUNTS),
351349
last_location: None,
352350
naccounts: 0,
353-
id_to_addr: HashMap::with_capacity(NACCOUNTS),
354-
token_owners,
351+
id_to_addr: HashMap::with_capacity(Self::NACCOUNTS),
352+
token_owners: None,
355353
uuid,
356354
directory: path,
357355
hashes_matrix: HashesMatrix::new(depth as usize),
@@ -363,6 +361,22 @@ impl DatabaseImpl<V2> {
363361
Self::create_with_dir(depth, None)
364362
}
365363

364+
pub fn create_with_token_owners(depth: u8) -> Self {
365+
let mut db = Self::create_with_dir(depth, None);
366+
db.set_token_owners();
367+
db
368+
}
369+
370+
pub fn set_token_owners(&mut self) {
371+
if self.token_owners.is_none() {
372+
self.token_owners = Some(HashMap::with_capacity(Self::NTOKENS));
373+
}
374+
}
375+
376+
pub fn unset_token_owners(&mut self) {
377+
self.token_owners = None;
378+
}
379+
366380
pub fn root_hash(&mut self) -> Fp {
367381
self.emulate_tree_to_get_hash_at(Address::root())
368382
}

0 commit comments

Comments
 (0)