async def get_blockchain_state(self) -> Dict: response = await self.fetch("get_blockchain_state", {}) response["tips"] = [ Header.from_json_dict(tip) for tip in response["tips"] ] response["lca"] = Header.from_json_dict(response["lca"]) return response
async def test_timestamp(self, initial_blockchain): blocks, b = initial_blockchain # Time too far in the past block_bad = FullBlock( HeaderBlock( blocks[9].header_block.proof_of_space, blocks[9].header_block.proof_of_time, blocks[9].header_block.challenge, Header( HeaderData( blocks[9].header_block.header.data.prev_header_hash, blocks[9].header_block.header.data.timestamp - 1000, blocks[9].header_block.header.data.filter_hash, blocks[9].header_block.header.data.proof_of_space_hash, blocks[9].header_block.header.data.body_hash, blocks[9].header_block.header.data.extension_data, ), blocks[9].header_block.header.harvester_signature, ), ), blocks[9].body, ) assert (await b.receive_block( block_bad, blocks[8].header_block)) == ReceiveBlockResult.INVALID_BLOCK # Time too far in the future block_bad = FullBlock( HeaderBlock( blocks[9].header_block.proof_of_space, blocks[9].header_block.proof_of_time, blocks[9].header_block.challenge, Header( HeaderData( blocks[9].header_block.header.data.prev_header_hash, uint64(int(time.time() + 3600 * 3)), blocks[9].header_block.header.data.filter_hash, blocks[9].header_block.header.data.proof_of_space_hash, blocks[9].header_block.header.data.body_hash, blocks[9].header_block.header.data.extension_data, ), blocks[9].header_block.header.harvester_signature, ), ), blocks[9].body, ) assert (await b.receive_block( block_bad, blocks[8].header_block)) == ReceiveBlockResult.INVALID_BLOCK
async def get_header_by_height(self, header_height) -> Optional[Header]: try: response = await self.fetch("get_header_by_height", {"height": header_height}) except Exception: return None return Header.from_json_dict(response["header"])
async def header_signature( self, header_signature: farmer_protocol.HeaderSignature ) -> OutboundMessageGenerator: """ Signature of header hash, by the harvester. This is enough to create an unfinished block, which only needs a Proof of Time to be finished. If the signature is valid, we call the unfinished_block routine. """ async with self.store.lock: candidate: Optional[ Tuple[Body, HeaderData, ProofOfSpace]] = await self.store.get_candidate_block( header_signature.pos_hash) if candidate is None: log.warning( f"PoS hash {header_signature.pos_hash} not found in database" ) return # Verifies that we have the correct header and body self.stored block_body, block_header_data, pos = candidate assert block_header_data.get_hash() == header_signature.header_hash block_header: Header = Header(block_header_data, header_signature.header_signature) header: HeaderBlock = HeaderBlock(pos, None, None, block_header) unfinished_block_obj: FullBlock = FullBlock(header, block_body) # Propagate to ourselves (which validates and does further propagations) request = peer_protocol.UnfinishedBlock(unfinished_block_obj) async for m in self.unfinished_block(request): # Yield all new messages (propagation to peers) yield m
async def test_body_hash(self, initial_blockchain): blocks, b = initial_blockchain block_bad = FullBlock( HeaderBlock( blocks[9].header_block.proof_of_space, blocks[9].header_block.proof_of_time, blocks[9].header_block.challenge, Header( HeaderData( blocks[9].header_block.header.data.prev_header_hash, blocks[9].header_block.header.data.timestamp, blocks[9].header_block.header.data.filter_hash, blocks[9].header_block.header.data.proof_of_space_hash, bytes([1] * 32), blocks[9].header_block.header.data.extension_data, ), blocks[9].header_block.header.harvester_signature, ), ), blocks[9].body, ) assert (await b.receive_block( block_bad, blocks[8].header_block)) == ReceiveBlockResult.INVALID_BLOCK
async def test_generator_hash(self, initial_blockchain): blocks, b = initial_blockchain new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, blocks[9].header.data.timestamp, blocks[9].header.data.filter_hash, blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.farmer_rewards_puzzle_hash, blocks[9].header.data.total_transaction_fees, blocks[9].header.data.pool_target, blocks[9].header.data.aggregated_signature, blocks[9].header.data.cost, blocks[9].header.data.extension_data, bytes([1] * 32), ) block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_plot_signature( new_header_data, blocks[9].proof_of_space.plot_public_key), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_TRANSACTIONS_GENERATOR_HASH
async def test_prev_pointer(self, initial_blockchain): blocks, b = initial_blockchain block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( HeaderData( blocks[9].header.data.height, bytes([1] * 32), blocks[9].header.data.timestamp, blocks[9].header.data.filter_hash, blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.farmer_rewards_puzzle_hash, blocks[9].header.data.total_transaction_fees, blocks[9].header.data.pool_target, blocks[9].header.data.aggregated_signature, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ), blocks[9].header.plot_signature, ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert (result) == ReceiveBlockResult.DISCONNECTED_BLOCK assert error_code is None
async def get_lca(self) -> Optional[Header]: cursor = await self.db.execute( "SELECT header from headers WHERE is_lca=1") row = await cursor.fetchone() await cursor.close() if row is not None: return Header.from_bytes(row[0]) return None
async def get_header(self, header_hash) -> Optional[Header]: try: response = await self.fetch("get_header", {"header_hash": header_hash.hex()}) except aiohttp.client_exceptions.ClientResponseError as e: if e.message == "Not Found": return None raise return Header.from_json_dict(response)
async def get_headers(self) -> Dict[bytes32, Header]: cursor = await self.db.execute( "SELECT header_hash, header from headers") rows = await cursor.fetchall() await cursor.close() return { bytes.fromhex(row[0]): Header.from_bytes(row[1]) for row in rows }
async def get_header(self, header_hash) -> Optional[Header]: try: response = await self.fetch("get_header", {"header_hash": header_hash.hex()}) if response["header"] is None: return None except Exception: return None return Header.from_json_dict(response["header"])
async def test_harvester_signature(self, initial_blockchain): blocks, b = initial_blockchain # Time too far in the past block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( blocks[9].header.data, PrivateKey.from_seed(b"0").sign_prepend(b"random junk"), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_HARVESTER_SIGNATURE
async def test_harvester_signature(self, initial_blockchain): blocks, b = initial_blockchain # Time too far in the past block_bad = FullBlock( HeaderBlock( blocks[9].header_block.proof_of_space, blocks[9].header_block.proof_of_time, blocks[9].header_block.challenge, Header( blocks[9].header_block.header.data, PrivateKey.from_seed(b"0").sign_prepend(b"random junk"), ), ), blocks[9].body, ) assert (await b.receive_block(block_bad)) == ReceiveBlockResult.INVALID_BLOCK
async def test_invalid_pos_hash(self, initial_blockchain): blocks, b = initial_blockchain bad_pos_proof = bytearray([i for i in blocks[9].proof_of_space.proof]) bad_pos_proof[0] = uint8((bad_pos_proof[0] + 1) % 256) bad_pos = ProofOfSpace( blocks[9].proof_of_space.challenge_hash, blocks[9].proof_of_space.pool_public_key, blocks[9].proof_of_space.plot_public_key, blocks[9].proof_of_space.size, bytes(bad_pos_proof), ) new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, blocks[9].header.data.timestamp, blocks[9].header.data.filter_hash, bad_pos.get_hash(), blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.farmer_rewards_puzzle_hash, blocks[9].header.data.total_transaction_fees, blocks[9].header.data.pool_target, blocks[9].header.data.aggregated_signature, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ) # Proof of space has invalid block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_plot_signature( new_header_data, blocks[9].proof_of_space.plot_public_key ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_POSPACE_HASH
async def test_plot_signature(self, initial_blockchain): blocks, b = initial_blockchain # Time too far in the past block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( blocks[9].header.data, AugSchemeMPL.sign(PrivateKey.from_seed(bytes([5] * 32)), token_bytes(32)), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_PLOT_SIGNATURE
async def test_invalid_coinbase_signature(self, initial_blockchain): blocks, b = initial_blockchain coinbase_coin, coinbase_signature = create_coinbase_coin_and_signature( blocks[9].header.data.height, blocks[9].header.data.coinbase.puzzle_hash, uint64(9991), PrivateKey.from_bytes( bytes.fromhex(list(bt.plot_config["plots"].values())[0]["pool_sk"]) ), ) new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, blocks[9].header.data.timestamp, blocks[9].header.data.filter_hash, blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.coinbase, coinbase_signature, blocks[9].header.data.fees_coin, blocks[9].header.data.aggregated_signature, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ) # Coinbase amount invalid block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_harvester_signature( new_header_data, blocks[9].proof_of_space.plot_pubkey ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_COINBASE_SIGNATURE
async def test_invalid_max_height(self, initial_blockchain): blocks, b = initial_blockchain print(blocks[9].header) pool_target = PoolTarget( blocks[9].header.data.pool_target.puzzle_hash, uint32(8) ) agg_sig = bt.get_pool_key_signature( pool_target, blocks[9].proof_of_space.pool_public_key ) assert agg_sig is not None new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, blocks[9].header.data.timestamp, blocks[9].header.data.filter_hash, blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.farmer_rewards_puzzle_hash, blocks[9].header.data.total_transaction_fees, pool_target, agg_sig, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ) block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_plot_signature( new_header_data, blocks[9].proof_of_space.plot_public_key ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_POOL_TARGET
async def test_invalid_filter_hash(self, initial_blockchain): blocks, b = initial_blockchain new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, blocks[9].header.data.timestamp, bytes32(bytes([3] * 32)), blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.coinbase, blocks[9].header.data.coinbase_signature, blocks[9].header.data.fees_coin, blocks[9].header.data.aggregated_signature, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ) block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_harvester_signature( new_header_data, blocks[9].proof_of_space.plot_pubkey ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_TRANSACTIONS_FILTER_HASH
async def get_heaviest_block_seen(self) -> Header: response = await self.fetch("get_heaviest_block_seen", {}) return Header.from_json_dict(response["tip"])
async def test_timestamp(self, initial_blockchain): blocks, b = initial_blockchain # Time too far in the past new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, blocks[9].header.data.timestamp - 1000, blocks[9].header.data.filter_hash, blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.farmer_rewards_puzzle_hash, blocks[9].header.data.total_transaction_fees, blocks[9].header.data.pool_target, blocks[9].header.data.aggregated_signature, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ) block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_plot_signature( new_header_data, blocks[9].proof_of_space.plot_public_key ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert (result) == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.TIMESTAMP_TOO_FAR_IN_PAST # Time too far in the future new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, uint64(int(time.time() + 3600 * 3)), blocks[9].header.data.filter_hash, blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.farmer_rewards_puzzle_hash, blocks[9].header.data.total_transaction_fees, blocks[9].header.data.pool_target, blocks[9].header.data.aggregated_signature, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ) block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_plot_signature( new_header_data, blocks[9].proof_of_space.plot_public_key ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert (result) == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.TIMESTAMP_TOO_FAR_IN_FUTURE
def _create_block( self, test_constants: Dict, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint128, timestamp: uint64, difficulty: uint64, min_iters: uint64, seed: bytes, genesis: bool = False, reward_puzzlehash: bytes32 = None, transactions: Program = None, aggsig: BLSSignature = None, fees: uint64 = uint64(0), ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ selected_prover = None selected_plot_sk = None selected_pool_sk = None selected_proof_index = 0 plots = list(self.plot_config["plots"].items()) selected_quality: Optional[bytes] = None best_quality = 0 if self.use_any_pos: for i in range(len(plots) * 3): # Allow passing in seed, to create reorgs and different chains random.seed(seed + i.to_bytes(4, "big")) seeded_pn = random.randint(0, len(plots) - 1) pool_sk = PrivateKey.from_bytes( bytes.fromhex(plots[seeded_pn][1]["pool_sk"]) ) plot_sk = PrivateKey.from_bytes( bytes.fromhex(plots[seeded_pn][1]["sk"]) ) prover = DiskProver(plots[seeded_pn][0]) qualities = prover.get_qualities_for_challenge(challenge_hash) if len(qualities) > 0: if self.use_any_pos: selected_quality = qualities[0] selected_prover = prover selected_pool_sk = pool_sk selected_plot_sk = plot_sk break else: for i in range(len(plots)): pool_sk = PrivateKey.from_bytes(bytes.fromhex(plots[i][1]["pool_sk"])) plot_sk = PrivateKey.from_bytes(bytes.fromhex(plots[i][1]["sk"])) prover = DiskProver(plots[i][0]) qualities = prover.get_qualities_for_challenge(challenge_hash) j = 0 for quality in qualities: qual_int = int.from_bytes(quality, "big", signed=False) if qual_int > best_quality: best_quality = qual_int selected_quality = quality selected_prover = prover selected_pool_sk = pool_sk selected_plot_sk = plot_sk selected_proof_index = j j += 1 assert selected_prover assert selected_pool_sk assert selected_plot_sk pool_pk = selected_pool_sk.get_public_key() plot_pk = selected_plot_sk.get_public_key() if selected_quality is None: raise RuntimeError("No proofs for this challenge") proof_xs: bytes = selected_prover.get_full_proof( challenge_hash, selected_proof_index ) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, pool_pk, plot_pk, selected_prover.get_size(), proof_xs ) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, min_iters ) # print("Doing iters", number_iters) int_size = (test_constants["DISCRIMINANT_SIZE_BITS"] + 16) >> 4 result = prove( challenge_hash, test_constants["DISCRIMINANT_SIZE_BITS"], number_iters ) output = ClassgroupElement( int512(int.from_bytes(result[0:int_size], "big", signed=True,)), int512( int.from_bytes(result[int_size : 2 * int_size], "big", signed=True,) ), ) proof_bytes = result[2 * int_size : 4 * int_size] proof_of_time = ProofOfTime( challenge_hash, number_iters, output, self.n_wesolowski, proof_bytes, ) if not reward_puzzlehash: reward_puzzlehash = self.fee_target # Use the extension data to create different blocks based on header hash extension_data: bytes32 = bytes32([random.randint(0, 255) for _ in range(32)]) cost = uint64(0) coinbase_reward = block_rewards.calculate_block_reward(height) fee_reward = uint64(block_rewards.calculate_base_fee(height) + fees) coinbase_coin, coinbase_signature = create_coinbase_coin_and_signature( height, reward_puzzlehash, coinbase_reward, selected_pool_sk ) parent_coin_name = std_hash(std_hash(height)) fees_coin = Coin(parent_coin_name, reward_puzzlehash, uint64(fee_reward)) # Create filter byte_array_tx: List[bytes32] = [] tx_additions: List[Coin] = [] tx_removals: List[bytes32] = [] encoded = None if transactions: error, npc_list, _ = get_name_puzzle_conditions(transactions) additions: List[Coin] = additions_for_npc(npc_list) for coin in additions: tx_additions.append(coin) byte_array_tx.append(bytearray(coin.puzzle_hash)) for npc in npc_list: tx_removals.append(npc.coin_name) byte_array_tx.append(bytearray(npc.coin_name)) bip158: PyBIP158 = PyBIP158(byte_array_tx) encoded = bytes(bip158.GetEncoded()) removal_merkle_set = MerkleSet() addition_merkle_set = MerkleSet() # Create removal Merkle set for coin_name in tx_removals: removal_merkle_set.add_already_hashed(coin_name) # Create addition Merkle set puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {} for coin in tx_additions: if coin.puzzle_hash in puzzlehash_coin_map: puzzlehash_coin_map[coin.puzzle_hash].append(coin) else: puzzlehash_coin_map[coin.puzzle_hash] = [coin] # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash for puzzle, coins in puzzlehash_coin_map.items(): addition_merkle_set.add_already_hashed(puzzle) addition_merkle_set.add_already_hashed(hash_coin_list(coins)) additions_root = addition_merkle_set.get_root() removal_root = removal_merkle_set.get_root() generator_hash = ( transactions.get_tree_hash() if transactions is not None else bytes32([0] * 32) ) filter_hash = std_hash(encoded) if encoded is not None else bytes32([0] * 32) header_data: HeaderData = HeaderData( height, prev_header_hash, timestamp, filter_hash, proof_of_space.get_hash(), uint128(prev_weight + difficulty), uint64(prev_iters + number_iters), additions_root, removal_root, coinbase_coin, coinbase_signature, fees_coin, aggsig, cost, extension_data, generator_hash, ) header_hash_sig: PrependSignature = selected_plot_sk.sign_prepend( header_data.get_hash() ) header: Header = Header(header_data, header_hash_sig) full_block: FullBlock = FullBlock( proof_of_space, proof_of_time, header, transactions, encoded ) return full_block
async def get_tips(self) -> List[bytes32]: cursor = await self.db.execute( "SELECT header from headers WHERE is_tip=1") rows = await cursor.fetchall() await cursor.close() return [Header.from_bytes(row[0]) for row in rows]
async def test_invalid_filter(self, two_nodes): num_blocks = 10 wallet_a = WalletTool() coinbase_puzzlehash = wallet_a.get_new_puzzlehash() wallet_receiver = WalletTool() receiver_puzzlehash = wallet_receiver.get_new_puzzlehash() blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash) full_node_1, full_node_2, server_1, server_2 = two_nodes for block in blocks: async for _ in full_node_1.respond_block( full_node_protocol.RespondBlock(block)): pass spent_block = blocks[1] spend_bundle = wallet_a.generate_signed_transaction( 1000, receiver_puzzlehash, spent_block.get_coinbase()) assert spend_bundle is not None tx: full_node_protocol.RespondTransaction = ( full_node_protocol.RespondTransaction(spend_bundle)) async for _ in full_node_1.respond_transaction(tx): outbound: OutboundMessage = _ # Maybe transaction means that it's accepted in mempool assert outbound.message.function == "new_transaction" sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name()) assert sb is spend_bundle last_block = blocks[10] next_spendbundle = await full_node_1.mempool_manager.create_bundle_for_tip( last_block.header) assert next_spendbundle is not None program = best_solution_program(next_spendbundle) aggsig = next_spendbundle.aggregated_signature dic_h = {11: (program, aggsig)} new_blocks = bt.get_consecutive_blocks(test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h) next_block = new_blocks[11] bad_header = HeaderData( next_block.header.data.height, next_block.header.data.prev_header_hash, next_block.header.data.timestamp, bytes32(bytes([3] * 32)), next_block.header.data.proof_of_space_hash, next_block.header.data.weight, next_block.header.data.total_iters, next_block.header.data.additions_root, next_block.header.data.removals_root, next_block.header.data.farmer_rewards_puzzle_hash, next_block.header.data.total_transaction_fees, next_block.header.data.pool_target, next_block.header.data.aggregated_signature, next_block.header.data.cost, next_block.header.data.extension_data, next_block.header.data.generator_hash, ) bad_block = FullBlock( next_block.proof_of_space, next_block.proof_of_time, Header( bad_header, bt.get_plot_signature( bad_header, next_block.proof_of_space.plot_public_key), ), next_block.transactions_generator, next_block.transactions_filter, ) result, removed, error_code = await full_node_1.blockchain.receive_block( bad_block) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_TRANSACTIONS_FILTER_HASH result, removed, error_code = await full_node_1.blockchain.receive_block( next_block) assert result == ReceiveBlockResult.ADDED_TO_HEAD
async def get_unfinished_block_headers(self, height: uint32) -> List[Header]: response = await self.fetch("get_unfinished_block_headers", {"height": height}) return [Header.from_json_dict(r) for r in response["headers"]]
def _create_block( self, test_constants: Dict, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint64, timestamp: uint64, difficulty: uint64, ips: uint64, seed: bytes, ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ prover = None plot_pk = None plot_sk = None qualities: List[bytes] = [] for pn in range(num_plots): # Allow passing in seed, to create reorgs and different chains seeded_pn = (pn + 17 * int.from_bytes(seed, "big")) % num_plots filename = self.filenames[seeded_pn] plot_pk = plot_pks[seeded_pn] plot_sk = plot_sks[seeded_pn] prover = DiskProver(os.path.join(self.plot_dir, filename)) qualities = prover.get_qualities_for_challenge(challenge_hash) if len(qualities) > 0: break assert prover assert plot_pk assert plot_sk if len(qualities) == 0: raise NoProofsOfSpaceFound("No proofs for this challenge") proof_xs: bytes = prover.get_full_proof(challenge_hash, 0) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, pool_pk, plot_pk, k, [uint8(b) for b in proof_xs]) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, ips, test_constants["MIN_BLOCK_TIME"]) disc: int = create_discriminant( challenge_hash, test_constants["DISCRIMINANT_SIZE_BITS"]) start_x: ClassGroup = ClassGroup.from_ab_discriminant(2, 1, disc) y_cl, proof_bytes = create_proof_of_time_nwesolowski( disc, start_x, number_iters, disc, n_wesolowski) output = ClassgroupElement(y_cl[0], y_cl[1]) proof_of_time = ProofOfTime( challenge_hash, number_iters, output, n_wesolowski, [uint8(b) for b in proof_bytes], ) coinbase: CoinbaseInfo = CoinbaseInfo( height, block_rewards.calculate_block_reward(uint32(height)), coinbase_target, ) coinbase_sig: PrependSignature = pool_sk.sign_prepend(bytes(coinbase)) fees_target: FeesTarget = FeesTarget(fee_target, uint64(0)) solutions_generator: bytes32 = sha256(seed).digest() cost = uint64(0) body: Body = Body(coinbase, coinbase_sig, fees_target, None, solutions_generator, cost) header_data: HeaderData = HeaderData( prev_header_hash, timestamp, bytes([0] * 32), proof_of_space.get_hash(), body.get_hash(), bytes([0] * 32), ) header_hash_sig: PrependSignature = plot_sk.sign_prepend( header_data.get_hash()) header: Header = Header(header_data, header_hash_sig) challenge = Challenge( challenge_hash, proof_of_space.get_hash(), proof_of_time.get_hash(), height, uint64(prev_weight + difficulty), uint64(prev_iters + number_iters), ) header_block = HeaderBlock(proof_of_space, proof_of_time, challenge, header) full_block: FullBlock = FullBlock(header_block, body) return full_block
def _create_block( self, test_constants: ConsensusConstants, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint128, timestamp: uint64, difficulty: int, min_iters: int, seed: bytes, genesis: bool = False, reward_puzzlehash: bytes32 = None, transactions: Program = None, aggsig: G2Element = None, fees: uint64 = uint64(0), ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ selected_plot_info = None selected_proof_index = 0 selected_quality: Optional[bytes] = None best_quality = 0 plots = [ pinfo for _, pinfo in sorted(list(self.plots.items()), key=lambda x: str(x[0])) ] if self.use_any_pos: random.seed(seed) for i in range(len(plots) * 3): # Allow passing in seed, to create reorgs and different chains seeded_pn = random.randint(0, len(plots) - 1) plot_info = plots[seeded_pn] plot_id = plot_info.prover.get_id() ccp = ProofOfSpace.can_create_proof( plot_id, challenge_hash, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if not ccp: continue qualities = plot_info.prover.get_qualities_for_challenge( challenge_hash) if len(qualities) > 0: selected_plot_info = plot_info selected_quality = qualities[0] break else: for i in range(len(plots)): plot_info = plots[i] j = 0 plot_id = plot_info.prover.get_id() ccp = ProofOfSpace.can_create_proof( plot_id, challenge_hash, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if not ccp: continue qualities = plot_info.prover.get_qualities_for_challenge( challenge_hash) for quality in qualities: qual_int = int.from_bytes(quality, "big", signed=False) if qual_int > best_quality: best_quality = qual_int selected_quality = quality selected_plot_info = plot_info selected_proof_index = j j += 1 assert selected_plot_info is not None if selected_quality is None: raise RuntimeError("No proofs for this challenge") proof_xs: bytes = selected_plot_info.prover.get_full_proof( challenge_hash, selected_proof_index) plot_pk = ProofOfSpace.generate_plot_public_key( selected_plot_info.local_sk.get_g1(), selected_plot_info.farmer_public_key, ) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, selected_plot_info.pool_public_key, plot_pk, selected_plot_info.prover.get_size(), proof_xs, ) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, min_iters, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if self.real_plots: print(f"Performing {number_iters} VDF iterations") int_size = (test_constants.DISCRIMINANT_SIZE_BITS + 16) >> 4 result = prove(challenge_hash, test_constants.DISCRIMINANT_SIZE_BITS, number_iters) output = ClassgroupElement( int512(int.from_bytes( result[0:int_size], "big", signed=True, )), int512( int.from_bytes( result[int_size:2 * int_size], "big", signed=True, )), ) proof_bytes = result[2 * int_size:4 * int_size] proof_of_time = ProofOfTime( challenge_hash, number_iters, output, uint8(0), proof_bytes, ) # Use the extension data to create different blocks based on header hash extension_data: bytes32 = bytes32( [random.randint(0, 255) for _ in range(32)]) cost = uint64(0) fee_reward = uint64(block_rewards.calculate_base_fee(height) + fees) std_hash(std_hash(height)) # Create filter byte_array_tx: List[bytes32] = [] tx_additions: List[Coin] = [] tx_removals: List[bytes32] = [] if transactions: error, npc_list, _ = get_name_puzzle_conditions(transactions) additions: List[Coin] = additions_for_npc(npc_list) for coin in additions: tx_additions.append(coin) byte_array_tx.append(bytearray(coin.puzzle_hash)) for npc in npc_list: tx_removals.append(npc.coin_name) byte_array_tx.append(bytearray(npc.coin_name)) farmer_ph = self.farmer_ph pool_ph = self.pool_ph if reward_puzzlehash is not None: farmer_ph = reward_puzzlehash pool_ph = reward_puzzlehash byte_array_tx.append(bytearray(farmer_ph)) byte_array_tx.append(bytearray(pool_ph)) bip158: PyBIP158 = PyBIP158(byte_array_tx) encoded = bytes(bip158.GetEncoded()) removal_merkle_set = MerkleSet() addition_merkle_set = MerkleSet() # Create removal Merkle set for coin_name in tx_removals: removal_merkle_set.add_already_hashed(coin_name) # Create addition Merkle set puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {} cb_reward = calculate_block_reward(height) cb_coin = create_coinbase_coin(height, pool_ph, cb_reward) fees_coin = create_fees_coin(height, farmer_ph, fee_reward) for coin in tx_additions + [cb_coin, fees_coin]: if coin.puzzle_hash in puzzlehash_coin_map: puzzlehash_coin_map[coin.puzzle_hash].append(coin) else: puzzlehash_coin_map[coin.puzzle_hash] = [coin] # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash for puzzle, coins in puzzlehash_coin_map.items(): addition_merkle_set.add_already_hashed(puzzle) addition_merkle_set.add_already_hashed(hash_coin_list(coins)) additions_root = addition_merkle_set.get_root() removal_root = removal_merkle_set.get_root() generator_hash = (transactions.get_tree_hash() if transactions is not None else bytes32([0] * 32)) filter_hash = std_hash(encoded) pool_target = PoolTarget(pool_ph, uint32(height)) pool_target_signature = self.get_pool_key_signature( pool_target, proof_of_space.pool_public_key) assert pool_target_signature is not None final_aggsig: G2Element = pool_target_signature if aggsig is not None: final_aggsig = AugSchemeMPL.aggregate([final_aggsig, aggsig]) header_data: HeaderData = HeaderData( height, prev_header_hash, timestamp, filter_hash, proof_of_space.get_hash(), uint128(prev_weight + difficulty), uint64(prev_iters + number_iters), additions_root, removal_root, farmer_ph, fee_reward, pool_target, final_aggsig, cost, extension_data, generator_hash, ) header_hash_sig: G2Element = self.get_plot_signature( header_data, plot_pk) header: Header = Header(header_data, header_hash_sig) full_block: FullBlock = FullBlock(proof_of_space, proof_of_time, header, transactions, encoded) return full_block
async def get_headers(self) -> List[Header]: cursor = await self.db.execute("SELECT * from headers") rows = await cursor.fetchall() await cursor.close() return [Header.from_bytes(row[3]) for row in rows]