mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 10:05:29 -05:00
Checkpoint Merge (#20686)
Co-authored-by: arvidn <arvid@libtorrent.org> Co-authored-by: Earle Lowe <30607889+emlowe@users.noreply.github.com> Co-authored-by: Zachary Brown <z.brown@chia.net> Co-authored-by: Amine Khaldi <amine.khaldi@reactos.org>
This commit is contained in:
co-authored by
arvidn
Earle Lowe
Zachary Brown
Amine Khaldi
parent
888de5db7b
commit
c3364780d4
@@ -144,6 +144,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Python environment
|
||||
if: ${{ !contains(inputs.runs-on, 'blacksmith-4vcpu-windows') }}
|
||||
uses: Chia-Network/actions/setup-python@main
|
||||
with:
|
||||
python-version: ${{ matrix.python.action }}
|
||||
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
concurrency-name: ubuntu-intel
|
||||
configuration: ${{ needs.configure.outputs.configuration }}
|
||||
matrix_mode: ${{ needs.configure.outputs.matrix_mode }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ github.repository_owner == 'Chia-Network' && github.event.repository.visibility == 'private' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
|
||||
arch: intel
|
||||
arch-emoji: 🌀
|
||||
collect-junit: false
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
concurrency-name: ubuntu-arm
|
||||
configuration: ${{ needs.configure.outputs.configuration }}
|
||||
matrix_mode: ${{ needs.configure.outputs.matrix_mode }}
|
||||
runs-on: ubuntu-24.04-arm
|
||||
runs-on: ${{ github.repository_owner == 'Chia-Network' && github.event.repository.visibility == 'private' && 'blacksmith-4vcpu-ubuntu-2404-arm' || 'ubuntu-24.04-arm' }}
|
||||
arch: arm
|
||||
arch-emoji: 💪
|
||||
collect-junit: false
|
||||
@@ -189,7 +189,7 @@ jobs:
|
||||
concurrency-name: windows
|
||||
configuration: ${{ needs.configure.outputs.configuration }}
|
||||
matrix_mode: ${{ needs.configure.outputs.matrix_mode }}
|
||||
runs-on: windows-latest
|
||||
runs-on: ${{ github.repository_owner == 'Chia-Network' && github.event.repository.visibility == 'private' && 'blacksmith-4vcpu-windows-2025' || 'windows-latest' }}
|
||||
arch: intel
|
||||
arch-emoji: 🌀
|
||||
collect-junit: false
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
from collections.abc import AsyncIterator
|
||||
|
||||
import pytest
|
||||
from chia_rs import ConsensusConstants, FullBlock, UnfinishedBlock
|
||||
from chia_rs import ConsensusConstants, FullBlock, UnfinishedBlock, VDFInfo, VDFProof
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
from chia_rs.sized_ints import uint8, uint16, uint32, uint64, uint128
|
||||
|
||||
@@ -30,6 +31,7 @@ from chia.protocols import timelord_protocol
|
||||
from chia.protocols.timelord_protocol import NewInfusionPointVDF
|
||||
from chia.simulator.block_tools import BlockTools, create_block_tools_async, get_signage_point, make_unfinished_block
|
||||
from chia.simulator.keyring import TempKeyring
|
||||
from chia.types.blockchain_format.classgroup import ClassgroupElement
|
||||
from chia.util.hash import std_hash
|
||||
from chia.util.recursive_replace import recursive_replace
|
||||
|
||||
@@ -1344,3 +1346,59 @@ async def test_unfinished_block_eviction_mark_requesting(
|
||||
assert is_requesting
|
||||
else:
|
||||
assert not is_requesting
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_add_to_future_ip_sets_ttl(seeded_random: random.Random) -> None:
|
||||
"""add_to_future_ip must register a TTL in future_cache_key_times so that
|
||||
clear_old_cache_entries can evict stale IP cache entries."""
|
||||
store = FullNodeStore(DEFAULT_CONSTANTS)
|
||||
|
||||
challenge = bytes32.random(seeded_random)
|
||||
vdf_info = VDFInfo(challenge, uint64(1000), ClassgroupElement.get_default_element())
|
||||
vdf_proof = VDFProof(uint8(0), b"\x00" * 100, False)
|
||||
ip = NewInfusionPointVDF(
|
||||
unfinished_reward_hash=bytes32.random(seeded_random),
|
||||
challenge_chain_ip_vdf=vdf_info,
|
||||
challenge_chain_ip_proof=vdf_proof,
|
||||
reward_chain_ip_vdf=vdf_info,
|
||||
reward_chain_ip_proof=vdf_proof,
|
||||
infused_challenge_chain_ip_vdf=None,
|
||||
infused_challenge_chain_ip_proof=None,
|
||||
)
|
||||
|
||||
store.add_to_future_ip(ip)
|
||||
|
||||
assert challenge in store.future_ip_cache
|
||||
assert challenge in store.future_cache_key_times, (
|
||||
"add_to_future_ip must set future_cache_key_times so entries can be evicted"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_clear_old_cache_entries_evicts_future_ip(seeded_random: random.Random) -> None:
|
||||
"""Entries added via add_to_future_ip must be evicted by clear_old_cache_entries
|
||||
after the 1-hour TTL expires."""
|
||||
store = FullNodeStore(DEFAULT_CONSTANTS)
|
||||
|
||||
challenge = bytes32.random(seeded_random)
|
||||
vdf_info = VDFInfo(challenge, uint64(1000), ClassgroupElement.get_default_element())
|
||||
vdf_proof = VDFProof(uint8(0), b"\x00" * 100, False)
|
||||
ip = NewInfusionPointVDF(
|
||||
unfinished_reward_hash=bytes32.random(seeded_random),
|
||||
challenge_chain_ip_vdf=vdf_info,
|
||||
challenge_chain_ip_proof=vdf_proof,
|
||||
reward_chain_ip_vdf=vdf_info,
|
||||
reward_chain_ip_proof=vdf_proof,
|
||||
infused_challenge_chain_ip_vdf=None,
|
||||
infused_challenge_chain_ip_proof=None,
|
||||
)
|
||||
|
||||
store.add_to_future_ip(ip)
|
||||
assert challenge in store.future_ip_cache
|
||||
|
||||
store.future_cache_key_times[challenge] = int(time.time()) - 3601
|
||||
store.clear_old_cache_entries()
|
||||
|
||||
assert challenge not in store.future_ip_cache, "stale IP cache entry was not evicted"
|
||||
assert challenge not in store.future_cache_key_times
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
Regression tests for FullNode.add_prevalidated_blocks().
|
||||
|
||||
SEC-349: Prevalidation failures must return typed errors instead of raising
|
||||
AssertionError, so the caller can ban the offending peer.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
from chia_rs.sized_ints import uint16, uint32, uint64
|
||||
|
||||
from chia.consensus.block_body_validation import ForkInfo
|
||||
from chia.consensus.multiprocess_validation import PreValidationResult
|
||||
from chia.full_node.full_node import FullNode
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.types.validation_state import ValidationState
|
||||
from chia.util.errors import Err
|
||||
|
||||
|
||||
def _make_fake_self() -> SimpleNamespace:
|
||||
return SimpleNamespace(
|
||||
blockchain=SimpleNamespace(
|
||||
get_block_record_from_db=AsyncMock(return_value=None),
|
||||
),
|
||||
weight_proof_handler=None,
|
||||
log=logging.getLogger("test.add_prevalidated_blocks"),
|
||||
_state_changed=lambda *a, **kw: None,
|
||||
)
|
||||
|
||||
|
||||
def _make_fake_block(height: int = 1) -> SimpleNamespace:
|
||||
return SimpleNamespace(
|
||||
header_hash=bytes32(b"\xaa" * 32),
|
||||
prev_header_hash=bytes32(b"\xbb" * 32),
|
||||
finished_sub_slots=[],
|
||||
height=uint32(height),
|
||||
)
|
||||
|
||||
|
||||
def _make_fork_info() -> ForkInfo:
|
||||
return ForkInfo(
|
||||
fork_height=-1,
|
||||
peak_height=-1,
|
||||
peak_hash=bytes32(b"\x00" * 32),
|
||||
)
|
||||
|
||||
|
||||
def _make_validation_state() -> ValidationState:
|
||||
return ValidationState(
|
||||
ssi=uint64(0),
|
||||
difficulty=uint64(0),
|
||||
prev_ses_block=None,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_prevalidation_error_returns_err_not_assert() -> None:
|
||||
"""A PreValidationResult with error != None must produce a typed Err return,
|
||||
not an AssertionError. This ensures the caller's peer-ban path executes."""
|
||||
fake_self = _make_fake_self()
|
||||
block = _make_fake_block()
|
||||
invalid_result = PreValidationResult(
|
||||
error=uint16(Err.INVALID_POSPACE.value),
|
||||
required_iters=None,
|
||||
conds=None,
|
||||
timing=uint32(0),
|
||||
)
|
||||
blockchain = SimpleNamespace(block_record=lambda _: None, remove_extra_block=lambda _: None)
|
||||
peer_info = PeerInfo("127.0.0.1", uint16(8444))
|
||||
|
||||
summary, err = await FullNode.add_prevalidated_blocks(
|
||||
fake_self, # type: ignore[arg-type]
|
||||
blockchain, # type: ignore[arg-type]
|
||||
[block], # type: ignore[list-item]
|
||||
[invalid_result],
|
||||
_make_fork_info(),
|
||||
peer_info,
|
||||
_make_validation_state(),
|
||||
)
|
||||
|
||||
assert err is not None, "Expected an error to be returned"
|
||||
assert err == Err.INVALID_POSPACE
|
||||
assert summary is None
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_prevalidation_none_required_iters_returns_err() -> None:
|
||||
"""A PreValidationResult with no error but required_iters=None must return
|
||||
Err.UNKNOWN instead of raising AssertionError."""
|
||||
fake_self = _make_fake_self()
|
||||
block = _make_fake_block()
|
||||
bad_result = PreValidationResult(
|
||||
error=None,
|
||||
required_iters=None,
|
||||
conds=None,
|
||||
timing=uint32(0),
|
||||
)
|
||||
blockchain = SimpleNamespace(block_record=lambda _: None, remove_extra_block=lambda _: None)
|
||||
peer_info = PeerInfo("127.0.0.1", uint16(8444))
|
||||
|
||||
summary, err = await FullNode.add_prevalidated_blocks(
|
||||
fake_self, # type: ignore[arg-type]
|
||||
blockchain, # type: ignore[arg-type]
|
||||
[block], # type: ignore[list-item]
|
||||
[bad_result],
|
||||
_make_fork_info(),
|
||||
peer_info,
|
||||
_make_validation_state(),
|
||||
)
|
||||
|
||||
assert err is not None, "Expected an error to be returned"
|
||||
assert err == Err.UNKNOWN
|
||||
assert summary is None
|
||||
@@ -4,11 +4,13 @@ from logging import Logger
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from chia_rs.sized_ints import uint16, uint64
|
||||
|
||||
from chia.full_node.full_node_api import FullNodeAPI
|
||||
from chia.server.node_discovery import FullNodeDiscovery
|
||||
from chia.server.node_discovery import FullNodeDiscovery, FullNodePeers
|
||||
from chia.server.server import ChiaServer
|
||||
from chia.simulator.block_tools import BlockTools
|
||||
from chia.types.peer_info import PeerInfo, TimestampedPeerInfo
|
||||
from chia.util.default_root import SIMULATOR_ROOT_PATH
|
||||
|
||||
|
||||
@@ -85,3 +87,156 @@ async def test_enable_private_networks(
|
||||
await discovery2.initialize_address_manager()
|
||||
assert discovery2.address_manager is not None
|
||||
assert discovery2.address_manager.allow_private_subnets is True
|
||||
|
||||
|
||||
class TestPeerHostValidation:
|
||||
"""Regression tests for SEC-145: unbounded peer list host strings."""
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_add_peers_common_rejects_oversized_host(
|
||||
self,
|
||||
two_nodes: tuple[FullNodeAPI, FullNodeAPI, ChiaServer, ChiaServer, BlockTools],
|
||||
) -> None:
|
||||
chia_server = two_nodes[2]
|
||||
discovery = FullNodeDiscovery(
|
||||
server=chia_server,
|
||||
target_outbound_count=0,
|
||||
peers_file_path=SIMULATOR_ROOT_PATH / Path(chia_server.config["peers_file_path"]),
|
||||
introducer_info={"host": "introducer.chia.net", "port": 8444, "enable_private_networks": True},
|
||||
dns_servers=[],
|
||||
peer_connect_interval=0,
|
||||
selected_network=chia_server.config["selected_network"],
|
||||
default_port=8444,
|
||||
log=Logger("test_host_validation"),
|
||||
)
|
||||
await discovery.initialize_address_manager()
|
||||
assert discovery.address_manager is not None
|
||||
|
||||
oversized_host = "A" * 1000
|
||||
peer_list = [
|
||||
TimestampedPeerInfo(oversized_host, uint16(8444), uint64(0)),
|
||||
]
|
||||
|
||||
# Must not raise, and must not add the peer
|
||||
await discovery._add_peers_common(peer_list, None, False)
|
||||
assert await discovery.address_manager.size() == 0
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_add_peers_common_rejects_non_ip_host(
|
||||
self,
|
||||
two_nodes: tuple[FullNodeAPI, FullNodeAPI, ChiaServer, ChiaServer, BlockTools],
|
||||
) -> None:
|
||||
chia_server = two_nodes[2]
|
||||
discovery = FullNodeDiscovery(
|
||||
server=chia_server,
|
||||
target_outbound_count=0,
|
||||
peers_file_path=SIMULATOR_ROOT_PATH / Path(chia_server.config["peers_file_path"]),
|
||||
introducer_info={"host": "introducer.chia.net", "port": 8444, "enable_private_networks": True},
|
||||
dns_servers=[],
|
||||
peer_connect_interval=0,
|
||||
selected_network=chia_server.config["selected_network"],
|
||||
default_port=8444,
|
||||
log=Logger("test_host_validation"),
|
||||
)
|
||||
await discovery.initialize_address_manager()
|
||||
assert discovery.address_manager is not None
|
||||
|
||||
invalid_hosts = ["not-an-ip-address", "hello world", "999.999.999.999", ""]
|
||||
peer_list = [TimestampedPeerInfo(host, uint16(8444), uint64(0)) for host in invalid_hosts]
|
||||
|
||||
await discovery._add_peers_common(peer_list, None, False)
|
||||
assert await discovery.address_manager.size() == 0
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_add_peers_common_accepts_valid_ipv4(
|
||||
self,
|
||||
two_nodes: tuple[FullNodeAPI, FullNodeAPI, ChiaServer, ChiaServer, BlockTools],
|
||||
) -> None:
|
||||
chia_server = two_nodes[2]
|
||||
discovery = FullNodeDiscovery(
|
||||
server=chia_server,
|
||||
target_outbound_count=0,
|
||||
peers_file_path=SIMULATOR_ROOT_PATH / Path(chia_server.config["peers_file_path"]),
|
||||
introducer_info={"host": "introducer.chia.net", "port": 8444, "enable_private_networks": True},
|
||||
dns_servers=[],
|
||||
peer_connect_interval=0,
|
||||
selected_network=chia_server.config["selected_network"],
|
||||
default_port=8444,
|
||||
log=Logger("test_host_validation"),
|
||||
)
|
||||
await discovery.initialize_address_manager()
|
||||
assert discovery.address_manager is not None
|
||||
|
||||
peer_list = [
|
||||
TimestampedPeerInfo("192.168.1.1", uint16(8444), uint64(0)),
|
||||
]
|
||||
|
||||
await discovery._add_peers_common(peer_list, None, False)
|
||||
assert await discovery.address_manager.size() >= 1
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_add_peers_common_mixed_valid_and_invalid(
|
||||
self,
|
||||
two_nodes: tuple[FullNodeAPI, FullNodeAPI, ChiaServer, ChiaServer, BlockTools],
|
||||
) -> None:
|
||||
"""Invalid hosts are skipped; valid hosts in the same batch are still added."""
|
||||
chia_server = two_nodes[2]
|
||||
discovery = FullNodeDiscovery(
|
||||
server=chia_server,
|
||||
target_outbound_count=0,
|
||||
peers_file_path=SIMULATOR_ROOT_PATH / Path(chia_server.config["peers_file_path"]),
|
||||
introducer_info={"host": "introducer.chia.net", "port": 8444, "enable_private_networks": True},
|
||||
dns_servers=[],
|
||||
peer_connect_interval=0,
|
||||
selected_network=chia_server.config["selected_network"],
|
||||
default_port=8444,
|
||||
log=Logger("test_host_validation"),
|
||||
)
|
||||
await discovery.initialize_address_manager()
|
||||
assert discovery.address_manager is not None
|
||||
|
||||
peer_list = [
|
||||
TimestampedPeerInfo("X" * 500, uint16(8444), uint64(0)),
|
||||
TimestampedPeerInfo("not-an-ip", uint16(8444), uint64(0)),
|
||||
TimestampedPeerInfo("192.168.1.1", uint16(8444), uint64(0)),
|
||||
TimestampedPeerInfo("192.168.1.2", uint16(8444), uint64(0)),
|
||||
]
|
||||
|
||||
await discovery._add_peers_common(peer_list, None, False)
|
||||
assert await discovery.address_manager.size() >= 1
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_add_peers_neighbour_rejects_invalid_host(
|
||||
self,
|
||||
two_nodes: tuple[FullNodeAPI, FullNodeAPI, ChiaServer, ChiaServer, BlockTools],
|
||||
) -> None:
|
||||
chia_server = two_nodes[2]
|
||||
discovery = FullNodePeers(
|
||||
server=chia_server,
|
||||
target_outbound_count=0,
|
||||
peers_file_path=SIMULATOR_ROOT_PATH / Path(chia_server.config["peers_file_path"]),
|
||||
introducer_info={"host": "introducer.chia.net", "port": 8444, "enable_private_networks": True},
|
||||
dns_servers=[],
|
||||
peer_connect_interval=0,
|
||||
selected_network=chia_server.config["selected_network"],
|
||||
default_port=8444,
|
||||
log=Logger("test_host_validation"),
|
||||
)
|
||||
await discovery.initialize_address_manager()
|
||||
|
||||
oversized_host = "B" * 500
|
||||
invalid_host = "not.an.ip"
|
||||
valid_host = "10.0.0.1"
|
||||
neighbour = PeerInfo("10.0.0.100", 8444)
|
||||
peers = [
|
||||
TimestampedPeerInfo(oversized_host, uint16(8444), uint64(0)),
|
||||
TimestampedPeerInfo(invalid_host, uint16(8444), uint64(0)),
|
||||
TimestampedPeerInfo(valid_host, uint16(8444), uint64(0)),
|
||||
]
|
||||
|
||||
await discovery.add_peers_neighbour(peers, neighbour)
|
||||
|
||||
known = discovery.neighbour_known_peers.get(neighbour, set())
|
||||
assert oversized_host not in known
|
||||
assert invalid_host not in known
|
||||
assert valid_host in known
|
||||
|
||||
@@ -73,6 +73,26 @@ class TestKeyringWrapper:
|
||||
)
|
||||
assert KeyringWrapper.get_shared_instance().has_cached_master_passphrase() is True
|
||||
|
||||
# When: the cached master passphrase has been cleared
|
||||
def test_has_cached_master_passphrase_cleared(self, empty_temp_file_keyring: TempKeyring):
|
||||
"""
|
||||
has_cached_master_passphrase should return False when the cache is cleared
|
||||
"""
|
||||
# Precondition: default passphrase is cached
|
||||
assert KeyringWrapper.get_shared_instance().has_cached_master_passphrase() is True
|
||||
|
||||
# When: clearing the cached passphrase
|
||||
KeyringWrapper.get_shared_instance().set_cached_master_passphrase(None)
|
||||
|
||||
# Expect: has_cached_master_passphrase reports no passphrase
|
||||
assert KeyringWrapper.get_shared_instance().has_cached_master_passphrase() is False
|
||||
|
||||
# When: setting an empty string passphrase
|
||||
KeyringWrapper.get_shared_instance().set_cached_master_passphrase("")
|
||||
|
||||
# Expect: empty string is also treated as "no passphrase"
|
||||
assert KeyringWrapper.get_shared_instance().has_cached_master_passphrase() is False
|
||||
|
||||
# When: using a file keyring
|
||||
def test_set_cached_master_passphrase(self, empty_temp_file_keyring: TempKeyring):
|
||||
"""
|
||||
|
||||
@@ -529,7 +529,9 @@ async def test_get_balance(
|
||||
# Generate some funds, get the balance and make sure it's as expected
|
||||
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
||||
await time_out_assert(30, wallet_synced)
|
||||
generated_funds = await full_node_api.farm_blocks_to_wallet(5, wallet_node.wallet_state_manager.main_wallet)
|
||||
generated_funds = await full_node_api.farm_blocks_to_wallet(
|
||||
5, wallet_node.wallet_state_manager.main_wallet, timeout=60
|
||||
)
|
||||
expected_generated_balance = Balance(
|
||||
confirmed_wallet_balance=uint128(generated_funds),
|
||||
unconfirmed_wallet_balance=uint128(generated_funds),
|
||||
|
||||
@@ -777,7 +777,7 @@ class DataLayer:
|
||||
|
||||
for uploader in uploaders:
|
||||
self.log.info(f"Using uploader {uploader} for store {store_id.hex()}")
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with aiohttp.ClientSession(timeout=self.client_timeout) as session:
|
||||
async with session.post(
|
||||
uploader.url + "/upload",
|
||||
json=request_json,
|
||||
@@ -838,7 +838,7 @@ class DataLayer:
|
||||
"group_files_by_store": self.group_files_by_store,
|
||||
}
|
||||
for uploader in uploaders:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with aiohttp.ClientSession(timeout=self.client_timeout) as session:
|
||||
async with session.post(
|
||||
uploader.url + "/add_missing_files",
|
||||
json=request_json,
|
||||
@@ -1336,7 +1336,7 @@ class DataLayer:
|
||||
async def get_uploaders(self, store_id: bytes32) -> list[PluginRemote]:
|
||||
uploaders = []
|
||||
for uploader in self.uploaders:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with aiohttp.ClientSession(timeout=self.client_timeout) as session:
|
||||
try:
|
||||
async with session.post(
|
||||
uploader.url + "/handle_upload",
|
||||
|
||||
@@ -314,6 +314,7 @@ class S3Plugin:
|
||||
|
||||
if not (bytes32.fromhex(file_name[:64]) == store_id):
|
||||
log.error(f"failed uploading file {file_name}, store id mismatch")
|
||||
continue
|
||||
|
||||
file_path = self.get_path_for_filename(store_id, file_name, group_files_by_store)
|
||||
target_file_name = self.get_s3_target_from_path(store_id, file_path, group_files_by_store)
|
||||
|
||||
@@ -1628,8 +1628,17 @@ class FullNode:
|
||||
for i, block in enumerate(blocks_to_validate):
|
||||
header_hash = block.header_hash
|
||||
assert vs.prev_ses_block is None or vs.prev_ses_block.height < block.height
|
||||
assert pre_validation_results[i].error is None
|
||||
assert pre_validation_results[i].required_iters is not None
|
||||
if pre_validation_results[i].error is not None:
|
||||
self.log.error(
|
||||
f"prevalidation failed for block {header_hash.hex()} height {block.height} "
|
||||
f"from peer {peer_info}: {Err(pre_validation_results[i].error).name}"
|
||||
)
|
||||
return agg_state_change_summary, Err(pre_validation_results[i].error)
|
||||
if pre_validation_results[i].required_iters is None:
|
||||
self.log.error(
|
||||
f"required_iters is None for block {header_hash.hex()} height {block.height} from peer {peer_info}"
|
||||
)
|
||||
return agg_state_change_summary, Err.UNKNOWN
|
||||
state_change_summary: StateChangeSummary | None
|
||||
# when adding blocks in batches, we won't have any overlapping
|
||||
# signatures with the mempool. There won't be any cache hits, so
|
||||
|
||||
@@ -356,6 +356,7 @@ class FullNodeStore:
|
||||
if ch not in self.future_ip_cache:
|
||||
self.future_ip_cache[ch] = []
|
||||
self.future_ip_cache[ch].append(infusion_point)
|
||||
self.future_cache_key_times[ch] = int(time.time())
|
||||
|
||||
def in_future_sp_cache(self, signage_point: SignagePoint, index: uint8) -> bool:
|
||||
if signage_point.rc_vdf is None:
|
||||
|
||||
@@ -34,6 +34,7 @@ from chia.util.task_referencer import create_referenced_task
|
||||
MAX_PEERS_RECEIVED_PER_REQUEST = 1000
|
||||
MAX_TOTAL_PEERS_RECEIVED = 3000
|
||||
MAX_CONCURRENT_OUTBOUND_CONNECTIONS = 70
|
||||
MAX_IP_ADDRESS_STRING_LENGTH = 45 # IPv4 max 15, IPv6 max 45
|
||||
NETWORK_ID_DEFAULT_PORTS = {
|
||||
"mainnet": 8444,
|
||||
"testnet7": 58444,
|
||||
@@ -459,6 +460,16 @@ class FullNodeDiscovery:
|
||||
if is_misbehaving:
|
||||
return None
|
||||
for peer in peer_list:
|
||||
# Fast-fail oversized junk before IP parsing. IPAddress.create() rejects
|
||||
# invalid input too, but parsing pathological long strings is much slower.
|
||||
if len(peer.host) > MAX_IP_ADDRESS_STRING_LENGTH:
|
||||
self.log.debug(f"Skipping peer with oversized host string ({len(peer.host)} chars)")
|
||||
continue
|
||||
try:
|
||||
IPAddress.create(peer.host)
|
||||
except ValueError:
|
||||
self.log.debug(f"Skipping peer with invalid host: {peer.host!r:.50}")
|
||||
continue
|
||||
if peer.timestamp < 100000000 or peer.timestamp > time.time() + 10 * 60:
|
||||
# Invalid timestamp, predefine a bad one.
|
||||
current_peer = TimestampedPeerInfo(
|
||||
@@ -552,6 +563,12 @@ class FullNodePeers(FullNodeDiscovery):
|
||||
async def add_peers_neighbour(self, peers: list[TimestampedPeerInfo], neighbour_info: PeerInfo) -> None:
|
||||
async with self.lock:
|
||||
for peer in peers:
|
||||
if len(peer.host) > MAX_IP_ADDRESS_STRING_LENGTH:
|
||||
continue
|
||||
try:
|
||||
IPAddress.create(peer.host)
|
||||
except ValueError:
|
||||
continue
|
||||
if neighbour_info not in self.neighbour_known_peers:
|
||||
self.neighbour_known_peers[neighbour_info] = set()
|
||||
if peer.host not in self.neighbour_known_peers[neighbour_info]:
|
||||
|
||||
@@ -219,7 +219,7 @@ class KeyringWrapper:
|
||||
self.cached_passphrase_is_validated = validated
|
||||
|
||||
def has_cached_master_passphrase(self) -> bool:
|
||||
passphrase = self.get_cached_master_passphrase()
|
||||
passphrase, _ = self.get_cached_master_passphrase()
|
||||
return passphrase is not None and len(passphrase) > 0
|
||||
|
||||
def has_master_passphrase(self) -> bool:
|
||||
|
||||
@@ -387,7 +387,9 @@ def tx_endpoint(
|
||||
tx_config,
|
||||
push=request.get("push", push),
|
||||
merge_spends=request.get("merge_spends", merge_spends),
|
||||
sign=request.get("sign", self.service.config.get("auto_sign_txs", True)),
|
||||
sign=False
|
||||
if func.__name__ == "take_offer"
|
||||
else request.get("sign", self.service.config.get("auto_sign_txs", True)),
|
||||
) as action_scope:
|
||||
response = await func(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user