These commits are when the Protocol Buffers files have changed: (only the last 100 relevant commits are shown)
| Commit: | 610d7c0 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
docs: align channel owner-read and membership-mode comments with shipped behavior The MembershipMode proto comment predated enforcement landing; the GetChannelOwner comment described a cross-shard LWW the read no longer performs; the GetChannelsByFid comment claimed an iff whose converse fails once the home shard prunes a verification the permanent shard-0 replica retains. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | be62f2e | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
docs: clarify channel read wire contract and pin collapse ChannelMetadataResponse modes are the resolved effective policy (never NONE); state_filter NONE means unfiltered; and get_channel_pin intentionally collapses "unpinned" and "never pinned" into an empty response. Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 3a0888a | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
feat: add channel message read APIs Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 27d72b7 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
fix: align channel owner reads with consensus Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 3404012 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | GitHub | |
feat: add channel message protos (#973) Adds the four channel message types from the native-channels proposal (farcaster protocol discussion #276) as wire shapes only: CHANNEL_UPDATE, CHANNEL_MEMBER, CHANNEL_PIN, and CHANNEL_MODERATE (MessageType 18β21), with their bodies (ChannelUpdateBody, ChannelMemberBody with a six-action member/moderator/ban state machine, ChannelPinBody, ChannelModerateBody). channel_id everywhere is the 32-byte keccak256 label matching the onchain channel registry's tokenId. No engine merges these types: validation rejects them on every network, and they are gated behind the V20 engine version for every future consumer. Adding a MessageType is not inert β the enum feeds shared try_from / variant-count code that live mainnet KEY_ADD validation depends on β so this PR pins that boundary explicitly: new types are excluded from live KEY_ADD scope validation, the scope-count limit is pinned by test, and the new enums' JSON read surface and presence semantics are pinned variant-by-variant. π€ Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches consensus-critical KEY_ADD scope validation on mainnet; mistakes could fork nodes at the same engine version. Channel protos are rejected everywhere today, so merge/state risk is low until ChannelMessages is wired with real validation. > > **Overview** > Adds **four channel message types** (`CHANNEL_UPDATE`, `CHANNEL_MEMBER`, `CHANNEL_PIN`, `CHANNEL_MODERATE`, types 18β21) to `message.proto` with bodies keyed by **32-byte `channel_id`** (keccak256 registry label), plus enums for casting/membership modes, member actions, and moderation actions. > > **Nothing merges yet:** stateless validation returns `InvalidMessageType` for all channel bodies; replication and stores treat them as inert; **`ProtocolFeature::ChannelMessages`** is added at **V20** and locked to activate with the other channel features. HTTP JSON mapping and round-trip tests cover the new shapes for reads only. > > Because new `MessageType` values affect **live mainnet KEY_ADD consensus**, the PR **pins** gasless-key scope rules: explicit allow/deny lists (channel types denied), **`MAX_KEY_ADD_SCOPES` fixed at 16** (not tied to enum size), and tests that reject channel scopes and exhaustively classify every variant. Shard/Block engine tests assert channel messages stay rejected at validation and merge. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3e8ca41eb5100d1ca53d0c93ff454794a32b6b1e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Matthew Bond <16600437+bond00729@users.noreply.github.com> Co-authored-by: Codex gpt-5 <codex@openai.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The documentation is generated from this commit.
| Commit: | f10aa94 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | GitHub | |
feat: emit channel ownership-change hint events (#964) ## Summary Adds the ownership-change **hint** layer from [FIP: Channels on Snapchain](https://github.com/farcasterxyz/protocol/discussions/276) (item 5): a new `ChannelOwnerChangeHint` HubEvent that tells consumers *when* a channel's owner may have changed so they can re-read it via `GetChannelOwner`. Hints are best-effort re-read triggers β they carry no resolved owner and no fid, because the addressβfid winner cannot be computed deterministically at merge time (the same reason resolution is a read-time operation in the base stack). Builds on the channel-registration read stack (#957β#960). Everything is dormant behind the unscheduled V20 engine version: `ProtocolFeature::ChannelOwnershipEvents` co-activates with `ChannelRegistrations` and `SortedBlockEngineEvents`, so with V20 unscheduled on mainnet and testnet, merging this changes nothing on any network. Co-activation with `ChannelRegistrations` also means no channel registration can predate the feature, so no replica backfill is needed. ## What's in it - **Wire contracts + gate** β on the HubEvent stream: `HUB_EVENT_TYPE_CHANNEL_OWNER_CHANGE_HINT = 12`, `ChannelOwnerChangeHintBody { channel_key, owner_address, cause }`, and a `ChannelOwnerChangeCause` enum (`NONE`/`REGISTER`/`TRANSFER`/`VERIFICATION_ADD`/`VERIFICATION_REMOVE`). On the block stream: `BLOCK_EVENT_TYPE_MERGE_ON_CHAIN_EVENT = 2` + `MergeOnChainEventEventBody` (carries the whole `OnChainEvent`, mirroring `MergeMessageEventBody`). All inert until the emission paths below activate. - **Fan-out replica + REGISTER/TRANSFER hints** β shard 0 fans each channel-register onchain-event merge to every data shard via the new block event; each data shard runs the *existing* channel-register fold against its own `OnchainEventStore`, building a per-shard, trie-free ownership replica (secondary indexes only, no primary event, no trie leaf). A REGISTER or TRANSFER that changes the recorded owner emits a hint. A behavior-preserving refactor lets the fold report its ownership outcome to the new caller while staying byte-identical for the two existing consensus callers. - **Verification hints** β when an Ethereum verification merges, a hook scans that shard's `ByOwnerAddress` replica for the verified address and emits a `VERIFICATION_ADD`/`VERIFICATION_REMOVE` hint per affected channel. The hook is structurally unable to fail, panic, or alter the merge (it only appends to the returned event vec; every fallible step warns and skips). Fan-out per verification is capped at 256 hints to bound the shared per-block HubEvent id budget (see Design notes). ## Design notes - **No fid, no resolved owner in the payload.** A hint is a nudge, not authoritative data. `owner_address` is the raw registry address the change concerns (a filter key, `bytes` per the codebase convention), not a resolution β consumers call `GetChannelOwner`. Renewals emit no hint (extending expiry is not an ownership change), and the `cause` enum is intentionally open (an unrecognized cause means "re-read"). - **Coverage is best-effort, at-least-once across the shard set, and may over-emit.** REGISTER/TRANSFER hints ride the fan-out; the latest update of a channel hints on every shard, but a single shard may coalesce a rapid same-channel burst down to just the latest hint. VERIFICATION\_\* hints appear only on the verifier's home shard. Complete coverage means subscribing to all shards and treating every hint as a re-read trigger; `GetChannelOwner` is always authoritative. - **The replica and hints are derived, node-local state.** The fan-out block event is consensus-ordered and V20-gated (with the feature off, block `events_hash` is byte-identical to base). The replica it builds is secondary-index-only and never enters the merkle trie; hint events map to zero trie keys and are not consensus-hashed. - **The 256-per-verification cap** keeps one verification from drawing more than a small fraction (~1.5%) of the 16384-events/block HubEvent id budget. Truncation is deterministic (ascending index-key order β same survivors on every node) and contract-legal: an owner of >256 channels who verifies loses hints for the overflow that block, and consumers reconcile via `GetChannelOwner`. - **V20 sequencing.** The three co-activation features share one `>= V20` arm and must merge before V20 is scheduled; a pinned test asserts they activate together. ## Testing ~30 new tests: shard-0 emission (feature on/off byte-identity), block-event application on data shards, REGISTER/TRANSFER and VERIFICATION\_\* hint emission, the burst-coalescing limitation, the fan-out cap and its deterministic truncation, malformed-verification-body no-panic, and trie-discipline (the hint contributes no trie keys). `cargo fmt --check` and `RUSTFLAGS="-Dwarnings -A mismatched_lifetime_syntaxes" cargo check --bins` clean; the stack's new tests pass. π€ Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches consensus-adjacent block-event replay, per-shard derived indexes, and a new public event API, but gates emission at V20 and keeps hints out of the merkle trie; main risk is consumer misunderstanding of at-least-once/coalesced hint semantics or replica drift if fold errors are only warned. > > **Overview** > Introduces **channel ownership-change hints** behind **`ProtocolFeature::ChannelOwnershipEvents`** (co-activated with channel registrations at **V20**, dormant on mainnet/testnet until scheduled). > > **Wire/API:** New `HUB_EVENT_TYPE_CHANNEL_OWNER_CHANGE_HINT` with `ChannelOwnerChangeHintBody` (`channel_key`, raw `owner_address`, `cause`). New block type **`BLOCK_EVENT_TYPE_MERGE_ON_CHAIN_EVENT`** carrying the full channel-register `OnChainEvent`. CLI subscribe defaults and HTTP JSON mapping include the new hub event. > > **Shard 0 β data shards:** When V20 is active, shard 0 emits a **`MergeOnChainEvent`** block event for each merged channel-register onchain event. Data shards replay it via **`fold_channel_register_replica`** (secondary indexes onlyβno trie) and emit a hint on **REGISTER** or **TRANSFER** that actually changes the recorded owner; **RENEW** updates expiry with no hint. > > **Verification path:** After a successful Ethereum **verification add/remove** merge, the engine scans the shardβs **ByOwnerAddress** replica and emits up to **256** hints per verification (bounded HubEvent id budget). Hints are **trie-free** and cannot fail the merge. > > **Operational notes:** Hint streams are best-effort and may coalesce on some shards; consumers should treat hints as βre-readβ triggers and use **`GetChannelOwner`** for authority. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 70e0ec583a5dbe073091d03de894c8dc90a39f10. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Matthew Bond <16600437+bond00729@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: topocount <17910833+topocount@users.noreply.github.com>
| Commit: | ce3f662 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
docs: correct comments and test name now that emission ships The "inert until emission ships" wire-contract comments (blocks.proto BlockEventType + MergeOnChainEventEventBody) and the factory helper comment predate the emission path; this change ships the emission and fold, so they now describe the V20-gated behavior instead. Rename test_channel_registrations_and_sorted_events_activate_together to ..._sorted_events_and_ownership_activate_together to match the three-way co-activation it now asserts, and update its doc reference. No behavior or wire-format change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 9efce20 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
docs(proto): correct ChannelOwnerChangeHintBody per-shard hint guarantee The consumer contract claimed REGISTER/TRANSFER hints appear on EVERY shard's stream. That is not guaranteed: the one data shard that also merges a channel event via its system-message path advances its owner index ahead of the fanned replay, so a rapid same-channel burst can be coalesced down to just the latest hint on that shard (the earlier events lose the shard-local strict-`<` LWW and emit no hint there). The latest update of a channel still hints on every shard, and the final owner state converges everywhere. Restate the guarantee accurately: no single shard's stream is guaranteed to carry every hint, so complete coverage means subscribing to all shards and treating each hint as a re-read trigger. Comment-only; no wire change (corpus regenerates with no diff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 4cbfccb | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
docs(proto): sharpen ownership-events wire-contract comments Refine the frozen doc comments on the ownership-events proto types ahead of merge (both the wire format and the consumer-contract text freeze at merge, so this is the last chance to get the wording right): - ChannelOwnerChangeHintBody: replace "best-effort" (reads as lossy delivery) with "a nudge, not authoritative data", removing the contradiction with the at-least-once guarantee stated one line below; tie at-least-once to the full shard set; make owner_address cause-accurate (newly-recorded owner for register/transfer, the verification-changed owner for verification causes); document channel_key as the argument to pass to GetChannelOwner. - ChannelOwnerChangeCause: document NONE as the never-emitted proto3 zero default and that the cause set is open (unknown -> re-read). - MergeOnChainEventEventBody: hedge the "channel fold consumes it" claim to future tense (inert until emission ships). Comment-only; no field numbers, enum values, or wire encoding change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 75438e5 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
feat: add dormant wire contracts + gate for channel ownership hints Land the frozen parts of the channel ownership-change hint feature ahead of emission: two wire contracts and a dormant feature gate, so later increments build against stable types while nothing observable changes on any network. - blocks.proto: BLOCK_EVENT_TYPE_MERGE_ON_CHAIN_EVENT (2) + MergeOnChainEventEventBody carrying the whole OnChainEvent, the shard-0 -> data-shard fan-out carrier. - hub_event.proto: HUB_EVENT_TYPE_CHANNEL_OWNER_CHANGE_HINT (12), ChannelOwnerChangeCause enum (RENEW intentionally absent), and ChannelOwnerChangeHintBody. The body doc comment is the public consumer contract: at-least-once, may over-emit, no fid claims (call GetChannelOwner), coverage requires subscribing to all shards. - version.rs: ProtocolFeature::ChannelOwnershipEvents folded into the V20 co-activation arm so its boundary is a compile-time guarantee, not just a test; the co-activation test now pins all three V20 features. - engine.rs handle_block_event: gated admission arm for the new body. Inactive -> InvalidMessageType (caller warns); active -> Ok(vec![]) no-op. Emission and the replica fold ship together in a later increment, so the arm is unreachable until then; the gate ships with the type to avoid a pre-gate replay divergence. - http_server.rs: full JSON mapping for the hint body + round-trip test. - merkle_trie for_hub_event: the hint is trie-free (subscriber-only), like MergeFailure/BlockConfirmed, so it never affects state roots. V20 stays unscheduled on mainnet/testnet; devnet keeps its V20 entry. No emission, no application logic, no behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | af80584 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | GitHub | |
feat: add channel owner resolution and read APIs (#960) ## Summary The read layer for channel registrations (final PR of the stack: #957, #958, #959). Adds three RPCs plus HTTP routes: - `GetChannelOwner(channel_key)` β `{ fid, owner_address, expiry }` (`GET /v1/channelOwner`) - `GetChannelsByAddress(owner_address)` β paged `ChannelInfo` list (`GET /v1/channelsByAddress`) - `GetChannelsByFid(fid)` β paged `ChannelInfo` list (`GET /v1/channelsByFid`) No merge-path changes β every production change in this PR is read-side. ## How resolution works Ownership base state (channel key β owner address + expiry) comes from the shard-0 fold, which exists on every node. The addressβfid hop happens at query time: scan each hosted shard's verification by-address index for candidates, re-validate each against primary `VerificationAdd` state, and pick the last-write-wins winner by `ts_hash`. No winner β `fid: 0` ("parked"), returned with the owner address and expiry so parking is visible. Parking and binding are computed, never stored β a verification merging anywhere changes the answer on the next read, and removing one falls back to the next-best verifier. **Consistency class** (documented on the RPCs): resolution covers only the shards a node hosts, and shards progress independently β same class as `GetOnChainEvents`. A partial node can return a stale or parked fid; only a node hosting all data shards is guaranteed the globally-correct owner. **Expiry contract**: expiry is returned as-is and may be in the past. The registry emits no onchain event when a lapsed registration's grace period ends, so the endpoints report the last known registration until a later one supersedes it; callers interpret `expiry` (per the FIP, a lapsed channel is still owned and renewable during grace β only management freezes). `NOT_FOUND` means only that no registration record exists. **Pagination**: by-address pages over the owner-address index; by-fid is a joined read over the fid's verified addresses Γ the owner-address index, using the index key as a composite cursor (one-directional; page until `next_page_token` is unset). ## Testing End-to-end scenario tests at the read layer: parked β verified delayed flip, last-verifier-wins independent of merge order (both shard directions), verification-remove fallback to survivor then to parked, cold-wallet transfer round-trip with expiry preserved, lapsed registrations returned/listed, `GetChannelsByFid` βΊ `GetChannelOwner` invariant asserted in both directions, cursor round-trips and exact-boundary final-empty-page. Full gates clean (`cargo test --lib`: 860 passed). π€ Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > New public read paths with node-local consistency and non-trivial join/pagination semantics; incorrect resolution or paging could mislead clients about channel ownership, though merge/ingestion behavior is largely unchanged. > > **Overview** > Adds **read APIs** for channel registry state: gRPC `GetChannelOwner`, `GetChannelsByAddress`, and `GetChannelsByFid`, plus matching **HTTP GET** routes (`/v1/channelOwner`, `/v1/channelsByAddress`, `/v1/channelsByFid`) with JSON types and hex/base64 encoding for addresses and page tokens. > > **Resolution behavior** is implemented on the hub server: registry **owner address + expiry** come from shard-0 storage; **fid** is computed at read time by scanning hosted shardsβ verification indexes, re-validating primaries, and picking a **last-write-wins** verifier (`fid: 0` = parked). List endpoints page the owner-address index; **by-fid** joins verified EVM addresses with that index via a **composite cursor**, filters to addresses the fid currently wins, and clamps page size. > > Storage gains **`get_channel_keys_for_owner_addresses`** for multi-address paging; index maintenance on merge now routes through the shared **validated** put/delete helpers. On-chain ingestion comments are updated to refer to **`decode_log_validate`** (no behavior change). **Broad server tests** cover parked/lapsed/LWW, listing invariants, pagination boundaries, and malformed addresses. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f95a1f7a5b64e90d6505a8a590dd60bbeeebe9ad. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: topocount <17910833+topocount@users.noreply.github.com> Co-authored-by: Codex GPT-5 <codex@openai.com> Co-authored-by: Matthew Bond <16600437+bond00729@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 6917a25 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | GitHub | |
feat: ingest channel registrations as onchain events (#957) ## Summary Adds ingestion for the channel registration events described in [FIP: Channels on Snapchain](https://github.com/farcasterxyz/protocol/discussions/276) (Β§1β2). Snapchain watches a stock Basenames registrar (`NameRegistered` / `NameRenewed` / `Transfer`) and folds the events into queryable ownership state: channel key β owner address + expiry. The pipeline mirrors the existing TierPurchase onchain-event path end to end (connector β mempool β engines β store indexes). First of a 4-PR stack (with #958, #959, #960). Everything is dormant behind the unscheduled V20 engine version: both engines drop `EVENT_TYPE_CHANNEL_REGISTER` events at admission until `ChannelRegistrations` activates, and the connector has no default contract address, so merging this changes nothing on any network. ## What's in it - **proto + feature gate** β `EVENT_TYPE_CHANNEL_REGISTER = 6`, `ChannelRegisterBody`, `EngineVersion::V20` + `ProtocolFeature::ChannelRegistrations` (scheduled on devnet only), admission gates in both ShardEngine and BlockEngine (onchain events route `[0, fid_shard]`, so shard 0 needs the gate too), full JSON API mapping. - **connector** β watches the three registrar events, submits them as onchain events. Names are decoded with `validate = true`; a name that isn't valid UTF-8 fails ABI decode identically at every connector and is dropped deterministically (documented in the proto β the contract's own validation is length-only). - **store fold** β derived indexes: `ByChannelKey` (key β owner address, expiry, ordering metadata), `ChannelKeyByLabel` (the 32-byte `keccak256(name)` label is the only identity a `Transfer` carries), and `ByOwnerAddress` (address β channel keys). REGISTER overwrites, RENEW overwrites the absolute expiry, TRANSFER joins via label and preserves expiry, mint-transfers dedup, malformed inputs skip the index fold but still merge as raw events. ## Design notes - **Events carry `fid = 0` permanently.** The registrar emits an owner _address_; the addressβfid mapping lives in verification state, which is sharded by the verifier's fid β no merging shard can see enough of it to resolve a globally-correct owner, and a stamped fid would go stale whenever verifications change. Resolution therefore happens at read time (final PR of the stack); nothing in the merge path ever consults the event fid. - The derived indexes are node-local state and are never consensus-hashed; no merge path reads them. - `ChannelRegisterBody` carries an `event_type` sub-enum plus the `label` join key (in addition to the FIP's `channel_key` / `expiry` / `owner_address`) because `NameRenewed` carries no owner and `Transfer` carries only the tokenId β a shared body with a type tag is the same shape `IdRegisterEventBody` uses. ## Testing Store fold tests (renewal overwrite, transfer rebind, re-registration, mint-transfer dedup in both orders, unknown-label and malformed-input skips), feature-gate activation tests in both engines, JSON round-trip. `cargo fmt --check`, `RUSTFLAGS="-Dwarnings ..." cargo check --bins`, and `cargo test --lib` all clean. π€ Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches onchain ingestion, merge/replay gates, and new derived state; behavior is intentionally inert on mainnet (no registrar address, V20 unscheduled) but connector dispatch and store fold logic are subtle and worth careful review. > > **Overview** > Adds end-to-end support for **channel registration** onchain events (`EVENT_TYPE_CHANNEL_REGISTER`), wired like tier purchases but dormant until **Engine V20** / `ProtocolFeature::ChannelRegistrations` (devnet-only schedule; mainnet/testnet stay off). > > **Protocol & API** β New `ChannelRegisterBody` (register / renew / transfer) and HTTP JSON mapping for channel register payloads. > > **Base connector** β Watches ChannelRegistrar `NameRegistered`, `NameRenewed`, and ERC-721 `Transfer` when `override_channel_registrar_address` is set (no default mainnet address yet). Events use **`fid = 0`**; owner is the EVM address. Names decode with **UTF-8 validation** so bad names are dropped, not corrupted. Base `Transfer` shares topic0 with OP IdRegistry; dispatch is **by chain**. Base registries are **polled** (tier + channel); persisted block cursor is the **min** across polled contracts. > > **Store** β Materializes `ChannelOwner` and indexes by channel key, label, and owner address; REGISTER/RENEW/TRANSFER fold with block/tx/log ordering. Invalid index inputs still merge raw events but skip indexes. **`get_channel_owner`** added for reads. > > **Engines** β Shard and block engines **skip** channel register merges when the feature gate is closed. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ab27fed1b6eea50275e6120ef4474925025f9c5d. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Matthew Bond <16600437+bond00729@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Codex gpt-5 <codex@openai.com> Co-authored-by: topocount <17910833+topocount@users.noreply.github.com>
| Commit: | 90e7761 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
fix(rpc): include lapsed registrations in channel list reads Match GetChannelOwner's expiry contract: lapsed registrations (expiry in the past) stay listed with their expiry until a later registration supersedes them, since release state is not computable from chain events (the registry emits nothing when grace ends). Dropping them hid in-grace channels from their owner exactly when a renewal prompt needs them. Callers interpret expiry themselves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | dc68395 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
feat: paginate GetChannelsByFid via composite address cursor Applies the increment-5 review. Replaces by-fid's page_size-as-result-cap (next_page_token always unset, which made "exactly N results" ambiguous between complete and truncated) with real cursor pagination: verified EVM addresses are sorted ascending and the ByOwnerAddress index key (address ++ channel_key) is walked as one globally ordered composite cursor via the new read-only get_channel_keys_for_owner_addresses helper. The token routes to a single address (skip below / resume within / fresh above), so it can never mis-scan across addresses. Contract now matches GetChannelsByAddress: page until next_page_token is unset. ByFid page size is clamped to PAGE_SIZE_MAX so an omitted/oversized request can't trigger an unbounded scan on a public read. Review fixes folded in: - warn! before skipping a VerificationAdd with no data and an owner-address index entry that disagrees with its primary record, matching the existing resolve_channel_owner_fid convention (were silent). - Dedup the two identical proto->HTTP response maps into From impls and share ChannelInfo building across both endpoints. - Document why the sanctioned ByOwnerAddress refactor's added validate_evm_address is unreachable on the merge path (behavior-identical). - Correct the paging doc wording (token unset marks completion; a present token means "call again", not "results guaranteed") on both RPCs and the store helper. - Tests: by-fid cursor round-trip across an address boundary, by-address pagination round-trip, non-Ethereum exclusion, malformed-address InvalidArgument, and the exact-boundary trailing-empty-page contract; drop the obsolete result-cap test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 7a59242 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
refactor(rpc): harden GetChannelOwner resolution and contract docs Review-driven refinements to the channel owner read endpoint; no change to production resolution behavior. - reuse FarcasterTime::current().to_unix_seconds() for the expiry check instead of a hand-rolled SystemTime helper (matches existing idiom) - express the LWW winner as a (ts_hash, Reverse(fid)) sort key so tuple ordering enforces the tie direction; order-independent across the unspecified HashMap shard iteration - log (warn) when a surviving primary VerificationAdd has no data, a data-integrity anomaly distinct from the benign orphan-index skip - move the request/response messages to request_response.proto to match every neighboring RPC - document the HTTP response fields and correct the winner-key comment - bound the public contract honestly: a node not hosting all data shards may return a stale (older) verifier or parked, only a full node is guaranteed globally correct - add tests: ownerAddress JSON serialization, reversed cross-shard LWW (pins order-independence), and add-then-remove reverting to parked Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 685734a | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
feat: add channel convenience reads Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 79d58b9 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
fix(rpc): return lapsed channel registrations instead of NOT_FOUND A registration whose expiry has passed is still owned during the registry's grace period (renewable by its owner; only management freezes), and the chain emits no event when grace ends, so the node cannot compute release state. Returning NOT_FOUND at expiry conflated "in grace" with "released" and hid the owner exactly when a renewal prompt needs it. Report the last known registration (owner address, resolved fid, expiry) as-is and let callers interpret expiry; NOT_FOUND now means only that no registration record exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | b695a90 | |
|---|---|---|
| Author: | topocount | |
| Committer: | topocount | |
feat: add channel owner resolution endpoint Co-authored-by: Codex GPT-5 <codex@openai.com>
| Commit: | ba98d6f | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
docs: correct stale merge-time resolution comments The read-layer design resolves channel owner addresses to fids at query time (GetChannelOwner), not at merge time. Update three connector comments written before that decision, and reword the ChannelRegisterBody doc so it describes the shipped index-input gating instead of promising future merge-side validation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | e678443 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | topocount | |
feat(channels): add ChannelRegister onchain event type and V20 feature gate Adds the wire format and dormant feature gate for channel registration ingestion (mirrors the TierPurchase onchain-event pattern): - proto: EVENT_TYPE_CHANNEL_REGISTER (6) and ChannelRegisterBody with a REGISTER/RENEW/TRANSFER sub-enum and keccak label as the cross-flavor join key (renew events carry no owner; transfer logs carry only the tokenId, so a register-only body cannot represent them) - version: EngineVersion::V20 + ProtocolFeature::ChannelRegistrations, unscheduled on mainnet/testnet; devnet bumped to V20 (runs latest) - engines: admission gates in both the shard engine system-message loop and the block engine (shard 0 receives every onchain event), skipping channel-register events while the feature is inactive so pre-activation merges can never become replay divergences once behavior lands - http: full JSON mapping for the new event body with a round-trip test Connector decode, store indices, and merge-side resolution land in follow-up PRs; until V20 is scheduled this code is dormant on all networks except devnet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 9438107 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
docs: align channel owner-read and membership-mode comments with shipped behavior The MembershipMode proto comment predated enforcement landing; the GetChannelOwner comment described a cross-shard LWW the read no longer performs; the GetChannelsByFid comment claimed an iff whose converse fails once the home shard prunes a verification the permanent shard-0 replica retains. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 2d40025 | |
|---|---|---|
| Author: | Matthew Bond | |
docs: clarify channel read wire contract and pin collapse ChannelMetadataResponse modes are the resolved effective policy (never NONE); state_filter NONE means unfiltered; and get_channel_pin intentionally collapses "unpinned" and "never pinned" into an empty response. Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | eff2bd1 | |
|---|---|---|
| Author: | Matthew Bond | |
feat: add channel message read APIs Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 4c1fdb2 | |
|---|---|---|
| Author: | Matthew Bond | |
fix: align channel owner reads with consensus Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 12b3425 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
docs: correct comments and test name now that emission ships The "inert until emission ships" wire-contract comments (blocks.proto BlockEventType + MergeOnChainEventEventBody) and the factory helper comment predate the emission path; this change ships the emission and fold, so they now describe the V20-gated behavior instead. Rename test_channel_registrations_and_sorted_events_activate_together to ..._sorted_events_and_ownership_activate_together to match the three-way co-activation it now asserts, and update its doc reference. No behavior or wire-format change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | d990071 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
fix(rpc): include lapsed registrations in channel list reads Match GetChannelOwner's expiry contract: lapsed registrations (expiry in the past) stay listed with their expiry until a later registration supersedes them, since release state is not computable from chain events (the registry emits nothing when grace ends). Dropping them hid in-grace channels from their owner exactly when a renewal prompt needs them. Callers interpret expiry themselves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 2a0ec06 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: paginate GetChannelsByFid via composite address cursor Applies the increment-5 review. Replaces by-fid's page_size-as-result-cap (next_page_token always unset, which made "exactly N results" ambiguous between complete and truncated) with real cursor pagination: verified EVM addresses are sorted ascending and the ByOwnerAddress index key (address ++ channel_key) is walked as one globally ordered composite cursor via the new read-only get_channel_keys_for_owner_addresses helper. The token routes to a single address (skip below / resume within / fresh above), so it can never mis-scan across addresses. Contract now matches GetChannelsByAddress: page until next_page_token is unset. ByFid page size is clamped to PAGE_SIZE_MAX so an omitted/oversized request can't trigger an unbounded scan on a public read. Review fixes folded in: - warn! before skipping a VerificationAdd with no data and an owner-address index entry that disagrees with its primary record, matching the existing resolve_channel_owner_fid convention (were silent). - Dedup the two identical proto->HTTP response maps into From impls and share ChannelInfo building across both endpoints. - Document why the sanctioned ByOwnerAddress refactor's added validate_evm_address is unreachable on the merge path (behavior-identical). - Correct the paging doc wording (token unset marks completion; a present token means "call again", not "results guaranteed") on both RPCs and the store helper. - Tests: by-fid cursor round-trip across an address boundary, by-address pagination round-trip, non-Ethereum exclusion, malformed-address InvalidArgument, and the exact-boundary trailing-empty-page contract; drop the obsolete result-cap test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | d558da5 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add channel convenience reads Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 6afbdfb | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
fix(rpc): return lapsed channel registrations instead of NOT_FOUND A registration whose expiry has passed is still owned during the registry's grace period (renewable by its owner; only management freezes), and the chain emits no event when grace ends, so the node cannot compute release state. Returning NOT_FOUND at expiry conflated "in grace" with "released" and hid the owner exactly when a renewal prompt needs it. Report the last known registration (owner address, resolved fid, expiry) as-is and let callers interpret expiry; NOT_FOUND now means only that no registration record exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 80ac6d8 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
refactor(rpc): harden GetChannelOwner resolution and contract docs Review-driven refinements to the channel owner read endpoint; no change to production resolution behavior. - reuse FarcasterTime::current().to_unix_seconds() for the expiry check instead of a hand-rolled SystemTime helper (matches existing idiom) - express the LWW winner as a (ts_hash, Reverse(fid)) sort key so tuple ordering enforces the tie direction; order-independent across the unspecified HashMap shard iteration - log (warn) when a surviving primary VerificationAdd has no data, a data-integrity anomaly distinct from the benign orphan-index skip - move the request/response messages to request_response.proto to match every neighboring RPC - document the HTTP response fields and correct the winner-key comment - bound the public contract honestly: a node not hosting all data shards may return a stale (older) verifier or parked, only a full node is guaranteed globally correct - add tests: ownerAddress JSON serialization, reversed cross-shard LWW (pins order-independence), and add-then-remove reverting to parked Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 7b5db8a | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
docs(proto): correct ChannelOwnerChangeHintBody per-shard hint guarantee The consumer contract claimed REGISTER/TRANSFER hints appear on EVERY shard's stream. That is not guaranteed: the one data shard that also merges a channel event via its system-message path advances its owner index ahead of the fanned replay, so a rapid same-channel burst can be coalesced down to just the latest hint on that shard (the earlier events lose the shard-local strict-`<` LWW and emit no hint there). The latest update of a channel still hints on every shard, and the final owner state converges everywhere. Restate the guarantee accurately: no single shard's stream is guaranteed to carry every hint, so complete coverage means subscribing to all shards and treating each hint as a re-read trigger. Comment-only; no wire change (corpus regenerates with no diff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 89b5dbb | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add channel owner resolution endpoint Co-authored-by: Codex GPT-5 <codex@openai.com>
| Commit: | 6b382c4 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
docs(proto): sharpen ownership-events wire-contract comments Refine the frozen doc comments on the ownership-events proto types ahead of merge (both the wire format and the consumer-contract text freeze at merge, so this is the last chance to get the wording right): - ChannelOwnerChangeHintBody: replace "best-effort" (reads as lossy delivery) with "a nudge, not authoritative data", removing the contradiction with the at-least-once guarantee stated one line below; tie at-least-once to the full shard set; make owner_address cause-accurate (newly-recorded owner for register/transfer, the verification-changed owner for verification causes); document channel_key as the argument to pass to GetChannelOwner. - ChannelOwnerChangeCause: document NONE as the never-emitted proto3 zero default and that the cause set is open (unknown -> re-read). - MergeOnChainEventEventBody: hedge the "channel fold consumes it" claim to future tense (inert until emission ships). Comment-only; no field numbers, enum values, or wire encoding change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 9c104b7 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add dormant wire contracts + gate for channel ownership hints Land the frozen parts of the channel ownership-change hint feature ahead of emission: two wire contracts and a dormant feature gate, so later increments build against stable types while nothing observable changes on any network. - blocks.proto: BLOCK_EVENT_TYPE_MERGE_ON_CHAIN_EVENT (2) + MergeOnChainEventEventBody carrying the whole OnChainEvent, the shard-0 -> data-shard fan-out carrier. - hub_event.proto: HUB_EVENT_TYPE_CHANNEL_OWNER_CHANGE_HINT (12), ChannelOwnerChangeCause enum (RENEW intentionally absent), and ChannelOwnerChangeHintBody. The body doc comment is the public consumer contract: at-least-once, may over-emit, no fid claims (call GetChannelOwner), coverage requires subscribing to all shards. - version.rs: ProtocolFeature::ChannelOwnershipEvents folded into the V20 co-activation arm so its boundary is a compile-time guarantee, not just a test; the co-activation test now pins all three V20 features. - engine.rs handle_block_event: gated admission arm for the new body. Inactive -> InvalidMessageType (caller warns); active -> Ok(vec![]) no-op. Emission and the replica fold ship together in a later increment, so the arm is unreachable until then; the gate ships with the type to avoid a pre-gate replay divergence. - http_server.rs: full JSON mapping for the hint body + round-trip test. - merkle_trie for_hub_event: the hint is trie-free (subscriber-only), like MergeFailure/BlockConfirmed, so it never affects state roots. V20 stays unscheduled on mainnet/testnet; devnet keeps its V20 entry. No emission, no application logic, no behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 2cc124a | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
docs: state channel wire semantics and dormancy contracts in protocol terms message.proto is a public, permanent artifact whose comments are also copied verbatim into generated code, so it should describe the protocol, not this repo's internals or its schedule: - channel_id named ChannelRegisterChannelKeyByLabel, an internal RocksDB postfix that means nothing to another implementer. Describe the value the way the sibling public proto already describes it β keccak256 of the channel key, equal to the ERC-721 tokenId β and point at ChannelRegisterBody.label, qualified by file, since message.proto does not import onchain_event.proto and the bare name is unresolvable from here. - ChannelPinBody deferred its semantics to internal planning vocabulary. State the intent plainly instead. - MembershipMode referenced a roadmap and used an undefined term. Say what self-join means in terms of the wire, and that nothing enforces it yet. Also make the dormancy contracts say what is actually true and what would break. The channel arm in validate_message is the only type-level rejection on the data-shard path, because ShardEngine's body dispatch ends in a permissive wildcard β replacing that arm with real validation admits these types to submit and gossip, so it must move together with the gate. The version.rs note now records that nothing will force that gate, and that routing and JSON read mapping do handle these types deliberately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The documentation is generated from this commit.
| Commit: | 10e07bb | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add dormant channel message protos Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | bd14ce6 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
docs: correct stale merge-time resolution comments The read-layer design resolves channel owner addresses to fids at query time (GetChannelOwner), not at merge time. Update three connector comments written before that decision, and reword the ChannelRegisterBody doc so it describes the shipped index-input gating instead of promising future merge-side validation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 0afdb35 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat(channels): add ChannelRegister onchain event type and V20 feature gate Adds the wire format and dormant feature gate for channel registration ingestion (mirrors the TierPurchase onchain-event pattern): - proto: EVENT_TYPE_CHANNEL_REGISTER (6) and ChannelRegisterBody with a REGISTER/RENEW/TRANSFER sub-enum and keccak label as the cross-flavor join key (renew events carry no owner; transfer logs carry only the tokenId, so a register-only body cannot represent them) - version: EngineVersion::V20 + ProtocolFeature::ChannelRegistrations, unscheduled on mainnet/testnet; devnet bumped to V20 (runs latest) - engines: admission gates in both the shard engine system-message loop and the block engine (shard 0 receives every onchain event), skipping channel-register events while the feature is inactive so pre-activation merges can never become replay divergences once behavior lands - http: full JSON mapping for the new event body with a round-trip test Connector decode, store indices, and merge-side resolution land in follow-up PRs; until V20 is scheduled this code is dormant on all networks except devnet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 63e3a10 | |
|---|---|---|
| Author: | Matthew Bond | |
docs: correct comments and test name now that emission ships The "inert until emission ships" wire-contract comments (blocks.proto BlockEventType + MergeOnChainEventEventBody) and the factory helper comment predate the emission path; this change ships the emission and fold, so they now describe the V20-gated behavior instead. Rename test_channel_registrations_and_sorted_events_activate_together to ..._sorted_events_and_ownership_activate_together to match the three-way co-activation it now asserts, and update its doc reference. No behavior or wire-format change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 94f5fd1 | |
|---|---|---|
| Author: | Matthew Bond | |
docs(proto): correct ChannelOwnerChangeHintBody per-shard hint guarantee The consumer contract claimed REGISTER/TRANSFER hints appear on EVERY shard's stream. That is not guaranteed: the one data shard that also merges a channel event via its system-message path advances its owner index ahead of the fanned replay, so a rapid same-channel burst can be coalesced down to just the latest hint on that shard (the earlier events lose the shard-local strict-`<` LWW and emit no hint there). The latest update of a channel still hints on every shard, and the final owner state converges everywhere. Restate the guarantee accurately: no single shard's stream is guaranteed to carry every hint, so complete coverage means subscribing to all shards and treating each hint as a re-read trigger. Comment-only; no wire change (corpus regenerates with no diff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | b3a79e8 | |
|---|---|---|
| Author: | Matthew Bond | |
docs(proto): sharpen ownership-events wire-contract comments Refine the frozen doc comments on the ownership-events proto types ahead of merge (both the wire format and the consumer-contract text freeze at merge, so this is the last chance to get the wording right): - ChannelOwnerChangeHintBody: replace "best-effort" (reads as lossy delivery) with "a nudge, not authoritative data", removing the contradiction with the at-least-once guarantee stated one line below; tie at-least-once to the full shard set; make owner_address cause-accurate (newly-recorded owner for register/transfer, the verification-changed owner for verification causes); document channel_key as the argument to pass to GetChannelOwner. - ChannelOwnerChangeCause: document NONE as the never-emitted proto3 zero default and that the cause set is open (unknown -> re-read). - MergeOnChainEventEventBody: hedge the "channel fold consumes it" claim to future tense (inert until emission ships). Comment-only; no field numbers, enum values, or wire encoding change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | c509c88 | |
|---|---|---|
| Author: | Matthew Bond | |
feat: add dormant wire contracts + gate for channel ownership hints Land the frozen parts of the channel ownership-change hint feature ahead of emission: two wire contracts and a dormant feature gate, so later increments build against stable types while nothing observable changes on any network. - blocks.proto: BLOCK_EVENT_TYPE_MERGE_ON_CHAIN_EVENT (2) + MergeOnChainEventEventBody carrying the whole OnChainEvent, the shard-0 -> data-shard fan-out carrier. - hub_event.proto: HUB_EVENT_TYPE_CHANNEL_OWNER_CHANGE_HINT (12), ChannelOwnerChangeCause enum (RENEW intentionally absent), and ChannelOwnerChangeHintBody. The body doc comment is the public consumer contract: at-least-once, may over-emit, no fid claims (call GetChannelOwner), coverage requires subscribing to all shards. - version.rs: ProtocolFeature::ChannelOwnershipEvents folded into the V20 co-activation arm so its boundary is a compile-time guarantee, not just a test; the co-activation test now pins all three V20 features. - engine.rs handle_block_event: gated admission arm for the new body. Inactive -> InvalidMessageType (caller warns); active -> Ok(vec![]) no-op. Emission and the replica fold ship together in a later increment, so the arm is unreachable until then; the gate ships with the type to avoid a pre-gate replay divergence. - http_server.rs: full JSON mapping for the hint body + round-trip test. - merkle_trie for_hub_event: the hint is trie-free (subscriber-only), like MergeFailure/BlockConfirmed, so it never affects state roots. V20 stays unscheduled on mainnet/testnet; devnet keeps its V20 entry. No emission, no application logic, no behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | b83fa97 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
fix(rpc): include lapsed registrations in channel list reads Match GetChannelOwner's expiry contract: lapsed registrations (expiry in the past) stay listed with their expiry until a later registration supersedes them, since release state is not computable from chain events (the registry emits nothing when grace ends). Dropping them hid in-grace channels from their owner exactly when a renewal prompt needs them. Callers interpret expiry themselves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 7217321 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: paginate GetChannelsByFid via composite address cursor Applies the increment-5 review. Replaces by-fid's page_size-as-result-cap (next_page_token always unset, which made "exactly N results" ambiguous between complete and truncated) with real cursor pagination: verified EVM addresses are sorted ascending and the ByOwnerAddress index key (address ++ channel_key) is walked as one globally ordered composite cursor via the new read-only get_channel_keys_for_owner_addresses helper. The token routes to a single address (skip below / resume within / fresh above), so it can never mis-scan across addresses. Contract now matches GetChannelsByAddress: page until next_page_token is unset. ByFid page size is clamped to PAGE_SIZE_MAX so an omitted/oversized request can't trigger an unbounded scan on a public read. Review fixes folded in: - warn! before skipping a VerificationAdd with no data and an owner-address index entry that disagrees with its primary record, matching the existing resolve_channel_owner_fid convention (were silent). - Dedup the two identical proto->HTTP response maps into From impls and share ChannelInfo building across both endpoints. - Document why the sanctioned ByOwnerAddress refactor's added validate_evm_address is unreachable on the merge path (behavior-identical). - Correct the paging doc wording (token unset marks completion; a present token means "call again", not "results guaranteed") on both RPCs and the store helper. - Tests: by-fid cursor round-trip across an address boundary, by-address pagination round-trip, non-Ethereum exclusion, malformed-address InvalidArgument, and the exact-boundary trailing-empty-page contract; drop the obsolete result-cap test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 5d0f648 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add channel convenience reads Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | 2dd19ed | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
fix(rpc): return lapsed channel registrations instead of NOT_FOUND A registration whose expiry has passed is still owned during the registry's grace period (renewable by its owner; only management freezes), and the chain emits no event when grace ends, so the node cannot compute release state. Returning NOT_FOUND at expiry conflated "in grace" with "released" and hid the owner exactly when a renewal prompt needs it. Report the last known registration (owner address, resolved fid, expiry) as-is and let callers interpret expiry; NOT_FOUND now means only that no registration record exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | a1daa6c | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
refactor(rpc): harden GetChannelOwner resolution and contract docs Review-driven refinements to the channel owner read endpoint; no change to production resolution behavior. - reuse FarcasterTime::current().to_unix_seconds() for the expiry check instead of a hand-rolled SystemTime helper (matches existing idiom) - express the LWW winner as a (ts_hash, Reverse(fid)) sort key so tuple ordering enforces the tie direction; order-independent across the unspecified HashMap shard iteration - log (warn) when a surviving primary VerificationAdd has no data, a data-integrity anomaly distinct from the benign orphan-index skip - move the request/response messages to request_response.proto to match every neighboring RPC - document the HTTP response fields and correct the winner-key comment - bound the public contract honestly: a node not hosting all data shards may return a stale (older) verifier or parked, only a full node is guaranteed globally correct - add tests: ownerAddress JSON serialization, reversed cross-shard LWW (pins order-independence), and add-then-remove reverting to parked Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | cf6a566 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add channel owner resolution endpoint Co-authored-by: Codex GPT-5 <codex@openai.com>
| Commit: | 69ba034 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
fix(rpc): include lapsed registrations in channel list reads Match GetChannelOwner's expiry contract: lapsed registrations (expiry in the past) stay listed with their expiry until a later registration supersedes them, since release state is not computable from chain events (the registry emits nothing when grace ends). Dropping them hid in-grace channels from their owner exactly when a renewal prompt needs them. Callers interpret expiry themselves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | d2355a7 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: paginate GetChannelsByFid via composite address cursor Applies the increment-5 review. Replaces by-fid's page_size-as-result-cap (next_page_token always unset, which made "exactly N results" ambiguous between complete and truncated) with real cursor pagination: verified EVM addresses are sorted ascending and the ByOwnerAddress index key (address ++ channel_key) is walked as one globally ordered composite cursor via the new read-only get_channel_keys_for_owner_addresses helper. The token routes to a single address (skip below / resume within / fresh above), so it can never mis-scan across addresses. Contract now matches GetChannelsByAddress: page until next_page_token is unset. ByFid page size is clamped to PAGE_SIZE_MAX so an omitted/oversized request can't trigger an unbounded scan on a public read. Review fixes folded in: - warn! before skipping a VerificationAdd with no data and an owner-address index entry that disagrees with its primary record, matching the existing resolve_channel_owner_fid convention (were silent). - Dedup the two identical proto->HTTP response maps into From impls and share ChannelInfo building across both endpoints. - Document why the sanctioned ByOwnerAddress refactor's added validate_evm_address is unreachable on the merge path (behavior-identical). - Correct the paging doc wording (token unset marks completion; a present token means "call again", not "results guaranteed") on both RPCs and the store helper. - Tests: by-fid cursor round-trip across an address boundary, by-address pagination round-trip, non-Ethereum exclusion, malformed-address InvalidArgument, and the exact-boundary trailing-empty-page contract; drop the obsolete result-cap test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | d256814 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
refactor(rpc): harden GetChannelOwner resolution and contract docs Review-driven refinements to the channel owner read endpoint; no change to production resolution behavior. - reuse FarcasterTime::current().to_unix_seconds() for the expiry check instead of a hand-rolled SystemTime helper (matches existing idiom) - express the LWW winner as a (ts_hash, Reverse(fid)) sort key so tuple ordering enforces the tie direction; order-independent across the unspecified HashMap shard iteration - log (warn) when a surviving primary VerificationAdd has no data, a data-integrity anomaly distinct from the benign orphan-index skip - move the request/response messages to request_response.proto to match every neighboring RPC - document the HTTP response fields and correct the winner-key comment - bound the public contract honestly: a node not hosting all data shards may return a stale (older) verifier or parked, only a full node is guaranteed globally correct - add tests: ownerAddress JSON serialization, reversed cross-shard LWW (pins order-independence), and add-then-remove reverting to parked Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Commit: | 864a9c4 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add channel convenience reads Co-authored-by: Codex gpt-5 <codex@openai.com>
| Commit: | fc5c7d9 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
fix(rpc): return lapsed channel registrations instead of NOT_FOUND A registration whose expiry has passed is still owned during the registry's grace period (renewable by its owner; only management freezes), and the chain emits no event when grace ends, so the node cannot compute release state. Returning NOT_FOUND at expiry conflated "in grace" with "released" and hid the owner exactly when a renewal prompt needs it. Report the last known registration (owner address, resolved fid, expiry) as-is and let callers interpret expiry; NOT_FOUND now means only that no registration record exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 3e66fb6 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat: add channel owner resolution endpoint Co-authored-by: Codex GPT-5 <codex@openai.com>
| Commit: | 9bdafdb | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
docs: correct stale merge-time resolution comments The read-layer design resolves channel owner addresses to fids at query time (GetChannelOwner), not at merge time. Update three connector comments written before that decision, and reword the ChannelRegisterBody doc so it describes the shipped index-input gating instead of promising future merge-side validation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | ae8e2d7 | |
|---|---|---|
| Author: | Matthew Bond | |
| Committer: | Matthew Bond | |
feat(channels): add ChannelRegister onchain event type and V20 feature gate Adds the wire format and dormant feature gate for channel registration ingestion (mirrors the TierPurchase onchain-event pattern): - proto: EVENT_TYPE_CHANNEL_REGISTER (6) and ChannelRegisterBody with a REGISTER/RENEW/TRANSFER sub-enum and keccak label as the cross-flavor join key (renew events carry no owner; transfer logs carry only the tokenId, so a register-only body cannot represent them) - version: EngineVersion::V20 + ProtocolFeature::ChannelRegistrations, unscheduled on mainnet/testnet; devnet bumped to V20 (runs latest) - engines: admission gates in both the shard engine system-message loop and the block engine (shard 0 receives every onchain event), skipping channel-register events while the feature is inactive so pre-activation merges can never become replay divergences once behavior lands - http: full JSON mapping for the new event body with a round-trip test Connector decode, store indices, and merge-side resolution land in follow-up PRs; until V20 is scheduled this code is dormant on all networks except devnet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Commit: | 87715c2 | |
|---|---|---|
| Author: | topocount | |
| Committer: | GitHub | |
feat(mesh): network-wide topology crawl over the gossip port (#928) Add an admin-gated crawl that assembles a network-wide view of the validator mesh by querying each connected validator for its local mesh view and aggregating the responses. - gossip: a sibling request-response behaviour for mesh diagnostics (/snapchain/diagnostics/mesh/1.0.0), separate from the Malachite sync rpc so the wire format can evolve independently. The responder serves only validator peers (fail-closed); non-validators are refused. - proto: MeshTopology + UnreachableNode messages and a GetMeshTopology RPC; the per-node query reuses GetMeshViewRequest/MeshView verbatim. - crawl: connected-only β query every connected validator concurrently, classify each response against the local validator set, and surface unconnected/unresponsive validators as `unreachable` rather than dropping them. Traversal is always validators-only; readers are never queried. - http: /v1/mesh?crawl=true returnshe topology (ascii or json). - render: per-topic gossip-mesh display β the single-node table gains a column per topic and the crawl renders one adjacency matrix per topic (directional cells flag one-way links). Topics are chosen via ?topics=a,b (default consensus,mempool; `all` selects every topic), validated against the canonical topic list. - consensus: latest_validator_public_keys() β the latest validator set per shard, unioned β feeds both validator classification and the diagnostics gate, so a removed validator is excluded from both. resolves NEYN-11925
| Commit: | ed87a50 | |
|---|---|---|
| Author: | topocount | |
| Committer: | GitHub | |
feat(mesh): local mesh view endpoint `/v1/mesh` (#927) Add an admin-gated endpoint that returns a node's view of the gossip mesh and topology: connected peers, per-topic mesh membership, and per-peer/per-topic gossip rates. Validators are identified cryptographically β a peer's libp2p PeerId is derived from its Ed25519 signing key and matched against the validator set at the current height β not by a heuristic. - proto: GetMeshView RPC + MeshView/MeshSelf/MeshPeer/TopicMembership/ GossipRate messages; ContactSource and MeshNodeType enums. Reserve ttl/visited_peer_ids on the request for the future propagating crawl. - gossip: track observed connection addresses separately from peer-attested contact info; assemble the raw mesh view from gossipsub all_peers/mesh_peers + sampled gossip-rate counters. - server: classify peers against the validator set (the only place pubkey -> set mapping happens) and admin-gate via admin_rpc_auth. - http_server: /v1/mesh route with ?at=ascii|json and ?validators_only=; ASCII table + consensus-mesh graph renderer. Bundled fix: connected peers lacking collected contact info (e.g. validators, which never publish it) were dropped from /v1/currentPeers. They now appear tagged source=DERIVED with their observed address, never conflated with COLLECTED peer-attested data. Properly fixing validator contact-info publishing is tracked in #911 / NEYN-11921. resolves #911 resolves NEYN-11921 merge plan: merge after #926
| Commit: | c292bfd | |
|---|---|---|
| Author: | Ty Book | |
| Committer: | GitHub | |
Add LIVE_AT user data support (#899) ## Summary Implementation of https://github.com/farcasterxyz/protocol/discussions/268 for Linear NEYN-10699. - Add `USER_DATA_TYPE_LIVE_AT = 14` with V17 feature gating and length-only validation. - Add a separate LIVE_AT mempool rate-limit budget based on storage units, with invalidation on storage rent and storage lending changes. - Coalesce pending LIVE_AT heartbeats by full LWW ordering (`timestamp + hash`) so each fid has at most one pending LIVE_AT per shard. - Add validation, mempool, rate-limit, consensus/devnet coverage, and docs/changelog updates. - Fix local Docker devnet peering so the compose stack can be used for end-to-end validation. ## Release schedule V17 enables `ProtocolFeature::LiveAt` by network schedule: - Testnet: 2026-05-21 5:00 PM UTC (`1779382800`) - Mainnet: 2026-06-04 5:00 PM UTC (`1780592400`) Nodes must deploy this PR's build before each network's `active_at`. Devnet is already V17 at `active_at = 0`, so local devnets accept LIVE_AT immediately. ## Test plan - `PATH="$HOME/.cargo/bin:$PATH" rustup run stable rustfmt --edition 2024 src/version/version.rs` - `PATH="$HOME/.cargo/bin:$PATH" rustup run stable cargo test test_live_at --lib` - `PATH="$HOME/.cargo/bin:$PATH" rustup run stable cargo test live_at_budget --lib` - `PATH="$HOME/.cargo/bin:$PATH" rustup run stable cargo test live_at_rate_limit_rejects_zero_storage_units --lib` - `PATH="$HOME/.cargo/bin:$PATH" rustup run stable cargo test live_at_activation_schedule --lib` - `PATH="$HOME/.cargo/bin:$PATH" rustup run stable cargo test test_next_version_timestamp_for --lib` - `PATH="$HOME/.cargo/bin:$PATH" rustup run stable cargo test test_live_at_consensus --test consensus_test` - Local Docker devnet smoke: `docker compose build`, `docker compose up`, verified `/v1/info` block progress and LIVE_AT set/update/clear readback through the running devnet. Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
| Commit: | 82f7518 | |
|---|---|---|
| Author: | topocount | |
| Committer: | GitHub | |
feat: surface current user/app nonces on signersByFid (#888) Adds `current_user_nonce` (always populated) and `requester_fid_nonces` (map<uint64, uint32> keyed by requester FID) to SignersByFidResponse, plus a new SignersByFidRequest carrying repeated `requester_fids` for batched lookups. Exposes the gasless-key counters that previously lived only behind merge-time validation, so clients composing the next signed- key-request no longer have to track nonces locally β including across revocations, where the per-key record is gone but the counter persists.
| Commit: | 0fb53e3 | |
|---|---|---|
| Author: | topocount | |
| Committer: | GitHub | |
feat: add unified signer API with on-chain and off-chain support (#864) This commit introduces a new unified signer API, including the `GetSigner` and `GetSignersByFid` RPC methods, which return both on-chain and off-chain (gasless) signer records. The `Signer` message structure is enhanced to accommodate fields relevant to both sources, and the corresponding responses are defined. Additionally, tests are added to ensure the correct functionality of the new API endpoints, verifying the integration of on-chain and gasless key records. This change aims to streamline the signer retrieval process and improve overall API usability.
| Commit: | 4ed61da | |
|---|---|---|
| Author: | Copilot | |
| Committer: | GitHub | |
Remove shard_id from BlocksRequest (#822) `GetBlocks` always returns blocks from shard 0 regardless of the `shard_id` value in `BlocksRequest`, making the field silently misleading to callers. ### Changes - **Proto**: Remove `shard_id` (field 1) from `BlocksRequest`; mark as `reserved` to prevent field number reuse - **Rust**: Remove `shard_id: 0` from `BlocksRequest` construction in `cli.rs` and `rpc_client.rs` - **Docs**: Update `blocks.md` to drop `shard_id` from the `BlocksRequest` table ```proto // Before message BlocksRequest { uint32 shard_id = 1; uint64 start_block_number = 2; optional uint64 stop_block_number = 3; } // After message BlocksRequest { reserved 1; reserved "shard_id"; uint64 start_block_number = 2; optional uint64 stop_block_number = 3; } ``` --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: manan19 <377624+manan19@users.noreply.github.com>
| Commit: | 11ef8a2 | |
|---|---|---|
| Author: | topocount | |
| Committer: | GitHub | |
feat: add KEY_ADD and KEY_REMOVE to protos (#786) add empty handlers where needed
| Commit: | 2de072c | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | Aditi Srinivasan | |
debug account root mismatch
| Commit: | 93dc859 | |
|---|---|---|
| Author: | Christopher Wallace | |
| Committer: | GitHub | |
feat: separate proto files into standalone crate (#729) ## Summary - Extracts protocol buffer definitions into a separate `snapchain-proto` crate so consumers can import protos without depending on the entire snapchain project - Converts the project to a Cargo workspace with `proto/` as a workspace member - Uses extension traits pattern to maintain functionality while respecting Rust's orphan rules ## Changes - Create `proto/` workspace member with its own `Cargo.toml` and `build.rs` - Move proto definitions from `src/proto/` to `proto/definitions/` - Move proto-only impls (Height, ShardHash, Message, FullProposal, ConsensusMessage, BlockEvent) to proto crate - Add extension traits for methods requiring main crate types: - `CommitsExt` for commit certificate conversion - `FullProposalExt` for proposer address - `HubEventExt` for event construction - `HubEventStorageExt` for storage operations - Rename `HubEvent::from()` to `HubEvent::new_event()` to avoid trait conflict with `std::convert::From` ## Test plan - [ ] Docker compose build passes - [ ] All existing tests pass - [ ] Proto crate can be imported independently
| Commit: | 3e753b6 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
chore: add more information about lent and borrowed units to rpc (#717) Update the `GetCurrentStorageLimitsByFid` rpc to include details about how many units are lent, borrowed, and purchased to figure out where there's a discrepancy between snapchain and the client.
| Commit: | e634aa8 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
chore: add bulk rpc for querying `LendStorage` messages (#702) This rpc provides us a way to know if a `LendStorage` message has been merged into snapchain.
| Commit: | bd553ec | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
feat: enable migrating onchain events to shard 0 and fix bugs with storage lending (#669) Add an rpc to migrate onchain events to shard 0 from each of the shard 1 and 2. In addition, this feature contains several fixes necessary to support storage lends properly. The bugs were discovered while testing in testnet. As of this feature storage lending is fully supported in testnet. Fixes: - Reset event id to the current block number when starting to propose, validate, or commit a block in the block engine. This was missing so all event ids were being produced with height 0 and we exhausted the available sequence numbers. - Force storage lenders to retain 1 unit of storage so they can revoke storage they've lent. Otherwise, the lender runs into rate limits when trying to revoke lent storage after lending all their storage out. This is easier and safer than bypassing rate limits. - Listen to confirmed blocks in the mempool and evict storage lends that have been put into blocks in shard 0. - Have rate limits in the mempool key off the fid's shard and never shard 0. The mempool was crashing trying to find a `ShardStore` for shard 0. - Add a count of onchain events to the `GetInfo` rpc so we can validate that the migration of onchain events to shard 0 has happened correctly.
| Commit: | 32fa4b1 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
chore: separate out ContactInfo and GossipMessage protos (#695) The client protos fail to generate with an error because the type definition for `GossipMessage` is too big. Separate out `ContactInfo` and `GossipMessage` so we can use `ContactInfo` in rpcs on the client without having to pull in `GossipMessage`.
| Commit: | f136e05 | |
|---|---|---|
| Author: | adityapk00 | |
| Committer: | GitHub | |
feat: Discover nodes to sync with using gossip p2p (#682) when replication starts, we seed it with an initial bootstrap list. The replication service then spins up a lightweight gossip node to discover peers to sync against.
| Commit: | 05b8bf5 | |
|---|---|---|
| Author: | adityapk00 | |
| Committer: | GitHub | |
feat: Configure or get (from public IP) announce_rpc_address (#681) Allow setting a `announce_rpc_address` in the config and share that out in the ContactInfo gossip messages. If not configured, get it from public IP
| Commit: | 3821bc1 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
feat: support storage lending in the shard and block engines (#665) Support storage lending. This feature enables support for storage lending in the `BlockEngine` and `ShardEngine` but the messages aren't handled properly in the rpc server and mempool yet.
| Commit: | 01ce09d | |
|---|---|---|
| Author: | adityapk00 | |
| Committer: | GitHub | |
feat: Add the ability to start a node via replication (#667) These are the client changes to support replication. It's not enabled yet, once we feel confident with some manual testing, I'll turn it on.
| Commit: | a4acb11 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
feat: implement system message to revalidate a user message with external proofs
| Commit: | 81a7f5f | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | Aditi Srinivasan | |
feat: periodically revoke invalid ens proofs
| Commit: | 8290347 | |
|---|---|---|
| Author: | Sanjay | |
| Committer: | GitHub | |
feat: Add profile token user data type (#666) Implement https://github.com/farcasterxyz/protocol/discussions/247. Will go live on the protocol on Sep 24 (engine version 10)
| Commit: | 541cb3f | |
|---|---|---|
| Author: | adityapk00 | |
| Committer: | GitHub | |
feat: Add trie-based replication server (#663) Modify the replication server to use trie-based iteration to get all messages instead of by DB.
| Commit: | 5339d97 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
feat: read block events from shard 0 in other shards (#639) Have all other shards consume block events from shard 0.
| Commit: | 00a776c | |
|---|---|---|
| Author: | Aditya Kulkarni | |
Merge remote-tracking branch 'adityapk/trie_iteration_replicator' into trie_iteration_client
| Commit: | 2d94da9 | |
|---|---|---|
| Author: | Aditya Kulkarni | |
return shard_chunk and blocks in metadata
| Commit: | 57fdbc1 | |
|---|---|---|
| Author: | Aditya Kulkarni | |
wip
| Commit: | e853adf | |
|---|---|---|
| Author: | Aditya Kulkarni | |
fix panic while decoding trie key
| Commit: | bf64df2 | |
|---|---|---|
| Author: | Aditya Kulkarni | |
wip
| Commit: | 2a4bd80 | |
|---|---|---|
| Author: | Aditya Kulkarni | |
Merge remote-tracking branch 'origin/main' into trie_iteration_replicator
| Commit: | a60e5a2 | |
|---|---|---|
| Author: | Aditya Kulkarni | |
Merge remote-tracking branch 'adityapk/trie_iteration_replicator' into trie_iteration_client
| Commit: | 4d62fd0 | |
|---|---|---|
| Author: | adityapk00 | |
| Committer: | GitHub | |
feat: Allow attaching orphaned nodes to the merkle trie (#651) When we write to the trie from parallel threads (like we do in the replicator), we will write "orphaned" nodes. These are nodes that are written to the DB, but not yet attached to the root node. This is because we want to write in parallel threads to different parts of the trie. Once the operation is done, we call `attach_to_root` and `recalculate_hashes` to add new nodes to the trie. This way, we can parallelize construction of the trie. Also remove excluded_hash() and snapshot() which we don't use anymore. The `get_trie_node_metadata` is still supported, which can be used to get info about the parts of a trie.
| Commit: | a912c1e | |
|---|---|---|
| Author: | Aditya Kulkarni | |
add onchain event support
| Commit: | 250915a | |
|---|---|---|
| Author: | Aditya Kulkarni | |
read user messages
| Commit: | 1e63e00 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
feat: produce transactions and events in shard 0 (#634) Produce blocks from shard 0 that contain block events and transactions. We use a heartbeat event for testing the events pipeline.
| Commit: | 8c1f942 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | Aditi Srinivasan | |
fixes
| Commit: | c80dc67 | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | Aditi Srinivasan | |
rough end to end impl of cross shard communication
| Commit: | 50a8833 | |
|---|---|---|
| Author: | Sanjay Raveendran | |
| Committer: | Sanjay Raveendran | |
Upgrade malachite to latest
| Commit: | a6b4a2b | |
|---|---|---|
| Author: | Paul | |
| Committer: | GitHub | |
Add transaction replication endpoint(s) (#606) Adds endpoints for fetching replication shapshot metadata and transactions for a given shard. --------- Co-authored-by: Paul <515868+eyeamera@users.noreply.github.com>
| Commit: | 592d858 | |
|---|---|---|
| Author: | adityapk00 | |
| Committer: | GitHub | |
feat: Add SubmitBulkMessages rpc/http API (#600) Add the SubmitBulkMessages API (which had been commented out). The endpoint (both grpc and http API) accepts an array of messages, and submits them sequentially. Right now, the messages need to be independent (i.e., a message that depends on a previous message in the array to succeed is not supported yet) It will attempt to merge all the messages, and return success/failure for each one individually
| Commit: | 47fc859 | |
|---|---|---|
| Author: | Sanjay | |
| Committer: | GitHub | |
feat: expose next engine version timestamp in GetInfo API (#595) Based on https://github.com/farcasterxyz/snapchain/pull/587 Fixes https://github.com/farcasterxyz/snapchain/issues/586 --------- Co-authored-by: christopherwxyz <christopher@unofficial.run>
| Commit: | 302cf60 | |
|---|---|---|
| Author: | Paul | |
| Committer: | GitHub | |
Read implementation for replication (#589) Adds: - Extension to Engine to support replaying individual transactions for backfilling FID state data - ReplicationStores: used to hold "snapshots" (read-only views) into individual shard stores taken at a specified block interval. These snapshots are utilized to provide a consistent view into FID data at known block height(s). - Replicator: the business logic for taking snapshots and exporting FID data - Replication Service: a new grpc service that exposes an interface in front of replicator for querying FID state. Follow-ups: - [ ] Some accounts have a lot of messages (e.g. fids 2 and 3) so we'll need to sync them over multiple requests. - [ ] Solve for message ordering at the engine level [:link:](https://github.com/farcasterxyz/snapchain/pull/589#discussion_r2216488125) - [ ] Allow db-free lookups at the engine level (e.g. when an FID doesn't exist we shouldn't error) - [ ] Don't commit transactions for FID unless the account root matches [:link:](https://github.com/farcasterxyz/snapchain/pull/589#discussion_r2221348609) Towards: #510 --------- Co-authored-by: Paul <515868+eyeamera@users.noreply.github.com>
| Commit: | 46f736e | |
|---|---|---|
| Author: | Frederik Bolding | |
| Committer: | GitHub | |
feat: Add endpoint for connected peers (#541) Add RPC endpoint for getting the currently connected peers. To do this, introduces a `BTreeMap` in `SnapchainGossip` that stores the contact info received from other peers.
| Commit: | 6dc8dfa | |
|---|---|---|
| Author: | Sanjay | |
| Committer: | GitHub | |
feat: Storage redenomination (#570) Implements the Storage Re-denomination FIP: https://github.com/farcasterxyz/protocol/discussions/229 As part of it, we're also extending the expiry of storage units that are set to expire on Aug 29 2025 by one year.
| Commit: | 8515428 | |
|---|---|---|
| Author: | Sanjay | |
| Committer: | GitHub | |
feat: Add admin endpoint to retry fname transfers (#546) Admin endpoint to retry fname transfers by fid.
| Commit: | a0367ad | |
|---|---|---|
| Author: | Aditi Srinivasan | |
| Committer: | GitHub | |
feat: add event counts per event type to BlockConfirmed (#555) This is required to help the client determine if there are missed events with filtering applied on the event stream.