Encrypted ACL: Testing
Pre-Alpha Disclaimer: This is an early pre-alpha release for exploring the SDK and starting development only. There is no real encryption — all data is completely public and stored as plaintext on-chain. Do not submit any sensitive or real data. Encryption keys and the trust model are not final; do not rely on any encryption guarantees or key material until mainnet. All interfaces, APIs, and data formats are subject to change without notice. The Solana program and all on-chain data will be wiped periodically and everything will be deleted when we transition to Encrypt Alpha 1. This software is provided “as is” without warranty of any kind; use is entirely at your own risk and dWallet Labs assumes no liability for any damages arising from its use.
1. Unit Tests (Graph Logic)
Verify the FHE graphs produce correct bitwise results using a mock evaluator. No SBF build needed.
cargo test -p encrypted-acl-anchor --lib
#![allow(unused)]
fn main() {
#[test]
fn grant_single_permission() {
let r = run_mock(
grant_permission_graph,
&[0, 1],
&[FheType::EUint64, FheType::EUint64],
);
assert_eq!(r[0], 1, "granting READ (bit 0) to 0 should yield 1");
}
#[test]
fn grant_multiple_permissions() {
let r = run_mock(
grant_permission_graph,
&[1, 2],
&[FheType::EUint64, FheType::EUint64],
);
assert_eq!(r[0], 3, "granting WRITE (bit 1) to READ (1) should yield 3");
}
#[test]
fn revoke_permission() {
let r = run_mock(
revoke_permission_graph,
&[3, 0xFFFFFFFFFFFFFFFE],
&[FheType::EUint64, FheType::EUint64],
);
assert_eq!(r[0], 2, "revoking READ (bit 0) from 3 should yield 2");
}
#[test]
fn check_has_permission() {
let r = run_mock(
check_permission_graph,
&[5, 1],
&[FheType::EUint64, FheType::EUint64],
);
assert_eq!(r[0], 1, "checking READ on 5 (READ|EXECUTE) should yield 1");
}
#[test]
fn check_missing_permission() {
let r = run_mock(
check_permission_graph,
&[4, 1],
&[FheType::EUint64, FheType::EUint64],
);
assert_eq!(r[0], 0, "checking READ on 4 (EXECUTE only) should yield 0");
}
#[test]
fn graph_shapes() {
let d = grant_permission_graph();
let pg = parse_graph(&d).unwrap();
assert_eq!(pg.header().num_inputs(), 2);
assert_eq!(pg.header().num_outputs(), 1);
// Same shape for revoke and check
}
}
2. LiteSVM Integration Tests (E2E)
Full lifecycle tests using LiteSVM with EncryptTestContext. Tests the
complete flow: create resource, grant, revoke, check, and decrypt.
just build-sbf-examples
cargo test -p encrypted-acl-anchor --test litesvm
The test helpers abstract common patterns:
#![allow(unused)]
fn main() {
// Create a resource with encrypted-zero permissions
fn create_resource(ctx, program_id, admin) -> (resource_pda, permissions_ct, resource_id)
// Grant a permission: create encrypted bit, CPI, evaluate graph
fn do_grant(ctx, program_id, ..., permission_value: u128)
// Revoke a permission: create encrypted mask, CPI, evaluate graph
fn do_revoke(ctx, program_id, ..., revoke_mask: u128)
// Check a permission: create encrypted bit + result, CPI, evaluate, decrypt
fn do_check(ctx, program_id, ..., permission_value: u128) -> u128
}
Full lifecycle test:
#![allow(unused)]
fn main() {
#[test]
fn test_full_acl_lifecycle() {
let mut ctx = EncryptTestContext::new_default();
let (program_id, cpi_authority, cpi_bump) = setup_anchor_program(&mut ctx);
let admin = ctx.new_funded_keypair();
// 1. Create resource
let (resource_pda, perm_ct, resource_id) =
create_resource(&mut ctx, &program_id, &admin);
// 2. Grant READ (bit 0 = 1)
do_grant(&mut ctx, &program_id, &cpi_authority, cpi_bump, &admin,
&resource_pda, &perm_ct, 1);
// 3. Grant WRITE (bit 1 = 2)
do_grant(&mut ctx, &program_id, &cpi_authority, cpi_bump, &admin,
&resource_pda, &perm_ct, 2);
// 4. Check READ -- should pass
let checker1 = ctx.new_funded_keypair();
let result = do_check(&mut ctx, &program_id, &cpi_authority, cpi_bump,
&checker1, &resource_pda, &perm_ct, &resource_id, 1);
assert_eq!(result, 1, "should have READ after granting");
// 5. Revoke READ (mask = 0xFFFFFFFFFFFFFFFE)
do_revoke(&mut ctx, &program_id, &cpi_authority, cpi_bump, &admin,
&resource_pda, &perm_ct, 0xFFFFFFFFFFFFFFFE);
// 6. Check READ -- should fail
let checker2 = ctx.new_funded_keypair();
let result = do_check(&mut ctx, &program_id, &cpi_authority, cpi_bump,
&checker2, &resource_pda, &perm_ct, &resource_id, 1);
assert_eq!(result, 0, "should NOT have READ after revoking");
// 7. Decrypt permissions to verify = 2 (WRITE only)
let perm_value = ctx.decrypt_from_store(&perm_ct);
assert_eq!(perm_value, 2, "permissions should be 2 (WRITE only)");
}
}
3. Mollusk Instruction-Level Tests
Mollusk tests reveal_check and reveal_permissions in isolation. No CPI or
Encrypt program needed – just raw account data and instruction processing.
just build-sbf-examples
cargo test -p encrypted-acl-anchor --test mollusk
Tests cover:
reveal_checksucceeds with matching digestreveal_checkrejects wrong checkerreveal_checkrejects digest mismatchreveal_permissionssucceeds with matching digestreveal_permissionsrejects wrong admin
#![allow(unused)]
fn main() {
#[test]
fn test_reveal_check_success() {
let (mollusk, pid) = setup();
let checker = Pubkey::new_unique();
let digest = [0xABu8; 32];
let check_data = build_anchor_access_check(&checker, &Pubkey::new_unique(), &digest, 0);
let request_data = build_decryption_request_data(&digest, 1);
let result = mollusk.process_instruction(/* ... */);
assert!(result.program_result.is_ok());
let revealed = u64::from_le_bytes(
result.resulting_accounts[0].1.data[104..112].try_into().unwrap(),
);
assert_eq!(revealed, 1);
}
#[test]
fn test_reveal_permissions_success() {
// Same pattern, checks resource.revealed_permissions at offset 136..144
}
}
4. Running All Tests
# Everything (build + all test types)
just test-examples
# Just LiteSVM e2e
just test-examples-litesvm
# Just Mollusk
just test-examples-mollusk
# Just program-test
just test-examples-program-test
# Single crate, all tests
cargo test -p encrypted-acl-anchor