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 Tests

Pre-Alpha Disclaimer: This is a pre-alpha release for development and testing only. Signing uses a single mock signer, not real distributed MPC. All 11 protocol operations are implemented (DKG, Sign, Presign, FutureSign, ReEncryptShare, etc.) across all 4 curves and 7 signature schemes, but without real MPC security guarantees. The dWallet keys, trust model, and signing protocol are not final; do not rely on any 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 Ika 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.

Overview

E2E tests run the full dWallet lifecycle against Solana devnet and the pre-alpha dWallet gRPC service. This tests the complete flow including on-chain program execution, CPI, and signing.

Prerequisites

ResourceEndpoint
dWallet gRPChttps://pre-alpha-dev-1.ika.ika-network.net:443
Solana RPChttps://api.devnet.solana.com

Deploy your program to devnet, then run:

cargo run -p e2e-voting -- <DWALLET_PROGRAM_ID> <VOTING_PROGRAM_ID>

Override endpoints via environment variables:

RPC_URL=https://api.devnet.solana.com \
GRPC_URL=pre-alpha-dev-1.ika.ika-network.net:443 \
cargo run -p e2e-voting -- <DWALLET_PROGRAM_ID> <VOTING_PROGRAM_ID>

E2E Flow

The voting E2E demo performs 7 steps:

Step 1: Wait for Program Initialization

The mock signer creates:

  • DWalletCoordinator PDA (["dwallet_coordinator"]) – 116 bytes
  • NetworkEncryptionKey PDA (["network_encryption_key", noa_pubkey]) – 164 bytes
#![allow(unused)]
fn main() {
let (coordinator_pda, _) =
    Pubkey::find_program_address(&[b"dwallet_coordinator"], &dwallet_program_id);

poll_until(&client, &coordinator_pda, |data| {
    data.len() >= 116 && data[0] == 1 // DISC_COORDINATOR
}, Duration::from_secs(30));
}

Step 2: Create dWallet

The NOA commits a dWallet via CommitDWallet (discriminator 31):

The dWallet PDA seeds are ["dwallet", chunks_of(curve_byte ‖ public_key)] — concatenate the curve byte with the public key into a single buffer, then split into 32-byte chunks (Solana’s MAX_SEED_LEN) and pass each chunk as its own seed. For 32-byte pubkeys (Curve25519/Ristretto/Ed25519) the payload is 33 bytes → chunks [32, 1]; for 33-byte compressed SEC1 pubkeys (Secp256k1/r1) it is 34 bytes → chunks [32, 2].

#![allow(unused)]
fn main() {
let mut payload = Vec::with_capacity(1 + public_key.len());
payload.push(curve);
payload.extend_from_slice(&public_key);

let mut seeds: Vec<&[u8]> = Vec::with_capacity(4);
seeds.push(b"dwallet");
for chunk in payload.chunks(32) {
    seeds.push(chunk);
}

let (dwallet_pda, dwallet_bump) =
    Pubkey::find_program_address(&seeds, &dwallet_program_id);
}

Step 3: Transfer Authority

Transfer dWallet authority to the voting program’s CPI PDA:

#![allow(unused)]
fn main() {
let (cpi_authority, _) = Pubkey::find_program_address(
    &[b"__ika_cpi_authority"],
    &voting_program_id,
);
}

Step 4: Create Proposal

Create a proposal with quorum = 3:

#![allow(unused)]
fn main() {
let message = b"Transfer 100 USDC to treasury";
let message_hash = keccak256(message);
}

Step 5: Cast 3 Votes

Three voters (Alice, Bob, Charlie) each cast YES. Charlie’s vote reaches quorum and triggers the approve_message CPI.

The last vote transaction includes 10 accounts (5 base + 5 CPI accounts).

Step 6: Verify MessageApproval

#![allow(unused)]
fn main() {
let ma_data = client.get_account(&message_approval_pda)?.data;
assert_eq!(ma_data[0], 14);  // discriminator
assert_eq!(ma_data[139], 0); // status = Pending
}

Step 7: Sign and Verify

The mock signer signs the message and calls CommitSignature (discriminator 43):

#![allow(unused)]
fn main() {
let signed_data = client.get_account(&message_approval_pda)?.data;
let sig_len = u16::from_le_bytes(signed_data[140..142].try_into().unwrap()) as usize;
let signature = &signed_data[142..142 + sig_len];
}

Key PDA Seeds

PDASeedsProgram
DWalletCoordinator["dwallet_coordinator"]dWallet
NetworkEncryptionKey["network_encryption_key", noa_pubkey]dWallet
DWallet["dwallet", chunks_of(curve ‖ public_key)] (32-byte chunks)dWallet
MessageApproval["message_approval", dwallet, message_hash]dWallet
CPI Authority["__ika_cpi_authority"]Your program
Proposal["proposal", proposal_id]Voting
VoteRecord["vote", proposal_id, voter]Voting

Key Discriminators

InstructionDiscriminator
CreateProposal (voting)0
CastVote (voting)1
ApproveMessage (dWallet)8
TransferOwnership (dWallet)24
CommitDWallet (dWallet)31
CommitSignature (dWallet)43

Running the E2E Test

cargo run -p e2e-voting -- <DWALLET_PROGRAM_ID> <VOTING_PROGRAM_ID>

Expected output:

=== dWallet Voting E2E Demo ===

[Setup] Funding payer...
  > Payer: <pubkey>
[1/7] Creating dWallet via CommitDWallet...
  > dWallet created: <pubkey>
[2/7] Transferring dWallet authority to voting program...
  > Authority transferred to CPI PDA: <pubkey>
[3/7] Creating voting proposal (quorum=3)...
  > Proposal: <pubkey>
[4/7] Vote 1/3: Alice casts YES...
[4/7] Vote 2/3: Bob casts YES...
[4/7] Vote 3/3: Charlie casts YES...
  > Proposal approved (yes_votes=3)
[5/7] Verifying MessageApproval on-chain...
  > MessageApproval: <pubkey>
[6/7] Signing message with NOA key and committing on-chain...
  > Signature committed on-chain!
[7/7] Reading signature from MessageApproval...
  > Signature: <hex>

=== E2E Test Passed! ===