Skip to content

Conversation

@MicBun
Copy link
Member

@MicBun MicBun commented Jan 29, 2026

resolves: https://github.com/truflation/website/issues/3205

Summary by CodeRabbit

  • New Features

    • Settlement enabled by default (30‑minute schedule) with public actions to view and update settings.
    • Background jobs now use fresh read transactions for up-to-date reads.
  • Bug Fixes

    • Scheduler uses a persistent shutdown context for graceful termination and starts independent of block context.
    • Scheduler requests missing attestations and continues runs; switched to a user-level signer in settlement flows.
  • Tests

    • Expanded coverage for scheduler resilience, attestation flows, decoding/queries, and transaction construction (includes an accidental duplicate test).
  • Chores

    • Updated bridge/escrow addresses in tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@MicBun MicBun self-assigned this Jan 29, 2026
@holdex
Copy link

holdex bot commented Jan 29, 2026

Time Submission Status

Member Status Time Action Last Update
MicBun ✅ Submitted 12h Update time Jan 31, 2026, 3:41 AM

You can submit time with the command. Example:

@holdex pr submit-time 15m

See available commands to help comply with our Guidelines.

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Adds a ShutdownContext to tn_settlement and uses it for scheduler lifecycle; introduces an EngineOps interface and attestation request flow with ABI-decoding of stored query_components and fresh-read DB pool support; adds settlement_config migration/actions; updates ctor call sites and tests accordingly.

Changes

Cohort / File(s) Summary
Scheduler & lifecycle
extensions/tn_settlement/extension.go, extensions/tn_settlement/scheduler_lifecycle.go, extensions/tn_settlement/scheduler/scheduler.go, extensions/tn_settlement/scheduler/scheduler_test.go
Add ShutdownContext() and integrate it into scheduler start/stop; introduce EngineOps interface; scheduler requests attestations for markets missing them; add test TestSchedulerJobRunsAfterParentContextCanceled (duplicated in patch).
Engine ops: attestation, ABI decode & DBPool
extensions/tn_settlement/internal/engine_ops.go, extensions/tn_settlement/internal/engine_ops_test.go
Add QueryComponents type, dbPool field and constructor arg; add getFreshReadTx, GetMarketQueryComponents, decodeQueryComponents, RequestAttestationForMarket; implement ABI decode/encode, tx build/sign/broadcast paths; extensive new tests validating decoding and request flow.
Digest: fresh-read support & ctor changes
extensions/tn_digest/internal/engine_ops.go, extensions/tn_digest/tn_digest.go, extensions/tn_digest/*_test.go
Add dbPool to EngineOperations, add getFreshReadTx helper, switch background reads to fresh transactions; update constructor signature and call sites (tests insert nil where appropriate).
Integration & call-site updates
extensions/tn_settlement/tn_settlement.go, extensions/tn_settlement/settlement_integration_test.go, extensions/tn_digest/*
Update NewEngineOperations call sites to pass new DBPool argument (or nil in tests); adjust expectations in integration tests for default settlement config.
Migrations: settlement config actions
internal/migrations/041-settlement-config-actions.sql
Add migration enabling automatic settlement by default and two actions: update_settlement_config (role-gated, validated) and get_settlement_config (public view).
Order-book validation SQL
internal/migrations/037-order-book-validation.sql
Rework validate_market_collateral to compute expected collateral aggregated across all unsettled markets per bridge instead of per-market.
Tests & bridge helpers
tests/streams/hoodi_bridge_coexistence_test.go, tests/streams/utils/erc20/helper.go
Update escrow addresses, add per-bridge/test state resets and clarifying comments to ensure isolated test state.

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler as "Scheduler\n(cron)"
  participant EngineOps as "EngineOps\n(RequestAttestation)"
  participant DB as "Database\n(query_components)"
  participant Signer as "Signer\n(GetUserSigner)"
  participant Broadcaster as "Broadcaster\n(tx broadcast)"

  rect rgba(135,206,235,0.5)
  Scheduler->>EngineOps: RunOnce -> FindUnsettledMarkets()
  Scheduler->>EngineOps: For market without attestation -> RequestAttestationForMarket(ctx, market)
  end

  rect rgba(144,238,144,0.5)
  EngineOps->>DB: GetMarketQueryComponents(queryID)
  DB-->>EngineOps: returns ABI-encoded query_components ([]byte)
  EngineOps->>EngineOps: decodeQueryComponents -> QueryComponents
  EngineOps->>Signer: Sign request_attestation payload
  Signer-->>EngineOps: signature
  EngineOps->>Broadcaster: Broadcast signed tx
  Broadcaster-->>EngineOps: tx result / hash
  EngineOps-->>Scheduler: report success/failure
  Scheduler->>Scheduler: continue processing markets
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • pr-time-tracker
  • outerlook

Poem

🐇 I hopped through cron and bytes tonight,
I decoded queries by moonbeam light,
I signed a plea and sent it clear,
Tests reset bridges — all good cheer,
Settlement hums — carrot delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chore: configure automatic market settlement triggers' accurately describes the main changes: adding settlement configuration, lifecycle management, and scheduler integration for automatic market settlement.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch settleConfig

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

@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

🤖 Fix all issues with AI agents
In `@extensions/tn_settlement/scheduler/scheduler_test.go`:
- Around line 371-422: The test never asserts the job ran because jobExecuted is
never signaled; replace the nil EngineOps with a mock implementation of the
EngineOps interface (or a minimal struct) whose settleMarkets (or the method
invoked by the scheduler job) writes to the jobExecuted channel when called,
pass that mock into NewSettlementScheduler, then change the Sleep-based check to
wait for a value from jobExecuted with a timeout (e.g., select on <-jobExecuted
and time.After) before stopping the scheduler; update
TestSchedulerJobRunsAfterParentContextCanceled to reference the mock and the
settleMarkets method so the test fails if the job never executes.

In `@internal/migrations/041-settlement-config-actions.sql`:
- Line 1: The migration header inside the file 041-settlement-config-actions.sql
is incorrect (it reads "MIGRATION 042"); update the top comment to match the
filename (change "MIGRATION 042" to "MIGRATION 041") so the migration number in
the header and the file name are consistent; verify the comment string at the
top of the file that currently contains "MIGRATION 042" and replace it with
"MIGRATION 041".
🧹 Nitpick comments (1)
tests/streams/hoodi_bridge_coexistence_test.go (1)

39-46: Package-level mutable state may cause issues if tests run in parallel.

The variables ttPointCounter, tt2PointCounter, ttPrevPoint, and tt2PrevPoint are package-level mutable state. While each test function resets ttPrevPoint and tt2PrevPoint, the counters are only incremented, never reset. If these tests or other tests in the package run in parallel (t.Parallel()), this could lead to data races.

Consider either:

  1. Ensuring these tests never run in parallel (current implicit behavior)
  2. Moving these variables into a test-local struct passed between helper functions

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

🤖 Fix all issues with AI agents
In `@extensions/tn_settlement/scheduler/scheduler.go`:
- Around line 258-268: In RunOnce, don’t swallow errors from
engineOps.AttestationExists — replace the current "continue" on err with
handling that surfaces the failure: log the error with s.logger (include
"query_id" and the error) and return or propagate the error to the caller so the
caller can detect failure; adjust the block around
engineOps.AttestationExists(ctx, market.Hash) (and keep the existing
RequestAttestationForMarket call unchanged) so failures from AttestationExists
are not silently ignored.
🧹 Nitpick comments (2)
extensions/tn_settlement/internal/engine_ops.go (1)

354-380: Make “market not found” vs. “invalid query_components” distinguishable.
Right now, a row with NULL/invalid query_components is surfaced as “market not found,” which obscures data issues. Consider returning an explicit error for NULL or unexpected types.

♻️ Proposed refinement
 func (e *EngineOperations) GetMarketQueryComponents(ctx context.Context, queryID int) (*QueryComponents, error) {
 	var queryComponentsBytes []byte
 	var found bool
+	var foundRow bool

 	err := e.engine.ExecuteWithoutEngineCtx(ctx, e.db,
 		`SELECT query_components FROM ob_queries WHERE id = $query_id`,
 		map[string]any{"query_id": int64(queryID)},
 		func(row *common.Row) error {
 			if len(row.Values) >= 1 {
-				if bytes, ok := row.Values[0].([]byte); ok {
-					queryComponentsBytes = bytes
-					found = true
-				}
+				foundRow = true
+				if row.Values[0] == nil {
+					return fmt.Errorf("query_components is NULL for query_id=%d", queryID)
+				}
+				bytes, ok := row.Values[0].([]byte)
+				if !ok {
+					return fmt.Errorf("unexpected query_components type: %T", row.Values[0])
+				}
+				queryComponentsBytes = bytes
+				found = true
 			}
 			return nil
 		})

 	if err != nil {
 		return nil, fmt.Errorf("fetch query_components: %w", err)
 	}
-	if !found {
+	if !foundRow {
 		return nil, fmt.Errorf("market not found: query_id=%d", queryID)
 	}
+	if !found {
+		return nil, fmt.Errorf("query_components missing or invalid for query_id=%d", queryID)
+	}

 	return decodeQueryComponents(queryComponentsBytes)
 }
extensions/tn_settlement/internal/engine_ops_test.go (1)

615-646: Guard against silent streamID truncation in test helper.
Copying into a [32]byte silently truncates longer inputs. A small length check makes test failures clearer.

♻️ Proposed tweak
 func encodeQueryComponentsForTest(dataProvider, streamID, actionName string, argsBytes []byte) ([]byte, error) {
+	if len(streamID) > 32 {
+		return nil, errors.New("streamID must be <= 32 bytes")
+	}
 	addressType, err := gethAbi.NewType("address", "", nil)
 	if err != nil {
 		return nil, err
 	}

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

🤖 Fix all issues with AI agents
In `@extensions/tn_settlement/scheduler/scheduler_test.go`:
- Around line 95-105: The mock in mockEngineOps (method FindUnsettledMarkets)
currently ignores the incoming ctx and can allow tests to pass when the job's
parent context is canceled; update FindUnsettledMarkets to check ctx.Err() at
the start and, if non-nil, call the test failure function (e.g., t.Fatal or fail
helper) via the onFindUnsettledMarkets callback or by making the mock take a
*testing.T so the mock fails the test; ensure this check is present in
mockEngineOps.FindUnsettledMarkets (and the other mock instances referenced
around lines 412-456) so the test fails immediately when ctx is canceled rather
than returning an empty slice.
🧹 Nitpick comments (1)
extensions/tn_settlement/internal/engine_ops_test.go (1)

6-13: Tighten ArgsBytes assertions to verify content, not just length.
A wrong payload with the same length would pass today; a byte-wise check makes the test more precise.

🔧 Suggested tweak
 import (
+	"bytes"
 	"context"
 	"errors"
 	"fmt"
@@
-	if len(decoded.ArgsBytes) != len(argsBytes) {
-		t.Errorf("ArgsBytes length mismatch: expected %d, got %d", len(argsBytes), len(decoded.ArgsBytes))
-	}
+	if !bytes.Equal(decoded.ArgsBytes, argsBytes) {
+		t.Errorf("ArgsBytes mismatch: expected %v, got %v", argsBytes, decoded.ArgsBytes)
+	}

Also applies to: 477-509

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

🤖 Fix all issues with AI agents
In `@extensions/tn_settlement/scheduler_lifecycle.go`:
- Around line 127-128: The scheduler is started with context.Background() in
startScheduler which prevents cancellation on graceful node shutdown and can
leave the scheduler running even if leaderwatch callbacks (settlementLeaderLose)
never fire; change the startup to derive the scheduler context from a node-level
shutdown context (or Extension's context) so it is cancelled on process
shutdown, or ensure Extension.Close() is invoked by the node shutdown sequence;
specifically, replace the Background() usage with a cancellable parent context
tied to the node/Extension lifecycle (use the existing Extension context or a
passed-in shutdownCtx) and keep the explicit scheduler.Stop() call in
Extension.Close() so the scheduler reliably stops on shutdown.
🧹 Nitpick comments (1)
extensions/tn_settlement/internal/engine_ops_test.go (1)

653-675: Consider adding a compile-time interface check.

The mock implementation has stub methods that return nil. Adding a compile-time assertion ensures the mock stays in sync if the common.Engine interface changes.

🔧 Suggested improvement
 // Mock engine for query components test
 type mockEngineForQueryComponents struct {
 	queryComponents []byte
 }
+
+// Compile-time check that mockEngineForQueryComponents implements common.Engine
+var _ common.Engine = (*mockEngineForQueryComponents)(nil)

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

🤖 Fix all issues with AI agents
In `@internal/migrations/037-order-book-validation.sql`:
- Around line 108-143: The aggregates $total_shares_all_markets and
$total_buys_cents_all_markets can overflow as BIGINT when summing across
markets; change the SELECT SUM(...) casts to NUMERIC (avoid ::BIGINT), declare
those variables as NUMERIC instead of BIGINT, and propagate NUMERIC types into
the downstream calculations (shares_collateral, buys_collateral,
expected_collateral) so all arithmetic is done in NUMERIC to prevent overflow
before converting or comparing as needed.

@MicBun MicBun merged commit 7ed2689 into main Jan 31, 2026
7 of 8 checks passed
@MicBun MicBun deleted the settleConfig branch January 31, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants