Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions abis/ModelRegistry.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,56 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getActiveModelsWithDetails",
"inputs": [],
"outputs": [
{
"name": "models",
"type": "tuple[]",
"internalType": "struct IModelRegistry.ModelDetails[]",
"components": [
{
"name": "modelId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "modelName",
"type": "string",
"internalType": "string"
},
{
"name": "modelVerifier",
"type": "address",
"internalType": "address"
},
{
"name": "verificationStrategy",
"type": "uint8",
"internalType": "enum IModelRegistry.VerificationStrategy"
},
{
"name": "computeCost",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "requiredFUCUs",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "isActive",
"type": "bool",
"internalType": "bool"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getRandomActiveModel",
Expand Down
23 changes: 23 additions & 0 deletions abis/SertnNodesManager.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,29 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getAllNodesWithDetails",
"inputs": [],
"outputs": [
{
"name": "nodeDetails",
"type": "uint256[8][]",
"internalType": "uint256[8][]"
},
{
"name": "supportedModels",
"type": "uint256[][]",
"internalType": "uint256[][]"
},
{
"name": "modelAllocations",
"type": "uint256[][]",
"internalType": "uint256[][]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getAvailableFucusForOperatorModel",
Expand Down
8 changes: 7 additions & 1 deletion abis/SertnServiceManager.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@
},
{
"type": "function",
"name": "taskCompleted",
"name": "taskResolved",
"inputs": [
{
"name": "_operator",
Expand Down Expand Up @@ -556,6 +556,12 @@
"indexed": false,
"internalType": "uint256"
},
{
"name": "token",
"type": "address",
"indexed": false,
"internalType": "contract IERC20"
},
{
"name": "currentInterval",
"type": "uint32",
Expand Down
240 changes: 240 additions & 0 deletions abis/SertnTaskManager.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,150 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getTaskHistoryStats",
"inputs": [],
"outputs": [
{
"name": "totalTasks",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "resolvedTasks",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "rejectedTasks",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "pendingTasksCount",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getTasksByModel",
"inputs": [
{
"name": "modelId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "offset",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "limit",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getTasksByOperator",
"inputs": [
{
"name": "operator",
"type": "address",
"internalType": "address"
},
{
"name": "offset",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "limit",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getTasksByState",
"inputs": [
{
"name": "state",
"type": "uint8",
"internalType": "enum ISertnTaskManager.TaskState"
},
{
"name": "offset",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "limit",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getTasksByUser",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
},
{
"name": "offset",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "limit",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "initialize",
Expand Down Expand Up @@ -494,6 +638,102 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "tasksByModel",
"inputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "tasksByOperator",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "tasksByState",
"inputs": [
{
"name": "",
"type": "uint8",
"internalType": "enum ISertnTaskManager.TaskState"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "tasksByUser",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "transferOwnership",
Expand Down
3 changes: 3 additions & 0 deletions client/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ dependencies = [
"eth-abi>=5.2.0",
"python-dotenv>=1.1.1",
"gitpython>=3.1.45",
"cachetools>=6.2.1",
"pymemcache>=4.0.0",
"starlette>=0.49.1",
Comment on lines +23 to +25
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify Starlette dependency necessity.

Starlette is already a transitive dependency of FastAPI (line 14). Adding it explicitly could be intentional for version pinning, but may also introduce version conflicts if FastAPI expects a different Starlette version.

Please verify:

  1. Whether this explicit dependency is necessary or if FastAPI's transitive dependency suffices
  2. That the minimum version (0.49.1) is compatible with the FastAPI version in use (0.115.12)

🏁 Script executed:

#!/bin/bash
# Check if FastAPI 0.115.12 has any Starlette version constraints
pip index versions fastapi | grep -A 10 "0.115.12" || echo "Checking FastAPI dependencies..."
python3 -c "
import urllib.request
import json
try:
    with urllib.request.urlopen('https://pypi.org/pypi/fastapi/0.115.12/json') as response:
        data = json.loads(response.read())
        deps = data.get('info', {}).get('requires_dist', [])
        starlette_deps = [d for d in deps if 'starlette' in d.lower()]
        if starlette_deps:
            print('FastAPI 0.115.12 Starlette requirements:')
            for dep in starlette_deps:
                print(f'  {dep}')
        else:
            print('No explicit Starlette dependency found')
except Exception as e:
    print(f'Error: {e}')
"

Length of output: 2168


I need to search more specifically for FastAPI 0.115.12's Starlette requirements to get a definitive answer.

Based on my verification, I've found a critical issue with the Starlette dependency constraint.

Critical: Starlette version 0.49.1 is incompatible with FastAPI 0.115.12.

FastAPI 0.115.12 requires starlette >=0.40.0,<0.47.0, but the added constraint specifies starlette>=0.49.1, which falls outside the upper bound. This will cause dependency resolution failures during installation.

This appears to be a typo. Verify the intended Starlette version—likely should be >=0.40.1 or similar to align with FastAPI's compatibility range.

🤖 Prompt for AI Agents
In client/pyproject.toml around lines 23 to 25, the Starlette constraint
"starlette>=0.49.1" is incompatible with FastAPI 0.115.12 (which requires
starlette >=0.40.0,<0.47.0); change the Starlette requirement to a compatible
range (for example "starlette>=0.40.0,<0.47.0" or the specific patch you intend
such as "starlette>=0.40.1,<0.47.0") so dependency resolution succeeds, then run
dependency resolution (pip/poetry) to verify no conflicts.

]

[project.optional-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions client/src/aggregator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from aggregator.errors import InvalidProofError
from aggregator.server import AggregatorServer
from common.abis import ERC20_ABI, STRATEGY_ABI
from common.addresses import addresses
from common.auto_update import AutoUpdate
from common.config import AggregatorConfig
from common.constants import (
Expand All @@ -31,6 +32,7 @@


def run_aggregator(config: AggregatorConfig) -> None:
addresses.init_addresses(chain_id=config.chain_id)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify error handling for address initialization.

The address initialization is correctly placed before any component that might need addresses. However, ensure proper error handling for initialization failures.

Please verify:

  1. That config.chain_id is always set (not None or invalid)
  2. That init_addresses raises appropriate exceptions on failure (the FileNotFoundError from the addresses module is visible in snippets)
  3. Whether calling init_addresses multiple times (e.g., in tests) is safe

🏁 Script executed:

#!/bin/bash
# Check if init_addresses handles being called multiple times
ast-grep --pattern 'def init_addresses($$$) {
  $$$
}'

# Check for any guard against multiple initialization
rg -n -A5 -B5 'def init_addresses' client/src/common/addresses.py

# Look for chain_id validation in config
rg -n -C3 'chain_id' client/src/common/config.py

Length of output: 776


I need to gather more detailed information about the init_addresses implementation and how it's being called.

Now I need to examine the actual implementation of init_addresses:

I need to examine the actual implementation more carefully. Let me search for the complete context:

Based on my verification through the script output and web research, I can now generate the rewritten review comment:

Add error handling for address initialization failures and verify initialization safety.

The config.chain_id parameter is validated with a constraint (ge=1) and has a default value (31337), so it should always be available. However, two concerns need addressing:

  1. Error handling: The init_addresses method attempts to load a deployment file based on chain_id. If the file doesn't exist (invalid chain, missing deployment), FileNotFoundError will be raised. Consider wrapping the call with appropriate exception handling or documenting expected exceptions.

  2. Multiple initialization: Verify that calling init_addresses multiple times (e.g., in tests) is safe and idempotent. If state is stored, ensure it can be reinitialized without side effects.


🏁 Script executed:

#!/bin/bash
# Get full init_addresses implementation
rg -n -A20 'def init_addresses' client/src/common/addresses.py

# Check how addresses is imported and used in main.py
rg -n -B2 -A10 'addresses' client/src/aggregator/main.py | head -50

Length of output: 140


Let me search for these files in the repository:


🏁 Script executed:

#!/bin/bash
# Find addresses-related files
fd -name "addresses.py"

# Find aggregator main.py
fd -name "main.py" -path "*/aggregator/*"

# List directory structure to understand repo layout
find . -maxdepth 3 -type d -name "client" -o -name "aggregator" -o -name "common" 2>/dev/null | head -20

Length of output: 514


Let me search for these files with corrected paths:


🏁 Script executed:

#!/bin/bash
# Find addresses.py
fd addresses.py ./client/src/common

# Find main.py in aggregator
fd main.py ./client/src/aggregator

# Get full init_addresses implementation
cat ./client/src/common/addresses.py | grep -n -A 30 "def init_addresses"

# Check how addresses is used in main.py
cat ./client/src/aggregator/main.py | grep -n -B 3 -A 5 "addresses"

Length of output: 3322


Add guard clause to prevent multiple initializations.

Error handling for init_addresses is already well-implemented with specific FileNotFoundError and KeyError exceptions. However, the method sets an _initialized flag but lacks a guard clause to check it before re-initializing. This means multiple calls will re-execute the initialization logic, which is inefficient and could fail if called again after the deployment file becomes unavailable.

Suggested fix: Add a guard at the start of init_addresses:

def init_addresses(self, chain_id: int) -> None:
    if self._initialized:
        return
    # ... rest of implementation

The config.chain_id is always valid (has constraint ge=1 and default value 31337).

🤖 Prompt for AI Agents
In client/src/aggregator/main.py around line 35, init_addresses is called but
the init_addresses implementation lacks a guard to prevent re-initialization;
add an early-return guard at the top of init_addresses that checks
self._initialized and returns immediately if True, leaving the existing
FileNotFoundError/KeyError handling and setting of _initialized unchanged so
subsequent calls are no-ops.

logger.info("Starting Sertn Aggregator...")
aggregator = Aggregator(config=config)
threading.Thread(target=aggregator.start_sending_new_tasks, args=[]).start()
Expand Down
Loading