fix: stop blockchain bulk sync after first failed block
Some checks failed
Blockchain Synchronization Verification / sync-verification (push) Failing after 10s
Cross-Chain Functionality Tests / test-cross-chain-sync (push) Successful in 2s
Cross-Chain Functionality Tests / test-cross-chain-transactions (push) Successful in 2s
Cross-Chain Functionality Tests / test-cross-chain-bridge (push) Has been skipped
Cross-Chain Functionality Tests / test-multi-chain-consensus (push) Successful in 2s
Cross-Chain Functionality Tests / aggregate-results (push) Has been skipped
Cross-Node Transaction Testing / transaction-test (push) Successful in 10s
Deploy to Testnet / deploy-testnet (push) Successful in 1m10s
Integration Tests / test-service-integration (push) Successful in 2m37s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 4s
Multi-Node Stress Testing / stress-test (push) Successful in 5s
Node Failover Simulation / failover-test (push) Successful in 2s
P2P Network Verification / p2p-verification (push) Successful in 2s
Python Tests / test-python (push) Successful in 16s
Security Scanning / security-scan (push) Successful in 30s

This commit is contained in:
aitbc
2026-05-02 16:01:56 +02:00
parent b9d061948c
commit 88281f39ac
2 changed files with 61 additions and 1 deletions

View File

@@ -295,7 +295,7 @@ class ChainSync:
else:
logger.warning("Block import failed during bulk", extra={"height": block_data.get("height"), "reason": result.reason})
# Stop on first failure to avoid gaps
break
return imported
start_height = end_height + 1
# Brief pause to avoid overwhelming the DB (use adaptive poll interval)

View File

@@ -6,6 +6,7 @@ import sys
import pytest
from datetime import datetime, UTC
from contextlib import contextmanager
from unittest.mock import AsyncMock, Mock
from sqlmodel import Session, SQLModel, create_engine, select
@@ -204,6 +205,65 @@ class TestChainSyncAppend:
assert result.accepted is False
assert "Gap" in result.reason
class TestChainSyncBulkImport:
@pytest.mark.asyncio
async def test_bulk_import_stops_on_first_failed_block(self):
local_head = Mock(height=349)
session_result = Mock(first=Mock(return_value=local_head))
session = Mock(exec=Mock(return_value=session_result))
@contextmanager
def session_factory():
yield session
sync = ChainSync(session_factory, chain_id="test", validate_signatures=False)
sync._calculate_dynamic_batch_size = lambda gap: 2
sync._get_adaptive_bulk_sync_interval = lambda gap: 0
sync._get_adaptive_poll_interval = lambda gap: 0
class FakeResponse:
def raise_for_status(self):
return None
def json(self):
return {"height": 353}
sync._client.get = AsyncMock(return_value=FakeResponse())
fetch_calls = []
async def fake_fetch_blocks_range(start, end, source_url):
fetch_calls.append((start, end, source_url))
if start == 350:
return [
{"height": 350, "hash": "0x350", "parent_hash": "0x349"},
{"height": 351, "hash": "0x351", "parent_hash": "0x350"},
]
raise AssertionError(f"unexpected fetch for range {start}-{end}")
sync.fetch_blocks_range = fake_fetch_blocks_range
import_calls = []
def fake_import_block(block_data):
import_calls.append(block_data["height"])
return ImportResult(
accepted=False,
height=block_data["height"],
block_hash=block_data["hash"],
reason="Gap detected (our height: 349, received: 350)",
)
sync.import_block = fake_import_block
imported = await sync.bulk_import_from("http://peer.example")
assert imported == 0
assert fetch_calls == [(350, 351, "http://peer.example")]
assert import_calls == [350]
def test_append_with_transactions(self, session_factory):
sync = ChainSync(session_factory, chain_id="test", validate_signatures=False)
blocks = _seed_chain(session_factory, count=1, chain_id="test")