<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jayanth Kumar</title>
    <description>The latest articles on DEV Community by Jayanth Kumar (@jayanthkumarmorem).</description>
    <link>https://dev.to/jayanthkumarmorem</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3870589%2Fc9ff904b-7fac-4e81-b0fe-15d45d2510d8.jpeg</url>
      <title>DEV Community: Jayanth Kumar</title>
      <link>https://dev.to/jayanthkumarmorem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jayanthkumarmorem"/>
    <language>en</language>
    <item>
      <title>I Made a Single CUDA Kernel Speak: Streaming Qwen3-TTS at 50ms Latency on an RTX 5090</title>
      <dc:creator>Jayanth Kumar</dc:creator>
      <pubDate>Thu, 09 Apr 2026 21:49:30 +0000</pubDate>
      <link>https://dev.to/jayanthkumarmorem/i-made-a-single-cuda-kernel-speak-streaming-qwen3-tts-at-50ms-latency-on-an-rtx-5090-53if</link>
      <guid>https://dev.to/jayanthkumarmorem/i-made-a-single-cuda-kernel-speak-streaming-qwen3-tts-at-50ms-latency-on-an-rtx-5090-53if</guid>
      <description>&lt;p&gt;My first measurement said &lt;strong&gt;35,932 milliseconds&lt;/strong&gt;. The &lt;strong&gt;target was 90.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's not a typo. &lt;strong&gt;Thirty-five &lt;em&gt;seconds&lt;/em&gt; to produce the first chunk of audio&lt;/strong&gt; from a text-to-speech system that was supposed to feel like a natural conversation. I was off by a factor of 400. And I had less than a day to fix it.&lt;/p&gt;

&lt;p&gt;Here's how I went from knowing nothing about CUDA megakernels, nothing about TTS pipelines, and nothing about Pipecat — to streaming real-time speech synthesis at 50ms TTFC and 0.17 RTF on a single RTX 5090. With 3 lines of kernel code changed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Self Challenge
&lt;/h2&gt;

&lt;p&gt;The self task was deceptively simple on paper: use &lt;a href="https://blog.alpindale.net/posts/5090_decode_optimization/" rel="noopener noreferrer"&gt;AlpinDale's qwen_megakernel&lt;/a&gt; , a ~1,200-line CUDA program that runs Qwen3-0.6B text generation at 1,000 tokens/second on an RTX 5090 and make it run &lt;a href="https://huggingface.co/Qwen/Qwen3-TTS" rel="noopener noreferrer"&gt;Qwen3-TTS&lt;/a&gt; speech synthesis inside a &lt;a href="https://docs.pipecat.ai" rel="noopener noreferrer"&gt;Pipecat&lt;/a&gt; voice agent pipeline.&lt;/p&gt;

&lt;p&gt;Two hard targets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TTFC (time to first audio chunk): &amp;lt; 90 ms&lt;/strong&gt; — how long before the user hears anything&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RTF (real-time factor): &amp;lt; 0.3&lt;/strong&gt; — generating 1 second of speech must take less than 300ms&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;And one non-negotiable constraint:&lt;/strong&gt; the audio must stream frame-by-frame to Pipecat. No buffering the full utterance. The user should start hearing the response almost immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'd never read a CUDA kernel before. I'd never built a TTS pipeline. I'd never used Pipecat. Let's go.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Even Is a Megakernel?
&lt;/h2&gt;

&lt;p&gt;Before I could adapt something, &lt;strong&gt;I needed to understand what it does.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Normally, when &lt;strong&gt;PyTorch runs a transformer model, it launches hundreds of separate GPU operations&lt;/strong&gt;, one for each matrix multiply, one for each attention computation, one for each normalization. &lt;strong&gt;Each launch has overhead&lt;/strong&gt;: the &lt;strong&gt;CPU tells the GPU what to do&lt;/strong&gt;, the &lt;strong&gt;GPU starts up, does the work, finishes, and waits for the next instruction.&lt;/strong&gt; For a 28-layer transformer, that's hundreds of round trips per token.&lt;/p&gt;

&lt;p&gt;A megakernel eliminates all of that. It packs the &lt;em&gt;entire&lt;/em&gt; forward pass, all 28 layers, all the matrix multiplies, all the attention, all the normalization, into a single GPU program. 128 persistent thread blocks, each with 512 threads, launched once and running continuously. Data flows through shared memory and L2 cache instead of being written to and read from global DRAM between operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result:&lt;/strong&gt; 1,000 tokens per second on a single RTX 5090. That's 0.97 milliseconds per decode step. The kernel hits 71% of the theoretical memory bandwidth of GDDR7. It's fast.&lt;/p&gt;

&lt;p&gt;But it was built for text generation — Qwen3-0.6B, vocabulary of 151,936 tokens, standard RoPE. I needed it to generate audio codes — Qwen3-TTS's talker decoder, vocabulary of 3,072, different RoPE frequency, and a completely different input/output format.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Lucky Break: Same Architecture, Different Vocabulary
&lt;/h2&gt;

&lt;p&gt;My first real breakthrough came from comparing the two model configs side by side. The Qwen3-TTS talker decoder is, architecturally, the &lt;em&gt;exact same model&lt;/em&gt; as Qwen3-0.6B. Same hidden dimension (1024). Same 28 layers. Same 16 query heads, 8 KV heads, head dimension 128, intermediate size 3072. The transformer backbone is identical.&lt;/p&gt;

&lt;p&gt;The differences were cosmetic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vocabulary&lt;/strong&gt;: 3,072 audio codes vs. 151,936 text tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RoPE frequency&lt;/strong&gt;: 1,000,000 vs. 10,000&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Output layer&lt;/strong&gt;: Separate weight matrix instead of tied with input embeddings&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This meant I didn't need to rewrite the kernel. I needed to change &lt;em&gt;parameters&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The vocabulary change was trivial — the kernel reads &lt;code&gt;LDG_VOCAB_SIZE&lt;/code&gt; as a compile-time constant. I created a new build script that passes &lt;code&gt;-DLDG_VOCAB_SIZE=3072&lt;/code&gt; instead of &lt;code&gt;151936&lt;/code&gt;, and reduced the LM head thread blocks from 1,280 to 16 (48x fewer tokens to scan). No kernel code changes.&lt;/p&gt;

&lt;p&gt;The RoPE frequency only affects precomputed tables in Python. No kernel changes.&lt;/p&gt;

&lt;p&gt;The untied output layer just meant loading a separate weight tensor. No kernel changes.&lt;/p&gt;

&lt;p&gt;So far: zero lines of CUDA modified.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 3-Line Patch That Made Everything Possible
&lt;/h2&gt;

&lt;p&gt;Here's where I needed to actually touch the kernel.&lt;/p&gt;

&lt;p&gt;In text generation, each decode step feeds the model a single token ID. The kernel looks up that token's embedding from a table. Simple.&lt;/p&gt;

&lt;p&gt;But in TTS, the input to each decode step is a &lt;em&gt;sum of 17 different embeddings&lt;/em&gt;: 16 codebook group embeddings from the previous audio frame, plus a trailing text token. There's no single token ID for a weighted sum of 17 vectors.&lt;/p&gt;

&lt;p&gt;I could have launched a separate GPU operation to compute this embedding before each decode step. But that adds launch overhead every single frame — and when you're generating 12.5 frames per second and each frame needs to be fast, those microseconds add up.&lt;/p&gt;

&lt;p&gt;Instead, I added an &lt;strong&gt;embedding sentinel&lt;/strong&gt; — 3 lines in &lt;code&gt;kernel.cu&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;__nv_bfloat16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;embed_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_token_id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;embed_weight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;input_token_id&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;HIDDEN_SIZE&lt;/span&gt;
                          &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hidden_buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;token_id &amp;gt;= 0&lt;/code&gt;: normal embedding lookup, exactly as before. When &lt;code&gt;token_id == -1&lt;/code&gt;: skip the lookup, read from a pre-filled buffer where Python has already written the combined embedding. No extra kernel launch. No synchronization. Fully backward-compatible.&lt;/p&gt;

&lt;p&gt;That's it. Three lines. The only CUDA code I changed in the entire project.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Discovery That Saved Everything: &lt;code&gt;num_layers&lt;/code&gt; Is Runtime
&lt;/h2&gt;

&lt;p&gt;This is the part of the story where I went from "this might work" to "this is going to crush the targets."&lt;/p&gt;

&lt;p&gt;Qwen3-TTS doesn't just have a talker decoder. After each decode step, a separate &lt;strong&gt;code predictor&lt;/strong&gt; — a smaller, 5-layer transformer — takes the talker's hidden state and expands it into 15 additional codebook groups. Each audio frame needs all 16 groups: 1 from the talker, 15 from the code predictor.&lt;/p&gt;

&lt;p&gt;My first code predictor implementation used standard PyTorch. It worked. It also took &lt;strong&gt;179 milliseconds per frame&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let me do the math on that. At 12.5 frames per second, generating 1 second of audio takes 179ms × 12.5 = 2,237ms. That's an RTF of 2.24 — &lt;strong&gt;seven and a half times over the target&lt;/strong&gt;. The code predictor alone made the entire project impossible.&lt;/p&gt;

&lt;p&gt;I started looking at torch.compile, CUDA graphs, custom attention kernels. Then I noticed something while re-reading the megakernel's main loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;num_layers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;num_layers&lt;/code&gt; isn't a constant. It's a runtime parameter. The kernel doesn't care if it's 28 or 5 or 1 — it just loops that many times.&lt;/p&gt;

&lt;p&gt;And the code predictor has the &lt;em&gt;exact same architecture&lt;/em&gt; as the talker — same hidden size, same attention heads, same everything. Just 5 layers instead of 28.&lt;/p&gt;

&lt;p&gt;What if I just... call the same kernel with &lt;code&gt;num_layers=5&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;I packed the code predictor's weights into the same struct format the kernel expects. Allocated a separate, smaller KV cache. Called the same compiled binary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;179ms → 10.9ms.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An 18x speedup. Zero kernel code changes. RTF dropped from 2.24 to 0.175. Target obliterated.&lt;/p&gt;

&lt;p&gt;This was the single most important insight of the entire project: the megakernel isn't just for the talker decoder. It's a &lt;strong&gt;general-purpose transformer accelerator&lt;/strong&gt; that you can reuse for any model with the same architecture, at any layer count, by just swapping the weights and adjusting one parameter.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 35-Second TTFC and the One-Word Fix
&lt;/h2&gt;

&lt;p&gt;With the kernel adapted and the code predictor running fast, I wired up the full pipeline: text tokenization → embedding projection → talker prefill → autoregressive decode → code predictor → vocoder → audio chunks. End to end.&lt;/p&gt;

&lt;p&gt;First measurement: &lt;strong&gt;TTFC = 35,932ms. RTF = 0.605.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The RTF I expected — the code predictor was still on PyTorch at that point. But 35 seconds for TTFC? The system was generating the &lt;em&gt;entire utterance&lt;/em&gt; before sending a single audio chunk.&lt;/p&gt;

&lt;p&gt;I looked at my frame generation function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_generate_codec_frames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;frames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_frames&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;frames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate_one_frame&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;frames&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a list. The streaming wrapper iterates over that list. But by the time it gets the list, all 2,048 frames have already been generated.&lt;/p&gt;

&lt;p&gt;The fix was one keyword:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_generate_codec_frames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_frames&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;generate_one_frame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;yield&lt;/code&gt; instead of &lt;code&gt;append&lt;/code&gt; + &lt;code&gt;return&lt;/code&gt;. A Python generator hands each frame to the consumer the moment it's produced. TTFC: &lt;strong&gt;35,932ms → 1,096ms.&lt;/strong&gt; A 33x improvement from changing one word.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chasing the Last 1,000 Milliseconds
&lt;/h2&gt;

&lt;p&gt;At 1,096ms, I could see the real bottleneck: the vocoder's first decode call took &lt;strong&gt;834ms&lt;/strong&gt;. CUDA is deeply lazy — the first time you run an operation, it allocates memory, compiles internal kernels, and sets up buffers. The second time, it's instant.&lt;/p&gt;

&lt;p&gt;I added warmup: three dummy vocoder decode calls during engine initialization, with different input sizes. Cold start: 834ms. After warmup: 38ms. &lt;strong&gt;TTFC: 1,096ms → 192ms.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Still over target. The code predictor's first call took 107ms instead of 13ms. Same story — but trickier. My warmup code ran with &lt;code&gt;do_sample=False&lt;/code&gt; (argmax — pick the most likely token). But the real pipeline uses sampling (randomly pick from top candidates). &lt;code&gt;torch.multinomial&lt;/code&gt;, &lt;code&gt;torch.softmax&lt;/code&gt;, and &lt;code&gt;torch.topk&lt;/code&gt; each have their own first-call overhead, independent of the argmax path.&lt;/p&gt;

&lt;p&gt;I added sampling warmup. &lt;strong&gt;TTFC: 192ms → 92ms.&lt;/strong&gt; Almost there.&lt;/p&gt;

&lt;p&gt;The last 12ms came from batching. I was computing text embeddings in 5 separate calls (role tokens, content tokens, special tokens...), each launching its own chain of GPU operations. Combining them into a single call cut embedding time from 14ms to 7ms. Then I precomputed every embedding that doesn't change between utterances — role tokens, TTS special tokens, codec tags — and cached them during initialization. Another 6ms saved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final TTFC: 50.5ms&lt;/strong&gt; (non-streaming pipeline test). &lt;strong&gt;81.6ms&lt;/strong&gt; with vocoder and streaming overhead.&lt;/p&gt;

&lt;p&gt;Target was 90ms. We got there with room to spare.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Prefill Format: Reading 1,800 Lines of Source Code to Find 3 Token IDs
&lt;/h2&gt;

&lt;p&gt;Not everything was about performance. I spent an embarrassing amount of time debugging poor audio quality before realizing the problem was in the &lt;em&gt;prefill format&lt;/em&gt; — the sequence of tokens that primes the decoder before it starts generating audio.&lt;/p&gt;

&lt;p&gt;The Qwen3-TTS decoder expects an 8-step conditioning sequence. Three of those steps use "thinking tokens" — special IDs (2155, 2156, 2157) that represent a compressed thinking phase. These aren't mentioned in the model card. They're not in the config file. They're not in any documentation.&lt;/p&gt;

&lt;p&gt;I found them by reading line 2136 of &lt;code&gt;modeling_qwen3_tts.py&lt;/code&gt; — the 1,800-line generation script that ships with the model. My initial implementation used padding tokens in those positions. The model generated audio, but it sounded off. Once I matched the exact official format, quality improved immediately.&lt;/p&gt;

&lt;p&gt;The lesson: when you're integrating with a specific model, the source code is the only reliable documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The One Thing I Couldn't Fix
&lt;/h2&gt;

&lt;p&gt;The talker decoder uses M-RoPE (Multimodal RoPE) — a variant of positional encoding that splits the 64 head-dimension pairs into three groups of [24, 20, 20], each potentially tracking a different position.&lt;/p&gt;

&lt;p&gt;The megakernel implements standard 1D RoPE. All pairs use the same position.&lt;/p&gt;

&lt;p&gt;Implementing M-RoPE would mean modifying the attention computation — the most performance-critical, most carefully tuned part of the entire kernel. AlpinDale spent months optimizing those lines. A subtle bug would silently corrupt every output.&lt;/p&gt;

&lt;p&gt;I chose to ship without it. For text-only TTS (no vision input), the three M-RoPE positions are identical anyway, so the immediate impact is minimal. The practical consequence: the model doesn't reliably emit an end-of-sequence token, so I use a word-count-based heuristic to estimate when to stop generating. It works well enough — "Hello, how are you?" produces 4 seconds of audio, a 52-word paragraph produces 42 seconds — but it's not perfect.&lt;/p&gt;

&lt;p&gt;I document this honestly as a known limitation, with a clear description of what would fix it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Final Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTFC (non-streaming)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 90 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TTFC (streaming + vocoder)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;81.6 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 90 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTF (non-streaming)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.175&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTF (streaming)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.234&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code predictor (megakernel)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10.9 ms/frame&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Talker decode&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1 ms/step&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;TTFC breakdown&lt;/strong&gt; (50.5ms):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tokenize: 2.3ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Embed text: 7.2ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prefill (8 megakernel steps): 24.9ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;First talker decode: 3.1ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;First code predictor: 13.0ms&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Per-frame cost&lt;/strong&gt; (~15ms for 80ms of audio):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Talker decode: ~1ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code predictor: ~11ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Embedding computation: ~1ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vocoder (amortized): ~2ms&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number measured with &lt;code&gt;torch.cuda.synchronize()&lt;/code&gt; barriers. No hand-waving.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Actually Changed in the Kernel
&lt;/h2&gt;

&lt;p&gt;Let me be precise:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 lines in&lt;/strong&gt; &lt;code&gt;kernel.cu&lt;/code&gt;: The embedding sentinel (&lt;code&gt;token_id &amp;lt; 0&lt;/code&gt; → read from buffer instead of embedding table).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 constants in the build script&lt;/strong&gt;: &lt;code&gt;LDG_VOCAB_SIZE=3072&lt;/code&gt; (was 151936), &lt;code&gt;LDG_LM_NUM_BLOCKS=16&lt;/code&gt; (was 1280).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0 lines for the biggest optimization&lt;/strong&gt;: The &lt;code&gt;num_layers&lt;/code&gt; runtime parameter was already there. I just called the same kernel with &lt;code&gt;num_layers=5&lt;/code&gt; for the code predictor.&lt;/p&gt;

&lt;p&gt;Everything else — weight loading, the TTS pipeline, the streaming engine, the Pipecat integration, the vocoder, the warmup strategy, the prefill format — was Python. About 4,500 lines of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pipecat Integration: The Clean Part
&lt;/h2&gt;

&lt;p&gt;With the engine producing streaming audio, plugging it into &lt;a href="https://docs.pipecat.ai" rel="noopener noreferrer"&gt;Pipecat&lt;/a&gt; was the cleanest part of the project. Pipecat has a well-designed TTS service interface: subclass &lt;code&gt;TTSService&lt;/code&gt;, implement &lt;code&gt;run_tts&lt;/code&gt; as an async generator that yields audio frames, and the framework handles routing, turn management, and interruptions.&lt;/p&gt;

&lt;p&gt;The full voice agent pipeline flows like a conversation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User speaks → Deepgram STT → OpenAI LLM → Megakernel TTS → User hears
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Audio streams frame-by-frame — the first chunk (80ms of audio) ships as soon as it's decoded, with subsequent chunks batching 10 frames (~800ms) for efficiency. There's also a text-only mode for testing without a microphone or API keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Optimization Journey, Visualized
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Metric          Start          →  End
──────────────────────────────────────────
TTFC            35,932 ms      →  50.5 ms      (711x improvement)
RTF             0.605          →  0.175         (3.5x improvement)
Code predictor  179 ms/frame   →  10.9 ms/frame (18x improvement)
Kernel changes  -              →  3 lines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 711x TTFC improvement came from five things stacking:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Generator streaming (&lt;code&gt;yield&lt;/code&gt; vs &lt;code&gt;return&lt;/code&gt;) — 33x&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vocoder warmup — 6x&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sampling path warmup — 2x&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Batched embedding — 1.1x&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Precomputed constants — 1.15x&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The RTF improvement came from one thing: running the code predictor through the megakernel instead of PyTorch. That single insight — noticing that &lt;code&gt;num_layers&lt;/code&gt; is runtime, not compile-time — was worth an 18x speedup.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Next
&lt;/h2&gt;

&lt;p&gt;Two things would make the biggest difference with more time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;M-RoPE in the kernel.&lt;/strong&gt; Modify the RoPE rotation in &lt;code&gt;ldg_attention&lt;/code&gt; to split the 64 head-dimension pairs into three groups with independent position counters. This would fix EOS detection, eliminate the frame limit heuristic, and likely improve audio quality. It's a surgical change — maybe 20 lines — but it touches the hottest path in the kernel, so it needs very careful testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token suppression.&lt;/strong&gt; The official Qwen3-TTS implementation suppresses tokens 2048-3071 (except EOS) during talker decode. Without this, the model can occasionally generate meaningless special tokens. Adding a suppression mask to the logits before argmax/sampling is straightforward in Python and would improve output consistency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;If you're doing inference engineering and this kind of work interests you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the kernel, even if you don't understand all of it.&lt;/strong&gt; I didn't understand every line of &lt;code&gt;kernel.cu&lt;/code&gt;. But I understood enough to notice that &lt;code&gt;num_layers&lt;/code&gt; was runtime, that the embedding lookup was a clean injection point, and that the RoPE tables were precomputed in Python. Those three observations drove every optimization in the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profile before you optimize, but also before you despair.&lt;/strong&gt; When I saw 35,932ms TTFC, I could have panicked. Instead I profiled, found that 99.7% of the time was spent generating frames that hadn't been yielded yet, and fixed it with one keyword. The real bottleneck was always hiding behind a bigger, more obvious one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boring optimizations compound.&lt;/strong&gt; Generator streaming, warmup routines, batched embeddings, precomputed constants — none of these are flashy. But 33x × 6x × 2x × 1.1x × 1.15x = 500x. The flashy optimization (megakernel code predictor) was "only" 18x. The boring ones, stacked, were 28x more impactful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship what works, document what doesn't.&lt;/strong&gt; I could have spent days trying to implement M-RoPE in the kernel. Instead, I shipped with a workaround, documented the limitation clearly, hit both performance targets, and moved on. Perfect is the enemy of shipped.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The full source code is at&lt;/em&gt; &lt;a href="https://github.com/jayanth-kumar-morem/qwen-megakernel-tts" rel="noopener noreferrer"&gt;&lt;em&gt;github.com/jayanth-kumar-morem/qwen-megakernel-tts&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. Built on&lt;/em&gt; &lt;a href="https://github.com/AlpinDale/qwen_megakernel" rel="noopener noreferrer"&gt;&lt;em&gt;AlpinDale's qwen_megakernel&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://huggingface.co/Qwen/Qwen3-TTS" rel="noopener noreferrer"&gt;&lt;em&gt;Qwen3-TTS&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, and&lt;/em&gt; &lt;a href="https://docs.pipecat.ai" rel="noopener noreferrer"&gt;&lt;em&gt;Pipecat&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nlp</category>
      <category>performance</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Hedgehog-Enabled Verifiable Instant Runoff Voting with Extreme Coercion Resistance on Solana</title>
      <dc:creator>Jayanth Kumar</dc:creator>
      <pubDate>Thu, 09 Apr 2026 21:48:33 +0000</pubDate>
      <link>https://dev.to/jayanthkumarmorem/hedgehog-enabled-verifiable-instant-runoff-voting-with-extreme-coercion-resistance-on-solana-38c6</link>
      <guid>https://dev.to/jayanthkumarmorem/hedgehog-enabled-verifiable-instant-runoff-voting-with-extreme-coercion-resistance-on-solana-38c6</guid>
      <description>&lt;h3&gt;
  
  
  &lt;strong&gt;Abstract&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Electronic voting systems deployed on public blockchains offer the promise of unprecedented transparency and verifiability, yet contemporary solutions often present a stark trade-off between the expressiveness of the supported voting schemes and the robustness of their protection against sophisticated coercion tactics. This paper introduces a novel, fully-decentralized electronic voting system architected on the Solana blockchain that, for the first time, synthesizes verifiable Instant Runoff Voting (IRV) with a cryptographically-enforced mechanism for "extreme coercion resistance." Our system extends a baseline zero-knowledge voting architecture by incorporating two pivotal innovations: (1) a matrix-based ballot representation and a universally verifiable shifting procedure for IRV tallying, which enables complex, multi-round election computations to be conducted with full integrity; and (2) a "hedgehog/nullification" protocol, which empowers voters or their designated agents to irrevocably and anonymously cancel a coerced vote by submitting a zero-knowledge proof of key ownership. We provide a detailed specification of the requisite modifications to the on-chain smart contract logic, the off-chain decentralized storage architecture, and the formal design of three new, highly-optimized Circom circuits for proving ballot well-formedness, verifying state transitions between IRV rounds, and executing vote nullification. A rigorous security analysis is presented, formally demonstrating ballot secrecy, end-to-end verifiability, and resilience against the extreme coercion model. Finally, we conduct a comprehensive performance analysis, illustrating how the strategic application of advanced Arithmetization-Oriented (AO) hashing primitives and the inherent parallelism of the Solana network render our expressive and secure voting system computationally practical for real-world deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Introduction&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The escalating global demand for accessible, transparent, and secure democratic mechanisms has catalyzed significant research and development into blockchain-based electronic voting solutions. Such systems hold the potential to deliver end-to-end (E2E) verifiability, a critical property wherein any external observer can mathematically confirm that the final election outcome was computed correctly from the set of valid ballots, all without compromising the fundamental right to voter privacy.&lt;/p&gt;

&lt;p&gt;While current zero-knowledge (ZK) voting platforms, exemplified by foundational systems like the "Cloak-minster Ballot," have demonstrated considerable success in achieving anonymity and verifiability for simple plurality elections through the sophisticated integration of ZK-SNARKs, smart contracts, and decentralized storage, they exhibit critical deficiencies in two areas essential for achieving broader democratic legitimacy and security.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited Expressiveness:&lt;/strong&gt; These systems are architecturally constrained to support only basic electoral models. They lack the inherent capability to handle more sophisticated schemes like Instant Runoff Voting (IRV). IRV is of paramount importance in modern democracies for its ability to minimize vote splitting and elect more broadly representative candidates, but its complex multi-round, vote-transfer logic is fundamentally incompatible with the simple additive tallying mechanisms found in existing platforms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vulnerability to Coercion:&lt;/strong&gt; Existing platforms lack robust mechanisms to protect voters from sophisticated forms of vote-buying and coercion. This vulnerability is particularly acute under the "extreme coercion" model, a realistic and potent threat scenario where an adversary successfully acquires all of a voter's secret cryptographic keys and can thereby compel them to cast a specific vote under duress.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This paper directly confronts these deficiencies by proposing a novel, authority-free e-voting architecture on the high-performance Solana blockchain. We make the following specific and substantial contributions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Synthesized Protocol for Expressive and Coercion-Resistant Voting:&lt;/strong&gt; We design and specify a complete, integrated protocol that combines verifiable IRV tallying with an unstoppable nullification mechanism, achieving a synthesis of features not previously seen in the existing academic literature.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Novel Cryptographic Constructions:&lt;/strong&gt; We formally specify three new zero-knowledge circuits essential for the system's operation: &lt;code&gt;P_IRV_WF&lt;/code&gt; for proving the well-formedness of a matrix-based IRV ballot, &lt;code&gt;P_Shift&lt;/code&gt; for verifying the state transition between successive IRV rounds, and &lt;code&gt;P_Nullify&lt;/code&gt; for enabling the privacy-preserving, anonymous nullification of a vote.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Concrete Solana-based Architecture:&lt;/strong&gt; We provide a detailed architectural blueprint, outlining the necessary modifications to the on-chain Anchor smart contracts and the off-chain IPFS data structures of the Cloak-minster Ballot system to support our advanced features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rigorous Security and Performance Analysis:&lt;/strong&gt; We furnish a formal security model and proofs for the system's core properties, including the critical guarantee of extreme coercion resistance. Furthermore, we analyze the computational costs and demonstrate the system's practical feasibility, highlighting significant performance enhancements achieved through the use of optimized Arithmetization-Oriented (AO) hashing primitives.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. Preliminaries&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Cryptographic Building Blocks&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Our system is constructed upon a robust foundation of well-established cryptographic primitives, each selected for its specific security and performance characteristics.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero-Knowledge SNARKs:&lt;/strong&gt; We leverage Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge (ZK-SNARKs), specifically the Groth16 proving system. This system is distinguished by its constant-size proofs, which do not grow with the complexity of the computation being proven, and its extremely fast verification times, making it ideal for on-chain applications. A SNARK protocol involves a prover, who generates a proof demonstrating knowledge of a secret witness for a given computation, and a verifier, who checks the proof's validity. The security of this process relies on a Common Reference String (CRS), a set of public parameters generated during a one-time, multi-party trusted setup ceremony, which produces the proving and verification keys for a specific arithmetic circuit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Elliptic Curve Cryptography &amp;amp; Pedersen Commitments:&lt;/strong&gt; Standard Elliptic Curve Cryptography (ECC) forms the basis of the digital signature schemes and public-private key pairs used for authentication and authorization throughout the system. We also make extensive use of Pedersen commitments. These commitments are homomorphic, allowing for computations on committed values, and more importantly, they offer perfect (information-theoretic) hiding. This means the commitment reveals absolutely no information about the committed value, a property that is crucial for achieving everlasting privacy, as even future computational breakthroughs cannot compromise the secrecy of past votes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Merkle Trees and Sparse Merkle Trees (SMTs):&lt;/strong&gt; Data integrity and efficient set membership proofs are managed using Merkle trees. Following the model of the baseline Cloak-minster system, standard Merkle trees are employed for proofs of inclusion (e.g., to prove that a voter's identity is part of the official registration roster). In contrast, Sparse Merkle Trees (SMTs) are used for proofs of non-inclusion. This is essential for the spent nullifier list, which prevents double-voting by allowing a voter to prove that their unique nullifier has &lt;em&gt;not&lt;/em&gt; yet been used.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Arithmetization-Oriented (AO) Hashing&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The performance of ZK-SNARKs is profoundly dependent on the efficiency of the cryptographic hash functions used within their arithmetic circuits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Need for ZK-Friendly Hashing:&lt;/strong&gt; Traditional hash functions like SHA-2, which are designed for optimal performance on bit-oriented CPU architectures, are prohibitively expensive to implement within arithmetic circuits defined over large prime fields. The process of "arithmetization"—translating a computation into a system of polynomial equations, such as a Rank-1 Constraint System (R1CS)—reveals that the dominant cost metric is the circuit's multiplicative complexity, i.e., the number of multiplication gates. The high multiplicative complexity of standard hashes results in enormous circuits, leading to impractically long proof generation times and high computational costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Poseidon:&lt;/strong&gt; To overcome this bottleneck, Arithmetization-Oriented (AO) hash functions were developed. The Cloak-minster system utilizes Poseidon, a widely adopted AO hash function built using a sponge construction. It is specifically designed to have a low number of multiplication gates when expressed as an arithmetic circuit, making it highly efficient for in-circuit operations such as Merkle tree construction and nullifier generation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Advanced AO Primitives (PGV-ELC):&lt;/strong&gt; While Poseidon offers significant efficiency gains, recent cryptographic research has yielded even more optimized primitives. The PGV-ELC mode, a blockcipher-based construction, demonstrates superior performance. When instantiated with an AO-friendly blockcipher like HADES (creating a construction we refer to as "POSEIDON-DM"), it can reduce the number of R1CS constraints by up to 50% and improve native Merkle tree construction time by up to 9x compared to the standard sponge-based Poseidon. This substantial performance gain is achieved by leveraging a more efficient modular design and optimizing for wider-arity Merkle trees. This paper leverages these advanced AO primitives to ensure the computational feasibility of our more complex circuits, particularly the state transition proof.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;The Solana Programming Model&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Our system is architected for deployment on the Solana blockchain, capitalizing on its unique high-performance characteristics.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anchor Framework:&lt;/strong&gt; We utilize the Anchor framework, a powerful development suite that significantly simplifies the creation of secure and efficient Solana programs (smart contracts). It provides a Rust-based domain-specific language that abstracts away much of the complexity of the Solana runtime, handling boilerplate for serialization, deserialization, and instruction processing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Key Concepts:&lt;/strong&gt; The architecture is built upon core Solana concepts, including &lt;strong&gt;Program Derived Addresses (PDAs)&lt;/strong&gt;. PDAs are special accounts that can be programmatically controlled, allowing a smart contract to sign for and own other accounts. This enables the creation of unique, deterministically addressable state for each election, managed entirely by the on-chain logic. The system operates within Solana's account model and is designed to be highly efficient with respect to transaction costs, which are measured in computational units.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Formal Definitions of Advanced Voting Concepts&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instant Runoff Voting (IRV):&lt;/strong&gt; As formally defined in related work, IRV is a ranked-choice voting system where voters rank candidates in order of preference. If no single candidate secures an absolute majority of first-preference votes, the candidate with the fewest first-preference votes is eliminated. The votes cast for the eliminated candidate are then transferred to the next-ranked preference indicated on those ballots. This iterative process of elimination and vote redistribution continues in rounds until one candidate achieves a majority of the remaining votes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extreme Coercion Resistance:&lt;/strong&gt; This is a strong, formally defined security property designed to model realistic and severe threats. It assumes an adversary can learn &lt;em&gt;all&lt;/em&gt; of a voter's secret keys and can observe all of their interactions with the voting system. The only capability the adversary is assumed to lack is access to a private, untappable communication channel between the voter and a trusted agent, or "hedgehog." A system possessing extreme coercion resistance must provide the voter with an unstoppable and unattributable mechanism to nullify a vote that was cast under duress, even after the fact.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. Architectural Foundation: An Analysis of the Cloak-minster Ballot&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To ground our proposed enhancements in a concrete context, we first conduct a thorough technical analysis of the "Cloak-minster Ballot," a representative baseline ZK-voting system that encapsulates the current state of the art for simple, private elections.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;System Overview&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://github.com/RippnerLabs/cloak-minster-ballot/" rel="noopener noreferrer"&gt;Cloak-minster Ballot&lt;/a&gt; is a decentralized voting platform architected across four distinct, interacting layers. The &lt;strong&gt;Frontend Layer&lt;/strong&gt;, built with the Next.js framework, provides the user interface for voter registration and ballot casting. The &lt;strong&gt;Blockchain Layer&lt;/strong&gt; employs a Solana smart contract, developed using the Anchor framework, to manage the on-chain election state and verify cryptographic proofs. The &lt;strong&gt;Storage Layer&lt;/strong&gt; leverages the InterPlanetary File System (IPFS) for scalable, decentralized, and content-addressed off-chain storage of large data sets, such as the complete lists of voter and spent nullifiers. Finally, the &lt;strong&gt;Zero-Knowledge Layer&lt;/strong&gt;, utilizing Circom for circuit definition and SnarkJS for proof generation, produces the cryptographic proofs that are the cornerstone of the system's privacy and integrity guarantees.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Component Deep Dive&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Election Lifecycle:&lt;/strong&gt; The system enforces a rigidly defined lifecycle managed by on-chain instructions. An administrator initiates the process by calling an instruction to create the election account, which is a Program Derived Address (PDA). The election then enters a registration phase, where instructions for registering voters and updating the registration Merkle root handle the enrollment of anonymous identities. A specific instruction closes the registration period and transitions the election to the voting phase. During this phase, the vote instruction processes incoming ballots. Finally, a concluding instruction closes the election and finalizes the results. These state transitions are strictly controlled by on-chain boolean flags (&lt;code&gt;is_registration_open&lt;/code&gt;, &lt;code&gt;is_voting_open&lt;/code&gt;) stored within the main &lt;code&gt;Election&lt;/code&gt; account data structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Nullifier Model:&lt;/strong&gt; The dual goals of anonymity and double-vote prevention are achieved through a sophisticated dual-nullifier scheme. During registration, each voter generates a unique &lt;code&gt;identity_nullifier&lt;/code&gt; by executing a specific ZK circuit. This nullifier is added to a Merkle tree of all registered voters, and the root of this tree is stored on-chain. When casting a vote, the voter must provide a ZK proof of membership in this registration tree. To prevent the same voter from casting multiple ballots, they also generate a &lt;code&gt;spent_nullifier&lt;/code&gt; (which is unique to the voter and the specific election). This spent nullifier is checked against a Sparse Merkle Tree (SMT) containing all previously spent nullifiers for that election. If the proof of non-membership in the SMT is valid, the vote is accepted, and the new spent nullifier is added to the tree, preventing its reuse.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Critical Evaluation and Research Gaps&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;While the &lt;a href="https://github.com/RippnerLabs/cloak-minster-ballot/" rel="noopener noreferrer"&gt;Cloak-minster&lt;/a&gt; system provides a robust and well-architected foundation for simple, private voting, it suffers from two significant and fundamental limitations that prevent its application in more demanding democratic contexts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; The architecture is cryptographically sound, providing strong E2E verifiability and anonymity for plurality elections. Its use of the high-performance Solana blockchain and the decentralized IPFS storage layer makes it both scalable and resilient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limitation 1: Lack of Expressiveness:&lt;/strong&gt; The system is fundamentally and architecturally designed for plurality voting. This is most evident in the on-chain data structure of the &lt;code&gt;Election&lt;/code&gt; account, which contains a simple array of big numbers (&lt;code&gt;tallies: BN&lt;/code&gt;) to count the votes for each option. This flat data structure is incapable of representing the ranked preferences required by IRV or handling the complex, multi-round vote transfer logic that is central to its tallying process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limitation 2: Lack of Coercion Resistance:&lt;/strong&gt; The system's security model does not address the threat of voter coercion. The cryptographic credential that authorizes a vote, referred to as a "voucher," bundles the Merkle proof of registration and the identity nullifier. An adversary who obtains a voter's secret key can generate this voucher and cast a vote on their behalf. The system possesses no mechanism to detect or remediate this form of attack, rendering it vulnerable under the extreme coercion threat model where such key theft is assumed to be a feasible attack vector. The design of this voucher system crystallizes the vulnerability; it becomes the single point of failure under coercion. An adversary's objective simplifies to controlling the generation or transmission of this single credential. Consequently, any robust defense cannot merely focus on protecting the secrecy of the inputs to the voucher; it must assume the voucher itself can be compromised. This critical insight directly motivates the introduction of a separate, uncoercible action that can be taken &lt;em&gt;after&lt;/em&gt; a coerced vote has been cast, which is precisely the role of the post-facto nullification mechanism facilitated by the out-of-band hedgehog model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. A Novel Architecture for Coercion-Resistant IRV&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To address the identified limitations of existing systems, we propose a new architecture that extends the Cloak-minster baseline. Our design introduces new on-chain state representations, new ZK circuits, and a modified election lifecycle to natively support both the expressive power of IRV and the robust security of the hedgehog/nullification mechanism for extreme coercion resistance.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Modified State Representation&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IRV Ballot Matrix:&lt;/strong&gt; The fundamental concept of a vote is redefined. Instead of representing a single choice, a voter's ballot is encoded as an encrypted n×n permutation matrix, where n is the number of candidates. In this matrix, each row corresponds to a preference level (1st, 2nd, etc.), and each column corresponds to a specific candidate. A '1' at position (i,j) signifies that candidate j is the voter's i-th preference. To manage on-chain costs and maintain scalability, this matrix is stored off-chain on IPFS, with only its cryptographic commitment being recorded on the Solana blockchain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;On-Chain State Modifications (Anchor):&lt;/strong&gt; The &lt;code&gt;Election&lt;/code&gt; account PDA, managed by the on-chain Anchor program, is extended with several new fields to handle the increased complexity of IRV and the nullification mechanism :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;election_type: ElectionType&lt;/code&gt;: An enum to differentiate between &lt;code&gt;Plurality&lt;/code&gt; and &lt;code&gt;IRV&lt;/code&gt; election types, allowing the contract to enforce the correct logic.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;current_round: u8&lt;/code&gt;: A counter to track the current round of the IRV tallying process.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;eliminated_candidates: Vec&amp;lt;PublicKey&amp;gt;&lt;/code&gt;: A dynamically sized list that stores the public keys of candidates who have been eliminated in previous rounds.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;round_tallies: Vec&amp;lt;Vec&amp;lt;BN&amp;gt;&amp;gt;&lt;/code&gt;: A nested vector that stores the vote tallies for each candidate in each round of the election.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;nullified_ballots: MerkleTree&lt;/code&gt;: A new on-chain Merkle tree root to immutably and verifiably track the identities of voters whose ballots have been nullified.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Voter Identity for Nullification:&lt;/strong&gt; To enable the hedgehog mechanism, the voter registration process is adapted from the Votexx model. Each voter is now required to generate and register two distinct public-private key pairs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;pk_vote&lt;/code&gt;: The public key used to authorize the submission of the voter's ballot.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;pk_nullify&lt;/code&gt;: The public key whose corresponding secret key, &lt;code&gt;sk_nullify&lt;/code&gt;, is required to authorize the nullification of the vote. This "opposite key" dynamic creates a crucial separation of concerns: one key is used for the act of voting, while the other, which can be securely shared with a trusted hedgehog agent, is reserved for remediation in the event of coercion.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;The Complete Election Lifecycle (Modified)&lt;/strong&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Election Initialization:&lt;/strong&gt; The instruction to initialize an election is updated to accept an &lt;code&gt;election_type&lt;/code&gt; parameter and to initialize the new IRV-specific state variables on the &lt;code&gt;Election&lt;/code&gt; account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Voter Registration:&lt;/strong&gt; The voter registration process is modified. A voter now commits to both &lt;code&gt;pk_vote&lt;/code&gt; and &lt;code&gt;pk_nullify&lt;/code&gt; on-chain, establishing a verifiable and unbreakable cryptographic link between their voting identity and their nullification identity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ballot Casting:&lt;/strong&gt; Voters generate their IRV ballot matrix, encrypt it, and then generate a ZK proof (&lt;code&gt;P_IRV_WF&lt;/code&gt;) demonstrating that the encrypted data corresponds to a valid permutation matrix. This proof, along with the commitment to the ballot, is submitted in a single transaction to the blockchain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IRV Tallying and Elimination Rounds:&lt;/strong&gt; This new phase is managed by a designated but fully verifiable tallying authority (e.g., a DAO or a set of decentralized trustees). It proceeds in discrete rounds:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*   **Tally Round:** The authority tallies the first-preference votes from the current set of active ballot matrices.

*   **Eliminate Candidate:** The candidate with the fewest votes is identified, and their public key is added to the on-chain `eliminated_candidates` list.

*   **Verifiable Shift:** This is the most cryptographically intensive and critical step. The authority executes the shifting procedure described in the Camel protocol for all active ballots, which effectively removes the eliminated candidate and promotes lower-ranked candidates on each ballot. To ensure verifiability without incurring prohibitive on-chain costs, the authority computes this global state transition off-chain and generates a single, comprehensive ZK proof (`P_Shift`). This proof, which attests to the correctness of the entire round's state transition, is then posted to and verified by the Solana smart contract. This process repeats until a candidate achieves a majority.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nullification Phase:&lt;/strong&gt; A dedicated time window, typically opened after voting closes but before the final tally is certified, is designated for nullification. During this phase, hedgehogs (or the voters themselves) can submit a nullification transaction. This transaction contains a ZK proof (&lt;code&gt;P_Nullify&lt;/code&gt;) of knowledge of a secret key &lt;code&gt;sk_nullify&lt;/code&gt; that corresponds to a registered voter. Upon successful on-chain verification of this proof, the voter's identity is added to the &lt;code&gt;nullified_ballots&lt;/code&gt; Merkle tree.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Finalization:&lt;/strong&gt; The final tally calculation is performed by the authority, which must discount any ballots cast by voters whose identities appear in the &lt;code&gt;nullified_ballots&lt;/code&gt; tree. The final, verified result is then published.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The verifiable shift mechanism is a cornerstone of this architecture's practicality. A naive implementation where each voter's ballot matrix is shifted and verified in a separate on-chain transaction would be computationally and financially infeasible due to the immense aggregate costs. The architectural decision to delegate the global state transition to a centralized-but-verifiable authority, who then proves the correctness of the entire operation with a single ZK-SNARK, is what makes the system viable. The state of all ballots can be represented by a single Merkle root of their commitments. The IRV round transition is thus a deterministic function: &lt;code&gt;(Old_State_Root, Eliminated_Candidate) -&amp;gt; New_State_Root&lt;/code&gt;. This entire function can be encoded into the &lt;code&gt;P_Shift&lt;/code&gt; circuit. While this proof is large and computationally intensive to &lt;em&gt;generate&lt;/em&gt; off-chain, it is constant-size and efficient to &lt;em&gt;verify&lt;/em&gt; on-chain, perfectly aligning with the ideal model for blockchain scalability.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;5. Core Cryptographic Protocols and ZK Circuits&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This section provides the detailed technical specifications for the three new zero-knowledge circuits that form the cryptographic core of our proposed system. These circuits are designed for implementation in a framework like Circom, which is compatible with the Groth16 proving system.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;P_IRV_WF&lt;/code&gt;: Proof of Well-Formed IRV Ballot
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; To allow a voter to prove that their submitted encrypted ballot corresponds to a valid n×n permutation matrix without revealing their ranked preferences. This protocol adapts the well-formedness proof from the Camel protocol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public Inputs:&lt;/strong&gt; A cryptographic commitment to the voter's ballot matrix.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Private Inputs:&lt;/strong&gt; The plaintext n×n ballot matrix Vk​, and the randomness used to generate the commitment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Circuit Logic:&lt;/strong&gt; The circuit enforces the two fundamental mathematical properties of a permutation matrix:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  &lt;strong&gt;Binary Constraint:&lt;/strong&gt; Each element (vij​)k​ in the matrix must be either 0 or 1. This is enforced within the arithmetic circuit with the constraint vij​⋅(vij​−1)=0 for all matrix elements i,j.

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Sum Constraint:&lt;/strong&gt; The sum of the elements in each row and each column must be exactly 1. This ensures that each preference rank is assigned to exactly one candidate, and each candidate is assigned exactly one preference rank, preventing invalid ballots.
&lt;/li&gt;
&lt;/ol&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;


&lt;code&gt;P_Shift&lt;/code&gt;: Proof of Correct Elimination Round
&lt;/h4&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; To enable the tallying authority to generate a single, verifiable proof that it correctly transitioned the global state of all ballots from one IRV round to the next by correctly applying the elimination and shifting logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public Inputs:&lt;/strong&gt; The Merkle root of all ballot commitments from round m−1, the public key of the candidate α(m−1) eliminated in that round, and the new Merkle root of ballot commitments for round m.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Private Inputs:&lt;/strong&gt; The complete set of plaintext ballot matrices from round m−1, along with their corresponding Merkle proofs of inclusion in the old state root.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Circuit Logic:&lt;/strong&gt; This is the most complex circuit in the system. Its logic can be deconstructed as follows:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  For each ballot in the set, the circuit first verifies its Merkle proof against the old state root.

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It then simulates the shifting procedure from the Camel protocol. It identifies the row corresponding to the eliminated candidate α(m−1) and computationally "removes" it, shifting all subsequent rows up by one position.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It computes a new cryptographic commitment to the newly formed ballot matrix for round m.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, it uses all the new ballot commitments to compute the new global Merkle root and asserts that this computed root matches the public input root for round m.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;code&gt;P_Nullify&lt;/code&gt;: Proof of Nullification Authority&lt;br&gt;
&lt;/h4&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; To allow a hedgehog agent to anonymously prove they possess the authority to nullify a specific voter's ballot, without revealing which voter they are acting for. This protocol adapts concepts from Votexx (proof of key ownership) and linkable ring signatures (anonymous proofs of set membership).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public Inputs:&lt;/strong&gt; The public key of the voter whose ballot is being nullified (&lt;code&gt;pk_vote&lt;/code&gt;), and the Merkle root of all registered nullification keys (&lt;code&gt;pk_nullify&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Private Inputs:&lt;/strong&gt; The secret nullification key &lt;code&gt;sk_nullify&lt;/code&gt;, and the Merkle proof demonstrating that the corresponding &lt;code&gt;pk_nullify&lt;/code&gt; is part of the registered set of nullification keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Circuit Logic:&lt;/strong&gt; The circuit performs three critical cryptographic checks:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  &lt;strong&gt;Membership Verification:&lt;/strong&gt; It takes the public &lt;code&gt;pk_nullify&lt;/code&gt; (derived from the private &lt;code&gt;sk_nullify&lt;/code&gt;) and the private Merkle proof, and verifies the proof against the public Merkle root of the nullification key roster.

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Key Ownership:&lt;/strong&gt; It performs the elliptic curve scalar multiplication inside the circuit (pknullify​=sknullify​⋅G) to derive the public key from the private key and asserts that it matches the public key used in the membership proof.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linkage Verification:&lt;/strong&gt; It verifies the cryptographic link between &lt;code&gt;pk_vote&lt;/code&gt; and &lt;code&gt;pk_nullify&lt;/code&gt; that was established and committed to during the registration phase. This ensures that the key being used for nullification is legitimately authorized to act on behalf of the key that cast the original vote.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;strong&gt;Table: Cryptographic Primitive Mapping&lt;/strong&gt;&lt;br&gt;
&lt;/h4&gt;


&lt;p&gt;The following table maps the high-level concepts from the source research to their concrete implementations in our proposed system, demonstrating the synthesis of these ideas.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;colgroup&gt;
&lt;col&gt;
&lt;col&gt;
&lt;col&gt;
&lt;col&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;System Component&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Core Concept&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Source Paper&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Proposed Implementation&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Ballot Structure&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Matrix-based IRV Ballot&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Camel&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Encrypted n×n permutation matrix stored on IPFS, with its commitment stored on-chain.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;IRV Round Logic&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Verifiable Shifting&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Camel&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;P_Shift&lt;/code&gt; ZK circuit proving correct global state transition between rounds.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Coercion Resistance&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Hedgehog/Nullification&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Votexx&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;P_Nullify&lt;/code&gt; ZK circuit proving knowledge of &lt;code&gt;sk_nullify&lt;/code&gt; for anonymous nullification.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;ZK Optimization&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Arithmetization-Oriented Hashing&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Replace Poseidon with the more efficient POSEIDON-DM (HADES in PGV-ELC mode) in all circuits to reduce constraints.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Privacy Model&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Everlasting Privacy&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Use perfectly hiding Pedersen commitments for the public bulletin board, keeping computationally secure ElGamal ciphertexts private to the tallying authority.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;General Compliance&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Polynomial Consistency Checks&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;CONAN&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;(Future Work) A framework for extending the system beyond IRV to other complex voting rules&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;6. Security and Performance Analysis&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Security Guarantees&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verifiability:&lt;/strong&gt; The system achieves robust end-to-end verifiability. The integrity of the entire election is anchored to the public bulletin board on the Solana blockchain. Any external observer can download the &lt;code&gt;P_Shift&lt;/code&gt; proof for each IRV round and independently verify it, confirming that the tallying process was executed precisely according to the specified rules. Similarly, the &lt;code&gt;nullified_ballots&lt;/code&gt; Merkle root provides a transparent, immutable, and verifiable record of all discounted votes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ballot Privacy:&lt;/strong&gt; Ballot secrecy is computational, relying on the hardness of the discrete logarithm problem for the ElGamal encryption scheme and the zero-knowledge property of the SNARKs. To elevate this guarantee to &lt;em&gt;everlasting privacy&lt;/em&gt;, we adopt the model proposed by Pointcheval , which makes a critical distinction between public and private data. The public bulletin board would only store perfectly hiding commitments (e.g., Pedersen commitments) of the ballots. The computationally secure ElGamal ciphertexts, which are needed for the homomorphic tally, would be shared privately with the tallying authority and can be securely deleted after the election. This ensures that even a future break in the encryption scheme (e.g., due to quantum computers) would not compromise the privacy of past votes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extreme Coercion Resistance:&lt;/strong&gt; The system provides demonstrable resistance against the extreme coercion model. A formal security argument, following the Universal Composability (UC) framework utilized in the Votexx paper , can be constructed. The security of the mechanism hinges on the core assumption of an untappable channel between the voter and their hedgehog, which exists outside the blockchain protocol itself. The protocol's role is to ensure that once a hedgehog is activated through this external channel, their nullification action on the blockchain is unstoppable, irrevocable, and anonymous. An adversary who knows both &lt;code&gt;sk_vote&lt;/code&gt; and &lt;code&gt;sk_nullify&lt;/code&gt; can cast a vote but cannot prevent the hedgehog from nullifying it, as they cannot block the external communication channel or forge a nullification proof for a different voter without the correct secret key.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Performance Analysis and Optimization&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Constraint Complexity:&lt;/strong&gt; The asymptotic R1CS constraint complexity of the new circuits is a key performance indicator for ZK-proof generation time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;P_IRV_WF&lt;/code&gt;: The complexity is dominated by the row and column sum checks, resulting in O(n2) constraints for an n-candidate election.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;P_Shift&lt;/code&gt;: This is the largest and most computationally intensive circuit. Its complexity is approximately O(N⋅n2), where N is the number of voters, as it must process every ballot in the state transition.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;P_Nullify&lt;/code&gt;: The complexity is dominated by the Merkle proof verification, resulting in O(logR) constraints, where R is the total number of registered voters.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Impact of AO Hashing:&lt;/strong&gt; The use of optimized AO hashing is critical for making the &lt;code&gt;P_Shift&lt;/code&gt; circuit practical. Based on the performance analysis in , replacing a standard sponge-based Poseidon with a more efficient primitive like POSEIDON-DM can reduce the number of constraints in the hashing-intensive parts of the circuit by up to 50%. For a large, complex circuit like &lt;code&gt;P_Shift&lt;/code&gt;, where Merkle roots are repeatedly computed, this could translate into an overall proof generation time reduction of 20-30% or more.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Solana Transaction Costs:&lt;/strong&gt; The on-chain costs are primarily driven by data storage and proof verification.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Voter Transactions:&lt;/strong&gt; Casting a ballot involves storing a commitment and verifying the &lt;code&gt;P_IRV_WF&lt;/code&gt; proof, which is a relatively low-cost transaction.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Authority Transactions:&lt;/strong&gt; The most expensive on-chain operations are performed by the tallying authority (posting the &lt;code&gt;P_Shift&lt;/code&gt; proof for each IRV round) and by hedgehogs (submitting &lt;code&gt;P_Nullify&lt;/code&gt; proofs). The cost is dominated by the on-chain verification of the Groth16 proofs, which, while efficient, still consumes computational resources.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Parallelism:&lt;/strong&gt; Solana's Sealevel architecture, which allows for the parallel processing of non-overlapping transactions, is highly advantageous for the nullification phase. A large number of hedgehogs can submit their nullification proofs simultaneously, and the network can process them in parallel, preventing a potential bottleneck and ensuring the system remains responsive even under high load.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;7. Conclusion and Future Work&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Summary of Contributions&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;We have presented the complete and detailed design for a novel electronic voting system that significantly advances the state of the art by simultaneously providing both expressive voting capabilities (Instant Runoff Voting) and robust security against extreme coercion. By synthesizing and extending concepts from multiple leading research papers—including matrix-based ballots from the Camel protocol, the hedgehog/nullification model from Votexx, and performance optimizations from Arithmetization-Oriented hashing research—and grounding them in a concrete, high-performance architecture on the Solana blockchain, we have demonstrated a clear and practical path toward more secure, flexible, and democratic online elections. Our work serves to bridge the critical gap between theoretical cryptographic possibilities and practical, deployable systems ready for real-world application.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Future Work&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generalized Compliance (CONAN):&lt;/strong&gt; The IRV matrix representation is a specific method for enforcing a complex voting rule. A promising direction for future work is to generalize this framework to support a wider range of rules. By incorporating the polynomial-based consistency checks and decoy-term methods from the CONAN protocol , the system could be transformed into a generic "verifiable compliance engine." This would allow it to support a diverse array of voting schemes, such as quadratic voting or Condorcet methods, by simply defining a new compliance predicate circuit, thereby making the platform significantly more versatile.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decentralized Tallying Authority:&lt;/strong&gt; The current model relies on a designated, albeit fully verifiable, tallying authority to execute the IRV rounds. A significant step towards complete decentralization would be to distribute this role among a network of nodes that collaboratively compute the round transitions and the &lt;code&gt;P_Shift&lt;/code&gt; proof using secure Multi-Party Computation (MPC) protocols.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-Quantum Security:&lt;/strong&gt; The system, as currently designed, relies on elliptic curve cryptography, which is known to be vulnerable to attacks from large-scale quantum computers. A critical avenue for future research is to investigate and integrate post-quantum cryptographic primitives. This would involve replacing the current signatures, commitments, and SNARKs with lattice-based alternatives, such as the blind signatures proposed in , to ensure the long-term security and enduring viability of the voting system in a post-quantum world.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>solana</category>
      <category>zk</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
