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
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:
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user