miden-validator 0.17.0-rc.1

Miden validator
Documentation
use super::*;

fn config(seed: u32) -> ProtocolConfig {
    use miden_protocol::protocol_config::KernelConfig;

    let base = test_protocol_config();
    ProtocolConfig::new(
        base.fee_asset_id(),
        KernelConfig::new(Word::from([seed, 0, 0, 0]), vec![]).unwrap(),
        base.batch_kernel().clone(),
        base.block_kernel().clone(),
        base.proof_verification().clone(),
    )
    .unwrap()
}

fn header_at(base: &BlockHeader, height: u32, config: &ProtocolConfig) -> BlockHeader {
    BlockHeader::new(
        base.commitment(),
        height.into(),
        base.chain_commitment(),
        base.account_root(),
        base.nullifier_root(),
        base.note_root(),
        base.tx_commitment(),
        base.validator_config().clone(),
        base.fee_parameters().clone(),
        config.to_commitment(),
        base.next_protocol_config().cloned(),
        base.timestamp() + 1,
    )
}

pub(super) async fn history(db: &ValidatorDbWriter) -> Vec<(i64, Word, ProtocolConfig)> {
    db.reader.reader.read("activation_history", |tx| {
        tx.query(
            "SELECT block_number, commitment, protocol_config FROM protocol_configs ORDER BY block_number",
            &[],
            |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
        )
    }).await.unwrap()
}

#[tokio::test]
async fn activations_include_reused_configs_and_survive_reopen() {
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join("validator.sqlite3");
    let first = test_protocol_config();
    let second = config(42);
    let genesis = genesis_header(&first);
    bootstrap(path.clone(), NonZeroUsize::new(2).unwrap(), genesis.clone(), first.clone())
        .await
        .unwrap();
    let db = load(path.clone()).await.unwrap();
    let mut parent = genesis;
    for (height, active, supplied) in
        [(1, &first, true), (2, &second, true), (3, &second, false), (4, &first, false)]
    {
        let header = header_at(&parent, height, active);
        db.upsert_block_header_with_protocol_config(
            header.clone(),
            supplied.then(|| active.clone()),
        )
        .await
        .unwrap();
        parent = header;
    }
    let expected = vec![
        (0, first.to_commitment(), first.clone()),
        (2, second.to_commitment(), second),
        (4, first.to_commitment(), first.clone()),
    ];
    assert_eq!(history(&db).await, expected);
    assert_eq!(db.load_protocol_config(first.to_commitment()).await.unwrap(), Some(first));
    drop(db);
    let db = load(path).await.unwrap();
    assert_eq!(history(&db).await, expected);
    assert_eq!(db.load_chain_tip().await.unwrap(), Some(parent));
}

#[tokio::test]
async fn repeated_tip_writes_preserve_activation() {
    let dir = tempfile::tempdir().unwrap();
    let db = setup(dir.path().join("validator.sqlite3")).await.unwrap();
    let first = test_protocol_config();
    let second = config(42);
    let genesis = genesis_header(&first);
    db.upsert_block_header_with_protocol_config(genesis.clone(), Some(first.clone()))
        .await
        .unwrap();
    let tip = header_at(&genesis, 1, &second);
    db.upsert_block_header_with_protocol_config(tip.clone(), Some(second.clone()))
        .await
        .unwrap();
    db.upsert_block_header_with_protocol_config(tip.clone(), None).await.unwrap();
    let tip = header_with_next_timestamp(&tip);
    db.upsert_block_header_with_protocol_config(tip.clone(), Some(second.clone()))
        .await
        .unwrap();
    assert_eq!(
        history(&db).await,
        vec![(0, first.to_commitment(), first.clone()), (1, second.to_commitment(), second),]
    );
    assert_eq!(db.load_chain_tip().await.unwrap(), Some(tip));
}

#[tokio::test]
async fn invalid_configs_preserve_existing_history() {
    let dir = tempfile::tempdir().unwrap();
    let db = setup(dir.path().join("validator.sqlite3")).await.unwrap();
    let first = test_protocol_config();
    let second = config(42);
    let genesis = genesis_header(&first);
    db.upsert_block_header_with_protocol_config(genesis.clone(), Some(first.clone()))
        .await
        .unwrap();
    let before = history(&db).await;
    for supplied in [None, Some(first)] {
        db.upsert_block_header_with_protocol_config(header_at(&genesis, 1, &second), supplied)
            .await
            .unwrap_err();
        assert_eq!(history(&db).await, before);
        assert_eq!(db.load_chain_tip().await.unwrap(), Some(genesis.clone()));
    }
}