diff --git a/electron-react/src/components/fullNode/FullNode.jsx b/electron-react/src/components/fullNode/FullNode.jsx index 95222baf57..2cfd620590 100644 --- a/electron-react/src/components/fullNode/FullNode.jsx +++ b/electron-react/src/components/fullNode/FullNode.jsx @@ -155,7 +155,7 @@ const getStatusItems = (state, connected) => { const status_items = []; if (state.sync && state.sync.sync_mode) { const progress = state.sync.sync_progress_sub_height; - const tip = state.sync.sync_tip_height; + const tip = state.sync.sync_tip_sub_height; const item = { label: Status, value: ( @@ -202,7 +202,7 @@ const getStatusItems = (state, connected) => { }; status_items.push(item); } - + if (connected) { status_items.push({ label: Connection Status, @@ -241,7 +241,7 @@ const getStatusItems = (state, connected) => { const peakTimestamp = state.peak?.foliage_block?.timestamp; status_items.push({ label: Peak Time, - value: peakTimestamp + value: peakTimestamp ? unix_to_short_date(Number.parseInt(peakTimestamp)) : '', tooltip: ( @@ -400,16 +400,16 @@ const BlocksCard = () => { - + {latestBlocks.map((record) => { const isFinished = true; //record.finished_reward_slot_hashes && !!record.finished_reward_slot_hashes.length; - const { + const { foliage_block: { height, timestamp, header_hash = 'mocked-hash', prev_block_hash, - } + } } = record; return ( @@ -456,7 +456,7 @@ const BlocksCard = () => { )} - + ); }; diff --git a/requirements-dev.txt b/requirements-dev.txt index d43ead6d46..2aab7f4386 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -black>=19.10b0 +black>=20.8b1 # black Dependencies appdirs~=1.4.4 attrs>=18.1.0 diff --git a/src/cmds/configure.py b/src/cmds/configure.py index 14cd616196..f1f52c1686 100644 --- a/src/cmds/configure.py +++ b/src/cmds/configure.py @@ -1,4 +1,7 @@ -from src.util.config import load_config, save_config +from src.util.config import ( + load_config, + save_config, +) from argparse import ArgumentParser from typing import Dict from src.util.default_root import DEFAULT_ROOT_PATH diff --git a/src/cmds/init.py b/src/cmds/init.py index 6632813087..a1f9a62431 100644 --- a/src/cmds/init.py +++ b/src/cmds/init.py @@ -237,6 +237,7 @@ def chia_init(root_path: Path): DO_NOT_MIGRATE_SETTINGS: List[str] = [ "full_node.introducer_peer", "wallet.introducer_peer", + "wallet.full_node_peer", "full_node.database_path", "wallet.database_path", "full_node.simulator_database_path", diff --git a/src/consensus/block_body_validation.py b/src/consensus/block_body_validation.py index 68d6497f4d..e727fa801e 100644 --- a/src/consensus/block_body_validation.py +++ b/src/consensus/block_body_validation.py @@ -31,6 +31,10 @@ from src.util.errors import Err from src.util.hash import std_hash from src.util.ints import uint64, uint32 +import logging + +log = logging.getLogger(__name__) + async def validate_block_body( constants: ConsensusConstants, @@ -232,6 +236,7 @@ async def validate_block_body( additions_since_fork[c.name()] = (c, curr.sub_block_height) for coinbase_coin in curr.get_included_reward_coins(): + additions_since_fork[coinbase_coin.name()] = (coinbase_coin, curr.sub_block_height) coinbases_since_fork[coinbase_coin.name()] = curr.sub_block_height if curr.sub_block_height == 0: break diff --git a/src/consensus/block_header_validation.py b/src/consensus/block_header_validation.py index c03fef7894..7ee5228e8e 100644 --- a/src/consensus/block_header_validation.py +++ b/src/consensus/block_header_validation.py @@ -253,9 +253,12 @@ async def validate_unfinished_header_block( # 2o. Check challenge sub-slot hash in reward sub-slot if sub_slot.challenge_chain.get_hash() != sub_slot.reward_chain.challenge_chain_sub_slot_hash: - return None, ValidationError( - Err.INVALID_CHALLENGE_SLOT_HASH_RC, - "sub-slot hash in reward sub-slot mismatch", + return ( + None, + ValidationError( + Err.INVALID_CHALLENGE_SLOT_HASH_RC, + "sub-slot hash in reward sub-slot mismatch", + ), ) eos_vdf_iters: uint64 = sub_slot_iters @@ -330,9 +333,12 @@ async def validate_unfinished_header_block( if genesis_block: # 2r. Check deficit (MIN_SUB.. deficit edge case for genesis block) if sub_slot.reward_chain.deficit != constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK: - return None, ValidationError( - Err.INVALID_DEFICIT, - f"genesis, expected deficit {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK}", + return ( + None, + ValidationError( + Err.INVALID_DEFICIT, + f"genesis, expected deficit {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK}", + ), ) else: assert prev_sb is not None @@ -342,10 +348,13 @@ async def validate_unfinished_header_block( log.error( constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK, ) - return None, ValidationError( - Err.INVALID_DEFICIT, - f"expected deficit {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK}, saw " - f"{sub_slot.reward_chain.deficit}", + return ( + None, + ValidationError( + Err.INVALID_DEFICIT, + f"expected deficit {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK}, saw " + f"{sub_slot.reward_chain.deficit}", + ), ) else: # 2t. Otherwise, deficit stays the same at the slot ends, cannot reset until 0 @@ -358,17 +367,23 @@ async def validate_unfinished_header_block( if ses_hash is not None: # 3a. Check that genesis block does not have sub-epoch summary if genesis_block: - return None, ValidationError( - Err.INVALID_SUB_EPOCH_SUMMARY_HASH, - "genesis with sub-epoch-summary hash", + return ( + None, + ValidationError( + Err.INVALID_SUB_EPOCH_SUMMARY_HASH, + "genesis with sub-epoch-summary hash", + ), ) assert prev_sb is not None # 3b. Check that we finished a slot and we finished a sub-epoch if not new_sub_slot or not can_finish_se: - return None, ValidationError( - Err.INVALID_SUB_EPOCH_SUMMARY_HASH, - f"new sub-slot: {new_sub_slot} finishes sub-epoch {can_finish_se}", + return ( + None, + ValidationError( + Err.INVALID_SUB_EPOCH_SUMMARY_HASH, + f"new sub-slot: {new_sub_slot} finishes sub-epoch {can_finish_se}", + ), ) # 3c. Check the actual sub-epoch is correct @@ -383,16 +398,22 @@ async def validate_unfinished_header_block( expected_hash = expected_sub_epoch_summary.get_hash() if expected_hash != ses_hash: log.error(f"{expected_sub_epoch_summary}") - return None, ValidationError( - Err.INVALID_SUB_EPOCH_SUMMARY, - f"expected ses hash: {expected_hash} got {ses_hash} ", + return ( + None, + ValidationError( + Err.INVALID_SUB_EPOCH_SUMMARY, + f"expected ses hash: {expected_hash} got {ses_hash} ", + ), ) elif new_sub_slot and not genesis_block: # 3d. Check that we don't have to include a sub-epoch summary if can_finish_se or can_finish_epoch: - return None, ValidationError( - Err.INVALID_SUB_EPOCH_SUMMARY, - "block finishes sub-epoch but ses-hash is None", + return ( + None, + ValidationError( + Err.INVALID_SUB_EPOCH_SUMMARY, + "block finishes sub-epoch but ses-hash is None", + ), ) # 4. Check if the number of sub-blocks is less than the max @@ -511,9 +532,12 @@ async def validate_unfinished_header_block( total_iters = uint128(prev_sb.total_iters - prev_sb.ip_iters(constants)) total_iters = uint128(total_iters + ip_iters) if total_iters != header_block.reward_chain_sub_block.total_iters: - return None, ValidationError( - Err.INVALID_TOTAL_ITERS, - f"expected {total_iters} got {header_block.reward_chain_sub_block.total_iters}", + return ( + None, + ValidationError( + Err.INVALID_TOTAL_ITERS, + f"expected {total_iters} got {header_block.reward_chain_sub_block.total_iters}", + ), ) sp_total_iters: uint128 = uint128(total_iters - ip_iters + sp_iters - (sub_slot_iters if overflow else 0)) @@ -919,9 +943,12 @@ async def validate_finished_header_block( assert header_block.infused_challenge_chain_ip_proof is not None # If we have an ICC chain, deficit must be 0, 1, 2 or 3 if deficit >= constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1: - return None, ValidationError( - Err.INVALID_ICC_VDF, - f"icc vdf and deficit is bigger or equal to {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1}", + return ( + None, + ValidationError( + Err.INVALID_ICC_VDF, + f"icc vdf and deficit is bigger or equal to {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1}", + ), ) if new_sub_slot: last_ss = header_block.finished_sub_slots[-1] diff --git a/src/consensus/blockchain.py b/src/consensus/blockchain.py index b51a942a39..910b7d7c7d 100644 --- a/src/consensus/blockchain.py +++ b/src/consensus/blockchain.py @@ -143,11 +143,11 @@ class Blockchain: async def get_block_peak(self) -> Optional[FullBlock]: """ Return peak block""" - if self.peak_height is None or self.peak_height == 0: + if self.peak_height is None: return None start = int(self.peak_height) peak = None - while start > 0: + while start >= 0: block = await self.block_store.get_full_block(self.sub_height_to_hash[uint32(start)]) if block is not None and block.is_block(): peak = block diff --git a/src/farmer/farmer.py b/src/farmer/farmer.py index c844b4ea2e..a33579a226 100644 --- a/src/farmer/farmer.py +++ b/src/farmer/farmer.py @@ -3,6 +3,7 @@ import logging import time from typing import Dict, List, Optional, Callable, Tuple, Any +import src.server.ws_connection as ws # lgtm [py/import-and-import-from] from blspy import G1Element from src.server.ws_connection import WSChiaConnection @@ -42,7 +43,7 @@ class Farmer: self.proofs_of_space: Dict[bytes32, List[Tuple[str, ProofOfSpace]]] = {} # Quality string to plot identifier and challenge_hash, for use with harvester.RequestSignatures - self.quality_str_to_identifiers: Dict[bytes32, Tuple[str, bytes32, bytes32]] = {} + self.quality_str_to_identifiers: Dict[bytes32, Tuple[str, bytes32, bytes32, bytes32]] = {} # number of responses to each signage point self.number_of_responses: Dict[bytes32, int] = {} @@ -112,6 +113,10 @@ class Farmer: if self.state_changed_callback is not None: self.state_changed_callback(change, sp_hash) + def on_disconnect(self, connection: ws.WSChiaConnection): + self.log.info(f"peer disconnected {connection.get_peer_info()}") + self.state_changed("close_connection", bytes32([0] * 32)) + def get_public_keys(self): return [child_sk.get_g1() for child_sk in self._private_keys] diff --git a/src/farmer/farmer_api.py b/src/farmer/farmer_api.py index 3bfc5ba5b5..1d39139394 100644 --- a/src/farmer/farmer_api.py +++ b/src/farmer/farmer_api.py @@ -2,6 +2,7 @@ import time from typing import Callable from blspy import AugSchemeMPL, G2Element +import src.server.ws_connection as ws from src.consensus.pot_iterations import ( calculate_iterations_quality, @@ -12,7 +13,7 @@ from src.protocols import harvester_protocol, farmer_protocol from src.server.outbound_message import Message, NodeType from src.types.pool_target import PoolTarget from src.types.proof_of_space import ProofOfSpace -from src.util.api_decorators import api_request +from src.util.api_decorators import api_request, peer_required from src.util.ints import uint32, uint64 @@ -26,7 +27,10 @@ class FarmerAPI: self.farmer.state_changed_callback = callback @api_request - async def new_proof_of_space(self, new_proof_of_space: harvester_protocol.NewProofOfSpace): + @peer_required + async def new_proof_of_space( + self, new_proof_of_space: harvester_protocol.NewProofOfSpace, peer: ws.WSChiaConnection + ): """ This is a response from the harvester, for a NewChallenge. Here we check if the proof of space is sufficiently good, and if so, we ask for the whole proof. @@ -100,11 +104,11 @@ class FarmerAPI: new_proof_of_space.plot_identifier, new_proof_of_space.challenge_hash, new_proof_of_space.sp_hash, + peer.peer_node_id, ) self.farmer.cache_add_time[computed_quality_string] = uint64(int(time.time())) - msg = Message("request_signatures", request) - await self.farmer.server.send_to_all([msg], NodeType.HARVESTER) + return Message("request_signatures", request) @api_request async def respond_signatures(self, response: harvester_protocol.RespondSignatures): @@ -250,20 +254,15 @@ class FarmerAPI: self.farmer.log.error(f"Do not have quality string {full_node_request.quality_string}") return - ( - plot_identifier, - challenge_hash, - sp_hash, - ) = self.farmer.quality_str_to_identifiers[full_node_request.quality_string] + (plot_identifier, challenge_hash, sp_hash, node_id) = self.farmer.quality_str_to_identifiers[ + full_node_request.quality_string + ] request = harvester_protocol.RequestSignatures( plot_identifier, challenge_hash, sp_hash, - [ - full_node_request.foliage_sub_block_hash, - full_node_request.foliage_block_hash, - ], + [full_node_request.foliage_sub_block_hash, full_node_request.foliage_block_hash], ) msg = Message("request_signatures", request) - await self.farmer.server.send_to_all([msg], NodeType.HARVESTER) + await self.farmer.server.send_to_specific([msg], node_id) diff --git a/src/full_node/full_node.py b/src/full_node/full_node.py index a8339dca40..dafa4de02c 100644 --- a/src/full_node/full_node.py +++ b/src/full_node/full_node.py @@ -85,6 +85,7 @@ class FullNode: self.constants = consensus_constants self.pow_pending: Set[bytes32] = set() self.pow_creation: Dict[uint32, asyncio.Event] = {} + self.state_changed_callback: Optional[Callable] = None if name: self.log = logging.getLogger(name) @@ -284,6 +285,7 @@ class FullNode: and challenges to timelords. """ + self._state_changed("add_connection") if self.full_node_peers is not None: asyncio.create_task(self.full_node_peers.on_connect(connection)) @@ -321,8 +323,9 @@ class FullNode: elif connection.connection_type is NodeType.TIMELORD: await self.send_peak_to_timelords() - async def _on_disconnect(self, connection: ws.WSChiaConnection): + def on_disconnect(self, connection: ws.WSChiaConnection): self.log.info(f"peer disconnected {connection.get_peer_info()}") + self._state_changed("close_connection") def _num_needed_peers(self) -> int: assert self.server is not None diff --git a/src/full_node/full_node_store.py b/src/full_node/full_node_store.py index a78674ac21..3217e16e05 100644 --- a/src/full_node/full_node_store.py +++ b/src/full_node/full_node_store.py @@ -382,6 +382,11 @@ class FullNodeStore: cc_vdf_info_expected, ): return False + + if rc_vdf_info_expected.challenge != signage_point.rc_vdf.challenge: + # This signage point is probably outdated + return False + if not signage_point.rc_proof.is_valid( self.constants, ClassgroupElement.get_default_element(), diff --git a/src/harvester/harvester.py b/src/harvester/harvester.py index d7ff6d347d..00f436f502 100644 --- a/src/harvester/harvester.py +++ b/src/harvester/harvester.py @@ -1,5 +1,6 @@ import logging import asyncio +import src.server.ws_connection as ws # lgtm [py/import-and-import-from] from concurrent.futures.thread import ThreadPoolExecutor from pathlib import Path from typing import Dict, Optional, Tuple, List, Callable, Set @@ -51,6 +52,7 @@ class Harvester: self.constants = constants self.cached_challenges = [] self.log = log + self.state_changed_callback: Optional[Callable] = None async def _start(self): self._refresh_lock = asyncio.Lock() @@ -69,6 +71,10 @@ class Harvester: if self.state_changed_callback is not None: self.state_changed_callback(change) + def on_disconnect(self, connection: ws.WSChiaConnection): + self.log.info(f"peer disconnected {connection.get_peer_info()}") + self._state_changed("close_connection") + def get_plots(self) -> Tuple[List[Dict], List[str], List[str]]: response_plots: List[Dict] = [] for path, plot_info in self.provers.items(): diff --git a/src/introducer/introducer.py b/src/introducer/introducer.py index 1cfe245295..d46b9847ae 100644 --- a/src/introducer/introducer.py +++ b/src/introducer/introducer.py @@ -1,7 +1,7 @@ import asyncio import logging import time -from typing import Dict, Optional +from typing import Dict, Optional, Callable from src.types.sized_bytes import bytes32 from src.server.server import ChiaServer @@ -15,6 +15,7 @@ class Introducer: self._shut_down = False self.server: Optional[ChiaServer] = None self.log = logging.getLogger(__name__) + self.state_changed_callback: Optional[Callable] = None async def _start(self): self._vetting_task = asyncio.create_task(self._vetting_loop()) diff --git a/src/rpc/full_node_rpc_api.py b/src/rpc/full_node_rpc_api.py index d95300d52b..55dc0a3029 100644 --- a/src/rpc/full_node_rpc_api.py +++ b/src/rpc/full_node_rpc_api.py @@ -97,10 +97,7 @@ class FullNodeRpcApi: uint32(max(1, full_peak.sub_block_height - 1000)) ].hex() space = await self.get_network_space( - { - "newer_block_header_hash": newer_block_hex, - "older_block_header_hash": older_block_hex, - } + {"newer_block_header_hash": newer_block_hex, "older_block_header_hash": older_block_hex} ) else: space = {"space": uint128(0)} diff --git a/src/rpc/rpc_server.py b/src/rpc/rpc_server.py index f67e4ab573..68f76e8d91 100644 --- a/src/rpc/rpc_server.py +++ b/src/rpc/rpc_server.py @@ -45,6 +45,7 @@ class RpcServer: if self.websocket is None: return payloads: List[Dict] = await self.rpc_api._state_changed(*args) + log.error(f"State changed: {change}") if change == "add_connection" or change == "close_connection": data = await self.get_connections({}) diff --git a/src/rpc/wallet_rpc_client.py b/src/rpc/wallet_rpc_client.py index c821b609c1..04850aaf70 100644 --- a/src/rpc/wallet_rpc_client.py +++ b/src/rpc/wallet_rpc_client.py @@ -21,12 +21,9 @@ class WalletRpcClient(RpcClient): try: return await self.fetch( "log_in", - { - "host": "https://backup.chia.net", - "fingerprint": fingerprint, - "type": "start", - }, + {"host": "https://backup.chia.net", "fingerprint": fingerprint, "type": "start"}, ) + except ValueError as e: return e.args[0] @@ -48,11 +45,7 @@ class WalletRpcClient(RpcClient): try: return await self.fetch( "log_in", - { - "host": "https://backup.chia.net", - "fingerprint": fingerprint, - "type": "skip", - }, + {"host": "https://backup.chia.net", "fingerprint": fingerprint, "type": "skip"}, ) except ValueError as e: return e.args[0] diff --git a/src/server/server.py b/src/server/server.py index 4fd087d8d9..bc91e4769a 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -11,9 +11,7 @@ from aiohttp import web, ClientTimeout, client_exceptions, ClientSession from src.server.introducer_peers import IntroducerPeers from src.server.outbound_message import NodeType, Message, Payload -from src.server.ssl_context import ( - load_ssl_paths, -) +from src.server.ssl_context import load_ssl_paths from src.server.ws_connection import WSChiaConnection from src.types.peer_info import PeerInfo from src.types.sized_bytes import bytes32 @@ -49,6 +47,7 @@ class ChiaServer: def __init__( self, port: int, + node: Any, api: Any, local_type: NodeType, ping_interval: int, @@ -78,7 +77,7 @@ class ChiaServer: # Open connection tasks. These will be cancelled if self._oc_tasks: List[asyncio.Task] = [] - # Taks list to keep references to tasks, so they don'y get GCd + # Taks list to keep references to tasks, so they don't get GCd self._tasks: List[asyncio.Task] = [] if name: @@ -89,6 +88,7 @@ class ChiaServer: # Our unique random node id that we will send to other peers, regenerated on launch self.node_id = create_node_id() self.api = api + self.node = node self.root_path = root_path self.config = config self.on_connect: Optional[Callable] = None @@ -293,6 +293,9 @@ class ChiaServer: self.connection_by_type[connection.connection_type].pop(connection.peer_node_id) else: self.log.error(f"Invalid connection type for connection {connection}, while closing") + on_disconnect = getattr(self.node, "on_disconnect", None) + if on_disconnect is not None: + on_disconnect(connection) async def incoming_api_task(self): self.tasks = set() diff --git a/src/server/start_service.py b/src/server/start_service.py index 6eb05c625c..8ccebfdb06 100644 --- a/src/server/start_service.py +++ b/src/server/start_service.py @@ -67,6 +67,7 @@ class Service: self._server = ChiaServer( advertised_port, + node, peer_api, node_type, ping_interval, diff --git a/src/timelord/timelord.py b/src/timelord/timelord.py index b6310bbe5a..299a2ee644 100644 --- a/src/timelord/timelord.py +++ b/src/timelord/timelord.py @@ -4,7 +4,7 @@ import io import logging import time import traceback -from typing import Dict, List, Optional, Tuple +from typing import Dict, List, Optional, Tuple, Callable from chiavdf import create_discriminant @@ -21,9 +21,7 @@ from src.timelord.timelord_state import LastState from src.timelord.types import Chain, IterationType, StateType from src.types.classgroup import ClassgroupElement from src.types.end_of_slot_bundle import EndOfSubSlotBundle -from src.types.reward_chain_sub_block import ( - RewardChainSubBlock, -) +from src.types.reward_chain_sub_block import RewardChainSubBlock from src.types.sized_bytes import bytes32 from src.types.slots import ( ChallengeChainSubSlot, @@ -85,6 +83,7 @@ class Timelord: self.vdf_failures_count: int = 0 self.total_unfinished: int = 0 self.total_infused: int = 0 + self.state_changed_callback: Optional[Callable] = None async def _start(self): self.lock: asyncio.Lock = asyncio.Lock() diff --git a/src/types/peer_info.py b/src/types/peer_info.py index 58f3b508b8..b6989bf84a 100644 --- a/src/types/peer_info.py +++ b/src/types/peer_info.py @@ -37,12 +37,7 @@ class PeerInfo(Streamable): ip_v4 = ipaddress.IPv4Address(self.host) ip = ipaddress.IPv6Address(int(ipaddress.IPv6Address("2002::")) | (int(ip_v4) << 80)) key = ip.packed - key += bytes( - [ - self.port // 0x100, - self.port & 0x0FF, - ] - ) + key += bytes([self.port // 0x100, self.port & 0x0FF]) return key def get_group(self): diff --git a/src/util/block_tools.py b/src/util/block_tools.py index 47d352c72b..5284988b1b 100644 --- a/src/util/block_tools.py +++ b/src/util/block_tools.py @@ -16,9 +16,7 @@ from src.consensus.deficit import calculate_deficit from src.cmds.init import create_default_chia_config, initialize_ssl from src.cmds.plots import create_plots -from src.consensus.coinbase import ( - create_puzzlehash_for_pk, -) +from src.consensus.coinbase import create_puzzlehash_for_pk from src.consensus.constants import ConsensusConstants from src.consensus.pot_iterations import ( calculate_ip_iters, @@ -246,7 +244,7 @@ class BlockTools: timestamp=uint64(int(time.time())), farmer_reward_puzzle_hash=farmer_reward_puzzle_hash, ) - print(f"Created block 0 iters: {genesis.total_iters}") + log.info(f"Created block 0 iters: {genesis.total_iters}") num_empty_slots_added = skip_slots block_list = [genesis] num_blocks -= 1 @@ -384,7 +382,7 @@ class BlockTools: sub_blocks_added_this_sub_slot += 1 sub_blocks[full_block.header_hash] = sub_block_record - print( + log.info( f"Created block {sub_block_record.sub_block_height} ove=False, iters " f"{sub_block_record.total_iters}" ) @@ -456,7 +454,7 @@ class BlockTools: new_sub_slot_iters: Optional[uint64] = sub_epoch_summary.new_sub_slot_iters new_difficulty: Optional[uint64] = sub_epoch_summary.new_difficulty - print("Sub epoch summary:", sub_epoch_summary) + log.info(f"Sub epoch summary: {sub_epoch_summary}") else: ses_hash = None new_sub_slot_iters = None @@ -518,7 +516,7 @@ class BlockTools: if transaction_data_included: transaction_data = None sub_slots_finished += 1 - print( + log.info( f"Sub slot finished. Sub-blocks included: {sub_blocks_added_this_sub_slot} sub_blocks_per_slot: " f"{(len(block_list) - initial_block_list_len)/sub_slots_finished}" ) @@ -597,7 +595,7 @@ class BlockTools: block_list.append(full_block) sub_blocks_added_this_sub_slot += 1 - print( + log.info( f"Created block {sub_block_record.sub_block_height } ov=True, iters " f"{sub_block_record.total_iters}" ) diff --git a/src/util/logging.py b/src/util/logging.py index 2fb2cd545e..6ddc081d4b 100644 --- a/src/util/logging.py +++ b/src/util/logging.py @@ -47,6 +47,7 @@ def initialize_logging(service_name: str, logging_config: Dict, root_path: Path) logger.setLevel(logging.INFO) elif logging_config["log_level"] == "DEBUG": logger.setLevel(logging.DEBUG) + logging.getLogger("aiosqlite").setLevel(logging.INFO) # Too much logging on debug level else: logger.setLevel(logging.INFO) else: diff --git a/src/wallet/cc_wallet/cc_utils.py b/src/wallet/cc_wallet/cc_utils.py index 748748614d..28d782404d 100644 --- a/src/wallet/cc_wallet/cc_utils.py +++ b/src/wallet/cc_wallet/cc_utils.py @@ -59,11 +59,7 @@ def lineage_proof_for_cc_parent(parent_coin: Coin, parent_inner_puzzle_hash: byt return Program.to( ( 1, - [ - parent_coin.parent_coin_info, - parent_inner_puzzle_hash, - parent_coin.amount, - ], + [parent_coin.parent_coin_info, parent_inner_puzzle_hash, parent_coin.amount], ) ) diff --git a/src/wallet/puzzles/test_cc.py b/src/wallet/puzzles/test_cc.py index 32e1006d5b..f6ffbe3412 100644 --- a/src/wallet/puzzles/test_cc.py +++ b/src/wallet/puzzles/test_cc.py @@ -19,12 +19,8 @@ from src.wallet.cc_wallet.cc_utils import ( spend_bundle_for_spendable_ccs, CC_MOD, ) -from src.wallet.puzzles.genesis_by_coin_id_with_0 import ( - create_genesis_or_zero_coin_checker, -) -from src.wallet.puzzles.genesis_by_puzzle_hash_with_0 import ( - create_genesis_puzzle_or_zero_coin_checker, -) +from src.wallet.puzzles.genesis_by_coin_id_with_0 import create_genesis_or_zero_coin_checker +from src.wallet.puzzles.genesis_by_puzzle_hash_with_0 import create_genesis_puzzle_or_zero_coin_checker CONDITIONS = dict((k, bytes(v)[0]) for k, v in ConditionOpcode.__members__.items()) diff --git a/src/wallet/trade_manager.py b/src/wallet/trade_manager.py index e20bca2859..a395edb9c8 100644 --- a/src/wallet/trade_manager.py +++ b/src/wallet/trade_manager.py @@ -16,9 +16,7 @@ from src.util.hash import std_hash from src.util.ints import uint32, uint64 from src.wallet.cc_wallet import cc_utils from src.wallet.cc_wallet.cc_wallet import CCWallet -from src.wallet.puzzles.genesis_by_coin_id_with_0 import ( - genesis_coin_id_for_genesis_coin_checker, -) +from src.wallet.puzzles.genesis_by_coin_id_with_0 import genesis_coin_id_for_genesis_coin_checker from src.wallet.trade_record import TradeRecord from src.wallet.trading.trade_status import TradeStatus from src.wallet.trading.trade_store import TradeStore diff --git a/src/wallet/wallet_blockchain.py b/src/wallet/wallet_blockchain.py index 58edf1512c..69fa19ea14 100644 --- a/src/wallet/wallet_blockchain.py +++ b/src/wallet/wallet_blockchain.py @@ -20,9 +20,7 @@ from src.types.unfinished_block import UnfinishedBlock from src.util.errors import Err from src.util.ints import uint32, uint64 from src.consensus.find_fork_point import find_fork_point_in_chain -from src.consensus.block_header_validation import ( - validate_finished_header_block, -) +from src.consensus.block_header_validation import validate_finished_header_block from src.wallet.block_record import HeaderBlockRecord from src.wallet.wallet_coin_store import WalletCoinStore from src.wallet.wallet_block_store import WalletBlockStore diff --git a/src/wallet/wallet_transaction_store.py b/src/wallet/wallet_transaction_store.py index c13c730661..8eb5eb9204 100644 --- a/src/wallet/wallet_transaction_store.py +++ b/src/wallet/wallet_transaction_store.py @@ -71,7 +71,8 @@ class WalletTransactionStore: return self async def _init_cache(self): - print("init cache here") + # init cache here + pass async def _clear_database(self): cursor = await self.db_connection.execute("DELETE FROM transaction_record") diff --git a/tests/consensus/test_blockchain.py b/tests/consensus/test_blockchain.py index d2e8a11394..9c2b6528af 100644 --- a/tests/consensus/test_blockchain.py +++ b/tests/consensus/test_blockchain.py @@ -1,9 +1,9 @@ # flake8: noqa: F811, F401 import asyncio from dataclasses import replace +import logging import pytest from blspy import AugSchemeMPL, G2Element -from pytest import raises from src.consensus.blockchain import ReceiveBlockResult from src.types.classgroup import ClassgroupElement @@ -23,6 +23,8 @@ from tests.full_node.fixtures import default_1000_blocks # noqa: F401 from tests.full_node.fixtures import default_400_blocks # noqa: F401 from tests.full_node.fixtures import default_10000_blocks # noqa: F401 +log = logging.getLogger(__name__) + @pytest.fixture(scope="module") def event_loop(): @@ -55,1489 +57,1481 @@ class TestGenesisBlock: if proof.is_valid(test_constants, ClassgroupElement.get_default_element(), vdf) is False: raise Exception("invalid proof") + @pytest.mark.asyncio + async def test_non_overflow_genesis(self, empty_blockchain): + assert empty_blockchain.get_peak() is None + genesis = bt.get_consecutive_blocks(1, force_overflow=False)[0] + result, err, _ = await empty_blockchain.receive_block(genesis, False) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + assert empty_blockchain.get_peak().sub_block_height == 0 -# -# -# -# @pytest.mark.asyncio -# async def test_non_overflow_genesis(self, empty_blockchain): -# assert empty_blockchain.get_peak() is None -# genesis = bt.get_consecutive_blocks(1, force_overflow=False)[0] -# result, err, _ = await empty_blockchain.receive_block(genesis, False) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# assert empty_blockchain.get_peak().sub_block_height == 0 -# -# @pytest.mark.asyncio -# async def test_overflow_genesis(self, empty_blockchain): -# genesis = bt.get_consecutive_blocks(1, force_overflow=True)[0] -# result, err, _ = await empty_blockchain.receive_block(genesis, False) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_genesis_empty_slots(self, empty_blockchain): -# genesis = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=3)[0] -# result, err, _ = await empty_blockchain.receive_block(genesis, False) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_overflow_genesis_empty_slots(self, empty_blockchain): -# genesis = bt.get_consecutive_blocks(1, force_overflow=True, skip_slots=3)[0] -# result, err, _ = await empty_blockchain.receive_block(genesis, False) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_genesis_validate_1(self, empty_blockchain): -# genesis = bt.get_consecutive_blocks(1, force_overflow=False)[0] -# bad_prev = bytes([1] * 32) -# genesis = recursive_replace(genesis, "foliage_sub_block.prev_sub_block_hash", bad_prev) -# result, err, _ = await empty_blockchain.receive_block(genesis, False) -# assert err == Err.INVALID_PREV_BLOCK_HASH -# -# -# class TestBlockHeaderValidation: -# @pytest.mark.asyncio -# async def test_long_chain(self, empty_blockchain, default_1000_blocks): -# blocks = default_1000_blocks -# for block in blocks: -# if ( -# len(block.finished_sub_slots) > 0 -# and block.finished_sub_slots[0].challenge_chain.subepoch_summary_hash is not None -# ): -# # Sub/Epoch. Try using a bad ssi and difficulty to test 2m and 2n -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[0], -# "challenge_chain.new_sub_slot_iters", -# uint64(10000000), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_NEW_SUB_SLOT_ITERS -# new_finished_ss_2 = recursive_replace( -# block.finished_sub_slots[0], -# "challenge_chain.new_difficulty", -# uint64(10000000), -# ) -# block_bad_2 = recursive_replace( -# block, "finished_sub_slots", [new_finished_ss_2] + block.finished_sub_slots[1:] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_2) -# assert err == Err.INVALID_NEW_DIFFICULTY -# -# # 3c -# new_finished_ss_3: EndOfSubSlotBundle = recursive_replace( -# block.finished_sub_slots[0], -# "challenge_chain.subepoch_summary_hash", -# bytes([0] * 32), -# ) -# new_finished_ss_3 = recursive_replace( -# new_finished_ss_3, -# "reward_chain.challenge_chain_sub_slot_hash", -# new_finished_ss_3.challenge_chain.get_hash(), -# ) -# block_bad_3 = recursive_replace( -# block, "finished_sub_slots", [new_finished_ss_3] + block.finished_sub_slots[1:] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_3) -# assert err == Err.INVALID_SUB_EPOCH_SUMMARY -# -# # 3d -# new_finished_ss_4 = recursive_replace( -# block.finished_sub_slots[0], -# "challenge_chain.subepoch_summary_hash", -# None, -# ) -# new_finished_ss_4 = recursive_replace( -# new_finished_ss_4, -# "reward_chain.challenge_chain_sub_slot_hash", -# new_finished_ss_4.challenge_chain.get_hash(), -# ) -# block_bad_4 = recursive_replace( -# block, "finished_sub_slots", [new_finished_ss_4] + block.finished_sub_slots[1:] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_4) -# assert err == Err.INVALID_SUB_EPOCH_SUMMARY or err == Err.INVALID_NEW_SUB_SLOT_ITERS -# -# result, err, _ = await empty_blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# print( -# f"Added block {block.sub_block_height} total iters {block.total_iters} new slot? {len(block.finished_sub_slots)}" -# ) -# assert empty_blockchain.get_peak().sub_block_height == len(blocks) - 1 -# -# @pytest.mark.asyncio -# async def test_unfinished_blocks(self, empty_blockchain): -# blockchain = empty_blockchain -# blocks = bt.get_consecutive_blocks(2) -# for block in blocks[:-1]: -# result, err, _ = await blockchain.receive_block(block) -# assert result == ReceiveBlockResult.NEW_PEAK -# block = blocks[-1] -# unf = UnfinishedBlock( -# block.finished_sub_slots, -# block.reward_chain_sub_block.get_unfinished(), -# block.challenge_chain_sp_proof, -# block.reward_chain_sp_proof, -# block.foliage_sub_block, -# block.foliage_block, -# block.transactions_info, -# block.transactions_generator, -# ) -# _, err = await blockchain.validate_unfinished_block(unf, False) -# assert err is None -# result, err, _ = await blockchain.receive_block(block) -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, force_overflow=True) -# block = blocks[-1] -# unf = UnfinishedBlock( -# block.finished_sub_slots, -# block.reward_chain_sub_block.get_unfinished(), -# block.challenge_chain_sp_proof, -# block.reward_chain_sp_proof, -# block.foliage_sub_block, -# block.foliage_block, -# block.transactions_info, -# block.transactions_generator, -# ) -# _, err = await blockchain.validate_unfinished_block(unf, False) -# assert err is None -# -# @pytest.mark.asyncio -# async def test_empty_genesis(self, empty_blockchain): -# blockchain = empty_blockchain -# for block in bt.get_consecutive_blocks(2, skip_slots=3): -# result, err, _ = await blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_empty_slots_non_genesis(self, empty_blockchain): -# blockchain = empty_blockchain -# blocks = bt.get_consecutive_blocks(10) -# for block in blocks: -# result, err, _ = await blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# blocks = bt.get_consecutive_blocks(10, skip_slots=2, block_list_input=blocks) -# for block in blocks[10:]: -# result, err, _ = await blockchain.receive_block(block) -# assert err is None -# assert blockchain.get_peak().sub_block_height == 19 -# -# @pytest.mark.asyncio -# async def test_one_sb_per_slot(self, empty_blockchain): -# blockchain = empty_blockchain -# num_blocks = 20 -# blocks = [] -# for i in range(num_blocks): -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) -# result, err, _ = await blockchain.receive_block(blocks[-1]) -# assert result == ReceiveBlockResult.NEW_PEAK -# assert blockchain.get_peak().sub_block_height == num_blocks - 1 -# -# @pytest.mark.asyncio -# async def test_one_sb_per_two_slots(self, empty_blockchain): -# blockchain = empty_blockchain -# num_blocks = 20 -# blocks = [] -# for i in range(num_blocks): # Same thing, but 2 sub-slots per sub-block -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=2) -# result, err, _ = await blockchain.receive_block(blocks[-1]) -# assert result == ReceiveBlockResult.NEW_PEAK -# assert blockchain.get_peak().sub_block_height == num_blocks - 1 -# -# @pytest.mark.asyncio -# async def test_one_sb_per_five_slots(self, empty_blockchain): -# blockchain = empty_blockchain -# num_blocks = 10 -# blocks = [] -# for i in range(num_blocks): # Same thing, but 5 sub-slots per sub-block -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=5) -# result, err, _ = await blockchain.receive_block(blocks[-1]) -# assert result == ReceiveBlockResult.NEW_PEAK -# assert blockchain.get_peak().sub_block_height == num_blocks - 1 -# -# @pytest.mark.asyncio -# async def test_basic_chain_overflow(self, empty_blockchain): -# blocks = bt.get_consecutive_blocks(5, force_overflow=True) -# for block in blocks: -# result, err, _ = await empty_blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# print(f"added {block.sub_block_height} {block.total_iters}") -# assert empty_blockchain.get_peak().sub_block_height == len(blocks) - 1 -# -# @pytest.mark.asyncio -# async def test_one_sb_per_two_slots_force_overflow(self, empty_blockchain): -# blockchain = empty_blockchain -# num_blocks = 10 -# blocks = [] -# for i in range(num_blocks): -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=2, force_overflow=True) -# result, err, _ = await blockchain.receive_block(blocks[-1]) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# assert blockchain.get_peak().sub_block_height == num_blocks - 1 -# -# @pytest.mark.asyncio -# async def test_invalid_prev(self, empty_blockchain): -# # 1 -# blocks = bt.get_consecutive_blocks(2, force_overflow=False) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_1_bad = recursive_replace(blocks[-1], "foliage_sub_block.prev_sub_block_hash", bytes([0] * 32)) -# print(block_1_bad) -# -# result, err, _ = await empty_blockchain.receive_block(block_1_bad) -# assert result == ReceiveBlockResult.DISCONNECTED_BLOCK -# -# @pytest.mark.asyncio -# async def test_invalid_pospace(self, empty_blockchain): -# # 2 -# blocks = bt.get_consecutive_blocks(2, force_overflow=False) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_1_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.proof_of_space.proof", bytes([0] * 32)) -# -# result, err, _ = await empty_blockchain.receive_block(block_1_bad) -# assert result == ReceiveBlockResult.INVALID_BLOCK -# assert err == Err.INVALID_POSPACE -# -# @pytest.mark.asyncio -# async def test_invalid_sub_slot_challenge_hash_genesis(self, empty_blockchain): -# # 2a -# blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1) -# new_finished_ss = recursive_replace( -# blocks[0].finished_sub_slots[0], -# "challenge_chain.challenge_chain_end_of_slot_vdf.challenge", -# bytes([2] * 32), -# ) -# block_0_bad = recursive_replace( -# blocks[0], "finished_sub_slots", [new_finished_ss] + blocks[0].finished_sub_slots[1:] -# ) -# -# result, err, _ = await empty_blockchain.receive_block(block_0_bad) -# assert result == ReceiveBlockResult.INVALID_BLOCK -# assert err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH -# -# @pytest.mark.asyncio -# async def test_invalid_sub_slot_challenge_hash_non_genesis(self, empty_blockchain): -# # 2b -# blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=0) -# blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1, block_list_input=blocks) -# print(blocks) -# new_finished_ss = recursive_replace( -# blocks[1].finished_sub_slots[0], -# "challenge_chain.challenge_chain_end_of_slot_vdf.challenge", -# bytes([2] * 32), -# ) -# block_1_bad = recursive_replace( -# blocks[1], "finished_sub_slots", [new_finished_ss] + blocks[1].finished_sub_slots[1:] -# ) -# -# _, _, _ = await empty_blockchain.receive_block(blocks[0]) -# result, err, _ = await empty_blockchain.receive_block(block_1_bad) -# assert result == ReceiveBlockResult.INVALID_BLOCK -# assert err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH -# -# @pytest.mark.asyncio -# async def test_invalid_sub_slot_challenge_hash_empty_ss(self, empty_blockchain): -# # 2c -# blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=0) -# blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=2, block_list_input=blocks) -# new_finished_ss = recursive_replace( -# blocks[1].finished_sub_slots[-1], -# "challenge_chain.challenge_chain_end_of_slot_vdf.challenge", -# bytes([2] * 32), -# ) -# block_1_bad = recursive_replace( -# blocks[1], "finished_sub_slots", blocks[1].finished_sub_slots[:-1] + [new_finished_ss] -# ) -# -# _, _, _ = await empty_blockchain.receive_block(blocks[0]) -# result, err, _ = await empty_blockchain.receive_block(block_1_bad) -# assert result == ReceiveBlockResult.INVALID_BLOCK -# assert err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH -# -# @pytest.mark.asyncio -# async def test_genesis_no_icc(self, empty_blockchain): -# # 2d -# blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1) -# new_finished_ss = recursive_replace( -# blocks[0].finished_sub_slots[0], -# "infused_challenge_chain", -# InfusedChallengeChainSubSlot( -# VDFInfo( -# bytes([0] * 32), -# uint64(1200), -# ClassgroupElement.get_default_element(), -# ) -# ), -# ) -# block_0_bad = recursive_replace( -# blocks[0], "finished_sub_slots", [new_finished_ss] + blocks[0].finished_sub_slots[1:] -# ) -# -# result, err, _ = await empty_blockchain.receive_block(block_0_bad) -# assert result == ReceiveBlockResult.INVALID_BLOCK -# assert err == Err.SHOULD_NOT_HAVE_ICC -# -# @pytest.mark.asyncio -# async def test_invalid_icc_sub_slot_vdf(self, empty_blockchain): -# blocks = bt.get_consecutive_blocks(10) -# for block in blocks: -# if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is not None: -# # Bad iters -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[-1], -# "infused_challenge_chain", -# InfusedChallengeChainSubSlot( -# replace( -# block.finished_sub_slots[ -# -1 -# ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf, -# number_of_iterations=10000000, -# ) -# ), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_ICC_EOS_VDF -# -# # Bad output -# new_finished_ss_2 = recursive_replace( -# block.finished_sub_slots[-1], -# "infused_challenge_chain", -# InfusedChallengeChainSubSlot( -# replace( -# block.finished_sub_slots[ -# -1 -# ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf, -# output=ClassgroupElement.get_default_element(), -# ) -# ), -# ) -# block_bad_2 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_2) -# assert err == Err.INVALID_ICC_EOS_VDF -# -# # Bad challenge hash -# new_finished_ss_3 = recursive_replace( -# block.finished_sub_slots[-1], -# "infused_challenge_chain", -# InfusedChallengeChainSubSlot( -# replace( -# block.finished_sub_slots[ -# -1 -# ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf, -# challenge=bytes([0] * 32), -# ) -# ), -# ) -# block_bad_3 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_3) -# assert err == Err.INVALID_ICC_EOS_VDF -# -# # Bad proof -# new_finished_ss_5 = recursive_replace( -# block.finished_sub_slots[-1], -# "proofs.infused_challenge_chain_slot_proof", -# VDFProof(uint8(0), b"1239819023890"), -# ) -# block_bad_5 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_5) -# assert err == Err.INVALID_ICC_EOS_VDF -# -# result, err, _ = await empty_blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_invalid_icc_into_cc(self, empty_blockchain): -# blockchain = empty_blockchain -# blocks = bt.get_consecutive_blocks(1) -# assert (await blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# case_1, case_2 = False, False -# while not case_1 or not case_2: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) -# block = blocks[-1] -# if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is not None: -# if ( -# block.finished_sub_slots[-1].reward_chain.deficit -# == test_constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK -# ): -# # 2g -# case_1 = True -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[-1], -# "challenge_chain", -# replace( -# block.finished_sub_slots[-1].challenge_chain, -# infused_challenge_chain_sub_slot_hash=bytes([1] * 32), -# ), -# ) -# else: -# # 2h -# case_2 = True -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[-1], -# "challenge_chain", -# replace( -# block.finished_sub_slots[-1].challenge_chain, -# infused_challenge_chain_sub_slot_hash=block.finished_sub_slots[ -# -1 -# ].infused_challenge_chain.get_hash(), -# ), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] -# ) -# result, err, _ = await blockchain.receive_block(block_bad) -# assert err == Err.INVALID_ICC_HASH_CC -# -# # 2i -# new_finished_ss_bad_rc = recursive_replace( -# block.finished_sub_slots[-1], -# "reward_chain", -# replace(block.finished_sub_slots[-1].reward_chain, infused_challenge_chain_sub_slot_hash=None), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc] -# ) -# result, err, _ = await blockchain.receive_block(block_bad) -# assert err == Err.INVALID_ICC_HASH_RC -# elif len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is None: -# # 2j -# new_finished_ss_bad_cc = recursive_replace( -# block.finished_sub_slots[-1], -# "challenge_chain", -# replace( -# block.finished_sub_slots[-1].challenge_chain, -# infused_challenge_chain_sub_slot_hash=bytes([1] * 32), -# ), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_cc] -# ) -# print(len(block.finished_sub_slots)) -# result, err, _ = await blockchain.receive_block(block_bad) -# assert err == Err.INVALID_ICC_HASH_CC -# -# # 2k -# new_finished_ss_bad_rc = recursive_replace( -# block.finished_sub_slots[-1], -# "reward_chain", -# replace( -# block.finished_sub_slots[-1].reward_chain, infused_challenge_chain_sub_slot_hash=bytes([1] * 32) -# ), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc] -# ) -# result, err, _ = await blockchain.receive_block(block_bad) -# assert err == Err.INVALID_ICC_HASH_RC -# -# # Finally, add the block properly -# result, err, _ = await blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_empty_slot_no_ses(self, empty_blockchain): -# # 2l -# blockchain = empty_blockchain -# blocks = bt.get_consecutive_blocks(1) -# assert (await blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=4) -# -# new_finished_ss = recursive_replace( -# blocks[-1].finished_sub_slots[-1], -# "challenge_chain", -# replace(blocks[-1].finished_sub_slots[-1].challenge_chain, subepoch_summary_hash=std_hash(b"0")), -# ) -# block_bad = recursive_replace( -# blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss] -# ) -# result, err, _ = await blockchain.receive_block(block_bad) -# assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH -# -# @pytest.mark.asyncio -# async def test_empty_sub_slots_epoch(self, empty_blockchain): -# # 2m -# # Tests adding an empty sub slot after the sub-epoch / epoch. -# # Also tests overflow block in epoch -# blocks_base = bt.get_consecutive_blocks(test_constants.EPOCH_SUB_BLOCKS) -# blocks_1 = bt.get_consecutive_blocks(1, block_list_input=blocks_base, force_overflow=True) -# blocks_2 = bt.get_consecutive_blocks(1, skip_slots=1, block_list_input=blocks_base, force_overflow=True) -# blocks_3 = bt.get_consecutive_blocks(1, skip_slots=2, block_list_input=blocks_base, force_overflow=True) -# blocks_4 = bt.get_consecutive_blocks(1, block_list_input=blocks_base) -# for block in blocks_base: -# result, err, _ = await empty_blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# for block in [blocks_1[-1], blocks_2[-1], blocks_3[-1], blocks_4[-1]]: -# result, err, _ = await empty_blockchain.receive_block(block) -# assert err is None -# -# @pytest.mark.asyncio -# async def test_wrong_cc_hash_rc(self, empty_blockchain): -# # 2o -# blockchain = empty_blockchain -# blocks = bt.get_consecutive_blocks(1, skip_slots=1) -# blocks = bt.get_consecutive_blocks(1, skip_slots=1, block_list_input=blocks) -# assert (await blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# new_finished_ss = recursive_replace( -# blocks[-1].finished_sub_slots[-1], -# "reward_chain", -# replace(blocks[-1].finished_sub_slots[-1].reward_chain, challenge_chain_sub_slot_hash=bytes([3] * 32)), -# ) -# block_1_bad = recursive_replace( -# blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss] -# ) -# -# result, err, _ = await blockchain.receive_block(block_1_bad) -# assert result == ReceiveBlockResult.INVALID_BLOCK -# assert err == Err.INVALID_CHALLENGE_SLOT_HASH_RC -# -# @pytest.mark.asyncio -# async def test_invalid_cc_sub_slot_vdf(self, empty_blockchain): -# # 2q -# blocks = bt.get_consecutive_blocks(10) -# for block in blocks: -# if len(block.finished_sub_slots): -# # Bad iters -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[-1], -# "challenge_chain", -# recursive_replace( -# block.finished_sub_slots[-1].challenge_chain, -# "challenge_chain_end_of_slot_vdf.number_of_iterations", -# uint64(10000000), -# ), -# ) -# new_finished_ss = recursive_replace( -# new_finished_ss, -# "reward_chain.challenge_chain_sub_slot_hash", -# new_finished_ss.challenge_chain.get_hash(), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_CC_EOS_VDF -# -# # Bad output -# new_finished_ss_2 = recursive_replace( -# block.finished_sub_slots[-1], -# "challenge_chain", -# recursive_replace( -# block.finished_sub_slots[-1].challenge_chain, -# "challenge_chain_end_of_slot_vdf.output", -# ClassgroupElement.get_default_element(), -# ), -# ) -# -# new_finished_ss_2 = recursive_replace( -# new_finished_ss_2, -# "reward_chain.challenge_chain_sub_slot_hash", -# new_finished_ss_2.challenge_chain.get_hash(), -# ) -# block_bad_2 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_2) -# assert err == Err.INVALID_CC_EOS_VDF -# -# # Bad challenge hash -# new_finished_ss_3 = recursive_replace( -# block.finished_sub_slots[-1], -# "challenge_chain", -# recursive_replace( -# block.finished_sub_slots[-1].challenge_chain, -# "challenge_chain_end_of_slot_vdf.challenge", -# bytes([1] * 32), -# ), -# ) -# -# new_finished_ss_3 = recursive_replace( -# new_finished_ss_3, -# "reward_chain.challenge_chain_sub_slot_hash", -# new_finished_ss_3.challenge_chain.get_hash(), -# ) -# block_bad_3 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_3) -# assert err == Err.INVALID_CC_EOS_VDF or err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH -# -# # Bad proof -# new_finished_ss_5 = recursive_replace( -# block.finished_sub_slots[-1], -# "proofs.challenge_chain_slot_proof", -# VDFProof(uint8(0), b"1239819023890"), -# ) -# block_bad_5 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_5) -# assert err == Err.INVALID_CC_EOS_VDF -# -# result, err, _ = await empty_blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_invalid_rc_sub_slot_vdf(self, empty_blockchain): -# # 2p -# blocks = bt.get_consecutive_blocks(10) -# for block in blocks: -# if len(block.finished_sub_slots): -# # Bad iters -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[-1], -# "reward_chain", -# recursive_replace( -# block.finished_sub_slots[-1].reward_chain, -# "end_of_slot_vdf.number_of_iterations", -# uint64(10000000), -# ), -# ) -# block_bad = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_RC_EOS_VDF -# -# # Bad output -# new_finished_ss_2 = recursive_replace( -# block.finished_sub_slots[-1], -# "reward_chain", -# recursive_replace( -# block.finished_sub_slots[-1].reward_chain, -# "end_of_slot_vdf.output", -# ClassgroupElement.get_default_element(), -# ), -# ) -# block_bad_2 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_2) -# assert err == Err.INVALID_RC_EOS_VDF -# -# # Bad challenge hash -# new_finished_ss_3 = recursive_replace( -# block.finished_sub_slots[-1], -# "reward_chain", -# recursive_replace( -# block.finished_sub_slots[-1].reward_chain, -# "end_of_slot_vdf.challenge", -# bytes([1] * 32), -# ), -# ) -# block_bad_3 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_3) -# assert err == Err.INVALID_RC_EOS_VDF -# -# # Bad proof -# new_finished_ss_5 = recursive_replace( -# block.finished_sub_slots[-1], -# "proofs.reward_chain_slot_proof", -# VDFProof(uint8(0), b"1239819023890"), -# ) -# block_bad_5 = recursive_replace( -# block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad_5) -# assert err == Err.INVALID_RC_EOS_VDF -# -# result, err, _ = await empty_blockchain.receive_block(block) -# assert err is None -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_genesis_bad_deficit(self, empty_blockchain): -# # 2r -# block = bt.get_consecutive_blocks(1, skip_slots=2)[0] -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[-1], -# "reward_chain", -# recursive_replace( -# block.finished_sub_slots[-1].reward_chain, -# "deficit", -# test_constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1, -# ), -# ) -# block_bad = recursive_replace(block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_DEFICIT -# -# @pytest.mark.asyncio -# async def test_reset_deficit(self, empty_blockchain): -# # 2s, 2t -# blockchain = empty_blockchain -# blocks = bt.get_consecutive_blocks(2) -# await empty_blockchain.receive_block(blocks[0]) -# await empty_blockchain.receive_block(blocks[1]) -# case_1, case_2 = False, False -# while not case_1 or not case_2: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) -# if len(blocks[-1].finished_sub_slots) > 0: -# new_finished_ss = recursive_replace( -# blocks[-1].finished_sub_slots[-1], -# "reward_chain", -# recursive_replace( -# blocks[-1].finished_sub_slots[-1].reward_chain, -# "deficit", -# uint8(0), -# ), -# ) -# if blockchain.sub_blocks[blocks[-2].header_hash].deficit == 0: -# case_1 = True -# else: -# case_2 = True -# -# block_bad = recursive_replace( -# blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_DEFICIT or err == Err.INVALID_ICC_HASH_CC -# -# result, err, _ = await empty_blockchain.receive_block(blocks[-1]) -# assert result == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_genesis_has_ses(self, empty_blockchain): -# # 3a -# block = bt.get_consecutive_blocks(1, skip_slots=1)[0] -# new_finished_ss = recursive_replace( -# block.finished_sub_slots[0], -# "challenge_chain", -# recursive_replace( -# block.finished_sub_slots[0].challenge_chain, -# "subepoch_summary_hash", -# bytes([0] * 32), -# ), -# ) -# -# new_finished_ss = recursive_replace( -# new_finished_ss, -# "reward_chain", -# replace( -# new_finished_ss.reward_chain, challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash() -# ), -# ) -# block_bad = recursive_replace(block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:]) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH -# -# @pytest.mark.asyncio -# async def test_no_ses_if_no_se(self, empty_blockchain): -# # 3b -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if len(blocks[-1].finished_sub_slots) > 0: -# new_finished_ss: EndOfSubSlotBundle = recursive_replace( -# blocks[-1].finished_sub_slots[0], -# "challenge_chain", -# recursive_replace( -# blocks[-1].finished_sub_slots[0].challenge_chain, -# "subepoch_summary_hash", -# bytes([0] * 32), -# ), -# ) -# -# new_finished_ss = recursive_replace( -# new_finished_ss, -# "reward_chain", -# replace( -# new_finished_ss.reward_chain, -# challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash(), -# ), -# ) -# block_bad = recursive_replace( -# blocks[-1], "finished_sub_slots", [new_finished_ss] + blocks[-1].finished_sub_slots[1:] -# ) -# result, err, _ = await empty_blockchain.receive_block(block_bad) -# assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH -# return -# await empty_blockchain.receive_block(blocks[-1]) -# -# @pytest.mark.asyncio -# async def test_too_many_sub_blocks(self, empty_blockchain): -# # 4: TODO -# pass -# -# @pytest.mark.asyncio -# async def test_bad_pos(self, empty_blockchain): -# # 5 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.proof_of_space.challenge", std_hash(b"")) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE -# -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.proof_of_space.pool_contract_puzzle_hash", std_hash(b"") -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE -# -# block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.proof_of_space.pool_public_key", None) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE -# -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.proof_of_space.plot_public_key", -# AugSchemeMPL.key_gen(std_hash(b"1231n")).get_g1(), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.proof_of_space.size", -# 32, -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.proof_of_space.proof", -# bytes([1] * int(blocks[-1].reward_chain_sub_block.proof_of_space.size * 64 / 8)), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE -# -# # TODO: test not passing the plot filter -# -# @pytest.mark.asyncio -# async def test_bad_signage_point_index(self, empty_blockchain): -# # 6 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# with raises(ValueError): -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.signage_point_index", test_constants.NUM_SPS_SUB_SLOT -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_SP_INDEX -# with raises(ValueError): -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.signage_point_index", test_constants.NUM_SPS_SUB_SLOT + 1 -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_SP_INDEX -# -# @pytest.mark.asyncio -# async def test_sp_0_no_sp(self, empty_blockchain): -# # 7 -# blocks = [] -# case_1, case_2 = False, False -# while not case_1 or not case_2: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].reward_chain_sub_block.signage_point_index == 0: -# case_1 = True -# block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.signage_point_index", uint8(1)) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_SP_INDEX -# else: -# case_2 = True -# block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.signage_point_index", uint8(0)) -# error_code = (await empty_blockchain.receive_block(block_bad))[1] -# assert error_code == Err.INVALID_SP_INDEX or error_code == Err.INVALID_POSPACE -# assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_epoch_overflows(self, empty_blockchain): -# # 9. TODO. This is hard to test because it requires modifying the block tools to make these special blocks -# pass -# -# @pytest.mark.asyncio -# async def test_bad_total_iters(self, empty_blockchain): -# # 10 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.total_iters", blocks[-1].reward_chain_sub_block.total_iters + 1 -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_TOTAL_ITERS -# -# @pytest.mark.asyncio -# async def test_bad_rc_sp_vdf(self, empty_blockchain): -# # 11 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].reward_chain_sub_block.signage_point_index != 0: -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.reward_chain_sp_vdf.challenge", std_hash(b"1") -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.reward_chain_sp_vdf.output", -# ClassgroupElement(int512(10), int512(2)), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.reward_chain_sp_vdf.number_of_iterations", -# uint64(1111111111111), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sp_proof", -# VDFProof(uint8(0), std_hash(b"")), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF -# return -# assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_bad_rc_sp_sig(self, empty_blockchain): -# # 12 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.reward_chain_sp_signature", G2Element.generator() -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SIGNATURE -# -# @pytest.mark.asyncio -# async def test_bad_cc_sp_vdf(self, empty_blockchain): -# # 13. Note: does not validate fully due to proof of space being validated first -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].reward_chain_sub_block.signage_point_index != 0: -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.challenge_chain_sp_vdf.challenge", std_hash(b"1") -# ) -# assert (await empty_blockchain.receive_block(block_bad))[0] == ReceiveBlockResult.INVALID_BLOCK -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.challenge_chain_sp_vdf.output", -# ClassgroupElement(int512(10), int512(2)), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[0] == ReceiveBlockResult.INVALID_BLOCK -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.challenge_chain_sp_vdf.number_of_iterations", -# uint64(1111111111111), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[0] == ReceiveBlockResult.INVALID_BLOCK -# block_bad = recursive_replace( -# blocks[-1], -# "challenge_chain_sp_proof", -# VDFProof(uint8(0), std_hash(b"")), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_SP_VDF -# return -# assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_bad_cc_sp_sig(self, empty_blockchain): -# # 14 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.challenge_chain_sp_signature", G2Element.generator() -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_SIGNATURE -# -# @pytest.mark.asyncio -# async def test_is_block(self, empty_blockchain): -# # 15: TODO -# pass -# -# @pytest.mark.asyncio -# async def test_bad_foliage_sb_sig(self, empty_blockchain): -# # 16 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_sub_block_signature", G2Element.generator() -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PLOT_SIGNATURE -# -# @pytest.mark.asyncio -# async def test_bad_foliage_block_sig(self, empty_blockchain): -# # 17 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].foliage_block is not None: -# block_bad = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_block_signature", G2Element.generator() -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PLOT_SIGNATURE -# return -# assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK -# -# @pytest.mark.asyncio -# async def test_unfinished_reward_chain_sb_hash(self, empty_blockchain): -# # 18 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_sub_block_data.unfinished_reward_block_hash", std_hash(b"2") -# ) -# new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() -# new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_URSB_HASH -# -# @pytest.mark.asyncio -# async def test_pool_target_height(self, empty_blockchain): -# # 19 -# blocks = bt.get_consecutive_blocks(3) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# assert (await empty_blockchain.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_sub_block_data.pool_target.max_height", 1 -# ) -# new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() -# new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.OLD_POOL_TARGET -# -# @pytest.mark.asyncio -# async def test_pool_target_pre_farm(self, empty_blockchain): -# # 20a -# blocks = bt.get_consecutive_blocks(1) -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_sub_block_data.pool_target.puzzle_hash", std_hash(b"12") -# ) -# new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() -# new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREFARM -# -# @pytest.mark.asyncio -# async def test_pool_target_signature(self, empty_blockchain): -# # 20b -# blocks = bt.get_consecutive_blocks(3) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# assert (await empty_blockchain.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_sub_block_data.pool_signature", G2Element.generator() -# ) -# new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() -# new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POOL_SIGNATURE -# -# @pytest.mark.asyncio -# async def test_foliage_data_presence(self, empty_blockchain): -# # 22 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# case_1, case_2 = False, False -# while not case_1 or not case_2: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].foliage_block is not None: -# case_1 = True -# block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_sub_block.foliage_block_hash", None) -# else: -# case_2 = True -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_block_hash", std_hash(b"") -# ) -# err_code = (await empty_blockchain.receive_block(block_bad))[1] -# assert err_code == Err.INVALID_FOLIAGE_BLOCK_PRESENCE or err_code == Err.INVALID_IS_BLOCK -# await empty_blockchain.receive_block(blocks[-1]) -# -# @pytest.mark.asyncio -# async def test_foliage_block_hash(self, empty_blockchain): -# # 23 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# case_1, case_2 = False, False -# while not case_1 or not case_2: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].foliage_block is not None: -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_sub_block.foliage_block_hash", std_hash(b"2") -# ) -# -# new_m = block_bad.foliage_sub_block.foliage_block_hash -# new_fbh_sig = bt.get_plot_signature( -# new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key -# ) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_FOLIAGE_BLOCK_HASH -# return -# await empty_blockchain.receive_block(blocks[-1]) -# -# @pytest.mark.asyncio -# async def test_genesis_bad_prev_block(self, empty_blockchain): -# # 24a -# blocks = bt.get_consecutive_blocks(1) -# block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_block.prev_block_hash", std_hash(b"2")) -# block_bad: FullBlock = recursive_replace( -# block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() -# ) -# new_m = block_bad.foliage_sub_block.foliage_block_hash -# new_fbh_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREV_BLOCK_HASH -# -# @pytest.mark.asyncio -# async def test_bad_prev_block_non_genesis(self, empty_blockchain): -# # 24b -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].foliage_block is not None: -# block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_block.prev_block_hash", std_hash(b"2")) -# block_bad: FullBlock = recursive_replace( -# block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() -# ) -# new_m = block_bad.foliage_sub_block.foliage_block_hash -# new_fbh_sig = bt.get_plot_signature( -# new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key -# ) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREV_BLOCK_HASH -# return -# await empty_blockchain.receive_block(blocks[-1]) -# -# @pytest.mark.asyncio -# async def test_bad_filter_hash(self, empty_blockchain): -# # 25 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].foliage_block is not None: -# block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_block.filter_hash", std_hash(b"2")) -# block_bad: FullBlock = recursive_replace( -# block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() -# ) -# new_m = block_bad.foliage_sub_block.foliage_block_hash -# new_fbh_sig = bt.get_plot_signature( -# new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key -# ) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_TRANSACTIONS_FILTER_HASH -# return -# await empty_blockchain.receive_block(blocks[-1]) -# -# @pytest.mark.asyncio -# async def test_bad_timestamp(self, empty_blockchain): -# # 26 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if blocks[-1].foliage_block is not None: -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_block.timestamp", blocks[0].foliage_block.timestamp - 10 -# ) -# block_bad: FullBlock = recursive_replace( -# block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() -# ) -# new_m = block_bad.foliage_sub_block.foliage_block_hash -# new_fbh_sig = bt.get_plot_signature( -# new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key -# ) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.TIMESTAMP_TOO_FAR_IN_PAST -# -# block_bad: FullBlock = recursive_replace( -# blocks[-1], "foliage_block.timestamp", blocks[0].foliage_block.timestamp + 10000000 -# ) -# block_bad: FullBlock = recursive_replace( -# block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() -# ) -# new_m = block_bad.foliage_sub_block.foliage_block_hash -# new_fbh_sig = bt.get_plot_signature( -# new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key -# ) -# block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.TIMESTAMP_TOO_FAR_IN_FUTURE -# return -# await empty_blockchain.receive_block(blocks[-1]) -# -# @pytest.mark.asyncio -# async def test_sub_block_height(self, empty_blockchain): -# # 27 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.sub_block_height", 2) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_HEIGHT -# -# @pytest.mark.asyncio -# async def test_sub_block_height_genesis(self, empty_blockchain): -# # 27 -# blocks = bt.get_consecutive_blocks(1) -# block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.sub_block_height", 1) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREV_BLOCK_HASH -# -# @pytest.mark.asyncio -# async def test_weight(self, empty_blockchain): -# # 28 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.weight", 22131) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_WEIGHT -# -# @pytest.mark.asyncio -# async def test_weight_genesis(self, empty_blockchain): -# # 28 -# blocks = bt.get_consecutive_blocks(1) -# block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.weight", 0) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_WEIGHT -# -# @pytest.mark.asyncio -# async def test_bad_cc_ip_vdf(self, empty_blockchain): -# # 29 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.challenge_chain_ip_vdf.challenge", std_hash(b"1") -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.challenge_chain_ip_vdf.output", -# ClassgroupElement(int512(10), int512(2)), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.challenge_chain_ip_vdf.number_of_iterations", -# uint64(1111111111111), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "challenge_chain_ip_proof", -# VDFProof(uint8(0), std_hash(b"")), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF -# -# @pytest.mark.asyncio -# async def test_bad_rc_ip_vdf(self, empty_blockchain): -# # 30 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.reward_chain_ip_vdf.challenge", std_hash(b"1") -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.reward_chain_ip_vdf.output", -# ClassgroupElement(int512(10), int512(2)), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.reward_chain_ip_vdf.number_of_iterations", -# uint64(1111111111111), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_ip_proof", -# VDFProof(uint8(0), std_hash(b"")), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF -# -# @pytest.mark.asyncio -# async def test_bad_icc_ip_vdf(self, empty_blockchain): -# # 31 -# blocks = bt.get_consecutive_blocks(1) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# block_bad = recursive_replace( -# blocks[-1], "reward_chain_sub_block.infused_challenge_chain_ip_vdf.challenge", std_hash(b"1") -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.infused_challenge_chain_ip_vdf.output", -# ClassgroupElement(int512(10), int512(2)), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "reward_chain_sub_block.infused_challenge_chain_ip_vdf.number_of_iterations", -# uint64(1111111111111), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF -# block_bad = recursive_replace( -# blocks[-1], -# "infused_challenge_chain_ip_proof", -# VDFProof(uint8(0), std_hash(b"")), -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF -# -# @pytest.mark.asyncio -# async def test_reward_block_hash(self, empty_blockchain): -# # 32 -# blocks = bt.get_consecutive_blocks(2) -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_sub_block.reward_block_hash", std_hash(b"")) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_REWARD_BLOCK_HASH -# -# @pytest.mark.asyncio -# async def test_reward_block_hash_2(self, empty_blockchain): -# # 33 -# blocks = bt.get_consecutive_blocks(1) -# block_bad: FullBlock = recursive_replace(blocks[0], "reward_chain_sub_block.is_block", False) -# block_bad: FullBlock = recursive_replace( -# block_bad, "foliage_sub_block.reward_block_hash", block_bad.reward_chain_sub_block.get_hash() -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_FOLIAGE_BLOCK_PRESENCE -# assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK -# -# # Test one which should not be a block -# while True: -# blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) -# if not blocks[-1].is_block(): -# block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.is_block", True) -# block_bad: FullBlock = recursive_replace( -# block_bad, "foliage_sub_block.reward_block_hash", block_bad.reward_chain_sub_block.get_hash() -# ) -# assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_FOLIAGE_BLOCK_PRESENCE -# return -# assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK -# -# -# class TestBodyValidation: -# @pytest.mark.asyncio -# async def test_not_block_but_has_data(self, empty_blockchain): -# # TODO -# pass -# -# -# class TestReorgs: -# @pytest.mark.asyncio -# async def test_basic_reorg(self, empty_blockchain): -# b = empty_blockchain -# blocks = bt.get_consecutive_blocks(15) -# -# for block in blocks: -# assert (await b.receive_block(block))[0] == ReceiveBlockResult.NEW_PEAK -# assert b.get_peak().sub_block_height == 14 -# -# blocks_reorg_chain = bt.get_consecutive_blocks(7, blocks[:10], seed=b"2") -# for reorg_block in blocks_reorg_chain: -# result, error_code, fork_height = await b.receive_block(reorg_block) -# if reorg_block.sub_block_height < 10: -# assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK -# elif reorg_block.sub_block_height < 14: -# assert result == ReceiveBlockResult.ADDED_AS_ORPHAN -# elif reorg_block.sub_block_height >= 15: -# assert result == ReceiveBlockResult.NEW_PEAK -# assert error_code is None -# assert b.get_peak().sub_block_height == 16 -# -# @pytest.mark.asyncio -# async def test_long_reorg(self, empty_blockchain): -# # Reorg longer than a difficulty adjustment -# # Also tests higher weight chain but lower height -# b = empty_blockchain -# num_blocks_chain_1 = 3 * test_constants.EPOCH_SUB_BLOCKS + test_constants.MAX_SUB_SLOT_SUB_BLOCKS + 10 -# num_blocks_chain_2_start = test_constants.EPOCH_SUB_BLOCKS - 20 -# num_blocks_chain_2 = 3 * test_constants.EPOCH_SUB_BLOCKS + test_constants.MAX_SUB_SLOT_SUB_BLOCKS + 8 -# -# blocks = bt.get_consecutive_blocks(num_blocks_chain_1) -# -# for block in blocks: -# assert (await b.receive_block(block))[0] == ReceiveBlockResult.NEW_PEAK -# chain_1_height = b.get_peak().sub_block_height -# chain_1_weight = b.get_peak().weight -# assert chain_1_height == (num_blocks_chain_1 - 1) -# -# # These blocks will have less time between them (timestamp) and therefore will make difficulty go up -# # This means that the weight will grow faster, and we can get a heavier chain with lower height -# blocks_reorg_chain = bt.get_consecutive_blocks( -# num_blocks_chain_2 - num_blocks_chain_2_start, -# blocks[:num_blocks_chain_2_start], -# seed=b"2", -# time_per_sub_block=8, -# ) -# found_orphan = False -# for reorg_block in blocks_reorg_chain: -# result, error_code, fork_height = await b.receive_block(reorg_block) -# if reorg_block.sub_block_height < num_blocks_chain_2_start: -# assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK -# if reorg_block.weight <= chain_1_weight: -# if result == ReceiveBlockResult.ADDED_AS_ORPHAN: -# found_orphan = True -# assert error_code is None -# assert result == ReceiveBlockResult.ADDED_AS_ORPHAN or result == ReceiveBlockResult.ALREADY_HAVE_BLOCK -# elif reorg_block.weight > chain_1_weight: -# assert reorg_block.sub_block_height < chain_1_height -# assert result == ReceiveBlockResult.NEW_PEAK -# assert error_code is None -# assert found_orphan -# -# assert b.get_peak().weight > chain_1_weight -# assert b.get_peak().sub_block_height < chain_1_height -# -# @pytest.mark.asyncio -# async def test_reorg_from_genesis(self, empty_blockchain): -# b = empty_blockchain -# blocks = bt.get_consecutive_blocks(15) -# -# for block in blocks: -# assert (await b.receive_block(block))[0] == ReceiveBlockResult.NEW_PEAK -# assert b.get_peak().sub_block_height == 14 -# -# # Reorg to alternate chain that is 1 height longer -# found_orphan = False -# blocks_reorg_chain = bt.get_consecutive_blocks(16, [], seed=b"2") -# for reorg_block in blocks_reorg_chain: -# result, error_code, fork_height = await b.receive_block(reorg_block) -# print(reorg_block.sub_block_height, result) -# if reorg_block.sub_block_height < 14: -# if result == ReceiveBlockResult.ADDED_AS_ORPHAN: -# found_orphan = True -# assert result == ReceiveBlockResult.ADDED_AS_ORPHAN or result == ReceiveBlockResult.ALREADY_HAVE_BLOCK -# elif reorg_block.sub_block_height >= 15: -# assert result == ReceiveBlockResult.NEW_PEAK -# assert error_code is None -# -# # Back to original chain -# blocks_reorg_chain_2 = bt.get_consecutive_blocks(3, blocks, seed=b"3") -# -# result, error_code, fork_height = await b.receive_block(blocks_reorg_chain_2[-3]) -# assert result == ReceiveBlockResult.ADDED_AS_ORPHAN -# -# result, error_code, fork_height = await b.receive_block(blocks_reorg_chain_2[-2]) -# assert result == ReceiveBlockResult.NEW_PEAK -# -# result, error_code, fork_height = await b.receive_block(blocks_reorg_chain_2[-1]) -# assert result == ReceiveBlockResult.NEW_PEAK -# assert found_orphan -# assert b.get_peak().sub_block_height == 17 + @pytest.mark.asyncio + async def test_overflow_genesis(self, empty_blockchain): + genesis = bt.get_consecutive_blocks(1, force_overflow=True)[0] + result, err, _ = await empty_blockchain.receive_block(genesis, False) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_genesis_empty_slots(self, empty_blockchain): + genesis = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=3)[0] + result, err, _ = await empty_blockchain.receive_block(genesis, False) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_overflow_genesis_empty_slots(self, empty_blockchain): + genesis = bt.get_consecutive_blocks(1, force_overflow=True, skip_slots=3)[0] + result, err, _ = await empty_blockchain.receive_block(genesis, False) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_genesis_validate_1(self, empty_blockchain): + genesis = bt.get_consecutive_blocks(1, force_overflow=False)[0] + bad_prev = bytes([1] * 32) + genesis = recursive_replace(genesis, "foliage_sub_block.prev_sub_block_hash", bad_prev) + result, err, _ = await empty_blockchain.receive_block(genesis, False) + assert err == Err.INVALID_PREV_BLOCK_HASH + + +class TestBlockHeaderValidation: + @pytest.mark.asyncio + async def test_long_chain(self, empty_blockchain, default_1000_blocks): + blocks = default_1000_blocks + for block in blocks: + if ( + len(block.finished_sub_slots) > 0 + and block.finished_sub_slots[0].challenge_chain.subepoch_summary_hash is not None + ): + # Sub/Epoch. Try using a bad ssi and difficulty to test 2m and 2n + new_finished_ss = recursive_replace( + block.finished_sub_slots[0], + "challenge_chain.new_sub_slot_iters", + uint64(10000000), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_NEW_SUB_SLOT_ITERS + new_finished_ss_2 = recursive_replace( + block.finished_sub_slots[0], + "challenge_chain.new_difficulty", + uint64(10000000), + ) + block_bad_2 = recursive_replace( + block, "finished_sub_slots", [new_finished_ss_2] + block.finished_sub_slots[1:] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_2) + assert err == Err.INVALID_NEW_DIFFICULTY + + # 3c + new_finished_ss_3: EndOfSubSlotBundle = recursive_replace( + block.finished_sub_slots[0], + "challenge_chain.subepoch_summary_hash", + bytes([0] * 32), + ) + new_finished_ss_3 = recursive_replace( + new_finished_ss_3, + "reward_chain.challenge_chain_sub_slot_hash", + new_finished_ss_3.challenge_chain.get_hash(), + ) + block_bad_3 = recursive_replace( + block, "finished_sub_slots", [new_finished_ss_3] + block.finished_sub_slots[1:] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_3) + assert err == Err.INVALID_SUB_EPOCH_SUMMARY + + # 3d + new_finished_ss_4 = recursive_replace( + block.finished_sub_slots[0], + "challenge_chain.subepoch_summary_hash", + None, + ) + new_finished_ss_4 = recursive_replace( + new_finished_ss_4, + "reward_chain.challenge_chain_sub_slot_hash", + new_finished_ss_4.challenge_chain.get_hash(), + ) + block_bad_4 = recursive_replace( + block, "finished_sub_slots", [new_finished_ss_4] + block.finished_sub_slots[1:] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_4) + assert err == Err.INVALID_SUB_EPOCH_SUMMARY or err == Err.INVALID_NEW_SUB_SLOT_ITERS + + result, err, _ = await empty_blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + log.info( + f"Added block {block.sub_block_height} total iters {block.total_iters} " + f"new slot? {len(block.finished_sub_slots)}" + ) + assert empty_blockchain.get_peak().sub_block_height == len(blocks) - 1 + + @pytest.mark.asyncio + async def test_unfinished_blocks(self, empty_blockchain): + blockchain = empty_blockchain + blocks = bt.get_consecutive_blocks(2) + for block in blocks[:-1]: + result, err, _ = await blockchain.receive_block(block) + assert result == ReceiveBlockResult.NEW_PEAK + block = blocks[-1] + unf = UnfinishedBlock( + block.finished_sub_slots, + block.reward_chain_sub_block.get_unfinished(), + block.challenge_chain_sp_proof, + block.reward_chain_sp_proof, + block.foliage_sub_block, + block.foliage_block, + block.transactions_info, + block.transactions_generator, + ) + _, err = await blockchain.validate_unfinished_block(unf, False) + assert err is None + result, err, _ = await blockchain.receive_block(block) + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, force_overflow=True) + block = blocks[-1] + unf = UnfinishedBlock( + block.finished_sub_slots, + block.reward_chain_sub_block.get_unfinished(), + block.challenge_chain_sp_proof, + block.reward_chain_sp_proof, + block.foliage_sub_block, + block.foliage_block, + block.transactions_info, + block.transactions_generator, + ) + _, err = await blockchain.validate_unfinished_block(unf, False) + assert err is None + + @pytest.mark.asyncio + async def test_empty_genesis(self, empty_blockchain): + blockchain = empty_blockchain + for block in bt.get_consecutive_blocks(2, skip_slots=3): + result, err, _ = await blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_empty_slots_non_genesis(self, empty_blockchain): + blockchain = empty_blockchain + blocks = bt.get_consecutive_blocks(10) + for block in blocks: + result, err, _ = await blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + blocks = bt.get_consecutive_blocks(10, skip_slots=2, block_list_input=blocks) + for block in blocks[10:]: + result, err, _ = await blockchain.receive_block(block) + assert err is None + assert blockchain.get_peak().sub_block_height == 19 + + @pytest.mark.asyncio + async def test_one_sb_per_slot(self, empty_blockchain): + blockchain = empty_blockchain + num_blocks = 20 + blocks = [] + for i in range(num_blocks): + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) + result, err, _ = await blockchain.receive_block(blocks[-1]) + assert result == ReceiveBlockResult.NEW_PEAK + assert blockchain.get_peak().sub_block_height == num_blocks - 1 + + @pytest.mark.asyncio + async def test_one_sb_per_two_slots(self, empty_blockchain): + blockchain = empty_blockchain + num_blocks = 20 + blocks = [] + for i in range(num_blocks): # Same thing, but 2 sub-slots per sub-block + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=2) + result, err, _ = await blockchain.receive_block(blocks[-1]) + assert result == ReceiveBlockResult.NEW_PEAK + assert blockchain.get_peak().sub_block_height == num_blocks - 1 + + @pytest.mark.asyncio + async def test_one_sb_per_five_slots(self, empty_blockchain): + blockchain = empty_blockchain + num_blocks = 10 + blocks = [] + for i in range(num_blocks): # Same thing, but 5 sub-slots per sub-block + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=5) + result, err, _ = await blockchain.receive_block(blocks[-1]) + assert result == ReceiveBlockResult.NEW_PEAK + assert blockchain.get_peak().sub_block_height == num_blocks - 1 + + @pytest.mark.asyncio + async def test_basic_chain_overflow(self, empty_blockchain): + blocks = bt.get_consecutive_blocks(5, force_overflow=True) + for block in blocks: + result, err, _ = await empty_blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + assert empty_blockchain.get_peak().sub_block_height == len(blocks) - 1 + + @pytest.mark.asyncio + async def test_one_sb_per_two_slots_force_overflow(self, empty_blockchain): + blockchain = empty_blockchain + num_blocks = 10 + blocks = [] + for i in range(num_blocks): + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=2, force_overflow=True) + result, err, _ = await blockchain.receive_block(blocks[-1]) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + assert blockchain.get_peak().sub_block_height == num_blocks - 1 + + @pytest.mark.asyncio + async def test_invalid_prev(self, empty_blockchain): + # 1 + blocks = bt.get_consecutive_blocks(2, force_overflow=False) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_1_bad = recursive_replace(blocks[-1], "foliage_sub_block.prev_sub_block_hash", bytes([0] * 32)) + + result, err, _ = await empty_blockchain.receive_block(block_1_bad) + assert result == ReceiveBlockResult.DISCONNECTED_BLOCK + + @pytest.mark.asyncio + async def test_invalid_pospace(self, empty_blockchain): + # 2 + blocks = bt.get_consecutive_blocks(2, force_overflow=False) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_1_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.proof_of_space.proof", bytes([0] * 32)) + + result, err, _ = await empty_blockchain.receive_block(block_1_bad) + assert result == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.INVALID_POSPACE + + @pytest.mark.asyncio + async def test_invalid_sub_slot_challenge_hash_genesis(self, empty_blockchain): + # 2a + blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1) + new_finished_ss = recursive_replace( + blocks[0].finished_sub_slots[0], + "challenge_chain.challenge_chain_end_of_slot_vdf.challenge", + bytes([2] * 32), + ) + block_0_bad = recursive_replace( + blocks[0], "finished_sub_slots", [new_finished_ss] + blocks[0].finished_sub_slots[1:] + ) + + result, err, _ = await empty_blockchain.receive_block(block_0_bad) + assert result == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH + + @pytest.mark.asyncio + async def test_invalid_sub_slot_challenge_hash_non_genesis(self, empty_blockchain): + # 2b + blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=0) + blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1, block_list_input=blocks) + new_finished_ss = recursive_replace( + blocks[1].finished_sub_slots[0], + "challenge_chain.challenge_chain_end_of_slot_vdf.challenge", + bytes([2] * 32), + ) + block_1_bad = recursive_replace( + blocks[1], "finished_sub_slots", [new_finished_ss] + blocks[1].finished_sub_slots[1:] + ) + + _, _, _ = await empty_blockchain.receive_block(blocks[0]) + result, err, _ = await empty_blockchain.receive_block(block_1_bad) + assert result == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH + + @pytest.mark.asyncio + async def test_invalid_sub_slot_challenge_hash_empty_ss(self, empty_blockchain): + # 2c + blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=0) + blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=2, block_list_input=blocks) + new_finished_ss = recursive_replace( + blocks[1].finished_sub_slots[-1], + "challenge_chain.challenge_chain_end_of_slot_vdf.challenge", + bytes([2] * 32), + ) + block_1_bad = recursive_replace( + blocks[1], "finished_sub_slots", blocks[1].finished_sub_slots[:-1] + [new_finished_ss] + ) + + _, _, _ = await empty_blockchain.receive_block(blocks[0]) + result, err, _ = await empty_blockchain.receive_block(block_1_bad) + assert result == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH + + @pytest.mark.asyncio + async def test_genesis_no_icc(self, empty_blockchain): + # 2d + blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1) + new_finished_ss = recursive_replace( + blocks[0].finished_sub_slots[0], + "infused_challenge_chain", + InfusedChallengeChainSubSlot( + VDFInfo( + bytes([0] * 32), + uint64(1200), + ClassgroupElement.get_default_element(), + ) + ), + ) + block_0_bad = recursive_replace( + blocks[0], "finished_sub_slots", [new_finished_ss] + blocks[0].finished_sub_slots[1:] + ) + + result, err, _ = await empty_blockchain.receive_block(block_0_bad) + assert result == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.SHOULD_NOT_HAVE_ICC + + @pytest.mark.asyncio + async def test_invalid_icc_sub_slot_vdf(self, empty_blockchain): + blocks = bt.get_consecutive_blocks(10) + for block in blocks: + if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is not None: + # Bad iters + new_finished_ss = recursive_replace( + block.finished_sub_slots[-1], + "infused_challenge_chain", + InfusedChallengeChainSubSlot( + replace( + block.finished_sub_slots[ + -1 + ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf, + number_of_iterations=10000000, + ) + ), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_ICC_EOS_VDF + + # Bad output + new_finished_ss_2 = recursive_replace( + block.finished_sub_slots[-1], + "infused_challenge_chain", + InfusedChallengeChainSubSlot( + replace( + block.finished_sub_slots[ + -1 + ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf, + output=ClassgroupElement.get_default_element(), + ) + ), + ) + block_bad_2 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_2) + assert err == Err.INVALID_ICC_EOS_VDF + + # Bad challenge hash + new_finished_ss_3 = recursive_replace( + block.finished_sub_slots[-1], + "infused_challenge_chain", + InfusedChallengeChainSubSlot( + replace( + block.finished_sub_slots[ + -1 + ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf, + challenge=bytes([0] * 32), + ) + ), + ) + block_bad_3 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_3) + assert err == Err.INVALID_ICC_EOS_VDF + + # Bad proof + new_finished_ss_5 = recursive_replace( + block.finished_sub_slots[-1], + "proofs.infused_challenge_chain_slot_proof", + VDFProof(uint8(0), b"1239819023890"), + ) + block_bad_5 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_5) + assert err == Err.INVALID_ICC_EOS_VDF + + result, err, _ = await empty_blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_invalid_icc_into_cc(self, empty_blockchain): + blockchain = empty_blockchain + blocks = bt.get_consecutive_blocks(1) + assert (await blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + case_1, case_2 = False, False + while not case_1 or not case_2: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) + block = blocks[-1] + if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is not None: + if ( + block.finished_sub_slots[-1].reward_chain.deficit + == test_constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK + ): + # 2g + case_1 = True + new_finished_ss = recursive_replace( + block.finished_sub_slots[-1], + "challenge_chain", + replace( + block.finished_sub_slots[-1].challenge_chain, + infused_challenge_chain_sub_slot_hash=bytes([1] * 32), + ), + ) + else: + # 2h + case_2 = True + new_finished_ss = recursive_replace( + block.finished_sub_slots[-1], + "challenge_chain", + replace( + block.finished_sub_slots[-1].challenge_chain, + infused_challenge_chain_sub_slot_hash=block.finished_sub_slots[ + -1 + ].infused_challenge_chain.get_hash(), + ), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] + ) + result, err, _ = await blockchain.receive_block(block_bad) + assert err == Err.INVALID_ICC_HASH_CC + + # 2i + new_finished_ss_bad_rc = recursive_replace( + block.finished_sub_slots[-1], + "reward_chain", + replace(block.finished_sub_slots[-1].reward_chain, infused_challenge_chain_sub_slot_hash=None), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc] + ) + result, err, _ = await blockchain.receive_block(block_bad) + assert err == Err.INVALID_ICC_HASH_RC + elif len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is None: + # 2j + new_finished_ss_bad_cc = recursive_replace( + block.finished_sub_slots[-1], + "challenge_chain", + replace( + block.finished_sub_slots[-1].challenge_chain, + infused_challenge_chain_sub_slot_hash=bytes([1] * 32), + ), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_cc] + ) + result, err, _ = await blockchain.receive_block(block_bad) + assert err == Err.INVALID_ICC_HASH_CC + + # 2k + new_finished_ss_bad_rc = recursive_replace( + block.finished_sub_slots[-1], + "reward_chain", + replace( + block.finished_sub_slots[-1].reward_chain, infused_challenge_chain_sub_slot_hash=bytes([1] * 32) + ), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc] + ) + result, err, _ = await blockchain.receive_block(block_bad) + assert err == Err.INVALID_ICC_HASH_RC + + # Finally, add the block properly + result, err, _ = await blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_empty_slot_no_ses(self, empty_blockchain): + # 2l + blockchain = empty_blockchain + blocks = bt.get_consecutive_blocks(1) + assert (await blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=4) + + new_finished_ss = recursive_replace( + blocks[-1].finished_sub_slots[-1], + "challenge_chain", + replace(blocks[-1].finished_sub_slots[-1].challenge_chain, subepoch_summary_hash=std_hash(b"0")), + ) + block_bad = recursive_replace( + blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss] + ) + result, err, _ = await blockchain.receive_block(block_bad) + assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH + + @pytest.mark.asyncio + async def test_empty_sub_slots_epoch(self, empty_blockchain): + # 2m + # Tests adding an empty sub slot after the sub-epoch / epoch. + # Also tests overflow block in epoch + blocks_base = bt.get_consecutive_blocks(test_constants.EPOCH_SUB_BLOCKS) + blocks_1 = bt.get_consecutive_blocks(1, block_list_input=blocks_base, force_overflow=True) + blocks_2 = bt.get_consecutive_blocks(1, skip_slots=1, block_list_input=blocks_base, force_overflow=True) + blocks_3 = bt.get_consecutive_blocks(1, skip_slots=2, block_list_input=blocks_base, force_overflow=True) + blocks_4 = bt.get_consecutive_blocks(1, block_list_input=blocks_base) + for block in blocks_base: + result, err, _ = await empty_blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + for block in [blocks_1[-1], blocks_2[-1], blocks_3[-1], blocks_4[-1]]: + result, err, _ = await empty_blockchain.receive_block(block) + assert err is None + + @pytest.mark.asyncio + async def test_wrong_cc_hash_rc(self, empty_blockchain): + # 2o + blockchain = empty_blockchain + blocks = bt.get_consecutive_blocks(1, skip_slots=1) + blocks = bt.get_consecutive_blocks(1, skip_slots=1, block_list_input=blocks) + assert (await blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + new_finished_ss = recursive_replace( + blocks[-1].finished_sub_slots[-1], + "reward_chain", + replace(blocks[-1].finished_sub_slots[-1].reward_chain, challenge_chain_sub_slot_hash=bytes([3] * 32)), + ) + block_1_bad = recursive_replace( + blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss] + ) + + result, err, _ = await blockchain.receive_block(block_1_bad) + assert result == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.INVALID_CHALLENGE_SLOT_HASH_RC + + @pytest.mark.asyncio + async def test_invalid_cc_sub_slot_vdf(self, empty_blockchain): + # 2q + blocks = bt.get_consecutive_blocks(10) + for block in blocks: + if len(block.finished_sub_slots): + # Bad iters + new_finished_ss = recursive_replace( + block.finished_sub_slots[-1], + "challenge_chain", + recursive_replace( + block.finished_sub_slots[-1].challenge_chain, + "challenge_chain_end_of_slot_vdf.number_of_iterations", + uint64(10000000), + ), + ) + new_finished_ss = recursive_replace( + new_finished_ss, + "reward_chain.challenge_chain_sub_slot_hash", + new_finished_ss.challenge_chain.get_hash(), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_CC_EOS_VDF + + # Bad output + new_finished_ss_2 = recursive_replace( + block.finished_sub_slots[-1], + "challenge_chain", + recursive_replace( + block.finished_sub_slots[-1].challenge_chain, + "challenge_chain_end_of_slot_vdf.output", + ClassgroupElement.get_default_element(), + ), + ) + + new_finished_ss_2 = recursive_replace( + new_finished_ss_2, + "reward_chain.challenge_chain_sub_slot_hash", + new_finished_ss_2.challenge_chain.get_hash(), + ) + block_bad_2 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_2) + assert err == Err.INVALID_CC_EOS_VDF + + # Bad challenge hash + new_finished_ss_3 = recursive_replace( + block.finished_sub_slots[-1], + "challenge_chain", + recursive_replace( + block.finished_sub_slots[-1].challenge_chain, + "challenge_chain_end_of_slot_vdf.challenge", + bytes([1] * 32), + ), + ) + + new_finished_ss_3 = recursive_replace( + new_finished_ss_3, + "reward_chain.challenge_chain_sub_slot_hash", + new_finished_ss_3.challenge_chain.get_hash(), + ) + block_bad_3 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_3) + assert err == Err.INVALID_CC_EOS_VDF or err == Err.INVALID_PREV_CHALLENGE_SLOT_HASH + + # Bad proof + new_finished_ss_5 = recursive_replace( + block.finished_sub_slots[-1], + "proofs.challenge_chain_slot_proof", + VDFProof(uint8(0), b"1239819023890"), + ) + block_bad_5 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_5) + assert err == Err.INVALID_CC_EOS_VDF + + result, err, _ = await empty_blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_invalid_rc_sub_slot_vdf(self, empty_blockchain): + # 2p + blocks = bt.get_consecutive_blocks(10) + for block in blocks: + if len(block.finished_sub_slots): + # Bad iters + new_finished_ss = recursive_replace( + block.finished_sub_slots[-1], + "reward_chain", + recursive_replace( + block.finished_sub_slots[-1].reward_chain, + "end_of_slot_vdf.number_of_iterations", + uint64(10000000), + ), + ) + block_bad = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_RC_EOS_VDF + + # Bad output + new_finished_ss_2 = recursive_replace( + block.finished_sub_slots[-1], + "reward_chain", + recursive_replace( + block.finished_sub_slots[-1].reward_chain, + "end_of_slot_vdf.output", + ClassgroupElement.get_default_element(), + ), + ) + block_bad_2 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_2) + assert err == Err.INVALID_RC_EOS_VDF + + # Bad challenge hash + new_finished_ss_3 = recursive_replace( + block.finished_sub_slots[-1], + "reward_chain", + recursive_replace( + block.finished_sub_slots[-1].reward_chain, + "end_of_slot_vdf.challenge", + bytes([1] * 32), + ), + ) + block_bad_3 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_3) + assert err == Err.INVALID_RC_EOS_VDF + + # Bad proof + new_finished_ss_5 = recursive_replace( + block.finished_sub_slots[-1], + "proofs.reward_chain_slot_proof", + VDFProof(uint8(0), b"1239819023890"), + ) + block_bad_5 = recursive_replace( + block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad_5) + assert err == Err.INVALID_RC_EOS_VDF + + result, err, _ = await empty_blockchain.receive_block(block) + assert err is None + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_genesis_bad_deficit(self, empty_blockchain): + # 2r + block = bt.get_consecutive_blocks(1, skip_slots=2)[0] + new_finished_ss = recursive_replace( + block.finished_sub_slots[-1], + "reward_chain", + recursive_replace( + block.finished_sub_slots[-1].reward_chain, + "deficit", + test_constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1, + ), + ) + block_bad = recursive_replace(block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_DEFICIT + + @pytest.mark.asyncio + async def test_reset_deficit(self, empty_blockchain): + # 2s, 2t + blockchain = empty_blockchain + blocks = bt.get_consecutive_blocks(2) + await empty_blockchain.receive_block(blocks[0]) + await empty_blockchain.receive_block(blocks[1]) + case_1, case_2 = False, False + while not case_1 or not case_2: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1) + if len(blocks[-1].finished_sub_slots) > 0: + new_finished_ss = recursive_replace( + blocks[-1].finished_sub_slots[-1], + "reward_chain", + recursive_replace( + blocks[-1].finished_sub_slots[-1].reward_chain, + "deficit", + uint8(0), + ), + ) + if blockchain.sub_blocks[blocks[-2].header_hash].deficit == 0: + case_1 = True + else: + case_2 = True + + block_bad = recursive_replace( + blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_DEFICIT or err == Err.INVALID_ICC_HASH_CC + + result, err, _ = await empty_blockchain.receive_block(blocks[-1]) + assert result == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_genesis_has_ses(self, empty_blockchain): + # 3a + block = bt.get_consecutive_blocks(1, skip_slots=1)[0] + new_finished_ss = recursive_replace( + block.finished_sub_slots[0], + "challenge_chain", + recursive_replace( + block.finished_sub_slots[0].challenge_chain, + "subepoch_summary_hash", + bytes([0] * 32), + ), + ) + + new_finished_ss = recursive_replace( + new_finished_ss, + "reward_chain", + replace( + new_finished_ss.reward_chain, challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash() + ), + ) + block_bad = recursive_replace(block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:]) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH + + @pytest.mark.asyncio + async def test_no_ses_if_no_se(self, empty_blockchain): + # 3b + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if len(blocks[-1].finished_sub_slots) > 0: + new_finished_ss: EndOfSubSlotBundle = recursive_replace( + blocks[-1].finished_sub_slots[0], + "challenge_chain", + recursive_replace( + blocks[-1].finished_sub_slots[0].challenge_chain, + "subepoch_summary_hash", + bytes([0] * 32), + ), + ) + + new_finished_ss = recursive_replace( + new_finished_ss, + "reward_chain", + replace( + new_finished_ss.reward_chain, + challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash(), + ), + ) + block_bad = recursive_replace( + blocks[-1], "finished_sub_slots", [new_finished_ss] + blocks[-1].finished_sub_slots[1:] + ) + result, err, _ = await empty_blockchain.receive_block(block_bad) + assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH + return + await empty_blockchain.receive_block(blocks[-1]) + + @pytest.mark.asyncio + async def test_too_many_sub_blocks(self, empty_blockchain): + # 4: TODO + pass + + @pytest.mark.asyncio + async def test_bad_pos(self, empty_blockchain): + # 5 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.proof_of_space.challenge", std_hash(b"")) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE + + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.proof_of_space.pool_contract_puzzle_hash", std_hash(b"") + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE + + block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.proof_of_space.pool_public_key", None) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE + + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.proof_of_space.plot_public_key", + AugSchemeMPL.key_gen(std_hash(b"1231n")).get_g1(), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.proof_of_space.size", + 32, + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.proof_of_space.proof", + bytes([1] * int(blocks[-1].reward_chain_sub_block.proof_of_space.size * 64 / 8)), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POSPACE + + # TODO: test not passing the plot filter + + @pytest.mark.asyncio + async def test_bad_signage_point_index(self, empty_blockchain): + # 6 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + with pytest.raises(ValueError): + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.signage_point_index", test_constants.NUM_SPS_SUB_SLOT + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_SP_INDEX + with pytest.raises(ValueError): + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.signage_point_index", test_constants.NUM_SPS_SUB_SLOT + 1 + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_SP_INDEX + + @pytest.mark.asyncio + async def test_sp_0_no_sp(self, empty_blockchain): + # 7 + blocks = [] + case_1, case_2 = False, False + while not case_1 or not case_2: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].reward_chain_sub_block.signage_point_index == 0: + case_1 = True + block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.signage_point_index", uint8(1)) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_SP_INDEX + else: + case_2 = True + block_bad = recursive_replace(blocks[-1], "reward_chain_sub_block.signage_point_index", uint8(0)) + error_code = (await empty_blockchain.receive_block(block_bad))[1] + assert error_code == Err.INVALID_SP_INDEX or error_code == Err.INVALID_POSPACE + assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_epoch_overflows(self, empty_blockchain): + # 9. TODO. This is hard to test because it requires modifying the block tools to make these special blocks + pass + + @pytest.mark.asyncio + async def test_bad_total_iters(self, empty_blockchain): + # 10 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.total_iters", blocks[-1].reward_chain_sub_block.total_iters + 1 + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_TOTAL_ITERS + + @pytest.mark.asyncio + async def test_bad_rc_sp_vdf(self, empty_blockchain): + # 11 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].reward_chain_sub_block.signage_point_index != 0: + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.reward_chain_sp_vdf.challenge", std_hash(b"1") + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.reward_chain_sp_vdf.output", + ClassgroupElement(int512(10), int512(2)), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.reward_chain_sp_vdf.number_of_iterations", + uint64(1111111111111), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sp_proof", + VDFProof(uint8(0), std_hash(b"")), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF + return + assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_bad_rc_sp_sig(self, empty_blockchain): + # 12 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.reward_chain_sp_signature", G2Element.generator() + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SIGNATURE + + @pytest.mark.asyncio + async def test_bad_cc_sp_vdf(self, empty_blockchain): + # 13. Note: does not validate fully due to proof of space being validated first + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].reward_chain_sub_block.signage_point_index != 0: + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.challenge_chain_sp_vdf.challenge", std_hash(b"1") + ) + assert (await empty_blockchain.receive_block(block_bad))[0] == ReceiveBlockResult.INVALID_BLOCK + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.challenge_chain_sp_vdf.output", + ClassgroupElement(int512(10), int512(2)), + ) + assert (await empty_blockchain.receive_block(block_bad))[0] == ReceiveBlockResult.INVALID_BLOCK + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.challenge_chain_sp_vdf.number_of_iterations", + uint64(1111111111111), + ) + assert (await empty_blockchain.receive_block(block_bad))[0] == ReceiveBlockResult.INVALID_BLOCK + block_bad = recursive_replace( + blocks[-1], + "challenge_chain_sp_proof", + VDFProof(uint8(0), std_hash(b"")), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_SP_VDF + return + assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_bad_cc_sp_sig(self, empty_blockchain): + # 14 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.challenge_chain_sp_signature", G2Element.generator() + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_SIGNATURE + + @pytest.mark.asyncio + async def test_is_block(self, empty_blockchain): + # 15: TODO + pass + + @pytest.mark.asyncio + async def test_bad_foliage_sb_sig(self, empty_blockchain): + # 16 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_sub_block_signature", G2Element.generator() + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PLOT_SIGNATURE + + @pytest.mark.asyncio + async def test_bad_foliage_block_sig(self, empty_blockchain): + # 17 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].foliage_block is not None: + block_bad = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_block_signature", G2Element.generator() + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PLOT_SIGNATURE + return + assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK + + @pytest.mark.asyncio + async def test_unfinished_reward_chain_sb_hash(self, empty_blockchain): + # 18 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_sub_block_data.unfinished_reward_block_hash", std_hash(b"2") + ) + new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() + new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_URSB_HASH + + @pytest.mark.asyncio + async def test_pool_target_height(self, empty_blockchain): + # 19 + blocks = bt.get_consecutive_blocks(3) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + assert (await empty_blockchain.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_sub_block_data.pool_target.max_height", 1 + ) + new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() + new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.OLD_POOL_TARGET + + @pytest.mark.asyncio + async def test_pool_target_pre_farm(self, empty_blockchain): + # 20a + blocks = bt.get_consecutive_blocks(1) + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_sub_block_data.pool_target.puzzle_hash", std_hash(b"12") + ) + new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() + new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREFARM + + @pytest.mark.asyncio + async def test_pool_target_signature(self, empty_blockchain): + # 20b + blocks = bt.get_consecutive_blocks(3) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + assert (await empty_blockchain.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_sub_block_data.pool_signature", G2Element.generator() + ) + new_m = block_bad.foliage_sub_block.foliage_sub_block_data.get_hash() + new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_sub_block_signature", new_fsb_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_POOL_SIGNATURE + + @pytest.mark.asyncio + async def test_foliage_data_presence(self, empty_blockchain): + # 22 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + case_1, case_2 = False, False + while not case_1 or not case_2: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].foliage_block is not None: + case_1 = True + block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_sub_block.foliage_block_hash", None) + else: + case_2 = True + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_block_hash", std_hash(b"") + ) + err_code = (await empty_blockchain.receive_block(block_bad))[1] + assert err_code == Err.INVALID_FOLIAGE_BLOCK_PRESENCE or err_code == Err.INVALID_IS_BLOCK + await empty_blockchain.receive_block(blocks[-1]) + + @pytest.mark.asyncio + async def test_foliage_block_hash(self, empty_blockchain): + # 23 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + case_1, case_2 = False, False + while not case_1 or not case_2: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].foliage_block is not None: + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_sub_block.foliage_block_hash", std_hash(b"2") + ) + + new_m = block_bad.foliage_sub_block.foliage_block_hash + new_fbh_sig = bt.get_plot_signature( + new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key + ) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_FOLIAGE_BLOCK_HASH + return + await empty_blockchain.receive_block(blocks[-1]) + + @pytest.mark.asyncio + async def test_genesis_bad_prev_block(self, empty_blockchain): + # 24a + blocks = bt.get_consecutive_blocks(1) + block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_block.prev_block_hash", std_hash(b"2")) + block_bad: FullBlock = recursive_replace( + block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() + ) + new_m = block_bad.foliage_sub_block.foliage_block_hash + new_fbh_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREV_BLOCK_HASH + + @pytest.mark.asyncio + async def test_bad_prev_block_non_genesis(self, empty_blockchain): + # 24b + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].foliage_block is not None: + block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_block.prev_block_hash", std_hash(b"2")) + block_bad: FullBlock = recursive_replace( + block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() + ) + new_m = block_bad.foliage_sub_block.foliage_block_hash + new_fbh_sig = bt.get_plot_signature( + new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key + ) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREV_BLOCK_HASH + return + await empty_blockchain.receive_block(blocks[-1]) + + @pytest.mark.asyncio + async def test_bad_filter_hash(self, empty_blockchain): + # 25 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].foliage_block is not None: + block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_block.filter_hash", std_hash(b"2")) + block_bad: FullBlock = recursive_replace( + block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() + ) + new_m = block_bad.foliage_sub_block.foliage_block_hash + new_fbh_sig = bt.get_plot_signature( + new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key + ) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_TRANSACTIONS_FILTER_HASH + return + await empty_blockchain.receive_block(blocks[-1]) + + @pytest.mark.asyncio + async def test_bad_timestamp(self, empty_blockchain): + # 26 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if blocks[-1].foliage_block is not None: + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_block.timestamp", blocks[0].foliage_block.timestamp - 10 + ) + block_bad: FullBlock = recursive_replace( + block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() + ) + new_m = block_bad.foliage_sub_block.foliage_block_hash + new_fbh_sig = bt.get_plot_signature( + new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key + ) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.TIMESTAMP_TOO_FAR_IN_PAST + + block_bad: FullBlock = recursive_replace( + blocks[-1], "foliage_block.timestamp", blocks[0].foliage_block.timestamp + 10000000 + ) + block_bad: FullBlock = recursive_replace( + block_bad, "foliage_sub_block.foliage_block_hash", block_bad.foliage_block.get_hash() + ) + new_m = block_bad.foliage_sub_block.foliage_block_hash + new_fbh_sig = bt.get_plot_signature( + new_m, blocks[-1].reward_chain_sub_block.proof_of_space.plot_public_key + ) + block_bad = recursive_replace(block_bad, "foliage_sub_block.foliage_block_signature", new_fbh_sig) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.TIMESTAMP_TOO_FAR_IN_FUTURE + return + await empty_blockchain.receive_block(blocks[-1]) + + @pytest.mark.asyncio + async def test_sub_block_height(self, empty_blockchain): + # 27 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.sub_block_height", 2) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_HEIGHT + + @pytest.mark.asyncio + async def test_sub_block_height_genesis(self, empty_blockchain): + # 27 + blocks = bt.get_consecutive_blocks(1) + block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.sub_block_height", 1) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_PREV_BLOCK_HASH + + @pytest.mark.asyncio + async def test_weight(self, empty_blockchain): + # 28 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.weight", 22131) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_WEIGHT + + @pytest.mark.asyncio + async def test_weight_genesis(self, empty_blockchain): + # 28 + blocks = bt.get_consecutive_blocks(1) + block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.weight", 0) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_WEIGHT + + @pytest.mark.asyncio + async def test_bad_cc_ip_vdf(self, empty_blockchain): + # 29 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.challenge_chain_ip_vdf.challenge", std_hash(b"1") + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.challenge_chain_ip_vdf.output", + ClassgroupElement(int512(10), int512(2)), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.challenge_chain_ip_vdf.number_of_iterations", + uint64(1111111111111), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF + block_bad = recursive_replace( + blocks[-1], + "challenge_chain_ip_proof", + VDFProof(uint8(0), std_hash(b"")), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF + + @pytest.mark.asyncio + async def test_bad_rc_ip_vdf(self, empty_blockchain): + # 30 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.reward_chain_ip_vdf.challenge", std_hash(b"1") + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.reward_chain_ip_vdf.output", + ClassgroupElement(int512(10), int512(2)), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.reward_chain_ip_vdf.number_of_iterations", + uint64(1111111111111), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_ip_proof", + VDFProof(uint8(0), std_hash(b"")), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF + + @pytest.mark.asyncio + async def test_bad_icc_ip_vdf(self, empty_blockchain): + # 31 + blocks = bt.get_consecutive_blocks(1) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + block_bad = recursive_replace( + blocks[-1], "reward_chain_sub_block.infused_challenge_chain_ip_vdf.challenge", std_hash(b"1") + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.infused_challenge_chain_ip_vdf.output", + ClassgroupElement(int512(10), int512(2)), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF + block_bad = recursive_replace( + blocks[-1], + "reward_chain_sub_block.infused_challenge_chain_ip_vdf.number_of_iterations", + uint64(1111111111111), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF + block_bad = recursive_replace( + blocks[-1], + "infused_challenge_chain_ip_proof", + VDFProof(uint8(0), std_hash(b"")), + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF + + @pytest.mark.asyncio + async def test_reward_block_hash(self, empty_blockchain): + # 32 + blocks = bt.get_consecutive_blocks(2) + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + block_bad: FullBlock = recursive_replace(blocks[-1], "foliage_sub_block.reward_block_hash", std_hash(b"")) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_REWARD_BLOCK_HASH + + @pytest.mark.asyncio + async def test_reward_block_hash_2(self, empty_blockchain): + # 33 + blocks = bt.get_consecutive_blocks(1) + block_bad: FullBlock = recursive_replace(blocks[0], "reward_chain_sub_block.is_block", False) + block_bad: FullBlock = recursive_replace( + block_bad, "foliage_sub_block.reward_block_hash", block_bad.reward_chain_sub_block.get_hash() + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_FOLIAGE_BLOCK_PRESENCE + assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK + + # Test one which should not be a block + while True: + blocks = bt.get_consecutive_blocks(1, block_list_input=blocks) + if not blocks[-1].is_block(): + block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_sub_block.is_block", True) + block_bad: FullBlock = recursive_replace( + block_bad, "foliage_sub_block.reward_block_hash", block_bad.reward_chain_sub_block.get_hash() + ) + assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_FOLIAGE_BLOCK_PRESENCE + return + assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK + + +class TestBodyValidation: + @pytest.mark.asyncio + async def test_not_block_but_has_data(self, empty_blockchain): + # TODO + pass + + +class TestReorgs: + @pytest.mark.asyncio + async def test_basic_reorg(self, empty_blockchain): + b = empty_blockchain + blocks = bt.get_consecutive_blocks(15) + + for block in blocks: + assert (await b.receive_block(block))[0] == ReceiveBlockResult.NEW_PEAK + assert b.get_peak().sub_block_height == 14 + + blocks_reorg_chain = bt.get_consecutive_blocks(7, blocks[:10], seed=b"2") + for reorg_block in blocks_reorg_chain: + result, error_code, fork_height = await b.receive_block(reorg_block) + if reorg_block.sub_block_height < 10: + assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK + elif reorg_block.sub_block_height < 14: + assert result == ReceiveBlockResult.ADDED_AS_ORPHAN + elif reorg_block.sub_block_height >= 15: + assert result == ReceiveBlockResult.NEW_PEAK + assert error_code is None + assert b.get_peak().sub_block_height == 16 + + @pytest.mark.asyncio + async def test_long_reorg(self, empty_blockchain): + # Reorg longer than a difficulty adjustment + # Also tests higher weight chain but lower height + b = empty_blockchain + num_blocks_chain_1 = 3 * test_constants.EPOCH_SUB_BLOCKS + test_constants.MAX_SUB_SLOT_SUB_BLOCKS + 10 + num_blocks_chain_2_start = test_constants.EPOCH_SUB_BLOCKS - 20 + num_blocks_chain_2 = 3 * test_constants.EPOCH_SUB_BLOCKS + test_constants.MAX_SUB_SLOT_SUB_BLOCKS + 8 + + blocks = bt.get_consecutive_blocks(num_blocks_chain_1) + + for block in blocks: + assert (await b.receive_block(block))[0] == ReceiveBlockResult.NEW_PEAK + chain_1_height = b.get_peak().sub_block_height + chain_1_weight = b.get_peak().weight + assert chain_1_height == (num_blocks_chain_1 - 1) + + # These blocks will have less time between them (timestamp) and therefore will make difficulty go up + # This means that the weight will grow faster, and we can get a heavier chain with lower height + blocks_reorg_chain = bt.get_consecutive_blocks( + num_blocks_chain_2 - num_blocks_chain_2_start, + blocks[:num_blocks_chain_2_start], + seed=b"2", + time_per_sub_block=8, + ) + found_orphan = False + for reorg_block in blocks_reorg_chain: + result, error_code, fork_height = await b.receive_block(reorg_block) + if reorg_block.sub_block_height < num_blocks_chain_2_start: + assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK + if reorg_block.weight <= chain_1_weight: + if result == ReceiveBlockResult.ADDED_AS_ORPHAN: + found_orphan = True + assert error_code is None + assert result == ReceiveBlockResult.ADDED_AS_ORPHAN or result == ReceiveBlockResult.ALREADY_HAVE_BLOCK + elif reorg_block.weight > chain_1_weight: + assert reorg_block.sub_block_height < chain_1_height + assert result == ReceiveBlockResult.NEW_PEAK + assert error_code is None + assert found_orphan + + assert b.get_peak().weight > chain_1_weight + assert b.get_peak().sub_block_height < chain_1_height + + @pytest.mark.asyncio + async def test_reorg_from_genesis(self, empty_blockchain): + b = empty_blockchain + blocks = bt.get_consecutive_blocks(15) + + for block in blocks: + assert (await b.receive_block(block))[0] == ReceiveBlockResult.NEW_PEAK + assert b.get_peak().sub_block_height == 14 + + # Reorg to alternate chain that is 1 height longer + found_orphan = False + blocks_reorg_chain = bt.get_consecutive_blocks(16, [], seed=b"2") + for reorg_block in blocks_reorg_chain: + result, error_code, fork_height = await b.receive_block(reorg_block) + if reorg_block.sub_block_height < 14: + if result == ReceiveBlockResult.ADDED_AS_ORPHAN: + found_orphan = True + assert result == ReceiveBlockResult.ADDED_AS_ORPHAN or result == ReceiveBlockResult.ALREADY_HAVE_BLOCK + elif reorg_block.sub_block_height >= 15: + assert result == ReceiveBlockResult.NEW_PEAK + assert error_code is None + + # Back to original chain + blocks_reorg_chain_2 = bt.get_consecutive_blocks(3, blocks, seed=b"3") + + result, error_code, fork_height = await b.receive_block(blocks_reorg_chain_2[-3]) + assert result == ReceiveBlockResult.ADDED_AS_ORPHAN + + result, error_code, fork_height = await b.receive_block(blocks_reorg_chain_2[-2]) + assert result == ReceiveBlockResult.NEW_PEAK + + result, error_code, fork_height = await b.receive_block(blocks_reorg_chain_2[-1]) + assert result == ReceiveBlockResult.NEW_PEAK + assert found_orphan + assert b.get_peak().sub_block_height == 17 diff --git a/tests/consensus/test_pot_iterations.py b/tests/consensus/test_pot_iterations.py index f083cf3746..33c721a3ac 100644 --- a/tests/consensus/test_pot_iterations.py +++ b/tests/consensus/test_pot_iterations.py @@ -10,12 +10,7 @@ from src.util.ints import uint8, uint64 from src.consensus.default_constants import DEFAULT_CONSTANTS from pytest import raises -test_constants = DEFAULT_CONSTANTS.replace( - **{ - "NUM_SPS_SUB_SLOT": 32, - "SUB_SLOT_TIME_TARGET": 300, - } -) +test_constants = DEFAULT_CONSTANTS.replace(**{"NUM_SPS_SUB_SLOT": 32, "SUB_SLOT_TIME_TARGET": 300}) class TestPotIterations: @@ -58,7 +53,6 @@ class TestPotIterations: required_iters = sp_interval_iters - 1 ip_iters = calculate_ip_iters(test_constants, ssi, uint8(13), required_iters) - print(sp_iters, sp_interval_iters, required_iters) assert ip_iters == sp_iters + test_constants.NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters required_iters = uint64(1) @@ -115,8 +109,6 @@ class TestPotIterations: total_wins_in_slot += 1 win_percentage = {k: wins[k] / sum(wins.values()) for k in farmer_ks.keys()} - print(win_percentage) - print(percentage_space) for k in farmer_ks.keys(): # Win rate is proportional to percentage of space assert abs(win_percentage[k] - percentage_space[k]) < 0.01 diff --git a/tests/full_node/test_blockchain_transactions.py b/tests/full_node/test_blockchain_transactions.py index d88ef1160c..7970bca4d7 100644 --- a/tests/full_node/test_blockchain_transactions.py +++ b/tests/full_node/test_blockchain_transactions.py @@ -3,8 +3,10 @@ import pytest from src.consensus.blockchain import ReceiveBlockResult from src.protocols import full_node_protocol +from src.types.condition_opcodes import ConditionOpcode +from src.types.condition_var_pair import ConditionVarPair from src.types.spend_bundle import SpendBundle -from src.util.errors import Err +from src.util.errors import Err, ConsensusError from tests.full_node.test_full_node import connect_and_get_peer from tests.setup_nodes import setup_two_nodes, test_constants, bt from src.util.wallet_tools import WalletTool @@ -75,7 +77,6 @@ class TestBlockchainTransactions: await full_node_1.respond_sub_block(full_node_protocol.RespondSubBlock(next_block)) assert next_block.header_hash == full_node_1.blockchain.get_peak().header_hash - print("Adde block ", next_block) added_coins = next_spendbundle.additions() @@ -127,458 +128,408 @@ class TestBlockchainTransactions: assert res == ReceiveBlockResult.INVALID_BLOCK assert err == Err.DOUBLE_SPEND + @pytest.mark.asyncio + async def test_validate_blockchain_duplicate_output(self, two_nodes): + num_blocks = 3 + wallet_a = WALLET_A + coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] + receiver_puzzlehash = BURN_PUZZLE_HASH -# @pytest.mark.asyncio -# async def test_validate_blockchain_duplicate_output(self, two_nodes): -# num_blocks = 10 -# wallet_a = WALLET_A -# coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] -# receiver_puzzlehash = BURN_PUZZLE_HASH -# -# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) -# full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes -# full_node_1 = full_node_api_1.full_node -# -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# spent_block = blocks[1] -# -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spent_block.get_coinbase()) -# spend_bundle_double = wallet_a.generate_signed_transaction( -# 1000, receiver_puzzlehash, spent_block.get_coinbase() -# ) -# -# block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_double]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {(num_blocks + 1): (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) -# -# next_block = new_blocks[(num_blocks + 1)] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# -# assert error is Err.DUPLICATE_OUTPUT -# -# @pytest.mark.asyncio -# async def test_validate_blockchain_with_reorg_double_spend(self, two_nodes): -# num_blocks = 10 -# wallet_a = WALLET_A -# coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] -# receiver_puzzlehash = BURN_PUZZLE_HASH -# -# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) -# full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes -# full_node_1 = full_node_api_1.full_node -# -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# spent_block = blocks[1] -# -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spent_block.get_coinbase()) -# block_spendbundle = SpendBundle.aggregate([spend_bundle]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {11: (program, aggsig)} -# blocks = bt.get_consecutive_blocks(test_constants, 10, blocks, 10, b"", coinbase_puzzlehash, dic_h) -# # Move chain to height 20, with a spend at height 11 -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# # Reorg at block 5, same spend at block 13 and 14 that was previously at block 11 -# dic_h = {13: (program, aggsig), 14: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 9, -# blocks[:6], -# 10, -# b"another seed", -# coinbase_puzzlehash, -# dic_h, -# ) -# -# for block in new_blocks[:13]: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# next_block = new_blocks[13] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# assert error is None -# -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[13])) -# -# next_block = new_blocks[14] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# assert error is Err.DOUBLE_SPEND -# -# # Now test Reorg at block 5, same spend at block 9 that was previously at block 11 -# dic_h = {9: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 4, -# blocks[:6], -# 10, -# b"another seed 2", -# coinbase_puzzlehash, -# dic_h, -# ) -# for block in new_blocks[:9]: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# next_block = new_blocks[9] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# assert error is None -# -# # Now test Reorg at block 10, same spend at block 11 that was previously at block 11 -# dic_h = {11: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 4, -# blocks[:11], -# 10, -# b"another seed 3", -# coinbase_puzzlehash, -# dic_h, -# ) -# for block in new_blocks[:11]: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# next_block = new_blocks[11] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# assert error is None -# -# # Now test Reorg at block 11, same spend at block 12 that was previously at block 11 -# dic_h = {12: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 4, -# blocks[:12], -# 10, -# b"another seed 4", -# coinbase_puzzlehash, -# dic_h, -# ) -# for block in new_blocks[:12]: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# next_block = new_blocks[12] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# assert error is Err.DOUBLE_SPEND -# -# # Now test Reorg at block 11, same spend at block 15 that was previously at block 11 -# dic_h = {15: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 4, -# blocks[:12], -# 10, -# b"another seed 5", -# coinbase_puzzlehash, -# dic_h, -# ) -# for block in new_blocks[:15]: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# next_block = new_blocks[15] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# assert error is Err.DOUBLE_SPEND -# -# @pytest.mark.asyncio -# async def test_validate_blockchain_spend_reorg_coin(self, two_nodes): -# num_blocks = 10 -# wallet_a = WALLET_A -# coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] -# receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] -# receiver_2_puzzlehash = WALLET_A_PUZZLE_HASHES[2] -# receiver_3_puzzlehash = WALLET_A_PUZZLE_HASHES[3] -# -# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) -# full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes -# full_node_1 = full_node_api_1.full_node -# -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# spent_block = blocks[1] -# -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, spent_block.get_coinbase()) -# block_spendbundle = SpendBundle.aggregate([spend_bundle]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {5: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 1, -# blocks[:5], -# 10, -# b"spend_reorg_coin", -# coinbase_puzzlehash, -# dic_h, -# ) -# -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) -# assert new_blocks[-1].header_hash in full_node_1.blockchain.headers -# -# coin_2 = None -# for coin in new_blocks[-1].additions(): -# if coin.puzzle_hash == receiver_1_puzzlehash: -# coin_2 = coin -# break -# assert coin_2 is not None -# -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_2_puzzlehash, coin_2) -# block_spendbundle = SpendBundle.aggregate([spend_bundle]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {6: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 1, -# new_blocks[:6], -# 10, -# b"spend_reorg_coin", -# coinbase_puzzlehash, -# dic_h, -# ) -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) -# assert new_blocks[-1].header_hash in full_node_1.blockchain.headers -# -# coin_3 = None -# for coin in new_blocks[-1].additions(): -# if coin.puzzle_hash == receiver_2_puzzlehash: -# coin_3 = coin -# break -# assert coin_3 is not None -# -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_3_puzzlehash, coin_3) -# block_spendbundle = SpendBundle.aggregate([spend_bundle]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {7: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 1, -# new_blocks[:7], -# 10, -# b"spend_reorg_coin", -# coinbase_puzzlehash, -# dic_h, -# ) -# -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) -# assert new_blocks[-1].header_hash in full_node_1.blockchain.headers -# -# coin_4 = None -# for coin in new_blocks[-1].additions(): -# if coin.puzzle_hash == receiver_3_puzzlehash: -# coin_4 = coin -# break -# assert coin_4 is not None -# -# @pytest.mark.asyncio -# async def test_validate_blockchain_spend_reorg_cb_coin(self, two_nodes): -# num_blocks = 10 -# wallet_a = WALLET_A -# coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] -# receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] -# -# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) -# full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes -# full_node_1 = full_node_api_1.full_node -# -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# # Spends a coinbase created in reorg -# new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks[:6], -# 10, b"reorg cb coin", coinbase_puzzlehash) -# -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) -# assert new_blocks[-1].header_hash in full_node_1.blockchain.headers -# -# spent_block = new_blocks[-1] -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, -# spent_block.get_coinbase()) -# spend_bundle_2 = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, -# spent_block.get_fees_coin()) -# -# block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_2]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {7: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 1, -# new_blocks, -# 10, -# b"reorg cb coin", -# coinbase_puzzlehash, -# dic_h, -# ) -# error = await full_node_1.blockchain._validate_transactions( -# new_blocks[-1], new_blocks[-1].get_fees_coin().amount -# ) -# assert error is None -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) -# assert new_blocks[-1].header_hash in full_node_1.blockchain.headers -# -# coins_created = [] -# for coin in new_blocks[-1].additions(): -# if coin.puzzle_hash == receiver_1_puzzlehash: -# coins_created.append(coin) -# assert len(coins_created) == 2 -# -# @pytest.mark.asyncio -# async def test_validate_blockchain_spend_reorg_cb_coin_freeze(self, two_nodes_standard_freeze): -# num_blocks = 10 -# wallet_a = WALLET_A -# coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] -# receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] -# -# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) -# full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes_standard_freeze -# full_node_1 = full_node_api_1.full_node -# -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# # Spends a coinbase created in reorg -# new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks[:6], 10, -# b"reorg cb coin", coinbase_puzzlehash) -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) -# -# assert new_blocks[-1].header_hash in full_node_1.blockchain.headers -# -# spent_block = new_blocks[-1] -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, spent_block.get_coinbase()) -# block_spendbundle = SpendBundle.aggregate([spend_bundle]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {7: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 1, -# new_blocks, -# 10, -# b"reorg cb coin", -# coinbase_puzzlehash, -# dic_h, -# ) -# error = await full_node_1.blockchain._validate_transactions( -# new_blocks[-1], new_blocks[-1].get_coinbase().amount -# ) -# assert error is Err.COINBASE_NOT_YET_SPENDABLE -# -# @pytest.mark.asyncio -# async def test_validate_blockchain_spend_reorg_since_genesis(self, two_nodes): -# num_blocks = 10 -# wallet_a = WALLET_A -# coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] -# receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] -# -# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) -# full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes -# full_node_1 = full_node_api_1.full_node -# -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# spent_block = blocks[1] -# -# spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, spent_block.get_coinbase()) -# block_spendbundle = SpendBundle.aggregate([spend_bundle]) -# program = best_solution_program(block_spendbundle) -# aggsig = block_spendbundle.aggregated_signature -# -# dic_h = {11: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) -# -# # Spends a coin in a genesis reorg, that was already spent -# dic_h = {5: (program, aggsig)} -# new_blocks = bt.get_consecutive_blocks( -# test_constants, -# 12, -# [], -# 10, -# b"reorg since genesis", -# coinbase_puzzlehash, -# dic_h, -# ) -# for block in new_blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# assert new_blocks[-1].header_hash in full_node_1.blockchain.headers -# -# @pytest.mark.asyncio -# async def test_assert_my_coin_id(self, two_nodes): -# num_blocks = 10 -# wallet_a = WALLET_A -# coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] -# receiver_puzzlehash = BURN_PUZZLE_HASH -# -# # Farm blocks -# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) -# full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes -# full_node_1 = full_node_api_1.full_node -# -# for block in blocks: -# await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) -# -# # Coinbase that gets spent -# spent_block = blocks[1] -# bad_block = blocks[2] -# valid_cvp = ConditionVarPair( -# ConditionOpcode.ASSERT_MY_COIN_ID, -# spent_block.get_coinbase().name(), -# None, -# ) -# valid_dic = {valid_cvp.opcode: [valid_cvp]} -# bad_cvp = ConditionVarPair( -# ConditionOpcode.ASSERT_MY_COIN_ID, -# bad_block.get_coinbase().name(), -# None, -# ) -# -# bad_dic = {bad_cvp.opcode: [bad_cvp]} -# bad_spend_bundle = wallet_a.generate_signed_transaction( -# 1000, receiver_puzzlehash, spent_block.get_coinbase(), bad_dic -# ) -# -# valid_spend_bundle = wallet_a.generate_signed_transaction( -# 1000, receiver_puzzlehash, spent_block.get_coinbase(), valid_dic -# ) -# -# # Invalid block bundle -# assert bad_spend_bundle is not None -# invalid_program = best_solution_program(bad_spend_bundle) -# aggsig = bad_spend_bundle.aggregated_signature -# -# # Create another block that includes our transaction -# dic_h = {11: (invalid_program, aggsig)} -# invalid_new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) -# -# # Try to validate that block -# next_block = invalid_new_blocks[11] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# -# assert error is Err.ASSERT_MY_COIN_ID_FAILED -# -# # Valid block bundle -# assert valid_spend_bundle is not None -# valid_program = best_solution_program(valid_spend_bundle) -# aggsig = valid_spend_bundle.aggregated_signature -# -# # Create another block that includes our transaction -# dic_h = {11: (valid_program, aggsig)} -# new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks[:11], 10, b"1", coinbase_puzzlehash, dic_h) -# next_block = new_blocks[11] -# error = await full_node_1.blockchain._validate_transactions(next_block, next_block.get_fees_coin().amount) -# assert error is None -# -# @pytest.mark.asyncio + blocks = bt.get_consecutive_blocks( + num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_block=True + ) + full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes + full_node_1 = full_node_api_1.full_node + + for block in blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + spend_block = blocks[1] + + spend_coin = None + for coin in list(spend_block.get_included_reward_coins()): + if coin.puzzle_hash == coinbase_puzzlehash: + spend_coin = coin + + spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin) + spend_bundle_double = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin) + + block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_double]) + + new_blocks = bt.get_consecutive_blocks( + 1, + block_list_input=blocks, + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=block_spendbundle, + guarantee_block=True, + ) + + next_block = new_blocks[-1] + res, err, _ = await full_node_1.blockchain.receive_block(next_block) + assert res == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.DUPLICATE_OUTPUT + + @pytest.mark.asyncio + async def test_validate_blockchain_with_reorg_double_spend(self, two_nodes): + num_blocks = 10 + wallet_a = WALLET_A + coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] + receiver_puzzlehash = BURN_PUZZLE_HASH + + blocks = bt.get_consecutive_blocks( + num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_block=True + ) + full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes + + for block in blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + spend_block = blocks[1] + + spend_coin = None + for coin in list(spend_block.get_included_reward_coins()): + if coin.puzzle_hash == coinbase_puzzlehash: + spend_coin = coin + + spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin) + + blocks_spend = bt.get_consecutive_blocks( + 1, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_block=True, transaction_data=spend_bundle + ) + # Move chain to height 10, with a spend at height 10 + for block in blocks_spend: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + # Reorg at height 5, add up to and including height 12 + new_blocks = bt.get_consecutive_blocks( + 7, + blocks[:6], + farmer_reward_puzzle_hash=coinbase_puzzlehash, + guarantee_block=True, + seed=b"another seed", + ) + + for block in new_blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + # Spend the same coin in the new reorg chain at height 13 + new_blocks = bt.get_consecutive_blocks( + 1, + new_blocks, + farmer_reward_puzzle_hash=coinbase_puzzlehash, + guarantee_block=True, + transaction_data=spend_bundle, + ) + + res, err, _ = await full_node_api_1.full_node.blockchain.receive_block(new_blocks[-1]) + assert err is None + assert res == ReceiveBlockResult.NEW_PEAK + + # But can't spend it twice + new_blocks_double = bt.get_consecutive_blocks( + 1, + new_blocks, + farmer_reward_puzzle_hash=coinbase_puzzlehash, + guarantee_block=True, + transaction_data=spend_bundle, + ) + + res, err, _ = await full_node_api_1.full_node.blockchain.receive_block(new_blocks_double[-1]) + assert err is Err.DOUBLE_SPEND + assert res == ReceiveBlockResult.INVALID_BLOCK + + # Now test Reorg at block 5, same spend at block height 12 + new_blocks_reorg = bt.get_consecutive_blocks( + 1, + new_blocks[:12], + farmer_reward_puzzle_hash=coinbase_puzzlehash, + guarantee_block=True, + transaction_data=spend_bundle, + seed=b"spend at 12 is ok", + ) + for block in new_blocks_reorg: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + # Spend at height 13 is also OK (same height) + new_blocks_reorg = bt.get_consecutive_blocks( + 1, + new_blocks[:13], + farmer_reward_puzzle_hash=coinbase_puzzlehash, + guarantee_block=True, + transaction_data=spend_bundle, + seed=b"spend at 13 is ok", + ) + for block in new_blocks_reorg: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + # Spend at height 14 is not OK (already spend) + new_blocks_reorg = bt.get_consecutive_blocks( + 1, + new_blocks[:14], + farmer_reward_puzzle_hash=coinbase_puzzlehash, + guarantee_block=True, + transaction_data=spend_bundle, + seed=b"spend at 14 is double spend", + ) + with pytest.raises(ConsensusError): + for block in new_blocks_reorg: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + @pytest.mark.asyncio + async def test_validate_blockchain_spend_reorg_coin(self, two_nodes): + num_blocks = 10 + wallet_a = WALLET_A + coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] + receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] + receiver_2_puzzlehash = WALLET_A_PUZZLE_HASHES[2] + receiver_3_puzzlehash = WALLET_A_PUZZLE_HASHES[3] + + blocks = bt.get_consecutive_blocks( + num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_block=True + ) + full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes + + for block in blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + spend_block = blocks[1] + + spend_coin = None + for coin in list(spend_block.get_included_reward_coins()): + if coin.puzzle_hash == coinbase_puzzlehash: + spend_coin = coin + + spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, spend_coin) + + new_blocks = bt.get_consecutive_blocks( + 1, + blocks[:5], + seed=b"spend_reorg_coin", + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=spend_bundle, + guarantee_block=True, + ) + + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) + + coin_2 = None + for coin in new_blocks[-1].additions(): + if coin.puzzle_hash == receiver_1_puzzlehash: + coin_2 = coin + break + assert coin_2 is not None + + spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_2_puzzlehash, coin_2) + + new_blocks = bt.get_consecutive_blocks( + 1, + new_blocks[:6], + seed=b"spend_reorg_coin", + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=spend_bundle, + guarantee_block=True, + ) + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) + + coin_3 = None + for coin in new_blocks[-1].additions(): + if coin.puzzle_hash == receiver_2_puzzlehash: + coin_3 = coin + break + assert coin_3 is not None + + spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_3_puzzlehash, coin_3) + + new_blocks = bt.get_consecutive_blocks( + 1, + new_blocks[:7], + seed=b"spend_reorg_coin", + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=spend_bundle, + guarantee_block=True, + ) + + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) + + coin_4 = None + for coin in new_blocks[-1].additions(): + if coin.puzzle_hash == receiver_3_puzzlehash: + coin_4 = coin + break + assert coin_4 is not None + + @pytest.mark.asyncio + async def test_validate_blockchain_spend_reorg_cb_coin(self, two_nodes): + num_blocks = 15 + wallet_a = WALLET_A + coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] + receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] + + blocks = bt.get_consecutive_blocks(num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash) + full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes + + for block in blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + # Spends a coinbase created in reorg + new_blocks = bt.get_consecutive_blocks( + 5, + blocks[:6], + seed=b"reorg cb coin", + farmer_reward_puzzle_hash=coinbase_puzzlehash, + guarantee_block=True, + ) + + for block in new_blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + spend_block = new_blocks[-1] + spend_coin = None + for coin in list(spend_block.get_included_reward_coins()): + if coin.puzzle_hash == coinbase_puzzlehash: + spend_coin = coin + spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, spend_coin) + + new_blocks = bt.get_consecutive_blocks( + 1, + new_blocks, + seed=b"reorg cb coin", + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=spend_bundle, + guarantee_block=True, + ) + + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) + + coins_created = [] + for coin in new_blocks[-1].additions(): + if coin.puzzle_hash == receiver_1_puzzlehash: + coins_created.append(coin) + assert len(coins_created) == 1 + + @pytest.mark.asyncio + async def test_validate_blockchain_spend_reorg_since_genesis(self, two_nodes): + num_blocks = 10 + wallet_a = WALLET_A + coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] + receiver_1_puzzlehash = WALLET_A_PUZZLE_HASHES[1] + + blocks = bt.get_consecutive_blocks( + num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_block=True + ) + full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes + + for block in blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + spend_block = blocks[-1] + spend_coin = None + for coin in list(spend_block.get_included_reward_coins()): + if coin.puzzle_hash == coinbase_puzzlehash: + spend_coin = coin + spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_1_puzzlehash, spend_coin) + + new_blocks = bt.get_consecutive_blocks( + 1, blocks, seed=b"", farmer_reward_puzzle_hash=coinbase_puzzlehash, transaction_data=spend_bundle + ) + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) + + # Spends a coin in a genesis reorg, that was already spent + new_blocks = bt.get_consecutive_blocks( + 12, + [], + seed=b"reorg since genesis", + farmer_reward_puzzle_hash=coinbase_puzzlehash, + ) + for block in new_blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + new_blocks = bt.get_consecutive_blocks( + 1, + new_blocks, + seed=b"reorg since genesis", + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=spend_bundle, + ) + + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(new_blocks[-1])) + + @pytest.mark.asyncio + async def test_assert_my_coin_id(self, two_nodes): + num_blocks = 10 + wallet_a = WALLET_A + coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0] + receiver_puzzlehash = BURN_PUZZLE_HASH + + # Farm blocks + blocks = bt.get_consecutive_blocks( + num_blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_block=True + ) + full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes + full_node_1 = full_node_api_1.full_node + + for block in blocks: + await full_node_api_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block)) + + # Coinbase that gets spent + + spend_block = blocks[1] + bad_block = blocks[2] + spend_coin = None + bad_spend_coin = None + for coin in list(spend_block.get_included_reward_coins()): + if coin.puzzle_hash == coinbase_puzzlehash: + spend_coin = coin + for coin in list(bad_block.get_included_reward_coins()): + if coin.puzzle_hash == coinbase_puzzlehash: + bad_spend_coin = coin + valid_cvp = ConditionVarPair(ConditionOpcode.ASSERT_MY_COIN_ID, spend_coin.name(), None) + valid_dic = {valid_cvp.opcode: [valid_cvp]} + bad_cvp = ConditionVarPair( + ConditionOpcode.ASSERT_MY_COIN_ID, + bad_spend_coin.name(), + None, + ) + + bad_dic = {bad_cvp.opcode: [bad_cvp]} + bad_spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin, bad_dic) + + valid_spend_bundle = wallet_a.generate_signed_transaction(1000, receiver_puzzlehash, spend_coin, valid_dic) + + assert bad_spend_bundle is not None + assert valid_spend_bundle is not None + + # Invalid block bundle + # Create another block that includes our transaction + invalid_new_blocks = bt.get_consecutive_blocks( + 1, + blocks, + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=bad_spend_bundle, + guarantee_block=True, + ) + + # Try to validate that block + res, err, _ = await full_node_1.blockchain.receive_block(invalid_new_blocks[-1]) + assert res == ReceiveBlockResult.INVALID_BLOCK + assert err == Err.ASSERT_MY_COIN_ID_FAILED + + # Valid block bundle + # Create another block that includes our transaction + new_blocks = bt.get_consecutive_blocks( + 1, + blocks, + farmer_reward_puzzle_hash=coinbase_puzzlehash, + transaction_data=valid_spend_bundle, + guarantee_block=True, + ) + res, err, _ = await full_node_1.blockchain.receive_block(new_blocks[-1]) + assert res == ReceiveBlockResult.NEW_PEAK + assert err is None + + +# # +# # @pytest.mark.asyncio # async def test_assert_coin_consumed(self, two_nodes): # # num_blocks = 10 diff --git a/tests/full_node/test_full_node.py b/tests/full_node/test_full_node.py index a0d43d7e1c..115d6fc2ec 100644 --- a/tests/full_node/test_full_node.py +++ b/tests/full_node/test_full_node.py @@ -157,9 +157,7 @@ class TestFullNodeProtocol: async def have_msgs(): await full_node_2.full_node.full_node_peers.address_manager.add_to_new_table( - [ - TimestampedPeerInfo("127.0.0.1", uint16(1000), uint64(int(time.time())) - 1000), - ], + [TimestampedPeerInfo("127.0.0.1", uint16(1000), uint64(int(time.time())) - 1000)], None, ) msg = await full_node_2.full_node.full_node_peers.request_peers(PeerInfo("[::1]", server_2._port)) diff --git a/tests/full_node/test_mempool.py b/tests/full_node/test_mempool.py index 622bd183d8..1e8298e57c 100644 --- a/tests/full_node/test_mempool.py +++ b/tests/full_node/test_mempool.py @@ -10,9 +10,7 @@ from src.types.coin_solution import CoinSolution from src.types.condition_var_pair import ConditionVarPair from src.types.condition_opcodes import ConditionOpcode from src.types.spend_bundle import SpendBundle -from src.util.condition_tools import ( - conditions_for_solution, -) +from src.util.condition_tools import conditions_for_solution from src.util.clvm import int_to_bytes from src.util.ints import uint64 from tests.full_node.test_full_node import connect_and_get_peer, node_height_at_least diff --git a/tests/rpc/test_farmer_harvester_rpc.py b/tests/rpc/test_farmer_harvester_rpc.py index 2f13499f18..5f70da8d32 100644 --- a/tests/rpc/test_farmer_harvester_rpc.py +++ b/tests/rpc/test_farmer_harvester_rpc.py @@ -75,7 +75,6 @@ class TestRpc: await time_out_assert(15, have_connections, True) assert (await client.get_signage_point(std_hash(b"2"))) is None - print(await client.get_signage_points()) assert len(await client.get_signage_points()) == 0 async def have_signage_points(): @@ -119,7 +118,6 @@ class TestRpc: res_2 = await client_2.get_plots() assert len(res_2["plots"]) == num_plots - print(await client_2.get_plot_directories()) assert len(await client_2.get_plot_directories()) == 1 await client_2.add_plot_directory(str(plot_dir)) @@ -134,7 +132,6 @@ class TestRpc: assert len(res_3["plots"]) == num_plots await client_2.remove_plot_directory(str(plot_dir)) - print(await client_2.get_plot_directories()) assert len(await client_2.get_plot_directories()) == 1 finally: diff --git a/tests/test_simulation.py b/tests/test_simulation.py index cc129e21b4..623e577b5b 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -1,5 +1,4 @@ import pytest - from tests.full_node.test_full_sync import node_height_at_least from tests.setup_nodes import setup_full_system, test_constants from src.util.ints import uint16 @@ -33,7 +32,7 @@ class TestSimulation: node1, node2, _, _, _, _, _, server1 = simulation await server1.start_client(PeerInfo("localhost", uint16(21238))) # Use node2 to test node communication, since only node1 extends the chain. - await time_out_assert(1000, node_height_at_least, True, node2, 10) + await time_out_assert(1000, node_height_at_least, True, node2, 7) # Wait additional 2 minutes to get a compact block. # max_height = node1.full_node.blockchain.lca_block.height diff --git a/tests/time_out_assert.py b/tests/time_out_assert.py index f781651a1e..2db0b9e061 100644 --- a/tests/time_out_assert.py +++ b/tests/time_out_assert.py @@ -1,7 +1,10 @@ import asyncio import time +import logging from typing import Callable +log = logging.getLogger(__name__) + async def time_out_assert_custom_interval(timeout: int, interval, function, value=True, *args, **kwargs): start = time.time() @@ -40,7 +43,7 @@ def time_out_messages(incoming_queue: asyncio.Queue, msg_name: str, count: int = for _ in range(count): response = (await incoming_queue.get())[0].msg.function if response != msg_name: - print(f"Error, found {response} instead of {msg_name}") + log.error(f"Error, found {response} instead of {msg_name}") return False return True