Skip to content

Commit 9bed52e

Browse files
authored
feat: Validator database (#1614)
1 parent 2340d92 commit 9bed52e

28 files changed

Lines changed: 497 additions & 204 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- [BREAKING] Move block proving from Blocker Producer to the Store ([#1579](https://github.com/0xMiden/miden-node/pull/1579)).
88
- [BREAKING] Updated miden-base dependencies to use `next` branch; renamed `NoteInputs` to `NoteStorage`, `.inputs()` to `.storage()`, and database `inputs` column to `storage` ([#1595](https://github.com/0xMiden/miden-node/pull/1595)).
9+
- Validator now persists validated transactions ([#1614](https://github.com/0xMiden/miden-node/pull/1614)).
910
- [BREAKING] Remove `SynState` and introduce `SyncChainMmr` ([#1591](https://github.com/0xMiden/miden-node/issues/1591)).
1011
- Introduce `SyncChainMmr` RPC endpoint to sync chain MMR deltas within specified block ranges ([#1591](https://github.com/0xMiden/miden-node/issues/1591)).
1112

Cargo.lock

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

bin/node/.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ MIDEN_NODE_STORE_RPC_URL=
1010
MIDEN_NODE_STORE_NTX_BUILDER_URL=
1111
MIDEN_NODE_STORE_BLOCK_PRODUCER_URL=
1212
MIDEN_NODE_VALIDATOR_BLOCK_PRODUCER_URL=
13-
MIDEN_NODE_VALIDATOR_INSECURE_SECRET_KEY=
13+
MIDEN_NODE_VALIDATOR_KEY=
1414
MIDEN_NODE_RPC_URL=http://0.0.0.0:57291
1515
MIDEN_NODE_DATA_DIRECTORY=./
1616
MIDEN_NODE_ENABLE_OTEL=true

bin/node/src/commands/bundled.rs

Lines changed: 94 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use miden_node_rpc::Rpc;
88
use miden_node_store::Store;
99
use miden_node_utils::grpc::UrlExt;
1010
use miden_node_validator::Validator;
11-
use miden_protocol::block::BlockSigner;
1211
use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey;
1312
use miden_protocol::utils::Deserializable;
1413
use tokio::net::TcpListener;
@@ -22,9 +21,10 @@ use crate::commands::{
2221
ENV_BLOCK_PROVER_URL,
2322
ENV_ENABLE_OTEL,
2423
ENV_GENESIS_CONFIG_FILE,
25-
ENV_VALIDATOR_INSECURE_SECRET_KEY,
24+
ENV_VALIDATOR_KEY,
2625
INSECURE_VALIDATOR_KEY_HEX,
2726
NtxBuilderConfig,
27+
ValidatorConfig,
2828
duration_to_human_readable_string,
2929
};
3030

@@ -51,12 +51,12 @@ pub enum BundledCommand {
5151
///
5252
/// If not provided, a predefined key is used.
5353
#[arg(
54-
long = "validator.insecure.secret-key",
55-
env = ENV_VALIDATOR_INSECURE_SECRET_KEY,
56-
value_name = "VALIDATOR_INSECURE_SECRET_KEY",
54+
long = "validator.key",
55+
env = ENV_VALIDATOR_KEY,
56+
value_name = "VALIDATOR_KEY",
5757
default_value = INSECURE_VALIDATOR_KEY_HEX
5858
)]
59-
validator_insecure_secret_key: String,
59+
validator_key: String,
6060
},
6161

6262
/// Runs all three node components in the same process.
@@ -82,6 +82,9 @@ pub enum BundledCommand {
8282
#[command(flatten)]
8383
ntx_builder: NtxBuilderConfig,
8484

85+
#[command(flatten)]
86+
validator: ValidatorConfig,
87+
8588
/// Enables the exporting of traces for OpenTelemetry.
8689
///
8790
/// This can be further configured using environment variables as defined in the official
@@ -99,15 +102,6 @@ pub enum BundledCommand {
99102
value_name = "DURATION"
100103
)]
101104
grpc_timeout: Duration,
102-
103-
/// Insecure, hex-encoded validator secret key for development and testing purposes.
104-
#[arg(
105-
long = "validator.insecure.secret-key",
106-
env = ENV_VALIDATOR_INSECURE_SECRET_KEY,
107-
value_name = "VALIDATOR_INSECURE_SECRET_KEY",
108-
default_value = INSECURE_VALIDATOR_KEY_HEX
109-
)]
110-
validator_insecure_secret_key: String,
111105
},
112106
}
113107

@@ -118,14 +112,14 @@ impl BundledCommand {
118112
data_directory,
119113
accounts_directory,
120114
genesis_config_file,
121-
validator_insecure_secret_key,
115+
validator_key,
122116
} => {
123117
// Currently the bundled bootstrap is identical to the store's bootstrap.
124118
crate::commands::store::StoreCommand::Bootstrap {
125119
data_directory,
126120
accounts_directory,
127121
genesis_config_file,
128-
validator_insecure_secret_key,
122+
validator_key,
129123
}
130124
.handle()
131125
.await
@@ -137,20 +131,18 @@ impl BundledCommand {
137131
data_directory,
138132
block_producer,
139133
ntx_builder,
134+
validator,
140135
enable_otel: _,
141136
grpc_timeout,
142-
validator_insecure_secret_key,
143137
} => {
144-
let secret_key_bytes = hex::decode(validator_insecure_secret_key)?;
145-
let signer = SecretKey::read_from_bytes(&secret_key_bytes)?;
146138
Self::start(
147139
rpc_url,
148140
block_prover_url,
149141
data_directory,
150-
ntx_builder,
151142
block_producer,
143+
ntx_builder,
144+
validator,
152145
grpc_timeout,
153-
signer,
154146
)
155147
.await
156148
},
@@ -162,10 +154,10 @@ impl BundledCommand {
162154
rpc_url: Url,
163155
block_prover_url: Option<Url>,
164156
data_directory: PathBuf,
165-
ntx_builder: NtxBuilderConfig,
166157
block_producer: BlockProducerConfig,
158+
ntx_builder: NtxBuilderConfig,
159+
validator: ValidatorConfig,
167160
grpc_timeout: Duration,
168-
signer: impl BlockSigner + Send + Sync + 'static,
169161
) -> anyhow::Result<()> {
170162
// Start listening on all gRPC urls so that inter-component connections can be created
171163
// before each component is fully started up.
@@ -177,17 +169,19 @@ impl BundledCommand {
177169
.await
178170
.context("Failed to bind to RPC gRPC endpoint")?;
179171

180-
let block_producer_address = TcpListener::bind("127.0.0.1:0")
181-
.await
182-
.context("Failed to bind to block-producer gRPC endpoint")?
183-
.local_addr()
184-
.context("Failed to retrieve the block-producer's gRPC address")?;
172+
let (block_producer_url, block_producer_address) = {
173+
let socket_addr = TcpListener::bind("127.0.0.1:0")
174+
.await
175+
.context("Failed to bind to block-producer gRPC endpoint")?
176+
.local_addr()
177+
.context("Failed to retrieve the block-producer's gRPC address")?;
178+
let url = Url::parse(&format!("http://{socket_addr}"))
179+
.context("Failed to parse Block Producer URL")?;
180+
(url, socket_addr)
181+
};
185182

186-
let validator_address = TcpListener::bind("127.0.0.1:0")
187-
.await
188-
.context("Failed to bind to validator gRPC endpoint")?
189-
.local_addr()
190-
.context("Failed to retrieve the validator's gRPC address")?;
183+
// Validator URL is either specified remote, or generated local.
184+
let (validator_url, validator_socket_address) = validator.to_addresses().await?;
191185

192186
// Store addresses for each exposed API
193187
let store_rpc_listener = TcpListener::bind("127.0.0.1:0")
@@ -231,85 +225,68 @@ impl BundledCommand {
231225
let should_start_ntx_builder = !ntx_builder.disabled;
232226

233227
// Start block-producer. The block-producer's endpoint is available after loading completes.
234-
let block_producer_id = join_set
235-
.spawn({
236-
let store_url = Url::parse(&format!("http://{store_block_producer_address}"))
237-
.context("Failed to parse URL")?;
238-
let validator_url = Url::parse(&format!("http://{validator_address}"))
239-
.context("Failed to parse URL")?;
240-
async move {
241-
BlockProducer {
242-
block_producer_address,
243-
store_url,
244-
validator_url,
245-
batch_prover_url: block_producer.batch_prover_url,
246-
batch_interval: block_producer.batch_interval,
247-
block_interval: block_producer.block_interval,
248-
max_batches_per_block: block_producer.max_batches_per_block,
249-
max_txs_per_batch: block_producer.max_txs_per_batch,
250-
grpc_timeout,
251-
mempool_tx_capacity: block_producer.mempool_tx_capacity,
228+
let block_producer_id = {
229+
let validator_url = validator_url.clone();
230+
join_set
231+
.spawn({
232+
let store_url = Url::parse(&format!("http://{store_block_producer_address}"))
233+
.context("Failed to parse URL")?;
234+
async move {
235+
BlockProducer {
236+
block_producer_address,
237+
store_url,
238+
validator_url,
239+
batch_prover_url: block_producer.batch_prover_url,
240+
batch_interval: block_producer.batch_interval,
241+
block_interval: block_producer.block_interval,
242+
max_batches_per_block: block_producer.max_batches_per_block,
243+
max_txs_per_batch: block_producer.max_txs_per_batch,
244+
grpc_timeout,
245+
mempool_tx_capacity: block_producer.mempool_tx_capacity,
246+
}
247+
.serve()
248+
.await
249+
.context("failed while serving block-producer component")
252250
}
253-
.serve()
254-
.await
255-
.context("failed while serving block-producer component")
256-
}
257-
})
258-
.id();
251+
})
252+
.id()
253+
};
259254

260-
let validator_id = join_set
261-
.spawn({
262-
async move {
263-
Validator {
264-
address: validator_address,
255+
// Start RPC component.
256+
let rpc_id = {
257+
let block_producer_url = block_producer_url.clone();
258+
let validator_url = validator_url.clone();
259+
join_set
260+
.spawn(async move {
261+
let store_url = Url::parse(&format!("http://{store_rpc_address}"))
262+
.context("Failed to parse URL")?;
263+
Rpc {
264+
listener: grpc_rpc,
265+
store_url,
266+
block_producer_url: Some(block_producer_url),
267+
validator_url,
265268
grpc_timeout,
266-
signer,
267269
}
268270
.serve()
269271
.await
270-
.context("failed while serving validator component")
271-
}
272-
})
273-
.id();
274-
275-
// Start RPC component.
276-
let rpc_id = join_set
277-
.spawn(async move {
278-
let store_url = Url::parse(&format!("http://{store_rpc_address}"))
279-
.context("Failed to parse URL")?;
280-
let block_producer_url = Url::parse(&format!("http://{block_producer_address}"))
281-
.context("Failed to parse URL")?;
282-
let validator_url = Url::parse(&format!("http://{validator_address}"))
283-
.context("Failed to parse URL")?;
284-
Rpc {
285-
listener: grpc_rpc,
286-
store_url,
287-
block_producer_url: Some(block_producer_url),
288-
validator_url,
289-
grpc_timeout,
290-
}
291-
.serve()
292-
.await
293-
.context("failed while serving RPC component")
294-
})
295-
.id();
272+
.context("failed while serving RPC component")
273+
})
274+
.id()
275+
};
296276

297277
// Lookup table so we can identify the failed component.
298278
let mut component_ids = HashMap::from([
299279
(store_id, "store"),
300280
(block_producer_id, "block-producer"),
301-
(validator_id, "validator"),
302281
(rpc_id, "rpc"),
303282
]);
304283

305284
// Start network transaction builder. The endpoint is available after loading completes.
306285
if should_start_ntx_builder {
307286
let store_ntx_builder_url = Url::parse(&format!("http://{store_ntx_builder_address}"))
308287
.context("Failed to parse URL")?;
309-
let validator_url = Url::parse(&format!("http://{validator_address}"))
310-
.context("Failed to parse URL")?;
311-
let block_producer_url = Url::parse(&format!("http://{block_producer_address}"))
312-
.context("Failed to parse URL")?;
288+
let block_producer_url = block_producer_url.clone();
289+
let validator_url = validator_url.clone();
313290

314291
let builder_config = ntx_builder.into_builder_config(
315292
store_ntx_builder_url,
@@ -331,6 +308,28 @@ impl BundledCommand {
331308
component_ids.insert(id, "ntx-builder");
332309
}
333310

311+
// Start the Validator if we have bound a socket.
312+
if let Some(address) = validator_socket_address {
313+
let secret_key_bytes = hex::decode(validator.validator_key)?;
314+
let signer = SecretKey::read_from_bytes(&secret_key_bytes)?;
315+
let id = join_set
316+
.spawn({
317+
async move {
318+
Validator {
319+
address,
320+
grpc_timeout,
321+
signer,
322+
data_directory,
323+
}
324+
.serve()
325+
.await
326+
.context("failed while serving validator component")
327+
}
328+
})
329+
.id();
330+
component_ids.insert(id, "validator");
331+
}
332+
334333
// SAFETY: The joinset is definitely not empty.
335334
let component_result = join_set.join_next_with_id().await.unwrap();
336335

0 commit comments

Comments
 (0)