از این صفحه برای پیوستن به مجموعهٔ اعتبارسنجها در یک chain در حال اجرا، خروج از آن و خواندن دفتر ثبت استفاده کنید. وزن اجماع باید عمومی باشد؛ به همین دلیل دفتر ثبت اعتبارسنجها تنها جدول این chain است که مبالغ را آشکار نگه میدارد. با این حال استیک از یک نوت محافظتشده خارج میشود و به یک نوت محافظتشده برمیگردد.
دفتر ثبت
برای هر اعتبارسنجی که تاکنون استیک کرده است یک ردیف وجود دارد که کلید آن نشانی base58 اعتبارسنج است:
ValidatorEntry {
public_key, // Dilithium2: signs blocks, and this validator's staking actions
stake: u64, // bonded: the weight an epoch's set gives it
pending: Vec<(u64, u64)>, // unbonding, as (release_epoch, amount), oldest first
rewards: u64, // fees earned as a block proposer, unpaid
payout: ShieldedAddress, // rand1… — where a withdraw pays
nonce: u64, // replay protection for its signed actions
activation_epoch: u64, // under a `staking` section: the first epoch this row may be in a set
}
همهٔ فیلدها عمومیاند و دفتر ثبت در ریشهٔ وضعیت هر بلاک هش میشود. هیچ ردیفی هرگز حذف نمیشود: اعتبارسنجی که همهٔ استیک خود را از حالت بانداژ خارج کند، ردیفی با stake = 0 نگه میدارد.
| ثابت | مقدار | معنا |
|---|---|---|
MIN_STAKE | 1,000 RAND | استیکی که یک ردیف برای حضور در مجموعهٔ یک epoch لازم دارد |
MAX_VALIDATORS | 100 | بزرگترین مجموعهای که یک epoch میتواند داشته باشد |
UNBONDING_EPOCHS | 2 | تعداد epochهایی که یک مبلغ خارجشده از بانداژ باید منتظر بماند تا قابل برداشت شود |
epoch_blocks | 1,000 در chain 20 | تعداد بلاکها در هر epoch؛ یک پارامتر genesis |
Epochها
epoch(h) = h / epoch_blocks. مجموعهٔ epoch شمارهٔ e ≥ 1 از دفتر ثبت، به وضعیت آن در آخرین بلاک epoch شمارهٔ e − 1، مشتق میشود: هر ردیف با stake ≥ MIN_STAKE، که MAX_VALIDATORS ردیف برتر بر اساس (stake descending, address ascending) از میان آنها برگزیده و بر اساس نشانی مرتب میشود. رهبر یک view، رأیدهندگان یک بلاک و راستیآزمایی یک گواهی همگی از مجموعهٔ epochی استفاده میکنند که بلاک به آن تعلق دارد. دو پیامد:
- یک bond تا مرز بعدی وزن نیست. این استیک بلافاصله در دفتر ثبت قرار میگیرد، اما مجموعه فقط در مرز epoch تغییر میکند؛ در chain بدون بخش
stakingهمان مرز بعدی، و در chain 20 مرزی دیرتر (در ادامه). - یک unbond در مرز بعدی از وزن میکاهد، در حالی که مبلغ آن دو epoch منتظر میماند تا قابل برداشت شود. اعتبارسنج مدتها پیش از آنکه بتواند برداشت کند، پیشنهاد بلاک را متوقف میکند.
اگر مشتقسازی نتیجهٔ تهی بدهد، مجموعهٔ قبلی ادامه مییابد. گرهای که با --validator راهاندازی شده و کلید آن در هیچ مجموعهٔ فعلی نیست، صرفاً مشاهده میکند تا یک epoch آن را بپذیرد؛ بنابراین اعتبارسنجی که پس از genesis استیک میکند نیازی به راهاندازی مجدد ندارد. rand_status مقدار is_validator (کلید اینجاست) و active_validator (در مجموعهٔ فعلی است) را جداگانه گزارش میکند.
بخش staking
یک genesis میتواند بخش staking داشته باشد (fullnode v0.5.4، audit v4 STAKE-2). بدون آن، قواعد بالا تمام ماجراست، مانند chain 14. هر chain از chain 15 به بعد آن را دارد. تنظیمات chain 20 چنین است:
"staking": {
"faucet_budget_per_epoch": "10000000000000", "bond_activation_epochs": 2,
"max_weight_bps": 3333, "max_stake_entry_bps_per_epoch": 2500,
"admission_by_vote": true, "registration_v2": true,
"faucet_recipients": ["rand1…", "…16 wallets…"],
"faucet_minters": ["<validator address>", "…18 validators…"]
}
| فیلد | chain 20 | قاعده |
|---|---|---|
bond_activation_epochs | 2 | bondی که در epoch شمارهٔ e اعمال شود برای epoch شمارهٔ e + 1 + 2 در صف قرار میگیرد؛ چه ثبتنام و چه افزایش استیک. مبلغ آن بلافاصله در stake است اما تا آن زمان وزن نیست، و استیکِ در صف را نمیتوان unbond کرد |
max_stake_entry_bps_per_epoch | 2,500 (25 %) | بیشترین استیک، شامل ثبتنامها و افزایشها با هم، که در یک مرز به وزن تبدیل میشود، بهصورت کسری از وزن فعال در همان مرز: ⌊active · bps / 10,000⌋. ردیفهای سررسیده فراتر از آن تا مرز بعدی و به ترتیب bond منتظر میمانند. روی 26,000 RAND استیک فعال chain 20 این مقدار 6,500 RAND در هر مرز است. سهم یک واردشونده پس از k مرز، در هر مقیاسی، حداکثر 1 − 1.25^−k است؛ پس از دو مرز حدود یکسوم |
admission_by_vote | true | bondی که یک کلید جدید را ثبت میکند رد میشود (NotAdmitted) تا زمانی که اعتبارسنجهایی با بیش از دو سوم وزن رأی به آن کلید رأی داده باشند (در ادامه); افزایش استیک به رأی نیاز ندارد |
max_weight_bps | 3,333 | وزن هیچ اعتبارسنجی از 33.33 % مجموع وزن مجموعهاش بیشتر نمیشود؛ مازاد در مجموعه محدود میشود و stake در دفتر ثبت تغییر نمیکند |
registration_v2 | true | ثبتنام، rand-register-2 را روی هش genesis، شناسهٔ chain، نشانی اعتبارسنج و نشانی پرداخت امضا میکند؛ ثبتنام v1 رد میشود |
faucet_budget_per_epoch | 10,000 RAND | faucet در هر epoch حداکثر همین مقدار mint میکند (فراتر از آن FaucetBudgetExhausted؛ شمارنده در epoch بعدی از نو آغاز میشود) |
faucet_recipients | 16 wallets | یک Mint از faucet فقط به همین wallets پرداخت میکند (FaucetRecipientNotAllowed)؛ همین فهرست است که امکان میدهد faucet در کنار پل قرار بگیرد |
faucet_minters | 18 validators | یک Mint از faucet فقط با این کلیدهای اعتبارسنج امضا میشود (MinterNotAllowed)، هم هنگام پذیرش و هم هنگام اعمال بلاک بررسی میشود، هر قدر هم که کلید دیگری از زمان bond شدنش گذشته باشد. در chain 20 اینها 18 نفر از 26 اعتبارسنج genesis هستند. این فهرست از chain 16 در هر genesis بوده است |
slashing | absent | بخش slashing وجود ندارد، پس هیچ اعتبارسنجی slash یا jail نمیشود (آنچه عمومی است) |
با وجود این بخش، شمارندهٔ faucet و صف bond به ریشهٔ وضعیت، تحت دامنهٔ rand-state-5، افزوده میشوند و برگ اعتبارسنج rand-validator-leaf-4 است. chain 20 همچنین قیمتهای پویای gas و وضعیت برنامههای RPL-2 را دارد که پس از آنها افزوده میشوند و دامنه را به rand-state-8 میبرند، و admission by vote این ریشه را یک بار دیگر بهصورت H("rand-state-admitted-1", root ‖ admitted_root) میپیچد. chain 15 فهرست faucet_minters نداشت: در آن، هر گره یک Mint از faucet را فقط وقتی در استخر قرار میداد و پیش میفرستاد که یک اعتبارسنج genesis آن را امضا کرده باشد (MinterNotAllowed، بهعنوان سیاست گره)، تا اعتبارسنجی که پس از genesis bond شده نتواند از طریق استخر بودجه را تخلیه کند. با وجود این فهرست، همان رد کردن به یک قاعدهٔ اعتبار تبدیل میشود.
تاریخچه: chainهای 15 تا 19 admission_by_vote نداشتند، پس هر کلیدی با 1,000 RAND میتوانست ثبتنام کند، و بودجهٔ ورود را بهصورت max_stake_entry_per_epoch ثابت 10,000 RAND در هر مرز تعیین میکردند. یک مقدار ثابت برای یک genesis خاص اندازهگیری شده است؛ کسر، سهم واردشونده را صرفنظر از میزان استیک یکسان نگه میدارد. این دو فیلد را نمیتوان با هم تنظیم کرد.
Admission by vote
جدید در fullnode v0.6.8 (audit v6، STAKE-2) و فعال برای chain 20. با 26 کلید که هر کدام 1,000 RAND دارند، یکسوم مسدودکنندهٔ مجموعه حدود 13,000 RAND استیک خریداریشده است و هیچ چیزی slash نمیشود. تحت این قاعده، مجموعه تصمیم میگیرد کدام کلیدهای جدید اصلاً اجازهٔ ثبتنام دارند.
- رأی یک action است،
AdmitValidator { candidate, signatures }، بدون bundle و بدون کارمزد. هر امضا یک امضای Dilithium2 از یک اعتبارسنج رویblake3("rand-admit-validator-1" ‖ genesis hash ‖ candidate address)است، به ترتیب صعودی نشانی رأیدهندگان. رأیدهندگان باید بهطور اکید بیش از دو سوم وزن مجموعهٔ رأیدهنده را داشته باشند، همان حساب یک گواهی حد نصاب: در chain 20، 18 نفر از 26 اعتبارسنج. رأی به یک genesis مقید است و به nonce نیاز ندارد: یک پذیرش با ثبتنامی که اجازه میدهد مصرف میشود. - مجموعهٔ رأیدهنده همان مشتقسازی خود دفتر ثبت برای epoch فعلی است. bond در صف در آن وزن نیست و unbond بلافاصله از آن خارج میشود. یک نامزد نمیتواند به خودش رأی بدهد.
- مجموعهٔ پذیرفتهشده بخشی از وضعیت اجماع است، حداکثر 256 کلید در انتظار ثبتنام (فراتر از آن
AdmissionSetFull).rand_getAdmittedآن را ارائه میدهد؛rand_getLimits.admission_by_voteمیگوید یک chain به رأی نیاز دارد یا نه.
# each voting validator, offline
rand-node admit sign --candidate <registration hex or public key> --key node.key.json --genesis-hash <hash>
# whoever collects the lines
rand-node admit submit --candidate <…> --signature <line> --signature <line> … --rpc http://127.0.0.1:8545
سپس bond مطابق زیر پیش میرود. rand_getGenesisHash هشی را برمیگرداند که admit sign به آن نیاز دارد.
چهار فرمان
اینکه کدام باینری مالک کدام action است از این نتیجه میشود که کدام کلید آن را امضا میکند: یک bond نوتها را خرج میکند، پس یک فرمان wallet است؛ unbond و withdraw با کلید Dilithium2 اعتبارسنج امضا میشوند که فقط گره آن را دارد.
| action | فرمان | همراه با | پرداخت |
|---|---|---|---|
| register | rand-node register | هیچچیز — یک blob چاپ میکند | هیچچیز |
Bond | rand bond | یک bundle که burn_r آن استیک است | 0.001 RAND از نوتهای wallet |
Unbond | rand-node unbond | هیچچیز (bundle: null) | هیچچیز |
Withdraw | rand-node withdraw | هیچچیز (bundle: null) | 0.001 RAND از مبلغ برداشتشده |
Register
rand-node register --key node.key.json --payout "$(rand --key payout.key.json address)" --v2 [--rpc http://127.0.0.1:8545]
نشانی اعتبارسنج و یک Registration بهصورت hex چاپ میکند: کلید عمومی آن، نشانی پرداخت و یک امضا. با --v2، که chain 20 آن را الزامی میکند، امضا تحت rand-register-2 روی هش genesis زنجیره، شناسهٔ chain، نشانی اعتبارسنج و نشانی پرداخت است؛ بدون آن، شکل v1 یعنی blake3("rand-register" ‖ bincode(chain_id, payout)) است که chain 20 آن را رد میکند (در chainی با binding_domain: 1 مانند chain 20، پیام ثبتنام در هر حال همان v2 است). RPC برای شناسهٔ chain و، با --v2، هش genesis خوانده میشود، پس ثبتنامی که برای یک chain امضا شده در chain دیگر رد میشود. نشانی پرداخت یک نشانی wallet است، نه کلید گره، و تنها فیلدی است که یک افزایش استیک بعدی نمیتواند آن را تغییر دهد. hex را به کسی بدهید که استیک را در اختیار دارد.
Bond
rand bond <validator base58> <amount in RAND> [--registration <hex>] [--fee <RAND>] [--no-wait] [--cuda]
یک تراکنش محافظتشدهٔ معمولی: کیف پول بستهٔ دارایی پنهان ۴-ورودی ۴-خروجی را اثبات میکند، همان بستهای که هر تراکنش از chain 14 به بعد داشته است، و مبلغ سهامگذاریشده در سوزاندن RAND آن، یعنی burn_r، قرار میگیرد. دفتر کل یک bond را فقط وقتی میپذیرد که burn_r == amount باشد. در chain 20 بسته دو اثبات دارد، همانطور که هر بسته از chain 17 داشته است: اثبات بستهٔ tier-14 و یک اثبات مجوز tier-10 روی کلید خرجکردن. هر دو بهطور پیشفرض بهصورت محلی اثبات میشوند، در حدود دو دقیقه روی یک لپتاپ Apple Silicon (حافظهٔ اثبات). با گزینهٔ --prover کیف پول، اثبات بسته بهجای آن توسط یک اثباتکنندهٔ جفتشده ساخته میشود و اثبات مجوز روی دستگاه کیف پول میماند. --registration دقیقاً وقتی لازم است که اعتبارسنج هنوز در دفتر ثبت نباشد، و وقتی هست رد میشود؛ کیف پول ابتدا از دفتر ثبت میپرسد. در chain 20 یک bond ثبتنامی با NotAdmitted رد میشود، مگر اینکه مجموعه ابتدا به آن کلید رأی داده باشد (پذیرش با رأی); افزایش سهام نیازی به رأی ندارد. ثبتنام دستکم 1,000 RAND سهامگذاری میکند؛ افزایش سهام میتواند هر مبلغ بالاتر از صفر باشد. آنچه زنجیره میآموزد این است که سهام این اعتبارسنج به این اندازه افزایش یافته است؛ اینکه کدام یادداشتها پرداخت کردهاند و مال چه کسی بودهاند، مانند یک انتقال پنهان است — هر کسی میتواند روی یک اعتبارسنج سهامگذاری کند بیآنکه چیزی جز مبلغ آشکار شود.
submitted bond 8c3f…e1
0 RAND out, 1000 RAND burned, 0.999 RAND change, fee 0.001 RAND, anchored at height 412
2nRdFC…: stake 1000 RAND, counting as consensus weight from epoch 69
دورهای که کیف پول چاپ میکند مرز بعدی است. تأخیر فعالسازی را در نظر نمیگیرد: در chain 20 یک bond که در دورهٔ 68 ثبت شده باشد، زودترین از دورهٔ 71 وزن دارد، و اگر بودجهٔ ورود، یعنی یکچهارم وزن فعال در هر مرز، مصرف شده باشد، دیرتر.
آزادسازی سهام
rand-node unbond <amount in RAND> --key node.key.json [--rpc …] [--no-wait]
مبلغ amount را از stake به pending منتقل میکند با release_epoch = epoch + 2 و nonce را یک واحد افزایش میدهد. رایگان و بدون بسته است، در chainهای 14 تا 19 روی blake3("rand-unbond" ‖ bincode(chain_id, validator, amount, nonce)) و در chain 20 روی blake3("rand-unbond-2" ‖ bincode(genesis_hash, chain_id, validator, amount, nonce)) امضا میشود (genesis با binding_domain: 1)؛ در زمان یک بلوک ثبت میشود. rand-node نسخهٔ v0.6.8 شکل مناسب زنجیرهای را که با آن صحبت میکند انتخاب میکند. آزادسازی بیش از سهام، یا صفر، رد میشود؛ چند آزادسازی در یک دوره در یک ردیف pending ادغام میشوند.
برداشت
rand-node withdraw <amount in RAND> --key node.key.json [--rpc …] [--no-wait]
ارزش آزادشده را — rewards بهعلاوهٔ ردیفهای pending که release_epoch آنها فرا رسیده است، از قدیمیترین — در یک یادداشت واریز به نشانی payout در دفتر ثبت میپردازد. توسط اعتبارسنج امضا میشود (در chain 20 تحت rand-withdraw-2، مانند آزادسازی ابتدا روی هش genesis) و بدون بسته است اما رایگان نیست: کارمزد پایهٔ 0.001 RAND از مبلغ کسر شده و به پیشنهاددهندهٔ بلوکی که آن را اعمال میکند میرسد، بنابراین یک برداشت 1,000 RAND یادداشتی به ارزش 999.999 میسازد و مبلغی که نتواند کارمزد پایه را پوشش دهد رد میشود. خود زنجیره یادداشت را محاسبه میکند:
cm = H_CM(payout.pk, from = 0, amount − 0.001 RAND, asset 0, time, r)
time ارتفاع head در زمان اجرای فرمان است (چاپ میشود و امضا آن را متعهد میکند)، و در هر نقطه از پنجرهٔ بسته پذیرفته میشود (1,024 بلوک در chain 20 که genesis آن proof_window_blocks را تعیین میکند؛ 256 در chainهای قبلی)؛ r منتشر میشود تا زنجیره هرگز تعهدی را از شبکه نپذیرد. پس از آن، کیف پول payout یادداشت را با پویش پیدا میکند، بیآنکه چیزی به آن گفته شده باشد:
rand --key payout.key.json balance # balance: 999.999 RAND
پیوستن و ترک
# on the joining validator's machine
rand-node register --key node.key.json --payout "$(rand --key payout.key.json address)" --v2
rand-node run --datadir ./data --key node.key.json --validator --bootstrap /ip4/…/p2p/…
# it syncs and observes; rand_status says is_validator true, active_validator false
# on chain 20: validators holding more than two thirds of the weight vote the key in
rand-node admit sign --candidate <hex from above> --key <their node.key.json> --genesis-hash <hash>
rand-node admit submit --candidate <hex from above> --signature <line> … --rpc http://127.0.0.1:8545
# on the machine holding the stake
rand bond <validator base58> 1000 --registration <hex>
rand validators # the new row, active false until it activates
# from its activation epoch on (on chain 20, three boundaries after the bond at the earliest)
rand-node status # active_validator: true
rand-node unbond 1000 --key node.key.json # out of the set at the next boundary
# wait two epochs
rand-node withdraw 1000 --key node.key.json # a note worth 999.999 RAND at the payout address
پیش از آزادسازی سهام، حساب حدنصاب را بررسی کنید: مجموعهای با n عضو به بیش از 2/3 سهام خود بهصورت برخط نیاز دارد. در chain 20 هر 26 اعتبارسنج دقیقاً روی MIN_STAKE هستند، بنابراین هر آزادسازیای آن گره را از مجموعهٔ دورهٔ بعد بیرون میاندازد، و 18 نفر از 26 باید برخط بمانند. اعتبارسنجی که برخط است اما رأی نمیدهد نیز علیه حدنصاب حساب میشود: از chain 14 هر اعتبارسنج از رأیدادن به بلوکی که بیش از 15 s جلوتر از ساعت خودش باشد خودداری میکند، بنابراین اعتبارسنجی که ساعتش کند است بدون هیچ خطایی از رأیدادن باز میماند. روی آن NTP اجرا کنید (اجرای یک اعتبارسنج).
Genesis دفتر ثبت را مستقیماً مقداردهی میکند — یک ورودی برای هر --validator <key or hex pubkey>,<stake>,<payout> — و سهامی کمتر از حداقل را رد میکند. genesis در chain 20، 26 ردیف هر یک 1,000 RAND میسازد، با همان کلیدهای chain 19.
از v0.5.11 این build یک vesting در genesis نیز دارد: یک دفتر عمومی از تخصیصهای قفلشده زمانی که از آن میتوان یک ورودی غیرقابلبازگشت را برای سهامگذاری RAND قفلشده بهعنوان سهام یک اعتبارسنج bond کرد (rand-node vesting bond) و پس گرفت (rand-node vesting unbond). این به بخش vesting در genesis نیاز دارد. هیچ زنجیرهای آن را ندارد، chain 20 هم، و بدون آن این اقدامها هنگام پذیرش رد میشوند. (v0.6.8 همچنین اجازه میدهد یک ورودی vesting قابلابطال فقط توسط حدنصابی از امضاکنندگان به یک خزانهٔ ثابت ابطال شود، باز هم فقط تحت بخش vesting.)
چه چیزی عمومی است
| عمومی | پنهان | |
|---|---|---|
| دفتر ثبت | هر ردیف: نشانی، سهام، صف آزادسازی، پاداشهای پرداختنشده، نشانی payout، nonce | هیچ |
Bond | اعتبارسنج، مبلغ، اینکه ثبتنام پیوست بوده است یا نه، burn_r بسته | کدام یادداشتها پرداخت کردهاند، مالک آنها کیست، باقیمانده |
AdmitValidator (chain 20) | نامزد و هر رأیدهنده با امضایش | چیز دیگری وجود ندارد |
Unbond | اعتبارسنج، مبلغ، nonce | چیز دیگری وجود ندارد |
Withdraw | اعتبارسنج، مبلغ، nonce، time و r یادداشت | چه کسی میتواند یادداشت را باز کند، و هر خرج بعدی آن |
پاداشها کارمزد بلوکهایی است که یک اعتبارسنج پیشنهاد داده است. هیچ پاداش بلوکی و هیچ تورمی وجود ندارد، و در chain 20 هیچ slashing و jailing برای اعتبارسنجها وجود ندارد: اعتبارسنجی که رفتار نادرست کند هیچ هزینهای از سهام خود نمیپردازد و چاره بر عهدهٔ اپراتورهاست. Fullnode v0.6.8 یک قاعدهٔ slashing برای دوپهلوگویی رهبر دارد (audit v6، STAKE-1: دو سرایند بلوک امضاشده برای یک view توسط یک کلید، equivocation_bps از سهام را نابود و کلید را jail میکند)، اما فقط تحت بخش staking.slashing در genesis، و chain 20 چنین بخشی ندارد (rand_getLimits.slashing برابر null است)؛ fullnode آن را فقط وقتی توصیه میکند که سهام در اختیار بیش از یک اپراتور باشد. (کد تجمیع بلوک همچنین یک اقدام SlashAggregator برای aggregatorهایی دارد که دو تجمیع متفاوت را در یک nonce امضا میکنند، و در chain 20 که بخش تجمیع ندارد فعال نیست.)
خواندن آن
rand validators # the whole register
rand status # is_validator / active_validator for the node asked
→ {"method":"rand_getValidators","params":[]}
← [{ "address": "2nRdFC…", "stake": "1000000000000",
"pending": [{ "release_epoch": 71, "amount": "1000000000000" }],
"rewards": "4000000", "payout": "rand1…", "nonce": 3, "active": true, "jailed_until": null }]
→ {"method":"rand_getAdmitted","params":[]}
← { "admission_by_vote": true, "admitted": [], "max": 256 }
→ {"method":"rand_getEpoch","params":[]}
← { "epoch": 69, "epoch_blocks": 1000, "next_set": ["2nRdFC…", "ByDkxs…"] }
مبلغها رشتههای اعشاری از واحدها هستند. active میگوید آیا ردیف در مجموعهای است که دورهٔ جاری را اجرا میکند؛ jailed_until جز تحت بخش slashing برابر null است؛ admitted کلیدهایی را فهرست میکند که به آنها رأی داده شده اما هنوز ثبتنام نکردهاند؛ next_set یک برآورد است که هر bond و unbond پیش از مرز آن را جابهجا میکند. GET /validators در explorer مقادیر share_percent، blocks_proposed و آخرین ارتفاع پیشنهادشده را اضافه میکند.