-
Notifications
You must be signed in to change notification settings - Fork 0
feat(recheck): add LEP-6 storage recheck evidence runtime #290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
j-rafique
merged 1 commit into
supernode/LEP-6-chain-client-extensions
from
supernode/LEP-6-recheck-evidence
May 4, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package queries | ||
|
|
||
| import ( | ||
| "context" | ||
| "database/sql" | ||
| "fmt" | ||
| "time" | ||
|
|
||
| audittypes "github.com/LumeraProtocol/lumera/x/audit/v1/types" | ||
| ) | ||
|
|
||
| type RecheckSubmissionRecord struct { | ||
| EpochID uint64 | ||
| TicketID string | ||
| TargetAccount string | ||
| ChallengedTranscriptHash string | ||
| RecheckTranscriptHash string | ||
| ResultClass audittypes.StorageProofResultClass | ||
| SubmittedAt int64 | ||
| } | ||
|
|
||
| const createStorageRecheckSubmissions = ` | ||
| CREATE TABLE IF NOT EXISTS storage_recheck_submissions ( | ||
| epoch_id INTEGER NOT NULL, | ||
| ticket_id TEXT NOT NULL, | ||
| target_account TEXT NOT NULL, | ||
| challenged_transcript_hash TEXT NOT NULL, | ||
| recheck_transcript_hash TEXT NOT NULL, | ||
| result_class INTEGER NOT NULL, | ||
| submitted_at INTEGER NOT NULL, | ||
| PRIMARY KEY (epoch_id, ticket_id) | ||
| );` | ||
|
|
||
| func (s *SQLiteStore) HasRecheckSubmission(ctx context.Context, epochID uint64, ticketID string) (bool, error) { | ||
| const stmt = `SELECT 1 FROM storage_recheck_submissions WHERE epoch_id = ? AND ticket_id = ? LIMIT 1` | ||
| var one int | ||
| err := s.db.QueryRowContext(ctx, stmt, epochID, ticketID).Scan(&one) | ||
| if err != nil { | ||
| if err == sql.ErrNoRows { | ||
| return false, nil | ||
| } | ||
| return false, err | ||
| } | ||
| return true, nil | ||
| } | ||
|
|
||
| func (s *SQLiteStore) RecordRecheckSubmission(ctx context.Context, epochID uint64, ticketID, targetAccount, challengedTranscriptHash, recheckTranscriptHash string, resultClass audittypes.StorageProofResultClass) error { | ||
| const stmt = `INSERT OR IGNORE INTO storage_recheck_submissions (epoch_id, ticket_id, target_account, challenged_transcript_hash, recheck_transcript_hash, result_class, submitted_at) VALUES (?, ?, ?, ?, ?, ?, ?)` | ||
| if epochID == 0 || ticketID == "" { | ||
| return fmt.Errorf("epoch_id and ticket_id are required") | ||
| } | ||
| _, err := s.db.ExecContext(ctx, stmt, epochID, ticketID, targetAccount, challengedTranscriptHash, recheckTranscriptHash, int32(resultClass), time.Now().Unix()) | ||
| return err | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package queries | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| audittypes "github.com/LumeraProtocol/lumera/x/audit/v1/types" | ||
| ) | ||
|
|
||
| type RecheckQueries interface { | ||
| HasRecheckSubmission(ctx context.Context, epochID uint64, ticketID string) (bool, error) | ||
| RecordRecheckSubmission(ctx context.Context, epochID uint64, ticketID, targetAccount, challengedTranscriptHash, recheckTranscriptHash string, resultClass audittypes.StorageProofResultClass) error | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package queries | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
|
|
||
| audittypes "github.com/LumeraProtocol/lumera/x/audit/v1/types" | ||
| "github.com/jmoiron/sqlx" | ||
| _ "github.com/mattn/go-sqlite3" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestRecheckSubmissionDedupKeyEpochTicket(t *testing.T) { | ||
| db := sqlx.MustConnect("sqlite3", ":memory:") | ||
| defer db.Close() | ||
| _, err := db.Exec(createStorageRecheckSubmissions) | ||
| require.NoError(t, err) | ||
| store := &SQLiteStore{db: db} | ||
| ctx := context.Background() | ||
|
|
||
| exists, err := store.HasRecheckSubmission(ctx, 7, "ticket-1") | ||
| require.NoError(t, err) | ||
| require.False(t, exists) | ||
|
|
||
| require.NoError(t, store.RecordRecheckSubmission(ctx, 7, "ticket-1", "target-a", "orig", "rh1", audittypes.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_PASS)) | ||
| exists, err = store.HasRecheckSubmission(ctx, 7, "ticket-1") | ||
| require.NoError(t, err) | ||
| require.True(t, exists) | ||
|
|
||
| // Same ticket in a different epoch is intentionally a different replay key. | ||
| exists, err = store.HasRecheckSubmission(ctx, 8, "ticket-1") | ||
| require.NoError(t, err) | ||
| require.False(t, exists) | ||
|
|
||
| // INSERT OR IGNORE makes local retry recording idempotent and preserves the | ||
| // first successful on-chain submission record. | ||
| require.NoError(t, store.RecordRecheckSubmission(ctx, 7, "ticket-1", "target-b", "orig2", "rh2", audittypes.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_RECHECK_CONFIRMED_FAIL)) | ||
| var target string | ||
| require.NoError(t, db.QueryRowContext(ctx, `SELECT target_account FROM storage_recheck_submissions WHERE epoch_id=? AND ticket_id=?`, 7, "ticket-1").Scan(&target)) | ||
| require.Equal(t, "target-a", target) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package recheck | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
|
|
||
| audittypes "github.com/LumeraProtocol/lumera/x/audit/v1/types" | ||
| sdktx "github.com/cosmos/cosmos-sdk/types/tx" | ||
| ) | ||
|
|
||
| type TxSubmitter interface { | ||
| SubmitStorageRecheckEvidence(ctx context.Context, epochID uint64, challengedSupernodeAccount, ticketID, challengedResultTranscriptHash, recheckTranscriptHash string, recheckResultClass audittypes.StorageProofResultClass, details string) (*sdktx.BroadcastTxResponse, error) | ||
| } | ||
|
|
||
| type Attestor struct { | ||
| self string | ||
| msg TxSubmitter | ||
| store Store | ||
| } | ||
|
|
||
| func NewAttestor(self string, msg TxSubmitter, store Store) *Attestor { | ||
| return &Attestor{self: strings.TrimSpace(self), msg: msg, store: store} | ||
| } | ||
|
|
||
| func (a *Attestor) Submit(ctx context.Context, c Candidate, r RecheckResult) error { | ||
| if a == nil || a.msg == nil || a.store == nil { | ||
| return fmt.Errorf("recheck attestor missing deps") | ||
| } | ||
| if !c.Valid() || c.TargetAccount == a.self || c.OriginalReporter == a.self { | ||
| return fmt.Errorf("invalid recheck candidate") | ||
| } | ||
| if strings.TrimSpace(r.TranscriptHash) == "" || !validRecheckResultClass(r.ResultClass) { | ||
| return fmt.Errorf("invalid recheck result") | ||
| } | ||
| _, err := a.msg.SubmitStorageRecheckEvidence(ctx, c.EpochID, c.TargetAccount, c.TicketID, c.ChallengedTranscriptHash, r.TranscriptHash, r.ResultClass, r.Details) | ||
| if err != nil { | ||
| if isAlreadySubmittedError(err) { | ||
| return a.store.RecordRecheckSubmission(ctx, c.EpochID, c.TicketID, c.TargetAccount, c.ChallengedTranscriptHash, r.TranscriptHash, r.ResultClass) | ||
| } | ||
| return err | ||
| } | ||
| return a.store.RecordRecheckSubmission(ctx, c.EpochID, c.TicketID, c.TargetAccount, c.ChallengedTranscriptHash, r.TranscriptHash, r.ResultClass) | ||
| } | ||
|
|
||
| func validRecheckResultClass(cls audittypes.StorageProofResultClass) bool { | ||
| switch cls { | ||
| case audittypes.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_PASS, | ||
| audittypes.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_RECHECK_CONFIRMED_FAIL, | ||
| audittypes.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_TIMEOUT_OR_NO_RESPONSE, | ||
| audittypes.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_OBSERVER_QUORUM_FAIL, | ||
| audittypes.StorageProofResultClass_STORAGE_PROOF_RESULT_CLASS_INVALID_TRANSCRIPT: | ||
| return true | ||
| default: | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| func isAlreadySubmittedError(err error) bool { | ||
| if err == nil { | ||
| return false | ||
| } | ||
| s := strings.ToLower(err.Error()) | ||
| return strings.Contains(s, "recheck evidence already submitted") | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.