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
18 changes: 17 additions & 1 deletion .github/workflows/backend_integration_tests_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,21 @@ jobs:
- name: Prepare GenVM cache directory
run: mkdir -p /tmp/genvm-cache/pc && chmod -R 0777 /tmp/genvm-cache

- name: Free up disk space
run: |
echo "=== Disk space before cleanup ==="
df -h
echo ""
echo "Removing unused hosted-runner tools and Docker artifacts..."
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker system prune --all --force --volumes
echo ""
echo "=== Disk space after cleanup ==="
df -h

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand Down Expand Up @@ -143,6 +158,7 @@ jobs:
http://0.0.0.0:4000/api

- name: Run tests (parallel)
timeout-minutes: 90
env:
TEST_PROVIDER: ${{ vars.LLM_PROVIDER || 'openrouter' }}
TEST_PROVIDER_MODEL: ${{ vars.LLM_MODEL || 'deepseek/deepseek-v3.2' }}
Expand All @@ -151,6 +167,7 @@ jobs:
# Validator smoke test runs serial (GenVM LLM module restarts on each CRUD op).
# Full CRUD coverage is in db-sqlalchemy/validators_registry_test.py (<1s).
- name: Run validator smoke test (serial)
timeout-minutes: 20
env:
TEST_PROVIDER: ${{ vars.LLM_PROVIDER || 'openrouter' }}
TEST_PROVIDER_MODEL: ${{ vars.LLM_MODEL || 'deepseek/deepseek-v3.2' }}
Expand Down Expand Up @@ -212,4 +229,3 @@ jobs:

# - name: Run Docker Compose
# run: docker compose -f tests/hardhat/docker-compose.yml --project-directory . up tests --build --force-recreate --always-recreate-deps

2 changes: 1 addition & 1 deletion .github/workflows/frontend-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ jobs:
with:
verbose: true
token: ${{ secrets.codecov_token }}
fail_ci_if_error: true
fail_ci_if_error: false
directory: frontend/coverage
12 changes: 9 additions & 3 deletions .github/workflows/genvm-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ on:
- "examples/contracts/**"
- "tests/load/contracts/**"
- "tests/direct/contracts/**"
- "tests/integration/icontracts/contracts/**"
push:
branches:
- main
paths:
- "examples/contracts/**"
- "tests/load/contracts/**"
- "tests/direct/contracts/**"
- "tests/integration/icontracts/contracts/**"

jobs:
lint-contracts:
Expand All @@ -30,9 +28,14 @@ jobs:
python-version: "3.12"

- name: Install genvm-linter
run: pip install genvm-linter
# This commit packages as 0.10.0 and includes SDK artifact resolution fixes
# needed by `genvm-lint check` for the v0.2.16 runner hashes below.
run: pip install "genvm-linter @ git+https://github.com/genlayerlabs/genvm-linter.git@fe426bcc28de0523294f5fb6b93280b1bb8de302"

- name: Lint example contracts
env:
# These examples currently pin v0.2.x runner hashes.
GENVM_VERSION: v0.2.16
run: |
failed=0
for f in examples/contracts/*.py; do
Expand All @@ -45,6 +48,9 @@ jobs:
exit $failed

- name: Lint test contracts
env:
# These test contracts currently pin v0.2.x runner hashes.
GENVM_VERSION: v0.2.16
run: |
failed=0
for f in tests/load/contracts/*.py tests/direct/contracts/*.py; do
Expand Down
119 changes: 70 additions & 49 deletions .github/workflows/load-test-oha.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ jobs:
- name: Prepare GenVM cache directory
run: mkdir -p /tmp/genvm-cache/pc && chmod -R 0777 /tmp/genvm-cache

- name: Free up disk space
run: |
echo "=== Disk space before cleanup ==="
df -h
echo ""
echo "Removing unused hosted-runner tools and Docker artifacts..."
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker system prune --all --force --volumes
echo ""
echo "=== Disk space after cleanup ==="
df -h

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand Down Expand Up @@ -158,47 +173,47 @@ jobs:
fi

- name: Setup validators
timeout-minutes: 5
env:
LLM_PROVIDER: ${{ vars.LLM_PROVIDER || 'openrouter' }}
LLM_MODEL: ${{ vars.LLM_MODEL || 'deepseek/deepseek-v3.2' }}
run: |
echo "Creating 5 validators with retry logic..."
created=0
for i in {1..5}; do
success=false
for attempt in 1 2 3; do
echo "Creating validator $i/5 (attempt $attempt/3)..."
response=$(curl -s -X POST http://localhost:4000/api \
-H "Content-Type: application/json" \
-d "{\"jsonrpc\":\"2.0\",\"method\":\"sim_createRandomValidator\",\"params\":[1],\"id\":$i}")

has_result=$(python3 -c "import json,sys; r=json.loads(sys.argv[1]); print('yes' if 'result' in r else 'no')" "$response" 2>/dev/null || echo "no")
if [ "$has_result" = "yes" ]; then
echo "Validator $i created"
created=$((created + 1))
echo "Creating 5 validators for $LLM_PROVIDER/$LLM_MODEL with retry logic..."
payload=$(python3 -c 'import json, os; print(json.dumps({"jsonrpc":"2.0","method":"sim_createRandomValidators","params":[5,8,12,[os.environ["LLM_PROVIDER"]],[os.environ["LLM_MODEL"]]],"id":1}))')
success=false
for attempt in 1 2 3; do
echo "Creating validators (attempt $attempt/3)..."
if response=$(curl --max-time 60 -sS -X POST http://localhost:4000/api \
-H "Content-Type: application/json" \
-d "$payload"); then
created=$(python3 -c "import json,sys; r=json.loads(sys.argv[1]); result=r.get('result', []); print(len(result) if isinstance(result, list) else 0)" "$response" 2>/dev/null || echo "0")
if [ "$created" -ge 5 ]; then
echo "Created $created validators"
success=true
break
fi

echo "Failed: $response"
if [ "$attempt" -lt 3 ]; then
echo "Retrying in 3s..."
sleep 3
fi
done

if [ "$success" = false ]; then
echo "ERROR: Could not create validator $i after 3 attempts"
exit 1
else
echo "Validator creation request failed"
fi

# Brief pause between validators
if [ "$i" -lt 5 ]; then sleep 2; fi
if [ "$attempt" -lt 3 ]; then
echo "Retrying in 5s..."
sleep 5
fi
done

if [ "$success" = false ]; then
echo "ERROR: Could not create validators after 3 attempts"
exit 1
fi

echo "Waiting 5 seconds for validators to register..."
sleep 5

# Verify validator count with retry
for attempt in 1 2 3; do
response=$(curl -s -X POST http://localhost:4000/api \
response=$(curl --max-time 20 -sS -X POST http://localhost:4000/api \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"sim_countValidators","params":[],"id":100}')
count=$(python3 -c "import json,sys; r=json.loads(sys.argv[1]); print(r.get('result',0))" "$response" 2>/dev/null || echo "0")
Expand All @@ -214,7 +229,7 @@ jobs:

if [ "$count" -lt 5 ]; then
echo "ERROR: Expected at least 5 validators, found $count"
curl -s -X POST http://localhost:4000/api \
curl --max-time 20 -sS -X POST http://localhost:4000/api \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"sim_getAllValidators","params":[],"id":101}'
exit 1
Expand Down Expand Up @@ -442,40 +457,46 @@ jobs:
fi

- name: Re-setup validators for state integrity test
timeout-minutes: 5
env:
LLM_PROVIDER: ${{ vars.LLM_PROVIDER || 'openrouter' }}
LLM_MODEL: ${{ vars.LLM_MODEL || 'deepseek/deepseek-v3.2' }}
run: |
echo "Re-creating validators (previous step cleaned them up)..."
for i in {1..5}; do
success=false
for attempt in 1 2 3; do
echo "Creating validator $i/5 (attempt $attempt/3)..."
response=$(curl -s -X POST http://localhost:4000/api \
-H "Content-Type: application/json" \
-d "{\"jsonrpc\":\"2.0\",\"method\":\"sim_createRandomValidator\",\"params\":[1],\"id\":$i}")

has_result=$(python3 -c "import json,sys; r=json.loads(sys.argv[1]); print('yes' if 'result' in r else 'no')" "$response" 2>/dev/null || echo "no")
if [ "$has_result" = "yes" ]; then
echo "Validator $i created"
echo "Re-creating validators for $LLM_PROVIDER/$LLM_MODEL (previous step cleaned them up)..."
payload=$(python3 -c 'import json, os; print(json.dumps({"jsonrpc":"2.0","method":"sim_createRandomValidators","params":[5,8,12,[os.environ["LLM_PROVIDER"]],[os.environ["LLM_MODEL"]]],"id":1}))')
success=false
for attempt in 1 2 3; do
echo "Creating validators (attempt $attempt/3)..."
if response=$(curl --max-time 60 -sS -X POST http://localhost:4000/api \
-H "Content-Type: application/json" \
-d "$payload"); then
created=$(python3 -c "import json,sys; r=json.loads(sys.argv[1]); result=r.get('result', []); print(len(result) if isinstance(result, list) else 0)" "$response" 2>/dev/null || echo "0")
if [ "$created" -ge 5 ]; then
echo "Created $created validators"
success=true
break
fi

echo "Failed: $response"
if [ "$attempt" -lt 3 ]; then sleep 3; fi
done

if [ "$success" = false ]; then
echo "ERROR: Could not create validator $i after 3 attempts"
exit 1
else
echo "Validator creation request failed"
fi

if [ "$i" -lt 5 ]; then sleep 2; fi
if [ "$attempt" -lt 3 ]; then
echo "Retrying in 5s..."
sleep 5
fi
done

if [ "$success" = false ]; then
echo "ERROR: Could not create validators after 3 attempts"
exit 1
fi

echo "Waiting 5 seconds for validators to register..."
sleep 5

# Verify validators exist
response=$(curl -s -X POST http://localhost:4000/api \
response=$(curl --max-time 20 -sS -X POST http://localhost:4000/api \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"sim_countValidators","params":[],"id":100}')
count=$(python3 -c "import json,sys; r=json.loads(sys.argv[1]); print(r.get('result',0))" "$response" 2>/dev/null || echo "0")
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/unit-tests-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ jobs:
- run: pip install pytest-cov pytest-xdist
- run: pytest tests/unit --cov=backend --cov-report=xml --cov-branch -n auto
- name: SonarCloud Scan
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: sonarsource/sonarqube-scan-action@v7.0.0
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
2 changes: 1 addition & 1 deletion backend/protocol_rpc/fees.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ def studio_fee_config(policy: StudioFeePolicy | None = None) -> dict[str, Any]:
"messageFees": {
"mode1": {
"accounting": True,
"genvmExecution": False,
"genvmExecution": True,
},
"mode2": {
"accounting": True,
Expand Down
2 changes: 1 addition & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jsonschema==4.26.0
loguru==0.7.3
web3==7.14.1
PyYAML==6.0.3
genvm-linter==0.7.1
genvm-linter==0.10.0

# FastAPI and ASGI dependencies
fastapi==0.135.1
Expand Down
9 changes: 7 additions & 2 deletions examples/contracts/log_indexer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# v0.2.16
# {
# "Seq": [
# { "Depends": "py-lib-genlayer-embeddings:09h0i209wrzh4xzq86f79c60x0ifs7xcjwl53ysrnw06i54ddxyi" },
# { "Depends": "py-lib-genlayer-embeddings:0bmbm3cyfwxsyh454z53vxqjf47wz2q7smcqp1q4g4a6k2kidnyk" },
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
# ]
# }
Expand All @@ -23,7 +23,12 @@ class StoreValue:

# contract class
class LogIndexer(gl.Contract):
vector_store: gle.VecDB[np.float32, typing.Literal[384], StoreValue]
vector_store: gle.VecDB[
np.float32,
typing.Literal[384],
StoreValue,
gle.EuclideanDistanceSquared,
]

def __init__(self):
pass
Expand Down
24 changes: 24 additions & 0 deletions tests/integration/icontracts/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
import os
import time
from dotenv import load_dotenv
from typing import Any

Expand Down Expand Up @@ -42,6 +43,27 @@ def get_mock_provider_config() -> dict[str, str]:
}


def _wait_for_validator_count(min_count: int, timeout: float = 20.0) -> None:
deadline = time.monotonic() + timeout
last_count = 0
while time.monotonic() < deadline:
validators_result = post_request_localhost(
payload("sim_getAllValidators")
).json()
assert has_success_status(validators_result)
last_count = len(validators_result.get("result", []))
if last_count >= min_count:
# Validator changes are delivered to workers via reload events.
# Give the consensus worker a short window to consume the update
# before the test submits a transaction.
time.sleep(2)
return
time.sleep(0.5)
raise AssertionError(
f"Expected at least {min_count} validators, found {last_count} after {timeout}s"
)


@pytest.fixture
def setup_validators():
created_validator_addresses = []
Expand Down Expand Up @@ -86,6 +108,7 @@ def _setup(mock_response: Any = None) -> None:
).json()
assert has_success_status(result)
created_validator_addresses.append(result["result"]["address"])
_wait_for_validator_count(5)
else:
cfg = get_provider_config()
# Non-mock mode: only create the validators that are still missing
Expand All @@ -110,6 +133,7 @@ def _setup(mock_response: Any = None) -> None:
# Track created validators for cleanup
for validator in result.get("result", []):
created_validator_addresses.append(validator["address"])
_wait_for_validator_count(5)

yield _setup

Expand Down
1 change: 1 addition & 0 deletions tests/integration/icontracts/contracts/company_naming.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# { "Depends": "py-genlayer:test" }

import json
import genlayer as gl
from genlayer import *

MAX_SCORE_DIFFERENCE = 3
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# v0.1.0
# { "Depends": "py-genlayer:test" }

import genlayer as gl
from genlayer import *


Expand Down Expand Up @@ -93,4 +94,4 @@ def test_corrupt_state_value(self) -> None:
def test_cross_contract_call(self, target_address: str) -> None:
"""Test invalid cross-contract calls"""
# Try to call a non-existent method on another contract
gl.get_contract_at(Address(target_address)).non_existent_method()
gl.contract.get_at(Address(target_address)).non_existent_method()
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# v0.1.0
# { "Depends": "py-genlayer:test" }

from genlayer import *
import genlayer as gl
import json


Expand Down
Loading
Loading