Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

E2E Confidential Voting Demo

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.

Run a full confidential voting scenario against the Encrypt pre-alpha on Solana devnet.

Prerequisites

  • Rust toolchain (edition 2024)
  • just command runner
  • bun (for TypeScript demos)
  • Solana CLI (for airdrop)

Install JS dependencies:

bun install

Quick Start

The demos connect to the pre-alpha executor on devnet automatically:

just demo-web3 <ENCRYPT_PROGRAM_ID> <VOTING_PROGRAM_ID>

Available Demos

CommandSDKFile
just demo-web3 <ENC> <VOTE>@solana/web3.js (v1)e2e-voting-web3.ts
just demo-kit <ENC> <VOTE>@solana/kit (v2)e2e-voting-kit.ts
just demo-gill <ENC> <VOTE>gille2e-voting-gill.ts
just demo-rust <ENC> <VOTE>solana-sdk (Rust)e2e-voting-rust/
just demo <ENC> <VOTE>All four sequentially

What the Demo Does

  1. Create proposal — initializes encrypted yes/no tally counters (both start at 0)
  2. Cast 5 votes — 3 YES + 2 NO, each as an encrypted boolean via execute_graph
  3. Close proposal — authority locks voting
  4. Request decryption — asks executor to decrypt the tally ciphertexts
  5. Reveal results — reads decrypted values on-chain

Expected output:

═══ Results ═══

  → Total votes: 5
  → Yes votes: 3
  → No votes: 2

  Proposal PASSED (3 yes / 2 no)

How It Works

The demo script acts as a user client only — it encrypts values client-side, submits them via gRPC, and sends on-chain transactions. The pre-alpha executor running on devnet handles everything else:

  1. Script encrypts vote value and submits via gRPC CreateInput → executor verifies proof + creates ciphertext on-chain → returns account address
  2. Script sends cast_vote on voting program (CPI to execute_graph)
  3. Executor detects GraphExecuted event, evaluates the graph, and calls commit_ciphertext
  4. Script sends request_decryption on voting program (CPI to request_decryption)
  5. Executor detects DecryptionRequested event, decrypts, and calls respond_decryption

No authority keypair needed — the client never touches the executor’s keys.

Client SDK Usage

Rust:

#![allow(unused)]
fn main() {
let mut encrypt = EncryptClient::connect().await?;
let vote_ct = encrypt.create_input::<Bool>(true, &program_id, &network_key).await?;
}

TypeScript:

const encrypt = createEncryptClient(); // connects to pre-alpha endpoint
const { ciphertextIdentifiers } = await encrypt.createInput({
  chain: Chain.Solana,
  inputs: [{ ciphertextBytes: mockCiphertext(1n), fheType: 0 }],
  authorized: programId.toBytes(),
  networkEncryptionPublicKey: networkKey,
});

Reading Ciphertexts Off-Chain

Rust:

#![allow(unused)]
fn main() {
let result = client.read_ciphertext(&ct_pubkey, &reencryption_key, epoch, &keypair).await?;
// result.value = plaintext bytes (mock) or re-encrypted ct (production)
}

TypeScript:

import { encodeReadCiphertextMessage } from "@encrypt.xyz/pre-alpha-solana-client/grpc";

const msg = encodeReadCiphertextMessage(Chain.Solana, ctId, new Uint8Array(), 1n);
const result = await encrypt.readCiphertext({
  message: msg,
  signature: Buffer.alloc(64), // not needed for public ciphertexts
  signer: Buffer.alloc(32),
});

For private ciphertexts, sign the BCS message with your ed25519 keypair.

Troubleshooting

Airdrop failed — Solana devnet faucet may be rate-limited. Wait a few seconds and retry, or fund the keypair manually.

Transaction simulation failed — the program may have been wiped (pre-alpha data is reset periodically). Check the program ID is still deployed.

Timeout waiting for executor — the pre-alpha executor may be restarting. Wait a minute and retry.