Skip to content

audit: implement storage truth enforcement and self-healing lifecycle#121

Open
j-rafique wants to merge 1 commit intoLEP-6-heal-op-lifecyclefrom
LEP-6-activation
Open

audit: implement storage truth enforcement and self-healing lifecycle#121
j-rafique wants to merge 1 commit intoLEP-6-heal-op-lifecyclefrom
LEP-6-activation

Conversation

@j-rafique
Copy link
Copy Markdown
Contributor

No description provided.

@roomote-v0
Copy link
Copy Markdown

roomote-v0 Bot commented Apr 21, 2026

Rooviewer Clock   See task

Reviewed the storage truth enforcement and self-healing lifecycle implementation. Found 2 behavioral issues and 1 dead code item.

  • CleanPassCount never resets on failure -- the recovery gate can be trivially satisfied by historical clean passes accumulated before any violations, allowing premature recovery after postponement (storage_truth_scoring.go)
  • ClassACountWindow undercounts Class A faults -- INDEX artifact failures (e.g. TIMEOUT on an INDEX artifact) are not counted as Class A in the window counter, causing the enforcement fallback predicates to miss justified postponements (storage_truth_scoring.go)
  • Dead code mulInt64ByUint64Saturated -- defined but never called (storage_truth_scoring.go)

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

Comment on lines +221 to +226
if isPass {
state.CleanPassCount++
state.LastCleanPassEpoch = epochID
}

if isFailure {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CleanPassCount is incremented on every PASS but never reset when a failure occurs. The recovery gate in shouldRecoverFromStorageTruthPostponement checks state.CleanPassCount >= requiredPasses (default 3). Because the counter accumulates over the node's entire lifetime, a node that earned 3+ clean passes before any violations would immediately satisfy the recovery gate once decay brings its score below the watch threshold -- even right after being postponed for severe storage-truth faults. The spec's intent ("recovery requires N clean passes") likely means N clean passes since the last failure or since postponement, not N total historical passes. Consider resetting CleanPassCount to 0 when a failure is recorded.

Suggested change
if isPass {
state.CleanPassCount++
state.LastCleanPassEpoch = epochID
}
if isFailure {
if isPass {
state.CleanPassCount++
state.LastCleanPassEpoch = epochID
}
if isFailure {
// Reset clean pass streak -- recovery requires consecutive clean passes after failures.
state.CleanPassCount = 0

Fix it with Roo Code or mention @roomote and request a fix.

Comment on lines +252 to +261
switch result.ResultClass {
case types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_HASH_MISMATCH,
types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_RECHECK_CONFIRMED_FAIL:
state.ClassACountWindow++
state.LastClassAEpoch = epochID
case types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_TIMEOUT_OR_NO_RESPONSE:
state.ClassBCountWindow++
state.LastClassBEpoch = epochID
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClassACountWindow only increments for HASH_MISMATCH and RECHECK_CONFIRMED_FAIL, but the Class A definition in storageTruthIsClassAFault (used by enforcement predicates) also includes any failure with ARTIFACT_CLASS_INDEX regardless of result class. When fact indexes are empty, the enforcement fallback path in storageTruthPostponePredicatesMet uses state.ClassACountWindow to evaluate postpone predicates. This means INDEX artifact failures (e.g. TIMEOUT_OR_NO_RESPONSE on an INDEX artifact) would not be counted as Class A in the fallback, potentially preventing a justified postponement.

Suggested change
switch result.ResultClass {
case types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_HASH_MISMATCH,
types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_RECHECK_CONFIRMED_FAIL:
state.ClassACountWindow++
state.LastClassAEpoch = epochID
case types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_TIMEOUT_OR_NO_RESPONSE:
state.ClassBCountWindow++
state.LastClassBEpoch = epochID
}
}
case types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_HASH_MISMATCH,
types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_RECHECK_CONFIRMED_FAIL:
state.ClassACountWindow++
state.LastClassAEpoch = epochID
case types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_TIMEOUT_OR_NO_RESPONSE:
state.ClassBCountWindow++
state.LastClassBEpoch = epochID
default:
// Other failure result classes on INDEX artifacts are also Class A.
}
if result.ArtifactClass == types.StorageProofArtifactClass_STORAGE_PROOF_ARTIFACT_CLASS_INDEX {
if result.ResultClass != types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_HASH_MISMATCH &&
result.ResultClass != types.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_RECHECK_CONFIRMED_FAIL {
state.ClassACountWindow++
state.LastClassAEpoch = epochID
}
}

Fix it with Roo Code or mention @roomote and request a fix.

return result
}

func mulInt64ByUint64Saturated(v int64, m uint64) int64 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mulInt64ByUint64Saturated is defined but has no callers in the codebase. It appears to be leftover from a previous iteration of the scoring logic. Consider removing it to reduce dead code.

Fix it with Roo Code or mention @roomote and request a fix.

@mateeullahmalik
Copy link
Copy Markdown
Contributor

Production-gate review by Zee — 15 findings

Methodology: full file-by-file read of every non-generated changed file in this PR's diff (pr-121 vs its base branch), cross-checked against:

  • LEP-6 spec (Notion source of truth)
  • invariant-first-coding skill (write-path enumeration, sibling symmetry, single source of truth, post-fix re-audit)
  • Cosmos SDK consensus discipline (no float, no map iteration, bounded EndBlock, genesis round-trip, errorsmod wrapping)

Status legend: each finding's status is computed at the PR #122 stack-tip (consensus-gap-fixes commit a51c439), so 'FIXED' means a downstream PR in the stack already addresses it; 'OPEN' means it is still present at the tip and must be fixed before merge / Phase-2 activation. Severity rubric in the charter (~/work/lep6-review/ctx/charter.md): CRITICAL = consensus halt / state corruption / non-determinism in ABCI; HIGH = spec mismatch with economic impact, missing genesis round-trip, replay enabler; MEDIUM = invariant asymmetry without immediate exploit, unbounded loop with practical bound, missing param validation.

Severity breakdown: CRITICAL=1, HIGH=7, MEDIUM=7


121-F16 — DETERMINISM: float64 arithmetic in EndBlock consensus path drives state mutation

  • Severity: CRITICAL
  • File: x/audit/v1/keeper/storage_truth_divergence.go
  • Lines: 32 (struct field), 50 (negRate := float64(stats.negative)/float64(stats.total)), 65-69 (sort.Float64s, medianFloat64), 73 (entry.negRate <= 2.0*medianNegRate), 97-98 (strconv.FormatFloat in event attribute)
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN at PR feat(audit): finalize LEP-6 consensus gap fixes #122 head — must fix before mainnet activation. Float in Cosmos consensus path is an anti-pattern even when IEEE-754 is nominally deterministic; Go does not contractually guarantee bit-exact float ops across compiler versions/optimizer flags/CPU instruction selection (e.g. FMA fusion). Event attributes built with strconv.FormatFloat are written into the consensus event log and any cross-version difference becomes an apphash mismatch.
  • What: ApplyReporterDivergenceAtEpochEnd runs every EndBlock at epoch end. It computes per-reporter negRate := float64(neg)/float64(total), sorts a []float64 of rates, takes the float median, and the predicate entry.negRate <= 2.0*medianNegRate gates a +8 reliability penalty (state mutation via applyReporterReliabilityDelta). It additionally emits reporter_neg_rate and median_neg_rate event attributes via strconv.FormatFloat(..., 'f', 4, 64), which become part of the consensus event log.
  • Suggested fix: Replace with integer / cosmossdk.io/math.LegacyDec arithmetic. The spec predicate neg_rate_reporter > 2.0 * neg_rate_network can be expressed exactly as cross-multiplied integers: negR * totN > 2 * negN * totR (after sorting reporters by (negR, totR) for the median). Replace medianFloat64(rates) with a deterministic median over (numerator, denominator) integer pairs. Replace event attribute formatting with integer/string forms (e.g. emit negative and total separately, let off-chain consumers compute the rate). This is a hard determinism gate — flag must block merge/activation.

121-F1 — Recheck PASS double-counts original-reporter penalty: +12 (contradiction) + +25 (explicit) = +37 vs spec +25

  • Severity: HIGH
  • File: x/audit/v1/keeper/msg_storage_truth.go
  • Lines: 117-141 (synthesis call) interacting with storage_truth_scoring.go:575-587
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — please confirm at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip. Either bypass storageTruthBookkeepingForResult for BucketType==RECHECK, or apply only one of the deltas in the recheck handler.
  • What: SubmitStorageRecheckEvidence synthesises a PASS result, applies scores → bookkeeping fires contradiction +12 against LastReporterSupernodeAccount. Handler then applies explicit +25. Net is +37, but spec §15.1 says +25 only.

121-F2 — Recheck +25 / −3 applied to LastReporterSupernodeAccount instead of the transcript's reporter

  • Severity: HIGH
  • File: x/audit/v1/keeper/msg_storage_truth.go
  • Lines: 104-115, 130-141, 152-164, 165-174
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — fix is one-line: use challengedRecord.ReporterAccount (already loaded line 65) instead of ticketState.LastReporterSupernodeAccount.
  • What: Spec §15.1 references the reporter of the overturned fail. Code uses the most recent reporter on the ticket — possibly a different account if a later epoch report wrote to the ticket.

121-F3 — Ticket-level contradiction predicate is direction-agnostic — penalises honest reporters

  • Severity: HIGH
  • File: x/audit/v1/keeper/storage_truth_scoring.go
  • Lines: 575-587, 658-663
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN at PR feat(audit): finalize LEP-6 consensus gap fixes #122 (the predicate at lines 718-719 is still bidirectional: (prev==PASS && currentFailure) || (current==PASS && prevFailure)).
  • What: On (prev=PASS, current=fail): the prior honest reporter is marked contradictedReporter and gets the −12. Spec §15.1 only penalises the reporter who submitted the bad outcome, AND requires '2 independent clean passes' (or 1 pass + clean recheck) within W=7 — the predicate fires on a single contradicting result.

121-F4 — Strong-postpone predicate LastIndexFailEpoch > 0 fallback never times out

  • Severity: HIGH
  • File: x/audit/v1/keeper/enforcement.go
  • Lines: 678-679
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — guard with epochDelta(epochID, state.LastIndexFailEpoch) < classAWindow, or remove the fallback and rely solely on the windowed fact-index.
  • What: LastIndexFailEpoch is set in updateNodeSuspicionHistoryFields and never reset. Once a node has any index failure ever, this branch is permanently true. Combined with N transiently spiking ≥140, makes strong-postpone trivially reachable forever.

121-F6 — Five new fact indexes (st/nf/, st/rrs/, st/spt/, st/fh/, st/rce/) never pruned + iteration is full-prefix in DeliverTx and EndBlock

  • Severity: HIGH
  • File: x/audit/v1/keeper/prune.go (omission)
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — see also PR feat(audit): finalize LEP-6 consensus gap fixes #122 Finding 1 (per-result transcript scans in DeliverTx). Add prefix prunes keyed off the embedded u64be epoch_id; ensure KeepLastEpochEntries >= max(divergence_window, classA_window=14, classB_window=7, oldClassAFaultWindow=21, contradictionWindow=7, patternWindow=14).
  • What: distinctNodeFailedTickets, storageTruthReporterDivergenceStats, hasStorageTruthFailedHeal iterate the entire prefix and filter by epoch in code. Per-result calls in DeliverTx via MsgSubmitEpochReport; per-supernode calls every EndBlock at epoch end. Linear gas growth in chain age — canonical Cosmos 'unbounded loop in EndBlock' anti-pattern.

121-F7 — StorageTruthPostponementKey state has no genesis import/export

  • Severity: HIGH
  • File: x/audit/v1/keeper/genesis.go (omission)
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN at PR feat(audit): finalize LEP-6 consensus gap fixes #122 — also include RecheckEvidence dedup keys to prevent recheck replay across export round-trip.
  • What: setStorageTruthPostponedAtEpochID writes to ap/st/<acct>. ExportGenesis doesn't iterate StorageTruthPostponementPrefix(). Chain export+import drops every active storage-truth postponement → instant 'recovery' of every postponed node on restart.

121-F8 — CleanPassCount is cumulative since first state creation; recovery threshold trivially satisfied

  • Severity: HIGH
  • File: x/audit/v1/keeper/storage_truth_scoring.go (~222), x/audit/v1/keeper/enforcement.go (591-595)
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — snapshot CleanPassCountAtPostpone when setStorageTruthPostponedAtEpochID fires, compare delta only on recovery.
  • What: Long-tenured node with hundreds of historical PASSes that then accumulates postpone-level N satisfies CleanPassCount >= requiredPasses instantly the moment N decays back below threshold — zero post-postponement clean passes required. Spec §17 requires the count after postponement.

121-F10 — oldClassAFaultWindow hardcoded const = 21; not parameterised

121-F11 — Heal scheduling does not exclude the failing target from healer/verifier candidate pool

  • Severity: MEDIUM
  • File: x/audit/v1/keeper/storage_truth_heal_ops.go
  • Lines: 248-268, 231-246
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — filter active accounts to exclude state.LastTargetSupernodeAccount, postponed nodes, and recent class-A offenders for the ticket before passing to assignStorageTruthHealParticipants.
  • What: Spec §18 says deterministic singleton healer, but the entire LEP-6 model exists because the failing holder cannot be trusted. Self-healing the same target you just penalised defeats the point.

121-F12 — Strong-postpone band has no behavioural distinction from postpone

  • Severity: MEDIUM
  • File: x/audit/v1/keeper/enforcement.go:560-571, 137-152
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — either differentiate (distinct event/reason, stricter recovery: higher RecoveryCleanPassCount, longer min-time), or remove the strong-postpone surface to avoid suggesting it does something.
  • What: storageTruthBandEventType(strongPostpone) reuses postpone event; both bands call setSupernodePostponed with same reason; recovery treats both identically.

121-F13 — storageTruthEligibleChallengers ineligibility window check is inverted

  • Severity: MEDIUM
  • File: x/audit/v1/keeper/audit_peer_assignment.go:294
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — replace with if score >= threshold || (state.IneligibleUntilEpoch != 0 && state.IneligibleUntilEpoch >= epochID) { continue }.
  • What: if score >= threshold && (IneligibleUntilEpoch == 0 || IneligibleUntilEpoch >= epochID) { continue } makes a high-score reporter ineligible whenever IneligibleUntilEpoch == 0 (i.e. the K=7-epoch ban has never been set). Effectively unbounded ban as soon as score crosses threshold.

121-F15 — Divergence engine falls back to stale WindowPositiveCount/WindowNegativeCount aggregates

  • Severity: MEDIUM
  • File: x/audit/v1/keeper/storage_truth_divergence.go:45-48
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — drop the fallback (use fact-index result authoritatively) or apply same epochID - WindowStartEpoch >= divergenceWindow reset at read time.
  • What: When fact-index returns stats.total==0, code falls back to lazy-reset window counts. A long-inactive reporter with stale counts biases the median used by spec §15.2 divergence detection.

121-F5 — Old-Class-A predicate fallback uses total ClassACountWindow instead of bucket-scoped count

  • Severity: MEDIUM
  • File: x/audit/v1/keeper/enforcement.go
  • Lines: 651-654
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — track OldClassACountWindow separately or rely solely on the fact-index path which already enforces bucket+class.
  • What: Spec §17 requires '2 OLD Class A faults on DISTINCT tickets in 21 epochs'. Code's fallback can be satisfied by 2 RECENT Class A + any single old-bucket fail of any class — over-counts.

121-F9 — Class-B postpone fallback uses 14-epoch reset window where spec demands 7

  • Severity: MEDIUM
  • File: x/audit/v1/keeper/enforcement.go (639-642), storage_truth_scoring.go (241-245)
  • Status at PR feat(audit): finalize LEP-6 consensus gap fixes #122 tip: OPEN — drop the count-window fallback for Class-B and rely on the windowed fact-index, which already uses classBWindow=7.
  • What: Spec §17: '4 Class B faults in 7 epochs'. ClassBCountWindow resets only on epochID-WindowStartEpoch >= StorageTruthPatternEscalationWindow (default 14). Fallback over-postpones.

This review is posted as a COMMENT (not REQUEST_CHANGES) so it does not block merge mechanically — but the CRITICAL and HIGH items must be triaged before activation. I'm available to walk through any of these in detail.

— Zee

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements LEP-6 storage-truth “truth enforcement” and self-healing lifecycle by extending audit module params/state, adding new KV indexes for transcript/failure evidence, integrating recheck + divergence scoring, and tightening simulation/system/integration coverage around the new behaviors.

Changes:

  • Add new storage-truth params (thresholds/windows/deadlines) and update validation/defaulting semantics (including treating UNSPECIFIED as a no-op mode).
  • Implement transcript/failure indexing, reporter divergence scoring at epoch end, heal-op scheduling priority rules, and recheck evidence processing with replay protection.
  • Expand unit/integration/system tests and simulation operation registry to cover the new LEP-6 workflows and edge cases.

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
x/audit/v1/types/params_test.go Updates defaulting expectations for enforcement mode.
x/audit/v1/types/params.go Adds LEP-6 params/keys/defaults and expands validation logic.
x/audit/v1/types/keys.go Adds KV key prefixes/builders for postponement, recheck dedup, and fact indexes.
x/audit/v1/types/events.go Adds new storage-truth event types and attributes.
x/audit/v1/simulation/storage_truth.go Adds no-op simulation handlers for new storage-truth msgs.
x/audit/v1/module/simulation_test.go Updates weighted-ops tests to include storage-truth ops.
x/audit/v1/module/simulation.go Registers weighted operations for new storage-truth msgs.
x/audit/v1/keeper/storage_truth_scoring_internal_test.go Updates scoring/decay/trust-band unit tests for new model.
x/audit/v1/keeper/storage_truth_scoring.go Refactors scoring to be result-aware; adds new bookkeeping, decay, and history tracking.
x/audit/v1/keeper/storage_truth_recheck_state.go Adds KV dedup state for recheck evidence submissions.
x/audit/v1/keeper/storage_truth_postponement_state.go Adds KV state for storage-truth postponement tracking.
x/audit/v1/keeper/storage_truth_heal_ops_test.go Adjusts heal-op scheduling tests for new eligibility predicate fields.
x/audit/v1/keeper/storage_truth_heal_ops.go Adds enforcement gating, eligibility predicate, priority sort, and configurable deadlines.
x/audit/v1/keeper/storage_truth_fact_indexes.go Introduces transcript/failure/reporter-result KV indexes and helpers.
x/audit/v1/keeper/storage_truth_divergence_test.go Adds tests for reporter divergence penalties and volume gating.
x/audit/v1/keeper/storage_truth_divergence.go Implements divergence scoring based on rolling-window outliers vs median.
x/audit/v1/keeper/query_storage_truth_test.go Updates query expectations for new scoring deltas.
x/audit/v1/keeper/query_assigned_targets.go Applies eligibility filtering for challengers before target assignment.
x/audit/v1/keeper/msg_submit_epoch_report_test.go Adds FULL-mode compound proof coverage requirement test.
x/audit/v1/keeper/msg_submit_epoch_report_storage_truth_scores_test.go Updates score expectations for new deltas/decay/trust multiplier logic.
x/audit/v1/keeper/msg_submit_epoch_report_storage_proofs.go Adds compound coverage validation for FULL enforcement mode.
x/audit/v1/keeper/msg_submit_epoch_report.go Filters challengers by eligibility and indexes transcripts before scoring.
x/audit/v1/keeper/msg_storage_truth_test.go Adds extensive unit tests for recheck + heal verification quorum + edge cases.
x/audit/v1/keeper/msg_storage_truth.go Implements recheck evidence flow; adjusts heal claim/verification and finalize behavior.
x/audit/v1/keeper/enforcement_predicates_test.go Adds tests for enforcement matrix predicates and recovery gating.
x/audit/v1/keeper/enforcement.go Adds storage-truth banding, enforcement, postponement tracking, and recovery path.
x/audit/v1/keeper/audit_peer_assignment_test.go Adds tests for one-third coverage assignment vs legacy mode.
x/audit/v1/keeper/audit_peer_assignment.go Adds storage-truth assignment algorithm + challenger eligibility filtering.
x/audit/v1/keeper/abci.go Runs reporter divergence scoring at end-block before heal-op processing.
tests/systemtests/lep5_action_test.go Extends test signature expiration horizon for stability.
tests/systemtests/audit_test_helpers_test.go Adds epoch seed derivation, enforcement-mode mutator, and transcript seeding helpers.
tests/systemtests/audit_submit_and_query_test.go Sets enforcement mode to UNSPECIFIED for legacy assignment expectations.
tests/systemtests/audit_storage_truth_edge_cases_test.go Adds end-to-end edge-case tests for enforcement/recovery/replay/heal failure paths.
tests/systemtests/audit_recovery_enforcement_test.go Increases epoch length + adds timeouts; forces UNSPECIFIED mode for determinism.
tests/systemtests/audit_peer_ports_enforcement_test.go Uses epoch-derived seed instead of header hash for deterministic assignment.
tests/systemtests/audit_peer_observation_completeness_test.go Ensures the actual prober is used when asserting completeness failure.
tests/systemtests/audit_host_requirements_enforcement_test.go Forces UNSPECIFIED mode for legacy host-requirement enforcement tests.
tests/systemtests/audit_host_requirements_bypass_test.go Forces UNSPECIFIED mode for legacy host-requirement bypass tests.
tests/system/audit/msg_storage_truth_test.go Adds msg-server “system” tests using a live app wiring (no mocks).
tests/integration/audit/keeper_test.go Adds keeper integration suite using real codec + IAVL KV store.
proto/lumera/audit/v1/params.proto Extends Params protobuf with new LEP-6 fields.
proto/lumera/audit/v1/audit.proto Extends protobuf state for node/reporter/ticket tracking and adds docs/comments.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +88 to +99
// Replay protection: one recheck per (epoch, ticket, creator).
if m.HasRecheckEvidence(sdkCtx, req.EpochId, req.TicketId, req.Creator) {
return nil, errorsmod.Wrapf(types.ErrInvalidRecheckEvidence, "recheck evidence already submitted for epoch %d ticket %q by %q", req.EpochId, req.TicketId, req.Creator)
}
m.SetRecheckEvidence(sdkCtx, req.EpochId, req.TicketId, req.Creator)

// Derive current epoch for scoring context.
params := m.GetParams(sdkCtx).WithDefaults()
currentEpoch, err := deriveEpochAtHeight(sdkCtx.BlockHeight(), params)
if err != nil {
return nil, err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants