Skip to content

feat: enable attestation queue for validator signing workflow#1204

Merged
MicBun merged 2 commits intomainfrom
nodeNotify
Oct 9, 2025
Merged

feat: enable attestation queue for validator signing workflow#1204
MicBun merged 2 commits intomainfrom
nodeNotify

Conversation

@MicBun
Copy link
Member

@MicBun MicBun commented Oct 9, 2025

Adds tn_attestation extension to support data attestation signing. This enables Kuneiform schemas to queue attestation hashes that will be signed by the leader validator.

Extension structure:

  • queue_for_signing() precompile (SYSTEM access only)
  • Thread-safe attestation queue with deduplication
  • Leader watch integration (prepared for signing worker)
  • Comprehensive test coverage

resolves: #1205

Description

Related Problem

Summary by CodeRabbit

  • New Features

    • Added a tn_attestation extension that provides a leader-aware, deterministic precompile for queueing attestation hashes and ties into startup and leader lifecycle hooks with readiness and leadership logging.
    • Introduced a global in-memory, thread-safe, deduplicating FIFO queue with max-capacity and eviction for pending attestations.
  • Tests

    • Added comprehensive tests covering queue behavior, concurrency, deduplication, FIFO eviction, and precompile deterministic behavior across leader/non-leader scenarios.

Adds tn_attestation extension to support data attestation signing.
This enables Kuneiform schemas to queue attestation hashes that will
be signed by the leader validator.

Extension structure:
- queue_for_signing() precompile (SYSTEM access only)
- Thread-safe attestation queue with deduplication
- Leader watch integration (prepared for signing worker)
- Comprehensive test coverage

resolves: https://github.com/trufnetwork/kwil-db/issues/1620
@MicBun MicBun requested a review from outerlook October 9, 2025 12:02
@MicBun MicBun self-assigned this Oct 9, 2025
@MicBun MicBun added the type: feat New feature or request label Oct 9, 2025
@holdex
Copy link

holdex bot commented Oct 9, 2025

Time Submission Status

Member Status Time Action Last Update
MicBun ✅ Submitted 6h Update time Oct 9, 2025, 2:15 PM
@outerlook ❌ Missing - ⚠️ Submit time -

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Walkthrough

Adds a new tn_attestation extension: registers it at startup, provides a precompile queue_for_signing(attestation_hash), implements a thread-safe in-memory attestation queue with deduplication and FIFO eviction, wires engine-ready and leader lifecycle hooks, and adds unit tests for queue and precompile behavior.

Changes

Cohort / File(s) Summary
Extension Registration
extensions/register.go
Imports tn_attestation and calls tn_attestation.InitializeExtension() during package init to register the extension.
tn_attestation: Constants
extensions/tn_attestation/constants.go
Adds exported constant ExtensionName = "tn_attestation".
tn_attestation: Precompile
extensions/tn_attestation/precompile.go
Adds precompile queue_for_signing(attestation_hash text) that validates input, checks proposer/leader status, enqueues hashes only on leader, logs enqueue, and returns nil for determinism. No cache configured.
tn_attestation: Queue Implementation
extensions/tn_attestation/queue.go
Implements AttestationQueue with thread-safe deduplication, FIFO order tracking, max-capacity eviction, methods NewAttestationQueue, Enqueue, DequeueAll, Len, Clear, Copy, and a lazy singleton accessor GetAttestationQueue().
tn_attestation: Initialization & Hooks
extensions/tn_attestation/tn_attestation.go
Adds InitializeExtension() registering the precompile, engine-ready hook, and leader watcher callbacks (OnAcquire, OnLose, OnEndBlock). Contains TODOs for starting/stopping a signing worker and processing the queue.
Tests
extensions/tn_attestation/tn_attestation_test.go
Adds tests for AttestationQueue (construction, enqueue/dupes, dequeue, clear, copy, concurrency, max-size eviction, FIFO order) and for the queue_for_signing precompile (leader vs non-leader behavior, empty-hash error, determinism).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller (Attestation Tx)
  participant PC as Precompile: tn_attestation.queue_for_signing
  participant P as Proposer Check
  participant Q as AttestationQueue

  C->>PC: queue_for_signing(attestation_hash)
  PC->>PC: validate non-empty
  PC->>P: is current node proposer/leader?
  alt Node is leader
    P-->>PC: yes
    PC->>Q: Enqueue(attestation_hash)
    Q-->>PC: enqueued / duplicate ignored
  else Not leader or unknown proposer
    P-->>PC: no/unknown
    PC-->>PC: no-op (deterministic)
  end
  PC-->>C: nil (deterministic)
Loading
sequenceDiagram
  autonumber
  participant S as Startup
  participant EXT as tn_attestation.InitializeExtension
  participant HK as Engine Ready Hook
  participant LW as Leader Watcher
  participant Q as AttestationQueue

  S->>EXT: call InitializeExtension()
  EXT->>HK: hooks.RegisterEngineReadyHook(...)
  EXT->>LW: leaderwatch.Register(OnAcquire/OnLose/OnEndBlock)
  HK-->>Q: (on ready) log queue size if app present
  LW-->>EXT: OnAcquire/OnLose/OnEndBlock callbacks (TODO: start/stop worker, process queue)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit hums where hashes queue,
I guard the line and sort what's new.
If I am leader, I'll hold each sign,
If not, I wait — deterministic, fine.
Come hop along, and nibble through. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the addition of an attestation queue integration into the validator signing workflow, directly reflecting the primary feature introduced.
Linked Issues Check ✅ Passed The changes fully implement the queue_for_signing precompile, ensure no-ops for non-leaders, enqueue hashes for leaders, maintain determinism, and include tests covering leadership scenarios and edge cases, meeting all acceptance criteria from issue #1205.
Out of Scope Changes Check ✅ Passed All modifications introduce and integrate the tn_attestation extension, its queue, precompile, and tests as specified by the linked issue, with no unrelated or extraneous code changes present.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
✨ 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 nodeNotify

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89bccbf and f12142e.

📒 Files selected for processing (4)
  • extensions/tn_attestation/precompile.go (1 hunks)
  • extensions/tn_attestation/queue.go (1 hunks)
  • extensions/tn_attestation/tn_attestation.go (1 hunks)
  • extensions/tn_attestation/tn_attestation_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • extensions/tn_attestation/precompile.go
🧰 Additional context used
🧬 Code graph analysis (2)
extensions/tn_attestation/tn_attestation.go (3)
extensions/tn_attestation/constants.go (1)
  • ExtensionName (3-3)
extensions/leaderwatch/leaderwatch.go (2)
  • Register (149-162)
  • Callbacks (13-17)
extensions/tn_attestation/queue.go (1)
  • GetAttestationQueue (115-120)
extensions/tn_attestation/tn_attestation_test.go (2)
extensions/tn_attestation/constants.go (1)
  • ExtensionName (3-3)
extensions/tn_attestation/queue.go (3)
  • NewAttestationQueue (22-27)
  • MaxQueueSize (9-9)
  • GetAttestationQueue (115-120)
⏰ 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

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.

outerlook
outerlook previously approved these changes Oct 9, 2025
Copy link

@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: 3

🧹 Nitpick comments (7)
extensions/tn_attestation/tn_attestation.go (1)

50-95: Remove commented-out code or document as reference implementation.

The leader lifecycle callbacks contain TODO comments with commented-out code (lines 61-62, 76-77, 88-94). Consider either:

  1. Remove the commented code if the final implementation will differ
  2. Keep it as reference but add a comment explaining it's a planned implementation

Example:

 // onLeaderAcquire is called when the node becomes the leader.
-// TODO: Start the signing worker here
+// TODO: Start the signing worker here (reference implementation below)
 func onLeaderAcquire(ctx context.Context, app *common.App, block *common.BlockContext) {
 	if app == nil || app.Service == nil {
 		return
 	}
 
 	logger := app.Service.Logger.New(ExtensionName)
 	logger.Info("tn_attestation: acquired leadership")
 
-	// TODO: Start signing worker
+	// Reference implementation (to be completed in future PR):
 	// ext := GetExtension()
 	// ext.startSigningWorker()
 }
extensions/tn_attestation/tn_attestation_test.go (6)

184-188: Ensure precompile is registered before use to avoid nil-initializer panics

Subtests rely on "RegistrationSuccess" running first. Running this subtest in isolation can panic. Call a helper to ensure registration.

Apply this diff:

+    ensurePrecompileRegistered(t)
     // Initialize the precompile
     initializer := precompiles.RegisteredPrecompiles()[ExtensionName]
     precompile, err := initializer(context.Background(), app.Service, nil, "", nil)
     require.NoError(t, err)

Add this helper to the file (outside tests):

// test helper to guarantee registration without ordering assumptions
func ensurePrecompileRegistered(t *testing.T) {
	t.Helper()
	if _, ok := precompiles.RegisteredPrecompiles()[ExtensionName]; !ok {
		require.NoError(t, registerPrecompile())
	}
}

232-235: Repeat: ensure precompile registered in each subtest

Same robustness concern here.

+    ensurePrecompileRegistered(t)
     // Initialize the precompile
     initializer := precompiles.RegisteredPrecompiles()[ExtensionName]
     precompile, err := initializer(context.Background(), app.Service, nil, "", nil)
     require.NoError(t, err)

268-271: Repeat: ensure precompile registered in each subtest

Protect against isolated subtest runs.

+    ensurePrecompileRegistered(t)
     // Initialize the precompile
     initializer := precompiles.RegisteredPrecompiles()[ExtensionName]
     precompile, err := initializer(context.Background(), app.Service, nil, "", nil)
     require.NoError(t, err)

329-332: Repeat: ensure precompile registered in each subtest

Protect against ordering assumptions.

+    ensurePrecompileRegistered(t)
     // Initialize the precompile
     initializer := precompiles.RegisteredPrecompiles()[ExtensionName]
     precompile, err := initializer(context.Background(), appLeader.Service, nil, "", nil)
     require.NoError(t, err)

371-374: Repeat: ensure precompile registered in each subtest

Protect against ordering assumptions.

+    ensurePrecompileRegistered(t)
     // Initialize the precompile
     initializer := precompiles.RegisteredPrecompiles()[ExtensionName]
     precompile, err := initializer(context.Background(), app.Service, nil, "", nil)
     require.NoError(t, err)

190-191: Avoid relying on Methods[0]; select method by name "queue_for_signing"

Indexing assumes method order and is brittle. Resolve by name to future‑proof tests.

Example pattern:

var handler func(*common.EngineContext, *common.App, []any, func([]any) error) error
for _, m := range precompile.Methods {
	if m.Name == "queue_for_signing" {
		handler = m.Handler
		break
	}
}
require.NotNil(t, handler)

Also applies to: 237-238, 273-274, 333-337, 376-377

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02061e8 and d83d923.

📒 Files selected for processing (6)
  • extensions/register.go (2 hunks)
  • extensions/tn_attestation/constants.go (1 hunks)
  • extensions/tn_attestation/precompile.go (1 hunks)
  • extensions/tn_attestation/queue.go (1 hunks)
  • extensions/tn_attestation/tn_attestation.go (1 hunks)
  • extensions/tn_attestation/tn_attestation_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
extensions/register.go (1)
extensions/tn_attestation/tn_attestation.go (1)
  • InitializeExtension (16-35)
extensions/tn_attestation/precompile.go (2)
extensions/tn_attestation/constants.go (1)
  • ExtensionName (3-3)
extensions/tn_attestation/queue.go (2)
  • GetAttestationQueue (86-91)
  • AttestationQueue (7-10)
extensions/tn_attestation/tn_attestation.go (3)
extensions/tn_attestation/constants.go (1)
  • ExtensionName (3-3)
extensions/leaderwatch/leaderwatch.go (2)
  • Register (149-162)
  • Callbacks (13-17)
extensions/tn_attestation/queue.go (1)
  • GetAttestationQueue (86-91)
extensions/tn_attestation/tn_attestation_test.go (2)
extensions/tn_attestation/queue.go (2)
  • NewAttestationQueue (13-17)
  • GetAttestationQueue (86-91)
extensions/tn_attestation/constants.go (1)
  • ExtensionName (3-3)
⏰ 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
🔇 Additional comments (10)
extensions/register.go (1)

6-6: LGTM! Extension registration follows established pattern.

The tn_attestation extension is properly imported and initialized alongside existing extensions.

Also applies to: 17-17

extensions/tn_attestation/constants.go (1)

1-3: LGTM! Clean constant definition.

The extension name constant is appropriately defined and will be used consistently throughout the extension.

extensions/tn_attestation/precompile.go (1)

26-65: LGTM! Handler correctly maintains determinism.

The handler properly implements the requirements:

  • ✅ Validates non-empty attestation hash
  • ✅ Defensively checks leadership by comparing proposer identity
  • ✅ Only enqueues when node is leader
  • ✅ Returns nil for all validators to maintain determinism
  • ✅ Includes debug logging when available

The leadership detection logic (lines 38-47) correctly treats missing context as non-leader, ensuring deterministic behavior across all validators.

extensions/tn_attestation/tn_attestation.go (2)

16-35: LGTM! Extension initialization is robust.

The initialization sequence properly registers all components with appropriate error handling:

  • ✅ Precompile registration
  • ✅ Engine ready hook
  • ✅ Leader watcher callbacks

The use of panic for registration failures is appropriate for initialization-time errors.


38-48: LGTM! Engine ready hook properly logs initialization.

The hook safely checks for nil values and logs the queue state, providing useful observability.

extensions/tn_attestation/queue.go (5)

7-17: LGTM! Queue structure and constructor are well-designed.

  • ✅ Uses sync.RWMutex for efficient read/write concurrency
  • ✅ Uses map[string]struct{} for O(1) deduplication (zero memory overhead for values)
  • ✅ Clean constructor pattern

21-31: LGTM! Enqueue correctly implements deduplication.

  • ✅ Proper write locking
  • ✅ Returns boolean indicating whether hash was added
  • ✅ Efficient O(1) duplicate detection

34-51: LGTM! DequeueAll safely extracts all hashes.

  • ✅ Proper write locking
  • ✅ Returns nil for empty queue (clearer than empty slice)
  • ✅ Clears queue after dequeuing

54-58: LGTM! Read operations correctly use RLock.

Both Len() and Copy() properly use RLock for read-only operations, allowing concurrent reads while preventing data races.

Also applies to: 68-77


86-91: LGTM! Singleton pattern correctly implemented.

  • ✅ Uses sync.Once for thread-safe lazy initialization
  • ✅ Exported function provides global access for precompile and worker

Copy link

@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

🧹 Nitpick comments (1)
extensions/tn_attestation/tn_attestation.go (1)

39-40: Consider extracting the repeated nil checks.

The pattern if app == nil || app.Service == nil { return ... } appears in four callbacks. While the current code is clear, you could optionally extract this to a helper function to reduce duplication:

// validateApp returns false if app or app.Service is nil
func validateApp(app *common.App) bool {
    return app != nil && app.Service != nil
}

Then use it in the callbacks:

func engineReadyHook(ctx context.Context, app *common.App) error {
    if !validateApp(app) {
        return nil
    }
    // ... rest of function
}

However, this is a minor improvement and the current explicit checks are perfectly acceptable.

Also applies to: 53-54, 69-70, 85-86

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d83d923 and a5bdc7a.

📒 Files selected for processing (2)
  • extensions/tn_attestation/tn_attestation.go (1 hunks)
  • extensions/tn_attestation/tn_attestation_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • extensions/tn_attestation/tn_attestation_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
extensions/tn_attestation/tn_attestation.go (3)
extensions/tn_attestation/constants.go (1)
  • ExtensionName (3-3)
extensions/leaderwatch/leaderwatch.go (2)
  • Register (149-162)
  • Callbacks (13-17)
extensions/tn_attestation/queue.go (1)
  • GetAttestationQueue (86-91)
⏰ 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
🔇 Additional comments (4)
extensions/tn_attestation/tn_attestation.go (4)

16-35: LGTM! Clean initialization setup.

The extension registration is well-structured with clear error messages. Using panic for initialization failures is an acceptable pattern in Go, ensuring the node fails fast if critical components cannot be registered.


37-48: LGTM! Well-implemented status hook.

The nil checks are appropriate, and logging the queue size provides useful diagnostic information at startup.


50-64: Ensure the TODO is tracked in the project.

The placeholder for starting the signing worker is clear, but please verify that this follow-up work is tracked (e.g., in an issue or project board) so the signing workflow can be completed in a future PR.


66-80: Ensure the TODO is tracked in the project.

Similar to onLeaderAcquire, please verify that the signing worker shutdown is tracked for future implementation.

@MicBun MicBun merged commit 9c8e016 into main Oct 9, 2025
6 of 7 checks passed
@MicBun MicBun deleted the nodeNotify branch October 9, 2025 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feat New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problem: No precompile to notify leader of signing tasks

2 participants