Rand Protocol
مستندات
مفاهیم اصلی

Mempool

کاری که یک گره با یک تراکنش از لحظهٔ دریافت تا قرار گرفتن در بلاک انجام می‌دهد: ترتیب پذیرش، ردیابی تعارض به‌جای nonce، چهار کارگر تأیید، سنجهٔ همتا و حافظهٔ نهان حکم‌ها، شیوهٔ انتخاب بلاک توسط پیشنهاددهنده، و دلیل ناپدید شدن خودبه‌خودی یک خرج مضاعف بازنده.

از این صفحه برای فهمیدن این استفاده کنید که پس از پذیرش یک تراکنش توسط rand_sendTransaction و پیش از آنکه بلاکی آن را حمل کند، چه بر سر آن می‌آید: اگر رد شده، چرا رد شده؛ چرا ممکن است بی‌آنکه هرگز شکست بخورد از استخر ناپدید شود؛ یک بلاک به چه ترتیبی پر می‌شود؛ و سیلی از اثبات‌های نامعتبر چه کاری می‌تواند و چه کاری نمی‌تواند با یک اعتبارسنج بکند. ممپول در crates/randprotocol-node/src/mempool.rs است؛ نگهبان‌های پیش از آن در admission.rs قرار دارند.

فرستنده‌ای نیست، پس نانسی هم نیست

در یک زنجیرهٔ مبتنی بر حساب، ممپول تراکنش‌های هر فرستنده را بر اساس نانس مرتب می‌کند. یک زنجیرهٔ shielded فرستنده ندارد: یک بسته نه نشانی‌ای دارد و نه امضایی، و دو بسته تنها زمانی به هم مرتبط می‌شوند که به یک نوت واحد دست بزنند. بنابراین ترتیب‌دهی به‌ازای هر فرستنده‌ای باقی نمی‌ماند. آنچه جای حسابداری نانس را می‌گیرد ردیابی تعارض است: استخر نال‌فایرها و تعهدهای هر تراکنش در انتظار را نمایه می‌کند و تازه‌واردی را که هر یک از آن‌ها را مدعی شود رد می‌کند. همین قاعده دربارهٔ چند شمارندهٔ اندکی هم صادق است که دفتر کل به‌ازای هر مقدارشان فقط یک تراکنش می‌پذیرد: نانس ثبت یک اعتبارسنج (برای Unbond یا Withdraw)، mint_nonce یک توکن (مشترک میان TokenMint و SetAuthority)، اندیس ثبتی که یک RegisterToken به آن مقید است، و pause_nonce و list_nonce پل. هر کدام جایگاهی است که حداکثر یک تراکنش در استخر می‌تواند آن را در اختیار داشته باشد. از دو تراکنش از این دست، حداکثر یکی می‌تواند وارد بلاک شود، و حمل دیگری هم از فضای بلاک پیشنهاددهنده می‌کاهد و هم در هر دور شایعه‌پراکنی یک راستی‌آزمایی اثبات بر دوش هر گره می‌گذارد.

استخر حداکثر ۱۰٬۰۰۰ تراکنش را نگه می‌دارد (Mempool::new(10_000) در node.rs) و، از v0.6.8، حداکثر بایت‌های معادل هشت بلاک: MAX_POOL_BLOCKS = ۸ برابر max_block_bytes زنجیره، یعنی ۱۶۰ MiB در زنجیرهٔ ۲۰ (ممیزی v6، CH-7). پیش از آن فقط تعداد محدود بود، در حالی که یک تراکنش حاوی اثبات از حدود ۱٫۳ MB تا بیش از ۱۲ MB حجم دارد. هر تراکنش در برابر دفتر کل در نوک درخت اجماع اعتبارسنجی می‌شود، نه آخرین بلاک نهایی‌شده: وضعیتی که یک پیشنهاددهنده در واقع روی آن بنا می‌کند.

پذیرش، ارزان پیش از گران

تراکنشی که از راه RPC یا شایعه‌پراکنی می‌رسد به ترتیب از این گام‌ها می‌گذرد، و نخستین ردّ همان خطایی است که می‌بینید:

  1. تکراری. هش پیش‌تر در استخر است: already in mempool.
  2. تعارض. یک نال‌فایر، یک تعهد یا یکی از جایگاه‌های نانس و اندیس بالا پیش‌تر توسط یک تراکنش در انتظار مدعی شده است: conflicts with a pending transaction over <hex>. برای جایگاه توکن، مقدار hex یک کلید نمایشی است: یک واژهٔ برچسب (tokn برای mint_nonce، tidx برای اندیس ثبت)، سپس اندیس و نانس. گواهی پلی که دو رله‌گر بر سر آن رقابت کرده‌اند با چکیده‌اش تشخیص داده می‌شود: conflicts with a pending transaction over bridge attestation <hash>.
  3. پر بودن. mempool full. بررسی ظرفیت آخرین بررسی از این سه است. در سقف بایت، تازه‌وارد می‌تواند جای ورودی‌های درون استخر را بگیرد که اکیداً از او پایین‌ترند — نخست بر اساس کارمزدِ بالاتر از کف فعلی هر یک به‌ازای هر KiB، سپس بر اساس کارمزد — و ارزان‌ترین‌ها را اول بیرون می‌اندازد تا جا شود؛ اگر این کافی نباشد، یا فقط برابر باشد، خودِ او رد می‌شود. اقدامات حاکمیتی بیرون از هر دو سقف‌اند و هرگز بیرون انداخته نمی‌شوند: چهار اقدام حاکمیتی پل یعنی PauseMints، UnpauseMints، RegisterBridgedToken، ListBacking، و از v0.6.8 RotatePqGuardians، RotatePauseKey، شکل‌های …V2 آن‌ها، CancelRotation، AdmitValidator و SlashEquivocation. بایت‌ها و ورودی‌های آن‌ها همچنان در سقف‌ها شمرده می‌شود. PauseMints ترمز لازم برای زمان لو رفتن کلید نگهبان است: بدون بسته، بدون کارمزد، و تنها تراکنشی که باید در حالی که مهاجم در حال mint است به بلاک برسد، پس نباید استخرِ پر آن را بیرون نگه دارد. این معافیت با گام تعارض محدود می‌شود: pause_nonce و list_nonce هر کدام در هر زمان فقط یک تراکنش در استخر می‌پذیرند، بنابراین چهار اقدام پل حداکثر می‌توانند دو ورودی فراتر از سقف اضافه کنند؛ بقیه هر کدام یک نانس یا حد نصاب مختص خود را مصرف می‌کنند.
  4. نیمهٔ ارزان Ledger::validate. هر آنچه میان پذیرش و گنجاندن می‌تواند کهنه شود و اثباتی نمی‌خواهد: لنگر یکی از ۱٬۰۲۴ ریشهٔ آخر در زنجیرهٔ ۲۰ است (proof_window_blocks در genesis؛ بدون آن ۲۵۶)، time درون پنجره است، نال‌فایرها خرج نشده‌اند، تعهدها وجود ندارند، چکیدهٔ یک گواهی مدعی نشده است، نانس ثبت یک اقدام staking جاری است، mint_nonce یا اندیس ثبت یک اقدام توکن همانی است که رجیستری در آن قرار دارد (wrong mint nonce و wrong token index وقتی زنجیره از آن گذشته باشد)، و نانس یک توقف یا یک فهرست‌شدن همان نانس جاری پل است. تعهدهایی که یک تراکنش می‌سازد نیز همین‌جا محاسبه می‌شوند، از جمله نوتی که خود دفتر کل برای یک Withdraw، یک BridgeAttest یا یک پرداخت aggregator استخراج می‌کند و روی سیم نیست اما آن هم نباید تصادم کند. سقف‌های اندازه پیش از رمزگشایی هر چیزی بررسی می‌شوند: اثباتی بزرگ‌تر از max_proof_bytes زنجیره (به‌طور پیش‌فرض ۲ MiB، ۴ MiB در زنجیرهٔ ۲۰)، گواهی‌ای بزرگ‌تر از ۱۶ KiB، یا تراکنشی بزرگ‌تر از یک بلاک (max_block_bytes: به‌طور پیش‌فرض ۴ MiB، ۲۰ MiB در زنجیرهٔ ۲۰) یکسره رد می‌شود، پس یک blob بیش‌ازحد بزرگ هیچ کاری نمی‌خرد. اثبات یک بسته به شکل نشانگر مُهرشده (هرس‌شده) نیز در همین گام، بیرون از همگام‌سازی به شکل مُهرشده، با PrunedFormOutsideSync رد می‌شود. سپس، همچنان بدون راستی‌آزمایی چیزی، استخر سرآیندهای اثبات را می‌خواند. سه سیاست نخست می‌آیند: فراخوانی‌ای که جدول ورودی، keccak یا sha256 آن کمتر از 2^7 سطر دارد در استخر پذیرفته نمی‌شود (CallRevealsPrivateInputs)، همچنین استقراری که هیچ فراخوانی نتواند آن را نگه دارد (ProgramUncallable)، و تراکنشی که اثبات بسته یا فراخوانی‌اش فیلدی در سرآیند دارد که اثبات‌کنندهٔ درستکار نمی‌نوشت (NonCanonicalProof). دو مورد آخر در زنجیرهٔ ۲۰، زیر hardening_v6، قاعدهٔ دفتر کل نیز هستند. از v0.6.8، سرآیندی که درجهٔ اعلام‌شده‌اش از سقف بلندترین جدول فراتر برود، پیش از آنکه بررسی آن را پیمایش کند با NonCanonicalProof رد می‌شود (ممیزی v6، ZKV-4)، و هر panic در هر نقطه از این پیش‌غربال رد شدن همان تراکنش است (VerifierPanicked)، نه مرگ گره. سپس کف کارمزد: در زنجیرهٔ ۲۰ استخر حد گاز اعلام‌شده در اثبات یک call یا invoke را رمزگشایی می‌کند و کارمزدی کمتر از BUNDLE_BASE + gas_price × limit + byte_price × ⌈bytes/1024⌉ را به قیمت‌های نوک، به‌علاوهٔ کارمزد سلولی یک invoke، با FeeTooLow رد می‌کند (کارمزدها). این قاعدهٔ خود بلاک است، پس استخر دقیقاً همان چیزی را می‌طلبد که بلاک می‌طلبد.
  5. نیمهٔ گران، بیرون از حلقهٔ اجماع. تراکنش در صف قرار می‌گیرد، حداکثر ۶۴ ردیف عمق، برای یکی از چهار کارگر راستی‌آزمایی. یک کارگر باقی Ledger::validate را روی یک وظیفهٔ مسدودکننده اجرا می‌کند: چکیدهٔ بسته، حد گازی که اثبات بسته اعلام می‌کند (در زنجیرهٔ ۲۰ دقیقاً ۲۰٬۴۷۹، وگرنه BundleGasLimit)، اثبات بسته و، از زنجیرهٔ ۱۷، اثبات مجوزدهی آن در برابر binding تراکنش، و برای یک call اثبات خودش و کف کارمزدش. اعتبارسنجی در برابر یک اسنپ‌شات از نوک انجام می‌شود که به‌صورت تنبل کلون می‌شود، حداکثر یک بار به‌ازای هر تغییر نوک و فقط تا وقتی چیزی منتظر است. تراکنش شایعه‌شده‌ای که صف را پر ببیند، به‌جای مسدود کردن حلقه‌ای که رأی می‌دهد، دور انداخته می‌شود، نه راستی‌آزمایی می‌شود و نه ارسال می‌شود؛ دور بعدی شایعه‌پراکنی همتا دوباره آن را می‌آورد.
  6. بازبررسی و ورود به استخر. insert_verified گام ۴ را دوباره در برابر نوکی اجرا می‌کند که تراکنش واقعاً روی آن در استخر قرار می‌گیرد، چون نوک ممکن است در حین راستی‌آزمایی اثبات جابه‌جا شده باشد، سپس آن را می‌پذیرد و، برای ترافیک شایعه‌شده، ارسال می‌کند.

گام‌های ۱ تا ۴ همان Mempool::precheck هستند؛ گام ۵ تنها گامی است که یک STARK را راستی‌آزمایی می‌کند. چون این هزینه روی یک کارگر پرداخت می‌شود، سیلی از اثبات‌های نامعتبر چهار نخ را مشغول می‌کند و هرگز حلقهٔ اجماع را. تراکنش‌ها و اقدامات ترتیب پذیرش خود دفتر کل را فهرست می‌کند که گام‌های ۴ و ۵ روی هم آن را اجرا می‌کنند.

دو نگهبان و یک حافظهٔ نهان برای شایعه‌پراکنی

پیش از پذیرش، دو چیز فقط دربارهٔ تراکنش‌هایی که از همتاها می‌رسند اعمال می‌شود؛ ارسال‌های RPC از آن‌ها معافند، چون آن درگاه مختص خود اپراتور است و پیش‌تر با حد بدنهٔ درخواست محدود شده است. تنها مسیر RPC که سنجیده می‌شود faucet است، در ادامه.

  • شمارندهٔ بایت برای هر همتا. از نسخهٔ v0.6.8، هر فریم گاسیپ پیش از رمزگشایی به بودجهٔ بایتی ارسال‌کننده‌اش منظور می‌شود. این بودجه یک انفجار (burst) هشت‌فریمی دارد و ثانیه‌ای دو فریم با بیشینهٔ اندازهٔ انتقال پر می‌شود. فریمی که از این حد بگذرد، رمزگشایی‌نشده دور انداخته می‌شود (audit v6, GOSSIP-1). شناسهٔ تراکنش که از کل اثبات هش می‌گیرد، فقط در محدودهٔ سهمیهٔ ارسال‌کننده محاسبه می‌شود.
  • شمارندهٔ تراکنش برای هر همتا. یک سطل توکن (token bucket) برای هر همتای ارسال‌کننده، با انفجار 16 و پرشدن 4 تراکنش در ثانیه (PEER_TX_BURST، PEER_TX_PER_SEC). این شمارنده همتایی را که تراکنش را ارسال کرده می‌سنجد، نه نویسندهٔ آن را؛ زنجیرهٔ shielded نمی‌تواند نویسنده را مشخص کند.
  • حافظهٔ نهان احکام. تراکنشی که این گره پیش‌تر به دلیلی رد کرده که تنها تابعی از بایت‌های خودش است، مانند اثبات نامعتبر، امضای نامعتبر یا بخشی بزرگ‌تر از حد مجاز، از یک حافظهٔ نهان FIFO با 8,192 ورودی (REFUSED_CACHE_ENTRIES) دوباره رد می‌شود و بار دوم راستی‌آزمایی نمی‌شود. حکمی که دربارهٔ وضعیت این گره چیزی می‌گوید، مثل لنگر (anchor) ناشناخته یا nullifier خرج‌شده، هرگز در حافظهٔ نهان نمی‌ماند. در غیر این صورت، گرهی که یک بلوک عقب است خودش را در برابر تراکنش‌هایی که به‌زودی معتبر می‌شوند مسموم می‌کرد. این فهرست یک فهرست مجاز (allowlist) است، پس هر خطای تازه تا وقتی کسی خلافش را تصمیم نگرفته، «وضعیت» به حساب می‌آید. این ورودی‌ها نشان می‌دهند مرز کجاست:
    • BadPauseSignature (the pause signature does not verify) از v0.6.8 به بعد دائمی نیست (audit v6, BRG-18). این خطا امضا را با کلید توقف فعلی زنجیره می‌سنجد که قواعد پل نسخهٔ 2 می‌توانند آن را بچرخانند؛ پس وضعیت است. در غیر این صورت گرهی که یک بلوک پشت یک چرخش مانده، PauseMints دارندهٔ جدید را تا راه‌اندازی مجدد رد می‌کرد. اندیس خارج از بازهٔ هم‌امضای PQ هم به همین دلیل از حافظهٔ نهان بیرون رفت. تا v0.6.7، در زنجیره‌های 18 و 19، هر دو به‌عنوان دائمی ذخیره می‌شدند. اکنون هر توقف نامعتبر تکراری برای هر بار تحویل یک راستی‌آزمایی Dilithium2 هزینه دارد که شمارندهٔ هر همتا آن را محدود می‌کند.
    • PrunedFormOutsideSync عمداً دائمی نیست. شکل نشانه‌گذاری‌شدهٔ یک بستهٔ مهرشده به شناسهٔ خود تراکنش خام هش می‌شود و حافظهٔ نهان با همان شناسه کلید می‌خورد؛ پس ذخیرهٔ این رد، به هر همتای گاسیپ اجازه می‌داد تراکنش درست را در سراسر شبکه سانسور کند.
    • سقف گس اعلام‌شدهٔ یک اثبات، بایت‌های خودش در برابر یک ثابت genesis است؛ پس BundleGasLimit و AuthGasLimit دائمی هستند. FeeTooLow دائمی نیست: در زنجیرهٔ 20 کف کارمزد با قیمت‌ها جابه‌جا می‌شود. سه سیاست سرآیند در گام 4 نیز هرگز در حافظهٔ نهان نمی‌مانند: هر کدام دانش همین ساخت است و ساخت بعدی ممکن است آن را جابه‌جا کند.
    • در زنجیره‌ای با فهرست مجاز faucet (زنجیرهٔ 20)، FaucetRecipientNotAllowed دائمی است: گیرندهٔ خود mint را با یک ثابت genesis مقایسه می‌کند. FaucetBudgetExhausted دائمی نیست: وضعیت است و همان mint در epoch بعدی دوباره پذیرفته می‌شود.
    • BadSignature در یک کنش توکن هم، برخلاف کنش staking، دائمی نیست: مرجع mint یک توکن وضعیت است، یک SetAuthority می‌تواند آن را به کلید دیگری بدهد، و همان بایت‌ها ممکن است پیش از ثبت آن چرخش رد شوند و پس از آن پذیرفته شوند. ردهای سطح بایتِ توکن (نام یا نماد نامعتبر، اعشار بیش از حد، مقدار صفر، اندیس توکن 0) در حافظهٔ نهان می‌مانند؛ همچنین است فیلد burn که کنش اجازهٔ تنظیم آن را ندارد، mint که تعهدش (commitment) به مقدارش باز نمی‌شود، و سپردهٔ پل که blinding آن، blinding مشتق‌شده نیست.

محدودیت faucet

rand_mint بدون کارمزد است و هر فراخوانی یک تراکنش در استخر هزینه دارد، و در زنجیرهٔ 20 faucet روشن است و پلی که ارزش پشتوانهٔ آن را نگه می‌دارد پشت آن قرار دارد. genesis کاری را که faucet می‌تواند بکند محدود می‌کند: یک mint فقط به 16 کیف پول مجاز می‌پردازد و حداکثر 10,000 RAND در هر epoch با 1,000 بلوک (Staking)، و فقط یکی از 18 کلید فهرست staking.faucet_minters در genesis می‌تواند آن را امضا کند (MinterNotAllowed، هرگز در حافظهٔ نهان نمی‌ماند: از زنجیرهٔ 16 قاعدهٔ دفتر کل است و در زنجیرهٔ 15 سیاست گره بود، که فهرست همان validatorهای genesis بود). افزون بر این، هر گره faucet خودش را می‌سنجد: هشت mint پشت‌سرهم، با پرشدن یکی در ثانیه (FAUCET_MINT_BURST، FAUCET_MINT_PER_SEC). بعد از آن فراخوانی چنین پاسخ می‌دهد: faucet is rate limited on this node (8 mints back to back, refilling at 1/s); try again shortly. این محدودیت برای هر فرایند گره است، نه برای هر فراخوانی‌کننده، چون درگاه RPC هویت همتا ندارد که بتوان سنجید و منبع حفاظت‌شده استخر همین گره است. همراه با استثنای ظرفیت در بالا، سیل faucet نمی‌تواند استخر را پیش از یک PauseMints پر کند.

Gossipsub با اعتبارسنجی در سطح برنامه اجرا می‌شود؛ پس یک تراکنش فقط پس از آنکه اینجا راستی‌آزمایی شد به همتاهای دیگر ارسال می‌شود. همتایی که تراکنش‌های نامعتبر سرازیر می‌کند، با شمارنده کند می‌شود، از حافظهٔ نهان پاسخ می‌گیرد و حلقهٔ اجماع را هیچ هزینه‌ای نمی‌دهد.

ساخت یک بلوک

وقتی یک validator رهبری یک view را بر عهده می‌گیرد، از استخر نامزدها را می‌خواهد. candidates_within فقط تراکنش‌هایی را نگه می‌دارد که هنوز بر دفتر کلی که روی آن بنا می‌کند اعمال می‌شوند، و نیمهٔ ارزان پذیرش را دوباره اجرا می‌کند: لنگر هنوز در پنجره باشد، time هنوز در آن باشد، nullifierها هنوز خرج نشده باشند، commitmentها هنوز وجود نداشته باشند. همچنین کنش توکنی را که زنجیره از mint_nonce یا اندیس ثبت آن گذشته، و توقف یا فهرست‌گذاری با nonce کهنه را کنار می‌گذارد. از v0.6.8 هر call و invoke را با قیمت‌های جاری در نوک زنجیره‌ای که روی آن بنا می‌کند و از سقف گسی که هنگام پذیرش به خاطر سپرده شده دوباره قیمت‌گذاری می‌کند، و آن را که کارمزدش اکنون زیر کفش است کنار می‌گذارد؛ آن تراکنش در استخر می‌ماند و وقتی قیمت‌ها پایین بیایند دوباره پیشنهاد می‌شود (audit v6, CH-9). در زنجیرهٔ 20 سپس بقیه را اول حاکمیت، بعد بر اساس کارمزد بالای کف جاری به‌ازای هر KiB تراکنش، بعد بر اساس کارمزد از بیشترین، و در تساوی بر اساس هش مرتب می‌کند، تا هر پیشنهاددهندهٔ درست‌کار که روی همان استخر بنا می‌کند همان بلوک را بردارد، و تا سقف 2,000 تراکنش و سقف بلوک زنجیره پر می‌کند (MAX_BLOCK_TXS؛ max_block_bytes، به‌طور پیش‌فرض 4 MiB و در زنجیرهٔ 20 برابر 20 MiB). آنچه بلوک کم دارد بایت است، و به همین دلیل مازاد به‌ازای هر KiB گرفته می‌شود. زنجیره‌ای بدون بخش gas و گره‌ای بدون سیاست گس این کلید را رد می‌کنند و بر اساس حاکمیت، کارمزد و هش مرتب می‌کنند، همان‌طور که زنجیرهٔ 15 می‌کرد. اول‌بودن حاکمیت نیمهٔ دیگر تضمین توقف است: PauseMints طبق طراحی کارمزد نمی‌دهد، چون باید از کیف پولی کار کند که RAND ندارد؛ پس ترتیب ساده بر اساس کارمزد، ترمز اضطراری را پشت هر تراکنشی که می‌پردازد قرار می‌داد.

یک قاعدهٔ دیگر هنگامی اعمال می‌شود که یک call یا invoke آماده است (audit v6, POOL-2؛ سیاست گره در هر زنجیره، نه قاعدهٔ اعتبار). فقط آن‌ها قیمت بایت می‌پردازند، و انتقالی که کمی بیش از پایهٔ ثابتش می‌پردازد در هر KiB از آن‌ها پیشی می‌گیرد؛ پس جریانی از انتقال‌ها می‌توانست یک call آماده را از هر بلوک دور نگه دارد. بنابراین تراکنش‌های با کارمزد ثابت در یک گذر نخست حداکثر سه‌چهارم بایت‌های بلوک را می‌گیرند؛ پس از جاگذاری callها، جای باقی‌مانده با انتقال‌های نگه‌داشته‌شده، به ترتیب خودشان، پر می‌شود. فضای بلوک هدر نمی‌رود، و وقتی هیچ call آماده نباشد چیزی تغییر نمی‌کند (Fees).

تراکنشی که برای فضای باقی‌مانده بیش از حد بزرگ است کنار گذاشته می‌شود، نه پایان بلوک: تراکنش کوچک‌تری که پشت آن است همچنان می‌رود. یک انتقال در زنجیرهٔ 18 حدود 2.85 MB است، شامل یک اثبات بسته 1.49 MB و یک اثبات احراز 1.36 MB؛ پس بلوک 20 MiB آن جای حدود هفت انتقال دارد و بلوک پیش‌فرض 4 MiB جای یکی. در زنجیرهٔ 15، با حدود 1.43 MB برای هر انتقال، همان 20 MiB چهارده انتقال جا می‌داد. زیر بار، کارمزد است که تعیین می‌کند کدام‌ها (Fees).

هرس

پس از هر commit، prune دقیقاً آنچه را candidates_within کنار می‌گذاشت از استخر حذف می‌کند: تراکنش‌هایی که نوتشان را شخص دیگری خرج کرده، commitmentشان اکنون وجود دارد، nonce یا اندیس ثبتشان را زنجیره پشت سر گذاشته، یا لنگر یا timeشان از پنجره بیرون رفته است (1,024 بلوک در زنجیرهٔ 20، و 256 تا زنجیرهٔ 19). بنابراین بسته‌ای که در رقابت double-spend می‌بازد خودبه‌خود ناپدید می‌شود، بی‌آنکه برای کسی خطایی تولید کند. پذیرفته‌شدن توسط rand_sendTransaction به معنای commit نیست: rand_getTransaction را تا وقتی بلوکی برگرداند بپرسید، و اگر پنجرهٔ لنگر بدون آن بگذرد، بسته را روی لنگر تازه دوباره بسازید و دوباره ارسال کنید.

زیر نظر گرفتن

rand_status مقدارهای mempool_size، verify_queue (تراکنش‌هایی که منتظر یک worker هستند) و refused_cache (ورودی‌های حافظهٔ نهان احکام) را گزارش می‌دهد. verify_queue که روی 64 بماند یعنی اثبات‌ها سریع‌تر از آنچه چهار worker راستی‌آزمایی می‌کنند می‌رسند، حدود 16 ms برای هر کدام با کلید گرم؛ و mempool_size که با پر ماندن بلوک‌ها رشد کند یعنی کارمزد، نه ترتیب رسیدن، شمول را تعیین می‌کند.

هنوز چه چیزی نیست

  • بدون جایگزینی. قیمت‌های gas و بایت در chain 20 کف هزینهٔ یک فراخوانی را در هر بلاک، تا سقفشان، جابه‌جا می‌کنند؛ کف هزینهٔ یک انتقال ثابت است. تراکنشی که در استخر قرار دارد را نمی‌توان با تراکنشی با کارمزد بالاتر روی همان note‌ها جایگزین کرد، زیرا قاعدهٔ تعارض آن را رد می‌کند. فراخوانی‌ای که قیمتش از دسترس خارج شده است، بدون آنکه پیشنهاد شود، در استخر می‌ماند تا قیمت‌ها پایین بیایند یا پنجرهٔ anchor آن بگذرد. در v0.6.7 (chain 18 و 19) فراخوانی موجود در استخر همان کفی را که هنگام پذیرش داشت نگه می‌داشت، پس از افزایش قیمت هم همچنان پیشنهاد می‌شد و در اجرای آزمایشی پیشنهاددهنده با FeeTooLow شکست می‌خورد. سند فضای بلاک در fullnode، mempool مرتب‌شده بر اساس کارمزد را گام پیش از بلاک بزرگ‌تر، و تجمیع در سطح بلاک را عاملی می‌داند که سقف هر بلاک را بالا می‌برد.
  • بدون انقضا بر اساس سن. تراکنش موجود در استخر فقط با درج در بلاک، با حذف در سقف بایت، یا با پنجره‌های anchor و زمان از استخر خارج می‌شود: ۱٬۰۲۴ بلاک در chain 20، یعنی حدود ۲۳ دقیقه با ۱٫۳۴ ثانیه برای هر بلاک که explorer در آن شبکه گزارش داد. تا chain 19 پنجره‌ها ۲۵۶ بلاک، یعنی حدود پنج دقیقه، بود.
  • بدون RPC برای mempool. هیچ متدی برای فهرست‌کردن تراکنش‌های در انتظار وجود ندارد؛ mempool_size در rand_status و تعداد، بایت و قدیمی‌ترین سن در rand_getMempoolInfo تنها نماهای موجودند و explorer فقط بلاک‌های نهایی‌شده را نمایه می‌کند.

آخرین بار با کامیت c0878c3f از fullnode (زنجیرهٔ 21) در تاریخ 2026-10-12، و fullnode v0.6.8 `c9c9bd3c` (chain 20): `crates/randprotocol-node/src/mempool.rs` (`MAX_POOL_BLOCKS`, `make_room`, `is_governance`, the call reservation in `candidates_within`), the commits for CH-7, CH-9, POOL-2, BRG-18, GOSSIP-1 and ZKV-4, and `rand_getLimits` on chain 20, fullnode v0.6.7 `86941a1`: `crates/randprotocol-node/src/mempool.rs`, `admission.rs`, `crates/randprotocol-core/src/ledger/mod.rs` (the gas floor), fullnode `90e7716`: `docs/architecture.md` §8, `docs/rpc.md` (changelog, the 2026-09-28 gas entries), fullnode `ed88241`: `docs/confidential.md` (transaction binding) بررسی شد. هرجا کد و این صفحه با هم اختلاف دارند، حق با کد است؛ به ما اطلاع دهید.