Skip to content

Commit 18c7633

Browse files
committed
simpliy add_transaction() and replace literals with the CONSENSUS_ERROR_BAN_SECONDS constant
1 parent 61034b5 commit 18c7633

File tree

7 files changed

+85
-68
lines changed

7 files changed

+85
-68
lines changed

chia/_tests/core/full_node/test_full_node.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,10 @@ async def suppress_value_error(coro: Coroutine[Any, Any, None]) -> None:
919919

920920

921921
@pytest.mark.anyio
922+
@pytest.mark.limit_consensus_modes(
923+
allowed=[ConsensusMode.HARD_FORK_2_0, ConsensusMode.HARD_FORK_3_0],
924+
reason="We can no longer (reliably) farm blocks from before the hard fork",
925+
)
922926
async def test_new_transaction_and_mempool(
923927
wallet_nodes: tuple[
924928
FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, WalletTool, WalletTool, BlockTools
@@ -946,8 +950,10 @@ async def test_new_transaction_and_mempool(
946950

947951
# Makes a bunch of coins
948952
conditions_dict: dict[ConditionOpcode, list[ConditionWithArgs]] = {ConditionOpcode.CREATE_COIN: []}
949-
# This should fit in one transaction
950-
for _ in range(100):
953+
# This should fit in one transaction. The test constants have a max block cost of 400,000,000
954+
# and the default max *transaction* cost is half that, so 200,000,000. CREATE_COIN has a cost of
955+
# 1,800,000, we create 80 coins
956+
for _ in range(80):
951957
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
952958
puzzle_hashes.append(receiver_puzzlehash)
953959
output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [receiver_puzzlehash, int_to_bytes(10000000000)])
@@ -1046,8 +1052,8 @@ async def test_new_transaction_and_mempool(
10461052
# these numbers reflect the capacity of the mempool. In these
10471053
# tests MEMPOOL_BLOCK_BUFFER is 1. The other factors are COST_PER_BYTE
10481054
# and MAX_BLOCK_COST_CLVM
1049-
assert included_tx == 23
1050-
assert not_included_tx == 10
1055+
assert included_tx == 20
1056+
assert not_included_tx == 7
10511057
assert seen_bigger_transaction_has_high_fee
10521058

10531059
# Mempool is full

chia/_tests/core/mempool/test_mempool.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
7373
from chia.types.mempool_item import MempoolItem, UnspentLineageInfo
7474
from chia.util.casts import int_to_bytes
75-
from chia.util.errors import Err
75+
from chia.util.errors import Err, ValidationError
7676
from chia.util.hash import std_hash
7777
from chia.util.recursive_replace import recursive_replace
7878
from chia.wallet.conditions import AssertCoinAnnouncement, AssertPuzzleAnnouncement
@@ -361,7 +361,10 @@ async def respond_transaction(
361361
self.full_node.full_node_store.pending_tx_request.pop(spend_name)
362362
if spend_name in self.full_node.full_node_store.peers_with_tx:
363363
self.full_node.full_node_store.peers_with_tx.pop(spend_name)
364-
ret = await self.full_node.add_transaction(tx.transaction, spend_name, peer, test)
364+
try:
365+
ret = await self.full_node.add_transaction(tx.transaction, spend_name, peer, test)
366+
except ValidationError as e:
367+
ret = (MempoolInclusionStatus.FAILED, e.code)
365368
invariant_check_mempool(self.full_node.mempool_manager.mempool)
366369
return ret
367370

@@ -2865,7 +2868,10 @@ async def test_invalid_coin_spend_coin(
28652868
coin_spend_0 = make_spend(coin_0, cs.puzzle_reveal, cs.solution)
28662869
new_bundle = recursive_replace(spend_bundle, "coin_spends", [coin_spend_0, *spend_bundle.coin_spends[1:]])
28672870
assert spend_bundle is not None
2868-
res = await full_node_1.full_node.add_transaction(new_bundle, new_bundle.name(), test=True)
2871+
try:
2872+
res = await full_node_1.full_node.add_transaction(new_bundle, new_bundle.name(), test=True)
2873+
except ValidationError as e:
2874+
res = (MempoolInclusionStatus.FAILED, e.code)
28692875
assert res == (MempoolInclusionStatus.FAILED, Err.WRONG_PUZZLE_HASH)
28702876

28712877

chia/full_node/full_node.py

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
from chia.protocols.full_node_protocol import RequestBlocks, RespondBlock, RespondBlocks, RespondSignagePoint
6969
from chia.protocols.outbound_message import Message, NodeType, make_msg
7070
from chia.protocols.protocol_message_types import ProtocolMessageTypes
71+
from chia.protocols.protocol_timing import CONSENSUS_ERROR_BAN_SECONDS
7172
from chia.protocols.shared_protocol import Capability
7273
from chia.protocols.wallet_protocol import CoinStateUpdate, RemovedMempoolItem
7374
from chia.rpc.rpc_server import StateChangedProtocol
@@ -87,7 +88,7 @@
8788
from chia.util.db_synchronous import db_synchronous_on
8889
from chia.util.db_version import lookup_db_version, set_db_version_async
8990
from chia.util.db_wrapper import DBWrapper2, manage_connection
90-
from chia.util.errors import ConsensusError, Err, TimestampError, ValidationError
91+
from chia.util.errors import ConsensusError, Err, TimestampError
9192
from chia.util.limited_semaphore import LimitedSemaphore
9293
from chia.util.network import is_localhost
9394
from chia.util.path import path_from_root
@@ -506,7 +507,7 @@ async def _handle_one_transaction(self, entry: TransactionQueueEntry) -> None:
506507
error_stack = traceback.format_exc()
507508
self.log.error(f"Error in _handle_one_transaction, closing: {error_stack}")
508509
if peer is not None:
509-
await peer.close()
510+
await peer.close(CONSENSUS_ERROR_BAN_SECONDS)
510511
finally:
511512
self.add_transaction_semaphore.release()
512513

@@ -1092,13 +1093,13 @@ async def request_validate_wp(
10921093
response = await weight_proof_peer.call_api(FullNodeAPI.request_proof_of_weight, request, timeout=wp_timeout)
10931094
# Disconnect from this peer, because they have not behaved properly
10941095
if response is None or not isinstance(response, full_node_protocol.RespondProofOfWeight):
1095-
await weight_proof_peer.close(600)
1096+
await weight_proof_peer.close(CONSENSUS_ERROR_BAN_SECONDS)
10961097
raise RuntimeError(f"Weight proof did not arrive in time from peer: {weight_proof_peer.peer_info.host}")
10971098
if response.wp.recent_chain_data[-1].reward_chain_block.height != peak_height:
1098-
await weight_proof_peer.close(600)
1099+
await weight_proof_peer.close(CONSENSUS_ERROR_BAN_SECONDS)
10991100
raise RuntimeError(f"Weight proof had the wrong height: {weight_proof_peer.peer_info.host}")
11001101
if response.wp.recent_chain_data[-1].reward_chain_block.weight != peak_weight:
1101-
await weight_proof_peer.close(600)
1102+
await weight_proof_peer.close(CONSENSUS_ERROR_BAN_SECONDS)
11021103
raise RuntimeError(f"Weight proof had the wrong weight: {weight_proof_peer.peer_info.host}")
11031104
if self.in_bad_peak_cache(response.wp):
11041105
raise ValueError("Weight proof failed bad peak cache validation")
@@ -1113,10 +1114,10 @@ async def request_validate_wp(
11131114
try:
11141115
validated, fork_point, summaries = await self.weight_proof_handler.validate_weight_proof(response.wp)
11151116
except Exception as e:
1116-
await weight_proof_peer.close(600)
1117+
await weight_proof_peer.close(CONSENSUS_ERROR_BAN_SECONDS)
11171118
raise ValueError(f"Weight proof validation threw an error {e}")
11181119
if not validated:
1119-
await weight_proof_peer.close(600)
1120+
await weight_proof_peer.close(CONSENSUS_ERROR_BAN_SECONDS)
11201121
raise ValueError("Weight proof validation failed")
11211122
self.log.info(f"Re-checked peers: total of {len(peers_with_peak)} peers with peak {peak_height}")
11221123
self.sync_store.set_sync_mode(True)
@@ -1378,7 +1379,7 @@ async def ingest_blocks(
13781379
vs,
13791380
)
13801381
if err is not None:
1381-
await peer.close(600)
1382+
await peer.close(CONSENSUS_ERROR_BAN_SECONDS)
13821383
raise ValueError(f"Failed to validate block batch {start_height} to {end_height}: {err}")
13831384
if end_height - block_rate_height > 100:
13841385
now = time.monotonic()
@@ -2767,66 +2768,56 @@ async def add_transaction(
27672768
return MempoolInclusionStatus.SUCCESS, None
27682769
if self.mempool_manager.seen(spend_name):
27692770
return MempoolInclusionStatus.FAILED, Err.ALREADY_INCLUDING_TRANSACTION
2770-
self.mempool_manager.add_and_maybe_pop_seen(spend_name)
27712771
self.log.debug(f"Processing transaction: {spend_name}")
27722772
# Ignore if syncing or if we have not yet received a block
27732773
# the mempool must have a peak to validate transactions
27742774
if self.sync_store.get_sync_mode() or self.mempool_manager.peak is None:
2775-
status = MempoolInclusionStatus.FAILED
2776-
error: Optional[Err] = Err.NO_TRANSACTIONS_WHILE_SYNCING
2777-
self.mempool_manager.remove_seen(spend_name)
2778-
else:
2775+
return MempoolInclusionStatus.FAILED, Err.NO_TRANSACTIONS_WHILE_SYNCING
2776+
2777+
cost_result = await self.mempool_manager.pre_validate_spendbundle(transaction, spend_name, self._bls_cache)
2778+
2779+
self.mempool_manager.add_and_maybe_pop_seen(spend_name)
2780+
2781+
if self.config.get("log_mempool", False): # pragma: no cover
27792782
try:
2780-
cost_result = await self.mempool_manager.pre_validate_spendbundle(
2781-
transaction, spend_name, self._bls_cache
2782-
)
2783-
except ValidationError as e:
2784-
self.mempool_manager.remove_seen(spend_name)
2785-
return MempoolInclusionStatus.FAILED, e.code
2783+
mempool_dir = path_from_root(self.root_path, "mempool-log") / f"{self.blockchain.get_peak_height()}"
2784+
mempool_dir.mkdir(parents=True, exist_ok=True)
2785+
with open(mempool_dir / f"{spend_name}.bundle", "wb+") as f:
2786+
f.write(bytes(transaction))
27862787
except Exception:
2787-
self.mempool_manager.remove_seen(spend_name)
2788-
raise
2788+
self.log.exception(f"Failed to log mempool item: {spend_name}")
27892789

2790-
if self.config.get("log_mempool", False): # pragma: no cover
2791-
try:
2792-
mempool_dir = path_from_root(self.root_path, "mempool-log") / f"{self.blockchain.get_peak_height()}"
2793-
mempool_dir.mkdir(parents=True, exist_ok=True)
2794-
with open(mempool_dir / f"{spend_name}.bundle", "wb+") as f:
2795-
f.write(bytes(transaction))
2796-
except Exception:
2797-
self.log.exception(f"Failed to log mempool item: {spend_name}")
2798-
2799-
async with self.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.low):
2800-
if self.mempool_manager.get_spendbundle(spend_name) is not None:
2801-
self.mempool_manager.remove_seen(spend_name)
2802-
return MempoolInclusionStatus.SUCCESS, None
2803-
if self.mempool_manager.peak is None:
2804-
return MempoolInclusionStatus.FAILED, Err.MEMPOOL_NOT_INITIALIZED
2805-
info = await self.mempool_manager.add_spend_bundle(
2806-
transaction, cost_result, spend_name, self.mempool_manager.peak.height
2807-
)
2808-
status = info.status
2809-
error = info.error
2810-
if status == MempoolInclusionStatus.SUCCESS:
2811-
self.log.debug(
2812-
f"Added transaction to mempool: {spend_name} mempool size: "
2813-
f"{self.mempool_manager.mempool.total_mempool_cost()} normalized "
2814-
f"{self.mempool_manager.mempool.total_mempool_cost() / 5000000}"
2815-
)
2790+
async with self.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.low):
2791+
if self.mempool_manager.get_spendbundle(spend_name) is not None:
2792+
self.mempool_manager.remove_seen(spend_name)
2793+
return MempoolInclusionStatus.SUCCESS, None
2794+
if self.mempool_manager.peak is None:
2795+
return MempoolInclusionStatus.FAILED, Err.MEMPOOL_NOT_INITIALIZED
2796+
info = await self.mempool_manager.add_spend_bundle(
2797+
transaction, cost_result, spend_name, self.mempool_manager.peak.height
2798+
)
2799+
status = info.status
2800+
error = info.error
2801+
if status == MempoolInclusionStatus.SUCCESS:
2802+
self.log.debug(
2803+
f"Added transaction to mempool: {spend_name} mempool size: "
2804+
f"{self.mempool_manager.mempool.total_mempool_cost()} normalized "
2805+
f"{self.mempool_manager.mempool.total_mempool_cost() / 5000000}"
2806+
)
28162807

2817-
# Only broadcast successful transactions, not pending ones. Otherwise it's a DOS
2818-
# vector.
2819-
mempool_item = self.mempool_manager.get_mempool_item(spend_name)
2820-
assert mempool_item is not None
2821-
await self.broadcast_removed_tx(info.removals)
2822-
await self.broadcast_added_tx(mempool_item, current_peer=peer)
2808+
# Only broadcast successful transactions, not pending ones. Otherwise it's a DOS
2809+
# vector.
2810+
mempool_item = self.mempool_manager.get_mempool_item(spend_name)
2811+
assert mempool_item is not None
2812+
await self.broadcast_removed_tx(info.removals)
2813+
await self.broadcast_added_tx(mempool_item, current_peer=peer)
28232814

2824-
if self.simulator_transaction_callback is not None: # callback
2825-
await self.simulator_transaction_callback(spend_name)
2815+
if self.simulator_transaction_callback is not None: # callback
2816+
await self.simulator_transaction_callback(spend_name)
28262817

2827-
else:
2828-
self.mempool_manager.remove_seen(spend_name)
2829-
self.log.debug(f"Wasn't able to add transaction with id {spend_name}, status {status} error: {error}")
2818+
else:
2819+
self.mempool_manager.remove_seen(spend_name)
2820+
self.log.debug(f"Wasn't able to add transaction with id {spend_name}, status {status} error: {error}")
28302821
return status, error
28312822

28322823
async def broadcast_added_tx(

chia/full_node/mempool_manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ async def pre_validate_spendbundle(
460460
)
461461
# validate_clvm_and_signature raises a ValueError with an error code
462462
except ValueError as e:
463+
log.warning(f"max CLVM cost: {self.max_tx_clvm_cost}")
463464
# Convert that to a ValidationError
464465
if len(e.args) > 1:
465466
error = Err(e.args[1])

chia/server/server.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -553,9 +553,15 @@ async def connection_closed(
553553
# in this case we still want to do the banning logic and remove the connection from the list
554554
# but the other cleanup should already have been done so we skip that
555555

556-
if is_localhost(connection.peer_info.host) and ban_time != 0:
557-
self.log.warning(f"Trying to ban localhost for {ban_time}, but will not ban")
558-
ban_time = 0
556+
if ban_time > 0:
557+
if is_localhost(connection.peer_info.host):
558+
self.log.warning(f"Trying to ban localhost for {ban_time}, but will not ban")
559+
ban_time = 0
560+
elif self.is_trusted_peer(connection, self.config.get("trusted_peers", {})):
561+
self.log.warning(
562+
f"Trying to ban trusted peer {connection.peer_info.host} for {ban_time}, but will not ban"
563+
)
564+
ban_time = 0
559565
if ban_time > 0:
560566
ban_until: float = time.time() + ban_time
561567
self.log.warning(f"Banning {connection.peer_info.host} for {ban_time} seconds")

chia/server/ws_connection.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ async def ban_peer_bad_protocol(self, log_err_msg: str) -> None:
358358
self.log.error(f"Banning peer for {ban_seconds} seconds: {self.peer_info.host} {log_err_msg}")
359359
await self.close(ban_seconds, WSCloseCode.PROTOCOL_ERROR, Err.INVALID_PROTOCOL_MESSAGE)
360360

361+
async def ban_peer_consensus_error(self, log_err_msg: str) -> None:
362+
"""Ban peer for consensus rule violation"""
363+
ban_seconds = CONSENSUS_ERROR_BAN_SECONDS
364+
self.log.error(f"Banning peer for {ban_seconds} seconds: {self.peer_info.host} {log_err_msg}")
365+
await self.close(ban_seconds)
366+
361367
def cancel_pending_requests(self) -> None:
362368
for message_id, event in self.pending_requests.items():
363369
try:

chia/simulator/block_tools.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ def setup_new_gen(
389389
program = simple_solution_generator(transaction_data).program
390390
block_refs = []
391391
cost = compute_block_cost(program, self.constants, uint32(curr.height + 1), prev_tx_height)
392+
print(f"computed cost: {cost} additions: {len(additions)} removals: {len(removals)}")
392393
return NewBlockGenerator(
393394
program,
394395
[],

0 commit comments

Comments
 (0)