Skip to content

Commit 0059b5e

Browse files
authored
[CHIA-1087] Simplify batch pre validate blocks (#18602)
* minor simplification of stacked if-conditions and early exits on failure paths * Simplify NPCResult -> SpendBundleConditions * make include_spends() take SpendBundleConditions, rather than NPCResult
1 parent 439cd07 commit 0059b5e

File tree

3 files changed

+48
-47
lines changed

3 files changed

+48
-47
lines changed

chia/consensus/block_body_validation.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from dataclasses import dataclass, field
66
from typing import Awaitable, Callable, Collection, Dict, List, Optional, Set, Tuple, Union
77

8-
from chia_rs import AugSchemeMPL, BLSCache, G1Element
8+
from chia_rs import AugSchemeMPL, BLSCache, G1Element, SpendBundleConditions
99
from chiabip158 import PyBIP158
1010

1111
from chia.consensus.block_record import BlockRecord
@@ -85,7 +85,7 @@ def reset(self, fork_height: int, header_hash: bytes32) -> None:
8585
self.removals_since_fork = {}
8686
self.block_hashes = []
8787

88-
def include_spends(self, npc_result: Optional[NPCResult], block: FullBlock, header_hash: bytes32) -> None:
88+
def include_spends(self, conds: Optional[SpendBundleConditions], block: FullBlock, header_hash: bytes32) -> None:
8989
height = block.height
9090

9191
assert self.peak_height == height - 1
@@ -97,11 +97,10 @@ def include_spends(self, npc_result: Optional[NPCResult], block: FullBlock, head
9797
self.peak_height = int(block.height)
9898
self.peak_hash = header_hash
9999

100-
if npc_result is not None:
101-
assert npc_result.conds is not None
100+
if conds is not None:
102101
assert block.foliage_transaction_block is not None
103102
timestamp = block.foliage_transaction_block.timestamp
104-
for spend in npc_result.conds.spends:
103+
for spend in conds.spends:
105104
self.removals_since_fork[bytes32(spend.coin_id)] = ForkRem(bytes32(spend.puzzle_hash), height)
106105
for puzzle_hash, amount, hint in spend.create_coin:
107106
coin = Coin(bytes32(spend.coin_id), bytes32(puzzle_hash), uint64(amount))

chia/consensus/blockchain.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ async def run_single_block(self, block: FullBlock, fork_info: ForkInfo) -> None:
282282
)
283283
assert npc.error is None
284284

285-
fork_info.include_spends(npc, block, block.header_hash)
285+
fork_info.include_spends(None if npc is None else npc.conds, block, block.header_hash)
286286

287287
async def add_block(
288288
self,
@@ -412,7 +412,7 @@ async def add_block(
412412
# main chain, we still need to re-run it to update the additions and
413413
# removals in fork_info.
414414
await self.advance_fork_info(block, fork_info)
415-
fork_info.include_spends(npc_result, block, header_hash)
415+
fork_info.include_spends(None if npc_result is None else npc_result.conds, block, header_hash)
416416
self.add_block_record(block_rec)
417417
return AddBlockResult.ALREADY_HAVE_BLOCK, None, None
418418

@@ -444,7 +444,7 @@ async def add_block(
444444
# case we're validating blocks on a fork, the next block validation will
445445
# need to know of these additions and removals. Also, _reconsider_peak()
446446
# will need these results
447-
fork_info.include_spends(npc_result, block, header_hash)
447+
fork_info.include_spends(None if npc_result is None else npc_result.conds, block, header_hash)
448448

449449
# block_to_block_record() require the previous block in the cache
450450
if not genesis and prev_block is not None:

chia/consensus/multiprocess_validation.py

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from dataclasses import dataclass
99
from typing import Dict, List, Optional, Sequence, Tuple
1010

11-
from chia_rs import AugSchemeMPL
11+
from chia_rs import AugSchemeMPL, SpendBundleConditions
1212

1313
from chia.consensus.block_header_validation import validate_finished_header_block
1414
from chia.consensus.block_record import BlockRecord
@@ -53,7 +53,7 @@ def batch_pre_validate_blocks(
5353
blocks_pickled: Dict[bytes, bytes],
5454
full_blocks_pickled: List[bytes],
5555
prev_transaction_generators: List[Optional[List[bytes]]],
56-
npc_results: Dict[uint32, bytes],
56+
conditions: Dict[uint32, bytes],
5757
expected_difficulty: List[uint64],
5858
expected_sub_slot_iters: List[uint64],
5959
validate_signatures: bool,
@@ -71,16 +71,14 @@ def batch_pre_validate_blocks(
7171
block: FullBlock = FullBlock.from_bytes_unchecked(full_blocks_pickled[i])
7272
tx_additions: List[Coin] = []
7373
removals: List[bytes32] = []
74-
npc_result: Optional[NPCResult] = None
75-
if block.height in npc_results:
76-
npc_result = NPCResult.from_bytes(npc_results[block.height])
77-
assert npc_result is not None
78-
if npc_result.conds is not None:
79-
removals, tx_additions = tx_removals_and_additions(npc_result.conds)
80-
else:
81-
removals, tx_additions = [], []
82-
83-
if block.transactions_generator is not None and npc_result is None:
74+
conds: Optional[SpendBundleConditions] = None
75+
if block.height in conditions:
76+
conds = SpendBundleConditions.from_bytes(conditions[block.height])
77+
removals, tx_additions = tx_removals_and_additions(conds)
78+
elif block.transactions_generator is not None:
79+
# TODO: this function would be simpler if conditions were
80+
# required to be passed in for all transaction blocks. We would
81+
# no longer need prev_transaction_generators
8482
prev_generators = prev_transaction_generators[i]
8583
assert prev_generators is not None
8684
assert block.transactions_info is not None
@@ -93,15 +91,17 @@ def batch_pre_validate_blocks(
9391
height=block.height,
9492
constants=constants,
9593
)
96-
removals, tx_additions = tx_removals_and_additions(npc_result.conds)
97-
if npc_result is not None and npc_result.error is not None:
98-
validation_time = time.monotonic() - validation_start
99-
results.append(
100-
PreValidationResult(
101-
uint16(npc_result.error), None, npc_result, False, uint32(validation_time * 1000)
94+
if npc_result.error is not None:
95+
validation_time = time.monotonic() - validation_start
96+
results.append(
97+
PreValidationResult(
98+
uint16(npc_result.error), None, npc_result, False, uint32(validation_time * 1000)
99+
)
102100
)
103-
)
104-
continue
101+
continue
102+
assert npc_result.conds is not None
103+
conds = npc_result.conds
104+
removals, tx_additions = tx_removals_and_additions(conds)
105105

106106
header_block = get_block_header(block, tx_additions, removals)
107107
prev_ses_block = None
@@ -123,28 +123,28 @@ def batch_pre_validate_blocks(
123123
error_int = uint16(error.code.value)
124124

125125
successfully_validated_signatures = False
126-
# If we failed CLVM, no need to validate signature, the block is already invalid
127-
if error_int is None:
128-
# If this is False, it means either we don't have a signature (not a tx block) or we have an invalid
129-
# signature (which also puts in an error) or we didn't validate the signature because we want to
130-
# validate it later. add_block will attempt to validate the signature later.
131-
if validate_signatures:
132-
if npc_result is not None and block.transactions_info is not None:
133-
assert npc_result.conds
134-
pairs_pks, pairs_msgs = pkm_pairs(npc_result.conds, constants.AGG_SIG_ME_ADDITIONAL_DATA)
135-
if not AugSchemeMPL.aggregate_verify(
136-
pairs_pks, pairs_msgs, block.transactions_info.aggregated_signature
137-
):
138-
error_int = uint16(Err.BAD_AGGREGATE_SIGNATURE.value)
139-
else:
140-
successfully_validated_signatures = True
126+
# If we failed header block validation, no need to validate
127+
# signature, the block is already invalid If this is False, it means
128+
# either we don't have a signature (not a tx block) or we have an
129+
# invalid signature (which also puts in an error) or we didn't
130+
# validate the signature because we want to validate it later.
131+
# add_block will attempt to validate the signature later.
132+
if error_int is None and validate_signatures and conds is not None:
133+
assert block.transactions_info is not None
134+
pairs_pks, pairs_msgs = pkm_pairs(conds, constants.AGG_SIG_ME_ADDITIONAL_DATA)
135+
if not AugSchemeMPL.aggregate_verify(
136+
pairs_pks, pairs_msgs, block.transactions_info.aggregated_signature
137+
):
138+
error_int = uint16(Err.BAD_AGGREGATE_SIGNATURE.value)
139+
else:
140+
successfully_validated_signatures = True
141141

142142
validation_time = time.monotonic() - validation_start
143143
results.append(
144144
PreValidationResult(
145145
error_int,
146146
required_iters,
147-
npc_result,
147+
None if conds is None else NPCResult(None, conds),
148148
successfully_validated_signatures,
149149
uint32(validation_time * 1000),
150150
)
@@ -274,9 +274,11 @@ async def pre_validate_blocks_multiprocessing(
274274
if block_rec.sub_epoch_summary_included is not None:
275275
prev_ses_block = block_rec
276276

277-
npc_results_pickled = {}
277+
conditions_pickled = {}
278278
for k, v in npc_results.items():
279-
npc_results_pickled[k] = bytes(v)
279+
assert v.error is None
280+
assert v.conds is not None
281+
conditions_pickled[k] = bytes(v.conds)
280282
futures = []
281283
# Pool of workers to validate blocks concurrently
282284
recent_blocks_bytes = {bytes(k): bytes(v) for k, v in recent_blocks.items()} # convert to bytes
@@ -321,7 +323,7 @@ async def pre_validate_blocks_multiprocessing(
321323
recent_blocks_bytes,
322324
b_pickled,
323325
previous_generators,
324-
npc_results_pickled,
326+
conditions_pickled,
325327
[diff_ssis[j][0] for j in range(i, end_i)],
326328
[diff_ssis[j][1] for j in range(i, end_i)],
327329
validate_signatures,

0 commit comments

Comments
 (0)