world-id-core 0.11.0

Reference SDK for interacting with the World ID Protocol. For authenticator providers, issuers and RPs.
Documentation
#![cfg(feature = "authenticator")]

use alloy::primitives::U256;
use backon::{ExponentialBuilder, Retryable};
use world_id_core::{Authenticator, AuthenticatorError, api_types::GatewayRequestState};
use world_id_gateway::{
    BatchPolicyConfig, GatewayConfig, SignerArgs, defaults, spawn_gateway_for_tests,
};
use world_id_primitives::{Config, ServiceEndpoint};
use world_id_test_utils::anvil::TestAnvil;

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_authenticator_registration() {
    rustls::crypto::aws_lc_rs::default_provider()
        .install_default()
        .expect("can install");
    let anvil = TestAnvil::spawn().expect("failed to spawn anvil");

    let deployer = anvil.signer(0).unwrap();

    let registry_address = anvil
        .deploy_world_id_registry(deployer.clone())
        .await
        .unwrap();

    // Spawn gateway pointing to the same anvil instance
    let signer_args = SignerArgs::from_wallet(hex::encode(deployer.to_bytes()));
    let gateway_config = GatewayConfig {
        registry_addr: registry_address,
        registry_version: None,
        provider: world_id_gateway::ProviderArgs {
            http: Some(vec![anvil.endpoint().parse().unwrap()]),
            signer: Some(signer_args),
            ..Default::default()
        },
        listen_addr: (std::net::Ipv4Addr::LOCALHOST, 0).into(),
        max_create_batch_size: 10,
        max_ops_batch_size: 10,
        redis_url: std::env::var("REDIS_URL")
            .unwrap_or_else(|_| "redis://localhost:6379".to_string()),
        request_timeout_secs: 10,
        rate_limit_max_requests: None,
        rate_limit_window_secs: None,
        sweeper_interval_secs: defaults::SWEEPER_INTERVAL_SECS,
        stale_queued_threshold_secs: defaults::STALE_QUEUED_THRESHOLD_SECS,
        stale_submitted_threshold_secs: defaults::STALE_SUBMITTED_THRESHOLD_SECS,
        batch_policy: BatchPolicyConfig::default(),
    };
    let gateway = spawn_gateway_for_tests(gateway_config)
        .await
        .expect("failed to spawn gateway");
    let gw_addr = gateway.listen_addr;
    let gateway_url = format!("http://{}:{}", gw_addr.ip(), gw_addr.port());

    let config = Config::new(
        Some(anvil.endpoint().to_string()),
        anvil.instance.chain_id(),
        registry_address,
        ServiceEndpoint::direct("http://127.0.0.1:0".to_string()), // not needed for this test
        ServiceEndpoint::direct(gateway_url.clone()),
        Vec::new(),
        2,
    )
    .unwrap();

    let seed = [1u8; 32];
    let recovery_address = anvil.signer(1).unwrap().address();
    // Account doesn't exist, so init will error
    let result = Authenticator::init(&seed, config.clone()).await;
    assert!(matches!(
        result,
        Err(AuthenticatorError::AccountDoesNotExist)
    ),);

    // Create the account (awaits until creation)
    // NOTE how we use `register()` instead of `init_or_register()` to test this specific flow.
    let start = std::time::Instant::now();
    let initializing_account =
        Authenticator::register(&seed, config.clone(), Some(recovery_address))
            .await
            .unwrap();

    let poller = || async {
        match initializing_account.poll_status().await {
            Ok(GatewayRequestState::Finalized { .. }) => Ok(()),
            _ => Err(""),
        }
    };

    poller
        .retry(ExponentialBuilder::default().with_max_times(10))
        .await
        .unwrap();

    let authenticator = Authenticator::init(&seed, config.clone()).await.unwrap();
    let elapsed = start.elapsed();
    tracing::info!("Account creation successful in {elapsed:?}");
    assert_eq!(authenticator.leaf_index(), 1);
    assert_eq!(authenticator.recovery_counter(), U256::from(0));

    // If we initialize again, it will work
    let authenticator = Authenticator::init(&seed, config).await.unwrap();
    assert_eq!(authenticator.leaf_index(), 1);
}