توضیح‌نامه‌ی ۰۵

چرخه‌ی کامل: از تماس NFC تا رای روی زنجیره

این متن یک کاربر، یک رای را، از ابتدا تا انتها ردیابی می‌کند. هر توضیح‌نامه‌ی دیگر یک برش را عمیق پوشش می‌دهد؛ این یکی کل خط لوله را پیمایش می‌کند و نشان می‌دهد هر تکه‌ی داده کجا زندگی می‌کند و کجا می‌میرد.

اگر فقط یک توضیح‌نامه می‌خوانید، این را دوم بخوانید (بعد از ).

سه مرحله

۱. ثبت‌نام

یک‌بار

۲. ورود به کیف پول

در هر نشست

۳. رای‌گیری

در هر پیشنهاد

ثبت‌نام سنگین است: اسکن NFC، اثبات ZK، نوشتن روی زنجیره. یک بار به ازای هر سند انجام می‌شود. ورود به کیف پول محلی است: PIN / بیومتریک، بدون تعامل با زنجیره. رای‌گیری متوسط‌سنگین است: یک اثبات «query» کوچک‌تر و یک تراکنش روی زنجیره به ازای هر برگه‌ی رای.

مرحله‌ی ۱: ثبت‌نام

گام ۱٫۱ — کاربر کیف پول جمهور را نصب می‌کند

برنامه‌ی کیف پول از App Store یا Play Store دانلود می‌شود. در اولین اجرا:

داده‌ی تولیدشده: کلید خصوصی کیف پول. فقط روی این دستگاه زندگی می‌کند، برای همیشه. داده‌ی فرستاده‌شده به خارج از دستگاه: کلید عمومی کیف پول (BabyJubjub {x, y}) + payload گواهی.

گام ۱٫۲ — کاربر MRZ را با دوربین اسکن می‌کند

کیف پول از دوربین دستگاه برای OCR ناحیه‌ی قابل‌خواندن ماشینی گذرنامه یا INID خود بهره می‌برد. این یک اسکن یک‌خطی متنی است؛ داده برای مشتق‌کردن کلید جلسه‌ی NFC نیاز است.

داده‌ی تولیدشده: رشته‌ی MRZ (شماره‌ی سند، تاریخ تولد، انقضا). عمر: در حافظه‌ی برنامه نگه‌داشته می‌شود، برای مشتق‌کردن کلیدهای BAC/PACE به‌کاربرده می‌شود، سپس دور انداخته می‌شود.

گام ۱٫۳ — کاربر سند را روی تلفن می‌گذارد (NFC)

کیف پول یک جلسه‌ی NFC امن با تراشه با استفاده از BAC (گذرنامه‌های قدیمی‌تر) یا PACE (گذرنامه‌های جدیدتر / INIDها) برقرار می‌کند. در این جلسه می‌خواند:

داده‌ی تولیدشده: کل دامپ تراشه. فقط در حافظه‌ی برنامه زندگی می‌کند؛ هرگز روی دیسک نوشته نمی‌شود، هرگز به بیرون از دستگاه فرستاده نمی‌شود.

گام ۱٫۴ — برنامه مدار ZK مناسب را انتخاب می‌کند

کیف پول گواهی Document Signer را بررسی می‌کند تا تعیین کند:

بر اساس آن، یک شناسه‌ی مدار مثل passport_rsa_2048_sha256_e65537 یا inid_rsa_2048 انتخاب می‌کند.

گام ۱٫۵ — تولید Witness

کیف پول داده‌ی تراشه + راز کاربر + ریشه‌ی فعلی ICAO را به WASM مدار انتخاب‌شده می‌دهد. یک ماژول بومی (witnesscalculator) witness را محاسبه می‌کند — کل فهرست مقادیر میانی که میلیون‌ها قید مدار را برآورده می‌کنند. این ۲۰ تا ۶۰ ثانیه طول می‌کشد.

داده‌ی تولیدشده: witness (~ ۱۰ تا ۱۰۰ مگابایت، در حافظه). پس از تولید اثبات دور انداخته می‌شود.

گام ۱٫۶ — تولید اثبات

کیف پول اثبات‌گر Groth16 (rapidsnark-wrp، ماژول بومی) را فراخوانی می‌کند. ورودی: witness + کلید اثبات مدار (~ صدها مگابایت، از پیش در برنامه بسته‌بندی شده). خروجی: یک اثبات ۲۵۶ بایتی و ~ ۲۴ سیگنال عمومی. ۳۰ تا ۹۰ ثانیه CPU تلفن، فن‌ها روشن، باتری تخلیه می‌شود.

داده‌ی تولیدشده: اثبات ۲۵۶ بایتی + سیگنال‌های عمومی (nullifier، ریشه‌ی ICAO، کد تابعیت، تاریخ‌ها). این تنها آرتیفکت‌هایی هستند که تلفن را ترک خواهند کرد.

گام ۱٫۷ — فرستادن به رله‌ی ثبت‌نام

کیف پول اثبات + سیگنال‌های عمومی را POST می‌کند به:

https://api.iranians.vote/integrations/registration-relayer/v1/register

رله یک سرویس Go نازک است. این:

  1. اثبات را به‌عنوان یک بررسی سلامت‌سنجی خارج از زنجیره راستی‌آزمایی می‌کند (ارزان).
  2. اثبات + سیگنال‌ها را در یک تراکنش راریمو L2 می‌پیچد.
  3. تراکنش را با کیف پول دارای موجودی خود امضا می‌کند (gas RMO).
  4. به RPC راریمو L2 منتشر می‌کند.
  5. هش تراکنش را به کیف پول برمی‌گرداند.

رله داده‌ی سند کاربر را نمی‌بیند — فقط اثبات + سیگنال‌های عمومی را. وجود آن برای پرداخت gas است، نه برای دروازه‌گذاری دسترسی.

داده‌ی تولیدشده روی زنجیره: یک ورودی در RegistrationSMT (درخت Merkle پراکنده از تعهدات هویت). کاربر اکنون ثبت‌نام شده. هش گواهی DS هم اگر قبلن نبود به CertificatesSMT اضافه می‌شود.

گام ۱٫۸ — برنامه یک علامت «ثبت‌نام‌شده» محلی می‌نویسد

کیف پول ذخیره می‌کند: تعهد هویت مشتق‌شده از راز کیف پول، هش تراکنش، و شناسه‌ی مدار استفاده‌شده. هیچ چیز درباره‌ی خود سند.

داده‌ی نابودشده در این گام: MRZ، DG1/DG2/DG13/DG15، SOD، گواهی‌ها، witness. همه در حافظه صفر می‌شوند.

مرحله‌ی ۱ کامل شد. کاربر اکنون می‌تواند وارد شود و رای دهد. هرگز نیاز نیست سند را دوباره اسکن کند، مگر این‌که برنامه را دوباره نصب کند یا کیف پولش را بچرخاند.

مرحله‌ی ۲: ورود به کیف پول

وقتی کاربر برنامه را باز می‌کند، کیف پول را با Face ID / Touch ID / PIN قفل‌گشایی می‌کند. سیستم‌عامل کلید خصوصی BabyJubjub را از ذخیره‌سازی امن رمزگشایی می‌کند؛ فقط برای مدت نشست در حافظه زندگی می‌کند.

برای SSO «ورود با جمهور» (که از سوی تراز، Civic-Compass، شرکای آینده به‌کاربرده می‌شود)، جریان این است:

  1. طرف متکی (مثلن تراز) /v1/authorize?client_id=…&code_challenge=… را باز می‌کند.
  2. sso-svc یک nonce برمی‌گرداند + از راه Universal Link به کیف پول هدایت می‌کند.
  3. کیف پول {nonce, client_id, app_attestation} را با کلید BabyJubjub خود امضا می‌کند.
  4. sso-svc امضا + گواهی را راستی‌آزمایی می‌کند، یک code یک‌بارمصرف صادر می‌کند.
  5. RP code + code_verifier را برای یک JWT دسترسی + بازنشانی مبادله می‌کند.
  6. sub JWT یک subject جفت‌مدار است — HMAC(secret, wallet_id : client_id) — بنابراین RPهای مختلف شناسه‌های متفاوتی برای همان کاربر می‌بینند. بدون همبستگی بین سرویس‌ها.

هیچ تعامل با زنجیره در این مرحله نیست. اعتماد از ثبت‌نام روی زنجیره‌ی مرحله‌ی ۱ ریشه می‌گیرد، اما خود پروتکل ورود کاملن خارج از زنجیره است.

مرحله‌ی ۳: رای‌گیری

گام ۳٫۱ — کاربر یک پیشنهاد را باز می‌کند

کیف پول پیشنهادهای فعال را از قرارداد رای‌گیری روی راریمو L2 می‌گیرد و نمایش می‌دهد. هر پیشنهاد دارای:

گام ۳٫۲ — کاربر یک گزینه را انتخاب می‌کند

کیف پول انتخاب را به‌صورت محلی به شناسه‌ی پیشنهاد گره می‌زند. هنوز امضایی نیست.

گام ۳٫۳ — تولید اثبات Query

این یک اثبات ZK کوچک‌تر و سریع‌تر از اثبات ثبت‌نام است. سند را دوباره اسکن نمی‌کند؛ از رازی که از قبل در کیف پول ذخیره شده بهره می‌برد.

اثبات می‌کند:

  1. «من در RegistrationSMT در ریشه‌ی R ثبت‌نام شده‌ام.»
  2. «هویت من سلکتور پیشنهاد را برآورده می‌کند» (تابعیت، سن، و غیره).
  3. «nullifier من برای event_id = proposal.id برابر N است.»
  4. «من به گزینه‌ی X رای می‌دهم.»

این اثبات حدود ۱۰ تا ۳۰ ثانیه روی تلفن طول می‌کشد. خروجی: ~ ۲۵۶ بایت + ~ ۲۳ سیگنال عمومی (مدار INID) یا ~ ۲۴ (مدار گذرنامه).

گام ۳٫۴ — فرستادن به رله‌ی رای‌گیری

https://api.iranians.vote/integrations/proof-verification-relayer/v3/vote

رله اثبات را در یک تراکنش راریمو L2 می‌پیچد، با کیف پول دارای موجودی خود امضا می‌کند، منتشر می‌کند. قرارداد رای‌گیری:

  1. precompile جفت‌سازی BN254 در نشانی 0x08 را فراخوانی می‌کند.
  2. بررسی می‌کند که nullifier استفاده نشده.
  3. شمارش رای برای گزینه‌ی X را افزایش می‌دهد.
  4. nullifier را به‌عنوان استفاده‌شده ثبت می‌کند.

اگر کاربر سعی کند دوباره رای دهد: قرارداد the key already exists برمی‌گرداند ← رله 400 Bad Request برمی‌گرداند ← کیف پول صفحه‌ی «قبلاً رای داده‌اید» را نشان می‌دهد.

گام ۳٫۵ — نتیجه

تراکنش در حدود ۲ ثانیه روی راریمو L2 ماین می‌شود. کاربر رسید خود را می‌بیند: هش تراکنش، رای ثبت‌شده، گزینه‌ی انتخاب‌شده.

مرحله‌ی ۳ برای این پیشنهاد کامل شد. کاربر می‌تواند مستقل به پیشنهاد بعدی رای دهد — همان کیف پول، nullifier متفاوت.

هر تکه‌ی داده کجا زندگی می‌کند، در یک جدول

دادهروی تلفن؟به خارج فرستاده می‌شود؟روی زنجیره؟عمر
کلید خصوصی کیف پول (BabyJubjub)✅ Secure enclave❌ هرگز❌ هرگزدائمی تا حذف برنامه
کلید عمومی کیف پول✅ در ثبت‌نامدائمی در DB sso-svc
رشته‌ی MRZ✅ در حافظهچند ثانیه (نشست NFC)
DG1، DG2، DG13، DG15✅ در حافظهتا تولید اثبات، سپس صفر
SOD + گواهی DS✅ در حافظهتا تولید اثبات، سپس صفر
witness ZK✅ در حافظهچند ثانیه، سپس صفر
اثبات ZK (۲۵۶ بایت)✅ مختصراً✅ به رله✅ بخشی از داده‌ی تراکنشدائمی روی زنجیره
سیگنال‌های عمومی✅ مختصراً✅ به رله✅ بخشی از داده‌ی تراکنشدائمی روی زنجیره
Nullifier✅ مختصراً✅ به رله✅ ثبت‌شده در قرارداددائمی روی زنجیره
هش گواهی DS✅ مختصراً✅ به رله✅ یک‌بار به ازای هر گواهی در CertificatesSMTدائمی روی زنجیره
تعهد هویت✅ مختصراً✅ به رله✅ یک‌بار در RegistrationSMTدائمی روی زنجیره
انتخاب رای (گزینه‌ی X)✅ مختصراً✅ درون اثبات✅ شمرده در قرارداددائمی روی زنجیره
subject جفت‌مدار (SSO)✅ در JWT به RPبه اندازه‌ی عمر JWT نشست

الگو ثابت است: داده‌ی سند هرگز دستگاه را ترک نمی‌کند؛ فقط اثبات رمزنگارانه و خروجی‌های عمومی اعلام‌شده‌ی آن می‌روند. هر چیزی روی زنجیره یا هش است، یا شمارش، یا یک تعهد تصادفی‌شده.

روایت گام‌به‌گام (نسخه‌ی غیرفنی)

برای زمانی که یک رای‌دهنده‌ی ایرانی در خارج می‌پرسد «وقتی گذرنامه‌ام را روی برنامه می‌گذارم چه می‌شود؟»، همان داستان بدون اصطلاحات فنی:

صفحه‌ی اول گذرنامه‌تان را با دوربین اسکن می‌کنید. گذرنامه را به تلفن می‌چسبانید — تراشه‌ی درون گذرنامه با تلفن شما از راه رادیوی برد کوتاه حرف می‌زند.

تلفن شما عکس، نام، تاریخ‌ها، و یک امضای دیجیتال کوچک را از تراشه می‌خواند — مدرکی از سازمان ثبت احوال ایران که سند واقعی است.

سپس تلفن کاری هوشمندانه می‌کند: یک رسید ریاضی می‌سازد که می‌گوید «من یک گذرنامه‌ی واقعی ایرانی دارم، بالای ۱۸ سال هستم، اسم و شماره‌ام اینجا ظاهر نمی‌شوند.» این رسید تقریباً به اندازه‌ی یک توییت است. داده‌ی گذرنامه‌ی شما سپس از تلفن پاک می‌شود.

رسید روی بلاک‌چین آپلود می‌شود — یک دفتر کل عمومی و مقاوم در برابر دستکاری که در ده‌ها کشور اجرا می‌شود. بلاک‌چین ریاضی را بررسی می‌کند، رسید را می‌پذیرد، و حالا شما ثبت‌نام شده‌اید: یک شهروند واقعی ایرانی، واجد شرایط رای، بدون نامی متصل.

وقتی یک رای‌گیری انجام می‌شود، یک رسید کوچک‌تر تولید می‌کنید: «من یک کاربر ثبت‌نام‌شده هستم، رای من بله است.» بلاک‌چین آن را می‌شمارد، شما را به‌عنوان رای‌داده روی این پرسش علامت می‌زند، و نمی‌توانید روی همان پرسش دو بار رای دهید.

هیچ‌کس نام شما را نمی‌داند. هیچ‌کس نمی‌تواند حساب شما را بگیرد. هیچ‌کس نمی‌تواند رای شما را حذف کند.

آن پاراگراف تنها نسخه‌ای از سیستم است که اکثر خوانندگان نیاز خواهند داشت. هر چیزی در این پوشه وجود دارد تا درست‌بودن آن را پشتیبانی کند.