Skip to content

Commit 4d53991

Browse files
committed
Merge branch 'next' into mmagician-claude/update-base-to-beta
2 parents 5320e57 + 84c853b commit 4d53991

File tree

13 files changed

+240
-176
lines changed

13 files changed

+240
-176
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
- [BREAKING] Renamed `NoteRoot` protobuf message used in `GetNoteScriptByRoot` gRPC endpoints into `NoteScriptRoot` ([#1722](https://github.com/0xMiden/node/pull/1722)).
3131
- [BREAKING] Modified `TransactionHeader` serialization to allow converting back into the native type after serialization ([#1759](https://github.com/0xMiden/node/issues/1759)).
3232
- Removed `chain_tip` requirement from mempool subscription request ([#1771](https://github.com/0xMiden/node/pull/1771)).
33+
- Moved bootstrap procedure to `miden-node validator bootstrap` command ([#1764](https://github.com/0xMiden/node/pull/1764)).
3334

3435
### Fixes
3536

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/node/src/commands/bundled.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,21 @@ impl BundledCommand {
9696
genesis_config_file,
9797
validator_key,
9898
} => {
99-
// Currently the bundled bootstrap is identical to the store's bootstrap.
100-
crate::commands::store::StoreCommand::Bootstrap {
101-
data_directory,
102-
accounts_directory,
103-
genesis_config_file,
99+
// Run validator bootstrap to create genesis block + account files.
100+
crate::commands::validator::ValidatorCommand::bootstrap_genesis(
101+
&data_directory,
102+
&accounts_directory,
103+
genesis_config_file.as_ref(),
104104
validator_key,
105-
}
106-
.handle()
105+
)
107106
.await
108-
.context("failed to bootstrap the store component")
107+
.context("failed to bootstrap genesis block")?;
108+
109+
// Feed the genesis block file into the store bootstrap.
110+
let genesis_block_path =
111+
data_directory.join(crate::commands::validator::GENESIS_BLOCK_FILENAME);
112+
crate::commands::store::bootstrap_store(&data_directory, &genesis_block_path)
113+
.context("failed to bootstrap the store component")
109114
},
110115
BundledCommand::Start {
111116
rpc_url,

bin/node/src/commands/store.rs

Lines changed: 24 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ use std::path::{Path, PathBuf};
22

33
use anyhow::Context;
44
use miden_node_store::Store;
5-
use miden_node_store::genesis::config::{AccountFileWithName, GenesisConfig};
5+
use miden_node_store::genesis::GenesisBlock;
66
use miden_node_utils::clap::GrpcOptionsInternal;
7+
use miden_node_utils::fs::ensure_empty_directory;
78
use miden_node_utils::grpc::UrlExt;
8-
use miden_node_utils::signer::BlockSigner;
9-
use miden_node_validator::ValidatorSigner;
9+
use miden_protocol::block::ProvenBlock;
10+
use miden_protocol::utils::serde::Deserializable;
1011
use url::Url;
1112

1213
use super::{
@@ -15,35 +16,21 @@ use super::{
1516
ENV_STORE_NTX_BUILDER_URL,
1617
ENV_STORE_RPC_URL,
1718
};
18-
use crate::commands::{
19-
ENV_BLOCK_PROVER_URL,
20-
ENV_ENABLE_OTEL,
21-
ENV_GENESIS_CONFIG_FILE,
22-
ValidatorKey,
23-
};
19+
use crate::commands::{ENV_BLOCK_PROVER_URL, ENV_ENABLE_OTEL};
2420

2521
#[expect(clippy::large_enum_variant, reason = "single use enum")]
2622
#[derive(clap::Subcommand)]
2723
pub enum StoreCommand {
28-
/// Bootstraps the blockchain database with the genesis block.
29-
///
30-
/// The genesis block contains a single public faucet account. The private key for this
31-
/// account is written to the `accounts-directory` which can be used to control the account.
24+
/// Bootstraps the blockchain database with a pre-existing genesis block.
3225
///
33-
/// This key is not required by the node and can be moved.
26+
/// The genesis block file should be produced by `miden-node validator bootstrap`.
3427
Bootstrap {
3528
/// Directory in which to store the database and raw block data.
3629
#[arg(long, env = ENV_DATA_DIRECTORY, value_name = "DIR")]
3730
data_directory: PathBuf,
38-
/// Directory to write the account data to.
39-
#[arg(long, value_name = "DIR")]
40-
accounts_directory: PathBuf,
41-
/// Use the given configuration file to construct the genesis state from.
42-
#[arg(long, env = ENV_GENESIS_CONFIG_FILE, value_name = "GENESIS_CONFIG")]
43-
genesis_config_file: Option<PathBuf>,
44-
/// Configuration for the Validator key used to sign genesis block.
45-
#[command(flatten)]
46-
validator_key: ValidatorKey,
31+
/// Path to the pre-signed genesis block file produced by the validator.
32+
#[arg(long, value_name = "FILE")]
33+
genesis_block: PathBuf,
4734
},
4835

4936
/// Starts the store component.
@@ -84,22 +71,12 @@ pub enum StoreCommand {
8471
}
8572

8673
impl StoreCommand {
87-
/// Executes the subcommand as described by each variants documentation.
74+
/// Executes the subcommand as described by each variant's documentation.
8875
pub async fn handle(self) -> anyhow::Result<()> {
8976
match self {
90-
StoreCommand::Bootstrap {
91-
data_directory,
92-
accounts_directory,
93-
genesis_config_file,
94-
validator_key,
95-
} => {
96-
Self::bootstrap(
97-
&data_directory,
98-
&accounts_directory,
99-
genesis_config_file.as_ref(),
100-
validator_key,
101-
)
102-
.await
77+
StoreCommand::Bootstrap { data_directory, genesis_block } => {
78+
ensure_empty_directory(&data_directory)?;
79+
bootstrap_store(&data_directory, &genesis_block)
10380
},
10481
StoreCommand::Start {
10582
rpc_url,
@@ -172,93 +149,16 @@ impl StoreCommand {
172149
.await
173150
.context("failed while serving store component")
174151
}
152+
}
175153

176-
async fn bootstrap(
177-
data_directory: &Path,
178-
accounts_directory: &Path,
179-
genesis_config: Option<&PathBuf>,
180-
validator_key: ValidatorKey,
181-
) -> anyhow::Result<()> {
182-
// Parse genesis config (or default if not given).
183-
let config = genesis_config
184-
.map(|file_path| {
185-
GenesisConfig::read_toml_file(file_path).with_context(|| {
186-
format!("failed to parse genesis config from file {}", file_path.display())
187-
})
188-
})
189-
.transpose()?
190-
.unwrap_or_default();
191-
192-
// Create directories if they do not already exist.
193-
for directory in &[accounts_directory, data_directory] {
194-
if fs_err::exists(directory)? {
195-
let is_empty = fs_err::read_dir(directory)?.next().is_none();
196-
// If the directory exists and is empty, we store the files there
197-
if !is_empty {
198-
anyhow::bail!(format!("{} exists but it is not empty.", directory.display()));
199-
}
200-
} else {
201-
fs_err::create_dir(directory).with_context(|| {
202-
format!(
203-
"failed to create {} at {}",
204-
directory
205-
.file_name()
206-
.unwrap_or(std::ffi::OsStr::new("directory"))
207-
.display(),
208-
directory.display()
209-
)
210-
})?;
211-
}
212-
}
213-
214-
// Bootstrap with KMS key or local key.
215-
let signer = validator_key.into_signer().await?;
216-
match signer {
217-
ValidatorSigner::Kms(signer) => {
218-
Self::bootstrap_accounts_and_store(
219-
config,
220-
signer,
221-
accounts_directory,
222-
data_directory,
223-
)
224-
.await
225-
},
226-
ValidatorSigner::Local(signer) => {
227-
Self::bootstrap_accounts_and_store(
228-
config,
229-
signer,
230-
accounts_directory,
231-
data_directory,
232-
)
233-
.await
234-
},
235-
}
236-
}
237-
238-
/// Builds the genesis state of the chain, writes accounts to file, and bootstraps the store.
239-
async fn bootstrap_accounts_and_store(
240-
config: GenesisConfig,
241-
signer: impl BlockSigner,
242-
accounts_directory: &Path,
243-
data_directory: &Path,
244-
) -> anyhow::Result<()> {
245-
// Build genesis state with the provided signer.
246-
let (genesis_state, secrets) = config.into_state(signer)?;
247-
248-
// Write accounts to file.
249-
for item in secrets.as_account_files(&genesis_state) {
250-
let AccountFileWithName { account_file, name } = item?;
251-
let accountpath = accounts_directory.join(name);
252-
// do not override existing keys
253-
fs_err::OpenOptions::new()
254-
.create_new(true)
255-
.write(true)
256-
.open(&accountpath)
257-
.context("key file already exists")?;
258-
account_file.write(accountpath)?;
259-
}
154+
/// Reads a genesis block from disk, validates it, and bootstraps the store.
155+
pub fn bootstrap_store(data_directory: &Path, genesis_block_path: &Path) -> anyhow::Result<()> {
156+
// Read and deserialize the genesis block file.
157+
let bytes = fs_err::read(genesis_block_path).context("failed to read genesis block")?;
158+
let proven_block = ProvenBlock::read_from_bytes(&bytes)
159+
.context("failed to deserialize genesis block from file")?;
160+
let genesis_block =
161+
GenesisBlock::try_from(proven_block).context("genesis block validation failed")?;
260162

261-
// Bootstrap store.
262-
Store::bootstrap(genesis_state, data_directory).await
263-
}
163+
Store::bootstrap(&genesis_block, data_directory)
264164
}

0 commit comments

Comments
 (0)