> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inco.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Access Control

> Managing access to encrypted data

# Access Control

Use `allow` and `is_allowed` to manage decryption permissions for handles.

<Warning>
  The access control design is WIP and subject to change in future versions.
</Warning>

## granting Access

```rust theme={null}
pub fn grant_decrypt_access(
    ctx: Context<GrantAccess>,
    handle: u128,
    allowed_address: Pubkey,
) -> Result<()> {
    let cpi_ctx = CpiContext::new(
        ctx.accounts.inco_lightning_program.to_account_info(),
        Allow {
            allowance_account: ctx.accounts.allowance_account.to_account_info(),
            signer: ctx.accounts.authority.to_account_info(),
            allowed_address: ctx.accounts.allowed_address.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        },
    );

    allow(cpi_ctx, handle, true, allowed_address)?;
    Ok(())
}
```

## Checking Access

```rust theme={null}
pub fn check_access(
    ctx: Context<CheckAccess>,
    handle: u128,
) -> Result<bool> {
    let cpi_ctx = CpiContext::new(
        ctx.accounts.inco_lightning_program.to_account_info(),
        IsAllowed {
            allowance_account: ctx.accounts.allowance_account.to_account_info(),
            allowed_address: ctx.accounts.allowed_address.to_account_info(),
        },
    );

    is_allowed(cpi_ctx, handle)
}
```

## How Allowance Works

```mermaid theme={null}
flowchart TD
    subgraph Creation["Handle Creation"]
        A["Program creates<br/>encrypted handle"] --> B["Handle stored<br/>in account"]
    end

    subgraph AllowanceGrant["Granting Access"]
        C["Program calls allow()"]
        D["Allowance PDA derived from:<br/>[handle_bytes, allowed_address]"]
        E[("Allowance Account<br/>Created On-Chain")]
    end

    subgraph Access["Access Check"]
        F["User requests decrypt"]
        G{{"is_allowed?"}}
        H["✅ Allowed"]
        I["❌ Denied"]
    end

    B --> C
    C --> D
    D --> E
    E --> G
    F --> G
    G -->|"PDA exists"| H
    G -->|"No PDA"| I

    style A fill:#667eea,color:#fff
    style B fill:#764ba2,color:#fff
    style E fill:#4ecdc4,color:#fff
    style H fill:#2ecc71,color:#fff
    style I fill:#e74c3c,color:#fff
```

### Allowance PDA Derivation

```mermaid theme={null}
flowchart LR
    subgraph Inputs["PDA Seeds"]
        A["Handle<br/>(16 bytes LE)"]
        B["Allowed Address<br/>(32 bytes)"]
    end

    subgraph Derivation["PDA Derivation"]
        C{{"findProgramAddressSync"}}
        D["Inco Lightning<br/>Program ID"]
    end

    subgraph Output["Result"]
        E[("Allowance PDA<br/>Address")]
    end

    A --> C
    B --> C
    D --> C
    C --> E

    style A fill:#ff6b6b,color:#fff
    style B fill:#45b7d1,color:#fff
    style E fill:#96ceb4,color:#fff
```

***

## Access Control Functions

| Function     | Description                                                |
| ------------ | ---------------------------------------------------------- |
| `allow`      | Grant or revoke decryption access for a specific address   |
| `is_allowed` | Check if an address has decryption permission for a handle |

## Best Practice: Minimal Permissions

Grant minimal decryption permissions:

```rust theme={null}
// Only allow the account owner to decrypt their balance
allow(ctx, balance_handle, true, owner_pubkey)?;
```

<Note>
  Access control is essential for privacy. Without explicit permission grants, encrypted values cannot be decrypted.
</Note>

## Client-Side: Simulation to Get Handles

Since encrypted operations produce new handles, you often need to **simulate** the transaction first to get the resulting handle before you can derive the allowance PDA and grant access in the same transaction.

### Why Simulation is Needed

1. Operations like `e_add`, `mint_to`, `transfer` produce new handles
2. The allowance PDA is derived from `[handle.to_le_bytes(), allowed_address]`
3. You need the handle value before you can compute the PDA address
4. Simulation lets you "peek" at the result handle before actually submitting

### Simulation Pattern

```typescript theme={null}
import { PublicKey } from '@solana/web3.js';

const INCO_LIGHTNING_PROGRAM_ID = new PublicKey('5sjEbPiqgZrYwR31ahR6Uk9wf5awoX61YGg7jExQSwaj');

// Helper: derive allowance PDA from handle
function getAllowancePda(handle: bigint, allowedAddress: PublicKey): [PublicKey, number] {
  const handleBuffer = Buffer.alloc(16);
  let h = handle;
  for (let i = 0; i < 16; i++) {
    handleBuffer[i] = Number(h & BigInt(0xff));
    h = h >> BigInt(8);
  }
  return PublicKey.findProgramAddressSync(
    [handleBuffer, allowedAddress.toBuffer()],
    INCO_LIGHTNING_PROGRAM_ID
  );
}

// Helper: simulate transaction and extract handle from account data
async function simulateAndGetHandle(
  connection: Connection,
  tx: Transaction,
  accountPubkey: PublicKey,
  walletKeypair: Keypair
): Promise<bigint | null> {
  const { blockhash } = await connection.getLatestBlockhash();
  tx.recentBlockhash = blockhash;
  tx.feePayer = walletKeypair.publicKey;
  tx.sign(walletKeypair);

  const simulation = await connection.simulateTransaction(tx, undefined, [accountPubkey]);
  if (simulation.value.err) return null;

  if (simulation.value.accounts?.[0]?.data) {
    const data = Buffer.from(simulation.value.accounts[0].data[0], "base64");
    // Extract handle from account data (offset depends on your account struct)
    const amountBytes = data.slice(72, 88);  // Adjust offset as needed
    let handle = BigInt(0);
    for (let i = 15; i >= 0; i--) {
      handle = handle * BigInt(256) + BigInt(amountBytes[i]);
    }
    return handle;
  }
  return null;
}
```

### Example: Mint with Auto-Allow

```typescript theme={null}
// Step 1: Build transaction for simulation (without allowance accounts)
const txForSim = await program.methods
  .mintTo(hexToBuffer(encryptedAmount), inputType)
  .accounts({
    mint: mintKeypair.publicKey,
    account: ownerAccountKp.publicKey,
    mintAuthority: walletKeypair.publicKey,
    incoLightningProgram: INCO_LIGHTNING_PROGRAM_ID,
    systemProgram: SystemProgram.programId,
  })
  .transaction();

// Step 2: Simulate to get the new handle
const newHandle = await simulateAndGetHandle(connection, txForSim, ownerAccountKp.publicKey, walletKeypair);

// Step 3: Derive allowance PDA from the simulated handle
const [allowancePda] = getAllowancePda(newHandle!, walletKeypair.publicKey);

// Step 4: Execute real transaction with allowance accounts
const tx = await program.methods
  .mintTo(hexToBuffer(encryptedAmount), inputType)
  .accounts({
    mint: mintKeypair.publicKey,
    account: ownerAccountKp.publicKey,
    mintAuthority: walletKeypair.publicKey,
    incoLightningProgram: INCO_LIGHTNING_PROGRAM_ID,
    systemProgram: SystemProgram.programId,
  })
  .remainingAccounts([
    { pubkey: allowancePda, isSigner: false, isWritable: true },
    { pubkey: walletKeypair.publicKey, isSigner: false, isWritable: false },
  ])
  .rpc();
```

### Example: Transfer with Auto-Allow for Both Accounts

```typescript theme={null}
// Step 1: Build transaction for simulation
const txForSim = await program.methods
  .transfer(hexToBuffer(encryptedAmount), inputType)
  .accounts({
    source: sourceAccountKp.publicKey,
    destination: destAccountKp.publicKey,
    authority: walletKeypair.publicKey,
    incoLightningProgram: INCO_LIGHTNING_PROGRAM_ID,
    systemProgram: SystemProgram.programId,
  })
  .transaction();

// Step 2: Simulate to get both new handles
const { blockhash } = await connection.getLatestBlockhash();
txForSim.recentBlockhash = blockhash;
txForSim.feePayer = walletKeypair.publicKey;
txForSim.sign(walletKeypair);

const simulation = await connection.simulateTransaction(
  txForSim,
  undefined,
  [sourceAccountKp.publicKey, destAccountKp.publicKey]
);

// Extract handles from both accounts
const sourceHandle = extractHandleFromSimulation(simulation.value.accounts?.[0]);
const destHandle = extractHandleFromSimulation(simulation.value.accounts?.[1]);

// Step 3: Derive allowance PDAs
const [sourceAllowancePda] = getAllowancePda(sourceHandle!, walletKeypair.publicKey);
const [destAllowancePda] = getAllowancePda(destHandle!, walletKeypair.publicKey);

// Step 4: Execute with allowance accounts for both source and destination
const tx = await program.methods
  .transfer(hexToBuffer(encryptedAmount), inputType)
  .accounts({
    source: sourceAccountKp.publicKey,
    destination: destAccountKp.publicKey,
    authority: walletKeypair.publicKey,
    incoLightningProgram: INCO_LIGHTNING_PROGRAM_ID,
    systemProgram: SystemProgram.programId,
  })
  .remainingAccounts([
    { pubkey: sourceAllowancePda, isSigner: false, isWritable: true },
    { pubkey: walletKeypair.publicKey, isSigner: false, isWritable: false },
    { pubkey: destAllowancePda, isSigner: false, isWritable: true },
    { pubkey: walletKeypair.publicKey, isSigner: false, isWritable: false },
  ])
  .rpc();
```

<Note>
  For complete working examples with simulation patterns, see the [lightning-rod-solana repository](https://github.com/Inco-fhevm/lightning-rod-solana).
</Note>
