Inside Private Ephemeral Rollups: A Technical Deep Dive for Solana Builders

Inside Private Ephemeral Rollups: A Technical Deep Dive for Solana Builders

Andy (Developer Relations)
Andy (Developer Relations)
@fauxfire_

Solana has always been fast, scalable, and composable, but it has never been private. Every program call, account, and state transition is visible on-chain. That transparency is powerful for verification, but it makes certain applications impossible to build.

MagicBlock is changing that. We recently announced the launch of our high-performance, general-purpose Trusted Execution Environments (TEEs) to Solana. We call this the Private Ephemeral Rollup (PER).

By leveraging the security guarantees of Intel's Trust Domain Extensions (TDX) and combining them, combined with MagicBlock’s Ephemeral Rollup (ER) technology, we’re making it possible to run sensitive logic within a hardware-secured environment, while retaining the composability and speed of Solana.

For the first time, builders can design applications that are simultaneously:

  • Confidential: state is protected from all unauthorized parties
  • Scalable: running inside an ER that can execute blocks at high throughput and low latency
  • Composable: still able to interoperate with other Solana programs
  • Compliant: easy-to-enforce compliance thanks to a fine-grained access control layer

This unlocks new categories of applications that were previously impossible on Solana, including confidential transfers, sealed-bid auctions, private games, and enterprise workflows.

Trusted Execution Environments

A trusted execution environment acts as a vault inside a CPU. Generally, when you run a program - such as a validator - the operating system can see and influence everything: the code, the state, and the memory. A TEE creates a hardware-secured space that physically prevents interference, even by the machine it’s running on.

We used a TEE to protect the entire state of an Ephemeral Rollup. Normally, when you execute a smart contract onchain, every step of the process is visible to anyone. Any observer can see what program your transaction is calling, what accounts you’re calling it with, and what the subsequent state changes are. Using the TEE, we can selectively shield the state of the ER. 

Every account is public by default, like in Solana, but programs can explicitly define access rules for their accounts.

This means you can prevent your transfers, program calls, and interactions from being broadcast to the entire world.

Why We Chose TEE

There’s no shortage of approaches to privacy in crypto: fully homomorphic encryption (FHE), zero-knowledge proofs (ZK), and multi-party computation (MPC) are all powerful tools. But each comes with tradeoffs:

Solution What it Does Pros Cons
Trusted Execution Environments (TEEs) Hardware-secured execution within a CPU Near-native performance, run normal code Trust assumption in vendor hardware
Fully Homomorphic Encryption (FHE) Compute directly on encrypted data Maximal secrecy, data never decrypted Extremely slow, specialized tooling, complex key management
Zero Knowledge Proofs (ZK) Prove correctness without revealing inputs Efficient verification, great for identity & compliance Proof generation is computationally heavy
Multi-Party Computation (MPC) Split a secret across multiple parties for joint computation Strong guarantees of shared trust High coordination overhead, latency, and specialized tooling

Every choice comes with tradeoffs:

  • ZK is excellent for verification, not for general-purpose computation
  • MPC works when shared trust matters, but not when low latency is critical
  • FHE is promising in theory, but still far from practical for most workloads

We chose TEE as our solution because it offers the most practical benefits: confidentiality, real-time performance, and developer-friendliness that Solana builders can utilize today. 

Application Unlocks

Until now, every application built on Solana has been fully transparent by design. Every account, state transition, and program call is fully public. While this transparency enables trustless verification, it also limits what developers can realistically build.

Private Ephemeral Rollups remove this limitation. By combining Solana’s composability and speed with hardware-backed confidentiality, developers can now design applications that were previously impractical or impossible:

  • Confidential Transfers: move assets privately, without exposing balances or counterparties
  • Sealed-Bid Auctions: enable fair price discovery by keeping bids hidden until settlement
  • Private Games: support use cases like poker or strategic games where revealing state undermines gameplay
  • Enterprise and Compliance Flows: run payroll, supply chain operations, or regulated processes with blockchain guarantees while keeping sensitive data private
  • Identity-Based Access: verify group membership without revealing unnecessary account history.

Program Implementation Flow

Writing programs for the MagicBlock TEE is streamlined to make it as close as possible to a vanilla Solana program. The most important distinction is the existence of the Permission Program.

The permission program is used to manage fine-grained privacy control for individual Solana accounts and account groups. Here is the flow of the Permission Program.

  1. Create a Permission Group: This is done via CPI into the permission program. You can define an arbitrary number of groups, each with different subsets of users and permissions.
  2. Create Permissions: Add permissions to the group we created. Currently, the permission account implies read access, but in the future, there may be a finer split of read/write access.
  3. Access: When attempting to access account data from the Private Ephemeral Rollup on the client-side, you will be required to authenticate your identity to access permissioned state.

The abstraction into groups mean you can easily modify the permissions of any set of users in a single transaction. This permissioning is done on the Solana L1, although it can be updated on the fly. 

Below is an example implementation:

use magicblock_permission_client::instructions::{
    CreateGroupCpiBuilder, CreatePermissionCpiBuilder,
};


pub fn create_permission(ctx: Context<CreatePermission>, id: Pubkey) -> Result<()> {
       let CreatePermission {
           payer,
           permission,
           permission_program,
           group,
           deposit,
           user,
           system_program,
       } = ctx.accounts;

 // [1] Create a Permission Group
       CreateGroupCpiBuilder::new(&permission_program)
           .group(&group)
           .id(id)
           .members(vec![user.key()])
           .payer(&payer)
           .system_program(system_program)
           .invoke()?;

 // [2] Create Permissions
       CreatePermissionCpiBuilder::new(&permission_program)
           .permission(&permission)
           .delegated_account(&deposit.to_account_info())
           .group(&group)
           .payer(&payer)
           .system_program(system_program)
           .invoke_signed(&[&[
               DEPOSIT_PDA_SEED,
               user.key().as_ref(),
               deposit.token_mint.as_ref(),
               &[ctx.bumps.deposit],
           ]])?;

       Ok(())
   }

#[derive(Accounts)]
pub struct CreatePermission<'info> {
   #[account(mut)]
   pub payer: Signer<'info>,
   /// CHECK: Anyone can create the permission
   pub user: UncheckedAccount<'info>,
   #[account(
       seeds = [DEPOSIT_PDA_SEED, user.key().as_ref(), deposit.token_mint.as_ref()],
       bump
   )]
   pub deposit: Account<'info, Deposit>,
   /// CHECK: Checked by the permission program
   #[account(mut)]
   pub permission: UncheckedAccount<'info>,
   /// CHECK: Checked by the permission program
   #[account(mut)]
   pub group: UncheckedAccount<'info>,
   /// CHECK: Checked by the permission program
   pub permission_program: UncheckedAccount<'info>,
   pub system_program: Program<'info, System>,
}

Client Implementation 

When implementing the frontend, there are three main distinctions from a traditional Solana frontend.

  1. Attestation: To verify the RPC is running on secure hardware, users can send a challenge to the RPC. The RPC will respond with cryptographically signed data that users can verify, attesting that it is running on secure hardware.
  2. Client Challenges: To verify that the user accessing the state is permissioned, it must prove to the RPC that it owns the specified public key. This requires signing a challenge message generated by the RPC. If successful, you receive an access token.
  3. Access: To access state information, simply pass your access token as a query parameter of the RPC URL when creating a connection.

Attestation

The frontend uses TDX (Trust Domain Extensions) quote verification to attest that the ephemeral rollup server is running on genuine secure hardware. This is implemented through the verifyTeeRpcIntegrity method.

  1. Generate Challenge: Create a random 32-byte challenge and encode it as base64. This ensures each attestation request is unique and prevents replay attacks.
  2. Request Quote: Send the challenge to the TEE RPC server. The server generates a TDX quote that includes the challenge, proving it's running on genuine Intel TDX hardware.
  3. Fetch Collateral: Retrieve cryptographic certificates and collateral data from the PCCS (Provisioning Certificate Caching Service) using the quote. This provides the trust chain needed for verification.
  4. Verify Quote: Use the DCAP QVL (Quote Verification Library) WASM module to cryptographically verify the quote against the collateral and current timestamp. This confirms the hardware attestation is valid and recent.

We handle this flow using our SDK:

import {verifyTeeRpcIntegrity} from '@magicblock-labs/ephemeral-rollups-sdk';

const isIntegrityVerified = await verifyTeeRpcIntegrity(PRIVATE_ER_URL);

Client Challenge Flow

  1. Request Challenge: Request a challenge from our RPC. As a parameter, pass the public key of the wallet making the challenge.
  2. Sign Challenge: Sign the message received from the challenge request using the keypair passed during request. This proves you own the associated keypair.
  3. Submit Challenge: Send back the signed challenge message. If valid, this will return an authorization token that can be used to access the state.

We handle this flow using our SDK:

import {getAuthToken} from '@magicblock-labs/ephemeral-rollups-sdk';

const { publicKey, signMessage } = useWallet();
const token = await getAuthToken(PRIVATE_ER_URL, publicKey, signMessage);

Access

When creating a connection, simply pass along the Auth token you received from the challenge.

function useEphemeralConnection() {
 const { authToken } = usePrivateRollupAuth();
 const ephemeralConnection = useMemo(() => {
   if (authToken) {
     return new Connection(`${EPHEMERAL_RPC_URL}?token=${authToken}`, 'confirmed');
   }
   return null;
 }, [authToken]);

 return { ephemeralConnection };
}

Next Steps

If you’re interested in using the Private Ephemeral Rollup, please reach out. We’ll love to support your project in any way we can.