feat(chain-extensions): add claim-root-with-hotkey for contract root-basket claims - #3099
feat(chain-extensions): add claim-root-with-hotkey for contract root-basket claims#3099Benup211 wants to merge 6 commits into
Conversation
…basket claims Exposes function 37 (ClaimRootWithHotkeyV1) so an ink! contract that holds root stake as a coldkey can claim its per-validator basket entitlement through the 0x1000 chain extension. The contract is the coldkey; realized TAO is staked back to root on the hotkey and the realized amount is returned. Basket work is bounded to MAX_ROOT_CLAIM_WORK and charged upfront (no post-dispatch refund), mirroring the signed extrinsic's admission gate.
|
@Benup211 is attempting to deploy a commit to the RaoFoundation Team on Vercel. A member of the Team first needs to authorize it. |
…laim-root-with-hotkey Seeds the hotkey's root basket with MAX_ROOT_CLAIM_WORK + 1 escrow rows and asserts ClaimRootWithHotkeyV1 returns RuntimeError before charging any weight.
UnArbosSix
left a comment
There was a problem hiding this comment.
Successful contract claims can leave the root-staker coldkey index inconsistent. The new chain-extension arm calls do_root_claim(caller, vec![hotkey]) directly and returns the payout, but never calls maybe_add_coldkey_index(&caller). The normal claim_root_with_hotkey extrinsic explicitly does exactly that after do_root_claim, so this is a real semantic difference between the two entry points, not incidental cleanup.
…act root claims ClaimRootWithHotkeyV1 called do_root_claim(caller, vec![hotkey]) and returned the payout without registering the caller in the staking-coldkey index, unlike the claim_root / claim_root_with_hotkey extrinsics, which call maybe_add_coldkey_index(&coldkey) unconditionally after do_root_claim (dispatches.rs:1988, :2023). A paying claim stakes its payout back on root under the claiming coldkey (claim_root.rs:774-781), so the claim itself can be a contract coldkey's first root stake;leaving it invisible to the index until the slow per-block add-only sweep happens to reach it. The arm now mirrors the signed flow: after a successful claim it calls maybe_add_coldkey_index(&caller) before writing the output. Idempotent, and covered by the pre-charged claim_root(MAX_ROOT_CLAIM_WORK) envelope; error and over-envelope paths remain write-free as in the signed flows. Tests: fresh coldkey is indexed after a zero-payout claim; a paying claim (tao > 0) indexes the caller; repeat claims leave the index untouched; the 257-row rejection leaves no index trace.
|
Good catch- ext-39 was the only claim entry point skipping index maintenance, and you're right that it's a real semantic difference, not incidental. do_root_claim stakes a successful payout back on root under the claiming coldkey (claim_root.rs:774-781) without touching the index, so a contract's first root stake arriving via the extension would leave it unindexed until the per-block add-only sweep happens to pick it up and a contract that only ever noop-claims would never be indexed at all, unlike on the signed path. Fixed: the arm now calls maybe_add_coldkey_index(&caller) on the Ok path, restoring exact parity with claim_root_with_hotkey, which indexes the caller unconditionally after do_root_claim (zero-payout claims included). The Err and over-envelope paths remain write-free, matching the signed ? early-returns. No weight change is needed, the arm pre-charges claim_root(MAX_ROOT_CLAIM_WORK), the same envelope the signed extrinsic declares and the same cost the claim_root benchmark already measures with the index writes included. Added regression tests covering: a fresh coldkey indexed after a zero-payout claim, a paying claim (tao > 0) indexing the caller, repeat claims leaving StakingColdkeys/StakingColdkeysByIndex/NumStakingColdkeys unchanged, and the 257-row rejection leaving no index trace. Full crate suite passes (60 tests) with clean clippy/fmt. |
Description
Adds a new chain-extension function (
ClaimRootWithHotkeyV1, id 39) so an pallet/ink! contract that holds root (netuid 0) stake as a coldkey can claim its per-validator basket entitlement through the0x1000chain extension.do_root_claim(env.caller(), vec![hotkey])is invoked with the contract address as the origin.RootClaimOutcome::tao) is written back so the contract can update its own bookkeeping.MAX_ROOT_CLAIM_WORK(256) before charging any weight, mirroring the rows clause of the signed extrinsic'sroot_claim_fits_declared_budgetadmission gate. The fullclaim_root(MAX_ROOT_CLAIM_WORK)weight is then charged viaenv.charge_weight(a chain extension has no post-dispatch weight refund).Files changed:
chain-extensions/src/types.rs— addClaimRootWithHotkeyV1 = 39toFunctionIdchain-extensions/src/lib.rs— dispatch arm (decode → bound → charge weight → claim → write output)chain-extensions/src/tests.rs—claim_root_with_hotkey_noop_returns_zero(success path: full 256-unit charge, zero payout) andclaim_root_with_hotkey_rejects_basket_above_envelope(257-row basket →RuntimeError, no weight charged)Related Issue(s)
None.
Type of Change
Breaking Change
No breaking change. This is purely additive: a new chain-extension function id (39) and dispatch arm. Existing function ids 0–38 and their SCALE encodings are unchanged.
Checklist
./scripts/fix_rust.shto ensure my code is formatted and linted correctlyScreenshots (if applicable)
N/A
Additional Notes
MoveStakeLimitV1,CallerMoveStakeLimitV1) were taken by release-448, so this function landed on 39. The discriminant and roundtrip tests assert16..=39.spec_versionbump (or theno-spec-version-bumplabel) — left for the release train / maintainer to coordinate.cargo test -p subtensor-chain-extensions— 57 passed, 0 failed (incl. both claim tests)cargo clippy -p subtensor-chain-extensions --all-targets— cleancargo fmt -p subtensor-chain-extensions -- --check— clean./scripts/fix_rust.shwas not run (it auto-commits workspace-wide).claim_root(MAX_ROOT_CLAIM_WORK)charge with a pre-charge row-count bound is deliberate — unlike the signed extrinsic (which declares the same 256-unit envelope and refunds post-dispatch down toroot_claim_actual_weight), a chain extension has no post-dispatch refund, so the envelope must be charged in full and oversized baskets rejected before any weight is charged.