persist per-topic NonceV2 epoch sequence (ENGN-8910) - #970
Conversation
Wire AllocateNextEpochNonce into StartEpoch and round-trip the last-nonce map through genesis so epochs stay unique across restarts. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest Buf updates on your PR. Results from workflow Buf Linter / buf (pull_request).
|
There was a problem hiding this comment.
cubic analysis
1 issue found across 10 files
Not reviewed (too large): x/emissions/api/emissions/v10/genesis.pulsar.go (~2,188 lines) - if these are generated or fixture files, add them to ignored paths to exclude them from future reviews.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="x/emissions/keeper/epoch.go">
<violation number="1" location="x/emissions/keeper/epoch.go:103">
P2: Starting an epoch panics once the persisted sequence reaches the uint56 limit, instead of returning a recoverable allocation error. Guard the maximum payload before calling `NextNonce` so an exhausted sequence cannot halt this execution path.</violation>
</file>
Linked issue analysis
Linked issue: ENGN-8910: Epoch nonce format + per-topic sequence persistence
| Status | Acceptance criteria | Notes |
|---|---|---|
| ✅ | Add per-topic sequence store for last allocated epoch NonceV2 and persist it through genesis/export-import | The keeper now includes topicLastEpochNonce map, genesis init/export iterate and set logic, and the genesis proto/types were extended to carry TopicLastEpochNonces. |
| ✅ | Wire per-topic sequence into StartEpoch so every new epoch gets a unique NonceV2 per topic | StartEpoch now calls AllocateNextEpochNonce and no longer uses a zero nonce placeholder; AllocateNextEpochNonce computes NextNonce() and persists it as the topic's last nonce. |
| ✅ | Unit tests cover monotonic per-topic allocation and genesis round-trip (sequence survives restart/export-import) | Added tests exercise monotonic allocation across topics and verify that exported genesis carries last-nonce rows which are initialized by InitGenesis and produce the same next allocations after import. |
Architecture diagram
sequenceDiagram
participant Trigger as Epoch Trigger (BeginBlocker/Msg)
participant Keeper as Keeper
participant Store as topicLastEpochNonce Store
participant GenesisExporter as Genesis Export
participant GenesisImporter as Genesis Import
Note over Trigger,Store: NEW: Per-topic epoch nonce allocation
Trigger->>Keeper: StartEpoch(topicID)
Keeper->>Keeper: AllocateNextEpochNonce(topicID)
Keeper->>Store: Get(topicID)
alt First allocation for topic
Store-->>Keeper: not found (ErrNotFound)
Keeper->>Keeper: lastNonce = ZeroNonce()
else Subsequent allocation
Store-->>Keeper: lastNonce
end
Keeper->>Keeper: nextNonce = lastNonce.NextNonce()
Keeper->>Store: Set(topicID, nextNonce)
Store-->>Keeper: ok
Keeper->>Keeper: Create epoch with nextNonce
Keeper-->>Trigger: epoch started
Note over Trigger,Store: Monotonic per topic: NonceV2 version V1, payload increments
Note over GenesisExporter,GenesisImporter: NEW: Genesis round-trip for nonces
GenesisExporter->>Keeper: ExportGenesis(ctx)
Keeper->>Store: Iterate()
Store-->>Keeper: topicID → NonceV2 pairs
Keeper->>Keeper: Build TopicLastEpochNonces slice
Keeper-->>GenesisExporter: genesis data with nonces
GenesisImporter->>Keeper: InitGenesis(ctx, data)
Keeper->>Keeper: Iterate TopicLastEpochNonces
loop each entry
Keeper->>Store: Set(topicID, nonce)
Store-->>Keeper: ok
end
Keeper-->>GenesisImporter: genesis initialized
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| lastNonce = types.ZeroNonce() | ||
| } | ||
|
|
||
| nextNonce := lastNonce.NextNonce() |
There was a problem hiding this comment.
P2: Starting an epoch panics once the persisted sequence reaches the uint56 limit, instead of returning a recoverable allocation error. Guard the maximum payload before calling NextNonce so an exhausted sequence cannot halt this execution path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At x/emissions/keeper/epoch.go, line 103:
<comment>Starting an epoch panics once the persisted sequence reaches the uint56 limit, instead of returning a recoverable allocation error. Guard the maximum payload before calling `NextNonce` so an exhausted sequence cannot halt this execution path.</comment>
<file context>
@@ -75,6 +76,37 @@ func (k *Keeper) setupEpochFSMEngine() {
+ lastNonce = types.ZeroNonce()
+ }
+
+ nextNonce := lastNonce.NextNonce()
+ if err := k.topicLastEpochNonce.Set(ctx, topicID, nextNonce); err != nil {
+ return types.NonceV2(0), err
</file context>
| nextNonce := lastNonce.NextNonce() | |
| if lastNonce.Payload() == (uint64(1)<<56)-1 { | |
| return types.NonceV2(0), errors.New("epoch nonce sequence exhausted") | |
| } | |
| nextNonce := lastNonce.NextNonce() |
Format v10 protos, ignore EpochState prefix rules (INIT is a real zero state), and clear gosec/exhaustruct/forcetypeassert/ineffassign/staticcheck findings so CI lint can pass. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
topicLastEpochNoncestore andAllocateNextEpochNonceso eachStartEpochgets a uniqueNonceV2(Bech32 deferred).topic_last_epoch_nonces).Test plan
go test ./x/emissions/keeper/ -run 'TestKeeperTestSuite/TestAllocateNextEpochNonceMonotonicPerTopic|TestKeeperTestSuite/TestTopicLastEpochNonceGenesisRoundTrip'Linear: ENGN-8910
Made with Cursor
Summary by cubic
Persists per-topic
types.NonceV2epoch sequences and makesStartEpochallocate the next per-topic nonce; sequences survive restarts and export/import. Keepstypes.NonceV2, defers Bech32. Linear: ENGN-8910.topicLastEpochNoncemap (TopicLastEpochNonceKey=112) withGetTopicLastEpochNonceandAllocateNextEpochNonce; first allocation isZeroNonce().NextNonce()(V1, payload 1).StartEpochtoAllocateNextEpochNonceso every epoch is unique per topic.topic_last_epoch_nonces(types.TopicIdAndEpochNonce) with Init/Export;NewGenesisStateinitializes it empty.x/emissions/keeper/epoch_nonce_test.go.x/emissions/proto/buf.yamlrules forEpochState; fix golangci issues (explicit FSMAdvanceasserts,NonceV2.Versionbitmask with//nolint:gosec, add context in tests, minor static checks).Written for commit 35530f6. Summary will update on new commits.