Skip to content

feat: enable attestation querying and retrieval#1216

Merged
MicBun merged 4 commits into
mainfrom
chore/query-att
Oct 14, 2025
Merged

feat: enable attestation querying and retrieval#1216
MicBun merged 4 commits into
mainfrom
chore/query-att

Conversation

@outerlook
Copy link
Copy Markdown
Contributor

@outerlook outerlook commented Oct 14, 2025

Description

Enables users to query and retrieve attestations using transaction IDs, introducing new retrieval endpoints and simplifying the attestation workflow.

New User-Facing Features

  • get_signed_attestation($request_tx_id): Retrieve a specific signed attestation by its transaction ID
  • list_attestations($requester, $limit, $offset, $order_by): Query attestations with pagination and filtering
  • request_attestation now returns both request_tx_id and attestation_hash for flexible retrieval

Simplified API

  • Reduced sign_attestation from 4 required parameters to 2 (request_tx_id, signature)
  • Transaction ID as natural identifier instead of composite key lookup
  • Attestation retrieval no longer requires tracking multiple metadata fields

Technical Improvements

  • Schema uses request_tx_id as primary key for simpler queries
  • Added composite index for efficient signing workflow
  • Fixed DB handle bug ensuring leader processes current block attestations correctly
  • Comprehensive test coverage for new retrieval endpoints and error cases

Related Problem

How Has This Been Tested?

New comprehensive test suite covering:

  • Attestation request flow with new return format
  • Retrieval with error cases (not found, not yet signed)
  • Payload structure validation
  • Integration tests updated for 2-parameter signing
  • All existing tests pass with new schema

Unit test added for DB handle update bug fix.

Summary by CodeRabbit

  • New Features
    • Attestations are now keyed and tracked by request_tx_id; requests return request_tx_id plus attestation_hash. Signing/submission payloads use request_tx_id + signature. Added views to fetch signed attestations and list attestations with filtering/pagination.
  • Bug Fixes
    • Normalized ECDSA signature V to consistently produce EVM-compatible 27/28 values.
  • Tests
    • New test helpers and extended suites covering request_tx_id flows, signing, retrieval, and listing.
  • Chores
    • Database schema and migrations updated to support request_tx_id primary key.

…ethod

- Updated the attestation schema to use request_tx_id as the primary key, improving uniqueness and clarity.
- Introduced the verify_payload method in the tn_attestation extension for validating attestation signatures against canonical payloads.
- Refactored existing actions to accommodate the new request_tx_id and adjusted related SQL queries.
- Enhanced integration tests to cover new functionality and ensure robust validation of attestation processes.
- Improved error handling and logging throughout the attestation workflow for better operational visibility.

These changes enhance the reliability and maintainability of the attestation system, ensuring accurate processing and verification of attestations.
…station extension

- Deleted the verify_payload method from the tn_attestation extension, simplifying the precompile registration.
- Removed associated tests and the verify_precompile.go file to streamline the codebase.
- Updated comments in precompile.go to reflect the removal of the verify_payload method.

These changes enhance the maintainability of the tn_attestation extension by eliminating unused functionality.
@outerlook outerlook self-assigned this Oct 14, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 14, 2025

Walkthrough

Adds request_tx_id as the primary identifier across schema, stored procedures, processing, signing, submission, and tests; propagates it through prepared signing payloads and broadcaster/test harnesses; normalizes ECDSA V values; wires extension to App during leadership transitions; introduces test helpers and retrieval/listing tests.

Changes

Cohort / File(s) Summary
Schema & actions
internal/migrations/023-attestation-schema.sql, internal/migrations/024-attestation-actions.sql
Add request_tx_id TEXT PRIMARY KEY; convert old PK to unique composite; update request_attestation to return (request_tx_id, attestation_hash); change sign_attestation to (request_tx_id, signature); add get_signed_attestation(request_tx_id) and list_attestations(...); add index for signing queries.
Processor & prepared payloads
extensions/tn_attestation/processor.go, extensions/tn_attestation/processor_test.go
Add requestTxID to attestationRecord and PreparedSignature; read request_tx_id from SQL rows; validate/clone columns with new helpers (cloneBytesValue*, extractInt64); propagate RequestTxID into prepared signing work; update tests to expect string request_tx_id arg.
Worker / submit & integration tests
extensions/tn_attestation/worker.go, extensions/tn_attestation/...integration_test.go, extensions/tn_attestation/harness_integration_test.go
Change submitSignature to accept *PreparedSignature and return error; encode payload as (request_tx_id, signature) only; update argument indices/decoding and retry/error flow; extend broadcaster and test harness to decode/store request_tx_id (new decodeStringArg).
Signer normalization
extensions/tn_attestation/signer.go
Normalize ECDSA V to 27/28 by stripping offsets, masking LSB, and adding 27 (robust handling of 0/1, 27/28, or chainId-offset V).
Leadership / app wiring
extensions/tn_attestation/tn_attestation.go, extensions/tn_attestation/tn_attestation_test.go
Call ext.setApp(app) in onLeaderAcquire/onLeaderLose/onLeaderEndBlock; tests updated to provide App with Engine and DB in end-block path.
Precompile comment
extensions/tn_attestation/precompile.go
Shortened explanatory comment; no behavior changes.
Tests & helpers
tests/streams/attestation/test_helpers.go, tests/streams/attestation/attestation_request_test.go, tests/streams/attestation/attestation_retrieval_test.go
Add AttestationTestHelper and TestAddresses; refactor request tests to use helper and surface request_tx_id; add retrieval/listing test suites (TestGetSignedAttestation, TestListAttestations) and helpers for signing, requesting, counting, and digest computation.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Requester
  participant Engine as Engine/Extension
  participant DB as DB (actions)
  participant Worker as Signer Worker
  participant Signer as ECDSA Signer

  Note over Requester,DB: request_attestation → returns request_tx_id
  Requester->>Engine: call request_attestation(args)
  Engine->>DB: request_attestation(...)
  DB-->>Engine: (request_tx_id, attestation_hash)
  Engine-->>Requester: (request_tx_id, attestation_hash)

  Note over Engine,Worker: prepare includes request_tx_id
  Engine->>Worker: prepareSigningWork(RequestTxID, Digest, ...)
  Worker->>Signer: SignDigest(digest)
  Signer-->>Worker: signature (65 bytes, V normalized)
  Worker->>DB: sign_attestation(request_tx_id, signature)
  alt DB success
    DB-->>Worker: code=OK
  else DB failure
    DB-->>Worker: code!=OK
    Worker-->>Worker: retry / log and return
  end

  Note over Requester,DB: retrieval by request_tx_id
  Requester->>Engine: call get_signed_attestation(request_tx_id)
  Engine->>DB: get_signed_attestation(request_tx_id)
  DB-->>Engine: canonical_payload + signature
  Engine-->>Requester: signed attestation payload
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Suggested labels

type: feat

Suggested reviewers

  • MicBun
  • williamrusdyputra

Poem

I nibbled bytes and found a string,
A tx_id snug upon my wing.
I flipped one bit in V so sly,
Now signatures hop to twenty-seven high.
Hop—signed, listed, fetched—my carrot dance 🎋🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR includes unrelated changes beyond retrieval and workflow simplification objectives, notably the ECDSA signature V normalization in signer.go and a comment-only edit in precompile.go, which do not pertain to the scope of issue #1199. Please isolate the ECDSA V normalization and comment cleanup into a separate PR or clearly justify their inclusion to maintain focus on the attestation querying and retrieval objectives.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly summarizes the core feature of this changeset by stating that attestation querying and retrieval are being enabled, directly reflecting the primary user-facing enhancements introduced in this PR.
Linked Issues Check ✅ Passed The implementation adds the get_signed_attestation and list_attestations public VIEW actions with the specified parameters and validation logic, updates request_attestation and sign_attestation to return transaction IDs, and supports filtering, pagination, and ordering as defined in issue #1199.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/query-att

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
extensions/tn_attestation/tn_attestation.go (1)

174-180: Guard block may be nil in logging

block can be nil; using block.Height will panic. Log conditionally.

-        logger.Info("tn_attestation: processing queued attestations",
-            "count", len(hashes),
-            "block_height", block.Height)
+        if block != nil {
+            logger.Info("tn_attestation: processing queued attestations",
+                "count", len(hashes),
+                "block_height", block.Height)
+        } else {
+            logger.Info("tn_attestation: processing queued attestations",
+                "count", len(hashes))
+        }
🧹 Nitpick comments (12)
internal/migrations/023-attestation-schema.sql (1)

40-43: Prefer a partial index for unsigned attestations

For queries like WHERE attestation_hash = $1 AND signature IS NULL, a partial index on (attestation_hash) WHERE signature IS NULL is typically smaller and faster than a composite including the signature column.

Proposed addition (keep existing if you still need it for other patterns):

CREATE INDEX IF NOT EXISTS ix_att_unsigned_by_hash
    ON attestations(attestation_hash)
    WHERE signature IS NULL;
tests/streams/attestation/test_helpers.go (2)

123-129: Surface action errors by default (or add a “CallActionOK” helper)

CallAction doesn’t fail the test when res.Error is set. Either assert res.Error == nil here or add a separate helper (e.g., CallActionOK) that does, and use it in tests that expect success.

Example:

 func (h *AttestationTestHelper) CallAction(actionName string, args []any, resultFn func(*common.Row) error) *common.CallResult {
   engineCtx := h.NewEngineContext()
   res, err := h.platform.Engine.Call(engineCtx, h.platform.DB, "", actionName, args, resultFn)
   require.NoError(h.t, err, "call action %s", actionName)
-  return res
+  require.Nil(h.t, res.Error, "action %s returned error: %v", actionName, res.Error)
+  return res
 }

Or:

func (h *AttestationTestHelper) CallActionOK(...) *common.CallResult { /* assert res.Error == nil */ }

159-190: Nice helper flow; validate signature format if API normalizes V

If sign_attestation expects normalized ECDSA V, consider asserting the produced signature conforms (length 65 and V normalized) to catch regressions.

extensions/tn_attestation/processor.go (1)

198-203: *Make bytesCloneOrNil resilient to []byte (and avoid panics)

Decode drivers can yield either []byte or *[]byte. Handle both and return a descriptive error for unexpected types.

-func bytesCloneOrNil(v any) []byte {
-	if v == nil {
-		return nil
-	}
-	return bytes.Clone(v.([]byte))
-}
+func bytesCloneOrNil(v any) []byte {
+	if v == nil {
+		return nil
+	}
+	switch b := v.(type) {
+	case []byte:
+		return bytes.Clone(b)
+	case *[]byte:
+		if b == nil {
+			return nil
+		}
+		return bytes.Clone(*b)
+	default:
+		// Be defensive; upstream callers should handle this error if you choose to plumb it.
+		panic(fmt.Errorf("unexpected BYTEA type %T", v))
+	}
+}
extensions/tn_attestation/signer.go (1)

63-72: V normalization to {27,28} is appropriate; add tests for variants

Logic correctly maps 0/1, 27/28, and 27+N forms to 27/28. Add unit tests covering each variant to prevent regressions. Optionally assert signature length is 65 before indexing.

extensions/tn_attestation/processor_test.go (1)

76-85: TestPrepareSigningWork: assert RequestTxID as well

Since the engine row now carries request_tx_id, assert it in ps to catch mapping regressions.

 prepared, err := ext.prepareSigningWork(context.Background(), hex.EncodeToString(hash[:]))
 require.NoError(t, err)
 require.Len(t, prepared, 1)
 
 ps := prepared[0]
+assert.Equal(t, "0xprepare", ps.RequestTxID)
 assert.Equal(t, hash[:], ps.Hash)
 assert.Equal(t, payload, ps.Payload)
 assert.Equal(t, int64(123), ps.CreatedHeight)
 assert.Len(t, ps.Signature, 65)

Also applies to: 95-104

tests/streams/attestation/attestation_retrieval_test.go (5)

54-67: get_signed_attestation: happy path assertions

Verifying payload non-empty and larger than signature is a good proxy. Consider also asserting signature length=65 after splitting (as in PayloadStructure).


85-103: PayloadStructure: split canonical||signature correctly

Solid structural checks. Optionally assert canonical starts with version/algo bytes if you want extra guardrails.


146-164: list_attestations: no-filter path validates shape

Counts and column count checks look good. You might also assert attestation_hash length (32) and requester non-empty for a sample row.


176-188: Pagination logic: basic coverage OK

Two pages of 3 results each is fine. Consider adding an explicit order_by override test (ASC) to ensure ordering behavior is respected.


190-193: MaxLimit test exercises high limit

Given server clamps to 5000, consider a separate test that passes >5000 and asserts that returned count never exceeds 5000 when dataset is large (future-proofing).

extensions/tn_attestation/worker.go (1)

71-86: Minor: don’t hard-fail when Requester is missing (log instead)

submitSignature currently returns an error if Requester is empty, but Requester is only used for logging/status. Consider downgrading to a warning and proceed with signing to avoid blocking if DB ever yields NULLs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a8343e and c7e9b4c.

📒 Files selected for processing (14)
  • extensions/tn_attestation/harness_integration_test.go (7 hunks)
  • extensions/tn_attestation/integration_test.go (5 hunks)
  • extensions/tn_attestation/precompile.go (1 hunks)
  • extensions/tn_attestation/processor.go (3 hunks)
  • extensions/tn_attestation/processor_test.go (4 hunks)
  • extensions/tn_attestation/signer.go (1 hunks)
  • extensions/tn_attestation/tn_attestation.go (3 hunks)
  • extensions/tn_attestation/tn_attestation_test.go (1 hunks)
  • extensions/tn_attestation/worker.go (3 hunks)
  • internal/migrations/023-attestation-schema.sql (3 hunks)
  • internal/migrations/024-attestation-actions.sql (4 hunks)
  • tests/streams/attestation/attestation_request_test.go (3 hunks)
  • tests/streams/attestation/attestation_retrieval_test.go (1 hunks)
  • tests/streams/attestation/test_helpers.go (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-10T13:00:13.731Z
Learnt from: outerlook
PR: trufnetwork/node#1207
File: internal/migrations/023-attestation-schema.sql:20-21
Timestamp: 2025-10-10T13:00:13.731Z
Learning: In the attestations table (internal/migrations/023-attestation-schema.sql), the primary key is (requester, created_height, attestation_hash) because attestation_hash is computed deterministically from user input only (version|algo|data_provider|stream_id|action_id|args) and does not include created_height. This allows the same user to request the same attestation at different block heights.

Applied to files:

  • internal/migrations/023-attestation-schema.sql
  • internal/migrations/024-attestation-actions.sql
📚 Learning: 2025-10-10T13:00:14.189Z
Learnt from: outerlook
PR: trufnetwork/node#1207
File: internal/migrations/024-attestation-actions.sql:58-76
Timestamp: 2025-10-10T13:00:14.189Z
Learning: In the attestation system for internal/migrations/024-attestation-actions.sql, the attestation_hash is computed from (version|algo|data_provider|stream_id|action_id|args) and intentionally excludes created_height. This design ensures the hash is deterministic based only on user input, not network state like block height.

Applied to files:

  • internal/migrations/023-attestation-schema.sql
  • internal/migrations/024-attestation-actions.sql
🧬 Code graph analysis (3)
tests/streams/attestation/attestation_retrieval_test.go (4)
tests/streams/attestation/test_helpers.go (9)
  • NewTestAddresses (41-50)
  • NewAttestationTestHelper (60-66)
  • TestActionIDGet (23-23)
  • AttestationTestHelper (53-57)
  • SignatureLength (27-27)
  • InvalidTxID (30-30)
  • MinCanonicalLength (28-28)
  • TestActionIDList (24-24)
  • TestAddresses (34-38)
tests/streams/utils/runner.go (1)
  • RunSchemaTest (37-116)
internal/migrations/migration.go (1)
  • GetSeedScriptPaths (15-48)
tests/streams/utils/utils.go (1)
  • GetTestOptionsWithCache (75-88)
tests/streams/attestation/attestation_request_test.go (2)
tests/streams/attestation/test_helpers.go (4)
  • NewTestAddresses (41-50)
  • NewAttestationTestHelper (60-66)
  • TestActionIDRequest (22-22)
  • AttestationTestHelper (53-57)
extensions/tn_utils/serialization.go (2)
  • EncodeActionArgs (45-76)
  • EncodeQueryResultCanonical (145-184)
tests/streams/attestation/test_helpers.go (1)
extensions/tn_utils/serialization.go (1)
  • EncodeActionArgs (45-76)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: acceptance-test
🔇 Additional comments (30)
extensions/tn_attestation/precompile.go (1)

16-16: Comment clarification LGTM

Shorter comment is accurate; keeping Cache nil here is correct since effects are leader-local and non-deterministic state is avoided.

extensions/tn_attestation/tn_attestation_test.go (3)

531-535: Good: App carries Engine and DB for end-block scan

Including Engine and DB in App aligns with ext.setApp(app) usage and fallback scan path.


112-132: go.mod already specifies Go 1.24.1 (>=1.22). No changes required.


559-567: toInt helper is defined in this package’s test files; no changes needed.

extensions/tn_attestation/tn_attestation.go (1)

105-105: setApp(app) addition LGTM

Ensures extension has full App (Engine/DB) during leadership lifecycle and end-block processing.

Also applies to: 135-135, 165-165

extensions/tn_attestation/processor.go (4)

24-27: PreparedSignature.RequestTxID addition looks good

Carrying request_tx_id end-to-end is correct and unblocks the 2‑arg sign flow. No issues.


49-53: Query shape update aligns with schema; consider verifying index usage

Selecting request_tx_id first and filtering by attestation_hash with signature IS NULL matches the new workflow. Ensure the migration’s index supports this filter and order (e.g., partial or composite index on (attestation_hash, signature IS NULL, created_height)).

If helpful, I can draft an EXPLAIN plan check for the exact SQL to confirm index usage in CI.


43-45: Confirm attestation_hash material (spec vs implementation)

Comments say SQL computes hash from request parameters. Acceptance notes suggest including created_height and result in the canonical bytes used for hash. Today computeAttestationHash excludes those. Please confirm the intended spec and align SQL + Go accordingly; at minimum, document the chosen hash material to avoid future divergence.


124-131: Including RequestTxID in prepared payload is correct

Plumbing request_tx_id here keeps the worker simple and matches the new 2‑arg action.

extensions/tn_attestation/harness_integration_test.go (5)

103-131: Validating request_attestation now returns (request_tx_id, attestation_hash)

Good coverage of types and arity; asserting non-empty txID/hash prevents silent regressions.


200-207: PreparedSignature now includes RequestTxID in tests — good

Asserting RequestTxID continuity strengthens end-to-end guarantees.


315-337: Row mapping includes request_tx_id — OK

Select list/order matches mapping; the decode handles nullable signature/pubkey/height correctly.


374-381: Broadcaster decodes 2‑arg layout (request_tx_id, signature) correctly

Shape checks and decoding paths look solid; aligns with worker payload.

Also applies to: 406-407


434-447: decodeStringArg helper: concise and safe

Handles string/*string variants with require checks. LGTM.

extensions/tn_attestation/processor_test.go (2)

182-189: 2‑arg decoding check in submit path is correct

Verifying request_tx_id and signature length = 65 is sufficient here.


223-237: decodeStringArg test helper is fine

Covers string/*string; mirrors harness helper. LGTM.

tests/streams/attestation/attestation_retrieval_test.go (3)

17-52: Top-level retrieval tests are well-structured

Schema harness + per-scenario subtests provide clear coverage. Good use of require.


69-84: Error cases (not found / not yet signed) covered

Clear expectation messages; aligns with acceptance criteria.


165-174: Filter by requester: good targeted assertion

Index 2 requester check matches declared return schema.

extensions/tn_attestation/integration_test.go (2)

62-73: Integration: requestTxID propagation validated

Asserting HashHex, CreatedHeight, and RequestTxID covers the new data flow. Good.

Also applies to: 112-118


143-148: Broadcaster captures request_tx_id and signature correctly

Robust argument decoding with clear errors; state capture is useful for assertions.

Also applies to: 159-187

extensions/tn_attestation/worker.go (2)

126-133: Switch to 2‑argument payload (request_tx_id, signature) is correct

Encoding request_tx_id as string and trimming payload to two args aligns with the new action signature.

Also applies to: 139-140


267-289: Status worker: clearer success/failure logging and early return

Logging branches and early return on resolution look good. Behavior: one confirmed/failure result ends the loop; unresolved continues with backoff. LGTM.

Also applies to: 291-296

internal/migrations/024-attestation-actions.sql (4)

18-20: LGTM! Transaction ID properly captured as primary identifier.

The function signature correctly returns both request_tx_id and attestation_hash, with request_tx_id captured from @txid. This aligns with the PR objective of using transaction ID as the natural identifier.


104-106: LGTM! Simplified signature and consistent request_tx_id usage.

The action signature correctly reduces to 2 required parameters as specified in the PR objectives. All validation, lookups, and error messages consistently use request_tx_id as the identifier.

Also applies to: 121-150


154-199: LGTM! Attestation retrieval implementation meets all requirements.

The get_signed_attestation action correctly:

  • Uses request_tx_id as the lookup key (per PR objectives).
  • Validates both existence and signature presence.
  • Returns the concatenated canonical payload + signature.
  • Documents client-side verification responsibility clearly.

201-274: LGTM! Attestation listing implementation meets all requirements.

The list_attestations action correctly implements:

  • Optional requester filtering (NULL returns all attestations).
  • Pagination with reasonable limits (default 5000, max 5000).
  • Sorting by created_height with request_tx_id as secondary sort for deterministic ordering.
  • All required return columns as specified in PR objectives.
tests/streams/attestation/attestation_request_test.go (3)

21-34: LGTM! Helper-based test setup improves maintainability.

The refactoring to use AttestationTestHelper and centralized address management makes the tests more maintainable and reduces boilerplate code.


52-72: LGTM! Test correctly validates request_tx_id handling.

The test properly:

  • Captures both return values from request_attestation.
  • Validates that request_tx_id is non-empty and matches stored value.
  • Uses the helper pattern consistently.

97-140: LGTM! Row unpacking correctly handles request_tx_id field.

The attestationRow struct and fetchAttestationRow function correctly:

  • Add the requestTxID field to the struct.
  • Include request_tx_id in the SELECT statement as the first column.
  • Unpack row values with proper indexing (0=request_tx_id, 1=requester, etc.).
  • Use defensive copying for byte slices to prevent aliasing issues.

Comment thread extensions/tn_attestation/processor.go
Comment thread internal/migrations/023-attestation-schema.sql
@holdex
Copy link
Copy Markdown

holdex Bot commented Oct 14, 2025

Time Submission Status

Member Status Time Action Last Update
outerlook ✅ Submitted 6h Update time Oct 14, 2025, 11:07 PM
MicBun ✅ Submitted 20min Update time Oct 15, 2025, 12:13 AM

…sion

- Improved the fetchUnsignedAttestations method by adding type checks and error handling for various fields, ensuring robust data processing.
- Introduced helper functions for cloning byte slices and extracting int64 values, enhancing code clarity and maintainability.
- Updated the bytesCloneOrNil function to handle different input types more effectively.

These changes improve the reliability and maintainability of the attestation processing workflow, ensuring accurate handling of unsigned attestations.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7e9b4c and 4b3c491.

📒 Files selected for processing (1)
  • extensions/tn_attestation/processor.go (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: acceptance-test

Comment thread extensions/tn_attestation/processor.go Outdated
- Removed the bytesCloneOrNil function to simplify the codebase, retaining only the bytesClone function for cloning byte slices.
- Ensured consistent handling of the Requester field in the prepareSigningWork method.

These changes enhance code clarity and maintainability by reducing redundancy in byte handling functions.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
extensions/tn_attestation/processor.go (1)

55-96: Safer row decoding with explicit checks — nice; consider []byte fallback for request_tx_id

Great move to validated assertions and clear errors. To be driver-agnostic, accept []byte for request_tx_id as a fallback.

Apply this diff:

-            txID, ok := row.Values[0].(string)
-            if !ok {
-                return fmt.Errorf("unexpected request_tx_id type %T", row.Values[0])
-            }
+            var txID string
+            switch v := row.Values[0].(type) {
+            case string:
+                txID = v
+            case []byte:
+                txID = string(v)
+            default:
+                return fmt.Errorf("unexpected request_tx_id type %T", row.Values[0])
+            }
🧹 Nitpick comments (3)
extensions/tn_attestation/processor.go (3)

43-54: Query shape is fine; ensure index supports filter/sort

Plan should be index-friendly: WHERE attestation_hash = ? AND signature IS NULL ORDER BY created_height ASC. Confirm composite index order matches this (e.g., (attestation_hash, signature, created_height)) to avoid sorts.


154-162: Avoid re-signing identical digests (same hash across multiple requesters)

Many records for one attestation_hash likely share the same canonical payload; cache signatures per digest to cut duplicate signing calls.

Apply this diff:

-    prepared := make([]*PreparedSignature, 0, len(records))
+    prepared := make([]*PreparedSignature, 0, len(records))
+    // Cache signatures per digest to avoid repeated signing work.
+    seen := make(map[[32]byte][]byte)

     for _, rec := range records {
         payload, err := ParseCanonicalPayload(rec.canonical)
         if err != nil {
             return nil, fmt.Errorf("parse canonical payload: %w", err)
         }

         // Validate stored hash matches caller inputs; SQL computes it from request parameters.
         expectedHash := computeAttestationHash(payload)
         if !bytes.Equal(expectedHash[:], rec.hash) {
             return nil, fmt.Errorf("attestation hash mismatch: expected %x, db %x", expectedHash, rec.hash)
         }

-        digest := payload.SigningDigest()
-        signature, err := signer.SignDigest(digest[:])
-        if err != nil {
-            return nil, fmt.Errorf("sign digest: %w", err)
-        }
+        digest := payload.SigningDigest()
+        var signature []byte
+        if cached, ok := seen[digest]; ok {
+            signature = cached
+        } else {
+            sig, err := signer.SignDigest(digest[:])
+            if err != nil {
+                return nil, fmt.Errorf("sign digest: %w", err)
+            }
+            signature = sig
+            seen[digest] = bytesClone(signature)
+        }

260-271: Broaden extractInt64 to common driver variants

Some drivers return int32/*int32 for BIGINT in narrow ranges. Add these cases.

Apply this diff:

 func extractInt64(value any) (int64, error) {
     switch v := value.(type) {
     case int64:
         return v, nil
     case *int64:
         if v == nil {
             return 0, fmt.Errorf("value is NULL")
         }
         return *v, nil
+    case int32:
+        return int64(v), nil
+    case *int32:
+        if v == nil {
+            return 0, fmt.Errorf("value is NULL")
+        }
+        return int64(*v), nil
     default:
         return 0, fmt.Errorf("unexpected type %T", value)
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b3c491 and 6a30be0.

📒 Files selected for processing (1)
  • extensions/tn_attestation/processor.go (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: acceptance-test
🔇 Additional comments (5)
extensions/tn_attestation/processor.go (5)

16-16: Adding requestTxID to attestationRecord looks good

Keeps the natural key close to the record.


24-34: Propagating RequestTxID via PreparedSignature is correct

This surfaces the natural identifier to callers for logging/auditing.


142-146: Confirm attestation-hash algorithm matches DB; consider hashing canonical bytes

If the contract is “hash over result_canonical,” compute directly over rec.canonical to eliminate drift between encoders.

Apply this diff if appropriate:

-        expectedHash := computeAttestationHash(payload)
-        if !bytes.Equal(expectedHash[:], rec.hash) {
-            return nil, fmt.Errorf("attestation hash mismatch: expected %x, db %x", expectedHash, rec.hash)
-        }
+        expected := sha256.Sum256(rec.canonical)
+        if !bytes.Equal(expected[:], rec.hash) {
+            return nil, fmt.Errorf("attestation hash mismatch: expected %x, db %x", expected, rec.hash)
+        }

229-241: cloneBytesValue: solid defensive helper

Clear errors on NULL/unexpected types; matches usage for NOT NULL bytea columns.


243-258: cloneBytesValueAllowNil: good nullability handling

Appropriate for nullable requester; keeps surprises surfaced via errors for unexpected types.

@outerlook outerlook requested a review from MicBun October 14, 2025 17:33
Copy link
Copy Markdown
Contributor

@MicBun MicBun left a comment

Choose a reason for hiding this comment

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

Lgtm

@MicBun MicBun merged commit 0b92a38 into main Oct 14, 2025
7 checks passed
@MicBun MicBun deleted the chore/query-att branch October 14, 2025 23:07
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.

Problem: Users cannot retrieve signed attestations

2 participants