feat(control-plane): read/write pallet-resource-market offers - #70
Merged
Conversation
Second slice of #15, on top of the delegated calls from #69. internal/blockchainbridge/resourcemarket.go: - AnnounceOfferFor/RemoveOfferFor submit announce_offer_for/ remove_offer_for, mirroring EnsureActive's proven submission shape exactly (serialize nonce use under the same mutex, sudo-wrap, sign, submit). Not live-verified: the locally running dev chain predates this pallet change and I have no way to rebuild/redeploy it in this sandbox (no libclang for openinfra-node), so the encoding is built and unit-tested against the same primitives EnsureActive already uses in production, not against a live extrinsic acceptance. - FinalizedOffer/decodeResourceOffer read pallet-resource-market's Offers map (single-key Blake2_128Concat, same shape as the reputation/validator reads from #46-#49) and decode cpu/ram/storage (fixed u32+u64+u64) plus capabilities (compact-length-prefixed bytes, the same shape decodeAccountIdVec already handles for the validator set). This is live-verified: Offers storage is unchanged by #69, so it ran against the running local dev chain during development -- correct "no offer yet" for every registered provider, no decode errors. ResourceOffer documents this bridge's own unit convention explicitly (CPU in millicores, matching workloadapi.CPUCoresToMillicores and the scheduler; RAM in MB, storage in GB, matching ResourceCapability on the wire) since the pallet's u32/u64 fields carry no unit themselves -- issue #15 asks for units to be defined, not left implicit. Adds 5 tests: fixed-field + capabilities round trip across several shapes including empty capabilities; truncated and trailing-byte inputs both rejected (a decoder that silently ignores trailing bytes would also silently accept a corrupted encoding); encodeBoundedBytes matches the existing compact decoder; storage key is deterministic and provider-distinguishing. Verified (control-plane/): go build ./...; go vet ./...; gofmt -l .; go test ./... (full suite green). Still open in #15: a reconciler that actually calls Announce/ RemoveOfferFor as provider capacity/status changes, the scheduler checking finalized on-chain offers before selecting a provider, and integer pricing (needs a proto change + consumer analysis, out of scope for a bridge-layer change). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
flo2517
added a commit
that referenced
this pull request
Aug 6, 2026
…e capacity (#71) Third and final slice of #15, on top of the delegated calls (#69) and the bridge read/write layer (#70). internal/resourcemarket/reconciler.go: - ReconcileOnce lists the same live schedulable-provider view the scheduler ranks against, computes each provider's desired ResourceOffer from its declared *total* capacity (the ceiling the scheduler's atomic Postgres capacity check already uses -- not the fast-changing "available" figure, which stays off-chain in Redis by design), compares it to the chain's FinalizedOffer, and calls AnnounceOfferFor only when it actually differs. Providers that drop out of the schedulable set (deregistered, or heartbeat went stale) have their offer withdrawn via RemoveOfferFor. - Withdrawal tracking (the offering map) is in-memory, not persisted: after a Control Plane restart, a provider that vanished during the outage keeps a stale on-chain offer for up to one reconcile interval before it's noticed and removed. This is a deliberately bounded, self-healing gap, not a security control -- no scheduling decision consults on-chain offers yet, so a briefly-stale offer has no live consequence today. - clampToUint32 protects the pallet's u32 CPU field from a total large enough to overflow it, instead of silently wrapping. Wired into cmd/controlplane/main.go via a marketBridge adapter that combines *blockchainbridge.Registrar's write methods with *blockchainbridge.RPCClient's read methods into the resourcemarket.Market interface the reconciler depends on, run alongside the existing provider-join outbox reconciler. Adds 8 tests: announces a new provider, skips an already-correct offer, updates a changed offer, withdraws a provider that drops out of the schedulable set (and forgets it afterward), skips providers without a usable 32-byte key or capabilities, tolerates a directory read failure without panicking, keeps retrying a failed announce on the next pass, and clampToUint32's boundary cases. Verified (control-plane/): gofmt -l .; go build ./...; go vet ./...; go test ./... -- full suite green against merged main, including this package (0.027s). This closes out #15's three planned slices. Still explicitly out of scope, left for follow-up: the scheduler consulting finalized on-chain offers before selecting a provider, and integer pricing (needs a proto change + consumer analysis). Co-authored-by: FlorianJeandenans <florian.jeandenans@skin-soft.org> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.
Summary
Second slice of #15, on top of the delegated calls from #69.
internal/blockchainbridge/resourcemarket.goAnnounceOfferFor/RemoveOfferForsubmitannounce_offer_for/remove_offer_for, mirroringEnsureActive's proven submission shape exactly (serialize nonce use under the same mutex, sudo-wrap, sign, submit). Not live-verified: the locally running dev chain predates this pallet change and I have no way to rebuild/redeploy it in this sandbox (nolibclangforopeninfra-node), so the encoding is built and unit-tested against the same primitivesEnsureActivealready uses in production, not against a live extrinsic acceptance. Flagging this explicitly rather than implying more confidence than I have.FinalizedOffer/decodeResourceOfferreadpallet-resource-market'sOffersmap (single-keyBlake2_128Concat, same shape as the reputation/validator reads from #46–#49) and decodecpu/ram/storage(fixedu32+u64+u64) pluscapabilities(compact-length-prefixed bytes, the same shapedecodeAccountIdVecalready handles for the validator set). This is live-verified —Offersstorage is unchanged by #69, so I ran it against the running local dev chain during development: correct "no offer yet" for every registered provider, no decode errors.ResourceOfferdocuments this bridge's own unit convention explicitly (CPU in millicores, matchingworkloadapi.CPUCoresToMillicoresand the scheduler; RAM in MB, storage in GB, matchingResourceCapabilityon the wire) since the pallet'su32/u64fields carry no unit themselves — issue #15 asks for units to be defined, not left implicit.Tests (5 new)
Fixed-field + capabilities round trip across several shapes including empty capabilities · truncated and trailing-byte inputs both rejected (a decoder that silently ignores trailing bytes would also silently accept a corrupted encoding) ·
encodeBoundedBytesmatches the existing compact decoder · storage key is deterministic and provider-distinguishing.Verification
Full suite green.
Still open in #15
A reconciler that actually calls Announce/RemoveOfferFor as provider capacity/status changes, the scheduler checking finalized on-chain offers before selecting a provider, and integer pricing (needs a proto change + consumer analysis, out of scope for a bridge-layer change).
🤖 Generated with Claude Code