Skip to content

feat: Validator bootstrap#1764

Merged
sergerad merged 15 commits intonextfrom
sergerad-validator-bootstrap
Mar 12, 2026
Merged

feat: Validator bootstrap#1764
sergerad merged 15 commits intonextfrom
sergerad-validator-bootstrap

Conversation

@sergerad
Copy link
Collaborator

@sergerad sergerad commented Mar 9, 2026

Closes #1761

Tested via the following commands locally:

function bootstrapnode() {
    # Bootstrap validator
    rm -rf /tmp/validator /tmp/accounts
    cfg="/Users/sergeradinovich/Source/miden-node/crates/store/src/genesis/config/samples/01-simple.toml"
    export AWS_REGION=REDACTED
    miden-node validator bootstrap --data-directory /tmp/validator --accounts-directory /tmp/accounts --genesis-config-file "$cfg" --validator.key.kms-id REDACTED
    # Bootstrap store
     rm -rf /tmp/store
    miden-node store bootstrap --data-directory /tmp/store --genesis-block /tmp/validator/genesis.dat
}

function startnode() {
   miden-node bundled start --rpc.url http://0.0.0.0:57291 --data-directory /tmp/store --enable-otel --validator.url http://127.0.0.1:9191
}

function startvalidator() {
    export AWS_REGION=eu-north-1
   miden-node validator start --enable-otel --data-directory /tmp/validator http://0.0.0.0:9191 --key.kms-id REDACTED
}

@sergerad sergerad requested review from Mirko-von-Leipzig, bobbinth and mmagician and removed request for Mirko-von-Leipzig and bobbinth March 9, 2026 23:53
@sergerad
Copy link
Collaborator Author

Noting that this innocuous error does occur on the first start of the bundled node:

2026-03-10T02:19:48.260606Z ERROR ntx.block_producer.client.subscribe_to_mempool: miden-ntx-builder: crates/ntx-builder/src/block_producer.rs:61: error: status: 'Client specified an invalid argument', self: "Mempool's chain tip 1 does not match request's 0", metadata: {"content-type": "application/grpc", "date": "Tue, 10 Mar 2026 02:19:48 GMT"}

@Mirko-von-Leipzig
Copy link
Collaborator

Noting that this innocuous error does occur on the first start of the bundled node:

2026-03-10T02:19:48.260606Z ERROR ntx.block_producer.client.subscribe_to_mempool: miden-ntx-builder: crates/ntx-builder/src/block_producer.rs:61: error: status: 'Client specified an invalid argument', self: "Mempool's chain tip 1 does not match request's 0", metadata: {"content-type": "application/grpc", "date": "Tue, 10 Mar 2026 02:19:48 GMT"}

Ah that's not unexpected; though we can fix that now.

We currently have a loop of:

loop {
    let chain_tip = store.get_latest_header().await.block_num();
    if let Ok(subscription) = block_producer.subscribe(chain_tip).await {
        break subscription;
    }
}

which means there is a race condition between us fetching the chain tip and subscribing.

@SantiagoPittella I think we now remove the chain_tip from the mempool subscription gRPC as per #1487, which will "fix" this.

Subscribing to the mempool currently requires the block-height, but this requirement can be removed so long as the above task is spawned after the subscription is created.

"genesis block signature verification failed",
);

Ok(Self(block))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work for any valid signature<> key pair, which means the store will accept any genesis block as valid.

I wonder if we should instead keep the validator_key argument on the store bootstrap command - but change it to only mean the public key of the validator (AFAIU, currently a private key is provided).
E.g. validator_key -> validator_pk?

This way, the store is sure that the genesis block comes from the canonical validator.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to think about how this should work though.

afaik currently the pub key for each block isn't derived from the config, but rather its the key specified in the genesis block. As in, by definition, the node cannot be configured with a key -- otherwise the node operator can determine the "valid" key.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should instead keep the validator_key argument on the store bootstrap command - but change it to only mean the public key of the validator (AFAIU, currently a private key is provided).
E.g. validator_key -> validator_pk?

The store bootstrap command does not take a private key:

Usage: miden-node store bootstrap --data-directory <DIR> --genesis-block <FILE>

Options:
      --data-directory <DIR>  Directory in which to store the database and raw block data [env: MIDEN_NODE_DATA_DIRECTORY=]
      --genesis-block <FILE>  Path to the pre-signed genesis block file produced by the validator
  -h, --help                  Print help (see more with '--help')

This will work for any valid signature<> key pair, which means the store will accept any genesis block as valid.

I think this is the point - that the node operator relies on the validator as the source of truth for this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bobbinth the validator currently doesn't ensure the "canonical chain" at all. As in, the node operator could theoretically modify a historical block and the validator would still sign the subsequent ones (because the validator doesn't check the chain of commitments and signatures).

Is this something we want to add to the validator? Any of these:

  • Track the chain tip
  • Verify prev_block_commitment links to the last block it signed
  • Verify block_num is sequential
  • Verify chain_commitment (the MMR) is consistent
  • Verify the previous block's signature

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bobbinth the validator currently doesn't ensure the "canonical chain" at all. As in, the node operator could theoretically modify a historical block and the validator would still sign the subsequent ones (because the validator doesn't check the chain of commitments and signatures).

Is this something we want to add to the validator?

I think the validator should make sure the block it is signing is the valid next block in the chain (for some reason, I thought that was already the case, but if not, let's create an issue for this).

The way it could work:

  • We use the validator to "bootstrap" the genesis block (I think this is what this PR is about, but I haven't reviewed the details yet). This results in the validator keeping track of the chain tip (which is at first just the genesis block).
  • For any new block that the validator needs to sign, the validator:
    • Makes sure that all transactions in the block are the ones it has seen/validated before (we already do this, I believe).
    • Makes sure that given this transactions, we get the block header that we need to sign (we already do this, I believe).
    • Makes sure that the new block header is built on top of the previous block. This may also require checking consistency of account and nullifier tree updates (it may be possible to incorporate this in the above two checks, but I'm not 100% sure).
  • If all looks good, the validator signs the block, and sets the new block's header as the chain tip.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sergerad sergerad requested a review from mmagician March 10, 2026 22:01
/// to disk.
async fn build_and_write_genesis(
config: GenesisConfig,
signer: impl BlockSigner,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would suggest we use &dyn BlockSigner since this isn't really performance critical.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think it gets gnarly because of the associated error type

pub trait BlockSigner {
    type Error: error::Error + Send + Sync + 'static;

@sergerad sergerad merged commit 84c853b into next Mar 12, 2026
18 checks passed
@sergerad sergerad deleted the sergerad-validator-bootstrap branch March 12, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dedicated validator bootstrap command

4 participants