Restore contract-proxied stake transfers - #3140
Conversation
The proxy origin-filter inheritance fix in release 453 (do_proxy now
propagates the caller's call filter into the proxied call) closed a real
filter-laundering loophole — but it also broke contracts that legitimately
execute transfer_stake through an explicit user proxy delegation.
An escrow contract deployed on this chain performs deposits as:
call_runtime(Proxy::proxy(real = depositor, TransferStake { .. }))
where the depositor has registered the contract as a proxy beforehand.
Pre-453 the inner TransferStake ran on a freshly authenticated origin and
succeeded; post-453 it must pass ContractCallFilter, which only whitelists
Proxy::proxy, so the inner call fails with CallFiltered inside
ProxyExecuted and every deposit reverts.
Whitelist the single inner call the flow needs. Security posture is
preserved: the transfer still requires the user's explicit proxy
delegation, and the inherited-filter mechanism keeps every other nested
call blocked.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🛡️ AI Review — Skeptic (security review)VERDICT: SAFE HIGH scrutiny: account is 63 days old, but has repository write access and substantive merged history; no Gittensor association found. Branch targets main. Static review found the allowlist expansion limited to FindingsNo findings. ConclusionNo malicious behavior or security vulnerability was found in this diff. 🔍 AI Review — Auditor (domain review)VERDICT: 👍 UNKNOWN Gittensor association; newer account with repository write access and substantive merged contribution history. The filter expansion is narrowly limited to PR #3139 contains the same functional commit but omits this branch's required runtime version bump. This PR is the better candidate. Recommend closing #3139. Static review only; runtime execution was unnecessary. Findings
ConclusionThe implementation restores the intended contract-proxy flow without bypassing inherited proxy restrictions. It is ready after adding focused regression coverage. |
| RuntimeCall::SubtensorModule(inner) => { | ||
| matches!(inner, pallet_subtensor::Call::transfer_stake { .. }) |
There was a problem hiding this comment.
[MEDIUM] Add regression coverage for the expanded contract filter
This production regression fix changes a security-sensitive runtime allowlist but adds no test. Add focused coverage proving that the filter accepts transfer_stake and Proxy::proxy, rejects unrelated calls, and—preferably through dispatch—preserves rejection by NonTransfer, NonFungible, and over-limit SmallTransfer proxies.
|
🔄 AI review updated — Skeptic: SAFE Auditor: 👍 |
Context
Release 453 fixed a proxy filter-laundering vulnerability by preserving inherited origin filters across nested proxy calls.
This also blocked legitimate contract deposit flows that execute:
The inner
transfer_stakemust now pass both the contract call filter and the user’s proxy-type filter. BecauseContractCallFilteronly permittedProxy::proxy, these calls began failing withCallFiltered.Changes
SubtensorModule::transfer_stakethroughContractCallFilter.spec_versionfrom 453 to 454.Security
This does not reintroduce the proxy vulnerability fixed in release 453.
The effective authorization remains the intersection of all inherited filters:
transfer_stake.NonTransferandNonFungibleproxies continue to reject the transfer.SmallTransfercontinues to enforce its amount limit.transfer_stakecan only move stake owned by the contract account itself.Validation
cargo fmt --check --allgit diff --check