استخدم هذه الصفحة للانضمام إلى مجموعة المدققين على سلسلة قيد التشغيل، وللخروج منها، ولقراءة السجل. يجب أن يكون وزن الإجماع علنيًا، ولذلك فإن سجل المدققين هو الجدول الوحيد على هذه السلسلة الذي يحتفظ بالمبالغ مكشوفة؛ ومع ذلك تخرج الحصة من ملاحظة محمية وتعود إلى ملاحظة محمية.
السجل
صف واحد لكل مدقق ربط حصة يومًا ما، مفهرس بعنوانه بصيغة 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 | الحصة التي يحتاجها الصف ليدخل في مجموعة أي حقبة |
MAX_VALIDATORS | 100 | أكبر مجموعة يمكن أن تكون لحقبة |
UNBONDING_EPOCHS | 2 | عدد الحقب التي ينتظرها المبلغ بعد فك ربطه قبل أن يمكن سحبه |
epoch_blocks | 1,000 على chain 20 | عدد الكتل في الحقبة؛ معامل في كتلة التكوين |
الحقب
epoch(h) = h / epoch_blocks. تُشتق مجموعة الحقبة e ≥ 1 من السجل كما كان عند آخر كتلة في الحقبة e − 1: كل صف له stake ≥ MIN_STAKE، وأعلى MAX_VALIDATORS وفق الترتيب (stake descending, address ascending)، ثم تُرتَّب بحسب العنوان. ويستخدم قائد العرض (view) ومصوِّتو الكتلة والتحقق من الشهادة جميعًا مجموعة الحقبة التي تنتمي إليها الكتلة. ويترتب على ذلك أمران:
- الربط لا يصير وزنًا حتى حدٍّ لاحق. هو في السجل فورًا، لكن المجموعة لا تتغير إلا عند حدّ الحقبة — الحدّ التالي على سلسلة بلا قسم
staking، وأبعد من ذلك على chain 20 (أدناه). - فك الربط يكلّف وزنًا عند الحدّ التالي، بينما ينتظر المبلغ حقبتين ليصبح قابلًا للسحب. ويتوقف المدقق عن اقتراح الكتل قبل وقت طويل من قدرته على السحب.
إذا كان الاشتقاق فارغًا، تُنقل المجموعة السابقة كما هي. والعقدة التي بدأت بالخيار --validator ومفتاحها غير موجود في أي مجموعة حالية تكتفي بالمراقبة إلى أن تقبلها حقبة، ولذلك فإن المدقق الذي يربط حصته بعد التكوين لا يحتاج إلى إعادة تشغيل؛ ويُبلغ rand_status عن is_validator (المفتاح موجود هنا) وعن active_validator (المفتاح في المجموعة الحالية) كلٌّ على حدة.
قسم staking
قد تحمل كتلة التكوين قسمًا باسم staking (fullnode v0.5.4، التدقيق v4 STAKE-2). وبدونه تكون القواعد أعلاه هي الصورة كاملة، كما في chain 14. وكل سلسلة منذ 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 | الربط المطبَّق في الحقبة e يُدرج في قائمة الانتظار للحقبة e + 1 + 2؛ التسجيل وزيادة الحصة سواء. مبلغه في stake فورًا لكنه ليس وزنًا حتى ذلك الحين، ولا يمكن فك ربط الحصة المنتظِرة |
max_stake_entry_bps_per_epoch | 2,500 (25 %) | أقصى حصة، من التسجيلات وزيادات الحصة معًا، تصير وزنًا عند حدّ واحد، كنسبة من الوزن الفعّال القائم عنده: ⌊active · bps / 10,000⌋. والصفوف المستحقة التي تتجاوزه تنتظر الحدّ التالي، بحسب ترتيب الربط. وعلى حصة chain 20 الفعّالة البالغة 26,000 RAND يكون ذلك 6,500 RAND عند كل حدّ. وحصة الداخل الجديد بعد k من الحدود لا تتجاوز 1 − 1.25^−k على أي مقياس، أي نحو الثلث بعد حدّين |
admission_by_vote | true | الربط الذي يسجّل مفتاحًا جديدًا يُرفض (NotAdmitted) إلى أن يصوّت لقبول المفتاح مدققون يملكون أكثر من ثلثي وزن التصويت (أدناه); أما زيادة الحصة فلا تحتاج إلى تصويت |
max_weight_bps | 3,333 | لا يتجاوز وزن أي مدقق 33.33 % من إجمالي مجموعته؛ ويُقتطع الفائض في المجموعة، ويبقى stake في السجل دون تغيير |
registration_v2 | true | يوقّع التسجيل rand-register-2 على تجزئة كتلة التكوين ومعرّف السلسلة وعنوان المدقق وعنوان الدفع؛ ويُرفض تسجيل v1 |
faucet_budget_per_epoch | 10,000 RAND | لا تسك الحنفية (faucet) أكثر من هذا في الحقبة الواحدة (FaucetBudgetExhausted عند تجاوزه؛ ويبدأ العدّاد من جديد في الحقبة التالية) |
faucet_recipients | 16 wallets | لا يدفع Mint الخاص بالحنفية إلا لهذه المحافظ (FaucetRecipientNotAllowed)؛ وهذه القائمة هي ما يتيح وجود حنفية بجوار الجسر |
faucet_minters | 18 validators | لا يجوز توقيع Mint الخاص بالحنفية إلا بمفاتيح هؤلاء المدققين (MinterNotAllowed)، ويُتحقق من ذلك عند القبول وعند تطبيق الكتلة، مهما طال ربط مفتاح آخر. وعلى chain 20 هم 18 من المدققين الستة والعشرين في التكوين. والقائمة موجودة في كل كتلة تكوين منذ chain 16 |
slashing | absent | لا قسم للمعاقبة بالخصم (slashing)، فلا يُعاقَب أي مدقق بالخصم ولا يُوقَف (ما هو علني) |
تحت هذا القسم يُلحق عدّاد الحنفية وقائمة انتظار الربط بجذر الحالة، تحت النطاق rand-state-5، وورقة المدقق هي rand-validator-leaf-4. وتحمل chain 20 أيضًا أسعار غاز ديناميكية وحالة برامج RPL-2 تُلحق بعدهما، فينتقل نطاقها إلى rand-state-8، ويلفّ القبول بالتصويت ذلك الجذر مرة أخرى على هيئة H("rand-state-admitted-1", root ‖ admitted_root). ولم تكن في chain 15 قائمة faucet_minters: فيها كانت كل عقدة تجمع Mint الحنفية وتمرّره فقط إذا وقّعه مدقق من مدققي التكوين (MinterNotAllowed، كسياسة للعقدة)، حتى لا يتمكن مدقق ربط حصته بعد التكوين من استنزاف الميزانية عبر المجمّع. ومع القائمة صار الرفض نفسه قاعدة صلاحية.
التاريخ: لم يكن في السلاسل 15 إلى 19 admission_by_vote، فكان بإمكان أي مفتاح يملك 1,000 RAND أن يسجّل، وحدّدت ميزانية الدخول بقيمة ثابتة هي max_stake_entry_per_epoch بمقدار 10,000 RAND عند كل حدّ. والرقم الثابت يُقاس على تكوين واحد؛ أما النسبة فتُبقي الداخل الجديد عند الحصة نفسها أيًّا كان حجم الحصة. ولا يجوز ضبط الحقلين معًا.
القبول بالتصويت
جديد في fullnode v0.6.8 (التدقيق v6، STAKE-2) ومفعّل على chain 20. مع 26 مفتاحًا بحصة 1,000 RAND لكل منها، يبلغ الثلث المعطِّل في المجموعة نحو 13,000 RAND من حصة مشتراة، ولا شيء يُعاقَب بالخصم. وبموجب هذه القاعدة تقرّر المجموعة أي المفاتيح الجديدة يجوز لها أن تسجّل أصلًا.
- التصويت إجراء، هو
AdmitValidator { candidate, signatures }، بلا حزمة وبلا رسوم. كل توقيع هو توقيع Dilithium2 لمدقق علىblake3("rand-admit-validator-1" ‖ genesis hash ‖ candidate address)، بترتيب تصاعدي لعناوين المصوّتين. ويجب أن يملك المصوّتون أكثر من ثلثي وزن مجموعة التصويت بصرامة، وهو حساب شهادة النصاب: على chain 20، 18 من 26 مدققًا. والتصويت مرتبط بتكوين واحد ولا يحتاج إلى nonce: إذ يُستهلك القبول بالتسجيل الذي يتيحه. - مجموعة التصويت هي الاشتقاق الخاص بالسجل للحقبة الحالية. والربط المنتظِر ليس وزنًا فيها، وفك الربط يُخرج صاحبه منها فورًا. ولا يستطيع المرشّح أن يصوّت لنفسه.
- المجموعة المقبولة حالة إجماع، وفيها 256 مفتاحًا على الأكثر في انتظار التسجيل (
AdmissionSetFullعند تجاوزها). ويخدمهاrand_getAdmitted؛ ويبيّنrand_getLimits.admission_by_voteما إذا كانت السلسلة تحتاج إلى التصويت.
# 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
ثم يمضي الربط كما يلي. ويعيد rand_getGenesisHash التجزئة التي يحتاجها admit sign.
الأوامر الأربعة
أيُّ ملف تنفيذي يملك أي إجراء يتبع المفتاح الذي يوقّعه: الربط ينفق ملاحظات، فهو أمر في المحفظة؛ وفك الربط والسحب يوقّعهما مفتاح Dilithium2 الخاص بالمدقق، وهو لا يوجد إلا لدى العقدة.
| الإجراء | الأمر | يعتمد على | يدفع |
|---|---|---|---|
| التسجيل | rand-node register | لا شيء — يطبع كتلة بيانات | لا شيء |
Bond | rand bond | حزمة يكون burn_r فيها هو الحصة | 0.001 RAND من ملاحظات المحفظة |
Unbond | rand-node unbond | لا شيء (bundle: null) | لا شيء |
Withdraw | rand-node withdraw | لا شيء (bundle: null) | 0.001 RAND من المبلغ المسحوب |
التسجيل
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 على تجزئة كتلة التكوين للسلسلة ومعرّف السلسلة وعنوان المدقق وعنوان الدفع؛ وبدونه تكون صيغة v1، أي blake3("rand-register" ‖ bincode(chain_id, payout))، التي ترفضها chain 20 (وعلى سلسلة بقيمة binding_domain: 1 مثل chain 20 تكون رسالة التسجيل هي رسالة v2 على أي حال). وتُقرأ من RPC قيمة معرّف السلسلة، ومع --v2 تجزئة كتلة التكوين، فيُرفض على سلسلة أخرى أي تسجيل وُقّع لسلسلة بعينها. وعنوان الدفع عنوان محفظة، لا مفتاح عقدة، وهو الحقل الوحيد الذي لا تستطيع زيادة الحصة اللاحقة تغييره. سلّم قيمة hex لمن يحتفظ بالحصة.
الربط
rand bond <validator base58> <amount in RAND> [--registration <hex>] [--fee <RAND>] [--no-wait] [--cuda]
معاملة محمية عادية: تُثبت المحفظة حزمة الأصول المخفية ذات 4 مدخلات و4 مخرجات التي تحملها كل معاملة منذ chain 14، مع المبلغ المودَع كحصة ضمن حرق RAND الخاص بها، burn_r، ولا يقبل السجل الدفتري سندًا إلا إذا كان burn_r == amount. في chain 20 تحمل الحزمة إثباتين، كما تحملهما كل حزمة منذ chain 17: إثبات الحزمة من المستوى 14 وإثبات تفويض من المستوى 10 على مفتاح الإنفاق. يُنشأ الإثباتان محليًا افتراضيًا، في نحو دقيقتين على حاسوب محمول بمعالج Apple Silicon (ذاكرة الإثبات). أما مع الخيار --prover في المحفظة فيُنشئ مُثبِت مقترن إثبات الحزمة، ويبقى إثبات التفويض على جهاز المحفظة. يكون الخيار --registration مطلوبًا تمامًا حين لا يكون المدقِّق في السجل بعد، ومرفوضًا حين يكون فيه؛ وتسأل المحفظة السجل أولًا. في chain 20 يُرفض السند المسجِّل بالخطأ 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 يصبح السند الذي أُودع في الحقبة 68 وزنًا في الحقبة 71 على أقرب تقدير، وفي وقت لاحق إذا استُنفدت ميزانية الدخول، وهي ربع الوزن النشط عند كل حد.
فك السند
rand-node unbond <amount in RAND> --key node.key.json [--rpc …] [--no-wait]
ينقل amount من stake إلى pending مع release_epoch = epoch + 2 ويزيد قيمة nonce. مجاني ومن دون حزمة، ويُوقَّع على blake3("rand-unbond" ‖ bincode(chain_id, validator, amount, nonce)) في السلاسل من 14 إلى 19 وعلى blake3("rand-unbond-2" ‖ bincode(genesis_hash, chain_id, validator, amount, nonce)) في chain 20 (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، على hash التكوين الأول أولًا، مثل فك السند) ولا يحتاج إلى حزمة، لكنه ليس مجانيًا: تذهب الرسوم الأساسية البالغة 0.001 RAND إلى مقترِح الكتلة التي تطبّقه، وتُقتطع من المبلغ، فسحب 1,000 RAND ينشئ مذكرة قيمتها 999.999، ويُرفض أي مبلغ لا يغطي الرسوم الأساسية. تحسب السلسلة المذكرة بنفسها:
cm = H_CM(payout.pk, from = 0, amount − 0.001 RAND, asset 0, time, r)
time هو ارتفاع الرأس حين نُفّذ الأمر (يُطبع ويرتبط به التوقيع)، ويُقبل في أي موضع داخل نافذة الحزمة (1,024 كتلة في chain 20، التي يحدد تكوينها proof_window_blocks؛ و256 في السلاسل السابقة)؛ وr منشور كي لا تقبل السلسلة التزامًا من الشبكة قط. بعد ذلك تجد محفظة الدفع المذكرة بالمسح، دون أن يُخبرها أحد بشيء:
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 مدقِّقًا تحتاج إلى أكثر من ثلثي حصتها متصلةً بالشبكة. في chain 20 يقف المدقِّقون الستة والعشرون جميعًا عند MIN_STAKE تمامًا، فأي فك للسند، مهما صغر، يُخرج تلك العقدة من مجموعة الحقبة التالية، ويجب أن يبقى 18 من أصل 26 متصلين. والمدقِّق الذي يعمل ولا يصوّت يُحتسب على النصاب أيضًا: منذ chain 14 يمتنع المدقِّق عن التصويت لكتلة تسبق ساعته بأكثر من 15 ثانية، فالمدقِّق الذي تتأخر ساعته يتوقف عن التصويت دون أي خطأ. شغّل NTP عليه (تشغيل مدقِّق).
يملأ التكوين (genesis) السجل مباشرة — بإدخال واحد لكل --validator <key or hex pubkey>,<stake>,<payout> — ويرفض أي حصة دون الحد الأدنى. يملأ تكوين chain 20 السجل بـ 26 صفًا، في كل منها 1,000 RAND، وبالمفاتيح نفسها التي في chain 19.
منذ v0.5.11 يحمل الإصدار أيضًا استحقاق التكوين: سجلًا علنيًا للمخصصات المقيَّدة زمنيًا، يستطيع منه أي إدخال غير قابل للإلغاء أن يودع RAND المقيَّد حصةً لمدقِّق (rand-node vesting bond) وأن يستردها (rand-node vesting unbond). ويحتاج إلى قسم vesting في التكوين. ولا تملك أي سلسلة هذا القسم، ومنها chain 20، ومن دونه تُرفض هذه الإجراءات عند القبول. (كما يتيح v0.6.8 ألا يُلغى إدخال استحقاق قابل للإلغاء إلا بعتبة من الموقّعين إلى خزينة ثابتة، وذلك أيضًا تحت قسم vesting فقط.)
ما هو علني
| علني | مخفي | |
|---|---|---|
| السجل | كل صف: العنوان، الحصة، طابور فك السند، المكافآت غير المدفوعة، عنوان الدفع، nonce | لا شيء |
Bond | المدقِّق، المبلغ، هل أُرفق تسجيل، قيمة burn_r في الحزمة | أي المذكرات دفعت، ومن يملكها، والباقي |
AdmitValidator (chain 20) | المرشح وكل مصوّت مع توقيعه | لا يوجد شيء آخر |
Unbond | المدقِّق، المبلغ، nonce | لا يوجد شيء آخر |
Withdraw | المدقِّق، المبلغ، nonce، وtime وr الخاصان بالمذكرة | من يستطيع فتح المذكرة، وكل إنفاق لاحق لها |
المكافآت هي رسوم الكتل التي اقترحها المدقِّق. لا توجد مكافآت كتل ولا تضخم، وفي chain 20 لا يوجد خصم من الحصة ولا سجن للمدقِّقين: المدقِّق الذي يسيء التصرف لا يخسر شيئًا من حصته، والعلاج يعود إلى المشغّلين. يحمل Fullnode v0.6.8 قاعدة خصم لتعدد الرؤوس من القائد (التدقيق v6، STAKE-1: رأسا كتلتين موقّعان لعرض واحد بمفتاح واحد يُتلفان equivocation_bps من الحصة ويسجنان المفتاح)، لكن ذلك لا يسري إلا تحت قسم staking.slashing في التكوين، وليس لدى chain 20 هذا القسم (rand_getLimits.slashing تساوي null)؛ ويوصي fullnode بها فقط حين تكون الحصة بيد أكثر من مشغّل واحد. (ويحمل كود تجميع الكتل كذلك إجراء SlashAggregator للمُجمِّعين الذين يوقّعون تجميعين مختلفين عند 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 هي null إلا تحت قسم الخصم؛ ويسرد admitted المفاتيح التي صُوّت لقبولها ولم تسجَّل بعد؛ وnext_set إسقاط يتغير مع كل سند وفك سند قبل الحد. ويضيف GET /validators في المستكشف share_percent وblocks_proposed وآخر ارتفاع اقتُرحت فيه كتلة.