feat(server,cli): support PGAS allowances - #391
Open
TarikGul wants to merge 7 commits into
Open
Conversation
…tension `build_unsigned_extrinsic`, `build_proof_message`, and the info-variant lookup each hardcoded `AsResources`. PGAS authorizes through `AsPgas` on Asset Hub and needs the same three, so they now take the extension identifier and the `AsResources` forms are thin wrappers over them. The field-enum lookup changed shape rather than gaining a parameter. It located the collection by type name, `MembershipCollection`, which is specific to `AsResources`; `AsPgas` names its own `PgasCollection`. It now locates the field enum by the variant it contains, since the membership tier inside them is named the same either way. `MissingMembershipCollection` and `MissingLitePeopleCollection` give way to one `MissingExtensionFieldVariant` that names the extension, the info variant, and the tier it looked for. Both generalized builders read the transaction-extension version from metadata, so a second authorizing extension encodes for the pipeline the runtime declares rather than repeating the constant that was removed from the `AsResources` path. Tests pin the wrappers against the general forms, so the pair cannot drift, and cover an unknown extension, info variant, and nested variant.
…zation The claim's chain-independent half: the ring-VRF alias context, the spent-alias storage key, the per-day slot scan, the `Pgas.claim_pgas` call, and the `AsPgas` extension `extra`. Each piece sits beside the statement-store and long-term-storage equivalent it mirrors rather than in a module of its own, so the alias helper reuses `SlotError::AliasInContext`, the storage key reuses `blake2_128_concat`, and the extension `extra` reuses `OPTION_SOME` and the identifier-parameterized builders. Two layouts differ from their siblings and are deliberate. The PGAS alias context writes day and slot little-endian where the other two are big-endian, because the mobile wallet writes them that way and the runtime verifies the proof against the same bytes. `AsPgas` names its membership enum `PgasCollection`, so the tier is resolved by variant name rather than by the `MembershipCollection` type name. The slot model follows long-term storage, not the statement store: `Pgas.ClaimedGasAliases` records a spent alias with a unit value, so presence is the whole answer and no entry is decoded. Verified against live Asset Hub (`next-asset-hub-paseo`, spec 2000036) before writing: `AsPgas::Claim` declares exactly the five fields encoded here, `Pgas` is pallet 99 with `claim_pgas(u32, AccountId32)`, `ClaimedGasAliases` hashes as `Identity(u32be day)` then `Blake2_128Concat(alias)`, and `MaxClaimsPerPeriodPerLitePerson` is 40. A test pins the five-field payload, since a short payload is accepted locally and then panics the runtime in `validate_transaction`.
The claim orchestrator. Two chains take part: membership and its ring revision come from the People chain, the claim is submitted on Asset Hub, and Asset Hub learns about People's rings through `MembersSubscriber`. That propagation is the part worth care. Asset Hub keeps only its most recent roots per ring, so a revision it does not hold is either still arriving or gone for good, and those need different answers — the first is worth waiting for, the second means the proof must be rebuilt against a newer ring. `await_ring_revision` distinguishes them and is public, so a host can check propagation before offering a claim. The claim retries against a different slot on a duplicate submission, which the statement-store and long-term-storage claims already do and the flow this was ported from did not: two hosts racing the same day both scan a free slot before either lands. `Metadata::extension_info_field_count` joins the other three lookups in taking the extension identifier, so the arity guard covers `AsPgas` as well as `AsResources`. Verified against live Asset Hub (`next-asset-hub-paseo`, spec 2000036) and the live People chain, in `tests/live_asset_hub.rs`: the runtime serves V16 and declares five fields for `AsPgas::Claim`; `RingCommitmentRecord` decodes the real `RingRoots` value, where People's current ring 2 revision is among the roots Asset Hub holds; and a long-pruned revision reports as pruned instead of waiting out the timeout. Those tests connect directly rather than through the host's `ChainProvider`, which filters Asset Hub out unless `E2E_LIVE_CHAIN=1` and then silently falls back to the People chain.
`pgas-check` mirrors `alloc-check` for the PGAS path: it reports ring membership on the People chain, whether Asset Hub has imported that ring's revision, the day's first unclaimed slot, and with `--submit` performs the claim. It connects to both chains directly rather than through the host's `ChainProvider`. That provider drops Asset Hub unless `E2E_LIVE_CHAIN=1` and then falls back to the People chain for any unrecognised genesis, so a claim routed through it would run against People metadata and fail for reasons that have nothing to do with the claim. The preset gains `asset_hub_ws` and `asset_hub_genesis`, and the Asset Hub entry in `live_chain_endpoints` now carries the genesis the chain actually reports. The previous value predates a testnet redeployment, and a wrong hash there is worse than a stale one elsewhere: routing matches on it, so a host asking for Asset Hub silently reached the People chain instead. Verified against paseo: a claim was submitted for day 20678, took slot 0, and the next scan advanced to slot 1, so the spent-alias accounting holds. A second claim took slot 1.
…ailable `SmartContractAllowance` was protocol-only: both allocation paths answered `NotAvailable` regardless of what the host could do. They now claim, through one allocator shared by the direct and SSO paths. PGAS differs from the other two allowances in what it credits. The statement-store and Bulletin allowances derive a dedicated `//allowance//…` account and hand its key back; PGAS pre-warms a balance on the product account the request names, so it takes a `DerivationIndex`, credits `product_keypair`, and returns nothing. That matches `SsoAllocatedResource::SmartContractAllowance`, which already carried no key material. Asset Hub is resolved through the host's chain set rather than a configured hash. `features::genesis_for` answers which genesis the host serves for a role, and `chain_info` now sits on it so the product-facing method and in-core callers cannot disagree. A host that serves no Asset Hub reports that plainly, which is better than reaching whatever chain a stale hash happens to route to. `ChainRpcClient` replaces `BulletinRpcClient`: two chains use it now and its `context` field already said which. No host currently returns an Asset Hub entry, so this answers `NotAvailable` in production until they do — the claim itself is verified against paseo. The batch test asserted `SmartContractAllowance => NotAvailable`, which the stub made true for free. It now fails the connect deliberately, so what it pins is that one resource failing does not poison the others. Asserting the outcome alone cannot distinguish a real claim from the old stub, so a second test polls the allocation once and requires it to be waiting on the Asset Hub connection.
24 tasks
`ChainNotServed` and `SupportedChains` are only constructed by the native allocator, so on `wasm32-unknown-unknown` they are unreachable variants. CI checks that target with `-D warnings`, which makes an unconstructed variant an error. Both are now gated to native, as `ProductAccount` in the same enum already is.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SmartContractAllowancewas protocol-only: both allocation paths answeredNotAvailableregardless of what the host could do. They now claim PGAS on Asset Hub, through one allocator shared by the direct and SSO paths.Shape
Two chains take part. Membership and its ring revision come from the People chain; the claim is submitted on Asset Hub, which learns about People's rings through
MembersSubscriber. A proof can only be verified against a revision Asset Hub has already imported, so the flow waits for it rather than submitting something theruntime cannot check. Asset Hub keeps only its most recent roots per ring, so a revision it does not hold is either still arriving or gone for good,
await_ring_revisiontells those apart, because the first is worth waiting for and the second means rebuilding the proof against a newer ring.PGAS differs from the other two allowances in what it credits. The statement-store and Bulletin allowances derive a dedicated
//allowance//…account and hand its key back; PGAS pre-warms a balance on the product account the request names, so it takes aDerivationIndex, creditsproduct_keypair, and returns nothing. That matchesSsoAllocatedResource::SmartContractAllowance, which already carried no key material.Asset Hub is resolved through the host's chain set rather than a configured hash.
features::genesis_foranswers which genesis the host serves for a role, andchain_infonow sits on it so the product-facingget_chain_infoand in-core callers cannot disagree about what the host serves.Groundwork
build_proof_message,build_unsigned_extrinsic, and the two info-variant lookups each hardcodedAsResources; they now take the extension identifier, with theAsResourcesforms as thin wrappers. The field-enum lookup changed shape rather than gaining a parameter — it located the collection by the type nameMembershipCollection, whichAsPgasdoes not have (it names its ownPgasCollection), so it now finds the enum by the variant it contains. Both generalized builders read the transaction-extension version from metadata, so the second authorizing extension encodes for the pipeline the runtime declares.A routing bug fixed on the way
The Asset Hub entry in the CLI's
live_chain_endpointscarriedbf0488db…while the chain reports23e730eb…. Routing matches on that hash, so a host asking for Asset Hub silently reached the People chain instead, andChainContextCacheonly warns on genesis divergence — the claim would then have run against People metadata under an Asset Hub cache key. Corrected to the value the chain reports.Scope
Part of #334, covering "Make sure that PGAS allowances are supported".
No host currently returns an Asset Hub entry from
supported_chains— the CLI errors, the JS and iOS bridges return empty sets, and dotli does not implement it (though it already has the Asset Hub config and connect path). So this answersNotAvailablein production until that lands, which is a follow-up in the host repos. The claim itself is verified below.Verification
Offline: the alias context and spent-alias key layouts, the five-field
AsPgas::Claimpayload, the shared lookups against theirAsResourceswrappers, and the chain-set resolver. A test polls a PGAS allocation once and requires it to be waiting on the Asset Hub connection, asserting the outcome alone cannot distinguish a real claim from the previous stub, since both answerNotAvailable.Live against paseo Asset Hub (
next-asset-hub-paseo, spec 2000036) and the People chain. Before writing anything, every on-chain assumption was checked:AsPgas::Claimdeclares exactly the five fields encoded here,Pgasis pallet 99 withclaim_pgas(u32, AccountId32), both storage keys hash as expected, andMaxClaimsPerPeriodPerLitePersonis 40.tests/live_asset_hub.rsthen covers the runtime serving V16,RingCommitmentRecorddecoding the realRingRootsvalue, and a long-pruned revision reporting as pruned rather than waiting out the timeout.A claim was submitted for day 20678 and took slot 0; the next scan advanced to slot 1, and a second claim took it. The claim retries against a different slot on a duplicate submission, as the statement-store and long-term-storage claims do and the flow this was ported from did not.
pgas-checkmirrorsalloc-checkfor this path and connects to both chains directly: the host'sChainProviderdrops Asset Hub unlessE2E_LIVE_CHAIN=1and then falls back to the People chain, so a claim routed through it would fail for reasons unrelated to the claim.