Skip to content

Commit 56d8f83

Browse files
fix: address access pass CI checks
1 parent 2da57be commit 56d8f83

2 files changed

Lines changed: 8 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
1111
- CI
1212
- The Agave toolchain install retries, and a failed one now fails the job. Eight workflow steps across five workflows ran `sh -c "$(curl -sSfL .../install)"` once with no retry, so a transient reset from `release.anza.xyz` killed a job before it ran anything. That form also swallowed a failed fetch: the command substitution comes back empty, `sh -c ""` exits 0, and the step passed having installed nothing. The eight are now one composite action at `.github/actions/solana-toolchain` that fetches and runs as separate steps, checks `solana --version` actually runs, clears partial state between attempts, bounds every wait so a stalled handshake or hung transfer reaches the backoff instead of sitting until the job times out, and backs off. Each caller keeps the version it used before; `solana.yml` and `offchain.local-validator.yml` are on v3.0.12 and the other three on v3.0.4, which is drift worth settling separately.
1313
- Serviceability
14+
- New user accounts record the selected access-pass address, and deletion rejects another pass while preserving legacy address validation.
1415
- `write_stake_mirror` in the instruction crate and `WriteStakeMirrorCommand` in the Rust SDK, so the relayer has a caller-side surface for the instruction A6 added. The command departs from its neighbours in one way: every other one uses `append_payer_permission_account`, which attaches the caller's `Permission` account only when it already exists, because a legacy `GlobalState` key might authorize instead. No legacy key satisfies `STAKE_ORACLE`, so a missing `Permission` account is always fatal here, and the command says so locally rather than sending a transaction that can only come back `NotAllowed`. It checks what `authorize` checks, not just ownership: an account that does not decode, a suspended `Permission`, and one lacking the flag are each refused with the reason named, which matters because suspending a `Permission` is what revoking the relayer's key looks like.
1516
- New `WriteStakeMirror` instruction (variant 119) and a `STAKE_ORACLE` permission, which is what a relayer will call to copy a builder's Solana stake onto the DZ ledger. Nothing could write a `StakeMirror` before this. The instruction refuses a write whose `source_slot` is not newer than the stored one: polling makes a repeated write harmless but says nothing about ordering, and a retry carrying an older read could otherwise walk the mirror backwards, so the program enforces it rather than trusting the caller. It also refuses to reassign a mirror to a different builder, since the builder is not a PDA seed, and it carries `feed_key` forward rather than taking it from the caller, because `CreateFeed` writes that to spend the stake and zeroing it would let one bond back two feeds. No legacy GlobalState key maps to `STAKE_ORACLE`, so a holder needs a real `Permission` account even while `require-permission-accounts` is clear. Gated on `allow-staked-feeds` with the rest of RFC-28. `STAKE_ORACLE` is grantable through `doublezero permission set --add stake-oracle`, named by `permission audit` and `bitmask_to_names`, listed in `AUTHORIZE_GATED_FLAGS` and in the Go SDK's flag constants. It is the first flag no legacy `GlobalState` key can satisfy, which the audit's own test now asserts through a `PERMISSION_ONLY_FLAGS` list rather than treating as a gap in the enumeration.
1617
- `Feed` carries the RFC-28 stake terms: `builder`, `stake_ref`, `spec_id`, `sla_hash`, `committed_rate_bits_per_sec` and a lifecycle `status`. Setting them needs the new `allow-staked-feeds` feature flag, which no cluster has, so `CreateFeed` refuses a builder until the stake mirror and the attestor exist; without the flag the instruction behaves as before. The fields are appended rather than versioned, so a feed written before this decodes with them defaulted and `try_acc_write` resizes the account on the next update. `status` is the exception to defaulting: a short account reads `Active`, because reading it as `Pending` would pull every live catalog feed out of service. The rate is bits per second, not basis points, which is what `bps` means elsewhere in DoubleZero.
@@ -19,6 +20,8 @@ All notable changes to this project will be documented in this file.
1920
- A feed admits a subscriber only while its status is `Active`. `SubscribeFeed` and `CreateSubscribeUser` both check it where the seat is spent, so a feed that is pending conformance, halted, or retired stops taking subscribers the moment its status changes, and retiring one needs no sweep over the access passes already holding a seat for it. The check is deliberately not in the shared coverage path, which `UnsubscribeFeed` also runs: gating there would leave a user holding a seat on a retired feed with no way to release it. New error `FeedNotActive` (123). Live subscribers are unaffected until they disconnect; evicting them is separate work.
2021
- Test coverage for the RFC-28 publish-rights path, which needs no new instruction. A feed's multicast groups are created with `owner` set to the builder, and `AddMulticastGroupPubAllowlist` authorizes on `mgroup.owner == payer`, so the builder grants its own publish rights without the catalog admin that created the feed. The test walks it with two distinct signers.
2122
- SDK
23+
- User deletion and disconnect select the recorded pass, while legacy conflicts list both candidates and require `--access-pass`.
24+
- Go, Python, and TypeScript readers expose the recorded address and decode legacy accounts with the default public key.
2225
- The Rust SDK sends program logs and errors through the log facade instead of straight to stderr. A failed transaction's program logs now arrive as one `error!` record carrying the signature, so a service embedding the SDK can capture, filter and ship them. Four `eprintln!` sites bypassed logging entirely, while the same function logged the successful path at debug level: an operator saw every transaction that worked and got raw stderr for one that failed. RFC-20 already asks modules to use the standard log macros. One behavior change for the CLI: `--log-level off` silences program logs, which previously printed whatever the level (#4299).
2326
- `sdk/shreds/go` carries the feed subscription program's `FeedDistribution` account: how much USDC one feed collected for one calendar month. The program is a second program alongside shred subscription and had nothing in this SDK, so each consumer decoded the account at fixed byte offsets itself, lake included. The account is a bytemuck Pod read here field by field, which agrees with the Pod bytes only because the field order leaves no interior padding; `TestStructSizes` pins the 120-byte total and a new test pins every field against a real mainnet account. `Client` is built around one program ID and so gains no fetch method, and `DeserializeFeedDistribution` is exported for a caller that makes its own `getProgramAccounts` call. `make sdk-test` never ran `./sdk/shreds/go/...`, so this package's layout pins have never run in CI; it runs them now. (#4216)
2427
- The TypeScript and Python `Feed` deserializers read the RFC-28 tail and synthesize `Active` for an account that carries no status byte, matching the Rust program. New `feed_legacy` fixture covers that path alongside the updated `feed` fixture.

sdk/serviceability/typescript/serviceability/tests/fixtures.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,9 +408,11 @@ describe("User fixture", () => {
408408
expect(expected).toBeDefined();
409409
expect(u.feedPks[i].toBase58()).toBe(expected!);
410410
}
411-
expect(u.accessPassPubKey.toBase58()).toBe(
412-
meta.fields.find((f) => f.name === "AccessPassPk")?.value,
413-
);
411+
const expectedAccessPass = meta.fields.find(
412+
(f) => f.name === "AccessPassPk",
413+
)?.value;
414+
expect(expectedAccessPass).toBeDefined();
415+
expect(u.accessPassPubKey.toBase58()).toBe(expectedAccessPass!);
414416
});
415417

416418
test("backward compat: old layout yields zero for new fields", () => {

0 commit comments

Comments
 (0)