Skip to content

Commit 6c570a5

Browse files
authored
CHIA-3883 Tolerate quote related cost mismatch for older nodes (#20460)
Tolerate quote related cost mismatch for older nodes.
1 parent 34ded1b commit 6c570a5

File tree

6 files changed

+46
-19
lines changed

6 files changed

+46
-19
lines changed

chia/_tests/core/full_node/test_full_node.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
)
9696
from chia.types.blockchain_format.serialized_program import SerializedProgram
9797
from chia.types.blockchain_format.vdf import CompressibleVDFField, VDFProof
98+
from chia.types.clvm_cost import QUOTE_BYTES, QUOTE_EXECUTION_COST
9899
from chia.types.coin_spend import make_spend
99100
from chia.types.condition_opcodes import ConditionOpcode
100101
from chia.types.condition_with_args import ConditionWithArgs
@@ -3388,6 +3389,7 @@ async def test_pending_tx_cache_retry_on_new_peak(
33883389
@pytest.mark.parametrize("mismatch_fee", [True, False])
33893390
@pytest.mark.parametrize("tx_already_seen", [True, False])
33903391
@pytest.mark.parametrize("mismatch_on_reannounce", [True, False])
3392+
@pytest.mark.parametrize("tolerated_quote_cost_diff", [True, False])
33913393
async def test_ban_for_mismatched_tx_cost_fee(
33923394
three_nodes: list[FullNodeAPI],
33933395
bt: BlockTools,
@@ -3396,6 +3398,7 @@ async def test_ban_for_mismatched_tx_cost_fee(
33963398
mismatch_fee: bool,
33973399
tx_already_seen: bool,
33983400
mismatch_on_reannounce: bool,
3401+
tolerated_quote_cost_diff: bool,
33993402
) -> None:
34003403
"""
34013404
Tests that a peer gets banned if it sends a `NewTransaction` message with a
@@ -3406,6 +3409,8 @@ async def test_ban_for_mismatched_tx_cost_fee(
34063409
the ones specified in the `NewTransaction` message.
34073410
With `mismatch_on_reannounce` we control whether the peer sent us the same
34083411
transaction twice with different cost and fee.
3412+
With `tolerated_quote_cost_diff` we cover older nodes with a specific cost
3413+
mismatch due to the byte size cost and execution cost of the wrapper quote.
34093414
"""
34103415
full_node_1, full_node_2, full_node_3 = three_nodes
34113416
server_1 = full_node_1.full_node.server
@@ -3439,7 +3444,9 @@ async def test_ban_for_mismatched_tx_cost_fee(
34393444
assert mempool_item is not None
34403445
# Now send a NewTransaction with a cost and/or fee mismatch from the second
34413446
# full node.
3442-
cost = uint64(mempool_item.cost + 1) if mismatch_cost else mempool_item.cost
3447+
quote_cost = QUOTE_BYTES * node.constants.COST_PER_BYTE + QUOTE_EXECUTION_COST
3448+
cost_diff = quote_cost if tolerated_quote_cost_diff else 0
3449+
cost = uint64(mempool_item.cost + 1) if mismatch_cost else uint64(mempool_item.cost + cost_diff)
34433450
fee = uint64(mempool_item.fee + 1) if mismatch_fee else mempool_item.fee
34443451
msg = make_msg(ProtocolMessageTypes.new_transaction, NewTransaction(mempool_item.name, cost, fee))
34453452
# We won't ban localhost, so let's set a different ip address for the

chia/_tests/core/mempool/test_mempool_manager.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@
4141
from chia.full_node.mempool import MAX_SKIPPED_ITEMS, PRIORITY_TX_THRESHOLD
4242
from chia.full_node.mempool_manager import (
4343
MEMPOOL_MIN_FEE_INCREASE,
44-
QUOTE_BYTES,
45-
QUOTE_EXECUTION_COST,
4644
MempoolManager,
4745
TimelockConditions,
4846
can_replace,
@@ -63,7 +61,7 @@
6361
from chia.types.blockchain_format.coin import Coin
6462
from chia.types.blockchain_format.program import DEFAULT_FLAGS, INFINITE_COST, Program
6563
from chia.types.blockchain_format.serialized_program import SerializedProgram
66-
from chia.types.clvm_cost import CLVMCost
64+
from chia.types.clvm_cost import QUOTE_BYTES, QUOTE_EXECUTION_COST, CLVMCost
6765
from chia.types.coin_spend import make_spend
6866
from chia.types.condition_opcodes import ConditionOpcode
6967
from chia.types.condition_with_args import ConditionWithArgs

chia/full_node/full_node.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
from chia.server.ws_connection import WSChiaConnection
7979
from chia.types.blockchain_format.classgroup import ClassgroupElement
8080
from chia.types.blockchain_format.vdf import CompressibleVDFField, VDFInfo, VDFProof, validate_vdf
81+
from chia.types.clvm_cost import QUOTE_BYTES, QUOTE_EXECUTION_COST
8182
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
8283
from chia.types.mempool_item import MempoolItem
8384
from chia.types.peer_info import PeerInfo
@@ -2818,7 +2819,14 @@ async def add_transaction(
28182819
# Now that we validated this transaction, check what fees and
28192820
# costs the peers have advertised for it.
28202821
for peer_id, entry in peers_with_tx.items():
2821-
if entry.advertised_fee == mempool_item.fee and entry.advertised_cost == mempool_item.cost:
2822+
# Older nodes (2.4.3 and earlier) compute the cost slightly
2823+
# differently. They include the byte cost and execution cost of
2824+
# the quote for the puzzle.
2825+
tolerated_diff = QUOTE_BYTES * self.constants.COST_PER_BYTE + QUOTE_EXECUTION_COST
2826+
if entry.advertised_fee == mempool_item.fee and (
2827+
entry.advertised_cost == mempool_item.cost
2828+
or entry.advertised_cost == mempool_item.cost + tolerated_diff
2829+
):
28222830
continue
28232831
self.log.warning(
28242832
f"Banning peer {peer_id}. Sent us a new tx {spend_name} with mismatch "

chia/full_node/full_node_api.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
from chia.types.block_protocol import BlockInfo
6868
from chia.types.blockchain_format.coin import Coin, hash_coin_ids
6969
from chia.types.blockchain_format.proof_of_space import verify_and_get_quality_string
70+
from chia.types.clvm_cost import QUOTE_BYTES, QUOTE_EXECUTION_COST
7071
from chia.types.generator_types import BlockGenerator, NewBlockGenerator
7172
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
7273
from chia.types.peer_info import PeerInfo
@@ -220,11 +221,17 @@ async def new_transaction(
220221
# If already seen, the cost and fee must match, otherwise ban the peer
221222
mempool_item = self.full_node.mempool_manager.get_mempool_item(transaction.transaction_id, include_pending=True)
222223
if mempool_item is not None:
223-
if mempool_item.cost != transaction.cost or mempool_item.fee != transaction.fees:
224+
# Older nodes (2.4.3 and earlier) compute the cost slightly
225+
# differently. They include the byte cost and execution cost of
226+
# the quote for the puzzle.
227+
tolerated_diff = QUOTE_BYTES * self.full_node.constants.COST_PER_BYTE + QUOTE_EXECUTION_COST
228+
if (transaction.cost != mempool_item.cost and transaction.cost != mempool_item.cost + tolerated_diff) or (
229+
transaction.fees != mempool_item.fee
230+
):
224231
self.log.warning(
225-
f"Banning peer {peer.peer_node_id}. Sent us an already seen tx {transaction.transaction_id} "
226-
f"with mismatch on cost {transaction.cost} vs validation cost {mempool_item.cost} and/or "
227-
f"fee {transaction.fees} vs {mempool_item.fee}."
232+
f"Banning peer {peer.peer_node_id} version {peer.version}. Sent us an already seen tx "
233+
f"{transaction.transaction_id} with mismatch on cost {transaction.cost} vs validation "
234+
f"cost {mempool_item.cost} and/or fee {transaction.fees} vs {mempool_item.fee}."
228235
)
229236
await peer.close(CONSENSUS_ERROR_BAN_SECONDS)
230237
return None
@@ -244,11 +251,19 @@ async def new_transaction(
244251
return None
245252
prev = current_map.get(peer.peer_node_id)
246253
if prev is not None:
247-
if prev.advertised_fee != transaction.fees or prev.advertised_cost != transaction.cost:
254+
# Older nodes (2.4.3 and earlier) compute the cost slightly
255+
# differently. They include the byte cost and execution cost of
256+
# the quote for the puzzle.
257+
tolerated_diff = QUOTE_BYTES * self.full_node.constants.COST_PER_BYTE + QUOTE_EXECUTION_COST
258+
if prev.advertised_fee != transaction.fees or (
259+
prev.advertised_cost != transaction.cost
260+
and abs(prev.advertised_cost - transaction.cost) != tolerated_diff
261+
):
248262
self.log.warning(
249-
f"Banning peer {peer.peer_node_id}. Sent us a new tx {transaction.transaction_id} with "
250-
f"mismatch on cost {transaction.cost} vs previous advertised cost {prev.advertised_cost} "
251-
f"and/or fee {transaction.fees} vs previous advertised fee {prev.advertised_fee}."
263+
f"Banning peer {peer.peer_node_id} version {peer.version}. Sent us a new tx "
264+
f"{transaction.transaction_id} with mismatch on cost {transaction.cost} vs "
265+
f"previous advertised cost {prev.advertised_cost} and/or fee {transaction.fees} "
266+
f"vs previous advertised fee {prev.advertised_fee}."
252267
)
253268
await peer.close(CONSENSUS_ERROR_BAN_SECONDS)
254269
return None

chia/full_node/mempool_manager.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from chia.full_node.mempool import MEMPOOL_ITEM_FEE_LIMIT, Mempool, MempoolRemoveInfo, MempoolRemoveReason
3737
from chia.full_node.pending_tx_cache import ConflictTxCache, PendingTxCache
3838
from chia.types.blockchain_format.coin import Coin
39-
from chia.types.clvm_cost import CLVMCost
39+
from chia.types.clvm_cost import QUOTE_BYTES, QUOTE_EXECUTION_COST, CLVMCost
4040
from chia.types.fee_rate import FeeRate
4141
from chia.types.generator_types import NewBlockGenerator
4242
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
@@ -140,11 +140,6 @@ class NewPeakInfo:
140140
removals: list[MempoolRemoveInfo]
141141

142142

143-
# For block overhead cost calculation
144-
QUOTE_BYTES = 2
145-
QUOTE_EXECUTION_COST = 20
146-
147-
148143
def is_atom_canonical(clvm_buffer: bytes, offset: int) -> tuple[int, bool]:
149144
b = clvm_buffer[offset]
150145
if (b & 0b11000000) == 0b10000000:

chia/types/clvm_cost.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@
1111
"""
1212

1313
CLVMCost = NewType("CLVMCost", uint64)
14+
15+
# For block overhead cost calculation
16+
QUOTE_BYTES = 2
17+
QUOTE_EXECUTION_COST = 20

0 commit comments

Comments
 (0)